@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
@@ -5,16 +5,16 @@
5
5
  import * as fs from "node:fs/promises";
6
6
  import * as path from "node:path";
7
7
  import { markMutating } from "../command-annotations.js";
8
- import { buildIndexes, createPlan, createTask, filterPlansByStatus, getAuthor, initContext, loadAllTasks, loadPlans, mutatePlanAtomically, savePlan, saveTask, shortestUniqueUlid, } from "../../parser/index.js";
8
+ import { addChildItem, addProjectLevelTraitItem, buildIndexes, createPlan, createSpecItem, createTask, findPlanByRef, filterPlansByStatus, getAuthor, initContext, loadPlans, mutatePlanAtomically, savePlan, saveTask, shortestUniqueUlid, } from "../../parser/index.js";
9
9
  import { commitIfShadow } from "../../parser/shadow.js";
10
+ import { parsePlanDocument, topologicalSort, } from "../../parser/plan-document.js";
10
11
  import { errors } from "../../strings/index.js";
11
12
  import { fieldLabels } from "../../strings/labels.js";
12
13
  import { formatRelativeTime as formatRelativeTimeUtil } from "../../utils/time.js";
13
14
  import { EXIT_CODES } from "../exit-codes.js";
14
- import { error, info, output, success } from "../output.js";
15
+ import { error, info, isJsonMode, output, success, warn } from "../output.js";
15
16
  import { ulid } from "ulid";
16
17
  import { registerPlanImportCommand } from "./plan-import.js";
17
- import { parseIntOption } from "../validators.js";
18
18
  /**
19
19
  * Format relative time for display
20
20
  */
@@ -31,14 +31,389 @@ function resolvePlanRef(ref, plans) {
31
31
  p._ulid.toLowerCase().startsWith(cleanRef.toLowerCase()) ||
32
32
  p.slugs.includes(cleanRef));
33
33
  if (!plan) {
34
- error(errors.reference.planNotFound(ref));
35
- process.exit(EXIT_CODES.NOT_FOUND);
34
+ exitDeriveWithGuidance(errors.reference.planNotFound(ref), EXIT_CODES.NOT_FOUND, "Check available plans with: kspec plan list", {
35
+ ref,
36
+ entity: "plan",
37
+ });
36
38
  }
37
39
  return plan;
38
40
  }
39
41
  function shortPlanRef(plan, plans) {
40
42
  return shortestUniqueUlid(plan._ulid, plans.map((candidate) => candidate._ulid));
41
43
  }
