triflux 10.3.4 → 10.7.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 (329) hide show
  1. package/LICENSE +21 -21
  2. package/bin/tfx-doctor-tui.mjs +1 -1
  3. package/bin/tfx-doctor.mjs +6 -1
  4. package/bin/tfx-profile.mjs +1 -1
  5. package/bin/tfx-setup-tui.mjs +1 -1
  6. package/bin/tfx-setup.mjs +6 -1
  7. package/bin/triflux.mjs +2396 -1140
  8. package/hooks/agent-route-guard.mjs +12 -8
  9. package/hooks/cross-review-tracker.mjs +21 -8
  10. package/hooks/error-context.mjs +19 -7
  11. package/hooks/hook-adaptive-collector.mjs +18 -16
  12. package/hooks/hook-manager.mjs +93 -32
  13. package/hooks/hook-orchestrator.mjs +108 -24
  14. package/hooks/hook-registry.json +11 -0
  15. package/hooks/keyword-rules.json +6 -10
  16. package/hooks/lib/resolve-root.mjs +1 -1
  17. package/hooks/mcp-config-watcher.mjs +6 -2
  18. package/hooks/pipeline-stop.mjs +3 -6
  19. package/hooks/safety-guard.mjs +99 -28
  20. package/hooks/session-start-fast.mjs +143 -0
  21. package/hooks/subagent-verifier.mjs +5 -4
  22. package/hub/account-broker.mjs +256 -60
  23. package/hub/adaptive-diagnostic.mjs +75 -48
  24. package/hub/adaptive-inject.mjs +95 -57
  25. package/hub/adaptive-memory.mjs +156 -42
  26. package/hub/adaptive.mjs +60 -31
  27. package/hub/assign-callbacks.mjs +67 -30
  28. package/hub/bridge.mjs +0 -1
  29. package/hub/cli-adapter-base.mjs +200 -48
  30. package/hub/codex-adapter.mjs +76 -96
  31. package/hub/codex-compat.mjs +3 -3
  32. package/hub/codex-preflight.mjs +63 -37
  33. package/hub/delegator/contracts.mjs +19 -23
  34. package/hub/delegator/index.mjs +3 -3
  35. package/hub/delegator/service.mjs +88 -64
  36. package/hub/delegator/tool-definitions.mjs +5 -5
  37. package/hub/fullcycle.mjs +33 -17
  38. package/hub/gemini-adapter.mjs +69 -94
  39. package/hub/hitl.mjs +89 -30
  40. package/hub/intent.mjs +161 -38
  41. package/hub/lib/cache-guard.mjs +43 -17
  42. package/hub/lib/mcp-response-cache.mjs +66 -32
  43. package/hub/lib/memory-store.mjs +285 -111
  44. package/hub/lib/path-utils.mjs +35 -37
  45. package/hub/lib/process-utils.mjs +106 -37
  46. package/hub/lib/spawn-trace.mjs +527 -0
  47. package/hub/lib/ssh-command.mjs +34 -4
  48. package/hub/lib/ssh-retry.mjs +5 -1
  49. package/hub/lib/uuidv7.mjs +4 -3
  50. package/hub/memory-doctor.mjs +266 -106
  51. package/hub/middleware/request-logger.mjs +61 -34
  52. package/hub/paths.mjs +9 -9
  53. package/hub/pipeline/gates/confidence.mjs +34 -15
  54. package/hub/pipeline/gates/consensus.mjs +27 -15
  55. package/hub/pipeline/gates/index.mjs +7 -3
  56. package/hub/pipeline/gates/selfcheck.mjs +57 -19
  57. package/hub/pipeline/index.mjs +77 -42
  58. package/hub/pipeline/state.mjs +10 -10
  59. package/hub/pipeline/transitions.mjs +40 -23
  60. package/hub/platform.mjs +57 -48
  61. package/hub/promote-penalties.mjs +25 -7
  62. package/hub/quality/deslop.mjs +70 -49
  63. package/hub/research.mjs +32 -25
  64. package/hub/router.mjs +240 -107
  65. package/hub/routing/complexity.mjs +132 -29
  66. package/hub/routing/index.mjs +17 -12
  67. package/hub/routing/q-learning.mjs +76 -28
  68. package/hub/server.mjs +4 -4
  69. package/hub/session-fingerprint.mjs +126 -60
  70. package/hub/state.mjs +84 -43
  71. package/hub/store-adapter.mjs +59 -26
  72. package/hub/store.mjs +356 -153
  73. package/hub/team/agent-map.json +22 -7
  74. package/hub/team/ansi.mjs +186 -122
  75. package/hub/team/backend.mjs +28 -10
  76. package/hub/team/cli/commands/attach.mjs +29 -9
  77. package/hub/team/cli/commands/control.mjs +29 -8
  78. package/hub/team/cli/commands/debug.mjs +32 -11
  79. package/hub/team/cli/commands/focus.mjs +38 -11
  80. package/hub/team/cli/commands/interrupt.mjs +18 -6
  81. package/hub/team/cli/commands/kill.mjs +16 -5
  82. package/hub/team/cli/commands/list.mjs +11 -4
  83. package/hub/team/cli/commands/send.mjs +19 -6
  84. package/hub/team/cli/commands/start/index.mjs +154 -31
  85. package/hub/team/cli/commands/start/parse-args.mjs +38 -11
  86. package/hub/team/cli/commands/start/start-headless.mjs +112 -36
  87. package/hub/team/cli/commands/start/start-in-process.mjs +12 -2
  88. package/hub/team/cli/commands/start/start-mux.mjs +70 -21
  89. package/hub/team/cli/commands/start/start-wt.mjs +29 -12
  90. package/hub/team/cli/commands/status.mjs +43 -14
  91. package/hub/team/cli/commands/stop.mjs +11 -4
  92. package/hub/team/cli/commands/task.mjs +8 -3
  93. package/hub/team/cli/commands/tasks.mjs +1 -1
  94. package/hub/team/cli/index.mjs +2 -2
  95. package/hub/team/cli/manifest.mjs +38 -8
  96. package/hub/team/cli/render.mjs +30 -8
  97. package/hub/team/cli/services/attach-fallback.mjs +31 -11
  98. package/hub/team/cli/services/hub-client.mjs +42 -14
  99. package/hub/team/cli/services/member-selector.mjs +11 -4
  100. package/hub/team/cli/services/native-control.mjs +48 -21
  101. package/hub/team/cli/services/runtime-mode.mjs +2 -1
  102. package/hub/team/cli/services/state-store.mjs +25 -8
  103. package/hub/team/cli/services/task-model.mjs +16 -6
  104. package/hub/team/conductor-mesh-bridge.mjs +24 -23
  105. package/hub/team/conductor.mjs +8 -4
  106. package/hub/team/dashboard-anchor.mjs +4 -5
  107. package/hub/team/dashboard-layout.mjs +3 -1
  108. package/hub/team/dashboard-open.mjs +41 -21
  109. package/hub/team/dashboard.mjs +76 -28
  110. package/hub/team/event-log.mjs +18 -10
  111. package/hub/team/handoff.mjs +31 -15
  112. package/hub/team/headless.mjs +2 -1
  113. package/hub/team/health-probe.mjs +69 -54
  114. package/hub/team/launcher-template.mjs +16 -13
  115. package/hub/team/native-supervisor.mjs +65 -21
  116. package/hub/team/native.mjs +74 -35
  117. package/hub/team/nativeProxy.mjs +184 -113
  118. package/hub/team/notify.mjs +119 -76
  119. package/hub/team/orchestrator.mjs +9 -4
  120. package/hub/team/pane.mjs +12 -7
  121. package/hub/team/process-cleanup.mjs +25 -16
  122. package/hub/team/psmux.mjs +491 -201
  123. package/hub/team/remote-probe.mjs +68 -52
  124. package/hub/team/remote-session.mjs +117 -59
  125. package/hub/team/remote-watcher.mjs +61 -33
  126. package/hub/team/routing.mjs +51 -25
  127. package/hub/team/runtime-strategy.mjs +3 -1
  128. package/hub/team/session.mjs +98 -34
  129. package/hub/team/staleState.mjs +72 -30
  130. package/hub/team/swarm-locks.mjs +15 -13
  131. package/hub/team/swarm-planner.mjs +32 -21
  132. package/hub/team/swarm-reconciler.mjs +48 -23
  133. package/hub/team/tui-lite.mjs +266 -68
  134. package/hub/team/tui-remote-adapter.mjs +14 -10
  135. package/hub/team/tui-viewer.mjs +99 -43
  136. package/hub/team/tui.mjs +708 -271
  137. package/hub/team/worktree-lifecycle.mjs +152 -58
  138. package/hub/team/wt-manager.mjs +24 -14
  139. package/hub/token-mode.mjs +71 -71
  140. package/hub/tray.mjs +66 -23
  141. package/hub/workers/claude-worker.mjs +162 -118
  142. package/hub/workers/codex-mcp.mjs +192 -141
  143. package/hub/workers/delegator-mcp.mjs +507 -333
  144. package/hub/workers/factory.mjs +8 -8
  145. package/hub/workers/gemini-worker.mjs +115 -84
  146. package/hub/workers/interface.mjs +6 -1
  147. package/hub/workers/worker-utils.mjs +21 -14
  148. package/hud/colors.mjs +27 -9
  149. package/hud/constants.mjs +162 -26
  150. package/hud/context-monitor.mjs +82 -41
  151. package/hud/hud-qos-status.mjs +129 -49
  152. package/hud/mission-board.mjs +6 -3
  153. package/hud/providers/claude.mjs +226 -115
  154. package/hud/providers/codex.mjs +62 -22
  155. package/hud/providers/gemini.mjs +168 -56
  156. package/hud/renderers.mjs +384 -119
  157. package/hud/terminal.mjs +101 -31
  158. package/hud/utils.mjs +78 -38
  159. package/mesh/index.mjs +11 -5
  160. package/mesh/mesh-budget.mjs +18 -9
  161. package/mesh/mesh-heartbeat.mjs +1 -1
  162. package/mesh/mesh-queue.mjs +3 -5
  163. package/mesh/mesh-router.mjs +5 -4
  164. package/package.json +2 -1
  165. package/scripts/__tests__/gen-skill-docs.test.mjs +36 -7
  166. package/scripts/__tests__/keyword-detector.test.mjs +77 -28
  167. package/scripts/__tests__/mcp-guard-engine.test.mjs +58 -20
  168. package/scripts/__tests__/remote-spawn-transfer.test.mjs +30 -19
  169. package/scripts/__tests__/remote-spawn.test.mjs +10 -4
  170. package/scripts/__tests__/session-start-fast.test.mjs +36 -0
  171. package/scripts/__tests__/skill-template.test.mjs +98 -50
  172. package/scripts/__tests__/smoke.test.mjs +1 -1
  173. package/scripts/__tests__/spawn-trace.test.mjs +102 -0
  174. package/scripts/__tests__/tfx-doctor-diagnose.test.mjs +48 -0
  175. package/scripts/cache-doctor.mjs +11 -4
  176. package/scripts/cache-warmup.mjs +96 -37
  177. package/scripts/claudemd-sync.mjs +27 -17
  178. package/scripts/codex-gateway-preflight.mjs +52 -37
  179. package/scripts/codex-mcp-gateway-sync.mjs +59 -39
  180. package/scripts/completions/tfx.bash +47 -47
  181. package/scripts/completions/tfx.fish +44 -44
  182. package/scripts/completions/tfx.zsh +83 -83
  183. package/scripts/config-audit.mjs +232 -0
  184. package/scripts/convert-to-tmpl.mjs +54 -0
  185. package/scripts/cross-review-gate.mjs +35 -12
  186. package/scripts/cross-review-tracker.mjs +21 -8
  187. package/scripts/demo.mjs +35 -17
  188. package/scripts/doctor-diagnose.mjs +284 -0
  189. package/scripts/gen-skill-docs.mjs +7 -2
  190. package/scripts/gen-skill-manifest.mjs +2 -1
  191. package/scripts/headless-guard.mjs +86 -48
  192. package/scripts/hub-ensure.mjs +45 -26
  193. package/scripts/keyword-detector.mjs +41 -20
  194. package/scripts/keyword-rules-expander.mjs +47 -30
  195. package/scripts/lib/claudemd-scanner.mjs +6 -1
  196. package/scripts/lib/context.mjs +3 -3
  197. package/scripts/lib/cross-review-utils.mjs +6 -3
  198. package/scripts/lib/env-probe.mjs +47 -28
  199. package/scripts/lib/gemini-profiles.mjs +44 -10
  200. package/scripts/lib/handoff.mjs +33 -17
  201. package/scripts/lib/hook-utils.mjs +8 -6
  202. package/scripts/lib/keyword-rules.mjs +43 -19
  203. package/scripts/lib/logger.mjs +24 -24
  204. package/scripts/lib/mcp-filter.mjs +377 -239
  205. package/scripts/lib/mcp-guard-engine.mjs +194 -79
  206. package/scripts/lib/mcp-manifest.mjs +23 -13
  207. package/scripts/lib/mcp-server-catalog.mjs +300 -63
  208. package/scripts/lib/psmux-info.mjs +11 -6
  209. package/scripts/lib/remote-spawn-transfer.mjs +44 -14
  210. package/scripts/lib/skill-template.mjs +30 -7
  211. package/scripts/mcp-check.mjs +58 -39
  212. package/scripts/mcp-gateway-config.mjs +83 -39
  213. package/scripts/mcp-gateway-ensure.mjs +43 -35
  214. package/scripts/mcp-gateway-integration-test.mjs +70 -58
  215. package/scripts/mcp-gateway-start.mjs +126 -60
  216. package/scripts/mcp-gateway-verify.mjs +24 -22
  217. package/scripts/mcp-safety-guard.mjs +44 -11
  218. package/scripts/notion-read.mjs +199 -84
  219. package/scripts/pack.mjs +94 -89
  220. package/scripts/preflight-cache.mjs +27 -10
  221. package/scripts/preinstall.mjs +42 -13
  222. package/scripts/remote-spawn.mjs +309 -94
  223. package/scripts/run.cjs +8 -5
  224. package/scripts/session-spawn-helper.mjs +130 -39
  225. package/scripts/session-stale-cleanup.mjs +123 -0
  226. package/scripts/setup.mjs +941 -492
  227. package/scripts/test-lock.mjs +20 -7
  228. package/scripts/test-tfx-route-no-claude-native.mjs +16 -12
  229. package/scripts/tfx-batch-stats.mjs +32 -11
  230. package/scripts/tfx-gate-activate.mjs +11 -4
  231. package/scripts/tfx-route-post.mjs +87 -20
  232. package/scripts/tfx-route-worker.mjs +57 -51
  233. package/scripts/tfx-route.sh +41 -124
  234. package/scripts/tmp-cleanup.mjs +21 -7
  235. package/scripts/token-snapshot.mjs +204 -85
  236. package/skills/.omc/state/agent-replay-8f0e10a9-9693-4410-96f5-a6b07e8ed995.jsonl +1 -0
  237. package/skills/.omc/state/idle-notif-cooldown.json +3 -0
  238. package/skills/.omc/state/last-tool-error.json +7 -0
  239. package/skills/.omc/state/subagent-tracking.json +7 -0
  240. package/skills/_templates/base.md +1 -6
  241. package/skills/merge-worktree/SKILL.md.tmpl +144 -0
  242. package/skills/shared/telemetry-segment.md +6 -0
  243. package/skills/star-prompt/SKILL.md.tmpl +222 -0
  244. package/skills/tfx-analysis/SKILL.md.tmpl +107 -0
  245. package/skills/tfx-analysis/skill.json +1 -6
  246. package/skills/tfx-auto/SKILL.md +1 -0
  247. package/skills/tfx-auto-codex/SKILL.md.tmpl +106 -0
  248. package/skills/tfx-auto-codex/skill.json +1 -3
  249. package/skills/tfx-autopilot/SKILL.md.tmpl +116 -0
  250. package/skills/tfx-autopilot/skill.json +1 -5
  251. package/skills/tfx-autoresearch/SKILL.md.tmpl +136 -0
  252. package/skills/tfx-autoroute/SKILL.md.tmpl +189 -0
  253. package/skills/tfx-autoroute/skill.json +1 -7
  254. package/skills/tfx-codex/SKILL.md +1 -0
  255. package/skills/tfx-codex/skill.json +1 -3
  256. package/skills/tfx-codex-swarm/SKILL.md.tmpl +16 -0
  257. package/skills/tfx-codex-swarm/evals/evals.json +1 -1
  258. package/skills/tfx-codex-swarm/skill.json +1 -4
  259. package/skills/tfx-codex-swarm-workspace/iteration-1/benchmark.json +54 -12
  260. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/grading.json +35 -7
  261. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/grading.json +35 -7
  262. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/grading.json +25 -5
  263. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/grading.json +25 -5
  264. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/grading.json +20 -4
  265. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/grading.json +16 -4
  266. package/skills/tfx-consensus/SKILL.md.tmpl +146 -0
  267. package/skills/tfx-debate/SKILL.md.tmpl +192 -0
  268. package/skills/tfx-debate/skill.json +1 -7
  269. package/skills/tfx-deep-analysis/SKILL.md.tmpl +228 -0
  270. package/skills/tfx-deep-analysis/skill.json +1 -5
  271. package/skills/tfx-deep-interview/SKILL.md.tmpl +203 -0
  272. package/skills/tfx-deep-plan/SKILL.md.tmpl +282 -0
  273. package/skills/tfx-deep-qa/SKILL.md.tmpl +165 -0
  274. package/skills/tfx-deep-qa/skill.json +1 -6
  275. package/skills/tfx-deep-research/SKILL.md.tmpl +217 -0
  276. package/skills/tfx-deep-review/SKILL.md.tmpl +179 -0
  277. package/skills/tfx-doctor/SKILL.md +21 -0
  278. package/skills/tfx-doctor/SKILL.md.tmpl +172 -0
  279. package/skills/tfx-doctor/skill.json +1 -3
  280. package/skills/tfx-find/SKILL.md +1 -0
  281. package/skills/tfx-forge/SKILL.md.tmpl +187 -0
  282. package/skills/tfx-fullcycle/SKILL.md.tmpl +286 -0
  283. package/skills/tfx-fullcycle/skill.json +1 -6
  284. package/skills/tfx-gemini/SKILL.md.tmpl +91 -0
  285. package/skills/tfx-gemini/skill.json +1 -3
  286. package/skills/tfx-hooks/SKILL.md.tmpl +216 -0
  287. package/skills/tfx-hooks/skill.json +1 -3
  288. package/skills/tfx-hub/SKILL.md.tmpl +212 -0
  289. package/skills/tfx-hub/skill.json +1 -3
  290. package/skills/tfx-index/SKILL.md +1 -0
  291. package/skills/tfx-index/skill.json +1 -6
  292. package/skills/tfx-interview/SKILL.md.tmpl +285 -0
  293. package/skills/tfx-multi/SKILL.md.tmpl +183 -0
  294. package/skills/tfx-multi/skill.json +1 -3
  295. package/skills/tfx-panel/SKILL.md.tmpl +189 -0
  296. package/skills/tfx-panel/skill.json +1 -7
  297. package/skills/tfx-persist/SKILL.md.tmpl +270 -0
  298. package/skills/tfx-persist/skill.json +1 -7
  299. package/skills/tfx-plan/SKILL.md +1 -0
  300. package/skills/tfx-plan/skill.json +1 -6
  301. package/skills/tfx-profile/SKILL.md.tmpl +239 -0
  302. package/skills/tfx-profile/skill.json +1 -3
  303. package/skills/tfx-prune/SKILL.md.tmpl +200 -0
  304. package/skills/tfx-prune/skill.json +1 -7
  305. package/skills/tfx-psmux-rules/SKILL.md.tmpl +326 -0
  306. package/skills/tfx-psmux-rules/skill.json +1 -4
  307. package/skills/tfx-qa/SKILL.md +1 -0
  308. package/skills/tfx-qa/skill.json +1 -6
  309. package/skills/tfx-ralph/SKILL.md.tmpl +28 -0
  310. package/skills/tfx-ralph/skill.json +1 -4
  311. package/skills/tfx-remote-setup/SKILL.md.tmpl +576 -0
  312. package/skills/tfx-remote-setup/skill.json +1 -3
  313. package/skills/tfx-remote-spawn/SKILL.md.tmpl +263 -0
  314. package/skills/tfx-remote-spawn/references/hosts.json +16 -0
  315. package/skills/tfx-remote-spawn/skill.json +1 -4
  316. package/skills/tfx-research/SKILL.md +1 -0
  317. package/skills/tfx-review/SKILL.md +1 -0
  318. package/skills/tfx-review/skill.json +1 -6
  319. package/skills/tfx-setup/SKILL.md.tmpl +504 -0
  320. package/skills/tfx-setup/skill.json +1 -3
  321. package/skills/tfx-swarm/SKILL.md +22 -0
  322. package/skills/tfx-swarm/SKILL.md.tmpl +218 -0
  323. package/tui/codex-profile.mjs +88 -33
  324. package/tui/core.mjs +45 -15
  325. package/tui/doctor.mjs +75 -28
  326. package/tui/gemini-profile.mjs +74 -29
  327. package/tui/monitor-data.mjs +8 -4
  328. package/tui/monitor.mjs +71 -27
  329. package/tui/setup.mjs +133 -42
