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,36 @@
1
+ import { type UploadPolicy } from '@spexcode/spec-core';
2
+ type UploadMeta = {
3
+ version: 1;
4
+ id: string;
5
+ name: string;
6
+ size: number;
7
+ offset: number;
8
+ createdAt: number;
9
+ updatedAt: number;
10
+ };
11
+ export type UploadStatus = Pick<UploadMeta, 'id' | 'name' | 'size' | 'offset'> & Pick<UploadPolicy, 'chunkBytes' | 'concurrency' | 'requestTimeoutMs' | 'retryLimit' | 'retryDelayMs'>;
12
+ export declare class UploadError extends Error {
13
+ readonly status: number;
14
+ readonly offset?: number | undefined;
15
+ constructor(status: number, message: string, offset?: number | undefined);
16
+ }
17
+ export declare const evidenceMaxBytes: () => number;
18
+ export declare function cleanupExpiredUploads(policy?: Required<{
19
+ maxBytes?: number;
20
+ chunkBytes?: number;
21
+ concurrency?: number;
22
+ requestTimeoutMs?: number;
23
+ retryLimit?: number;
24
+ retryDelayMs?: number;
25
+ incompleteTtlMs?: number;
26
+ cleanupIntervalMs?: number;
27
+ minFreeBytes?: number;
28
+ evidenceMaxBytes?: number;
29
+ }>): void;
30
+ export declare function startUploadReaper(): void;
31
+ export declare function createUpload(name: unknown, size: unknown): UploadStatus;
32
+ export declare function uploadStatus(id: string): UploadStatus;
33
+ export declare function appendUpload(id: string, offset: unknown, body: ReadableStream<Uint8Array> | null, contentLength?: string): Promise<UploadStatus>;
34
+ export declare function completeUpload(id: string): string;
35
+ export declare function cancelUpload(id: string): void;
36
+ export {};
@@ -0,0 +1,243 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { createWriteStream, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, statfsSync, truncateSync, writeFileSync } from 'node:fs';
3
+ import { tmpdir } from 'node:os';
4
+ import { basename, join } from 'node:path';
5
+ import { Readable, Transform } from 'node:stream';
6
+ import { pipeline } from 'node:stream/promises';
7
+ import { repoRoot } from '@spexcode/spec-core';
8
+ import { readUploadPolicy } from '@spexcode/spec-core';
9
+ // The backend's tmpdir is the worker's filesystem. Completed files stay directly under this sink; private
10
+ // metadata and partial bytes live below .staging until an exact-length transfer is promoted atomically.
11
+ const UPLOAD_DIR = join(tmpdir(), 'spexcode-uploads');
12
+ const STAGING_DIR = join(UPLOAD_DIR, '.staging');
13
+ export class UploadError extends Error {
14
+ status;
15
+ offset;
16
+ constructor(status, message, offset) {
17
+ super(message);
18
+ this.status = status;
19
+ this.offset = offset;
20
+ }
21
+ }
22
+ const uploadPolicy = () => readUploadPolicy(repoRoot());
23
+ export const evidenceMaxBytes = () => uploadPolicy().evidenceMaxBytes;
24
+ function ensureDirs() {
25
+ mkdirSync(STAGING_DIR, { recursive: true });
26
+ }
27
+ function safeName(name) {
28
+ const base = basename(name || '').replace(/[^A-Za-z0-9._-]/g, '_').replace(/^\.+/, '');
29
+ return base || 'upload';
30
+ }
31
+ function validId(id) {
32
+ return /^[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i.test(id);
33
+ }
34
+ function metaPath(id) {
35
+ return join(STAGING_DIR, `${id}.json`);
36
+ }
37
+ function partPath(id) {
38
+ return join(STAGING_DIR, `${id}.part`);
39
+ }
40
+ function removeTransfer(id) {
41
+ rmSync(metaPath(id), { force: true });
42
+ rmSync(partPath(id), { force: true });
43
+ }
44
+ function readMetaFile(id) {
45
+ if (!validId(id))
46
+ return null;
47
+ try {
48
+ const meta = JSON.parse(readFileSync(metaPath(id), 'utf8'));
49
+ if (meta.version !== 1 || meta.id !== id || typeof meta.name !== 'string' ||
50
+ !Number.isSafeInteger(meta.size) || meta.size <= 0 ||
51
+ !Number.isSafeInteger(meta.offset) || meta.offset < 0 || meta.offset > meta.size ||
52
+ !Number.isSafeInteger(meta.createdAt) || !Number.isSafeInteger(meta.updatedAt))
53
+ return null;
54
+ return meta;
55
+ }
56
+ catch {
57
+ return null;
58
+ }
59
+ }
60
+ function writeMeta(meta) {
61
+ const path = metaPath(meta.id);
62
+ const temp = `${path}.${process.pid}.${randomUUID()}.tmp`;
63
+ writeFileSync(temp, JSON.stringify(meta) + '\n');
64
+ renameSync(temp, path);
65
+ }
66
+ function actualOffset(meta) {
67
+ try {
68
+ return statSync(partPath(meta.id)).size;
69
+ }
70
+ catch {
71
+ return 0;
72
+ }
73
+ }
74
+ function status(meta, policy) {
75
+ const { chunkBytes, concurrency, requestTimeoutMs, retryLimit, retryDelayMs } = policy;
76
+ return { id: meta.id, name: meta.name, size: meta.size, offset: meta.offset, chunkBytes, concurrency, requestTimeoutMs, retryLimit, retryDelayMs };
77
+ }
78
+ function syncOffset(meta) {
79
+ const offset = actualOffset(meta);
80
+ if (offset > meta.size)
81
+ throw new UploadError(409, 'upload staging file exceeds its declared length');
82
+ if (offset !== meta.offset) {
83
+ meta.offset = offset;
84
+ meta.updatedAt = Date.now();
85
+ writeMeta(meta);
86
+ }
87
+ return meta;
88
+ }
89
+ function loadUpload(id, policy) {
90
+ const meta = readMetaFile(id);
91
+ if (!meta)
92
+ throw new UploadError(404, 'upload not found');
93
+ if (Date.now() - meta.updatedAt > policy.incompleteTtlMs) {
94
+ removeTransfer(id);
95
+ throw new UploadError(404, 'upload expired');
96
+ }
97
+ return syncOffset(meta);
98
+ }
99
+ function reservedBytes(policy) {
100
+ let total = 0;
101
+ for (const entry of readdirSync(STAGING_DIR, { withFileTypes: true })) {
102
+ if (!entry.isFile() || !entry.name.endsWith('.json'))
103
+ continue;
104
+ const meta = readMetaFile(entry.name.slice(0, -5));
105
+ if (!meta || Date.now() - meta.updatedAt > policy.incompleteTtlMs)
106
+ continue;
107
+ total += Math.max(0, meta.size - actualOffset(meta));
108
+ }
109
+ return total;
110
+ }
111
+ function reserveCapacity(size, policy) {
112
+ let available = 0;
113
+ try {
114
+ const fs = statfsSync(UPLOAD_DIR);
115
+ available = Number(fs.bavail) * Number(fs.bsize);
116
+ }
117
+ catch {
118
+ // A filesystem that cannot report capacity will still fail loudly during the stream write.
119
+ return;
120
+ }
121
+ if (size > Math.max(0, available - policy.minFreeBytes - reservedBytes(policy))) {
122
+ throw new UploadError(507, 'insufficient backend disk capacity for this upload');
123
+ }
124
+ }
125
+ export function cleanupExpiredUploads(policy = uploadPolicy()) {
126
+ ensureDirs();
127
+ const now = Date.now();
128
+ for (const entry of readdirSync(STAGING_DIR, { withFileTypes: true })) {
129
+ if (!entry.isFile() || !entry.name.endsWith('.json'))
130
+ continue;
131
+ const id = entry.name.slice(0, -5);
132
+ const meta = readMetaFile(id);
133
+ if (!meta || now - meta.updatedAt > policy.incompleteTtlMs)
134
+ removeTransfer(id);
135
+ }
136
+ for (const entry of readdirSync(STAGING_DIR, { withFileTypes: true })) {
137
+ if (!entry.isFile() || !entry.name.endsWith('.part'))
138
+ continue;
139
+ const id = entry.name.slice(0, -5);
140
+ if (readMetaFile(id))
141
+ continue;
142
+ try {
143
+ if (now - statSync(partPath(id)).mtimeMs > policy.incompleteTtlMs)
144
+ rmSync(partPath(id), { force: true });
145
+ }
146
+ catch { /* raced a cancellation or another cleanup pass */ }
147
+ }
148
+ }
149
+ export function startUploadReaper() {
150
+ const sweep = () => {
151
+ const policy = uploadPolicy();
152
+ cleanupExpiredUploads(policy);
153
+ const timer = setTimeout(sweep, policy.cleanupIntervalMs);
154
+ timer.unref();
155
+ };
156
+ sweep();
157
+ }
158
+ export function createUpload(name, size) {
159
+ const policy = uploadPolicy();
160
+ if (typeof name !== 'string' || !name.trim())
161
+ throw new UploadError(400, 'file name is required');
162
+ if (typeof size !== 'number' || !Number.isSafeInteger(size) || size <= 0)
163
+ throw new UploadError(400, 'file must not be empty');
164
+ if (size > policy.maxBytes)
165
+ throw new UploadError(413, 'file exceeds the configured upload limit');
166
+ ensureDirs();
167
+ cleanupExpiredUploads(policy);
168
+ reserveCapacity(size, policy);
169
+ const now = Date.now();
170
+ const meta = { version: 1, id: randomUUID(), name: safeName(name), size, offset: 0, createdAt: now, updatedAt: now };
171
+ writeMeta(meta);
172
+ return status(meta, policy);
173
+ }
174
+ export function uploadStatus(id) {
175
+ const policy = uploadPolicy();
176
+ return status(loadUpload(id, policy), policy);
177
+ }
178
+ export async function appendUpload(id, offset, body, contentLength) {
179
+ const policy = uploadPolicy();
180
+ const meta = loadUpload(id, policy);
181
+ if (typeof offset !== 'number' || !Number.isSafeInteger(offset) || offset < 0)
182
+ throw new UploadError(400, 'upload offset must be a non-negative integer');
183
+ if (offset !== meta.offset)
184
+ throw new UploadError(409, 'upload offset does not match the committed bytes', meta.offset);
185
+ if (!body)
186
+ throw new UploadError(400, 'upload chunk is required');
187
+ const declared = contentLength == null ? null : Number(contentLength);
188
+ if (declared != null && (!Number.isSafeInteger(declared) || declared <= 0))
189
+ throw new UploadError(400, 'content-length must be a positive integer');
190
+ if (declared != null && (declared > policy.chunkBytes || meta.offset + declared > meta.size)) {
191
+ throw new UploadError(413, 'upload chunk exceeds its declared bounds');
192
+ }
193
+ let written = 0;
194
+ const before = meta.offset;
195
+ const limiter = new Transform({
196
+ transform(chunk, _encoding, callback) {
197
+ written += chunk.length;
198
+ if (written > policy.chunkBytes || before + written > meta.size) {
199
+ callback(new UploadError(413, 'upload chunk exceeds its declared bounds'));
200
+ return;
201
+ }
202
+ callback(null, chunk);
203
+ },
204
+ });
205
+ try {
206
+ await pipeline(Readable.fromWeb(body), limiter, createWriteStream(partPath(id), { flags: 'a' }));
207
+ }
208
+ catch (error) {
209
+ if (error instanceof UploadError) {
210
+ truncateSync(partPath(id), before);
211
+ throw error;
212
+ }
213
+ // An interrupted connection may have committed a valid prefix. The next GET reports its actual length.
214
+ meta.offset = actualOffset(meta);
215
+ meta.updatedAt = Date.now();
216
+ writeMeta(meta);
217
+ throw new UploadError(500, `upload write failed: ${error.message}`);
218
+ }
219
+ if (written === 0)
220
+ throw new UploadError(400, 'upload chunk is empty');
221
+ meta.offset = actualOffset(meta);
222
+ meta.updatedAt = Date.now();
223
+ writeMeta(meta);
224
+ return status(meta, policy);
225
+ }
226
+ export function completeUpload(id) {
227
+ const meta = loadUpload(id, uploadPolicy());
228
+ if (meta.offset !== meta.size)
229
+ throw new UploadError(409, `upload is incomplete (${meta.offset} of ${meta.size} bytes)`, meta.offset);
230
+ const path = join(UPLOAD_DIR, `${Date.now().toString(36)}-${meta.id}-${safeName(meta.name)}`);
231
+ try {
232
+ renameSync(partPath(id), path);
233
+ rmSync(metaPath(id), { force: true });
234
+ return path;
235
+ }
236
+ catch (error) {
237
+ throw new UploadError(500, `upload completion failed: ${error.message}`);
238
+ }
239
+ }
240
+ export function cancelUpload(id) {
241
+ loadUpload(id, uploadPolicy());
242
+ removeTransfer(id);
243
+ }
@@ -0,0 +1 @@
1
+ export declare function seedWorktreeHostState(main: string, wt: string): void;
@@ -0,0 +1,47 @@
1
+ import { appendFileSync, copyFileSync, existsSync, mkdirSync, readFileSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import { git } from '@spexcode/spec-core';
4
+ export function seedWorktreeHostState(main, wt) {
5
+ const f = 'spexcode.local.json';
6
+ try {
7
+ if (!existsSync(join(main, f)) || existsSync(join(wt, f)))
8
+ return;
9
+ copyFileSync(join(main, f), join(wt, f));
10
+ }
11
+ catch (e) {
12
+ console.error(`spexcode: could not seed ${f} from ${main} into worktree ${wt} — that worker runs on defaults (${e})`);
13
+ return;
14
+ }
15
+ hideSeededFromGit(wt, [f]);
16
+ }
17
+ // what we seed, we hide: a seeded entry git still sees is force-add bait (a real PR once carried seeded
18
+ // files into a product repo). `.git/info/exclude` lives in the COMMON git dir, so one write hides the entry
19
+ // in every linked worktree AND the main checkout. Only entries seeded by THIS call and reported un-ignored
20
+ // by `git check-ignore` are written: idempotent across dispatches, and a repo whose materialize already ignores
21
+ // the overlay (materialize's block under any policy) writes nothing — the self-heal for a half-configured repo.
22
+ function hideSeededFromGit(wt, seeded) {
23
+ for (const f of seeded) {
24
+ try {
25
+ if (isIgnored(wt, f))
26
+ continue;
27
+ const exclude = join(git(['-C', wt, 'rev-parse', '--path-format=absolute', '--git-common-dir']).trim(), 'info', 'exclude');
28
+ mkdirSync(dirname(exclude), { recursive: true });
29
+ const cur = existsSync(exclude) ? readFileSync(exclude, 'utf8') : '';
30
+ appendFileSync(exclude, `${cur && !cur.endsWith('\n') ? '\n' : ''}${f}\n`);
31
+ }
32
+ catch (e) {
33
+ console.error(`spexcode: could not hide seeded ${f} in the shared info/exclude for ${wt} — it will show untracked there (${e})`);
34
+ }
35
+ }
36
+ }
37
+ function isIgnored(wt, f) {
38
+ try {
39
+ git(['-C', wt, 'check-ignore', '-q', f]);
40
+ return true;
41
+ }
42
+ catch (e) {
43
+ if (e?.status === 1)
44
+ return false; // check-ignore's documented "not ignored" exit
45
+ throw e;
46
+ }
47
+ }
@@ -16,14 +16,14 @@
16
16
  # session-worktree creation, and the pre-commit/post-checkout/post-merge hooks). .plugins edits are
17
17
  # git-transactional: they take effect at the commit/checkout/merge that carries them, like any other source.
18
18
  set -u
19
- # args: `<harness> <Event>`. A harness id as $1 (claude|codex|opencode|pi|plugin) is consumed; otherwise we keep
19
+ # args: `<harness> <Event>`. A harness id as $1 (claude|codex|opencode|pi|zcode|plugin) is consumed; otherwise we keep
20
20
  # $1 as the event and default the harness to claude — so a stale shim still written as `dispatch.sh <Event>`
21
21
  # keeps working. `plugin` is the bundle form ([[plugin-harness]]), `opencode` the generated event-bus plugin
22
- # ([[opencode-harness]]), and `pi` the generated extension ([[pi-harness]]): all three SYNTHESIZE claude-shaped
23
- # payloads (Claude tool names + file_path), so they join the claude branch in harness.sh via the default case —
24
- # no parse arm of their own.
22
+ # ([[opencode-harness]]), `pi` the generated extension ([[pi-harness]]), and `zcode` the native adapter: all four
23
+ # carry Claude-shaped payloads (Claude tool names + file_path), so they join the claude branch in harness.sh via
24
+ # the default case — no parse arm of their own.
25
25
  harness=claude
26
- case "${1:-}" in claude|codex|opencode|pi|plugin) harness="$1"; shift ;; esac
26
+ case "${1:-}" in claude|codex|opencode|pi|zcode|plugin) harness="$1"; shift ;; esac
27
27
  event="${1:?usage: dispatch.sh <harness> <Event>}"
