@cairnkeep/cli 2.3.1 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/README.md +221 -6
  3. package/bin/cairn +43 -2
  4. package/claude/capability-contract/hooks/capability-command-finish.sh +81 -0
  5. package/claude/capability-contract/hooks/capability-command-start.sh +76 -0
  6. package/claude/hooks/compaction-capture.sh +34 -0
  7. package/claude/hooks/memory-capture.sh +47 -5
  8. package/claude/hooks/memory-wakeup.sh +55 -2
  9. package/docs/building-an-overlay.md +1 -1
  10. package/docs/design/meta-agent.md +439 -0
  11. package/docs/learning/lessons/L00-why-cairnkeep.md +1 -1
  12. package/docs/learning/lessons/L01-safe-trial.md +3 -3
  13. package/docs/learning/lessons/L02-installation.md +2 -2
  14. package/docs/learning/lessons/L03-first-project.md +1 -1
  15. package/docs/operating.md +531 -4
  16. package/docs/privacy-and-data-flow.md +417 -0
  17. package/docs/storage.md +252 -0
  18. package/examples/eval/README.md +42 -0
  19. package/examples/eval/adapter.json +12 -0
  20. package/examples/eval/bundled-fake.json +1 -0
  21. package/examples/eval/task-set.json +1 -0
  22. package/mcp-memory-server/dist/artifact-cli.js +184 -0
  23. package/mcp-memory-server/dist/artifact-schema.js +218 -0
  24. package/mcp-memory-server/dist/artifact-store.js +812 -0
  25. package/mcp-memory-server/dist/capability-adapter.js +286 -0
  26. package/mcp-memory-server/dist/capability-cli.js +361 -0
  27. package/mcp-memory-server/dist/capability-config.js +310 -0
  28. package/mcp-memory-server/dist/capability-harness.js +496 -0
  29. package/mcp-memory-server/dist/capability-registry.js +123 -0
  30. package/mcp-memory-server/dist/capability-schema.js +65 -0
  31. package/mcp-memory-server/dist/capability-store.js +431 -0
  32. package/mcp-memory-server/dist/compaction-normalize.js +267 -0
  33. package/mcp-memory-server/dist/eval-cli.js +523 -0
  34. package/mcp-memory-server/dist/eval-plan.js +346 -0
  35. package/mcp-memory-server/dist/eval-process.js +273 -0
  36. package/mcp-memory-server/dist/eval-report.js +609 -0
  37. package/mcp-memory-server/dist/eval-runner.js +827 -0
  38. package/mcp-memory-server/dist/eval-schema.js +368 -0
  39. package/mcp-memory-server/dist/eval-statistics.js +161 -0
  40. package/mcp-memory-server/dist/eval-workspace.js +313 -0
  41. package/mcp-memory-server/dist/failure-signature.js +141 -0
  42. package/mcp-memory-server/dist/index.js +600 -55
  43. package/mcp-memory-server/dist/node-cli.js +84 -0
  44. package/mcp-memory-server/dist/node-schema.js +170 -0
  45. package/mcp-memory-server/dist/node-store.js +455 -0
  46. package/mcp-memory-server/dist/note-cli.js +235 -0
  47. package/mcp-memory-server/dist/note-distiller.js +177 -0
  48. package/mcp-memory-server/dist/note-enrichment.js +93 -0
  49. package/mcp-memory-server/dist/note-schema.js +4 -0
  50. package/mcp-memory-server/dist/note-store.js +985 -0
  51. package/mcp-memory-server/dist/trajectory-cli.js +141 -0
  52. package/mcp-memory-server/dist/trajectory-normalize.js +448 -0
  53. package/mcp-memory-server/dist/trajectory-redaction.js +155 -0
  54. package/mcp-memory-server/dist/trajectory-schema.js +73 -0
  55. package/mcp-memory-server/dist/trajectory-store.js +339 -0
  56. package/mcp-memory-server/package.json +24 -2
  57. package/mcp-memory-server/scripts/fixtures/compaction-claude-code-2.1.219.json +9 -0
  58. package/mcp-memory-server/scripts/fixtures/compaction-claude-code-2.1.220.json +9 -0
  59. package/mcp-memory-server/scripts/fixtures/compaction-opencode-1.17.20-event.json +6 -0
  60. package/mcp-memory-server/scripts/fixtures/compaction-opencode-1.17.20-messages.json +119 -0
  61. package/mcp-memory-server/scripts/fixtures/compaction-unknown-version.json +17 -0
  62. package/opencode/capability-contract/plugins/capability-command.ts +319 -0
  63. package/opencode/command/graphify.md +25 -14
  64. package/opencode/plugins/memory-capture.ts +76 -10
  65. package/opencode/plugins/memory-wakeup.ts +58 -6
  66. package/package.json +8 -4
  67. package/pi/extensions/cairnkeep-trajectory.ts +55 -0
  68. package/schemas/artifact.schema.json +133 -0
  69. package/schemas/capability-callback.schema.json +68 -0
  70. package/schemas/capability-contract.schema.json +126 -0
  71. package/schemas/eval-adapter.schema.json +43 -0
  72. package/schemas/eval-protocol.schema.json +114 -0
  73. package/schemas/eval-report.schema.json +423 -0
  74. package/schemas/eval-task-set.schema.json +114 -0
  75. package/schemas/memory-node.schema.json +90 -0
  76. package/schemas/note.schema.json +112 -0
  77. package/schemas/trajectory-redaction.schema.json +25 -0
  78. package/schemas/trajectory.schema.json +82 -0
  79. package/scripts/bootstrap.sh +4 -0
  80. package/scripts/completion.sh +119 -5
  81. package/scripts/doctor.sh +284 -0
  82. package/scripts/fake-eval-adapter.mjs +120 -0
  83. package/scripts/fixtures/capability-harness-contracts.json +408 -0
  84. package/scripts/install-audit-timer.sh +3 -1
  85. package/scripts/lib/capability-opencode-plugin-harness.mjs +291 -0
  86. package/scripts/lib/trajectory-opencode-plugin-harness.mjs +25 -0
  87. package/scripts/lib/trajectory-pi-extension-harness.mjs +42 -0
  88. package/scripts/memory-wiki-audit.sh +49 -0
  89. package/scripts/smoke-bash32.sh +9 -0
  90. package/scripts/sync-claude-assets.sh +99 -6
  91. package/scripts/sync-opencode-graphify-assets.sh +24 -3
  92. package/scripts/sync-opencode-plugin-assets.sh +31 -5
  93. package/scripts/sync-opencode-security-assets.sh +24 -3
  94. package/scripts/sync-opencode-wiki-assets.sh +24 -3
  95. package/scripts/sync-pi-assets.sh +72 -0
  96. package/scripts/test-audit-timer.sh +98 -0
  97. package/scripts/test-bootstrap-untracked.sh +48 -0
  98. package/scripts/test-cli-dispatch.sh +41 -2
  99. package/scripts/test-compaction-hooks.sh +495 -0
  100. package/scripts/test-completion.sh +77 -0
  101. package/scripts/test-doctor.sh +348 -1
  102. package/scripts/test-eval-cli.sh +175 -0
  103. package/scripts/test-launcher-hooks.sh +602 -3
  104. package/scripts/test-notes-cli.sh +114 -0
  105. package/scripts/test-package-install.sh +251 -0
  106. package/scripts/test-phase16-node-lifecycle.sh +50 -0
  107. package/scripts/test-phase17-artifact-lifecycle.sh +370 -0
  108. package/scripts/test-phase18-capability-lifecycle.sh +504 -0
  109. package/scripts/test-phase18-harness-boundary.sh +1060 -0
  110. package/scripts/test-phase19-eval-lifecycle.sh +1015 -0
  111. package/scripts/test-trajectory-cli.sh +87 -0
  112. package/scripts/test-trajectory-hooks.sh +142 -0
  113. package/scripts/test-uninstall.sh +231 -5
  114. package/scripts/uninstall.sh +37 -5
  115. package/scripts/verify-dependency-security.mjs +121 -0
  116. package/scripts/verify-docs-parity.sh +527 -1
  117. package/scripts/verify-phase18-runtime-evidence.mjs +895 -0
  118. package/scripts/verify-phase19-runtime-evidence.mjs +947 -0
  119. package/templates/capabilities.json.template +7 -0
  120. package/templates/env.example.template +51 -0
  121. package/templates/memory-wiki-audit.service.template +1 -1
  122. package/templates/start-claude.sh.template +36 -0
  123. package/templates/start-opencode.sh.template +47 -5
  124. package/templates/start-pi.sh.template +30 -0
  125. package/templates/trajectory-redaction.json.template +4 -0
