spexcode 0.6.2 → 0.6.6

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 (495) hide show
  1. package/README.md +3 -1
  2. package/bin/spex.mjs +4 -0
  3. package/node_modules/@spexcode/session-core/dist/delivery-queue.d.ts +23 -0
  4. package/node_modules/@spexcode/session-core/dist/delivery-queue.js +195 -0
  5. package/node_modules/@spexcode/session-core/dist/index.d.ts +5 -0
  6. package/node_modules/@spexcode/session-core/dist/index.js +5 -0
  7. package/node_modules/@spexcode/session-core/dist/internal.d.ts +3 -0
  8. package/node_modules/@spexcode/session-core/dist/internal.js +3 -0
  9. package/node_modules/@spexcode/session-core/dist/message.d.ts +22 -0
  10. package/node_modules/@spexcode/session-core/dist/message.js +52 -0
  11. package/node_modules/@spexcode/session-core/dist/record-lock.d.ts +7 -0
  12. package/node_modules/@spexcode/session-core/dist/record-lock.js +152 -0
  13. package/node_modules/@spexcode/session-core/dist/runtime-session.d.ts +50 -0
  14. package/node_modules/@spexcode/session-core/dist/runtime-session.js +286 -0
  15. package/node_modules/@spexcode/session-core/dist/session-cursors.d.ts +14 -0
  16. package/node_modules/@spexcode/session-core/dist/session-cursors.js +82 -0
  17. package/node_modules/@spexcode/session-core/dist/session-timeline.d.ts +46 -0
  18. package/node_modules/@spexcode/session-core/dist/session-timeline.js +216 -0
  19. package/node_modules/@spexcode/session-core/package.json +33 -0
  20. package/node_modules/@spexcode/spec-cli/bin/spex.mjs +77 -0
  21. package/node_modules/@spexcode/spec-cli/dist/attach.d.ts +2 -0
  22. package/node_modules/@spexcode/spec-cli/dist/attach.js +48 -0
  23. package/node_modules/@spexcode/spec-cli/dist/claude-headless.d.ts +36 -0
  24. package/node_modules/@spexcode/spec-cli/dist/claude-headless.js +316 -0
  25. package/node_modules/@spexcode/spec-cli/dist/cli.d.ts +1 -0
  26. package/node_modules/@spexcode/spec-cli/dist/cli.js +1908 -0
  27. package/node_modules/@spexcode/spec-cli/dist/client.d.ts +112 -0
  28. package/node_modules/@spexcode/spec-cli/dist/client.js +481 -0
  29. package/node_modules/@spexcode/spec-cli/dist/cockpit.d.ts +7 -0
  30. package/node_modules/@spexcode/spec-cli/dist/cockpit.js +26 -0
  31. package/node_modules/@spexcode/spec-cli/dist/codex-headless.d.ts +1 -0
  32. package/{spec-cli/src/codex-headless.ts → node_modules/@spexcode/spec-cli/dist/codex-headless.js} +3 -9
  33. package/node_modules/@spexcode/spec-cli/dist/codex-runtime-generations.d.ts +58 -0
  34. package/node_modules/@spexcode/spec-cli/dist/codex-runtime-generations.js +798 -0
  35. package/node_modules/@spexcode/spec-cli/dist/commit-surgery.d.ts +1 -0
  36. package/node_modules/@spexcode/spec-cli/dist/commit-surgery.js +90 -0
  37. package/node_modules/@spexcode/spec-cli/dist/contract-filter.d.ts +14 -0
  38. package/node_modules/@spexcode/spec-cli/dist/contract-filter.js +219 -0
  39. package/node_modules/@spexcode/spec-cli/dist/dashboard-assets.d.ts +6 -0
  40. package/node_modules/@spexcode/spec-cli/dist/dashboard-assets.js +50 -0
  41. package/node_modules/@spexcode/spec-cli/dist/doctor.d.ts +9 -0
  42. package/node_modules/@spexcode/spec-cli/dist/doctor.js +553 -0
  43. package/node_modules/@spexcode/spec-cli/dist/eval-host.d.ts +1 -0
  44. package/node_modules/@spexcode/spec-cli/dist/eval-host.js +19 -0
  45. package/node_modules/@spexcode/spec-cli/dist/execution-trace.d.ts +26 -0
  46. package/node_modules/@spexcode/spec-cli/dist/execution-trace.js +475 -0
  47. package/node_modules/@spexcode/spec-cli/dist/file-write.d.ts +2 -0
  48. package/node_modules/@spexcode/spec-cli/dist/file-write.js +26 -0
  49. package/node_modules/@spexcode/spec-cli/dist/flat.d.ts +89 -0
  50. package/node_modules/@spexcode/spec-cli/dist/flat.js +1076 -0
  51. package/node_modules/@spexcode/spec-cli/dist/gateway-auth.d.ts +56 -0
  52. package/node_modules/@spexcode/spec-cli/dist/gateway-auth.js +168 -0
  53. package/node_modules/@spexcode/spec-cli/dist/gateway-hub.d.ts +28 -0
  54. package/node_modules/@spexcode/spec-cli/dist/gateway-hub.js +363 -0
  55. package/node_modules/@spexcode/spec-cli/dist/gateway.d.ts +38 -0
  56. package/node_modules/@spexcode/spec-cli/dist/gateway.js +527 -0
  57. package/node_modules/@spexcode/spec-cli/dist/graphCache.d.ts +27 -0
  58. package/node_modules/@spexcode/spec-cli/dist/graphCache.js +717 -0
  59. package/node_modules/@spexcode/spec-cli/dist/graphSnapshot.d.ts +14 -0
  60. package/node_modules/@spexcode/spec-cli/dist/graphSnapshot.js +36 -0
  61. package/node_modules/@spexcode/spec-cli/dist/graphStream.d.ts +85 -0
  62. package/node_modules/@spexcode/spec-cli/dist/graphStream.js +1195 -0
  63. package/node_modules/@spexcode/spec-cli/dist/guidance-catalog.d.ts +64 -0
  64. package/node_modules/@spexcode/spec-cli/dist/guidance-catalog.js +166 -0
  65. package/node_modules/@spexcode/spec-cli/dist/guide.d.ts +8 -0
  66. package/{spec-cli/src/guide.ts → node_modules/@spexcode/spec-cli/dist/guide.js} +36 -32
  67. package/node_modules/@spexcode/spec-cli/dist/harness-select.d.ts +18 -0
  68. package/node_modules/@spexcode/spec-cli/dist/harness-select.js +62 -0
  69. package/node_modules/@spexcode/spec-cli/dist/harness.d.ts +338 -0
  70. package/node_modules/@spexcode/spec-cli/dist/harness.js +3146 -0
  71. package/node_modules/@spexcode/spec-cli/dist/headless-controller.d.ts +9 -0
  72. package/node_modules/@spexcode/spec-cli/dist/headless-controller.js +47 -0
  73. package/node_modules/@spexcode/spec-cli/dist/help.d.ts +15 -0
  74. package/node_modules/@spexcode/spec-cli/dist/help.js +594 -0
  75. package/node_modules/@spexcode/spec-cli/dist/hook-prompts.d.ts +18 -0
  76. package/node_modules/@spexcode/spec-cli/dist/hook-prompts.js +122 -0
  77. package/node_modules/@spexcode/spec-cli/dist/hooks.d.ts +1 -0
  78. package/{spec-cli/src/hooks.ts → node_modules/@spexcode/spec-cli/dist/hooks.js} +15 -15
  79. package/node_modules/@spexcode/spec-cli/dist/host-resources.d.ts +97 -0
  80. package/node_modules/@spexcode/spec-cli/dist/host-resources.js +795 -0
  81. package/node_modules/@spexcode/spec-cli/dist/host.d.ts +88 -0
  82. package/node_modules/@spexcode/spec-cli/dist/host.js +663 -0
  83. package/node_modules/@spexcode/spec-cli/dist/index.d.ts +1 -0
  84. package/node_modules/@spexcode/spec-cli/dist/index.js +899 -0
  85. package/node_modules/@spexcode/spec-cli/dist/init.d.ts +1 -0
  86. package/node_modules/@spexcode/spec-cli/dist/init.js +284 -0
  87. package/node_modules/@spexcode/spec-cli/dist/issues-cli.d.ts +6 -0
  88. package/node_modules/@spexcode/spec-cli/dist/issues-cli.js +334 -0
  89. package/node_modules/@spexcode/spec-cli/dist/issues.d.ts +69 -0
  90. package/node_modules/@spexcode/spec-cli/dist/issues.js +189 -0
  91. package/node_modules/@spexcode/spec-cli/dist/lint.d.ts +34 -0
  92. package/node_modules/@spexcode/spec-cli/dist/lint.js +476 -0
  93. package/node_modules/@spexcode/spec-cli/dist/listen.d.ts +8 -0
  94. package/node_modules/@spexcode/spec-cli/dist/listen.js +24 -0
  95. package/node_modules/@spexcode/spec-cli/dist/localIssues.d.ts +65 -0
  96. package/node_modules/@spexcode/spec-cli/dist/localIssues.js +582 -0
  97. package/node_modules/@spexcode/spec-cli/dist/login-page.d.ts +5 -0
  98. package/{spec-cli/src/login-page.ts → node_modules/@spexcode/spec-cli/dist/login-page.js} +7 -7
  99. package/node_modules/@spexcode/spec-cli/dist/loop-in.d.ts +23 -0
  100. package/{spec-cli/src/loop-in.ts → node_modules/@spexcode/spec-cli/dist/loop-in.js} +35 -45
  101. package/node_modules/@spexcode/spec-cli/dist/machine-peer.d.ts +69 -0
  102. package/node_modules/@spexcode/spec-cli/dist/machine-peer.js +603 -0
  103. package/node_modules/@spexcode/spec-cli/dist/materialize.d.ts +18 -0
  104. package/node_modules/@spexcode/spec-cli/dist/materialize.js +468 -0
  105. package/node_modules/@spexcode/spec-cli/dist/mentions.d.ts +50 -0
  106. package/node_modules/@spexcode/spec-cli/dist/mentions.js +117 -0
  107. package/node_modules/@spexcode/spec-cli/dist/opencode-headless.d.ts +5 -0
  108. package/node_modules/@spexcode/spec-cli/dist/opencode-headless.js +215 -0
  109. package/node_modules/@spexcode/spec-cli/dist/opencode.d.ts +3 -0
  110. package/{spec-cli/src/opencode.ts → node_modules/@spexcode/spec-cli/dist/opencode.js} +9 -13
  111. package/node_modules/@spexcode/spec-cli/dist/pi-harness.d.ts +3 -0
  112. package/{spec-cli/src/pi-harness.ts → node_modules/@spexcode/spec-cli/dist/pi-harness.js} +45 -40
  113. package/node_modules/@spexcode/spec-cli/dist/pi-headless.d.ts +25 -0
  114. package/node_modules/@spexcode/spec-cli/dist/pi-headless.js +196 -0
  115. package/node_modules/@spexcode/spec-cli/dist/plugin-harness.d.ts +21 -0
  116. package/node_modules/@spexcode/spec-cli/dist/plugin-harness.js +146 -0
  117. package/node_modules/@spexcode/spec-cli/dist/pty-bridge.d.ts +10 -0
  118. package/node_modules/@spexcode/spec-cli/dist/pty-bridge.js +404 -0
  119. package/node_modules/@spexcode/spec-cli/dist/pty-helper.d.mts +1 -0
  120. package/node_modules/@spexcode/spec-cli/dist/pty-helper.mjs +101 -0
  121. package/node_modules/@spexcode/spec-cli/dist/pty-native-helper.d.mts +2 -0
  122. package/node_modules/@spexcode/spec-cli/dist/pty-native-helper.mjs +23 -0
  123. package/node_modules/@spexcode/spec-cli/dist/public-graph.d.ts +46 -0
  124. package/node_modules/@spexcode/spec-cli/dist/public-graph.js +56 -0
  125. package/node_modules/@spexcode/spec-cli/dist/ranker.d.ts +15 -0
  126. package/node_modules/@spexcode/spec-cli/dist/ranker.js +188 -0
  127. package/node_modules/@spexcode/spec-cli/dist/reaper.d.ts +6 -0
  128. package/node_modules/@spexcode/spec-cli/dist/reaper.js +81 -0
  129. package/node_modules/@spexcode/spec-cli/dist/reviews.d.ts +189 -0
  130. package/node_modules/@spexcode/spec-cli/dist/reviews.js +350 -0
  131. package/node_modules/@spexcode/spec-cli/dist/runtime-guard.d.ts +6 -0
  132. package/node_modules/@spexcode/spec-cli/dist/runtime-guard.js +37 -0
  133. package/node_modules/@spexcode/spec-cli/dist/runtime-ownership.d.ts +21 -0
  134. package/node_modules/@spexcode/spec-cli/dist/runtime-ownership.js +84 -0
  135. package/node_modules/@spexcode/spec-cli/dist/runtime-rotate.d.ts +1 -0
  136. package/node_modules/@spexcode/spec-cli/dist/runtime-rotate.js +58 -0
  137. package/node_modules/@spexcode/spec-cli/dist/search.bench.d.mts +1 -0
  138. package/node_modules/@spexcode/spec-cli/dist/search.bench.mjs +75 -0
  139. package/node_modules/@spexcode/spec-cli/dist/search.d.ts +20 -0
  140. package/node_modules/@spexcode/spec-cli/dist/search.js +61 -0
  141. package/node_modules/@spexcode/spec-cli/dist/session-declarations.d.ts +13 -0
  142. package/node_modules/@spexcode/spec-cli/dist/session-declarations.js +126 -0
  143. package/node_modules/@spexcode/spec-cli/dist/session-execution.d.ts +10 -0
  144. package/node_modules/@spexcode/spec-cli/dist/session-execution.js +70 -0
  145. package/node_modules/@spexcode/spec-cli/dist/session-files.d.ts +28 -0
  146. package/node_modules/@spexcode/spec-cli/dist/session-files.js +130 -0
  147. package/node_modules/@spexcode/spec-cli/dist/session-follow.d.ts +31 -0
  148. package/node_modules/@spexcode/spec-cli/dist/session-follow.js +179 -0
  149. package/node_modules/@spexcode/spec-cli/dist/session-reparent.d.ts +10 -0
  150. package/node_modules/@spexcode/spec-cli/dist/session-reparent.js +33 -0
  151. package/node_modules/@spexcode/spec-cli/dist/session-timeline.d.ts +11 -0
  152. package/node_modules/@spexcode/spec-cli/dist/session-timeline.js +20 -0
  153. package/node_modules/@spexcode/spec-cli/dist/session-web.d.ts +24 -0
  154. package/node_modules/@spexcode/spec-cli/dist/session-web.js +141 -0
  155. package/node_modules/@spexcode/spec-cli/dist/sessions.d.ts +433 -0
  156. package/node_modules/@spexcode/spec-cli/dist/sessions.js +4667 -0
  157. package/node_modules/@spexcode/spec-cli/dist/sh.d.ts +1 -0
  158. package/node_modules/@spexcode/spec-cli/dist/sh.js +3 -0
  159. package/node_modules/@spexcode/spec-cli/dist/shim-runtime.d.ts +1 -0
  160. package/{spec-cli/src/shim-runtime.ts → node_modules/@spexcode/spec-cli/dist/shim-runtime.js} +3 -4
  161. package/node_modules/@spexcode/spec-cli/dist/slash-commands.d.ts +9 -0
  162. package/node_modules/@spexcode/spec-cli/dist/slash-commands.js +307 -0
  163. package/node_modules/@spexcode/spec-cli/dist/source-files.d.ts +9 -0
  164. package/node_modules/@spexcode/spec-cli/dist/source-files.js +113 -0
  165. package/node_modules/@spexcode/spec-cli/dist/supervise.d.ts +1 -0
  166. package/node_modules/@spexcode/spec-cli/dist/supervise.js +308 -0
  167. package/node_modules/@spexcode/spec-cli/dist/tree.d.ts +25 -0
  168. package/node_modules/@spexcode/spec-cli/dist/tree.js +98 -0
  169. package/node_modules/@spexcode/spec-cli/dist/tsx-bin.d.ts +3 -0
  170. package/node_modules/@spexcode/spec-cli/dist/tsx-bin.js +41 -0
  171. package/node_modules/@spexcode/spec-cli/dist/uninstall.d.ts +3 -0
  172. package/node_modules/@spexcode/spec-cli/dist/uninstall.js +177 -0
  173. package/node_modules/@spexcode/spec-cli/dist/uploads.d.ts +36 -0
  174. package/node_modules/@spexcode/spec-cli/dist/uploads.js +243 -0
  175. package/node_modules/@spexcode/spec-cli/dist/worktree-sources.d.ts +1 -0
  176. package/node_modules/@spexcode/spec-cli/dist/worktree-sources.js +47 -0
  177. package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/dispatch.sh +5 -5
  178. package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/harness.sh +1 -1
  179. package/node_modules/@spexcode/spec-cli/package.json +49 -0
  180. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/commands/supervisor/spec.md +8 -0
  181. package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-first/spec-first.sh +1 -1
  182. package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-of-file/spec-of-file.sh +1 -0
  183. package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/stop-gate/spec.md +2 -2
  184. package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/stop-gate/stop-gate.sh +31 -29
  185. package/node_modules/@spexcode/spec-core/dist/anchors.d.ts +94 -0
  186. package/node_modules/@spexcode/spec-core/dist/anchors.js +730 -0
  187. package/node_modules/@spexcode/spec-core/dist/git.d.ts +170 -0
  188. package/node_modules/@spexcode/spec-core/dist/git.js +2768 -0
  189. package/node_modules/@spexcode/spec-core/dist/graph-delta.d.ts +44 -0
  190. package/node_modules/@spexcode/spec-core/dist/graph-delta.js +72 -0
  191. package/node_modules/@spexcode/spec-core/dist/graph.d.ts +34 -0
  192. package/node_modules/@spexcode/spec-core/dist/graph.js +237 -0
  193. package/node_modules/@spexcode/spec-core/dist/graphDelta.d.ts +3 -0
  194. package/node_modules/@spexcode/spec-core/dist/graphDelta.js +13 -0
  195. package/node_modules/@spexcode/spec-core/dist/harness-identity.d.ts +31 -0
  196. package/node_modules/@spexcode/spec-core/dist/harness-identity.js +20 -0
  197. package/node_modules/@spexcode/spec-core/dist/identity-presets.d.ts +152 -0
  198. package/node_modules/@spexcode/spec-core/dist/identity-presets.js +132 -0
  199. package/node_modules/@spexcode/spec-core/dist/index.d.ts +45 -0
  200. package/node_modules/@spexcode/spec-core/dist/index.js +19 -0
  201. package/node_modules/@spexcode/spec-core/dist/layout.d.ts +187 -0
  202. package/node_modules/@spexcode/spec-core/dist/layout.js +548 -0
  203. package/node_modules/@spexcode/spec-core/dist/process-identity.d.ts +37 -0
  204. package/node_modules/@spexcode/spec-core/dist/process-identity.js +214 -0
  205. package/node_modules/@spexcode/spec-core/dist/project-identity.d.ts +12 -0
  206. package/node_modules/@spexcode/spec-core/dist/project-identity.js +71 -0
  207. package/node_modules/@spexcode/spec-core/dist/project-store.d.ts +3 -0
  208. package/node_modules/@spexcode/spec-core/dist/project-store.js +14 -0
  209. package/node_modules/@spexcode/spec-core/dist/resilience.d.ts +2 -0
  210. package/node_modules/@spexcode/spec-core/dist/resilience.js +40 -0
  211. package/node_modules/@spexcode/spec-core/dist/review/index.d.ts +3 -0
  212. package/node_modules/@spexcode/spec-core/dist/review/index.js +5 -0
  213. package/node_modules/@spexcode/spec-core/dist/review/reviewFilters.d.ts +77 -0
  214. package/node_modules/@spexcode/spec-core/dist/review/reviewFilters.js +308 -0
  215. package/node_modules/@spexcode/spec-core/dist/review/reviewQuery.d.ts +66 -0
  216. package/node_modules/@spexcode/spec-core/dist/review/reviewQuery.js +180 -0
  217. package/node_modules/@spexcode/spec-core/dist/review/session.d.ts +4 -0
  218. package/node_modules/@spexcode/spec-core/dist/review/session.js +8 -0
  219. package/node_modules/@spexcode/spec-core/dist/reviewSnapshot.d.ts +15 -0
  220. package/node_modules/@spexcode/spec-core/dist/reviewSnapshot.js +12 -0
  221. package/node_modules/@spexcode/spec-core/dist/root-lru.d.ts +4 -0
  222. package/{spec-cli/src/root-lru.ts → node_modules/@spexcode/spec-core/dist/root-lru.js} +26 -30
  223. package/node_modules/@spexcode/spec-core/dist/specs.d.ts +117 -0
  224. package/node_modules/@spexcode/spec-core/dist/specs.js +489 -0
  225. package/node_modules/@spexcode/spec-core/package.json +28 -0
  226. package/node_modules/@spexcode/spec-eval/dist/cache.d.ts +10 -0
  227. package/node_modules/@spexcode/spec-eval/dist/cache.js +50 -0
  228. package/node_modules/@spexcode/spec-eval/dist/cli.d.ts +22 -0
  229. package/node_modules/@spexcode/spec-eval/dist/cli.js +981 -0
  230. package/node_modules/@spexcode/spec-eval/dist/evaltab.d.ts +98 -0
  231. package/node_modules/@spexcode/spec-eval/dist/evaltab.js +176 -0
  232. package/node_modules/@spexcode/spec-eval/dist/filing.d.ts +16 -0
  233. package/node_modules/@spexcode/spec-eval/dist/filing.js +36 -0
  234. package/node_modules/@spexcode/spec-eval/dist/freshness.d.ts +48 -0
  235. package/node_modules/@spexcode/spec-eval/dist/freshness.js +799 -0
  236. package/node_modules/@spexcode/spec-eval/dist/host.d.ts +46 -0
  237. package/node_modules/@spexcode/spec-eval/dist/host.js +53 -0
  238. package/node_modules/@spexcode/spec-eval/dist/humanok.d.ts +11 -0
  239. package/node_modules/@spexcode/spec-eval/dist/humanok.js +27 -0
  240. package/node_modules/@spexcode/spec-eval/dist/index.d.ts +4 -0
  241. package/node_modules/@spexcode/spec-eval/dist/index.js +4 -0
  242. package/node_modules/@spexcode/spec-eval/dist/remarks.d.ts +31 -0
  243. package/node_modules/@spexcode/spec-eval/dist/remarks.js +1 -0
  244. package/node_modules/@spexcode/spec-eval/dist/scenariofresh.d.ts +13 -0
  245. package/node_modules/@spexcode/spec-eval/dist/scenariofresh.js +303 -0
  246. package/node_modules/@spexcode/spec-eval/dist/scenarios.d.ts +99 -0
  247. package/node_modules/@spexcode/spec-eval/dist/scenarios.js +650 -0
  248. package/node_modules/@spexcode/spec-eval/dist/sessioneval.d.ts +307 -0
  249. package/node_modules/@spexcode/spec-eval/dist/sessioneval.js +1883 -0
  250. package/node_modules/@spexcode/spec-eval/dist/sidecar.d.ts +55 -0
  251. package/node_modules/@spexcode/spec-eval/dist/sidecar.js +82 -0
  252. package/node_modules/@spexcode/spec-eval/dist/timeline.d.ts +25 -0
  253. package/node_modules/@spexcode/spec-eval/dist/timeline.js +65 -0
  254. package/node_modules/@spexcode/spec-eval/dist/ui-path.d.ts +1 -0
  255. package/node_modules/@spexcode/spec-eval/dist/ui-path.js +2 -0
  256. package/node_modules/@spexcode/spec-eval/package.json +36 -0
  257. package/node_modules/@spexcode/spec-forge/dist/cache.d.ts +29 -0
  258. package/node_modules/@spexcode/spec-forge/dist/cache.js +59 -0
  259. package/node_modules/@spexcode/spec-forge/dist/cli.d.ts +1 -0
  260. package/node_modules/@spexcode/spec-forge/dist/cli.js +100 -0
  261. package/node_modules/@spexcode/spec-forge/dist/drivers/github.d.ts +2 -0
  262. package/node_modules/@spexcode/spec-forge/dist/drivers/github.js +116 -0
  263. package/node_modules/@spexcode/spec-forge/dist/drivers/gitlab.d.ts +7 -0
  264. package/node_modules/@spexcode/spec-forge/dist/drivers/gitlab.js +144 -0
  265. package/node_modules/@spexcode/spec-forge/dist/drivers.d.ts +10 -0
  266. package/node_modules/@spexcode/spec-forge/dist/drivers.js +83 -0
  267. package/node_modules/@spexcode/spec-forge/dist/index.d.ts +4 -0
  268. package/node_modules/@spexcode/spec-forge/dist/index.js +4 -0
  269. package/node_modules/@spexcode/spec-forge/dist/links.d.ts +15 -0
  270. package/node_modules/@spexcode/spec-forge/dist/links.js +65 -0
  271. package/node_modules/@spexcode/spec-forge/dist/needs-eval.d.ts +10 -0
  272. package/node_modules/@spexcode/spec-forge/dist/needs-eval.js +20 -0
  273. package/node_modules/@spexcode/spec-forge/dist/port.d.ts +53 -0
  274. package/node_modules/@spexcode/spec-forge/dist/port.js +1 -0
  275. package/node_modules/@spexcode/spec-forge/dist/resident.d.ts +7 -0
  276. package/node_modules/@spexcode/spec-forge/dist/resident.js +43 -0
  277. package/node_modules/@spexcode/spec-forge/package.json +32 -0
  278. package/package.json +33 -19
  279. package/spec-cli/bin/spex.mjs +0 -69
  280. package/spec-cli/src/anchors.ts +0 -728
  281. package/spec-cli/src/attach.ts +0 -43
  282. package/spec-cli/src/claude-headless.ts +0 -321
  283. package/spec-cli/src/cli.ts +0 -1258
  284. package/spec-cli/src/client.ts +0 -414
  285. package/spec-cli/src/cockpit.ts +0 -43
  286. package/spec-cli/src/codex-runtime-generations.ts +0 -646
  287. package/spec-cli/src/commit-surgery.ts +0 -68
  288. package/spec-cli/src/contract-filter.ts +0 -202
  289. package/spec-cli/src/delivery-queue.ts +0 -165
  290. package/spec-cli/src/doctor.ts +0 -516
  291. package/spec-cli/src/execution-trace.ts +0 -444
  292. package/spec-cli/src/file-write.ts +0 -22
  293. package/spec-cli/src/gateway-auth.ts +0 -181
  294. package/spec-cli/src/gateway-hub.ts +0 -358
  295. package/spec-cli/src/gateway.ts +0 -461
  296. package/spec-cli/src/git.ts +0 -2556
  297. package/spec-cli/src/graph.ts +0 -271
  298. package/spec-cli/src/graphCache.ts +0 -710
  299. package/spec-cli/src/graphDelta.ts +0 -77
  300. package/spec-cli/src/graphStream.ts +0 -1019
  301. package/spec-cli/src/harness-select.ts +0 -77
  302. package/spec-cli/src/harness.ts +0 -3025
  303. package/spec-cli/src/headless-controller.ts +0 -63
  304. package/spec-cli/src/help.ts +0 -492
  305. package/spec-cli/src/host-resources.ts +0 -762
  306. package/spec-cli/src/host.ts +0 -611
  307. package/spec-cli/src/identity-presets.d.ts +0 -13
  308. package/spec-cli/src/identity-presets.js +0 -138
  309. package/spec-cli/src/index.ts +0 -818
  310. package/spec-cli/src/init.ts +0 -258
  311. package/spec-cli/src/issues-cli.ts +0 -276
  312. package/spec-cli/src/issues.ts +0 -237
  313. package/spec-cli/src/layout.ts +0 -640
  314. package/spec-cli/src/lint.ts +0 -488
  315. package/spec-cli/src/listen.ts +0 -28
  316. package/spec-cli/src/localIssues.ts +0 -538
  317. package/spec-cli/src/materialize.ts +0 -417
  318. package/spec-cli/src/mentions.ts +0 -81
  319. package/spec-cli/src/opencode-headless.ts +0 -205
  320. package/spec-cli/src/pi-headless.ts +0 -153
  321. package/spec-cli/src/plugin-harness.ts +0 -157
  322. package/spec-cli/src/process-identity.ts +0 -207
  323. package/spec-cli/src/project-identity.ts +0 -73
  324. package/spec-cli/src/project-store.ts +0 -17
  325. package/spec-cli/src/pty-bridge.ts +0 -396
  326. package/spec-cli/src/pty-helper.mjs +0 -96
  327. package/spec-cli/src/pty-native-helper.mjs +0 -22
  328. package/spec-cli/src/ranker.ts +0 -191
  329. package/spec-cli/src/reaper.ts +0 -125
  330. package/spec-cli/src/resilience.ts +0 -41
  331. package/spec-cli/src/reviewSnapshot.ts +0 -28
  332. package/spec-cli/src/reviews.ts +0 -434
  333. package/spec-cli/src/runtime-guard.ts +0 -35
  334. package/spec-cli/src/runtime-ownership.ts +0 -88
  335. package/spec-cli/src/search.bench.mjs +0 -80
  336. package/spec-cli/src/search.ts +0 -63
  337. package/spec-cli/src/session-cursors.ts +0 -91
  338. package/spec-cli/src/session-execution.ts +0 -68
  339. package/spec-cli/src/session-files.ts +0 -121
  340. package/spec-cli/src/session-follow.ts +0 -182
  341. package/spec-cli/src/session-reparent.ts +0 -33
  342. package/spec-cli/src/session-timeline.ts +0 -247
  343. package/spec-cli/src/session-web.ts +0 -135
  344. package/spec-cli/src/sessions.ts +0 -3883
  345. package/spec-cli/src/sh.ts +0 -3
  346. package/spec-cli/src/slash-commands.ts +0 -318
  347. package/spec-cli/src/source-files.ts +0 -104
  348. package/spec-cli/src/specs.ts +0 -498
  349. package/spec-cli/src/supervise.ts +0 -245
  350. package/spec-cli/src/tree.ts +0 -118
  351. package/spec-cli/src/tsx-bin.ts +0 -18
  352. package/spec-cli/src/uninstall.ts +0 -171
  353. package/spec-cli/src/uploads.ts +0 -253
  354. package/spec-cli/src/worktree-sources.ts +0 -42
  355. package/spec-cli/templates/spec/project/.plugins/commands/supervisor/spec.md +0 -8
  356. package/spec-dashboard/dist/assets/App-B6IbFbGu.js +0 -2
  357. package/spec-dashboard/dist/assets/Dashboard-C5ap-Sga.css +0 -1
  358. package/spec-dashboard/dist/assets/Dashboard-_sDk7Rcg.js +0 -27
  359. package/spec-dashboard/dist/assets/EvalsPage-BT357Lyt.js +0 -2
  360. package/spec-dashboard/dist/assets/IssuesPage-BG9rNOgV.js +0 -1
  361. package/spec-dashboard/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  362. package/spec-dashboard/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  363. package/spec-dashboard/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  364. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  365. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  366. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  367. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  368. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  369. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  370. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  371. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  372. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  373. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  374. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  375. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  376. package/spec-dashboard/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  377. package/spec-dashboard/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  378. package/spec-dashboard/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  379. package/spec-dashboard/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  380. package/spec-dashboard/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  381. package/spec-dashboard/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  382. package/spec-dashboard/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  383. package/spec-dashboard/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  384. package/spec-dashboard/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  385. package/spec-dashboard/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  386. package/spec-dashboard/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  387. package/spec-dashboard/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  388. package/spec-dashboard/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  389. package/spec-dashboard/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  390. package/spec-dashboard/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  391. package/spec-dashboard/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  392. package/spec-dashboard/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  393. package/spec-dashboard/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  394. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  395. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  396. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  397. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  398. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  399. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  400. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  401. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  402. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  403. package/spec-dashboard/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  404. package/spec-dashboard/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  405. package/spec-dashboard/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  406. package/spec-dashboard/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  407. package/spec-dashboard/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  408. package/spec-dashboard/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  409. package/spec-dashboard/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  410. package/spec-dashboard/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  411. package/spec-dashboard/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  412. package/spec-dashboard/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  413. package/spec-dashboard/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  414. package/spec-dashboard/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  415. package/spec-dashboard/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  416. package/spec-dashboard/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  417. package/spec-dashboard/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  418. package/spec-dashboard/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  419. package/spec-dashboard/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  420. package/spec-dashboard/dist/assets/MobileApp-D9yPMR3A.js +0 -2
  421. package/spec-dashboard/dist/assets/Modal-B3EcgeA3.js +0 -1
  422. package/spec-dashboard/dist/assets/PageScroll-CNZOugWV.js +0 -1
  423. package/spec-dashboard/dist/assets/ProjectsPage-C2CRHmvs.js +0 -1
  424. package/spec-dashboard/dist/assets/SessionInterface-6GBZ9nXN.css +0 -32
  425. package/spec-dashboard/dist/assets/SessionInterface-DOEjuT3l.js +0 -40
  426. package/spec-dashboard/dist/assets/SessionWindow-1nYgZ4CL.js +0 -1
  427. package/spec-dashboard/dist/assets/Settings-7WeTJn0V.js +0 -1
  428. package/spec-dashboard/dist/assets/Thread-CEbzAFQn.js +0 -13
  429. package/spec-dashboard/dist/assets/bindings-BC9vqpYU.js +0 -1
  430. package/spec-dashboard/dist/assets/data-SlvxNA2r.js +0 -1
  431. package/spec-dashboard/dist/assets/index-DLVeOHL-.js +0 -41
  432. package/spec-dashboard/dist/assets/index-DYLGfPjx.css +0 -1
  433. package/spec-dashboard/dist/assets/launch-Cp579UoJ.css +0 -1
  434. package/spec-dashboard/dist/assets/launch-yxCYr64x.js +0 -276
  435. package/spec-dashboard/dist/assets/sessionSurface--7l69h4F.js +0 -1
  436. package/spec-dashboard/dist/index.html +0 -29
  437. package/spec-dashboard/src/reviewFilters.js +0 -322
  438. package/spec-dashboard/src/reviewQuery.js +0 -174
  439. package/spec-dashboard/src/session.js +0 -199
  440. package/spec-eval/src/cache.ts +0 -57
  441. package/spec-eval/src/cli.ts +0 -894
  442. package/spec-eval/src/evaltab.ts +0 -264
  443. package/spec-eval/src/filing.ts +0 -39
  444. package/spec-eval/src/freshness.ts +0 -836
  445. package/spec-eval/src/humanok.ts +0 -28
  446. package/spec-eval/src/scenariofresh.ts +0 -244
  447. package/spec-eval/src/scenarios.ts +0 -672
  448. package/spec-eval/src/sessioneval.ts +0 -2220
  449. package/spec-eval/src/sidecar.ts +0 -93
  450. package/spec-eval/src/timeline.ts +0 -54
  451. package/spec-forge/src/__fixtures__/github-forge.json +0 -63
  452. package/spec-forge/src/cache.ts +0 -66
  453. package/spec-forge/src/cli.ts +0 -99
  454. package/spec-forge/src/drivers/github.ts +0 -132
  455. package/spec-forge/src/drivers/gitlab.ts +0 -153
  456. package/spec-forge/src/drivers.ts +0 -73
  457. package/spec-forge/src/links.ts +0 -72
  458. package/spec-forge/src/needs-eval.ts +0 -29
  459. package/spec-forge/src/port.ts +0 -36
  460. package/spec-forge/src/resident.ts +0 -46
  461. /package/{spec-cli → node_modules/@spexcode/spec-cli}/README.md +0 -0
  462. /package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/compat/mark-active-0.5.2-eef1.fixture +0 -0
  463. /package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/compat/mark-active-sed-v0.fixture +0 -0
  464. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/commit-msg +0 -0
  465. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/post-checkout +0 -0
  466. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/post-merge +0 -0
  467. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/pre-commit +0 -0
  468. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/prepare-commit-msg +0 -0
  469. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/reference-transaction +0 -0
  470. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/extract/spec.md +0 -0
  471. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/regroup/spec.md +0 -0
  472. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/rename/spec.md +0 -0
  473. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/spec.md +0 -0
  474. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/tidy/spec.md +0 -0
  475. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/comment-altitude/spec.md +0 -0
  476. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/idle/idle.sh +0 -0
  477. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/idle/spec.md +0 -0
  478. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/mark-active/mark-active.sh +0 -0
  479. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/mark-active/spec.md +0 -0
  480. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/session-fail/fail.sh +0 -0
  481. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/session-fail/spec.md +0 -0
  482. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-first/spec.md +0 -0
  483. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-of-file/spec.md +0 -0
  484. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec.md +0 -0
  485. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/atomic-landing/spec.md +0 -0
  486. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/forge-link/spec.md +0 -0
  487. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/memory-hygiene/spec.md +0 -0
  488. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/reproduce-before-fix/spec.md +0 -0
  489. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/spec.md +0 -0
  490. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/skills/distill/digest.mjs +0 -0
  491. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/skills/distill/spec.md +0 -0
  492. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/skills/spec.md +0 -0
  493. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/spec.md +0 -0
  494. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/spec.md +0 -0
  495. /package/{spec-cli → node_modules/@spexcode/spec-core}/templates/spexcode.json +0 -0
