This document outlines the process of adding new commands to the Switch CLI using our plugin-based approach.
Each command is structured as a separate module with a default export conforming to the Command
interface. Here's the basic structure of a command:
import { Command } from "../types.ts";
const myNewCommand: Command = {
name: "my-new-command",
description: "Description of what the command does",
usage: "switch my-new-command [options]",
options: [
{ flags: "-o, --option <value>", description: "Description of the option" },
{ flags: "-h, --help", description: "Show help for this command" },
],
examples: [
"switch my-new-command",
"switch my-new-command --option value",
],
execute: async (args: Record<string, unknown>) => {
// Command implementation goes here
},
};
export default myNewCommand;
-
Create a new file: Create a new file in the
commands
directory, e.g.,myNewCommand.ts
. This file will contain all the logic for your new command. -
Import necessary types and utilities: At the top of your new file, import the required types and utility functions. This typically includes the
Command
type, CLI utilities, and color functions for output formatting.import { Command } from "../types.ts"; import { echo, title } from "../cli.ts"; import { green, red } from "https://deno.land/[email protected]/fmt/colors.ts";
-
Define your command object: Create an object that conforms to the
Command
interface. This object should include the command's name, description, usage instructions, available options, and usage examples.const myNewCommand: Command = { name: "my-new-command", description: "Description of what the command does", usage: "switch my-new-command [options]", options: [ { flags: "-o, --option <value>", description: "Description of the option" }, { flags: "-h, --help", description: "Show help for this command" }, ], examples: [ "switch my-new-command", "switch my-new-command --option value", ], execute: async (args: Record<string, unknown>) => { // Command implementation will go here }, };
-
Implement the
execute
function: This is where the main logic of your command goes. Start by checking if the user has requested help, and if not, proceed with the command's functionality.execute: async (args: Record<string, unknown>) => { if ( args.help === true || args.h === true || (Array.isArray(args._) && (args._.includes("help") || args._.includes("--help"))) ) { showCommandHelp(); return; } // Your command logic goes here title("My New Command"); echo(green("Command executed successfully!")); },
-
Implement a help function: Create a separate function to display help information for your command. This function should show the usage, description, available options, and examples.
function showCommandHelp() { echo(`Usage: ${myNewCommand.usage}`); echo(myNewCommand.description); echo("\nOptions:"); myNewCommand.options?.forEach(option => { echo(` ${option.flags.padEnd(30)} ${option.description}`); }); echo("\nExamples:"); myNewCommand.examples?.forEach(example => { echo(` ${example}`); }); }
-
Export your command: Make sure to export your command as the default export at the end of the file. This allows it to be easily imported and registered in the main CLI file.
export default myNewCommand;
import { Command } from "../types.ts";
import { echo, title } from "../cli.ts";
import { green, red } from "https://deno.land/[email protected]/fmt/colors.ts";
const greetCommand: Command = {
name: "greet",
description: "Greet a user",
usage: "switch greet [options] <name>",
options: [
{ flags: "-l, --loud", description: "Greet loudly" },
{ flags: "-h, --help", description: "Show help for this command" },
],
examples: [
"switch greet John",
"switch greet --loud Jane",
],
execute: async (args: Record<string, unknown>) => {
if (args.help === true || args.h === true) {
showGreetHelp();
return;
}
const name = Array.isArray(args._) ? args._[0] : "User";
const greeting = args.loud ? `HELLO, ${name.toUpperCase()}!` : `Hello, ${name}!`;
title("Greeting");
echo(green(greeting));
},
};
function showGreetHelp() {
echo(`Usage: ${greetCommand.usage}`);
echo(greetCommand.description);
echo("\nOptions:");
greetCommand.options?.forEach(option => {
echo(` ${option.flags.padEnd(20)} ${option.description}`);
});
echo("\nExamples:");
greetCommand.examples?.forEach(example => {
echo(` ${example}`);
});
}
export default greetCommand;
Sometimes you may need to create a command that executes OS shell (or 'gh', 'az') commands). Here's how to do it, using a dir
command as an example:
-
Import necessary modules: In addition to the usual imports, make sure to import the
runShellCommand
function from your CLI utilities.import { Command } from "../types.ts"; import { title, echo, runShellCommand } from "../cli.ts";
-
Define your command object: Create your command object as usual, but pay special attention to the options that will be passed to the shell command.
const dirCommand: Command = { name: "dir", description: "Show the content of the current directory", usage: "switch dir [options]", options: [ { flags: "-a, --all", description: "Show hidden files" }, { flags: "-l, --long", description: "Use long listing format" }, ], examples: [ "switch dir", "switch dir --all", "switch dir -l", "switch dir -al", ], execute: async (args: Record<string, unknown>) => { // Implementation will go here } };
-
Implement the execute function: In the execute function, you'll need to:
- Check for help request
- Prepare arguments for the shell command based on user input
- Execute the shell command using
runShellCommand
- Handle the result
execute: async (args: Record<string, unknown>) => { if (args.help) { showDirHelp(); return; } title("Folder Content"); const lsArgs: string[] = []; if (args.a || args.all) lsArgs.push("-a"); if (args.l || args.long) lsArgs.push("-l"); const result = await runShellCommand("ls", lsArgs); if (result.ok) { echo(result.value); } else { echo(`Error: ${result.value}`); } }
-
Implement the help function: Create a function to display help information for your command.
function showDirHelp() { echo("Usage: " + dirCommand.usage); echo(dirCommand.description); echo("\nOptions:"); dirCommand.options?.forEach(option => { echo(` ${option.flags.padEnd(20)} ${option.description}`); }); echo("\nExamples:"); dirCommand.examples?.forEach(example => { echo(` ${example}`); }); }
-
Export the command: Don't forget to export your command as the default export.
export default dirCommand;
import { Command } from "../types.ts";
import { title, echo, runShellCommand } from "../cli.ts";
const dirCommand: Command = {
name: "dir",
description: "Show the content of the current directory",
usage: "switch dir [options]",
options: [
{ flags: "-a, --all", description: "Show hidden files" },
{ flags: "-l, --long", description: "Use long listing format" },
],
examples: [
"switch dir",
"switch dir --all",
"switch dir -l",
"switch dir -al",
],
execute: async (args: Record<string, unknown>) => {
if (args.help) {
showDirHelp();
return;
}
title("Folder Content");
const lsArgs: string[] = [];
if (args.a || args.all) lsArgs.push("-a");
if (args.l || args.long) lsArgs.push("-l");
const result = await runShellCommand("ls", lsArgs);
if (result.ok) {
echo(result.value);
} else {
echo(`Error: ${result.value}`);
}
}
};
function showDirHelp() {
echo("Usage: " + dirCommand.usage);
echo(dirCommand.description);
echo("\nOptions:");
dirCommand.options?.forEach(option => {
echo(` ${option.flags.padEnd(20)} ${option.description}`);
});
echo("\nExamples:");
dirCommand.examples?.forEach(example => {
echo(` ${example}`);
});
}
export default dirCommand;
- Use the
runShellCommand
function to execute shell commands. This function typically handles the complexities of running a shell command and returns a result object. - Parse user input carefully to construct the arguments for your shell command.
- Always handle both success and error cases when executing a shell command.
- Provide clear feedback to the user about the result of the command execution.
By following this guide, you should be able to easily add new commands to the Switch CLI using our plugin-based approach. This modular structure allows for easy maintenance and expansion of the CLI's functionality. If you have any questions or need further assistance, please don't hesitate to ask the development team.