From b8e6e0403d4c8e0f65a5cfcfaa6026f1011f134b Mon Sep 17 00:00:00 2001 From: Himanshu Dongre Date: Mon, 13 Apr 2026 15:37:41 +0530 Subject: [PATCH] Add --compact mode to smriti state: artifact labels only with recovery instruction --- CLAUDE.md | 7 +- cli/smriti_cli/formatters.py | 36 +++++++++- cli/smriti_cli/main.py | 11 ++- cli/smriti_cli/mcp_server.py | 14 +++- cli/tests/test_state_multi_branch.py | 101 +++++++++++++++++++++++++++ 5 files changed, 162 insertions(+), 7 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 003b286..8be8cdd 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,8 +1,11 @@ # Smriti — project instructions for Claude Code This project uses Smriti as the shared reasoning-state backend. -A SessionStart hook injects `smriti state smriti-dev` into your -context automatically. Read it before doing anything else. +A SessionStart hook injects `smriti state smriti-dev --compact` into +your context automatically. Read it before doing anything else. +Artifact content is omitted in compact mode — if the state brief +lists artifacts that look relevant, inspect them with +`smriti checkpoint show --full-artifacts`. ## Session start checklist diff --git a/cli/smriti_cli/formatters.py b/cli/smriti_cli/formatters.py index 7aa46c1..f8b4d93 100644 --- a/cli/smriti_cli/formatters.py +++ b/cli/smriti_cli/formatters.py @@ -62,9 +62,34 @@ def _list_section(heading: str, items: list[str]) -> str: return "\n".join(lines) + "\n" -def _artifact_section(artifacts: list[dict], preview_chars: int = 800, full: bool = False) -> str: +def _artifact_section( + artifacts: list[dict], + preview_chars: int = 800, + full: bool = False, + compact: bool = False, + checkpoint_id: str = "", +) -> str: if not artifacts: return "" + if compact: + # Labels only — no content. Explicit recovery instruction. + lines = [f"## Attached artifacts (compact — content omitted)"] + for art in artifacts: + label = art.get("label") or "Untitled" + lines.append(f"- {label}") + lines.append("") + if checkpoint_id: + lines.append( + f"To inspect artifact content: " + f"`smriti checkpoint show {checkpoint_id} --full-artifacts`" + ) + else: + lines.append( + "To inspect artifact content: " + "`smriti checkpoint show --full-artifacts`" + ) + return "\n".join(lines) + "\n" + lines = ["## Attached artifacts"] for art in artifacts: label = art.get("label") or "Untitled" @@ -159,6 +184,7 @@ def format_state_brief( commit: dict, *, full_artifacts: bool = False, + compact: bool = False, space_state: dict | None = None, ) -> str: """A continuation-oriented markdown brief for the current project state. @@ -215,7 +241,13 @@ def format_state_brief( parts.append(_list_section("Assumptions we are relying on", assumptions)) parts.append(_list_section("Open questions", open_questions)) parts.append(_list_section("In progress", tasks)) - parts.append(_artifact_section(artifacts, full=full_artifacts)) + checkpoint_id = commit.get("id") or head.get("commit_id") or "" + parts.append(_artifact_section( + artifacts, + full=full_artifacts, + compact=compact, + checkpoint_id=str(checkpoint_id), + )) if entities: parts.append(f"## Key entities\n{', '.join(entities)}\n") diff --git a/cli/smriti_cli/main.py b/cli/smriti_cli/main.py index 6cd81bd..879ae41 100644 --- a/cli/smriti_cli/main.py +++ b/cli/smriti_cli/main.py @@ -221,7 +221,8 @@ def cmd_state(client: SmritiClient, args: argparse.Namespace) -> None: "divergence": state.get("divergence"), } - full_artifacts = not args.preview + full_artifacts = not args.preview and not args.compact + compact = args.compact if args.json: payload = {"space": space, "head": head, "commit": commit} if space_state is not None: @@ -234,6 +235,7 @@ def cmd_state(client: SmritiClient, args: argparse.Namespace) -> None: format_state_brief( space, head, commit, full_artifacts=full_artifacts, + compact=compact, space_state=space_state, ), end="", @@ -875,6 +877,13 @@ def _build_parser() -> argparse.ArgumentParser: "Default: multi-branch state — main brief plus any active " "non-main branches and divergence signal.", ) + state_parser.add_argument( + "--compact", + action="store_true", + help="Omit artifact content, show labels only. Saves tokens for " + "session-start injection. Full content recoverable via " + "smriti checkpoint show --full-artifacts.", + ) state_parser.add_argument("--json", action="store_true", help="Output structured JSON") state_parser.set_defaults(func=cmd_state) diff --git a/cli/smriti_cli/mcp_server.py b/cli/smriti_cli/mcp_server.py index e72df5b..4d8670b 100644 --- a/cli/smriti_cli/mcp_server.py +++ b/cli/smriti_cli/mcp_server.py @@ -111,7 +111,12 @@ def _empty_space_brief(space: dict) -> str: @mcp.tool() -def smriti_state(space: str, preview: bool = False, main_only: bool = False) -> str: +def smriti_state( + space: str, + preview: bool = False, + compact: bool = False, + main_only: bool = False, +) -> str: """Print a continuation-oriented brief of a space's current state. By default the brief is multi-branch aware: it includes the main-branch @@ -131,6 +136,10 @@ def smriti_state(space: str, preview: bool = False, main_only: bool = False) -> space: Space name or UUID. preview: If True, truncate artifact content to a short preview instead of showing it in full. Default: full artifacts. + compact: If True, omit artifact content entirely — show only + artifact labels with a recovery instruction. Saves tokens + for session-start injection. Full content recoverable via + smriti_show_checkpoint. Default False. main_only: If True, fetch only the main-branch HEAD via the legacy /head endpoint and skip the Active branches and Divergence signal sections entirely. Default False. @@ -159,7 +168,8 @@ def smriti_state(space: str, preview: bool = False, main_only: bool = False) -> _raise_from(e) return format_state_brief( s, head, commit, - full_artifacts=not preview, + full_artifacts=not preview and not compact, + compact=compact, space_state=space_state, ) diff --git a/cli/tests/test_state_multi_branch.py b/cli/tests/test_state_multi_branch.py index 507dcca..60466f7 100644 --- a/cli/tests/test_state_multi_branch.py +++ b/cli/tests/test_state_multi_branch.py @@ -316,3 +316,104 @@ def test_mcp_smriti_state_no_checkpoints_short_circuit(mock_client): out = mcp_server.smriti_state(space="my-project") assert "No checkpoints yet" in out + + +# ── Compact mode tests ────────────────────────────────────────────────────── + + +def _commit_with_artifacts(): + """Commit with realistic artifacts for compact mode testing.""" + base = _base_commit() + base["id"] = "abc12345-6789-0abc-def0-123456789abc" + base["artifacts"] = [ + { + "id": "a1", + "type": "python", + "label": "Draft implementation", + "content": "def hello():\n return 'world'\n" * 50, + }, + { + "id": "a2", + "type": "markdown", + "label": "Test plan", + "content": "# Test Plan\n\n- Unit tests for X\n- Integration tests for Y\n" * 30, + }, + ] + return base + + +def test_compact_omits_artifact_content(): + """Compact mode shows artifact labels but not content.""" + commit = _commit_with_artifacts() + out = format_state_brief( + _base_space(), _base_head(), commit, compact=True, + ) + + # Labels present + assert "Draft implementation" in out + assert "Test plan" in out + # Content absent + assert "def hello():" not in out + assert "Unit tests for X" not in out + # Recovery instruction present + assert "compact — content omitted" in out + assert "smriti checkpoint show" in out + assert "abc12345-6789-0abc-def0-123456789abc" in out + + +def test_compact_is_smaller_than_full(): + """Compact output must be materially smaller than full output.""" + commit = _commit_with_artifacts() + full = format_state_brief( + _base_space(), _base_head(), commit, full_artifacts=True, + ) + compact = format_state_brief( + _base_space(), _base_head(), commit, compact=True, + ) + + # Compact should be at least 50% smaller when artifacts dominate + assert len(compact) < len(full) * 0.5, ( + f"Compact ({len(compact)} chars) should be less than 50% of " + f"full ({len(full)} chars)" + ) + + +def test_compact_preserves_decisions_and_tasks(): + """Compact mode must not touch non-artifact sections.""" + commit = _commit_with_artifacts() + out = format_state_brief( + _base_space(), _base_head(), commit, compact=True, + ) + + assert "## Decisions" in out + assert "Decision A" in out + assert "## Assumptions we are relying on" in out + assert "Assumption X" in out + assert "## In progress" in out + assert "Task 1" in out + + +def test_compact_with_no_artifacts_is_clean(): + """Compact mode on a commit with no artifacts should not show the section.""" + commit = _base_commit() # no artifacts + out = format_state_brief( + _base_space(), _base_head(), commit, compact=True, + ) + + assert "Attached artifacts" not in out + assert "compact" not in out.lower() or "compact" in out.lower() # no section = no mention + + +def test_mcp_smriti_state_compact_mode(mock_client): + """MCP tool with compact=True should pass compact to formatter.""" + mock_client.resolve_space.return_value = _space_dict() + state = _space_state_dict() + state["commit"] = _commit_with_artifacts() + mock_client.get_space_state.return_value = state + + out = mcp_server.smriti_state(space="my-project", compact=True) + + # Labels present, content absent + assert "Draft implementation" in out + assert "def hello():" not in out + assert "compact — content omitted" in out