@draht/coding-agent 2026.3.14 → 2026.3.25-1

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 (345) hide show
  1. package/README.md +45 -30
  2. package/dist/bun/cli.d.ts +3 -0
  3. package/dist/bun/cli.d.ts.map +1 -0
  4. package/dist/bun/cli.js +7 -0
  5. package/dist/bun/cli.js.map +1 -0
  6. package/dist/bun/register-bedrock.d.ts +2 -0
  7. package/dist/bun/register-bedrock.d.ts.map +1 -0
  8. package/dist/bun/register-bedrock.js +4 -0
  9. package/dist/bun/register-bedrock.js.map +1 -0
  10. package/dist/cli/args.d.ts +1 -0
  11. package/dist/cli/args.d.ts.map +1 -1
  12. package/dist/cli/args.js +11 -6
  13. package/dist/cli/args.js.map +1 -1
  14. package/dist/cli/file-processor.d.ts.map +1 -1
  15. package/dist/cli/file-processor.js +4 -0
  16. package/dist/cli/file-processor.js.map +1 -1
  17. package/dist/cli/initial-message.d.ts +18 -0
  18. package/dist/cli/initial-message.d.ts.map +1 -0
  19. package/dist/cli/initial-message.js +22 -0
  20. package/dist/cli/initial-message.js.map +1 -0
  21. package/dist/cli/session-picker.d.ts.map +1 -1
  22. package/dist/cli/session-picker.js +2 -1
  23. package/dist/cli/session-picker.js.map +1 -1
  24. package/dist/cli.d.ts.map +1 -1
  25. package/dist/cli.js +1 -3
  26. package/dist/cli.js.map +1 -1
  27. package/dist/core/agent-session.d.ts +38 -5
  28. package/dist/core/agent-session.d.ts.map +1 -1
  29. package/dist/core/agent-session.js +201 -73
  30. package/dist/core/agent-session.js.map +1 -1
  31. package/dist/core/bash-executor.d.ts +6 -7
  32. package/dist/core/bash-executor.d.ts.map +1 -1
  33. package/dist/core/bash-executor.js +8 -107
  34. package/dist/core/bash-executor.js.map +1 -1
  35. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  36. package/dist/core/compaction/branch-summarization.js +1 -0
  37. package/dist/core/compaction/branch-summarization.js.map +1 -1
  38. package/dist/core/compaction/compaction.d.ts.map +1 -1
  39. package/dist/core/compaction/compaction.js +2 -0
  40. package/dist/core/compaction/compaction.js.map +1 -1
  41. package/dist/core/exec.d.ts.map +1 -1
  42. package/dist/core/exec.js +7 -3
  43. package/dist/core/exec.js.map +1 -1
  44. package/dist/core/export-html/index.d.ts +2 -2
  45. package/dist/core/export-html/index.d.ts.map +1 -1
  46. package/dist/core/export-html/index.js +7 -6
  47. package/dist/core/export-html/index.js.map +1 -1
  48. package/dist/core/export-html/template.css +43 -13
  49. package/dist/core/export-html/template.html +1 -0
  50. package/dist/core/export-html/template.js +107 -0
  51. package/dist/core/export-html/tool-renderer.d.ts +2 -2
  52. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  53. package/dist/core/export-html/tool-renderer.js +41 -16
  54. package/dist/core/export-html/tool-renderer.js.map +1 -1
  55. package/dist/core/extensions/index.d.ts +4 -3
  56. package/dist/core/extensions/index.d.ts.map +1 -1
  57. package/dist/core/extensions/index.js +1 -1
  58. package/dist/core/extensions/index.js.map +1 -1
  59. package/dist/core/extensions/loader.d.ts.map +1 -1
  60. package/dist/core/extensions/loader.js +16 -6
  61. package/dist/core/extensions/loader.js.map +1 -1
  62. package/dist/core/extensions/runner.d.ts +9 -9
  63. package/dist/core/extensions/runner.d.ts.map +1 -1
  64. package/dist/core/extensions/runner.js +89 -71
  65. package/dist/core/extensions/runner.js.map +1 -1
  66. package/dist/core/extensions/types.d.ts +49 -13
  67. package/dist/core/extensions/types.d.ts.map +1 -1
  68. package/dist/core/extensions/types.js.map +1 -1
  69. package/dist/core/extensions/wrapper.d.ts +4 -11
  70. package/dist/core/extensions/wrapper.d.ts.map +1 -1
  71. package/dist/core/extensions/wrapper.js +6 -86
  72. package/dist/core/extensions/wrapper.js.map +1 -1
  73. package/dist/core/footer-data-provider.d.ts +13 -1
  74. package/dist/core/footer-data-provider.d.ts.map +1 -1
  75. package/dist/core/footer-data-provider.js +155 -37
  76. package/dist/core/footer-data-provider.js.map +1 -1
  77. package/dist/core/index.d.ts +2 -1
  78. package/dist/core/index.d.ts.map +1 -1
  79. package/dist/core/index.js +2 -1
  80. package/dist/core/index.js.map +1 -1
  81. package/dist/core/keybindings.d.ts +270 -50
  82. package/dist/core/keybindings.d.ts.map +1 -1
  83. package/dist/core/keybindings.js +222 -134
  84. package/dist/core/keybindings.js.map +1 -1
  85. package/dist/core/model-registry.d.ts +1 -0
  86. package/dist/core/model-registry.d.ts.map +1 -1
  87. package/dist/core/model-registry.js +49 -23
  88. package/dist/core/model-registry.js.map +1 -1
  89. package/dist/core/model-resolver.d.ts +6 -0
  90. package/dist/core/model-resolver.d.ts.map +1 -1
  91. package/dist/core/model-resolver.js +41 -17
  92. package/dist/core/model-resolver.js.map +1 -1
  93. package/dist/core/output-guard.d.ts +6 -0
  94. package/dist/core/output-guard.d.ts.map +1 -0
  95. package/dist/core/output-guard.js +59 -0
  96. package/dist/core/output-guard.js.map +1 -0
  97. package/dist/core/package-manager.d.ts +22 -1
  98. package/dist/core/package-manager.d.ts.map +1 -1
  99. package/dist/core/package-manager.js +373 -53
  100. package/dist/core/package-manager.js.map +1 -1
  101. package/dist/core/prompt-templates.d.ts +2 -1
  102. package/dist/core/prompt-templates.d.ts.map +1 -1
  103. package/dist/core/prompt-templates.js +39 -39
  104. package/dist/core/prompt-templates.js.map +1 -1
  105. package/dist/core/resolve-config-value.d.ts.map +1 -1
  106. package/dist/core/resolve-config-value.js +43 -8
  107. package/dist/core/resolve-config-value.js.map +1 -1
  108. package/dist/core/resource-loader.d.ts +6 -7
  109. package/dist/core/resource-loader.d.ts.map +1 -1
  110. package/dist/core/resource-loader.js +141 -118
  111. package/dist/core/resource-loader.js.map +1 -1
  112. package/dist/core/sdk.d.ts +3 -3
  113. package/dist/core/sdk.d.ts.map +1 -1
  114. package/dist/core/sdk.js +4 -4
  115. package/dist/core/sdk.js.map +1 -1
  116. package/dist/core/session-manager.d.ts +6 -0
  117. package/dist/core/session-manager.d.ts.map +1 -1
  118. package/dist/core/session-manager.js +9 -10
  119. package/dist/core/session-manager.js.map +1 -1
  120. package/dist/core/settings-manager.d.ts +3 -0
  121. package/dist/core/settings-manager.d.ts.map +1 -1
  122. package/dist/core/settings-manager.js +8 -0
  123. package/dist/core/settings-manager.js.map +1 -1
  124. package/dist/core/skills.d.ts +5 -3
  125. package/dist/core/skills.d.ts.map +1 -1
  126. package/dist/core/skills.js +54 -9
  127. package/dist/core/skills.js.map +1 -1
  128. package/dist/core/slash-commands.d.ts +2 -3
  129. package/dist/core/slash-commands.d.ts.map +1 -1
  130. package/dist/core/slash-commands.js +3 -2
  131. package/dist/core/slash-commands.js.map +1 -1
  132. package/dist/core/source-info.d.ts +18 -0
  133. package/dist/core/source-info.d.ts.map +1 -0
  134. package/dist/core/source-info.js +19 -0
  135. package/dist/core/source-info.js.map +1 -0
  136. package/dist/core/system-prompt.d.ts.map +1 -1
  137. package/dist/core/system-prompt.js +17 -60
  138. package/dist/core/system-prompt.js.map +1 -1
  139. package/dist/core/tools/bash.d.ts +24 -6
  140. package/dist/core/tools/bash.d.ts.map +1 -1
  141. package/dist/core/tools/bash.js +210 -110
  142. package/dist/core/tools/bash.js.map +1 -1
  143. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  144. package/dist/core/tools/edit-diff.js +1 -0
  145. package/dist/core/tools/edit-diff.js.map +1 -1
  146. package/dist/core/tools/edit.d.ts +14 -2
  147. package/dist/core/tools/edit.d.ts.map +1 -1
  148. package/dist/core/tools/edit.js +95 -23
  149. package/dist/core/tools/edit.js.map +1 -1
  150. package/dist/core/tools/file-mutation-queue.d.ts +6 -0
  151. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -0
  152. package/dist/core/tools/file-mutation-queue.js +37 -0
  153. package/dist/core/tools/file-mutation-queue.js.map +1 -0
  154. package/dist/core/tools/find.d.ts +11 -4
  155. package/dist/core/tools/find.d.ts.map +1 -1
  156. package/dist/core/tools/find.js +82 -30
  157. package/dist/core/tools/find.js.map +1 -1
  158. package/dist/core/tools/grep.d.ts +15 -4
  159. package/dist/core/tools/grep.d.ts.map +1 -1
  160. package/dist/core/tools/grep.js +83 -29
  161. package/dist/core/tools/grep.js.map +1 -1
  162. package/dist/core/tools/index.d.ts +58 -19
  163. package/dist/core/tools/index.d.ts.map +1 -1
  164. package/dist/core/tools/index.js +51 -26
  165. package/dist/core/tools/index.js.map +1 -1
  166. package/dist/core/tools/ls.d.ts +9 -3
  167. package/dist/core/tools/ls.d.ts.map +1 -1
  168. package/dist/core/tools/ls.js +67 -13
  169. package/dist/core/tools/ls.js.map +1 -1
  170. package/dist/core/tools/read.d.ts +10 -3
  171. package/dist/core/tools/read.d.ts.map +1 -1
  172. package/dist/core/tools/read.js +110 -51
  173. package/dist/core/tools/read.js.map +1 -1
  174. package/dist/core/tools/render-utils.d.ts +21 -0
  175. package/dist/core/tools/render-utils.d.ts.map +1 -0
  176. package/dist/core/tools/render-utils.js +49 -0
  177. package/dist/core/tools/render-utils.js.map +1 -0
  178. package/dist/core/tools/tool-definition-wrapper.d.ts +14 -0
  179. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -0
  180. package/dist/core/tools/tool-definition-wrapper.js +30 -0
  181. package/dist/core/tools/tool-definition-wrapper.js.map +1 -0
  182. package/dist/core/tools/write.d.ts +9 -3
  183. package/dist/core/tools/write.d.ts.map +1 -1
  184. package/dist/core/tools/write.js +168 -30
  185. package/dist/core/tools/write.js.map +1 -1
  186. package/dist/index.d.ts +5 -4
  187. package/dist/index.d.ts.map +1 -1
  188. package/dist/index.js +4 -3
  189. package/dist/index.js.map +1 -1
  190. package/dist/main.d.ts.map +1 -1
  191. package/dist/main.js +105 -226
  192. package/dist/main.js.map +1 -1
  193. package/dist/modes/interactive/components/bash-execution.d.ts +0 -1
  194. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  195. package/dist/modes/interactive/components/bash-execution.js +22 -9
  196. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  197. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
  198. package/dist/modes/interactive/components/bordered-loader.js +1 -1
  199. package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
  200. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  201. package/dist/modes/interactive/components/branch-summary-message.js +2 -2
  202. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  203. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  204. package/dist/modes/interactive/components/compaction-summary-message.js +2 -2
  205. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  206. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  207. package/dist/modes/interactive/components/config-selector.js +8 -8
  208. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  209. package/dist/modes/interactive/components/custom-editor.d.ts +3 -3
  210. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  211. package/dist/modes/interactive/components/custom-editor.js +6 -6
  212. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  213. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  214. package/dist/modes/interactive/components/extension-editor.js +9 -9
  215. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  216. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  217. package/dist/modes/interactive/components/extension-input.js +5 -5
  218. package/dist/modes/interactive/components/extension-input.js.map +1 -1
  219. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  220. package/dist/modes/interactive/components/extension-selector.js +8 -8
  221. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  222. package/dist/modes/interactive/components/index.d.ts +1 -1
  223. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  224. package/dist/modes/interactive/components/index.js +1 -1
  225. package/dist/modes/interactive/components/index.js.map +1 -1
  226. package/dist/modes/interactive/components/keybinding-hints.d.ts +3 -36
  227. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  228. package/dist/modes/interactive/components/keybinding-hints.js +5 -44
  229. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
  230. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  231. package/dist/modes/interactive/components/login-dialog.js +6 -6
  232. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  233. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  234. package/dist/modes/interactive/components/model-selector.js +13 -9
  235. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  236. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  237. package/dist/modes/interactive/components/oauth-selector.js +6 -6
  238. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  239. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  240. package/dist/modes/interactive/components/scoped-models-selector.js +4 -4
  241. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  242. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  243. package/dist/modes/interactive/components/session-selector.js +32 -35
  244. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  245. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  246. package/dist/modes/interactive/components/settings-selector.js +5 -1
  247. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  248. package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
  249. package/dist/modes/interactive/components/show-images-selector.js +5 -1
  250. package/dist/modes/interactive/components/show-images-selector.js.map +1 -1
  251. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  252. package/dist/modes/interactive/components/skill-invocation-message.js +2 -2
  253. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  254. package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
  255. package/dist/modes/interactive/components/theme-selector.js +5 -1
  256. package/dist/modes/interactive/components/theme-selector.js.map +1 -1
  257. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  258. package/dist/modes/interactive/components/thinking-selector.js +5 -1
  259. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  260. package/dist/modes/interactive/components/tool-execution.d.ts +16 -34
  261. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  262. package/dist/modes/interactive/components/tool-execution.js +128 -636
  263. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  264. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  265. package/dist/modes/interactive/components/tree-selector.js +27 -16
  266. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  267. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
  268. package/dist/modes/interactive/components/user-message-selector.js +6 -6
  269. package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
  270. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  271. package/dist/modes/interactive/components/user-message.js +2 -1
  272. package/dist/modes/interactive/components/user-message.js.map +1 -1
  273. package/dist/modes/interactive/interactive-mode.d.ts +7 -11
  274. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  275. package/dist/modes/interactive/interactive-mode.js +353 -214
  276. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  277. package/dist/modes/interactive/theme/theme.d.ts +3 -0
  278. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  279. package/dist/modes/interactive/theme/theme.js +63 -37
  280. package/dist/modes/interactive/theme/theme.js.map +1 -1
  281. package/dist/modes/print-mode.d.ts.map +1 -1
  282. package/dist/modes/print-mode.js +5 -11
  283. package/dist/modes/print-mode.js.map +1 -1
  284. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  285. package/dist/modes/rpc/rpc-mode.js +27 -17
  286. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  287. package/dist/modes/rpc/rpc-types.d.ts +3 -4
  288. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  289. package/dist/modes/rpc/rpc-types.js.map +1 -1
  290. package/dist/utils/child-process.d.ts +11 -0
  291. package/dist/utils/child-process.d.ts.map +1 -0
  292. package/dist/utils/child-process.js +78 -0
  293. package/dist/utils/child-process.js.map +1 -0
  294. package/dist/utils/clipboard-image.d.ts.map +1 -1
  295. package/dist/utils/clipboard-image.js +94 -11
  296. package/dist/utils/clipboard-image.js.map +1 -1
  297. package/dist/utils/clipboard-native.d.ts +1 -0
  298. package/dist/utils/clipboard-native.d.ts.map +1 -1
  299. package/dist/utils/clipboard-native.js.map +1 -1
  300. package/dist/utils/clipboard.d.ts +1 -1
  301. package/dist/utils/clipboard.d.ts.map +1 -1
  302. package/dist/utils/clipboard.js +27 -16
  303. package/dist/utils/clipboard.js.map +1 -1
  304. package/dist/utils/exif-orientation.d.ts +5 -0
  305. package/dist/utils/exif-orientation.d.ts.map +1 -0
  306. package/dist/utils/exif-orientation.js +158 -0
  307. package/dist/utils/exif-orientation.js.map +1 -0
  308. package/dist/utils/image-convert.d.ts.map +1 -1
  309. package/dist/utils/image-convert.js +5 -1
  310. package/dist/utils/image-convert.js.map +1 -1
  311. package/dist/utils/image-resize.d.ts +5 -5
  312. package/dist/utils/image-resize.d.ts.map +1 -1
  313. package/dist/utils/image-resize.js +51 -95
  314. package/dist/utils/image-resize.js.map +1 -1
  315. package/dist/utils/tools-manager.d.ts.map +1 -1
  316. package/dist/utils/tools-manager.js +5 -4
  317. package/dist/utils/tools-manager.js.map +1 -1
  318. package/docs/custom-provider.md +6 -2
  319. package/docs/extensions.md +108 -21
  320. package/docs/keybindings.md +103 -112
  321. package/docs/models.md +39 -1
  322. package/docs/packages.md +9 -0
  323. package/docs/providers.md +7 -0
  324. package/docs/rpc.md +15 -6
  325. package/docs/sdk.md +2 -2
  326. package/docs/settings.md +9 -0
  327. package/docs/terminal-setup.md +11 -0
  328. package/docs/tui.md +2 -2
  329. package/examples/extensions/README.md +2 -2
  330. package/examples/extensions/antigravity-image-gen.ts +5 -3
  331. package/examples/extensions/built-in-tool-renderer.ts +8 -8
  332. package/examples/extensions/commands.ts +3 -3
  333. package/examples/extensions/minimal-mode.ts +14 -14
  334. package/examples/extensions/question.ts +2 -2
  335. package/examples/extensions/questionnaire.ts +2 -2
  336. package/examples/extensions/subagent/index.ts +30 -8
  337. package/examples/extensions/titlebar-spinner.ts +2 -2
  338. package/examples/extensions/todo.ts +2 -2
  339. package/examples/extensions/tool-override.ts +9 -7
  340. package/examples/extensions/truncated-tool.ts +8 -5
  341. package/examples/sdk/04-skills.ts +8 -2
  342. package/examples/sdk/08-prompt-templates.ts +8 -2
  343. package/examples/sdk/12-full-control.ts +0 -1
  344. package/examples/sdk/README.md +1 -1
  345. package/package.json +4 -4
