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
27 changes: 25 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.16.2 init -y --browser
npx -y firecrawl-cli@1.19.3 init -y --browser
```

- `-y` runs setup non-interactively
Expand Down Expand Up @@ -50,6 +50,29 @@ To install the Firecrawl MCP server into your editors (Cursor, Claude Code, VS C
firecrawl setup mcp
```

To make Firecrawl the default web provider for supported AI agents:

```bash
firecrawl setup defaults
```

This disables native web fetch/search where supported so agents route web work
through Firecrawl. When run interactively, it asks harness by harness (Claude
Code, Codex) so you can choose exactly which to change. Use `-y` to skip the
picker and apply to all, or `--agent` to target one:

```bash
firecrawl setup defaults --agent codex # only Codex
firecrawl setup defaults -y # all harnesses, no prompts
```

To undo those config changes (also interactive, harness by harness):

```bash
firecrawl setup defaults --undo # pick which to restore
firecrawl setup defaults --undo --agent claude
```

## Quick Start

Just run a command - the CLI will prompt you to authenticate if needed:
Expand Down Expand Up @@ -652,7 +675,7 @@ firecrawl --status
```

```
🔥 firecrawl cli v1.16.2
🔥 firecrawl cli v1.19.3

● Authenticated via stored credentials
Concurrency: 0/100 jobs (parallel scrape limit)
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "firecrawl-cli",
"version": "1.19.2",
"version": "1.19.3",
"description": "Command-line interface for Firecrawl. Scrape, crawl, and extract data from any website directly from your terminal.",
"main": "dist/index.js",
"bin": {
Expand Down
8 changes: 4 additions & 4 deletions skills/firecrawl-cli/rules/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ description: |
## Quick Setup (Recommended)

```bash
npx -y firecrawl-cli@1.16.2 init -y --browser
npx -y firecrawl-cli@1.19.3 init -y --browser
```

This installs `firecrawl-cli` globally, authenticates via browser, and installs core, build, and workflow skills.
Expand All @@ -37,7 +37,7 @@ firecrawl setup workflows
## Manual Install

```bash
npm install -g firecrawl-cli@1.16.2
npm install -g firecrawl-cli@1.19.3
```

## Verify
Expand Down Expand Up @@ -79,5 +79,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.16.2 --version`
3. Reinstall: `npm install -g firecrawl-cli@1.16.2`
2. Try: `npx firecrawl-cli@1.19.3 --version`
3. Reinstall: `npm install -g firecrawl-cli@1.19.3`
2 changes: 1 addition & 1 deletion skills/firecrawl-cli/rules/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.16.2
npm install -g firecrawl-cli@1.19.3
```
32 changes: 32 additions & 0 deletions src/__tests__/commands/setup.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { execSync } from 'child_process';
import { handleSetupCommand } from '../../commands/setup';
import { configureWebDefaults } from '../../utils/web-defaults';

vi.mock('child_process', () => ({
execSync: vi.fn(),
}));

vi.mock('../../utils/web-defaults', () => ({
configureWebDefaults: vi.fn(async () => []),
}));

describe('handleSetupCommand', () => {
beforeEach(() => {
vi.clearAllMocks();
Expand Down Expand Up @@ -50,6 +55,33 @@ describe('handleSetupCommand', () => {
);
});

it('configures Firecrawl as the default web provider', async () => {
await handleSetupCommand('defaults', { yes: true });

expect(configureWebDefaults).toHaveBeenCalledWith({
undo: false,
agents: undefined,
});
});

it('undoes default web provider config', async () => {
await handleSetupCommand('defaults', { undo: true, yes: true });

expect(configureWebDefaults).toHaveBeenCalledWith({
undo: true,
agents: undefined,
});
});

it('limits defaults config to a single agent', async () => {
await handleSetupCommand('defaults', { undo: true, agent: 'codex' });

expect(configureWebDefaults).toHaveBeenCalledWith({
undo: true,
agents: ['Codex'],
});
});

it('strips inherited npm_* env vars before nested npx calls', async () => {
// Reproduces the bug where running this CLI under `npx -y firecrawl-cli@VERSION`
// leaks npm_command/npm_lifecycle_event/npm_execpath into nested
Expand Down
122 changes: 122 additions & 0 deletions src/__tests__/utils/web-defaults.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { promises as fs } from 'fs';
import os from 'os';
import path from 'path';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { configureWebDefaults } from '../../utils/web-defaults';

const originalHome = process.env.HOME;
let tempHome: string;

async function read(relativePath: string): Promise<string> {
return fs.readFile(path.join(tempHome, relativePath), 'utf8');
}

async function write(relativePath: string, content: string): Promise<void> {
const filePath = path.join(tempHome, relativePath);
await fs.mkdir(path.dirname(filePath), { recursive: true });
await fs.writeFile(filePath, content, 'utf8');
}

describe('configureWebDefaults', () => {
beforeEach(async () => {
tempHome = await fs.mkdtemp(path.join(os.tmpdir(), 'firecrawl-web-'));
process.env.HOME = tempHome;
});

afterEach(async () => {
if (originalHome === undefined) delete process.env.HOME;
else process.env.HOME = originalHome;
await fs.rm(tempHome, { recursive: true, force: true });
});

it('disables native Claude Code and Codex web tools', async () => {
const results = await configureWebDefaults();

expect(results.map((result) => result.changed)).toEqual([true, true]);
expect(JSON.parse(await read('.claude/settings.json'))).toEqual({
permissions: {
deny: ['WebSearch', 'WebFetch'],
},
});
expect(await read('.codex/config.toml')).toBe('web_search = "disabled"\n');
});

it('preserves existing Claude permissions and Codex config while disabling web', async () => {
await write(
'.claude/settings.json',
JSON.stringify({
permissions: {
allow: ['Read'],
deny: ['Bash(rm *)', 'WebSearch'],
},
})
);
await write(
'.codex/config.toml',
'model = "gpt-5"\nweb_search = "cached"\n'
);

await configureWebDefaults();

expect(JSON.parse(await read('.claude/settings.json'))).toEqual({
permissions: {
allow: ['Read'],
deny: ['Bash(rm *)', 'WebSearch', 'WebFetch'],
},
});
expect(await read('.codex/config.toml')).toBe(
'model = "gpt-5"\nweb_search = "disabled"\n'
);
});

it('undoes only the native web defaults', async () => {
await write(
'.claude/settings.json',
JSON.stringify({
permissions: {
deny: ['Bash(rm *)', 'WebSearch', 'WebFetch'],
},
})
);
await write(
'.codex/config.toml',
'model = "gpt-5"\nweb_search = "disabled"\n'
);

const results = await configureWebDefaults({ undo: true });

expect(results.map((result) => result.changed)).toEqual([true, true]);
expect(JSON.parse(await read('.claude/settings.json'))).toEqual({
permissions: {
deny: ['Bash(rm *)'],
},
});
expect(await read('.codex/config.toml')).toBe('model = "gpt-5"\n');
});

it('writes Codex web_search at the root before TOML tables', async () => {
await write(
'.codex/config.toml',
'model = "gpt-5"\n\n[mcp_servers.firecrawl]\ncommand = "npx"\n'
);

await configureWebDefaults();

expect(await read('.codex/config.toml')).toBe(
'model = "gpt-5"\n\nweb_search = "disabled"\n[mcp_servers.firecrawl]\ncommand = "npx"\n'
);
});

it('does not undo table-local Codex web_search settings', async () => {
await write(
'.codex/config.toml',
'model = "gpt-5"\n\n[profiles.research]\nweb_search = "disabled"\n'
);

await configureWebDefaults({ undo: true });

expect(await read('.codex/config.toml')).toBe(
'model = "gpt-5"\n\n[profiles.research]\nweb_search = "disabled"\n'
);
});
});
3 changes: 3 additions & 0 deletions src/commands/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@ function printNextSteps(skillCount: number | null): void {
console.log(
` ${arrow} ${dim}Add MCP: ${reset} ${bold}firecrawl setup mcp${reset}`
);
console.log(
` ${arrow} ${dim}Default web:${reset} ${bold}firecrawl setup defaults${reset}`
);
console.log(
` ${arrow} ${dim}All commands:${reset} ${bold}firecrawl --help${reset}`
);
Expand Down
Loading
Loading