smriti/cli
Himanshu Dongre 6028dacff1 Extract checkpoint fields from freeform markdown via LLM
Round 3 of the dogfood confirmed that every multi-branch CLI command
works end-to-end, but the single biggest remaining friction is still
checkpoint payload construction. Each agent hands off a ~15-18 KB
markdown document; turning that into the Smriti schema (decisions,
assumptions, tasks, open_questions, entities, artifacts) is three
minutes of hand-written JSON per checkpoint and adds no product value.

This build adds an LLM-powered extractor that collapses that work into
one pipe command:

  cat /tmp/r3_agent_a_output.md | smriti checkpoint create my-project \
      --extract --author-agent codex-A

The CLI reads stdin as freeform markdown, calls the new
POST /api/v5/checkpoint/extract endpoint, maps the returned fields
into a commit payload, and writes the checkpoint. --dry-run prints the
extracted payload without committing so users can review first.
--extract and --from-json are mutually exclusive.

Backend architecture mirrors the existing review endpoint: stateless
LLM call (no session or commit ID required), uses the same background
intelligence provider (cfg.background.provider / cfg.background.model)
as draft and review, same JSON-mode prompt shape, same 502-on-parse-
error pattern. The extractor endpoint differs in one small way: it
passes allow_mock=True to get_adapter so unconfigured test envs fall
back to MockAdapter without raising 500. Production envs always have
a real provider configured and never hit this fallback.

The extractor is the first LLM-backed endpoint that gets tested
against a real mock response. To make that work, MockAdapter.send now
detects response_format={"type": "json_object"} in kwargs and returns
a canned JSON blob covering every field any current Smriti endpoint
looks for (title, objective, summary, decisions, assumptions, tasks,
open_questions, entities, artifacts, issues, suggestions). Existing
chat.send text-mode tests are unaffected because they don't pass
response_format. This also unblocks future tests for draft and review.

Manual verification against a real OpenAI provider: piped a realistic
23-line handoff markdown with 4 decisions, 3 assumptions, 3 tasks,
2 open questions, and a python code block. The extractor returned
exactly those items in the right fields (4/3/3/2/1) and produced a
valid checkpoint with all fields populated. Round 4's load-bearing
claim — zero hand-written JSON per checkpoint — is now achievable.

153/153 backend tests pass (149 pre-existing + 4 new extract tests).
2026-04-11 19:15:03 +05:30
..
smriti_cli Extract checkpoint fields from freeform markdown via LLM 2026-04-11 19:15:03 +05:30
pyproject.toml Add CLI for agent and programmatic access 2026-04-11 11:10:01 +05:30
README.md Extract checkpoint fields from freeform markdown via LLM 2026-04-11 19:15: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 (full artifacts by default)
smriti state <space> --preview                           # truncate artifacts to a short preview
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 JSON file
smriti checkpoint create <space> --extract               # reads markdown, LLM extracts schema fields
smriti checkpoint create <space> --extract --dry-run     # preview the extracted payload without committing
smriti checkpoint create <space> --session <session-id>  # attach to existing session
smriti checkpoint create <space> --author-agent claude-code
smriti checkpoint create <space> --project-root /path    # override cwd auto-capture
smriti checkpoint show <checkpoint-id>
smriti checkpoint list <space>
smriti checkpoint review <checkpoint-id>
smriti checkpoint delete <checkpoint-id> [--cascade] [-y]

smriti state shows full artifact content by default — flip to --preview for the truncated brief.

smriti checkpoint create auto-captures the current working directory as the checkpoint's project_root so cross-agent handoffs know where the project actually lives on disk. Pass --project-root /absolute/path to override or --no-project-root to skip. Tag the checkpoint with an explicit --author-agent <name> (like claude-code or codex-local); without it, the backend falls back to the session's active provider.

Extracting checkpoints from freeform agent output: instead of hand-writing the JSON payload, pipe an agent's markdown output to --extract and let Smriti's background LLM extract the structured fields (decisions, assumptions, tasks, open questions, entities, artifacts) for you:

# Extract and commit in one step
cat /tmp/r3_agent_a_output.md | smriti checkpoint create my-project --extract --author-agent codex-A

# Preview what would be extracted, without committing
cat /tmp/r3_agent_a_output.md | smriti checkpoint create my-project --extract --dry-run

--extract reads stdin as freeform markdown, sends it to POST /api/v5/checkpoint/extract, and uses the returned fields to build the commit payload. --dry-run prints the extracted payload as JSON and exits without creating a checkpoint. --extract and --from-json are mutually exclusive.

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
project_root string Absolute path to the project's working directory. Auto-captured by the CLI at commit time; can be overridden via the payload or --project-root.
author_agent string Agent identifier. The CLI flag --author-agent overrides any payload value; when unset the backend tags the checkpoint with the session's active provider.

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)