28
28
  export SPEXCODE_HARNESS="$harness"
29
29
  # the harness.sh path (the adapter's shell mirror) — sibling of this script; hook handlers source it, and we
@@ -108,7 +108,7 @@ hp_is_subagent() {
108
108
  printf '%s' "${1%%\"tool_input\"*}" | grep -q '"agent_id"[[:space:]]*:' && printf 1
109
109
  }
110
110
 
111
- # the per-PROJECT GLOBAL runtime dir (mirrors spec-cli/src/layout.ts `runtimeRoot`): <store>/projects/<enc>,
111
+ # the per-PROJECT GLOBAL runtime dir (mirrors packages/spec-core/src/layout.ts `runtimeRoot`): <store>/projects/<enc>,
112
112
  # keyed by the project (dirname of the ABSOLUTE git-common-dir, so the answer is identical from main or any
113
113
  # worktree). The per-session dirs and the per-tree materialize slots (hp_tree_dir) live under it.
114
114
  # Echoes the dir; returns non-zero (echoing nothing) when git can't resolve, so a caller can `|| exit 0`.
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@spexcode/spec-cli",
3
+ "version": "0.6.5",
4
+ "type": "module",
5
+ "description": "SpexCode CLI + server. The root spexcode package delegates to this compiled package; dashboard assets live in @spexcode/spec-dashboard.",
6
+ "bin": {
7
+ "spex": "bin/spex.mjs"
8
+ },
9
+ "exports": {
10
+ ".": "./dist/index.js",
11
+ "./bin/spex.mjs": "./bin/spex.mjs",
12
+ "./ranker": "./dist/ranker.js",
13
+ "./eval-host": "./dist/eval-host.js",
14
+ "./package.json": "./package.json"
15
+ },
16
+ "files": [
17
+ "bin",
18
+ "dist",
19
+ "templates",
20
+ "hooks"
21
+ ],
22
+ "bundledDependencies": [
23
+ "@spexcode/spec-core",
24
+ "@spexcode/spec-eval",
25
+ "@spexcode/spec-forge"
26
+ ],
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
30
+ "scripts": {
31
+ "build": "node ../scripts/build-dist.mjs",
32
+ "prepublishOnly": "node ../scripts/release-publish.mjs --from-package-publish",
33
+ "serve": "npm run build && node bin/spex.mjs serve",
34
+ "lint": "npm run build && node bin/spex.mjs spec lint",
35
+ "test": "tsx --import ../scripts/test-home.mjs --test src/*.test.ts"
36
+ },
37
+ "dependencies": {
38
+ "@spexcode/spec-core": "0.6.5",
39
+ "@spexcode/spec-eval": "0.6.5",
40
+ "@spexcode/spec-forge": "0.6.5"
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^20.16.0",
44
+ "tsx": "^4.19.2",
45
+ "typescript": "^5.6.3"
46
+ }
47
+ }
@@ -0,0 +1,8 @@
1
+ ---
2
+ title: supervisor
3
+ surface: command
4
+ status: active
5
+ hue: 280
6
+ desc: Launch a supervisor agent that manages other agents from the main checkout to drive a goal to completion.
7
+ ---
8
+ You are a SpexCode supervisor — a **manager**, not a feature worker. Your work base is the main checkout (the repository root), NOT your own worktree: do all git via `git -C <root>`, everything else via the `spex` CLI, and never write feature code. This preset IS your complete playbook (dispatch → monitor → review → merge → close, and how to parallelize) — the CLI's own `spex help session` is the reference for every verb's exact semantics. Drive the goal: decompose it into worker-sized tasks and dispatch one worker per independent task (`spex session new "<task>"` creates the parent-child relation and automatically installs your managed `parent` watch; inspect that existing relation/source with `spex session watch list`, so do NOT add a manual watch for this child — give it ONLY its task; a task about one specific node mentions it as `[[<id>]]`, which only sets the branch name and board attribution; the session's real node links come from what it edits), review proposals with `spex session review <id>`, dispatch the merge of good ones back to their own session (`spex session merge <id>`; the doer syncs the base into its own worktree first, so what reaches `<root>` is a trivial merge) and confirm it landed, then close. `<root>` is the fleet's ONE landing door — it takes one landing at a time, so a worker whose merge finds it mid-merge waits rather than racing, and you never fix up another lane's half-merged index yourself. Never let a worker self-merge; keep `spex spec lint` at 0 errors. To READ a worker's current state, use the one-shot snapshots (`spex session review <id>` or `spex session ls` — both return immediately); for a local next-lifecycle-edge read when no managed delivery exists, background `spex session wait <id>` — it returns only when it OBSERVES the worker transition from non-actionable into an actionable status (an already-actionable arrival state does not return it), printing the observed status path; `spex session watch stream` is human-only and STREAMS forever, so never block on it. **Stay parked while your fleet runs:** `spex session new`'s managed watch delivery is a real wake-up, so park while it exists. A manual `spex session watch <id>` is deliberate ongoing supervision of an existing session, not a one-off wait: it adds your `manual` source, and every future AUTHORED state transition is queued as a message to the watcher until `spex session watch cancel <id>` removes that source. That creates continuing traffic/noise, so use manual watch only when you want that consequence; `watch list` inspects the relation/source and `watch cancel` removes only the manual source. An `@<session>` explanation request is point-to-point: use `spex session send <id> "<question>"`; the reply hint returns over send, never watch/wait. Only go `asking` when you genuinely need the human. This matters because the dashboard **folds each child under you and shows YOUR own status for the whole group** (session-nesting, no child-status aggregation), so a supervisor that stays parked-while-they-run is what makes that folded group status honest. Two footguns that bite a fresh supervisor. First: before `spex session close <id>`, confirm the merge landed (`git -C <root> log -1` shows HEAD at the new merge commit) — closing an unmerged branch discards the work. Second: `<id>` always names a WORKER YOU DISPATCHED, spelled out — never `.` and never your own id. `.` means the session running the command, so `close .` deletes your own worktree, branch and record mid-turn and takes your fleet's manager down with it; your own ending is a declaration (`done --propose close`), never a close you run on yourself. **DRAIN THE ISSUES** (issues / local-issues) as part of your loop: `spex issue ls` lists every open concern in one place — the taste concerns finished sessions recorded locally, AND the forge's issues, store-tagged. Cluster the same concern yourself (use judgment — duplicates are a recurrence SIGNAL, not noise; fold them into one) and weigh by recurrence AND novelty — **recurrence is salience, not importance, so never just fix the highest count**: a sharp single-voice concern can outrank a popular gripe. For the ones worth acting on, `spex session new "<task>"` a worker to land it (mention the concern's node as `[[<id>]]` if it has one), then `spex issue ls resolve <id> --as accepted|landed` (or `rejected`, with a reply saying why) so the store reflects the decision. Report progress as you go and when the goal is complete. Your goal follows:
@@ -37,7 +37,7 @@ EOF
37
37
  [ -n "$owner" ] || exit 0