@@ -3,24 +3,29 @@
3
3
  // Convention: .codex-swarm/wt-{slug} paths, swarm/{runId}/{slug} branches.
4
4
  // Remote support: host option → SSH-based git operations via remote-session.mjs.
5
5
 
6
- import { execFile } from 'node:child_process';
7
- import { resolve, normalize, join } from 'node:path';
8
- import { mkdir, rm, access, readdir } from 'node:fs/promises';
9
- import { remoteGit, validateHost } from './remote-session.mjs';
6
+ import { execFile } from "node:child_process";
7
+ import { access, mkdir, readdir, rm } from "node:fs/promises";
8
+ import { join, normalize, resolve } from "node:path";
9
+ import { remoteGit, validateHost } from "./remote-session.mjs";
10
10
 
11
- const SWARM_ROOT = '.codex-swarm';
11
+ const SWARM_ROOT = ".codex-swarm";
12
12
  const SLEEP_MS = 2000; // WT race-guard (MEMORY.md: wt-attach-spacing)
13
13
 
14
14
  function git(args, cwd) {
15
15
  return new Promise((res, rej) => {
16
- execFile('git', args, { cwd, windowsHide: true, timeout: 30_000 }, (err, stdout, stderr) => {
17
- if (err) {
18
- const msg = `git ${args[0]} failed: ${stderr?.trim() || err.message}`;
19
- rej(new Error(msg));
20
- } else {
21
- res(stdout.trim());
22
- }
23
- });
16
+ execFile(
17
+ "git",
18
+ args,
19
+ { cwd, windowsHide: true, timeout: 30_000 },
20
+ (err, stdout, stderr) => {
21
+ if (err) {
22
+ const msg = `git ${args[0]} failed: ${stderr?.trim() || err.message}`;
23
+ rej(new Error(msg));
24
+ } else {
25
+ res(stdout.trim());
26
+ }
27
+ },
28
+ );
24
29
  });
25
30
  }
