xcoder-dna 0.0.0-stage → 1.3.0

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 (390) hide show
  1. package/LICENSE +66 -0
  2. package/README.md +87 -2
  3. package/dist/agents/ai-chat-debate.d.ts +36 -0
  4. package/dist/agents/ai-chat-debate.js +300 -0
  5. package/dist/agents/ai-chat-report-writer.d.ts +7 -0
  6. package/dist/agents/ai-chat-report-writer.js +100 -0
  7. package/dist/agents/duplicate-detector.d.ts +41 -0
  8. package/dist/agents/duplicate-detector.js +65 -0
  9. package/dist/agents/genesis-agent.d.ts +61 -0
  10. package/dist/agents/genesis-agent.js +340 -0
  11. package/dist/agents/samurai-agent.d.ts +53 -0
  12. package/dist/agents/samurai-agent.js +279 -0
  13. package/dist/agents/sub-agent.d.ts +56 -0
  14. package/dist/agents/sub-agent.js +353 -0
  15. package/dist/backlog/backlog-requirement-validator.d.ts +10 -0
  16. package/dist/backlog/backlog-requirement-validator.js +60 -0
  17. package/dist/backlog/backlog-store.d.ts +29 -0
  18. package/dist/backlog/backlog-store.js +148 -0
  19. package/dist/backlog/po-agent.d.ts +16 -0
  20. package/dist/backlog/po-agent.js +121 -0
  21. package/dist/backlog/types.d.ts +25 -0
  22. package/dist/backlog/types.js +4 -0
  23. package/dist/bench/run.d.ts +11 -0
  24. package/dist/bench/run.js +185 -0
  25. package/dist/bench/types.d.ts +50 -0
  26. package/dist/bench/types.js +1 -0
  27. package/dist/cli/swarm.d.ts +10 -0
  28. package/dist/cli/swarm.js +100 -0
  29. package/dist/cli/task.d.ts +14 -0
  30. package/dist/cli/task.js +121 -0
  31. package/dist/cli/wbc-arg.d.ts +6 -0
  32. package/dist/cli/wbc-arg.js +40 -0
  33. package/dist/cli/wbc.d.ts +45 -0
  34. package/dist/cli/wbc.js +98 -0
  35. package/dist/commands/license.d.ts +9 -0
  36. package/dist/commands/license.js +140 -0
  37. package/dist/commands/release.d.ts +44 -0
  38. package/dist/commands/release.js +180 -0
  39. package/dist/commands/test.d.ts +50 -0
  40. package/dist/commands/test.js +385 -0
  41. package/dist/config/loadConfig.d.ts +82 -0
  42. package/dist/config/loadConfig.js +240 -0
  43. package/dist/config/secure-mode.d.ts +29 -0
  44. package/dist/config/secure-mode.js +63 -0
  45. package/dist/config/user-config.d.ts +39 -0
  46. package/dist/config/user-config.js +202 -0
  47. package/dist/engine/reasoning-engine.d.ts +21 -0
  48. package/dist/engine/reasoning-engine.js +59 -0
  49. package/dist/events/bus.d.ts +22 -0
  50. package/dist/events/bus.js +18 -0
  51. package/dist/index.d.ts +79 -0
  52. package/dist/index.js +77 -0
  53. package/dist/indexing/ignoreRules.d.ts +1 -0
  54. package/dist/indexing/ignoreRules.js +53 -0
  55. package/dist/instance-swarm/instance-swarm-orchestrator.d.ts +57 -0
  56. package/dist/instance-swarm/instance-swarm-orchestrator.js +204 -0
  57. package/dist/instance-swarm/protocol-name-resolver.d.ts +23 -0
  58. package/dist/instance-swarm/protocol-name-resolver.js +64 -0
  59. package/dist/license/gate.d.ts +31 -0
  60. package/dist/license/gate.js +188 -0
  61. package/dist/license/http-client.d.ts +43 -0
  62. package/dist/license/http-client.js +195 -0
  63. package/dist/license/manager.d.ts +39 -0
  64. package/dist/license/manager.js +175 -0
  65. package/dist/license/receipt.d.ts +27 -0
  66. package/dist/license/receipt.js +108 -0
  67. package/dist/license/state-codec.d.ts +72 -0
  68. package/dist/license/state-codec.js +218 -0
  69. package/dist/license/state-paths.d.ts +17 -0
  70. package/dist/license/state-paths.js +64 -0
  71. package/dist/license/store.d.ts +44 -0
  72. package/dist/license/store.js +223 -0
  73. package/dist/license/trust-anchor.d.ts +24 -0
  74. package/dist/license/trust-anchor.js +74 -0
  75. package/dist/license/types.d.ts +34 -0
  76. package/dist/license/types.js +19 -0
  77. package/dist/llm/budget.d.ts +23 -0
  78. package/dist/llm/budget.js +58 -0
  79. package/dist/llm/client.d.ts +53 -0
  80. package/dist/llm/client.js +122 -0
  81. package/dist/llm/factory.d.ts +14 -0
  82. package/dist/llm/factory.js +32 -0
  83. package/dist/orchestrator/defect-logger.d.ts +31 -0
  84. package/dist/orchestrator/defect-logger.js +101 -0
  85. package/dist/orchestrator/defect-manager.d.ts +118 -0
  86. package/dist/orchestrator/defect-manager.js +338 -0
  87. package/dist/orchestrator/defect-migration.d.ts +14 -0
  88. package/dist/orchestrator/defect-migration.js +174 -0
  89. package/dist/orchestrator/defect-store.d.ts +48 -0
  90. package/dist/orchestrator/defect-store.js +76 -0
  91. package/dist/orchestrator/healing.d.ts +14 -0
  92. package/dist/orchestrator/healing.js +11 -0
  93. package/dist/orchestrator/interactive-steering.d.ts +64 -0
  94. package/dist/orchestrator/interactive-steering.js +107 -0
  95. package/dist/orchestrator/interrupt-handler.d.ts +30 -0
  96. package/dist/orchestrator/interrupt-handler.js +70 -0
  97. package/dist/orchestrator/lesson-manager.d.ts +25 -0
  98. package/dist/orchestrator/lesson-manager.js +122 -0
  99. package/dist/orchestrator/progress-summary.d.ts +15 -0
  100. package/dist/orchestrator/progress-summary.js +106 -0
  101. package/dist/orchestrator/reasoning-score.d.ts +49 -0
  102. package/dist/orchestrator/reasoning-score.js +117 -0
  103. package/dist/orchestrator/swarm.d.ts +36 -0
  104. package/dist/orchestrator/swarm.js +77 -0
  105. package/dist/orchestrator/task-factory.d.ts +15 -0
  106. package/dist/orchestrator/task-factory.js +176 -0
  107. package/dist/orchestrator/validation-gate.d.ts +23 -0
  108. package/dist/orchestrator/validation-gate.js +155 -0
  109. package/dist/protocol/react-contract.d.ts +136 -0
  110. package/dist/protocol/react-contract.js +698 -0
  111. package/dist/protocol/wks-protocol-manager.d.ts +22 -0
  112. package/dist/protocol/wks-protocol-manager.js +72 -0
  113. package/dist/sdlc-runtime/conversation-runner.d.ts +8 -0
  114. package/dist/sdlc-runtime/conversation-runner.js +64 -0
  115. package/dist/sdlc-runtime/dag-utils.d.ts +15 -0
  116. package/dist/sdlc-runtime/dag-utils.js +67 -0
  117. package/dist/sdlc-runtime/defect-integration.d.ts +7 -0
  118. package/dist/sdlc-runtime/defect-integration.js +99 -0
  119. package/dist/sdlc-runtime/execution-loop.d.ts +38 -0
  120. package/dist/sdlc-runtime/execution-loop.js +274 -0
  121. package/dist/sdlc-runtime/lesson-recorder.d.ts +9 -0
  122. package/dist/sdlc-runtime/lesson-recorder.js +27 -0
  123. package/dist/sdlc-runtime/plan-initializer.d.ts +47 -0
  124. package/dist/sdlc-runtime/plan-initializer.js +135 -0
  125. package/dist/sdlc-runtime/progress-persister.d.ts +10 -0
  126. package/dist/sdlc-runtime/progress-persister.js +34 -0
  127. package/dist/sdlc-runtime/protocol-loader.d.ts +10 -0
  128. package/dist/sdlc-runtime/protocol-loader.js +21 -0
  129. package/dist/sdlc-runtime/reporting.d.ts +24 -0
  130. package/dist/sdlc-runtime/reporting.js +68 -0
  131. package/dist/sdlc-runtime/sdlc-orchestrator.d.ts +59 -0
  132. package/dist/sdlc-runtime/sdlc-orchestrator.js +170 -0
  133. package/dist/sdlc-runtime/sdlc-skill.d.ts +44 -0
  134. package/dist/sdlc-runtime/sdlc-skill.js +148 -0
  135. package/dist/sdlc-runtime/steering-handler.d.ts +14 -0
  136. package/dist/sdlc-runtime/steering-handler.js +51 -0
  137. package/dist/sdlc-runtime/trace-recorder.d.ts +10 -0
  138. package/dist/sdlc-runtime/trace-recorder.js +7 -0
  139. package/dist/security/index.d.ts +16 -0
  140. package/dist/security/index.js +12 -0
  141. package/dist/security/input-screen.d.ts +30 -0
  142. package/dist/security/input-screen.js +75 -0
  143. package/dist/security/tool-call-guard.d.ts +16 -0
  144. package/dist/security/tool-call-guard.js +163 -0
  145. package/dist/security/untrusted-content.d.ts +34 -0
  146. package/dist/security/untrusted-content.js +63 -0
  147. package/dist/skills/advanced-dfir/SKILL.md +85 -0
  148. package/dist/skills/advanced-pentest-lab/SKILL.md +88 -0
  149. package/dist/skills/analyst/SKILL.md +55 -0
  150. package/dist/skills/architect/SKILL.md +54 -0
  151. package/dist/skills/aws/SKILL.md +92 -0
  152. package/dist/skills/azure/SKILL.md +90 -0
  153. package/dist/skills/bylaw-context.d.ts +45 -0
  154. package/dist/skills/bylaw-context.js +61 -0
  155. package/dist/skills/bylaw-review.d.ts +20 -0
  156. package/dist/skills/bylaw-review.js +101 -0
  157. package/dist/skills/conversation/SKILL.md +29 -0
  158. package/dist/skills/design-patterns/SKILL.md +190 -0
  159. package/dist/skills/devops/SKILL.md +55 -0
  160. package/dist/skills/directives.d.ts +20 -0
  161. package/dist/skills/directives.js +313 -0
  162. package/dist/skills/docker/SKILL.md +76 -0
  163. package/dist/skills/docker-expert/SKILL.md +56 -0
  164. package/dist/skills/enterprise-systems-analyst/SKILL.md +83 -0
  165. package/dist/skills/enterprise-systems-analyst/references/crm.md +41 -0
  166. package/dist/skills/enterprise-systems-analyst/references/erp.md +40 -0
  167. package/dist/skills/enterprise-systems-analyst/references/financial-systems.md +35 -0
  168. package/dist/skills/enterprise-systems-analyst/references/hcm.md +38 -0
  169. package/dist/skills/enterprise-systems-analyst/references/mrp.md +37 -0
  170. package/dist/skills/enterprise-systems-analyst/references/scm.md +32 -0
  171. package/dist/skills/ethical-hacking-methodology/SKILL.md +119 -0
  172. package/dist/skills/externalSkillLoader.d.ts +25 -0
  173. package/dist/skills/externalSkillLoader.js +65 -0
  174. package/dist/skills/externalSkillRegistry.d.ts +33 -0
  175. package/dist/skills/externalSkillRegistry.js +94 -0
  176. package/dist/skills/filesystem-management/SKILL.md +61 -0
  177. package/dist/skills/first-run-seed.d.ts +26 -0
  178. package/dist/skills/first-run-seed.js +75 -0
  179. package/dist/skills/git-vcs/SKILL.md +84 -0
  180. package/dist/skills/kafka/SKILL.md +89 -0
  181. package/dist/skills/kubernetes/SKILL.md +93 -0
  182. package/dist/skills/kubernetes-expert/SKILL.md +54 -0
  183. package/dist/skills/openshift/SKILL.md +93 -0
  184. package/dist/skills/pentester/SKILL.md +59 -0
  185. package/dist/skills/performance-tester/SKILL.md +57 -0
  186. package/dist/skills/playwright-ui-tester/SKILL.md +67 -0
  187. package/dist/skills/programmer/SKILL.md +58 -0
  188. package/dist/skills/project-scaffolding/SKILL.md +133 -0
  189. package/dist/skills/qa-engineer/SKILL.md +91 -0
  190. package/dist/skills/rca/SKILL.md +58 -0
  191. package/dist/skills/redhat/SKILL.md +92 -0
  192. package/dist/skills/rosa/SKILL.md +80 -0
  193. package/dist/skills/scrum-framework/SKILL.md +74 -0
  194. package/dist/skills/scrum-framework/references/artifacts_and_quality.md +79 -0
  195. package/dist/skills/scrum-framework/references/roles_and_events.md +105 -0
  196. package/dist/skills/scrum-master-agent/SKILL.md +74 -0
  197. package/dist/skills/scrum-master-agent/references/behaviors.md +56 -0
  198. package/dist/skills/sdlc-skill.d.ts +1 -0
  199. package/dist/skills/sdlc-skill.js +4 -0
  200. package/dist/skills/secops/SKILL.md +52 -0
  201. package/dist/skills/secure-architect.bylaws.d.ts +68 -0
  202. package/dist/skills/secure-architect.bylaws.js +525 -0
  203. package/dist/skills/seed-skills.d.ts +48 -0
  204. package/dist/skills/seed-skills.js +213 -0
  205. package/dist/skills/skill-authoring/SKILL.md +123 -0
  206. package/dist/skills/skill-header.d.ts +51 -0
  207. package/dist/skills/skill-header.js +163 -0
  208. package/dist/skills/skill-path-guard.d.ts +41 -0
  209. package/dist/skills/skill-path-guard.js +95 -0
  210. package/dist/skills/skillRouter.d.ts +51 -0
  211. package/dist/skills/skillRouter.js +90 -0
  212. package/dist/skills/skills-root.d.ts +64 -0
  213. package/dist/skills/skills-root.js +168 -0
  214. package/dist/skills/software-architect/SKILL.md +77 -0
  215. package/dist/skills/software-engineer/SKILL.md +74 -0
  216. package/dist/skills/solid-principles/SKILL.md +94 -0
  217. package/dist/skills/task-planning/SKILL.md +115 -0
  218. package/dist/skills/tester/SKILL.md +54 -0
  219. package/dist/skills/ubuntu/SKILL.md +87 -0
  220. package/dist/skills/ui-ux-design/SKILL.md +114 -0
  221. package/dist/skills/workspace-context/SKILL.md +103 -0
  222. package/dist/sprint/controller.d.ts +37 -0
  223. package/dist/sprint/controller.js +220 -0
  224. package/dist/sprint/dag-builder.d.ts +14 -0
  225. package/dist/sprint/dag-builder.js +105 -0
  226. package/dist/sprint/reporter.d.ts +11 -0
  227. package/dist/sprint/reporter.js +97 -0
  228. package/dist/sprint/state-store.d.ts +10 -0
  229. package/dist/sprint/state-store.js +71 -0
  230. package/dist/sprint/types.d.ts +24 -0
  231. package/dist/sprint/types.js +4 -0
  232. package/dist/store/file-version-store.d.ts +24 -0
  233. package/dist/store/file-version-store.js +73 -0
  234. package/dist/store/task-store.d.ts +90 -0
  235. package/dist/store/task-store.js +166 -0
  236. package/dist/task-ingest/architect-detailer.d.ts +43 -0
  237. package/dist/task-ingest/architect-detailer.js +141 -0
  238. package/dist/task-ingest/detail-schema.d.ts +9 -0
  239. package/dist/task-ingest/detail-schema.js +195 -0
  240. package/dist/task-ingest/detailed-run.d.ts +36 -0
  241. package/dist/task-ingest/detailed-run.js +108 -0
  242. package/dist/task-ingest/extract-id.d.ts +9 -0
  243. package/dist/task-ingest/extract-id.js +24 -0
  244. package/dist/task-ingest/index.d.ts +23 -0
  245. package/dist/task-ingest/index.js +14 -0
  246. package/dist/task-ingest/ingest.d.ts +45 -0
  247. package/dist/task-ingest/ingest.js +163 -0
  248. package/dist/task-ingest/list-ingested.d.ts +13 -0
  249. package/dist/task-ingest/list-ingested.js +30 -0
  250. package/dist/task-ingest/pending-filter.d.ts +30 -0
  251. package/dist/task-ingest/pending-filter.js +59 -0
  252. package/dist/task-ingest/scanner.d.ts +56 -0
  253. package/dist/task-ingest/scanner.js +216 -0
  254. package/dist/task-ingest/source-reader.d.ts +25 -0
  255. package/dist/task-ingest/source-reader.js +93 -0
  256. package/dist/task-ingest/spec-builder.d.ts +34 -0
  257. package/dist/task-ingest/spec-builder.js +89 -0
  258. package/dist/task-ingest/wbs-extract.d.ts +50 -0
  259. package/dist/task-ingest/wbs-extract.js +226 -0
  260. package/dist/task-ingest/wbs-parser.d.ts +36 -0
  261. package/dist/task-ingest/wbs-parser.js +170 -0
  262. package/dist/tools/apiTestTool.d.ts +37 -0
  263. package/dist/tools/apiTestTool.js +128 -0
  264. package/dist/tools/ast-tools.d.ts +183 -0
  265. package/dist/tools/ast-tools.js +853 -0
  266. package/dist/tools/browse-tool.d.ts +105 -0
  267. package/dist/tools/browse-tool.js +394 -0
  268. package/dist/tools/credential.d.ts +17 -0
  269. package/dist/tools/credential.js +32 -0
  270. package/dist/tools/dependency-graph.d.ts +23 -0
  271. package/dist/tools/dependency-graph.js +164 -0
  272. package/dist/tools/edit-tool.d.ts +34 -0
  273. package/dist/tools/edit-tool.js +95 -0
  274. package/dist/tools/file-header-manager.d.ts +27 -0
  275. package/dist/tools/file-header-manager.js +153 -0
  276. package/dist/tools/fs-tools.d.ts +154 -0
  277. package/dist/tools/fs-tools.js +337 -0
  278. package/dist/tools/git-tools.d.ts +79 -0
  279. package/dist/tools/git-tools.js +348 -0
  280. package/dist/tools/github-tool.d.ts +48 -0
  281. package/dist/tools/github-tool.js +147 -0
  282. package/dist/tools/globTool.d.ts +20 -0
  283. package/dist/tools/globTool.js +34 -0
  284. package/dist/tools/grepTool.d.ts +19 -0
  285. package/dist/tools/grepTool.js +44 -0
  286. package/dist/tools/index-freshness.d.ts +58 -0
  287. package/dist/tools/index-freshness.js +190 -0
  288. package/dist/tools/index-glob.d.ts +48 -0
  289. package/dist/tools/index-glob.js +175 -0
  290. package/dist/tools/load-skill-tool.d.ts +15 -0
  291. package/dist/tools/load-skill-tool.js +114 -0
  292. package/dist/tools/patch-tools.d.ts +58 -0
  293. package/dist/tools/patch-tools.js +153 -0
  294. package/dist/tools/playwright-tool.d.ts +66 -0
  295. package/dist/tools/playwright-tool.js +121 -0
  296. package/dist/tools/project-detect.d.ts +20 -0
  297. package/dist/tools/project-detect.js +87 -0
  298. package/dist/tools/prompt-guidance.d.ts +22 -0
  299. package/dist/tools/prompt-guidance.js +23 -0
  300. package/dist/tools/scheduleTool.d.ts +19 -0
  301. package/dist/tools/scheduleTool.js +181 -0
  302. package/dist/tools/search-tools.d.ts +27 -0
  303. package/dist/tools/search-tools.js +78 -0
  304. package/dist/tools/sed-tool.d.ts +66 -0
  305. package/dist/tools/sed-tool.js +402 -0
  306. package/dist/tools/shell-result.d.ts +11 -0
  307. package/dist/tools/shell-result.js +1 -0
  308. package/dist/tools/shell-tool.d.ts +45 -0
  309. package/dist/tools/shell-tool.js +141 -0
  310. package/dist/tools/siteCrawlerTool.d.ts +32 -0
  311. package/dist/tools/siteCrawlerTool.js +205 -0
  312. package/dist/tools/tool-registry.d.ts +67 -0
  313. package/dist/tools/tool-registry.js +596 -0
  314. package/dist/tools/validate-tool.d.ts +23 -0
  315. package/dist/tools/validate-tool.js +201 -0
  316. package/dist/tools/workspace-index.d.ts +46 -0
  317. package/dist/tools/workspace-index.js +173 -0
  318. package/dist/types.d.ts +222 -0
  319. package/dist/types.js +7 -0
  320. package/dist/updater/index.d.ts +14 -0
  321. package/dist/updater/index.js +120 -0
  322. package/dist/util/banner.d.ts +25 -0
  323. package/dist/util/banner.js +140 -0
  324. package/dist/util/command-suggester.d.ts +6 -0
  325. package/dist/util/command-suggester.js +25 -0
  326. package/dist/util/confirm-interrupt.d.ts +10 -0
  327. package/dist/util/confirm-interrupt.js +31 -0
  328. package/dist/util/continue-trigger.d.ts +11 -0
  329. package/dist/util/continue-trigger.js +20 -0
  330. package/dist/util/env-info.d.ts +18 -0
  331. package/dist/util/env-info.js +37 -0
  332. package/dist/util/file-access-log.d.ts +35 -0
  333. package/dist/util/file-access-log.js +140 -0
  334. package/dist/util/footer-manager.d.ts +124 -0
  335. package/dist/util/footer-manager.js +195 -0
  336. package/dist/util/footer-renderer.d.ts +67 -0
  337. package/dist/util/footer-renderer.js +319 -0
  338. package/dist/util/footer-state.d.ts +34 -0
  339. package/dist/util/footer-state.js +18 -0
  340. package/dist/util/footer-text.d.ts +51 -0
  341. package/dist/util/footer-text.js +74 -0
  342. package/dist/util/ids.d.ts +1 -0
  343. package/dist/util/ids.js +4 -0
  344. package/dist/util/levenshtein.d.ts +6 -0
  345. package/dist/util/levenshtein.js +34 -0
  346. package/dist/util/logger.d.ts +10 -0
  347. package/dist/util/logger.js +41 -0
  348. package/dist/util/logs.d.ts +26 -0
  349. package/dist/util/logs.js +88 -0
  350. package/dist/util/output-handler.d.ts +89 -0
  351. package/dist/util/output-handler.js +193 -0
  352. package/dist/util/output-interceptor.d.ts +18 -0
  353. package/dist/util/output-interceptor.js +49 -0
  354. package/dist/util/output-paths.d.ts +43 -0
  355. package/dist/util/output-paths.js +121 -0
  356. package/dist/util/package-json.d.ts +6 -0
  357. package/dist/util/package-json.js +36 -0
  358. package/dist/util/process-kill.d.ts +27 -0
  359. package/dist/util/process-kill.js +148 -0
  360. package/dist/util/protocol-error-logger.d.ts +33 -0
  361. package/dist/util/protocol-error-logger.js +73 -0
  362. package/dist/util/redact.d.ts +25 -0
  363. package/dist/util/redact.js +103 -0
  364. package/dist/util/scroll-region-guard.d.ts +33 -0
  365. package/dist/util/scroll-region-guard.js +63 -0
  366. package/dist/util/spinner.d.ts +15 -0
  367. package/dist/util/spinner.js +49 -0
  368. package/dist/util/terminal-output.d.ts +34 -0
  369. package/dist/util/terminal-output.js +1 -0
  370. package/dist/util/terminal-silent.d.ts +6 -0
  371. package/dist/util/terminal-silent.js +20 -0
  372. package/dist/util/terminal.d.ts +56 -0
  373. package/dist/util/terminal.js +124 -0
  374. package/dist/util/text-sanitize.d.ts +8 -0
  375. package/dist/util/text-sanitize.js +45 -0
  376. package/dist/util/thinking-spinner.d.ts +27 -0
  377. package/dist/util/thinking-spinner.js +189 -0
  378. package/dist/util/tool-error-logger.d.ts +33 -0
  379. package/dist/util/tool-error-logger.js +77 -0
  380. package/dist/util/tty.d.ts +13 -0
  381. package/dist/util/tty.js +16 -0
  382. package/dist/util/xcoder-paths.d.ts +56 -0
  383. package/dist/util/xcoder-paths.js +63 -0
  384. package/dist/version.d.ts +2 -0
  385. package/dist/version.js +4 -0
  386. package/dist/worker-agent/to-subtask-node.d.ts +35 -0
  387. package/dist/worker-agent/to-subtask-node.js +41 -0
  388. package/dist/worker-agent/worker-orchestrator.d.ts +104 -0
  389. package/dist/worker-agent/worker-orchestrator.js +201 -0
  390. package/package.json +39 -4