38
38
 
39
39
  mkdir -p "$sdir"; : > "$sent"
40
- reason="Before accessing governed source $path, read its governing spec FIRST: $owner. Read the relevant NEIGHBORS too: the parent that scopes it, the siblings it borders, and the children that refine it. Then reconcile deliberately: change the spec if the intent is changing, or make the code honor it. The one forbidden move is code that silently diverges from its spec. (Fires once per session, at the first governed code read.)"
40
+ reason=$($S internal hook-prompt spec-first --path "$path" --owner "$owner") || exit 1
41
41
  esc=$(printf '%s' "$reason" | sed 's/\\/\\\\/g; s/"/\\"/g' | awk 'BEGIN{ORS=""} NR>1{print "\\n"} {print}')
42
42
  printf '{"decision":"block","reason":"%s"}\n' "$esc"
43
43
  exit 0
@@ -60,6 +60,7 @@ done <<EOF
60
60
  $paths
61
61
  EOF
62
62
  [ -n "$msg" ] || exit 0
63
+ msg=$($S internal hook-prompt spec-of-file --details "$msg") || exit 1
63
64
  esc=$(printf '%s' "$msg" | sed 's/\\/\\\\/g; s/"/\\"/g' | awk 'BEGIN{ORS=""} NR>1{print "\\n"} {print}')