package/CHANGELOG.md CHANGED
@@ -3,6 +3,73 @@
3
3
  All notable user-facing changes are documented here. This project follows
4
4
  [Semantic Versioning](https://semver.org/).
5
5
 
6
+ ## [Unreleased]
7
+
8
+ ## [2.4.0] - 2026-07-29
9
+
10
+ ### Added
11
+
12
+ - Add opt-in, local-only, structured Claude Code and OpenCode session
13
+ trajectories with pre-write redaction, reasoning omission, bounded retention
14
+ and no model or network dependency.
15
+ - Add `cairn trajectory list|show|prune`, shell completion, and explicit
16
+ `cairn doctor --repair` support for trajectory metadata and indexes.
17
+ - Add native Pi trajectory capture through its `session_shutdown` extension,
18
+ `cairn sync-pi`, a generic Pi launcher, uninstall coverage, and the same
19
+ opt-in local redaction, size, and retention boundary as other harnesses.
20
+ - Add default-off deterministic hindsight-note distillation, exact repeated
21
+ error lookup, typed local Markdown hierarchy, lifecycle history, explicit
22
+ corroborated shared promotion, and `cairn notes` CLI/completion support.
23
+ - Add separately opted-in provider-neutral note prose enrichment and integrate
24
+ all-project incremental distillation into the existing audit timer without
25
+ adding online-agent latency.
26
+ - Add default-off typed memory metadata/tags, hard-filtered discovery,
27
+ history-aware structured `memory_import`, and logical project/shared note
28
+ address spaces with journaled crash recovery and doctor integration.
29
+ - Add independent default-off compaction continuity for supported Claude Code
30
+ and OpenCode seams, capturing only harness-produced summaries into local,
31
+ redacted, bounded immutable revisions with structured fresh-session recovery.
32
+ - Add a separate local artifact store for compaction summaries, diffs, test
33
+ output, and generated-file metadata/snapshots; four gated MCP tools;
34
+ `cairn artifact list|show|delete|prune`; doctor, completion, package, and
35
+ backup-first uninstall coverage; and separately consented HTTP access.
36
+ - Add a default-off managed contract for eight existing capabilities, including
37
+ project CLI overrides, compatibility-aware status/digests, exact MCP tool
38
+ omission, guarded installed workflows, and separately consented payload-free
39
+ local callback records.
40
+ - Add a default-off, adapter-driven two-pass evaluation harness with immutable
41
+ task sets, paired seeds, confidence intervals, per-capability ablations,
42
+ deterministic offline fixtures, bounded reports, and exact-source runtime
43
+ evidence without claiming an unmeasured quality improvement.
44
+ - Add a design-only meta-agent contract describing how future configuration
45
+ proposals could be evaluated through the existing harness without giving
46
+ Cairnkeep ownership of an agent or inference loop.
47
+
48
+ ### Fixed
49
+
50
+ - Load the native OpenCode capability plugin through its supported single-export
51
+ shape, run its coordinator with Node under OpenCode's Bun host, accept the
52
+ pinned runtime event envelope, and settle terminal callbacks before the host
53
+ can exit.
54
+ - Layer `CAIRN_EXTRA_SETTINGS` through OpenCode's supported
55
+ `OPENCODE_CONFIG` environment seam and keep headless graph commands in the
56
+ tool-calling path until their real owner operation completes.
57
+ - Pin the Claude Code 2.1.220 `PostCompact` `prompt_id` payload shape alongside
58
+ the retained 2.1.219 adapter while continuing to refuse unknown versions.
59
+ - Preserve redacted Pi provider failures as trajectory system events and treat
60
+ a closed trajectory CLI output pipe as a normal consumer exit.
61
+ - Make the audit script executable so its rendered systemd service target can
62
+ run directly.
63
+ - Bind evaluation note distillation to the admitted task workspace so nested
64
+ task trajectories cannot be missed or replaced by a source-root fallback.
65
+ - Upgrade the MCP SDK to 1.30.0 so both package graphs use the upstream-owned,
66
+ patched Hono transport and report zero production vulnerabilities.
67
+
68
+ Trajectory/compaction capture, artifact access, and note distillation/enrichment remain disabled by default. Existing
69
+ MCP tools, memory database paths, and remote HTTP behavior are unchanged. No
70
+ memory-quality or efficiency improvement is claimed before the evaluation
71
+ harness measures it.
72
+
6
73
  ## [2.3.1] - 2026-07-25
7
74
 
8
75
  ### Fixed
@@ -129,6 +196,8 @@ stored memories.
129
196
  - Preserve executable permissions and Bash 3.2 portability on macOS.
130
197
  - Add backup-first uninstall and SQLite-safe memory export/import guidance.
131
198
 
199
+ [Unreleased]: https://github.com/cairnkeep/cairnkeep/compare/v2.4.0...HEAD
200
+ [2.4.0]: https://github.com/cairnkeep/cairnkeep/compare/v2.3.1...v2.4.0
132
201
  [2.3.1]: https://github.com/cairnkeep/cairnkeep/compare/v2.3.0...v2.3.1
133
202
  [2.3.0]: https://github.com/cairnkeep/cairnkeep/compare/v2.2.1...v2.3.0
134
203
  [2.2.1]: https://github.com/cairnkeep/cairnkeep/compare/v2.2.0...v2.2.1
package/README.md CHANGED
@@ -9,15 +9,16 @@
9
9
  A *cairn* is a stack of stones left as a trail marker for whoever follows; a
10
10
  *keep* is where you store what matters. **Cairnkeep** is where coding agents
11
11
  stack durable memory — decisions, pitfalls, patterns — and follow the trail
12
- across sessions, projects, and harnesses (Claude Code, OpenCode, …).
12
+ across sessions, projects, and harnesses (Claude Code, OpenCode, Pi, …).
13
13
 
14
14
  ## Status
15
15
 
16
- Shipped: the memory server, the `cairn` CLI (`bootstrap`, `memory-server`, `sync`,
17
- `doctor`, `memory`, `audit-timer`, `completion`, `uninstall`) installable via
16
+ Shipped: the memory server, the `cairn` CLI (`bootstrap`, `memory-server`, `sync`, `sync-pi`,
17
+ `doctor`, `trajectory`, `artifact`, `notes`, `memory`, `eval`, `audit-timer`, `completion`, `uninstall`) installable via
18
18
  `npm i -g @cairnkeep/cli`, and the
19
19
  operating layer (commands,
20
- agents, hooks) installed on both Claude Code and OpenCode. The generic launchers
20
+ agents, hooks) installed on Claude Code and OpenCode, plus a native Pi
21
+ trajectory extension. The generic launchers
21
22
  expose wrapper seams (`.ai/pre-launch.sh`, `CAIRN_EXTRA_SETTINGS`,
22
23
  `.ai/post-exit.sh`) so an enterprise wrapper can add provider/credential setup
