@kynetic-ai/spec 0.10.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 (487) 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 +150 -10
  7. package/dist/agent-runtime/dispatch.d.ts.map +1 -1
  8. package/dist/agent-runtime/dispatch.js +1248 -244
  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 +172 -60
  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/meta.d.ts.map +1 -1
  70. package/dist/cli/commands/meta.js +10 -1
  71. package/dist/cli/commands/meta.js.map +1 -1
  72. package/dist/cli/commands/plan-import.d.ts +3 -3
  73. package/dist/cli/commands/plan-import.d.ts.map +1 -1
  74. package/dist/cli/commands/plan-import.js +213 -528
  75. package/dist/cli/commands/plan-import.js.map +1 -1
  76. package/dist/cli/commands/plan.d.ts.map +1 -1
  77. package/dist/cli/commands/plan.js +533 -83
  78. package/dist/cli/commands/plan.js.map +1 -1
  79. package/dist/cli/commands/review.d.ts +14 -0
  80. package/dist/cli/commands/review.d.ts.map +1 -0
  81. package/dist/cli/commands/review.js +1142 -0
  82. package/dist/cli/commands/review.js.map +1 -0
  83. package/dist/cli/commands/serve.d.ts +1 -0
  84. package/dist/cli/commands/serve.d.ts.map +1 -1
  85. package/dist/cli/commands/serve.js +33 -10
  86. package/dist/cli/commands/serve.js.map +1 -1
  87. package/dist/cli/commands/session/checkpoint.d.ts +2 -4
  88. package/dist/cli/commands/session/checkpoint.d.ts.map +1 -1
  89. package/dist/cli/commands/session/checkpoint.js +6 -107
  90. package/dist/cli/commands/session/checkpoint.js.map +1 -1
  91. package/dist/cli/commands/session/commands.d.ts.map +1 -1
  92. package/dist/cli/commands/session/commands.js +33 -23
  93. package/dist/cli/commands/session/commands.js.map +1 -1
  94. package/dist/cli/commands/session/compact.js +4 -4
  95. package/dist/cli/commands/session/compact.js.map +1 -1
  96. package/dist/cli/commands/session/create.js +2 -2
  97. package/dist/cli/commands/session/create.js.map +1 -1
  98. package/dist/cli/commands/session/format.d.ts.map +1 -1
  99. package/dist/cli/commands/session/format.js +1 -6
  100. package/dist/cli/commands/session/format.js.map +1 -1
  101. package/dist/cli/commands/session/log.d.ts +32 -7
  102. package/dist/cli/commands/session/log.d.ts.map +1 -1
  103. package/dist/cli/commands/session/log.js +166 -60
  104. package/dist/cli/commands/session/log.js.map +1 -1
  105. package/dist/cli/commands/session/migrate.d.ts +9 -0
  106. package/dist/cli/commands/session/migrate.d.ts.map +1 -0
  107. package/dist/cli/commands/session/migrate.js +46 -0
  108. package/dist/cli/commands/session/migrate.js.map +1 -0
  109. package/dist/cli/commands/session/stale-close.d.ts.map +1 -1
  110. package/dist/cli/commands/session/stale-close.js +5 -8
  111. package/dist/cli/commands/session/stale-close.js.map +1 -1
  112. package/dist/cli/commands/session/types.d.ts +1 -1
  113. package/dist/cli/commands/session/types.d.ts.map +1 -1
  114. package/dist/cli/commands/setup.d.ts +2 -2
  115. package/dist/cli/commands/setup.d.ts.map +1 -1
  116. package/dist/cli/commands/setup.js +287 -257
  117. package/dist/cli/commands/setup.js.map +1 -1
  118. package/dist/cli/commands/shadow.d.ts.map +1 -1
  119. package/dist/cli/commands/shadow.js +147 -31
  120. package/dist/cli/commands/shadow.js.map +1 -1
  121. package/dist/cli/commands/skill-crud.d.ts +7 -0
  122. package/dist/cli/commands/skill-crud.d.ts.map +1 -1
  123. package/dist/cli/commands/skill-crud.js +41 -18
  124. package/dist/cli/commands/skill-crud.js.map +1 -1
  125. package/dist/cli/commands/skill-diff.d.ts.map +1 -1
  126. package/dist/cli/commands/skill-diff.js +29 -3
  127. package/dist/cli/commands/skill-diff.js.map +1 -1
  128. package/dist/cli/commands/skill-install.d.ts.map +1 -1
  129. package/dist/cli/commands/skill-install.js +5 -4
  130. package/dist/cli/commands/skill-install.js.map +1 -1
  131. package/dist/cli/commands/task.d.ts.map +1 -1
  132. package/dist/cli/commands/task.js +359 -49
  133. package/dist/cli/commands/task.js.map +1 -1
  134. package/dist/cli/commands/trait.d.ts.map +1 -1
  135. package/dist/cli/commands/trait.js +5 -27
  136. package/dist/cli/commands/trait.js.map +1 -1
  137. package/dist/cli/commands/validate.d.ts.map +1 -1
  138. package/dist/cli/commands/validate.js +113 -52
  139. package/dist/cli/commands/validate.js.map +1 -1
  140. package/dist/cli/index.d.ts.map +1 -1
  141. package/dist/cli/index.js +69 -2
  142. package/dist/cli/index.js.map +1 -1
  143. package/dist/cli/output.d.ts +26 -0
  144. package/dist/cli/output.d.ts.map +1 -1
  145. package/dist/cli/output.js +108 -1
  146. package/dist/cli/output.js.map +1 -1
  147. package/dist/cli/sync-mode.d.ts +44 -0
  148. package/dist/cli/sync-mode.d.ts.map +1 -0
  149. package/dist/cli/sync-mode.js +64 -0
  150. package/dist/cli/sync-mode.js.map +1 -0
  151. package/dist/daemon/middleware/project-context.ts +25 -7
  152. package/dist/daemon/project-context.ts +18 -0
  153. package/dist/daemon/routes/agent-dispatch.ts +107 -23
  154. package/dist/daemon/routes/aggregation.ts +184 -0
  155. package/dist/daemon/routes/inbox.ts +5 -0
  156. package/dist/daemon/routes/items.ts +167 -0
  157. package/dist/daemon/routes/meta.ts +141 -1
  158. package/dist/daemon/routes/plans.ts +147 -0
  159. package/dist/daemon/routes/projects.ts +28 -6
  160. package/dist/daemon/routes/ref-resolution.ts +119 -0
  161. package/dist/daemon/routes/refs.ts +42 -0
  162. package/dist/daemon/routes/session-related.ts +140 -0
  163. package/dist/daemon/routes/sessions.ts +581 -0
  164. package/dist/daemon/routes/tasks.ts +257 -2
  165. package/dist/daemon/routes/triage.ts +40 -1
  166. package/dist/daemon/routes/validation.ts +1 -1
  167. package/dist/daemon/server.ts +165 -50
  168. package/dist/daemon/session-sync.ts +11 -0
  169. package/dist/daemon/shadow-sync.ts +11 -0
  170. package/dist/daemon/watcher.ts +56 -5
  171. package/dist/daemon/websocket/project-resolution.ts +77 -0
  172. package/dist/export/json.d.ts.map +1 -1
  173. package/dist/export/json.js +104 -1
  174. package/dist/export/json.js.map +1 -1
  175. package/dist/export/types.d.ts +52 -1
  176. package/dist/export/types.d.ts.map +1 -1
  177. package/dist/index.d.ts +1 -0
  178. package/dist/index.d.ts.map +1 -1
  179. package/dist/index.js +1 -0
  180. package/dist/index.js.map +1 -1
  181. package/dist/parser/agent-detection.d.ts +1 -1
  182. package/dist/parser/agent-detection.d.ts.map +1 -1
  183. package/dist/parser/agent-detection.js +10 -0
  184. package/dist/parser/agent-detection.js.map +1 -1
  185. package/dist/parser/alignment.d.ts.map +1 -1
  186. package/dist/parser/alignment.js +4 -2
  187. package/dist/parser/alignment.js.map +1 -1
  188. package/dist/parser/config.d.ts +397 -2
  189. package/dist/parser/config.d.ts.map +1 -1
  190. package/dist/parser/config.js +125 -3
  191. package/dist/parser/config.js.map +1 -1
  192. package/dist/parser/dispatch-workspaces.d.ts +18 -0
  193. package/dist/parser/dispatch-workspaces.d.ts.map +1 -0
  194. package/dist/parser/dispatch-workspaces.js +209 -0
  195. package/dist/parser/dispatch-workspaces.js.map +1 -0
  196. package/dist/parser/doctor.d.ts.map +1 -1
  197. package/dist/parser/doctor.js +27 -8
  198. package/dist/parser/doctor.js.map +1 -1
  199. package/dist/parser/file-lock.d.ts.map +1 -1
  200. package/dist/parser/file-lock.js +9 -2
  201. package/dist/parser/file-lock.js.map +1 -1
  202. package/dist/parser/index.d.ts +6 -0
  203. package/dist/parser/index.d.ts.map +1 -1
  204. package/dist/parser/index.js +6 -0
  205. package/dist/parser/index.js.map +1 -1
  206. package/dist/parser/plans.d.ts.map +1 -1
  207. package/dist/parser/plans.js +1 -0
  208. package/dist/parser/plans.js.map +1 -1
  209. package/dist/parser/refs.d.ts +8 -1
  210. package/dist/parser/refs.d.ts.map +1 -1
  211. package/dist/parser/refs.js +27 -1
  212. package/dist/parser/refs.js.map +1 -1
  213. package/dist/parser/review-operations.d.ts +72 -0
  214. package/dist/parser/review-operations.d.ts.map +1 -0
  215. package/dist/parser/review-operations.js +185 -0
  216. package/dist/parser/review-operations.js.map +1 -0
  217. package/dist/parser/review-task-integration.d.ts +78 -0
  218. package/dist/parser/review-task-integration.d.ts.map +1 -0
  219. package/dist/parser/review-task-integration.js +173 -0
  220. package/dist/parser/review-task-integration.js.map +1 -0
  221. package/dist/parser/review-threads.d.ts +101 -0
  222. package/dist/parser/review-threads.d.ts.map +1 -0
  223. package/dist/parser/review-threads.js +222 -0
  224. package/dist/parser/review-threads.js.map +1 -0
  225. package/dist/parser/review-validation.d.ts +69 -0
  226. package/dist/parser/review-validation.d.ts.map +1 -0
  227. package/dist/parser/review-validation.js +207 -0
  228. package/dist/parser/review-validation.js.map +1 -0
  229. package/dist/parser/reviews.d.ts +58 -0
  230. package/dist/parser/reviews.d.ts.map +1 -0
  231. package/dist/parser/reviews.js +230 -0
  232. package/dist/parser/reviews.js.map +1 -0
  233. package/dist/parser/session-branch.d.ts +91 -0
  234. package/dist/parser/session-branch.d.ts.map +1 -0
  235. package/dist/parser/session-branch.js +565 -0
  236. package/dist/parser/session-branch.js.map +1 -0
  237. package/dist/parser/session-sync-scheduler.d.ts +53 -0
  238. package/dist/parser/session-sync-scheduler.d.ts.map +1 -0
  239. package/dist/parser/session-sync-scheduler.js +100 -0
  240. package/dist/parser/session-sync-scheduler.js.map +1 -0
  241. package/dist/parser/setup-status.d.ts +7 -1
  242. package/dist/parser/setup-status.d.ts.map +1 -1
  243. package/dist/parser/setup-status.js +104 -39
  244. package/dist/parser/setup-status.js.map +1 -1
  245. package/dist/parser/shadow-sync-scheduler.d.ts +71 -0
  246. package/dist/parser/shadow-sync-scheduler.d.ts.map +1 -0
  247. package/dist/parser/shadow-sync-scheduler.js +139 -0
  248. package/dist/parser/shadow-sync-scheduler.js.map +1 -0
  249. package/dist/parser/shadow.d.ts +121 -14
  250. package/dist/parser/shadow.d.ts.map +1 -1
  251. package/dist/parser/shadow.js +752 -27
  252. package/dist/parser/shadow.js.map +1 -1
  253. package/dist/parser/skill-render.d.ts +24 -0
  254. package/dist/parser/skill-render.d.ts.map +1 -1
  255. package/dist/parser/skill-render.js +98 -26
  256. package/dist/parser/skill-render.js.map +1 -1
  257. package/dist/parser/validate.d.ts +43 -3
  258. package/dist/parser/validate.d.ts.map +1 -1
  259. package/dist/parser/validate.js +204 -30
  260. package/dist/parser/validate.js.map +1 -1
  261. package/dist/parser/yaml.d.ts +47 -11
  262. package/dist/parser/yaml.d.ts.map +1 -1
  263. package/dist/parser/yaml.js +329 -149
  264. package/dist/parser/yaml.js.map +1 -1
  265. package/dist/review/checks.d.ts +97 -0
  266. package/dist/review/checks.d.ts.map +1 -0
  267. package/dist/review/checks.js +175 -0
  268. package/dist/review/checks.js.map +1 -0
  269. package/dist/review/index.d.ts +3 -0
  270. package/dist/review/index.d.ts.map +1 -0
  271. package/dist/review/index.js +3 -0
  272. package/dist/review/index.js.map +1 -0
  273. package/dist/review/subject-bindings.d.ts +83 -0
  274. package/dist/review/subject-bindings.d.ts.map +1 -0
  275. package/dist/review/subject-bindings.js +175 -0
  276. package/dist/review/subject-bindings.js.map +1 -0
  277. package/dist/schema/common.d.ts +26 -0
  278. package/dist/schema/common.d.ts.map +1 -1
  279. package/dist/schema/common.js +13 -0
  280. package/dist/schema/common.js.map +1 -1
  281. package/dist/schema/dispatch-workspace.d.ts +2643 -0
  282. package/dist/schema/dispatch-workspace.d.ts.map +1 -0
  283. package/dist/schema/dispatch-workspace.js +187 -0
  284. package/dist/schema/dispatch-workspace.js.map +1 -0
  285. package/dist/schema/inbox.d.ts +8 -8
  286. package/dist/schema/index.d.ts +2 -0
  287. package/dist/schema/index.d.ts.map +1 -1
  288. package/dist/schema/index.js +2 -0
  289. package/dist/schema/index.js.map +1 -1
  290. package/dist/schema/meta.d.ts +663 -116
  291. package/dist/schema/meta.d.ts.map +1 -1
  292. package/dist/schema/meta.js +28 -0
  293. package/dist/schema/meta.js.map +1 -1
  294. package/dist/schema/plan.d.ts +30 -19
  295. package/dist/schema/plan.d.ts.map +1 -1
  296. package/dist/schema/plan.js +3 -1
  297. package/dist/schema/plan.js.map +1 -1
  298. package/dist/schema/review-records.d.ts +2676 -0
  299. package/dist/schema/review-records.d.ts.map +1 -0
  300. package/dist/schema/review-records.js +232 -0
  301. package/dist/schema/review-records.js.map +1 -0
  302. package/dist/schema/spec.d.ts +32 -14
  303. package/dist/schema/spec.d.ts.map +1 -1
  304. package/dist/schema/spec.js +5 -0
  305. package/dist/schema/spec.js.map +1 -1
  306. package/dist/schema/task.d.ts +187 -29
  307. package/dist/schema/task.d.ts.map +1 -1
  308. package/dist/schema/task.js +12 -2
  309. package/dist/schema/task.js.map +1 -1
  310. package/dist/schema/triage.d.ts +22 -22
  311. package/dist/sessions/cache.d.ts +119 -0
  312. package/dist/sessions/cache.d.ts.map +1 -0
  313. package/dist/sessions/cache.js +284 -0
  314. package/dist/sessions/cache.js.map +1 -0
  315. package/dist/sessions/index.d.ts +1 -0
  316. package/dist/sessions/index.d.ts.map +1 -1
  317. package/dist/sessions/index.js +2 -0
  318. package/dist/sessions/index.js.map +1 -1
  319. package/dist/sessions/legacy.d.ts +77 -0
  320. package/dist/sessions/legacy.d.ts.map +1 -0
  321. package/dist/sessions/legacy.js +146 -0
  322. package/dist/sessions/legacy.js.map +1 -0
  323. package/dist/sessions/store.d.ts +115 -71
  324. package/dist/sessions/store.d.ts.map +1 -1
  325. package/dist/sessions/store.js +357 -182
  326. package/dist/sessions/store.js.map +1 -1
  327. package/dist/sessions/types.d.ts +44 -16
  328. package/dist/sessions/types.d.ts.map +1 -1
  329. package/dist/sessions/types.js +11 -2
  330. package/dist/sessions/types.js.map +1 -1
  331. package/dist/strings/errors.d.ts +32 -0
  332. package/dist/strings/errors.d.ts.map +1 -1
  333. package/dist/strings/errors.js +17 -0
  334. package/dist/strings/errors.js.map +1 -1
  335. package/dist/strings/labels.d.ts +1 -0
  336. package/dist/strings/labels.d.ts.map +1 -1
  337. package/dist/strings/labels.js +1 -0
  338. package/dist/strings/labels.js.map +1 -1
  339. package/dist/utils/activity.d.ts +101 -0
  340. package/dist/utils/activity.d.ts.map +1 -0
  341. package/dist/utils/activity.js +408 -0
  342. package/dist/utils/activity.js.map +1 -0
  343. package/dist/utils/git.d.ts +31 -0
  344. package/dist/utils/git.d.ts.map +1 -1
  345. package/dist/utils/git.js +87 -0
  346. package/dist/utils/git.js.map +1 -1
  347. package/dist/utils/index.d.ts +2 -0
  348. package/dist/utils/index.d.ts.map +1 -1
  349. package/dist/utils/index.js +1 -0
  350. package/dist/utils/index.js.map +1 -1
  351. package/dist/web-ui/_app/immutable/assets/0.tmlwn-Ih.css +1 -0
  352. package/dist/web-ui/_app/immutable/assets/9.BwwJybWx.css +1 -0
  353. package/dist/web-ui/_app/immutable/chunks/2KqE8gtn.js +1 -0
  354. package/dist/web-ui/_app/immutable/chunks/70-t_QvE.js +1 -0
  355. package/dist/web-ui/_app/immutable/chunks/AiWQj974.js +1 -0
  356. package/dist/web-ui/_app/immutable/chunks/B25nWFyA.js +5 -0
  357. package/dist/web-ui/_app/immutable/chunks/B2bcA_Q_.js +1 -0
  358. package/dist/web-ui/_app/immutable/chunks/B5e5HYyB.js +1 -0
  359. package/dist/web-ui/_app/immutable/chunks/B7-5z6eA.js +1 -0
  360. package/dist/web-ui/_app/immutable/chunks/B7bGmhK0.js +1 -0
  361. package/dist/web-ui/_app/immutable/chunks/B8tYZKAE.js +1 -0
  362. package/dist/web-ui/_app/immutable/chunks/BFGAyJjD.js +1 -0
  363. package/dist/web-ui/_app/immutable/chunks/BG0850zf.js +1 -0
  364. package/dist/web-ui/_app/immutable/chunks/BG8eSzAd.js +1 -0
  365. package/dist/web-ui/_app/immutable/chunks/BIMxXS8I.js +1 -0
  366. package/dist/web-ui/_app/immutable/chunks/BSzL1fpU.js +1 -0
  367. package/dist/web-ui/_app/immutable/chunks/BYtjHfeq.js +1 -0
  368. package/dist/web-ui/_app/immutable/chunks/{D1ArdqNb.js → Bp5pFYXL.js} +1 -1
  369. package/dist/web-ui/_app/immutable/chunks/BsJFsuAT.js +1 -0
  370. package/dist/web-ui/_app/immutable/chunks/BvpNHcD6.js +1 -0
  371. package/dist/web-ui/_app/immutable/chunks/BypqA25-.js +1 -0
  372. package/dist/web-ui/_app/immutable/chunks/C0w6WDm5.js +1 -0
  373. package/dist/web-ui/_app/immutable/chunks/C5_PAZ0y.js +1 -0
  374. package/dist/web-ui/_app/immutable/chunks/CDRO15Iv.js +1 -0
  375. package/dist/web-ui/_app/immutable/chunks/CF1CoqD5.js +1 -0
  376. package/dist/web-ui/_app/immutable/chunks/CS2sa4_m.js +1 -0
  377. package/dist/web-ui/_app/immutable/chunks/CWUQwB9H.js +1 -0
  378. package/dist/web-ui/_app/immutable/chunks/CY5FDdSU.js +1 -0
  379. package/dist/web-ui/_app/immutable/chunks/C_7MTDoj.js +1 -0
  380. package/dist/web-ui/_app/immutable/chunks/CaAJD3dl.js +1 -0
  381. package/dist/web-ui/_app/immutable/chunks/{i-XnOIX0.js → ChB5iyEL.js} +1 -1
  382. package/dist/web-ui/_app/immutable/chunks/ChQD-6N8.js +1 -0
  383. package/dist/web-ui/_app/immutable/chunks/{BCkp8Hs8.js → CqbsoCwA.js} +1 -1
  384. package/dist/web-ui/_app/immutable/chunks/DCeJW50p.js +1 -0
  385. package/dist/web-ui/_app/immutable/chunks/DJtZNgcs.js +1 -0
  386. package/dist/web-ui/_app/immutable/chunks/DKIeaprD.js +1 -0
  387. package/dist/web-ui/_app/immutable/chunks/DLd2uVIA.js +1 -0
  388. package/dist/web-ui/_app/immutable/chunks/DW_subyT.js +2 -0
  389. package/dist/web-ui/_app/immutable/chunks/DbU6lVn0.js +1 -0
  390. package/dist/web-ui/_app/immutable/chunks/Dc7ZCC5m.js +1 -0
  391. package/dist/web-ui/_app/immutable/chunks/Dd5umPsk.js +2 -0
  392. package/dist/web-ui/_app/immutable/chunks/Dg_zDpDS.js +1 -0
  393. package/dist/web-ui/_app/immutable/chunks/Dgqu8Yuc.js +1 -0
  394. package/dist/web-ui/_app/immutable/chunks/DmxsPZTB.js +1 -0
  395. package/dist/web-ui/_app/immutable/chunks/DphTaFUB.js +1 -0
  396. package/dist/web-ui/_app/immutable/chunks/DqK4iHp0.js +1 -0
  397. package/dist/web-ui/_app/immutable/chunks/DqT6OH_u.js +2 -0
  398. package/dist/web-ui/_app/immutable/chunks/Ds9I9wQb.js +1 -0
  399. package/dist/web-ui/_app/immutable/chunks/Du5ng3u4.js +1 -0
  400. package/dist/web-ui/_app/immutable/chunks/DxJw79Wi.js +1 -0
  401. package/dist/web-ui/_app/immutable/chunks/GFTX8GgV.js +1 -0
  402. package/dist/web-ui/_app/immutable/chunks/HNjs76Zz.js +1 -0
  403. package/dist/web-ui/_app/immutable/chunks/HVMjDi4_.js +1 -0
  404. package/dist/web-ui/_app/immutable/chunks/P0A_fJvS.js +1 -0
  405. package/dist/web-ui/_app/immutable/chunks/T3vGWjIL.js +1 -0
  406. package/dist/web-ui/_app/immutable/chunks/VTmrX9Qu.js +1 -0
  407. package/dist/web-ui/_app/immutable/chunks/Xvwhx_F1.js +1 -0
  408. package/dist/web-ui/_app/immutable/chunks/Yyz1XMQA.js +1 -0
  409. package/dist/web-ui/_app/immutable/chunks/dh5HeqUr.js +1 -0
  410. package/dist/web-ui/_app/immutable/chunks/fZMteyca.js +62 -0
  411. package/dist/web-ui/_app/immutable/chunks/{D28BF5MJ.js → gPrj-hqC.js} +1 -1
  412. package/dist/web-ui/_app/immutable/chunks/htcWMiYN.js +1 -0
  413. package/dist/web-ui/_app/immutable/chunks/oTsvd9y4.js +1 -0
  414. package/dist/web-ui/_app/immutable/chunks/qJfLUwU4.js +1 -0
  415. package/dist/web-ui/_app/immutable/chunks/xCtiO_JE.js +1 -0
  416. package/dist/web-ui/_app/immutable/chunks/y4GeEH6k.js +1 -0
  417. package/dist/web-ui/_app/immutable/entry/app.C4h_eOn6.js +2 -0
  418. package/dist/web-ui/_app/immutable/entry/start.CQFTf9ep.js +1 -0
  419. package/dist/web-ui/_app/immutable/nodes/0.Dh1xO970.js +1 -0
  420. package/dist/web-ui/_app/immutable/nodes/1.l75D3Opx.js +1 -0
  421. package/dist/web-ui/_app/immutable/nodes/10.DBidBPc-.js +1 -0
  422. package/dist/web-ui/_app/immutable/nodes/11.Ab0gUKWe.js +1 -0
  423. package/dist/web-ui/_app/immutable/nodes/12.CMsnoxfs.js +1 -0
  424. package/dist/web-ui/_app/immutable/nodes/13.D8YKuknB.js +1 -0
  425. package/dist/web-ui/_app/immutable/nodes/14.DZ0aan7y.js +1 -0
  426. package/dist/web-ui/_app/immutable/nodes/15.CUIKreDL.js +2 -0
  427. package/dist/web-ui/_app/immutable/nodes/16.BWc8--BO.js +1 -0
  428. package/dist/web-ui/_app/immutable/nodes/2.CDUonbuh.js +1 -0
  429. package/dist/web-ui/_app/immutable/nodes/3.Ctg3M00i.js +1 -0
  430. package/dist/web-ui/_app/immutable/nodes/4.Ci-JDwbA.js +2 -0
  431. package/dist/web-ui/_app/immutable/nodes/5.CTyEDAq0.js +1 -0
  432. package/dist/web-ui/_app/immutable/nodes/6.BTZZqsAb.js +1 -0
  433. package/dist/web-ui/_app/immutable/nodes/7.BI52g_Jo.js +137 -0
  434. package/dist/web-ui/_app/immutable/nodes/8.3hZPaB9x.js +1 -0
  435. package/dist/web-ui/_app/immutable/nodes/9.DS49kvwl.js +29 -0
  436. package/dist/web-ui/_app/version.json +1 -1
  437. package/dist/web-ui/favicon-192.png +0 -0
  438. package/dist/web-ui/favicon-32.png +0 -0
  439. package/dist/web-ui/favicon.ico +0 -0
  440. package/dist/web-ui/index.html +14 -11
  441. package/package.json +14 -7
  442. package/plugin/.claude-plugin/marketplace.json +1 -1
  443. package/plugin/.claude-plugin/plugin.json +1 -1
  444. package/plugin/plugins/kspec/skills/merge/SKILL.md +127 -0
  445. package/plugin/plugins/kspec/skills/plan/SKILL.md +55 -26
  446. package/plugin/plugins/kspec/skills/review/SKILL.md +350 -133
  447. package/plugin/plugins/kspec/skills/task-work/SKILL.md +96 -106
  448. package/templates/agents-sections/04-pr-workflow.md +15 -12
  449. package/templates/agents-sections/06-ralph-loop.md +15 -10
  450. package/templates/skills/manifest.yaml +25 -7
  451. package/templates/skills/merge/SKILL.md +120 -0
  452. package/templates/skills/plan/SKILL.md +55 -26
  453. package/templates/skills/review/SKILL.md +346 -130
  454. package/templates/skills/task-work/SKILL.md +93 -103
  455. package/dist/web-ui/_app/immutable/assets/0.BxCxvrZR.css +0 -1
  456. package/dist/web-ui/_app/immutable/chunks/B-CZR0q8.js +0 -1
  457. package/dist/web-ui/_app/immutable/chunks/B1IR5Su5.js +0 -1
  458. package/dist/web-ui/_app/immutable/chunks/B_Cvvtc4.js +0 -1
  459. package/dist/web-ui/_app/immutable/chunks/BtFaGGII.js +0 -1
  460. package/dist/web-ui/_app/immutable/chunks/Bu8JVsCH.js +0 -1
  461. package/dist/web-ui/_app/immutable/chunks/C87u-CNA.js +0 -1
  462. package/dist/web-ui/_app/immutable/chunks/CrFkBTYp.js +0 -1
  463. package/dist/web-ui/_app/immutable/chunks/D6RtLpzL.js +0 -1
  464. package/dist/web-ui/_app/immutable/chunks/D7FHSgx2.js +0 -1
  465. package/dist/web-ui/_app/immutable/chunks/DBXrsxZQ.js +0 -2
  466. package/dist/web-ui/_app/immutable/chunks/Da_hHMuA.js +0 -1
  467. package/dist/web-ui/_app/immutable/chunks/Do6LchSF.js +0 -1
  468. package/dist/web-ui/_app/immutable/chunks/DoNPtcAw.js +0 -1
  469. package/dist/web-ui/_app/immutable/chunks/DtUbXRZz.js +0 -1
  470. package/dist/web-ui/_app/immutable/chunks/DyFPRlLl.js +0 -1
  471. package/dist/web-ui/_app/immutable/chunks/DzAP8lRM.js +0 -1
  472. package/dist/web-ui/_app/immutable/chunks/DzVXElzN.js +0 -2
  473. package/dist/web-ui/_app/immutable/chunks/aoPBFken.js +0 -1
  474. package/dist/web-ui/_app/immutable/chunks/laxtrUO3.js +0 -1
  475. package/dist/web-ui/_app/immutable/chunks/q1nIWgqB.js +0 -1
  476. package/dist/web-ui/_app/immutable/chunks/sTLbk5Nm.js +0 -1
  477. package/dist/web-ui/_app/immutable/chunks/vwKgQu5P.js +0 -5
  478. package/dist/web-ui/_app/immutable/entry/app.BCwMcqnT.js +0 -2
  479. package/dist/web-ui/_app/immutable/entry/start.wKCQH-tt.js +0 -1
  480. package/dist/web-ui/_app/immutable/nodes/0.CjGVMG74.js +0 -1
  481. package/dist/web-ui/_app/immutable/nodes/1.B6_AIPan.js +0 -1
  482. package/dist/web-ui/_app/immutable/nodes/2.q4oCS7Ws.js +0 -1
  483. package/dist/web-ui/_app/immutable/nodes/3.rTKZf9o2.js +0 -1
  484. package/dist/web-ui/_app/immutable/nodes/4.DVIDRu1d.js +0 -1
  485. package/dist/web-ui/_app/immutable/nodes/5.8PtPXIOd.js +0 -1
  486. package/dist/web-ui/_app/immutable/nodes/6.ZZrTemy_.js +0 -1
  487. package/dist/web-ui/_app/immutable/nodes/7.IP-gxCxi.js +0 -1
