@rasensio/aidlc 1.16.0 → 1.18.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/README.md +9 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +4 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/compliance.d.ts +36 -0
- package/dist/commands/compliance.d.ts.map +1 -0
- package/dist/commands/compliance.js +275 -0
- package/dist/commands/compliance.js.map +1 -0
- package/dist/commands/constitution.d.ts +26 -0
- package/dist/commands/constitution.d.ts.map +1 -0
- package/dist/commands/constitution.js +145 -0
- package/dist/commands/constitution.js.map +1 -0
- package/dist/commands/cost.d.ts.map +1 -1
- package/dist/commands/cost.js +54 -35
- package/dist/commands/cost.js.map +1 -1
- package/dist/commands/doctor.js +7 -0
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/gate.d.ts.map +1 -1
- package/dist/commands/gate.js +13 -0
- package/dist/commands/gate.js.map +1 -1
- package/dist/commands/init.d.ts +14 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +140 -17
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/metrics.d.ts.map +1 -1
- package/dist/commands/metrics.js +6 -1
- package/dist/commands/metrics.js.map +1 -1
- package/dist/commands/review.d.ts +2 -27
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +10 -166
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +7 -0
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/transition.d.ts.map +1 -1
- package/dist/commands/transition.js +24 -1
- package/dist/commands/transition.js.map +1 -1
- package/dist/compile/adapters/claude-code.d.ts.map +1 -1
- package/dist/compile/adapters/claude-code.js +5 -69
- package/dist/compile/adapters/claude-code.js.map +1 -1
- package/dist/compile/adapters/codex.d.ts +22 -1
- package/dist/compile/adapters/codex.d.ts.map +1 -1
- package/dist/compile/adapters/codex.js +61 -27
- package/dist/compile/adapters/codex.js.map +1 -1
- package/dist/compile/adapters/copilot.d.ts +64 -0
- package/dist/compile/adapters/copilot.d.ts.map +1 -0
- package/dist/compile/adapters/copilot.js +272 -0
- package/dist/compile/adapters/copilot.js.map +1 -0
- package/dist/compile/adapters/cursor.d.ts.map +1 -1
- package/dist/compile/adapters/cursor.js +2 -19
- package/dist/compile/adapters/cursor.js.map +1 -1
- package/dist/compile/adapters/index.d.ts.map +1 -1
- package/dist/compile/adapters/index.js +2 -0
- package/dist/compile/adapters/index.js.map +1 -1
- package/dist/compile/adapters/kiro.d.ts.map +1 -1
- package/dist/compile/adapters/kiro.js +1 -19
- package/dist/compile/adapters/kiro.js.map +1 -1
- package/dist/compile/adapters/windsurf.d.ts.map +1 -1
- package/dist/compile/adapters/windsurf.js +2 -1
- package/dist/compile/adapters/windsurf.js.map +1 -1
- package/dist/compile/capability-resolve.d.ts +34 -0
- package/dist/compile/capability-resolve.d.ts.map +1 -0
- package/dist/compile/capability-resolve.js +85 -0
- package/dist/compile/capability-resolve.js.map +1 -0
- package/dist/compile/guidance-install.d.ts +16 -19
- package/dist/compile/guidance-install.d.ts.map +1 -1
- package/dist/compile/guidance-install.js +26 -111
- package/dist/compile/guidance-install.js.map +1 -1
- package/dist/compile/layer-install.d.ts +122 -0
- package/dist/compile/layer-install.d.ts.map +1 -0
- package/dist/compile/layer-install.js +195 -0
- package/dist/compile/layer-install.js.map +1 -0
- package/dist/compile/limits.d.ts +10 -0
- package/dist/compile/limits.d.ts.map +1 -1
- package/dist/compile/limits.js +10 -0
- package/dist/compile/limits.js.map +1 -1
- package/dist/compile/loaders.d.ts +14 -0
- package/dist/compile/loaders.d.ts.map +1 -1
- package/dist/compile/loaders.js +23 -18
- package/dist/compile/loaders.js.map +1 -1
- package/dist/compile/naming.d.ts +23 -0
- package/dist/compile/naming.d.ts.map +1 -1
- package/dist/compile/naming.js +19 -1
- package/dist/compile/naming.js.map +1 -1
- package/dist/compile/skill-format.d.ts +39 -0
- package/dist/compile/skill-format.d.ts.map +1 -0
- package/dist/compile/skill-format.js +50 -0
- package/dist/compile/skill-format.js.map +1 -0
- package/dist/compliance/check.d.ts +79 -0
- package/dist/compliance/check.d.ts.map +1 -0
- package/dist/compliance/check.js +224 -0
- package/dist/compliance/check.js.map +1 -0
- package/dist/compliance/config.d.ts +91 -0
- package/dist/compliance/config.d.ts.map +1 -0
- package/dist/compliance/config.js +173 -0
- package/dist/compliance/config.js.map +1 -0
- package/dist/compliance/findings.d.ts +70 -0
- package/dist/compliance/findings.d.ts.map +1 -0
- package/dist/compliance/findings.js +132 -0
- package/dist/compliance/findings.js.map +1 -0
- package/dist/compliance/gate-criterion.d.ts +87 -0
- package/dist/compliance/gate-criterion.d.ts.map +1 -0
- package/dist/compliance/gate-criterion.js +168 -0
- package/dist/compliance/gate-criterion.js.map +1 -0
- package/dist/compliance/install.d.ts +135 -0
- package/dist/compliance/install.d.ts.map +1 -0
- package/dist/compliance/install.js +291 -0
- package/dist/compliance/install.js.map +1 -0
- package/dist/compliance/report.d.ts +144 -0
- package/dist/compliance/report.d.ts.map +1 -0
- package/dist/compliance/report.js +347 -0
- package/dist/compliance/report.js.map +1 -0
- package/dist/compliance/resolver.d.ts +65 -0
- package/dist/compliance/resolver.d.ts.map +1 -0
- package/dist/compliance/resolver.js +176 -0
- package/dist/compliance/resolver.js.map +1 -0
- package/dist/compliance/schema.d.ts +74 -0
- package/dist/compliance/schema.d.ts.map +1 -0
- package/dist/compliance/schema.js +474 -0
- package/dist/compliance/schema.js.map +1 -0
- package/dist/compliance/types.d.ts +140 -0
- package/dist/compliance/types.d.ts.map +1 -0
- package/dist/compliance/types.js +29 -0
- package/dist/compliance/types.js.map +1 -0
- package/dist/constitution/check.d.ts +22 -0
- package/dist/constitution/check.d.ts.map +1 -0
- package/dist/constitution/check.js +89 -0
- package/dist/constitution/check.js.map +1 -0
- package/dist/constitution/docx.d.ts +51 -0
- package/dist/constitution/docx.d.ts.map +1 -0
- package/dist/constitution/docx.js +157 -0
- package/dist/constitution/docx.js.map +1 -0
- package/dist/constitution/entries.d.ts +27 -0
- package/dist/constitution/entries.d.ts.map +1 -0
- package/dist/constitution/entries.js +65 -0
- package/dist/constitution/entries.js.map +1 -0
- package/dist/constitution/extract.d.ts +34 -0
- package/dist/constitution/extract.d.ts.map +1 -0
- package/dist/constitution/extract.js +117 -0
- package/dist/constitution/extract.js.map +1 -0
- package/dist/constitution/snapshot.d.ts +47 -0
- package/dist/constitution/snapshot.d.ts.map +1 -0
- package/dist/constitution/snapshot.js +159 -0
- package/dist/constitution/snapshot.js.map +1 -0
- package/dist/constitution/statements.d.ts +37 -0
- package/dist/constitution/statements.d.ts.map +1 -0
- package/dist/constitution/statements.js +64 -0
- package/dist/constitution/statements.js.map +1 -0
- package/dist/constitution/types.d.ts +80 -0
- package/dist/constitution/types.d.ts.map +1 -0
- package/dist/constitution/types.js +9 -0
- package/dist/constitution/types.js.map +1 -0
- package/dist/core/gate.d.ts +10 -0
- package/dist/core/gate.d.ts.map +1 -1
- package/dist/core/gate.js.map +1 -1
- package/dist/core/paths.d.ts +48 -0
- package/dist/core/paths.d.ts.map +1 -0
- package/dist/core/paths.js +57 -0
- package/dist/core/paths.js.map +1 -0
- package/dist/core/types.d.ts +13 -2
- package/dist/core/types.d.ts.map +1 -1
- package/dist/cost/hooks/registry.d.ts +44 -0
- package/dist/cost/hooks/registry.d.ts.map +1 -0
- package/dist/cost/hooks/registry.js +66 -0
- package/dist/cost/hooks/registry.js.map +1 -0
- package/dist/cost/pin.d.ts +63 -0
- package/dist/cost/pin.d.ts.map +1 -0
- package/dist/cost/pin.js +95 -0
- package/dist/cost/pin.js.map +1 -0
- package/dist/cost/providers/claude-code.d.ts +9 -2
- package/dist/cost/providers/claude-code.d.ts.map +1 -1
- package/dist/cost/providers/claude-code.js +10 -5
- package/dist/cost/providers/claude-code.js.map +1 -1
- package/dist/cost/providers/codex.d.ts +107 -0
- package/dist/cost/providers/codex.d.ts.map +1 -0
- package/dist/cost/providers/codex.js +283 -0
- package/dist/cost/providers/codex.js.map +1 -0
- package/dist/cost/providers/index.d.ts.map +1 -1
- package/dist/cost/providers/index.js +6 -0
- package/dist/cost/providers/index.js.map +1 -1
- package/dist/cost/rates.d.ts.map +1 -1
- package/dist/cost/rates.js +14 -0
- package/dist/cost/rates.js.map +1 -1
- package/dist/cost/report.d.ts +23 -1
- package/dist/cost/report.d.ts.map +1 -1
- package/dist/cost/report.js +26 -5
- package/dist/cost/report.js.map +1 -1
- package/dist/cost/sessions.d.ts +10 -1
- package/dist/cost/sessions.d.ts.map +1 -1
- package/dist/cost/sessions.js +10 -2
- package/dist/cost/sessions.js.map +1 -1
- package/dist/cost/sync.d.ts.map +1 -1
- package/dist/cost/sync.js +190 -5
- package/dist/cost/sync.js.map +1 -1
- package/dist/doctor/migrations/compliance-pack-drift.d.ts +62 -0
- package/dist/doctor/migrations/compliance-pack-drift.d.ts.map +1 -0
- package/dist/doctor/migrations/compliance-pack-drift.js +255 -0
- package/dist/doctor/migrations/compliance-pack-drift.js.map +1 -0
- package/dist/doctor/migrations/index.d.ts.map +1 -1
- package/dist/doctor/migrations/index.js +11 -0
- package/dist/doctor/migrations/index.js.map +1 -1
- package/dist/doctor/migrations/legacy-cost-hooks.d.ts.map +1 -1
- package/dist/doctor/migrations/legacy-cost-hooks.js +7 -4
- package/dist/doctor/migrations/legacy-cost-hooks.js.map +1 -1
- package/dist/doctor/migrations/missing-cost-shim.d.ts.map +1 -1
- package/dist/doctor/migrations/missing-cost-shim.js +5 -4
- package/dist/doctor/migrations/missing-cost-shim.js.map +1 -1
- package/dist/doctor/migrations/outdated-cost-hooks.d.ts.map +1 -1
- package/dist/doctor/migrations/outdated-cost-hooks.js +7 -4
- package/dist/doctor/migrations/outdated-cost-hooks.js.map +1 -1
- package/dist/doctor/migrations/stale-idea-skill.d.ts.map +1 -1
- package/dist/doctor/migrations/stale-idea-skill.js +2 -1
- package/dist/doctor/migrations/stale-idea-skill.js.map +1 -1
- package/dist/doctor/runner.js +14 -0
- package/dist/doctor/runner.js.map +1 -1
- package/dist/doctor/types.d.ts +35 -3
- package/dist/doctor/types.d.ts.map +1 -1
- package/dist/menu/roster.d.ts.map +1 -1
- package/dist/menu/roster.js +5 -0
- package/dist/menu/roster.js.map +1 -1
- package/dist/metrics/report.d.ts.map +1 -1
- package/dist/metrics/report.js +11 -2
- package/dist/metrics/report.js.map +1 -1
- package/dist/operational/packagers.d.ts.map +1 -1
- package/dist/operational/packagers.js +32 -0
- package/dist/operational/packagers.js.map +1 -1
- package/dist/review/findings-parser.d.ts +92 -0
- package/dist/review/findings-parser.d.ts.map +1 -0
- package/dist/review/findings-parser.js +196 -0
- package/dist/review/findings-parser.js.map +1 -0
- package/dist/setup/setup-compute.d.ts +10 -15
- package/dist/setup/setup-compute.d.ts.map +1 -1
- package/dist/setup/setup-compute.js +39 -0
- package/dist/setup/setup-compute.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability-map validation and activation-directive resolution.
|
|
3
|
+
*
|
|
4
|
+
* Extracted verbatim from `adapters/claude-code.ts`, which held the only
|
|
5
|
+
* validating implementation, at the moment a second adapter (copilot) needed the
|
|
6
|
+
* same behaviour. The one generalisation is that `resolveActivation` now takes
|
|
7
|
+
* the platform id instead of hardcoding `'claude-code'` when reading
|
|
8
|
+
* `native_mode_hint`.
|
|
9
|
+
*
|
|
10
|
+
* Note for anyone adding a third consumer: `adapters/codex.ts` has its own
|
|
11
|
+
* `getCapabilityDirective`, which does NOT validate the map and emits no
|
|
12
|
+
* warnings for a malformed one. It is deliberately left alone here — migrating it
|
|
13
|
+
* would change codex's emitted warnings, which is a behaviour change, not an
|
|
14
|
+
* extraction. Prefer this module for anything new.
|
|
15
|
+
*
|
|
16
|
+
* Requirements: copilot-platform-support/AC-24
|
|
17
|
+
*
|
|
18
|
+
* @module
|
|
19
|
+
*/
|
|
20
|
+
/** Valid lifecycle phase names for capability map validation. */
|
|
21
|
+
const VALID_PHASES = new Set([
|
|
22
|
+
'ideation',
|
|
23
|
+
'requirements',
|
|
24
|
+
'design',
|
|
25
|
+
'implementation',
|
|
26
|
+
'testing',
|
|
27
|
+
'deployment',
|
|
28
|
+
'maintenance',
|
|
29
|
+
]);
|
|
30
|
+
/**
|
|
31
|
+
* Validate and sanitize a capability map. Returns a clean map with only valid
|
|
32
|
+
* entries, or null if the map is entirely unusable. Emits warnings for problems.
|
|
33
|
+
*/
|
|
34
|
+
export function validateCapabilityMap(caps, warnings) {
|
|
35
|
+
const phaseMap = new Map();
|
|
36
|
+
if (!caps.capabilities || typeof caps.capabilities !== 'object') {
|
|
37
|
+
warnings.push(`capability map for '${caps.platform}': malformed capabilities object; proceeding capability-free`);
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
let hasValidEntry = false;
|
|
41
|
+
for (const [capName, entry] of Object.entries(caps.capabilities)) {
|
|
42
|
+
if (!entry || typeof entry !== 'object') {
|
|
43
|
+
warnings.push(`capability map '${caps.platform}': capability '${capName}' is malformed; skipping`);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (!Array.isArray(entry.phases)) {
|
|
47
|
+
warnings.push(`capability map '${caps.platform}': capability '${capName}' has invalid phases; skipping`);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (!entry.activation || typeof entry.activation !== 'string') {
|
|
51
|
+
warnings.push(`capability map '${caps.platform}': capability '${capName}' missing activation text; skipping`);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
for (const phase of entry.phases) {
|
|
55
|
+
if (!VALID_PHASES.has(phase)) {
|
|
56
|
+
warnings.push(`capability map '${caps.platform}': unknown phase '${phase}' in capability '${capName}'; skipping phase`);
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
phaseMap.set(phase, entry);
|
|
60
|
+
hasValidEntry = true;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return hasValidEntry ? phaseMap : null;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Derive the activation directive for a skill given the resolution precedence:
|
|
67
|
+
*
|
|
68
|
+
* 1. `skill.meta.native_mode_hint[platform]` → use it
|
|
69
|
+
* 2. Capability map entry covering the skill's phase → its activation text
|
|
70
|
+
* 3. Otherwise → null, meaning full structured guidance with no native reference
|
|
71
|
+
*/
|
|
72
|
+
export function resolveActivation(skill, platform, phaseMap) {
|
|
73
|
+
const hint = skill.meta.native_mode_hint?.[platform];
|
|
74
|
+
if (hint) {
|
|
75
|
+
return hint;
|
|
76
|
+
}
|
|
77
|
+
if (phaseMap) {
|
|
78
|
+
const entry = phaseMap.get(skill.meta.phase);
|
|
79
|
+
if (entry) {
|
|
80
|
+
return entry.activation;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=capability-resolve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capability-resolve.js","sourceRoot":"","sources":["../../src/compile/capability-resolve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAUH,iEAAiE;AACjE,MAAM,YAAY,GAAwB,IAAI,GAAG,CAAS;IACxD,UAAU;IACV,cAAc;IACd,QAAQ;IACR,gBAAgB;IAChB,SAAS;IACT,YAAY;IACZ,aAAa;CACd,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CACnC,IAAmB,EACnB,QAAkB;IAElB,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA8B,CAAC;IAEvD,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,OAAO,IAAI,CAAC,YAAY,KAAK,QAAQ,EAAE,CAAC;QAChE,QAAQ,CAAC,IAAI,CACX,uBAAuB,IAAI,CAAC,QAAQ,8DAA8D,CACnG,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,aAAa,GAAG,KAAK,CAAC;IAE1B,KAAK,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;QACjE,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YACxC,QAAQ,CAAC,IAAI,CACX,mBAAmB,IAAI,CAAC,QAAQ,kBAAkB,OAAO,0BAA0B,CACpF,CAAC;YACF,SAAS;QACX,CAAC;QAED,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;YACjC,QAAQ,CAAC,IAAI,CACX,mBAAmB,IAAI,CAAC,QAAQ,kBAAkB,OAAO,gCAAgC,CAC1F,CAAC;YACF,SAAS;QACX,CAAC;QAED,IAAI,CAAC,KAAK,CAAC,UAAU,IAAI,OAAO,KAAK,CAAC,UAAU,KAAK,QAAQ,EAAE,CAAC;YAC9D,QAAQ,CAAC,IAAI,CACX,mBAAmB,IAAI,CAAC,QAAQ,kBAAkB,OAAO,qCAAqC,CAC/F,CAAC;YACF,SAAS;QACX,CAAC;QAED,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YACjC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC7B,QAAQ,CAAC,IAAI,CACX,mBAAmB,IAAI,CAAC,QAAQ,qBAAqB,KAAK,oBAAoB,OAAO,mBAAmB,CACzG,CAAC;gBACF,SAAS;YACX,CAAC;YACD,QAAQ,CAAC,GAAG,CAAC,KAAkB,EAAE,KAAK,CAAC,CAAC;YACxC,aAAa,GAAG,IAAI,CAAC;QACvB,CAAC;IACH,CAAC;IAED,OAAO,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;AACzC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAC/B,KAAY,EACZ,QAAoB,EACpB,QAAgD;IAEhD,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC,QAAQ,CAAC,CAAC;IACrD,IAAI,IAAI,EAAE,CAAC;QACT,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7C,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,KAAK,CAAC,UAAU,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -13,27 +13,24 @@
|
|
|
13
13
|
* replaced so registrations the project owns (notably the retrospective-fed
|
|
14
14
|
* `lessons` layer) survive.
|
|
15
15
|
*
|
|
16
|
+
* The mechanics now live in `./layer-install.ts`, which takes an explicit entry
|
|
17
|
+
* list. This module is the framework's caller of it: it passes the content
|
|
18
|
+
* package's whole guidance index and the bundled-body comparison. Compliance
|
|
19
|
+
* packs deliberately do **not** come through here — a pack layer registered in
|
|
20
|
+
* the content guidance index would be installed by every caller of this function,
|
|
21
|
+
* and one of those callers is a `safe`-tier doctor migration that auto-applies
|
|
22
|
+
* without prompting (compliance-packs/AC-53, AC-54).
|
|
23
|
+
*
|
|
16
24
|
* Requirements: guidance-layers-never-resolve/AC-1, AC-3, AC-4
|
|
17
25
|
*/
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
* that is not `<name>.md`.
|
|
27
|
-
*/
|
|
28
|
-
installedPaths: string[];
|
|
29
|
-
/** Layer names already present with identical content. */
|
|
30
|
-
alreadyCurrent: string[];
|
|
31
|
-
/** Layer names present but locally edited — left untouched. */
|
|
32
|
-
locallyModified: string[];
|
|
33
|
-
/** True when the project's `index.yaml` gained entries. */
|
|
34
|
-
indexUpdated: boolean;
|
|
35
|
-
warnings: string[];
|
|
36
|
-
}
|
|
26
|
+
import { type LayerInstallReport } from './layer-install.js';
|
|
27
|
+
/**
|
|
28
|
+
* Outcome of an installation pass.
|
|
29
|
+
*
|
|
30
|
+
* Structurally identical to `LayerInstallReport`; kept as a named alias because
|
|
31
|
+
* `init`, `update` and the doctor migration all import this name.
|
|
32
|
+
*/
|
|
33
|
+
export type GuidanceInstallReport = LayerInstallReport;
|
|
37
34
|
/**
|
|
38
35
|
* Install every guidance layer registered in the content package into a project.
|
|
39
36
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guidance-install.d.ts","sourceRoot":"","sources":["../../src/compile/guidance-install.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"guidance-install.d.ts","sourceRoot":"","sources":["../../src/compile/guidance-install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH,OAAO,EAIL,KAAK,kBAAkB,EACxB,MAAM,oBAAoB,CAAC;AAE5B;;;;;GAKG;AACH,MAAM,MAAM,qBAAqB,GAAG,kBAAkB,CAAC;AAEvD;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GAC9B,qBAAqB,CAuBvB;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,qBAAqB,GAAG,MAAM,GAAG,IAAI,CASrF"}
|
|
@@ -13,58 +13,18 @@
|
|
|
13
13
|
* replaced so registrations the project owns (notably the retrospective-fed
|
|
14
14
|
* `lessons` layer) survive.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
* Read and validate a guidance `index.yaml`, returning its entries.
|
|
24
|
-
*
|
|
25
|
-
* @returns Entries, or null when the file is absent or unusable.
|
|
26
|
-
*/
|
|
27
|
-
function readIndexEntries(indexPath) {
|
|
28
|
-
if (!existsSync(indexPath)) {
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
let parsed;
|
|
32
|
-
try {
|
|
33
|
-
parsed = parseYaml(readFileSync(indexPath, 'utf8'));
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
if (typeof parsed !== 'object' || parsed === null || !('layers' in parsed)) {
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
const layers = parsed.layers;
|
|
42
|
-
if (!Array.isArray(layers)) {
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
return layers.filter((e) => typeof e === 'object' &&
|
|
46
|
-
e !== null &&
|
|
47
|
-
typeof e.name === 'string' &&
|
|
48
|
-
typeof e.file === 'string');
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* True when `file` is a safe relative name that stays inside `dir`.
|
|
16
|
+
* The mechanics now live in `./layer-install.ts`, which takes an explicit entry
|
|
17
|
+
* list. This module is the framework's caller of it: it passes the content
|
|
18
|
+
* package's whole guidance index and the bundled-body comparison. Compliance
|
|
19
|
+
* packs deliberately do **not** come through here — a pack layer registered in
|
|
20
|
+
* the content guidance index would be installed by every caller of this function,
|
|
21
|
+
* and one of those callers is a `safe`-tier doctor migration that auto-applies
|
|
22
|
+
* without prompting (compliance-packs/AC-53, AC-54).
|
|
52
23
|
*
|
|
53
|
-
*
|
|
54
|
-
* path or a `..` segment would make `join` escape `.aidlc/guidance/` and, on the
|
|
55
|
-
* target side, write outside it. Checked lexically **before** any disk access —
|
|
56
|
-
* ordering this after an existence check is how a sibling defect in this repo
|
|
57
|
-
* turned an error path into an out-of-bounds oracle.
|
|
24
|
+
* Requirements: guidance-layers-never-resolve/AC-1, AC-3, AC-4
|
|
58
25
|
*/
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return false;
|
|
62
|
-
if (file.split(/[\\/]/).includes('..'))
|
|
63
|
-
return false;
|
|
64
|
-
const root = resolve(dir);
|
|
65
|
-
const target = resolve(dir, file);
|
|
66
|
-
return target !== root && target.startsWith(root + sep);
|
|
67
|
-
}
|
|
26
|
+
import { join } from 'node:path';
|
|
27
|
+
import { bundledBodyDiffers, installLayers, readIndexEntries, } from './layer-install.js';
|
|
68
28
|
/**
|
|
69
29
|
* Install every guidance layer registered in the content package into a project.
|
|
70
30
|
*
|
|
@@ -73,71 +33,26 @@ function isContainedRelative(dir, file) {
|
|
|
73
33
|
* @param opts.dryRun - Compute the report without writing anything.
|
|
74
34
|
*/
|
|
75
35
|
export function installBundledGuidance(projectRoot, contentRoot, opts = {}) {
|
|
76
|
-
const report = {
|
|
77
|
-
installed: [],
|
|
78
|
-
installedPaths: [],
|
|
79
|
-
alreadyCurrent: [],
|
|
80
|
-
locallyModified: [],
|
|
81
|
-
indexUpdated: false,
|
|
82
|
-
warnings: [],
|
|
83
|
-
};
|
|
84
36
|
const bundledDir = join(contentRoot, 'guidance');
|
|
85
37
|
const bundledEntries = readIndexEntries(join(bundledDir, 'index.yaml'));
|
|
86
38
|
if (!bundledEntries) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (!isContainedRelative(bundledDir, entry.file) || !isContainedRelative(targetDir, entry.file)) {
|
|
98
|
-
report.warnings.push(`guidance layer '${entry.name}' registers an out-of-bounds file and was skipped: ${entry.file}`);
|
|
99
|
-
continue;
|
|
100
|
-
}
|
|
101
|
-
const source = join(bundledDir, entry.file);
|
|
102
|
-
if (!existsSync(source)) {
|
|
103
|
-
report.warnings.push(`guidance layer '${entry.name}' is registered in the content package but its file is missing: ${entry.file}`);
|
|
104
|
-
continue;
|
|
105
|
-
}
|
|
106
|
-
const bundledBody = readFileSync(source, 'utf8');
|
|
107
|
-
const target = join(targetDir, entry.file);
|
|
108
|
-
if (existsSync(target)) {
|
|
109
|
-
// A layer the project has edited is the project's, not ours (AC-4).
|
|
110
|
-
if (readFileSync(target, 'utf8') === bundledBody) {
|
|
111
|
-
report.alreadyCurrent.push(entry.name);
|
|
112
|
-
}
|
|
113
|
-
else {
|
|
114
|
-
report.locallyModified.push(entry.name);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
report.installed.push(entry.name);
|
|
119
|
-
report.installedPaths.push(`.aidlc/guidance/${entry.file}`);
|
|
120
|
-
if (!opts.dryRun) {
|
|
121
|
-
mkdirSync(targetDir, { recursive: true });
|
|
122
|
-
writeFileSync(target, bundledBody, 'utf8');
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
// Register by name regardless of whether the body was written, so a
|
|
126
|
-
// locally-modified layer that is missing from the index still becomes active.
|
|
127
|
-
if (!registeredNames.has(entry.name)) {
|
|
128
|
-
mergedEntries.push({ name: entry.name, file: entry.file });
|
|
129
|
-
registeredNames.add(entry.name);
|
|
130
|
-
report.indexUpdated = true;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
if (report.indexUpdated) {
|
|
134
|
-
report.installedPaths.push('.aidlc/guidance/index.yaml');
|
|
135
|
-
if (!opts.dryRun) {
|
|
136
|
-
mkdirSync(targetDir, { recursive: true });
|
|
137
|
-
writeFileSync(join(targetDir, 'index.yaml'), serializeYaml({ layers: mergedEntries }), 'utf8');
|
|
138
|
-
}
|
|
39
|
+
return {
|
|
40
|
+
installed: [],
|
|
41
|
+
installedPaths: [],
|
|
42
|
+
alreadyCurrent: [],
|
|
43
|
+
locallyModified: [],
|
|
44
|
+
indexUpdated: false,
|
|
45
|
+
warnings: [
|
|
46
|
+
`content package has no readable guidance index at ${join(bundledDir, 'index.yaml')}`,
|
|
47
|
+
],
|
|
48
|
+
};
|
|
139
49
|
}
|
|
140
|
-
return
|
|
50
|
+
return installLayers(bundledDir, join(projectRoot, '.aidlc', 'guidance'), bundledEntries, {
|
|
51
|
+
isModified: bundledBodyDiffers,
|
|
52
|
+
relPrefix: '.aidlc/guidance',
|
|
53
|
+
label: 'guidance layer',
|
|
54
|
+
...(opts.dryRun !== undefined && { dryRun: opts.dryRun }),
|
|
55
|
+
});
|
|
141
56
|
}
|
|
142
57
|
/**
|
|
143
58
|
* Render a one-line summary of an installation pass, or null when nothing happened.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guidance-install.js","sourceRoot":"","sources":["../../src/compile/guidance-install.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"guidance-install.js","sourceRoot":"","sources":["../../src/compile/guidance-install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,gBAAgB,GAEjB,MAAM,oBAAoB,CAAC;AAU5B;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CACpC,WAAmB,EACnB,WAAmB,EACnB,OAA6B,EAAE;IAE/B,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IACjD,MAAM,cAAc,GAAG,gBAAgB,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC,CAAC;IAExE,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,OAAO;YACL,SAAS,EAAE,EAAE;YACb,cAAc,EAAE,EAAE;YAClB,cAAc,EAAE,EAAE;YAClB,eAAe,EAAE,EAAE;YACnB,YAAY,EAAE,KAAK;YACnB,QAAQ,EAAE;gBACR,qDAAqD,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,EAAE;aACtF;SACF,CAAC;IACJ,CAAC;IAED,OAAO,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,WAAW,EAAE,QAAQ,EAAE,UAAU,CAAC,EAAE,cAAc,EAAE;QACxF,UAAU,EAAE,kBAAkB;QAC9B,SAAS,EAAE,iBAAiB;QAC5B,KAAK,EAAE,gBAAgB;QACvB,GAAG,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;KAC1D,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,wBAAwB,CAAC,MAA6B;IACpE,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChC,KAAK,CAAC,IAAI,CAAC,aAAa,MAAM,CAAC,SAAS,CAAC,MAAM,uBAAuB,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACvG,CAAC;IACD,IAAI,MAAM,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtC,KAAK,CAAC,IAAI,CAAC,QAAQ,MAAM,CAAC,eAAe,CAAC,MAAM,sBAAsB,MAAM,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7G,CAAC;IACD,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACnE,CAAC"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The guidance-layer install primitive.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from `guidance-install.ts` so compliance packs can reuse the copy,
|
|
5
|
+
* the containment check and the index merge without reusing its entry point.
|
|
6
|
+
* `installBundledGuidance` installs *every* layer registered in the content
|
|
7
|
+
* package's guidance index and is called from `init`, `update`, and a `safe`-tier
|
|
8
|
+
* doctor migration that auto-applies without prompting — so a pack layer
|
|
9
|
+
* registered in that index would land in every upgrading project with no pack
|
|
10
|
+
* requested. This primitive takes an explicit entry list and has no
|
|
11
|
+
* install-everything mode.
|
|
12
|
+
*
|
|
13
|
+
* **The modified-detector is injected, not shared.** `installBundledGuidance`
|
|
14
|
+
* decides "locally modified" by comparing the on-disk body against the
|
|
15
|
+
* *currently bundled* body. That is right for framework layers, whose bundled
|
|
16
|
+
* body is the only reference available, and wrong for packs: a pack whose content
|
|
17
|
+
* is updated upstream would make every installed layer read as locally edited, so
|
|
18
|
+
* `remove` would refuse to delete anything and corrected control text would never
|
|
19
|
+
* land. Packs compare against the hash recorded at install instead. A primitive
|
|
20
|
+
* that hard-coded either rule would be wrong for one caller.
|
|
21
|
+
*
|
|
22
|
+
* Requirements: compliance-packs/AC-63 (single containment definition), and the
|
|
23
|
+
* behaviour `guidance-layers-never-resolve/AC-1, AC-3, AC-4, AC-5, AC-14`
|
|
24
|
+
* established, which this file now carries.
|
|
25
|
+
*/
|
|
26
|
+
/** One entry in a guidance `index.yaml`. Extra keys are data and are preserved. */
|
|
27
|
+
export interface GuidanceIndexEntry {
|
|
28
|
+
name: string;
|
|
29
|
+
file: string;
|
|
30
|
+
[key: string]: unknown;
|
|
31
|
+
}
|
|
32
|
+
/** Outcome of an installation pass. */
|
|
33
|
+
export interface LayerInstallReport {
|
|
34
|
+
/** Layer names newly written to disk. */
|
|
35
|
+
installed: string[];
|
|
36
|
+
/**
|
|
37
|
+
* Repo-relative paths newly written, including `index.yaml` when it changed.
|
|
38
|
+
* Kept alongside `installed` so callers never have to reconstruct a filename
|
|
39
|
+
* from a layer name — the two differ whenever a layer registers a filename
|
|
40
|
+
* that is not `<name>.md`.
|
|
41
|
+
*/
|
|
42
|
+
installedPaths: string[];
|
|
43
|
+
/** Layer names already present with identical content. */
|
|
44
|
+
alreadyCurrent: string[];
|
|
45
|
+
/** Layer names present but modified per the injected detector — left untouched. */
|
|
46
|
+
locallyModified: string[];
|
|
47
|
+
/** True when the target `index.yaml` gained entries. */
|
|
48
|
+
indexUpdated: boolean;
|
|
49
|
+
warnings: string[];
|
|
50
|
+
}
|
|
51
|
+
/** How a caller decides a layer on disk is not ours to overwrite. */
|
|
52
|
+
export type ModifiedDetector = (targetPath: string, bundledBody: string) => boolean;
|
|
53
|
+
export interface InstallLayersOptions {
|
|
54
|
+
/** Decides whether an existing target counts as locally modified. */
|
|
55
|
+
isModified: ModifiedDetector;
|
|
56
|
+
/** Repo-relative prefix for `installedPaths`, e.g. `.aidlc/guidance`. */
|
|
57
|
+
relPrefix: string;
|
|
58
|
+
/** Compute the report without writing anything. */
|
|
59
|
+
dryRun?: boolean;
|
|
60
|
+
/** Label used in warnings, e.g. `guidance layer` or `pack 'soc2' layer`. */
|
|
61
|
+
label?: string;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Read and validate a guidance `index.yaml`, returning its entries.
|
|
65
|
+
*
|
|
66
|
+
* @returns Entries, or null when the file is absent or unusable.
|
|
67
|
+
*/
|
|
68
|
+
export declare function readIndexEntries(indexPath: string): GuidanceIndexEntry[] | null;
|
|
69
|
+
/**
|
|
70
|
+
* True when `file` is a safe relative name that stays inside `dir`.
|
|
71
|
+
*
|
|
72
|
+
* `file` comes out of a YAML index, so it is data, not a constant: an absolute
|
|
73
|
+
* path or a `..` segment would make `join` escape the guidance directory and, on
|
|
74
|
+
* the target side, write outside it. Checked lexically **before** any disk access
|
|
75
|
+
* — ordering this after an existence check is how a sibling defect in this repo
|
|
76
|
+
* turned an error path into an out-of-bounds oracle.
|
|
77
|
+
*
|
|
78
|
+
* Exported so there is exactly one definition of the rule; a pack loader calls
|
|
79
|
+
* this rather than writing a second one (compliance-packs/AC-63).
|
|
80
|
+
*/
|
|
81
|
+
export declare function isContainedRelative(dir: string, file: string): boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Copy an explicit list of layers into `targetDir` and merge the target index.
|
|
84
|
+
*
|
|
85
|
+
* Non-destructive by construction: a layer the injected detector reports as
|
|
86
|
+
* modified is left alone, and existing index entries are preserved wholesale
|
|
87
|
+
* rather than rebuilt, so fields the target owns (notably the retrospective-fed
|
|
88
|
+
* `lessons` layer's `last_consolidated`) survive.
|
|
89
|
+
*
|
|
90
|
+
* @param sourceDir - Directory holding the layer bodies to copy from.
|
|
91
|
+
* @param targetDir - Directory to install into, e.g. `<root>/.aidlc/guidance`.
|
|
92
|
+
* @param entries - The layers to install. There is no "everything" mode.
|
|
93
|
+
* @param opts - Injected detector, path prefix, dry-run flag, warning label.
|
|
94
|
+
*/
|
|
95
|
+
export declare function installLayers(sourceDir: string, targetDir: string, entries: GuidanceIndexEntry[], opts: InstallLayersOptions): LayerInstallReport;
|
|
96
|
+
/**
|
|
97
|
+
* The framework's modified-detector: the on-disk body differs from the bundled one.
|
|
98
|
+
*
|
|
99
|
+
* Correct for bundled framework layers, whose bundled body is the only reference
|
|
100
|
+
* that exists. Deliberately **not** used for packs — see the module note.
|
|
101
|
+
*/
|
|
102
|
+
export declare const bundledBodyDiffers: ModifiedDetector;
|
|
103
|
+
/**
|
|
104
|
+
* Drop layer registrations from a target index by name.
|
|
105
|
+
*
|
|
106
|
+
* Removing the registration is what makes a layer *inert*, and it is separate from
|
|
107
|
+
* deleting the file on purpose. When a pack is removed but one of its layers has
|
|
108
|
+
* been locally edited, the bytes stay (they are the project's now) while the
|
|
109
|
+
* registration goes — otherwise `loadGuidanceLayers` keeps handing an uninstalled
|
|
110
|
+
* pack's constraints to every agent, which is the drift state `doctor` classifies
|
|
111
|
+
* as a defect.
|
|
112
|
+
*
|
|
113
|
+
* Never touches the files themselves, and never touches a registration it was not
|
|
114
|
+
* asked about.
|
|
115
|
+
*
|
|
116
|
+
* @returns The names actually removed, so a caller can report accurately rather
|
|
117
|
+
* than assuming.
|
|
118
|
+
*/
|
|
119
|
+
export declare function unregisterLayers(targetDir: string, names: string[], opts?: {
|
|
120
|
+
dryRun?: boolean;
|
|
121
|
+
}): string[];
|
|
122
|
+
//# sourceMappingURL=layer-install.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layer-install.d.ts","sourceRoot":"","sources":["../../src/compile/layer-install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAOH,mFAAmF;AACnF,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,uCAAuC;AACvC,MAAM,WAAW,kBAAkB;IACjC,yCAAyC;IACzC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB;;;;;OAKG;IACH,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,0DAA0D;IAC1D,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,mFAAmF;IACnF,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,wDAAwD;IACxD,YAAY,EAAE,OAAO,CAAC;IACtB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,qEAAqE;AACrE,MAAM,MAAM,gBAAgB,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC;AAEpF,MAAM,WAAW,oBAAoB;IACnC,qEAAqE;IACrE,UAAU,EAAE,gBAAgB,CAAC;IAC7B,yEAAyE;IACzE,SAAS,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,kBAAkB,EAAE,GAAG,IAAI,CA4B/E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAOtE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,kBAAkB,EAAE,EAC7B,IAAI,EAAE,oBAAoB,GACzB,kBAAkB,CA4EpB;AAED;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,EAAE,gBACiB,CAAC;AAEnD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EAAE,EACf,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GAC9B,MAAM,EAAE,CAaV"}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The guidance-layer install primitive.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from `guidance-install.ts` so compliance packs can reuse the copy,
|
|
5
|
+
* the containment check and the index merge without reusing its entry point.
|
|
6
|
+
* `installBundledGuidance` installs *every* layer registered in the content
|
|
7
|
+
* package's guidance index and is called from `init`, `update`, and a `safe`-tier
|
|
8
|
+
* doctor migration that auto-applies without prompting — so a pack layer
|
|
9
|
+
* registered in that index would land in every upgrading project with no pack
|
|
10
|
+
* requested. This primitive takes an explicit entry list and has no
|
|
11
|
+
* install-everything mode.
|
|
12
|
+
*
|
|
13
|
+
* **The modified-detector is injected, not shared.** `installBundledGuidance`
|
|
14
|
+
* decides "locally modified" by comparing the on-disk body against the
|
|
15
|
+
* *currently bundled* body. That is right for framework layers, whose bundled
|
|
16
|
+
* body is the only reference available, and wrong for packs: a pack whose content
|
|
17
|
+
* is updated upstream would make every installed layer read as locally edited, so
|
|
18
|
+
* `remove` would refuse to delete anything and corrected control text would never
|
|
19
|
+
* land. Packs compare against the hash recorded at install instead. A primitive
|
|
20
|
+
* that hard-coded either rule would be wrong for one caller.
|
|
21
|
+
*
|
|
22
|
+
* Requirements: compliance-packs/AC-63 (single containment definition), and the
|
|
23
|
+
* behaviour `guidance-layers-never-resolve/AC-1, AC-3, AC-4, AC-5, AC-14`
|
|
24
|
+
* established, which this file now carries.
|
|
25
|
+
*/
|
|
26
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
27
|
+
import { isAbsolute, join, resolve, sep } from 'node:path';
|
|
28
|
+
import { parse as parseYaml } from 'yaml';
|
|
29
|
+
import { serializeYaml } from '../state/yaml-helpers.js';
|
|
30
|
+
/**
|
|
31
|
+
* Read and validate a guidance `index.yaml`, returning its entries.
|
|
32
|
+
*
|
|
33
|
+
* @returns Entries, or null when the file is absent or unusable.
|
|
34
|
+
*/
|
|
35
|
+
export function readIndexEntries(indexPath) {
|
|
36
|
+
if (!existsSync(indexPath)) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
let parsed;
|
|
40
|
+
try {
|
|
41
|
+
parsed = parseYaml(readFileSync(indexPath, 'utf8'));
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
if (typeof parsed !== 'object' || parsed === null || !('layers' in parsed)) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
const layers = parsed.layers;
|
|
50
|
+
if (!Array.isArray(layers)) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
return layers.filter((e) => typeof e === 'object' &&
|
|
54
|
+
e !== null &&
|
|
55
|
+
typeof e.name === 'string' &&
|
|
56
|
+
typeof e.file === 'string');
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* True when `file` is a safe relative name that stays inside `dir`.
|
|
60
|
+
*
|
|
61
|
+
* `file` comes out of a YAML index, so it is data, not a constant: an absolute
|
|
62
|
+
* path or a `..` segment would make `join` escape the guidance directory and, on
|
|
63
|
+
* the target side, write outside it. Checked lexically **before** any disk access
|
|
64
|
+
* — ordering this after an existence check is how a sibling defect in this repo
|
|
65
|
+
* turned an error path into an out-of-bounds oracle.
|
|
66
|
+
*
|
|
67
|
+
* Exported so there is exactly one definition of the rule; a pack loader calls
|
|
68
|
+
* this rather than writing a second one (compliance-packs/AC-63).
|
|
69
|
+
*/
|
|
70
|
+
export function isContainedRelative(dir, file) {
|
|
71
|
+
if (file === '' || isAbsolute(file))
|
|
72
|
+
return false;
|
|
73
|
+
if (file.split(/[\\/]/).includes('..'))
|
|
74
|
+
return false;
|
|
75
|
+
const root = resolve(dir);
|
|
76
|
+
const target = resolve(dir, file);
|
|
77
|
+
return target !== root && target.startsWith(root + sep);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Copy an explicit list of layers into `targetDir` and merge the target index.
|
|
81
|
+
*
|
|
82
|
+
* Non-destructive by construction: a layer the injected detector reports as
|
|
83
|
+
* modified is left alone, and existing index entries are preserved wholesale
|
|
84
|
+
* rather than rebuilt, so fields the target owns (notably the retrospective-fed
|
|
85
|
+
* `lessons` layer's `last_consolidated`) survive.
|
|
86
|
+
*
|
|
87
|
+
* @param sourceDir - Directory holding the layer bodies to copy from.
|
|
88
|
+
* @param targetDir - Directory to install into, e.g. `<root>/.aidlc/guidance`.
|
|
89
|
+
* @param entries - The layers to install. There is no "everything" mode.
|
|
90
|
+
* @param opts - Injected detector, path prefix, dry-run flag, warning label.
|
|
91
|
+
*/
|
|
92
|
+
export function installLayers(sourceDir, targetDir, entries, opts) {
|
|
93
|
+
const label = opts.label ?? 'guidance layer';
|
|
94
|
+
const report = {
|
|
95
|
+
installed: [],
|
|
96
|
+
installedPaths: [],
|
|
97
|
+
alreadyCurrent: [],
|
|
98
|
+
locallyModified: [],
|
|
99
|
+
indexUpdated: false,
|
|
100
|
+
warnings: [],
|
|
101
|
+
};
|
|
102
|
+
const indexPath = join(targetDir, 'index.yaml');
|
|
103
|
+
const existingEntries = readIndexEntries(indexPath);
|
|
104
|
+
const mergedEntries = existingEntries ? [...existingEntries] : [];
|
|
105
|
+
const registeredNames = new Set(mergedEntries.map((e) => e.name));
|
|
106
|
+
for (const entry of entries) {
|
|
107
|
+
// Bounds before existence: a registration naming an absolute path or a `..`
|
|
108
|
+
// segment is rejected outright rather than resolved and then stat'd.
|
|
109
|
+
if (!isContainedRelative(sourceDir, entry.file) || !isContainedRelative(targetDir, entry.file)) {
|
|
110
|
+
report.warnings.push(`${label} '${entry.name}' registers an out-of-bounds file and was skipped: ${entry.file}`);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
const source = join(sourceDir, entry.file);
|
|
114
|
+
if (!existsSync(source)) {
|
|
115
|
+
report.warnings.push(`${label} '${entry.name}' is registered but its file is missing: ${entry.file}`);
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const bundledBody = readFileSync(source, 'utf8');
|
|
119
|
+
const target = join(targetDir, entry.file);
|
|
120
|
+
if (existsSync(target)) {
|
|
121
|
+
// A layer the project has changed is the project's, not ours.
|
|
122
|
+
if (opts.isModified(target, bundledBody)) {
|
|
123
|
+
report.locallyModified.push(entry.name);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
report.alreadyCurrent.push(entry.name);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
report.installed.push(entry.name);
|
|
131
|
+
report.installedPaths.push(`${opts.relPrefix}/${entry.file}`);
|
|
132
|
+
if (!opts.dryRun) {
|
|
133
|
+
mkdirSync(targetDir, { recursive: true });
|
|
134
|
+
writeFileSync(target, bundledBody, 'utf8');
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// Register by name regardless of whether the body was written, so a
|
|
138
|
+
// locally-modified layer that is missing from the index still becomes active.
|
|
139
|
+
//
|
|
140
|
+
// The whole entry is spread rather than rebuilt as `{name, file}`: rebuilding
|
|
141
|
+
// silently drops every other key on first registration, which is where a pack
|
|
142
|
+
// layer's `always_pointer` flag would be lost. Safe because `readIndexEntries`
|
|
143
|
+
// already guarantees `name` and `file` are strings.
|
|
144
|
+
if (!registeredNames.has(entry.name)) {
|
|
145
|
+
mergedEntries.push({ ...entry });
|
|
146
|
+
registeredNames.add(entry.name);
|
|
147
|
+
report.indexUpdated = true;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (report.indexUpdated) {
|
|
151
|
+
report.installedPaths.push(`${opts.relPrefix}/index.yaml`);
|
|
152
|
+
if (!opts.dryRun) {
|
|
153
|
+
mkdirSync(targetDir, { recursive: true });
|
|
154
|
+
writeFileSync(indexPath, serializeYaml({ layers: mergedEntries }), 'utf8');
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return report;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* The framework's modified-detector: the on-disk body differs from the bundled one.
|
|
161
|
+
*
|
|
162
|
+
* Correct for bundled framework layers, whose bundled body is the only reference
|
|
163
|
+
* that exists. Deliberately **not** used for packs — see the module note.
|
|
164
|
+
*/
|
|
165
|
+
export const bundledBodyDiffers = (targetPath, bundledBody) => readFileSync(targetPath, 'utf8') !== bundledBody;
|
|
166
|
+
/**
|
|
167
|
+
* Drop layer registrations from a target index by name.
|
|
168
|
+
*
|
|
169
|
+
* Removing the registration is what makes a layer *inert*, and it is separate from
|
|
170
|
+
* deleting the file on purpose. When a pack is removed but one of its layers has
|
|
171
|
+
* been locally edited, the bytes stay (they are the project's now) while the
|
|
172
|
+
* registration goes — otherwise `loadGuidanceLayers` keeps handing an uninstalled
|
|
173
|
+
* pack's constraints to every agent, which is the drift state `doctor` classifies
|
|
174
|
+
* as a defect.
|
|
175
|
+
*
|
|
176
|
+
* Never touches the files themselves, and never touches a registration it was not
|
|
177
|
+
* asked about.
|
|
178
|
+
*
|
|
179
|
+
* @returns The names actually removed, so a caller can report accurately rather
|
|
180
|
+
* than assuming.
|
|
181
|
+
*/
|
|
182
|
+
export function unregisterLayers(targetDir, names, opts = {}) {
|
|
183
|
+
const indexPath = join(targetDir, 'index.yaml');
|
|
184
|
+
const entries = readIndexEntries(indexPath);
|
|
185
|
+
if (!entries)
|
|
186
|
+
return [];
|
|
187
|
+
const wanted = new Set(names);
|
|
188
|
+
const kept = entries.filter((e) => !wanted.has(e.name));
|
|
189
|
+
const removed = entries.filter((e) => wanted.has(e.name)).map((e) => e.name);
|
|
190
|
+
if (removed.length > 0 && !opts.dryRun) {
|
|
191
|
+
writeFileSync(indexPath, serializeYaml({ layers: kept }), 'utf8');
|
|
192
|
+
}
|
|
193
|
+
return removed;
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=layer-install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layer-install.js","sourceRoot":"","sources":["../../src/compile/layer-install.ts"],"names":[],"mappings":"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"}
|