23
24
  without forking them. Also shipped: context exploration (`/context-explore`) and
@@ -35,6 +36,7 @@ and 20 are end-of-life upstream.
35
36
  |---|---|
36
37
  | Claude Code on Linux/macOS | Memory server plus commands, agents, hooks, and launchers |
37
38
  | OpenCode on Linux/macOS | Memory server plus commands, plugins, hooks, and launchers |
39
+ | Pi on Linux/macOS | Native opt-in trajectory extension and launcher; no bundled MCP bridge |
38
40
  | Codex CLI | Memory MCP server; no Cairnkeep operating-layer assets |
39
41
  | Other MCP clients | Memory and optional domain-knowledge MCP tools |
40
42
  | Native Windows | Not supported by the Bash-based installer; use WSL (not yet CI-verified) |
@@ -67,10 +69,17 @@ storage paths, secrets, and private derived images, see
67
69
  embedding-ranked search against any OpenAI-compatible endpoint.
68
70
  - **`bin/cairn`** — the CLI. `cairn bootstrap [path]` scaffolds a project's
69
71
  `.ai/` launchers + env; `cairn doctor` health-checks the configured pieces;
72
+ `cairn trajectory list|show|prune` manages opt-in local session trajectories;
73
+ `cairn artifact list|show|delete|prune` manages opt-in local artifacts;
74
+ `cairn eval validate|run|ablate|report|prune|delete` coordinates explicit
75
+ local two-pass and one-capability-at-a-time measurements;
76
+ `cairn notes distill|search-error|promote|doctor` compiles and searches
77
+ default-off local hindsight notes outside the online agent path;
70
78
  `cairn memory export|import` relocates the durable store between machines
71
79
  (`export` requires the optional `sqlite3` CLI);
72
80
  `cairn audit-timer` installs the scheduled memory+wiki audit;
73
81
  `cairn completion bash|zsh|fish` generates shell completion definitions; and
82
+ `cairn sync-pi` installs the native Pi trajectory extension;
74
83
  `cairn uninstall` reverses the install (backup-first, revertible).
75
84
  - **`templates/`** — project scaffolding (generic launchers, env) plus the
76
85
  derived-knowledge layer (wiki, alignment, graph, security, planning).
@@ -139,6 +148,35 @@ patterns/transactional-migrations: Use transactional migrations for schema chang
139
148
  The exact command rendering depends on the client. Any MCP client can call
140
149
  `memory_write` and `memory_search` directly.
141
150
 
