@davidorex/pi-context 0.26.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.
Files changed (126) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +195 -0
  3. package/dist/block-api.d.ts +328 -0
  4. package/dist/block-api.d.ts.map +1 -0
  5. package/dist/block-api.js +1167 -0
  6. package/dist/block-api.js.map +1 -0
  7. package/dist/block-validation.d.ts +33 -0
  8. package/dist/block-validation.d.ts.map +1 -0
  9. package/dist/block-validation.js +213 -0
  10. package/dist/block-validation.js.map +1 -0
  11. package/dist/context-dir.d.ts +93 -0
  12. package/dist/context-dir.d.ts.map +1 -0
  13. package/dist/context-dir.js +224 -0
  14. package/dist/context-dir.js.map +1 -0
  15. package/dist/context-sdk.d.ts +428 -0
  16. package/dist/context-sdk.d.ts.map +1 -0
  17. package/dist/context-sdk.js +1327 -0
  18. package/dist/context-sdk.js.map +1 -0
  19. package/dist/context.d.ts +497 -0
  20. package/dist/context.d.ts.map +1 -0
  21. package/dist/context.js +1060 -0
  22. package/dist/context.js.map +1 -0
  23. package/dist/dispatch-context.d.ts +112 -0
  24. package/dist/dispatch-context.d.ts.map +1 -0
  25. package/dist/dispatch-context.js +119 -0
  26. package/dist/dispatch-context.js.map +1 -0
  27. package/dist/execution-context.d.ts +138 -0
  28. package/dist/execution-context.d.ts.map +1 -0
  29. package/dist/execution-context.js +151 -0
  30. package/dist/execution-context.js.map +1 -0
  31. package/dist/index.d.ts +34 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +1655 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/lens-validator.d.ts +50 -0
  36. package/dist/lens-validator.d.ts.map +1 -0
  37. package/dist/lens-validator.js +46 -0
  38. package/dist/lens-validator.js.map +1 -0
  39. package/dist/lens-view.d.ts +75 -0
  40. package/dist/lens-view.d.ts.map +1 -0
  41. package/dist/lens-view.js +253 -0
  42. package/dist/lens-view.js.map +1 -0
  43. package/dist/rename-canonical-id.d.ts +64 -0
  44. package/dist/rename-canonical-id.d.ts.map +1 -0
  45. package/dist/rename-canonical-id.js +378 -0
  46. package/dist/rename-canonical-id.js.map +1 -0
  47. package/dist/roadmap-plan.d.ts +186 -0
  48. package/dist/roadmap-plan.d.ts.map +1 -0
  49. package/dist/roadmap-plan.js +534 -0
  50. package/dist/roadmap-plan.js.map +1 -0
  51. package/dist/samples-catalog.d.ts +48 -0
  52. package/dist/samples-catalog.d.ts.map +1 -0
  53. package/dist/samples-catalog.js +128 -0
  54. package/dist/samples-catalog.js.map +1 -0
  55. package/dist/schema-migrations.d.ts +82 -0
  56. package/dist/schema-migrations.d.ts.map +1 -0
  57. package/dist/schema-migrations.js +106 -0
  58. package/dist/schema-migrations.js.map +1 -0
  59. package/dist/schema-validator.d.ts +76 -0
  60. package/dist/schema-validator.d.ts.map +1 -0
  61. package/dist/schema-validator.js +219 -0
  62. package/dist/schema-validator.js.map +1 -0
  63. package/dist/schema-write.d.ts +122 -0
  64. package/dist/schema-write.d.ts.map +1 -0
  65. package/dist/schema-write.js +210 -0
  66. package/dist/schema-write.js.map +1 -0
  67. package/dist/status-vocab.d.ts +60 -0
  68. package/dist/status-vocab.d.ts.map +1 -0
  69. package/dist/status-vocab.js +109 -0
  70. package/dist/status-vocab.js.map +1 -0
  71. package/dist/test-helpers.d.ts +12 -0
  72. package/dist/test-helpers.d.ts.map +1 -0
  73. package/dist/test-helpers.js +28 -0
  74. package/dist/test-helpers.js.map +1 -0
  75. package/dist/topo.d.ts +19 -0
  76. package/dist/topo.d.ts.map +1 -0
  77. package/dist/topo.js +99 -0
  78. package/dist/topo.js.map +1 -0
  79. package/dist/update-check.d.ts +6 -0
  80. package/dist/update-check.d.ts.map +1 -0
  81. package/dist/update-check.js +82 -0
  82. package/dist/update-check.js.map +1 -0
  83. package/package.json +115 -0
  84. package/samples/blocks/context-contracts.json +3 -0
  85. package/samples/blocks/conventions.json +3 -0
  86. package/samples/blocks/decisions.json +3 -0
  87. package/samples/blocks/features.json +3 -0
  88. package/samples/blocks/framework-gaps.json +3 -0
  89. package/samples/blocks/issues.json +3 -0
  90. package/samples/blocks/layer-plans.json +3 -0
  91. package/samples/blocks/phase.json +3 -0
  92. package/samples/blocks/rationale.json +3 -0
  93. package/samples/blocks/requirements.json +3 -0
  94. package/samples/blocks/research.json +3 -0
  95. package/samples/blocks/spec-reviews.json +3 -0
  96. package/samples/blocks/story.json +3 -0
  97. package/samples/blocks/tasks.json +3 -0
  98. package/samples/blocks/verification.json +3 -0
  99. package/samples/conception.json +467 -0
  100. package/samples/schemas/context-contracts.schema.json +89 -0
  101. package/samples/schemas/conventions.schema.json +34 -0
  102. package/samples/schemas/decisions.schema.json +87 -0
  103. package/samples/schemas/features.schema.json +53 -0
  104. package/samples/schemas/framework-gaps.schema.json +64 -0
  105. package/samples/schemas/issues.schema.json +39 -0
  106. package/samples/schemas/layer-plans.schema.json +92 -0
  107. package/samples/schemas/phase.schema.json +118 -0
  108. package/samples/schemas/rationale.schema.json +24 -0
  109. package/samples/schemas/requirements.schema.json +34 -0
  110. package/samples/schemas/research.schema.json +134 -0
  111. package/samples/schemas/spec-reviews.schema.json +62 -0
  112. package/samples/schemas/story.schema.json +73 -0
  113. package/samples/schemas/tasks.schema.json +34 -0
  114. package/samples/schemas/verification.schema.json +37 -0
  115. package/schemas/bootstrap.schema.json +24 -0
  116. package/schemas/config.schema.json +193 -0
  117. package/schemas/layer.schema.json +9 -0
  118. package/schemas/priority.schema.json +9 -0
  119. package/schemas/relations.schema.json +31 -0
  120. package/schemas/severity.schema.json +9 -0
  121. package/schemas/source.schema.json +9 -0
  122. package/schemas/status.schema.json +9 -0
  123. package/schemas/verification-method.schema.json +9 -0
  124. package/skill-narrative.md +130 -0
  125. package/skills/pi-context/SKILL.md +684 -0
  126. package/skills/pi-context/references/bundled-resources.md +47 -0
