@planu/cli 0.89.0 → 0.90.1

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 (446) hide show
  1. package/dist/cli/commands/activate.d.ts +14 -0
  2. package/dist/cli/commands/activate.d.ts.map +1 -0
  3. package/dist/cli/commands/activate.js +174 -0
  4. package/dist/cli/commands/activate.js.map +1 -0
  5. package/dist/cli/commands/doctor.d.ts +16 -0
  6. package/dist/cli/commands/doctor.d.ts.map +1 -0
  7. package/dist/cli/commands/doctor.js +162 -0
  8. package/dist/cli/commands/doctor.js.map +1 -0
  9. package/dist/cli/commands/install.d.ts +48 -0
  10. package/dist/cli/commands/install.d.ts.map +1 -0
  11. package/dist/cli/commands/install.js +348 -0
  12. package/dist/cli/commands/install.js.map +1 -0
  13. package/dist/cli/commands/uninstall.d.ts +10 -0
  14. package/dist/cli/commands/uninstall.d.ts.map +1 -0
  15. package/dist/cli/commands/uninstall.js +133 -0
  16. package/dist/cli/commands/uninstall.js.map +1 -0
  17. package/dist/cli/router.d.ts.map +1 -1
  18. package/dist/cli/router.js +9 -1
  19. package/dist/cli/router.js.map +1 -1
  20. package/dist/config/license-plans.json +2 -1
  21. package/dist/engine/agent-generator.test.d.ts +2 -0
  22. package/dist/engine/agent-generator.test.d.ts.map +1 -0
  23. package/dist/engine/agent-generator.test.js +556 -0
  24. package/dist/engine/agent-generator.test.js.map +1 -0
  25. package/dist/engine/analyzer.test.d.ts +2 -0
  26. package/dist/engine/analyzer.test.d.ts.map +1 -0
  27. package/dist/engine/analyzer.test.js +1461 -0
  28. package/dist/engine/analyzer.test.js.map +1 -0
  29. package/dist/engine/auditor.test.d.ts +2 -0
  30. package/dist/engine/auditor.test.d.ts.map +1 -0
  31. package/dist/engine/auditor.test.js +2075 -0
  32. package/dist/engine/auditor.test.js.map +1 -0
  33. package/dist/engine/doc-generator.test.d.ts +2 -0
  34. package/dist/engine/doc-generator.test.d.ts.map +1 -0
  35. package/dist/engine/doc-generator.test.js +961 -0
  36. package/dist/engine/doc-generator.test.js.map +1 -0
  37. package/dist/engine/estimator.test.d.ts +2 -0
  38. package/dist/engine/estimator.test.d.ts.map +1 -0
  39. package/dist/engine/estimator.test.js +334 -0
  40. package/dist/engine/estimator.test.js.map +1 -0
  41. package/dist/engine/skill-generator.test.d.ts +2 -0
  42. package/dist/engine/skill-generator.test.d.ts.map +1 -0
  43. package/dist/engine/skill-generator.test.js +742 -0
  44. package/dist/engine/skill-generator.test.js.map +1 -0
  45. package/dist/engine/spec-migrator/filesystem-import.d.ts +14 -0
  46. package/dist/engine/spec-migrator/filesystem-import.d.ts.map +1 -0
  47. package/dist/engine/spec-migrator/filesystem-import.js +96 -0
  48. package/dist/engine/spec-migrator/filesystem-import.js.map +1 -0
  49. package/dist/engine/spec-migrator/flatten-specs.d.ts +12 -0
  50. package/dist/engine/spec-migrator/flatten-specs.d.ts.map +1 -0
  51. package/dist/engine/spec-migrator/flatten-specs.js +111 -0
  52. package/dist/engine/spec-migrator/flatten-specs.js.map +1 -0
  53. package/dist/engine/spec-migrator/folder-operations.d.ts +9 -0
  54. package/dist/engine/spec-migrator/folder-operations.d.ts.map +1 -0
  55. package/dist/engine/spec-migrator/folder-operations.js +109 -0
  56. package/dist/engine/spec-migrator/folder-operations.js.map +1 -0
  57. package/dist/engine/spec-migrator/frontmatter-parser.d.ts +11 -0
  58. package/dist/engine/spec-migrator/frontmatter-parser.d.ts.map +1 -0
  59. package/dist/engine/spec-migrator/frontmatter-parser.js +92 -0
  60. package/dist/engine/spec-migrator/frontmatter-parser.js.map +1 -0
  61. package/dist/engine/spec-migrator/index.d.ts +9 -0
  62. package/dist/engine/spec-migrator/index.d.ts.map +1 -0
  63. package/dist/engine/spec-migrator/index.js +18 -0
  64. package/dist/engine/spec-migrator/index.js.map +1 -0
  65. package/dist/engine/spec-migrator/legacy-migration.d.ts +13 -0
  66. package/dist/engine/spec-migrator/legacy-migration.d.ts.map +1 -0
  67. package/dist/engine/spec-migrator/legacy-migration.js +75 -0
  68. package/dist/engine/spec-migrator/legacy-migration.js.map +1 -0
  69. package/dist/engine/spec-migrator/migration-validator.d.ts +20 -0
  70. package/dist/engine/spec-migrator/migration-validator.d.ts.map +1 -0
  71. package/dist/engine/spec-migrator/migration-validator.js +35 -0
  72. package/dist/engine/spec-migrator/migration-validator.js.map +1 -0
  73. package/dist/engine/spec-migrator/path-utils.d.ts +13 -0
  74. package/dist/engine/spec-migrator/path-utils.d.ts.map +1 -0
  75. package/dist/engine/spec-migrator/path-utils.js +40 -0
  76. package/dist/engine/spec-migrator/path-utils.js.map +1 -0
  77. package/dist/engine/spec-migrator/prefix-migration.d.ts +11 -0
  78. package/dist/engine/spec-migrator/prefix-migration.d.ts.map +1 -0
  79. package/dist/engine/spec-migrator/prefix-migration.js +73 -0
  80. package/dist/engine/spec-migrator/prefix-migration.js.map +1 -0
  81. package/dist/engine/spec-migrator/reconcile-paths.d.ts +12 -0
  82. package/dist/engine/spec-migrator/reconcile-paths.d.ts.map +1 -0
  83. package/dist/engine/spec-migrator/reconcile-paths.js +77 -0
  84. package/dist/engine/spec-migrator/reconcile-paths.js.map +1 -0
  85. package/dist/engine/spec-migrator/version-detection.d.ts +5 -0
  86. package/dist/engine/spec-migrator/version-detection.d.ts.map +1 -0
  87. package/dist/engine/spec-migrator/version-detection.js +19 -0
  88. package/dist/engine/spec-migrator/version-detection.js.map +1 -0
  89. package/dist/engine/spec-migrator.d.ts +1 -58
  90. package/dist/engine/spec-migrator.d.ts.map +1 -1
  91. package/dist/engine/spec-migrator.js +2 -658
  92. package/dist/engine/spec-migrator.js.map +1 -1
  93. package/dist/engine/spec-summary-html/dashboard-renderer.d.ts +6 -0
  94. package/dist/engine/spec-summary-html/dashboard-renderer.d.ts.map +1 -0
  95. package/dist/engine/spec-summary-html/dashboard-renderer.js +333 -0
  96. package/dist/engine/spec-summary-html/dashboard-renderer.js.map +1 -0
  97. package/dist/engine/spec-summary-html/hash-utils.d.ts +11 -0
  98. package/dist/engine/spec-summary-html/hash-utils.d.ts.map +1 -0
  99. package/dist/engine/spec-summary-html/hash-utils.js +39 -0
  100. package/dist/engine/spec-summary-html/hash-utils.js.map +1 -0
  101. package/dist/engine/spec-summary-html/index.d.ts +4 -0
  102. package/dist/engine/spec-summary-html/index.d.ts.map +1 -0
  103. package/dist/engine/spec-summary-html/index.js +6 -0
  104. package/dist/engine/spec-summary-html/index.js.map +1 -0
  105. package/dist/engine/spec-summary-html/report-renderer.d.ts +9 -0
  106. package/dist/engine/spec-summary-html/report-renderer.d.ts.map +1 -0
  107. package/dist/engine/spec-summary-html/report-renderer.js +139 -0
  108. package/dist/engine/spec-summary-html/report-renderer.js.map +1 -0
  109. package/dist/engine/spec-summary-html.d.ts +1 -0
  110. package/dist/engine/spec-summary-html.d.ts.map +1 -1
  111. package/dist/engine/spec-summary-html.js +19 -473
  112. package/dist/engine/spec-summary-html.js.map +1 -1
  113. package/dist/engine/update-notifier.d.ts +8 -0
  114. package/dist/engine/update-notifier.d.ts.map +1 -0
  115. package/dist/engine/update-notifier.js +130 -0
  116. package/dist/engine/update-notifier.js.map +1 -0
  117. package/dist/engine/validator/dor-dod.d.ts.map +1 -1
  118. package/dist/engine/validator/dor-dod.js +8 -5
  119. package/dist/engine/validator/dor-dod.js.map +1 -1
  120. package/dist/engine/validator.d.ts.map +1 -1
  121. package/dist/engine/validator.js +4 -3
  122. package/dist/engine/validator.js.map +1 -1
  123. package/dist/engine/validator.test.d.ts +2 -0
  124. package/dist/engine/validator.test.d.ts.map +1 -0
  125. package/dist/engine/validator.test.js +2371 -0
  126. package/dist/engine/validator.test.js.map +1 -0
  127. package/dist/engine/web-fetcher.test.d.ts +2 -0
  128. package/dist/engine/web-fetcher.test.d.ts.map +1 -0
  129. package/dist/engine/web-fetcher.test.js +360 -0
  130. package/dist/engine/web-fetcher.test.js.map +1 -0
  131. package/dist/i18n/index.test.d.ts +2 -0
  132. package/dist/i18n/index.test.d.ts.map +1 -0
  133. package/dist/i18n/index.test.js +375 -0
  134. package/dist/i18n/index.test.js.map +1 -0
  135. package/dist/index.js +8 -0
  136. package/dist/index.js.map +1 -1
  137. package/dist/index.test.d.ts +2 -0
  138. package/dist/index.test.d.ts.map +1 -0
  139. package/dist/index.test.js +124 -0
  140. package/dist/index.test.js.map +1 -0
  141. package/dist/resources/patterns.test.d.ts +2 -0
  142. package/dist/resources/patterns.test.d.ts.map +1 -0
  143. package/dist/resources/patterns.test.js +142 -0
  144. package/dist/resources/patterns.test.js.map +1 -0
  145. package/dist/resources/process.test.d.ts +2 -0
  146. package/dist/resources/process.test.d.ts.map +1 -0
  147. package/dist/resources/process.test.js +48 -0
  148. package/dist/resources/process.test.js.map +1 -0
  149. package/dist/resources/registry.test.d.ts +2 -0
  150. package/dist/resources/registry.test.d.ts.map +1 -0
  151. package/dist/resources/registry.test.js +138 -0
  152. package/dist/resources/registry.test.js.map +1 -0
  153. package/dist/resources/specs.test.d.ts +2 -0
  154. package/dist/resources/specs.test.d.ts.map +1 -0
  155. package/dist/resources/specs.test.js +130 -0
  156. package/dist/resources/specs.test.js.map +1 -0
  157. package/dist/resources/templates.test.d.ts +2 -0
  158. package/dist/resources/templates.test.d.ts.map +1 -0
  159. package/dist/resources/templates.test.js +119 -0
  160. package/dist/resources/templates.test.js.map +1 -0
  161. package/dist/smoke.test.d.ts +2 -0
  162. package/dist/smoke.test.d.ts.map +1 -0
  163. package/dist/smoke.test.js +229 -0
  164. package/dist/smoke.test.js.map +1 -0
  165. package/dist/storage/base-store.test.d.ts +2 -0
  166. package/dist/storage/base-store.test.d.ts.map +1 -0
  167. package/dist/storage/base-store.test.js +180 -0
  168. package/dist/storage/base-store.test.js.map +1 -0
  169. package/dist/storage/global-store.test.d.ts +2 -0
  170. package/dist/storage/global-store.test.d.ts.map +1 -0
  171. package/dist/storage/global-store.test.js +327 -0
  172. package/dist/storage/global-store.test.js.map +1 -0
  173. package/dist/storage/index.test.d.ts +2 -0
  174. package/dist/storage/index.test.d.ts.map +1 -0
  175. package/dist/storage/index.test.js +56 -0
  176. package/dist/storage/index.test.js.map +1 -0
  177. package/dist/storage/knowledge-store.test.d.ts +2 -0
  178. package/dist/storage/knowledge-store.test.d.ts.map +1 -0
  179. package/dist/storage/knowledge-store.test.js +368 -0
  180. package/dist/storage/knowledge-store.test.js.map +1 -0
  181. package/dist/storage/metrics-store.test.d.ts +2 -0
  182. package/dist/storage/metrics-store.test.d.ts.map +1 -0
  183. package/dist/storage/metrics-store.test.js +212 -0
  184. package/dist/storage/metrics-store.test.js.map +1 -0
  185. package/dist/storage/pattern-store.test.d.ts +2 -0
  186. package/dist/storage/pattern-store.test.d.ts.map +1 -0
  187. package/dist/storage/pattern-store.test.js +224 -0
  188. package/dist/storage/pattern-store.test.js.map +1 -0
  189. package/dist/storage/spec-store.test.d.ts +2 -0
  190. package/dist/storage/spec-store.test.d.ts.map +1 -0
  191. package/dist/storage/spec-store.test.js +227 -0
  192. package/dist/storage/spec-store.test.js.map +1 -0
  193. package/dist/tools/audit.test.d.ts +2 -0
  194. package/dist/tools/audit.test.d.ts.map +1 -0
  195. package/dist/tools/audit.test.js +169 -0
  196. package/dist/tools/audit.test.js.map +1 -0
  197. package/dist/tools/challenge-spec.test.d.ts +2 -0
  198. package/dist/tools/challenge-spec.test.d.ts.map +1 -0
  199. package/dist/tools/challenge-spec.test.js +782 -0
  200. package/dist/tools/challenge-spec.test.js.map +1 -0
  201. package/dist/tools/check-versions.test.d.ts +2 -0
  202. package/dist/tools/check-versions.test.d.ts.map +1 -0
  203. package/dist/tools/check-versions.test.js +214 -0
  204. package/dist/tools/check-versions.test.js.map +1 -0
  205. package/dist/tools/clarify-requirements.test.d.ts +2 -0
  206. package/dist/tools/clarify-requirements.test.d.ts.map +1 -0
  207. package/dist/tools/clarify-requirements.test.js +161 -0
  208. package/dist/tools/clarify-requirements.test.js.map +1 -0
  209. package/dist/tools/consult-docs.test.d.ts +2 -0
  210. package/dist/tools/consult-docs.test.d.ts.map +1 -0
  211. package/dist/tools/consult-docs.test.js +140 -0
  212. package/dist/tools/consult-docs.test.js.map +1 -0
  213. package/dist/tools/create-spec.test.d.ts +2 -0
  214. package/dist/tools/create-spec.test.d.ts.map +1 -0
  215. package/dist/tools/create-spec.test.js +233 -0
  216. package/dist/tools/create-spec.test.js.map +1 -0
  217. package/dist/tools/define-ui-contract.test.d.ts +2 -0
  218. package/dist/tools/define-ui-contract.test.d.ts.map +1 -0
  219. package/dist/tools/define-ui-contract.test.js +479 -0
  220. package/dist/tools/define-ui-contract.test.js.map +1 -0
  221. package/dist/tools/design-schema.test.d.ts +2 -0
  222. package/dist/tools/design-schema.test.d.ts.map +1 -0
  223. package/dist/tools/design-schema.test.js +301 -0
  224. package/dist/tools/design-schema.test.js.map +1 -0
  225. package/dist/tools/detect-agent.test.d.ts +2 -0
  226. package/dist/tools/detect-agent.test.d.ts.map +1 -0
  227. package/dist/tools/detect-agent.test.js +133 -0
  228. package/dist/tools/detect-agent.test.js.map +1 -0
  229. package/dist/tools/detect-drift.test.d.ts +2 -0
  230. package/dist/tools/detect-drift.test.d.ts.map +1 -0
  231. package/dist/tools/detect-drift.test.js +312 -0
  232. package/dist/tools/detect-drift.test.js.map +1 -0
  233. package/dist/tools/discover-mcps.test.d.ts +2 -0
  234. package/dist/tools/discover-mcps.test.d.ts.map +1 -0
  235. package/dist/tools/discover-mcps.test.js +345 -0
  236. package/dist/tools/discover-mcps.test.js.map +1 -0
  237. package/dist/tools/estimate.test.d.ts +2 -0
  238. package/dist/tools/estimate.test.d.ts.map +1 -0
  239. package/dist/tools/estimate.test.js +137 -0
  240. package/dist/tools/estimate.test.js.map +1 -0
  241. package/dist/tools/generate-adr.test.d.ts +2 -0
  242. package/dist/tools/generate-adr.test.d.ts.map +1 -0
  243. package/dist/tools/generate-adr.test.js +206 -0
  244. package/dist/tools/generate-adr.test.js.map +1 -0
  245. package/dist/tools/generate-checklist.test.d.ts +2 -0
  246. package/dist/tools/generate-checklist.test.d.ts.map +1 -0
  247. package/dist/tools/generate-checklist.test.js +201 -0
  248. package/dist/tools/generate-checklist.test.js.map +1 -0
  249. package/dist/tools/generate-docs.test.d.ts +2 -0
  250. package/dist/tools/generate-docs.test.d.ts.map +1 -0
  251. package/dist/tools/generate-docs.test.js +183 -0
  252. package/dist/tools/generate-docs.test.js.map +1 -0
  253. package/dist/tools/generate-execution-plan.test.d.ts +2 -0
  254. package/dist/tools/generate-execution-plan.test.d.ts.map +1 -0
  255. package/dist/tools/generate-execution-plan.test.js +643 -0
  256. package/dist/tools/generate-execution-plan.test.js.map +1 -0
  257. package/dist/tools/generate-rules.test.d.ts +2 -0
  258. package/dist/tools/generate-rules.test.d.ts.map +1 -0
  259. package/dist/tools/generate-rules.test.js +148 -0
  260. package/dist/tools/generate-rules.test.js.map +1 -0
  261. package/dist/tools/generate-skill.test.d.ts +2 -0
  262. package/dist/tools/generate-skill.test.d.ts.map +1 -0
  263. package/dist/tools/generate-skill.test.js +138 -0
  264. package/dist/tools/generate-skill.test.js.map +1 -0
  265. package/dist/tools/generate-sub-agent.test.d.ts +2 -0
  266. package/dist/tools/generate-sub-agent.test.d.ts.map +1 -0
  267. package/dist/tools/generate-sub-agent.test.js +162 -0
  268. package/dist/tools/generate-sub-agent.test.js.map +1 -0
  269. package/dist/tools/generate-tests.test.d.ts +2 -0
  270. package/dist/tools/generate-tests.test.d.ts.map +1 -0
  271. package/dist/tools/generate-tests.test.js +222 -0
  272. package/dist/tools/generate-tests.test.js.map +1 -0
  273. package/dist/tools/init-constitution.test.d.ts +2 -0
  274. package/dist/tools/init-constitution.test.d.ts.map +1 -0
  275. package/dist/tools/init-constitution.test.js +398 -0
  276. package/dist/tools/init-constitution.test.js.map +1 -0
  277. package/dist/tools/init-project/config-builder.d.ts +12 -0
  278. package/dist/tools/init-project/config-builder.d.ts.map +1 -0
  279. package/dist/tools/init-project/config-builder.js +31 -0
  280. package/dist/tools/init-project/config-builder.js.map +1 -0
  281. package/dist/tools/init-project/git-setup.d.ts +8 -0
  282. package/dist/tools/init-project/git-setup.d.ts.map +1 -0
  283. package/dist/tools/init-project/git-setup.js +70 -0
  284. package/dist/tools/init-project/git-setup.js.map +1 -0
  285. package/dist/tools/init-project/handler.d.ts.map +1 -1
  286. package/dist/tools/init-project/handler.js +25 -371
  287. package/dist/tools/init-project/handler.js.map +1 -1
  288. package/dist/tools/init-project/lifecycle-helpers.d.ts +32 -0
  289. package/dist/tools/init-project/lifecycle-helpers.d.ts.map +1 -0
  290. package/dist/tools/init-project/lifecycle-helpers.js +153 -0
  291. package/dist/tools/init-project/lifecycle-helpers.js.map +1 -0
  292. package/dist/tools/init-project/migration-runner.d.ts +28 -0
  293. package/dist/tools/init-project/migration-runner.d.ts.map +1 -0
  294. package/dist/tools/init-project/migration-runner.js +57 -0
  295. package/dist/tools/init-project/migration-runner.js.map +1 -0
  296. package/dist/tools/init-project/rules-writer.d.ts +14 -0
  297. package/dist/tools/init-project/rules-writer.d.ts.map +1 -0
  298. package/dist/tools/init-project/rules-writer.js +43 -0
  299. package/dist/tools/init-project/rules-writer.js.map +1 -0
  300. package/dist/tools/init-project/scaffold-writer.d.ts +29 -0
  301. package/dist/tools/init-project/scaffold-writer.d.ts.map +1 -0
  302. package/dist/tools/init-project/scaffold-writer.js +76 -0
  303. package/dist/tools/init-project/scaffold-writer.js.map +1 -0
  304. package/dist/tools/init-project/stack-detector.d.ts +16 -0
  305. package/dist/tools/init-project/stack-detector.d.ts.map +1 -0
  306. package/dist/tools/init-project/stack-detector.js +19 -0
  307. package/dist/tools/init-project/stack-detector.js.map +1 -0
  308. package/dist/tools/init-project.test.d.ts +2 -0
  309. package/dist/tools/init-project.test.d.ts.map +1 -0
  310. package/dist/tools/init-project.test.js +158 -0
  311. package/dist/tools/init-project.test.js.map +1 -0
  312. package/dist/tools/integrate-pm.test.d.ts +2 -0
  313. package/dist/tools/integrate-pm.test.d.ts.map +1 -0
  314. package/dist/tools/integrate-pm.test.js +558 -0
  315. package/dist/tools/integrate-pm.test.js.map +1 -0
  316. package/dist/tools/learn.test.d.ts +2 -0
  317. package/dist/tools/learn.test.d.ts.map +1 -0
  318. package/dist/tools/learn.test.js +123 -0
  319. package/dist/tools/learn.test.js.map +1 -0
  320. package/dist/tools/list-specs.js +1 -1
  321. package/dist/tools/list-specs.js.map +1 -1
  322. package/dist/tools/list-specs.test.d.ts +2 -0
  323. package/dist/tools/list-specs.test.d.ts.map +1 -0
  324. package/dist/tools/list-specs.test.js +110 -0
  325. package/dist/tools/list-specs.test.js.map +1 -0
  326. package/dist/tools/manage-context.test.d.ts +2 -0
  327. package/dist/tools/manage-context.test.d.ts.map +1 -0
  328. package/dist/tools/manage-context.test.js +359 -0
  329. package/dist/tools/manage-context.test.js.map +1 -0
  330. package/dist/tools/manage-git.test.d.ts +2 -0
  331. package/dist/tools/manage-git.test.d.ts.map +1 -0
  332. package/dist/tools/manage-git.test.js +882 -0
  333. package/dist/tools/manage-git.test.js.map +1 -0
  334. package/dist/tools/orchestrate.test.d.ts +2 -0
  335. package/dist/tools/orchestrate.test.d.ts.map +1 -0
  336. package/dist/tools/orchestrate.test.js +1117 -0
  337. package/dist/tools/orchestrate.test.js.map +1 -0
  338. package/dist/tools/reconcile-spec.test.d.ts +2 -0
  339. package/dist/tools/reconcile-spec.test.d.ts.map +1 -0
  340. package/dist/tools/reconcile-spec.test.js +259 -0
  341. package/dist/tools/reconcile-spec.test.js.map +1 -0
  342. package/dist/tools/red-team.d.ts +3 -0
  343. package/dist/tools/red-team.d.ts.map +1 -0
  344. package/dist/tools/red-team.js +302 -0
  345. package/dist/tools/red-team.js.map +1 -0
  346. package/dist/tools/register-platform-tools/design-stack-tools.d.ts.map +1 -1
  347. package/dist/tools/register-platform-tools/design-stack-tools.js +14 -0
  348. package/dist/tools/register-platform-tools/design-stack-tools.js.map +1 -1
  349. package/dist/tools/register-platform-tools.test.d.ts +2 -0
  350. package/dist/tools/register-platform-tools.test.d.ts.map +1 -0
  351. package/dist/tools/register-platform-tools.test.js +404 -0
  352. package/dist/tools/register-platform-tools.test.js.map +1 -0
  353. package/dist/tools/register-spec-tools.test.d.ts +2 -0
  354. package/dist/tools/register-spec-tools.test.d.ts.map +1 -0
  355. package/dist/tools/register-spec-tools.test.js +407 -0
  356. package/dist/tools/register-spec-tools.test.js.map +1 -0
  357. package/dist/tools/reverse-engineer.test.d.ts +2 -0
  358. package/dist/tools/reverse-engineer.test.d.ts.map +1 -0
  359. package/dist/tools/reverse-engineer.test.js +206 -0
  360. package/dist/tools/reverse-engineer.test.js.map +1 -0
  361. package/dist/tools/schemas.d.ts +20 -0
  362. package/dist/tools/schemas.d.ts.map +1 -0
  363. package/dist/tools/schemas.js +133 -0
  364. package/dist/tools/schemas.js.map +1 -0
  365. package/dist/tools/schemas.test.d.ts +2 -0
  366. package/dist/tools/schemas.test.d.ts.map +1 -0
  367. package/dist/tools/schemas.test.js +245 -0
  368. package/dist/tools/schemas.test.js.map +1 -0
  369. package/dist/tools/set-locale.test.d.ts +2 -0
  370. package/dist/tools/set-locale.test.d.ts.map +1 -0
  371. package/dist/tools/set-locale.test.js +74 -0
  372. package/dist/tools/set-locale.test.js.map +1 -0
  373. package/dist/tools/suggest-mcps.test.d.ts +2 -0
  374. package/dist/tools/suggest-mcps.test.d.ts.map +1 -0
  375. package/dist/tools/suggest-mcps.test.js +198 -0
  376. package/dist/tools/suggest-mcps.test.js.map +1 -0
  377. package/dist/tools/suggest-stack.test.d.ts +2 -0
  378. package/dist/tools/suggest-stack.test.d.ts.map +1 -0
  379. package/dist/tools/suggest-stack.test.js +181 -0
  380. package/dist/tools/suggest-stack.test.js.map +1 -0
  381. package/dist/tools/suggest-tooling.test.d.ts +2 -0
  382. package/dist/tools/suggest-tooling.test.d.ts.map +1 -0
  383. package/dist/tools/suggest-tooling.test.js +213 -0
  384. package/dist/tools/suggest-tooling.test.js.map +1 -0
  385. package/dist/tools/summarize-spec.test.d.ts +2 -0
  386. package/dist/tools/summarize-spec.test.d.ts.map +1 -0
  387. package/dist/tools/summarize-spec.test.js +180 -0
  388. package/dist/tools/summarize-spec.test.js.map +1 -0
  389. package/dist/tools/update-status/dod-gates.d.ts +16 -0
  390. package/dist/tools/update-status/dod-gates.d.ts.map +1 -0
  391. package/dist/tools/update-status/dod-gates.js +117 -0
  392. package/dist/tools/update-status/dod-gates.js.map +1 -0
  393. package/dist/tools/update-status/file-sync.d.ts +6 -0
  394. package/dist/tools/update-status/file-sync.d.ts.map +1 -0
  395. package/dist/tools/update-status/file-sync.js +112 -0
  396. package/dist/tools/update-status/file-sync.js.map +1 -0
  397. package/dist/tools/update-status/index.d.ts +3 -0
  398. package/dist/tools/update-status/index.d.ts.map +1 -0
  399. package/dist/tools/update-status/index.js +181 -0
  400. package/dist/tools/update-status/index.js.map +1 -0
  401. package/dist/tools/update-status/response-builder.d.ts +4 -0
  402. package/dist/tools/update-status/response-builder.d.ts.map +1 -0
  403. package/dist/tools/update-status/response-builder.js +69 -0
  404. package/dist/tools/update-status/response-builder.js.map +1 -0
  405. package/dist/tools/update-status/side-effects.d.ts +15 -0
  406. package/dist/tools/update-status/side-effects.d.ts.map +1 -0
  407. package/dist/tools/update-status/side-effects.js +64 -0
  408. package/dist/tools/update-status/side-effects.js.map +1 -0
  409. package/dist/tools/update-status/transition-guard.d.ts +20 -0
  410. package/dist/tools/update-status/transition-guard.d.ts.map +1 -0
  411. package/dist/tools/update-status/transition-guard.js +75 -0
  412. package/dist/tools/update-status/transition-guard.js.map +1 -0
  413. package/dist/tools/update-status.d.ts +1 -2
  414. package/dist/tools/update-status.d.ts.map +1 -1
  415. package/dist/tools/update-status.js +2 -481
  416. package/dist/tools/update-status.js.map +1 -1
  417. package/dist/tools/update-status.test.d.ts +2 -0
  418. package/dist/tools/update-status.test.d.ts.map +1 -0
  419. package/dist/tools/update-status.test.js +142 -0
  420. package/dist/tools/update-status.test.js.map +1 -0
  421. package/dist/tools/validate.d.ts.map +1 -1
  422. package/dist/tools/validate.js +6 -4
  423. package/dist/tools/validate.js.map +1 -1
  424. package/dist/tools/validate.test.d.ts +2 -0
  425. package/dist/tools/validate.test.d.ts.map +1 -0
  426. package/dist/tools/validate.test.js +137 -0
  427. package/dist/tools/validate.test.js.map +1 -0
  428. package/dist/types/analysis.d.ts +2 -1
  429. package/dist/types/analysis.d.ts.map +1 -1
  430. package/dist/types/index.d.ts +2 -0
  431. package/dist/types/index.d.ts.map +1 -1
  432. package/dist/types/index.js +2 -0
  433. package/dist/types/index.js.map +1 -1
  434. package/dist/types/red-team.d.ts +29 -0
  435. package/dist/types/red-team.d.ts.map +1 -0
  436. package/dist/types/red-team.js +3 -0
  437. package/dist/types/red-team.js.map +1 -0
  438. package/dist/types/update-notifier.d.ts +5 -0
  439. package/dist/types/update-notifier.d.ts.map +1 -0
  440. package/dist/types/update-notifier.js +3 -0
  441. package/dist/types/update-notifier.js.map +1 -0
  442. package/package.json +9 -2
  443. package/src/config/license-plans.json +2 -1
  444. package/src/i18n/messages/en.json +5 -0
  445. package/src/i18n/messages/es.json +5 -0
  446. package/src/i18n/messages/pt.json +5 -0
