@cluesmith/codev 2.0.0-rc.7 → 2.0.0-rc.70

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 (456) hide show
  1. package/bin/af.js +2 -2
  2. package/bin/consult.js +1 -1
  3. package/bin/porch.js +6 -35
  4. package/dashboard/dist/assets/index-C7FtNK6Y.css +32 -0
  5. package/dashboard/dist/assets/index-CDAINZKT.js +131 -0
  6. package/dashboard/dist/assets/index-CDAINZKT.js.map +1 -0
  7. package/dashboard/dist/index.html +14 -0
  8. package/dist/agent-farm/cli.d.ts.map +1 -1
  9. package/dist/agent-farm/cli.js +173 -118
  10. package/dist/agent-farm/cli.js.map +1 -1
  11. package/dist/agent-farm/commands/architect.d.ts +3 -3
  12. package/dist/agent-farm/commands/architect.d.ts.map +1 -1
  13. package/dist/agent-farm/commands/architect.js +20 -147
  14. package/dist/agent-farm/commands/architect.js.map +1 -1
  15. package/dist/agent-farm/commands/attach.d.ts +13 -0
  16. package/dist/agent-farm/commands/attach.d.ts.map +1 -0
  17. package/dist/agent-farm/commands/attach.js +144 -0
  18. package/dist/agent-farm/commands/attach.js.map +1 -0
  19. package/dist/agent-farm/commands/cleanup.d.ts.map +1 -1
  20. package/dist/agent-farm/commands/cleanup.js +35 -19
  21. package/dist/agent-farm/commands/cleanup.js.map +1 -1
  22. package/dist/agent-farm/commands/consult.d.ts +3 -4
  23. package/dist/agent-farm/commands/consult.d.ts.map +1 -1
  24. package/dist/agent-farm/commands/consult.js +27 -37
  25. package/dist/agent-farm/commands/consult.js.map +1 -1
  26. package/dist/agent-farm/commands/index.d.ts +2 -2
  27. package/dist/agent-farm/commands/index.d.ts.map +1 -1
  28. package/dist/agent-farm/commands/index.js +2 -2
  29. package/dist/agent-farm/commands/index.js.map +1 -1
  30. package/dist/agent-farm/commands/open.d.ts +4 -2
  31. package/dist/agent-farm/commands/open.d.ts.map +1 -1
  32. package/dist/agent-farm/commands/open.js +33 -83
  33. package/dist/agent-farm/commands/open.js.map +1 -1
  34. package/dist/agent-farm/commands/send.d.ts +1 -1
  35. package/dist/agent-farm/commands/send.d.ts.map +1 -1
  36. package/dist/agent-farm/commands/send.js +60 -79
  37. package/dist/agent-farm/commands/send.js.map +1 -1
  38. package/dist/agent-farm/commands/shell.d.ts +15 -0
  39. package/dist/agent-farm/commands/shell.d.ts.map +1 -0
  40. package/dist/agent-farm/commands/shell.js +50 -0
  41. package/dist/agent-farm/commands/shell.js.map +1 -0
  42. package/dist/agent-farm/commands/spawn.d.ts.map +1 -1
  43. package/dist/agent-farm/commands/spawn.js +597 -281
  44. package/dist/agent-farm/commands/spawn.js.map +1 -1
  45. package/dist/agent-farm/commands/start.d.ts +10 -20
  46. package/dist/agent-farm/commands/start.d.ts.map +1 -1
  47. package/dist/agent-farm/commands/start.js +45 -491
  48. package/dist/agent-farm/commands/start.js.map +1 -1
  49. package/dist/agent-farm/commands/status.d.ts +2 -0
  50. package/dist/agent-farm/commands/status.d.ts.map +1 -1
  51. package/dist/agent-farm/commands/status.js +75 -24
  52. package/dist/agent-farm/commands/status.js.map +1 -1
  53. package/dist/agent-farm/commands/stop.d.ts +6 -0
  54. package/dist/agent-farm/commands/stop.d.ts.map +1 -1
  55. package/dist/agent-farm/commands/stop.js +49 -109
  56. package/dist/agent-farm/commands/stop.js.map +1 -1
  57. package/dist/agent-farm/commands/tower-cloud.d.ts +48 -0
  58. package/dist/agent-farm/commands/tower-cloud.d.ts.map +1 -0
  59. package/dist/agent-farm/commands/tower-cloud.js +334 -0
  60. package/dist/agent-farm/commands/tower-cloud.js.map +1 -0
  61. package/dist/agent-farm/commands/tower.d.ts +9 -0
  62. package/dist/agent-farm/commands/tower.d.ts.map +1 -1
  63. package/dist/agent-farm/commands/tower.js +59 -19
  64. package/dist/agent-farm/commands/tower.js.map +1 -1
  65. package/dist/agent-farm/db/index.d.ts +6 -2
  66. package/dist/agent-farm/db/index.d.ts.map +1 -1
  67. package/dist/agent-farm/db/index.js +246 -18
  68. package/dist/agent-farm/db/index.js.map +1 -1
  69. package/dist/agent-farm/db/migrate.d.ts +0 -4
  70. package/dist/agent-farm/db/migrate.d.ts.map +1 -1
  71. package/dist/agent-farm/db/migrate.js +6 -55
  72. package/dist/agent-farm/db/migrate.js.map +1 -1
  73. package/dist/agent-farm/db/schema.d.ts +3 -3
  74. package/dist/agent-farm/db/schema.d.ts.map +1 -1
  75. package/dist/agent-farm/db/schema.js +25 -19
  76. package/dist/agent-farm/db/schema.js.map +1 -1
  77. package/dist/agent-farm/db/types.d.ts +3 -13
  78. package/dist/agent-farm/db/types.d.ts.map +1 -1
  79. package/dist/agent-farm/db/types.js +3 -11
  80. package/dist/agent-farm/db/types.js.map +1 -1
  81. package/dist/agent-farm/hq-connector.d.ts +2 -6
  82. package/dist/agent-farm/hq-connector.d.ts.map +1 -1
  83. package/dist/agent-farm/hq-connector.js +2 -17
  84. package/dist/agent-farm/hq-connector.js.map +1 -1
  85. package/dist/agent-farm/lib/cloud-config.d.ts +59 -0
  86. package/dist/agent-farm/lib/cloud-config.d.ts.map +1 -0
  87. package/dist/agent-farm/lib/cloud-config.js +143 -0
  88. package/dist/agent-farm/lib/cloud-config.js.map +1 -0
  89. package/dist/agent-farm/lib/tower-client.d.ts +163 -0
  90. package/dist/agent-farm/lib/tower-client.d.ts.map +1 -0
  91. package/dist/agent-farm/lib/tower-client.js +233 -0
  92. package/dist/agent-farm/lib/tower-client.js.map +1 -0
  93. package/dist/agent-farm/lib/tunnel-client.d.ts +117 -0
  94. package/dist/agent-farm/lib/tunnel-client.d.ts.map +1 -0
  95. package/dist/agent-farm/lib/tunnel-client.js +504 -0
  96. package/dist/agent-farm/lib/tunnel-client.js.map +1 -0
  97. package/dist/agent-farm/servers/tower-server.js +2650 -185
  98. package/dist/agent-farm/servers/tower-server.js.map +1 -1
  99. package/dist/agent-farm/state.d.ts +6 -12
  100. package/dist/agent-farm/state.d.ts.map +1 -1
  101. package/dist/agent-farm/state.js +34 -49
  102. package/dist/agent-farm/state.js.map +1 -1
  103. package/dist/agent-farm/types.d.ts +49 -26
  104. package/dist/agent-farm/types.d.ts.map +1 -1
  105. package/dist/agent-farm/utils/config.d.ts +0 -5
  106. package/dist/agent-farm/utils/config.d.ts.map +1 -1
  107. package/dist/agent-farm/utils/config.js +12 -44
  108. package/dist/agent-farm/utils/config.js.map +1 -1
  109. package/dist/agent-farm/utils/deps.d.ts.map +1 -1
  110. package/dist/agent-farm/utils/deps.js +0 -32
  111. package/dist/agent-farm/utils/deps.js.map +1 -1
  112. package/dist/agent-farm/utils/file-tabs.d.ts +27 -0
  113. package/dist/agent-farm/utils/file-tabs.d.ts.map +1 -0
  114. package/dist/agent-farm/utils/file-tabs.js +46 -0
  115. package/dist/agent-farm/utils/file-tabs.js.map +1 -0
  116. package/dist/agent-farm/utils/gate-status.d.ts +16 -0
  117. package/dist/agent-farm/utils/gate-status.d.ts.map +1 -0
  118. package/dist/agent-farm/utils/gate-status.js +79 -0
  119. package/dist/agent-farm/utils/gate-status.js.map +1 -0
  120. package/dist/agent-farm/utils/gate-watcher.d.ts +38 -0
  121. package/dist/agent-farm/utils/gate-watcher.d.ts.map +1 -0
  122. package/dist/agent-farm/utils/gate-watcher.js +122 -0
  123. package/dist/agent-farm/utils/gate-watcher.js.map +1 -0
  124. package/dist/agent-farm/utils/index.d.ts +0 -1
  125. package/dist/agent-farm/utils/index.d.ts.map +1 -1
  126. package/dist/agent-farm/utils/index.js +0 -1
  127. package/dist/agent-farm/utils/index.js.map +1 -1
  128. package/dist/agent-farm/utils/notifications.d.ts +30 -0
  129. package/dist/agent-farm/utils/notifications.d.ts.map +1 -0
  130. package/dist/agent-farm/utils/notifications.js +121 -0
  131. package/dist/agent-farm/utils/notifications.js.map +1 -0
  132. package/dist/agent-farm/utils/server-utils.d.ts +5 -5
  133. package/dist/agent-farm/utils/server-utils.d.ts.map +1 -1
  134. package/dist/agent-farm/utils/server-utils.js +5 -16
  135. package/dist/agent-farm/utils/server-utils.js.map +1 -1
  136. package/dist/agent-farm/utils/session.d.ts +32 -0
  137. package/dist/agent-farm/utils/session.d.ts.map +1 -0
  138. package/dist/agent-farm/utils/session.js +57 -0
  139. package/dist/agent-farm/utils/session.js.map +1 -0
  140. package/dist/agent-farm/utils/shell.d.ts +9 -22
  141. package/dist/agent-farm/utils/shell.d.ts.map +1 -1
  142. package/dist/agent-farm/utils/shell.js +34 -34
  143. package/dist/agent-farm/utils/shell.js.map +1 -1
  144. package/dist/cli.d.ts.map +1 -1
  145. package/dist/cli.js +11 -54
  146. package/dist/cli.js.map +1 -1
  147. package/dist/commands/adopt.d.ts.map +1 -1
  148. package/dist/commands/adopt.js +49 -4
  149. package/dist/commands/adopt.js.map +1 -1
  150. package/dist/commands/consult/index.d.ts +13 -2
  151. package/dist/commands/consult/index.d.ts.map +1 -1
  152. package/dist/commands/consult/index.js +245 -29
  153. package/dist/commands/consult/index.js.map +1 -1
  154. package/dist/commands/doctor.d.ts.map +1 -1
  155. package/dist/commands/doctor.js +96 -79
  156. package/dist/commands/doctor.js.map +1 -1
  157. package/dist/commands/init.d.ts.map +1 -1
  158. package/dist/commands/init.js +41 -2
  159. package/dist/commands/init.js.map +1 -1
  160. package/dist/commands/porch/build-counter.d.ts +5 -0
  161. package/dist/commands/porch/build-counter.d.ts.map +1 -0
  162. package/dist/commands/porch/build-counter.js +5 -0
  163. package/dist/commands/porch/build-counter.js.map +1 -0
  164. package/dist/commands/porch/checks.d.ts +17 -29
  165. package/dist/commands/porch/checks.d.ts.map +1 -1
  166. package/dist/commands/porch/checks.js +96 -144
  167. package/dist/commands/porch/checks.js.map +1 -1
  168. package/dist/commands/porch/index.d.ts +25 -43
  169. package/dist/commands/porch/index.d.ts.map +1 -1
  170. package/dist/commands/porch/index.js +463 -1116
  171. package/dist/commands/porch/index.js.map +1 -1
  172. package/dist/commands/porch/next.d.ts +22 -0
  173. package/dist/commands/porch/next.d.ts.map +1 -0
  174. package/dist/commands/porch/next.js +571 -0
  175. package/dist/commands/porch/next.js.map +1 -0
  176. package/dist/commands/porch/plan.d.ts +70 -0
  177. package/dist/commands/porch/plan.d.ts.map +1 -0
  178. package/dist/commands/porch/plan.js +190 -0
  179. package/dist/commands/porch/plan.js.map +1 -0
  180. package/dist/commands/porch/prompts.d.ts +19 -0
  181. package/dist/commands/porch/prompts.d.ts.map +1 -0
  182. package/dist/commands/porch/prompts.js +277 -0
  183. package/dist/commands/porch/prompts.js.map +1 -0
  184. package/dist/commands/porch/protocol.d.ts +59 -0
  185. package/dist/commands/porch/protocol.d.ts.map +1 -0
  186. package/dist/commands/porch/protocol.js +294 -0
  187. package/dist/commands/porch/protocol.js.map +1 -0
  188. package/dist/commands/porch/state.d.ts +36 -107
  189. package/dist/commands/porch/state.d.ts.map +1 -1
  190. package/dist/commands/porch/state.js +120 -699
  191. package/dist/commands/porch/state.js.map +1 -1
  192. package/dist/commands/porch/types.d.ts +99 -164
  193. package/dist/commands/porch/types.d.ts.map +1 -1
  194. package/dist/commands/porch/types.js +2 -1
  195. package/dist/commands/porch/types.js.map +1 -1
  196. package/dist/commands/porch/verdict.d.ts +31 -0
  197. package/dist/commands/porch/verdict.d.ts.map +1 -0
  198. package/dist/commands/porch/verdict.js +59 -0
  199. package/dist/commands/porch/verdict.js.map +1 -0
  200. package/dist/commands/update.d.ts.map +1 -1
  201. package/dist/commands/update.js +31 -0
  202. package/dist/commands/update.js.map +1 -1
  203. package/dist/lib/scaffold.d.ts +37 -0
  204. package/dist/lib/scaffold.d.ts.map +1 -1
  205. package/dist/lib/scaffold.js +114 -0
  206. package/dist/lib/scaffold.js.map +1 -1
  207. package/dist/terminal/index.d.ts +8 -0
  208. package/dist/terminal/index.d.ts.map +1 -0
  209. package/dist/terminal/index.js +5 -0
  210. package/dist/terminal/index.js.map +1 -0
  211. package/dist/terminal/pty-manager.d.ts +69 -0
  212. package/dist/terminal/pty-manager.d.ts.map +1 -0
  213. package/dist/terminal/pty-manager.js +377 -0
  214. package/dist/terminal/pty-manager.js.map +1 -0
  215. package/dist/terminal/pty-session.d.ts +104 -0
  216. package/dist/terminal/pty-session.d.ts.map +1 -0
  217. package/dist/terminal/pty-session.js +327 -0
  218. package/dist/terminal/pty-session.js.map +1 -0
  219. package/dist/terminal/ring-buffer.d.ts +34 -0
  220. package/dist/terminal/ring-buffer.d.ts.map +1 -0
  221. package/dist/terminal/ring-buffer.js +94 -0
  222. package/dist/terminal/ring-buffer.js.map +1 -0
  223. package/dist/terminal/session-manager.d.ts +115 -0
  224. package/dist/terminal/session-manager.d.ts.map +1 -0
  225. package/dist/terminal/session-manager.js +582 -0
  226. package/dist/terminal/session-manager.js.map +1 -0
  227. package/dist/terminal/shepherd-client.d.ts +58 -0
  228. package/dist/terminal/shepherd-client.d.ts.map +1 -0
  229. package/dist/terminal/shepherd-client.js +212 -0
  230. package/dist/terminal/shepherd-client.js.map +1 -0
  231. package/dist/terminal/shepherd-main.d.ts +19 -0
  232. package/dist/terminal/shepherd-main.d.ts.map +1 -0
  233. package/dist/terminal/shepherd-main.js +153 -0
  234. package/dist/terminal/shepherd-main.js.map +1 -0
  235. package/dist/terminal/shepherd-process.d.ts +75 -0
  236. package/dist/terminal/shepherd-process.d.ts.map +1 -0
  237. package/dist/terminal/shepherd-process.js +279 -0
  238. package/dist/terminal/shepherd-process.js.map +1 -0
  239. package/dist/terminal/shepherd-protocol.d.ts +115 -0
  240. package/dist/terminal/shepherd-protocol.d.ts.map +1 -0
  241. package/dist/terminal/shepherd-protocol.js +214 -0
  242. package/dist/terminal/shepherd-protocol.js.map +1 -0
  243. package/dist/terminal/shepherd-replay-buffer.d.ts +38 -0
  244. package/dist/terminal/shepherd-replay-buffer.d.ts.map +1 -0
  245. package/dist/terminal/shepherd-replay-buffer.js +94 -0
  246. package/dist/terminal/shepherd-replay-buffer.js.map +1 -0
  247. package/dist/terminal/ws-protocol.d.ts +27 -0
  248. package/dist/terminal/ws-protocol.d.ts.map +1 -0
  249. package/dist/terminal/ws-protocol.js +44 -0
  250. package/dist/terminal/ws-protocol.js.map +1 -0
  251. package/package.json +19 -5
  252. package/skeleton/.claude/skills/af/SKILL.md +89 -0
  253. package/skeleton/.claude/skills/codev/SKILL.md +41 -0
  254. package/skeleton/.claude/skills/consult/SKILL.md +81 -0
  255. package/skeleton/.claude/skills/generate-image/SKILL.md +56 -0
  256. package/skeleton/DEPENDENCIES.md +4 -62
  257. package/skeleton/builders.md +1 -1
  258. package/skeleton/consult-types/impl-review.md +18 -9
  259. package/skeleton/consult-types/integration-review.md +1 -1
  260. package/skeleton/consult-types/plan-review.md +1 -1
  261. package/skeleton/consult-types/pr-ready.md +1 -1
  262. package/skeleton/consult-types/spec-review.md +1 -1
  263. package/skeleton/porch/prompts/defend.md +1 -1
  264. package/skeleton/porch/prompts/evaluate.md +2 -2
  265. package/skeleton/porch/prompts/implement.md +1 -1
  266. package/skeleton/porch/prompts/plan.md +1 -1
  267. package/skeleton/porch/prompts/review.md +4 -4
  268. package/skeleton/porch/prompts/specify.md +1 -1
  269. package/skeleton/porch/prompts/understand.md +2 -2
  270. package/skeleton/protocol-schema.json +282 -0
  271. package/skeleton/protocols/bugfix/builder-prompt.md +54 -0
  272. package/skeleton/protocols/bugfix/prompts/fix.md +77 -0
  273. package/skeleton/protocols/bugfix/prompts/investigate.md +77 -0
  274. package/skeleton/protocols/bugfix/prompts/pr.md +84 -0
  275. package/skeleton/protocols/bugfix/protocol.json +20 -33
  276. package/skeleton/protocols/experiment/builder-prompt.md +52 -0
  277. package/skeleton/protocols/experiment/protocol.json +101 -0
  278. package/skeleton/protocols/experiment/protocol.md +3 -3
  279. package/skeleton/protocols/experiment/templates/notes.md +1 -1
  280. package/skeleton/protocols/maintain/builder-prompt.md +46 -0
  281. package/skeleton/protocols/maintain/prompts/audit.md +111 -0
  282. package/skeleton/protocols/maintain/prompts/clean.md +91 -0
  283. package/skeleton/protocols/maintain/prompts/sync.md +113 -0
  284. package/skeleton/protocols/maintain/prompts/verify.md +110 -0
  285. package/skeleton/protocols/maintain/protocol.json +141 -0
  286. package/skeleton/protocols/maintain/protocol.md +17 -11
  287. package/skeleton/protocols/protocol-schema.json +54 -1
  288. package/skeleton/protocols/spir/builder-prompt.md +59 -0
  289. package/skeleton/protocols/spir/prompts/implement.md +208 -0
  290. package/skeleton/protocols/{spider → spir}/prompts/plan.md +6 -70
  291. package/skeleton/protocols/{spider → spir}/prompts/review.md +20 -39
  292. package/skeleton/protocols/{spider → spir}/prompts/specify.md +33 -61
  293. package/skeleton/protocols/spir/protocol.json +156 -0
  294. package/skeleton/protocols/{spider → spir}/protocol.md +35 -21
  295. package/skeleton/protocols/{spider → spir}/templates/plan.md +14 -0
  296. package/skeleton/protocols/spir/templates/review.md +89 -0
  297. package/skeleton/protocols/tick/builder-prompt.md +56 -0
  298. package/skeleton/protocols/tick/protocol.json +7 -2
  299. package/skeleton/protocols/tick/protocol.md +18 -18
  300. package/skeleton/protocols/tick/templates/review.md +1 -1
  301. package/skeleton/resources/commands/agent-farm.md +63 -46
  302. package/skeleton/resources/commands/codev.md +0 -2
  303. package/skeleton/resources/commands/overview.md +7 -17
  304. package/skeleton/resources/workflow-reference.md +4 -4
  305. package/skeleton/roles/architect.md +152 -315
  306. package/skeleton/roles/builder.md +110 -218
  307. package/skeleton/roles/consultant.md +6 -6
  308. package/skeleton/templates/AGENTS.md +2 -2
  309. package/skeleton/templates/CLAUDE.md +2 -2
  310. package/skeleton/templates/cheatsheet.md +7 -5
  311. package/skeleton/templates/projectlist.md +1 -1
  312. package/templates/dashboard/index.html +17 -43
  313. package/templates/dashboard/js/dialogs.js +7 -7
  314. package/templates/dashboard/js/files.js +2 -2
  315. package/templates/dashboard/js/main.js +4 -4
  316. package/templates/dashboard/js/projects.js +3 -3
  317. package/templates/dashboard/js/tabs.js +1 -1
  318. package/templates/dashboard/js/utils.js +22 -87
  319. package/templates/open.html +26 -0
  320. package/templates/tower.html +642 -36
  321. package/dist/agent-farm/commands/kickoff.d.ts +0 -20
  322. package/dist/agent-farm/commands/kickoff.d.ts.map +0 -1
  323. package/dist/agent-farm/commands/kickoff.js +0 -337
  324. package/dist/agent-farm/commands/kickoff.js.map +0 -1
  325. package/dist/agent-farm/commands/rename.d.ts +0 -13
  326. package/dist/agent-farm/commands/rename.d.ts.map +0 -1
  327. package/dist/agent-farm/commands/rename.js +0 -33
  328. package/dist/agent-farm/commands/rename.js.map +0 -1
  329. package/dist/agent-farm/commands/tutorial.d.ts +0 -10
  330. package/dist/agent-farm/commands/tutorial.d.ts.map +0 -1
  331. package/dist/agent-farm/commands/tutorial.js +0 -49
  332. package/dist/agent-farm/commands/tutorial.js.map +0 -1
  333. package/dist/agent-farm/commands/util.d.ts +0 -15
  334. package/dist/agent-farm/commands/util.d.ts.map +0 -1
  335. package/dist/agent-farm/commands/util.js +0 -108
  336. package/dist/agent-farm/commands/util.js.map +0 -1
  337. package/dist/agent-farm/servers/dashboard-server.d.ts +0 -7
  338. package/dist/agent-farm/servers/dashboard-server.d.ts.map +0 -1
  339. package/dist/agent-farm/servers/dashboard-server.js +0 -1872
  340. package/dist/agent-farm/servers/dashboard-server.js.map +0 -1
  341. package/dist/agent-farm/servers/open-server.d.ts +0 -7
  342. package/dist/agent-farm/servers/open-server.d.ts.map +0 -1
  343. package/dist/agent-farm/servers/open-server.js +0 -315
  344. package/dist/agent-farm/servers/open-server.js.map +0 -1
  345. package/dist/agent-farm/tutorial/index.d.ts +0 -8
  346. package/dist/agent-farm/tutorial/index.d.ts.map +0 -1
  347. package/dist/agent-farm/tutorial/index.js +0 -8
  348. package/dist/agent-farm/tutorial/index.js.map +0 -1
  349. package/dist/agent-farm/tutorial/prompts.d.ts +0 -57
  350. package/dist/agent-farm/tutorial/prompts.d.ts.map +0 -1
  351. package/dist/agent-farm/tutorial/prompts.js +0 -147
  352. package/dist/agent-farm/tutorial/prompts.js.map +0 -1
  353. package/dist/agent-farm/tutorial/runner.d.ts +0 -52
  354. package/dist/agent-farm/tutorial/runner.d.ts.map +0 -1
  355. package/dist/agent-farm/tutorial/runner.js +0 -204
  356. package/dist/agent-farm/tutorial/runner.js.map +0 -1
  357. package/dist/agent-farm/tutorial/state.d.ts +0 -26
  358. package/dist/agent-farm/tutorial/state.d.ts.map +0 -1
  359. package/dist/agent-farm/tutorial/state.js +0 -89
  360. package/dist/agent-farm/tutorial/state.js.map +0 -1
  361. package/dist/agent-farm/tutorial/steps/first-spec.d.ts +0 -7
  362. package/dist/agent-farm/tutorial/steps/first-spec.d.ts.map +0 -1
  363. package/dist/agent-farm/tutorial/steps/first-spec.js +0 -136
  364. package/dist/agent-farm/tutorial/steps/first-spec.js.map +0 -1
  365. package/dist/agent-farm/tutorial/steps/implementation.d.ts +0 -7
  366. package/dist/agent-farm/tutorial/steps/implementation.d.ts.map +0 -1
  367. package/dist/agent-farm/tutorial/steps/implementation.js +0 -76
  368. package/dist/agent-farm/tutorial/steps/implementation.js.map +0 -1
  369. package/dist/agent-farm/tutorial/steps/index.d.ts +0 -10
  370. package/dist/agent-farm/tutorial/steps/index.d.ts.map +0 -1
  371. package/dist/agent-farm/tutorial/steps/index.js +0 -10
  372. package/dist/agent-farm/tutorial/steps/index.js.map +0 -1
  373. package/dist/agent-farm/tutorial/steps/planning.d.ts +0 -7
  374. package/dist/agent-farm/tutorial/steps/planning.d.ts.map +0 -1
  375. package/dist/agent-farm/tutorial/steps/planning.js +0 -143
  376. package/dist/agent-farm/tutorial/steps/planning.js.map +0 -1
  377. package/dist/agent-farm/tutorial/steps/review.d.ts +0 -7
  378. package/dist/agent-farm/tutorial/steps/review.d.ts.map +0 -1
  379. package/dist/agent-farm/tutorial/steps/review.js +0 -78
  380. package/dist/agent-farm/tutorial/steps/review.js.map +0 -1
  381. package/dist/agent-farm/tutorial/steps/setup.d.ts +0 -7
  382. package/dist/agent-farm/tutorial/steps/setup.d.ts.map +0 -1
  383. package/dist/agent-farm/tutorial/steps/setup.js +0 -126
  384. package/dist/agent-farm/tutorial/steps/setup.js.map +0 -1
  385. package/dist/agent-farm/tutorial/steps/welcome.d.ts +0 -7
  386. package/dist/agent-farm/tutorial/steps/welcome.d.ts.map +0 -1
  387. package/dist/agent-farm/tutorial/steps/welcome.js +0 -50
  388. package/dist/agent-farm/tutorial/steps/welcome.js.map +0 -1
  389. package/dist/agent-farm/utils/orphan-handler.d.ts +0 -27
  390. package/dist/agent-farm/utils/orphan-handler.d.ts.map +0 -1
  391. package/dist/agent-farm/utils/orphan-handler.js +0 -149
  392. package/dist/agent-farm/utils/orphan-handler.js.map +0 -1
  393. package/dist/agent-farm/utils/port-registry.d.ts +0 -58
  394. package/dist/agent-farm/utils/port-registry.d.ts.map +0 -1
  395. package/dist/agent-farm/utils/port-registry.js +0 -166
  396. package/dist/agent-farm/utils/port-registry.js.map +0 -1
  397. package/dist/agent-farm/utils/terminal-ports.d.ts +0 -18
  398. package/dist/agent-farm/utils/terminal-ports.d.ts.map +0 -1
  399. package/dist/agent-farm/utils/terminal-ports.js +0 -35
  400. package/dist/agent-farm/utils/terminal-ports.js.map +0 -1
  401. package/dist/commands/pcheck/cache.d.ts +0 -48
  402. package/dist/commands/pcheck/cache.d.ts.map +0 -1
  403. package/dist/commands/pcheck/cache.js +0 -170
  404. package/dist/commands/pcheck/cache.js.map +0 -1
  405. package/dist/commands/pcheck/evaluator.d.ts +0 -15
  406. package/dist/commands/pcheck/evaluator.d.ts.map +0 -1
  407. package/dist/commands/pcheck/evaluator.js +0 -246
  408. package/dist/commands/pcheck/evaluator.js.map +0 -1
  409. package/dist/commands/pcheck/index.d.ts +0 -12
  410. package/dist/commands/pcheck/index.d.ts.map +0 -1
  411. package/dist/commands/pcheck/index.js +0 -249
  412. package/dist/commands/pcheck/index.js.map +0 -1
  413. package/dist/commands/pcheck/parser.d.ts +0 -39
  414. package/dist/commands/pcheck/parser.d.ts.map +0 -1
  415. package/dist/commands/pcheck/parser.js +0 -155
  416. package/dist/commands/pcheck/parser.js.map +0 -1
  417. package/dist/commands/pcheck/types.d.ts +0 -82
  418. package/dist/commands/pcheck/types.d.ts.map +0 -1
  419. package/dist/commands/pcheck/types.js +0 -5
  420. package/dist/commands/pcheck/types.js.map +0 -1
  421. package/dist/commands/porch/consultation.d.ts +0 -56
  422. package/dist/commands/porch/consultation.d.ts.map +0 -1
  423. package/dist/commands/porch/consultation.js +0 -330
  424. package/dist/commands/porch/consultation.js.map +0 -1
  425. package/dist/commands/porch/notifications.d.ts +0 -99
  426. package/dist/commands/porch/notifications.d.ts.map +0 -1
  427. package/dist/commands/porch/notifications.js +0 -223
  428. package/dist/commands/porch/notifications.js.map +0 -1
  429. package/dist/commands/porch/plan-parser.d.ts +0 -38
  430. package/dist/commands/porch/plan-parser.d.ts.map +0 -1
  431. package/dist/commands/porch/plan-parser.js +0 -166
  432. package/dist/commands/porch/plan-parser.js.map +0 -1
  433. package/dist/commands/porch/protocol-loader.d.ts +0 -46
  434. package/dist/commands/porch/protocol-loader.d.ts.map +0 -1
  435. package/dist/commands/porch/protocol-loader.js +0 -253
  436. package/dist/commands/porch/protocol-loader.js.map +0 -1
  437. package/dist/commands/porch/signal-parser.d.ts +0 -88
  438. package/dist/commands/porch/signal-parser.d.ts.map +0 -1
  439. package/dist/commands/porch/signal-parser.js +0 -148
  440. package/dist/commands/porch/signal-parser.js.map +0 -1
  441. package/dist/commands/tower.d.ts +0 -16
  442. package/dist/commands/tower.d.ts.map +0 -1
  443. package/dist/commands/tower.js +0 -21
  444. package/dist/commands/tower.js.map +0 -1
  445. package/skeleton/config.json +0 -7
  446. package/skeleton/porch/protocols/bugfix.json +0 -85
  447. package/skeleton/porch/protocols/spider.json +0 -135
  448. package/skeleton/porch/protocols/tick.json +0 -76
  449. package/skeleton/protocols/spider/prompts/defend.md +0 -215
  450. package/skeleton/protocols/spider/prompts/evaluate.md +0 -241
  451. package/skeleton/protocols/spider/prompts/implement.md +0 -149
  452. package/skeleton/protocols/spider/protocol.json +0 -210
  453. package/skeleton/protocols/spider/templates/review.md +0 -207
  454. package/templates/dashboard/css/activity.css +0 -151
  455. package/templates/dashboard/js/activity.js +0 -112
  456. /package/skeleton/protocols/{spider → spir}/templates/spec.md +0 -0
