seamark

package module
v0.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 13, 2026 License: Apache-2.0 Imports: 1 Imported by: 0

README

License Go Platform

Repository history and code blast radius for your editor, agents, and CI.

Seamark finds which files change together, explains unusual code, and shows which code paths can reach a database write, a process spawn, or production infrastructure. It provides editor diagnostics, answers why on the CLI, and checks agent commands against your rules. The rules are machine checks, not paragraphs in a prompt. Mistakes get caught instead of explained.

A seamark is a navigational marker that shows both the safe channel and the hazard.

Why seamark?

Most code-graph tools use the same design: parse symbols, store a graph, and save tokens. Three problems remain:

Problem What seamark does about it
The graph knows what the code is, not why Mines git history: empirical co-change (with lift), the commit trail per region, revert markers
Agents repeat mistakes; the fix is a prompt paragraph that costs tokens every turn and gets ignored Rules are checks, evaluated at edit/run time; they cost nothing until violated
Agents cause real damage and the guardrails are regex denylists A real shell parser + effect classification + policy over your declared environment, with an append-only audit log

On a production monorepo with 831 files and 447 commits, Seamark built the full Python, TypeScript, and Go index in ~3s. Its strongest signal was a manually synchronized Python↔TypeScript schema contract: 38 shared commits with lift 6.6. Language servers cannot detect this history signal, but Seamark reported it as a save-time diagnostic. Seamark also found 598 symbols that could reach a sink such as a database write, process spawn, or network egress.

Every result in the index is falsifiable and traceable to a parse, commit, or policy file. Seamark does not store LLM-generated "insights." The index is local. Seamark requires no account or API key and sends no telemetry.

Get started

brew install seamark-dev/tap/seamark

cd your-project
seamark init        # scaffold config + wire the Claude Code agent hooks
seamark index       # parse + mine history + propagate effects (~seconds)
seamark orient      # map the repo: hubs, load-bearing symbols, decisions

The tap ships pre-built bottles for Apple Silicon macOS and x86_64 Linux; other platforms (including Intel macOS, Homebrew Tier 3) build from source automatically. Shell completions install with the formula. seamark why <symbol-or-file> then answers the first real question about any unfamiliar code.

Other install routes

Tagged releases attach smoke-tested archives for macOS and Linux (amd64/arm64) with SHA-256 checksums:

sha256sum -c --ignore-missing SHA256SUMS   # macOS: shasum -a 256 -c --ignore-missing SHA256SUMS
tar -xzf seamark_*.tar.gz
mkdir -p ~/.local/bin
install seamark_*/seamark ~/.local/bin/

A matching checksum verifies the archive against the published SHA256SUMS. This verifies integrity, not publisher identity. Artifact signing is not yet available (docs/STATUS.md).

Or build from source — Go ≥ 1.25 and a C compiler (tree-sitter uses CGO):

git clone https://github.com/seamark-dev/seamark && cd seamark
make install        # builds and installs to ~/.local/bin/seamark

Both of these routes install to ~/.local/bin; make sure that directory is on your PATH. The Homebrew route handles PATH on its own.

seamark init is optional. It performs the one-time setup without overwriting existing files. It writes starter .seamark/policy.yaml, .seamark/lessons.yaml, and .seamark/config.yaml; adds the .gitignore exceptions; and merges the gate and review-lesson hooks into .claude/settings.json. Existing hooks remain unchanged, so you can safely run the command again. Pass --print to preview each change. A fresh install with the starter warn policy never blocks anything. An existing enforce policy remains in force. Enabling enforcement is a separate, explicit action (Journey 3).

Optional agent skills help your coding agent decide when to use Seamark. See the installation examples for Claude Code and Codex below.

The graph and your proposal decisions are in one SQLite database under .seamark/, beside the audit logs and generated reports. Git ignores these files but keeps the reviewed YAML overlays. After you decide on proposals, the database is not a temporary cache. See Durable state.

The mental model

Five nouns cover the learning pipeline, used with exactly these meanings everywhere — CLI help, docs, and report alike:

review comment or fix commit         what a reviewer (or a fix) said, once
        ↓  mined by `seamark index --reviews` or `--fixes-only`
finding      one raw observation, kept verbatim with its provenance
        ↓  clustered on recurrence (≥2)
lesson       a pattern recurring across findings in a region — review-
             or fix-derived alike
        ↓  distilled by your agent CLI (optional) — or written by hand
proposal     a candidate rule awaiting YOUR decision
        ↓  `lessons --apply`  (a dismissal sticks until its evidence changes)
pin          an accepted rule, surfaced to agents at edit time

And five more for the risk layer:

Term Meaning
effect an observable capability — db:write, proc:exec, net:egress, infra:mutate
sink an API or command that directly produces an effect; everything else reaches effects transitively
decision one historical record: a commit, PR, or revert mined from git
region a file-or-directory scope that lessons, pins, and policy attach to
coupling empirical co-change measured from history — "usually travels with", never "depends on"

Journey 1: understand an unfamiliar repository

Five minutes from clone to knowing where the bodies are buried.

seamark index       # ~seconds; parse + mine history + propagate effects
seamark orient      # the one-screen overview

orient shows scale, module layout, the most-called production API, the change hubs (files whose edits rarely travel alone), and the recent decision trail. Then interrogate anything that looks load-bearing:

$ seamark why gate.EvalCommand
internal/gate.EvalCommand  (function)
  defined  internal/gate/gate.go:136
  sig      func EvalCommand(p *Policy, catalog *effects.Catalog, root, commandLine string) (*Decision, error)
  effects  proc:exec [depth 1]

