spexcode 0.6.2 → 0.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (495) hide show
  1. package/README.md +3 -1
  2. package/bin/spex.mjs +4 -0
  3. package/node_modules/@spexcode/session-core/dist/delivery-queue.d.ts +23 -0
  4. package/node_modules/@spexcode/session-core/dist/delivery-queue.js +195 -0
  5. package/node_modules/@spexcode/session-core/dist/index.d.ts +5 -0
  6. package/node_modules/@spexcode/session-core/dist/index.js +5 -0
  7. package/node_modules/@spexcode/session-core/dist/internal.d.ts +3 -0
  8. package/node_modules/@spexcode/session-core/dist/internal.js +3 -0
  9. package/node_modules/@spexcode/session-core/dist/message.d.ts +22 -0
  10. package/node_modules/@spexcode/session-core/dist/message.js +52 -0
  11. package/node_modules/@spexcode/session-core/dist/record-lock.d.ts +7 -0
  12. package/node_modules/@spexcode/session-core/dist/record-lock.js +152 -0
  13. package/node_modules/@spexcode/session-core/dist/runtime-session.d.ts +50 -0
  14. package/node_modules/@spexcode/session-core/dist/runtime-session.js +286 -0
  15. package/node_modules/@spexcode/session-core/dist/session-cursors.d.ts +14 -0
  16. package/node_modules/@spexcode/session-core/dist/session-cursors.js +82 -0
  17. package/node_modules/@spexcode/session-core/dist/session-timeline.d.ts +46 -0
  18. package/node_modules/@spexcode/session-core/dist/session-timeline.js +216 -0
  19. package/node_modules/@spexcode/session-core/package.json +33 -0
  20. package/node_modules/@spexcode/spec-cli/bin/spex.mjs +77 -0
  21. package/node_modules/@spexcode/spec-cli/dist/attach.d.ts +2 -0
  22. package/node_modules/@spexcode/spec-cli/dist/attach.js +48 -0
  23. package/node_modules/@spexcode/spec-cli/dist/claude-headless.d.ts +36 -0
  24. package/node_modules/@spexcode/spec-cli/dist/claude-headless.js +316 -0
  25. package/node_modules/@spexcode/spec-cli/dist/cli.d.ts +1 -0
  26. package/node_modules/@spexcode/spec-cli/dist/cli.js +1908 -0
  27. package/node_modules/@spexcode/spec-cli/dist/client.d.ts +112 -0
  28. package/node_modules/@spexcode/spec-cli/dist/client.js +481 -0
  29. package/node_modules/@spexcode/spec-cli/dist/cockpit.d.ts +7 -0
  30. package/node_modules/@spexcode/spec-cli/dist/cockpit.js +26 -0
  31. package/node_modules/@spexcode/spec-cli/dist/codex-headless.d.ts +1 -0
  32. package/{spec-cli/src/codex-headless.ts → node_modules/@spexcode/spec-cli/dist/codex-headless.js} +3 -9
  33. package/node_modules/@spexcode/spec-cli/dist/codex-runtime-generations.d.ts +58 -0
  34. package/node_modules/@spexcode/spec-cli/dist/codex-runtime-generations.js +798 -0
  35. package/node_modules/@spexcode/spec-cli/dist/commit-surgery.d.ts +1 -0
  36. package/node_modules/@spexcode/spec-cli/dist/commit-surgery.js +90 -0
  37. package/node_modules/@spexcode/spec-cli/dist/contract-filter.d.ts +14 -0
  38. package/node_modules/@spexcode/spec-cli/dist/contract-filter.js +219 -0
  39. package/node_modules/@spexcode/spec-cli/dist/dashboard-assets.d.ts +6 -0
  40. package/node_modules/@spexcode/spec-cli/dist/dashboard-assets.js +50 -0
  41. package/node_modules/@spexcode/spec-cli/dist/doctor.d.ts +9 -0
  42. package/node_modules/@spexcode/spec-cli/dist/doctor.js +553 -0
  43. package/node_modules/@spexcode/spec-cli/dist/eval-host.d.ts +1 -0
  44. package/node_modules/@spexcode/spec-cli/dist/eval-host.js +19 -0
  45. package/node_modules/@spexcode/spec-cli/dist/execution-trace.d.ts +26 -0
  46. package/node_modules/@spexcode/spec-cli/dist/execution-trace.js +475 -0
  47. package/node_modules/@spexcode/spec-cli/dist/file-write.d.ts +2 -0
  48. package/node_modules/@spexcode/spec-cli/dist/file-write.js +26 -0
  49. package/node_modules/@spexcode/spec-cli/dist/flat.d.ts +89 -0
  50. package/node_modules/@spexcode/spec-cli/dist/flat.js +1076 -0
  51. package/node_modules/@spexcode/spec-cli/dist/gateway-auth.d.ts +56 -0
  52. package/node_modules/@spexcode/spec-cli/dist/gateway-auth.js +168 -0
  53. package/node_modules/@spexcode/spec-cli/dist/gateway-hub.d.ts +28 -0
  54. package/node_modules/@spexcode/spec-cli/dist/gateway-hub.js +363 -0
  55. package/node_modules/@spexcode/spec-cli/dist/gateway.d.ts +38 -0
  56. package/node_modules/@spexcode/spec-cli/dist/gateway.js +527 -0
  57. package/node_modules/@spexcode/spec-cli/dist/graphCache.d.ts +27 -0
  58. package/node_modules/@spexcode/spec-cli/dist/graphCache.js +717 -0
  59. package/node_modules/@spexcode/spec-cli/dist/graphSnapshot.d.ts +14 -0
  60. package/node_modules/@spexcode/spec-cli/dist/graphSnapshot.js +36 -0
  61. package/node_modules/@spexcode/spec-cli/dist/graphStream.d.ts +85 -0
  62. package/node_modules/@spexcode/spec-cli/dist/graphStream.js +1195 -0
  63. package/node_modules/@spexcode/spec-cli/dist/guidance-catalog.d.ts +64 -0
  64. package/node_modules/@spexcode/spec-cli/dist/guidance-catalog.js +166 -0
  65. package/node_modules/@spexcode/spec-cli/dist/guide.d.ts +8 -0
  66. package/{spec-cli/src/guide.ts → node_modules/@spexcode/spec-cli/dist/guide.js} +36 -32
  67. package/node_modules/@spexcode/spec-cli/dist/harness-select.d.ts +18 -0
  68. package/node_modules/@spexcode/spec-cli/dist/harness-select.js +62 -0
  69. package/node_modules/@spexcode/spec-cli/dist/harness.d.ts +338 -0
  70. package/node_modules/@spexcode/spec-cli/dist/harness.js +3146 -0
  71. package/node_modules/@spexcode/spec-cli/dist/headless-controller.d.ts +9 -0
  72. package/node_modules/@spexcode/spec-cli/dist/headless-controller.js +47 -0
  73. package/node_modules/@spexcode/spec-cli/dist/help.d.ts +15 -0
  74. package/node_modules/@spexcode/spec-cli/dist/help.js +594 -0
  75. package/node_modules/@spexcode/spec-cli/dist/hook-prompts.d.ts +18 -0
  76. package/node_modules/@spexcode/spec-cli/dist/hook-prompts.js +122 -0
  77. package/node_modules/@spexcode/spec-cli/dist/hooks.d.ts +1 -0
  78. package/{spec-cli/src/hooks.ts → node_modules/@spexcode/spec-cli/dist/hooks.js} +15 -15
  79. package/node_modules/@spexcode/spec-cli/dist/host-resources.d.ts +97 -0
  80. package/node_modules/@spexcode/spec-cli/dist/host-resources.js +795 -0
  81. package/node_modules/@spexcode/spec-cli/dist/host.d.ts +88 -0
  82. package/node_modules/@spexcode/spec-cli/dist/host.js +663 -0
  83. package/node_modules/@spexcode/spec-cli/dist/index.d.ts +1 -0
  84. package/node_modules/@spexcode/spec-cli/dist/index.js +899 -0
  85. package/node_modules/@spexcode/spec-cli/dist/init.d.ts +1 -0
  86. package/node_modules/@spexcode/spec-cli/dist/init.js +284 -0
  87. package/node_modules/@spexcode/spec-cli/dist/issues-cli.d.ts +6 -0
  88. package/node_modules/@spexcode/spec-cli/dist/issues-cli.js +334 -0
  89. package/node_modules/@spexcode/spec-cli/dist/issues.d.ts +69 -0
  90. package/node_modules/@spexcode/spec-cli/dist/issues.js +189 -0
  91. package/node_modules/@spexcode/spec-cli/dist/lint.d.ts +34 -0
  92. package/node_modules/@spexcode/spec-cli/dist/lint.js +476 -0
  93. package/node_modules/@spexcode/spec-cli/dist/listen.d.ts +8 -0
  94. package/node_modules/@spexcode/spec-cli/dist/listen.js +24 -0
  95. package/node_modules/@spexcode/spec-cli/dist/localIssues.d.ts +65 -0
  96. package/node_modules/@spexcode/spec-cli/dist/localIssues.js +582 -0
  97. package/node_modules/@spexcode/spec-cli/dist/login-page.d.ts +5 -0
  98. package/{spec-cli/src/login-page.ts → node_modules/@spexcode/spec-cli/dist/login-page.js} +7 -7
  99. package/node_modules/@spexcode/spec-cli/dist/loop-in.d.ts +23 -0
  100. package/{spec-cli/src/loop-in.ts → node_modules/@spexcode/spec-cli/dist/loop-in.js} +35 -45
  101. package/node_modules/@spexcode/spec-cli/dist/machine-peer.d.ts +69 -0
  102. package/node_modules/@spexcode/spec-cli/dist/machine-peer.js +603 -0
  103. package/node_modules/@spexcode/spec-cli/dist/materialize.d.ts +18 -0
  104. package/node_modules/@spexcode/spec-cli/dist/materialize.js +468 -0
  105. package/node_modules/@spexcode/spec-cli/dist/mentions.d.ts +50 -0
  106. package/node_modules/@spexcode/spec-cli/dist/mentions.js +117 -0
  107. package/node_modules/@spexcode/spec-cli/dist/opencode-headless.d.ts +5 -0
  108. package/node_modules/@spexcode/spec-cli/dist/opencode-headless.js +215 -0
  109. package/node_modules/@spexcode/spec-cli/dist/opencode.d.ts +3 -0
  110. package/{spec-cli/src/opencode.ts → node_modules/@spexcode/spec-cli/dist/opencode.js} +9 -13
  111. package/node_modules/@spexcode/spec-cli/dist/pi-harness.d.ts +3 -0
  112. package/{spec-cli/src/pi-harness.ts → node_modules/@spexcode/spec-cli/dist/pi-harness.js} +45 -40
  113. package/node_modules/@spexcode/spec-cli/dist/pi-headless.d.ts +25 -0
  114. package/node_modules/@spexcode/spec-cli/dist/pi-headless.js +196 -0
  115. package/node_modules/@spexcode/spec-cli/dist/plugin-harness.d.ts +21 -0
  116. package/node_modules/@spexcode/spec-cli/dist/plugin-harness.js +146 -0
  117. package/node_modules/@spexcode/spec-cli/dist/pty-bridge.d.ts +10 -0
  118. package/node_modules/@spexcode/spec-cli/dist/pty-bridge.js +404 -0
  119. package/node_modules/@spexcode/spec-cli/dist/pty-helper.d.mts +1 -0
  120. package/node_modules/@spexcode/spec-cli/dist/pty-helper.mjs +101 -0
  121. package/node_modules/@spexcode/spec-cli/dist/pty-native-helper.d.mts +2 -0
  122. package/node_modules/@spexcode/spec-cli/dist/pty-native-helper.mjs +23 -0
  123. package/node_modules/@spexcode/spec-cli/dist/public-graph.d.ts +46 -0
  124. package/node_modules/@spexcode/spec-cli/dist/public-graph.js +56 -0
  125. package/node_modules/@spexcode/spec-cli/dist/ranker.d.ts +15 -0
  126. package/node_modules/@spexcode/spec-cli/dist/ranker.js +188 -0
  127. package/node_modules/@spexcode/spec-cli/dist/reaper.d.ts +6 -0
  128. package/node_modules/@spexcode/spec-cli/dist/reaper.js +81 -0
  129. package/node_modules/@spexcode/spec-cli/dist/reviews.d.ts +189 -0
  130. package/node_modules/@spexcode/spec-cli/dist/reviews.js +350 -0
  131. package/node_modules/@spexcode/spec-cli/dist/runtime-guard.d.ts +6 -0
  132. package/node_modules/@spexcode/spec-cli/dist/runtime-guard.js +37 -0
  133. package/node_modules/@spexcode/spec-cli/dist/runtime-ownership.d.ts +21 -0
  134. package/node_modules/@spexcode/spec-cli/dist/runtime-ownership.js +84 -0
  135. package/node_modules/@spexcode/spec-cli/dist/runtime-rotate.d.ts +1 -0
  136. package/node_modules/@spexcode/spec-cli/dist/runtime-rotate.js +58 -0
  137. package/node_modules/@spexcode/spec-cli/dist/search.bench.d.mts +1 -0
  138. package/node_modules/@spexcode/spec-cli/dist/search.bench.mjs +75 -0
  139. package/node_modules/@spexcode/spec-cli/dist/search.d.ts +20 -0
  140. package/node_modules/@spexcode/spec-cli/dist/search.js +61 -0
  141. package/node_modules/@spexcode/spec-cli/dist/session-declarations.d.ts +13 -0
  142. package/node_modules/@spexcode/spec-cli/dist/session-declarations.js +126 -0
  143. package/node_modules/@spexcode/spec-cli/dist/session-execution.d.ts +10 -0
  144. package/node_modules/@spexcode/spec-cli/dist/session-execution.js +70 -0
  145. package/node_modules/@spexcode/spec-cli/dist/session-files.d.ts +28 -0
  146. package/node_modules/@spexcode/spec-cli/dist/session-files.js +130 -0
  147. package/node_modules/@spexcode/spec-cli/dist/session-follow.d.ts +31 -0
  148. package/node_modules/@spexcode/spec-cli/dist/session-follow.js +179 -0
  149. package/node_modules/@spexcode/spec-cli/dist/session-reparent.d.ts +10 -0
  150. package/node_modules/@spexcode/spec-cli/dist/session-reparent.js +33 -0
  151. package/node_modules/@spexcode/spec-cli/dist/session-timeline.d.ts +11 -0
  152. package/node_modules/@spexcode/spec-cli/dist/session-timeline.js +20 -0
  153. package/node_modules/@spexcode/spec-cli/dist/session-web.d.ts +24 -0
  154. package/node_modules/@spexcode/spec-cli/dist/session-web.js +141 -0
  155. package/node_modules/@spexcode/spec-cli/dist/sessions.d.ts +433 -0
  156. package/node_modules/@spexcode/spec-cli/dist/sessions.js +4667 -0
  157. package/node_modules/@spexcode/spec-cli/dist/sh.d.ts +1 -0
  158. package/node_modules/@spexcode/spec-cli/dist/sh.js +3 -0
  159. package/node_modules/@spexcode/spec-cli/dist/shim-runtime.d.ts +1 -0
  160. package/{spec-cli/src/shim-runtime.ts → node_modules/@spexcode/spec-cli/dist/shim-runtime.js} +3 -4
  161. package/node_modules/@spexcode/spec-cli/dist/slash-commands.d.ts +9 -0
  162. package/node_modules/@spexcode/spec-cli/dist/slash-commands.js +307 -0
  163. package/node_modules/@spexcode/spec-cli/dist/source-files.d.ts +9 -0
  164. package/node_modules/@spexcode/spec-cli/dist/source-files.js +113 -0
  165. package/node_modules/@spexcode/spec-cli/dist/supervise.d.ts +1 -0
  166. package/node_modules/@spexcode/spec-cli/dist/supervise.js +308 -0
  167. package/node_modules/@spexcode/spec-cli/dist/tree.d.ts +25 -0
  168. package/node_modules/@spexcode/spec-cli/dist/tree.js +98 -0
  169. package/node_modules/@spexcode/spec-cli/dist/tsx-bin.d.ts +3 -0
  170. package/node_modules/@spexcode/spec-cli/dist/tsx-bin.js +41 -0
  171. package/node_modules/@spexcode/spec-cli/dist/uninstall.d.ts +3 -0
  172. package/node_modules/@spexcode/spec-cli/dist/uninstall.js +177 -0
  173. package/node_modules/@spexcode/spec-cli/dist/uploads.d.ts +36 -0
  174. package/node_modules/@spexcode/spec-cli/dist/uploads.js +243 -0
  175. package/node_modules/@spexcode/spec-cli/dist/worktree-sources.d.ts +1 -0
  176. package/node_modules/@spexcode/spec-cli/dist/worktree-sources.js +47 -0
  177. package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/dispatch.sh +5 -5
  178. package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/harness.sh +1 -1
  179. package/node_modules/@spexcode/spec-cli/package.json +49 -0
  180. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/commands/supervisor/spec.md +8 -0
  181. package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-first/spec-first.sh +1 -1
  182. package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-of-file/spec-of-file.sh +1 -0
  183. package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/stop-gate/spec.md +2 -2
  184. package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/stop-gate/stop-gate.sh +31 -29
  185. package/node_modules/@spexcode/spec-core/dist/anchors.d.ts +94 -0
  186. package/node_modules/@spexcode/spec-core/dist/anchors.js +730 -0
  187. package/node_modules/@spexcode/spec-core/dist/git.d.ts +170 -0
  188. package/node_modules/@spexcode/spec-core/dist/git.js +2768 -0
  189. package/node_modules/@spexcode/spec-core/dist/graph-delta.d.ts +44 -0
  190. package/node_modules/@spexcode/spec-core/dist/graph-delta.js +72 -0
  191. package/node_modules/@spexcode/spec-core/dist/graph.d.ts +34 -0
  192. package/node_modules/@spexcode/spec-core/dist/graph.js +237 -0
  193. package/node_modules/@spexcode/spec-core/dist/graphDelta.d.ts +3 -0
  194. package/node_modules/@spexcode/spec-core/dist/graphDelta.js +13 -0
  195. package/node_modules/@spexcode/spec-core/dist/harness-identity.d.ts +31 -0
  196. package/node_modules/@spexcode/spec-core/dist/harness-identity.js +20 -0
  197. package/node_modules/@spexcode/spec-core/dist/identity-presets.d.ts +152 -0
  198. package/node_modules/@spexcode/spec-core/dist/identity-presets.js +132 -0
  199. package/node_modules/@spexcode/spec-core/dist/index.d.ts +45 -0
  200. package/node_modules/@spexcode/spec-core/dist/index.js +19 -0
  201. package/node_modules/@spexcode/spec-core/dist/layout.d.ts +187 -0
  202. package/node_modules/@spexcode/spec-core/dist/layout.js +548 -0
  203. package/node_modules/@spexcode/spec-core/dist/process-identity.d.ts +37 -0
  204. package/node_modules/@spexcode/spec-core/dist/process-identity.js +214 -0
  205. package/node_modules/@spexcode/spec-core/dist/project-identity.d.ts +12 -0
  206. package/node_modules/@spexcode/spec-core/dist/project-identity.js +71 -0
  207. package/node_modules/@spexcode/spec-core/dist/project-store.d.ts +3 -0
  208. package/node_modules/@spexcode/spec-core/dist/project-store.js +14 -0
  209. package/node_modules/@spexcode/spec-core/dist/resilience.d.ts +2 -0
  210. package/node_modules/@spexcode/spec-core/dist/resilience.js +40 -0
  211. package/node_modules/@spexcode/spec-core/dist/review/index.d.ts +3 -0
  212. package/node_modules/@spexcode/spec-core/dist/review/index.js +5 -0
  213. package/node_modules/@spexcode/spec-core/dist/review/reviewFilters.d.ts +77 -0
  214. package/node_modules/@spexcode/spec-core/dist/review/reviewFilters.js +308 -0
  215. package/node_modules/@spexcode/spec-core/dist/review/reviewQuery.d.ts +66 -0
  216. package/node_modules/@spexcode/spec-core/dist/review/reviewQuery.js +180 -0
  217. package/node_modules/@spexcode/spec-core/dist/review/session.d.ts +4 -0
  218. package/node_modules/@spexcode/spec-core/dist/review/session.js +8 -0
  219. package/node_modules/@spexcode/spec-core/dist/reviewSnapshot.d.ts +15 -0
  220. package/node_modules/@spexcode/spec-core/dist/reviewSnapshot.js +12 -0
  221. package/node_modules/@spexcode/spec-core/dist/root-lru.d.ts +4 -0
  222. package/{spec-cli/src/root-lru.ts → node_modules/@spexcode/spec-core/dist/root-lru.js} +26 -30
  223. package/node_modules/@spexcode/spec-core/dist/specs.d.ts +117 -0
  224. package/node_modules/@spexcode/spec-core/dist/specs.js +489 -0
  225. package/node_modules/@spexcode/spec-core/package.json +28 -0
  226. package/node_modules/@spexcode/spec-eval/dist/cache.d.ts +10 -0
  227. package/node_modules/@spexcode/spec-eval/dist/cache.js +50 -0
  228. package/node_modules/@spexcode/spec-eval/dist/cli.d.ts +22 -0
  229. package/node_modules/@spexcode/spec-eval/dist/cli.js +981 -0
  230. package/node_modules/@spexcode/spec-eval/dist/evaltab.d.ts +98 -0
  231. package/node_modules/@spexcode/spec-eval/dist/evaltab.js +176 -0
  232. package/node_modules/@spexcode/spec-eval/dist/filing.d.ts +16 -0
  233. package/node_modules/@spexcode/spec-eval/dist/filing.js +36 -0
  234. package/node_modules/@spexcode/spec-eval/dist/freshness.d.ts +48 -0
  235. package/node_modules/@spexcode/spec-eval/dist/freshness.js +799 -0
  236. package/node_modules/@spexcode/spec-eval/dist/host.d.ts +46 -0
  237. package/node_modules/@spexcode/spec-eval/dist/host.js +53 -0
  238. package/node_modules/@spexcode/spec-eval/dist/humanok.d.ts +11 -0
  239. package/node_modules/@spexcode/spec-eval/dist/humanok.js +27 -0
  240. package/node_modules/@spexcode/spec-eval/dist/index.d.ts +4 -0
  241. package/node_modules/@spexcode/spec-eval/dist/index.js +4 -0
  242. package/node_modules/@spexcode/spec-eval/dist/remarks.d.ts +31 -0
  243. package/node_modules/@spexcode/spec-eval/dist/remarks.js +1 -0
  244. package/node_modules/@spexcode/spec-eval/dist/scenariofresh.d.ts +13 -0
  245. package/node_modules/@spexcode/spec-eval/dist/scenariofresh.js +303 -0
  246. package/node_modules/@spexcode/spec-eval/dist/scenarios.d.ts +99 -0
  247. package/node_modules/@spexcode/spec-eval/dist/scenarios.js +650 -0
  248. package/node_modules/@spexcode/spec-eval/dist/sessioneval.d.ts +307 -0
  249. package/node_modules/@spexcode/spec-eval/dist/sessioneval.js +1883 -0
  250. package/node_modules/@spexcode/spec-eval/dist/sidecar.d.ts +55 -0
  251. package/node_modules/@spexcode/spec-eval/dist/sidecar.js +82 -0
  252. package/node_modules/@spexcode/spec-eval/dist/timeline.d.ts +25 -0
  253. package/node_modules/@spexcode/spec-eval/dist/timeline.js +65 -0
  254. package/node_modules/@spexcode/spec-eval/dist/ui-path.d.ts +1 -0
  255. package/node_modules/@spexcode/spec-eval/dist/ui-path.js +2 -0
  256. package/node_modules/@spexcode/spec-eval/package.json +36 -0
  257. package/node_modules/@spexcode/spec-forge/dist/cache.d.ts +29 -0
  258. package/node_modules/@spexcode/spec-forge/dist/cache.js +59 -0
  259. package/node_modules/@spexcode/spec-forge/dist/cli.d.ts +1 -0
  260. package/node_modules/@spexcode/spec-forge/dist/cli.js +100 -0
  261. package/node_modules/@spexcode/spec-forge/dist/drivers/github.d.ts +2 -0
  262. package/node_modules/@spexcode/spec-forge/dist/drivers/github.js +116 -0
  263. package/node_modules/@spexcode/spec-forge/dist/drivers/gitlab.d.ts +7 -0
  264. package/node_modules/@spexcode/spec-forge/dist/drivers/gitlab.js +144 -0
  265. package/node_modules/@spexcode/spec-forge/dist/drivers.d.ts +10 -0
  266. package/node_modules/@spexcode/spec-forge/dist/drivers.js +83 -0
  267. package/node_modules/@spexcode/spec-forge/dist/index.d.ts +4 -0
  268. package/node_modules/@spexcode/spec-forge/dist/index.js +4 -0
  269. package/node_modules/@spexcode/spec-forge/dist/links.d.ts +15 -0
  270. package/node_modules/@spexcode/spec-forge/dist/links.js +65 -0
  271. package/node_modules/@spexcode/spec-forge/dist/needs-eval.d.ts +10 -0
  272. package/node_modules/@spexcode/spec-forge/dist/needs-eval.js +20 -0
  273. package/node_modules/@spexcode/spec-forge/dist/port.d.ts +53 -0
  274. package/node_modules/@spexcode/spec-forge/dist/port.js +1 -0
  275. package/node_modules/@spexcode/spec-forge/dist/resident.d.ts +7 -0
  276. package/node_modules/@spexcode/spec-forge/dist/resident.js +43 -0
  277. package/node_modules/@spexcode/spec-forge/package.json +32 -0
  278. package/package.json +33 -19
  279. package/spec-cli/bin/spex.mjs +0 -69
  280. package/spec-cli/src/anchors.ts +0 -728
  281. package/spec-cli/src/attach.ts +0 -43
  282. package/spec-cli/src/claude-headless.ts +0 -321
  283. package/spec-cli/src/cli.ts +0 -1258
  284. package/spec-cli/src/client.ts +0 -414
  285. package/spec-cli/src/cockpit.ts +0 -43
  286. package/spec-cli/src/codex-runtime-generations.ts +0 -646
  287. package/spec-cli/src/commit-surgery.ts +0 -68
  288. package/spec-cli/src/contract-filter.ts +0 -202
  289. package/spec-cli/src/delivery-queue.ts +0 -165
  290. package/spec-cli/src/doctor.ts +0 -516
  291. package/spec-cli/src/execution-trace.ts +0 -444
  292. package/spec-cli/src/file-write.ts +0 -22
  293. package/spec-cli/src/gateway-auth.ts +0 -181
  294. package/spec-cli/src/gateway-hub.ts +0 -358
  295. package/spec-cli/src/gateway.ts +0 -461
  296. package/spec-cli/src/git.ts +0 -2556
  297. package/spec-cli/src/graph.ts +0 -271
  298. package/spec-cli/src/graphCache.ts +0 -710
  299. package/spec-cli/src/graphDelta.ts +0 -77
  300. package/spec-cli/src/graphStream.ts +0 -1019
  301. package/spec-cli/src/harness-select.ts +0 -77
  302. package/spec-cli/src/harness.ts +0 -3025
  303. package/spec-cli/src/headless-controller.ts +0 -63
  304. package/spec-cli/src/help.ts +0 -492
  305. package/spec-cli/src/host-resources.ts +0 -762
  306. package/spec-cli/src/host.ts +0 -611
  307. package/spec-cli/src/identity-presets.d.ts +0 -13
  308. package/spec-cli/src/identity-presets.js +0 -138
  309. package/spec-cli/src/index.ts +0 -818
  310. package/spec-cli/src/init.ts +0 -258
  311. package/spec-cli/src/issues-cli.ts +0 -276
  312. package/spec-cli/src/issues.ts +0 -237
  313. package/spec-cli/src/layout.ts +0 -640
  314. package/spec-cli/src/lint.ts +0 -488
  315. package/spec-cli/src/listen.ts +0 -28
  316. package/spec-cli/src/localIssues.ts +0 -538
  317. package/spec-cli/src/materialize.ts +0 -417
  318. package/spec-cli/src/mentions.ts +0 -81
  319. package/spec-cli/src/opencode-headless.ts +0 -205
  320. package/spec-cli/src/pi-headless.ts +0 -153
  321. package/spec-cli/src/plugin-harness.ts +0 -157
  322. package/spec-cli/src/process-identity.ts +0 -207
  323. package/spec-cli/src/project-identity.ts +0 -73
  324. package/spec-cli/src/project-store.ts +0 -17
  325. package/spec-cli/src/pty-bridge.ts +0 -396
  326. package/spec-cli/src/pty-helper.mjs +0 -96
  327. package/spec-cli/src/pty-native-helper.mjs +0 -22
  328. package/spec-cli/src/ranker.ts +0 -191
  329. package/spec-cli/src/reaper.ts +0 -125
  330. package/spec-cli/src/resilience.ts +0 -41
  331. package/spec-cli/src/reviewSnapshot.ts +0 -28
  332. package/spec-cli/src/reviews.ts +0 -434
  333. package/spec-cli/src/runtime-guard.ts +0 -35
  334. package/spec-cli/src/runtime-ownership.ts +0 -88
  335. package/spec-cli/src/search.bench.mjs +0 -80
  336. package/spec-cli/src/search.ts +0 -63
  337. package/spec-cli/src/session-cursors.ts +0 -91
  338. package/spec-cli/src/session-execution.ts +0 -68
  339. package/spec-cli/src/session-files.ts +0 -121
  340. package/spec-cli/src/session-follow.ts +0 -182
  341. package/spec-cli/src/session-reparent.ts +0 -33
  342. package/spec-cli/src/session-timeline.ts +0 -247
  343. package/spec-cli/src/session-web.ts +0 -135
  344. package/spec-cli/src/sessions.ts +0 -3883
  345. package/spec-cli/src/sh.ts +0 -3
  346. package/spec-cli/src/slash-commands.ts +0 -318
  347. package/spec-cli/src/source-files.ts +0 -104
  348. package/spec-cli/src/specs.ts +0 -498
  349. package/spec-cli/src/supervise.ts +0 -245
  350. package/spec-cli/src/tree.ts +0 -118
  351. package/spec-cli/src/tsx-bin.ts +0 -18
  352. package/spec-cli/src/uninstall.ts +0 -171
  353. package/spec-cli/src/uploads.ts +0 -253
  354. package/spec-cli/src/worktree-sources.ts +0 -42
  355. package/spec-cli/templates/spec/project/.plugins/commands/supervisor/spec.md +0 -8
  356. package/spec-dashboard/dist/assets/App-B6IbFbGu.js +0 -2
  357. package/spec-dashboard/dist/assets/Dashboard-C5ap-Sga.css +0 -1
  358. package/spec-dashboard/dist/assets/Dashboard-_sDk7Rcg.js +0 -27
  359. package/spec-dashboard/dist/assets/EvalsPage-BT357Lyt.js +0 -2
  360. package/spec-dashboard/dist/assets/IssuesPage-BG9rNOgV.js +0 -1
  361. package/spec-dashboard/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  362. package/spec-dashboard/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  363. package/spec-dashboard/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  364. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  365. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  366. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  367. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  368. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  369. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  370. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  371. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  372. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  373. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  374. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  375. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  376. package/spec-dashboard/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  377. package/spec-dashboard/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  378. package/spec-dashboard/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  379. package/spec-dashboard/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  380. package/spec-dashboard/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  381. package/spec-dashboard/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  382. package/spec-dashboard/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  383. package/spec-dashboard/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  384. package/spec-dashboard/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  385. package/spec-dashboard/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  386. package/spec-dashboard/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  387. package/spec-dashboard/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  388. package/spec-dashboard/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  389. package/spec-dashboard/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  390. package/spec-dashboard/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  391. package/spec-dashboard/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  392. package/spec-dashboard/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  393. package/spec-dashboard/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  394. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  395. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  396. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  397. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  398. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  399. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  400. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  401. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  402. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  403. package/spec-dashboard/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  404. package/spec-dashboard/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  405. package/spec-dashboard/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  406. package/spec-dashboard/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  407. package/spec-dashboard/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  408. package/spec-dashboard/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  409. package/spec-dashboard/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  410. package/spec-dashboard/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  411. package/spec-dashboard/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  412. package/spec-dashboard/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  413. package/spec-dashboard/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  414. package/spec-dashboard/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  415. package/spec-dashboard/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  416. package/spec-dashboard/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  417. package/spec-dashboard/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  418. package/spec-dashboard/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  419. package/spec-dashboard/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  420. package/spec-dashboard/dist/assets/MobileApp-D9yPMR3A.js +0 -2
  421. package/spec-dashboard/dist/assets/Modal-B3EcgeA3.js +0 -1
  422. package/spec-dashboard/dist/assets/PageScroll-CNZOugWV.js +0 -1
  423. package/spec-dashboard/dist/assets/ProjectsPage-C2CRHmvs.js +0 -1
  424. package/spec-dashboard/dist/assets/SessionInterface-6GBZ9nXN.css +0 -32
  425. package/spec-dashboard/dist/assets/SessionInterface-DOEjuT3l.js +0 -40
  426. package/spec-dashboard/dist/assets/SessionWindow-1nYgZ4CL.js +0 -1
  427. package/spec-dashboard/dist/assets/Settings-7WeTJn0V.js +0 -1
  428. package/spec-dashboard/dist/assets/Thread-CEbzAFQn.js +0 -13
  429. package/spec-dashboard/dist/assets/bindings-BC9vqpYU.js +0 -1
  430. package/spec-dashboard/dist/assets/data-SlvxNA2r.js +0 -1
  431. package/spec-dashboard/dist/assets/index-DLVeOHL-.js +0 -41
  432. package/spec-dashboard/dist/assets/index-DYLGfPjx.css +0 -1
  433. package/spec-dashboard/dist/assets/launch-Cp579UoJ.css +0 -1
  434. package/spec-dashboard/dist/assets/launch-yxCYr64x.js +0 -276
  435. package/spec-dashboard/dist/assets/sessionSurface--7l69h4F.js +0 -1
  436. package/spec-dashboard/dist/index.html +0 -29
  437. package/spec-dashboard/src/reviewFilters.js +0 -322
  438. package/spec-dashboard/src/reviewQuery.js +0 -174
  439. package/spec-dashboard/src/session.js +0 -199
  440. package/spec-eval/src/cache.ts +0 -57
  441. package/spec-eval/src/cli.ts +0 -894
  442. package/spec-eval/src/evaltab.ts +0 -264
  443. package/spec-eval/src/filing.ts +0 -39
  444. package/spec-eval/src/freshness.ts +0 -836
  445. package/spec-eval/src/humanok.ts +0 -28
  446. package/spec-eval/src/scenariofresh.ts +0 -244
  447. package/spec-eval/src/scenarios.ts +0 -672
  448. package/spec-eval/src/sessioneval.ts +0 -2220
  449. package/spec-eval/src/sidecar.ts +0 -93
  450. package/spec-eval/src/timeline.ts +0 -54
  451. package/spec-forge/src/__fixtures__/github-forge.json +0 -63
  452. package/spec-forge/src/cache.ts +0 -66
  453. package/spec-forge/src/cli.ts +0 -99
  454. package/spec-forge/src/drivers/github.ts +0 -132
  455. package/spec-forge/src/drivers/gitlab.ts +0 -153
  456. package/spec-forge/src/drivers.ts +0 -73
  457. package/spec-forge/src/links.ts +0 -72
  458. package/spec-forge/src/needs-eval.ts +0 -29
  459. package/spec-forge/src/port.ts +0 -36
  460. package/spec-forge/src/resident.ts +0 -46
  461. /package/{spec-cli → node_modules/@spexcode/spec-cli}/README.md +0 -0
  462. /package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/compat/mark-active-0.5.2-eef1.fixture +0 -0
  463. /package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/compat/mark-active-sed-v0.fixture +0 -0
  464. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/commit-msg +0 -0
  465. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/post-checkout +0 -0
  466. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/post-merge +0 -0
  467. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/pre-commit +0 -0
  468. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/prepare-commit-msg +0 -0
  469. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/reference-transaction +0 -0
  470. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/extract/spec.md +0 -0
  471. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/regroup/spec.md +0 -0
  472. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/rename/spec.md +0 -0
  473. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/spec.md +0 -0
  474. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/tidy/spec.md +0 -0
  475. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/comment-altitude/spec.md +0 -0
  476. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/idle/idle.sh +0 -0
  477. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/idle/spec.md +0 -0
  478. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/mark-active/mark-active.sh +0 -0
  479. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/mark-active/spec.md +0 -0
  480. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/session-fail/fail.sh +0 -0
  481. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/session-fail/spec.md +0 -0
  482. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-first/spec.md +0 -0
  483. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-of-file/spec.md +0 -0
  484. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec.md +0 -0
  485. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/atomic-landing/spec.md +0 -0
  486. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/forge-link/spec.md +0 -0
  487. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/memory-hygiene/spec.md +0 -0
  488. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/reproduce-before-fix/spec.md +0 -0
  489. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/spec.md +0 -0
  490. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/skills/distill/digest.mjs +0 -0
  491. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/skills/distill/spec.md +0 -0
  492. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/skills/spec.md +0 -0
  493. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/spec.md +0 -0
  494. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/spec.md +0 -0
  495. /package/{spec-cli → node_modules/@spexcode/spec-core}/templates/spexcode.json +0 -0
