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/scripts/test-lock.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
|
|
2
3
|
// scripts/test-lock.mjs — 테스트 러너 싱글톤 lockfile 가드
|
|
3
4
|
//
|
|
4
5
|
// npm test 동시 실행 방지. conductor 같은 child-spawn 테스트가
|
|
@@ -6,9 +7,9 @@
|
|
|
6
7
|
//
|
|
7
8
|
// 사용: node scripts/test-lock.mjs [-- ...node --test args]
|
|
8
9
|
|
|
9
|
-
import { writeFileSync, readFileSync, unlinkSync, mkdirSync } from "node:fs";
|
|
10
|
-
import { join } from "node:path";
|
|
11
10
|
import { spawn } from "node:child_process";
|
|
11
|
+
import { mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
12
|
+
import { join } from "node:path";
|
|
12
13
|
|
|
13
14
|
const LOCK_DIR = join(import.meta.dirname, "..", ".test-lock");
|
|
14
15
|
const LOCK_FILE = join(LOCK_DIR, "pid.lock");
|
|
@@ -31,8 +32,12 @@ function acquireLock() {
|
|
|
31
32
|
// Windows에서 process.kill(pid,0)이 git-bash PID를 못 잡음.
|
|
32
33
|
// 단순하게: lockfile이 있고 STALE_THRESHOLD 이내면 거부.
|
|
33
34
|
if (age >= 0 && age < STALE_THRESHOLD_MS) {
|
|
34
|
-
console.error(
|
|
35
|
-
|
|
35
|
+
console.error(
|
|
36
|
+
`\x1b[31m✗ 테스트 이미 실행 중 (PID ${existing.pid}, ${Math.round(age / 1000)}초 전 시작)\x1b[0m`,
|
|
37
|
+
);
|
|
38
|
+
console.error(
|
|
39
|
+
` 동시 실행은 WT 메모리 폭발을 유발합니다. 기다리거나 PID를 kill하세요.`,
|
|
40
|
+
);
|
|
36
41
|
console.error(` 강제 해제: rm ${LOCK_FILE}`);
|
|
37
42
|
process.exit(1);
|
|
38
43
|
}
|
|
@@ -46,7 +51,9 @@ function releaseLock() {
|
|
|
46
51
|
try {
|
|
47
52
|
const lock = readLock();
|
|
48
53
|
if (lock && lock.pid === process.pid) unlinkSync(LOCK_FILE);
|
|
49
|
-
} catch {
|
|
54
|
+
} catch {
|
|
55
|
+
/* ignore */
|
|
56
|
+
}
|
|
50
57
|
}
|
|
51
58
|
|
|
52
59
|
// ── main ──
|
|
@@ -55,8 +62,14 @@ acquireLock();
|
|
|
55
62
|
|
|
56
63
|
// cleanup on exit
|
|
57
64
|
process.on("exit", releaseLock);
|
|
58
|
-
process.on("SIGINT", () => {
|
|
59
|
-
|
|
65
|
+
process.on("SIGINT", () => {
|
|
66
|
+
releaseLock();
|
|
67
|
+
process.exit(130);
|
|
68
|
+
});
|
|
69
|
+
process.on("SIGTERM", () => {
|
|
70
|
+
releaseLock();
|
|
71
|
+
process.exit(143);
|
|
72
|
+
});
|
|
60
73
|
|
|
61
74
|
// forward args after -- to node --test
|
|
62
75
|
const args = process.argv.slice(2);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import test from "node:test";
|
|
3
2
|
import assert from "node:assert/strict";
|
|
4
3
|
import { spawnSync } from "node:child_process";
|
|
5
4
|
import { dirname, resolve } from "node:path";
|
|
5
|
+
import test from "node:test";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
|
|
8
8
|
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
|
|
@@ -14,13 +14,13 @@ function runBash(command) {
|
|
|
14
14
|
encoding: "utf8",
|
|
15
15
|
env: {
|
|
16
16
|
...process.env,
|
|
17
|
-
TFX_TEAM_NAME:
|
|
18
|
-
TFX_TEAM_TASK_ID:
|
|
19
|
-
TFX_TEAM_AGENT_NAME:
|
|
20
|
-
TFX_TEAM_LEAD_NAME:
|
|
21
|
-
TFX_HUB_URL:
|
|
22
|
-
TMUX:
|
|
23
|
-
}
|
|
17
|
+
TFX_TEAM_NAME: "",
|
|
18
|
+
TFX_TEAM_TASK_ID: "",
|
|
19
|
+
TFX_TEAM_AGENT_NAME: "",
|
|
20
|
+
TFX_TEAM_LEAD_NAME: "",
|
|
21
|
+
TFX_HUB_URL: "",
|
|
22
|
+
TMUX: "",
|
|
23
|
+
},
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -30,7 +30,7 @@ function out(result) {
|
|
|
30
30
|
|
|
31
31
|
test("gemini 모드에서는 no-claude-native 강제 치환이 적용되지 않는다", () => {
|
|
32
32
|
const result = runBash(
|
|
33
|
-
"TFX_CLI_MODE=gemini TFX_NO_CLAUDE_NATIVE=1 bash scripts/tfx-route.sh explore 'test-case'"
|
|
33
|
+
"TFX_CLI_MODE=gemini TFX_NO_CLAUDE_NATIVE=1 bash scripts/tfx-route.sh explore 'test-case'",
|
|
34
34
|
);
|
|
35
35
|
|
|
36
36
|
assert.equal(result.status, 0, out(result));
|
|
@@ -39,17 +39,21 @@ test("gemini 모드에서는 no-claude-native 강제 치환이 적용되지 않
|
|
|
39
39
|
|
|
40
40
|
test("auto 모드 + no-claude-native=1이면 explore가 codex로 치환된다", () => {
|
|
41
41
|
const result = runBash(
|
|
42
|
-
"TFX_CLI_MODE=auto TFX_NO_CLAUDE_NATIVE=1 CODEX_BIN=true bash scripts/tfx-route.sh explore 'test-case' minimal 5"
|
|
42
|
+
"TFX_CLI_MODE=auto TFX_NO_CLAUDE_NATIVE=1 CODEX_BIN=true bash scripts/tfx-route.sh explore 'test-case' minimal 5",
|
|
43
43
|
);
|
|
44
44
|
|
|
45
45
|
assert.equal(result.status, 0, out(result));
|
|
46
|
-
assert.match(
|
|
46
|
+
assert.match(
|
|
47
|
+
out(result),
|
|
48
|
+
/TFX_NO_CLAUDE_NATIVE=1: explore -> codex/,
|
|
49
|
+
out(result),
|
|
50
|
+
);
|
|
47
51
|
assert.match(out(result), /type=codex|cli:\\s*codex/i, out(result));
|
|
48
52
|
});
|
|
49
53
|
|
|
50
54
|
test("TFX_NO_CLAUDE_NATIVE는 0/1 값만 허용한다", () => {
|
|
51
55
|
const result = runBash(
|
|
52
|
-
"TFX_NO_CLAUDE_NATIVE=2 bash scripts/tfx-route.sh explore 'test-case'"
|
|
56
|
+
"TFX_NO_CLAUDE_NATIVE=2 bash scripts/tfx-route.sh explore 'test-case'",
|
|
53
57
|
);
|
|
54
58
|
|
|
55
59
|
assert.notEqual(result.status, 0, out(result));
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
// node tfx-batch-stats.mjs agent <name> 특정 에이전트 통계
|
|
12
12
|
|
|
13
13
|
import { readFileSync } from "node:fs";
|
|
14
|
-
import { join } from "node:path";
|
|
15
14
|
import { homedir } from "node:os";
|
|
15
|
+
import { join } from "node:path";
|
|
16
16
|
|
|
17
17
|
const CACHE_DIR = join(homedir(), ".claude", "cache");
|
|
18
18
|
const EVENTS_FILE = join(CACHE_DIR, "batch-events.jsonl");
|
|
@@ -21,18 +21,30 @@ const EVENTS_FILE = join(CACHE_DIR, "batch-events.jsonl");
|
|
|
21
21
|
const AIMD_INITIAL = 3;
|
|
22
22
|
const AIMD_MIN = 1;
|
|
23
23
|
const AIMD_MAX = 10;
|
|
24
|
-
const AIMD_INC = 1;
|
|
25
|
-
const AIMD_DEC = 0.5;
|
|
24
|
+
const AIMD_INC = 1; // 성공 시 +1
|
|
25
|
+
const AIMD_DEC = 0.5; // 실패 시 ×0.5
|
|
26
26
|
const WINDOW_MS = 30 * 60 * 1000; // 30분 윈도우
|
|
27
27
|
|
|
28
28
|
// ── 이벤트 읽기 ──
|
|
29
29
|
export function readBatchEvents(opts = {}) {
|
|
30
30
|
const { sinceMs = 0, agent = null } = opts;
|
|
31
31
|
try {
|
|
32
|
-
const lines = readFileSync(EVENTS_FILE, "utf-8")
|
|
32
|
+
const lines = readFileSync(EVENTS_FILE, "utf-8")
|
|
33
|
+
.trim()
|
|
34
|
+
.split("\n")
|
|
35
|
+
.filter(Boolean);
|
|
33
36
|
return lines
|
|
34
|
-
.map((l) => {
|
|
35
|
-
|
|
37
|
+
.map((l) => {
|
|
38
|
+
try {
|
|
39
|
+
return JSON.parse(l);
|
|
40
|
+
} catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
.filter(
|
|
45
|
+
(e) =>
|
|
46
|
+
e && (!sinceMs || e.ts >= sinceMs) && (!agent || e.agent === agent),
|
|
47
|
+
);
|
|
36
48
|
} catch {
|
|
37
49
|
return [];
|
|
38
50
|
}
|
|
@@ -44,9 +56,11 @@ export function getAgentStats(opts = {}) {
|
|
|
44
56
|
const stats = {};
|
|
45
57
|
|
|
46
58
|
for (const ev of events) {
|
|
47
|
-
if (!stats[ev.agent])
|
|
59
|
+
if (!stats[ev.agent])
|
|
60
|
+
stats[ev.agent] = { success: 0, fail: 0, timeout: 0, total: 0 };
|
|
48
61
|
const s = stats[ev.agent];
|
|
49
|
-
if (ev.result === "success" || ev.result === "success_with_warnings")
|
|
62
|
+
if (ev.result === "success" || ev.result === "success_with_warnings")
|
|
63
|
+
s.success++;
|
|
50
64
|
else if (ev.result === "timeout") s.timeout++;
|
|
51
65
|
else s.fail++;
|
|
52
66
|
s.total++;
|
|
@@ -85,11 +99,18 @@ if (scriptName.endsWith("tfx-batch-stats.mjs")) {
|
|
|
85
99
|
const sinceMs = recent ? Date.now() - WINDOW_MS : 0;
|
|
86
100
|
|
|
87
101
|
if (cmd === "batch") {
|
|
88
|
-
console.log(
|
|
102
|
+
console.log(
|
|
103
|
+
JSON.stringify({ batchSize: getAimdBatchSize(), window: "30m" }),
|
|
104
|
+
);
|
|
89
105
|
} else if (cmd === "agent") {
|
|
90
106
|
const name = process.argv[3];
|
|
91
|
-
if (!name) {
|
|
92
|
-
|
|
107
|
+
if (!name) {
|
|
108
|
+
console.error("에이전트명 필수: node tfx-batch-stats.mjs agent executor");
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
console.log(
|
|
112
|
+
JSON.stringify(getAgentStats({ sinceMs, agent: name }), null, 2),
|
|
113
|
+
);
|
|
93
114
|
} else {
|
|
94
115
|
console.log(JSON.stringify(getAgentStats({ sinceMs }), null, 2));
|
|
95
116
|
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* 자동 만료: 30분
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
13
13
|
import { tmpdir } from "node:os";
|
|
14
14
|
import { join } from "node:path";
|
|
15
15
|
|
|
@@ -42,15 +42,22 @@ async function main() {
|
|
|
42
42
|
|
|
43
43
|
// 모든 tfx CLI 라우팅 스킬에 gate 적용
|
|
44
44
|
const TFX_ROUTING_SKILLS = new Set([
|
|
45
|
-
"tfx-multi",
|
|
46
|
-
"tfx-
|
|
45
|
+
"tfx-multi",
|
|
46
|
+
"tfx-team",
|
|
47
|
+
"tfx-auto",
|
|
48
|
+
"tfx-auto-codex",
|
|
49
|
+
"tfx-codex",
|
|
50
|
+
"tfx-gemini",
|
|
51
|
+
"tfx-autoresearch",
|
|
47
52
|
]);
|
|
48
53
|
|
|
49
54
|
if (TFX_ROUTING_SKILLS.has(skill)) {
|
|
50
55
|
// 활성화: 상태 파일 생성/갱신
|
|
56
|
+
// ppid = Claude Code 세션 PID (훅은 Claude Code의 자식 프로세스)
|
|
51
57
|
const state = {
|
|
52
58
|
active: true,
|
|
53
59
|
activatedAt: Date.now(),
|
|
60
|
+
ownerPid: process.ppid,
|
|
54
61
|
dispatched: false,
|
|
55
62
|
nativeWorkCalls: 0,
|
|
56
63
|
nativeWorkCallsSinceDispatch: 0,
|
|
@@ -64,7 +71,7 @@ async function main() {
|
|
|
64
71
|
hookEventName: "PreToolUse",
|
|
65
72
|
additionalContext:
|
|
66
73
|
"[tfx-multi] gate 활성화됨. CLI 작업은 headless로 dispatch 필수:\n" +
|
|
67
|
-
|
|
74
|
+
"Bash(\"tfx multi --teammate-mode headless --auto-attach --dashboard --assign 'codex:프롬프트:역할' --timeout 600\")",
|
|
68
75
|
},
|
|
69
76
|
}),
|
|
70
77
|
);
|
|
@@ -13,9 +13,17 @@
|
|
|
13
13
|
// 6. CLI 이슈 자동 수집
|
|
14
14
|
// 7. 구조화된 결과 출력 (=== TFX-ROUTE RESULT ===)
|
|
15
15
|
|
|
16
|
-
import {
|
|
17
|
-
|
|
16
|
+
import {
|
|
17
|
+
appendFileSync,
|
|
18
|
+
existsSync,
|
|
19
|
+
mkdirSync,
|
|
20
|
+
readdirSync,
|
|
21
|
+
readFileSync,
|
|
22
|
+
statSync,
|
|
23
|
+
writeFileSync,
|
|
24
|
+
} from "fs";
|
|
18
25
|
import { homedir } from "os";
|
|
26
|
+
import { join } from "path";
|
|
19
27
|
|
|
20
28
|
const HOME = homedir();
|
|
21
29
|
const CACHE_DIR = join(HOME, ".claude", "cache");
|
|
@@ -171,7 +179,10 @@ function extractCodexSessionId(rawOutput, stderrContent = "") {
|
|
|
171
179
|
|
|
172
180
|
function appendCodexResumeHint(output, rawOutput, stderrContent = "") {
|
|
173
181
|
const normalizedOutput = String(output || "").trim();
|
|
174
|
-
if (
|
|
182
|
+
if (
|
|
183
|
+
/Codex session ID:/i.test(normalizedOutput) ||
|
|
184
|
+
/codex resume\s+/i.test(normalizedOutput)
|
|
185
|
+
) {
|
|
175
186
|
return normalizedOutput;
|
|
176
187
|
}
|
|
177
188
|
|
|
@@ -184,7 +195,11 @@ function appendCodexResumeHint(output, rawOutput, stderrContent = "") {
|
|
|
184
195
|
return normalizedOutput ? `${normalizedOutput}\n\n${hint}` : hint;
|
|
185
196
|
}
|
|
186
197
|
|
|
187
|
-
function prepareCliOutput(
|
|
198
|
+
function prepareCliOutput(
|
|
199
|
+
rawOutput,
|
|
200
|
+
cliType,
|
|
201
|
+
{ cleanTui = true, stderrContent = "" } = {},
|
|
202
|
+
) {
|
|
188
203
|
let prepared = cliType === "codex" ? filterCodexOutput(rawOutput) : rawOutput;
|
|
189
204
|
if (cleanTui && process.env.TFX_CLEAN_TUI !== "0") {
|
|
190
205
|
prepared = cleanTuiArtifacts(prepared, cliType);
|
|
@@ -214,7 +229,9 @@ function cleanTuiArtifacts(output, cliType) {
|
|
|
214
229
|
.replace(/^\s*codex\s*$/gm, "")
|
|
215
230
|
.replace(/^[^\S\n]*[›❯]\s*Applied.*$/gm, "");
|
|
216
231
|
} else if (normalizedCliType.startsWith("gemini")) {
|
|
217
|
-
cleaned = cleaned
|
|
232
|
+
cleaned = cleaned
|
|
233
|
+
.replace(/^[^\S\n]*[╭╮╰╯│─═].*$/gm, "")
|
|
234
|
+
.replace(/^[^\S\n]*>\s*$/gm, "");
|
|
218
235
|
} else if (normalizedCliType.startsWith("claude")) {
|
|
219
236
|
cleaned = cleaned.replace(/^[^\S\n]*[━─]{5,}.*$/gm, "");
|
|
220
237
|
}
|
|
@@ -285,7 +302,10 @@ function recordBatchEvent(result, agent) {
|
|
|
285
302
|
const eventsFile = join(CACHE_DIR, "batch-events.jsonl");
|
|
286
303
|
try {
|
|
287
304
|
mkdirSync(CACHE_DIR, { recursive: true });
|
|
288
|
-
appendFileSync(
|
|
305
|
+
appendFileSync(
|
|
306
|
+
eventsFile,
|
|
307
|
+
JSON.stringify({ ts: Date.now(), agent, result }) + "\n",
|
|
308
|
+
);
|
|
289
309
|
|
|
290
310
|
// 자동 회전: 200줄 초과 시 최근 100줄 유지
|
|
291
311
|
const content = readFileSync(eventsFile, "utf-8").trim();
|
|
@@ -301,12 +321,42 @@ function trackCliIssue(cliType, agent, stderrText, exitCode) {
|
|
|
301
321
|
if (!stderrText && exitCode === 0) return;
|
|
302
322
|
|
|
303
323
|
const patterns = [
|
|
304
|
-
{
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
324
|
+
{
|
|
325
|
+
regex: /sandbox image.*missing/i,
|
|
326
|
+
pattern: "sandbox_missing",
|
|
327
|
+
msg: "Docker sandbox image not found",
|
|
328
|
+
severity: "warn",
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
regex: /rate.limit|429|too many requests/i,
|
|
332
|
+
pattern: "rate_limit",
|
|
333
|
+
msg: "API rate limit exceeded",
|
|
334
|
+
severity: "warn",
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
regex: /ECONNREFUSED|ENOTFOUND|network/i,
|
|
338
|
+
pattern: "network_error",
|
|
339
|
+
msg: "Network connection failed",
|
|
340
|
+
severity: "error",
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
regex: /deprecated/i,
|
|
344
|
+
pattern: "deprecated_flag",
|
|
345
|
+
msg: "Deprecated flag/feature detected",
|
|
346
|
+
severity: "warn",
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
regex: /API_KEY.*not.set|auth.*fail|unauthorized|401/i,
|
|
350
|
+
pattern: "auth_error",
|
|
351
|
+
msg: "Authentication failed",
|
|
352
|
+
severity: "error",
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
regex: /ENOMEM|out of memory|heap/i,
|
|
356
|
+
pattern: "oom",
|
|
357
|
+
msg: "Out of memory",
|
|
358
|
+
severity: "error",
|
|
359
|
+
},
|
|
310
360
|
];
|
|
311
361
|
|
|
312
362
|
let matched = null;
|
|
@@ -318,7 +368,11 @@ function trackCliIssue(cliType, agent, stderrText, exitCode) {
|
|
|
318
368
|
}
|
|
319
369
|
|
|
320
370
|
if (!matched && exitCode !== 0 && exitCode !== 124) {
|
|
321
|
-
matched = {
|
|
371
|
+
matched = {
|
|
372
|
+
pattern: "unknown_error",
|
|
373
|
+
msg: `Exit code ${exitCode}`,
|
|
374
|
+
severity: "warn",
|
|
375
|
+
};
|
|
322
376
|
}
|
|
323
377
|
|
|
324
378
|
if (!matched) return;
|
|
@@ -329,21 +383,30 @@ function trackCliIssue(cliType, agent, stderrText, exitCode) {
|
|
|
329
383
|
|
|
330
384
|
// 중복 방지: 같은 패턴+cli가 최근 5분 내 기록됐으면 건너뜀
|
|
331
385
|
if (existsSync(issuesFile)) {
|
|
332
|
-
const lines = readFileSync(issuesFile, "utf-8")
|
|
386
|
+
const lines = readFileSync(issuesFile, "utf-8")
|
|
387
|
+
.trim()
|
|
388
|
+
.split("\n")
|
|
389
|
+
.slice(-5);
|
|
333
390
|
const now = Date.now();
|
|
334
391
|
for (const line of lines) {
|
|
335
392
|
try {
|
|
336
393
|
const entry = JSON.parse(line);
|
|
337
|
-
if (
|
|
394
|
+
if (
|
|
395
|
+
entry.pattern === matched.pattern &&
|
|
396
|
+
entry.cli === cliType &&
|
|
397
|
+
now - entry.ts < 300000
|
|
398
|
+
)
|
|
399
|
+
return;
|
|
338
400
|
} catch {}
|
|
339
401
|
}
|
|
340
402
|
}
|
|
341
403
|
|
|
342
404
|
const snippet = stderrText.substring(0, 200).replace(/\n/g, " ");
|
|
343
405
|
|
|
344
|
-
const retryCount =
|
|
345
|
-
|
|
346
|
-
|
|
406
|
+
const retryCount =
|
|
407
|
+
matched.pattern === "rate_limit" && cliType === "gemini"
|
|
408
|
+
? parseInt(process.env.TFX_GEMINI_429_RETRIES || "0", 10)
|
|
409
|
+
: undefined;
|
|
347
410
|
|
|
348
411
|
const issueEntry = {
|
|
349
412
|
ts: Date.now(),
|
|
@@ -440,7 +503,8 @@ function main() {
|
|
|
440
503
|
if (exitCode === 0) accumulateTokens(cliType, tokens);
|
|
441
504
|
|
|
442
505
|
// 5. AIMD 배치 이벤트
|
|
443
|
-
const aimdResult =
|
|
506
|
+
const aimdResult =
|
|
507
|
+
exitCode === 0 ? "success" : exitCode === 124 ? "timeout" : "failed";
|
|
444
508
|
recordBatchEvent(aimdResult, agent);
|
|
445
509
|
|
|
446
510
|
// 6. CLI 이슈 추적
|
|
@@ -462,7 +526,9 @@ function main() {
|
|
|
462
526
|
if (exitCode === 0) {
|
|
463
527
|
if (stderrContent) {
|
|
464
528
|
console.log("status: success_with_warnings");
|
|
465
|
-
console.log(
|
|
529
|
+
console.log(
|
|
530
|
+
`warnings: ${stderrContent.split("\n").slice(0, 3).join(" ")}`,
|
|
531
|
+
);
|
|
466
532
|
} else {
|
|
467
533
|
console.log("status: success");
|
|
468
534
|
}
|
|
@@ -498,6 +564,7 @@ function main() {
|
|
|
498
564
|
}
|
|
499
565
|
|
|
500
566
|
import { fileURLToPath } from "url";
|
|
567
|
+
|
|
501
568
|
if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
502
569
|
main();
|
|
503
570
|
}
|