CLI commands
syngraphe [options] [command]Or you can also use the shorthand syg:
syg [options] [command]| Option | Effect |
|---|---|
-v, --version | Print the version and exit |
-h, --help | Print help and exit |
--scope <path> | Select an existing directory relative to the Git root |
The CLI is installed under two interchangeable names: syngraphe and the shorthand syg. This reference uses the full name.
Every command must run inside a Git working tree. Without --scope, paths resolve against the Git root regardless of the working directory. With --scope, context and agent files resolve inside that directory. The scope argument itself is always Git-root-relative, using forward slashes. The option can appear before or after the command. Nothing here reaches the network.
status, check, and stats also accept --all; it cannot be combined with --scope.
Nested contexts and monorepos
Use --scope <path> to manage context for a specific package or directory within your repository. See Nested contexts and monorepos for examples and details.
syngraphe init
Creates the repository context and the agent bootstrap files.
syngraphe init [--policy] [--dry-run] [--json]| Option | Effect |
|---|---|
--policy | Also create AGENT-POLICY.md. An existing one is left untouched. |
--dry-run | Render the plan and exit, changing no repository contents or Git state. |
--json | Emit the plan as versioned JSON; requires --dry-run. |
What it does
- Inspects
.context/,AGENTS.md, and every registered agent integration. - Builds a single plan: files to create, files to patch, things left unchanged, conflicts found.
- Renders the plan as human text, or as the public JSON projection with
--dry-run --json. - Applies exactly that plan — unless
--dry-runwas passed, or the plan reported conflicts.
--dry-run is not a simulation: it runs the same planner as the real command and performs no repository mutations — it does not modify repository contents or Git state. It stops before the apply step. JSON changes only the rendering. It never includes created contents or patch before/after text.
Output
Syngraphe initialization plan
CREATE
.context/manifest.json
.context/index.md
.context/truth/architecture.md
.context/truth/conventions.md
.context/state/current.md
.context/decisions/README.md
.context/history/README.md
PATCH
AGENTS.md
+ Syngraphe repository-context bootstrap
CLAUDE.md
+ @AGENTS.md compatibility import
UNCHANGED
.cursor/rules/ (vendor configuration, left untouched)
No files were modified.| Section | Meaning |
|---|---|
CREATE | Files that do not exist and will be created. |
PATCH | Existing files that gain a managed block. Nothing else in them changes. |
UNCHANGED | What was found and deliberately left alone, with the reason. |
CONFLICTS | What blocks the run. When present, nothing at all is written. |
A real run ends with N files written. instead of No files were modified.; a run with nothing to do ends with Nothing to do. The repository context is already initialized.
For machine-readable dry runs, use syngraphe init --dry-run --json. Standard output then contains only the JSON payload and its final newline. See plan JSON.
Behaviour worth knowing
- Idempotent. A second run on an initialized repository writes nothing.
- Additive. Only the text between Syngraphe's markers is ever written into an existing file.
- All-or-nothing. If the plan contains conflicts, no operation is applied.
- Precondition-checked. Every operation is verified against the current state before the first write, so a stale plan fails instead of half-applying.
- Byte-exact. An existing
AGENTS.mdorCLAUDE.mdthat is not valid UTF-8 is a conflict rather than a patch; a UTF-8 byte order mark is kept. - Self-updating blocks. A managed block still holding a body an earlier Syngraphe version wrote is updated in place. Only text Syngraphe itself published is replaced; a hand-edited block is drift, and drift is still reported instead of overwritten. See managed blocks.
Exit codes
| Code | When |
|---|---|
0 | The plan was applied, or --dry-run completed, or there was nothing to do. |
1 | Conflicts were reported, or .context/ is unrelated or has an invalid manifest. |
2 | Not inside a Git repository, or invalid usage such as --json without --dry-run. |
3 | .context/manifest.json declares an unsupported schema version. |
syngraphe policy add
Writes AGENT-POLICY.md at the repository root: a starting point for how an agent should work in this repository — planning, delegation, consequential actions, long-running processes and verification.
syngraphe policy add [--force] [--dry-run] [--json]| Option | Effect |
|---|---|
--force | Replace an existing policy file with the current template. |
--dry-run | Render the plan and exit, changing no repository contents or Git state. |
--json | Emit the plan as versioned JSON; requires --dry-run. |
The file is a seed, not a managed file. Syngraphe writes it once and never compares, patches or checks it again: edit it freely, and expect every repository's copy to diverge. AGENTS.md points at it with one conditional line inside the managed block, so the reference costs nothing in a repository that has no policy file.
Why it is separate from AGENTS.md
AGENTS.md is loaded by an agent in every session. It holds durable repository facts — commands, layout, conventions, invariants — and every line there is paid for on every task. Process rules mainly matter for multi-step, costly or risky work, so they live in a file an agent opens when the work warrants it.
Why it is at the repository root and not in .context/
.context/ holds facts about this repository. A policy is an instruction to the agent, which is what AGENTS.md and CLAUDE.md are — and those already live at the root. Keeping it out of .context/ also keeps it out of the context size and token estimates syngraphe stats reports.
One policy governs the whole repository, so the command refuses --scope.
Behaviour worth knowing
- Never silently destructive. An existing
AGENT-POLICY.mdis a conflict that stops the run and exits1. Replacing it needs--forceon the command line; there is no prompt, because the CLI has no interactive mode and a plan that depended on a terminal could not be dry-run or scripted. - Idempotent through
init.syngraphe init --policycreates the file when it is absent and reports it as unchanged when something is already there — whatever it contains, and whatever its letter case. The policy is an optional extra, so it never stops initialization. - Case-safe. A file differing only by case, such as
agent-policy.md, is a conflict on every platform: on a case-insensitive filesystem the two spellings are one file, and on a case-sensitive one a second spelling would be invisible to everybody else. - Byte-exact. With
--force, the existing file is replaced only if its bytes still match the ones the plan was built from, and a file that is not valid UTF-8 is a conflict rather than a replacement.
Exit codes
| Code | When |
|---|---|
0 | The policy was written, was already current, or --dry-run completed. |
1 | A conflict was reported: the file exists without --force, collides by case, or cannot be read. |
2 | Not inside a Git repository, --scope was used, or --json without --dry-run. |
syngraphe status
Summarizes the repository context. Read-only, offline, fast.
syngraphe statusSyngraphe
Context
schema v1
layout standard
Knowledge
architecture present
conventions present
current state present
decisions 3
history 2
Agents
AGENTS.md ready
Claude ready
Cursor native
Codex native
Integrity
0 errors
1 warning| Section | Contents |
|---|---|
Context | Declared schema version and layout — or status not initialized when there is no context. |
Knowledge | Whether each core document exists; the number of regular .md files in decisions/ and history/, excluding README.md in any letter case, symlinks and directories. |
Agents | The state of AGENTS.md and of every registered integration. See agent integrations. |
Integrity | Error and warning counts from the same checks syngraphe check runs. |
status always exits 0: it reports, it does not judge. Use check for a pass/fail answer.
syngraphe check
Runs the deterministic check registry.
syngraphe check [--json] [--strict]| Option | Effect |
|---|---|
--json | Emit the findings as a versioned JSON payload. |
--strict | Warnings fail the command as well as errors. |
Human output
Syngraphe context integrity
✓ manifest
✓ context structure
✓ internal references
✓ AGENTS.md
✓ Claude integration
! context state
WARN STATE002 .context/state/current.md
Current state contains only headings.
Describe the current focus so the file is useful to humans and agents.
1 warningOne line per check, then the findings, then a summary.
| Symbol | Meaning |
|---|---|
✓ | The check found nothing. |
! | The check produced warnings only. |
✗ | The check produced at least one error. |
Each finding is printed as SEVERITY CODE file:line, the message, and any details. Every code is documented in checks and findings.
JSON output
See JSON output for the payload shape and its stability guarantees.
Exit codes
| Code | When |
|---|---|
0 | No errors — and no warnings under --strict. |
1 | At least one error, or a warning under --strict. |
2 | Not inside a Git repository, or invalid usage. |
3 | A MANIFEST003 finding: the context schema is unsupported. |
Exit code 3 takes precedence over 1: an unsupported schema means the answer to every other question is unreliable, and the fix is to upgrade the tool rather than change the repository.
syngraphe stats
Measures context size and reports advisory bloat signals. Requires an initialized, supported context.
syngraphe stats [--json] [--budget <tokens>] [--scope <path> | --all]- Bytes and file counts include every regular file under the selected
.context/recursively. - Words are whitespace-separated runs in
.mdfiles. Estimated tokens areceil(UTF-8 bytes / 4)per Markdown file, summed for totals. This is a rough sizing heuristic, not a model tokenizer or the actual context loaded by an agent. - Active means everything outside
history/; it includes decisions, optional documents and metadata. History is reported separately, and total is their sum. Agent bootstrap files are not counted. - The default total Markdown budget is 8,000 estimated tokens.
--budgetaccepts a positive safe integer. Exceeding it is advisory and still exits0. - The human report lists the ten largest Markdown files, all documents above 2,000 estimated tokens, and groups of nonempty Markdown files with identical contents. JSON includes every document, sorted by descending bytes and then path.
- Symlinks and other non-regular entries encountered during traversal are skipped and reported. Required context paths reached through symlinks are refused before inspection.
Exit codes: 0 for a report, 1 for an absent, incomplete or unusable context, 2 for invalid arguments or unsafe paths, 3 for an unsupported schema. No content is rewritten or deleted. See JSON output for the versioned report.
Document commands
syngraphe truth new <name> [--title <title>] [--dry-run] [--json]
syngraphe decision new <name> [--title <title>] [--dry-run] [--json]
syngraphe state new <name> [--title <title>] [--dry-run] [--json]
syngraphe history new <name> [--title <title>] [--dry-run] [--json]
syngraphe truth list
syngraphe decision list
syngraphe state list
syngraphe history list
syngraphe state archive <name> [--dry-run] [--json]All commands accept --scope <path> and require an initialized, supported context.
new creates a file in truth/, decisions/, state/ or history/. The name is a filename, with an optional .md suffix: letters, numbers, hyphens and underscores, starting with a letter or number, at most 120 characters before the suffix. Paths, README, and Windows device names are refused. Existing names, including case-only collisions, are conflicts; there is no overwrite option. Thus truth new architecture conflicts with the core truth/architecture.md rather than special-casing or replacing it.
The default heading uses the filename with hyphens and underscores replaced by spaces. --title sets a nonempty single-line heading. The generated body contains only headings:
| Category | Headings |
|---|---|
truth | Top-level title only |
decision | Context; Decision; Alternatives considered; Consequences |
state | Current focus; Recent relevant changes; Next; Blockers |
history | Summary; Outcome; Follow-up |
The truth template is deliberately only # <title> with a final newline: truth may describe architecture, domain concepts, constraints, APIs, or something else, so Syngraphe does not impose generic sections. No decision number, date or status is invented. The files stay ordinary Markdown. The index is left for the author to curate; new files do not automatically join its always-relevant reading list.
list prints top-level regular .md files in filename order, excluding README.md in any letter case, symlinks and directories — the same rule status counts with. truth list includes the core architecture.md and conventions.md; state list includes current.md. An empty list prints No documents found.
state archive copies the full UTF-8 content of state/current.md to history/<name>.md, preserving line endings and a missing final newline, then resets current state to its original empty template. The archive keeps the original title. Refill current state afterwards; until then check reports STATE002. Review any document-relative links in the archived content.
Every write uses the same plan/apply flow as init. A destination conflict, unsafe write path or stale source aborts before the first write. A new document is created exclusively: if another process takes the name first, the command fails with exit code 1 instead of overwriting it. Current state is reset the same way: if state/current.md changes after the plan was built, the edit is kept and the command fails with exit code 1. Each write is atomic (on filesystems without hard links, a crash can leave it partial), but the two-file archive is not a filesystem transaction: an IO failure after creation may leave the archive and original state both present, preserving the source. --dry-run renders this same plan without applying it.
Add --json to a dry run for the independently versioned, content-free plan projection:
syngraphe truth new domain-model --title "Domain model" --dry-run --json
syngraphe state archive completed --dry-run --json--json without --dry-run is invalid usage and writes nothing. List commands do not accept JSON.
Exit codes: 0 for success or dry-run, 1 for a conflict or unusable context, 2 for invalid names, titles, scopes or unsafe paths, 3 for an unsupported schema.
Programmatic use
The same core is exported from the package, so a script can run the checks without parsing terminal output:
import { Repository, createCheckContext, runChecks } from "syngraphe";
const repository = await Repository.open(process.cwd());
const { findings, errors, warnings } = await runChecks(await createCheckContext(repository));planInitialization(repository) returns the same plan init renders, if you want to inspect it before deciding anything.
planToJson(plan, scope) projects a plan into the same stable JSON contract as CLI dry runs; PLAN_JSON_VERSION versions that shape independently from check and statistics reports. The projection deliberately omits internal file contents and patch states.
(await Repository.open(cwd)).inScope("packages/api") selects a package. The returned root is the scope's absolute directory, gitRoot is the containing Git root, and scope is its Git-relative POSIX path (. at the root). Repository.open and Repository.atRoot retain their default behavior. discoverScopes(repository) returns the same scope list used by --all.
inspectStats(repository, budget?) returns structured statistics. planDocument(repository, "truth", "domain-model", { title: "Domain model" }) returns a creation plan; planDocument(repository, "history", "phase_one", { archive: true }) returns an archival plan. listDocuments(repository, "state") returns the sorted paths without terminal formatting.