@isaacriehm/cairn-core 0.7.3 → 0.9.1

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 (229) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/attention/bulk-accept.d.ts +0 -2
  3. package/dist/attention/bulk-accept.js +0 -3
  4. package/dist/attention/bulk-accept.js.map +1 -1
  5. package/dist/attention/scoring.d.ts +1 -3
  6. package/dist/attention/scoring.js +1 -12
  7. package/dist/attention/scoring.js.map +1 -1
  8. package/dist/claude/cache.js +1 -0
  9. package/dist/claude/cache.js.map +1 -1
  10. package/dist/claude/runner.js +25 -1
  11. package/dist/claude/runner.js.map +1 -1
  12. package/dist/claude/types.d.ts +8 -0
  13. package/dist/doctor/index.js +57 -0
  14. package/dist/doctor/index.js.map +1 -1
  15. package/dist/gc/citation-integrity.js +3 -1
  16. package/dist/gc/citation-integrity.js.map +1 -1
  17. package/dist/gc/doc-claims.d.ts +31 -0
  18. package/dist/gc/doc-claims.js +213 -0
  19. package/dist/gc/doc-claims.js.map +1 -0
  20. package/dist/gc/doc-source-drift.d.ts +31 -0
  21. package/dist/gc/doc-source-drift.js +190 -0
  22. package/dist/gc/doc-source-drift.js.map +1 -0
  23. package/dist/gc/index.d.ts +4 -0
  24. package/dist/gc/index.js +2 -0
  25. package/dist/gc/index.js.map +1 -1
  26. package/dist/gc/sweep.js +18 -0
  27. package/dist/gc/sweep.js.map +1 -1
  28. package/dist/gc/types.d.ts +2 -2
  29. package/dist/hooks/runners/context-threshold.d.ts +63 -0
  30. package/dist/hooks/runners/context-threshold.js +179 -0
  31. package/dist/hooks/runners/context-threshold.js.map +1 -0
  32. package/dist/hooks/runners/gc-autotrigger.d.ts +49 -0
  33. package/dist/hooks/runners/gc-autotrigger.js +87 -0
  34. package/dist/hooks/runners/gc-autotrigger.js.map +1 -0
  35. package/dist/hooks/runners/index.d.ts +2 -0
  36. package/dist/hooks/runners/index.js +1 -0
  37. package/dist/hooks/runners/index.js.map +1 -1
  38. package/dist/hooks/runners/payload.d.ts +10 -0
  39. package/dist/hooks/runners/payload.js +13 -0
  40. package/dist/hooks/runners/payload.js.map +1 -1
  41. package/dist/hooks/runners/session-end.js +2 -4
  42. package/dist/hooks/runners/session-end.js.map +1 -1
  43. package/dist/hooks/runners/session-start.js +157 -9
  44. package/dist/hooks/runners/session-start.js.map +1 -1
  45. package/dist/hooks/runners/stop.js +119 -0
  46. package/dist/hooks/runners/stop.js.map +1 -1
  47. package/dist/init/brand-derive.js +6 -1
  48. package/dist/init/brand-derive.js.map +1 -1
  49. package/dist/init/brand-setup.d.ts +12 -1
  50. package/dist/init/brand-setup.js +36 -1
  51. package/dist/init/brand-setup.js.map +1 -1
  52. package/dist/init/curator/corpus.d.ts +92 -0
  53. package/dist/init/curator/corpus.js +171 -0
  54. package/dist/init/curator/corpus.js.map +1 -0
  55. package/dist/init/curator/emit.d.ts +42 -0
  56. package/dist/init/curator/emit.js +230 -0
  57. package/dist/init/curator/emit.js.map +1 -0
  58. package/dist/init/curator/index.d.ts +1 -0
  59. package/dist/init/curator/index.js +2 -0
  60. package/dist/init/curator/index.js.map +1 -0
  61. package/dist/init/curator/regex-prefilter.d.ts +54 -0
  62. package/dist/init/curator/regex-prefilter.js +185 -0
  63. package/dist/init/curator/regex-prefilter.js.map +1 -0
  64. package/dist/init/curator/validate.d.ts +46 -0
  65. package/dist/init/curator/validate.js +100 -0
  66. package/dist/init/curator/validate.js.map +1 -0
  67. package/dist/init/curator/walker.d.ts +36 -0
  68. package/dist/init/curator/walker.js +380 -0
  69. package/dist/init/curator/walker.js.map +1 -0
  70. package/dist/init/eta-calibration.d.ts +39 -0
  71. package/dist/init/eta-calibration.js +143 -0
  72. package/dist/init/eta-calibration.js.map +1 -0
  73. package/dist/init/index.d.ts +8 -3
  74. package/dist/init/index.js +4 -1
  75. package/dist/init/index.js.map +1 -1
  76. package/dist/init/ingest-docs.d.ts +6 -1
  77. package/dist/init/ingest-docs.js.map +1 -1
  78. package/dist/init/init.js +15 -26
  79. package/dist/init/init.js.map +1 -1
  80. package/dist/init/mapper-merge.d.ts +4 -6
  81. package/dist/init/mapper-merge.js +11 -34
  82. package/dist/init/mapper-merge.js.map +1 -1
  83. package/dist/init/mapper-parallel.d.ts +0 -1
  84. package/dist/init/mapper-parallel.js +20 -12
  85. package/dist/init/mapper-parallel.js.map +1 -1
  86. package/dist/init/mapper-prompts.d.ts +1 -4
  87. package/dist/init/mapper-prompts.js +2 -6
  88. package/dist/init/mapper-prompts.js.map +1 -1
  89. package/dist/init/mapper.d.ts +8 -7
  90. package/dist/init/mapper.js +23 -15
  91. package/dist/init/mapper.js.map +1 -1
  92. package/dist/init/overlay.js +0 -1
  93. package/dist/init/overlay.js.map +1 -1
  94. package/dist/init/phases/1-detect.d.ts +17 -4
  95. package/dist/init/phases/1-detect.js +48 -4
  96. package/dist/init/phases/1-detect.js.map +1 -1
  97. package/dist/init/phases/10-rules-merge.d.ts +7 -2
  98. package/dist/init/phases/10-rules-merge.js +17 -32
  99. package/dist/init/phases/10-rules-merge.js.map +1 -1
  100. package/dist/init/phases/11-baseline.js.map +1 -1
  101. package/dist/init/phases/12-strip.js +14 -1
  102. package/dist/init/phases/12-strip.js.map +1 -1
  103. package/dist/init/phases/13-multidev.d.ts +5 -1
  104. package/dist/init/phases/13-multidev.js +23 -2
  105. package/dist/init/phases/13-multidev.js.map +1 -1
  106. package/dist/init/phases/3-mapper.js.map +1 -1
  107. package/dist/init/phases/4-seed.js +2 -3
  108. package/dist/init/phases/4-seed.js.map +1 -1
  109. package/dist/init/phases/5-preflight.d.ts +42 -0
  110. package/dist/init/phases/5-preflight.js +244 -0
  111. package/dist/init/phases/5-preflight.js.map +1 -0
  112. package/dist/init/phases/6-brand.js +2 -4
  113. package/dist/init/phases/6-brand.js.map +1 -1
  114. package/dist/init/phases/7-topic-index.d.ts +6 -0
  115. package/dist/init/phases/7-topic-index.js +13 -0
  116. package/dist/init/phases/7-topic-index.js.map +1 -1
  117. package/dist/init/phases/8-docs-ingest.d.ts +6 -5
  118. package/dist/init/phases/8-docs-ingest.js +16 -43
  119. package/dist/init/phases/8-docs-ingest.js.map +1 -1
  120. package/dist/init/phases/9a-walker.d.ts +15 -0
  121. package/dist/init/phases/9a-walker.js +63 -0
  122. package/dist/init/phases/9a-walker.js.map +1 -0
  123. package/dist/init/phases/9b-curate.d.ts +19 -0
  124. package/dist/init/phases/9b-curate.js +79 -0
  125. package/dist/init/phases/9b-curate.js.map +1 -0
  126. package/dist/init/phases/9c-emit.d.ts +13 -0
  127. package/dist/init/phases/9c-emit.js +57 -0
  128. package/dist/init/phases/9c-emit.js.map +1 -0
  129. package/dist/init/phases/index.d.ts +6 -5
  130. package/dist/init/phases/index.js +4 -4
  131. package/dist/init/phases/index.js.map +1 -1
  132. package/dist/init/phases/mapper-output-io.d.ts +5 -5
  133. package/dist/init/phases/mapper-output-io.js +5 -5
  134. package/dist/init/phases/orchestrator.d.ts +10 -0
  135. package/dist/init/phases/orchestrator.js +13 -1
  136. package/dist/init/phases/orchestrator.js.map +1 -1
  137. package/dist/init/phases/state-io.js +1 -1
  138. package/dist/init/phases/types.d.ts +115 -9
  139. package/dist/init/phases/types.js +4 -2
  140. package/dist/init/phases/types.js.map +1 -1
  141. package/dist/init/post-git-init.d.ts +53 -0
  142. package/dist/init/post-git-init.js +74 -0
  143. package/dist/init/post-git-init.js.map +1 -0
  144. package/dist/init/preflight-guards.d.ts +11 -2
  145. package/dist/init/preflight-guards.js +27 -5
  146. package/dist/init/preflight-guards.js.map +1 -1
  147. package/dist/init/rules-merge/index.d.ts +1 -1
  148. package/dist/init/rules-merge/ingest.d.ts +6 -1
  149. package/dist/init/rules-merge/ingest.js.map +1 -1
  150. package/dist/init/skill-budget.d.ts +39 -0
  151. package/dist/init/skill-budget.js +99 -0
  152. package/dist/init/skill-budget.js.map +1 -0
  153. package/dist/init/source-comments/ingest.d.ts +0 -2
  154. package/dist/init/source-comments/ingest.js.map +1 -1
  155. package/dist/init/source-comments/walker.js +2 -2
  156. package/dist/init/topic-index/index.d.ts +8 -0
  157. package/dist/init/topic-index/index.js +10 -2
  158. package/dist/init/topic-index/index.js.map +1 -1
  159. package/dist/init/topic-index/judge.d.ts +15 -0
  160. package/dist/init/topic-index/judge.js +15 -1
  161. package/dist/init/topic-index/judge.js.map +1 -1
  162. package/dist/init/topic-index/resolve.js +41 -14
  163. package/dist/init/topic-index/resolve.js.map +1 -1
  164. package/dist/init/types.d.ts +9 -0
  165. package/dist/init/walker.d.ts +1 -1
  166. package/dist/init/walker.js +1 -1
  167. package/dist/init/workflow-block.d.ts +5 -6
  168. package/dist/init/workflow-block.js +5 -9
  169. package/dist/init/workflow-block.js.map +1 -1
  170. package/dist/mcp/bootstrap-guard.js +2 -2
  171. package/dist/mcp/bootstrap-guard.js.map +1 -1
  172. package/dist/mcp/schemas.d.ts +34 -0
  173. package/dist/mcp/schemas.js +50 -0
  174. package/dist/mcp/schemas.js.map +1 -1
  175. package/dist/mcp/telemetry.d.ts +1 -1
  176. package/dist/mcp/tools/bootstrap-retry.d.ts +23 -0
  177. package/dist/mcp/tools/bootstrap-retry.js +53 -0
  178. package/dist/mcp/tools/bootstrap-retry.js.map +1 -0
  179. package/dist/mcp/tools/bulk-accept-attention.d.ts +1 -1
  180. package/dist/mcp/tools/bulk-accept-attention.js +4 -6
  181. package/dist/mcp/tools/bulk-accept-attention.js.map +1 -1
  182. package/dist/mcp/tools/index.js +10 -0
  183. package/dist/mcp/tools/index.js.map +1 -1
  184. package/dist/mcp/tools/init-phases.d.ts +7 -6
  185. package/dist/mcp/tools/init-phases.js +36 -65
  186. package/dist/mcp/tools/init-phases.js.map +1 -1
  187. package/dist/mcp/tools/resume.d.ts +21 -0
  188. package/dist/mcp/tools/resume.js +88 -0
  189. package/dist/mcp/tools/resume.js.map +1 -0
  190. package/dist/mcp/tools/task-complete.d.ts +23 -0
  191. package/dist/mcp/tools/task-complete.js +51 -0
  192. package/dist/mcp/tools/task-complete.js.map +1 -0
  193. package/dist/mcp/tools/task-create.js +17 -11
  194. package/dist/mcp/tools/task-create.js.map +1 -1
  195. package/dist/mcp/tools/task-journal-append.d.ts +22 -0
  196. package/dist/mcp/tools/task-journal-append.js +44 -0
  197. package/dist/mcp/tools/task-journal-append.js.map +1 -0
  198. package/dist/paths/index.js +1 -1
  199. package/dist/paths/index.js.map +1 -1
  200. package/dist/status-line/format.js +20 -0
  201. package/dist/status-line/format.js.map +1 -1
  202. package/dist/tasks/index.d.ts +2 -0
  203. package/dist/tasks/index.js +2 -0
  204. package/dist/tasks/index.js.map +1 -0
  205. package/dist/tasks/lifecycle.d.ts +107 -0
  206. package/dist/tasks/lifecycle.js +302 -0
  207. package/dist/tasks/lifecycle.js.map +1 -0
  208. package/dist/trace/index.d.ts +1 -1
  209. package/dist/trace/index.js +2 -2
  210. package/dist/trace/index.js.map +1 -1
  211. package/package.json +2 -2
  212. package/templates/.cairn/config/trust-policy.yaml +0 -3
  213. package/templates/.cairn/config/workflow.md +0 -1
  214. package/templates/.cairn/ground/canonical-map/topics.yaml +0 -12
  215. package/dist/init/phases/5-pilot.d.ts +0 -10
  216. package/dist/init/phases/5-pilot.js +0 -108
  217. package/dist/init/phases/5-pilot.js.map +0 -1
  218. package/dist/init/phases/9-source-comments.d.ts +0 -6
  219. package/dist/init/phases/9-source-comments.js +0 -55
  220. package/dist/init/phases/9-source-comments.js.map +0 -1
  221. package/dist/init/phases/parallel-8910.d.ts +0 -27
  222. package/dist/init/phases/parallel-8910.js +0 -172
  223. package/dist/init/phases/parallel-8910.js.map +0 -1
  224. package/dist/init/phases/source-comments-output-io.d.ts +0 -84
  225. package/dist/init/phases/source-comments-output-io.js +0 -81
  226. package/dist/init/phases/source-comments-output-io.js.map +0 -1
  227. package/templates/.cairn/ground/capabilities/mcp-tools.yaml +0 -29
  228. package/templates/.cairn/ground/capabilities/skills.yaml +0 -25
  229. package/templates/.cairn/ground/capabilities/snippets.yaml +0 -29
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Context-threshold detection for the Stop hook.
3
+ *
4
+ * When mid-task context approaches the active model's window, surface
5
+ * an inline `[a] keep going [b] /clear and resume now [c] mark task
6
+ * done` choice via Claude Code's AskUserQuestion. The Stop hook can't
7
+ * call AskUserQuestion directly (only the model can), so it injects
8
+ * `decision: block` with an instructional reason that prompts main
9
+ * Claude to render the question.
10
+ *
11
+ * Threshold defaults to 50 % of the active model's window:
12
+ * - claude-opus-* → 1_000_000 tokens, fire at 500_000
13
+ * - claude-sonnet-* → 200_000 tokens, fire at 100_000
14
+ * - claude-haiku-* → 200_000 tokens, fire at 100_000
15
+ * - unknown model → assume Opus shape (1M / 500k threshold)
16
+ *
17
+ * Token count is estimated from the transcript file size (`bytes / 4`)
18
+ * — overcounts a little on JSON whitespace, undercounts on unicode-
19
+ * heavy turns. Good enough to fire near the threshold; not a budget
20
+ * check.
21
+ *
22
+ * Suppress re-fire within the same session by stamping
23
+ * `.cairn/sessions/<id>/ctx-threshold-warned.json`. Once stamped, the
24
+ * threshold prompt re-fires only when usage climbs another +10 %
25
+ * past the last warning.
26
+ */
27
+ import { existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
28
+ import { join } from "node:path";
29
+ const MODEL_WINDOW_FALLBACK = 1_000_000;
30
+ function modelWindow(model) {
31
+ if (/opus/i.test(model))
32
+ return 1_000_000;
33
+ if (/sonnet/i.test(model))
34
+ return 200_000;
35
+ if (/haiku/i.test(model))
36
+ return 200_000;
37
+ return MODEL_WINDOW_FALLBACK;
38
+ }
39
+ /**
40
+ * Walk the last ~64 KB of the transcript looking for the most recent
41
+ * `model` field. Claude Code transcript lines are JSON; each assistant
42
+ * turn carries a `message.model` string. Skipping the full file keeps
43
+ * the hook fast on long sessions.
44
+ */
45
+ function readModelFromTranscript(path) {
46
+ try {
47
+ const stat = statSync(path);
48
+ const tail = Math.min(stat.size, 65_536);
49
+ const fd = readFileSync(path, "utf8");
50
+ const slice = fd.slice(Math.max(0, fd.length - tail));
51
+ const lines = slice.split(/\r?\n/);
52
+ for (let i = lines.length - 1; i >= 0; i--) {
53
+ const line = lines[i];
54
+ if (line === undefined || line.length === 0)
55
+ continue;
56
+ try {
57
+ const obj = JSON.parse(line);
58
+ const m = obj.message?.model;
59
+ if (typeof m === "string" && m.length > 0)
60
+ return m;
61
+ }
62
+ catch {
63
+ // skip malformed lines
64
+ }
65
+ }
66
+ }
67
+ catch {
68
+ return null;
69
+ }
70
+ return null;
71
+ }
72
+ function estimateTokens(transcriptPath) {
73
+ try {
74
+ return Math.floor(statSync(transcriptPath).size / 4);
75
+ }
76
+ catch {
77
+ return 0;
78
+ }
79
+ }
80
+ function warnedStatePath(repoRoot, sessionId) {
81
+ return join(repoRoot, ".cairn", "sessions", sessionId, "ctx-threshold-warned.json");
82
+ }
83
+ function readWarned(repoRoot, sessionId) {
84
+ const path = warnedStatePath(repoRoot, sessionId);
85
+ if (!existsSync(path))
86
+ return null;
87
+ try {
88
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
89
+ if (typeof parsed === "object" &&
90
+ parsed !== null &&
91
+ typeof parsed.ts === "number" &&
92
+ typeof parsed.warned_at_tokens === "number") {
93
+ return parsed;
94
+ }
95
+ }
96
+ catch {
97
+ // fall through
98
+ }
99
+ return null;
100
+ }
101
+ function writeWarned(repoRoot, sessionId, state) {
102
+ try {
103
+ writeFileSync(warnedStatePath(repoRoot, sessionId), `${JSON.stringify(state, null, 2)}\n`, "utf8");
104
+ }
105
+ catch {
106
+ // best-effort
107
+ }
108
+ }
109
+ /**
110
+ * Returns the current threshold result. Stamps the warned-state file
111
+ * on a hit so re-fires within the same session are suppressed until
112
+ * usage climbs another +10 % of the window.
113
+ */
114
+ export function checkContextThreshold(input) {
115
+ if (input.transcriptPath === null || input.transcriptPath.length === 0) {
116
+ return { hit: false };
117
+ }
118
+ if (!existsSync(input.transcriptPath))
119
+ return { hit: false };
120
+ const model = input.modelOverride ?? readModelFromTranscript(input.transcriptPath) ?? "unknown";
121
+ const windowTokens = input.windowOverride ?? modelWindow(model);
122
+ const fraction = input.thresholdFraction ?? 0.5;
123
+ const thresholdTokens = Math.floor(windowTokens * fraction);
124
+ const estimated = estimateTokens(input.transcriptPath);
125
+ if (estimated < thresholdTokens)
126
+ return { hit: false };
127
+ const warned = readWarned(input.repoRoot, input.sessionId);
128
+ const reFireSlackTokens = Math.floor(windowTokens * 0.1);
129
+ if (warned !== null && estimated < warned.warned_at_tokens + reFireSlackTokens) {
130
+ return { hit: false };
131
+ }
132
+ writeWarned(input.repoRoot, input.sessionId, {
133
+ ts: Date.now(),
134
+ warned_at_tokens: estimated,
135
+ });
136
+ return {
137
+ hit: true,
138
+ estimatedTokens: estimated,
139
+ windowTokens,
140
+ pct: Math.round((estimated / windowTokens) * 100),
141
+ model,
142
+ taskId: null,
143
+ };
144
+ }
145
+ /**
146
+ * Render the inline prompt that the Stop hook injects via
147
+ * `decision: block`. The text instructs main Claude to render the
148
+ * three-option AskUserQuestion. Format-locked so the `[b]` branch
149
+ * always emits the literal `/cairn-resume <task_id>` token the
150
+ * operator pastes after `/clear`.
151
+ */
152
+ export function renderContextThresholdHint(hit, taskId) {
153
+ const taskLine = taskId !== null
154
+ ? `Active task: \`${taskId}\`.`
155
+ : "No active task — context still climbing through general work.";
156
+ const resumeLine = taskId !== null
157
+ ? `If the operator picks **\`b\`**, emit a code block containing exactly:\n\n\`\`\`\n/cairn-resume ${taskId}\n\`\`\`\n\nThe operator copies that, runs \`/clear\`, then pastes it into the fresh chat — Cairn rebuilds context from \`.cairn/tasks/active/${taskId}/journal.jsonl\`.`
158
+ : "If the operator picks **`b`**, instruct them to `/clear` and re-ask. There's no task journal to resume from yet.";
159
+ return [
160
+ `## Cairn — context threshold reached`,
161
+ "",
162
+ `Estimated **${hit.estimatedTokens.toLocaleString()} / ${hit.windowTokens.toLocaleString()} tokens (${hit.pct}%)** for \`${hit.model}\`. Trust degrades as context climbs — best to compact now.`,
163
+ "",
164
+ taskLine,
165
+ "",
166
+ "Render this question via the `AskUserQuestion` tool — do not skip:",
167
+ "",
168
+ "> Context at " + hit.pct + "% of " + hit.model + " window. Pick:",
169
+ "> ",
170
+ "> - `[a]` keep going (warn re-fires every +10 %)",
171
+ "> - `[b]` `/clear` and resume now (Cairn writes the resume prompt)",
172
+ "> - `[c]` mark task done (graduate the active TSK and start fresh)",
173
+ "",
174
+ resumeLine,
175
+ "",
176
+ "On `[c]`, call `cairn_task_complete({task_id, outcome: \"succeeded\"})` for the active task before ending the turn. On `[a]`, just continue.",
177
+ ].join("\n");
178
+ }
179
+ //# sourceMappingURL=context-threshold.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-threshold.js","sourceRoot":"","sources":["../../../src/hooks/runners/context-threshold.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Stop-hook GC autotrigger.
3
+ *
4
+ * Runs at the tail of every Stop tick when no init is in flight. If the
5
+ * last GC run is older than the threshold (default 24 h), spawns
6
+ * `cairn gc sweep` detached so the next attention surface (or
7
+ * operator-visible run) reflects current drift. The Stop hook does NOT
8
+ * wait for the spawned process — it stamps the marker, fires the spawn,
9
+ * unrefs, and returns.
10
+ *
11
+ * `cairn gc sweep` is sweep-only (no commit). The findings flow through
12
+ * `cairn-attention` for operator triage. `cairn gc run --apply-classes`
13
+ * stays operator-driven until the autotrigger is proven safe in the
14
+ * field.
15
+ */
16
+ export interface GcAutotriggerOptions {
17
+ repoRoot: string;
18
+ /** Override threshold in hours. Default 24. */
19
+ thresholdHours?: number;
20
+ /** Override "now"; injected by tests. */
21
+ now?: Date;
22
+ /**
23
+ * Replace the actual `child_process.spawn` call. Smokes pass a
24
+ * recorder so they can assert arguments without forking a real
25
+ * subprocess.
26
+ */
27
+ spawner?: (argv: GcAutotriggerArgv) => void;
28
+ /**
29
+ * Override CLAUDE_PLUGIN_ROOT; defaults to process.env. Required for
30
+ * tests that don't set the real env var.
31
+ */
32
+ pluginRoot?: string;
33
+ }
34
+ export interface GcAutotriggerArgv {
35
+ cmd: string;
36
+ args: string[];
37
+ cwd: string;
38
+ }
39
+ export type GcAutotriggerReason = "first_run" | "threshold_passed" | "fresh" | "no_plugin_root" | "no_cli_bundle";
40
+ export interface GcAutotriggerResult {
41
+ triggered: boolean;
42
+ reason: GcAutotriggerReason;
43
+ thresholdHours: number;
44
+ /** ISO timestamp of the marker as it stood before this call. "" when none. */
45
+ lastRunIso: string;
46
+ /** Argv that was (or would have been) spawned. Set when triggered=true. */
47
+ spawned?: GcAutotriggerArgv;
48
+ }
49
+ export declare function runGcAutotriggerCheck(opts: GcAutotriggerOptions): GcAutotriggerResult;
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Stop-hook GC autotrigger.
3
+ *
4
+ * Runs at the tail of every Stop tick when no init is in flight. If the
5
+ * last GC run is older than the threshold (default 24 h), spawns
6
+ * `cairn gc sweep` detached so the next attention surface (or
7
+ * operator-visible run) reflects current drift. The Stop hook does NOT
8
+ * wait for the spawned process — it stamps the marker, fires the spawn,
9
+ * unrefs, and returns.
10
+ *
11
+ * `cairn gc sweep` is sweep-only (no commit). The findings flow through
12
+ * `cairn-attention` for operator triage. `cairn gc run --apply-classes`
13
+ * stays operator-driven until the autotrigger is proven safe in the
14
+ * field.
15
+ */
16
+ import { spawn } from "node:child_process";
17
+ import { existsSync, readFileSync } from "node:fs";
18
+ import { join } from "node:path";
19
+ import { writeFileSafe } from "@isaacriehm/cairn-state";
20
+ const MARKER_REL = ".cairn/.gc-last-run";
21
+ const DEFAULT_THRESHOLD_HOURS = 24;
22
+ export function runGcAutotriggerCheck(opts) {
23
+ const thresholdHours = opts.thresholdHours ?? DEFAULT_THRESHOLD_HOURS;
24
+ const now = opts.now ?? new Date();
25
+ const markerAbs = join(opts.repoRoot, MARKER_REL);
26
+ const prior = readMarkerMtime(markerAbs);
27
+ let triggered = false;
28
+ let reason;
29
+ if (prior === null) {
30
+ triggered = true;
31
+ reason = "first_run";
32
+ }
33
+ else if (now.getTime() - prior.getTime() >= thresholdHours * 3_600_000) {
34
+ triggered = true;
35
+ reason = "threshold_passed";
36
+ }
37
+ else {
38
+ triggered = false;
39
+ reason = "fresh";
40
+ }
41
+ const lastRunIso = prior ? prior.toISOString() : "";
42
+ if (!triggered)
43
+ return { triggered, reason, thresholdHours, lastRunIso };
44
+ const pluginRoot = opts.pluginRoot ?? process.env["CLAUDE_PLUGIN_ROOT"];
45
+ if (typeof pluginRoot !== "string" || pluginRoot.length === 0) {
46
+ return { triggered: false, reason: "no_plugin_root", thresholdHours, lastRunIso };
47
+ }
48
+ const cliPath = join(pluginRoot, "dist", "cli.mjs");
49
+ if (!existsSync(cliPath)) {
50
+ return { triggered: false, reason: "no_cli_bundle", thresholdHours, lastRunIso };
51
+ }
52
+ writeFileSafe(markerAbs, now.toISOString());
53
+ const argv = {
54
+ cmd: process.execPath,
55
+ args: [cliPath, "gc", "sweep", "--repo-root", opts.repoRoot],
56
+ cwd: opts.repoRoot,
57
+ };
58
+ if (opts.spawner) {
59
+ opts.spawner(argv);
60
+ }
61
+ else {
62
+ const child = spawn(argv.cmd, argv.args, {
63
+ cwd: argv.cwd,
64
+ detached: true,
65
+ stdio: "ignore",
66
+ env: { ...process.env, CAIRN_GC_AUTOTRIGGERED: "1" },
67
+ });
68
+ child.unref();
69
+ }
70
+ return { triggered, reason, thresholdHours, lastRunIso, spawned: argv };
71
+ }
72
+ function readMarkerMtime(absPath) {
73
+ if (!existsSync(absPath))
74
+ return null;
75
+ let raw;
76
+ try {
77
+ raw = readFileSync(absPath, "utf8").trim();
78
+ }
79
+ catch {
80
+ return null;
81
+ }
82
+ const parsed = Date.parse(raw);
83
+ if (!Number.isFinite(parsed))
84
+ return null;
85
+ return new Date(parsed);
86
+ }
87
+ //# sourceMappingURL=gc-autotrigger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gc-autotrigger.js","sourceRoot":"","sources":["../../../src/hooks/runners/gc-autotrigger.ts"],"names":[],"mappings":"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"}
@@ -7,6 +7,8 @@ export { runSessionStartHook } from "./session-start.js";
7
7
  export { runSessionEndHook } from "./session-end.js";
8
8
  export { runStopHook } from "./stop.js";
9
9
  export { runUserPromptSubmitHook } from "./user-prompt-submit.js";
10
+ export { runGcAutotriggerCheck } from "./gc-autotrigger.js";
11
+ export type { GcAutotriggerArgv, GcAutotriggerOptions, GcAutotriggerReason, GcAutotriggerResult, } from "./gc-autotrigger.js";
10
12
  export { renderBypassHint, scanBypassedCommits, } from "../bypass-detection.js";
11
13
  export type { BypassedCommit, ScanBypassResult, } from "../bypass-detection.js";
12
14
  export { seedAttestedCommits } from "../seed-attested.js";
@@ -7,6 +7,7 @@ export { runSessionStartHook } from "./session-start.js";
7
7
  export { runSessionEndHook } from "./session-end.js";
8
8
  export { runStopHook } from "./stop.js";
9
9
  export { runUserPromptSubmitHook } from "./user-prompt-submit.js";
10
+ export { runGcAutotriggerCheck } from "./gc-autotrigger.js";
10
11
  export { renderBypassHint, scanBypassedCommits, } from "../bypass-detection.js";
11
12
  export { seedAttestedCommits } from "../seed-attested.js";
12
13
  export { CAIRN_HOOK_VERSION, emitShapeB, parseHookPayload, readHookStdin, appendTelemetry, } from "./payload.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/hooks/runners/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EACL,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,wBAAwB,CAAC;AAKhC,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,EACL,kBAAkB,EAClB,UAAU,EACV,gBAAgB,EAChB,aAAa,EACb,eAAe,GAChB,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/hooks/runners/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAO5D,OAAO,EACL,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,wBAAwB,CAAC;AAKhC,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,EACL,kBAAkB,EAClB,UAAU,EACV,gBAAgB,EAChB,aAAa,EACb,eAAe,GAChB,MAAM,cAAc,CAAC"}
