@qodo/sdk 0.13.3 → 2.0.0-next.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 (764) hide show
  1. package/LICENSE +31 -118
  2. package/README.md +133 -121
  3. package/bin/qodo-skills.mjs +13 -0
  4. package/bundled-skills/code-review/SKILL.md +41 -0
  5. package/bundled-skills/pr-summary/SKILL.md +59 -0
  6. package/bundled-skills/test-gen/SKILL.md +47 -0
  7. package/dist/auth/index.browser.d.ts +38 -0
  8. package/dist/auth/index.browser.d.ts.map +1 -0
  9. package/dist/auth/index.browser.js +62 -0
  10. package/dist/auth/index.browser.js.map +1 -0
  11. package/dist/auth/index.d.ts +44 -30
  12. package/dist/auth/index.d.ts.map +1 -1
  13. package/dist/auth/index.js +57 -110
  14. package/dist/auth/index.js.map +1 -1
  15. package/dist/client/AgentsClient.d.ts +33 -0
  16. package/dist/client/AgentsClient.d.ts.map +1 -0
  17. package/dist/client/AgentsClient.js +40 -0
  18. package/dist/client/AgentsClient.js.map +1 -0
  19. package/dist/client/ArtifactsClient.d.ts +43 -0
  20. package/dist/client/ArtifactsClient.d.ts.map +1 -0
  21. package/dist/client/ArtifactsClient.js +54 -0
  22. package/dist/client/ArtifactsClient.js.map +1 -0
  23. package/dist/client/BulletinClient.d.ts +45 -0
  24. package/dist/client/BulletinClient.d.ts.map +1 -0
  25. package/dist/client/BulletinClient.js +51 -0
  26. package/dist/client/BulletinClient.js.map +1 -0
  27. package/dist/client/InfoClient.d.ts +58 -0
  28. package/dist/client/InfoClient.d.ts.map +1 -0
  29. package/dist/client/InfoClient.js +135 -0
  30. package/dist/client/InfoClient.js.map +1 -0
  31. package/dist/client/PipelineClient.d.ts +162 -0
  32. package/dist/client/PipelineClient.d.ts.map +1 -0
  33. package/dist/client/PipelineClient.js +340 -0
  34. package/dist/client/PipelineClient.js.map +1 -0
  35. package/dist/client/QarRegistryClient.d.ts +396 -0
  36. package/dist/client/QarRegistryClient.d.ts.map +1 -0
  37. package/dist/client/QarRegistryClient.js +536 -0
  38. package/dist/client/QarRegistryClient.js.map +1 -0
  39. package/dist/client/QodoClient.d.ts +296 -0
  40. package/dist/client/QodoClient.d.ts.map +1 -0
  41. package/dist/client/QodoClient.js +803 -0
  42. package/dist/client/QodoClient.js.map +1 -0
  43. package/dist/client/SpecsClient.d.ts +121 -0
  44. package/dist/client/SpecsClient.d.ts.map +1 -0
  45. package/dist/client/SpecsClient.js +252 -0
  46. package/dist/client/SpecsClient.js.map +1 -0
  47. package/dist/client/StateClient.d.ts +35 -0
  48. package/dist/client/StateClient.d.ts.map +1 -0
  49. package/dist/client/StateClient.js +36 -0
  50. package/dist/client/StateClient.js.map +1 -0
  51. package/dist/client/TaskClient.d.ts +706 -0
  52. package/dist/client/TaskClient.d.ts.map +1 -0
  53. package/dist/client/TaskClient.js +2522 -0
  54. package/dist/client/TaskClient.js.map +1 -0
  55. package/dist/client/ToolClient.d.ts +278 -0
  56. package/dist/client/ToolClient.d.ts.map +1 -0
  57. package/dist/client/ToolClient.js +1115 -0
  58. package/dist/client/ToolClient.js.map +1 -0
  59. package/dist/client/a2a/index.d.ts +10 -0
  60. package/dist/client/a2a/index.d.ts.map +1 -0
  61. package/dist/client/a2a/index.js +9 -0
  62. package/dist/client/a2a/index.js.map +1 -0
  63. package/dist/client/a2a/registerA2A.d.ts +170 -0
  64. package/dist/client/a2a/registerA2A.d.ts.map +1 -0
  65. package/dist/client/a2a/registerA2A.js +85 -0
  66. package/dist/client/a2a/registerA2A.js.map +1 -0
  67. package/dist/client/connection.d.ts +800 -0
  68. package/dist/client/connection.d.ts.map +1 -0
  69. package/dist/client/connection.js +2020 -0
  70. package/dist/client/connection.js.map +1 -0
  71. package/dist/client/errors.d.ts +735 -0
  72. package/dist/client/errors.d.ts.map +1 -0
  73. package/dist/client/errors.js +921 -0
  74. package/dist/client/errors.js.map +1 -0
  75. package/dist/client/index.d.ts +26 -0
  76. package/dist/client/index.d.ts.map +1 -0
  77. package/dist/client/index.js +20 -0
  78. package/dist/client/index.js.map +1 -0
  79. package/dist/client/inlineGraph.d.ts +66 -0
  80. package/dist/client/inlineGraph.d.ts.map +1 -0
  81. package/dist/client/inlineGraph.js +500 -0
  82. package/dist/client/inlineGraph.js.map +1 -0
  83. package/dist/client/internal/thenable.d.ts +27 -0
  84. package/dist/client/internal/thenable.d.ts.map +1 -0
  85. package/dist/client/internal/thenable.js +31 -0
  86. package/dist/client/internal/thenable.js.map +1 -0
  87. package/dist/client/iterator.d.ts +32 -0
  88. package/dist/client/iterator.d.ts.map +1 -0
  89. package/dist/client/iterator.js +73 -0
  90. package/dist/client/iterator.js.map +1 -0
  91. package/dist/client/mcp/McpClientPool.browser.d.ts +76 -0
  92. package/dist/client/mcp/McpClientPool.browser.d.ts.map +1 -0
  93. package/dist/client/mcp/McpClientPool.browser.js +78 -0
  94. package/dist/client/mcp/McpClientPool.browser.js.map +1 -0
  95. package/dist/client/mcp/McpClientPool.d.ts +236 -0
  96. package/dist/client/mcp/McpClientPool.d.ts.map +1 -0
  97. package/dist/client/mcp/McpClientPool.js +585 -0
  98. package/dist/client/mcp/McpClientPool.js.map +1 -0
  99. package/dist/client/mcp/projection.d.ts +109 -0
  100. package/dist/client/mcp/projection.d.ts.map +1 -0
  101. package/dist/client/mcp/projection.js +446 -0
  102. package/dist/client/mcp/projection.js.map +1 -0
  103. package/dist/client/mcp/substituteEnv.browser.d.ts +18 -0
  104. package/dist/client/mcp/substituteEnv.browser.d.ts.map +1 -0
  105. package/dist/client/mcp/substituteEnv.browser.js +20 -0
  106. package/dist/client/mcp/substituteEnv.browser.js.map +1 -0
  107. package/dist/client/mcp/substituteEnv.d.ts +45 -0
  108. package/dist/client/mcp/substituteEnv.d.ts.map +1 -0
  109. package/dist/client/mcp/substituteEnv.js +63 -0
  110. package/dist/client/mcp/substituteEnv.js.map +1 -0
  111. package/dist/client/observers.d.ts +57 -0
  112. package/dist/client/observers.d.ts.map +1 -0
  113. package/dist/client/observers.js +203 -0
  114. package/dist/client/observers.js.map +1 -0
  115. package/dist/client/options.d.ts +269 -0
  116. package/dist/client/options.d.ts.map +1 -0
  117. package/dist/client/options.js +9 -0
  118. package/dist/client/options.js.map +1 -0
  119. package/dist/client/tools/_readlineApprovalPrompt.browser.d.ts +17 -0
  120. package/dist/client/tools/_readlineApprovalPrompt.browser.d.ts.map +1 -0
  121. package/dist/client/tools/_readlineApprovalPrompt.browser.js +24 -0
  122. package/dist/client/tools/_readlineApprovalPrompt.browser.js.map +1 -0
  123. package/dist/client/tools/_readlineApprovalPrompt.d.ts +33 -0
  124. package/dist/client/tools/_readlineApprovalPrompt.d.ts.map +1 -0
  125. package/dist/client/tools/_readlineApprovalPrompt.js +90 -0
  126. package/dist/client/tools/_readlineApprovalPrompt.js.map +1 -0
  127. package/dist/client/tools/approval.d.ts +280 -0
  128. package/dist/client/tools/approval.d.ts.map +1 -0
  129. package/dist/client/tools/approval.js +229 -0
  130. package/dist/client/tools/approval.js.map +1 -0
  131. package/dist/client/tools/bindFunctionToolDefs.d.ts +156 -0
  132. package/dist/client/tools/bindFunctionToolDefs.d.ts.map +1 -0
  133. package/dist/client/tools/bindFunctionToolDefs.js +360 -0
  134. package/dist/client/tools/bindFunctionToolDefs.js.map +1 -0
  135. package/dist/client/tools/defineFunctionTool.d.ts +277 -0
  136. package/dist/client/tools/defineFunctionTool.d.ts.map +1 -0
  137. package/dist/client/tools/defineFunctionTool.js +190 -0
  138. package/dist/client/tools/defineFunctionTool.js.map +1 -0
  139. package/dist/client/transport.browser.d.ts +20 -0
  140. package/dist/client/transport.browser.d.ts.map +1 -0
  141. package/dist/client/transport.browser.js +29 -0
  142. package/dist/client/transport.browser.js.map +1 -0
  143. package/dist/client/transport.d.ts +47 -0
  144. package/dist/client/transport.d.ts.map +1 -0
  145. package/dist/client/transport.js +102 -0
  146. package/dist/client/transport.js.map +1 -0
  147. package/dist/client/transport.shared.d.ts +30 -0
  148. package/dist/client/transport.shared.d.ts.map +1 -0
  149. package/dist/client/transport.shared.js +40 -0
  150. package/dist/client/transport.shared.js.map +1 -0
  151. package/dist/client/uuid.d.ts +32 -0
  152. package/dist/client/uuid.d.ts.map +1 -0
  153. package/dist/client/uuid.js +65 -0
  154. package/dist/client/uuid.js.map +1 -0
  155. package/dist/index.d.ts +88 -39
  156. package/dist/index.d.ts.map +1 -1
  157. package/dist/index.js +166 -43
  158. package/dist/index.js.map +1 -1
  159. package/dist/observability/attributes.d.ts +136 -0
  160. package/dist/observability/attributes.d.ts.map +1 -0
  161. package/dist/observability/attributes.js +184 -0
  162. package/dist/observability/attributes.js.map +1 -0
  163. package/dist/observability/index.d.ts +14 -0
  164. package/dist/observability/index.d.ts.map +1 -0
  165. package/dist/observability/index.js +11 -0
  166. package/dist/observability/index.js.map +1 -0
  167. package/dist/observability/resolveOTel.browser.d.ts +13 -0
  168. package/dist/observability/resolveOTel.browser.d.ts.map +1 -0
  169. package/dist/observability/resolveOTel.browser.js +14 -0
  170. package/dist/observability/resolveOTel.browser.js.map +1 -0
  171. package/dist/observability/resolveOTel.d.ts +28 -0
  172. package/dist/observability/resolveOTel.d.ts.map +1 -0
  173. package/dist/observability/resolveOTel.js +74 -0
  174. package/dist/observability/resolveOTel.js.map +1 -0
  175. package/dist/observability/spans.d.ts +198 -0
  176. package/dist/observability/spans.d.ts.map +1 -0
  177. package/dist/observability/spans.js +300 -0
  178. package/dist/observability/spans.js.map +1 -0
  179. package/dist/observability/traceContext.d.ts +51 -0
  180. package/dist/observability/traceContext.d.ts.map +1 -0
  181. package/dist/observability/traceContext.js +151 -0
  182. package/dist/observability/traceContext.js.map +1 -0
  183. package/dist/observability/transportMetrics.d.ts +58 -0
  184. package/dist/observability/transportMetrics.d.ts.map +1 -0
  185. package/dist/observability/transportMetrics.js +55 -0
  186. package/dist/observability/transportMetrics.js.map +1 -0
  187. package/dist/qar/agentSpec.d.ts +93 -0
  188. package/dist/qar/agentSpec.d.ts.map +1 -0
  189. package/dist/qar/agentSpec.js +184 -0
  190. package/dist/qar/agentSpec.js.map +1 -0
  191. package/dist/qar/clientEvents.d.ts +86 -0
  192. package/dist/qar/clientEvents.d.ts.map +1 -0
  193. package/dist/qar/clientEvents.js +36 -0
  194. package/dist/qar/clientEvents.js.map +1 -0
  195. package/dist/qar/envelopes.d.ts +227 -0
  196. package/dist/qar/envelopes.d.ts.map +1 -0
  197. package/dist/qar/envelopes.js +67 -0
  198. package/dist/qar/envelopes.js.map +1 -0
  199. package/dist/qar/generated/envelope.d.ts +332 -0
  200. package/dist/qar/generated/envelope.d.ts.map +1 -0
  201. package/dist/qar/generated/envelope.js +15 -0
  202. package/dist/qar/generated/envelope.js.map +1 -0
  203. package/dist/qar/generated/qar-info.d.ts +76 -0
  204. package/dist/qar/generated/qar-info.d.ts.map +1 -0
  205. package/dist/qar/generated/qar-info.js +15 -0
  206. package/dist/qar/generated/qar-info.js.map +1 -0
  207. package/dist/qar/generated/qodo-task-start-payload.d.ts +54 -0
  208. package/dist/qar/generated/qodo-task-start-payload.d.ts.map +1 -0
  209. package/dist/qar/generated/qodo-task-start-payload.js +15 -0
  210. package/dist/qar/generated/qodo-task-start-payload.js.map +1 -0
  211. package/dist/qar/ids.d.ts +19 -0
  212. package/dist/qar/ids.d.ts.map +1 -0
  213. package/dist/qar/ids.js +11 -0
  214. package/dist/qar/ids.js.map +1 -0
  215. package/dist/qar/index.d.ts +24 -0
  216. package/dist/qar/index.d.ts.map +1 -0
  217. package/dist/qar/index.js +16 -0
  218. package/dist/qar/index.js.map +1 -0
  219. package/dist/qar/info.d.ts +37 -0
  220. package/dist/qar/info.d.ts.map +1 -0
  221. package/dist/qar/info.js +17 -0
  222. package/dist/qar/info.js.map +1 -0
  223. package/dist/qar/json.d.ts +14 -0
  224. package/dist/qar/json.d.ts.map +1 -0
  225. package/dist/qar/json.js +9 -0
  226. package/dist/qar/json.js.map +1 -0
  227. package/dist/qar/payloads.d.ts +480 -0
  228. package/dist/qar/payloads.d.ts.map +1 -0
  229. package/dist/qar/payloads.js +37 -0
  230. package/dist/qar/payloads.js.map +1 -0
  231. package/dist/qar/specs.d.ts +604 -0
  232. package/dist/qar/specs.d.ts.map +1 -0
  233. package/dist/qar/specs.js +29 -0
  234. package/dist/qar/specs.js.map +1 -0
  235. package/dist/qar/taskEvents.d.ts +25 -0
  236. package/dist/qar/taskEvents.d.ts.map +1 -0
  237. package/dist/qar/taskEvents.js +22 -0
  238. package/dist/qar/taskEvents.js.map +1 -0
  239. package/dist/qar/trace.d.ts +12 -0
  240. package/dist/qar/trace.d.ts.map +1 -0
  241. package/dist/qar/trace.js +12 -0
  242. package/dist/qar/trace.js.map +1 -0
  243. package/dist/skills/activation.d.ts +177 -0
  244. package/dist/skills/activation.d.ts.map +1 -0
  245. package/dist/skills/activation.js +428 -0
  246. package/dist/skills/activation.js.map +1 -0
  247. package/dist/skills/cli/index.browser.d.ts +18 -0
  248. package/dist/skills/cli/index.browser.d.ts.map +1 -0
  249. package/dist/skills/cli/index.browser.js +27 -0
  250. package/dist/skills/cli/index.browser.js.map +1 -0
  251. package/dist/skills/cli/index.d.ts +37 -0
  252. package/dist/skills/cli/index.d.ts.map +1 -0
  253. package/dist/skills/cli/index.js +494 -0
  254. package/dist/skills/cli/index.js.map +1 -0
  255. package/dist/skills/events.d.ts +255 -0
  256. package/dist/skills/events.d.ts.map +1 -0
  257. package/dist/skills/events.js +224 -0
  258. package/dist/skills/events.js.map +1 -0
  259. package/dist/skills/index.d.ts +45 -0
  260. package/dist/skills/index.d.ts.map +1 -0
  261. package/dist/skills/index.js +34 -0
  262. package/dist/skills/index.js.map +1 -0
  263. package/dist/skills/inject.d.ts +57 -0
  264. package/dist/skills/inject.d.ts.map +1 -0
  265. package/dist/skills/inject.js +162 -0
  266. package/dist/skills/inject.js.map +1 -0
  267. package/dist/skills/lockfile.browser.d.ts +56 -0
  268. package/dist/skills/lockfile.browser.d.ts.map +1 -0
  269. package/dist/skills/lockfile.browser.js +55 -0
  270. package/dist/skills/lockfile.browser.js.map +1 -0
  271. package/dist/skills/lockfile.d.ts +137 -0
  272. package/dist/skills/lockfile.d.ts.map +1 -0
  273. package/dist/skills/lockfile.js +423 -0
  274. package/dist/skills/lockfile.js.map +1 -0
  275. package/dist/skills/manager.browser.d.ts +94 -0
  276. package/dist/skills/manager.browser.d.ts.map +1 -0
  277. package/dist/skills/manager.browser.js +159 -0
  278. package/dist/skills/manager.browser.js.map +1 -0
  279. package/dist/skills/manager.d.ts +362 -0
  280. package/dist/skills/manager.d.ts.map +1 -0
  281. package/dist/skills/manager.js +1386 -0
  282. package/dist/skills/manager.js.map +1 -0
  283. package/dist/skills/mcp/index.d.ts +15 -0
  284. package/dist/skills/mcp/index.d.ts.map +1 -0
  285. package/dist/skills/mcp/index.js +12 -0
  286. package/dist/skills/mcp/index.js.map +1 -0
  287. package/dist/skills/mcp/path.browser.d.ts +27 -0
  288. package/dist/skills/mcp/path.browser.d.ts.map +1 -0
  289. package/dist/skills/mcp/path.browser.js +33 -0
  290. package/dist/skills/mcp/path.browser.js.map +1 -0
  291. package/dist/skills/mcp/path.d.ts +57 -0
  292. package/dist/skills/mcp/path.d.ts.map +1 -0
  293. package/dist/skills/mcp/path.js +150 -0
  294. package/dist/skills/mcp/path.js.map +1 -0
  295. package/dist/skills/mcp/server.browser.d.ts +32 -0
  296. package/dist/skills/mcp/server.browser.d.ts.map +1 -0
  297. package/dist/skills/mcp/server.browser.js +53 -0
  298. package/dist/skills/mcp/server.browser.js.map +1 -0
  299. package/dist/skills/mcp/server.d.ts +144 -0
  300. package/dist/skills/mcp/server.d.ts.map +1 -0
  301. package/dist/skills/mcp/server.js +841 -0
  302. package/dist/skills/mcp/server.js.map +1 -0
  303. package/dist/skills/mcp/types.d.ts +72 -0
  304. package/dist/skills/mcp/types.d.ts.map +1 -0
  305. package/dist/skills/mcp/types.js +20 -0
  306. package/dist/skills/mcp/types.js.map +1 -0
  307. package/dist/skills/mcp/wireDefs.d.ts +58 -0
  308. package/dist/skills/mcp/wireDefs.d.ts.map +1 -0
  309. package/dist/skills/mcp/wireDefs.js +141 -0
  310. package/dist/skills/mcp/wireDefs.js.map +1 -0
  311. package/dist/skills/parser.d.ts +63 -0
  312. package/dist/skills/parser.d.ts.map +1 -0
  313. package/dist/skills/parser.js +755 -0
  314. package/dist/skills/parser.js.map +1 -0
  315. package/dist/skills/prefilter.d.ts +104 -0
  316. package/dist/skills/prefilter.d.ts.map +1 -0
  317. package/dist/skills/prefilter.js +398 -0
  318. package/dist/skills/prefilter.js.map +1 -0
  319. package/dist/skills/preprocess.d.ts +169 -0
  320. package/dist/skills/preprocess.d.ts.map +1 -0
  321. package/dist/skills/preprocess.js +535 -0
  322. package/dist/skills/preprocess.js.map +1 -0
  323. package/dist/skills/render.d.ts +83 -0
  324. package/dist/skills/render.d.ts.map +1 -0
  325. package/dist/skills/render.js +397 -0
  326. package/dist/skills/render.js.map +1 -0
  327. package/dist/skills/sources/index.browser.d.ts +29 -0
  328. package/dist/skills/sources/index.browser.d.ts.map +1 -0
  329. package/dist/skills/sources/index.browser.js +16 -0
  330. package/dist/skills/sources/index.browser.js.map +1 -0
  331. package/dist/skills/sources/index.d.ts +59 -0
  332. package/dist/skills/sources/index.d.ts.map +1 -0
  333. package/dist/skills/sources/index.js +471 -0
  334. package/dist/skills/sources/index.js.map +1 -0
  335. package/dist/skills/sources/walk.browser.d.ts +17 -0
  336. package/dist/skills/sources/walk.browser.d.ts.map +1 -0
  337. package/dist/skills/sources/walk.browser.js +19 -0
  338. package/dist/skills/sources/walk.browser.js.map +1 -0
  339. package/dist/skills/sources/walk.d.ts +68 -0
  340. package/dist/skills/sources/walk.d.ts.map +1 -0
  341. package/dist/skills/sources/walk.js +264 -0
  342. package/dist/skills/sources/walk.js.map +1 -0
  343. package/dist/skills/substitute.d.ts +87 -0
  344. package/dist/skills/substitute.d.ts.map +1 -0
  345. package/dist/skills/substitute.js +322 -0
  346. package/dist/skills/substitute.js.map +1 -0
  347. package/dist/skills/testing/SkillKit.browser.d.ts +62 -0
  348. package/dist/skills/testing/SkillKit.browser.d.ts.map +1 -0
  349. package/dist/skills/testing/SkillKit.browser.js +41 -0
  350. package/dist/skills/testing/SkillKit.browser.js.map +1 -0
  351. package/dist/skills/testing/SkillKit.d.ts +130 -0
  352. package/dist/skills/testing/SkillKit.d.ts.map +1 -0
  353. package/dist/skills/testing/SkillKit.js +316 -0
  354. package/dist/skills/testing/SkillKit.js.map +1 -0
  355. package/dist/skills/testing/index.d.ts +9 -0
  356. package/dist/skills/testing/index.d.ts.map +1 -0
  357. package/dist/skills/testing/index.js +8 -0
  358. package/dist/skills/testing/index.js.map +1 -0
  359. package/dist/skills/trust.d.ts +72 -0
  360. package/dist/skills/trust.d.ts.map +1 -0
  361. package/dist/skills/trust.js +183 -0
  362. package/dist/skills/trust.js.map +1 -0
  363. package/dist/skills/types.d.ts +627 -0
  364. package/dist/skills/types.d.ts.map +1 -0
  365. package/dist/skills/types.js +85 -0
  366. package/dist/skills/types.js.map +1 -0
  367. package/dist/skills/validator.d.ts +95 -0
  368. package/dist/skills/validator.d.ts.map +1 -0
  369. package/dist/skills/validator.js +486 -0
  370. package/dist/skills/validator.js.map +1 -0
  371. package/dist/tracing/PipelineTracer.d.ts +35 -22
  372. package/dist/tracing/PipelineTracer.d.ts.map +1 -1
  373. package/dist/tracing/PipelineTracer.js +106 -61
  374. package/dist/tracing/PipelineTracer.js.map +1 -1
  375. package/dist/tracing/SdkTracer.d.ts +63 -61
  376. package/dist/tracing/SdkTracer.d.ts.map +1 -1
  377. package/dist/tracing/SdkTracer.js +185 -177
  378. package/dist/tracing/SdkTracer.js.map +1 -1
  379. package/dist/tracing/index.d.ts +10 -1
  380. package/dist/tracing/index.d.ts.map +1 -1
  381. package/dist/tracing/index.js +9 -0
  382. package/dist/tracing/index.js.map +1 -1
  383. package/dist/tracing/types.d.ts +89 -16
  384. package/dist/tracing/types.d.ts.map +1 -1
  385. package/dist/tracing/types.js +17 -4
  386. package/dist/tracing/types.js.map +1 -1
  387. package/dist/types.d.ts +6 -1
  388. package/dist/types.d.ts.map +1 -1
  389. package/dist/types.js +4 -0
  390. package/dist/types.js.map +1 -1
  391. package/dist/version.d.ts.map +1 -1
  392. package/dist/version.js +10 -20
  393. package/dist/version.js.map +1 -1
  394. package/package.json +53 -39
  395. package/.claude/skills/qodo-agent/SKILL.md +0 -974
  396. package/.claude/skills/qodo-agent/assets/programmatic-agent.ts +0 -407
  397. package/.claude/skills/qodo-agent/references/builtin-tools.md +0 -342
  398. package/.claude/skills/qodo-agent/references/common-issues.md +0 -537
  399. package/bin/rg +0 -0
  400. package/dist/api/agent.d.ts +0 -104
  401. package/dist/api/agent.d.ts.map +0 -1
  402. package/dist/api/agent.js +0 -939
  403. package/dist/api/agent.js.map +0 -1
  404. package/dist/api/analytics.d.ts +0 -43
  405. package/dist/api/analytics.d.ts.map +0 -1
  406. package/dist/api/analytics.js +0 -163
  407. package/dist/api/analytics.js.map +0 -1
  408. package/dist/api/http.d.ts +0 -5
  409. package/dist/api/http.d.ts.map +0 -1
  410. package/dist/api/http.js +0 -62
  411. package/dist/api/http.js.map +0 -1
  412. package/dist/api/index.d.ts +0 -12
  413. package/dist/api/index.d.ts.map +0 -1
  414. package/dist/api/index.js +0 -17
  415. package/dist/api/index.js.map +0 -1
  416. package/dist/api/taskTracking.d.ts +0 -54
  417. package/dist/api/taskTracking.d.ts.map +0 -1
  418. package/dist/api/taskTracking.js +0 -208
  419. package/dist/api/taskTracking.js.map +0 -1
  420. package/dist/api/types.d.ts +0 -93
  421. package/dist/api/types.d.ts.map +0 -1
  422. package/dist/api/types.js +0 -2
  423. package/dist/api/types.js.map +0 -1
  424. package/dist/api/utils.d.ts +0 -8
  425. package/dist/api/utils.d.ts.map +0 -1
  426. package/dist/api/utils.js +0 -63
  427. package/dist/api/utils.js.map +0 -1
  428. package/dist/api/websocket.d.ts +0 -203
  429. package/dist/api/websocket.d.ts.map +0 -1
  430. package/dist/api/websocket.js +0 -1166
  431. package/dist/api/websocket.js.map +0 -1
  432. package/dist/bin/install-skill.d.ts +0 -14
  433. package/dist/bin/install-skill.d.ts.map +0 -1
  434. package/dist/bin/install-skill.js +0 -125
  435. package/dist/bin/install-skill.js.map +0 -1
  436. package/dist/bin/run-helpers.d.ts +0 -34
  437. package/dist/bin/run-helpers.d.ts.map +0 -1
  438. package/dist/bin/run-helpers.js +0 -186
  439. package/dist/bin/run-helpers.js.map +0 -1
  440. package/dist/bin/run.d.ts +0 -13
  441. package/dist/bin/run.d.ts.map +0 -1
  442. package/dist/bin/run.js +0 -57
  443. package/dist/bin/run.js.map +0 -1
  444. package/dist/clients/index.d.ts +0 -10
  445. package/dist/clients/index.d.ts.map +0 -1
  446. package/dist/clients/index.js +0 -8
  447. package/dist/clients/index.js.map +0 -1
  448. package/dist/clients/info/InfoClient.d.ts +0 -37
  449. package/dist/clients/info/InfoClient.d.ts.map +0 -1
  450. package/dist/clients/info/InfoClient.js +0 -69
  451. package/dist/clients/info/InfoClient.js.map +0 -1
  452. package/dist/clients/info/index.d.ts +0 -4
  453. package/dist/clients/info/index.d.ts.map +0 -1
  454. package/dist/clients/info/index.js +0 -2
  455. package/dist/clients/info/index.js.map +0 -1
  456. package/dist/clients/info/types.d.ts +0 -21
  457. package/dist/clients/info/types.d.ts.map +0 -1
  458. package/dist/clients/info/types.js +0 -2
  459. package/dist/clients/info/types.js.map +0 -1
  460. package/dist/clients/sessions/SessionsClient.d.ts +0 -34
  461. package/dist/clients/sessions/SessionsClient.d.ts.map +0 -1
  462. package/dist/clients/sessions/SessionsClient.js +0 -71
  463. package/dist/clients/sessions/SessionsClient.js.map +0 -1
  464. package/dist/clients/sessions/index.d.ts +0 -4
  465. package/dist/clients/sessions/index.d.ts.map +0 -1
  466. package/dist/clients/sessions/index.js +0 -2
  467. package/dist/clients/sessions/index.js.map +0 -1
  468. package/dist/clients/sessions/types.d.ts +0 -20
  469. package/dist/clients/sessions/types.d.ts.map +0 -1
  470. package/dist/clients/sessions/types.js +0 -2
  471. package/dist/clients/sessions/types.js.map +0 -1
  472. package/dist/clients/tools/ToolsClient.d.ts +0 -39
  473. package/dist/clients/tools/ToolsClient.d.ts.map +0 -1
  474. package/dist/clients/tools/ToolsClient.js +0 -95
  475. package/dist/clients/tools/ToolsClient.js.map +0 -1
  476. package/dist/clients/tools/index.d.ts +0 -4
  477. package/dist/clients/tools/index.d.ts.map +0 -1
  478. package/dist/clients/tools/index.js +0 -2
  479. package/dist/clients/tools/index.js.map +0 -1
  480. package/dist/clients/tools/types.d.ts +0 -14
  481. package/dist/clients/tools/types.d.ts.map +0 -1
  482. package/dist/clients/tools/types.js +0 -2
  483. package/dist/clients/tools/types.js.map +0 -1
  484. package/dist/config/ConfigManager.d.ts +0 -43
  485. package/dist/config/ConfigManager.d.ts.map +0 -1
  486. package/dist/config/ConfigManager.js +0 -472
  487. package/dist/config/ConfigManager.js.map +0 -1
  488. package/dist/config/index.d.ts +0 -6
  489. package/dist/config/index.d.ts.map +0 -1
  490. package/dist/config/index.js +0 -7
  491. package/dist/config/index.js.map +0 -1
  492. package/dist/config/urlConfig.d.ts +0 -15
  493. package/dist/config/urlConfig.d.ts.map +0 -1
  494. package/dist/config/urlConfig.js +0 -75
  495. package/dist/config/urlConfig.js.map +0 -1
  496. package/dist/constants/errors.d.ts +0 -2
  497. package/dist/constants/errors.d.ts.map +0 -1
  498. package/dist/constants/errors.js +0 -2
  499. package/dist/constants/errors.js.map +0 -1
  500. package/dist/constants/index.d.ts +0 -7
  501. package/dist/constants/index.d.ts.map +0 -1
  502. package/dist/constants/index.js +0 -11
  503. package/dist/constants/index.js.map +0 -1
  504. package/dist/constants/tools.d.ts +0 -4
  505. package/dist/constants/tools.d.ts.map +0 -1
  506. package/dist/constants/tools.js +0 -4
  507. package/dist/constants/tools.js.map +0 -1
  508. package/dist/constants/versions.d.ts +0 -2
  509. package/dist/constants/versions.d.ts.map +0 -1
  510. package/dist/constants/versions.js +0 -2
  511. package/dist/constants/versions.js.map +0 -1
  512. package/dist/context/buildUserContext.d.ts +0 -18
  513. package/dist/context/buildUserContext.d.ts.map +0 -1
  514. package/dist/context/buildUserContext.js +0 -34
  515. package/dist/context/buildUserContext.js.map +0 -1
  516. package/dist/context/index.d.ts +0 -9
  517. package/dist/context/index.d.ts.map +0 -1
  518. package/dist/context/index.js +0 -9
  519. package/dist/context/index.js.map +0 -1
  520. package/dist/context/messageManager.d.ts +0 -42
  521. package/dist/context/messageManager.d.ts.map +0 -1
  522. package/dist/context/messageManager.js +0 -322
  523. package/dist/context/messageManager.js.map +0 -1
  524. package/dist/context/taskFocus.d.ts +0 -2
  525. package/dist/context/taskFocus.d.ts.map +0 -1
  526. package/dist/context/taskFocus.js +0 -26
  527. package/dist/context/taskFocus.js.map +0 -1
  528. package/dist/context/userInput.d.ts +0 -3
  529. package/dist/context/userInput.d.ts.map +0 -1
  530. package/dist/context/userInput.js +0 -20
  531. package/dist/context/userInput.js.map +0 -1
  532. package/dist/mcp/MCPManager.d.ts +0 -109
  533. package/dist/mcp/MCPManager.d.ts.map +0 -1
  534. package/dist/mcp/MCPManager.js +0 -592
  535. package/dist/mcp/MCPManager.js.map +0 -1
  536. package/dist/mcp/approvedTools.d.ts +0 -4
  537. package/dist/mcp/approvedTools.d.ts.map +0 -1
  538. package/dist/mcp/approvedTools.js +0 -19
  539. package/dist/mcp/approvedTools.js.map +0 -1
  540. package/dist/mcp/baseServer.d.ts +0 -75
  541. package/dist/mcp/baseServer.d.ts.map +0 -1
  542. package/dist/mcp/baseServer.js +0 -107
  543. package/dist/mcp/baseServer.js.map +0 -1
  544. package/dist/mcp/builtinServers.d.ts +0 -15
  545. package/dist/mcp/builtinServers.d.ts.map +0 -1
  546. package/dist/mcp/builtinServers.js +0 -141
  547. package/dist/mcp/builtinServers.js.map +0 -1
  548. package/dist/mcp/dynamicBEServer.d.ts +0 -20
  549. package/dist/mcp/dynamicBEServer.d.ts.map +0 -1
  550. package/dist/mcp/dynamicBEServer.js +0 -52
  551. package/dist/mcp/dynamicBEServer.js.map +0 -1
  552. package/dist/mcp/index.d.ts +0 -18
  553. package/dist/mcp/index.d.ts.map +0 -1
  554. package/dist/mcp/index.js +0 -23
  555. package/dist/mcp/index.js.map +0 -1
  556. package/dist/mcp/mcpInitialization.d.ts +0 -2
  557. package/dist/mcp/mcpInitialization.d.ts.map +0 -1
  558. package/dist/mcp/mcpInitialization.js +0 -56
  559. package/dist/mcp/mcpInitialization.js.map +0 -1
  560. package/dist/mcp/servers/filesystem.d.ts +0 -44
  561. package/dist/mcp/servers/filesystem.d.ts.map +0 -1
  562. package/dist/mcp/servers/filesystem.js +0 -776
  563. package/dist/mcp/servers/filesystem.js.map +0 -1
  564. package/dist/mcp/servers/git.d.ts +0 -18
  565. package/dist/mcp/servers/git.d.ts.map +0 -1
  566. package/dist/mcp/servers/git.js +0 -441
  567. package/dist/mcp/servers/git.js.map +0 -1
  568. package/dist/mcp/servers/ripgrep.d.ts +0 -39
  569. package/dist/mcp/servers/ripgrep.d.ts.map +0 -1
  570. package/dist/mcp/servers/ripgrep.js +0 -550
  571. package/dist/mcp/servers/ripgrep.js.map +0 -1
  572. package/dist/mcp/servers/shell.d.ts +0 -20
  573. package/dist/mcp/servers/shell.d.ts.map +0 -1
  574. package/dist/mcp/servers/shell.js +0 -519
  575. package/dist/mcp/servers/shell.js.map +0 -1
  576. package/dist/mcp/serversRegistry.d.ts +0 -55
  577. package/dist/mcp/serversRegistry.d.ts.map +0 -1
  578. package/dist/mcp/serversRegistry.js +0 -416
  579. package/dist/mcp/serversRegistry.js.map +0 -1
  580. package/dist/mcp/toolProcessor.d.ts +0 -82
  581. package/dist/mcp/toolProcessor.d.ts.map +0 -1
  582. package/dist/mcp/toolProcessor.js +0 -392
  583. package/dist/mcp/toolProcessor.js.map +0 -1
  584. package/dist/mcp/types.d.ts +0 -29
  585. package/dist/mcp/types.d.ts.map +0 -1
  586. package/dist/mcp/types.js +0 -2
  587. package/dist/mcp/types.js.map +0 -1
  588. package/dist/messages/index.d.ts +0 -8
  589. package/dist/messages/index.d.ts.map +0 -1
  590. package/dist/messages/index.js +0 -7
  591. package/dist/messages/index.js.map +0 -1
  592. package/dist/messages/openai.d.ts +0 -26
  593. package/dist/messages/openai.d.ts.map +0 -1
  594. package/dist/messages/openai.js +0 -55
  595. package/dist/messages/openai.js.map +0 -1
  596. package/dist/messages/types.d.ts +0 -73
  597. package/dist/messages/types.d.ts.map +0 -1
  598. package/dist/messages/types.js +0 -78
  599. package/dist/messages/types.js.map +0 -1
  600. package/dist/parser/index.d.ts +0 -72
  601. package/dist/parser/index.d.ts.map +0 -1
  602. package/dist/parser/index.js +0 -967
  603. package/dist/parser/index.js.map +0 -1
  604. package/dist/parser/types.d.ts +0 -153
  605. package/dist/parser/types.d.ts.map +0 -1
  606. package/dist/parser/types.js +0 -6
  607. package/dist/parser/types.js.map +0 -1
  608. package/dist/parser/utils.d.ts +0 -18
  609. package/dist/parser/utils.d.ts.map +0 -1
  610. package/dist/parser/utils.js +0 -64
  611. package/dist/parser/utils.js.map +0 -1
  612. package/dist/sdk/QodoSDK.d.ts +0 -218
  613. package/dist/sdk/QodoSDK.d.ts.map +0 -1
  614. package/dist/sdk/QodoSDK.js +0 -1115
  615. package/dist/sdk/QodoSDK.js.map +0 -1
  616. package/dist/sdk/artifacts.d.ts +0 -156
  617. package/dist/sdk/artifacts.d.ts.map +0 -1
  618. package/dist/sdk/artifacts.js +0 -166
  619. package/dist/sdk/artifacts.js.map +0 -1
  620. package/dist/sdk/bootstrap.d.ts +0 -16
  621. package/dist/sdk/bootstrap.d.ts.map +0 -1
  622. package/dist/sdk/bootstrap.js +0 -28
  623. package/dist/sdk/bootstrap.js.map +0 -1
  624. package/dist/sdk/builders.d.ts +0 -54
  625. package/dist/sdk/builders.d.ts.map +0 -1
  626. package/dist/sdk/builders.js +0 -117
  627. package/dist/sdk/builders.js.map +0 -1
  628. package/dist/sdk/defaults.d.ts +0 -11
  629. package/dist/sdk/defaults.d.ts.map +0 -1
  630. package/dist/sdk/defaults.js +0 -39
  631. package/dist/sdk/defaults.js.map +0 -1
  632. package/dist/sdk/discovery.d.ts +0 -2
  633. package/dist/sdk/discovery.d.ts.map +0 -1
  634. package/dist/sdk/discovery.js +0 -25
  635. package/dist/sdk/discovery.js.map +0 -1
  636. package/dist/sdk/events.d.ts +0 -269
  637. package/dist/sdk/events.d.ts.map +0 -1
  638. package/dist/sdk/events.js +0 -69
  639. package/dist/sdk/events.js.map +0 -1
  640. package/dist/sdk/exit-expression.d.ts +0 -13
  641. package/dist/sdk/exit-expression.d.ts.map +0 -1
  642. package/dist/sdk/exit-expression.js +0 -35
  643. package/dist/sdk/exit-expression.js.map +0 -1
  644. package/dist/sdk/index.d.ts +0 -17
  645. package/dist/sdk/index.d.ts.map +0 -1
  646. package/dist/sdk/index.js +0 -17
  647. package/dist/sdk/index.js.map +0 -1
  648. package/dist/sdk/middleware.d.ts +0 -59
  649. package/dist/sdk/middleware.d.ts.map +0 -1
  650. package/dist/sdk/middleware.js +0 -69
  651. package/dist/sdk/middleware.js.map +0 -1
  652. package/dist/sdk/pipeline/PipelineBuilder.d.ts +0 -79
  653. package/dist/sdk/pipeline/PipelineBuilder.d.ts.map +0 -1
  654. package/dist/sdk/pipeline/PipelineBuilder.js +0 -129
  655. package/dist/sdk/pipeline/PipelineBuilder.js.map +0 -1
  656. package/dist/sdk/pipeline/PipelineRunner.d.ts +0 -28
  657. package/dist/sdk/pipeline/PipelineRunner.d.ts.map +0 -1
  658. package/dist/sdk/pipeline/PipelineRunner.js +0 -326
  659. package/dist/sdk/pipeline/PipelineRunner.js.map +0 -1
  660. package/dist/sdk/pipeline/compiler.d.ts +0 -24
  661. package/dist/sdk/pipeline/compiler.d.ts.map +0 -1
  662. package/dist/sdk/pipeline/compiler.js +0 -199
  663. package/dist/sdk/pipeline/compiler.js.map +0 -1
  664. package/dist/sdk/pipeline/declarative.d.ts +0 -34
  665. package/dist/sdk/pipeline/declarative.d.ts.map +0 -1
  666. package/dist/sdk/pipeline/declarative.js +0 -9
  667. package/dist/sdk/pipeline/declarative.js.map +0 -1
  668. package/dist/sdk/pipeline/index.d.ts +0 -20
  669. package/dist/sdk/pipeline/index.d.ts.map +0 -1
  670. package/dist/sdk/pipeline/index.js +0 -19
  671. package/dist/sdk/pipeline/index.js.map +0 -1
  672. package/dist/sdk/pipeline/types.d.ts +0 -93
  673. package/dist/sdk/pipeline/types.d.ts.map +0 -1
  674. package/dist/sdk/pipeline/types.js +0 -10
  675. package/dist/sdk/pipeline/types.js.map +0 -1
  676. package/dist/sdk/policies.d.ts +0 -163
  677. package/dist/sdk/policies.d.ts.map +0 -1
  678. package/dist/sdk/policies.js +0 -243
  679. package/dist/sdk/policies.js.map +0 -1
  680. package/dist/sdk/runner/AgentRunner.d.ts +0 -22
  681. package/dist/sdk/runner/AgentRunner.d.ts.map +0 -1
  682. package/dist/sdk/runner/AgentRunner.js +0 -222
  683. package/dist/sdk/runner/AgentRunner.js.map +0 -1
  684. package/dist/sdk/runner/finalize.d.ts +0 -56
  685. package/dist/sdk/runner/finalize.d.ts.map +0 -1
  686. package/dist/sdk/runner/finalize.js +0 -155
  687. package/dist/sdk/runner/finalize.js.map +0 -1
  688. package/dist/sdk/runner/formats.d.ts +0 -7
  689. package/dist/sdk/runner/formats.d.ts.map +0 -1
  690. package/dist/sdk/runner/formats.js +0 -76
  691. package/dist/sdk/runner/formats.js.map +0 -1
  692. package/dist/sdk/runner/index.d.ts +0 -9
  693. package/dist/sdk/runner/index.d.ts.map +0 -1
  694. package/dist/sdk/runner/index.js +0 -9
  695. package/dist/sdk/runner/index.js.map +0 -1
  696. package/dist/sdk/runner/progress.d.ts +0 -3
  697. package/dist/sdk/runner/progress.d.ts.map +0 -1
  698. package/dist/sdk/runner/progress.js +0 -16
  699. package/dist/sdk/runner/progress.js.map +0 -1
  700. package/dist/sdk/schemas.d.ts +0 -72
  701. package/dist/sdk/schemas.d.ts.map +0 -1
  702. package/dist/sdk/schemas.js +0 -282
  703. package/dist/sdk/schemas.js.map +0 -1
  704. package/dist/sdk/trigger-context.d.ts +0 -24
  705. package/dist/sdk/trigger-context.d.ts.map +0 -1
  706. package/dist/sdk/trigger-context.js +0 -136
  707. package/dist/sdk/trigger-context.js.map +0 -1
  708. package/dist/session/SessionContext.d.ts +0 -89
  709. package/dist/session/SessionContext.d.ts.map +0 -1
  710. package/dist/session/SessionContext.js +0 -410
  711. package/dist/session/SessionContext.js.map +0 -1
  712. package/dist/session/environment.d.ts +0 -52
  713. package/dist/session/environment.d.ts.map +0 -1
  714. package/dist/session/environment.js +0 -27
  715. package/dist/session/environment.js.map +0 -1
  716. package/dist/session/history.d.ts +0 -18
  717. package/dist/session/history.d.ts.map +0 -1
  718. package/dist/session/history.js +0 -68
  719. package/dist/session/history.js.map +0 -1
  720. package/dist/session/index.d.ts +0 -10
  721. package/dist/session/index.d.ts.map +0 -1
  722. package/dist/session/index.js +0 -9
  723. package/dist/session/index.js.map +0 -1
  724. package/dist/session/serverData.d.ts +0 -38
  725. package/dist/session/serverData.d.ts.map +0 -1
  726. package/dist/session/serverData.js +0 -261
  727. package/dist/session/serverData.js.map +0 -1
  728. package/dist/tracing/pipelineHelpers.d.ts +0 -29
  729. package/dist/tracing/pipelineHelpers.d.ts.map +0 -1
  730. package/dist/tracing/pipelineHelpers.js +0 -224
  731. package/dist/tracing/pipelineHelpers.js.map +0 -1
  732. package/dist/tracking/Tracker.d.ts +0 -55
  733. package/dist/tracking/Tracker.d.ts.map +0 -1
  734. package/dist/tracking/Tracker.js +0 -217
  735. package/dist/tracking/Tracker.js.map +0 -1
  736. package/dist/tracking/index.d.ts +0 -8
  737. package/dist/tracking/index.d.ts.map +0 -1
  738. package/dist/tracking/index.js +0 -8
  739. package/dist/tracking/index.js.map +0 -1
  740. package/dist/tracking/schemas.d.ts +0 -292
  741. package/dist/tracking/schemas.d.ts.map +0 -1
  742. package/dist/tracking/schemas.js +0 -91
  743. package/dist/tracking/schemas.js.map +0 -1
  744. package/dist/utils/extractSetFlags.d.ts +0 -6
  745. package/dist/utils/extractSetFlags.d.ts.map +0 -1
  746. package/dist/utils/extractSetFlags.js +0 -16
  747. package/dist/utils/extractSetFlags.js.map +0 -1
  748. package/dist/utils/formatTimeAgo.d.ts +0 -2
  749. package/dist/utils/formatTimeAgo.d.ts.map +0 -1
  750. package/dist/utils/formatTimeAgo.js +0 -20
  751. package/dist/utils/formatTimeAgo.js.map +0 -1
  752. package/dist/utils/index.d.ts +0 -12
  753. package/dist/utils/index.d.ts.map +0 -1
  754. package/dist/utils/index.js +0 -12
  755. package/dist/utils/index.js.map +0 -1
  756. package/dist/utils/machineId.d.ts +0 -14
  757. package/dist/utils/machineId.d.ts.map +0 -1
  758. package/dist/utils/machineId.js +0 -66
  759. package/dist/utils/machineId.js.map +0 -1
  760. package/dist/utils/pathUtils.d.ts +0 -22
  761. package/dist/utils/pathUtils.d.ts.map +0 -1
  762. package/dist/utils/pathUtils.js +0 -54
  763. package/dist/utils/pathUtils.js.map +0 -1
  764. package/scripts/download-ripgrep.js +0 -269
