@isaacriehm/cairn-core 0.18.2 → 0.19.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/dedup.d.ts +24 -0
- package/dist/attention/dedup.js +42 -1
- package/dist/attention/dedup.js.map +1 -1
- package/dist/components/audit.d.ts +13 -2
- package/dist/components/audit.js +111 -14
- package/dist/components/audit.js.map +1 -1
- package/dist/init/detect-components.d.ts +33 -27
- package/dist/init/detect-components.js +231 -130
- package/dist/init/detect-components.js.map +1 -1
- package/dist/init/phases/4-seed.js +5 -5
- package/dist/init/phases/4-seed.js.map +1 -1
- package/dist/mcp/tools/record-decision.js +43 -5
- package/dist/mcp/tools/record-decision.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,153 +1,255 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* LLM-driven, convention-agnostic component-layout detection.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Cairn adoption ALWAYS runs inside an LLM coding agent, so detection
|
|
5
|
+
* leans on a model rather than a hardcoded convention list — there is no
|
|
6
|
+
* `src/components` / `packages/*` assumption baked in. A Sonnet call reads
|
|
7
|
+
* the repo's structural digest (per-directory file-extension histogram,
|
|
8
|
+
* the dirs that hold a `package.json`, and any workspace-manifest files)
|
|
9
|
+
* and returns the `components:` config: which workspaces carry reusable
|
|
10
|
+
* UI, where their component dirs live, the extensions in play, and a
|
|
11
|
+
* taxonomy that fits THAT workspace. A non-UI repo (a backend with no
|
|
12
|
+
* components) returns null and is left untouched.
|
|
8
13
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
14
|
+
* Only mechanical, repo-agnostic facts stay deterministic: the file walk
|
|
15
|
+
* and the universal build-output exclude list. Everything that requires
|
|
16
|
+
* understanding "what is this directory for" is the model's job — naming,
|
|
17
|
+
* monorepo tooling, framework, and taxonomy are all inferred, never
|
|
18
|
+
* assumed.
|
|
19
|
+
*
|
|
20
|
+
* Isolation invariant (port invariant 3): a workspace is NEVER emitted as
|
|
21
|
+
* `shared`. The flag is omitted (it normalizes to isolated); the operator
|
|
22
|
+
* opts in afterward (the skill asks).
|
|
13
23
|
*/
|
|
14
|
-
import { existsSync, readFileSync,
|
|
15
|
-
import {
|
|
24
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
25
|
+
import { join } from "node:path";
|
|
16
26
|
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
27
|
+
import { z } from "zod";
|
|
28
|
+
import { DEFAULT_EXCLUDE, walkFs, } from "@isaacriehm/cairn-state";
|
|
29
|
+
import { runClaude } from "../claude/index.js";
|
|
30
|
+
import { logger } from "../logger.js";
|
|
31
|
+
const log = logger("init.detect-components");
|
|
32
|
+
const TIMEOUT_MS = 120_000;
|
|
33
|
+
/** Cap the directory histogram so a huge repo can't blow the prompt. */
|
|
34
|
+
const MAX_DIGEST_DIRS = 600;
|
|
35
|
+
const MAX_MANIFEST_CHARS = 2_000;
|
|
36
|
+
/** Workspace-tooling manifests, read verbatim as grouping signal. */
|
|
37
|
+
const WORKSPACE_MANIFESTS = [
|
|
38
|
+
"pnpm-workspace.yaml",
|
|
39
|
+
"lerna.json",
|
|
40
|
+
"nx.json",
|
|
41
|
+
"turbo.json",
|
|
42
|
+
"rush.json",
|
|
26
43
|
];
|
|
27
|
-
/** Monorepo package parents to scan for nested component dirs. */
|
|
28
|
-
const MONOREPO_PARENTS = ["packages", "apps"];
|
|
29
|
-
/** Extensions we sniff for beyond the React default. */
|
|
30
|
-
const SVELTE_EXT = ".svelte";
|
|
31
|
-
const VUE_EXT = ".vue";
|
|
32
44
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* presence-driven so we never invent extensions a repo doesn't use).
|
|
45
|
+
* Walk the repo once and build an agnostic structural digest: a
|
|
46
|
+
* per-directory extension histogram plus the set of dirs that carry a
|
|
47
|
+
* `package.json`. No convention names are consulted — this is raw shape.
|
|
37
48
|
*/
|
|
38
|
-
function
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
dir:
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
49
|
+
function buildRepoDigest(repoRoot) {
|
|
50
|
+
const skip = new Set([...DEFAULT_EXCLUDE]);
|
|
51
|
+
const perDir = new Map();
|
|
52
|
+
const packageRoots = [];
|
|
53
|
+
walkFs({
|
|
54
|
+
dir: repoRoot,
|
|
55
|
+
repoRoot,
|
|
56
|
+
skipDirs: skip,
|
|
57
|
+
onFile: (rel, _abs, entry) => {
|
|
58
|
+
const slash = rel.lastIndexOf("/");
|
|
59
|
+
const dir = slash === -1 ? "." : rel.slice(0, slash);
|
|
60
|
+
if (entry.name === "package.json")
|
|
61
|
+
packageRoots.push(dir);
|
|
62
|
+
const dot = rel.lastIndexOf(".");
|
|
63
|
+
const ext = dot > slash ? rel.slice(dot) : "(noext)";
|
|
64
|
+
let m = perDir.get(dir);
|
|
65
|
+
if (m === undefined) {
|
|
66
|
+
m = new Map();
|
|
67
|
+
perDir.set(dir, m);
|
|
68
|
+
}
|
|
69
|
+
m.set(ext, (m.get(ext) ?? 0) + 1);
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
const dirs = [...perDir.entries()].sort((a, b) => a[0].localeCompare(b[0]));
|
|
73
|
+
const lines = [];
|
|
74
|
+
for (const [dir, exts] of dirs) {
|
|
75
|
+
const parts = [...exts.entries()]
|
|
76
|
+
.sort((a, b) => b[1] - a[1])
|
|
77
|
+
.map(([e, c]) => `${c}${e}`);
|
|
78
|
+
lines.push(`${dir}: ${parts.join(" ")}`);
|
|
79
|
+
if (lines.length >= MAX_DIGEST_DIRS) {
|
|
80
|
+
lines.push(`… (${dirs.length - MAX_DIGEST_DIRS} more dirs truncated)`);
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
61
83
|
}
|
|
62
|
-
|
|
63
|
-
// Keep the React pair as the baseline unless the repo is purely
|
|
64
|
-
// Vue/Svelte — most TS UI repos still carry .tsx alongside framework files.
|
|
65
|
-
if (hasReact || (!hasVue && !hasSvelte))
|
|
66
|
-
exts.push(...DEFAULT_EXTENSIONS);
|
|
67
|
-
if (hasVue)
|
|
68
|
-
exts.push(VUE_EXT);
|
|
69
|
-
if (hasSvelte)
|
|
70
|
-
exts.push(SVELTE_EXT);
|
|
71
|
-
return exts;
|
|
84
|
+
return { histogram: lines.join("\n"), packageRoots: packageRoots.sort() };
|
|
72
85
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
function probeMonorepo(repoRoot) {
|
|
79
|
-
const exclude = new Set([...DEFAULT_EXCLUDE]);
|
|
80
|
-
const found = [];
|
|
81
|
-
const usedNames = new Set();
|
|
82
|
-
for (const parent of MONOREPO_PARENTS) {
|
|
83
|
-
const parentAbs = join(repoRoot, parent);
|
|
84
|
-
if (!existsSync(parentAbs))
|
|
86
|
+
function readWorkspaceManifests(repoRoot) {
|
|
87
|
+
const out = [];
|
|
88
|
+
for (const name of WORKSPACE_MANIFESTS) {
|
|
89
|
+
const p = join(repoRoot, name);
|
|
90
|
+
if (!existsSync(p))
|
|
85
91
|
continue;
|
|
86
|
-
let subs;
|
|
87
92
|
try {
|
|
88
|
-
|
|
93
|
+
out.push(`${name}:\n${readFileSync(p, "utf8").slice(0, MAX_MANIFEST_CHARS)}`);
|
|
89
94
|
}
|
|
90
95
|
catch {
|
|
91
|
-
|
|
96
|
+
/* unreadable → skip */
|
|
92
97
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
let name = d.name;
|
|
102
|
-
if (usedNames.has(name))
|
|
103
|
-
name = `${parent}-${d.name}`;
|
|
104
|
-
usedNames.add(name);
|
|
105
|
-
found.push({ name, dirs });
|
|
98
|
+
}
|
|
99
|
+
const rootPkg = join(repoRoot, "package.json");
|
|
100
|
+
if (existsSync(rootPkg)) {
|
|
101
|
+
try {
|
|
102
|
+
out.push(`package.json (root):\n${readFileSync(rootPkg, "utf8").slice(0, MAX_MANIFEST_CHARS)}`);
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
/* skip */
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
|
-
return
|
|
108
|
+
return out.join("\n\n");
|
|
109
109
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
|
|
110
|
+
const SYSTEM_PROMPT = `You map a repository's reusable-UI-component layout for a component registry. You receive the repo's workspace-manifest files, the directories that contain a package.json, and a per-directory file-extension histogram.
|
|
111
|
+
|
|
112
|
+
Return STRICT JSON matching the schema. No prose, no markdown.
|
|
113
|
+
|
|
114
|
+
Definitions:
|
|
115
|
+
- A "component dir" is a directory whose primary contents are REUSABLE UI components (buttons, cards, modals, layout, navigation, domain widgets). It is NOT a route/page dir, NOT tests, NOT stories, NOT backend/service/data/model code, NOT email templates.
|
|
116
|
+
- A "workspace" is an independently-scoped package. A monorepo has 2+ (each typically rooted at a package.json); a single app has one. Infer workspaces from the manifests / package roots / structure.
|
|
117
|
+
|
|
118
|
+
Hard rules — be convention-agnostic:
|
|
119
|
+
- Do NOT assume any naming. Component dirs are NOT necessarily named "components"; workspaces are NOT necessarily under "packages/" or "apps/". Decide from the actual extension histogram and package roots, wherever they sit (top-level dirs, nested, anywhere).
|
|
120
|
+
- componentDirs are repo-relative POSIX paths that appear in the histogram.
|
|
121
|
+
- Include a workspace ONLY if it actually contains reusable UI components. A backend-only or data-only package (e.g. mostly .ts services with no component dir, or only email-template files) is OMITTED ENTIRELY.
|
|
122
|
+
- extensions: the component file extensions actually present in that workspace's component dirs (e.g. ".tsx", ".jsx", ".vue", ".svelte", ".astro").
|
|
123
|
+
- categories: a SHORT taxonomy (5-12 lowercase kebab-case tags) derived from what THIS workspace actually is — a marketing site leans ["layout","navigation","marketing","forms","media","feedback"], an app shell leans ["layout","navigation","shell","domain","forms","overlay","feedback","data-display"]. Do not copy a fixed list; fit the workspace.
|
|
124
|
+
- name: "" for a single-app repo; for monorepo workspaces use the package's directory name (the last path segment of its root).
|
|
125
|
+
- If the repo has NO reusable UI components at all, return {"is_ui_repo": false, "monorepo": false, "workspaces": []}.`;
|
|
126
|
+
const OUTPUT_SCHEMA = {
|
|
127
|
+
type: "object",
|
|
128
|
+
required: ["is_ui_repo", "monorepo", "workspaces"],
|
|
129
|
+
properties: {
|
|
130
|
+
is_ui_repo: { type: "boolean" },
|
|
131
|
+
monorepo: { type: "boolean" },
|
|
132
|
+
workspaces: {
|
|
133
|
+
type: "array",
|
|
134
|
+
items: {
|
|
135
|
+
type: "object",
|
|
136
|
+
required: ["name", "componentDirs", "extensions", "categories"],
|
|
137
|
+
properties: {
|
|
138
|
+
name: { type: "string" },
|
|
139
|
+
componentDirs: { type: "array", items: { type: "string" } },
|
|
140
|
+
extensions: { type: "array", items: { type: "string" } },
|
|
141
|
+
categories: { type: "array", items: { type: "string" } },
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
const ResultSchema = z.object({
|
|
148
|
+
is_ui_repo: z.boolean(),
|
|
149
|
+
monorepo: z.boolean(),
|
|
150
|
+
workspaces: z.array(z.object({
|
|
151
|
+
name: z.string(),
|
|
152
|
+
componentDirs: z.array(z.string()),
|
|
153
|
+
extensions: z.array(z.string()),
|
|
154
|
+
categories: z.array(z.string()),
|
|
155
|
+
})),
|
|
156
|
+
});
|
|
157
|
+
function buildPrompt(repoRoot) {
|
|
158
|
+
const digest = buildRepoDigest(repoRoot);
|
|
159
|
+
const manifests = readWorkspaceManifests(repoRoot);
|
|
160
|
+
return [
|
|
161
|
+
"WORKSPACE MANIFESTS:",
|
|
162
|
+
manifests.length > 0 ? manifests : "(none)",
|
|
163
|
+
"",
|
|
164
|
+
"DIRS CONTAINING A package.json (workspace boundaries):",
|
|
165
|
+
digest.packageRoots.length > 0 ? digest.packageRoots.join("\n") : "(none)",
|
|
166
|
+
"",
|
|
167
|
+
"DIRECTORY EXTENSION HISTOGRAM (path: <count><ext> …):",
|
|
168
|
+
digest.histogram.length > 0 ? digest.histogram : "(no source files found)",
|
|
169
|
+
"",
|
|
170
|
+
"Return the component-layout JSON.",
|
|
171
|
+
].join("\n");
|
|
172
|
+
}
|
|
173
|
+
async function attempt(repoRoot, prompt) {
|
|
174
|
+
let result;
|
|
175
|
+
try {
|
|
176
|
+
result = await runClaude({
|
|
177
|
+
tier: "sonnet",
|
|
178
|
+
prompt,
|
|
179
|
+
system: SYSTEM_PROMPT,
|
|
180
|
+
jsonSchema: OUTPUT_SCHEMA,
|
|
181
|
+
timeoutMs: TIMEOUT_MS,
|
|
182
|
+
repoRoot,
|
|
183
|
+
cacheable: true,
|
|
184
|
+
isolateAmbientContext: true,
|
|
185
|
+
purpose: "init.detect-components",
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
catch (err) {
|
|
189
|
+
log.warn({ error: err instanceof Error ? err.message : String(err) }, "detect-components: runClaude failed");
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
const parsed = ResultSchema.safeParse(result.parsed);
|
|
193
|
+
if (!parsed.success) {
|
|
194
|
+
log.warn({ error: parsed.error.message }, "detect-components: response failed schema");
|
|
195
|
+
return null;
|
|
132
196
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
197
|
+
return parsed.data;
|
|
198
|
+
}
|
|
199
|
+
function toConfig(parsed) {
|
|
200
|
+
if (!parsed.is_ui_repo)
|
|
201
|
+
return null;
|
|
202
|
+
const ws = parsed.workspaces.filter((w) => w.componentDirs.length > 0);
|
|
203
|
+
if (ws.length === 0)
|
|
138
204
|
return null;
|
|
205
|
+
const exclude = [...DEFAULT_EXCLUDE];
|
|
206
|
+
// Multi-workspace → monorepo `workspaces` form. A lone workspace (even
|
|
207
|
+
// if the model flagged the repo a monorepo) collapses to the flat form.
|
|
208
|
+
if (ws.length >= 2) {
|
|
209
|
+
const usedNames = new Set();
|
|
210
|
+
const workspaces = {};
|
|
211
|
+
for (const w of ws) {
|
|
212
|
+
let name = w.name.trim().length > 0 ? w.name.trim() : "app";
|
|
213
|
+
let n = 2;
|
|
214
|
+
while (usedNames.has(name))
|
|
215
|
+
name = `${w.name || "app"}-${n++}`;
|
|
216
|
+
usedNames.add(name);
|
|
217
|
+
workspaces[name] = {
|
|
218
|
+
componentDirs: w.componentDirs,
|
|
219
|
+
extensions: w.extensions,
|
|
220
|
+
categories: w.categories,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
return { exclude, workspaces };
|
|
224
|
+
}
|
|
225
|
+
const single = ws[0];
|
|
139
226
|
return {
|
|
140
|
-
componentDirs:
|
|
141
|
-
extensions:
|
|
142
|
-
categories:
|
|
143
|
-
exclude
|
|
227
|
+
componentDirs: single.componentDirs,
|
|
228
|
+
extensions: single.extensions,
|
|
229
|
+
categories: single.categories,
|
|
230
|
+
exclude,
|
|
144
231
|
};
|
|
145
232
|
}
|
|
233
|
+
/**
|
|
234
|
+
* Detect the repo's component layout via the model and return a
|
|
235
|
+
* `components:` config block (raw yaml shape), or `null` when the repo
|
|
236
|
+
* carries no reusable UI components. Retries the model call once before
|
|
237
|
+
* giving up. There is no deterministic fallback by design: detection only
|
|
238
|
+
* ever runs inside an LLM coding agent, so "no model" means adoption is
|
|
239
|
+
* not happening at all.
|
|
240
|
+
*/
|
|
241
|
+
export async function detectComponentsConfig(repoRoot) {
|
|
242
|
+
const prompt = buildPrompt(repoRoot);
|
|
243
|
+
const out = (await attempt(repoRoot, prompt)) ?? (await attempt(repoRoot, prompt));
|
|
244
|
+
if (out === null)
|
|
245
|
+
return null;
|
|
246
|
+
return toConfig(out);
|
|
247
|
+
}
|
|
146
248
|
/**
|
|
147
249
|
* Backfill a `components:` block into an already-adopted repo's
|
|
148
|
-
* `.cairn/config.yaml`.
|
|
149
|
-
*
|
|
150
|
-
*
|
|
250
|
+
* `.cairn/config.yaml`. Runs the same LLM detection adoption Phase 4-seed
|
|
251
|
+
* runs, but MERGES the key into a config that already exists (preserving
|
|
252
|
+
* every other key) rather than writing a fresh file.
|
|
151
253
|
*
|
|
152
254
|
* Idempotent: a repo that already carries a `components:` block is left
|
|
153
255
|
* untouched ("exists"). The standalone backfill path
|
|
@@ -155,10 +257,10 @@ export function detectComponentsConfig(repoRoot, _detection) {
|
|
|
155
257
|
* is the only caller; adoption keeps using `detectComponentsConfig`
|
|
156
258
|
* directly inside 4-seed.
|
|
157
259
|
*
|
|
158
|
-
* Isolation invariant (port invariant 3):
|
|
159
|
-
*
|
|
260
|
+
* Isolation invariant (port invariant 3): workspaces are never emitted as
|
|
261
|
+
* `shared` — the operator opts in afterward (the skill asks).
|
|
160
262
|
*/
|
|
161
|
-
export function ensureComponentsConfig(repoRoot) {
|
|
263
|
+
export async function ensureComponentsConfig(repoRoot) {
|
|
162
264
|
const configPath = join(repoRoot, ".cairn", "config.yaml");
|
|
163
265
|
if (!existsSync(configPath)) {
|
|
164
266
|
return { status: "not-adopted", monorepo: false };
|
|
@@ -168,8 +270,7 @@ export function ensureComponentsConfig(repoRoot) {
|
|
|
168
270
|
if (parsed["components"] !== undefined && parsed["components"] !== null) {
|
|
169
271
|
return { status: "exists", monorepo: false };
|
|
170
272
|
}
|
|
171
|
-
const
|
|
172
|
-
const components = detectComponentsConfig(repoRoot, detection);
|
|
273
|
+
const components = await detectComponentsConfig(repoRoot);
|
|
173
274
|
if (components === null) {
|
|
174
275
|
return { status: "none", monorepo: false };
|
|
175
276
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detect-components.js","sourceRoot":"","sources":["../../src/init/detect-components.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"detect-components.js","sourceRoot":"","sources":["../../src/init/detect-components.ts"],"names":[],"mappings":"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"}
|
|
@@ -84,11 +84,11 @@ export async function runPhase4Seed(state) {
|
|
|
84
84
|
decidedSlug: projectSlug,
|
|
85
85
|
...(mapperOutput !== undefined ? { mapperOutput } : {}),
|
|
86
86
|
});
|
|
87
|
-
// Attach a detected `components:` block (
|
|
88
|
-
//
|
|
89
|
-
// the whole component store stays inert. overlay.ts is
|
|
90
|
-
// IO); the detection lives here where 4-seed already does IO.
|
|
91
|
-
const components = detectComponentsConfig(state.repoRoot
|
|
87
|
+
// Attach a detected `components:` block (LLM-driven, agnostic — no
|
|
88
|
+
// convention list). Null for non-UI repos — the key is simply
|
|
89
|
+
// omitted, so the whole component store stays inert. overlay.ts is
|
|
90
|
+
// pure (no IO); the detection lives here where 4-seed already does IO.
|
|
91
|
+
const components = await detectComponentsConfig(state.repoRoot);
|
|
92
92
|
if (components !== null)
|
|
93
93
|
config["components"] = components;
|
|
94
94
|
writeFileSync(configPath, stringifyYaml(config), "utf8");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"4-seed.js","sourceRoot":"","sources":["../../../src/init/phases/4-seed.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"4-seed.js","sourceRoot":"","sources":["../../../src/init/phases/4-seed.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,7 +3,8 @@ import { join } from "node:path";
|
|
|
3
3
|
import { stringify as stringifyYaml } from "yaml";
|
|
4
4
|
import { computeDecisionId, scanExistingDecisionIds, } from "../../decision-capture/index.js";
|
|
5
5
|
import { writeInvalidationEvent } from "../../events/index.js";
|
|
6
|
-
import {
|
|
6
|
+
import { isDuplicateOfAccepted } from "../../attention/dedup.js";
|
|
7
|
+
import { bodyContentHash, decisionsAutoAccept, decisionsDir, deriveDecId, readAnchorMap, readRejectedYaml, readTopicIndex, setTopic, writeDecisionsLedger, writeFileCandidatesMap, writeTopicIndex, } from "@isaacriehm/cairn-state";
|
|
7
8
|
import { DecisionAssertion } from "@isaacriehm/cairn-state";
|
|
8
9
|
import { withWriteLock } from "../../lock.js";
|
|
9
10
|
import { requireBootstrap } from "../bootstrap-guard.js";
|
|
@@ -49,8 +50,9 @@ async function handler(ctx, input) {
|
|
|
49
50
|
}
|
|
50
51
|
return withWriteLock(ctx.repoRoot, async () => {
|
|
51
52
|
const existingIds = scanExistingDecisionIds(ctx.repoRoot);
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
// `target` may be promoted from "inbox" to "accepted" by the
|
|
54
|
+
// auto-accept gate below (after the body/title are resolved).
|
|
55
|
+
let target = input.target ?? "inbox";
|
|
54
56
|
let id;
|
|
55
57
|
let body;
|
|
56
58
|
let title;
|
|
@@ -122,6 +124,34 @@ async function handler(ctx, input) {
|
|
|
122
124
|
if (input.supersedes !== undefined && !existingIds.has(input.supersedes)) {
|
|
123
125
|
return mcpError("SUPERSEDES_NOT_FOUND", `supersedes target "${input.supersedes}" not found`);
|
|
124
126
|
}
|
|
127
|
+
// ── Auto-accept gate ──────────────────────────────────────────────
|
|
128
|
+
// When auto-accept is on (the default) and the caller did NOT pin a
|
|
129
|
+
// target, an AI-proposed decision lands straight in the ledger instead
|
|
130
|
+
// of the triage inbox — the human review checkpoint shifts to the
|
|
131
|
+
// committed-ground-state PR diff. Verify-then-accept: assertions
|
|
132
|
+
// already passed schema validation above; here we dedup against the
|
|
133
|
+
// accepted ledger so a restatement of an existing decision still queues
|
|
134
|
+
// as a draft for human eyes rather than silently re-landing.
|
|
135
|
+
//
|
|
136
|
+
// An explicit `target` is always honored: `"accepted"` forces direct
|
|
137
|
+
// accept (skips the dedup gate); `"inbox"` forces a triage draft even
|
|
138
|
+
// when auto-accept is globally on (the per-call escape hatch).
|
|
139
|
+
let autoAccepted = false;
|
|
140
|
+
let dedupNote;
|
|
141
|
+
if (input.target === undefined && decisionsAutoAccept(ctx.repoRoot)) {
|
|
142
|
+
const dup = isDuplicateOfAccepted({ repoRoot: ctx.repoRoot, title });
|
|
143
|
+
if (dup.dup) {
|
|
144
|
+
dedupNote =
|
|
145
|
+
`near-duplicate of accepted ${dup.matchId} ("${dup.matchTitle}", ` +
|
|
146
|
+
`sim ${dup.similarity}) — queued as a draft for triage instead of ` +
|
|
147
|
+
`auto-accepting`;
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
target = "accepted";
|
|
151
|
+
autoAccepted = true;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
const outDir = target === "accepted" ? dir : inboxDir;
|
|
125
155
|
mkdirSync(outDir, { recursive: true });
|
|
126
156
|
const frontmatter = {
|
|
127
157
|
id,
|
|
@@ -138,6 +168,7 @@ async function handler(ctx, input) {
|
|
|
138
168
|
sot_path: sotPath,
|
|
139
169
|
sot_content_hash: bodyContentHash(body),
|
|
140
170
|
source_file: sourceFile,
|
|
171
|
+
...(autoAccepted ? { auto_accepted: true } : {}),
|
|
141
172
|
...(input.supersedes !== undefined ? { supersedes: input.supersedes } : {}),
|
|
142
173
|
...(input.assertions !== undefined ? { assertions: input.assertions } : {}),
|
|
143
174
|
...(input.human_review_hint !== undefined ? { human_review_hint: input.human_review_hint } : {}),
|
|
@@ -174,7 +205,14 @@ async function handler(ctx, input) {
|
|
|
174
205
|
catch {
|
|
175
206
|
/* ignore */
|
|
176
207
|
}
|
|
177
|
-
return {
|
|
208
|
+
return {
|
|
209
|
+
ok: true,
|
|
210
|
+
id,
|
|
211
|
+
target,
|
|
212
|
+
path: relPath,
|
|
213
|
+
auto_accepted: autoAccepted,
|
|
214
|
+
...(dedupNote !== undefined ? { note: dedupNote } : {}),
|
|
215
|
+
};
|
|
178
216
|
});
|
|
179
217
|
}
|
|
180
218
|
function relativePath(id, target) {
|
|
@@ -213,7 +251,7 @@ function allocateUniqueDecId(input, existingIds) {
|
|
|
213
251
|
}
|
|
214
252
|
export const recordDecisionTool = {
|
|
215
253
|
name: "cairn_record_decision",
|
|
216
|
-
description: "Record a decision
|
|
254
|
+
description: "Record a decision. Use `slug` to promote a candidate from the topic index, or provide `title` and `summary` for a manual entry. By default decisions AUTO-ACCEPT straight into the ledger (the human review checkpoint is the committed-ground-state PR diff); a near-duplicate of an already-accepted decision falls back to an `_inbox/` draft instead. The response reports `auto_accepted` and, on dedup fallback, a `note`. Set `decisions.auto_accept: false` in `.cairn/config.yaml` to restore per-draft triage. `target='accepted'` forces direct accept (skips the dedup gate).\n\n" +
|
|
217
255
|
"**`assertions` schema** (only emit one of these `kind` values — anything else returns INVALID_ASSERTION_KIND):\n" +
|
|
218
256
|
"- `text_must_match` / `text_must_not_match`: `{id, kind, pattern, in_globs[]}` — regex over files in globs.\n" +
|
|
219
257
|
"- `ast_pattern`: `{id, kind, language, pattern, in_globs[]}` — AST grep in named lang over globs.\n" +
|