@@ -1,93 +0,0 @@
1
- import { readFileSync, appendFileSync, existsSync } from 'node:fs'
2
-
3
- export type Verdict = { status: 'pass' | 'fail'; note?: string }
4
-
5
- export type EvidenceKind = 'image' | 'transcript' | 'video' | 'data'
6
- export type Evidence = { hash: string; kind: EvidenceKind }
7
-
8
- export type Reading = {
9
- scenario: string
10
- codeSha: string
11
- scenarioHash?: string
12
- evidence?: Evidence[]
13
- blob?: string | null
14
- blobKind?: EvidenceKind
15
- timelineBlob?: string
16
- evaluator?: string
17
- by?: string
18
- verdict?: Verdict
19
- ts: string
20
- }
21
-
22
- export function evidenceOf(r: { evidence?: Evidence[]; blob?: string | null; blobKind?: EvidenceKind }): Evidence[] {
23
- if (r.evidence?.length) return r.evidence
24
- if (r.blob) return [{ hash: r.blob, kind: r.blobKind ?? 'image' }]
25
- return []
26
- }
27
-
28
- export function isJsonBlob(b: Buffer): boolean {
29
- if (!b.length || b.includes(0)) return false // empty or binary → not JSON text
30
- if (b.length > 4_000_000) return false // don't parse an unbounded blob just to sniff a type
31
- const s = b.toString('utf8').trim()
32
- const open = s[0], close = s[s.length - 1]
33
- if (!((open === '{' && close === '}') || (open === '[' && close === ']'))) return false
34
- try { const v = JSON.parse(s); return v !== null && typeof v === 'object' } catch { return false }
35
- }
36
-
37
- export type Retraction = { retracts: string; scenario: string; note?: string; by?: string; ts: string }
38
-
39
- export type HumanOk = { kind: 'human-ok'; scenario: string; okTs: string; okSha: string; by: string; ts: string }
40
-
41
- export function readSidecar(sidecarPath: string): { readings: Reading[]; retractions: Retraction[]; oks: HumanOk[] } {
42
- const readings: Reading[] = []
43
- const retractions: Retraction[] = []
44
- const oks: HumanOk[] = []
45
- if (!existsSync(sidecarPath)) return { readings, retractions, oks }
46
- for (const line of readFileSync(sidecarPath, 'utf8').split('\n')) {
47
- const t = line.trim()
48
- if (!t) continue
49
- try {
50
- const r = JSON.parse(t)
51
- if (!r || typeof r.scenario !== 'string') continue
52
- if (typeof r.retracts === 'string') retractions.push(r as Retraction)
53
- else if (r.kind === 'human-ok' && typeof r.okTs === 'string') oks.push(r as HumanOk)
54
- else if (typeof r.codeSha === 'string') readings.push(r as Reading)
55
- } catch { /* Keep earlier events readable after an incomplete append. */ }
56
- }
57
- return { readings, retractions, oks }
58
- }
59
-
60
- export function applyRetractions(readings: Reading[], retractions: Retraction[]): Reading[] {
61
- if (!retractions.length) return readings
62
- const gone = new Set(retractions.map((x) => `${x.scenario}\0${x.retracts}`))
63
- return readings.filter((r) => !gone.has(`${r.scenario}\0${r.ts}`))
64
- }
65
-
66
- export function readReadings(sidecarPath: string): Reading[] {
67
- const { readings, retractions } = readSidecar(sidecarPath)
68
- return applyRetractions(readings, retractions)
69
- }
70
-
71
- export function appendReading(sidecarPath: string, r: Reading): void {
72
- appendFileSync(sidecarPath, JSON.stringify(r) + '\n')
73
- }
74
-
75
- export function appendRetraction(sidecarPath: string, r: Retraction): void {
76
- appendFileSync(sidecarPath, JSON.stringify(r) + '\n')
77
- }
78
-
79
- export function appendHumanOk(sidecarPath: string, r: HumanOk): void {
80
- appendFileSync(sidecarPath, JSON.stringify(r) + '\n')
81
- }
82
-
83
- export function humanOkFor(oks: HumanOk[], scenario: string, readingTs: string): HumanOk | null {
84
- let hit: HumanOk | null = null
85
- for (const o of oks) if (o.scenario === scenario && o.okTs === readingTs) hit = o
86
- return hit
87
- }
88
-
89
- export function latestPerScenario(readings: Reading[]): Map<string, Reading> {
90
- const m = new Map<string, Reading>()
91
- for (const r of readings) m.set(r.scenario, r) // later lines overwrite earlier → last wins
92
- return m
93
- }
@@ -1,54 +0,0 @@
1
- export type TimelineEvent = { at: number; step: string; node?: string }
2
- export type StepTimeline = { v: 2; axis: string; events: TimelineEvent[] }
3
-
4
- export type LegacyTimelineEvent = { tMs: number; step: string; node?: string }
5
- export type LegacyStepTimeline = { v: 1; events: LegacyTimelineEvent[] }
6
-
7
- const V2_EVENT_KEYS = new Set(['at', 'step', 'node'])
8
- const V1_EVENT_KEYS = new Set(['tMs', 'step', 'node'])
9
-
10
- export function validateTimeline(raw: unknown): string[] {
11
- if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) return ['timeline must be a JSON object { v, axis, events }']
12
- const o = raw as Record<string, unknown>
13
- if (o.v !== 1 && o.v !== 2) return ['`v` must be 1 (legacy time axis) or 2 (axis-tagged)']
14
- const errs: string[] = []
15
- const v2 = o.v === 2
16
- const posKey = v2 ? 'at' : 'tMs'
17
- const rootKeys = v2 ? new Set(['v', 'axis', 'events']) : new Set(['v', 'events'])
18
- const evKeys = v2 ? V2_EVENT_KEYS : V1_EVENT_KEYS
19
- for (const k of Object.keys(o)) if (!rootKeys.has(k)) errs.push(`unknown field \`${k}\` (allowed: ${[...rootKeys].join(', ')})`)
20
- if (v2 && (typeof o.axis !== 'string' || !o.axis.trim())) errs.push('`axis` must be a non-empty string (e.g. time, frame, line, index)')
21
- if (!Array.isArray(o.events)) { errs.push('`events` must be an array'); return errs }
22
- let prev = -Infinity
23
- o.events.forEach((e, i) => {
24
- if (typeof e !== 'object' || e === null || Array.isArray(e)) { errs.push(`events[${i}] must be an object`); return }
25
- const ev = e as Record<string, unknown>
26
- for (const k of Object.keys(ev)) if (!evKeys.has(k)) errs.push(`events[${i}]: unknown field \`${k}\` (allowed: ${[...evKeys].join(', ')})`)
27
- const pos = ev[posKey]
28
- if (typeof pos !== 'number' || !Number.isFinite(pos) || pos < 0) {
29
- errs.push(`events[${i}].${posKey} must be a finite number ≥ 0`)
30
- } else {
31
- if (pos < prev) errs.push(`events[${i}].${posKey} is out of order (the list is ordered by position)`)
32
- prev = pos
33
- }
34
- if (typeof ev.step !== 'string' || !ev.step.trim()) errs.push(`events[${i}].step must be a non-empty string`)
35
- if (ev.node !== undefined && (typeof ev.node !== 'string' || !ev.node.trim())) errs.push(`events[${i}].node must be a non-empty string when present`)
36
- })
37
- return errs
38
- }
39
-
40
- export function normalizeTimeline(raw: unknown): { axis: string; events: TimelineEvent[] } {
41
- const o = (raw ?? {}) as Record<string, any>
42
- const events: any[] = Array.isArray(o.events) ? o.events : []
43
- if (o.v === 1) return { axis: 'time', events: events.map((e) => ({ at: e.tMs, step: e.step, ...(e.node ? { node: e.node } : {}) })) }
44
- return { axis: typeof o.axis === 'string' ? o.axis : 'time', events: events.map((e) => ({ at: e.at, step: e.step, ...(e.node ? { node: e.node } : {}) })) }
45
- }
46
-
47
- export function stepAt(events: TimelineEvent[], pos: number): TimelineEvent | null {
48
- let hit: TimelineEvent | null = null
49
- for (const e of events) {
50
- if (e.at <= pos) hit = e
51
- else break
52
- }
53
- return hit
54
- }
@@ -1,63 +0,0 @@
1
- {
2
- "_note": "Fixture mirroring githubDriver output (the vendor-neutral ForgeIssue[]/ForgePR[] a `gh issue/pr list` collapses to) for a sample repo. Drives the needs-eval resolver test with no network.",
3
- "issues": [
4
- {
5
- "number": 1,
6
- "title": "login redirect is broken on /logout",
7
- "body": "Steps to repro: click logout, land on a 404.\n\nSpec: sessions\nneeds-eval\n",
8
- "url": "https://github.com/acme/repo/issues/1",
9
- "state": "open",
10
- "labels": []
11
- },
12
- {
13
- "number": 2,
14
- "title": "board badge count is off by one",
15
- "body": "The open-issue badge over-counts.\n\nSpec: dashboard-issues",
16
- "url": "https://github.com/acme/repo/issues/2",
17
- "state": "open",
18
- "labels": ["bug", "needs-eval"]
19
- },
20
- {
21
- "number": 3,
22
- "title": "tracer mislinks a node/<id> branch with a dash",
23
- "body": "branchToNode picks the short id.\n\nSpec: links",
24
- "url": "https://github.com/acme/repo/issues/3",
25
- "state": "open",
26
- "labels": ["bug"]
27
- },
28
- {
29
- "number": 4,
30
- "title": "old forge-cache drift already verified",
31
- "body": "Spec: forge-cache\nNeeds-Eval\n",
32
- "url": "https://github.com/acme/repo/issues/4",
33
- "state": "closed",
34
- "labels": []
35
- },
36
- {
37
- "number": 5,
38
- "title": "flagged but names no node",
39
- "body": "This one is flagged but carries no Spec: marker and no closing PR.\n\nneeds-eval\n",
40
- "url": "https://github.com/acme/repo/issues/5",
41
- "state": "open",
42
- "labels": ["needs-eval"]
43
- },
44
- {
45
- "number": 6,
46
- "title": "fixed via an open PR, no marker on the issue",
47
- "body": "A worked bug; the node comes transitively from the closing PR.\n\nneeds-eval: ignore-the-trailing-text",
48
- "url": "https://github.com/acme/repo/issues/6",
49
- "state": "open",
50
- "labels": ["needs-eval"]
51
- }
52
- ],
53
- "prs": [
54
- {
55
- "number": 20,
56
- "title": "fix logout redirect",
57
- "url": "https://github.com/acme/repo/pull/20",
58
- "state": "open",
59
- "headRefName": "node/port-7f3a1c2",
60
- "closesIssues": [6]
61
- }
62
- ]
63
- }
@@ -1,66 +0,0 @@
1
- import type { ForgeDriver, ForgeIssue, ForgePR } from './port.js'
2
- import { resolveLinks, type NodeLinks } from './links.js'
3
-
4
- export type ForgeDelta =
5
- | { kind: 'issue'; issue: ForgeIssue }
6
- | { kind: 'pr'; pr: ForgePR }
7
- | { kind: 'remove'; target: 'issue' | 'pr'; number: number }
8
-
9
- function sameMap<T>(a: Map<number, T>, b: Map<number, T>): boolean {
10
- if (a.size !== b.size) return false
11
- for (const [number, value] of a)
12
- if (!b.has(number) || JSON.stringify(value) !== JSON.stringify(b.get(number))) return false
13
- return true
14
- }
15
-
16
- export class ForgeCache {
17
- private issues = new Map<number, ForgeIssue>()
18
- private prs = new Map<number, ForgePR>()
19
- private revision = 0
20
-
21
- apply(delta: ForgeDelta): void {
22
- if (delta.kind === 'issue') this.set(this.issues, delta.issue)
23
- else if (delta.kind === 'pr') this.set(this.prs, delta.pr)
24
- else {
25
- const target = delta.target === 'issue' ? this.issues : this.prs
26
- if (target.delete(delta.number)) this.revision++
27
- }
28
- }
29
-
30
- async reconcile(driver: ForgeDriver): Promise<void> {
31
- const [issues, prs] = await Promise.all([driver.listIssues(), driver.listPRs()])
32
- const nextIssues = new Map(issues.map((i) => [i.number, i]))
33
- const nextPRs = new Map(prs.map((p) => [p.number, p]))
34
- if (!sameMap(this.issues, nextIssues) || !sameMap(this.prs, nextPRs)) this.revision++
35
- this.issues = nextIssues
36
- this.prs = nextPRs
37
- }
38
-
39
- applyIssues(issues: ForgeIssue[]): void {
40
- for (const i of issues) this.set(this.issues, i)
41
- }
42
- setPRs(prs: ForgePR[]): void {
43
- const next = new Map(prs.map((p) => [p.number, p]))
44
- if (!sameMap(this.prs, next)) this.revision++
45
- this.prs = next
46
- }
47
-
48
- view(nodeIds: string[]): NodeLinks[] {
49
- return resolveLinks([...this.issues.values()], [...this.prs.values()], nodeIds)
50
- }
51
-
52
- state(): { issues: ForgeIssue[]; prs: ForgePR[] } {
53
- return { issues: [...this.issues.values()], prs: [...this.prs.values()] }
54
- }
55
-
56
- stateRevision(): number {
57
- return this.revision
58
- }
59
-
60
- private set<T extends ForgeIssue | ForgePR>(target: Map<number, T>, value: T): void {
61
- if (JSON.stringify(target.get(value.number)) !== JSON.stringify(value)) {
62
- target.set(value.number, value)
63
- this.revision++
64
- }
65
- }
66
- }
@@ -1,99 +0,0 @@
1
- import { loadSpecs } from '../../spec-cli/src/specs.js'
2
- import type { ForgeDriver, ForgeIssue, ForgePR } from './port.js'
3
- import { FORGE_DRIVERS, forgeDriverFor, resolveForgeHost } from './drivers.js'
4
- import { resolveLinks, type NodeLinks } from './links.js'
5
- import { resolveEvalPending, type NodeEvalPending } from './needs-eval.js'
6
-
7
- function flag(args: string[], name: string): string | undefined {
8
- const i = args.indexOf(`--${name}`)
9
- return i >= 0 ? args[i + 1] : undefined
10
- }
11
- const has = (args: string[], name: string) => args.includes(`--${name}`)
12
-
13
- async function readForge(
14
- args: string[],
15
- ): Promise<{ driver: ForgeDriver; nodeIds: string[]; issues: ForgeIssue[]; prs: ForgePR[] } | null> {
16
- const host = flag(args, 'store') ?? resolveForgeHost()
17
- const driver = forgeDriverFor(host)
18
- if (!driver) {
19
- console.error(`spex issue links: unknown --store '${host}' (known: ${FORGE_DRIVERS.map((d) => d.host).join(', ')})`)
20
- return null
21
- }
22
- const nodeIds = (await loadSpecs()).map((s) => s.id)
23
- const [issues, prs] = await Promise.all([driver.listIssues(), driver.listPRs()])
24
- return { driver, nodeIds, issues, prs }
25
- }
26
-
27
- function render(links: NodeLinks[]): string {
28
- const out: string[] = []
29
- for (const n of links) {
30
- out.push(`\n${n.node}`)
31
- if (n.issues.length) {
32
- out.push(' issues:')
33
- for (const i of n.issues) out.push(` #${i.number} ${i.state} ${i.title} (via ${i.via}) ${i.url}`)
34
- }
35
- if (n.prs.length) {
36
- out.push(' prs:')
37
- for (const p of n.prs) out.push(` #${p.number} ${p.state} ${p.title} ${p.headRefName} ${p.url}`)
38
- }
39
- }
40
- return out.join('\n')
41
- }
42
-
43
- async function links(args: string[]): Promise<number> {
44
- const forge = await readForge(args)
45
- if (!forge) return 2
46
- const { driver, nodeIds, issues, prs } = forge
47
- let resolved = resolveLinks(issues, prs, nodeIds)
48
-
49
- const only = flag(args, 'node')
50
- if (only) {
51
- if (!nodeIds.includes(only)) { console.error(`spex issue links: no such node '${only}'`); return 1 }
52
- resolved = resolved.filter((n) => n.node === only)
53
- }
54
-
55
- if (has(args, 'json')) { console.log(JSON.stringify(resolved, null, 2)); return 0 }
56
- const nIssues = resolved.reduce((a, n) => a + n.issues.length, 0)
57
- const nPRs = resolved.reduce((a, n) => a + n.prs.length, 0)
58
- console.log(
59
- `spec-forge · ${driver.host} · ${resolved.length} linked node(s) · ${nIssues} issue(s), ${nPRs} pr(s)` +
60
- ` · traced ${issues.length} issue(s), ${prs.length} pr(s)`,
61
- )
62
- if (resolved.length) console.log(render(resolved))
63
- return 0
64
- }
65
-
66
- function renderPending(pending: NodeEvalPending[]): string {
67
- const out: string[] = []
68
- for (const n of pending) {
69
- out.push(`\n${n.node}`)
70
- for (const i of n.pending) out.push(` #${i.number} ${i.state} ${i.title} (via ${i.via}) ${i.url}`)
71
- }
72
- return out.join('\n')
73
- }
74
-
75
- async function evalPending(args: string[]): Promise<number> {
76
- const forge = await readForge(args)
77
- if (!forge) return 2
78
- const { driver, nodeIds, issues, prs } = forge
79
- let resolved = resolveEvalPending(issues, prs, nodeIds)
80
-
81
- const only = flag(args, 'node')
82
- if (only) {
83
- if (!nodeIds.includes(only)) { console.error(`spex issue links: no such node '${only}'`); return 1 }
84
- resolved = resolved.filter((n) => n.node === only)
85
- }
86
-
87
- if (has(args, 'json')) { console.log(JSON.stringify(resolved, null, 2)); return 0 }
88
- const nPending = resolved.reduce((a, n) => a + n.pending.length, 0)
89
- console.log(
90
- `spec-forge · ${driver.host} · ${resolved.length} node(s) with eval pending · ${nPending} issue(s)` +
91
- ` · traced ${issues.length} issue(s), ${prs.length} pr(s)`,
92
- )
93
- if (resolved.length) console.log(renderPending(resolved))
94
- return 0
95
- }
96
-
97
- export async function runIssueLinks(args: string[]): Promise<number> {
98
- return has(args, 'pending') ? evalPending(args) : links(args)
99
- }
@@ -1,132 +0,0 @@
1
- import { execFile } from 'node:child_process'
2
- import { promisify } from 'node:util'
3
- import type { ForgeComment, ForgeDriver, ForgeIssue, ForgePR } from '../port.js'
4
-
5
- const run = promisify(execFile)
6
-
7
- // maxBuffer is raised above the 1MB default: a busy repo's issue/PR JSON can exceed it
8
- async function gh<T>(args: string[]): Promise<T> {
9
- const { stdout } = await run('gh', args, { maxBuffer: 16 * 1024 * 1024 })
10
- return JSON.parse(stdout) as T
11
- }
12
-
13
- export const githubDriver: ForgeDriver = {
14
- host: 'github',
15
-
16
- // fetch open and closed in separate `--limit 200` windows and merge, so a flood of closed issues can't crowd the open set out of one shared `--state all` limit
17
- async listIssues(): Promise<ForgeIssue[]> {
18
- const list = (state: string) =>
19
- gh<{ number: number; title: string; body: string; url: string; state: string; labels: { name: string }[]; author: { login: string } | null; createdAt: string; comments: { author: { login: string } | null; body: string; createdAt: string }[] }[]>(
20
- ['issue', 'list', '--state', state, '--limit', '200', '--json', 'number,title,body,url,state,labels,author,createdAt,comments'],
21
- )
22
- const [open, closed] = await Promise.all([list('open'), list('closed')])
23
- return [...open, ...closed].map((r) => ({
24
- number: r.number,
25
- title: r.title,
26
- body: r.body ?? '',
27
- url: r.url,
28
- state: (r.state || '').toLowerCase(),
29
- labels: (r.labels ?? []).map((l) => l.name),
30
- author: r.author?.login ?? '',
31
- createdAt: r.createdAt ?? '',
32
- comments: (r.comments ?? []).map((c) => ({ author: c.author?.login ?? '', createdAt: c.createdAt ?? '', body: c.body ?? '' })),
33
- }))
34
- },
35
-
36
- async listIssuesSince(sinceISO: string): Promise<ForgeIssue[]> {
37
- type ApiRow = {
38
- number: number; title: string; body: string | null; html_url: string; state: string
39
- labels: ({ name?: string } | string)[]; user: { login: string } | null; created_at: string
40
- comments: number; pull_request?: unknown
41
- }
42
- const out: ApiRow[] = []
43
- for (let page = 1; page <= 20; page++) {
44
- const rows = await gh<ApiRow[]>(['api', `repos/{owner}/{repo}/issues?state=all&since=${encodeURIComponent(sinceISO)}&per_page=100&page=${page}`])
45
- out.push(...rows)
46
- if (rows.length < 100) break
47
- }
48
- return Promise.all(out.filter((r) => !r.pull_request).map(async (r) => ({
49
- number: r.number,
50
- title: r.title,
51
- body: r.body ?? '',
52
- url: r.html_url,
53
- state: (r.state || '').toLowerCase(),
54
- labels: (r.labels ?? []).map((l) => (typeof l === 'string' ? l : l.name ?? '')).filter(Boolean),
55
- author: r.user?.login ?? '',
56
- createdAt: r.created_at ?? '',
57
- comments: r.comments > 0 ? await listComments(r.number) : [],
58
- })))
59
- },
60
-
61
- async listPRs(): Promise<ForgePR[]> {
62
- type Row = {
63
- number: number; title: string; url: string; state: string; headRefName: string
64
- closingIssuesReferences?: { number: number }[]
65
- }
66
- const base = ['pr', 'list', '--state', 'open', '--limit', '200', '--json']
67
- const fields = 'number,title,url,state,headRefName'
68
- let rows: Row[]
69
- try {
70
- rows = await gh<Row[]>([...base, `${fields},closingIssuesReferences`])
71
- } catch (err) {
72
- if (!isUnknownFieldError(err)) throw err
73
- warnNoTransitiveOnce()
74
- rows = await gh<Row[]>([...base, fields])
75
- }
76
- return rows.map((r) => ({
77
- number: r.number,
78
- title: r.title,
79
- url: r.url,
80
- state: r.state,
81
- headRefName: r.headRefName,
82
- closesIssues: (r.closingIssuesReferences ?? []).map((c) => c.number),
83
- }))
84
- },
85
-
86
- async createIssue({ title, body }: { title: string; body: string }): Promise<{ number: number; url: string }> {
87
- const { stdout } = await run('gh', ['issue', 'create', '--title', title, '--body', body], { maxBuffer: 1024 * 1024 })
88
- const url = stdout.trim().split('\n').pop() ?? ''
89
- const number = parseInt(url.split('/').pop() ?? '', 10)
90
- if (!url.startsWith('http') || !Number.isFinite(number)) throw new Error(`gh issue create returned an unexpected result: ${stdout.trim()}`)
91
- return { number, url }
92
- },
93
-
94
- async createComment({ number, body }: { number: number; body: string }): Promise<{ url: string }> {
95
- const { stdout } = await run('gh', ['issue', 'comment', String(number), '--body', body], { maxBuffer: 1024 * 1024 })
96
- const url = stdout.trim().split('\n').pop() ?? ''
97
- if (!url.startsWith('http')) throw new Error(`gh issue comment returned an unexpected result: ${stdout.trim()}`)
98
- return { url }
99
- },
100
-
101
- // the lifecycle write verb (see port.ts). `gh issue close` does not reliably print a permalink, so read
102
- // the issue URL back through gh's JSON surface after the close succeeds.
103
- async closeIssue({ number }: { number: number }): Promise<{ url: string }> {
104
- await run('gh', ['issue', 'close', String(number)], { maxBuffer: 1024 * 1024 })
105
- const r = await gh<{ url: string }>(['issue', 'view', String(number), '--json', 'url'])
106
- if (!r.url?.startsWith('http')) throw new Error(`gh issue view returned an unexpected url after close: ${JSON.stringify(r)}`)
107
- return { url: r.url }
108
- },
109
- }
110
-
111
- async function listComments(number: number): Promise<ForgeComment[]> {
112
- const rows = await gh<{ user: { login: string } | null; body: string | null; created_at: string }[]>(
113
- ['api', `repos/{owner}/{repo}/issues/${number}/comments?per_page=100`],
114
- )
115
- return rows.map((c) => ({ author: c.user?.login ?? '', createdAt: c.created_at ?? '', body: c.body ?? '' }))
116
- }
117
-
118
- function isUnknownFieldError(err: unknown): boolean {
119
- const e = err as { stderr?: string; message?: string }
120
- const text = `${e?.stderr ?? ''}\n${e?.message ?? ''}`
121
- return /unknown json field/i.test(text)
122
- }
123
-
124
- let warnedNoTransitive = false
125
- function warnNoTransitiveOnce(): void {
126
- if (warnedNoTransitive) return
127
- warnedNoTransitive = true
128
- console.warn(
129
- 'spec-forge: this `gh` is too old for `closingIssuesReferences` — transitive issue↔PR links are ' +
130
- 'disabled (branch + `Spec:` marker links still work). Upgrade `gh` to restore them.',
131
- )
132
- }
@@ -1,153 +0,0 @@
1
- import { execFileSync } from 'node:child_process'
2
- import type { ForgeComment, ForgeDriver, ForgeIssue, ForgePR } from '../port.js'
3
-
4
- type Ctx = { base: string; host: string; project: string; token: string }
5
- let ctx: Ctx | null = null
6
-
7
- function gitlabCtx(): Ctx {
8
- if (ctx) return ctx
9
- const remote = execFileSync('git', ['remote', 'get-url', 'origin'], { encoding: 'utf8' }).trim()
10
- const parsed = parseRemote(remote)
11
- if (!parsed) throw new Error(`gitlab: cannot parse origin remote '${remote}' as a GitLab URL`)
12
- const token = process.env.GITLAB_TOKEN || credentialToken(parsed.host)
13
- if (!token) {
14
- throw new Error(
15
- `gitlab: no token for ${parsed.host} — set GITLAB_TOKEN, or store a PAT in git's credential store:\n` +
16
- ` printf "protocol=https\\nhost=${parsed.host}\\nusername=<user>\\npassword=<token>\\n\\n" | git credential approve`,
17
- )
18
- }
19
- ctx = { ...parsed, token }
20
- return ctx
21
- }
22
-
23
- export function parseRemote(remote: string): { base: string; host: string; project: string } | null {
24
- let m = remote.match(/^(https?:\/\/[^/]+)\/(.+?)(?:\.git)?\/?$/)
25
- if (m) return { base: m[1], host: m[1].replace(/^https?:\/\//, ''), project: m[2] }
26
- m = remote.match(/^(?:ssh:\/\/)?[\w.-]+@([\w.-]+)(?::\d+)?[:/](.+?)(?:\.git)?\/?$/)
27
- if (m) return { base: `https://${m[1]}`, host: m[1], project: m[2] }
28
- return null
29
- }
30
-
31
- function credentialToken(host: string): string {
32
- try {
33
- const out = execFileSync('git', ['credential', 'fill'], {
34
- input: `protocol=https\nhost=${host}\n\n`,
35
- encoding: 'utf8',
36
- env: { ...process.env, GIT_TERMINAL_PROMPT: '0', GIT_ASKPASS: 'true' },
37
- stdio: ['pipe', 'pipe', 'ignore'],
38
- })
39
- return out.match(/^password=(.+)$/m)?.[1] ?? ''
40
- } catch {
41
- return ''
42
- }
43
- }
44
-
45
- async function api<T>(path: string, init?: RequestInit): Promise<T> {
46
- const { base, project, token } = gitlabCtx()
47
- const sep = path.includes('?') ? '&' : '?'
48
- const url = `${base}/api/v4/projects/${encodeURIComponent(project)}/${path}${init?.method ? '' : `${sep}per_page=100`}`
49
- const res = await fetch(url, {
50
- ...init,
51
- headers: { 'PRIVATE-TOKEN': token, 'content-type': 'application/json', ...init?.headers },
52
- })
53
- if (!res.ok) throw new Error(`gitlab: ${init?.method ?? 'GET'} ${path} → ${res.status} ${(await res.text()).slice(0, 300)}`)
54
- return (await res.json()) as T
55
- }
56
-
57
- // GitLab's API has no bounded list query; stop at a short page and cap the walk.
58
- async function paged<T>(path: string): Promise<T[]> {
59
- const sep = path.includes('?') ? '&' : '?'
60
- const out: T[] = []
61
- for (let page = 1; page <= 20; page++) {
62
- const rows = await api<T[]>(`${path}${sep}page=${page}`)
63
- out.push(...rows)
64
- if (rows.length < 100) break
65
- }
66
- return out
67
- }
68
-
69
- type ApiIssue = {
70
- iid: number; title: string; description: string | null; web_url: string; state: string
71
- labels: string[]; author: { username: string } | null; created_at: string; user_notes_count: number
72
- }
73
-
74
- async function toIssue(r: ApiIssue): Promise<ForgeIssue> {
75
- return {
76
- number: r.iid,
77
- title: r.title,
78
- body: r.description ?? '',
79
- url: r.web_url,
80
- state: normalizeState(r.state),
81
- labels: r.labels ?? [],
82
- author: r.author?.username ?? '',
83
- createdAt: r.created_at ?? '',
84
- comments: r.user_notes_count > 0 ? await listNotes(r.iid) : [],
85
- }
86
- }
87
-
88
- function normalizeState(state: string): string {
89
- const s = (state || '').toLowerCase()
90
- return s === 'opened' ? 'open' : s
91
- }
92
-
93
- async function listNotes(iid: number): Promise<ForgeComment[]> {
94
- type Note = { system: boolean; author: { username: string } | null; created_at: string; body: string | null }
95
- const rows = await paged<Note>(`issues/${iid}/notes`)
96
- return rows
97
- .filter((n) => !n.system)
98
- .map((n) => ({ author: n.author?.username ?? '', createdAt: n.created_at ?? '', body: n.body ?? '' }))
99
- }
100
-
101
- export const gitlabDriver: ForgeDriver = {
102
- host: 'gitlab',
103
-
104
- async listIssues(): Promise<ForgeIssue[]> {
105
- const rows = await paged<ApiIssue>('issues?state=all')
106
- return Promise.all(rows.map(toIssue))
107
- },
108
-
109
- async listIssuesSince(sinceISO: string): Promise<ForgeIssue[]> {
110
- const rows = await paged<ApiIssue>(`issues?state=all&updated_after=${encodeURIComponent(sinceISO)}`)
111
- return Promise.all(rows.map(toIssue))
112
- },
113
-
114
- async listPRs(): Promise<ForgePR[]> {
115
- type ApiMR = { iid: number; title: string; web_url: string; state: string; source_branch: string }
116
- const rows = await paged<ApiMR>('merge_requests?state=opened')
117
- return Promise.all(
118
- rows.map(async (r) => ({
119
- number: r.iid,
120
- title: r.title,
121
- url: r.web_url,
122
- state: normalizeState(r.state),
123
- headRefName: r.source_branch,
124
- closesIssues: (await paged<{ iid: number }>(`merge_requests/${r.iid}/closes_issues`)).map((i) => i.iid),
125
- })),
126
- )
127
- },
128
-
129
- async createIssue({ title, body }: { title: string; body: string }): Promise<{ number: number; url: string }> {
130
- const r = await api<{ iid: number; web_url: string }>('issues', {
131
- method: 'POST',
132
- body: JSON.stringify({ title, description: body }),
133
- })
134
- if (!r.web_url?.startsWith('http') || !Number.isFinite(r.iid)) throw new Error(`gitlab: issue create returned an unexpected result: ${JSON.stringify(r)}`)
135
- return { number: r.iid, url: r.web_url }
136
- },
137
-
138
- async createComment({ number, body }: { number: number; body: string }): Promise<{ url: string }> {
139
- const { base, project } = gitlabCtx()
140
- const r = await api<{ id: number }>(`issues/${number}/notes`, { method: 'POST', body: JSON.stringify({ body }) })
141
- if (!Number.isFinite(r.id)) throw new Error(`gitlab: note create returned an unexpected result: ${JSON.stringify(r)}`)
142
- return { url: `${base}/${project}/-/issues/${number}#note_${r.id}` }
143
- },
144
-
145
- async closeIssue({ number }: { number: number }): Promise<{ url: string }> {
146
- const r = await api<{ web_url: string; state: string }>(`issues/${number}`, {
147
- method: 'PUT',
148
- body: JSON.stringify({ state_event: 'close' }),
149
- })
150
- if (!r.web_url?.startsWith('http')) throw new Error(`gitlab: issue close returned an unexpected result: ${JSON.stringify(r)}`)
151
- return { url: r.web_url }
152
- },
153
- }