44
+ async function resolveDeriveModuleRef(ctx, plans, foundPlan, moduleOption) {
45
+ const moduleRef = moduleOption ?? foundPlan.module_ref ?? null;
46
+ if (!moduleRef) {
47
+ return null;
48
+ }
49
+ const { refIndex } = await buildIndexes(ctx, plans);
50
+ const moduleResult = refIndex.resolve(moduleRef);
51
+ if (!moduleResult.ok) {
52
+ exitDeriveWithGuidance(errors.reference.itemNotFound(moduleRef), EXIT_CODES.NOT_FOUND, "Check available modules with: kspec item list --type module", {
53
+ ref: moduleRef,
54
+ entity: "module",
55
+ });
56
+ }
57
+ const moduleItem = moduleResult.item;
58
+ if (moduleItem.type !== "module") {
59
+ exitDeriveWithGuidance(`${moduleRef} is not a module (type: ${moduleItem.type})`, EXIT_CODES.USAGE_ERROR, "Pass a module @ref from: kspec item list --type module", {
60
+ field: "module",
61
+ value: moduleItem.type,
62
+ });
63
+ }
64
+ return moduleRef.startsWith("@") ? moduleRef : `@${moduleRef}`;
65
+ }
66
+ function slugify(title) {
67
+ return title
68
+ .toLowerCase()
69
+ .replace(/[^a-z0-9]+/g, "-")
70
+ .replace(/^-|-$/g, "")
71
+ .slice(0, 50);
72
+ }
73
+ function toRef(ref) {
74
+ return ref.startsWith("@") ? ref : `@${ref}`;
75
+ }
76
+ function canonicalRef(item) {
77
+ return `@${item.slugs[0] || item._ulid}`;
78
+ }
79
+ function nextUniqueSlug(baseSlug, reservedSlugs) {
80
+ let slug = baseSlug;
81
+ let counter = 1;
82
+ while (reservedSlugs.has(slug)) {
83
+ slug = `${baseSlug}-${counter}`;
84
+ counter++;
85
+ }
86
+ reservedSlugs.add(slug);
87
+ return slug;
88
+ }
89
+ function createNote(content, author) {
90
+ return {
91
+ _ulid: ulid(),
92
+ created_at: new Date().toISOString(),
93
+ author,
94
+ content,
95
+ };
96
+ }
97
+ function exitDeriveWithGuidance(message, exitCode, suggestion, details) {
98
+ if (suggestion) {
99
+ if (isJsonMode()) {
100
+ error(message, {
101
+ ...details,
102
+ suggestion,
103
+ guidance: suggestion,
104
+ });
105
+ }
106
+ else {
107
+ error(message);
108
+ console.error(`Suggestion: ${suggestion}`);
109
+ }
110
+ }
111
+ else {
112
+ error(message, isJsonMode() ? details : undefined);
113
+ }
114
+ process.exit(exitCode);
115
+ }
116
+ function emitDeriveResult(result) {
117
+ output(result, () => {
118
+ if (result.dry_run) {
119
+ console.log("Dry run - no changes made\n");
120
+ }
121
+ console.log(`Plan: ${result.plan_ref}`);
122
+ console.log(`Module: ${result.module_ref}`);
123
+ console.log(`Created specs: ${result.created_specs.length}`);
124
+ for (const ref of result.created_specs) {
125
+ console.log(` - ${ref}`);
126
+ }
127
+ console.log(`Created tasks: ${result.created_tasks.length}`);
128
+ for (const ref of result.created_tasks) {
129
+ console.log(` - ${ref}`);
130
+ }
131
+ if (result.skipped.length > 0) {
132
+ console.log(`Skipped: ${result.skipped.length}`);
133
+ for (const skipped of result.skipped) {
134
+ console.log(` - ${skipped.ref} (${skipped.title}): ${skipped.reason}`);
135
+ }
136
+ }
137
+ if (result.errors.length > 0) {
138
+ console.log(`Errors: ${result.errors.length}`);
139
+ for (const err of result.errors) {
140
+ console.log(` - [${err.type}] ${err.message}`);
141
+ }
142
+ }
143
+ });
144
+ }
145
+ function reportWarnings(warnings) {
146
+ for (const warning of warnings) {
147
+ warn(warning.ref ? `${warning.ref}: ${warning.message}` : warning.message);
148
+ }
149
+ }
150
+ function normalizeSpecTraits(spec, refIndex, createdSpecs, warnings) {
151
+ const normalized = new Set();
152
+ for (const rawTrait of spec.traits || []) {
153
+ const traitRef = toRef(rawTrait);
154
+ const localTrait = createdSpecs.get(traitRef.slice(1));
155
+ if (localTrait?.item.type === "trait") {
156
+ normalized.add(localTrait.ref);
157
+ continue;
158
+ }
159
+ const resolved = refIndex.resolve(traitRef);
160
+ if (resolved.ok) {
161
+ const item = resolved.item;
162
+ if (item.type !== "trait") {
163
+ warnings.push({
164
+ kind: "spec",
165
+ ref: spec.slug ? `@${spec.slug}` : undefined,
166
+ message: `${traitRef} resolved to ${item.type}, not a trait. Storing normalized reference for later review.`,
167
+ });
168
+ normalized.add(traitRef);
169
+ continue;
170
+ }
171
+ normalized.add(canonicalRef(item));
172
+ continue;
173
+ }
174
+ normalized.add(traitRef);
175
+ }
176
+ return [...normalized];
177
+ }
178
+ function normalizeSpecDependencies(spec, refIndex, createdSpecs, warnings) {
179
+ return (spec.depends_on || []).map((rawDependency) => {
180
+ const dependencyRef = toRef(rawDependency);
181
+ const localDependency = createdSpecs.get(dependencyRef.slice(1));
182
+ if (localDependency) {
183
+ return localDependency.ref;
184
+ }
185
+ const resolved = refIndex.resolve(dependencyRef);
186
+ if (resolved.ok) {
187
+ const item = resolved.item;
188
+ return canonicalRef(item);
189
+ }
190
+ warnings.push({
191
+ kind: "spec",
192
+ ref: spec.slug ? `@${spec.slug}` : undefined,
193
+ message: `Unresolved dependency ${dependencyRef}. Keeping the reference as-is for later resolution.`,
194
+ });
195
+ return dependencyRef;
196
+ });
197
+ }
198
+ async function materializePlanSpecs(ctx, foundPlan, moduleRef, parsedPlan, refIndex, items, reservedSlugs, dryRun, warnings, skipped) {
199
+ const sortResult = topologicalSort(parsedPlan.specs);
200
+ if (sortResult.error) {
201
+ exitDeriveWithGuidance(sortResult.error.message, EXIT_CODES.USAGE_ERROR, "Fix the parent references in the plan content so they form an acyclic tree.", {
202
+ type: sortResult.error.type,
203
+ });
204
+ }
205
+ const createdSpecs = new Map();
206
+ const materialized = [];
207
+ const moduleResult = refIndex.resolve(moduleRef);
208
+ if (!moduleResult.ok) {
209
+ exitDeriveWithGuidance(errors.reference.itemNotFound(moduleRef), EXIT_CODES.NOT_FOUND, "Check available modules with: kspec item list --type module", {
210
+ ref: moduleRef,
211
+ entity: "module",
212
+ });
213
+ }
214
+ const moduleItem = moduleResult.item;
215
+ for (const spec of sortResult.sorted) {
216
+ const localSlug = spec.slug || slugify(spec.title);
217
+ const itemType = (spec.type || "feature");
218
+ const itemSlug = nextUniqueSlug(localSlug, reservedSlugs);
219
+ const itemRef = `@${itemSlug}`;
220
+ let parent = null;
221
+ if (!(itemType === "trait" && !spec.parent)) {
222
+ if (spec.parent) {
223
+ const localParent = createdSpecs.get(spec.parent.startsWith("@") ? spec.parent.slice(1) : spec.parent);
224
+ if (localParent) {
225
+ parent = localParent.item;
226
+ }
227
+ else {
228
+ const resolvedParent = refIndex.resolve(spec.parent);
229
+ if (!resolvedParent.ok) {
230
+ const reason = `Parent ${toRef(spec.parent)} not found. Use an existing @ref or add the parent spec to the plan.`;
231
+ warnings.push({ kind: "spec", ref: itemRef, message: reason });
232
+ skipped.push({
233
+ kind: "spec",
234
+ ref: itemRef,
235
+ title: spec.title,
236
+ reason,
237
+ });
238
+ continue;
239
+ }
240
+ parent = resolvedParent.item;
241
+ }
242
+ }
243
+ else {
244
+ parent = moduleItem;
245
+ }
246
+ }
247
+ const input = {
248
+ title: spec.title,
249
+ type: itemType,
250
+ slugs: [itemSlug],
251
+ description: spec.description,
252
+ priority: spec.priority,
253
+ tags: [],
254
+ acceptance_criteria: spec.acceptance_criteria,
255
+ depends_on: normalizeSpecDependencies(spec, refIndex, createdSpecs, warnings),
256
+ implements: [],
257
+ relates_to: [],
258
+ tests: [],
259
+ traits: normalizeSpecTraits(spec, refIndex, createdSpecs, warnings),
260
+ notes: [],
261
+ };
262
+ const newItem = createSpecItem(input);
263
+ let createdItem;
264
+ if (dryRun) {
265
+ createdItem = {
266
+ ...newItem,
267
+ _sourceFile: parent?._sourceFile,
268
+ };
269
+ }
270
+ else if (itemType === "trait" && !spec.parent) {
271
+ const addResult = await addProjectLevelTraitItem(ctx, newItem);
272
+ createdItem = {
273
+ ...addResult.item,
274
+ _sourceFile: ctx.manifestPath || undefined,
275
+ _path: addResult.path,
276
+ };
277
+ }
278
+ else {
279
+ const addResult = await addChildItem(ctx, parent, newItem);
280
+ createdItem = {
281
+ ...addResult.item,
282
+ _sourceFile: parent._sourceFile,
283
+ _path: addResult.path,
284
+ };
285
+ }
286
+ const materializedSpec = {
287
+ localSlug,
288
+ ref: itemRef,
289
+ item: createdItem,
290
+ source: spec,
291
+ };
292
+ createdSpecs.set(localSlug, materializedSpec);
293
+ materialized.push(materializedSpec);
294
+ }
295
+ return materialized;
296
+ }
297
+ function buildTaskPlans(planRef, specItems, deriveFromSpecs, additionalTasks, refIndex, allTasks, reservedSlugs, author, warnings) {
298
+ const taskPlans = [];
299
+ const specTaskRefByLocalSlug = new Map();
300
+ const taskRefByLocalKey = new Map();
301
+ const shouldDeriveFromSpecs = deriveFromSpecs !== false;
302
+ if (shouldDeriveFromSpecs) {
303
+ for (const specItem of specItems) {
304
+ const taskSlug = nextUniqueSlug(slugify(`implement-${specItem.source.title}`), reservedSlugs);
305
+ const taskRef = `@${taskSlug}`;
306
+ specTaskRefByLocalSlug.set(specItem.localSlug, taskRef);
307
+ taskRefByLocalKey.set(specItem.localSlug, taskRef);
308
+ }
309
+ }
310
+ for (const task of additionalTasks || []) {
311
+ const localKey = task.slug || slugify(task.title);
312
+ const taskSlug = nextUniqueSlug(task.slug || localKey, reservedSlugs);
313
+ taskRefByLocalKey.set(localKey, `@${taskSlug}`);
314
+ }
315
+ if (shouldDeriveFromSpecs) {
316
+ for (const specItem of specItems) {
317
+ const taskRef = specTaskRefByLocalSlug.get(specItem.localSlug);
318
+ const taskSlug = taskRef.slice(1);
319
+ const dependsOn = (specItem.item.depends_on || []).map((dependencyRef) => {
320
+ const localDependency = specTaskRefByLocalSlug.get(dependencyRef.slice(1));
321
+ return localDependency || dependencyRef;
322
+ });
323
+ const notes = specItem.source.implementation_notes
324
+ ? [createNote(specItem.source.implementation_notes, author)]
325
+ : [];
326
+ taskPlans.push({
327
+ localKey: specItem.localSlug,
328
+ ref: taskRef,
329
+ input: {
330
+ title: `Implement ${specItem.source.title}`,
331
+ type: "task",
332
+ slugs: [taskSlug],
333
+ spec_ref: specItem.ref,
334
+ plan_ref: planRef,
335
+ priority: specItem.source.priority ?? 3,
336
+ tags: [],
337
+ depends_on: dependsOn,
338
+ notes,
339
+ origin: "derived",
340
+ derivation: "auto",
341
+ },
342
+ });
343
+ }
344
+ }
345
+ for (const task of additionalTasks || []) {
346
+ const localKey = task.slug || slugify(task.title);
347
+ const taskRef = taskRefByLocalKey.get(localKey);
348
+ const taskSlug = taskRef.slice(1);
349
+ const specRef = task.spec_ref
350
+ ? (() => {
351
+ const localSpec = specItems.find((candidate) => candidate.localSlug ===
352
+ (task.spec_ref.startsWith("@")
353
+ ? task.spec_ref.slice(1)
354
+ : task.spec_ref));
355
+ if (localSpec) {
356
+ return localSpec.ref;
357
+ }
358
+ const resolved = refIndex.resolve(task.spec_ref);
359
+ if (resolved.ok) {
360
+ return canonicalRef(resolved.item);
361
+ }
362
+ warnings.push({
363
+ kind: "task",
364
+ ref: taskRef,
365
+ message: `Unresolved spec_ref ${toRef(task.spec_ref)} on additional task. Keeping normalized reference.`,
366
+ });
367
+ return toRef(task.spec_ref);
368
+ })()
369
+ : null;
370
+ const dependsOn = (task.depends_on || []).map((dependencyRef) => {
371
+ const normalized = toRef(dependencyRef);
372
+ const localTaskDependency = taskRefByLocalKey.get(normalized.slice(1));
373
+ if (localTaskDependency) {
374
+ return localTaskDependency;
375
+ }
376
+ const resolved = refIndex.resolve(normalized);
377
+ if (resolved.ok) {
378
+ return canonicalRef(resolved.item);
379
+ }
380
+ warnings.push({
381
+ kind: "task",
382
+ ref: taskRef,
383
+ message: `Unresolved depends_on ${normalized} on additional task. Keeping normalized reference.`,
384
+ });
385
+ return normalized;
386
+ });
387
+ taskPlans.push({
388
+ localKey,
389
+ ref: taskRef,
390
+ input: {
391
+ title: task.title,
392
+ type: "task",
393
+ slugs: [taskSlug],
394
+ description: task.description,
395
+ spec_ref: specRef,
396
+ plan_ref: planRef,
397
+ priority: task.priority ?? 3,
398
+ tags: task.tags || [],
399
+ depends_on: dependsOn,
400
+ notes: [],
401
+ origin: "derived",
402
+ },
403
+ });
404
+ }
405
+ const existingTaskSlugs = new Set(allTasks.flatMap((task) => task.slugs));
406
+ for (const taskPlan of taskPlans) {
407
+ if (existingTaskSlugs.has(taskPlan.input.slugs?.[0] || "")) {
408
+ warnings.push({
409
+ kind: "task",
410
+ ref: taskPlan.ref,
411
+ message: "Task slug collided with existing work and was renumbered.",
412
+ });
413
+ }
414
+ }
415
+ return taskPlans;
416
+ }
42
417
  /**
43
418
  * Register the 'plan' command group
44
419
  */
