Skip to content

Pass knowledge, endpoint and spec to a run without a config file - #140

Open
DavertMik wants to merge 9 commits into
mainfrom
knowledge-as-option
Open

Pass knowledge, endpoint and spec to a run without a config file#140
DavertMik wants to merge 9 commits into
mainfrom
knowledge-as-option

Conversation

@DavertMik

@DavertMik DavertMik commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

--knowledge takes facts on the command line and keeps them in memory for that run only, so credentials and one-off test data never land in knowledge/.

npx explorbot explore /pay --knowledge 'my credit card is 13213213213'

Plain text applies everywhere. Frontmatter scopes it the way a knowledge file does — url: for a page, endpoint: for an API endpoint — and the rest of the file grammar follows, including wait/waitForElement and ${env.VAR} interpolation:

npx explorbot explore / --knowledge '---
url: /login
---
Log in as admin@example.com / secret123'

The flag repeats, since gray-matter reads one frontmatter block per string, so several scoped facts need several flags.

How it works

It registers once on the program root, the way --ws does, and both now live in the same place: src/commands/options/, one BaseOption subclass per run-level flag — flags, description, an optional collect for a repeatable value, and an apply() that runs after parsing, behind a public register(program) a bin calls. A bin registers what it offers, so prima keeps its surface while the others take both flags. apply() hands the value to its owner and stops: --ws to remote.attach(), --knowledge to config, beside EXPLORBOT_KNOWLEDGE and materializeKnowledge, where the run's inputs are resolved whatever they arrive on. KnowledgeTracker asks config for what the flag collected and knows nothing about a command line. Commander merges parent options down through optsWithGlobals, so that single registration covers every command — the mounted api, docs and prima subcommands included — and the flag can sit anywhere on the line: before the command, after it, or after variadic arguments. Nothing in ExplorBot, Prima, DocBot or ApiBot carries a knowledge option; the three standalone boat bins get one registration line each.

KnowledgeTracker holds the parsed entries beside the ones it loads from disk and matches both through the same structural patterns — no new matching path. Its constructor takes an optional override, which is what the tests use and what keeps the module state a fallback rather than the only way in.

Session entries stay out of knows and listAllKnowledge, which report what is on disk. They do show in /context:knowledge, listed as --knowledge #1.

The API boat now reads knowledge

knowledge/ was never read at runtime by the API boat, so endpoint knowledge written by api know sat unused. Chief now loads it when planning an endpoint and Curler when testing one — which is what makes --knowledge useful for auth on the API side.

This changes behaviour for existing users: endpoint: files already on disk, and EXPLORBOT_KNOWLEDGE, now appear in Chief and Curler prompts where they previously did not.

Breaking: drill --knowledge renamed

explorbot drill --knowledge <path> is now --save-knowledge <path>, in the CLI and in the TUI (/drill --save-knowledge). It saves what drilling learned rather than supplying facts, and the two cannot share a flag on the same command.

Checks