64
65
  printf '{"hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":"%s"}}\n' "$esc"
65
66
  exit 0
@@ -10,9 +10,9 @@ block: true
10
10
  ---
11
11
  The blocking stop gate, with two jobs, each holding a hard loop-break so it never blocks twice on the same cause and never lets a dishonest stop through.
12
12
 
13
- The COMMIT gate keeps a done/merge proposal honest: such a proposal is rejected while the branch still carries uncommitted work or is zero commits ahead of main, because the ritual commits the spec and code BEFORE proposing. Clean work is allowed to stop; a dirty proposal blocks once with the reason, and if the agent ignores it the gate escapes by downgrading to `asking` so a false "ready to merge" can never stand.
13
+ The COMMIT gate keeps a done/merge proposal honest: such a proposal is rejected while the branch still carries uncommitted work or is zero commits ahead of main, because the ritual commits the spec and code BEFORE proposing. Clean work is allowed to stop; a dirty proposal blocks once with the reason, and if the agent ignores it the gate escapes by downgrading to `asking` so a false "ready to merge" can never stand. `done --propose nothing` is a porcelain trap, not a declaration: it writes no state and names merge, close, ask, and park as the real destinations; legacy `nothing` records remain readable.
14
14
 
15
- The DECLARE gate refuses to let a session stop in an undeclared `active` state, since a state is a claim the board and other agents act on, not a box ticked to end a turn. A declared state stops freely; an undeclared first stop emits `{"decision":"block"}` and the dispatcher exits 2 so the harness actually interrupts the stop and shows the reason; on the forced continuation it auto-declares a safe default committed work becomes `awaiting`, otherwise `asking` so the loop is guaranteed to end.
15
+ The DECLARE gate refuses to let a session stop in an undeclared `active` state, since a state is a claim the board and other agents act on, not a box ticked to end a turn. A declared state stops freely; an undeclared first stop emits `{"decision":"block"}` and the dispatcher exits 2 so the harness actually interrupts the stop and shows the reason; on the forced continuation it declares `asking`, so the loop is guaranteed to end without inventing a completion state. state owns the choice: a posted file or web artifact, a reported finding/recommendation awaiting a human decision, or any handoff awaiting human direction selects `asking`; `close-pending` requires genuinely settled work with no such outstanding decision or follow-up.
16
16
 
