Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 26 additions & 76 deletions src/commands/create.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,26 @@
/**
* `firecrawl create` command — scaffolds Firecrawl starter projects.
*
* Hidden from --help until `firecrawl-agent-cli` is published to npm.
* Hidden from --help until the flow is battle-tested in the wild. The
* scaffolder is vendored under `src/utils/agent-scaffold/` (mirrored from
* `firecrawl/firecrawl-agent`). At runtime it clones the public agent repo
* to get templates — no separate npm package for the agent CLI.
*
* Once visible, the command tree will grow to include additional kinds
* (scrape, browser, ai, app). For now, `agent` is the only kind.
*
* Implementation is a thin delegator: `firecrawl create agent ...` execs
* `npx -y firecrawl-agent-cli create ...` and passes all flags through.
* This avoids vendoring the scaffold code in the root CLI; the agent repo
* remains the single source of truth for templates and the manifest.
*/

import { Command } from 'commander';
import { spawn } from 'child_process';

/** npm package name of the Firecrawl Agent CLI (bin: `firecrawl-agent`). */
const AGENT_CLI_PACKAGE = 'firecrawl-agent-cli';

/**
* Execute `npx -y <AGENT_CLI_PACKAGE> create ...` with inherited stdio so
* the agent CLI's interactive prompts render in the user's terminal.
* Resolves with the child exit code; callers forward it to `process.exit`.
*/
function runAgentCli(args: string[]): Promise<number> {
const npx = process.platform === 'win32' ? 'npx.cmd' : 'npx';
return new Promise((resolve) => {
const child = spawn(npx, ['-y', AGENT_CLI_PACKAGE, 'create', ...args], {
stdio: 'inherit',
env: process.env,
});
child.on('exit', (code) => resolve(code ?? 1));
child.on('error', (err) => {
console.error(
`\nFailed to launch ${AGENT_CLI_PACKAGE} via npx:`,
err.message
);
console.error(
`\n Install it directly and retry: npm install -g ${AGENT_CLI_PACKAGE}\n`
);
resolve(1);
});
});
}
import type { CreateOptions } from '../utils/agent-scaffold/create-flow';

function collect(val: string, acc: string[]): string[] {
acc.push(val);
return acc;
}

/**
* Build the `agent` subcommand. Flag surface mirrors `firecrawl-agent create`
* exactly — anything the downstream CLI accepts is passed through verbatim.
* Build the `agent` subcommand. Flag surface mirrors the upstream agent CLI.
* The scaffold flow itself lives in `utils/agent-scaffold/create-flow.ts`.
*/
function createAgentSubcommand(): Command {
return new Command('agent')
Expand Down Expand Up @@ -88,48 +58,28 @@ function createAgentSubcommand(): Command {
[]
)
.option('--skip-install', 'Skip npm install')
.allowUnknownOption() // Forward future flags without requiring a CLI update
.action(
async (
projectName: string | undefined,
options: Record<string, unknown>,
cmd: Command
options: CreateOptions & Record<string, unknown>
) => {
const args: string[] = [];
if (projectName) args.push(projectName);

// Pass through known options. Commander camelCases hyphenated flags,
// so we map back to the CLI-facing kebab-case form.
const flagMap: Array<[string, string]> = [
['template', '-t'],
['provider', '--provider'],
['model', '--model'],
['subAgentProvider', '--sub-agent-provider'],
['subAgentModel', '--sub-agent-model'],
['from', '--from'],
['apiKey', '--api-key'],
];
for (const [optKey, flag] of flagMap) {
const val = options[optKey];
if (typeof val === 'string' && val.length > 0) args.push(flag, val);
}

// --key is repeatable
const keys = options.key;
if (Array.isArray(keys)) {
for (const k of keys) {
if (typeof k === 'string' && k.length > 0) args.push('--key', k);
}
}

if (options.skipInstall) args.push('--skip-install');

// Forward any unknown/forward-compatible options verbatim.
const passthrough = cmd.args.slice(projectName ? 1 : 0);
for (const extra of passthrough) args.push(extra);

const code = await runAgentCli(args);
if (code !== 0) process.exit(code);
// Lazy-load the scaffolder so startup cost (inquirer, git) only hits
// users who actually invoke `create`. Keeps the rest of the CLI snappy.
const { handleCreate } =
await import('../utils/agent-scaffold/create-flow');
await handleCreate(projectName, {
template: options.template,
provider: options.provider,
model: options.model,
subAgentProvider: options.subAgentProvider,
subAgentModel: options.subAgentModel,
from: options.from,
apiKey: options.apiKey,
key: Array.isArray(options.key)
? (options.key as string[])
: undefined,
skipInstall: options.skipInstall as boolean | undefined,
});
}
);
}
Expand Down
Loading
Loading