@aperant/framework 0.6.7 → 0.7.3

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 (205) hide show
  1. package/CHANGELOG.md +240 -0
  2. package/agents/apt-planner.md +12 -0
  3. package/agents/apt-pr-review-fixer.md +13 -9
  4. package/bin/apt-tools.mjs +7 -0
  5. package/dist/cli/ci-watch/stop-matrix.d.mts.map +1 -1
  6. package/dist/cli/ci-watch/stop-matrix.mjs +16 -0
  7. package/dist/cli/ci-watch/stop-matrix.mjs.map +1 -1
  8. package/dist/cli/commands/ci-watch.d.mts +11 -0
  9. package/dist/cli/commands/ci-watch.d.mts.map +1 -1
  10. package/dist/cli/commands/ci-watch.mjs +147 -3
  11. package/dist/cli/commands/ci-watch.mjs.map +1 -1
  12. package/dist/cli/commands/features-audit.d.mts +24 -0
  13. package/dist/cli/commands/features-audit.d.mts.map +1 -1
  14. package/dist/cli/commands/features-audit.mjs +159 -5
  15. package/dist/cli/commands/features-audit.mjs.map +1 -1
  16. package/dist/cli/commands/health-check.d.mts +16 -0
  17. package/dist/cli/commands/health-check.d.mts.map +1 -1
  18. package/dist/cli/commands/health-check.mjs +119 -3
  19. package/dist/cli/commands/health-check.mjs.map +1 -1
  20. package/dist/cli/commands/init.d.mts +19 -1
  21. package/dist/cli/commands/init.d.mts.map +1 -1
  22. package/dist/cli/commands/init.mjs +143 -8
  23. package/dist/cli/commands/init.mjs.map +1 -1
  24. package/dist/cli/commands/modes.d.mts.map +1 -1
  25. package/dist/cli/commands/modes.mjs +11 -0
  26. package/dist/cli/commands/modes.mjs.map +1 -1
  27. package/dist/cli/commands/pr-review-audit-fixer.d.mts +13 -0
  28. package/dist/cli/commands/pr-review-audit-fixer.d.mts.map +1 -1
  29. package/dist/cli/commands/pr-review-audit-fixer.mjs +18 -5
  30. package/dist/cli/commands/pr-review-audit-fixer.mjs.map +1 -1
  31. package/dist/cli/commands/route.d.mts.map +1 -1
  32. package/dist/cli/commands/route.mjs +37 -2
  33. package/dist/cli/commands/route.mjs.map +1 -1
  34. package/dist/cli/commands/task.d.mts.map +1 -1
  35. package/dist/cli/commands/task.mjs +132 -5
  36. package/dist/cli/commands/task.mjs.map +1 -1
  37. package/dist/cli/commands/validate-evidence.d.mts +24 -2
  38. package/dist/cli/commands/validate-evidence.d.mts.map +1 -1
  39. package/dist/cli/commands/validate-evidence.mjs +154 -17
  40. package/dist/cli/commands/validate-evidence.mjs.map +1 -1
  41. package/dist/cli/commands/vitest-doctor.d.mts +2 -0
  42. package/dist/cli/commands/vitest-doctor.d.mts.map +1 -0
  43. package/dist/cli/commands/vitest-doctor.mjs +168 -0
  44. package/dist/cli/commands/vitest-doctor.mjs.map +1 -0
  45. package/dist/cli/config/gitignore-drift.d.mts +23 -1
  46. package/dist/cli/config/gitignore-drift.d.mts.map +1 -1
  47. package/dist/cli/config/gitignore-drift.mjs +81 -3
  48. package/dist/cli/config/gitignore-drift.mjs.map +1 -1
  49. package/dist/cli/config/load.d.mts +56 -2
  50. package/dist/cli/config/load.d.mts.map +1 -1
  51. package/dist/cli/config/load.mjs +192 -2
  52. package/dist/cli/config/load.mjs.map +1 -1
  53. package/dist/cli/consistency/parse-review.mjs +7 -0
  54. package/dist/cli/consistency/parse-review.mjs.map +1 -1
  55. package/dist/cli/dispatch.d.mts.map +1 -1
  56. package/dist/cli/dispatch.mjs +24 -2
  57. package/dist/cli/dispatch.mjs.map +1 -1
  58. package/dist/cli/gate/gates/gitignore-in-sync.d.mts +1 -1
  59. package/dist/cli/gate/gates/gitignore-in-sync.d.mts.map +1 -1
  60. package/dist/cli/gate/gates/gitignore-in-sync.mjs +5 -2
  61. package/dist/cli/gate/gates/gitignore-in-sync.mjs.map +1 -1
  62. package/dist/cli/gate/gates/review-clean.d.mts +5 -1
  63. package/dist/cli/gate/gates/review-clean.d.mts.map +1 -1
  64. package/dist/cli/gate/gates/review-clean.mjs +23 -18
  65. package/dist/cli/gate/gates/review-clean.mjs.map +1 -1
  66. package/dist/cli/gate/gates/verify-approved.d.mts +49 -1
  67. package/dist/cli/gate/gates/verify-approved.d.mts.map +1 -1
  68. package/dist/cli/gate/gates/verify-approved.mjs +93 -14
  69. package/dist/cli/gate/gates/verify-approved.mjs.map +1 -1
  70. package/dist/cli/help.d.mts.map +1 -1
  71. package/dist/cli/help.mjs +8 -2
  72. package/dist/cli/help.mjs.map +1 -1
  73. package/dist/cli/host/detect.d.mts +122 -9
  74. package/dist/cli/host/detect.d.mts.map +1 -1
  75. package/dist/cli/host/detect.mjs +132 -0
  76. package/dist/cli/host/detect.mjs.map +1 -1
  77. package/dist/cli/install/legacy-paths.d.mts +38 -0
  78. package/dist/cli/install/legacy-paths.d.mts.map +1 -0
  79. package/dist/cli/install/legacy-paths.mjs +69 -0
  80. package/dist/cli/install/legacy-paths.mjs.map +1 -0
  81. package/dist/cli/install/runtime-detect.d.mts +13 -0
  82. package/dist/cli/install/runtime-detect.d.mts.map +1 -1
  83. package/dist/cli/install/runtime-detect.mjs +9 -0
  84. package/dist/cli/install/runtime-detect.mjs.map +1 -1
  85. package/dist/cli/install/runtime-migrate.d.mts +84 -0
  86. package/dist/cli/install/runtime-migrate.d.mts.map +1 -0
  87. package/dist/cli/install/runtime-migrate.mjs +244 -0
  88. package/dist/cli/install/runtime-migrate.mjs.map +1 -0
  89. package/dist/cli/route/drift-detect.d.mts +20 -0
  90. package/dist/cli/route/drift-detect.d.mts.map +1 -0
  91. package/dist/cli/route/drift-detect.mjs +107 -0
  92. package/dist/cli/route/drift-detect.mjs.map +1 -0
  93. package/dist/cli/task/index-md.d.mts.map +1 -1
  94. package/dist/cli/task/index-md.mjs +14 -2
  95. package/dist/cli/task/index-md.mjs.map +1 -1
  96. package/dist/cli/util/aperant-section.d.mts +34 -0
  97. package/dist/cli/util/aperant-section.d.mts.map +1 -0
  98. package/dist/cli/util/aperant-section.mjs +127 -0
  99. package/dist/cli/util/aperant-section.mjs.map +1 -0
  100. package/dist/cli/util/copy.d.mts +28 -1
  101. package/dist/cli/util/copy.d.mts.map +1 -1
  102. package/dist/cli/util/copy.mjs +43 -55
  103. package/dist/cli/util/copy.mjs.map +1 -1
  104. package/dist/cli/util/semver.d.mts +17 -0
  105. package/dist/cli/util/semver.d.mts.map +1 -0
  106. package/dist/cli/util/semver.mjs +29 -0
  107. package/dist/cli/util/semver.mjs.map +1 -0
  108. package/dist/cli/util/skill-installs.d.mts +65 -9
  109. package/dist/cli/util/skill-installs.d.mts.map +1 -1
  110. package/dist/cli/util/skill-installs.mjs +130 -21
  111. package/dist/cli/util/skill-installs.mjs.map +1 -1
  112. package/dist/cli/util/version-preflight.d.mts +44 -0
  113. package/dist/cli/util/version-preflight.d.mts.map +1 -0
  114. package/dist/cli/util/version-preflight.mjs +66 -0
  115. package/dist/cli/util/version-preflight.mjs.map +1 -0
  116. package/dist/plugin/.claude-plugin/plugin.json +11 -2
  117. package/dist/plugin/agents/apt-improver.md +99 -0
  118. package/dist/plugin/agents/apt-planner.md +127 -10
  119. package/dist/plugin/agents/apt-pr-review-fixer.md +13 -9
  120. package/dist/plugin/skills/apt/SKILL.md +1 -0
  121. package/dist/plugin/skills/apt-close-task/SKILL.md +63 -1
  122. package/dist/plugin/skills/apt-debug/SKILL.md +39 -6
  123. package/dist/plugin/skills/apt-debug/appendices/diagnose-discipline.md +119 -0
  124. package/dist/plugin/skills/apt-diagram/SKILL.md +378 -0
  125. package/dist/plugin/skills/apt-diagram/appendices/design-discipline.md +97 -0
  126. package/dist/plugin/skills/apt-discuss/SKILL.md +72 -5
  127. package/dist/plugin/skills/apt-discuss/appendices/grill-discipline.md +104 -0
  128. package/dist/plugin/skills/apt-discuss/appendices/zoom-out-helper.md +79 -0
  129. package/dist/plugin/skills/apt-execute/SKILL.md +57 -5
  130. package/dist/plugin/skills/apt-execute/appendices/tdd-mode.md +107 -0
  131. package/dist/plugin/skills/apt-improve/DEEPENING.md +84 -0
  132. package/dist/plugin/skills/apt-improve/INTERFACE-DESIGN.md +97 -0
  133. package/dist/plugin/skills/apt-improve/LANGUAGE.md +104 -0
  134. package/dist/plugin/skills/apt-improve/SKILL.md +141 -0
  135. package/dist/plugin/skills/apt-plan/SKILL.md +171 -4
  136. package/dist/plugin/skills/apt-plan/adapters/conductor.md +98 -0
  137. package/dist/plugin/skills/apt-pr-review/SKILL.md +57 -18
  138. package/dist/plugin/skills/apt-prototype/LOGIC.md +109 -0
  139. package/dist/plugin/skills/apt-prototype/SKILL.md +143 -0
  140. package/dist/plugin/skills/apt-prototype/UI.md +90 -0
  141. package/dist/plugin/skills/apt-quick/SKILL.md +49 -8
  142. package/dist/plugin/skills/apt-release-notes/SKILL.md +193 -0
  143. package/dist/plugin/skills/apt-release-notes/appendices/persona-voice.md +59 -0
  144. package/dist/plugin/skills/apt-review/SKILL.md +2 -0
  145. package/dist/plugin/skills/apt-run/SKILL.md +32 -4
  146. package/dist/plugin/skills/apt-setup/SKILL.md +308 -6
  147. package/dist/plugin/skills/apt-ship/SKILL.md +122 -1
  148. package/dist/plugin/skills/apt-spar/SKILL.md +315 -0
  149. package/dist/plugin/skills/apt-triage/AGENT-BRIEF.md +84 -0
  150. package/dist/plugin/skills/apt-triage/OUT-OF-SCOPE.md +75 -0
  151. package/dist/plugin/skills/apt-triage/SKILL.md +169 -0
  152. package/dist/plugin/skills/apt-update/SKILL.md +77 -10
  153. package/dist/plugin/skills/apt-verify/SKILL.md +3 -0
  154. package/dist/plugin/skills/apt-verify-proof/SKILL.md +10 -5
  155. package/dist/plugin/skills/apt-watch-ci/SKILL.md +166 -0
  156. package/dist/plugin/skills/apt-zoom-out/SKILL.md +130 -0
  157. package/package.json +133 -133
  158. package/prompts/conductor-framework-context.md +63 -0
  159. package/prompts/conductor-system.md +11 -0
  160. package/skills/apt-close-task/SKILL.md +6 -0
  161. package/skills/apt-discuss/SKILL.md +47 -5
  162. package/skills/apt-execute/SKILL.md +9 -0
  163. package/skills/apt-plan/SKILL.md +12 -0
  164. package/skills/apt-pr-review/SKILL.md +11 -2
  165. package/skills/apt-quick/SKILL.md +19 -8
  166. package/skills/apt-researcher.md +1 -0
  167. package/skills/apt-setup/SKILL.md +33 -2
  168. package/skills/apt-ship/SKILL.md +16 -4
  169. package/skills/apt-spar/SKILL.md +36 -11
  170. package/skills/apt-update/SKILL.md +26 -1
  171. package/skills/apt-verify-proof/SKILL.md +7 -5
  172. package/skills/apt-watch-ci/SKILL.md +4 -1
  173. package/src/cli/ci-watch/stop-matrix.mjs +17 -0
  174. package/src/cli/commands/ci-watch.mjs +152 -3
  175. package/src/cli/commands/features-audit.mjs +164 -5
  176. package/src/cli/commands/health-check.mjs +116 -3
  177. package/src/cli/commands/init.mjs +154 -6
  178. package/src/cli/commands/modes.mjs +11 -0
  179. package/src/cli/commands/pr-review-audit-fixer.mjs +18 -5
  180. package/src/cli/commands/route.mjs +38 -2
  181. package/src/cli/commands/task.mjs +132 -5
  182. package/src/cli/commands/validate-evidence.mjs +158 -17
  183. package/src/cli/commands/vitest-doctor.mjs +173 -0
  184. package/src/cli/config/gitignore-drift.mjs +74 -3
  185. package/src/cli/config/load.mjs +188 -2
  186. package/src/cli/consistency/parse-review.mjs +6 -0
  187. package/src/cli/dispatch.mjs +23 -2
  188. package/src/cli/gate/gates/gitignore-in-sync.mjs +5 -2
  189. package/src/cli/gate/gates/review-clean.mjs +24 -19
  190. package/src/cli/gate/gates/verify-approved.mjs +97 -14
  191. package/src/cli/help.mjs +8 -2
  192. package/src/cli/host/detect.mjs +135 -0
  193. package/src/cli/install/legacy-paths.mjs +69 -0
  194. package/src/cli/install/runtime-detect.mjs +9 -0
  195. package/src/cli/install/runtime-migrate.mjs +252 -0
  196. package/src/cli/route/drift-detect.mjs +107 -0
  197. package/src/cli/task/index-md.mjs +15 -2
  198. package/src/cli/util/aperant-section.mjs +136 -0
  199. package/src/cli/util/copy.mjs +43 -56
  200. package/src/cli/util/semver.mjs +28 -0
  201. package/src/cli/util/skill-installs.mjs +134 -21
  202. package/src/cli/util/version-preflight.mjs +65 -0
  203. package/templates/aperant-claude-md-appendix.md +37 -0
  204. package/templates/config.json +2 -7
  205. package/workflows/verify-proof.md +8 -3