17
17
  The block text is where the declaration ritual is taught, so it is written to be read at two depths. The FULL teaching text prints once per session: it names the PATH-independent CLI once as a shared prefix, lists the five choices as a compact menu each with its application condition (park policed hardest — a false park is the most damaging mislabel). A real wake source is either a background job or a managed session-follow subscription whose normal send delivery will re-enter the parent; a caller without that address must still arm background `session wait`. It ends with the ordering discipline: declare LAST, then stop — a declaration followed by more tool calls honestly re-flips the record to active ([[mark-active]], by design), so making the declaration the turn's final call is what eliminates the park→block→re-park loop at its source. Every later undeclared stop in the same session gets a ONE-LINE version instead (a heavy session hits the gate 15-20 times a night; re-printing the full menu is token noise). The once-sentinel is a plain file beside the session record in the global store — the same per-session-sentinel mechanism as the CLI's note-truncation notice, never a second scheme. The terse line stays self-explanatory: it carries the command menu, the declare-LAST reminder, and the `spex help session` recovery entry, so an agent that never saw the full text (a compacted context) recovers every choice's condition from the entry rather than from memory — the whole full-to-terse information gap is closable from the line itself.
18
18
 
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env bash
2
2
  # @@@ stop-gate - a blocking Stop hook with TWO jobs, each with a HARD loop-break (never blocks more than