callers (4)
  [qualified]     internal/cli.newGateCmd                      internal/cli/gate.go:21
  (+3 in tests)

calls (8)  — 3 resolved by name match only
  [unique-name]   internal/effects.Catalog.MatchCommand        internal/effects/effects.go:140
  [same-package]  internal/gate.gitPush                        internal/gate/gate.go:388
  ...

usually changed with  (empirical, lift > 1 means beyond chance)
   6/58  commits  lift 4.1   internal/effects/effects.go  · mostly MatchCommand, Load
recent decisions
  2026-07-26  ...  Implement Python parser

Read it top to bottom: this function can ultimately spawn a process (one hop away), here is its production surface (test callers collapse to a count instead of burying it), and here is the commit trail. Every call edge declares how it was derived ([qualified], [same-package], [same-class], or the low-confidence [unique-name]), so you always know how much to trust an edge. The usually changed with lines carry function grain: · mostly … names the functions git's hunk headers show actually moved in the shared commits — a factual report, not a statistical claim. On a repo with real history, this section is where the surprises live.

Languages

Language Symbols & calls Effect sinks Notes
Go multi-module monorepos supported
TypeScript / TSX / JavaScript ES-module resolution, cross-file named imports
Python relative imports, __init__ packages, self/cls resolution
SQL, HCL history layer only planned co-change needs no parser

The history layer (co-change, decisions) is language-agnostic — it works on every file git tracks.

Journey 2: stop repeating review mistakes

Ten minutes to make the last hundred code reviews teach your agents.

Agents repeat mistakes. Review threads contain the correction, but the lesson does not persist after the session. It can appear once from CodeRabbit, once from Copilot, and once from a tired reviewer. Mine it instead:

seamark index --reviews    # review comments via your gh CLI + fix commits from local git
seamark index --fixes-only # fix commits only; local git, deterministic and offline

Use --fixes-only for a pinned historical checkout or a run that must not use live GitHub data. It refreshes the local-fix source without fetching or deleting the existing review corpus. Use a fresh index when the experiment must contain only fixes.

Seamark shows recurring feedback for the relevant region through the same seamark why and seamark orient commands that an agent already calls. It does not add tokens to every turn or add content to CLAUDE.md. For example, from a private monorepo:

$ seamark why scripts/fetcher.py
...
reviewers keep flagging  (recurring across pull requests)
  ×2     scripts     [copilot]     this script hard-codes a postgres url including credentials
  ×2     scripts     [coderabbit]  RUF003

Lint codes are the shallow end. The one-off comments the recurrence threshold hides are often the most valuable, and the ledger keeps them for the distiller and for you:

$ seamark lessons --list
  ×1   core/session_calendar.py      [coderabbit]  loader is stricter on the go side than here — drift risk
  ×1   ingestor/cmd/ingestor/run.go  [coderabbit]  do not invalidate the live archive before its replacement succeeds
  …

A Python↔Go session calendar quietly drifting apart, an archive invalidated before its replacement exists — each said exactly once, each a production incident wearing a review comment. seamark lessons --distill is how they become permanent: it batches the raw findings through your own agent CLI (a full disclosure prints first; --dry-run prints it and sends nothing — the exact payload is documented in docs/data-flow.md) into proposed pins — every proposal cites its evidence, nothing lands without your explicit --apply, and a dismissal sticks until its evidence changes.

That drift-risk one-off, distilled together with a "regenerate the frontend schema after this change" comment from api/schemas.py, is now this applied pin in the monorepo's lessons.yaml — two review remarks that became the repo's contract-synchronization rule:

- rule: keep-parallel-implementations-consistent
  region: api
  regions: [api, core]
  note: "When two runtimes or call sites share a contract, mirror validation
    strictness and regenerate derived artifacts: run make sync-api after
    schemas.py changes, and keep the Go/Python seed loaders equally
    fail-fast."
  # distilled by claude/v2 from 2 findings (seamark lessons --distill, p60)

The fallback region set is computed from the cited evidence, never guessed: a theme living in api AND core says exactly that instead of claiming the whole repo. A verified trigger can narrow that fallback to the exact edit surface where the mistake is introduced. (Measured on the two development corpora, evidence-coverage regions cut repo-wide * pins from 35 of 65 to 3.)

More pins the two repos distilled and applied:

  • recompute-derived-fields-with-source"When a write changes inputs to derived columns (net_pnl, dedup_hash, avg price), recompute and persist them in the same transaction as the source update; never leave derived state stale or split across commits."
  • no-parquet-for-live-session"Never read the historical 1m parquet archive for today's/live session data — it lacks live-session bars and mixes historical with live."
  • unsubscribe-on-every-exit-path (v2/pkg/engine/resolve) — "Every subscription termination path must also unsubscribe, not just close the writer; closing alone leaves the sub registered in triggers, skewing counters and leaking resources."
  • copy-shared-mutable-data (v2/pkg/engine) — "Clone caller-supplied maps/headers and copy byte slices from pooled or datasource-owned buffers before storing or mutating them; aliasing reusable backing arrays causes cross-request races and corruption."

None of that is lint. Those are the house rules a senior reviewer carries in their head — extracted from this repo's own history, scoped to the region they came from, and injected exactly when an agent is about to edit there. That injection is the edit hook seamark init wires: one local index read per edit, silent for files with no lessons. (Proven: a headless agent given a plain edit task with no mention of seamark still named every flagged rule.)