@@ -0,0 +1,64 @@
1
+ /**
2
+ * canonical_id rename engine (FGAP-060 / DEC-0035).
3
+ *
4
+ * canonical_ids are primary-key-permanent — the steady state is "never rename".
5
+ * The rare, deliberate rename routes exclusively through `renameCanonicalId`,
6
+ * which rewrites every substrate surface that carries the id as DATA and
7
+ * REPORTS (never rewrites) every out-of-substrate occurrence (analysis MDs,
8
+ * git history) so the operator can follow up manually.
9
+ *
10
+ * Edge model (DEC-0013): inter-item references live ONLY as relations.json
11
+ * edges — there is NO inline-FK field on item schemas, so the engine performs
12
+ * NO inline-FK sweep. Item renames touch (1) the item's home block id field and
13
+ * (2) every edge whose parent/child equals the old id.
14
+ *
15
+ * Five canonical_id kinds exist; this engine implements four:
16
+ * - item (a block item's id, e.g. DEC-0001)
17
+ * - relation_type (a config.relation_types[].canonical_id)
18
+ * - lens (a config.lenses[].id)
19
+ * - layer (a config.layers[].id)
20
+ * The fifth — block_kind — THROWS unsupported: a block_kind rename requires a
21
+ * coupled file / data_path / array_key / schema_path filesystem cascade, which
22
+ * is tracked separately.
23
+ *
24
+ * Write discipline:
25
+ * - Guards (existence / collision / block_kind / unknown-kind) throw BEFORE
26
+ * any write — a guard failure leaves the substrate byte-untouched.
27
+ * - dryRun computes the would-change counts but performs ZERO writes.
28
+ * - All config-surface changes accumulate into a single deep-cloned config
29
+ * object written ONCE via writeConfig (never multiple config writes).
30
+ */
31
+ export type RenameKind = "item" | "relation_type" | "lens" | "layer";
32
+ export interface RenameReport {
33
+ kind: string;
34
+ oldId: string;
35
+ newId: string;
36
+ dryRun: boolean;
37
+ /** Per-surface rewrite tally — each entry is one (file, field) surface that
38
+ * carried ≥1 occurrence of oldId, with the count of occurrences rewritten. */
39
+ substrateRewrites: {
40
+ file: string;
41
+ field: string;
42
+ count: number;
43
+ }[];
44
+ /** Out-of-substrate occurrences — REPORT ONLY, never rewritten. */
45
+ outOfSubstrate: {
46
+ source: string;
47
+ context: string;
48
+ }[];
49
+ }
50
+ /**
51
+ * Rename a canonical_id of `kind` from `oldId` to `newId`.
52
+ *
53
+ * Returns a {@link RenameReport} describing every substrate surface rewritten
54
+ * (or that WOULD be rewritten, under `dryRun`) plus every out-of-substrate
55
+ * occurrence found (always report-only).
56
+ *
57
+ * Throws on: kind=block_kind (unsupported), unknown kind, missing config,
58
+ * oldId not present for the kind, or collision (newId already exists for the
59
+ * kind). All such throws occur before any write.
60
+ */
61
+ export declare function renameCanonicalId(cwd: string, kind: string, oldId: string, newId: string, opts?: {
62
+ dryRun?: boolean;
63
+ }): RenameReport;
64
+ //# sourceMappingURL=rename-canonical-id.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rename-canonical-id.d.ts","sourceRoot":"","sources":["../src/rename-canonical-id.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AASH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,eAAe,GAAG,MAAM,GAAG,OAAO,CAAC;AAErE,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB;kFAC8E;IAC9E,iBAAiB,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACpE,mEAAmE;IACnE,cAAc,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACtD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAChC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GACzB,YAAY,CA2Qd"}
@@ -0,0 +1,378 @@
1
+ /**
2
+ * canonical_id rename engine (FGAP-060 / DEC-0035).
3
+ *
4
+ * canonical_ids are primary-key-permanent — the steady state is "never rename".
5
+ * The rare, deliberate rename routes exclusively through `renameCanonicalId`,
6
+ * which rewrites every substrate surface that carries the id as DATA and
7
+ * REPORTS (never rewrites) every out-of-substrate occurrence (analysis MDs,
8
+ * git history) so the operator can follow up manually.
9
+ *
10
+ * Edge model (DEC-0013): inter-item references live ONLY as relations.json
11
+ * edges — there is NO inline-FK field on item schemas, so the engine performs
12
+ * NO inline-FK sweep. Item renames touch (1) the item's home block id field and
13
+ * (2) every edge whose parent/child equals the old id.
14
+ *
15
+ * Five canonical_id kinds exist; this engine implements four:
16
+ * - item (a block item's id, e.g. DEC-0001)
17
+ * - relation_type (a config.relation_types[].canonical_id)
18
+ * - lens (a config.lenses[].id)
19
+ * - layer (a config.layers[].id)
20
+ * The fifth — block_kind — THROWS unsupported: a block_kind rename requires a
21
+ * coupled file / data_path / array_key / schema_path filesystem cascade, which
22
+ * is tracked separately.
23
+ *
24
+ * Write discipline:
25
+ * - Guards (existence / collision / block_kind / unknown-kind) throw BEFORE
26
+ * any write — a guard failure leaves the substrate byte-untouched.
27
+ * - dryRun computes the would-change counts but performs ZERO writes.
28
+ * - All config-surface changes accumulate into a single deep-cloned config
29
+ * object written ONCE via writeConfig (never multiple config writes).
30
+ */
31
+ import { execSync } from "node:child_process";
32
+ import fs from "node:fs";
33
+ import path from "node:path";
34
+ import { readBlock, updateItemInBlock, writeBlock } from "./block-api.js";
35
+ import { loadConfig, loadRelations, writeConfig, writeRelations } from "./context.js";
36
+ import { buildIdIndex } from "./context-sdk.js";
37
+ /**
38
+ * Rename a canonical_id of `kind` from `oldId` to `newId`.
39
+ *
40
+ * Returns a {@link RenameReport} describing every substrate surface rewritten
41
+ * (or that WOULD be rewritten, under `dryRun`) plus every out-of-substrate
42
+ * occurrence found (always report-only).
43
+ *
44
+ * Throws on: kind=block_kind (unsupported), unknown kind, missing config,
45
+ * oldId not present for the kind, or collision (newId already exists for the
46
+ * kind). All such throws occur before any write.
47
+ */
48
+ export function renameCanonicalId(cwd, kind, oldId, newId, opts) {
49
+ const dryRun = opts?.dryRun ?? false;
50
+ const report = {
51
+ kind,
52
+ oldId,
53
+ newId,
54
+ dryRun,
55
+ substrateRewrites: [],
56
+ outOfSubstrate: [],
57
+ };
58
+ if (kind === "block_kind") {
59
+ throw new Error("renameCanonicalId: kind 'block_kind' is not supported — a block_kind canonical_id rename requires a coupled file/data_path/array_key/schema_path cascade (buildIdIndex resolves loc.block by file basename). Tracked separately; use display_name for relabeling.");
60
+ }
61
+ const config = loadConfig(cwd);
62
+ if (!config) {
63
+ throw new Error("renameCanonicalId: no config.json");
64
+ }
65
+ // Single deep-cloned config accumulator: every config-surface rewrite mutates
66
+ // THIS object; a single writeConfig at the end (when !dryRun and changed)
67
+ // commits all surfaces atomically. Never write config more than once.
68
+ const nextConfig = JSON.parse(JSON.stringify(config));
69
+ let configChanged = false;
70
+ if (kind === "item") {
71
+ const idx = buildIdIndex(cwd);
72
+ const loc = idx.get(oldId);
73
+ if (!loc)
74
+ throw new Error(`renameCanonicalId: item '${oldId}' not found`);
75
+ if (idx.has(newId))
76
+ throw new Error(`renameCanonicalId: collision — item '${newId}' already exists`);
77
+ // Home block id field.
78
+ if (!dryRun) {
79
+ updateItemInBlock(cwd, loc.block, loc.arrayKey, (i) => i.id === oldId, { id: newId });
80
+ }
81
+ report.substrateRewrites.push({ file: `${loc.block}.json`, field: "id", count: 1 });
82
+ // Edges: rewrite parent and/or child wherever they equal oldId. Each
83
+ // endpoint occurrence counts once (an edge with oldId on BOTH endpoints
84
+ // counts as 2).
85
+ const edges = loadRelations(cwd);
86
+ let n = 0;
87
+ const next = edges.map((e) => {
88
+ let p = e.parent;
89
+ let c = e.child;
90
+ if (p === oldId) {
91
+ p = newId;
92
+ n++;
93
+ }
94
+ if (c === oldId) {
95
+ c = newId;
96
+ n++;
97
+ }
98
+ return { ...e, parent: p, child: c };
99
+ });
100
+ if (n > 0) {
101
+ if (!dryRun)
102
+ writeRelations(cwd, next);
103
+ report.substrateRewrites.push({ file: "relations.json", field: "parent/child", count: n });
104
+ }
105
+ }
106
+ else if (kind === "relation_type") {
107
+ const rt = config.relation_types ?? [];
108
+ if (!rt.some((r) => r.canonical_id === oldId)) {
109
+ throw new Error(`renameCanonicalId: relation_type '${oldId}' not found`);
110
+ }
111
+ if (rt.some((r) => r.canonical_id === newId)) {
112
+ throw new Error(`renameCanonicalId: collision — relation_type '${newId}' already exists`);
113
+ }
114
+ // config.relation_types[].canonical_id
115
+ for (const r of nextConfig.relation_types ?? []) {
116
+ if (r.canonical_id === oldId) {
117
+ r.canonical_id = newId;
118
+ configChanged = true;
119
+ report.substrateRewrites.push({ file: "config.json", field: "relation_types[].canonical_id", count: 1 });
120
+ }
121
+ }
122
+ // config.invariants[].relation_types[] (array of strings)
123
+ let invCount = 0;
124
+ for (const inv of nextConfig.invariants ?? []) {
125
+ if (!Array.isArray(inv.relation_types))
126
+ continue;
127
+ inv.relation_types = inv.relation_types.map((v) => {
128
+ if (v === oldId) {
129
+ invCount++;
130
+ return newId;
131
+ }
132
+ return v;
133
+ });
134
+ }
135
+ if (invCount > 0) {
136
+ configChanged = true;
137
+ report.substrateRewrites.push({ file: "config.json", field: "invariants[].relation_types[]", count: invCount });
138
+ }
139
+ // config.lenses[].relation_type
140
+ let lensCount = 0;
141
+ for (const l of nextConfig.lenses ?? []) {
142
+ if (l.relation_type === oldId) {
143
+ l.relation_type = newId;
144
+ lensCount++;
145
+ }
146
+ }
147
+ if (lensCount > 0) {
148
+ configChanged = true;
149
+ report.substrateRewrites.push({ file: "config.json", field: "lenses[].relation_type", count: lensCount });
150
+ }
151
+ // config.hierarchy[].relation_type
152
+ let hierCount = 0;
153
+ for (const h of nextConfig.hierarchy ?? []) {
154
+ if (h.relation_type === oldId) {
155
+ h.relation_type = newId;
156
+ hierCount++;
157
+ }
158
+ }
159
+ if (hierCount > 0) {
160
+ configChanged = true;
161
+ report.substrateRewrites.push({ file: "config.json", field: "hierarchy[].relation_type", count: hierCount });
162
+ }
163
+ // relations.json edges by relation_type
164
+ const edges = loadRelations(cwd);
165
+ let n = 0;
166
+ const next = edges.map((e) => {
167
+ if (e.relation_type === oldId) {
168
+ n++;
169
+ return { ...e, relation_type: newId };
170
+ }
171
+ return e;
172
+ });
173
+ if (n > 0) {
174
+ if (!dryRun)
175
+ writeRelations(cwd, next);
176
+ report.substrateRewrites.push({ file: "relations.json", field: "relation_type", count: n });
177
+ }
178
+ // context-contracts block (ONLY if its data file exists). Each contract's
179
+ // bundle_relation_types[].relation_type is rewritten. Absent block →
180
+ // silently skip (this repo's .project has no context-contracts data file).
181
+ try {
182
+ const ccData = readBlock(cwd, "context-contracts");
183
+ let ccCount = 0;
184
+ let arrayKey;
185
+ for (const [k, v] of Object.entries(ccData)) {
186
+ if (Array.isArray(v)) {
187
+ arrayKey = k;
188
+ break;
189
+ }
190
+ }
191
+ if (arrayKey) {
192
+ const contracts = ccData[arrayKey];
193
+ for (const contract of contracts) {
194
+ const brt = contract.bundle_relation_types;
195
+ if (!Array.isArray(brt))
196
+ continue;
197
+ for (const entry of brt) {
198
+ if (entry && entry.relation_type === oldId) {
199
+ entry.relation_type = newId;
200
+ ccCount++;
201
+ }
202
+ }
203
+ }
204
+ if (ccCount > 0) {
205
+ if (!dryRun)
206
+ writeBlock(cwd, "context-contracts", ccData);
207
+ report.substrateRewrites.push({
208
+ file: "context-contracts.json",
209
+ field: "bundle_relation_types[].relation_type",
210
+ count: ccCount,
211
+ });
212
+ }
213
+ }
214
+ }
215
+ catch {
216
+ // context-contracts block absent — skip silently.
217
+ }
218
+ }
219
+ else if (kind === "lens") {
220
+ const ls = config.lenses ?? [];
221
+ if (!ls.some((l) => l.id === oldId))
222
+ throw new Error(`renameCanonicalId: lens '${oldId}' not found`);
223
+ if (ls.some((l) => l.id === newId)) {
224
+ throw new Error(`renameCanonicalId: collision — lens '${newId}' already exists`);
225
+ }
226
+ // config.lenses[].id
227
+ let idCount = 0;
228
+ for (const l of nextConfig.lenses ?? []) {
229
+ if (l.id === oldId) {
230
+ l.id = newId;
231
+ idCount++;
232
+ }
233
+ }
234
+ if (idCount > 0) {
235
+ configChanged = true;
236
+ report.substrateRewrites.push({ file: "config.json", field: "lenses[].id", count: idCount });
237
+ }
238
+ // config.lenses[].members[].lens (composition member references)
239
+ let memberCount = 0;
240
+ for (const l of nextConfig.lenses ?? []) {
241
+ for (const m of l.members ?? []) {
242
+ if (m.lens === oldId) {
243
+ m.lens = newId;
244
+ memberCount++;
245
+ }
246
+ }
247
+ }
248
+ if (memberCount > 0) {
249
+ configChanged = true;
250
+ report.substrateRewrites.push({ file: "config.json", field: "lenses[].members[].lens", count: memberCount });
251
+ }
252
+ }
253
+ else if (kind === "layer") {
254
+ const ly = config.layers ?? [];
255
+ if (!ly.some((l) => l.id === oldId))
256
+ throw new Error(`renameCanonicalId: layer '${oldId}' not found`);
257
+ if (ly.some((l) => l.id === newId)) {
258
+ throw new Error(`renameCanonicalId: collision — layer '${newId}' already exists`);
259
+ }
260
+ // config.layers[].id
261
+ let idCount = 0;
262
+ for (const l of nextConfig.layers ?? []) {
263
+ if (l.id === oldId) {
264
+ l.id = newId;
265
+ idCount++;
266
+ }
267
+ }
268
+ if (idCount > 0) {
269
+ configChanged = true;
270
+ report.substrateRewrites.push({ file: "config.json", field: "layers[].id", count: idCount });
271
+ }
272
+ // config.block_kinds[].layer (FK to layers[].id)
273
+ let bkCount = 0;
274
+ for (const bk of nextConfig.block_kinds ?? []) {
275
+ if (bk.layer === oldId) {
276
+ bk.layer = newId;
277
+ bkCount++;
278
+ }
279
+ }
280
+ if (bkCount > 0) {
281
+ configChanged = true;
282
+ report.substrateRewrites.push({ file: "config.json", field: "block_kinds[].layer", count: bkCount });
283
+ }
284
+ }
285
+ else {
286
+ throw new Error(`renameCanonicalId: unknown kind '${kind}'`);
287
+ }
288
+ // ── config.naming alias key (ALL kinds) ───────────────────────────────────
289
+ // The naming map keys are canonical ids → display labels. A rename of the id
290
+ // must carry its alias key forward (value preserved). This folds into the
291
+ // SAME config write.
292
+ if (nextConfig.naming && Object.hasOwn(nextConfig.naming, oldId)) {
293
+ const value = nextConfig.naming[oldId];
294
+ delete nextConfig.naming[oldId];
295
+ nextConfig.naming[newId] = value;
296
+ configChanged = true;
297
+ report.substrateRewrites.push({ file: "config.json", field: "naming", count: 1 });
298
+ }
299
+ // ── Single config write ───────────────────────────────────────────────────
300
+ if (configChanged && !dryRun) {
301
+ writeConfig(cwd, nextConfig);
302
+ }
303
+ // ── Out-of-substrate scan (report-only, ALL kinds, NEVER rewrite) ──────────
304
+ collectAnalysisMatches(cwd, oldId, report);
305
+ collectGitMatches(cwd, oldId, report);
306
+ return report;
307
+ }
308
+ /**
309
+ * Recursively walk `<cwd>/analysis/**.md`; for each file whose text contains
310
+ * oldId, push a report-only entry. Skips silently when the analysis dir is
311
+ * absent. NEVER rewrites file content.
312
+ */
313
+ function collectAnalysisMatches(cwd, oldId, report) {
314
+ const analysisDir = path.join(cwd, "analysis");
315
+ if (!fs.existsSync(analysisDir))
316
+ return;
317
+ const walk = (dir) => {
318
+ let entries;
319
+ try {
320
+ entries = fs.readdirSync(dir, { withFileTypes: true });
321
+ }
322
+ catch {
323
+ return;
324
+ }
325
+ for (const entry of entries) {
326
+ const full = path.join(dir, entry.name);
327
+ if (entry.isDirectory()) {
328
+ walk(full);
329
+ }
330
+ else if (entry.isFile() && entry.name.endsWith(".md")) {
331
+ let text;
332
+ try {
333
+ text = fs.readFileSync(full, "utf-8");
334
+ }
335
+ catch {
336
+ continue;
337
+ }
338
+ if (text.includes(oldId)) {
339
+ const rel = path.relative(cwd, full);
340
+ const firstLine = text.split("\n").find((ln) => ln.includes(oldId));
341
+ report.outOfSubstrate.push({
342
+ source: rel,
343
+ context: firstLine ? firstLine.trim() : "matches in analysis md",
344
+ });
345
+ }
346
+ }
347
+ }
348
+ };
349
+ walk(analysisDir);
350
+ }
351
+ /**
352
+ * Best-effort `git log --oneline -S<oldId>` against `cwd`; each output line
353
+ * becomes a report-only entry. Any failure (no git, not a repo, etc.) is
354
+ * swallowed — the scan is purely informational.
355
+ */
356
+ function collectGitMatches(cwd, oldId, report) {
357
+ try {
358
+ const out = execSync(`git log --oneline -S${shellQuote(oldId)}`, {
359
+ cwd,
360
+ stdio: ["ignore", "pipe", "ignore"],
361
+ encoding: "utf-8",
362
+ });
363
+ for (const line of out.split("\n")) {
364
+ const trimmed = line.trim();
365
+ if (trimmed.length > 0) {
366
+ report.outOfSubstrate.push({ source: "git", context: trimmed });
367
+ }
368
+ }
369
+ }
370
+ catch {
371
+ // no git / not a repo / no matches with nonzero exit — skip silently.
372
+ }
373
+ }
374
+ /** Single-quote a token for safe shell interpolation (escapes embedded quotes). */
375
+ function shellQuote(s) {
376
+ return `'${s.replace(/'/g, "'\\''")}'`;
377
+ }
378
+ //# sourceMappingURL=rename-canonical-id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rename-canonical-id.js","sourceRoot":"","sources":["../src/rename-canonical-id.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC1E,OAAO,EAA+B,UAAU,EAAE,aAAa,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnH,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAgBhD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,iBAAiB,CAChC,GAAW,EACX,IAAY,EACZ,KAAa,EACb,KAAa,EACb,IAA2B;IAE3B,MAAM,MAAM,GAAG,IAAI,EAAE,MAAM,IAAI,KAAK,CAAC;IACrC,MAAM,MAAM,GAAiB;QAC5B,IAAI;QACJ,KAAK;QACL,KAAK;QACL,MAAM;QACN,iBAAiB,EAAE,EAAE;QACrB,cAAc,EAAE,EAAE;KAClB,CAAC;IAEF,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CACd,mQAAmQ,CACnQ,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;IAC/B,IAAI,CAAC,MAAM,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACtD,CAAC;IAED,8EAA8E;IAC9E,0EAA0E;IAC1E,sEAAsE;IACtE,MAAM,UAAU,GAAgB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;IACnE,IAAI,aAAa,GAAG,KAAK,CAAC;IAE1B,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACrB,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;QAC9B,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC3B,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,KAAK,aAAa,CAAC,CAAC;QAC1E,IAAI,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,KAAK,kBAAkB,CAAC,CAAC;QAErG,uBAAuB;QACvB,IAAI,CAAC,MAAM,EAAE,CAAC;YACb,iBAAiB,CAAC,GAAG,EAAE,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QACvF,CAAC;QACD,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC,KAAK,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QAEpF,qEAAqE;QACrE,wEAAwE;QACxE,gBAAgB;QAChB,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,MAAM,IAAI,GAAW,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YACpC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;YACjB,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC;YAChB,IAAI,CAAC,KAAK,KAAK,EAAE,CAAC;gBACjB,CAAC,GAAG,KAAK,CAAC;gBACV,CAAC,EAAE,CAAC;YACL,CAAC;YACD,IAAI,CAAC,KAAK,KAAK,EAAE,CAAC;gBACjB,CAAC,GAAG,KAAK,CAAC;gBACV,CAAC,EAAE,CAAC;YACL,CAAC;YACD,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;QACtC,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,MAAM;gBAAE,cAAc,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YACvC,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QAC5F,CAAC;IACF,CAAC;SAAM,IAAI,IAAI,KAAK,eAAe,EAAE,CAAC;QACrC,MAAM,EAAE,GAAG,MAAM,CAAC,cAAc,IAAI,EAAE,CAAC;QACvC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,KAAK,KAAK,CAAC,EAAE,CAAC;YAC/C,MAAM,IAAI,KAAK,CAAC,qCAAqC,KAAK,aAAa,CAAC,CAAC;QAC1E,CAAC;QACD,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,KAAK,KAAK,CAAC,EAAE,CAAC;YAC9C,MAAM,IAAI,KAAK,CAAC,iDAAiD,KAAK,kBAAkB,CAAC,CAAC;QAC3F,CAAC;QAED,uCAAuC;QACvC,KAAK,MAAM,CAAC,IAAI,UAAU,CAAC,cAAc,IAAI,EAAE,EAAE,CAAC;YACjD,IAAI,CAAC,CAAC,YAAY,KAAK,KAAK,EAAE,CAAC;gBAC9B,CAAC,CAAC,YAAY,GAAG,KAAK,CAAC;gBACvB,aAAa,GAAG,IAAI,CAAC;gBACrB,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,+BAA+B,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;YAC1G,CAAC;QACF,CAAC;QAED,0DAA0D;QAC1D,IAAI,QAAQ,GAAG,CAAC,CAAC;QACjB,KAAK,MAAM,GAAG,IAAI,UAAU,CAAC,UAAU,IAAI,EAAE,EAAE,CAAC;YAC/C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;gBAAE,SAAS;YACjD,GAAG,CAAC,cAAc,GAAG,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;gBACjD,IAAI,CAAC,KAAK,KAAK,EAAE,CAAC;oBACjB,QAAQ,EAAE,CAAC;oBACX,OAAO,KAAK,CAAC;gBACd,CAAC;gBACD,OAAO,CAAC,CAAC;YACV,CAAC,CAAC,CAAC;QACJ,CAAC;QACD,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;YAClB,aAAa,GAAG,IAAI,CAAC;YACrB,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,+BAA+B,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QACjH,CAAC;QAED,gCAAgC;QAChC,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,KAAK,MAAM,CAAC,IAAI,UAAU,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;YACzC,IAAI,CAAC,CAAC,aAAa,KAAK,KAAK,EAAE,CAAC;gBAC/B,CAAC,CAAC,aAAa,GAAG,KAAK,CAAC;gBACxB,SAAS,EAAE,CAAC;YACb,CAAC;QACF,CAAC;QACD,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;YACnB,aAAa,GAAG,IAAI,CAAC;YACrB,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,wBAAwB,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;QAC3G,CAAC;QAED,mCAAmC;QACnC,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,KAAK,MAAM,CAAC,IAAI,UAAU,CAAC,SAAS,IAAI,EAAE,EAAE,CAAC;YAC5C,IAAI,CAAC,CAAC,aAAa,KAAK,KAAK,EAAE,CAAC;gBAC/B,CAAC,CAAC,aAAa,GAAG,KAAK,CAAC;gBACxB,SAAS,EAAE,CAAC;YACb,CAAC;QACF,CAAC;QACD,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;YACnB,aAAa,GAAG,IAAI,CAAC;YACrB,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,2BAA2B,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;QAC9G,CAAC;QAED,wCAAwC;QACxC,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,MAAM,IAAI,GAAW,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YACpC,IAAI,CAAC,CAAC,aAAa,KAAK,KAAK,EAAE,CAAC;gBAC/B,CAAC,EAAE,CAAC;gBACJ,OAAO,EAAE,GAAG,CAAC,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC;YACvC,CAAC;YACD,OAAO,CAAC,CAAC;QACV,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,MAAM;gBAAE,cAAc,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YACvC,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QAC7F,CAAC;QAED,0EAA0E;QAC1E,qEAAqE;QACrE,2EAA2E;QAC3E,IAAI,CAAC;YACJ,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,EAAE,mBAAmB,CAA4B,CAAC;YAC9E,IAAI,OAAO,GAAG,CAAC,CAAC;YAChB,IAAI,QAA4B,CAAC;YACjC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC7C,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;oBACtB,QAAQ,GAAG,CAAC,CAAC;oBACb,MAAM;gBACP,CAAC;YACF,CAAC;YACD,IAAI,QAAQ,EAAE,CAAC;gBACd,MAAM,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAmC,CAAC;gBACrE,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;oBAClC,MAAM,GAAG,GAAG,QAAQ,CAAC,qBAAqB,CAAC;oBAC3C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;wBAAE,SAAS;oBAClC,KAAK,MAAM,KAAK,IAAI,GAAqC,EAAE,CAAC;wBAC3D,IAAI,KAAK,IAAI,KAAK,CAAC,aAAa,KAAK,KAAK,EAAE,CAAC;4BAC5C,KAAK,CAAC,aAAa,GAAG,KAAK,CAAC;4BAC5B,OAAO,EAAE,CAAC;wBACX,CAAC;oBACF,CAAC;gBACF,CAAC;gBACD,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;oBACjB,IAAI,CAAC,MAAM;wBAAE,UAAU,CAAC,GAAG,EAAE,mBAAmB,EAAE,MAAM,CAAC,CAAC;oBAC1D,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC;wBAC7B,IAAI,EAAE,wBAAwB;wBAC9B,KAAK,EAAE,uCAAuC;wBAC9C,KAAK,EAAE,OAAO;qBACd,CAAC,CAAC;gBACJ,CAAC;YACF,CAAC;QACF,CAAC;QAAC,MAAM,CAAC;YACR,kDAAkD;QACnD,CAAC;IACF,CAAC;SAAM,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QAC5B,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC;QAC/B,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,KAAK,aAAa,CAAC,CAAC;QACrG,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CAAC,wCAAwC,KAAK,kBAAkB,CAAC,CAAC;QAClF,CAAC;QAED,qBAAqB;QACrB,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,KAAK,MAAM,CAAC,IAAI,UAAU,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;YACzC,IAAI,CAAC,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;gBACpB,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC;gBACb,OAAO,EAAE,CAAC;YACX,CAAC;QACF,CAAC;QACD,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;YACjB,aAAa,GAAG,IAAI,CAAC;YACrB,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QAC9F,CAAC;QAED,iEAAiE;QACjE,IAAI,WAAW,GAAG,CAAC,CAAC;QACpB,KAAK,MAAM,CAAC,IAAI,UAAU,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;YACzC,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;gBACjC,IAAI,CAAC,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;oBACtB,CAAC,CAAC,IAAI,GAAG,KAAK,CAAC;oBACf,WAAW,EAAE,CAAC;gBACf,CAAC;YACF,CAAC;QACF,CAAC;QACD,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;YACrB,aAAa,GAAG,IAAI,CAAC;YACrB,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,yBAAyB,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;QAC9G,CAAC;IACF,CAAC;SAAM,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QAC7B,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC;QAC/B,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,6BAA6B,KAAK,aAAa,CAAC,CAAC;QACtG,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CAAC,yCAAyC,KAAK,kBAAkB,CAAC,CAAC;QACnF,CAAC;QAED,qBAAqB;QACrB,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,KAAK,MAAM,CAAC,IAAI,UAAU,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;YACzC,IAAI,CAAC,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;gBACpB,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC;gBACb,OAAO,EAAE,CAAC;YACX,CAAC;QACF,CAAC;QACD,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;YACjB,aAAa,GAAG,IAAI,CAAC;YACrB,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QAC9F,CAAC;QAED,iDAAiD;QACjD,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,KAAK,MAAM,EAAE,IAAI,UAAU,CAAC,WAAW,IAAI,EAAE,EAAE,CAAC;YAC/C,IAAI,EAAE,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;gBACxB,EAAE,CAAC,KAAK,GAAG,KAAK,CAAC;gBACjB,OAAO,EAAE,CAAC;YACX,CAAC;QACF,CAAC;QACD,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;YACjB,aAAa,GAAG,IAAI,CAAC;YACrB,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,qBAAqB,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QACtG,CAAC;IACF,CAAC;SAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,oCAAoC,IAAI,GAAG,CAAC,CAAC;IAC9D,CAAC;IAED,6EAA6E;IAC7E,6EAA6E;IAC7E,0EAA0E;IAC1E,qBAAqB;IACrB,IAAI,UAAU,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC;QAClE,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvC,OAAO,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAChC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC;QACjC,aAAa,GAAG,IAAI,CAAC;QACrB,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;IACnF,CAAC;IAED,6EAA6E;IAC7E,IAAI,aAAa,IAAI,CAAC,MAAM,EAAE,CAAC;QAC9B,WAAW,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IAC9B,CAAC;IAED,8EAA8E;IAC9E,sBAAsB,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IAC3C,iBAAiB,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IAEtC,OAAO,MAAM,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,SAAS,sBAAsB,CAAC,GAAW,EAAE,KAAa,EAAE,MAAoB;IAC/E,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IAC/C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC;QAAE,OAAO;IAExC,MAAM,IAAI,GAAG,CAAC,GAAW,EAAQ,EAAE;QAClC,IAAI,OAAoB,CAAC;QACzB,IAAI,CAAC;YACJ,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QACxD,CAAC;QAAC,MAAM,CAAC;YACR,OAAO;QACR,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACxC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBACzB,IAAI,CAAC,IAAI,CAAC,CAAC;YACZ,CAAC;iBAAM,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gBACzD,IAAI,IAAY,CAAC;gBACjB,IAAI,CAAC;oBACJ,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;gBACvC,CAAC;gBAAC,MAAM,CAAC;oBACR,SAAS;gBACV,CAAC;gBACD,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;oBAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;oBACrC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;oBACpE,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC;wBAC1B,MAAM,EAAE,GAAG;wBACX,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,wBAAwB;qBAChE,CAAC,CAAC;gBACJ,CAAC;YACF,CAAC;QACF,CAAC;IACF,CAAC,CAAC;IACF,IAAI,CAAC,WAAW,CAAC,CAAC;AACnB,CAAC;AAED;;;;GAIG;AACH,SAAS,iBAAiB,CAAC,GAAW,EAAE,KAAa,EAAE,MAAoB;IAC1E,IAAI,CAAC;QACJ,MAAM,GAAG,GAAG,QAAQ,CAAC,uBAAuB,UAAU,CAAC,KAAK,CAAC,EAAE,EAAE;YAChE,GAAG;YACH,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;YACnC,QAAQ,EAAE,OAAO;SACjB,CAAC,CAAC;QACH,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACpC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YAC5B,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACxB,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;YACjE,CAAC;QACF,CAAC;IACF,CAAC;IAAC,MAAM,CAAC;QACR,sEAAsE;IACvE,CAAC;AACF,CAAC;AAED,mFAAmF;AACnF,SAAS,UAAU,CAAC,CAAS;IAC5B,OAAO,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC;AACxC,CAAC"}
@@ -0,0 +1,186 @@
1
+ import { type Edge, type ItemRecord, type StatusBucket } from "./context.js";
2
+ import { type LoadedLensView } from "./lens-view.js";
3
+ import { resolveStatusVocabulary, STATUS_VOCABULARY_DEFAULTS } from "./status-vocab.js";
4
+ import { topoSort } from "./topo.js";
5
+ export type { StatusBucket } from "./status-vocab.js";
6
+ export { resolveStatusVocabulary, STATUS_VOCABULARY_DEFAULTS };
7
+ export interface PhaseStatus {
8
+ bucket: StatusBucket;
9
+ counts: Record<StatusBucket, number>;
10
+ total: number;
11
+ }
12
+ /**
13
+ * Bucket each item's status field against the vocabulary; return counts
14
+ * + the rolled-up bucket (most-blocking bucket present, or unknown when
15
+ * items array is empty / no items have a status field).
16
+ *
17
+ * Rollup precedence (most-blocking wins for the bucket field):
18
+ * blocked > in_progress > todo > complete > unknown
19
+ * — meaning a phase with any blocked item rolls up as blocked even if
20
+ * other items are complete; a phase with all-complete items rolls up
21
+ * complete. This matches typical product-management roadmap-status
22
+ * conventions (a phase isn't "in progress" if everything's done; it's
23
+ * "complete"; a phase with anything blocked needs attention).
24
+ *
25
+ * Vocabulary defaults to STATUS_VOCABULARY_DEFAULTS when omitted —
26
+ * loaders that have already resolved per-project overrides via
27
+ * resolveStatusVocabulary should pass the resolved map in to keep
28
+ * config.status_buckets-driven mappings consistent across a single load.
29
+ */
30
+ export declare function rollupPhaseStatus(items: ItemRecord[], vocabulary?: Record<string, StatusBucket>): PhaseStatus;
31
+ /**
32
+ * Generic topological sort with cycle detection. Implementation:
33
+ * Kahn's algorithm — repeatedly remove nodes with no incoming edges,
34
+ * append to order, decrement in-degree of their dependents. Any nodes
35
+ * remaining after the in-degree pass form one or more cycles; the
36
+ * cycle-finding pass uses DFS with recursion-stack tracking to extract
37
+ * each distinct cycle path.
38
+ *
39
+ * Returns:
40
+ * order — node ids in dependency-respecting order. Independent nodes
41
+ * appear in input order (Kahn's algorithm preserves insertion order
42
+ * within a stratum).
43
+ * cycles — array of cycle paths, each as a string[] starting and
44
+ * ending with the same id. Empty when graph is acyclic.
45
+ *
46
+ * Caller chooses whether cycles are fatal: validateRoadmaps surfaces
47
+ * roadmap_phase_cycle; loadRoadmap returns the cycles[] alongside a
48
+ * partial order so renderRoadmap can show what it could and what
49
+ * couldn't be ordered.
50
+ */
51
+ export { topoSort };
52
+ /**
53
+ * Phase entry as authored in roadmap.json. NO depends_on field — ordering
54
+ * lives in relations.json per DEC-0012.
55
+ */
56
+ export interface PhaseSpec {
57
+ id: string;
58
+ name: string;
59
+ description?: string;
60
+ lens: string;
61
+ milestone?: string;
62
+ exit_criteria?: string[];
63
+ }
64
+ /**
65
+ * Inline milestone declared by a roadmap. evidence_block + evidence_query
66
+ * together drive milestoneSatisfied evaluation: milestone is satisfied
67
+ * when at least one item in evidence_block matches every key/value in
68
+ * evidence_query by ===.
69
+ */
70
+ export interface MilestoneSpec {
71
+ id: string;
72
+ name: string;
73
+ criterion?: string;
74
+ evidence_block?: string;
75
+ evidence_query?: Record<string, unknown>;
76
+ }
77
+ /**
78
+ * Roadmap entry as authored in roadmap.json's roadmaps[] array.
79
+ */
80
+ export interface RoadmapSpec {
81
+ id: string;
82
+ title: string;
83
+ description?: string;
84
+ status?: string;
85
+ phases: PhaseSpec[];
86
+ milestones?: MilestoneSpec[];
87
+ }
88
+ /**
89
+ * Per-phase loaded view: phase spec + its lens-view (or error) + status
90
+ * rollup over the lens's items + optional milestone resolution.
91
+ */
92
+ export interface PhaseView {
93
+ phase: PhaseSpec;
94
+ lensView: LoadedLensView | {
95
+ error: string;
96
+ };
97
+ status: PhaseStatus;
98
+ milestone?: MilestoneSpec;
99
+ milestoneSatisfied?: boolean;
100
+ }
101
+ /**
102
+ * Materialized roadmap view. `phases` carries PhaseView entries in
103
+ * authored input order. `phaseOrder` is the topo order over the
104
+ * `phase_depends_on` edges scoped to in-roadmap phases — partial when
105
+ * cycles are present (cycle-participating phases are absent from
106
+ * phaseOrder and surfaced separately in `cycles`). `edges` is the
107
+ * authoritative scoped subset of authored edges and is the renderer's
108
+ * sole source for per-phase adjacency lines (the renderer never infers
109
+ * adjacency from phaseOrder consecutive pairs — that pattern fabricates
110
+ * sibling edges in branching DAGs and is forbidden).
111
+ */
112
+ export interface RoadmapView {
113
+ roadmap: RoadmapSpec;
114
+ phases: PhaseView[];
115
+ phaseOrder: string[];
116
+ cycles: string[][];
117
+ edges: Edge[];
118
+ }
119
+ export interface RoadmapValidationIssue {
120
+ code: "roadmap_lens_missing" | "roadmap_phase_dep_missing" | "roadmap_phase_cycle" | "roadmap_milestone_evidence_block_missing" | "roadmap_milestone_query_invalid" | "roadmap_composition_cycle" | "roadmap_status_unknown_value";
121
+ message: string;
122
+ roadmap_id?: string;
123
+ phase_id?: string;
124
+ cycle?: string[];
125
+ }
126
+ /**
127
+ * Load a single roadmap by id. Returns the materialized RoadmapView or a
128
+ * structured { error } when config / block / id is missing.
129
+ *
130
+ * Algorithm:
131
+ * 1. Load context; bail when no .project/config.json.
132
+ * 2. Defensively read roadmap.json; bail when absent.
133
+ * 3. Lookup the roadmap by id; bail when unknown.
134
+ * 4. Build PhaseView[] in authored input order (loadLensView per phase,
135
+ * rollupPhaseStatus over the resulting items, milestone resolution
136
+ * where declared).
137
+ * 5. Compute the in-roadmap edge subset (phase_depends_on edges whose
138
+ * both endpoints are in the roadmap) and topo-sort to derive
139
+ * phaseOrder + cycles.
140
+ *
141
+ * The returned `edges` field is the authoritative scoped subset that the
142
+ * renderer consumes for per-phase adjacency lines.
143
+ */
144
+ export declare function loadRoadmap(cwd: string, roadmapId: string): RoadmapView | {
145
+ error: string;
146
+ };
147
+ /**
148
+ * Discovery — returns one summary entry per roadmap. Empty array when
149
+ * roadmap.json is absent (listing is discovery; absence is the truthful
150
+ * answer, not an error).
151
+ */
152
+ export declare function listRoadmaps(cwd: string): Array<{
153
+ id: string;
154
+ title: string;
155
+ status?: string;
156
+ phaseCount: number;
157
+ }>;
158
+ /**
159
+ * Validate every roadmap × phase × milestone. Codes are opaque slugs;
160
+ * display strings resolve via diagMessage (config.display_strings →
161
+ * embedded English fallback). The pi-context lens-validator dispatch
162
+ * (registered at module bottom) maps these to ContextValidationIssue-
163
+ * compatible records and merges them into validateContext's output.
164
+ *
165
+ * Roadmap.json absent → { status: "clean", issues: [] } (opt-in block;
166
+ * absence is not a defect).
167
+ */
168
+ export declare function validateRoadmaps(cwd: string): {
169
+ status: "clean" | "warnings" | "invalid";
170
+ issues: RoadmapValidationIssue[];
171
+ };
172
+ /**
173
+ * Render a loaded RoadmapView as pure-textual markdown — NO mermaid, NO
174
+ * diagrams, NO graph syntax. Per-phase **Depends on:** adjacency lines
175
+ * are sourced strictly from view.edges (sorted alphabetically; "—" when
176
+ * the phase has no incoming phase_depends_on edges). Cycle-participating
177
+ * phases are surfaced under a separate "**Unordered (cycle-participating):**"
178
+ * heading and a "**Cycles detected:**" line. The renderer never infers
179
+ * adjacency from phaseOrder consecutive pairs (that pattern fabricates
180
+ * sibling edges in branching DAGs).
181
+ *
182
+ * naming consulted only for lens-target display labels; phase / roadmap
183
+ * ids render as-is.
184
+ */
185
+ export declare function renderRoadmap(view: RoadmapView, naming: Record<string, string> | undefined): string;
186
+ //# sourceMappingURL=roadmap-plan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roadmap-plan.d.ts","sourceRoot":"","sources":["../src/roadmap-plan.ts"],"names":[],"mappings":"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"}