diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 96e7f99..635f449 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,7 +22,9 @@ The recommended way to create new provider skills is using our AI-powered genera - **Node.js 18+** - **Python 3.9+** (for FastAPI examples) -- **[Claude CLI](https://docs.anthropic.com/en/docs/claude-cli)** — Install and authenticate with `claude login` +- **AI CLI Tool** — One of the following: + - [Claude CLI](https://docs.anthropic.com/en/docs/claude-cli) — Install and authenticate with `claude login` (default) + - [Copilot CLI](https://docs.github.com/en/copilot/how-tos/use-copilot-for-common-tasks/use-copilot-in-the-cli) — GitHub Copilot command-line tool - **GITHUB_TOKEN** — For PR creation (optional but recommended) ```bash @@ -182,13 +184,20 @@ providers: By default, all providers run in parallel. Use `--parallel ` to limit concurrency. -### 4. Preview What Would Happen (Dry Run) +### 4. Use a Different CLI Tool + +```bash +# Use Copilot instead of Claude +./scripts/generate-skills.sh generate "twilio" --cli copilot --create-pr +``` + +### 5. Preview What Would Happen (Dry Run) ```bash ./scripts/generate-skills.sh generate "linear" --dry-run ``` -### 5. Resume After a Failed Generation +### 6. Resume After a Failed Generation If generation fails (tests won't pass, API timeout, etc.), the worktree is preserved: @@ -208,7 +217,7 @@ The review command will: 3. AI reviews and fixes any issues 4. If within thresholds, pushes and creates PR -### 6. Update an Existing Skill +### 7. Update an Existing Skill Use the review command to improve skills already in the repository: @@ -236,7 +245,7 @@ providers: Verify signature verification is current with latest SDK. ``` -### 7. Manual Review and PR Creation +### 8. Manual Review and PR Creation ```bash # Generate without PR - inspect results first @@ -256,7 +265,7 @@ gh pr create --title "feat: add clerk-webhooks skill" When creating or updating a PR, use the title and description format in the next section. -### 8. Pull Request Title and Description +### 9. Pull Request Title and Description Use these conventions so PRs are consistent and easy to review. @@ -284,7 +293,7 @@ Use these conventions so PRs are consistent and easy to review. For generator-created PRs, the body may also include **Generation details** (provider, tests passed, review passed, iterations, issues found/fixed). When editing an existing PR (e.g. after review), update the title and description to match these conventions if they don't already. -### 9. Clean Up Worktrees +### 10. Clean Up Worktrees ```bash # List all worktrees @@ -316,11 +325,12 @@ rm -rf .worktrees && git worktree prune | Option | Description | Default | |--------|-------------|---------| +| `--cli ` | CLI tool to use (`claude`, `copilot`) | claude | | `--working-dir ` | Generate in specific directory (skip worktree) | Creates worktree | | `--no-worktree` | Generate in current directory (shorthand for `--working-dir .`) | Creates worktree | | `--create-pr [type]` | Push and create PR (`true` or `draft`) | No PR | | `--parallel ` | Max concurrent generations | All providers | -| `--model ` | Claude model | claude-opus-4-20250514 | +| `--model ` | Model to use | claude-opus-4-20250514 | | `--max-iterations ` | Max test/fix cycles | 3 | | `--base-branch ` | Branch to create from | main | | `--skip-tests` | Skip test execution | false | @@ -338,18 +348,69 @@ rm -rf .worktrees && git worktree prune | Option | Description | Default | |--------|-------------|---------| +| `--cli ` | CLI tool to use (`claude`, `copilot`) | claude | | `--working-dir ` | Review in specific directory (skip worktree) | Creates worktree | | `--no-worktree` | Review in current directory (shorthand for `--working-dir .`) | Creates worktree | | `--create-pr [type]` | Create PR with fixes | No PR | | `--max-iterations ` | Max fix cycles | 3 | | `--parallel ` | Max concurrent reviews | All providers | -| `--model ` | Claude model | claude-opus-4-20250514 | +| `--model ` | Model to use | claude-opus-4-20250514 | | `--branch-prefix ` | Branch name prefix | improve | | `--config ` | YAML config file | — | | `--dry-run` | Preview without executing | false | --- +## Adding New CLI Tools + +The generator supports a pluggable CLI adapter system. To add support for a new AI CLI tool: + +1. **Create an adapter file** at `scripts/skill-generator/lib/cli-adapters/.ts`: + +```typescript +import type { CliAdapter, CliAdapterOptions, CliCommandConfig } from './types'; + +const DEFAULT_MODEL = 'your-model-name-here'; +export const myToolAdapter: CliAdapter = { + name: 'mytool', + + buildCommand(options: CliAdapterOptions): CliCommandConfig { + const model = options.model ?? DEFAULT_MODEL; + + return { + command: 'mytool', // The CLI command to execute + args: [ + // Arguments specific to this CLI tool + '--model', model, + '--some-flag', + ], + }; + }, +}; +``` + +2. **Register the adapter** in `scripts/skill-generator/lib/cli-adapters/index.ts`: + +```typescript +import { myToolAdapter } from './mytool'; + +const adapters: Map = new Map([ + ['claude', claudeAdapter], + ['copilot', copilotAdapter], + ['mytool', myToolAdapter], // Add your adapter here +]); +``` + +3. **Use it** with the `--cli` flag: + +```bash +./scripts/generate-skills.sh generate stripe --cli mytool +``` + +The adapter interface is simple — it just needs to return the command name and arguments. The generator handles stdin prompt passing, timeout, progress display, and output capture. + +--- + ## Manual Contribution Prefer to write code yourself? Follow these steps. diff --git a/scripts/skill-generator/index.ts b/scripts/skill-generator/index.ts index e40fa94..aaafe4f 100644 --- a/scripts/skill-generator/index.ts +++ b/scripts/skill-generator/index.ts @@ -7,7 +7,7 @@ */ import { config } from 'dotenv'; -import { Command } from 'commander'; +import { Command, Option } from 'commander'; import chalk from 'chalk'; import pLimit from 'p-limit'; import { mkdirSync, writeFileSync, appendFileSync, existsSync } from 'fs'; @@ -25,7 +25,8 @@ import type { import { mergeProviderConfigs, skillExists } from './lib/config'; import { generateSkill } from './lib/generator'; import { reviewExistingSkill } from './lib/reviewer'; -import { DEFAULT_MODEL, setCachedVersions } from './lib/claude'; +import { setCachedVersions } from './lib/cli'; +import { DEFAULT_CLI_TOOL, AVAILABLE_CLI_TOOLS } from './lib/cli-adapters'; import { getLatestVersions } from './lib/versions'; import { createWorktree, @@ -143,6 +144,7 @@ async function handleGenerate( maxIterations: string; createPr: boolean | string; model: string; + cli: string; workingDir?: string; // Generate in this directory (skip worktree creation) worktree?: boolean; // Set to false by --no-worktree flag } @@ -186,6 +188,7 @@ async function handleGenerate( : providerConfigs.length; console.log(chalk.blue(`Providers (${providerConfigs.length}): ${providerConfigs.map(p => p.name).join(', ')}`)); + console.log(chalk.blue(`CLI tool: ${options.cli}`)); console.log(chalk.blue(`Model: ${options.model}`)); if (!useProvidedDir && providerConfigs.length > 1) { console.log(chalk.blue(`Parallel: ${parallelCount}`)); @@ -206,6 +209,7 @@ async function handleGenerate( maxIterations: parseInt(options.maxIterations, 10), createPr: normalizeCreatePr(options.createPr), model: options.model, + cliTool: options.cli, }; const { dir: resultsDir } = createResultsDir(); @@ -398,6 +402,7 @@ async function handleReview( createPr: boolean | string; branchPrefix: string; model: string; + cli: string; workingDir?: string; // Review in this directory (any git checkout, worktree, or local path) worktree?: boolean; // Set to false by --no-worktree flag } @@ -473,6 +478,7 @@ async function handleReview( : existingProviders.length; console.log(chalk.blue(`Providers: ${existingProviders.map(p => p.name).join(', ')}`)); + console.log(chalk.blue(`CLI tool: ${options.cli}`)); console.log(chalk.blue(`Model: ${options.model}`)); if (!useProvidedDir && existingProviders.length > 1) { console.log(chalk.blue(`Parallel: ${parallelCount}`)); @@ -488,6 +494,7 @@ async function handleReview( createPr: normalizeCreatePr(options.createPr), branchPrefix: options.branchPrefix, model: options.model, + cliTool: options.cli, }; const { dir: resultsDir } = createResultsDir(); @@ -664,6 +671,7 @@ async function handleReview( dryRun: reviewOptions.dryRun, maxIterations: reviewOptions.maxIterations, model: reviewOptions.model, + cliTool: reviewOptions.cliTool, parallel: existingProviders.length > 1, }); @@ -825,7 +833,7 @@ const program = new Command(); program .name('skill-generator') - .description('Generate and review webhook skills using Claude CLI') + .description('Generate and review webhook skills using AI CLI tools') .version('1.0.0'); program @@ -833,7 +841,8 @@ program .description('Generate new webhook skills') .argument('[providers...]', 'Provider names, or provider=url, or provider=url|notes (e.g. elevenlabs=https://github.com/elevenlabs/elevenlabs-js|Official SDK supports webhook verification)') .option('--config ', 'Load provider configs from YAML file') - .option('--model ', 'Claude model to use', DEFAULT_MODEL) + .addOption(new Option('--cli ', 'CLI tool to use').choices(AVAILABLE_CLI_TOOLS).default(DEFAULT_CLI_TOOL)) + .option('--model ', "Model to use (defaults to CLI tool's default model)", undefined) .option('--parallel ', 'Max concurrent agents (default: all providers)') .option('--dry-run', 'Show what would be done without executing', false) .option('--base-branch ', 'Branch to create from', 'main') @@ -850,7 +859,8 @@ program .description('Review and improve existing webhook skills') .argument('[providers...]', 'Provider names, or provider=url, or provider=url|notes (e.g. elevenlabs=https://.../elevenlabs-js|Prefer SDK verification in skill)') .option('--config ', 'Load provider configs from YAML file') - .option('--model ', 'Claude model to use', DEFAULT_MODEL) + .addOption(new Option('--cli ', 'CLI tool to use').choices(AVAILABLE_CLI_TOOLS).default(DEFAULT_CLI_TOOL)) + .option('--model ', "Model to use (defaults to CLI tool's default model)", undefined) .option('--parallel ', 'Max concurrent agents (default: all providers)') .option('--dry-run', 'Show what would be done without executing', false) .option('--max-iterations ', 'Max review/fix cycles', '3') diff --git a/scripts/skill-generator/lib/cli-adapters/claude.ts b/scripts/skill-generator/lib/cli-adapters/claude.ts new file mode 100644 index 0000000..755f712 --- /dev/null +++ b/scripts/skill-generator/lib/cli-adapters/claude.ts @@ -0,0 +1,27 @@ +/** + * Claude CLI adapter + * + * Supports the Anthropic Claude CLI tool + * https://github.com/anthropics/claude-cli + */ + +import type { CliAdapter, CliAdapterOptions, CliCommandConfig } from './types'; + +const DEFAULT_MODEL = 'claude-opus-4-20250514'; + +export const claudeAdapter: CliAdapter = { + name: 'claude', + + buildCommand(options: CliAdapterOptions): CliCommandConfig { + const model = options.model ?? DEFAULT_MODEL; + + return { + command: 'claude', + args: [ + '-p', + '--model', model, + '--dangerously-skip-permissions', + ], + }; + }, +}; diff --git a/scripts/skill-generator/lib/cli-adapters/copilot.ts b/scripts/skill-generator/lib/cli-adapters/copilot.ts new file mode 100644 index 0000000..9e43fc5 --- /dev/null +++ b/scripts/skill-generator/lib/cli-adapters/copilot.ts @@ -0,0 +1,33 @@ +/** + * Copilot CLI adapter + * + * Supports the GitHub Copilot CLI tool + */ + +import type { CliAdapter, CliAdapterOptions, CliCommandConfig } from './types'; + +// Default model for the GitHub Copilot CLI; can be overridden via options.model +// Valid choices: claude-sonnet-4.5, claude-haiku-4.5, claude-opus-4.5, claude-sonnet-4, +// gemini-3-pro-preview, gpt-5.2-codex, gpt-5.2, gpt-5.1-codex-max, gpt-5.1-codex, +// gpt-5.1, gpt-5, gpt-5.1-codex-mini, gpt-5-mini, gpt-4.1 +// Note: gpt-5 has known issues with web fetching causing "invalid_request_body" errors +const DEFAULT_MODEL = 'claude-sonnet-4'; + +export const copilotAdapter: CliAdapter = { + name: 'copilot', + + buildCommand(options: CliAdapterOptions): CliCommandConfig { + const model = options.model ?? DEFAULT_MODEL; + + return { + command: 'copilot', + args: [ + // Copilot CLI reads prompts from stdin (no -p flag needed like Claude) + '--model', model, + // Use --allow-all to enable tools, paths, AND URLs without interactive prompts + // (--allow-all-tools alone still blocks URL fetches in non-interactive mode) + '--allow-all', + ], + }; + }, +}; diff --git a/scripts/skill-generator/lib/cli-adapters/index.ts b/scripts/skill-generator/lib/cli-adapters/index.ts new file mode 100644 index 0000000..0f5df04 --- /dev/null +++ b/scripts/skill-generator/lib/cli-adapters/index.ts @@ -0,0 +1,48 @@ +/** + * CLI Adapter factory + * + * Registry and factory for CLI adapters + */ + +import type { CliAdapter } from './types'; +import { claudeAdapter } from './claude'; +import { copilotAdapter } from './copilot'; + +// Re-export types +export type { CliAdapter, CliAdapterOptions, CliCommandConfig } from './types'; + +/** + * Registry of available CLI adapters + */ +const adapters: Map = new Map([ + ['claude', claudeAdapter], + ['copilot', copilotAdapter], +]); + +/** + * List of available CLI tool names (for help text and validation) + */ +export const AVAILABLE_CLI_TOOLS = Array.from(adapters.keys()); + +/** + * Default CLI tool to use + */ +export const DEFAULT_CLI_TOOL = 'claude'; + +/** + * Get a CLI adapter by name + * + * @param name - The name of the CLI tool (e.g., 'claude', 'copilot') + * @returns The CLI adapter + * @throws Error if the adapter is not found + */ +export function getCliAdapter(name: string): CliAdapter { + const adapter = adapters.get(name); + + if (!adapter) { + const available = AVAILABLE_CLI_TOOLS.join(', '); + throw new Error(`Unknown CLI tool: '${name}'. Available tools: ${available}`); + } + + return adapter; +} diff --git a/scripts/skill-generator/lib/cli-adapters/types.ts b/scripts/skill-generator/lib/cli-adapters/types.ts new file mode 100644 index 0000000..9b78fd2 --- /dev/null +++ b/scripts/skill-generator/lib/cli-adapters/types.ts @@ -0,0 +1,29 @@ +/** + * CLI Adapter types for supporting multiple AI CLI tools + */ + +/** + * Options passed to the adapter when building the command + */ +export interface CliAdapterOptions { + model?: string; +} + +/** + * The command configuration returned by an adapter + */ +export interface CliCommandConfig { + command: string; + args: string[]; +} + +/** + * Interface that all CLI adapters must implement + */ +export interface CliAdapter { + /** Unique identifier for this CLI tool */ + name: string; + + /** Build the command and arguments for executing the CLI */ + buildCommand(options: CliAdapterOptions): CliCommandConfig; +} diff --git a/scripts/skill-generator/lib/claude.ts b/scripts/skill-generator/lib/cli.ts similarity index 85% rename from scripts/skill-generator/lib/claude.ts rename to scripts/skill-generator/lib/cli.ts index ffabee5..e8f904d 100644 --- a/scripts/skill-generator/lib/claude.ts +++ b/scripts/skill-generator/lib/cli.ts @@ -1,5 +1,5 @@ /** - * Claude CLI wrapper for running prompts + * CLI wrapper for running prompts with different AI CLI tools */ import { execa, type ExecaError } from 'execa'; @@ -7,6 +7,7 @@ import { readFileSync } from 'fs'; import { join } from 'path'; import type { ProviderConfig, Logger, ReviewResult } from './types'; import { type PackageVersions, formatVersionsTable } from './versions'; +import { getCliAdapter, DEFAULT_CLI_TOOL } from './cli-adapters'; const PROMPTS_DIR = join(__dirname, '..', 'prompts'); @@ -111,32 +112,36 @@ export function buildPromptReplacements(provider: ProviderConfig): Record { - const { workingDir, logger, dryRun, model = DEFAULT_MODEL, parallel = false } = options; + const { workingDir, logger, dryRun, model, cliTool = DEFAULT_CLI_TOOL, parallel = false } = options; + + // Get the adapter for the specified CLI tool + const adapter = getCliAdapter(cliTool); + const { command, args } = adapter.buildCommand({ model }); if (dryRun) { - logger.info(`[DRY RUN] Would run Claude (model: ${model}) with prompt:`); + logger.info(`[DRY RUN] Would run ${adapter.name} (model: ${model ?? 'default'}) with prompt:`); logger.debug(prompt.slice(0, 500) + '...'); return { output: '[DRY RUN] Skipped', success: true }; } try { - logger.info(`Running Claude CLI (model: ${model})...`); + logger.info(`Running ${adapter.name} CLI (model: ${model ?? 'default'})...`); logger.info(`Working directory: ${workingDir}`); const startTime = Date.now(); @@ -174,11 +179,7 @@ export async function runClaude( // Use stdin to pass the prompt - more reliable for long prompts // Capture output while also streaming it to console - const subprocess = execa('claude', [ - '-p', - '--model', model, - '--dangerously-skip-permissions', - ], { + const subprocess = execa(command, args, { cwd: workingDir, input: prompt, // Pass prompt via stdin timeout: 20 * 60 * 1000, // 20 minute timeout for Opus @@ -209,87 +210,88 @@ export async function runClaude( clearInterval(progressInterval); if (parallel) { - logger.success(`Claude completed in ${formatElapsed()}`); + logger.success(`${adapter.name} completed in ${formatElapsed()}`); } else { process.stdout.write(`\r✓ Completed in ${formatElapsed()} \n`); } - // Show Claude's output (only in non-parallel mode to avoid interleaved output) + // Show CLI output (only in non-parallel mode to avoid interleaved output) if (stdout && !parallel) { - console.log('\n--- Claude Output ---'); + console.log(`\n--- ${adapter.name} Output ---`); console.log(stdout); console.log('--- End Output ---\n'); } if (result.exitCode !== 0) { - logger.error(`Claude CLI failed with exit code ${result.exitCode}`); + logger.error(`${adapter.name} CLI failed with exit code ${result.exitCode}`); if (stderr) logger.error(stderr); return { output: stderr || stdout, success: false }; } - logger.info(`Claude CLI completed successfully`); + logger.info(`${adapter.name} CLI completed successfully`); return { output: stdout, success: true }; } catch (error) { const execaError = error as ExecaError; - logger.error(`Claude CLI error: ${execaError.message}`); + logger.error(`${adapter.name} CLI error: ${execaError.message}`); return { output: execaError.message, success: false }; } } /** - * Common options for Claude operations + * Common options for CLI operations */ -interface ClaudeOperationOptions { +interface CliOperationOptions { workingDir: string; logger: Logger; dryRun?: boolean; model?: string; + cliTool?: string; parallel?: boolean; existingTodo?: string | null; // Content from TODO.md if it exists } /** - * Run Claude to generate a skill + * Run CLI to generate a skill */ export async function runGenerateSkill( provider: ProviderConfig, - options: ClaudeOperationOptions + options: CliOperationOptions ): Promise<{ output: string; success: boolean }> { const replacements = buildPromptReplacements(provider); const prompt = loadPrompt('generate-skill.md', replacements); - return runClaude(prompt, options); + return runCLI(prompt, options); } /** - * Run Claude to review a skill + * Run CLI to review a skill */ export async function runReviewSkill( provider: ProviderConfig, - options: ClaudeOperationOptions + options: CliOperationOptions ): Promise<{ output: string; success: boolean; reviewResult?: ReviewResult }> { const replacements = buildPromptReplacements(provider); const prompt = loadPrompt('review-skill.md', replacements); - const result = await runClaude(prompt, options); + const result = await runCLI(prompt, options); if (!result.success) { return result; } - // Try to parse the review result from Claude's output + // Try to parse the review result from CLI output const reviewResult = parseReviewResult(result.output); return { ...result, reviewResult }; } /** - * Run Claude to fix issues + * Run CLI to fix issues */ export async function runFixIssues( provider: ProviderConfig, issuesJson: string, - options: ClaudeOperationOptions + options: CliOperationOptions ): Promise<{ output: string; success: boolean }> { // Build TODO context section if we have existing TODO.md content let todoContext = ''; @@ -312,7 +314,7 @@ ${options.existingTodo} }; const prompt = loadPrompt('fix-issues.md', replacements); - return runClaude(prompt, options); + return runCLI(prompt, options); } /** diff --git a/scripts/skill-generator/lib/generator.ts b/scripts/skill-generator/lib/generator.ts index 6dd216a..9165180 100644 --- a/scripts/skill-generator/lib/generator.ts +++ b/scripts/skill-generator/lib/generator.ts @@ -14,7 +14,7 @@ import type { TestResult, } from './types'; import { getSkillPath, skillExists } from './config'; -import { runGenerateSkill } from './claude'; +import { runGenerateSkill } from './cli'; import { reviewAndIterate } from './reviewer'; import { createWorktree, @@ -177,7 +177,7 @@ export async function generateSkill( } ): Promise { const { rootDir, logger, logFile } = context; - const { model } = options; + const { model, cliTool } = options; const startTime = Date.now(); const branchName = context.branchName || `feat/${provider.name}-webhooks`; @@ -229,13 +229,14 @@ export async function generateSkill( worktreePath = worktreeResult.path; } - // Generate skill (Claude works in the worktree directory) + // Generate skill (CLI tool works in the worktree directory) logger.info(`Generating skill for ${provider.displayName || provider.name}...`); const generateResult = await runGenerateSkill(provider, { workingDir: worktreePath, logger, dryRun: options.dryRun, model, + cliTool, parallel: context.isParallel, }); @@ -258,6 +259,7 @@ export async function generateSkill( skipReview: options.skipReview, maxIterations: options.maxIterations, model, + cliTool, parallel: context.isParallel, }); diff --git a/scripts/skill-generator/lib/reviewer.ts b/scripts/skill-generator/lib/reviewer.ts index 29ccc5c..a812e93 100644 --- a/scripts/skill-generator/lib/reviewer.ts +++ b/scripts/skill-generator/lib/reviewer.ts @@ -11,7 +11,7 @@ import type { ReviewResult, ReviewIssue, } from './types'; -import { runReviewSkill, runFixIssues } from './claude'; +import { runReviewSkill, runFixIssues } from './cli'; import { runTests } from './generator'; /** @@ -32,6 +32,7 @@ export interface ReviewAndIterateOptions { skipReview?: boolean; maxIterations: number; model?: string; + cliTool?: string; parallel?: boolean; } @@ -155,7 +156,7 @@ export async function reviewAndIterate( provider: ProviderConfig, options: ReviewAndIterateOptions ): Promise { - const { workingDir, logger, dryRun, skipTests, skipReview, maxIterations, model, parallel } = options; + const { workingDir, logger, dryRun, skipTests, skipReview, maxIterations, model, cliTool, parallel } = options; logger.info(`Starting review for ${provider.displayName || provider.name}`); logger.info(`Acceptance thresholds: critical=${ACCEPTANCE_THRESHOLDS.maxCritical}, major≤${ACCEPTANCE_THRESHOLDS.maxMajor}, minor≤${ACCEPTANCE_THRESHOLDS.maxMinor}, total≤${ACCEPTANCE_THRESHOLDS.maxTotal}`); @@ -215,6 +216,7 @@ export async function reviewAndIterate( logger, dryRun, model, + cliTool, parallel, existingTodo, }); @@ -230,7 +232,7 @@ export async function reviewAndIterate( // Phase 2: Review content accuracy if (!skipReview) { logger.info('Reviewing content accuracy...'); - const review = await runReviewSkill(provider, { workingDir, logger, dryRun, model, parallel }); + const review = await runReviewSkill(provider, { workingDir, logger, dryRun, model, cliTool, parallel }); if (!review.success) { reviewResult = { passed: false, details: 'Review failed to run' }; @@ -293,6 +295,7 @@ export async function reviewAndIterate( logger, dryRun, model, + cliTool, parallel, existingTodo, }); diff --git a/scripts/skill-generator/lib/types.ts b/scripts/skill-generator/lib/types.ts index 1dcff4c..f99999f 100644 --- a/scripts/skill-generator/lib/types.ts +++ b/scripts/skill-generator/lib/types.ts @@ -29,6 +29,7 @@ export interface BaseOptions { maxIterations: number; configFile?: string; model: string; + cliTool: string; } /**