@kynetic-ai/spec 0.1.2 → 0.3.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 (510) 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 +116 -0
  32. package/dist/cli/batch-exec.d.ts.map +1 -0
  33. package/dist/cli/batch-exec.js +694 -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 +140 -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 +57 -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 +533 -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 +516 -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 +1097 -169
  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 +811 -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 +1233 -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 +53 -0
  156. package/dist/cli/commands/skill-install.d.ts.map +1 -0
  157. package/dist/cli/commands/skill-install.js +452 -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 +569 -346
  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 +227 -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 +569 -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 +235 -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 +468 -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 +351 -0
  309. package/dist/parser/config.d.ts.map +1 -0
  310. package/dist/parser/config.js +326 -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 +189 -0
  345. package/dist/parser/plan-document.d.ts.map +1 -0
  346. package/dist/parser/plan-document.js +340 -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 +277 -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 +83 -0
  401. package/dist/ralph/subagent.d.ts.map +1 -0
  402. package/dist/ralph/subagent.js +174 -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 +95 -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 +2 -2
  413. package/dist/schema/common.d.ts.map +1 -1
  414. package/dist/schema/common.js +34 -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 +233 -1
  448. package/dist/sessions/store.d.ts.map +1 -1
  449. package/dist/sessions/store.js +628 -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 +51 -0
  456. package/dist/strings/errors.d.ts.map +1 -1
  457. package/dist/strings/errors.js +136 -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/utils/commit.d.ts +1 -1
  474. package/dist/utils/commit.d.ts.map +1 -1
  475. package/dist/utils/commit.js +28 -26
  476. package/dist/utils/commit.js.map +1 -1
  477. package/dist/utils/git.d.ts +1 -1
  478. package/dist/utils/git.d.ts.map +1 -1
  479. package/dist/utils/git.js +40 -38
  480. package/dist/utils/git.js.map +1 -1
  481. package/dist/utils/grep.js +11 -11
  482. package/dist/utils/grep.js.map +1 -1
  483. package/dist/utils/index.d.ts +7 -7
  484. package/dist/utils/index.d.ts.map +1 -1
  485. package/dist/utils/index.js +4 -4
  486. package/dist/utils/index.js.map +1 -1
  487. package/dist/utils/time.d.ts.map +1 -1
  488. package/dist/utils/time.js +10 -10
  489. package/dist/utils/time.js.map +1 -1
  490. package/package.json +28 -5
  491. package/plugin/.claude-plugin/marketplace.json +17 -0
  492. package/plugin/.claude-plugin/plugin.json +5 -0
  493. package/plugin/plugins/kspec/skills/help/SKILL.md +42 -0
  494. package/plugin/plugins/kspec/skills/triage/SKILL.md +206 -0
  495. package/plugin/plugins/kspec/skills/triage/docs/automation.md +120 -0
  496. package/plugin/plugins/kspec/skills/triage/docs/inbox.md +144 -0
  497. package/plugin/plugins/kspec/skills/triage/docs/observations.md +85 -0
  498. package/templates/agents-sections/01-quick-start.md +22 -0
  499. package/templates/agents-sections/02-shadow-branch.md +34 -0
  500. package/templates/agents-sections/03-task-lifecycle.md +48 -0
  501. package/templates/agents-sections/04-pr-workflow.md +17 -0
  502. package/templates/agents-sections/05-commit-convention.md +27 -0
  503. package/templates/agents-sections/06-ralph-loop.md +45 -0
  504. package/templates/hooks/pre-commit +34 -0
  505. package/templates/skills/help/SKILL.md +37 -0
  506. package/templates/skills/manifest.yaml +15 -0
  507. package/templates/skills/triage/SKILL.md +199 -0
  508. package/templates/skills/triage/docs/automation.md +120 -0
  509. package/templates/skills/triage/docs/inbox.md +144 -0
  510. package/templates/skills/triage/docs/observations.md +85 -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,7 +257,7 @@ 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 {
@@ -261,7 +275,7 @@ export async function readEvents(specDir, sessionId) {
261
275
  */
262
276
  export async function readEventsSince(specDir, sessionId, since, until) {
263
277
  const events = await readEvents(specDir, sessionId);
264
- return events.filter(e => {
278
+ return events.filter((e) => {
265
279
  if (e.ts < since)
266
280
  return false;
267
281
  if (until !== undefined && e.ts > until)
@@ -283,6 +297,121 @@ export async function getLastEvent(specDir, sessionId) {
283
297
  }
284
298
  return events[events.length - 1];
285
299
  }
300
+ /**
301
+ * Count lines in events.jsonl without parsing JSON.
302
+ * Much faster than readEvents() for large files.
303
+ */
304
+ async function countEventLines(specDir, sessionId) {
305
+ const eventsPath = getSessionEventsPath(specDir, sessionId);
306
+ try {
307
+ const content = await fsPromises.readFile(eventsPath, "utf-8");
308
+ if (!content.trim())
309
+ return 0;
310
+ return content.trim().split("\n").length;
311
+ }
312
+ catch {
313
+ return 0;
314
+ }
315
+ }
316
+ /**
317
+ * Count context-iter-*.json files for a session (iteration count).
318
+ */
319
+ async function countIterations(specDir, sessionId) {
320
+ const sessionDir = getSessionDir(specDir, sessionId);
321
+ try {
322
+ const entries = await fsPromises.readdir(sessionDir);
323
+ return entries.filter((e) => e.startsWith("context-iter-") && e.endsWith(".json")).length;
324
+ }
325
+ catch {
326
+ return 0;
327
+ }
328
+ }
329
+ /**
330
+ * Count task completions by scanning events for tool calls that invoke
331
+ * `kspec task complete` or `npm run dev -- task complete`.
332
+ *
333
+ * Real sessions record task completions as session.update events with
334
+ * sessionUpdate: "tool_call" and rawInput.command containing the complete command.
335
+ * We use a fast substring check before JSON parsing for performance.
336
+ */
337
+ async function countTaskCompletions(specDir, sessionId) {
338
+ const eventsPath = getSessionEventsPath(specDir, sessionId);
339
+ try {
340
+ const content = await fsPromises.readFile(eventsPath, "utf-8");
341
+ if (!content.trim())
342
+ return 0;
343
+ const lines = content.trim().split("\n");
344
+ let count = 0;
345
+ for (const line of lines) {
346
+ // Quick substring pre-filter: only parse lines that might contain task complete commands
347
+ if (!line.includes("task complete"))
348
+ continue;
349
+ // Must be a tool_call event (session.update with sessionUpdate: "tool_call")
350
+ if (!line.includes('"tool_call"'))
351
+ continue;
352
+ try {
353
+ const event = JSON.parse(line);
354
+ const command = event?.data?.update?.rawInput?.command;
355
+ if (typeof command === "string" && /\btask complete\b/.test(command)) {
356
+ count++;
357
+ }
358
+ }
359
+ catch {
360
+ // Skip unparseable lines
361
+ }
362
+ }
363
+ return count;
364
+ }
365
+ catch {
366
+ return 0;
367
+ }
368
+ }
369
+ /**
370
+ * Get a summary of a single session for list display.
371
+ *
372
+ * Gathers metadata and computes metrics lazily (only parses what's needed).
373
+ *
374
+ * @param specDir - The .kspec directory path
375
+ * @param sessionId - Session ID
376
+ * @returns Session summary or null if session doesn't exist
377
+ */
378
+ export async function getSessionLogSummary(specDir, sessionId) {
379
+ const metadata = await getSession(specDir, sessionId);
380
+ if (!metadata)
381
+ return null;
382
+ const [eventCount, iterationCount, tasksCompleted] = await Promise.all([
383
+ countEventLines(specDir, sessionId),
384
+ countIterations(specDir, sessionId),
385
+ countTaskCompletions(specDir, sessionId),
386
+ ]);
387
+ const startMs = new Date(metadata.started_at).getTime();
388
+ const endMs = metadata.ended_at
389
+ ? new Date(metadata.ended_at).getTime()
390
+ : Date.now();
391
+ const durationMs = endMs - startMs;
392
+ return {
393
+ id: metadata.id,
394
+ status: metadata.status,
395
+ agent_type: metadata.agent_type,
396
+ started_at: metadata.started_at,
397
+ ended_at: metadata.ended_at,
398
+ duration_ms: durationMs,
399
+ event_count: eventCount,
400
+ iteration_count: iterationCount,
401
+ tasks_completed: tasksCompleted,
402
+ };
403
+ }
404
+ /**
405
+ * Get summaries for all sessions.
406
+ *
407
+ * @param specDir - The .kspec directory path
408
+ * @returns Array of session summaries
409
+ */
410
+ export async function getAllSessionLogSummaries(specDir) {
411
+ const sessionIds = await listSessions(specDir);
412
+ const summaries = await Promise.all(sessionIds.map((id) => getSessionLogSummary(specDir, id)));
413
+ return summaries.filter((s) => s !== null);
414
+ }
286
415
  // ─── Context Snapshots ───────────────────────────────────────────────────────
287
416
  /**
288
417
  * Save session context snapshot for a given iteration.
@@ -302,7 +431,7 @@ export async function saveSessionContext(specDir, sessionId, iteration, context)
302
431
  await fsPromises.mkdir(sessionDir, { recursive: true });
303
432
  // Write context snapshot as pretty JSON
304
433
  const content = JSON.stringify(context, null, 2);
305
- await fsPromises.writeFile(contextPath, content, 'utf-8');
434
+ await fsPromises.writeFile(contextPath, content, "utf-8");
306
435
  }
307
436
  /**
308
437
  * Read session context snapshot for a given iteration.
@@ -315,11 +444,479 @@ export async function saveSessionContext(specDir, sessionId, iteration, context)
315
444
  export async function readSessionContext(specDir, sessionId, iteration) {
316
445
  const contextPath = getSessionContextPath(specDir, sessionId, iteration);
317
446
  try {
318
- const content = await fsPromises.readFile(contextPath, 'utf-8');
447
+ const content = await fsPromises.readFile(contextPath, "utf-8");
319
448
  return JSON.parse(content);
320
449
  }
321
450
  catch {
322
451
  return null;
323
452
  }
324
453
  }
454
+ /**
455
+ * Resolve a session ID or prefix to a full session ID.
456
+ *
457
+ * AC: @session-log-show ac-7, ac-8, ac-9
458
+ *
459
+ * @param specDir - The .kspec directory path
460
+ * @param idOrPrefix - Full session ID or prefix (e.g., first 8 chars)
461
+ * @returns Resolution result
462
+ */
463
+ export async function resolveSessionId(specDir, idOrPrefix) {
464
+ const sessionIds = await listSessions(specDir);
465
+ // First, try exact match
466
+ if (sessionIds.includes(idOrPrefix)) {
467
+ return { ok: true, id: idOrPrefix };
468
+ }
469
+ // Try prefix match
470
+ const matches = sessionIds.filter((id) => id.startsWith(idOrPrefix));
471
+ if (matches.length === 0) {
472
+ return { ok: false, error: "not_found" };
473
+ }
474
+ if (matches.length === 1) {
475
+ return { ok: true, id: matches[0] };
476
+ }
477
+ // Ambiguous - multiple matches
478
+ return { ok: false, error: "ambiguous", matches };
479
+ }
480
+ /**
481
+ * Get iteration number from a context snapshot file.
482
+ */
483
+ async function getIterationNumbers(specDir, sessionId) {
484
+ const sessionDir = getSessionDir(specDir, sessionId);
485
+ try {
486
+ const entries = await fsPromises.readdir(sessionDir);
487
+ const iterations = [];
488
+ for (const entry of entries) {
489
+ const match = entry.match(/^context-iter-(\d+)\.json$/);
490
+ if (match) {
491
+ iterations.push(parseInt(match[1], 10));
492
+ }
493
+ }
494
+ return iterations.sort((a, b) => a - b);
495
+ }
496
+ catch {
497
+ return [];
498
+ }
499
+ }
500
+ /**
501
+ * Extract task refs from task commands in event data.
502
+ */
503
+ function extractTaskRef(command) {
504
+ // Match patterns like: kspec task start @ref, kspec task complete @ref
505
+ const match = command.match(/@[\w-]+/);
506
+ return match ? match[0] : null;
507
+ }
508
+ /**
509
+ * Compute per-iteration summaries from events.
510
+ *
511
+ * Dynamically creates iteration buckets based on both context snapshot files
512
+ * AND event data to handle cases where events are logged before context
513
+ * snapshots exist (e.g., active sessions).
514
+ *
515
+ * AC: @session-log-show ac-2
516
+ */
517
+ async function computeIterationSummaries(specDir, sessionId) {
518
+ const events = await readEvents(specDir, sessionId);
519
+ const snapshotIterations = await getIterationNumbers(specDir, sessionId);
520
+ // Collect all iteration numbers from both snapshots and events
521
+ const allIterations = new Set(snapshotIterations);
522
+ for (const event of events) {
523
+ const data = event.data;
524
+ if (typeof data?.iteration === "number") {
525
+ allIterations.add(data.iteration);
526
+ }
527
+ }
528
+ // If no iterations found anywhere, create a single iteration-0 summary
529
+ if (allIterations.size === 0) {
530
+ return [
531
+ {
532
+ iteration: 0,
533
+ event_count: events.length,
534
+ tasks_started: [],
535
+ tasks_completed: [],
536
+ },
537
+ ];
538
+ }
539
+ // Create buckets for all known iterations
540
+ const iterations = Array.from(allIterations).sort((a, b) => a - b);
541
+ const iterationMap = new Map();
542
+ for (const n of iterations) {
543
+ iterationMap.set(n, []);
544
+ }
545
+ for (const event of events) {
546
+ // Try to get iteration from event data
547
+ const data = event.data;
548
+ const iter = data?.iteration;
549
+ if (typeof iter === "number" && iterationMap.has(iter)) {
550
+ iterationMap.get(iter).push(event);
551
+ }
552
+ else {
553
+ // Events without iteration info (lifecycle events) go to iteration 0
554
+ // or the first known iteration if 0 doesn't exist
555
+ const fallbackIter = iterationMap.has(0) ? 0 : iterations[0];
556
+ iterationMap.get(fallbackIter).push(event);
557
+ }
558
+ }
559
+ const summaries = [];
560
+ for (const [iterNum, iterEvents] of iterationMap) {
561
+ const tasksStarted = [];
562
+ const tasksCompleted = [];
563
+ for (const event of iterEvents) {
564
+ if (event.type === "session.update") {
565
+ const data = event.data;
566
+ const command = data?.update?.rawInput?.command;
567
+ if (typeof command === "string") {
568
+ if (/\btask start\b/.test(command)) {
569
+ const ref = extractTaskRef(command);
570
+ if (ref)
571
+ tasksStarted.push(ref);
572
+ }
573
+ else if (/\btask complete\b/.test(command)) {
574
+ const ref = extractTaskRef(command);
575
+ if (ref)
576
+ tasksCompleted.push(ref);
577
+ }
578
+ }
579
+ }
580
+ }
581
+ summaries.push({
582
+ iteration: iterNum,
583
+ event_count: iterEvents.length,
584
+ tasks_started: tasksStarted,
585
+ tasks_completed: tasksCompleted,
586
+ });
587
+ }
588
+ return summaries.sort((a, b) => a.iteration - b.iteration);
589
+ }
590
+ /**
591
+ * Get full session detail for session log show.
592
+ *
593
+ * @param specDir - The .kspec directory path
594
+ * @param sessionId - Session ID (must be resolved first)
595
+ * @returns Session detail or null if not found
596
+ */
597
+ export async function getSessionLogDetail(specDir, sessionId) {
598
+ const metadata = await getSession(specDir, sessionId);
599
+ if (!metadata)
600
+ return null;
601
+ const [eventCount, iterationCount, iterations] = await Promise.all([
602
+ countEventLines(specDir, sessionId),
603
+ countIterations(specDir, sessionId),
604
+ computeIterationSummaries(specDir, sessionId),
605
+ ]);
606
+ const startMs = new Date(metadata.started_at).getTime();
607
+ const endMs = metadata.ended_at
608
+ ? new Date(metadata.ended_at).getTime()
609
+ : Date.now();
610
+ const durationMs = endMs - startMs;
611
+ return {
612
+ id: metadata.id,
613
+ status: metadata.status,
614
+ agent_type: metadata.agent_type,
615
+ task_id: metadata.task_id,
616
+ started_at: metadata.started_at,
617
+ ended_at: metadata.ended_at,
618
+ duration_ms: durationMs,
619
+ event_count: eventCount,
620
+ iteration_count: iterationCount,
621
+ iterations,
622
+ };
623
+ }
624
+ /**
625
+ * Compute aggregate statistics from session summaries.
626
+ *
627
+ * @param summaries - Array of session log summaries
628
+ * @returns Aggregate statistics
629
+ */
630
+ export function computeSessionLogStats(summaries) {
631
+ if (summaries.length === 0) {
632
+ return {
633
+ total_sessions: 0,
634
+ total_events: 0,
635
+ total_iterations: 0,
636
+ total_tasks_completed: 0,
637
+ total_duration_ms: 0,
638
+ avg_duration_ms: 0,
639
+ avg_iterations_per_session: 0,
640
+ avg_tasks_per_session: 0,
641
+ status_breakdown: [],
642
+ };
643
+ }
644
+ // Compute totals
645
+ let totalEvents = 0;
646
+ let totalIterations = 0;
647
+ let totalTasksCompleted = 0;
648
+ let totalDuration = 0;
649
+ const statusCounts = {
650
+ active: 0,
651
+ completed: 0,
652
+ abandoned: 0,
653
+ };
654
+ for (const s of summaries) {
655
+ totalEvents += s.event_count;
656
+ totalIterations += s.iteration_count;
657
+ totalTasksCompleted += s.tasks_completed;
658
+ totalDuration += s.duration_ms;
659
+ statusCounts[s.status] = (statusCounts[s.status] || 0) + 1;
660
+ }
661
+ const n = summaries.length;
662
+ // Build status breakdown
663
+ const statusBreakdown = [];
664
+ for (const status of ["completed", "active", "abandoned"]) {
665
+ const count = statusCounts[status] || 0;
666
+ if (count > 0) {
667
+ statusBreakdown.push({
668
+ status,
669
+ count,
670
+ percentage: Math.round((count / n) * 100 * 10) / 10, // 1 decimal place
671
+ });
672
+ }
673
+ }
674
+ return {
675
+ total_sessions: n,
676
+ total_events: totalEvents,
677
+ total_iterations: totalIterations,
678
+ total_tasks_completed: totalTasksCompleted,
679
+ total_duration_ms: totalDuration,
680
+ avg_duration_ms: Math.round(totalDuration / n),
681
+ avg_iterations_per_session: Math.round((totalIterations / n) * 10) / 10,
682
+ avg_tasks_per_session: Math.round((totalTasksCompleted / n) * 10) / 10,
683
+ status_breakdown: statusBreakdown,
684
+ };
685
+ }
686
+ /**
687
+ * Count tool calls by scanning events.jsonl for tool_call events.
688
+ *
689
+ * This is relatively expensive as it parses all events, so only call
690
+ * when --tool-usage is requested.
691
+ *
692
+ * AC: @session-log-stats ac-6
693
+ */
694
+ export async function computeToolUsageStats(specDir, sessionIds, limit = 10) {
695
+ const toolCounts = {};
696
+ let totalToolCalls = 0;
697
+ for (const sessionId of sessionIds) {
698
+ const eventsPath = getSessionEventsPath(specDir, sessionId);
699
+ try {
700
+ const content = await fsPromises.readFile(eventsPath, "utf-8");
701
+ if (!content.trim())
702
+ continue;
703
+ const lines = content.trim().split("\n");
704
+ for (const line of lines) {
705
+ // Quick pre-filter: only parse lines that might be tool_call events
706
+ if (!line.includes('"tool_call"'))
707
+ continue;
708
+ try {
709
+ const event = JSON.parse(line);
710
+ if (event?.type === "session.update") {
711
+ const update = event?.data?.update;
712
+ if (update?.sessionUpdate === "tool_call") {
713
+ const toolName = update?._meta?.claudeCode?.toolName || "unknown";
714
+ toolCounts[toolName] = (toolCounts[toolName] || 0) + 1;
715
+ totalToolCalls++;
716
+ }
717
+ }
718
+ }
719
+ catch {
720
+ // Skip unparseable lines
721
+ }
722
+ }
723
+ }
724
+ catch {
725
+ // Skip sessions without events
726
+ }
727
+ }
728
+ // Sort by count descending, take top N
729
+ const sorted = Object.entries(toolCounts)
730
+ .map(([tool_name, count]) => ({
731
+ tool_name,
732
+ count,
733
+ percentage: totalToolCalls > 0
734
+ ? Math.round((count / totalToolCalls) * 100 * 10) / 10
735
+ : 0,
736
+ }))
737
+ .sort((a, b) => b.count - a.count)
738
+ .slice(0, limit);
739
+ return sorted;
740
+ }
741
+ /**
742
+ * Compute ISO-8601 week number and week-year in UTC.
743
+ *
744
+ * ISO-8601 rules:
745
+ * - Week 1 contains the first Thursday of the year
746
+ * - Week starts on Monday
747
+ * - Week-year may differ from calendar year at year boundaries
748
+ *
749
+ * Returns [weekYear, weekNumber] tuple.
750
+ */
751
+ function getISOWeekUTC(date) {
752
+ // Clone and normalize to UTC midnight
753
+ const d = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
754
+ // Get day of week (ISO: Monday=1, Sunday=7)
755
+ const dayOfWeek = d.getUTCDay() || 7;
756
+ // Set to nearest Thursday (determines week-year)
757
+ d.setUTCDate(d.getUTCDate() + 4 - dayOfWeek);
758
+ // Week-year is the year of this Thursday
759
+ const weekYear = d.getUTCFullYear();
760
+ // January 4 is always in week 1 (as it's in the first week with a Thursday)
761
+ const jan4 = new Date(Date.UTC(weekYear, 0, 4));
762
+ const jan4DayOfWeek = jan4.getUTCDay() || 7;
763
+ // Start of week 1 (Monday before or on Jan 4)
764
+ const week1Start = new Date(jan4);
765
+ week1Start.setUTCDate(jan4.getUTCDate() - (jan4DayOfWeek - 1));
766
+ // Week number = weeks between week 1 start and the Thursday we found
767
+ const weekNum = Math.floor((d.getTime() - week1Start.getTime()) / (7 * 24 * 60 * 60 * 1000)) + 1;
768
+ return [weekYear, weekNum];
769
+ }
770
+ /**
771
+ * Group sessions by time period (day or week).
772
+ *
773
+ * AC: @session-log-stats ac-7
774
+ */
775
+ export function computeTimePeriodStats(summaries, groupBy) {
776
+ const buckets = {};
777
+ for (const s of summaries) {
778
+ const date = new Date(s.started_at);
779
+ let period;
780
+ if (groupBy === "day") {
781
+ period = date.toISOString().split("T")[0]; // YYYY-MM-DD in UTC
782
+ }
783
+ else {
784
+ // Week: use ISO-8601 week format (YYYY-Www) in UTC
785
+ const [weekYear, weekNum] = getISOWeekUTC(date);
786
+ period = `${weekYear}-W${weekNum.toString().padStart(2, "0")}`;
787
+ }
788
+ if (!buckets[period]) {
789
+ buckets[period] = { sessions: 0, tasks: 0, duration: 0 };
790
+ }
791
+ buckets[period].sessions += 1;
792
+ buckets[period].tasks += s.tasks_completed;
793
+ buckets[period].duration += s.duration_ms;
794
+ }
795
+ // Convert to array and sort by period (newest first for display)
796
+ const result = Object.entries(buckets)
797
+ .map(([period, data]) => ({
798
+ period,
799
+ sessions_count: data.sessions,
800
+ tasks_completed: data.tasks,
801
+ total_duration_ms: data.duration,
802
+ }))
803
+ .sort((a, b) => b.period.localeCompare(a.period));
804
+ return result;
805
+ }
806
+ /**
807
+ * Extract a content excerpt around a match, limited to maxLength chars.
808
+ *
809
+ * AC: @session-log-search ac-4
810
+ */
811
+ function extractContentExcerpt(data, pattern, maxLength = 200) {
812
+ // Stringify the data for searching
813
+ const str = JSON.stringify(data);
814
+ const lowerStr = str.toLowerCase();
815
+ const lowerPattern = pattern.toLowerCase();
816
+ const matchIndex = lowerStr.indexOf(lowerPattern);
817
+ if (matchIndex === -1) {
818
+ // Shouldn't happen since we pre-filtered, but return start of content
819
+ return str.length > maxLength ? str.slice(0, maxLength - 3) + "..." : str;
820
+ }
821
+ // Calculate excerpt window centered on match
822
+ const matchLen = pattern.length;
823
+ const contextBefore = Math.floor((maxLength - matchLen) / 2);
824
+ const start = Math.max(0, matchIndex - contextBefore);
825
+ const end = Math.min(str.length, start + maxLength);
826
+ let excerpt = str.slice(start, end);
827
+ // Add ellipsis indicators
828
+ if (start > 0) {
829
+ excerpt = "..." + excerpt.slice(3);
830
+ }
831
+ if (end < str.length) {
832
+ excerpt = excerpt.slice(0, -3) + "...";
833
+ }
834
+ return excerpt;
835
+ }
836
+ /**
837
+ * Search across session events for a pattern.
838
+ *
839
+ * This streams through events.jsonl files, applying filters as early as possible
840
+ * for performance. Sessions are pre-filtered by metadata (--since, --agent) before
841
+ * scanning events to reduce I/O.
842
+ *
843
+ * AC: @session-log-search ac-1, ac-2, ac-3, ac-5, ac-7
844
+ *
845
+ * @param specDir - The .kspec directory path
846
+ * @param pattern - Case-insensitive substring to search for
847
+ * @param options - Search filtering options
848
+ * @returns Array of search results grouped by session
849
+ */
850
+ export async function searchSessionEvents(specDir, pattern, options = {}) {
851
+ // Defense-in-depth: normalize limit to a valid positive integer
852
+ const rawLimit = options.limit ?? 50;
853
+ const limit = Number.isNaN(rawLimit) || rawLimit <= 0 ? 50 : rawLimit;
854
+ const lowerPattern = pattern.toLowerCase();
855
+ // Get all session summaries for metadata filtering
856
+ const allSummaries = await getAllSessionLogSummaries(specDir);
857
+ // AC: @session-log-search ac-3 - Pre-filter by --since
858
+ let filteredSummaries = allSummaries;
859
+ if (options.sinceDate) {
860
+ filteredSummaries = filteredSummaries.filter((s) => new Date(s.started_at) >= options.sinceDate);
861
+ }
862
+ // AC: @session-log-search ac-7 - Pre-filter by --agent
863
+ if (options.agentType) {
864
+ filteredSummaries = filteredSummaries.filter((s) => s.agent_type === options.agentType);
865
+ }
866
+ const results = [];
867
+ let totalMatches = 0;
868
+ for (const summary of filteredSummaries) {
869
+ if (totalMatches >= limit)
870
+ break;
871
+ const eventsPath = getSessionEventsPath(specDir, summary.id);
872
+ let content;
873
+ try {
874
+ content = await fsPromises.readFile(eventsPath, "utf-8");
875
+ }
876
+ catch {
877
+ continue; // Skip sessions without events
878
+ }
879
+ if (!content.trim())
880
+ continue;
881
+ const matches = [];
882
+ const lines = content.trim().split("\n");
883
+ for (const line of lines) {
884
+ if (totalMatches >= limit)
885
+ break;
886
+ // Quick substring pre-filter before parsing JSON
887
+ if (!line.toLowerCase().includes(lowerPattern))
888
+ continue;
889
+ try {
890
+ const event = JSON.parse(line);
891
+ // AC: @session-log-search ac-2 - Filter by event type
892
+ if (options.eventType && event.type !== options.eventType)
893
+ continue;
894
+ // Verify match in stringified data (not just line, in case pattern appears in metadata)
895
+ const dataStr = JSON.stringify(event.data);
896
+ if (!dataStr.toLowerCase().includes(lowerPattern))
897
+ continue;
898
+ // AC: @session-log-search ac-4 - Create match with excerpt
899
+ matches.push({
900
+ session_id: summary.id,
901
+ timestamp: event.ts,
902
+ event_type: event.type,
903
+ content_excerpt: extractContentExcerpt(event.data, pattern, 200),
904
+ });
905
+ totalMatches++;
906
+ }
907
+ catch {
908
+ // Skip unparseable lines
909
+ }
910
+ }
911
+ if (matches.length > 0) {
912
+ results.push({
913
+ session_id: summary.id,
914
+ agent_type: summary.agent_type,
915
+ started_at: summary.started_at,
916
+ matches,
917
+ });
918
+ }
919
+ }
920
+ return results;
921
+ }
325
922
  //# sourceMappingURL=store.js.map