Every distilled pin also carries a live confidence tier — strong / fair / weak, computed on each read from what its citations still support: distinct events, review+fix corroboration, recency, and whether the cited files still exist. Weak pins lose injection-budget slots to strong ones and are tagged when they do surface; nothing is stored, nothing is model-scored.

The whole lifecycle is seamark lessons, one flag per decision:

Flag What it does
--file <path> the lessons that would fire when editing one file — the hook's view, uncapped
--list / --region <dir> the raw ledger, one-offs included, with copy-paste config syntax
--distill batch new findings through your agent CLI into proposed pins; already-distilled evidence is never paid for twice (--region, --limit, --dry-run budget it; a preflight always discloses first)
--proposals the decision ledger, free: pending/applied/dismissed, each with its confidence facts, its prompt-era note, its outcome verdict (applied pins), and the regions today's inference would assign
--apply p3,p7 pin chosen proposals (ranges work: p1..p9); writes lessons.yaml only with distill.write, else prints the block to paste
--dismiss p2 record a no — the same evidence is never re-proposed
--prune p16,p45 retire pins that restate another (the ledger names the clusters); the theme stays pinned by its survivor
--retarget p3 update an applied pin to the regions current inference supports — verified trigger scopes when available, evidence coverage otherwise; ordinary failures roll lessons.yaml back, and if a hard crash leaves the file ahead of the ledger, the next run detects and repairs exactly that
--extract-triggers ask your agent CLI where each already-pinned mistake is MADE (see below); every answer is verified, answered proposals are never re-paid, and --dry-run discloses first
--stats the firing log: which lessons actually reach agents (split by surface: hook / change_set / check), which never fire — the decay signal — and per-pin outcomes: did the mistake recur after the pin started firing (working / not landing / untested)
--hook the PreToolUse entry point seamark init wires; offline, silent when a file has no lessons

Evidence tells Seamark where a problem was observed or repaired. A trigger path tells it where an author can introduce the mistake. Those can differ: a "regenerate the client" finding may live on the generated TypeScript file, while the omission begins in the backend model. Distillation must answer the trigger question for every proposed pattern. Each named path must parse and exist in the working tree, then it is accepted when it is an exact cited production path, the immediate parent of one, or co-change history confirms it against the cited evidence. Verified triggers become the precise delivery scopes—even a single file—while evidence coverage remains the fallback if none verify. Unverified names never move delivery. The --proposals ledger and HTML report show the current scope, and --retarget applies it explicitly to an installed pin. --extract-triggers asks the same question for proposals distilled before trigger extraction existed. When that semantic question changes, legacy negative answers are re-asked once; existing positive trigger paths are left alone.

A clean matched benchmark of this exact failure mode measured a +100 percentage-point difference-in-differences effect across five pairs per scope: the trigger-scoped lesson preserved the invariant 5/5 versus 0/5 without the hook, while the repair-scoped control was 3/5 in both arms and never fired. This is controlled synthetic evidence under one pinned model/runtime, not external validation; see the full report.

A second matched benchmark reproduced the mechanism on an exact historical OpenTelemetry-Go commit. Trigger-scoped delivery preserved the parallel histogram-reset invariant 5/5 versus 0/5 without the hook; the repair-scoped control was 1/5 versus 0/5. The resulting +80 percentage-point difference-in-differences effect passed its precommitted +40-point threshold, and all 20 sessions completed the visible task. This is public-repository evidence for one pinned task and runtime—not a claim that every repository or model benefits. The step-by-step OpenTelemetry case study connects historical learning, maintainer acceptance, hook delivery, and the audit trail. Its reusable evidence rules are in the case-study protocol. See the full report and benchmark runbook for the independent paired evidence.

Mined text is scrubbed of secret-shaped values (connection strings, tokens) before it is stored — a credential a reviewer quoted once must not be re-broadcast into agent context on every edit — and review evidence has a shelf life: two years by default, with the newest 200 comments always kept so slow repositories stay covered.

A committed .seamark/lessons.yaml tunes what surfaces — mute kills noise, pin forces what must never be ignored (single region or a regions: [api, db] set), threshold sets the recurrence bar, pin_budget caps the hook's injection (default 3), change_budget the change_set block (default 6), and hook_delivery controls whether a matching lesson repeats during the current agent context.

Choose how often hook lessons repeat

Choose one of these settings in .seamark/lessons.yaml. If the key is omitted, Seamark uses always.

Use the default when every matching edit should receive the reminder:

# Repeat matching lessons after every edit (default).
hook_delivery: always

Use the opt-in mode to reduce repeated context when an agent edits the same area several times:

# Deliver each lesson once, then allow it again after context compaction.
hook_delivery: once-per-context

once-per-context suppresses only lessons already delivered in the current agent context—the conversation content the model can still see. The seamark init command installs the PostCompact reset that lets those lessons return after Claude Code compacts old context. After upgrading Seamark, run seamark init once to install or refresh these hooks; changing the setting later does not require running init again. If the session identity or local state is unavailable, Seamark fails open and delivers the lesson normally.

See Hook delivery modes for the state, privacy, and lifecycle details. seamark report renders the whole decision queue as one self-contained HTML page. The full pipeline — mining heuristics, fix-commit classification, region inference, confidence tiers, distillation economics, near-duplicate pruning, the firing stats, and the outcome loop that answers whether pins actually change behavior — is documented in docs/lessons.md.

Journey 3: guard agent commands

