Code Map

Fisk AI turns a Fisk command-line application into an LLM agent by introspecting its command tree and exposing the allowed commands as tools. This section is a reading guide to how that is implemented, aimed at contributors, reviewers, and anyone auditing the harness before trusting it with a production tool.

Snapshot

Generated 2026-07-24 against tag v0.0.3. Commits after this one may make parts of this map stale.

The mental model

There is one core and several faces. A Fisk application is introspected once into a set of tools; a YAML file narrows that set and decides what needs approval; and then the same selection is either driven by a model in an agent loop, served to an MCP client, or served to other agents over NATS. Three durable stores sit underneath, and none of them is part of the conversation the model sees until the harness decides to put it there.

fisk CLI appintrospected onceagent.yamlselects and gatestool set and loopone flat namespacegated, bounded, journaledrunterminal, human gatemcphttp clientsa2aother agentsmemorymodel-written notesknowledgeoperator-owned docssessionsappend-only journalthe same selection drives all three faces; only the confirmation policy differs
One core, three faces, three stores. What changes between the faces is whether a human can be reached.

What the design optimizes for

The project describes itself by what it does not have. Reading the source, three commitments show up repeatedly and explain most of the structure.

Nothing is silently weakened. A configured confirm tag that matches no tool is warned about, because leaving it unreported would give a false sense of safety. A tool-name collision aborts the run rather than shadowing, because shadowing a gated command would strip its gate. A tag-based exclude on a remote host is rejected outright, because discovery carries no tags and the filter could never be honored.

Failures land at startup. An unknown backend, a typo in an options block, a bucket with a TTL, an unreachable remote agent, a stale knowledge manifest: all of them stop the process before the model is contacted, and the error names the fix.

Untrusted text stays data. Model-written memories and retrieved documents are wrapped, labeled as data rather than instruction, sanitized at write time, and sanitized again at render time.

How to read this section

Start with Architecture for the layering and the patterns that repeat, then Configuration, since every entry point begins by parsing a file. From there, The agent loop and Tools and introspection are the core, and the rest can be read in any order.

Each page names real files and symbols, states the invariants the safety story depends on, and is explicit about what is reserved, unused, or aspirational rather than presenting the whole tree as finished.

Explore

  • Architecture

    How the packages layer, how implementations are linked in, and the invariants that repeat everywhere

  • Configuration

    One YAML file, three modes, and the strictness that makes a typo fail at startup

  • The agent loop

    Call the model, run the tools it asks for, journal everything, and stop for the right reason

  • Tools and introspection

    Turning a Fisk command tree into tools the model may call, and the tags that decide what it may not

  • Model providers

    A provider-neutral message model that doubles as the on-disk format, and one package allowed to speak an SDK

  • Sessions and replay

    An append-only journal, a pure fold, and a fingerprint that refuses an incoherent resume

  • Memory

    Small durable notes an agent writes for its future self, with the key charset as the security boundary

  • Knowledge

    A hybrid search index in one SQLite file, with no cgo, no sidecar, and no build-tag matrix

  • Serving: MCP and A2A

    Two ways to hand an application's tools to something else, and why one exposes gated tools while the other refuses

  • Terminal and events

    One typed event contract, two terminal surfaces, and a screen owned by exactly one goroutine

  • Reference and map

    The command surface, the source map by package, key types, and the vocabulary {class=“children children-type-tree children-sort-”}