@kynetic-ai/spec 0.9.1 → 0.11.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 (310) hide show
  1. package/README.md +2 -1
  2. package/dist/acp/client.d.ts +6 -1
  3. package/dist/acp/client.d.ts.map +1 -1
  4. package/dist/acp/client.js +7 -2
  5. package/dist/acp/client.js.map +1 -1
  6. package/dist/acp/framing.d.ts +12 -1
  7. package/dist/acp/framing.d.ts.map +1 -1
  8. package/dist/acp/framing.js +27 -4
  9. package/dist/acp/framing.js.map +1 -1
  10. package/dist/agent-runtime/dispatch.d.ts +292 -0
  11. package/dist/agent-runtime/dispatch.d.ts.map +1 -0
  12. package/dist/agent-runtime/dispatch.js +860 -0
  13. package/dist/agent-runtime/dispatch.js.map +1 -0
  14. package/dist/agent-runtime/index.d.ts +11 -0
  15. package/dist/agent-runtime/index.d.ts.map +1 -0
  16. package/dist/agent-runtime/index.js +11 -0
  17. package/dist/agent-runtime/index.js.map +1 -0
  18. package/dist/agent-runtime/invocation.d.ts +86 -0
  19. package/dist/agent-runtime/invocation.d.ts.map +1 -0
  20. package/dist/agent-runtime/invocation.js +442 -0
  21. package/dist/agent-runtime/invocation.js.map +1 -0
  22. package/dist/agent-runtime/prompts.d.ts +50 -0
  23. package/dist/agent-runtime/prompts.d.ts.map +1 -0
  24. package/dist/agent-runtime/prompts.js +108 -0
  25. package/dist/agent-runtime/prompts.js.map +1 -0
  26. package/dist/agents/spawner.d.ts.map +1 -1
  27. package/dist/agents/spawner.js +60 -4
  28. package/dist/agents/spawner.js.map +1 -1
  29. package/dist/cli/batch-exec.d.ts.map +1 -1
  30. package/dist/cli/batch-exec.js +140 -62
  31. package/dist/cli/batch-exec.js.map +1 -1
  32. package/dist/cli/batch-write-buffer.d.ts +141 -0
  33. package/dist/cli/batch-write-buffer.d.ts.map +1 -0
  34. package/dist/cli/batch-write-buffer.js +400 -0
  35. package/dist/cli/batch-write-buffer.js.map +1 -0
  36. package/dist/cli/commands/agent.d.ts +20 -0
  37. package/dist/cli/commands/agent.d.ts.map +1 -0
  38. package/dist/cli/commands/agent.js +831 -0
  39. package/dist/cli/commands/agent.js.map +1 -0
  40. package/dist/cli/commands/inbox.d.ts.map +1 -1
  41. package/dist/cli/commands/inbox.js +46 -22
  42. package/dist/cli/commands/inbox.js.map +1 -1
  43. package/dist/cli/commands/index.d.ts +1 -0
  44. package/dist/cli/commands/index.d.ts.map +1 -1
  45. package/dist/cli/commands/index.js +1 -0
  46. package/dist/cli/commands/index.js.map +1 -1
  47. package/dist/cli/commands/item.d.ts.map +1 -1
  48. package/dist/cli/commands/item.js +22 -16
  49. package/dist/cli/commands/item.js.map +1 -1
  50. package/dist/cli/commands/log.js +1 -1
  51. package/dist/cli/commands/log.js.map +1 -1
  52. package/dist/cli/commands/meta.d.ts.map +1 -1
  53. package/dist/cli/commands/meta.js +168 -6
  54. package/dist/cli/commands/meta.js.map +1 -1
  55. package/dist/cli/commands/module.d.ts.map +1 -1
  56. package/dist/cli/commands/module.js +2 -1
  57. package/dist/cli/commands/module.js.map +1 -1
  58. package/dist/cli/commands/plan-import.js +19 -3
  59. package/dist/cli/commands/plan-import.js.map +1 -1
  60. package/dist/cli/commands/plan.d.ts.map +1 -1
  61. package/dist/cli/commands/plan.js +87 -43
  62. package/dist/cli/commands/plan.js.map +1 -1
  63. package/dist/cli/commands/ralph.d.ts +5 -56
  64. package/dist/cli/commands/ralph.d.ts.map +1 -1
  65. package/dist/cli/commands/ralph.js +52 -1502
  66. package/dist/cli/commands/ralph.js.map +1 -1
  67. package/dist/cli/commands/search.d.ts.map +1 -1
  68. package/dist/cli/commands/search.js +22 -13
  69. package/dist/cli/commands/search.js.map +1 -1
  70. package/dist/cli/commands/serve.d.ts.map +1 -1
  71. package/dist/cli/commands/serve.js +70 -11
  72. package/dist/cli/commands/serve.js.map +1 -1
  73. package/dist/cli/commands/session/checkpoint.d.ts.map +1 -1
  74. package/dist/cli/commands/session/checkpoint.js +7 -2
  75. package/dist/cli/commands/session/checkpoint.js.map +1 -1
  76. package/dist/cli/commands/session/commands.d.ts.map +1 -1
  77. package/dist/cli/commands/session/commands.js +15 -0
  78. package/dist/cli/commands/session/commands.js.map +1 -1
  79. package/dist/cli/commands/session/context.d.ts.map +1 -1
  80. package/dist/cli/commands/session/context.js +10 -5
  81. package/dist/cli/commands/session/context.js.map +1 -1
  82. package/dist/cli/commands/session/log.d.ts +1 -0
  83. package/dist/cli/commands/session/log.d.ts.map +1 -1
  84. package/dist/cli/commands/session/log.js +124 -8
  85. package/dist/cli/commands/session/log.js.map +1 -1
  86. package/dist/cli/commands/session/stale-close.d.ts +17 -0
  87. package/dist/cli/commands/session/stale-close.d.ts.map +1 -0
  88. package/dist/cli/commands/session/stale-close.js +378 -0
  89. package/dist/cli/commands/session/stale-close.js.map +1 -0
  90. package/dist/cli/commands/setup.d.ts.map +1 -1
  91. package/dist/cli/commands/setup.js +95 -0
  92. package/dist/cli/commands/setup.js.map +1 -1
  93. package/dist/cli/commands/skill-crud.d.ts.map +1 -1
  94. package/dist/cli/commands/skill-crud.js +4 -3
  95. package/dist/cli/commands/skill-crud.js.map +1 -1
  96. package/dist/cli/commands/skill-diff.d.ts.map +1 -1
  97. package/dist/cli/commands/skill-diff.js +15 -0
  98. package/dist/cli/commands/skill-diff.js.map +1 -1
  99. package/dist/cli/commands/skill-install.d.ts.map +1 -1
  100. package/dist/cli/commands/skill-install.js +50 -18
  101. package/dist/cli/commands/skill-install.js.map +1 -1
  102. package/dist/cli/commands/task.d.ts.map +1 -1
  103. package/dist/cli/commands/task.js +536 -310
  104. package/dist/cli/commands/task.js.map +1 -1
  105. package/dist/cli/commands/tasks.js +1 -1
  106. package/dist/cli/commands/tasks.js.map +1 -1
  107. package/dist/cli/commands/triage.d.ts.map +1 -1
  108. package/dist/cli/commands/triage.js +37 -13
  109. package/dist/cli/commands/triage.js.map +1 -1
  110. package/dist/cli/commands/validate.d.ts.map +1 -1
  111. package/dist/cli/commands/validate.js +65 -25
  112. package/dist/cli/commands/validate.js.map +1 -1
  113. package/dist/cli/help/content.d.ts.map +1 -1
  114. package/dist/cli/help/content.js +5 -0
  115. package/dist/cli/help/content.js.map +1 -1
  116. package/dist/cli/index.d.ts.map +1 -1
  117. package/dist/cli/index.js +2 -1
  118. package/dist/cli/index.js.map +1 -1
  119. package/dist/cli/output.d.ts.map +1 -1
  120. package/dist/cli/output.js +5 -1
  121. package/dist/cli/output.js.map +1 -1
  122. package/dist/daemon/project-context.ts +22 -0
  123. package/dist/daemon/routes/agent-dispatch.ts +279 -0
  124. package/dist/daemon/routes/items.ts +22 -0
  125. package/dist/daemon/routes/meta.ts +141 -1
  126. package/dist/daemon/routes/plans.ts +147 -0
  127. package/dist/daemon/routes/sessions.ts +180 -0
  128. package/dist/daemon/routes/tasks.ts +198 -0
  129. package/dist/daemon/routes/validation.ts +1 -1
  130. package/dist/daemon/server.ts +77 -21
  131. package/dist/daemon/websocket/handler.ts +67 -6
  132. package/dist/daemon/websocket/lifecycle.ts +19 -0
  133. package/dist/daemon/websocket/pubsub.ts +74 -3
  134. package/dist/export/html.d.ts.map +1 -1
  135. package/dist/export/html.js +5 -2
  136. package/dist/export/html.js.map +1 -1
  137. package/dist/export/triage.d.ts +1 -1
  138. package/dist/export/triage.d.ts.map +1 -1
  139. package/dist/export/triage.js +5 -3
  140. package/dist/export/triage.js.map +1 -1
  141. package/dist/parser/alignment.d.ts.map +1 -1
  142. package/dist/parser/alignment.js +10 -5
  143. package/dist/parser/alignment.js.map +1 -1
  144. package/dist/parser/assess.js +1 -1
  145. package/dist/parser/assess.js.map +1 -1
  146. package/dist/parser/config.d.ts +6 -6
  147. package/dist/parser/meta.d.ts.map +1 -1
  148. package/dist/parser/meta.js +9 -8
  149. package/dist/parser/meta.js.map +1 -1
  150. package/dist/parser/plan-document.d.ts +12 -12
  151. package/dist/parser/plans.d.ts +7 -0
  152. package/dist/parser/plans.d.ts.map +1 -1
  153. package/dist/parser/plans.js +100 -15
  154. package/dist/parser/plans.js.map +1 -1
  155. package/dist/parser/refs.d.ts +5 -0
  156. package/dist/parser/refs.d.ts.map +1 -1
  157. package/dist/parser/refs.js +17 -12
  158. package/dist/parser/refs.js.map +1 -1
  159. package/dist/parser/shadow.d.ts +1 -1
  160. package/dist/parser/shadow.d.ts.map +1 -1
  161. package/dist/parser/shadow.js +71 -4
  162. package/dist/parser/shadow.js.map +1 -1
  163. package/dist/parser/skill-render.d.ts.map +1 -1
  164. package/dist/parser/skill-render.js +6 -3
  165. package/dist/parser/skill-render.js.map +1 -1
  166. package/dist/parser/validate.d.ts.map +1 -1
  167. package/dist/parser/validate.js +35 -76
  168. package/dist/parser/validate.js.map +1 -1
  169. package/dist/parser/yaml.d.ts +24 -5
  170. package/dist/parser/yaml.d.ts.map +1 -1
  171. package/dist/parser/yaml.js +224 -64
  172. package/dist/parser/yaml.js.map +1 -1
  173. package/dist/schema/meta.d.ts +457 -119
  174. package/dist/schema/meta.d.ts.map +1 -1
  175. package/dist/schema/meta.js +56 -0
  176. package/dist/schema/meta.js.map +1 -1
  177. package/dist/schema/plan.d.ts +22 -22
  178. package/dist/schema/spec.d.ts +39 -39
  179. package/dist/schema/task.d.ts +43 -32
  180. package/dist/schema/task.d.ts.map +1 -1
  181. package/dist/schema/task.js +5 -0
  182. package/dist/schema/task.js.map +1 -1
  183. package/dist/sessions/store.d.ts +126 -0
  184. package/dist/sessions/store.d.ts.map +1 -1
  185. package/dist/sessions/store.js +440 -22
  186. package/dist/sessions/store.js.map +1 -1
  187. package/dist/sessions/types.d.ts +75 -17
  188. package/dist/sessions/types.d.ts.map +1 -1
  189. package/dist/sessions/types.js +51 -1
  190. package/dist/sessions/types.js.map +1 -1
  191. package/dist/triage/actions.d.ts +1 -0
  192. package/dist/triage/actions.d.ts.map +1 -1
  193. package/dist/triage/actions.js +34 -7
  194. package/dist/triage/actions.js.map +1 -1
  195. package/dist/utils/commit.js +1 -1
  196. package/dist/utils/commit.js.map +1 -1
  197. package/dist/web-ui/_app/env.js +1 -0
  198. package/dist/web-ui/_app/immutable/assets/0.BJaYkGW2.css +1 -0
  199. package/dist/web-ui/_app/immutable/assets/9.SzGLxi4x.css +1 -0
  200. package/dist/web-ui/_app/immutable/assets/select-trigger.CV-KWLNP.css +1 -0
  201. package/dist/web-ui/_app/immutable/chunks/-lc0BifF.js +1 -0
  202. package/dist/web-ui/_app/immutable/chunks/62JVKtnb.js +1 -0
  203. package/dist/web-ui/_app/immutable/chunks/8RBjHMN1.js +1 -0
  204. package/dist/web-ui/_app/immutable/chunks/B5LJFxqa.js +1 -0
  205. package/dist/web-ui/_app/immutable/chunks/B5wTVqxm.js +1 -0
  206. package/dist/web-ui/_app/immutable/chunks/B6VSmczZ.js +1 -0
  207. package/dist/web-ui/_app/immutable/chunks/B8a0xDxR.js +1 -0
  208. package/dist/web-ui/_app/immutable/chunks/BEOQc37C.js +1 -0
  209. package/dist/web-ui/_app/immutable/chunks/BHtYorjv.js +1 -0
  210. package/dist/web-ui/_app/immutable/chunks/BJ0JX3ea.js +1 -0
  211. package/dist/web-ui/_app/immutable/chunks/BMuCqDX8.js +1 -0
  212. package/dist/web-ui/_app/immutable/chunks/BP352uRn.js +1 -0
  213. package/dist/web-ui/_app/immutable/chunks/BUZujXJ2.js +1 -0
  214. package/dist/web-ui/_app/immutable/chunks/BVA9Exy-.js +1 -0
  215. package/dist/web-ui/_app/immutable/chunks/BWET-efb.js +1 -0
  216. package/dist/web-ui/_app/immutable/chunks/BXkNecpt.js +1 -0
  217. package/dist/web-ui/_app/immutable/chunks/BYzrIfX8.js +1 -0
  218. package/dist/web-ui/_app/immutable/chunks/BkOJ8DkV.js +1 -0
  219. package/dist/web-ui/_app/immutable/chunks/BpuwufMc.js +1 -0
  220. package/dist/web-ui/_app/immutable/chunks/BwMO4RrG.js +1 -0
  221. package/dist/web-ui/_app/immutable/chunks/BysXJlZb.js +1 -0
  222. package/dist/web-ui/_app/immutable/chunks/C076q4JN.js +1 -0
  223. package/dist/web-ui/_app/immutable/chunks/C33JaVbg.js +1 -0
  224. package/dist/web-ui/_app/immutable/chunks/CGtqifKp.js +1 -0
  225. package/dist/web-ui/_app/immutable/chunks/CHDZZ7OG.js +1 -0
  226. package/dist/web-ui/_app/immutable/chunks/CPPfDSei.js +5 -0
  227. package/dist/web-ui/_app/immutable/chunks/CUir3f4J.js +60 -0
  228. package/dist/web-ui/_app/immutable/chunks/Cncwi6fQ.js +1 -0
  229. package/dist/web-ui/_app/immutable/chunks/CrCIbn0C.js +1 -0
  230. package/dist/web-ui/_app/immutable/chunks/CwELQvbx.js +1 -0
  231. package/dist/web-ui/_app/immutable/chunks/D3vxvonu.js +1 -0
  232. package/dist/web-ui/_app/immutable/chunks/D6TVmR9T.js +1 -0
  233. package/dist/web-ui/_app/immutable/chunks/D7LTux4W.js +1 -0
  234. package/dist/web-ui/_app/immutable/chunks/D82RulSH.js +1 -0
  235. package/dist/web-ui/_app/immutable/chunks/D9QNBZM2.js +2 -0
  236. package/dist/web-ui/_app/immutable/chunks/DAMmvwn4.js +1 -0
  237. package/dist/web-ui/_app/immutable/chunks/DAh4Wfku.js +1 -0
  238. package/dist/web-ui/_app/immutable/chunks/DAx07bEQ.js +1 -0
  239. package/dist/web-ui/_app/immutable/chunks/DBYE9jOd.js +1 -0
  240. package/dist/web-ui/_app/immutable/chunks/DOno4cA2.js +1 -0
  241. package/dist/web-ui/_app/immutable/chunks/DQA8NZIH.js +2 -0
  242. package/dist/web-ui/_app/immutable/chunks/DRfPm2bo.js +1 -0
  243. package/dist/web-ui/_app/immutable/chunks/DhQhksaB.js +1 -0
  244. package/dist/web-ui/_app/immutable/chunks/DjG7s6hm.js +1 -0
  245. package/dist/web-ui/_app/immutable/chunks/DjcCz-PU.js +2 -0
  246. package/dist/web-ui/_app/immutable/chunks/DkltRNvh.js +1 -0
  247. package/dist/web-ui/_app/immutable/chunks/DlaTnPKL.js +1 -0
  248. package/dist/web-ui/_app/immutable/chunks/DvA-KON-.js +1 -0
  249. package/dist/web-ui/_app/immutable/chunks/DxCk-KHc.js +1 -0
  250. package/dist/web-ui/_app/immutable/chunks/DzO4hlg9.js +1 -0
  251. package/dist/web-ui/_app/immutable/chunks/Eo4gF7ih.js +1 -0
  252. package/dist/web-ui/_app/immutable/chunks/ExCq5swK.js +1 -0
  253. package/dist/web-ui/_app/immutable/chunks/T3zZGv51.js +1 -0
  254. package/dist/web-ui/_app/immutable/chunks/XZumBYeP.js +1 -0
  255. package/dist/web-ui/_app/immutable/chunks/_ySfNjkF.js +1 -0
  256. package/dist/web-ui/_app/immutable/chunks/iEtR5cV6.js +1 -0
  257. package/dist/web-ui/_app/immutable/chunks/k_Qegko0.js +1 -0
  258. package/dist/web-ui/_app/immutable/chunks/pE6cYWlS.js +1 -0
  259. package/dist/web-ui/_app/immutable/entry/app.Cgu6uKeS.js +2 -0
  260. package/dist/web-ui/_app/immutable/entry/start.9XifnLoB.js +1 -0
  261. package/dist/web-ui/_app/immutable/nodes/0.DISwcKSK.js +1 -0
  262. package/dist/web-ui/_app/immutable/nodes/1.Cx2Ufqp1.js +1 -0
  263. package/dist/web-ui/_app/immutable/nodes/10.C3z8ijXL.js +1 -0
  264. package/dist/web-ui/_app/immutable/nodes/11.DZdIjZmM.js +1 -0
  265. package/dist/web-ui/_app/immutable/nodes/12.FsIGfAOa.js +1 -0
  266. package/dist/web-ui/_app/immutable/nodes/13.DZoFwagf.js +1 -0
  267. package/dist/web-ui/_app/immutable/nodes/14.DaIzDKbQ.js +1 -0
  268. package/dist/web-ui/_app/immutable/nodes/15.BYyt4XWF.js +2 -0
  269. package/dist/web-ui/_app/immutable/nodes/16.CQkSqpOe.js +1 -0
  270. package/dist/web-ui/_app/immutable/nodes/2.Bkf_j2UJ.js +1 -0
  271. package/dist/web-ui/_app/immutable/nodes/3.kaMCurJG.js +1 -0
  272. package/dist/web-ui/_app/immutable/nodes/4.BSsFPTHG.js +2 -0
  273. package/dist/web-ui/_app/immutable/nodes/5.CpPlcCEZ.js +1 -0
  274. package/dist/web-ui/_app/immutable/nodes/6.BN4FqQmY.js +1 -0
  275. package/dist/web-ui/_app/immutable/nodes/7.9kBYIZik.js +1 -0
  276. package/dist/web-ui/_app/immutable/nodes/8.BuijtZ6B.js +1 -0
  277. package/dist/web-ui/_app/immutable/nodes/9.C-Weba8R.js +1 -0
  278. package/dist/web-ui/_app/version.json +1 -0
  279. package/dist/web-ui/index.html +39 -0
  280. package/dist/web-ui/robots.txt +3 -0
  281. package/package.json +4 -2
  282. package/plugin/.claude-plugin/marketplace.json +1 -1
  283. package/plugin/.claude-plugin/plugin.json +1 -1
  284. package/plugin/plugins/kspec/skills/task-work/SKILL.md +25 -2
  285. package/templates/agents-sections/06-ralph-loop.md +64 -11
  286. package/templates/skills/task-work/SKILL.md +25 -2
  287. package/dist/ralph/cli-renderer.d.ts +0 -27
  288. package/dist/ralph/cli-renderer.d.ts.map +0 -1
  289. package/dist/ralph/cli-renderer.js +0 -250
  290. package/dist/ralph/cli-renderer.js.map +0 -1
  291. package/dist/ralph/events.d.ts +0 -65
  292. package/dist/ralph/events.d.ts.map +0 -1
  293. package/dist/ralph/events.js +0 -600
  294. package/dist/ralph/events.js.map +0 -1
  295. package/dist/ralph/index.d.ts +0 -11
  296. package/dist/ralph/index.d.ts.map +0 -1
  297. package/dist/ralph/index.js +0 -16
  298. package/dist/ralph/index.js.map +0 -1
  299. package/dist/ralph/loop-errors.d.ts +0 -83
  300. package/dist/ralph/loop-errors.d.ts.map +0 -1
  301. package/dist/ralph/loop-errors.js +0 -150
  302. package/dist/ralph/loop-errors.js.map +0 -1
  303. package/dist/ralph/subagent.d.ts +0 -127
  304. package/dist/ralph/subagent.d.ts.map +0 -1
  305. package/dist/ralph/subagent.js +0 -268
  306. package/dist/ralph/subagent.js.map +0 -1
  307. package/dist/ralph/wrap-up.d.ts +0 -127
  308. package/dist/ralph/wrap-up.d.ts.map +0 -1
  309. package/dist/ralph/wrap-up.js +0 -271
  310. package/dist/ralph/wrap-up.js.map +0 -1
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Agent Prompt Building
3
+ *
4
+ * Resolves skills from the skill registry and builds structured prompts
5
+ * for agent invocations. Extracted from ralph.ts to be reusable by both
6
+ * the dispatch engine and CLI one-shot mode.
7
+ *
8
+ * AC: @agent-invocation-lifecycle ac-7
9
+ */
10
+ /**
11
+ * A resolved skill with its content loaded.
12
+ */
13
+ export interface ResolvedSkill {
14
+ /** Skill ID */
15
+ id: string;
16
+ /** Full markdown content of the skill */
17
+ content: string;
18
+ }
19
+ /**
20
+ * Options for building an agent prompt.
21
+ */
22
+ export interface BuildPromptOptions {
23
+ /** Base prompt text */
24
+ basePrompt: string;
25
+ /** Skill IDs to resolve and include */
26
+ skillIds: string[];
27
+ /** The .kspec directory for resolving skill content */
28
+ specDir: string;
29
+ /** Adapter identifier used to format cross-skill references */
30
+ adapterId?: string;
31
+ }
32
+ /**
33
+ * Resolve skill content from the skill registry.
34
+ *
35
+ * Skills are stored in .kspec/skills/<id>/SKILL.md.
36
+ * Missing skills are silently skipped.
37
+ *
38
+ * AC: @agent-invocation-lifecycle ac-7
39
+ */
40
+ export declare function resolveSkills(skillIds: string[], specDir: string): Promise<ResolvedSkill[]>;
41
+ /**
42
+ * Build a prompt with resolved skill content appended.
43
+ *
44
+ * When the agent definition specifies skills, their content is resolved
45
+ * from the skill registry and included in the prompt.
46
+ *
47
+ * AC: @agent-invocation-lifecycle ac-7
48
+ */
49
+ export declare function buildPromptWithSkills(options: BuildPromptOptions): Promise<string>;
50
+ //# sourceMappingURL=prompts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/agent-runtime/prompts.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AASH;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,eAAe;IACf,EAAE,EAAE,MAAM,CAAC;IACX,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,uBAAuB;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,uDAAuD;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA6DD;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAAE,EAClB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,aAAa,EAAE,CAAC,CAc1B;AAED;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CA2BxF"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Agent Prompt Building
3
+ *
4
+ * Resolves skills from the skill registry and builds structured prompts
5
+ * for agent invocations. Extracted from ralph.ts to be reusable by both
6
+ * the dispatch engine and CLI one-shot mode.
7
+ *
8
+ * AC: @agent-invocation-lifecycle ac-7
9
+ */
10
+ import * as path from "node:path";
11
+ import * as fs from "node:fs/promises";
12
+ import { resolveSkillReferenceTokensForPlatform } from "../parser/skill-render.js";
13
+ function getSkillReferencePlatform(adapterId) {
14
+ switch (adapterId) {
15
+ case "claude-agent-acp":
16
+ case "claude-code-acp":
17
+ return "claude-code";
18
+ case "codex-acp":
19
+ return "codex";
20
+ default:
21
+ return null;
22
+ }
23
+ }
24
+ function getProjectRootFromSpecDir(specDir) {
25
+ return path.basename(specDir) === ".kspec" ? path.dirname(specDir) : specDir;
26
+ }
27
+ async function loadKspecSkillIds(specDir) {
28
+ const projectRoot = getProjectRootFromSpecDir(specDir);
29
+ const skillsDir = path.join(projectRoot, ".agents", "skills");
30
+ try {
31
+ const entries = await fs.readdir(skillsDir, { withFileTypes: true });
32
+ const ids = new Set();
33
+ for (const entry of entries) {
34
+ if (!entry.isDirectory()) {
35
+ continue;
36
+ }
37
+ if (!entry.name.startsWith("kspec-")) {
38
+ continue;
39
+ }
40
+ ids.add(entry.name.slice("kspec-".length));
41
+ }
42
+ return ids;
43
+ }
44
+ catch {
45
+ return new Set();
46
+ }
47
+ }
48
+ async function rewriteSkillReferences(text, specDir, adapterId) {
49
+ const platform = getSkillReferencePlatform(adapterId);
50
+ if (!platform) {
51
+ return text;
52
+ }
53
+ const kspecSkillIds = await loadKspecSkillIds(specDir);
54
+ const skillOrigins = new Map();
55
+ for (const skillId of kspecSkillIds) {
56
+ skillOrigins.set(skillId, "core");
57
+ }
58
+ return resolveSkillReferenceTokensForPlatform(text, platform, skillOrigins);
59
+ }
60
+ // ─── Skill Resolution ─────────────────────────────────────────────────────────
61
+ /**
62
+ * Resolve skill content from the skill registry.
63
+ *
64
+ * Skills are stored in .kspec/skills/<id>/SKILL.md.
65
+ * Missing skills are silently skipped.
66
+ *
67
+ * AC: @agent-invocation-lifecycle ac-7
68
+ */
69
+ export async function resolveSkills(skillIds, specDir) {
70
+ const resolved = [];
71
+ for (const skillId of skillIds) {
72
+ const contentPath = path.join(specDir, "skills", skillId, "SKILL.md");
73
+ try {
74
+ const content = await fs.readFile(contentPath, "utf-8");
75
+ resolved.push({ id: skillId, content });
76
+ }
77
+ catch {
78
+ // Skill not found — skip silently
79
+ }
80
+ }
81
+ return resolved;
82
+ }
83
+ /**
84
+ * Build a prompt with resolved skill content appended.
85
+ *
86
+ * When the agent definition specifies skills, their content is resolved
87
+ * from the skill registry and included in the prompt.
88
+ *
89
+ * AC: @agent-invocation-lifecycle ac-7
90
+ */
91
+ export async function buildPromptWithSkills(options) {
92
+ const { basePrompt, skillIds, specDir, adapterId, } = options;
93
+ if (skillIds.length === 0) {
94
+ return basePrompt;
95
+ }
96
+ const resolvedSkills = await resolveSkills(skillIds, specDir);
97
+ if (resolvedSkills.length === 0) {
98
+ return basePrompt;
99
+ }
100
+ const skillSections = resolvedSkills
101
+ .map((skill) => `<!-- Skill: ${skill.id} -->\n${skill.content}`)
102
+ .join("\n\n");
103
+ const promptWithSkills = `${basePrompt}\n\n## Skills\n\n${skillSections}`;
104
+ // AC: @agent-invocation-lifecycle ac-10
105
+ // Rewrite portable {skill:<id>} references to adapter-specific invocation syntax.
106
+ return rewriteSkillReferences(promptWithSkills, specDir, adapterId);
107
+ }
108
+ //# sourceMappingURL=prompts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../src/agent-runtime/prompts.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"spawner.d.ts","sourceRoot":"","sources":["../../src/agents/spawner.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,KAAK,YAAY,EAAS,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,sCAAsC;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,uCAAuC;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,2DAA2D;IAC3D,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,yBAAyB;IACzB,aAAa,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,OAAO,GAAG,QAAQ,CAAC,CAAC;CAC5D;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,uCAAuC;IACvC,MAAM,EAAE,SAAS,CAAC;IAClB,+BAA+B;IAC/B,OAAO,EAAE,YAAY,CAAC;IACtB,6BAA6B;IAC7B,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,OAAO,KAAK,IAAI,CAAC;CACzC;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,iBAAiB,GACzB,YAAY,CAoDd;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,YAAY,CAAC,CAWvB"}
1
+ {"version":3,"file":"spawner.d.ts","sourceRoot":"","sources":["../../src/agents/spawner.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,KAAK,YAAY,EAAS,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,sCAAsC;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,uCAAuC;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,2DAA2D;IAC3D,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,yBAAyB;IACzB,aAAa,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,OAAO,GAAG,QAAQ,CAAC,CAAC;CAC5D;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,uCAAuC;IACvC,MAAM,EAAE,SAAS,CAAC;IAClB,+BAA+B;IAC/B,OAAO,EAAE,YAAY,CAAC;IACtB,6BAA6B;IAC7B,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,OAAO,KAAK,IAAI,CAAC;CACzC;AAmDD;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,iBAAiB,GACzB,YAAY,CA+Dd;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,YAAY,CAAC,CAWvB"}
@@ -6,6 +6,50 @@
6
6
  */
7
7
  import { spawn } from "node:child_process";
8
8
  import { ACPClient } from "../acp/index.js";
9
+ const UNEXPECTED_CASE_PREFIX = "Unexpected case:";
10
+ const RATE_LIMIT_EVENT_TYPE = "rate_limit_event";
11
+ function isNonActionableAdapterStderrLine(line) {
12
+ const trimmed = line.trim();
13
+ if (!trimmed.startsWith(UNEXPECTED_CASE_PREFIX))
14
+ return false;
15
+ const payload = trimmed.slice(UNEXPECTED_CASE_PREFIX.length).trim();
16
+ if (!payload)
17
+ return false;
18
+ try {
19
+ const parsed = JSON.parse(payload);
20
+ return parsed.type === RATE_LIMIT_EVENT_TYPE;
21
+ }
22
+ catch {
23
+ // Keep a narrow fallback pattern in case adapter logs malformed JSON.
24
+ return /"type"\s*:\s*"rate_limit_event"/.test(payload);
25
+ }
26
+ }
27
+ function forwardFilteredAdapterStderr(child) {
28
+ if (!child.stderr)
29
+ return;
30
+ child.stderr.setEncoding("utf-8");
31
+ let pending = "";
32
+ const forward = (line, withNewline) => {
33
+ if (isNonActionableAdapterStderrLine(line))
34
+ return;
35
+ process.stderr.write(withNewline ? `${line}\n` : line);
36
+ };
37
+ child.stderr.on("data", (chunk) => {
38
+ pending += chunk.toString();
39
+ let newlineIndex = pending.indexOf("\n");
40
+ while (newlineIndex !== -1) {
41
+ const line = pending.slice(0, newlineIndex);
42
+ pending = pending.slice(newlineIndex + 1);
43
+ forward(line, true);
44
+ newlineIndex = pending.indexOf("\n");
45
+ }
46
+ });
47
+ child.stderr.on("end", () => {
48
+ if (pending.length > 0) {
49
+ forward(pending, false);
50
+ }
51
+ });
52
+ }
9
53
  /**
10
54
  * Spawn an ACP agent using the specified adapter.
11
55
  *
@@ -31,8 +75,10 @@ export function spawnAgent(adapter, options) {
31
75
  cwd,
32
76
  env: processEnv,
33
77
  shell: adapter.shell,
34
- stdio: ["pipe", "pipe", "inherit"], // pipe stdin/stdout, inherit stderr
78
+ stdio: ["pipe", "pipe", "pipe"], // pipe all stdio so stderr can be filtered
35
79
  });
80
+ // Keep actionable adapter stderr visible while dropping known non-actionable noise.
81
+ forwardFilteredAdapterStderr(child);
36
82
  // Ensure stdin/stdout are available
37
83
  if (!child.stdin || !child.stdout) {
38
84
  child.kill();
@@ -47,10 +93,20 @@ export function spawnAgent(adapter, options) {
47
93
  stdin: child.stdout, // We read from child's stdout
48
94
  stdout: child.stdin, // We write to child's stdin
49
95
  });
50
- // Forward process exit to client close
51
- child.on("exit", () => {
96
+ // Forward process exit to client close, surfacing exit code/signal
97
+ child.on("exit", (code, signal) => {
52
98
  if (!client.isClosed()) {
53
- client.close();
99
+ let reason;
100
+ if (signal !== null) {
101
+ reason = `Subagent process exited with signal ${signal}`;
102
+ }
103
+ else if (code !== null) {
104
+ reason = `Subagent process exited with code ${code}`;
105
+ }
106
+ else {
107
+ reason = "Subagent process exited unexpectedly";
108
+ }
109
+ client.close(reason);
54
110
  }
55
111
  });
56
112
  // Kill function with graceful shutdown
@@ -1 +1 @@
1
- {"version":3,"file":"spawner.js","sourceRoot":"","sources":["../../src/agents/spawner.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAqB,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAyB,MAAM,iBAAiB,CAAC;AA6BnE;;;;;;;;;GASG;AACH,MAAM,UAAU,UAAU,CACxB,OAAqB,EACrB,OAA0B;IAE1B,MAAM,EAAE,GAAG,EAAE,GAAG,GAAG,EAAE,EAAE,SAAS,EAAE,aAAa,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC;IAEjE,8BAA8B;IAC9B,MAAM,UAAU,GAAG;QACjB,GAAG,OAAO,CAAC,GAAG;QACd,GAAG,OAAO,CAAC,GAAG;QACd,GAAG,GAAG;KACP,CAAC;IAEF,2DAA2D;IAC3D,MAAM,IAAI,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,CAAC;IAErD,0BAA0B;IAC1B,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE;QACzC,GAAG;QACH,GAAG,EAAE,UAAU;QACf,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,oCAAoC;KACzE,CAAC,CAAC;IAEH,oCAAoC;IACpC,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;QAClC,KAAK,CAAC,IAAI,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAC9D,CAAC;IAED,+CAA+C;IAC/C,uCAAuC;IACvC,iDAAiD;IACjD,gDAAgD;IAChD,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,GAAG,aAAa;QAChB,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,8BAA8B;QACnD,MAAM,EAAE,KAAK,CAAC,KAA8B,EAAE,4BAA4B;KAC3E,CAAC,CAAC;IAEH,uCAAuC;IACvC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;QACpB,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;YACvB,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,uCAAuC;IACvC,MAAM,IAAI,GAAG,CAAC,SAAyB,SAAS,EAAQ,EAAE;QACxD,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YAClB,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrB,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AAC1C,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,OAAqB,EACrB,OAA0B;IAE1B,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAE3C,IAAI,CAAC;QACH,MAAM,KAAK,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;QAChC,OAAO,KAAK,CAAC;IACf,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,qCAAqC;QACrC,KAAK,CAAC,IAAI,EAAE,CAAC;QACb,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"spawner.js","sourceRoot":"","sources":["../../src/agents/spawner.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"batch-exec.d.ts","sourceRoot":"","sources":["../../src/cli/batch-exec.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAkB,MAAM,WAAW,CAAC;AAEzD,OAAO,EAEL,KAAK,YAAY,EAGjB,KAAK,eAAe,EACpB,KAAK,UAAU,EAChB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAgBtD,MAAM,MAAM,gBAAgB,GACxB;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAIrC,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,MAAM,wBAAwB,GAChC,iBAAiB,GACjB,kBAAkB,GAClB,aAAa,GACb,kBAAkB,CAAC;AAEvB,MAAM,WAAW,oBAAoB;IACnC,+BAA+B;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,iCAAiC;IACjC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,oCAAoC;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,2BAA2B;IAC3B,IAAI,EAAE,wBAAwB,CAAC;IAC/B,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,qBAAqB;IACpC,6CAA6C;IAC7C,KAAK,EAAE,OAAO,CAAC;IACf,mDAAmD;IACnD,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,wBAAwB;IACxB,MAAM,EAAE,oBAAoB,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,oBAAoB;IACnC;;;;;OAKG;IACH,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,WAAW,KAAK,OAAO,CAAC;CAC/C;AAmCD;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC,UAAU,CAAC,CA4BrB;AA0FD;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,YAAY,EAAE,EACxB,OAAO,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE,oBAAoB,GAC7B,qBAAqB,CAkHvB;AAID;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,qBAAqB,EAC7B,IAAI,GAAE,OAAe,GACpB,MAAM,CA6BR;AAID,MAAM,WAAW,mBAAmB;IAClC,qCAAqC;IACrC,MAAM,EAAE,OAAO,CAAC;IAChB,iDAAiD;IACjD,eAAe,EAAE,OAAO,CAAC;IACzB,yBAAyB;IACzB,MAAM,EAAE,OAAO,CAAC;IAChB,kBAAkB;IAClB,IAAI,EAAE,OAAO,CAAC;CACf;AAsBD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,GAAG,MAAM,EAAE,CAuElF;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAOnD;AAYD;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAChC,QAAQ,EAAE,YAAY,EAAE,EACxB,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,eAAe,CAAC,CAqD1B"}
1
+ {"version":3,"file":"batch-exec.d.ts","sourceRoot":"","sources":["../../src/cli/batch-exec.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,OAAO,EAAkB,MAAM,WAAW,CAAC;AAEzD,OAAO,EAEL,KAAK,YAAY,EAGjB,KAAK,eAAe,EACpB,KAAK,UAAU,EAChB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAoBtD,MAAM,MAAM,gBAAgB,GACxB;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAMrC,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,MAAM,wBAAwB,GAChC,iBAAiB,GACjB,kBAAkB,GAClB,aAAa,GACb,kBAAkB,CAAC;AAEvB,MAAM,WAAW,oBAAoB;IACnC,+BAA+B;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,iCAAiC;IACjC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,oCAAoC;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,2BAA2B;IAC3B,IAAI,EAAE,wBAAwB,CAAC;IAC/B,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,qBAAqB;IACpC,6CAA6C;IAC7C,KAAK,EAAE,OAAO,CAAC;IACf,mDAAmD;IACnD,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,wBAAwB;IACxB,MAAM,EAAE,oBAAoB,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,oBAAoB;IACnC;;;;;OAKG;IACH,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,WAAW,KAAK,OAAO,CAAC;CAC/C;AA+FD;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC,UAAU,CAAC,CA4BrB;AAuGD;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,YAAY,EAAE,EACxB,OAAO,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE,oBAAoB,GAC7B,qBAAqB,CAkHvB;AAID;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,qBAAqB,EAC7B,IAAI,GAAE,OAAe,GACpB,MAAM,CA6BR;AAID,MAAM,WAAW,mBAAmB;IAClC,qCAAqC;IACrC,MAAM,EAAE,OAAO,CAAC;IAChB,iDAAiD;IACjD,eAAe,EAAE,OAAO,CAAC;IACzB,yBAAyB;IACzB,MAAM,EAAE,OAAO,CAAC;IAChB,kBAAkB;IAClB,IAAI,EAAE,OAAO,CAAC;CACf;AAmDD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,GAAG,MAAM,EAAE,CAoFlF;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAOnD;AAYD;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAChC,QAAQ,EAAE,YAAY,EAAE,EACxB,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,eAAe,CAAC,CAqD1B"}
@@ -6,8 +6,6 @@
6
6
  * mode, and produces helpful error messages.
7
7
  */
8
8
  import * as fs from "node:fs/promises";
9
- import * as path from "node:path";
10
- import * as os from "node:os";
11
9
  import chalk from "chalk";
12
10
  import { ZodError } from "zod";
13
11
  import { BatchInputSchema, } from "../schema/batch.js";
@@ -18,6 +16,8 @@ import { findClosestCommand } from "./suggest.js";
18
16
  import { createBatchCommandFilter } from "./command-annotations.js";
19
17
  import { setJsonMode, setVerboseMode } from "./output.js";
20
18
  import { BatchExitError, OutputCapture, installExitInterceptor, uninstallExitInterceptor, setBatchMode, } from "./batch-context.js";
19
+ import { activateBatchBuffer, deactivateBatchBuffer, } from "./batch-write-buffer.js";
20
+ const BATCH_STDIN_IDLE_TIMEOUT_MS = 1000;
21
21
  // ── Error Types ─────────────────────────────────────────────────────
22
22
  export class BatchParseError extends Error {
23
23
  constructor(message) {
@@ -26,6 +26,52 @@ export class BatchParseError extends Error {
26
26
  }
27
27
  }
28
28
  // ── Parsing ─────────────────────────────────────────────────────────
29
+ /**
30
+ * Read stdin with an idle timeout so non-interactive callers that keep
31
+ * stdin open without writing do not block forever.
32
+ */
33
+ async function readStdinWithIdleTimeout(timeoutMs) {
34
+ return new Promise((resolve) => {
35
+ let data = "";
36
+ let timeout;
37
+ const cleanup = () => {
38
+ if (timeout) {
39
+ clearTimeout(timeout);
40
+ }
41
+ process.stdin.removeListener("data", onData);
42
+ process.stdin.removeListener("end", onEnd);
43
+ process.stdin.removeListener("error", onError);
44
+ };
45
+ const armTimeout = () => {
46
+ if (timeout) {
47
+ clearTimeout(timeout);
48
+ }
49
+ timeout = setTimeout(() => {
50
+ cleanup();
51
+ resolve(data);
52
+ }, timeoutMs);
53
+ timeout.unref?.();
54
+ };
55
+ const onData = (chunk) => {
56
+ data += chunk;
57
+ armTimeout();
58
+ };
59
+ const onEnd = () => {
60
+ cleanup();
61
+ resolve(data);
62
+ };
63
+ const onError = () => {
64
+ cleanup();
65
+ resolve(data);
66
+ };
67
+ process.stdin.setEncoding("utf8");
68
+ process.stdin.on("data", onData);
69
+ process.stdin.on("end", onEnd);
70
+ process.stdin.on("error", onError);
71
+ process.stdin.resume();
72
+ armTimeout();
73
+ });
74
+ }
29
75
  /**
30
76
  * Read raw text from a batch input source.
31
77
  */
@@ -41,13 +87,17 @@ async function readSource(source) {
41
87
  throw new BatchParseError(`Failed to read batch file: ${err.message}`);
42
88
  }
43
89
  case "stdin": {
44
- const chunks = [];
45
- for await (const chunk of process.stdin) {
46
- chunks.push(chunk);
47
- }
48
- const text = Buffer.concat(chunks).toString("utf-8");
90
+ const text = process.stdin.isTTY
91
+ ? await (async () => {
92
+ const chunks = [];
93
+ for await (const chunk of process.stdin) {
94
+ chunks.push(chunk);
95
+ }
96
+ return Buffer.concat(chunks).toString("utf-8");
97
+ })()
98
+ : await readStdinWithIdleTimeout(BATCH_STDIN_IDLE_TIMEOUT_MS);
49
99
  if (!text.trim()) {
50
- throw new BatchParseError("No input received on stdin");
100
+ throw new BatchParseError(`No input received on stdin. Provide --commands <json>, --file <path>, or pipe JSON via stdin.`);
51
101
  }
52
102
  return text;
53
103
  }
@@ -116,6 +166,17 @@ function normalizeArgKey(key) {
116
166
  .replace(/([a-z0-9])([A-Z])/g, "$1-$2")
117
167
  .toLowerCase();
118
168
  }
169
+ /**
170
+ * Batch-only argument aliases for common plural forms.
171
+ * Keys and values are canonical kebab-case.
172
+ */
173
+ const BATCH_ARG_ALIASES = {
174
+ tags: "tag",
175
+ };
176
+ function resolveBatchArgAlias(key) {
177
+ const canonical = normalizeArgKey(key);
178
+ return BATCH_ARG_ALIASES[canonical] ?? canonical;
179
+ }
119
180
  /**
120
181
  * Get all known argument and option names for a command, returning both
121
182
  * kebab-case, camelCase, and underscore variants.
@@ -231,7 +292,7 @@ export function validateBatchCommands(commands, program, options) {
231
292
  // Collect all known names as flat array for suggestion matching
232
293
  const knownNamesArray = Array.from(knownNames);
233
294
  for (const argKey of Object.keys(cmd.args)) {
234
- if (!knownCanonicalNames.has(normalizeArgKey(argKey))) {
295
+ if (!knownCanonicalNames.has(resolveBatchArgAlias(argKey))) {
235
296
  const suggestion = findClosestCommand(argKey, knownNamesArray);
236
297
  errors.push({
237
298
  index: i,
@@ -246,7 +307,7 @@ export function validateBatchCommands(commands, program, options) {
246
307
  // 5. Check for missing required args
247
308
  for (const reqName of requiredNames) {
248
309
  const normalizedReqName = normalizeArgKey(reqName);
249
- if (!Object.keys(cmd.args).some((k) => normalizeArgKey(k) === normalizedReqName)) {
310
+ if (!Object.keys(cmd.args).some((k) => resolveBatchArgAlias(k) === normalizedReqName)) {
250
311
  errors.push({
251
312
  index: i,
252
313
  id: cmd.id,
@@ -314,6 +375,30 @@ function toArgString(value) {
314
375
  }
315
376
  return String(value);
316
377
  }
378
+ function normalizePriorityAlias(value) {
379
+ if (Array.isArray(value)) {
380
+ return value.map((v) => normalizePriorityAlias(v));
381
+ }
382
+ if (typeof value !== "string") {
383
+ return value;
384
+ }
385
+ const match = value.match(/^[Pp]([1-5])$/);
386
+ return match ? match[1] : value;
387
+ }
388
+ function isNumericPriorityOption(option) {
389
+ if (!option || normalizeArgKey(option.name) !== "priority") {
390
+ return false;
391
+ }
392
+ const hasNumericPlaceholder = /<n(?:umber)?>/.test(option.flags);
393
+ const hasNumericRangeHint = /\b1-5\b/.test(option.description ?? "");
394
+ return hasNumericPlaceholder || hasNumericRangeHint;
395
+ }
396
+ function isNumericPriorityArgument(arg) {
397
+ if (normalizeArgKey(arg.name) !== "priority") {
398
+ return false;
399
+ }
400
+ return /\b1-5\b/.test(arg.description ?? "");
401
+ }
317
402
  export function buildCommandArgv(cmd, cmdMeta) {
318
403
  const argv = [...cmd.command.trim().split(/\s+/)];
319
404
  const consumedKeys = new Set();
@@ -331,23 +416,27 @@ export function buildCommandArgv(cmd, cmdMeta) {
331
416
  flags: opt.flags,
332
417
  variadic: opt.variadic,
333
418
  name,
419
+ description: opt.description,
334
420
  });
335
421
  }
336
422
  }
337
423
  // Phase 1: Emit positional args in Commander definition order
338
424
  for (const argDef of positionalDefs) {
339
425
  const canonicalName = normalizeArgKey(argDef.name);
340
- const matchedKey = Object.keys(cmd.args).find((k) => normalizeArgKey(k) === canonicalName);
426
+ const matchedKey = Object.keys(cmd.args).find((k) => resolveBatchArgAlias(k) === canonicalName);
341
427
  const value = matchedKey ? cmd.args[matchedKey] : undefined;
342
428
  if (value === undefined)
343
429
  continue;
344
430
  consumedKeys.add(matchedKey);
345
- if (Array.isArray(value)) {
346
- for (const v of value)
431
+ const normalizedValue = isNumericPriorityArgument(argDef)
432
+ ? normalizePriorityAlias(value)
433
+ : value;
434
+ if (Array.isArray(normalizedValue)) {
435
+ for (const v of normalizedValue)
347
436
  argv.push(toArgString(v));
348
437
  }
349
438
  else {
350
- argv.push(toArgString(value));
439
+ argv.push(toArgString(normalizedValue));
351
440
  }
352
441
  }
353
442
  // Phase 2: Emit options from remaining keys
@@ -355,27 +444,30 @@ export function buildCommandArgv(cmd, cmdMeta) {
355
444
  if (consumedKeys.has(key)) {
356
445
  continue;
357
446
  }
358
- const canonicalKey = normalizeArgKey(key);
447
+ const canonicalKey = resolveBatchArgAlias(key);
359
448
  // Skip positional args (already emitted)
360
449
  if (positionalCanonicalNameSet.has(canonicalKey)) {
361
450
  continue;
362
451
  }
363
452
  const knownOption = optionMap.get(canonicalKey);
364
453
  const flagName = `--${knownOption?.name ?? canonicalKey}`;
365
- if (typeof value === "boolean") {
366
- if (value) {
454
+ const normalizedValue = isNumericPriorityOption(knownOption)
455
+ ? normalizePriorityAlias(value)
456
+ : value;
457
+ if (typeof normalizedValue === "boolean") {
458
+ if (normalizedValue) {
367
459
  argv.push(flagName);
368
460
  }
369
461
  // false booleans: omit (Commander treats absence as false)
370
462
  }
371
- else if (Array.isArray(value)) {
463
+ else if (Array.isArray(normalizedValue)) {
372
464
  // Variadic or repeated options
373
- for (const v of value) {
465
+ for (const v of normalizedValue) {
374
466
  argv.push(flagName, toArgString(v));
375
467
  }
376
468
  }
377
- else if (value !== null && value !== undefined) {
378
- argv.push(flagName, toArgString(value));
469
+ else if (normalizedValue !== null && normalizedValue !== undefined) {
470
+ argv.push(flagName, toArgString(normalizedValue));
379
471
  }
380
472
  }
381
473
  return argv;
@@ -464,34 +556,37 @@ export async function executeBatch(commands, program, options) {
464
556
  }
465
557
  }
466
558
  /**
467
- * Atomic execution: copy specDir to temp, run all, copy back on success.
559
+ * Atomic execution: buffer all writes in memory, flush to disk on success.
560
+ *
561
+ * Replaces the old fs.cp(realSpecDir, tempDir) approach with an in-memory
562
+ * write buffer. Writes during batch execution are intercepted by yaml.ts and
563
+ * stored in the buffer. On success, the buffer is flushed to the real specDir.
564
+ * On failure, the buffer is discarded — the real .kspec/ is never touched.
468
565
  *
469
566
  * AC: @batch-exec ac-default-atomic
470
567
  * AC: @batch-exec ac-atomic-rollback
471
568
  * AC: @batch-exec ac-atomic-isolation
472
569
  * AC: @batch-exec ac-single-commit
570
+ * AC: @batch-write-buffer ac-1 — writes buffered in memory, not on disk
571
+ * AC: @batch-write-buffer ac-4 — rollback discards buffer
572
+ * AC: @batch-write-buffer ac-5 — sessions/ never copied (buffer is per-file)
573
+ * AC: @batch-write-buffer ac-6 — real .kspec/ unchanged until flush
473
574
  */
474
575
  async function executeAtomic(commands, program, tree, options) {
475
- // Get the real context for copy-back
476
576
  const ctx = await initContext();
477
577
  const realSpecDir = ctx.specDir;
478
- // Create temp copy using mkdtemp for safe, collision-free naming
479
- const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "kspec-batch-"));
480
- await fs.cp(realSpecDir, tempDir, { recursive: true });
481
- // Remove .git from temp copy to prevent worktree pointer leaks
482
- await fs.rm(path.join(tempDir, ".git"), { force: true, recursive: true });
578
+ // Activate in-memory write buffer writes to specDir go to buffer
579
+ const buffer = activateBatchBuffer(realSpecDir);
483
580
  // Set up atomic context
484
581
  const savedChalkLevel = chalk.level;
485
- const savedSpecDir = process.env.KSPEC_SPEC_DIR;
486
582
  const savedBatchProjectRoot = process.env.KSPEC_BATCH_PROJECT_ROOT;
487
- // AC: @project-config ac-7 — set real project root before redirecting spec dir
583
+ // AC: @project-config ac-7 — set real project root for config resolution
488
584
  process.env.KSPEC_BATCH_PROJECT_ROOT = ctx.rootDir;
489
- process.env.KSPEC_SPEC_DIR = tempDir;
490
585
  setBatchMode(true);
491
586
  chalk.level = 0;
492
587
  const results = [];
493
588
  let allSucceeded = true;
494
- let copyBackFailed = false;
589
+ let flushFailed = false;
495
590
  try {
496
591
  for (let i = 0; i < commands.length; i++) {
497
592
  const cmd = commands[i];
@@ -499,7 +594,7 @@ async function executeAtomic(commands, program, tree, options) {
499
594
  results.push(result);
500
595
  if (!result.success) {
501
596
  allSucceeded = false;
502
- // Atomic mode: stop on first failure, discard everything
597
+ // Atomic mode: stop on first failure, discard buffer
503
598
  // Fill remaining as not-executed
504
599
  for (let j = i + 1; j < commands.length; j++) {
505
600
  results.push({
@@ -513,21 +608,12 @@ async function executeAtomic(commands, program, tree, options) {
513
608
  break;
514
609
  }
515
610
  }
516
- // Copy back on success
611
+ // Flush buffer to disk on success
612
+ // AC: @batch-write-buffer ac-3 — only written files flushed
613
+ // AC: @batch-write-buffer ac-7 — flush failure reported, pre-batch state preserved
517
614
  if (allSucceeded) {
518
615
  try {
519
- // Clear real specDir contents (except .git and .gitattributes) before copy-back
520
- const entries = await fs.readdir(realSpecDir);
521
- for (const entry of entries) {
522
- if (entry === ".git" || entry === ".gitattributes")
523
- continue;
524
- await fs.rm(path.join(realSpecDir, entry), { recursive: true, force: true });
525
- }
526
- // Copy temp contents back
527
- const tempEntries = await fs.readdir(tempDir);
528
- for (const entry of tempEntries) {
529
- await fs.cp(path.join(tempDir, entry), path.join(realSpecDir, entry), { recursive: true });
530
- }
616
+ await buffer.flush();
531
617
  // Single shadow commit for all changes
532
618
  if (ctx.shadow?.enabled) {
533
619
  const successCount = results.filter((r) => r.success).length;
@@ -535,25 +621,21 @@ async function executeAtomic(commands, program, tree, options) {
535
621
  shadowPushAsync(ctx.shadow.worktreeDir);
536
622
  }
537
623
  }
538
- catch (copyErr) {
539
- // Copy-back failed — preserve tempDir for manual recovery
540
- copyBackFailed = true;
624
+ catch (flushErr) {
625
+ flushFailed = true;
541
626
  allSucceeded = false;
542
- console.error(`Batch copy-back failed: ${copyErr instanceof Error ? copyErr.message : copyErr}`);
543
- console.error(`Temp dir preserved for recovery: ${tempDir}`);
627
+ console.error(`Batch flush failed: ${flushErr instanceof Error ? flushErr.message : flushErr}`);
544
628
  }
545
629
  }
546
630
  }
547
631
  finally {
548
- // Clean up
632
+ // Always deactivate buffer (discard if not flushed)
633
+ if (!allSucceeded || flushFailed) {
634
+ buffer.discard();
635
+ }
636
+ deactivateBatchBuffer();
549
637
  setBatchMode(false);
550
638
  chalk.level = savedChalkLevel;
551
- if (savedSpecDir !== undefined) {
552
- process.env.KSPEC_SPEC_DIR = savedSpecDir;
553
- }
554
- else {
555
- delete process.env.KSPEC_SPEC_DIR;
556
- }
557
639
  // AC: @project-config ac-7 — restore batch project root env var
558
640
  if (savedBatchProjectRoot !== undefined) {
559
641
  process.env.KSPEC_BATCH_PROJECT_ROOT = savedBatchProjectRoot;
@@ -561,10 +643,6 @@ async function executeAtomic(commands, program, tree, options) {
561
643
  else {
562
644
  delete process.env.KSPEC_BATCH_PROJECT_ROOT;
563
645
  }
564
- // Only remove temp dir if copy-back succeeded (or commands failed)
565
- if (!copyBackFailed) {
566
- await fs.rm(tempDir, { recursive: true, force: true }).catch(() => { });
567
- }
568
646
  }
569
647
  const succeeded = results.filter((r) => r.success).length;
570
648
  const failed = results.filter((r) => !r.success).length;