@aperant/framework 0.8.0 → 0.8.4

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 (271) hide show
  1. package/CHANGELOG.md +311 -1
  2. package/README.md +12 -0
  3. package/agents/apt-planner.md +5 -4
  4. package/dist/cli/commands/adr.mjs +1 -1
  5. package/dist/cli/commands/audit-branch-current.d.mts +25 -0
  6. package/dist/cli/commands/audit-branch-current.d.mts.map +1 -0
  7. package/dist/cli/commands/audit-branch-current.mjs +252 -0
  8. package/dist/cli/commands/audit-branch-current.mjs.map +1 -0
  9. package/dist/cli/commands/audit.d.mts.map +1 -1
  10. package/dist/cli/commands/audit.mjs +13 -2
  11. package/dist/cli/commands/audit.mjs.map +1 -1
  12. package/dist/cli/commands/check-version.d.mts.map +1 -1
  13. package/dist/cli/commands/check-version.mjs +19 -1
  14. package/dist/cli/commands/check-version.mjs.map +1 -1
  15. package/dist/cli/commands/context.mjs +3 -3
  16. package/dist/cli/commands/context.mjs.map +1 -1
  17. package/dist/cli/commands/detect-runtime.d.mts +6 -0
  18. package/dist/cli/commands/detect-runtime.d.mts.map +1 -0
  19. package/dist/cli/commands/detect-runtime.mjs +136 -0
  20. package/dist/cli/commands/detect-runtime.mjs.map +1 -0
  21. package/dist/cli/commands/driver-doctor.d.mts +29 -0
  22. package/dist/cli/commands/driver-doctor.d.mts.map +1 -0
  23. package/dist/cli/commands/driver-doctor.mjs +291 -0
  24. package/dist/cli/commands/driver-doctor.mjs.map +1 -0
  25. package/dist/cli/commands/gate.mjs +14 -0
  26. package/dist/cli/commands/gate.mjs.map +1 -1
  27. package/dist/cli/commands/init.d.mts.map +1 -1
  28. package/dist/cli/commands/init.mjs +76 -33
  29. package/dist/cli/commands/init.mjs.map +1 -1
  30. package/dist/cli/commands/install-from-source.d.mts +2 -0
  31. package/dist/cli/commands/install-from-source.d.mts.map +1 -0
  32. package/dist/cli/commands/install-from-source.mjs +2 -0
  33. package/dist/cli/commands/install-from-source.mjs.map +1 -0
  34. package/dist/cli/commands/modes.mjs +1 -1
  35. package/dist/cli/commands/route.d.mts.map +1 -1
  36. package/dist/cli/commands/route.mjs +38 -1
  37. package/dist/cli/commands/route.mjs.map +1 -1
  38. package/dist/cli/commands/triage.mjs +1 -1
  39. package/dist/cli/config/load.d.mts.map +1 -1
  40. package/dist/cli/config/load.mjs +9 -3
  41. package/dist/cli/config/load.mjs.map +1 -1
  42. package/dist/cli/coordination/auto-emit-artifact-ready.mjs +1 -1
  43. package/dist/cli/coordination/event-schema.d.mts.map +1 -1
  44. package/dist/cli/coordination/event-schema.mjs +18 -0
  45. package/dist/cli/coordination/event-schema.mjs.map +1 -1
  46. package/dist/cli/design/frontmatter-schema.d.mts +4 -4
  47. package/dist/cli/dispatch.d.mts.map +1 -1
  48. package/dist/cli/dispatch.mjs +18 -1
  49. package/dist/cli/dispatch.mjs.map +1 -1
  50. package/dist/cli/gate/context.d.mts +1 -0
  51. package/dist/cli/gate/context.d.mts.map +1 -1
  52. package/dist/cli/gate/context.mjs +14 -1
  53. package/dist/cli/gate/context.mjs.map +1 -1
  54. package/dist/cli/gate/gates/consistency-check.d.mts +1 -0
  55. package/dist/cli/gate/gates/consistency-check.d.mts.map +1 -1
  56. package/dist/cli/gate/gates/consistency-check.mjs +1 -0
  57. package/dist/cli/gate/gates/consistency-check.mjs.map +1 -1
  58. package/dist/cli/gate/gates/review-clean.d.mts +1 -0
  59. package/dist/cli/gate/gates/review-clean.d.mts.map +1 -1
  60. package/dist/cli/gate/gates/review-clean.mjs +1 -0
  61. package/dist/cli/gate/gates/review-clean.mjs.map +1 -1
  62. package/dist/cli/gate/gates/verify-approved.d.mts +1 -0
  63. package/dist/cli/gate/gates/verify-approved.d.mts.map +1 -1
  64. package/dist/cli/gate/gates/verify-approved.mjs +1 -0
  65. package/dist/cli/gate/gates/verify-approved.mjs.map +1 -1
  66. package/dist/cli/gate/registry.d.mts.map +1 -1
  67. package/dist/cli/gate/registry.mjs +9 -1
  68. package/dist/cli/gate/registry.mjs.map +1 -1
  69. package/dist/cli/install/cli-to-install-root.d.mts.map +1 -1
  70. package/dist/cli/install/cli-to-install-root.mjs +0 -2
  71. package/dist/cli/install/cli-to-install-root.mjs.map +1 -1
  72. package/dist/cli/install/find-workspace-framework-version.d.mts +16 -0
  73. package/dist/cli/install/find-workspace-framework-version.d.mts.map +1 -0
  74. package/dist/cli/install/find-workspace-framework-version.mjs +85 -0
  75. package/dist/cli/install/find-workspace-framework-version.mjs.map +1 -0
  76. package/dist/cli/install/install-from-source.d.mts +10 -0
  77. package/dist/cli/install/install-from-source.d.mts.map +1 -0
  78. package/dist/cli/install/install-from-source.mjs +271 -0
  79. package/dist/cli/install/install-from-source.mjs.map +1 -0
  80. package/dist/cli/install/install-kernel.d.mts.map +1 -1
  81. package/dist/cli/install/install-kernel.mjs +24 -0
  82. package/dist/cli/install/install-kernel.mjs.map +1 -1
  83. package/dist/cli/install/legacy-paths.d.mts +33 -11
  84. package/dist/cli/install/legacy-paths.d.mts.map +1 -1
  85. package/dist/cli/install/legacy-paths.mjs +161 -39
  86. package/dist/cli/install/legacy-paths.mjs.map +1 -1
  87. package/dist/cli/install/runtime-detect.d.mts +2 -6
  88. package/dist/cli/install/runtime-detect.d.mts.map +1 -1
  89. package/dist/cli/install/runtime-detect.mjs +9 -4
  90. package/dist/cli/install/runtime-detect.mjs.map +1 -1
  91. package/dist/cli/install/runtime-migrate.d.mts +34 -14
  92. package/dist/cli/install/runtime-migrate.d.mts.map +1 -1
  93. package/dist/cli/install/runtime-migrate.mjs +63 -30
  94. package/dist/cli/install/runtime-migrate.mjs.map +1 -1
  95. package/dist/cli/install/transforms/codex.d.mts.map +1 -1
  96. package/dist/cli/install/transforms/codex.mjs +28 -17
  97. package/dist/cli/install/transforms/codex.mjs.map +1 -1
  98. package/dist/cli/install/transforms/pi.d.mts +6 -0
  99. package/dist/cli/install/transforms/pi.d.mts.map +1 -0
  100. package/dist/cli/install/transforms/pi.mjs +37 -0
  101. package/dist/cli/install/transforms/pi.mjs.map +1 -0
  102. package/dist/cli/personas/sidecar.d.mts +1 -1
  103. package/dist/cli/roadmap/lifecycle.d.mts +1 -1
  104. package/dist/cli/roadmap/rollup.d.mts +2 -2
  105. package/dist/cli/skill-author/contract.mjs +2 -2
  106. package/dist/cli/skill-author/contract.mjs.map +1 -1
  107. package/dist/cli/verify-proof/audit.d.mts +34 -0
  108. package/dist/cli/verify-proof/audit.d.mts.map +1 -0
  109. package/dist/cli/verify-proof/audit.mjs +53 -0
  110. package/dist/cli/verify-proof/audit.mjs.map +1 -0
  111. package/dist/cli/verify-proof/exec.d.mts +20 -0
  112. package/dist/cli/verify-proof/exec.d.mts.map +1 -0
  113. package/dist/cli/verify-proof/exec.mjs +74 -0
  114. package/dist/cli/verify-proof/exec.mjs.map +1 -0
  115. package/dist/cli/verify-proof/idl/index.d.mts +2 -0
  116. package/dist/cli/verify-proof/idl/index.d.mts.map +1 -0
  117. package/dist/cli/verify-proof/idl/index.mjs +14 -0
  118. package/dist/cli/verify-proof/idl/index.mjs.map +1 -0
  119. package/dist/cli/verify-proof/idl/types.d.ts +9 -0
  120. package/dist/cli/verify-proof/idl/types.d.ts.map +1 -0
  121. package/dist/cli/verify-proof/idl/types.js +9 -0
  122. package/dist/cli/verify-proof/idl/types.js.map +1 -0
  123. package/dist/cli/verify-proof/manifest-validator.d.mts +19 -0
  124. package/dist/cli/verify-proof/manifest-validator.d.mts.map +1 -0
  125. package/dist/cli/verify-proof/manifest-validator.mjs +182 -0
  126. package/dist/cli/verify-proof/manifest-validator.mjs.map +1 -0
  127. package/dist/cli/verify-proof/resolver.d.mts +43 -0
  128. package/dist/cli/verify-proof/resolver.d.mts.map +1 -0
  129. package/dist/cli/verify-proof/resolver.mjs +160 -0
  130. package/dist/cli/verify-proof/resolver.mjs.map +1 -0
  131. package/dist/cli/verify-proof/runtime-detect.d.mts +27 -0
  132. package/dist/cli/verify-proof/runtime-detect.d.mts.map +1 -0
  133. package/dist/cli/verify-proof/runtime-detect.mjs +116 -0
  134. package/dist/cli/verify-proof/runtime-detect.mjs.map +1 -0
  135. package/dist/cli/verify-proof/trust.d.mts +70 -0
  136. package/dist/cli/verify-proof/trust.d.mts.map +1 -0
  137. package/dist/cli/verify-proof/trust.mjs +174 -0
  138. package/dist/cli/verify-proof/trust.mjs.map +1 -0
  139. package/dist/plugin/.claude-plugin/plugin.json +4 -1
  140. package/dist/plugin/agents/apt-executor.md +12 -0
  141. package/dist/plugin/agents/apt-planner.md +5 -4
  142. package/dist/plugin/agents/apt-team-docs-narrator.md +11 -0
  143. package/dist/plugin/skills/apt/SKILL.md +78 -8
  144. package/dist/plugin/skills/apt-caveman/SKILL.md +64 -0
  145. package/dist/plugin/skills/apt-debug/SKILL.md +36 -4
  146. package/dist/plugin/skills/apt-debug/appendices/diagnose-discipline.md +2 -11
  147. package/dist/plugin/skills/apt-discuss/SKILL.md +4 -4
  148. package/dist/plugin/skills/apt-discuss/appendices/grill-discipline.md +2 -11
  149. package/dist/plugin/skills/apt-discuss/appendices/zoom-out-helper.md +0 -8
  150. package/dist/plugin/skills/apt-execute/SKILL.md +40 -4
  151. package/dist/plugin/skills/apt-execute/appendices/tdd-mode.md +2 -10
  152. package/dist/plugin/skills/apt-fan-out/SKILL.md +159 -0
  153. package/dist/plugin/skills/apt-handoff/SKILL.md +228 -0
  154. package/dist/plugin/skills/apt-improve/DEEPENING.md +0 -8
  155. package/dist/plugin/skills/apt-improve/INTERFACE-DESIGN.md +0 -8
  156. package/dist/plugin/skills/apt-improve/LANGUAGE.md +0 -8
  157. package/dist/plugin/skills/apt-improve/SKILL.md +4 -4
  158. package/dist/plugin/skills/apt-plan/SKILL.md +23 -5
  159. package/dist/plugin/skills/apt-plan/adapters/conductor.md +1 -1
  160. package/dist/plugin/skills/apt-pr-review/SKILL.md +8 -1
  161. package/dist/plugin/skills/apt-prototype/LOGIC.md +0 -8
  162. package/dist/plugin/skills/apt-prototype/SKILL.md +4 -4
  163. package/dist/plugin/skills/apt-prototype/UI.md +0 -8
  164. package/dist/plugin/skills/apt-quick/SKILL.md +31 -1
  165. package/dist/plugin/skills/apt-resume/SKILL.md +54 -31
  166. package/dist/plugin/skills/apt-review/SKILL.md +4 -0
  167. package/dist/plugin/skills/apt-run/SKILL.md +134 -21
  168. package/dist/plugin/skills/apt-setup/SKILL.md +97 -8
  169. package/dist/plugin/skills/apt-ship/SKILL.md +165 -9
  170. package/dist/plugin/skills/apt-spar/SKILL.md +77 -25
  171. package/dist/plugin/skills/apt-triage/AGENT-BRIEF.md +0 -8
  172. package/dist/plugin/skills/apt-triage/OUT-OF-SCOPE.md +0 -7
  173. package/dist/plugin/skills/apt-triage/SKILL.md +2 -2
  174. package/dist/plugin/skills/apt-update/SKILL.md +74 -17
  175. package/dist/plugin/skills/apt-verify/SKILL.md +11 -0
  176. package/dist/plugin/skills/apt-verify-proof/SKILL.md +48 -1
  177. package/dist/plugin/skills/apt-watch-ci/SKILL.md +2 -2
  178. package/dist/plugin/skills/apt-zoom-out/SKILL.md +3 -3
  179. package/dist/schemas/quick-task.d.ts +17 -17
  180. package/dist/schemas/quick-task.d.ts.map +1 -1
  181. package/dist/schemas/quick-task.js +2 -2
  182. package/dist/schemas/quick-task.js.map +1 -1
  183. package/dist/types/config.d.ts +33 -0
  184. package/dist/types/config.d.ts.map +1 -1
  185. package/dist/types/state.d.ts +1 -1
  186. package/dist/types/state.d.ts.map +1 -1
  187. package/dist/types/task-record.d.ts +1 -1
  188. package/dist/types/task-record.d.ts.map +1 -1
  189. package/package.json +134 -133
  190. package/prompts/planner.md +1 -1
  191. package/prompts/spec_writer.md +1 -1
  192. package/skills/apt/SKILL.md +78 -8
  193. package/skills/apt-caveman/SKILL.md +64 -0
  194. package/skills/apt-debug/SKILL.md +36 -4
  195. package/skills/apt-debug/appendices/diagnose-discipline.md +2 -11
  196. package/skills/apt-discuss/SKILL.md +4 -4
  197. package/skills/apt-discuss/appendices/grill-discipline.md +2 -11
  198. package/skills/apt-discuss/appendices/zoom-out-helper.md +0 -8
  199. package/skills/apt-execute/SKILL.md +27 -4
  200. package/skills/apt-execute/appendices/tdd-mode.md +2 -10
  201. package/skills/apt-fan-out/SKILL.md +36 -1
  202. package/skills/apt-handoff/SKILL.md +228 -0
  203. package/skills/apt-improve/DEEPENING.md +0 -8
  204. package/skills/apt-improve/INTERFACE-DESIGN.md +0 -8
  205. package/skills/apt-improve/LANGUAGE.md +0 -8
  206. package/skills/apt-improve/SKILL.md +4 -4
  207. package/skills/apt-plan/SKILL.md +23 -5
  208. package/skills/apt-plan/adapters/conductor.md +1 -1
  209. package/skills/apt-planner.md +1 -1
  210. package/skills/apt-prototype/LOGIC.md +0 -8
  211. package/skills/apt-prototype/SKILL.md +4 -4
  212. package/skills/apt-prototype/UI.md +0 -8
  213. package/skills/apt-quick/SKILL.md +31 -1
  214. package/skills/apt-resume/SKILL.md +54 -31
  215. package/skills/apt-run/SKILL.md +114 -16
  216. package/skills/apt-setup/SKILL.md +97 -8
  217. package/skills/apt-ship/SKILL.md +165 -9
  218. package/skills/apt-spar/SKILL.md +65 -16
  219. package/skills/apt-triage/AGENT-BRIEF.md +0 -8
  220. package/skills/apt-triage/OUT-OF-SCOPE.md +0 -7
  221. package/skills/apt-triage/SKILL.md +2 -2
  222. package/skills/apt-update/SKILL.md +74 -17
  223. package/skills/apt-verify-proof/SKILL.md +48 -1
  224. package/skills/apt-zoom-out/SKILL.md +3 -3
  225. package/src/cli/commands/adr.mjs +1 -1
  226. package/src/cli/commands/audit-branch-current.mjs +250 -0
  227. package/src/cli/commands/audit.mjs +13 -2
  228. package/src/cli/commands/check-version.mjs +20 -1
  229. package/src/cli/commands/context.mjs +3 -3
  230. package/src/cli/commands/detect-runtime.mjs +134 -0
  231. package/src/cli/commands/driver-doctor.mjs +294 -0
  232. package/src/cli/commands/gate.mjs +15 -0
  233. package/src/cli/commands/init.mjs +75 -33
  234. package/src/cli/commands/install-from-source.mjs +1 -0
  235. package/src/cli/commands/modes.mjs +1 -1
  236. package/src/cli/commands/route.mjs +42 -1
  237. package/src/cli/commands/triage.mjs +1 -1
  238. package/src/cli/config/load.mjs +9 -3
  239. package/src/cli/coordination/auto-emit-artifact-ready.mjs +1 -1
  240. package/src/cli/coordination/event-schema.mjs +19 -0
  241. package/src/cli/dispatch.mjs +17 -1
  242. package/src/cli/gate/context.mjs +20 -1
  243. package/src/cli/gate/gates/consistency-check.mjs +1 -0
  244. package/src/cli/gate/gates/review-clean.mjs +1 -0
  245. package/src/cli/gate/gates/verify-approved.mjs +1 -0
  246. package/src/cli/gate/registry.mjs +11 -1
  247. package/src/cli/install/cli-to-install-root.mjs +0 -2
  248. package/src/cli/install/find-workspace-framework-version.mjs +88 -0
  249. package/src/cli/install/install-from-source.mjs +276 -0
  250. package/src/cli/install/install-kernel.mjs +24 -0
  251. package/src/cli/install/legacy-paths.mjs +162 -39
  252. package/src/cli/install/runtime-detect.mjs +9 -4
  253. package/src/cli/install/runtime-migrate.mjs +63 -30
  254. package/src/cli/install/transforms/codex.mjs +26 -17
  255. package/src/cli/install/transforms/pi.mjs +37 -0
  256. package/src/cli/skill-author/contract.mjs +2 -2
  257. package/src/cli/verify-proof/.gitkeep +0 -0
  258. package/src/cli/verify-proof/audit.mjs +55 -0
  259. package/src/cli/verify-proof/exec.mjs +74 -0
  260. package/src/cli/verify-proof/idl/index.mjs +24 -0
  261. package/src/cli/verify-proof/idl/types.ts +41 -0
  262. package/src/cli/verify-proof/manifest-schema.json +211 -0
  263. package/src/cli/verify-proof/manifest-validator.mjs +184 -0
  264. package/src/cli/verify-proof/resolver.mjs +163 -0
  265. package/src/cli/verify-proof/runtime-detect.mjs +122 -0
  266. package/src/cli/verify-proof/trust.mjs +187 -0
  267. package/templates/adr-format.md +0 -8
  268. package/templates/aperant-claude-md-appendix.md +1 -1
  269. package/templates/context-format.md +1 -9
  270. package/templates/proof-verification.md +19 -5
  271. package/workflows/verify-proof.md +56 -14