26
31
 
@@ -30,7 +35,7 @@ function sleep(ms) {
30
35
 
31
36
  /** Normalize path for Windows compatibility. */
32
37
  function normPath(p) {
33
- return normalize(p).replace(/\\/g, '/');
38
+ return normalize(p).replace(/\\/g, "/");
34
39
  }
35
40
 
36
41
  /**
@@ -45,22 +50,45 @@ function normPath(p) {
45
50
  * @param {object} [opts.remoteEnv] — remote environment from probeRemoteEnv()
46
51
  * @returns {Promise<{ worktreePath: string, branchName: string, remote: boolean }>}
47
52
  */
48
- export async function ensureWorktree({ slug, runId, rootDir = process.cwd(), baseBranch = 'main', host, remoteEnv }) {
53
+ export async function ensureWorktree({
54
+ slug,
55
+ runId,
56
+ rootDir = process.cwd(),
57
+ baseBranch = "main",
58
+ host,
59
+ remoteEnv,
60
+ }) {
49
61
  const branchName = `swarm/${runId}/${slug}`;
50
62
 
51
63
  // ── Remote path: SSH-based worktree creation ──
52
64
  if (host && remoteEnv) {
53
- const remoteRoot = rootDir.replace(/\\/g, '/');
65
+ const remoteRoot = rootDir.replace(/\\/g, "/");
54
66
  const remoteWtDir = `${remoteRoot}/${SWARM_ROOT}/wt-${slug}`;
55
67
 
56
- try { remoteGit(host, remoteEnv, ['worktree', 'prune'], remoteRoot); } catch { /* best-effort */ }
68
+ try {
69
+ remoteGit(host, remoteEnv, ["worktree", "prune"], remoteRoot);
70
+ } catch {
71
+ /* best-effort */
72
+ }
57
73
 
58
74
  try {
59
- remoteGit(host, remoteEnv, ['worktree', 'add', remoteWtDir, '-b', branchName, baseBranch], remoteRoot);
75
+ remoteGit(
76
+ host,
77
+ remoteEnv,
78
+ ["worktree", "add", remoteWtDir, "-b", branchName, baseBranch],
79
+ remoteRoot,
80
+ );
60
81
  } catch {
61
82
  try {
62
- remoteGit(host, remoteEnv, ['worktree', 'add', remoteWtDir, branchName], remoteRoot);
63
- } catch { /* already exists — acceptable */ }
83
+ remoteGit(
84
+ host,
85
+ remoteEnv,
86
+ ["worktree", "add", remoteWtDir, branchName],
87
+ remoteRoot,
88
+ );
89
+ } catch {
90
+ /* already exists — acceptable */
91
+ }
64
92
  }
65
93
 
66
94
  return { worktreePath: remoteWtDir, branchName, remote: true };
@@ -74,27 +102,36 @@ export async function ensureWorktree({ slug, runId, rootDir = process.cwd(), bas
74
102
  // Check if worktree already exists
75
103
  try {
76
104
  await access(wtDir);
77
- await git(['rev-parse', '--is-inside-work-tree'], wtDir);
105
+ await git(["rev-parse", "--is-inside-work-tree"], wtDir);
78
106
  return { worktreePath: normPath(wtDir), branchName, remote: false };
79
107
  } catch {
80
108
  // Doesn't exist or invalid — create fresh
81
109
  }
82
110
 
83
111
  try {
84
- await git(['worktree', 'prune'], rootDir);
85
- } catch { /* best-effort */ }
112
+ await git(["worktree", "prune"], rootDir);
113
+ } catch {
114
+ /* best-effort */
115
+ }
86
116
 
87
117
  await sleep(SLEEP_MS);
88
118
 
89
119
  try {
90
- await git(['worktree', 'add', wtDir, '-b', branchName, baseBranch], rootDir);
120
+ await git(
121
+ ["worktree", "add", wtDir, "-b", branchName, baseBranch],
122
+ rootDir,
123
+ );
91
124
  } catch {
92
- await git(['worktree', 'add', wtDir, branchName], rootDir);
125
+ await git(["worktree", "add", wtDir, branchName], rootDir);
93
126
  }
94
127
 
95
128
  // #34 L2: worktree에 복사된 .claude-plugin 제거 (하네스가 PLUGIN_ROOT를 오인하는 것 방지)
96
- const pluginDir = join(wtDir, '.claude-plugin');
97
- try { await rm(pluginDir, { recursive: true, force: true }); } catch { /* absent → ok */ }
129
+ const pluginDir = join(wtDir, ".claude-plugin");
130
+ try {
131
+ await rm(pluginDir, { recursive: true, force: true });
132
+ } catch {
133
+ /* absent → ok */
134
+ }
98
135
 
99
136
  return { worktreePath: normPath(wtDir), branchName, remote: false };
100
137
  }
@@ -108,18 +145,22 @@ export async function ensureWorktree({ slug, runId, rootDir = process.cwd(), bas
108
145
  * @param {string} [opts.rootDir=process.cwd()]
109
146
  * @returns {Promise<{ integrationBranch: string, baseCommit: string }>}
110
147
  */
111
- export async function prepareIntegrationBranch({ runId, baseBranch, rootDir = process.cwd() }) {
148
+ export async function prepareIntegrationBranch({
149
+ runId,
150
+ baseBranch,
151
+ rootDir = process.cwd(),
152
+ }) {
112
153
  const integrationBranch = `swarm/${runId}/merge`;
113
154
 
114
155
  // Record base commit for rollback
115
- const baseCommit = await git(['rev-parse', baseBranch], rootDir);
156
+ const baseCommit = await git(["rev-parse", baseBranch], rootDir);
116
157
 
117
158
  // Create integration branch from base
118
159
  try {
119
- await git(['branch', integrationBranch, baseBranch], rootDir);
160
+ await git(["branch", integrationBranch, baseBranch], rootDir);
120
161
  } catch {
121
162
  // Branch may already exist — reset to base
122
- await git(['branch', '-f', integrationBranch, baseBranch], rootDir);
163
+ await git(["branch", "-f", integrationBranch, baseBranch], rootDir);
123
164
  }
124
165
 
125
166
  return { integrationBranch, baseCommit };
@@ -135,25 +176,41 @@ export async function prepareIntegrationBranch({ runId, baseBranch, rootDir = pr
135
176
  * @param {string} [opts.rootDir=process.cwd()]
136
177
  * @returns {Promise<{ ok: boolean, headCommit?: string, error?: string }>}
137
178
  */
138
- export async function rebaseShardOntoIntegration({ shardBranch, integrationBranch, rootDir = process.cwd() }) {
179
+ export async function rebaseShardOntoIntegration({
180
+ shardBranch,
181
+ integrationBranch,
182
+ rootDir = process.cwd(),
183
+ }) {
139
184
  // Backup integration HEAD for rollback
140
- const backupCommit = await git(['rev-parse', integrationBranch], rootDir);
185
+ const backupCommit = await git(["rev-parse", integrationBranch], rootDir);
141
186
 
142
187
  try {
143
188
  // Rebase shard onto integration
144
- await git(['rebase', integrationBranch, shardBranch], rootDir);
189
+ await git(["rebase", integrationBranch, shardBranch], rootDir);
145
190
 
146
191
  // Fast-forward integration to include shard changes
147
- await git(['checkout', integrationBranch], rootDir);
148
- await git(['merge', '--ff-only', shardBranch], rootDir);
192
+ await git(["checkout", integrationBranch], rootDir);
193
+ await git(["merge", "--ff-only", shardBranch], rootDir);
149
194
 
150
- const headCommit = await git(['rev-parse', 'HEAD'], rootDir);
195
+ const headCommit = await git(["rev-parse", "HEAD"], rootDir);
151
196
  return { ok: true, headCommit };
152
197
  } catch (err) {
153
198
  // Abort rebase and restore integration branch
154
- try { await git(['rebase', '--abort'], rootDir); } catch { /* already clean */ }
155
- try { await git(['checkout', integrationBranch], rootDir); } catch { /* best-effort */ }
156
- try { await git(['reset', '--hard', backupCommit], rootDir); } catch { /* best-effort */ }
199
+ try {
200
+ await git(["rebase", "--abort"], rootDir);
201
+ } catch {
202
+ /* already clean */
203
+ }
204
+ try {
205
+ await git(["checkout", integrationBranch], rootDir);
206
+ } catch {
207
+ /* best-effort */
208
+ }
209
+ try {
210
+ await git(["reset", "--hard", backupCommit], rootDir);
211
+ } catch {
212
+ /* best-effort */
213
+ }
157
214
 
158
215
  return { ok: false, error: err.message };
159
216
  }
@@ -169,18 +226,30 @@ export async function rebaseShardOntoIntegration({ shardBranch, integrationBranc
169
226
  * @param {string} [opts.rootDir=process.cwd()]
170
227
  * @param {boolean} [opts.force=false]
171
228
  */
172
- export async function pruneWorktree({ worktreePath, branchName, rootDir = process.cwd(), force = false }) {
173
- const forceFlag = force ? '--force' : '';
229
+ export async function pruneWorktree({
230
+ worktreePath,
231
+ branchName,
232
+ rootDir = process.cwd(),
233
+ force = false,
234
+ }) {
235
+ const forceFlag = force ? "--force" : "";
174
236
 
175
237
  // Remove worktree (with retry for Windows file handle issues — E5)
176
238
  for (let attempt = 0; attempt < 3; attempt++) {
177
239
  try {
178
- await git(['worktree', 'remove', worktreePath, ...(forceFlag ? [forceFlag] : [])], rootDir);
240
+ await git(
241
+ ["worktree", "remove", worktreePath, ...(forceFlag ? [forceFlag] : [])],
242
+ rootDir,
243
+ );
179
244
  break;
180
245
  } catch (_err) {
181
246
  if (attempt === 2) {
182
247
  // Last resort: rm the directory and prune
183
- try { await rm(worktreePath, { recursive: true, force: true }); } catch { /* ignore */ }
248
+ try {
249
+ await rm(worktreePath, { recursive: true, force: true });
250
+ } catch {
251
+ /* ignore */
252
+ }
184
253
  }
185
254
  await sleep(SLEEP_MS);
186
255
  }
@@ -188,11 +257,19 @@ export async function pruneWorktree({ worktreePath, branchName, rootDir = proces
188
257
 
189
258
  // Prune stale worktree references
190
259
  await sleep(SLEEP_MS);
191
- try { await git(['worktree', 'prune'], rootDir); } catch { /* best-effort */ }
260
+ try {
261
+ await git(["worktree", "prune"], rootDir);
262
+ } catch {
263
+ /* best-effort */
264
+ }
192
265
 
193
266
  // Delete branch if specified
194
267
  if (branchName) {
195
- try { await git(['branch', '-D', branchName], rootDir); } catch { /* may not exist */ }
268
+ try {
269
+ await git(["branch", "-D", branchName], rootDir);
270
+ } catch {
271
+ /* may not exist */
272
+ }
196
273
  }
197
274
  }
198
275
 
@@ -216,17 +293,18 @@ export async function pruneOrphanWorktrees({ rootDir = process.cwd() } = {}) {
216
293
  return removed; // .codex-swarm/ doesn't exist → nothing to clean
217
294
  }
218
295
 
219
- const wtDirs = entries.filter(e => e.startsWith('wt-'));
296
+ const wtDirs = entries.filter((e) => e.startsWith("wt-"));
220
297
  if (wtDirs.length === 0) return removed;
221
298
 
222
299
  // Get registered worktree paths from git
223
300
  let registeredPaths;
224
301
  try {
225
- const raw = await git(['worktree', 'list', '--porcelain'], rootDir);
302
+ const raw = await git(["worktree", "list", "--porcelain"], rootDir);
226
303
  registeredPaths = new Set(
227
- raw.split('\n')
228
- .filter(l => l.startsWith('worktree '))
229
- .map(l => normPath(l.slice('worktree '.length))),
304
+ raw
305
+ .split("\n")
306
+ .filter((l) => l.startsWith("worktree "))
307
+ .map((l) => normPath(l.slice("worktree ".length))),
230
308
  );
231
309
  } catch {
232
310
  return removed; // git worktree list failed → don't remove anything
@@ -239,13 +317,19 @@ export async function pruneOrphanWorktrees({ rootDir = process.cwd() } = {}) {
239
317
  try {
240
318
  await rm(fullPath, { recursive: true, force: true });
241
319
  removed.push(dir);
242
- } catch { /* best-effort */ }
320
+ } catch {
321
+ /* best-effort */
322
+ }
243
323
  }
244
324
  }
245
325
 
246
326
  // Prune stale git references
247
327
  if (removed.length > 0) {
248
- try { await git(['worktree', 'prune'], rootDir); } catch { /* best-effort */ }
328
+ try {
329
+ await git(["worktree", "prune"], rootDir);
330
+ } catch {
331
+ /* best-effort */
332
+ }
249
333
  }
250
334
 
251
335
  return removed;
@@ -265,24 +349,34 @@ export async function pruneOrphanWorktrees({ rootDir = process.cwd() } = {}) {
265
349
  * @param {string} [opts.rootDir=process.cwd()] — local repo root
266
350
  * @returns {Promise<{ ok: boolean, localRef?: string, error?: string }>}
267
351
  */
268
- export async function fetchRemoteShard({ host, sshUser, remoteRepoPath, branchName, rootDir = process.cwd() }) {
352
+ export async function fetchRemoteShard({
353
+ host,
354
+ sshUser,
355
+ remoteRepoPath,
356
+ branchName,
357
+ rootDir = process.cwd(),
358
+ }) {
269
359
  validateHost(host);
270
360
 
271
361
  const remoteName = `_swarm-${host}-${Date.now()}`;
272
362
  const sshUrl = `ssh://${sshUser}@${host}${remoteRepoPath}`;
273
363
 
274
364
  try {
275
- await git(['remote', 'add', remoteName, sshUrl], rootDir);
365
+ await git(["remote", "add", remoteName, sshUrl], rootDir);
276
366
 
277
- await git(['fetch', remoteName, branchName, '--no-tags'], rootDir);
367
+ await git(["fetch", remoteName, branchName, "--no-tags"], rootDir);
278
368
 
279
369
  const localRef = `${remoteName}/${branchName}`;
280
- const headCommit = await git(['rev-parse', `FETCH_HEAD`], rootDir);
370
+ const headCommit = await git(["rev-parse", `FETCH_HEAD`], rootDir);
281
371
 
282
372
  return { ok: true, localRef, headCommit };
283
373
  } catch (err) {
284
374
  return { ok: false, error: err.message };
285
375
  } finally {
286
- try { await git(['remote', 'remove', remoteName], rootDir); } catch { /* cleanup */ }
376
+ try {
377
+ await git(["remote", "remove", remoteName], rootDir);
378
+ } catch {
379
+ /* cleanup */
380
+ }
287
381
  }
288
382
  }
@@ -1,4 +1,4 @@
1
- import childProcess from "node:child_process";
1
+ import * as childProcess from "../lib/spawn-trace.mjs";
2
2
  import { existsSync, mkdirSync, readFileSync, rmSync } from "node:fs";
3
3
  import { platform as osPlatform, tmpdir } from "node:os";
4
4
  import { join } from "node:path";
@@ -61,8 +61,8 @@ function buildWrappedCommand(pidFile, command) {
61
61
  * CreateProcess 이중 쿼팅 문제를 완전히 회피한다.
62
62
  */
63
63
  function encodeForPowerShell(script) {
64
- const buf = Buffer.from(script, 'utf16le');
65
- return buf.toString('base64');
64
+ const buf = Buffer.from(script, "utf16le");
65
+ return buf.toString("base64");
66
66
  }
67
67
 
68
68
  function matchesTitlePattern(title, pattern) {
@@ -316,29 +316,35 @@ export function createWtManager(opts = {}) {
316
316
  * @param {number} [opts.size] — 퍼센트 (0-100)
317
317
  */
318
318
  async function splitPane(opts = {}) {
319
- const direction = opts.direction === 'H' ? '-H' : '-V';
320
- const args = ['-w', windowName, 'sp', direction];
319
+ const direction = opts.direction === "H" ? "-H" : "-V";
320
+ const args = ["-w", windowName, "sp", direction];
321
321
 
322
322
  if (opts.title) {
323
- args.push('--title', String(opts.title));
323
+ args.push("--title", String(opts.title));
324
324
  }
325
325
  if (opts.profile) {
326
- args.push('--profile', String(opts.profile));
326
+ args.push("--profile", String(opts.profile));
327
327
  }
328
328
  if (opts.size && Number.isFinite(opts.size)) {
329
- args.push('-s', String(opts.size / 100));
329
+ args.push("-s", String(opts.size / 100));
330
330
  }
331
331
  if (opts.cwd) {
332
- args.push('-d', String(opts.cwd));
332
+ args.push("-d", String(opts.cwd));
333
333
  }
334
334
  if (opts.command) {
335
335
  const script = opts.command;
336
- args.push('--', 'powershell.exe', '-NoExit', '-EncodedCommand', encodeForPowerShell(script));
336
+ args.push(
337
+ "--",
338
+ "powershell.exe",
339
+ "-NoExit",
340
+ "-EncodedCommand",
341
+ encodeForPowerShell(script),
342
+ );
337
343
  }
338
344
 
339
- const child = spawnFn('wt.exe', args, {
345
+ const child = spawnFn("wt.exe", args, {
340
346
  detached: true,
341
- stdio: 'ignore',
347
+ stdio: "ignore",
342
348
  });
343
349
  child?.unref?.();
344
350
 
@@ -355,12 +361,16 @@ export function createWtManager(opts = {}) {
355
361
 
356
362
  // 첫 번째는 새 탭으로 생성
357
363
  const first = panes[0];
358
- await createTab({ title: first.title, command: first.command, profile: first.profile });
364
+ await createTab({
365
+ title: first.title,
366
+ command: first.command,
367
+ profile: first.profile,
368
+ });
359
369
 
360
370
  // 나머지는 split-pane으로 분할
361
371
  for (let i = 1; i < panes.length; i++) {
362
372
  await splitPane({
363
- direction: panes[i].direction || 'V',
373
+ direction: panes[i].direction || "V",
364
374
  title: panes[i].title,
365
375
  command: panes[i].command,
366
376
  profile: panes[i].profile,