@@ -0,0 +1,127 @@
1
+ /**
2
+ * util/aperant-section.mjs — generator for the CLAUDE.md / AGENTS.md
3
+ * "## Aperant Framework" section (FRAMEWORK-BUG-023).
4
+ *
5
+ * Replaces the previously-hardcoded `APT_SECTION` constant in copy.mjs.
6
+ * The skill TABLE is rebuilt from `discoverSkills` (filesystem source of
7
+ * truth — same helper the router uses at runtime) so install and runtime
8
+ * can never disagree about the catalog.
9
+ *
10
+ * Curated-doc risk: the appendix template
11
+ * (`templates/aperant-claude-md-appendix.md`) still contains hand-written
12
+ * sub-sections — "Fast Path Guarantee", "Discoverability —
13
+ * `apt-tools modes list`", "Feature Registry", "Config". These are small
14
+ * (~30 lines), rarely change, and rot would be visible (sections about
15
+ * non-existent features). Acceptable tradeoff per plan §Approach (c).
16
+ */
17
+ import { readFileSync } from 'node:fs';
18
+ import { join } from 'node:path';
19
+ import { getTrustedSkillSources } from '../config/load.mjs';
20
+ import { discoverSkills } from '../route/skill-discover.mjs';
21
+ const SECTION_START = '<!-- APT:framework-start -->';
22
+ const SECTION_END = '<!-- APT:framework-end -->';
23
+ const SECTION_HEADING = '## Aperant Framework';
24
+ const APPENDIX_REL_PATH = ['templates', 'aperant-claude-md-appendix.md'];
25
+ /**
26
+ * Escape a SKILL `description` for safe inclusion as a markdown table cell.
27
+ * The only character we have to defend against is the pipe (`|`) — it
28
+ * terminates the table column. `apt:triage` ships with a literal `|` in
29
+ * its description, so this is a live case, not a theoretical one.
30
+ *
31
+ * @param {string} text
32
+ * @returns {string}
33
+ */
34
+ function escapePipes(text) {
35
+ return text.replace(/\|/g, '\\|');
36
+ }
37
+ /**
38
+ * Deterministic sort: the bare `apt` router skill first, then ASCII-sorted
39
+ * by `name`. The router goes first so the generated table mirrors the user's
40
+ * mental model — the entry point, then everything reachable from it.
41
+ *
42
+ * @param {{ name: string }} a
43
+ * @param {{ name: string }} b
44
+ * @returns {number}
45
+ */
46
+ function sortSkills(a, b) {
47
+ if (a.name === 'apt' && b.name !== 'apt')
48
+ return -1;
49
+ if (b.name === 'apt' && a.name !== 'apt')
50
+ return 1;
51
+ return a.name.localeCompare(b.name);
52
+ }
53
+ /**
54
+ * Build the full "## Aperant Framework" section, wrapped in the
55
+ * `<!-- APT:framework-* -->` markers.
56
+ *
57
+ * @param {Object} opts
58
+ * @param {string} opts.canonicalRoot Absolute path to the framework root
59
+ * (the directory that contains
60
+ * `templates/aperant-claude-md-appendix.md`).
61
+ * @param {string} opts.targetDir Project root (passed to discoverSkills).
62
+ * @param {string} [opts.cli] Host CLI label (forwarded to
63
+ * getTrustedSkillSources so the install
64
+ * root for the runtime is included).
65
+ * @returns {string} The full section, including markers
66
+ * and trailing newline before the
67
+ * end-marker. No leading newline.
68
+ */
69
+ export function buildAptSection({ canonicalRoot, targetDir, cli }) {
70
+ const trustedSkillSources = getTrustedSkillSources(targetDir, cli);
71
+ const discovered = discoverSkills({ targetDir, trustedSkillSources });
72
+ const skills = discovered.skills
73
+ .filter((s) => s.user_invocable === true && s.internal === false)
74
+ .slice()
75
+ .sort(sortSkills);
76
+ const rows = skills.map((s) => `| \`/${s.name}\` | ${escapePipes(s.description)} |`);
77
+ const tableBlock = [
78
+ '### Available Skills',
79
+ '| Skill | Description |',
80
+ '|-------|-------------|',
81
+ ...rows,
82
+ ].join('\n');
83
+ const appendixPath = join(canonicalRoot, ...APPENDIX_REL_PATH);
84
+ let appendix;
85
+ try {
86
+ appendix = readFileSync(appendixPath, 'utf-8').replace(/\n+$/, '');
87
+ }
88
+ catch (cause) {
89
+ const err = new Error(`aperant-section: template not found at ${appendixPath} — re-install the framework package`, { cause });
90
+ err.code = 'aperant-section.template-missing';
91
+ throw err;
92
+ }
93
+ const body = [SECTION_HEADING, '', appendix, '', tableBlock].join('\n');
94
+ return `${SECTION_START}\n${body}\n${SECTION_END}`;
95
+ }
96
+ /**
97
+ * Extract the `/apt:<slug>` (or bare `/apt`) row IDs from the
98
+ * `<!-- APT:framework-* -->` section of an existing instruction file.
99
+ * Returns `[]` when the file has no APT section.
100
+ *
101
+ * Used by `injectInstructionFile` to detect foreign rows that would be
102
+ * silently clobbered by a regeneration — see `--force-overwrite-catalog`.
103
+ *
104
+ * @param {string} text Full instruction-file contents.
105
+ * @returns {string[]} Row IDs in file order, e.g. `['/apt', '/apt:quick', …]`.
106
+ */
107
+ export function extractAptSectionRowIds(text) {
108
+ const match = text.match(/<!-- APT:framework-start -->([\s\S]*?)<!-- APT:framework-end -->/);
109
+ if (!match)
110
+ return [];
111
+ // Strip HTML comments (including multiline) before scanning for row IDs so
112
+ // that commented-out example rows don't trigger false-positive foreign-row exits.
113
+ const section = match[1].replace(/<!--[\s\S]*?-->/g, '');
114
+ const ids = [];
115
+ // Match table rows whose first cell is `/apt` or `/apt:<slug>` wrapped in
116
+ // backticks. The leading `|` plus optional whitespace anchors us to a
117
+ // markdown table cell; the trailing backtick + ` |` anchors the column
118
+ // boundary so we don't match prose mentions like "the `/apt` router".
119
+ const rowRe = /^\|\s*`(\/apt(?::[a-z0-9-]+)?)`\s*\|/gm;
120
+ let m = rowRe.exec(section);
121
+ while (m !== null) {
122
+ ids.push(m[1]);
123
+ m = rowRe.exec(section);
124
+ }
125
+ return ids;
126
+ }
127
+ //# sourceMappingURL=aperant-section.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aperant-section.mjs","sourceRoot":"","sources":["../../../src/cli/util/aperant-section.mjs"],"names":[],"mappings":"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"}
@@ -1,3 +1,30 @@
1
1
  export function copyDirRecursive(src: any, dest: any): void;
