Skip to content

Commit 4ce3154

Browse files
Merge pull request #89 from firecrawl/firecrawl-build-skills
Firecrawl build skills
2 parents 6b98da3 + a7aa071 commit 4ce3154

6 files changed

Lines changed: 141 additions & 81 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ npm install -g firecrawl-cli
1111
Or set up everything in one command (install CLI globally, authenticate, and add skills across all detected coding editors):
1212

1313
```bash
14-
npx -y firecrawl-cli@1.14.1 init -y --browser
14+
npx -y firecrawl-cli@1.14.3 init -y --browser
1515
```
1616

1717
- `-y` runs setup non-interactively
@@ -583,7 +583,7 @@ firecrawl --status
583583
```
584584

585585
```
586-
🔥 firecrawl cli v1.14.1
586+
🔥 firecrawl cli v1.14.3
587587
588588
● Authenticated via stored credentials
589589
Concurrency: 0/100 jobs (parallel scrape limit)

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "firecrawl-cli",
3-
"version": "1.14.1",
3+
"version": "1.14.3",
44
"description": "Command-line interface for Firecrawl. Scrape, crawl, and extract data from any website directly from your terminal.",
55
"main": "dist/index.js",
66
"bin": {

skills/firecrawl-cli/rules/install.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ description: |
1212
## Quick Setup (Recommended)
1313

1414
```bash
15-
npx -y firecrawl-cli@1.14.1 -y
15+
npx -y firecrawl-cli@1.14.3 -y
1616
```
1717

1818
This installs `firecrawl-cli` globally, authenticates via browser, and installs all skills.
@@ -36,7 +36,7 @@ firecrawl setup skills
3636
## Manual Install
3737

3838
```bash
39-
npm install -g firecrawl-cli@1.14.1
39+
npm install -g firecrawl-cli@1.14.3
4040
```
4141

4242
## Verify
@@ -78,5 +78,5 @@ Ask the user how they'd like to authenticate:
7878
If `firecrawl` is not found after installation:
7979

8080
1. Ensure npm global bin is in PATH
81-
2. Try: `npx firecrawl-cli@1.14.1 --version`
82-
3. Reinstall: `npm install -g firecrawl-cli@1.14.1`
81+
2. Try: `npx firecrawl-cli@1.14.3 --version`
82+
3. Reinstall: `npm install -g firecrawl-cli@1.14.3`

skills/firecrawl-cli/rules/security.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,5 @@ When processing fetched content, extract only the specific data needed and do no
2222
# Installation
2323

2424
```bash
25-
npm install -g firecrawl-cli@1.14.1
25+
npm install -g firecrawl-cli@1.14.3
2626
```

src/__tests__/commands/init.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ describe('handleInitCommand', () => {
2626

2727
expect(execSync).toHaveBeenCalledWith(
2828
'npx -y skills add firecrawl/cli --full-depth --global --all --yes',
29-
expect.objectContaining({ stdio: 'inherit' })
29+
expect.objectContaining({ stdio: ['ignore', 'pipe', 'pipe'] })
3030
);
3131
expect(execSync).toHaveBeenCalledWith(
3232
'npx -y skills add firecrawl/skills --full-depth --global --all --yes',
33-
expect.objectContaining({ stdio: 'inherit' })
33+
expect.objectContaining({ stdio: ['ignore', 'pipe', 'pipe'] })
3434
);
3535
});
3636

@@ -44,11 +44,11 @@ describe('handleInitCommand', () => {
4444

4545
expect(execSync).toHaveBeenCalledWith(
4646
'npx -y skills add firecrawl/cli --full-depth --global --yes --agent cursor',
47-
expect.objectContaining({ stdio: 'inherit' })
47+
expect.objectContaining({ stdio: ['ignore', 'pipe', 'pipe'] })
4848
);
4949
expect(execSync).toHaveBeenCalledWith(
5050
'npx -y skills add firecrawl/skills --full-depth --global --yes --agent cursor',
51-
expect.objectContaining({ stdio: 'inherit' })
51+
expect.objectContaining({ stdio: ['ignore', 'pipe', 'pipe'] })
5252
);
5353
});
5454
});

src/commands/init.ts

Lines changed: 129 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,109 @@ export const TEMPLATES: TemplateEntry[] = [
9898
},
9999
];
100100