bun test tests/unit (1084), tests/integration (80), boat/prima/tests (131), node --test tests/node/*.mjs (10), format and lint all pass. Eight new unit tests cover session parsing — no-frontmatter reaching every page and endpoint, url: and endpoint: scoping, several entries staying independent, frontmatter hints reaching getStateParameters, ${env.VAR} interpolation, nothing written to the knowledge directory — plus a round trip through the registered option proving the flag reaches a freshly built tracker.

This one is worth a regression run before merge — it changes what reaches Chief and Curler prompts on every API run.


Also: endpoint and spec from the command line

The knowledge flag closes one of the three inputs a run needs. A config file was still the only way to say where the app is and what documentation describes it, so these commits close the other two in the same shape: a flag, an environment twin, and no file required.

API boat

explorbot api plan /users \
  --endpoint https://api.example.com/v1 \
  --spec ./openapi.yaml \
  --knowledge 'Send X-Api-Key: ${env.API_KEY} on every request'

--endpoint sets EXPLORBOT_URL and --spec sets EXPLORBOT_API_SPEC; both beat the config file when given. Two things this fixes beyond the convenience:

  • Global mode flattened the base endpoint to the origin. https://api.example.com/v1 became https://api.example.com, so an API whose base carries a version or project segment could not sensibly be tested from ~/.explorbot. The prefix is kept now, and a target passed as a full URL is stripped back to a base-relative path so it cannot double up.
  • api test had no way to name an endpoint. It takes a plan file, so in global mode it died in resolveSiteTarget unless EXPLORBOT_URL happened to be exported.

prima

--spec points at a Docbot application spec directory, or its index.md, to be read as page knowledge — the flag form of the --spec that explorbot start already takes. It works through a new EXPLORBOT_SPEC variable that resolves into dirs.spec in every config mode, so every browser command gains it, and prima's EXPLORBOT_*PRIMA_CLI_* mirroring produces PRIMA_CLI_SPEC with no further code.

docs collect

--url gives a relative path argument its base URL:

explorbot docs collect /dashboard --url https://app.example.com

It travels as baseUrl rather than through the environment, so it beats a URL pinned in a config file.

Doc collection gets no --spec: it is the command that writes one.

Environment knowledge in global mode

materializeKnowledge ran only in config-free mode, so EXPLORBOT_KNOWLEDGE and EXPLORBOT_KNOWLEDGE_FILE did nothing for a run on ~/.explorbot/config.js. Both enterGlobalMode implementations now write into the site's knowledge directory, so the two variables reach exploration, prima, doc collection and API testing alike. The semantics stay per-run: the next run rewrites the file, a run with neither variable removes it, and learn and know remain the way to keep a fact.

Together with the knowledge flag, this is what lets a full API run happen with nothing on disk but the global model config.

Checks

bun test tests/unit (1134) and tests/integration (82) pass; format, lint and bunosh docs:sync --check are clean. Seven new unit tests cover the endpoint prefix and its stripping, spec precedence over a config file, and environment knowledge materialized in both global modes. Each boat was also driven end to end against a sandboxed HOME holding a global config: the API boat resolving endpoint, spec and knowledge together; prima loading an application spec through EXPLORBOT_SPEC; doc collection registering a site from --url.

One drive-by fix: config printed an absolute dirs entry glued onto the project root, visible as soon as a spec lived outside the project.

🤖 Generated with Claude Code

DavertMik and others added 6 commits August 24, 2026 17:13
--knowledge takes facts on the command line and keeps them in memory for
that run only, so credentials and one-off test data never land in
knowledge/. Plain text applies everywhere; frontmatter scopes it the way
a knowledge file does - url: for a page, endpoint: for an API endpoint -
and the rest of the file grammar follows, including wait/waitForElement
and ${env.VAR} interpolation. The flag repeats, since gray-matter reads
one frontmatter block per string.

KnowledgeTracker holds session entries beside the ones it loads from
disk and matches both through the same structural patterns. Its
constructor now takes an options object, which lets the API boat point
it at its own knowledge directory without a loaded web ConfigParser.

That directory was never read at runtime before, so endpoint knowledge
written by `api know` sat unused. Chief now loads it when planning an
endpoint and Curler when testing one, which is what makes --knowledge
useful for auth on the API side.

drill's --knowledge <path> becomes --save-knowledge <path>, in the CLI
and in the TUI. It saves what drilling learned rather than supplying
facts, and the two cannot share a flag on the same command.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The flag was threaded through four option interfaces, four buildOptions
functions and four addCommonOptions helpers to reach a tracker that
already knew what to do with it. It now registers on the program the way
--ws does: one option plus a preAction hook, both living in
knowledge-tracker.ts beside the code that reads them.

Commander merges parent options down through optsWithGlobals, so one
registration covers every command, the mounted api/docs/prima
subcommands included, and the flag can sit anywhere on the line -
before the command, after it, or after variadic arguments.

Prima, doc-collector and the api CLI go back to what they were; ExplorBot
no longer carries a knowledge option at all. The constructor still takes
one, which is what the tests use and what makes the module state a
fallback rather than the only way in.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A run needs to know where the app is, what documentation describes it, and
the facts an agent cannot infer. Only the last of those could be given on the
command line, so the rest still needed a config file.

API boat: --endpoint and --spec, mirroring EXPLORBOT_URL and
EXPLORBOT_API_SPEC. --endpoint keeps its path prefix as api.baseEndpoint,
which global mode used to flatten to the origin, and gives `api test` an
endpoint it never had an argument for. A target passed as a full URL is
stripped back to a base-relative path.

prima: --spec names a Docbot application spec to read as page knowledge,
through the new EXPLORBOT_SPEC variable, which resolves into dirs.spec in
every config mode and so serves every browser command. Prima's env mirroring
turns it into PRIMA_CLI_SPEC on its own.

docs collect: --url gives a relative path argument its base URL.

Global mode now materializes EXPLORBOT_KNOWLEDGE and EXPLORBOT_KNOWLEDGE_FILE
into the site's knowledge directory, where they were inert before, so the
variables reach exploration, prima, doc collection and API testing alike.

Also fixes `config` printing an absolute dirs entry glued onto the project
root.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WEFNJs2DKN8jKwnzZpi7Dk
@DavertMik DavertMik changed the title Pass knowledge to a run without writing a file Pass knowledge, endpoint and spec to a run without a config file Aug 30, 2026
DavertMik and others added 3 commits August 31, 2026 00:20
KnowledgeTracker answers what is true from what was stored; it has no
business importing commander or owning a flag. The registration moves to
src/commands/knowledge-option.ts, and the values it collects land in Stats,
the session-state store the tracker already sits below. The tracker now reads
Stats.knowledge and knows nothing about where it came from.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WEFNJs2DKN8jKwnzZpi7Dk
The option belongs where the run's inputs are already resolved, next to
EXPLORBOT_KNOWLEDGE and materializeKnowledge, registered the way remote
registers --ws. commands/ holds TUI command classes, not CLI wiring, so the
file added there is gone and Stats no longer carries a value that was only
passing through.

KnowledgeTracker asks config for what the flag collected, through the
dependency it already has.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WEFNJs2DKN8jKwnzZpi7Dk
--ws and --knowledge belong to a run rather than to a command, and each was
declared inside the module that consumed it: remote owned --ws, config owned
--knowledge. src/commands/options/ now holds them as BaseOption subclasses —
flags, description, an optional collect for a repeatable value, and an apply()
that runs after parsing — with one public register(program) a bin calls.

A bin registers what it offers, so prima keeps its surface and the others keep
both flags. apply() hands the value on and stops there: --ws to remote.attach,
--knowledge to config, which holds the run's inputs. remote loses its commander
import and its command-path helper.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WEFNJs2DKN8jKwnzZpi7Dk
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant