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
@@ -1,7 +1,5 @@
1
1
  // hub/lib/path-utils.mjs — Windows/POSIX 경로 변환 유틸리티
2
2
 
3
- import { platform } from 'node:os';
4
-
5
3
  /**
6
4
  * Windows 경로를 Git Bash 스타일 POSIX 경로로 변환한다.
7
5
  * C:\foo\bar → /c/foo/bar
@@ -11,19 +9,19 @@ import { platform } from 'node:os';
11
9
  * @returns {string}
12
10
  */
13
11
  export function toPosixPath(windowsPath) {
14
- if (windowsPath == null) return '';
12
+ if (windowsPath == null) return "";
15
13
  const p = String(windowsPath);
16
- if (!p) return '';
14
+ if (!p) return "";
17
15
 
18
16
  // 이미 POSIX 경로 (/ 로 시작하거나 드라이브 레터 없음)
19
17
  const winDriveMatch = p.match(/^([A-Za-z]):[/\\](.*)/);
20
18
  if (!winDriveMatch) {
21
19
  // 백슬래시만 있는 상대 경로도 forward slash로 변환
22
- return p.replace(/\\/g, '/');
20
+ return p.replace(/\\/g, "/");
23
21
  }
24
22
 
25
23
  const drive = winDriveMatch[1].toLowerCase();
26
- const rest = winDriveMatch[2].replace(/\\/g, '/');
24
+ const rest = winDriveMatch[2].replace(/\\/g, "/");
27
25
  return `/${drive}/${rest}`;
28
26
  }
29
27
 