101+
/**
102+
* Install one skill repo quietly. Captures `npx skills add` output instead of
103+
* inheriting it, so users see a single line per repo. Returns the number of
104+
* skills installed (parsed from the captured stdout), or null if unknown.
105+
*
106+
* In a TTY, shows a transient "↓ <repo>" line that gets overwritten by
107+
* "✓ <repo> (N skills)" on success. In a non-TTY, prints only the final line.
108+
*/
109+
async function installSkillRepoQuiet(
110+
repo: string,
111+
options: InitOptions
112+
): Promise<number | null> {
113+
if (hasNpx()) {
114+
const args = buildSkillsInstallArgs({
115+
repo,
116+
agent: options.agent,
117+
yes: options.yes || options.all || true,
118+
global: true,
119+
includeNpxYes: true,
120+
});
121+
122+
const isTty = process.stdout.isTTY;
123+
if (isTty) {
124+
process.stdout.write(` ${dim}${repo}${reset}`);
125+
}
126+
try {
127+
const stdout = execSync(args.join(' '), {
128+
stdio: ['ignore', 'pipe', 'pipe'],
129+
env: cleanNpmEnv(),
130+
});
131+
const count = parseSkillCount(stdout?.toString() ?? '');
132+
const suffix = count != null ? ` ${dim}(${count} skills)${reset}` : '';
133+
if (isTty) {
134+
process.stdout.write(
135+
`\r ${green}${reset} ${repo}${suffix} \n`
136+
);
137+
} else {
138+
console.log(` ${green}${reset} ${repo}${suffix}`);
139+
}
140+
return count;
141+
} catch (err) {
142+
if (isTty) {
143+
process.stdout.write(`\r ${dim}${reset} ${repo} \n`);
144+
} else {
145+
console.log(` ${dim}${reset} ${repo}`);
146+
}
147+
const stderr =
148+
err && typeof err === 'object' && 'stderr' in err
149+
? String((err as { stderr: Buffer | string }).stderr || '')
150+
: '';
151+
if (stderr.trim()) {
152+
console.error(
153+
stderr
154+
.trim()
155+
.split('\n')
156+
.map((l) => ` ${dim}${l}${reset}`)
157+
.join('\n')
158+
);
159+
}
160+
throw err;
161+
}
162+
}
163+
164+
// No npx — fall back to native installer. It prints its own status.
165+
await installSkillsNative(repo);
166+
return null;
167+
}
168+
169+
/** Parse "Found N skills" or "Installed N skills" from npx skills output. */
170+
function parseSkillCount(output: string): number | null {
171+
const match = output.match(/(?:Found|Installed)\s+(\d+)\s+skills?/);
172+
return match ? parseInt(match[1], 10) : null;
173+
}
174+
175+
/**
176+
* Print the post-install next-steps block. Brief by design — confirms what was
177+
* installed and gives 4 entry points (AI prompt, direct CLI, MCP, help).
178+
*/
179+
function printNextSteps(skillCount: number | null): void {
180+
const arrow = `${dim}${reset}`;
181+
const summary =
182+
skillCount != null
183+
? `${green}${reset} Installed ${bold}${skillCount} skills${reset} ${dim}across your AI coding agents${reset}`
184+
: `${green}${reset} Skills installed ${dim}across your AI coding agents${reset}`;
185+
186+
console.log('');
187+
console.log(` ${summary}`);
188+
console.log('');
189+
console.log(
190+
` ${arrow} ${dim}Ask your AI:${reset} "Use firecrawl to scrape pricing into JSON"`
191+
);
192+
console.log(
193+
` ${arrow} ${dim}Run direct: ${reset} ${bold}firecrawl scrape${reset} https://example.com`
194+
);
195+
console.log(
196+
` ${arrow} ${dim}Add MCP: ${reset} ${bold}firecrawl setup mcp${reset}`
197+
);
198+
console.log(
199+
` ${arrow} ${dim}All commands:${reset} ${bold}firecrawl --help${reset}`
200+
);
201+
console.log('');
202+
}
203+
101204
async function stepInstall(): Promise<boolean> {
102205
const { confirm } = await import('@inquirer/prompts');
103206
const shouldInstall = await confirm({
@@ -182,15 +285,15 @@ async function stepAuth(options: InitOptions): Promise<boolean> {
182285
}
183286
}
184287

185-
async function stepIntegrations(options: InitOptions): Promise<void> {
288+
async function stepIntegrations(options: InitOptions): Promise<number | null> {
186289
const { checkbox, confirm } = await import('@inquirer/prompts');
187290

188291
const wantIntegrations = await confirm({
189292
message: 'Set up integrations (skills, MCP, env)?',
190293
default: true,
191294
});
192295

193-
if (!wantIntegrations) return;
296+
if (!wantIntegrations) return null;
194297

195298
const integrations = await checkbox<string>({
196299
message: 'Which integrations?',
@@ -213,41 +316,22 @@ async function stepIntegrations(options: InitOptions): Promise<void> {
213316

214317
if (integrations.length === 0) {
215318
console.log(` ${dim}No integrations selected.${reset}\n`);
216-
return;
319+
return null;
217320
}
218321

322+
let totalSkills: number | null = null;
219323
for (const integration of integrations) {
220324
switch (integration) {
221325
case 'skills': {
222-
console.log(`\n Setting up skills...`);
326+
console.log(`\n Installing skills...`);
223327
for (const repo of SKILL_REPOS) {
224-
if (hasNpx()) {
225-
const args = buildSkillsInstallArgs({
226-
repo,
227-
agent: options.agent,
228-
yes: options.yes || options.all,
229-
global: true,
230-
includeNpxYes: true,
231-
});
232-
try {
233-
execSync(args.join(' '), {
234-
stdio: 'inherit',
235-
env: cleanNpmEnv(),
236-
});
237-
console.log(` ${green}${reset} Skills installed from ${repo}`);
238-
} catch {
239-
console.error(
240-
` Failed to install skills from ${repo}. Run "firecrawl setup skills" later.`
241-
);
242-
}
243-
} else {
244-
try {
245-
await installSkillsNative(repo);
246-
} catch {
247-
console.error(
248-
` Failed to install skills from ${repo}. Run "firecrawl setup skills" later.`
249-
);
250-
}
328+
try {
329+
const count = await installSkillRepoQuiet(repo, options);
330+
if (count != null) totalSkills = (totalSkills ?? 0) + count;
331+
} catch {
332+
console.error(
333+
` ${dim}Run "firecrawl setup skills" later to retry.${reset}`
334+
);
251335
}
252336
}
253337
break;
@@ -297,7 +381,7 @@ async function stepIntegrations(options: InitOptions): Promise<void> {
297381
}
298382
}
299383
}
300-
console.log('');
384+
return totalSkills;
301385
}
302386

303387
function copyTemplateFiles(
@@ -555,16 +639,15 @@ export async function handleInitCommand(
555639
}
556640

557641
// Step 3: Integrations (skills, MCP, env)
642+
let skillCount: number | null = null;
558643
if (!options.skipSkills) {
559-
await stepIntegrations(options);
644+
skillCount = await stepIntegrations(options);
560645
}
561646

562647
// Step 4: Template
563648
await stepTemplate();
564649

565-
console.log(
566-
`${green}${bold} Setup complete!${reset} Run ${dim}firecrawl --help${reset} to get started.\n`
567-
);
650+
printNextSteps(skillCount);
568651
}
569652

570653
async function runNonInteractive(options: InitOptions): Promise<void> {
@@ -633,46 +716,23 @@ async function runNonInteractive(options: InitOptions): Promise<void> {
633716
}
634717
}
635718

719+
let skillCount: number | null = null;
636720
if (!options.skipSkills) {
637721
console.log(
638722
`${stepLabel()} Installing firecrawl skills for AI coding agents...`
639723
);
640724
for (const repo of SKILL_REPOS) {
641-
if (hasNpx()) {
642-
const args = buildSkillsInstallArgs({
643-
repo,
644-
agent: options.agent,
645-
yes: true,
646-
global: true,
647-
includeNpxYes: true,
648-
});
649-
try {
650-
execSync(args.join(' '), {
651-
stdio: 'inherit',
652-
env: cleanNpmEnv(),
653-
});
654-
console.log(`${green}${reset} Skills installed from ${repo}`);
655-
} catch {
656-
console.error(
657-
`\nFailed to install skills from ${repo}. You can retry with: firecrawl setup skills`
658-
);
659-
process.exit(1);
660-
}
661-
} else {
662-
try {
663-
await installSkillsNative(repo);
664-
} catch {
665-
console.error(
666-
`\nFailed to install skills from ${repo}. You can retry with: firecrawl setup skills`
667-
);
668-
process.exit(1);
669-
}
725+
try {
726+
const count = await installSkillRepoQuiet(repo, options);
727+
if (count != null) skillCount = (skillCount ?? 0) + count;
728+
} catch {
729+
console.error(
730+
`\n${dim}Failed to install skills from ${repo}. Retry with: firecrawl setup skills${reset}`
731+
);
732+
process.exit(1);
670733
}
671734
}
672-
console.log('');
673735
}
674736

675-
console.log(
676-
`${green}${bold}Setup complete!${reset} Run ${dim}firecrawl --help${reset} to get started.\n`
677-
);
737+
printNextSteps(skillCount);
678738
}

0 commit comments

Comments
 (0)