@isaacriehm/cairn-core 0.22.6 → 0.24.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/dist/.tsbuildinfo +1 -1
- package/dist/cites/expand.d.ts +75 -0
- package/dist/cites/expand.js +197 -0
- package/dist/cites/expand.js.map +1 -0
- package/dist/context/index.d.ts +2 -0
- package/dist/context/index.js +1 -0
- package/dist/context/index.js.map +1 -1
- package/dist/context/task-summary.d.ts +18 -0
- package/dist/context/task-summary.js +94 -23
- package/dist/context/task-summary.js.map +1 -1
- package/dist/context/working-header.d.ts +29 -0
- package/dist/context/working-header.js +125 -0
- package/dist/context/working-header.js.map +1 -0
- package/dist/doctor/index.d.ts +10 -4
- package/dist/doctor/index.js +24 -11
- package/dist/doctor/index.js.map +1 -1
- package/dist/gc/completion-integrity.d.ts +0 -1
- package/dist/gc/completion-integrity.js +0 -6
- package/dist/gc/completion-integrity.js.map +1 -1
- package/dist/hooks/post-tool-use/index.d.ts +1 -1
- package/dist/hooks/post-tool-use/index.js +1 -1
- package/dist/hooks/post-tool-use/index.js.map +1 -1
- package/dist/hooks/post-tool-use/post-write.js +16 -0
- package/dist/hooks/post-tool-use/post-write.js.map +1 -1
- package/dist/hooks/post-tool-use/read-enricher.js +96 -4
- package/dist/hooks/post-tool-use/read-enricher.js.map +1 -1
- package/dist/hooks/post-tool-use/sot-align.js +52 -21
- package/dist/hooks/post-tool-use/sot-align.js.map +1 -1
- package/dist/hooks/runners/annotate-surface.d.ts +41 -0
- package/dist/hooks/runners/annotate-surface.js +152 -0
- package/dist/hooks/runners/annotate-surface.js.map +1 -0
- package/dist/hooks/runners/stop.js +26 -0
- package/dist/hooks/runners/stop.js.map +1 -1
- package/dist/hooks/runners/user-prompt-submit.js +59 -9
- package/dist/hooks/runners/user-prompt-submit.js.map +1 -1
- package/dist/hooks/sot-align-common.d.ts +13 -0
- package/dist/hooks/sot-align-common.js +69 -0
- package/dist/hooks/sot-align-common.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/init/claude-rule.d.ts +1 -0
- package/dist/init/claude-rule.js +1 -1
- package/dist/init/claude-rule.js.map +1 -1
- package/dist/init/source-comments/ingest.js +11 -8
- package/dist/init/source-comments/ingest.js.map +1 -1
- package/dist/invariants/prune.d.ts +50 -0
- package/dist/invariants/prune.js +113 -0
- package/dist/invariants/prune.js.map +1 -0
- package/dist/mcp/schemas.d.ts +27 -1
- package/dist/mcp/schemas.js +22 -0
- package/dist/mcp/schemas.js.map +1 -1
- package/dist/mcp/tools/component-annotate.d.ts +33 -0
- package/dist/mcp/tools/component-annotate.js +189 -0
- package/dist/mcp/tools/component-annotate.js.map +1 -0
- package/dist/mcp/tools/index.js +3 -1
- package/dist/mcp/tools/index.js.map +1 -1
- package/dist/mcp/tools/resume.js +10 -34
- package/dist/mcp/tools/resume.js.map +1 -1
- package/dist/session/index.d.ts +2 -0
- package/dist/session/index.js +2 -0
- package/dist/session/index.js.map +1 -1
- package/dist/session/seen.d.ts +37 -0
- package/dist/session/seen.js +110 -0
- package/dist/session/seen.js.map +1 -0
- package/dist/session/touched.d.ts +17 -0
- package/dist/session/touched.js +57 -0
- package/dist/session/touched.js.map +1 -0
- package/dist/tasks/spec-reader.d.ts +27 -0
- package/dist/tasks/spec-reader.js +60 -0
- package/dist/tasks/spec-reader.js.map +1 -0
- package/dist/uninstall/index.d.ts +44 -0
- package/dist/uninstall/index.js +185 -0
- package/dist/uninstall/index.js.map +1 -0
- package/package.json +2 -2
- package/templates/.cairn/config/sensors.yaml +10 -3
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inline cite expander — the inverse of sot-align's strip-replace.
|
|
3
|
+
*
|
|
4
|
+
* sot-align replaces a prose block with a bare `// §DEC-<hash>` /
|
|
5
|
+
* `// §INV-<hash>` citation. This expands such a citation back into the
|
|
6
|
+
* entity's body, rendered as a plain comment in the file's own comment
|
|
7
|
+
* style. Two uses:
|
|
8
|
+
*
|
|
9
|
+
* 1. Uninstall — un-cite a repo so removing `.cairn/` leaves the source
|
|
10
|
+
* self-documenting, with no dangling `§DEC-/§INV-` references.
|
|
11
|
+
* 2. General "expand cites" tooling.
|
|
12
|
+
*
|
|
13
|
+
* Only a PURE cite line — one whose entire content (after the comment
|
|
14
|
+
* leader) is citation tokens — is expanded. A citation that shares a line
|
|
15
|
+
* with code is left untouched (counted as `inlineSkipped`); a citation
|
|
16
|
+
* whose entity is missing on disk is left as-is (`danglingSkipped`).
|
|
17
|
+
*/
|
|
18
|
+
export interface ExpandResult {
|
|
19
|
+
text: string;
|
|
20
|
+
/** Citations replaced with their entity body. */
|
|
21
|
+
expanded: number;
|
|
22
|
+
/** Citation ids with no entity on disk — left in place. */
|
|
23
|
+
danglingSkipped: number;
|
|
24
|
+
/** Citations sharing a line with code — left in place. */
|
|
25
|
+
inlineSkipped: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Pure transform: expand every pure-cite line in `text`. `resolve` returns
|
|
29
|
+
* an entity body (post-frontmatter) by id, or null when it doesn't exist.
|
|
30
|
+
*/
|
|
31
|
+
export declare function expandCitesInText(text: string, resolve: (id: string) => string | null): ExpandResult;
|
|
32
|
+
export interface ExpandCitesFileOptions {
|
|
33
|
+
repoRoot: string;
|
|
34
|
+
/** Repo-relative path. */
|
|
35
|
+
filePath: string;
|
|
36
|
+
/** When true, compute the result but don't write. */
|
|
37
|
+
dryRun?: boolean;
|
|
38
|
+
}
|
|
39
|
+
export interface ExpandCitesFileResult extends ExpandResult {
|
|
40
|
+
filePath: string;
|
|
41
|
+
changed: boolean;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Expand every pure-cite line in one source file, resolving ids against the
|
|
45
|
+
* live `.cairn/ground/{decisions,invariants}/` store. Writes in place unless
|
|
46
|
+
* `dryRun`.
|
|
47
|
+
*/
|
|
48
|
+
export declare function expandCitesInFile(opts: ExpandCitesFileOptions): ExpandCitesFileResult;
|
|
49
|
+
/**
|
|
50
|
+
* Walk the working tree for files that contain a `§DEC-/§INV-` token. The
|
|
51
|
+
* source of truth for un-citing is the source itself, NOT the scope-index —
|
|
52
|
+
* a stale/missing scope-index would silently leave dangling cites behind.
|
|
53
|
+
*/
|
|
54
|
+
export declare function findCitedFiles(repoRoot: string): string[];
|
|
55
|
+
export interface ExpandCitesRepoOptions {
|
|
56
|
+
repoRoot: string;
|
|
57
|
+
/**
|
|
58
|
+
* Explicit repo-relative files to expand. When omitted, the working tree
|
|
59
|
+
* is scanned for every file carrying a `§DEC-/§INV-` token.
|
|
60
|
+
*/
|
|
61
|
+
files?: string[];
|
|
62
|
+
dryRun?: boolean;
|
|
63
|
+
}
|
|
64
|
+
export interface ExpandCitesRepoResult {
|
|
65
|
+
files: ExpandCitesFileResult[];
|
|
66
|
+
filesChanged: number;
|
|
67
|
+
expanded: number;
|
|
68
|
+
danglingSkipped: number;
|
|
69
|
+
inlineSkipped: number;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Expand cites across many files. Defaults to the scope-index's cited-file
|
|
73
|
+
* set; pass `files` to target a subset (or when the scope-index is absent).
|
|
74
|
+
*/
|
|
75
|
+
export declare function expandCitesInRepo(opts: ExpandCitesRepoOptions): ExpandCitesRepoResult;
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inline cite expander — the inverse of sot-align's strip-replace.
|
|
3
|
+
*
|
|
4
|
+
* sot-align replaces a prose block with a bare `// §DEC-<hash>` /
|
|
5
|
+
* `// §INV-<hash>` citation. This expands such a citation back into the
|
|
6
|
+
* entity's body, rendered as a plain comment in the file's own comment
|
|
7
|
+
* style. Two uses:
|
|
8
|
+
*
|
|
9
|
+
* 1. Uninstall — un-cite a repo so removing `.cairn/` leaves the source
|
|
10
|
+
* self-documenting, with no dangling `§DEC-/§INV-` references.
|
|
11
|
+
* 2. General "expand cites" tooling.
|
|
12
|
+
*
|
|
13
|
+
* Only a PURE cite line — one whose entire content (after the comment
|
|
14
|
+
* leader) is citation tokens — is expanded. A citation that shares a line
|
|
15
|
+
* with code is left untouched (counted as `inlineSkipped`); a citation
|
|
16
|
+
* whose entity is missing on disk is left as-is (`danglingSkipped`).
|
|
17
|
+
*/
|
|
18
|
+
import { existsSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
19
|
+
import { join } from "node:path";
|
|
20
|
+
import { readEntityBody } from "../hooks/sot-align-common.js";
|
|
21
|
+
const CITE_RE = /§(DEC|INV)-([0-9a-f]{7,})/g;
|
|
22
|
+
/** A line that, with all cite tokens removed, is only indent + comment leader. */
|
|
23
|
+
const PURE_CITE_LEADER_RE = /^(\s*)(\/\/+|#+|;+|--+|\*)?\s*$/;
|
|
24
|
+
/**
|
|
25
|
+
* Pure transform: expand every pure-cite line in `text`. `resolve` returns
|
|
26
|
+
* an entity body (post-frontmatter) by id, or null when it doesn't exist.
|
|
27
|
+
*/
|
|
28
|
+
export function expandCitesInText(text, resolve) {
|
|
29
|
+
let expanded = 0;
|
|
30
|
+
let danglingSkipped = 0;
|
|
31
|
+
let inlineSkipped = 0;
|
|
32
|
+
const eol = text.includes("\r\n") ? "\r\n" : "\n";
|
|
33
|
+
const lines = text.split(/\r?\n/);
|
|
34
|
+
const out = [];
|
|
35
|
+
for (const line of lines) {
|
|
36
|
+
const cites = [...line.matchAll(CITE_RE)];
|
|
37
|
+
if (cites.length === 0) {
|
|
38
|
+
out.push(line);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const stripped = line.replace(CITE_RE, "").replace(/\s+$/, "");
|
|
42
|
+
const leaderMatch = stripped.match(PURE_CITE_LEADER_RE);
|
|
43
|
+
if (leaderMatch === null) {
|
|
44
|
+
// Citation shares the line with code — don't touch it.
|
|
45
|
+
inlineSkipped += cites.length;
|
|
46
|
+
out.push(line);
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const indent = leaderMatch[1] ?? "";
|
|
50
|
+
const leader = leaderMatch[2] ?? "//";
|
|
51
|
+
const replacement = [];
|
|
52
|
+
let expandedAny = false;
|
|
53
|
+
cites.forEach((m, idx) => {
|
|
54
|
+
const id = `${m[1]}-${m[2]}`;
|
|
55
|
+
const body = resolve(id);
|
|
56
|
+
if (body === null) {
|
|
57
|
+
danglingSkipped += 1;
|
|
58
|
+
replacement.push(`${indent}${leader} §${id}`); // keep dangling cite verbatim
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (expandedAny)
|
|
62
|
+
replacement.push(`${indent}${leader}`); // separator between bodies
|
|
63
|
+
for (const bl of body.split(/\r?\n/)) {
|
|
64
|
+
replacement.push(bl.length > 0 ? `${indent}${leader} ${bl}` : `${indent}${leader}`);
|
|
65
|
+
}
|
|
66
|
+
expanded += 1;
|
|
67
|
+
expandedAny = true;
|
|
68
|
+
void idx;
|
|
69
|
+
});
|
|
70
|
+
out.push(...replacement);
|
|
71
|
+
}
|
|
72
|
+
return { text: out.join(eol), expanded, danglingSkipped, inlineSkipped };
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Expand every pure-cite line in one source file, resolving ids against the
|
|
76
|
+
* live `.cairn/ground/{decisions,invariants}/` store. Writes in place unless
|
|
77
|
+
* `dryRun`.
|
|
78
|
+
*/
|
|
79
|
+
export function expandCitesInFile(opts) {
|
|
80
|
+
const abs = join(opts.repoRoot, opts.filePath);
|
|
81
|
+
const empty = {
|
|
82
|
+
filePath: opts.filePath,
|
|
83
|
+
text: "",
|
|
84
|
+
expanded: 0,
|
|
85
|
+
danglingSkipped: 0,
|
|
86
|
+
inlineSkipped: 0,
|
|
87
|
+
changed: false,
|
|
88
|
+
};
|
|
89
|
+
if (!existsSync(abs))
|
|
90
|
+
return empty;
|
|
91
|
+
let source;
|
|
92
|
+
try {
|
|
93
|
+
source = readFileSync(abs, "utf8");
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return empty;
|
|
97
|
+
}
|
|
98
|
+
const result = expandCitesInText(source, (id) => readEntityBody(opts.repoRoot, id));
|
|
99
|
+
const changed = result.text !== source;
|
|
100
|
+
if (changed && opts.dryRun !== true) {
|
|
101
|
+
writeFileSync(abs, result.text, "utf8");
|
|
102
|
+
}
|
|
103
|
+
return { ...result, filePath: opts.filePath, changed };
|
|
104
|
+
}
|
|
105
|
+
/** Dirs the cited-file scan never descends into. */
|
|
106
|
+
const SCAN_SKIP_DIRS = new Set([
|
|
107
|
+
".git",
|
|
108
|
+
".cairn",
|
|
109
|
+
"node_modules",
|
|
110
|
+
"dist",
|
|
111
|
+
"build",
|
|
112
|
+
"out",
|
|
113
|
+
"coverage",
|
|
114
|
+
".next",
|
|
115
|
+
".turbo",
|
|
116
|
+
".vercel",
|
|
117
|
+
]);
|
|
118
|
+
const CITE_SCAN_RE = /§(?:DEC|INV)-[0-9a-f]{7,}/;
|
|
119
|
+
const SCAN_SKIP_EXT = new Set([
|
|
120
|
+
".png", ".jpg", ".jpeg", ".gif", ".webp", ".ico", ".pdf", ".zip", ".gz",
|
|
121
|
+
".woff", ".woff2", ".ttf", ".eot", ".mp4", ".mov", ".wasm", ".lock", ".map",
|
|
122
|
+
]);
|
|
123
|
+
const SCAN_MAX_BYTES = 2_000_000;
|
|
124
|
+
/**
|
|
125
|
+
* Walk the working tree for files that contain a `§DEC-/§INV-` token. The
|
|
126
|
+
* source of truth for un-citing is the source itself, NOT the scope-index —
|
|
127
|
+
* a stale/missing scope-index would silently leave dangling cites behind.
|
|
128
|
+
*/
|
|
129
|
+
export function findCitedFiles(repoRoot) {
|
|
130
|
+
const out = [];
|
|
131
|
+
const walk = (absDir, relDir) => {
|
|
132
|
+
let entries;
|
|
133
|
+
try {
|
|
134
|
+
entries = readdirSync(absDir, { withFileTypes: true });
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
for (const e of entries) {
|
|
140
|
+
const rel = relDir.length > 0 ? `${relDir}/${e.name}` : e.name;
|
|
141
|
+
if (e.isDirectory()) {
|
|
142
|
+
if (SCAN_SKIP_DIRS.has(e.name))
|
|
143
|
+
continue;
|
|
144
|
+
walk(join(absDir, e.name), rel);
|
|
145
|
+
}
|
|
146
|
+
else if (e.isFile()) {
|
|
147
|
+
const dot = e.name.lastIndexOf(".");
|
|
148
|
+
if (dot !== -1 && SCAN_SKIP_EXT.has(e.name.slice(dot).toLowerCase()))
|
|
149
|
+
continue;
|
|
150
|
+
const abs = join(absDir, e.name);
|
|
151
|
+
try {
|
|
152
|
+
if (statSync(abs).size > SCAN_MAX_BYTES)
|
|
153
|
+
continue;
|
|
154
|
+
if (CITE_SCAN_RE.test(readFileSync(abs, "utf8")))
|
|
155
|
+
out.push(rel);
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
/* unreadable — skip */
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
walk(repoRoot, "");
|
|
164
|
+
return out;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Expand cites across many files. Defaults to the scope-index's cited-file
|
|
168
|
+
* set; pass `files` to target a subset (or when the scope-index is absent).
|
|
169
|
+
*/
|
|
170
|
+
export function expandCitesInRepo(opts) {
|
|
171
|
+
const targets = opts.files ?? findCitedFiles(opts.repoRoot);
|
|
172
|
+
const out = {
|
|
173
|
+
files: [],
|
|
174
|
+
filesChanged: 0,
|
|
175
|
+
expanded: 0,
|
|
176
|
+
danglingSkipped: 0,
|
|
177
|
+
inlineSkipped: 0,
|
|
178
|
+
};
|
|
179
|
+
for (const filePath of targets) {
|
|
180
|
+
const r = expandCitesInFile({
|
|
181
|
+
repoRoot: opts.repoRoot,
|
|
182
|
+
filePath,
|
|
183
|
+
...(opts.dryRun !== undefined ? { dryRun: opts.dryRun } : {}),
|
|
184
|
+
});
|
|
185
|
+
if (r.expanded === 0 && r.danglingSkipped === 0 && r.inlineSkipped === 0) {
|
|
186
|
+
continue; // no cites in this file
|
|
187
|
+
}
|
|
188
|
+
out.files.push(r);
|
|
189
|
+
if (r.changed)
|
|
190
|
+
out.filesChanged += 1;
|
|
191
|
+
out.expanded += r.expanded;
|
|
192
|
+
out.danglingSkipped += r.danglingSkipped;
|
|
193
|
+
out.inlineSkipped += r.inlineSkipped;
|
|
194
|
+
}
|
|
195
|
+
return out;
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=expand.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expand.js","sourceRoot":"","sources":["../../src/cites/expand.ts"],"names":[],"mappings":"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"}
|
package/dist/context/index.d.ts
CHANGED
|
@@ -9,3 +9,5 @@ export { buildSpecDelta } from "./spec-delta.js";
|
|
|
9
9
|
export type { SpecDelta } from "./spec-delta.js";
|
|
10
10
|
export { readActiveTaskSummary } from "./task-summary.js";
|
|
11
11
|
export type { ActiveTaskSummary } from "./task-summary.js";
|
|
12
|
+
export { buildWorkingHeader } from "./working-header.js";
|
|
13
|
+
export type { WorkingHeader } from "./working-header.js";
|
package/dist/context/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/context/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/context/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -16,3 +16,21 @@ export interface ActiveTaskSummary {
|
|
|
16
16
|
taskModule: string;
|
|
17
17
|
}
|
|
18
18
|
export declare function readActiveTaskSummary(repoRoot: string): ActiveTaskSummary | null;
|
|
19
|
+
/**
|
|
20
|
+
* Summary for one specific active task id, or null when it is not
|
|
21
|
+
* in-flight. Used by the working-context header to render the task this
|
|
22
|
+
* session is actually on (resolved by session affinity) rather than the
|
|
23
|
+
* first/arbitrary active task.
|
|
24
|
+
*/
|
|
25
|
+
export declare function readTaskSummaryById(repoRoot: string, taskId: string): ActiveTaskSummary | null;
|
|
26
|
+
/**
|
|
27
|
+
* Resolve which active task THIS session is on, for multi-task /
|
|
28
|
+
* multi-window correctness. Among in-flight tasks, prefer one this
|
|
29
|
+
* session created or last journaled (`created_by_session` /
|
|
30
|
+
* `last_journal_session` on status.yaml); fall back to the most-recently
|
|
31
|
+
* touched active task. Returns null when nothing is in flight.
|
|
32
|
+
*
|
|
33
|
+
* Without this, two Claude windows on one checkout would both surface
|
|
34
|
+
* whichever task was globally first/most-recent — wrong frame.
|
|
35
|
+
*/
|
|
36
|
+
export declare function resolveSessionTaskId(repoRoot: string, sessionId: string | null): string | null;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* unrecognized values collapse to `idle` so the surface stays quiet
|
|
9
9
|
* when nothing is actually in flight.
|
|
10
10
|
*/
|
|
11
|
-
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
11
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
12
12
|
import { join } from "node:path";
|
|
13
13
|
import { parse as parseYaml } from "yaml";
|
|
14
14
|
import { cairnDir, parseFrontmatter } from "@isaacriehm/cairn-state";
|
|
@@ -52,8 +52,48 @@ export function readActiveTaskSummary(repoRoot) {
|
|
|
52
52
|
for (const e of dirents) {
|
|
53
53
|
if (!e.isDirectory())
|
|
54
54
|
continue;
|
|
55
|
-
const
|
|
56
|
-
|
|
55
|
+
const summary = summarizeActiveTask(join(activeDir, e.name), e.name);
|
|
56
|
+
if (summary !== null)
|
|
57
|
+
return summary;
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Summary for one specific active task id, or null when it is not
|
|
63
|
+
* in-flight. Used by the working-context header to render the task this
|
|
64
|
+
* session is actually on (resolved by session affinity) rather than the
|
|
65
|
+
* first/arbitrary active task.
|
|
66
|
+
*/
|
|
67
|
+
export function readTaskSummaryById(repoRoot, taskId) {
|
|
68
|
+
return summarizeActiveTask(cairnDir(repoRoot, "tasks", "active", taskId), taskId);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Resolve which active task THIS session is on, for multi-task /
|
|
72
|
+
* multi-window correctness. Among in-flight tasks, prefer one this
|
|
73
|
+
* session created or last journaled (`created_by_session` /
|
|
74
|
+
* `last_journal_session` on status.yaml); fall back to the most-recently
|
|
75
|
+
* touched active task. Returns null when nothing is in flight.
|
|
76
|
+
*
|
|
77
|
+
* Without this, two Claude windows on one checkout would both surface
|
|
78
|
+
* whichever task was globally first/most-recent — wrong frame.
|
|
79
|
+
*/
|
|
80
|
+
export function resolveSessionTaskId(repoRoot, sessionId) {
|
|
81
|
+
const activeDir = cairnDir(repoRoot, "tasks", "active");
|
|
82
|
+
if (!existsSync(activeDir))
|
|
83
|
+
return null;
|
|
84
|
+
let dirents;
|
|
85
|
+
try {
|
|
86
|
+
dirents = readdirSync(activeDir, { withFileTypes: true, encoding: "utf8" });
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
const sid = sessionId !== null && sessionId.length > 0 ? sessionId : null;
|
|
92
|
+
const candidates = [];
|
|
93
|
+
for (const e of dirents) {
|
|
94
|
+
if (!e.isDirectory())
|
|
95
|
+
continue;
|
|
96
|
+
const statusPath = join(activeDir, e.name, "status.yaml");
|
|
57
97
|
if (!existsSync(statusPath))
|
|
58
98
|
continue;
|
|
59
99
|
let parsed;
|
|
@@ -65,29 +105,60 @@ export function readActiveTaskSummary(repoRoot) {
|
|
|
65
105
|
}
|
|
66
106
|
if (typeof parsed !== "object" || parsed === null)
|
|
67
107
|
continue;
|
|
68
|
-
const
|
|
108
|
+
const o = parsed;
|
|
109
|
+
const phase = o["phase"];
|
|
69
110
|
if (typeof phase !== "string" || !ACTIVE_PHASES.has(phase))
|
|
70
111
|
continue;
|
|
71
|
-
let
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
try {
|
|
75
|
-
const specText = readFileSync(specPath, "utf8");
|
|
76
|
-
const body = parseFrontmatter(specText).body;
|
|
77
|
-
const m = body.match(/^#\s+(.+)$/m);
|
|
78
|
-
if (m && m[1])
|
|
79
|
-
title = m[1].trim();
|
|
80
|
-
}
|
|
81
|
-
catch {
|
|
82
|
-
// fall through to taskId
|
|
83
|
-
}
|
|
112
|
+
let mtimeMs = 0;
|
|
113
|
+
try {
|
|
114
|
+
mtimeMs = statSync(statusPath).mtimeMs;
|
|
84
115
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
116
|
+
catch {
|
|
117
|
+
// keep 0 — undated tasks sort last
|
|
118
|
+
}
|
|
119
|
+
const owned = sid !== null &&
|
|
120
|
+
(o["last_journal_session"] === sid || o["created_by_session"] === sid);
|
|
121
|
+
candidates.push({ taskId: e.name, mtimeMs, owned });
|
|
90
122
|
}
|
|
91
|
-
|
|
123
|
+
if (candidates.length === 0)
|
|
124
|
+
return null;
|
|
125
|
+
const byRecent = (a, b) => b.mtimeMs - a.mtimeMs;
|
|
126
|
+
const owned = candidates.filter((c) => c.owned).sort(byRecent);
|
|
127
|
+
if (owned.length > 0)
|
|
128
|
+
return owned[0].taskId;
|
|
129
|
+
return candidates.sort(byRecent)[0].taskId;
|
|
130
|
+
}
|
|
131
|
+
/** Read one active task dir into a summary, or null when not in-flight. */
|
|
132
|
+
function summarizeActiveTask(taskDir, taskId) {
|
|
133
|
+
const statusPath = join(taskDir, "status.yaml");
|
|
134
|
+
if (!existsSync(statusPath))
|
|
135
|
+
return null;
|
|
136
|
+
let parsed;
|
|
137
|
+
try {
|
|
138
|
+
parsed = parseYaml(readFileSync(statusPath, "utf8"));
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
if (typeof parsed !== "object" || parsed === null)
|
|
144
|
+
return null;
|
|
145
|
+
const phase = parsed.phase;
|
|
146
|
+
if (typeof phase !== "string" || !ACTIVE_PHASES.has(phase))
|
|
147
|
+
return null;
|
|
148
|
+
let title = taskId;
|
|
149
|
+
const specPath = join(taskDir, "spec.tightened.md");
|
|
150
|
+
if (existsSync(specPath)) {
|
|
151
|
+
try {
|
|
152
|
+
const specText = readFileSync(specPath, "utf8");
|
|
153
|
+
const body = parseFrontmatter(specText).body;
|
|
154
|
+
const m = body.match(/^#\s+(.+)$/m);
|
|
155
|
+
if (m && m[1])
|
|
156
|
+
title = m[1].trim();
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
// fall through to taskId
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return { taskId, taskState: mapPhase(phase), taskModule: title };
|
|
92
163
|
}
|
|
93
164
|
//# sourceMappingURL=task-summary.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-summary.js","sourceRoot":"","sources":["../../src/context/task-summary.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAe,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"task-summary.js","sourceRoot":"","sources":["../../src/context/task-summary.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Working-context header — the context engine's stage-1 surface.
|
|
3
|
+
*
|
|
4
|
+
* Per prompt (UserPromptSubmit), the server injects a compact "what am
|
|
5
|
+
* I working on" frame so the agent never has to call `cairn_in_scope` /
|
|
6
|
+
* `cairn_mission_get` to know its frame. This module builds that text +
|
|
7
|
+
* a fingerprint; the runner injects it only when the fingerprint
|
|
8
|
+
* changed (per-session dedup via session/seen.ts).
|
|
9
|
+
*
|
|
10
|
+
* The header is the PERSISTENT in-scope id INDEX (every prompt) — ids
|
|
11
|
+
* only, no bodies. The stage-2 read-enricher shows each DEC/INV/
|
|
12
|
+
* component BODY once per session. So the agent always knows WHAT is in
|
|
13
|
+
* scope (this header) even after a body scrolled off (D13).
|
|
14
|
+
*
|
|
15
|
+
* Pure-FS, zero LLM — safe to run on every prompt inside a hook.
|
|
16
|
+
*
|
|
17
|
+
* Spec: docs/CONTEXT_ENGINE.md (stage 1), CAIRN_REBUILD §6 / D11–D13.
|
|
18
|
+
*/
|
|
19
|
+
export interface WorkingHeader {
|
|
20
|
+
text: string;
|
|
21
|
+
fingerprint: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Build the working-context header for the repo's current frame, or
|
|
25
|
+
* null when there is no active task AND no active mission (D11 — inject
|
|
26
|
+
* nothing extra). When a task is active but has no in-scope ids, the
|
|
27
|
+
* "In scope" line is omitted (active-task line only).
|
|
28
|
+
*/
|
|
29
|
+
export declare function buildWorkingHeader(repoRoot: string, sessionId: string | null): WorkingHeader | null;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Working-context header — the context engine's stage-1 surface.
|
|
3
|
+
*
|
|
4
|
+
* Per prompt (UserPromptSubmit), the server injects a compact "what am
|
|
5
|
+
* I working on" frame so the agent never has to call `cairn_in_scope` /
|
|
6
|
+
* `cairn_mission_get` to know its frame. This module builds that text +
|
|
7
|
+
* a fingerprint; the runner injects it only when the fingerprint
|
|
8
|
+
* changed (per-session dedup via session/seen.ts).
|
|
9
|
+
*
|
|
10
|
+
* The header is the PERSISTENT in-scope id INDEX (every prompt) — ids
|
|
11
|
+
* only, no bodies. The stage-2 read-enricher shows each DEC/INV/
|
|
12
|
+
* component BODY once per session. So the agent always knows WHAT is in
|
|
13
|
+
* scope (this header) even after a body scrolled off (D13).
|
|
14
|
+
*
|
|
15
|
+
* Pure-FS, zero LLM — safe to run on every prompt inside a hook.
|
|
16
|
+
*
|
|
17
|
+
* Spec: docs/CONTEXT_ENGINE.md (stage 1), CAIRN_REBUILD §6 / D11–D13.
|
|
18
|
+
*/
|
|
19
|
+
import { cairnDir, findActiveMission, readMissionState, readRoadmap, } from "@isaacriehm/cairn-state";
|
|
20
|
+
import { resolveSessionTaskId, readTaskSummaryById } from "./task-summary.js";
|
|
21
|
+
import { readTaskMissionAnchor } from "../missions/index.js";
|
|
22
|
+
import { readTaskSpec } from "../tasks/spec-reader.js";
|
|
23
|
+
import { fingerprintText } from "../session/seen.js";
|
|
24
|
+
const GOAL_MAX = 120;
|
|
25
|
+
const ID_LIST_CAP = 12;
|
|
26
|
+
/** Truncate at a word boundary, appending `…` when cut. */
|
|
27
|
+
function truncateAtWord(s, max) {
|
|
28
|
+
const flat = s.replace(/\s+/g, " ").trim();
|
|
29
|
+
if (flat.length <= max)
|
|
30
|
+
return flat;
|
|
31
|
+
const slice = flat.slice(0, max);
|
|
32
|
+
const lastSpace = slice.lastIndexOf(" ");
|
|
33
|
+
const cut = lastSpace > max * 0.6 ? slice.slice(0, lastSpace) : slice;
|
|
34
|
+
return `${cut.trimEnd()}…`;
|
|
35
|
+
}
|
|
36
|
+
/** Render up to `cap` ids, then `(+N more)`. Empty → "". */
|
|
37
|
+
function renderIdList(ids, cap) {
|
|
38
|
+
if (ids.length === 0)
|
|
39
|
+
return "";
|
|
40
|
+
const head = ids.slice(0, cap);
|
|
41
|
+
const extra = ids.length - head.length;
|
|
42
|
+
const base = head.join(", ");
|
|
43
|
+
return extra > 0 ? `${base} (+${extra} more)` : base;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Build the working-context header for the repo's current frame, or
|
|
47
|
+
* null when there is no active task AND no active mission (D11 — inject
|
|
48
|
+
* nothing extra). When a task is active but has no in-scope ids, the
|
|
49
|
+
* "In scope" line is omitted (active-task line only).
|
|
50
|
+
*/
|
|
51
|
+
export function buildWorkingHeader(repoRoot, sessionId) {
|
|
52
|
+
// Resolve the task THIS session is on (affinity), not a global pick —
|
|
53
|
+
// correct when multiple tasks are active across windows.
|
|
54
|
+
const taskId = resolveSessionTaskId(repoRoot, sessionId);
|
|
55
|
+
const active = taskId !== null ? readTaskSummaryById(repoRoot, taskId) : null;
|
|
56
|
+
// Mission: prefer the resolved task's own anchor (the mission + phase
|
|
57
|
+
// this session is working) over a global scan — correct when several
|
|
58
|
+
// missions are active. Fall back to the single active mission only
|
|
59
|
+
// when there's no task to anchor on.
|
|
60
|
+
const anchor = active !== null ? readTaskMissionAnchor(repoRoot, active.taskId) : null;
|
|
61
|
+
const missionId = anchor?.mission_id ?? findActiveMission(repoRoot);
|
|
62
|
+
const preferredPhaseId = anchor?.phase_id ?? null;
|
|
63
|
+
if (active === null && missionId === null)
|
|
64
|
+
return null;
|
|
65
|
+
const lines = ["## Cairn — working context"];
|
|
66
|
+
let hasInScope = false;
|
|
67
|
+
if (active !== null) {
|
|
68
|
+
const spec = readTaskSpec(cairnDir(repoRoot, "tasks", "active", active.taskId));
|
|
69
|
+
const goal = spec !== null && spec.goal.length > 0 ? spec.goal : "";
|
|
70
|
+
const parts = [`Active: ${active.taskId} (${active.taskState})`];
|
|
71
|
+
if (goal.length > 0)
|
|
72
|
+
parts.push(`— ${truncateAtWord(goal, GOAL_MAX)}`);
|
|
73
|
+
if (active.taskModule.length > 0 && active.taskModule !== active.taskId) {
|
|
74
|
+
parts.push(`· module ${active.taskModule}`);
|
|
75
|
+
}
|
|
76
|
+
lines.push(parts.join(" "));
|
|
77
|
+
const decs = spec?.inScopeDecisions ?? [];
|
|
78
|
+
const invs = spec?.inScopeInvariants ?? [];
|
|
79
|
+
if (decs.length > 0 || invs.length > 0) {
|
|
80
|
+
hasInScope = true;
|
|
81
|
+
const segs = [];
|
|
82
|
+
if (decs.length > 0)
|
|
83
|
+
segs.push(renderIdList(decs, ID_LIST_CAP));
|
|
84
|
+
if (invs.length > 0)
|
|
85
|
+
segs.push(renderIdList(invs, ID_LIST_CAP));
|
|
86
|
+
lines.push(`In scope: ${segs.join(" · ")}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (missionId !== null) {
|
|
90
|
+
const missionLine = renderMissionLine(repoRoot, missionId, preferredPhaseId);
|
|
91
|
+
if (missionLine !== null)
|
|
92
|
+
lines.push(missionLine);
|
|
93
|
+
}
|
|
94
|
+
if (hasInScope) {
|
|
95
|
+
lines.push("Bodies on demand: cairn_decision_get / cairn_invariant_get.");
|
|
96
|
+
}
|
|
97
|
+
const text = lines.join("\n");
|
|
98
|
+
return { text, fingerprint: fingerprintText(text) };
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* `Mission: <title> — phase <i>/<n> "<phase title>"`. Degrades to a
|
|
102
|
+
* title-only line when the cursor phase can't be located, and to null
|
|
103
|
+
* when the mission state/roadmap is unreadable (D-graceful: never throw).
|
|
104
|
+
*/
|
|
105
|
+
function renderMissionLine(repoRoot, missionId, preferredPhaseId) {
|
|
106
|
+
const roadmap = readRoadmap(repoRoot, missionId);
|
|
107
|
+
const state = readMissionState(repoRoot, missionId);
|
|
108
|
+
if (roadmap === null)
|
|
109
|
+
return null;
|
|
110
|
+
const title = roadmap.frontmatter.title;
|
|
111
|
+
const phases = roadmap.frontmatter.phases;
|
|
112
|
+
// The task anchor's phase wins (the phase THIS session is working);
|
|
113
|
+
// fall back to the mission cursor when there's no task anchor.
|
|
114
|
+
const activePhase = preferredPhaseId ?? state?.cursor.active_phase ?? null;
|
|
115
|
+
if (activePhase !== null) {
|
|
116
|
+
const idx = phases.findIndex((p) => p.id === activePhase);
|
|
117
|
+
if (idx >= 0) {
|
|
118
|
+
const phase = phases[idx];
|
|
119
|
+
const phaseTitle = phase !== undefined ? phase.title : activePhase;
|
|
120
|
+
return `Mission: ${title} — phase ${idx + 1}/${phases.length} "${phaseTitle}"`;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return `Mission: ${title}`;
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=working-header.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"working-header.js","sourceRoot":"","sources":["../../src/context/working-header.ts"],"names":[],"mappings":"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"}
|