@@ -32,8 +32,18 @@ export type HookEventName = "SessionStart" | "SessionEnd" | "Stop" | "UserPrompt
32
32
  * `hookEventName` MUST match the hook event the runner was invoked
33
33
  * for; mismatches are rejected as `Hook returned incorrect event
34
34
  * name` in Claude Code 2.1+.
35
+ *
36
+ * `Stop`, `SessionEnd`, and `PreCompact` reject `hookSpecificOutput`
37
+ * entirely under Claude Code 2.1+ — those runners must call
38
+ * `emitContinue` instead.
35
39
  */
36
40
  export declare function emitShapeB(context: string, hookEventName: HookEventName): never;
41
+ /**
42
+ * Write a bare `{continue: true}` payload and exit. Use for hook
43
+ * events that Claude Code 2.1+ refuses with a `hookSpecificOutput`
44
+ * envelope (currently `Stop`, `SessionEnd`, and `PreCompact`).
45
+ */
46
+ export declare function emitContinue(): never;
37
47
  /** Truncated append-only telemetry sink. */
38
48
  export declare function appendTelemetry(row: {
39
49
  repoRoot: string;
@@ -42,6 +42,10 @@ export function parseHookPayload(text) {
42
42
  * `hookEventName` MUST match the hook event the runner was invoked
43
43
  * for; mismatches are rejected as `Hook returned incorrect event
44
44
  * name` in Claude Code 2.1+.
45
+ *
46
+ * `Stop`, `SessionEnd`, and `PreCompact` reject `hookSpecificOutput`
47
+ * entirely under Claude Code 2.1+ — those runners must call
48
+ * `emitContinue` instead.
45
49
  */
46
50
  export function emitShapeB(context, hookEventName) {
47
51
  const payload = {
@@ -54,6 +58,15 @@ export function emitShapeB(context, hookEventName) {
54
58
  process.stdout.write(JSON.stringify(payload));
55
59
  process.exit(0);
56
60
  }
61
+ /**
62
+ * Write a bare `{continue: true}` payload and exit. Use for hook
63
+ * events that Claude Code 2.1+ refuses with a `hookSpecificOutput`
64
+ * envelope (currently `Stop`, `SessionEnd`, and `PreCompact`).
65
+ */
66
+ export function emitContinue() {
67
+ process.stdout.write(JSON.stringify({ continue: true }));
68
+ process.exit(0);
69
+ }
57
70
  /** Truncated append-only telemetry sink. */
58
71
  export function appendTelemetry(row) {
59
72
  const dir = join(row.repoRoot, ".cairn", "state", "telemetry");
@@ -1 +1 @@
1
- {"version":3,"file":"payload.js","sourceRoot":"","sources":["../../../src/hooks/runners/payload.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"payload.js","sourceRoot":"","sources":["../../../src/hooks/runners/payload.ts"],"names":[],"mappings":"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"}
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import { resolveRepoRoot } from "../../session-start/index.js";
8
8
  import { cleanupSession } from "../../session/index.js";
9
- import { emitShapeB, parseHookPayload, readHookStdin, appendTelemetry, } from "./payload.js";
9
+ import { emitContinue, parseHookPayload, readHookStdin, appendTelemetry, } from "./payload.js";
10
10
  export async function runSessionEndHook() {
11
11
  const startedAt = Date.now();
12
12
  const raw = await readHookStdin();
@@ -33,8 +33,6 @@ export async function runSessionEndHook() {
33
33
  extra: { removed },
34
34
  });
35
35
  }
36
- const out = { continue: true };
37
- void out;
38
- emitShapeB("", "SessionEnd");
36
+ emitContinue();
39
37
  }
40
38
  //# sourceMappingURL=session-end.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"session-end.js","sourceRoot":"","sources":["../../../src/hooks/runners/session-end.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,aAAa,EACb,eAAe,GAChB,MAAM,cAAc,CAAC;AAMtB,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,MAAM,GAAG,GAAG,MAAM,aAAa,EAAE,CAAC;IAClC,MAAM,OAAO,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACtC,MAAM,SAAS,GAAG,OAAO,OAAO,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;IACrF,MAAM,QAAQ,GAAG,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;IAC/E,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IAE3C,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,QAAQ,KAAK,IAAI,IAAI,SAAS,KAAK,IAAI,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpE,IAAI,CAAC;YACH,OAAO,GAAG,cAAc,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QAChD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,QAAQ,CAAC,IAAI,CACX,mBAAmB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CACtE,CAAC;QACJ,CAAC;QAED,eAAe,CAAC;YACd,QAAQ,EAAE,QAAkB;YAC5B,SAAS;YACT,IAAI,EAAE,aAAa;YACnB,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;YAClC,MAAM,EAAE,IAAI;YACZ,QAAQ;YACR,KAAK,EAAE,EAAE,OAAO,EAAE;SACnB,CAAC,CAAC;IACL,CAAC;IAED,MAAM,GAAG,GAA2B,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACvD,KAAK,GAAG,CAAC;IACT,UAAU,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC;AAC/B,CAAC"}
1
+ {"version":3,"file":"session-end.js","sourceRoot":"","sources":["../../../src/hooks/runners/session-end.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,aAAa,EACb,eAAe,GAChB,MAAM,cAAc,CAAC;AAEtB,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,MAAM,GAAG,GAAG,MAAM,aAAa,EAAE,CAAC;IAClC,MAAM,OAAO,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACtC,MAAM,SAAS,GAAG,OAAO,OAAO,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;IACrF,MAAM,QAAQ,GAAG,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;IAC/E,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IAE3C,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,QAAQ,KAAK,IAAI,IAAI,SAAS,KAAK,IAAI,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpE,IAAI,CAAC;YACH,OAAO,GAAG,cAAc,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QAChD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,QAAQ,CAAC,IAAI,CACX,mBAAmB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CACtE,CAAC;QACJ,CAAC;QAED,eAAe,CAAC;YACd,QAAQ,EAAE,QAAkB;YAC5B,SAAS;YACT,IAAI,EAAE,aAAa;YACnB,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;YAClC,MAAM,EAAE,IAAI;YACZ,QAAQ;YACR,KAAK,EAAE,EAAE,OAAO,EAAE;SACnB,CAAC,CAAC;IACL,CAAC;IAED,YAAY,EAAE,CAAC;AACjB,CAAC"}