@kynetic-ai/spec 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (501) hide show
  1. package/README.md +55 -455
  2. package/dist/agent-runtime/bootstrap.d.ts +31 -0
  3. package/dist/agent-runtime/bootstrap.d.ts.map +1 -0
  4. package/dist/agent-runtime/bootstrap.js +302 -0
  5. package/dist/agent-runtime/bootstrap.js.map +1 -0
  6. package/dist/agent-runtime/dispatch.d.ts +119 -10
  7. package/dist/agent-runtime/dispatch.d.ts.map +1 -1
  8. package/dist/agent-runtime/dispatch.js +1154 -219
  9. package/dist/agent-runtime/dispatch.js.map +1 -1
  10. package/dist/agent-runtime/invocation.d.ts +28 -1
  11. package/dist/agent-runtime/invocation.d.ts.map +1 -1
  12. package/dist/agent-runtime/invocation.js +171 -59
  13. package/dist/agent-runtime/invocation.js.map +1 -1
  14. package/dist/agent-runtime/prompts.d.ts +9 -0
  15. package/dist/agent-runtime/prompts.d.ts.map +1 -1
  16. package/dist/agent-runtime/prompts.js +42 -7
  17. package/dist/agent-runtime/prompts.js.map +1 -1
  18. package/dist/agent-runtime/session-event-accumulator.d.ts +83 -0
  19. package/dist/agent-runtime/session-event-accumulator.d.ts.map +1 -0
  20. package/dist/agent-runtime/session-event-accumulator.js +203 -0
  21. package/dist/agent-runtime/session-event-accumulator.js.map +1 -0
  22. package/dist/agent-runtime/session-event-types.d.ts +67 -0
  23. package/dist/agent-runtime/session-event-types.d.ts.map +1 -0
  24. package/dist/agent-runtime/session-event-types.js +13 -0
  25. package/dist/agent-runtime/session-event-types.js.map +1 -0
  26. package/dist/agent-runtime/workspace.d.ts +244 -0
  27. package/dist/agent-runtime/workspace.d.ts.map +1 -0
  28. package/dist/agent-runtime/workspace.js +2025 -0
  29. package/dist/agent-runtime/workspace.js.map +1 -0
  30. package/dist/agents/adapters.d.ts.map +1 -1
  31. package/dist/agents/adapters.js +58 -13
  32. package/dist/agents/adapters.js.map +1 -1
  33. package/dist/agents/spawner.d.ts +8 -0
  34. package/dist/agents/spawner.d.ts.map +1 -1
  35. package/dist/agents/spawner.js +25 -3
  36. package/dist/agents/spawner.js.map +1 -1
  37. package/dist/cli/batch-exec.js +1 -1
  38. package/dist/cli/batch-exec.js.map +1 -1
  39. package/dist/cli/command-annotations.d.ts +15 -3
  40. package/dist/cli/command-annotations.d.ts.map +1 -1
  41. package/dist/cli/command-annotations.js +23 -3
  42. package/dist/cli/command-annotations.js.map +1 -1
  43. package/dist/cli/commands/agent.d.ts +2 -0
  44. package/dist/cli/commands/agent.d.ts.map +1 -1
  45. package/dist/cli/commands/agent.js +144 -27
  46. package/dist/cli/commands/agent.js.map +1 -1
  47. package/dist/cli/commands/agents.d.ts.map +1 -1
  48. package/dist/cli/commands/agents.js +5 -5
  49. package/dist/cli/commands/agents.js.map +1 -1
  50. package/dist/cli/commands/derive.d.ts.map +1 -1
  51. package/dist/cli/commands/derive.js +118 -3
  52. package/dist/cli/commands/derive.js.map +1 -1
  53. package/dist/cli/commands/guard.d.ts.map +1 -1
  54. package/dist/cli/commands/guard.js +8 -6
  55. package/dist/cli/commands/guard.js.map +1 -1
  56. package/dist/cli/commands/index.d.ts +1 -0
  57. package/dist/cli/commands/index.d.ts.map +1 -1
  58. package/dist/cli/commands/index.js +1 -0
  59. package/dist/cli/commands/index.js.map +1 -1
  60. package/dist/cli/commands/init.d.ts.map +1 -1
  61. package/dist/cli/commands/init.js +20 -0
  62. package/dist/cli/commands/init.js.map +1 -1
  63. package/dist/cli/commands/item.d.ts.map +1 -1
  64. package/dist/cli/commands/item.js +205 -47
  65. package/dist/cli/commands/item.js.map +1 -1
  66. package/dist/cli/commands/log.d.ts.map +1 -1
  67. package/dist/cli/commands/log.js +24 -10
  68. package/dist/cli/commands/log.js.map +1 -1
  69. package/dist/cli/commands/plan-import.d.ts +3 -3
  70. package/dist/cli/commands/plan-import.d.ts.map +1 -1
  71. package/dist/cli/commands/plan-import.js +213 -528
  72. package/dist/cli/commands/plan-import.js.map +1 -1
  73. package/dist/cli/commands/plan.d.ts.map +1 -1
  74. package/dist/cli/commands/plan.js +533 -83
  75. package/dist/cli/commands/plan.js.map +1 -1
  76. package/dist/cli/commands/review.d.ts +14 -0
  77. package/dist/cli/commands/review.d.ts.map +1 -0
  78. package/dist/cli/commands/review.js +1142 -0
  79. package/dist/cli/commands/review.js.map +1 -0
  80. package/dist/cli/commands/serve.d.ts +1 -0
  81. package/dist/cli/commands/serve.d.ts.map +1 -1
  82. package/dist/cli/commands/serve.js +33 -10
  83. package/dist/cli/commands/serve.js.map +1 -1
  84. package/dist/cli/commands/session/checkpoint.d.ts +2 -4
  85. package/dist/cli/commands/session/checkpoint.d.ts.map +1 -1
  86. package/dist/cli/commands/session/checkpoint.js +6 -107
  87. package/dist/cli/commands/session/checkpoint.js.map +1 -1
  88. package/dist/cli/commands/session/commands.d.ts.map +1 -1
  89. package/dist/cli/commands/session/commands.js +33 -23
  90. package/dist/cli/commands/session/commands.js.map +1 -1
  91. package/dist/cli/commands/session/compact.js +4 -4
  92. package/dist/cli/commands/session/compact.js.map +1 -1
  93. package/dist/cli/commands/session/create.js +2 -2
  94. package/dist/cli/commands/session/create.js.map +1 -1
  95. package/dist/cli/commands/session/format.d.ts.map +1 -1
  96. package/dist/cli/commands/session/format.js +1 -6
  97. package/dist/cli/commands/session/format.js.map +1 -1
  98. package/dist/cli/commands/session/log.d.ts +32 -7
  99. package/dist/cli/commands/session/log.d.ts.map +1 -1
  100. package/dist/cli/commands/session/log.js +166 -60
  101. package/dist/cli/commands/session/log.js.map +1 -1
  102. package/dist/cli/commands/session/migrate.d.ts +9 -0
  103. package/dist/cli/commands/session/migrate.d.ts.map +1 -0
  104. package/dist/cli/commands/session/migrate.js +46 -0
  105. package/dist/cli/commands/session/migrate.js.map +1 -0
  106. package/dist/cli/commands/session/stale-close.d.ts.map +1 -1
  107. package/dist/cli/commands/session/stale-close.js +5 -8
  108. package/dist/cli/commands/session/stale-close.js.map +1 -1
  109. package/dist/cli/commands/session/types.d.ts +1 -1
  110. package/dist/cli/commands/session/types.d.ts.map +1 -1
  111. package/dist/cli/commands/setup.d.ts +2 -2
  112. package/dist/cli/commands/setup.d.ts.map +1 -1
  113. package/dist/cli/commands/setup.js +287 -257
  114. package/dist/cli/commands/setup.js.map +1 -1
  115. package/dist/cli/commands/shadow.d.ts.map +1 -1
  116. package/dist/cli/commands/shadow.js +147 -31
  117. package/dist/cli/commands/shadow.js.map +1 -1
  118. package/dist/cli/commands/skill-crud.d.ts +7 -0
  119. package/dist/cli/commands/skill-crud.d.ts.map +1 -1
  120. package/dist/cli/commands/skill-crud.js +41 -18
  121. package/dist/cli/commands/skill-crud.js.map +1 -1
  122. package/dist/cli/commands/skill-diff.d.ts.map +1 -1
  123. package/dist/cli/commands/skill-diff.js +29 -3
  124. package/dist/cli/commands/skill-diff.js.map +1 -1
  125. package/dist/cli/commands/skill-install.d.ts.map +1 -1
  126. package/dist/cli/commands/skill-install.js +5 -4
  127. package/dist/cli/commands/skill-install.js.map +1 -1
  128. package/dist/cli/commands/task.d.ts.map +1 -1
  129. package/dist/cli/commands/task.js +359 -49
  130. package/dist/cli/commands/task.js.map +1 -1
  131. package/dist/cli/commands/trait.d.ts.map +1 -1
  132. package/dist/cli/commands/trait.js +5 -27
  133. package/dist/cli/commands/trait.js.map +1 -1
  134. package/dist/cli/commands/validate.d.ts.map +1 -1
  135. package/dist/cli/commands/validate.js +113 -52
  136. package/dist/cli/commands/validate.js.map +1 -1
  137. package/dist/cli/index.d.ts.map +1 -1
  138. package/dist/cli/index.js +69 -2
  139. package/dist/cli/index.js.map +1 -1
  140. package/dist/cli/output.d.ts +26 -0
  141. package/dist/cli/output.d.ts.map +1 -1
  142. package/dist/cli/output.js +108 -1
  143. package/dist/cli/output.js.map +1 -1
  144. package/dist/cli/sync-mode.d.ts +44 -0
  145. package/dist/cli/sync-mode.d.ts.map +1 -0
  146. package/dist/cli/sync-mode.js +64 -0
  147. package/dist/cli/sync-mode.js.map +1 -0
  148. package/dist/daemon/middleware/project-context.ts +25 -7
  149. package/dist/daemon/project-context.ts +18 -0
  150. package/dist/daemon/routes/agent-dispatch.ts +99 -22
  151. package/dist/daemon/routes/aggregation.ts +184 -0
  152. package/dist/daemon/routes/inbox.ts +5 -0
  153. package/dist/daemon/routes/items.ts +145 -0
  154. package/dist/daemon/routes/meta.ts +1 -1
  155. package/dist/daemon/routes/projects.ts +28 -6
  156. package/dist/daemon/routes/ref-resolution.ts +119 -0
  157. package/dist/daemon/routes/refs.ts +42 -0
  158. package/dist/daemon/routes/session-related.ts +140 -0
  159. package/dist/daemon/routes/sessions.ts +420 -19
  160. package/dist/daemon/routes/tasks.ts +62 -5
  161. package/dist/daemon/routes/triage.ts +40 -1
  162. package/dist/daemon/server.ts +143 -49
  163. package/dist/daemon/session-sync.ts +11 -0
  164. package/dist/daemon/shadow-sync.ts +11 -0
  165. package/dist/daemon/watcher.ts +56 -5
  166. package/dist/daemon/websocket/project-resolution.ts +77 -0
  167. package/dist/export/json.d.ts.map +1 -1
  168. package/dist/export/json.js +104 -1
  169. package/dist/export/json.js.map +1 -1
  170. package/dist/export/types.d.ts +52 -1
  171. package/dist/export/types.d.ts.map +1 -1
  172. package/dist/index.d.ts +1 -0
  173. package/dist/index.d.ts.map +1 -1
  174. package/dist/index.js +1 -0
  175. package/dist/index.js.map +1 -1
  176. package/dist/parser/agent-detection.d.ts +1 -1
  177. package/dist/parser/agent-detection.d.ts.map +1 -1
  178. package/dist/parser/agent-detection.js +10 -0
  179. package/dist/parser/agent-detection.js.map +1 -1
  180. package/dist/parser/config.d.ts +397 -2
  181. package/dist/parser/config.d.ts.map +1 -1
  182. package/dist/parser/config.js +125 -3
  183. package/dist/parser/config.js.map +1 -1
  184. package/dist/parser/dispatch-workspaces.d.ts +18 -0
  185. package/dist/parser/dispatch-workspaces.d.ts.map +1 -0
  186. package/dist/parser/dispatch-workspaces.js +209 -0
  187. package/dist/parser/dispatch-workspaces.js.map +1 -0
  188. package/dist/parser/doctor.d.ts.map +1 -1
  189. package/dist/parser/doctor.js +27 -8
  190. package/dist/parser/doctor.js.map +1 -1
  191. package/dist/parser/file-lock.d.ts.map +1 -1
  192. package/dist/parser/file-lock.js +9 -2
  193. package/dist/parser/file-lock.js.map +1 -1
  194. package/dist/parser/index.d.ts +6 -0
  195. package/dist/parser/index.d.ts.map +1 -1
  196. package/dist/parser/index.js +6 -0
  197. package/dist/parser/index.js.map +1 -1
  198. package/dist/parser/plans.d.ts.map +1 -1
  199. package/dist/parser/plans.js +1 -0
  200. package/dist/parser/plans.js.map +1 -1
  201. package/dist/parser/refs.d.ts +8 -1
  202. package/dist/parser/refs.d.ts.map +1 -1
  203. package/dist/parser/refs.js +27 -1
  204. package/dist/parser/refs.js.map +1 -1
  205. package/dist/parser/review-operations.d.ts +72 -0
  206. package/dist/parser/review-operations.d.ts.map +1 -0
  207. package/dist/parser/review-operations.js +185 -0
  208. package/dist/parser/review-operations.js.map +1 -0
  209. package/dist/parser/review-task-integration.d.ts +78 -0
  210. package/dist/parser/review-task-integration.d.ts.map +1 -0
  211. package/dist/parser/review-task-integration.js +173 -0
  212. package/dist/parser/review-task-integration.js.map +1 -0
  213. package/dist/parser/review-threads.d.ts +101 -0
  214. package/dist/parser/review-threads.d.ts.map +1 -0
  215. package/dist/parser/review-threads.js +222 -0
  216. package/dist/parser/review-threads.js.map +1 -0
  217. package/dist/parser/review-validation.d.ts +69 -0
  218. package/dist/parser/review-validation.d.ts.map +1 -0
  219. package/dist/parser/review-validation.js +207 -0
  220. package/dist/parser/review-validation.js.map +1 -0
  221. package/dist/parser/reviews.d.ts +58 -0
  222. package/dist/parser/reviews.d.ts.map +1 -0
  223. package/dist/parser/reviews.js +230 -0
  224. package/dist/parser/reviews.js.map +1 -0
  225. package/dist/parser/session-branch.d.ts +91 -0
  226. package/dist/parser/session-branch.d.ts.map +1 -0
  227. package/dist/parser/session-branch.js +565 -0
  228. package/dist/parser/session-branch.js.map +1 -0
  229. package/dist/parser/session-sync-scheduler.d.ts +53 -0
  230. package/dist/parser/session-sync-scheduler.d.ts.map +1 -0
  231. package/dist/parser/session-sync-scheduler.js +100 -0
  232. package/dist/parser/session-sync-scheduler.js.map +1 -0
  233. package/dist/parser/setup-status.d.ts +7 -1
  234. package/dist/parser/setup-status.d.ts.map +1 -1
  235. package/dist/parser/setup-status.js +104 -39
  236. package/dist/parser/setup-status.js.map +1 -1
  237. package/dist/parser/shadow-sync-scheduler.d.ts +71 -0
  238. package/dist/parser/shadow-sync-scheduler.d.ts.map +1 -0
  239. package/dist/parser/shadow-sync-scheduler.js +139 -0
  240. package/dist/parser/shadow-sync-scheduler.js.map +1 -0
  241. package/dist/parser/shadow.d.ts +121 -14
  242. package/dist/parser/shadow.d.ts.map +1 -1
  243. package/dist/parser/shadow.js +752 -27
  244. package/dist/parser/shadow.js.map +1 -1
  245. package/dist/parser/skill-render.d.ts +24 -0
  246. package/dist/parser/skill-render.d.ts.map +1 -1
  247. package/dist/parser/skill-render.js +98 -26
  248. package/dist/parser/skill-render.js.map +1 -1
  249. package/dist/parser/validate.d.ts +43 -3
  250. package/dist/parser/validate.d.ts.map +1 -1
  251. package/dist/parser/validate.js +204 -30
  252. package/dist/parser/validate.js.map +1 -1
  253. package/dist/parser/yaml.d.ts +47 -11
  254. package/dist/parser/yaml.d.ts.map +1 -1
  255. package/dist/parser/yaml.js +329 -149
  256. package/dist/parser/yaml.js.map +1 -1
  257. package/dist/review/checks.d.ts +97 -0
  258. package/dist/review/checks.d.ts.map +1 -0
  259. package/dist/review/checks.js +175 -0
  260. package/dist/review/checks.js.map +1 -0
  261. package/dist/review/index.d.ts +3 -0
  262. package/dist/review/index.d.ts.map +1 -0
  263. package/dist/review/index.js +3 -0
  264. package/dist/review/index.js.map +1 -0
  265. package/dist/review/subject-bindings.d.ts +83 -0
  266. package/dist/review/subject-bindings.d.ts.map +1 -0
  267. package/dist/review/subject-bindings.js +175 -0
  268. package/dist/review/subject-bindings.js.map +1 -0
  269. package/dist/schema/common.d.ts +26 -0
  270. package/dist/schema/common.d.ts.map +1 -1
  271. package/dist/schema/common.js +13 -0
  272. package/dist/schema/common.js.map +1 -1
  273. package/dist/schema/dispatch-workspace.d.ts +2643 -0
  274. package/dist/schema/dispatch-workspace.d.ts.map +1 -0
  275. package/dist/schema/dispatch-workspace.js +187 -0
  276. package/dist/schema/dispatch-workspace.js.map +1 -0
  277. package/dist/schema/inbox.d.ts +8 -8
  278. package/dist/schema/index.d.ts +2 -0
  279. package/dist/schema/index.d.ts.map +1 -1
  280. package/dist/schema/index.js +2 -0
  281. package/dist/schema/index.js.map +1 -1
  282. package/dist/schema/meta.d.ts +648 -116
  283. package/dist/schema/meta.d.ts.map +1 -1
  284. package/dist/schema/meta.js +27 -0
  285. package/dist/schema/meta.js.map +1 -1
  286. package/dist/schema/plan.d.ts +30 -19
  287. package/dist/schema/plan.d.ts.map +1 -1
  288. package/dist/schema/plan.js +3 -1
  289. package/dist/schema/plan.js.map +1 -1
  290. package/dist/schema/review-records.d.ts +2676 -0
  291. package/dist/schema/review-records.d.ts.map +1 -0
  292. package/dist/schema/review-records.js +232 -0
  293. package/dist/schema/review-records.js.map +1 -0
  294. package/dist/schema/spec.d.ts +32 -14
  295. package/dist/schema/spec.d.ts.map +1 -1
  296. package/dist/schema/spec.js +5 -0
  297. package/dist/schema/spec.js.map +1 -1
  298. package/dist/schema/task.d.ts +187 -29
  299. package/dist/schema/task.d.ts.map +1 -1
  300. package/dist/schema/task.js +12 -2
  301. package/dist/schema/task.js.map +1 -1
  302. package/dist/schema/triage.d.ts +22 -22
  303. package/dist/sessions/cache.d.ts +119 -0
  304. package/dist/sessions/cache.d.ts.map +1 -0
  305. package/dist/sessions/cache.js +284 -0
  306. package/dist/sessions/cache.js.map +1 -0
  307. package/dist/sessions/index.d.ts +1 -0
  308. package/dist/sessions/index.d.ts.map +1 -1
  309. package/dist/sessions/index.js +2 -0
  310. package/dist/sessions/index.js.map +1 -1
  311. package/dist/sessions/legacy.d.ts +77 -0
  312. package/dist/sessions/legacy.d.ts.map +1 -0
  313. package/dist/sessions/legacy.js +146 -0
  314. package/dist/sessions/legacy.js.map +1 -0
  315. package/dist/sessions/store.d.ts +103 -73
  316. package/dist/sessions/store.d.ts.map +1 -1
  317. package/dist/sessions/store.js +335 -186
  318. package/dist/sessions/store.js.map +1 -1
  319. package/dist/sessions/types.d.ts +44 -16
  320. package/dist/sessions/types.d.ts.map +1 -1
  321. package/dist/sessions/types.js +11 -2
  322. package/dist/sessions/types.js.map +1 -1
  323. package/dist/strings/errors.d.ts +32 -0
  324. package/dist/strings/errors.d.ts.map +1 -1
  325. package/dist/strings/errors.js +17 -0
  326. package/dist/strings/errors.js.map +1 -1
  327. package/dist/strings/labels.d.ts +1 -0
  328. package/dist/strings/labels.d.ts.map +1 -1
  329. package/dist/strings/labels.js +1 -0
  330. package/dist/strings/labels.js.map +1 -1
  331. package/dist/utils/activity.d.ts +101 -0
  332. package/dist/utils/activity.d.ts.map +1 -0
  333. package/dist/utils/activity.js +408 -0
  334. package/dist/utils/activity.js.map +1 -0
  335. package/dist/utils/git.d.ts +31 -0
  336. package/dist/utils/git.d.ts.map +1 -1
  337. package/dist/utils/git.js +87 -0
  338. package/dist/utils/git.js.map +1 -1
  339. package/dist/utils/index.d.ts +2 -0
  340. package/dist/utils/index.d.ts.map +1 -1
  341. package/dist/utils/index.js +1 -0
  342. package/dist/utils/index.js.map +1 -1
  343. package/dist/web-ui/_app/immutable/assets/0.tmlwn-Ih.css +1 -0
  344. package/dist/web-ui/_app/immutable/assets/9.BwwJybWx.css +1 -0
  345. package/dist/web-ui/_app/immutable/chunks/2KqE8gtn.js +1 -0
  346. package/dist/web-ui/_app/immutable/chunks/70-t_QvE.js +1 -0
  347. package/dist/web-ui/_app/immutable/chunks/AiWQj974.js +1 -0
  348. package/dist/web-ui/_app/immutable/chunks/{CPPfDSei.js → B25nWFyA.js} +4 -4
  349. package/dist/web-ui/_app/immutable/chunks/{DBYE9jOd.js → B2bcA_Q_.js} +1 -1
  350. package/dist/web-ui/_app/immutable/chunks/B5e5HYyB.js +1 -0
  351. package/dist/web-ui/_app/immutable/chunks/B7-5z6eA.js +1 -0
  352. package/dist/web-ui/_app/immutable/chunks/B7bGmhK0.js +1 -0
  353. package/dist/web-ui/_app/immutable/chunks/{DzO4hlg9.js → B8tYZKAE.js} +1 -1
  354. package/dist/web-ui/_app/immutable/chunks/{B5LJFxqa.js → BFGAyJjD.js} +1 -1
  355. package/dist/web-ui/_app/immutable/chunks/BG0850zf.js +1 -0
  356. package/dist/web-ui/_app/immutable/chunks/{DAMmvwn4.js → BG8eSzAd.js} +1 -1
  357. package/dist/web-ui/_app/immutable/chunks/BIMxXS8I.js +1 -0
  358. package/dist/web-ui/_app/immutable/chunks/BSzL1fpU.js +1 -0
  359. package/dist/web-ui/_app/immutable/chunks/BYtjHfeq.js +1 -0
  360. package/dist/web-ui/_app/immutable/chunks/{DxCk-KHc.js → Bp5pFYXL.js} +1 -1
  361. package/dist/web-ui/_app/immutable/chunks/{B8a0xDxR.js → BsJFsuAT.js} +1 -1
  362. package/dist/web-ui/_app/immutable/chunks/BvpNHcD6.js +1 -0
  363. package/dist/web-ui/_app/immutable/chunks/BypqA25-.js +1 -0
  364. package/dist/web-ui/_app/immutable/chunks/{BVA9Exy-.js → C0w6WDm5.js} +1 -1
  365. package/dist/web-ui/_app/immutable/chunks/C5_PAZ0y.js +1 -0
  366. package/dist/web-ui/_app/immutable/chunks/CDRO15Iv.js +1 -0
  367. package/dist/web-ui/_app/immutable/chunks/CF1CoqD5.js +1 -0
  368. package/dist/web-ui/_app/immutable/chunks/CS2sa4_m.js +1 -0
  369. package/dist/web-ui/_app/immutable/chunks/{BJ0JX3ea.js → CWUQwB9H.js} +1 -1
  370. package/dist/web-ui/_app/immutable/chunks/CY5FDdSU.js +1 -0
  371. package/dist/web-ui/_app/immutable/chunks/C_7MTDoj.js +1 -0
  372. package/dist/web-ui/_app/immutable/chunks/{D3vxvonu.js → CaAJD3dl.js} +1 -1
  373. package/dist/web-ui/_app/immutable/chunks/{BP352uRn.js → ChB5iyEL.js} +1 -1
  374. package/dist/web-ui/_app/immutable/chunks/{pE6cYWlS.js → ChQD-6N8.js} +1 -1
  375. package/dist/web-ui/_app/immutable/chunks/{Eo4gF7ih.js → CqbsoCwA.js} +1 -1
  376. package/dist/web-ui/_app/immutable/chunks/DCeJW50p.js +1 -0
  377. package/dist/web-ui/_app/immutable/chunks/{Cncwi6fQ.js → DJtZNgcs.js} +1 -1
  378. package/dist/web-ui/_app/immutable/chunks/DKIeaprD.js +1 -0
  379. package/dist/web-ui/_app/immutable/chunks/DLd2uVIA.js +1 -0
  380. package/dist/web-ui/_app/immutable/chunks/{DjcCz-PU.js → DW_subyT.js} +2 -2
  381. package/dist/web-ui/_app/immutable/chunks/DbU6lVn0.js +1 -0
  382. package/dist/web-ui/_app/immutable/chunks/Dc7ZCC5m.js +1 -0
  383. package/dist/web-ui/_app/immutable/chunks/Dd5umPsk.js +2 -0
  384. package/dist/web-ui/_app/immutable/chunks/{BysXJlZb.js → Dg_zDpDS.js} +1 -1
  385. package/dist/web-ui/_app/immutable/chunks/Dgqu8Yuc.js +1 -0
  386. package/dist/web-ui/_app/immutable/chunks/DmxsPZTB.js +1 -0
  387. package/dist/web-ui/_app/immutable/chunks/DphTaFUB.js +1 -0
  388. package/dist/web-ui/_app/immutable/chunks/DqK4iHp0.js +1 -0
  389. package/dist/web-ui/_app/immutable/chunks/{D9QNBZM2.js → DqT6OH_u.js} +2 -2
  390. package/dist/web-ui/_app/immutable/chunks/Ds9I9wQb.js +1 -0
  391. package/dist/web-ui/_app/immutable/chunks/Du5ng3u4.js +1 -0
  392. package/dist/web-ui/_app/immutable/chunks/DxJw79Wi.js +1 -0
  393. package/dist/web-ui/_app/immutable/chunks/GFTX8GgV.js +1 -0
  394. package/dist/web-ui/_app/immutable/chunks/{C076q4JN.js → HNjs76Zz.js} +1 -1
  395. package/dist/web-ui/_app/immutable/chunks/HVMjDi4_.js +1 -0
  396. package/dist/web-ui/_app/immutable/chunks/{BkOJ8DkV.js → P0A_fJvS.js} +1 -1
  397. package/dist/web-ui/_app/immutable/chunks/T3vGWjIL.js +1 -0
  398. package/dist/web-ui/_app/immutable/chunks/VTmrX9Qu.js +1 -0
  399. package/dist/web-ui/_app/immutable/chunks/{k_Qegko0.js → Xvwhx_F1.js} +1 -1
  400. package/dist/web-ui/_app/immutable/chunks/Yyz1XMQA.js +1 -0
  401. package/dist/web-ui/_app/immutable/chunks/{62JVKtnb.js → dh5HeqUr.js} +1 -1
  402. package/dist/web-ui/_app/immutable/chunks/fZMteyca.js +62 -0
  403. package/dist/web-ui/_app/immutable/chunks/{D82RulSH.js → gPrj-hqC.js} +1 -1
  404. package/dist/web-ui/_app/immutable/chunks/htcWMiYN.js +1 -0
  405. package/dist/web-ui/_app/immutable/chunks/{CwELQvbx.js → oTsvd9y4.js} +1 -1
  406. package/dist/web-ui/_app/immutable/chunks/qJfLUwU4.js +1 -0
  407. package/dist/web-ui/_app/immutable/chunks/xCtiO_JE.js +1 -0
  408. package/dist/web-ui/_app/immutable/chunks/{DvA-KON-.js → y4GeEH6k.js} +1 -1
  409. package/dist/web-ui/_app/immutable/entry/app.C4h_eOn6.js +2 -0
  410. package/dist/web-ui/_app/immutable/entry/start.CQFTf9ep.js +1 -0
  411. package/dist/web-ui/_app/immutable/nodes/0.Dh1xO970.js +1 -0
  412. package/dist/web-ui/_app/immutable/nodes/1.l75D3Opx.js +1 -0
  413. package/dist/web-ui/_app/immutable/nodes/10.DBidBPc-.js +1 -0
  414. package/dist/web-ui/_app/immutable/nodes/11.Ab0gUKWe.js +1 -0
  415. package/dist/web-ui/_app/immutable/nodes/12.CMsnoxfs.js +1 -0
  416. package/dist/web-ui/_app/immutable/nodes/13.D8YKuknB.js +1 -0
  417. package/dist/web-ui/_app/immutable/nodes/14.DZ0aan7y.js +1 -0
  418. package/dist/web-ui/_app/immutable/nodes/15.CUIKreDL.js +2 -0
  419. package/dist/web-ui/_app/immutable/nodes/16.BWc8--BO.js +1 -0
  420. package/dist/web-ui/_app/immutable/nodes/2.CDUonbuh.js +1 -0
  421. package/dist/web-ui/_app/immutable/nodes/3.Ctg3M00i.js +1 -0
  422. package/dist/web-ui/_app/immutable/nodes/4.Ci-JDwbA.js +2 -0
  423. package/dist/web-ui/_app/immutable/nodes/5.CTyEDAq0.js +1 -0
  424. package/dist/web-ui/_app/immutable/nodes/6.BTZZqsAb.js +1 -0
  425. package/dist/web-ui/_app/immutable/nodes/7.BI52g_Jo.js +137 -0
  426. package/dist/web-ui/_app/immutable/nodes/8.3hZPaB9x.js +1 -0
  427. package/dist/web-ui/_app/immutable/nodes/9.DS49kvwl.js +29 -0
  428. package/dist/web-ui/_app/version.json +1 -1
  429. package/dist/web-ui/favicon-192.png +0 -0
  430. package/dist/web-ui/favicon-32.png +0 -0
  431. package/dist/web-ui/favicon.ico +0 -0
  432. package/dist/web-ui/index.html +14 -14
  433. package/package.json +12 -6
  434. package/plugin/.claude-plugin/marketplace.json +1 -1
  435. package/plugin/.claude-plugin/plugin.json +1 -1
  436. package/plugin/plugins/kspec/skills/merge/SKILL.md +127 -0
  437. package/plugin/plugins/kspec/skills/plan/SKILL.md +55 -26
  438. package/plugin/plugins/kspec/skills/review/SKILL.md +350 -133
  439. package/plugin/plugins/kspec/skills/task-work/SKILL.md +96 -106
  440. package/templates/agents-sections/04-pr-workflow.md +15 -12
  441. package/templates/agents-sections/06-ralph-loop.md +15 -10
  442. package/templates/skills/manifest.yaml +25 -7
  443. package/templates/skills/merge/SKILL.md +120 -0
  444. package/templates/skills/plan/SKILL.md +55 -26
  445. package/templates/skills/review/SKILL.md +346 -130
  446. package/templates/skills/task-work/SKILL.md +93 -103
  447. package/dist/web-ui/_app/immutable/assets/0.BJaYkGW2.css +0 -1
  448. package/dist/web-ui/_app/immutable/assets/9.SzGLxi4x.css +0 -1
  449. package/dist/web-ui/_app/immutable/chunks/-lc0BifF.js +0 -1
  450. package/dist/web-ui/_app/immutable/chunks/8RBjHMN1.js +0 -1
  451. package/dist/web-ui/_app/immutable/chunks/B5wTVqxm.js +0 -1
  452. package/dist/web-ui/_app/immutable/chunks/B6VSmczZ.js +0 -1
  453. package/dist/web-ui/_app/immutable/chunks/BEOQc37C.js +0 -1
  454. package/dist/web-ui/_app/immutable/chunks/BHtYorjv.js +0 -1
  455. package/dist/web-ui/_app/immutable/chunks/BMuCqDX8.js +0 -1
  456. package/dist/web-ui/_app/immutable/chunks/BUZujXJ2.js +0 -1
  457. package/dist/web-ui/_app/immutable/chunks/BWET-efb.js +0 -1
  458. package/dist/web-ui/_app/immutable/chunks/BXkNecpt.js +0 -1
  459. package/dist/web-ui/_app/immutable/chunks/BYzrIfX8.js +0 -1
  460. package/dist/web-ui/_app/immutable/chunks/BpuwufMc.js +0 -1
  461. package/dist/web-ui/_app/immutable/chunks/BwMO4RrG.js +0 -1
  462. package/dist/web-ui/_app/immutable/chunks/C33JaVbg.js +0 -1
  463. package/dist/web-ui/_app/immutable/chunks/CGtqifKp.js +0 -1
  464. package/dist/web-ui/_app/immutable/chunks/CHDZZ7OG.js +0 -1
  465. package/dist/web-ui/_app/immutable/chunks/CUir3f4J.js +0 -60
  466. package/dist/web-ui/_app/immutable/chunks/CrCIbn0C.js +0 -1
  467. package/dist/web-ui/_app/immutable/chunks/D6TVmR9T.js +0 -1
  468. package/dist/web-ui/_app/immutable/chunks/D7LTux4W.js +0 -1
  469. package/dist/web-ui/_app/immutable/chunks/DAh4Wfku.js +0 -1
  470. package/dist/web-ui/_app/immutable/chunks/DAx07bEQ.js +0 -1
  471. package/dist/web-ui/_app/immutable/chunks/DOno4cA2.js +0 -1
  472. package/dist/web-ui/_app/immutable/chunks/DQA8NZIH.js +0 -2
  473. package/dist/web-ui/_app/immutable/chunks/DRfPm2bo.js +0 -1
  474. package/dist/web-ui/_app/immutable/chunks/DhQhksaB.js +0 -1
  475. package/dist/web-ui/_app/immutable/chunks/DjG7s6hm.js +0 -1
  476. package/dist/web-ui/_app/immutable/chunks/DkltRNvh.js +0 -1
  477. package/dist/web-ui/_app/immutable/chunks/DlaTnPKL.js +0 -1
  478. package/dist/web-ui/_app/immutable/chunks/ExCq5swK.js +0 -1
  479. package/dist/web-ui/_app/immutable/chunks/T3zZGv51.js +0 -1
  480. package/dist/web-ui/_app/immutable/chunks/XZumBYeP.js +0 -1
  481. package/dist/web-ui/_app/immutable/chunks/_ySfNjkF.js +0 -1
  482. package/dist/web-ui/_app/immutable/chunks/iEtR5cV6.js +0 -1
  483. package/dist/web-ui/_app/immutable/entry/app.Cgu6uKeS.js +0 -2
  484. package/dist/web-ui/_app/immutable/entry/start.9XifnLoB.js +0 -1
  485. package/dist/web-ui/_app/immutable/nodes/0.DISwcKSK.js +0 -1
  486. package/dist/web-ui/_app/immutable/nodes/1.Cx2Ufqp1.js +0 -1
  487. package/dist/web-ui/_app/immutable/nodes/10.C3z8ijXL.js +0 -1
  488. package/dist/web-ui/_app/immutable/nodes/11.DZdIjZmM.js +0 -1
  489. package/dist/web-ui/_app/immutable/nodes/12.FsIGfAOa.js +0 -1
  490. package/dist/web-ui/_app/immutable/nodes/13.DZoFwagf.js +0 -1
  491. package/dist/web-ui/_app/immutable/nodes/14.DaIzDKbQ.js +0 -1
  492. package/dist/web-ui/_app/immutable/nodes/15.BYyt4XWF.js +0 -2
  493. package/dist/web-ui/_app/immutable/nodes/16.CQkSqpOe.js +0 -1
  494. package/dist/web-ui/_app/immutable/nodes/2.Bkf_j2UJ.js +0 -1
  495. package/dist/web-ui/_app/immutable/nodes/3.kaMCurJG.js +0 -1
  496. package/dist/web-ui/_app/immutable/nodes/4.BSsFPTHG.js +0 -2
  497. package/dist/web-ui/_app/immutable/nodes/5.CpPlcCEZ.js +0 -1
  498. package/dist/web-ui/_app/immutable/nodes/6.BN4FqQmY.js +0 -1
  499. package/dist/web-ui/_app/immutable/nodes/7.9kBYIZik.js +0 -1
  500. package/dist/web-ui/_app/immutable/nodes/8.BuijtZ6B.js +0 -1
  501. package/dist/web-ui/_app/immutable/nodes/9.C-Weba8R.js +0 -1
