triflux 3.1.0-dev.5 → 3.2.0-dev.2
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 +192 -43
- package/hooks/hooks.json +12 -0
- package/hub/bridge.mjs +137 -51
- package/hub/server.mjs +100 -29
- package/hub/team/cli.mjs +1335 -0
- package/hub/team/dashboard.mjs +166 -0
- package/hub/team/native-supervisor.mjs +300 -0
- package/hub/team/native.mjs +92 -0
- package/hub/team/nativeProxy.mjs +455 -0
- package/hub/team/orchestrator.mjs +166 -0
- package/hub/team/pane.mjs +110 -0
- package/hub/team/session.mjs +529 -0
- 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 +13 -73
- package/skills/tfx-team/SKILL.md +290 -0
package/hub/tools.mjs
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
|
-
// hub/tools.mjs — MCP 도구
|
|
2
|
-
// register
|
|
1
|
+
// hub/tools.mjs — MCP 도구 정의
|
|
2
|
+
// register/status/publish/ask/poll/handoff/HITL + team proxy
|
|
3
3
|
// 모든 도구 응답: { ok: boolean, error?: { code, message }, data?: ... }
|
|
4
4
|
|
|
5
|
+
import {
|
|
6
|
+
teamInfo,
|
|
7
|
+
teamTaskList,
|
|
8
|
+
teamTaskUpdate,
|
|
9
|
+
teamSendMessage,
|
|
10
|
+
} from './team/nativeProxy.mjs';
|
|
11
|
+
|
|
5
12
|
/**
|
|
6
13
|
* MCP 도구 목록 생성
|
|
7
14
|
* @param {object} store — createStore() 반환
|
|
@@ -145,19 +152,19 @@ export function createTools(store, router, hitl) {
|
|
|
145
152
|
auto_ack: args.auto_ack,
|
|
146
153
|
});
|
|
147
154
|
|
|
148
|
-
// wait_ms > 0 이고 메시지 없으면 짧은 간격으로 반복 재시도
|
|
149
|
-
if (!messages.length && args.wait_ms > 0) {
|
|
150
|
-
const interval = Math.min(args.wait_ms, 500);
|
|
151
|
-
const deadline = Date.now() + Math.min(args.wait_ms, 30000);
|
|
152
|
-
while (!messages.length && Date.now() < deadline) {
|
|
153
|
-
await new Promise(r => setTimeout(r, interval));
|
|
154
|
-
messages = store.pollForAgent(args.agent_id, {
|
|
155
|
-
max_messages: args.max_messages,
|
|
156
|
-
include_topics: args.include_topics,
|
|
157
|
-
auto_ack: args.auto_ack,
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
}
|
|
155
|
+
// wait_ms > 0 이고 메시지 없으면 짧은 간격으로 반복 재시도
|
|
156
|
+
if (!messages.length && args.wait_ms > 0) {
|
|
157
|
+
const interval = Math.min(args.wait_ms, 500);
|
|
158
|
+
const deadline = Date.now() + Math.min(args.wait_ms, 30000);
|
|
159
|
+
while (!messages.length && Date.now() < deadline) {
|
|
160
|
+
await new Promise(r => setTimeout(r, interval));
|
|
161
|
+
messages = store.pollForAgent(args.agent_id, {
|
|
162
|
+
max_messages: args.max_messages,
|
|
163
|
+
include_topics: args.include_topics,
|
|
164
|
+
auto_ack: args.auto_ack,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
161
168
|
|
|
162
169
|
return {
|
|
163
170
|
ok: true,
|
|
@@ -238,5 +245,96 @@ export function createTools(store, router, hitl) {
|
|
|
238
245
|
return hitl.submitHumanInput(args);
|
|
239
246
|
}),
|
|
240
247
|
},
|
|
248
|
+
|
|
249
|
+
// ── 9. team_info ──
|
|
250
|
+
{
|
|
251
|
+
name: 'team_info',
|
|
252
|
+
description: 'Claude Native Teams 메타/멤버/경로 정보를 조회합니다',
|
|
253
|
+
inputSchema: {
|
|
254
|
+
type: 'object',
|
|
255
|
+
required: ['team_name'],
|
|
256
|
+
properties: {
|
|
257
|
+
team_name: { type: 'string', minLength: 1, maxLength: 128, pattern: '^[a-z0-9][a-z0-9-]*$' },
|
|
258
|
+
include_members: { type: 'boolean', default: true },
|
|
259
|
+
include_paths: { type: 'boolean', default: true },
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
handler: wrap('TEAM_INFO_FAILED', (args) => {
|
|
263
|
+
return teamInfo(args);
|
|
264
|
+
}),
|
|
265
|
+
},
|
|
266
|
+
|
|
267
|
+
// ── 10. team_task_list ──
|
|
268
|
+
{
|
|
269
|
+
name: 'team_task_list',
|
|
270
|
+
description: 'Claude Native Teams task 목록을 owner/status 조건으로 조회합니다',
|
|
271
|
+
inputSchema: {
|
|
272
|
+
type: 'object',
|
|
273
|
+
required: ['team_name'],
|
|
274
|
+
properties: {
|
|
275
|
+
team_name: { type: 'string', pattern: '^[a-z0-9][a-z0-9-]*$' },
|
|
276
|
+
owner: { type: 'string' },
|
|
277
|
+
statuses: {
|
|
278
|
+
type: 'array',
|
|
279
|
+
items: { type: 'string', enum: ['pending', 'in_progress', 'completed', 'failed', 'deleted'] },
|
|
280
|
+
maxItems: 8,
|
|
281
|
+
},
|
|
282
|
+
include_internal: { type: 'boolean', default: false },
|
|
283
|
+
limit: { type: 'integer', minimum: 1, maximum: 1000, default: 200 },
|
|
284
|
+
},
|
|
285
|
+
},
|
|
286
|
+
handler: wrap('TEAM_TASK_LIST_FAILED', (args) => {
|
|
287
|
+
return teamTaskList(args);
|
|
288
|
+
}),
|
|
289
|
+
},
|
|
290
|
+
|
|
291
|
+
// ── 11. team_task_update ──
|
|
292
|
+
{
|
|
293
|
+
name: 'team_task_update',
|
|
294
|
+
description: 'Claude Native Teams task를 claim/update 합니다',
|
|
295
|
+
inputSchema: {
|
|
296
|
+
type: 'object',
|
|
297
|
+
required: ['team_name', 'task_id'],
|
|
298
|
+
properties: {
|
|
299
|
+
team_name: { type: 'string', pattern: '^[a-z0-9][a-z0-9-]*$' },
|
|
300
|
+
task_id: { type: 'string', minLength: 1, maxLength: 64 },
|
|
301
|
+
claim: { type: 'boolean', default: false },
|
|
302
|
+
owner: { type: 'string' },
|
|
303
|
+
status: { type: 'string', enum: ['pending', 'in_progress', 'completed', 'failed', 'deleted'] },
|
|
304
|
+
subject: { type: 'string' },
|
|
305
|
+
description: { type: 'string' },
|
|
306
|
+
activeForm: { type: 'string' },
|
|
307
|
+
add_blocks: { type: 'array', items: { type: 'string' } },
|
|
308
|
+
add_blocked_by: { type: 'array', items: { type: 'string' } },
|
|
309
|
+
metadata_patch: { type: 'object' },
|
|
310
|
+
if_match_mtime_ms: { type: 'number' },
|
|
311
|
+
actor: { type: 'string' },
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
handler: wrap('TEAM_TASK_UPDATE_FAILED', (args) => {
|
|
315
|
+
return teamTaskUpdate(args);
|
|
316
|
+
}),
|
|
317
|
+
},
|
|
318
|
+
|
|
319
|
+
// ── 12. team_send_message ──
|
|
320
|
+
{
|
|
321
|
+
name: 'team_send_message',
|
|
322
|
+
description: 'Claude Native Teams inbox에 메시지를 append 합니다',
|
|
323
|
+
inputSchema: {
|
|
324
|
+
type: 'object',
|
|
325
|
+
required: ['team_name', 'from', 'text'],
|
|
326
|
+
properties: {
|
|
327
|
+
team_name: { type: 'string', pattern: '^[a-z0-9][a-z0-9-]*$' },
|
|
328
|
+
from: { type: 'string', minLength: 1, maxLength: 128 },
|
|
329
|
+
to: { type: 'string', default: 'team-lead' },
|
|
330
|
+
text: { type: 'string', minLength: 1, maxLength: 200000 },
|
|
331
|
+
summary: { type: 'string', maxLength: 1000 },
|
|
332
|
+
color: { type: 'string', default: 'blue' },
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
handler: wrap('TEAM_SEND_MESSAGE_FAILED', (args) => {
|
|
336
|
+
return teamSendMessage(args);
|
|
337
|
+
}),
|
|
338
|
+
},
|
|
241
339
|
];
|
|
242
340
|
}
|
package/package.json
CHANGED
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
|
@@ -38,38 +38,6 @@ STDERR_LOG="/tmp/tfx-route-${AGENT_TYPE}-${TIMESTAMP}-stderr.log"
|
|
|
38
38
|
STDOUT_LOG="/tmp/tfx-route-${AGENT_TYPE}-${TIMESTAMP}-stdout.log"
|
|
39
39
|
TFX_TMP="${TMPDIR:-/tmp}"
|
|
40
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
|
-
}
|
|
72
|
-
|
|
73
41
|
# fallback 시 원래 에이전트 정보 보존
|
|
74
42
|
ORIGINAL_AGENT=""
|
|
75
43
|
ORIGINAL_CLI_ARGS=""
|
|
@@ -99,7 +67,7 @@ route_agent() {
|
|
|
99
67
|
CLI_EFFORT="high"; DEFAULT_TIMEOUT=1080; RUN_MODE="fg"; OPUS_OVERSIGHT="false" ;;
|
|
100
68
|
build-fixer)
|
|
101
69
|
CLI_TYPE="codex"; CLI_CMD="codex"
|
|
102
|
-
CLI_ARGS="--profile fast
|
|
70
|
+
CLI_ARGS="exec --profile fast ${codex_base}"
|
|
103
71
|
CLI_EFFORT="fast"; DEFAULT_TIMEOUT=540; RUN_MODE="fg"; OPUS_OVERSIGHT="false" ;;
|
|
104
72
|
debugger)
|
|
105
73
|
CLI_TYPE="codex"; CLI_CMD="codex"
|
|
@@ -107,39 +75,39 @@ route_agent() {
|
|
|
107
75
|
CLI_EFFORT="high"; DEFAULT_TIMEOUT=900; RUN_MODE="bg"; OPUS_OVERSIGHT="false" ;;
|
|
108
76
|
deep-executor)
|
|
109
77
|
CLI_TYPE="codex"; CLI_CMD="codex"
|
|
110
|
-
CLI_ARGS="--profile xhigh
|
|
78
|
+
CLI_ARGS="exec --profile xhigh ${codex_base}"
|
|
111
79
|
CLI_EFFORT="xhigh"; DEFAULT_TIMEOUT=3600; RUN_MODE="bg"; OPUS_OVERSIGHT="true" ;;
|
|
112
80
|
|
|
113
81
|
# ─── 설계/분석 레인 ───
|
|
114
82
|
architect)
|
|
115
83
|
CLI_TYPE="codex"; CLI_CMD="codex"
|
|
116
|
-
CLI_ARGS="--profile xhigh
|
|
84
|
+
CLI_ARGS="exec --profile xhigh ${codex_base}"
|
|
117
85
|
CLI_EFFORT="xhigh"; DEFAULT_TIMEOUT=3600; RUN_MODE="bg"; OPUS_OVERSIGHT="true" ;;
|
|
118
86
|
planner)
|
|
119
87
|
CLI_TYPE="codex"; CLI_CMD="codex"
|
|
120
|
-
CLI_ARGS="--profile xhigh
|
|
88
|
+
CLI_ARGS="exec --profile xhigh ${codex_base}"
|
|
121
89
|
CLI_EFFORT="xhigh"; DEFAULT_TIMEOUT=3600; RUN_MODE="fg"; OPUS_OVERSIGHT="true" ;;
|
|
122
90
|
critic)
|
|
123
91
|
CLI_TYPE="codex"; CLI_CMD="codex"
|
|
124
|
-
CLI_ARGS="--profile xhigh
|
|
92
|
+
CLI_ARGS="exec --profile xhigh ${codex_base}"
|
|
125
93
|
CLI_EFFORT="xhigh"; DEFAULT_TIMEOUT=3600; RUN_MODE="bg"; OPUS_OVERSIGHT="true" ;;
|
|
126
94
|
analyst)
|
|
127
95
|
CLI_TYPE="codex"; CLI_CMD="codex"
|
|
128
|
-
CLI_ARGS="--profile xhigh
|
|
96
|
+
CLI_ARGS="exec --profile xhigh ${codex_base}"
|
|
129
97
|
CLI_EFFORT="xhigh"; DEFAULT_TIMEOUT=3600; RUN_MODE="fg"; OPUS_OVERSIGHT="true" ;;
|
|
130
98
|
|
|
131
99
|
# ─── 리뷰 레인 ───
|
|
132
100
|
code-reviewer)
|
|
133
101
|
CLI_TYPE="codex"; CLI_CMD="codex"
|
|
134
|
-
CLI_ARGS="--profile thorough
|
|
102
|
+
CLI_ARGS="exec --profile thorough ${codex_base} review"
|
|
135
103
|
CLI_EFFORT="thorough"; DEFAULT_TIMEOUT=1800; RUN_MODE="bg"; OPUS_OVERSIGHT="false" ;;
|
|
136
104
|
security-reviewer)
|
|
137
105
|
CLI_TYPE="codex"; CLI_CMD="codex"
|
|
138
|
-
CLI_ARGS="--profile thorough
|
|
106
|
+
CLI_ARGS="exec --profile thorough ${codex_base} review"
|
|
139
107
|
CLI_EFFORT="thorough"; DEFAULT_TIMEOUT=1800; RUN_MODE="bg"; OPUS_OVERSIGHT="true" ;;
|
|
140
108
|
quality-reviewer)
|
|
141
109
|
CLI_TYPE="codex"; CLI_CMD="codex"
|
|
142
|
-
CLI_ARGS="--profile thorough
|
|
110
|
+
CLI_ARGS="exec --profile thorough ${codex_base} review"
|
|
143
111
|
CLI_EFFORT="thorough"; DEFAULT_TIMEOUT=1800; RUN_MODE="bg"; OPUS_OVERSIGHT="false" ;;
|
|
144
112
|
|
|
145
113
|
# ─── 리서치 레인 ───
|
|
@@ -149,7 +117,7 @@ route_agent() {
|
|
|
149
117
|
CLI_EFFORT="high"; DEFAULT_TIMEOUT=1440; RUN_MODE="bg"; OPUS_OVERSIGHT="false" ;;
|
|
150
118
|
scientist-deep)
|
|
151
119
|
CLI_TYPE="codex"; CLI_CMD="codex"
|
|
152
|
-
CLI_ARGS="--profile thorough
|
|
120
|
+
CLI_ARGS="exec --profile thorough ${codex_base}"
|
|
153
121
|
CLI_EFFORT="thorough"; DEFAULT_TIMEOUT=3600; RUN_MODE="bg"; OPUS_OVERSIGHT="false" ;;
|
|
154
122
|
document-specialist)
|
|
155
123
|
CLI_TYPE="codex"; CLI_CMD="codex"
|
|
@@ -183,7 +151,7 @@ route_agent() {
|
|
|
183
151
|
# ─── 경량 ───
|
|
184
152
|
spark)
|
|
185
153
|
CLI_TYPE="codex"; CLI_CMD="codex"
|
|
186
|
-
CLI_ARGS="--profile spark_fast
|
|
154
|
+
CLI_ARGS="exec --profile spark_fast ${codex_base}"
|
|
187
155
|
CLI_EFFORT="spark_fast"; DEFAULT_TIMEOUT=180; RUN_MODE="fg"; OPUS_OVERSIGHT="false" ;;
|
|
188
156
|
*)
|
|
189
157
|
echo "ERROR: 알 수 없는 에이전트 타입: $agent" >&2
|
|
@@ -208,7 +176,7 @@ apply_cli_mode() {
|
|
|
208
176
|
designer)
|
|
209
177
|
CLI_ARGS="exec ${codex_base}"; CLI_EFFORT="high"; DEFAULT_TIMEOUT=600 ;;
|
|
210
178
|
writer)
|
|
211
|
-
CLI_ARGS="--profile spark_fast
|
|
179
|
+
CLI_ARGS="exec --profile spark_fast ${codex_base}"; CLI_EFFORT="spark_fast"; DEFAULT_TIMEOUT=180 ;;
|
|
212
180
|
esac
|
|
213
181
|
echo "[tfx-route] TFX_CLI_MODE=codex: $AGENT_TYPE → codex($CLI_EFFORT)로 리매핑" >&2
|
|
214
182
|
fi ;;
|
|
@@ -385,31 +353,11 @@ ${ctx_content}
|
|
|
385
353
|
|
|
386
354
|
# 메타정보 (stderr)
|
|
387
355
|
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
|
|
356
|
+
echo "[tfx-route] opus_oversight=$OPUS_OVERSIGHT mcp_profile=$MCP_PROFILE" >&2
|
|
389
357
|
|
|
390
358
|
# Per-process 에이전트 등록
|
|
391
359
|
register_agent
|
|
392
360
|
|
|
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
361
|
# CLI 실행 (stderr 분리 + 타임아웃 + 소요시간 측정)
|
|
414
362
|
local exit_code=0
|
|
415
363
|
local start_time
|
|
@@ -466,14 +414,6 @@ ${ctx_content}
|
|
|
466
414
|
end_time=$(date +%s)
|
|
467
415
|
local elapsed=$((end_time - start_time))
|
|
468
416
|
|
|
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
417
|
# ── 후처리: 단일 node 프로세스로 위임 ──
|
|
478
418
|
# 토큰 추출, 출력 필터링, 로그, 토큰 누적, AIMD, 이슈 추적, 결과 출력 전부 처리
|
|
479
419
|
local post_script="${HOME}/.claude/scripts/tfx-route-post.mjs"
|