@kynetic-ai/spec 0.1.2 → 0.4.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 (540) hide show
  1. package/README.md +250 -17
  2. package/dist/acp/client.d.ts +18 -4
  3. package/dist/acp/client.d.ts.map +1 -1
  4. package/dist/acp/client.js +44 -26
  5. package/dist/acp/client.js.map +1 -1
  6. package/dist/acp/framing.d.ts +2 -2
  7. package/dist/acp/framing.d.ts.map +1 -1
  8. package/dist/acp/framing.js +37 -29
  9. package/dist/acp/framing.js.map +1 -1
  10. package/dist/acp/index.d.ts +6 -7
  11. package/dist/acp/index.d.ts.map +1 -1
  12. package/dist/acp/index.js +3 -3
  13. package/dist/acp/index.js.map +1 -1
  14. package/dist/acp/types.d.ts +5 -5
  15. package/dist/acp/types.d.ts.map +1 -1
  16. package/dist/acp/types.js +18 -18
  17. package/dist/acp/types.js.map +1 -1
  18. package/dist/agents/adapters.d.ts.map +1 -1
  19. package/dist/agents/adapters.js +24 -13
  20. package/dist/agents/adapters.js.map +1 -1
  21. package/dist/agents/index.d.ts +2 -2
  22. package/dist/agents/index.js +2 -2
  23. package/dist/agents/spawner.d.ts +4 -4
  24. package/dist/agents/spawner.d.ts.map +1 -1
  25. package/dist/agents/spawner.js +6 -6
  26. package/dist/agents/spawner.js.map +1 -1
  27. package/dist/cli/batch-context.d.ts +43 -0
  28. package/dist/cli/batch-context.d.ts.map +1 -0
  29. package/dist/cli/batch-context.js +93 -0
  30. package/dist/cli/batch-context.js.map +1 -0
  31. package/dist/cli/batch-exec.d.ts +107 -0
  32. package/dist/cli/batch-exec.d.ts.map +1 -0
  33. package/dist/cli/batch-exec.js +706 -0
  34. package/dist/cli/batch-exec.js.map +1 -0
  35. package/dist/cli/batch.d.ts +4 -2
  36. package/dist/cli/batch.d.ts.map +1 -1
  37. package/dist/cli/batch.js +15 -14
  38. package/dist/cli/batch.js.map +1 -1
  39. package/dist/cli/command-annotations.d.ts +23 -0
  40. package/dist/cli/command-annotations.d.ts.map +1 -0
  41. package/dist/cli/command-annotations.js +27 -0
  42. package/dist/cli/command-annotations.js.map +1 -0
  43. package/dist/cli/commands/agents.d.ts +46 -0
  44. package/dist/cli/commands/agents.d.ts.map +1 -0
  45. package/dist/cli/commands/agents.js +377 -0
  46. package/dist/cli/commands/agents.js.map +1 -0
  47. package/dist/cli/commands/batch.d.ts +20 -0
  48. package/dist/cli/commands/batch.d.ts.map +1 -0
  49. package/dist/cli/commands/batch.js +214 -0
  50. package/dist/cli/commands/batch.js.map +1 -0
  51. package/dist/cli/commands/clone-for-testing.d.ts +1 -1
  52. package/dist/cli/commands/clone-for-testing.d.ts.map +1 -1
  53. package/dist/cli/commands/clone-for-testing.js +37 -47
  54. package/dist/cli/commands/clone-for-testing.js.map +1 -1
  55. package/dist/cli/commands/derive.d.ts +1 -1
  56. package/dist/cli/commands/derive.d.ts.map +1 -1
  57. package/dist/cli/commands/derive.js +141 -88
  58. package/dist/cli/commands/derive.js.map +1 -1
  59. package/dist/cli/commands/doctor.d.ts +11 -0
  60. package/dist/cli/commands/doctor.d.ts.map +1 -0
  61. package/dist/cli/commands/doctor.js +152 -0
  62. package/dist/cli/commands/doctor.js.map +1 -0
  63. package/dist/cli/commands/export.d.ts +12 -0
  64. package/dist/cli/commands/export.d.ts.map +1 -0
  65. package/dist/cli/commands/export.js +134 -0
  66. package/dist/cli/commands/export.js.map +1 -0
  67. package/dist/cli/commands/help.d.ts +1 -1
  68. package/dist/cli/commands/help.d.ts.map +1 -1
  69. package/dist/cli/commands/help.js +163 -37
  70. package/dist/cli/commands/help.js.map +1 -1
  71. package/dist/cli/commands/inbox.d.ts +1 -1
  72. package/dist/cli/commands/inbox.d.ts.map +1 -1
  73. package/dist/cli/commands/inbox.js +178 -56
  74. package/dist/cli/commands/inbox.js.map +1 -1
  75. package/dist/cli/commands/index.d.ts +31 -19
  76. package/dist/cli/commands/index.d.ts.map +1 -1
  77. package/dist/cli/commands/index.js +31 -19
  78. package/dist/cli/commands/index.js.map +1 -1
  79. package/dist/cli/commands/init.d.ts +5 -1
  80. package/dist/cli/commands/init.d.ts.map +1 -1
  81. package/dist/cli/commands/init.js +108 -57
  82. package/dist/cli/commands/init.js.map +1 -1
  83. package/dist/cli/commands/item.d.ts +1 -1
  84. package/dist/cli/commands/item.d.ts.map +1 -1
  85. package/dist/cli/commands/item.js +557 -274
  86. package/dist/cli/commands/item.js.map +1 -1
  87. package/dist/cli/commands/link.d.ts +1 -1
  88. package/dist/cli/commands/link.d.ts.map +1 -1
  89. package/dist/cli/commands/link.js +55 -46
  90. package/dist/cli/commands/link.js.map +1 -1
  91. package/dist/cli/commands/log.d.ts +1 -1
  92. package/dist/cli/commands/log.d.ts.map +1 -1
  93. package/dist/cli/commands/log.js +58 -51
  94. package/dist/cli/commands/log.js.map +1 -1
  95. package/dist/cli/commands/merge-driver.d.ts +19 -0
  96. package/dist/cli/commands/merge-driver.d.ts.map +1 -0
  97. package/dist/cli/commands/merge-driver.js +398 -0
  98. package/dist/cli/commands/merge-driver.js.map +1 -0
  99. package/dist/cli/commands/meta.d.ts +1 -1
  100. package/dist/cli/commands/meta.d.ts.map +1 -1
  101. package/dist/cli/commands/meta.js +534 -399
  102. package/dist/cli/commands/meta.js.map +1 -1
  103. package/dist/cli/commands/module.d.ts +1 -1
  104. package/dist/cli/commands/module.d.ts.map +1 -1
  105. package/dist/cli/commands/module.js +30 -25
  106. package/dist/cli/commands/module.js.map +1 -1
  107. package/dist/cli/commands/plan-import.d.ts +11 -0
  108. package/dist/cli/commands/plan-import.d.ts.map +1 -0
  109. package/dist/cli/commands/plan-import.js +547 -0
  110. package/dist/cli/commands/plan-import.js.map +1 -0
  111. package/dist/cli/commands/plan.d.ts +10 -0
  112. package/dist/cli/commands/plan.d.ts.map +1 -0
  113. package/dist/cli/commands/plan.js +421 -0
  114. package/dist/cli/commands/plan.js.map +1 -0
  115. package/dist/cli/commands/ralph.d.ts +1 -1
  116. package/dist/cli/commands/ralph.d.ts.map +1 -1
  117. package/dist/cli/commands/ralph.js +1109 -170
  118. package/dist/cli/commands/ralph.js.map +1 -1
  119. package/dist/cli/commands/refs.d.ts +13 -0
  120. package/dist/cli/commands/refs.d.ts.map +1 -0
  121. package/dist/cli/commands/refs.js +283 -0
  122. package/dist/cli/commands/refs.js.map +1 -0
  123. package/dist/cli/commands/search.d.ts +1 -1
  124. package/dist/cli/commands/search.d.ts.map +1 -1
  125. package/dist/cli/commands/search.js +199 -37
  126. package/dist/cli/commands/search.js.map +1 -1
  127. package/dist/cli/commands/serve.d.ts +10 -0
  128. package/dist/cli/commands/serve.d.ts.map +1 -0
  129. package/dist/cli/commands/serve.js +491 -0
  130. package/dist/cli/commands/serve.js.map +1 -0
  131. package/dist/cli/commands/session.d.ts +25 -6
  132. package/dist/cli/commands/session.d.ts.map +1 -1
  133. package/dist/cli/commands/session.js +810 -127
  134. package/dist/cli/commands/session.js.map +1 -1
  135. package/dist/cli/commands/setup-seeding.d.ts +81 -0
  136. package/dist/cli/commands/setup-seeding.d.ts.map +1 -0
  137. package/dist/cli/commands/setup-seeding.js +292 -0
  138. package/dist/cli/commands/setup-seeding.js.map +1 -0
  139. package/dist/cli/commands/setup.d.ts +77 -3
  140. package/dist/cli/commands/setup.d.ts.map +1 -1
  141. package/dist/cli/commands/setup.js +1267 -274
  142. package/dist/cli/commands/setup.js.map +1 -1
  143. package/dist/cli/commands/shadow.d.ts +1 -1
  144. package/dist/cli/commands/shadow.d.ts.map +1 -1
  145. package/dist/cli/commands/shadow.js +70 -50
  146. package/dist/cli/commands/shadow.js.map +1 -1
  147. package/dist/cli/commands/skill-crud.d.ts +58 -0
  148. package/dist/cli/commands/skill-crud.d.ts.map +1 -0
  149. package/dist/cli/commands/skill-crud.js +753 -0
  150. package/dist/cli/commands/skill-crud.js.map +1 -0
  151. package/dist/cli/commands/skill-diff.d.ts +27 -0
  152. package/dist/cli/commands/skill-diff.d.ts.map +1 -0
  153. package/dist/cli/commands/skill-diff.js +840 -0
  154. package/dist/cli/commands/skill-diff.js.map +1 -0
  155. package/dist/cli/commands/skill-install.d.ts +56 -0
  156. package/dist/cli/commands/skill-install.d.ts.map +1 -0
  157. package/dist/cli/commands/skill-install.js +509 -0
  158. package/dist/cli/commands/skill-install.js.map +1 -0
  159. package/dist/cli/commands/skill.d.ts +20 -0
  160. package/dist/cli/commands/skill.d.ts.map +1 -0
  161. package/dist/cli/commands/skill.js +36 -0
  162. package/dist/cli/commands/skill.js.map +1 -0
  163. package/dist/cli/commands/task.d.ts +1 -1
  164. package/dist/cli/commands/task.d.ts.map +1 -1
  165. package/dist/cli/commands/task.js +584 -350
  166. package/dist/cli/commands/task.js.map +1 -1
  167. package/dist/cli/commands/tasks.d.ts +26 -1
  168. package/dist/cli/commands/tasks.d.ts.map +1 -1
  169. package/dist/cli/commands/tasks.js +225 -122
  170. package/dist/cli/commands/tasks.js.map +1 -1
  171. package/dist/cli/commands/trait.d.ts +1 -1
  172. package/dist/cli/commands/trait.d.ts.map +1 -1
  173. package/dist/cli/commands/trait.js +166 -101
  174. package/dist/cli/commands/trait.js.map +1 -1
  175. package/dist/cli/commands/triage.d.ts +7 -0
  176. package/dist/cli/commands/triage.d.ts.map +1 -0
  177. package/dist/cli/commands/triage.js +483 -0
  178. package/dist/cli/commands/triage.js.map +1 -0
  179. package/dist/cli/commands/util.d.ts +7 -0
  180. package/dist/cli/commands/util.d.ts.map +1 -0
  181. package/dist/cli/commands/util.js +30 -0
  182. package/dist/cli/commands/util.js.map +1 -0
  183. package/dist/cli/commands/validate.d.ts +1 -1
  184. package/dist/cli/commands/validate.d.ts.map +1 -1
  185. package/dist/cli/commands/validate.js +264 -83
  186. package/dist/cli/commands/validate.js.map +1 -1
  187. package/dist/cli/commands/workflow.d.ts +16 -0
  188. package/dist/cli/commands/workflow.d.ts.map +1 -0
  189. package/dist/cli/commands/workflow.js +851 -0
  190. package/dist/cli/commands/workflow.js.map +1 -0
  191. package/dist/cli/exit-codes.d.ts +7 -0
  192. package/dist/cli/exit-codes.d.ts.map +1 -1
  193. package/dist/cli/exit-codes.js +26 -18
  194. package/dist/cli/exit-codes.js.map +1 -1
  195. package/dist/cli/help/content.d.ts.map +1 -1
  196. package/dist/cli/help/content.js +86 -71
  197. package/dist/cli/help/content.js.map +1 -1
  198. package/dist/cli/index.d.ts +1 -1
  199. package/dist/cli/index.d.ts.map +1 -1
  200. package/dist/cli/index.js +131 -19
  201. package/dist/cli/index.js.map +1 -1
  202. package/dist/cli/introspection.d.ts +6 -2
  203. package/dist/cli/introspection.d.ts.map +1 -1
  204. package/dist/cli/introspection.js +11 -8
  205. package/dist/cli/introspection.js.map +1 -1
  206. package/dist/cli/output.d.ts +64 -4
  207. package/dist/cli/output.d.ts.map +1 -1
  208. package/dist/cli/output.js +237 -85
  209. package/dist/cli/output.js.map +1 -1
  210. package/dist/cli/parse-utils.d.ts +21 -0
  211. package/dist/cli/parse-utils.d.ts.map +1 -0
  212. package/dist/cli/parse-utils.js +32 -0
  213. package/dist/cli/parse-utils.js.map +1 -0
  214. package/dist/cli/pid-utils.d.ts +72 -0
  215. package/dist/cli/pid-utils.d.ts.map +1 -0
  216. package/dist/cli/pid-utils.js +174 -0
  217. package/dist/cli/pid-utils.js.map +1 -0
  218. package/dist/cli/suggest.d.ts.map +1 -1
  219. package/dist/cli/suggest.js +1 -2
  220. package/dist/cli/suggest.js.map +1 -1
  221. package/dist/cli/validators.d.ts +43 -0
  222. package/dist/cli/validators.d.ts.map +1 -0
  223. package/dist/cli/validators.js +84 -0
  224. package/dist/cli/validators.js.map +1 -0
  225. package/dist/daemon/index.ts +52 -0
  226. package/dist/daemon/middleware/project-context.ts +126 -0
  227. package/dist/daemon/pid.ts +179 -0
  228. package/dist/daemon/project-context.ts +343 -0
  229. package/dist/daemon/routes/inbox.ts +164 -0
  230. package/dist/daemon/routes/items.ts +322 -0
  231. package/dist/daemon/routes/meta.ts +118 -0
  232. package/dist/daemon/routes/projects.ts +162 -0
  233. package/dist/daemon/routes/tasks.ts +327 -0
  234. package/dist/daemon/routes/triage.ts +402 -0
  235. package/dist/daemon/routes/validation.ts +248 -0
  236. package/dist/daemon/server.ts +408 -0
  237. package/dist/daemon/watcher.ts +195 -0
  238. package/dist/daemon/websocket/handler.ts +138 -0
  239. package/dist/daemon/websocket/heartbeat.ts +71 -0
  240. package/dist/daemon/websocket/pubsub.ts +125 -0
  241. package/dist/daemon/websocket/types.ts +66 -0
  242. package/dist/export/html.d.ts +19 -0
  243. package/dist/export/html.d.ts.map +1 -0
  244. package/dist/export/html.js +239 -0
  245. package/dist/export/html.js.map +1 -0
  246. package/dist/export/index.d.ts +10 -0
  247. package/dist/export/index.d.ts.map +1 -0
  248. package/dist/export/index.js +10 -0
  249. package/dist/export/index.js.map +1 -0
  250. package/dist/export/json.d.ts +24 -0
  251. package/dist/export/json.d.ts.map +1 -0
  252. package/dist/export/json.js +198 -0
  253. package/dist/export/json.js.map +1 -0
  254. package/dist/export/triage.d.ts +51 -0
  255. package/dist/export/triage.d.ts.map +1 -0
  256. package/dist/export/triage.js +83 -0
  257. package/dist/export/triage.js.map +1 -0
  258. package/dist/export/types.d.ts +122 -0
  259. package/dist/export/types.d.ts.map +1 -0
  260. package/dist/export/types.js +9 -0
  261. package/dist/export/types.js.map +1 -0
  262. package/dist/index.d.ts +2 -2
  263. package/dist/index.js +2 -2
  264. package/dist/lib/claude-plugin-registry.d.ts +66 -0
  265. package/dist/lib/claude-plugin-registry.d.ts.map +1 -0
  266. package/dist/lib/claude-plugin-registry.js +318 -0
  267. package/dist/lib/claude-plugin-registry.js.map +1 -0
  268. package/dist/merge/arrays.d.ts +87 -0
  269. package/dist/merge/arrays.d.ts.map +1 -0
  270. package/dist/merge/arrays.js +164 -0
  271. package/dist/merge/arrays.js.map +1 -0
  272. package/dist/merge/file-type.d.ts +32 -0
  273. package/dist/merge/file-type.d.ts.map +1 -0
  274. package/dist/merge/file-type.js +70 -0
  275. package/dist/merge/file-type.js.map +1 -0
  276. package/dist/merge/index.d.ts +14 -0
  277. package/dist/merge/index.d.ts.map +1 -0
  278. package/dist/merge/index.js +11 -0
  279. package/dist/merge/index.js.map +1 -0
  280. package/dist/merge/objects.d.ts +46 -0
  281. package/dist/merge/objects.d.ts.map +1 -0
  282. package/dist/merge/objects.js +193 -0
  283. package/dist/merge/objects.js.map +1 -0
  284. package/dist/merge/parse.d.ts +23 -0
  285. package/dist/merge/parse.d.ts.map +1 -0
  286. package/dist/merge/parse.js +78 -0
  287. package/dist/merge/parse.js.map +1 -0
  288. package/dist/merge/resolve.d.ts +66 -0
  289. package/dist/merge/resolve.d.ts.map +1 -0
  290. package/dist/merge/resolve.js +189 -0
  291. package/dist/merge/resolve.js.map +1 -0
  292. package/dist/merge/types.d.ts +82 -0
  293. package/dist/merge/types.d.ts.map +1 -0
  294. package/dist/merge/types.js +8 -0
  295. package/dist/merge/types.js.map +1 -0
  296. package/dist/parser/agent-data-sections.d.ts +53 -0
  297. package/dist/parser/agent-data-sections.d.ts.map +1 -0
  298. package/dist/parser/agent-data-sections.js +118 -0
  299. package/dist/parser/agent-data-sections.js.map +1 -0
  300. package/dist/parser/alignment.d.ts +4 -4
  301. package/dist/parser/alignment.d.ts.map +1 -1
  302. package/dist/parser/alignment.js +27 -22
  303. package/dist/parser/alignment.js.map +1 -1
  304. package/dist/parser/assess.d.ts +5 -5
  305. package/dist/parser/assess.d.ts.map +1 -1
  306. package/dist/parser/assess.js +36 -32
  307. package/dist/parser/assess.js.map +1 -1
  308. package/dist/parser/config.d.ts +457 -0
  309. package/dist/parser/config.d.ts.map +1 -0
  310. package/dist/parser/config.js +373 -0
  311. package/dist/parser/config.js.map +1 -0
  312. package/dist/parser/convention-validation.d.ts +1 -1
  313. package/dist/parser/convention-validation.d.ts.map +1 -1
  314. package/dist/parser/convention-validation.js +21 -16
  315. package/dist/parser/convention-validation.js.map +1 -1
  316. package/dist/parser/coverage-cache.d.ts +49 -0
  317. package/dist/parser/coverage-cache.d.ts.map +1 -0
  318. package/dist/parser/coverage-cache.js +123 -0
  319. package/dist/parser/coverage-cache.js.map +1 -0
  320. package/dist/parser/daemon-status.d.ts +37 -0
  321. package/dist/parser/daemon-status.d.ts.map +1 -0
  322. package/dist/parser/daemon-status.js +67 -0
  323. package/dist/parser/daemon-status.js.map +1 -0
  324. package/dist/parser/doctor.d.ts +107 -0
  325. package/dist/parser/doctor.d.ts.map +1 -0
  326. package/dist/parser/doctor.js +366 -0
  327. package/dist/parser/doctor.js.map +1 -0
  328. package/dist/parser/fix.d.ts +1 -1
  329. package/dist/parser/fix.d.ts.map +1 -1
  330. package/dist/parser/fix.js +31 -27
  331. package/dist/parser/fix.js.map +1 -1
  332. package/dist/parser/index.d.ts +16 -11
  333. package/dist/parser/index.d.ts.map +1 -1
  334. package/dist/parser/index.js +16 -11
  335. package/dist/parser/index.js.map +1 -1
  336. package/dist/parser/items.d.ts +8 -2
  337. package/dist/parser/items.d.ts.map +1 -1
  338. package/dist/parser/items.js +71 -35
  339. package/dist/parser/items.js.map +1 -1
  340. package/dist/parser/meta.d.ts +167 -9
  341. package/dist/parser/meta.d.ts.map +1 -1
  342. package/dist/parser/meta.js +379 -46
  343. package/dist/parser/meta.js.map +1 -1
  344. package/dist/parser/plan-document.d.ts +197 -0
  345. package/dist/parser/plan-document.d.ts.map +1 -0
  346. package/dist/parser/plan-document.js +341 -0
  347. package/dist/parser/plan-document.js.map +1 -0
  348. package/dist/parser/plans.d.ts +59 -0
  349. package/dist/parser/plans.d.ts.map +1 -0
  350. package/dist/parser/plans.js +239 -0
  351. package/dist/parser/plans.js.map +1 -0
  352. package/dist/parser/refs.d.ts +22 -9
  353. package/dist/parser/refs.d.ts.map +1 -1
  354. package/dist/parser/refs.js +102 -50
  355. package/dist/parser/refs.js.map +1 -1
  356. package/dist/parser/setup-status.d.ts +71 -0
  357. package/dist/parser/setup-status.d.ts.map +1 -0
  358. package/dist/parser/setup-status.js +269 -0
  359. package/dist/parser/setup-status.js.map +1 -0
  360. package/dist/parser/shadow.d.ts +150 -19
  361. package/dist/parser/shadow.d.ts.map +1 -1
  362. package/dist/parser/shadow.js +548 -187
  363. package/dist/parser/shadow.js.map +1 -1
  364. package/dist/parser/skill-render.d.ts +317 -0
  365. package/dist/parser/skill-render.d.ts.map +1 -0
  366. package/dist/parser/skill-render.js +943 -0
  367. package/dist/parser/skill-render.js.map +1 -0
  368. package/dist/parser/traits.d.ts +3 -3
  369. package/dist/parser/traits.d.ts.map +1 -1
  370. package/dist/parser/traits.js +2 -2
  371. package/dist/parser/traits.js.map +1 -1
  372. package/dist/parser/validate-skills.d.ts +32 -0
  373. package/dist/parser/validate-skills.d.ts.map +1 -0
  374. package/dist/parser/validate-skills.js +202 -0
  375. package/dist/parser/validate-skills.js.map +1 -0
  376. package/dist/parser/validate.d.ts +45 -3
  377. package/dist/parser/validate.d.ts.map +1 -1
  378. package/dist/parser/validate.js +622 -105
  379. package/dist/parser/validate.js.map +1 -1
  380. package/dist/parser/yaml.d.ts +83 -19
  381. package/dist/parser/yaml.d.ts.map +1 -1
  382. package/dist/parser/yaml.js +478 -173
  383. package/dist/parser/yaml.js.map +1 -1
  384. package/dist/ralph/cli-renderer.d.ts +8 -1
  385. package/dist/ralph/cli-renderer.d.ts.map +1 -1
  386. package/dist/ralph/cli-renderer.js +105 -34
  387. package/dist/ralph/cli-renderer.js.map +1 -1
  388. package/dist/ralph/events.d.ts +10 -10
  389. package/dist/ralph/events.d.ts.map +1 -1
  390. package/dist/ralph/events.js +301 -98
  391. package/dist/ralph/events.js.map +1 -1
  392. package/dist/ralph/index.d.ts +5 -2
  393. package/dist/ralph/index.d.ts.map +1 -1
  394. package/dist/ralph/index.js +9 -3
  395. package/dist/ralph/index.js.map +1 -1
  396. package/dist/ralph/loop-errors.d.ts +83 -0
  397. package/dist/ralph/loop-errors.d.ts.map +1 -0
  398. package/dist/ralph/loop-errors.js +150 -0
  399. package/dist/ralph/loop-errors.js.map +1 -0
  400. package/dist/ralph/subagent.d.ts +94 -0
  401. package/dist/ralph/subagent.d.ts.map +1 -0
  402. package/dist/ralph/subagent.js +193 -0
  403. package/dist/ralph/subagent.js.map +1 -0
  404. package/dist/ralph/wrap-up.d.ts +125 -0
  405. package/dist/ralph/wrap-up.d.ts.map +1 -0
  406. package/dist/ralph/wrap-up.js +270 -0
  407. package/dist/ralph/wrap-up.js.map +1 -0
  408. package/dist/schema/batch.d.ts +97 -0
  409. package/dist/schema/batch.d.ts.map +1 -0
  410. package/dist/schema/batch.js +24 -0
  411. package/dist/schema/batch.js.map +1 -0
  412. package/dist/schema/common.d.ts +8 -2
  413. package/dist/schema/common.d.ts.map +1 -1
  414. package/dist/schema/common.js +42 -31
  415. package/dist/schema/common.js.map +1 -1
  416. package/dist/schema/inbox.d.ts +12 -12
  417. package/dist/schema/inbox.js +4 -4
  418. package/dist/schema/inbox.js.map +1 -1
  419. package/dist/schema/index.d.ts +8 -5
  420. package/dist/schema/index.d.ts.map +1 -1
  421. package/dist/schema/index.js +8 -5
  422. package/dist/schema/index.js.map +1 -1
  423. package/dist/schema/meta.d.ts +1454 -27
  424. package/dist/schema/meta.d.ts.map +1 -1
  425. package/dist/schema/meta.js +198 -21
  426. package/dist/schema/meta.js.map +1 -1
  427. package/dist/schema/plan.d.ts +285 -0
  428. package/dist/schema/plan.d.ts.map +1 -0
  429. package/dist/schema/plan.js +81 -0
  430. package/dist/schema/plan.js.map +1 -0
  431. package/dist/schema/spec.d.ts +72 -33
  432. package/dist/schema/spec.d.ts.map +1 -1
  433. package/dist/schema/spec.js +22 -9
  434. package/dist/schema/spec.js.map +1 -1
  435. package/dist/schema/task.d.ts +172 -161
  436. package/dist/schema/task.d.ts.map +1 -1
  437. package/dist/schema/task.js +21 -12
  438. package/dist/schema/task.js.map +1 -1
  439. package/dist/schema/triage.d.ts +266 -0
  440. package/dist/schema/triage.d.ts.map +1 -0
  441. package/dist/schema/triage.js +134 -0
  442. package/dist/schema/triage.js.map +1 -0
  443. package/dist/sessions/index.d.ts +2 -2
  444. package/dist/sessions/index.d.ts.map +1 -1
  445. package/dist/sessions/index.js +3 -3
  446. package/dist/sessions/index.js.map +1 -1
  447. package/dist/sessions/store.d.ts +241 -1
  448. package/dist/sessions/store.d.ts.map +1 -1
  449. package/dist/sessions/store.js +810 -31
  450. package/dist/sessions/store.js.map +1 -1
  451. package/dist/sessions/types.d.ts +10 -10
  452. package/dist/sessions/types.d.ts.map +1 -1
  453. package/dist/sessions/types.js +10 -9
  454. package/dist/sessions/types.js.map +1 -1
  455. package/dist/strings/errors.d.ts +55 -0
  456. package/dist/strings/errors.d.ts.map +1 -1
  457. package/dist/strings/errors.js +138 -106
  458. package/dist/strings/errors.js.map +1 -1
  459. package/dist/strings/guidance.d.ts.map +1 -1
  460. package/dist/strings/guidance.js +16 -16
  461. package/dist/strings/guidance.js.map +1 -1
  462. package/dist/strings/index.d.ts +4 -4
  463. package/dist/strings/index.d.ts.map +1 -1
  464. package/dist/strings/index.js +4 -4
  465. package/dist/strings/index.js.map +1 -1
  466. package/dist/strings/labels.d.ts +4 -0
  467. package/dist/strings/labels.d.ts.map +1 -1
  468. package/dist/strings/labels.js +45 -41
  469. package/dist/strings/labels.js.map +1 -1
  470. package/dist/strings/validation.d.ts.map +1 -1
  471. package/dist/strings/validation.js +71 -71
  472. package/dist/strings/validation.js.map +1 -1
  473. package/dist/triage/actions.d.ts +27 -0
  474. package/dist/triage/actions.d.ts.map +1 -0
  475. package/dist/triage/actions.js +95 -0
  476. package/dist/triage/actions.js.map +1 -0
  477. package/dist/triage/constants.d.ts +6 -0
  478. package/dist/triage/constants.d.ts.map +1 -0
  479. package/dist/triage/constants.js +7 -0
  480. package/dist/triage/constants.js.map +1 -0
  481. package/dist/triage/index.d.ts +3 -0
  482. package/dist/triage/index.d.ts.map +1 -0
  483. package/dist/triage/index.js +3 -0
  484. package/dist/triage/index.js.map +1 -0
  485. package/dist/utils/commit.d.ts +1 -1
  486. package/dist/utils/commit.d.ts.map +1 -1
  487. package/dist/utils/commit.js +28 -26
  488. package/dist/utils/commit.js.map +1 -1
  489. package/dist/utils/git.d.ts +1 -1
  490. package/dist/utils/git.d.ts.map +1 -1
  491. package/dist/utils/git.js +40 -38
  492. package/dist/utils/git.js.map +1 -1
  493. package/dist/utils/grep.js +11 -11
  494. package/dist/utils/grep.js.map +1 -1
  495. package/dist/utils/index.d.ts +7 -7
  496. package/dist/utils/index.d.ts.map +1 -1
  497. package/dist/utils/index.js +4 -4
  498. package/dist/utils/index.js.map +1 -1
  499. package/dist/utils/time.d.ts.map +1 -1
  500. package/dist/utils/time.js +10 -10
  501. package/dist/utils/time.js.map +1 -1
  502. package/package.json +28 -5
  503. package/plugin/.claude-plugin/marketplace.json +17 -0
  504. package/plugin/.claude-plugin/plugin.json +5 -0
  505. package/plugin/plugins/kspec/skills/create-workflow/SKILL.md +235 -0
  506. package/plugin/plugins/kspec/skills/help/SKILL.md +42 -0
  507. package/plugin/plugins/kspec/skills/observations/SKILL.md +143 -0
  508. package/plugin/plugins/kspec/skills/plan/SKILL.md +343 -0
  509. package/plugin/plugins/kspec/skills/reflect/SKILL.md +161 -0
  510. package/plugin/plugins/kspec/skills/review/SKILL.md +193 -0
  511. package/plugin/plugins/kspec/skills/task-work/SKILL.md +303 -0
  512. package/plugin/plugins/kspec/skills/triage/SKILL.md +206 -0
  513. package/plugin/plugins/kspec/skills/triage/docs/automation.md +120 -0
  514. package/plugin/plugins/kspec/skills/triage/docs/inbox.md +144 -0
  515. package/plugin/plugins/kspec/skills/triage/docs/observations.md +85 -0
  516. package/plugin/plugins/kspec/skills/triage-automation/SKILL.md +140 -0
  517. package/plugin/plugins/kspec/skills/triage-inbox/SKILL.md +232 -0
  518. package/plugin/plugins/kspec/skills/writing-specs/SKILL.md +340 -0
  519. package/templates/agents-sections/01-quick-start.md +22 -0
  520. package/templates/agents-sections/02-shadow-branch.md +34 -0
  521. package/templates/agents-sections/03-task-lifecycle.md +48 -0
  522. package/templates/agents-sections/04-pr-workflow.md +17 -0
  523. package/templates/agents-sections/05-commit-convention.md +27 -0
  524. package/templates/agents-sections/06-ralph-loop.md +45 -0
  525. package/templates/hooks/pre-commit +34 -0
  526. package/templates/skills/create-workflow/SKILL.md +228 -0
  527. package/templates/skills/help/SKILL.md +37 -0
  528. package/templates/skills/manifest.yaml +60 -0
  529. package/templates/skills/observations/SKILL.md +137 -0
  530. package/templates/skills/plan/SKILL.md +336 -0
  531. package/templates/skills/reflect/SKILL.md +155 -0
  532. package/templates/skills/review/SKILL.md +186 -0
  533. package/templates/skills/task-work/SKILL.md +296 -0
  534. package/templates/skills/triage/SKILL.md +199 -0
  535. package/templates/skills/triage/docs/automation.md +120 -0
  536. package/templates/skills/triage/docs/inbox.md +144 -0
  537. package/templates/skills/triage/docs/observations.md +85 -0
  538. package/templates/skills/triage-automation/SKILL.md +134 -0
  539. package/templates/skills/triage-inbox/SKILL.md +225 -0
  540. package/templates/skills/writing-specs/SKILL.md +333 -0
