@misterhuydo/cairn-mcp 1.38.0 → 1.40.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 +178 -6
- package/dist/cairn-cli.js +85 -81
- package/dist/index.js +87 -77
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -196,6 +196,11 @@ cairn_roadmap { action: "focus", id: 3 } → make phase 3 the current f
|
|
|
196
196
|
cairn_roadmap { action: "set_status", id: 3, status: "done" } → ship it
|
|
197
197
|
cairn_roadmap { action: "add", parent: 3, text: "..." } → ad-hoc sub-task under a phase
|
|
198
198
|
cairn_roadmap { action: "deps_add", from: 2, to: 4 } → phase 2 blocks phase 4
|
|
199
|
+
cairn_roadmap { action: "reorder", order: "1 -> 8 -> 9 -> 5" } → state the priority order
|
|
200
|
+
cairn_roadmap { action: "reorder", move: 8, before: 5 } → "do phase 8 before phase 5"
|
|
201
|
+
cairn_roadmap { action: "reorder", decline: true } → "no stated order, deliberately" — and stop asking
|
|
202
|
+
cairn_roadmap { action: "set_status", id: 3, status: "deferred" } → park it: still in the plan, out of the live set
|
|
203
|
+
cairn_roadmap { action: "deferrable" } → propose which phases look parked, from the markers already in their titles
|
|
199
204
|
cairn_roadmap { action: "publish" } → (re)write the human-readable ROADMAP.md at the repo root
|
|
200
205
|
```
|
|
201
206
|
|
|
@@ -218,7 +223,8 @@ invisible:
|
|
|
218
223
|
flat markdown. Collapsing costs nothing: the fence keeps blank lines around it, so it
|
|
219
224
|
stays a valid CommonMark code block and both line-anchored and AST consumers still
|
|
220
225
|
find it. It carries stable
|
|
221
|
-
phase `id`s, `
|
|
226
|
+
phase `id`s, `ref` (the name that actually identifies a phase — see below), `number`,
|
|
227
|
+
`label`, `group`, `title`, `status`, `goal`, `blocked_by`, `slices`, the full
|
|
222
228
|
per-phase `detail` (the body you wrote in `.cairn/roadmap.md`, verbatim and in full —
|
|
223
229
|
thinning the prose never thins this), and `links` — the `[[wikilinks]]` in that body
|
|
224
230
|
resolved to the memory files behind each phase, so the reasoning is one tap away
|
|
@@ -231,10 +237,20 @@ plan **complete** — `done` and `stale` phases stay in `phases` with their real
|
|
|
231
237
|
because a consumer that cannot see finished work will confidently render an incomplete
|
|
232
238
|
plan. A shipped phase also carries `shipped` (the date) and `commit` (short HEAD when it
|
|
233
239
|
was marked done), which is what turns "this was done" into something you can point an
|
|
234
|
-
agent at when you are tracing a bug back to where it came from.
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
240
|
+
agent at when you are tracing a bug back to where it came from.
|
|
241
|
+
|
|
242
|
+
**`shipped_phases` is the shipping log** — every phase ever marked done, newest first,
|
|
243
|
+
read from the same `.cairn/roadmap_completed.md` the reader half's *Recently shipped*
|
|
244
|
+
section is generated from, so the two halves of one file cannot disagree about the same
|
|
245
|
+
fact. (They used to: two real projects published `shipped_phases: []` beside a markdown
|
|
246
|
+
list of six and eight shipped phases, and a consumer taking the JSON at its word saw two
|
|
247
|
+
projects that had shipped nothing.) The markdown shows the most recent few; this carries
|
|
248
|
+
all of them. Entries are identity and provenance only — `id`, `ref`, `number`, `title`,
|
|
249
|
+
`shipped`, `commit`, never `detail` — so a long-lived repo's history cannot bloat the
|
|
250
|
+
file. A phase still in the current plan appears in **both** `phases[]` and here: they are
|
|
251
|
+
two indexes of one plan, the tree and the chronology, joined by `id`. Entries logged
|
|
252
|
+
before the log carried ids publish no `id` and no `ref` rather than a guessed one. The
|
|
253
|
+
key is always present; its absence means the file predates this.
|
|
238
254
|
|
|
239
255
|
Three rules the format leaves implicit, each of which has cost a consumer real time:
|
|
240
256
|
|
|
@@ -249,7 +265,163 @@ Three rules the format leaves implicit, each of which has cost a consumer real t
|
|
|
249
265
|
one phase current, use `cursor_phase`, which is always the top-level ancestor and
|
|
250
266
|
always resolves in `phases`; `cursor_path` is the root-to-focus chain for breadcrumbs.
|
|
251
267
|
All three are `null` when nothing is active, so "nothing is in progress" is never
|
|
252
|
-
ambiguous with a broken pointer.
|
|
268
|
+
ambiguous with a broken pointer. A `cursor` you cannot find in `phases[]` is normal
|
|
269
|
+
and not a dangling pointer — look in `slices[]`, or just follow `cursor_path`.
|
|
270
|
+
- **`number` does not identify a phase; `ref` does.** `number` is the phase number
|
|
271
|
+
authored in the plan, and it is unique only *within a decision group* — one real plan
|
|
272
|
+
publishes `number: 1` on forty separate rows. Print or say **`ref`**, which is always
|
|
273
|
+
present and unique in the document: the project's own name for the phase (`TC-4`)
|
|
274
|
+
where the titles carry one, otherwise a bare number where that identifies, otherwise
|
|
275
|
+
a group-qualified one (`roadmap-3`), otherwise the row id. `ref` is a *name* and can
|
|
276
|
+
lengthen when phases are added elsewhere; `id` stays the stable machine key.
|
|
277
|
+
|
|
278
|
+
### The priority order
|
|
279
|
+
|
|
280
|
+
`## Phases` in `.cairn/roadmap.md` says what the phases are. It does not say what order
|
|
281
|
+
to work them in, and **renumbering to express that is destructive** — the file-to-DB
|
|
282
|
+
sync matches rows by phase number, so moving "Phase 8" to "Phase 2" reads as "phase 2's
|
|
283
|
+
text changed" and rewires which row is which.
|
|
284
|
+
|
|
285
|
+
So the order is its own statement. Author it as an `## Order` section, in the numbers
|
|
286
|
+
the phase headers already use:
|
|
287
|
+
|
|
288
|
+
```markdown
|
|
289
|
+
## Order
|
|
290
|
+
|
|
291
|
+
1 -> 8 -> 9 -> 5
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
or let a working session set it — `cairn_roadmap reorder` takes either the whole list or
|
|
295
|
+
a single relative move, so "do phase 8 before phase 5" mid-session lands in the file and
|
|
296
|
+
in the next publish. A relative move splices against the *effective* order (what was
|
|
297
|
+
stated, then the rest in plan order), so phases the stated list omits keep their place
|
|
298
|
+
instead of being silently demoted.
|
|
299
|
+
|
|
300
|
+
It publishes as **`order`** — phase ids, most important first — with
|
|
301
|
+
**`order_source: "authored"`**. A partial list is fine; anything it omits follows in plan
|
|
302
|
+
order. Two rules hold it honest:
|
|
303
|
+
|
|
304
|
+
- **It never contradicts `blocked_by`.** If A is blocked by B, B comes first. An order
|
|
305
|
+
that disagrees is *repaired* rather than refused (publishing is a background side
|
|
306
|
+
effect and must not fail) — an unmentioned blocker is pulled in, a late one is pulled
|
|
307
|
+
forward, and everything the deps do not implicate keeps its stated position. The
|
|
308
|
+
repair is reported in the `reorder` result, never applied silently.
|
|
309
|
+
- **cairn never publishes an order it worked out itself.** When nothing is stated the
|
|
310
|
+
`order` key is *absent*, so a consumer is free to derive one and label it as derived.
|
|
311
|
+
A guessed sequence and a stated one are different claims and must not look alike.
|
|
312
|
+
|
|
313
|
+
`next` follows the stated order too. A plan that publishes a priority the tool then
|
|
314
|
+
advances past is publishing a decoration.
|
|
315
|
+
|
|
316
|
+
### Deferred: work that is parked, not in progress
|
|
317
|
+
|
|
318
|
+
A phase is `deferred` when it is **finished, with a tail somebody deliberately parked**.
|
|
319
|
+
Not `open` — nobody is picking it up. Not `done` — the work is real and the plan should
|
|
320
|
+
still show it.
|
|
321
|
+
|
|
322
|
+
Without that status, one slice titled `S5 Billing (paid gate)` keeps its whole phase in
|
|
323
|
+
the live set forever, and anything that ranks live phases ranks parked work above
|
|
324
|
+
genuinely open work. Measured on a real plan: four of eleven live phases were live
|
|
325
|
+
*solely* because of one slice whose own title said `(later)` or `(paid gate)`, and the
|
|
326
|
+
phase with two genuinely open slices and nothing blocking it sorted **eighth**.
|
|
327
|
+
|
|
328
|
+
A phase is deferred either because you said so:
|
|
329
|
+
|
|
330
|
+
```
|
|
331
|
+
cairn_roadmap { action: "set_status", id: 3, status: "deferred" }
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
or because **every slice it has left is** — that half is *derived*, not stored, on every
|
|
335
|
+
read, from the same rows everything else already loads. Storing it would mean
|
|
336
|
+
re-deriving on each path that can change a slice, and one missed path leaves a phase
|
|
337
|
+
permanently mis-parked. The rule is exact: a phase with at least one deferred slice and
|
|
338
|
+
no slice still open, in progress or blocked is deferred; a phase whose slices are *all*
|
|
339
|
+
done keeps the status it was given, because whether it is finished is a judgement about
|
|
340
|
+
work outside its slices and cairn does not make it for you.
|
|
341
|
+
|
|
342
|
+
What parking does, and does not do:
|
|
343
|
+
|
|
344
|
+
- It **leaves the live set**: out of `next`, out of the published order, out of the
|
|
345
|
+
`M/N done` denominator (with the exclusion stated on the line, never quiet), and
|
|
346
|
+
published as `status: "deferred"`.
|
|
347
|
+
- It **stays in the plan**: shown in the tree as `[>]`, published in `phases[]`, *not*
|
|
348
|
+
pruned from `roadmap.md` and *not* logged to `roadmap_completed.md`. Parked is not
|
|
349
|
+
shipped. `set_status <id> open` picks it back up.
|
|
350
|
+
- The `## Order` section **still remembers it**, even though it drops out of the
|
|
351
|
+
published `order` — unparking should not cost you your place in the queue.
|
|
352
|
+
- A blocker that is parked still blocks: `next` refuses to advance past it and says
|
|
353
|
+
which phase did it. What changes is that the order stops dragging parked work forward.
|
|
354
|
+
|
|
355
|
+
Plans said this in prose long before there was a status for it. `deferrable` reads those
|
|
356
|
+
markers back:
|
|
357
|
+
|
|
358
|
+
```
|
|
359
|
+
cairn_roadmap { action: "deferrable" }
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
It proposes — it never writes. Candidates are titles carrying a parenthetical park
|
|
363
|
+
marker (`(later)`, `(paid gate)`, `(design-later)`, `(on hold)`, …), gated on the
|
|
364
|
+
parenthetical rather than the bare word, because "do this later" appears in half the
|
|
365
|
+
phase titles ever written. Each candidate comes with the consequence that makes it worth
|
|
366
|
+
confirming: which phases would leave the live set, and whether that is because of the
|
|
367
|
+
marker itself or because of what would be left under them. You confirm the ones you
|
|
368
|
+
mean, one `set_status` each.
|
|
369
|
+
|
|
370
|
+
### Being asked for the order
|
|
371
|
+
|
|
372
|
+
cairn shipped `order` and `reorder` — documented, with relative moves and cycle repair —
|
|
373
|
+
and two days later `order` was still unset in every project that had one. Not because
|
|
374
|
+
anyone disliked it: because nothing ever mentioned it. **A capability nobody is prompted
|
|
375
|
+
to use is indistinguishable, from the outside, from one that does not exist.**
|
|
376
|
+
|
|
377
|
+
So plan-*changing* actions — a `sync` that inserted or retired phases, `set_status` to
|
|
378
|
+
`done` or `deferred`, and `reorder` itself — may carry an advisory field:
|
|
379
|
+
|
|
380
|
+
```json
|
|
381
|
+
"order_advice": {
|
|
382
|
+
"state": "never_set",
|
|
383
|
+
"live_phases": 11,
|
|
384
|
+
"unordered": ["phase-25", "phase-60", "phase-101"],
|
|
385
|
+
"why": "11 live phases and no stated order, so `next` follows plan number order — the sequence they were written in, which is not a priority; 3 of them are waiting on another phase",
|
|
386
|
+
"ask": true
|
|
387
|
+
}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
Four things about it are deliberate:
|
|
391
|
+
|
|
392
|
+
- **It never fires on `tree`, `status` or `next`.** The end-of-turn hook calls those
|
|
393
|
+
after every assistant turn, and a nudge there is noise on a loop — which is precisely
|
|
394
|
+
how a cursor sat on a finished phase for weeks, on screen the whole time, in a line
|
|
395
|
+
everybody had stopped reading.
|
|
396
|
+
- **It is advisory.** Never a refusal, never a blocked action, and cairn never writes an
|
|
397
|
+
order itself. cairn is an MCP server and cannot ask a question; the agent can, and it
|
|
398
|
+
has the code in front of it, so it brings you a recommendation with reasons rather
|
|
399
|
+
than a bare "what order?". An order the agent invented would be published as
|
|
400
|
+
`order_source: "authored"` and shown to you as your own decision.
|
|
401
|
+
- **`why` is generated from the plan, and it is the point.** "No order is set" produces a
|
|
402
|
+
vague question and a vague answer.
|
|
403
|
+
- **It can go quiet.** "No order has been set" and "we decided not to have one" are
|
|
404
|
+
different facts. `cairn_roadmap reorder decline: true` records the second in the
|
|
405
|
+
`## Order` section, in a line you can read and delete, and `ask` goes false. It is not
|
|
406
|
+
a lock: any later `reorder` replaces it with no ceremony. The decline records the
|
|
407
|
+
phases live at the time, so a phase *added* later revives the question — and even then
|
|
408
|
+
the advisory says the decline stands and asks only where the newcomer goes.
|
|
409
|
+
|
|
410
|
+
A stated order is only reported as `stale` when it **points at something that is not
|
|
411
|
+
there** — a phase that shipped, got parked, or left the plan. Phases it simply does not
|
|
412
|
+
mention are reported in `unordered` and nothing more: *"phase 8 first, sort the rest out
|
|
413
|
+
when we get there"* is a complete answer, and an advisory that treats a partial order as
|
|
414
|
+
unfinished makes stating one a chore nobody completes.
|
|
415
|
+
|
|
416
|
+
`cursor_advice` is the same shape for the other thing nothing ever said out loud — a
|
|
417
|
+
cursor left on finished (`on_done_phase`), parked (`on_parked_phase`) or removed
|
|
418
|
+
(`dangling`) work, or unset while live phases remain. The cursor is cairn's own state,
|
|
419
|
+
so no consumer can warn about it on cairn's behalf.
|
|
420
|
+
|
|
421
|
+
Both ride on the **tool result**, not in the published `ROADMAP.md`. A file is read
|
|
422
|
+
continuously by panels and dashboards; an advisory in it would be a permanent nag on a
|
|
423
|
+
screen that cannot act on it. It goes to the one reader who can put the question to a
|
|
424
|
+
person.
|
|
253
425
|
|
|
254
426
|
The first line is the marker that says who generated the file and when
|
|
255
427
|
(`<!-- slot:roadmap format=roadmap/1 provider=cairn generated=… -->`). cairn declares
|