3
3
  # once on the same cause, never leaks a dishonest stop):
4
- # (A) COMMIT GATE — a done/merge proposal (awaiting + merge|nothing) is rejected while the node branch has
4
+ # (A) COMMIT GATE — a done/merge proposal (awaiting + merge; legacy nothing remains readable) is rejected while the node branch has
5
5
  # uncommitted work or 0 commits ahead of main; the dogfood ritual commits BEFORE proposing. Clean ->
6
6
  # allow; dirty -> block once with the reason, escape on the continuation to `asking` (needs the human).
7
7
  # (B) DECLARE GATE — a session may not stop in an undeclared (`active`) state:
8
8
  # declared (awaiting/parked/error/asking) . allow (the agent reported; nothing to do)
9
9
  # active, first stop (stop_hook_active false) .. block ONCE — instruct the agent to declare
10
- # active, the continuation (stop_hook_active true) auto-declare a safe default (awaiting/nothing if
11
- # committed, else `asking` needs the human) and allow. Guaranteed to end.
10
+ # active, the continuation (stop_hook_active true) auto-declare `asking` and allow. Guaranteed to end
11
+ # without inventing a completion state.
12
12
  # $SPEX is the PATH-independent CLI invocation (abs tsx + cli) injected by settingsArg, so the gate's own
13
13
  # auto-default AND the command it shows the agent both work even when `spex` is absent from PATH.
14
14
  # @@@ global store + governed gate - state lives in the per-session GLOBAL record session.json (keyed by the