@@ -0,0 +1,353 @@
1
+ import { buildAgentSystemPrompt, parseReactMessage } from "../protocol/react-contract.js";
2
+ import { buildToolManifest, dispatchTool } from "../tools/tool-registry.js";
3
+ import { ROLE_DIRECTIVES } from "../skills/directives.js";
4
+ import { buildBylawContext } from "../skills/bylaw-context.js";
5
+ import { getRoutedExternalSkills } from "../skills/skillRouter.js";
6
+ import { isExternalSkillsEnabled } from "../skills/externalSkillLoader.js";
7
+ import { getOrBuildIndex } from "../tools/workspace-index.js";
8
+ import { detectProjectCommands } from "../tools/project-detect.js";
9
+ import { getEnvironmentInfo } from "../util/env-info.js";
10
+ import { appendThink, appendSys, appendLlm, appendToken } from "../util/logs.js";
11
+ import { emitXcoderEvent } from "../events/bus.js";
12
+ import { DuplicateActionDetector } from "./duplicate-detector.js";
13
+ import * as term from "../util/terminal.js";
14
+ import { silentTerminal } from "../util/terminal-silent.js";
15
+ import { isProtocolErrorLoggingEnabled, logProtocolError } from "../util/protocol-error-logger.js";
16
+ import { isToolErrorLoggingEnabled, logToolError } from "../util/tool-error-logger.js";
17
+ import { recordLesson } from "../orchestrator/lesson-manager.js";
18
+ /**
19
+ * The "Instance Agent" of the design brief, mapped 1:1 onto Xcoder SDLC's Sub
20
+ * Agent: role and engine are config on the subtask node (`role`, `engine`),
21
+ * not baked into a special-purpose class.
22
+ *
23
+ * Isolation contract: `run()` NEVER throws. Every failure mode inside the
24
+ * loop — an LLM API error, a tool exception that somehow escaped
25
+ * `dispatchTool`'s own try/catch, an unparseable response — is caught,
26
+ * logged to `sys.log`, surfaced to the terminal, and turned into either a
27
+ * recoverable observation (the loop continues) or, if truly fatal, a
28
+ * `crashed: true` result the Master Orchestrator can route through its
29
+ * normal bounded healing/escalation path. The caller in `orchestrator/
30
+ * swarm.ts` still wraps this in its own try/catch as defense in depth, but
31
+ * this class is designed not to need that safety net.
32
+ */
33
+ export class SubAgent {
34
+ opts;
35
+ constructor(opts) {
36
+ this.opts = opts;
37
+ }
38
+ get term() {
39
+ return this.opts.quiet ? silentTerminal : term;
40
+ }
41
+ async run(subtask) {
42
+ const { taskId, workspaceRoot } = this.opts;
43
+ if (subtask.engine !== "react-v1") {
44
+ return this.crashResult(subtask, `Sub Agent engine "${subtask.engine}" is not implemented yet — only "react-v1" runs today.`);
45
+ }
46
+ let systemPrompt;
47
+ try {
48
+ const index = await getOrBuildIndex(workspaceRoot);
49
+ const detectedCommands = await detectProjectCommands(workspaceRoot, index);
50
+ const environment = getEnvironmentInfo();
51
+ const directive = ROLE_DIRECTIVES[subtask.role];
52
+ const override = this.opts.directiveOverride;
53
+ const effectiveDirective = typeof override === 'string' ? override : (override?.directive ?? directive.directive);
54
+ let effectiveSkills = typeof override === 'object' && override?.skills
55
+ ? override.skills
56
+ : (directive.skills ?? []);
57
+ // Append external skills relevant to this specific role (from
58
+ // skills/<name>/SKILL.md files) when XCODER_EXTERNAL_SKILLS is on.
59
+ // getRoutedExternalSkills() internally no-ops for 'conversation' (no
60
+ // allowlist entry) and returns [] when the flag is off, so this call
61
+ // is always safe to make unconditionally.
62
+ if (subtask.role !== "conversation" && isExternalSkillsEnabled()) {
63
+ effectiveSkills = [...effectiveSkills, ...getRoutedExternalSkills(subtask.role)];
64
+ }
65
+ const goal = this.opts.healingHint ? `${subtask.goal}\n\nHEALING HINT: ${this.opts.healingHint}` : subtask.goal;
66
+ const effectiveSubtask = { ...subtask, goal };
67
+ const protocolSection = this.opts.protocolOverrides?.[subtask.role];
68
+ const upstreamCtx = Object.keys(effectiveSubtask.upstreamContext).length
69
+ ? JSON.stringify(effectiveSubtask.upstreamContext, null, 2)
70
+ : undefined;
71
+ const goalText = [
72
+ `- id: ${effectiveSubtask.id}`,
73
+ `- stage: ${effectiveSubtask.stage}`,
74
+ `- goal: ${effectiveSubtask.goal}`,
75
+ `- what you must deliver back to the Master Orchestrator: ${effectiveSubtask.deliverableSpec}`,
76
+ `- acceptance criteria (the Validation Gate checks these independently — you`,
77
+ ` are not the one who decides you passed):`,
78
+ ...effectiveSubtask.acceptanceCriteria.map((ac) => ` - [${ac.id}] (${ac.type}) ${ac.description}`),
79
+ ].join("\n");
80
+ const bylaw = buildBylawContext({
81
+ stage: effectiveSubtask.stage,
82
+ role: effectiveSubtask.role,
83
+ goal: effectiveSubtask.goal,
84
+ });
85
+ systemPrompt = buildAgentSystemPrompt({
86
+ agentName: subtask.role,
87
+ roleDescription: effectiveDirective,
88
+ directive: effectiveDirective,
89
+ skills: effectiveSkills,
90
+ tools: buildToolManifest({ lessonLearned: this.opts.lessonLearned ?? false }),
91
+ environment,
92
+ detectedCommands,
93
+ goal: goalText,
94
+ upstreamContext: upstreamCtx,
95
+ protocolOverride: protocolSection,
96
+ isStandalone: false,
97
+ lessonContext: this.opts.lessonContext,
98
+ bylawContext: bylaw?.text,
99
+ });
100
+ }
101
+ catch (e) {
102
+ // Setup itself failing (e.g. can't read the workspace at all) is still
103
+ // not allowed to crash the orchestrator — surface it as a crash result.
104
+ return this.crashResult(subtask, `failed to prepare subtask: ${e.message}`);
105
+ }
106
+ const transcript = [
107
+ { role: "user", content: "Begin. Respond with your first contract message now." },
108
+ ];
109
+ const raoTrace = [];
110
+ const filesTouched = new Set();
111
+ const filesTouchedMeta = [];
112
+ const maxIterations = this.opts.maxIterations ?? 200;
113
+ const cumulative = { inputTokens: 0, outputTokens: 0 };
114
+ const dupDetector = new DuplicateActionDetector();
115
+ const ctx = { taskId, subtaskId: subtask.id };
116
+ // Fail fast on a deterministic LLM failure (same error N times in a row)
117
+ // instead of burning every iteration on a call that can never succeed.
118
+ const MAX_CONSECUTIVE_LLM_ERRORS = 3;
119
+ let lastLlmError = "";
120
+ let consecutiveLlmErrors = 0;
121
+ for (let i = 0; i < maxIterations; i++) {
122
+ await appendLlm(workspaceRoot, taskId, subtask.id, {
123
+ direction: "request",
124
+ content: transcript.at(-1)?.content ?? "",
125
+ });
126
+ const spinner = this.term.startThinking(subtask.id);
127
+ emitXcoderEvent({ type: "agent:thinking-start", taskId, subtaskId: subtask.id });
128
+ let raw;
129
+ let completion;
130
+ try {
131
+ completion = await this.opts.llm.complete(systemPrompt, transcript);
132
+ raw = completion.text;
133
+ cumulative.inputTokens += completion.usage.inputTokens;
134
+ cumulative.outputTokens += completion.usage.outputTokens;
135
+ await appendToken(workspaceRoot, taskId, subtask.id, {
136
+ inputTokens: completion.usage.inputTokens,
137
+ outputTokens: completion.usage.outputTokens,
138
+ cumulativeInput: cumulative.inputTokens,
139
+ cumulativeOutput: cumulative.outputTokens,
140
+ });
141
+ this.term.printTokens(subtask.id, completion.usage, cumulative);
142
+ emitXcoderEvent({
143
+ type: "agent:tokens",
144
+ taskId,
145
+ subtaskId: subtask.id,
146
+ data: { turn: completion.usage, cumulative },
147
+ });
148
+ await appendLlm(workspaceRoot, taskId, subtask.id, { direction: "response", content: raw, stopReason: completion.stopReason });
149
+ consecutiveLlmErrors = 0;
150
+ lastLlmError = "";
151
+ }
152
+ catch (e) {
153
+ // An LLM API failure (network, rate limit, auth) is exactly the
154
+ // kind of thing that used to take the whole process down with it.
155
+ // It's now a recoverable observation: log it, tell the model, let
156
+ // it try again — still bounded by maxIterations either way.
157
+ const message = e.message;
158
+ spinner.fail(`llm_error: ${message}`);
159
+ emitXcoderEvent({ type: "agent:crash", taskId, subtaskId: subtask.id, data: { message: `llm_error: ${message}`, recoverable: true } });
160
+ await appendSys(workspaceRoot, taskId, `[${subtask.id}] LLM call failed: ${message}`);
161
+ await appendThink(workspaceRoot, taskId, subtask.id, `(llm_error) ${message}`);
162
+ raoTrace.push({ thought: "(llm call failed)", action: null, observation: `llm_error: ${message}` });
163
+ consecutiveLlmErrors = message === lastLlmError ? consecutiveLlmErrors + 1 : 1;
164
+ lastLlmError = message;
165
+ if (consecutiveLlmErrors >= MAX_CONSECUTIVE_LLM_ERRORS) {
166
+ return this.crashResult(subtask, `LLM call failed ${consecutiveLlmErrors} times in a row with the same error: ${message}`);
167
+ }
168
+ transcript.push({ role: "user", content: `Observation: llm_error: ${message}\nTry again.` });
169
+ continue;
170
+ }
171
+ transcript.push({ role: "assistant", content: raw });
172
+ const parsed = parseReactMessage(raw);
173
+ if (!parsed.ok) {
174
+ spinner.stop("(unparseable response)");
175
+ this.term.printProtocolError(subtask.id, parsed.error);
176
+ emitXcoderEvent({ type: "agent:protocol-error", taskId, subtaskId: subtask.id, data: { message: parsed.error } });
177
+ if (this.opts.protocolErrorLog || isProtocolErrorLoggingEnabled()) {
178
+ logProtocolError({
179
+ ts: new Date().toISOString(),
180
+ taskId,
181
+ agentId: subtask.id,
182
+ iteration: i,
183
+ error: parsed.error,
184
+ raw,
185
+ model: completion.model ?? "unknown",
186
+ stopReason: completion.stopReason,
187
+ inputTokens: completion.usage.inputTokens,
188
+ outputTokens: completion.usage.outputTokens,
189
+ });
190
+ }
191
+ const isTruncated = completion.stopReason === 'max_tokens' || completion.stopReason === 'length';
192
+ const errObservation = isTruncated
193
+ ? `protocol_error: response was not valid JSON — your output was truncated because the token limit was reached (stop_reason: ${completion.stopReason}). The JSON was cut off mid-generation. Shorten your response — use fewer tools per turn, be more concise in your thought/action, and avoid including large file contents in your output. Parse error: ${parsed.error}`
194
+ : `protocol_error: ${parsed.error}`;
195
+ await appendThink(workspaceRoot, taskId, subtask.id, `(unparseable) ${errObservation}`);
196
+ raoTrace.push({ thought: "(unparseable response)", action: null, observation: errObservation });
197
+ transcript.push({ role: "user", content: `Observation: ${errObservation}\nRe-read the contract and try again.` });
198
+ continue;
199
+ }
200
+ const { thought, action, final_answer } = parsed.message;
201
+ spinner.stop(thought);
202
+ emitXcoderEvent({ type: "agent:thought", taskId, subtaskId: subtask.id, data: { thought } });
203
+ await appendThink(workspaceRoot, taskId, subtask.id, `Thought: ${thought}`);
204
+ if (final_answer) {
205
+ this.term.printFinalAnswer(subtask.id, final_answer.summary);
206
+ emitXcoderEvent({ type: "agent:final", taskId, subtaskId: subtask.id, data: { summary: final_answer.summary } });
207
+ await appendThink(workspaceRoot, taskId, subtask.id, `final_answer: ${final_answer.summary}`);
208
+ raoTrace.push({ thought, action: null, observation: "final_answer submitted" });
209
+ return {
210
+ summary: final_answer.summary,
211
+ deliverable: final_answer.deliverable,
212
+ filesTouched: [...filesTouched, ...final_answer.files_touched],
213
+ _filesTouchedMeta: filesTouchedMeta.length > 0 ? [...filesTouchedMeta] : undefined,
214
+ selfCheck: final_answer.self_check,
215
+ raoTrace,
216
+ tokenUsage: cumulative,
217
+ iterations: i + 1,
218
+ };
219
+ }
220
+ // Prose-only recovery: parseReactMessage's Layer 4a/4b can return
221
+ // { action: null, final_answer: null } when the model sends pure
222
+ // reasoning prose. Treat the thought as an observation and continue.
223
+ if (!action) {
224
+ const obs = `prose_recovery: no action or final_answer in response — model sent reasoning prose only. Thought: ${thought}`;
225
+ spinner.stop(`(prose-only: ${thought.slice(0, 80)}...)`);
226
+ emitXcoderEvent({ type: "agent:observation", taskId, subtaskId: subtask.id, data: { ok: true, tool: "prose_recovery", observation: obs } });
227
+ await appendThink(workspaceRoot, taskId, subtask.id, `Observation: ${obs}`);
228
+ raoTrace.push({ thought, action: null, observation: obs });
229
+ transcript.push({ role: "user", content: `Observation: ${obs}\nYou must respond with either an action or a final_answer.` });
230
+ continue;
231
+ }
232
+ const { tool, input } = action;
233
+ this.term.printAction(subtask.id, tool, input);
234
+ emitXcoderEvent({ type: "agent:action", taskId, subtaskId: subtask.id, data: { tool, input } });
235
+ await appendThink(workspaceRoot, taskId, subtask.id, `Action: ${tool} ${JSON.stringify(input)}`);
236
+ const dup = dupDetector.check(thought, tool, input, i + 1);
237
+ if (dup.isDuplicate) {
238
+ this.term.printDuplicateWarning(subtask.id, dup.warning);
239
+ emitXcoderEvent({ type: "agent:duplicate", taskId, subtaskId: subtask.id, data: { tool, priorIterations: dup.priorIterations, stuck: dup.stuck } });
240
+ await appendThink(workspaceRoot, taskId, subtask.id, `(duplicate) ${dup.warning}`);
241
+ }
242
+ if (dup.stuck) {
243
+ const message = `stuck in a repeated action loop: ${dupDetector.duplicateCount} duplicate actions detected — aborted after ${i + 1} iterations instead of running to the full ${maxIterations}-iteration cap.`;
244
+ await appendSys(workspaceRoot, taskId, `[${subtask.id}] ${message}`);
245
+ // Programmatic lesson recording (orchestrator-driven): when a Sub
246
+ // Agent gets stuck, record the pattern so the next run benefits.
247
+ if (this.opts.lessonLearned) {
248
+ recordLesson(workspaceRoot, {
249
+ source: `sub-agent:${subtask.role}`,
250
+ taskId,
251
+ tags: ["stuck-loop", subtask.role, subtask.stage],
252
+ context: `Agent got stuck in a repeated action loop after ${i + 1} iterations. The duplicate detector flagged ${dupDetector.duplicateCount} duplicate actions.`,
253
+ lesson: `When a Sub Agent gets stuck in a loop: check the observation text for actionable hints, reduce the max iterations, and consider re-planning with a clearer directive. The agent was ${subtask.role} running stage ${subtask.stage}.`,
254
+ }).catch(() => {
255
+ // Swallow — lesson recording is best-effort.
256
+ });
257
+ }
258
+ return {
259
+ summary: message,
260
+ deliverable: null,
261
+ filesTouched: [...filesTouched],
262
+ _filesTouchedMeta: filesTouchedMeta.length > 0 ? [...filesTouchedMeta] : undefined,
263
+ raoTrace,
264
+ tokenUsage: cumulative,
265
+ iterations: i + 1,
266
+ stuckLoop: true,
267
+ };
268
+ }
269
+ let result;
270
+ try {
271
+ result = await dispatchTool(workspaceRoot, tool, input, ctx);
272
+ }
273
+ catch (e) {
274
+ // dispatchTool already catches internally — this is a second net in
275
+ // case a future tool addition forgets to. Never let a tool crash
276
+ // the loop.
277
+ const message = e.message;
278
+ await appendSys(workspaceRoot, taskId, `[${subtask.id}] tool "${tool}" threw unexpectedly: ${message}`);
279
+ result = { tool, ok: false, output: { error: `unexpected tool failure: ${message}` } };
280
+ }
281
+ // log tool errors when --debug-tools is active
282
+ if (!result.ok && isToolErrorLoggingEnabled()) {
283
+ logToolError({
284
+ ts: new Date().toISOString(),
285
+ tool,
286
+ error: result.output?.error ?? "unknown",
287
+ ok: false,
288
+ taskId,
289
+ subtaskId: subtask.id,
290
+ input: JSON.stringify(input),
291
+ iteration: i + 1,
292
+ });
293
+ }
294
+ if (tool === "write_file" && result.ok) {
295
+ const out = result.output;
296
+ filesTouched.add(out.relPath);
297
+ filesTouchedMeta.push({ path: out.relPath, operation: "created" });
298
+ }
299
+ if (tool === "edit_file" && result.ok) {
300
+ const out = result.output;
301
+ filesTouched.add(out.relPath);
302
+ filesTouchedMeta.push({ path: out.relPath, operation: "modified" });
303
+ }
304
+ if (tool === "sed_tool" && result.ok) {
305
+ const out = result.output;
306
+ if (out.results) {
307
+ for (const r of out.results) {
308
+ if (r.matched) {
309
+ filesTouched.add(r.relPath);
310
+ filesTouchedMeta.push({ path: r.relPath, operation: "modified" });
311
+ }
312
+ }
313
+ }
314
+ }
315
+ let observation = JSON.stringify(result.output).slice(0, 4000); // keep the transcript bounded
316
+ if (dup.isDuplicate)
317
+ observation = `[DUPLICATE — see warning above] ${observation}`;
318
+ this.term.printObservation(subtask.id, result.ok, observation);
319
+ emitXcoderEvent({ type: "agent:observation", taskId, subtaskId: subtask.id, data: { ok: result.ok, tool, observation } });
320
+ await appendThink(workspaceRoot, taskId, subtask.id, `Observation: ${observation}`);
321
+ raoTrace.push({ thought, action: { tool, input }, observation });
322
+ transcript.push({
323
+ role: "user",
324
+ content: dup.warning ? `Observation: ${observation}\n\n${dup.warning}` : `Observation: ${observation}`,
325
+ });
326
+ }
327
+ await appendSys(workspaceRoot, taskId, `[${subtask.id}] exhausted ${maxIterations} iterations without a final_answer`);
328
+ return {
329
+ summary: `did not reach a final_answer within ${maxIterations} iterations`,
330
+ deliverable: null,
331
+ filesTouched: [...filesTouched],
332
+ _filesTouchedMeta: filesTouchedMeta.length > 0 ? [...filesTouchedMeta] : undefined,
333
+ raoTrace,
334
+ tokenUsage: cumulative,
335
+ iterations: maxIterations,
336
+ iterationsExhausted: true,
337
+ };
338
+ }
339
+ async crashResult(subtask, message) {
340
+ await appendSys(this.opts.workspaceRoot, this.opts.taskId, `[${subtask.id}] ${message}`);
341
+ this.term.printCrash(subtask.id, message);
342
+ emitXcoderEvent({ type: "agent:crash", taskId: this.opts.taskId, subtaskId: subtask.id, data: { message, recoverable: false } });
343
+ return {
344
+ summary: message,
345
+ deliverable: null,
346
+ filesTouched: [],
347
+ raoTrace: [],
348
+ tokenUsage: { inputTokens: 0, outputTokens: 0 },
349
+ iterations: 0,
350
+ crashed: true,
351
+ };
352
+ }
353
+ }
@@ -0,0 +1,10 @@
1
+ export interface BacklogRequirementCheck {
2
+ passed: boolean;
3
+ detail: string;
4
+ foundFiles: string[];
5
+ }
6
+ /**
7
+ * Checks whether any requirement markdown files exist under .xcoder/backlog/.
8
+ * Pure filesystem check — no LLM involvement, no backlog JSON parsing needed.
9
+ */
10
+ export declare function validateBacklogHasRequirements(workspaceRoot: string): Promise<BacklogRequirementCheck>;
@@ -0,0 +1,60 @@
1
+ // xcoder:version 1 | xcoder:task task-12fb57 | xcoder:updated 2026-08-04T12:24:23.969Z | xcoder:subtask code-st-33c7d1
2
+ import { promises as fs } from "node:fs";
3
+ import path from "node:path";
4
+ import { createXcoderPaths } from "../util/xcoder-paths.js";
5
+ /**
6
+ * Checks whether any requirement markdown files exist under .xcoder/backlog/.
7
+ * Pure filesystem check — no LLM involvement, no backlog JSON parsing needed.
8
+ */
9
+ export async function validateBacklogHasRequirements(workspaceRoot) {
10
+ const paths = createXcoderPaths(workspaceRoot);
11
+ const backlogDir = paths.backlogDir();
12
+ let entries;
13
+ try {
14
+ entries = await fs.readdir(backlogDir);
15
+ }
16
+ catch {
17
+ return {
18
+ passed: false,
19
+ detail: `Backlog directory not found: ${backlogDir}`,
20
+ foundFiles: [],
21
+ };
22
+ }
23
+ if (entries.length === 0) {
24
+ return {
25
+ passed: false,
26
+ detail: `Backlog directory is empty: ${backlogDir}`,
27
+ foundFiles: [],
28
+ };
29
+ }
30
+ const re = /requirements/i;
31
+ const matches = [];
32
+ for (const entry of entries) {
33
+ if (entry.endsWith(".md") && re.test(entry)) {
34
+ try {
35
+ const full = path.join(backlogDir, entry);
36
+ const stat = await fs.stat(full);
37
+ if (stat.isFile()) {
38
+ matches.push(full);
39
+ }
40
+ }
41
+ catch {
42
+ // skip unreadable entries
43
+ }
44
+ }
45
+ }
46
+ if (matches.length > 0) {
47
+ // Use .xcoder relative path in detail message
48
+ const xcoderBacklog = path.relative(workspaceRoot, backlogDir);
49
+ return {
50
+ passed: true,
51
+ detail: `Found ${matches.length} requirement file(s) in ${xcoderBacklog}/: ${path.basename(matches[0])}`,
52
+ foundFiles: matches,
53
+ };
54
+ }
55
+ return {
56
+ passed: false,
57
+ detail: `No requirement markdown files found in ${backlogDir}`,
58
+ foundFiles: [],
59
+ };
60
+ }
@@ -0,0 +1,29 @@
1
+ import type { BacklogFile, BacklogItem, BacklogStatus } from "./types.js";
2
+ /**
3
+ * Loads `.xcoder/backlog/_backlog.json`. Creates the directory and
4
+ * initializes with an empty file if it doesn't exist (idempotent).
5
+ */
6
+ export declare function loadBacklog(root: string): Promise<BacklogFile>;
7
+ /**
8
+ * Atomic write: tmp file → rename. Follows the task-store.ts pattern.
9
+ */
10
+ export declare function saveBacklog(root: string, data: BacklogFile): Promise<void>;
11
+ /**
12
+ * Add a new backlog item with status "open", assign the next sequential
13
+ * id (BL-001, BL-002, …), increment nextId, save, and return the item.
14
+ */
15
+ export declare function addBacklogItem(root: string, title: string, description?: string, priority?: string): Promise<BacklogItem>;
16
+ /**
17
+ * PATCH an existing item by id. Merges allowed fields, refreshes
18
+ * updatedAt. Returns the updated item or null if not found.
19
+ */
20
+ export declare function updateBacklogItem(root: string, id: string, patch: Partial<Pick<BacklogItem, "status" | "taskId" | "description" | "priority" | "sprintId" | "blockedReason" | "sprintRunAt">>): Promise<BacklogItem | null>;
21
+ /**
22
+ * Look up a single item by id. Returns null if not found.
23
+ */
24
+ export declare function getBacklogItem(root: string, id: string): Promise<BacklogItem | null>;
25
+ /**
26
+ * List items, optionally filtered by status. Sorted by createdAt
27
+ * ascending (oldest first).
28
+ */
29
+ export declare function listBacklogItems(root: string, statusFilter?: BacklogStatus): Promise<BacklogItem[]>;
@@ -0,0 +1,148 @@
1
+ // xcoder:version 5 | xcoder:task task-12fb57 | xcoder:updated 2026-08-04T12:26:19.567Z | xcoder:subtask code-st-33c7d1
2
+ import { promises as fs } from "node:fs";
3
+ import { createXcoderPaths } from "../util/xcoder-paths.js";
4
+ // ---------------------------------------------------------------------------
5
+ // Helpers
6
+ // ---------------------------------------------------------------------------
7
+ function pad(num, size = 3) {
8
+ return String(num).padStart(size, "0");
9
+ }
10
+ /**
11
+ * Normalize a raw item from disk into a fully-typed BacklogItem.
12
+ * Supplies defaults for fields that may be absent in older _backlog.json
13
+ * files (backward-compatibility).
14
+ */
15
+ function normalizeItem(raw) {
16
+ return {
17
+ id: String(raw.id ?? ""),
18
+ title: String(raw.title ?? ""),
19
+ description: String(raw.description ?? ""),
20
+ status: raw.status ?? "open",
21
+ priority: typeof raw.priority === "string" ? raw.priority : undefined,
22
+ taskId: typeof raw.taskId === "string" ? raw.taskId : undefined,
23
+ sprintId: typeof raw.sprintId === "string" ? raw.sprintId : null,
24
+ blockedReason: typeof raw.blockedReason === "string" ? raw.blockedReason : null,
25
+ sprintRunAt: typeof raw.sprintRunAt === "string" ? raw.sprintRunAt : null,
26
+ createdAt: String(raw.createdAt ?? new Date().toISOString()),
27
+ updatedAt: String(raw.updatedAt ?? new Date().toISOString()),
28
+ };
29
+ }
30
+ // ---------------------------------------------------------------------------
31
+ // Core load / save (atomic)
32
+ // ---------------------------------------------------------------------------
33
+ /**
34
+ * Loads `.xcoder/backlog/_backlog.json`. Creates the directory and
35
+ * initializes with an empty file if it doesn't exist (idempotent).
36
+ */
37
+ export async function loadBacklog(root) {
38
+ const paths = createXcoderPaths(root);
39
+ const file = paths.backlogFile();
40
+ try {
41
+ const raw = await fs.readFile(file, "utf-8");
42
+ const parsed = JSON.parse(raw);
43
+ // Basic structural validation.
44
+ if (parsed &&
45
+ typeof parsed === "object" &&
46
+ !Array.isArray(parsed) &&
47
+ Array.isArray(parsed.items) &&
48
+ parsed.meta &&
49
+ typeof parsed.meta === "object" &&
50
+ typeof parsed.meta.nextId === "number") {
51
+ return { meta: parsed.meta, items: parsed.items.map(normalizeItem) };
52
+ }
53
+ }
54
+ catch {
55
+ // File doesn't exist or is malformed — bootstrap below.
56
+ }
57
+ // Bootstrap: create dir + empty backlog.
58
+ const fresh = { meta: { nextId: 1 }, items: [] };
59
+ try {
60
+ await fs.mkdir(paths.backlogDir(), { recursive: true });
61
+ const tmp = `${file}.tmp`;
62
+ await fs.writeFile(tmp, JSON.stringify(fresh, null, 2), "utf8");
63
+ await fs.rename(tmp, file);
64
+ }
65
+ catch {
66
+ // If the dir already exists and we can't write, the save* functions will
67
+ // fail with a clearer error later.
68
+ }
69
+ return fresh;
70
+ }
71
+ /**
72
+ * Atomic write: tmp file → rename. Follows the task-store.ts pattern.
73
+ */
74
+ export async function saveBacklog(root, data) {
75
+ const paths = createXcoderPaths(root);
76
+ const file = paths.backlogFile();
77
+ await fs.mkdir(paths.backlogDir(), { recursive: true });
78
+ const tmp = `${file}.tmp`;
79
+ await fs.writeFile(tmp, JSON.stringify(data, null, 2), "utf8");
80
+ await fs.rename(tmp, file);
81
+ }
82
+ // ---------------------------------------------------------------------------
83
+ // CRUD operations
84
+ // ---------------------------------------------------------------------------
85
+ /**
86
+ * Add a new backlog item with status "open", assign the next sequential
87
+ * id (BL-001, BL-002, …), increment nextId, save, and return the item.
88
+ */
89
+ export async function addBacklogItem(root, title, description, priority) {
90
+ const data = await loadBacklog(root);
91
+ const id = `BL-${pad(data.meta.nextId)}`;
92
+ const now = new Date().toISOString();
93
+ const item = {
94
+ id,
95
+ title,
96
+ description: description ?? "",
97
+ status: "open",
98
+ priority,
99
+ sprintId: null,
100
+ blockedReason: null,
101
+ sprintRunAt: null,
102
+ createdAt: now,
103
+ updatedAt: now,
104
+ };
105
+ data.items.push(item);
106
+ data.meta.nextId += 1;
107
+ await saveBacklog(root, data);
108
+ return item;
109
+ }
110
+ /**
111
+ * PATCH an existing item by id. Merges allowed fields, refreshes
112
+ * updatedAt. Returns the updated item or null if not found.
113
+ */
114
+ export async function updateBacklogItem(root, id, patch) {
115
+ const data = await loadBacklog(root);
116
+ const idx = data.items.findIndex((it) => it.id === id);
117
+ if (idx === -1)
118
+ return null;
119
+ const existing = data.items[idx];
120
+ const updated = {
121
+ ...existing,
122
+ ...patch,
123
+ updatedAt: new Date().toISOString(),
124
+ };
125
+ data.items[idx] = updated;
126
+ await saveBacklog(root, data);
127
+ return updated;
128
+ }
129
+ /**
130
+ * Look up a single item by id. Returns null if not found.
131
+ */
132
+ export async function getBacklogItem(root, id) {
133
+ const data = await loadBacklog(root);
134
+ return data.items.find((it) => it.id === id) ?? null;
135
+ }
136
+ /**
137
+ * List items, optionally filtered by status. Sorted by createdAt
138
+ * ascending (oldest first).
139
+ */
140
+ export async function listBacklogItems(root, statusFilter) {
141
+ const data = await loadBacklog(root);
142
+ let items = data.items;
143
+ if (statusFilter) {
144
+ items = items.filter((it) => it.status === statusFilter);
145
+ }
146
+ // Sort by createdAt ascending.
147
+ return items.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
148
+ }
@@ -0,0 +1,16 @@
1
+ import type { BacklogItem } from "./types.js";
2
+ export interface PoPickResult {
3
+ selectedId: string;
4
+ title: string;
5
+ reasoning: string;
6
+ }
7
+ /**
8
+ * Runs a one-shot business-analyst Sub Agent (Product Owner) to select
9
+ * the highest-priority item from the provided backlog list.
10
+ *
11
+ * Follows the exact pattern of `src/ba-runner.ts`: creates a standalone
12
+ * SubAgent (no SdlcOrchestrator DAG), runs a single subtask, parses the
13
+ * deliverable. On LLM failure, falls back to deterministic
14
+ * oldest-open-item selection and logs a warning.
15
+ */
16
+ export declare function runPoPick(root: string, items: BacklogItem[], model?: string): Promise<PoPickResult>;