@@ -19,12 +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';
28
+ import { getCompletedSessionCountsByAgent } from '../../sessions/store.js';
27
29
  import type { PubSubManager } from '../websocket/pubsub.js';
30
+ import type { SessionEventData } from '@kynetic-ai/shared';
28
31
 
29
32
  const VALID_TASK_STATUSES = new Set<string>([
30
33
  "pending", "in_progress", "pending_review", "needs_work", "blocked", "completed", "cancelled",
@@ -43,31 +46,33 @@ export interface AgentDispatchRouteOptions {
43
46
  * Create a new dispatch engine with optional WebSocket broadcast wiring.
44
47
  * AC: @daemon-agent-dispatch ac-3, ac-4
45
48
  */
46
- function createEngine(projectDir: string, pubsub?: PubSubManager): DispatchEngine {
49
+ function createEngine(
50
+ projectDir: string,
51
+ cwd?: string,
52
+ pubsub?: PubSubManager,
53
+ ): DispatchEngine {
47
54
  return new DispatchEngine({
48
55
  projectDir,
56
+ cwd,
49
57
  kspecCliPath: DEFAULT_KSPEC_CLI_PATH,
50
58
  onInvocationEvent: pubsub
51
59
  ? (event: InvocationEvent) => {
60
+ // AC: @ui-api-aggregation ac-4 - Include task_title for display
52
61
  pubsub.broadcast('agents', 'agent_invocation', {
53
62
  session_id: event.session_id,
54
63
  agent_id: event.agent_id,
55
64
  task_id: event.task_id ?? null,
65
+ task_title: event.task_title ?? null,
56
66
  status: event.status,
57
67
  timestamp: event.timestamp,
58
68
  }, projectDir);
59
69
  }
60
70
  : undefined,
71
+ // AC: @session-event-broadcast ac-replaces-text-chunks
61
72
  // AC: @cli-agent-commands ac-13, @daemon-agent-dispatch ac-8
62
- onTextChunk: pubsub
63
- ? (sessionId: string, agentId: string, taskId: string | null, text: string) => {
64
- pubsub.broadcast('agents', 'agent_text_chunk', {
65
- session_id: sessionId,
66
- agent_id: agentId,
67
- task_id: taskId,
68
- text,
69
- timestamp: Date.now(),
70
- }, projectDir);
73
+ onSessionEvent: pubsub
74
+ ? (event: SessionEventData) => {
75
+ pubsub.broadcast('agents', event.type, event, projectDir);
71
76
  }
72
77
  : undefined,
73
78
  });
@@ -116,6 +121,28 @@ function processStateChangeEvent(
116
121
  return { accepted: true };
117
122
  }
118
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
+
119
146
  export function createAgentDispatchRoutes(options: AgentDispatchRouteOptions = {}) {
120
147
  const { pubsub } = options;
121
148
 
@@ -133,16 +160,34 @@ export function createAgentDispatchRoutes(options: AgentDispatchRouteOptions = {
133
160
  }, { body: stateChangeBodySchema })
134
161
 
135
162
  // AC: @daemon-agent-dispatch ac-6 - Unified dispatch start/stop via action field
136
- .post('/dispatch', async ({ body, projectContext }) => {
163
+ .post('/dispatch', async ({ body, projectContext, request, set }) => {
137
164
  const projectDir = projectContext.path;
138
165
 
139
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
+
140
178
  let engine = engines.get(projectDir);
141
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
+ }
142
187
  return { dispatch_enabled: true, reason: 'Already running' };
143
188
  }
144
189
 
145
- engine = createEngine(projectDir, pubsub);
190
+ engine = createEngine(projectDir, requestedCwd, pubsub);
146
191
  engines.set(projectDir, engine);
147
192
  await engine.start();
148
193
 
@@ -165,16 +210,34 @@ export function createAgentDispatchRoutes(options: AgentDispatchRouteOptions = {
165
210
  })
166
211
 
167
212
  // Start dispatch engine (legacy route)
168
- .post('/dispatch/start', async ({ projectContext }) => {
213
+ .post('/dispatch/start', async ({ projectContext, request, set }) => {
169
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
+ }
170
225
 
171
226
  let engine = engines.get(projectDir);
172
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
+ }
173
236
  return { started: false, reason: 'Already running', status: engine.getStatus() };
174
237
  }
175
238
 
176
239
  // AC: @agent-dispatch-engine ac-10 - pass kspecCliPath so task notes work from daemon-started engine
177
- engine = createEngine(projectDir, pubsub);
240
+ engine = createEngine(projectDir, requestedCwd, pubsub);
178
241
  engines.set(projectDir, engine);
179
242
 
180
243
  await engine.start();
@@ -214,18 +277,29 @@ export function createAgentDispatchRoutes(options: AgentDispatchRouteOptions = {
214
277
  })
215
278
 
216
279
  // AC: @daemon-agent-dispatch ac-5 - Public status endpoint
280
+ // AC: @ui-api-ref-resolution ac-1 - Include task_title for active invocations
217
281
  .get('/status', async ({ projectContext }) => {
218
282
  const projectDir = projectContext.path;
219
283
  const engineStatus = engines.get(projectDir)?.getStatus();
220
284
 
221
- let agentDefinitions: Array<{ id: string; name: string; adapter: string }> = [];
285
+ let agentDefinitions: Array<{ id: string; name: string; adapter: string; completed_sessions: number }> = [];
286
+ let completedCounts: Record<string, number> = {};
287
+ let refIndex: ReferenceIndex | null = null;
222
288
  try {
223
289
  const ctx = await initContext(projectDir);
224
- const meta = await loadMetaContext(ctx);
290
+ const [meta, counts, tasks, items] = await Promise.all([
291
+ loadMetaContext(ctx),
292
+ getCompletedSessionCountsByAgent(ctx.specDir),
293
+ loadAllTasks(ctx),
294
+ loadAllItems(ctx),
295
+ ]);
296
+ completedCounts = counts;
297
+ refIndex = new ReferenceIndex(tasks, items);
225
298
  agentDefinitions = meta.agents.map((a) => ({
226
299
  id: a.id,
227
300
  name: a.name,
228
301
  adapter: a.adapter ?? 'claude-agent-acp',
302
+ completed_sessions: completedCounts[a.id] ?? 0,
229
303
  }));
230
304
  } catch {
231
305
  // Agent definitions unavailable — return empty array
@@ -233,12 +307,22 @@ export function createAgentDispatchRoutes(options: AgentDispatchRouteOptions = {
233
307
 
234
308
  return {
235
309
  dispatch_enabled: engineStatus?.running ?? false,
236
- active_invocations: engineStatus?.invocations?.map((inv) => ({
237
- session_id: inv.sessionId,
238
- agent_id: inv.agentId,
239
- task_ref: inv.taskRef ?? null,
240
- elapsed_ms: inv.elapsedMs,
241
- })) ?? [],
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
+ }) ?? [],
242
326
  queue_depth: engineStatus?.queuedInvocations ?? 0,
243
327
  agent_definitions: agentDefinitions,
244
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
@@ -18,12 +18,17 @@ import {
18
18
  initContext,
19
19
  loadAllItems,
20
20
  loadAllTasks,
21
+ loadPlans,
22
+ findItemByRef,
23
+ findTaskByRef,
21
24
  ReferenceIndex,
22
25
  AlignmentIndex,
23
26
  getCachedTestCoverage,
24
27
  computeACCoverage,
25
28
  type LoadedSpecItem,
29
+ type LoadedTask,
26
30
  } from '../../parser/index.js';
31
+ import { getRelatedSessionsForItem } from './session-related.js';
27
32
 
28
33
  interface ItemsRouteOptions {}
29
34
 
@@ -88,6 +93,49 @@ function computeParentMap(items: LoadedSpecItem[]): Map<string, string | undefin
88
93
  return parentMap;
89
94
  }
90
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
+
91
139
  export function createItemsRoutes(options: ItemsRouteOptions = {}) {
92
140
  // No closure-scoped kspecDir needed - comes from middleware
93
141
 
@@ -144,6 +192,26 @@ export function createItemsRoutes(options: ItemsRouteOptions = {}) {
144
192
  );
145
193
  }
146
194
 
195
+ // Plan filter — show only specs derived from a given plan
196
+ if (query.plan) {
197
+ const plans = await loadPlans(ctx);
198
+ const plan = plans.find(
199
+ (p) => p._ulid === query.plan || p.slugs.includes(query.plan!)
200
+ );
201
+ if (plan) {
202
+ const derivedRefs = new Set(
203
+ plan.derived_specs.map((r) => (r.startsWith('@') ? r.slice(1) : r))
204
+ );
205
+ filtered = filtered.filter(
206
+ (item) =>
207
+ derivedRefs.has(item._ulid) ||
208
+ item.slugs.some((s) => derivedRefs.has(s))
209
+ );
210
+ } else {
211
+ filtered = [];
212
+ }
213
+ }
214
+
147
215
  // Pagination
148
216
  const total = filtered.length;
149
217
  const offset = Number(query.offset) || 0;
@@ -178,12 +246,80 @@ export function createItemsRoutes(options: ItemsRouteOptions = {}) {
178
246
  maturity: t.Optional(t.Union([t.String(), t.Array(t.String())])),
179
247
  implementation: t.Optional(t.Union([t.String(), t.Array(t.String())])),
180
248
  tag: t.Optional(t.Union([t.String(), t.Array(t.String())])),
249
+ plan: t.Optional(t.String()),
181
250
  limit: t.Optional(t.String()),
182
251
  offset: t.Optional(t.String()),
183
252
  }),
184
253
  }
185
254
  )
186
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
+
187
323
  // AC: @api-contract ac-10 - Get single item by ref
188
324
  .get(
189
325
  '/:ref',
@@ -318,5 +454,36 @@ export function createItemsRoutes(options: ItemsRouteOptions = {}) {
318
454
  ref: t.String(),
319
455
  }),
320
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
+ }
321
488
  );
322
489
  }