spexcode 0.6.2 → 0.6.5

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 (479) hide show
  1. package/README.md +3 -1
  2. package/bin/spex.mjs +4 -0
  3. package/node_modules/@spexcode/spec-cli/bin/spex.mjs +76 -0
  4. package/node_modules/@spexcode/spec-cli/dist/attach.d.ts +2 -0
  5. package/node_modules/@spexcode/spec-cli/dist/attach.js +48 -0
  6. package/node_modules/@spexcode/spec-cli/dist/claude-headless.d.ts +33 -0
  7. package/node_modules/@spexcode/spec-cli/dist/claude-headless.js +307 -0
  8. package/node_modules/@spexcode/spec-cli/dist/cli.d.ts +1 -0
  9. package/node_modules/@spexcode/spec-cli/dist/cli.js +1859 -0
  10. package/node_modules/@spexcode/spec-cli/dist/client.d.ts +111 -0
  11. package/node_modules/@spexcode/spec-cli/dist/client.js +476 -0
  12. package/node_modules/@spexcode/spec-cli/dist/cockpit.d.ts +7 -0
  13. package/node_modules/@spexcode/spec-cli/dist/cockpit.js +26 -0
  14. package/node_modules/@spexcode/spec-cli/dist/codex-headless.d.ts +1 -0
  15. package/{spec-cli/src/codex-headless.ts → node_modules/@spexcode/spec-cli/dist/codex-headless.js} +3 -9
  16. package/node_modules/@spexcode/spec-cli/dist/codex-runtime-generations.d.ts +53 -0
  17. package/node_modules/@spexcode/spec-cli/dist/codex-runtime-generations.js +686 -0
  18. package/node_modules/@spexcode/spec-cli/dist/commit-surgery.d.ts +1 -0
  19. package/node_modules/@spexcode/spec-cli/dist/commit-surgery.js +90 -0
  20. package/node_modules/@spexcode/spec-cli/dist/contract-filter.d.ts +14 -0
  21. package/node_modules/@spexcode/spec-cli/dist/contract-filter.js +219 -0
  22. package/node_modules/@spexcode/spec-cli/dist/dashboard-assets.d.ts +6 -0
  23. package/node_modules/@spexcode/spec-cli/dist/dashboard-assets.js +50 -0
  24. package/node_modules/@spexcode/spec-cli/dist/delivery-queue.d.ts +23 -0
  25. package/node_modules/@spexcode/spec-cli/dist/delivery-queue.js +179 -0
  26. package/node_modules/@spexcode/spec-cli/dist/doctor.d.ts +9 -0
  27. package/node_modules/@spexcode/spec-cli/dist/doctor.js +547 -0
  28. package/node_modules/@spexcode/spec-cli/dist/eval-host.d.ts +1 -0
  29. package/node_modules/@spexcode/spec-cli/dist/eval-host.js +19 -0
  30. package/node_modules/@spexcode/spec-cli/dist/execution-trace.d.ts +26 -0
  31. package/node_modules/@spexcode/spec-cli/dist/execution-trace.js +475 -0
  32. package/node_modules/@spexcode/spec-cli/dist/file-write.d.ts +2 -0
  33. package/node_modules/@spexcode/spec-cli/dist/file-write.js +26 -0
  34. package/node_modules/@spexcode/spec-cli/dist/flat.d.ts +89 -0
  35. package/node_modules/@spexcode/spec-cli/dist/flat.js +1076 -0
  36. package/node_modules/@spexcode/spec-cli/dist/gateway-auth.d.ts +56 -0
  37. package/node_modules/@spexcode/spec-cli/dist/gateway-auth.js +168 -0
  38. package/node_modules/@spexcode/spec-cli/dist/gateway-hub.d.ts +28 -0
  39. package/node_modules/@spexcode/spec-cli/dist/gateway-hub.js +361 -0
  40. package/node_modules/@spexcode/spec-cli/dist/gateway.d.ts +37 -0
  41. package/node_modules/@spexcode/spec-cli/dist/gateway.js +503 -0
  42. package/node_modules/@spexcode/spec-cli/dist/graphCache.d.ts +27 -0
  43. package/node_modules/@spexcode/spec-cli/dist/graphCache.js +716 -0
  44. package/node_modules/@spexcode/spec-cli/dist/graphSnapshot.d.ts +14 -0
  45. package/node_modules/@spexcode/spec-cli/dist/graphSnapshot.js +35 -0
  46. package/node_modules/@spexcode/spec-cli/dist/graphStream.d.ts +85 -0
  47. package/node_modules/@spexcode/spec-cli/dist/graphStream.js +1195 -0
  48. package/node_modules/@spexcode/spec-cli/dist/guidance-catalog.d.ts +64 -0
  49. package/node_modules/@spexcode/spec-cli/dist/guidance-catalog.js +166 -0
  50. package/node_modules/@spexcode/spec-cli/dist/guide.d.ts +8 -0
  51. package/{spec-cli/src/guide.ts → node_modules/@spexcode/spec-cli/dist/guide.js} +36 -32
  52. package/node_modules/@spexcode/spec-cli/dist/harness-select.d.ts +18 -0
  53. package/node_modules/@spexcode/spec-cli/dist/harness-select.js +62 -0
  54. package/node_modules/@spexcode/spec-cli/dist/harness.d.ts +325 -0
  55. package/node_modules/@spexcode/spec-cli/dist/harness.js +3026 -0
  56. package/node_modules/@spexcode/spec-cli/dist/headless-controller.d.ts +9 -0
  57. package/node_modules/@spexcode/spec-cli/dist/headless-controller.js +47 -0
  58. package/node_modules/@spexcode/spec-cli/dist/help.d.ts +10 -0
  59. package/node_modules/@spexcode/spec-cli/dist/help.js +574 -0
  60. package/node_modules/@spexcode/spec-cli/dist/hook-prompts.d.ts +18 -0
  61. package/node_modules/@spexcode/spec-cli/dist/hook-prompts.js +122 -0
  62. package/node_modules/@spexcode/spec-cli/dist/hooks.d.ts +1 -0
  63. package/{spec-cli/src/hooks.ts → node_modules/@spexcode/spec-cli/dist/hooks.js} +15 -15
  64. package/node_modules/@spexcode/spec-cli/dist/host-resources.d.ts +97 -0
  65. package/node_modules/@spexcode/spec-cli/dist/host-resources.js +795 -0
  66. package/node_modules/@spexcode/spec-cli/dist/host.d.ts +88 -0
  67. package/node_modules/@spexcode/spec-cli/dist/host.js +663 -0
  68. package/node_modules/@spexcode/spec-cli/dist/index.d.ts +1 -0
  69. package/node_modules/@spexcode/spec-cli/dist/index.js +899 -0
  70. package/node_modules/@spexcode/spec-cli/dist/init.d.ts +1 -0
  71. package/node_modules/@spexcode/spec-cli/dist/init.js +284 -0
  72. package/node_modules/@spexcode/spec-cli/dist/issues-cli.d.ts +6 -0
  73. package/node_modules/@spexcode/spec-cli/dist/issues-cli.js +334 -0
  74. package/node_modules/@spexcode/spec-cli/dist/issues.d.ts +69 -0
  75. package/node_modules/@spexcode/spec-cli/dist/issues.js +189 -0
  76. package/node_modules/@spexcode/spec-cli/dist/lint.d.ts +34 -0
  77. package/node_modules/@spexcode/spec-cli/dist/lint.js +476 -0
  78. package/node_modules/@spexcode/spec-cli/dist/listen.d.ts +7 -0
  79. package/{spec-cli/src/listen.ts → node_modules/@spexcode/spec-cli/dist/listen.js} +13 -17
  80. package/node_modules/@spexcode/spec-cli/dist/localIssues.d.ts +65 -0
  81. package/node_modules/@spexcode/spec-cli/dist/localIssues.js +582 -0
  82. package/node_modules/@spexcode/spec-cli/dist/login-page.d.ts +5 -0
  83. package/{spec-cli/src/login-page.ts → node_modules/@spexcode/spec-cli/dist/login-page.js} +7 -7
  84. package/node_modules/@spexcode/spec-cli/dist/loop-in.d.ts +23 -0
  85. package/{spec-cli/src/loop-in.ts → node_modules/@spexcode/spec-cli/dist/loop-in.js} +35 -45
  86. package/node_modules/@spexcode/spec-cli/dist/machine-peer.d.ts +69 -0
  87. package/node_modules/@spexcode/spec-cli/dist/machine-peer.js +603 -0
  88. package/node_modules/@spexcode/spec-cli/dist/materialize.d.ts +18 -0
  89. package/node_modules/@spexcode/spec-cli/dist/materialize.js +468 -0
  90. package/node_modules/@spexcode/spec-cli/dist/mentions.d.ts +50 -0
  91. package/node_modules/@spexcode/spec-cli/dist/mentions.js +117 -0
  92. package/node_modules/@spexcode/spec-cli/dist/opencode-headless.d.ts +4 -0
  93. package/node_modules/@spexcode/spec-cli/dist/opencode-headless.js +208 -0
  94. package/node_modules/@spexcode/spec-cli/dist/opencode.d.ts +3 -0
  95. package/{spec-cli/src/opencode.ts → node_modules/@spexcode/spec-cli/dist/opencode.js} +9 -13
  96. package/node_modules/@spexcode/spec-cli/dist/pi-harness.d.ts +3 -0
  97. package/{spec-cli/src/pi-harness.ts → node_modules/@spexcode/spec-cli/dist/pi-harness.js} +45 -40
  98. package/node_modules/@spexcode/spec-cli/dist/pi-headless.d.ts +25 -0
  99. package/node_modules/@spexcode/spec-cli/dist/pi-headless.js +196 -0
  100. package/node_modules/@spexcode/spec-cli/dist/plugin-harness.d.ts +21 -0
  101. package/node_modules/@spexcode/spec-cli/dist/plugin-harness.js +146 -0
  102. package/node_modules/@spexcode/spec-cli/dist/pty-bridge.d.ts +10 -0
  103. package/node_modules/@spexcode/spec-cli/dist/pty-bridge.js +404 -0
  104. package/node_modules/@spexcode/spec-cli/dist/pty-helper.d.mts +1 -0
  105. package/node_modules/@spexcode/spec-cli/dist/pty-helper.mjs +101 -0
  106. package/node_modules/@spexcode/spec-cli/dist/pty-native-helper.d.mts +2 -0
  107. package/node_modules/@spexcode/spec-cli/dist/pty-native-helper.mjs +23 -0
  108. package/node_modules/@spexcode/spec-cli/dist/public-graph.d.ts +46 -0
  109. package/node_modules/@spexcode/spec-cli/dist/public-graph.js +56 -0
  110. package/node_modules/@spexcode/spec-cli/dist/ranker.d.ts +15 -0
  111. package/node_modules/@spexcode/spec-cli/dist/ranker.js +188 -0
  112. package/node_modules/@spexcode/spec-cli/dist/reaper.d.ts +6 -0
  113. package/node_modules/@spexcode/spec-cli/dist/reaper.js +81 -0
  114. package/node_modules/@spexcode/spec-cli/dist/reviews.d.ts +189 -0
  115. package/node_modules/@spexcode/spec-cli/dist/reviews.js +350 -0
  116. package/node_modules/@spexcode/spec-cli/dist/runtime-guard.d.ts +6 -0
  117. package/node_modules/@spexcode/spec-cli/dist/runtime-guard.js +37 -0
  118. package/node_modules/@spexcode/spec-cli/dist/runtime-ownership.d.ts +21 -0
  119. package/node_modules/@spexcode/spec-cli/dist/runtime-ownership.js +84 -0
  120. package/node_modules/@spexcode/spec-cli/dist/search.bench.d.mts +1 -0
  121. package/node_modules/@spexcode/spec-cli/dist/search.bench.mjs +75 -0
  122. package/node_modules/@spexcode/spec-cli/dist/search.d.ts +20 -0
  123. package/node_modules/@spexcode/spec-cli/dist/search.js +61 -0
  124. package/node_modules/@spexcode/spec-cli/dist/session-cursors.d.ts +14 -0
  125. package/node_modules/@spexcode/spec-cli/dist/session-cursors.js +82 -0
  126. package/node_modules/@spexcode/spec-cli/dist/session-declarations.d.ts +13 -0
  127. package/node_modules/@spexcode/spec-cli/dist/session-declarations.js +126 -0
  128. package/node_modules/@spexcode/spec-cli/dist/session-execution.d.ts +10 -0
  129. package/node_modules/@spexcode/spec-cli/dist/session-execution.js +70 -0
  130. package/node_modules/@spexcode/spec-cli/dist/session-files.d.ts +28 -0
  131. package/node_modules/@spexcode/spec-cli/dist/session-files.js +130 -0
  132. package/node_modules/@spexcode/spec-cli/dist/session-follow.d.ts +31 -0
  133. package/node_modules/@spexcode/spec-cli/dist/session-follow.js +179 -0
  134. package/node_modules/@spexcode/spec-cli/dist/session-reparent.d.ts +10 -0
  135. package/node_modules/@spexcode/spec-cli/dist/session-reparent.js +33 -0
  136. package/node_modules/@spexcode/spec-cli/dist/session-timeline.d.ts +51 -0
  137. package/node_modules/@spexcode/spec-cli/dist/session-timeline.js +233 -0
  138. package/node_modules/@spexcode/spec-cli/dist/session-web.d.ts +24 -0
  139. package/node_modules/@spexcode/spec-cli/dist/session-web.js +141 -0
  140. package/node_modules/@spexcode/spec-cli/dist/sessions.d.ts +412 -0
  141. package/node_modules/@spexcode/spec-cli/dist/sessions.js +4239 -0
  142. package/node_modules/@spexcode/spec-cli/dist/sh.d.ts +1 -0
  143. package/node_modules/@spexcode/spec-cli/dist/sh.js +3 -0
  144. package/node_modules/@spexcode/spec-cli/dist/shim-runtime.d.ts +1 -0
  145. package/{spec-cli/src/shim-runtime.ts → node_modules/@spexcode/spec-cli/dist/shim-runtime.js} +3 -4
  146. package/node_modules/@spexcode/spec-cli/dist/slash-commands.d.ts +9 -0
  147. package/node_modules/@spexcode/spec-cli/dist/slash-commands.js +307 -0
  148. package/node_modules/@spexcode/spec-cli/dist/source-files.d.ts +9 -0
  149. package/node_modules/@spexcode/spec-cli/dist/source-files.js +113 -0
  150. package/node_modules/@spexcode/spec-cli/dist/supervise.d.ts +1 -0
  151. package/node_modules/@spexcode/spec-cli/dist/supervise.js +308 -0
  152. package/node_modules/@spexcode/spec-cli/dist/tree.d.ts +25 -0
  153. package/node_modules/@spexcode/spec-cli/dist/tree.js +98 -0
  154. package/node_modules/@spexcode/spec-cli/dist/tsx-bin.d.ts +3 -0
  155. package/node_modules/@spexcode/spec-cli/dist/tsx-bin.js +41 -0
  156. package/node_modules/@spexcode/spec-cli/dist/uninstall.d.ts +3 -0
  157. package/node_modules/@spexcode/spec-cli/dist/uninstall.js +177 -0
  158. package/node_modules/@spexcode/spec-cli/dist/uploads.d.ts +36 -0
  159. package/node_modules/@spexcode/spec-cli/dist/uploads.js +243 -0
  160. package/node_modules/@spexcode/spec-cli/dist/worktree-sources.d.ts +1 -0
  161. package/node_modules/@spexcode/spec-cli/dist/worktree-sources.js +47 -0
  162. package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/dispatch.sh +5 -5
  163. package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/harness.sh +1 -1
  164. package/node_modules/@spexcode/spec-cli/package.json +47 -0
  165. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/commands/supervisor/spec.md +8 -0
  166. package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-first/spec-first.sh +1 -1
  167. package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-of-file/spec-of-file.sh +1 -0
  168. package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/stop-gate/spec.md +2 -2
  169. package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/stop-gate/stop-gate.sh +31 -29
  170. package/node_modules/@spexcode/spec-core/dist/anchors.d.ts +94 -0
  171. package/node_modules/@spexcode/spec-core/dist/anchors.js +730 -0
  172. package/node_modules/@spexcode/spec-core/dist/git.d.ts +166 -0
  173. package/node_modules/@spexcode/spec-core/dist/git.js +2736 -0
  174. package/node_modules/@spexcode/spec-core/dist/graph-delta.d.ts +44 -0
  175. package/node_modules/@spexcode/spec-core/dist/graph-delta.js +72 -0
  176. package/node_modules/@spexcode/spec-core/dist/graph.d.ts +34 -0
  177. package/node_modules/@spexcode/spec-core/dist/graph.js +237 -0
  178. package/node_modules/@spexcode/spec-core/dist/graphDelta.d.ts +3 -0
  179. package/node_modules/@spexcode/spec-core/dist/graphDelta.js +13 -0
  180. package/node_modules/@spexcode/spec-core/dist/harness-identity.d.ts +31 -0
  181. package/node_modules/@spexcode/spec-core/dist/harness-identity.js +20 -0
  182. package/node_modules/@spexcode/spec-core/dist/identity-presets.d.ts +152 -0
  183. package/node_modules/@spexcode/spec-core/dist/identity-presets.js +132 -0
  184. package/node_modules/@spexcode/spec-core/dist/index.d.ts +45 -0
  185. package/node_modules/@spexcode/spec-core/dist/index.js +19 -0
  186. package/node_modules/@spexcode/spec-core/dist/layout.d.ts +183 -0
  187. package/node_modules/@spexcode/spec-core/dist/layout.js +548 -0
  188. package/node_modules/@spexcode/spec-core/dist/process-identity.d.ts +37 -0
  189. package/node_modules/@spexcode/spec-core/dist/process-identity.js +214 -0
  190. package/node_modules/@spexcode/spec-core/dist/project-identity.d.ts +12 -0
  191. package/node_modules/@spexcode/spec-core/dist/project-identity.js +71 -0
  192. package/node_modules/@spexcode/spec-core/dist/project-store.d.ts +3 -0
  193. package/node_modules/@spexcode/spec-core/dist/project-store.js +14 -0
  194. package/node_modules/@spexcode/spec-core/dist/resilience.d.ts +2 -0
  195. package/node_modules/@spexcode/spec-core/dist/resilience.js +40 -0
  196. package/node_modules/@spexcode/spec-core/dist/review/index.d.ts +3 -0
  197. package/node_modules/@spexcode/spec-core/dist/review/index.js +5 -0
  198. package/node_modules/@spexcode/spec-core/dist/review/reviewFilters.d.ts +77 -0
  199. package/node_modules/@spexcode/spec-core/dist/review/reviewFilters.js +308 -0
  200. package/node_modules/@spexcode/spec-core/dist/review/reviewQuery.d.ts +66 -0
  201. package/node_modules/@spexcode/spec-core/dist/review/reviewQuery.js +180 -0
  202. package/node_modules/@spexcode/spec-core/dist/review/session.d.ts +4 -0
  203. package/node_modules/@spexcode/spec-core/dist/review/session.js +8 -0
  204. package/node_modules/@spexcode/spec-core/dist/reviewSnapshot.d.ts +15 -0
  205. package/node_modules/@spexcode/spec-core/dist/reviewSnapshot.js +12 -0
  206. package/node_modules/@spexcode/spec-core/dist/root-lru.d.ts +4 -0
  207. package/{spec-cli/src/root-lru.ts → node_modules/@spexcode/spec-core/dist/root-lru.js} +26 -30
  208. package/node_modules/@spexcode/spec-core/dist/specs.d.ts +117 -0
  209. package/node_modules/@spexcode/spec-core/dist/specs.js +489 -0
  210. package/node_modules/@spexcode/spec-core/package.json +28 -0
  211. package/node_modules/@spexcode/spec-eval/dist/cache.d.ts +10 -0
  212. package/node_modules/@spexcode/spec-eval/dist/cache.js +50 -0
  213. package/node_modules/@spexcode/spec-eval/dist/cli.d.ts +22 -0
  214. package/node_modules/@spexcode/spec-eval/dist/cli.js +981 -0
  215. package/node_modules/@spexcode/spec-eval/dist/evaltab.d.ts +98 -0
  216. package/node_modules/@spexcode/spec-eval/dist/evaltab.js +176 -0
  217. package/node_modules/@spexcode/spec-eval/dist/filing.d.ts +16 -0
  218. package/node_modules/@spexcode/spec-eval/dist/filing.js +36 -0
  219. package/node_modules/@spexcode/spec-eval/dist/freshness.d.ts +48 -0
  220. package/node_modules/@spexcode/spec-eval/dist/freshness.js +799 -0
  221. package/node_modules/@spexcode/spec-eval/dist/host.d.ts +46 -0
  222. package/node_modules/@spexcode/spec-eval/dist/host.js +53 -0
  223. package/node_modules/@spexcode/spec-eval/dist/humanok.d.ts +11 -0
  224. package/node_modules/@spexcode/spec-eval/dist/humanok.js +27 -0
  225. package/node_modules/@spexcode/spec-eval/dist/index.d.ts +4 -0
  226. package/node_modules/@spexcode/spec-eval/dist/index.js +4 -0
  227. package/node_modules/@spexcode/spec-eval/dist/remarks.d.ts +31 -0
  228. package/node_modules/@spexcode/spec-eval/dist/remarks.js +1 -0
  229. package/node_modules/@spexcode/spec-eval/dist/scenariofresh.d.ts +13 -0
  230. package/node_modules/@spexcode/spec-eval/dist/scenariofresh.js +303 -0
  231. package/node_modules/@spexcode/spec-eval/dist/scenarios.d.ts +99 -0
  232. package/node_modules/@spexcode/spec-eval/dist/scenarios.js +650 -0
  233. package/node_modules/@spexcode/spec-eval/dist/sessioneval.d.ts +307 -0
  234. package/node_modules/@spexcode/spec-eval/dist/sessioneval.js +1883 -0
  235. package/node_modules/@spexcode/spec-eval/dist/sidecar.d.ts +55 -0
  236. package/node_modules/@spexcode/spec-eval/dist/sidecar.js +82 -0
  237. package/node_modules/@spexcode/spec-eval/dist/timeline.d.ts +25 -0
  238. package/node_modules/@spexcode/spec-eval/dist/timeline.js +65 -0
  239. package/node_modules/@spexcode/spec-eval/dist/ui-path.d.ts +1 -0
  240. package/node_modules/@spexcode/spec-eval/dist/ui-path.js +2 -0
  241. package/node_modules/@spexcode/spec-eval/package.json +36 -0
  242. package/node_modules/@spexcode/spec-forge/dist/cache.d.ts +29 -0
  243. package/node_modules/@spexcode/spec-forge/dist/cache.js +59 -0
  244. package/node_modules/@spexcode/spec-forge/dist/cli.d.ts +1 -0
  245. package/node_modules/@spexcode/spec-forge/dist/cli.js +100 -0
  246. package/node_modules/@spexcode/spec-forge/dist/drivers/github.d.ts +2 -0
  247. package/node_modules/@spexcode/spec-forge/dist/drivers/github.js +116 -0
  248. package/node_modules/@spexcode/spec-forge/dist/drivers/gitlab.d.ts +7 -0
  249. package/node_modules/@spexcode/spec-forge/dist/drivers/gitlab.js +144 -0
  250. package/node_modules/@spexcode/spec-forge/dist/drivers.d.ts +10 -0
  251. package/node_modules/@spexcode/spec-forge/dist/drivers.js +83 -0
  252. package/node_modules/@spexcode/spec-forge/dist/index.d.ts +4 -0
  253. package/node_modules/@spexcode/spec-forge/dist/index.js +4 -0
  254. package/node_modules/@spexcode/spec-forge/dist/links.d.ts +15 -0
  255. package/node_modules/@spexcode/spec-forge/dist/links.js +65 -0
  256. package/node_modules/@spexcode/spec-forge/dist/needs-eval.d.ts +10 -0
  257. package/node_modules/@spexcode/spec-forge/dist/needs-eval.js +20 -0
  258. package/node_modules/@spexcode/spec-forge/dist/port.d.ts +53 -0
  259. package/node_modules/@spexcode/spec-forge/dist/port.js +1 -0
  260. package/node_modules/@spexcode/spec-forge/dist/resident.d.ts +7 -0
  261. package/node_modules/@spexcode/spec-forge/dist/resident.js +43 -0
  262. package/node_modules/@spexcode/spec-forge/package.json +32 -0
  263. package/package.json +33 -19
  264. package/spec-cli/bin/spex.mjs +0 -69
  265. package/spec-cli/src/anchors.ts +0 -728
  266. package/spec-cli/src/attach.ts +0 -43
  267. package/spec-cli/src/claude-headless.ts +0 -321
  268. package/spec-cli/src/cli.ts +0 -1258
  269. package/spec-cli/src/client.ts +0 -414
  270. package/spec-cli/src/cockpit.ts +0 -43
  271. package/spec-cli/src/codex-runtime-generations.ts +0 -646
  272. package/spec-cli/src/commit-surgery.ts +0 -68
  273. package/spec-cli/src/contract-filter.ts +0 -202
  274. package/spec-cli/src/delivery-queue.ts +0 -165
  275. package/spec-cli/src/doctor.ts +0 -516
  276. package/spec-cli/src/execution-trace.ts +0 -444
  277. package/spec-cli/src/file-write.ts +0 -22
  278. package/spec-cli/src/gateway-auth.ts +0 -181
  279. package/spec-cli/src/gateway-hub.ts +0 -358
  280. package/spec-cli/src/gateway.ts +0 -461
  281. package/spec-cli/src/git.ts +0 -2556
  282. package/spec-cli/src/graph.ts +0 -271
  283. package/spec-cli/src/graphCache.ts +0 -710
  284. package/spec-cli/src/graphDelta.ts +0 -77
  285. package/spec-cli/src/graphStream.ts +0 -1019
  286. package/spec-cli/src/harness-select.ts +0 -77
  287. package/spec-cli/src/harness.ts +0 -3025
  288. package/spec-cli/src/headless-controller.ts +0 -63
  289. package/spec-cli/src/help.ts +0 -492
  290. package/spec-cli/src/host-resources.ts +0 -762
  291. package/spec-cli/src/host.ts +0 -611
  292. package/spec-cli/src/identity-presets.d.ts +0 -13
  293. package/spec-cli/src/identity-presets.js +0 -138
  294. package/spec-cli/src/index.ts +0 -818
  295. package/spec-cli/src/init.ts +0 -258
  296. package/spec-cli/src/issues-cli.ts +0 -276
  297. package/spec-cli/src/issues.ts +0 -237
  298. package/spec-cli/src/layout.ts +0 -640
  299. package/spec-cli/src/lint.ts +0 -488
  300. package/spec-cli/src/localIssues.ts +0 -538
  301. package/spec-cli/src/materialize.ts +0 -417
  302. package/spec-cli/src/mentions.ts +0 -81
  303. package/spec-cli/src/opencode-headless.ts +0 -205
  304. package/spec-cli/src/pi-headless.ts +0 -153
  305. package/spec-cli/src/plugin-harness.ts +0 -157
  306. package/spec-cli/src/process-identity.ts +0 -207
  307. package/spec-cli/src/project-identity.ts +0 -73
  308. package/spec-cli/src/project-store.ts +0 -17
  309. package/spec-cli/src/pty-bridge.ts +0 -396
  310. package/spec-cli/src/pty-helper.mjs +0 -96
  311. package/spec-cli/src/pty-native-helper.mjs +0 -22
  312. package/spec-cli/src/ranker.ts +0 -191
  313. package/spec-cli/src/reaper.ts +0 -125
  314. package/spec-cli/src/resilience.ts +0 -41
  315. package/spec-cli/src/reviewSnapshot.ts +0 -28
  316. package/spec-cli/src/reviews.ts +0 -434
  317. package/spec-cli/src/runtime-guard.ts +0 -35
  318. package/spec-cli/src/runtime-ownership.ts +0 -88
  319. package/spec-cli/src/search.bench.mjs +0 -80
  320. package/spec-cli/src/search.ts +0 -63
  321. package/spec-cli/src/session-cursors.ts +0 -91
  322. package/spec-cli/src/session-execution.ts +0 -68
  323. package/spec-cli/src/session-files.ts +0 -121
  324. package/spec-cli/src/session-follow.ts +0 -182
  325. package/spec-cli/src/session-reparent.ts +0 -33
  326. package/spec-cli/src/session-timeline.ts +0 -247
  327. package/spec-cli/src/session-web.ts +0 -135
  328. package/spec-cli/src/sessions.ts +0 -3883
  329. package/spec-cli/src/sh.ts +0 -3
  330. package/spec-cli/src/slash-commands.ts +0 -318
  331. package/spec-cli/src/source-files.ts +0 -104
  332. package/spec-cli/src/specs.ts +0 -498
  333. package/spec-cli/src/supervise.ts +0 -245
  334. package/spec-cli/src/tree.ts +0 -118
  335. package/spec-cli/src/tsx-bin.ts +0 -18
  336. package/spec-cli/src/uninstall.ts +0 -171
  337. package/spec-cli/src/uploads.ts +0 -253
  338. package/spec-cli/src/worktree-sources.ts +0 -42
  339. package/spec-cli/templates/spec/project/.plugins/commands/supervisor/spec.md +0 -8
  340. package/spec-dashboard/dist/assets/App-B6IbFbGu.js +0 -2
  341. package/spec-dashboard/dist/assets/Dashboard-C5ap-Sga.css +0 -1
  342. package/spec-dashboard/dist/assets/Dashboard-_sDk7Rcg.js +0 -27
  343. package/spec-dashboard/dist/assets/EvalsPage-BT357Lyt.js +0 -2
  344. package/spec-dashboard/dist/assets/IssuesPage-BG9rNOgV.js +0 -1
  345. package/spec-dashboard/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  346. package/spec-dashboard/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  347. package/spec-dashboard/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  348. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  349. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  350. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  351. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  352. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  353. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  354. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  355. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  356. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  357. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  358. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  359. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  360. package/spec-dashboard/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  361. package/spec-dashboard/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  362. package/spec-dashboard/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  363. package/spec-dashboard/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  364. package/spec-dashboard/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  365. package/spec-dashboard/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  366. package/spec-dashboard/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  367. package/spec-dashboard/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  368. package/spec-dashboard/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  369. package/spec-dashboard/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  370. package/spec-dashboard/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  371. package/spec-dashboard/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  372. package/spec-dashboard/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  373. package/spec-dashboard/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  374. package/spec-dashboard/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  375. package/spec-dashboard/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  376. package/spec-dashboard/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  377. package/spec-dashboard/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  378. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  379. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  380. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  381. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  382. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  383. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  384. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  385. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  386. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  387. package/spec-dashboard/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  388. package/spec-dashboard/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  389. package/spec-dashboard/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  390. package/spec-dashboard/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  391. package/spec-dashboard/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  392. package/spec-dashboard/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  393. package/spec-dashboard/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  394. package/spec-dashboard/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  395. package/spec-dashboard/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  396. package/spec-dashboard/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  397. package/spec-dashboard/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  398. package/spec-dashboard/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  399. package/spec-dashboard/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  400. package/spec-dashboard/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  401. package/spec-dashboard/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  402. package/spec-dashboard/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  403. package/spec-dashboard/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  404. package/spec-dashboard/dist/assets/MobileApp-D9yPMR3A.js +0 -2
  405. package/spec-dashboard/dist/assets/Modal-B3EcgeA3.js +0 -1
  406. package/spec-dashboard/dist/assets/PageScroll-CNZOugWV.js +0 -1
  407. package/spec-dashboard/dist/assets/ProjectsPage-C2CRHmvs.js +0 -1
  408. package/spec-dashboard/dist/assets/SessionInterface-6GBZ9nXN.css +0 -32
  409. package/spec-dashboard/dist/assets/SessionInterface-DOEjuT3l.js +0 -40
  410. package/spec-dashboard/dist/assets/SessionWindow-1nYgZ4CL.js +0 -1
  411. package/spec-dashboard/dist/assets/Settings-7WeTJn0V.js +0 -1
  412. package/spec-dashboard/dist/assets/Thread-CEbzAFQn.js +0 -13
  413. package/spec-dashboard/dist/assets/bindings-BC9vqpYU.js +0 -1
  414. package/spec-dashboard/dist/assets/data-SlvxNA2r.js +0 -1
  415. package/spec-dashboard/dist/assets/index-DLVeOHL-.js +0 -41
  416. package/spec-dashboard/dist/assets/index-DYLGfPjx.css +0 -1
  417. package/spec-dashboard/dist/assets/launch-Cp579UoJ.css +0 -1
  418. package/spec-dashboard/dist/assets/launch-yxCYr64x.js +0 -276
  419. package/spec-dashboard/dist/assets/sessionSurface--7l69h4F.js +0 -1
  420. package/spec-dashboard/dist/index.html +0 -29
  421. package/spec-dashboard/src/reviewFilters.js +0 -322
  422. package/spec-dashboard/src/reviewQuery.js +0 -174
  423. package/spec-dashboard/src/session.js +0 -199
  424. package/spec-eval/src/cache.ts +0 -57
  425. package/spec-eval/src/cli.ts +0 -894
  426. package/spec-eval/src/evaltab.ts +0 -264
  427. package/spec-eval/src/filing.ts +0 -39
  428. package/spec-eval/src/freshness.ts +0 -836
  429. package/spec-eval/src/humanok.ts +0 -28
  430. package/spec-eval/src/scenariofresh.ts +0 -244
  431. package/spec-eval/src/scenarios.ts +0 -672
  432. package/spec-eval/src/sessioneval.ts +0 -2220
  433. package/spec-eval/src/sidecar.ts +0 -93
  434. package/spec-eval/src/timeline.ts +0 -54
  435. package/spec-forge/src/__fixtures__/github-forge.json +0 -63
  436. package/spec-forge/src/cache.ts +0 -66
  437. package/spec-forge/src/cli.ts +0 -99
  438. package/spec-forge/src/drivers/github.ts +0 -132
  439. package/spec-forge/src/drivers/gitlab.ts +0 -153
  440. package/spec-forge/src/drivers.ts +0 -73
  441. package/spec-forge/src/links.ts +0 -72
  442. package/spec-forge/src/needs-eval.ts +0 -29
  443. package/spec-forge/src/port.ts +0 -36
  444. package/spec-forge/src/resident.ts +0 -46
  445. /package/{spec-cli → node_modules/@spexcode/spec-cli}/README.md +0 -0
  446. /package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/compat/mark-active-0.5.2-eef1.fixture +0 -0
  447. /package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/compat/mark-active-sed-v0.fixture +0 -0
  448. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/commit-msg +0 -0
  449. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/post-checkout +0 -0
  450. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/post-merge +0 -0
  451. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/pre-commit +0 -0
  452. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/prepare-commit-msg +0 -0
  453. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/reference-transaction +0 -0
  454. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/extract/spec.md +0 -0
  455. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/regroup/spec.md +0 -0
  456. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/rename/spec.md +0 -0
  457. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/spec.md +0 -0
  458. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/tidy/spec.md +0 -0
  459. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/comment-altitude/spec.md +0 -0
  460. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/idle/idle.sh +0 -0
  461. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/idle/spec.md +0 -0
  462. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/mark-active/mark-active.sh +0 -0
  463. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/mark-active/spec.md +0 -0
  464. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/session-fail/fail.sh +0 -0
  465. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/session-fail/spec.md +0 -0
  466. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-first/spec.md +0 -0
  467. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-of-file/spec.md +0 -0
  468. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec.md +0 -0
  469. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/atomic-landing/spec.md +0 -0
  470. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/forge-link/spec.md +0 -0
  471. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/memory-hygiene/spec.md +0 -0
  472. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/reproduce-before-fix/spec.md +0 -0
  473. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/spec.md +0 -0
  474. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/skills/distill/digest.mjs +0 -0
  475. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/skills/distill/spec.md +0 -0
  476. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/skills/spec.md +0 -0
  477. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/spec.md +0 -0
  478. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/spec.md +0 -0
  479. /package/{spec-cli → node_modules/@spexcode/spec-core}/templates/spexcode.json +0 -0
