triflux 10.3.1 → 10.3.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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +22 -22
- 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/ansi.mjs +44 -28
- 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/conductor.mjs +2 -2
- 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/team/tui-lite.mjs +4 -4
- package/hub/team/tui.mjs +16 -12
- package/hub/tray.mjs +368 -368
- package/hub/workers/codex-mcp.mjs +507 -507
- package/hub/workers/factory.mjs +21 -21
- package/hud/constants.mjs +8 -2
- package/hud/providers/codex.mjs +11 -0
- package/hud/providers/gemini.mjs +21 -0
- package/package.json +1 -1
- package/scripts/claudemd-sync.mjs +11 -24
- 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/setup.mjs +23 -0
- 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/hub/team/ansi.mjs
CHANGED
|
@@ -249,39 +249,55 @@ export function padRight(str, len) {
|
|
|
249
249
|
return str + " ".repeat(pad);
|
|
250
250
|
}
|
|
251
251
|
|
|
252
|
-
//
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
let acc = 0;
|
|
252
|
+
// ANSI-preserving slice: ANSI escape를 보존하면서 visible width만큼 자름
|
|
253
|
+
function ansiSlice(str, maxWidth) {
|
|
254
|
+
let result = "";
|
|
255
|
+
let visWidth = 0;
|
|
256
|
+
let hasAnsi = false;
|
|
259
257
|
let i = 0;
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
if (
|
|
263
|
-
|
|
264
|
-
|
|
258
|
+
|
|
259
|
+
while (i < str.length) {
|
|
260
|
+
if (str.charCodeAt(i) === 0x1B) {
|
|
261
|
+
const rest = str.slice(i);
|
|
262
|
+
const m = rest.match(/^(\x1b(?:\[[0-9;]*[a-zA-Z]|\].*?(?:\x07|\x1b\\)))/);
|
|
263
|
+
if (m) {
|
|
264
|
+
result += m[1];
|
|
265
|
+
hasAnsi = true;
|
|
266
|
+
i += m[1].length;
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const cp = str.codePointAt(i);
|
|
272
|
+
const charLen = cp > 0xFFFF ? 2 : 1;
|
|
273
|
+
const cw = charWidth(cp);
|
|
274
|
+
|
|
275
|
+
if (visWidth + cw > maxWidth) break;
|
|
276
|
+
|
|
277
|
+
result += str.slice(i, i + charLen);
|
|
278
|
+
visWidth += cw;
|
|
279
|
+
i += charLen;
|
|
265
280
|
}
|
|
266
|
-
|
|
281
|
+
|
|
282
|
+
return { result, visWidth, hasAnsi };
|
|
267
283
|
}
|
|
268
284
|
|
|
269
|
-
// wcwidth-aware
|
|
285
|
+
// wcwidth-aware truncate: wide char 경계에서 자름 (ANSI 보존)
|
|
286
|
+
export function truncate(str, maxLen) {
|
|
287
|
+
if (wcswidth(str) <= maxLen) return str;
|
|
288
|
+
|
|
289
|
+
const { result, hasAnsi } = ansiSlice(str, maxLen - 1);
|
|
290
|
+
return hasAnsi ? result + RESET + "…" : result + "…";
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// wcwidth-aware clip: 정확히 width 셀에 맞게 자르고 패딩 (ANSI 보존, wide char 경계 보정)
|
|
270
294
|
export function clip(str, width) {
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
// wide char이 경계를 넘으면 공백으로 채움
|
|
278
|
-
const result = plain.slice(0, i) + " ".repeat(width - acc);
|
|
279
|
-
return result;
|
|
280
|
-
}
|
|
281
|
-
acc += cw;
|
|
282
|
-
i += char.length;
|
|
283
|
-
}
|
|
284
|
-
return str + " ".repeat(width - acc);
|
|
295
|
+
const vis = wcswidth(str);
|
|
296
|
+
if (vis <= width) return str + " ".repeat(width - vis);
|
|
297
|
+
|
|
298
|
+
const { result, visWidth, hasAnsi } = ansiSlice(str, width);
|
|
299
|
+
const suffix = hasAnsi ? RESET : "";
|
|
300
|
+
return result + suffix + " ".repeat(Math.max(0, width - visWidth));
|
|
285
301
|
}
|
|
286
302
|
|
|
287
303
|
// ── Catppuccin Mocha 색상 상수 ──
|
|
@@ -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
|
+
}
|