spec 1.0 · reference
CLI reference
Reference CLI for the Leji specification: validate, index, changelog, freshness, conformance, status, route, viewer/view, detect, adopt, init, start, ci, and agent for a shared context layer.
Install it in whichever runtime your toolchain or CI uses; the command surface is identical across all three. Every command takes --root <dir>; the reporting commands also emit machine-readable --json. The same spec generates both this reference and leji --help. Working through an agent? The same validation and conformance are available as MCP tools.
Install
npm install -g @leji-org/lejiNew to Leji? The Quickstart walks through installing, then scaffolding or adopting your first context layer.
leji <command> [options]Commands
validate | Validate the context layer: manifest, artifacts, frontmatter, and lint rules. |
index | Generate the context index at the declared path, or verify it is current. |
changelog check | Verify the machine changelog: schema and append-only discipline. |
changelog compact | Fold the oldest changelog entries into a single compaction entry. |
freshness | Report review horizons across category documents and agent profiles. |
status | Report unindexed, dangling, and stale documents in the context layer. |
route | Show the governed context a task's scope routes to. |
conformance | Score the context layer against its claimed conformance level. |
mounts hydrate | Materialize declared federation mounts into the resolver cache. |
mounts status | Report each mount's availability, integrity, and pin ancestry. |
mounts locate | Print resolver state for one mount: projection path, pin, verification. |
viewer | Generate the static viewer for the context layer. |
viewer serve | Generate the viewer and serve it locally. |
viewer build | Export a self-contained static viewer folder for internal hosting. |
view | Alias for leji viewer serve (and opens the browser). |
start | Open a coding agent in this context layer, booted from the boot profile. |
detect | Detect the coding-agent hosts available on this machine. |
adopt | Adopt Leji into an existing repository. |
init | Bootstrap a new context layer from the templates. |
ci | Add a CI workflow that runs leji validate and index --check on every change. |
agent | Bind an additional named agent into an existing context layer. |
Exit codes
| Code | Meaning |
|---|---|
0 | Clean. No errors; warnings are allowed. |
1 | A check did not pass. Usually an error finding was reported; some commands also use it for a negative result with no finding, such as status --strict flagging an entry, mounts locate finding no hydrated projection, or start finding no boot profile. |
2 | Usage error, or an internal failure (e.g. init refusing to overwrite). |
Global options
--root <dir> | Repository root to operate on (default: the current directory). |
--json | Machine-readable JSON output instead of human-readable text. |
-v, --version | Print the Leji version and exit. |
-h, --help | Show help and exit. |
leji validate
Loads leji.json and checks it against the schemas and the lint rules: declared files exist, categories are populated, vendor entrypoints redirect to the boot profile, frontmatter is valid, and (per the claimed conformance level) the index is current and the changelog is append-only. One of the two gates the generated CI runs, beside leji index --check. With --content it also runs a warning-only content lint (placeholder text, generic boot identity, thin categories) that never errors and never affects a conformance level.
leji validate [--content] [--federation <available|required> [--paths <a,b,...>]] [--root <dir>] [--json]--content | Also run the warning-only content lint (placeholders, generic identity, thin categories). |
--federation <mode> | Opt-in federation enforcement: available fails on any unhydrated or unverified mount; required fails only for mounts the --paths task scope routes to. Run leji mounts hydrate first; ordinary validate never fails on availability. |
--paths <a,b,...> | Task scope for --federation=required: comma-separated repository paths; the routing algorithm decides which mounts the task touches. |
Examples
leji validate
leji validate --content
leji validate --root . --jsonleji index
Resolves the category index files to the documents they list and writes the context index to machine.indexPath. Ids are carried across a move when the move is unambiguous: a document whose path changes keeps its id if its content is unchanged and that content is unique in the context layer. A move that also edits the content, or that moves one of several byte-identical documents, cannot be carried and mints a fresh id. Declare a frontmatter id to make a document's id survive any move; that is the only unconditional guarantee. With --check it writes nothing and instead fails when the stored index no longer matches what the index files resolve to (a stale index is a hard failure).
leji index [--check] [--root <dir>] [--json]--check | Verify the stored index is current with the tree; write nothing. |
Examples
leji index
leji index --checkleji changelog check
Validates the declared changelog against its schema and checks append-only discipline against the committed state of the file at HEAD: surviving entries are immutable, and entries may be removed only from the oldest end and only alongside a compaction entry. The comparison is against HEAD, so it catches an uncommitted rewrite (which is what the pre-commit hook uses it for); in a CI checkout the working tree is HEAD, so it does not by itself detect a rewrite that arrives already committed. Reviewing the diff covers that. Without git the discipline is unverifiable and reported as a warning.
leji changelog check [--strict] [--root <dir>] [--json]--strict | Treat an unverifiable append-only check (no git baseline) as an error. |
Examples
leji changelog check
leji changelog check --strictleji changelog compact
Compacts the oldest end of the machine changelog, folding entries into a single compaction entry that records how many were folded and the id range removed.
- Selection:
--keep <n>folds every entry except the newest n;--before <date>folds entries dated before the given day. With both, an entry folds only if it satisfies both (the intersection). - At least one of
--keepor--beforeis required. - The folded set is always a contiguous run from the oldest end, so the result still satisfies the append-only discipline that
leji changelog checkenforces.
leji changelog compact [--keep <n>] [--before <YYYY-MM-DD>] [--root <dir>] [--json]--keep <n> | Keep the newest n entries; fold everything older. Must be a positive integer. |
--before <date> | Fold entries dated strictly before this YYYY-MM-DD day. |
Examples
leji changelog compact --keep 50
leji changelog compact --before 2026-01-01
leji changelog compact --keep 50 --before 2026-01-01leji freshness
Lists documents whose freshness.reviewAfter horizon has passed (expired) or falls within the next 30 days (upcoming). Report-only by default; expired horizons are warnings.
leji freshness [--strict] [--root <dir>] [--json]--strict | Treat expired horizons as errors instead of warnings. |
Examples
leji freshness
leji freshness --strict --jsonleji status
Informational health report: markdown under the context root that no category index lists (reference content), index entries whose listed path does not resolve (dangling), and stored-index paths the index files no longer resolve to (stale). It also reports shadowed entries and skipped READMEs, which are informational only, and whether the context layer at HEAD would project completely if a host mounted it (the closure enumerated, the failure detail, or no commit to judge). Report-only by default; exit 0. With --strict, exits nonzero when an unindexed, dangling, stale, or pending document is flagged (shadowed and skipped-README entries never fail the run), for CI use.
leji status [--strict] [--root <dir>] [--json]--strict | Exit nonzero when an unindexed, dangling, stale, or pending document is flagged, for CI. |
Examples
leji status
leji status --strict --jsonleji route
Read-only: given a task's scope (repository-relative paths it reads or changes, plus any categories and topics it names), print the slice of governed context that scope selects per the Task routing algorithm. Paths select the governed entries that contain them or are contained by them, and a path that is itself a governed document signals that document's category for decision and mount matching without expanding it; only a category the task explicitly names expands that category's intent documents and record candidates. Topics select sibling mounts and nothing else. Prints the expanded categories and the signalled ones, the governed documents (with each document's review horizon and whether it has expired), the record candidates a reader loads by judgment, the live decision records routed to the task, and the sibling mounts the supplied category and topic signals match. It computes the scope-dependent portion only: the boot profile's unconditional load set and the active agent profile's requiredRead are the caller's baseline and are never emitted here. Reads and reports context; it never executes a task.
- --paths and --categories take comma-separated values; either or both may be given.
- --topics is repeatable and each occurrence is one whole topic: values are never comma-split and never trimmed, all occurrences accumulate, exact duplicates count once, and an empty occurrence is an error.
- A topic matches a mount's declared topics by exact string equality, with no case conversion, normalization, locale, or fuzzy matching. A topic match selects the mount only: it never expands or signals a category, loads no document or record, and routes no decision.
- Document expiry is evaluated against --as-of (defaults to today); decisions carry no horizon.
- An empty path scope contributes no path matching and notes that path-scoped routing was not evaluated; categories named with --categories are still honored and still expand, and topics named with --topics are still matched. An empty whole scope, no paths, no categories, and no topics, routes only the org-wide unscoped live decisions.
leji route [--paths <a,b,...>] [--categories <a,b,...>] [--topics <topic>]... [--as-of <YYYY-MM-DD>] [--root <dir>] [--json]--paths <a,b,...> | Repository-relative paths the task reads or changes. Selects the entries they reach; signals a category without expanding it. |
--categories <a,b,...> | Content categories the task explicitly names. Only these expand a category's documents and record candidates. |
--topics <topic> | A topic the task explicitly names, matched against a mount's declared topics by exact equality. Repeatable, one whole topic per occurrence; selects mounts only. |
--as-of <YYYY-MM-DD> | Reference date for document expiry (default: today). |
Examples
leji route --paths src/payments/billing.ts
leji route --categories domain,system --json
leji route --topics "product surface" --topics billing --json
leji route --paths docs/system/invariants.md --as-of 2026-06-27 --jsonleji conformance
Runs the core, indexed, governed, and federated checklists. Machine-checkable items pass or fail; process items (review gate, CI, external consumers) are reported as manual. A machine failure at or below the claimed level is an error; evidence this run could not obtain reports unknown, which caps the verified level without refuting the claim. With --explain it also prints what it would take to reach the next level.
leji conformance [--explain] [--federation verify] [--root <dir>] [--json]--explain | Print actionable guidance for reaching the next conformance level. |
--federation verify | Run the networked pin-reachability probe against each mount's source (git ls-remote + witness-ref ancestry). Without it the pin-reachable item reports unknown, which never awards the federated level. |
Examples
leji conformance
leji conformance --explain
leji conformance --jsonleji mounts hydrate
For each declared federation mount, resolves the pinned commit from a local object store (an explicit hint in .leji/mounts.local.json, the resolver-managed store, or a unique matching submodule's object database) and extracts the sibling's layer projection into the gitignored cache under .leji/mounts/. The projection is the deduplicated union of everything the sibling's own manifest makes readable at the pin: the root leji.json, the tree under its declared context root, its boot profile, its machine index and changelog files when present, its agent-profiles and decision-records trees when present, every agent profile its agents map binds, every category index file, and every governed path its pinned generated index lists, wherever those live. The failure boundary follows the same line: a referenced or schema-required file absent at the pin (the boot profile, a category index, a bound agent profile, an indexed governed path) fails the projection naming the declaring artifact and the missing path, while an absent directory or an absent machine artifact contributes nothing and fails nothing. The only mutating mounts command, and offline by default: --fetch establishes the resolver-managed store for every declared mount, including one a hint already resolves, fetching the pin from the declared source, retaining it under refs/leji-pin/v1/, and refreshing the managed witness under refs/leji-witness/v1/ (the only writer of that namespace, since mounts status never fetches). Best-effort: an unavailable mount is reported and skipped (degraded knowledge, never a failed run); the exit code reflects declaration, safety, or projection errors only.
leji mounts hydrate [--fetch] [--root <dir>] [--json]--fetch | Establish the resolver-managed store for every declared mount: fetch and retain the pin, and refresh the managed witness ref. |
Examples
leji mounts hydrate
leji mounts hydrate --fetchleji mounts status
Read-only diagnostics for the declared federation mounts: whether the pinned projection is present in the cache, and an ancestry-aware pin report against the declared witness ref (trackingRef) computed from a reachable local object store: up-to-date, behind N, ahead, diverged, unrelated, or unknown, always naming the compared ref, the category of repository the comparison ran in (comparisonRepository: managed-store, hint, or submodule), whether the witness was the resolver's own ref or one it does not own (witnessProvenance), the observation time, and ancestry completeness. --check-integrity additionally re-derives the projection from the object store and compares it byte-for-byte (paths, modes, symlinks) against the cache. Never mutates and never touches the network.
leji mounts status [--check-integrity] [--root <dir>] [--json]--check-integrity | Verify the cached projection byte-for-byte against a reachable object store. |
Examples
leji mounts status
leji mounts status --check-integrity --jsonleji mounts locate
Resolves a declared mount's hydrated projection through resolver state (never by inferring cache paths): the projection directory, the pin, whether the bytes are present, and whether they verified this run. Readers obtain the mounted content's location from this command; a projection that cannot be verified is reported as present but unverified, which includes the case where a verification prerequisite (a reachable object store, a resolvable pin, a writable temp dir) is unavailable. Exits 0 when the projection is present, 1 otherwise.
leji mounts locate <name> [--root <dir>] [--json]Examples
leji mounts locate product-context
leji mounts locate product-context --jsonleji viewer
Projects the context index into a browsable Docsify viewer: writes a frontmatter-stripping index.html, a deterministic _sidebar.md, and the vendored viewer assets into the context layer's contained viewer directory. Presentation is non-normative; this is the reference projection. Generates only; use leji viewer serve (or leji view) to preview it locally, and leji viewer build to export a self-contained copy.
leji viewer [--root <dir>] [--json]Examples
leji viewer
leji viewer --jsonleji viewer serve
Generates the viewer, then serves it on localhost (a local preview, never hosting) at the web root. With --open it also opens your default browser at the viewer.
leji viewer serve [--port <n>] [--open] [--root <dir>] [--json]--open | Open the viewer in your default browser after serving. |
--port <n> | Port to serve on. Overrides the manifest viewer.port; default 5354 (LEJI on a phone keypad); 0 picks a free port. |
Examples
leji viewer serve
leji viewer serve --open
leji viewer serve --port 0leji viewer build
Regenerates the viewer and materializes it into a standalone static folder (default: .leji/viewer-dist/, kept out of git) that any host serves as-is. A custom --out must resolve inside the repository. The exported index.html warns that a context layer is sensitive and should be hosted behind internal authentication, not a public bucket.
leji viewer build [--out <dir>] [--root <dir>] [--json]--out <dir> | Output directory for the export (default: .leji/viewer-dist inside the context root; must resolve inside the repository). |
Examples
leji viewer build
leji viewer build --out dist/siteleji view
One-word shortcut to browse the context layer: generates the viewer, serves it on localhost, and opens your default browser. Equivalent to leji viewer serve --open.
leji view [--port <n>] [--root <dir>]--port <n> | Port to serve on. Overrides the manifest viewer.port; default 5354; 0 picks a free port. |
Examples
leji view
leji view --port 0leji start
Detects an installed agent (or use --agent), launches it from the context root, and points it at the boot profile so it loads the team's context first. The agent-facing counterpart to leji view. Several detected agents prompt for which; with none detected or in a non-interactive shell, it prints the command to run. Everything after a literal -- passes verbatim to the launched host binary, before the boot prompt. Host-specific flags ride with a pinned host: leji start --agent claude-code -- --chrome, never bare -- --chrome, which could hand the flag to whichever host gets picked.
leji start [--agent <name>] [--root <dir>] [-- <host flags…>]--agent <name> | Launch a specific host (claude-code or codex) instead of auto-detecting. |
-- <host flags…> | Pass the remaining arguments verbatim to the launched host binary; pin --agent when they are host-specific. |
Examples
leji start
leji start --agent codex
leji start --agent claude-code -- --chromeleji detect
Best-effort, read-only detection of installed agent hosts (Claude Code, Codex, Copilot, Gemini, Cursor, Windsurf), ranked by signal strength: a runnable binary, a config file in the repository, or a user-level config directory. Writes nothing; use it to decide which host to open with leji start --agent <name> (launchable hosts today: claude-code and codex; other detected hosts enter the context layer through their vendor-file redirect).
leji detect [--root <dir>] [--json]Examples
leji detect
leji detect --jsonleji adopt
Brings Leji into a repository that already has docs and agent config.
- Reuses an existing
docs/root, migrates any vendor entrypoints (CLAUDE.md,AGENTS.md, and so on) into the context layer without modifying the originals, and seeds the scaffold. - Writes a generated index, so the adopted context layer is ready for the CI job
leji ciwrites. The index is a requirement ofindexed, not ofcore; a hand-authored core context layer without one still conforms. --wire-adaptersconverts those entrypoints to one-line redirects, after migrating their content.- When no
AGENTS.mdexists, writes a pointer-only one (the portable entrypoint many agent hosts read) redirecting to the boot profile;--no-agentsskips it, and an existing file is never touched. --mode solo(a team of one) also seeds identity and writing-style starters and points the onboarding brief at the owner interview; existing files are never overwritten.- Refuses when a
leji.jsonexists,--dry-runincluded: a repository that already has a context layer has nothing to adopt. Also refuses when the git tree has uncommitted changes, which--dry-runis exempt from because it writes nothing.
leji adopt [--dir <path>] [--yes] [--mode <solo|team>] [--agent <name>] [--wire-adapters] [--no-agents] [--dry-run]--dir <path> | Target directory (default: the current directory). |
--yes, -y | Accept all defaults; run non-interactively. |
--mode <mode> | Working mode: solo (team of one; seeds identity + writing-style starters) or team (default). |
--agent <name> | Host to open in the context layer after the command (claude-code or codex). Selects the handoff host; the interactive flow may separately offer to register the MCP server or install the approval guard, each disclosed and consented to. |
--wire-adapters | Convert present vendor entrypoints to redirects (consented; content migrated first). |
--no-agents | Skip generating the portable AGENTS.md pointer (default: written when absent). |
--dry-run | Print the write plan and exit without changing anything. |
Examples
leji adopt
leji adopt --dry-run
leji adopt --mode solo
leji adopt --wire-adaptersleji init
Scaffolds a new context layer from the templates.
- Writes
leji.json, a boot profile, a pointer-onlyAGENTS.md(the portable entrypoint many agent hosts read, redirecting to the boot profile;--no-agentsskips it), seeded category documents, a first decision record, an agent onboarding brief, and a generated index, so the scaffold is ready for the CI jobleji ciwrites. At the indexed level it also writes the machine changelog. The index is a requirement ofindexed, not ofcore; a hand-authored core context layer without one still conforms. --mode solo(a team of one) also seeds identity and writing-style starters, maps the practice category, routes identity and writing work in the boot profile, and points the onboarding brief at the owner interview (answer in text or with dropped files).- Refuses to overwrite an existing
leji.json, and refuses when the git tree has uncommitted changes; never overwrites individual files. --dry-runprints the write plan without writing.- Also backs
npm create leji.
leji init [--dir <path>] [--yes] [--mode <solo|team>] [--level <core|indexed>] [--name <name>] [--agent <name>] [--no-agents] [--dry-run]--dir <path> | Target directory (default: the current directory). |
--yes, -y | Accept all defaults; run non-interactively. |
--mode <mode> | Working mode: solo (team of one; seeds identity + writing-style starters) or team (default). |
--level <level> | Conformance level to claim: core or indexed (default: core). |
--name <name> | Context layer name (default: derived from the directory). |
--agent <name> | Host to open in the context layer after the command (claude-code or codex). Selects the handoff host; the interactive flow may separately offer to register the MCP server or install the approval guard, each disclosed and consented to. |
--no-agents | Skip generating the portable AGENTS.md pointer (default: written when absent). |
--dry-run | Print the write plan and exit without creating any files. |
Examples
leji init
leji init --dry-run
leji init --mode solo
leji init --agent claude-codeleji ci
Adds a CI job that runs leji validate and leji index --check on every change, so your context layer stays honest in CI (the same two gates the --hooks pre-commit runs locally). Idempotent: a Leji workflow already in place is left untouched.
- The provider is inferred from the
originremote (a github.com host selects GitHub, any gitlab host GitLab, an Azure DevOps host Azure Pipelines) and falls back to GitHub when the remote names none;--provideroverrides the inference, and CircleCI is never inferred. - GitHub: writes its own workflow at
.github/workflows/leji.yml. - GitLab: merges a managed block into
.gitlab-ci.yml, creating the file if it's absent. - CircleCI: writes
.circleci/config.ymlif absent; when a config already exists, prints a snippet to add by hand instead of editing it. - Azure DevOps: writes
.azure-pipelines/leji.yml. ADO does not auto-discover it, so activation is manual: create a pipeline that points at the file (e.g.az pipelines create --yml-path .azure-pipelines/leji.yml), then add a build-validation branch policy onmainfor pull-request checks. This activation note prints once, on first creation. - Local hook (
--hooks): writes a managed pre-commit runningleji validateandleji index --check.core.hooksPathis detected, so a husky repo gets a managed block merged into.husky/pre-commitrather than a dead.git/hooksfile; an existing unmanaged hook is never touched, its snippet printed to add by hand. - Local-first: when the repository declares
@leji-org/lejiin its package.json, the generated CI job runs that lockfile-pinned install (npm ci, thennpx --no-install @leji-org/lejiforvalidateandindex --check); a repository without it falls back tonpx @leji-org/leji@1. The generated hook independently prefers a repo-localnode_modules/.bin/lejiwhen present, else thelejion PATH (it does not runnpm cior read the dependency declaration).
leji ci [--provider <github|gitlab|circleci|azure>] [--hooks] [--root <dir>] [--json]--hooks | Write a managed local pre-commit running validate + index --check; core.hooksPath is detected, so a husky repo gets a managed block in .husky/pre-commit, and an existing unmanaged hook is left untouched with the snippet printed. |
--provider <name> | CI provider: github (default when no remote is recognizable), gitlab, circleci, or azure. Without this flag the provider is inferred from the origin remote. |
Examples
leji ci
leji ci --provider gitlab
leji ci --provider azure
leji ci --hooksleji agent
Adds a second (or third) agent to a context layer that already has a leji.json.
- Writes a starter agent profile under the agent-profiles path and binds it in the manifest's agents map via an in-place edit that preserves the rest of the file.
- Never writes an agent-host entrypoint file. The portable
AGENTS.mdpointer is written byinitandadopt(unless--no-agents); single-vendor files likeCLAUDE.mdare only ever converted from an existing one byadopt --wire-adapters. --hostis optional: a host pins the profile to a specific external CLI; with none, it's a host-agnostic resident agent any host can run.- The role defaults to reviewer; pass
--rolefor a different one. - Binding the
defaultkey prints a note:agents.defaultselects a role profile, it does not load it, so instructions that must apply before every task belong in the boot profile rather than that profile. This note prints once, when the binding is written. - Idempotent: an existing profile or binding is left untouched. Requires an existing context layer.
leji agent --name <name> [--host <host>] [--role <role>] [--root <dir>] [--json]--name <name> | Name for the agent; also its profile id and agents-map key (kebab-case). |
--host <host> | Optional. Pin the profile to a host (claude-code, codex, copilot, gemini, cursor, windsurf; aliases ok); omit for a host-agnostic resident agent. |
--role <role> | Role the agent fills (default: reviewer). |
Examples
leji agent --name porter --role porter
leji agent --host codex --name reviewer
leji agent --host claude-code --name thought-partner --role advisor