@@ -1,774 +1,195 @@
1
1
  /**
2
2
  * Porch State Management
3
3
  *
4
- * Handles project state persistence with:
5
- * - Pure YAML format (no markdown frontmatter)
6
- * - Atomic writes (tmp file + fsync + rename)
7
- * - File locking (flock advisory locking)
8
- * - Crash recovery
4
+ * Handles project state persistence with atomic writes.
5
+ * Fails loudly on any error - no guessing.
9
6
  */
10
7
  import * as fs from 'node:fs';
11
8
  import * as path from 'node:path';
12
- import { openSync, closeSync, fsyncSync, writeFileSync, renameSync, unlinkSync, readFileSync } from 'node:fs';
13
- // ============================================================================
14
- // Constants
15
- // ============================================================================
16
- /** Directory for SPIDER project state (relative to project root) */
9
+ import * as yaml from 'js-yaml';
10
+ /** Directory for project state (relative to project root) */
17
11
  export const PROJECTS_DIR = 'codev/projects';
18
- /** Directory for TICK/BUGFIX execution state (relative to project root) */
19
- export const EXECUTIONS_DIR = 'codev/executions';
20
- /** Lock timeout in milliseconds */
21
- const LOCK_TIMEOUT_MS = 5000;
22
- /** Lock retry interval in milliseconds */
23
- const LOCK_RETRY_MS = 100;
24
12
  // ============================================================================