@@ -36,20 +34,20 @@ export function toPosixPath(windowsPath) {
36
34
  * @returns {string}
37
35
  */
38
36
  export function toWindowsPath(posixPath) {
39
- if (posixPath == null) return '';
37
+ if (posixPath == null) return "";
40
38
  const p = String(posixPath);
41
- if (!p) return '';
39
+ if (!p) return "";
42
40
 
43
41
  // 이미 Windows 경로
44
42
  if (/^[A-Za-z]:/.test(p)) {
45
- return p.replace(/\//g, '\\');
43
+ return p.replace(/\//g, "\\");
46
44
  }
47
45
 
48
46
  // Git Bash 스타일: /c/foo/bar
49
47
  const gitBashMatch = p.match(/^\/([a-zA-Z])(\/.*)?$/);
50
48
  if (gitBashMatch) {
51
49
  const drive = gitBashMatch[1].toUpperCase();
52
- const rest = gitBashMatch[2] ? gitBashMatch[2].replace(/\//g, '\\') : '\\';
50
+ const rest = gitBashMatch[2] ? gitBashMatch[2].replace(/\//g, "\\") : "\\";
53
51
  return `${drive}:${rest}`;
54
52
  }
55
53
 
@@ -63,12 +61,12 @@ export function toWindowsPath(posixPath) {
63
61
  * @returns {string}
64
62
  */
65
63
  export function normalizePath(p) {
66
- if (p == null) return '';
64
+ if (p == null) return "";
67
65
  const str = String(p);
68
- if (process.platform === 'win32') {
69
- return str.replace(/\//g, '\\');
66
+ if (process.platform === "win32") {
67
+ return str.replace(/\//g, "\\");
70
68
  }
71
- return str.replace(/\\/g, '/');
69
+ return str.replace(/\\/g, "/");
72
70
  }
73
71
 
74
72
  /**
@@ -78,29 +76,29 @@ export function normalizePath(p) {
78
76
  * @returns {string}
79
77
  */
80
78
  export function resolveShellPath(path, shellType) {
81
- if (path == null) return '';
79
+ if (path == null) return "";
82
80
  const p = String(path);
83
81
 
84
82
  switch (shellType) {
85
- case 'git-bash':
83
+ case "git-bash":
86
84
  return toPosixPath(p);
87
85
 
88
- case 'wsl': {
86
+ case "wsl": {
89
87
  // Git Bash 경로를 먼저 Windows로 변환 후 WSL 형식으로
90
- let winPath = /^[A-Za-z]:/.test(p) ? p : toWindowsPath(p);
88
+ const winPath = /^[A-Za-z]:/.test(p) ? p : toWindowsPath(p);
91
89
  const wslDriveMatch = winPath.match(/^([A-Za-z]):[/\\](.*)/);
92
90
  if (wslDriveMatch) {
93
91
  const drive = wslDriveMatch[1].toLowerCase();
94
- const rest = wslDriveMatch[2].replace(/\\/g, '/');
92
+ const rest = wslDriveMatch[2].replace(/\\/g, "/");
95
93
  return rest ? `/mnt/${drive}/${rest}` : `/mnt/${drive}`;
96
94
  }
97
95
  // 이미 /mnt/ 형식이면 그대로
98
- if (p.startsWith('/mnt/')) return p;
99
- return p.replace(/\\/g, '/');
96
+ if (p.startsWith("/mnt/")) return p;
97
+ return p.replace(/\\/g, "/");
100
98
  }
101
99
 
102
- case 'cmd':
103
- case 'powershell':
100
+ case "cmd":
101
+ case "powershell":
104
102
  return toWindowsPath(p);
105
103
 
106
104
  default:
@@ -115,35 +113,35 @@ export function resolveShellPath(path, shellType) {
115
113
  export function detectShellType() {
116
114
  // WSL 감지: WSL_DISTRO_NAME 또는 WSLENV
117
115
  if (process.env.WSL_DISTRO_NAME || process.env.WSLENV) {
118
- return 'wsl';
116
+ return "wsl";
119
117
  }
120
118
 
121
119
  // Windows 플랫폼
122
- if (process.platform === 'win32') {
123
- const shell = process.env.SHELL || '';
124
- const term = process.env.TERM || '';
125
- const msystem = process.env.MSYSTEM || '';
120
+ if (process.platform === "win32") {
121
+ const shell = process.env.SHELL || "";
122
+ const term = process.env.TERM || "";
123
+ const msystem = process.env.MSYSTEM || "";
126
124
 
127
125
  // Git Bash 감지: SHELL=/usr/bin/bash + MSYSTEM=MINGW64 등
128
126
  if (
129
- shell.includes('bash') ||
130
- msystem.startsWith('MINGW') ||
131
- msystem.startsWith('MSYS') ||
132
- term === 'xterm'
127
+ shell.includes("bash") ||
128
+ msystem.startsWith("MINGW") ||
129
+ msystem.startsWith("MSYS") ||
130
+ term === "xterm"
133
131
  ) {
134
- return 'git-bash';
132
+ return "git-bash";
135
133
  }
136
134
 
137
135
  // PowerShell 감지
138
136
  if (process.env.PSModulePath || process.env.PSHOME) {
139
- return 'powershell';
137
+ return "powershell";
140
138
  }
141
139
 
142
- return 'cmd';
140
+ return "cmd";
143
141
  }
144
142
 
145
143
  // 비-Windows
146
- return 'unix';
144
+ return "unix";
147
145
  }
148
146
 
149
147
  /**
@@ -153,7 +151,7 @@ export function detectShellType() {
153
151
  */
154
152
  export function isWslPath(p) {
155
153
  if (p == null) return false;
156
- return String(p).startsWith('/mnt/');
154
+ return String(p).startsWith("/mnt/");
157
155
  }
158
156
 
159
157
  /**
@@ -2,7 +2,13 @@
2
2
  // 프로세스 관련 공유 유틸리티
3
3
 
4
4
  import { execSync } from "node:child_process";
5
- import { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync } from "node:fs";
5
+ import {
6
+ existsSync,
7
+ mkdirSync,
8
+ readFileSync,
9
+ unlinkSync,
10
+ writeFileSync,
11
+ } from "node:fs";
6
12
  import { homedir, tmpdir } from "node:os";
7
13
  import { join } from "node:path";
8
14
  import { IS_WINDOWS, killProcess } from "../platform.mjs";
@@ -27,8 +33,8 @@ export function isPidAlive(pid) {
27
33
  process.kill(pid, 0);
28
34
  return true;
29
35
  } catch (e) {
30
- if (e?.code === 'EPERM') return true;
31
- if (e?.code === 'ESRCH') return false;
36
+ if (e?.code === "EPERM") return true;
37
+ if (e?.code === "ESRCH") return false;
32
38
  return false;
33
39
  }
34
40
  }
@@ -41,7 +47,9 @@ function sleepSyncMs(ms) {
41
47
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
42
48
  } catch {
43
49
  const end = Date.now() + ms;
44
- while (Date.now() < end) { /* spin */ }
50
+ while (Date.now() < end) {
51
+ /* spin */
52
+ }
45
53
  }
46
54
  }
47
55
 
@@ -56,7 +64,7 @@ function killWithEscalation(orphanPids, procMap) {
56
64
  if (orphanPids.length === 0) return 0;
57
65
 
58
66
  // SIGTERM 전 alive 스냅샷 — 이미 죽은 PID는 카운트에서 제외
59
- const aliveBeforeKill = new Set(orphanPids.filter(pid => isPidAlive(pid)));
67
+ const aliveBeforeKill = new Set(orphanPids.filter((pid) => isPidAlive(pid)));
60
68
 
61
69
  for (const pid of aliveBeforeKill) {
62
70
  killProcess(pid, { signal: "SIGTERM" });
@@ -77,7 +85,12 @@ function killWithEscalation(orphanPids, procMap) {
77
85
  IS_WINDOWS
78
86
  ? `powershell -NoProfile -WindowStyle Hidden -Command "(Get-CimInstance Win32_Process -Filter 'ProcessId=${pid}' -ErrorAction SilentlyContinue).ParentProcessId"`
79
87
  : `ps -o ppid= -p ${pid}`,
80
- { encoding: "utf8", timeout: 3000, stdio: ["pipe", "pipe", "pipe"], windowsHide: true },
88
+ {
89
+ encoding: "utf8",
90
+ timeout: 3000,
91
+ stdio: ["pipe", "pipe", "pipe"],
92
+ windowsHide: true,
93
+ },
81
94
  );
82
95
  const currentPpid = Number.parseInt(current.trim(), 10);
83
96
  if (Number.isFinite(currentPpid) && currentPpid !== snapshot.ppid) {
@@ -107,41 +120,56 @@ function ensureHelperScripts() {
107
120
  let needsUpdate = true;
108
121
  try {
109
122
  if (existsSync(VERSION_FILE)) {
110
- const cached = Number.parseInt(readFileSync(VERSION_FILE, "utf8").trim(), 10);
123
+ const cached = Number.parseInt(
124
+ readFileSync(VERSION_FILE, "utf8").trim(),
125
+ 10,
126
+ );
111
127
  if (cached === SCRIPT_VERSION) needsUpdate = false;
112
128
  }
113
129
  } catch {}
114
130
 
115
131
  if (needsUpdate) {
116
132
  // 기존 스크립트 삭제 후 재생성
117
- try { unlinkSync(SCAN_SCRIPT_PATH); } catch {}
118
- try { unlinkSync(TREE_SCRIPT_PATH); } catch {}
133
+ try {
134
+ unlinkSync(SCAN_SCRIPT_PATH);
135
+ } catch {}
136
+ try {
137
+ unlinkSync(TREE_SCRIPT_PATH);
138
+ } catch {}
119
139
  }
120
140
 
121
141
  if (!existsSync(TREE_SCRIPT_PATH)) {
122
- writeFileSync(TREE_SCRIPT_PATH, [
123
- "param([int]$StartPid)",
124
- "$p = $StartPid",
125
- "for ($i = 0; $i -lt 10; $i++) {",
126
- " if ($p -le 0) { break }",
127
- " Write-Output $p",
128
- ' $parent = (Get-CimInstance Win32_Process -Filter "ProcessId=$p" -ErrorAction SilentlyContinue).ParentProcessId',
129
- " if ($null -eq $parent -or $parent -le 0) { break }",
130
- " $p = $parent",
131
- "}",
132
- ].join("\n"), "utf8");
142
+ writeFileSync(
143
+ TREE_SCRIPT_PATH,
144
+ [
145
+ "param([int]$StartPid)",
146
+ "$p = $StartPid",
147
+ "for ($i = 0; $i -lt 10; $i++) {",
148
+ " if ($p -le 0) { break }",
149
+ " Write-Output $p",
150
+ ' $parent = (Get-CimInstance Win32_Process -Filter "ProcessId=$p" -ErrorAction SilentlyContinue).ParentProcessId',
151
+ " if ($null -eq $parent -or $parent -le 0) { break }",
152
+ " $p = $parent",
153
+ "}",
154
+ ].join("\n"),
155
+ "utf8",
156
+ );
133
157
  }
134
158
 
135
159
  if (!existsSync(SCAN_SCRIPT_PATH)) {
136
160
  // CLI + 쉘 + 런타임 전체를 스캔하여 PID,ParentPID,Name 출력
137
161
  // codex/claude/pwsh/uvx 누락 시 중간 프로세스가 alive 판정되어 고아 트리 전체가 보호됨
138
162
  // 예: WT(dead)→pwsh(alive,미스캔)→codex→cmd→node — pwsh에서 isPidAlive=true로 끊김
139
- writeFileSync(SCAN_SCRIPT_PATH, [
140
- "$ErrorActionPreference = 'SilentlyContinue'",
141
- "Get-CimInstance Win32_Process -Filter \"Name='node.exe' OR Name='bash.exe' OR Name='cmd.exe' OR Name='codex.exe' OR Name='claude.exe' OR Name='pwsh.exe' OR Name='uvx.exe'\" | ForEach-Object {",
142
- ' Write-Output "$($_.ProcessId),$($_.ParentProcessId),$($_.Name)"',
143
- "}",
144
- ].join("\n"), "utf8");
163
+ writeFileSync(
164
+ SCAN_SCRIPT_PATH,
165
+ [
166
+ "$ErrorActionPreference = 'SilentlyContinue'",
167
+ "Get-CimInstance Win32_Process -Filter \"Name='node.exe' OR Name='bash.exe' OR Name='cmd.exe' OR Name='codex.exe' OR Name='claude.exe' OR Name='pwsh.exe' OR Name='uvx.exe'\" | ForEach-Object {",
168
+ ' Write-Output "$($_.ProcessId),$($_.ParentProcessId),$($_.Name)"',
169
+ "}",
170
+ ].join("\n"),
171
+ "utf8",
172
+ );
145
173
  }
146
174
 
147
175
  if (needsUpdate) {
@@ -191,8 +219,12 @@ function hasLiveAncestorChain(pid, procMap, protectedPids) {
191
219
  // codex/claude도 포함: protectedPids + hasLiveAncestorChain이 활성 인스턴스를 보호하므로
192
220
  // 고아(부모 dead + 자식 dead)만 kill됨. pwsh.exe는 사용자 인터랙티브 쉘이므로 제외.
193
221
  const KILLABLE_NAMES = new Set([
194
- "node.exe", "bash.exe", "cmd.exe", "uvx.exe",
195
- "codex.exe", "claude.exe",
222
+ "node.exe",
223
+ "bash.exe",
224
+ "cmd.exe",
225
+ "uvx.exe",
226
+ "codex.exe",
227
+ "claude.exe",
196
228
  ]);
197
229
 
198
230
  /**
@@ -216,7 +248,13 @@ export function cleanupOrphanNodeProcesses() {
216
248
  // Hub PID 보호
217
249
  let hubPid = null;
218
250
  try {
219
- const hubPidPath = join(homedir(), ".claude", "cache", "tfx-hub", "hub.pid");
251
+ const hubPidPath = join(
252
+ homedir(),
253
+ ".claude",
254
+ "cache",
255
+ "tfx-hub",
256
+ "hub.pid",
257
+ );
220
258
  if (existsSync(hubPidPath)) {
221
259
  const hubInfo = JSON.parse(readFileSync(hubPidPath, "utf8"));
222
260
  hubPid = Number(hubInfo?.pid);
@@ -231,7 +269,12 @@ export function cleanupOrphanNodeProcesses() {
231
269
  try {
232
270
  const treeOutput = execSync(
233
271
  `powershell -NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File "${TREE_SCRIPT_PATH}" -StartPid ${myPid}`,
234
- { encoding: "utf8", timeout: 8000, stdio: ["pipe", "pipe", "pipe"], windowsHide: true },
272
+ {
273
+ encoding: "utf8",
274
+ timeout: 8000,
275
+ stdio: ["pipe", "pipe", "pipe"],
276
+ windowsHide: true,
277
+ },
235
278
  );
236
279
  for (const line of treeOutput.split(/\r?\n/)) {
237
280
  const pid = Number.parseInt(line.trim(), 10);
@@ -244,7 +287,12 @@ export function cleanupOrphanNodeProcesses() {
244
287
  try {
245
288
  const output = execSync(
246
289
  `powershell -NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File "${SCAN_SCRIPT_PATH}"`,
247
- { encoding: "utf8", timeout: 15000, stdio: ["pipe", "pipe", "pipe"], windowsHide: true },
290
+ {
291
+ encoding: "utf8",
292
+ timeout: 15000,
293
+ stdio: ["pipe", "pipe", "pipe"],
294
+ windowsHide: true,
295
+ },
248
296
  );
249
297
 
250
298
  for (const line of output.split(/\r?\n/)) {
@@ -276,7 +324,12 @@ export function cleanupOrphanNodeProcesses() {
276
324
  try {
277
325
  const countOutput = execSync(
278
326
  `powershell -NoProfile -WindowStyle Hidden -Command "(Get-Process node -ErrorAction SilentlyContinue).Count"`,
279
- { encoding: "utf8", timeout: 5000, stdio: ["pipe", "pipe", "pipe"], windowsHide: true },
327
+ {
328
+ encoding: "utf8",
329
+ timeout: 5000,
330
+ stdio: ["pipe", "pipe", "pipe"],
331
+ windowsHide: true,
332
+ },
280
333
  );
281
334
  remaining = Number.parseInt(countOutput.trim(), 10) || 0;
282
335
  } catch {}
@@ -296,7 +349,13 @@ function cleanupOrphansUnix() {
296
349
  const protectedPids = new Set();
297
350
  protectedPids.add(myPid);
298
351
  try {
299
- const hubPidPath = join(homedir(), ".claude", "cache", "tfx-hub", "hub.pid");
352
+ const hubPidPath = join(
353
+ homedir(),
354
+ ".claude",
355
+ "cache",
356
+ "tfx-hub",
357
+ "hub.pid",
358
+ );
300
359
  if (existsSync(hubPidPath)) {
301
360
  const hubPid = Number(JSON.parse(readFileSync(hubPidPath, "utf8"))?.pid);
302
361
  if (Number.isFinite(hubPid) && hubPid > 0) protectedPids.add(hubPid);
@@ -309,7 +368,9 @@ function cleanupOrphansUnix() {
309
368
  for (let i = 0; i < 10; i++) {
310
369
  protectedPids.add(current);
311
370
  const output = execSync(`ps -o ppid= -p ${current}`, {
312
- encoding: "utf8", timeout: 3000, stdio: ["pipe", "pipe", "pipe"],
371
+ encoding: "utf8",
372
+ timeout: 3000,
373
+ stdio: ["pipe", "pipe", "pipe"],
313
374
  });
314
375
  const ppid = Number.parseInt(output.trim(), 10);
315
376
  if (!Number.isFinite(ppid) || ppid <= 1) break;
@@ -321,7 +382,9 @@ function cleanupOrphansUnix() {
321
382
  const procMap = new Map();
322
383
  try {
323
384
  const output = execSync("ps -eo pid,ppid,comm", {
324
- encoding: "utf8", timeout: 10000, stdio: ["pipe", "pipe", "pipe"],
385
+ encoding: "utf8",
386
+ timeout: 10000,
387
+ stdio: ["pipe", "pipe", "pipe"],
325
388
  });
326
389
  for (const line of output.split("\n").slice(1)) {
327
390
  const parts = line.trim().split(/\s+/);
@@ -329,7 +392,11 @@ function cleanupOrphansUnix() {
329
392
  const pid = Number.parseInt(parts[0], 10);
330
393
  const ppid = Number.parseInt(parts[1], 10);
331
394
  const name = parts.slice(2).join(" ");
332
- if (Number.isFinite(pid) && pid > 0 && /^(node|bash|sh|python|codex|claude|uvx)/.test(name)) {
395
+ if (
396
+ Number.isFinite(pid) &&
397
+ pid > 0 &&
398
+ /^(node|bash|sh|python|codex|claude|uvx)/.test(name)
399
+ ) {
333
400
  procMap.set(pid, { ppid, name });
334
401
  }
335
402
  }
@@ -352,7 +419,9 @@ function cleanupOrphansUnix() {
352
419
  let remaining = 0;
353
420
  try {
354
421
  const output = execSync("ps -eo comm | grep -c '^node$'", {
355
- encoding: "utf8", timeout: 3000, stdio: ["pipe", "pipe", "pipe"],
422
+ encoding: "utf8",
423
+ timeout: 3000,
424
+ stdio: ["pipe", "pipe", "pipe"],
356
425
  });
357
426
  remaining = Number.parseInt(output.trim(), 10) || 0;
358
427
  } catch {}