151
+ For Pi, install the local trajectory adapter and use the scaffolded launcher:
152
+
153
+ ```bash
154
+ cairn sync-pi --apply
155
+ cairn bootstrap /path/to/project
156
+ cd /path/to/project && ./.ai/start-pi.sh
157
+ ```
158
+
159
+ This installs trajectory capture only. Cairnkeep does not bundle or select a Pi
160
+ MCP bridge; configure a user-chosen bridge separately if you also want the MCP
161
+ memory tools inside Pi.
162
+
163
+ Closed trajectories can be compiled into local hindsight notes without a model
164
+ or embedding service. Capture and distillation are separate opt-ins:
165
+
166
+ ```bash
167
+ export CAIRN_TRAJECTORY_CAPTURE=1 # affects future closed sessions
168
+ export CAIRN_NOTE_DISTILLATION=1
169
+ cairn notes distill --project /path/to/project --json
170
+ printf '%s\n' 'TypeError: example' | cairn notes search-error --project /path/to/project --json
171
+ ```
172
+
173
+ Notes keep deterministic failure, resolution, abandonment, and recurrence
174
+ history under `~/.cairnkeep/notes/`. Shared promotion is never automatic: it
175
+ requires compatible evidence from two distinct projects and an explicit
176
+ `cairn notes promote NOTE-ID --with NOTE-ID --confirm`. See the
177
+ [operating guide](docs/operating.md#hindsight-notes-opt-in) before enabling
178
+ optional model enrichment.
179
+
142
180
  Prefer working from a clone? Build the server with `cd mcp-memory-server && npm
143
181
  install && npm run build`, then use `scripts/sync-claude-assets.sh` and
144
182
  `bin/cairn` in place of the installed `cairn`.
@@ -159,8 +197,9 @@ unless a wrapper opts in:
159
197
  export env (e.g. a provider base URL) or abort the launch by returning
160
198
  non-zero. This is where credential refresh / connectivity setup lives.
161
199
  - **`CAIRN_EXTRA_SETTINGS`** — path to a settings file layered onto the harness
162
- (`--settings` for Claude Code, `--config` for OpenCode). Process env still
163
- wins, so an inline value beats the profile.
200
+ (`--settings` for Claude Code, `OPENCODE_CONFIG` for OpenCode). Pi has no equivalent
201
+ generic settings-file flag, so its launcher leaves this variable unused.
202
+ Process env still wins, so an inline value beats the profile.
164
203
  - **`.ai/post-exit.sh`** — sourced after the harness exits, with
165
204
  `CAIRN_EXIT_STATUS` set to its exit code.
166
205
 
@@ -188,6 +227,37 @@ search):
188
227
  | `CAIRN_MEMORY_EMBEDDING_MODEL` | Embedding model name (required for semantic search) |
189
228
  | `CAIRN_MEMORY_EMBEDDING_TIMEOUT_MS` | Embedding request timeout before substring fallback (default `15000`) |
190
229
  | `CAIRN_AGENTFS_BASE_DIR` | Base dir for global memory scopes (default `~/.cairnkeep`) |
230
+ | `CAIRN_TRAJECTORY_CAPTURE` | Opt in to local structured session capture (`1`, `true`, `yes`, or `on`; default off) |
231
+ | `CAIRN_TRAJECTORY_SESSION_MAX_BYTES` | Maximum serialized bytes per captured session (default `5242880`, 5 MiB) |
232
+ | `CAIRN_TRAJECTORY_STORE_MAX_BYTES` | Maximum logical bytes across local trajectories (default `268435456`, 256 MiB) |
233
+ | `CAIRN_TRAJECTORY_RETENTION_DAYS` | Retain captured sessions for this many days (default `30`) |
234
+ | `CAIRN_TRAJECTORY_REDACTION_FILE` | Optional project-contained redaction config (default `.ai/trajectory-redaction.json` when present) |
235
+ | `CAIRN_REDACTION_FILE` | Optional project-contained redaction config shared by trajectories and artifacts (falls back to `CAIRN_TRAJECTORY_REDACTION_FILE`) |
236
+ | `CAIRN_COMPACTION_CAPTURE` | Opt in to local harness-produced compaction capture and fresh-session recovery (off by default) |
237
+ | `CAIRN_ARTIFACT_STORE` | Expose four local stdio artifact tools (off by default; independent of compaction capture) |
238
+ | `CAIRN_ARTIFACT_HTTP` | Separately consent to artifact tools over authenticated HTTP (off by default; also requires `CAIRN_ARTIFACT_STORE`) |
239
+ | `CAIRN_ARTIFACT_MAX_BYTES` | Maximum stored bytes per artifact (default `1048576`, 1 MiB) |
240
+ | `CAIRN_ARTIFACT_SESSION_MAX_BYTES` | Maximum logical bytes per artifact session (default `16777216`, 16 MiB) |
241
+ | `CAIRN_ARTIFACT_STORE_MAX_BYTES` | Maximum logical bytes in one artifact store (default `268435456`, 256 MiB) |
242
+ | `CAIRN_ARTIFACT_RETENTION_DAYS` | Artifact age retention (default `30`) |
243
+ | `CAIRN_COMPACTION_MAX_REVISIONS` | Retained compaction revisions per session (default `8`) |
244
+ | `CAIRN_ARTIFACT_GENERATED_FILE_SNAPSHOT_MAX_BYTES` | Generated-file inline snapshot cap (default `262144`, 256 KiB; lower if the artifact cap is lower) |
245
+ | `CAIRN_NOTE_DISTILLATION` | Opt in to one-shot/scheduled local note distillation and lookup (default off) |
246
+ | `CAIRN_NOTE_ENRICHMENT` | Separately opt in to remote/local prose enrichment; never implied by credentials (default off) |
247
+ | `CAIRN_NOTE_ENRICHMENT_MODEL` | Explicit chat model for optional note enrichment (no default) |
248
+ | `CAIRN_NOTE_ENRICHMENT_TIMEOUT_MS` | Optional enrichment timeout (default `15000`) |
249
+ | `CAIRN_TYPED_MEMORY_NODES` | Opt in to typed metadata, filters, logical note address spaces, and `memory_import` (default off; restart the server after changing) |
250
+ | `CAIRN_CAPABILITY_CONTRACT` | Opt in to the managed eight-capability contract (default off) |
251
+ | `CAIRN_CAPABILITY_LOGGING` | Override payload-free local callback logging (`1`/`0`; default off) |
252
+ | `CAIRN_CAPABILITY_MEMORY_WRITE` | Override `memory.write` for this process |
253
+ | `CAIRN_CAPABILITY_MEMORY_SEARCH` | Override `memory.search` for this process |
254
+ | `CAIRN_CAPABILITY_NOTES_DISTILL` | Override `notes.distill` for this process |
255
+ | `CAIRN_CAPABILITY_WIKI` | Override `wiki` for this process |
256
+ | `CAIRN_CAPABILITY_GRAPH` | Override `graph` for this process |
257
+ | `CAIRN_CAPABILITY_SECURITY_AUDIT` | Override `security.audit` for this process |
258
+ | `CAIRN_CAPABILITY_ROUTE_CHECK` | Override `route.check` for this process |
259
+ | `CAIRN_CAPABILITY_CONTEXT_EXPLORE` | Override `context.explore` for this process |
260
+ | `CAIRN_EVAL` | Opt in to the local evaluation coordinator (default off; does not select a harness, model, endpoint, credential, or network) |
191
261
  | `CAIRN_GIT_PROVIDER` | Git host for collaboration commands: `github`\|`gitlab`\|`codeberg`\|`forgejo`\|`none` ([docs/git-providers.md](docs/git-providers.md)) |
