triflux 2.5.1 → 3.1.0-dev.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.
@@ -0,0 +1,448 @@
1
+ #!/usr/bin/env bash
2
+ # tfx-route.sh v2.0 — CLI 라우팅 래퍼 (triflux)
3
+ #
4
+ # v1.x: cli-route.sh (jq+python3+node 혼재, 동기 후처리 ~1s)
5
+ # v2.0: tfx-route.sh 리네임
6
+ # - 후처리 전부 tfx-route-post.mjs로 이관 (node 단일 ~100ms)
7
+ # - per-process 에이전트 등록 (race condition 구조적 제거)
8
+ # - get_mcp_hint 통합 (캐시/비캐시 단일 코드경로)
9
+ # - Gemini health check 지수 백오프 (30×1s → 5×exp)
10
+ # - 컨텍스트 파일 5번째 인자 지원
11
+ #
12
+ VERSION="2.0"
13
+ #
14
+ # 사용법:
15
+ # tfx-route.sh <agent_type> <prompt> [mcp_profile] [timeout_sec] [context_file]
16
+ #
17
+ # 예시:
18
+ # tfx-route.sh executor "코드 구현" implement
19
+ # tfx-route.sh architect "아키텍처 분석" analyze '' context.md
20
+
21
+ set -euo pipefail
22
+
23
+ # ── 인자 파싱 ──
24
+ AGENT_TYPE="${1:?에이전트 타입 필수 (executor, debugger, designer 등)}"
25
+ PROMPT="${2:?프롬프트 필수}"
26
+ MCP_PROFILE="${3:-auto}"
27
+ USER_TIMEOUT="${4:-}"
28
+ CONTEXT_FILE="${5:-}"
29
+
30
+ # ── CLI 경로 해석 (Windows npm global 대응) ──
31
+ CODEX_BIN="${CODEX_BIN:-$(command -v codex 2>/dev/null || echo codex)}"
32
+ GEMINI_BIN="${GEMINI_BIN:-$(command -v gemini 2>/dev/null || echo gemini)}"
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
+ # fallback 시 원래 에이전트 정보 보존
42
+ ORIGINAL_AGENT=""
43
+ ORIGINAL_CLI_ARGS=""
44
+
45
+ # ── Per-process 에이전트 등록 (원자적, 락 불필요) ──
46
+ register_agent() {
47
+ local agent_file="${TFX_TMP}/tfx-agent-$$.json"
48
+ echo "{\"pid\":$$,\"cli\":\"$CLI_TYPE\",\"agent\":\"$AGENT_TYPE\",\"started\":$(date +%s)}" \
49
+ > "$agent_file" 2>/dev/null || true
50
+ }
51
+
52
+ deregister_agent() {
53
+ rm -f "${TFX_TMP}/tfx-agent-$$.json" 2>/dev/null || true
54
+ }
55
+
56
+ # ── 라우팅 테이블 ──
57
+ # 반환: CLI_CMD, CLI_ARGS, CLI_TYPE, CLI_EFFORT, DEFAULT_TIMEOUT, RUN_MODE, OPUS_OVERSIGHT
58
+ route_agent() {
59
+ local agent="$1"
60
+ local codex_base="--dangerously-bypass-approvals-and-sandbox --skip-git-repo-check"
61
+
62
+ case "$agent" in
63
+ # ─── 구현 레인 ───
64
+ executor)
65
+ CLI_TYPE="codex"; CLI_CMD="codex"
66
+ CLI_ARGS="exec ${codex_base}"
67
+ CLI_EFFORT="high"; DEFAULT_TIMEOUT=1080; RUN_MODE="fg"; OPUS_OVERSIGHT="false" ;;
68
+ build-fixer)
69
+ CLI_TYPE="codex"; CLI_CMD="codex"
70
+ CLI_ARGS="--profile fast exec ${codex_base}"
71
+ CLI_EFFORT="fast"; DEFAULT_TIMEOUT=540; RUN_MODE="fg"; OPUS_OVERSIGHT="false" ;;
72
+ debugger)
73
+ CLI_TYPE="codex"; CLI_CMD="codex"
74
+ CLI_ARGS="exec ${codex_base}"
75
+ CLI_EFFORT="high"; DEFAULT_TIMEOUT=900; RUN_MODE="bg"; OPUS_OVERSIGHT="false" ;;
76
+ deep-executor)
77
+ CLI_TYPE="codex"; CLI_CMD="codex"
78
+ CLI_ARGS="--profile xhigh exec ${codex_base}"
79
+ CLI_EFFORT="xhigh"; DEFAULT_TIMEOUT=3600; RUN_MODE="bg"; OPUS_OVERSIGHT="true" ;;
80
+
81
+ # ─── 설계/분석 레인 ───
82
+ architect)
83
+ CLI_TYPE="codex"; CLI_CMD="codex"
84
+ CLI_ARGS="--profile xhigh exec ${codex_base}"
85
+ CLI_EFFORT="xhigh"; DEFAULT_TIMEOUT=3600; RUN_MODE="bg"; OPUS_OVERSIGHT="true" ;;
86
+ planner)
87
+ CLI_TYPE="codex"; CLI_CMD="codex"
88
+ CLI_ARGS="--profile xhigh exec ${codex_base}"
89
+ CLI_EFFORT="xhigh"; DEFAULT_TIMEOUT=3600; RUN_MODE="fg"; OPUS_OVERSIGHT="true" ;;
90
+ critic)
91
+ CLI_TYPE="codex"; CLI_CMD="codex"
92
+ CLI_ARGS="--profile xhigh exec ${codex_base}"
93
+ CLI_EFFORT="xhigh"; DEFAULT_TIMEOUT=3600; RUN_MODE="bg"; OPUS_OVERSIGHT="true" ;;
94
+ analyst)
95
+ CLI_TYPE="codex"; CLI_CMD="codex"
96
+ CLI_ARGS="--profile xhigh exec ${codex_base}"
97
+ CLI_EFFORT="xhigh"; DEFAULT_TIMEOUT=3600; RUN_MODE="fg"; OPUS_OVERSIGHT="true" ;;
98
+
99
+ # ─── 리뷰 레인 ───
100
+ code-reviewer)
101
+ CLI_TYPE="codex"; CLI_CMD="codex"
102
+ CLI_ARGS="--profile thorough exec ${codex_base} review"
103
+ CLI_EFFORT="thorough"; DEFAULT_TIMEOUT=1800; RUN_MODE="bg"; OPUS_OVERSIGHT="false" ;;
104
+ security-reviewer)
105
+ CLI_TYPE="codex"; CLI_CMD="codex"
106
+ CLI_ARGS="--profile thorough exec ${codex_base} review"
107
+ CLI_EFFORT="thorough"; DEFAULT_TIMEOUT=1800; RUN_MODE="bg"; OPUS_OVERSIGHT="true" ;;
108
+ quality-reviewer)
109
+ CLI_TYPE="codex"; CLI_CMD="codex"
110
+ CLI_ARGS="--profile thorough exec ${codex_base} review"
111
+ CLI_EFFORT="thorough"; DEFAULT_TIMEOUT=1800; RUN_MODE="bg"; OPUS_OVERSIGHT="false" ;;
112
+
113
+ # ─── 리서치 레인 ───
114
+ scientist)
115
+ CLI_TYPE="codex"; CLI_CMD="codex"
116
+ CLI_ARGS="exec ${codex_base}"
117
+ CLI_EFFORT="high"; DEFAULT_TIMEOUT=1440; RUN_MODE="bg"; OPUS_OVERSIGHT="false" ;;
118
+ scientist-deep)
119
+ CLI_TYPE="codex"; CLI_CMD="codex"
120
+ CLI_ARGS="--profile thorough exec ${codex_base}"
121
+ CLI_EFFORT="thorough"; DEFAULT_TIMEOUT=3600; RUN_MODE="bg"; OPUS_OVERSIGHT="false" ;;
122
+ document-specialist)
123
+ CLI_TYPE="codex"; CLI_CMD="codex"
124
+ CLI_ARGS="exec ${codex_base}"
125
+ CLI_EFFORT="high"; DEFAULT_TIMEOUT=1440; RUN_MODE="bg"; OPUS_OVERSIGHT="false" ;;
126
+
127
+ # ─── UI/문서 레인 ───
128
+ designer)
129
+ CLI_TYPE="gemini"; CLI_CMD="gemini"
130
+ CLI_ARGS="-m gemini-3.1-pro-preview -y --prompt"
131
+ CLI_EFFORT="pro"; DEFAULT_TIMEOUT=900; RUN_MODE="bg"; OPUS_OVERSIGHT="false" ;;
132
+ writer)
133
+ CLI_TYPE="gemini"; CLI_CMD="gemini"
134
+ CLI_ARGS="-m gemini-3-flash-preview -y --prompt"
135
+ CLI_EFFORT="flash"; DEFAULT_TIMEOUT=900; RUN_MODE="bg"; OPUS_OVERSIGHT="false" ;;
136
+
137
+ # ─── Claude 네이티브 ───
138
+ explore)
139
+ CLI_TYPE="claude-native"; CLI_CMD=""; CLI_ARGS=""
140
+ CLI_EFFORT="n/a"; DEFAULT_TIMEOUT=300; RUN_MODE="fg"; OPUS_OVERSIGHT="false" ;;
141
+ verifier)
142
+ CLI_TYPE="claude-native"; CLI_CMD=""; CLI_ARGS=""
143
+ CLI_EFFORT="n/a"; DEFAULT_TIMEOUT=300; RUN_MODE="fg"; OPUS_OVERSIGHT="false" ;;
144
+ test-engineer)
145
+ CLI_TYPE="claude-native"; CLI_CMD=""; CLI_ARGS=""
146
+ CLI_EFFORT="n/a"; DEFAULT_TIMEOUT=300; RUN_MODE="bg"; OPUS_OVERSIGHT="false" ;;
147
+ qa-tester)
148
+ CLI_TYPE="claude-native"; CLI_CMD=""; CLI_ARGS=""
149
+ CLI_EFFORT="n/a"; DEFAULT_TIMEOUT=300; RUN_MODE="bg"; OPUS_OVERSIGHT="false" ;;
150
+
151
+ # ─── 경량 ───
152
+ spark)
153
+ CLI_TYPE="codex"; CLI_CMD="codex"
154
+ CLI_ARGS="--profile spark_fast exec ${codex_base}"
155
+ CLI_EFFORT="spark_fast"; DEFAULT_TIMEOUT=180; RUN_MODE="fg"; OPUS_OVERSIGHT="false" ;;
156
+ *)
157
+ echo "ERROR: 알 수 없는 에이전트 타입: $agent" >&2
158
+ echo "사용 가능: executor, build-fixer, debugger, deep-executor, architect, planner, critic, analyst," >&2
159
+ echo " code-reviewer, security-reviewer, quality-reviewer, scientist, document-specialist," >&2
160
+ echo " designer, writer, explore, verifier, test-engineer, qa-tester, spark" >&2
161
+ exit 1 ;;
162
+ esac
163
+ }
164
+
165
+ # ── CLI 모드 오버라이드 (tfx-codex / tfx-gemini 스킬용) ──
166
+ TFX_CLI_MODE="${TFX_CLI_MODE:-auto}"
167
+
168
+ apply_cli_mode() {
169
+ local codex_base="--dangerously-bypass-approvals-and-sandbox --skip-git-repo-check"
170
+
171
+ case "$TFX_CLI_MODE" in
172
+ codex)
173
+ if [[ "$CLI_TYPE" == "gemini" ]]; then
174
+ CLI_TYPE="codex"; CLI_CMD="codex"
175
+ case "$AGENT_TYPE" in
176
+ designer)
177
+ CLI_ARGS="exec ${codex_base}"; CLI_EFFORT="high"; DEFAULT_TIMEOUT=600 ;;
178
+ writer)
179
+ CLI_ARGS="--profile spark_fast exec ${codex_base}"; CLI_EFFORT="spark_fast"; DEFAULT_TIMEOUT=180 ;;
180
+ esac
181
+ echo "[tfx-route] TFX_CLI_MODE=codex: $AGENT_TYPE → codex($CLI_EFFORT)로 리매핑" >&2
182
+ fi ;;
183
+ gemini)
184
+ if [[ "$CLI_TYPE" == "codex" ]]; then
185
+ CLI_TYPE="gemini"; CLI_CMD="gemini"
186
+ case "$AGENT_TYPE" in
187
+ executor|debugger|deep-executor|architect|planner|critic|analyst|\
188
+ code-reviewer|security-reviewer|quality-reviewer|scientist-deep)
189
+ CLI_ARGS="-m gemini-3.1-pro-preview -y --prompt"; CLI_EFFORT="pro" ;;
190
+ build-fixer|spark)
191
+ CLI_ARGS="-m gemini-3-flash-preview -y --prompt"; CLI_EFFORT="flash"; DEFAULT_TIMEOUT=180 ;;
192
+ *)
193
+ CLI_ARGS="-m gemini-3-flash-preview -y --prompt"; CLI_EFFORT="flash" ;;
194
+ esac
195
+ echo "[tfx-route] TFX_CLI_MODE=gemini: $AGENT_TYPE → gemini($CLI_EFFORT)로 리매핑" >&2
196
+ fi ;;
197
+ auto)
198
+ if [[ "$CLI_TYPE" == "codex" ]] && ! command -v "$CODEX_BIN" &>/dev/null; then
199
+ if command -v "$GEMINI_BIN" &>/dev/null; then
200
+ TFX_CLI_MODE="gemini"; apply_cli_mode; return
201
+ else
202
+ ORIGINAL_AGENT="${AGENT_TYPE}"; ORIGINAL_CLI_ARGS="$CLI_ARGS"
203
+ CLI_TYPE="claude-native"; CLI_CMD=""; CLI_ARGS=""
204
+ echo "[tfx-route] codex/gemini 모두 미설치: $AGENT_TYPE → claude-native fallback" >&2
205
+ fi
206
+ elif [[ "$CLI_TYPE" == "gemini" ]] && ! command -v "$GEMINI_BIN" &>/dev/null; then
207
+ if command -v "$CODEX_BIN" &>/dev/null; then
208
+ TFX_CLI_MODE="codex"; apply_cli_mode; return
209
+ else
210
+ ORIGINAL_AGENT="${AGENT_TYPE}"; ORIGINAL_CLI_ARGS="$CLI_ARGS"
211
+ CLI_TYPE="claude-native"; CLI_CMD=""; CLI_ARGS=""
212
+ echo "[tfx-route] codex/gemini 모두 미설치: $AGENT_TYPE → claude-native fallback" >&2
213
+ fi
214
+ fi ;;
215
+ esac
216
+ }
217
+
218
+ # ── MCP 인벤토리 캐시 ──
219
+ MCP_CACHE="${HOME}/.claude/cache/mcp-inventory.json"
220
+
221
+ get_cached_servers() {
222
+ local cli_type="$1"
223
+ if [[ -f "$MCP_CACHE" ]]; then
224
+ node -e 'const[,f,t]=process.argv;const inv=JSON.parse(require("fs").readFileSync(f,"utf8"));const s=(inv[t]||{}).servers||[];console.log(s.filter(x=>x.status==="enabled"||x.status==="configured").map(x=>x.name).join(","))' -- "$MCP_CACHE" "$cli_type" 2>/dev/null
225
+ fi
226
+ }
227
+
228
+ # ── MCP 프로필 → 프롬프트 힌트 (통합: 캐시 유무 단일 코드경로) ──
229
+ get_mcp_hint() {
230
+ local profile="$1"
231
+ local agent="$2"
232
+
233
+ # auto → 구체 프로필 해석
234
+ if [[ "$profile" == "auto" ]]; then
235
+ case "$agent" in
236
+ executor|build-fixer|debugger|deep-executor) profile="implement" ;;
237
+ architect|planner|critic|analyst) profile="analyze" ;;
238
+ code-reviewer|security-reviewer|quality-reviewer) profile="review" ;;
239
+ scientist|document-specialist) profile="analyze" ;;
240
+ designer|writer) profile="docs" ;;
241
+ *) profile="minimal" ;;
242
+ esac
243
+ fi
244
+
245
+ # 서버 목록: 캐시 있으면 실제, 없으면 전부 가용 가정 (기존 비캐시 동작과 동일)
246
+ local servers
247
+ servers=$(get_cached_servers "$CLI_TYPE")
248
+ [[ -z "$servers" ]] && servers="context7,brave-search,exa,tavily,playwright,sequential-thinking"
249
+
250
+ has_server() { echo ",$servers," | grep -q ",$1,"; }
251
+
252
+ local hint=""
253
+ case "$profile" in
254
+ implement)
255
+ has_server "context7" && hint+="context7으로 라이브러리 문서를 조회하세요. "
256
+ if has_server "brave-search"; then hint+="웹 검색은 brave-search를 사용하세요. "
257
+ elif has_server "exa"; then hint+="웹 검색은 exa를 사용하세요. "
258
+ elif has_server "tavily"; then hint+="웹 검색은 tavily를 사용하세요. "
259
+ fi
260
+ hint+="검색 도구 실패 시 재시도하지 말고 다음 도구로 전환하세요."
261
+ ;;
262
+ analyze)
263
+ has_server "context7" && hint+="context7으로 관련 문서를 조회하세요. "
264
+ local search_tools=""
265
+ has_server "brave-search" && search_tools+="brave-search, "
266
+ has_server "tavily" && search_tools+="tavily, "
267
+ has_server "exa" && search_tools+="exa, "
268
+ [[ -n "$search_tools" ]] && hint+="웹 검색 우선순위: ${search_tools%, }. 402 에러 시 즉시 다음 도구로 전환. "
269
+ has_server "playwright" && hint+="모든 검색 실패 시 playwright로 직접 방문 (최대 3 URL). "
270
+ hint+="검색 깊이를 제한하고 결과를 빠르게 요약하세요."
271
+ ;;
272
+ review)
273
+ has_server "sequential-thinking" && hint="sequential-thinking으로 체계적으로 분석하세요."
274
+ ;;
275
+ docs)
276
+ has_server "context7" && hint+="context7으로 공식 문서를 참조하세요. "
277
+ has_server "brave-search" && hint+="추가 검색은 brave-search를 사용하세요. "
278
+ hint+="검색 결과의 출처 URL을 함께 제시하세요."
279
+ ;;
280
+ minimal|none) ;;
281
+ esac
282
+ echo "$hint"
283
+ }
284
+
285
+ # ── Gemini MCP 서버 선택적 로드 ──
286
+ get_gemini_mcp_filter() {
287
+ local profile="$1"
288
+ case "$profile" in
289
+ implement) echo "--allowed-mcp-server-names context7,brave-search" ;;
290
+ analyze) echo "--allowed-mcp-server-names context7,brave-search,exa" ;;
291
+ review) echo "--allowed-mcp-server-names sequential-thinking" ;;
292
+ docs) echo "--allowed-mcp-server-names context7,brave-search" ;;
293
+ *) echo "" ;;
294
+ esac
295
+ }
296
+
297
+ # ── 메인 실행 ──
298
+ main() {
299
+ # 종료 시 per-process 에이전트 파일 자동 삭제
300
+ trap 'deregister_agent' EXIT
301
+
302
+ route_agent "$AGENT_TYPE"
303
+ apply_cli_mode
304
+
305
+ # CLI 경로 해석
306
+ case "$CLI_CMD" in
307
+ codex) CLI_CMD="$CODEX_BIN" ;;
308
+ gemini) CLI_CMD="$GEMINI_BIN" ;;
309
+ esac
310
+
311
+ # 타임아웃 결정
312
+ if [[ -n "$USER_TIMEOUT" ]]; then
313
+ TIMEOUT_SEC="$USER_TIMEOUT"
314
+ else
315
+ TIMEOUT_SEC="$DEFAULT_TIMEOUT"
316
+ fi
317
+
318
+ # 컨텍스트 파일 → 프롬프트에 주입
319
+ if [[ -n "$CONTEXT_FILE" && -f "$CONTEXT_FILE" ]]; then
320
+ local ctx_content
321
+ ctx_content=$(cat "$CONTEXT_FILE" 2>/dev/null | head -c 32768) # 32KB 상한
322
+ PROMPT="${PROMPT}
323
+
324
+ <prior_context>
325
+ ${ctx_content}
326
+ </prior_context>"
327
+ fi
328
+
329
+ # Claude 네이티브 에이전트는 이 스크립트로 처리 불가 → 메타데이터만 출력
330
+ if [[ "$CLI_TYPE" == "claude-native" ]]; then
331
+ local model="sonnet"
332
+ case "$AGENT_TYPE" in
333
+ explore) model="haiku" ;;
334
+ esac
335
+ echo "ROUTE_TYPE=claude-native"
336
+ echo "AGENT=$AGENT_TYPE"
337
+ echo "MODEL=$model"
338
+ echo "RUN_MODE=$RUN_MODE"
339
+ echo "OPUS_OVERSIGHT=$OPUS_OVERSIGHT"
340
+ echo "TIMEOUT=$TIMEOUT_SEC"
341
+ echo "MCP_PROFILE=$MCP_PROFILE"
342
+ [[ -n "$ORIGINAL_AGENT" ]] && echo "ORIGINAL_AGENT=$ORIGINAL_AGENT"
343
+ echo "PROMPT=$PROMPT"
344
+ echo "--- Claude Task($model) 에이전트로 위임하세요 ---"
345
+ exit 0
346
+ fi
347
+
348
+ # MCP 힌트 주입
349
+ local mcp_hint
350
+ mcp_hint=$(get_mcp_hint "$MCP_PROFILE" "$AGENT_TYPE")
351
+ local FULL_PROMPT="$PROMPT"
352
+ [[ -n "$mcp_hint" ]] && FULL_PROMPT="${PROMPT}. ${mcp_hint}"
353
+
354
+ # 메타정보 (stderr)
355
+ echo "[tfx-route] v${VERSION} type=$CLI_TYPE agent=$AGENT_TYPE effort=$CLI_EFFORT mode=$RUN_MODE timeout=${TIMEOUT_SEC}s" >&2
356
+ echo "[tfx-route] opus_oversight=$OPUS_OVERSIGHT mcp_profile=$MCP_PROFILE" >&2
357
+
358
+ # Per-process 에이전트 등록
359
+ register_agent
360
+
361
+ # CLI 실행 (stderr 분리 + 타임아웃 + 소요시간 측정)
362
+ local exit_code=0
363
+ local start_time
364
+ start_time=$(date +%s)
365
+
366
+ if [[ "$CLI_TYPE" == "codex" ]]; then
367
+ # Codex: stdout/stderr 모두 파일로 캡처 (post.mjs가 읽음)
368
+ timeout "$TIMEOUT_SEC" $CLI_CMD $CLI_ARGS "$FULL_PROMPT" >"$STDOUT_LOG" 2>"$STDERR_LOG" || exit_code=$?
369
+
370
+ elif [[ "$CLI_TYPE" == "gemini" ]]; then
371
+ # Gemini: MCP 프로필별 서버 필터
372
+ local gemini_mcp_filter
373
+ gemini_mcp_filter=$(get_gemini_mcp_filter "$MCP_PROFILE")
374
+ local gemini_args="$CLI_ARGS"
375
+ if [[ -n "$gemini_mcp_filter" ]]; then
376
+ gemini_args="${CLI_ARGS/--prompt/$gemini_mcp_filter --prompt}"
377
+ echo "[tfx-route] Gemini MCP 필터: $gemini_mcp_filter" >&2
378
+ fi
379
+
380
+ timeout "$TIMEOUT_SEC" $CLI_CMD $gemini_args "$FULL_PROMPT" >"$STDOUT_LOG" 2>"$STDERR_LOG" &
381
+ local pid=$!
382
+
383
+ # 지수 백오프 health check (v1.x: 30×1s → v2.0: 5×exp, 총 19초)
384
+ local health_ok=true
385
+ local intervals=(1 2 3 5 8)
386
+ for wait_sec in "${intervals[@]}"; do
387
+ sleep "$wait_sec"
388
+ # 출력 있으면 정상 → 조기 탈출
389
+ if [[ -s "$STDOUT_LOG" ]] || [[ -s "$STDERR_LOG" ]]; then
390
+ break
391
+ fi
392
+ # 프로세스 사망 + 출력 없음 → crash
393
+ if ! kill -0 "$pid" 2>/dev/null; then
394
+ health_ok=false
395
+ echo "[tfx-route] Gemini: 출력 없이 프로세스 종료 (${wait_sec}초 체크)" >&2
396
+ break
397
+ fi
398
+ done
399
+
400
+ if [[ "$health_ok" == "false" ]]; then
401
+ wait "$pid" 2>/dev/null
402
+ echo "[tfx-route] Gemini crash 감지, 재시도 중..." >&2
403
+ timeout "$TIMEOUT_SEC" $CLI_CMD $gemini_args "$FULL_PROMPT" >"$STDOUT_LOG" 2>"$STDERR_LOG" &
404
+ pid=$!
405
+ wait "$pid"
406
+ exit_code=$?
407
+ else
408
+ wait "$pid"
409
+ exit_code=$?
410
+ fi
411
+ fi
412
+
413
+ local end_time
414
+ end_time=$(date +%s)
415
+ local elapsed=$((end_time - start_time))
416
+
417
+ # ── 후처리: 단일 node 프로세스로 위임 ──
418
+ # 토큰 추출, 출력 필터링, 로그, 토큰 누적, AIMD, 이슈 추적, 결과 출력 전부 처리
419
+ local post_script="${HOME}/.claude/scripts/tfx-route-post.mjs"
420
+ if [[ -f "$post_script" ]]; then
421
+ node "$post_script" \
422
+ --agent "$AGENT_TYPE" \
423
+ --cli "$CLI_TYPE" \
424
+ --cli-cmd "$CLI_CMD" \
425
+ --effort "$CLI_EFFORT" \
426
+ --run-mode "$RUN_MODE" \
427
+ --opus "$OPUS_OVERSIGHT" \
428
+ --exit-code "$exit_code" \
429
+ --elapsed "$elapsed" \
430
+ --timeout "$TIMEOUT_SEC" \
431
+ --mcp-profile "$MCP_PROFILE" \
432
+ --stderr-log "$STDERR_LOG" \
433
+ --stdout-log "$STDOUT_LOG" \
434
+ --max-bytes "$MAX_STDOUT_BYTES"
435
+ else
436
+ # post.mjs 없으면 기본 출력 (fallback)
437
+ echo "=== TFX-ROUTE RESULT ==="
438
+ echo "agent: $AGENT_TYPE"
439
+ echo "cli: $CLI_TYPE"
440
+ echo "exit_code: $exit_code"
441
+ echo "elapsed: ${elapsed}s"
442
+ echo "status: $([ $exit_code -eq 0 ] && echo success || echo failed)"
443
+ echo "=== OUTPUT ==="
444
+ cat "$STDOUT_LOG" 2>/dev/null | head -c "$MAX_STDOUT_BYTES"
445
+ fi
446
+ }
447
+
448
+ main
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: tfx-auto
3
- description: 통합 CLI 오케스트레이터. 커맨드 숏컷(단일) + 자동 분류/분해(병렬) + 수동 병렬. cli-route.sh 기반.
3
+ description: 통합 CLI 오케스트레이터. 커맨드 숏컷(단일) + 자동 분류/분해(병렬) + 수동 병렬. tfx-route.sh 기반.
4
4
  triggers:
