Syngraphe keeps repository context versioned, current, and understandable by both humans and coding agents.
It's a context layer behind AGENTS.md — Git-native Markdown for architecture, current state and decisions, with deterministic integrity checks.
All made quick and easy with a small, Git-native command-line tool that creates and validates a .context/ directory of
Markdown documents describing what a repository is, how it is built, and what is happening in it
right now.
Documentation: syngraphe.dev — guides, CLI reference, finding codes and
the reasoning behind the design. The site's source is in docs/.
Project knowledge usually lives everywhere except where the code is: in chat threads, in tickets, in one person's head, in a vendor's cloud memory. It drifts, it is invisible in review, and it disappears when the tool that stored it does.
Syngraphe takes the opposite position:
- The context belongs to the repository, not to Syngraphe. It is committed, reviewed and branched with the code it describes.
- Git provides history and versioning. There is no separate database and no separate timeline.
- Markdown is the format. Anyone can read and edit the context with a text editor.
- Humans and coding agents read the same documents.
AGENTS.mdpoints agents at.context/; people open the same files. - The repository stays usable without Syngraphe. If the executable disappears,
.context/,AGENTS.md, Markdown and Git history remain complete and meaningful. Syngraphe implements the repository-context protocol; the protocol does not depend on Syngraphe.
The deterministic core works offline and without AI. Optional AI support may come later; it will never be required.
Requires Node.js 22.18 or newer and Git on the PATH.
npm install -g syngrapheOr run it without installing:
npx syngraphe statusThe CLI is installed under two interchangeable names: syngraphe and the shorthand syg. The
documentation uses the full name.
syngraphe init # create the repository context and agent bootstrap
syngraphe init --dry-run # show exactly what would change; no repository or Git changes
syngraphe init --dry-run --json # same plan as stable, content-free JSON
syngraphe init --policy # also seed AGENT-POLICY.md, leaving an existing one alone
syngraphe policy add # seed AGENT-POLICY.md later; refuses to replace an existing one
syngraphe policy add --force # replace it with the current template
syngraphe status # summarize the repository context
syngraphe check # run the deterministic integrity checks
syngraphe check --json # machine-readable findings, for CI
syngraphe check --strict # treat warnings as failures
syngraphe stats # bytes, estimated tokens, large files and duplicates
syngraphe stats --json --budget 8000
syngraphe truth new domain-model --title "Domain model"
syngraphe truth list
syngraphe decision new use_postgres --dry-run --json
syngraphe decision new use_postgres --title "Use PostgreSQL"
syngraphe decision list
syngraphe state new migration
syngraphe history new migration_outcome
syngraphe state archive phase_one --dry-run # preserve current state, then reset its template
syngraphe init --scope packages/api # independent context in an existing directory
syngraphe check --scope packages/api
syngraphe check --all # check discovered monorepo contexts
syngraphe stats --allAll four document categories — truth, decision, state, and history — support new <name>
(with --title and --dry-run) and list. They require an initialized context and never overwrite
an existing document. A new truth document is intentionally only a top-level heading; Syngraphe
does not invent a semantic structure for repository facts. state archive <name> copies current
state into history before resetting it; omitting --dry-run applies the plan.
Every mutating command accepts --json only together with --dry-run. This emits the same plan as
stable JSON and performs no repository mutations. The public projection includes operation paths
and summaries, but no created contents or patch before/after text.
stats estimates Markdown tokens as ceil(UTF-8 bytes / 4) per file. It separates history from
active content, flags documents over 2,000 estimated tokens and exact duplicates, and compares the
total against an advisory budget (8,000 by default). It does not measure an actual agent prompt.
--scope is relative to the Git root, independent of the working directory; without it commands
still select the root. status, check and stats accept --all for tracked and unignored nested
contexts. Each scope keeps its own .context/, AGENTS.md and CLAUDE.md; shared knowledge stays
in ancestor contexts. See the monorepo guide.
.context/
├── manifest.json # schema version and layout
├── index.md # router into the context
├── truth/
│ ├── architecture.md # current system architecture
│ └── conventions.md # repository conventions
├── state/
│ └── current.md # current focus, recent changes, next steps, blockers
├── decisions/
│ └── README.md # significant technical decisions, one file each
└── history/
└── README.md # completed or superseded operational context
It also adds a managed block to AGENTS.md, creating the file if needed, and a CLAUDE.md that
imports AGENTS.md — created if absent, or extended with the import block only if it already
exists. A CLAUDE.md that already imports AGENTS.md, or that is a symlink to it, is left alone.
With --policy it also seeds AGENT-POLICY.md at the repository root: a starting point for how an
agent should work here — planning, delegation, consequential actions, long-running processes and
verification. It is a seed and not a managed file: Syngraphe writes it once and never compares,
patches or checks it again. An existing one is left untouched; syngraphe policy add --force is the
only way to replace it. The AGENTS.md block references it with one conditional line, written
whether or not the file exists.
The templates are deliberately small. They are starting points for humans to fill in, not questionnaires.
| Directory | Lifecycle | Contents |
|---|---|---|
truth/ |
stable | architecture, conventions, domain concepts, constraints, invariants |
state/ |
volatile | current focus, recent relevant changes, next steps, blockers |
decisions/ |
append | significant technical and architectural decisions, with rationale |
history/ |
archive | completed or superseded operational context, kept out of the way |
Syngraphe edits files people also edit by hand, so it is conservative by construction:
-
It owns only the text between its own markers:
<!-- syngraphe:start version="1" --> <!-- Managed by Syngraphe. Do not edit this block manually. --> ... <!-- syngraphe:end -->
-
Everything outside those markers is user-owned and is preserved byte for byte, including line endings and a missing final newline. A file that is not valid UTF-8 is reported as a conflict instead of patched, because its bytes could not be kept exactly.
-
A managed block edited by hand is reported as drift, never silently overwritten.
-
Duplicate or malformed blocks are reported, never guessed at.
-
Initialization is idempotent: running it twice changes nothing the second time.
-
Every modifying command builds a plan, renders it, and applies exactly that plan.
--dry-runruns the same planner as the real command and performs no repository mutations: it does not modify repository contents or Git state. -
Writes are complete-file writes: the whole file is staged in a temporary file beside its destination, so an interrupted run cannot leave a half-written file. A new document is published with an operation that fails if the destination already exists, so two Syngraphe runs creating the same file cannot overwrite each other. A patched file is first moved aside and compared with the content the plan expects, so an edit made after the plan was checked is kept and the run fails. On filesystems without hard links, publication stays exclusive, but a crash during it can leave the new file partial.
-
Syngraphe rejects escaping paths and symlinks, and rechecks parent-directory identities during staging, publication and cleanup. A detected directory swap stops the operation. These checks are not a sandbox against a hostile process changing directories during individual filesystem calls; see the safety model.
-
An existing
.context/that is not a Syngraphe context is never touched: the command aborts and explains the conflict.
syngraphe check runs deterministic, offline checks. Codes are stable.
| Code | Severity | Meaning |
|---|---|---|
CTX001 |
error | repository context is not initialized |
CTX002 |
error | an expected context file is missing |
CTX003 |
error | .context/ exists but is not a Syngraphe context |
MANIFEST001 |
error | .context/manifest.json is missing |
MANIFEST002 |
error | manifest is not valid JSON, or has no schemaVersion |
MANIFEST003 |
error | manifest declares an unsupported schema version |
MANIFEST004 |
warning | manifest declares an unknown layout |
MANIFEST005 |
warning | manifest declares no protocol |
AGENT001 |
error | AGENTS.md has no Syngraphe block |
AGENT002 |
error | the AGENTS.md block was modified manually |
AGENT003 |
error | AGENTS.md contains duplicate Syngraphe blocks |
AGENT004 |
error | AGENTS.md markers are unbalanced |
AGENT005 |
error | AGENTS.md cannot be managed safely |
AGENT006 |
warning | the AGENTS.md block is from an earlier Syngraphe version |
CLAUDE001 |
warning | Claude is used but CLAUDE.md has no import of AGENTS.md |
CLAUDE002 – CLAUDE004 |
error | the CLAUDE.md block drifted, is duplicated, or is malformed |
CLAUDE005 |
warning | CLAUDE.md is a setup Syngraphe deliberately leaves alone |
CLAUDE006 |
warning | the CLAUDE.md block is from an earlier Syngraphe version |
LINK001 |
error | a context document references a path that does not exist |
STATE001 |
warning | state/current.md has not changed while the repository did |
STATE002 |
warning | state/current.md contains only headings |
Freshness is treated as a signal, not a verdict: STATE001 is a warning, it is only raised when the
repository has commits newer than the state document, and it says the age rather than declaring the
context stale.
| Code | Meaning |
|---|---|
0 |
success |
1 |
context integrity failure |
2 |
invalid CLI usage |
3 |
unsupported Syngraphe context schema |
4 |
internal Syngraphe failure |
By default errors fail a command and warnings are reported only. With --strict, warnings fail it
too.
Checks, statistics, monorepo reports, and mutating dry-run plans have independently versioned JSON
contracts. For example, syngraphe decision new use-postgres --dry-run --json emits a plan whose
operations contain paths and types without exposing file contents. See the
JSON reference for each payload.
{
"version": 1,
"ok": false,
"findings": [
{
"code": "LINK001",
"severity": "error",
"category": "references",
"file": ".context/index.md",
"line": 12,
"message": "Referenced path does not exist: truth/gone.md"
}
]
}version describes the payload shape and changes only when the shape does.
The official Action runs checks, statistics, or both, with monorepo scopes, file/line annotations, job summaries and a JSON report. It bundles Syngraphe and needs no package install or API token.
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: suffro/syngraphe@action-v1
with:
command: check-and-stats
all: 'true'
strict: 'true'Action releases use separate tags: action-v1.0.0 for an exact release and action-v1 for
compatible updates. Pin a reviewed full commit SHA when reproducibility is required.
See the Action reference for inputs,
outputs, report-only mode and optional budget enforcement.
AGENTS.md is the canonical bootstrap file. Agents that read it need nothing else:
| Agent | Integration |
|---|---|
| Claude | CLAUDE.md importing AGENTS.md (a thin shim) |
| Cursor | native — reads AGENTS.md |
| Codex | native — reads AGENTS.md |
Vendor-specific configuration such as .cursor/rules/ is detected for reporting and otherwise left
untouched.
Implemented:
syngraphe init, with human or JSON dry-run plans, and--policysyngraphe policy add, with--forcesyngraphe statussyngraphe check,--json,--strictsyngraphe stats,--json,--budgettruth,decision,state,history:new,list;state archive- explicit nested contexts with
--scope;--allfor read-only monorepo reports - the
.context/schema v1 and its templates - managed blocks in
AGENTS.mdandCLAUDE.md - the deterministic check registry
- the bundled GitHub Action with annotations, summaries and JSON reports
Syngraphe manages shared repository context and its integrity. It is not a rule transpiler and does not synchronize hooks, permissions, MCP configuration, subagents, skills, or vendor-specific commands. Vendor features stay vendor-specific.
Not implemented yet, by design: semantic AI analysis, doctor, update, reconcile, MCP servers,
vector databases, cloud services, direct AI provider integrations, Git hooks and
background services.
Destructive removal is also not implemented. When it arrives it will be an explicit command — never
called uninstall — that shows exactly what it would remove, preserves user-authored content
outside managed blocks, treats .context/ as potentially valuable human-authored data, and requires
an explicit confirmation phrase.
npm install
npm run build # compile to dist/
npm test # node:test suites, including real temporary Git repositories
npm run typecheck # tsc --noEmit over src/ and test/
npm run lint # Biome
npm run action:build # regenerate the committed GitHub Action bundle
npm run action:check # fail if the bundle differs from its sourcesThe source layout follows the dependency direction cli → commands → core → filesystem/Git. Agent
integrations (src/agents/) and checks (src/checks/) are registries consumed by commands, so
adding either is a new module plus one registration plus tests.
Apache-2.0. See LICENSE.