@@ -158,6 +533,12 @@ Examples:
158
533
  if (foundPlan.slugs.length > 0) {
159
534
  console.log(`Slugs: ${foundPlan.slugs.join(", ")}`);
160
535
  }
536
+ if (foundPlan.module_ref) {
537
+ console.log(`Module: ${foundPlan.module_ref}`);
538
+ }
539
+ if (foundPlan.source_path) {
540
+ console.log(`Source: ${foundPlan.source_path}`);
541
+ }
161
542
  console.log(`${fieldLabels.created} ${foundPlan.created_at} (${formatRelativeTime(foundPlan.created_at)})`);
162
543
  if (foundPlan.approved_at) {
163
544
  console.log(`Approved: ${foundPlan.approved_at} (${formatRelativeTime(foundPlan.approved_at)})`);
@@ -198,6 +579,52 @@ Examples:
198
579
  process.exit(EXIT_CODES.ERROR);
199
580
  }
200
581
  });
582
+ // kspec plan export <ref>
583
+ // AC: @plan-export ac-stdout, ac-output-file, ac-empty, ac-not-found, ac-json
584
+ plan
585
+ .command("export <ref>")
586
+ .description("Export stored plan content to stdout or a file")
587
+ .option("--output <path>", "Write plan content to the specified file")
588
+ .addHelpText("after", `
589
+ Examples:
590
+ $ kspec plan export @plan-ref
591
+ $ kspec plan export @plan-ref --output ./plan.md
592
+ $ kspec plan export @plan-ref --json`)
593
+ .action(async (ref, options) => {
594
+ try {
595
+ const ctx = await initContext();
596
+ const foundPlan = await findPlanByRef(ctx, ref);
597
+ if (!foundPlan) {
598
+ error(errors.reference.planNotFound(ref));
599
+ process.exit(EXIT_CODES.USAGE_ERROR);
600
+ }
601
+ if (foundPlan.content.length === 0) {
602
+ error("Plan has no content to export");
603
+ process.exit(EXIT_CODES.USAGE_ERROR);
604
+ }
605
+ if (isJsonMode()) {
606
+ output(foundPlan);
607
+ return;
608
+ }
609
+ if (options.output) {
610
+ const outputPath = path.resolve(process.cwd(), options.output);
611
+ try {
612
+ await fs.writeFile(outputPath, foundPlan.content, "utf-8");
613
+ }
614
+ catch (err) {
615
+ error(`Failed to write plan export file: ${options.output}`, err);
616
+ process.exit(EXIT_CODES.ERROR);
617
+ }
618
+ success(`Exported plan content to ${options.output}`);
619
+ return;
620
+ }
621
+ process.stdout.write(foundPlan.content);
622
+ }
623
+ catch (err) {
624
+ error("Failed to export plan", err);
625
+ process.exit(EXIT_CODES.ERROR);
626
+ }
627
+ });
201
628
  // kspec plan set <ref>
