npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@spechycom/archon

v0.20.0

Published

Readme

@spechycom/archon

A deterministic context compiler for AI coding agents.

Your project knowledge lives in a DAG of small, single-purpose documents instead of one giant CLAUDE.md. The agent says "I'm about to touch src/api/users.ts" — archon returns exactly the rules wired to that path, nothing else, over MCP. Same request + same knowledge version = same bytes, every time.

🇹🇷 Türkçe README


Contents

| | | |---|---| | Get started | Why · How it works · Quick start · Installation · Building the knowledge base | | Daily use | What a session looks like · Capturing decisions · Team workflows | | Reference | Tools — agent surface · Tools — admin surface · CLI · Prompts & resources | | Concepts | Knowledge model · Delivery budget · Determinism | | Beyond the DAG | Semantic embeddings · Code awareness · Episodic memory · Document discovery · Graph visualization · Quality tooling | | Plumbing | Editor adapters · Hooks · Agent Skills · Status & limitations · Development |


Why

A single instruction file (CLAUDE.md, .cursorrules, AGENTS.md) has three failure modes, and archon is built against each one:

| Single-file failure | What archon does instead | |---|---| | Every rule rides in every prompt, most of them irrelevant | Only documents reachable from the target files are compiled | | Token cost grows with the file, forever | An allocator inlines what fits and defers the rest as file references | | No record of what the agent actually read | Every compile is written to an audit log, queryable by compile_id |

The core retrieval path is deliberately dumb: no embeddings, no LLM, no randomness — four resolution steps over SQLite. Everything smarter (semantic suggestions, symbol graphs, episodic memory) is layered on top as additive, fail-open extras that can never change or break the deterministic result.

How it works

compile_context resolves the target files against the knowledge DAG in four deterministic steps:

target_files ──▶ 1. path_requires     glob match (picomatch) per file
                 2. layer_requires    file → layer (layer_rules) → docs
                 3. command_requires  exact match on the `command` argument
                 4. doc_depends_on    transitive closure over matched docs
                        │
                        ▼
                 ordering: specificity ↓, priority ↑, doc_id ↑
                        │
                        ▼
                 intent_tags expansion (optional, additive)
                        │
                        ▼
                 relevance filter (optional, needs `plan`)
                        │
                        ▼
                 delivery allocator → inline / deferred / omitted
                        │
                        ▼
                 audit row + snapshot_id + knowledge_version

The read path only ever sees status = 'approved' rows, and cycles in doc_depends_on are rejected at insert time — step 4 always terminates.

Quick start

  1. Run the setup wizard from your project root:

    npx @spechycom/archon@latest setup

    Interactive, no flags to memorize: pick your agent(s) (Claude Code, Cursor, Codex), it registers both MCP servers for you (archon agent surface + archon-admin), detects your stack and previews a knowledge template (or starts empty), deploys the editor adapters, and offers an optional local-embeddings step. One command, one screen at a time, a summary before anything is written.

  2. Keep coding normally. The wizard already deployed the editor adapters: CLAUDE.md / AGENTS.md / Cursor rules now instruct the agent to call archon before touching any file. You never call a tool by hand.

  3. Run the same command again any time to update. An existing .spechy/ is detected automatically and the wizard switches to update mode — version bump, template upgrade, adapter redeploy, symbol reindex, and (if enabled) an embeddings refresh, all idempotent.

Requires Node.js >= 18. Windows is supported — the wizard is written to behave the same in PowerShell/cmd.exe as in a POSIX shell — but it's new and hasn't had a field validation pass yet; if something looks wrong, please file an issue. The SQLite database is created and migrated on first start — plain DELETE-journal mode, one .db file, no -wal/-shm sidecars.

Can't or don't want to run the wizard (CI, a locked-down shell, hand control over every file)? See Manual installation below — it's the same result, one step at a time.

Installation

Two surfaces, deliberately two separate MCP server processes:

  • agent — read-only, cannot mutate knowledge. 9 tools. This is what your coding agent gets.
  • admin — everything including the write path, 37 tools total. Keep it away from the coding agent's session: an agent holding spechy_approve_proposal can approve its own proposals, which defeats the human-approval boundary (P-3, INV-6).

The split is enforced twice: admin tools are not registered on the agent surface (invisible to tools/list, "unknown tool" at the protocol level), and every admin service method independently asserts the surface as its first statement — even a caller bypassing the MCP server entirely cannot mutate canonical knowledge from the agent surface.

Always pass --project-root explicitly: an MCP client's cwd is not predictable, and file_path imports are sandboxed to this root.