Run warn-mode for a week; then decide if anything should actually block.

seamark gate classifies a shell command's effects and evaluates your policy before the command runs — built for agent pre-execution hooks and CI:

$ KUBECONFIG=~/.kube/prod.yaml seamark gate --command "terraform apply -auto-approve"
verdict  deny (mode: warn)
effects  [infra:mutate]
  [deny] no-prod-infra-mutation: production infrastructure mutation requires maintainer action

What makes it more than a denylist:

  • A real shell parser (mvdan.cc/sh) — every command in pipelines, &&-chains, and $(substitutions) is classified; unparseable input fails closed.
  • Indirection is detected, not missed$TOOL apply cannot be classified, so it is flagged as dynamic and policy decides; this is exactly the trick that walks through regex denylists.
  • Wrappers unwrapsudo -E env FOO=bar kubectl delete classifies as kubectl.
  • Subcommand-awareterraform plan passes, terraform apply does not; an argument merely named "apply" does not trigger.
  • Environment-awareenv.is_prod derives from the variables you declare (KUBECONFIG, DATABASE_URL, …) and your prod markers.

Policy is CEL over effect, command, env, and diff, in .seamark/policy.yaml:

mode: warn # report only; flip to "enforce" when the rules have earned trust
deny:
  - id: no-prod-infra-mutation
    when: 'effect.contains("infra:mutate") && env.is_prod'
    message: production infrastructure mutation requires maintainer action
require_approval:
  - id: prod-db-write
    when: 'effect.contains("db:write") && env.is_prod'
    message: database write against a production environment

seamark init wires the gate into .claude/settings.json as a PreToolUse hook on Bash (seamark gate --hook — the payload is read natively, no jq). By default the hook follows policy.yaml's mode (warn), so a first install never blocks a command. Opting in with seamark init --gate-mode enforce bakes --enforce into the hook: deny/require_approval verdicts exit 2 and block, and the gate fails closed — a malformed payload, a broken policy file, or an internal error blocks the command instead of silently allowing it. Re-running init without --gate-mode keeps whatever mode is installed, and every run ends with a gate line stating the effective behavior.

The same init also wires the edit-time lessons hook and a silent PostCompact reset. The reset matters only when .seamark/lessons.yaml opts into hook_delivery: once-per-context; default always delivery is unchanged.

The agent sees the denial reason and corrects course; you see every decision in .seamark/audit.jsonl — an append-only trail of what your agents attempted, when, and why it was allowed or blocked. The log is secret-safe by default: entries store the normalized command names, a SHA-256 of the input, the verdict, and the policy hash — never the raw command line, which frequently carries tokens, passwords, and connection strings. It is created 0600 and rotated by size and age (one previous generation kept; entries expire after 30 days). To also persist the input line, opt in via policy.yaml:

audit:
  raw: true

Raw inputs are scrubbed of secret-shaped patterns best-effort — treat such a log as sensitive.

Blast radius of a diff

git diff | seamark check          # or: seamark check   (uses git diff HEAD)

Changed lines map to symbols; symbols carry transitively-propagated effect tags; the union is what the change can ultimately reach — even when the edited function never touches a sink itself. Policy rules over diff.effects gate merges the same way gate gates commands, and diff.unindexed_files exposes coverage blind spots to your rules — changed files the index cannot attribute never silently read as safe. The text output also appends the recurring lessons governing the touched files — clearly marked advisory, never part of the verdict, printed even when the verdict blocks (a deny is exactly when they matter). --json stays verdict-shaped; --enforce makes blocking verdicts exit 2.

What Guard can and cannot defend against is stated plainly in docs/threat-model.md: it is a defense-in-depth policy layer, not a sandbox.

Surfaces: editor, agents, maintainers

Editorseamark lsp is a secondary language server that runs alongside gopls, pyright, or tsserver. It adds information they cannot see: effect reach and co-change partners in hover text, caller counts in code lenses, and save-time omission diagnostics ("usually changed together, not in this change: src/api/schema.ts — 38/58 commits, lift 6.6"). The thresholds are conservative because users disable noisy diagnostics. See docs/editors.md and the ready-made configurations in editors/.

Agentsseamark mcp provides the Model Context Protocol over standard input and output. It provides five tools, not forty. Tool definitions return to the model on every turn, so a large server can use more tokens than it saves:

Tool Answers
orient one-screen repo overview: modules, most-called API, change hubs, recent decisions
why everything about a symbol or file: callers with confidence, co-change, commits, the lessons that govern it
change_set pre-edit: what history says changes together with your planned files, plus the budgeted lessons governing them (new files included)
check a diff's reachable effects and the policy verdict, with the touched files' lessons as a marked advisory
expand progressive disclosure: turn any ref a report returned into its content — source lines, or lessons:<dir> for an area's raw review findings

Register it in Claude Code by dropping an .mcp.json at the repo root (this repository ships one):

{ "mcpServers": { "seamark": { "command": "seamark", "args": ["mcp"] } } }

Every tool call checks the workspace fingerprint and re-indexes when something changed. Answers do not come from a stale graph. The server also exposes seamark://orient and seamark://status as resources. It provides usage guidance: check related files before editing several files or unfamiliar code, review the diff before finishing, and request a repository overview only when the code is unfamiliar. The optional skills below expand this guidance into task-specific workflows.

Agent skills

Agent skills give your coding agent instructions for common tasks. Seamark includes three skills for Claude Code and Codex. They explain when to use Seamark's tools and how to interpret the results.