2
- export function injectInstructionFile(filePath: any): "updated" | "created";
2
+ /**
3
+ * Write the (already-generated) APT section into `filePath`. Creates the
4
+ * file if missing; replaces the section in-place if the markers are
5
+ * present.
6
+ *
7
+ * FRAMEWORK-BUG-023 — before overwriting an existing section, extract the
8
+ * row IDs in the on-disk file and compare to the row IDs in the freshly
9
+ * generated section. If the file contains rows that are NOT in the
10
+ * generated set ("foreign rows"), the regen would silently clobber them.
11
+ * In that case we abort and let the caller surface a structured error.
12
+ * The `--force-overwrite-catalog` escape hatch flips `force: true`.
13
+ *
14
+ * @param {string} filePath
15
+ * @param {Object} opts
16
+ * @param {string} opts.aptSection Output of buildAptSection() — wrapped
17
+ * in `<!-- APT:framework-* -->` markers.
18
+ * @param {boolean} [opts.force=false]
19
+ * Bypass the foreign-row check.
20
+ * @returns {{ status: 'created' | 'updated' | 'aborted-foreign-rows', written: boolean, foreignRowIds?: string[] }}
21
+ */
22
+ export function injectInstructionFile(filePath: string, { aptSection, force }: {
23
+ aptSection: string;
24
+ force?: boolean | undefined;
25
+ }): {
26
+ status: "created" | "updated" | "aborted-foreign-rows";
27
+ written: boolean;
28
+ foreignRowIds?: string[];
29
+ };
3
30
  //# sourceMappingURL=copy.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"copy.d.mts","sourceRoot":"","sources":["../../../src/cli/util/copy.mjs"],"names":[],"mappings":"AAiBA,4DAWC;AAiDD,4EAiBC"}