192
262
  | `CAIRN_ROUTE_ENDPOINT` | Base URL of an already-running token-miser routing/tiering proxy (unset → `route_check` is inert) |
193
263
  | `CAIRN_EXPLORE_BINARY` | Absolute path to the `token_miser` binary used by `context_explore` (unset → the tool throws) |
@@ -203,6 +273,151 @@ remote memory, and delegated exploration can send content to endpoints you
203
273
  configure. Review [Privacy and data flow](docs/privacy-and-data-flow.md) before
204
274
  enabling them.
205
275
 
276
+ ### Managed capability contract (opt-in)
277
+
278
+ `CAIRN_CAPABILITY_CONTRACT=1` enables one versioned, typed boundary around the
279
+ existing capability owners. The master flag and callback logging are both off
280
+ by default. With the master flag unset, MCP registration, installed
281
+ Markdown/workflow bytes, commands, processes, configuration reads, logging,
282
+ output, and performance remain on the legacy path.
283
+
284
+ | Capability ID | Kind | Existing owner | Compatibility default | Process override |
285
+ |---|---|---|---:|---|
286
+ | `memory.write` | MCP tool | `memory_write` | on | `CAIRN_CAPABILITY_MEMORY_WRITE` |
287
+ | `memory.search` | MCP tool | `memory_search` | on | `CAIRN_CAPABILITY_MEMORY_SEARCH` |
288
+ | `notes.distill` | offline job | note distillation CLI/timer | off | `CAIRN_CAPABILITY_NOTES_DISTILL` |
289
+ | `wiki` | operating workflow | installed wiki commands/workflows | on | `CAIRN_CAPABILITY_WIKI` |
290
+ | `graph` | operating workflow | installed graph commands | inherited from `graphify.enabled`, otherwise off | `CAIRN_CAPABILITY_GRAPH` |
291
+ | `security.audit` | operating workflow | installed security commands/workflows | on | `CAIRN_CAPABILITY_SECURITY_AUDIT` |
292
+ | `route.check` | MCP tool | existing token-miser routing delegate | on | `CAIRN_CAPABILITY_ROUTE_CHECK` |
293
+ | `context.explore` | MCP tool | existing token-miser exploration delegate | on | `CAIRN_CAPABILITY_CONTEXT_EXPLORE` |
294
+
295
+ Manage project overrides in `.ai/capabilities.json` through the CLI rather than
296
+ editing JSON:
297
+
298
+ ```bash
299
+ CAIRN_CAPABILITY_CONTRACT=1 cairn capabilities list
300
+ CAIRN_CAPABILITY_CONTRACT=1 cairn capabilities status --json
301
+ CAIRN_CAPABILITY_CONTRACT=1 cairn capabilities disable context.explore
302
+ CAIRN_CAPABILITY_CONTRACT=1 cairn capabilities reset context.explore
303
+ CAIRN_CAPABILITY_CONTRACT=1 cairn capabilities logging enable
304
+ ```
305
+
306
+ Process overrides take precedence over project values, which take precedence
307
+ over compatibility defaults. `reset` removes an explicit override and restores
308
+ the inherited state; it does not mean enabled. Changes to the four MCP
309
+ capabilities (`memory.write`, `memory.search`, `route.check`, and
310
+ `context.explore`) require a fresh memory-server start and disabled tools are
311
+ omitted from `tools/list`. Offline and operating workflows resolve state at
312
+ each invocation. Enabling the master does not enable `notes.distill` or graph
313
+ when their compatibility inputs remain off.
314
+
315
+ Callback records are local, payload-free operating evidence, not evaluation
316
+ results or telemetry. They require the contract, callback logging, and local
317
+ trajectory capture to be enabled together. Cairnkeep makes no capability gain,
318
+ memory-quality, or efficiency claim from this instrumentation; Phase 19 must
319
+ first create an explicit all-enabled baseline and measure one-disabled states.
320
+ See the [operating guide](docs/operating.md#managed-capability-contract-opt-in),
321
+ [storage contract](docs/storage.md#capability-callback-storage), and
322
+ [privacy flow](docs/privacy-and-data-flow.md#capability-callback-flow).
323
+
324
+ ### Evaluation harness (opt-in, measurement only)
325
+
326
+ `CAIRN_EVAL=1` enables `cairn eval` for a committed schema-v1 task set and an
327
+ explicit operator-owned adapter program. `validate` resolves immutable inputs,
328
+ limits, commands, digests, output containment, and a deterministic serial
329
+ schedule without invoking the adapter. `run` measures from-scratch Run 1 versus
330
+ a fresh Run 2 that can see only its own task's immutable note snapshot.
331
+ `ablate` measures an explicit all-eight-capabilities-on baseline against one
332
+ exactly-one-disabled treatment, with both passes in both arms. Execution prints
333
+ the invocation estimate and requires `--yes`; there is no retry, inference,
334
+ provider, endpoint, credential, model, or network default.
335
+
336
+ The canonical local JSON report records optional reported turns/tokens/cost,
337
+ independently verified pass state, full and note-eligible populations,
338
+ missingness, paired deltas, capability/notes/revision digests, and seeded
339
+ bootstrap uncertainty. Turns compare only under exact matching turn-semantics
340
+ compatibility IDs; token totals are never inferred and unknown verifier results
341
+ are never counted as pass or fail. Reports describe estimates and
342
+ inconclusive/small-sample conditions; they do not claim causality,
343
+ significance, quality change, or efficiency change.
344
+
345
+ The bundled fake task set and adapter are deterministic, offline framework
346
+ fixtures. They cannot support a Cairnkeep performance statement. Any future
347
+ efficiency statement requires a separate committed `live-evaluation` artifact
348
+ whose source, task-set, report, schema, note-snapshot, missingness, runtime, and
349
+ claim anchors all pass `scripts/verify-phase19-runtime-evidence.mjs`. No such
350
+ live claim or evidence artifact is shipped here, and external paper calibration
351
+ is neither a Cairnkeep result nor an acceptance target.
352
+
353
+ Reports and note snapshots stay under
354
+ `.agentfs/eval/experiments/<experiment-id>/`; default uninstall retains them,
355
+ while explicit purge is backup-first and revertible. Cancellation retains an
356
+ inspectable partial report. POSIX process-group cleanup is tested; Windows
357
+ descendant-process-tree termination is not guaranteed. See the
358
+ [operating guide](docs/operating.md#evaluation-harness-opt-in),
359
+ [storage contract](docs/storage.md#evaluation-report-and-note-snapshot-storage),
360
+ and [privacy flow](docs/privacy-and-data-flow.md#evaluation-adapter-and-report-flow).
361
+
362
+ ### Typed memory nodes and structured import (opt-in)
363
+
364
+ `CAIRN_TYPED_MEMORY_NODES=1` adds schema-v1 `node_type`, canonical `tags`,
365
+ `address_space`, hard list/search filters (`node_types`, `tags_all`,
366
+ `tags_any`), and exactly one new MCP tool: `memory_import`. The flag is read
367
+ when each server/session is created, so restart after changing it. Unset keeps
368
+ the existing 14 tools, schemas, response roots, raw values, files, processes,
369
+ network behavior, and output unchanged. It adds no read/edit/update/delete
370
+ aliases; `memory_supersede` remains the sole edit operation.
371
+
372
+ Core types are `memory`, `knowledge`, `hindsight`, `shared`, and `provenance`;
373
+ extensions use a namespaced form such as `team:runbook`. Tags are ASCII
374
+ lowercase, with whitespace and underscores normalized to hyphens, deduplicated,
375
+ and sorted. Filters are applied before ranking. Exact key/tag/type hits precede
376
+ semantic results; without a working configured embedding endpoint, search uses
377
+ stable local substring matching.
378
+
379
+ `memory_import` accepts one inline schema-v1 batch for one concrete scope and
380
+ one `memory`, `project-notes`, or `shared-notes` address. It supports 1–256
381
+ unique relative keys, values up to 256 KiB each and 5 MiB total, optional
382
+ `import_id`, `dry_run`, and `conflict_policy: reject|supersede`. Results contain
383
+ only digests, counts, keys, and actions—never supplied values. The default
384
+ reject policy cannot overwrite; explicit supersede preserves typed history.
385
+ Note addresses require `scope: project` and logical keys, never filesystem
386
+ paths. See the [operating guide](docs/operating.md#typed-memory-nodes-and-note-address-spaces-opt-in).
387
+
388
+ ### Compaction continuity and artifacts (opt-in)
389
+
390
+ `CAIRN_COMPACTION_CAPTURE=1` captures only harness-produced compaction summaries
391
+ from the pinned Claude Code `PostCompact` 2.1.219 and 2.1.220 payloads and
392
+ OpenCode `session.compacted` 1.17.20 seam. It stores redacted immutable revisions locally and injects the latest
393
+ valid structured goals, decisions, TODOs, and critical errors when a session
394
+ starts. A resumed session is preferred; a fresh session falls back to the
395
+ newest valid project summary and marks stale context for validation. The raw
396
+ redacted summary is available only through an explicit read. Cairnkeep never
397
+ generates or triggers compaction.
398
+
399
+ `CAIRN_ARTIFACT_STORE=1` independently exposes exactly four stdio MCP tools:
400
+ `artifact_write`, `artifact_read`, `artifact_list`, and `artifact_delete`.
401
+ The initial kinds are exactly `compaction_summary`, `diff`, `test_output`, and
402
+ `generated_file`. Generated-file paths are labels only; the server never reads
403
+ them. Operators can inspect retained local data whether or not MCP writes are
404
+ currently enabled:
405
+
406
+ ```bash
407
+ cairn artifact list --kind compaction_summary --json
408
+ cairn artifact show ARTIFACT-ID --json
409
+ cairn artifact delete ARTIFACT-ID --dry-run --json
410
+ cairn artifact prune --dry-run --include-protected --json
411
+ ```
412
+
413
+ Both capabilities are default-off, local, and require no key, model, endpoint,
414
+ telemetry, or network. Remote tools require double consent:
415
+ `CAIRN_ARTIFACT_STORE=1` plus `CAIRN_ARTIFACT_HTTP=1`, as well as the existing
416
+ bearer, `Host`, and validated `X-Cairn-Project` controls. Artifact HTTP never
417
+ exposes trajectory data. See the [operating guide](docs/operating.md#compaction-continuity-and-artifacts-opt-in),
418
+ [storage contract](docs/storage.md#artifact-storage), and
419
+ [privacy flows](docs/privacy-and-data-flow.md#compaction-and-artifact-flows).
420
+
206
421
  ## More
207
422
 
208
423
  - **Guided learning paths and video scripts** — [docs/learning/README.md](docs/learning/README.md)
package/bin/cairn CHANGED
@@ -32,8 +32,30 @@ Usage:
32
32
  Install/update the Claude Code operating layer (commands,
33
33
  agents, hooks) into ~/.claude, or into DIR for a single
34
34
  project (--live-root <project>/.claude).
35
+ cairn sync-pi [--apply] [--live-root DIR]
36
+ Install/update the Pi trajectory extension into
37
+ ~/.pi/agent, or into DIR for an isolated Pi root.
35
38
  cairn doctor Health-check the memory server and configured LLM /
36
- embedding endpoints, git provider, and store.
39
+ embedding endpoints, git provider, and stores.
40
+ Use --repair for derived trajectory/typed metadata,
41
+ indexes, and interrupted note transactions.
42
+ cairn trajectory <list|show|prune>
43
+ Inspect and prune opt-in local session trajectories.
44
+ cairn artifact <list|show|delete|prune>
45
+ Inspect and delete opt-in local artifacts. List/show
46
+ support --json; delete supports --dry-run; prune
47
+ supports --dry-run and --include-protected.
48
+ cairn capabilities <list|status|enable|disable|reset|logging>
49
+ Inspect and manage project capability state. MCP changes
50
+ require a memory-server restart; operating changes apply
51
+ on the next invocation.
52
+ cairn notes <distill|search-error|promote|doctor>
53
+ Compile closed trajectories into local hindsight notes,
54
+ search exact failure signatures, promote corroborated
55
+ notes, or validate the note store.
56
+ cairn eval <validate|run|ablate|report|prune|delete>
57
+ Validate or run default-off local evaluations, inspect
58
+ reports, and manage retained experiment results.
37
59
  cairn memory <path|export|import>
38
60
  Relocate global-scope memory. `path` prints its location;
39
61
  `export <file.tgz>` takes a WAL-safe snapshot (requires
@@ -41,7 +63,8 @@ Usage:
41
63
  cairn audit-timer [--on-calendar SPEC] [--para-root PATH] [--render-only DIR]
42
64
  Install (opt-in) a systemd user timer that runs the
43
65
  memory+wiki audit on a schedule; prints a cron alternative.
44
- cairn uninstall [--dry-run] [--yes] [--purge-memory] [--live-root DIR] [PROJECT ...]
66
+ cairn uninstall [--dry-run] [--yes] [--purge-memory] [--live-root DIR]
67
+ [--pi-live-root DIR] [PROJECT ...]
45
68
  Reverse what cairnkeep installed/modified (operating
46
69
  layer, hook registrations, MCP registration, audit
47
70
  timer, and any given projects' bootstrap scaffold).
@@ -72,9 +95,27 @@ case "$cmd" in
72
95
  # claude mcp add cairn-memory -s user -- cairn memory-server
73
96
  exec node "$CAIRN_ROOT/mcp-memory-server/dist/index.js" "$@"
74
97
  ;;
98
+ trajectory)
99
+ exec node "$CAIRN_ROOT/mcp-memory-server/dist/trajectory-cli.js" "$@"
100
+ ;;
101
+ artifact)
102
+ exec node "$CAIRN_ROOT/mcp-memory-server/dist/artifact-cli.js" "$@"
103
+ ;;
104
+ capabilities)
105
+ exec node "$CAIRN_ROOT/mcp-memory-server/dist/capability-cli.js" "$@"
106
+ ;;
107
+ notes)
108
+ exec node "$CAIRN_ROOT/mcp-memory-server/dist/note-cli.js" "$@"
109
+ ;;
110
+ eval)
111
+ exec node "$CAIRN_ROOT/mcp-memory-server/dist/eval-cli.js" "$@"
112
+ ;;
75
113
  sync)
76
114
  exec "$CAIRN_ROOT/scripts/sync-claude-assets.sh" "$@"
77
115
  ;;
116
+ sync-pi)
117
+ exec "$CAIRN_ROOT/scripts/sync-pi-assets.sh" "$@"
118
+ ;;
78
119
  audit-timer)
79
120
  exec "$CAIRN_ROOT/scripts/install-audit-timer.sh" "$@"
80
121
  ;;
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env bash
2
+ # Stop/StopFailure/CwdChanged/SessionEnd hook: lifecycle settlement transport.
3
+ set -euo pipefail
4
+
5
+ case "${CAIRN_CAPABILITY_CONTRACT:-}" in
6
+ 1|true|TRUE|yes|YES|on|ON) ;;
7
+ *) exit 0 ;;
8
+ esac
9
+
10
+ INFRA_ROOT="@@INFRA_ROOT@@"
11
+ COORDINATOR="$INFRA_ROOT/mcp-memory-server/dist/capability-cli.js"
12
+ [ -f "$COORDINATOR" ] || exit 0
13
+
14
+ input=$(cat) || exit 0
15
+ validated=$(printf '%s' "$input" | python3 /dev/fd/3 3<<'PY'
16
+ import json
17
+ import re
18
+ import sys
19
+
20
+ common = {"session_id", "transcript_path", "cwd", "hook_event_name"}
21
+ optional = {"prompt_id", "permission_mode", "effort", "agent_id", "agent_type"}
22
+ shapes = {
23
+ "Stop": ({"stop_hook_active", "last_assistant_message", "background_tasks", "session_crons"}, set()),
24
+ "StopFailure": ({"error"}, {"error_details", "last_assistant_message"}),
25
+ "CwdChanged": ({"old_cwd", "new_cwd"}, set()),
26
+ "SessionEnd": ({"reason"}, set()),
27
+ }
28
+ errors = {
29
+ "rate_limit", "overloaded", "authentication_failed", "oauth_org_not_allowed",
30
+ "billing_error", "invalid_request", "model_not_found", "server_error",
31
+ "max_output_tokens", "unknown",
32
+ }
33
+ reasons = {"clear", "resume", "logout", "prompt_input_exit", "bypass_permissions_disabled", "other"}
34
+
35
+ try:
36
+ value = json.load(sys.stdin)
37
+ if not isinstance(value, dict) or not common <= set(value):
38
+ raise ValueError
39
+ event = value["hook_event_name"]
40
+ if event not in shapes:
41
+ raise ValueError
42
+ event_required, event_optional = shapes[event]
43
+ required = common | event_required
44
+ if not required <= set(value) or set(value) - required - optional - event_optional:
45
+ raise ValueError
46
+ for name in ("session_id", "transcript_path", "cwd"):
47
+ if not isinstance(value[name], str):
48
+ raise ValueError
49
+ session = value["session_id"]
50
+ if not isinstance(session, str) or not re.fullmatch(r"(?:cairn:)?[A-Za-z0-9][A-Za-z0-9._:-]{0,255}", session) or session == "unknown":
51
+ raise ValueError
52
+ if event == "Stop":
53
+ if not isinstance(value["stop_hook_active"], bool) or not isinstance(value["last_assistant_message"], str):
54
+ raise ValueError
55
+ if not isinstance(value["background_tasks"], list) or not isinstance(value["session_crons"], list):
56
+ raise ValueError
57
+ operation, body = "harness-terminal", {"outcome": "success"}
58
+ elif event == "StopFailure":
59
+ if value["error"] not in errors:
60
+ raise ValueError
61
+ operation, body = "harness-terminal", {"outcome": "error"}
62
+ elif event == "SessionEnd":
63
+ if value["reason"] not in reasons:
64
+ raise ValueError
65
+ operation, body = "harness-terminal", {"outcome": "abandoned"}
66
+ else:
67
+ if not isinstance(value["old_cwd"], str) or not isinstance(value["new_cwd"], str):
68
+ raise ValueError
69
+ operation, body = "harness-cwd", {"old_cwd": value["old_cwd"], "new_cwd": value["new_cwd"]}
70
+ body.update({"schema_version": 1, "harness": "claude-code", "session_id": session})
71
+ print(operation)
72
+ print(json.dumps(body, separators=(",", ":")))
73
+ except Exception:
74
+ sys.exit(1)
75
+ PY
76
+ ) || exit 0
77
+
78
+ operation=${validated%%$'\n'*}
79
+ payload=${validated#*$'\n'}
80
+ printf '%s' "$payload" | node "$COORDINATOR" "$operation" >/dev/null 2>&1 || true
81
+ exit 0
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env bash
2
+ # UserPromptExpansion hook: deterministic operating-capability admission.
3
+ set -euo pipefail
4
+
5
+ case "${CAIRN_CAPABILITY_CONTRACT:-}" in
6
+ 1|true|TRUE|yes|YES|on|ON) ;;
7
+ *) printf '{}\n'; exit 0 ;;
8
+ esac
9
+
10
+ INFRA_ROOT="@@INFRA_ROOT@@"
11
+ COORDINATOR="$INFRA_ROOT/mcp-memory-server/dist/capability-cli.js"
12
+ FIXED_BLOCK='{"decision":"block","reason":"capability disabled"}'
13
+
14
+ block() {
15
+ printf '%s\n' "$FIXED_BLOCK"
16
+ exit 2
17
+ }
18
+
19
+ [ -f "$COORDINATOR" ] || block
20
+ input=$(cat) || block
21
+
22
+ validated=$(printf '%s' "$input" | python3 /dev/fd/3 "$PWD" 3<<'PY'
23
+ import json
24
+ import os
25
+ import re
26
+ import sys
27
+
28
+ required = {
29
+ "session_id", "transcript_path", "cwd", "hook_event_name",
30
+ "expansion_type", "command_name", "command_args", "command_source", "prompt",
31
+ }
32
+ optional = {"prompt_id", "permission_mode", "effort", "agent_id", "agent_type"}
33
+ commands = {"wiki-ingest", "wiki-query", "wiki-lint", "graphify", "security-audit"}
34
+
35
+ try:
36
+ value = json.load(sys.stdin)
37
+ if not isinstance(value, dict) or set(value) - required - optional or not required <= set(value):
38
+ raise ValueError
39
+ if value["hook_event_name"] != "UserPromptExpansion":
40
+ raise ValueError
41
+ if value["expansion_type"] not in {"slash_command", "mcp_prompt"}:
42
+ raise ValueError
43
+ if value["command_name"] not in commands:
44
+ raise ValueError
45
+ for name in required - {"hook_event_name", "expansion_type"}:
46
+ if not isinstance(value[name], str):
47
+ raise ValueError
48
+ session = value["session_id"]
49
+ if not isinstance(session, str) or not re.fullmatch(r"(?:cairn:)?[A-Za-z0-9][A-Za-z0-9._:-]{0,255}", session) or session == "unknown":
50
+ raise ValueError
51
+ event_root = os.path.realpath(value["cwd"])
52
+ process_root = os.path.realpath(sys.argv[1])
53
+ if not os.path.isabs(value["cwd"]) or event_root != process_root or not os.path.isdir(event_root):
54
+ raise ValueError
55
+ print(json.dumps({
56
+ "schema_version": 1,
57
+ "harness": "claude-code",
58
+ "command": value["command_name"],
59
+ "session_id": session,
60
+ "project_root": event_root,
61
+ }, separators=(",", ":")))
62
+ except Exception:
63
+ sys.exit(1)
64
+ PY
65
+ ) || block
66
+
67
+ decision=$(printf '%s' "$validated" | node "$COORDINATOR" harness-before 2>/dev/null) || {
68
+ printf '{}\n'
69
+ exit 0
70
+ }
71
+
72
+ case "$decision" in
73
+ '{"schema_version":1,"decision":"block","reason":"capability-disabled"}') block ;;
74
+ '{"schema_version":1,"decision":"allow"}') printf '{}\n' ;;
75
+ *) printf '{}\n' ;;
76
+ esac
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env bash
2
+ # PostCompact hook: persist the harness-produced compaction summary locally.
3
+ # The hook never generates or changes a summary and always fails open.
4
+ set -euo pipefail
5
+
6
+ case "${CAIRN_COMPACTION_CAPTURE:-}" in
7
+ 1|true|TRUE|yes|YES|on|ON) ;;
8
+ *) exit 0 ;;
9
+ esac
10
+
11
+ repo="$(pwd)"
12
+ ARTIFACT_ENTRY="@@INFRA_ROOT@@/mcp-memory-server/dist/artifact-cli.js"
13
+ [ -f "$ARTIFACT_ENTRY" ] || exit 0
14
+
15
+ # The payload has no version field. Resolve the running harness locally so an
16
+ # unpinned future shape cannot be accepted merely because its keys look known.
17
+ harness_version=$(claude --version 2>/dev/null | sed -nE 's/[^0-9]*([0-9]+\.[0-9]+\.[0-9]+).*/\1/p' | head -1)
18
+ [ -n "$harness_version" ] || harness_version=unknown
19
+
20
+ # Forward the documented PostCompact payload unchanged. The local CLI owns
21
+ # shape validation, redaction, and persistence; this boundary stays bounded
22
+ # and cannot block or fail the harness event.
23
+ node "$ARTIFACT_ENTRY" capture-claude "$repo" --harness-version "$harness_version" <&0 >/dev/null 2>&1 &
24
+ capture_pid=$!
25
+ (
26
+ sleep 3
27
+ kill -KILL "$capture_pid" >/dev/null 2>&1 || true
28
+ ) &
29
+ watchdog_pid=$!
30
+ wait "$capture_pid" >/dev/null 2>&1 || true
31
+ kill "$watchdog_pid" >/dev/null 2>&1 || true
32
+ wait "$watchdog_pid" >/dev/null 2>&1 || true
33
+
34
+ exit 0