Skill28.7k estrellas del repoactualizado today
implement-cli-command
This Claude Code skill guides implementing new CLI commands in the Composio TypeScript CLI using Effect.ts framework patterns, service dependency injection, and @effect/cli declarations. Use it when building fresh commands or subcommands, wiring commands into the command tree, or understanding existing command architecture to extend it. For CLI design decisions like argument names and help text, use the create-cli skill instead; for e2e tests, use create-cli-e2e.
Instalar en Claude Code
Copiargit clone --depth 1 https://github.com/ComposioHQ/composio /tmp/implement-cli-command && cp -r /tmp/implement-cli-command/.claude/skills/implement-cli-command ~/.claude/skills/implement-cli-commandDespués abre una sesión nueva de Claude Code; el skill carga automáticamente.
Definición
SKILL.md
# Implement CLI Command
Implement new commands and subcommands in `ts/packages/cli/`. Covers file creation, Effect patterns, service wiring, option declaration, output conventions, and registration.
## When to Use
- Implementing a new CLI command from a spec or design
- Adding a subcommand to an existing command group
- Wiring a new command into the command tree
- Understanding how existing commands work to extend them
For CLI **design** (arguments, flags, help text, UX), see the `create-cli` skill instead.
For CLI **e2e tests**, see the `create-cli-e2e` skill instead.
## Architecture
The CLI uses `@effect/cli` for command declaration, `effect` for the runtime, and a service-oriented architecture with dependency injection via Effect layers.
See `ts/packages/cli/AGENTS.md` for the full architecture reference (services, effects, models, dependencies, vendor submodule locations).
```
src/
├── bin.ts # Entry point — layer composition, error handling, runtime
├── commands/
│ ├── index.ts # Command tree — registers all commands
│ ├── $default.cmd.ts # Root command with global options (--log-level)
│ ├── version.cmd.ts # Simple data command
│ ├── whoami.cmd.ts # Data command with service dependency
│ ├── login.cmd.ts # Complex command (options, spinner, polling)
│ ├── logout.cmd.ts # Action command (no stdout data)
│ ├── upgrade.cmd.ts # Action command (delegates to service)
│ ├── generate/
│ │ ├── generate.cmd.ts # Parent command group for `composio generate`
│ │ ├── generate-py.cmd.ts # `composio generate py`
│ │ └── generate-ts.cmd.ts # `composio generate ts`
│ ├── manage/
│ │ └── manage.cmd.ts # Parent command group for `composio manage`
│ ├── ts/
│ │ ├── ts.cmd.ts # Existing TS generation internals, referenced from generate/
│ │ └── commands/
│ │ └── ts.generate.cmd.ts # Reusable TS generation logic
│ └── py/
│ ├── py.cmd.ts # Existing Python generation internals, referenced from generate/
│ └── commands/
│ └── py.generate.cmd.ts
├── services/ # Effect services (dependency injection)
├── effects/ # Reusable Effect computations
├── models/ # Effect Schema definitions
├── generation/ # Code generation pipeline
├── effect-errors/ # Error capture and formatting
└── ui/ # Terminal output helpers
```
### File Naming Convention
- Command files: `<name>.cmd.ts` (e.g., `version.cmd.ts`, `login.cmd.ts`)
- Subcommand implementation files: `<parent>.<name>.cmd.ts` inside `commands/` (e.g., `ts.generate.cmd.ts`)
- Parent command groups: `<name>.cmd.ts` at the group level (e.g., `generate/generate.cmd.ts`, `manage/manage.cmd.ts`)
- Wrapper subcommand entrypoints can also live beside their parent group (e.g., `generate/generate-py.cmd.ts`, `generate/generate-ts.cmd.ts`)
## Creating a New Command
### Step 1: Create the Command File
Create `src/commands/<name>.cmd.ts`.
**Minimal template** (data command, no options):
```typescript
import { Command } from '@effect/cli';
import { Effect } from 'effect';
import { TerminalUI } from 'src/services/terminal-ui';
export const myCmd = Command.make('my-command', {}).pipe(
Command.withDescription('Brief description of what the command does.'),
Command.withHandler(() =>
Effect.gen(function* () {
const ui = yield* TerminalUI;
// Compute result...
const result = 'some-value';
yield* ui.log.info(result); // Decoration → stderr
yield* ui.output(result); // Data → stdout (for scripts)
})
)
);
```
**Template with options:**
```typescript
import { Command, Options } from '@effect/cli';
import { Effect, Option } from 'effect';
import { TerminalUI } from 'src/services/terminal-ui';
import { ComposioToolkitsRepository } from 'src/services/composio-clients';
// Define options at module level
const toolkitSlug = Options.text('toolkit').pipe(
Options.withDescription('Toolkit slug to look up.')
);
const searchOpt = Options.optional(
Options.text('search')
).pipe(
Options.withDescription('Search query to filter results.')
);
export const myCmd = Command.make('my-command', { toolkitSlug, searchOpt }).pipe(
Command.withDescription('Brief description.'),
Command.withHandler(({ toolkitSlug, searchOpt }) =>
Effect.gen(function* () {
const ui = yield* TerminalUI;
const client = yield* ComposioToolkitsRepository;
yield* ui.intro('composio my-command');
// Use options — searchOpt is Option<string>
const search = Option.getOrUndefined(searchOpt);
// Fetch data with spinner
const result = yield* ui.withSpinner(
'Fetching data...',
client.getToolkits(),
{ successMessage: 'Done', errorMessage: 'Failed to fetch' }
);
// Output
yield* ui.note(formatResult(result), 'Result');
yield* ui.output(formatResult(result));
yield* ui.outro('Done');
})
)
);
```
### Step 2: Register the Command
Add the command to `src/commands/index.ts`:
```typescript
import { myCmd } from './my-command.cmd';
const $cmd = $defaultCmd.pipe(
Command.withSubcommands([
versionCmd,
upgradeCmd,
whoamiCmd,
loginCmd,
logoutCmd,
generateCmd,
manageCmd,
myCmd, // Add here
])
);
```
### Step 3: Add Required Service Layers (if any)
If your command uses a new service not already in `bin.ts`, add its layer:
```typescript
// In src/bin.ts
const layers = Layer.mergeAll(
// ... existing layers
MyNewServiceLive, // Add if needed
);
```
Most commands only use services already provided. The `ComposioToolkitsRepository` service is provided by `ComposioToolkitsRepositoryCachedLive` in `bin.ts` — you do not need to add a separate layer for the base repository.
## Creating a Subcommand Group
For commands like `composio manage toolkits list