Skill Helps the agent…
seamark-understand-repo Explore unfamiliar code, find important files, and understand past decisions.
seamark-plan-change Check related files, possible side effects, and past review feedback before editing.
seamark-review-change Check the actual diff against policy, the companion files it left out, and lessons, then run the relevant tests.

For example, when you ask an agent to change an API across several files, the planning skill tells it to check which other files usually change with them. This can reveal a generated client or another implementation that needs attention.

The skills do not require a repository overview before every task. For a typo, comment edit, or lookup in a known file, they tell the agent to work directly with the code.

Measured effect: in a paired benchmark on three synthetic repositories whose history carries a companion file the task must not forget (Claude Haiku 4.5, medium effort, five pairs per repository), MCP + skills kept the companion in sync in 12 of 15 sessions against 1 of 15 for the MCP server alone, a mean lift of 73 percentage points. The skills arm processed about twice the context per session. That spend is yours to decide, so the skills stay opt-in; the assessment is in bench/skills-report-v2.md and the protocol in bench/README.md.

Install

From your repository, choose one command:

seamark init --skills=claude   # Claude Code
seamark init --skills=codex    # Codex
seamark init --skills=all      # Both

The skills are installed in .claude/skills/ for Claude Code and .agents/skills/ for Codex. Commit the installed skill directories to share them with your team.

You can also use seamark init --skills: it installs for Claude Code and adds the Codex copies if the repository already has an .agents/ directory.

Let the agent call the tools without prompts

Claude Code asks before each seamark tool call unless a permission rule allows it. A skill's own allowed-tools grant lasts one turn, and in the version we tested (2.1.257) it applied only when the skill was invoked by name, although the documentation says it should also apply when the agent picks the skill. Persistent rules remove the prompts either way:

seamark init --skills --approve-tools   # or --approve-tools on its own

This merges eight exact allow rules into .claude/settings.json: one per seamark MCP tool (mcp__seamark__orient and the other four) and one per seamark skill (Skill(seamark-plan-change) and the other two). The tools are read-only queries over the local index, and a skill rule only lets the agent load that skill's text. Existing rules stay, nothing is ever removed, and --print previews the change. The tool rules are spelled with the server name your .mcp.json registers (mcp__sm__orient for a server named sm), a server-wide mcp__seamark rule counts as approving every tool, and a tool listed under permissions.deny or permissions.ask is reported as kept, never re-approved: Claude Code applies deny before allow, so an allow entry there would change nothing.

Codex approvals are written when --skills names Codex (codex or all) or, without an explicit client, when a .codex/ directory exists; --skills=claude configures Claude Code only. The skills themselves go to .agents/skills/ when that directory exists. When only one of the two directories exists, init says which approvals are still missing. A registration without approvals, for either client, is reported as partial with the re-run hint, because every call still prompts.

For Codex, run this from your repository to install the skills, connect Codex to Seamark, and let it use Seamark's five tools without asking for permission each time:

seamark init --skills=codex --approve-tools

The connection and tool permissions are saved in .codex/config.toml. Codex must trust the repository for these settings to apply. Seamark preserves your existing settings and reports any conflicting restrictions. Your personal or organization settings may still require approval.

Codex can choose the appropriate skill when you ask it to explore unfamiliar code, implement a change, or review your work. You can also name a skill directly:

$seamark-plan-change Plan how to add a JSON output option to this CLI.
  • Check the setup: run seamark doctor to see the installed skills, Seamark registration, and tool permissions.
  • Preview first: add --print to the setup command to see what would change without writing any files.
  • Undo the tool setup: remove only the tables Seamark added under the # seamark: comment in .codex/config.toml. The installed skills stay.

Keep the skills up to date

Run seamark doctor or seamark status to check the installed copies. After upgrading Seamark, repeat your installation command to update them.

Seamark updates only copies it manages and leaves unrelated skills alone. To customize a bundled skill, copy it under a different name; edits to a managed copy can be overwritten during an update.

What skills do—and do not do

Skills guide the agent's workflow. They do not enforce rules or replace tests. Hooks run checks and deliver reminders on matching tool calls. Policy defines which actions should be blocked or require approval; warn mode reports violations without blocking.

The skills also explain the limits of Seamark's evidence: files that often change together are not necessarily dependencies, lessons are advisory, and files missing from the index have not been assessed. Repository content and review comments returned by tools must be treated as evidence, not instructions.

Skills are optional; the MCP tools work without them. The paired benchmark that compares the MCP server alone with the MCP server plus skills (make skills-bench, see bench/README.md) ran two cohorts on 2026-09-05: the first found no effect and drove the revisions, the second passed the frozen claim, at a higher context and cost per task. The measured rows and figures are in bench/skills-report-v2.md.

See the skills guide for individual installation options and implementation details.

Maintainersseamark report creates one self-contained HTML page with the decision queue, near-duplicate pins, hotspot map, and full lesson ledger. Use --open to open it or -o - to write it to standard output. See docs/lessons.md.

Each surface handles freshness according to the risk of stale data. check self-repairs before evaluation because stale line spans can produce an incorrect safety result. The LSP re-indexes on save, and MCP checks on every call. why responds immediately and adds a staleness note. seamark index finishes in well under one second when nothing changed. A content-hashed, per-file parse cache keeps re-indexing at about 1.3 seconds on an 831-file monorepo, compared with about 3.2 seconds for a full parse. Resolution and propagation still run globally, so the edges remain exact.