1
+ {"version":3,"file":"copy.d.mts","sourceRoot":"","sources":["../../../src/cli/util/copy.mjs"],"names":[],"mappings":"AAwBA,4DAWC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,gDARW,MAAM,yBAEd;IAAqB,UAAU,EAAvB,MAAM;IAES,KAAK;CAE5B,GAAU;IAAE,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,sBAAsB,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CA0BlH"}
@@ -1,11 +1,18 @@
1
1
  /**
2
2
  * copy.mjs — filesystem copy + instruction-file injection helpers (C29).
3
3
  *
4
- * `copyDirRecursive` is a pure leaf; `injectInstructionFile` owns the
5
- * CLAUDE.md / AGENTS.md "<!-- APT:framework-* -->" section template.
4
+ * `copyDirRecursive` is a pure leaf. `injectInstructionFile` owns the
5
+ * CLAUDE.md / AGENTS.md "<!-- APT:framework-* -->" section write path.
6
+ *
7
+ * The APT section TEXT is no longer assembled here — it is generated by
8
+ * `util/aperant-section.mjs#buildAptSection` and passed in (FRAMEWORK-BUG-023).
9
+ * Callers compute the section once (from the filesystem skill catalog) and
10
+ * hand it to this helper. The defensive foreign-row check stays in this
11
+ * file so the read/compare/write happens atomically against `filePath`.
6
12
  */
7
13
  import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, } from 'node:fs';
8
14
  import { join } from 'node:path';
15
+ import { extractAptSectionRowIds } from './aperant-section.mjs';
9
16
  export function copyDirRecursive(src, dest) {
10
17
  mkdirSync(dest, { recursive: true });
11
18
  for (const entry of readdirSync(src, { withFileTypes: true })) {
@@ -19,63 +26,44 @@ export function copyDirRecursive(src, dest) {
19
26
  }
20
27
  }
21
28
  }
