smriti/cli
Himanshu Dongre 2a6614bd80 Add fork, compare, restore CLI commands and fix compare correctness
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).
2026-04-11 17:50:03 +05:30
..
smriti_cli Add fork, compare, restore CLI commands and fix compare correctness 2026-04-11 17:50:03 +05:30
pyproject.toml Add CLI for agent and programmatic access 2026-04-11 11:10:01 +05:30
README.md Add fork, compare, restore CLI commands and fix compare correctness 2026-04-11 17:50:03 +05:30

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

  • 0 success
  • 1 API error, invalid input, or backend unreachable
  • 130 interrupted (Ctrl+C)