triflux 3.2.0-dev.1 → 3.2.0-dev.3
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/bin/triflux.mjs +185 -43
- package/hooks/hooks.json +12 -0
- package/hub/bridge.mjs +137 -51
- package/hub/server.mjs +100 -29
- package/hub/team/cli.mjs +1080 -113
- package/hub/team/native-supervisor.mjs +300 -0
- package/hub/team/native.mjs +92 -0
- package/hub/team/nativeProxy.mjs +460 -0
- package/hub/team/orchestrator.mjs +99 -35
- package/hub/team/pane.mjs +30 -16
- package/hub/team/session.mjs +359 -16
- package/hub/tools.mjs +113 -15
- package/package.json +1 -1
- package/scripts/setup.mjs +95 -0
- package/scripts/team-keyword.mjs +35 -0
- package/scripts/tfx-route.sh +138 -102
- package/skills/tfx-team/SKILL.md +239 -152
package/scripts/setup.mjs
CHANGED
|
@@ -10,6 +10,25 @@ import { homedir } from "os";
|
|
|
10
10
|
|
|
11
11
|
const PLUGIN_ROOT = dirname(dirname(new URL(import.meta.url).pathname)).replace(/^\/([A-Z]:)/, "$1");
|
|
12
12
|
const CLAUDE_DIR = join(homedir(), ".claude");
|
|
13
|
+
const CODEX_DIR = join(homedir(), ".codex");
|
|
14
|
+
const CODEX_CONFIG_PATH = join(CODEX_DIR, "config.toml");
|
|
15
|
+
|
|
16
|
+
const REQUIRED_CODEX_PROFILES = [
|
|
17
|
+
{
|
|
18
|
+
name: "xhigh",
|
|
19
|
+
lines: [
|
|
20
|
+
'model = "gpt-5.3-codex"',
|
|
21
|
+
'model_reasoning_effort = "xhigh"',
|
|
22
|
+
],
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: "spark_fast",
|
|
26
|
+
lines: [
|
|
27
|
+
'model = "gpt-5.1-codex-mini"',
|
|
28
|
+
'model_reasoning_effort = "low"',
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
];
|
|
13
32
|
|
|
14
33
|
// ── 파일 동기화 ──
|
|
15
34
|
|
|
@@ -41,6 +60,45 @@ function getVersion(filePath) {
|
|
|
41
60
|
}
|
|
42
61
|
}
|
|
43
62
|
|
|
63
|
+
function escapeRegExp(value) {
|
|
64
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function hasProfileSection(tomlContent, profileName) {
|
|
68
|
+
const section = `^\\[profiles\\.${escapeRegExp(profileName)}\\]\\s*$`;
|
|
69
|
+
return new RegExp(section, "m").test(tomlContent);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function ensureCodexProfiles() {
|
|
73
|
+
try {
|
|
74
|
+
if (!existsSync(CODEX_DIR)) mkdirSync(CODEX_DIR, { recursive: true });
|
|
75
|
+
|
|
76
|
+
const original = existsSync(CODEX_CONFIG_PATH)
|
|
77
|
+
? readFileSync(CODEX_CONFIG_PATH, "utf8")
|
|
78
|
+
: "";
|
|
79
|
+
|
|
80
|
+
let updated = original;
|
|
81
|
+
let added = 0;
|
|
82
|
+
|
|
83
|
+
for (const profile of REQUIRED_CODEX_PROFILES) {
|
|
84
|
+
if (hasProfileSection(updated, profile.name)) continue;
|
|
85
|
+
|
|
86
|
+
if (updated.length > 0 && !updated.endsWith("\n")) updated += "\n";
|
|
87
|
+
if (updated.trim().length > 0) updated += "\n";
|
|
88
|
+
updated += `[profiles.${profile.name}]\n${profile.lines.join("\n")}\n`;
|
|
89
|
+
added++;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (added > 0) {
|
|
93
|
+
writeFileSync(CODEX_CONFIG_PATH, updated, "utf8");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return added;
|
|
97
|
+
} catch {
|
|
98
|
+
return 0;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
44
102
|
let synced = 0;
|
|
45
103
|
|
|
46
104
|
for (const { src, dst, label } of SYNC_MAP) {
|
|
@@ -130,6 +188,36 @@ if (existsSync(hudPath)) {
|
|
|
130
188
|
}
|
|
131
189
|
}
|
|
132
190
|
|
|
191
|
+
// ── Agent Teams 환경변수 자동 설정 ──
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
let agentSettings = {};
|
|
195
|
+
if (existsSync(settingsPath)) {
|
|
196
|
+
agentSettings = JSON.parse(readFileSync(settingsPath, "utf8"));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (!agentSettings.env) agentSettings.env = {};
|
|
200
|
+
let agentSettingsChanged = false;
|
|
201
|
+
|
|
202
|
+
if (agentSettings.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS !== "1") {
|
|
203
|
+
agentSettings.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = "1";
|
|
204
|
+
agentSettingsChanged = true;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// teammateMode: auto (tmux 밖이면 in-process, 안이면 split-pane)
|
|
208
|
+
if (!agentSettings.teammateMode) {
|
|
209
|
+
agentSettings.teammateMode = "auto";
|
|
210
|
+
agentSettingsChanged = true;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (agentSettingsChanged) {
|
|
214
|
+
writeFileSync(settingsPath, JSON.stringify(agentSettings, null, 2) + "\n", "utf8");
|
|
215
|
+
synced++;
|
|
216
|
+
}
|
|
217
|
+
} catch {
|
|
218
|
+
// settings.json 파싱 실패 시 무시 — 기존 설정 보존
|
|
219
|
+
}
|
|
220
|
+
|
|
133
221
|
// ── Stale PID 파일 정리 (hub 좀비 방지) ──
|
|
134
222
|
|
|
135
223
|
const HUB_PID_FILE = join(CLAUDE_DIR, "cache", "tfx-hub", "hub.pid");
|
|
@@ -196,6 +284,13 @@ if (process.platform === "win32") {
|
|
|
196
284
|
}
|
|
197
285
|
}
|
|
198
286
|
|
|
287
|
+
// ── Codex 프로필 자동 보정 ──
|
|
288
|
+
|
|
289
|
+
const codexProfilesAdded = ensureCodexProfiles();
|
|
290
|
+
if (codexProfilesAdded > 0) {
|
|
291
|
+
synced++;
|
|
292
|
+
}
|
|
293
|
+
|
|
199
294
|
// ── MCP 인벤토리 백그라운드 갱신 ──
|
|
200
295
|
|
|
201
296
|
import { spawn } from "child_process";
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// scripts/team-keyword.mjs — "team" 매직 키워드 → /tfx-team 라우팅
|
|
3
|
+
// UserPromptSubmit 훅에서 실행. stdin으로 프롬프트 수신.
|
|
4
|
+
import { readFileSync } from "node:fs";
|
|
5
|
+
|
|
6
|
+
// stdin에서 프롬프트 읽기
|
|
7
|
+
let prompt = "";
|
|
8
|
+
try {
|
|
9
|
+
prompt = readFileSync(0, "utf8");
|
|
10
|
+
} catch {
|
|
11
|
+
process.exit(0);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// 코드 블록 제거 (오탐 방지)
|
|
15
|
+
const cleaned = prompt
|
|
16
|
+
.replace(/```[\s\S]*?```/g, "")
|
|
17
|
+
.replace(/`[^`]+`/g, "")
|
|
18
|
+
.replace(/https?:\/\/\S+/g, "");
|
|
19
|
+
|
|
20
|
+
// "team" 키워드 감지 (소유격/관사 뒤는 제외)
|
|
21
|
+
const hasTeam =
|
|
22
|
+
/(?<!\b(?:my|the|our|a|his|her|their|its|omc|oh-my-claudecode)\s)\bteam\b/i.test(cleaned) ||
|
|
23
|
+
/\btfx[\s-]?team\b/i.test(cleaned);
|
|
24
|
+
|
|
25
|
+
if (hasTeam) {
|
|
26
|
+
console.log("[MAGIC KEYWORD: tfx-team]");
|
|
27
|
+
console.log("");
|
|
28
|
+
console.log("You MUST invoke the skill using the Skill tool:");
|
|
29
|
+
console.log("");
|
|
30
|
+
console.log("Skill: tfx-team");
|
|
31
|
+
console.log("");
|
|
32
|
+
console.log(`User request:\n${prompt.trim()}`);
|
|
33
|
+
console.log("");
|
|
34
|
+
console.log("IMPORTANT: Invoke the skill IMMEDIATELY. Do not proceed without loading the skill instructions.");
|
|
35
|
+
}
|
package/scripts/tfx-route.sh
CHANGED
|
@@ -31,44 +31,19 @@ CONTEXT_FILE="${5:-}"
|
|
|
31
31
|
CODEX_BIN="${CODEX_BIN:-$(command -v codex 2>/dev/null || echo codex)}"
|
|
32
32
|
GEMINI_BIN="${GEMINI_BIN:-$(command -v gemini 2>/dev/null || echo gemini)}"
|
|
33
33
|
|
|
34
|
-
# ── 상수 ──
|
|
35
|
-
MAX_STDOUT_BYTES=51200 # 50KB — Claude 컨텍스트 절약
|
|
36
|
-
TIMESTAMP=$(date +%s)
|
|
37
|
-
STDERR_LOG="/tmp/tfx-route-${AGENT_TYPE}-${TIMESTAMP}-stderr.log"
|
|
38
|
-
STDOUT_LOG="/tmp/tfx-route-${AGENT_TYPE}-${TIMESTAMP}-stdout.log"
|
|
39
|
-
TFX_TMP="${TMPDIR:-/tmp}"
|
|
40
|
-
|
|
41
|
-
# ──
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
local script_dir
|
|
48
|
-
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
49
|
-
local pkg_bridge="${script_dir}/../hub/bridge.mjs"
|
|
50
|
-
[[ -f "$pkg_bridge" ]] && echo "$pkg_bridge" && return
|
|
51
|
-
# 3. 설치된 triflux 패키지
|
|
52
|
-
local npm_bridge
|
|
53
|
-
npm_bridge="$(npm root -g 2>/dev/null)/triflux/hub/bridge.mjs"
|
|
54
|
-
[[ -f "$npm_bridge" ]] && echo "$npm_bridge" && return
|
|
55
|
-
echo ""
|
|
56
|
-
}
|
|
57
|
-
BRIDGE_BIN="$(find_bridge)"
|
|
58
|
-
HUB_ENABLED="false"
|
|
59
|
-
if [[ -n "$BRIDGE_BIN" ]]; then
|
|
60
|
-
# Hub 핑 (3초 타임아웃, 실패 시 무시)
|
|
61
|
-
HUB_PING=$(node "$BRIDGE_BIN" ping 2>/dev/null || echo '{"ok":false}')
|
|
62
|
-
if echo "$HUB_PING" | grep -q '"ok":true'; then
|
|
63
|
-
HUB_ENABLED="true"
|
|
64
|
-
fi
|
|
65
|
-
fi
|
|
66
|
-
|
|
67
|
-
# Hub 브릿지 래퍼 (Hub 꺼져있으면 아무것도 안 함)
|
|
68
|
-
hub_bridge() {
|
|
69
|
-
[[ "$HUB_ENABLED" != "true" ]] && return 0
|
|
70
|
-
node "$BRIDGE_BIN" "$@" 2>/dev/null || true
|
|
71
|
-
}
|
|
34
|
+
# ── 상수 ──
|
|
35
|
+
MAX_STDOUT_BYTES=51200 # 50KB — Claude 컨텍스트 절약
|
|
36
|
+
TIMESTAMP=$(date +%s)
|
|
37
|
+
STDERR_LOG="/tmp/tfx-route-${AGENT_TYPE}-${TIMESTAMP}-stderr.log"
|
|
38
|
+
STDOUT_LOG="/tmp/tfx-route-${AGENT_TYPE}-${TIMESTAMP}-stdout.log"
|
|
39
|
+
TFX_TMP="${TMPDIR:-/tmp}"
|
|
40
|
+
|
|
41
|
+
# ── 팀 환경변수 ──
|
|
42
|
+
TFX_TEAM_NAME="${TFX_TEAM_NAME:-}"
|
|
43
|
+
TFX_TEAM_TASK_ID="${TFX_TEAM_TASK_ID:-}"
|
|
44
|
+
TFX_TEAM_AGENT_NAME="${TFX_TEAM_AGENT_NAME:-${AGENT_TYPE}-worker-$$}"
|
|
45
|
+
TFX_TEAM_LEAD_NAME="${TFX_TEAM_LEAD_NAME:-team-lead}"
|
|
46
|
+
TFX_HUB_URL="${TFX_HUB_URL:-http://127.0.0.1:27888}"
|
|
72
47
|
|
|
73
48
|
# fallback 시 원래 에이전트 정보 보존
|
|
74
49
|
ORIGINAL_AGENT=""
|
|
@@ -81,13 +56,83 @@ register_agent() {
|
|
|
81
56
|
> "$agent_file" 2>/dev/null || true
|
|
82
57
|
}
|
|
83
58
|
|
|
84
|
-
deregister_agent() {
|
|
85
|
-
rm -f "${TFX_TMP}/tfx-agent-$$.json" 2>/dev/null || true
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
# ──
|
|
89
|
-
#
|
|
90
|
-
|
|
59
|
+
deregister_agent() {
|
|
60
|
+
rm -f "${TFX_TMP}/tfx-agent-$$.json" 2>/dev/null || true
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# ── 팀 Hub Bridge 통신 ──
|
|
64
|
+
# JSON 문자열 이스케이프 (큰따옴표, 백슬래시, 개행, 탭, CR)
|
|
65
|
+
json_escape() {
|
|
66
|
+
local s="${1:-}"
|
|
67
|
+
s="${s//\\/\\\\}"
|
|
68
|
+
s="${s//\"/\\\"}"
|
|
69
|
+
s="${s//$'\n'/\\n}"
|
|
70
|
+
s="${s//$'\t'/\\t}"
|
|
71
|
+
s="${s//$'\r'/\\r}"
|
|
72
|
+
echo "$s"
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
team_claim_task() {
|
|
76
|
+
[[ -z "$TFX_TEAM_NAME" || -z "$TFX_TEAM_TASK_ID" ]] && return 0
|
|
77
|
+
local http_code safe_team_name safe_task_id safe_agent_name
|
|
78
|
+
safe_team_name=$(json_escape "$TFX_TEAM_NAME")
|
|
79
|
+
safe_task_id=$(json_escape "$TFX_TEAM_TASK_ID")
|
|
80
|
+
safe_agent_name=$(json_escape "$TFX_TEAM_AGENT_NAME")
|
|
81
|
+
|
|
82
|
+
http_code=$(curl -sf -o /dev/null -w "%{http_code}" -X POST "${TFX_HUB_URL}/bridge/team/task-update" \
|
|
83
|
+
-H "Content-Type: application/json" \
|
|
84
|
+
-d "{\"team_name\":\"${safe_team_name}\",\"task_id\":\"${safe_task_id}\",\"claim\":true,\"owner\":\"${safe_agent_name}\",\"status\":\"in_progress\"}" \
|
|
85
|
+
2>/dev/null) || http_code="000"
|
|
86
|
+
|
|
87
|
+
case "$http_code" in
|
|
88
|
+
200) ;; # 성공
|
|
89
|
+
409)
|
|
90
|
+
echo "[tfx-route] CLAIM_CONFLICT: task ${TFX_TEAM_TASK_ID}가 이미 claim됨. 실행 중단." >&2
|
|
91
|
+
exit 0 ;;
|
|
92
|
+
000)
|
|
93
|
+
echo "[tfx-route] 경고: Hub 연결 실패 (미실행?). claim 없이 계속 실행." >&2 ;;
|
|
94
|
+
*)
|
|
95
|
+
echo "[tfx-route] 경고: Hub claim 응답 HTTP ${http_code}. claim 없이 계속 실행." >&2 ;;
|
|
96
|
+
esac
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
team_complete_task() {
|
|
100
|
+
local result_status="${1:-completed}"
|
|
101
|
+
local result_summary="${2:-작업 완료}"
|
|
102
|
+
local safe_team_name safe_task_id safe_agent_name safe_status
|
|
103
|
+
[[ -z "$TFX_TEAM_NAME" || -z "$TFX_TEAM_TASK_ID" ]] && return 0
|
|
104
|
+
safe_team_name=$(json_escape "$TFX_TEAM_NAME")
|
|
105
|
+
safe_task_id=$(json_escape "$TFX_TEAM_TASK_ID")
|
|
106
|
+
safe_agent_name=$(json_escape "$TFX_TEAM_AGENT_NAME")
|
|
107
|
+
safe_status=$(json_escape "$result_status")
|
|
108
|
+
|
|
109
|
+
# task 상태 업데이트
|
|
110
|
+
curl -sf -X POST "${TFX_HUB_URL}/bridge/team/task-update" \
|
|
111
|
+
-H "Content-Type: application/json" \
|
|
112
|
+
-d "{\"team_name\":\"${safe_team_name}\",\"task_id\":\"${safe_task_id}\",\"status\":\"${safe_status}\",\"owner\":\"${safe_agent_name}\"}" \
|
|
113
|
+
>/dev/null 2>&1 || true
|
|
114
|
+
|
|
115
|
+
# 리드에게 메시지 전송
|
|
116
|
+
local msg_text safe_text safe_lead_name
|
|
117
|
+
msg_text=$(echo "$result_summary" | head -c 4096)
|
|
118
|
+
safe_text=$(json_escape "$msg_text")
|
|
119
|
+
safe_lead_name=$(json_escape "$TFX_TEAM_LEAD_NAME")
|
|
120
|
+
|
|
121
|
+
curl -sf -X POST "${TFX_HUB_URL}/bridge/team/send-message" \
|
|
122
|
+
-H "Content-Type: application/json" \
|
|
123
|
+
-d "{\"team_name\":\"${safe_team_name}\",\"from\":\"${safe_agent_name}\",\"to\":\"${safe_lead_name}\",\"text\":\"${safe_text}\",\"summary\":\"task ${safe_task_id} ${safe_status}\"}" \
|
|
124
|
+
>/dev/null 2>&1 || true
|
|
125
|
+
|
|
126
|
+
# Hub result 발행 (poll_messages 채널 활성화)
|
|
127
|
+
curl -sf -X POST "${TFX_HUB_URL}/bridge/result" \
|
|
128
|
+
-H "Content-Type: application/json" \
|
|
129
|
+
-d "{\"agent_id\":\"${safe_agent_name}\",\"topic\":\"task.result\",\"payload\":{\"task_id\":\"${safe_task_id}\",\"status\":\"${safe_status}\"},\"trace_id\":\"${safe_team_name}\"}" \
|
|
130
|
+
>/dev/null 2>&1 || true
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
# ── 라우팅 테이블 ──
|
|
134
|
+
# 반환: CLI_CMD, CLI_ARGS, CLI_TYPE, CLI_EFFORT, DEFAULT_TIMEOUT, RUN_MODE, OPUS_OVERSIGHT
|
|
135
|
+
route_agent() {
|
|
91
136
|
local agent="$1"
|
|
92
137
|
local codex_base="--dangerously-bypass-approvals-and-sandbox --skip-git-repo-check"
|
|
93
138
|
|
|
@@ -99,7 +144,7 @@ route_agent() {
|
|
|
99
144
|
CLI_EFFORT="high"; DEFAULT_TIMEOUT=1080; RUN_MODE="fg"; OPUS_OVERSIGHT="false" ;;
|
|
100
145
|
build-fixer)
|
|
101
146
|
CLI_TYPE="codex"; CLI_CMD="codex"
|
|
102
|
-
CLI_ARGS="--profile fast
|
|
147
|
+
CLI_ARGS="exec --profile fast ${codex_base}"
|
|
103
148
|
CLI_EFFORT="fast"; DEFAULT_TIMEOUT=540; RUN_MODE="fg"; OPUS_OVERSIGHT="false" ;;
|
|
104
149
|
debugger)
|
|
105
150
|
CLI_TYPE="codex"; CLI_CMD="codex"
|
|
@@ -107,39 +152,39 @@ route_agent() {
|
|
|
107
152
|
CLI_EFFORT="high"; DEFAULT_TIMEOUT=900; RUN_MODE="bg"; OPUS_OVERSIGHT="false" ;;
|
|
108
153
|
deep-executor)
|
|
109
154
|
CLI_TYPE="codex"; CLI_CMD="codex"
|
|
110
|
-
CLI_ARGS="--profile xhigh
|
|
155
|
+
CLI_ARGS="exec --profile xhigh ${codex_base}"
|
|
111
156
|
CLI_EFFORT="xhigh"; DEFAULT_TIMEOUT=3600; RUN_MODE="bg"; OPUS_OVERSIGHT="true" ;;
|
|
112
157
|
|
|
113
158
|
# ─── 설계/분석 레인 ───
|
|
114
159
|
architect)
|
|
115
160
|
CLI_TYPE="codex"; CLI_CMD="codex"
|
|
116
|
-
CLI_ARGS="--profile xhigh
|
|
161
|
+
CLI_ARGS="exec --profile xhigh ${codex_base}"
|
|
117
162
|
CLI_EFFORT="xhigh"; DEFAULT_TIMEOUT=3600; RUN_MODE="bg"; OPUS_OVERSIGHT="true" ;;
|
|
118
163
|
planner)
|
|
119
164
|
CLI_TYPE="codex"; CLI_CMD="codex"
|
|
120
|
-
CLI_ARGS="--profile xhigh
|
|
165
|
+
CLI_ARGS="exec --profile xhigh ${codex_base}"
|
|
121
166
|
CLI_EFFORT="xhigh"; DEFAULT_TIMEOUT=3600; RUN_MODE="fg"; OPUS_OVERSIGHT="true" ;;
|
|
122
167
|
critic)
|
|
123
168
|
CLI_TYPE="codex"; CLI_CMD="codex"
|
|
124
|
-
CLI_ARGS="--profile xhigh
|
|
169
|
+
CLI_ARGS="exec --profile xhigh ${codex_base}"
|
|
125
170
|
CLI_EFFORT="xhigh"; DEFAULT_TIMEOUT=3600; RUN_MODE="bg"; OPUS_OVERSIGHT="true" ;;
|
|
126
171
|
analyst)
|
|
127
172
|
CLI_TYPE="codex"; CLI_CMD="codex"
|
|
128
|
-
CLI_ARGS="--profile xhigh
|
|
173
|
+
CLI_ARGS="exec --profile xhigh ${codex_base}"
|
|
129
174
|
CLI_EFFORT="xhigh"; DEFAULT_TIMEOUT=3600; RUN_MODE="fg"; OPUS_OVERSIGHT="true" ;;
|
|
130
175
|
|
|
131
176
|
# ─── 리뷰 레인 ───
|
|
132
177
|
code-reviewer)
|
|
133
178
|
CLI_TYPE="codex"; CLI_CMD="codex"
|
|
134
|
-
CLI_ARGS="--profile thorough
|
|
179
|
+
CLI_ARGS="exec --profile thorough ${codex_base} review"
|
|
135
180
|
CLI_EFFORT="thorough"; DEFAULT_TIMEOUT=1800; RUN_MODE="bg"; OPUS_OVERSIGHT="false" ;;
|
|
136
181
|
security-reviewer)
|
|
137
182
|
CLI_TYPE="codex"; CLI_CMD="codex"
|
|
138
|
-
CLI_ARGS="--profile thorough
|
|
183
|
+
CLI_ARGS="exec --profile thorough ${codex_base} review"
|
|
139
184
|
CLI_EFFORT="thorough"; DEFAULT_TIMEOUT=1800; RUN_MODE="bg"; OPUS_OVERSIGHT="true" ;;
|
|
140
185
|
quality-reviewer)
|
|
141
186
|
CLI_TYPE="codex"; CLI_CMD="codex"
|
|
142
|
-
CLI_ARGS="--profile thorough
|
|
187
|
+
CLI_ARGS="exec --profile thorough ${codex_base} review"
|
|
143
188
|
CLI_EFFORT="thorough"; DEFAULT_TIMEOUT=1800; RUN_MODE="bg"; OPUS_OVERSIGHT="false" ;;
|
|
144
189
|
|
|
145
190
|
# ─── 리서치 레인 ───
|
|
@@ -149,7 +194,7 @@ route_agent() {
|
|
|
149
194
|
CLI_EFFORT="high"; DEFAULT_TIMEOUT=1440; RUN_MODE="bg"; OPUS_OVERSIGHT="false" ;;
|
|
150
195
|
scientist-deep)
|
|
151
196
|
CLI_TYPE="codex"; CLI_CMD="codex"
|
|
152
|
-
CLI_ARGS="--profile thorough
|
|
197
|
+
CLI_ARGS="exec --profile thorough ${codex_base}"
|
|
153
198
|
CLI_EFFORT="thorough"; DEFAULT_TIMEOUT=3600; RUN_MODE="bg"; OPUS_OVERSIGHT="false" ;;
|
|
154
199
|
document-specialist)
|
|
155
200
|
CLI_TYPE="codex"; CLI_CMD="codex"
|
|
@@ -183,7 +228,7 @@ route_agent() {
|
|
|
183
228
|
# ─── 경량 ───
|
|
184
229
|
spark)
|
|
185
230
|
CLI_TYPE="codex"; CLI_CMD="codex"
|
|
186
|
-
CLI_ARGS="--profile spark_fast
|
|
231
|
+
CLI_ARGS="exec --profile spark_fast ${codex_base}"
|
|
187
232
|
CLI_EFFORT="spark_fast"; DEFAULT_TIMEOUT=180; RUN_MODE="fg"; OPUS_OVERSIGHT="false" ;;
|
|
188
233
|
*)
|
|
189
234
|
echo "ERROR: 알 수 없는 에이전트 타입: $agent" >&2
|
|
@@ -208,7 +253,7 @@ apply_cli_mode() {
|
|
|
208
253
|
designer)
|
|
209
254
|
CLI_ARGS="exec ${codex_base}"; CLI_EFFORT="high"; DEFAULT_TIMEOUT=600 ;;
|
|
210
255
|
writer)
|
|
211
|
-
CLI_ARGS="--profile spark_fast
|
|
256
|
+
CLI_ARGS="exec --profile spark_fast ${codex_base}"; CLI_EFFORT="spark_fast"; DEFAULT_TIMEOUT=180 ;;
|
|
212
257
|
esac
|
|
213
258
|
echo "[tfx-route] TFX_CLI_MODE=codex: $AGENT_TYPE → codex($CLI_EFFORT)로 리매핑" >&2
|
|
214
259
|
fi ;;
|
|
@@ -383,36 +428,20 @@ ${ctx_content}
|
|
|
383
428
|
local FULL_PROMPT="$PROMPT"
|
|
384
429
|
[[ -n "$mcp_hint" ]] && FULL_PROMPT="${PROMPT}. ${mcp_hint}"
|
|
385
430
|
|
|
386
|
-
# 메타정보 (stderr)
|
|
387
|
-
echo "[tfx-route] v${VERSION} type=$CLI_TYPE agent=$AGENT_TYPE effort=$CLI_EFFORT mode=$RUN_MODE timeout=${TIMEOUT_SEC}s" >&2
|
|
388
|
-
echo "[tfx-route] opus_oversight=$OPUS_OVERSIGHT mcp_profile=$MCP_PROFILE
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
--topics "$hub_topics" \
|
|
401
|
-
--capabilities "code,${AGENT_TYPE}"
|
|
402
|
-
|
|
403
|
-
# Hub 브릿지: 선행 컨텍스트 폴링 (DAG 의존 태스크용)
|
|
404
|
-
if [[ "$HUB_ENABLED" == "true" && -z "$CONTEXT_FILE" ]]; then
|
|
405
|
-
local hub_ctx_file="${TFX_TMP}/tfx-hub-ctx-${hub_agent_id}.md"
|
|
406
|
-
hub_bridge context --agent "$hub_agent_id" --topics "$hub_topics" --out "$hub_ctx_file"
|
|
407
|
-
if [[ -s "$hub_ctx_file" ]]; then
|
|
408
|
-
CONTEXT_FILE="$hub_ctx_file"
|
|
409
|
-
echo "[tfx-route] hub: 선행 컨텍스트 수신 ($(wc -c < "$hub_ctx_file") bytes)" >&2
|
|
410
|
-
fi
|
|
411
|
-
fi
|
|
412
|
-
|
|
413
|
-
# CLI 실행 (stderr 분리 + 타임아웃 + 소요시간 측정)
|
|
414
|
-
local exit_code=0
|
|
415
|
-
local start_time
|
|
431
|
+
# 메타정보 (stderr)
|
|
432
|
+
echo "[tfx-route] v${VERSION} type=$CLI_TYPE agent=$AGENT_TYPE effort=$CLI_EFFORT mode=$RUN_MODE timeout=${TIMEOUT_SEC}s" >&2
|
|
433
|
+
echo "[tfx-route] opus_oversight=$OPUS_OVERSIGHT mcp_profile=$MCP_PROFILE" >&2
|
|
434
|
+
[[ -n "$TFX_TEAM_NAME" ]] && echo "[tfx-route] team=$TFX_TEAM_NAME task=$TFX_TEAM_TASK_ID agent=$TFX_TEAM_AGENT_NAME" >&2
|
|
435
|
+
|
|
436
|
+
# Per-process 에이전트 등록
|
|
437
|
+
register_agent
|
|
438
|
+
|
|
439
|
+
# 팀 모드: task claim
|
|
440
|
+
team_claim_task
|
|
441
|
+
|
|
442
|
+
# CLI 실행 (stderr 분리 + 타임아웃 + 소요시간 측정)
|
|
443
|
+
local exit_code=0
|
|
444
|
+
local start_time
|
|
416
445
|
start_time=$(date +%s)
|
|
417
446
|
|
|
418
447
|
if [[ "$CLI_TYPE" == "codex" ]]; then
|
|
@@ -462,21 +491,28 @@ ${ctx_content}
|
|
|
462
491
|
fi
|
|
463
492
|
fi
|
|
464
493
|
|
|
465
|
-
local end_time
|
|
466
|
-
end_time=$(date +%s)
|
|
467
|
-
local elapsed=$((end_time - start_time))
|
|
468
|
-
|
|
469
|
-
#
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
494
|
+
local end_time
|
|
495
|
+
end_time=$(date +%s)
|
|
496
|
+
local elapsed=$((end_time - start_time))
|
|
497
|
+
|
|
498
|
+
# 팀 모드: task complete + 리드 보고
|
|
499
|
+
if [[ -n "$TFX_TEAM_NAME" ]]; then
|
|
500
|
+
if [[ "$exit_code" -eq 0 ]]; then
|
|
501
|
+
local output_preview
|
|
502
|
+
output_preview=$(head -c 2048 "$STDOUT_LOG" 2>/dev/null || echo "출력 없음")
|
|
503
|
+
team_complete_task "completed" "$output_preview"
|
|
504
|
+
elif [[ "$exit_code" -eq 124 ]]; then
|
|
505
|
+
team_complete_task "failed" "타임아웃 (${TIMEOUT_SEC}초)"
|
|
506
|
+
else
|
|
507
|
+
local err_preview
|
|
508
|
+
err_preview=$(tail -c 1024 "$STDERR_LOG" 2>/dev/null || echo "에러 정보 없음")
|
|
509
|
+
team_complete_task "failed" "exit_code=${exit_code}: ${err_preview}"
|
|
510
|
+
fi
|
|
511
|
+
fi
|
|
512
|
+
|
|
513
|
+
# ── 후처리: 단일 node 프로세스로 위임 ──
|
|
514
|
+
# 토큰 추출, 출력 필터링, 로그, 토큰 누적, AIMD, 이슈 추적, 결과 출력 전부 처리
|
|
515
|
+
local post_script="${HOME}/.claude/scripts/tfx-route-post.mjs"
|
|
480
516
|
if [[ -f "$post_script" ]]; then
|
|
481
517
|
node "$post_script" \
|
|
482
518
|
--agent "$AGENT_TYPE" \
|