5
5
  - tfx-auto
6
6
  - implement
@@ -29,9 +29,9 @@ argument-hint: "<command|task> [args...]"
29
29
  > **MANDATORY EXECUTION RULE — 이 규칙을 반드시 따르라**
30
30
  >
31
31
  > **작업 실행 시** (Phase 3), **절대 Task()를 직접 호출하지 마라.**
32
- > 아래 Claude 네이티브 유지 대상을 제외한 **모든 에이전트는 반드시 Bash("bash ~/.claude/scripts/cli-route.sh ...")로 실행**하라.
32
+ > 아래 Claude 네이티브 유지 대상을 제외한 **모든 에이전트는 반드시 Bash("bash ~/.claude/scripts/tfx-route.sh ...")로 실행**하라.
33
33
  >
34
- > **Bash(cli-route.sh)로 실행해야 하는 에이전트:**
34
+ > **Bash(tfx-route.sh)로 실행해야 하는 에이전트:**
35
35
  > executor, build-fixer, debugger, deep-executor, architect, planner, critic, analyst,
36
36
  > code-reviewer, security-reviewer, quality-reviewer, scientist, document-specialist,
37
37
  > designer, writer
@@ -61,7 +61,7 @@ argument-hint: "<command|task> [args...]"
61
61
  > 1. graph_type이 SEQUENTIAL 또는 DAG이면 → **반드시 레벨 기반 순차 실행**
