@kynetic-ai/spec 0.11.0 → 0.12.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 (501) hide show
  1. package/README.md +55 -455
  2. package/dist/agent-runtime/bootstrap.d.ts +31 -0
  3. package/dist/agent-runtime/bootstrap.d.ts.map +1 -0
  4. package/dist/agent-runtime/bootstrap.js +302 -0
  5. package/dist/agent-runtime/bootstrap.js.map +1 -0
  6. package/dist/agent-runtime/dispatch.d.ts +119 -10
  7. package/dist/agent-runtime/dispatch.d.ts.map +1 -1
  8. package/dist/agent-runtime/dispatch.js +1154 -219
  9. package/dist/agent-runtime/dispatch.js.map +1 -1
  10. package/dist/agent-runtime/invocation.d.ts +28 -1
  11. package/dist/agent-runtime/invocation.d.ts.map +1 -1
  12. package/dist/agent-runtime/invocation.js +171 -59
  13. package/dist/agent-runtime/invocation.js.map +1 -1
  14. package/dist/agent-runtime/prompts.d.ts +9 -0
  15. package/dist/agent-runtime/prompts.d.ts.map +1 -1
  16. package/dist/agent-runtime/prompts.js +42 -7
  17. package/dist/agent-runtime/prompts.js.map +1 -1
  18. package/dist/agent-runtime/session-event-accumulator.d.ts +83 -0
  19. package/dist/agent-runtime/session-event-accumulator.d.ts.map +1 -0
  20. package/dist/agent-runtime/session-event-accumulator.js +203 -0
  21. package/dist/agent-runtime/session-event-accumulator.js.map +1 -0
  22. package/dist/agent-runtime/session-event-types.d.ts +67 -0
  23. package/dist/agent-runtime/session-event-types.d.ts.map +1 -0
  24. package/dist/agent-runtime/session-event-types.js +13 -0
  25. package/dist/agent-runtime/session-event-types.js.map +1 -0
  26. package/dist/agent-runtime/workspace.d.ts +244 -0
  27. package/dist/agent-runtime/workspace.d.ts.map +1 -0
  28. package/dist/agent-runtime/workspace.js +2025 -0
  29. package/dist/agent-runtime/workspace.js.map +1 -0
  30. package/dist/agents/adapters.d.ts.map +1 -1
  31. package/dist/agents/adapters.js +58 -13
  32. package/dist/agents/adapters.js.map +1 -1
  33. package/dist/agents/spawner.d.ts +8 -0
  34. package/dist/agents/spawner.d.ts.map +1 -1
  35. package/dist/agents/spawner.js +25 -3
  36. package/dist/agents/spawner.js.map +1 -1
  37. package/dist/cli/batch-exec.js +1 -1
  38. package/dist/cli/batch-exec.js.map +1 -1
  39. package/dist/cli/command-annotations.d.ts +15 -3
  40. package/dist/cli/command-annotations.d.ts.map +1 -1
  41. package/dist/cli/command-annotations.js +23 -3
  42. package/dist/cli/command-annotations.js.map +1 -1
  43. package/dist/cli/commands/agent.d.ts +2 -0
  44. package/dist/cli/commands/agent.d.ts.map +1 -1
  45. package/dist/cli/commands/agent.js +144 -27
  46. package/dist/cli/commands/agent.js.map +1 -1
  47. package/dist/cli/commands/agents.d.ts.map +1 -1
  48. package/dist/cli/commands/agents.js +5 -5
  49. package/dist/cli/commands/agents.js.map +1 -1
  50. package/dist/cli/commands/derive.d.ts.map +1 -1
  51. package/dist/cli/commands/derive.js +118 -3
  52. package/dist/cli/commands/derive.js.map +1 -1
  53. package/dist/cli/commands/guard.d.ts.map +1 -1
  54. package/dist/cli/commands/guard.js +8 -6
  55. package/dist/cli/commands/guard.js.map +1 -1
  56. package/dist/cli/commands/index.d.ts +1 -0
  57. package/dist/cli/commands/index.d.ts.map +1 -1
  58. package/dist/cli/commands/index.js +1 -0
  59. package/dist/cli/commands/index.js.map +1 -1
  60. package/dist/cli/commands/init.d.ts.map +1 -1
  61. package/dist/cli/commands/init.js +20 -0
  62. package/dist/cli/commands/init.js.map +1 -1
  63. package/dist/cli/commands/item.d.ts.map +1 -1
  64. package/dist/cli/commands/item.js +205 -47
  65. package/dist/cli/commands/item.js.map +1 -1
  66. package/dist/cli/commands/log.d.ts.map +1 -1
  67. package/dist/cli/commands/log.js +24 -10
  68. package/dist/cli/commands/log.js.map +1 -1
  69. package/dist/cli/commands/plan-import.d.ts +3 -3
  70. package/dist/cli/commands/plan-import.d.ts.map +1 -1
  71. package/dist/cli/commands/plan-import.js +213 -528
  72. package/dist/cli/commands/plan-import.js.map +1 -1
  73. package/dist/cli/commands/plan.d.ts.map +1 -1
  74. package/dist/cli/commands/plan.js +533 -83
  75. package/dist/cli/commands/plan.js.map +1 -1
  76. package/dist/cli/commands/review.d.ts +14 -0
  77. package/dist/cli/commands/review.d.ts.map +1 -0
  78. package/dist/cli/commands/review.js +1142 -0
  79. package/dist/cli/commands/review.js.map +1 -0
  80. package/dist/cli/commands/serve.d.ts +1 -0
  81. package/dist/cli/commands/serve.d.ts.map +1 -1
  82. package/dist/cli/commands/serve.js +33 -10
  83. package/dist/cli/commands/serve.js.map +1 -1
  84. package/dist/cli/commands/session/checkpoint.d.ts +2 -4
  85. package/dist/cli/commands/session/checkpoint.d.ts.map +1 -1
  86. package/dist/cli/commands/session/checkpoint.js +6 -107
  87. package/dist/cli/commands/session/checkpoint.js.map +1 -1
  88. package/dist/cli/commands/session/commands.d.ts.map +1 -1
  89. package/dist/cli/commands/session/commands.js +33 -23
  90. package/dist/cli/commands/session/commands.js.map +1 -1
  91. package/dist/cli/commands/session/compact.js +4 -4
  92. package/dist/cli/commands/session/compact.js.map +1 -1
  93. package/dist/cli/commands/session/create.js +2 -2
  94. package/dist/cli/commands/session/create.js.map +1 -1
  95. package/dist/cli/commands/session/format.d.ts.map +1 -1
  96. package/dist/cli/commands/session/format.js +1 -6
  97. package/dist/cli/commands/session/format.js.map +1 -1
  98. package/dist/cli/commands/session/log.d.ts +32 -7
  99. package/dist/cli/commands/session/log.d.ts.map +1 -1
  100. package/dist/cli/commands/session/log.js +166 -60
  101. package/dist/cli/commands/session/log.js.map +1 -1
  102. package/dist/cli/commands/session/migrate.d.ts +9 -0
  103. package/dist/cli/commands/session/migrate.d.ts.map +1 -0
  104. package/dist/cli/commands/session/migrate.js +46 -0
  105. package/dist/cli/commands/session/migrate.js.map +1 -0
  106. package/dist/cli/commands/session/stale-close.d.ts.map +1 -1
  107. package/dist/cli/commands/session/stale-close.js +5 -8
  108. package/dist/cli/commands/session/stale-close.js.map +1 -1
  109. package/dist/cli/commands/session/types.d.ts +1 -1
  110. package/dist/cli/commands/session/types.d.ts.map +1 -1
  111. package/dist/cli/commands/setup.d.ts +2 -2
  112. package/dist/cli/commands/setup.d.ts.map +1 -1
  113. package/dist/cli/commands/setup.js +287 -257
  114. package/dist/cli/commands/setup.js.map +1 -1
  115. package/dist/cli/commands/shadow.d.ts.map +1 -1
  116. package/dist/cli/commands/shadow.js +147 -31
  117. package/dist/cli/commands/shadow.js.map +1 -1
  118. package/dist/cli/commands/skill-crud.d.ts +7 -0
  119. package/dist/cli/commands/skill-crud.d.ts.map +1 -1
  120. package/dist/cli/commands/skill-crud.js +41 -18
  121. package/dist/cli/commands/skill-crud.js.map +1 -1
  122. package/dist/cli/commands/skill-diff.d.ts.map +1 -1
  123. package/dist/cli/commands/skill-diff.js +29 -3
  124. package/dist/cli/commands/skill-diff.js.map +1 -1
  125. package/dist/cli/commands/skill-install.d.ts.map +1 -1
  126. package/dist/cli/commands/skill-install.js +5 -4
  127. package/dist/cli/commands/skill-install.js.map +1 -1
  128. package/dist/cli/commands/task.d.ts.map +1 -1
  129. package/dist/cli/commands/task.js +359 -49
  130. package/dist/cli/commands/task.js.map +1 -1
  131. package/dist/cli/commands/trait.d.ts.map +1 -1
  132. package/dist/cli/commands/trait.js +5 -27
  133. package/dist/cli/commands/trait.js.map +1 -1
  134. package/dist/cli/commands/validate.d.ts.map +1 -1
  135. package/dist/cli/commands/validate.js +113 -52
  136. package/dist/cli/commands/validate.js.map +1 -1
  137. package/dist/cli/index.d.ts.map +1 -1
  138. package/dist/cli/index.js +69 -2
  139. package/dist/cli/index.js.map +1 -1
  140. package/dist/cli/output.d.ts +26 -0
  141. package/dist/cli/output.d.ts.map +1 -1
  142. package/dist/cli/output.js +108 -1
  143. package/dist/cli/output.js.map +1 -1
  144. package/dist/cli/sync-mode.d.ts +44 -0
  145. package/dist/cli/sync-mode.d.ts.map +1 -0
  146. package/dist/cli/sync-mode.js +64 -0
  147. package/dist/cli/sync-mode.js.map +1 -0
  148. package/dist/daemon/middleware/project-context.ts +25 -7
  149. package/dist/daemon/project-context.ts +18 -0
  150. package/dist/daemon/routes/agent-dispatch.ts +99 -22
  151. package/dist/daemon/routes/aggregation.ts +184 -0
  152. package/dist/daemon/routes/inbox.ts +5 -0
  153. package/dist/daemon/routes/items.ts +145 -0
  154. package/dist/daemon/routes/meta.ts +1 -1
  155. package/dist/daemon/routes/projects.ts +28 -6
  156. package/dist/daemon/routes/ref-resolution.ts +119 -0
  157. package/dist/daemon/routes/refs.ts +42 -0
  158. package/dist/daemon/routes/session-related.ts +140 -0
  159. package/dist/daemon/routes/sessions.ts +420 -19
  160. package/dist/daemon/routes/tasks.ts +62 -5
  161. package/dist/daemon/routes/triage.ts +40 -1
  162. package/dist/daemon/server.ts +143 -49
  163. package/dist/daemon/session-sync.ts +11 -0
  164. package/dist/daemon/shadow-sync.ts +11 -0
  165. package/dist/daemon/watcher.ts +56 -5
  166. package/dist/daemon/websocket/project-resolution.ts +77 -0
  167. package/dist/export/json.d.ts.map +1 -1
  168. package/dist/export/json.js +104 -1
  169. package/dist/export/json.js.map +1 -1
  170. package/dist/export/types.d.ts +52 -1
  171. package/dist/export/types.d.ts.map +1 -1
  172. package/dist/index.d.ts +1 -0
  173. package/dist/index.d.ts.map +1 -1
  174. package/dist/index.js +1 -0
  175. package/dist/index.js.map +1 -1
  176. package/dist/parser/agent-detection.d.ts +1 -1
  177. package/dist/parser/agent-detection.d.ts.map +1 -1
  178. package/dist/parser/agent-detection.js +10 -0
  179. package/dist/parser/agent-detection.js.map +1 -1
  180. package/dist/parser/config.d.ts +397 -2
  181. package/dist/parser/config.d.ts.map +1 -1
  182. package/dist/parser/config.js +125 -3
  183. package/dist/parser/config.js.map +1 -1
  184. package/dist/parser/dispatch-workspaces.d.ts +18 -0
  185. package/dist/parser/dispatch-workspaces.d.ts.map +1 -0
  186. package/dist/parser/dispatch-workspaces.js +209 -0
  187. package/dist/parser/dispatch-workspaces.js.map +1 -0
  188. package/dist/parser/doctor.d.ts.map +1 -1
  189. package/dist/parser/doctor.js +27 -8
  190. package/dist/parser/doctor.js.map +1 -1
  191. package/dist/parser/file-lock.d.ts.map +1 -1
  192. package/dist/parser/file-lock.js +9 -2
  193. package/dist/parser/file-lock.js.map +1 -1
  194. package/dist/parser/index.d.ts +6 -0
  195. package/dist/parser/index.d.ts.map +1 -1
  196. package/dist/parser/index.js +6 -0
  197. package/dist/parser/index.js.map +1 -1
  198. package/dist/parser/plans.d.ts.map +1 -1
  199. package/dist/parser/plans.js +1 -0
  200. package/dist/parser/plans.js.map +1 -1
  201. package/dist/parser/refs.d.ts +8 -1
  202. package/dist/parser/refs.d.ts.map +1 -1
  203. package/dist/parser/refs.js +27 -1
  204. package/dist/parser/refs.js.map +1 -1
  205. package/dist/parser/review-operations.d.ts +72 -0
  206. package/dist/parser/review-operations.d.ts.map +1 -0
  207. package/dist/parser/review-operations.js +185 -0
  208. package/dist/parser/review-operations.js.map +1 -0
  209. package/dist/parser/review-task-integration.d.ts +78 -0
  210. package/dist/parser/review-task-integration.d.ts.map +1 -0
  211. package/dist/parser/review-task-integration.js +173 -0
  212. package/dist/parser/review-task-integration.js.map +1 -0
  213. package/dist/parser/review-threads.d.ts +101 -0
  214. package/dist/parser/review-threads.d.ts.map +1 -0
  215. package/dist/parser/review-threads.js +222 -0
  216. package/dist/parser/review-threads.js.map +1 -0
  217. package/dist/parser/review-validation.d.ts +69 -0
  218. package/dist/parser/review-validation.d.ts.map +1 -0
  219. package/dist/parser/review-validation.js +207 -0
  220. package/dist/parser/review-validation.js.map +1 -0
  221. package/dist/parser/reviews.d.ts +58 -0
  222. package/dist/parser/reviews.d.ts.map +1 -0
  223. package/dist/parser/reviews.js +230 -0
  224. package/dist/parser/reviews.js.map +1 -0
  225. package/dist/parser/session-branch.d.ts +91 -0
  226. package/dist/parser/session-branch.d.ts.map +1 -0
  227. package/dist/parser/session-branch.js +565 -0
  228. package/dist/parser/session-branch.js.map +1 -0
  229. package/dist/parser/session-sync-scheduler.d.ts +53 -0
  230. package/dist/parser/session-sync-scheduler.d.ts.map +1 -0
  231. package/dist/parser/session-sync-scheduler.js +100 -0
  232. package/dist/parser/session-sync-scheduler.js.map +1 -0
  233. package/dist/parser/setup-status.d.ts +7 -1
  234. package/dist/parser/setup-status.d.ts.map +1 -1
  235. package/dist/parser/setup-status.js +104 -39
  236. package/dist/parser/setup-status.js.map +1 -1
  237. package/dist/parser/shadow-sync-scheduler.d.ts +71 -0
  238. package/dist/parser/shadow-sync-scheduler.d.ts.map +1 -0
  239. package/dist/parser/shadow-sync-scheduler.js +139 -0
  240. package/dist/parser/shadow-sync-scheduler.js.map +1 -0
  241. package/dist/parser/shadow.d.ts +121 -14
  242. package/dist/parser/shadow.d.ts.map +1 -1
  243. package/dist/parser/shadow.js +752 -27
  244. package/dist/parser/shadow.js.map +1 -1
  245. package/dist/parser/skill-render.d.ts +24 -0
  246. package/dist/parser/skill-render.d.ts.map +1 -1
  247. package/dist/parser/skill-render.js +98 -26
  248. package/dist/parser/skill-render.js.map +1 -1
  249. package/dist/parser/validate.d.ts +43 -3
  250. package/dist/parser/validate.d.ts.map +1 -1
  251. package/dist/parser/validate.js +204 -30
  252. package/dist/parser/validate.js.map +1 -1
  253. package/dist/parser/yaml.d.ts +47 -11
  254. package/dist/parser/yaml.d.ts.map +1 -1
  255. package/dist/parser/yaml.js +329 -149
  256. package/dist/parser/yaml.js.map +1 -1
  257. package/dist/review/checks.d.ts +97 -0
  258. package/dist/review/checks.d.ts.map +1 -0
  259. package/dist/review/checks.js +175 -0
  260. package/dist/review/checks.js.map +1 -0
  261. package/dist/review/index.d.ts +3 -0
  262. package/dist/review/index.d.ts.map +1 -0
  263. package/dist/review/index.js +3 -0
  264. package/dist/review/index.js.map +1 -0
  265. package/dist/review/subject-bindings.d.ts +83 -0
  266. package/dist/review/subject-bindings.d.ts.map +1 -0
  267. package/dist/review/subject-bindings.js +175 -0
  268. package/dist/review/subject-bindings.js.map +1 -0
  269. package/dist/schema/common.d.ts +26 -0
  270. package/dist/schema/common.d.ts.map +1 -1
  271. package/dist/schema/common.js +13 -0
  272. package/dist/schema/common.js.map +1 -1
  273. package/dist/schema/dispatch-workspace.d.ts +2643 -0
  274. package/dist/schema/dispatch-workspace.d.ts.map +1 -0
  275. package/dist/schema/dispatch-workspace.js +187 -0
  276. package/dist/schema/dispatch-workspace.js.map +1 -0
  277. package/dist/schema/inbox.d.ts +8 -8
  278. package/dist/schema/index.d.ts +2 -0
  279. package/dist/schema/index.d.ts.map +1 -1
  280. package/dist/schema/index.js +2 -0
  281. package/dist/schema/index.js.map +1 -1
  282. package/dist/schema/meta.d.ts +648 -116
  283. package/dist/schema/meta.d.ts.map +1 -1
  284. package/dist/schema/meta.js +27 -0
  285. package/dist/schema/meta.js.map +1 -1
  286. package/dist/schema/plan.d.ts +30 -19
  287. package/dist/schema/plan.d.ts.map +1 -1
  288. package/dist/schema/plan.js +3 -1
  289. package/dist/schema/plan.js.map +1 -1
  290. package/dist/schema/review-records.d.ts +2676 -0
  291. package/dist/schema/review-records.d.ts.map +1 -0
  292. package/dist/schema/review-records.js +232 -0
  293. package/dist/schema/review-records.js.map +1 -0
  294. package/dist/schema/spec.d.ts +32 -14
  295. package/dist/schema/spec.d.ts.map +1 -1
  296. package/dist/schema/spec.js +5 -0
  297. package/dist/schema/spec.js.map +1 -1
  298. package/dist/schema/task.d.ts +187 -29
  299. package/dist/schema/task.d.ts.map +1 -1
  300. package/dist/schema/task.js +12 -2
  301. package/dist/schema/task.js.map +1 -1
  302. package/dist/schema/triage.d.ts +22 -22
  303. package/dist/sessions/cache.d.ts +119 -0
  304. package/dist/sessions/cache.d.ts.map +1 -0
  305. package/dist/sessions/cache.js +284 -0
  306. package/dist/sessions/cache.js.map +1 -0
  307. package/dist/sessions/index.d.ts +1 -0
  308. package/dist/sessions/index.d.ts.map +1 -1
  309. package/dist/sessions/index.js +2 -0
  310. package/dist/sessions/index.js.map +1 -1
  311. package/dist/sessions/legacy.d.ts +77 -0
  312. package/dist/sessions/legacy.d.ts.map +1 -0
  313. package/dist/sessions/legacy.js +146 -0
  314. package/dist/sessions/legacy.js.map +1 -0
  315. package/dist/sessions/store.d.ts +103 -73
  316. package/dist/sessions/store.d.ts.map +1 -1
  317. package/dist/sessions/store.js +335 -186
  318. package/dist/sessions/store.js.map +1 -1
  319. package/dist/sessions/types.d.ts +44 -16
  320. package/dist/sessions/types.d.ts.map +1 -1
  321. package/dist/sessions/types.js +11 -2
  322. package/dist/sessions/types.js.map +1 -1
  323. package/dist/strings/errors.d.ts +32 -0
  324. package/dist/strings/errors.d.ts.map +1 -1
  325. package/dist/strings/errors.js +17 -0
  326. package/dist/strings/errors.js.map +1 -1
  327. package/dist/strings/labels.d.ts +1 -0
  328. package/dist/strings/labels.d.ts.map +1 -1
  329. package/dist/strings/labels.js +1 -0
  330. package/dist/strings/labels.js.map +1 -1
  331. package/dist/utils/activity.d.ts +101 -0
  332. package/dist/utils/activity.d.ts.map +1 -0
  333. package/dist/utils/activity.js +408 -0
  334. package/dist/utils/activity.js.map +1 -0
  335. package/dist/utils/git.d.ts +31 -0
  336. package/dist/utils/git.d.ts.map +1 -1
  337. package/dist/utils/git.js +87 -0
  338. package/dist/utils/git.js.map +1 -1
  339. package/dist/utils/index.d.ts +2 -0
  340. package/dist/utils/index.d.ts.map +1 -1
  341. package/dist/utils/index.js +1 -0
  342. package/dist/utils/index.js.map +1 -1
  343. package/dist/web-ui/_app/immutable/assets/0.tmlwn-Ih.css +1 -0
  344. package/dist/web-ui/_app/immutable/assets/9.BwwJybWx.css +1 -0
  345. package/dist/web-ui/_app/immutable/chunks/2KqE8gtn.js +1 -0
  346. package/dist/web-ui/_app/immutable/chunks/70-t_QvE.js +1 -0
  347. package/dist/web-ui/_app/immutable/chunks/AiWQj974.js +1 -0
  348. package/dist/web-ui/_app/immutable/chunks/{CPPfDSei.js → B25nWFyA.js} +4 -4
  349. package/dist/web-ui/_app/immutable/chunks/{DBYE9jOd.js → B2bcA_Q_.js} +1 -1
  350. package/dist/web-ui/_app/immutable/chunks/B5e5HYyB.js +1 -0
  351. package/dist/web-ui/_app/immutable/chunks/B7-5z6eA.js +1 -0
  352. package/dist/web-ui/_app/immutable/chunks/B7bGmhK0.js +1 -0
  353. package/dist/web-ui/_app/immutable/chunks/{DzO4hlg9.js → B8tYZKAE.js} +1 -1
  354. package/dist/web-ui/_app/immutable/chunks/{B5LJFxqa.js → BFGAyJjD.js} +1 -1
  355. package/dist/web-ui/_app/immutable/chunks/BG0850zf.js +1 -0
  356. package/dist/web-ui/_app/immutable/chunks/{DAMmvwn4.js → BG8eSzAd.js} +1 -1
  357. package/dist/web-ui/_app/immutable/chunks/BIMxXS8I.js +1 -0
  358. package/dist/web-ui/_app/immutable/chunks/BSzL1fpU.js +1 -0
  359. package/dist/web-ui/_app/immutable/chunks/BYtjHfeq.js +1 -0
  360. package/dist/web-ui/_app/immutable/chunks/{DxCk-KHc.js → Bp5pFYXL.js} +1 -1
  361. package/dist/web-ui/_app/immutable/chunks/{B8a0xDxR.js → BsJFsuAT.js} +1 -1
  362. package/dist/web-ui/_app/immutable/chunks/BvpNHcD6.js +1 -0
  363. package/dist/web-ui/_app/immutable/chunks/BypqA25-.js +1 -0
  364. package/dist/web-ui/_app/immutable/chunks/{BVA9Exy-.js → C0w6WDm5.js} +1 -1
  365. package/dist/web-ui/_app/immutable/chunks/C5_PAZ0y.js +1 -0
  366. package/dist/web-ui/_app/immutable/chunks/CDRO15Iv.js +1 -0
  367. package/dist/web-ui/_app/immutable/chunks/CF1CoqD5.js +1 -0
  368. package/dist/web-ui/_app/immutable/chunks/CS2sa4_m.js +1 -0
  369. package/dist/web-ui/_app/immutable/chunks/{BJ0JX3ea.js → CWUQwB9H.js} +1 -1
  370. package/dist/web-ui/_app/immutable/chunks/CY5FDdSU.js +1 -0
  371. package/dist/web-ui/_app/immutable/chunks/C_7MTDoj.js +1 -0
  372. package/dist/web-ui/_app/immutable/chunks/{D3vxvonu.js → CaAJD3dl.js} +1 -1
  373. package/dist/web-ui/_app/immutable/chunks/{BP352uRn.js → ChB5iyEL.js} +1 -1
  374. package/dist/web-ui/_app/immutable/chunks/{pE6cYWlS.js → ChQD-6N8.js} +1 -1
  375. package/dist/web-ui/_app/immutable/chunks/{Eo4gF7ih.js → CqbsoCwA.js} +1 -1
  376. package/dist/web-ui/_app/immutable/chunks/DCeJW50p.js +1 -0
  377. package/dist/web-ui/_app/immutable/chunks/{Cncwi6fQ.js → DJtZNgcs.js} +1 -1
  378. package/dist/web-ui/_app/immutable/chunks/DKIeaprD.js +1 -0
  379. package/dist/web-ui/_app/immutable/chunks/DLd2uVIA.js +1 -0
  380. package/dist/web-ui/_app/immutable/chunks/{DjcCz-PU.js → DW_subyT.js} +2 -2
  381. package/dist/web-ui/_app/immutable/chunks/DbU6lVn0.js +1 -0
  382. package/dist/web-ui/_app/immutable/chunks/Dc7ZCC5m.js +1 -0
  383. package/dist/web-ui/_app/immutable/chunks/Dd5umPsk.js +2 -0
  384. package/dist/web-ui/_app/immutable/chunks/{BysXJlZb.js → Dg_zDpDS.js} +1 -1
  385. package/dist/web-ui/_app/immutable/chunks/Dgqu8Yuc.js +1 -0
  386. package/dist/web-ui/_app/immutable/chunks/DmxsPZTB.js +1 -0
  387. package/dist/web-ui/_app/immutable/chunks/DphTaFUB.js +1 -0
  388. package/dist/web-ui/_app/immutable/chunks/DqK4iHp0.js +1 -0
  389. package/dist/web-ui/_app/immutable/chunks/{D9QNBZM2.js → DqT6OH_u.js} +2 -2
  390. package/dist/web-ui/_app/immutable/chunks/Ds9I9wQb.js +1 -0
  391. package/dist/web-ui/_app/immutable/chunks/Du5ng3u4.js +1 -0
  392. package/dist/web-ui/_app/immutable/chunks/DxJw79Wi.js +1 -0
  393. package/dist/web-ui/_app/immutable/chunks/GFTX8GgV.js +1 -0
  394. package/dist/web-ui/_app/immutable/chunks/{C076q4JN.js → HNjs76Zz.js} +1 -1
  395. package/dist/web-ui/_app/immutable/chunks/HVMjDi4_.js +1 -0
  396. package/dist/web-ui/_app/immutable/chunks/{BkOJ8DkV.js → P0A_fJvS.js} +1 -1
  397. package/dist/web-ui/_app/immutable/chunks/T3vGWjIL.js +1 -0
  398. package/dist/web-ui/_app/immutable/chunks/VTmrX9Qu.js +1 -0
  399. package/dist/web-ui/_app/immutable/chunks/{k_Qegko0.js → Xvwhx_F1.js} +1 -1
  400. package/dist/web-ui/_app/immutable/chunks/Yyz1XMQA.js +1 -0
  401. package/dist/web-ui/_app/immutable/chunks/{62JVKtnb.js → dh5HeqUr.js} +1 -1
  402. package/dist/web-ui/_app/immutable/chunks/fZMteyca.js +62 -0
  403. package/dist/web-ui/_app/immutable/chunks/{D82RulSH.js → gPrj-hqC.js} +1 -1
  404. package/dist/web-ui/_app/immutable/chunks/htcWMiYN.js +1 -0
  405. package/dist/web-ui/_app/immutable/chunks/{CwELQvbx.js → oTsvd9y4.js} +1 -1
  406. package/dist/web-ui/_app/immutable/chunks/qJfLUwU4.js +1 -0
  407. package/dist/web-ui/_app/immutable/chunks/xCtiO_JE.js +1 -0
  408. package/dist/web-ui/_app/immutable/chunks/{DvA-KON-.js → y4GeEH6k.js} +1 -1
  409. package/dist/web-ui/_app/immutable/entry/app.C4h_eOn6.js +2 -0
  410. package/dist/web-ui/_app/immutable/entry/start.CQFTf9ep.js +1 -0
  411. package/dist/web-ui/_app/immutable/nodes/0.Dh1xO970.js +1 -0
  412. package/dist/web-ui/_app/immutable/nodes/1.l75D3Opx.js +1 -0
  413. package/dist/web-ui/_app/immutable/nodes/10.DBidBPc-.js +1 -0
  414. package/dist/web-ui/_app/immutable/nodes/11.Ab0gUKWe.js +1 -0
  415. package/dist/web-ui/_app/immutable/nodes/12.CMsnoxfs.js +1 -0
  416. package/dist/web-ui/_app/immutable/nodes/13.D8YKuknB.js +1 -0
  417. package/dist/web-ui/_app/immutable/nodes/14.DZ0aan7y.js +1 -0
  418. package/dist/web-ui/_app/immutable/nodes/15.CUIKreDL.js +2 -0
  419. package/dist/web-ui/_app/immutable/nodes/16.BWc8--BO.js +1 -0
  420. package/dist/web-ui/_app/immutable/nodes/2.CDUonbuh.js +1 -0
  421. package/dist/web-ui/_app/immutable/nodes/3.Ctg3M00i.js +1 -0
  422. package/dist/web-ui/_app/immutable/nodes/4.Ci-JDwbA.js +2 -0
  423. package/dist/web-ui/_app/immutable/nodes/5.CTyEDAq0.js +1 -0
  424. package/dist/web-ui/_app/immutable/nodes/6.BTZZqsAb.js +1 -0
  425. package/dist/web-ui/_app/immutable/nodes/7.BI52g_Jo.js +137 -0
  426. package/dist/web-ui/_app/immutable/nodes/8.3hZPaB9x.js +1 -0
  427. package/dist/web-ui/_app/immutable/nodes/9.DS49kvwl.js +29 -0
  428. package/dist/web-ui/_app/version.json +1 -1
  429. package/dist/web-ui/favicon-192.png +0 -0
  430. package/dist/web-ui/favicon-32.png +0 -0
  431. package/dist/web-ui/favicon.ico +0 -0
  432. package/dist/web-ui/index.html +14 -14
  433. package/package.json +12 -6
  434. package/plugin/.claude-plugin/marketplace.json +1 -1
  435. package/plugin/.claude-plugin/plugin.json +1 -1
  436. package/plugin/plugins/kspec/skills/merge/SKILL.md +127 -0
  437. package/plugin/plugins/kspec/skills/plan/SKILL.md +55 -26
  438. package/plugin/plugins/kspec/skills/review/SKILL.md +350 -133
  439. package/plugin/plugins/kspec/skills/task-work/SKILL.md +96 -106
  440. package/templates/agents-sections/04-pr-workflow.md +15 -12
  441. package/templates/agents-sections/06-ralph-loop.md +15 -10
  442. package/templates/skills/manifest.yaml +25 -7
  443. package/templates/skills/merge/SKILL.md +120 -0
  444. package/templates/skills/plan/SKILL.md +55 -26
  445. package/templates/skills/review/SKILL.md +346 -130
  446. package/templates/skills/task-work/SKILL.md +93 -103
  447. package/dist/web-ui/_app/immutable/assets/0.BJaYkGW2.css +0 -1
  448. package/dist/web-ui/_app/immutable/assets/9.SzGLxi4x.css +0 -1
  449. package/dist/web-ui/_app/immutable/chunks/-lc0BifF.js +0 -1
  450. package/dist/web-ui/_app/immutable/chunks/8RBjHMN1.js +0 -1
  451. package/dist/web-ui/_app/immutable/chunks/B5wTVqxm.js +0 -1
  452. package/dist/web-ui/_app/immutable/chunks/B6VSmczZ.js +0 -1
  453. package/dist/web-ui/_app/immutable/chunks/BEOQc37C.js +0 -1
  454. package/dist/web-ui/_app/immutable/chunks/BHtYorjv.js +0 -1
  455. package/dist/web-ui/_app/immutable/chunks/BMuCqDX8.js +0 -1
  456. package/dist/web-ui/_app/immutable/chunks/BUZujXJ2.js +0 -1
  457. package/dist/web-ui/_app/immutable/chunks/BWET-efb.js +0 -1
  458. package/dist/web-ui/_app/immutable/chunks/BXkNecpt.js +0 -1
  459. package/dist/web-ui/_app/immutable/chunks/BYzrIfX8.js +0 -1
  460. package/dist/web-ui/_app/immutable/chunks/BpuwufMc.js +0 -1
  461. package/dist/web-ui/_app/immutable/chunks/BwMO4RrG.js +0 -1
  462. package/dist/web-ui/_app/immutable/chunks/C33JaVbg.js +0 -1
  463. package/dist/web-ui/_app/immutable/chunks/CGtqifKp.js +0 -1
  464. package/dist/web-ui/_app/immutable/chunks/CHDZZ7OG.js +0 -1
  465. package/dist/web-ui/_app/immutable/chunks/CUir3f4J.js +0 -60
  466. package/dist/web-ui/_app/immutable/chunks/CrCIbn0C.js +0 -1
  467. package/dist/web-ui/_app/immutable/chunks/D6TVmR9T.js +0 -1
  468. package/dist/web-ui/_app/immutable/chunks/D7LTux4W.js +0 -1
  469. package/dist/web-ui/_app/immutable/chunks/DAh4Wfku.js +0 -1
  470. package/dist/web-ui/_app/immutable/chunks/DAx07bEQ.js +0 -1
  471. package/dist/web-ui/_app/immutable/chunks/DOno4cA2.js +0 -1
  472. package/dist/web-ui/_app/immutable/chunks/DQA8NZIH.js +0 -2
  473. package/dist/web-ui/_app/immutable/chunks/DRfPm2bo.js +0 -1
  474. package/dist/web-ui/_app/immutable/chunks/DhQhksaB.js +0 -1
  475. package/dist/web-ui/_app/immutable/chunks/DjG7s6hm.js +0 -1
  476. package/dist/web-ui/_app/immutable/chunks/DkltRNvh.js +0 -1
  477. package/dist/web-ui/_app/immutable/chunks/DlaTnPKL.js +0 -1
  478. package/dist/web-ui/_app/immutable/chunks/ExCq5swK.js +0 -1
  479. package/dist/web-ui/_app/immutable/chunks/T3zZGv51.js +0 -1
  480. package/dist/web-ui/_app/immutable/chunks/XZumBYeP.js +0 -1
  481. package/dist/web-ui/_app/immutable/chunks/_ySfNjkF.js +0 -1
  482. package/dist/web-ui/_app/immutable/chunks/iEtR5cV6.js +0 -1
  483. package/dist/web-ui/_app/immutable/entry/app.Cgu6uKeS.js +0 -2
  484. package/dist/web-ui/_app/immutable/entry/start.9XifnLoB.js +0 -1
  485. package/dist/web-ui/_app/immutable/nodes/0.DISwcKSK.js +0 -1
  486. package/dist/web-ui/_app/immutable/nodes/1.Cx2Ufqp1.js +0 -1
  487. package/dist/web-ui/_app/immutable/nodes/10.C3z8ijXL.js +0 -1
  488. package/dist/web-ui/_app/immutable/nodes/11.DZdIjZmM.js +0 -1
  489. package/dist/web-ui/_app/immutable/nodes/12.FsIGfAOa.js +0 -1
  490. package/dist/web-ui/_app/immutable/nodes/13.DZoFwagf.js +0 -1
  491. package/dist/web-ui/_app/immutable/nodes/14.DaIzDKbQ.js +0 -1
  492. package/dist/web-ui/_app/immutable/nodes/15.BYyt4XWF.js +0 -2
  493. package/dist/web-ui/_app/immutable/nodes/16.CQkSqpOe.js +0 -1
  494. package/dist/web-ui/_app/immutable/nodes/2.Bkf_j2UJ.js +0 -1
  495. package/dist/web-ui/_app/immutable/nodes/3.kaMCurJG.js +0 -1
  496. package/dist/web-ui/_app/immutable/nodes/4.BSsFPTHG.js +0 -2
  497. package/dist/web-ui/_app/immutable/nodes/5.CpPlcCEZ.js +0 -1
  498. package/dist/web-ui/_app/immutable/nodes/6.BN4FqQmY.js +0 -1
  499. package/dist/web-ui/_app/immutable/nodes/7.9kBYIZik.js +0 -1
  500. package/dist/web-ui/_app/immutable/nodes/8.BuijtZ6B.js +0 -1
  501. package/dist/web-ui/_app/immutable/nodes/9.C-Weba8R.js +0 -1
@@ -19,13 +19,15 @@
19
19
  * - @agent-dispatch-engine ac-4: CLI posts state change event to daemon
20
20
  */
21
21
 
22
+ import path from 'node:path';
22
23
  import { Elysia, t } from 'elysia';
23
24
  import { DispatchEngine } from '../../agent-runtime/dispatch.js';
24
25
  import type { TaskStateChange, TaskStatus, InvocationEvent } from '../../agent-runtime/dispatch.js';
25
26
  import { DEFAULT_KSPEC_CLI_PATH } from '../../agent-runtime/invocation.js';
26
- import { initContext, loadMetaContext } from '../../parser/index.js';
27
+ import { initContext, loadMetaContext, loadAllTasks, loadAllItems, ReferenceIndex, resolveProjectRoots } from '../../parser/index.js';
27
28
  import { getCompletedSessionCountsByAgent } from '../../sessions/store.js';
28
29
  import type { PubSubManager } from '../websocket/pubsub.js';
30
+ import type { SessionEventData } from '@kynetic-ai/shared';
29
31
 
30
32
  const VALID_TASK_STATUSES = new Set<string>([
31
33
  "pending", "in_progress", "pending_review", "needs_work", "blocked", "completed", "cancelled",
@@ -44,31 +46,33 @@ export interface AgentDispatchRouteOptions {
44
46
  * Create a new dispatch engine with optional WebSocket broadcast wiring.
45
47
  * AC: @daemon-agent-dispatch ac-3, ac-4
46
48
  */
47
- function createEngine(projectDir: string, pubsub?: PubSubManager): DispatchEngine {
49
+ function createEngine(
50
+ projectDir: string,
51
+ cwd?: string,
52
+ pubsub?: PubSubManager,
53
+ ): DispatchEngine {
48
54
  return new DispatchEngine({
49
55
  projectDir,
56
+ cwd,
50
57
  kspecCliPath: DEFAULT_KSPEC_CLI_PATH,
51
58
  onInvocationEvent: pubsub
52
59
  ? (event: InvocationEvent) => {
60
+ // AC: @ui-api-aggregation ac-4 - Include task_title for display
53
61
  pubsub.broadcast('agents', 'agent_invocation', {
54
62
  session_id: event.session_id,
55
63
  agent_id: event.agent_id,
56
64
  task_id: event.task_id ?? null,
65
+ task_title: event.task_title ?? null,
57
66
  status: event.status,
58
67
  timestamp: event.timestamp,
59
68
  }, projectDir);
60
69
  }
61
70
  : undefined,
71
+ // AC: @session-event-broadcast ac-replaces-text-chunks
62
72
  // AC: @cli-agent-commands ac-13, @daemon-agent-dispatch ac-8
63
- onTextChunk: pubsub
64
- ? (sessionId: string, agentId: string, taskId: string | null, text: string) => {
65
- pubsub.broadcast('agents', 'agent_text_chunk', {
66
- session_id: sessionId,
67
- agent_id: agentId,
68
- task_id: taskId,
69
- text,
70
- timestamp: Date.now(),
71
- }, projectDir);
73
+ onSessionEvent: pubsub
74
+ ? (event: SessionEventData) => {
75
+ pubsub.broadcast('agents', event.type, event, projectDir);
72
76
  }
73
77
  : undefined,
74
78
  });
@@ -117,6 +121,28 @@ function processStateChangeEvent(
117
121
  return { accepted: true };
118
122
  }
119
123
 
124
+ export function resolveDispatchCwd(
125
+ projectDir: string,
126
+ requestedCwd: string | null,
127
+ ): string {
128
+ if (requestedCwd && !path.isAbsolute(requestedCwd)) {
129
+ throw new Error('Dispatch cwd must be an absolute path');
130
+ }
131
+ const cwd = requestedCwd ? path.resolve(requestedCwd) : projectDir;
132
+
133
+ if (cwd === projectDir) {
134
+ return cwd;
135
+ }
136
+
137
+ const projectRoots = resolveProjectRoots(projectDir);
138
+ const cwdRoots = resolveProjectRoots(cwd);
139
+ if (!projectRoots || !cwdRoots || projectRoots.mainRoot !== cwdRoots.mainRoot) {
140
+ throw new Error('Dispatch cwd must belong to the same git project');
141
+ }
142
+
143
+ return cwd;
144
+ }
145
+
120
146
  export function createAgentDispatchRoutes(options: AgentDispatchRouteOptions = {}) {
121
147
  const { pubsub } = options;
122
148
 
@@ -134,16 +160,34 @@ export function createAgentDispatchRoutes(options: AgentDispatchRouteOptions = {
134
160
  }, { body: stateChangeBodySchema })
135
161
 
136
162
  // AC: @daemon-agent-dispatch ac-6 - Unified dispatch start/stop via action field
137
- .post('/dispatch', async ({ body, projectContext }) => {
163
+ .post('/dispatch', async ({ body, projectContext, request, set }) => {
138
164
  const projectDir = projectContext.path;
139
165
 
140
166
  if (body.action === 'start') {
167
+ let requestedCwd: string;
168
+ try {
169
+ requestedCwd = resolveDispatchCwd(projectDir, request.headers.get('X-Kspec-Cwd'));
170
+ } catch (err) {
171
+ set.status = 400;
172
+ return {
173
+ dispatch_enabled: false,
174
+ error: err instanceof Error ? err.message : String(err),
175
+ };
176
+ }
177
+
141
178
  let engine = engines.get(projectDir);
142
179
  if (engine?.getStatus().running) {
180
+ if (engine.getCwd() !== requestedCwd) {
181
+ set.status = 409;
182
+ return {
183
+ dispatch_enabled: true,
184
+ error: `Dispatch engine already running for ${projectDir} with cwd ${engine.getCwd()}`,
185
+ };
186
+ }
143
187
  return { dispatch_enabled: true, reason: 'Already running' };
144
188
  }
145
189
 
146
- engine = createEngine(projectDir, pubsub);
190
+ engine = createEngine(projectDir, requestedCwd, pubsub);
147
191
  engines.set(projectDir, engine);
148
192
  await engine.start();
149
193
 
@@ -166,16 +210,34 @@ export function createAgentDispatchRoutes(options: AgentDispatchRouteOptions = {
166
210
  })
167
211
 
168
212
  // Start dispatch engine (legacy route)
169
- .post('/dispatch/start', async ({ projectContext }) => {
213
+ .post('/dispatch/start', async ({ projectContext, request, set }) => {
170
214
  const projectDir = projectContext.path;
215
+ let requestedCwd: string;
216
+ try {
217
+ requestedCwd = resolveDispatchCwd(projectDir, request.headers.get('X-Kspec-Cwd'));
218
+ } catch (err) {
219
+ set.status = 400;
220
+ return {
221
+ started: false,
222
+ error: err instanceof Error ? err.message : String(err),
223
+ };
224
+ }
171
225
 
172
226
  let engine = engines.get(projectDir);
173
227
  if (engine?.getStatus().running) {
228
+ if (engine.getCwd() !== requestedCwd) {
229
+ set.status = 409;
230
+ return {
231
+ started: false,
232
+ error: `Dispatch engine already running for ${projectDir} with cwd ${engine.getCwd()}`,
233
+ status: engine.getStatus(),
234
+ };
235
+ }
174
236
  return { started: false, reason: 'Already running', status: engine.getStatus() };
175
237
  }
176
238
 
177
239
  // AC: @agent-dispatch-engine ac-10 - pass kspecCliPath so task notes work from daemon-started engine
178
- engine = createEngine(projectDir, pubsub);
240
+ engine = createEngine(projectDir, requestedCwd, pubsub);
179
241
  engines.set(projectDir, engine);
180
242
 
181
243
  await engine.start();
@@ -215,19 +277,24 @@ export function createAgentDispatchRoutes(options: AgentDispatchRouteOptions = {
215
277
  })
216
278
 
217
279
  // AC: @daemon-agent-dispatch ac-5 - Public status endpoint
280
+ // AC: @ui-api-ref-resolution ac-1 - Include task_title for active invocations
218
281
  .get('/status', async ({ projectContext }) => {
219
282
  const projectDir = projectContext.path;
220
283
  const engineStatus = engines.get(projectDir)?.getStatus();
221
284
 
222
285
  let agentDefinitions: Array<{ id: string; name: string; adapter: string; completed_sessions: number }> = [];
223
286
  let completedCounts: Record<string, number> = {};
287
+ let refIndex: ReferenceIndex | null = null;
224
288
  try {
225
289
  const ctx = await initContext(projectDir);
226
- const [meta, counts] = await Promise.all([
290
+ const [meta, counts, tasks, items] = await Promise.all([
227
291
  loadMetaContext(ctx),
228
292
  getCompletedSessionCountsByAgent(ctx.specDir),
293
+ loadAllTasks(ctx),
294
+ loadAllItems(ctx),
229
295
  ]);
230
296
  completedCounts = counts;
297
+ refIndex = new ReferenceIndex(tasks, items);
231
298
  agentDefinitions = meta.agents.map((a) => ({
232
299
  id: a.id,
233
300
  name: a.name,
@@ -240,12 +307,22 @@ export function createAgentDispatchRoutes(options: AgentDispatchRouteOptions = {
240
307
 
241
308
  return {
242
309
  dispatch_enabled: engineStatus?.running ?? false,
243
- active_invocations: engineStatus?.invocations?.map((inv) => ({
244
- session_id: inv.sessionId,
245
- agent_id: inv.agentId,
246
- task_ref: inv.taskRef ?? null,
247
- elapsed_ms: inv.elapsedMs,
248
- })) ?? [],
310
+ active_invocations: engineStatus?.invocations?.map((inv) => {
311
+ let task_title: string | null = null;
312
+ if (inv.taskRef && refIndex) {
313
+ const result = refIndex.resolve(inv.taskRef);
314
+ if (result.ok) {
315
+ task_title = (result.item as { title?: string }).title ?? null;
316
+ }
317
+ }
318
+ return {
319
+ session_id: inv.sessionId,
320
+ agent_id: inv.agentId,
321
+ task_ref: inv.taskRef ?? null,
322
+ task_title,
323
+ elapsed_ms: inv.elapsedMs,
324
+ };
325
+ }) ?? [],
249
326
  queue_depth: engineStatus?.queuedInvocations ?? 0,
250
327
  agent_definitions: agentDefinitions,
251
328
  };
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Aggregation API Routes
3
+ *
4
+ * Pre-computed server-side aggregation endpoints:
5
+ * - GET /api/aggregation/tasks/summary - Task status counts with dependency-aware distinctions
6
+ * - GET /api/aggregation/validation - Extended validation/alignment stats with entity and AC counts
7
+ * - GET /api/aggregation/inbox - Inbox items with inline triage status
8
+ *
9
+ * AC Coverage:
10
+ * - @ui-api-aggregation ac-1: Task status summary with ready vs blocked distinctions
11
+ * - @ui-api-aggregation ac-2: Validation/alignment stats with entity counts, AC counts, orphan counts
12
+ * - @ui-api-aggregation ac-3: Inbox items with inline triage status
13
+ */
14
+
15
+ import { Elysia } from 'elysia';
16
+ import {
17
+ initContext,
18
+ loadAllTasks,
19
+ loadInboxItems,
20
+ loadTriageRecords,
21
+ findTriageRecordByInboxRef,
22
+ buildIndexes,
23
+ validate,
24
+ AlignmentIndex,
25
+ areDependenciesMet,
26
+ } from '../../parser/index.js';
27
+ import type {
28
+ TaskStatusSummary,
29
+ ValidationAggregation,
30
+ InboxItemWithTriage,
31
+ } from '@kynetic-ai/shared';
32
+
33
+ interface AggregationRouteOptions {}
34
+
35
+ export function createAggregationRoutes(options: AggregationRouteOptions = {}) {
36
+ return new Elysia({ prefix: '/api/aggregation' })
37
+ // AC: @ui-api-aggregation ac-1 - Task status summary with dependency-aware distinctions
38
+ .get('/tasks/summary', async ({ projectContext }) => {
39
+ const ctx = await initContext(projectContext.path);
40
+ const tasks = await loadAllTasks(ctx);
41
+
42
+ // Count tasks by status
43
+ const counts: Record<string, number> = {};
44
+ for (const task of tasks) {
45
+ counts[task.status] = (counts[task.status] || 0) + 1;
46
+ }
47
+
48
+ // Dependency-aware distinctions: ready vs blocked by incomplete dependencies
49
+ let ready = 0;
50
+ let blockedByDependencies = 0;
51
+ for (const task of tasks) {
52
+ if (task.status !== 'pending' && task.status !== 'needs_work') continue;
53
+ if (task.blocked_by.length > 0) {
54
+ blockedByDependencies++;
55
+ } else if (!areDependenciesMet(task, tasks)) {
56
+ blockedByDependencies++;
57
+ } else {
58
+ ready++;
59
+ }
60
+ }
61
+
62
+ const result: TaskStatusSummary = {
63
+ counts,
64
+ ready,
65
+ blocked_by_dependencies: blockedByDependencies,
66
+ total: tasks.length,
67
+ };
68
+
69
+ return result;
70
+ })
71
+
72
+ // AC: @ui-api-aggregation ac-2 - Extended validation/alignment stats
73
+ .get('/validation', async ({ projectContext }) => {
74
+ const ctx = await initContext(projectContext.path);
75
+ const { tasks, items, refIndex } = await buildIndexes(ctx);
76
+
77
+ // Run validation for error/warning counts and completeness data
78
+ const validationResult = await validate(ctx);
79
+
80
+ // Build alignment index
81
+ const alignIndex = new AlignmentIndex(tasks, items);
82
+ alignIndex.buildLinks(refIndex);
83
+ const alignStats = alignIndex.getStats();
84
+ const alignWarnings = alignIndex.findAlignmentWarnings();
85
+
86
+ // Entity counts
87
+ const traitCount = items.filter((item) => item.type === 'trait').length;
88
+
89
+ // AC counts from completeness warnings
90
+ // Count total ACs across all non-trait items
91
+ let totalACs = 0;
92
+ for (const item of items) {
93
+ if (item.type !== 'trait') {
94
+ totalACs += item.acceptance_criteria?.length || 0;
95
+ }
96
+ }
97
+
98
+ // Count uncovered ACs from completeness warnings
99
+ // Each warning represents one item with N uncovered ACs listed in details
100
+ // Details format: "Uncovered: ac-1, ac-2, ac-3"
101
+ const uncoveredWarnings = validationResult.completenessWarnings.filter(
102
+ (w) => w.type === 'missing_test_coverage' && w.subtype === 'own_ac'
103
+ );
104
+ let uncoveredCount = 0;
105
+ for (const warning of uncoveredWarnings) {
106
+ if (warning.details) {
107
+ // Parse "Uncovered: ac-1, ac-2, ac-3" to count individual ACs
108
+ const acList = warning.details.replace(/^Uncovered:\s*/, '');
109
+ uncoveredCount += acList.split(',').filter((s) => s.trim().length > 0).length;
110
+ }
111
+ }
112
+ const coveredCount = Math.max(0, totalACs - uncoveredCount);
113
+
114
+ const result: ValidationAggregation = {
115
+ stats: {
116
+ totalSpecs: alignStats.totalSpecs,
117
+ specsWithTasks: alignStats.specsWithTasks,
118
+ alignedSpecs: alignStats.alignedSpecs,
119
+ orphanedSpecs: alignStats.orphanedSpecs,
120
+ },
121
+ warnings: alignWarnings,
122
+ entity_counts: {
123
+ items: validationResult.stats.itemsChecked,
124
+ tasks: validationResult.stats.tasksChecked,
125
+ traits: traitCount,
126
+ },
127
+ ac_counts: {
128
+ total: totalACs,
129
+ covered: coveredCount,
130
+ uncovered: uncoveredCount,
131
+ },
132
+ orphan_count: validationResult.orphans.length,
133
+ valid: validationResult.valid,
134
+ error_count: validationResult.schemaErrors.length + validationResult.refErrors.length,
135
+ warning_count: validationResult.refWarnings.length + validationResult.completenessWarnings.length,
136
+ };
137
+
138
+ return result;
139
+ })
140
+
141
+ // AC: @ui-api-aggregation ac-3 - Inbox items with inline triage status
142
+ .get('/inbox', async ({ projectContext }) => {
143
+ const ctx = await initContext(projectContext.path);
144
+ const inboxItems = await loadInboxItems(ctx);
145
+ const triageRecords = await loadTriageRecords(ctx);
146
+
147
+ // Sort by created_at descending (newest first)
148
+ const sorted = [...inboxItems].sort(
149
+ (a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()
150
+ );
151
+
152
+ // Merge triage status inline
153
+ const items: InboxItemWithTriage[] = sorted.map((item) => {
154
+ const triageRecord = findTriageRecordByInboxRef(triageRecords, item._ulid);
155
+
156
+ const result: InboxItemWithTriage = {
157
+ _ulid: item._ulid,
158
+ text: item.text,
159
+ tags: item.tags,
160
+ added_by: item.added_by,
161
+ created_at: item.created_at,
162
+ };
163
+
164
+ if (triageRecord) {
165
+ result.triage = {
166
+ _ulid: triageRecord._ulid,
167
+ status: triageRecord.status as 'pending' | 'triaged' | 'acted_on',
168
+ action: triageRecord.action as 'promote' | 'delete' | 'defer' | 'spec-gap' | 'duplicate' | undefined,
169
+ reasoning: triageRecord.reasoning,
170
+ decided_by: triageRecord.decided_by,
171
+ acted_at: triageRecord.acted_at,
172
+ result_ref: triageRecord.result_ref,
173
+ };
174
+ }
175
+
176
+ return result;
177
+ });
178
+
179
+ return {
180
+ items,
181
+ total: items.length,
182
+ };
183
+ });
184
+ }
@@ -88,9 +88,14 @@ export function createInboxRoutes(options: InboxRouteOptions) {
88
88
  await commitIfShadow(ctx.shadow, `inbox: add item ${item._ulid}`);
89
89
 
90
90
  // Broadcast update
91
+ // AC: @ui-api-aggregation ac-4 - Include full item data for in-place UI updates
91
92
  // AC: @multi-directory-daemon ac-18 - Broadcast scoped to request project
92
93
  pubsub.broadcast('inbox:updates', 'inbox_item_created', {
93
94
  ulid: item._ulid,
95
+ text: item.text,
96
+ tags: item.tags,
97
+ added_by: item.added_by,
98
+ created_at: item.created_at,
94
99
  }, projectContext.path);
95
100
 
96
101
  // AC: @api-contract ac-13 - Return item with generated ULID
@@ -19,12 +19,16 @@ import {
19
19
  loadAllItems,
20
20
  loadAllTasks,
21
21
  loadPlans,
22
+ findItemByRef,
23
+ findTaskByRef,
22
24
  ReferenceIndex,
23
25
  AlignmentIndex,
24
26
  getCachedTestCoverage,
25
27
  computeACCoverage,
26
28
  type LoadedSpecItem,
29
+ type LoadedTask,
27
30
  } from '../../parser/index.js';
31
+ import { getRelatedSessionsForItem } from './session-related.js';
28
32
 
29
33
  interface ItemsRouteOptions {}
30
34
 
@@ -89,6 +93,49 @@ function computeParentMap(items: LoadedSpecItem[]): Map<string, string | undefin
89
93
  return parentMap;
90
94
  }
91
95
 
96
+ function getItemImplementationStatus(item: LoadedSpecItem): string | undefined {
97
+ if (typeof item.status === 'string') {
98
+ return item.status;
99
+ }
100
+
101
+ return item.status?.implementation;
102
+ }
103
+
104
+ function getItemMaturity(item: LoadedSpecItem): string | undefined {
105
+ if (typeof item.status === 'object') {
106
+ return item.status?.maturity;
107
+ }
108
+
109
+ return undefined;
110
+ }
111
+
112
+ function toBatchSpecItemSummary(item: LoadedSpecItem) {
113
+ return {
114
+ kind: 'item',
115
+ ulid: item._ulid,
116
+ slugs: item.slugs,
117
+ title: item.title,
118
+ type: item.type,
119
+ status: getItemImplementationStatus(item),
120
+ maturity: getItemMaturity(item),
121
+ traits: item.traits ?? [],
122
+ ac_count: item.acceptance_criteria?.length ?? 0,
123
+ };
124
+ }
125
+
126
+ function toBatchTaskSummary(task: LoadedTask) {
127
+ return {
128
+ kind: 'task',
129
+ ulid: task._ulid,
130
+ slugs: task.slugs,
131
+ title: task.title,
132
+ status: task.status,
133
+ priority: task.priority,
134
+ spec_ref: task.spec_ref,
135
+ assignee: task.assignee,
136
+ };
137
+ }
138
+
92
139
  export function createItemsRoutes(options: ItemsRouteOptions = {}) {
93
140
  // No closure-scoped kspecDir needed - comes from middleware
94
141
 
@@ -206,6 +253,73 @@ export function createItemsRoutes(options: ItemsRouteOptions = {}) {
206
253
  }
207
254
  )
208
255
 
256
+ .post(
257
+ '/batch',
258
+ async ({ body, error: errorResponse, projectContext }) => {
259
+ const refs = body.refs;
260
+
261
+ // AC: @trait-api-endpoint ac-3 - Validate body
262
+ if (!Array.isArray(refs)) {
263
+ return errorResponse(400, {
264
+ error: 'validation_error',
265
+ details: [
266
+ {
267
+ field: 'refs',
268
+ message: 'Refs is required and must be an array of item references',
269
+ },
270
+ ],
271
+ });
272
+ }
273
+
274
+ // AC: @batch-item-fetch-api ac-5 - Enforce max batch size
275
+ if (refs.length > 100) {
276
+ return errorResponse(400, {
277
+ error: 'validation_error',
278
+ details: [
279
+ {
280
+ field: 'refs',
281
+ message: 'Maximum batch size is 100 refs',
282
+ },
283
+ ],
284
+ });
285
+ }
286
+
287
+ // AC: @multi-directory-daemon ac-1, ac-24 - Use project context from middleware
288
+ const ctx = await initContext(projectContext.path);
289
+ const items = await loadAllItems(ctx);
290
+ const tasks = await loadAllTasks(ctx);
291
+
292
+ const resolvedItems = [];
293
+ const unresolved: string[] = [];
294
+
295
+ for (const ref of refs) {
296
+ const task = findTaskByRef(tasks, ref);
297
+ if (task) {
298
+ resolvedItems.push(toBatchTaskSummary(task));
299
+ continue;
300
+ }
301
+
302
+ const item = findItemByRef(items, ref);
303
+ if (item) {
304
+ resolvedItems.push(toBatchSpecItemSummary(item));
305
+ continue;
306
+ }
307
+
308
+ unresolved.push(ref);
309
+ }
310
+
311
+ return {
312
+ items: resolvedItems,
313
+ unresolved,
314
+ };
315
+ },
316
+ {
317
+ body: t.Object({
318
+ refs: t.Optional(t.Array(t.String())),
319
+ }),
320
+ }
321
+ )
322
+
209
323
  // AC: @api-contract ac-10 - Get single item by ref
210
324
  .get(
211
325
  '/:ref',
@@ -340,5 +454,36 @@ export function createItemsRoutes(options: ItemsRouteOptions = {}) {
340
454
  ref: t.String(),
341
455
  }),
342
456
  }
457
+ )
458
+
459
+ .get(
460
+ '/:ref/sessions',
461
+ async ({ params, error: errorResponse, projectContext }) => {
462
+ const ctx = await initContext(projectContext.path);
463
+ const items = await loadAllItems(ctx);
464
+ const tasks = await loadAllTasks(ctx);
465
+ const result = await getRelatedSessionsForItem({
466
+ itemRef: params.ref,
467
+ items,
468
+ tasks,
469
+ sessionsDir: ctx.sessionsDir,
470
+ });
471
+
472
+ if ('error' in result) {
473
+ return errorResponse(404, result.error);
474
+ }
475
+
476
+ return {
477
+ items: result.sessions,
478
+ total: result.sessions.length,
479
+ offset: 0,
480
+ limit: result.sessions.length,
481
+ };
482
+ },
483
+ {
484
+ params: t.Object({
485
+ ref: t.String(),
486
+ }),
487
+ }
343
488
  );
344
489
  }
@@ -202,7 +202,7 @@ export function createMetaRoutes(options: MetaRouteOptions = {}) {
202
202
  return {
203
203
  project: manifest?.project ?? null,
204
204
  spec_version: manifest?.kynetic ?? null,
205
- root_dir: ctx.rootDir,
205
+ root_dir: ctx.projectRoot,
206
206
  remote_tracking: config.shadow.remote
207
207
  ? { value: config.shadow.remote.value, type: config.shadow.remote.type }
208
208
  : null,
@@ -20,10 +20,14 @@ import type { ProjectContextManager } from '../project-context';
20
20
 
21
21
  interface ProjectsRouteOptions {
22
22
  projectManager: ProjectContextManager;
23
+ /** Called after a project is registered (e.g., to start session sync). */
24
+ onProjectRegistered?: (projectPath: string) => Promise<void>;
25
+ /** Called before a project is unregistered (e.g., to stop session sync). */
26
+ onProjectUnregistered?: (projectPath: string) => void;
23
27
  }
24
28
 
25
29
  export function createProjectsRoutes(options: ProjectsRouteOptions) {
26
- const { projectManager } = options;
30
+ const { projectManager, onProjectRegistered, onProjectUnregistered } = options;
27
31
 
28
32
  return new Elysia({ prefix: '/api/projects' })
29
33
  // AC: @multi-directory-daemon ac-28 - List registered projects
@@ -91,6 +95,16 @@ export function createProjectsRoutes(options: ProjectsRouteOptions) {
91
95
  throw error;
92
96
  }
93
97
 
98
+ // Start session sync for the newly registered project
99
+ if (onProjectRegistered) {
100
+ try {
101
+ await onProjectRegistered(context.path);
102
+ } catch (error) {
103
+ // Session sync init failure does not block project registration
104
+ console.error(`[daemon] Failed to start session sync for ${context.path}:`, error);
105
+ }
106
+ }
107
+
94
108
  return {
95
109
  success: true,
96
110
  project: {
@@ -135,23 +149,31 @@ export function createProjectsRoutes(options: ProjectsRouteOptions) {
135
149
  // AC: @multi-directory-daemon ac-30 - Decode path from URL parameter
136
150
  const projectPath = decodeURIComponent(params.encodedPath);
137
151
 
138
- // Validate project exists
139
- if (!projectManager.hasProject(projectPath)) {
152
+ // Validate project exists and get normalized context
153
+ let context;
154
+ try {
155
+ context = projectManager.getProject(projectPath);
156
+ } catch {
140
157
  return errorResponse(404, {
141
158
  error: `Project not registered: ${projectPath}`,
142
159
  });
143
160
  }
144
161
 
145
162
  try {
163
+ // Stop session sync before unregistering (use normalized path)
164
+ if (onProjectUnregistered) {
165
+ onProjectUnregistered(context.path);
166
+ }
167
+
146
168
  // AC: @multi-directory-daemon ac-30 - Stop file watcher
147
- await projectManager.stopWatcher(projectPath);
169
+ await projectManager.stopWatcher(context.path);
148
170
 
149
171
  // AC: @multi-directory-daemon ac-30 - Unregister project
150
- projectManager.unregisterProject(projectPath);
172
+ projectManager.unregisterProject(context.path);
151
173
 
152
174
  return {
153
175
  success: true,
154
- message: `Project unregistered: ${projectPath}`,
176
+ message: `Project unregistered: ${context.path}`,
155
177
  };
156
178
  } catch (error: unknown) {
157
179
  return errorResponse(500, {