Token-saving tools for coding agents. A single binary (frg) with structured-JSON subcommands and an MCP server, so agents can consume compact, machine-readable output instead of raw terminal noise. Forge also includes a Claude Code hook integration and a Goose workflow skill.
Current version: 0.14.0
Status: developer preview. Forge is useful today, but the public API and MCP tool surface may still change before a 1.0 release.
Raw cargo, go test, pytest, and git diff output is verbose and token-expensive. frg wraps the most common operations and returns structured JSON: errors with file paths and line numbers, warnings grouped by rule, summaries instead of wall-of-text logs. The MCP layer surfaces the same tools directly to Claude Code without shell round-trips.
git clone https://github.com/ferrosadb/forge.git
cd forge
cargo install --path crates/cli --lockedAdd to ~/.claude/settings.json to expose the MCP server:
{
"mcpServers": {
"forge": {
"command": "/path/to/frg",
"args": ["--mcp"]
}
}
}Install Claude Code hooks (pre-tool-call output filtering):
frg init --globalUse frg init --show to inspect the installed hook and frg init --global --uninstall
to remove it. The Goose integration is a repository-local skill at
integrations/goose/forge. Configure that directory
through the Goose host's normal extension or skill-discovery settings.
Commands that produce structured reports write JSON to stdout unless noted;
commands with an explicit text format, such as glob --format brief, are the
exception. Pass output through frg hook in hooks to filter before Claude
reads it.
| Command | Description |
|---|---|
frg test-summary |
Parse stdin test runner output (cargo test, pytest, jest, go test, mix test) into structured pass/fail/skip counts and error locations |
frg log-distill [--context N] |
Extract errors and warnings from verbose build logs with surrounding context lines |
frg diff-filter |
Filter git diffs: collapse large hunks, skip noise files (lockfiles, generated), summarize binary changes |
frg lint-dedup |
Deduplicate lint warnings by rule ID; group by file for compact review |
frg log-monitor [--stall-threshold N] [--repeat-threshold N] [--max-events N] |
Detect log stalls, OOM signals, disk pressure, and repeated error patterns in a log stream |
frg mermaid-validate |
Validate Mermaid diagram syntax from stdin |
| Command | Description |
|---|---|
frg coverage-gate --coverage <lcov.info> --source <dir> [--baseline N] |
Validate test coverage meets a baseline; enforces complexity-coverage coupling (high-CC code requires higher coverage) |
frg smell-detect [paths...] |
Find code smells: long functions, high cyclomatic complexity, deep nesting |
frg doc-coverage [paths...] |
Check public API documentation coverage; report undocumented exports |
frg threat-scan [paths...] |
Scan for STRIDE attack patterns (spoofing, tampering, repudiation, info disclosure, DoS, elevation) |
frg fail-loud-scan [paths...] |
Find swallowed errors, fake success returns, silent fallbacks, and mock-data leaks |
frg concurrency-scan [paths...] [--categories ...] |
Detect locks, mutexes, channels, consensus patterns, replication concerns |
frg materialization-scan <paths...> [--include-tests] [--max-findings N] |
Find likely unbounded materialization in disk/storage/query I/O paths (Rust + Go): whole-file reads (read_to_string/io.ReadAll/os.ReadFile), query result materialization (rows_or_empty/ALLOW FILTERING / for rows.Next() / cursor.All), collect() / slices.Collect, growing Vec/slice via append, and map-of-Vec / map[K][]V grouping |
frg todo-extract [paths...] |
Extract TODO/FIXME/HACK comments with git blame attribution and age |
frg secret-scan [path] |
Find leaked API keys, credentials, private keys, and tokens |
frg deps-audit [path] [--min-severity LEVEL] |
Audit dependency lockfiles (Cargo.lock, package-lock.json, etc.) for known CVEs |
| Command | Description |
|---|---|
frg dsm extract|analyze [dir] |
Design Structure Matrix: extract returns raw dependency edges; analyze runs the full pipeline with cycle detection, cluster identification, metrics, and refactoring suggestions |
frg dsm dead-code [dir] |
Find unreachable declarations via BFS from entry points; --min-confidence definite|possible|all, --include-tests, --exclude <glob> (repeatable), skips generated files and #[cfg(test)] items |
frg dep-tree [dir] |
Build per-module dependency map showing import fan-in and fan-out |
frg api-diff |
Diff public API surface between two refs; detect breaking changes |
frg schema-diff |
Diff SQL/CQL/Cypher schemas between two files or refs for breaking migration detection |
frg merge-check |
Test branch merge-ability without side effects |
| Command | Description |
|---|---|
frg digest [paths...] |
Token-efficient structural summary: functions, types, imports — no bodies. Best first call on an unfamiliar file |
frg excerpt <target> [--context N] |
Extract a single named symbol with surrounding context lines |
frg lookup <symbol> [dir] |
Find symbol definitions across the project |
frg outline <file> |
Extract function signatures, type definitions, and module structure from a single file |
frg glob <pattern> [--format brief|json|csv|table] |
Find files matching a glob pattern; returns bounded per-file path, size, line count, modification time, and generated-file metadata |
frg project-detect [dir] |
Auto-detect project type, languages, frameworks, and applicable forge tools |
frg format-fix [dir] [--check] |
Auto-detect language and run the appropriate formatter; --check for CI |
These commands ingest structured knowledge into ferrosa-memory via the agent_memory keyspace.
| Command | Description |
|---|---|
frg ingest [dir] |
Ingest codebase structure: modules, functions, types, and their relationships as typed entities and edges |
frg ingest-descriptions [dir] |
LLM-backed one-line descriptions for public entities; supports a configured provider and validates ≤60 words while rejecting prompt-leak strings |
frg ingest-url <url> |
Fetch a web page and ingest its structure and concepts as entities |
frg fetch-url <url> |
Fetch a web page through Forge's trusted HTTP path and return compact read-only text, sections, and links without persistence |
frg web-search <query> |
Search for candidate URLs through an explicitly configured trusted SearXNG backend (FORGE_WEB_SEARCH_URL or SEARXNG_URL) |
frg ingest-paper <source> |
Ingest an academic paper (see PDF ingestion) |
frg ingest-corpus <path> |
Ingest corpus markdown distillation files; creates L1/L2/L3 entities with deterministic UUID5 IDs |
ingest-url and fetch-url share Forge's SSRF guard, sensitive-query stripping,
5 MB response cap, prompt-injection sanitization, and relative-link resolution.
Use ingest-url when the page should become durable ferrosa-memory knowledge;
use fetch-url when an agent only needs to read the page in the current turn.
Forge ships with no default search provider: web-search fails loud until a
user-owned/trusted SearXNG endpoint is configured. SearXNG result titles,
snippets, engines, and URLs are treated as hostile web content: Forge strips
active/hidden markup, removes control characters, decodes entities, blocks
prompt-injection patterns, strips sensitive URL query parameters, caps returned
text, and returns only a deterministic extractive summary generated from the
scrubbed text. No LLM summarizer is invoked in the search path.
| Command | Description |
|---|---|
frg task create <title> |
Create a task in the CQL-backed kanban store |
frg task update <id> |
Update task status, assignee, priority, or body |
frg task get <id> |
Fetch a single task by ID |
frg task list [--status STATUS] |
List tasks; filter by status |
frg task link <src> <dst> <type> |
Create a typed link between two tasks |
frg task unlink <src> <dst> <type> |
Remove a task link |
frg task comment <id> <body> |
Add a comment to a task |
frg task board |
Render the kanban board grouped by status |
frg checklist <action> |
Persistent workflow checklists with dependencies, leases, attempts, waiting gates, reviews, and scoring |
frg fmem-skill-ingest [--root <dir>] |
Ingest the SKILL.md catalog into ferrosa-memory as typed skill entities |
Two-way sync between a Google Sheet of bugs and the CQL task board. See Google Sheets sync for setup.
| Command | Description |
|---|---|
frg sheet auth <alias> |
Run the one-time Google OAuth consent flow and cache a refresh token |
frg sheet pull <alias> [--dry-run] |
Upsert open sheet rows into the task board (idempotent, never-move-backward) |
frg sheet push <alias> <row_id> [--status S] [--fix-ver V] [--notes N] [--dry-run] |
Write status/fix-version/resolution-notes back to the row's own cells |
Each wrapper parses native output into structured JSON with error locations, warning counts, and an actionable hint field on failure.
| Command | Subcommands |
|---|---|
frg run cargo <cmd> |
build, check, test, clippy, fmt_check |
frg run go <cmd> |
build, test, vet, fmt_check, mod_tidy |
frg run dotnet <cmd> |
build, test, format_check |
frg run npm <cmd> |
test, typecheck, lint, format_check, deps, build, audit |
frg run python <cmd> |
test, lint, format_check, deps, typecheck |
frg run mix <cmd> |
compile, test, format_check, deps |
| Command | Description |
|---|---|
frg gain [--json] |
Show token savings analytics since last reset |
frg analytics [--json] |
Detailed filter analytics per tool |
frg clear-analytics |
Reset analytics counters |
frg discover [dir] |
Scan project and suggest forge optimization opportunities |
frg init --global [--uninstall|--show] |
Install, remove, or inspect the Claude Code hook configuration |
frg hook |
Process tool output as a Claude Code hook (reads tool name + output from env) |
frg context [--session ID] [--clear] |
Show or clear tracked files and symbols for an agent session |
frg context-check [dir] |
Report available ferrosa-memory context for a project |
frg tool-aliases [--format FORMAT] |
Return the alias map for tool name mismatches between callers and forge |
frg version |
Print installed frg version |
The MCP server exposes Forge's agent-facing functionality directly to MCP clients. Use frg --mcp for stdio clients, or run frg --mcp-http --mcp-http-addr 127.0.0.1:9977 to expose a Streamable HTTP endpoint at POST /mcp.
Forge supports the draft 2026-07-28 MCP discovery/result shape: server/discover, resultType: "complete", server identity in result _meta, outputSchema on advertised tools, and cache metadata on tools/list. The HTTP endpoint requires the draft mirror headers (MCP-Protocol-Version, Mcp-Method, and Base64-safe Mcp-Name for tools/call), validates Origin, returns 405 for legacy GET/DELETE MCP endpoint traffic, and rejects client JSON-RPC response objects.
Tools are split into two tiers:
Tier 1 — always visible: core analysis, log processing, code quality, architecture, ingestion, task management.
Tier 2 — stack-detected: language toolchain wrappers appear only when the detected project stack matches (e.g., cargo only in Rust projects).
| MCP tool | Maps to |
|---|---|
project_detect |
frg project-detect |
project_summary |
frg project-detect --summary |
find_definition |
frg lookup |
module_outline |
frg outline |
dependency_tree |
frg dep-tree |
digest |
frg digest |
excerpt |
frg excerpt |
glob |
frg glob |
smell_detect |
frg smell-detect |
format_fix |
frg format-fix |
git_summary |
git status / log / diff (structured) |
list |
list all available forge tools |
tool_aliases |
frg tool-aliases |
version |
frg version |
test_summary |
frg test-summary |
log_distill |
frg log-distill |
diff_filter |
frg diff-filter |
lint_dedup |
frg lint-dedup |
log_monitor |
frg log-monitor |
coverage_gate |
frg coverage-gate |
doc_coverage |
frg doc-coverage |
concurrency_scan |
frg concurrency-scan |
materialization_scan |
frg materialization-scan |
dsm |
frg dsm |
dead_code |
frg dsm dead-code |
merge_check |
frg merge-check |
schema_diff |
frg schema-diff |
api_contract_diff |
frg api-diff |
threat_scan |
frg threat-scan |
fail_loud_scan |
frg fail-loud-scan |
secret_scan |
frg secret-scan |
deps_audit |
frg deps-audit |
todo_extract |
frg todo-extract |
checklist_state |
frg checklist |
fmem_skill_ingest |
frg fmem-skill-ingest |
mermaid_validate |
frg mermaid-validate |
ingest |
frg ingest |
ingest_url |
frg ingest-url |
fetch_url |
frg fetch-url |
web_search |
frg web-search |
ingest_paper |
frg ingest-paper |
ingest_corpus |
frg ingest-corpus |
task_create |
frg task create |
task_update |
frg task update |
task_get / task_list |
frg task get / frg task list |
task_link / task_unlink / task_comment |
frg task link / frg task unlink / frg task comment |
task_board |
frg task board |
sheet_auth |
frg sheet auth |
sheet_pull |
frg sheet pull |
sheet_push |
frg sheet push |
cargo (tier 2) |
frg run cargo |
clippy (tier 2) |
frg run cargo clippy |
go_tools (tier 2) |
frg run go |
dotnet (tier 2) |
frg run dotnet |
npm_tools (tier 2) |
frg run npm |
python_tools (tier 2) |
frg run python |
mix_* (tier 2) |
frg run mix |
Drive development from a Google Sheet of bugs: pull open rows into the CQL task board, work them, and push fix status back to the sheet — writing only the columns you designate, never touching the owner's other cells. Useful when a client or QA team files bugs in a spreadsheet they own and you want an agentic loop over them.
- Create a Google OAuth client. In the
Google Cloud Console: APIs & Services →
enable the Google Sheets API → Credentials → Create credentials → OAuth
client ID → Application type: Desktop app → download the
client_secret.json. - Point forge at it via env var (or
[google] client_secret_pathin.forge/config.toml):export FORGE_GOOGLE_OAUTH_CLIENT=/path/to/client_secret.json - Write a per-sheet mapping at
.forge/sheets/<alias>.toml— copy and editcrates/sheet-sync/examples/spoton-qa.toml. It declares thespreadsheet_id,tab, theid_column(the sheet's stable per-row id), a header→field[columns]map, thewritablecolumns (the only onespushmay ever write), astatus_map(sheet status → task status), and thedev_writable_status/terminal_statussets that drive the write-back handoff. Top-level array keys must appear before the[columns]/[status_map]tables (a TOML rule)..forge/is git-ignored, so your real ids and mappings stay local. - Authorize once — opens a browser consent page and caches a refresh token
under
~/.config/forge/sheet-sync/(stored0600, never logged):The OAuth scope is limited tofrg sheet auth <alias>
spreadsheets.
For agents/CI, or anyone who'd rather skip the browser consent flow
entirely: run
crates/sheet-sync/examples/provision-service-account.sh
(requires gcloud, already logged in) to create a Google service account
and download its key JSON, then share the target Google Sheet with the
printed service-account email as Editor — a fresh service account has no
access to anything until you do this. Point forge at the key —
export FORGE_GOOGLE_SERVICE_ACCOUNT=/path/to/sa-key.json (or
[google] service_account_path in .forge/config.toml) — and skip
frg sheet auth entirely: it's a no-op once a service account is
configured, and frg sheet pull/push mint tokens directly, no browser
required.
frg sheet pull <alias> --dry-run # preview what would import
frg sheet pull <alias> # upsert open rows into the task board
# ...pick a task, fix the bug, then:
frg sheet push <alias> <row_id> \
--status "In Progress" --fix-ver "<git sha on demo | version tag on prod>" \
--notes "what was done" --dry-run # preview the exact cell diff
frg sheet push <alias> <row_id> --status "In Progress" --fix-ver "abc1234" --notes "..."pull is idempotent — keyed by the sheet's row id, tracked in a sidecar
.forge/sheets/<alias>.state.toml — and never moves a task backward. The task
board is the CQL store (FORGE_CQL_HOST, same as frg task).
- Only the configured
writablecolumns are ever written — enforced in the planner and re-checked at the write boundary. - Rows are matched by normalized id; duplicate ids, or duplicate mapped-header text, fail loud rather than risk writing the wrong cell.
- Status is only ever advanced to a dev-owned value (e.g. In Progress, In
Review, Fixed - Needs Verification); client-owned or terminal states (New,
Verified/Closed, Won't Fix, …) are never overwritten.
fix_verand resolution notes are always yours to write. --dry-runon both commands prints the effect and writes nothing.
frg ingest-paper accepts:
- arxiv URLs (e.g.
https://arxiv.org/abs/2310.01234) - DOI links (
doi:10.xxxx/yyyy) - Semantic Scholar links
- bioRxiv / medRxiv / PubMed URLs
- IEEE / ACM URLs (opens browser for paywalled access)
- Generic web URLs
- Local PDF files (full structured extraction)
For local PDFs, the pipeline does structured extraction — not simple pdftotext. It is comparable in intent to Docling:
The crates/ingest/src/pdf/ module provides a typed element taxonomy and XY-Cut++ reading-order reconstruction:
| Element type | What it captures |
|---|---|
Heading |
Section headings with depth level (1 = title, 2 = section, 3 = subsection, …) |
Paragraph |
Body text blocks with bounding box and font metadata |
Table |
Tabular content with detected row/column shape |
List |
Bulleted or numbered list items |
Image |
Figure regions (bounding box only; no raster content) |
Caption |
Figure and table captions |
Formula |
Mathematical expressions |
HeaderFooter |
Running headers and footers (filtered from body) |
Watermark |
Background watermarks (filtered from body) |
Each element carries: page number, bounding box (left/bottom/right/top), text content, font name, font size, heading depth, and for tables, detected row/column count.
The reading_order.rs module implements XY-Cut++: it recursively bisects the page's bounding-box tree, first horizontally then vertically, to recover logical reading order from spatial layout. This correctly handles multi-column academic papers where a naïve top-to-bottom scan would interleave columns.
After extraction, ingest-paper writes to ferrosa-memory:
- Document entity — title, authors, abstract, year, DOI/URL
- Person entities — one per author with affiliation
- Section entities — heading hierarchy with depth, linked via
containsedges - Concept entities — key terms extracted from abstract and section headings
- Typed edges —
wrote(author→paper),references,discusses,affiliated_with,contains(paper→section)
| Capability | forge ingest-paper |
Docling |
|---|---|---|
| Reading order reconstruction | XY-Cut++ | DiT layout model + XY-Cut |
| Table detection | Row/col shape heuristics | Table Transformer (ML) |
| Formula handling | Type-tagged, not rendered | MathML export |
| Output target | ferrosa-memory knowledge graph | JSON / Markdown / Docling Doc |
| Image content | Bounding box only | Image export |
| OCR | No (text-layer PDFs only) | Yes (Tesseract / EasyOCR) |
If you need OCR'd PDFs or ML-quality table extraction, pre-process with Docling and ingest the Markdown output via frg ingest-url or a direct ferrosa-memory smart_ingest call.
crates/
cli/ frg binary — subcommand dispatch, MCP server, hook runner
ingest/ Ingestion pipelines
src/
pdf/ PDF structured extraction (element.rs, reading_order.rs, processors/)
paper.rs Academic paper ingestion orchestrator
corpus.rs Corpus markdown distillation ingestion
url.rs Web page fetching and ingestion
graph_loader.rs Code graph → ferrosa-memory loader
lsp.rs LSP-backed symbol resolution
descriptions/ LLM-backed entity description generation
skill_ingest/ SKILL.md catalog ingestion
tasks/ CQL-backed task store (TaskStore, schema, MCP tools)
hooks/ Claude Code hook scripts
specs/ Architecture decision records and feature specs
docs/ Extended documentation
git clone https://github.com/ferrosadb/forge.git
cd forge
cargo fmt --all
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace
cargo build --workspace --all-targetsBefore opening a PR or release, use cargo fmt --all -- --check rather than
the mutating formatter command above. CI (ci.yml)
runs format checking, clippy, build, and tests. All commands must be run from
the repository root — the workspace root for this crate tree.
See CONTRIBUTING.md. Security reports should follow SECURITY.md.
Apache-2.0. See LICENSE.