Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
0fd3b89
Refactored `configuredPlugins` to use a record-based structure instea…
jamius19 Jun 12, 2026
bb11db3
Added plugin context information README.md
jamius19 Jun 12, 2026
777e285
WIP Used the parse plugin in brightdata-fetch plugin to clean up the …
jamius19 Jun 12, 2026
24d9603
Refactored HTML to Markdown parse plugin
jamius19 Jun 12, 2026
6e93f28
Integrated parse plugin into brightdata-fetch tests
jamius19 Jun 12, 2026
da3609e
Added TS-ignore comments to plugin-loader tests to bypass unnecessary…
jamius19 Jun 12, 2026
120d2bc
Added `builtin-crawl4ai-fetch` plugin with headless browser configura…
jamius19 Jun 12, 2026
ec0a4aa
Removed fallback fetch plugin logic from `builtin-crawl4ai-fetch` plugin
jamius19 Jun 12, 2026
4945d75
Removed `Result` interface export from `builtin-crawl4ai-fetch` plugin
jamius19 Jun 12, 2026
0f3dd70
Made `results` optional and improved error handling in `builtin-crawl…
jamius19 Jun 12, 2026
d7ce773
Added tests for `builtin-crawl4ai-fetch` plugin and updated plugin re…
jamius19 Jun 12, 2026
13df600
Fixed context passing in tests involving plugins
jamius19 Jun 12, 2026
9f10b58
Updated README.md and CLAUDE.md
jamius19 Jun 12, 2026
59b91fd
Improved error handling and logging in `builtin-crawl4ai-fetch` plugin
jamius19 Jun 12, 2026
d62815f
Added `builtin-searxng-search` plugin with test coverage and document…
jamius19 Jun 12, 2026
4b8edf6
Removed localized date prefix from search plugins results
jamius19 Jun 12, 2026
510d627
Limited `builtin-searxng-search` plugin results to a maximum of 10
jamius19 Jun 12, 2026
e1b352f
Added `builtin-alterlab-fetch` plugin with test coverage and document…
jamius19 Jun 13, 2026
e3f04dd
Added `builtin-alterlab-search` plugin with test coverage and documen…
jamius19 Jun 13, 2026
b44c15d
Added `builtin-firecrawl-search` plugin with test coverage and docume…
jamius19 Jun 13, 2026
d1ba3e3
Added `collapseBlankLines` util function with tests and refactored it…
jamius19 Jun 13, 2026
70b8d11
Added `builtin-firecrawl-fetch` plugin with test coverage and documen…
jamius19 Jun 13, 2026
a7fc461
Added global search results limiting feature and updated tests
jamius19 Jun 13, 2026
b6bc095
Refactored search plugins to use `shouldShowSearchDescription` utilit…
jamius19 Jun 13, 2026
643a317
Split the documentation for configuration, plugin development, and co…
jamius19 Jun 13, 2026
4191f89
Removed License section from README
jamius19 Jun 13, 2026
09d86ea
Fixed typo in README.md under `ask` command description
jamius19 Jun 13, 2026
645ded2
Added quickstart section on README and updated default search and fet…
jamius19 Jun 13, 2026
8dd3c1b
Updated README to clarify default search and fetch plugins and added …
jamius19 Jun 13, 2026
521a7c6
Clarified wording in README for default search and fetch plugins
jamius19 Jun 13, 2026
33ad5fe
Updated CLAUDE.md
jamius19 Jun 13, 2026
34b65c7
Standardized capitalization of "Sibyl" across documentation files
jamius19 Jun 13, 2026
d5680d5
Fixed review comments
jamius19 Jun 13, 2026
0d25375
Added codecov configuration file to enforce coverage thresholds
jamius19 Jun 13, 2026
a80f7d9
Added null title handling for search plugins
jamius19 Jun 13, 2026
018fa6e
Added test for attribute allowlist handling in HTML-to-Markdown conve…
jamius19 Jun 13, 2026
380c87b
Updated Codecov config to enable comment behavior and require changes
jamius19 Jun 13, 2026
b579c38
Typed `fetchFn` return values in `builtin-exa-fetch` and `builtin-bri…
jamius19 Jun 13, 2026
0292001
Updated Codecov badge in README to include token parameter
jamius19 Jun 13, 2026
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
21 changes: 13 additions & 8 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,25 +29,29 @@ Follow these rules when editing code in this project.