202
629
  // AC: @plan-crud ac-3, ac-4
203
630
  markMutating(plan.command("set <ref>"))
@@ -362,102 +789,125 @@ Examples:
362
789
  }
363
790
  });
364
791
  // kspec plan derive <ref>
365
- // AC: @plan-derive ac-5, ac-6
792
+ // AC: @plan-derive-enhanced ac-parse-content through ac-commit
366
793
  markMutating(plan.command("derive <ref>"))
367
- .description("Create a task from a plan")
368
- .option("--title <title>", "Override task title")
369
- .option("--priority <n>", "Set task priority (1-5)")
794
+ .description("Materialize plan content into specs and optional tasks")
795
+ .option("--module <ref>", "Module context for derivation (overrides stored plan module)")
796
+ .option("--tasks", "Also derive implementation tasks after creating specs")
797
+ .option("--dry-run", "Preview derived specs/tasks without saving changes")
370
798
  .addHelpText("after", `
371
799
  Examples:
372
- $ kspec plan derive @plan-ref
373
- $ kspec plan derive @plan-ref --title "Custom title"
374
- $ kspec plan derive @plan-ref --priority 1`)
800
+ $ kspec plan derive @plan-ref --module @core
801
+ $ kspec plan derive @plan-ref --tasks
802
+ $ kspec plan derive @plan-ref --module @core --tasks --dry-run`)
375
803
  .action(async (ref, options) => {
376
804
  try {
377
805
  const ctx = await initContext();
378
806
  const plans = await loadPlans(ctx);
379
- const tasks = await loadAllTasks(ctx);
380
807
  const foundPlan = resolvePlanRef(ref, plans);
381
- // AC: @plan-derive ac-5 - check plan status
382
- if (foundPlan.status !== "approved" && foundPlan.status !== "active") {
383
- error(`Plan must be in 'approved' or 'active' status to derive tasks (current: ${foundPlan.status})`);
384
- process.exit(EXIT_CODES.USAGE_ERROR);
808
+ const planRef = canonicalRef(foundPlan);
809
+ const author = getAuthor(ctx.config?.identity?.author);
810
+ if (foundPlan.status === "active") {
811
+ exitDeriveWithGuidance("Plan already derived. Manage specs directly via kspec item set.", EXIT_CODES.CONFLICT, `Update derived work directly, for example: kspec item set ${planRef} ...`, {
812
+ current_status: foundPlan.status,
813
+ valid_next_states: ["manage-derived-work"],
814
+ });
815
+ }
816
+ if (foundPlan.status !== "approved") {
817
+ exitDeriveWithGuidance(`Plan must be in approved status to derive (current: ${foundPlan.status})`, EXIT_CODES.CONFLICT, `Approve the plan first with: kspec plan set ${planRef} --status approved`, {
818
+ current_status: foundPlan.status,
819
+ valid_next_states: ["approved"],
820
+ });
385
821
  }
386
- // Validate priority if provided
387
- if (options.priority !== undefined) {
388
- const priorityResult = parseIntOption(options.priority, {
389
- min: 1,
390
- max: 5,
391
- name: "Priority",
822
+ const moduleRef = await resolveDeriveModuleRef(ctx, plans, foundPlan, options.module);
823
+ if (!moduleRef) {
824
+ exitDeriveWithGuidance("Plan derive requires --module when the plan has no stored module ref", EXIT_CODES.USAGE_ERROR, `Re-run with a module, for example: kspec plan derive ${planRef} --module @your-module`, {
825
+ field: "module",
826
+ value: null,
392
827
  });
393
- if (!priorityResult.ok) {
394
- error(priorityResult.error);
395
- process.exit(EXIT_CODES.USAGE_ERROR);
828
+ }
829
+ const parsedPlan = parsePlanDocument(foundPlan.content);
830
+ const errorsList = [];
831
+ const warnings = [];
832
+ const skipped = [];
833
+ for (const parseError of parsedPlan.errors) {
834
+ if (parseError.type === "yaml") {
835
+ exitDeriveWithGuidance(parseError.message, EXIT_CODES.USAGE_ERROR, "Fix the YAML block in the plan document and run kspec plan derive again.", {
836
+ type: parseError.type,
837
+ });
396
838
  }
397
- // Replace raw string with validated number for downstream use
398
- options.priority = priorityResult.value;
839
+ errorsList.push({
840
+ type: parseError.type,
841
+ message: parseError.message,
842
+ });
399
843
  }
400
- // Generate task slug from plan title
401
- const generateSlug = (title) => {
402
- return title
403
- .toLowerCase()
404
- .replace(/[^a-z0-9]+/g, "-")
405
- .replace(/^-|-$/g, "")
406
- .slice(0, 50);
407
- };
408
- // Generate task title and slug
409
- const taskTitle = options.title || `Implement: ${foundPlan.title}`;
410
- let taskSlug = generateSlug(taskTitle);
411
- // Ensure slug uniqueness
412
- let slugSuffix = 1;
413
- const originalSlug = taskSlug;
414
- while (tasks.some((t) => t.slugs.includes(taskSlug))) {
415
- taskSlug = `${originalSlug}-${slugSuffix}`;
416
- slugSuffix++;
844
+ if (parsedPlan.specs.length === 0) {
845
+ exitDeriveWithGuidance("No specs found in plan content. Ensure ## Specs section contains a fenced YAML code block.", EXIT_CODES.USAGE_ERROR, "Add a ## Specs section with a ```yaml fenced block, then re-run derive.");
417
846
  }
