Round 2 of the agent handoff dogfood showed that every multi-branch
operation required reaching past the CLI into curl: fork had no CLI
command, `smriti checkpoint create` always spawned a fresh session with
no way to attach to a forked one, and the compare endpoint returned
useless output (common_ancestor_commit_id was missing from the response,
and shared-set matching was exact-string so two agents phrasing the
same commitment differently showed zero overlap).
This ships the full CLI surface for multi-branch workflows plus the
backend fixes that make compare actually useful:
smriti fork <checkpoint-id> [--branch <name>]
smriti restore <checkpoint-id>
smriti compare <checkpoint-a> <checkpoint-b>
smriti checkpoint create <space> --session <session-id>
The compare endpoint now walks parent chains to compute a lowest
common ancestor (bounded to 1000 steps with a cycle guard) and returns
it on CheckpointDiff as an optional uuid. Shared-set matching uses a
lightweight lowercase + punctuation-strip + whitespace-collapse
normalization for keying, but returns the original A-side strings so
the output stays readable. Four new compare tests cover direct and
two-step LCA, null LCA for unrelated checkpoints, and normalized
shared-set matching. Existing compare tests still pass unchanged
because their data ("Use Redis" vs "Use Postgres") is distinct at any
sensible normalization level.
`smriti restore <checkpoint>` is a pure read — it renders any
checkpoint as a continuation brief matching `smriti state <space>`
shape. `smriti fork` derives the space from the checkpoint so the
user does not have to pass it separately. `--session` on checkpoint
create is purely additive: when absent, the existing auto-session
behavior is unchanged.
147/147 backend tests pass (143 pre-existing + 4 new).
|
||
|---|---|---|
| .. | ||
| smriti_cli | ||
| pyproject.toml | ||
| README.md | ||
smriti-cli
Command-line access to Smriti's reasoning-state backend. Built for coding agents and scripts — pipe JSON in, get readable markdown out.
Install
From the repo root:
cd cli
pip install -e .
This installs a smriti command on your PATH.
Configuration
Set the backend URL via env var (defaults to http://localhost:8000):
export SMRITI_API_URL=http://localhost:8000
Or pass --api-url on any command.
Commands
smriti space list
smriti space create <name> [--description "..."]
smriti space delete <space> [-y]
smriti state <space> # continuation brief
smriti state <space> --full-artifacts # include full artifacts
smriti state <space> --json # structured output
smriti fork <checkpoint-id> [--branch <name>] # new session from checkpoint
smriti restore <checkpoint-id> # brief of a specific checkpoint
smriti compare <checkpoint-a> <checkpoint-b> # structured diff
smriti checkpoint create <space> # reads JSON from stdin
smriti checkpoint create <space> --from-json <path> # from file
smriti checkpoint create <space> --session <session-id> # attach to existing session
smriti checkpoint show <checkpoint-id>
smriti checkpoint list <space>
smriti checkpoint review <checkpoint-id>
smriti checkpoint delete <checkpoint-id> [--cascade] [-y]
Every command supports --json for structured output.
Typical agent workflow
Read current project state:
smriti state my-project
Write a checkpoint from a JSON object piped on stdin:
cat <<'JSON' | smriti checkpoint create my-project
{
"message": "Decided to use Pydantic for state validation",
"objective": "Build runtime-enforced state layer",
"summary": "...",
"decisions": ["Use Pydantic BaseModel for state", "extra=forbid blocks injection"],
"assumptions": ["Latency cost is acceptable"],
"tasks": ["Benchmark validation overhead"],
"open_questions": ["How to handle shared state across agents"],
"entities": ["Pydantic", "BaseModel"],
"artifacts": [
{"id": "a1", "type": "text", "label": "Draft implementation", "content": "..."}
]
}
JSON
Review a specific checkpoint for consistency issues:
smriti checkpoint review <checkpoint-id>
Multi-branch workflow
When you want to explore an alternative direction from a checkpoint without losing the main branch, fork it into a new session and write checkpoints there:
# Fork a new session off checkpoint C1
smriti fork <C1-checkpoint-id> --branch experiment
# The output gives you the new session ID. Write a checkpoint to that session:
cat <<'JSON' | smriti checkpoint create my-project --session <fork-session-id>
{
"message": "Alternative design direction",
"summary": "...",
"decisions": ["Try stdlib only instead of click"]
}
JSON
# Compare the two branches
smriti compare <C1-checkpoint-id> <new-checkpoint-id>
# Read any checkpoint as a continuation brief (what you'd need to continue from it)
smriti restore <new-checkpoint-id>
smriti compare shows a structured diff with Shared, Only in A, and Only in B sections for decisions, assumptions, and tasks, plus the lowest common ancestor of the two checkpoints. The shared-set matching is case- and punctuation-insensitive, so two agents phrasing the same commitment differently still show up as shared.
Checkpoint payload schema
Only message is required. Every other field defaults to empty.
| Field | Type | Notes |
|---|---|---|
message |
string | Short title (required) |
objective |
string | What you are working toward |
summary |
string | Narrative of what was figured out |
decisions |
string[] | Explicit choices made |
assumptions |
string[] | Things taken for granted |
tasks |
string[] | Concrete action items |
open_questions |
string[] | Unresolved issues |
entities |
string[] | Key concepts, tools, names |
artifacts |
object[] | {id, type, label, content} entries |
Space resolution
<space> arguments accept either the space name or the UUID. Names are matched exactly first, then case-insensitively. If multiple spaces match, the CLI asks you to use a UUID.
Exit codes
0success1API error, invalid input, or backend unreachable130interrupted (Ctrl+C)