Skip to content

Commit b7c16d0

Browse files
feat: vendor agent scaffolder into firecrawl-cli
Replaces the `npx firecrawl-agent-cli` delegator with an in-process scaffolder vendored from firecrawl/firecrawl-agent. No separate npm package for the agent CLI — the root firecrawl-cli clones the public agent repo at runtime to fetch templates + agent-core. - Vendor under src/utils/agent-scaffold/ (manifest, scaffold, credentials, ui, create-flow). Upstream-tracked; keep in sync. - manifest.ts: strip the "bundled next to CLI" branch (doesn't apply here), always clone the public repo. Nested-manifest fallback handles the current `.internal/cli/agent-manifest.json` location. - Extract handleCreate from upstream init.ts; drop the commander wrapper since the root CLI owns the command surface. - create.ts now lazy-imports the scaffolder so non-create paths stay snappy. Smoke-tested end-to-end against a local agent repo via --from: next template scaffolds correctly with agent-core, .env.local, and expected file layout.
1 parent fa0b300 commit b7c16d0

6 files changed

Lines changed: 1175 additions & 76 deletions

File tree

src/commands/create.ts

Lines changed: 26 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,26 @@
11
/**
22
* `firecrawl create` command — scaffolds Firecrawl starter projects.
33
*
4-
* Hidden from --help until `firecrawl-agent-cli` is published to npm.
4+
* Hidden from --help until the flow is battle-tested in the wild. The
5+
* scaffolder is vendored under `src/utils/agent-scaffold/` (mirrored from
6+
* `firecrawl/firecrawl-agent`). At runtime it clones the public agent repo
7+
* to get templates — no separate npm package for the agent CLI.
8+
*
59
* Once visible, the command tree will grow to include additional kinds
610
* (scrape, browser, ai, app). For now, `agent` is the only kind.
7-
*
8-
* Implementation is a thin delegator: `firecrawl create agent ...` execs
9-
* `npx -y firecrawl-agent-cli create ...` and passes all flags through.
10-
* This avoids vendoring the scaffold code in the root CLI; the agent repo
11-
* remains the single source of truth for templates and the manifest.
1211
*/
1312

1413
import { Command } from 'commander';
15-
import { spawn } from 'child_process';
16-
17-
/** npm package name of the Firecrawl Agent CLI (bin: `firecrawl-agent`). */
18-
const AGENT_CLI_PACKAGE = 'firecrawl-agent-cli';
19-
20-
/**
21-
* Execute `npx -y <AGENT_CLI_PACKAGE> create ...` with inherited stdio so
22-
* the agent CLI's interactive prompts render in the user's terminal.
23-
* Resolves with the child exit code; callers forward it to `process.exit`.
24-
*/
25-
function runAgentCli(args: string[]): Promise<number> {
26-
const npx = process.platform === 'win32' ? 'npx.cmd' : 'npx';
27-
return new Promise((resolve) => {
28-
const child = spawn(npx, ['-y', AGENT_CLI_PACKAGE, 'create', ...args], {
29-
stdio: 'inherit',
30-
env: process.env,
31-
});
32-
child.on('exit', (code) => resolve(code ?? 1));
33-
child.on('error', (err) => {
34-
console.error(
35-
`\nFailed to launch ${AGENT_CLI_PACKAGE} via npx:`,
36-
err.message
37-
);
38-
console.error(
39-
`\n Install it directly and retry: npm install -g ${AGENT_CLI_PACKAGE}\n`
40-
);
41-
resolve(1);
42-
});
43-
});
44-
}
14+
import type { CreateOptions } from '../utils/agent-scaffold/create-flow';
4515

4616
function collect(val: string, acc: string[]): string[] {
4717
acc.push(val);
4818
return acc;
4919
}
5020

5121
/**
52-
* Build the `agent` subcommand. Flag surface mirrors `firecrawl-agent create`
53-
* exactly — anything the downstream CLI accepts is passed through verbatim.
22+
* Build the `agent` subcommand. Flag surface mirrors the upstream agent CLI.
23+
* The scaffold flow itself lives in `utils/agent-scaffold/create-flow.ts`.
5424
*/
5525
function createAgentSubcommand(): Command {
5626
return new Command('agent')
@@ -88,48 +58,28 @@ function createAgentSubcommand(): Command {
8858
[]
8959
)
9060
.option('--skip-install', 'Skip npm install')
91-
.allowUnknownOption() // Forward future flags without requiring a CLI update
9261
.action(
9362
async (
9463
projectName: string | undefined,
95-
options: Record<string, unknown>,
96-
cmd: Command
64+
options: CreateOptions & Record<string, unknown>
9765
) => {
98-
const args: string[] = [];
99-
if (projectName) args.push(projectName);
100-
101-
// Pass through known options. Commander camelCases hyphenated flags,
102-
// so we map back to the CLI-facing kebab-case form.
103-
const flagMap: Array<[string, string]> = [
104-
['template', '-t'],
105-
['provider', '--provider'],
106-
['model', '--model'],
107-
['subAgentProvider', '--sub-agent-provider'],
108-
['subAgentModel', '--sub-agent-model'],
109-
['from', '--from'],
110-
['apiKey', '--api-key'],
111-
];
112-
for (const [optKey, flag] of flagMap) {
113-
const val = options[optKey];
114-
if (typeof val === 'string' && val.length > 0) args.push(flag, val);
115-
}
116-
117-
// --key is repeatable
118-
const keys = options.key;
119-
if (Array.isArray(keys)) {
120-
for (const k of keys) {
121-
if (typeof k === 'string' && k.length > 0) args.push('--key', k);
122-
}
123-
}
124-
125-
if (options.skipInstall) args.push('--skip-install');
126-
127-
// Forward any unknown/forward-compatible options verbatim.
128-
const passthrough = cmd.args.slice(projectName ? 1 : 0);
129-
for (const extra of passthrough) args.push(extra);
130-
131-
const code = await runAgentCli(args);
132-
if (code !== 0) process.exit(code);
66+
// Lazy-load the scaffolder so startup cost (inquirer, git) only hits
67+
// users who actually invoke `create`. Keeps the rest of the CLI snappy.
68+
const { handleCreate } =
69+
await import('../utils/agent-scaffold/create-flow');
70+
await handleCreate(projectName, {
71+
template: options.template,
72+
provider: options.provider,
73+
model: options.model,
74+
subAgentProvider: options.subAgentProvider,
75+
subAgentModel: options.subAgentModel,
76+
from: options.from,
77+
apiKey: options.apiKey,
78+
key: Array.isArray(options.key)
79+
? (options.key as string[])
80+
: undefined,
81+
skipInstall: options.skipInstall as boolean | undefined,
82+
});
13383
}
13484
);
13585
}

0 commit comments

Comments
 (0)