@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
@@ -4,14 +4,20 @@
4
4
  * REST endpoints for meta operations:
5
5
  * - GET /api/meta/session - get session context
6
6
  * - GET /api/meta/agents - list agents
7
+ * - PATCH /api/meta/agents/:id - update agent definition
7
8
  * - GET /api/meta/workflows - list workflows
8
9
  * - GET /api/meta/observations - list observations with filter
10
+ * - GET /api/meta/config - project config from manifest + kspec.config.yaml
11
+ * - GET /api/meta/shadow - shadow branch status
12
+ * - GET /api/meta/conventions - convention definitions
9
13
  *
10
14
  * AC Coverage:
11
15
  * - ac-15: GET /api/meta/session returns session context
12
16
  * - ac-16: GET /api/meta/agents returns all agents
13
17
  * - ac-17: GET /api/meta/workflows returns all workflows
14
18
  * - ac-18: GET /api/meta/observations with filter
19
+ * - @ui-agent-dispatch ac-4: PATCH /api/meta/agents/:id updates agent definition
20
+ * - @ui-settings-view ac-1: GET /api/meta/config, /shadow, /conventions
15
21
  */
16
22
 
17
23
  import { Elysia, t } from 'elysia';
@@ -19,7 +25,11 @@ import {
19
25
  initContext,
20
26
  loadMetaContext,
21
27
  loadSessionContext,
28
+ saveMetaItem,
22
29
  } from '../../parser/index.js';
30
+ import { commitIfShadow, getShadowStatus, hasRemoteTracking } from '../../parser/shadow.js';
31
+ import type { Agent } from '../../schema/meta.js';
32
+ import { AgentDispatchEventSchema } from '../../schema/meta.js';
23
33
 
24
34
  interface MetaRouteOptions {}
25
35
 
@@ -57,6 +67,73 @@ export function createMetaRoutes(options: MetaRouteOptions = {}) {
57
67
  };
58
68
  })
59
69
 
70
+ // AC: @ui-agent-dispatch ac-4 - Update agent definition
71
+ // Body schema mirrors editable fields from AgentSchema (src/schema/meta.ts).
72
+ // Dispatch event enum values derived from AgentDispatchEventSchema.
73
+ .patch(
74
+ '/agents/:id',
75
+ async ({ params, body, projectContext }) => {
76
+ const ctx = await initContext(projectContext.path);
77
+ const meta = await loadMetaContext(ctx);
78
+
79
+ // Find the agent by id
80
+ const agent = meta.agents.find((a) => a.id === params.id);
81
+ if (!agent) {
82
+ throw new Error(`Agent not found: ${params.id}`);
83
+ }
84
+
85
+ // Apply partial updates from body — result satisfies Agent type from schema
86
+ const updated: Agent = { ...agent, ...body };
87
+
88
+ await saveMetaItem(ctx, updated, 'agent');
89
+ await commitIfShadow(ctx.shadow, `meta: update agent ${params.id}`);
90
+
91
+ return updated;
92
+ },
93
+ {
94
+ params: t.Object({
95
+ id: t.String(),
96
+ }),
97
+ // Editable fields from AgentSchema — dispatch.on enum derived from AgentDispatchEventSchema
98
+ body: t.Object({
99
+ name: t.Optional(t.String()),
100
+ description: t.Optional(t.String()),
101
+ adapter: t.Optional(t.String()),
102
+ dispatch: t.Optional(
103
+ t.Array(
104
+ t.Object({
105
+ on: t.Union(AgentDispatchEventSchema.options.map((v) => t.Literal(v))),
106
+ filter: t.Optional(
107
+ t.Object({
108
+ automation: t.Optional(t.Union([t.Literal('eligible'), t.Literal('ineligible')])),
109
+ tags: t.Optional(t.Array(t.String())),
110
+ priority: t.Optional(t.Number()),
111
+ })
112
+ ),
113
+ })
114
+ )
115
+ ),
116
+ capabilities: t.Optional(t.Array(t.String())),
117
+ tools: t.Optional(t.Array(t.String())),
118
+ skills: t.Optional(t.Array(t.String())),
119
+ budget: t.Optional(
120
+ t.Object({
121
+ max_tasks: t.Optional(t.Number()),
122
+ max_retries: t.Optional(t.Number()),
123
+ timeout_minutes: t.Optional(t.Number()),
124
+ })
125
+ ),
126
+ concurrency: t.Optional(
127
+ t.Object({
128
+ max_concurrent: t.Optional(t.Number()),
129
+ })
130
+ ),
131
+ auto_approve: t.Optional(t.Boolean()),
132
+ prompt_template: t.Optional(t.String()),
133
+ }),
134
+ }
135
+ )
136
+
60
137
  // AC: @api-contract ac-17 - List workflows