@@ -1,205 +0,0 @@
1
- import { execFile } from 'node:child_process'
2
- import { mkdtempSync, readFileSync, rmSync } from 'node:fs'
3
- import { tmpdir, userInfo } from 'node:os'
4
- import { join } from 'node:path'
5
- import { promisify } from 'node:util'
6
- import { headlessTurnFailureShell, type DispatchResult, type HarnessDeliveryRecord } from './harness.js'
7
- import { shQuote } from './sh.js'
8
-
9
- const pexec = promisify(execFile)
10
- const WAKE_EARLY_EXIT_MS = 5_000
11
- const OUTCOME_POLL_MS = 25
12
-
13
- const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
14
-
15
- function accountLoginShell(): string {
16
- try { return userInfo().shell || '' } catch { return '' }
17
- }
18
-
19
- // A headless turn owns the pane only while `opencode run` is alive. Returning to a shell keeps the tmux
20
- // window as the session's durable home without adding a resident controller or an stdin bridge.
21
- function turnHome(command: string, outcomePath?: string): string {
22
- const outcomeSetup = outcomePath ? [
23
- `__spex_outcome_path=${shQuote(outcomePath)}`,
24
- `__spex_outcome_tmp=${shQuote(`${outcomePath}.tmp`)}`,
25
- '__spex_outcome() { printf \'%s\\n\' "$1" > "$__spex_outcome_tmp" && mv -f "$__spex_outcome_tmp" "$__spex_outcome_path"; }',
26
- '__spex_outcome "running:$$" || { printf "[spex opencode-headless] could not create turn outcome marker\\n" >&2; exit 125; }',
27
- ] : []
28
- const failure = outcomePath ? [
29
- '__spex_cas_rc=0',
30
- 'if [ "$__spex_rc" -ne 0 ]; then',
31
- ' __spex_outcome "reporting:$$:$__spex_rc" || true',
32
- ` ${headlessTurnFailureShell('opencode-headless', false)}`,
33
- ' __spex_cas_rc=$?',
34
- 'fi',
35
- '__spex_outcome "exit:$__spex_rc:cas:$__spex_cas_rc" || printf "[spex opencode-headless] could not finalize turn outcome marker\\n" >&2',
36
- ] : [
37
- `if [ "$__spex_rc" -ne 0 ]; then ${headlessTurnFailureShell('opencode-headless')}; fi`,
38
- ]
39
- const script = [
40
- ...outcomeSetup,
41
- command,
42
- '__spex_rc=$?',
43
- ...failure,
44
- '[ "$__spex_rc" -eq 0 ] || printf "[spex opencode-headless] turn exited rc=%s\\n" "$__spex_rc" >&2',
45
- 'exec "${SHELL:-/bin/sh}"',
46
- ].join('\n')
47
- return `bash -lc ${shQuote(script)} spexcode-opencode-headless`
48
- }
49
-
50
- // Launcher profiles carry a base executable plus its configured flags (`opencode --auto`). OpenCode parses
51
- // `run` as a subcommand, so it must sit between those two halves (`opencode run --auto`), not at the tail.
52
- function runPrelude(opencodeCmd: string): string[] {
53
- const accountShell = accountLoginShell()
54
- return [
55
- '__spex_login_shell="${SHELL:-}"',
56
- `[ -n "$__spex_login_shell" ] || __spex_login_shell=${shQuote(accountShell)}`,
57
- `__spex_cmd=(${opencodeCmd})`,
58
- '__spex_env=()',
59
- 'while [ "${#__spex_cmd[@]}" -gt 0 ] && [[ "${__spex_cmd[0]}" =~ ^[A-Za-z_][A-Za-z0-9_]*= ]]; do',
60
- ' __spex_env+=("${__spex_cmd[0]}")',
61
- ' __spex_cmd=("${__spex_cmd[@]:1}")',
62
- 'done',
63
- // Resolve OpenCode in the same login + interactive environment the user relies on. The launcher-leading
64
- // assignments are applied after shell startup, so its explicit per-session config still wins.
65
- '__spex_run() { [ -n "$__spex_login_shell" ] || { printf "[spex opencode-headless] no login shell could be resolved - turn NOT started\\n" >&2; return 126; }; "$__spex_login_shell" -ilc \'exec env "$@"\' spexcode-opencode-headless "${__spex_env[@]}" "${__spex_cmd[0]}" run "${__spex_cmd[@]:1}" "$@"; }',
66
- ]
67
- }
68
-
69
- export function opencodeHeadlessLaunchCommand(opencodeCmd = 'opencode'): string {
70
- const script = [
71
- ...runPrelude(opencodeCmd),
72
- 'if [ "${1:-}" = "--resume" ]; then',
73
- ' export SPEXCODE_OPENCODE_RESUME_ID="$2"',
74
- ' unset SPEXCODE_OPENCODE_CONTINUE',
75
- ' __spex_run --session "$2"',
76
- 'elif [ "${1:-}" = "--continue" ]; then',
77
- ' unset SPEXCODE_OPENCODE_RESUME_ID',
78
- ' export SPEXCODE_OPENCODE_CONTINUE=1',
79
- ' __spex_run --continue',
80
- 'elif [ -n "${1:-}" ]; then',
81
- ' unset SPEXCODE_OPENCODE_RESUME_ID SPEXCODE_OPENCODE_CONTINUE',
82
- ' __spex_run "$1"',
83
- 'else',
84
- ' __spex_run',
85
- 'fi',
86
- ].join('\n')
87
- return turnHome(script)
88
- }
89
-
90
- export function opencodeHeadlessWakeCommand(
91
- opencodeCmd: string,
92
- harnessSessionId: string | null | undefined,
93
- text: string,
94
- outcomePath?: string,
95
- ): string {
96
- const resume = harnessSessionId ? [
97
- `export SPEXCODE_OPENCODE_RESUME_ID=${shQuote(harnessSessionId)}`,
98
- 'unset SPEXCODE_OPENCODE_CONTINUE',
99
- `__spex_run --session ${shQuote(harnessSessionId)} ${shQuote(text)}`,
100
- ] : [
101
- 'unset SPEXCODE_OPENCODE_RESUME_ID',
102
- 'export SPEXCODE_OPENCODE_CONTINUE=1',
103
- `__spex_run --continue ${shQuote(text)}`,
104
- ]
105
- return turnHome([...runPrelude(opencodeCmd), ...resume].join('\n'), outcomePath)
106
- }
107
-
108
- type TurnOutcome =
109
- | { state: 'running'; pid: number }
110
- | { state: 'reporting'; pid: number; code: number }
111
- | { state: 'exit'; code: number; casCode: number }
112
- | { state: 'invalid' }
113
-
114
- function readTurnOutcome(path: string): TurnOutcome | undefined {
115
- let value: string
116
- try { value = readFileSync(path, 'utf8').trim() } catch (error) {
117
- if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined
118
- throw error
119
- }
120
- let match = /^running:(\d+)$/.exec(value)
121
- if (match) return { state: 'running', pid: Number(match[1]) }
122
- match = /^reporting:(\d+):(-?\d+)$/.exec(value)
123
- if (match) return { state: 'reporting', pid: Number(match[1]), code: Number(match[2]) }
124
- match = /^exit:(-?\d+):cas:(\d+)$/.exec(value)
125
- if (match) return { state: 'exit', code: Number(match[1]), casCode: Number(match[2]) }
126
- return { state: 'invalid' }
127
- }
128
-
129
- function turnExited(rec: HarnessDeliveryRecord, outcome: Extract<TurnOutcome, { state: 'exit' }>): DispatchResult {
130
- if (outcome.code === 0) return { ok: true }
131
- const cas = outcome.casCode === 0 ? '' : `; error CAS reporter also exited with code ${outcome.casCode}`
132
- return {
133
- ok: false,
134
- error: `opencode-headless turn exited with code ${outcome.code} during startup for session ${rec.session}${cas} - immediate wake failed`,
135
- }
136
- }
137
-
138
- function pidAlive(pid: number): boolean {
139
- if (!Number.isInteger(pid) || pid <= 0) return false
140
- try { process.kill(pid, 0); return true } catch (error) {
141
- return (error as NodeJS.ErrnoException).code === 'EPERM'
142
- }
143
- }
144
-
145
- export async function spawnOpenCodeHeadlessTurn(
146
- rec: HarnessDeliveryRecord,
147
- text: string,
148
- opencodeCmd: string,
149
- socketPath: string,
150
- ): Promise<DispatchResult> {
151
- if (!rec.worktreePath) return { ok: false, error: `opencode-headless session ${rec.session} has no worktree path - turn NOT started` }
152
- let outcomeDir: string
153
- try { outcomeDir = mkdtempSync(join(tmpdir(), 'spexcode-oh-turn-')) } catch (error) {
154
- return { ok: false, error: `opencode-headless could not prepare turn confirmation for session ${rec.session}: ${(error as Error).message}` }
155
- }
156
- const outcomePath = join(outcomeDir, 'exit-code')
157
- const tmuxSock = process.env.SPEXCODE_TMUX || 'spexcode'
158
- const args = [
159
- '-L', tmuxSock, 'respawn-pane', '-k', '-t', rec.session, '-c', rec.worktreePath,
160
- '-e', `SPEXCODE_SESSION_ID=${rec.session}`,
161
- '-e', 'CLAUDE_BG_BACKEND=daemon',
162
- '-e', `CLAUDE_BG_RENDEZVOUS_SOCK=${socketPath}`,
163
- ]
164
- for (const name of ['SPEXCODE_HOME', 'CODEX_HOME']) {
165
- const value = process.env[name]
166
- if (value) args.push('-e', `${name}=${value}`)
167
- }
168
- args.push(opencodeHeadlessWakeCommand(opencodeCmd, rec.harnessSessionId, text, outcomePath))
169
- try {
170
- await pexec('tmux', args, { timeout: 5_000 })
171
- const deadline = Date.now() + WAKE_EARLY_EXIT_MS
172
- for (;;) {
173
- const outcome = readTurnOutcome(outcomePath)
174
- if (outcome?.state === 'exit') return turnExited(rec, outcome)
175
- if (outcome?.state === 'invalid') {
176
- return { ok: false, error: `opencode-headless turn for session ${rec.session} wrote an invalid exit outcome - immediate wake not confirmed` }
177
- }
178
- if (Date.now() >= deadline) break
179
- await sleep(Math.min(OUTCOME_POLL_MS, deadline - Date.now()))
180
- }
181
-
182
- const first = readTurnOutcome(outcomePath)
183
- if (first?.state === 'exit') return turnExited(rec, first)
184
- if (!first) return { ok: false, error: `opencode-headless turn for session ${rec.session} never confirmed startup - immediate wake failed` }
185
- if (first.state === 'invalid') return { ok: false, error: `opencode-headless turn for session ${rec.session} wrote an invalid exit outcome - immediate wake not confirmed` }
186
- if (first.state === 'reporting') {
187
- return { ok: false, error: `opencode-headless turn exited with code ${first.code} but its error CAS reporter did not finish for session ${rec.session} - immediate wake failed` }
188
- }
189
- if (!pidAlive(first.pid)) {
190
- return { ok: false, error: `opencode-headless turn wrapper died before reporting an outcome for session ${rec.session} - immediate wake failed` }
191
- }
192
- await sleep(OUTCOME_POLL_MS)
193
- const settled = readTurnOutcome(outcomePath)
194
- if (settled?.state === 'exit') return turnExited(rec, settled)
195
- if (settled?.state !== 'running' || settled.pid !== first.pid || !pidAlive(settled.pid)) {
196
- return { ok: false, error: `opencode-headless turn did not remain live through startup for session ${rec.session} - immediate wake failed` }
197
- }
198
- return { ok: true }
199
- } catch (error) {
200
- const detail = error instanceof Error ? error.message : String(error)
201
- return { ok: false, error: `opencode-headless could not start a turn for session ${rec.session}: ${detail}` }
202
- } finally {
203
- rmSync(outcomeDir, { recursive: true, force: true })
204
- }
205
- }
@@ -1,153 +0,0 @@
1
- import { createServer, type Server, type Socket } from 'node:net'
2
- import { spawn, type ChildProcess } from 'node:child_process'
3
- import { mkdirSync, rmSync } from 'node:fs'
4
- import { tmpdir } from 'node:os'
5
- import { join } from 'node:path'
6
- import { fileURLToPath } from 'node:url'
7
- import type { DispatchResult, HarnessDeliveryRecord } from './harness.js'
8
- import { controlRequest, withTimeout } from './headless-controller.js'
9
- import { shQuote } from './sh.js'
10
-
11
- type ControlRequest = { type: 'deliver'; text: string; mid?: string }
12
- type ChildTurn = { process: ChildProcess; exited: Promise<number | null> }
13
-
14
- const PKG = fileURLToPath(new URL('..', import.meta.url))
15
- const SPEX = join(PKG, 'bin', 'spex.mjs')
16
- const CONTROL_TIMEOUT_MS = 30_000
17
- const START_TIMEOUT_MS = 30_000
18
-
19
- /** The resident controller socket is distinct from pi's per-turn rendezvous socket. */
20
- export const piHeadlessSock = (id: string) => join(tmpdir(), `spexcode-ph-${id}.sock`)
21
-
22
- export function piHeadlessLaunchCommand(id: string, runtimeDir: string, piCmd: string): string {
23
- return [shQuote(SPEX), 'internal', 'pi-headless-run', shQuote(id), shQuote(runtimeDir), shQuote(piCmd), '--'].join(' ')
24
- }
25
-
26
- export const deliverViaPiHeadless = (rec: HarnessDeliveryRecord, text: string) =>
27
- controlRequest(piHeadlessSock(rec.session), { type: 'deliver', text, mid: rec.mid }, {
28
- name: 'pi-headless', session: rec.session, timeoutMs: CONTROL_TIMEOUT_MS,
29
- rejected: 'pi-headless controller rejected the request',
30
- })
31
-
32
- export class PiHeadlessController {
33
- private server: Server | null = null
34
- private child: ChildTurn | null = null
35
- private controlQueue: Promise<void> = Promise.resolve()
36
- private closing = false
37
- private readonly socketPath: string
38
-
39
- constructor(
40
- private readonly id: string,
41
- _runtimeDir: string,
42
- private readonly piCmd: string,
43
- private readonly cwd = process.cwd(),
44
- ) {
45
- this.socketPath = piHeadlessSock(id)
46
- }
47
-
48
- async start(initialPrompt?: string): Promise<void> {
49
- try { rmSync(this.socketPath, { force: true }) } catch { /* stale control socket is replaced at startup */ }
50
- this.server = createServer((socket) => this.accept(socket))
51
- await new Promise<void>((resolve, reject) => {
52
- const onError = (error: Error) => { this.server?.off('listening', onListening); reject(error) }
53
- const onListening = () => { this.server?.off('error', onError); resolve() }
54
- this.server!.once('error', onError)
55
- this.server!.once('listening', onListening)
56
- this.server!.listen(this.socketPath)
57
- })
58
- if (initialPrompt) void this.spawnTurn(initialPrompt, false).catch((error) => {
59
- console.error(`[spex pi-headless] initial turn failed: ${(error as Error).message}`)
60
- })
61
- }
62
-
63
- async close(): Promise<void> {
64
- if (this.closing) return
65
- this.closing = true
66
- const child = this.child
67
- if (child && child.process.exitCode === null) child.process.kill('SIGTERM')
68
- await new Promise<void>((resolve) => {
69
- if (!this.server) return resolve()
70
- this.server.close(() => resolve())
71
- })
72
- // same proof-before-removal rule as every other teardown (harness.ts unlinkSocks): a socket path is keyed
73
- // by session id alone, so only a listener PROVEN dead is ours to unlink.
74
- const { rvSock, unlinkSocks } = await import('./harness.js')
75
- await unlinkSocks(this.socketPath, rvSock(this.id))
76
- }
77
-
78
- private accept(socket: Socket): void {
79
- socket.setEncoding('utf8')
80
- let buffer = ''
81
- let handled = false
82
- socket.on('data', (chunk) => {
83
- if (handled) return
84
- buffer += chunk
85
- const nl = buffer.indexOf('\n')
86
- if (nl < 0) return
87
- handled = true
88
- let request: ControlRequest
89
- try { request = JSON.parse(buffer.slice(0, nl)) as ControlRequest } catch (error) {
90
- socket.end(`${JSON.stringify({ ok: false, error: `invalid control request: ${(error as Error).message}` })}\n`)
91
- return
92
- }
93
- this.controlQueue = this.controlQueue.then(async () => {
94
- const result = await this.handle(request).catch((error) => ({ ok: false, error: (error as Error).message }))
95
- socket.end(`${JSON.stringify(result)}\n`)
96
- })
97
- })
98
- }
99
-
100
- private async handle(request: ControlRequest): Promise<DispatchResult> {
101
- if (request.type !== 'deliver') return { ok: false, error: 'unknown pi-headless control request' }
102
- if (!request.text) return { ok: false, error: 'empty prompt - nothing to deliver' }
103
-
104
- // A live extension listener is an in-flight pi turn. Only a proven absent listener may cold-wake a saved
105
- // session; an inconclusive probe must not start a duplicate turn.
106
- const { deliverViaSocketOrWake } = await import('./harness.js')
107
- return deliverViaSocketOrWake(this.id, request.text, request.mid, async () => {
108
- if (this.child) await withTimeout(this.child.exited, 5_000, `previous pi-headless turn did not exit for session ${this.id}`)
109
- await this.spawnTurn(request.text, true)
110
- return { ok: true }
111
- }, `could not determine whether pi turn ${this.id} is live — prompt NOT delivered`)
112
- }
113
-
114
- private async spawnTurn(text: string, resume: boolean): Promise<void> {
115
- if (this.closing) throw new Error('pi-headless controller is closing')
116
- const mode = resume ? ['--session', this.id] : ['--session-id', this.id]
117
- // Keep pi's default text mode. `--mode json` is intentionally omitted: it can hang in this runtime.
118
- const args = ['-p', ...mode, text]
119
- const command = `exec ${this.piCmd} ${args.map(shQuote).join(' ')}`
120
- const childProcess = spawn('/bin/sh', ['-lc', command], { cwd: this.cwd, env: process.env, stdio: ['ignore', 'pipe', 'pipe'] })
121
- let resolveExit!: (code: number | null) => void
122
- const exited = new Promise<number | null>((resolve) => { resolveExit = resolve })
123
- const turn: ChildTurn = { process: childProcess, exited }
124
- this.child = turn
125
- childProcess.stdout?.pipe(process.stdout)
126
- childProcess.stderr?.pipe(process.stderr)
127
- childProcess.once('error', (error) => console.error(`[spex pi-headless] child spawn failed: ${error.message}`))
128
- childProcess.once('close', (code) => {
129
- if (this.child === turn) this.child = null
130
- resolveExit(code)
131
- if (code !== 0 && !this.closing) void import('./harness.js').then(({ reportHeadlessTurnExit }) => reportHeadlessTurnExit(this.id, 'pi-headless', code, this.cwd))
132
- })
133
- await withTimeout(new Promise<void>((resolve, reject) => {
134
- childProcess.once('spawn', () => resolve())
135
- childProcess.once('error', reject)
136
- }), START_TIMEOUT_MS, `pi-headless child did not start for session ${this.id}`)
137
- }
138
- }
139
-
140
- export async function runPiHeadlessController(id: string, runtimeDir: string, piCmd: string, tail: string[]): Promise<void> {
141
- // runtimeDir is retained in the command shape for parity with claude-headless and future per-session output.
142
- mkdirSync(join(runtimeDir, 'sessions', id), { recursive: true })
143
- const controller = new PiHeadlessController(id, runtimeDir, piCmd)
144
- const resume = tail[0] === '--session'
145
- const prompt = resume ? undefined : tail[0] === '--session-id' ? tail.slice(2).join(' ') : tail.join(' ')
146
- await controller.start(prompt)
147
- await new Promise<void>((resolve) => {
148
- const stop = () => void controller.close().finally(resolve)
149
- process.once('SIGINT', stop)
150
- process.once('SIGTERM', stop)
151
- process.once('SIGHUP', stop)
152
- })
153
- }
@@ -1,157 +0,0 @@
1
- import { mkdirSync, readFileSync, existsSync, readdirSync, rmSync } from 'node:fs'
2
- import { join, dirname } from 'node:path'
3
- import { fileURLToPath } from 'node:url'
4
- import { copyFileIfChanged, writeFileIfChanged } from './file-write.js'
5
-
6
- // @@@ plugin-harness - the PLUGIN BUNDLE emitter: materialize the whole SpexCode system into ONE self-contained
7
- // Claude-plugin bundle dropped into the host-agent-scanned folder [[harness-select]] resolved (e.g. `.adopter-a` /
8
- // `.claude` → <folder>/plugins/spexcode/). It is the plugin-target counterpart of the native per-harness
9
- // write [[harness-adapter]] does — chosen INSTEAD of the natives (plugin exclusivity), so [[harness-delivery]]'s
10
- // materialize prunes every native first, then emits this. The bundle follows the de-facto Claude-plugin schema
11
- // (a `.claude-plugin/plugin.json` pointing at hooks/skills/commands/agents); the host's discovery order is
12
- // `.adopter-a-plugin > .claude-plugin > .codex-plugin` and adopter-a/Claude both read a `.claude-plugin` directly, so
13
- // the ONE `.claude-plugin` bundle reaches AdopterA, Claude, and (future) Codex from a single emit.
14
- //
15
- // The contract is NOT delivered by an always-on CLAUDE.md block here (the bundle never touches the repo's own
16
- // files) — it maps to a SessionStart hook that emits hookSpecificOutput.additionalContext (the harness-neutral
17
- // injection Claude/adopter-a normalize, the superpowers pattern), so a plugin host gets the contract with no
18
- // --append-system-prompt. The hooks reuse the SAME dispatch.sh wiring as the natives, located via the host's
19
- // ${CLAUDE_PLUGIN_ROOT} variable; dispatch.sh's first arg is the harness id `plugin`, so its shell mirror
20
- // (harness.sh) parses payloads as the claude family (adopter-a/Claude share Claude's tool names + file_path).
21
-
22
- const PKG = fileURLToPath(new URL('..', import.meta.url)) // installed spec-cli root
23
- const HOOKS_SRC = join(PKG, 'hooks') // the canonical dispatch.sh + harness.sh source
24
- const PLUGIN_NAME = 'spexcode'
25
- // the host substitutes ${CLAUDE_PLUGIN_ROOT} with the bundle's own absolute path before running a hook command
26
- // (the same variable adopter-a's hook-compat honours), so dispatch.sh/inject-contract.sh resolve regardless of
27
- // where the host scanned the bundle from.
28
- const PLUGIN_ROOT = '${CLAUDE_PLUGIN_ROOT}'
29
- // the lifecycle events the bundle binds — the Claude/adopter-a superset; a host that fires fewer (Codex) simply
30
- // never invokes the extras, so binding all is harmless and one emit serves every host.
31
- const PLUGIN_EVENTS = ['SessionStart', 'UserPromptSubmit', 'PreToolUse', 'PostToolUse', 'Stop', 'StopFailure', 'Notification'] as const
32
-
33
- // the gathered surface artifacts this materialize writes into the bundle. contract = the assembled guide+system
34
- // block (the SAME string the native path folds into CLAUDE.md, here delivered as additionalContext); skills/
35
- // agents/commands are the already-materialized file CONTENTS (reusing materialize's skillArtifact/agentArtifact),
36
- // keyed
37
- // by node name; spex is the baked tsx+cli invocation the dispatcher gate calls; version stamps plugin.json.
38
- export type PluginBundle = {
39
- contract: string
40
- skills: { name: string; content: string }[]
41
- agents: { name: string; content: string }[]
42
- commands: { name: string; content: string }[]
43
- spex: string
44
- version: string
45
- }
46
-
47
- // the bundle dir for a plugin folder: <proj>/<folder>/plugins/spexcode — `<folder>` is the host's plugins-scan
48
- // root ([[harness-select]] requires it explicit), `plugins/<name>` the conventional bundle location under it.
49
- export function pluginBundleDir(proj: string, folder: string): string {
50
- return join(proj, folder, 'plugins', PLUGIN_NAME)
51
- }
52
-
53
- // the de-facto Claude-plugin manifest: name (the bundle identity clean() gates on), version, description, and
54
- // the component pointers Claude/adopter-a discover (hooks.json + the skills/commands/agents dirs).
55
- function pluginManifest(version: string): string {
56
- return JSON.stringify({
57
- name: PLUGIN_NAME,
58
- version,
59
- description: 'SpexCode — spec-driven dev-flow contract, hooks, skills, commands & agents as one self-contained plugin.',
60
- hooks: './hooks/hooks.json',
61
- commands: './commands',
62
- agents: './agents',
63
- skills: './skills',
64
- }, null, 2)
65
- }
66
-
67
- // hooks.json in the Claude/adopter-a-compatible shape { "hooks": { "<Event>": [{ "hooks": [command…] }] } }. Every
68
- // event → the SHARED dispatch.sh (`plugin` baked as its harness id, SPEX inherited by handlers); SessionStart
69
- // ALSO runs inject-contract.sh first, so the contract additionalContext lands alongside the normal dispatch.
70
- function pluginHooksJson(spex: string): string {
71
- const dispatch = (e: string) => `SPEX='${spex}' bash "${PLUGIN_ROOT}/hooks/dispatch.sh" plugin ${e}`
72
- const inject = `bash "${PLUGIN_ROOT}/hooks/inject-contract.sh"`
73
- const hooks: Record<string, unknown> = {}
74
- for (const e of PLUGIN_EVENTS) {
75
- const cmds = e === 'SessionStart'
76
- ? [{ type: 'command', command: inject }, { type: 'command', command: dispatch(e) }]
77
- : [{ type: 'command', command: dispatch(e) }]
78
- hooks[e] = [{ hooks: cmds }]
79
- }
80
- return JSON.stringify({ hooks }, null, 2)
81
- }
82
-
83
- // the SessionStart hook OUTPUT carrying the contract as additionalContext. JSON-encoded HERE, at materialize time,
84
- // so the runtime hook is a trivial `cat` — never a fragile shell escaping of arbitrary contract prose.
85
- function contractContextJson(contract: string): string {
86
- return JSON.stringify({ hookSpecificOutput: { hookEventName: 'SessionStart', additionalContext: contract } }, null, 2)
87
- }
88
-
89
- // the SessionStart contract injector: print the pre-encoded additionalContext payload (the harness-neutral
90
- // stand-in for --append-system-prompt). A bare cat — all the encoding happened at materialize time.
91
- const INJECT_SH = `#!/usr/bin/env bash
92
- # Emit the SpexCode contract as SessionStart additionalContext — the harness-neutral contract injection (the
93
- # superpowers pattern; Claude/adopter-a normalize hookSpecificOutput.additionalContext) that replaces a plugin
94
- # host's missing --append-system-prompt. The JSON was written at materialize time, so this is a trivial cat.
95
- here="$(cd "$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
96
- [ -f "$here/contract-context.json" ] && cat "$here/contract-context.json"
97
- exit 0
98
- `
99
-
100
- // emit (or idempotently re-emit) the whole bundle into <folder>/plugins/spexcode.
101
- export function emitPlugin(proj: string, folder: string, r: PluginBundle): void {
102
- const bundle = pluginBundleDir(proj, folder)
103
- const meta = join(bundle, '.claude-plugin')
104
- const hooksDir = join(bundle, 'hooks')
105
- mkdirSync(meta, { recursive: true })
106
- mkdirSync(hooksDir, { recursive: true })
107
- writeFileIfChanged(join(meta, 'plugin.json'), pluginManifest(r.version))
108
- // hooks: the SHARED dispatcher + its shell mirror (copied verbatim — the exact native wiring), the contract
109
- // injector + its pre-encoded payload, and the event→dispatch binding.
110
- copyFileIfChanged(join(HOOKS_SRC, 'dispatch.sh'), join(hooksDir, 'dispatch.sh'))
111
- copyFileIfChanged(join(HOOKS_SRC, 'harness.sh'), join(hooksDir, 'harness.sh'))
112
- writeFileIfChanged(join(hooksDir, 'inject-contract.sh'), INJECT_SH)
113
- writeFileIfChanged(join(hooksDir, 'contract-context.json'), contractContextJson(r.contract))
114
- writeFileIfChanged(join(hooksDir, 'hooks.json'), pluginHooksJson(r.spex))
115
- // skills / agents / commands — the Claude-plugin layout, the SAME materialized contents as the native dirs.
116
- reconcileBundleDirectory(join(bundle, 'skills'), new Set(r.skills.map((s) => s.name)))
117
- reconcileBundleDirectory(join(bundle, 'agents'), new Set(r.agents.map((a) => `${a.name}.md`)))
118
- reconcileBundleDirectory(join(bundle, 'commands'), new Set(r.commands.map((c) => `${c.name}.md`)))
119
- for (const s of r.skills) writeBundleFile(join(bundle, 'skills', s.name, 'SKILL.md'), s.content)
120
- for (const a of r.agents) writeBundleFile(join(bundle, 'agents', `${a.name}.md`), a.content)
121
- for (const c of r.commands) writeBundleFile(join(bundle, 'commands', `${c.name}.md`), c.content)
122
- }
123
-
124
- function reconcileBundleDirectory(dir: string, expectedNames: ReadonlySet<string>): void {
125
- if (!existsSync(dir)) return
126
- for (const entry of readdirSync(dir)) {
127
- if (!expectedNames.has(entry)) rmSync(join(dir, entry), { recursive: true, force: true })
128
- }
129
- }
130
-
131
- function writeBundleFile(f: string, content: string): void {
132
- mkdirSync(dirname(f), { recursive: true })
133
- writeFileIfChanged(f, content)
134
- }
135
-
136
- // the INVERSE of emitPlugin — prune the bundle when its folder is DESELECTED ([[harness-delivery]] tracks the
137
- // previously-emitted folders and cleans any the current set dropped, e.g. switching plugin→native or folder A→B).
138
- // Identity-gated on the bundle's own plugin.json `name`, so it removes ONLY a spexcode bundle, never a folder
139
- // the user populated with another plugin.
140
- export function cleanPlugin(proj: string, folder: string): void {
141
- const bundle = pluginBundleDir(proj, folder)
142
- const manifest = join(bundle, '.claude-plugin', 'plugin.json')
143
- if (!existsSync(manifest)) return
144
- try {
145
- if (String(JSON.parse(readFileSync(manifest, 'utf8'))?.name) !== PLUGIN_NAME) return
146
- } catch { return }
147
- rmSync(bundle, { recursive: true, force: true })
148
- }
149
-
150
- // the bundle version stamped into plugin.json — the monorepo root `spexcode` package version (the published
151
- // artifact's version), falling back to the spec-cli package, then 0.0.0.
152
- export function pluginVersion(): string {
153
- for (const p of [join(PKG, '..', 'package.json'), join(PKG, 'package.json')]) {
154
- try { const v = JSON.parse(readFileSync(p, 'utf8'))?.version; if (v) return String(v) } catch { /* keep trying */ }
155
- }
156
- return '0.0.0'
157
- }