@@ -0,0 +1,1117 @@
1
+ // Tests for handleOrchestrate
2
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
3
+ vi.mock('../storage/base-store.js', () => ({
4
+ readJson: vi.fn(),
5
+ writeJson: vi.fn(),
6
+ projectDataDir: (projectId) => `data/projects/${projectId}`,
7
+ }));
8
+ vi.mock('../storage/index.js', () => ({
9
+ specStore: { listSpecs: vi.fn() },
10
+ }));
11
+ vi.mock('node:crypto', () => ({
12
+ randomUUID: () => 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee',
13
+ }));
14
+ import { handleOrchestrate } from './orchestrate.js';
15
+ import { readJson, writeJson } from '../storage/base-store.js';
16
+ import { specStore } from '../storage/index.js';
17
+ const mockReadJson = vi.mocked(readJson);
18
+ const mockWriteJson = vi.mocked(writeJson);
19
+ const mockListSpecs = vi.mocked(specStore.listSpecs);
20
+ function emptyState() {
21
+ return { sessions: [], locks: [], conflicts: [] };
22
+ }
23
+ beforeEach(() => {
24
+ vi.clearAllMocks();
25
+ mockWriteJson.mockResolvedValue(undefined);
26
+ });
27
+ describe('handleOrchestrate', () => {
28
+ // === register ===
29
+ it('should register a new agent session', async () => {
30
+ mockReadJson.mockResolvedValue(emptyState());
31
+ const result = await handleOrchestrate({
32
+ projectId: 'proj-1',
33
+ action: 'register',
34
+ });
35
+ expect(result.isError).toBeUndefined();
36
+ const data = JSON.parse(result.content[0].text);
37
+ expect(data.action).toBe('register');
38
+ expect(data.sessionId).toBeDefined();
39
+ expect(data.sessions).toHaveLength(1);
40
+ expect(mockWriteJson).toHaveBeenCalled();
41
+ });
42
+ it('should register with custom sessionId and specId', async () => {
43
+ mockReadJson.mockResolvedValue(emptyState());
44
+ const result = await handleOrchestrate({
45
+ projectId: 'proj-1',
46
+ action: 'register',
47
+ sessionId: 'my-agent',
48
+ specId: 'SPEC-001',
49
+ });
50
+ const data = JSON.parse(result.content[0].text);
51
+ expect(data.sessionId).toBe('my-agent');
52
+ expect(data.sessions[0].activeSpecs).toContain('SPEC-001');
53
+ });
54
+ it('should re-activate existing session', async () => {
55
+ mockReadJson.mockResolvedValue({
56
+ sessions: [{
57
+ sessionId: 'agent-1',
58
+ agentPlatform: 'custom',
59
+ projectId: 'proj-1',
60
+ activeSpecs: [],
61
+ lockedResources: [],
62
+ startedAt: '2025-01-01T00:00:00Z',
63
+ lastActivity: '2025-01-01T00:00:00Z',
64
+ status: 'disconnected',
65
+ }],
66
+ locks: [],
67
+ conflicts: [],
68
+ });
69
+ const result = await handleOrchestrate({
70
+ projectId: 'proj-1',
71
+ action: 'register',
72
+ sessionId: 'agent-1',
73
+ });
74
+ const data = JSON.parse(result.content[0].text);
75
+ expect(data.message).toContain('Re-activated');
76
+ });
77
+ it('should detect spec overlap conflict on register', async () => {
78
+ mockReadJson.mockResolvedValue({
79
+ sessions: [{
80
+ sessionId: 'agent-1',
81
+ agentPlatform: 'custom',
82
+ projectId: 'proj-1',
83
+ activeSpecs: ['SPEC-001'],
84
+ lockedResources: [],
85
+ startedAt: '2025-01-01T00:00:00Z',
86
+ lastActivity: new Date().toISOString(),
87
+ status: 'active',
88
+ }],
89
+ locks: [],
90
+ conflicts: [],
91
+ });
92
+ const result = await handleOrchestrate({
93
+ projectId: 'proj-1',
94
+ action: 'register',
95
+ sessionId: 'agent-2',
96
+ specId: 'SPEC-001',
97
+ });
98
+ const data = JSON.parse(result.content[0].text);
99
+ expect(data.conflicts).toBeDefined();
100
+ expect(data.conflicts).toHaveLength(1);
101
+ expect(data.conflicts[0].type).toBe('spec-overlap');
102
+ });
103
+ // === heartbeat ===
104
+ it('should error when heartbeat called without sessionId', async () => {
105
+ const result = await handleOrchestrate({
106
+ projectId: 'proj-1',
107
+ action: 'heartbeat',
108
+ });
109
+ expect(result.isError).toBe(true);
110
+ });
111
+ it('should error when heartbeat for unknown session', async () => {
112
+ mockReadJson.mockResolvedValue(emptyState());
113
+ const result = await handleOrchestrate({
114
+ projectId: 'proj-1',
115
+ action: 'heartbeat',
116
+ sessionId: 'unknown',
117
+ });
118
+ expect(result.isError).toBe(true);
119
+ });
120
+ it('should update heartbeat successfully', async () => {
121
+ mockReadJson.mockResolvedValue({
122
+ sessions: [{
123
+ sessionId: 'agent-1',
124
+ agentPlatform: 'custom',
125
+ projectId: 'proj-1',
126
+ activeSpecs: [],
127
+ lockedResources: [],
128
+ startedAt: '2025-01-01T00:00:00Z',
129
+ lastActivity: '2025-01-01T00:00:00Z',
130
+ status: 'active',
131
+ }],
132
+ locks: [],
133
+ conflicts: [],
134
+ });
135
+ const result = await handleOrchestrate({
136
+ projectId: 'proj-1',
137
+ action: 'heartbeat',
138
+ sessionId: 'agent-1',
139
+ });
140
+ const data = JSON.parse(result.content[0].text);
141
+ expect(data.action).toBe('heartbeat');
142
+ expect(data.message).toContain('Heartbeat received');
143
+ });
144
+ // === lock ===
145
+ it('should error when lock called without sessionId', async () => {
146
+ const result = await handleOrchestrate({
147
+ projectId: 'proj-1',
148
+ action: 'lock',
149
+ resourcePath: 'src/auth.ts',
150
+ });
151
+ expect(result.isError).toBe(true);
152
+ });
153
+ it('should error when lock called without resource', async () => {
154
+ const result = await handleOrchestrate({
155
+ projectId: 'proj-1',
156
+ action: 'lock',
157
+ sessionId: 'agent-1',
158
+ });
159
+ expect(result.isError).toBe(true);
160
+ });
161
+ it('should lock a resource', async () => {
162
+ mockReadJson.mockResolvedValue({
163
+ sessions: [{
164
+ sessionId: 'agent-1',
165
+ agentPlatform: 'custom',
166
+ projectId: 'proj-1',
167
+ activeSpecs: [],
168
+ lockedResources: [],
169
+ startedAt: '2025-01-01T00:00:00Z',
170
+ lastActivity: new Date().toISOString(),
171
+ status: 'active',
172
+ }],
173
+ locks: [],
174
+ conflicts: [],
175
+ });
176
+ const result = await handleOrchestrate({
177
+ projectId: 'proj-1',
178
+ action: 'lock',
179
+ sessionId: 'agent-1',
180
+ resourcePath: 'src/auth.ts',
181
+ });
182
+ const data = JSON.parse(result.content[0].text);
183
+ expect(data.granted).toBe(true);
184
+ });
185
+ it('should deny lock when resource locked by another', async () => {
186
+ const future = new Date(Date.now() + 3600000).toISOString();
187
+ mockReadJson.mockResolvedValue({
188
+ sessions: [{
189
+ sessionId: 'agent-1',
190
+ agentPlatform: 'custom',
191
+ projectId: 'proj-1',
192
+ activeSpecs: [],
193
+ lockedResources: [],
194
+ startedAt: '2025-01-01T00:00:00Z',
195
+ lastActivity: new Date().toISOString(),
196
+ status: 'active',
197
+ }],
198
+ locks: [{
199
+ type: 'file',
200
+ resourceId: 'src/auth.ts',
201
+ lockedBy: 'agent-2',
202
+ lockedAt: new Date().toISOString(),
203
+ reason: 'Working',
204
+ expiresAt: future,
205
+ }],
206
+ conflicts: [],
207
+ });
208
+ const result = await handleOrchestrate({
209
+ projectId: 'proj-1',
210
+ action: 'lock',
211
+ sessionId: 'agent-1',
212
+ resourcePath: 'src/auth.ts',
213
+ });
214
+ const data = JSON.parse(result.content[0].text);
215
+ expect(data.granted).toBe(false);
216
+ });
217
+ // === unlock ===
218
+ it('should error when unlock called without sessionId', async () => {
219
+ const result = await handleOrchestrate({
220
+ projectId: 'proj-1',
221
+ action: 'unlock',
222
+ resourcePath: 'src/auth.ts',
223
+ });
224
+ expect(result.isError).toBe(true);
225
+ });
226
+ it('should unlock a resource', async () => {
227
+ mockReadJson.mockResolvedValue({
228
+ sessions: [{
229
+ sessionId: 'agent-1',
230
+ agentPlatform: 'custom',
231
+ projectId: 'proj-1',
232
+ activeSpecs: [],
233
+ lockedResources: [{ resourceId: 'src/auth.ts' }],
234
+ startedAt: '2025-01-01T00:00:00Z',
235
+ lastActivity: new Date().toISOString(),
236
+ status: 'active',
237
+ }],
238
+ locks: [{
239
+ type: 'file',
240
+ resourceId: 'src/auth.ts',
241
+ lockedBy: 'agent-1',
242
+ lockedAt: new Date().toISOString(),
243
+ reason: 'Working',
244
+ }],
245
+ conflicts: [],
246
+ });
247
+ const result = await handleOrchestrate({
248
+ projectId: 'proj-1',
249
+ action: 'unlock',
250
+ sessionId: 'agent-1',
251
+ resourcePath: 'src/auth.ts',
252
+ });
253
+ const data = JSON.parse(result.content[0].text);
254
+ expect(data.granted).toBe(true);
255
+ });
256
+ // === distribute ===
257
+ it('should distribute tasks with no active sessions', async () => {
258
+ mockReadJson.mockResolvedValue(emptyState());
259
+ mockListSpecs.mockResolvedValue([]);
260
+ const result = await handleOrchestrate({
261
+ projectId: 'proj-1',
262
+ action: 'distribute',
263
+ });
264
+ const data = JSON.parse(result.content[0].text);
265
+ expect(data.action).toBe('distribute');
266
+ expect(data.message).toContain('No active agent sessions');
267
+ });
268
+ it('should distribute tasks across active agents', async () => {
269
+ mockReadJson.mockResolvedValue({
270
+ sessions: [{
271
+ sessionId: 'agent-1',
272
+ agentPlatform: 'custom',
273
+ projectId: 'proj-1',
274
+ activeSpecs: ['SPEC-001'],
275
+ lockedResources: [],
276
+ startedAt: '2025-01-01T00:00:00Z',
277
+ lastActivity: new Date().toISOString(),
278
+ status: 'active',
279
+ }],
280
+ locks: [],
281
+ conflicts: [],
282
+ });
283
+ mockListSpecs.mockResolvedValue([
284
+ {
285
+ id: 'SPEC-001',
286
+ status: 'implementing',
287
+ risk: 'high',
288
+ scope: 'feature',
289
+ estimation: { devHours: 8 },
290
+ dependencies: [],
291
+ },
292
+ {
293
+ id: 'SPEC-002',
294
+ status: 'approved',
295
+ risk: 'low',
296
+ scope: 'trivial',
297
+ estimation: { devHours: 2 },
298
+ dependencies: [],
299
+ },
300
+ ]);
301
+ const result = await handleOrchestrate({
302
+ projectId: 'proj-1',
303
+ action: 'distribute',
304
+ });
305
+ const data = JSON.parse(result.content[0].text);
306
+ expect(data.distribution.length).toBe(2);
307
+ });
308
+ // === status ===
309
+ it('should return orchestration status', async () => {
310
+ mockReadJson.mockResolvedValue({
311
+ sessions: [{
312
+ sessionId: 'agent-1',
313
+ agentPlatform: 'custom',
314
+ projectId: 'proj-1',
315
+ activeSpecs: [],
316
+ lockedResources: [],
317
+ startedAt: '2025-01-01T00:00:00Z',
318
+ lastActivity: new Date().toISOString(),
319
+ status: 'active',
320
+ }],
321
+ locks: [],
322
+ conflicts: [],
323
+ });
324
+ const result = await handleOrchestrate({
325
+ projectId: 'proj-1',
326
+ action: 'status',
327
+ });
328
+ const data = JSON.parse(result.content[0].text);
329
+ expect(data.action).toBe('status');
330
+ expect(data.activeSessions).toBe(1);
331
+ });
332
+ // === resolve-conflict ===
333
+ it('should resolve conflicts', async () => {
334
+ mockReadJson.mockResolvedValue({
335
+ sessions: [],
336
+ locks: [],
337
+ conflicts: [{
338
+ type: 'spec-overlap',
339
+ agents: ['agent-1', 'agent-2'],
340
+ resource: 'SPEC-001',
341
+ resolution: 'Coordinate',
342
+ severity: 'warning',
343
+ }],
344
+ });
345
+ const result = await handleOrchestrate({
346
+ projectId: 'proj-1',
347
+ action: 'resolve-conflict',
348
+ sessionId: 'agent-1',
349
+ specId: 'SPEC-001',
350
+ });
351
+ const data = JSON.parse(result.content[0].text);
352
+ expect(data.action).toBe('resolve-conflict');
353
+ expect(data.message).toContain('Resolved');
354
+ });
355
+ // === unknown action ===
356
+ it('should error for unknown action', async () => {
357
+ const result = await handleOrchestrate({
358
+ projectId: 'proj-1',
359
+ action: 'unknown',
360
+ });
361
+ expect(result.isError).toBe(true);
362
+ });
363
+ // === error handling ===
364
+ it('should catch and return general errors', async () => {
365
+ mockReadJson.mockRejectedValue(new Error('Disk full'));
366
+ const result = await handleOrchestrate({
367
+ projectId: 'proj-1',
368
+ action: 'status',
369
+ });
370
+ expect(result.isError).toBe(true);
371
+ expect(result.content[0].text).toContain('Disk full');
372
+ });
373
+ // --- Coverage for non-Error thrown (line 721) ---
374
+ it('should catch non-Error thrown values', async () => {
375
+ mockReadJson.mockRejectedValue('string error');
376
+ const result = await handleOrchestrate({
377
+ projectId: 'proj-1',
378
+ action: 'register',
379
+ });
380
+ expect(result.isError).toBe(true);
381
+ expect(result.content[0].text).toContain('string error');
382
+ });
383
+ // --- Coverage for resolve-conflict with force-unlock (lines 638-652) ---
384
+ it('should force-unlock resource when resolving conflict', async () => {
385
+ mockReadJson.mockResolvedValue({
386
+ sessions: [
387
+ {
388
+ sessionId: 'agent-1',
389
+ agentPlatform: 'custom',
390
+ projectId: 'proj-1',
391
+ activeSpecs: ['SPEC-001'],
392
+ lockedResources: [{ resourceId: 'SPEC-001', type: 'spec', lockedBy: 'agent-1' }],
393
+ startedAt: '2025-01-01T00:00:00Z',
394
+ lastActivity: new Date().toISOString(),
395
+ status: 'active',
396
+ },
397
+ {
398
+ sessionId: 'agent-2',
399
+ agentPlatform: 'custom',
400
+ projectId: 'proj-1',
401
+ activeSpecs: ['SPEC-001'],
402
+ lockedResources: [],
403
+ startedAt: '2025-01-01T00:00:00Z',
404
+ lastActivity: new Date().toISOString(),
405
+ status: 'active',
406
+ },
407
+ ],
408
+ locks: [{
409
+ type: 'spec',
410
+ resourceId: 'SPEC-001',
411
+ lockedBy: 'agent-1',
412
+ lockedAt: new Date().toISOString(),
413
+ reason: 'Working',
414
+ expiresAt: new Date(Date.now() + 3600000).toISOString(),
415
+ }],
416
+ conflicts: [{
417
+ type: 'spec-overlap',
418
+ agents: ['agent-1', 'agent-2'],
419
+ resource: 'SPEC-001',
420
+ resolution: 'Coordinate',
421
+ severity: 'warning',
422
+ }],
423
+ });
424
+ const result = await handleOrchestrate({
425
+ projectId: 'proj-1',
426
+ action: 'resolve-conflict',
427
+ sessionId: 'agent-2',
428
+ specId: 'SPEC-001',
429
+ });
430
+ const data = JSON.parse(result.content[0].text);
431
+ expect(data.message).toContain('Resolved');
432
+ // Lock should have been force-removed from agent-1
433
+ const savedState = mockWriteJson.mock.calls[0][1];
434
+ expect(savedState.locks).toHaveLength(0);
435
+ });
436
+ // --- Coverage for resolve-conflict with no matching conflicts ---
437
+ it('should report no matching conflicts when none found', async () => {
438
+ mockReadJson.mockResolvedValue({
439
+ sessions: [],
440
+ locks: [],
441
+ conflicts: [{
442
+ type: 'spec-overlap',
443
+ agents: ['agent-3', 'agent-4'],
444
+ resource: 'SPEC-002',
445
+ resolution: 'Coordinate',
446
+ severity: 'warning',
447
+ }],
448
+ });
449
+ const result = await handleOrchestrate({
450
+ projectId: 'proj-1',
451
+ action: 'resolve-conflict',
452
+ sessionId: 'agent-1',
453
+ specId: 'SPEC-001',
454
+ });
455
+ const data = JSON.parse(result.content[0].text);
456
+ expect(data.message).toContain('No matching conflicts');
457
+ });
458
+ // --- Coverage for resolve-conflict without sessionId or resourcePath ---
459
+ it('should resolve all conflicts when no filters provided', async () => {
460
+ mockReadJson.mockResolvedValue({
461
+ sessions: [],
462
+ locks: [],
463
+ conflicts: [
464
+ {
465
+ type: 'spec-overlap',
466
+ agents: ['agent-1', 'agent-2'],
467
+ resource: 'SPEC-001',
468
+ resolution: 'Coordinate',
469
+ severity: 'warning',
470
+ },
471
+ {
472
+ type: 'spec-overlap',
473
+ agents: ['agent-3', 'agent-4'],
474
+ resource: 'SPEC-002',
475
+ resolution: 'Coordinate',
476
+ severity: 'warning',
477
+ },
478
+ ],
479
+ });
480
+ const result = await handleOrchestrate({
481
+ projectId: 'proj-1',
482
+ action: 'resolve-conflict',
483
+ });
484
+ const data = JSON.parse(result.content[0].text);
485
+ expect(data.message).toContain('Resolved 2 conflict(s)');
486
+ });
487
+ // --- Coverage for unlock with no resource (specId or resourcePath) ---
488
+ it('should error when unlock called without resourcePath or specId', async () => {
489
+ const result = await handleOrchestrate({
490
+ projectId: 'proj-1',
491
+ action: 'unlock',
492
+ sessionId: 'agent-1',
493
+ });
494
+ expect(result.isError).toBe(true);
495
+ expect(result.content[0].text).toContain('resourcePath or specId');
496
+ });
497
+ // --- Coverage for unlock: lock not found ---
498
+ it('should error when trying to unlock a resource not locked by session', async () => {
499
+ mockReadJson.mockResolvedValue({
500
+ sessions: [{
501
+ sessionId: 'agent-1',
502
+ agentPlatform: 'custom',
503
+ projectId: 'proj-1',
504
+ activeSpecs: [],
505
+ lockedResources: [],
506
+ startedAt: '2025-01-01T00:00:00Z',
507
+ lastActivity: new Date().toISOString(),
508
+ status: 'active',
509
+ }],
510
+ locks: [],
511
+ conflicts: [],
512
+ });
513
+ const result = await handleOrchestrate({
514
+ projectId: 'proj-1',
515
+ action: 'unlock',
516
+ sessionId: 'agent-1',
517
+ resourcePath: 'src/not-locked.ts',
518
+ });
519
+ expect(result.isError).toBe(true);
520
+ expect(result.content[0].text).toContain('No lock on');
521
+ });
522
+ // --- Coverage for lock: session not found ---
523
+ it('should error when locking with non-existent session', async () => {
524
+ mockReadJson.mockResolvedValue(emptyState());
525
+ const result = await handleOrchestrate({
526
+ projectId: 'proj-1',
527
+ action: 'lock',
528
+ sessionId: 'unknown-agent',
529
+ resourcePath: 'src/auth.ts',
530
+ });
531
+ expect(result.isError).toBe(true);
532
+ expect(result.content[0].text).toContain('not found');
533
+ });
534
+ // --- Coverage for lock: extend existing lock by same session ---
535
+ it('should extend lock when same session locks same resource', async () => {
536
+ const future = new Date(Date.now() + 3600000).toISOString();
537
+ mockReadJson.mockResolvedValue({
538
+ sessions: [{
539
+ sessionId: 'agent-1',
540
+ agentPlatform: 'custom',
541
+ projectId: 'proj-1',
542
+ activeSpecs: [],
543
+ lockedResources: [{
544
+ type: 'file',
545
+ resourceId: 'src/auth.ts',
546
+ lockedBy: 'agent-1',
547
+ lockedAt: new Date().toISOString(),
548
+ reason: 'Working',
549
+ expiresAt: future,
550
+ }],
551
+ startedAt: '2025-01-01T00:00:00Z',
552
+ lastActivity: new Date().toISOString(),
553
+ status: 'active',
554
+ }],
555
+ locks: [{
556
+ type: 'file',
557
+ resourceId: 'src/auth.ts',
558
+ lockedBy: 'agent-1',
559
+ lockedAt: new Date().toISOString(),
560
+ reason: 'Working',
561
+ expiresAt: future,
562
+ }],
563
+ conflicts: [],
564
+ });
565
+ const result = await handleOrchestrate({
566
+ projectId: 'proj-1',
567
+ action: 'lock',
568
+ sessionId: 'agent-1',
569
+ resourcePath: 'src/auth.ts',
570
+ });
571
+ const data = JSON.parse(result.content[0].text);
572
+ expect(data.granted).toBe(true);
573
+ expect(data.message).toContain('extended');
574
+ });
575
+ // --- Coverage for lock using specId instead of resourcePath ---
576
+ it('should lock using specId as resource identifier', async () => {
577
+ mockReadJson.mockResolvedValue({
578
+ sessions: [{
579
+ sessionId: 'agent-1',
580
+ agentPlatform: 'custom',
581
+ projectId: 'proj-1',
582
+ activeSpecs: ['SPEC-001'],
583
+ lockedResources: [],
584
+ startedAt: '2025-01-01T00:00:00Z',
585
+ lastActivity: new Date().toISOString(),
586
+ status: 'active',
587
+ }],
588
+ locks: [],
589
+ conflicts: [],
590
+ });
591
+ const result = await handleOrchestrate({
592
+ projectId: 'proj-1',
593
+ action: 'lock',
594
+ sessionId: 'agent-1',
595
+ specId: 'SPEC-001',
596
+ });
597
+ const data = JSON.parse(result.content[0].text);
598
+ expect(data.granted).toBe(true);
599
+ });
600
+ // --- Coverage for lock: different resource types (config, knowledge) ---
601
+ it('should detect config resource type for .json files', async () => {
602
+ mockReadJson.mockResolvedValue({
603
+ sessions: [{
604
+ sessionId: 'agent-1',
605
+ agentPlatform: 'custom',
606
+ projectId: 'proj-1',
607
+ activeSpecs: [],
608
+ lockedResources: [],
609
+ startedAt: '2025-01-01T00:00:00Z',
610
+ lastActivity: new Date().toISOString(),
611
+ status: 'active',
612
+ }],
613
+ locks: [],
614
+ conflicts: [],
615
+ });
616
+ const result = await handleOrchestrate({
617
+ projectId: 'proj-1',
618
+ action: 'lock',
619
+ sessionId: 'agent-1',
620
+ resourcePath: 'config/settings.json',
621
+ });
622
+ const data = JSON.parse(result.content[0].text);
623
+ expect(data.granted).toBe(true);
624
+ });
625
+ it('should detect knowledge resource type', async () => {
626
+ mockReadJson.mockResolvedValue({
627
+ sessions: [{
628
+ sessionId: 'agent-1',
629
+ agentPlatform: 'custom',
630
+ projectId: 'proj-1',
631
+ activeSpecs: [],
632
+ lockedResources: [],
633
+ startedAt: '2025-01-01T00:00:00Z',
634
+ lastActivity: new Date().toISOString(),
635
+ status: 'active',
636
+ }],
637
+ locks: [],
638
+ conflicts: [],
639
+ });
640
+ const result = await handleOrchestrate({
641
+ projectId: 'proj-1',
642
+ action: 'lock',
643
+ sessionId: 'agent-1',
644
+ resourcePath: 'data/knowledge/base.json',
645
+ });
646
+ const data = JSON.parse(result.content[0].text);
647
+ expect(data.granted).toBe(true);
648
+ });
649
+ // --- Coverage for pruneExpiredLocks ---
650
+ it('should prune expired locks from state', async () => {
651
+ const expiredTime = new Date(Date.now() - 3600000).toISOString();
652
+ mockReadJson.mockResolvedValue({
653
+ sessions: [{
654
+ sessionId: 'agent-1',
655
+ agentPlatform: 'custom',
656
+ projectId: 'proj-1',
657
+ activeSpecs: [],
658
+ lockedResources: [{
659
+ type: 'file',
660
+ resourceId: 'src/old.ts',
661
+ lockedBy: 'agent-1',
662
+ lockedAt: '2025-01-01T00:00:00Z',
663
+ reason: 'Working',
664
+ expiresAt: expiredTime,
665
+ }],
666
+ startedAt: '2025-01-01T00:00:00Z',
667
+ lastActivity: new Date().toISOString(),
668
+ status: 'active',
669
+ }],
670
+ locks: [{
671
+ type: 'file',
672
+ resourceId: 'src/old.ts',
673
+ lockedBy: 'agent-1',
674
+ lockedAt: '2025-01-01T00:00:00Z',
675
+ reason: 'Working',
676
+ expiresAt: expiredTime,
677
+ }],
678
+ conflicts: [],
679
+ });
680
+ const result = await handleOrchestrate({
681
+ projectId: 'proj-1',
682
+ action: 'status',
683
+ });
684
+ const data = JSON.parse(result.content[0].text);
685
+ expect(data.activeLocks).toBe(0);
686
+ });
687
+ // --- Coverage for pruneIdleSessions ---
688
+ it('should mark idle sessions as disconnected', async () => {
689
+ const veryOldTime = new Date(Date.now() - 20 * 60 * 1000).toISOString(); // 20 min ago
690
+ mockReadJson.mockResolvedValue({
691
+ sessions: [{
692
+ sessionId: 'agent-1',
693
+ agentPlatform: 'custom',
694
+ projectId: 'proj-1',
695
+ activeSpecs: [],
696
+ lockedResources: [],
697
+ startedAt: '2025-01-01T00:00:00Z',
698
+ lastActivity: veryOldTime,
699
+ status: 'active',
700
+ }, {
701
+ sessionId: 'agent-2',
702
+ agentPlatform: 'custom',
703
+ projectId: 'proj-1',
704
+ activeSpecs: [],
705
+ lockedResources: [],
706
+ startedAt: '2025-01-01T00:00:00Z',
707
+ lastActivity: veryOldTime,
708
+ status: 'idle',
709
+ }],
710
+ locks: [],
711
+ conflicts: [],
712
+ });
713
+ const result = await handleOrchestrate({
714
+ projectId: 'proj-1',
715
+ action: 'status',
716
+ });
717
+ const data = JSON.parse(result.content[0].text);
718
+ expect(data.disconnectedSessions).toBe(2);
719
+ expect(data.activeSessions).toBe(0);
720
+ });
721
+ // --- Coverage for isLockExpired with no expiresAt ---
722
+ it('should not consider lock expired when expiresAt is undefined', async () => {
723
+ mockReadJson.mockResolvedValue({
724
+ sessions: [{
725
+ sessionId: 'agent-1',
726
+ agentPlatform: 'custom',
727
+ projectId: 'proj-1',
728
+ activeSpecs: [],
729
+ lockedResources: [],
730
+ startedAt: '2025-01-01T00:00:00Z',
731
+ lastActivity: new Date().toISOString(),
732
+ status: 'active',
733
+ }],
734
+ locks: [{
735
+ type: 'file',
736
+ resourceId: 'src/auth.ts',
737
+ lockedBy: 'agent-2',
738
+ lockedAt: new Date().toISOString(),
739
+ reason: 'Working',
740
+ // No expiresAt -> never expires
741
+ }],
742
+ conflicts: [],
743
+ });
744
+ const result = await handleOrchestrate({
745
+ projectId: 'proj-1',
746
+ action: 'lock',
747
+ sessionId: 'agent-1',
748
+ resourcePath: 'src/auth.ts',
749
+ });
750
+ const data = JSON.parse(result.content[0].text);
751
+ // Lock should still be held by agent-2 (not expired)
752
+ expect(data.granted).toBe(false);
753
+ expect(data.message).toContain('indefinitely');
754
+ });
755
+ // --- Coverage for distribute: dependency conflicts ---
756
+ it('should detect dependency conflicts between agents', async () => {
757
+ mockReadJson.mockResolvedValue({
758
+ sessions: [
759
+ {
760
+ sessionId: 'agent-1',
761
+ agentPlatform: 'custom',
762
+ projectId: 'proj-1',
763
+ activeSpecs: ['SPEC-001'],
764
+ lockedResources: [],
765
+ startedAt: '2025-01-01T00:00:00Z',
766
+ lastActivity: new Date().toISOString(),
767
+ status: 'active',
768
+ },
769
+ {
770
+ sessionId: 'agent-2',
771
+ agentPlatform: 'custom',
772
+ projectId: 'proj-1',
773
+ activeSpecs: ['SPEC-002'],
774
+ lockedResources: [],
775
+ startedAt: '2025-01-01T00:00:00Z',
776
+ lastActivity: new Date().toISOString(),
777
+ status: 'active',
778
+ },
779
+ ],
780
+ locks: [],
781
+ conflicts: [],
782
+ });
783
+ mockListSpecs.mockResolvedValue([
784
+ {
785
+ id: 'SPEC-001',
786
+ status: 'implementing',
787
+ risk: 'medium',
788
+ scope: 'feature',
789
+ estimation: { devHours: 8 },
790
+ dependencies: [],
791
+ },
792
+ {
793
+ id: 'SPEC-002',
794
+ status: 'implementing',
795
+ risk: 'critical',
796
+ scope: 'architectural',
797
+ estimation: { devHours: 16 },
798
+ dependencies: ['SPEC-001'], // depends on SPEC-001
799
+ },
800
+ ]);
801
+ const result = await handleOrchestrate({
802
+ projectId: 'proj-1',
803
+ action: 'distribute',
804
+ });
805
+ const data = JSON.parse(result.content[0].text);
806
+ expect(data.conflicts).toBeDefined();
807
+ expect(data.conflicts.length).toBeGreaterThan(0);
808
+ expect(data.conflicts[0].type).toBe('dependency-conflict');
809
+ });
810
+ // --- Coverage for distribute: blocked specs ---
811
+ it('should mark specs as blocked when dependencies not done', async () => {
812
+ mockReadJson.mockResolvedValue({
813
+ sessions: [{
814
+ sessionId: 'agent-1',
815
+ agentPlatform: 'custom',
816
+ projectId: 'proj-1',
817
+ activeSpecs: [],
818
+ lockedResources: [],
819
+ startedAt: '2025-01-01T00:00:00Z',
820
+ lastActivity: new Date().toISOString(),
821
+ status: 'active',
822
+ }],
823
+ locks: [],
824
+ conflicts: [],
825
+ });
826
+ mockListSpecs.mockResolvedValue([
827
+ {
828
+ id: 'SPEC-001',
829
+ status: 'implementing',
830
+ risk: 'medium',
831
+ scope: 'feature',
832
+ estimation: { devHours: 8 },
833
+ dependencies: [],
834
+ },
835
+ {
836
+ id: 'SPEC-002',
837
+ status: 'approved',
838
+ risk: 'low',
839
+ scope: 'feature',
840
+ estimation: { devHours: 4 },
841
+ dependencies: ['SPEC-001'],
842
+ },
843
+ ]);
844
+ const result = await handleOrchestrate({
845
+ projectId: 'proj-1',
846
+ action: 'distribute',
847
+ });
848
+ const data = JSON.parse(result.content[0].text);
849
+ const blockedTask = data.distribution.find((t) => t.specId === 'SPEC-002');
850
+ expect(blockedTask.status).toBe('blocked');
851
+ expect(blockedTask.blockedBy).toContain('SPEC-001');
852
+ });
853
+ // --- Coverage for register: re-activate with new specId ---
854
+ it('should add specId when re-activating existing session', async () => {
855
+ mockReadJson.mockResolvedValue({
856
+ sessions: [{
857
+ sessionId: 'agent-1',
858
+ agentPlatform: 'custom',
859
+ projectId: 'proj-1',
860
+ activeSpecs: ['SPEC-001'],
861
+ lockedResources: [],
862
+ startedAt: '2025-01-01T00:00:00Z',
863
+ lastActivity: '2025-01-01T00:00:00Z',
864
+ status: 'disconnected',
865
+ }],
866
+ locks: [],
867
+ conflicts: [],
868
+ });
869
+ const result = await handleOrchestrate({
870
+ projectId: 'proj-1',
871
+ action: 'register',
872
+ sessionId: 'agent-1',
873
+ specId: 'SPEC-002',
874
+ });
875
+ const data = JSON.parse(result.content[0].text);
876
+ expect(data.message).toContain('Re-activated');
877
+ const savedState = mockWriteJson.mock.calls[0][1];
878
+ expect(savedState.sessions[0].activeSpecs).toContain('SPEC-002');
879
+ });
880
+ // --- Coverage for unlock using specId ---
881
+ it('should unlock using specId as resource identifier', async () => {
882
+ mockReadJson.mockResolvedValue({
883
+ sessions: [{
884
+ sessionId: 'agent-1',
885
+ agentPlatform: 'custom',
886
+ projectId: 'proj-1',
887
+ activeSpecs: ['SPEC-001'],
888
+ lockedResources: [{ resourceId: 'SPEC-001', type: 'spec', lockedBy: 'agent-1' }],
889
+ startedAt: '2025-01-01T00:00:00Z',
890
+ lastActivity: new Date().toISOString(),
891
+ status: 'active',
892
+ }],
893
+ locks: [{
894
+ type: 'spec',
895
+ resourceId: 'SPEC-001',
896
+ lockedBy: 'agent-1',
897
+ lockedAt: new Date().toISOString(),
898
+ reason: 'Working',
899
+ }],
900
+ conflicts: [],
901
+ });
902
+ const result = await handleOrchestrate({
903
+ projectId: 'proj-1',
904
+ action: 'unlock',
905
+ sessionId: 'agent-1',
906
+ specId: 'SPEC-001',
907
+ });
908
+ const data = JSON.parse(result.content[0].text);
909
+ expect(data.granted).toBe(true);
910
+ });
911
+ // --- Coverage for defensive guard: existingSession after findIndex (line 109) ---
912
+ it('should throw when re-activating session but indexed access returns undefined', async () => {
913
+ const sessions = new Proxy([], {
914
+ get(target, prop) {
915
+ if (prop === 'findIndex') {
916
+ return () => 0;
917
+ }
918
+ return Reflect.get(target, prop);
919
+ },
920
+ });
921
+ mockReadJson.mockResolvedValue({ sessions, locks: [], conflicts: [] });
922
+ const result = await handleOrchestrate({
923
+ projectId: 'proj-1',
924
+ action: 'register',
925
+ sessionId: 'ghost-agent',
926
+ });
927
+ expect(result.isError).toBe(true);
928
+ expect(result.content[0].text).toContain('unexpectedly missing');
929
+ });
930
+ // --- Coverage for sort branch: a not blocked, b blocked -> -1 (line 524) ---
931
+ it('should sort blocked specs after non-blocked in distribute', async () => {
932
+ mockReadJson.mockResolvedValue({
933
+ sessions: [{
934
+ sessionId: 'agent-1',
935
+ agentPlatform: 'custom',
936
+ projectId: 'proj-1',
937
+ activeSpecs: [],
938
+ lockedResources: [],
939
+ startedAt: '2025-01-01T00:00:00Z',
940
+ lastActivity: new Date().toISOString(),
941
+ status: 'active',
942
+ }],
943
+ locks: [],
944
+ conflicts: [],
945
+ });
946
+ // SPEC-003 is blocked (depends on SPEC-001 which is not done)
947
+ // SPEC-002 is available (no deps)
948
+ // SPEC-001 is available (no deps)
949
+ // Ordering forces sort to compare blocked vs non-blocked in both directions
950
+ mockListSpecs.mockResolvedValue([
951
+ {
952
+ id: 'SPEC-003',
953
+ status: 'approved',
954
+ risk: 'low',
955
+ scope: 'feature',
956
+ estimation: { devHours: 2 },
957
+ dependencies: ['SPEC-001'],
958
+ },
959
+ {
960
+ id: 'SPEC-002',
961
+ status: 'approved',
962
+ risk: 'medium',
963
+ scope: 'feature',
964
+ estimation: { devHours: 4 },
965
+ dependencies: [],
966
+ },
967
+ {
968
+ id: 'SPEC-001',
969
+ status: 'draft',
970
+ risk: 'high',
971
+ scope: 'cross-module',
972
+ estimation: { devHours: 8 },
973
+ dependencies: [],
974
+ },
975
+ ]);
976
+ const result = await handleOrchestrate({
977
+ projectId: 'proj-1',
978
+ action: 'distribute',
979
+ });
980
+ const data = JSON.parse(result.content[0].text);
981
+ // Blocked items should come last
982
+ const lastItem = data.distribution[data.distribution.length - 1];
983
+ expect(lastItem.status).toBe('blocked');
984
+ expect(lastItem.specId).toBe('SPEC-003');
985
+ });
986
+ // --- Coverage for alreadyReported check in distribute (line 543) ---
987
+ it('should not duplicate dependency conflict reports between same agent pair', async () => {
988
+ mockReadJson.mockResolvedValue({
989
+ sessions: [
990
+ {
991
+ sessionId: 'agent-1',
992
+ agentPlatform: 'custom',
993
+ projectId: 'proj-1',
994
+ activeSpecs: ['SPEC-001', 'SPEC-003'],
995
+ lockedResources: [],
996
+ startedAt: '2025-01-01T00:00:00Z',
997
+ lastActivity: new Date().toISOString(),
998
+ status: 'active',
999
+ },
1000
+ {
1001
+ sessionId: 'agent-2',
1002
+ agentPlatform: 'custom',
1003
+ projectId: 'proj-1',
1004
+ activeSpecs: ['SPEC-002'],
1005
+ lockedResources: [],
1006
+ startedAt: '2025-01-01T00:00:00Z',
1007
+ lastActivity: new Date().toISOString(),
1008
+ status: 'active',
1009
+ },
1010
+ ],
1011
+ locks: [],
1012
+ conflicts: [],
1013
+ });
1014
+ // Both SPEC-002 and SPEC-003 depend on SPEC-001
1015
+ // SPEC-002 is worked on by agent-2 and depends on SPEC-001 (agent-1) -> conflict
1016
+ // SPEC-003 is worked on by agent-1 and depends on SPEC-001 (agent-1) -> same agent, no conflict
1017
+ // But we need duplicate: two specs causing the same agent-pair conflict on same resource
1018
+ // SPEC-002 depends on SPEC-001 -> conflict between agent-2 and agent-1 for resource SPEC-002
1019
+ // SPEC-002 also depends on SPEC-003 -> conflict between agent-2 and agent-1 for resource SPEC-002 (duplicate!)
1020
+ mockListSpecs.mockResolvedValue([
1021
+ {
1022
+ id: 'SPEC-001',
1023
+ status: 'implementing',
1024
+ risk: 'medium',
1025
+ scope: 'feature',
1026
+ estimation: { devHours: 8 },
1027
+ dependencies: [],
1028
+ },
1029
+ {
1030
+ id: 'SPEC-002',
1031
+ status: 'implementing',
1032
+ risk: 'medium',
1033
+ scope: 'feature',
1034
+ estimation: { devHours: 8 },
1035
+ dependencies: ['SPEC-001', 'SPEC-003'],
1036
+ },
1037
+ {
1038
+ id: 'SPEC-003',
1039
+ status: 'implementing',
1040
+ risk: 'medium',
1041
+ scope: 'feature',
1042
+ estimation: { devHours: 4 },
1043
+ dependencies: [],
1044
+ },
1045
+ ]);
1046
+ const result = await handleOrchestrate({
1047
+ projectId: 'proj-1',
1048
+ action: 'distribute',
1049
+ });
1050
+ const data = JSON.parse(result.content[0].text);
1051
+ // Should have conflicts, but no duplicates for the same resource+agents combo
1052
+ expect(data.conflicts).toBeDefined();
1053
+ const spec002Conflicts = data.conflicts.filter((c) => c.resource === 'SPEC-002');
1054
+ // Both dependencies (SPEC-001, SPEC-003) are on agent-1, but only 1 conflict per resource
1055
+ expect(spec002Conflicts.length).toBeLessThanOrEqual(2);
1056
+ // The alreadyReported check prevents duplicate agent-pair+resource combos
1057
+ const agentPairs = spec002Conflicts.map((c) => [...c.agents].sort().join(','));
1058
+ const uniquePairs = new Set(agentPairs);
1059
+ expect(uniquePairs.size).toBe(agentPairs.length);
1060
+ });
1061
+ // --- Coverage for status with idle and disconnected sessions ---
1062
+ it('should correctly count idle and disconnected sessions', async () => {
1063
+ const veryOldTime = new Date(Date.now() - 20 * 60 * 1000).toISOString();
1064
+ mockReadJson.mockResolvedValue({
1065
+ sessions: [
1066
+ {
1067
+ sessionId: 'agent-1',
1068
+ agentPlatform: 'custom',
1069
+ projectId: 'proj-1',
1070
+ activeSpecs: [],
1071
+ lockedResources: [],
1072
+ startedAt: '2025-01-01T00:00:00Z',
1073
+ lastActivity: new Date().toISOString(),
1074
+ status: 'active',
1075
+ },
1076
+ {
1077
+ sessionId: 'agent-2',
1078
+ agentPlatform: 'custom',
1079
+ projectId: 'proj-1',
1080
+ activeSpecs: [],
1081
+ lockedResources: [],
1082
+ startedAt: '2025-01-01T00:00:00Z',
1083
+ lastActivity: veryOldTime,
1084
+ status: 'idle',
1085
+ },
1086
+ {
1087
+ sessionId: 'agent-3',
1088
+ agentPlatform: 'custom',
1089
+ projectId: 'proj-1',
1090
+ activeSpecs: [],
1091
+ lockedResources: [],
1092
+ startedAt: '2025-01-01T00:00:00Z',
1093
+ lastActivity: '2020-01-01T00:00:00Z',
1094
+ status: 'disconnected',
1095
+ },
1096
+ ],
1097
+ locks: [],
1098
+ conflicts: [{
1099
+ type: 'spec-overlap',
1100
+ agents: ['agent-1', 'agent-2'],
1101
+ resource: 'SPEC-001',
1102
+ resolution: 'Coordinate',
1103
+ severity: 'warning',
1104
+ }],
1105
+ });
1106
+ const result = await handleOrchestrate({
1107
+ projectId: 'proj-1',
1108
+ action: 'status',
1109
+ });
1110
+ const data = JSON.parse(result.content[0].text);
1111
+ expect(data.totalSessions).toBe(3);
1112
+ expect(data.activeSessions).toBe(1);
1113
+ expect(data.disconnectedSessions).toBeGreaterThanOrEqual(1);
1114
+ expect(data.unresolvedConflicts).toBe(1);
1115
+ });
1116
+ });
1117
+ //# sourceMappingURL=orchestrate.test.js.map