@@ -0,0 +1,1386 @@
1
+ /**
2
+ * `SkillsManager`.
3
+ *
4
+ * Walks every configured source, parses + validates each `SKILL.md`, then
5
+ * builds an in-memory catalog keyed by fully-qualified
6
+ * `<vendor>/<name>@<version>` with shadowing.
7
+ *
8
+ * Construction is synchronous and cheap (validate config, store). The
9
+ * `discover()` method does the disk IO; it's idempotent — subsequent
10
+ * calls return the same already-built catalog. Public lookups via
11
+ * `list()` / `get()` await discovery completion implicitly.
12
+ *
13
+ * Invariants:
14
+ * - Catalog is built once per discovery; hot reload reruns discovery
15
+ * to produce a fresh snapshot.
16
+ * - Trust tier is derived from source type only; consumer-supplied
17
+ * `trustedSources` is used to elevate third-party sources.
18
+ * - Per-skill `min_sdk_version` exclusion lands here.
19
+ */
20
+ import { promises as fs, watch as fsWatch } from 'node:fs';
21
+ import * as path from 'node:path';
22
+ import { parseSkillDocument, SkillParseError } from './parser.js';
23
+ import { defaultWalkerEnv, walkSource } from './sources/index.js';
24
+ import { safeRealPath } from './sources/walk.js';
25
+ import { isSkillTrusted, validateTrustedSources } from './trust.js';
26
+ import { findLockfile, LockfileValidationError, loadLockfile, validateLockfileEntry, } from './lockfile.js';
27
+ import { DEFAULT_SOURCE_ORDER, REQUIRES_DEFAULT_MAX_DEPTH, REQUIRES_DEFAULT_MAX_TRANSITIVE, REQUIRES_DEFAULT_MAX_TRANSITIVE_TOKENS, SKILL_FILE_HARD_BYTES, SOURCE_DEFAULT_VENDORS, SOURCE_TRUST_TIERS, } from './types.js';
28
+ import { extractBodyReferences, semverLessThan, validateBody, validateFrontmatter, } from './validator.js';
29
+ /** Default `version` when frontmatter omits it. */
30
+ const DEFAULT_VERSION = '0.0.0';
31
+ export class SkillsManager {
32
+ config;
33
+ env;
34
+ emit;
35
+ trustedSourceIdentities;
36
+ requiresCaps;
37
+ /**
38
+ * Per-client active-skill set. Populated by caller-pin activation
39
+ * (`TaskClient`) and by LLM-driven `get_skill` calls (the MCP server,
40
+ * via `markLoaded`). Cleared by `resetActive()`.
41
+ *
42
+ * SDK 2.0 has no per-task AsyncLocalStorage env-scope (the design that
43
+ * shipped on SDK 1.x's `EnvironmentServices`); active-state lives on
44
+ * the manager, scoped to the client lifetime. Concurrent dispatches
45
+ * against the same client observe a merged set — documented in the
46
+ * architectural-translation note. `disconnect()` calls
47
+ * `skillsMcpServer.dispose()` which calls `resetActive()` (transitively
48
+ * through the existing `activeFqns` clear), so a fresh `connect()`
49
+ * starts cold.
50
+ */
51
+ activeFqns = new Set();
52
+ discoverPromise = null;
53
+ snapshot = null;
54
+ /**
55
+ * Active `fs.watch` handles keyed by the absolute directory path
56
+ * being watched. Populated by `startWatching()`, cleared by
57
+ * `stopWatching()` / `dispose()`.
58
+ */
59
+ watchers = new Map();
60
+ /** Pending watcher-triggered rebuild timer, debounced. */
61
+ rebuildTimer = null;
62
+ /** Promise that resolves with the next rebuilt snapshot, if a rebuild is in-flight. */
63
+ rebuildPromise = null;
64
+ /** Optional listener fired after every successful rebuild — used by tests / consumers. */
65
+ rebuildListeners = new Set();
66
+ epoch = 0;
67
+ /**
68
+ * In-flight `startWatching()` promise. Concurrent calls await the
69
+ * same promise so the install pass is serialised — defends against
70
+ * the watcher-duplication race when auto-start + manual start fire
71
+ * close together.
72
+ */
73
+ startWatchingPromise = null;
74
+ /**
75
+ * Marks that a watcher event arrived while a rebuild was already
76
+ * in flight. The current rebuild's `.then` re-schedules so the late
77
+ * change isn't dropped.
78
+ */
79
+ pendingRebuild = false;
80
+ constructor(config, env = defaultWalkerEnv()) {
81
+ this.config = config;
82
+ this.env = env;
83
+ this.emit = config.emit;
84
+ // Validate `trustedSources` against the configured `sources` list at
85
+ // construction time so a misconfigured allowlist fails loud at `new
86
+ // QodoClient({ skills: {...} })` instead of at the first activation
87
+ // attempt.
88
+ const sources = this.resolveSourcesList();
89
+ this.trustedSourceIdentities = validateTrustedSources(config.trustedSources, sources);
90
+ this.requiresCaps = resolveRequiresCaps(config.requires);
91
+ }
92
+ /**
93
+ * Forward a renderer-produced event batch to the configured sink.
94
+ * Used by `TaskClient`'s skills injection helpers — the renderer is
95
+ * pure and returns events as data; the manager owns the sink wiring
96
+ * so consumers see one consistent emission surface.
97
+ */
98
+ forwardEvents(events) {
99
+ for (const event of events)
100
+ this.emitEvent(event);
101
+ }
102
+ /**
103
+ * Walk every configured source, parse + validate, build the FQN-keyed
104
+ * catalog. Idempotent — concurrent calls share one in-flight promise.
105
+ */
106
+ discover() {
107
+ if (this.snapshot !== null)
108
+ return Promise.resolve(this.snapshot);
109
+ if (this.discoverPromise !== null)
110
+ return this.discoverPromise;
111
+ this.discoverPromise = this.doRebuild('initial_discover').then((snapshot) => {
112
+ if (this.config.watch === true) {
113
+ // Fire-and-forget — watcher installation reads the now-set
114
+ // `snapshot.bySource`; failures emit `sdk.skill.error`.
115
+ this.startWatching().catch(() => {
116
+ // ignored — startWatching surfaces errors through emitEvent
117
+ });
118
+ }
119
+ return snapshot;
120
+ });
121
+ return this.discoverPromise;
122
+ }
123
+ /** Resolved `true` once `discover()` has completed at least once. */
124
+ get isReady() {
125
+ return this.snapshot !== null;
126
+ }
127
+ /** Synchronous snapshot accessor. Returns `null` until `discover()` resolves. */
128
+ get currentSnapshot() {
129
+ return this.snapshot;
130
+ }
131
+ /**
132
+ * Constructor-supplied default `activate` set. `TaskClient` reads this
133
+ * when the per-call `opts.skills` is `undefined`. Returned read-only;
134
+ * `[]` and `undefined` are distinct (`[]` = explicit empty, `undefined`
135
+ * = "no default set").
136
+ */
137
+ get defaultActivate() {
138
+ return this.config.activate;
139
+ }
140
+ /**
141
+ * Constructor-supplied `indexCharBudget`. `TaskClient` forwards this
142
+ * to `renderSlimIndex()` so consumers can tighten the model-visible
143
+ * block below the 8000-char default. `undefined` means "renderer
144
+ * picks the default".
145
+ */
146
+ get indexCharBudget() {
147
+ return this.config.indexCharBudget;
148
+ }
149
+ /**
150
+ * Constructor-supplied `!command` preprocessing config.
151
+ * `undefined` means "default disabled" — the MCP server's `get_skill`
152
+ * handler short-circuits when this is `undefined` or `mode: 'disabled'`.
153
+ */
154
+ get preprocessingConfig() {
155
+ return this.config.preprocessing;
156
+ }
157
+ /**
158
+ * Constructor-supplied embedder, if any. `undefined` means
159
+ * "no pre-filter; rely on render-side truncation".
160
+ */
161
+ get embedder() {
162
+ return this.config.embedder;
163
+ }
164
+ /**
165
+ * Constructor-supplied embedding-pre-filter tuning. `undefined` means
166
+ * "use documented defaults" (`topK: 8`, `minScore: 0.35`).
167
+ */
168
+ get activationConfig() {
169
+ return this.config.activation;
170
+ }
171
+ /**
172
+ * List every catalog entry. Awaits discovery on first call. Returns
173
+ * latest-version-only by default; pass `{ includeAllVersions: true }`
174
+ * to surface every version recorded during discovery.
175
+ */
176
+ async list(opts = {}) {
177
+ const snapshot = await this.discover();
178
+ if (opts.includeAllVersions === true)
179
+ return snapshot.skills;
180
+ return collectLatestVersions(snapshot.skills);
181
+ }
182
+ /**
183
+ * Resolve a skill specifier (`name`, `vendor/name`, or `vendor/name@version`)
184
+ * to a `ParsedSkill` or `null` if no match.
185
+ *
186
+ * - Bare name: returns the latest-version match of any vendor; if multiple
187
+ * vendors ship a skill of that name, the highest-precedence source wins.
188
+ * - `vendor/name`: latest version of that vendor's skill.
189
+ * - `vendor/name@version`: exact pin.
190
+ */
191
+ async get(specifier) {
192
+ const snapshot = await this.discover();
193
+ return resolveSpecifier(snapshot.skills, specifier);
194
+ }
195
+ /**
196
+ * Verify every name in `specifiers` resolves. Throws on the first
197
+ * unresolved entry — the SDK uses this from the wire boundary to fail
198
+ * loud on caller-pinned activation per `skills-activation.md` §2.
199
+ */
200
+ async resolveOrThrow(specifiers) {
201
+ const snapshot = await this.discover();
202
+ const out = [];
203
+ for (const spec of specifiers) {
204
+ const resolved = resolveSpecifier(snapshot.skills, spec);
205
+ if (resolved === null) {
206
+ throw new SkillNotFoundError(spec);
207
+ }
208
+ out.push(resolved);
209
+ }
210
+ return out;
211
+ }
212
+ // ---------------------------------------------------------------------
213
+ // Trust + requires caps + active-skill state
214
+ // ---------------------------------------------------------------------
215
+ /**
216
+ * Whether `skill` is trusted enough to honor its declarative affordances
217
+ * (`allowed-tools`, `disable-model-invocation: false`, `hooks`). True for
218
+ * the `'trusted'` / `'operator'` tiers; for `'third-party'` true iff the
219
+ * skill's source identity is in `trustedSources`.
220
+ */
221
+ isTrusted(skill) {
222
+ return isSkillTrusted(skill, this.trustedSourceIdentities);
223
+ }
224
+ /**
225
+ * Resolved `requires:` caps with defaults filled in. Read by the
226
+ * activation pipeline (`TaskClient` caller-pin + `QodoSkillsMcpServer.get_skill`).
227
+ */
228
+ get requiresCapsResolved() {
229
+ return this.requiresCaps;
230
+ }
231
+ /**
232
+ * Snapshot of currently active skill FQNs. The active set is union of
233
+ * (a) caller-pinned skills the SDK injected for the latest dispatch and
234
+ * (b) skills the model loaded via `qodo-skills.get_skill`. Returned
235
+ * sorted for stable test output.
236
+ */
237
+ getActiveSkills() {
238
+ return [...this.activeFqns].sort();
239
+ }
240
+ /**
241
+ * Mark a skill FQN as active. Idempotent — re-marking emits no extra
242
+ * event. Returns `true` iff the FQN was newly added (i.e., this is a
243
+ * fresh activation, not a cache hit).
244
+ *
245
+ * The caller is responsible for emitting `sdk.skill.activated` — the
246
+ * event carries `source: 'caller' | 'llm'` which only the caller knows.
247
+ * `markActive` just owns the set membership.
248
+ */
249
+ markActive(fqn) {
250
+ if (this.activeFqns.has(fqn))
251
+ return false;
252
+ this.activeFqns.add(fqn);
253
+ return true;
254
+ }
255
+ /**
256
+ * Remove a skill FQN from the active set. Returns `true` iff it was
257
+ * present. Used by `QodoSkillsMcpServer.dispose` and (rarely) by
258
+ * consumers programmatically rolling back activation.
259
+ */
260
+ deactivate(fqn) {
261
+ return this.activeFqns.delete(fqn);
262
+ }
263
+ /**
264
+ * Clear the active set entirely. Called by `QodoSkillsMcpServer.dispose`
265
+ * on `disconnect()` so the next `connect()` starts cold.
266
+ */
267
+ resetActive() {
268
+ this.activeFqns.clear();
269
+ }
270
+ /**
271
+ * @internal — used by `QodoSkillsMcpServer` to share the active set with
272
+ * the body cache. Exposes the same `Set` the manager uses; mutations
273
+ * propagate to both surfaces.
274
+ */
275
+ get internalActiveFqns() {
276
+ return this.activeFqns;
277
+ }
278
+ // ---------------------------------------------------------------------
279
+ // Hot reload watcher (dev mode)
280
+ // ---------------------------------------------------------------------
281
+ /**
282
+ * Begin watching every configured local source directory for SKILL.md
283
+ * changes. Idempotent — calling twice has no extra effect.
284
+ *
285
+ * Requires `discover()` to have completed; the watcher targets only
286
+ * directories already known to be on disk. New top-level source dirs
287
+ * appearing post-startup are NOT auto-watched (operators would
288
+ * typically `stopWatching()` + reconfigure + start a new client).
289
+ *
290
+ * `package` and `bundled` sources are intentionally NOT watched —
291
+ * those ship with the application code and shouldn't churn at
292
+ * runtime. Watching them would expose tampering surface without
293
+ * meaningful upside.
294
+ *
295
+ * Default behavior is determined by `SkillsConfig.watch`:
296
+ * `undefined`/`false` ⇒ no-op; `true` ⇒ start watching after the first
297
+ * `discover()` resolves.
298
+ */
299
+ async startWatching() {
300
+ // Overlapping `startWatching()` calls (e.g. auto-start + an
301
+ // explicit manual start) could race past the
302
+ // `watchers.size > 0` early-out and install duplicate `FSWatcher`s
303
+ // for the same directory. Serialise through an in-flight promise so
304
+ // every concurrent caller waits on the same install pass.
305
+ if (this.startWatchingPromise !== null) {
306
+ await this.startWatchingPromise;
307
+ return;
308
+ }
309
+ this.startWatchingPromise = this.doStartWatching().finally(() => {
310
+ this.startWatchingPromise = null;
311
+ });
312
+ await this.startWatchingPromise;
313
+ }
314
+ async doStartWatching() {
315
+ await this.discover();
316
+ if (this.watchers.size > 0)
317
+ return;
318
+ const seen = new Set();
319
+ for (const result of this.snapshot.bySource) {
320
+ // Skip source types that ship with code and shouldn't churn.
321
+ if (!isWatchableSourceType(result.source.type))
322
+ continue;
323
+ // Watch each discovered skill's parent (the `<source>/<skill-dir>`'s
324
+ // parent — typically the source root). That covers add/change/delete
325
+ // of the skill's own SKILL.md and sibling adds (a new skill dir
326
+ // appearing alongside an existing one).
327
+ for (const walked of result.skills) {
328
+ const parent = path.dirname(walked.dirPath);
329
+ if (seen.has(parent))
330
+ continue;
331
+ const real = await safeRealPath(parent);
332
+ if (real === null || seen.has(real))
333
+ continue;
334
+ seen.add(real);
335
+ this.installWatcher(real);
336
+ }
337
+ // Also watch the source's declared root if it has one (covers an
338
+ // empty source — no skills yet, but operators expect adds to be
339
+ // picked up). When `source.path` is omitted (the common default
340
+ // for `project` / `user`), the watcher needs to know the
341
+ // resolved default location too; `resolveDefaultWatchRoot`
342
+ // bridges the env-based defaulting the walker itself does.
343
+ const root = watchableRoot(result.source) ?? resolveDefaultWatchRoot(result.source, this.env);
344
+ if (root !== null) {
345
+ const real = await safeRealPath(root);
346
+ if (real !== null && !seen.has(real)) {
347
+ seen.add(real);
348
+ this.installWatcher(real);
349
+ }
350
+ }
351
+ }
352
+ }
353
+ /**
354
+ * Tear down every active watcher and cancel any pending debounce
355
+ * timer. Safe to call multiple times. Called automatically by
356
+ * `dispose()`.
357
+ */
358
+ stopWatching() {
359
+ for (const watcher of this.watchers.values()) {
360
+ try {
361
+ watcher.close();
362
+ }
363
+ catch {
364
+ // Closing a watcher can throw on some platforms when the
365
+ // underlying directory is already gone; ignored — there's
366
+ // nothing to recover.
367
+ }
368
+ }
369
+ this.watchers.clear();
370
+ if (this.rebuildTimer !== null) {
371
+ clearTimeout(this.rebuildTimer);
372
+ this.rebuildTimer = null;
373
+ }
374
+ }
375
+ /**
376
+ * Tear down the manager — equivalent to `stopWatching()`. Provided as
377
+ * a parallel to `QodoSkillsMcpServer.dispose()` so consumer code can
378
+ * call both in a uniform shutdown sequence.
379
+ */
380
+ dispose() {
381
+ this.stopWatching();
382
+ this.rebuildListeners.clear();
383
+ }
384
+ /** Current catalog epoch counter. `0` before the first `discover()`. */
385
+ get catalogEpoch() {
386
+ return this.epoch;
387
+ }
388
+ /**
389
+ * Subscribe to rebuild events. The listener fires after every
390
+ * successful watcher-triggered rebuild (NOT on the initial discover).
391
+ * Returns an unsubscribe function. Used primarily by tests.
392
+ */
393
+ onRebuild(listener) {
394
+ this.rebuildListeners.add(listener);
395
+ return () => {
396
+ this.rebuildListeners.delete(listener);
397
+ };
398
+ }
399
+ /**
400
+ * Trigger an out-of-band rebuild. Test seam — production code goes
401
+ * through the debounced watcher path. The rebuild is in-flight
402
+ * already if another rebuild is queued; returns the same in-flight
403
+ * promise to avoid double-rebuild races.
404
+ */
405
+ rebuildNow() {
406
+ if (this.rebuildPromise !== null) {
407
+ // A change that arrives mid-rebuild must not be silently dropped.
408
+ // Record the trailing request; the current rebuild's finally-hook
409
+ // will fire another pass.
410
+ this.pendingRebuild = true;
411
+ return this.rebuildPromise;
412
+ }
413
+ this.rebuildPromise = this.doRebuild('watcher_event').finally(() => {
414
+ this.rebuildPromise = null;
415
+ if (this.pendingRebuild) {
416
+ this.pendingRebuild = false;
417
+ // Schedule the follow-up through the debouncer so multiple
418
+ // late changes coalesce into one extra rebuild.
419
+ this.scheduleRebuild();
420
+ }
421
+ });
422
+ return this.rebuildPromise;
423
+ }
424
+ installWatcher(dir) {
425
+ if (this.watchers.has(dir))
426
+ return;
427
+ let watcher;
428
+ try {
429
+ watcher = fsWatch(dir, { recursive: true });
430
+ }
431
+ catch (err) {
432
+ this.emitEvent({
433
+ kind: 'sdk.skill.error',
434
+ path: dir,
435
+ severity: 'warning',
436
+ message: `watch failed for ${dir}: ${errorMessage(err)}`,
437
+ });
438
+ return;
439
+ }
440
+ watcher.on('change', () => this.scheduleRebuild());
441
+ watcher.on('error', (err) => {
442
+ this.emitEvent({
443
+ kind: 'sdk.skill.error',
444
+ path: dir,
445
+ severity: 'warning',
446
+ message: `watcher error for ${dir}: ${err.message}`,
447
+ });
448
+ });
449
+ this.watchers.set(dir, watcher);
450
+ }
451
+ scheduleRebuild() {
452
+ if (this.rebuildTimer !== null)
453
+ return;
454
+ // If a rebuild is already in flight, record the trailing request
455
+ // and let the in-flight rebuild's finally-hook re-fire — no need
456
+ // to start a separate debounce timer that would race the hook.
457
+ if (this.rebuildPromise !== null) {
458
+ this.pendingRebuild = true;
459
+ return;
460
+ }
461
+ this.rebuildTimer = setTimeout(() => {
462
+ this.rebuildTimer = null;
463
+ this.rebuildNow().catch(() => {
464
+ // Errors are emitted as `sdk.skill.error` inside `doRebuild`;
465
+ // there's no caller to propagate to from the watcher path.
466
+ });
467
+ }, 100);
468
+ }
469
+ async doRebuild(reason) {
470
+ const previous = this.snapshot;
471
+ const next = await this.buildCatalog();
472
+ this.epoch += 1;
473
+ const stamped = { ...next, epoch: this.epoch };
474
+ this.snapshot = stamped;
475
+ this.emitEvent({
476
+ kind: 'sdk.skill.catalog_epoch_bumped',
477
+ from: this.epoch - 1,
478
+ to: this.epoch,
479
+ reason,
480
+ });
481
+ if (previous !== null) {
482
+ this.diffAndEmit(previous, stamped);
483
+ // `onRebuild()` is documented as firing only on watcher-triggered
484
+ // rebuilds, not on the initial discover.
485
+ // Listeners registered before `discover()` would otherwise see
486
+ // an unexpected callback at startup. Gate on `previous !== null`
487
+ // so the initial-discover path stays silent.
488
+ for (const listener of this.rebuildListeners) {
489
+ try {
490
+ listener(stamped);
491
+ }
492
+ catch {
493
+ // Listener sinks must not break the rebuild loop.
494
+ }
495
+ }
496
+ }
497
+ return stamped;
498
+ }
499
+ diffAndEmit(prev, next) {
500
+ const prevByFqn = new Map();
501
+ for (const s of prev.skills)
502
+ prevByFqn.set(s.fqn, s);
503
+ const nextByFqn = new Map();
504
+ for (const s of next.skills)
505
+ nextByFqn.set(s.fqn, s);
506
+ for (const [fqn, before] of prevByFqn) {
507
+ if (!nextByFqn.has(fqn)) {
508
+ this.emitEvent({
509
+ kind: 'sdk.skill.removed',
510
+ fqn,
511
+ source: before.source,
512
+ });
513
+ }
514
+ }
515
+ // `sdk.skill.discovered` events for fresh adds are emitted from
516
+ // inside `doDiscover()` already; nothing more to do here for the
517
+ // add path.
518
+ }
519
+ // ---------------------------------------------------------------------
520
+ // Internals
521
+ // ---------------------------------------------------------------------
522
+ /**
523
+ * Walk every configured source + parse + validate + assemble the
524
+ * catalog. Pure with respect to `this.snapshot` — the caller decides
525
+ * how to publish the returned snapshot (initial discover stamps the
526
+ * epoch; rebuild diffs against the prior snapshot first).
527
+ */
528
+ async buildCatalog() {
529
+ const sources = this.resolveSourcesList();
530
+ const bySource = [];
531
+ const allCandidates = [];
532
+ for (const source of sources) {
533
+ const start = Date.now();
534
+ let result;
535
+ try {
536
+ result = await walkSource(source, this.env);
537
+ }
538
+ catch (err) {
539
+ result = {
540
+ source,
541
+ skills: [],
542
+ errors: [{ message: `walker threw: ${errorMessage(err)}` }],
543
+ };
544
+ }
545
+ const durationMs = Date.now() - start;
546
+ this.emitEvent({
547
+ kind: 'sdk.skill.source_scanned',
548
+ source: result.source,
549
+ durationMs,
550
+ count: result.skills.length,
551
+ errors: result.errors.length,
552
+ });
553
+ for (const errEntry of result.errors) {
554
+ this.emitEvent({
555
+ kind: 'sdk.skill.error',
556
+ source: result.source,
557
+ ...(errEntry.path !== undefined ? { path: errEntry.path } : {}),
558
+ severity: 'error',
559
+ message: errEntry.message,
560
+ });
561
+ }
562
+ bySource.push(result);
563
+ for (const walked of result.skills) {
564
+ allCandidates.push({
565
+ walked,
566
+ source: result.source,
567
+ ...(result.resolvedVendor !== undefined ? { resolvedVendor: result.resolvedVendor } : {}),
568
+ });
569
+ }
570
+ }
571
+ // Parse + validate each candidate. Keyed by FQN; later candidates of
572
+ // the same FQN are shadow-resolved (highest-precedence source wins).
573
+ const byFqn = new Map();
574
+ const excluded = [];
575
+ const seenWithinSource = new Set();
576
+ let currentSourceKey = '';
577
+ for (const candidate of allCandidates) {
578
+ const sourceKey = sourceIdentity(candidate.source);
579
+ if (sourceKey !== currentSourceKey) {
580
+ seenWithinSource.clear();
581
+ currentSourceKey = sourceKey;
582
+ }
583
+ const parsed = await this.parseAndValidate(candidate.walked, candidate.source, candidate.resolvedVendor);
584
+ if (parsed === null)
585
+ continue;
586
+ const fqn = parsed.fqn;
587
+ const intraSourceKey = `${sourceKey}|${fqn}`;
588
+ if (seenWithinSource.has(intraSourceKey)) {
589
+ this.emitEvent({
590
+ kind: 'sdk.skill.error',
591
+ source: candidate.source,
592
+ path: candidate.walked.skillFilePath,
593
+ severity: 'error',
594
+ message: `duplicate FQN within source: ${fqn}`,
595
+ });
596
+ excluded.push({
597
+ source: candidate.source,
598
+ path: candidate.walked.skillFilePath,
599
+ reason: `duplicate FQN within source: ${fqn}`,
600
+ });
601
+ // Also drop any prior winner from the catalog if it came from
602
+ // the same source — both copies excluded.
603
+ const prior = byFqn.get(fqn);
604
+ if (prior !== undefined && sourceIdentity(prior.source) === sourceKey) {
605
+ byFqn.delete(fqn);
606
+ }
607
+ continue;
608
+ }
609
+ seenWithinSource.add(intraSourceKey);
610
+ const existing = byFqn.get(fqn);
611
+ if (existing === undefined) {
612
+ byFqn.set(fqn, parsed);
613
+ this.emitEvent({
614
+ kind: 'sdk.skill.discovered',
615
+ fqn,
616
+ source: parsed.source,
617
+ version: parsed.version,
618
+ trustTier: parsed.trustTier,
619
+ });
620
+ // Emit the dedicated tier-assigned event for audit-log replay.
621
+ // The information is already encoded in
622
+ // `sdk.skill.discovered.trustTier`; the explicit event surface
623
+ // lets operators wire a security-only sink without inheriting
624
+ // the rest of the discovery telemetry volume.
625
+ this.emitEvent({
626
+ kind: 'sdk.skill.tier_assigned',
627
+ skill: parsed.fqn,
628
+ tier: parsed.trustTier,
629
+ });
630
+ continue;
631
+ }
632
+ // Same FQN seen earlier; the earlier source has higher precedence
633
+ // (we iterate `sources` in highest-first order). The new one is
634
+ // shadowed.
635
+ this.emitEvent({
636
+ kind: 'sdk.skill.shadowed',
637
+ fqn,
638
+ winner: { source: existing.source, version: existing.version },
639
+ loser: { source: parsed.source, version: parsed.version },
640
+ });
641
+ excluded.push({
642
+ source: parsed.source,
643
+ path: parsed.skillFile,
644
+ reason: `shadowed by ${sourceIdentity(existing.source)}/${existing.version}`,
645
+ });
646
+ }
647
+ const skills = [...byFqn.values()].sort((a, b) => a.fqn.localeCompare(b.fqn));
648
+ // Validate any configured lockfile *after* discovery. Skills not in
649
+ // the lockfile are still included; pinned entries that drift or are
650
+ // unreachable fail boot loudly.
651
+ await this.applyLockfile(skills);
652
+ // `epoch` is stamped by the caller (`doRebuild`); placeholder `0`
653
+ // here keeps the shape consistent without redundant copying.
654
+ const snapshot = {
655
+ skills,
656
+ bySource,
657
+ excluded,
658
+ epoch: 0,
659
+ };
660
+ return snapshot;
661
+ }
662
+ /**
663
+ * Locate + apply the lockfile per `SkillsConfig.lockfile`. When
664
+ * `'off'`, returns immediately. When `'discover'`, searches the
665
+ * configured CWD; when an explicit `{ path }`, loads it directly.
666
+ *
667
+ * Validation outcomes:
668
+ *
669
+ * - All entries match → return silently.
670
+ * - At least one drifts → emit `sdk.skill.lock_drift` per offender + throw.
671
+ * - At least one unreachable → emit `sdk.skill.lock_unreachable` + throw.
672
+ *
673
+ * Throws `LockfileValidationError` so the wire boundary fails loud
674
+ * rather than serving a divergent catalog.
675
+ */
676
+ async applyLockfile(skills) {
677
+ const policy = this.config.lockfile ?? 'discover';
678
+ if (policy === 'off')
679
+ return;
680
+ let lockfilePath = null;
681
+ if (typeof policy === 'object' && policy !== null) {
682
+ lockfilePath = policy.path;
683
+ }
684
+ else {
685
+ // Discover the lockfile from the configured CWD AND from the
686
+ // nearest ancestor directory containing `agent.toml` — matches the
687
+ // documented "beside agent.toml" behavior.
688
+ // Walk up at most 8 levels so the search is bounded even when the
689
+ // CWD is buried in a deep monorepo.
690
+ const searchPaths = await collectLockfileSearchPaths(this.env.cwd);
691
+ lockfilePath = await findLockfile(searchPaths);
692
+ }
693
+ if (lockfilePath === null)
694
+ return;
695
+ let lockfile;
696
+ try {
697
+ lockfile = await loadLockfile(lockfilePath);
698
+ }
699
+ catch (err) {
700
+ // Malformed lockfile is a configuration error, surfaced through the
701
+ // emit sink and rethrown so the consumer fails fast.
702
+ const message = err instanceof Error ? err.message : String(err);
703
+ this.emitEvent({
704
+ kind: 'sdk.skill.error',
705
+ severity: 'error',
706
+ message: `failed to parse lockfile at ${lockfilePath}: ${message}`,
707
+ });
708
+ throw err;
709
+ }
710
+ const byFqn = new Map();
711
+ for (const skill of skills)
712
+ byFqn.set(skill.fqn, skill);
713
+ const failures = [];
714
+ for (const entry of lockfile.skills) {
715
+ const discovered = byFqn.get(entry.fqn);
716
+ const outcome = await validateLockfileEntry(entry, discovered);
717
+ switch (outcome.outcome) {
718
+ case 'ok':
719
+ continue;
720
+ case 'drift': {
721
+ this.emitEvent({
722
+ kind: 'sdk.skill.lock_drift',
723
+ fqn: entry.fqn,
724
+ expectedSha256: outcome.expectedSha256,
725
+ actualSha256: outcome.actualSha256,
726
+ });
727
+ failures.push({
728
+ fqn: entry.fqn,
729
+ kind: 'drift',
730
+ message: `expected sha256 ${outcome.expectedSha256}, got ${outcome.actualSha256}`,
731
+ });
732
+ break;
733
+ }
734
+ case 'unreachable': {
735
+ this.emitEvent({
736
+ kind: 'sdk.skill.lock_unreachable',
737
+ fqn: entry.fqn,
738
+ source: entry.source,
739
+ });
740
+ failures.push({
741
+ fqn: entry.fqn,
742
+ kind: 'unreachable',
743
+ message: outcome.reason,
744
+ });
745
+ break;
746
+ }
747
+ }
748
+ }
749
+ if (failures.length > 0) {
750
+ const summary = failures
751
+ .map((f) => ` - ${f.fqn}: ${f.kind} (${f.message})`)
752
+ .join('\n');
753
+ throw new LockfileValidationError(failures[0].fqn, failures[0].kind, `lockfile validation failed for ${failures.length} skill(s):\n${summary}\n` +
754
+ 'Resolve drift via `qodo skills lock --update <fqn>`.');
755
+ }
756
+ }
757
+ resolveSourcesList() {
758
+ if (this.config.sources !== undefined)
759
+ return this.config.sources;
760
+ // Default: project + user + bundled. We deliberately leave package
761
+ // / config / cli-fetched off the default because they require
762
+ // explicit opt-in (config has no default path; package needs a
763
+ // packageName; cli-fetched needs the CLI command to have run).
764
+ return DEFAULT_SOURCE_ORDER.flatMap((type) => {
765
+ switch (type) {
766
+ case 'project':
767
+ return [{ type: 'project' }];
768
+ case 'user':
769
+ return [{ type: 'user' }];
770
+ case 'bundled':
771
+ return [{ type: 'bundled' }];
772
+ default:
773
+ return [];
774
+ }
775
+ });
776
+ }
777
+ async parseAndValidate(walked, source, resolvedVendor) {
778
+ const dirName = path.basename(walked.dirPath);
779
+ // F2: enforce the file-size hard cap *before* `fs.readFile` so an
780
+ // attacker-controlled multi-megabyte SKILL.md cannot exhaust memory
781
+ // through the walker / parser path.
782
+ //
783
+ // We re-stat here at the read boundary rather than trusting
784
+ // `walked.size` (captured during discovery) — between scan and
785
+ // read the file can grow, which would otherwise bypass the cap.
786
+ // The walker's `size` is kept for early-exit on already-huge
787
+ // files; this re-stat closes the TOCTTOU window.
788
+ let liveStat;
789
+ try {
790
+ liveStat = await fs.stat(walked.skillFilePath);
791
+ }
792
+ catch (err) {
793
+ this.emitEvent({
794
+ kind: 'sdk.skill.error',
795
+ source,
796
+ path: walked.skillFilePath,
797
+ severity: 'error',
798
+ message: `stat failed: ${errorMessage(err)}`,
799
+ });
800
+ return null;
801
+ }
802
+ if (liveStat.size > SKILL_FILE_HARD_BYTES) {
803
+ this.emitEvent({
804
+ kind: 'sdk.skill.error',
805
+ source,
806
+ path: walked.skillFilePath,
807
+ severity: 'error',
808
+ message: `SKILL.md exceeds the ${SKILL_FILE_HARD_BYTES}-byte file cap ` +
809
+ `(${liveStat.size} bytes); skipping without reading`,
810
+ });
811
+ return null;
812
+ }
813
+ let text;
814
+ try {
815
+ text = await fs.readFile(walked.skillFilePath, 'utf-8');
816
+ }
817
+ catch (err) {
818
+ this.emitEvent({
819
+ kind: 'sdk.skill.error',
820
+ source,
821
+ path: walked.skillFilePath,
822
+ severity: 'error',
823
+ message: `readFile failed: ${errorMessage(err)}`,
824
+ });
825
+ return null;
826
+ }
827
+ // Defensive last-resort: even with the pre-read stat, a race between
828
+ // `stat` and `readFile` can grow the file by an unbounded amount.
829
+ // Verify the actual loaded byte length and reject if it exceeded
830
+ // the cap during the read window.
831
+ const actualBytes = Buffer.byteLength(text, 'utf-8');
832
+ if (actualBytes > SKILL_FILE_HARD_BYTES) {
833
+ this.emitEvent({
834
+ kind: 'sdk.skill.error',
835
+ source,
836
+ path: walked.skillFilePath,
837
+ severity: 'error',
838
+ message: `SKILL.md grew past the ${SKILL_FILE_HARD_BYTES}-byte file cap ` +
839
+ `during read (${actualBytes} bytes); skipping`,
840
+ });
841
+ return null;
842
+ }
843
+ let parsed;
844
+ try {
845
+ parsed = parseSkillDocument(text);
846
+ }
847
+ catch (err) {
848
+ const msg = err instanceof SkillParseError ? err.message : errorMessage(err);
849
+ this.emitEvent({
850
+ kind: 'sdk.skill.error',
851
+ source,
852
+ path: walked.skillFilePath,
853
+ severity: 'error',
854
+ message: msg,
855
+ });
856
+ return null;
857
+ }
858
+ const validated = validateFrontmatter(parsed.frontmatter, dirName);
859
+ for (const issue of validated.issues) {
860
+ this.emitEvent({
861
+ kind: 'sdk.skill.error',
862
+ source,
863
+ path: walked.skillFilePath,
864
+ severity: issue.severity,
865
+ message: issue.field !== undefined ? `${issue.field}: ${issue.message}` : issue.message,
866
+ });
867
+ }
868
+ for (const unknownField of validated.unknownFields) {
869
+ this.emitEvent({
870
+ kind: 'sdk.skill.unknown_field',
871
+ path: walked.skillFilePath,
872
+ field: unknownField,
873
+ });
874
+ }
875
+ // Fatal frontmatter issues exclude the skill.
876
+ if (validated.issues.some((i) => i.severity === 'error')) {
877
+ return null;
878
+ }
879
+ const bodyValidation = validateBody(parsed.body);
880
+ for (const issue of bodyValidation.issues) {
881
+ this.emitEvent({
882
+ kind: 'sdk.skill.error',
883
+ source,
884
+ path: walked.skillFilePath,
885
+ severity: issue.severity,
886
+ message: issue.message,
887
+ });
888
+ }
889
+ if (bodyValidation.issues.some((i) => i.severity === 'error')) {
890
+ return null;
891
+ }
892
+ const vendor = resolveVendor(validated.frontmatter.vendor, source, resolvedVendor);
893
+ const version = validated.frontmatter.version ?? DEFAULT_VERSION;
894
+ const name = validated.frontmatter.name; // validator guarantees presence
895
+ const fqn = `${vendor}/${name}@${version}`;
896
+ const fqnNoVersion = `${vendor}/${name}`;
897
+ // `min_sdk_version` enforcement: exclude with a warning if the running
898
+ // SDK is older than the declared minimum.
899
+ if (validated.frontmatter.min_sdk_version !== undefined && this.config.sdkVersion !== undefined) {
900
+ try {
901
+ if (semverLessThan(this.config.sdkVersion, validated.frontmatter.min_sdk_version)) {
902
+ this.emitEvent({
903
+ kind: 'sdk.skill.error',
904
+ source,
905
+ path: walked.skillFilePath,
906
+ severity: 'warning',
907
+ message: `min_sdk_version ${validated.frontmatter.min_sdk_version} not met by SDK ${this.config.sdkVersion}`,
908
+ });
909
+ return null;
910
+ }
911
+ }
912
+ catch {
913
+ // already reported as a validator error
914
+ }
915
+ }
916
+ const trustTier = SOURCE_TRUST_TIERS[source.type];
917
+ // Walk supporting-file references in the body. Emits a
918
+ // warning for each reference deeper than one segment (authors are
919
+ // encouraged to keep referenced files at the skill root for
920
+ // discoverability) and a warning for each reference whose target file
921
+ // doesn't exist. The skill itself still loads — these are advisory
922
+ // findings the operator can ignore or wire to CI.
923
+ await this.auditBodyReferences(walked, source, parsed.body);
924
+ return {
925
+ fqn,
926
+ fqnNoVersion,
927
+ name,
928
+ vendor,
929
+ version,
930
+ path: walked.dirPath,
931
+ skillFile: walked.skillFilePath,
932
+ mtimeMs: walked.mtimeMs,
933
+ frontmatter: validated.frontmatter,
934
+ body: parsed.body,
935
+ source,
936
+ trustTier,
937
+ raw: parsed.frontmatter,
938
+ };
939
+ }
940
+ /**
941
+ * Inspect the skill body for `[label](path)` and `get_skill_file("path")`
942
+ * references; emit:
943
+ *
944
+ * - `sdk.skill.error severity: 'warning'` when the referenced file
945
+ * doesn't exist under the skill directory.
946
+ * - `sdk.skill.error severity: 'warning'` when a reference has depth
947
+ * greater than 1 (e.g. `scripts/helpers/foo.py`). Authors who
948
+ * intentionally nest files can ignore this warning.
949
+ *
950
+ * The check is best-effort: any `fs.stat` failure other than ENOENT is
951
+ * silently ignored so a transient filesystem error doesn't poison
952
+ * discovery.
953
+ */
954
+ async auditBodyReferences(walked, source, body) {
955
+ const refs = extractBodyReferences(body);
956
+ for (const ref of refs) {
957
+ if (ref.depth > 1) {
958
+ this.emitEvent({
959
+ kind: 'sdk.skill.error',
960
+ source,
961
+ path: walked.skillFilePath,
962
+ severity: 'warning',
963
+ message: `reference '${ref.path}' is ${ref.depth} segments deep — ` +
964
+ 'authors are encouraged to keep referenced files at the skill root for discoverability',
965
+ });
966
+ }
967
+ // Reject obvious traversal in the reference itself before we touch
968
+ // the filesystem; the body might be hostile.
969
+ if (ref.path.split('/').some((seg) => seg === '..')) {
970
+ this.emitEvent({
971
+ kind: 'sdk.skill.error',
972
+ source,
973
+ path: walked.skillFilePath,
974
+ severity: 'warning',
975
+ message: `reference '${ref.path}' contains '..' — skipping existence check`,
976
+ });
977
+ continue;
978
+ }
979
+ const absolute = path.join(walked.dirPath, ref.path);
980
+ try {
981
+ await fs.stat(absolute);
982
+ }
983
+ catch (err) {
984
+ const code = err?.code;
985
+ if (code === 'ENOENT') {
986
+ this.emitEvent({
987
+ kind: 'sdk.skill.error',
988
+ source,
989
+ path: walked.skillFilePath,
990
+ severity: 'warning',
991
+ message: `broken reference '${ref.path}' — file not found in skill directory`,
992
+ });
993
+ }
994
+ // For non-ENOENT errors (permission denied, IO failure), stay
995
+ // silent — discovery shouldn't gate on transient FS issues.
996
+ }
997
+ }
998
+ }
999
+ emitEvent(event) {
1000
+ if (this.emit === undefined)
1001
+ return;
1002
+ try {
1003
+ this.emit(scrubEvent(event));
1004
+ }
1005
+ catch {
1006
+ // Telemetry sinks must not break catalog construction; swallow.
1007
+ }
1008
+ }
1009
+ }
1010
+ /**
1011
+ * Thrown by `SkillsManager.resolveOrThrow` when a caller-pinned specifier
1012
+ * doesn't match anything in the catalog. The wire boundary (TaskClient
1013
+ * startWithAgent / startWithGraph) re-throws these so consumers see a
1014
+ * loud failure per `skills-activation.md` §2.
1015
+ */
1016
+ export class SkillNotFoundError extends Error {
1017
+ specifier;
1018
+ constructor(specifier) {
1019
+ super(`Skill not found: ${specifier}`);
1020
+ this.name = 'SkillNotFoundError';
1021
+ this.specifier = specifier;
1022
+ }
1023
+ }
1024
+ /**
1025
+ * Thrown when a bare-name caller pin matches multiple skills under
1026
+ * different vendors (`acme/code-review` and `local/code-review` both
1027
+ * shipping `name: code-review`). The first-match-wins behavior silently
1028
+ * picked one, which could change at runtime depending on source order.
1029
+ * Force the caller to disambiguate with a `vendor/name` pin.
1030
+ */
1031
+ export class SkillAmbiguousPinError extends Error {
1032
+ specifier;
1033
+ candidates;
1034
+ constructor(specifier, candidates) {
1035
+ super(`Skill pin "${specifier}" is ambiguous — multiple vendors ship a skill ` +
1036
+ `of that name: ${candidates.join(', ')}. Pin with a full ` +
1037
+ '`vendor/name` specifier to disambiguate.');
1038
+ this.name = 'SkillAmbiguousPinError';
1039
+ this.specifier = specifier;
1040
+ this.candidates = candidates;
1041
+ }
1042
+ }
1043
+ /**
1044
+ * Thrown when caller-pinned skills cannot fit inside the slim-index
1045
+ * hard cap (`2 × charBudget`, floored at `charBudget + 4096`). Pinning
1046
+ * is a "must include" contract — silently dropping pins would make the
1047
+ * model behave as if the caller never pinned them; throwing forces the
1048
+ * caller to fix the pin list or raise the budget.
1049
+ */
1050
+ export class SkillsBudgetExceededError extends Error {
1051
+ omittedSkills;
1052
+ hardCap;
1053
+ constructor(omittedSkills, hardCap) {
1054
+ super(`caller-pinned skills exceeded the slim-index hard cap (${hardCap} chars). ` +
1055
+ `Omitted: ${omittedSkills.join(', ')}. Reduce the pin list or raise ` +
1056
+ '`SkillsConfig.indexCharBudget`.');
1057
+ this.name = 'SkillsBudgetExceededError';
1058
+ this.omittedSkills = omittedSkills;
1059
+ this.hardCap = hardCap;
1060
+ }
1061
+ }
1062
+ /**
1063
+ * Thrown when caller-pinned activation can't proceed because of a
1064
+ * `requires:` chain failure — trust violation, depth/breadth/token cap
1065
+ * breach, cycle, or unresolved dependency. The `kind` discriminator
1066
+ * carries the specific reason. Caller-pin
1067
+ * activation is an authoritative contract; failure must be loud rather
1068
+ * than silently dropping the affected skill.
1069
+ */
1070
+ export class QodoSkillError extends Error {
1071
+ skill;
1072
+ reason;
1073
+ constructor(skill, reason, message) {
1074
+ super(message);
1075
+ this.name = 'QodoSkillError';
1076
+ this.skill = skill;
1077
+ this.reason = reason;
1078
+ }
1079
+ }
1080
+ // ---------------------------------------------------------------------------
1081
+ // Helpers
1082
+ // ---------------------------------------------------------------------------
1083
+ /**
1084
+ * Resolve `vendor`: explicit frontmatter `vendor:` wins; otherwise the
1085
+ * source-derived default.
1086
+ */
1087
+ function resolveVendor(explicit, source, resolvedVendor) {
1088
+ if (explicit !== undefined && explicit.length > 0)
1089
+ return explicit;
1090
+ if (resolvedVendor !== undefined && resolvedVendor.length > 0)
1091
+ return resolvedVendor;
1092
+ const def = SOURCE_DEFAULT_VENDORS[source.type];
1093
+ if (def === '__from_package_name__' && source.type === 'package') {
1094
+ return source.packageName;
1095
+ }
1096
+ if (def === '__from_ref__' && source.type === 'cli-fetched') {
1097
+ const ref = source.ref;
1098
+ if (typeof ref === 'string' && ref.length > 0) {
1099
+ const first = ref.split('/')[0];
1100
+ if (first !== undefined && first.length > 0)
1101
+ return first;
1102
+ }
1103
+ return 'cli-fetched';
1104
+ }
1105
+ return def;
1106
+ }
1107
+ function sourceIdentity(source) {
1108
+ switch (source.type) {
1109
+ case 'project':
1110
+ return source.path !== undefined ? `project:${source.path}` : 'project';
1111
+ case 'config':
1112
+ return `config:${source.path}`;
1113
+ case 'user':
1114
+ return source.path !== undefined ? `user:${source.path}` : 'user';
1115
+ case 'package':
1116
+ return `package:${source.packageName}`;
1117
+ case 'cli-fetched':
1118
+ return source.ref !== undefined ? `cli-fetched:${source.ref}` : 'cli-fetched';
1119
+ case 'bundled':
1120
+ return source.path !== undefined ? `bundled:${source.path}` : 'bundled';
1121
+ }
1122
+ }
1123
+ function collectLatestVersions(skills) {
1124
+ const byFqnNoVersion = new Map();
1125
+ for (const skill of skills) {
1126
+ const existing = byFqnNoVersion.get(skill.fqnNoVersion);
1127
+ if (existing === undefined) {
1128
+ byFqnNoVersion.set(skill.fqnNoVersion, skill);
1129
+ continue;
1130
+ }
1131
+ try {
1132
+ if (semverLessThan(existing.version, skill.version)) {
1133
+ byFqnNoVersion.set(skill.fqnNoVersion, skill);
1134
+ }
1135
+ }
1136
+ catch {
1137
+ // both versions semver-invalid (shouldn't happen — validator
1138
+ // catches non-semver) — fall back to FQN string compare
1139
+ if (existing.fqn < skill.fqn)
1140
+ byFqnNoVersion.set(skill.fqnNoVersion, skill);
1141
+ }
1142
+ }
1143
+ return [...byFqnNoVersion.values()].sort((a, b) => a.fqn.localeCompare(b.fqn));
1144
+ }
1145
+ function resolveSpecifier(skills, specifier) {
1146
+ const versionMatch = /^(.+?)@([^@]+)$/.exec(specifier);
1147
+ if (versionMatch !== null) {
1148
+ const fqnNoVersion = versionMatch[1];
1149
+ const version = versionMatch[2];
1150
+ return skills.find((s) => s.fqnNoVersion === fqnNoVersion && s.version === version) ?? null;
1151
+ }
1152
+ // Bare name or vendor/name.
1153
+ if (specifier.includes('/')) {
1154
+ return collectLatestVersions(skills.filter((s) => s.fqnNoVersion === specifier))[0] ?? null;
1155
+ }
1156
+ // Bare name — find latest of each vendor matching `name === specifier`.
1157
+ const matches = skills.filter((s) => s.name === specifier);
1158
+ if (matches.length === 0)
1159
+ return null;
1160
+ const latest = collectLatestVersions(matches);
1161
+ // If multiple vendors match, take the FIRST in the catalog's source-order.
1162
+ // The catalog itself is sorted by FQN; for bare-name resolution we want
1163
+ // the highest-precedence source's pick, which the catalog construction
1164
+ // already chose (first-wins via the shadowing loop). Pick by smallest
1165
+ // source-precedence index across matches.
1166
+ return latest[0] ?? null;
1167
+ }
1168
+ function errorMessage(err) {
1169
+ if (err instanceof Error)
1170
+ return err.message;
1171
+ return String(err);
1172
+ }
1173
+ /**
1174
+ * F9: scrub absolute filesystem paths out of emitted `SkillEvent`s
1175
+ * before forwarding to the consumer's telemetry sink. Two fields
1176
+ * carry filesystem info:
1177
+ *
1178
+ * - `event.path` (absolute SKILL.md path) → reduced to the last two
1179
+ * path segments (`<skill-dir>/<file>`), enough for a developer to
1180
+ * identify the skill from logs without revealing home directories
1181
+ * or repo layout.
1182
+ * - `event.source.path` (for variants that carry it: project / user /
1183
+ * config / bundled) → stripped. The variant tag alone identifies
1184
+ * where the skill came from; the absolute resolved path is local
1185
+ * debug info the manager already retains internally.
1186
+ *
1187
+ * Manager-side state still references absolute paths for symlink
1188
+ * resolution, shadowing, and local debugging. This helper only
1189
+ * sanitises the *emitted* event payload.
1190
+ */
1191
+ function scrubEvent(event) {
1192
+ switch (event.kind) {
1193
+ case 'sdk.skill.error':
1194
+ return {
1195
+ ...event,
1196
+ ...(event.source !== undefined ? { source: stripSourcePath(event.source) } : {}),
1197
+ ...(event.path !== undefined ? { path: relativizeSkillPath(event.path) } : {}),
1198
+ };
1199
+ case 'sdk.skill.unknown_field':
1200
+ return { ...event, path: relativizeSkillPath(event.path) };
1201
+ case 'sdk.skill.source_scanned':
1202
+ return { ...event, source: stripSourcePath(event.source) };
1203
+ case 'sdk.skill.discovered':
1204
+ return { ...event, source: stripSourcePath(event.source) };
1205
+ case 'sdk.skill.shadowed':
1206
+ return {
1207
+ ...event,
1208
+ winner: { ...event.winner, source: stripSourcePath(event.winner.source) },
1209
+ loser: { ...event.loser, source: stripSourcePath(event.loser.source) },
1210
+ };
1211
+ // Runtime / activation / trust / substitution / lockfile /
1212
+ // preprocess / fork / removed events. The `skill` / `parent` /
1213
+ // `child` / `fqn` fields are FQNs (no
1214
+ // filesystem leakage); other payload fields carry no path data. The
1215
+ // `lock_unreachable` variant carries a `source`, which gets the same
1216
+ // path stripping the other source-carrying events get.
1217
+ case 'sdk.skill.loaded':
1218
+ case 'sdk.skill.file_loaded':
1219
+ case 'sdk.skill.file_rejected':
1220
+ case 'sdk.skill.invocation_denied':
1221
+ case 'sdk.skill.activated':
1222
+ case 'sdk.skill.disable_model_invocation_overridden':
1223
+ case 'sdk.skill.allowed_tools_applied':
1224
+ case 'sdk.skill.allowed_tools_advisory':
1225
+ case 'sdk.skill.allowed_tools_unmatched':
1226
+ case 'sdk.skill.hooks_ignored':
1227
+ case 'sdk.skill.tier_assigned':
1228
+ case 'sdk.skill.requires_trust_denied':
1229
+ case 'sdk.skill.requires_cap_exceeded':
1230
+ case 'sdk.skill.substitution_warn':
1231
+ case 'sdk.skill.lock_drift':
1232
+ case 'sdk.skill.preprocess_executed':
1233
+ case 'sdk.skill.preprocess_blocked':
1234
+ case 'sdk.skill.fork_declined':
1235
+ case 'sdk.skill.catalog_epoch_bumped':
1236
+ case 'sdk.skill.index_filtered':
1237
+ return event;
1238
+ case 'sdk.skill.lock_unreachable':
1239
+ case 'sdk.skill.removed':
1240
+ return { ...event, source: stripSourcePath(event.source) };
1241
+ default:
1242
+ return event;
1243
+ }
1244
+ }
1245
+ /**
1246
+ * Identify the top-level directory to watch for hot reload on a given
1247
+ * source variant. Returns `null` for variants whose root we can't
1248
+ * point to without re-walking the env.
1249
+ */
1250
+ function watchableRoot(source) {
1251
+ switch (source.type) {
1252
+ case 'project':
1253
+ return source.path ?? null;
1254
+ case 'config':
1255
+ return source.path;
1256
+ case 'user':
1257
+ return source.path ?? null;
1258
+ case 'cli-fetched':
1259
+ case 'package':
1260
+ case 'bundled':
1261
+ return null;
1262
+ }
1263
+ }
1264
+ /**
1265
+ * Whether a source-type is eligible for hot-reload watching. `package`
1266
+ * + `bundled` ship with code and shouldn't churn at runtime; `cli-fetched`
1267
+ * is intentionally static (the CLI command stamps a frozen ref).
1268
+ */
1269
+ function isWatchableSourceType(type) {
1270
+ return type === 'project' || type === 'config' || type === 'user';
1271
+ }
1272
+ /**
1273
+ * Resolve the env-derived default directory for sources whose `path`
1274
+ * field is optional (`project` / `user`). Mirrors the walker's
1275
+ * defaulting logic so the watcher can target the same directory the
1276
+ * discoverer scanned, even when no skills exist there yet. Returns
1277
+ * `null` for source variants without an env-derived default.
1278
+ *
1279
+ * Defaults `project` to the env CWD and `user` to
1280
+ * `$XDG_CONFIG_HOME/qodo/skills` or `~/.qodo/skills`.
1281
+ */
1282
+ function resolveDefaultWatchRoot(source, env) {
1283
+ if (source.type === 'project') {
1284
+ return path.join(env.cwd, '.qodo', 'skills');
1285
+ }
1286
+ if (source.type === 'user') {
1287
+ const xdg = env.env['XDG_CONFIG_HOME'];
1288
+ if (xdg !== undefined && xdg.length > 0)
1289
+ return path.join(xdg, 'qodo', 'skills');
1290
+ return path.join(env.homedir, '.qodo', 'skills');
1291
+ }
1292
+ return null;
1293
+ }
1294
+ /**
1295
+ * Fill `SkillsConfig.requires` defaults. Invalid (non-finite, non-positive)
1296
+ * inputs fall back to the documented defaults rather than throwing — the
1297
+ * caps are defense-in-depth; a misconfigured cap should not break the
1298
+ * activation path.
1299
+ */
1300
+ function resolveRequiresCaps(config) {
1301
+ const maxDepth = pickPositiveInt(config?.maxDepth, REQUIRES_DEFAULT_MAX_DEPTH);
1302
+ const maxTransitive = pickPositiveInt(config?.maxTransitive, REQUIRES_DEFAULT_MAX_TRANSITIVE);
1303
+ const maxTransitiveTokens = pickPositiveInt(config?.maxTransitiveTokens, REQUIRES_DEFAULT_MAX_TRANSITIVE_TOKENS);
1304
+ return { maxDepth, maxTransitive, maxTransitiveTokens };
1305
+ }
1306
+ function pickPositiveInt(value, fallback) {
1307
+ if (value === undefined)
1308
+ return fallback;
1309
+ if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) {
1310
+ return fallback;
1311
+ }
1312
+ return Math.floor(value);
1313
+ }
1314
+ function stripSourcePath(source) {
1315
+ switch (source.type) {
1316
+ case 'project':
1317
+ case 'user':
1318
+ case 'bundled':
1319
+ return { type: source.type };
1320
+ case 'config':
1321
+ // `config` requires `path` in the type — replace with the basename
1322
+ // so the operator can still tell `config:foo` apart from
1323
+ // `config:bar` without exposing the absolute resolved location.
1324
+ return { type: 'config', path: path.basename(source.path) };
1325
+ case 'package':
1326
+ return source.path !== undefined
1327
+ ? { type: 'package', packageName: source.packageName, path: path.basename(source.path) }
1328
+ : { type: 'package', packageName: source.packageName };
1329
+ case 'cli-fetched':
1330
+ return source;
1331
+ }
1332
+ }
1333
+ /**
1334
+ * Reduce an absolute SKILL.md path to its last two segments, e.g.
1335
+ * `/path/to/.qodo/skills/code-review/SKILL.md` → `code-review/SKILL.md`.
1336
+ * Non-absolute paths and very short paths pass through unchanged.
1337
+ */
1338
+ function relativizeSkillPath(absPath) {
1339
+ if (!path.isAbsolute(absPath))
1340
+ return absPath;
1341
+ const segments = absPath.split(/[/\\]/).filter((s) => s.length > 0);
1342
+ if (segments.length <= 2)
1343
+ return segments.join('/');
1344
+ return segments.slice(-2).join('/');
1345
+ }
1346
+ /**
1347
+ * Build the search-path list for lockfile discovery. Returns:
1348
+ *
1349
+ * 1. `cwd` (highest priority).
1350
+ * 2. The nearest ancestor of `cwd` that contains `agent.toml`. Walks
1351
+ * up at most {@link AGENT_TOML_WALK_DEPTH} levels so the search
1352
+ * is bounded even in deeply-nested directory trees.
1353
+ *
1354
+ * Both directories are searched in order; the first `qodo.lock.yaml`
1355
+ * hit wins. Used by `applyLockfile()` to satisfy the docs' "alongside
1356
+ * agent.toml" semantics.
1357
+ */
1358
+ async function collectLockfileSearchPaths(cwd) {
1359
+ const out = [cwd];
1360
+ let current = cwd;
1361
+ for (let depth = 0; depth < AGENT_TOML_WALK_DEPTH; depth += 1) {
1362
+ const candidate = path.join(current, 'agent.toml');
1363
+ try {
1364
+ const stat = await fs.stat(candidate);
1365
+ if (stat.isFile()) {
1366
+ // agent.toml lives at `current` — the lockfile would sit next
1367
+ // to it. Add `current` to the search list unless it's already
1368
+ // included as the CWD.
1369
+ if (current !== cwd)
1370
+ out.push(current);
1371
+ return out;
1372
+ }
1373
+ }
1374
+ catch {
1375
+ // Missing agent.toml at this level — keep walking up.
1376
+ }
1377
+ const parent = path.dirname(current);
1378
+ if (parent === current)
1379
+ break; // hit the filesystem root
1380
+ current = parent;
1381
+ }
1382
+ return out;
1383
+ }
1384
+ /** Maximum levels of parent directories the lockfile search walks. */
1385
+ const AGENT_TOML_WALK_DEPTH = 8;
1386
+ //# sourceMappingURL=manager.js.map