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.
- package/LICENSE +21 -21
- package/bin/tfx-doctor-tui.mjs +1 -1
- package/bin/tfx-doctor.mjs +6 -1
- package/bin/tfx-profile.mjs +1 -1
- package/bin/tfx-setup-tui.mjs +1 -1
- package/bin/tfx-setup.mjs +6 -1
- package/bin/triflux.mjs +2396 -1140
- package/hooks/agent-route-guard.mjs +12 -8
- package/hooks/cross-review-tracker.mjs +21 -8
- package/hooks/error-context.mjs +19 -7
- package/hooks/hook-adaptive-collector.mjs +18 -16
- package/hooks/hook-manager.mjs +93 -32
- package/hooks/hook-orchestrator.mjs +108 -24
- package/hooks/hook-registry.json +11 -0
- package/hooks/keyword-rules.json +6 -10
- package/hooks/lib/resolve-root.mjs +1 -1
- package/hooks/mcp-config-watcher.mjs +6 -2
- package/hooks/pipeline-stop.mjs +3 -6
- package/hooks/safety-guard.mjs +99 -28
- package/hooks/session-start-fast.mjs +143 -0
- package/hooks/subagent-verifier.mjs +5 -4
- package/hub/account-broker.mjs +256 -60
- package/hub/adaptive-diagnostic.mjs +75 -48
- package/hub/adaptive-inject.mjs +95 -57
- package/hub/adaptive-memory.mjs +156 -42
- package/hub/adaptive.mjs +60 -31
- package/hub/assign-callbacks.mjs +67 -30
- package/hub/bridge.mjs +0 -1
- package/hub/cli-adapter-base.mjs +200 -48
- package/hub/codex-adapter.mjs +76 -96
- package/hub/codex-compat.mjs +3 -3
- package/hub/codex-preflight.mjs +63 -37
- package/hub/delegator/contracts.mjs +19 -23
- package/hub/delegator/index.mjs +3 -3
- package/hub/delegator/service.mjs +88 -64
- package/hub/delegator/tool-definitions.mjs +5 -5
- package/hub/fullcycle.mjs +33 -17
- package/hub/gemini-adapter.mjs +69 -94
- package/hub/hitl.mjs +89 -30
- package/hub/intent.mjs +161 -38
- package/hub/lib/cache-guard.mjs +43 -17
- package/hub/lib/mcp-response-cache.mjs +66 -32
- package/hub/lib/memory-store.mjs +285 -111
- package/hub/lib/path-utils.mjs +35 -37
- package/hub/lib/process-utils.mjs +106 -37
- package/hub/lib/spawn-trace.mjs +527 -0
- package/hub/lib/ssh-command.mjs +34 -4
- package/hub/lib/ssh-retry.mjs +5 -1
- package/hub/lib/uuidv7.mjs +4 -3
- package/hub/memory-doctor.mjs +266 -106
- package/hub/middleware/request-logger.mjs +61 -34
- package/hub/paths.mjs +9 -9
- package/hub/pipeline/gates/confidence.mjs +34 -15
- package/hub/pipeline/gates/consensus.mjs +27 -15
- package/hub/pipeline/gates/index.mjs +7 -3
- package/hub/pipeline/gates/selfcheck.mjs +57 -19
- package/hub/pipeline/index.mjs +77 -42
- package/hub/pipeline/state.mjs +10 -10
- package/hub/pipeline/transitions.mjs +40 -23
- package/hub/platform.mjs +57 -48
- package/hub/promote-penalties.mjs +25 -7
- package/hub/quality/deslop.mjs +70 -49
- package/hub/research.mjs +32 -25
- package/hub/router.mjs +240 -107
- package/hub/routing/complexity.mjs +132 -29
- package/hub/routing/index.mjs +17 -12
- package/hub/routing/q-learning.mjs +76 -28
- package/hub/server.mjs +4 -4
- package/hub/session-fingerprint.mjs +126 -60
- package/hub/state.mjs +84 -43
- package/hub/store-adapter.mjs +59 -26
- package/hub/store.mjs +356 -153
- package/hub/team/agent-map.json +22 -7
- package/hub/team/ansi.mjs +186 -122
- package/hub/team/backend.mjs +28 -10
- package/hub/team/cli/commands/attach.mjs +29 -9
- package/hub/team/cli/commands/control.mjs +29 -8
- package/hub/team/cli/commands/debug.mjs +32 -11
- package/hub/team/cli/commands/focus.mjs +38 -11
- package/hub/team/cli/commands/interrupt.mjs +18 -6
- package/hub/team/cli/commands/kill.mjs +16 -5
- package/hub/team/cli/commands/list.mjs +11 -4
- package/hub/team/cli/commands/send.mjs +19 -6
- package/hub/team/cli/commands/start/index.mjs +154 -31
- package/hub/team/cli/commands/start/parse-args.mjs +38 -11
- package/hub/team/cli/commands/start/start-headless.mjs +112 -36
- package/hub/team/cli/commands/start/start-in-process.mjs +12 -2
- package/hub/team/cli/commands/start/start-mux.mjs +70 -21
- package/hub/team/cli/commands/start/start-wt.mjs +29 -12
- package/hub/team/cli/commands/status.mjs +43 -14
- package/hub/team/cli/commands/stop.mjs +11 -4
- package/hub/team/cli/commands/task.mjs +8 -3
- package/hub/team/cli/commands/tasks.mjs +1 -1
- package/hub/team/cli/index.mjs +2 -2
- package/hub/team/cli/manifest.mjs +38 -8
- package/hub/team/cli/render.mjs +30 -8
- package/hub/team/cli/services/attach-fallback.mjs +31 -11
- package/hub/team/cli/services/hub-client.mjs +42 -14
- package/hub/team/cli/services/member-selector.mjs +11 -4
- package/hub/team/cli/services/native-control.mjs +48 -21
- package/hub/team/cli/services/runtime-mode.mjs +2 -1
- package/hub/team/cli/services/state-store.mjs +25 -8
- package/hub/team/cli/services/task-model.mjs +16 -6
- package/hub/team/conductor-mesh-bridge.mjs +24 -23
- package/hub/team/conductor.mjs +8 -4
- package/hub/team/dashboard-anchor.mjs +4 -5
- package/hub/team/dashboard-layout.mjs +3 -1
- package/hub/team/dashboard-open.mjs +41 -21
- package/hub/team/dashboard.mjs +76 -28
- package/hub/team/event-log.mjs +18 -10
- package/hub/team/handoff.mjs +31 -15
- package/hub/team/headless.mjs +2 -1
- package/hub/team/health-probe.mjs +69 -54
- package/hub/team/launcher-template.mjs +16 -13
- package/hub/team/native-supervisor.mjs +65 -21
- package/hub/team/native.mjs +74 -35
- package/hub/team/nativeProxy.mjs +184 -113
- package/hub/team/notify.mjs +119 -76
- package/hub/team/orchestrator.mjs +9 -4
- package/hub/team/pane.mjs +12 -7
- package/hub/team/process-cleanup.mjs +25 -16
- package/hub/team/psmux.mjs +491 -201
- package/hub/team/remote-probe.mjs +68 -52
- package/hub/team/remote-session.mjs +117 -59
- package/hub/team/remote-watcher.mjs +61 -33
- package/hub/team/routing.mjs +51 -25
- package/hub/team/runtime-strategy.mjs +3 -1
- package/hub/team/session.mjs +98 -34
- package/hub/team/staleState.mjs +72 -30
- package/hub/team/swarm-locks.mjs +15 -13
- package/hub/team/swarm-planner.mjs +32 -21
- package/hub/team/swarm-reconciler.mjs +48 -23
- package/hub/team/tui-lite.mjs +266 -68
- package/hub/team/tui-remote-adapter.mjs +14 -10
- package/hub/team/tui-viewer.mjs +99 -43
- package/hub/team/tui.mjs +708 -271
- package/hub/team/worktree-lifecycle.mjs +152 -58
- package/hub/team/wt-manager.mjs +24 -14
- package/hub/token-mode.mjs +71 -71
- package/hub/tray.mjs +66 -23
- package/hub/workers/claude-worker.mjs +162 -118
- package/hub/workers/codex-mcp.mjs +192 -141
- package/hub/workers/delegator-mcp.mjs +507 -333
- package/hub/workers/factory.mjs +8 -8
- package/hub/workers/gemini-worker.mjs +115 -84
- package/hub/workers/interface.mjs +6 -1
- package/hub/workers/worker-utils.mjs +21 -14
- package/hud/colors.mjs +27 -9
- package/hud/constants.mjs +162 -26
- package/hud/context-monitor.mjs +82 -41
- package/hud/hud-qos-status.mjs +129 -49
- package/hud/mission-board.mjs +6 -3
- package/hud/providers/claude.mjs +226 -115
- package/hud/providers/codex.mjs +62 -22
- package/hud/providers/gemini.mjs +168 -56
- package/hud/renderers.mjs +384 -119
- package/hud/terminal.mjs +101 -31
- package/hud/utils.mjs +78 -38
- package/mesh/index.mjs +11 -5
- package/mesh/mesh-budget.mjs +18 -9
- package/mesh/mesh-heartbeat.mjs +1 -1
- package/mesh/mesh-queue.mjs +3 -5
- package/mesh/mesh-router.mjs +5 -4
- package/package.json +2 -1
- package/scripts/__tests__/gen-skill-docs.test.mjs +36 -7
- package/scripts/__tests__/keyword-detector.test.mjs +77 -28
- package/scripts/__tests__/mcp-guard-engine.test.mjs +58 -20
- package/scripts/__tests__/remote-spawn-transfer.test.mjs +30 -19
- package/scripts/__tests__/remote-spawn.test.mjs +10 -4
- package/scripts/__tests__/session-start-fast.test.mjs +36 -0
- package/scripts/__tests__/skill-template.test.mjs +98 -50
- package/scripts/__tests__/smoke.test.mjs +1 -1
- package/scripts/__tests__/spawn-trace.test.mjs +102 -0
- package/scripts/__tests__/tfx-doctor-diagnose.test.mjs +48 -0
- package/scripts/cache-doctor.mjs +11 -4
- package/scripts/cache-warmup.mjs +96 -37
- package/scripts/claudemd-sync.mjs +27 -17
- package/scripts/codex-gateway-preflight.mjs +52 -37
- package/scripts/codex-mcp-gateway-sync.mjs +59 -39
- package/scripts/completions/tfx.bash +47 -47
- package/scripts/completions/tfx.fish +44 -44
- package/scripts/completions/tfx.zsh +83 -83
- package/scripts/config-audit.mjs +232 -0
- package/scripts/convert-to-tmpl.mjs +54 -0
- package/scripts/cross-review-gate.mjs +35 -12
- package/scripts/cross-review-tracker.mjs +21 -8
- package/scripts/demo.mjs +35 -17
- package/scripts/doctor-diagnose.mjs +284 -0
- package/scripts/gen-skill-docs.mjs +7 -2
- package/scripts/gen-skill-manifest.mjs +2 -1
- package/scripts/headless-guard.mjs +86 -48
- package/scripts/hub-ensure.mjs +45 -26
- package/scripts/keyword-detector.mjs +41 -20
- package/scripts/keyword-rules-expander.mjs +47 -30
- package/scripts/lib/claudemd-scanner.mjs +6 -1
- package/scripts/lib/context.mjs +3 -3
- package/scripts/lib/cross-review-utils.mjs +6 -3
- package/scripts/lib/env-probe.mjs +47 -28
- package/scripts/lib/gemini-profiles.mjs +44 -10
- package/scripts/lib/handoff.mjs +33 -17
- package/scripts/lib/hook-utils.mjs +8 -6
- package/scripts/lib/keyword-rules.mjs +43 -19
- package/scripts/lib/logger.mjs +24 -24
- package/scripts/lib/mcp-filter.mjs +377 -239
- package/scripts/lib/mcp-guard-engine.mjs +194 -79
- package/scripts/lib/mcp-manifest.mjs +23 -13
- package/scripts/lib/mcp-server-catalog.mjs +300 -63
- package/scripts/lib/psmux-info.mjs +11 -6
- package/scripts/lib/remote-spawn-transfer.mjs +44 -14
- package/scripts/lib/skill-template.mjs +30 -7
- package/scripts/mcp-check.mjs +58 -39
- package/scripts/mcp-gateway-config.mjs +83 -39
- package/scripts/mcp-gateway-ensure.mjs +43 -35
- package/scripts/mcp-gateway-integration-test.mjs +70 -58
- package/scripts/mcp-gateway-start.mjs +126 -60
- package/scripts/mcp-gateway-verify.mjs +24 -22
- package/scripts/mcp-safety-guard.mjs +44 -11
- package/scripts/notion-read.mjs +199 -84
- package/scripts/pack.mjs +94 -89
- package/scripts/preflight-cache.mjs +27 -10
- package/scripts/preinstall.mjs +42 -13
- package/scripts/remote-spawn.mjs +309 -94
- package/scripts/run.cjs +8 -5
- package/scripts/session-spawn-helper.mjs +130 -39
- package/scripts/session-stale-cleanup.mjs +123 -0
- package/scripts/setup.mjs +941 -492
- package/scripts/test-lock.mjs +20 -7
- package/scripts/test-tfx-route-no-claude-native.mjs +16 -12
- package/scripts/tfx-batch-stats.mjs +32 -11
- package/scripts/tfx-gate-activate.mjs +11 -4
- package/scripts/tfx-route-post.mjs +87 -20
- package/scripts/tfx-route-worker.mjs +57 -51
- package/scripts/tfx-route.sh +41 -124
- package/scripts/tmp-cleanup.mjs +21 -7
- package/scripts/token-snapshot.mjs +204 -85
- package/skills/.omc/state/agent-replay-8f0e10a9-9693-4410-96f5-a6b07e8ed995.jsonl +1 -0
- package/skills/.omc/state/idle-notif-cooldown.json +3 -0
- package/skills/.omc/state/last-tool-error.json +7 -0
- package/skills/.omc/state/subagent-tracking.json +7 -0
- package/skills/_templates/base.md +1 -6
- package/skills/merge-worktree/SKILL.md.tmpl +144 -0
- package/skills/shared/telemetry-segment.md +6 -0
- package/skills/star-prompt/SKILL.md.tmpl +222 -0
- package/skills/tfx-analysis/SKILL.md.tmpl +107 -0
- package/skills/tfx-analysis/skill.json +1 -6
- package/skills/tfx-auto/SKILL.md +1 -0
- package/skills/tfx-auto-codex/SKILL.md.tmpl +106 -0
- package/skills/tfx-auto-codex/skill.json +1 -3
- package/skills/tfx-autopilot/SKILL.md.tmpl +116 -0
- package/skills/tfx-autopilot/skill.json +1 -5
- package/skills/tfx-autoresearch/SKILL.md.tmpl +136 -0
- package/skills/tfx-autoroute/SKILL.md.tmpl +189 -0
- package/skills/tfx-autoroute/skill.json +1 -7
- package/skills/tfx-codex/SKILL.md +1 -0
- package/skills/tfx-codex/skill.json +1 -3
- package/skills/tfx-codex-swarm/SKILL.md.tmpl +16 -0
- package/skills/tfx-codex-swarm/evals/evals.json +1 -1
- package/skills/tfx-codex-swarm/skill.json +1 -4
- package/skills/tfx-codex-swarm-workspace/iteration-1/benchmark.json +54 -12
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/grading.json +35 -7
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/grading.json +35 -7
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/grading.json +25 -5
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/grading.json +25 -5
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/grading.json +20 -4
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/grading.json +16 -4
- package/skills/tfx-consensus/SKILL.md.tmpl +146 -0
- package/skills/tfx-debate/SKILL.md.tmpl +192 -0
- package/skills/tfx-debate/skill.json +1 -7
- package/skills/tfx-deep-analysis/SKILL.md.tmpl +228 -0
- package/skills/tfx-deep-analysis/skill.json +1 -5
- package/skills/tfx-deep-interview/SKILL.md.tmpl +203 -0
- package/skills/tfx-deep-plan/SKILL.md.tmpl +282 -0
- package/skills/tfx-deep-qa/SKILL.md.tmpl +165 -0
- package/skills/tfx-deep-qa/skill.json +1 -6
- package/skills/tfx-deep-research/SKILL.md.tmpl +217 -0
- package/skills/tfx-deep-review/SKILL.md.tmpl +179 -0
- package/skills/tfx-doctor/SKILL.md +21 -0
- package/skills/tfx-doctor/SKILL.md.tmpl +172 -0
- package/skills/tfx-doctor/skill.json +1 -3
- package/skills/tfx-find/SKILL.md +1 -0
- package/skills/tfx-forge/SKILL.md.tmpl +187 -0
- package/skills/tfx-fullcycle/SKILL.md.tmpl +286 -0
- package/skills/tfx-fullcycle/skill.json +1 -6
- package/skills/tfx-gemini/SKILL.md.tmpl +91 -0
- package/skills/tfx-gemini/skill.json +1 -3
- package/skills/tfx-hooks/SKILL.md.tmpl +216 -0
- package/skills/tfx-hooks/skill.json +1 -3
- package/skills/tfx-hub/SKILL.md.tmpl +212 -0
- package/skills/tfx-hub/skill.json +1 -3
- package/skills/tfx-index/SKILL.md +1 -0
- package/skills/tfx-index/skill.json +1 -6
- package/skills/tfx-interview/SKILL.md.tmpl +285 -0
- package/skills/tfx-multi/SKILL.md.tmpl +183 -0
- package/skills/tfx-multi/skill.json +1 -3
- package/skills/tfx-panel/SKILL.md.tmpl +189 -0
- package/skills/tfx-panel/skill.json +1 -7
- package/skills/tfx-persist/SKILL.md.tmpl +270 -0
- package/skills/tfx-persist/skill.json +1 -7
- package/skills/tfx-plan/SKILL.md +1 -0
- package/skills/tfx-plan/skill.json +1 -6
- package/skills/tfx-profile/SKILL.md.tmpl +239 -0
- package/skills/tfx-profile/skill.json +1 -3
- package/skills/tfx-prune/SKILL.md.tmpl +200 -0
- package/skills/tfx-prune/skill.json +1 -7
- package/skills/tfx-psmux-rules/SKILL.md.tmpl +326 -0
- package/skills/tfx-psmux-rules/skill.json +1 -4
- package/skills/tfx-qa/SKILL.md +1 -0
- package/skills/tfx-qa/skill.json +1 -6
- package/skills/tfx-ralph/SKILL.md.tmpl +28 -0
- package/skills/tfx-ralph/skill.json +1 -4
- package/skills/tfx-remote-setup/SKILL.md.tmpl +576 -0
- package/skills/tfx-remote-setup/skill.json +1 -3
- package/skills/tfx-remote-spawn/SKILL.md.tmpl +263 -0
- package/skills/tfx-remote-spawn/references/hosts.json +16 -0
- package/skills/tfx-remote-spawn/skill.json +1 -4
- package/skills/tfx-research/SKILL.md +1 -0
- package/skills/tfx-review/SKILL.md +1 -0
- package/skills/tfx-review/skill.json +1 -6
- package/skills/tfx-setup/SKILL.md.tmpl +504 -0
- package/skills/tfx-setup/skill.json +1 -3
- package/skills/tfx-swarm/SKILL.md +22 -0
- package/skills/tfx-swarm/SKILL.md.tmpl +218 -0
- package/tui/codex-profile.mjs +88 -33
- package/tui/core.mjs +45 -15
- package/tui/doctor.mjs +75 -28
- package/tui/gemini-profile.mjs +74 -29
- package/tui/monitor-data.mjs +8 -4
- package/tui/monitor.mjs +71 -27
- package/tui/setup.mjs +133 -42
package/hub/lib/path-utils.mjs
CHANGED
|
@@ -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 ===
|
|
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
|
|
83
|
+
case "git-bash":
|
|
86
84
|
return toPosixPath(p);
|
|
87
85
|
|
|
88
|
-
case
|
|
86
|
+
case "wsl": {
|
|
89
87
|
// Git Bash 경로를 먼저 Windows로 변환 후 WSL 형식으로
|
|
90
|
-
|
|
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(
|
|
99
|
-
return p.replace(/\\/g,
|
|
96
|
+
if (p.startsWith("/mnt/")) return p;
|
|
97
|
+
return p.replace(/\\/g, "/");
|
|
100
98
|
}
|
|
101
99
|
|
|
102
|
-
case
|
|
103
|
-
case
|
|
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
|
|
116
|
+
return "wsl";
|
|
119
117
|
}
|
|
120
118
|
|
|
121
119
|
// Windows 플랫폼
|
|
122
|
-
if (process.platform ===
|
|
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(
|
|
130
|
-
msystem.startsWith(
|
|
131
|
-
msystem.startsWith(
|
|
132
|
-
term ===
|
|
127
|
+
shell.includes("bash") ||
|
|
128
|
+
msystem.startsWith("MINGW") ||
|
|
129
|
+
msystem.startsWith("MSYS") ||
|
|
130
|
+
term === "xterm"
|
|
133
131
|
) {
|
|
134
|
-
return
|
|
132
|
+
return "git-bash";
|
|
135
133
|
}
|
|
136
134
|
|
|
137
135
|
// PowerShell 감지
|
|
138
136
|
if (process.env.PSModulePath || process.env.PSHOME) {
|
|
139
|
-
return
|
|
137
|
+
return "powershell";
|
|
140
138
|
}
|
|
141
139
|
|
|
142
|
-
return
|
|
140
|
+
return "cmd";
|
|
143
141
|
}
|
|
144
142
|
|
|
145
143
|
// 비-Windows
|
|
146
|
-
return
|
|
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(
|
|
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 {
|
|
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 ===
|
|
31
|
-
if (e?.code ===
|
|
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) {
|
|
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
|
-
{
|
|
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(
|
|
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 {
|
|
118
|
-
|
|
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(
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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(
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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",
|
|
195
|
-
"
|
|
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(
|
|
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
|
-
{
|
|
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
|
-
{
|
|
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
|
-
{
|
|
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(
|
|
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",
|
|
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",
|
|
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 (
|
|
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",
|
|
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 {}
|