@@ -11,15 +11,15 @@
11
11
  * session.yaml # Metadata
12
12
  * events.jsonl # Append-only event log
13
13
  */
14
- import * as fs from 'node:fs';
15
- import * as fsPromises from 'node:fs/promises';
16
- import * as path from 'node:path';
17
- import * as YAML from 'yaml';
18
- import { SessionMetadataSchema, SessionEventSchema, } from './types.js';
14
+ import * as fs from "node:fs";
15
+ import * as fsPromises from "node:fs/promises";
16
+ import * as path from "node:path";
17
+ import * as YAML from "yaml";
18
+ import { SessionEventSchema, SessionMetadataSchema, } from "./types.js";
19
19
  // ─── Constants ───────────────────────────────────────────────────────────────
20
- const SESSIONS_DIR = 'sessions';
21
- const METADATA_FILE = 'session.yaml';
22
- const EVENTS_FILE = 'events.jsonl';
20
+ const SESSIONS_DIR = "sessions";
21
+ const METADATA_FILE = "session.yaml";
22
+ const EVENTS_FILE = "events.jsonl";
23
23
  // ─── Path Helpers ────────────────────────────────────────────────────────────
24
24
  /**
25
25
  * Get the sessions directory path within a spec directory.
@@ -72,14 +72,18 @@ export async function createSession(specDir, input) {
72
72
  id: input.id,
73
73
  task_id: input.task_id,
74
74
  agent_type: input.agent_type,
75
- status: input.status ?? 'active',
75
+ status: input.status ?? "active",
76
76
  started_at: input.started_at ?? new Date().toISOString(),
77
77
  ended_at: undefined,
78
78
  };
79
79
  // Validate and write metadata
80
80
  const validated = SessionMetadataSchema.parse(metadata);
81
- const content = YAML.stringify(validated, { indent: 2, lineWidth: 100, sortMapEntries: false });
82
- await fsPromises.writeFile(metadataPath, content, 'utf-8');
81
+ const content = YAML.stringify(validated, {
82
+ indent: 2,
83
+ lineWidth: 100,
84
+ sortMapEntries: false,
85
+ });
86
+ await fsPromises.writeFile(metadataPath, content, "utf-8");
83
87
  return validated;
84
88
  }
85
89
  /**
@@ -92,7 +96,7 @@ export async function createSession(specDir, input) {
92
96
  export async function getSession(specDir, sessionId) {
93
97
  const metadataPath = getSessionMetadataPath(specDir, sessionId);
94
98
  try {
95
- const content = await fsPromises.readFile(metadataPath, 'utf-8');
99
+ const content = await fsPromises.readFile(metadataPath, "utf-8");
96
100
  const raw = YAML.parse(content);
97
101
  return SessionMetadataSchema.parse(raw);
98
102
  }
@@ -119,11 +123,15 @@ export async function updateSessionStatus(specDir, sessionId, status) {
119
123
  const updated = {
120
124
  ...metadata,
121
125
  status,
122
- ended_at: status !== 'active' ? new Date().toISOString() : metadata.ended_at,
126
+ ended_at: status !== "active" ? new Date().toISOString() : metadata.ended_at,
123
127
  };
124
128
  const metadataPath = getSessionMetadataPath(specDir, sessionId);
125
- const content = YAML.stringify(updated, { indent: 2, lineWidth: 100, sortMapEntries: false });
126
- await fsPromises.writeFile(metadataPath, content, 'utf-8');
129
+ const content = YAML.stringify(updated, {
130
+ indent: 2,
131
+ lineWidth: 100,
132
+ sortMapEntries: false,
133
+ });
134
+ await fsPromises.writeFile(metadataPath, content, "utf-8");
127
135
  return updated;
128
136
  }
129
137
  /**
@@ -135,10 +143,10 @@ export async function updateSessionStatus(specDir, sessionId, status) {
135
143
  export async function listSessions(specDir) {
136
144
  const sessionsDir = getSessionsDir(specDir);
137
145
  try {
138
- const entries = await fsPromises.readdir(sessionsDir, { withFileTypes: true });
139
- return entries
140
- .filter(e => e.isDirectory())
141
- .map(e => e.name);
146
+ const entries = await fsPromises.readdir(sessionsDir, {
147
+ withFileTypes: true,
148
+ });
149
+ return entries.filter((e) => e.isDirectory()).map((e) => e.name);
142
150
  }
143
151
  catch {
144
152
  return [];
@@ -168,8 +176,11 @@ export async function sessionExists(specDir, sessionId) {
168
176
  async function getEventCount(specDir, sessionId) {
169
177
  const eventsPath = getSessionEventsPath(specDir, sessionId);
170
178
  try {
171
- const content = await fsPromises.readFile(eventsPath, 'utf-8');
172
- const lines = content.trim().split('\n').filter(line => line.length > 0);
179
+ const content = await fsPromises.readFile(eventsPath, "utf-8");
180
+ const lines = content
181
+ .trim()
182
+ .split("\n")
183
+ .filter((line) => line.length > 0);
173
184
  return lines.length;
174
185
  }
175
186
  catch {
@@ -200,7 +211,7 @@ export async function appendEvent(specDir, input) {
200
211
  // Ensure session directory exists (lazy creation)
201
212
  await fsPromises.mkdir(sessionDir, { recursive: true });
202
213
  // Get current event count for seq assignment
203
- const seq = input.seq ?? await getEventCount(specDir, input.session_id);
214
+ const seq = input.seq ?? (await getEventCount(specDir, input.session_id));
204
215
  // Build full event
205
216
  const event = {
206
217
  ts: input.ts ?? Date.now(),
@@ -212,10 +223,10 @@ export async function appendEvent(specDir, input) {
212
223
  };
213
224
  // Validate event
214
225
  const validated = SessionEventSchema.parse(event);
215
- // AC-3: Use synchronous append for crash safety
226
+ // AC: @session-events ac-3 - Use synchronous append for crash safety
216
227
  // This ensures the line is fully written before returning
217
- const line = JSON.stringify(validated) + '\n';
218
- fs.appendFileSync(eventsPath, line, 'utf-8');
228
+ const line = `${JSON.stringify(validated)}\n`;
229
+ fs.appendFileSync(eventsPath, line, "utf-8");
219
230
  return validated;
220
231
  }
221
232
  /**
@@ -230,8 +241,11 @@ export async function appendEvent(specDir, input) {
230
241
  export async function readEvents(specDir, sessionId) {
231
242
  const eventsPath = getSessionEventsPath(specDir, sessionId);
232
243
  try {
233
- const content = await fsPromises.readFile(eventsPath, 'utf-8');
234
- const lines = content.trim().split('\n').filter(line => line.length > 0);
244
+ const content = await fsPromises.readFile(eventsPath, "utf-8");
245
+ const lines = content
246
+ .trim()
247
+ .split("\n")
248
+ .filter((line) => line.length > 0);
235
249
  const events = [];
236
250
  for (const line of lines) {
237
251
  try {
@@ -243,13 +257,59 @@ export async function readEvents(specDir, sessionId) {
243
257
  // Skip invalid lines
244
258
  }
245
259
  }
246
- // AC-4: Sort by sequence number
260
+ // AC: @session-events ac-4 - Sort by sequence number
247
261
  return events.sort((a, b) => a.seq - b.seq);
248
262
  }
249
263
  catch {
250
264
  return [];
251
265
  }
252
266
  }
267
+ /**
268
+ * Deduplicate phased tool_call events.
269
+ *
270
+ * ACP SDK 0.14+ sends tool calls in two phases: first with empty rawInput,
271
+ * then with populated rawInput. This merges them by keeping only the version
272
+ * with populated rawInput per toolCallId.
273
+ */
274
+ export function deduplicatePhasedToolCalls(events) {
275
+ // First pass: find toolCallIds that have a populated rawInput version
276
+ const populatedToolCalls = new Map(); // toolCallId → index
277
+ for (let i = 0; i < events.length; i++) {
278
+ const event = events[i];
279
+ if (event.type !== "session.update")
280
+ continue;
281
+ const data = event.data;
282
+ const update = data?.update;
283
+ if (update?.sessionUpdate !== "tool_call")
284
+ continue;
285
+ const toolCallId = update.toolCallId || update.tool_call_id || update.id;
286
+ if (!toolCallId)
287
+ continue;
288
+ const rawInput = update.rawInput;
289
+ const hasContent = rawInput && Object.keys(rawInput).length > 0;
290
+ if (hasContent) {
291
+ populatedToolCalls.set(toolCallId, i);
292
+ }
293
+ else if (!populatedToolCalls.has(toolCallId)) {
294
+ // First (empty) version - track it in case no populated version exists
295
+ populatedToolCalls.set(toolCallId, i);
296
+ }
297
+ }
298
+ // Second pass: keep only the best version per toolCallId
299
+ return events.filter((event, i) => {
300
+ if (event.type !== "session.update")
301
+ return true;
302
+ const data = event.data;
303
+ const update = data?.update;
304
+ if (update?.sessionUpdate !== "tool_call")
305
+ return true;
306
+ const toolCallId = update.toolCallId || update.tool_call_id || update.id;
307
+ if (!toolCallId)
308
+ return true;
309
+ // Keep this event only if it's the best version (populated or only version)
310
+ return populatedToolCalls.get(toolCallId) === i;
311
+ });
312
+ }
253
313
  /**
254
314
  * Read events within a time range.
255
315
  *
@@ -261,7 +321,7 @@ export async function readEvents(specDir, sessionId) {
261
321
  */