@@ -7,7 +7,9 @@ import ignore from "ignore";
7
7
  import { minimatch } from "minimatch";
8
8
  import { getProjectConfigDir } from "../config.js";
9
9
  import { parseGitUrl } from "../utils/git.js";
10
+ import { isStdoutTakenOver } from "./output-guard.js";
10
11
  const NETWORK_TIMEOUT_MS = 10000;
12
+ const UPDATE_CHECK_CONCURRENCY = 4;
11
13
  function isOfflineModeEnabled() {
12
14
  const value = process.env.DRAHT_OFFLINE ?? process.env.PI_OFFLINE;
13
15
  if (!value)
@@ -25,6 +27,9 @@ const IGNORE_FILE_NAMES = [".gitignore", ".ignore", ".fdignore"];
25
27
  function toPosixPath(p) {
26
28
  return p.split(sep).join("/");
27
29
  }
30
+ function getHomeDir() {
31
+ return process.env.HOME || homedir();
32
+ }
28
33
  function prefixIgnorePattern(line, prefix) {
29
34
  const trimmed = line.trim();
30
35
  if (!trimmed)
@@ -383,43 +388,50 @@ function collectResourceFiles(dir, resourceType) {
383
388
  return collectFiles(dir, FILE_PATTERNS[resourceType]);
384
389
  }
385
390
  function matchesAnyPattern(filePath, patterns, baseDir) {
386
- const rel = relative(baseDir, filePath);
391
+ const rel = toPosixPath(relative(baseDir, filePath));
387
392
  const name = basename(filePath);
393
+ const filePathPosix = toPosixPath(filePath);
388
394
  const isSkillFile = name === "SKILL.md";
389
395
  const parentDir = isSkillFile ? dirname(filePath) : undefined;
390
- const parentRel = isSkillFile ? relative(baseDir, parentDir) : undefined;
396
+ const parentRel = isSkillFile ? toPosixPath(relative(baseDir, parentDir)) : undefined;
391
397
  const parentName = isSkillFile ? basename(parentDir) : undefined;
398
+ const parentDirPosix = isSkillFile ? toPosixPath(parentDir) : undefined;
392
399
  return patterns.some((pattern) => {
393
- if (minimatch(rel, pattern) || minimatch(name, pattern) || minimatch(filePath, pattern)) {
400
+ const normalizedPattern = toPosixPath(pattern);
401
+ if (minimatch(rel, normalizedPattern) ||
402
+ minimatch(name, normalizedPattern) ||
403
+ minimatch(filePathPosix, normalizedPattern)) {
394
404
  return true;
395
405
  }
396
406
  if (!isSkillFile)
397
407
  return false;
398
- return minimatch(parentRel, pattern) || minimatch(parentName, pattern) || minimatch(parentDir, pattern);
408
+ return (minimatch(parentRel, normalizedPattern) ||
409
+ minimatch(parentName, normalizedPattern) ||
410
+ minimatch(parentDirPosix, normalizedPattern));
399
411
  });
400
412
  }
401
413
  function normalizeExactPattern(pattern) {
402
- if (pattern.startsWith("./") || pattern.startsWith(".\\")) {
403
- return pattern.slice(2);
404
- }
405
- return pattern;
414
+ const normalized = pattern.startsWith("./") || pattern.startsWith(".\\") ? pattern.slice(2) : pattern;
415
+ return toPosixPath(normalized);
406
416
  }
407
417
  function matchesAnyExactPattern(filePath, patterns, baseDir) {
408
418
  if (patterns.length === 0)
409
419
  return false;
410
- const rel = relative(baseDir, filePath);
420
+ const rel = toPosixPath(relative(baseDir, filePath));
411
421
  const name = basename(filePath);
422
+ const filePathPosix = toPosixPath(filePath);
412
423
  const isSkillFile = name === "SKILL.md";
413
424
  const parentDir = isSkillFile ? dirname(filePath) : undefined;
414
- const parentRel = isSkillFile ? relative(baseDir, parentDir) : undefined;
425
+ const parentRel = isSkillFile ? toPosixPath(relative(baseDir, parentDir)) : undefined;
426
+ const parentDirPosix = isSkillFile ? toPosixPath(parentDir) : undefined;
415
427
  return patterns.some((pattern) => {
416
428
  const normalized = normalizeExactPattern(pattern);
417
- if (normalized === rel || normalized === filePath) {
429
+ if (normalized === rel || normalized === filePathPosix) {
418
430
  return true;
419
431
  }
420
432
  if (!isSkillFile)
421
433
  return false;
422
- return normalized === parentRel || normalized === parentDir;
434
+ return normalized === parentRel || normalized === parentDirPosix;
423
435
  });
424
436
  }
425
437
  function getOverridePatterns(entries) {
@@ -500,6 +512,7 @@ export class DefaultPackageManager {
500
512
  agentDir;
501
513
  settingsManager;
502
514
  globalNpmRoot;
515
+ globalNpmRootCommandKey;
503
516
  progressCallback;
504
517
  constructor(options) {
505
518
  this.cwd = options.cwd;
@@ -662,18 +675,27 @@ export class DefaultPackageManager {
662
675
  const globalSettings = this.settingsManager.getGlobalSettings();
663
676
  const projectSettings = this.settingsManager.getProjectSettings();
664
677
  const identity = source ? this.getPackageIdentity(source) : undefined;
678
+ let matched = false;
665
679
  for (const pkg of globalSettings.packages ?? []) {
666
680
  const sourceStr = typeof pkg === "string" ? pkg : pkg.source;
667
681
  if (identity && this.getPackageIdentity(sourceStr, "user") !== identity)
668
682
  continue;
683
+ matched = true;
669
684
  await this.updateSourceForScope(sourceStr, "user");
670
685
  }
671
686
  for (const pkg of projectSettings.packages ?? []) {
672
687
  const sourceStr = typeof pkg === "string" ? pkg : pkg.source;
673
688
  if (identity && this.getPackageIdentity(sourceStr, "project") !== identity)
674
689
  continue;
690
+ matched = true;
675
691
  await this.updateSourceForScope(sourceStr, "project");
676
692
  }
693
+ if (source && !matched) {
694
+ throw new Error(this.buildNoMatchingPackageMessage(source, [
695
+ ...(globalSettings.packages ?? []),
696
+ ...(projectSettings.packages ?? []),
697
+ ]));
698
+ }
677
699
  }
678
700
  async updateSourceForScope(source, scope) {
679
701
  if (isOfflineModeEnabled()) {
@@ -684,7 +706,10 @@ export class DefaultPackageManager {
684
706
  if (parsed.pinned)
685
707
  return;
686
708
  await this.withProgress("update", source, `Updating ${source}...`, async () => {
687
- await this.installNpm(parsed, scope, false);
709
+ await this.installNpm({
710
+ ...parsed,
711
+ spec: `${parsed.name}@latest`,
712
+ }, scope, false);
688
713
  });
689
714
  return;
690
715
  }
@@ -697,6 +722,62 @@ export class DefaultPackageManager {
697
722
  return;
698
723
  }
699
724
  }
725
+ async checkForAvailableUpdates() {
726
+ if (isOfflineModeEnabled()) {
727
+ return [];
728
+ }
729
+ const globalSettings = this.settingsManager.getGlobalSettings();
730
+ const projectSettings = this.settingsManager.getProjectSettings();
731
+ const allPackages = [];
732
+ for (const pkg of projectSettings.packages ?? []) {
733
+ allPackages.push({ pkg, scope: "project" });
734
+ }
735
+ for (const pkg of globalSettings.packages ?? []) {
736
+ allPackages.push({ pkg, scope: "user" });
737
+ }
738
+ const packageSources = this.dedupePackages(allPackages);
739
+ const checks = packageSources
740
+ .filter((entry) => entry.scope !== "temporary")
741
+ .map((entry) => async () => {
742
+ const source = typeof entry.pkg === "string" ? entry.pkg : entry.pkg.source;
743
+ const parsed = this.parseSource(source);
744
+ if (parsed.type === "local" || parsed.pinned) {
745
+ return undefined;
746
+ }
747
+ if (parsed.type === "npm") {
748
+ const installedPath = this.getNpmInstallPath(parsed, entry.scope);
749
+ if (!existsSync(installedPath)) {
750
+ return undefined;
751
+ }
752
+ const hasUpdate = await this.npmHasAvailableUpdate(parsed, installedPath);
753
+ if (!hasUpdate) {
754
+ return undefined;
755
+ }
756
+ return {
757
+ source,
758
+ displayName: parsed.name,
759
+ type: "npm",
760
+ scope: entry.scope,
761
+ };
762
+ }
763
+ const installedPath = this.getGitInstallPath(parsed, entry.scope);
764
+ if (!existsSync(installedPath)) {
765
+ return undefined;
766
+ }
767
+ const hasUpdate = await this.gitHasAvailableUpdate(installedPath);
768
+ if (!hasUpdate) {
769
+ return undefined;
770
+ }
771
+ return {
772
+ source,
773
+ displayName: `${parsed.host}/${parsed.path}`,
774
+ type: "git",
775
+ scope: entry.scope,
776
+ };
777
+ });
778
+ const results = await this.runWithConcurrency(checks, UPDATE_CHECK_CONCURRENCY);
779
+ return results.filter((result) => result !== undefined);
780
+ }
700
781
  async resolvePackageSources(sources, accumulator, onMissing) {
701
782
  for (const { pkg, scope } of sources) {
702
783
  const sourceStr = typeof pkg === "string" ? pkg : pkg.source;
@@ -726,7 +807,8 @@ export class DefaultPackageManager {
726
807
  };
727
808
  if (parsed.type === "npm") {
728
809
  const installedPath = this.getNpmInstallPath(parsed, scope);
729
- const needsInstall = !existsSync(installedPath) || (await this.npmNeedsUpdate(parsed, installedPath));
810
+ const needsInstall = !existsSync(installedPath) ||
811
+ (parsed.pinned && !(await this.installedNpmMatchesPinnedVersion(parsed, installedPath)));
730
812
  if (needsInstall) {
731
813
  const installed = await installMissing();
732
814
  if (!installed)
@@ -809,6 +891,35 @@ export class DefaultPackageManager {
809
891
  const baseDir = this.getBaseDirForScope(scope);
810
892
  return `local:${this.resolvePathFromBase(parsed.path, baseDir)}`;
811
893
  }
894
+ buildNoMatchingPackageMessage(source, configuredPackages) {
895
+ const suggestion = this.findSuggestedConfiguredSource(source, configuredPackages);
896
+ if (!suggestion) {
897
+ return `No matching package found for ${source}`;
898
+ }
899
+ return `No matching package found for ${source}. Did you mean ${suggestion}?`;
900
+ }
901
+ findSuggestedConfiguredSource(source, configuredPackages) {
902
+ const trimmedSource = source.trim();
903
+ const suggestions = new Set();
904
+ for (const pkg of configuredPackages) {
905
+ const sourceStr = this.getPackageSourceString(pkg);
906
+ const parsed = this.parseSource(sourceStr);
907
+ if (parsed.type === "npm") {
908
+ if (trimmedSource === parsed.name || trimmedSource === parsed.spec) {
909
+ suggestions.add(sourceStr);
910
+ }
911
+ continue;
912
+ }
913
+ if (parsed.type === "git") {
914
+ const shorthand = `${parsed.host}/${parsed.path}`;
915
+ const shorthandWithRef = parsed.ref ? `${shorthand}@${parsed.ref}` : undefined;
916
+ if (trimmedSource === shorthand || (shorthandWithRef && trimmedSource === shorthandWithRef)) {
917
+ suggestions.add(sourceStr);
918
+ }
919
+ }
920
+ }
921
+ return suggestions.values().next().value;
922
+ }
812
923
  packageSourcesMatch(existing, inputSource, scope) {
813
924
  const left = this.getSourceMatchKeyForSettings(this.getPackageSourceString(existing), scope);
814
925
  const right = this.getSourceMatchKeyForInput(inputSource);
@@ -852,30 +963,30 @@ export class DefaultPackageManager {
852
963
  }
853
964
  return { type: "local", path: source };
854
965
  }
855
- /**
856
- * Check if an npm package needs to be updated.
857
- * - For unpinned packages: check if registry has a newer version
858
- * - For pinned packages: check if installed version matches the pinned version
859
- */
860
- async npmNeedsUpdate(source, installedPath) {
966
+ async installedNpmMatchesPinnedVersion(source, installedPath) {
967
+ const installedVersion = this.getInstalledNpmVersion(installedPath);
968
+ if (!installedVersion) {
969
+ return false;
970
+ }
971
+ const { version: pinnedVersion } = this.parseNpmSpec(source.spec);
972
+ if (!pinnedVersion) {
973
+ return true;
974
+ }
975
+ return installedVersion === pinnedVersion;
976
+ }
977
+ async npmHasAvailableUpdate(source, installedPath) {
861
978
  if (isOfflineModeEnabled()) {
862
979
  return false;
863
980
  }
864
981
  const installedVersion = this.getInstalledNpmVersion(installedPath);
865
- if (!installedVersion)
866
- return true;
867
- const { version: pinnedVersion } = this.parseNpmSpec(source.spec);
868
- if (pinnedVersion) {
869
- // Pinned: check if installed matches pinned (exact match for now)
870
- return installedVersion !== pinnedVersion;
982
+ if (!installedVersion) {
983
+ return false;
871
984
  }
872
- // Unpinned: check registry for latest version
873
985
  try {
874
986
  const latestVersion = await this.getLatestNpmVersion(source.name);
875
987
  return latestVersion !== installedVersion;
876
988
  }
877
989
  catch {
878
- // If we can't check registry, assume it's fine
879
990
  return false;
880
991
  }
881
992
  }
@@ -901,6 +1012,145 @@ export class DefaultPackageManager {
901
1012
  const data = (await response.json());
902
1013
  return data.version;
903
1014
  }
1015
+ async gitHasAvailableUpdate(installedPath) {
1016
+ if (isOfflineModeEnabled()) {
1017
+ return false;
1018
+ }
1019
+ try {
1020
+ const localHead = await this.runCommandCapture("git", ["rev-parse", "HEAD"], {
1021
+ cwd: installedPath,
1022
+ timeoutMs: NETWORK_TIMEOUT_MS,
1023
+ });
1024
+ const remoteHead = await this.getRemoteGitHead(installedPath);
1025
+ return localHead.trim() !== remoteHead.trim();
1026
+ }
1027
+ catch {
1028
+ return false;
1029
+ }
1030
+ }
1031
+ async getRemoteGitHead(installedPath) {
1032
+ const upstreamRef = await this.getGitUpstreamRef(installedPath);
1033
+ if (upstreamRef) {
1034
+ const remoteHead = await this.runGitRemoteCommand(installedPath, ["ls-remote", "origin", upstreamRef]);
1035
+ const match = remoteHead.match(/^([0-9a-f]{40})\s+/m);
1036
+ if (match?.[1]) {
1037
+ return match[1];
1038
+ }
1039
+ }
1040
+ const remoteHead = await this.runGitRemoteCommand(installedPath, ["ls-remote", "origin", "HEAD"]);
1041
+ const match = remoteHead.match(/^([0-9a-f]{40})\s+HEAD$/m);
1042
+ if (!match?.[1]) {
1043
+ throw new Error("Failed to determine remote HEAD");
1044
+ }
1045
+ return match[1];
1046
+ }
1047
+ async getLocalGitUpdateTarget(installedPath) {
1048
+ try {
1049
+ const upstream = await this.runCommandCapture("git", ["rev-parse", "--abbrev-ref", "@{upstream}"], {
1050
+ cwd: installedPath,
1051
+ timeoutMs: NETWORK_TIMEOUT_MS,
1052
+ });
1053
+ const trimmedUpstream = upstream.trim();
1054
+ if (!trimmedUpstream.startsWith("origin/")) {
1055
+ throw new Error(`Unsupported upstream remote: ${trimmedUpstream}`);
1056
+ }
1057
+ const branch = trimmedUpstream.slice("origin/".length);
1058
+ if (!branch) {
1059
+ throw new Error("Missing upstream branch name");
1060
+ }
1061
+ const head = await this.runCommandCapture("git", ["rev-parse", "@{upstream}"], {
1062
+ cwd: installedPath,
1063
+ timeoutMs: NETWORK_TIMEOUT_MS,
1064
+ });
1065
+ return {
1066
+ ref: "@{upstream}",
1067
+ head,
1068
+ fetchArgs: [
1069
+ "fetch",
1070
+ "--prune",
1071
+ "--no-tags",
1072
+ "origin",
1073
+ `+refs/heads/${branch}:refs/remotes/origin/${branch}`,
1074
+ ],
1075
+ };
1076
+ }
1077
+ catch {
1078
+ await this.runCommand("git", ["remote", "set-head", "origin", "-a"], { cwd: installedPath }).catch(() => { });
1079
+ const head = await this.runCommandCapture("git", ["rev-parse", "origin/HEAD"], {
1080
+ cwd: installedPath,
1081
+ timeoutMs: NETWORK_TIMEOUT_MS,
1082
+ });
1083
+ const originHeadRef = await this.runCommandCapture("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
1084
+ cwd: installedPath,
1085
+ timeoutMs: NETWORK_TIMEOUT_MS,
1086
+ }).catch(() => "");
1087
+ const branch = originHeadRef.trim().replace(/^refs\/remotes\/origin\//, "");
1088
+ if (branch) {
1089
+ return {
1090
+ ref: "origin/HEAD",
1091
+ head,
1092
+ fetchArgs: [
1093
+ "fetch",
1094
+ "--prune",
1095
+ "--no-tags",
1096
+ "origin",
1097
+ `+refs/heads/${branch}:refs/remotes/origin/${branch}`,
1098
+ ],
1099
+ };
1100
+ }
1101
+ return {
1102
+ ref: "origin/HEAD",
1103
+ head,
1104
+ fetchArgs: ["fetch", "--prune", "--no-tags", "origin", "+HEAD:refs/remotes/origin/HEAD"],
1105
+ };
1106
+ }
1107
+ }
1108
+ async getGitUpstreamRef(installedPath) {
1109
+ try {
1110
+ const upstream = await this.runCommandCapture("git", ["rev-parse", "--abbrev-ref", "@{upstream}"], {
1111
+ cwd: installedPath,
1112
+ timeoutMs: NETWORK_TIMEOUT_MS,
1113
+ });
1114
+ const trimmed = upstream.trim();
1115
+ if (!trimmed.startsWith("origin/")) {
1116
+ return undefined;
1117
+ }
1118
+ const branch = trimmed.slice("origin/".length);
1119
+ return branch ? `refs/heads/${branch}` : undefined;
1120
+ }
1121
+ catch {
1122
+ return undefined;
1123
+ }
1124
+ }
1125
+ runGitRemoteCommand(installedPath, args) {
1126
+ return this.runCommandCapture("git", args, {
1127
+ cwd: installedPath,
1128
+ timeoutMs: NETWORK_TIMEOUT_MS,
1129
+ env: {
1130
+ GIT_TERMINAL_PROMPT: "0",
1131
+ },
1132
+ });
1133
+ }
1134
+ async runWithConcurrency(tasks, limit) {
1135
+ if (tasks.length === 0) {
1136
+ return [];
1137
+ }
1138
+ const results = new Array(tasks.length);
1139
+ let nextIndex = 0;
1140
+ const workerCount = Math.max(1, Math.min(limit, tasks.length));
1141
+ const worker = async () => {
1142
+ while (true) {
1143
+ const index = nextIndex;
1144
+ nextIndex += 1;
1145
+ if (index >= tasks.length) {
1146
+ return;
1147
+ }
1148
+ results[index] = await tasks[index]();
1149
+ }
1150
+ };
1151
+ await Promise.all(Array.from({ length: workerCount }, () => worker()));
1152
+ return results;
1153
+ }
904
1154
  /**
905
1155
  * Get a unique identity for a package, ignoring version/ref.
906
1156
  * Used to detect when the same package is in both global and project settings.
@@ -953,25 +1203,44 @@ export class DefaultPackageManager {
953
1203
  const version = match[2];
954
1204
  return { name, version };
955
1205
  }
1206
+ getNpmCommand() {
1207
+ const configuredCommand = this.settingsManager.getNpmCommand();
1208
+ if (!configuredCommand || configuredCommand.length === 0) {
1209
+ return { command: "npm", args: [] };
1210
+ }
1211
+ const [command, ...args] = configuredCommand;
1212
+ if (!command) {
1213
+ throw new Error("Invalid npmCommand: first array entry must be a non-empty command");
1214
+ }
1215
+ return { command, args };
1216
+ }
1217
+ async runNpmCommand(args, options) {
1218
+ const npmCommand = this.getNpmCommand();
1219
+ await this.runCommand(npmCommand.command, [...npmCommand.args, ...args], options);
1220
+ }
1221
+ runNpmCommandSync(args) {
1222
+ const npmCommand = this.getNpmCommand();
1223
+ return this.runCommandSync(npmCommand.command, [...npmCommand.args, ...args]);
1224
+ }
956
1225
  async installNpm(source, scope, temporary) {
957
1226
  if (scope === "user" && !temporary) {
958
- await this.runCommand("npm", ["install", "-g", source.spec]);
1227
+ await this.runNpmCommand(["install", "-g", source.spec]);
959
1228
  return;
960
1229
  }
961
1230
  const installRoot = this.getNpmInstallRoot(scope, temporary);
962
1231
  this.ensureNpmProject(installRoot);
963
- await this.runCommand("npm", ["install", source.spec, "--prefix", installRoot]);
1232
+ await this.runNpmCommand(["install", source.spec, "--prefix", installRoot]);
964
1233
  }
965
1234
  async uninstallNpm(source, scope) {
966
1235
  if (scope === "user") {
967
- await this.runCommand("npm", ["uninstall", "-g", source.name]);
1236
+ await this.runNpmCommand(["uninstall", "-g", source.name]);
968
1237
  return;
969
1238
  }
970
1239
  const installRoot = this.getNpmInstallRoot(scope, false);
971
1240
  if (!existsSync(installRoot)) {
972
1241
  return;
973
1242
  }
974
- await this.runCommand("npm", ["uninstall", source.name, "--prefix", installRoot]);
1243
+ await this.runNpmCommand(["uninstall", source.name, "--prefix", installRoot]);
975
1244
  }
976
1245
  async installGit(source, scope) {
977
1246
  const targetDir = this.getGitInstallPath(source, scope);
@@ -989,7 +1258,7 @@ export class DefaultPackageManager {
989
1258
  }
990
1259
  const packageJsonPath = join(targetDir, "package.json");
991
1260
  if (existsSync(packageJsonPath)) {
992
- await this.runCommand("npm", ["install"], { cwd: targetDir });
1261
+ await this.runNpmCommand(["install"], { cwd: targetDir });
993
1262
  }
994
1263
  }
995
1264
  async updateGit(source, scope) {
@@ -998,21 +1267,26 @@ export class DefaultPackageManager {
998
1267
  await this.installGit(source, scope);
999
1268
  return;
1000
1269
  }
1001
- // Fetch latest from remote (handles force-push by getting new history)
1002
- await this.runCommand("git", ["fetch", "--prune", "origin"], { cwd: targetDir });
1003
- // Reset to tracking branch. Fall back to origin/HEAD when no upstream is configured.
1004
- try {
1005
- await this.runCommand("git", ["reset", "--hard", "@{upstream}"], { cwd: targetDir });
1006
- }
1007
- catch {
1008
- await this.runCommand("git", ["remote", "set-head", "origin", "-a"], { cwd: targetDir }).catch(() => { });
1009
- await this.runCommand("git", ["reset", "--hard", "origin/HEAD"], { cwd: targetDir });
1270
+ const target = await this.getLocalGitUpdateTarget(targetDir);
1271
+ // Fetch only the ref we will reset to, avoiding unrelated branch/tag noise.
1272
+ await this.runCommand("git", target.fetchArgs, { cwd: targetDir });
1273
+ const localHead = await this.runCommandCapture("git", ["rev-parse", "HEAD"], {
1274
+ cwd: targetDir,
1275
+ timeoutMs: NETWORK_TIMEOUT_MS,
1276
+ });
1277
+ const refreshedTargetHead = await this.runCommandCapture("git", ["rev-parse", target.ref], {
1278
+ cwd: targetDir,
1279
+ timeoutMs: NETWORK_TIMEOUT_MS,
1280
+ });
1281
+ if (localHead.trim() === refreshedTargetHead.trim()) {
1282
+ return;
1010
1283
  }
1284
+ await this.runCommand("git", ["reset", "--hard", target.ref], { cwd: targetDir });
1011
1285
  // Clean untracked files (extensions should be pristine)
1012
1286
  await this.runCommand("git", ["clean", "-fdx"], { cwd: targetDir });
1013
1287
  const packageJsonPath = join(targetDir, "package.json");
1014
1288
  if (existsSync(packageJsonPath)) {
1015
- await this.runCommand("npm", ["install"], { cwd: targetDir });
1289
+ await this.runNpmCommand(["install"], { cwd: targetDir });
1016
1290
  }
1017
1291
  }
1018
1292
  async refreshTemporaryGitSource(source, sourceStr) {
@@ -1088,11 +1362,14 @@ export class DefaultPackageManager {
1088
1362
  return join(this.getGlobalNpmRoot(), "..");
1089
1363
  }
1090
1364
  getGlobalNpmRoot() {
1091
- if (this.globalNpmRoot) {
1365
+ const npmCommand = this.getNpmCommand();
1366
+ const commandKey = [npmCommand.command, ...npmCommand.args].join("\0");
1367
+ if (this.globalNpmRoot && this.globalNpmRootCommandKey === commandKey) {
1092
1368
  return this.globalNpmRoot;
1093
1369
  }
1094
- const result = this.runCommandSync("npm", ["root", "-g"]);
1370
+ const result = this.runNpmCommandSync(["root", "-g"]);
1095
1371
  this.globalNpmRoot = result.trim();
1372
+ this.globalNpmRootCommandKey = commandKey;
1096
1373
  return this.globalNpmRoot;
1097
1374
  }
1098
1375
  getNpmInstallPath(source, scope) {
@@ -1141,21 +1418,21 @@ export class DefaultPackageManager {
1141
1418
  resolvePath(input) {
1142
1419
  const trimmed = input.trim();
1143
1420
  if (trimmed === "~")
1144
- return homedir();
1421
+ return getHomeDir();
1145
1422
  if (trimmed.startsWith("~/"))
1146
- return join(homedir(), trimmed.slice(2));
1423
+ return join(getHomeDir(), trimmed.slice(2));
1147
1424
  if (trimmed.startsWith("~"))
1148
- return join(homedir(), trimmed.slice(1));
1425
+ return join(getHomeDir(), trimmed.slice(1));
1149
1426
  return resolve(this.cwd, trimmed);
1150
1427
  }
1151
1428
  resolvePathFromBase(input, baseDir) {
1152
1429
  const trimmed = input.trim();
1153
1430
  if (trimmed === "~")
1154
- return homedir();
1431
+ return getHomeDir();
1155
1432
  if (trimmed.startsWith("~/"))
1156
- return join(homedir(), trimmed.slice(2));
1433
+ return join(getHomeDir(), trimmed.slice(2));
1157
1434
  if (trimmed.startsWith("~"))
1158
- return join(homedir(), trimmed.slice(1));
1435
+ return join(getHomeDir(), trimmed.slice(1));
1159
1436
  return resolve(baseDir, trimmed);
1160
1437
  }
1161
1438
  collectPackageResources(packageRoot, accumulator, filter, metadata) {
@@ -1329,7 +1606,7 @@ export class DefaultPackageManager {
1329
1606
  prompts: join(projectBaseDir, "prompts"),
1330
1607
  themes: join(projectBaseDir, "themes"),
1331
1608
  };
1332
- const userAgentsSkillsDir = join(homedir(), ".agents", "skills");
1609
+ const userAgentsSkillsDir = join(getHomeDir(), ".agents", "skills");
1333
1610
  const projectAgentsSkillDirs = collectAncestorAgentsSkillDirs(this.cwd).filter((dir) => resolve(dir) !== resolve(userAgentsSkillsDir));
1334
1611
  const addResources = (resourceType, paths, metadata, overrides, baseDir) => {
1335
1612
  const target = this.getTargetMap(accumulator, resourceType);
@@ -1414,11 +1691,54 @@ export class DefaultPackageManager {
1414
1691
  themes: toResolved(accumulator.themes),
1415
1692
  };
1416
1693
  }
1694
+ runCommandCapture(command, args, options) {
1695
+ return new Promise((resolvePromise, reject) => {
1696
+ const child = spawn(command, args, {
1697
+ cwd: options?.cwd,
1698
+ stdio: ["ignore", "pipe", "pipe"],
1699
+ shell: process.platform === "win32",
1700
+ env: options?.env ? { ...process.env, ...options.env } : process.env,
1701
+ });
1702
+ let stdout = "";
1703
+ let stderr = "";
1704
+ let timedOut = false;
1705
+ const timeout = typeof options?.timeoutMs === "number"
1706
+ ? setTimeout(() => {
1707
+ timedOut = true;
1708
+ child.kill();
1709
+ }, options.timeoutMs)
1710
+ : undefined;
1711
+ child.stdout?.on("data", (data) => {
1712
+ stdout += data.toString();
1713
+ });
1714
+ child.stderr?.on("data", (data) => {
1715
+ stderr += data.toString();
1716
+ });
1717
+ child.on("error", (error) => {
1718
+ if (timeout)
1719
+ clearTimeout(timeout);
1720
+ reject(error);
1721
+ });
1722
+ child.on("exit", (code) => {
1723
+ if (timeout)
1724
+ clearTimeout(timeout);
1725
+ if (timedOut) {
1726
+ reject(new Error(`${command} ${args.join(" ")} timed out after ${options?.timeoutMs}ms`));
1727
+ return;
1728
+ }
1729
+ if (code === 0) {
1730
+ resolvePromise(stdout.trim());
1731
+ return;
1732
+ }
1733
+ reject(new Error(`${command} ${args.join(" ")} failed with code ${code}: ${stderr || stdout}`));
1734
+ });
1735
+ });
1736
+ }
1417
1737
  runCommand(command, args, options) {
1418
1738
  return new Promise((resolvePromise, reject) => {
1419
1739
  const child = spawn(command, args, {
1420
1740
  cwd: options?.cwd,
1421
- stdio: "inherit",
1741
+ stdio: isStdoutTakenOver() ? ["ignore", 2, 2] : "inherit",
1422
1742
  shell: process.platform === "win32",
1423
1743
  });
1424
1744
  child.on("error", reject);