From fa735ff336dba6841430a6d56d13af991bf0f399 Mon Sep 17 00:00:00 2001 From: Developers Digest <124798203+developersdigest@users.noreply.github.com> Date: Fri, 10 Apr 2026 10:17:42 -0400 Subject: [PATCH 1/3] quiet skill install output and add next-steps summary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the noisy 'npx skills add' inherited stdout (banner, +/symlink table, security report, ~80 lines per repo) with a single line per repo: " ✓ firecrawl/cli" / " ✓ firecrawl/skills". TTY runs get a transient "↓ " indicator while the install is in flight; non-TTY runs (CI, piped) get only the final line. Replace the trailing "Setup complete!" line with a printNextSteps() block that surfaces: - what just happened (skills installed globally) - what firecrawl is for (search, scrape, crawl, interact) - 4 example prompts (2 build-skill triggers, 2 direct CLI calls) - pointer to firecrawl --help Both interactive and non-interactive init paths use the same helpers, so output is consistent regardless of how users enter the flow. Bumped to 1.14.2. --- README.md | 4 +- package.json | 2 +- skills/firecrawl-cli/rules/install.md | 8 +- skills/firecrawl-cli/rules/security.md | 2 +- src/__tests__/commands/init.test.ts | 8 +- src/commands/init.ts | 185 ++++++++++++++++--------- 6 files changed, 133 insertions(+), 76 deletions(-) diff --git a/README.md b/README.md index 7b5a414b9..2500f07de 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ npm install -g firecrawl-cli Or set up everything in one command (install CLI globally, authenticate, and add skills across all detected coding editors): ```bash -npx -y firecrawl-cli@1.14.1 init -y --browser +npx -y firecrawl-cli@1.14.2 init -y --browser ``` - `-y` runs setup non-interactively @@ -583,7 +583,7 @@ firecrawl --status ``` ``` - 🔥 firecrawl cli v1.14.1 + 🔥 firecrawl cli v1.14.2 ● Authenticated via stored credentials Concurrency: 0/100 jobs (parallel scrape limit) diff --git a/package.json b/package.json index b095cda29..841bf5859 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firecrawl-cli", - "version": "1.14.1", + "version": "1.14.2", "description": "Command-line interface for Firecrawl. Scrape, crawl, and extract data from any website directly from your terminal.", "main": "dist/index.js", "bin": { diff --git a/skills/firecrawl-cli/rules/install.md b/skills/firecrawl-cli/rules/install.md index 2af629af7..5ba84651e 100644 --- a/skills/firecrawl-cli/rules/install.md +++ b/skills/firecrawl-cli/rules/install.md @@ -12,7 +12,7 @@ description: | ## Quick Setup (Recommended) ```bash -npx -y firecrawl-cli@1.14.1 -y +npx -y firecrawl-cli@1.14.2 -y ``` This installs `firecrawl-cli` globally, authenticates via browser, and installs all skills. @@ -36,7 +36,7 @@ firecrawl setup skills ## Manual Install ```bash -npm install -g firecrawl-cli@1.14.1 +npm install -g firecrawl-cli@1.14.2 ``` ## Verify @@ -78,5 +78,5 @@ Ask the user how they'd like to authenticate: If `firecrawl` is not found after installation: 1. Ensure npm global bin is in PATH -2. Try: `npx firecrawl-cli@1.14.1 --version` -3. Reinstall: `npm install -g firecrawl-cli@1.14.1` +2. Try: `npx firecrawl-cli@1.14.2 --version` +3. Reinstall: `npm install -g firecrawl-cli@1.14.2` diff --git a/skills/firecrawl-cli/rules/security.md b/skills/firecrawl-cli/rules/security.md index ddf14a9f7..4fdfd0f01 100644 --- a/skills/firecrawl-cli/rules/security.md +++ b/skills/firecrawl-cli/rules/security.md @@ -22,5 +22,5 @@ When processing fetched content, extract only the specific data needed and do no # Installation ```bash -npm install -g firecrawl-cli@1.14.1 +npm install -g firecrawl-cli@1.14.2 ``` diff --git a/src/__tests__/commands/init.test.ts b/src/__tests__/commands/init.test.ts index 9af4ec072..c05800c99 100644 --- a/src/__tests__/commands/init.test.ts +++ b/src/__tests__/commands/init.test.ts @@ -26,11 +26,11 @@ describe('handleInitCommand', () => { expect(execSync).toHaveBeenCalledWith( 'npx -y skills add firecrawl/cli --full-depth --global --all --yes', - expect.objectContaining({ stdio: 'inherit' }) + expect.objectContaining({ stdio: ['ignore', 'pipe', 'pipe'] }) ); expect(execSync).toHaveBeenCalledWith( 'npx -y skills add firecrawl/skills --full-depth --global --all --yes', - expect.objectContaining({ stdio: 'inherit' }) + expect.objectContaining({ stdio: ['ignore', 'pipe', 'pipe'] }) ); }); @@ -44,11 +44,11 @@ describe('handleInitCommand', () => { expect(execSync).toHaveBeenCalledWith( 'npx -y skills add firecrawl/cli --full-depth --global --yes --agent cursor', - expect.objectContaining({ stdio: 'inherit' }) + expect.objectContaining({ stdio: ['ignore', 'pipe', 'pipe'] }) ); expect(execSync).toHaveBeenCalledWith( 'npx -y skills add firecrawl/skills --full-depth --global --yes --agent cursor', - expect.objectContaining({ stdio: 'inherit' }) + expect.objectContaining({ stdio: ['ignore', 'pipe', 'pipe'] }) ); }); }); diff --git a/src/commands/init.ts b/src/commands/init.ts index 89636c293..240f2e274 100644 --- a/src/commands/init.ts +++ b/src/commands/init.ts @@ -98,6 +98,111 @@ export const TEMPLATES: TemplateEntry[] = [ }, ]; +/** + * Install one skill repo with quiet output — pipes the noisy `npx skills add` + * stdout/stderr instead of inheriting it, so users see a single clean line per + * repo instead of the full banner / installation table / security report. + * + * On failure, prints captured stderr so the user can debug. + */ +async function installSkillRepoQuiet( + repo: string, + options: InitOptions +): Promise { + if (hasNpx()) { + const args = buildSkillsInstallArgs({ + repo, + agent: options.agent, + yes: options.yes || options.all || true, + global: true, + includeNpxYes: true, + }); + + // In a TTY, show a transient "↓ " line that gets overwritten by + // "✓ " once the install completes. In a non-TTY (CI logs, piped), + // just print the final line — `\r` overwrites don't render correctly. + const isTty = process.stdout.isTTY; + if (isTty) { + process.stdout.write(` ${dim}↓ ${repo}${reset}`); + } + try { + execSync(args.join(' '), { + stdio: ['ignore', 'pipe', 'pipe'], + env: cleanNpmEnv(), + }); + if (isTty) { + process.stdout.write(`\r ${green}✓${reset} ${repo} \n`); + } else { + console.log(` ${green}✓${reset} ${repo}`); + } + } catch (err) { + if (isTty) { + process.stdout.write(`\r ${dim}✗${reset} ${repo} \n`); + } else { + console.log(` ${dim}✗${reset} ${repo}`); + } + const stderr = + err && typeof err === 'object' && 'stderr' in err + ? String((err as { stderr: Buffer | string }).stderr || '') + : ''; + if (stderr.trim()) { + console.error( + stderr + .trim() + .split('\n') + .map((l) => ` ${dim}${l}${reset}`) + .join('\n') + ); + } + throw err; + } + return; + } + + // No npx available — use native fallback. It prints its own dimmed status. + await installSkillsNative(repo); +} + +/** + * Print the post-install next-steps block. Shown at the end of both the + * interactive and non-interactive init flows. + */ +function printNextSteps(): void { + const arrow = `${dim}→${reset}`; + const heading = (text: string) => `${bold}${text}${reset}`; + + console.log(''); + console.log(`${green}${bold} ✓ Firecrawl is ready${reset}`); + console.log(''); + console.log(` Skills are installed globally across your AI coding agents.`); + console.log( + ` Use Firecrawl to ${bold}search${reset}, ${bold}scrape${reset}, ${bold}crawl${reset}, and ${bold}interact${reset} with the web.` + ); + console.log(''); + console.log(` ${heading('Next steps')}`); + console.log(''); + console.log(` ${dim}Ask your AI coding agent:${reset}`); + console.log( + ` ${arrow} "Use firecrawl to scrape pricing from competitor sites and save to JSON"` + ); + console.log( + ` ${arrow} "Build a Next.js dashboard that uses firecrawl to search tech news"` + ); + console.log(''); + console.log(` ${dim}Or run firecrawl directly:${reset}`); + console.log( + ` ${arrow} ${bold}firecrawl scrape${reset} https://news.ycombinator.com` + ); + console.log( + ` ${arrow} ${bold}firecrawl search${reset} "best AI coding tools 2026" --limit 5` + ); + console.log(''); + console.log( + ` ${dim}Run${reset} ${bold}firecrawl --help${reset} ${dim}for the full command reference.${reset}` + ); + console.log(''); +} + async function stepInstall(): Promise { const { confirm } = await import('@inquirer/prompts'); const shouldInstall = await confirm({ @@ -219,35 +324,14 @@ async function stepIntegrations(options: InitOptions): Promise { for (const integration of integrations) { switch (integration) { case 'skills': { - console.log(`\n Setting up skills...`); + console.log(`\n Installing skills...`); for (const repo of SKILL_REPOS) { - if (hasNpx()) { - const args = buildSkillsInstallArgs({ - repo, - agent: options.agent, - yes: options.yes || options.all, - global: true, - includeNpxYes: true, - }); - try { - execSync(args.join(' '), { - stdio: 'inherit', - env: cleanNpmEnv(), - }); - console.log(` ${green}✓${reset} Skills installed from ${repo}`); - } catch { - console.error( - ` Failed to install skills from ${repo}. Run "firecrawl setup skills" later.` - ); - } - } else { - try { - await installSkillsNative(repo); - } catch { - console.error( - ` Failed to install skills from ${repo}. Run "firecrawl setup skills" later.` - ); - } + try { + await installSkillRepoQuiet(repo, options); + } catch { + console.error( + ` ${dim}Run "firecrawl setup skills" later to retry.${reset}` + ); } } break; @@ -562,9 +646,7 @@ export async function handleInitCommand( // Step 4: Template await stepTemplate(); - console.log( - `${green}${bold} Setup complete!${reset} Run ${dim}firecrawl --help${reset} to get started.\n` - ); + printNextSteps(); } async function runNonInteractive(options: InitOptions): Promise { @@ -638,41 +720,16 @@ async function runNonInteractive(options: InitOptions): Promise { `${stepLabel()} Installing firecrawl skills for AI coding agents...` ); for (const repo of SKILL_REPOS) { - if (hasNpx()) { - const args = buildSkillsInstallArgs({ - repo, - agent: options.agent, - yes: true, - global: true, - includeNpxYes: true, - }); - try { - execSync(args.join(' '), { - stdio: 'inherit', - env: cleanNpmEnv(), - }); - console.log(`${green}✓${reset} Skills installed from ${repo}`); - } catch { - console.error( - `\nFailed to install skills from ${repo}. You can retry with: firecrawl setup skills` - ); - process.exit(1); - } - } else { - try { - await installSkillsNative(repo); - } catch { - console.error( - `\nFailed to install skills from ${repo}. You can retry with: firecrawl setup skills` - ); - process.exit(1); - } + try { + await installSkillRepoQuiet(repo, options); + } catch { + console.error( + `\n${dim}Failed to install skills from ${repo}. Retry with: firecrawl setup skills${reset}` + ); + process.exit(1); } } - console.log(''); } - console.log( - `${green}${bold}Setup complete!${reset} Run ${dim}firecrawl --help${reset} to get started.\n` - ); + printNextSteps(); } From c2e4856ef56c1b437cf8c433778f66541831ff9f Mon Sep 17 00:00:00 2001 From: Developers Digest <124798203+developersdigest@users.noreply.github.com> Date: Fri, 10 Apr 2026 10:21:21 -0400 Subject: [PATCH 2/3] add mcp install hint to next-steps summary --- src/commands/init.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/commands/init.ts b/src/commands/init.ts index 240f2e274..ce1aac68e 100644 --- a/src/commands/init.ts +++ b/src/commands/init.ts @@ -197,6 +197,9 @@ function printNextSteps(): void { ` ${arrow} ${bold}firecrawl search${reset} "best AI coding tools 2026" --limit 5` ); console.log(''); + console.log( + ` ${dim}Want MCP for editor integration? Run${reset} ${bold}firecrawl setup mcp${reset}` + ); console.log( ` ${dim}Run${reset} ${bold}firecrawl --help${reset} ${dim}for the full command reference.${reset}` ); From a7aa071f7a4ebfdde78ad423cb2d18f8e4c6fd98 Mon Sep 17 00:00:00 2001 From: Developers Digest <124798203+developersdigest@users.noreply.github.com> Date: Fri, 10 Apr 2026 10:25:14 -0400 Subject: [PATCH 3/3] tighten next-steps summary and surface skill count MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parse '(Found|Installed) N skills' from captured npx skills output and sum across both repos so the post-install summary shows "✓ Installed 14 skills across your AI coding agents" instead of vague text. Collapse the next-steps block from 18 lines to 7: drop the 4 example prompts in favor of one of each (AI prompt + direct CLI), plus the MCP install hint and the --help pointer. Each entry uses padded labels ("Ask your AI:", "Run direct: ", "Add MCP: ", "All commands:") so the commands align in a column. stepIntegrations and runNonInteractive both thread the count through to printNextSteps. Tests still mock execSync as undefined, so parseSkillCount handles the empty case. Bumped to 1.14.3. --- README.md | 4 +- package.json | 2 +- skills/firecrawl-cli/rules/install.md | 8 +-- skills/firecrawl-cli/rules/security.md | 2 +- src/commands/init.ts | 96 +++++++++++++------------- 5 files changed, 56 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index 2500f07de..c64bae568 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ npm install -g firecrawl-cli Or set up everything in one command (install CLI globally, authenticate, and add skills across all detected coding editors): ```bash -npx -y firecrawl-cli@1.14.2 init -y --browser +npx -y firecrawl-cli@1.14.3 init -y --browser ``` - `-y` runs setup non-interactively @@ -583,7 +583,7 @@ firecrawl --status ``` ``` - 🔥 firecrawl cli v1.14.2 + 🔥 firecrawl cli v1.14.3 ● Authenticated via stored credentials Concurrency: 0/100 jobs (parallel scrape limit) diff --git a/package.json b/package.json index 841bf5859..9a57b72d0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firecrawl-cli", - "version": "1.14.2", + "version": "1.14.3", "description": "Command-line interface for Firecrawl. Scrape, crawl, and extract data from any website directly from your terminal.", "main": "dist/index.js", "bin": { diff --git a/skills/firecrawl-cli/rules/install.md b/skills/firecrawl-cli/rules/install.md index 5ba84651e..5c26745d0 100644 --- a/skills/firecrawl-cli/rules/install.md +++ b/skills/firecrawl-cli/rules/install.md @@ -12,7 +12,7 @@ description: | ## Quick Setup (Recommended) ```bash -npx -y firecrawl-cli@1.14.2 -y +npx -y firecrawl-cli@1.14.3 -y ``` This installs `firecrawl-cli` globally, authenticates via browser, and installs all skills. @@ -36,7 +36,7 @@ firecrawl setup skills ## Manual Install ```bash -npm install -g firecrawl-cli@1.14.2 +npm install -g firecrawl-cli@1.14.3 ``` ## Verify @@ -78,5 +78,5 @@ Ask the user how they'd like to authenticate: If `firecrawl` is not found after installation: 1. Ensure npm global bin is in PATH -2. Try: `npx firecrawl-cli@1.14.2 --version` -3. Reinstall: `npm install -g firecrawl-cli@1.14.2` +2. Try: `npx firecrawl-cli@1.14.3 --version` +3. Reinstall: `npm install -g firecrawl-cli@1.14.3` diff --git a/skills/firecrawl-cli/rules/security.md b/skills/firecrawl-cli/rules/security.md index 4fdfd0f01..5e6189321 100644 --- a/skills/firecrawl-cli/rules/security.md +++ b/skills/firecrawl-cli/rules/security.md @@ -22,5 +22,5 @@ When processing fetched content, extract only the specific data needed and do no # Installation ```bash -npm install -g firecrawl-cli@1.14.2 +npm install -g firecrawl-cli@1.14.3 ``` diff --git a/src/commands/init.ts b/src/commands/init.ts index ce1aac68e..c8b7ca275 100644 --- a/src/commands/init.ts +++ b/src/commands/init.ts @@ -99,16 +99,17 @@ export const TEMPLATES: TemplateEntry[] = [ ]; /** - * Install one skill repo with quiet output — pipes the noisy `npx skills add` - * stdout/stderr instead of inheriting it, so users see a single clean line per - * repo instead of the full banner / installation table / security report. + * Install one skill repo quietly. Captures `npx skills add` output instead of + * inheriting it, so users see a single line per repo. Returns the number of + * skills installed (parsed from the captured stdout), or null if unknown. * - * On failure, prints captured stderr so the user can debug. + * In a TTY, shows a transient "↓ " line that gets overwritten by + * "✓ (N skills)" on success. In a non-TTY, prints only the final line. */ async function installSkillRepoQuiet( repo: string, options: InitOptions -): Promise { +): Promise { if (hasNpx()) { const args = buildSkillsInstallArgs({ repo, @@ -118,23 +119,25 @@ async function installSkillRepoQuiet( includeNpxYes: true, }); - // In a TTY, show a transient "↓ " line that gets overwritten by - // "✓ " once the install completes. In a non-TTY (CI logs, piped), - // just print the final line — `\r` overwrites don't render correctly. const isTty = process.stdout.isTTY; if (isTty) { process.stdout.write(` ${dim}↓ ${repo}${reset}`); } try { - execSync(args.join(' '), { + const stdout = execSync(args.join(' '), { stdio: ['ignore', 'pipe', 'pipe'], env: cleanNpmEnv(), }); + const count = parseSkillCount(stdout?.toString() ?? ''); + const suffix = count != null ? ` ${dim}(${count} skills)${reset}` : ''; if (isTty) { - process.stdout.write(`\r ${green}✓${reset} ${repo} \n`); + process.stdout.write( + `\r ${green}✓${reset} ${repo}${suffix} \n` + ); } else { - console.log(` ${green}✓${reset} ${repo}`); + console.log(` ${green}✓${reset} ${repo}${suffix}`); } + return count; } catch (err) { if (isTty) { process.stdout.write(`\r ${dim}✗${reset} ${repo} \n`); @@ -156,52 +159,44 @@ async function installSkillRepoQuiet( } throw err; } - return; } - // No npx available — use native fallback. It prints its own dimmed status. + // No npx — fall back to native installer. It prints its own status. await installSkillsNative(repo); + return null; +} + +/** Parse "Found N skills" or "Installed N skills" from npx skills output. */ +function parseSkillCount(output: string): number | null { + const match = output.match(/(?:Found|Installed)\s+(\d+)\s+skills?/); + return match ? parseInt(match[1], 10) : null; } /** - * Print the post-install next-steps block. Shown at the end of both the - * interactive and non-interactive init flows. + * Print the post-install next-steps block. Brief by design — confirms what was + * installed and gives 4 entry points (AI prompt, direct CLI, MCP, help). */ -function printNextSteps(): void { +function printNextSteps(skillCount: number | null): void { const arrow = `${dim}→${reset}`; - const heading = (text: string) => `${bold}${text}${reset}`; + const summary = + skillCount != null + ? `${green}✓${reset} Installed ${bold}${skillCount} skills${reset} ${dim}across your AI coding agents${reset}` + : `${green}✓${reset} Skills installed ${dim}across your AI coding agents${reset}`; console.log(''); - console.log(`${green}${bold} ✓ Firecrawl is ready${reset}`); + console.log(` ${summary}`); console.log(''); - console.log(` Skills are installed globally across your AI coding agents.`); console.log( - ` Use Firecrawl to ${bold}search${reset}, ${bold}scrape${reset}, ${bold}crawl${reset}, and ${bold}interact${reset} with the web.` + ` ${arrow} ${dim}Ask your AI:${reset} "Use firecrawl to scrape pricing into JSON"` ); - console.log(''); - console.log(` ${heading('Next steps')}`); - console.log(''); - console.log(` ${dim}Ask your AI coding agent:${reset}`); console.log( - ` ${arrow} "Use firecrawl to scrape pricing from competitor sites and save to JSON"` + ` ${arrow} ${dim}Run direct: ${reset} ${bold}firecrawl scrape${reset} https://example.com` ); console.log( - ` ${arrow} "Build a Next.js dashboard that uses firecrawl to search tech news"` + ` ${arrow} ${dim}Add MCP: ${reset} ${bold}firecrawl setup mcp${reset}` ); - console.log(''); - console.log(` ${dim}Or run firecrawl directly:${reset}`); console.log( - ` ${arrow} ${bold}firecrawl scrape${reset} https://news.ycombinator.com` - ); - console.log( - ` ${arrow} ${bold}firecrawl search${reset} "best AI coding tools 2026" --limit 5` - ); - console.log(''); - console.log( - ` ${dim}Want MCP for editor integration? Run${reset} ${bold}firecrawl setup mcp${reset}` - ); - console.log( - ` ${dim}Run${reset} ${bold}firecrawl --help${reset} ${dim}for the full command reference.${reset}` + ` ${arrow} ${dim}All commands:${reset} ${bold}firecrawl --help${reset}` ); console.log(''); } @@ -290,7 +285,7 @@ async function stepAuth(options: InitOptions): Promise { } } -async function stepIntegrations(options: InitOptions): Promise { +async function stepIntegrations(options: InitOptions): Promise { const { checkbox, confirm } = await import('@inquirer/prompts'); const wantIntegrations = await confirm({ @@ -298,7 +293,7 @@ async function stepIntegrations(options: InitOptions): Promise { default: true, }); - if (!wantIntegrations) return; + if (!wantIntegrations) return null; const integrations = await checkbox({ message: 'Which integrations?', @@ -321,16 +316,18 @@ async function stepIntegrations(options: InitOptions): Promise { if (integrations.length === 0) { console.log(` ${dim}No integrations selected.${reset}\n`); - return; + return null; } + let totalSkills: number | null = null; for (const integration of integrations) { switch (integration) { case 'skills': { console.log(`\n Installing skills...`); for (const repo of SKILL_REPOS) { try { - await installSkillRepoQuiet(repo, options); + const count = await installSkillRepoQuiet(repo, options); + if (count != null) totalSkills = (totalSkills ?? 0) + count; } catch { console.error( ` ${dim}Run "firecrawl setup skills" later to retry.${reset}` @@ -384,7 +381,7 @@ async function stepIntegrations(options: InitOptions): Promise { } } } - console.log(''); + return totalSkills; } function copyTemplateFiles( @@ -642,14 +639,15 @@ export async function handleInitCommand( } // Step 3: Integrations (skills, MCP, env) + let skillCount: number | null = null; if (!options.skipSkills) { - await stepIntegrations(options); + skillCount = await stepIntegrations(options); } // Step 4: Template await stepTemplate(); - printNextSteps(); + printNextSteps(skillCount); } async function runNonInteractive(options: InitOptions): Promise { @@ -718,13 +716,15 @@ async function runNonInteractive(options: InitOptions): Promise { } } + let skillCount: number | null = null; if (!options.skipSkills) { console.log( `${stepLabel()} Installing firecrawl skills for AI coding agents...` ); for (const repo of SKILL_REPOS) { try { - await installSkillRepoQuiet(repo, options); + const count = await installSkillRepoQuiet(repo, options); + if (count != null) skillCount = (skillCount ?? 0) + count; } catch { console.error( `\n${dim}Failed to install skills from ${repo}. Retry with: firecrawl setup skills${reset}` @@ -734,5 +734,5 @@ async function runNonInteractive(options: InitOptions): Promise { } } - printNextSteps(); + printNextSteps(skillCount); }