@triflux/core 10.0.3 → 10.25.1
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/hooks/agent-route-guard.mjs +14 -10
- package/hooks/cross-review-tracker.mjs +21 -8
- package/hooks/error-context.mjs +47 -7
- package/hooks/hook-adaptive-collector.mjs +18 -16
- package/hooks/hook-manager.mjs +93 -32
- package/hooks/hook-orchestrator.mjs +279 -33
- package/hooks/hook-registry.json +104 -7
- package/hooks/hooks.json +49 -1
- package/hooks/keyword-rules.json +183 -23
- package/hooks/lib/resolve-root.mjs +1 -1
- package/hooks/mcp-config-watcher.mjs +6 -2
- package/hooks/permission-safe-allow.mjs +233 -0
- package/hooks/pipeline-stop.mjs +369 -22
- package/hooks/post-tool-tips.mjs +306 -0
- package/hooks/pre-compact-snapshot.mjs +181 -0
- package/hooks/safety-guard.mjs +308 -18
- package/hooks/session-end-cleanup.mjs +345 -0
- package/hooks/session-start-fast.mjs +214 -0
- package/hooks/subagent-tracker.mjs +267 -0
- package/hooks/subagent-verifier.mjs +9 -4
- package/hub/account-broker.mjs +1036 -66
- package/hub/adaptive-diagnostic.mjs +75 -48
- package/hub/adaptive-inject.mjs +96 -58
- package/hub/adaptive-memory.mjs +156 -42
- package/hub/adaptive.mjs +60 -31
- package/hub/assign-callbacks.mjs +163 -126
- package/hub/bridge.mjs +832 -291
- package/hub/cli-adapter-base.mjs +321 -97
- package/hub/codex-adapter.mjs +179 -93
- package/hub/codex-compat.mjs +4 -3
- package/hub/codex-preflight.mjs +113 -37
- package/hub/delegator/contracts.mjs +19 -23
- package/hub/delegator/index.mjs +14 -14
- package/hub/delegator/service.mjs +88 -64
- package/hub/delegator/tool-definitions.mjs +35 -35
- package/hub/dynamic-routing-engine.mjs +511 -0
- package/hub/fullcycle.mjs +34 -17
- package/hub/gemini-adapter.mjs +77 -95
- package/hub/hitl.mjs +202 -143
- package/hub/intent.mjs +171 -39
- package/hub/lib/bash-path.mjs +73 -0
- package/hub/lib/cache-guard.mjs +43 -17
- package/hub/lib/env-detect.mjs +145 -0
- package/hub/lib/hosts-compat.mjs +319 -0
- package/hub/lib/mcp-response-cache.mjs +239 -0
- package/hub/lib/memory-store.mjs +303 -107
- package/hub/lib/path-utils.mjs +165 -0
- package/hub/lib/phase-manager.mjs +493 -0
- package/hub/lib/process-utils.mjs +797 -104
- package/hub/lib/prompt-tmp.mjs +33 -0
- package/hub/lib/risk-tier.mjs +53 -0
- package/hub/lib/spawn-trace.mjs +561 -0
- package/hub/lib/ssh-command.mjs +44 -24
- package/hub/lib/ssh-retry.mjs +5 -1
- package/hub/lib/staged-file-detect.mjs +43 -0
- package/hub/lib/state-snapshot.mjs +293 -0
- package/hub/lib/tfx-route-args.mjs +309 -0
- package/hub/lib/timeline-adapter.mjs +190 -0
- package/hub/lib/trace-recorder.mjs +153 -0
- package/hub/lib/uuidv7.mjs +4 -3
- package/hub/memory-doctor.mjs +266 -106
- package/hub/middleware/quota-middleware.mjs +70 -0
- 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 +65 -48
- package/hub/quality/deslop.mjs +70 -49
- package/hub/reflexion.mjs +116 -58
- package/hub/research.mjs +34 -25
- package/hub/router.mjs +941 -791
- package/hub/routing/complexity.mjs +132 -29
- package/hub/routing/index.mjs +17 -12
- package/hub/routing/q-learning.mjs +76 -28
- package/hub/routing-snapshot.mjs +261 -0
- package/hub/session-fingerprint.mjs +129 -63
- package/hub/state.mjs +86 -44
- package/hub/team/retry-state-machine.mjs +267 -0
- package/hub/token-mode.mjs +76 -72
- package/hub/workers/worker-utils.mjs +22 -16
- package/hud/colors.mjs +27 -9
- package/hud/constants.mjs +184 -27
- package/hud/context-monitor.mjs +133 -44
- package/hud/hud-qos-status.mjs +153 -50
- package/hud/mission-board.mjs +56 -0
- package/hud/providers/claude.mjs +398 -114
- package/hud/providers/codex.mjs +96 -21
- package/hud/providers/gemini.mjs +184 -51
- package/hud/renderers.mjs +413 -123
- package/hud/terminal.mjs +101 -31
- package/hud/utils.mjs +81 -39
- package/mesh/index.mjs +69 -0
- package/mesh/mesh-budget.mjs +137 -0
- package/mesh/mesh-heartbeat.mjs +100 -0
- package/mesh/mesh-protocol.mjs +96 -0
- package/mesh/mesh-queue.mjs +163 -0
- package/mesh/mesh-registry.mjs +78 -0
- package/mesh/mesh-router.mjs +77 -0
- package/package.json +5 -1
- package/scripts/lib/agent-json.mjs +27 -0
- package/scripts/lib/async.mjs +174 -0
- package/scripts/lib/claudemd-scanner.mjs +6 -1
- package/scripts/lib/cli-agy.mjs +62 -0
- package/scripts/lib/cli-claude.mjs +78 -0
- package/scripts/lib/cli-codex.mjs +199 -0
- package/scripts/lib/cli-gemini.mjs +67 -0
- package/scripts/lib/codex-recovery.sh +50 -0
- package/scripts/lib/context.mjs +3 -3
- package/scripts/lib/cross-review-utils.mjs +13 -5
- package/scripts/lib/doctor-env-checks.mjs +121 -0
- package/scripts/lib/dynamic-route-cli.mjs +107 -0
- package/scripts/lib/env-probe.mjs +58 -30
- package/scripts/lib/env.mjs +14 -0
- 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/hub.mjs +48 -0
- package/scripts/lib/keyword-rules.mjs +48 -19
- package/scripts/lib/logger.mjs +24 -24
- package/scripts/lib/mcp-filter.mjs +393 -249
- package/scripts/lib/mcp-gateway-health-check.mjs +159 -0
- package/scripts/lib/mcp-gateway-servers.mjs +70 -0
- package/scripts/lib/mcp-gateway-wrapper-check.mjs +35 -0
- package/scripts/lib/mcp-guard-engine.mjs +1282 -116
- package/scripts/lib/mcp-health.mjs +773 -0
- package/scripts/lib/mcp-manifest.mjs +24 -14
- package/scripts/lib/mcp-server-catalog.mjs +355 -118
- package/scripts/lib/pid.mjs +63 -0
- package/scripts/lib/process-utils.mjs +26 -0
- package/scripts/lib/psmux-info.mjs +72 -12
- package/scripts/lib/quota.mjs +47 -0
- package/scripts/lib/remote-spawn-transfer.mjs +44 -14
- package/scripts/lib/skill-state.mjs +220 -0
- package/scripts/lib/skill-template.mjs +36 -11
- package/scripts/lib/team.mjs +75 -0
- package/scripts/lib/timeout.mjs +66 -0
- package/scripts/lib/tmp.mjs +26 -0
- package/scripts/lib/toml.mjs +113 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import {
|
|
4
|
+
existsSync,
|
|
5
|
+
mkdirSync,
|
|
6
|
+
readFileSync,
|
|
7
|
+
statSync,
|
|
8
|
+
writeFileSync,
|
|
9
|
+
} from "node:fs";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
|
|
12
|
+
function jobDir(jobsDir, jobId) {
|
|
13
|
+
return join(jobsDir, jobId);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function readState(dir) {
|
|
17
|
+
const path = join(dir, "state.json");
|
|
18
|
+
if (!existsSync(path)) return null;
|
|
19
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function writeState(dir, state) {
|
|
23
|
+
writeFileSync(join(dir, "state.json"), `${JSON.stringify(state, null, 2)}\n`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function fileSize(path) {
|
|
27
|
+
try {
|
|
28
|
+
return statSync(path).size;
|
|
29
|
+
} catch {
|
|
30
|
+
return 0;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function isAlive(pid) {
|
|
35
|
+
if (!pid || pid === "starting") return false;
|
|
36
|
+
try {
|
|
37
|
+
process.kill(Number(pid), 0);
|
|
38
|
+
return true;
|
|
39
|
+
} catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function createJob({
|
|
45
|
+
jobsDir,
|
|
46
|
+
command,
|
|
47
|
+
args = [],
|
|
48
|
+
env = process.env,
|
|
49
|
+
cwd = process.cwd(),
|
|
50
|
+
timeoutMs = 0,
|
|
51
|
+
}) {
|
|
52
|
+
mkdirSync(jobsDir, { recursive: true });
|
|
53
|
+
const id = randomUUID();
|
|
54
|
+
const dir = jobDir(jobsDir, id);
|
|
55
|
+
mkdirSync(dir, { recursive: true });
|
|
56
|
+
const startedAt = Date.now();
|
|
57
|
+
const stdoutPath = join(dir, "result.log");
|
|
58
|
+
const stderrPath = join(dir, "stderr.log");
|
|
59
|
+
writeState(dir, {
|
|
60
|
+
id,
|
|
61
|
+
state: "starting",
|
|
62
|
+
startedAt,
|
|
63
|
+
command,
|
|
64
|
+
args,
|
|
65
|
+
timeoutMs,
|
|
66
|
+
});
|
|
67
|
+
writeFileSync(join(dir, "pid"), "starting");
|
|
68
|
+
writeFileSync(join(dir, "start_time"), String(Math.floor(startedAt / 1000)));
|
|
69
|
+
|
|
70
|
+
const child = spawn(command, args, {
|
|
71
|
+
cwd,
|
|
72
|
+
env,
|
|
73
|
+
detached: true,
|
|
74
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
75
|
+
});
|
|
76
|
+
child.unref?.();
|
|
77
|
+
writeFileSync(join(dir, "pid"), String(child.pid));
|
|
78
|
+
writeState(dir, {
|
|
79
|
+
id,
|
|
80
|
+
state: "running",
|
|
81
|
+
pid: child.pid,
|
|
82
|
+
startedAt,
|
|
83
|
+
command,
|
|
84
|
+
args,
|
|
85
|
+
timeoutMs,
|
|
86
|
+
});
|
|
87
|
+
child.stdout?.on("data", (chunk) => {
|
|
88
|
+
writeFileSync(stdoutPath, chunk, { flag: "a" });
|
|
89
|
+
});
|
|
90
|
+
child.stderr?.on("data", (chunk) => {
|
|
91
|
+
writeFileSync(stderrPath, chunk, { flag: "a" });
|
|
92
|
+
});
|
|
93
|
+
let timer = null;
|
|
94
|
+
let timedOut = false;
|
|
95
|
+
if (timeoutMs > 0) {
|
|
96
|
+
timer = setTimeout(() => {
|
|
97
|
+
timedOut = true;
|
|
98
|
+
try {
|
|
99
|
+
process.kill(child.pid, "SIGTERM");
|
|
100
|
+
} catch {
|
|
101
|
+
// Process already exited.
|
|
102
|
+
}
|
|
103
|
+
}, timeoutMs);
|
|
104
|
+
timer.unref?.();
|
|
105
|
+
}
|
|
106
|
+
child.on("exit", (code) => {
|
|
107
|
+
if (timer) clearTimeout(timer);
|
|
108
|
+
const exitCode = timedOut ? 124 : (code ?? 1);
|
|
109
|
+
writeFileSync(join(dir, "exit_code"), String(exitCode));
|
|
110
|
+
writeFileSync(join(dir, "done"), "");
|
|
111
|
+
writeState(dir, {
|
|
112
|
+
id,
|
|
113
|
+
state: exitCode === 0 ? "done" : exitCode === 124 ? "timeout" : "failed",
|
|
114
|
+
pid: child.pid,
|
|
115
|
+
startedAt,
|
|
116
|
+
finishedAt: Date.now(),
|
|
117
|
+
exitCode,
|
|
118
|
+
command,
|
|
119
|
+
args,
|
|
120
|
+
timeoutMs,
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
return { id, dir, pid: child.pid };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function getJobStatus(jobId, { jobsDir }) {
|
|
128
|
+
const dir = jobDir(jobsDir, jobId);
|
|
129
|
+
if (!existsSync(dir))
|
|
130
|
+
return { state: "error", text: "error: job not found", exitCode: 1 };
|
|
131
|
+
const state = readState(dir);
|
|
132
|
+
if (state?.state === "done") return { ...state, text: "done" };
|
|
133
|
+
if (state?.state === "timeout") return { ...state, text: "timeout" };
|
|
134
|
+
if (state?.state === "failed") return { ...state, text: "failed" };
|
|
135
|
+
const pidText = existsSync(join(dir, "pid"))
|
|
136
|
+
? readFileSync(join(dir, "pid"), "utf8").trim()
|
|
137
|
+
: "";
|
|
138
|
+
if (pidText === "starting")
|
|
139
|
+
return { ...(state ?? {}), state: "starting", text: "starting" };
|
|
140
|
+
if (isAlive(pidText)) {
|
|
141
|
+
const start =
|
|
142
|
+
Number(readFileSync(join(dir, "start_time"), "utf8")) ||
|
|
143
|
+
Math.floor(Date.now() / 1000);
|
|
144
|
+
const elapsed = Math.max(0, Math.floor(Date.now() / 1000) - start);
|
|
145
|
+
const bytes = fileSize(join(dir, "result.log"));
|
|
146
|
+
return {
|
|
147
|
+
...(state ?? {}),
|
|
148
|
+
state: "running",
|
|
149
|
+
text: `running elapsed=${elapsed}s output=${bytes}B`,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
return { ...(state ?? {}), state: "failed", text: "failed" };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function getJobResult(jobId, { jobsDir }) {
|
|
156
|
+
const dir = jobDir(jobsDir, jobId);
|
|
157
|
+
if (!existsSync(dir)) throw new Error("job not found");
|
|
158
|
+
const status = getJobStatus(jobId, { jobsDir });
|
|
159
|
+
if (!["done", "timeout", "failed"].includes(status.state)) {
|
|
160
|
+
throw new Error("job still running");
|
|
161
|
+
}
|
|
162
|
+
const stdout = existsSync(join(dir, "result.log"))
|
|
163
|
+
? readFileSync(join(dir, "result.log"), "utf8")
|
|
164
|
+
: "";
|
|
165
|
+
const stderr = existsSync(join(dir, "stderr.log"))
|
|
166
|
+
? readFileSync(join(dir, "stderr.log"), "utf8")
|
|
167
|
+
: "";
|
|
168
|
+
return {
|
|
169
|
+
state: status.state,
|
|
170
|
+
exitCode: status.exitCode ?? 1,
|
|
171
|
+
stdout: stdout || stderr,
|
|
172
|
+
stderr,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
@@ -23,7 +23,12 @@ const DEFAULT_TFX_TEMPLATE = [
|
|
|
23
23
|
].join("\n");
|
|
24
24
|
|
|
25
25
|
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
|
|
26
|
-
const DEFAULT_TEMPLATE_PATH = join(
|
|
26
|
+
const DEFAULT_TEMPLATE_PATH = join(
|
|
27
|
+
SCRIPT_DIR,
|
|
28
|
+
"..",
|
|
29
|
+
"templates",
|
|
30
|
+
"claudemd-tfx-section.md",
|
|
31
|
+
);
|
|
27
32
|
|
|
28
33
|
function resolveVersion(version) {
|
|
29
34
|
if (version) return version;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// cli-agy.mjs — Antigravity (agy) CLI adapter (Phase 0 PoC).
|
|
2
|
+
//
|
|
3
|
+
// 책임: agy --print 는 stdin-only 이므로 plan() 에서 stdinMode="pipe" 시그널을 반환한다.
|
|
4
|
+
// positional prompt 패턴은 환영 메시지 폴백을 유발하므로 사용 금지.
|
|
5
|
+
//
|
|
6
|
+
// 출처: scripts/tfx-route.sh route_agent() L1052-L1059, run_codex_exec() L2036-L2046.
|
|
7
|
+
// .claude/rules/tfx-update-logic.md Antigravity CLI 1.0.0 정밀 sanity matrix.
|
|
8
|
+
|
|
9
|
+
export const id = "agy";
|
|
10
|
+
export const cliType = "antigravity";
|
|
11
|
+
export const command = "agy";
|
|
12
|
+
|
|
13
|
+
const AGENT_PROFILES = {
|
|
14
|
+
antigravity: {
|
|
15
|
+
profile: "agy_v1",
|
|
16
|
+
timeoutSec: 900,
|
|
17
|
+
runMode: "bg",
|
|
18
|
+
opusOversight: false,
|
|
19
|
+
mcpHint: "docs",
|
|
20
|
+
},
|
|
21
|
+
agy: {
|
|
22
|
+
profile: "agy_v1",
|
|
23
|
+
timeoutSec: 900,
|
|
24
|
+
runMode: "bg",
|
|
25
|
+
opusOversight: false,
|
|
26
|
+
mcpHint: "docs",
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const DEFAULT_PROFILE = AGENT_PROFILES.antigravity;
|
|
31
|
+
|
|
32
|
+
export function plan({
|
|
33
|
+
agent,
|
|
34
|
+
prompt = "",
|
|
35
|
+
mcpProfile = "auto",
|
|
36
|
+
timeoutSec,
|
|
37
|
+
contextFile,
|
|
38
|
+
} = {}) {
|
|
39
|
+
if (!agent) {
|
|
40
|
+
throw new Error("[cli-agy] agent required");
|
|
41
|
+
}
|
|
42
|
+
const cfg = AGENT_PROFILES[agent] ?? DEFAULT_PROFILE;
|
|
43
|
+
const effectiveTimeoutSec =
|
|
44
|
+
Number.isFinite(timeoutSec) && timeoutSec > 0 ? timeoutSec : cfg.timeoutSec;
|
|
45
|
+
return {
|
|
46
|
+
command,
|
|
47
|
+
profile: cfg.profile,
|
|
48
|
+
effort: cfg.profile,
|
|
49
|
+
args: ["--print", "--dangerously-skip-permissions"],
|
|
50
|
+
stdinMode: "pipe",
|
|
51
|
+
timeoutMs: effectiveTimeoutSec * 1000,
|
|
52
|
+
runMode: cfg.runMode,
|
|
53
|
+
opusOversight: cfg.opusOversight,
|
|
54
|
+
mcpProfile: mcpProfile === "auto" ? cfg.mcpHint : mcpProfile,
|
|
55
|
+
promptLength: prompt.length,
|
|
56
|
+
contextFile: contextFile || null,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function describe() {
|
|
61
|
+
return { id, cliType, command, agents: Object.keys(AGENT_PROFILES) };
|
|
62
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// cli-claude.mjs — Claude native adapter (Phase 0 PoC).
|
|
2
|
+
//
|
|
3
|
+
// 책임: Claude 는 외부 CLI 가 아닌 Claude Code Agent 도구로 dispatch 된다. plan() 은
|
|
4
|
+
// command=null 을 반환하고 호출자가 Agent() 로 라우팅하도록 신호한다.
|
|
5
|
+
//
|
|
6
|
+
// 출처: scripts/tfx-route.sh route_agent() L1062-L1063 (explore|claude),
|
|
7
|
+
// L1196-L1203 (TFX_CLI_MODE=gemini fallback), TFX_VERIFIER_OVERRIDE=claude.
|
|
8
|
+
|
|
9
|
+
export const id = "claude";
|
|
10
|
+
export const cliType = "claude-native";
|
|
11
|
+
export const command = null;
|
|
12
|
+
|
|
13
|
+
const AGENT_PROFILES = {
|
|
14
|
+
explore: {
|
|
15
|
+
timeoutSec: 600,
|
|
16
|
+
runMode: "fg",
|
|
17
|
+
opusOversight: false,
|
|
18
|
+
model: "haiku",
|
|
19
|
+
},
|
|
20
|
+
claude: {
|
|
21
|
+
timeoutSec: 600,
|
|
22
|
+
runMode: "fg",
|
|
23
|
+
opusOversight: false,
|
|
24
|
+
model: "haiku",
|
|
25
|
+
},
|
|
26
|
+
"test-engineer": {
|
|
27
|
+
timeoutSec: 1200,
|
|
28
|
+
runMode: "bg",
|
|
29
|
+
opusOversight: false,
|
|
30
|
+
model: "sonnet",
|
|
31
|
+
},
|
|
32
|
+
"qa-tester": {
|
|
33
|
+
timeoutSec: 1200,
|
|
34
|
+
runMode: "bg",
|
|
35
|
+
opusOversight: false,
|
|
36
|
+
model: "sonnet",
|
|
37
|
+
},
|
|
38
|
+
verifier: {
|
|
39
|
+
timeoutSec: 1200,
|
|
40
|
+
runMode: "fg",
|
|
41
|
+
opusOversight: false,
|
|
42
|
+
model: "sonnet",
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const DEFAULT_PROFILE = AGENT_PROFILES.explore;
|
|
47
|
+
|
|
48
|
+
export function plan({
|
|
49
|
+
agent,
|
|
50
|
+
prompt = "",
|
|
51
|
+
mcpProfile = "auto",
|
|
52
|
+
timeoutSec,
|
|
53
|
+
contextFile,
|
|
54
|
+
} = {}) {
|
|
55
|
+
if (!agent) {
|
|
56
|
+
throw new Error("[cli-claude] agent required");
|
|
57
|
+
}
|
|
58
|
+
const cfg = AGENT_PROFILES[agent] ?? DEFAULT_PROFILE;
|
|
59
|
+
const effectiveTimeoutSec =
|
|
60
|
+
Number.isFinite(timeoutSec) && timeoutSec > 0 ? timeoutSec : cfg.timeoutSec;
|
|
61
|
+
return {
|
|
62
|
+
command,
|
|
63
|
+
profile: "claude-native",
|
|
64
|
+
effort: "n/a",
|
|
65
|
+
timeoutMs: effectiveTimeoutSec * 1000,
|
|
66
|
+
runMode: cfg.runMode,
|
|
67
|
+
opusOversight: cfg.opusOversight,
|
|
68
|
+
model: cfg.model,
|
|
69
|
+
mcpProfile: mcpProfile === "auto" ? null : mcpProfile,
|
|
70
|
+
promptLength: prompt.length,
|
|
71
|
+
contextFile: contextFile || null,
|
|
72
|
+
note: "Claude native — dispatched via Claude Code Agent tool, not CLI",
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function describe() {
|
|
77
|
+
return { id, cliType, command, agents: Object.keys(AGENT_PROFILES) };
|
|
78
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
// cli-codex.mjs — Codex CLI adapter (Phase 0 PoC).
|
|
2
|
+
//
|
|
3
|
+
// 책임: agent 이름과 입력 파라미터를 받아 Codex 실행 계획(profile/effort/timeout/runMode)을
|
|
4
|
+
// 산출한다. 실제 spawn 은 Phase 1 에서 이관된다 (현재는 tfx-route.sh 가 수행).
|
|
5
|
+
//
|
|
6
|
+
// 진입점: tfx-route.mjs → selectAdapter("codex") → plan({...}).
|
|
7
|
+
// 출처: scripts/tfx-route.sh route_agent() L1001-L1079 case 문 미러.
|
|
8
|
+
|
|
9
|
+
export const id = "codex";
|
|
10
|
+
export const cliType = "codex";
|
|
11
|
+
export const command = "codex";
|
|
12
|
+
|
|
13
|
+
const AGENT_PROFILES = {
|
|
14
|
+
executor: {
|
|
15
|
+
profile: "gpt55_high",
|
|
16
|
+
timeoutSec: 1080,
|
|
17
|
+
runMode: "fg",
|
|
18
|
+
opusOversight: false,
|
|
19
|
+
mcpHint: "implement",
|
|
20
|
+
},
|
|
21
|
+
codex: {
|
|
22
|
+
profile: "gpt55_high",
|
|
23
|
+
timeoutSec: 1080,
|
|
24
|
+
runMode: "fg",
|
|
25
|
+
opusOversight: false,
|
|
26
|
+
mcpHint: "implement",
|
|
27
|
+
},
|
|
28
|
+
"build-fixer": {
|
|
29
|
+
profile: "gpt55_low",
|
|
30
|
+
timeoutSec: 540,
|
|
31
|
+
runMode: "fg",
|
|
32
|
+
opusOversight: false,
|
|
33
|
+
mcpHint: "implement",
|
|
34
|
+
},
|
|
35
|
+
cleanup: {
|
|
36
|
+
profile: "mini54_med",
|
|
37
|
+
timeoutSec: 540,
|
|
38
|
+
runMode: "fg",
|
|
39
|
+
opusOversight: false,
|
|
40
|
+
mcpHint: "implement",
|
|
41
|
+
},
|
|
42
|
+
deslop: {
|
|
43
|
+
profile: "mini54_med",
|
|
44
|
+
timeoutSec: 540,
|
|
45
|
+
runMode: "fg",
|
|
46
|
+
opusOversight: false,
|
|
47
|
+
mcpHint: "implement",
|
|
48
|
+
},
|
|
49
|
+
debugger: {
|
|
50
|
+
profile: "gpt55_xhigh",
|
|
51
|
+
timeoutSec: 900,
|
|
52
|
+
runMode: "bg",
|
|
53
|
+
opusOversight: false,
|
|
54
|
+
mcpHint: "implement",
|
|
55
|
+
},
|
|
56
|
+
"deep-executor": {
|
|
57
|
+
profile: "gpt55_xhigh",
|
|
58
|
+
timeoutSec: 3600,
|
|
59
|
+
runMode: "bg",
|
|
60
|
+
opusOversight: true,
|
|
61
|
+
mcpHint: "implement",
|
|
62
|
+
},
|
|
63
|
+
architect: {
|
|
64
|
+
profile: "gpt55_xhigh",
|
|
65
|
+
timeoutSec: 3600,
|
|
66
|
+
runMode: "bg",
|
|
67
|
+
opusOversight: true,
|
|
68
|
+
mcpHint: "analyze",
|
|
69
|
+
},
|
|
70
|
+
critic: {
|
|
71
|
+
profile: "gpt55_xhigh",
|
|
72
|
+
timeoutSec: 3600,
|
|
73
|
+
runMode: "bg",
|
|
74
|
+
opusOversight: true,
|
|
75
|
+
mcpHint: "analyze",
|
|
76
|
+
},
|
|
77
|
+
planner: {
|
|
78
|
+
profile: "gpt55_xhigh",
|
|
79
|
+
timeoutSec: 3600,
|
|
80
|
+
runMode: "fg",
|
|
81
|
+
opusOversight: true,
|
|
82
|
+
mcpHint: "analyze",
|
|
83
|
+
},
|
|
84
|
+
analyst: {
|
|
85
|
+
profile: "gpt55_xhigh",
|
|
86
|
+
timeoutSec: 3600,
|
|
87
|
+
runMode: "fg",
|
|
88
|
+
opusOversight: true,
|
|
89
|
+
mcpHint: "analyze",
|
|
90
|
+
},
|
|
91
|
+
"code-reviewer": {
|
|
92
|
+
profile: "gpt55_high",
|
|
93
|
+
timeoutSec: 1800,
|
|
94
|
+
runMode: "bg",
|
|
95
|
+
opusOversight: false,
|
|
96
|
+
mcpHint: "review",
|
|
97
|
+
subcommand: "review",
|
|
98
|
+
},
|
|
99
|
+
"quality-reviewer": {
|
|
100
|
+
profile: "gpt55_high",
|
|
101
|
+
timeoutSec: 1800,
|
|
102
|
+
runMode: "bg",
|
|
103
|
+
opusOversight: false,
|
|
104
|
+
mcpHint: "review",
|
|
105
|
+
subcommand: "review",
|
|
106
|
+
},
|
|
107
|
+
"security-reviewer": {
|
|
108
|
+
profile: "gpt55_xhigh",
|
|
109
|
+
timeoutSec: 1800,
|
|
110
|
+
runMode: "bg",
|
|
111
|
+
opusOversight: true,
|
|
112
|
+
mcpHint: "review",
|
|
113
|
+
subcommand: "review",
|
|
114
|
+
},
|
|
115
|
+
scientist: {
|
|
116
|
+
profile: "gpt55_high",
|
|
117
|
+
timeoutSec: 1440,
|
|
118
|
+
runMode: "bg",
|
|
119
|
+
opusOversight: false,
|
|
120
|
+
mcpHint: "analyze",
|
|
121
|
+
},
|
|
122
|
+
"document-specialist": {
|
|
123
|
+
profile: "gpt55_high",
|
|
124
|
+
timeoutSec: 1440,
|
|
125
|
+
runMode: "bg",
|
|
126
|
+
opusOversight: false,
|
|
127
|
+
mcpHint: "analyze",
|
|
128
|
+
},
|
|
129
|
+
"scientist-deep": {
|
|
130
|
+
profile: "gpt55_xhigh",
|
|
131
|
+
timeoutSec: 3600,
|
|
132
|
+
runMode: "bg",
|
|
133
|
+
opusOversight: false,
|
|
134
|
+
mcpHint: "analyze",
|
|
135
|
+
},
|
|
136
|
+
verifier: {
|
|
137
|
+
profile: "gpt55_high",
|
|
138
|
+
timeoutSec: 1200,
|
|
139
|
+
runMode: "fg",
|
|
140
|
+
opusOversight: false,
|
|
141
|
+
mcpHint: "review",
|
|
142
|
+
subcommand: "review",
|
|
143
|
+
},
|
|
144
|
+
"test-engineer": {
|
|
145
|
+
profile: "gpt55_high",
|
|
146
|
+
timeoutSec: 1200,
|
|
147
|
+
runMode: "bg",
|
|
148
|
+
opusOversight: false,
|
|
149
|
+
mcpHint: "implement",
|
|
150
|
+
},
|
|
151
|
+
"qa-tester": {
|
|
152
|
+
profile: "gpt55_high",
|
|
153
|
+
timeoutSec: 1200,
|
|
154
|
+
runMode: "bg",
|
|
155
|
+
opusOversight: false,
|
|
156
|
+
mcpHint: "review",
|
|
157
|
+
subcommand: "review",
|
|
158
|
+
},
|
|
159
|
+
spark: {
|
|
160
|
+
profile: "spark53_low",
|
|
161
|
+
timeoutSec: 180,
|
|
162
|
+
runMode: "fg",
|
|
163
|
+
opusOversight: false,
|
|
164
|
+
mcpHint: "implement",
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const DEFAULT_PROFILE = AGENT_PROFILES.executor;
|
|
169
|
+
|
|
170
|
+
export function plan({
|
|
171
|
+
agent,
|
|
172
|
+
prompt = "",
|
|
173
|
+
mcpProfile = "auto",
|
|
174
|
+
timeoutSec,
|
|
175
|
+
contextFile,
|
|
176
|
+
} = {}) {
|
|
177
|
+
if (!agent) {
|
|
178
|
+
throw new Error("[cli-codex] agent required");
|
|
179
|
+
}
|
|
180
|
+
const cfg = AGENT_PROFILES[agent] ?? DEFAULT_PROFILE;
|
|
181
|
+
const effectiveTimeoutSec =
|
|
182
|
+
Number.isFinite(timeoutSec) && timeoutSec > 0 ? timeoutSec : cfg.timeoutSec;
|
|
183
|
+
return {
|
|
184
|
+
command,
|
|
185
|
+
subcommand: cfg.subcommand ?? "exec",
|
|
186
|
+
profile: cfg.profile,
|
|
187
|
+
effort: cfg.profile,
|
|
188
|
+
timeoutMs: effectiveTimeoutSec * 1000,
|
|
189
|
+
runMode: cfg.runMode,
|
|
190
|
+
opusOversight: cfg.opusOversight,
|
|
191
|
+
mcpProfile: mcpProfile === "auto" ? cfg.mcpHint : mcpProfile,
|
|
192
|
+
promptLength: prompt.length,
|
|
193
|
+
contextFile: contextFile || null,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function describe() {
|
|
198
|
+
return { id, cliType, command, agents: Object.keys(AGENT_PROFILES) };
|
|
199
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// cli-gemini.mjs — Gemini CLI adapter (Phase 0 PoC).
|
|
2
|
+
//
|
|
3
|
+
// 책임: Gemini 프로파일/모델/timeout 매핑. resolve_gemini_profile() 의 결과는 Phase 1 에서
|
|
4
|
+
// 통합한다 (Phase 0 은 라벨만 전달).
|
|
5
|
+
//
|
|
6
|
+
// 출처: scripts/tfx-route.sh route_agent() L1045-L1050.
|
|
7
|
+
|
|
8
|
+
export const id = "gemini";
|
|
9
|
+
export const cliType = "gemini";
|
|
10
|
+
export const command = "gemini";
|
|
11
|
+
|
|
12
|
+
const AGENT_PROFILES = {
|
|
13
|
+
designer: {
|
|
14
|
+
profile: "pro31",
|
|
15
|
+
timeoutSec: 900,
|
|
16
|
+
runMode: "bg",
|
|
17
|
+
opusOversight: false,
|
|
18
|
+
mcpHint: "docs",
|
|
19
|
+
},
|
|
20
|
+
gemini: {
|
|
21
|
+
profile: "pro31",
|
|
22
|
+
timeoutSec: 900,
|
|
23
|
+
runMode: "bg",
|
|
24
|
+
opusOversight: false,
|
|
25
|
+
mcpHint: "docs",
|
|
26
|
+
},
|
|
27
|
+
writer: {
|
|
28
|
+
profile: "flash3",
|
|
29
|
+
timeoutSec: 900,
|
|
30
|
+
runMode: "bg",
|
|
31
|
+
opusOversight: false,
|
|
32
|
+
mcpHint: "docs",
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const DEFAULT_PROFILE = AGENT_PROFILES.gemini;
|
|
37
|
+
|
|
38
|
+
export function plan({
|
|
39
|
+
agent,
|
|
40
|
+
prompt = "",
|
|
41
|
+
mcpProfile = "auto",
|
|
42
|
+
timeoutSec,
|
|
43
|
+
contextFile,
|
|
44
|
+
} = {}) {
|
|
45
|
+
if (!agent) {
|
|
46
|
+
throw new Error("[cli-gemini] agent required");
|
|
47
|
+
}
|
|
48
|
+
const cfg = AGENT_PROFILES[agent] ?? DEFAULT_PROFILE;
|
|
49
|
+
const effectiveTimeoutSec =
|
|
50
|
+
Number.isFinite(timeoutSec) && timeoutSec > 0 ? timeoutSec : cfg.timeoutSec;
|
|
51
|
+
return {
|
|
52
|
+
command,
|
|
53
|
+
profile: cfg.profile,
|
|
54
|
+
effort: cfg.profile,
|
|
55
|
+
args: ["-m", cfg.profile, "-y", "--prompt"],
|
|
56
|
+
timeoutMs: effectiveTimeoutSec * 1000,
|
|
57
|
+
runMode: cfg.runMode,
|
|
58
|
+
opusOversight: cfg.opusOversight,
|
|
59
|
+
mcpProfile: mcpProfile === "auto" ? cfg.mcpHint : mcpProfile,
|
|
60
|
+
promptLength: prompt.length,
|
|
61
|
+
contextFile: contextFile || null,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function describe() {
|
|
66
|
+
return { id, cliType, command, agents: Object.keys(AGENT_PROFILES) };
|
|
67
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# codex-recovery.sh — codex exec stdout 4계단 recovery helper.
|
|
3
|
+
# 사용: STDOUT_LOG / STDERR_LOG env 설정 후 `source` + `recover_codex_stdout`.
|
|
4
|
+
#
|
|
5
|
+
# 1차 codex marker → stderr 의 마지막 "codex" 라인 이후 본문 회수
|
|
6
|
+
# 2차 node parser → MCP/header/sandbox 로그 제외, 응답 부분만 추출
|
|
7
|
+
# 3차 stderr tail → "tokens used" 직전까지 tail buffer 회수
|
|
8
|
+
# 4차 FALLBACK_FAILED → 모두 실패 시 marker + stderr_log 경로 출력
|
|
9
|
+
|
|
10
|
+
recover_codex_stdout() {
|
|
11
|
+
if [[ -s "$STDOUT_LOG" || ! -s "$STDERR_LOG" ]]; then
|
|
12
|
+
return 0
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
# 1차: codex marker
|
|
16
|
+
sed 's/\r$//' "$STDERR_LOG" \
|
|
17
|
+
| awk '/^codex$/{found=NR;content=""} found && NR>found{content=content RS $0} END{if(content) print substr(content,2)}' \
|
|
18
|
+
> "$STDOUT_LOG"
|
|
19
|
+
|
|
20
|
+
# 2차: node parser (MCP/header/sandbox 로그 제외)
|
|
21
|
+
if [[ ! -s "$STDOUT_LOG" ]]; then
|
|
22
|
+
node -e '
|
|
23
|
+
const fs=require("fs"),lines=fs.readFileSync(process.argv[1],"utf-8").split(/\r?\n/);
|
|
24
|
+
const skip=/^(mcp[: ]|OpenAI Codex|--------|workdir:|model:|provider:|approval:|sandbox:|reasoning|session id:|user$|tokens used|EXIT:|exec$|"[A-Z]:|succeeded in |\s*$)/;
|
|
25
|
+
const out=lines.filter(l=>!skip.test(l));
|
|
26
|
+
if(out.length) fs.writeFileSync(process.argv[2],out.join("\n"));
|
|
27
|
+
' -- "$STDERR_LOG" "$STDOUT_LOG" 2>/dev/null || true
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
# 3차: stderr tail before "tokens used"
|
|
31
|
+
if [[ ! -s "$STDOUT_LOG" ]]; then
|
|
32
|
+
sed 's/\r$//' "$STDERR_LOG" \
|
|
33
|
+
| awk '
|
|
34
|
+
/^tokens used/ { exit }
|
|
35
|
+
{ buf[NR]=$0 }
|
|
36
|
+
END {
|
|
37
|
+
start=NR-200; if (start<1) start=1
|
|
38
|
+
for (i=start; i<=NR; i++) if (i in buf) print buf[i]
|
|
39
|
+
}' \
|
|
40
|
+
> "$STDOUT_LOG"
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
if [[ -s "$STDOUT_LOG" ]]; then
|
|
44
|
+
echo "[tfx-route] 경고: codex stdout 비어있음, stderr에서 응답 복구 ($(wc -c < "$STDOUT_LOG" | tr -d ' ') bytes)" >&2
|
|
45
|
+
else
|
|
46
|
+
# 4차: FALLBACK_FAILED
|
|
47
|
+
echo "[tfx-route] FALLBACK_FAILED stderr_log=$STDERR_LOG" >&2
|
|
48
|
+
echo "[tfx-route] 경고: codex stdout 비어있음, stderr 복구도 실패. 위 stderr_log 경로에서 raw codex 출력 확인 가능." >&2
|
|
49
|
+
fi
|
|
50
|
+
}
|
package/scripts/lib/context.mjs
CHANGED
|
@@ -19,10 +19,10 @@
|
|
|
19
19
|
* log.info('result.processed'); // correlationId 자동 포함
|
|
20
20
|
* }
|
|
21
21
|
*/
|
|
22
|
-
import { AsyncLocalStorage } from
|
|
23
|
-
import { randomUUID } from
|
|
22
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
23
|
+
import { randomUUID } from "node:crypto";
|
|
24
24
|
|
|
25
|
-
import { logger } from
|
|
25
|
+
import { logger } from "./logger.mjs";
|
|
26
26
|
|
|
27
27
|
/** @type {AsyncLocalStorage<{logger: import('pino').Logger, correlationId: string}>} */
|
|
28
28
|
export const asyncLocalStorage = new AsyncLocalStorage();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
|
|
3
3
|
export const SESSION_TTL_SEC = 30 * 60;
|
|
4
|
-
export const STATE_REL_PATH = join(".
|
|
4
|
+
export const STATE_REL_PATH = join(".triflux", "state", "cross-review.json");
|
|
5
5
|
|
|
6
6
|
export function readStdin() {
|
|
7
7
|
return new Promise((resolve) => {
|
|
@@ -28,8 +28,10 @@ export function nowSec() {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
export function resolveBaseDir(payload) {
|
|
31
|
-
if (typeof payload?.cwd === "string" && payload.cwd.trim())
|
|
32
|
-
|
|
31
|
+
if (typeof payload?.cwd === "string" && payload.cwd.trim())
|
|
32
|
+
return payload.cwd;
|
|
33
|
+
if (typeof payload?.directory === "string" && payload.directory.trim())
|
|
34
|
+
return payload.directory;
|
|
33
35
|
return process.cwd();
|
|
34
36
|
}
|
|
35
37
|
|
|
@@ -37,8 +39,14 @@ export function shouldTrackPath(filePath) {
|
|
|
37
39
|
if (typeof filePath !== "string" || !filePath.trim()) return false;
|
|
38
40
|
|
|
39
41
|
const lower = filePath.toLowerCase();
|
|
40
|
-
if (
|
|
41
|
-
|
|
42
|
+
if (
|
|
43
|
+
lower.startsWith(".triflux/") ||
|
|
44
|
+
lower.startsWith(".omc/") ||
|
|
45
|
+
lower.startsWith(".claude/")
|
|
46
|
+
)
|
|
47
|
+
return false;
|
|
48
|
+
if (lower === "package-lock.json" || lower.endsWith("/package-lock.json"))
|
|
49
|
+
return false;
|
|
42
50
|
if (/\.(md|lock|yml|yaml)$/i.test(lower)) return false;
|
|
43
51
|
return true;
|
|
44
52
|
}
|