@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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/attention/bulk-accept.d.ts +0 -2
- package/dist/attention/bulk-accept.js +0 -3
- package/dist/attention/bulk-accept.js.map +1 -1
- package/dist/attention/scoring.d.ts +1 -3
- package/dist/attention/scoring.js +1 -12
- package/dist/attention/scoring.js.map +1 -1
- package/dist/claude/cache.js +1 -0
- package/dist/claude/cache.js.map +1 -1
- package/dist/claude/runner.js +25 -1
- package/dist/claude/runner.js.map +1 -1
- package/dist/claude/types.d.ts +8 -0
- package/dist/doctor/index.js +57 -0
- package/dist/doctor/index.js.map +1 -1
- package/dist/gc/citation-integrity.js +3 -1
- package/dist/gc/citation-integrity.js.map +1 -1
- package/dist/gc/doc-claims.d.ts +31 -0
- package/dist/gc/doc-claims.js +213 -0
- package/dist/gc/doc-claims.js.map +1 -0
- package/dist/gc/doc-source-drift.d.ts +31 -0
- package/dist/gc/doc-source-drift.js +190 -0
- package/dist/gc/doc-source-drift.js.map +1 -0
- package/dist/gc/index.d.ts +4 -0
- package/dist/gc/index.js +2 -0
- package/dist/gc/index.js.map +1 -1
- package/dist/gc/sweep.js +18 -0
- package/dist/gc/sweep.js.map +1 -1
- package/dist/gc/types.d.ts +2 -2
- package/dist/hooks/runners/context-threshold.d.ts +63 -0
- package/dist/hooks/runners/context-threshold.js +179 -0
- package/dist/hooks/runners/context-threshold.js.map +1 -0
- package/dist/hooks/runners/gc-autotrigger.d.ts +49 -0
- package/dist/hooks/runners/gc-autotrigger.js +87 -0
- package/dist/hooks/runners/gc-autotrigger.js.map +1 -0
- package/dist/hooks/runners/index.d.ts +2 -0
- package/dist/hooks/runners/index.js +1 -0
- package/dist/hooks/runners/index.js.map +1 -1
- package/dist/hooks/runners/payload.d.ts +10 -0
- package/dist/hooks/runners/payload.js +13 -0
- package/dist/hooks/runners/payload.js.map +1 -1
- package/dist/hooks/runners/session-end.js +2 -4
- package/dist/hooks/runners/session-end.js.map +1 -1
- package/dist/hooks/runners/session-start.js +157 -9
- package/dist/hooks/runners/session-start.js.map +1 -1
- package/dist/hooks/runners/stop.js +119 -0
- package/dist/hooks/runners/stop.js.map +1 -1
- package/dist/init/brand-derive.js +6 -1
- package/dist/init/brand-derive.js.map +1 -1
- package/dist/init/brand-setup.d.ts +12 -1
- package/dist/init/brand-setup.js +36 -1
- package/dist/init/brand-setup.js.map +1 -1
- package/dist/init/curator/corpus.d.ts +92 -0
- package/dist/init/curator/corpus.js +171 -0
- package/dist/init/curator/corpus.js.map +1 -0
- package/dist/init/curator/emit.d.ts +42 -0
- package/dist/init/curator/emit.js +230 -0
- package/dist/init/curator/emit.js.map +1 -0
- package/dist/init/curator/index.d.ts +1 -0
- package/dist/init/curator/index.js +2 -0
- package/dist/init/curator/index.js.map +1 -0
- package/dist/init/curator/regex-prefilter.d.ts +54 -0
- package/dist/init/curator/regex-prefilter.js +185 -0
- package/dist/init/curator/regex-prefilter.js.map +1 -0
- package/dist/init/curator/validate.d.ts +46 -0
- package/dist/init/curator/validate.js +100 -0
- package/dist/init/curator/validate.js.map +1 -0
- package/dist/init/curator/walker.d.ts +36 -0
- package/dist/init/curator/walker.js +380 -0
- package/dist/init/curator/walker.js.map +1 -0
- package/dist/init/eta-calibration.d.ts +39 -0
- package/dist/init/eta-calibration.js +143 -0
- package/dist/init/eta-calibration.js.map +1 -0
- package/dist/init/index.d.ts +8 -3
- package/dist/init/index.js +4 -1
- package/dist/init/index.js.map +1 -1
- package/dist/init/ingest-docs.d.ts +6 -1
- package/dist/init/ingest-docs.js.map +1 -1
- package/dist/init/init.js +15 -26
- package/dist/init/init.js.map +1 -1
- package/dist/init/mapper-merge.d.ts +4 -6
- package/dist/init/mapper-merge.js +11 -34
- package/dist/init/mapper-merge.js.map +1 -1
- package/dist/init/mapper-parallel.d.ts +0 -1
- package/dist/init/mapper-parallel.js +20 -12
- package/dist/init/mapper-parallel.js.map +1 -1
- package/dist/init/mapper-prompts.d.ts +1 -4
- package/dist/init/mapper-prompts.js +2 -6
- package/dist/init/mapper-prompts.js.map +1 -1
- package/dist/init/mapper.d.ts +8 -7
- package/dist/init/mapper.js +23 -15
- package/dist/init/mapper.js.map +1 -1
- package/dist/init/overlay.js +0 -1
- package/dist/init/overlay.js.map +1 -1
- package/dist/init/phases/1-detect.d.ts +17 -4
- package/dist/init/phases/1-detect.js +48 -4
- package/dist/init/phases/1-detect.js.map +1 -1
- package/dist/init/phases/10-rules-merge.d.ts +7 -2
- package/dist/init/phases/10-rules-merge.js +17 -32
- package/dist/init/phases/10-rules-merge.js.map +1 -1
- package/dist/init/phases/11-baseline.js.map +1 -1
- package/dist/init/phases/12-strip.js +14 -1
- package/dist/init/phases/12-strip.js.map +1 -1
- package/dist/init/phases/13-multidev.d.ts +5 -1
- package/dist/init/phases/13-multidev.js +23 -2
- package/dist/init/phases/13-multidev.js.map +1 -1
- package/dist/init/phases/3-mapper.js.map +1 -1
- package/dist/init/phases/4-seed.js +2 -3
- package/dist/init/phases/4-seed.js.map +1 -1
- package/dist/init/phases/5-preflight.d.ts +42 -0
- package/dist/init/phases/5-preflight.js +244 -0
- package/dist/init/phases/5-preflight.js.map +1 -0
- package/dist/init/phases/6-brand.js +2 -4
- package/dist/init/phases/6-brand.js.map +1 -1
- package/dist/init/phases/7-topic-index.d.ts +6 -0
- package/dist/init/phases/7-topic-index.js +13 -0
- package/dist/init/phases/7-topic-index.js.map +1 -1
- package/dist/init/phases/8-docs-ingest.d.ts +6 -5
- package/dist/init/phases/8-docs-ingest.js +16 -43
- package/dist/init/phases/8-docs-ingest.js.map +1 -1
- package/dist/init/phases/9a-walker.d.ts +15 -0
- package/dist/init/phases/9a-walker.js +63 -0
- package/dist/init/phases/9a-walker.js.map +1 -0
- package/dist/init/phases/9b-curate.d.ts +19 -0
- package/dist/init/phases/9b-curate.js +79 -0
- package/dist/init/phases/9b-curate.js.map +1 -0
- package/dist/init/phases/9c-emit.d.ts +13 -0
- package/dist/init/phases/9c-emit.js +57 -0
- package/dist/init/phases/9c-emit.js.map +1 -0
- package/dist/init/phases/index.d.ts +6 -5
- package/dist/init/phases/index.js +4 -4
- package/dist/init/phases/index.js.map +1 -1
- package/dist/init/phases/mapper-output-io.d.ts +5 -5
- package/dist/init/phases/mapper-output-io.js +5 -5
- package/dist/init/phases/orchestrator.d.ts +10 -0
- package/dist/init/phases/orchestrator.js +13 -1
- package/dist/init/phases/orchestrator.js.map +1 -1
- package/dist/init/phases/state-io.js +1 -1
- package/dist/init/phases/types.d.ts +115 -9
- package/dist/init/phases/types.js +4 -2
- package/dist/init/phases/types.js.map +1 -1
- package/dist/init/post-git-init.d.ts +53 -0
- package/dist/init/post-git-init.js +74 -0
- package/dist/init/post-git-init.js.map +1 -0
- package/dist/init/preflight-guards.d.ts +11 -2
- package/dist/init/preflight-guards.js +27 -5
- package/dist/init/preflight-guards.js.map +1 -1
- package/dist/init/rules-merge/index.d.ts +1 -1
- package/dist/init/rules-merge/ingest.d.ts +6 -1
- package/dist/init/rules-merge/ingest.js.map +1 -1
- package/dist/init/skill-budget.d.ts +39 -0
- package/dist/init/skill-budget.js +99 -0
- package/dist/init/skill-budget.js.map +1 -0
- package/dist/init/source-comments/ingest.d.ts +0 -2
- package/dist/init/source-comments/ingest.js.map +1 -1
- package/dist/init/source-comments/walker.js +2 -2
- package/dist/init/topic-index/index.d.ts +8 -0
- package/dist/init/topic-index/index.js +10 -2
- package/dist/init/topic-index/index.js.map +1 -1
- package/dist/init/topic-index/judge.d.ts +15 -0
- package/dist/init/topic-index/judge.js +15 -1
- package/dist/init/topic-index/judge.js.map +1 -1
- package/dist/init/topic-index/resolve.js +41 -14
- package/dist/init/topic-index/resolve.js.map +1 -1
- package/dist/init/types.d.ts +9 -0
- package/dist/init/walker.d.ts +1 -1
- package/dist/init/walker.js +1 -1
- package/dist/init/workflow-block.d.ts +5 -6
- package/dist/init/workflow-block.js +5 -9
- package/dist/init/workflow-block.js.map +1 -1
- package/dist/mcp/bootstrap-guard.js +2 -2
- package/dist/mcp/bootstrap-guard.js.map +1 -1
- package/dist/mcp/schemas.d.ts +34 -0
- package/dist/mcp/schemas.js +50 -0
- package/dist/mcp/schemas.js.map +1 -1
- package/dist/mcp/telemetry.d.ts +1 -1
- package/dist/mcp/tools/bootstrap-retry.d.ts +23 -0
- package/dist/mcp/tools/bootstrap-retry.js +53 -0
- package/dist/mcp/tools/bootstrap-retry.js.map +1 -0
- package/dist/mcp/tools/bulk-accept-attention.d.ts +1 -1
- package/dist/mcp/tools/bulk-accept-attention.js +4 -6
- package/dist/mcp/tools/bulk-accept-attention.js.map +1 -1
- package/dist/mcp/tools/index.js +10 -0
- package/dist/mcp/tools/index.js.map +1 -1
- package/dist/mcp/tools/init-phases.d.ts +7 -6
- package/dist/mcp/tools/init-phases.js +36 -65
- package/dist/mcp/tools/init-phases.js.map +1 -1
- package/dist/mcp/tools/resume.d.ts +21 -0
- package/dist/mcp/tools/resume.js +88 -0
- package/dist/mcp/tools/resume.js.map +1 -0
- package/dist/mcp/tools/task-complete.d.ts +23 -0
- package/dist/mcp/tools/task-complete.js +51 -0
- package/dist/mcp/tools/task-complete.js.map +1 -0
- package/dist/mcp/tools/task-create.js +17 -11
- package/dist/mcp/tools/task-create.js.map +1 -1
- package/dist/mcp/tools/task-journal-append.d.ts +22 -0
- package/dist/mcp/tools/task-journal-append.js +44 -0
- package/dist/mcp/tools/task-journal-append.js.map +1 -0
- package/dist/paths/index.js +1 -1
- package/dist/paths/index.js.map +1 -1
- package/dist/status-line/format.js +20 -0
- package/dist/status-line/format.js.map +1 -1
- package/dist/tasks/index.d.ts +2 -0
- package/dist/tasks/index.js +2 -0
- package/dist/tasks/index.js.map +1 -0
- package/dist/tasks/lifecycle.d.ts +107 -0
- package/dist/tasks/lifecycle.js +302 -0
- package/dist/tasks/lifecycle.js.map +1 -0
- package/dist/trace/index.d.ts +1 -1
- package/dist/trace/index.js +2 -2
- package/dist/trace/index.js.map +1 -1
- package/package.json +2 -2
- package/templates/.cairn/config/trust-policy.yaml +0 -3
- package/templates/.cairn/config/workflow.md +0 -1
- package/templates/.cairn/ground/canonical-map/topics.yaml +0 -12
- package/dist/init/phases/5-pilot.d.ts +0 -10
- package/dist/init/phases/5-pilot.js +0 -108
- package/dist/init/phases/5-pilot.js.map +0 -1
- package/dist/init/phases/9-source-comments.d.ts +0 -6
- package/dist/init/phases/9-source-comments.js +0 -55
- package/dist/init/phases/9-source-comments.js.map +0 -1
- package/dist/init/phases/parallel-8910.d.ts +0 -27
- package/dist/init/phases/parallel-8910.js +0 -172
- package/dist/init/phases/parallel-8910.js.map +0 -1
- package/dist/init/phases/source-comments-output-io.d.ts +0 -84
- package/dist/init/phases/source-comments-output-io.js +0 -81
- package/dist/init/phases/source-comments-output-io.js.map +0 -1
- package/templates/.cairn/ground/capabilities/mcp-tools.yaml +0 -29
- package/templates/.cairn/ground/capabilities/skills.yaml +0 -25
- 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":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,EAAW,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAO,CAAC;AAE1C,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACjC,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACtC,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC1B,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACtC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC9B,CAAC,CAAC,WAAW,EAAE,CAAC;AAIjB,MAAM,UAAU,aAAa;IAC3B,OAAO,IAAI,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE;QAC9B,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAChE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;YAC3B,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;QACnD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACxC,IAAI,CAAC;QACH,MAAM,GAAG,GAAY,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,MAAM,GAAG,uBAAuB,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACtD,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAqBD
|
|
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 {
|
|
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
|
-
|
|
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,
|
|
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"}
|