`sibyl` is a CLI web search/crawl tool for AI Agents (`bin: sibyl` → `dist/cli.js`) with a filesystem-based plugin system. Key modules:

- `src/cli.ts` — entry point. Ensures dirs + config exist, loads plugins, dispatches commands (`search`, `fetch`, `help`/`--help`/`-h`, `version`/`--version`). Only `search` and `fetch` are wired up via the async `handleSearch`/`handleFetch` helpers (awaited by `main`); `fetch` prints the fetch plugin's output directly (it no longer runs a `parse` plugin), so the `ask` and `parse` plugin types are part of the contract but not yet dispatched by any command. `main` is exported and only auto-runs when the file is the actual CLI entry (`import.meta.url` vs `process.argv[1]` guard), so tests can import it without side effects.
- `src/cli.ts` — entry point. Ensures dirs + config exist, loads plugins, builds a `PluginContext` (`buildPluginContext`), and dispatches commands (`search`, `fetch`, `help`/`--help`/`-h`, `version`/`--version`). Only `search` and `fetch` are wired up via the async `handleSearch`/`handleFetch` helpers (awaited by `main`), each passing the context as the last arg to the selected plugin's `fn`. The `fetch` command prints the fetch plugin's output directly — the CLI doesn't dispatch a separate `parse` step, but a fetch plugin may itself run the configured parse plugin via `context.configuredPlugins.parse` (`builtin-brightdata-fetch`, `builtin-crawl4ai-fetch`, and `builtin-alterlab-fetch` do; `builtin-firecrawl-fetch` does only in its raw-HTML mode; `builtin-exa-fetch` returns content as-is). `ask` is part of the contract but not dispatched by any command. `main` is exported and only auto-runs when the file is the actual CLI entry (`import.meta.url` vs `process.argv[1]` guard), so tests can import it without side effects.
- `src/setup.ts` — ensures `~/.sibyl` and `~/.sibyl/plugins` exist, and loads/creates/validates `~/.sibyl/config.json` (all on every invocation).
- `src/plugin-loader.ts` — assembles the active plugin set: builtin plugins + external (on-disk) plugins; validates the external ones.
- `src/plugins/config.ts` — `getBuiltinPlugins()`, the in-repo builtin plugin registry.
- `src/utils.ts` — pure helpers (`isValidHttpUrl`).
- `src/utils.ts` — pure helpers: `isValidHttpUrl`, `stripSearchResultDatePrefix` (strips localized SERP date prefixes), `collapseBlankLines`, and the search-setting readers `getSearchResultsLimit` / `shouldShowSearchDescription` (see Conventions).
- `src/exit.ts` — `exit()`, the single wrapper around `process.exit` (see Conventions).
- `src/@types/` — `plugin.ts` (plugin contract) and `sibyl-config.ts` (config shape).

User-facing docs live in `docs/` — `CONFIGURATION.md` (config + per-plugin env-var tables), `CREATING-PLUGINS.md`, and `CONTRIBUTION.md` (linked from `README.md`).

### Plugin system (the core concept)

Plugins live in `~/.sibyl/plugins/<name>/main.js` (note: `.js`, loaded at runtime via dynamic `import()`). A plugin module must provide a **single export** named `SilbylPlugin` (spelling is part of the contract) — a declaration object with three fields:

1. `name: string` — non-empty, identifies the plugin.
2. `type: "search" | "fetch" | "ask" | "parse"`.
3. `fn` — the function implementing the plugin's logic. Its signature depends on `type` (`src/@types/plugin.ts`):
- `search`: `(query) => Promise<string>`
- `fetch`: `(url) => Promise<string>`
- `ask`: `(parsedContent, query) => Promise<string>`
- `parse`: `(html) => Promise<string>`
3. `fn` — the function implementing the plugin's logic. Every `fn` receives a `PluginContext` as its **last** argument; its signature otherwise depends on `type` (`src/@types/plugin.ts`):
- `search`: `(query, context) => Promise<string>`
- `fetch`: `(url, context) => Promise<string>`
- `ask`: `(parsedContent, query, context) => Promise<string>`
- `parse`: `(html, context) => Promise<string>`