62
62
  > 2. `.omc/context/{session_id}/` 디렉토리 생성했는가?
63
63
  > 3. Level 0 완료 후 context_output 파일을 저장했는가?
64
- > 4. Level 1+ 태스크에 cli-route.sh **5번째 인자로 context file**을 전달했는가?
64
+ > 4. Level 1+ 태스크에 tfx-route.sh **5번째 인자로 context file**을 전달했는가?
65
65
  > 5. 실패한 태스크의 후속 의존 태스크를 SKIP 처리했는가?
66
66
 
67
67
  ## 3가지 모드
@@ -82,8 +82,8 @@ argument-hint: "<command|task> [args...]"
82
82
 
83
83
  ## 커맨드 숏컷 (트리아지 없이 즉시 실행)
84
84
 
85
- 커맨드명이 매칭되면 트리아지를 건너뛰고 cli-route.sh로 즉시 라우팅.
86
- 타임아웃은 cli-route.sh 에이전트별 기본값 사용.
85
+ 커맨드명이 매칭되면 트리아지를 건너뛰고 tfx-route.sh로 즉시 라우팅.
86
+ 타임아웃은 tfx-route.sh 에이전트별 기본값 사용.
87
87
 
88
88
  ### Codex 직행
89
89
 
@@ -99,7 +99,7 @@ argument-hint: "<command|task> [args...]"
99
99
  | `pm` | planner | analyze |