22
- const APT_SECTION = `<!-- APT:framework-start -->
23
- ## Aperant Framework
24
-
25
- This project uses the [Aperant Framework](https://github.com/Mikalsen-AI/aperant-cloud-desktop/tree/main/packages/framework) for AI-assisted development.
26
-
27
- ### Available Skills
28
- | Skill | Description |
29
- |-------|-------------|
30
- | \`/apt\` | Smart router — auto-detects context, routes to the right skill |
31
- | \`/apt:quick\` | Fast task execution skip planning, go straight to implementation |
32
- | \`/apt:discuss\` | Interactive decision-making assumptions, gray areas, power mode |
33
- | \`/apt:debug\` | Systematic debugging with persistent state across context resets |
34
- | \`/apt:scan\` | Scan codebase to build/refresh the feature registry (recommended after setup) |
35
- | \`/apt:plan\` | Analyze a task, investigate codebase, produce spec + implementation plan |
36
- | \`/apt:execute\` | Implement subtasks from a plan with atomic commits |
37
- | \`/apt:verify\` | Score implementation on 4 dimensions (correctness, completeness, quality, coverage) |
38
- | \`/apt:verify-proof\` | Visual proof verification — screenshots, video, HTML report with feature checklists |
39
- | \`/apt:review\` | 6-pass inline code review (logic / security / quality / architecture / performance / docs-coherence) with auto-fix pipeline |
40
- | \`/apt:pr-review\` | Multi-agent PR review 6 parallel specialists, validator, fixer, self-reviewer (owned / external authorship) |
41
- | \`/apt:run\` | Full pipeline: plan → execute → verify → review |
42
- | \`/apt:roundtable\` | 10-agent structured debate for complex decisions |
43
- | \`/apt:stress-test\` | Scenario simulation to catch gaps in plans |
44
- | \`/apt:ship\` | Create PR with QA scores and decision traceability |
45
- | \`/apt:pause\` | Human-initiated session handoff — save state when YOU need to stop (not for agent-initiated bailouts) |
46
- | \`/apt:resume\` | Restore context from a paused session and route to next action |
47
- | \`/apt:docs\` | Generate/update documentation verified against the actual codebase |
48
- | \`/apt:setup\` | Change framework preferences (verification style, multi-model, branching) |
49
-
50
- ### Feature Registry
51
- The feature registry (\`.aperant/features/\`) is a living inventory of every interactive
52
- feature in the app. It's the source of truth for what to verify and review.
53
-
54
- - **Created by:** \`/apt:scan\` (initial scan or full refresh)
55
- - **Updated by:** \`/apt:execute\` post-step (\`apt-tools features-audit --apply-stubs\` auto-registers new UI/core/hook/IPC files per the docs-coherence policy)
56
- - **Consumed by:** \`/apt:verify-proof\` (generates test cases from registry)
57
- - **Checked by:** \`/apt:review\` Pass 6 (flags unregistered new components, applies stubs for auto-update policies)
58
-
59
- Run \`/apt:scan\` after setting up Aperant on a brownfield project, or after major changes.
60
-
61
- ### Config
62
- - Settings: \`.aperant/config.json\` (run \`/apt:setup\` to change)
63
- - Features: \`.aperant/features/\` (feature registry -- run \`/apt:scan\` to create)
64
- - State: \`.aperant/state.json\` (execution state)
65
- - Evidence: \`.aperant/proof/\` (proof reports and screenshots)
66
- - Prompts: \`.aperant/prompts/\` (agent prompt templates)
67
- <!-- APT:framework-end -->`;
68
- export function injectInstructionFile(filePath) {
29
+ /**
30
+ * Write the (already-generated) APT section into `filePath`. Creates the
31
+ * file if missing; replaces the section in-place if the markers are
32
+ * present.
33
+ *
34
+ * FRAMEWORK-BUG-023 — before overwriting an existing section, extract the
35
+ * row IDs in the on-disk file and compare to the row IDs in the freshly
36
+ * generated section. If the file contains rows that are NOT in the
37
+ * generated set ("foreign rows"), the regen would silently clobber them.
38
+ * In that case we abort and let the caller surface a structured error.
39
+ * The `--force-overwrite-catalog` escape hatch flips `force: true`.
40
+ *
41
+ * @param {string} filePath
42
+ * @param {Object} opts
43
+ * @param {string} opts.aptSection Output of buildAptSection() wrapped
44
+ * in `<!-- APT:framework-* -->` markers.
45
+ * @param {boolean} [opts.force=false]
46
+ * Bypass the foreign-row check.
47
+ * @returns {{ status: 'created' | 'updated' | 'aborted-foreign-rows', written: boolean, foreignRowIds?: string[] }}
48
+ */
49
+ export function injectInstructionFile(filePath, { aptSection, force = false }) {
69
50
  const existing = existsSync(filePath) ? readFileSync(filePath, 'utf-8') : '';
70
51
  if (existing.includes('<!-- APT:framework-start -->')) {
71
- // Update existing section in-place
72
- const updated = existing.replace(/<!-- APT:framework-start -->[\s\S]*?<!-- APT:framework-end -->/, APT_SECTION);
52
+ if (!force) {
53
+ const existingIds = new Set(extractAptSectionRowIds(existing));
54
+ const generatedIds = new Set(extractAptSectionRowIds(aptSection));
55
+ const foreignRowIds = [...existingIds].filter((id) => !generatedIds.has(id));
56
+ if (foreignRowIds.length > 0) {
57
+ return { status: 'aborted-foreign-rows', written: false, foreignRowIds };
58
+ }
59
+ }
60
+ const updated = existing.replace(/<!-- APT:framework-start -->[\s\S]*?<!-- APT:framework-end -->/, aptSection);
73
61
  writeFileSync(filePath, updated, 'utf-8');
74
- return 'updated';
62
+ return { status: 'updated', written: true };
75
63
  }
76
- // Append
64
+ // Append (greenfield create — no foreign-row concern).
77
65
  const separator = existing.length > 0 ? '\n\n' : '';
78
- writeFileSync(filePath, existing + separator + APT_SECTION + '\n', 'utf-8');
79
- return 'created';
66
+ writeFileSync(filePath, existing + separator + aptSection + '\n', 'utf-8');
67
+ return { status: 'created', written: true };
80
68
  }
81
69
  //# sourceMappingURL=copy.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"copy.mjs","sourceRoot":"","sources":["../../../src/cli/util/copy.mjs"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACN,YAAY,EACZ,UAAU,EACV,SAAS,EACT,WAAW,EACX,YAAY,EACZ,aAAa,GACb,MAAM,SAAS,CAAA;AAChB,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEhC,MAAM,UAAU,gBAAgB,CAAC,GAAG,EAAE,IAAI;IACzC,SAAS,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IACpC,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QAC/D,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;QACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;QACvC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACzB,gBAAgB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;QACpC,CAAC;aAAM,CAAC;YACP,YAAY,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;QAChC,CAAC;IACF,CAAC;AACF,CAAC;AAED,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BA6CO,CAAA;AAE3B,MAAM,UAAU,qBAAqB,CAAC,QAAQ;IAC7C,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IAE5E,IAAI,QAAQ,CAAC,QAAQ,CAAC,8BAA8B,CAAC,EAAE,CAAC;QACvD,mCAAmC;QACnC,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAC/B,gEAAgE,EAChE,WAAW,CACX,CAAA;QACD,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;QACzC,OAAO,SAAS,CAAA;IACjB,CAAC;IAED,SAAS;IACT,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAA;IACnD,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,IAAI,EAAE,OAAO,CAAC,CAAA;IAC3E,OAAO,SAAS,CAAA;AACjB,CAAC"}
1
+ {"version":3,"file":"copy.mjs","sourceRoot":"","sources":["../../../src/cli/util/copy.mjs"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EACN,YAAY,EACZ,UAAU,EACV,SAAS,EACT,WAAW,EACX,YAAY,EACZ,aAAa,GACb,MAAM,SAAS,CAAA;AAChB,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AAE/D,MAAM,UAAU,gBAAgB,CAAC,GAAG,EAAE,IAAI;IACzC,SAAS,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IACpC,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QAC/D,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;QACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;QACvC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACzB,gBAAgB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;QACpC,CAAC;aAAM,CAAC;YACP,YAAY,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;QAChC,CAAC;IACF,CAAC;AACF,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAAQ,EAAE,EAAE,UAAU,EAAE,KAAK,GAAG,KAAK,EAAE;IAC5E,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IAE5E,IAAI,QAAQ,CAAC,QAAQ,CAAC,8BAA8B,CAAC,EAAE,CAAC;QACvD,IAAI,CAAC,KAAK,EAAE,CAAC;YACZ,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,uBAAuB,CAAC,QAAQ,CAAC,CAAC,CAAA;YAC9D,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,uBAAuB,CAAC,UAAU,CAAC,CAAC,CAAA;YACjE,MAAM,aAAa,GAAG,CAAC,GAAG,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAA;YAC5E,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC9B,OAAO,EAAE,MAAM,EAAE,sBAAsB,EAAE,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,CAAA;YACzE,CAAC;QACF,CAAC;QACD,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAC/B,gEAAgE,EAChE,UAAU,CACV,CAAA;QACD,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;QACzC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;IAC5C,CAAC;IAED,uDAAuD;IACvD,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAA;IACnD,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,GAAG,UAAU,GAAG,IAAI,EAAE,OAAO,CAAC,CAAA;IAC1E,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;AAC5C,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Compare two semver-shape strings. Returns -1 / 0 / 1. Ignores
3
+ * pre-release + build metadata (sufficient for "min version" checks
4
+ * where we only care about the MAJOR.MINOR.PATCH triple).
5
+ *
6
+ * @param {string} a
7
+ * @param {string} b
8
+ * @returns {-1 | 0 | 1}
9
+ */
10
+ export function compareVersions(a: string, b: string): -1 | 0 | 1;
11
+ /**
12
+ * util/semver.mjs — shared semver helpers used by route/drift-detect.mjs
13
+ * and util/version-preflight.mjs. Extracted to avoid duplication (QUA-002).
14
+ */
15
+ /** Semver-shape pattern — mirrors install/update-chips.mjs:64. */
16
+ export const SEMVER_RE: RegExp;
17
+ //# sourceMappingURL=semver.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semver.d.mts","sourceRoot":"","sources":["../../../src/cli/util/semver.mjs"],"names":[],"mappings":"AAQA;;;;;;;;GAQG;AACH,mCAJW,MAAM,KACN,MAAM,GACJ,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAYtB;AA3BD;;;GAGG;AAEH,kEAAkE;AAClE,+BAAmF"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * util/semver.mjs — shared semver helpers used by route/drift-detect.mjs
3
+ * and util/version-preflight.mjs. Extracted to avoid duplication (QUA-002).
4
+ */
5
+ /** Semver-shape pattern — mirrors install/update-chips.mjs:64. */
6
+ export const SEMVER_RE = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
7
+ /**
8
+ * Compare two semver-shape strings. Returns -1 / 0 / 1. Ignores
9
+ * pre-release + build metadata (sufficient for "min version" checks
10
+ * where we only care about the MAJOR.MINOR.PATCH triple).
11
+ *
12
+ * @param {string} a
13
+ * @param {string} b
14
+ * @returns {-1 | 0 | 1}
15
+ */
16
+ export function compareVersions(a, b) {
17
+ const pa = a.split(/[-+]/, 1)[0].split('.').map(Number);
18
+ const pb = b.split(/[-+]/, 1)[0].split('.').map(Number);
19
+ for (let i = 0; i < 3; i++) {
20
+ const av = pa[i] ?? 0;
21
+ const bv = pb[i] ?? 0;
22
+ if (av < bv)
23
+ return -1;
24
+ if (av > bv)
25
+ return 1;
26
+ }
27
+ return 0;
28
+ }
29
+ //# sourceMappingURL=semver.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semver.mjs","sourceRoot":"","sources":["../../../src/cli/util/semver.mjs"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,kEAAkE;AAClE,MAAM,CAAC,MAAM,SAAS,GAAG,0DAA0D,CAAA;AAEnF;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,CAAC,EAAE,CAAC;IACnC,MAAM,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IACvD,MAAM,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IACvD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5B,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;QACrB,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;QACrB,IAAI,EAAE,GAAG,EAAE;YAAE,OAAO,CAAC,CAAC,CAAA;QACtB,IAAI,EAAE,GAAG,EAAE;YAAE,OAAO,CAAC,CAAA;IACtB,CAAC;IACD,OAAO,CAAC,CAAA;AACT,CAAC"}
@@ -1,14 +1,70 @@
1
- export function getSkillInstallSurfaces(projectDir: any): {
2
- version_file: string;
1
+ /**
2
+ * @typedef {Object} SkillInstallSurface
3
+ * @property {string} host Backward-compat host id.
4
+ * @property {string} runtime Canonical runtime id (from RUNTIMES).
5
+ * @property {string} install_root Absolute path of the runtime install root.
6
+ * @property {string} manifest_path Absolute path of apt-file-manifest.json.
7
+ * @property {string|null} legacy_version_file Absolute path of the legacy .apt-version stamp, or null.
8
+ */
9
+ /**
10
+ * Return one install-surface descriptor per runtime in `RUNTIMES`.
11
+ *
12
+ * @param {string} projectDir
13
+ * @returns {SkillInstallSurface[]}
14
+ */
15
+ export function getSkillInstallSurfaces(projectDir: string): SkillInstallSurface[];
16
+ /**
17
+ * @typedef {Object} SkillInstallState
18
+ * @property {string} host
19
+ * @property {string} runtime
20
+ * @property {string} install_root
21
+ * @property {string} manifest_path
22
+ * @property {string|null} legacy_version_file
23
+ * @property {boolean} installed
24
+ * @property {string|null} installed_version
25
+ * @property {boolean} stale
26
+ * @property {'manifest' | 'manifest-malformed' | 'legacy_version_file' | null} source
27
+ */
28
+ /**
29
+ * Read each surface's install state. See the file header for the
30
+ * precedence rules.
31
+ *
32
+ * @param {string} projectDir
33
+ * @param {string} frameworkVersion
34
+ * @returns {SkillInstallState[]}
35
+ */
36
+ export function readSkillInstallState(projectDir: string, frameworkVersion: string): SkillInstallState[];
37
+ export type SkillInstallSurface = {
38
+ /**
39
+ * Backward-compat host id.
40
+ */
3
41
  host: string;
4
- skills_dir: string;
5
- }[];
6
- export function readSkillInstallState(projectDir: any, frameworkVersion: any): {
42
+ /**
43
+ * Canonical runtime id (from RUNTIMES).
44
+ */
45
+ runtime: string;
46
+ /**
47
+ * Absolute path of the runtime install root.
48
+ */
49
+ install_root: string;
50
+ /**
51
+ * Absolute path of apt-file-manifest.json.
52
+ */
53
+ manifest_path: string;
54
+ /**
55
+ * Absolute path of the legacy .apt-version stamp, or null.
56
+ */
57
+ legacy_version_file: string | null;
58
+ };
59
+ export type SkillInstallState = {
60
+ host: string;
61
+ runtime: string;
62
+ install_root: string;
63
+ manifest_path: string;
64
+ legacy_version_file: string | null;
7
65
  installed: boolean;
8
66
  installed_version: string | null;
9
67
  stale: boolean;
10
- version_file: string;
11
- host: string;
12
- skills_dir: string;
13
- }[];
68
+ source: "manifest" | "manifest-malformed" | "legacy_version_file" | null;
69
+ };
14
70
  //# sourceMappingURL=skill-installs.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"skill-installs.d.mts","sourceRoot":"","sources":["../../../src/cli/util/skill-installs.mjs"],"names":[],"mappings":"AAOA;;;;IAeC;AAED;;;;;;;IAYC"}
1
+ {"version":3,"file":"skill-installs.d.mts","sourceRoot":"","sources":["../../../src/cli/util/skill-installs.mjs"],"names":[],"mappings":"AA+CA;;;;;;;GAOG;AAEH;;;;;GAKG;AACH,oDAHW,MAAM,GACJ,mBAAmB,EAAE,CAgBjC;AAED;;;;;;;;;;;GAWG;AAEH;;;;;;;GAOG;AACH,kDAJW,MAAM,oBACN,MAAM,GACJ,iBAAiB,EAAE,CAoD/B;;;;;UApGa,MAAM;;;;aACN,MAAM;;;;kBACN,MAAM;;;;mBACN,MAAM;;;;yBACN,MAAM,GAAC,IAAI;;;UA2BX,MAAM;aACN,MAAM;kBACN,MAAM;mBACN,MAAM;yBACN,MAAM,GAAC,IAAI;eACX,OAAO;uBACP,MAAM,GAAC,IAAI;WACX,OAAO;YACP,UAAU,GAAG,oBAAoB,GAAG,qBAAqB,GAAG,IAAI"}
@@ -1,34 +1,143 @@
1
1
  /**
2
- * skill-installs.mjs — shared skill install surfaces per host.
2
+ * skill-installs.mjs — shared skill install surfaces per runtime (BUG-022).
3
+ *
4
+ * Source of truth for "what does the framework have installed on this
5
+ * project, per runtime, and is it current?" — the post-022 implementation
6
+ * is manifest-aware:
7
+ *
8
+ * 1. Per surface, prefer `apt-file-manifest.json` (the v0.6.7+ runtime
9
+ * manifest). When present and valid, `framework_version` is the
10
+ * authoritative answer.
11
+ * 2. Manifest present but malformed → reported as `installed: true`,
12
+ * `stale: true`, `source: 'manifest-malformed'`. Don't silently
13
+ * report healthy.
14
+ * 3. Else fall back to `legacy_version_file` (the pre-manifest
15
+ * `.apt-version` stamp; only defined for codex via
16
+ * `LEGACY_INSTALL_PATHS` today). `source: 'legacy_version_file'`.
17
+ * 4. Else → not installed for this surface.
18
+ *
19
+ * The surface array is derived from `RUNTIMES` (single source of truth in
20
+ * `runtime-detect.mjs`) so adding a new runtime there auto-extends every
21
+ * downstream diagnostic. The legacy fallback path is only populated for
22
+ * runtimes that declare a legacy topology in `LEGACY_INSTALL_PATHS`.
23
+ *
24
+ * Backward-compat: `host` field stays `'claude-code'` for the claude
25
+ * runtime (the statusline chip + several other consumers key on this
26
+ * exact string). The new `runtime` field exposes the descriptor id
27
+ * (`'claude'`, `'codex'`, …) for callers that want the canonical name.
3
28
  */
4
- import { existsSync, readFileSync } from 'node:fs';
29
+ import { existsSync, readFileSync, statSync } from 'node:fs';
5
30
  import { join, resolve } from 'node:path';
31
+ import { LEGACY_INSTALL_PATHS } from '../install/legacy-paths.mjs';
32
+ import { MANIFEST_FILENAME, readManifest } from '../install/manifest.mjs';
33
+ import { RUNTIMES } from '../install/runtime-detect.mjs';
34
+ /**
35
+ * Map a runtime descriptor id to the `host` value historical consumers
36
+ * expect. The Claude runtime is the only one with a non-identity mapping
37
+ * (`'claude' → 'claude-code'`) — every other id is its own host string.
38
+ *
39
+ * @param {string} runtimeId
40
+ * @returns {string}
41
+ */
42
+ function hostFor(runtimeId) {
43
+ return runtimeId === 'claude' ? 'claude-code' : runtimeId;
44
+ }
45
+ /**
46
+ * @typedef {Object} SkillInstallSurface
47
+ * @property {string} host Backward-compat host id.
48
+ * @property {string} runtime Canonical runtime id (from RUNTIMES).
49
+ * @property {string} install_root Absolute path of the runtime install root.
50
+ * @property {string} manifest_path Absolute path of apt-file-manifest.json.
51
+ * @property {string|null} legacy_version_file Absolute path of the legacy .apt-version stamp, or null.
52
+ */
53
+ /**
54
+ * Return one install-surface descriptor per runtime in `RUNTIMES`.
55
+ *
56
+ * @param {string} projectDir
57
+ * @returns {SkillInstallSurface[]}
58
+ */
6
59
  export function getSkillInstallSurfaces(projectDir) {
7
60
  const root = resolve(projectDir);
8
- return [
9
- {
10
- host: 'claude-code',
11
- skills_dir: join(root, '.claude', 'skills'),
12
- },
13
- {
14
- host: 'codex',
15
- skills_dir: join(root, '.agents', 'skills'),
16
- },
17
- ].map((surface) => ({
18
- ...surface,
19
- version_file: join(surface.skills_dir, '.apt-version'),
20
- }));
61
+ return RUNTIMES.map((descriptor) => {
62
+ const install_root = join(root, descriptor.installRoot);
63
+ const legacyTopology = LEGACY_INSTALL_PATHS[descriptor.id];
64
+ const legacy_version_file = legacyTopology ? join(root, legacyTopology.version_file) : null;
65
+ return {
66
+ host: hostFor(descriptor.id),
67
+ runtime: descriptor.id,
68
+ install_root,
69
+ manifest_path: join(install_root, MANIFEST_FILENAME),
70
+ legacy_version_file,
71
+ };
72
+ });
21
73
  }
74
+ /**
75
+ * @typedef {Object} SkillInstallState
76
+ * @property {string} host
77
+ * @property {string} runtime
78
+ * @property {string} install_root
79
+ * @property {string} manifest_path
80
+ * @property {string|null} legacy_version_file
81
+ * @property {boolean} installed
82
+ * @property {string|null} installed_version
83
+ * @property {boolean} stale
84
+ * @property {'manifest' | 'manifest-malformed' | 'legacy_version_file' | null} source
85
+ */
86
+ /**
87
+ * Read each surface's install state. See the file header for the
88
+ * precedence rules.
89
+ *
90
+ * @param {string} projectDir
91
+ * @param {string} frameworkVersion
92
+ * @returns {SkillInstallState[]}
93
+ */
22
94
  export function readSkillInstallState(projectDir, frameworkVersion) {
23
95
  return getSkillInstallSurfaces(projectDir).map((surface) => {
24
- const installed_version = existsSync(surface.version_file)
25
- ? readFileSync(surface.version_file, 'utf-8').trim()
26
- : null;
96
+ const manifestExists = existsSync(surface.manifest_path);
97
+ if (manifestExists) {
98
+ const manifest = readManifest(surface.install_root);
99
+ if (manifest && typeof manifest.framework_version === 'string') {
100
+ return {
101
+ ...surface,
102
+ installed: true,
103
+ installed_version: manifest.framework_version,
104
+ stale: manifest.framework_version !== frameworkVersion,
105
+ source: /** @type {const} */ ('manifest'),
106
+ };
107
+ }
108
+ // Manifest file present but parser rejected it (malformed JSON,
109
+ // missing required fields, …). Don't silently report healthy.
110
+ return {
111
+ ...surface,
112
+ installed: true,
113
+ installed_version: null,
114
+ stale: true,
115
+ source: /** @type {const} */ ('manifest-malformed'),
116
+ };
117
+ }
118
+ if (surface.legacy_version_file && existsSync(surface.legacy_version_file)) {
119
+ try {
120
+ if (statSync(surface.legacy_version_file).isFile()) {
121
+ const version = readFileSync(surface.legacy_version_file, 'utf-8').trim();
122
+ return {
123
+ ...surface,
124
+ installed: true,
125
+ installed_version: version,
126
+ stale: version !== frameworkVersion,
127
+ source: /** @type {const} */ ('legacy_version_file'),
128
+ };
129
+ }
130
+ }
131
+ catch {
132
+ /* fall through to "not installed" */
133
+ }
134
+ }
27
135
  return {
28
136
  ...surface,
29
- installed: installed_version !== null,
30
- installed_version,
31
- stale: installed_version !== null && installed_version !== frameworkVersion,
137
+ installed: false,
138
+ installed_version: null,
139
+ stale: false,
140
+ source: null,
32
141
  };
33
142
  });
34
143
  }
@@ -1 +1 @@
1
- {"version":3,"file":"skill-installs.mjs","sourceRoot":"","sources":["../../../src/cli/util/skill-installs.mjs"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAClD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAEzC,MAAM,UAAU,uBAAuB,CAAC,UAAU;IACjD,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC,CAAA;IAChC,OAAO;QACN;YACC,IAAI,EAAE,aAAa;YACnB,UAAU,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,CAAC;SAC3C;QACD;YACC,IAAI,EAAE,OAAO;YACb,UAAU,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,CAAC;SAC3C;KACD,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACnB,GAAG,OAAO;QACV,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,cAAc,CAAC;KACtD,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,UAAU,EAAE,gBAAgB;IACjE,OAAO,uBAAuB,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QAC1D,MAAM,iBAAiB,GAAG,UAAU,CAAC,OAAO,CAAC,YAAY,CAAC;YACzD,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE;YACpD,CAAC,CAAC,IAAI,CAAA;QACP,OAAO;YACN,GAAG,OAAO;YACV,SAAS,EAAE,iBAAiB,KAAK,IAAI;YACrC,iBAAiB;YACjB,KAAK,EAAE,iBAAiB,KAAK,IAAI,IAAI,iBAAiB,KAAK,gBAAgB;SAC3E,CAAA;IACF,CAAC,CAAC,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"skill-installs.mjs","sourceRoot":"","sources":["../../../src/cli/util/skill-installs.mjs"],"names":[],"mappings":"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"}