`PluginContext` (`src/@types/plugin.ts`) lets a plugin reach the rest of the plugin system: `{ configuredPlugins: Partial<Record<PluginType, PluginTypeDeclaration>>, allPlugins: PluginTypeDeclaration[], getPlugin(name): PluginTypeDeclaration | null }`. `configuredPlugins` is keyed by type (the per-type selection from config), `allPlugins` is everything loaded, and `getPlugin` looks up by name. It's built once in `cli.ts` and threaded to every `fn`; plugins consume it only if needed (a 1-arg `fn` still satisfies the contract via structural typing). This is how a fetch plugin runs the configured parser: `context.configuredPlugins.parse?.fn(html, context)`.

Key detail: `fn` is a **field of `SilbylPlugin`**, so the loader validates and parses a single export. The external `SilbylPlugin` is structurally identical to the internal `PluginTypeDeclaration` `{ name, type, fn }`.

Expand All @@ -69,7 +73,7 @@ When changing the plugin shape, update all three together: `src/@types/plugin.ts

Shape: `SibylConfig` (`src/@types/sibyl-config.ts`) — `{ plugins: Partial<Record<PluginType, string>>, variables: { name, value }[] }`. `plugins` maps `type` → plugin name (e.g. `{ "search": "builtin-exa-search" }`); keying by type structurally enforces at most one plugin per type. `variables` is a list of `{ name, value }` pairs injected into `process.env`.

- `loadOrCreateConfigFile()` (`setup.ts`) writes a default config (`writeDefaultSibylConfig`) when the file is missing or empty, then parses, validates, and injects variables.
- `loadOrCreateConfigFile()` (`setup.ts`) writes a default config (`writeDefaultSibylConfig`) when the file is missing or empty, then parses, validates, and injects variables. The default selects `builtin-searxng-search` / `builtin-crawl4ai-fetch` / `builtin-parse-htmlToMd` with no `variables` (fully local, no-API-key backends).
- `injectConfigVariables()` (`setup.ts`) sets `process.env[name] = value` for each config variable. **Config wins over the environment** — a variable named in config overrides any existing env var; names absent from config fall back to their existing env value. (Plugins like `builtin-exa-search` read `process.env.EXA_API_KEY` at call time, so they pick up either source.)
- `validateConfig()` checks each entry's name is a non-empty string; on failure it `console.error`s and `process.exit(1)` (hard exit, not a skip-with-warning like plugin loading).
- Plugin selection: `loadPlugins()` loads _all_ available plugins (builtins + disk), then `cli.ts` picks the one to run **by name from config** — e.g. the `search` command looks up `config.plugins.search` and finds the loaded plugin whose `type === "search"` and `name` matches. Missing config entry or no matching loaded plugin → `console.error` + non-zero exit.
Expand All @@ -80,3 +84,4 @@ Shape: `SibylConfig` (`src/@types/sibyl-config.ts`) — `{ plugins: Partial<Reco
- TypeScript is strict, plus `noUncheckedIndexedAccess` and `exactOptionalPropertyTypes` — index access can be `undefined` and optional props can't be assigned `undefined` explicitly.
- `allowImportingTsExtensions` + `rewriteRelativeImportExtensions` are on, so source uses explicit extensions on relative imports and `tsc` rewrites them on build. Existing imports are inconsistent — some `.ts`, one `.js` (`./loader.js`); match the file you're editing.
- File header comment block on each module: `Author: Jamius Siam` / `Since: <date>`.
- Search plugins read two shared settings via `src/utils.ts` helpers — `getSearchResultsLimit()` (`SIBYL_SEARCH_RESULTS_LIMIT`, default `10`; passed to the provider's API when it supports a result-count param, and the results array is always `.slice(0, limit)`d) and `shouldShowSearchDescription()` (`SIBYL_SHOW_SEARCH_DESCRIPTION`, default `true`). New search builtins should use both.
Loading
Loading