100
100
 
101
101
  ```bash
102
- Bash("bash ~/.claude/scripts/cli-route.sh {에이전트} '{PROMPT}' {MCP프로필}")
102
+ Bash("bash ~/.claude/scripts/tfx-route.sh {에이전트} '{PROMPT}' {MCP프로필}")
103
103
  ```
104
104
 
105
105
  ### Codex 2단계 (분석 → 실행)
@@ -110,10 +110,10 @@ Bash("bash ~/.claude/scripts/cli-route.sh {에이전트} '{PROMPT}' {MCP프로
110
110
 
111
111
  ```bash
112
112
  # 1단계 — 분석
113
- Bash("bash ~/.claude/scripts/cli-route.sh code-reviewer '{PROMPT}' review")
113
+ Bash("bash ~/.claude/scripts/tfx-route.sh code-reviewer '{PROMPT}' review")
114
114
  # 사용자에게 분석 결과 보고, 승인 요청
115
115
  # 2단계 — 수정 (승인 시)
116
- Bash("bash ~/.claude/scripts/cli-route.sh executor '리뷰 결과 반영: {1단계_결과_요약}' implement")
116
+ Bash("bash ~/.claude/scripts/tfx-route.sh executor '리뷰 결과 반영: {1단계_결과_요약}' implement")
117
117
  ```
118
118
 
119
119
  ### Codex 병렬 (동시 다중 에이전트)
@@ -126,17 +126,17 @@ Bash("bash ~/.claude/scripts/cli-route.sh executor '리뷰 결과 반영: {1단
126
126
 
127
127
  ```bash
128
128
  # analyze — 2개 병렬
129
- Bash("bash ~/.claude/scripts/cli-route.sh quality-reviewer '{PROMPT}' review", run_in_background=true)
130
- Bash("bash ~/.claude/scripts/cli-route.sh security-reviewer '{PROMPT}' review", run_in_background=true)
129
+ Bash("bash ~/.claude/scripts/tfx-route.sh quality-reviewer '{PROMPT}' review", run_in_background=true)
130
+ Bash("bash ~/.claude/scripts/tfx-route.sh security-reviewer '{PROMPT}' review", run_in_background=true)
131
131
 
132
132
  # spec-panel — 3개 병렬
133
- Bash("bash ~/.claude/scripts/cli-route.sh architect '기술 실현성 평가: {PROMPT}' analyze", run_in_background=true)
134
- Bash("bash ~/.claude/scripts/cli-route.sh analyst '요구사항 완전성 평가: {PROMPT}' analyze", run_in_background=true)
135
- Bash("bash ~/.claude/scripts/cli-route.sh critic '비판적 검토: {PROMPT}' analyze", run_in_background=true)
133
+ Bash("bash ~/.claude/scripts/tfx-route.sh architect '기술 실현성 평가: {PROMPT}' analyze", run_in_background=true)
134
+ Bash("bash ~/.claude/scripts/tfx-route.sh analyst '요구사항 완전성 평가: {PROMPT}' analyze", run_in_background=true)
135
+ Bash("bash ~/.claude/scripts/tfx-route.sh critic '비판적 검토: {PROMPT}' analyze", run_in_background=true)
136
136
 
137
137
  # business-panel — 2개 병렬
138
- Bash("bash ~/.claude/scripts/cli-route.sh analyst '시장/전략 분석: {PROMPT}' analyze", run_in_background=true)
139
- Bash("bash ~/.claude/scripts/cli-route.sh architect '기술-비즈니스 연계 분석: {PROMPT}' analyze", run_in_background=true)
138
+ Bash("bash ~/.claude/scripts/tfx-route.sh analyst '시장/전략 분석: {PROMPT}' analyze", run_in_background=true)
139
+ Bash("bash ~/.claude/scripts/tfx-route.sh architect '기술-비즈니스 연계 분석: {PROMPT}' analyze", run_in_background=true)
140
140
  ```
141
141
 
142
142
  ### Gemini 직행
@@ -147,7 +147,7 @@ Bash("bash ~/.claude/scripts/cli-route.sh architect '기술-비즈니스 연계
147
147
  | `document` | writer | docs |
148
148
 
149
149
  ```bash
150
- Bash("bash ~/.claude/scripts/cli-route.sh writer '{PROMPT}' docs")
150
+ Bash("bash ~/.claude/scripts/tfx-route.sh writer '{PROMPT}' docs")
151
151
  ```
152
152
 
153
153
  ### Claude 네이티브
@@ -172,7 +172,7 @@ Agent(subagent_type="oh-my-claudecode:explore", model="haiku",
172
172
  prompt="다음 작업의 영향 범위 탐색 — 관련 파일, 모듈, 의존성 목록화: {PROMPT}")
173
173
 
174
174
  # 2. 분석 (Codex)
175
- Bash("bash ~/.claude/scripts/cli-route.sh analyst '탐색 결과: {explore_결과}. 작업: {PROMPT}. 평가: 영향범위, 복잡도(S/M/L/XL), 리스크, 선행조건, 권장실행방식' analyze")
175
+ Bash("bash ~/.claude/scripts/tfx-route.sh analyst '탐색 결과: {explore_결과}. 작업: {PROMPT}. 평가: 영향범위, 복잡도(S/M/L/XL), 리스크, 선행조건, 권장실행방식' analyze")
176
176
  ```
177
177
 
178
178
  #### index-repo — 코드베이스 인덱싱
@@ -228,7 +228,7 @@ Agent(subagent_type="oh-my-claudecode:explore", model="haiku",
228
228
 
229
229
  ## 필수 조건
230
230
 
231
- - `~/.claude/scripts/cli-route.sh` — CLI 라우팅 래퍼 (필수)
231
+ - `~/.claude/scripts/tfx-route.sh` — CLI 라우팅 래퍼 (필수)
232
232
  - **codex** CLI: `npm install -g @openai/codex` (codex 워커 사용 시)
233
233
  - **gemini** CLI: `npm install -g @google/gemini-cli` (gemini 워커 사용 시)
234
234
  - tmux **불필요**
@@ -255,7 +255,7 @@ Agent(subagent_type="oh-my-claudecode:explore", model="haiku",
255
255
  → 오케스트레이터 자체가 Opus이므로 별도 Agent 불필요
256
256
  |
257
257
  v
258
- [cli-route.sh × N 병렬 실행]
258
+ [tfx-route.sh × N 병렬 실행]
259
259
  → Windows면 Gemini 안정화 자동 적용
260
260
  |
261
261
  v
@@ -290,8 +290,8 @@ User: "/tfx-auto 인증 리팩터링 + UI 개선 + 테스트"
290
290
  |
291
291
  v
292
292
  [Phase 3: 병렬 실행]
293
- Bash("cli-route.sh executor '리팩터링' implement", run_in_background=true)
294
- Bash("cli-route.sh designer 'UI 개선' docs", run_in_background=true)
293
+ Bash("tfx-route.sh executor '리팩터링' implement", run_in_background=true)
294
+ Bash("tfx-route.sh designer 'UI 개선' docs", run_in_background=true)
295
295
  Agent(subagent_type="oh-my-claudecode:test-engineer", model="sonnet", run_in_background=true)
296
296
  |
297
297
  v
@@ -413,7 +413,7 @@ graph_type에 따라 실행 전략이 달라진다:
413
413
  모든 서브태스크를 단일 메시지에서 병렬로 실행:
414
414
 
415
415
  ```bash
416
- Bash("bash ~/.claude/scripts/cli-route.sh {agent} '{prompt}' {mcp_profile}",
416
+ Bash("bash ~/.claude/scripts/tfx-route.sh {agent} '{prompt}' {mcp_profile}",
417
417
  run_in_background=true)
418
418
  ```
419
419
 
@@ -452,7 +452,7 @@ For each level L from 0 to max_level:
452
452
  echo "=== Context from: {source_task_id} ===" >> combined.md
453
453
  cat .omc/context/{session_id}/{ctx} >> combined.md
454
454
  b. CLI 에이전트:
455
- Bash("bash ~/.claude/scripts/cli-route.sh {agent} '{prompt}' {mcp} {timeout} {context_file}",
455
+ Bash("bash ~/.claude/scripts/tfx-route.sh {agent} '{prompt}' {mcp} {timeout} {context_file}",
456
456
  run_in_background=(같은 레벨에 다른 태스크가 있으면))
457
457
  c. Claude 네이티브 에이전트:
458
458
  Agent(subagent_type="oh-my-claudecode:{agent}", model="{model}",
@@ -474,11 +474,11 @@ For each level L from 0 to max_level:
474
474
 
475
475
  ```bash
476
476
  # 컨텍스트 없는 경우 (Level 0 또는 독립)
477
- Bash("bash ~/.claude/scripts/cli-route.sh {agent} '{prompt}' {mcp_profile}",
477
+ Bash("bash ~/.claude/scripts/tfx-route.sh {agent} '{prompt}' {mcp_profile}",
478
478
  run_in_background=true)
479
479
 
480
480
  # 컨텍스트 있는 경우 (Level 1+, 의존 태스크)
481
- Bash("bash ~/.claude/scripts/cli-route.sh {agent} '{prompt}' {mcp_profile} .omc/context/{sid}/combined-{task_id}.md",
481
+ Bash("bash ~/.claude/scripts/tfx-route.sh {agent} '{prompt}' {mcp_profile} .omc/context/{sid}/combined-{task_id}.md",
482
482
  run_in_background=true)
483
483
  ```
484
484
 
@@ -518,7 +518,7 @@ Agent(subagent_type="oh-my-claudecode:{agent}", model="{model}",
518
518
 
519
519
  #### OUTPUT 섹션 추출 (CLI 결과에서)
520
520
 
521
- cli-route.sh 출력에서 `=== OUTPUT ===` ~ 다음 `===` 사이의 내용을 추출:
521
+ tfx-route.sh 출력에서 `=== OUTPUT ===` ~ 다음 `===` 사이의 내용을 추출:
522
522
  ```bash
523
523
  # OUTPUT 추출
524
524
  echo "$result" | sed -n '/^=== OUTPUT ===/,/^=== /{/^=== OUTPUT ===/d;/^=== /d;p}'
@@ -537,7 +537,7 @@ Bash("node ~/.claude/scripts/token-snapshot.mjs snapshot post-{subtask_id}")
537
537
  Bash("node ~/.claude/scripts/token-snapshot.mjs diff pre-{subtask_id} post-{subtask_id} --agent {agent} --cli {cli} --id {subtask_id}")
538
538
  ```
539
539
 
540
- > **참고:** cli-route.sh가 실행별 토큰을 JSONL 로그에 직접 기록하므로, 병렬 실행 시에도 정확한 추적 가능.
540
+ > **참고:** tfx-route.sh가 실행별 토큰을 JSONL 로그에 직접 기록하므로, 병렬 실행 시에도 정확한 추적 가능.
541
541
  > 스냅샷 diff는 단일 워커 검증용, 로그 토큰은 병렬 워커 구분용으로 이중 추적.
542
542
 
543
543
  ### Phase 4: 결과 수집
@@ -546,7 +546,7 @@ Bash("node ~/.claude/scripts/token-snapshot.mjs diff pre-{subtask_id} post-{subt
546
546
 
547
547
  #### CLI 워커 결과 파싱
548
548
 
549
- `=== CLI-ROUTE RESULT ===` 헤더에서:
549
+ `=== TFX-ROUTE RESULT ===` 헤더에서:
550
550
 
551
551
  | 필드 | 의미 |
552
552
  |------|------|
@@ -637,7 +637,7 @@ t3 → t4 (t3-implementation.md, 4.1KB)
637
637
 
638
638
  | 에러 | 원인 | 처리 |
639
639
  |------|------|------|
640
- | `cli-route.sh: not found` | 래퍼 스크립트 미설치 | `~/.claude/scripts/cli-route.sh` 생성 |
640
+ | `tfx-route.sh: not found` | 래퍼 스크립트 미설치 | `~/.claude/scripts/tfx-route.sh` 생성 |
641
641
  | `codex: command not found` | Codex CLI 미설치 | `npm install -g @openai/codex` |
642
642
  | `gemini: command not found` | Gemini CLI 미설치 | `npm install -g @google/gemini-cli` |
643
643
  | `status: timeout` | CLI 타임아웃 (에이전트별 동적) | 타임아웃 늘리거나 작업 범위 축소 |
@@ -654,7 +654,7 @@ t3 → t4 (t3-implementation.md, 4.1KB)
654
654
 
655
655
  | 항목 | 설명 |
656
656
  |------|------|
657
- | `~/.claude/scripts/cli-route.sh` | CLI 라우팅 래퍼 (필수) |
657
+ | `~/.claude/scripts/tfx-route.sh` | CLI 라우팅 래퍼 (필수) |
658
658
  | `/omc-teams` | tmux 기반 CLI 워커 (별도) |
659
659
 
660
660
  ## Troubleshooting
@@ -44,7 +44,7 @@ argument-hint: "\"작업 설명\" | N:codex \"작업 설명\""
44
44
 
45
45
  1. **Phase 3 CLI 실행 시** `TFX_CLI_MODE=codex`를 환경변수로 전달:
46
46
  ```bash
47
- TFX_CLI_MODE=codex bash ~/.claude/scripts/cli-route.sh {agent} '{prompt}' {mcp_profile}
47
+ TFX_CLI_MODE=codex bash ~/.claude/scripts/tfx-route.sh {agent} '{prompt}' {mcp_profile}
48
48
  ```
49
49
 
50
50
  2. **Phase 2 트리아지에서** gemini 분류 결과를 codex로 강제 변환: