@duckmind/dm-darwin-x64 0.43.2 → 0.43.8

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 (256) hide show
  1. package/dm +0 -0
  2. package/extensions/.dm-extensions.json +242 -49
  3. package/extensions/dm-9router-ext/package.json +2 -19
  4. package/extensions/dm-9router-ext/src/index.js +5 -0
  5. package/extensions/dm-ask-user/index.js +10 -0
  6. package/extensions/dm-ask-user/package.json +1 -21
  7. package/extensions/dm-cli-anything/package.json +0 -6
  8. package/extensions/dm-context/package.json +2 -12
  9. package/extensions/dm-context/src/context.js +1 -0
  10. package/extensions/dm-context/src/index.js +7 -0
  11. package/extensions/dm-context/src/utils.js +1 -0
  12. package/extensions/dm-cua/package.json +0 -7
  13. package/extensions/dm-fff/package.json +1 -28
  14. package/extensions/dm-fff/src/index.js +15 -0
  15. package/extensions/dm-fff/src/query.js +1 -0
  16. package/extensions/dm-goal/package.json +2 -31
  17. package/extensions/dm-goal/src/goal.js +43 -0
  18. package/extensions/dm-grill-me/index.js +174 -0
  19. package/extensions/dm-grill-me/package.json +1 -26
  20. package/extensions/dm-image2/package.json +0 -7
  21. package/extensions/dm-subagents/package.json +1 -32
  22. package/extensions/dm-subagents/src/agents/agent-management.js +37 -0
  23. package/extensions/dm-subagents/src/agents/agent-memory.js +6 -0
  24. package/extensions/dm-subagents/src/agents/agent-scope.js +1 -0
  25. package/extensions/dm-subagents/src/agents/agent-selection.js +1 -0
  26. package/extensions/dm-subagents/src/agents/agent-serializer.js +7 -0
  27. package/extensions/dm-subagents/src/agents/agents.js +11 -0
  28. package/extensions/dm-subagents/src/agents/chain-serializer.js +12 -0
  29. package/extensions/dm-subagents/src/agents/frontmatter.js +6 -0
  30. package/extensions/dm-subagents/src/agents/identity.js +1 -0
  31. package/extensions/dm-subagents/src/agents/proactive-skills.js +1 -0
  32. package/extensions/dm-subagents/src/agents/skills.js +6 -0
  33. package/extensions/dm-subagents/src/extension/config.js +2 -0
  34. package/extensions/dm-subagents/src/extension/control-notices.js +4 -0
  35. package/extensions/dm-subagents/src/extension/doctor.js +16 -0
  36. package/extensions/dm-subagents/src/extension/fanout-child.js +231 -0
  37. package/extensions/dm-subagents/src/extension/index.js +367 -0
  38. package/extensions/dm-subagents/src/extension/rpc.js +8 -0
  39. package/extensions/dm-subagents/src/extension/schemas.js +1 -0
  40. package/extensions/dm-subagents/src/extension/{tool-description.ts → tool-description.js} +6 -126
  41. package/extensions/dm-subagents/src/intercom/intercom-bridge.js +13 -0
  42. package/extensions/dm-subagents/src/intercom/native-supervisor-channel.js +5 -0
  43. package/extensions/dm-subagents/src/intercom/result-intercom.js +3 -0
  44. package/extensions/dm-subagents/src/profiles/profiles.js +3 -0
  45. package/extensions/dm-subagents/src/runs/background/async-execution.js +46 -0
  46. package/extensions/dm-subagents/src/runs/background/async-job-tracker.js +14 -0
  47. package/extensions/dm-subagents/src/runs/background/async-resume.js +8 -0
  48. package/extensions/dm-subagents/src/runs/background/async-status.js +12 -0
  49. package/extensions/dm-subagents/src/runs/background/chain-append.js +2 -0
  50. package/extensions/dm-subagents/src/runs/background/chain-root-attachment.js +1 -0
  51. package/extensions/dm-subagents/src/runs/background/completion-batcher.js +1 -0
  52. package/extensions/dm-subagents/src/runs/background/completion-dedupe.js +1 -0
  53. package/extensions/dm-subagents/src/runs/background/control-channel.js +1 -0
  54. package/extensions/dm-subagents/src/runs/background/fleet-view.js +17 -0
  55. package/extensions/dm-subagents/src/runs/background/notify.js +3 -0
  56. package/extensions/dm-subagents/src/runs/background/parallel-groups.js +1 -0
  57. package/extensions/dm-subagents/src/runs/background/result-watcher.js +8 -0
  58. package/extensions/dm-subagents/src/runs/background/run-id-resolver.js +4 -0
  59. package/extensions/dm-subagents/src/runs/background/run-status.js +23 -0
  60. package/extensions/dm-subagents/src/runs/background/scheduled-runs.js +4 -0
  61. package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.js +11 -0
  62. package/extensions/dm-subagents/src/runs/background/subagent-runner.js +78 -0
  63. package/extensions/dm-subagents/src/runs/background/top-level-async.js +1 -0
  64. package/extensions/dm-subagents/src/runs/background/wait.js +11 -0
  65. package/extensions/dm-subagents/src/runs/foreground/chain-clarify.js +12 -0
  66. package/extensions/dm-subagents/src/runs/foreground/chain-execution.js +102 -0
  67. package/extensions/dm-subagents/src/runs/foreground/execution.js +51 -0
  68. package/extensions/dm-subagents/src/runs/foreground/subagent-executor.js +228 -0
  69. package/extensions/dm-subagents/src/runs/shared/acceptance.js +3 -0
  70. package/extensions/dm-subagents/src/runs/shared/chain-outputs.js +1 -0
  71. package/extensions/dm-subagents/src/runs/shared/completion-guard.js +3 -0
  72. package/extensions/dm-subagents/src/runs/shared/dm-args.js +1 -0
  73. package/extensions/dm-subagents/src/runs/shared/dm-spawn.js +1 -0
  74. package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.js +1 -0
  75. package/extensions/dm-subagents/src/runs/shared/long-running-guard.js +1 -0
  76. package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.js +1 -0
  77. package/extensions/dm-subagents/src/runs/shared/model-fallback.js +1 -0
  78. package/extensions/dm-subagents/src/runs/shared/model-scope.js +1 -0
  79. package/extensions/dm-subagents/src/runs/shared/nested-events.js +8 -0
  80. package/extensions/dm-subagents/src/runs/shared/nested-path.js +1 -0
  81. package/extensions/dm-subagents/src/runs/shared/nested-render.js +1 -0
  82. package/extensions/dm-subagents/src/runs/shared/parallel-utils.js +5 -0
  83. package/extensions/dm-subagents/src/runs/shared/run-history.js +5 -0
  84. package/extensions/dm-subagents/src/runs/shared/single-output.js +14 -0
  85. package/extensions/dm-subagents/src/runs/shared/structured-output.js +1 -0
  86. package/extensions/dm-subagents/src/runs/shared/subagent-control.js +5 -0
  87. package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.js +22 -0
  88. package/extensions/dm-subagents/src/runs/shared/tool-budget.js +1 -0
  89. package/extensions/dm-subagents/src/runs/shared/turn-budget.js +7 -0
  90. package/extensions/dm-subagents/src/runs/shared/workflow-graph.js +1 -0
  91. package/extensions/dm-subagents/src/runs/shared/worktree.js +3 -0
  92. package/extensions/dm-subagents/src/shared/artifacts.js +2 -0
  93. package/extensions/dm-subagents/src/shared/atomic-json.js +1 -0
  94. package/extensions/dm-subagents/src/shared/child-transcript.js +5 -0
  95. package/extensions/dm-subagents/src/shared/file-coalescer.js +1 -0
  96. package/extensions/dm-subagents/src/shared/fork-context.js +6 -0
  97. package/extensions/dm-subagents/src/shared/formatters.js +9 -0
  98. package/extensions/dm-subagents/src/shared/jsonl-writer.js +2 -0
  99. package/extensions/dm-subagents/src/shared/model-info.js +1 -0
  100. package/extensions/dm-subagents/src/shared/post-exit-stdio-guard.js +1 -0
  101. package/extensions/dm-subagents/src/shared/session-identity.js +1 -0
  102. package/extensions/dm-subagents/src/shared/session-tokens.js +2 -0
  103. package/extensions/dm-subagents/src/shared/settings.js +23 -0
  104. package/extensions/dm-subagents/src/shared/status-format.js +1 -0
  105. package/extensions/dm-subagents/src/shared/types.js +8 -0
  106. package/extensions/dm-subagents/src/shared/utils.js +2 -0
  107. package/extensions/dm-subagents/src/slash/prompt-template-bridge.js +1 -0
  108. package/extensions/dm-subagents/src/slash/prompt-workflows.js +7 -0
  109. package/extensions/dm-subagents/src/slash/slash-bridge.js +1 -0
  110. package/extensions/dm-subagents/src/slash/slash-commands.js +38 -0
  111. package/extensions/dm-subagents/src/slash/slash-live-state.js +6 -0
  112. package/extensions/dm-subagents/src/tui/render-helpers.js +1 -0
  113. package/extensions/dm-subagents/src/tui/render.js +4 -0
  114. package/extensions/dm-tasks/package.json +1 -30
  115. package/extensions/dm-tasks/src/auto-clear.js +1 -0
  116. package/extensions/dm-tasks/src/index.js +212 -0
  117. package/extensions/dm-tasks/src/process-tracker.js +1 -0
  118. package/extensions/dm-tasks/src/task-store.js +1 -0
  119. package/extensions/dm-tasks/src/tasks-config.js +1 -0
  120. package/extensions/dm-tasks/src/types.js +0 -0
  121. package/extensions/dm-tasks/src/ui/settings-menu.js +1 -0
  122. package/extensions/dm-tasks/src/ui/task-widget.js +1 -0
  123. package/extensions/dm-usage/index.js +9 -0
  124. package/extensions/dm-usage/package.json +1 -18
  125. package/extensions/greedysearch-dm/bin/search.mjs +135 -135
  126. package/extensions/greedysearch-dm/index.js +20 -0
  127. package/extensions/greedysearch-dm/package.json +1 -22
  128. package/extensions/greedysearch-dm/src/formatters/results.js +8 -0
  129. package/extensions/greedysearch-dm/src/formatters/sources.js +1 -0
  130. package/extensions/greedysearch-dm/src/formatters/synthesis.js +1 -0
  131. package/extensions/greedysearch-dm/src/search/engines.mjs +9 -9
  132. package/extensions/greedysearch-dm/src/search/paths.mjs +1 -0
  133. package/extensions/greedysearch-dm/src/search/research.mjs +100 -100
  134. package/extensions/greedysearch-dm/src/search/synthesis-runner.mjs +10 -10
  135. package/extensions/greedysearch-dm/src/tools/greedy-search-handler.js +20 -0
  136. package/extensions/greedysearch-dm/src/tools/shared.js +9 -0
  137. package/extensions/greedysearch-dm/src/types.js +0 -0
  138. package/extensions/greedysearch-dm/src/utils/helpers.js +1 -0
  139. package/package.json +1 -1
  140. package/extensions/dm-9router-ext/src/index.ts +0 -541
  141. package/extensions/dm-ask-user/index.ts +0 -857
  142. package/extensions/dm-context/src/context.ts +0 -158
  143. package/extensions/dm-context/src/index.ts +0 -439
  144. package/extensions/dm-context/src/utils.ts +0 -6
  145. package/extensions/dm-fff/src/index.ts +0 -1023
  146. package/extensions/dm-fff/src/query.ts +0 -87
  147. package/extensions/dm-goal/src/goal.ts +0 -1073
  148. package/extensions/dm-grill-me/index.ts +0 -1085
  149. package/extensions/dm-subagents/src/agents/agent-management.ts +0 -1052
  150. package/extensions/dm-subagents/src/agents/agent-memory.ts +0 -254
  151. package/extensions/dm-subagents/src/agents/agent-scope.ts +0 -6
  152. package/extensions/dm-subagents/src/agents/agent-selection.ts +0 -25
  153. package/extensions/dm-subagents/src/agents/agent-serializer.ts +0 -121
  154. package/extensions/dm-subagents/src/agents/agents.ts +0 -1553
  155. package/extensions/dm-subagents/src/agents/chain-serializer.ts +0 -277
  156. package/extensions/dm-subagents/src/agents/frontmatter.ts +0 -93
  157. package/extensions/dm-subagents/src/agents/identity.ts +0 -30
  158. package/extensions/dm-subagents/src/agents/proactive-skills.ts +0 -191
  159. package/extensions/dm-subagents/src/agents/skills.ts +0 -729
  160. package/extensions/dm-subagents/src/extension/config.ts +0 -39
  161. package/extensions/dm-subagents/src/extension/control-notices.ts +0 -92
  162. package/extensions/dm-subagents/src/extension/doctor.ts +0 -214
  163. package/extensions/dm-subagents/src/extension/fanout-child.ts +0 -172
  164. package/extensions/dm-subagents/src/extension/index.ts +0 -656
  165. package/extensions/dm-subagents/src/extension/rpc.ts +0 -369
  166. package/extensions/dm-subagents/src/extension/schemas.ts +0 -309
  167. package/extensions/dm-subagents/src/intercom/intercom-bridge.ts +0 -180
  168. package/extensions/dm-subagents/src/intercom/native-supervisor-channel.ts +0 -519
  169. package/extensions/dm-subagents/src/intercom/result-intercom.ts +0 -377
  170. package/extensions/dm-subagents/src/profiles/profiles.ts +0 -637
  171. package/extensions/dm-subagents/src/runs/background/async-execution.ts +0 -1065
  172. package/extensions/dm-subagents/src/runs/background/async-job-tracker.ts +0 -441
  173. package/extensions/dm-subagents/src/runs/background/async-resume.ts +0 -391
  174. package/extensions/dm-subagents/src/runs/background/async-status.ts +0 -395
  175. package/extensions/dm-subagents/src/runs/background/chain-append.ts +0 -282
  176. package/extensions/dm-subagents/src/runs/background/chain-root-attachment.ts +0 -191
  177. package/extensions/dm-subagents/src/runs/background/completion-batcher.ts +0 -166
  178. package/extensions/dm-subagents/src/runs/background/completion-dedupe.ts +0 -63
  179. package/extensions/dm-subagents/src/runs/background/control-channel.ts +0 -332
  180. package/extensions/dm-subagents/src/runs/background/fleet-view.ts +0 -515
  181. package/extensions/dm-subagents/src/runs/background/notify.ts +0 -225
  182. package/extensions/dm-subagents/src/runs/background/parallel-groups.ts +0 -45
  183. package/extensions/dm-subagents/src/runs/background/result-watcher.ts +0 -315
  184. package/extensions/dm-subagents/src/runs/background/run-id-resolver.ts +0 -84
  185. package/extensions/dm-subagents/src/runs/background/run-status.ts +0 -434
  186. package/extensions/dm-subagents/src/runs/background/scheduled-runs.ts +0 -514
  187. package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.ts +0 -368
  188. package/extensions/dm-subagents/src/runs/background/subagent-runner.ts +0 -3171
  189. package/extensions/dm-subagents/src/runs/background/top-level-async.ts +0 -13
  190. package/extensions/dm-subagents/src/runs/background/wait.ts +0 -353
  191. package/extensions/dm-subagents/src/runs/foreground/chain-clarify.ts +0 -1333
  192. package/extensions/dm-subagents/src/runs/foreground/chain-execution.ts +0 -1313
  193. package/extensions/dm-subagents/src/runs/foreground/execution.ts +0 -1239
  194. package/extensions/dm-subagents/src/runs/foreground/subagent-executor.ts +0 -3613
  195. package/extensions/dm-subagents/src/runs/shared/acceptance.ts +0 -879
  196. package/extensions/dm-subagents/src/runs/shared/chain-outputs.ts +0 -116
  197. package/extensions/dm-subagents/src/runs/shared/completion-guard.ts +0 -147
  198. package/extensions/dm-subagents/src/runs/shared/dm-args.ts +0 -271
  199. package/extensions/dm-subagents/src/runs/shared/dm-spawn.ts +0 -147
  200. package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.ts +0 -295
  201. package/extensions/dm-subagents/src/runs/shared/long-running-guard.ts +0 -175
  202. package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +0 -365
  203. package/extensions/dm-subagents/src/runs/shared/model-fallback.ts +0 -292
  204. package/extensions/dm-subagents/src/runs/shared/model-scope.ts +0 -128
  205. package/extensions/dm-subagents/src/runs/shared/nested-events.ts +0 -908
  206. package/extensions/dm-subagents/src/runs/shared/nested-path.ts +0 -52
  207. package/extensions/dm-subagents/src/runs/shared/nested-render.ts +0 -115
  208. package/extensions/dm-subagents/src/runs/shared/parallel-utils.ts +0 -198
  209. package/extensions/dm-subagents/src/runs/shared/run-history.ts +0 -60
  210. package/extensions/dm-subagents/src/runs/shared/single-output.ts +0 -180
  211. package/extensions/dm-subagents/src/runs/shared/structured-output.ts +0 -77
  212. package/extensions/dm-subagents/src/runs/shared/subagent-control.ts +0 -223
  213. package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.ts +0 -342
  214. package/extensions/dm-subagents/src/runs/shared/tool-budget.ts +0 -74
  215. package/extensions/dm-subagents/src/runs/shared/turn-budget.ts +0 -52
  216. package/extensions/dm-subagents/src/runs/shared/workflow-graph.ts +0 -206
  217. package/extensions/dm-subagents/src/runs/shared/worktree.ts +0 -600
  218. package/extensions/dm-subagents/src/shared/artifacts.ts +0 -113
  219. package/extensions/dm-subagents/src/shared/atomic-json.ts +0 -86
  220. package/extensions/dm-subagents/src/shared/child-transcript.ts +0 -212
  221. package/extensions/dm-subagents/src/shared/file-coalescer.ts +0 -40
  222. package/extensions/dm-subagents/src/shared/fork-context.ts +0 -194
  223. package/extensions/dm-subagents/src/shared/formatters.ts +0 -133
  224. package/extensions/dm-subagents/src/shared/jsonl-writer.ts +0 -81
  225. package/extensions/dm-subagents/src/shared/model-info.ts +0 -78
  226. package/extensions/dm-subagents/src/shared/post-exit-stdio-guard.ts +0 -85
  227. package/extensions/dm-subagents/src/shared/session-identity.ts +0 -10
  228. package/extensions/dm-subagents/src/shared/session-tokens.ts +0 -44
  229. package/extensions/dm-subagents/src/shared/settings.ts +0 -450
  230. package/extensions/dm-subagents/src/shared/status-format.ts +0 -49
  231. package/extensions/dm-subagents/src/shared/types.ts +0 -1257
  232. package/extensions/dm-subagents/src/shared/utils.ts +0 -554
  233. package/extensions/dm-subagents/src/slash/prompt-template-bridge.ts +0 -420
  234. package/extensions/dm-subagents/src/slash/prompt-workflows.ts +0 -330
  235. package/extensions/dm-subagents/src/slash/slash-bridge.ts +0 -176
  236. package/extensions/dm-subagents/src/slash/slash-commands.ts +0 -1296
  237. package/extensions/dm-subagents/src/slash/slash-live-state.ts +0 -292
  238. package/extensions/dm-subagents/src/tui/render-helpers.ts +0 -80
  239. package/extensions/dm-subagents/src/tui/render.ts +0 -1748
  240. package/extensions/dm-tasks/src/auto-clear.ts +0 -91
  241. package/extensions/dm-tasks/src/index.ts +0 -1145
  242. package/extensions/dm-tasks/src/process-tracker.ts +0 -140
  243. package/extensions/dm-tasks/src/task-store.ts +0 -305
  244. package/extensions/dm-tasks/src/tasks-config.ts +0 -23
  245. package/extensions/dm-tasks/src/types.ts +0 -40
  246. package/extensions/dm-tasks/src/ui/settings-menu.ts +0 -100
  247. package/extensions/dm-tasks/src/ui/task-widget.ts +0 -265
  248. package/extensions/dm-usage/index.ts +0 -1718
  249. package/extensions/greedysearch-dm/index.ts +0 -177
  250. package/extensions/greedysearch-dm/src/formatters/results.ts +0 -163
  251. package/extensions/greedysearch-dm/src/formatters/sources.ts +0 -116
  252. package/extensions/greedysearch-dm/src/formatters/synthesis.ts +0 -87
  253. package/extensions/greedysearch-dm/src/tools/greedy-search-handler.ts +0 -370
  254. package/extensions/greedysearch-dm/src/tools/shared.ts +0 -187
  255. package/extensions/greedysearch-dm/src/types.ts +0 -110
  256. package/extensions/greedysearch-dm/src/utils/helpers.ts +0 -40
@@ -1,600 +0,0 @@
1
- import { spawnSync } from "node:child_process";
2
- import * as fs from "node:fs";
3
- import * as os from "node:os";
4
- import * as path from "node:path";
5
-
6
- export interface WorktreeSetup {
7
- cwd: string;
8
- worktrees: WorktreeInfo[];
9
- baseCommit: string;
10
- }
11
-
12
- interface WorktreeInfo {
13
- path: string;
14
- agentCwd: string;
15
- branch: string;
16
- index: number;
17
- nodeModulesLinked: boolean;
18
- syntheticPaths: string[];
19
- }
20
-
21
- interface WorktreeDiff {
22
- index: number;
23
- agent: string;
24
- branch: string;
25
- diffStat: string;
26
- filesChanged: number;
27
- insertions: number;
28
- deletions: number;
29
- patchPath: string;
30
- }
31
-
32
- interface WorktreeTaskCwdConflict {
33
- index: number;
34
- agent: string;
35
- cwd: string;
36
- }
37
-
38
- interface WorktreeSetupHookConfig {
39
- hookPath: string;
40
- timeoutMs?: number;
41
- }
42
-
43
- interface CreateWorktreesOptions {
44
- agents?: string[];
45
- setupHook?: WorktreeSetupHookConfig;
46
- baseDir?: string;
47
- }
48
-
49
- interface ResolvedWorktreeSetupHook {
50
- hookPath: string;
51
- timeoutMs: number;
52
- }
53
-
54
- interface WorktreeSetupHookInput {
55
- version: 1;
56
- repoRoot: string;
57
- worktreePath: string;
58
- agentCwd: string;
59
- branch: string;
60
- index: number;
61
- runId: string;
62
- baseCommit: string;
63
- agent?: string;
64
- }
65
-
66
- interface WorktreeSetupHookOutput {
67
- syntheticPaths?: string[];
68
- }
69
-
70
- interface GitResult {
71
- stdout: string;
72
- stderr: string;
73
- status: number | null;
74
- }
75
-
76
- interface RepoState {
77
- toplevel: string;
78
- cwdRelative: string;
79
- baseCommit: string;
80
- }
81
-
82
- const DEFAULT_WORKTREE_SETUP_HOOK_TIMEOUT_MS = 30000;
83
-
84
- function runGit(cwd: string, args: string[]): GitResult {
85
- const result = spawnSync("git", ["-C", cwd, ...args], { encoding: "utf-8" });
86
- return {
87
- stdout: result.stdout ?? "",
88
- stderr: result.stderr ?? "",
89
- status: result.status,
90
- };
91
- }
92
-
93
- function runGitChecked(cwd: string, args: string[]): string {
94
- const result = runGit(cwd, args);
95
- if (result.status !== 0) {
96
- const command = `git -C ${cwd} ${args.join(" ")}`;
97
- const message = result.stderr.trim() || result.stdout.trim() || `${command} failed`;
98
- throw new Error(message);
99
- }
100
- return result.stdout;
101
- }
102
-
103
- function resolveRepoState(cwd: string): RepoState {
104
- const cwdRelative = resolveRepoCwdRelative(cwd);
105
- const toplevel = runGitChecked(cwd, ["rev-parse", "--show-toplevel"]).trim();
106
-
107
- const status = runGitChecked(toplevel, ["status", "--porcelain"]);
108
- if (status.trim().length > 0) {
109
- throw new Error("worktree isolation requires a clean git working tree. Commit or stash changes first.");
110
- }
111
-
112
- const baseCommit = runGitChecked(toplevel, ["rev-parse", "HEAD"]).trim();
113
- return { toplevel, cwdRelative, baseCommit };
114
- }
115
-
116
- function normalizeComparableCwd(cwd: string): string {
117
- const resolved = path.resolve(cwd);
118
- try {
119
- return fs.realpathSync(resolved);
120
- } catch {
121
- // Use the unresolved absolute path when realpath resolution is unavailable.
122
- return resolved;
123
- }
124
- }
125
-
126
- export function findWorktreeTaskCwdConflict(
127
- tasks: ReadonlyArray<{ agent: string; cwd?: string }>,
128
- sharedCwd: string,
129
- ): WorktreeTaskCwdConflict | undefined {
130
- const normalizedSharedCwd = normalizeComparableCwd(sharedCwd);
131
- for (let index = 0; index < tasks.length; index++) {
132
- const task = tasks[index]!;
133
- if (!task.cwd) continue;
134
- const taskCwd = path.isAbsolute(task.cwd) ? task.cwd : path.resolve(sharedCwd, task.cwd);
135
- if (normalizeComparableCwd(taskCwd) === normalizedSharedCwd) continue;
136
- return { index, agent: task.agent, cwd: task.cwd };
137
- }
138
- return undefined;
139
- }
140
-
141
- export function formatWorktreeTaskCwdConflict(
142
- conflict: WorktreeTaskCwdConflict,
143
- sharedCwd: string,
144
- ): string {
145
- return `worktree isolation uses the shared cwd (${sharedCwd}); task ${conflict.index + 1} (${conflict.agent}) sets cwd to ${conflict.cwd}. Remove task-level cwd overrides or disable worktree.`;
146
- }
147
-
148
- function safePatchAgentName(agent: string): string {
149
- return agent.replace(/[^\w.-]/g, "_");
150
- }
151
-
152
- function buildWorktreeBranch(runId: string, index: number): string {
153
- return `dm-parallel-${runId}-${index}`;
154
- }
155
-
156
- function resolveWorktreeBaseDir(configuredBaseDir: string | undefined, repoRoot: string): string {
157
- const rawBaseDir = configuredBaseDir ?? process.env.DM_SUBAGENTS_WORKTREE_DIR;
158
- if (rawBaseDir === undefined) return os.tmpdir();
159
-
160
- const trimmed = rawBaseDir.trim();
161
- if (!trimmed) throw new Error("worktree base directory cannot be empty");
162
-
163
- const expanded = trimmed.startsWith("~/") ? path.join(os.homedir(), trimmed.slice(2)) : trimmed;
164
- const resolved = path.isAbsolute(expanded) ? expanded : path.resolve(repoRoot, expanded);
165
- try {
166
- fs.mkdirSync(resolved, { recursive: true });
167
- } catch (error) {
168
- const message = error instanceof Error ? error.message : String(error);
169
- throw new Error(`failed to create worktree base directory ${resolved}: ${message}`);
170
- }
171
- return resolved;
172
- }
173
-
174
- function buildWorktreePath(baseDir: string, runId: string, index: number): string {
175
- return path.join(baseDir, `dm-worktree-${runId}-${index}`);
176
- }
177
-
178
- function resolveRepoCwdRelative(cwd: string): string {
179
- const repoCheck = runGit(cwd, ["rev-parse", "--is-inside-work-tree"]);
180
- if (repoCheck.status !== 0 || repoCheck.stdout.trim() !== "true") {
181
- throw new Error("worktree isolation requires a git repository");
182
- }
183
- const rawPrefix = runGitChecked(cwd, ["rev-parse", "--show-prefix"]).trim();
184
- const normalizedPrefix = rawPrefix
185
- ? path.normalize(rawPrefix.replace(/[\\/]+$/, ""))
186
- : "";
187
- return normalizedPrefix === "." ? "" : normalizedPrefix;
188
- }
189
-
190
- export function resolveExpectedWorktreeAgentCwd(cwd: string, runId: string, index: number, baseDir?: string): string {
191
- const cwdRelative = resolveRepoCwdRelative(cwd);
192
- const repoRoot = runGitChecked(cwd, ["rev-parse", "--show-toplevel"]).trim();
193
- const worktreePath = buildWorktreePath(resolveWorktreeBaseDir(baseDir, repoRoot), runId, index);
194
- return cwdRelative ? path.join(worktreePath, cwdRelative) : worktreePath;
195
- }
196
-
197
- function linkNodeModulesIfPresent(toplevel: string, worktreePath: string): boolean {
198
- const nodeModulesPath = path.join(toplevel, "node_modules");
199
- const nodeModulesLinkPath = path.join(worktreePath, "node_modules");
200
- if (!fs.existsSync(nodeModulesPath) || fs.existsSync(nodeModulesLinkPath)) return false;
201
- try {
202
- fs.symlinkSync(nodeModulesPath, nodeModulesLinkPath);
203
- return true;
204
- } catch {
205
- // Symlink creation is optional (e.g., unsupported filesystems on CI runners).
206
- return false;
207
- }
208
- }
209
-
210
- function parseHookTimeout(timeoutMs: number | undefined): number {
211
- if (timeoutMs === undefined) return DEFAULT_WORKTREE_SETUP_HOOK_TIMEOUT_MS;
212
- if (!Number.isInteger(timeoutMs) || timeoutMs <= 0) {
213
- throw new Error("worktree setup hook timeout must be an integer greater than 0");
214
- }
215
- return timeoutMs;
216
- }
217
-
218
- function resolveWorktreeSetupHook(
219
- repoRoot: string,
220
- config: WorktreeSetupHookConfig | undefined,
221
- ): ResolvedWorktreeSetupHook | undefined {
222
- if (!config) return undefined;
223
- const hookPath = config.hookPath.trim();
224
- if (!hookPath) {
225
- throw new Error("worktree setup hook path cannot be empty");
226
- }
227
-
228
- const expandedHookPath = hookPath.startsWith("~/") ? path.join(os.homedir(), hookPath.slice(2)) : hookPath;
229
- let resolvedPath: string;
230
- if (path.isAbsolute(expandedHookPath)) {
231
- resolvedPath = expandedHookPath;
232
- } else if (expandedHookPath.includes("/") || expandedHookPath.includes("\\")) {
233
- resolvedPath = path.resolve(repoRoot, expandedHookPath);
234
- } else {
235
- throw new Error("worktree setup hook must be an absolute path or a repo-relative path");
236
- }
237
-
238
- if (!fs.existsSync(resolvedPath)) {
239
- throw new Error(`worktree setup hook not found: ${resolvedPath}`);
240
- }
241
- if (fs.statSync(resolvedPath).isDirectory()) {
242
- throw new Error(`worktree setup hook must be a file, got directory: ${resolvedPath}`);
243
- }
244
-
245
- return {
246
- hookPath: resolvedPath,
247
- timeoutMs: parseHookTimeout(config.timeoutMs),
248
- };
249
- }
250
-
251
- function normalizeSyntheticPath(worktreePath: string, rawPath: string): string {
252
- const trimmed = rawPath.trim();
253
- if (!trimmed) throw new Error("synthetic path cannot be empty");
254
- if (path.isAbsolute(trimmed)) throw new Error(`synthetic path must be relative: ${rawPath}`);
255
-
256
- const resolved = path.resolve(worktreePath, trimmed);
257
- const relative = path.relative(worktreePath, resolved);
258
- if (!relative || relative === ".") {
259
- throw new Error(`synthetic path cannot target the worktree root: ${rawPath}`);
260
- }
261
- if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
262
- throw new Error(`synthetic path escapes the worktree root: ${rawPath}`);
263
- }
264
- return path.normalize(relative);
265
- }
266
-
267
- function hasTrackedEntries(worktreePath: string, relativePath: string): boolean {
268
- const result = runGit(worktreePath, ["ls-files", "--", relativePath]);
269
- return result.status === 0 && result.stdout.trim().length > 0;
270
- }
271
-
272
- function parseWorktreeSetupHookOutput(rawStdout: string): WorktreeSetupHookOutput {
273
- const trimmed = rawStdout.trim();
274
- if (!trimmed) {
275
- throw new Error("worktree setup hook returned empty stdout; expected JSON object");
276
- }
277
- let parsed: unknown;
278
- try {
279
- parsed = JSON.parse(trimmed);
280
- } catch (error) {
281
- const message = error instanceof Error ? error.message : String(error);
282
- throw new Error(`worktree setup hook returned invalid JSON: ${message}`);
283
- }
284
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
285
- throw new Error("worktree setup hook stdout must be a JSON object");
286
- }
287
- return parsed as WorktreeSetupHookOutput;
288
- }
289
-
290
- function runWorktreeSetupHook(
291
- hook: ResolvedWorktreeSetupHook,
292
- input: WorktreeSetupHookInput,
293
- ): string[] {
294
- const result = spawnSync(hook.hookPath, [], {
295
- cwd: input.worktreePath,
296
- encoding: "utf-8",
297
- input: JSON.stringify(input),
298
- timeout: hook.timeoutMs,
299
- shell: false,
300
- });
301
-
302
- if (result.error) {
303
- const code = "code" in result.error ? result.error.code : undefined;
304
- if (code === "ETIMEDOUT") {
305
- throw new Error(`worktree setup hook timed out after ${hook.timeoutMs}ms`);
306
- }
307
- throw new Error(`worktree setup hook failed: ${result.error.message}`);
308
- }
309
-
310
- if (result.status !== 0) {
311
- const details = result.stderr.trim() || result.stdout.trim() || "no output";
312
- throw new Error(`worktree setup hook failed with exit code ${result.status}: ${details}`);
313
- }
314
-
315
- const output = parseWorktreeSetupHookOutput(result.stdout);
316
- if (output.syntheticPaths === undefined) return [];
317
- if (!Array.isArray(output.syntheticPaths)) {
318
- throw new Error("worktree setup hook output field 'syntheticPaths' must be an array of relative paths");
319
- }
320
-
321
- const uniquePaths = new Set<string>();
322
- for (const candidate of output.syntheticPaths) {
323
- if (typeof candidate !== "string") {
324
- throw new Error("worktree setup hook output field 'syntheticPaths' must contain only strings");
325
- }
326
- const normalizedPath = normalizeSyntheticPath(input.worktreePath, candidate);
327
- if (hasTrackedEntries(input.worktreePath, normalizedPath)) {
328
- throw new Error(`worktree setup hook cannot mark tracked paths as synthetic: ${normalizedPath}`);
329
- }
330
- uniquePaths.add(normalizedPath);
331
- }
332
- return [...uniquePaths];
333
- }
334
-
335
- function createSingleWorktree(
336
- toplevel: string,
337
- cwdRelative: string,
338
- runId: string,
339
- index: number,
340
- baseCommit: string,
341
- setupHook: ResolvedWorktreeSetupHook | undefined,
342
- agent: string | undefined,
343
- baseDir: string,
344
- ): WorktreeInfo {
345
- const branch = buildWorktreeBranch(runId, index);
346
- const worktreePath = buildWorktreePath(baseDir, runId, index);
347
- const add = runGit(toplevel, ["worktree", "add", worktreePath, "-b", branch, "HEAD"]);
348
- if (add.status !== 0) {
349
- const message = add.stderr.trim() || add.stdout.trim() || `failed to create worktree ${worktreePath}`;
350
- throw new Error(message);
351
- }
352
-
353
- const agentCwd = cwdRelative ? path.join(worktreePath, cwdRelative) : worktreePath;
354
- try {
355
- const nodeModulesLinked = linkNodeModulesIfPresent(toplevel, worktreePath);
356
- const syntheticPaths = nodeModulesLinked ? ["node_modules"] : [];
357
-
358
- if (setupHook) {
359
- const hookSyntheticPaths = runWorktreeSetupHook(setupHook, {
360
- version: 1,
361
- repoRoot: toplevel,
362
- worktreePath,
363
- agentCwd,
364
- branch,
365
- index,
366
- runId,
367
- baseCommit,
368
- agent,
369
- });
370
- syntheticPaths.push(...hookSyntheticPaths);
371
- }
372
-
373
- return {
374
- path: worktreePath,
375
- agentCwd,
376
- branch,
377
- index,
378
- nodeModulesLinked,
379
- syntheticPaths,
380
- };
381
- } catch (error) {
382
- try { runGitChecked(toplevel, ["worktree", "remove", "--force", worktreePath]); } catch {
383
- // Best-effort rollback; preserve the original setup failure.
384
- }
385
- try { runGitChecked(toplevel, ["branch", "-D", branch]); } catch {
386
- // Best-effort rollback; preserve the original setup failure.
387
- }
388
- throw error;
389
- }
390
- }
391
-
392
- function removeSyntheticPath(worktree: WorktreeInfo, syntheticPath: string): void {
393
- const resolved = path.resolve(worktree.path, syntheticPath);
394
- const relative = path.relative(worktree.path, resolved);
395
- if (!relative || relative === "." || relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
396
- return;
397
- }
398
-
399
- let stat: fs.Stats;
400
- try {
401
- stat = fs.lstatSync(resolved);
402
- } catch (error) {
403
- const code = error && typeof error === "object" && "code" in error ? (error as { code?: unknown }).code : undefined;
404
- if (code === "ENOENT") return;
405
- throw error;
406
- }
407
-
408
- if (stat.isSymbolicLink()) {
409
- fs.unlinkSync(resolved);
410
- return;
411
- }
412
- if (stat.isDirectory()) {
413
- fs.rmSync(resolved, { recursive: true, force: true });
414
- return;
415
- }
416
- fs.rmSync(resolved, { force: true });
417
- }
418
-
419
- function removeSyntheticPathsBeforeDiff(worktree: WorktreeInfo): void {
420
- if (worktree.syntheticPaths.length === 0) return;
421
- const seen = new Set<string>();
422
- for (const syntheticPath of worktree.syntheticPaths) {
423
- if (seen.has(syntheticPath)) continue;
424
- seen.add(syntheticPath);
425
- removeSyntheticPath(worktree, syntheticPath);
426
- }
427
- }
428
-
429
- function emptyDiff(index: number, agent: string, branch: string, patchPath: string): WorktreeDiff {
430
- return {
431
- index,
432
- agent,
433
- branch,
434
- diffStat: "",
435
- filesChanged: 0,
436
- insertions: 0,
437
- deletions: 0,
438
- patchPath,
439
- };
440
- }
441
-
442
- function parseNumstat(numstat: string): { filesChanged: number; insertions: number; deletions: number } {
443
- const lines = numstat
444
- .split("\n")
445
- .map((line) => line.trim())
446
- .filter(Boolean);
447
- let filesChanged = 0;
448
- let insertions = 0;
449
- let deletions = 0;
450
-
451
- for (const line of lines) {
452
- const [rawInsertions, rawDeletions] = line.split("\t");
453
- if (rawInsertions === undefined || rawDeletions === undefined) continue;
454
- filesChanged++;
455
- if (/^\d+$/.test(rawInsertions)) insertions += parseInt(rawInsertions, 10);
456
- if (/^\d+$/.test(rawDeletions)) deletions += parseInt(rawDeletions, 10);
457
- }
458
-
459
- return { filesChanged, insertions, deletions };
460
- }
461
-
462
- function captureWorktreeDiff(
463
- setup: WorktreeSetup,
464
- worktree: WorktreeInfo,
465
- agent: string,
466
- patchPath: string,
467
- ): WorktreeDiff {
468
- removeSyntheticPathsBeforeDiff(worktree);
469
- runGitChecked(worktree.path, ["add", "-A"]);
470
- const diffStat = runGitChecked(worktree.path, ["diff", "--cached", "--stat", setup.baseCommit]).trim();
471
- const patch = runGitChecked(worktree.path, ["diff", "--cached", setup.baseCommit]);
472
- const numstat = runGitChecked(worktree.path, ["diff", "--cached", "--numstat", setup.baseCommit]);
473
- fs.writeFileSync(patchPath, patch, "utf-8");
474
-
475
- if (!patch.trim()) {
476
- return emptyDiff(worktree.index, agent, worktree.branch, patchPath);
477
- }
478
-
479
- const parsed = parseNumstat(numstat);
480
- return {
481
- index: worktree.index,
482
- agent,
483
- branch: worktree.branch,
484
- diffStat,
485
- filesChanged: parsed.filesChanged,
486
- insertions: parsed.insertions,
487
- deletions: parsed.deletions,
488
- patchPath,
489
- };
490
- }
491
-
492
- function writeEmptyPatch(patchPath: string): void {
493
- try {
494
- fs.writeFileSync(patchPath, "", "utf-8");
495
- } catch {
496
- // Diff artifact writing is best-effort in error paths.
497
- }
498
- }
499
-
500
- function cleanupSingleWorktree(repoCwd: string, worktree: WorktreeInfo): void {
501
- try { runGitChecked(repoCwd, ["worktree", "remove", "--force", worktree.path]); } catch {
502
- // Cleanup is best-effort to avoid masking caller errors.
503
- }
504
- try { runGitChecked(repoCwd, ["branch", "-D", worktree.branch]); } catch {
505
- // Cleanup is best-effort to avoid masking caller errors.
506
- }
507
- }
508
-
509
- function hasWorktreeChanges(diff: WorktreeDiff): boolean {
510
- return diff.filesChanged > 0 || diff.insertions > 0 || diff.deletions > 0 || diff.diffStat.trim().length > 0;
511
- }
512
-
513
- export function createWorktrees(cwd: string, runId: string, count: number, options?: CreateWorktreesOptions): WorktreeSetup {
514
- const repo = resolveRepoState(cwd);
515
- const setupHook = resolveWorktreeSetupHook(repo.toplevel, options?.setupHook);
516
- const baseDir = resolveWorktreeBaseDir(options?.baseDir, repo.toplevel);
517
- const worktrees: WorktreeInfo[] = [];
518
-
519
- try {
520
- for (let index = 0; index < count; index++) {
521
- worktrees.push(createSingleWorktree(
522
- repo.toplevel,
523
- repo.cwdRelative,
524
- runId,
525
- index,
526
- repo.baseCommit,
527
- setupHook,
528
- options?.agents?.[index],
529
- baseDir,
530
- ));
531
- }
532
- } catch (error) {
533
- cleanupWorktrees({
534
- cwd: repo.toplevel,
535
- worktrees,
536
- baseCommit: repo.baseCommit,
537
- });
538
- throw error;
539
- }
540
-
541
- return {
542
- cwd: repo.toplevel,
543
- worktrees,
544
- baseCommit: repo.baseCommit,
545
- };
546
- }
547
-
548
- export function diffWorktrees(setup: WorktreeSetup, agents: string[], diffsDir: string): WorktreeDiff[] {
549
- try {
550
- fs.mkdirSync(diffsDir, { recursive: true });
551
- } catch {
552
- // Returning no diffs is safer than failing the whole command on artifact-dir issues.
553
- return [];
554
- }
555
-
556
- const diffs: WorktreeDiff[] = [];
557
- for (let index = 0; index < setup.worktrees.length; index++) {
558
- const worktree = setup.worktrees[index]!;
559
- const agent = agents[index] ?? `task-${index + 1}`;
560
- const patchPath = path.join(diffsDir, `task-${index}-${safePatchAgentName(agent)}.patch`);
561
- try {
562
- diffs.push(captureWorktreeDiff(setup, worktree, agent, patchPath));
563
- } catch {
564
- // Preserve execution flow; failed diff capture maps to an empty per-task patch.
565
- writeEmptyPatch(patchPath);
566
- diffs.push(emptyDiff(index, agent, worktree.branch, patchPath));
567
- }
568
- }
569
-
570
- return diffs;
571
- }
572
-
573
- export function cleanupWorktrees(setup: WorktreeSetup): void {
574
- for (let index = setup.worktrees.length - 1; index >= 0; index--) {
575
- cleanupSingleWorktree(setup.cwd, setup.worktrees[index]!);
576
- }
577
- try { runGitChecked(setup.cwd, ["worktree", "prune"]); } catch {
578
- // Pruning is best-effort cleanup.
579
- }
580
- }
581
-
582
- export function formatWorktreeDiffSummary(diffs: WorktreeDiff[]): string {
583
- const changed = diffs.filter(hasWorktreeChanges);
584
- if (changed.length === 0) return "";
585
-
586
- const lines: string[] = ["=== Worktree Changes ===", ""];
587
- for (const diff of changed) {
588
- lines.push(
589
- `--- Task ${diff.index + 1} (${diff.agent}): ${diff.filesChanged} files changed, +${diff.insertions} -${diff.deletions} ---`,
590
- );
591
- if (diff.diffStat.trim().length > 0) {
592
- lines.push(diff.diffStat);
593
- }
594
- lines.push("");
595
- }
596
-
597
- const patchesDir = path.dirname(changed[0]!.patchPath);
598
- lines.push(`Full patches: ${patchesDir}`);
599
- return lines.join("\n").trimEnd();
600
- }
@@ -1,113 +0,0 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
- import { TEMP_ARTIFACTS_DIR, type ArtifactPaths } from "./types.ts";
4
- import { getAgentDir } from "./utils.ts";
5
- const CLEANUP_MARKER_FILE = ".last-cleanup";
6
- const PROJECT_ARTIFACT_ROOT = ".dm-subagents";
7
-
8
- export function getProjectSubagentsDir(cwd: string): string {
9
- return path.join(cwd, PROJECT_ARTIFACT_ROOT);
10
- }
11
-
12
- export function getProjectArtifactsDir(cwd: string): string {
13
- return path.join(getProjectSubagentsDir(cwd), "artifacts");
14
- }
15
-
16
- export function getProjectChainRunsDir(cwd: string): string {
17
- return path.join(getProjectSubagentsDir(cwd), "chain-runs");
18
- }
19
-
20
- export function getArtifactsDir(sessionFile: string | null, projectCwd?: string): string {
21
- if (projectCwd) return getProjectArtifactsDir(projectCwd);
22
- if (sessionFile) {
23
- const sessionDir = path.dirname(sessionFile);
24
- return path.join(sessionDir, "subagent-artifacts");
25
- }
26
- return TEMP_ARTIFACTS_DIR;
27
- }
28
-
29
- export function getArtifactPaths(artifactsDir: string, runId: string, agent: string, index?: number): ArtifactPaths {
30
- const suffix = index !== undefined ? `_${index}` : "";
31
- const safeAgent = agent.replace(/[^\w.-]/g, "_");
32
- const base = `${runId}_${safeAgent}${suffix}`;
33
- return {
34
- inputPath: path.join(artifactsDir, `${base}_input.md`),
35
- outputPath: path.join(artifactsDir, `${base}_output.md`),
36
- jsonlPath: path.join(artifactsDir, `${base}.jsonl`),
37
- transcriptPath: path.join(artifactsDir, `${base}_transcript.jsonl`),
38
- metadataPath: path.join(artifactsDir, `${base}_meta.json`),
39
- };
40
- }
41
-
42
- export function ensureArtifactsDir(dir: string): void {
43
- fs.mkdirSync(dir, { recursive: true });
44
- }
45
-
46
- export function writeArtifact(filePath: string, content: string): void {
47
- fs.writeFileSync(filePath, content, "utf-8");
48
- }
49
-
50
- export function writeMetadata(filePath: string, metadata: object): void {
51
- fs.writeFileSync(filePath, JSON.stringify(metadata, null, 2), "utf-8");
52
- }
53
-
54
- export function appendJsonl(filePath: string, line: string): void {
55
- fs.appendFileSync(filePath, `${line}\n`);
56
- }
57
-
58
- export function cleanupOldArtifacts(dir: string, maxAgeDays: number): void {
59
- if (!fs.existsSync(dir)) return;
60
-
61
- const markerPath = path.join(dir, CLEANUP_MARKER_FILE);
62
- const now = Date.now();
63
-
64
- if (fs.existsSync(markerPath)) {
65
- const stat = fs.statSync(markerPath);
66
- if (now - stat.mtimeMs < 24 * 60 * 60 * 1000) return;
67
- }
68
-
69
- const maxAgeMs = maxAgeDays * 24 * 60 * 60 * 1000;
70
- const cutoff = now - maxAgeMs;
71
-
72
- for (const file of fs.readdirSync(dir)) {
73
- if (file === CLEANUP_MARKER_FILE) continue;
74
- const filePath = path.join(dir, file);
75
- try {
76
- const stat = fs.statSync(filePath);
77
- if (stat.mtimeMs < cutoff) {
78
- fs.unlinkSync(filePath);
79
- }
80
- } catch {
81
- // Artifact cleanup is best-effort housekeeping. Skip files that disappear
82
- // or become unreadable while scanning so one bad entry does not block the rest.
83
- }
84
- }
85
-
86
- fs.writeFileSync(markerPath, String(now));
87
- }
88
-
89
- export function cleanupAllArtifactDirs(maxAgeDays: number): void {
90
- cleanupOldArtifacts(TEMP_ARTIFACTS_DIR, maxAgeDays);
91
-
92
- const sessionsBase = path.join(getAgentDir(), "sessions");
93
- if (!fs.existsSync(sessionsBase)) return;
94
-
95
- let dirs: string[];
96
- try {
97
- dirs = fs.readdirSync(sessionsBase);
98
- } catch {
99
- // Session artifact cleanup is best-effort. If the sessions root cannot be read,
100
- // skip cleanup instead of failing extension startup.
101
- return;
102
- }
103
-
104
- for (const dir of dirs) {
105
- const artifactsDir = path.join(sessionsBase, dir, "subagent-artifacts");
106
- try {
107
- cleanupOldArtifacts(artifactsDir, maxAgeDays);
108
- } catch {
109
- // Session cleanup is best-effort. Keep going so one unreadable session dir
110
- // does not block cleanup for the rest.
111
- }
112
- }
113
- }