418
- // AC: @plan-derive ac-5 - create task with plan_ref
419
- const planRef = foundPlan.slugs[0]
420
- ? `@${foundPlan.slugs[0]}`
421
- : `@${shortPlanRef(foundPlan, plans)}`;
422
- const taskInput = {
423
- title: taskTitle,
424
- type: "task",
425
- plan_ref: planRef,
426
- priority: options.priority ?? 3,
427
- slugs: [taskSlug],
428
- tags: [],
429
- depends_on: [],
430
- notes: [],
431
- };
432
- const newTask = createTask(taskInput);
433
- await saveTask(ctx, newTask);
434
- // AC: @plan-derive ac-5 - update plan's derived_tasks array
435
- const taskRef = `@${taskSlug}`;
436
- const updatedPlan = await mutatePlanAtomically(ctx, foundPlan, (latestPlan) => {
437
- const nextPlan = {
438
- ...latestPlan,
439
- slugs: [...latestPlan.slugs],
440
- derived_tasks: [...latestPlan.derived_tasks],
441
- derived_specs: [...latestPlan.derived_specs],
442
- notes: [...latestPlan.notes],
443
- };
444
- if (!nextPlan.derived_tasks.includes(taskRef)) {
445
- nextPlan.derived_tasks.push(taskRef);
847
+ const { refIndex, items, tasks } = await buildIndexes(ctx, plans);
848
+ const reservedSlugs = new Set([
849
+ ...plans.flatMap((plan) => plan.slugs),
850
+ ...items.flatMap((item) => item.slugs),
851
+ ...tasks.flatMap((task) => task.slugs),
852
+ ]);
853
+ const materializedSpecs = await materializePlanSpecs(ctx, foundPlan, moduleRef, parsedPlan, refIndex, items, reservedSlugs, Boolean(options.dryRun), warnings, skipped);
854
+ const createdSpecRefs = materializedSpecs.map((item) => item.ref);
855
+ let taskPlans = [];
856
+ if (options.tasks) {
857
+ taskPlans = buildTaskPlans(planRef, materializedSpecs, parsedPlan.tasks.derive_from_specs, parsedPlan.tasks.additional_tasks, refIndex, tasks, reservedSlugs, author, warnings);
858
+ }
859
+ const createdTaskRefs = taskPlans.map((taskPlan) => taskPlan.ref);
860
+ if (!options.dryRun) {
861
+ for (const taskPlan of taskPlans) {
862
+ const newTask = createTask(taskPlan.input);
863
+ await saveTask(ctx, newTask);
446
864
  }
447
- // AC: @plan-derive ac-5 - transition plan to active if not already
448
- if (nextPlan.status === "approved") {
865
+ const updatedPlan = await mutatePlanAtomically(ctx, foundPlan, (latestPlan) => {
866
+ const nextPlan = {
867
+ ...latestPlan,
868
+ slugs: [...latestPlan.slugs],
869
+ derived_tasks: [...latestPlan.derived_tasks],
870
+ derived_specs: [...latestPlan.derived_specs],
871
+ notes: [...latestPlan.notes],
872
+ };
873
+ for (const specRef of createdSpecRefs) {
874
+ if (!nextPlan.derived_specs.includes(specRef)) {
875
+ nextPlan.derived_specs.push(specRef);
876
+ }
877
+ }
878
+ for (const taskRef of createdTaskRefs) {
879
+ if (!nextPlan.derived_tasks.includes(taskRef)) {
880
+ nextPlan.derived_tasks.push(taskRef);
881
+ }
882
+ }
449
883
  nextPlan.status = "active";
450
- }
451
- return nextPlan;
452
- });
453
- await commitIfShadow(ctx.shadow, "plan-derive", updatedPlan.slugs[0] || updatedPlan._ulid.slice(0, 8), taskTitle);
454
- success(`Created task from plan: ${taskRef}`, {
455
- task: newTask,
456
- plan: updatedPlan,
884
+ if (parsedPlan.implementationNotes?.trim()) {
885
+ nextPlan.notes.push(createNote(parsedPlan.implementationNotes.trim(), author));
886
+ }
887
+ return nextPlan;
888
+ });
889
+ await commitIfShadow(ctx.shadow, "plan-derive", updatedPlan.slugs[0] || updatedPlan._ulid.slice(0, 8), `${createdSpecRefs.length} specs${options.tasks ? `, ${createdTaskRefs.length} tasks` : ""}`);
890
+ }
891
+ else if (parsedPlan.implementationNotes?.trim()) {
892
+ warnings.push({
893
+ kind: "plan",
894
+ ref: planRef,
895
+ message: "Global implementation notes would be added to the plan during execution.",
896
+ });
897
+ }
898
+ reportWarnings(warnings);
899
+ emitDeriveResult({
900
+ dry_run: Boolean(options.dryRun),
901
+ plan_ref: planRef,
902
+ module_ref: moduleRef,
903
+ created_specs: createdSpecRefs,
904
+ created_tasks: createdTaskRefs,
905
+ skipped,
906
+ errors: errorsList,
457
907
  });
458
908
  }
459
909
  catch (err) {
460
- error("Failed to derive task from plan", err);
910
+ error("Failed to derive plan content", err);
461
911
  process.exit(EXIT_CODES.ERROR);
462
912
  }
463
913
  });