agent-folio
v0.7.0
Published
Local-first, zero-server reports for coding-agent work
Downloads
513
Maintainers
Readme
Folio
Folio turns substantial coding-agent output into durable, readable HTML reports you can review like code. It is local-first and server-free: one CLI command stores source and metadata, generates one standalone HTML file, and opens it in your browser.
Select report text, add anchored comments in the right review rail, then copy structured Markdown back into your coding agent. No daemon, browser extension, cloud account, or network connection participates in that loop.

Install
Folio is distributed as source through npm and requires Bun 1.3 or newer. Run it without installing:
bunx agent-folio --helpnpx uses the same npm package and works when bun is already on PATH:
npx agent-folio --helpInstall the command globally if preferred:
bun add --global agent-folio
folio --helpRemove it with bun remove --global agent-folio. The npm package executes the TypeScript source directly with Bun; it does not download or build a platform bundle. Node-only execution is not currently supported because Folio uses Bun's SQLite and HTTP runtime APIs.
Standalone executables remain available as an optional installation. Download the archive for your OS and CPU from the GitHub release, verify it against SHA256SUMS, extract it, and place the executable on PATH. macOS and Linux users can install an extracted binary with:
install -m 755 folio-darwin-arm64 ~/.local/bin/folioUse the matching filename for your platform. Release assets cover macOS, Linux, and Windows on arm64 and x64, and include SHA256SUMS.
The macOS downloads are not notarized. If Gatekeeper quarantines one, review the downloaded file and approve it in System Settings → Privacy & Security before running it.
To compile and install a standalone binary from a checkout:
bun install
bun run build:installbuild:install runs an optional repository-local scripts/postinstall.sh after replacing the binary. The hook is Git-ignored so each user can define local service startup or other machine-specific work. It receives FOLIO_BIN and FOLIO_BIN_DIR; a missing hook is a successful no-op. Pass --no-postinstall to skip it explicitly.
This builds a standalone executable at ~/.local/bin/folio. It does not edit shell profiles. Use --bin-dir <directory> for another location or --force to replace an existing binary:
bun run scripts/install.ts --bin-dir ~/.local/bin --forceQuick start
folio create examples/auth-investigation.mdocReport opens as standalone file:// HTML.
Select text → Comment → write feedback → Copy All Comments.
Use --no-open for automation:
bun run src/cli.ts create examples/auth-investigation.mdoc --no-open --jsonFolio Markdoc
Reports use strict Folio Markdoc v1: YAML frontmatter, one leading summary, ordinary Markdown, and a small semantic tag set.
---
schema: folio/v1
title: Refresh investigation
kind: investigation
tags: [authentication]
---
{% summary %}
Concurrent refreshes can exchange one token twice.
{% /summary %}
## Finding
{% finding title="Refresh race" severity="high" confidence="high" %}
Two requests enter the refresh path concurrently.
{% /finding %}Generate a skeleton or concise syntax guide:
folio template investigation
folio formatRaw HTML, arbitrary tags, unsafe links, H1 headings, nested semantic blocks, and normal Markdown images are rejected.
File links and media artifacts
Reference source files with paths relative to Git root. Folio turns them into local links and preserves evidence locations in exported feedback:
The race starts in {% file path="src/auth/session.ts" lines="84-117" /%}.Attach image or video artifacts from testing with the dedicated media tag:
{% media path="artifacts/fixed-page.png" alt="Fixed page in Chrome" caption="Acceptance result" /%}
{% media path="artifacts/interaction.mp4" kind="video" caption="Interaction recording" /%}Media bytes are embedded as data URLs. Copying only report.html keeps images, videos, styling, annotation UI, and export logic intact. Media and file paths must be Git-root-relative; symlinked media may not escape the repository.
Flint charts
Models can add semantic Flint chart specs directly to reports. Folio validates each spec, compiles it through Flint's Plotly backend, and embeds both the compiled figure and Plotly runtime into chart-bearing HTML. Charts stay interactive without a server or network connection.
{% chart alt="Requests by month" caption="Monthly request volume" %}
```flint
{
"data": { "values": [{ "month": "Jan", "requests": 120 }, { "month": "Feb", "requests": 180 }] },
"semantic_types": { "month": "Month", "requests": "Count" },
"chart_spec": {
"chartType": "Bar Chart",
"encodings": { "x": "month", "y": "requests" }
}
}
```
{% /chart %}Charts require useful alt text and one fenced flint JSON object. Only inline data.values is allowed; Folio never fetches chart data or reads paths named by a model. Encoded fields must exist in the rows and have semantic types. Limits are 5,000 rows, 100 fields, and 512 KiB per chart.
Browser review
Each paragraph, heading, list item, blockquote, and code block has a deterministic annotation anchor. Selections stay inside one block and may not overlap existing comments.
Comments appear in a right-side review rail and support add, edit, delete, clear, and best-effort localStorage persistence. If storage is unavailable, current-tab review still works. Copy All Comments writes document-ordered Markdown containing report context, quoted text, section, optional evidence path, and feedback. Clipboard failure opens a manual-copy dialog. Download Comments.md exports identical Markdown.
An agent can opt a report into direct feedback dispatch by passing an exact-session command that reads the generated Markdown from stdin:
folio create report.mdoc --callback-command 'workmux send folio'
folio create report.mdoc --callback-command 'codex exec resume 01234567-89ab-cdef-0123-456789abcdef -'
folio serveThe bundled Folio skill checks workmux list --json first and otherwise uses
the exact CODEX_THREAD_ID; it never targets “latest”. Workmux sends to the
live pane. Direct Codex starts an explicit non-interactive continuation of that
session.
Open the report through http://127.0.0.1:7331, add comments, then choose Send Callback to Agent beside Copy All Comments. Folio shows the exact command and comment count for confirmation before running it. Callback commands are stored only in the local catalog, never in immutable report artifacts, metadata files, or shared HTML. They run only through a loopback archive server, from the report repository when available, with feedback on stdin rather than interpolated into the command.
The table of contents navigates report headings. Night Owl is the default palette; theme selection supports system, light, and dark modes. Source files use locally bundled MicroLighter grammars. Timestamps use the viewer's locale. HTML and PDF exports omit review controls and absolute local links. Markdown exports flatten semantic blocks into ordinary Markdown while retaining their values and repository-relative file labels.
Comments never enter SQLite and are not synchronized. A new report revision gets a new report ID and separate browser review state.
CLI
folio create <file|-> [--stdin] [--no-open] [--json] [--supersedes <id>] [--callback-command <command>]
folio validate <file|-> [--json]
folio template <kind>
folio format
folio list [--repo <key>] [--kind <kind>] [--limit <n>] [--json]
folio show <id> [--source] [--json]
folio export <id> <--md|--html|--pdf> [--out <directory>]
folio open <id|latest>
folio path
folio serve [--portless] [--host 127.0.0.1] [--port 7331]
folio skill install [--target <skills-directory>] [--data-dir <directory>] [--force] [--json]
folio completion <bash|zsh|fish>
folio --versionPass --data-dir <directory> to any command that reads or writes the catalog. It has precedence over FOLIO_HOME; relative paths resolve from the current directory.
Folio loads optional UI overrides from <data-directory>/settings.json. Copy
the packaged settings.json to that location and change any
subset of the light or dark palette. Colors must use six- or eight-digit hex
notation; unknown keys fail fast instead of becoming CSS.
create validates before writing, collects local Git metadata, writes report artifacts atomically, and inserts catalog metadata. --callback-command stores an optional command in the catalog without placing it in report artifacts. HTML opens by default only in an interactive terminal; --json, --no-open, CI, and redirected output skip implicit opening. Browser-opening failure does not discard or fail report creation.
export prints Markdown to stdout when --md is used without --out. Markdown with --out, HTML, and PDF are written as out/<report-id>.<format> by default; pass --out <directory> to choose another directory. PDF export uses a locally installed Chrome, Chromium, or Edge executable; set FOLIO_CHROME_BIN when automatic discovery cannot find it.
Unknown options fail. Use -- before a path beginning with -. Set FOLIO_DEBUG=1 to include a stack trace for unexpected failures. Generate basic shell completion with, for example, folio completion zsh.
Storage
Folio resolves its data directory in this order:
FOLIO_HOME$XDG_DATA_HOME/folio~/.local/share/folio
folio.sqlite
reports/<report-id>/
report.mdoc
report.html
meta.jsonSQLite uses foreign keys and WAL mode. Reports are immutable. meta.json duplicates important catalog metadata so artifact directories remain understandable without SQLite. Folio never writes generated reports into your Git repository.
Optional archive
The review workflow does not require a server. folio serve adds an archive, repository source viewer, and opt-in callback delivery:
folio serve
# http://127.0.0.1:7331For a stable local URL, run the archive through bundled Portless:
folio serve --portless
# https://folio.localhostPortless starts its local proxy and gives Folio an available loopback port, avoiding fixed-port collisions. On first launch, macOS or Linux may request administrator access to trust its local CA and bind HTTPS port 443. --portless cannot be combined with --port; without --portless, explicit --host and --port flags still override HOST and PORT.
The archive lists known Git repositories in a right-side panel with report counts. Repositories with multiple recorded branches expand to branch filters. Repository, branch, clickable tag, search, and report-type filters preserve one another. New reports appear through SSE without reloading the page.
Served reports use the current review shell, so older immutable artifacts gain current navigation and controls without being rewritten on disk. Back to all reports stays above the table of contents in the sticky sidebar. Referenced repository files open in a Night Owl syntax-highlighted viewer with JetBrains Mono, line-aware comments, and the shared theme switcher. Source access is limited to files referenced by the report and contained inside the repository root.
Callback execution and source viewing are loopback-only. Each served callback gets an unguessable server-lifetime token, rejects concurrent and oversized requests, and has a 30-second timeout. A non-loopback host requires explicit --allow-network; callbacks and repository links remain disabled, served metadata omits the absolute repository root, and repository links are removed.
Coding-agent skill
Repo includes compact skill at skills/folio-report. Install it into Codex skill discovery:
folio skill installWithout a global install:
bunx agent-folio skill installThe command installs into $CODEX_HOME/skills/folio-report, or ~/.codex/skills/folio-report when CODEX_HOME is unset. Use --target <skills-directory> for another agent skill directory. Existing modified content is preserved unless you explicitly pass --force.
For coding-agent sandboxes that cannot write to the normal user-data directory, configure a writable report root while installing the skill:
folio skill install --data-dir .folio --forceThe installer records this value in the skill's small runtime reference. The skill then passes --data-dir .folio whenever it invokes Folio. Relative paths are repository-local; add .folio/ to that repository's ignore rules if you use this layout. An absolute writable path can instead provide one shared catalog.
Then invoke $folio-report, or let it trigger for substantial work products. Skill uses progressive disclosure: short workflow in SKILL.md, full format details in references/format.md, and CLI-generated templates for common report kinds.
For substantial work in an existing repository, the skill first uses folio list --json, folio show <id> --json, and folio export <id> --md to recover relevant prior insights. Historical reports remain context rather than current truth, so the agent verifies drift-prone claims against the repository before relying on them.
Development
bun run typecheck
bun test
bun run check
bun run test:package
bun run build:binary
bun run build:installTests isolate FOLIO_HOME; they never touch your real catalog.
bun run build:release cross-compiles the six optional release executables into dist/. GitHub Actions checks Linux, macOS, and Windows, smoke-tests the packed npm command, bundles executables with license notices, generates SHA-256 checksums, publishes the npm package with provenance, and creates a GitHub release when a matching version tag is pushed:
bun run release -- 0.3.0 --dry-run
bun run release -- 0.3.0The release command requires a clean main synchronized with origin/main, the srsatt Git/GitHub identity, a matching package.json version and changelog section, and a version tag that does not exist. It runs all local gates, creates an annotated tag without rewriting commits, pushes the tag, and watches the release workflow. Use --skip-gates only when the same commit was already verified; CI always reruns the gates. Before the first release, configure srsatt/folio as a trusted publisher for agent-folio on npm. A manual workflow run builds the same downloadable archives as workflow artifacts without creating a GitHub release or publishing npm.
Security model
Report input is untrusted text. Folio allowlists tags and attributes, rejects raw HTML and unsafe URLs, HTML-escapes report content, and writes comment content only through DOM text APIs. Generated HTML contains no remote scripts, stylesheets, fonts, analytics, telemetry, uploads, or background processes. Media is read locally once and embedded. Repository links require an explicit click. Optional callbacks require a loopback server, an explicit stored command, a per-server token, and browser confirmation showing the exact command; comment text is passed only on stdin.
MVP limits
- Text selections cannot cross blocks or overlap.
- Review state is browser-local and differs between
file://and archive HTTP origins. - Direct callbacks require opening the report through loopback
folio serve; standalone files retain copy/download feedback only. - Media embedding increases HTML size, especially for video.
- Repository links target local absolute
file://paths generated at creation time, so they may not work after moving HTML to another machine. - No collaboration, comment threads, syncing, report editing, full-text search, generated DOCX files, AI calls, MCP, or GitHub/GitLab API integration. PDF export requires a locally installed Chromium-family browser.
See Folio.md for complete product specification.
