triflux 10.3.4 → 10.4.0
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/CLAUDE.md +193 -0
- package/LICENSE +21 -21
- package/hooks/hook-registry.json +256 -256
- package/hub/adaptive-inject.mjs +1 -1
- package/hub/assign-callbacks.mjs +120 -120
- package/hub/delegator/index.mjs +14 -14
- package/hub/delegator/tool-definitions.mjs +35 -35
- package/hub/hitl.mjs +143 -143
- package/hub/router.mjs +791 -791
- package/hub/session-fingerprint.mjs +1 -1
- package/hub/team/cli/commands/attach.mjs +37 -37
- package/hub/team/cli/commands/debug.mjs +74 -74
- package/hub/team/cli/commands/focus.mjs +53 -53
- package/hub/team/cli/commands/list.mjs +24 -24
- package/hub/team/cli/commands/start/start-in-process.mjs +40 -40
- package/hub/team/cli/commands/start/start-mux.mjs +73 -73
- package/hub/team/cli/commands/start/start-wt.mjs +69 -69
- package/hub/team/cli/commands/tasks.mjs +13 -13
- package/hub/team/cli/render.mjs +30 -30
- package/hub/team/cli/services/attach-fallback.mjs +54 -54
- package/hub/team/cli/services/member-selector.mjs +30 -30
- package/hub/team/cli/services/native-control.mjs +116 -116
- package/hub/team/cli/services/task-model.mjs +30 -30
- package/hub/team/notify.mjs +1 -1
- package/hub/team/orchestrator.mjs +161 -161
- package/hub/team/session.mjs +611 -611
- package/hub/team/shared.mjs +13 -13
- package/hub/tray.mjs +368 -368
- package/hub/workers/codex-mcp.mjs +507 -507
- package/hub/workers/factory.mjs +21 -21
- package/package.json +21 -55
- package/references/hosts.json +33 -0
- package/scripts/completions/tfx.bash +47 -47
- package/scripts/completions/tfx.fish +44 -44
- package/scripts/completions/tfx.zsh +83 -83
- package/scripts/hub-ensure.mjs +120 -120
- package/scripts/keyword-detector.mjs +272 -272
- package/scripts/keyword-rules-expander.mjs +521 -521
- package/scripts/lib/mcp-server-catalog.mjs +118 -118
- package/scripts/notion-read.mjs +553 -553
- package/scripts/test-tfx-route-no-claude-native.mjs +57 -57
- package/scripts/tfx-batch-stats.mjs +96 -96
- package/skills/.omc/state/agent-replay-8f0e10a9-9693-4410-96f5-a6b07e8ed995.jsonl +1 -0
- package/skills/.omc/state/idle-notif-cooldown.json +3 -0
- package/skills/.omc/state/last-tool-error.json +7 -0
- package/skills/.omc/state/subagent-tracking.json +7 -0
- package/skills/tfx-remote-spawn/references/hosts.json +16 -0
- package/skills/tfx-workspace/async-tests/run-tests.sh +203 -0
- package/skills/tfx-workspace/evals/evals.json +79 -0
- package/skills/tfx-workspace/iteration-1/benchmark.json +162 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/eval_metadata.json +11 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/grading.json +9 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/outputs/analysis.md +154 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/grading.json +9 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/outputs/analysis.md +126 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/eval_metadata.json +11 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/grading.json +9 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/outputs/analysis.md +119 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/grading.json +9 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/outputs/analysis.md +115 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/eval_metadata.json +10 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/grading.json +8 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/outputs/analysis.md +86 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/grading.json +8 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/outputs/analysis.md +81 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/eval_metadata.json +12 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/grading.json +10 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/outputs/analysis.md +316 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/grading.json +10 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/outputs/analysis.md +352 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/review.html +1325 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/eval_metadata.json +12 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/grading.json +10 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/outputs/analysis.md +97 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/grading.json +10 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/outputs/analysis.md +94 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/eval_metadata.json +12 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/grading.json +10 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/outputs/analysis.md +209 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/grading.json +10 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/outputs/analysis.md +193 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-2/benchmark.json +62 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/eval_metadata.json +13 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/grading.json +11 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/outputs/analysis.md +382 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/grading.json +11 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/outputs/analysis.md +333 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-2/review.html +1325 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-auto/SKILL.md +217 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-auto-codex/SKILL.md +77 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-codex/SKILL.md +65 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-doctor/SKILL.md +94 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-gemini/SKILL.md +82 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-hub/SKILL.md +133 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-multi/SKILL.md +426 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-setup/SKILL.md +101 -0
- package/.claude-plugin/marketplace.json +0 -34
- package/.claude-plugin/plugin.json +0 -22
- package/config/mcp-registry.json +0 -29
- package/tui/codex-profile.mjs +0 -402
- package/tui/core.mjs +0 -236
- package/tui/doctor.mjs +0 -328
- package/tui/gemini-profile.mjs +0 -254
- package/tui/monitor-data.mjs +0 -148
- package/tui/monitor.mjs +0 -295
- package/tui/setup.mjs +0 -442
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
import { attachSession } from "../../session.mjs";
|
|
2
|
-
import { DIM, RESET } from "../../shared.mjs";
|
|
3
|
-
import { buildManualAttachCommand, launchAttachInWindowsTerminal, wantsWtAttachFallback } from "../services/attach-fallback.mjs";
|
|
4
|
-
import { isNativeMode, isTeamAlive, isWtMode } from "../services/runtime-mode.mjs";
|
|
5
|
-
import { loadTeamState } from "../services/state-store.mjs";
|
|
6
|
-
import { fail, ok, warn } from "../render.mjs";
|
|
7
|
-
|
|
8
|
-
export async function teamAttach(args = []) {
|
|
9
|
-
const state = loadTeamState();
|
|
10
|
-
if (!state || !isTeamAlive(state)) {
|
|
11
|
-
console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
if (isNativeMode(state)) {
|
|
15
|
-
console.log(`\n ${DIM}in-process 모드는 별도 attach가 없습니다.${RESET}\n ${DIM}상태 확인: tfx multi status${RESET}\n`);
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
if (isWtMode(state)) {
|
|
19
|
-
console.log(`\n ${DIM}wt 모드는 attach 개념이 없습니다 (Windows Terminal pane가 독립 실행됨).${RESET}\n ${DIM}재실행/정리는: tfx multi stop${RESET}\n`);
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
try {
|
|
24
|
-
attachSession(state.sessionName);
|
|
25
|
-
} catch (error) {
|
|
26
|
-
const allowWt = wantsWtAttachFallback(args);
|
|
27
|
-
if (allowWt && await launchAttachInWindowsTerminal(state.sessionName)) {
|
|
28
|
-
warn(`현재 터미널에서 attach 실패: ${error.message}`);
|
|
29
|
-
ok("Windows Terminal split-pane로 attach 재시도 창을 열었습니다.");
|
|
30
|
-
console.log(` ${DIM}수동 attach 명령: ${buildManualAttachCommand(state.sessionName)}${RESET}\n`);
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
fail(`attach 실패: ${error.message}`);
|
|
34
|
-
warn(allowWt ? "WT 분할창 attach 자동 검증 실패 (session_attached 증가 없음)" : "자동 WT 분할은 기본 비활성입니다. 필요 시 --wt 옵션으로 실행하세요.");
|
|
35
|
-
console.log(` ${DIM}수동 attach 명령: ${buildManualAttachCommand(state.sessionName)}${RESET}\n`);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
1
|
+
import { attachSession } from "../../session.mjs";
|
|
2
|
+
import { DIM, RESET } from "../../shared.mjs";
|
|
3
|
+
import { buildManualAttachCommand, launchAttachInWindowsTerminal, wantsWtAttachFallback } from "../services/attach-fallback.mjs";
|
|
4
|
+
import { isNativeMode, isTeamAlive, isWtMode } from "../services/runtime-mode.mjs";
|
|
5
|
+
import { loadTeamState } from "../services/state-store.mjs";
|
|
6
|
+
import { fail, ok, warn } from "../render.mjs";
|
|
7
|
+
|
|
8
|
+
export async function teamAttach(args = []) {
|
|
9
|
+
const state = loadTeamState();
|
|
10
|
+
if (!state || !isTeamAlive(state)) {
|
|
11
|
+
console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
if (isNativeMode(state)) {
|
|
15
|
+
console.log(`\n ${DIM}in-process 모드는 별도 attach가 없습니다.${RESET}\n ${DIM}상태 확인: tfx multi status${RESET}\n`);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (isWtMode(state)) {
|
|
19
|
+
console.log(`\n ${DIM}wt 모드는 attach 개념이 없습니다 (Windows Terminal pane가 독립 실행됨).${RESET}\n ${DIM}재실행/정리는: tfx multi stop${RESET}\n`);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
attachSession(state.sessionName);
|
|
25
|
+
} catch (error) {
|
|
26
|
+
const allowWt = wantsWtAttachFallback(args);
|
|
27
|
+
if (allowWt && await launchAttachInWindowsTerminal(state.sessionName)) {
|
|
28
|
+
warn(`현재 터미널에서 attach 실패: ${error.message}`);
|
|
29
|
+
ok("Windows Terminal split-pane로 attach 재시도 창을 열었습니다.");
|
|
30
|
+
console.log(` ${DIM}수동 attach 명령: ${buildManualAttachCommand(state.sessionName)}${RESET}\n`);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
fail(`attach 실패: ${error.message}`);
|
|
34
|
+
warn(allowWt ? "WT 분할창 attach 자동 검증 실패 (session_attached 증가 없음)" : "자동 WT 분할은 기본 비활성입니다. 필요 시 --wt 옵션으로 실행하세요.");
|
|
35
|
+
console.log(` ${DIM}수동 attach 명령: ${buildManualAttachCommand(state.sessionName)}${RESET}\n`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -1,74 +1,74 @@
|
|
|
1
|
-
import { AMBER, BOLD, DIM, RESET } from "../../shared.mjs";
|
|
2
|
-
import {
|
|
3
|
-
capturePaneOutput,
|
|
4
|
-
detectMultiplexer,
|
|
5
|
-
getSessionAttachedCount,
|
|
6
|
-
hasWindowsTerminal,
|
|
7
|
-
hasWindowsTerminalSession,
|
|
8
|
-
listSessions,
|
|
9
|
-
} from "../../session.mjs";
|
|
10
|
-
import { getHubInfo, nativeGetStatus } from "../services/hub-client.mjs";
|
|
11
|
-
import { isNativeMode, isTeamAlive, isWtMode } from "../services/runtime-mode.mjs";
|
|
12
|
-
import { loadTeamState, TEAM_PROFILE } from "../services/state-store.mjs";
|
|
13
|
-
import { formatCompletionSuffix } from "../render.mjs";
|
|
14
|
-
|
|
15
|
-
export async function teamDebug(args = []) {
|
|
16
|
-
const state = loadTeamState();
|
|
17
|
-
const flagIndex = args.findIndex((arg) => arg === "--lines" || arg === "-n");
|
|
18
|
-
const lines = flagIndex === -1 ? 20 : Math.max(3, parseInt(args[flagIndex + 1] || "20", 10) || 20);
|
|
19
|
-
const hub = await getHubInfo();
|
|
20
|
-
|
|
21
|
-
console.log(`\n ${AMBER}${BOLD}⬡ Team Debug${RESET}\n`);
|
|
22
|
-
console.log(` platform: ${process.platform}`);
|
|
23
|
-
console.log(` node: ${process.version}`);
|
|
24
|
-
console.log(` tty: stdout=${!!process.stdout.isTTY}, stdin=${!!process.stdin.isTTY}`);
|
|
25
|
-
console.log(` mux: ${detectMultiplexer() || "none"}`);
|
|
26
|
-
console.log(` hub-pid: ${hub ? `${hub.pid}` : "-"}`);
|
|
27
|
-
console.log(` hub-url: ${hub?.url || "-"}`);
|
|
28
|
-
const sessions = listSessions();
|
|
29
|
-
console.log(` sessions: ${sessions.length ? sessions.join(", ") : "-"}`);
|
|
30
|
-
|
|
31
|
-
if (!state) {
|
|
32
|
-
console.log(`\n ${DIM}team-state 없음 (활성 세션 없음)${RESET}\n`);
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
console.log(`\n ${BOLD}state${RESET}`);
|
|
37
|
-
console.log(` session: ${state.sessionName}`);
|
|
38
|
-
console.log(` profile: ${state.profile || TEAM_PROFILE}`);
|
|
39
|
-
console.log(` mode: ${state.teammateMode || "tmux"}`);
|
|
40
|
-
console.log(` lead: ${state.lead}`);
|
|
41
|
-
console.log(` agents: ${(state.agents || []).join(", ")}`);
|
|
42
|
-
console.log(` alive: ${isTeamAlive(state) ? "yes" : "no"}`);
|
|
43
|
-
console.log(` attached: ${getSessionAttachedCount(state.sessionName) ?? "-"}`);
|
|
44
|
-
|
|
45
|
-
if (isWtMode(state)) {
|
|
46
|
-
console.log(`\n ${BOLD}wt-session${RESET}`);
|
|
47
|
-
console.log(` window: ${state?.wt?.windowId ?? 0}`);
|
|
48
|
-
console.log(` layout: ${state?.wt?.layout || state?.layout || "-"}`);
|
|
49
|
-
console.log(` panes: ${state?.wt?.paneCount ?? (state.members || []).length}`);
|
|
50
|
-
console.log(` wt.exe: ${hasWindowsTerminal() ? "yes" : "no"}`);
|
|
51
|
-
console.log(` WT_SESSION:${hasWindowsTerminalSession() ? "yes" : "no"}`);
|
|
52
|
-
console.log("");
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (isNativeMode(state)) {
|
|
57
|
-
console.log(`\n ${BOLD}native-members${RESET}`);
|
|
58
|
-
const members = (await nativeGetStatus(state))?.data?.members || [];
|
|
59
|
-
if (!members.length) console.log(` ${DIM}(no data)${RESET}`);
|
|
60
|
-
for (const member of members) console.log(` - ${member.name}: ${member.status}${formatCompletionSuffix(member)}${member.lastPreview ? ` ${DIM}${member.lastPreview}${RESET}` : ""}`);
|
|
61
|
-
console.log("");
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
console.log(`\n ${BOLD}pane-tail${RESET} ${DIM}(last ${lines} lines)${RESET}`);
|
|
66
|
-
if (!(state.members || []).length) console.log(` ${DIM}(members 없음)${RESET}`);
|
|
67
|
-
for (const member of state.members || []) {
|
|
68
|
-
console.log(`\n [${member.name}] ${member.pane}`);
|
|
69
|
-
for (const line of (capturePaneOutput(member.pane, lines) || "(empty)").split("\n").slice(-lines)) {
|
|
70
|
-
console.log(` ${line}`);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
console.log("");
|
|
74
|
-
}
|
|
1
|
+
import { AMBER, BOLD, DIM, RESET } from "../../shared.mjs";
|
|
2
|
+
import {
|
|
3
|
+
capturePaneOutput,
|
|
4
|
+
detectMultiplexer,
|
|
5
|
+
getSessionAttachedCount,
|
|
6
|
+
hasWindowsTerminal,
|
|
7
|
+
hasWindowsTerminalSession,
|
|
8
|
+
listSessions,
|
|
9
|
+
} from "../../session.mjs";
|
|
10
|
+
import { getHubInfo, nativeGetStatus } from "../services/hub-client.mjs";
|
|
11
|
+
import { isNativeMode, isTeamAlive, isWtMode } from "../services/runtime-mode.mjs";
|
|
12
|
+
import { loadTeamState, TEAM_PROFILE } from "../services/state-store.mjs";
|
|
13
|
+
import { formatCompletionSuffix } from "../render.mjs";
|
|
14
|
+
|
|
15
|
+
export async function teamDebug(args = []) {
|
|
16
|
+
const state = loadTeamState();
|
|
17
|
+
const flagIndex = args.findIndex((arg) => arg === "--lines" || arg === "-n");
|
|
18
|
+
const lines = flagIndex === -1 ? 20 : Math.max(3, parseInt(args[flagIndex + 1] || "20", 10) || 20);
|
|
19
|
+
const hub = await getHubInfo();
|
|
20
|
+
|
|
21
|
+
console.log(`\n ${AMBER}${BOLD}⬡ Team Debug${RESET}\n`);
|
|
22
|
+
console.log(` platform: ${process.platform}`);
|
|
23
|
+
console.log(` node: ${process.version}`);
|
|
24
|
+
console.log(` tty: stdout=${!!process.stdout.isTTY}, stdin=${!!process.stdin.isTTY}`);
|
|
25
|
+
console.log(` mux: ${detectMultiplexer() || "none"}`);
|
|
26
|
+
console.log(` hub-pid: ${hub ? `${hub.pid}` : "-"}`);
|
|
27
|
+
console.log(` hub-url: ${hub?.url || "-"}`);
|
|
28
|
+
const sessions = listSessions();
|
|
29
|
+
console.log(` sessions: ${sessions.length ? sessions.join(", ") : "-"}`);
|
|
30
|
+
|
|
31
|
+
if (!state) {
|
|
32
|
+
console.log(`\n ${DIM}team-state 없음 (활성 세션 없음)${RESET}\n`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
console.log(`\n ${BOLD}state${RESET}`);
|
|
37
|
+
console.log(` session: ${state.sessionName}`);
|
|
38
|
+
console.log(` profile: ${state.profile || TEAM_PROFILE}`);
|
|
39
|
+
console.log(` mode: ${state.teammateMode || "tmux"}`);
|
|
40
|
+
console.log(` lead: ${state.lead}`);
|
|
41
|
+
console.log(` agents: ${(state.agents || []).join(", ")}`);
|
|
42
|
+
console.log(` alive: ${isTeamAlive(state) ? "yes" : "no"}`);
|
|
43
|
+
console.log(` attached: ${getSessionAttachedCount(state.sessionName) ?? "-"}`);
|
|
44
|
+
|
|
45
|
+
if (isWtMode(state)) {
|
|
46
|
+
console.log(`\n ${BOLD}wt-session${RESET}`);
|
|
47
|
+
console.log(` window: ${state?.wt?.windowId ?? 0}`);
|
|
48
|
+
console.log(` layout: ${state?.wt?.layout || state?.layout || "-"}`);
|
|
49
|
+
console.log(` panes: ${state?.wt?.paneCount ?? (state.members || []).length}`);
|
|
50
|
+
console.log(` wt.exe: ${hasWindowsTerminal() ? "yes" : "no"}`);
|
|
51
|
+
console.log(` WT_SESSION:${hasWindowsTerminalSession() ? "yes" : "no"}`);
|
|
52
|
+
console.log("");
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (isNativeMode(state)) {
|
|
57
|
+
console.log(`\n ${BOLD}native-members${RESET}`);
|
|
58
|
+
const members = (await nativeGetStatus(state))?.data?.members || [];
|
|
59
|
+
if (!members.length) console.log(` ${DIM}(no data)${RESET}`);
|
|
60
|
+
for (const member of members) console.log(` - ${member.name}: ${member.status}${formatCompletionSuffix(member)}${member.lastPreview ? ` ${DIM}${member.lastPreview}${RESET}` : ""}`);
|
|
61
|
+
console.log("");
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
console.log(`\n ${BOLD}pane-tail${RESET} ${DIM}(last ${lines} lines)${RESET}`);
|
|
66
|
+
if (!(state.members || []).length) console.log(` ${DIM}(members 없음)${RESET}`);
|
|
67
|
+
for (const member of state.members || []) {
|
|
68
|
+
console.log(`\n [${member.name}] ${member.pane}`);
|
|
69
|
+
for (const line of (capturePaneOutput(member.pane, lines) || "(empty)").split("\n").slice(-lines)) {
|
|
70
|
+
console.log(` ${line}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
console.log("");
|
|
74
|
+
}
|
|
@@ -1,53 +1,53 @@
|
|
|
1
|
-
import { attachSession, focusPane, focusWtPane } from "../../session.mjs";
|
|
2
|
-
import { DIM, RESET, WHITE } from "../../shared.mjs";
|
|
3
|
-
import { buildManualAttachCommand, launchAttachInWindowsTerminal, wantsWtAttachFallback } from "../services/attach-fallback.mjs";
|
|
4
|
-
import { resolveMember } from "../services/member-selector.mjs";
|
|
5
|
-
import { isNativeMode, isTeamAlive, isWtMode } from "../services/runtime-mode.mjs";
|
|
6
|
-
import { loadTeamState } from "../services/state-store.mjs";
|
|
7
|
-
import { fail, ok, warn } from "../render.mjs";
|
|
8
|
-
|
|
9
|
-
export async function teamFocus(args = []) {
|
|
10
|
-
const state = loadTeamState();
|
|
11
|
-
if (!state || !isTeamAlive(state)) {
|
|
12
|
-
console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
if (isNativeMode(state)) {
|
|
16
|
-
console.log(`\n ${DIM}in-process 모드는 focus/attach 개념이 없습니다.${RESET}\n ${DIM}직접 지시: tfx multi send <대상> "메시지"${RESET}\n`);
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const member = resolveMember(state, args[0]);
|
|
21
|
-
if (!member) {
|
|
22
|
-
console.log(`\n 사용법: ${WHITE}tfx multi focus <lead|이름|번호>${RESET}\n`);
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (isWtMode(state)) {
|
|
27
|
-
const paneIndex = Number(/^wt:(\d+)$/.exec(member.pane || "")?.[1]);
|
|
28
|
-
if (!Number.isFinite(paneIndex)) {
|
|
29
|
-
console.log(`\n ${DIM}wt pane 인덱스 파싱 실패: ${member.pane}${RESET}\n`);
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
if (focusWtPane(paneIndex, { layout: state?.wt?.layout || state?.layout || "1xN" })) ok(`${member.name} pane 포커스 이동 (wt)`);
|
|
33
|
-
else warn("wt pane 포커스 이동 실패 (WT_SESSION/wt.exe 상태 확인 필요)");
|
|
34
|
-
console.log("");
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
focusPane(member.pane, { zoom: false });
|
|
39
|
-
try {
|
|
40
|
-
attachSession(state.sessionName);
|
|
41
|
-
} catch (error) {
|
|
42
|
-
const allowWt = wantsWtAttachFallback(args);
|
|
43
|
-
if (allowWt && await launchAttachInWindowsTerminal(state.sessionName)) {
|
|
44
|
-
warn(`현재 터미널에서 attach 실패: ${error.message}`);
|
|
45
|
-
ok("Windows Terminal split-pane로 attach 재시도 창을 열었습니다.");
|
|
46
|
-
console.log(` ${DIM}수동 attach 명령: ${buildManualAttachCommand(state.sessionName)}${RESET}\n`);
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
fail(`attach 실패: ${error.message}`);
|
|
50
|
-
warn(allowWt ? "WT 분할창 attach 자동 검증 실패 (session_attached 증가 없음)" : "자동 WT 분할은 기본 비활성입니다. 필요 시 --wt 옵션으로 실행하세요.");
|
|
51
|
-
console.log(` ${DIM}수동 attach 명령: ${buildManualAttachCommand(state.sessionName)}${RESET}\n`);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
1
|
+
import { attachSession, focusPane, focusWtPane } from "../../session.mjs";
|
|
2
|
+
import { DIM, RESET, WHITE } from "../../shared.mjs";
|
|
3
|
+
import { buildManualAttachCommand, launchAttachInWindowsTerminal, wantsWtAttachFallback } from "../services/attach-fallback.mjs";
|
|
4
|
+
import { resolveMember } from "../services/member-selector.mjs";
|
|
5
|
+
import { isNativeMode, isTeamAlive, isWtMode } from "../services/runtime-mode.mjs";
|
|
6
|
+
import { loadTeamState } from "../services/state-store.mjs";
|
|
7
|
+
import { fail, ok, warn } from "../render.mjs";
|
|
8
|
+
|
|
9
|
+
export async function teamFocus(args = []) {
|
|
10
|
+
const state = loadTeamState();
|
|
11
|
+
if (!state || !isTeamAlive(state)) {
|
|
12
|
+
console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if (isNativeMode(state)) {
|
|
16
|
+
console.log(`\n ${DIM}in-process 모드는 focus/attach 개념이 없습니다.${RESET}\n ${DIM}직접 지시: tfx multi send <대상> "메시지"${RESET}\n`);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const member = resolveMember(state, args[0]);
|
|
21
|
+
if (!member) {
|
|
22
|
+
console.log(`\n 사용법: ${WHITE}tfx multi focus <lead|이름|번호>${RESET}\n`);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (isWtMode(state)) {
|
|
27
|
+
const paneIndex = Number(/^wt:(\d+)$/.exec(member.pane || "")?.[1]);
|
|
28
|
+
if (!Number.isFinite(paneIndex)) {
|
|
29
|
+
console.log(`\n ${DIM}wt pane 인덱스 파싱 실패: ${member.pane}${RESET}\n`);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (focusWtPane(paneIndex, { layout: state?.wt?.layout || state?.layout || "1xN" })) ok(`${member.name} pane 포커스 이동 (wt)`);
|
|
33
|
+
else warn("wt pane 포커스 이동 실패 (WT_SESSION/wt.exe 상태 확인 필요)");
|
|
34
|
+
console.log("");
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
focusPane(member.pane, { zoom: false });
|
|
39
|
+
try {
|
|
40
|
+
attachSession(state.sessionName);
|
|
41
|
+
} catch (error) {
|
|
42
|
+
const allowWt = wantsWtAttachFallback(args);
|
|
43
|
+
if (allowWt && await launchAttachInWindowsTerminal(state.sessionName)) {
|
|
44
|
+
warn(`현재 터미널에서 attach 실패: ${error.message}`);
|
|
45
|
+
ok("Windows Terminal split-pane로 attach 재시도 창을 열었습니다.");
|
|
46
|
+
console.log(` ${DIM}수동 attach 명령: ${buildManualAttachCommand(state.sessionName)}${RESET}\n`);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
fail(`attach 실패: ${error.message}`);
|
|
50
|
+
warn(allowWt ? "WT 분할창 attach 자동 검증 실패 (session_attached 증가 없음)" : "자동 WT 분할은 기본 비활성입니다. 필요 시 --wt 옵션으로 실행하세요.");
|
|
51
|
+
console.log(` ${DIM}수동 attach 명령: ${buildManualAttachCommand(state.sessionName)}${RESET}\n`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import { AMBER, BOLD, DIM, GREEN, RESET } from "../../shared.mjs";
|
|
2
|
-
import { listSessions } from "../../session.mjs";
|
|
3
|
-
import { isNativeMode, isTeamAlive, isWtMode } from "../services/runtime-mode.mjs";
|
|
4
|
-
import { loadTeamState } from "../services/state-store.mjs";
|
|
5
|
-
|
|
6
|
-
export function teamList() {
|
|
7
|
-
const state = loadTeamState();
|
|
8
|
-
if (state && isTeamAlive(state) && (isNativeMode(state) || isWtMode(state))) {
|
|
9
|
-
console.log(`\n ${AMBER}${BOLD}⬡ 팀 세션 목록${RESET}\n`);
|
|
10
|
-
console.log(` ${GREEN}●${RESET} ${state.sessionName} ${DIM}(${isNativeMode(state) ? "in-process" : "wt"})${RESET}`);
|
|
11
|
-
console.log("");
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const sessions = listSessions();
|
|
16
|
-
if (!sessions.length) {
|
|
17
|
-
console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
console.log(`\n ${AMBER}${BOLD}⬡ 팀 세션 목록${RESET}\n`);
|
|
22
|
-
for (const session of sessions) console.log(` ${GREEN}●${RESET} ${session}`);
|
|
23
|
-
console.log("");
|
|
24
|
-
}
|
|
1
|
+
import { AMBER, BOLD, DIM, GREEN, RESET } from "../../shared.mjs";
|
|
2
|
+
import { listSessions } from "../../session.mjs";
|
|
3
|
+
import { isNativeMode, isTeamAlive, isWtMode } from "../services/runtime-mode.mjs";
|
|
4
|
+
import { loadTeamState } from "../services/state-store.mjs";
|
|
5
|
+
|
|
6
|
+
export function teamList() {
|
|
7
|
+
const state = loadTeamState();
|
|
8
|
+
if (state && isTeamAlive(state) && (isNativeMode(state) || isWtMode(state))) {
|
|
9
|
+
console.log(`\n ${AMBER}${BOLD}⬡ 팀 세션 목록${RESET}\n`);
|
|
10
|
+
console.log(` ${GREEN}●${RESET} ${state.sessionName} ${DIM}(${isNativeMode(state) ? "in-process" : "wt"})${RESET}`);
|
|
11
|
+
console.log("");
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const sessions = listSessions();
|
|
16
|
+
if (!sessions.length) {
|
|
17
|
+
console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
console.log(`\n ${AMBER}${BOLD}⬡ 팀 세션 목록${RESET}\n`);
|
|
22
|
+
for (const session of sessions) console.log(` ${GREEN}●${RESET} ${session}`);
|
|
23
|
+
console.log("");
|
|
24
|
+
}
|
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
import { startNativeSupervisor } from "../../services/native-control.mjs";
|
|
2
|
-
import { buildTasks } from "../../services/task-model.mjs";
|
|
3
|
-
|
|
4
|
-
export async function startInProcessTeam({ sessionId, task, lead, agents, subtasks, hubUrl }) {
|
|
5
|
-
const { runtime, members } = await startNativeSupervisor({
|
|
6
|
-
sessionId,
|
|
7
|
-
task,
|
|
8
|
-
lead,
|
|
9
|
-
agents,
|
|
10
|
-
subtasks,
|
|
11
|
-
hubUrl,
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
if (!runtime?.controlUrl) return null;
|
|
15
|
-
|
|
16
|
-
return {
|
|
17
|
-
sessionName: sessionId,
|
|
18
|
-
task,
|
|
19
|
-
lead,
|
|
20
|
-
agents,
|
|
21
|
-
layout: "native",
|
|
22
|
-
teammateMode: "in-process",
|
|
23
|
-
startedAt: Date.now(),
|
|
24
|
-
hubUrl,
|
|
25
|
-
members: members.map((member, index) => ({
|
|
26
|
-
role: member.role,
|
|
27
|
-
name: member.name,
|
|
28
|
-
cli: member.cli,
|
|
29
|
-
agentId: member.agentId,
|
|
30
|
-
pane: `native:${index}`,
|
|
31
|
-
subtask: member.subtask || null,
|
|
32
|
-
})),
|
|
33
|
-
panes: {},
|
|
34
|
-
tasks: buildTasks(subtasks, members.filter((member) => member.role === "worker")),
|
|
35
|
-
native: {
|
|
36
|
-
controlUrl: runtime.controlUrl,
|
|
37
|
-
supervisorPid: runtime.supervisorPid,
|
|
38
|
-
},
|
|
39
|
-
};
|
|
40
|
-
}
|
|
1
|
+
import { startNativeSupervisor } from "../../services/native-control.mjs";
|
|
2
|
+
import { buildTasks } from "../../services/task-model.mjs";
|
|
3
|
+
|
|
4
|
+
export async function startInProcessTeam({ sessionId, task, lead, agents, subtasks, hubUrl }) {
|
|
5
|
+
const { runtime, members } = await startNativeSupervisor({
|
|
6
|
+
sessionId,
|
|
7
|
+
task,
|
|
8
|
+
lead,
|
|
9
|
+
agents,
|
|
10
|
+
subtasks,
|
|
11
|
+
hubUrl,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
if (!runtime?.controlUrl) return null;
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
sessionName: sessionId,
|
|
18
|
+
task,
|
|
19
|
+
lead,
|
|
20
|
+
agents,
|
|
21
|
+
layout: "native",
|
|
22
|
+
teammateMode: "in-process",
|
|
23
|
+
startedAt: Date.now(),
|
|
24
|
+
hubUrl,
|
|
25
|
+
members: members.map((member, index) => ({
|
|
26
|
+
role: member.role,
|
|
27
|
+
name: member.name,
|
|
28
|
+
cli: member.cli,
|
|
29
|
+
agentId: member.agentId,
|
|
30
|
+
pane: `native:${index}`,
|
|
31
|
+
subtask: member.subtask || null,
|
|
32
|
+
})),
|
|
33
|
+
panes: {},
|
|
34
|
+
tasks: buildTasks(subtasks, members.filter((member) => member.role === "worker")),
|
|
35
|
+
native: {
|
|
36
|
+
controlUrl: runtime.controlUrl,
|
|
37
|
+
supervisorPid: runtime.supervisorPid,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -1,73 +1,73 @@
|
|
|
1
|
-
import { join } from "node:path";
|
|
2
|
-
|
|
3
|
-
import { buildCliCommand, startCliInPane } from "../../../pane.mjs";
|
|
4
|
-
import { orchestrate } from "../../../orchestrator.mjs";
|
|
5
|
-
import { attachSession, configureTeammateKeybindings, createSession } from "../../../session.mjs";
|
|
6
|
-
import { BOLD, DIM, GREEN, RESET } from "../../../shared.mjs";
|
|
7
|
-
import { toAgentId } from "../../services/member-selector.mjs";
|
|
8
|
-
import { PKG_ROOT, TEAM_PROFILE } from "../../services/state-store.mjs";
|
|
9
|
-
import { buildTasks } from "../../services/task-model.mjs";
|
|
10
|
-
import { ok, warn } from "../../render.mjs";
|
|
11
|
-
|
|
12
|
-
export async function startMuxTeam({ sessionId, task, lead, agents, subtasks, layout, hubUrl, teammateMode }) {
|
|
13
|
-
const paneCount = agents.length + 1;
|
|
14
|
-
const effectiveLayout = paneCount <= 4 ? layout : (layout === "Nx1" ? "Nx1" : "1xN");
|
|
15
|
-
console.log(` 레이아웃: ${effectiveLayout} (${paneCount} panes)`);
|
|
16
|
-
|
|
17
|
-
const session = createSession(sessionId, { layout: effectiveLayout, paneCount });
|
|
18
|
-
const leadTarget = session.panes[0];
|
|
19
|
-
startCliInPane(leadTarget, buildCliCommand(lead));
|
|
20
|
-
|
|
21
|
-
const members = [{ role: "lead", name: "lead", cli: lead, pane: leadTarget, agentId: toAgentId(lead, leadTarget) }];
|
|
22
|
-
const assignments = [];
|
|
23
|
-
for (let index = 0; index < agents.length; index += 1) {
|
|
24
|
-
const cli = agents[index];
|
|
25
|
-
const pane = session.panes[index + 1];
|
|
26
|
-
startCliInPane(pane, buildCliCommand(cli));
|
|
27
|
-
const worker = { role: "worker", name: `${cli}-${index + 1}`, cli, pane, subtask: subtasks[index], agentId: toAgentId(cli, pane) };
|
|
28
|
-
members.push(worker);
|
|
29
|
-
assignments.push({ target: pane, cli, subtask: subtasks[index] });
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
ok("CLI 초기화 대기 (3초)...");
|
|
33
|
-
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
34
|
-
await orchestrate(sessionId, assignments, { hubUrl, teammateMode, lead: { target: leadTarget, cli: lead, task } });
|
|
35
|
-
ok("리드/워커 프롬프트 주입 완료");
|
|
36
|
-
|
|
37
|
-
return {
|
|
38
|
-
sessionName: sessionId,
|
|
39
|
-
task,
|
|
40
|
-
lead,
|
|
41
|
-
agents,
|
|
42
|
-
layout: effectiveLayout,
|
|
43
|
-
teammateMode,
|
|
44
|
-
startedAt: Date.now(),
|
|
45
|
-
hubUrl,
|
|
46
|
-
members,
|
|
47
|
-
panes: Object.fromEntries(members.map((member) => [member.pane, {
|
|
48
|
-
role: member.role,
|
|
49
|
-
name: member.name,
|
|
50
|
-
cli: member.cli,
|
|
51
|
-
agentId: member.agentId,
|
|
52
|
-
subtask: member.subtask || null,
|
|
53
|
-
}])),
|
|
54
|
-
tasks: buildTasks(subtasks, members.filter((member) => member.role === "worker")),
|
|
55
|
-
postSave() {
|
|
56
|
-
const profilePrefix = TEAM_PROFILE === "team" ? "" : `TFX_TEAM_PROFILE=${TEAM_PROFILE} `;
|
|
57
|
-
const taskListCommand = `${profilePrefix}${process.execPath} ${join(PKG_ROOT, "bin", "triflux.mjs")} team tasks`;
|
|
58
|
-
configureTeammateKeybindings(sessionId, { inProcess: false, taskListCommand });
|
|
59
|
-
console.log(`\n ${GREEN}${BOLD}팀 세션 준비 완료${RESET}`);
|
|
60
|
-
console.log(` ${DIM}Shift+Down: 다음 팀메이트 전환${RESET}`);
|
|
61
|
-
console.log(` ${DIM}Shift+Tab / Shift+Left: 이전 팀메이트 전환${RESET}`);
|
|
62
|
-
console.log(` ${DIM}Escape: 현재 팀메이트 인터럽트${RESET}`);
|
|
63
|
-
console.log(` ${DIM}Ctrl+T: 태스크 목록${RESET}`);
|
|
64
|
-
console.log(` ${DIM}참고: Shift+Up은 Claude Code 미지원 (scroll-up 충돌). Shift+Tab 사용${RESET}`);
|
|
65
|
-
console.log(` ${DIM}Ctrl+B → D: 세션 분리 (백그라운드)${RESET}\n`);
|
|
66
|
-
if (process.stdout.isTTY && process.stdin.isTTY) attachSession(sessionId);
|
|
67
|
-
else {
|
|
68
|
-
warn("TTY 미지원 환경이라 자동 attach를 생략함");
|
|
69
|
-
console.log(` ${DIM}수동 연결: tfx multi attach${RESET}\n`);
|
|
70
|
-
}
|
|
71
|
-
},
|
|
72
|
-
};
|
|
73
|
-
}
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
|
|
3
|
+
import { buildCliCommand, startCliInPane } from "../../../pane.mjs";
|
|
4
|
+
import { orchestrate } from "../../../orchestrator.mjs";
|
|
5
|
+
import { attachSession, configureTeammateKeybindings, createSession } from "../../../session.mjs";
|
|
6
|
+
import { BOLD, DIM, GREEN, RESET } from "../../../shared.mjs";
|
|
7
|
+
import { toAgentId } from "../../services/member-selector.mjs";
|
|
8
|
+
import { PKG_ROOT, TEAM_PROFILE } from "../../services/state-store.mjs";
|
|
9
|
+
import { buildTasks } from "../../services/task-model.mjs";
|
|
10
|
+
import { ok, warn } from "../../render.mjs";
|
|
11
|
+
|
|
12
|
+
export async function startMuxTeam({ sessionId, task, lead, agents, subtasks, layout, hubUrl, teammateMode }) {
|
|
13
|
+
const paneCount = agents.length + 1;
|
|
14
|
+
const effectiveLayout = paneCount <= 4 ? layout : (layout === "Nx1" ? "Nx1" : "1xN");
|
|
15
|
+
console.log(` 레이아웃: ${effectiveLayout} (${paneCount} panes)`);
|
|
16
|
+
|
|
17
|
+
const session = createSession(sessionId, { layout: effectiveLayout, paneCount });
|
|
18
|
+
const leadTarget = session.panes[0];
|
|
19
|
+
startCliInPane(leadTarget, buildCliCommand(lead));
|
|
20
|
+
|
|
21
|
+
const members = [{ role: "lead", name: "lead", cli: lead, pane: leadTarget, agentId: toAgentId(lead, leadTarget) }];
|
|
22
|
+
const assignments = [];
|
|
23
|
+
for (let index = 0; index < agents.length; index += 1) {
|
|
24
|
+
const cli = agents[index];
|
|
25
|
+
const pane = session.panes[index + 1];
|
|
26
|
+
startCliInPane(pane, buildCliCommand(cli));
|
|
27
|
+
const worker = { role: "worker", name: `${cli}-${index + 1}`, cli, pane, subtask: subtasks[index], agentId: toAgentId(cli, pane) };
|
|
28
|
+
members.push(worker);
|
|
29
|
+
assignments.push({ target: pane, cli, subtask: subtasks[index] });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
ok("CLI 초기화 대기 (3초)...");
|
|
33
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
34
|
+
await orchestrate(sessionId, assignments, { hubUrl, teammateMode, lead: { target: leadTarget, cli: lead, task } });
|
|
35
|
+
ok("리드/워커 프롬프트 주입 완료");
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
sessionName: sessionId,
|
|
39
|
+
task,
|
|
40
|
+
lead,
|
|
41
|
+
agents,
|
|
42
|
+
layout: effectiveLayout,
|
|
43
|
+
teammateMode,
|
|
44
|
+
startedAt: Date.now(),
|
|
45
|
+
hubUrl,
|
|
46
|
+
members,
|
|
47
|
+
panes: Object.fromEntries(members.map((member) => [member.pane, {
|
|
48
|
+
role: member.role,
|
|
49
|
+
name: member.name,
|
|
50
|
+
cli: member.cli,
|
|
51
|
+
agentId: member.agentId,
|
|
52
|
+
subtask: member.subtask || null,
|
|
53
|
+
}])),
|
|
54
|
+
tasks: buildTasks(subtasks, members.filter((member) => member.role === "worker")),
|
|
55
|
+
postSave() {
|
|
56
|
+
const profilePrefix = TEAM_PROFILE === "team" ? "" : `TFX_TEAM_PROFILE=${TEAM_PROFILE} `;
|
|
57
|
+
const taskListCommand = `${profilePrefix}${process.execPath} ${join(PKG_ROOT, "bin", "triflux.mjs")} team tasks`;
|
|
58
|
+
configureTeammateKeybindings(sessionId, { inProcess: false, taskListCommand });
|
|
59
|
+
console.log(`\n ${GREEN}${BOLD}팀 세션 준비 완료${RESET}`);
|
|
60
|
+
console.log(` ${DIM}Shift+Down: 다음 팀메이트 전환${RESET}`);
|
|
61
|
+
console.log(` ${DIM}Shift+Tab / Shift+Left: 이전 팀메이트 전환${RESET}`);
|
|
62
|
+
console.log(` ${DIM}Escape: 현재 팀메이트 인터럽트${RESET}`);
|
|
63
|
+
console.log(` ${DIM}Ctrl+T: 태스크 목록${RESET}`);
|
|
64
|
+
console.log(` ${DIM}참고: Shift+Up은 Claude Code 미지원 (scroll-up 충돌). Shift+Tab 사용${RESET}`);
|
|
65
|
+
console.log(` ${DIM}Ctrl+B → D: 세션 분리 (백그라운드)${RESET}\n`);
|
|
66
|
+
if (process.stdout.isTTY && process.stdin.isTTY) attachSession(sessionId);
|
|
67
|
+
else {
|
|
68
|
+
warn("TTY 미지원 환경이라 자동 attach를 생략함");
|
|
69
|
+
console.log(` ${DIM}수동 연결: tfx multi attach${RESET}\n`);
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|