triflux 3.2.0-dev.1 → 3.2.0-dev.10
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/README.ko.md +26 -18
- package/README.md +26 -18
- package/bin/triflux.mjs +1614 -1084
- package/hooks/hooks.json +12 -0
- package/hooks/keyword-rules.json +354 -0
- package/hub/bridge.mjs +371 -193
- package/hub/hitl.mjs +45 -31
- package/hub/pipe.mjs +457 -0
- package/hub/router.mjs +422 -161
- package/hub/server.mjs +429 -344
- package/hub/store.mjs +388 -314
- package/hub/team/cli-team-common.mjs +348 -0
- package/hub/team/cli-team-control.mjs +393 -0
- package/hub/team/cli-team-start.mjs +516 -0
- package/hub/team/cli-team-status.mjs +269 -0
- package/hub/team/cli.mjs +99 -368
- package/hub/team/dashboard.mjs +165 -64
- package/hub/team/native-supervisor.mjs +300 -0
- package/hub/team/native.mjs +62 -0
- package/hub/team/nativeProxy.mjs +534 -0
- package/hub/team/orchestrator.mjs +99 -35
- package/hub/team/pane.mjs +138 -101
- package/hub/team/psmux.mjs +297 -0
- package/hub/team/session.mjs +608 -186
- package/hub/team/shared.mjs +13 -0
- package/hub/team/staleState.mjs +299 -0
- package/hub/tools.mjs +140 -53
- package/hub/workers/claude-worker.mjs +446 -0
- package/hub/workers/codex-mcp.mjs +414 -0
- package/hub/workers/factory.mjs +18 -0
- package/hub/workers/gemini-worker.mjs +349 -0
- package/hub/workers/interface.mjs +41 -0
- package/hud/hud-qos-status.mjs +1789 -1732
- package/package.json +6 -2
- package/scripts/__tests__/keyword-detector.test.mjs +234 -0
- package/scripts/hub-ensure.mjs +83 -0
- package/scripts/keyword-detector.mjs +272 -0
- package/scripts/keyword-rules-expander.mjs +521 -0
- package/scripts/lib/keyword-rules.mjs +168 -0
- package/scripts/psmux-steering-prototype.sh +368 -0
- package/scripts/run.cjs +62 -0
- package/scripts/setup.mjs +189 -7
- package/scripts/test-tfx-route-no-claude-native.mjs +49 -0
- package/scripts/tfx-route-worker.mjs +161 -0
- package/scripts/tfx-route.sh +943 -508
- package/skills/tfx-auto/SKILL.md +90 -564
- package/skills/tfx-auto-codex/SKILL.md +77 -0
- package/skills/tfx-codex/SKILL.md +1 -4
- package/skills/tfx-doctor/SKILL.md +1 -0
- package/skills/tfx-gemini/SKILL.md +1 -4
- package/skills/tfx-multi/SKILL.md +296 -0
- package/skills/tfx-setup/SKILL.md +1 -4
- package/skills/tfx-team/SKILL.md +0 -172
package/hub/team/cli.mjs
CHANGED
|
@@ -1,368 +1,99 @@
|
|
|
1
|
-
// hub/team/cli.mjs —
|
|
2
|
-
// bin/triflux.mjs에서 import하여 사용
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
return
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (args[i] === "--agents" && args[i + 1]) {
|
|
101
|
-
agents = args[++i].split(",").map((s) => s.trim().toLowerCase());
|
|
102
|
-
} else if (args[i] === "--layout" && args[i + 1]) {
|
|
103
|
-
layout = args[++i];
|
|
104
|
-
} else if (!args[i].startsWith("-")) {
|
|
105
|
-
task = args[i];
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return { agents, layout, task };
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// ── 서브커맨드 ──
|
|
113
|
-
|
|
114
|
-
async function teamStart() {
|
|
115
|
-
// 1. tmux 확인
|
|
116
|
-
const mux = detectMultiplexer();
|
|
117
|
-
if (!mux) {
|
|
118
|
-
console.log(`
|
|
119
|
-
${RED}${BOLD}tmux 미발견${RESET}
|
|
120
|
-
|
|
121
|
-
tfx team은 tmux가 필요합니다:
|
|
122
|
-
WSL2: ${WHITE}wsl sudo apt install tmux${RESET}
|
|
123
|
-
macOS: ${WHITE}brew install tmux${RESET}
|
|
124
|
-
Linux: ${WHITE}apt install tmux${RESET}
|
|
125
|
-
|
|
126
|
-
Windows에서는 WSL2를 권장합니다:
|
|
127
|
-
1. ${WHITE}wsl --install${RESET}
|
|
128
|
-
2. ${WHITE}wsl sudo apt install tmux${RESET}
|
|
129
|
-
3. ${WHITE}tfx team "작업"${RESET}
|
|
130
|
-
`);
|
|
131
|
-
process.exit(1);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// 2. 인자 파싱
|
|
135
|
-
const { agents, layout, task } = parseTeamArgs();
|
|
136
|
-
if (!task) {
|
|
137
|
-
console.log(`\n ${AMBER}${BOLD}⬡ tfx team${RESET}\n`);
|
|
138
|
-
console.log(` 사용법: ${WHITE}tfx team "작업 설명"${RESET}`);
|
|
139
|
-
console.log(` ${WHITE}tfx team --agents codex,gemini "작업"${RESET}`);
|
|
140
|
-
console.log(` ${WHITE}tfx team --layout 1x3 "작업"${RESET}\n`);
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// 3. Hub 확인 + lazy-start
|
|
145
|
-
console.log(`\n ${AMBER}${BOLD}⬡ tfx team${RESET}\n`);
|
|
146
|
-
let hub = getHubInfo();
|
|
147
|
-
if (!hub) {
|
|
148
|
-
process.stdout.write(` Hub 시작 중...`);
|
|
149
|
-
hub = startHubDaemon();
|
|
150
|
-
if (hub) {
|
|
151
|
-
console.log(` ${GREEN}✓${RESET}`);
|
|
152
|
-
} else {
|
|
153
|
-
console.log(` ${RED}✗${RESET}`);
|
|
154
|
-
warn("Hub 시작 실패 — 수동으로 실행: tfx hub start");
|
|
155
|
-
// Hub 없이도 계속 진행 (통신만 불가)
|
|
156
|
-
}
|
|
157
|
-
} else {
|
|
158
|
-
ok(`Hub: ${DIM}${hub.url}${RESET}`);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// 4. 세션 ID 생성
|
|
162
|
-
const sessionId = `tfx-team-${Date.now().toString(36).slice(-4)}`;
|
|
163
|
-
|
|
164
|
-
// 5. 작업 분해
|
|
165
|
-
const subtasks = decomposeTask(task, agents.length);
|
|
166
|
-
|
|
167
|
-
console.log(` 세션: ${WHITE}${sessionId}${RESET}`);
|
|
168
|
-
console.log(` 레이아웃: ${layout} (${agents.length + 1} panes)`);
|
|
169
|
-
console.log(` 에이전트: ${agents.map((a) => `${AMBER}${a}${RESET}`).join(", ")}`);
|
|
170
|
-
for (let i = 0; i < subtasks.length; i++) {
|
|
171
|
-
const preview = subtasks[i].length > 40 ? subtasks[i].slice(0, 40) + "…" : subtasks[i];
|
|
172
|
-
console.log(` ${DIM}[${agents[i]}] ${preview}${RESET}`);
|
|
173
|
-
}
|
|
174
|
-
console.log("");
|
|
175
|
-
|
|
176
|
-
// 6. tmux 세션 생성
|
|
177
|
-
const session = createSession(sessionId, {
|
|
178
|
-
layout,
|
|
179
|
-
paneCount: agents.length + 1, // +1 for dashboard
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
// 7. Dashboard 시작 (Pane 0)
|
|
183
|
-
const dashCmd = `node ${PKG_ROOT}/hub/team/dashboard.mjs --session ${sessionId} --interval 2`;
|
|
184
|
-
startCliInPane(session.panes[0], dashCmd);
|
|
185
|
-
|
|
186
|
-
// 8. CLI 에이전트 시작 (Pane 1~N)
|
|
187
|
-
const assignments = [];
|
|
188
|
-
for (let i = 0; i < agents.length; i++) {
|
|
189
|
-
const cli = agents[i];
|
|
190
|
-
const target = session.panes[i + 1];
|
|
191
|
-
const command = buildCliCommand(cli);
|
|
192
|
-
startCliInPane(target, command);
|
|
193
|
-
assignments.push({ target, cli, subtask: subtasks[i] });
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// 9. CLI 초기화 대기 (3초 — interactive 모드 진입 시간)
|
|
197
|
-
ok("CLI 초기화 대기 (3초)...");
|
|
198
|
-
await new Promise((r) => setTimeout(r, 3000));
|
|
199
|
-
|
|
200
|
-
// 10. 프롬프트 주입
|
|
201
|
-
const hubUrl = hub?.url || "http://127.0.0.1:27888/mcp";
|
|
202
|
-
await orchestrate(sessionId, assignments, { hubUrl });
|
|
203
|
-
ok("프롬프트 주입 완료");
|
|
204
|
-
|
|
205
|
-
// 11. 팀 상태 저장
|
|
206
|
-
const panes = { [session.panes[0]]: { role: "dashboard" } };
|
|
207
|
-
for (let i = 0; i < agents.length; i++) {
|
|
208
|
-
panes[session.panes[i + 1]] = {
|
|
209
|
-
cli: agents[i],
|
|
210
|
-
agentId: `${agents[i]}-${session.panes[i + 1].split(".").pop()}`,
|
|
211
|
-
subtask: subtasks[i],
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
saveTeamState({
|
|
215
|
-
sessionName: sessionId,
|
|
216
|
-
agents,
|
|
217
|
-
task,
|
|
218
|
-
layout,
|
|
219
|
-
startedAt: Date.now(),
|
|
220
|
-
hubUrl,
|
|
221
|
-
panes,
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
// 12. tmux attach
|
|
225
|
-
console.log(`\n ${GREEN}${BOLD}팀 세션 준비 완료${RESET}`);
|
|
226
|
-
console.log(` ${DIM}Ctrl+B → 방향키로 pane 전환${RESET}`);
|
|
227
|
-
console.log(` ${DIM}Ctrl+B → D로 세션 분리 (백그라운드)${RESET}\n`);
|
|
228
|
-
attachSession(sessionId);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
function teamStatus() {
|
|
232
|
-
const state = loadTeamState();
|
|
233
|
-
if (!state) {
|
|
234
|
-
console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
const alive = sessionExists(state.sessionName);
|
|
239
|
-
const status = alive ? `${GREEN}● active${RESET}` : `${RED}● dead${RESET}`;
|
|
240
|
-
const uptime = alive ? `${Math.round((Date.now() - state.startedAt) / 60000)}분` : "-";
|
|
241
|
-
|
|
242
|
-
console.log(`\n ${AMBER}${BOLD}⬡ tfx team${RESET} ${status}\n`);
|
|
243
|
-
console.log(` 세션: ${state.sessionName}`);
|
|
244
|
-
console.log(` 작업: ${state.task}`);
|
|
245
|
-
console.log(` 에이전트: ${state.agents.join(", ")}`);
|
|
246
|
-
console.log(` Uptime: ${uptime}`);
|
|
247
|
-
console.log("");
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
function teamAttach() {
|
|
251
|
-
const state = loadTeamState();
|
|
252
|
-
if (!state || !sessionExists(state.sessionName)) {
|
|
253
|
-
console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
attachSession(state.sessionName);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
function teamStop() {
|
|
260
|
-
const state = loadTeamState();
|
|
261
|
-
if (!state) {
|
|
262
|
-
console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
if (sessionExists(state.sessionName)) {
|
|
267
|
-
killSession(state.sessionName);
|
|
268
|
-
ok(`세션 종료: ${state.sessionName}`);
|
|
269
|
-
} else {
|
|
270
|
-
console.log(` ${DIM}세션 이미 종료됨${RESET}`);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// 상태 파일 정리
|
|
274
|
-
clearTeamState();
|
|
275
|
-
console.log("");
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
function teamKill() {
|
|
279
|
-
// 모든 tfx-team- 세션 강제 종료
|
|
280
|
-
const sessions = listSessions();
|
|
281
|
-
if (sessions.length === 0) {
|
|
282
|
-
console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
|
-
for (const s of sessions) {
|
|
286
|
-
killSession(s);
|
|
287
|
-
ok(`종료: ${s}`);
|
|
288
|
-
}
|
|
289
|
-
clearTeamState();
|
|
290
|
-
console.log("");
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
function teamSend() {
|
|
294
|
-
const state = loadTeamState();
|
|
295
|
-
if (!state || !sessionExists(state.sessionName)) {
|
|
296
|
-
console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
|
|
297
|
-
return;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
const paneIdx = parseInt(process.argv[4], 10);
|
|
301
|
-
const message = process.argv.slice(5).join(" ");
|
|
302
|
-
if (isNaN(paneIdx) || !message) {
|
|
303
|
-
console.log(`\n 사용법: ${WHITE}tfx team send <pane번호> "메시지"${RESET}\n`);
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
const target = `${state.sessionName}:0.${paneIdx}`;
|
|
308
|
-
injectPrompt(target, message);
|
|
309
|
-
ok(`Pane ${paneIdx}에 메시지 주입 완료`);
|
|
310
|
-
console.log("");
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
function teamList() {
|
|
314
|
-
const sessions = listSessions();
|
|
315
|
-
if (sessions.length === 0) {
|
|
316
|
-
console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
|
|
317
|
-
return;
|
|
318
|
-
}
|
|
319
|
-
console.log(`\n ${AMBER}${BOLD}⬡ 팀 세션 목록${RESET}\n`);
|
|
320
|
-
for (const s of sessions) {
|
|
321
|
-
console.log(` ${GREEN}●${RESET} ${s}`);
|
|
322
|
-
}
|
|
323
|
-
console.log("");
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
function teamHelp() {
|
|
327
|
-
console.log(`
|
|
328
|
-
${AMBER}${BOLD}⬡ tfx team${RESET} ${DIM}멀티-CLI 팀 모드 (tmux + Hub)${RESET}
|
|
329
|
-
|
|
330
|
-
${BOLD}시작${RESET}
|
|
331
|
-
${WHITE}tfx team "작업 설명"${RESET} ${GRAY}기본 (codex x2 + gemini)${RESET}
|
|
332
|
-
${WHITE}tfx team --agents codex,gemini "작업"${RESET} ${GRAY}에이전트 지정${RESET}
|
|
333
|
-
${WHITE}tfx team --layout 1x3 "작업"${RESET} ${GRAY}레이아웃 지정${RESET}
|
|
334
|
-
|
|
335
|
-
${BOLD}제어${RESET}
|
|
336
|
-
${WHITE}tfx team status${RESET} ${GRAY}현재 팀 상태${RESET}
|
|
337
|
-
${WHITE}tfx team attach${RESET} ${GRAY}tmux 세션 연결${RESET}
|
|
338
|
-
${WHITE}tfx team send${RESET} ${DIM}N "msg"${RESET} ${GRAY}Pane N에 입력${RESET}
|
|
339
|
-
${WHITE}tfx team stop${RESET} ${GRAY}graceful 종료${RESET}
|
|
340
|
-
${WHITE}tfx team kill${RESET} ${GRAY}모든 팀 세션 강제 종료${RESET}
|
|
341
|
-
${WHITE}tfx team list${RESET} ${GRAY}활성 세션 목록${RESET}
|
|
342
|
-
`);
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// ── 메인 진입점 ──
|
|
346
|
-
|
|
347
|
-
/**
|
|
348
|
-
* tfx team 서브커맨드 라우터
|
|
349
|
-
* bin/triflux.mjs에서 호출
|
|
350
|
-
*/
|
|
351
|
-
export async function cmdTeam() {
|
|
352
|
-
const sub = process.argv[3];
|
|
353
|
-
|
|
354
|
-
switch (sub) {
|
|
355
|
-
case "status": return teamStatus();
|
|
356
|
-
case "attach": return teamAttach();
|
|
357
|
-
case "stop": return teamStop();
|
|
358
|
-
case "kill": return teamKill();
|
|
359
|
-
case "send": return teamSend();
|
|
360
|
-
case "list": return teamList();
|
|
361
|
-
case "help": case "--help": case "-h":
|
|
362
|
-
return teamHelp();
|
|
363
|
-
case undefined:
|
|
364
|
-
return teamHelp();
|
|
365
|
-
default:
|
|
366
|
-
return teamStart();
|
|
367
|
-
}
|
|
368
|
-
}
|
|
1
|
+
// hub/team/cli.mjs — team CLI UI/네비게이션 진입점
|
|
2
|
+
// bin/triflux.mjs에서 import하여 사용
|
|
3
|
+
import { AMBER, GRAY, DIM, BOLD, RESET, WHITE } from "./shared.mjs";
|
|
4
|
+
import { TEAM_SUBCOMMANDS } from "./cli-team-common.mjs";
|
|
5
|
+
import { teamStart } from "./cli-team-start.mjs";
|
|
6
|
+
import { teamStatus, teamTasks, teamTaskUpdate, teamDebug, teamList } from "./cli-team-status.mjs";
|
|
7
|
+
import {
|
|
8
|
+
teamAttach,
|
|
9
|
+
teamFocus,
|
|
10
|
+
teamInterrupt,
|
|
11
|
+
teamControl,
|
|
12
|
+
teamStop,
|
|
13
|
+
teamKill,
|
|
14
|
+
teamSend,
|
|
15
|
+
} from "./cli-team-control.mjs";
|
|
16
|
+
|
|
17
|
+
function teamHelp() {
|
|
18
|
+
console.log(`
|
|
19
|
+
${AMBER}${BOLD}⬡ tfx multi${RESET} ${DIM}멀티-CLI 팀 모드 (Lead + Teammates)${RESET}
|
|
20
|
+
|
|
21
|
+
${BOLD}시작${RESET}
|
|
22
|
+
${WHITE}tfx multi "작업 설명"${RESET}
|
|
23
|
+
${WHITE}tfx multi --agents codex,gemini --lead claude "작업"${RESET}
|
|
24
|
+
${WHITE}tfx multi --teammate-mode tmux "작업"${RESET}
|
|
25
|
+
${WHITE}tfx multi --teammate-mode wt "작업"${RESET} ${DIM}(Windows Terminal split-pane)${RESET}
|
|
26
|
+
${WHITE}tfx multi --layout 1xN "작업"${RESET} ${DIM}(세로 분할 컬럼)${RESET}
|
|
27
|
+
${WHITE}tfx multi --layout Nx1 "작업"${RESET} ${DIM}(가로 분할 스택)${RESET}
|
|
28
|
+
${WHITE}tfx multi --teammate-mode in-process "작업"${RESET} ${DIM}(tmux 불필요)${RESET}
|
|
29
|
+
|
|
30
|
+
${BOLD}제어${RESET}
|
|
31
|
+
${WHITE}tfx multi status${RESET} ${GRAY}현재 팀 상태${RESET}
|
|
32
|
+
${WHITE}tfx multi debug${RESET} ${DIM}[--lines 30]${RESET} ${GRAY}강화 디버그 출력(환경/세션/pane tail)${RESET}
|
|
33
|
+
${WHITE}tfx multi tasks${RESET} ${GRAY}공유 태스크 목록${RESET}
|
|
34
|
+
${WHITE}tfx multi task${RESET} ${DIM}<pending|progress|done> <T1>${RESET} ${GRAY}태스크 상태 갱신${RESET}
|
|
35
|
+
${WHITE}tfx multi attach${RESET} ${DIM}[--wt]${RESET} ${GRAY}세션 재연결 (WT 분할은 opt-in)${RESET}
|
|
36
|
+
${WHITE}tfx multi focus${RESET} ${DIM}<lead|이름|번호> [--wt]${RESET} ${GRAY}특정 팀메이트 포커스${RESET}
|
|
37
|
+
${WHITE}tfx multi send${RESET} ${DIM}<lead|이름|번호> "msg"${RESET} ${GRAY}팀메이트에 메시지 주입${RESET}
|
|
38
|
+
${WHITE}tfx multi interrupt${RESET} ${DIM}<대상>${RESET} ${GRAY}팀메이트 인터럽트(C-c)${RESET}
|
|
39
|
+
${WHITE}tfx multi control${RESET} ${DIM}<대상> <cmd>${RESET} ${GRAY}리드 제어명령(interrupt|stop|pause|resume)${RESET}
|
|
40
|
+
${WHITE}tfx multi stop${RESET} ${GRAY}graceful 종료${RESET}
|
|
41
|
+
${WHITE}tfx multi kill${RESET} ${GRAY}모든 팀 세션 강제 종료${RESET}
|
|
42
|
+
${WHITE}tfx multi list${RESET} ${GRAY}활성 세션 목록${RESET}
|
|
43
|
+
|
|
44
|
+
${BOLD}키 조작(Claude teammate 스타일, tmux 모드)${RESET}
|
|
45
|
+
${WHITE}Shift+Down${RESET} ${GRAY}다음 팀메이트${RESET}
|
|
46
|
+
${WHITE}Shift+Tab${RESET} ${GRAY}이전 팀메이트 (권장)${RESET}
|
|
47
|
+
${WHITE}Shift+Left${RESET} ${GRAY}이전 팀메이트 (대체)${RESET}
|
|
48
|
+
${WHITE}Shift+Up${RESET} ${GRAY}미지원 (Claude Code가 캡처 불가, scroll-up 충돌)${RESET}
|
|
49
|
+
${WHITE}Escape${RESET} ${GRAY}현재 팀메이트 인터럽트${RESET}
|
|
50
|
+
${WHITE}Ctrl+T${RESET} ${GRAY}태스크 목록 토글${RESET}
|
|
51
|
+
`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* tfx multi 서브커맨드 라우터
|
|
56
|
+
* bin/triflux.mjs에서 호출
|
|
57
|
+
*/
|
|
58
|
+
export async function cmdTeam() {
|
|
59
|
+
const rawSub = process.argv[3];
|
|
60
|
+
const sub = typeof rawSub === "string" ? rawSub.toLowerCase() : rawSub;
|
|
61
|
+
|
|
62
|
+
switch (sub) {
|
|
63
|
+
case "status":
|
|
64
|
+
return teamStatus();
|
|
65
|
+
case "debug":
|
|
66
|
+
return teamDebug();
|
|
67
|
+
case "tasks":
|
|
68
|
+
return teamTasks();
|
|
69
|
+
case "task":
|
|
70
|
+
return teamTaskUpdate();
|
|
71
|
+
case "attach":
|
|
72
|
+
return teamAttach();
|
|
73
|
+
case "focus":
|
|
74
|
+
return teamFocus();
|
|
75
|
+
case "interrupt":
|
|
76
|
+
return teamInterrupt();
|
|
77
|
+
case "control":
|
|
78
|
+
return teamControl();
|
|
79
|
+
case "stop":
|
|
80
|
+
return teamStop();
|
|
81
|
+
case "kill":
|
|
82
|
+
return teamKill();
|
|
83
|
+
case "send":
|
|
84
|
+
return teamSend();
|
|
85
|
+
case "list":
|
|
86
|
+
return teamList();
|
|
87
|
+
case "help":
|
|
88
|
+
case "--help":
|
|
89
|
+
case "-h":
|
|
90
|
+
return teamHelp();
|
|
91
|
+
case undefined:
|
|
92
|
+
return teamHelp();
|
|
93
|
+
default:
|
|
94
|
+
if (typeof sub === "string" && !sub.startsWith("-") && TEAM_SUBCOMMANDS.has(sub)) {
|
|
95
|
+
return teamHelp();
|
|
96
|
+
}
|
|
97
|
+
return teamStart();
|
|
98
|
+
}
|
|
99
|
+
}
|