How much can the answers be trusted?

seamark status          # or --json; also served as MCP resource seamark://status
workspace      current (schema v3)
parsed         703 of 832 seen files (98 skipped by config)
symbols        1221, 2931 edges — call resolution 71% qualified · 24% same-package · 5% unique-name (1796 calls)
effects        83 direct-sink symbols, 692 by propagation
history        3814 decisions; evidence median age 74d (oldest 1042d)
reviews        3 lessons from 120 review findings; last mined 9d ago
distillation   claude -p — external data processing when run (see `lessons --distill --dry-run`)
gate           hook installed; policy mode warn governs
skills         claude 3/3 current · codex not installed

Every safety-sensitive answer needs this context: "no effects found" from a half-parsed index is not "no effects." The same honesty runs through the other surfaces — orient warns when files failed to parse, and seamark check attaches a note when changed files have no indexed symbols, so absence of evidence never silently reads as evidence of safety.

Installation health is the other half:

seamark doctor          # read-only, offline; exit 1 when a check fails

doctor verifies everything seamark needs to run — git, the index database (schema version and SQLite integrity), policy and effect-catalogue compilation, Claude Code hook wiring, the distillation agent, gh, MCP registration, the agent skills, and that the policy-as-code overlays are not accidentally gitignored — and prints an exact corrective action for anything broken, changing nothing itself.

Durable state: the index is not a throwaway cache

.seamark/index.db carries two kinds of state with different lifecycles. The derived graph — symbols, edges, co-change, decisions, effects — is rebuilt from the workspace on every reindex. But the same file also holds durable decisions: proposal history with your applied/dismissed verdicts, and the distillation memory that keeps paid agent calls from ever being repeated. Rebuilds (including --force) preserve those tables; deleting the file destroys them. The schema is versioned with ordered migrations — an older seamark refuses a newer database instead of guessing at it.

To back up or move the durable part:

seamark state export --out decisions.json   # proposals + distillation memory
seamark state import decisions.json         # merge into this clone (works pre-index)

Import never overwrites a local decision: it adds missing rows, and a still-pending proposal may adopt an imported verdict — a decision beats no decision.

Configuration

What gets indexed — the indexer skips anything .gitignore ignores and files carrying the conventional Code generated … DO NOT EDIT. header (generated code inflates the graph and pollutes most-called lists without ever being hand-navigated). A committed .seamark/config.yaml tunes both:

index:
  generated: true # index generated files after all
  exclude: # extra paths to skip, on top of .gitignore
    - "*.pb.go" # basename glob, any directory
    - "internal/gen/" # directory prefix
    # - "**/*_test.go" # uncomment for a production-only graph

Skipped files are counted in the seamark index output, and a malformed config — including an exclude glob that could never match — fails the index loudly rather than being silently ignored. Config edits count as workspace changes, so the next index picks them up automatically.

The same config.yaml carries the other opt-ins, each defaulting to the safe side:

reviews:
  window_days: 730 # review-comment shelf life; 0 = unlimited.
  #                  The newest 200 comments always survive the window.
distill:
  write: false # --apply/--prune/--retarget print the block for you to
  #              paste; flip to true to let them edit lessons.yaml
agent:
  cli: claude # the agent CLI --distill pipes findings through (the default)
  # argv: ["my-llm", "--stdin"]   # …or a custom command line

History mining has two flags on seamark index itself: --max-commits bounds the git window (default 5000) and --max-files-per-commit excludes bulk refactors from co-change (default 30). Every command takes -C <dir> to name the workspace and --db <path> to override the index location; status, doctor, gate, and check speak --json for machines.

Effect catalogue — effect knowledge is data, not code. The built-in catalogue covers ~40 sinks across Go, Python, and JS/TS plus common CLI tools; your workspace extends it additively in .seamark/effects.yaml:

sinks:
  - language: python
    import: my_company_infra
    names: [apply, provision]
    tag: infra:mutate
commands:
  - name: my-deploy-tool
    subcommands: [rollout]
    tag: infra:mutate

Custom tags propagate up the call graph and participate in policy exactly like the built-ins.

How it works

  sources                     seamark                      surfaces
┌──────────┐      ┌───────────────────────────┐      ┌──────────────┐
│ source   │─parse─▶ symbols / calls / imports │────▶ │ LSP  (stdio) │
│ tree     │      │                           │      ├──────────────┤
├──────────┤      │  co-change (lift) +       │────▶ │ CLI / hooks  │
│ git log  │─mine──▶ decisions per region      │      ├──────────────┤
├──────────┤      │                           │────▶ │ MCP  (stdio) │
│ .seamark/ │─load──▶ effect propagation to     │      └──────────────┘
│ yaml     │      │  fixpoint + CEL policy    │
└──────────┘      └────────── SQLite ─────────┘

One binary, one SQLite file, no daemon required. Parsing is tree-sitter; call edges are resolved syntactically and labeled with their derivation so consumers can filter by confidence. Effects seed from the catalogue at call sites (including calls into external dependencies) and propagate backwards along call edges to fixpoint, with depth. What touches the network, what reaches a model, and what persists is one table in docs/data-flow.md.