25
13
  // Path Utilities
26
14
  // ============================================================================
27
15
  /**
28
- * Get the status file path for a SPIDER project
29
- */
30
- export function getProjectStatusPath(projectRoot, projectId, name) {
31
- const projectDir = name
32
- ? path.join(projectRoot, PROJECTS_DIR, `${projectId}-${name}`)
33
- : path.join(projectRoot, PROJECTS_DIR, projectId);
34
- return path.join(projectDir, 'status.yaml');
35
- }
36
- /**
37
- * Get the status file path for a TICK/BUGFIX execution
38
- */
39
- export function getExecutionStatusPath(projectRoot, protocol, id, name) {
40
- const dirName = name ? `${protocol}_${id}_${name}` : `${protocol}_${id}`;
41
- return path.join(projectRoot, EXECUTIONS_DIR, dirName, 'status.yaml');
42
- }
43
- /**
44
- * Get the project directory for a SPIDER project
16
+ * Get the project directory path
45
17
  */
46
18
  export function getProjectDir(projectRoot, projectId, name) {
47
- return name
48
- ? path.join(projectRoot, PROJECTS_DIR, `${projectId}-${name}`)
49
- : path.join(projectRoot, PROJECTS_DIR, projectId);
50
- }
51
- /**
52
- * Get the worktree path for a protocol execution
53
- */
54
- export function getWorktreePath(projectRoot, protocol, id, name) {
55
- const dirName = name ? `${protocol}_${id}_${name}` : `${protocol}_${id}`;
56
- return path.join(projectRoot, 'worktrees', dirName);
57
- }
58
- /**
59
- * Acquire an advisory lock on a file
60
- * Creates a .lock file to indicate lock ownership
61
- */
62
- export async function acquireLock(filePath) {
63
- const lockFile = `${filePath}.lock`;
64
- const startTime = Date.now();
65
- while (Date.now() - startTime < LOCK_TIMEOUT_MS) {
66
- try {
67
- // Try to create lock file exclusively
68
- const fd = openSync(lockFile, 'wx');
69
- // Write our PID for debugging
70
- writeFileSync(lockFile, `${process.pid}\n`);
71
- return { fd, lockFile };
72
- }
73
- catch (err) {
74
- if (err.code === 'EEXIST') {
75
- // Lock file exists, check if stale
76
- try {
77
- const stat = fs.statSync(lockFile);
78
- // If lock is older than 60 seconds, consider it stale
79
- if (Date.now() - stat.mtimeMs > 60000) {
80
- unlinkSync(lockFile);
81
- continue;
82
- }
83
- }
84
- catch {
85
- // Lock file disappeared, retry
86
- continue;
87
- }
88
- // Wait and retry
89
- await new Promise(r => setTimeout(r, LOCK_RETRY_MS));
90
- }
91
- else {
92
- throw err;
93
- }
94
- }
95
- }
96
- throw new Error(`Failed to acquire lock on ${filePath} after ${LOCK_TIMEOUT_MS}ms`);
19
+ return path.join(projectRoot, PROJECTS_DIR, `${projectId}-${name}`);
97
20
  }
