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/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
+ }
@@ -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
- # ── Hub 브릿지 (선택적 — Hub 미실행 시 무시) ──
42
- # 패키지 내 브릿지 탐색 (npm global / git local 모두 대응)
43
- find_bridge() {
44
- # 1. 환경변수 지정
45
- [[ -n "${TFX_BRIDGE:-}" && -f "$TFX_BRIDGE" ]] && echo "$TFX_BRIDGE" && return
46
- # 2. 같은 패키지 내
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
- # 반환: CLI_CMD, CLI_ARGS, CLI_TYPE, CLI_EFFORT, DEFAULT_TIMEOUT, RUN_MODE, OPUS_OVERSIGHT
90
- route_agent() {
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 exec ${codex_base}"
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 exec ${codex_base}"
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 exec ${codex_base}"
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 exec ${codex_base}"
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 exec ${codex_base}"
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 exec ${codex_base}"
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 exec ${codex_base} review"
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 exec ${codex_base} review"
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 exec ${codex_base} review"
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 exec ${codex_base}"
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 exec ${codex_base}"
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 exec ${codex_base}"; CLI_EFFORT="spark_fast"; DEFAULT_TIMEOUT=180 ;;
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 hub=$HUB_ENABLED" >&2
389
-
390
- # Per-process 에이전트 등록
391
- register_agent
392
-
393
- # Hub 브릿지: 에이전트 등록 (프로세스 수명 기반 lease)
394
- local hub_agent_id="${AGENT_TYPE}-$$"
395
- local hub_topics="${AGENT_TYPE},task.result"
396
- hub_bridge register \
397
- --agent "$hub_agent_id" \
398
- --cli "$CLI_TYPE" \
399
- --timeout "$TIMEOUT_SEC" \
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
- # Hub 브릿지: 결과 발행 + 에이전트 해제
470
- hub_bridge result \
471
- --agent "$hub_agent_id" \
472
- --file "$STDOUT_LOG" \
473
- --topic "task.result" \
474
- --exit-code "$exit_code"
475
- hub_bridge deregister --agent "$hub_agent_id"
476
-
477
- # ── 후처리: 단일 node 프로세스로 위임 ──
478
- # 토큰 추출, 출력 필터링, 로그, 토큰 누적, AIMD, 이슈 추적, 결과 출력 전부 처리
479
- local post_script="${HOME}/.claude/scripts/tfx-route-post.mjs"
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" \