262
322
  export async function readEventsSince(specDir, sessionId, since, until) {
263
323
  const events = await readEvents(specDir, sessionId);
264
- return events.filter(e => {
324
+ return events.filter((e) => {
265
325
  if (e.ts < since)
266
326
  return false;
267
327
  if (until !== undefined && e.ts > until)
@@ -283,6 +343,121 @@ export async function getLastEvent(specDir, sessionId) {
283
343
  }
284
344
  return events[events.length - 1];
285
345
  }
346
+ /**
347
+ * Count lines in events.jsonl without parsing JSON.
348
+ * Much faster than readEvents() for large files.
349
+ */
350
+ async function countEventLines(specDir, sessionId) {
351
+ const eventsPath = getSessionEventsPath(specDir, sessionId);
352
+ try {
353
+ const content = await fsPromises.readFile(eventsPath, "utf-8");
354
+ if (!content.trim())
355
+ return 0;
356
+ return content.trim().split("\n").length;
357
+ }
358
+ catch {
359
+ return 0;
360
+ }
361
+ }
362
+ /**
363
+ * Count context-iter-*.json files for a session (iteration count).
364
+ */
365
+ async function countIterations(specDir, sessionId) {
366
+ const sessionDir = getSessionDir(specDir, sessionId);
367
+ try {
368
+ const entries = await fsPromises.readdir(sessionDir);
369
+ return entries.filter((e) => e.startsWith("context-iter-") && e.endsWith(".json")).length;
370
+ }
371
+ catch {
372
+ return 0;
373
+ }
374
+ }
375
+ /**
376
+ * Count task completions by scanning events for tool calls that invoke
377
+ * `kspec task complete` or `npm run dev -- task complete`.
378
+ *
379
+ * Real sessions record task completions as session.update events with
380
+ * sessionUpdate: "tool_call" and rawInput.command containing the complete command.
381
+ * We use a fast substring check before JSON parsing for performance.
382
+ */
383
+ async function countTaskCompletions(specDir, sessionId) {
384
+ const eventsPath = getSessionEventsPath(specDir, sessionId);
385
+ try {
386
+ const content = await fsPromises.readFile(eventsPath, "utf-8");
387
+ if (!content.trim())
388
+ return 0;
389
+ const lines = content.trim().split("\n");
390
+ let count = 0;
391
+ for (const line of lines) {
392
+ // Quick substring pre-filter: only parse lines that might contain task complete commands
393
+ if (!line.includes("task complete"))
394
+ continue;
395
+ // Must be a tool_call event (session.update with sessionUpdate: "tool_call")
396
+ if (!line.includes('"tool_call"'))
397
+ continue;
398
+ try {
399
+ const event = JSON.parse(line);
400
+ const command = event?.data?.update?.rawInput?.command;
401
+ if (typeof command === "string" && /\btask complete\b/.test(command)) {
402
+ count++;
403
+ }
404
+ }
405
+ catch {
406
+ // Skip unparseable lines
407
+ }
408
+ }
409
+ return count;
410
+ }
411
+ catch {
412
+ return 0;
413
+ }
414
+ }
415
+ /**
416
+ * Get a summary of a single session for list display.
417
+ *
418
+ * Gathers metadata and computes metrics lazily (only parses what's needed).
419
+ *
420
+ * @param specDir - The .kspec directory path
421
+ * @param sessionId - Session ID
422
+ * @returns Session summary or null if session doesn't exist
423
+ */
424
+ export async function getSessionLogSummary(specDir, sessionId) {
425
+ const metadata = await getSession(specDir, sessionId);
426
+ if (!metadata)
427
+ return null;
428
+ const [eventCount, iterationCount, tasksCompleted] = await Promise.all([
429
+ countEventLines(specDir, sessionId),
430
+ countIterationsBoundaryAware(specDir, sessionId),
431
+ countTaskCompletions(specDir, sessionId),
432
+ ]);
433
+ const startMs = new Date(metadata.started_at).getTime();
434
+ const endMs = metadata.ended_at
435
+ ? new Date(metadata.ended_at).getTime()
436
+ : Date.now();
437
+ const durationMs = endMs - startMs;
438
+ return {
439
+ id: metadata.id,
440
+ status: metadata.status,
441
+ agent_type: metadata.agent_type,
442
+ started_at: metadata.started_at,
443
+ ended_at: metadata.ended_at,
444
+ duration_ms: durationMs,
445
+ event_count: eventCount,
446
+ iteration_count: iterationCount,
447
+ tasks_completed: tasksCompleted,
448
+ };
449
+ }
450
+ /**
451
+ * Get summaries for all sessions.
452
+ *
453
+ * @param specDir - The .kspec directory path
454
+ * @returns Array of session summaries
455
+ */
456
+ export async function getAllSessionLogSummaries(specDir) {
457
+ const sessionIds = await listSessions(specDir);
458
+ const summaries = await Promise.all(sessionIds.map((id) => getSessionLogSummary(specDir, id)));
459
+ return summaries.filter((s) => s !== null);
460
+ }
286
461
  // ─── Context Snapshots ───────────────────────────────────────────────────────
287
462
  /**
288
463
  * Save session context snapshot for a given iteration.
@@ -302,7 +477,7 @@ export async function saveSessionContext(specDir, sessionId, iteration, context)
302
477
  await fsPromises.mkdir(sessionDir, { recursive: true });
303
478
  // Write context snapshot as pretty JSON
304
479
  const content = JSON.stringify(context, null, 2);
305
- await fsPromises.writeFile(contextPath, content, 'utf-8');
480
+ await fsPromises.writeFile(contextPath, content, "utf-8");
306
481
  }
307
482
  /**
308
483
  * Read session context snapshot for a given iteration.
@@ -315,11 +490,615 @@ export async function saveSessionContext(specDir, sessionId, iteration, context)
315
490
  export async function readSessionContext(specDir, sessionId, iteration) {
316
491
  const contextPath = getSessionContextPath(specDir, sessionId, iteration);
317
492
  try {
318
- const content = await fsPromises.readFile(contextPath, 'utf-8');
493
+ const content = await fsPromises.readFile(contextPath, "utf-8");
319
494
  return JSON.parse(content);
320
495
  }
321
496
  catch {
322
497
  return null;
323
498
  }
324
499
  }
500
+ /**
501
+ * Resolve a session ID or prefix to a full session ID.
502
+ *
503
+ * AC: @session-log-show ac-7, ac-8, ac-9
504
+ *
505
+ * @param specDir - The .kspec directory path
506
+ * @param idOrPrefix - Full session ID or prefix (e.g., first 8 chars)
507
+ * @returns Resolution result
508
+ */
509
+ export async function resolveSessionId(specDir, idOrPrefix) {
510
+ const sessionIds = await listSessions(specDir);
511
+ // First, try exact match
512
+ if (sessionIds.includes(idOrPrefix)) {
513
+ return { ok: true, id: idOrPrefix };
514
+ }
515
+ // Try prefix match
516
+ const matches = sessionIds.filter((id) => id.startsWith(idOrPrefix));
517
+ if (matches.length === 0) {
518
+ return { ok: false, error: "not_found" };
519
+ }
520
+ if (matches.length === 1) {
521
+ return { ok: true, id: matches[0] };
522
+ }
523
+ // Ambiguous - multiple matches
524
+ return { ok: false, error: "ambiguous", matches };
525
+ }
526
+ /**
527
+ * Get iteration number from a context snapshot file.
528
+ */
529
+ async function getIterationNumbers(specDir, sessionId) {
530
+ const sessionDir = getSessionDir(specDir, sessionId);
531
+ try {
532
+ const entries = await fsPromises.readdir(sessionDir);
533
+ const iterations = [];
534
+ for (const entry of entries) {
535
+ const match = entry.match(/^context-iter-(\d+)\.json$/);
536
+ if (match) {
537
+ iterations.push(parseInt(match[1], 10));
538
+ }
539
+ }
540
+ return iterations.sort((a, b) => a - b);
541
+ }
542
+ catch {
543
+ return [];
544
+ }
545
+ }
546
+ /**
547
+ * Extract task refs from task commands in event data.
548
+ */
549
+ function extractTaskRef(command) {
550
+ // Match patterns like: kspec task start @ref, kspec task complete @ref
551
+ const match = command.match(/@[\w-]+/);
552
+ return match ? match[0] : null;
553
+ }
554
+ /**
555
+ * Find iteration boundaries from prompt.sent events with phase "task-work".
556
+ *
557
+ * Ralph emits these synchronously at the start of each iteration, so their
558
+ * array positions are reliable even when concurrent fire-and-forget events
559
+ * produce duplicate seq numbers.
560
+ *
561
+ * Returns validated, monotonically increasing boundaries.
562
+ */
563
+ function findIterationBoundaries(events) {
564
+ const raw = [];
565
+ for (let i = 0; i < events.length; i++) {
566
+ const event = events[i];
567
+ if (event.type !== "prompt.sent")
568
+ continue;
569
+ const data = event.data;
570
+ if (data?.phase !== "task-work" ||
571
+ typeof data?.iteration !== "number") {
572
+ continue;
573
+ }
574
+ raw.push({ index: i, iteration: data.iteration });
575
+ }
576
+ // Validate: filter to monotonically increasing iteration numbers, deduplicate
577
+ const validated = [];
578
+ let lastIter = -Infinity;
579
+ for (const b of raw) {
580
+ if (b.iteration > lastIter) {
581
+ validated.push(b);
582
+ lastIter = b.iteration;
583
+ }
584
+ }
585
+ return validated;
586
+ }
587
+ /**
588
+ * Extract task start/complete refs from a slice of events.
589
+ */
590
+ function extractTaskTransitions(events) {
591
+ const tasksStarted = [];
592
+ const tasksCompleted = [];
593
+ for (const event of events) {
594
+ if (event.type === "session.update") {
595
+ const data = event.data;
596
+ const command = data?.update?.rawInput?.command;
597
+ if (typeof command === "string") {
598
+ if (/\btask start\b/.test(command)) {
599
+ const ref = extractTaskRef(command);
600
+ if (ref)
601
+ tasksStarted.push(ref);
602
+ }
603
+ else if (/\btask complete\b/.test(command)) {
604
+ const ref = extractTaskRef(command);
605
+ if (ref)
606
+ tasksCompleted.push(ref);
607
+ }
608
+ }
609
+ }
610
+ }
611
+ return { tasksStarted, tasksCompleted };
612
+ }
613
+ /**
614
+ * Legacy iteration grouping: groups events by their data.iteration field.
615
+ *
616
+ * Used as fallback for sessions that don't have prompt.sent boundary events
617
+ * with phase "task-work" (pre-boundary sessions or non-ralph sessions).
618
+ *
619
+ * AC: @session-log-show ac-2
620
+ */
621
+ function legacyIterationGrouping(events, snapshotIterations) {
622
+ // Collect all iteration numbers from both snapshots and events
623
+ const allIterations = new Set(snapshotIterations);
624
+ for (const event of events) {
625
+ const data = event.data;
626
+ if (typeof data?.iteration === "number") {
627
+ allIterations.add(data.iteration);
628
+ }
629
+ }
630
+ // If no iterations found anywhere, synthesize iteration-0 only if events exist
631
+ if (allIterations.size === 0) {
632
+ if (events.length === 0) {
633
+ return [];
634
+ }
635
+ const { tasksStarted, tasksCompleted } = extractTaskTransitions(events);
636
+ return [
637
+ {
638
+ iteration: 0,
639
+ event_count: events.length,
640
+ tasks_started: tasksStarted,
641
+ tasks_completed: tasksCompleted,
642
+ },
643
+ ];
644
+ }
645
+ // Create buckets for all known iterations
646
+ const iterations = Array.from(allIterations).sort((a, b) => a - b);
647
+ const iterationMap = new Map();
648
+ for (const n of iterations) {
649
+ iterationMap.set(n, []);
650
+ }
651
+ for (const event of events) {
652
+ const data = event.data;
653
+ const iter = data?.iteration;
654
+ if (typeof iter === "number" && iterationMap.has(iter)) {
655
+ iterationMap.get(iter).push(event);
656
+ }
657
+ else {
658
+ const fallbackIter = iterationMap.has(0) ? 0 : iterations[0];
659
+ iterationMap.get(fallbackIter).push(event);
660
+ }
661
+ }
662
+ const summaries = [];
663
+ for (const [iterNum, iterEvents] of iterationMap) {
664
+ const { tasksStarted, tasksCompleted } = extractTaskTransitions(iterEvents);
665
+ summaries.push({
666
+ iteration: iterNum,
667
+ event_count: iterEvents.length,
668
+ tasks_started: tasksStarted,
669
+ tasks_completed: tasksCompleted,
670
+ });
671
+ }
672
+ return summaries.sort((a, b) => a.iteration - b.iteration);
673
+ }
674
+ /**
675
+ * Boundary-based iteration grouping: splits events by prompt.sent boundary
676
+ * positions (array indices) instead of trusting data.iteration fields.
677
+ *
678
+ * This is resilient to producer-side bugs where concurrent fire-and-forget
679
+ * event logging captures the wrong iteration number.
680
+ *
681
+ * AC: @session-log-show ac-10
682
+ */
683
+ function boundaryIterationGrouping(events, boundaries) {
684
+ const summaries = [];
685
+ for (let b = 0; b < boundaries.length; b++) {
686
+ const startIdx = boundaries[b].index;
687
+ const endIdx = b + 1 < boundaries.length ? boundaries[b + 1].index : events.length;
688
+ const iterEvents = events.slice(startIdx, endIdx);
689
+ const { tasksStarted, tasksCompleted } = extractTaskTransitions(iterEvents);
690
+ summaries.push({
691
+ iteration: boundaries[b].iteration,
692
+ event_count: iterEvents.length,
693
+ tasks_started: tasksStarted,
694
+ tasks_completed: tasksCompleted,
695
+ });
696
+ }
697
+ // Pre-boundary events (before the first prompt.sent) merge into first iteration
698
+ if (boundaries.length > 0 && boundaries[0].index > 0) {
699
+ const preBoundaryEvents = events.slice(0, boundaries[0].index);
700
+ const { tasksStarted, tasksCompleted } = extractTaskTransitions(preBoundaryEvents);
701
+ summaries[0].event_count += preBoundaryEvents.length;
702
+ summaries[0].tasks_started = [...tasksStarted, ...summaries[0].tasks_started];
703
+ summaries[0].tasks_completed = [...tasksCompleted, ...summaries[0].tasks_completed];
704
+ }
705
+ return summaries;
706
+ }
707
+ /**
708
+ * Compute per-iteration summaries from events.
709
+ *
710
+ * Uses prompt.sent boundary events (phase: "task-work") when available for
711
+ * accurate index-based grouping. Falls back to legacy data.iteration grouping
712
+ * for sessions without boundaries.
713
+ *
714
+ * AC: @session-log-show ac-2, ac-10
715
+ */
716
+ async function computeIterationSummaries(specDir, sessionId) {
717
+ const events = await readEvents(specDir, sessionId);
718
+ const boundaries = findIterationBoundaries(events);
719
+ if (boundaries.length > 0) {
720
+ return boundaryIterationGrouping(events, boundaries);
721
+ }
722
+ // Legacy fallback: no prompt.sent boundaries with phase "task-work"
723
+ const snapshotIterations = await getIterationNumbers(specDir, sessionId);
724
+ return legacyIterationGrouping(events, snapshotIterations);
725
+ }
726
+ /**
727
+ * Count iterations using boundary-aware logic, without computing full summaries.
728
+ *
729
+ * For use in getSessionLogSummary() (session log list, session log stats) to
730
+ * ensure iteration_count agrees with session log show.
731
+ *
732
+ * Falls back to counting context-iter-*.json files when no boundaries exist.
733
+ */
734
+ async function countIterationsBoundaryAware(specDir, sessionId) {
735
+ const events = await readEvents(specDir, sessionId);
736
+ const boundaries = findIterationBoundaries(events);
737
+ if (boundaries.length > 0) {
738
+ return boundaries.length;
739
+ }
740
+ // Legacy fallback: count from context snapshots and event data
741
+ const snapshotIterations = await getIterationNumbers(specDir, sessionId);
742
+ const allIterations = new Set(snapshotIterations);
743
+ for (const event of events) {
744
+ const data = event.data;
745
+ if (typeof data?.iteration === "number") {
746
+ allIterations.add(data.iteration);
747
+ }
748
+ }
749
+ return allIterations.size || (events.length > 0 ? 1 : 0);
750
+ }
751
+ /**
752
+ * Get full session detail for session log show.
753
+ *
754
+ * @param specDir - The .kspec directory path
755
+ * @param sessionId - Session ID (must be resolved first)
756
+ * @returns Session detail or null if not found
757
+ */
758
+ export async function getSessionLogDetail(specDir, sessionId) {
759
+ const metadata = await getSession(specDir, sessionId);
760
+ if (!metadata)
761
+ return null;
762
+ const [eventCount, iterations] = await Promise.all([
763
+ countEventLines(specDir, sessionId),
764
+ computeIterationSummaries(specDir, sessionId),
765
+ ]);
766
+ const startMs = new Date(metadata.started_at).getTime();
767
+ const endMs = metadata.ended_at
768
+ ? new Date(metadata.ended_at).getTime()
769
+ : Date.now();
770
+ const durationMs = endMs - startMs;
771
+ return {
772
+ id: metadata.id,
773
+ status: metadata.status,
774
+ agent_type: metadata.agent_type,
775
+ task_id: metadata.task_id,
776
+ started_at: metadata.started_at,
777
+ ended_at: metadata.ended_at,
778
+ duration_ms: durationMs,
779
+ event_count: eventCount,
780
+ iteration_count: iterations.length,
781
+ iterations,
782
+ };
783
+ }
784
+ /**
785
+ * Compute aggregate statistics from session summaries.
786
+ *
787
+ * @param summaries - Array of session log summaries
788
+ * @returns Aggregate statistics
789
+ */
790
+ export function computeSessionLogStats(summaries) {
791
+ if (summaries.length === 0) {
792
+ return {
793
+ total_sessions: 0,
794
+ total_events: 0,
795
+ total_iterations: 0,
796
+ total_tasks_completed: 0,
797
+ total_duration_ms: 0,
798
+ avg_duration_ms: 0,
799
+ avg_iterations_per_session: 0,
800
+ avg_tasks_per_session: 0,
801
+ status_breakdown: [],
802
+ };
803
+ }
804
+ // Compute totals
805
+ let totalEvents = 0;
806
+ let totalIterations = 0;
807
+ let totalTasksCompleted = 0;
808
+ let totalDuration = 0;
809
+ const statusCounts = {
810
+ active: 0,
811
+ completed: 0,
812
+ abandoned: 0,
813
+ };
814
+ for (const s of summaries) {
815
+ totalEvents += s.event_count;
816
+ totalIterations += s.iteration_count;
817
+ totalTasksCompleted += s.tasks_completed;
818
+ totalDuration += s.duration_ms;
819
+ statusCounts[s.status] = (statusCounts[s.status] || 0) + 1;
820
+ }
821
+ const n = summaries.length;
822
+ // Build status breakdown
823
+ const statusBreakdown = [];
824
+ for (const status of ["completed", "active", "abandoned"]) {
825
+ const count = statusCounts[status] || 0;
826
+ if (count > 0) {
827
+ statusBreakdown.push({
828
+ status,
829
+ count,
830
+ percentage: Math.round((count / n) * 100 * 10) / 10, // 1 decimal place
831
+ });
832
+ }
833
+ }
834
+ return {
835
+ total_sessions: n,
836
+ total_events: totalEvents,
837
+ total_iterations: totalIterations,
838
+ total_tasks_completed: totalTasksCompleted,
839
+ total_duration_ms: totalDuration,
840
+ avg_duration_ms: Math.round(totalDuration / n),
841
+ avg_iterations_per_session: Math.round((totalIterations / n) * 10) / 10,
842
+ avg_tasks_per_session: Math.round((totalTasksCompleted / n) * 10) / 10,
843
+ status_breakdown: statusBreakdown,
844
+ };
845
+ }
846
+ /**
847
+ * Count tool calls by scanning events.jsonl for tool_call events.
848
+ *
849
+ * This is relatively expensive as it parses all events, so only call
850
+ * when --tool-usage is requested.
851
+ *
852
+ * AC: @session-log-stats ac-6
853
+ */
854
+ export async function computeToolUsageStats(specDir, sessionIds, limit = 10) {
855
+ const toolCounts = {};
856
+ let totalToolCalls = 0;
857
+ for (const sessionId of sessionIds) {
858
+ const eventsPath = getSessionEventsPath(specDir, sessionId);
859
+ try {
860
+ const content = await fsPromises.readFile(eventsPath, "utf-8");
861
+ if (!content.trim())
862
+ continue;
863
+ const lines = content.trim().split("\n");
864
+ // Track seen toolCallIds to deduplicate phased events
865
+ const seenToolCallIds = new Set();
866
+ for (const line of lines) {
867
+ // Quick pre-filter: only parse lines that might be tool_call events
868
+ if (!line.includes('"tool_call"'))
869
+ continue;
870
+ try {
871
+ const event = JSON.parse(line);
872
+ if (event?.type === "session.update") {
873
+ const update = event?.data?.update;
874
+ if (update?.sessionUpdate === "tool_call") {
875
+ // Deduplicate phased tool_call events by toolCallId
876
+ const toolCallId = update?.toolCallId || update?.tool_call_id || update?.id;
877
+ if (toolCallId && seenToolCallIds.has(toolCallId))
878
+ continue;
879
+ if (toolCallId)
880
+ seenToolCallIds.add(toolCallId);
881
+ const toolName = update?._meta?.claudeCode?.toolName || "unknown";
882
+ toolCounts[toolName] = (toolCounts[toolName] || 0) + 1;
883
+ totalToolCalls++;
884
+ }
885
+ }
886
+ }
887
+ catch {
888
+ // Skip unparseable lines
889
+ }
890
+ }
891
+ }
892
+ catch {
893
+ // Skip sessions without events
894
+ }
895
+ }
896
+ // Sort by count descending, take top N
897
+ const sorted = Object.entries(toolCounts)
898
+ .map(([tool_name, count]) => ({
899
+ tool_name,
900
+ count,
901
+ percentage: totalToolCalls > 0
902
+ ? Math.round((count / totalToolCalls) * 100 * 10) / 10
903
+ : 0,
904
+ }))
905
+ .sort((a, b) => b.count - a.count)
906
+ .slice(0, limit);
907
+ return sorted;
908
+ }
909
+ /**
910
+ * Compute ISO-8601 week number and week-year in UTC.
911
+ *
912
+ * ISO-8601 rules:
913
+ * - Week 1 contains the first Thursday of the year
914
+ * - Week starts on Monday
915
+ * - Week-year may differ from calendar year at year boundaries
916
+ *
917
+ * Returns [weekYear, weekNumber] tuple.
918
+ */
919
+ function getISOWeekUTC(date) {
920
+ // Clone and normalize to UTC midnight
921
+ const d = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
922
+ // Get day of week (ISO: Monday=1, Sunday=7)
923
+ const dayOfWeek = d.getUTCDay() || 7;
924
+ // Set to nearest Thursday (determines week-year)
925
+ d.setUTCDate(d.getUTCDate() + 4 - dayOfWeek);
926
+ // Week-year is the year of this Thursday
927
+ const weekYear = d.getUTCFullYear();
928
+ // January 4 is always in week 1 (as it's in the first week with a Thursday)
929
+ const jan4 = new Date(Date.UTC(weekYear, 0, 4));
930
+ const jan4DayOfWeek = jan4.getUTCDay() || 7;
931
+ // Start of week 1 (Monday before or on Jan 4)
932
+ const week1Start = new Date(jan4);
933
+ week1Start.setUTCDate(jan4.getUTCDate() - (jan4DayOfWeek - 1));
934
+ // Week number = weeks between week 1 start and the Thursday we found
935
+ const weekNum = Math.floor((d.getTime() - week1Start.getTime()) / (7 * 24 * 60 * 60 * 1000)) + 1;
936
+ return [weekYear, weekNum];
937
+ }
938
+ /**
939
+ * Group sessions by time period (day or week).
940
+ *
941
+ * AC: @session-log-stats ac-7
942
+ */
943
+ export function computeTimePeriodStats(summaries, groupBy) {
944
+ const buckets = {};
945
+ for (const s of summaries) {
946
+ const date = new Date(s.started_at);
947
+ let period;
948
+ if (groupBy === "day") {
949
+ period = date.toISOString().split("T")[0]; // YYYY-MM-DD in UTC
950
+ }
951
+ else {
952
+ // Week: use ISO-8601 week format (YYYY-Www) in UTC
953
+ const [weekYear, weekNum] = getISOWeekUTC(date);
954
+ period = `${weekYear}-W${weekNum.toString().padStart(2, "0")}`;
955
+ }
956
+ if (!buckets[period]) {
957
+ buckets[period] = { sessions: 0, tasks: 0, duration: 0 };
958
+ }
959
+ buckets[period].sessions += 1;
960
+ buckets[period].tasks += s.tasks_completed;
961
+ buckets[period].duration += s.duration_ms;
962
+ }
963
+ // Convert to array and sort by period (newest first for display)
964
+ const result = Object.entries(buckets)
965
+ .map(([period, data]) => ({
966
+ period,
967
+ sessions_count: data.sessions,
968
+ tasks_completed: data.tasks,
969
+ total_duration_ms: data.duration,
970
+ }))
971
+ .sort((a, b) => b.period.localeCompare(a.period));
972
+ return result;
973
+ }
974
+ /**
975
+ * Extract a content excerpt around a match, limited to maxLength chars.
976
+ *
977
+ * AC: @session-log-search ac-4
978
+ */
979
+ function extractContentExcerpt(data, pattern, maxLength = 200) {
980
+ // Stringify the data for searching
981
+ const str = JSON.stringify(data);
982
+ const lowerStr = str.toLowerCase();
983
+ const lowerPattern = pattern.toLowerCase();
984
+ const matchIndex = lowerStr.indexOf(lowerPattern);
985
+ if (matchIndex === -1) {
986
+ // Shouldn't happen since we pre-filtered, but return start of content
987
+ return str.length > maxLength ? str.slice(0, maxLength - 3) + "..." : str;
988
+ }
989
+ // Calculate excerpt window centered on match
990
+ const matchLen = pattern.length;
991
+ const contextBefore = Math.floor((maxLength - matchLen) / 2);
992
+ const start = Math.max(0, matchIndex - contextBefore);
993
+ const end = Math.min(str.length, start + maxLength);
994
+ let excerpt = str.slice(start, end);
995
+ // Add ellipsis indicators
996
+ if (start > 0) {
997
+ excerpt = "..." + excerpt.slice(3);
998
+ }
999
+ if (end < str.length) {
1000
+ excerpt = excerpt.slice(0, -3) + "...";
1001
+ }
1002
+ return excerpt;
1003
+ }
1004
+ /**
1005
+ * Search across session events for a pattern.
1006
+ *
1007
+ * This streams through events.jsonl files, applying filters as early as possible
1008
+ * for performance. Sessions are pre-filtered by metadata (--since, --agent) before
1009
+ * scanning events to reduce I/O.
1010
+ *
1011
+ * AC: @session-log-search ac-1, ac-2, ac-3, ac-5, ac-7
1012
+ *
1013
+ * @param specDir - The .kspec directory path
1014
+ * @param pattern - Case-insensitive substring to search for
1015
+ * @param options - Search filtering options
1016
+ * @returns Array of search results grouped by session
1017
+ */
1018
+ export async function searchSessionEvents(specDir, pattern, options = {}) {
1019
+ // Defense-in-depth: normalize limit to a valid positive integer
1020
+ const rawLimit = options.limit ?? 50;
1021
+ const limit = Number.isNaN(rawLimit) || rawLimit <= 0 ? 50 : rawLimit;
1022
+ const lowerPattern = pattern.toLowerCase();
1023
+ // Get all session summaries for metadata filtering
1024
+ const allSummaries = await getAllSessionLogSummaries(specDir);
1025
+ // AC: @session-log-search ac-3 - Pre-filter by --since
1026
+ let filteredSummaries = allSummaries;
1027
+ if (options.sinceDate) {
1028
+ filteredSummaries = filteredSummaries.filter((s) => new Date(s.started_at) >= options.sinceDate);
1029
+ }
1030
+ // AC: @session-log-search ac-7 - Pre-filter by --agent
1031
+ if (options.agentType) {
1032
+ filteredSummaries = filteredSummaries.filter((s) => s.agent_type === options.agentType);
1033
+ }
1034
+ const results = [];
1035
+ let totalMatches = 0;
1036
+ for (const summary of filteredSummaries) {
1037
+ if (totalMatches >= limit)
1038
+ break;
1039
+ const eventsPath = getSessionEventsPath(specDir, summary.id);
1040
+ let content;
1041
+ try {
1042
+ content = await fsPromises.readFile(eventsPath, "utf-8");
1043
+ }
1044
+ catch {
1045
+ continue; // Skip sessions without events
1046
+ }
1047
+ if (!content.trim())
1048
+ continue;
1049
+ const matches = [];
1050
+ const lines = content.trim().split("\n");
1051
+ // Track seen tool_call IDs to skip phased duplicates
1052
+ const seenToolCallIds = new Set();
1053
+ for (const line of lines) {
1054
+ if (totalMatches >= limit)
1055
+ break;
1056
+ // Quick substring pre-filter before parsing JSON
1057
+ if (!line.toLowerCase().includes(lowerPattern))
1058
+ continue;
1059
+ try {
1060
+ const event = JSON.parse(line);
1061
+ // AC: @session-log-search ac-2 - Filter by event type
1062
+ if (options.eventType && event.type !== options.eventType)
1063
+ continue;
1064
+ // Deduplicate phased tool_call events
1065
+ if (event?.type === "session.update") {
1066
+ const update = event?.data?.update;
1067
+ if (update?.sessionUpdate === "tool_call") {
1068
+ const toolCallId = update?.toolCallId || update?.tool_call_id || update?.id;
1069
+ if (toolCallId) {
1070
+ if (seenToolCallIds.has(toolCallId))
1071
+ continue;
1072
+ seenToolCallIds.add(toolCallId);
1073
+ }
1074
+ }
1075
+ }
1076
+ // Verify match in stringified data (not just line, in case pattern appears in metadata)
1077
+ const dataStr = JSON.stringify(event.data);
1078
+ if (!dataStr.toLowerCase().includes(lowerPattern))
1079
+ continue;
1080
+ // AC: @session-log-search ac-4 - Create match with excerpt
1081
+ matches.push({
1082
+ session_id: summary.id,
1083
+ timestamp: event.ts,
1084
+ event_type: event.type,
1085
+ content_excerpt: extractContentExcerpt(event.data, pattern, 200),
1086
+ });
1087
+ totalMatches++;
1088
+ }
1089
+ catch {
1090
+ // Skip unparseable lines
1091
+ }
1092
+ }
1093
+ if (matches.length > 0) {
1094
+ results.push({
1095
+ session_id: summary.id,
1096
+ agent_type: summary.agent_type,
1097
+ started_at: summary.started_at,
1098
+ matches,
1099
+ });
1100
+ }
1101
+ }
1102
+ return results;
1103
+ }
325
1104
  //# sourceMappingURL=store.js.map