@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
@@ -0,0 +1,840 @@
1
+ /**
2
+ * Skill render, status, diff, and verify commands.
3
+ *
4
+ * AC: @skill-rendering ac-1 through ac-5 - kspec skill render
5
+ * AC: @skill-render-cli ac-1 through ac-4 - render/status/diff CLI
6
+ * AC: @skill-drift-detection ac-3, ac-4 - drift handling with --force
7
+ * AC: @multi-platform-render-cli ac-1 through ac-7 - multi-platform rendering
8
+ * AC: @skill-drift-detection-improvements ac-2 - kspec skill verify
9
+ */
10
+ import * as fs from "node:fs/promises";
11
+ import * as path from "node:path";
12
+ import chalk from "chalk";
13
+ import Table from "cli-table3";
14
+ import { createTwoFilesPatch } from "diff";
15
+ import { findMetaItemByRef, initContext, isSkill, loadMetaContext, loadSkillContent, } from "../../parser/index.js";
16
+ import { isKspecManagedSkill as isKspecManaged, KSPEC_MANAGED_MARKER, generateFrontmatter, getRenderer, getAllRenderers, getSkillSubdir, contentsEqual, directoriesEqual, migrateOldPluginPaths, } from "../../parser/skill-render.js";
17
+ import { errors } from "../../strings/errors.js";
18
+ import { EXIT_CODES } from "../exit-codes.js";
19
+ import { error, output, success } from "../output.js";
20
+ // ============================================================================
21
+ // Helpers
22
+ // ============================================================================
23
+ /**
24
+ * Get the sync status of a skill for a specific platform
25
+ * AC: @multi-platform-render-cli ac-3 - Per-platform status checking
26
+ */
27
+ async function getMultiPlatformSyncStatus(ctx, projectRoot, skill, renderer) {
28
+ // Use the renderer's drift check
29
+ const driftStatus = await renderer.checkDrift(ctx.specDir, projectRoot, skill.id, { origin: skill.origin });
30
+ // Core skills on claude-code are plugin-provided, not locally rendered
31
+ if (driftStatus === "plugin-provided") {
32
+ return {
33
+ id: skill.id,
34
+ platform: renderer.platform,
35
+ status: "plugin-provided",
36
+ docsStatus: "no-docs",
37
+ };
38
+ }
39
+ // Compute the effective output dir for this skill-platform pair
40
+ const effectiveOutputDir = renderer.defaultOutputDir;
41
+ // Map drift status to sync status
42
+ let status;
43
+ switch (driftStatus) {
44
+ case "in-sync":
45
+ status = "in-sync";
46
+ break;
47
+ case "drifted":
48
+ status = "drifted";
49
+ break;
50
+ case "not-rendered":
51
+ status = "not-rendered";
52
+ break;
53
+ case "no-hash":
54
+ // No hash stored - need to check if content matches expected
55
+ const renderedPath = path.join(projectRoot, effectiveOutputDir, getSkillSubdir(skill.id, skill.origin, renderer.platform), "SKILL.md");
56
+ try {
57
+ await fs.readFile(renderedPath, "utf-8");
58
+ // File exists but no hash - treat as needing sync
59
+ status = "drifted";
60
+ }
61
+ catch {
62
+ status = "not-rendered";
63
+ }
64
+ break;
65
+ }
66
+ // Check docs status for this platform
67
+ const sourceDocsDir = path.join(ctx.specDir, "skills", skill.id, "docs");
68
+ const targetDocsDir = path.join(projectRoot, effectiveOutputDir, getSkillSubdir(skill.id, skill.origin, renderer.platform), "docs");
69
+ let docsStatus = "no-docs";
70
+ try {
71
+ await fs.stat(sourceDocsDir);
72
+ // Source docs exist, check target
73
+ try {
74
+ await fs.stat(targetDocsDir);
75
+ // Both exist, compare
76
+ const equal = await directoriesEqual(sourceDocsDir, targetDocsDir);
77
+ docsStatus = equal ? "in-sync" : "drifted";
78
+ }
79
+ catch {
80
+ docsStatus = "not-rendered";
81
+ }
82
+ }
83
+ catch {
84
+ // No source docs
85
+ docsStatus = "no-docs";
86
+ }
87
+ return {
88
+ id: skill.id,
89
+ platform: renderer.platform,
90
+ status,
91
+ docsStatus,
92
+ };
93
+ }
94
+ /**
95
+ * Get the expected rendered content for a skill
96
+ */
97
+ export async function getExpectedRenderedContent(ctx, skill) {
98
+ const sourceContent = await loadSkillContent(ctx, skill);
99
+ if (!sourceContent) {
100
+ // No source content, generate placeholder
101
+ const frontmatter = generateFrontmatter(skill);
102
+ return `${frontmatter}\n${KSPEC_MANAGED_MARKER}\n\n# ${skill.name}\n\n${skill.description || ""}\n`;
103
+ }
104
+ // Generate rendered content with frontmatter and marker
105
+ const frontmatter = generateFrontmatter(skill);
106
+ // Check if source already has frontmatter - if so, strip it
107
+ const frontmatterMatch = sourceContent.match(/^---\n[\s\S]*?\n---\n?/);
108
+ const contentWithoutFrontmatter = frontmatterMatch
109
+ ? sourceContent.slice(frontmatterMatch[0].length)
110
+ : sourceContent;
111
+ return `${frontmatter}\n${KSPEC_MANAGED_MARKER}\n${contentWithoutFrontmatter}`;
112
+ }
113
+ /**
114
+ * Generate unified diff between two strings.
115
+ * Uses the 'diff' library for correct LCS-based diffing.
116
+ * AC: @skill-render-cli ac-4
117
+ * AC: @guard-script-and-diff-quality ac-2
118
+ */
119
+ export function generateUnifiedDiff(actual, expected, actualPath, expectedPath) {
120
+ if (contentsEqual(actual, expected)) {
121
+ return [];
122
+ }
123
+ const patch = createTwoFilesPatch(actualPath, expectedPath, actual, expected, "", "", {
124
+ context: 3,
125
+ });
126
+ // Split into lines and remove the first line (Index: ...) which createTwoFilesPatch adds
127
+ const lines = patch.split("\n");
128
+ // createTwoFilesPatch outputs: "Index: ...\n===...\n--- ...\n+++ ...\n@@ ... @@\n..."
129
+ // Skip the "Index:" and "===" header lines to match our expected format
130
+ const startIdx = lines.findIndex((l) => l.startsWith("---"));
131
+ if (startIdx === -1)
132
+ return [];
133
+ // Remove trailing empty line that createTwoFilesPatch adds
134
+ const result = lines.slice(startIdx);
135
+ if (result.length > 0 && result[result.length - 1] === "") {
136
+ result.pop();
137
+ }
138
+ return result;
139
+ }
140
+ // ============================================================================
141
+ // Command Registration
142
+ // ============================================================================
143
+ /**
144
+ * Register render/status/diff/verify skill commands
145
+ */
146
+ export function registerSkillDiffCommands(skill) {
147
+ // AC: @skill-rendering ac-1 through ac-5 - kspec skill render
148
+ // AC: @skill-render-cli ac-1, ac-2 - kspec skill render / kspec skill render @skill-id
149
+ // AC: @skill-drift-detection ac-3, ac-4 - drift handling with --force
150
+ // AC: @multi-platform-render-cli ac-1 through ac-7 - multi-platform rendering
151
+ // AC: @trait-dry-run - supports --dry-run
152
+ // AC: @trait-error-guidance - provides error guidance
153
+ skill
154
+ .command("render [ref]")
155
+ .description("Render skills from shadow branch to platform-specific files on main branch")
156
+ .option("--clean", "Remove orphaned managed skill directories")
157
+ .option("--dry-run", "Show what would be changed without applying")
158
+ .option("--force", "Overwrite drifted skill files (manually edited)")
159
+ .option("--output-dir <path>", "Custom output directory (overrides platform default)")
160
+ .option("--skill <id>", "Render only a specific skill (deprecated, use positional arg)")
161
+ .action(async (ref, options) => {
162
+ try {
163
+ const ctx = await initContext();
164
+ if (!ctx.manifestPath) {
165
+ error(errors.project.noKspecProject);
166
+ console.log(chalk.gray("Try: kspec init to initialize a kspec project"));
167
+ process.exit(EXIT_CODES.ERROR);
168
+ }
169
+ const metaCtx = await loadMetaContext(ctx);
170
+ // Use rootDir (project root), not specDir (which is .kspec/ in shadow mode)
171
+ const projectRoot = ctx.rootDir;
172
+ const dryRun = options.dryRun || false;
173
+ const force = options.force || false;
174
+ const customOutputDir = options.outputDir;
175
+ const results = [];
176
+ const cleanResults = [];
177
+ const warnings = [];
178
+ // Get all skills (no platform filtering - we'll check per-platform)
179
+ let skillsToRender = metaCtx.skills;
180
+ // AC: @skill-render-cli ac-2 - Filter to specific skill if requested
181
+ // Support both positional ref argument and --skill option
182
+ const skillRef = ref || options.skill;
183
+ if (skillRef) {
184
+ // Resolve the ref to a skill (uses _type discriminant)
185
+ const item = findMetaItemByRef(metaCtx, skillRef);
186
+ if (!item || !isSkill(item)) {
187
+ error(`Skill not found: ${skillRef}`);
188
+ console.log(chalk.gray("Try: kspec skill list"));
189
+ process.exit(EXIT_CODES.NOT_FOUND);
190
+ }
191
+ const skill = item;
192
+ skillsToRender = skillsToRender.filter((s) => s.id === skill.id);
193
+ }
194
+ // Track which platforms we're using to create output directories and for clean
195
+ const usedPlatforms = new Set();
196
+ // AC: @multi-platform-render-cli ac-1, ac-2 - Render each skill to each of its platforms
197
+ for (const skill of skillsToRender) {
198
+ for (const platform of skill.platforms) {
199
+ // Get the renderer for this platform
200
+ const renderer = getRenderer(platform);
201
+ // AC: @multi-platform-render-cli ac-7 - Warn for unregistered platforms
202
+ if (!renderer) {
203
+ warnings.push(`${skill.id}: unregistered platform '${platform}' (skipped)`);
204
+ results.push({
205
+ id: skill.id,
206
+ platform,
207
+ action: "skipped",
208
+ path: "",
209
+ skipReason: `unregistered platform '${platform}'`,
210
+ });
211
+ continue;
212
+ }
213
+ usedPlatforms.add(platform);
214
+ // Core skills on claude-code are plugin-provided, skip local render
215
+ // But still run migration cleanup for old render paths
216
+ if (skill.origin === "core" && platform === "claude-code" && !customOutputDir) {
217
+ if (!dryRun) {
218
+ await migrateOldPluginPaths(projectRoot, skill.id);
219
+ }
220
+ results.push({
221
+ id: skill.id,
222
+ platform,
223
+ action: "skipped",
224
+ path: "",
225
+ skipReason: "core skill provided by npm package plugin",
226
+ skipCode: "plugin-provided",
227
+ });
228
+ continue;
229
+ }
230
+ // Determine output directory
231
+ const effectiveDir = customOutputDir || renderer.defaultOutputDir;
232
+ // Ensure output directory exists
233
+ const targetSkillsDir = path.join(projectRoot, effectiveDir);
234
+ if (!dryRun) {
235
+ await fs.mkdir(targetSkillsDir, { recursive: true });
236
+ }
237
+ // Check for drift before rendering
238
+ // AC: @skill-drift-detection ac-3, ac-4 - Check drift and skip without --force
239
+ const driftStatus = await renderer.checkDrift(ctx.specDir, projectRoot, skill.id, { outputDir: customOutputDir, origin: skill.origin });
240
+ // AC: @skill-drift-detection ac-3 - Skip drifted skills without --force
241
+ if (driftStatus === "drifted" && !force) {
242
+ const renderedPath = path.join(projectRoot, effectiveDir, getSkillSubdir(skill.id, skill.origin, platform), "SKILL.md");
243
+ results.push({
244
+ id: skill.id,
245
+ platform,
246
+ action: "skipped",
247
+ path: renderedPath,
248
+ skipReason: "drifted (use --force to overwrite)",
249
+ });
250
+ continue;
251
+ }
252
+ // AC: @skill-drift-detection ac-4 - Render (overwrite) when --force is used
253
+ // AC: @multi-platform-render-cli ac-4 - Pass custom outputDir to renderer
254
+ const result = await renderer.render(ctx, projectRoot, skill, {
255
+ dryRun,
256
+ storeHash: true,
257
+ outputDir: customOutputDir,
258
+ });
259
+ results.push({
260
+ id: result.id,
261
+ platform: result.platform,
262
+ action: result.action,
263
+ path: result.paths[0] || "",
264
+ skipReason: result.skipReason,
265
+ skipCode: result.skipCode,
266
+ });
267
+ }
268
+ }
269
+ // Handle --clean: remove orphaned managed skills per platform
270
+ // AC: @skill-rendering ac-4, ac-5
271
+ // AC: @multi-platform-render-cli ac-5 - Per-platform cleanup
272
+ if (options.clean) {
273
+ // Build set of active skill subdirs per platform (accounts for namespacing)
274
+ const activeSubdirsByPlatform = new Map();
275
+ for (const skill of metaCtx.skills) {
276
+ for (const platform of skill.platforms) {
277
+ if (!activeSubdirsByPlatform.has(platform)) {
278
+ activeSubdirsByPlatform.set(platform, new Set());
279
+ }
280
+ activeSubdirsByPlatform.get(platform).add(getSkillSubdir(skill.id, skill.origin, platform));
281
+ }
282
+ }
283
+ // Helper to check and clean a skill directory
284
+ async function cleanSkillDir(skillId, skillDir, activeSubdirs, subdir, platform, hasNestedSkills) {
285
+ const skillMdPath = path.join(skillDir, "SKILL.md");
286
+ // Skip if skill still exists in meta for this platform
287
+ if (activeSubdirs.has(subdir))
288
+ return;
289
+ // AC: @skill-rendering ac-4 - Only consider kspec-managed skills
290
+ const isManaged = await isKspecManaged(skillMdPath);
291
+ if (isManaged) {
292
+ // AC: @skill-rendering ac-5 - Remove orphaned directory
293
+ // AC: @skill-rendering ac-6 - Preserve active nested skills in namespace dirs
294
+ if (!dryRun) {
295
+ if (hasNestedSkills) {
296
+ // Directory contains nested skills — only remove the SKILL.md,
297
+ // not the entire directory tree
298
+ await fs.rm(skillMdPath, { force: true });
299
+ }
300
+ else {
301
+ await fs.rm(skillDir, { recursive: true, force: true });
302
+ }
303
+ }
304
+ cleanResults.push({
305
+ id: skillId,
306
+ path: hasNestedSkills ? skillMdPath : skillDir,
307
+ action: "removed",
308
+ platform,
309
+ });
310
+ }
311
+ else {
312
+ cleanResults.push({
313
+ id: skillId,
314
+ path: skillDir,
315
+ action: "skipped",
316
+ reason: "Not managed by kspec",
317
+ platform,
318
+ });
319
+ }
320
+ }
321
+ // Helper to scan and clean orphaned skills in a directory
322
+ async function scanAndClean(targetSkillsDir, activeSubdirs, platform) {
323
+ try {
324
+ const entries = await fs.readdir(targetSkillsDir, {
325
+ withFileTypes: true,
326
+ });
327
+ for (const entry of entries) {
328
+ if (!entry.isDirectory())
329
+ continue;
330
+ const skillDir = path.join(targetSkillsDir, entry.name);
331
+ // Check for SKILL.md in this directory
332
+ const hasSkillMd = await fs.access(path.join(skillDir, "SKILL.md")).then(() => true, () => false);
333
+ if (hasSkillMd) {
334
+ await cleanSkillDir(entry.name, skillDir, activeSubdirs, entry.name, platform);
335
+ }
336
+ }
337
+ }
338
+ catch {
339
+ // Output directory doesn't exist, nothing to clean
340
+ }
341
+ }
342
+ // Build separate active sets for default dir vs plugin dir
343
+ // Core skills on claude-code are active in plugin dir, NOT in .claude/skills/
344
+ const nonCoreActiveByPlatform = new Map();
345
+ const coreActiveByPlatform = new Map();
346
+ for (const skill of metaCtx.skills) {
347
+ for (const platform of skill.platforms) {
348
+ if (skill.origin === "core" && platform === "claude-code") {
349
+ if (!coreActiveByPlatform.has(platform)) {
350
+ coreActiveByPlatform.set(platform, new Set());
351
+ }
352
+ coreActiveByPlatform.get(platform).add(skill.id);
353
+ }
354
+ else {
355
+ if (!nonCoreActiveByPlatform.has(platform)) {
356
+ nonCoreActiveByPlatform.set(platform, new Set());
357
+ }
358
+ nonCoreActiveByPlatform.get(platform).add(getSkillSubdir(skill.id, skill.origin, platform));
359
+ }
360
+ }
361
+ }
362
+ // Clean each platform's output directory
363
+ const renderers = getAllRenderers();
364
+ for (const renderer of renderers) {
365
+ // Clean platform default directory (project/local skills only)
366
+ const nonCoreActive = nonCoreActiveByPlatform.get(renderer.platform) || new Set();
367
+ const outputDir = customOutputDir || renderer.defaultOutputDir;
368
+ await scanAndClean(path.join(projectRoot, outputDir), nonCoreActive, renderer.platform);
369
+ // Core skills on claude-code are now plugin-provided.
370
+ // Clean old namespaced dirs and legacy plugin-rendered paths.
371
+ if (renderer.platform === "claude-code" && !customOutputDir) {
372
+ // Clean orphaned legacy plugin-rendered core skills (.claude/plugins/kspec/skills/<id>/)
373
+ const coreActive = coreActiveByPlatform.get(renderer.platform) || new Set();
374
+ const legacyPluginSkillsDir = path.join(projectRoot, ".claude", "plugins", "kspec", "skills");
375
+ await scanAndClean(legacyPluginSkillsDir, coreActive, renderer.platform);
376
+ // Clean old namespaced dirs (.claude/skills/kspec/<id>/) that may remain from PR #440
377
+ const oldNamespaceDir = path.join(projectRoot, renderer.defaultOutputDir, "kspec");
378
+ try {
379
+ const entries = await fs.readdir(oldNamespaceDir, { withFileTypes: true });
380
+ for (const entry of entries) {
381
+ if (!entry.isDirectory()) {
382
+ // Handle orphaned SKILL.md at namespace root
383
+ if (entry.name === "SKILL.md") {
384
+ const skillMdPath = path.join(oldNamespaceDir, "SKILL.md");
385
+ const isManaged = await isKspecManaged(skillMdPath);
386
+ if (isManaged && !dryRun) {
387
+ await fs.rm(skillMdPath, { force: true });
388
+ }
389
+ if (isManaged) {
390
+ cleanResults.push({
391
+ id: "kspec",
392
+ path: skillMdPath,
393
+ action: "removed",
394
+ platform: renderer.platform,
395
+ });
396
+ }
397
+ }
398
+ continue;
399
+ }
400
+ const nestedDir = path.join(oldNamespaceDir, entry.name);
401
+ const nestedHasSkillMd = await fs.access(path.join(nestedDir, "SKILL.md")).then(() => true, () => false);
402
+ if (nestedHasSkillMd) {
403
+ const isManaged = await isKspecManaged(path.join(nestedDir, "SKILL.md"));
404
+ if (isManaged) {
405
+ if (!dryRun) {
406
+ await fs.rm(nestedDir, { recursive: true, force: true });
407
+ }
408
+ cleanResults.push({
409
+ id: entry.name,
410
+ path: nestedDir,
411
+ action: "removed",
412
+ platform: renderer.platform,
413
+ });
414
+ }
415
+ }
416
+ }
417
+ // Clean up empty kspec dir
418
+ if (!dryRun) {
419
+ try {
420
+ const remaining = await fs.readdir(oldNamespaceDir);
421
+ if (remaining.length === 0) {
422
+ await fs.rm(oldNamespaceDir, { recursive: true, force: true });
423
+ }
424
+ }
425
+ catch {
426
+ // Already removed or doesn't exist
427
+ }
428
+ }
429
+ }
430
+ catch {
431
+ // Old namespace dir doesn't exist
432
+ }
433
+ }
434
+ }
435
+ }
436
+ // Output results
437
+ // AC: @trait-dry-run ac-6 - JSON output includes dry_run field
438
+ output({
439
+ dry_run: dryRun,
440
+ rendered: results,
441
+ cleaned: cleanResults,
442
+ warnings,
443
+ }, () => {
444
+ // AC: @trait-dry-run ac-3 - Clear indication this is a preview
445
+ if (dryRun) {
446
+ console.log(chalk.yellow("DRY RUN - No changes made"));
447
+ console.log();
448
+ }
449
+ // AC: @multi-platform-render-cli ac-7 - Show warnings for unregistered platforms
450
+ if (warnings.length > 0) {
451
+ console.log(chalk.yellow("Warnings:"));
452
+ for (const warning of warnings) {
453
+ console.log(` ${chalk.yellow("!")} ${warning}`);
454
+ }
455
+ console.log();
456
+ }
457
+ // Render results
458
+ const created = results.filter((r) => r.action === "created");
459
+ const updated = results.filter((r) => r.action === "updated");
460
+ const unchanged = results.filter((r) => r.action === "unchanged");
461
+ // AC: @skill-drift-detection ac-3 - Track skipped drifted skills
462
+ const skippedDrifted = results.filter((r) => r.action === "skipped" && r.skipReason?.includes("drifted"));
463
+ const skippedUnregistered = results.filter((r) => r.action === "skipped" && r.skipReason?.includes("unregistered"));
464
+ // AC: @multi-platform-render-cli ac-6 - Include Platform column in output
465
+ if (created.length > 0) {
466
+ console.log(chalk.green(`Created: ${created.length} skill(s)`));
467
+ for (const r of created) {
468
+ console.log(` ${chalk.green("+")} ${r.id} ${chalk.gray(`[${r.platform}]`)}`);
469
+ }
470
+ }
471
+ if (updated.length > 0) {
472
+ console.log(chalk.blue(`Updated: ${updated.length} skill(s)`));
473
+ for (const r of updated) {
474
+ console.log(` ${chalk.blue("~")} ${r.id} ${chalk.gray(`[${r.platform}]`)}`);
475
+ }
476
+ }
477
+ if (unchanged.length > 0 && results.length <= 10) {
478
+ console.log(chalk.gray(`Unchanged: ${unchanged.length} skill(s)`));
479
+ }
480
+ // AC: @skill-drift-detection ac-3 - Show warning for skipped drifted skills
481
+ if (skippedDrifted.length > 0) {
482
+ console.log();
483
+ console.log(chalk.yellow(`Skipped: ${skippedDrifted.length} drifted skill(s)`));
484
+ for (const r of skippedDrifted) {
485
+ console.log(` ${chalk.yellow("!")} ${r.id} ${chalk.gray(`[${r.platform}]`)}: ${r.skipReason || "drifted"}`);
486
+ }
487
+ console.log();
488
+ console.log(chalk.yellow("Use --force to overwrite drifted skills"));
489
+ }
490
+ // Clean results
491
+ if (cleanResults.length > 0) {
492
+ const removed = cleanResults.filter((r) => r.action === "removed");
493
+ const skipped = cleanResults.filter((r) => r.action === "skipped");
494
+ if (removed.length > 0) {
495
+ console.log();
496
+ console.log(chalk.red(`Removed: ${removed.length} orphaned skill(s)`));
497
+ for (const r of removed) {
498
+ // AC: @multi-platform-render-cli ac-6 - Platform column in clean output
499
+ console.log(` ${chalk.red("-")} ${r.id} ${chalk.gray(`[${r.platform}]`)}`);
500
+ }
501
+ }
502
+ if (skipped.length > 0) {
503
+ console.log(chalk.gray(`Skipped: ${skipped.length} unmanaged skill(s)`));
504
+ }
505
+ }
506
+ // Summary
507
+ console.log();
508
+ if (dryRun) {
509
+ console.log(chalk.yellow("No changes were made. Run without --dry-run to apply."));
510
+ }
511
+ else {
512
+ const changedCount = created.length + updated.length;
513
+ const cleanedCount = cleanResults.filter((r) => r.action === "removed").length;
514
+ if (changedCount > 0 || cleanedCount > 0) {
515
+ success(`Rendered ${changedCount} skill(s)${cleanedCount > 0 ? `, cleaned ${cleanedCount}` : ""}`);
516
+ }
517
+ else {
518
+ console.log(chalk.gray("No changes needed"));
519
+ }
520
+ }
521
+ });
522
+ }
523
+ catch (err) {
524
+ error("Failed to render skills", err);
525
+ process.exit(EXIT_CODES.ERROR);
526
+ }
527
+ });
528
+ // AC: @skill-render-cli ac-3 - kspec skill status
529
+ // AC: @multi-platform-render-cli ac-3 - Per-platform status rows
530
+ skill
531
+ .command("status")
532
+ .description("Show sync status of rendered skills")
533
+ .action(async () => {
534
+ try {
535
+ const ctx = await initContext();
536
+ if (!ctx.manifestPath) {
537
+ error(errors.project.noKspecProject);
538
+ console.log(chalk.gray("Try: kspec init to initialize a kspec project"));
539
+ process.exit(EXIT_CODES.ERROR);
540
+ }
541
+ const metaCtx = await loadMetaContext(ctx);
542
+ const projectRoot = ctx.rootDir;
543
+ // Check all skills (not just claude-code)
544
+ const skillsToCheck = metaCtx.skills;
545
+ if (skillsToCheck.length === 0) {
546
+ console.log(chalk.yellow("No skills found"));
547
+ return;
548
+ }
549
+ // AC: @multi-platform-render-cli ac-3 - Build status per skill-platform pair
550
+ const statusResults = [];
551
+ for (const skill of skillsToCheck) {
552
+ for (const platform of skill.platforms) {
553
+ const renderer = getRenderer(platform);
554
+ if (!renderer) {
555
+ // Unregistered platform - show as not-rendered
556
+ statusResults.push({
557
+ id: skill.id,
558
+ platform,
559
+ status: "not-rendered",
560
+ docsStatus: "no-docs",
561
+ warning: `unregistered platform '${platform}'`,
562
+ });
563
+ continue;
564
+ }
565
+ const status = await getMultiPlatformSyncStatus(ctx, projectRoot, skill, renderer);
566
+ statusResults.push(status);
567
+ }
568
+ }
569
+ // Output results
570
+ output(statusResults, () => {
571
+ // AC: @multi-platform-render-cli ac-3 - Table shows Platform column
572
+ const table = new Table({
573
+ head: [
574
+ chalk.bold("ID"),
575
+ chalk.bold("Platform"),
576
+ chalk.bold("Status"),
577
+ chalk.bold("Docs"),
578
+ ],
579
+ style: {
580
+ head: [],
581
+ border: [],
582
+ },
583
+ });
584
+ for (const result of statusResults) {
585
+ const statusColor = result.status === "in-sync"
586
+ ? chalk.green
587
+ : result.status === "drifted"
588
+ ? chalk.yellow
589
+ : result.status === "plugin-provided"
590
+ ? chalk.cyan
591
+ : chalk.gray;
592
+ const docsStatusColor = result.docsStatus === "in-sync"
593
+ ? chalk.green
594
+ : result.docsStatus === "drifted"
595
+ ? chalk.yellow
596
+ : chalk.gray;
597
+ table.push([
598
+ result.id,
599
+ result.platform,
600
+ statusColor(result.status + (result.warning ? " (!)" : "")),
601
+ docsStatusColor(result.docsStatus || "-"),
602
+ ]);
603
+ }
604
+ console.log(table.toString());
605
+ // Summary
606
+ const inSync = statusResults.filter((r) => r.status === "in-sync").length;
607
+ const drifted = statusResults.filter((r) => r.status === "drifted").length;
608
+ const notRendered = statusResults.filter((r) => r.status === "not-rendered").length;
609
+ const pluginProvided = statusResults.filter((r) => r.status === "plugin-provided").length;
610
+ const warningCount = statusResults.filter((r) => r.warning).length;
611
+ console.log();
612
+ if (drifted > 0) {
613
+ console.log(chalk.yellow(`${drifted} skill(s) drifted - run 'kspec skill render' to sync`));
614
+ }
615
+ if (notRendered > 0) {
616
+ console.log(chalk.gray(`${notRendered} skill(s) not rendered`));
617
+ }
618
+ if (pluginProvided > 0) {
619
+ console.log(chalk.cyan(`${pluginProvided} skill(s) plugin-provided`));
620
+ }
621
+ if (warningCount > 0) {
622
+ console.log(chalk.yellow(`${warningCount} skill(s) with warnings`));
623
+ }
624
+ if (inSync + pluginProvided === statusResults.length) {
625
+ console.log(chalk.green("All skills in sync"));
626
+ }
627
+ });
628
+ }
629
+ catch (err) {
630
+ error("Failed to check skill status", err);
631
+ process.exit(EXIT_CODES.ERROR);
632
+ }
633
+ });
634
+ // AC: @skill-render-cli ac-4 - kspec skill diff
635
+ skill
636
+ .command("diff <ref>")
637
+ .description("Show diff between source and rendered skill")
638
+ .action(async (ref) => {
639
+ try {
640
+ const ctx = await initContext();
641
+ if (!ctx.manifestPath) {
642
+ error(errors.project.noKspecProject);
643
+ console.log(chalk.gray("Try: kspec init to initialize a kspec project"));
644
+ process.exit(EXIT_CODES.ERROR);
645
+ }
646
+ const metaCtx = await loadMetaContext(ctx);
647
+ const item = findMetaItemByRef(metaCtx, ref);
648
+ if (!item) {
649
+ error(`Skill not found: ${ref}`);
650
+ console.log(chalk.gray("Try: kspec skill list"));
651
+ process.exit(EXIT_CODES.NOT_FOUND);
652
+ }
653
+ // Check it's a skill (uses _type discriminant)
654
+ if (!isSkill(item)) {
655
+ error(`Item ${ref} is not a skill`);
656
+ process.exit(EXIT_CODES.ERROR);
657
+ }
658
+ const skill = item;
659
+ const projectRoot = ctx.rootDir;
660
+ // Core skills on claude-code are plugin-provided
661
+ if (skill.origin === "core") {
662
+ output({
663
+ id: skill.id,
664
+ hasDiff: false,
665
+ diff: [],
666
+ pluginProvided: true,
667
+ }, () => {
668
+ console.log(chalk.blue(`${skill.id}: plugin-provided (no local render to diff)`));
669
+ });
670
+ return;
671
+ }
672
+ // Get expected rendered content
673
+ const expectedContent = await getExpectedRenderedContent(ctx, skill);
674
+ // Get actual rendered content
675
+ const renderedPath = path.join(projectRoot, ".claude/skills", skill.id, "SKILL.md");
676
+ let actualContent = "";
677
+ try {
678
+ actualContent = await fs.readFile(renderedPath, "utf-8");
679
+ }
680
+ catch {
681
+ // File doesn't exist
682
+ }
683
+ // Generate diff
684
+ const diff = generateUnifiedDiff(actualContent, expectedContent, `a/${skill.id}/SKILL.md`, `b/${skill.id}/SKILL.md`);
685
+ // Output
686
+ output({
687
+ id: skill.id,
688
+ hasDiff: diff.length > 0,
689
+ diff,
690
+ }, () => {
691
+ if (diff.length === 0) {
692
+ console.log(chalk.green(`${skill.id}: in sync`));
693
+ }
694
+ else {
695
+ console.log(chalk.yellow(`${skill.id}: drifted`));
696
+ console.log();
697
+ // Print colored diff
698
+ for (const line of diff) {
699
+ if (line.startsWith("+") && !line.startsWith("+++")) {
700
+ console.log(chalk.green(line));
701
+ }
702
+ else if (line.startsWith("-") && !line.startsWith("---")) {
703
+ console.log(chalk.red(line));
704
+ }
705
+ else if (line.startsWith("@@")) {
706
+ console.log(chalk.cyan(line));
707
+ }
708
+ else {
709
+ console.log(line);
710
+ }
711
+ }
712
+ }
713
+ });
714
+ }
715
+ catch (err) {
716
+ error("Failed to generate diff", err);
717
+ process.exit(EXIT_CODES.ERROR);
718
+ }
719
+ });
720
+ // AC: @skill-drift-detection-improvements ac-2 - kspec skill verify
721
+ skill
722
+ .command("verify")
723
+ .description("Verify rendered skills match their source (reports drift with guidance)")
724
+ .option("--json", "Output as JSON")
725
+ .action(async (options) => {
726
+ try {
727
+ const ctx = await initContext();
728
+ if (!ctx.manifestPath) {
729
+ error(errors.project.noKspecProject);
730
+ console.log(chalk.gray("Try: kspec init to initialize a kspec project"));
731
+ process.exit(EXIT_CODES.ERROR);
732
+ }
733
+ const metaCtx = await loadMetaContext(ctx);
734
+ const projectRoot = ctx.rootDir;
735
+ const skillsToCheck = metaCtx.skills;
736
+ if (skillsToCheck.length === 0) {
737
+ output([], () => {
738
+ console.log(chalk.yellow("No skills found"));
739
+ });
740
+ return;
741
+ }
742
+ const results = [];
743
+ for (const skill of skillsToCheck) {
744
+ for (const platform of skill.platforms) {
745
+ const renderer = getRenderer(platform);
746
+ if (!renderer) {
747
+ results.push({
748
+ id: skill.id,
749
+ platform,
750
+ status: "not-rendered",
751
+ guidance: `Unregistered platform '${platform}'. No renderer available.`,
752
+ });
753
+ continue;
754
+ }
755
+ const driftStatus = await renderer.checkDrift(ctx.specDir, projectRoot, skill.id, { origin: skill.origin });
756
+ switch (driftStatus) {
757
+ case "in-sync":
758
+ results.push({ id: skill.id, platform, status: "ok" });
759
+ break;
760
+ case "drifted":
761
+ results.push({
762
+ id: skill.id,
763
+ platform,
764
+ status: "drifted",
765
+ guidance: `Rendered file has been modified. Run 'kspec skill render ${skill.id} --force' to overwrite with source, or 'kspec skill diff ${skill.id}' to review changes.`,
766
+ });
767
+ break;
768
+ case "not-rendered":
769
+ results.push({
770
+ id: skill.id,
771
+ platform,
772
+ status: "not-rendered",
773
+ guidance: `Not yet rendered. Run 'kspec skill render ${skill.id}' to generate.`,
774
+ });
775
+ break;
776
+ case "no-hash":
777
+ results.push({
778
+ id: skill.id,
779
+ platform,
780
+ status: "no-hash",
781
+ guidance: `No render hash stored. Run 'kspec skill render ${skill.id}' to render and store hash.`,
782
+ });
783
+ break;
784
+ }
785
+ }
786
+ }
787
+ const driftedResults = results.filter((r) => r.status === "drifted");
788
+ const okResults = results.filter((r) => r.status === "ok");
789
+ const notRenderedResults = results.filter((r) => r.status === "not-rendered");
790
+ const noHashResults = results.filter((r) => r.status === "no-hash");
791
+ output(results, () => {
792
+ if (driftedResults.length === 0 && notRenderedResults.length === 0 && noHashResults.length === 0) {
793
+ console.log(chalk.green(`All ${okResults.length} rendered skill(s) verified — no drift detected.`));
794
+ return;
795
+ }
796
+ // Show drifted skills with guidance
797
+ if (driftedResults.length > 0) {
798
+ console.log(chalk.yellow.bold(`\nDrifted (${driftedResults.length}):`));
799
+ for (const r of driftedResults) {
800
+ console.log(` ${chalk.yellow("●")} ${r.id} [${r.platform}]`);
801
+ console.log(` ${chalk.gray(r.guidance)}`);
802
+ }
803
+ }
804
+ // Show not-rendered
805
+ if (notRenderedResults.length > 0) {
806
+ console.log(chalk.gray.bold(`\nNot rendered (${notRenderedResults.length}):`));
807
+ for (const r of notRenderedResults) {
808
+ console.log(` ${chalk.gray("○")} ${r.id} [${r.platform}]`);
809
+ console.log(` ${chalk.gray(r.guidance)}`);
810
+ }
811
+ }
812
+ // Show no-hash
813
+ if (noHashResults.length > 0) {
814
+ console.log(chalk.gray.bold(`\nNo hash (${noHashResults.length}):`));
815
+ for (const r of noHashResults) {
816
+ console.log(` ${chalk.gray("○")} ${r.id} [${r.platform}]`);
817
+ console.log(` ${chalk.gray(r.guidance)}`);
818
+ }
819
+ }
820
+ // Summary line
821
+ if (okResults.length > 0) {
822
+ console.log(chalk.green(`\n${okResults.length} skill(s) verified OK.`));
823
+ }
824
+ // Actionable summary
825
+ if (driftedResults.length > 0) {
826
+ console.log(chalk.yellow(`\nTo sync all drifted skills: kspec skill render --force`));
827
+ }
828
+ });
829
+ // Exit with non-zero if any skills drifted
830
+ if (driftedResults.length > 0) {
831
+ process.exit(EXIT_CODES.ERROR);
832
+ }
833
+ }
834
+ catch (err) {
835
+ error("Failed to verify skills", err);
836
+ process.exit(EXIT_CODES.ERROR);
837
+ }
838
+ });
839
+ }
840
+ //# sourceMappingURL=skill-diff.js.map