The setup wizard (see Quick start) does everything below for you — registration for both surfaces, per-client. Read on only if you want to do it by hand, or the wizard isn't an option in your environment.

Manual installation

Claude Code

Option 1 — edit .mcp.json (project root; for Cursor the same JSON goes in .cursor/mcp.json):

// .mcp.json — only the agent surface belongs here
{
  "mcpServers": {
    "archon": {
      "command": "npx",
      "args": [
        "-y", "@spechycom/archon",
        "--surface", "agent",
        "--db", ".spechy/archon.db",
        "--project-root", "/absolute/path/to/project"
      ]
    }
  }
}

Commit this file — that's what MCP's project scope is for. Do not add archon-admin to .mcp.json: the file has no per-agent scoping, so every server in it connects to the coding agent's session. The admin entry goes in your personal (local) scope instead:

claude mcp add-json archon-admin '{
  "command": "npx",
  "args": ["-y", "@spechycom/archon", "--surface", "admin", "--db", ".spechy/archon.db", "--project-root", "'"$PWD"'"]
}' --scope local

Option 2 — CLI for both: same claude mcp add-json shape with --scope project for archon (agent). Run /mcp inside Claude Code afterwards to confirm both servers connected.

(Context cost of mounting admin anyway is small where tool schemas load on demand — measured once at ~426 tokens — so keep it out for the boundary, not the tokens.)

Codex

codex mcp add archon -- npx -y @spechycom/archon --surface agent --db .spechy/archon.db --project-root "$PWD"
codex mcp add archon-admin -- npx -y @spechycom/archon --surface admin --db .spechy/archon.db --project-root "$PWD"

Or write the same two entries into ~/.codex/config.toml directly. Codex has no per-agent scoping either — only add archon-admin where you control who drives the session. codex mcp list confirms registration.

Windows

"$PWD" is bash/zsh syntax. On Windows, edit .mcp.json / config.toml directly (plain JSON/TOML, no shell involved) and use forward slashes in --project-root (C:/Users/you/project) — Node resolves them fine and you skip \\ escaping. From PowerShell, "$($PWD.Path)" replaces "$PWD"; from cmd.exe, paste the absolute path literally. WSL counts as Linux.

Flags

| Flag | Default | Notes | |---|---|---| | --surface | agent | agent or admin | | --db | .spechy/archon.db | Relative paths resolve against --project-root; parent dir auto-created | | --project-root | process.cwd() | Always pass it explicitly | | --templates | — | Overrides the local templates directory used by the init_* tools |

Building the knowledge base

Three entry points, by what you're starting from:

A — new project, bootstrap from a template

Packaged templates: Laravel, Next.js, Python, Go (the last two match on language alone). Vite is recognized as a signal but has no bundled template yet. Via the admin surface:

  1. spechy_init_detect — read-only: inspects manifests (package.json, composer.json, …) and directory shape, scores the templates, returns a preview + preview_hash. Optional placeholders override template values (Next.js app_root is auto-detected between src/app/app). No fit, or empty on purpose → skip_template: true.
  2. spechy_init_confirm({ preview_hash }) — bootstraps canonical knowledge from the previewed template and deploys the editor adapters. The hash is process-scoped; a stale one is rejected.
  3. Add your real project rules with spechy_import_doc (see C), then verify with one spechy_compile_context call against a covered path.

The spechy-setup Agent Skill walks an agent through exactly this.

B — existing docs to migrate

Don't init from a template — bulk-import what you have. The spechy-bulk-import Agent Skill drives the analyze pipeline: spechy_analyze_import_batch splits each file into import units by ## heading and infers kind/edge_hints, spechy_execute_import_plan turns the reviewed plan into pending proposals sharing one bundle_id, and spechy_approve_proposal_bundle lands the whole batch as a single snapshot. Nothing enters canonical knowledge without that approval step.

C — one document at a time

{
  "doc_id": "api-constraint",
  "title": "API layer constraints",
  "kind": "constraint",
  "content": "Never call fetch directly from components. Always go through src/api/client.ts.",
  "edge_hints": [{ "edge_type": "path_requires", "source_value": "src/api/**" }]
}

spechy_import_doc (admin) with inline content or a repo-relative file_path. Write documents small and single-purpose — a document is the unit of retrieval, so one covering three topics gets pulled in for all three. The tool warns above 4096 bytes, at 2+ ## headings, and when no edge_hints are given (an edge-less document is unreachable from any compile). Re-importing a doc_id overwrites content but accumulates edges. A fresh database tells you so: compile_context returns a "Project not initialized" warning instead of silently returning nothing.