98
21
  /**
99
- * Release an advisory lock
22
+ * Get the status.yaml path for a project
100
23
  */
101
- export function releaseLock(lock) {
102
- try {
103
- closeSync(lock.fd);
104
- unlinkSync(lock.lockFile);
105
- }
106
- catch {
107
- // Ignore errors during cleanup
108
- }
24
+ export function getStatusPath(projectRoot, projectId, name) {
25
+ return path.join(getProjectDir(projectRoot, projectId, name), 'status.yaml');
109
26
  }
110
27
  // ============================================================================
111
- // YAML Serialization
28
+ // State Operations
112
29
  // ============================================================================
113
30
  /**
114
- * Simple YAML serializer for project state
115
- * Handles our specific data structures without external dependencies
31
+ * Read project state from status.yaml
32
+ * Fails loudly if file is missing or corrupted.
116
33
  */
117
- export function serializeState(state) {
118
- const lines = [];
119
- // Basic fields
120
- lines.push(`id: "${state.id}"`);
121
- lines.push(`title: "${state.title}"`);
122
- lines.push(`protocol: "${state.protocol}"`);
123
- lines.push(`state: "${state.current_state}"`);
124
- if (state.worktree) {
125
- lines.push(`worktree: "${state.worktree}"`);
126
- }
127
- lines.push('');
128
- // Gates
129
- lines.push('gates:');
130
- if (state.gates && Object.keys(state.gates).length > 0) {
131
- for (const [gateId, gateStatus] of Object.entries(state.gates)) {
132
- const status = gateStatus.status || 'pending';
133
- const requestedAt = gateStatus.requested_at ? `, requested_at: "${gateStatus.requested_at}"` : '';
134
- const approvedAt = gateStatus.approved_at ? `, approved_at: "${gateStatus.approved_at}"` : '';
135
- lines.push(` ${gateId}: { status: ${status}${requestedAt}${approvedAt} }`);
136
- }
137
- }
138
- else {
139
- lines.push(' # No gates defined');
140
- }
141
- lines.push('');
142
- // Phases (for phased implementation)
143
- lines.push('phases:');
144
- if (state.phases && Object.keys(state.phases).length > 0) {
145
- for (const [phaseId, phaseStatus] of Object.entries(state.phases)) {
146
- if (typeof phaseStatus === 'object' && phaseStatus !== null) {
147
- const ps = phaseStatus;
148
- const status = ps.status || 'pending';
149
- const title = ps.title ? `, title: "${ps.title}"` : '';
150
- lines.push(` ${phaseId}: { status: ${status}${title} }`);
151
- }
152
- }
153
- }
154
- else {
155
- lines.push(' # No phases extracted yet');
156
- }
157
- lines.push('');
158
- // Plan phases (extracted from plan.md)
159
- if (state.plan_phases && state.plan_phases.length > 0) {
160
- lines.push('plan_phases:');
161
- for (const phase of state.plan_phases) {
162
- lines.push(` - id: "${phase.id}"`);
163
- lines.push(` title: "${phase.title}"`);
164
- if (phase.description) {
165
- lines.push(` description: "${phase.description.replace(/"/g, '\\"')}"`);
166
- }
167
- }
168
- lines.push('');
34
+ export function readState(statusPath) {
35
+ if (!fs.existsSync(statusPath)) {
36
+ throw new Error(`Project not found: ${statusPath}\nRun 'porch init' to create a new project.`);
169
37
  }
170
- // Consultation attempts (for tracking retries across iterations)
171
- if (state.consultation_attempts && Object.keys(state.consultation_attempts).length > 0) {
172
- lines.push('consultation_attempts:');
173
- for (const [stateKey, count] of Object.entries(state.consultation_attempts)) {
174
- lines.push(` "${stateKey}": ${count}`);
175
- }
176
- lines.push('');
177
- }
178
- // Metadata
179
- lines.push(`iteration: ${state.iteration || 0}`);
180
- lines.push(`started_at: "${state.started_at || new Date().toISOString()}"`);
181
- lines.push(`last_updated: "${new Date().toISOString()}"`);
182
- lines.push('');
183
- // Log
184
- lines.push('log:');
185
- if (state.log && state.log.length > 0) {
186
- for (const entry of state.log) {
187
- if (typeof entry === 'string') {
188
- lines.push(` - "${entry}"`);
189
- }
190
- else if (typeof entry === 'object' && entry !== null) {
191
- const logEntry = entry;
192
- const ts = logEntry.ts || new Date().toISOString();
193
- const event = logEntry.event || 'unknown';
194
- let entryLine = ` - ts: "${ts}"`;
195
- lines.push(entryLine);
196
- lines.push(` event: "${event}"`);
197
- if (logEntry.from)
198
- lines.push(` from: "${logEntry.from}"`);
199
- if (logEntry.to)
200
- lines.push(` to: "${logEntry.to}"`);
201
- if (logEntry.signal)
202
- lines.push(` signal: "${logEntry.signal}"`);
203
- }
204
- }
205
- }
206
- return lines.join('\n') + '\n';
207
- }
208
- /**
209
- * Parse YAML status file into ProjectState
210
- */
211
- export function parseState(content) {
212
- const state = {
213
- gates: {},
214
- phases: {},
215
- log: [],
216
- consultation_attempts: {},
217
- };
218
- const lines = content.split('\n');
219
- let currentSection = '';
220
- let currentArrayItem = null;
221
- for (const line of lines) {
222
- // Skip comments and empty lines
223
- if (line.trim().startsWith('#') || line.trim() === '') {
224
- continue;
225
- }
226
- // Reset section if we hit a non-indented line (top-level field)
227
- if (currentSection && !line.startsWith(' ') && !line.startsWith('\t')) {
228
- // If we have a pending array item, push it before leaving the section
229
- if (currentArrayItem) {
230
- if (currentSection === 'plan_phases') {
231
- state.plan_phases.push(currentArrayItem);
232
- }
233
- else if (currentSection === 'log') {
234
- state.log.push(currentArrayItem);
235
- }
236
- currentArrayItem = null;
237
- }
238
- currentSection = '';
239
- }
240
- // Detect section headers
241
- if (line.match(/^gates:\s*$/)) {
242
- currentSection = 'gates';
243
- continue;
244
- }
245
- if (line.match(/^phases:\s*$/)) {
246
- currentSection = 'phases';
247
- continue;
248
- }
249
- if (line.match(/^plan_phases:\s*$/)) {
250
- currentSection = 'plan_phases';
251
- state.plan_phases = [];
252
- continue;
253
- }
254
- if (line.match(/^log:\s*$/)) {
255
- currentSection = 'log';
256
- continue;
257
- }
258
- if (line.match(/^consultation_attempts:\s*$/)) {
259
- currentSection = 'consultation_attempts';
260
- continue;
261
- }
262
- // Parse based on section
263
- if (currentSection === 'gates') {
264
- // Parse: gate_id: { status: pending, requested_at: "..." }
265
- const match = line.match(/^\s+(\w+):\s*\{\s*status:\s*(\w+)(?:,\s*requested_at:\s*"([^"]*)")?(?:,\s*approved_at:\s*"([^"]*)")?\s*\}/);
266
- if (match) {
267
- const [, gateId, status, requestedAt, approvedAt] = match;
268
- state.gates[gateId] = {
269
- status: status,
270
- ...(requestedAt && { requested_at: requestedAt }),
271
- ...(approvedAt && { approved_at: approvedAt }),
272
- };
273
- }
274
- continue;
275
- }
276
- if (currentSection === 'phases') {
277
- // Parse: phase_id: { status: pending, title: "..." }
278
- const match = line.match(/^\s+(\w+):\s*\{\s*status:\s*(\w+)(?:,\s*title:\s*"([^"]*)")?\s*\}/);
279
- if (match) {
280
- const [, phaseId, status, title] = match;
281
- state.phases[phaseId] = {
282
- status: status,
283
- ...(title && { title }),
284
- };
285
- }
286
- continue;
287
- }
288
- if (currentSection === 'plan_phases') {
289
- // Parse array items
290
- if (line.match(/^\s+-\s+id:/)) {
291
- if (currentArrayItem) {
292
- state.plan_phases.push(currentArrayItem);
293
- }
294
- currentArrayItem = {};
295
- const idMatch = line.match(/id:\s*"([^"]*)"/);
296
- if (idMatch)
297
- currentArrayItem.id = idMatch[1];
298
- }
299
- else if (line.match(/^\s+title:/)) {
300
- const titleMatch = line.match(/title:\s*"([^"]*)"/);
301
- if (titleMatch && currentArrayItem)
302
- currentArrayItem.title = titleMatch[1];
303
- }
304
- else if (line.match(/^\s+description:/)) {
305
- const descMatch = line.match(/description:\s*"([^"]*)"/);
306
- if (descMatch && currentArrayItem)
307
- currentArrayItem.description = descMatch[1];
308
- }
309
- continue;
310
- }
311
- if (currentSection === 'consultation_attempts') {
312
- // Parse: "state:key": count
313
- const match = line.match(/^\s+"([^"]+)":\s*(\d+)/);
314
- if (match) {
315
- const [, stateKey, count] = match;
316
- state.consultation_attempts[stateKey] = parseInt(count, 10);
317
- }
318
- continue;
319
- }
320
- if (currentSection === 'log') {
321
- // Parse log entries - simplified
322
- if (line.match(/^\s+-\s+ts:/)) {
323
- if (currentArrayItem) {
324
- state.log.push(currentArrayItem);
325
- }
326
- currentArrayItem = {};
327
- const tsMatch = line.match(/ts:\s*"([^"]*)"/);
328
- if (tsMatch)
329
- currentArrayItem.ts = tsMatch[1];
330
- }
331
- else if (line.match(/^\s+event:/)) {
332
- const eventMatch = line.match(/event:\s*"([^"]*)"/);
333
- if (eventMatch && currentArrayItem)
334
- currentArrayItem.event = eventMatch[1];
335
- }
336
- else if (line.match(/^\s+from:/)) {
337
- const fromMatch = line.match(/from:\s*"([^"]*)"/);
338
- if (fromMatch && currentArrayItem)
339
- currentArrayItem.from = fromMatch[1];
340
- }
341
- else if (line.match(/^\s+to:/)) {
342
- const toMatch = line.match(/to:\s*"([^"]*)"/);
343
- if (toMatch && currentArrayItem)
344
- currentArrayItem.to = toMatch[1];
345
- }
346
- else if (line.match(/^\s+signal:/)) {
347
- const signalMatch = line.match(/signal:\s*"([^"]*)"/);
348
- if (signalMatch && currentArrayItem)
349
- currentArrayItem.signal = signalMatch[1];
350
- }
351
- else if (line.match(/^\s+-\s*"[^"]*"/)) {
352
- // Simple string log entry
353
- const strMatch = line.match(/^\s+-\s*"([^"]*)"/);
354
- if (strMatch)
355
- state.log.push(strMatch[1]);
356
- }
357
- continue;
38
+ try {
39
+ const content = fs.readFileSync(statusPath, 'utf-8');
40
+ const state = yaml.load(content);
41
+ // Basic validation
42
+ if (!state || typeof state !== 'object') {
43
+ throw new Error('Invalid state file: not an object');
358
44
  }
359
- // Top-level fields
360
- const kvMatch = line.match(/^(\w+):\s*"?([^"\n]*)"?$/);
361
- if (kvMatch) {
362
- const [, key, value] = kvMatch;
363
- switch (key) {
364
- case 'id':
365
- state.id = value;
366
- break;
367
- case 'title':
368
- state.title = value;
369
- break;
370
- case 'protocol':
371
- state.protocol = value;
372
- break;
373
- case 'state':
374
- state.current_state = value;
375
- break;
376
- case 'worktree':
377
- state.worktree = value;
378
- break;
379
- case 'iteration':
380
- state.iteration = parseInt(value, 10);
381
- break;
382
- case 'started_at':
383
- state.started_at = value;
384
- break;
385
- case 'last_updated':
386
- state.last_updated = value;
387
- break;
388
- }
45
+ if (!state.id || !state.protocol || !state.phase) {
46
+ throw new Error('Invalid state file: missing required fields (id, protocol, phase)');
389
47
  }
48
+ return state;
390
49
  }