@@ -0,0 +1 @@
1
+ export declare function specInit(targetArg: string | undefined, presetArg?: string, harnessArg?: string): Promise<void>;
@@ -0,0 +1,284 @@
1
+ import { existsSync, mkdirSync, copyFileSync, readFileSync, readdirSync, renameSync, rmSync, chmodSync, writeFileSync } from 'node:fs';
2
+ import { join, resolve, relative, dirname } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { execFileSync } from 'node:child_process';
5
+ import { gitBinary, parseFrontmatter, readConfig, readJsonConfig, templateConfigPath } from '@spexcode/spec-core';
6
+ import { resolveHarnessTargets, partitionHarnesses, parseHarnessFlag, NATIVE_HARNESS_IDS } from './harness-select.js';
7
+ // this file lives at <pkgRoot>/src/init.ts, so `..` is the package root — the same derivation the
8
+ // launch paths use, never a hardcoded repo path (so a relocated/installed package still finds its data).
9
+ const pkgRoot = fileURLToPath(new URL('..', import.meta.url));
10
+ const TEMPLATES = join(pkgRoot, 'templates');
11
+ const MANAGED_HOOK_HEADER = '# spexcode-managed-hook-v1';
12
+ function isManagedHook(path) {
13
+ const source = readFileSync(path, 'utf8');
14
+ return source.split('\n').slice(0, 4).includes(MANAGED_HOOK_HEADER);
15
+ }
16
+ function replaceHook(source, dest) {
17
+ const temp = `${dest}.spexcode-${process.pid}`;
18
+ try {
19
+ copyFileSync(source, temp);
20
+ chmodSync(temp, 0o755);
21
+ renameSync(temp, dest);
22
+ }
23
+ finally {
24
+ rmSync(temp, { force: true });
25
+ }
26
+ }
27
+ // the cumulative preset chain, lean → cautious (see [[init-preset]]). `default` is the live `.plugins`
28
+ // instance set (planted from templates/spec); a higher tier would be a SEPARATE package under
29
+ // templates/presets/<tier>/ that seeding stacks ON TOP — a superset. No non-default tier ships today
30
+ // (the `careful` package was retired); the chain mechanism stays for when one earns its keep.
31
+ // Selection matters ONLY here at seed time; the running repo just
32
+ // walks whatever `.plugins` ended up planted, so there is no launcher-side preset gate.
33
+ const PRESET_TIERS = ['default'];
34
+ const presetRank = (name) => PRESET_TIERS.indexOf(name);
35
+ // recursively copy srcDir -> destDir, NEVER overwriting an existing file. Returns the repo-relative
36
+ // paths of files actually written (so the caller can report exactly what was planted vs already there).
37
+ function copyTreeNoClobber(srcDir, destDir, base, includeDir = () => true) {
38
+ if (!includeDir(srcDir))
39
+ return [];
40
+ const written = [];
41
+ mkdirSync(destDir, { recursive: true });
42
+ for (const e of readdirSync(srcDir, { withFileTypes: true })) {
43
+ const src = join(srcDir, e.name);
44
+ const dest = join(destDir, e.name);
45
+ if (e.isDirectory()) {
46
+ written.push(...copyTreeNoClobber(src, dest, base, includeDir));
47
+ }
48
+ else if (existsSync(dest)) {
49
+ // additive only — a pre-existing file is the user's, leave it untouched.
50
+ continue;
51
+ }
52
+ else {
53
+ copyFileSync(src, dest);
54
+ written.push(relative(base, dest));
55
+ }
56
+ }
57
+ return written;
58
+ }
59
+ function seedableForEvents(dir, reachableEvents) {
60
+ const spec = join(dir, 'spec.md');
61
+ if (!existsSync(spec))
62
+ return true;
63
+ const { fm } = parseFrontmatter(readFileSync(spec, 'utf8'));
64
+ const values = (value) => Array.isArray(value) ? value : value ? [value] : [];
65
+ const surfaces = values(fm.surface).flatMap((value) => value.split(',')).map((value) => value.trim());
66
+ if (!surfaces.includes('hook'))
67
+ return true;
68
+ const events = values(fm.events).flatMap((value) => value.split(',')).map((value) => value.trim()).filter(Boolean);
69
+ return !events.length || events.some((event) => reachableEvents.has(event));
70
+ }
71
+ // resolve the target repo's git hooks dir the same way scripts/install-hooks.sh does — the COMMON dir's
72
+ // hooks/ (shared across all worktrees). Returns null (with a loud reason) when <dir> isn't a git repo.
73
+ function resolveHooksDir(dir) {
74
+ try {
75
+ const common = execFileSync(gitBinary(process.env), ['-C', dir, 'rev-parse', '--path-format=absolute', '--git-common-dir'], {
76
+ encoding: 'utf8',
77
+ stdio: ['ignore', 'pipe', 'ignore'], // swallow git's own "fatal: not a git repository" — our warning is the signal
78
+ }).trim();
79
+ return join(common, 'hooks');
80
+ }
81
+ catch {
82
+ return null;
83
+ }
84
+ }
85
+ // Detect once, at adoption, while the root checkout still names the branch being adopted. Steady-state
86
+ // layout never re-derives trunk identity from a mutable checkout (an ordinary `git switch node/x` must not
87
+ // turn that feature branch into trunk).
88
+ function adoptionMainBranch(dir) {
89
+ try {
90
+ const common = execFileSync(gitBinary(process.env), ['-C', dir, 'rev-parse', '--path-format=absolute', '--git-common-dir'], { encoding: 'utf8' }).trim();
91
+ const branch = execFileSync(gitBinary(process.env), ['-C', dirname(common), 'symbolic-ref', '--short', 'HEAD'], { encoding: 'utf8' }).trim();
92
+ if (branch)
93
+ return branch;
94
+ }
95
+ catch { /* render the one product-level repair below */ }
96
+ const error = new Error('cannot determine the source-of-truth branch at adoption — check out the trunk in the root checkout, or set "mainBranch" in spexcode.json before re-running `spex init`');
97
+ error.name = 'ConfigError';
98
+ throw error;
99
+ }
100
+ export async function specInit(targetArg, presetArg, harnessArg) {
101
+ const targetDir = resolve(targetArg ?? process.cwd());
102
+ // the preset the NEW adopter gets — `--preset <name>` wins, else an existing target spexcode.json's
103
+ // `preset` field, else the lean `default`. Validated loudly against the chain (an unknown name would
104
+ // otherwise seed silently). A non-default tier stacks its template package on top of the default set below.
105
+ const selected = (presetArg ?? '').trim() || (readConfig(targetDir).preset ?? '').trim() || 'default';
106
+ if (!PRESET_TIERS.includes(selected)) {
107
+ console.error(`spex init: unknown preset '${selected}'. Valid presets (cumulative, lean→cautious): ${PRESET_TIERS.join(', ')}.`);
108
+ process.exit(1);
109
+ }
110
+ // SpexCode is git-backed: git IS the version database and the hooks live in `.git`. A non-git target
111
+ // would leave a HALF-STATE — specs on disk but no version history, no hooks, no harness shims, no
112
+ // sessions. So fail LOUD before writing anything, rather than seeding debris and warning past it. We do
113
+ // NOT `git init` for them: creating a repo is a side effect beyond init's remit (a subdir, a dir not
114
+ // meant as a repo root), and `git init` is one deliberate command.
115
+ try {
116
+ execFileSync(gitBinary(process.env), ['-C', targetDir, 'rev-parse', '--is-inside-work-tree'], { stdio: ['ignore', 'ignore', 'ignore'] });
117
+ }
118
+ catch {
119
+ console.error(`spex init: ${targetDir} is not a git repository. SpexCode is git-backed (git is the version database; the hooks live in .git). Run \`git init\` there first, then \`spex init\`.`);
120
+ process.exit(1);
121
+ }
122
+ // the harness DELIVERY TARGET set ([[harness-select]]) is a REQUIRED, explicit choice — `--harness <ids>`
123
+ // stamps it into spexcode.json; absent the flag, a pre-existing explicit `harnesses` field IS the choice.
124
+ // Neither → abort BEFORE writing anything: with many harnesses, a silent "deliver to all" would litter the
125
+ // adopter's tree (and global tool configs) with artifacts for harnesses they never installed. Legality
126
+ // (unknown ids, plugin exclusivity, empty set) fails loud here too, not as a soft materialize warning.
127
+ const flagRaw = (harnessArg ?? '').trim() ? parseHarnessFlag(harnessArg.trim()) : null;
128
+ const chosenHarnesses = flagRaw ?? readConfig(targetDir).harnesses ?? null;
129
+ if (chosenHarnesses === null) {
130
+ console.error(`spex init: --harness is required — name the harness(es) this repo delivers into, e.g. \`spex init --harness claude\`. Known native ids: ${NATIVE_HARNESS_IDS.join(', ')} (comma-separate several; a plugin bundle: --harness plugin:<folder>). A pre-existing spexcode.json "harnesses" field also satisfies this.`);
131
+ process.exit(1);
132
+ }
133
+ let selectedNativeEvents = null;
134
+ try {
135
+ const targets = resolveHarnessTargets(chosenHarnesses);
136
+ const native = partitionHarnesses(targets).selected;
137
+ if (native.length)
138
+ selectedNativeEvents = new Set(native.flatMap((h) => h.events));
139
+ }
140
+ catch (e) {
141
+ console.error(`spex init: ${e.message}`);
142
+ process.exit(1);
143
+ }
144
+ if (!existsSync(targetDir))
145
+ mkdirSync(targetDir, { recursive: true });
146
+ console.log(`spex init → ${targetDir}`);
147
+ // 1. seed the spec tree: templates/spec/* -> <dir>/.spec/* (an existing .spec aborts this phase loudly).
148
+ const specDest = join(targetDir, '.spec');
149
+ if (existsSync(specDest)) {
150
+ console.warn(`• .spec already exists at ${specDest} — skipping spec scaffold (won't overwrite an existing tree).`);
151
+ }
152
+ else {
153
+ const includeSeedDir = (dir) => selectedNativeEvents === null || seedableForEvents(dir, selectedNativeEvents);
154
+ const planted = copyTreeNoClobber(join(TEMPLATES, 'spec'), specDest, targetDir, includeSeedDir);
155
+ console.log(`✓ seeded ${planted.length} spec file(s) under .spec/ (root 'project' node + default .plugins)`);
156
+ // 1a. stack the selected preset's package(s) ON TOP of the default set — cumulative, so every tier from
157
+ // just above `default` up to the selection is planted. Each lives under templates/presets/<tier>/ mirroring
158
+ // the default layout (its `.plugins/<plugin>` lands in the seeded project node's `.plugins`). See [[init-preset]].
159
+ for (let r = 1; r <= presetRank(selected); r++) {
160
+ const tier = PRESET_TIERS[r];
161
+ const pkg = join(TEMPLATES, 'presets', tier);
162
+ if (!existsSync(pkg)) {
163
+ console.warn(`• preset '${tier}' has no package at ${pkg} — skipped.`);
164
+ continue;
165
+ }
166
+ const added = copyTreeNoClobber(pkg, join(specDest, 'project'), targetDir, includeSeedDir);
167
+ console.log(`✓ seeded preset '${tier}' (${added.length} file(s)) into .plugins`);
168
+ }
169
+ }
170
+ // 1b. plant a starter spexcode.json (the lint/layout knob) carrying the CHOSEN `harnesses` set. The
171
+ // template ships launchers for every native harness; seeding keeps only the SELECTED ones (a launcher for
172
+ // a tool the adopter didn't pick is exactly the litter --harness exists to prevent) — a plugin-only
173
+ // selection keeps them all, since the bundle serves the HOST agent while dispatched sessions still need a
174
+ // launcher. The success message reports values read back from the planted file, never restated literals.
175
+ const cfgDest = join(targetDir, 'spexcode.json');
176
+ const nativeChosen = chosenHarnesses.filter((m) => typeof m === 'string');
177
+ if (existsSync(cfgDest)) {
178
+ const cfg = (readJsonConfig(cfgDest) ?? {});
179
+ const stampedBranch = typeof cfg.mainBranch !== 'string' || !cfg.mainBranch.trim();
180
+ if (stampedBranch)
181
+ cfg.mainBranch = adoptionMainBranch(targetDir);
182
+ if (flagRaw)
183
+ cfg.harnesses = flagRaw;
184
+ if (flagRaw || stampedBranch) {
185
+ writeFileSync(cfgDest, JSON.stringify(cfg, null, 2) + '\n');
186
+ console.log(`✓ stamped ${[
187
+ flagRaw ? `"harnesses": ${JSON.stringify(flagRaw)}` : '',
188
+ stampedBranch ? `"mainBranch": ${JSON.stringify(cfg.mainBranch)}` : '',
189
+ ].filter(Boolean).join(' and ')} into the existing spexcode.json (other fields untouched)`);
190
+ }
191
+ else {
192
+ console.warn(`• spexcode.json already exists at ${cfgDest} — left untouched (harnesses: ${JSON.stringify(chosenHarnesses)}).`);
193
+ }
194
+ }
195
+ else {
196
+ const cfg = (readJsonConfig(templateConfigPath) ?? {});
197
+ cfg.harnesses = chosenHarnesses;
198
+ cfg.mainBranch = adoptionMainBranch(targetDir);
199
+ if (nativeChosen.length && cfg.sessions?.launchers) {
200
+ cfg.sessions.launchers = Object.fromEntries(Object.entries(cfg.sessions.launchers).filter(([, l]) => nativeChosen.includes(l.harness ?? 'claude')));
201
+ const names = Object.keys(cfg.sessions.launchers);
202
+ if (names.length)
203
+ cfg.sessions.defaultLauncher = names[0];
204
+ }
205
+ writeFileSync(cfgDest, JSON.stringify(cfg, null, 2) + '\n');
206
+ const roots = JSON.stringify(readJsonConfig(cfgDest)?.lint?.governedRoots ?? null);
207
+ console.log(`✓ planted spexcode.json — mainBranch ${JSON.stringify(cfg.mainBranch)}, harnesses ${JSON.stringify(chosenHarnesses)}, launchers ${JSON.stringify(Object.keys(cfg.sessions?.launchers ?? {}))}; lint.governedRoots starts as ${roots} (the whole git-tracked tree, tests excluded)`);
208
+ }
209
+ // 2. install the git hooks. Unknown existing hooks are user-owned and stay byte-identical. An exact
210
+ // SpexCode snapshot is refreshed because a multi-hook protocol cannot mix old and new generations.
211
+ const hooksDir = resolveHooksDir(targetDir);
212
+ if (!hooksDir) {
213
+ console.warn(`• ${targetDir} is not a git repository — skipped hook install. Run \`git init\` there, then \`spex init\` again (or \`npm run hooks\`).`);
214
+ }
215
+ else {
216
+ mkdirSync(hooksDir, { recursive: true });
217
+ const hooksSrc = join(TEMPLATES, 'hooks');
218
+ const installed = [];
219
+ const refreshed = [];
220
+ for (const e of readdirSync(hooksSrc, { withFileTypes: true })) {
221
+ if (!e.isFile())
222
+ continue;
223
+ const source = join(hooksSrc, e.name);
224
+ const dest = join(hooksDir, e.name);
225
+ if (existsSync(dest)) {
226
+ if (readFileSync(dest).equals(readFileSync(source)))
227
+ continue;
228
+ if (isManagedHook(dest)) {
229
+ replaceHook(source, dest);
230
+ refreshed.push(e.name);
231
+ }
232
+ else {
233
+ console.warn(`• user hook ${e.name} already exists in ${hooksDir} — left untouched.`);
234
+ }
235
+ continue;
236
+ }
237
+ copyFileSync(source, dest);
238
+ chmodSync(dest, 0o755);
239
+ installed.push(e.name);
240
+ }
241
+ if (installed.length)
242
+ console.log(`✓ installed git hooks (${installed.join(', ')}) → ${hooksDir}`);
243
+ if (refreshed.length)
244
+ console.log(`✓ refreshed SpexCode hook snapshots (${refreshed.join(', ')}) → ${hooksDir}`);
245
+ }
246
+ // 2c. MATERIALIZE the harness-discovered artifacts so a USER-self-launched harness works with zero further
247
+ // steps. Runs with cwd = the target so the loaders read the just-seeded
248
+ // .plugins. Idempotent — the planted git hooks (pre-commit/post-checkout/post-merge) keep it fresh
249
+ // thereafter on the git-native anchors ([[commit-surgery]]); no harness event ever triggers a materialize.
250
+ const prevCwd = process.cwd();
251
+ try {
252
+ process.chdir(targetDir);
253
+ const { materialize } = await import('./materialize.js');
254
+ const result = materialize(targetDir);
255
+ const display = (path) => {
256
+ const local = relative(targetDir, path);
257
+ return local && local !== '..' && !local.startsWith('../') && !local.startsWith('..\\') ? local : path;
258
+ };
259
+ console.log(`✓ materialized harness artifacts (${result.planted.map((a) => `${a.kind}: ${display(a.path)}`).join(', ')})`);
260
+ }
261
+ catch (e) {
262
+ console.warn(`• materialize skipped (${e.message}) — run \`spex materialize\` once the packages are installed.`);
263
+ }
264
+ finally {
265
+ process.chdir(prevCwd);
266
+ }
267
+ // 3. next steps — what the human must do to bring the instance to life. The governedRoots line reads the
268
+ // LIVE value (the planted starter's, or a pre-existing config's) so it can never drift from what's on disk.
269
+ const rootsNow = JSON.stringify(readJsonConfig(cfgDest)?.lint?.governedRoots ?? null);
270
+ console.log(`
271
+ Next steps:
272
+ 1. The seeded .spec/ tree and spexcode.json are project source of truth: add and commit them. Generated
273
+ harness artifacts are machine-local and can stay untracked.
274
+ 2. Edit .spec/project/spec.md to describe YOUR project, then grow child nodes beneath it.
275
+ 3. lint.governedRoots in spexcode.json (currently ${rootsNow}) names what \`spex spec lint\` governs —
276
+ ["."] governs the whole git-tracked tree (tests excluded); narrow it to explicit source roots
277
+ when you want a curated graph.
278
+ 4. Start the backend and open the dashboard:
279
+ spex serve # http://localhost:8787
280
+ 5. \`spex spec lint\` should report 0 errors. Coverage warnings are your adoption TODO (source files no
281
+ spec node claims yet). You're adopting SpexCode — the spec tree is now ground truth.
282
+ (On a fresh CLONE, re-run \`spex init\` — git never clones .git/hooks/, and the harness shims are
283
+ gitignored machine-local files that regenerate per-machine.)`);
284
+ }
@@ -0,0 +1,6 @@
1
+ export declare function runIssues(args: string[]): Promise<number>;
2
+ export declare function runIssueWrite(args: string[]): Promise<number>;
3
+ export declare const ISSUE_WRITE_SUBS: Set<string>;
4
+ export declare function runRemark(args: string[]): Promise<number>;
5
+ export declare function runResolve(args: string[]): Promise<number>;
6
+ export declare function runRetract(args: string[]): Promise<number>;
@@ -0,0 +1,334 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { closeIssue, findIssue, isRemark, mergedIssues, promote } from './issues.js';
3
+ import { FORGE_DRIVERS, forgeDriverFor, resolveForgeHost } from '@spexcode/spec-forge/drivers';
4
+ import { currentSession, issuesEnabled, resolveRemark, retractRemark } from './localIssues.js';
5
+ import { summarizeDispatch, summarizeLoopIn } from './mentions.js';
6
+ import { loadSpecsLite } from '@spexcode/spec-core';
7
+ // @@@ issues-cli - the `spex issue` / `spex remark` CLI surface: argv parsing, console output, exit codes.
8
+ // It lives ABOVE the eval layer, which is the whole point. These handlers used to sit in `issues.ts` and
9
+ // `localIssues.ts` — modules the eval package imports — and a CLI surface is by definition the topmost layer,
10
+ // so hosting one down there gave those files two altitudes at once. That is what held the spec/eval package
11
+ // cycle: the loop-in's candidate resolution needs eval knowledge, its only callers were these handlers, and
12
+ // they could not reach eval from below it. Nothing here is new logic; it is the same surface at its own height.
13
+ //
14
+ // It is NOT merged into `cli.ts`: that file is the thin dispatch hub ([[cli-surface]]) whose eighty lazy
15
+ // `await import(...)` sites keep one invocation from loading every verb's module. Moving 265 lines of verb
16
+ // implementation there would have traded one two-altitude module for another. The hub gains one lazy line
17
+ // pointing here instead.
18
+ const fl = (args, name) => {
19
+ const i = args.indexOf(`--${name}`);
20
+ return i >= 0 ? args[i + 1] : undefined;
21
+ };
22
+ const hasFlag = (args, name) => args.includes(`--${name}`);
23
+ // the CLI's live forge pull — `ls` and `show` own their freshness (a live driver read), degrading LOUDLY
24
+ // to local-only (one stderr note) when the forge is unreachable: local reading never hostages on a network.
25
+ async function liveForgeSlice(verb) {
26
+ try {
27
+ const host = resolveForgeHost();
28
+ const driver = forgeDriverFor(host);
29
+ if (!driver)
30
+ throw new Error(`no driver for this repo's forge host '${host}' (known: ${FORGE_DRIVERS.map((d) => d.host).join(', ')})`);
31
+ const [issues, prs] = await Promise.all([driver.listIssues(), driver.listPRs()]);
32
+ return { host: driver.host, state: { issues, prs } };
33
+ }
34
+ catch (e) {
35
+ console.error(`spex issue ${verb}: forge unreachable — local only (${e instanceof Error ? e.message.split('\n')[0] : e})`);
36
+ return null;
37
+ }
38
+ }
39
+ // the single-issue read behind `spex issue show` AND `GET /api/issues/:id` — find the thread in the SAME
40
+ // merged, eval-remark-free read every issue surface consumes (never a second lookup path: an eval-remark
41
+ // thread is not an issue, so `show` can't see one either). A local id needs no forge slice; a forge id
42
+ // (`<host>#<n>`) reads from the caller-supplied slice (live pull on the CLI, resident cache on the server).
43
+ function renderIssue(t) {
44
+ const L = [];
45
+ L.push(`${t.concern} [${t.id}]`);
46
+ L.push(` ${[t.store, t.status, t.nodes.length ? `re: ${t.nodes.join(', ')}` : '', t.by ? `by ${t.by}` : '', t.created].filter(Boolean).join(' · ')}`);
47
+ if (t.url)
48
+ L.push(` ${t.url}`);
49
+ if (t.evidence.length)
50
+ L.push(` evidence: ${t.evidence.join(', ')}`);
51
+ L.push('', t.body);
52
+ for (const r of t.replies) {
53
+ L.push('', `── ${isRemark(r) ? `remark ${t.id}#${r.rid}${r.resolved ? ` (resolved by ${r.resolvedBy})` : ' (unresolved)'}` : 'reply'}: ${r.by} @ ${r.at} ──`);
54
+ L.push(r.body);
55
+ }
56
+ return L.join('\n');
57
+ }
58
+ // `spex issue <verb>` — the ONE issue surface, a noun drawer ([[cli-surface]]). `ls` is THE read over
59
+ // every store: the drain view a supervisor/human works from, `[--node id] [--store local|<host>] [--all]
60
+ // [--json]`; `show <id>` is the single-thread detail (the same read GET /api/issues/:id serves). The
61
+ // write verbs (open|reply — localIssues.ts) are store-routed (`open --store <host>` / a `<host>#<n>` id
62
+ // go through the driver); `close` is the store-routed lifecycle verb (the SAME closeIssue the dashboard's
63
+ // Close button calls); `promote` is the one cross-store verb; `links` is the read-only forge→spec trace
64
+ // (spec-forge). The list imposes NO salience ranking — replies are a signal the drain WEIGHS by judgment,
65
+ // never an automatic priority order. The forge slice is a LIVE pull that degrades loudly to local-only.
66
+ // (`nudge` left this drawer for `spex internal nudge` — only the post-merge hook calls it; the old
67
+ // on|off|status toggle verbs are gone — the switch is the `issues.enabled` settings key.)
68
+ export async function runIssues(args) {
69
+ // the drawer's READ verbs (ls/show) surface a store failure exactly as the writes do
70
+ // ([[issues-store-rename]]'s both-exist teeth): one clean `spex issue: <message>` line + exit 1, never a
71
+ // raw stack — the message carries the repair, the stack is internals. The verbs that already catch with
72
+ // a more specific prefix (open/reply/close/promote) return before this guard ever sees their errors.
73
+ try {
74
+ return await issueVerbs(args);
75
+ }
76
+ catch (e) {
77
+ console.error(`spex issue: ${e instanceof Error ? e.message : e}`);
78
+ return 1;
79
+ }
80
+ }
81
+ async function issueVerbs(args) {
82
+ if (ISSUE_WRITE_SUBS.has(args[0]))
83
+ return runIssueWrite(args);
84
+ if (args[0] === 'on' || args[0] === 'off' || args[0] === 'status') {
85
+ // v0.3.0 signpost — report the new home, never run ([[cli-surface]]: a removed spelling only points).
86
+ console.error(`spex: \`spex issue ${args[0]}\` was removed in v0.3.0 — the switch is the \`issues.enabled\` key in spexcode.json (edit the JSON; \`spex guide settings\` documents it, \`spex doctor\` reports its state)`);
87
+ return 2;
88
+ }
89
+ if (args[0] === 'show') {
90
+ const id = args[1];
91
+ if (!id || id.startsWith('--')) {
92
+ console.error('usage: spex issue show <issue-id> [--json] (a local id, or a forge id like github#12)');
93
+ return 2;
94
+ }
95
+ const nodeIds = loadSpecsLite().map((s) => s.id);
96
+ const t = findIssue(id, id.includes('#') ? await liveForgeSlice('show') : null, nodeIds);
97
+ if (!t) {
98
+ console.error(`spex issue show: no issue '${id}' (see \`spex issue ls --all\`)`);
99
+ return 1;
100
+ }
101
+ console.log(hasFlag(args, 'json') ? JSON.stringify(t, null, 2) : renderIssue(t));
102
+ return 0;
103
+ }
104
+ if (args[0] === 'links') {
105
+ const { runIssueLinks } = await import('@spexcode/spec-forge/cli');
106
+ return runIssueLinks(args.slice(1));
107
+ }
108
+ if (args[0] === 'close') {
109
+ // the CLI leg of the ONE close verb ([[issues]] closeIssue — the same routing POST /api/issues/:id/close
110
+ // runs): a local id resolves the thread `landed`, a forge id (`<host>#<n>`) closes the remote issue
111
+ // through the driver. Lifecycle on the issue object, never node state.
112
+ const id = args[1];
113
+ if (!id || id.startsWith('--')) {
114
+ console.error('usage: spex issue close <issue-id> (a local id, or a forge id like github#12)');
115
+ return 2;
116
+ }
117
+ try {
118
+ const r = await closeIssue(id);
119
+ console.log(r.store === 'local'
120
+ ? `closed '${id}' — local thread landed`
121
+ : `closed '${id}' on ${r.store}${r.url ? ` ${r.url}` : ''}`);
122
+ return 0;
123
+ }
124
+ catch (e) {
125
+ console.error(`spex issue close: ${e instanceof Error ? e.message : e}`);
126
+ return 1;
127
+ }
128
+ }
129
+ if (args[0] === 'promote') {
130
+ const id = args[1];
131
+ if (!id || id.startsWith('--')) {
132
+ console.error('usage: spex issue promote <local-issue-id>');
133
+ return 2;
134
+ }
135
+ try {
136
+ const r = await promote(id);
137
+ console.log(`promoted '${id}' → ${r.host}#${r.number} ${r.url}\n local thread closed landed (permalink recorded in its reply trail)`);
138
+ return 0;
139
+ }
140
+ catch (e) {
141
+ console.error(`spex issue promote: ${e instanceof Error ? e.message : e}`);
142
+ return 1;
143
+ }
144
+ }
145
+ if (args[0] !== 'ls') {
146
+ console.error(`spex issue: unknown verb '${args[0]}' — ls | show | open | reply | close | promote | links (spex help issue)`);
147
+ return 2;
148
+ }
149
+ args = args.slice(1);
150
+ const nodeIds = loadSpecsLite().map((s) => s.id);
151
+ const forge = await liveForgeSlice('ls');
152
+ let issues = mergedIssues(forge, nodeIds);
153
+ const node = fl(args, 'node');
154
+ const store = fl(args, 'store');
155
+ if (node)
156
+ issues = issues.filter((p) => p.nodes.includes(node));
157
+ if (store)
158
+ issues = issues.filter((p) => p.store === store);
159
+ if (!hasFlag(args, 'all'))
160
+ issues = issues.filter((p) => p.status === 'open');
161
+ if (hasFlag(args, 'json')) {
162
+ console.log(JSON.stringify(issues, null, 2));
163
+ return 0;
164
+ }
165
+ if (!issues.length) {
166
+ console.log(node ? `no issues for node '${node}'` : 'no open issues');
167
+ return 0;
168
+ }
169
+ console.log(`issues — ${issues.length} ${hasFlag(args, 'all') ? 'total' : 'open'}${store ? ` in '${store}'` : ''}${node ? ` for '${node}'` : ''}\n`);
170
+ for (const p of issues) {
171
+ const tags = [p.store, p.status !== 'open' ? `[${p.status}]` : '', p.nodes.length ? `re: ${p.nodes.join(', ')}` : '', p.by ? `by ${p.by}` : ''].filter(Boolean).join(' · ');
172
+ console.log(`• ${p.concern} [${p.id}]`);
173
+ console.log(` ${tags}`);
174
+ if (p.replies.length)
175
+ console.log(` ${p.replies.length} reply(ies) in thread`);
176
+ if (p.url)
177
+ console.log(` ${p.url}`);
178
+ }
179
+ if (!issuesEnabled())
180
+ console.log('\n(the issues workflow is OFF — set `"issues": { "enabled": true }` in spexcode.json to re-enable writes/nudges)');
181
+ return 0;
182
+ }
183
+ const VALUE_FLAGS = new Set(['--node', '--body', '--evidence', '--scenario', '--code-sha', '--store']);
184
+ // bare positionals, skipping flags + their values.
185
+ function bare(args) {
186
+ const out = [];
187
+ for (let i = 0; i < args.length; i++) {
188
+ const t = args[i];
189
+ if (t.startsWith('--')) {
190
+ if (VALUE_FLAGS.has(t))
191
+ i++;
192
+ continue;
193
+ }
194
+ out.push(t);
195
+ }
196
+ return out;
197
+ }
198
+ // `--body -` reads stdin; `--body "text"` is literal; absent → undefined.
199
+ function readBody(args) {
200
+ const v = fl(args, 'body');
201
+ if (v === undefined)
202
+ return undefined;
203
+ return v === '-' ? readFileSync(0, 'utf8') : v;
204
+ }
205
+ // a repeatable value flag: every `--<name> <value>` pair, in order.
206
+ const repeated = (args, name) => args.flatMap((a, i) => (a === `--${name}` ? [args[i + 1]] : [])).filter(Boolean);
207
+ // the local-issue WRITE verbs of the issue drawer (`spex issue <verb>`): open "<concern>" [--store local|<host>] [--node id…]
208
+ // [--evidence hash…] [--body -|text], and the id-based reply. Store is a property of the issue,
209
+ // never a second command — open and reply route by it (issues.ts createIssue/replyIssue).
210
+ export async function runIssueWrite(args) {
211
+ const sub = args[0];
212
+ try {
213
+ if (sub === 'reply') {
214
+ const id = bare(args.slice(1))[0];
215
+ const body = readBody(args);
216
+ if (!id || !body) {
217
+ console.error('usage: spex issue reply <issue-id> --body -|<text> [--evidence <hash>…]');
218
+ return 2;
219
+ }
220
+ // the ONE store-routed reply verb ([[issues]]): a forge id posts a real comment through the driver,
221
+ // a local id commits to the store — the same command either way (dynamic import: no static cycle).
222
+ const r = await (await import('./loop-in.js')).replyIssueWithLoopIn(id, body, { evidence: repeated(args, 'evidence') });
223
+ console.log(r.store === 'local'
224
+ ? `replied to '${id}' — ${r.replies?.length} post(s) in thread`
225
+ : `commented on '${id}' — ${r.url}`);
226
+ const dispatched = summarizeDispatch(r.outcomes);
227
+ if (dispatched)
228
+ console.log(` ${dispatched}`);
229
+ const s = summarizeLoopIn(r.loopIn);
230
+ if (s)
231
+ console.log(` ${s}`);
232
+ return 0;
233
+ }
234
+ // `open`: start a new issue — STORE-ROUTED through the one creation port ([[issues]] createIssue, the
235
+ // same routine POST /api/issues runs): default local commits to the trunk store; `--store <host>`
236
+ // creates the real forge issue through that store's driver (no promote round-trip when the concern is
237
+ // born forge-visible). The concern is the bare positional(s) after the sub.
238
+ const concern = sub === 'open' ? bare(args.slice(1)).join(' ').trim() : '';
239
+ if (!concern) {
240
+ console.error('usage: spex issue open "<concern>" [--store local|<host>] [--node <id>…] [--evidence <hash>…] [--body -|<text>]\n spex issue reply|close|promote <issue-id> …');
241
+ return 2;
242
+ }
243
+ const r = await (await import('./issues.js')).createIssue(concern, {
244
+ store: fl(args, 'store'),
245
+ nodes: repeated(args, 'node'),
246
+ body: readBody(args),
247
+ evidence: repeated(args, 'evidence'),
248
+ });
249
+ const re = r.nodes.length ? ` (re: ${r.nodes.join(', ')})` : '';
250
+ console.log(r.store === 'local'
251
+ ? `opened '${r.id}'${re} — committed to the local issue store; read it with \`spex issue ls\``
252
+ : `opened '${r.id}' on ${r.store}${re} — ${r.url}`);
253
+ const dispatched = summarizeDispatch(r.outcomes);
254
+ if (dispatched)
255
+ console.log(` ${dispatched}`);
256
+ return 0;
257
+ }
258
+ catch (e) {
259
+ console.error(`spex issue: ${e instanceof Error ? e.message : e}`);
260
+ return 1;
261
+ }
262
+ }
263
+ // the first positionals runIssueWrite handles — the issue drawer routes these to it. Exported so the
264
+ // router and the runner can never drift. (`nudge` is not here: it is machine plumbing, called only by the
265
+ // post-merge hook as `spex internal nudge`; the on|off|status toggle verbs died in v0.3.0 — the switch is
266
+ // the `issues.enabled` settings key.)
267
+ export const ISSUE_WRITE_SUBS = new Set(['open', 'reply']);
268
+ // ── remark CLI ([[remark-substrate]]) — CLI-first: the whole author→resolve→retract loop, no server needed ──
269
+ // `spex remark add <issue-id | <node> --scenario <name>> --body -|<text> [--code-sha <sha>] [--evidence <hash>…]`
270
+ // host = a local issue id, OR a <node> with --scenario <name>. Records targetCodeSha (default: worktree HEAD).
271
+ export async function runRemark(args) {
272
+ try {
273
+ const scenario = fl(args, 'scenario');
274
+ const positional = bare(args)[0];
275
+ const body = readBody(args);
276
+ if (!positional || !body) {
277
+ console.error('usage: spex remark add <issue-id | node --scenario name> --body -|<text> [--code-sha <sha>] [--evidence <hash>…]');
278
+ return 2;
279
+ }
280
+ // THE FLAG DECIDES THE PARSE ([[cli-surface]] §1): `--scenario` present ⇒ the positional is a NODE id
281
+ // (the remark pins to that node's scenario track); absent ⇒ it is an ISSUE id. Never type-sniffed —
282
+ // a node id and an issue id are both bare slugs, so any "looks like" guess would misroute; the flag
283
+ // is the one unambiguous discriminator, and a wrong host fails loud downstream (unknown issue/node).
284
+ const host = scenario ? { node: positional, scenario } : { issue: positional };
285
+ const r = await (await import('./loop-in.js')).remarkWithLoopIn(host, body, { codeSha: fl(args, 'code-sha'), evidence: repeated(args, 'evidence') });
286
+ console.log(`remark ${r.ref} (against ${r.codeSha.slice(0, 7) || 'HEAD'}) — read it with \`spex issue ls --all\``);
287
+ const dispatched = summarizeDispatch(r.outcomes);
288
+ if (dispatched)
289
+ console.log(` ${dispatched}`);
290
+ const s = summarizeLoopIn(r.loopIn);
291
+ if (s)
292
+ console.log(` ${s}`);
293
+ return 0;
294
+ }
295
+ catch (e) {
296
+ console.error(`spex remark add: ${e instanceof Error ? e.message : e}`);
297
+ return 1;
298
+ }
299
+ }
300
+ // `spex remark resolve <remark-ref>` — flip resolved=true (agent-only, never the author, monotonic — see resolveRemark).
301
+ export async function runResolve(args) {
302
+ const ref = bare(args)[0];
303
+ if (!ref) {
304
+ console.error('usage: spex remark resolve <remark-ref> (the <thread-id>#<rid> `spex remark add` printed)');
305
+ return 2;
306
+ }
307
+ try {
308
+ const by = currentSession();
309
+ resolveRemark(ref, by);
310
+ console.log(`resolved remark ${ref} — by ${by}`);
311
+ return 0;
312
+ }
313
+ catch (e) {
314
+ console.error(`spex remark resolve: ${e instanceof Error ? e.message : e}`);
315
+ return 1;
316
+ }
317
+ }
318
+ // `spex remark retract <remark-ref>` — the author withdraws their OWN remark, removing it (author-only — see retractRemark).
319
+ export async function runRetract(args) {
320
+ const ref = bare(args)[0];
321
+ if (!ref) {
322
+ console.error('usage: spex remark retract <remark-ref>');
323
+ return 2;
324
+ }
325
+ try {
326
+ retractRemark(ref, currentSession());
327
+ console.log(`retracted remark ${ref}`);
328
+ return 0;
329
+ }
330
+ catch (e) {
331
+ console.error(`spex remark retract: ${e instanceof Error ? e.message : e}`);
332
+ return 1;
333
+ }
334
+ }