Alternatively, keep rules in a YAML seed file (documents[] with file/patterns/depends_on, plus layer_rules[]) committed to git:

spechy-archon seed spechy.seed.yaml

What a session looks like

You don't call tools yourself — the deployed adapter text instructs the agent. What that adds up to, scenario by scenario:

Before touching a file the agent calls spechy_compile_context with the files and a one-line plan, reads what comes back inline, opens deferred documents' source_path itself, and obeys notices.

"Did this change break a written rule?" — spechy_verify_done with the session's diff mechanically checks changed files against every approved constraint carrying a ```verify YAML block (forbid_import, require_companion), and suggests likely-affected test files. No human has to spot the violation first.

"What does changing these files touch?" — spechy_impact({ files }) lists every approved document the DAG maps those files to, with the exact edge as evidence. It answers which rules apply; verify_done answers was a rule broken.

"How does the code get from A to B?" — spechy_search_logic_flow BFS-searches the tree-sitter call graph for the shortest call path between two symbols. See Code awareness.

"Didn't we try this before?" — spechy_recall returns past runs: files touched, plans, session notes. Never injected automatically — asked for when the work has a past. compile_context's notices independently flag failed/reverted attempts from the last 90 days on the same files.

The agent missed a rule you know exists — it (or you) files spechy_observe with event_type: "compile_miss"; a wrong-content rule gets "review_correction". Neither touches canonical knowledge — an admin triages later (spechy_process_observations → proposals → approve/reject). The spechy-triage Agent Skill has the decision tree.

Reviewing / scaffolding / explaining — the three bundled prompts cover "review these files against their rules", "scaffold with template documents included", and "explain the rules for this file to a human".

A doc's source file drifted — spechy_sync_docs (admin) checks file-anchored documents against their sources and opens an update_doc proposal per drifted one; dry_run: true previews.