391
- // Push final array item if exists
392
- if (currentArrayItem) {
393
- if (currentSection === 'plan_phases') {
394
- state.plan_phases.push(currentArrayItem);
395
- }
396
- else if (currentSection === 'log') {
397
- state.log.push(currentArrayItem);
50
+ catch (err) {
51
+ if (err instanceof yaml.YAMLException) {
52
+ throw new Error(`Invalid state file: YAML parse error\n${err.message}`);
398
53
  }
54
+ throw err;
399
55
  }
400
- return state;
401
56
  }
402
- // ============================================================================
403
- // State Operations
404
- // ============================================================================
405
57
  /**
406
- * Read project state from status file
58
+ * Write project state atomically (tmp file + rename)
407
59
  */
408
- export function readState(statusFilePath) {
409
- // Check for crash recovery - .tmp file exists
410
- const tmpPath = `${statusFilePath}.tmp`;
411
- if (fs.existsSync(tmpPath)) {
412
- try {
413
- const tmpContent = readFileSync(tmpPath, 'utf-8');
414
- const tmpState = parseState(tmpContent);
415
- // tmp file is valid, use it and clean up
416
- renameSync(tmpPath, statusFilePath);
417
- console.log('[porch] Recovered state from interrupted write');
418
- return tmpState;
419
- }
420
- catch {
421
- // tmp file is corrupt, delete it
422
- unlinkSync(tmpPath);
423
- }
424
- }
425
- if (!fs.existsSync(statusFilePath)) {
426
- return null;
427
- }
428
- const content = readFileSync(statusFilePath, 'utf-8');
429
- return parseState(content);
430
- }
431
- /**
432
- * Write project state atomically
433
- * Uses tmp file + fsync + rename for crash safety
434
- */
435
- export async function writeState(statusFilePath, state) {
436
- const lock = await acquireLock(statusFilePath);
437
- try {
438
- const content = serializeState(state);
439
- const tmpPath = `${statusFilePath}.tmp`;
440
- const dir = path.dirname(statusFilePath);
441
- // Ensure directory exists
442
- fs.mkdirSync(dir, { recursive: true });
443
- // Write to temp file
444
- const fd = openSync(tmpPath, 'w');
445
- writeFileSync(fd, content);
446
- fsyncSync(fd);
447
- closeSync(fd);
448
- // Atomic rename
449
- renameSync(tmpPath, statusFilePath);
450
- }
451
- finally {
452
- releaseLock(lock);
453
- }
60
+ export function writeState(statusPath, state) {
61
+ const dir = path.dirname(statusPath);
62
+ const tmpPath = `${statusPath}.tmp`;
63
+ // Ensure directory exists
64
+ fs.mkdirSync(dir, { recursive: true });
65
+ // Update timestamp
66
+ state.updated_at = new Date().toISOString();
67
+ // Write to temp file then rename (atomic)
68
+ const content = yaml.dump(state, {
69
+ indent: 2,
70
+ lineWidth: 120,
71
+ noRefs: true,
72
+ });
73
+ fs.writeFileSync(tmpPath, content, 'utf-8');
74
+ fs.renameSync(tmpPath, statusPath);
454
75
  }
455
76
  /**
456
- * Create initial project state
77
+ * Create initial state for a new project.
78
+ *
79
+ * Always starts at the first protocol phase. If artifacts (spec, plan)
80
+ * already exist with approval metadata (YAML frontmatter), the run loop
81
+ * will detect this and skip those phases automatically.
457
82
  */
458
- export function createInitialState(protocol, projectId, title, worktreePath) {
83
+ export function createInitialState(protocol, projectId, title, _projectRoot) {
459
84
  const now = new Date().toISOString();
460
- // Extract gates from protocol
85
+ // Initialize gates from protocol
461
86
  const gates = {};
462
87
  for (const phase of protocol.phases) {
463
88
  if (phase.gate) {
464
- const gateId = `${phase.id}_approval`;
465
- gates[gateId] = { status: 'pending' };
89
+ gates[phase.gate] = { status: 'pending' };
466
90
  }
467
91
  }
92
+ const initialPhase = protocol.phases[0]?.id || 'specify';
468
93
  return {
469
94
  id: projectId,
470
95
  title,
471
96
  protocol: protocol.name,
472
- current_state: protocol.initial || `${protocol.phases[0]?.id}:draft`,
473
- worktree: worktreePath,
474
- gates,
475
- phases: {},
97
+ phase: initialPhase,
476
98
  plan_phases: [],
477
- iteration: 0,
99
+ current_plan_phase: null,
100
+ gates,
101
+ iteration: 1,
102
+ build_complete: false,
103
+ history: [],
478
104
  started_at: now,
479
- last_updated: now,
480
- log: [{
481
- ts: now,
482
- event: 'state_change',
483
- from: null,
484
- to: protocol.initial || `${protocol.phases[0]?.id}:draft`,
485
- }],
486
- };
487
- }
488
- /**
489
- * Update state with a new current state
490
- */
491
- export function updateState(state, newState, options = {}) {
492
- const now = new Date().toISOString();
493
- const logEntry = {
494
- ts: now,
495
- event: 'state_change',
496
- from: state.current_state,
497
- to: newState,
498
- };
499
- if (options.signal) {
500
- logEntry.signal = options.signal;
501
- }
502
- return {
503
- ...state,
504
- current_state: newState,
505
- iteration: state.iteration + 1,
506
- last_updated: now,
507
- log: [...state.log, logEntry],
508
- };
509
- }
510
- /**
511
- * Approve a gate in state
512
- */
513
- export function approveGate(state, gateId) {
514
- const now = new Date().toISOString();
515
- return {
516
- ...state,
517
- gates: {
518
- ...state.gates,
519
- [gateId]: {
520
- ...state.gates[gateId],
521
- status: 'passed',
522
- approved_at: now,
523
- },
524
- },
525
- last_updated: now,
526
- log: [...state.log, {
527
- ts: now,
528
- event: 'gate_approved',
529
- gate: gateId,
530
- }],
531
- };
532
- }
533
- /**
534
- * Request a gate approval (mark as pending with timestamp)
535
- */
536
- export function requestGateApproval(state, gateId) {
537
- const now = new Date().toISOString();
538
- return {
539
- ...state,
540
- gates: {
541
- ...state.gates,
542
- [gateId]: {
543
- status: 'pending',
544
- requested_at: now,
545
- },
546
- },
547
- last_updated: now,
548
- log: [...state.log, {
549
- ts: now,
550
- event: 'gate_requested',
551
- gate: gateId,
552
- }],
553
- };
554
- }
555
- /**
556
- * Update phase status
557
- */
558
- export function updatePhaseStatus(state, phaseId, status) {
559
- const now = new Date().toISOString();
560
- return {
561
- ...state,
562
- phases: {
563
- ...state.phases,
564
- [phaseId]: {
565
- ...state.phases[phaseId],
566
- status,
567
- },
568
- },
569
- last_updated: now,
570
- log: [...state.log, {
571
- ts: now,
572
- event: 'phase_status_change',
573
- phase: phaseId,
574
- status,
575
- }],
576
- };
577
- }
578
- /**
579
- * Set plan phases extracted from plan.md
580
- */
581
- export function setPlanPhases(state, phases) {
582
- const now = new Date().toISOString();
583
- // Initialize phase status for each extracted phase
584
- const phaseStatus = {};
585
- for (const phase of phases) {
586
- phaseStatus[phase.id] = { status: 'pending', title: phase.title };
587
- }
588
- return {
589
- ...state,
590
- plan_phases: phases,
591
- phases: phaseStatus,
592
- last_updated: now,
593
- log: [...state.log, {
594
- ts: now,
595
- event: 'plan_phases_extracted',
596
- count: phases.length,
597
- }],
105
+ updated_at: now,
598
106
  };
599
107
  }
600
108
  // ============================================================================
601
109
  // Discovery
602
110
  // ============================================================================
603
111
  /**
604
- * Find all SPIDER projects
112
+ * Find status.yaml by project ID (searches for NNNN-* directories)
605
113
  */
606
- export function findProjects(projectRoot) {
114
+ export function findStatusPath(projectRoot, projectId) {
607
115
  const projectsDir = path.join(projectRoot, PROJECTS_DIR);
608
116
  if (!fs.existsSync(projectsDir)) {
609
- return [];
117
+ return null;
610
118
  }
611
119
  const entries = fs.readdirSync(projectsDir, { withFileTypes: true });
612
- const projects = [];
613
120
  for (const entry of entries) {
614
- if (entry.isDirectory()) {
121
+ if (entry.isDirectory() && entry.name.startsWith(`${projectId}-`)) {
615
122
  const statusPath = path.join(projectsDir, entry.name, 'status.yaml');
616
123
  if (fs.existsSync(statusPath)) {
617
- // Extract project ID from directory name (format: NNNN-name or just NNNN)
618
- const idMatch = entry.name.match(/^(\d+)/);
619
- if (idMatch) {
620
- projects.push({
621
- id: idMatch[1],
622
- path: statusPath,
623
- });
624
- }
124
+ return statusPath;
625
125
  }
626
126
  }
627
127
  }
628
- return projects;
128
+ return null;
629
129
  }
630
130
  /**
631
- * Find all executions (TICK, BUGFIX, etc.)
131
+ * Detect project ID from the current working directory if inside a builder worktree.
132
+ * Works from any subdirectory within the worktree.
133
+ * Returns the porch project ID (e.g. "bugfix-237" or "0073"), or null if not in a recognized worktree.
632
134
  */
633
- export function findExecutions(projectRoot) {
634
- const executionsDir = path.join(projectRoot, EXECUTIONS_DIR);
635
- if (!fs.existsSync(executionsDir)) {
636
- return [];
135
+ export function detectProjectIdFromCwd(cwd) {
136
+ const normalized = path.resolve(cwd).split(path.sep).join('/');
137
+ const match = normalized.match(/\/\.builders\/(bugfix-(\d+)|(\d{4}))(\/|$)/);
138
+ if (!match)
139
+ return null;
140
+ // Bugfix worktrees use "bugfix-N" as the porch project ID
141
+ if (match[2])
142
+ return `bugfix-${match[2]}`;
143
+ // Spec worktrees use zero-padded numeric IDs
144
+ return match[3];
145
+ }
146
+ /**
147
+ * Resolve project ID using the priority chain:
148
+ * 1. Explicit CLI argument (highest priority)
149
+ * 2. CWD worktree detection
150
+ * 3. Filesystem scan fallback
151
+ * 4. Error if none succeed
152
+ */
153
+ export function resolveProjectId(provided, cwd, projectRoot) {
154
+ // 1. Explicit CLI argument (highest priority)
155
+ if (provided)
156
+ return { id: provided, source: 'explicit' };
157
+ // 2. CWD worktree detection
158
+ const fromCwd = detectProjectIdFromCwd(cwd);
159
+ if (fromCwd)
160
+ return { id: fromCwd, source: 'cwd' };
161
+ // 3. Filesystem scan fallback
162
+ const detected = detectProjectId(projectRoot);
163
+ if (detected)
164
+ return { id: detected, source: 'filesystem' };
165
+ // 4. Error — none of the detection methods succeeded
166
+ throw new Error('Cannot determine project ID. Provide it explicitly or run from a builder worktree.');
167
+ }
168
+ /**
169
+ * Auto-detect project ID when only one project exists.
170
+ * Returns null if zero or multiple projects found.
171
+ */
172
+ export function detectProjectId(projectRoot) {
173
+ const projectsDir = path.join(projectRoot, PROJECTS_DIR);
174
+ if (!fs.existsSync(projectsDir)) {
175
+ return null;
637
176
  }
638
- const entries = fs.readdirSync(executionsDir, { withFileTypes: true });
639
- const executions = [];
177
+ const entries = fs.readdirSync(projectsDir, { withFileTypes: true });
178
+ const projects = [];
640
179
  for (const entry of entries) {
641
180
  if (entry.isDirectory()) {
642
- const statusPath = path.join(executionsDir, entry.name, 'status.yaml');
643
- if (fs.existsSync(statusPath)) {
644
- // Parse directory name (format: protocol_id_name)
645
- const match = entry.name.match(/^(\w+)_(\w+)/);
646
- if (match) {
647
- executions.push({
648
- protocol: match[1],
649
- id: match[2],
650
- path: statusPath,
651
- });
652
- }
653
- }
654
- }
655
- }
656
- return executions;
657
- }
658
- /**
659
- * Find status file for a project by ID
660
- */
661
- export function findStatusFile(projectRoot, projectId) {
662
- // Check projects directory first
663
- const projectsDir = path.join(projectRoot, PROJECTS_DIR);
664
- if (fs.existsSync(projectsDir)) {
665
- const entries = fs.readdirSync(projectsDir);
666
- for (const entry of entries) {
667
- if (entry.startsWith(projectId)) {
668
- const statusPath = path.join(projectsDir, entry, 'status.yaml');
669
- if (fs.existsSync(statusPath)) {
670
- return statusPath;
671
- }
672
- }
673
- }
674
- }
675
- // Check executions directory
676
- const executionsDir = path.join(projectRoot, EXECUTIONS_DIR);
677
- if (fs.existsSync(executionsDir)) {
678
- const entries = fs.readdirSync(executionsDir);
679
- for (const entry of entries) {
680
- if (entry.includes(`_${projectId}`)) {
681
- const statusPath = path.join(executionsDir, entry, 'status.yaml');
181
+ // Extract project ID from directory name
182
+ // Matches: "0076-skip-close" -> "0076", "bugfix-237-fix-name" -> "bugfix-237"
183
+ const match = entry.name.match(/^(bugfix-\d+|\d{4})-/);
184
+ if (match) {
185
+ const statusPath = path.join(projectsDir, entry.name, 'status.yaml');
682
186
  if (fs.existsSync(statusPath)) {
683
- return statusPath;
684
- }
685
- }
686
- }
687
- }
688
- return null;
689
- }
690
- // ============================================================================
691
- // Consultation Attempt Tracking
692
- // ============================================================================
693
- /**
694
- * Get the number of consultation attempts for a given state
695
- */
696
- export function getConsultationAttempts(state, stateKey) {
697
- return state.consultation_attempts?.[stateKey] ?? 0;
698
- }
699
- /**
700
- * Increment consultation attempts for a given state
701
- */
702
- export function incrementConsultationAttempts(state, stateKey) {
703
- const now = new Date().toISOString();
704
- const currentAttempts = getConsultationAttempts(state, stateKey);
705
- return {
706
- ...state,
707
- consultation_attempts: {
708
- ...state.consultation_attempts,
709
- [stateKey]: currentAttempts + 1,
710
- },
711
- last_updated: now,
712
- log: [...state.log, {
713
- ts: now,
714
- event: 'consultation_attempt',
715
- phase: stateKey,
716
- count: currentAttempts + 1,
717
- }],
718
- };
719
- }
720
- /**
721
- * Reset consultation attempts for a given state (e.g., after gate approval)
722
- */
723
- export function resetConsultationAttempts(state, stateKey) {
724
- const newAttempts = { ...state.consultation_attempts };
725
- delete newAttempts[stateKey];
726
- return {
727
- ...state,
728
- consultation_attempts: newAttempts,
729
- last_updated: new Date().toISOString(),
730
- };
731
- }
732
- // ============================================================================
733
- // Discovery
734
- // ============================================================================
735
- /**
736
- * Find all status files with pending gates
737
- */
738
- export function findPendingGates(projectRoot) {
739
- const pending = [];
740
- // Check projects
741
- for (const { id, path: statusPath } of findProjects(projectRoot)) {
742
- const state = readState(statusPath);
743
- if (state && state.gates) {
744
- for (const [gateId, gateStatus] of Object.entries(state.gates)) {
745
- if (gateStatus.status === 'pending' && gateStatus.requested_at) {
746
- pending.push({
747
- projectId: id,
748
- gateId,
749
- requestedAt: gateStatus.requested_at,
750
- statusPath,
751
- });
752
- }
753
- }
754
- }
755
- }
756
- // Check executions
757
- for (const { id, path: statusPath } of findExecutions(projectRoot)) {
758
- const state = readState(statusPath);
759
- if (state && state.gates) {
760
- for (const [gateId, gateStatus] of Object.entries(state.gates)) {
761
- if (gateStatus.status === 'pending' && gateStatus.requested_at) {
762
- pending.push({
763
- projectId: id,
764
- gateId,
765
- requestedAt: gateStatus.requested_at,
766
- statusPath,
767
- });
187
+ projects.push(match[1]);
768
188
  }
769
189
  }
770
190
  }
771
191
  }
772
- return pending;
192
+ // Only return if exactly one project
193
+ return projects.length === 1 ? projects[0] : null;
773
194
  }
774
195
  //# sourceMappingURL=state.js.map