From e61aa19dd74fe230a8abddea7646ee63ee8e269f Mon Sep 17 00:00:00 2001 From: Dan Moore Date: Wed, 4 Feb 2026 10:04:36 -0700 Subject: [PATCH 01/10] feat: added ability to specify a different cli tool also added support for copilot as that tool --- CONTRIBUTING.md | 79 ++++++++++++++++--- scripts/skill-generator/index.ts | 18 ++++- .../lib/cli-adapters/claude.ts | 27 +++++++ .../lib/cli-adapters/copilot.ts | 25 ++++++ .../skill-generator/lib/cli-adapters/index.ts | 49 ++++++++++++ .../skill-generator/lib/cli-adapters/types.ts | 29 +++++++ .../skill-generator/lib/{claude.ts => cli.ts} | 65 +++++++-------- scripts/skill-generator/lib/generator.ts | 8 +- scripts/skill-generator/lib/reviewer.ts | 9 ++- scripts/skill-generator/lib/types.ts | 1 + 10 files changed, 260 insertions(+), 50 deletions(-) create mode 100644 scripts/skill-generator/lib/cli-adapters/claude.ts create mode 100644 scripts/skill-generator/lib/cli-adapters/copilot.ts create mode 100644 scripts/skill-generator/lib/cli-adapters/index.ts create mode 100644 scripts/skill-generator/lib/cli-adapters/types.ts rename scripts/skill-generator/lib/{claude.ts => cli.ts} (83%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 96e7f99..64e5fbb 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://githubnext.com/projects/copilot-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'; +import { DEFAULT_MODEL } from './claude'; + +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 0422168..4b420fe 100644 --- a/scripts/skill-generator/index.ts +++ b/scripts/skill-generator/index.ts @@ -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 { DEFAULT_MODEL, 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(); @@ -386,6 +390,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 } @@ -461,6 +466,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}`)); @@ -476,6 +482,7 @@ async function handleReview( createPr: normalizeCreatePr(options.createPr), branchPrefix: options.branchPrefix, model: options.model, + cliTool: options.cli, }; const { dir: resultsDir } = createResultsDir(); @@ -640,6 +647,7 @@ async function handleReview( dryRun: reviewOptions.dryRun, maxIterations: reviewOptions.maxIterations, model: reviewOptions.model, + cliTool: reviewOptions.cliTool, parallel: existingProviders.length > 1, }); @@ -801,7 +809,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 @@ -809,7 +817,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) + .option('--cli ', `CLI tool to use (${AVAILABLE_CLI_TOOLS.join(', ')})`, DEFAULT_CLI_TOOL) + .option('--model ', 'Model to use', DEFAULT_MODEL) .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') @@ -826,7 +835,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) + .option('--cli ', `CLI tool to use (${AVAILABLE_CLI_TOOLS.join(', ')})`, DEFAULT_CLI_TOOL) + .option('--model ', 'Model to use', DEFAULT_MODEL) .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..44501e8 --- /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'; + +export 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..07ef267 --- /dev/null +++ b/scripts/skill-generator/lib/cli-adapters/copilot.ts @@ -0,0 +1,25 @@ +/** + * Copilot CLI adapter + * + * Supports the GitHub Copilot CLI tool + */ + +import type { CliAdapter, CliAdapterOptions, CliCommandConfig } from './types'; +import { DEFAULT_MODEL } from './claude'; + +export const copilotAdapter: CliAdapter = { + name: 'copilot', + + buildCommand(options: CliAdapterOptions): CliCommandConfig { + const model = options.model ?? DEFAULT_MODEL; + + return { + command: 'copilot', + args: [ + // Note: copilot does not use -p flag + '--model', model, + '--allow-all-tools', + ], + }; + }, +}; 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..76caf9c --- /dev/null +++ b/scripts/skill-generator/lib/cli-adapters/index.ts @@ -0,0 +1,49 @@ +/** + * CLI Adapter factory + * + * Registry and factory for CLI adapters + */ + +import type { CliAdapter } from './types'; +import { claudeAdapter, DEFAULT_MODEL } from './claude'; +import { copilotAdapter } from './copilot'; + +// Re-export types and default model +export type { CliAdapter, CliAdapterOptions, CliCommandConfig } from './types'; +export { DEFAULT_MODEL }; + +/** + * 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 83% rename from scripts/skill-generator/lib/claude.ts rename to scripts/skill-generator/lib/cli.ts index 28ee2d3..deda73a 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, DEFAULT_MODEL } from './cli-adapters'; const PROMPTS_DIR = join(__dirname, '..', 'prompts'); @@ -111,32 +112,37 @@ 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 +180,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,86 +211,87 @@ 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; } /** - * 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 }> { const replacements = { ...buildPromptReplacements(provider), @@ -296,7 +299,7 @@ export async function runFixIssues( }; 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 886ab28..fe7d6bc 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}`); @@ -208,6 +209,7 @@ export async function reviewAndIterate( logger, dryRun, model, + cliTool, parallel, }); @@ -222,7 +224,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' }; @@ -285,6 +287,7 @@ export async function reviewAndIterate( logger, dryRun, model, + cliTool, parallel, }); 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; } /** From ef5ac757bc3df3e6b677de9200b5e1b46f3dfd35 Mon Sep 17 00:00:00 2001 From: Dan Moore Date: Wed, 4 Feb 2026 12:23:01 -0700 Subject: [PATCH 02/10] use default copilot model that actually exists --- scripts/skill-generator/lib/cli-adapters/claude.ts | 2 +- scripts/skill-generator/lib/cli-adapters/copilot.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/skill-generator/lib/cli-adapters/claude.ts b/scripts/skill-generator/lib/cli-adapters/claude.ts index 44501e8..755f712 100644 --- a/scripts/skill-generator/lib/cli-adapters/claude.ts +++ b/scripts/skill-generator/lib/cli-adapters/claude.ts @@ -7,7 +7,7 @@ import type { CliAdapter, CliAdapterOptions, CliCommandConfig } from './types'; -export const DEFAULT_MODEL = 'claude-opus-4-20250514'; +const DEFAULT_MODEL = 'claude-opus-4-20250514'; export const claudeAdapter: CliAdapter = { name: 'claude', diff --git a/scripts/skill-generator/lib/cli-adapters/copilot.ts b/scripts/skill-generator/lib/cli-adapters/copilot.ts index 07ef267..ab05df7 100644 --- a/scripts/skill-generator/lib/cli-adapters/copilot.ts +++ b/scripts/skill-generator/lib/cli-adapters/copilot.ts @@ -5,7 +5,8 @@ */ import type { CliAdapter, CliAdapterOptions, CliCommandConfig } from './types'; -import { DEFAULT_MODEL } from './claude'; + +const DEFAULT_MODEL = 'claude-opus-4.5'; export const copilotAdapter: CliAdapter = { name: 'copilot', From 8db9c38c43481b2b15b2fd59f415e35bbf375ca0 Mon Sep 17 00:00:00 2001 From: Dan Moore Date: Wed, 4 Feb 2026 20:37:31 -0700 Subject: [PATCH 03/10] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- CONTRIBUTING.md | 2 +- scripts/skill-generator/lib/cli-adapters/copilot.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 64e5fbb..60283b2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -369,8 +369,8 @@ The generator supports a pluggable CLI adapter system. To add support for a new ```typescript import type { CliAdapter, CliAdapterOptions, CliCommandConfig } from './types'; -import { DEFAULT_MODEL } from './claude'; +const DEFAULT_MODEL = 'claude-opus-4-20250514'; export const myToolAdapter: CliAdapter = { name: 'mytool', diff --git a/scripts/skill-generator/lib/cli-adapters/copilot.ts b/scripts/skill-generator/lib/cli-adapters/copilot.ts index ab05df7..e682dc4 100644 --- a/scripts/skill-generator/lib/cli-adapters/copilot.ts +++ b/scripts/skill-generator/lib/cli-adapters/copilot.ts @@ -6,7 +6,8 @@ import type { CliAdapter, CliAdapterOptions, CliCommandConfig } from './types'; -const DEFAULT_MODEL = 'claude-opus-4.5'; +// Default model for the GitHub Copilot CLI; can be overridden via options.model +const DEFAULT_MODEL = 'gpt-4o'; export const copilotAdapter: CliAdapter = { name: 'copilot', From 8d96540156b08e2344ed2fff7646d6471f5b9749 Mon Sep 17 00:00:00 2001 From: Dan Moore Date: Wed, 4 Feb 2026 20:38:33 -0700 Subject: [PATCH 04/10] updated link to copilot doc --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 60283b2..9e5afc8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,7 +24,7 @@ The recommended way to create new provider skills is using our AI-powered genera - **Python 3.9+** (for FastAPI examples) - **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://githubnext.com/projects/copilot-cli) — GitHub Copilot command-line tool + - [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 From 8ce6f5444ebea4ef8f29cccae8b1412f687928e5 Mon Sep 17 00:00:00 2001 From: Dan Moore Date: Wed, 4 Feb 2026 20:39:47 -0700 Subject: [PATCH 05/10] updated doc for better default model name handling --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9e5afc8..635f449 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -370,7 +370,7 @@ The generator supports a pluggable CLI adapter system. To add support for a new ```typescript import type { CliAdapter, CliAdapterOptions, CliCommandConfig } from './types'; -const DEFAULT_MODEL = 'claude-opus-4-20250514'; +const DEFAULT_MODEL = 'your-model-name-here'; export const myToolAdapter: CliAdapter = { name: 'mytool', From 0b9b43ebedfc32554d735dfc8bc807947f2bc1ad Mon Sep 17 00:00:00 2001 From: Dan Moore Date: Wed, 4 Feb 2026 20:40:58 -0700 Subject: [PATCH 06/10] don't have a default model, just a default adapter --- scripts/skill-generator/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/skill-generator/index.ts b/scripts/skill-generator/index.ts index 356ffe0..e3aef76 100644 --- a/scripts/skill-generator/index.ts +++ b/scripts/skill-generator/index.ts @@ -25,7 +25,7 @@ import type { import { mergeProviderConfigs, skillExists } from './lib/config'; import { generateSkill } from './lib/generator'; import { reviewExistingSkill } from './lib/reviewer'; -import { DEFAULT_MODEL, setCachedVersions } from './lib/cli'; +import { setCachedVersions } from './lib/cli'; import { DEFAULT_CLI_TOOL, AVAILABLE_CLI_TOOLS } from './lib/cli-adapters'; import { getLatestVersions } from './lib/versions'; import { @@ -842,7 +842,7 @@ program .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('--cli ', `CLI tool to use (${AVAILABLE_CLI_TOOLS.join(', ')})`, DEFAULT_CLI_TOOL) - .option('--model ', 'Model to use', DEFAULT_MODEL) + .option('--model ', 'Model to use', 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') @@ -860,7 +860,7 @@ program .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('--cli ', `CLI tool to use (${AVAILABLE_CLI_TOOLS.join(', ')})`, DEFAULT_CLI_TOOL) - .option('--model ', 'Model to use', DEFAULT_MODEL) + .option('--model ', 'Model to use', 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') From ea15a7492a5bf8d8dfe83ded406a06c8f94c120d Mon Sep 17 00:00:00 2001 From: Dan Moore Date: Wed, 4 Feb 2026 20:48:00 -0700 Subject: [PATCH 07/10] cleaning up default model value handling --- scripts/skill-generator/index.ts | 4 ++-- scripts/skill-generator/lib/cli-adapters/index.ts | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/scripts/skill-generator/index.ts b/scripts/skill-generator/index.ts index e3aef76..6d84f37 100644 --- a/scripts/skill-generator/index.ts +++ b/scripts/skill-generator/index.ts @@ -842,7 +842,7 @@ program .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('--cli ', `CLI tool to use (${AVAILABLE_CLI_TOOLS.join(', ')})`, DEFAULT_CLI_TOOL) - .option('--model ', 'Model to use', undefined) + .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') @@ -860,7 +860,7 @@ program .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('--cli ', `CLI tool to use (${AVAILABLE_CLI_TOOLS.join(', ')})`, DEFAULT_CLI_TOOL) - .option('--model ', 'Model to use', undefined) + .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/index.ts b/scripts/skill-generator/lib/cli-adapters/index.ts index 76caf9c..13c8a43 100644 --- a/scripts/skill-generator/lib/cli-adapters/index.ts +++ b/scripts/skill-generator/lib/cli-adapters/index.ts @@ -5,12 +5,11 @@ */ import type { CliAdapter } from './types'; -import { claudeAdapter, DEFAULT_MODEL } from './claude'; +import { claudeAdapter } from './claude'; import { copilotAdapter } from './copilot'; // Re-export types and default model export type { CliAdapter, CliAdapterOptions, CliCommandConfig } from './types'; -export { DEFAULT_MODEL }; /** * Registry of available CLI adapters From 533912e447b3ac0dd9df238a5bcc54d6ec6cda7f Mon Sep 17 00:00:00 2001 From: Dan Moore Date: Wed, 4 Feb 2026 20:50:49 -0700 Subject: [PATCH 08/10] fixes from my mistakes --- scripts/skill-generator/index.ts | 4 ++-- scripts/skill-generator/lib/cli-adapters/index.ts | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/scripts/skill-generator/index.ts b/scripts/skill-generator/index.ts index 6d84f37..633254c 100644 --- a/scripts/skill-generator/index.ts +++ b/scripts/skill-generator/index.ts @@ -842,7 +842,7 @@ program .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('--cli ', `CLI tool to use (${AVAILABLE_CLI_TOOLS.join(', ')})`, DEFAULT_CLI_TOOL) - .option('--model ', 'Model to use (defaults to CLI tool's default model)', undefined) + .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') @@ -860,7 +860,7 @@ program .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('--cli ', `CLI tool to use (${AVAILABLE_CLI_TOOLS.join(', ')})`, DEFAULT_CLI_TOOL) - .option('--model ', 'Model to use (defaults to CLI tool's default model)', undefined) + .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/index.ts b/scripts/skill-generator/lib/cli-adapters/index.ts index 13c8a43..0d02b57 100644 --- a/scripts/skill-generator/lib/cli-adapters/index.ts +++ b/scripts/skill-generator/lib/cli-adapters/index.ts @@ -11,6 +11,11 @@ import { copilotAdapter } from './copilot'; // Re-export types and default model export type { CliAdapter, CliAdapterOptions, CliCommandConfig } from './types'; +/** + * Default model to use (Claude's default since it's the default CLI tool) + */ +export const DEFAULT_MODEL = 'claude-opus-4-20250514'; + /** * Registry of available CLI adapters */ From e8f7fc5b05c8f5a33ced70926b1bfce2d593c6e0 Mon Sep 17 00:00:00 2001 From: Dan Moore Date: Wed, 4 Feb 2026 20:58:36 -0700 Subject: [PATCH 09/10] cleanup of unused export --- scripts/skill-generator/lib/cli-adapters/index.ts | 7 +------ scripts/skill-generator/lib/cli.ts | 5 ++--- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/scripts/skill-generator/lib/cli-adapters/index.ts b/scripts/skill-generator/lib/cli-adapters/index.ts index 0d02b57..0f5df04 100644 --- a/scripts/skill-generator/lib/cli-adapters/index.ts +++ b/scripts/skill-generator/lib/cli-adapters/index.ts @@ -8,14 +8,9 @@ import type { CliAdapter } from './types'; import { claudeAdapter } from './claude'; import { copilotAdapter } from './copilot'; -// Re-export types and default model +// Re-export types export type { CliAdapter, CliAdapterOptions, CliCommandConfig } from './types'; -/** - * Default model to use (Claude's default since it's the default CLI tool) - */ -export const DEFAULT_MODEL = 'claude-opus-4-20250514'; - /** * Registry of available CLI adapters */ diff --git a/scripts/skill-generator/lib/cli.ts b/scripts/skill-generator/lib/cli.ts index 1e6f2e4..e8f904d 100644 --- a/scripts/skill-generator/lib/cli.ts +++ b/scripts/skill-generator/lib/cli.ts @@ -7,7 +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, DEFAULT_MODEL } from './cli-adapters'; +import { getCliAdapter, DEFAULT_CLI_TOOL } from './cli-adapters'; const PROMPTS_DIR = join(__dirname, '..', 'prompts'); @@ -112,8 +112,7 @@ export function buildPromptReplacements(provider: ProviderConfig): Record Date: Thu, 5 Feb 2026 13:52:15 +0000 Subject: [PATCH 10/10] fix(cli-adapters): improve Copilot CLI compatibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes discovered during CLI adapter testing: - Change Copilot --allow-all-tools to --allow-all (--allow-all-tools alone blocks URL fetches in non-interactive mode) - Change Copilot default model from gpt-4o to claude-sonnet-4 (gpt-5 has known issues with web fetching causing "invalid_request_body" errors) - Add CLI tool validation using Commander's choices() for better UX - Improve comments explaining stdin handling and flag purposes Tested with: - Claude CLI → Postmark skill (39 tests pass) - Copilot CLI → WooCommerce skill (31 tests pass) Co-authored-by: mooreds Co-authored-by: Cursor --- scripts/skill-generator/index.ts | 6 +++--- scripts/skill-generator/lib/cli-adapters/copilot.ts | 12 +++++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/scripts/skill-generator/index.ts b/scripts/skill-generator/index.ts index 633254c..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'; @@ -841,7 +841,7 @@ 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('--cli ', `CLI tool to use (${AVAILABLE_CLI_TOOLS.join(', ')})`, DEFAULT_CLI_TOOL) + .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) @@ -859,7 +859,7 @@ 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('--cli ', `CLI tool to use (${AVAILABLE_CLI_TOOLS.join(', ')})`, DEFAULT_CLI_TOOL) + .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) diff --git a/scripts/skill-generator/lib/cli-adapters/copilot.ts b/scripts/skill-generator/lib/cli-adapters/copilot.ts index e682dc4..9e43fc5 100644 --- a/scripts/skill-generator/lib/cli-adapters/copilot.ts +++ b/scripts/skill-generator/lib/cli-adapters/copilot.ts @@ -7,7 +7,11 @@ import type { CliAdapter, CliAdapterOptions, CliCommandConfig } from './types'; // Default model for the GitHub Copilot CLI; can be overridden via options.model -const DEFAULT_MODEL = 'gpt-4o'; +// 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', @@ -18,9 +22,11 @@ export const copilotAdapter: CliAdapter = { return { command: 'copilot', args: [ - // Note: copilot does not use -p flag + // Copilot CLI reads prompts from stdin (no -p flag needed like Claude) '--model', model, - '--allow-all-tools', + // 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', ], }; },