Honest limits

  • Call resolution is syntactic, not type-checked. gopls will always be better at find references within one language — that is not the game. Low-confidence edges are labeled [unique-name] and test doubles are excluded from that tier.
  • A local variable shadowing an import alias can produce a wrong edge (declared as such via its origin label). Scope tracking is future work.
  • Python's DB-API has no syntactic read/write split, so cursor.execute tags conservatively as db:write.
  • Co-change needs history: on a young repo the empirical layer is thin until commits accumulate.
  • Seamark is a navigator, not an oracle. It tells you where to look and what usually travels together; "usually changes with" is empirical evidence, never a guarantee that your edit is safe or complete. For a pinpoint lookup of a known symbol, a plain file read is cheaper — seamark earns its round-trip on orientation, risk, and history questions.

Status & roadmap

The current production status per capability profile — Navigate (stable), Learn (functional, integration-dependent), Guard (warn ready, enforcement beta) — lives in docs/STATUS.md, kept separate from the design history in docs/PLAN.md. Trust boundaries: docs/data-flow.md and docs/threat-model.md.

Planned next (see docs/PLAN.md): zero-token check promotion from recurring lessons, function-grain precision refinements, public signal evaluation, a history watermark and incremental daemon for keystroke-adjacent freshness, and signed artifacts with npm packaging (Homebrew shipped via seamark-dev/homebrew-tap).

Development

make test     # full suite (testify)
make lint     # golangci-lint
make index    # self-index this repo
make smoke    # end-to-end run of the built binary in a fixture repo
make skills-validate   # Claude Code's strict validator over skills/ (local; needs the claude CLI)

Contributions that need no Go at all: the effect catalogue and default policy are plain YAML — adding sinks for your framework is a ten-line PR.

License

Apache-2.0. See LICENSE.

Documentation

Overview

Package seamark is the module root. It holds one thing: the agent skills embedded from the skills/ tree. The tree stays at the repository root because `npx skills add seamark-dev/seamark --skill <name>` looks for SKILL.md there. A go:embed directive cannot reach a parent directory, so the embedding package sits beside the tree.

Index

Constants

This section is empty.

Variables

View Source
var SkillsFS embed.FS

SkillsFS is the skills/ tree, byte for byte, as built into the binary. The embed directive skips names that start with "." or "_", so the tree must carry none; a test under internal/skills compares the two. internal/skills is the only reader: init, doctor, and status go through it.

Functions

This section is empty.

Types

This section is empty.

Directories