61
138
  .get('/workflows', async ({ projectContext }) => {
62
139
  // AC: @multi-directory-daemon ac-1, ac-24 - Use project context from middleware
@@ -114,5 +191,68 @@ export function createMetaRoutes(options: MetaRouteOptions = {}) {
114
191
  type: t.Optional(t.Union([t.String(), t.Array(t.String())])),
115
192
  }),
116
193
  }
117
- );
194
+ )
195
+
196
+ // AC: @ui-settings-view ac-1 - Project config from manifest
197
+ .get('/config', async ({ projectContext }) => {
198
+ const ctx = await initContext(projectContext.path);
199
+ const manifest = ctx.manifest;
200
+ const config = ctx.config;
201
+
202
+ return {
203
+ project: manifest?.project ?? null,
204
+ spec_version: manifest?.kynetic ?? null,
205
+ root_dir: ctx.projectRoot,
206
+ remote_tracking: config.shadow.remote
207
+ ? { value: config.shadow.remote.value, type: config.shadow.remote.type }
208
+ : null,
209
+ daemon: {
210
+ port: config.daemon.port,
211
+ host: config.daemon.host,
212
+ auto_start: config.daemon.auto_start,
213
+ },
214
+ };
215
+ })
216
+
217
+ // AC: @ui-settings-view ac-1 - Shadow branch status
218
+ .get('/shadow', async ({ projectContext }) => {
219
+ const ctx = await initContext(projectContext.path);
220
+
221
+ if (!ctx.shadow) {
222
+ return {
223
+ enabled: false,
224
+ branch_name: null,
225
+ worktree_dir: null,
226
+ healthy: false,
227
+ remote_tracking: false,
228
+ };
229
+ }
230
+
231
+ const status = await getShadowStatus(ctx.rootDir, {
232
+ branchName: ctx.shadow.branchName,
233
+ directory: ctx.config.shadow.directory,
234
+ });
235
+ const hasRemote = await hasRemoteTracking(ctx.shadow.worktreeDir, {
236
+ branchName: ctx.shadow.branchName,
237
+ });
238
+
239
+ return {
240
+ enabled: ctx.shadow.enabled,
241
+ branch_name: ctx.shadow.branchName,
242
+ worktree_dir: ctx.shadow.worktreeDir,
243
+ healthy: status.healthy,
244
+ remote_tracking: hasRemote,
245
+ };
246
+ })
247
+
248
+ // AC: @ui-settings-view ac-1 - Convention definitions
249
+ .get('/conventions', async ({ projectContext }) => {
250
+ const ctx = await initContext(projectContext.path);
251
+ const meta = await loadMetaContext(ctx);
252
+
253
+ return {
254
+ items: meta.conventions,
255
+ total: meta.conventions.length,
256
+ };
257
+ });
118
258
  }
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Plans API Routes
3
+ *
4
+ * REST endpoints for plan operations:
5
+ * - GET /api/plans - list plans with status filter and task progress
6
+ * - GET /api/plans/:ref - get single plan with content
7
+ *
8
+ * AC Coverage:
9
+ * - @ui-plans-view ac-1: Plan list with title, status, dates, linked counts, progress
10
+ * - @ui-plans-view ac-2: Plan detail with content for expand/detail view
11
+ */
12
+
13
+ import { Elysia, t } from 'elysia';
14
+ import {
15
+ initContext,
16
+ loadPlans,
17
+ loadAllTasks,
18
+ findPlanByRef,
19
+ type LoadedPlan,
20
+ } from '../../parser/index.js';
21
+ import type { PlanSummary, PlanDetail } from '@kynetic-ai/shared';
22
+
23
+ interface PlansRouteOptions {}
24
+
25
+ /**
26
+ * Build a task status lookup map from loaded tasks.
27
+ */
28
+ function buildTaskStatusMap(tasks: Array<{ _ulid: string; slugs: string[]; status: string }>) {
29
+ const tasksByRef = new Map<string, { status: string }>();
30
+ for (const task of tasks) {
31
+ tasksByRef.set(task._ulid, { status: task.status });
32
+ for (const slug of task.slugs) {
33
+ tasksByRef.set(slug, { status: task.status });
34
+ }
35
+ }
36
+ return tasksByRef;
37
+ }
38
+
39
+ /**
40
+ * Compute task progress for a plan's derived tasks.
41
+ */
42
+ function computeTaskProgress(
43
+ derivedTasks: string[],
44
+ tasksByRef: Map<string, { status: string }>
45
+ ) {
46
+ const progress = { total: 0, completed: 0, in_progress: 0, pending: 0, blocked: 0 };
47
+ for (const ref of derivedTasks) {
48
+ const cleanRef = ref.startsWith('@') ? ref.slice(1) : ref;
49
+ const task = tasksByRef.get(cleanRef);
50
+ if (task) {
51
+ progress.total++;
52
+ if (task.status === 'completed') progress.completed++;
53
+ else if (task.status === 'in_progress' || task.status === 'pending_review' || task.status === 'needs_work') progress.in_progress++;
54
+ else if (task.status === 'blocked') progress.blocked++;
55
+ else progress.pending++;
56
+ }
57
+ }
58
+ return progress;
59
+ }
60
+
61
+ /**
62
+ * Map a loaded plan to a PlanSummary.
63
+ */
64
+ function toPlanSummary(
65
+ plan: LoadedPlan,
66
+ tasksByRef: Map<string, { status: string }>
67
+ ): PlanSummary {
68
+ return {
69
+ _ulid: plan._ulid,
70
+ slugs: plan.slugs,
71
+ title: plan.title,
72
+ status: plan.status,
73
+ created_at: plan.created_at,
74
+ approved_at: plan.approved_at ?? undefined,
75
+ completed_at: plan.completed_at ?? undefined,
76
+ derived_specs: plan.derived_specs,
77
+ derived_tasks: plan.derived_tasks,
78
+ spec_count: plan.derived_specs.length,
79
+ task_count: plan.derived_tasks.length,
80
+ task_progress: computeTaskProgress(plan.derived_tasks, tasksByRef),
81
+ };
82
+ }
83
+
84
+ export function createPlansRoutes(options: PlansRouteOptions = {}) {
85
+ return new Elysia({ prefix: '/api/plans' })
86
+ // AC: @ui-plans-view ac-1 - List plans with progress
87
+ .get(
88
+ '/',
89
+ async ({ query, projectContext }) => {
90
+ const ctx = await initContext(projectContext.path);
91
+ const plans = await loadPlans(ctx);
92
+ const tasks = await loadAllTasks(ctx);
93
+ const tasksByRef = buildTaskStatusMap(tasks);
94
+
95
+ // Apply status filter
96
+ let filtered: LoadedPlan[] = plans;
97
+ if (query.status) {
98
+ const statusFilters = Array.isArray(query.status) ? query.status : [query.status];
99
+ filtered = filtered.filter((plan) => statusFilters.includes(plan.status));
100
+ }
101
+
102
+ // Sort by created_at descending (newest first)
103
+ const sorted = [...filtered].sort(
104
+ (a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()
105
+ );
106
+
107
+ // AC: @ui-plans-view ac-1 - Compute progress for each plan
108
+ const items: PlanSummary[] = sorted.map((plan) => toPlanSummary(plan, tasksByRef));
109
+
110
+ return {
111
+ items,
112
+ total: items.length,
113
+ };
114
+ },
115
+ {
116
+ query: t.Object({
117
+ status: t.Optional(t.Union([t.String(), t.Array(t.String())])),
118
+ }),
119
+ }
120
+ )
121
+ // AC: @ui-plans-view ac-2 - Get single plan with content (lazy-loaded by UI on expand)
122
+ .get(
123
+ '/:ref',
124
+ async ({ params, error: errorResponse, projectContext }) => {
125
+ const ctx = await initContext(projectContext.path);
126
+ const plan = await findPlanByRef(ctx, params.ref);
127
+
128
+ if (!plan) {
129
+ return errorResponse(404, {
130
+ error: 'not_found',
131
+ message: `Plan reference "${params.ref}" not found`,
132
+ suggestion: 'Use kspec plan list to find valid plan references',
133
+ });
134
+ }
135
+
136
+ const tasks = await loadAllTasks(ctx);
137
+ const tasksByRef = buildTaskStatusMap(tasks);
138
+
139
+ const detail: PlanDetail = {
140
+ ...toPlanSummary(plan, tasksByRef),
141
+ content: plan.content,
142
+ };
143
+
144
+ return detail;
145
+ }
146
+ );
147
+ }
@@ -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, {
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Reference Resolution Utilities
3
+ *
4
+ * Server-side resolution of @references to display-ready metadata.
5
+ * Used by API routes to include resolved titles alongside raw refs.
6
+ *
7
+ * AC: @ui-api-ref-resolution ac-1, ac-2, ac-3
8
+ */
9
+
10
+ import type { ReferenceIndex } from '../../parser/index.js';
11
+
12
+ /**
13
+ * Resolved metadata for a single-valued reference.
14
+ * AC: @ui-api-ref-resolution ac-1
15
+ */
16
+ export interface ResolvedRef {
17
+ ref: string;
18
+ title: string | null;
19
+ }
20
+
21
+ /**
22
+ * Resolved metadata for an array reference entry with status.
23
+ * AC: @ui-api-ref-resolution ac-2
24
+ */
25
+ export interface ResolvedRefEntry {
26
+ ref: string;
27
+ title: string | null;
28
+ status: string | null;
29
+ }
30
+
31
+ /**
32
+ * Lightweight index entry for the ref index endpoint.
33
+ * AC: @ui-api-ref-resolution ac-4, ac-5
34
+ */
35
+ export interface RefIndexEntry {
36
+ title: string;
37
+ type: string;
38
+ status?: string;
39
+ }
40
+
41
+ /**
42
+ * Resolve a single-valued reference to its title.
43
+ * Returns null title if the ref cannot be resolved (ac-3).
44
+ *
45
+ * AC: @ui-api-ref-resolution ac-1, ac-3
46
+ */
47
+ export function resolveRefTitle(
48
+ index: ReferenceIndex,
49
+ ref: string | undefined | null,
50
+ ): string | null {
51
+ if (!ref) return null;
52
+ const result = index.resolve(ref);
53
+ if (!result.ok) return null;
54
+ const item = result.item as { title?: string };
55
+ return item.title ?? null;
56
+ }
57
+
58
+ /**
59
+ * Resolve an array of references to entries with title and status.
60
+ * Invalid/deleted refs get null title but preserve the raw ref (ac-3).
61
+ *
62
+ * AC: @ui-api-ref-resolution ac-2, ac-3
63
+ */
64
+ export function resolveRefEntries(
65
+ index: ReferenceIndex,
66
+ refs: string[] | undefined | null,
67
+ ): ResolvedRefEntry[] {
68
+ if (!refs || refs.length === 0) return [];
69
+ return refs.map((ref) => {
70
+ const result = index.resolve(ref);
71
+ if (!result.ok) {
72
+ return { ref, title: null, status: null };
73
+ }
74
+ const item = result.item as { title?: string; status?: string };
75
+ return {
76
+ ref,
77
+ title: item.title ?? null,
78
+ status: item.status ?? null,
79
+ };
80
+ });
81
+ }
82
+
83
+ /**
84
+ * Build a lightweight ref index map for the index endpoint.
85
+ * Includes both ULID and slug keys for each entity.
86
+ *
87
+ * AC: @ui-api-ref-resolution ac-4, ac-5
88
+ */
89
+ export function buildRefIndex(
90
+ index: ReferenceIndex,
91
+ ): Record<string, RefIndexEntry> {
92
+ const result: Record<string, RefIndexEntry> = {};
93
+
94
+ for (const ulid of index.getAllUlids()) {
95
+ const item = index.getByUlid(ulid);
96
+ if (!item) continue;
97
+
98
+ const typed = item as { title?: string; type?: string; status?: string; slugs?: string[] };
99
+ if (!typed.title) continue;
100
+
101
+ const entry: RefIndexEntry = {
102
+ title: typed.title,
103
+ type: typed.type ?? 'unknown',
104
+ ...(typed.status ? { status: typed.status } : {}),
105
+ };
106
+
107
+ // Index by ULID
108
+ result[ulid] = entry;
109
+
110
+ // Index by slugs
111
+ if (typed.slugs) {
112
+ for (const slug of typed.slugs) {
113
+ result[slug] = entry;
114
+ }
115
+ }
116
+ }
117
+
118
+ return result;
119
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Ref Index API Route
3
+ *
4
+ * Lightweight endpoint for resolving arbitrary refs to display metadata.
5
+ * Returns a map of all resolvable refs (tasks, items, traits) with
6
+ * title, type, and status. Both ULID and slug keys are included.
7
+ *
8
+ * AC Coverage:
9
+ * - @ui-api-ref-resolution ac-4: Returns map of all resolvable refs with display metadata
10
+ * - @ui-api-ref-resolution ac-5: Payload is significantly smaller than full entity lists
11
+ * - @trait-api-endpoint ac-1: Returns 2xx with JSON body
12
+ * - @trait-api-endpoint ac-6: Includes X-Request-Id header (via middleware)
13
+ */
14
+
15
+ import { Elysia } from 'elysia';
16
+ import {
17
+ initContext,
18
+ loadAllTasks,
19
+ loadAllItems,
20
+ loadPlans,
21
+ ReferenceIndex,
22
+ } from '../../parser/index.js';
23
+ import { buildRefIndex } from './ref-resolution.js';
24
+
25
+ export function createRefsRoutes() {
26
+ return new Elysia({ prefix: '/api/refs' })
27
+
28
+ // AC: @ui-api-ref-resolution ac-4, ac-5 - Lightweight ref index endpoint
29
+ // AC: @trait-api-endpoint ac-1 - Returns 2xx with JSON body
30
+ .get('/', async ({ projectContext }) => {
31
+ const ctx = await initContext(projectContext.path);
32
+ const [tasks, items, plans] = await Promise.all([
33
+ loadAllTasks(ctx),
34
+ loadAllItems(ctx),
35
+ loadPlans(ctx),
36
+ ]);
37
+ const index = new ReferenceIndex(tasks, items, [], plans);
38
+ const refs = buildRefIndex(index);
39
+
40
+ return { refs };
41
+ });
42
+ }
@@ -0,0 +1,140 @@
1
+ import {
2
+ AlignmentIndex,
3
+ ReferenceIndex,
4
+ type LoadedSpecItem,
5
+ type LoadedTask,
6
+ } from '../../parser/index.js';
7
+ import { getSessionCache } from '../../sessions/cache.js';
8
+ import type { SessionLogSummary } from '../../sessions/store.js';
9
+
10
+ interface RelatedSessionsNotFound {
11
+ error: 'not_found';
12
+ message: string;
13
+ suggestion: string;
14
+ }
15
+
16
+ function sortSessions(sessions: SessionLogSummary[]): SessionLogSummary[] {
17
+ return [...sessions].sort(
18
+ (a, b) => new Date(b.started_at).getTime() - new Date(a.started_at).getTime()
19
+ );
20
+ }
21
+
22
+ function buildTaskRefSet(task: Pick<LoadedTask, '_ulid' | 'slugs'>): Set<string> {
23
+ const refs = new Set<string>([task._ulid, `@${task._ulid}`]);
24
+ for (const slug of task.slugs) {
25
+ refs.add(slug);
26
+ refs.add(`@${slug}`);
27
+ }
28
+ return refs;
29
+ }
30
+
31
+ function filterSessionsByTaskRefs(
32
+ sessions: SessionLogSummary[],
33
+ taskRefs: Set<string>
34
+ ): SessionLogSummary[] {
35
+ return sessions.filter((session) => {
36
+ if (!session.task_id) return false;
37
+ const taskId = session.task_id.startsWith('@')
38
+ ? session.task_id.slice(1)
39
+ : session.task_id;
40
+ return taskRefs.has(taskId) || taskRefs.has(session.task_id);
41
+ });
42
+ }
43
+
44
+ export async function getRelatedSessionsForTask(params: {
45
+ items: LoadedSpecItem[];
46
+ tasks: LoadedTask[];
47
+ taskRef: string;
48
+ sessionsDir: string;
49
+ }): Promise<
50
+ | { sessions: SessionLogSummary[]; task: LoadedTask }
51
+ | { error: RelatedSessionsNotFound }
52
+ > {
53
+ const { items, tasks, taskRef, sessionsDir } = params;
54
+ const refIndex = new ReferenceIndex(tasks, items);
55
+ const resolved = refIndex.resolve(taskRef);
56
+
57
+ if (!resolved.ok) {
58
+ return {
59
+ error: {
60
+ error: 'not_found',
61
+ message: `Task reference "${taskRef}" not found`,
62
+ suggestion: 'Use GET /api/tasks or kspec task list to find valid task references',
63
+ },
64
+ };
65
+ }
66
+
67
+ const task = tasks.find((candidate) => candidate._ulid === resolved.ulid);
68
+ if (!task) {
69
+ return {
70
+ error: {
71
+ error: 'not_found',
72
+ message: `Reference "${taskRef}" is not a task`,
73
+ suggestion: 'This reference might point to a spec item instead',
74
+ },
75
+ };
76
+ }
77
+
78
+ const sessionCache = getSessionCache(sessionsDir);
79
+ const sessions = await sessionCache.getAll(sessionsDir);
80
+ const filtered = filterSessionsByTaskRefs(sessions, buildTaskRefSet(task));
81
+
82
+ return {
83
+ task,
84
+ sessions: sortSessions(filtered),
85
+ };
86
+ }
87
+
88
+ export async function getRelatedSessionsForItem(params: {
89
+ itemRef: string;
90
+ items: LoadedSpecItem[];
91
+ tasks: LoadedTask[];
92
+ sessionsDir: string;
93
+ }): Promise<
94
+ | { item: LoadedSpecItem; sessions: SessionLogSummary[] }
95
+ | { error: RelatedSessionsNotFound }
96
+ > {
97
+ const { itemRef, items, tasks, sessionsDir } = params;
98
+ const refIndex = new ReferenceIndex(tasks, items);
99
+ const alignmentIndex = new AlignmentIndex(tasks, items);
100
+ alignmentIndex.buildLinks(refIndex);
101
+
102
+ const resolved = refIndex.resolve(itemRef);
103
+ if (!resolved.ok) {
104
+ return {
105
+ error: {
106
+ error: 'not_found',
107
+ message: `Item reference "${itemRef}" not found`,
108
+ suggestion: 'Use GET /api/items or kspec item list to find valid item references',
109
+ },
110
+ };
111
+ }
112
+
113
+ const item = items.find((candidate) => candidate._ulid === resolved.ulid);
114
+ if (!item) {
115
+ return {
116
+ error: {
117
+ error: 'not_found',
118
+ message: `Reference "${itemRef}" is not a spec item`,
119
+ suggestion: 'This reference might point to a task instead',
120
+ },
121
+ };
122
+ }
123
+
124
+ const linkedTasks = alignmentIndex.getTasksForSpec(resolved.ulid);
125
+ const taskRefs = new Set<string>();
126
+ for (const task of linkedTasks) {
127
+ for (const ref of buildTaskRefSet(task)) {
128
+ taskRefs.add(ref);
129
+ }
130
+ }
131
+
132
+ const sessionCache = getSessionCache(sessionsDir);
133
+ const sessions = await sessionCache.getAll(sessionsDir);
134
+ const filtered = filterSessionsByTaskRefs(sessions, taskRefs);
135
+
136
+ return {
137
+ item,
138
+ sessions: sortSessions(filtered),
139
+ };
140
+ }