package/CHANGELOG.md CHANGED
@@ -3,7 +3,317 @@
3
3
  All notable changes to `@aperant/framework` are documented here. See
4
4
  [`VERSIONING.md`](VERSIONING.md) for the per-tier contract.
5
5
 
6
- ## [Unreleased]
6
+ ## 0.8.4 — 2026-05-22 — `/apt:update` polish + `apt:handoff`, `apt:caveman` skills + `apt:resume` HANDOFF discovery
7
+
8
+ Bundled release: five `/apt:update` fix bundles (converged from a 3-round
9
+ Codex sparring session at `.aperant/spar/apt-update-fixes-20260522-083919.md`,
10
+ resolving 9 reported issues + 2 hidden defects) plus two new skills and one
11
+ extended skill.
12
+
13
+ > **VERSIONING.md note.** New skills normally trigger MINOR per the
14
+ > contract. Operator opted for PATCH here because the surface area is
15
+ > small (2 skills, 1 extension) and the apt:update polish is the load-
16
+ > bearing change. Future MINOR bumps remain reserved for transform /
17
+ > manifest / CLI flag additions.
18
+
19
+ ### Added
20
+
21
+ - **`/apt:handoff`** (new skill at `packages/framework/skills/apt-handoff/SKILL.md`).
22
+ Agent-to-agent handoff — compacts the current conversation into a
23
+ `HANDOFF.md` artifact under `.aperant/handoffs/{id}/` (or
24
+ `{task_dir}/handoffs/{id}/` when inside a routed task), indexed via a
25
+ new `.aperant/HANDOFFS_INDEX.md`. Distinct from `/apt:pause` along
26
+ four axes: trigger (agent vs human), audience (different agent vs
27
+ future-self), uncommitted-work handling (does NOT stash), and index
28
+ file. `stage: pause`, `intent: capture`, `task_context: none`.
29
+ - **`/apt:caveman`** (new skill at `packages/framework/skills/apt-caveman/SKILL.md`).
30
+ Ultra-compressed terse output mode — toggles a persistent "smart
31
+ caveman" style that cuts conversational token usage while keeping
32
+ full technical accuracy. `stage: ops`, `intent: configure`,
33
+ `allowed-tools: ""`.
34
+
35
+ ### Changed
36
+
37
+ - **`/apt:resume`** now discovers HANDOFF artifacts in addition to
38
+ CONTINUE artifacts. New step `1b` reads `.aperant/HANDOFFS_INDEX.md`;
39
+ step `1d` scans `.aperant/{tasks/*/handoffs,handoffs}/*/HANDOFF.md` as
40
+ fallback. The Present Sessions list tags each row `[pause]` or
41
+ `[handoff]` (handoffs render first — higher urgency). Step 3 splits
42
+ into `3a` (pause: read CONTINUE.md, pop stash) vs `3b` (handoff: read
43
+ Focus, verify external state, do NOT pop stash). Cleanup removes the
44
+ matching index row regardless of type.
45
+
46
+ ### Fixed
47
+
48
+ - **Bundle A — Workspace-aware version detection.** New helper
49
+ `findWorkspaceFrameworkVersion(targetDir)` walks up to a sibling
50
+ `packages/framework/package.json` (max 6 frames, mirroring
51
+ `route/drift-detect.mjs:32`). The SessionStart worker cache + the
52
+ `apt-tools check-version --json` envelope now carry an additive
53
+ `framework.workspace_version: string | null` field. When the
54
+ workspace is ahead of the running kernel, `update_available` flips
55
+ true regardless of npm cache state — the workspace is the source of
56
+ truth on self-host. `--from-source` flag preserved as explicit
57
+ override per ID-03. Resolves issues 1, 7, 9.
58
+ - **Bundle B — Idempotent kernel install.** `installKernel` short-
59
+ circuits when the kernel package.json already reports the target
60
+ version AND the kernel binary exists. Returns the same
61
+ `status: 'installed'` envelope with `reason: 'already at target
62
+ version'` + `durationMs: 0` so the three downstream consumers
63
+ (init.mjs:1441, init.mjs:1735, SKILL.md Scenario D) keep their
64
+ existing branching. No new enum value (D-02). Resolves issues 5, 6.
65
+ - **Bundle C — SKILL.md doc gaps.** Adds the `pnpm install --filter
66
+ @aperant/framework... --frozen-lockfile=false` precondition before
67
+ the `pnpm pack` recipe (AC6); adds a hard warning against
68
+ substituting `npm pack` — names the `workspace:*` literal +
69
+ `EUNSUPPORTEDPROTOCOL` keyword (AC7); adds a `Migrated:` line slot
70
+ to all three Scenario A/B/C templates, conditional on
71
+ `legacy_cleanup?.ran === true && rmdirs.some(p => p.includes('.codex'))`
72
+ (AC8). Resolves issues 2, 4, 8.
73
+ - **Bundle D — Workspace-dep chain.** New CLI subcommand
74
+ `apt-tools install-from-source <project-dir>` walks workspace
75
+ `dependencies` + `peerDependencies` for `workspace:*` entries
76
+ (matching by package `name`, not directory name —
77
+ `@aperant/driver-sdk` lives at `packages/framework-driver-sdk/`),
78
+ pnpm-packs each, then invokes a single `npm install --prefix
79
+ .aperant/deps <tarball-list>...` with the existing kernel-install
80
+ flag set (`--no-save`, `--no-package-lock`, `--no-audit`,
81
+ `--no-fund`). Handles the same-PR unpublished-pair case
82
+ (`framework@X` + `driver-sdk@Y` both at `workspace:*`, both
83
+ unpublished) without `EUNSUPPORTEDPROTOCOL`. SKILL.md §1.7
84
+ simplified to a single apt-tools invocation; the manual recipe
85
+ preserved as a debugging-aid footnote. Resolves issue 3.
86
+ - **Bundle E — Init comment inversion + Scenario A null-latest.**
87
+ Fixed `init.mjs:1253-1268` comment + stderr direction (LEGACY =
88
+ `.codex/`, CURRENT = `.agents/skills/<folder>/SKILL.md`; was
89
+ reversed; the v0.8.2 inversion is documented in
90
+ `runtime-migrate.mjs:7-12`). Scenario A now requires
91
+ `latest_version != null` to avoid false "version bump" chips when
92
+ npm-latest is unknown (cache stale, not published, network down).
93
+ New Scenario E ("no version info available") branch handles that
94
+ case explicitly — refreshes runtime files for safety, tells the
95
+ user the bump is deferred. Resolves 2 hidden defects.
96
+
97
+ ### Schema status
98
+
99
+ Envelope changes are strictly additive — the new
100
+ `framework.workspace_version` field is nullable, existing callers
101
+ ignore unknown fields. **No `schema_version` bump** (cache stays at
102
+ `1`, locked by `update-chips.mjs:97-98` + the
103
+ `check-version-cache-merge.test.ts:137-148` schema-2 rejection
104
+ regression test).
105
+
106
+ ### Decision source
107
+
108
+ Codex sparring session 2026-05-22 (3 rounds, CONVERGED).
109
+
110
+ ### Tests
111
+
112
+ - **New:** `find-workspace-framework-version.test.ts`,
113
+ `install-kernel-idempotent.test.ts`,
114
+ `apt-update-skill-doc-gaps.test.ts`, `install-from-source.test.ts`,
115
+ `init-legacy-cleanup-direction.test.ts` (5 files / 23 new cases).
116
+ - **Augmented:** `check-version-cache-merge.test.ts` (+2 cases for
117
+ the workspace_version envelope field).
118
+ - **Regression-locked:** `apt-update-from-source-skill-structure.test.ts`,
119
+ `route-source-drift-detection.test.ts`, `legacy-cleanup-codex.test.ts`,
120
+ `check-version-cache-merge.test.ts` (existing schema_version=2
121
+ rejection at `:137-148`). All still pass.
122
+
123
+ ## 0.8.3 — 2026-05-21 — FRAMEWORK-RFC-001 — `/apt:spar` pipeline integration with install-time consent
124
+
125
+ Minor patch release. Flips `/apt:spar` from manual-only (SPAR-07 v1) to
126
+ pipeline-integrated under explicit install-time consent. Adds a per-device
127
+ opt-in question to `/apt:setup` Batch 2 Pipeline; wires `/apt:run` and the
128
+ `/apt` router to honor it with track-aware gates (QUICK rejects, STANDARD
129
+ = `plan` gate, DEEP = `plan,execute` gates); updates SPAR-07 wording.
130
+ Per-invocation `--spar-gates` flag remains as the manual override path;
131
+ rounds always defer to the skill default to avoid reintroducing
132
+ FRAMEWORK-BUG-038.
133
+
134
+ ### What
135
+
136
+ - **`SparConfig` schema delta** — three per-device fields land in
137
+ `.aperant/config.local.json` per AUDIT-001: `spar.enabled: boolean`,
138
+ `spar.partner: 'codex' | 'claude' | 'gemini' | null`, `spar.hint?: boolean`.
139
+ Added to `packages/framework/src/types/config.ts` as
140
+ `AperantConfig.spar?: SparConfig`. No migration path needed — spar lands
141
+ in `config.local.json` from day one with no shared-config legacy.
142
+
143
+ - **`apt-setup` Sparring Auto-Fire (Batch 2)** — 3-phase question block
144
+ inserted after PR Review Auto-Suggest:
145
+ - Phase 1 (Yes/No): master switch.
146
+ - Phase 2 (partner picker, only if Yes): reads
147
+ `host.partner_transports.{codex,claude,gemini}` from
148
+ `apt-tools host-detect` to filter by availability, AND excludes
149
+ `host.cli` (Open Q1 same-host filter). Graceful skip with
150
+ `spar.enabled: true, spar.partner: null` when zero partners detected.
151
+ - Phase 3 (hint sub-picker, only if No): opt-in to per-stage reminder
152
+ `[apt] {stage} complete. /apt:spar available before next stage.`
153
+ All three fields route through `writeLocalConfigField` per AUDIT-001.
154
+
155
+ - **`apt-run` flag + gate handlers** —
156
+ - `--spar-gates <plan|execute|plan,execute>` flag (per-invocation
157
+ override of merged-config consent). QUICK hard-rejects with verbatim
158
+ `[apt:run] --spar-gates ignored for QUICK track (Fast Path Guarantee)`.
159
+ - §3b "Stage 1.5 — Spar Gate (plan)": resolves effective gates, composes
160
+ topic frame from spec.md §Scope (task description fallback per
161
+ Open Q4), dispatches `/apt:spar --timeout-ms 120000 [--with <partner>]`
162
+ with NO `--rounds` flag (FRAMEWORK-BUG-038 regression guard).
163
+ - §4b "Stage 2.5 — Spar Gate (execute)": same logic, gate name
164
+ `"execute"`, topic frame from `build-progress.json`.
165
+ - Pipeline-mode `--timeout-ms 120000` (2-min Bash-rung fast-fail) is
166
+ asymmetric with manual mode (5min/15min defaults). Worst-case DEEP
167
+ wall budget: 2 gates × 3 rounds × 2 min = 12 min (vs ~90 min before
168
+ this clamp under MCP-rung partner degradation).
169
+ - §5 Verify spawn context: verifier reads Layer 2 spar findings
170
+ (`.aperant/spar/<slug>-<ts>.md`) before scoring.
171
+ - Per-stage `spar.hint` nudge at each of plan/execute/verify/review
172
+ Output: anchors (4 conditional prints, gated on
173
+ `spar.hint === true && spar.enabled === false`).
174
+
175
+ - **`apt` router envelope contract (load-bearing AC11)** — `route.mjs`
176
+ emits two deterministic consent fields:
177
+ - `envelope.spar_inject_gates` (flag-override path): pre-computed gate
178
+ string `"plan"` (STANDARD) or `"plan,execute"` (DEEP) when consent on
179
+ AND user did NOT pass explicit `--spar-gates`. Absent for QUICK/DEBUG.
180
+ - `envelope.spar_consent` (host-LLM path): block
181
+ `{ enabled, partner, user_passed_explicit_gates }` when consent on.
182
+ Host model composes `--spar-gates` from spar_consent + chosen track.
183
+ Both fields ABSENT when consent off — envelope byte-equivalent to
184
+ pre-RFC shape for users who never opted in. `apt/SKILL.md` MODE: classify
185
+ Step 4 reads ONLY the envelope (never re-reads merged config from
186
+ prompt-space) — structural test grep-locks this invariant.
187
+
188
+ - **`apt-plan` + `apt-execute` `--spar` flags** — end-of-process dispatch
189
+ to `/apt:spar` against local artifacts (spec.md / build-progress.json).
190
+ Same `--timeout-ms 120000` + no-rounds discipline. Per-skill manual
191
+ surface for users who don't use `/apt:run` end-to-end.
192
+
193
+ - **`apt-spar/SKILL.md` SPAR-07 wording** — updated from v1
194
+ "manually invoked only" to v2 "honors install-time consent". §1-§9
195
+ process body is byte-equivalent to pre-edit baseline (sha256
196
+ 9c2852ce…7da7b3 preserved by `apt-spar-pipeline-manual-path-preserved.test.ts`
197
+ hash guard). Manual `/apt:spar` invocation path UNCHANGED.
198
+
199
+ ### Why
200
+
201
+ Founder usage data confirmed that cross-provider sparring surfaces
202
+ blind-spot defects that single-provider review misses. SPAR-07's v1
203
+ escape hatch was always deferred-pending-data; the data is in. Pipeline
204
+ integration removes the per-task `--spar-gates` flag drudgery while
205
+ preserving QUICK Fast Path Guarantee + manual override surfaces.
206
+
207
+ ### Compatibility
208
+
209
+ - Backward-compatible. Users who never opt in to `spar.enabled: true`
210
+ see identical behavior to 0.8.2 — envelope shape byte-equivalent,
211
+ apt-run pipeline unchanged.
212
+ - Manual `/apt:spar` invocation path BYTE-EQUIVALENT (process body
213
+ hash-locked).
214
+ - `--spar-gates` flag is opt-in per-invocation. QUICK hard-rejects.
215
+ - Migration: no path needed. Spar lands in `config.local.json` from day
216
+ one; no shared-config legacy.
217
+
218
+ ### Out of scope (follow-up RFC-001-b candidates)
219
+
220
+ - Verify-stage spar gate (deferred — verify already iterates fix-and-retry).
221
+ - Pre-ship spar gate (likely redundant with `/apt:pr-review`).
222
+ - Multi-partner concurrent spar (use `/apt:roundtable` for 10-agent).
223
+ - Re-plan-on-spar-cede behavioral feedback loop.
224
+ - `--mcp-timeout-ms` per-invocation flag on `apt-spar` (Open Q5 —
225
+ MCP-rung pipeline tightening). Today MCP rung is config-only at
226
+ `apt-spar.mcp_timeout_ms`; pipeline mode accepts the asymmetry.
227
+
228
+ ### Version
229
+
230
+ - `packages/framework/package.json`: 0.8.2 → 0.8.3
231
+
232
+ ### Commits
233
+
234
+ - `4d67ca1d` feat(framework): add SparConfig schema for RFC-001 pipeline consent
235
+ - `e301b776` feat(framework): apt-setup Sparring Auto-Fire prompt (RFC-001 install-time consent)
236
+ - `20dea366` feat(framework): update SPAR-07 wording to v2 install-time consent contract
237
+ - `3f9ef384` feat(framework): apt-run --spar-gates flag parsing + QUICK hard-reject
238
+ - `be06429a` feat(framework): apt-run §3b + §4b spar-gate handlers
239
+ - `36c7702d` feat(framework): emit spar_inject_gates + spar_consent envelope fields
240
+ - `d803f8c5` feat(framework): apt router consent-driven --spar-gates auto-inject
241
+ - `726ae3f5` feat(framework): apt-plan + apt-execute --spar per-skill flag
242
+ - `e796580e` feat(framework): apt-run spar.hint nudge at stage success-ends
243
+
244
+ ## 0.8.2 — 2026-05-21 — Codex skill-discovery regression fix
245
+
246
+ Codex skill-discovery regression fix — install now writes to the
247
+ documented `.agents/skills/<folder>/SKILL.md` surface.
248
+
249
+ ### Fixed
250
+
251
+ - Codex `installRoot` re-targeted from `.codex` to `.agents` so SKILL.md
252
+ lands at `<targetDir>/.agents/skills/<folder>/SKILL.md` per
253
+ https://developers.openai.com/codex/skills. The previous v0.7/v0.8
254
+ `.codex/agents/` + `.codex/commands/` topology was a dead surface
255
+ (Codex CLI 0.132.0 doesn't discover skills there) and a fresh
256
+ `apt-tools init . --codex` reported 95 files installed while
257
+ `codex debug prompt-input` listed zero apt-* skills.
258
+ - Codex frontmatter strip switched from a 2-key blacklist
259
+ (`['tools', 'color']`) to an allowed_properties whitelist
260
+ (`KEEP = ['name', 'description']`), matching
261
+ `~/.codex/skills/.system/skill-creator/scripts/quick_validate.py:40`
262
+ and the working third-party skill examples. Drift-proof: any new
263
+ framework frontmatter key is dropped from Codex output without
264
+ breaking the validator.
265
+ - Codex transform now emits a single output (`skills/<folder>/SKILL.md`)
266
+ per canonical skill. The previous two-output emission
267
+ (`agents/<folder>.md` + `commands/<folder>.md`) is dropped along with
268
+ the `fileType === 'agent'` branch (markdown agents are not a Codex
269
+ surface; Codex custom-agents are TOML under `.codex/agents/`).
270
+ - `runLegacyCleanup` now GCs the v0.7/v0.8 `.codex/agents/apt-*.md` +
271
+ `.codex/commands/apt-*.md` + `.codex/apt-file-manifest.json` misdirect
272
+ on the next `apt-tools init . --codex`. `.codex/config.toml` and any
273
+ user-added `.codex/` content is preserved.
274
+
275
+ ### Changed
276
+
277
+ - `getTrustedSkillSources` now skips the Codex install root in the
278
+ per-cli filter (`cli !== 'codex'` guard). Keeps
279
+ `.aperant/logs/route-dropped.jsonl` clean for router invocations on
280
+ Codex hosts — Codex's stripped frontmatter would otherwise fail
281
+ `SkillFrontmatterSchema`. Canonical + kernel roots still serve
282
+ routing.
283
+ - `LEGACY_INSTALL_PATHS` schema gains a `honor_manifest?: boolean`
284
+ field (default `true`). When `false`, a present
285
+ `apt-file-manifest.json` at the legacy root does NOT trigger the
286
+ managed-no-op — used for known-dead surfaces like the v0.7/v0.8
287
+ codex misdirect. Existing legacy entries are unaffected (default true
288
+ preserves the managed-install safety).
289
+
290
+ ### Version
291
+
292
+ - `packages/framework/package.json`: 0.8.1 → 0.8.2
293
+
294
+ ## 0.8.1 — 2026-05-20 — Pi runtime + idempotent re-init repair
295
+
296
+ Patch release making `apt-tools init` safe to rerun on another machine after
297
+ pulling a current `.aperant/config.json`.
298
+
299
+ ### Fixed
300
+
301
+ - Current-schema `init` no longer returns `up_to_date` when explicit runtime
302
+ flags were passed or when suggested runtime roots are missing
303
+ `apt-file-manifest.json`; it reruns the runtime installer for those roots.
304
+ - True no-op behavior is preserved when no runtime roots are suggested and no
305
+ explicit runtime flags are passed.
306
+
307
+ ### Added
308
+
309
+ - Pi is now an installable runtime via `--pi`, with files written under
310
+ `.pi/skills/` and `.pi/prompts/`.
311
+ - `cliToInstallRoot('pi')` now resolves to `.pi`.
312
+
313
+ ### Version
314
+
315
+ - `packages/framework/package.json`: 0.8.0 → 0.8.1
316
+ - CLI golden fixtures refreshed
7
317
 
8
318
  ## 0.8.0 — 2026-05-20 — cross-task fan-out + 5 framework defects (BUG-033..038)
9
319
 
package/README.md CHANGED
@@ -11,6 +11,12 @@
11
11
  npx @aperant/framework init .
12
12
  ```
13
13
 
14
+ For non-interactive install or re-init across common runtimes:
15
+
16
+ ```bash
17
+ npx @aperant/framework init . --claude --codex --pi --yes
18
+ ```
19
+
14
20
  ## Usage
15
21
 
16
22
  Just type `/apt` and describe what you want in natural language. The router classifies your input and dispatches to the right skill.
@@ -25,6 +31,12 @@ Just type `/apt` and describe what you want in natural language. The router clas
25
31
 
26
32
  You can also call any skill directly (e.g. `/apt:plan ...`, `/apt:debug ...`).
27
33
 
34
+ Runtime command surfaces differ slightly:
35
+
36
+ - Claude: `/apt:plan`
37
+ - Codex: `/apt-plan`
38
+ - Pi: `/apt`, `/apt:plan` via `.pi/prompts/`
39
+
28
40
  ## Available commands
29
41
 
30
42
  | Stage | Command | Purpose |
@@ -32,7 +32,8 @@ Before planning, discover project context:
32
32
  <environment>
33
33
  - **Working directory:** The project root
34
34
  - **apt-tools:** Always invoke as `node packages/framework/bin/apt-tools.mjs`
35
- - **Task directory:** Provided in the spawn prompt as `task_dir`. ALL artifacts (spec.md, implementation_plan.json) are written here, NOT to project root.
35
+ - **Task directory:** Provided in the spawn prompt as `task_dir`. It MUST be an absolute path under the main repo's `.aperant/tasks/{task-id}/`. ALL artifacts (spec.md, implementation_plan.json) are written here, NOT to project root and NOT to a worktree-local `.aperant/tasks/` directory.
36
+ - **Worktree path:** If provided, use it only for code investigation/editing context. Do not derive artifact paths from it.
36
37
  - **Config:** `.aperant/config.json` — project preferences
37
38
  - **State:** `.aperant/state.json` — execution state
38
39
  </environment>
@@ -43,7 +44,7 @@ Before planning, discover project context:
43
44
 
44
45
  1. Read `CLAUDE.md` and `AGENTS.md` if they exist — extract conventions, stack, constraints
45
46
  2. Run `node packages/framework/bin/apt-tools.mjs state .` to understand current project state
46
- 3. Parse the task description and task_dir from the spawn prompt
47
+ 3. Parse the task description and task_dir from the spawn prompt. If task_dir is relative, resolve the real main repo path first instead of writing under the current worktree.
47
48
 
48
49
  ## 2. Assess Complexity
49
50
 
@@ -211,7 +212,7 @@ If unable to enumerate (network unavailable, action docs unclear), flag it expli
211
212
 
212
213
  ## 4. Write Specification
213
214
 
214
- The spec.md content format **branches on track** (Pocock adoption ID-05,
215
+ The spec.md content format **branches on track** (ID-05,
215
216
  Fast Path Guarantee). QUICK-routed tasks keep the existing 4-section
216
217
  shape; STANDARD/DEEP/COMPLEX tasks get a PRD-shaped artifact that
217
218
  survives the task as a second-consumer document.
@@ -344,7 +345,7 @@ fix it now, not later.
344
345
 
345
346
  ## 5. Create Implementation Plan
346
347
 
347
- Subtask schema branches on track (Pocock adoption ID-05 — Fast Path Guarantee).
348
+ Subtask schema branches on track.
348
349
 
349
350
  ### 5.A. QUICK track / SIMPLE complexity — legacy flat subtask schema
350
351
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * commands/adr.mjs — `apt-tools adr write` (Pocock adoption ST-6, AC5).
2
+ * commands/adr.mjs — `apt-tools adr write`.
3
3
  *
4
4
  * Writes an ADR under `docs/adr/NNNN-slug.md` ONLY when all three Nygard
5
5
  * gates pass: hard-to-reverse AND surprising-without-context AND
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @param {string} projectDir
3
+ * @param {string[]} extraArgs
4
+ */
5
+ export function auditBranchCurrent(projectDir: string, extraArgs?: string[]): import("../util/result.mjs").CommandResult;
6
+ export { parseConflictPaths as __parseConflictPathsForTests };
7
+ /**
8
+ * Parse `git merge-tree --write-tree --no-messages HEAD origin/<base>` output
9
+ * for conflict file paths. Modern (git ≥ 2.38) merge-tree emits, on conflict:
10
+ *
11
+ * <tree-OID>
12
+ * <conflict report lines, each "<mode> <stage> <oid>\t<path>" for stage > 0>
13
+ * (NUL separator)
14
+ * <textual conflict messages>
15
+ *
16
+ * Older variants emit a `+<<<<<<< ... +======= ... +>>>>>>>` diff per file.
17
+ * For correctness we use the stable form: extract file paths from the conflict
18
+ * report. The report includes the SAME path multiple times (once per non-zero
19
+ * stage), so we de-dupe.
20
+ *
21
+ * @param {string} stdout
22
+ * @returns {string[]}
23
+ */
24
+ declare function parseConflictPaths(stdout: string): string[];
25
+ //# sourceMappingURL=audit-branch-current.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-branch-current.d.mts","sourceRoot":"","sources":["../../../src/cli/commands/audit-branch-current.mjs"],"names":[],"mappings":"AAmIA;;;GAGG;AACH,+CAHW,MAAM,cACN,MAAM,EAAE,8CAgHlB;;AAtKD;;;;;;;;;;;;;;;;GAgBG;AACH,4CAHW,MAAM,GACJ,MAAM,EAAE,CAsBpB"}
@@ -0,0 +1,252 @@
1
+ /**
2
+ * commands/audit-branch-current.mjs — pre-PR rebase gate envelope for apt:ship §0.7.
3
+ *
4
+ * Pure read-only branch-drift audit. Given a base ref (default `main`), reports
5
+ * how many commits HEAD is ahead/behind `origin/<base>`, captures the current
6
+ * `origin/<base>` tip SHA (the lease target for any subsequent
7
+ * `--force-with-lease=refs/heads/<branch>:<sha>` push), and probes whether a
8
+ * trial merge against the remote tip would conflict.
9
+ *
10
+ * Single network call: `git fetch origin <base>`. Fetch failure is recovered
11
+ * as `audit_warning: 'fetch-failed'` — the envelope still returns, just with
12
+ * stale counts. No `git rebase`, no `git push`, no `git reset` — the SKILL.md
13
+ * prose owns history rewrites.
14
+ *
15
+ * Envelope:
16
+ *
17
+ * {
18
+ * status: 'ok',
19
+ * command: 'audit-branch-current',
20
+ * projectDir: string,
21
+ * base: string,
22
+ * ahead: number, // commits HEAD has over origin/<base>
23
+ * behind: number, // commits origin/<base> has over HEAD
24
+ * remote_tip_sha: string | null, // origin/<base> after fetch
25
+ * mergeable: boolean | null, // null when probe unavailable
26
+ * conflicts_preview?: string[], // populated only when mergeable=false
27
+ * audit_warning?: string // 'fetch-failed' | 'git-version-too-old' | ...
28
+ * }
29
+ *
30
+ * Spec cross-ref: .aperant/tasks/apt-ship-pre-pr-rebase-gate-audit-branch_21-05-26/spec.md
31
+ *
32
+ * @internal
33
+ */
34
+ import { execFileSync } from 'node:child_process';
35
+ import { resolve } from 'node:path';
36
+ import { err, ok } from '../util/result.mjs';
37
+ /**
38
+ * Try-execute a git command, returning {ok, stdout, stderr, code}. Never throws.
39
+ *
40
+ * @param {string} cwd
41
+ * @param {string[]} args
42
+ */
43
+ function gitTry(cwd, args) {
44
+ try {
45
+ const stdout = execFileSync('git', args, {
46
+ cwd,
47
+ encoding: 'utf-8',
48
+ stdio: ['ignore', 'pipe', 'pipe'],
49
+ });
50
+ return { ok: true, stdout, stderr: '', code: 0 };
51
+ }
52
+ catch (e) {
53
+ // execFileSync throws an error whose `stdout`, `stderr`, and `status`
54
+ // properties carry the captured output and exit code.
55
+ return {
56
+ ok: false,
57
+ stdout: typeof e?.stdout === 'string' ? e.stdout : (e?.stdout?.toString() ?? ''),
58
+ stderr: typeof e?.stderr === 'string' ? e.stderr : (e?.stderr?.toString() ?? ''),
59
+ code: typeof e?.status === 'number' ? e.status : 1,
60
+ };
61
+ }
62
+ }
63
+ /**
64
+ * Parse `--base <ref>` (or `--base=<ref>`) from extraArgs. Defaults to 'main'.
65
+ *
66
+ * @param {string[]} extraArgs
67
+ * @returns {string}
68
+ */
69
+ function parseBaseFlag(extraArgs) {
70
+ for (let i = 0; i < extraArgs.length; i++) {
71
+ const a = extraArgs[i];
72
+ if (a === '--base' && i + 1 < extraArgs.length)
73
+ return extraArgs[i + 1];
74
+ if (a.startsWith('--base='))
75
+ return a.slice('--base='.length);
76
+ }
77
+ return 'main';
78
+ }
79
+ /**
80
+ * Parse `git merge-tree --write-tree --no-messages HEAD origin/<base>` output
81
+ * for conflict file paths. Modern (git ≥ 2.38) merge-tree emits, on conflict:
82
+ *
83
+ * <tree-OID>
84
+ * <conflict report lines, each "<mode> <stage> <oid>\t<path>" for stage > 0>
85
+ * (NUL separator)
86
+ * <textual conflict messages>
87
+ *
88
+ * Older variants emit a `+<<<<<<< ... +======= ... +>>>>>>>` diff per file.
89
+ * For correctness we use the stable form: extract file paths from the conflict
90
+ * report. The report includes the SAME path multiple times (once per non-zero
91
+ * stage), so we de-dupe.
92
+ *
93
+ * @param {string} stdout
94
+ * @returns {string[]}
95
+ */
96
+ function parseConflictPaths(stdout) {
97
+ const paths = new Set();
98
+ for (const rawLine of stdout.split('\n')) {
99
+ const line = rawLine.replace(/\0/g, '');
100
+ if (!line.trim())
101
+ continue;
102
+ // Match "<mode> <oid> <stage>\t<path>" — git merge-tree --write-tree
103
+ // emits conflict report lines as "<mode> <oid> <stage>\t<path>".
104
+ // Stage 0 means no conflict, so we only surface stage ≥ 1.
105
+ const m = line.match(/^(\d{6})\s+([0-9a-f]{4,40})\s+(\d)\t(.+)$/);
106
+ if (m) {
107
+ const stage = Number.parseInt(m[3], 10);
108
+ if (stage >= 1)
109
+ paths.add(m[4]);
110
+ continue;
111
+ }
112
+ // Fallback (older git): the textual report block prefixes paths with
113
+ // "CONFLICT (content): Merge conflict in <path>".
114
+ const c = line.match(/Merge conflict in (.+)$/);
115
+ if (c)
116
+ paths.add(c[1].trim());
117
+ }
118
+ return Array.from(paths).sort();
119
+ }
120
+ /**
121
+ * Detect "git too old" from merge-tree stderr. Pre-2.38 git rejects
122
+ * `--write-tree` outright; older still rejects merge-tree's modern arg
123
+ * shape entirely. We treat any non-zero-with-no-output as "version too old"
124
+ * — callers are expected to fall back gracefully.
125
+ *
126
+ * @param {string} stderr
127
+ * @returns {boolean}
128
+ */
129
+ function isGitVersionTooOld(stderr) {
130
+ return /unknown option|did you mean|usage: git merge-tree/i.test(stderr);
131
+ }
132
+ /**
133
+ * @param {string} projectDir
134
+ * @param {string[]} extraArgs
135
+ */
136
+ export function auditBranchCurrent(projectDir, extraArgs = []) {
137
+ if (!projectDir) {
138
+ return err('Usage: apt-tools audit branch-current <project-dir> [--base <ref>]');
139
+ }
140
+ const targetDir = resolve(projectDir);
141
+ const base = parseBaseFlag(extraArgs);
142
+ /** @type {string | undefined} */
143
+ let auditWarning;
144
+ /** @type {string[]} */
145
+ const warnings = [];
146
+ // 1. Fetch origin <base>. Tolerate failure (offline, no remote, etc.).
147
+ const fetch = gitTry(targetDir, ['fetch', 'origin', base]);
148
+ if (!fetch.ok) {
149
+ auditWarning = 'fetch-failed';
150
+ warnings.push(fetch.stderr.trim().split('\n').slice(0, 3).join(' | '));
151
+ }
152
+ // 2. Resolve origin/<base> tip.
153
+ let remoteTipSha = null;
154
+ const tip = gitTry(targetDir, ['rev-parse', `origin/${base}`]);
155
+ if (tip.ok) {
156
+ remoteTipSha = tip.stdout.trim();
157
+ }
158
+ else {
159
+ // No origin/<base> ref at all — emit an envelope with null counts so the
160
+ // caller can recognise the gap. The SKILL.md prose treats null counts as
161
+ // "audit unavailable, STOP with a clear message" rather than proceeding.
162
+ return ok({
163
+ status: 'ok',
164
+ command: 'audit-branch-current',
165
+ projectDir: targetDir,
166
+ base,
167
+ ahead: null,
168
+ behind: null,
169
+ remote_tip_sha: null,
170
+ mergeable: null,
171
+ audit_warning: auditWarning ?? 'no-remote-base-ref',
172
+ warnings,
173
+ });
174
+ }
175
+ // 3. Counts.
176
+ let ahead = 0;
177
+ let behind = 0;
178
+ const aheadOut = gitTry(targetDir, ['rev-list', '--count', `origin/${base}..HEAD`]);
179
+ const behindOut = gitTry(targetDir, ['rev-list', '--count', `HEAD..origin/${base}`]);
180
+ if (aheadOut.ok)
181
+ ahead = Number.parseInt(aheadOut.stdout.trim(), 10) || 0;
182
+ if (behindOut.ok)
183
+ behind = Number.parseInt(behindOut.stdout.trim(), 10) || 0;
184
+ // 4. Mergeability.
185
+ // - When behind === 0, the branch is current or strictly ahead — no
186
+ // remote changes to integrate, mergeable = true by definition.
187
+ // - When behind > 0 AND ahead === 0, the branch is strictly fast-forward
188
+ // behind — a clean fast-forward rebase is always mergeable.
189
+ // - When diverged (behind > 0 AND ahead > 0), probe via git merge-tree.
190
+ let mergeable;
191
+ /** @type {string[] | undefined} */
192
+ let conflictsPreview;
193
+ if (behind === 0) {
194
+ mergeable = true;
195
+ }
196
+ else if (behind > 0 && ahead === 0) {
197
+ mergeable = true;
198
+ }
199
+ else {
200
+ // Diverged. Run modern merge-tree trial.
201
+ const probe = gitTry(targetDir, [
202
+ 'merge-tree',
203
+ '--write-tree',
204
+ '--no-messages',
205
+ 'HEAD',
206
+ `origin/${base}`,
207
+ ]);
208
+ if (probe.ok) {
209
+ mergeable = true;
210
+ }
211
+ else if (probe.code === 1) {
212
+ // Documented merge-tree exit-code semantics: 0 clean, 1 conflict,
213
+ // other = invocation error.
214
+ mergeable = false;
215
+ conflictsPreview = parseConflictPaths(probe.stdout);
216
+ }
217
+ else if (isGitVersionTooOld(probe.stderr)) {
218
+ mergeable = null;
219
+ auditWarning = auditWarning ?? 'git-version-too-old';
220
+ warnings.push(`git merge-tree --write-tree unsupported: ${probe.stderr.trim().slice(0, 200)}`);
221
+ }
222
+ else {
223
+ // Unknown probe failure — treat as unknown rather than asserting
224
+ // clean.
225
+ mergeable = null;
226
+ auditWarning = auditWarning ?? 'merge-tree-probe-failed';
227
+ warnings.push(`git merge-tree probe failed (code ${probe.code}): ${probe.stderr.trim().slice(0, 200)}`);
228
+ }
229
+ }
230
+ const envelope = {
231
+ status: 'ok',
232
+ command: 'audit-branch-current',
233
+ projectDir: targetDir,
234
+ base,
235
+ ahead,
236
+ behind,
237
+ remote_tip_sha: remoteTipSha,
238
+ mergeable,
239
+ };
240
+ if (conflictsPreview && conflictsPreview.length > 0) {
241
+ envelope.conflicts_preview = conflictsPreview;
242
+ }
243
+ if (auditWarning)
244
+ envelope.audit_warning = auditWarning;
245
+ if (warnings.length > 0)
246
+ envelope.warnings = warnings;
247
+ return ok(envelope);
248
+ }
249
+ // Test-only export for unit-level parser coverage (not part of the public CLI
250
+ // contract).
251
+ export { parseConflictPaths as __parseConflictPathsForTests };
252
+ //# sourceMappingURL=audit-branch-current.mjs.map