@@ -65,12 +65,13 @@ eval_advisory() {
65
65
  n=$(printf '%s\n' "$out" | grep -cE 'eval-(drift|missing|coverage):')
66
66
  [ "${n:-0}" -gt 0 ] || return 0 # no gap in what you changed (or eval lint unavailable) -> nothing to nudge
67
67
  ids=$(printf '%s\n' "$out" | sed -n "s/.*eval-[a-z]*: '\([^']*\)'.*/\1/p" | awk '!seen[$0]++' | head -6 | paste -sd' ' -)
68
- msg="eval — the loss signal the optimizer reads — flags ${n} gap(s) in nodes you changed: ${ids}. A node whose score went stale/unmeasured: re-measure it — PRODUCE the measurement YOURSELF with a real run of the scenario's actual surface (its tag on the \`spex eval lint --changed\` line tells you WHICH surface to run), compare to expected, and file it with \`spex eval add <node>\`; don't desk-check it, and don't defer to reviewing a recording after the fact. A FRONTEND node with no eval.md: give it one (a scenario — description + expected), since an obvious UI change should carry a loss signal. \`spex eval lint --changed\` lists them. (Advisory — fires once, not a gate.)"
68
+ msg=$($S internal hook-prompt stop-gate --variant eval --count "$n" --ids "$ids") || return 0
69
69
  esc=$(printf '%s' "$msg" | sed 's/\\/\\\\/g; s/"/\\"/g')
70
70
  printf '{"hookSpecificOutput":{"hookEventName":"Stop","additionalContext":"%s"}}\n' "$esc"
71
71
  }
72
72
 
73
- # @@@ commit gate - a declaration of done/merge (awaiting + proposal merge|nothing) is only honest once the
73
+ # @@@ commit gate - a declaration of done/merge (awaiting + proposal merge; legacy nothing is accepted only
74
+ # for backward-readable records) is only honest once the
74
75
  # node branch carries the work as COMMITS: the dogfood ritual commits spec+code BEFORE any proposal, yet a
75
76
  # dashboard-launched agent kept proposing merge with 0 commits / a dirty tree. So before allowing such a
76
77
  # declaration we run the deterministic check (`spex internal commit-gate`, which goes through git.ts's git()