Path Synopsis
cmd
lessons-bench command
Command lessons-bench runs the active experiment: does the seamark lessons hook change what a headless agent writes? Two arms differing in exactly one bit (the hook wired or absent), a fresh synthetic or pinned-public worktree per trial, a frozen task, and a deterministic judge.
Command lessons-bench runs the active experiment: does the seamark lessons hook change what a headless agent writes? Two arms differing in exactly one bit (the hook wired or absent), a fresh synthetic or pinned-public worktree per trial, a frozen task, and a deterministic judge.
lessons-bench-prepare command
Command lessons-bench-prepare materializes immutable public-repository source and offline dependencies before a lessons benchmark run.
Command lessons-bench-prepare materializes immutable public-repository source and offline dependencies before a lessons benchmark run.
lessons-bench-report command
Command lessons-bench-report renders explicit benchmark JSONL inputs into a deterministic Markdown report evaluated against frozen claims.
Command lessons-bench-report renders explicit benchmark JSONL inputs into a deterministic Markdown report evaluated against frozen claims.
seamark command
Command seamark is the CLI entry point.
Command seamark is the CLI entry point.
skills-bench command
Command skills-bench runs the skills workflow experiment: does installing the seamark agent skills change what a headless agent does on a companion-file task, compared with the seamark MCP server alone? Two arms that differ in exactly one thing (the skills installed and callable, or not), a fresh fixture whose history carries the companion pair, a frozen task, and the lessons benchmark's deterministic judges.
Command skills-bench runs the skills workflow experiment: does installing the seamark agent skills change what a headless agent does on a companion-file task, compared with the seamark MCP server alone? Two arms that differ in exactly one thing (the skills installed and callable, or not), a fresh fixture whose history carries the companion pair, a frozen task, and the lessons benchmark's deterministic judges.
skills-bench-report command
Command skills-bench-report renders workflow result files, and optionally activation result files, into a deterministic Markdown report evaluated against the frozen workflow claims and activation criteria.
Command skills-bench-report renders workflow result files, and optionally activation result files, into a deterministic Markdown report evaluated against the frozen workflow claims and activation criteria.
internal
agent
Package agent invokes the user's own coding-agent CLI for one-shot prompted tasks — the inference seam behind features like lesson distillation.
Package agent invokes the user's own coding-agent CLI for one-shot prompted tasks — the inference seam behind features like lesson distillation.
approve
Package approve manages the configuration that lets a coding agent call the seamark MCP tools without a permission prompt: Claude Code allow rules in .claude/settings.json and Codex per-tool approvals in .codex/config.toml.
Package approve manages the configuration that lets a coding agent call the seamark MCP tools without a permission prompt: Claude Code allow rules in .claude/settings.json and Codex per-tool approvals in .codex/config.toml.
cli
Package cli wires the seamark commands.
Package cli wires the seamark commands.
confidence
Package confidence computes a deterministic evidence-health tier for distilled proposals (RFC-002 §6) — a leaf package, because both the distiller's surfaces and the report layer must reach one answer and neither may import the other.
Package confidence computes a deterministic evidence-health tier for distilled proposals (RFC-002 §6) — a leaf package, because both the distiller's surfaces and the report layer must reach one answer and neither may import the other.
distill
Package distill turns the raw review findings behind lessons into candidate groups for LLM distillation (RFC-001 §5.4 Tier 2).
Package distill turns the raw review findings behind lessons into candidate groups for LLM distillation (RFC-001 §5.4 Tier 2).
doctor
Package doctor diagnoses the seamark installation: binary, git, the index database's schema and integrity, policy and effect-catalogue compilation, hook wiring, agent and gh availability, agent skills, tool approvals, and gitignore sanity.
Package doctor diagnoses the seamark installation: binary, git, the index database's schema and integrity, policy and effect-catalogue compilation, hook wiring, agent and gh availability, agent skills, tool approvals, and gitignore sanity.
effects
Package effects holds the sink catalogue: which calls can ultimately write a database, spawn a process, touch the filesystem, or leave the network (RFC-001 §5.2).
Package effects holds the sink catalogue: which calls can ultimately write a database, spawn a process, touch the filesystem, or leave the network (RFC-001 §5.2).
fixes
Package fixes mines fix commits into findings — the lesson source that exists in every repository, review culture or none.
Package fixes mines fix commits into findings — the lesson source that exists in every repository, review culture or none.
gate
Package gate is the pre-execution and pre-edit policy engine (RFC-001 §5.5): commands are parsed with a real shell parser (never regex), classified against the effect catalogue, and evaluated against CEL rules over the declared environment.
Package gate is the pre-execution and pre-edit policy engine (RFC-001 §5.5): commands are parsed with a real shell parser (never regex), classified against the effect catalogue, and evaluated against CEL rules over the declared environment.
history
Package history mines the git log into the history layer of the graph: commits become Decision rows, and file co-occurrence across commits becomes CoChange pairs with lift.
Package history mines the git log into the history layer of the graph: commits become Decision rows, and file co-occurrence across commits becomes CoChange pairs with lift.
hooks
Package hooks knows how seamark's Claude Code hooks are spelled in .claude/settings.json: the marker strings, the ownership rule, and gate-mode detection live here once — init writes hooks, status reads them, and two copies of the matching logic would drift.
Package hooks knows how seamark's Claude Code hooks are spelled in .claude/settings.json: the marker strings, the ownership rule, and gate-mode detection live here once — init writes hooks, status reads them, and two copies of the matching logic would drift.
htmlreport
Package htmlreport renders the index as one self-contained HTML page.
Package htmlreport renders the index as one self-contained HTML page.
index
Package index orchestrates a full indexing pass: walk the workspace, parse supported files, resolve call references into edges, mine git history, and write everything into the store atomically.
Package index orchestrates a full indexing pass: walk the workspace, parse supported files, resolve call references into edges, mine git history, and write everything into the store atomically.
lsp
Package lsp serves the seamark graph to editors over the Language Server Protocol.
Package lsp serves the seamark graph to editors over the Language Server Protocol.
mcp
Package mcp serves the seamark index over the Model Context Protocol's stdio transport: newline-delimited JSON-RPC 2.0, the same hand-rolled approach the LSP server took (RFC-001 §6.2).
Package mcp serves the seamark index over the Model Context Protocol's stdio transport: newline-delimited JSON-RPC 2.0, the same hand-rolled approach the LSP server took (RFC-001 §6.2).
model
Package model holds the shared data types of the seamark graph.
Package model holds the shared data types of the seamark graph.
outcome
Package outcome implements the passive loop: for each applied pin that has fired at least once, it checks whether the pin's mistake recurred after agents started seeing it.
Package outcome implements the passive loop: for each applied pin that has fired at least once, it checks whether the pin's mistake recurred after agents started seeing it.
parse
Package parse turns source files into language-neutral symbol and reference facts.
Package parse turns source files into language-neutral symbol and reference facts.
redact
Package redact scrubs secret-shaped values from text seamark stores or displays: audited command lines (the gate's opt-in raw log), mined review-comment bodies, and fix-commit patches.
Package redact scrubs secret-shaped values from text seamark stores or displays: audited command lines (the gate's opt-in raw log), mined review-comment bodies, and fix-commit patches.
render
Package render holds small text-shaping helpers shared by the CLI and LSP surfaces: everything user-visible passes through here so untrusted input (git metadata, source-derived signatures) is handled once.
Package render holds small text-shaping helpers shared by the CLI and LSP surfaces: everything user-visible passes through here so untrusted input (git metadata, source-derived signatures) is handled once.
report
Package report renders seamark's text reports — the shared surface behind the CLI, the MCP server, and anything else that answers questions from the index.
Package report renders seamark's text reports — the shared surface behind the CLI, the MCP server, and anything else that answers questions from the index.
reviews
Package reviews mines pull-request review comments into lessons: the recurring feedback left on the same kind of change.
Package reviews mines pull-request review comments into lessons: the recurring feedback left on the same kind of change.
skills
Package skills is the catalogue of agent skills that ship inside the seamark binary.
Package skills is the catalogue of agent skills that ship inside the seamark binary.
status
Package status gathers and renders the index's semantic health: how much of the workspace is actually covered, how confident the edges are, how old the history evidence is, and which integrations are live.
Package status gathers and renders the index's semantic health: how much of the workspace is actually covered, how confident the edges are, how old the history evidence is, and which integrations are live.
store
Package store owns the on-disk index: a single SQLite file holding the structure, history, effects, and policy tables (RFC-001 §5.1).
Package store owns the on-disk index: a single SQLite file holding the structure, history, effects, and policy tables (RFC-001 §5.1).
wording
Package wording decides whether two short rule/note pairs say the same thing.
Package wording decides whether two short rule/note pairs say the same thing.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL