@misterhuydo/cairn-mcp 1.17.0 → 1.19.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.
package/README.md CHANGED
@@ -50,10 +50,10 @@ server load.
50
50
  | Hook | Trigger | Effect |
51
51
  |---|---|---|
52
52
  | `PreToolUse[Read]` | Every file read | Source files compressed ~68% before Claude sees them; large files show structural outline with **line numbers** to save tokens |
53
- | `PreToolUse[Edit]` | Every file edit | Blocks Edit if Claude only saw compressed content — requires a full re-read first |
53
+ | `PreToolUse[Edit]` | Every file edit | Blocks Edit if Claude only saw compressed content — requires a full re-read first. Reported as a permission decision with a one-line reason, **not** as a red hook error: being sent back to re-read a file is a normal step, and the full source rides along out of sight so it does not fill the screen |
54
54
  | `PostToolUse[ExitPlanMode]` | A plan is approved | The plan is parsed into `### Phase N` entries in `.cairn/roadmap.md`, synced, and the cursor activated |
55
55
  | `PostToolUse[TodoWrite]` | The todo list changes | Todos mirror onto the roadmap as sub-tasks under the current phase (or seed root phases if none exist) |
56
- | `Stop` | End of every response | Session auto-saved to `.cairn/session.json`, Claude auto-memory backed up to `.cairn/memory/`, the roadmap re-surfaced, and any git-detected completed phases surfaced for you to confirm — all in one line |
56
+ | `Stop` | End of every response | Session auto-saved to `.cairn/session.json`, Claude auto-memory backed up to `.cairn/memory/`, the roadmap re-surfaced, any git-detected completed phases surfaced for you to confirm, and on a long session one line naming what clearing would cost — all in one line |
57
57
  | `UserPromptSubmit` | First message of a new session | Fresh project: Claude prompted to run `cairn_maintain`. Returning session: Claude prompted to run `cairn_resume`. Memory restored from `.cairn/memory/` if the Claude store is empty (new machine / fresh clone) |
58
58
 
59
59
  ---
@@ -83,7 +83,7 @@ No manual steps. The index lives in `.cairn/index.db` inside your project — li
83
83
  | `cairn_todos` | Scan codebase for TODO/FIXME/HACK comments, add manual items, resolve and list them |
84
84
  | `cairn_roadmap` | The active project plan: phases authored in `.cairn/roadmap.md`, with a cursor, dependencies, and auto-pruning of shipped phases into `.cairn/roadmap_completed.md` |
85
85
  | `cairn_bundle` | Minified source snapshot (auto-handled by hooks) |
86
- | `cairn_checkpoint` | Save session state (auto-handled by hooks) |
86
+ | `cairn_checkpoint` | Save session state (auto-handled by hooks). At a stopping point, answer `would_be_lost` — the one part that git and the index cannot recover |
87
87
  | `cairn_minify` | Minify a single file on demand (fallback when hooks are not installed) |
88
88
  | `cairn_switch` | Switch active project root mid-session (use for maintenance on a sibling service) |
89
89
  | `cairn_memo` | Save a preference, decision, or discovery to the project's persistent memory |
@@ -169,24 +169,41 @@ cairn_roadmap { action: "deps_add", from: 2, to: 4 } → phase 2 block
169
169
  cairn_roadmap { action: "publish" } → (re)write the human-readable ROADMAP.md at the repo root
170
170
  ```
171
171
 
172
- The live plan is also projected into a legible **`ROADMAP.md` at the repo root** — a
173
- human-readable view with checkboxes, per-item status, and a `← current` marker on the
174
- cursor phase. It refreshes automatically on session activity (the end-of-turn hook and
175
- the first prompt of a session) as well as on every `cairn_roadmap` call, so a workspace
176
- that has a roadmap but hasn't touched the roadmap tool this session still shows a
177
- populated file. It is deterministic (no timestamps, stable ordering, so an unchanged
178
- plan re-writes byte-identically and leaves clean git diffs), never written as an empty
179
- placeholder, and written atomically (temp file + rename) so a reader never catches a
180
- half-written plan.
172
+ The live plan is also projected into a **`ROADMAP.md` at the repo root**, published in
173
+ the **`roadmap/1`** slot format so any cockpit or tool can read it without writing a
174
+ parser per provider. One file carries both halves — two files drift, and the drift is
175
+ invisible:
176
+
177
+ - **A markdown half for humans:** checkboxes, per-item status, a one-line summary per
178
+ phase, and a `← current` marker on the cursor phase.
179
+ - **One `roadmap-json` block for consumers**, at the end of the file. It carries stable
180
+ phase `id`s, `number`, `title`, `status`, `goal`, `blocked_by`, `slices`, the full
181
+ per-phase `detail` (the body you wrote in `.cairn/roadmap.md`), and `links` — the
182
+ `[[wikilinks]]` in that body resolved to the memory files behind each phase, so the
183
+ reasoning is one tap away instead of "go and find it". A link that cannot be resolved
184
+ is dropped rather than emitted dead.
185
+
186
+ The first line is the marker that says who generated the file and when
187
+ (`<!-- slot:roadmap format=roadmap/1 provider=cairn generated=… -->`). cairn declares
188
+ the slot in its MCP entry in `~/.claude.json`, so a cockpit does not have to guess.
189
+
190
+ It refreshes automatically on session activity (the end-of-turn hook and the first
191
+ prompt of a session) as well as on every `cairn_roadmap` call, so a workspace that has a
192
+ roadmap but hasn't touched the roadmap tool this session still shows a populated file.
193
+ Phase `id`s are stable across regenerations (they key off the database row, so renaming
194
+ a phase never changes its id), unknown JSON fields survive a rewrite, an unchanged plan
195
+ re-writes byte-identically (the `generated` stamp is only refreshed when something
196
+ actually changed, so diffs stay clean), nothing is ever written as an empty placeholder,
197
+ and writes are atomic (temp file + rename) so a reader never catches a half-written plan.
181
198
 
182
199
  An existing `ROADMAP.md` is never destroyed. If the file is there but carries no
183
200
  provider marker (hand-written, or from a tool that doesn't stamp its output), cairn
184
201
  **adopts** it: the original is renamed to `ROADMAP.bak.md` (or `ROADMAP.bak.2.md`, `.3`,
185
- … — an existing backup is never overwritten), the generated file records where it came
186
- from (`migrated-from=ROADMAP.bak.md`), and the result reports `backed_up_to`. If the
187
- file is stamped by a *different* provider, that's a conflict over who owns the path:
188
- cairn leaves it completely alone. Any publish that declines returns a `warning` that the
189
- caller surfaces to you, so a stale plan can't rot in place unnoticed.
202
+ … — an existing backup is never overwritten), the data block records `migrated_from`
203
+ and `migrated`, and the result reports `backed_up_to`. If the file is stamped by a
204
+ *different* provider, that's a conflict over who owns the path: cairn leaves it
205
+ completely alone. Any publish that declines returns a `warning` that the caller surfaces
206
+ to you, so a stale plan can't rot in place unnoticed.
190
207
 
191
208
  Editing `roadmap.md` *is* editing the plan — read actions re-seed from it, and the
192
209
  end-of-turn hook folds in any hand-edits. Marking a phase **done** prunes it from
@@ -219,6 +236,52 @@ before calling `set_status … done`, so a stray keyword can't silently prune yo
219
236
 
220
237
  ---
221
238
 
239
+ ## Stopping points: what clearing would cost
240
+
241
+ Before `/clear` or `/compact`, the question that actually matters is not *how full
242
+ is the window*. Fullness tells you **when**; it never tells you **which**. What
243
+ decides it is whether the saved state is sufficient — "the slice is three-quarters
244
+ built and the fact that long-press selection has never run on a real handset is
245
+ written down nowhere" is a judgement about the handoff, not about a percentage.
246
+
247
+ Cairn cannot make that judgement for you. It cannot audit for absence: the gap
248
+ worth reporting is by definition the thing nobody wrote down, and a lazy
249
+ checkpoint looks exactly like a thorough one from the outside. A tool that
250
+ computed this would print "nothing outstanding" on precisely the sessions where
251
+ the most was outstanding. So cairn does the part it can do honestly — it **demands
252
+ the judgement on the way in and carries it across the gap**:
253
+
254
+ 1. **`cairn_checkpoint` asks.** A `would_be_lost` field whose description is the
255
+ literal question: what would a fresh session not know? Unverified work, a
256
+ thread you are mid-way through, a decision whose reason never made it into the
257
+ commit message. A checkpoint that skips it says so in its result rather than
258
+ passing silently.
259
+ 2. **`cairn_resume` carries it back**, near the top of the payload and first in
260
+ the summary, because everything else resume returns is recoverable from git and
261
+ the index and this is the only part that is not. It also lands on the first
262
+ prompt of the next session, since a session picked up after `/clear` rarely
263
+ opens with the word "resume".
264
+ 3. **It ages.** The end-of-turn hook checkpoints every turn, so `session.json`
265
+ always *looks* fresh while its contents may be hours and several slices stale.
266
+ The handoff is stamped only when it is actually written, never by the
267
+ heartbeat that copies it forward, and anything written before commits that
268
+ have since landed is reported as possibly stale.
269
+ 4. **Then, and only then, one line.** When the session transcript crosses a coarse
270
+ size threshold **and** something is actually at risk, the Stop hook adds a line
271
+ naming the loss: *"Clearing now would lose: … — last written before the 3
272
+ commits since."* When the handoff is current it says what clearing costs
273
+ instead, which is the more useful message and the one that lets you actually
274
+ clear.
275
+
276
+ Deliberately not built: no percentage (the transcript keeps growing across a
277
+ compaction while the window resets, so a precise number would claim an accuracy
278
+ it does not have), no transcript parsing (`statSync` only — this runs on every
279
+ single turn), no repeating the line when nothing changed, and no auto-clearing,
280
+ auto-compacting, or filling the field in on your behalf. Same shape as completed-phase
281
+ detection: detect, surface, confirm with the user, never apply.
282
+
283
+ ---
284
+
222
285
  ## Supported languages
223
286
 
224
287
  | Language | What Cairn extracts |