@@ -79,8 +80,8 @@ eval_advisory() {
79
80
  # it) escape the loop by downgrading to `asking` (needs the human) with a clear note, so a FALSE "ready to
80
81
  # merge" never stands. (A propose-close declaration is exempt — it discards the worktree, so commits are moot.)
81
82
  # The PROPOSAL rides into the check: `merge` claims there is committed work to land (so 0-ahead blocks too),
82
- # `nothing` claims only that the work is committed (so a clean tree is the whole gate — a lane whose work
83
- # already merged may pause for the human without minting an empty commit to get through).
83
+ # New public `done --propose nothing` calls trap before this hook; keep its legacy branch so historic records
84
+ # stop normally instead of becoming corrupt.
84
85
  if [ "${status:-active}" = awaiting ] && { [ "$proposal" = merge ] || [ "$proposal" = nothing ]; }; then
85
86
  if gatemsg=$($S internal commit-gate "$proposal" 2>&1); then
86
87
  # nudge ONCE: emit on the natural stop, but STAY SILENT on the forced re-stop the additionalContext
@@ -93,28 +94,19 @@ if [ "${status:-active}" = awaiting ] && { [ "$proposal" = merge ] || [ "$propos
93
94
  $S session ask --session "$sid" --note "stopped with uncommitted work — commit your spec+code on the node branch, then re-declare done" >/dev/null 2>&1 || true
94
95
  exit 0
95
96
  fi
96
- esc=$(printf '%s' "$gatemsg" | sed 's/[\\"]/\\&/g')
97
- printf '{"decision":"block","reason":"Not ready to declare done: %s. The dogfood ritual lands every change as a git commit on your node branch BEFORE you propose. Commit your spec.md + code on this node branch (spec: <id> — <reason>, with a Session: trailer), then re-run %s session done --propose %s."}\n' "$esc" "$S" "$proposal"
97
+ reason=$($S internal hook-prompt stop-gate --variant commit --reason "$gatemsg" --cli "$S" --propose "$proposal") || exit 1
98
+ esc=$(printf '%s' "$reason" | sed 's/[\\"]/\\&/g')
99
+ printf '{"decision":"block","reason":"%s"}\n' "$esc"
98
100
  exit 0
99
101
  fi
100
102
 
101
- # any OTHER already-declared state (parked / error / asking / awaiting+close) -> let it stop.
103
+ # Any other declared state (parked / error / asking / awaiting+close, plus legacy awaiting+nothing) stops.
102
104
  [ "${status:-active}" != "active" ] && exit 0
103
105
 
104
106
  if [ "$cont" = true ]; then
105
- # the forced continuation also stopped without declaring -> escape the loop, don't block. Keep the commit
106
- # gate airtight: default to awaiting/nothing only when the tree is actually clean; otherwise an undeclared
107
- # stop with uncommitted work becomes `asking` (needs the human), never a false awaiting/done. The default
108
- # IS `nothing`, so it is judged as `nothing` — an already-merged lane defaults honestly instead of being
109
- # pushed to `asking` for having nothing left ahead of main.
110
- if $S internal commit-gate nothing >/dev/null 2>&1; then
111
- $S internal session-state awaiting --session "$sid" --propose nothing --note "auto: stopped without declaring" >/dev/null 2>&1 || true
112
- # NOTE: no eval nudge on the auto-declare path. It only runs on the forced continuation (cont=true),
113
- # where a guarded advisory could never fire anyway, and an unguarded one was a second loop vector (a
114
- # mark-active tool call could re-enter this branch). The clean-done path above is the single nudge site.
115
- else
116
- $S session ask --session "$sid" --note "auto: stopped without declaring and with uncommitted work — commit your spec+code on the node branch, then declare" >/dev/null 2>&1 || true
117
- fi
107
+ # The forced continuation also stopped without declaring. Escape into asking: no default may invent a
108
+ # completed lane, and the stopped agent now needs a human prompt to choose merge, close, ask, or park.
109
+ $S session ask --session "$sid" --note "auto: stopped without declaring — choose merge, close, ask, or park; done --propose nothing records no state" >/dev/null 2>&1 || true
118
110
  exit 0
119
111
  fi
120
112
 
@@ -129,19 +121,29 @@ fi
129
121
  # of the full-to-terse information gap is recoverable from the entry, none of it from memory.
130
122
  taught="$sdir/stop-gate-taught"
131
123
  if [ -f "$taught" ]; then
132
- printf '{"decision":"block","reason":"undeclared stop — declare the ONE true state as your LAST call: `%s session <done --propose merge (review; ONLY clickable merge)|nothing (done; no merge)|close (close-pending) / park (parked; managed watch delivery or real background wake-up) / ask (asking; human reply)>`. Conditions: `%s help session`."}\n' "$S" "$S"
124
+ reason=$($S internal hook-prompt stop-gate --variant terse --cli "$S") || exit 1
125
+ esc=$(printf '%s' "$reason" | sed 's/[\\"]/\\&/g')
126
+ printf '{"decision":"block","reason":"%s"}\n' "$esc"
133
127
  exit 0
134
128
  fi
135
129
  touch "$taught" 2>/dev/null || true
136
130
  # The full reason names the PATH-independent CLI ($S) ONCE as a shared `<CLI> session <choice>` prefix, then
137
- # lists the five choices as a compact newline menu of bare subcommands — so the terminal output stays legible
138
- # instead of repeating the long abs path per option. It EMPHASIZES that each state is a CLAIM others act on
139
- # (not a box to tick to end the turn) and gives the precise APPLICATION CONDITION for each — so the agent
140
- # picks the TRUE one. park is policed hardest because a false park (no real background task) reads on the
131
+ # lists the four real states plus the `nothing` trap as a compact newline menu of bare subcommands — so the
132
+ # terminal output stays legible instead of repeating the long abs path per option. It EMPHASIZES that each
133
+ # state is a CLAIM others act on (not a box to tick to end the turn) and gives the precise APPLICATION CONDITION
134
+ # for each — so the agent picks the TRUE one. park is policed hardest because a false park (no real background task) reads on the
141
135
  # board as "fine, self-resuming" when the agent actually needs the human, which is the most damaging mislabel.
142
136
  # It ends with the ORDERING discipline — declare LAST, then stop — because a declaration followed by more
143
137
  # tool calls honestly re-flips the record to active (mark-active, by design) and re-blocks the next stop;
144
138
  # this block text is the one place every undeclared stopper is guaranteed to read, so the teaching that
145
139
  # kills the park->block->re-park loop at its source lives here.
146
- printf '{"decision":"block","reason":"Your session state is a CLAIM the graph, your supervisor, and other agents act on — not a box to tick to end the turn. Stopping undeclared makes your outcome a guess. Pick the ONE that is TRUE right now and run `%s session <choice>`, choosing the <choice> whose condition holds:\\n • done --propose merge — spec+code COMMITTED on the branch and genuinely ready for human review. It declares REVIEW and is the ONLY proposal that offers a clickable merge.\\n • done --propose nothing — committed, but you are NOT proposing a merge; paused for the human to look. It declares DONE, never a merge.\\n • done --propose close — you PROPOSE discarding this worktree; the human performs the close. It declares CLOSE-PENDING, not merge. This is how a session ends ITSELF — never run `session close` on your own id, which would delete your worktree mid-turn.\\n • ask --note <your-question> — you need the human: a real question, or you are simply stopped awaiting direction. It declares ASKING and resumes only when they reply.\\n • park --note <what-you-await> — ONLY when a real wake-up will resume you: a managed spex session watch subscription whose send delivery reaches you, or a spex session wait you backgrounded/a running build/job. It declares PARKED and self-resumes. With neither, you are waiting on the human: use ask, never park as a default.\\n\\nDECLARE LAST, THEN STOP: finish everything else in the turn first — speak, send your messages, establish managed watches or arm background waits — and make the declaration your FINAL call. Any tool call AFTER it flips your record back to active (mark-active, by design: activity is activity), so the next stop re-blocks and demands a fresh declaration; declaring last kills that loop at its source.\\n\\n(This full explanation shows once per session; later undeclared stops get a one-line reminder. `%s help session` re-explains the choices any time.)"}\n' "$S" "$S"
140
+ if [ -s "$sdir/files.json" ] && grep -qE '"[^"]+"' "$sdir/files.json"; then
141
+ reason=$($S internal hook-prompt stop-gate --variant artifact) || exit 1
142
+ esc=$(printf '%s' "$reason" | sed 's/[\\"]/\\&/g')
143
+ printf '{"decision":"block","reason":"%s"}\n' "$esc"
144
+ exit 0
145
+ fi
146
+ reason=$($S internal hook-prompt stop-gate --variant full --cli "$S") || exit 1
147
+ esc=$(printf '%s' "$reason" | sed 's/\\/\\\\/g; s/"/\\"/g' | awk 'BEGIN{ORS=""} NR>1{print "\\n"} {print}')
148
+ printf '{"decision":"block","reason":"%s"}\n' "$esc"
147
149
  exit 0