@@ -1,46 +1,42 @@
1
1
  /**
2
2
  * Plan import CLI command
3
- * AC: @plan-import ac-11 through ac-33
3
+ * AC: @plan-import-content-only ac-draft-default through ac-module-stored
4
4
  */
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 { addChildItem, buildIndexes, createPlan, createSpecItem, createTask, getAuthor, initContext, loadAllTasks, loadPlans, savePlan, saveTask, updateSpecItem, } from "../../parser/index.js";
9
- import { parsePlanDocument, topologicalSort, validateParentRefs, } from "../../parser/plan-document.js";
8
+ import { buildIndexes, createPlan, findPlanByRef, getAuthor, initContext, mutatePlanAtomically, loadPlans, savePlan, } from "../../parser/index.js";
10
9
  import { commitIfShadow } from "../../parser/shadow.js";
11
- import { normalizeRefInput } from "../../schema/index.js";
10
+ import { PlanStatusSchema, } from "../../schema/index.js";
12
11
  import { errors } from "../../strings/index.js";
13
12
  import { EXIT_CODES } from "../exit-codes.js";
14
- import { error, info, isJsonMode, output, success, warn } from "../output.js";
13
+ import { error, isJsonMode, output, success, warn } from "../output.js";
15
14
  import { ulid } from "ulid";
16
15
  /**
17
- * Register plan import command
18
- * AC: @plan-import ac-11, ac-15, ac-32
16
+ * Register plan import command.
17
+ * AC: @plan-import-content-only ac-module-optional, ac-status-override, ac-update-ignored
19
18
  */
20
19
  export function registerPlanImportCommand(planCommand) {
21
20
  markMutating(planCommand.command("import <path>"))
22
- .description("Import plan document and auto-generate specs/tasks")
23
- .requiredOption("--module <ref>", "Module to add specs under (e.g., @core-module)")
24
- .option("--dry-run", "Show what would be created without making changes")
25
- .option("--update", "Update existing specs instead of skipping them")
21
+ .description("Import a plan document as stored content without deriving work")
22
+ .option("--into <ref>", "Update an existing draft or approved plan with file content")
23
+ .option("--module <ref>", "Optional module to store on the plan for later derive")
24
+ .option("--status <status>", "Initial plan status (default: draft)")
25
+ .option("--dry-run", "Show the plan record that would be created without saving")
26
+ .option("--update", "Ignored for content-only import; derivation happens separately")
27
+ .option("--reason <text>", "Reason note when updating an existing plan via --into")
26
28
  .option("--json", "Output as JSON")
27
29
  .addHelpText("after", `
28
30
  Format:
29
- Plan documents use markdown with a ## Specs section containing a fenced
30
- YAML code block. The YAML must be wrapped in triple-backtick yaml markers:
31
-
32
- ## Specs
33
- \`\`\`yaml
34
- - title: My Feature
35
- type: feature
36
- \`\`\`
37
-
38
- Without the fenced code block, specs will not be detected.
31
+ Import stores the markdown document as plan content. Specs and tasks are not
32
+ created during import; use "kspec plan derive" after approval to materialize
33
+ the stored document.
39
34
 
40
35
  Examples:
41
- $ kspec plan import ./plan.md --module @core
42
- $ kspec plan import ./plan.md --module @api --dry-run
43
- $ kspec plan import ./plan.md --module @features --update --json`)
36
+ $ kspec plan import ./plan.md
37
+ $ kspec plan import ./plan.md --status approved --json
38
+ $ kspec plan import ./plan.md --module @core --dry-run
39
+ $ kspec plan import ./edited.md --into @plan-ref --reason "Addressed review feedback"`)
44
40
  .action(async (planPath, options) => {
45
41
  try {
46
42
  await importPlan(planPath, options);
@@ -51,30 +47,12 @@ Examples:
51
47
  }
52
48
  });
53
49
  }
54
- function mergeAcceptanceCriteriaById(existingCriteria, incomingCriteria) {
55
- if (!existingCriteria || existingCriteria.length === 0) {
56
- return incomingCriteria;
57
- }
58
- const incomingById = new Map(incomingCriteria.map(ac => [ac.id, ac]));
59
- const existingIds = new Set(existingCriteria.map(ac => ac.id));
60
- const merged = existingCriteria.map(ac => incomingById.get(ac.id) ?? ac);
61
- for (const ac of incomingCriteria) {
62
- if (!existingIds.has(ac.id)) {
63
- merged.push(ac);
64
- }
65
- }
66
- return merged;
67
- }
68
50
  /**
69
- * Import plan document and create specs/tasks
70
- * AC: @plan-import ac-11 through ac-33
51
+ * Import plan document as content-only storage.
52
+ * AC: @plan-import-content-only ac-draft-default through ac-module-stored
71
53
  */
72
54
  async function importPlan(planPath, options) {
73
- // JSON mode is set by global preAction hook
74
55
  const ctx = await initContext();
75
- const author = getAuthor(ctx.config?.identity?.author);
76
- // Read plan file
77
- // AC: @plan-import ac-21 - Handle file read errors
78
56
  const fullPath = path.resolve(process.cwd(), planPath);
79
57
  let content;
80
58
  try {
@@ -84,513 +62,220 @@ async function importPlan(planPath, options) {
84
62
  error(`Failed to read plan file: ${planPath}`, err);
85
63
  process.exit(EXIT_CODES.USAGE_ERROR);
86
64
  }
87
- // Parse plan document
88
- // AC: @plan-import ac-11, ac-12, ac-13, ac-21, ac-22
89
- const parsed = parsePlanDocument(content);
90
- // Report parsing errors
91
- if (parsed.errors.length > 0) {
92
- for (const err of parsed.errors) {
93
- if (err.type === "yaml") {
94
- // AC: @plan-import ac-21 - YAML parse errors (fatal)
95
- error(err.message);
96
- process.exit(EXIT_CODES.USAGE_ERROR);
97
- }
98
- else if (err.type === "validation") {
99
- // AC: @plan-import ac-22 - Validation errors (non-fatal, add to warnings)
100
- warn(err.message);
101
- }
102
- }
65
+ if (options.into) {
66
+ await importIntoExistingPlan(ctx, fullPath, content, options);
67
+ return;
103
68
  }
104
- // Load plans for cross-namespace slug safety
105
- const plans = await loadPlans(ctx);
106
- // Build indexes to check existing specs (include plans for cross-namespace slug safety)
107
- const { refIndex, items } = await buildIndexes(ctx, plans);
108
- const existingSpecRefs = new Set();
109
- for (const item of items) {
110
- for (const slug of item.slugs || []) {
111
- existingSpecRefs.add(slug);
112
- }
113
- existingSpecRefs.add(refIndex.shortUlid(item._ulid));
114
- }
115
- // Resolve module reference
116
- const moduleResult = refIndex.resolve(options.module);
117
- if (!moduleResult.ok) {
118
- error(errors.reference.itemNotFound(options.module));
119
- process.exit(EXIT_CODES.NOT_FOUND);
69
+ if (options.update) {
70
+ warn("--update is ignored for content-only import. Use `kspec plan derive` to materialize specs and tasks.");
120
71
  }
121
- const moduleItem = moduleResult.item;
122
- // Verify it's a module
123
- if (moduleItem.type !== "module") {
124
- error(`${options.module} is not a module (type: ${moduleItem.type})`);
72
+ const statusResult = PlanStatusSchema.safeParse(options.status || "draft");
73
+ if (!statusResult.success) {
74
+ error(`Invalid status: '${options.status}'. Valid values: ${PlanStatusSchema.options.join(", ")}`);
125
75
  process.exit(EXIT_CODES.USAGE_ERROR);
126
76
  }
127
- // Validate parent references
128
- // AC: @plan-import ac-17, ac-33
129
- const parentErrors = validateParentRefs(parsed.specs, existingSpecRefs);
130
- parsed.errors.push(...parentErrors);
131
- // Sort specs topologically
132
- // AC: @plan-import ac-16, ac-18
133
- const { sorted, error: sortError } = topologicalSort(parsed.specs);
134
- if (sortError) {
135
- // AC: @plan-import ac-18 - Circular dependency detection
136
- error(sortError.message);
137
- process.exit(EXIT_CODES.USAGE_ERROR);
77
+ const plans = await loadPlans(ctx);
78
+ const { refIndex } = await buildIndexes(ctx, plans);
79
+ let storedModuleRef = null;
80
+ if (options.module) {
81
+ const moduleResult = refIndex.resolve(options.module);
82
+ if (!moduleResult.ok) {
83
+ error(errors.reference.itemNotFound(options.module));
84
+ process.exit(EXIT_CODES.NOT_FOUND);
85
+ }
86
+ const moduleItem = moduleResult.item;
87
+ if (moduleItem.type !== "module") {
88
+ error(`${options.module} is not a module (type: ${moduleItem.type})`);
89
+ process.exit(EXIT_CODES.USAGE_ERROR);
90
+ }
91
+ storedModuleRef = options.module.startsWith("@")
92
+ ? options.module
93
+ : `@${options.module}`;
138
94
  }
139
- // Create plan record
140
- // AC: @plan-import ac-24, ac-28
141
- // Auto-namespace plan slugs with "plan-" prefix to prevent collision with spec slugs
142
- // Ensure uniqueness across all namespaces (plans + specs + tasks via refIndex)
143
- let planSlug = `plan-${generateSlug(parsed.title)}`;
144
- let counter = 1;
145
- const baseSlug = planSlug;
146
- while (!refIndex.isSlugAvailable(planSlug)) {
147
- planSlug = `${baseSlug}-${counter}`;
148
- counter++;
95
+ const title = extractOptionalPlanTitle(content) ?? "Untitled Plan";
96
+ const planSlug = nextAvailablePlanSlug(title, refIndex);
97
+ const preview = {
98
+ ref: `@${planSlug}`,
99
+ title,
100
+ status: statusResult.data,
101
+ content,
102
+ module_ref: storedModuleRef,
103
+ source_path: fullPath,
104
+ derived_specs: [],
105
+ derived_tasks: [],
106
+ changes: [],
107
+ note_message: null,
108
+ };
109
+ if (options.dryRun) {
110
+ emitImportResult(preview, { dryRun: true });
111
+ return;
149
112
  }
150
- // Track all slugs created during this import (refIndex is stale after mutations)
151
- const importReservedSlugs = new Set([planSlug]);
152
113
  const planInput = {
153
- title: parsed.title,
154
- content: parsed.content,
155
- status: "active",
114
+ title,
115
+ content,
116
+ status: statusResult.data,
156
117
  slugs: [planSlug],
157
118
  source_path: fullPath,
119
+ module_ref: storedModuleRef,
158
120
  };
159
- const newPlan = createPlan(planInput);
160
- const planRef = `@${planSlug}`;
161
- // AC: @plan-import ac-13 - Attach global implementation notes to plan record
162
- if (parsed.implementationNotes) {
163
- newPlan.notes.push({
164
- _ulid: ulid(),
165
- created_at: new Date().toISOString(),
166
- author,
167
- content: `Implementation notes:\n\n${parsed.implementationNotes}`,
168
- });
121
+ const plan = createPlan(planInput);
122
+ await saveImportedPlan(ctx, plan, preview);
123
+ }
124
+ async function saveImportedPlan(ctx, plan, preview) {
125
+ await savePlan(ctx, plan);
126
+ await commitIfShadow(ctx.shadow, "plan-import", plan.slugs[0] || plan._ulid.slice(0, 8), plan.title);
127
+ emitImportResult({
128
+ ...preview,
129
+ ref: plan.slugs[0] ? `@${plan.slugs[0]}` : `@${plan._ulid}`,
130
+ }, { dryRun: false, createdAt: plan.created_at });
131
+ }
132
+ async function importIntoExistingPlan(ctx, fullPath, content, options) {
133
+ const foundPlan = await findPlanByRef(ctx, options.into);
134
+ if (!foundPlan) {
135
+ exitImportWithGuidance(errors.reference.planNotFound(options.into), EXIT_CODES.NOT_FOUND, "Check available plans with: kspec plan list", { ref: options.into, entity: "plan" });
169
136
  }
170
- // Initialize result
171
- const result = {
172
- createdSpecs: [],
173
- updatedSpecs: [],
174
- createdTasks: [],
175
- errors: [],
176
- skipped: [],
177
- planRef,
137
+ const titleFromFile = extractOptionalPlanTitle(content);
138
+ const nextTitle = titleFromFile ?? foundPlan.title;
139
+ const noteMessage = options.reason || "Content updated from file";
140
+ const preview = {
141
+ ref: foundPlan.slugs[0] ? `@${foundPlan.slugs[0]}` : `@${foundPlan._ulid}`,
142
+ title: nextTitle,
143
+ status: foundPlan.status,
144
+ content,
145
+ module_ref: foundPlan.module_ref ?? null,
146
+ source_path: fullPath,
147
+ derived_specs: [...foundPlan.derived_specs],
148
+ derived_tasks: [...foundPlan.derived_tasks],
149
+ changes: titleFromFile ? ["title", "content"] : ["content"],
150
+ note_message: noteMessage,
178
151
  };
179
- // Add parser validation errors to result
180
- // AC: @plan-import ac-22, ac-29 - Include parser validation errors in summary
181
- for (const err of parsed.errors) {
182
- if (err.type === "validation") {
183
- result.errors.push({ message: err.message });
184
- }
152
+ if (options.module) {
153
+ warn("--module is ignored with --into. Existing plan module assignment is unchanged.");
185
154
  }
186
- // Track newly created specs for parent resolution
187
- // AC: @plan-import ac-16 - Resolve local references
188
- const createdSpecsMap = new Map();
189
- // Process specs
190
- // AC: @plan-import ac-14, ac-16, ac-17, ac-22, ac-23, ac-25, ac-26, ac-29
191
- for (let i = 0; i < sorted.length; i++) {
192
- const spec = sorted[i];
193
- try {
194
- // Validate required fields first
195
- // AC: @plan-import ac-22
196
- if (!spec.title) {
197
- const errMsg = `Spec at index ${i} missing required field: title`;
198
- warn(errMsg);
199
- result.errors.push({ message: errMsg, spec });
200
- continue;
201
- }
202
- const specSlug = spec.slug || generateSlug(spec.title);
203
- const specRef = `@${specSlug}`;
204
- // Check against slugs reserved during this import (plan slug, earlier specs)
205
- if (importReservedSlugs.has(specSlug)) {
206
- warn(`Spec slug "${specSlug}" collides with another item in this import, skipping`);
207
- result.skipped.push({ slug: specSlug, reason: "Slug collision within import" });
208
- continue;
209
- }
210
- // Check if spec already exists in pre-import state
211
- const exists = refIndex.resolve(specRef);
212
- if (exists.ok && !options.update) {
213
- // AC: @plan-import ac-14, ac-25 - Skip existing specs
214
- warn(`Skipping existing spec: ${specRef}`);
215
- result.skipped.push({ slug: specSlug, reason: "Already exists" });
216
- continue;
217
- }
218
- if (exists.ok && options.update) {
219
- // AC: @plan-import ac-26 - Update existing spec
220
- // Updates: description, type, tags, traits, and acceptance_criteria
221
- const existingSpec = exists.item;
222
- if (options.dryRun) {
223
- info(`Would update spec: ${specRef}`);
224
- result.updatedSpecs.push(specRef);
225
- }
226
- else {
227
- // Build updates from plan spec
228
- const updates = {};
229
- if (spec.description !== undefined) {
230
- updates.description = spec.description;
231
- }
232
- if (spec.type !== undefined) {
233
- updates.type = spec.type;
234
- }
235
- if (spec.traits !== undefined) {
236
- updates.traits = spec.traits.map(normalizeRefInput);
237
- }
238
- if (spec.depends_on !== undefined) {
239
- updates.depends_on = spec.depends_on.map(normalizeRefInput);
240
- }
241
- if (spec.acceptance_criteria !== undefined) {
242
- updates.acceptance_criteria = mergeAcceptanceCriteriaById(existingSpec.acceptance_criteria, spec.acceptance_criteria);
243
- }
244
- await updateSpecItem(ctx, existingSpec, updates);
245
- info(`Updated spec: ${specRef}`);
246
- result.updatedSpecs.push(specRef);
247
- }
248
- continue;
249
- }
250
- // Resolve parent reference
251
- // AC: @plan-import ac-16, ac-17
252
- let parent = null;
253
- if (spec.parent) {
254
- const parentRef = normalizeRefInput(spec.parent);
255
- // Check if parent was just created in this import
256
- const parentSlug = parentRef.slice(1);
257
- if (createdSpecsMap.has(parentSlug)) {
258
- parent = createdSpecsMap.get(parentSlug);
259
- }
260
- else {
261
- // Check existing specs
262
- const parentResult = refIndex.resolve(parentRef);
263
- if (!parentResult.ok) {
264
- // AC: @plan-import ac-17, ac-33 - Missing parent with hint
265
- const errMsg = `Parent ${parentRef} not found. Check parent exists or define it earlier in plan`;
266
- warn(errMsg);
267
- result.errors.push({ message: errMsg, spec });
268
- result.skipped.push({ slug: specSlug, reason: "Missing parent" });
269
- continue;
270
- }
271
- parent = parentResult.item;
272
- }
273
- }
274
- // Determine if this is a root-level trait (type: trait with no parent)
275
- const isRootTrait = (spec.type === "trait") && !parent;
276
- if (options.dryRun) {
277
- // AC: @plan-import ac-15 - Dry run mode
278
- info(`Would create spec: ${specRef} ${spec.parent ? `under ${spec.parent}` : isRootTrait ? "(project-level trait)" : "(root)"}`);
279
- result.createdSpecs.push(specRef);
280
- importReservedSlugs.add(specSlug);
281
- // Track would-be created spec for parent resolution in dry-run
282
- // AC: @plan-import ac-35 - Map depends_on in dry-run too
283
- const dryRunSpec = createSpecItem({
284
- title: spec.title,
285
- type: spec.type || "feature",
286
- slugs: [specSlug],
287
- description: spec.description,
288
- ...(spec.acceptance_criteria ? { acceptance_criteria: spec.acceptance_criteria } : {}),
289
- priority: undefined,
290
- tags: [],
291
- depends_on: (spec.depends_on || []).map(normalizeRefInput),
292
- implements: [],
293
- relates_to: [],
294
- tests: [],
295
- traits: (spec.traits || []).map(normalizeRefInput),
296
- notes: [],
297
- });
298
- createdSpecsMap.set(specSlug, dryRunSpec);
299
- }
300
- else {
301
- // Create spec item
302
- // AC: @plan-import ac-35 - Map depends_on to spec depends_on
303
- const specInput = {
304
- title: spec.title,
305
- type: spec.type || "feature",
306
- slugs: [specSlug],
307
- description: spec.description,
308
- priority: undefined,
309
- tags: [],
310
- depends_on: (spec.depends_on || []).map(normalizeRefInput),
311
- implements: [],
312
- relates_to: [],
313
- tests: [],
314
- traits: (spec.traits || []).map(normalizeRefInput),
315
- notes: [],
316
- };
317
- // Add acceptance criteria if present
318
- if (spec.acceptance_criteria) {
319
- specInput.acceptance_criteria = spec.acceptance_criteria;
320
- }
321
- const newSpec = createSpecItem(specInput);
322
- if (isRootTrait) {
323
- // Traits without a parent go to kynetic.yaml project-level traits array,
324
- // not the module-level traits array (same logic as `kspec trait add`)
325
- if (!ctx.manifestPath) {
326
- throw new Error("Could not find kynetic.yaml");
327
- }
328
- const { readYamlFile, writeYamlFilePreserveFormat } = await import("../../parser/yaml.js");
329
- const manifest = await readYamlFile(ctx.manifestPath);
330
- if (!manifest) {
331
- throw new Error("Could not load kynetic.yaml");
332
- }
333
- // Ensure traits array exists at root
334
- if (!Array.isArray(manifest.traits)) {
335
- manifest.traits = [];
336
- }
337
- // Strip metadata from newSpec (_sourceFile, _path)
338
- const { _sourceFile, _path, ...cleanItem } = newSpec;
339
- manifest.traits.push(cleanItem);
340
- await writeYamlFilePreserveFormat(ctx.manifestPath, manifest);
341
- const traitIndex = manifest.traits.length - 1;
342
- const createdSpec = {
343
- ...newSpec,
344
- _sourceFile: ctx.manifestPath,
345
- _path: `traits[${traitIndex}]`,
346
- };
347
- createdSpecsMap.set(specSlug, createdSpec);
348
- }
349
- else {
350
- // Add spec to parent (or module if no parent)
351
- const actualParent = parent || moduleItem;
352
- const addResult = await addChildItem(ctx, actualParent, newSpec);
353
- // Track the created spec for parent resolution
354
- // Need to construct LoadedSpecItem with _sourceFile and _path for nested specs
355
- const createdSpec = {
356
- ...addResult.item,
357
- _sourceFile: actualParent._sourceFile,
358
- _path: addResult.path,
359
- };
360
- createdSpecsMap.set(specSlug, createdSpec);
361
- }
362
- result.createdSpecs.push(specRef);
363
- importReservedSlugs.add(specSlug);
364
- newPlan.derived_specs.push(specRef);
365
- info(`Created spec: ${specRef}`);
366
- }
367
- }
368
- catch (err) {
369
- const errMsg = `Failed to create spec "${spec.title}": ${err instanceof Error ? err.message : String(err)}`;
370
- warn(errMsg);
371
- result.errors.push({ message: errMsg, spec });
372
- }
155
+ if (options.update) {
156
+ warn("--update is ignored with --into. Existing plan specs are not modified during re-import.");
373
157
  }
374
- // Derive tasks from specs
375
- // AC: @plan-import ac-12, ac-13, ac-19, ac-20, ac-35
376
- if (parsed.tasks.derive_from_specs && result.createdSpecs.length > 0) {
377
- const tasks = await loadAllTasks(ctx);
378
- // Combine existing task slugs + all slugs reserved during this import (plan + specs)
379
- const importSlugs = new Set([
380
- ...tasks.flatMap((t) => t.slugs),
381
- ...importReservedSlugs,
382
- ]);
383
- // AC: @plan-import ac-35 - Build spec→task slug mapping for depends_on resolution
384
- // First pass: generate all task slugs so we can resolve spec depends_on → task depends_on
385
- const specToTaskSlug = new Map();
386
- for (const specRef of result.createdSpecs) {
387
- const specSlug = specRef.slice(1);
388
- const spec = createdSpecsMap.get(specSlug);
389
- if (!spec)
390
- continue;
391
- const taskTitle = `Implement ${spec.title}`;
392
- const taskSlug = generateSlug(taskTitle);
393
- let uniqueSlug = taskSlug;
394
- let counter = 1;
395
- while (importSlugs.has(uniqueSlug) || !refIndex.isSlugAvailable(uniqueSlug)) {
396
- uniqueSlug = `${taskSlug}-${counter}`;
397
- counter++;
398
- }
399
- importSlugs.add(uniqueSlug);
400
- importReservedSlugs.add(uniqueSlug);
401
- specToTaskSlug.set(specSlug, uniqueSlug);
402
- }
403
- for (const specRef of result.createdSpecs) {
404
- // Get spec from createdSpecsMap (works in both dry-run and real mode)
405
- const specSlug = specRef.slice(1);
406
- const spec = createdSpecsMap.get(specSlug);
407
- if (!spec)
408
- continue;
409
- const uniqueSlug = specToTaskSlug.get(specSlug);
410
- if (options.dryRun) {
411
- info(`Would derive task: @${uniqueSlug} from ${specRef}`);
412
- result.createdTasks.push(`@${uniqueSlug}`);
413
- }
414
- else {
415
- // AC: @plan-import ac-35 - Resolve spec depends_on to task depends_on
416
- const planSpec = parsed.specs.find(s => (s.slug || generateSlug(s.title)) === specSlug);
417
- const taskDependsOn = [];
418
- if (planSpec?.depends_on) {
419
- for (const depRef of planSpec.depends_on) {
420
- const depSlug = depRef.startsWith("@") ? depRef.slice(1) : depRef;
421
- const depTaskSlug = specToTaskSlug.get(depSlug);
422
- if (depTaskSlug) {
423
- // Dependency spec was in this import — link to its derived task
424
- taskDependsOn.push(`@${depTaskSlug}`);
425
- }
426
- else {
427
- // Dependency spec already existed — find an existing task with that spec_ref
428
- const normalizedDepRef = normalizeRefInput(depRef);
429
- const existingTask = tasks.find(t => t.spec_ref === normalizedDepRef);
430
- if (existingTask && existingTask.slugs.length > 0) {
431
- taskDependsOn.push(`@${existingTask.slugs[0]}`);
432
- }
433
- else {
434
- // No task found for this spec — pass the spec ref as-is
435
- taskDependsOn.push(normalizedDepRef);
436
- }
437
- }
438
- }
439
- }
440
- // AC: @plan-import ac-19 - Task has both spec_ref and plan_ref
441
- // AC: @plan-import ac-36 - Derived task inherits priority from plan spec
442
- const taskInput = {
443
- title: `Implement ${spec.title}`,
444
- type: "task",
445
- spec_ref: specRef,
446
- plan_ref: planRef,
447
- priority: planSpec?.priority ?? 3,
448
- slugs: [uniqueSlug],
449
- tags: [],
450
- depends_on: taskDependsOn,
451
- notes: [],
452
- };
453
- const newTask = createTask(taskInput);
454
- // AC: @plan-import ac-13 - Scoped implementation notes
455
- if (planSpec?.implementation_notes) {
456
- // Per-spec notes go directly to this task
457
- newTask.notes.push({
458
- _ulid: ulid(),
459
- created_at: new Date().toISOString(),
460
- author,
461
- content: `Implementation notes:\n\n${planSpec.implementation_notes}`,
462
- });
463
- }
464
- else if (parsed.implementationNotes) {
465
- // No per-spec notes — reference the plan
466
- newTask.notes.push({
467
- _ulid: ulid(),
468
- created_at: new Date().toISOString(),
469
- author,
470
- content: `See plan ${planRef} for implementation notes`,
471
- });
472
- }
473
- await saveTask(ctx, newTask);
474
- result.createdTasks.push(`@${uniqueSlug}`);
475
- newPlan.derived_tasks.push(`@${uniqueSlug}`);
476
- info(`Derived task: @${uniqueSlug}`);
477
- }
478
- }
158
+ if (options.status) {
159
+ warn("--status is ignored with --into. Existing plan status is unchanged.");
479
160
  }
480
- // Create manual tasks
481
- // AC: @plan-import ac-27
482
- if (parsed.tasks.additional_tasks) {
483
- // Reload tasks to catch any created by derive loop above
484
- // Combine with all slugs reserved during this import (plan + specs)
485
- const manualTasks = await loadAllTasks(ctx);
486
- const manualImportSlugs = new Set([
487
- ...manualTasks.flatMap((t) => t.slugs),
488
- ...importReservedSlugs,
489
- ]);
490
- for (const taskDef of parsed.tasks.additional_tasks) {
491
- const taskSlug = taskDef.slug || generateSlug(taskDef.title);
492
- // Ensure slug uniqueness across all namespaces + this import's slugs
493
- let uniqueSlug = taskSlug;
494
- let counter = 1;
495
- while (manualImportSlugs.has(uniqueSlug) || !refIndex.isSlugAvailable(uniqueSlug)) {
496
- uniqueSlug = `${taskSlug}-${counter}`;
497
- counter++;
498
- }
499
- manualImportSlugs.add(uniqueSlug);
500
- if (options.dryRun) {
501
- info(`Would create manual task: @${uniqueSlug}`);
502
- result.createdTasks.push(`@${uniqueSlug}`);
503
- }
504
- else {
505
- // AC: @plan-import ac-27, ac-35 - Manual tasks have plan_ref; spec_ref and depends_on honored
506
- const taskInput = {
507
- title: taskDef.title,
508
- type: "task",
509
- plan_ref: planRef,
510
- priority: taskDef.priority || 3,
511
- slugs: [uniqueSlug],
512
- tags: taskDef.tags || [],
513
- depends_on: (taskDef.depends_on || []).map(normalizeRefInput),
514
- notes: [],
515
- ...(taskDef.spec_ref ? { spec_ref: normalizeRefInput(taskDef.spec_ref) } : {}),
516
- };
517
- if (taskDef.description) {
518
- taskInput.notes = [
519
- {
520
- _ulid: ulid(),
521
- created_at: new Date().toISOString(),
522
- author,
523
- content: taskDef.description,
524
- },
525
- ];
526
- }
527
- const newTask = createTask(taskInput);
528
- await saveTask(ctx, newTask);
529
- result.createdTasks.push(`@${uniqueSlug}`);
530
- newPlan.derived_tasks.push(`@${uniqueSlug}`);
531
- info(`Created manual task: @${uniqueSlug}`);
532
- }
533
- }
161
+ assertImportIntoAllowed(foundPlan);
162
+ if (options.dryRun) {
163
+ emitImportResult(preview, { dryRun: true });
164
+ return;
534
165
  }
535
- // Save plan record
536
- // AC: @plan-import ac-24
537
- if (!options.dryRun) {
538
- await savePlan(ctx, newPlan);
539
- await commitIfShadow(ctx.shadow, "plan-import", planSlug, parsed.title);
166
+ const author = getAuthor(ctx.config?.identity?.author);
167
+ const note = createPlanNote(noteMessage, author);
168
+ const updatedPlan = await mutatePlanAtomically(ctx, foundPlan, (latestPlan) => {
169
+ assertImportIntoAllowed(latestPlan);
170
+ return {
171
+ ...latestPlan,
172
+ title: nextTitle,
173
+ content,
174
+ notes: [...latestPlan.notes, note],
175
+ };
176
+ });
177
+ await commitIfShadow(ctx.shadow, "plan-import", updatedPlan.slugs[0] || updatedPlan._ulid.slice(0, 8), updatedPlan.title);
178
+ emitImportResult(preview, { dryRun: false, createdAt: updatedPlan.created_at });
179
+ }
180
+ function assertImportIntoAllowed(plan) {
181
+ if (plan.status === "active") {
182
+ exitImportWithGuidance("Cannot update active plan. Derive is a one-shot operation.", EXIT_CODES.CONFLICT, "Create a new plan iteration instead of re-importing into an active plan.", {
183
+ current_status: plan.status,
184
+ valid_statuses: ["draft", "approved"],
185
+ });
540
186
  }
541
- // AC: @plan-import ac-23, ac-29 - Summary output
542
- const successCount = result.createdSpecs.length +
543
- result.updatedSpecs.length +
544
- result.createdTasks.length;
545
- const errorCount = result.errors.length + result.skipped.length;
546
- // AC: @plan-import ac-32 - JSON output
547
- if (isJsonMode()) {
548
- output({
549
- plan: planRef,
550
- created_specs: result.createdSpecs,
551
- updated_specs: result.updatedSpecs,
552
- created_tasks: result.createdTasks,
553
- errors: result.errors,
554
- skipped: result.skipped,
187
+ if (plan.status === "completed" || plan.status === "rejected") {
188
+ exitImportWithGuidance("Cannot update plan in terminal status", EXIT_CODES.CONFLICT, "Reopen or replace the plan with a new draft/approved plan.", {
189
+ current_status: plan.status,
190
+ valid_statuses: ["draft", "approved"],
555
191
  });
556
192
  }
557
- else {
558
- // Human-readable output
559
- if (options.dryRun) {
560
- console.log("\nDry run - no changes made\n");
561
- }
562
- console.log(`Plan: ${planRef}`);
563
- console.log(`Created ${result.createdSpecs.length} specs`);
564
- if (result.updatedSpecs.length > 0) {
565
- console.log(`Updated ${result.updatedSpecs.length} specs`);
566
- }
567
- console.log(`Created ${result.createdTasks.length} tasks`);
568
- if (result.errors.length > 0) {
569
- console.log(`\nErrors (${result.errors.length}):`);
570
- for (const err of result.errors) {
571
- console.log(` - ${err.message}`);
572
- }
573
- }
574
- if (result.skipped.length > 0) {
575
- console.log(`\nSkipped (${result.skipped.length}):`);
576
- for (const skip of result.skipped) {
577
- console.log(` - @${skip.slug}: ${skip.reason}`);
578
- }
193
+ }
194
+ function createPlanNote(content, author) {
195
+ return {
196
+ _ulid: ulid(),
197
+ created_at: new Date().toISOString(),
198
+ author,
199
+ content,
200
+ };
201
+ }
202
+ function exitImportWithGuidance(message, exitCode, suggestion, details) {
203
+ if (suggestion) {
204
+ if (isJsonMode()) {
205
+ error(message, {
206
+ ...details,
207
+ suggestion,
208
+ guidance: suggestion,
209
+ });
579
210
  }
580
- // AC: @plan-import ac-15, ac-23 - Exit code 0 on success
581
- if (!options.dryRun && successCount > 0) {
582
- success(`\nImported plan: ${successCount} items created, ${errorCount} errors`);
211
+ else {
212
+ error(message);
213
+ console.error(`Suggestion: ${suggestion}`);
583
214
  }
584
- } // end of else block (non-JSON output)
585
- // Exit code logic
586
- // AC: @plan-import ac-15 - Dry run exits 0
587
- // AC: @plan-import ac-23 - Success with some errors still exits 0
588
- // Return normally so batch mode treats this as success instead of
589
- // intercepting process.exit(0) as an exception path.
590
- return;
215
+ }
216
+ else {
217
+ error(message, isJsonMode() ? details : undefined);
218
+ }
219
+ process.exit(exitCode);
220
+ }
221
+ function emitImportResult(preview, options) {
222
+ const payload = {
223
+ dry_run: options.dryRun,
224
+ plan_ref: preview.ref,
225
+ title: preview.title,
226
+ status: preview.status,
227
+ module_ref: preview.module_ref,
228
+ source_path: preview.source_path,
229
+ created_at: options.createdAt ?? null,
230
+ derived_specs: preview.derived_specs,
231
+ derived_tasks: preview.derived_tasks,
232
+ content: preview.content,
233
+ changes: preview.changes,
234
+ note_message: preview.note_message,
235
+ };
236
+ if (isJsonMode()) {
237
+ output(payload);
238
+ return;
239
+ }
240
+ if (options.dryRun) {
241
+ console.log("Dry run - no changes made\n");
242
+ }
243
+ console.log(`Plan: ${preview.ref}`);
244
+ console.log(`Title: ${preview.title}`);
245
+ console.log(`Status: ${preview.status}`);
246
+ if (preview.module_ref) {
247
+ console.log(`Stored module: ${preview.module_ref}`);
248
+ }
249
+ console.log(`Source: ${preview.source_path}`);
250
+ console.log("Content stored: full document");
251
+ console.log("Derived specs: 0");
252
+ console.log("Derived tasks: 0");
253
+ if (preview.changes.length > 0) {
254
+ console.log(`Changes: ${preview.changes.join(", ")}`);
255
+ }
256
+ if (preview.note_message) {
257
+ console.log(`Note: ${preview.note_message}`);
258
+ }
259
+ if (!options.dryRun) {
260
+ success("Imported plan content");
261
+ }
262
+ }
263
+ function extractOptionalPlanTitle(content) {
264
+ const titleMatch = content.match(/^#\s+(.+)$/m);
265
+ return titleMatch ? titleMatch[1].trim() : null;
266
+ }
267
+ function nextAvailablePlanSlug(title, refIndex) {
268
+ let planSlug = `plan-${generateSlug(title)}`;
269
+ let counter = 1;
270
+ const baseSlug = planSlug;
271
+ while (!refIndex.isSlugAvailable(planSlug)) {
272
+ planSlug = `${baseSlug}-${counter}`;
273
+ counter++;
274
+ }
275
+ return planSlug;
591
276
  }
592
277
  /**
593
- * Generate URL-safe slug from title
278
+ * Generate URL-safe slug from title.
594
279
  */
595
280
  function generateSlug(title) {
596
281
  return title