A packaged template got a new version — compile_context notices tell you proactively; spechy_check_upgrade shows the diff, spechy_apply_upgrade turns it into pending proposals (never overwriting documents you've modified), and spechy_sync_init_manifest re-baselines after the bundle is resolved — skip that last call and the same upgrade is offered forever.

Health checks — spechy_workspace_status for a read-only dashboard; spechy-archon doctor for the full scan (isolated docs, dead edges, missing/stale sources, cycles, oversized docs, near-duplicate rules, co-change suggestions).

Capturing decisions (standing rules)

Tell the agent something meant to outlive the turn — "from now on, don't run tests unless I say so" — and the adapter text has it call spechy_observe with event_type: "manual_note" and ask you to confirm. Your next prompt commits it: the UserPromptSubmit hook reads your reply, and a confirmation approves the rule into the knowledge base and writes it to .spechy/rules.md — a plain-text, git-tracked catalogue you can read or hand-edit (run sync-rules-file after editing directly). Approving the same proposal from the admin surface lands in the same place.

If the new rule collides with an existing one, observe returns conflict_candidates and the agent offers keep both / replace / cancel — persisted via spechy_resolve_conflict.

To retire a decision outright, call spechy_delete_decision (admin surface) with its doc_id — no counter-rule or conflict dance needed. It is a soft-delete: the rule is deprecated (its edges follow, the knowledge version bumps, a snapshot is written) and disappears from every compile, but the record stays in history and snapshots. There is deliberately no hard-delete: snapshots are immutable (INV-3).

Rules scoped to a file/layer/command come back through normal compiles. Behaviour rules with no file to attach to are never loaded unconditionally: the hook matches your prompt's wording against each rule and injects only what looks relevant — deliberately noise-limited (a rule already mentioned this session isn't re-injected, and there's a hard per-session cap).

Team workflows

Cheapest first:

  • Standing rules sync for free — commit .spechy/rules.md. A teammate who pulls picks the rules up on their next session; the hook absorbs whatever's on disk into its own database before rendering over it, so a pulled rule is never silently lost.
  • Commit a spechy.seed.yaml, everyone runs spechy-archon seed after pulling — reviewable as a normal PR diff. Best for structural knowledge.
  • spechy-archon export <file> / import <file> — portable JSON snapshot of the whole database. One-off syncs, pre-risky-operation backups, machine moves. import fully replaces the target DB.
  • Rebuild per environment from the same sources (spechy-bulk-import).
  • Commit the .db itself as a last resort — works, but every pull that changes it swaps the file under the running server (which reopens the connection by itself, at the cost of one warning-carrying compile).

If the team diverges (knowledge_version mismatch), compiles stop matching between people — see docs/TROUBLESHOOTING.md. More role-by-role workflows: docs/USAGE.md.

Tools — agent surface

Tool names take their prefix from one place (src/config/adapter-config.ts), appearing as spechy_compile_context etc. 9 tools, all read-only against canonical knowledge:

| Tool | One-liner | |---|---| | compile_context | The main event — compile the rules for a set of target files | | verify_done | Mechanically check a diff against verify-block constraints | | impact | Which approved documents do these files map to, and via which edges | | search_logic_flow | Shortest call path between two code symbols | | get_compile_audit | Full audit record for a past compile_id | | recall | Episodic index of past runs, plans and session notes | | get_known_tags | Approved intent-tag catalog + tag_catalog_hash for caching | | observe | Report a gap/decision/outcome — never mutates knowledge itself | | workspace_status | Read-only dashboard: versions, backlogs, row counts |

spechy_compile_context

| Param | Type | Notes | |---|---|---| | target_files | string[] (min 1) | Required. Repo-relative paths about to be touched | | target_layers | string[] | Skip layer classification, use these directly | | command | string | Feeds command_requires; "scaffold" also unlocks templates | | plan | string | Free text; enables relevance scoring | | min_relevance | 0..1 | Drops documents below the threshold (needs plan) | | intent_tags | string[] | Cross-cutting tags (e.g. "auth") resolved via tag_mappings into an additive expanded section; [] opts out | | max_inline_bytes | int > 0 | Inline budget, default 8192 | | content_mode | auto \| always \| metadata | Default auto — see Delivery budget | | include_debug | boolean | Adds debug_info: near-miss edges, layer classification, budget drops | | agent_id | string | Recorded in the audit log |

Response: compile_id, snapshot_id, knowledge_version, base.documents[] (each with doc_id, title, kind, delivery, content_bytes, content_hash, plus content when inline, source_path when file-backed, relevance when a plan was given, omit_reason when dropped), base.templates[], base.resolution_path[], optional expanded, warnings[], notices[].

warnings vs notices: warnings are part of the deterministic result — same input, same version, same warnings. Notices report time-varying operational state: pending proposals, an observation backlog, a stale adapter deploy, a newer bundled-template version, an empty knowledge base, and failed/reverted attempts from the last 90 days touching the same files. Because they're time-varying, notices are never written into the audit log.

debug_info.near_miss_edges is the one to look at when nothing matched: it lists patterns that exist but didn't match, so you can tell a wrong glob from a wrong path.

spechy_verify_done

Takes diff (unified diff text) and/or changed_files — at least one. Checks the changed files/layers against every approved constraint document carrying a fenced ```verify YAML block (forbid_import: string[], require_companion: string[]); documents without the block are silently skipped. Returns violations[] (forbidden_import / missing_companion, each with the trigger file and evidence), plus best-effort affected_tests[] — a one-hop static-import scan combined with a git co-change signal, "run these first", never "only run these".

Results are cached content-addressed in the database (keyed on input + knowledge_version), with a small ephemeral .spechy/last-verify.json pointer; the PreCompact hook re-injects the last verdict after context compaction so a verified change isn't re-litigated. Inspect/prune with spechy-archon verify-cache --status [--prune-stale].

spechy_impact

{ files: string[] } → every approved document reachable via path_requires/layer_requires (all kinds, not just constraints), plus second-order documents through doc_depends_on — each with evidence[] (edge type, id, pattern, matched file) and a via_dependency_chain[] (empty = direct match). command_requires is out of scope in v0.

spechy_search_logic_flow

from_symbol/to_symbol (name or symbol_id) + optional max_depth (default and hard cap 10). BFS over the symbol graph's calls edges only — imports/type references/extends/implements are not execution steps. Returns every path at the shortest distance (paths: string[][]), depth: null when unreachable, and ambiguous_from/ambiguous_to (BFS doesn't run) when a name matches multiple symbols. Language coverage:

| Language | Extensions | class | function | method | interface | extends | implements | |---|---|---|---|---|---|---|---| | TypeScript | .ts .tsx | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | JavaScript | .js .jsx .mjs .cjs | ✅ | ✅ | ✅ | — | — | — | | PHP | .php | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ (incl. trait use) | | Python | .py | ✅ | ✅ | ✅ | — | ✅ | — | | Go | .go | ✅ (struct) | ✅ | ✅ (receiver) | ✅ | — (no inheritance) | — (implicit) |

Only file-local qualified-name matches resolve to a concrete to_symbol_id; cross-file references stay name-only. The index is built by spechy-archon reindex-symbols (full-repo scan via git ls-files, unchanged files skipped by content hash, cross-file edges resolved in one batch pass) — installed as a SessionStart hook so it refreshes itself.

spechy_observe

The agent's one write-shaped tool — it records observations, never canonical knowledge:

| event_type | Required payload | |---|---| | compile_miss | target_files[], review_comment (+ optional missing_doc, target_doc_id) | | review_correction | target_doc_id, proposed_content, review_comment | | pr_merged | changed_files[] (+ optional pr_title) | | manual_note | note (+ optional hints[], abstract) | | document_import, doc_gap_detected | free-form object |

compile_miss answers with a triage_hint ("1 near-miss edge exists for src/api/** — check whether target_files should match it"). manual_note/document_import payloads are secret-scanned (AWS/PEM/ GitHub/Slack/OpenAI key patterns + entropy) — a hit warns, never blocks.

spechy_recall

Filters: files, run_id, recent_runs (default 10, max 50), free-text search, gaps: true for accumulated near-miss counts. Returns an index — pointers, not content; follow a compile_id into get_compile_audit for detail. Search ranks episodic hits by a blended score (½ text, ¼ recency, ¼ graph proximity) and penalizes hits whose knowledge has since been superseded. Deliberately never injected into compile_context. Over-budget results say truncated: true.

The rest

get_compile_audit(compile_id) returns the recorded request, resolved doc ids, delivery stats, budget utilization, drops, near-misses, layer classification and duration — errors are logged too, so a failed compile is still auditable. get_known_tags lists the approved intent-tag catalog with a tag_catalog_hash worth caching. workspace_status reports knowledge_version, latest snapshot, recent compile regions, unresolved counts, pending proposals and canonical row counts — and writes nothing, not even a compile-log row.

Tools — admin surface

The admin surface has all 37 tools: the 9 above plus 28 write-path and curation tools.

Importing & maintaining documents

| Tool | Notes | |---|---| | import_doc | Direct import as approved — one transaction: content + edges + version bump + snapshot. Warns on >4096 bytes, 2+ ## headings, missing edge_hints, likely secrets. Edges accumulate across re-imports | | analyze_doc / analyze_import_batch | Read-only: split source files into suggested import units (by ## heading), infer kind/tags/edge_hints, flag overlap with approved docs (and between items, for the batch form) | | execute_import_plan | Turns a reviewed plan into pending new_doc/add_edge proposals sharing a bundle_id. Atomic — one collision rolls back the whole call | | sync_docs | Drift check for file-anchored documents against their sources (whole-file hash or section/line anchor); opens update_doc proposals, dry_run previews | | set_doc_tags | Replaces a document's tag_mappings entry. Outside the canonical DAG: no version bump, no snapshot, no approval | | get_document / get_edge | Full row by id regardless of status — for chasing ids surfaced by doctor |

Proposals & observations — the human-approval pipeline

| Tool | Notes | |---|---| | list_proposals | One status per call (pending default); each carries computed secret_scan_warnings | | get_proposal | Proposal + evidence_observation_ids[], or null | | approve_proposal | Applies the mutation, bumps knowledge_version, writes a snapshot; modifications shallow-merge over the payload first | | reject_proposal | Marks rejected with a comment; never touches knowledge | | preflight_proposal_bundle | Read-only dry run of a bundle: order, dependency/conflict validation, simulated outcome | | approve_proposal_bundle | Applies a whole bundle in dependency order as one version bump + one snapshot; validation failure rolls back everything | | process_observations | Runs every analyzer over unanalyzed observations → { proposed, skipped, errors[] } | | list_observations / archive_observations | Triage helpers; archiving means "triaged", not "resolved" | | resolve_conflict | Persists the keep-both / supersede / cancel answer to a conflict_candidates hit | | delete_decision | Deletes a captured decision (standing rule) directly — soft-delete: deprecates the document (edges cascade, version bump, snapshot), history stays intact. Rejects non-standing documents |

Init, upgrade & operations

| Tool | Notes | |---|---| | init_detect / init_confirm | Template bootstrap with preview + process-scoped hash (see Building the knowledge base) | | check_upgrade / apply_upgrade / sync_init_manifest | Template upgrade as pending proposals; approve document-first (edge approval doesn't verify its target exists), then re-baseline the manifest | | deploy_adapters | Same as the CLI deploy-adapters — callable by an agent when notices flag a stale adapter file | | doctor / get_stats | Health scan and row counts + doc-usage stats (never_delivered hints at recall({ gaps: true }) for per-edge reasons) | | review_edge_scopes / apply_edge_scopes | Scope review for path_requires edges whose glob matches most of the project. Deciding a rule's true scope is a semantic judgement, so archon asks the connected agent instead of guessing; every glob the agent returns is validated against the real file list, and the outcome is a bundle of remove_edge + add_edge proposals a human still has to approve (see ADR 0057) | | maintenance | Read-only gap-coverage preview of what process_observations would keep/drop |

Detailed parameter shapes for the init/upgrade family: docs/adr/0013-init-and-templates.md.

CLI

All commands share --db / --project-root.

spechy-archon setup                  # interactive setup/update wizard (agent
                                     #   registration, init, adapter deploy, repair)
spechy-archon seed <file>            # load a YAML seed file
spechy-archon stats                  # row counts at a glance
spechy-archon doctor                 # full workspace health scan
spechy-archon export <file>          # JSON backup of the whole database
spechy-archon import <file>          # restore (fully replaces the DB)
spechy-archon deploy-adapters        # write adapter files + skills + hooks
                                     #   [--targets claude,cursor,codex] [--dry-run]
spechy-archon sync-rules-file        # absorb hand-edits to .spechy/rules.md
                                     #   (diffed add/update/deprecate, never a wipe)
spechy-archon sync-rules             # sync the shared rules package into documents
                                     #   [--check-latest] asks npm for newer versions
                                     #   (network, fail-open; runs on post-merge)
spechy-archon reindex-symbols        # full-repo tree-sitter symbol reindex
spechy-archon graph [<file>]         # interactive HTML view of the knowledge base
                                     #   (default .spechy/graph.html)
spechy-archon eval [--replay] [--json]
                                     # delivery-quality report from the audit log;
                                     #   --replay re-runs logged requests on today's code
spechy-archon bench [--full] [--external] [--json]
                                     # retrieval/DAG/resource benchmark; --full adds
                                     #   LLM-judge A/B (needs ANTHROPIC_API_KEY);
                                     #   --external adds 15 real-commit cases,
                                     #   reported separately from the internal corpus
spechy-archon embed-docs             # compute/update Ollama embeddings
                                     #   [--model <name>] [--ollama-url <url>]
spechy-archon verify-cache --status [--prune-stale]
                                     # inspect / prune the verify_done cache
spechy-archon prune-log --older-than <days>
                                     # fold old audit rows into aggregates, then
                                     #   delete them — no default on purpose
spechy-archon maintenance            # gap-coverage preview; --reclaim-observations
                                     #   [--stale-after-days <n>] re-queues analyzed-
                                     #   but-never-cited observations (default 1 day)
spechy-archon webui                  # local browser admin panel (127.0.0.1 only)
                                     #   [--port <n>] [--no-open]

An unknown command or flag prints usage and exits 1 instead of silently starting the server. The hook forms (sync-rules --hook, verify-cache --hook, reindex-symbols --hook) are installed for you by deploy-adapters — see Hooks.

Prompts & resources

The MCP server also exposes three prompts (visible in Claude Code's / menu) and one resource:

| Prompt | Args | What it does | |---|---|---| | spechy-review | target_files (comma-separated) | Compile context for the files, then review them against exactly what came back | | spechy-scaffold | target_files | Compile with command: "scaffold" so template documents are included, then follow them | | spechy-explain | target_file | Read-only: summarize the matched documents for a human, propose nothing |

spechy://guide/workflow is a markdown operating guide covering the read/write/approve cycle — the same content underlying docs/USAGE.md.

Knowledge model

Documents — doc_id, title, kind (guideline / pattern / constraint / template / reference), content, content_hash, status, source_path. template is special-cased by the allocator.

Edges — how a document becomes reachable:

| edge_type | Source | Matching | |---|---|---| | path_requires | glob pattern | picomatch against each target file | | layer_requires | layer name | file → layer via layer_rules, or target_layers | | command_requires | command name | exact string equality | | doc_depends_on | doc_id | transitive pull-in; cycles rejected at insert |

Layer rules — path_pattern → layer_name; most specific match wins per file, unmatched classifies as null.

Specificity is computed from the pattern, never stored by hand: per / segment, ** adds 0, a *-containing segment adds 1, a literal adds 2 — so src/api/client.ts (6) outranks src/api/** (4) outranks src/** (2). Only path_requires edges have specificity. Final ordering is specificity ↓, priority ↑, doc_id ↑ — no ties left to iteration order.

Delivery budget

Every resolved document ends up inline (content in the response), deferred (metadata + source_path, the agent reads the file itself) or omitted (policy):

  1. Templates are omitted unless command === "scaffold" or content_mode === "always".
  2. Documents without a source_path exist only in the database — there is nothing on disk to defer to, so they always inline. If their total exceeds max_inline_bytes, the compile returns a budget_exceeded error naming the offenders instead of silently truncating.
  3. Everything else competes for the remaining budget, ordered by class (template → document → expanded), then relevance ↓, priority ↑, doc_id ↑. Under auto, a file-backed document over 4096 bytes never becomes an inline candidate; metadata defers everything; always makes every file-backed document a candidate.

Relevance (when a plan is given) is the fraction of plan terms found in the document — word-boundary and camelCase-aware, purely lexical. It orders and optionally filters; it is not semantic search (that's embeddings).

Determinism

| | | |---|---| | INV-1 | The read path only sees status = 'approved' rows | | INV-2 | doc_depends_on cannot form a cycle | | INV-3 | Snapshots are immutable once written | | INV-4 | knowledge_version increases monotonically | | INV-5 | Every compile is auditable (inputs, outputs, snapshot). Two documented narrowings: an unwritable DB reports the missed audit write in warnings rather than failing the call, and prune-log folds old rows into aggregates before deleting them | | INV-6 | The agent surface cannot mutate canonical knowledge — admin tools aren't registered on it at all | | P-1 | Same input + same knowledge_version = same output | | P-3 | Every mutation of canonical knowledge requires human approval |

Reasoning behind each: docs/INVARIANTS.md and docs/adr/.

Semantic embeddings

On by default, zero configuration, fully local. The deterministic DAG result never depends on this — embeddings only add suggestions for rules about the same topic in different words (a "billing" rule surfacing for a "payments" plan). Runs against a local Ollama server; nothing leaves your machine.

If Ollama isn't running when needed, archon starts it itself and shuts it down after — it never touches an instance it didn't start. Startup gets up to 30 seconds; past that (or with Ollama not installed at all) the call falls back to the plain deterministic result with a note in warnings. No request ever fails because of embeddings.

To get value from it:

ollama pull granite-embedding:278m     # once, ~560MB, multilingual
spechy-archon embed-docs               # re-run after editing rules;
                                       # only re-embeds what changed

SPECHY_EMBED_MODEL and SPECHY_OLLAMA_URL customize which model/server — they don't turn the feature on or off.

Code awareness

Three layers, all read-only and fail-open:

  • Symbol graph — reindex-symbols parses the repo with tree-sitter (TS/JS/PHP/Python/Go) into symbols/symbol_edges; search_logic_flow answers path questions over it.
  • Impact analysis — impact maps changed files to the rule surface they enter.
  • Mechanical verification — verify_done enforces machine-checkable constraint rules against a diff and suggests affected tests, with a compaction-surviving result cache.

Details under Tools — agent surface.

Episodic memory

Every server run is recorded (files touched, plan, best-effort session id); turn outcomes land as session_note observations. recall queries this history on demand, and compile notices proactively mark files with recent failed/reverted attempts. History is kept out of compile_context's result on purpose — the past is a pointer, not a prompt tax.

Document discovery

New doc-like files added to the repo after your initial import don't slip through: the SessionStart hook diffs git ls-files against approved documents and pending proposals, announces "N new document candidates" at session start, and points at the spechy-bulk-import skill. Discovery itself proposes nothing — the import still goes through pending proposals and human approval (P-3).

Graph visualization

spechy-archon graph            # writes .spechy/graph.html
spechy-archon graph out.html   # or wherever

One interactive HTML file: approved documents, every edge type, embedding-similarity links, and the pending backlog (proposals/observations) as ghost nodes. No server needed; it loads D3 from a CDN, so viewing needs internet. Good for spotting isolated clusters, over-connected hubs, and where the backlog accumulates.

Quality tooling

  • eval — delivery-quality report from the audit log: missed and unnecessary deliveries, budget waste; --replay re-runs logged requests against their own snapshot with today's code.
  • bench — labeled retrieval/DAG/resource corpus; --external adds 15 real-commit cases reported separately (so "100% on internal fixtures" can't hide "60% on real tasks"); --full adds an LLM-judge generation A/B (needs ANTHROPIC_API_KEY, skipped without it).
  • doctor — the health scan, also callable as an admin tool.
  • prune-log — audit-log retention with the fold-then-delete rule; auto-generated session notes are pruned, hand-written ones kept.

Editor adapters

deploy-adapters writes a managed rule block into the files editors actually read, and installs the bundled skills and hooks:

spechy-archon deploy-adapters --db <path> --project-root <path> [--targets claude,cursor,codex] [--dry-run]

| Target | Files | |---|---| | claude | CLAUDE.md + .claude/skills/*/SKILL.md + .claude/settings.json hooks + git hooks | | cursor | .cursor/rules/spechy-process.mdc + .cursor/skills/*/SKILL.md | | codex | AGENTS.md + .codex/skills/*/SKILL.md |

CLAUDE.md/AGENTS.md get a block wrapped in <!-- spechy:start --> / <!-- spechy:end --> — everything outside it is yours and never touched. Missing/duplicated markers error out rather than guessing at a rewrite. The Cursor file is entirely spechy-owned (<!-- spechy:managed -->); an existing file without that marker is treated as hand-written and reported as a conflict (which exits 0 — a warning, never a blocker).

On every compile the server re-renders each deployed target with the running version's config and compares hashes recorded in adapter_meta; a mismatch (e.g. after upgrading archon) surfaces as a notice. This does not detect hand-edits after deploy — the check reads adapter_meta, not the file (known limitation, see docs/adr/0012-adapters.md).

The rule block itself is the entire enforcement mechanism — MUST/NEVER instructions the agent reads every session. There is no technical gate that forces an agent to compile first.

Hooks

Installed by deploy-adapters (claude target). All fail-open: an erroring hook logs to stderr and exits 0 — never blocks a session, commit or merge.

Claude Code hooks (marker-managed entries in .claude/settings.json; your own hooks are left alone):

| Event | Command | Purpose | |---|---|---| | SessionStart + UserPromptSubmit | sync-rules --hook | Absorb .spechy/rules.md edits, inject relevant behaviour rules, commit confirmed rule captures — see Capturing decisions | | SessionStart | reindex-symbols --hook | Refresh the symbol index + run document discovery | | PreCompact | verify-cache --hook | Re-inject the last verify_done verdict after compaction |

Git hooks (managed blocks in .git/hooks/):

| Hook | Command | Purpose | |---|---|---| | pre-commit | sync-rules | Keep shared rules-package content synced into documents | | post-merge | sync-rules --check-latest | Same, plus one fail-open registry check for a newer rules package |

Agent Skills

Three Agent Skills ship in skills/ and deploy with the adapters:

| Skill | Use when | |---|---| | spechy-setup | Setting up archon in a project, configuring MCP, bootstrapping knowledge | | spechy-bulk-import | Bulk-importing existing documentation via the analyze pipeline + bundle approval | | spechy-triage | Triaging observations, reviewing compile misses, maintaining knowledge quality |

Source lives flat (skills/spechy-setup.md); deploy rewrites cross-links and drops each at <target>/skills/<name>/SKILL.md with a <!-- spechy:managed-skill --> marker — a hand-written file at that path without the marker is never overwritten, only reported as a conflict.

Status & limitations

Everything documented above is shipped. Known gaps, current as of this writing:

  • list_proposals filters one status per call — no combined query.
  • archive_observations doesn't check evidence — archiving an observation already cited by a proposal is allowed on purpose ("triaged" ≠ "resolved").
  • apply_upgrade doesn't finalize itself — call sync_init_manifest after resolving the bundle, or the same upgrade is offered forever.
  • intent_tags resolve nothing until set_doc_tags populates tag_mappings — nothing does that automatically; an SLM tagger port exists (src/core/read/tagger.ts) but isn't wired in.
  • Four packaged templates (Laravel, Next.js, Python, Go). Vite is a recognized signal without a bundled template. Everything else: bulk-import or manual, via skip_template: true.
  • Adapter staleness detection reads adapter_meta, not the files — hand-edits after deploy go unnoticed.
  • search_logic_flow v1 scope — Python decorators and Go generics/defined non-struct types are not parsed; Go extends/implements are deliberately empty (no inheritance; interface satisfaction is structural).

Development

pnpm install
pnpm test          # vitest
pnpm check         # tsc --noEmit && biome check .
pnpm build         # rm -rf dist && tsc -p tsconfig.build.json

Run a local build without publishing:

node dist/main.js --surface admin --db /tmp/archon.db --project-root "$PWD"

Layout: src/core/store (schema, repository, snapshot, seed, migrations), src/core/read (compiler, allocator, relevance, embedder, verify, impact, symbol graph), src/core/memory (search, recall, eval, bench, prune), src/core/init (detection, templates, bootstrap/upgrade), src/core/automation (observation analyzers, secret scan), src/adapters (editor targets + hooks), src/mcp (server, services), src/config/adapter-config.ts (tool prefix — change it here, never as a string literal).

License

Unpublished license terms — internal Spechy project.