zero-workspace 0.0.2

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 (507) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +187 -0
  3. package/config/component-versions.json +16 -0
  4. package/config/scenarioCapabilities.json +29 -0
  5. package/config/version-notes.yaml +244 -0
  6. package/dist/adapters/OutputAdapter.d.ts +79 -0
  7. package/dist/adapters/OutputAdapter.d.ts.map +1 -0
  8. package/dist/adapters/OutputAdapter.js +124 -0
  9. package/dist/adapters/OutputAdapter.js.map +1 -0
  10. package/dist/cli/check-node-version.d.ts +3 -0
  11. package/dist/cli/check-node-version.d.ts.map +1 -0
  12. package/dist/cli/check-node-version.js +153 -0
  13. package/dist/cli/check-node-version.js.map +1 -0
  14. package/dist/cli/plugins.d.ts +41 -0
  15. package/dist/cli/plugins.d.ts.map +1 -0
  16. package/dist/cli/plugins.js +742 -0
  17. package/dist/cli/plugins.js.map +1 -0
  18. package/dist/cli/rebuild.d.ts +63 -0
  19. package/dist/cli/rebuild.d.ts.map +1 -0
  20. package/dist/cli/rebuild.js +989 -0
  21. package/dist/cli/rebuild.js.map +1 -0
  22. package/dist/cli/repair.d.ts +7 -0
  23. package/dist/cli/repair.d.ts.map +1 -0
  24. package/dist/cli/repair.js +925 -0
  25. package/dist/cli/repair.js.map +1 -0
  26. package/dist/cli/setup.d.ts +7 -0
  27. package/dist/cli/setup.d.ts.map +1 -0
  28. package/dist/cli/setup.js +452 -0
  29. package/dist/cli/setup.js.map +1 -0
  30. package/dist/cli/update.d.ts +10 -0
  31. package/dist/cli/update.d.ts.map +1 -0
  32. package/dist/cli/update.js +426 -0
  33. package/dist/cli/update.js.map +1 -0
  34. package/dist/cli/webui.d.ts +6 -0
  35. package/dist/cli/webui.d.ts.map +1 -0
  36. package/dist/cli/webui.js +210 -0
  37. package/dist/cli/webui.js.map +1 -0
  38. package/dist/http/index.d.ts +3 -0
  39. package/dist/http/index.d.ts.map +1 -0
  40. package/dist/http/index.js +15 -0
  41. package/dist/http/index.js.map +1 -0
  42. package/dist/http/middleware/errorHandler.d.ts +16 -0
  43. package/dist/http/middleware/errorHandler.d.ts.map +1 -0
  44. package/dist/http/middleware/errorHandler.js +79 -0
  45. package/dist/http/middleware/errorHandler.js.map +1 -0
  46. package/dist/http/routes/admin.d.ts +3 -0
  47. package/dist/http/routes/admin.d.ts.map +1 -0
  48. package/dist/http/routes/admin.js +730 -0
  49. package/dist/http/routes/admin.js.map +1 -0
  50. package/dist/http/routes/backup.d.ts +3 -0
  51. package/dist/http/routes/backup.d.ts.map +1 -0
  52. package/dist/http/routes/backup.js +172 -0
  53. package/dist/http/routes/backup.js.map +1 -0
  54. package/dist/http/routes/config.d.ts +3 -0
  55. package/dist/http/routes/config.d.ts.map +1 -0
  56. package/dist/http/routes/config.js +157 -0
  57. package/dist/http/routes/config.js.map +1 -0
  58. package/dist/http/routes/context.d.ts +3 -0
  59. package/dist/http/routes/context.d.ts.map +1 -0
  60. package/dist/http/routes/context.js +82 -0
  61. package/dist/http/routes/context.js.map +1 -0
  62. package/dist/http/routes/log.d.ts +3 -0
  63. package/dist/http/routes/log.d.ts.map +1 -0
  64. package/dist/http/routes/log.js +105 -0
  65. package/dist/http/routes/log.js.map +1 -0
  66. package/dist/http/routes/memo.d.ts +6 -0
  67. package/dist/http/routes/memo.d.ts.map +1 -0
  68. package/dist/http/routes/memo.js +29 -0
  69. package/dist/http/routes/memo.js.map +1 -0
  70. package/dist/http/routes/node.d.ts +3 -0
  71. package/dist/http/routes/node.d.ts.map +1 -0
  72. package/dist/http/routes/node.js +251 -0
  73. package/dist/http/routes/node.js.map +1 -0
  74. package/dist/http/routes/state.d.ts +3 -0
  75. package/dist/http/routes/state.d.ts.map +1 -0
  76. package/dist/http/routes/state.js +48 -0
  77. package/dist/http/routes/state.js.map +1 -0
  78. package/dist/http/routes/workspace.d.ts +3 -0
  79. package/dist/http/routes/workspace.d.ts.map +1 -0
  80. package/dist/http/routes/workspace.js +249 -0
  81. package/dist/http/routes/workspace.js.map +1 -0
  82. package/dist/http/server.d.ts +10 -0
  83. package/dist/http/server.d.ts.map +1 -0
  84. package/dist/http/server.js +284 -0
  85. package/dist/http/server.js.map +1 -0
  86. package/dist/http/services.d.ts +93 -0
  87. package/dist/http/services.d.ts.map +1 -0
  88. package/dist/http/services.js +297 -0
  89. package/dist/http/services.js.map +1 -0
  90. package/dist/index.d.ts +3 -0
  91. package/dist/index.d.ts.map +1 -0
  92. package/dist/index.js +1073 -0
  93. package/dist/index.js.map +1 -0
  94. package/dist/prompts/guidanceContent.d.ts +18 -0
  95. package/dist/prompts/guidanceContent.d.ts.map +1 -0
  96. package/dist/prompts/guidanceContent.js +814 -0
  97. package/dist/prompts/guidanceContent.js.map +1 -0
  98. package/dist/prompts/index.d.ts +2 -0
  99. package/dist/prompts/index.d.ts.map +1 -0
  100. package/dist/prompts/index.js +4 -0
  101. package/dist/prompts/index.js.map +1 -0
  102. package/dist/prompts/instructions.d.ts +56 -0
  103. package/dist/prompts/instructions.d.ts.map +1 -0
  104. package/dist/prompts/instructions.js +1343 -0
  105. package/dist/prompts/instructions.js.map +1 -0
  106. package/dist/services/BackupService.d.ts +104 -0
  107. package/dist/services/BackupService.d.ts.map +1 -0
  108. package/dist/services/BackupService.js +549 -0
  109. package/dist/services/BackupService.js.map +1 -0
  110. package/dist/services/CapabilityService.d.ts +38 -0
  111. package/dist/services/CapabilityService.d.ts.map +1 -0
  112. package/dist/services/CapabilityService.js +256 -0
  113. package/dist/services/CapabilityService.js.map +1 -0
  114. package/dist/services/ConfigService.d.ts +35 -0
  115. package/dist/services/ConfigService.d.ts.map +1 -0
  116. package/dist/services/ConfigService.js +105 -0
  117. package/dist/services/ConfigService.js.map +1 -0
  118. package/dist/services/ContextService.d.ts +65 -0
  119. package/dist/services/ContextService.d.ts.map +1 -0
  120. package/dist/services/ContextService.js +503 -0
  121. package/dist/services/ContextService.js.map +1 -0
  122. package/dist/services/DetectionService.d.ts +76 -0
  123. package/dist/services/DetectionService.d.ts.map +1 -0
  124. package/dist/services/DetectionService.js +262 -0
  125. package/dist/services/DetectionService.js.map +1 -0
  126. package/dist/services/DispatchService.d.ts +267 -0
  127. package/dist/services/DispatchService.d.ts.map +1 -0
  128. package/dist/services/DispatchService.js +1357 -0
  129. package/dist/services/DispatchService.js.map +1 -0
  130. package/dist/services/EventService.d.ts +81 -0
  131. package/dist/services/EventService.d.ts.map +1 -0
  132. package/dist/services/EventService.js +187 -0
  133. package/dist/services/EventService.js.map +1 -0
  134. package/dist/services/GuidanceService.d.ts +64 -0
  135. package/dist/services/GuidanceService.d.ts.map +1 -0
  136. package/dist/services/GuidanceService.js +259 -0
  137. package/dist/services/GuidanceService.js.map +1 -0
  138. package/dist/services/HealthService.d.ts +43 -0
  139. package/dist/services/HealthService.d.ts.map +1 -0
  140. package/dist/services/HealthService.js +276 -0
  141. package/dist/services/HealthService.js.map +1 -0
  142. package/dist/services/InstallationService.d.ts +62 -0
  143. package/dist/services/InstallationService.d.ts.map +1 -0
  144. package/dist/services/InstallationService.js +204 -0
  145. package/dist/services/InstallationService.js.map +1 -0
  146. package/dist/services/LogService.d.ts +35 -0
  147. package/dist/services/LogService.d.ts.map +1 -0
  148. package/dist/services/LogService.js +189 -0
  149. package/dist/services/LogService.js.map +1 -0
  150. package/dist/services/MemoService.d.ts +39 -0
  151. package/dist/services/MemoService.d.ts.map +1 -0
  152. package/dist/services/MemoService.js +288 -0
  153. package/dist/services/MemoService.js.map +1 -0
  154. package/dist/services/NodeService.d.ts +90 -0
  155. package/dist/services/NodeService.d.ts.map +1 -0
  156. package/dist/services/NodeService.js +958 -0
  157. package/dist/services/NodeService.js.map +1 -0
  158. package/dist/services/OpenSpecParser.d.ts +43 -0
  159. package/dist/services/OpenSpecParser.d.ts.map +1 -0
  160. package/dist/services/OpenSpecParser.js +191 -0
  161. package/dist/services/OpenSpecParser.js.map +1 -0
  162. package/dist/services/ReferenceService.d.ts +35 -0
  163. package/dist/services/ReferenceService.d.ts.map +1 -0
  164. package/dist/services/ReferenceService.js +195 -0
  165. package/dist/services/ReferenceService.js.map +1 -0
  166. package/dist/services/RepairService.d.ts +36 -0
  167. package/dist/services/RepairService.d.ts.map +1 -0
  168. package/dist/services/RepairService.js +429 -0
  169. package/dist/services/RepairService.js.map +1 -0
  170. package/dist/services/SearchService.d.ts +34 -0
  171. package/dist/services/SearchService.d.ts.map +1 -0
  172. package/dist/services/SearchService.js +293 -0
  173. package/dist/services/SearchService.js.map +1 -0
  174. package/dist/services/SessionService.d.ts +136 -0
  175. package/dist/services/SessionService.d.ts.map +1 -0
  176. package/dist/services/SessionService.js +297 -0
  177. package/dist/services/SessionService.js.map +1 -0
  178. package/dist/services/StateService.d.ts +97 -0
  179. package/dist/services/StateService.d.ts.map +1 -0
  180. package/dist/services/StateService.js +846 -0
  181. package/dist/services/StateService.js.map +1 -0
  182. package/dist/services/TutorialService.d.ts +114 -0
  183. package/dist/services/TutorialService.d.ts.map +1 -0
  184. package/dist/services/TutorialService.js +1262 -0
  185. package/dist/services/TutorialService.js.map +1 -0
  186. package/dist/services/WorkspaceService.d.ts +273 -0
  187. package/dist/services/WorkspaceService.d.ts.map +1 -0
  188. package/dist/services/WorkspaceService.js +1764 -0
  189. package/dist/services/WorkspaceService.js.map +1 -0
  190. package/dist/services/index.d.ts +15 -0
  191. package/dist/services/index.d.ts.map +1 -0
  192. package/dist/services/index.js +14 -0
  193. package/dist/services/index.js.map +1 -0
  194. package/dist/storage/FileSystemAdapter.d.ts +223 -0
  195. package/dist/storage/FileSystemAdapter.d.ts.map +1 -0
  196. package/dist/storage/FileSystemAdapter.js +384 -0
  197. package/dist/storage/FileSystemAdapter.js.map +1 -0
  198. package/dist/storage/JsonStorage.d.ts +158 -0
  199. package/dist/storage/JsonStorage.d.ts.map +1 -0
  200. package/dist/storage/JsonStorage.js +613 -0
  201. package/dist/storage/JsonStorage.js.map +1 -0
  202. package/dist/storage/MarkdownStorage.d.ts +178 -0
  203. package/dist/storage/MarkdownStorage.d.ts.map +1 -0
  204. package/dist/storage/MarkdownStorage.js +918 -0
  205. package/dist/storage/MarkdownStorage.js.map +1 -0
  206. package/dist/storage/SessionBindingStorage.d.ts +69 -0
  207. package/dist/storage/SessionBindingStorage.d.ts.map +1 -0
  208. package/dist/storage/SessionBindingStorage.js +131 -0
  209. package/dist/storage/SessionBindingStorage.js.map +1 -0
  210. package/dist/storage/index.d.ts +6 -0
  211. package/dist/storage/index.d.ts.map +1 -0
  212. package/dist/storage/index.js +6 -0
  213. package/dist/storage/index.js.map +1 -0
  214. package/dist/tools/capability.d.ts +18 -0
  215. package/dist/tools/capability.d.ts.map +1 -0
  216. package/dist/tools/capability.js +73 -0
  217. package/dist/tools/capability.js.map +1 -0
  218. package/dist/tools/config.d.ts +14 -0
  219. package/dist/tools/config.d.ts.map +1 -0
  220. package/dist/tools/config.js +61 -0
  221. package/dist/tools/config.js.map +1 -0
  222. package/dist/tools/context.d.ts +22 -0
  223. package/dist/tools/context.d.ts.map +1 -0
  224. package/dist/tools/context.js +139 -0
  225. package/dist/tools/context.js.map +1 -0
  226. package/dist/tools/dispatch.d.ts +41 -0
  227. package/dist/tools/dispatch.d.ts.map +1 -0
  228. package/dist/tools/dispatch.js +380 -0
  229. package/dist/tools/dispatch.js.map +1 -0
  230. package/dist/tools/help.d.ts +44 -0
  231. package/dist/tools/help.d.ts.map +1 -0
  232. package/dist/tools/help.js +227 -0
  233. package/dist/tools/help.js.map +1 -0
  234. package/dist/tools/import.d.ts +17 -0
  235. package/dist/tools/import.d.ts.map +1 -0
  236. package/dist/tools/import.js +96 -0
  237. package/dist/tools/import.js.map +1 -0
  238. package/dist/tools/index.d.ts +12 -0
  239. package/dist/tools/index.d.ts.map +1 -0
  240. package/dist/tools/index.js +13 -0
  241. package/dist/tools/index.js.map +1 -0
  242. package/dist/tools/log.d.ts +21 -0
  243. package/dist/tools/log.d.ts.map +1 -0
  244. package/dist/tools/log.js +93 -0
  245. package/dist/tools/log.js.map +1 -0
  246. package/dist/tools/memo.d.ts +26 -0
  247. package/dist/tools/memo.d.ts.map +1 -0
  248. package/dist/tools/memo.js +188 -0
  249. package/dist/tools/memo.js.map +1 -0
  250. package/dist/tools/node.d.ts +34 -0
  251. package/dist/tools/node.d.ts.map +1 -0
  252. package/dist/tools/node.js +328 -0
  253. package/dist/tools/node.js.map +1 -0
  254. package/dist/tools/search.d.ts +14 -0
  255. package/dist/tools/search.d.ts.map +1 -0
  256. package/dist/tools/search.js +95 -0
  257. package/dist/tools/search.js.map +1 -0
  258. package/dist/tools/session.d.ts +22 -0
  259. package/dist/tools/session.d.ts.map +1 -0
  260. package/dist/tools/session.js +127 -0
  261. package/dist/tools/session.js.map +1 -0
  262. package/dist/tools/state.d.ts +10 -0
  263. package/dist/tools/state.d.ts.map +1 -0
  264. package/dist/tools/state.js +79 -0
  265. package/dist/tools/state.js.map +1 -0
  266. package/dist/tools/workspace.d.ts +38 -0
  267. package/dist/tools/workspace.d.ts.map +1 -0
  268. package/dist/tools/workspace.js +240 -0
  269. package/dist/tools/workspace.js.map +1 -0
  270. package/dist/types/capability.d.ts +36 -0
  271. package/dist/types/capability.d.ts.map +1 -0
  272. package/dist/types/capability.js +3 -0
  273. package/dist/types/capability.js.map +1 -0
  274. package/dist/types/confirmation.d.ts +35 -0
  275. package/dist/types/confirmation.d.ts.map +1 -0
  276. package/dist/types/confirmation.js +3 -0
  277. package/dist/types/confirmation.js.map +1 -0
  278. package/dist/types/context.d.ts +174 -0
  279. package/dist/types/context.d.ts.map +1 -0
  280. package/dist/types/context.js +3 -0
  281. package/dist/types/context.js.map +1 -0
  282. package/dist/types/errors.d.ts +81 -0
  283. package/dist/types/errors.d.ts.map +1 -0
  284. package/dist/types/errors.js +154 -0
  285. package/dist/types/errors.js.map +1 -0
  286. package/dist/types/guidance.d.ts +162 -0
  287. package/dist/types/guidance.d.ts.map +1 -0
  288. package/dist/types/guidance.js +4 -0
  289. package/dist/types/guidance.js.map +1 -0
  290. package/dist/types/health.d.ts +61 -0
  291. package/dist/types/health.d.ts.map +1 -0
  292. package/dist/types/health.js +3 -0
  293. package/dist/types/health.js.map +1 -0
  294. package/dist/types/index.d.ts +10 -0
  295. package/dist/types/index.d.ts.map +1 -0
  296. package/dist/types/index.js +11 -0
  297. package/dist/types/index.js.map +1 -0
  298. package/dist/types/memo.d.ts +132 -0
  299. package/dist/types/memo.d.ts.map +1 -0
  300. package/dist/types/memo.js +3 -0
  301. package/dist/types/memo.js.map +1 -0
  302. package/dist/types/node.d.ts +316 -0
  303. package/dist/types/node.d.ts.map +1 -0
  304. package/dist/types/node.js +3 -0
  305. package/dist/types/node.js.map +1 -0
  306. package/dist/types/repair.d.ts +62 -0
  307. package/dist/types/repair.d.ts.map +1 -0
  308. package/dist/types/repair.js +4 -0
  309. package/dist/types/repair.js.map +1 -0
  310. package/dist/types/search.d.ts +58 -0
  311. package/dist/types/search.d.ts.map +1 -0
  312. package/dist/types/search.js +3 -0
  313. package/dist/types/search.js.map +1 -0
  314. package/dist/types/settings.d.ts +109 -0
  315. package/dist/types/settings.d.ts.map +1 -0
  316. package/dist/types/settings.js +30 -0
  317. package/dist/types/settings.js.map +1 -0
  318. package/dist/types/workspace.d.ts +357 -0
  319. package/dist/types/workspace.d.ts.map +1 -0
  320. package/dist/types/workspace.js +3 -0
  321. package/dist/types/workspace.js.map +1 -0
  322. package/dist/utils/contentValidation.d.ts +47 -0
  323. package/dist/utils/contentValidation.d.ts.map +1 -0
  324. package/dist/utils/contentValidation.js +93 -0
  325. package/dist/utils/contentValidation.js.map +1 -0
  326. package/dist/utils/devLog.d.ts +43 -0
  327. package/dist/utils/devLog.d.ts.map +1 -0
  328. package/dist/utils/devLog.js +94 -0
  329. package/dist/utils/devLog.js.map +1 -0
  330. package/dist/utils/errorLogger.d.ts +27 -0
  331. package/dist/utils/errorLogger.d.ts.map +1 -0
  332. package/dist/utils/errorLogger.js +105 -0
  333. package/dist/utils/errorLogger.js.map +1 -0
  334. package/dist/utils/git.d.ts +123 -0
  335. package/dist/utils/git.d.ts.map +1 -0
  336. package/dist/utils/git.js +400 -0
  337. package/dist/utils/git.js.map +1 -0
  338. package/dist/utils/hash.d.ts +32 -0
  339. package/dist/utils/hash.d.ts.map +1 -0
  340. package/dist/utils/hash.js +37 -0
  341. package/dist/utils/hash.js.map +1 -0
  342. package/dist/utils/id.d.ts +54 -0
  343. package/dist/utils/id.d.ts.map +1 -0
  344. package/dist/utils/id.js +96 -0
  345. package/dist/utils/id.js.map +1 -0
  346. package/dist/utils/index.d.ts +8 -0
  347. package/dist/utils/index.d.ts.map +1 -0
  348. package/dist/utils/index.js +9 -0
  349. package/dist/utils/index.js.map +1 -0
  350. package/dist/utils/logger.d.ts +42 -0
  351. package/dist/utils/logger.d.ts.map +1 -0
  352. package/dist/utils/logger.js +228 -0
  353. package/dist/utils/logger.js.map +1 -0
  354. package/dist/utils/manualChangeFormatter.d.ts +8 -0
  355. package/dist/utils/manualChangeFormatter.d.ts.map +1 -0
  356. package/dist/utils/manualChangeFormatter.js +21 -0
  357. package/dist/utils/manualChangeFormatter.js.map +1 -0
  358. package/dist/utils/paramValidator.d.ts +35 -0
  359. package/dist/utils/paramValidator.d.ts.map +1 -0
  360. package/dist/utils/paramValidator.js +214 -0
  361. package/dist/utils/paramValidator.js.map +1 -0
  362. package/dist/utils/port.d.ts +7 -0
  363. package/dist/utils/port.d.ts.map +1 -0
  364. package/dist/utils/port.js +28 -0
  365. package/dist/utils/port.js.map +1 -0
  366. package/dist/utils/processManager.d.ts +53 -0
  367. package/dist/utils/processManager.d.ts.map +1 -0
  368. package/dist/utils/processManager.js +267 -0
  369. package/dist/utils/processManager.js.map +1 -0
  370. package/dist/utils/sessionLogger.d.ts +28 -0
  371. package/dist/utils/sessionLogger.d.ts.map +1 -0
  372. package/dist/utils/sessionLogger.js +142 -0
  373. package/dist/utils/sessionLogger.js.map +1 -0
  374. package/dist/utils/time.d.ts +15 -0
  375. package/dist/utils/time.d.ts.map +1 -0
  376. package/dist/utils/time.js +32 -0
  377. package/dist/utils/time.js.map +1 -0
  378. package/dist/utils/validation.d.ts +23 -0
  379. package/dist/utils/validation.d.ts.map +1 -0
  380. package/dist/utils/validation.js +88 -0
  381. package/dist/utils/validation.js.map +1 -0
  382. package/docs//346/227/245/345/277/227/347/263/273/347/273/237.md +389 -0
  383. package/docs//347/224/250/346/210/267/346/211/213/345/206/214.md +1446 -0
  384. package/docs//347/224/250/346/210/267/346/211/213/345/206/214/344/270/216/346/212/200/346/234/257/346/214/207/345/215/227.md +873 -0
  385. package/package.json +94 -0
  386. package/plugin/README.md +141 -0
  387. package/plugin/agents/zero-executor.md +114 -0
  388. package/plugin/agents/zero-reviewer.md +133 -0
  389. package/plugin/docs/diagnostic-guide.md +128 -0
  390. package/plugin/hooks/hooks.json.deprecated +70 -0
  391. package/plugin/scripts/cursor-hook-entry.cjs +217 -0
  392. package/plugin/scripts/hook-entry.cjs +663 -0
  393. package/plugin/scripts/openspec-import.cjs +714 -0
  394. package/plugin/scripts/shared/binding.cjs +98 -0
  395. package/plugin/scripts/shared/config.cjs +65 -0
  396. package/plugin/scripts/shared/context.cjs +120 -0
  397. package/plugin/scripts/shared/index.cjs +34 -0
  398. package/plugin/scripts/shared/logger.cjs +196 -0
  399. package/plugin/scripts/shared/reminder.cjs +261 -0
  400. package/plugin/scripts/shared/utils.cjs +62 -0
  401. package/plugin/scripts/shared/workspace.cjs +322 -0
  402. package/plugin/skills/aligning-intent/SKILL.md +275 -0
  403. package/plugin/skills/analyzing-measurements/SKILL.md +223 -0
  404. package/plugin/skills/bootstrapping-workspace/SKILL.md +260 -0
  405. package/plugin/skills/designing-solutions/SKILL.md +363 -0
  406. package/plugin/skills/diagnosing-issues/SKILL.md +219 -0
  407. package/plugin/skills/discovering-context/SKILL.md +283 -0
  408. package/plugin/skills/dispatching-parent/SKILL.md +399 -0
  409. package/plugin/skills/executing-task/SKILL.md +340 -0
  410. package/plugin/skills/memo-create/SKILL.md +222 -0
  411. package/plugin/skills/planning-verification/SKILL.md +245 -0
  412. package/plugin/skills/preparing-dispatch/SKILL.md +299 -0
  413. package/plugin/skills/researching-tech/SKILL.md +223 -0
  414. package/plugin/skills/reviewing-quality/SKILL.md +354 -0
  415. package/plugin/skills/reviewing-spec/SKILL.md +333 -0
  416. package/plugin/skills/starting-info-flow/SKILL.md +196 -0
  417. package/web/README.md +5 -0
  418. package/web/dist/assets/DocsView-Bls_Vjsr.css +1 -0
  419. package/web/dist/assets/DocsView-Cxc0B63r.js +1447 -0
  420. package/web/dist/assets/HomeView-C7df9thb.js +9 -0
  421. package/web/dist/assets/HomeView-ufUdnfHk.css +1 -0
  422. package/web/dist/assets/MarkdownContent-DXp6CtSP.js +308 -0
  423. package/web/dist/assets/MarkdownContent-NFqiOBLH.css +1 -0
  424. package/web/dist/assets/NotFoundView-BYX1oZAn.css +1 -0
  425. package/web/dist/assets/NotFoundView-zrc0lT9q.js +1 -0
  426. package/web/dist/assets/WorkspaceView-BckqgNcX.js +27 -0
  427. package/web/dist/assets/WorkspaceView-J1dgpYMx.css +1 -0
  428. package/web/dist/assets/WsConfirmDialog-C1CvL4my.css +1 -0
  429. package/web/dist/assets/WsConfirmDialog-gLEP7uBD.js +4 -0
  430. package/web/dist/assets/arc-DPkKTkUT.js +1 -0
  431. package/web/dist/assets/architectureDiagram-VXUJARFQ-CEGpqUlZ.js +36 -0
  432. package/web/dist/assets/blockDiagram-VD42YOAC-Bv-mqdQH.js +122 -0
  433. package/web/dist/assets/c4Diagram-YG6GDRKO-DRyPatZ_.js +10 -0
  434. package/web/dist/assets/channel-B84mKLDZ.js +1 -0
  435. package/web/dist/assets/chunk-4BX2VUAB-c7DivX0u.js +1 -0
  436. package/web/dist/assets/chunk-55IACEB6-CGKTaLlo.js +1 -0
  437. package/web/dist/assets/chunk-B4BG7PRW-Czhx5Q_P.js +165 -0
  438. package/web/dist/assets/chunk-DI55MBZ5-CQVA7hcZ.js +220 -0
  439. package/web/dist/assets/chunk-FMBD7UC4-hEiPmi7V.js +15 -0
  440. package/web/dist/assets/chunk-QN33PNHL-rL6yYI-E.js +1 -0
  441. package/web/dist/assets/chunk-QZHKN3VN-BRyHBBzq.js +1 -0
  442. package/web/dist/assets/chunk-TZMSLE5B-D4PXmTz9.js +1 -0
  443. package/web/dist/assets/classDiagram-2ON5EDUG-CNn53ohi.js +1 -0
  444. package/web/dist/assets/classDiagram-v2-WZHVMYZB-CNn53ohi.js +1 -0
  445. package/web/dist/assets/cose-bilkent-S5V4N54A-BAREnRga.js +1 -0
  446. package/web/dist/assets/cytoscape.esm-BnkdMOzK.js +321 -0
  447. package/web/dist/assets/dagre-6UL2VRFP-DaYzb3MT.js +4 -0
  448. package/web/dist/assets/defaultLocale-C4B-KCzX.js +1 -0
  449. package/web/dist/assets/diagram-PSM6KHXK-BFltDqvd.js +24 -0
  450. package/web/dist/assets/diagram-QEK2KX5R-CR4VU2La.js +43 -0
  451. package/web/dist/assets/diagram-S2PKOQOG-0UfIeT-1.js +24 -0
  452. package/web/dist/assets/erDiagram-Q2GNP2WA-Bo17Xmng.js +60 -0
  453. package/web/dist/assets/flowDiagram-NV44I4VS-CzqhQp8s.js +162 -0
  454. package/web/dist/assets/ganttDiagram-JELNMOA3-TXwXtUcq.js +267 -0
  455. package/web/dist/assets/gitGraphDiagram-NY62KEGX-CoFQTy9O.js +65 -0
  456. package/web/dist/assets/graph-CIQcRIVd.js +1 -0
  457. package/web/dist/assets/index-BgLd_o_M.css +1 -0
  458. package/web/dist/assets/index-Cd_J3fZn.js +30 -0
  459. package/web/dist/assets/infoDiagram-WHAUD3N6-Dq0xXfVu.js +2 -0
  460. package/web/dist/assets/init-Gi6I4Gst.js +1 -0
  461. package/web/dist/assets/journeyDiagram-XKPGCS4Q-jIg5BOfC.js +139 -0
  462. package/web/dist/assets/kanban-definition-3W4ZIXB7-D2giu6aZ.js +89 -0
  463. package/web/dist/assets/katex-XbL3y5x-.js +261 -0
  464. package/web/dist/assets/layout-Bm-XCM-8.js +1 -0
  465. package/web/dist/assets/linear-FbekP9OZ.js +1 -0
  466. package/web/dist/assets/min-BrRCpYmF.js +1 -0
  467. package/web/dist/assets/mindmap-definition-VGOIOE7T-o-4ubbY9.js +68 -0
  468. package/web/dist/assets/noto-emoji-0-400-normal-BTQbhB77.woff +0 -0
  469. package/web/dist/assets/noto-emoji-0-400-normal-DHdy6Uhy.woff2 +0 -0
  470. package/web/dist/assets/noto-emoji-1-400-normal-0IvkdXBB.woff +0 -0
  471. package/web/dist/assets/noto-emoji-1-400-normal-BY9OovbM.woff2 +0 -0
  472. package/web/dist/assets/noto-emoji-10-400-normal-D9w4QCof.woff2 +0 -0
  473. package/web/dist/assets/noto-emoji-10-400-normal-DtCumcZR.woff +0 -0
  474. package/web/dist/assets/noto-emoji-11-400-normal-BboTlyvx.woff +0 -0
  475. package/web/dist/assets/noto-emoji-12-400-normal-BB5pgBKj.woff2 +0 -0
  476. package/web/dist/assets/noto-emoji-12-400-normal-g186qhiA.woff +0 -0
  477. package/web/dist/assets/noto-emoji-2-400-normal-BKCR1azW.woff2 +0 -0
  478. package/web/dist/assets/noto-emoji-2-400-normal-BYH0KhDr.woff +0 -0
  479. package/web/dist/assets/noto-emoji-3-400-normal-CnPTUeEK.woff +0 -0
  480. package/web/dist/assets/noto-emoji-3-400-normal-TrTb2VQM.woff2 +0 -0
  481. package/web/dist/assets/noto-emoji-4-400-normal-BxD0KVdj.woff +0 -0
  482. package/web/dist/assets/noto-emoji-4-400-normal-s_n9EyG1.woff2 +0 -0
  483. package/web/dist/assets/noto-emoji-5-400-normal-C190AIxR.woff +0 -0
  484. package/web/dist/assets/noto-emoji-5-400-normal-Ctfx4xc6.woff2 +0 -0
  485. package/web/dist/assets/noto-emoji-6-400-normal-DlXlXWt7.woff +0 -0
  486. package/web/dist/assets/noto-emoji-6-400-normal-NzsjD754.woff2 +0 -0
  487. package/web/dist/assets/noto-emoji-7-400-normal-BHP8KeA6.woff2 +0 -0
  488. package/web/dist/assets/noto-emoji-7-400-normal-CtuKhtAZ.woff +0 -0
  489. package/web/dist/assets/noto-emoji-8-400-normal-DR49ZFe7.woff +0 -0
  490. package/web/dist/assets/noto-emoji-8-400-normal-Dvmkf6b2.woff2 +0 -0
  491. package/web/dist/assets/noto-emoji-9-400-normal-BeHJQ2iK.woff2 +0 -0
  492. package/web/dist/assets/noto-emoji-9-400-normal-BlXmCgeQ.woff +0 -0
  493. package/web/dist/assets/ordinal-Cboi1Yqb.js +1 -0
  494. package/web/dist/assets/pieDiagram-ADFJNKIX-C45wSpld.js +30 -0
  495. package/web/dist/assets/quadrantDiagram-AYHSOK5B-tv-_fe-W.js +7 -0
  496. package/web/dist/assets/reduce-CoLNNlNb.js +1 -0
  497. package/web/dist/assets/requirementDiagram-UZGBJVZJ-Dn6PDfkL.js +64 -0
  498. package/web/dist/assets/sankeyDiagram-TZEHDZUN-CbXTZAsG.js +10 -0
  499. package/web/dist/assets/sequenceDiagram-WL72ISMW-B-o1CUJ5.js +145 -0
  500. package/web/dist/assets/stateDiagram-FKZM4ZOC-hzrupXQi.js +1 -0
  501. package/web/dist/assets/stateDiagram-v2-4FDKWEC3-DN-c2M96.js +1 -0
  502. package/web/dist/assets/timeline-definition-IT6M3QCI-R5SP9GDo.js +61 -0
  503. package/web/dist/assets/treemap-KMMF4GRG-Pg9KlUOt.js +128 -0
  504. package/web/dist/assets/xychartDiagram-PRI3JC2R-C3vbJhd1.js +7 -0
  505. package/web/dist/favicon.svg +13 -0
  506. package/web/dist/index.html +17 -0
  507. package//351/205/215/347/275/256/346/226/271/345/274/217.md +330 -0
@@ -0,0 +1,1357 @@
1
+ // src/services/DispatchService.ts
2
+ import { ZeroError } from "../types/errors.js";
3
+ import { now } from "../utils/time.js";
4
+ import { generateNodeId, generateNodeDirName } from "../utils/id.js";
5
+ import { eventService } from "./EventService.js";
6
+ import { isGitRepo, ensureGitExclude, getCurrentBranch, hasUncommittedChanges, createBackupBranch, createProcessBranch, checkoutBranch, getCurrentCommit, commitDispatch, deleteAllWorkspaceBranches, deleteProcessBranch, deleteBackupBranch, getProcessBranchName, isOnProcessBranch, getCommitsBetween, squashMergeProcessBranch, rebaseMergeProcessBranch, cherryPickToWorkingTree, } from "../utils/git.js";
7
+ /**
8
+ * 派发服务
9
+ * 处理派发功能的核心业务逻辑
10
+ */
11
+ export class DispatchService {
12
+ json;
13
+ md;
14
+ fs;
15
+ configService;
16
+ nodeService;
17
+ constructor(json, md, fs, configService) {
18
+ this.json = json;
19
+ this.md = md;
20
+ this.fs = fs;
21
+ this.configService = configService;
22
+ }
23
+ /**
24
+ * 设置 NodeService 依赖(用于自动创建 Review 节点)
25
+ */
26
+ setNodeService(nodeService) {
27
+ this.nodeService = nodeService;
28
+ }
29
+ /**
30
+ * 启用派发模式
31
+ */
32
+ async enableDispatch(workspaceId, projectRoot, options) {
33
+ // 获取工作区目录名
34
+ const location = await this.json.getWorkspaceLocation(workspaceId);
35
+ const dirName = location?.dirName || workspaceId;
36
+ // 0. 检查是否已启用(11.1 模式不可变)
37
+ const existingConfig = await this.json.readWorkspaceConfig(projectRoot, dirName);
38
+ if (existingConfig.dispatch?.enabled) {
39
+ throw new ZeroError("DISPATCH_ALREADY_ENABLED", "派发模式已启用。如需切换模式,请先执行 dispatch_disable,再重新 enable");
40
+ }
41
+ // 1. 检测是否 git 仓库
42
+ const isGit = await isGitRepo(projectRoot);
43
+ // 1.1 确保工作区目录被 git 排除(无论是否使用 Git 模式)
44
+ if (isGit) {
45
+ await ensureGitExclude(projectRoot);
46
+ }
47
+ // 2. 确定 useGit 值
48
+ let useGit;
49
+ if (options?.useGit !== undefined) {
50
+ // 用户显式指定
51
+ if (options.useGit && !isGit) {
52
+ throw new ZeroError("GIT_NOT_FOUND", "当前项目不是 git 仓库,无法启用 Git 模式");
53
+ }
54
+ useGit = options.useGit;
55
+ }
56
+ else {
57
+ // 从全局配置读取默认派发模式
58
+ const defaultMode = await this.configService?.getDefaultDispatchMode() ?? "none";
59
+ if (defaultMode === "git") {
60
+ if (!isGit) {
61
+ throw new ZeroError("GIT_NOT_FOUND", "全局配置为 Git 模式,但当前项目不是 git 仓库。请修改全局配置或显式指定 useGit=false");
62
+ }
63
+ useGit = true;
64
+ }
65
+ else {
66
+ // none 或 no-git 都使用无 Git 模式
67
+ useGit = false;
68
+ }
69
+ }
70
+ // 3. 检查派发并发冲突
71
+ // - 无 Git 模式:不涉及分支切换,多个工作区互不影响,不限制
72
+ // - 有 Git 模式:会切换分支,同一 git 仓库内只允许一个派发
73
+ if (useGit) {
74
+ const activeWorkspaceInfo = await this.getActiveDispatchWorkspaceWithMode(projectRoot, workspaceId);
75
+ // 只有当已有工作区也是 Git 模式时才冲突
76
+ if (activeWorkspaceInfo?.useGit) {
77
+ throw new ZeroError("DISPATCH_CONFLICT", `已有工作区 ${activeWorkspaceInfo.workspaceId} 正在使用 Git 模式派发,请先完成或取消该派发`);
78
+ }
79
+ }
80
+ // 无 Git 模式不检查冲突,直接允许
81
+ // 4. Git 模式:创建分支
82
+ let originalBranch;
83
+ let processBranch;
84
+ let backupBranches;
85
+ if (useGit) {
86
+ // 4a. 记录原分支
87
+ originalBranch = await getCurrentBranch(projectRoot);
88
+ const backupBranchesList = [];
89
+ // 4b. 检查是否有未提交内容
90
+ if (await hasUncommittedChanges(projectRoot)) {
91
+ // 创建备份分支(包含未提交修改)
92
+ const backupBranch = await createBackupBranch(workspaceId, projectRoot);
93
+ backupBranchesList.push(backupBranch);
94
+ // 注意:不切回原分支,直接从 backup(包含修改)创建 process 分支
95
+ // 这样 process 分支会包含所有未提交的修改
96
+ }
97
+ // 4c. 从当前 HEAD 创建派发分支(如果有备份,则基于备份;否则基于原分支)
98
+ processBranch = await createProcessBranch(workspaceId, projectRoot);
99
+ backupBranches = backupBranchesList.length > 0 ? backupBranchesList : undefined;
100
+ }
101
+ // 5. 构建派发配置
102
+ const dispatchConfig = {
103
+ enabled: true,
104
+ useGit,
105
+ enabledAt: Date.now(),
106
+ originalBranch,
107
+ processBranch,
108
+ backupBranches,
109
+ limits: {
110
+ timeoutMs: 300000, // 5 分钟
111
+ maxRetries: 3,
112
+ },
113
+ };
114
+ // 6. 更新 workspace.json
115
+ const config = await this.json.readWorkspaceConfig(projectRoot, dirName);
116
+ config.dispatch = dispatchConfig;
117
+ config.updatedAt = now();
118
+ await this.json.writeWorkspaceConfig(projectRoot, dirName, config);
119
+ // 7. 记录日志
120
+ const mode = useGit ? "Git 模式" : "无 Git 模式";
121
+ const detail = useGit ? `,派发分支: ${processBranch}` : "";
122
+ await this.md.appendLog(projectRoot, dirName, {
123
+ time: now(),
124
+ operator: "system",
125
+ event: `派发模式已启用(${mode})${detail}`,
126
+ });
127
+ // 8. 发送事件通知
128
+ eventService.emitDispatchUpdate(workspaceId, "");
129
+ return { success: true, config: dispatchConfig };
130
+ }
131
+ /**
132
+ * 查询禁用派发状态(返回 actionRequired 让用户选择)
133
+ */
134
+ async queryDisableDispatch(workspaceId, projectRoot) {
135
+ // 获取工作区目录名
136
+ const location = await this.json.getWorkspaceLocation(workspaceId);
137
+ const dirName = location?.dirName || workspaceId;
138
+ // 1. 读取配置
139
+ const config = await this.json.readWorkspaceConfig(projectRoot, dirName);
140
+ if (!config.dispatch?.enabled) {
141
+ return { success: true }; // 已经禁用
142
+ }
143
+ // 1.1 检查是否有正在执行的派发任务
144
+ // 只检查 executing 状态,passed/failed 表示已完成(保留 dispatch 对象供 WebUI 显示历史)
145
+ const graph = await this.json.readGraph(projectRoot, dirName);
146
+ const activeDispatchNodes = [];
147
+ for (const [nodeId, node] of Object.entries(graph.nodes)) {
148
+ if (node.dispatch && node.dispatch.status === "executing") {
149
+ activeDispatchNodes.push(nodeId);
150
+ }
151
+ }
152
+ if (activeDispatchNodes.length > 0) {
153
+ throw new ZeroError("DISPATCH_IN_PROGRESS", `无法关闭派发:当前有 ${activeDispatchNodes.length} 个节点正在派发执行中 (${activeDispatchNodes.join(", ")})`);
154
+ }
155
+ // 1.2 检查是否有可完成的 planning 节点(所有子节点都已完成)
156
+ const completablePlanningNodes = [];
157
+ const terminalStatuses = new Set(["completed", "failed", "cancelled"]);
158
+ for (const [nodeId, node] of Object.entries(graph.nodes)) {
159
+ if (node.type === "planning" && node.status === "monitoring") {
160
+ // 检查所有子节点是否都已完成
161
+ const allChildrenCompleted = node.children.every(childId => {
162
+ const child = graph.nodes[childId];
163
+ return child && terminalStatuses.has(child.status);
164
+ });
165
+ if (allChildrenCompleted && node.children.length > 0) {
166
+ completablePlanningNodes.push(nodeId);
167
+ }
168
+ }
169
+ }
170
+ // 1.3 验证 Git 环境(11.2 环境变化检测)
171
+ // 注意:这里不抛错,而是在返回信息中提示用户
172
+ let gitEnvironmentLost = false;
173
+ if (config.dispatch.useGit) {
174
+ const isGit = await isGitRepo(projectRoot);
175
+ if (!isGit) {
176
+ gitEnvironmentLost = true;
177
+ }
178
+ }
179
+ const useGit = config.dispatch.useGit;
180
+ if (useGit) {
181
+ // Git 模式:返回完整的合并选项
182
+ if (gitEnvironmentLost) {
183
+ // Git 环境丢失,返回简化的 actionRequired
184
+ const actionRequired = {
185
+ type: "dispatch_complete_choice",
186
+ message: "⚠️ Git 环境已丢失(.git 目录不存在),只能清理配置",
187
+ data: {
188
+ workspaceId,
189
+ useGit: true,
190
+ gitEnvironmentLost: true,
191
+ },
192
+ };
193
+ return {
194
+ actionRequired,
195
+ status: {
196
+ backupBranch: null,
197
+ hasBackupChanges: false,
198
+ useGit: true
199
+ }
200
+ };
201
+ }
202
+ const { originalBranch, processBranch, backupBranches } = config.dispatch;
203
+ const startCommit = config.dispatch.backupBranches?.[0]
204
+ ? await getCurrentCommit(projectRoot) // 会在后面获取正确的 startCommit
205
+ : (await this.getOriginalBranchCommit(originalBranch, projectRoot));
206
+ // 2. 获取派发分支上的提交列表
207
+ const processCommits = await getCommitsBetween(startCommit, processBranch, projectRoot);
208
+ // 3. 获取备份分支信息
209
+ const backupBranch = backupBranches?.[0] || null;
210
+ const hasBackupChanges = backupBranch !== null;
211
+ // 4. 构建状态摘要
212
+ const status = {
213
+ originalBranch: originalBranch,
214
+ processBranch: processBranch,
215
+ backupBranch,
216
+ hasBackupChanges,
217
+ processCommits,
218
+ startMarker: startCommit,
219
+ useGit: true,
220
+ };
221
+ // 5. 构建 actionRequired
222
+ const actionRequired = {
223
+ type: "dispatch_complete_choice",
224
+ message: "派发任务完成,请选择合并策略和分支保留选项",
225
+ data: {
226
+ workspaceId,
227
+ originalBranch: originalBranch,
228
+ backupBranch,
229
+ hasBackupChanges,
230
+ processCommits: processCommits.map(c => `${c.hash.substring(0, 7)} ${c.message}`),
231
+ mergeOptions: [
232
+ { value: "sequential", label: "按顺序合并", description: "保留每个任务的独立提交,线性历史" },
233
+ { value: "squash", label: "squash 合并", description: "压缩为一个提交,最干净" },
234
+ { value: "cherry-pick", label: "遴选到本地", description: "应用修改到工作区但不提交,可手动调整" },
235
+ { value: "skip", label: "暂不合并", description: "保留分支,稍后手动处理" },
236
+ ],
237
+ branchOptions: {
238
+ keepBackupBranch: { default: false, description: "保留备份分支(可用于查看历史)" },
239
+ keepProcessBranch: { default: false, description: "保留派发分支(可用于对比)" },
240
+ },
241
+ },
242
+ };
243
+ // 添加可完成 planning 节点的提醒
244
+ if (completablePlanningNodes.length > 0) {
245
+ actionRequired.message += `\n\n💡 **提醒**:以下 planning 节点的所有子任务已完成,请填写结论并完成:${completablePlanningNodes.join(", ")}`;
246
+ }
247
+ return { actionRequired, status };
248
+ }
249
+ else {
250
+ // 无 Git 模式:返回简化的 actionRequired(仅需确认)
251
+ const status = {
252
+ backupBranch: null,
253
+ hasBackupChanges: false,
254
+ useGit: false,
255
+ };
256
+ let message = "派发任务完成(无 Git 模式),确认关闭?";
257
+ // 添加可完成 planning 节点的提醒
258
+ if (completablePlanningNodes.length > 0) {
259
+ message += `\n\n💡 **提醒**:以下 planning 节点的所有子任务已完成,请填写结论并完成:${completablePlanningNodes.join(", ")}`;
260
+ }
261
+ const actionRequired = {
262
+ type: "dispatch_complete_choice",
263
+ message,
264
+ data: {
265
+ workspaceId,
266
+ useGit: false,
267
+ },
268
+ };
269
+ return { actionRequired, status };
270
+ }
271
+ }
272
+ /**
273
+ * 执行用户选择的禁用操作
274
+ */
275
+ async executeDisableChoice(projectRoot, params) {
276
+ const { workspaceId, mergeStrategy, keepBackupBranch, keepProcessBranch, commitMessage } = params;
277
+ // 获取工作区目录名
278
+ const location = await this.json.getWorkspaceLocation(workspaceId);
279
+ const dirName = location?.dirName || workspaceId;
280
+ // 1. 读取配置
281
+ const config = await this.json.readWorkspaceConfig(projectRoot, dirName);
282
+ if (!config.dispatch?.enabled) {
283
+ return { success: true, message: "派发模式已禁用" };
284
+ }
285
+ const useGit = config.dispatch.useGit;
286
+ let resultMessage = "";
287
+ if (useGit) {
288
+ // Git 模式:检测 Git 环境
289
+ const isGit = await isGitRepo(projectRoot);
290
+ if (!isGit) {
291
+ // Git 环境已丢失,只能清理配置
292
+ resultMessage = "⚠️ Git 环境已丢失,已清理派发配置(无法执行 git 操作)";
293
+ }
294
+ else {
295
+ // Git 环境正常,执行合并策略
296
+ const { originalBranch, processBranch, backupBranches } = config.dispatch;
297
+ const startCommit = await this.getOriginalBranchCommit(originalBranch, projectRoot);
298
+ // 2. 根据策略执行合并
299
+ switch (mergeStrategy) {
300
+ case "sequential":
301
+ await rebaseMergeProcessBranch(workspaceId, originalBranch, projectRoot);
302
+ resultMessage = "已按顺序合并所有提交";
303
+ break;
304
+ case "squash":
305
+ const msg = commitMessage || `zero: 完成工作区 ${workspaceId} 派发任务`;
306
+ await squashMergeProcessBranch(workspaceId, originalBranch, msg, projectRoot);
307
+ resultMessage = "已 squash 合并为一个提交";
308
+ break;
309
+ case "cherry-pick":
310
+ await cherryPickToWorkingTree(workspaceId, originalBranch, startCommit, projectRoot);
311
+ resultMessage = "已将修改应用到工作区(未提交),请手动调整后提交";
312
+ break;
313
+ case "skip":
314
+ await checkoutBranch(originalBranch, projectRoot);
315
+ resultMessage = "已切回原分支,派发分支保留";
316
+ break;
317
+ }
318
+ // 3. 清理分支(根据用户选择)
319
+ if (!keepProcessBranch && mergeStrategy !== "skip") {
320
+ await deleteProcessBranch(workspaceId, projectRoot);
321
+ }
322
+ if (!keepBackupBranch && backupBranches?.length) {
323
+ await deleteBackupBranch(workspaceId, undefined, projectRoot);
324
+ }
325
+ }
326
+ }
327
+ else {
328
+ // 无 Git 模式:仅清理配置,跳过 git 操作
329
+ resultMessage = "派发模式已关闭(无 Git 模式)";
330
+ }
331
+ // 4. 更新配置
332
+ config.dispatch = undefined;
333
+ config.updatedAt = now();
334
+ await this.json.writeWorkspaceConfig(projectRoot, dirName, config);
335
+ // 5. 记录日志
336
+ await this.md.appendLog(projectRoot, dirName, {
337
+ time: now(),
338
+ operator: "system",
339
+ event: `派发模式已禁用: ${resultMessage}`,
340
+ });
341
+ // 6. 发送事件通知
342
+ eventService.emitDispatchUpdate(workspaceId, "");
343
+ return { success: true, message: resultMessage };
344
+ }
345
+ /**
346
+ * 获取原分支的起始 commit(用于确定派发提交范围)
347
+ */
348
+ async getOriginalBranchCommit(originalBranch, projectRoot) {
349
+ // 获取原分支的 HEAD commit
350
+ const { stdout } = await import("child_process").then(cp => import("util").then(util => util.promisify(cp.exec)(`git rev-parse "${originalBranch}"`, { cwd: projectRoot })));
351
+ return stdout.trim();
352
+ }
353
+ /**
354
+ * 禁用派发模式(兼容旧接口,直接合并)
355
+ * @deprecated 使用 queryDisableDispatch + executeDisableChoice 代替
356
+ */
357
+ async disableDispatch(workspaceId, projectRoot, merge = false) {
358
+ if (merge) {
359
+ return this.executeDisableChoice(projectRoot, {
360
+ workspaceId,
361
+ mergeStrategy: "squash",
362
+ keepBackupBranch: false,
363
+ keepProcessBranch: false,
364
+ });
365
+ }
366
+ else {
367
+ return this.executeDisableChoice(projectRoot, {
368
+ workspaceId,
369
+ mergeStrategy: "skip",
370
+ keepBackupBranch: false,
371
+ keepProcessBranch: false,
372
+ });
373
+ }
374
+ }
375
+ /**
376
+ * 检查节点是否准备好派发
377
+ * @param nodeInfo 节点信息
378
+ * @param nodeMeta 节点元数据
379
+ * @returns 完整性检查结果
380
+ */
381
+ checkNodeReadiness(nodeInfo, nodeMeta) {
382
+ const errors = [];
383
+ const warnings = [];
384
+ // 必须检查:需求描述
385
+ if (!nodeInfo.requirement || nodeInfo.requirement.trim() === "") {
386
+ errors.push("节点缺少需求描述(requirement),无法派发");
387
+ }
388
+ // 建议检查:验收标准
389
+ const criteria = nodeInfo.acceptanceCriteria || nodeMeta.acceptanceCriteria;
390
+ if (!criteria || criteria.length === 0) {
391
+ warnings.push("节点缺少验收标准(acceptanceCriteria),可能影响执行质量");
392
+ }
393
+ // 建议检查:重试场景下的失败历史
394
+ if (nodeMeta.dispatch?.attempts && nodeMeta.dispatch.attempts.length > 0) {
395
+ const lastAttempt = nodeMeta.dispatch.attempts[nodeMeta.dispatch.attempts.length - 1];
396
+ if (lastAttempt.status === "failed" && !lastAttempt.failureReason) {
397
+ warnings.push("上次执行失败但未记录失败原因,可能影响重试效果");
398
+ }
399
+ }
400
+ return {
401
+ ready: errors.length === 0,
402
+ errors,
403
+ warnings,
404
+ };
405
+ }
406
+ /**
407
+ * 准备派发节点任务
408
+ * @deprecated 使用 upgradeToDispatchParent 代替
409
+ */
410
+ async prepareDispatch(workspaceId, projectRoot, nodeId) {
411
+ // 向后兼容:调用新方法
412
+ const result = await this.upgradeToDispatchParent(workspaceId, projectRoot, nodeId);
413
+ // 转换返回格式以保持向后兼容
414
+ if (!result.upgraded) {
415
+ throw new ZeroError("DISPATCH_SKIP", result.skipReason || "节点无需派发");
416
+ }
417
+ return {
418
+ success: true,
419
+ startMarker: "", // 新逻辑不再记录 startMarker
420
+ actionRequired: result.actionRequired,
421
+ };
422
+ }
423
+ /**
424
+ * 升级执行节点为派发母节点
425
+ * 将 execution 节点升级为 planning 节点,并设置为派发母节点
426
+ */
427
+ async upgradeToDispatchParent(workspaceId, projectRoot, nodeId) {
428
+ // 获取工作区目录名
429
+ const location = await this.json.getWorkspaceLocation(workspaceId);
430
+ const wsDirName = location?.dirName || workspaceId;
431
+ // 1. 验证派发模式已启用
432
+ const config = await this.json.readWorkspaceConfig(projectRoot, wsDirName);
433
+ if (!config.dispatch?.enabled) {
434
+ throw new ZeroError("DISPATCH_NOT_ENABLED", "派发模式未启用");
435
+ }
436
+ // 1.1 验证 Git 环境(11.2 环境变化检测)
437
+ await this.validateGitEnvironment(workspaceId, projectRoot, config);
438
+ // 2. 验证节点类型
439
+ const graph = await this.json.readGraph(projectRoot, wsDirName);
440
+ const node = graph.nodes[nodeId];
441
+ if (!node) {
442
+ throw new ZeroError("NODE_NOT_FOUND", `节点 ${nodeId} 不存在`);
443
+ }
444
+ if (node.type !== "execution") {
445
+ throw new ZeroError("INVALID_NODE_TYPE", "只有执行节点可以升级为派发母节点");
446
+ }
447
+ // 2.1 验证节点不是派发子节点
448
+ const dispatchChildRoles = ["dispatch_exec", "dispatch_spec", "dispatch_quality"];
449
+ if (node.role && dispatchChildRoles.includes(node.role)) {
450
+ throw new ZeroError("INVALID_NODE_ROLE", `派发子节点(${node.role})不能再升级为派发母节点`);
451
+ }
452
+ // 3. 检查上级节点角色
453
+ if (node.parentId && node.parentId !== "root") {
454
+ const parent = graph.nodes[node.parentId];
455
+ if (parent?.role === "info_collection" || parent?.role === "info_summary") {
456
+ return {
457
+ success: true,
458
+ upgraded: false,
459
+ skipReason: "上级是信息节点,可直接执行,无需派发",
460
+ };
461
+ }
462
+ }
463
+ // 4. 升级节点类型为 planning
464
+ node.type = "planning";
465
+ // 5. 初始化派发母节点标识(children 由 dispatch_create 填充)
466
+ // 注意:不设置 node.dispatch,母节点使用 dispatchParent 字段
467
+ // 6. 状态改为 monitoring
468
+ node.status = "monitoring";
469
+ node.updatedAt = now();
470
+ // 保存更新
471
+ await this.json.writeGraph(projectRoot, wsDirName, graph);
472
+ // 7. 记录日志
473
+ await this.md.appendLog(projectRoot, wsDirName, {
474
+ time: now(),
475
+ operator: "system",
476
+ event: `节点 ${nodeId} 已升级为派发母节点`,
477
+ }, nodeId);
478
+ // 8. 发送事件通知
479
+ eventService.emitDispatchUpdate(workspaceId, nodeId);
480
+ // 9. 返回 actionRequired
481
+ const actionRequired = {
482
+ type: "invoke_skill",
483
+ message: `## ⚠️ MUST: 调用 Skill(dispatching-parent)
484
+
485
+ **你 MUST 立即调用 Skill(dispatching-parent)**,否则派发流程会失败。
486
+
487
+ \`\`\`
488
+ Skill(skill: "dispatching-parent")
489
+ \`\`\`
490
+
491
+ **如果 Skill 不可用**,使用 plugin_path 获取路径后 Read:
492
+ \`\`\`
493
+ plugin_path(type: "skill", name: "dispatching-parent") → 获取路径
494
+ Read(file_path: <返回的路径>/SKILL.md)
495
+ \`\`\`
496
+
497
+ ### 为什么必须调用?
498
+
499
+ 1. Skill 包含完整的派发流程(exec → spec → quality → 完成)
500
+ 2. 跳过 Skill 会导致:
501
+ - 忘记派发 spec 审查节点
502
+ - 聚焦点不正确
503
+ - 日志不完整
504
+ - 节点状态不一致
505
+
506
+ ### NEVER 直接调用 dispatch_create
507
+
508
+ **错误做法**:直接调用 dispatch_create
509
+ **正确做法**:先调用 Skill(dispatching-parent) 获取完整流程指导
510
+
511
+ 你现在是**协调者**,NEVER 直接执行任务。`,
512
+ data: {
513
+ skill: "dispatching-parent",
514
+ workspaceId,
515
+ nodeId,
516
+ },
517
+ };
518
+ return {
519
+ success: true,
520
+ upgraded: true,
521
+ actionRequired,
522
+ };
523
+ }
524
+ /**
525
+ * 处理派发完成
526
+ * 简化版:不再自动创建 Review 节点,由 dispatch_create 统一创建子节点
527
+ */
528
+ async completeDispatch(workspaceId, projectRoot, nodeId, success, conclusion) {
529
+ // 获取工作区目录名
530
+ const location = await this.json.getWorkspaceLocation(workspaceId);
531
+ const wsDirName = location?.dirName || workspaceId;
532
+ // 1. 读取配置和节点
533
+ const config = await this.json.readWorkspaceConfig(projectRoot, wsDirName);
534
+ // 1.1 验证 Git 环境(11.2 环境变化检测)
535
+ await this.validateGitEnvironment(workspaceId, projectRoot, config);
536
+ const graph = await this.json.readGraph(projectRoot, wsDirName);
537
+ const node = graph.nodes[nodeId];
538
+ if (!node) {
539
+ throw new ZeroError("NODE_NOT_FOUND", `节点 ${nodeId} 不存在`);
540
+ }
541
+ // 验证节点有派发信息
542
+ if (!node.dispatch) {
543
+ throw new ZeroError("INVALID_NODE_STATUS", "节点没有派发信息,无法完成派发");
544
+ }
545
+ // 验证节点已经开始执行(必须先调用 node_transition(action="start"))
546
+ if (node.dispatch.status !== "executing") {
547
+ throw new ZeroError("INVALID_DISPATCH_STATUS", `节点派发状态为 ${node.dispatch.status},必须先调用 node_transition(action="start") 开始执行后才能完成。`);
548
+ }
549
+ // 验证节点状态正确
550
+ if (node.status !== "implementing") {
551
+ throw new ZeroError("INVALID_NODE_STATUS", `节点状态为 ${node.status},预期为 implementing。请确保已正确调用 node_transition(action="start")。`);
552
+ }
553
+ const useGit = config.dispatch?.useGit ?? false;
554
+ // 获取节点目录名(用于读取和更新 Markdown 文件)
555
+ const nodeDirName = node.dirName || nodeId;
556
+ const nodeInfo = await this.md.readNodeInfo(projectRoot, wsDirName, nodeDirName);
557
+ // 2. 记录 endMarker
558
+ let endMarker;
559
+ if (useGit && success) {
560
+ // Git 模式且成功:提交更改,记录 commit hash
561
+ endMarker = await commitDispatch(nodeId, nodeInfo.title, projectRoot);
562
+ }
563
+ else {
564
+ // 无 Git 模式或失败:记录时间戳(失败时由母节点决定是否 reset)
565
+ endMarker = Date.now().toString();
566
+ }
567
+ // 3. 更新节点状态
568
+ node.dispatch.endMarker = endMarker;
569
+ node.dispatch.status = success ? "passed" : "failed";
570
+ node.status = success ? "completed" : "failed";
571
+ if (conclusion) {
572
+ node.conclusion = conclusion.replace(/\\n/g, "\n");
573
+ }
574
+ node.updatedAt = now();
575
+ // 4. 保存更改
576
+ await this.json.writeGraph(projectRoot, wsDirName, graph);
577
+ await this.md.updateNodeStatus(projectRoot, wsDirName, nodeDirName, success ? "completed" : "failed");
578
+ if (conclusion) {
579
+ await this.md.updateConclusion(projectRoot, wsDirName, nodeDirName, conclusion);
580
+ }
581
+ // 5. 记录日志
582
+ const markerInfo = useGit && success ? `commit: ${endMarker.substring(0, 7)}` : `timestamp: ${endMarker}`;
583
+ const statusText = success ? "完成" : "失败";
584
+ await this.md.appendLog(projectRoot, wsDirName, {
585
+ time: now(),
586
+ operator: "zero-executor",
587
+ event: `节点 ${nodeId} 派发执行${statusText},${markerInfo}${conclusion ? `: ${conclusion.substring(0, 50)}...` : ""}`,
588
+ }, nodeId);
589
+ // 6. 发送事件通知
590
+ eventService.emitDispatchUpdate(workspaceId, nodeId);
591
+ // 7. 返回简单结果
592
+ return {
593
+ success,
594
+ endMarker,
595
+ hint: success ? "执行完成" : "执行失败",
596
+ };
597
+ }
598
+ /**
599
+ * 处理测试结果
600
+ * 注:在新的附属化设计中,测试节点和执行节点是兄弟关系,由父管理节点统一管理。
601
+ * 此方法简化为记录测试结果,不再操作关联的执行节点。
602
+ */
603
+ async handleTestResult(workspaceId, projectRoot, testNodeId, passed, _conclusion) {
604
+ // 获取工作区目录名
605
+ const location = await this.json.getWorkspaceLocation(workspaceId);
606
+ const wsDirName = location?.dirName || workspaceId;
607
+ // 1. 读取配置和测试节点
608
+ const config = await this.json.readWorkspaceConfig(projectRoot, wsDirName);
609
+ // 1.1 验证 Git 环境(11.2 环境变化检测)
610
+ await this.validateGitEnvironment(workspaceId, projectRoot, config);
611
+ const graph = await this.json.readGraph(projectRoot, wsDirName);
612
+ const testNode = graph.nodes[testNodeId];
613
+ if (!testNode) {
614
+ throw new ZeroError("INVALID_TEST_NODE", "无效的测试节点");
615
+ }
616
+ if (passed) {
617
+ // 测试通过:记录日志
618
+ await this.md.appendLog(projectRoot, wsDirName, {
619
+ time: now(),
620
+ operator: "zero-reviewer",
621
+ event: `测试节点 ${testNodeId} 验证通过`,
622
+ }, testNodeId);
623
+ return {
624
+ success: true,
625
+ hint: "测试通过,返回父节点继续处理",
626
+ };
627
+ }
628
+ else {
629
+ // 测试失败:记录日志
630
+ await this.md.appendLog(projectRoot, wsDirName, {
631
+ time: now(),
632
+ operator: "zero-reviewer",
633
+ event: `测试节点 ${testNodeId} 验证失败`,
634
+ }, testNodeId);
635
+ return {
636
+ success: false,
637
+ hint: "测试失败,返回父节点决策",
638
+ };
639
+ }
640
+ }
641
+ /**
642
+ * 获取 Git 状态信息
643
+ */
644
+ async getGitStatus(workspaceId, projectRoot) {
645
+ // 获取工作区目录名
646
+ const location = await this.json.getWorkspaceLocation(workspaceId);
647
+ const wsDirName = location?.dirName || workspaceId;
648
+ // 读取配置,检查是否使用 Git 模式
649
+ const config = await this.json.readWorkspaceConfig(projectRoot, wsDirName);
650
+ const useGit = config.dispatch?.useGit ?? false;
651
+ if (!useGit) {
652
+ // 无 Git 模式:返回 null
653
+ return null;
654
+ }
655
+ if (!(await isGitRepo(projectRoot))) {
656
+ return null;
657
+ }
658
+ const currentBranch = await getCurrentBranch(projectRoot);
659
+ const uncommittedChanges = await hasUncommittedChanges(projectRoot);
660
+ const isDispatch = await isOnProcessBranch(workspaceId, projectRoot);
661
+ return {
662
+ currentBranch,
663
+ hasUncommittedChanges: uncommittedChanges,
664
+ isDispatchBranch: isDispatch,
665
+ };
666
+ }
667
+ /**
668
+ * 清理派发分支
669
+ */
670
+ async cleanupBranches(workspaceId, projectRoot) {
671
+ // 获取工作区目录名
672
+ const location = await this.json.getWorkspaceLocation(workspaceId);
673
+ const wsDirName = location?.dirName || workspaceId;
674
+ // 读取配置,检查是否使用 Git 模式
675
+ const config = await this.json.readWorkspaceConfig(projectRoot, wsDirName);
676
+ const useGit = config.dispatch?.useGit ?? false;
677
+ if (!useGit) {
678
+ // 无 Git 模式:直接返回成功
679
+ return { success: true, deleted: [] };
680
+ }
681
+ const deleted = [];
682
+ try {
683
+ await deleteAllWorkspaceBranches(workspaceId, projectRoot);
684
+ deleted.push(getProcessBranchName(workspaceId));
685
+ }
686
+ catch {
687
+ // 忽略清理错误
688
+ }
689
+ return { success: true, deleted };
690
+ }
691
+ /**
692
+ * 检测 Git 环境是否仍然可用(11.2 环境变化检测)
693
+ * 如果配置要求 Git 模式但 .git 目录消失,抛出警告
694
+ */
695
+ async validateGitEnvironment(workspaceId, projectRoot, config) {
696
+ if (config.dispatch?.enabled && config.dispatch.useGit) {
697
+ const isGit = await isGitRepo(projectRoot);
698
+ if (!isGit) {
699
+ throw new ZeroError("GIT_ENVIRONMENT_LOST", `⚠️ 派发使用 Git 模式,但 .git 目录已消失。建议执行 dispatch_disable 清理派发状态。`);
700
+ }
701
+ }
702
+ }
703
+ /**
704
+ * 检测活跃的派发工作区及其模式(用于混合模式检测)
705
+ * 返回工作区 ID 和 useGit 模式
706
+ */
707
+ async getActiveDispatchWorkspaceWithMode(projectRoot, excludeWorkspaceId) {
708
+ try {
709
+ // 通过 index.json 获取所有工作区(支持新的可读目录名格式)
710
+ const index = await this.json.readIndex();
711
+ const workspacesInProject = index.workspaces.filter(ws => ws.projectRoot === projectRoot && ws.status !== "archived");
712
+ // 遍历所有工作区配置
713
+ for (const wsEntry of workspacesInProject) {
714
+ if (wsEntry.id === excludeWorkspaceId) {
715
+ continue;
716
+ }
717
+ try {
718
+ const wsDirName = wsEntry.dirName || wsEntry.id; // 向后兼容
719
+ const config = await this.json.readWorkspaceConfig(projectRoot, wsDirName);
720
+ if (config.dispatch?.enabled) {
721
+ return {
722
+ workspaceId: wsEntry.id,
723
+ useGit: config.dispatch.useGit ?? false,
724
+ };
725
+ }
726
+ }
727
+ catch {
728
+ // 忽略读取失败的工作区
729
+ continue;
730
+ }
731
+ }
732
+ return null;
733
+ }
734
+ catch {
735
+ return null;
736
+ }
737
+ }
738
+ /**
739
+ * 通过配置文件检测活跃的派发工作区(用于无 Git 模式)
740
+ */
741
+ async getActiveDispatchWorkspaceByConfig(projectRoot, excludeWorkspaceId) {
742
+ try {
743
+ // 通过 index.json 获取所有工作区(支持新的可读目录名格式)
744
+ const index = await this.json.readIndex();
745
+ const workspacesInProject = index.workspaces.filter(ws => ws.projectRoot === projectRoot && ws.status !== "archived");
746
+ // 遍历所有工作区配置
747
+ for (const wsEntry of workspacesInProject) {
748
+ if (wsEntry.id === excludeWorkspaceId) {
749
+ continue;
750
+ }
751
+ try {
752
+ const wsDirName = wsEntry.dirName || wsEntry.id; // 向后兼容
753
+ const config = await this.json.readWorkspaceConfig(projectRoot, wsDirName);
754
+ if (config.dispatch?.enabled) {
755
+ return wsEntry.id;
756
+ }
757
+ }
758
+ catch {
759
+ // 忽略读取失败的工作区
760
+ continue;
761
+ }
762
+ }
763
+ return null;
764
+ }
765
+ catch {
766
+ return null;
767
+ }
768
+ }
769
+ /**
770
+ * 构建执行者 prompt(增强版:包含完整任务上下文)
771
+ */
772
+ buildExecutorPrompt(workspaceId, nodeId, nodeInfo, nodeMeta) {
773
+ const sections = [];
774
+ // 基础信息
775
+ sections.push(`# Task Execution Context
776
+
777
+ **Workspace**: ${workspaceId}
778
+ **Node ID**: ${nodeId}
779
+ **Title**: ${nodeInfo.title}`);
780
+ // 需求描述
781
+ sections.push(`## Requirement
782
+
783
+ ${nodeInfo.requirement}`);
784
+ // 验收标准
785
+ const criteria = nodeInfo.acceptanceCriteria || nodeMeta.acceptanceCriteria;
786
+ if (criteria && criteria.length > 0) {
787
+ const criteriaList = criteria
788
+ .map((c, i) => `${i + 1}. **WHEN** ${c.when} **THEN** ${c.then}`)
789
+ .join("\n");
790
+ sections.push(`## Acceptance Criteria
791
+
792
+ ${criteriaList}`);
793
+ }
794
+ // 重试上下文(如果有失败历史)
795
+ if (nodeMeta.dispatch?.attempts && nodeMeta.dispatch.attempts.length > 0) {
796
+ const failedAttempts = nodeMeta.dispatch.attempts.filter(a => a.status === "failed");
797
+ if (failedAttempts.length > 0) {
798
+ const lastFailed = failedAttempts[failedAttempts.length - 1];
799
+ sections.push(`## Previous Failure Context
800
+
801
+ ⚠️ This is retry attempt #${nodeMeta.dispatch.attempts.length + 1}
802
+
803
+ **Last failure reason**: ${lastFailed.failureReason || "Not specified"}
804
+ **Last conclusion**: ${lastFailed.conclusion || "Not available"}
805
+
806
+ Please address the issues from previous attempts.`);
807
+ }
808
+ }
809
+ // 执行指令
810
+ sections.push(`## Execution Instructions
811
+
812
+ ### Step 0: Invoke Skill (REQUIRED FIRST)
813
+ \`\`\`
814
+ Skill(skill: "executing-task")
815
+ \`\`\`
816
+ This provides detailed SOP for task execution. If unavailable, use plugin_path to read SKILL.md.
817
+
818
+ ### Step 1: Start the node
819
+ \`\`\`
820
+ node_transition(workspaceId="${workspaceId}", nodeId="${nodeId}", action="start")
821
+ \`\`\`
822
+
823
+ ### Steps 2-5: Execute
824
+ 1. **Assess** task scope and verify information completeness
825
+ 2. **Execute** the task within defined boundaries (no scope expansion)
826
+ 3. **Log** progress via log_append at key milestones
827
+ 4. **Complete** with dispatch_complete when done
828
+
829
+ ### On Success:
830
+ \`\`\`
831
+ dispatch_complete(workspaceId="${workspaceId}", nodeId="${nodeId}", success=true, conclusion="<summary of what was done>")
832
+ \`\`\`
833
+
834
+ ### On Failure:
835
+ \`\`\`
836
+ dispatch_complete(workspaceId="${workspaceId}", nodeId="${nodeId}", success=false, conclusion="<reason for failure and suggestions>")
837
+ \`\`\`
838
+
839
+ **CRITICAL**:
840
+ - You MUST invoke Skill(executing-task) FIRST for detailed SOP
841
+ - You MUST call node_transition(start) to begin the task
842
+ - You MUST call dispatch_complete to finalize
843
+
844
+ **SCOPE CONTROL**: Execute only what is specified. If task is unclear or too large, FAIL with clear reason.
845
+
846
+ **IMPLEMENTATION CONSTRAINTS** (NEVER violate):
847
+ - MUST implement exactly as specified - NEVER simplify, skip, or defer any part
848
+ - NEVER use TODO/FIXME/HACK comments as placeholder for missing implementation
849
+ - NEVER use temporary workarounds ("暂时"、"简化"、"临时")
850
+ - If requirement is unclear → FAIL with info_insufficient, don't guess or simplify`);
851
+ return sections.join("\n\n");
852
+ }
853
+ /**
854
+ * 构建 Reviewer prompt(用于 spec 和 quality 节点)
855
+ */
856
+ buildReviewerPrompt(workspaceId, nodeId, targetNodeId, role, nodeInfo) {
857
+ const sections = [];
858
+ const isSpec = role === "dispatch_spec";
859
+ const skillName = isSpec ? "reviewing-spec" : "reviewing-quality";
860
+ const taskType = isSpec ? "Spec Review" : "Quality Review";
861
+ // 基础信息
862
+ sections.push(`# ${taskType} Task
863
+
864
+ **Workspace**: ${workspaceId}
865
+ **Node ID**: ${nodeId}
866
+ **Target Node**: ${targetNodeId}
867
+ **Role**: ${role}
868
+ **Title**: ${nodeInfo.title}`);
869
+ // 验收标准(仅 spec review)
870
+ if (isSpec && nodeInfo.acceptanceCriteria && nodeInfo.acceptanceCriteria.length > 0) {
871
+ const criteriaList = nodeInfo.acceptanceCriteria
872
+ .map((c, i) => `${i + 1}. **WHEN** ${c.when} **THEN** ${c.then}`)
873
+ .join("\n");
874
+ sections.push(`## Acceptance Criteria to Verify
875
+
876
+ ${criteriaList}`);
877
+ // Gate Function 强调
878
+ sections.push(`## ⚠️ Gate Function: MUST RUN Verification Commands
879
+
880
+ **Iron Law: NO PASS WITHOUT RUNNING VERIFICATION COMMANDS YOURSELF**
881
+
882
+ For each criterion above, check if it has a **Verify** column (format: \`[cmd]\`, \`[manual]\`, \`[check]\`):
883
+
884
+ - \`[cmd] <command>\` → You MUST run this command yourself (do NOT trust exec's output)
885
+ - \`[manual] <steps>\` → Verify exec logged evidence of performing these steps
886
+ - \`[check] <condition>\` → Inspect code to confirm condition is met
887
+
888
+ **You MUST produce an Evidence Table**:
889
+ | # | Criterion | Verify | Command/Action | Result | Status |
890
+ |---|-----------|--------|----------------|--------|--------|
891
+
892
+ **Red Flags** (if you're thinking these, STOP):
893
+ - "Exec already ran the test" → RUN IT YOURSELF
894
+ - "I can see exec's output in the log" → LOGS CAN LIE, RUN IT YOURSELF
895
+ - "Code looks correct" → LOOKING CORRECT ≠ WORKING, RUN THE COMMAND`);
896
+ }
897
+ // 审查指令
898
+ sections.push(`## Review Instructions
899
+
900
+ ### Step 0: Invoke Skill (REQUIRED FIRST)
901
+ \`\`\`
902
+ Skill(skill: "${skillName}")
903
+ \`\`\`
904
+ This provides detailed SOP for ${taskType.toLowerCase()}. If unavailable, use plugin_path to read SKILL.md.
905
+
906
+ ### Step 1: Start the node
907
+ \`\`\`
908
+ node_transition(workspaceId="${workspaceId}", nodeId="${nodeId}", action="start")
909
+ \`\`\`
910
+
911
+ ### Steps 2-4: Review
912
+ 1. **Verify** ${isSpec ? "each acceptance criterion independently" : "code quality, maintainability, and best practices"}
913
+ 2. **Log** each verification via log_append
914
+ 3. **Complete** with dispatch_complete when done
915
+
916
+ ### On Pass:
917
+ \`\`\`
918
+ dispatch_complete(workspaceId="${workspaceId}", nodeId="${nodeId}", success=true, conclusion="<审查通过:具体验证结果>")
919
+ \`\`\`
920
+
921
+ ### On Fail:
922
+ \`\`\`
923
+ dispatch_complete(workspaceId="${workspaceId}", nodeId="${nodeId}", success=false, conclusion="<审查失败:具体问题列表>")
924
+ \`\`\`
925
+
926
+ **CRITICAL**:
927
+ - You MUST invoke Skill(${skillName}) FIRST for detailed SOP
928
+ - You MUST verify INDEPENDENTLY - do NOT trust executor's conclusion
929
+ - You MUST call dispatch_complete to finalize
930
+ ${isSpec ? "- ANY criterion fails → entire review FAILS" : "- Report specific issues with evidence"}`);
931
+ return sections.join("\n\n");
932
+ }
933
+ /**
934
+ * 创建派发子节点(exec + spec + quality)
935
+ * 在派发母节点下一次性创建所有派发子节点
936
+ */
937
+ async createDispatchChildren(workspaceId, projectRoot, parentId, exec, includeQuality = true) {
938
+ // 获取工作区目录名
939
+ const location = await this.json.getWorkspaceLocation(workspaceId);
940
+ const wsDirName = location?.dirName || workspaceId;
941
+ // 1. 验证派发模式已启用
942
+ const config = await this.json.readWorkspaceConfig(projectRoot, wsDirName);
943
+ if (!config.dispatch?.enabled) {
944
+ throw new ZeroError("DISPATCH_NOT_ENABLED", "派发模式未启用");
945
+ }
946
+ // 1.1 验证 Git 环境
947
+ await this.validateGitEnvironment(workspaceId, projectRoot, config);
948
+ // 2. 验证 parentId 是派发母节点
949
+ const graph = await this.json.readGraph(projectRoot, wsDirName);
950
+ const parent = graph.nodes[parentId];
951
+ if (!parent) {
952
+ throw new ZeroError("NODE_NOT_FOUND", `节点 ${parentId} 不存在`);
953
+ }
954
+ // 2.1 检查是否是派发母节点(通过 dispatch_node 升级后 status 为 monitoring)
955
+ if (parent.status !== "monitoring" || parent.type !== "planning") {
956
+ throw new ZeroError("INVALID_DISPATCH_PARENT", "parentId 必须是派发母节点(需先调用 dispatch_node 升级)");
957
+ }
958
+ // 2.2 检查是否已有子节点
959
+ if (parent.dispatchParent?.children) {
960
+ throw new ZeroError("DISPATCH_CHILDREN_EXIST", "派发子节点已存在,无法重复创建");
961
+ }
962
+ const useGit = config.dispatch.useGit ?? false;
963
+ const currentTime = now();
964
+ // 3. 生成简短的父节点标题(去掉可能的前缀)
965
+ const parentNodeDirName = parent.dirName || parentId;
966
+ const parentInfo = await this.md.readNodeInfo(projectRoot, wsDirName, parentNodeDirName);
967
+ const shortTitle = parentInfo.title.replace(/^\[.*?\]\s*/, "").substring(0, 30);
968
+ // 4. 准备节点数据(内存中)
969
+ const execNodeId = generateNodeId();
970
+ const execNodeDirName = generateNodeDirName(`[Exec] ${shortTitle}`, execNodeId);
971
+ const execNode = {
972
+ id: execNodeId,
973
+ dirName: execNodeDirName,
974
+ type: "execution",
975
+ parentId: parentId,
976
+ children: [],
977
+ status: "pending",
978
+ isolate: false,
979
+ references: [],
980
+ conclusion: null,
981
+ role: "dispatch_exec",
982
+ acceptanceCriteria: exec.acceptanceCriteria,
983
+ createdAt: currentTime,
984
+ updatedAt: currentTime,
985
+ };
986
+ // 5. 自动生成 spec 节点
987
+ const specNodeId = generateNodeId();
988
+ const specNodeDirName = generateNodeDirName(`[Spec] ${shortTitle}`, specNodeId);
989
+ const specAcceptanceCriteria = exec.acceptanceCriteria.map(c => ({
990
+ when: `检查: ${c.when}`,
991
+ then: `验证: ${c.then}`,
992
+ }));
993
+ const specNode = {
994
+ id: specNodeId,
995
+ dirName: specNodeDirName,
996
+ type: "execution",
997
+ parentId: parentId,
998
+ children: [],
999
+ status: "pending",
1000
+ isolate: false,
1001
+ references: [],
1002
+ conclusion: null,
1003
+ role: "dispatch_spec",
1004
+ acceptanceCriteria: specAcceptanceCriteria,
1005
+ createdAt: currentTime,
1006
+ updatedAt: currentTime,
1007
+ };
1008
+ // 6. 可选创建 quality 节点
1009
+ let qualityNode = null;
1010
+ let qualityNodeId;
1011
+ let qualityNodeDirName;
1012
+ if (includeQuality) {
1013
+ qualityNodeId = generateNodeId();
1014
+ qualityNodeDirName = generateNodeDirName(`[Quality] ${shortTitle}`, qualityNodeId);
1015
+ qualityNode = {
1016
+ id: qualityNodeId,
1017
+ dirName: qualityNodeDirName,
1018
+ type: "execution",
1019
+ parentId: parentId,
1020
+ children: [],
1021
+ status: "pending",
1022
+ isolate: false,
1023
+ references: [],
1024
+ conclusion: null,
1025
+ role: "dispatch_quality",
1026
+ createdAt: currentTime,
1027
+ updatedAt: currentTime,
1028
+ };
1029
+ }
1030
+ // 7. 事务性写入
1031
+ const createdDirs = [];
1032
+ try {
1033
+ // 7.1 创建节点目录和文件
1034
+ // Exec 节点
1035
+ const execNodePath = this.fs.getNodePath(projectRoot, wsDirName, execNodeDirName);
1036
+ await this.fs.mkdir(execNodePath);
1037
+ createdDirs.push(execNodePath);
1038
+ const execNodeInfo = {
1039
+ id: execNodeId,
1040
+ type: "execution",
1041
+ title: `[Exec] ${shortTitle}`,
1042
+ status: "pending",
1043
+ createdAt: currentTime,
1044
+ updatedAt: currentTime,
1045
+ requirement: exec.requirement.replace(/\\n/g, "\n"),
1046
+ docs: [],
1047
+ notes: "",
1048
+ conclusion: "",
1049
+ acceptanceCriteria: exec.acceptanceCriteria,
1050
+ };
1051
+ await this.md.writeNodeInfo(projectRoot, wsDirName, execNodeDirName, execNodeInfo);
1052
+ await this.md.createEmptyLog(projectRoot, wsDirName, execNodeDirName);
1053
+ await this.md.createEmptyProblem(projectRoot, wsDirName, execNodeDirName);
1054
+ // Spec 节点
1055
+ const specNodePath = this.fs.getNodePath(projectRoot, wsDirName, specNodeDirName);
1056
+ await this.fs.mkdir(specNodePath);
1057
+ createdDirs.push(specNodePath);
1058
+ const specNodeInfo = {
1059
+ id: specNodeId,
1060
+ type: "execution",
1061
+ title: `[Spec] ${shortTitle}`,
1062
+ status: "pending",
1063
+ createdAt: currentTime,
1064
+ updatedAt: currentTime,
1065
+ requirement: `验证执行节点实现是否符合需求规格。\n\n**被验证节点**: ${execNodeId}`,
1066
+ docs: [],
1067
+ notes: "",
1068
+ conclusion: "",
1069
+ acceptanceCriteria: specAcceptanceCriteria,
1070
+ };
1071
+ await this.md.writeNodeInfo(projectRoot, wsDirName, specNodeDirName, specNodeInfo);
1072
+ await this.md.createEmptyLog(projectRoot, wsDirName, specNodeDirName);
1073
+ await this.md.createEmptyProblem(projectRoot, wsDirName, specNodeDirName);
1074
+ // Quality 节点(可选)
1075
+ if (qualityNode && qualityNodeDirName) {
1076
+ const qualityNodePath = this.fs.getNodePath(projectRoot, wsDirName, qualityNodeDirName);
1077
+ await this.fs.mkdir(qualityNodePath);
1078
+ createdDirs.push(qualityNodePath);
1079
+ const qualityNodeInfo = {
1080
+ id: qualityNodeId,
1081
+ type: "execution",
1082
+ title: `[Quality] ${shortTitle}`,
1083
+ status: "pending",
1084
+ createdAt: currentTime,
1085
+ updatedAt: currentTime,
1086
+ requirement: `检查执行节点的代码质量。\n\n**被检查节点**: ${execNodeId}\n\n**审查要点**:\n1. 代码可读性和可维护性\n2. 错误处理是否完善\n3. 是否遵循项目编码规范\n4. 是否存在潜在的性能问题\n5. 是否存在安全漏洞`,
1087
+ docs: [],
1088
+ notes: "",
1089
+ conclusion: "",
1090
+ };
1091
+ await this.md.writeNodeInfo(projectRoot, wsDirName, qualityNodeDirName, qualityNodeInfo);
1092
+ await this.md.createEmptyLog(projectRoot, wsDirName, qualityNodeDirName);
1093
+ await this.md.createEmptyProblem(projectRoot, wsDirName, qualityNodeDirName);
1094
+ }
1095
+ // 7.2 更新 graph.json(一次性写入所有节点)
1096
+ graph.nodes[execNodeId] = execNode;
1097
+ graph.nodes[specNodeId] = specNode;
1098
+ if (qualityNode) {
1099
+ graph.nodes[qualityNodeId] = qualityNode;
1100
+ }
1101
+ parent.children.push(execNodeId, specNodeId);
1102
+ if (qualityNodeId) {
1103
+ parent.children.push(qualityNodeId);
1104
+ }
1105
+ parent.dispatchParent = {
1106
+ children: {
1107
+ execId: execNodeId,
1108
+ specId: specNodeId,
1109
+ qualityId: qualityNodeId,
1110
+ },
1111
+ };
1112
+ parent.updatedAt = currentTime;
1113
+ // 8. 设置派发信息(所有子节点初始为 pending,executor 调用 start 时变为 executing)
1114
+ execNode.dispatch = {
1115
+ status: "pending",
1116
+ };
1117
+ specNode.dispatch = {
1118
+ status: "pending",
1119
+ };
1120
+ if (qualityNode) {
1121
+ qualityNode.dispatch = {
1122
+ status: "pending",
1123
+ };
1124
+ }
1125
+ // 9. 自动聚焦到 exec 节点(下一个要执行的节点)
1126
+ graph.currentFocus = execNodeId;
1127
+ await this.json.writeGraph(projectRoot, wsDirName, graph);
1128
+ }
1129
+ catch (error) {
1130
+ // 回滚:删除已创建的目录
1131
+ for (const dir of createdDirs) {
1132
+ try {
1133
+ await this.fs.rmdir(dir);
1134
+ }
1135
+ catch {
1136
+ // 忽略删除错误
1137
+ }
1138
+ }
1139
+ throw error;
1140
+ }
1141
+ // 9. 记录日志
1142
+ const nodeList = includeQuality
1143
+ ? `exec=${execNodeId}, spec=${specNodeId}, quality=${qualityNodeId}`
1144
+ : `exec=${execNodeId}, spec=${specNodeId}`;
1145
+ await this.md.appendLog(projectRoot, wsDirName, {
1146
+ time: now(),
1147
+ operator: "system",
1148
+ event: `派发子节点已创建: ${nodeList}`,
1149
+ }, parentId);
1150
+ // 10. 发送事件通知
1151
+ eventService.emitDispatchUpdate(workspaceId, parentId);
1152
+ // 11. 构建所有节点的 prompt
1153
+ const execNodeMeta = graph.nodes[execNodeId];
1154
+ const execPrompt = this.buildExecutorPrompt(workspaceId, execNodeId, {
1155
+ title: `[Exec] ${shortTitle}`,
1156
+ requirement: exec.requirement,
1157
+ acceptanceCriteria: exec.acceptanceCriteria,
1158
+ }, execNodeMeta);
1159
+ const specPrompt = this.buildReviewerPrompt(workspaceId, specNodeId, execNodeId, "dispatch_spec", {
1160
+ title: `[Spec] ${shortTitle}`,
1161
+ acceptanceCriteria: exec.acceptanceCriteria,
1162
+ });
1163
+ const qualityPrompt = qualityNodeId
1164
+ ? this.buildReviewerPrompt(workspaceId, qualityNodeId, execNodeId, "dispatch_quality", { title: `[Quality] ${shortTitle}` })
1165
+ : undefined;
1166
+ // 12. 返回结果
1167
+ return {
1168
+ execId: execNodeId,
1169
+ specId: specNodeId,
1170
+ qualityId: qualityNodeId,
1171
+ actionRequired: {
1172
+ type: "dispatch_task",
1173
+ message: `## ⚠️ MUST: 完成完整派发流程
1174
+
1175
+ ### 你 MUST 按顺序完成以下步骤,NEVER 在中途停止
1176
+
1177
+ ---
1178
+
1179
+ ## 第1步:派发 exec 节点(立即执行)
1180
+
1181
+ \`\`\`
1182
+ Task(
1183
+ subagent_type: "zero-executor",
1184
+ description: "执行派发任务",
1185
+ prompt: <下方 data.execPrompt 中的完整内容>
1186
+ )
1187
+ \`\`\`
1188
+
1189
+ ---
1190
+
1191
+ ## 第2步:exec 完成后,MUST 派发 spec 节点
1192
+
1193
+ **⚠️ NEVER 在 exec 完成后停止!**
1194
+
1195
+ \`\`\`
1196
+ Task(
1197
+ subagent_type: "zero-reviewer",
1198
+ description: "规格审查",
1199
+ prompt: <下方 data.specPrompt 中的完整内容>
1200
+ )
1201
+ \`\`\`
1202
+
1203
+ spec 节点 ID: **${specNodeId}**
1204
+
1205
+ ---
1206
+
1207
+ ## 第3步:spec 通过后,派发 quality 节点(如有)
1208
+
1209
+ ${qualityNodeId ? `\`\`\`
1210
+ Task(
1211
+ subagent_type: "zero-reviewer",
1212
+ description: "质量审查",
1213
+ prompt: <下方 data.qualityPrompt 中的完整内容>
1214
+ )
1215
+ \`\`\`
1216
+
1217
+ quality 节点 ID: **${qualityNodeId}**` : "未创建 quality 节点,跳过此步"}
1218
+
1219
+ ---
1220
+
1221
+ ## 第4步:所有子节点完成后,完成母节点
1222
+
1223
+ \`\`\`
1224
+ node_transition(workspaceId, nodeId="${parentId}", action="complete", conclusion="...")
1225
+ \`\`\`
1226
+
1227
+ ---
1228
+
1229
+ ### Red Flags - 如果你在想这些,立即停止
1230
+
1231
+ - "exec 完成了,任务结束了" → NEVER,MUST 继续派发 spec
1232
+ - "spec 应该会自动运行" → NEVER,你 MUST 手动派发
1233
+ - "我直接完成母节点" → NEVER,MUST 等所有子节点完成`,
1234
+ data: {
1235
+ workspaceId,
1236
+ parentId,
1237
+ execId: execNodeId,
1238
+ specId: specNodeId,
1239
+ qualityId: qualityNodeId,
1240
+ execPrompt,
1241
+ specPrompt,
1242
+ qualityPrompt,
1243
+ timeout: config.dispatch.limits?.timeoutMs || 300000,
1244
+ },
1245
+ },
1246
+ };
1247
+ }
1248
+ // ========== HTTP API 包装方法 ==========
1249
+ /**
1250
+ * 启用派发模式 (HTTP API 包装)
1251
+ */
1252
+ async enable(params) {
1253
+ const projectRoot = await this.getProjectRoot(params.workspaceId);
1254
+ const result = await this.enableDispatch(params.workspaceId, projectRoot, {
1255
+ useGit: params.useGit,
1256
+ });
1257
+ const hint = result.config.useGit
1258
+ ? "⚠️ Git 模式(实验功能)已启用"
1259
+ : "派发模式已启用(无 Git)";
1260
+ return { ...result, hint };
1261
+ }
1262
+ /**
1263
+ * 查询禁用派发选项 (HTTP API 包装)
1264
+ */
1265
+ async queryDisable(params) {
1266
+ const projectRoot = await this.getProjectRoot(params.workspaceId);
1267
+ const result = await this.queryDisableDispatch(params.workspaceId, projectRoot);
1268
+ if ("actionRequired" in result) {
1269
+ // 返回状态信息供前端显示
1270
+ return {
1271
+ success: true,
1272
+ status: result.status,
1273
+ hint: result.actionRequired.message,
1274
+ };
1275
+ }
1276
+ return result;
1277
+ }
1278
+ /**
1279
+ * 执行禁用派发 (HTTP API 包装)
1280
+ */
1281
+ async executeDisable(params) {
1282
+ const projectRoot = await this.getProjectRoot(params.workspaceId);
1283
+ const result = await this.executeDisableChoice(projectRoot, {
1284
+ workspaceId: params.workspaceId,
1285
+ mergeStrategy: params.mergeStrategy,
1286
+ keepBackupBranch: params.keepBackupBranch ?? false,
1287
+ keepProcessBranch: params.keepProcessBranch ?? false,
1288
+ commitMessage: params.commitMessage,
1289
+ });
1290
+ return {
1291
+ success: result.success,
1292
+ hint: result.message,
1293
+ };
1294
+ }
1295
+ /**
1296
+ * 切换派发模式 (HTTP API 包装)
1297
+ * 仅支持在已启用派发模式时切换 useGit 值
1298
+ */
1299
+ async switchMode(params) {
1300
+ // 获取工作区位置信息
1301
+ const location = await this.json.getWorkspaceLocation(params.workspaceId);
1302
+ if (!location) {
1303
+ throw new ZeroError("WORKSPACE_NOT_FOUND", `工作区不存在: ${params.workspaceId}`);
1304
+ }
1305
+ const { projectRoot, dirName: wsDirName } = location;
1306
+ // 1. 读取当前配置
1307
+ const config = await this.json.readWorkspaceConfig(projectRoot, wsDirName);
1308
+ if (!config.dispatch?.enabled) {
1309
+ throw new ZeroError("DISPATCH_NOT_ENABLED", "派发模式未启用,无法切换模式");
1310
+ }
1311
+ // 2. 检查是否有正在执行的派发任务
1312
+ // 只检查 executing 状态,passed/failed 表示已完成
1313
+ const graph = await this.json.readGraph(projectRoot, wsDirName);
1314
+ const activeDispatchNodes = [];
1315
+ for (const [nodeId, node] of Object.entries(graph.nodes)) {
1316
+ if (node.dispatch && node.dispatch.status === "executing") {
1317
+ activeDispatchNodes.push(nodeId);
1318
+ }
1319
+ }
1320
+ if (activeDispatchNodes.length > 0) {
1321
+ throw new ZeroError("DISPATCH_IN_PROGRESS", `无法切换模式:当前有 ${activeDispatchNodes.length} 个节点正在派发执行中 (${activeDispatchNodes.join(", ")})`);
1322
+ }
1323
+ // 3. 如果要切换到 Git 模式,检查 Git 环境
1324
+ if (params.useGit && !(await isGitRepo(projectRoot))) {
1325
+ throw new ZeroError("GIT_NOT_FOUND", "当前项目不是 git 仓库,无法切换到 Git 模式");
1326
+ }
1327
+ // 4. 更新配置
1328
+ const oldMode = config.dispatch.useGit;
1329
+ config.dispatch.useGit = params.useGit;
1330
+ config.updatedAt = now();
1331
+ await this.json.writeWorkspaceConfig(projectRoot, wsDirName, config);
1332
+ // 5. 记录日志
1333
+ const fromMode = oldMode ? "Git 模式" : "无 Git 模式";
1334
+ const toMode = params.useGit ? "Git 模式" : "无 Git 模式";
1335
+ await this.md.appendLog(projectRoot, wsDirName, {
1336
+ time: now(),
1337
+ operator: "system",
1338
+ event: `派发模式已切换: ${fromMode} → ${toMode}`,
1339
+ });
1340
+ return {
1341
+ success: true,
1342
+ hint: `派发模式已从 ${fromMode} 切换到 ${toMode}`,
1343
+ };
1344
+ }
1345
+ /**
1346
+ * 获取工作区的项目根目录
1347
+ */
1348
+ async getProjectRoot(workspaceId) {
1349
+ const index = await this.json.readIndex();
1350
+ const workspace = index.workspaces.find((w) => w.id === workspaceId);
1351
+ if (!workspace) {
1352
+ throw new ZeroError("WORKSPACE_NOT_FOUND", `工作区不存在: ${workspaceId}`);
1353
+ }
1354
+ return workspace.projectRoot;
1355
+ }
1356
+ }
1357
+ //# sourceMappingURL=DispatchService.js.map