@triflux/remote 10.1.0 → 10.25.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/hub/pipe.mjs +239 -121
- package/hub/public/dashboard.html +61 -0
- package/hub/server.mjs +2165 -708
- package/hub/store-adapter.mjs +59 -26
- package/hub/store.mjs +356 -153
- package/hub/team/agent-map.json +26 -7
- package/hub/team/ansi.mjs +245 -146
- package/hub/team/backend.mjs +84 -17
- package/hub/team/build-worker-prompt.mjs +42 -0
- package/hub/team/check-mcp-hub.mjs +49 -0
- package/hub/team/claude-daemon-control.mjs +219 -0
- package/hub/team/claude-native-bridge.mjs +784 -0
- package/hub/team/claude-session-projection.mjs +65 -0
- package/hub/team/cli/commands/attach.mjs +57 -37
- package/hub/team/cli/commands/control.mjs +29 -8
- package/hub/team/cli/commands/debug.mjs +95 -74
- package/hub/team/cli/commands/focus.mjs +80 -53
- package/hub/team/cli/commands/interrupt.mjs +18 -6
- package/hub/team/cli/commands/kill.mjs +16 -5
- package/hub/team/cli/commands/list.mjs +31 -24
- package/hub/team/cli/commands/send.mjs +19 -6
- package/hub/team/cli/commands/start/index.mjs +154 -33
- package/hub/team/cli/commands/start/parse-args.mjs +89 -12
- package/hub/team/cli/commands/start/start-headless.mjs +125 -37
- package/hub/team/cli/commands/start/start-in-process.mjs +50 -40
- package/hub/team/cli/commands/start/start-mux.mjs +122 -73
- package/hub/team/cli/commands/start/start-wt.mjs +86 -69
- package/hub/team/cli/commands/status.mjs +43 -14
- package/hub/team/cli/commands/stop.mjs +11 -4
- package/hub/team/cli/commands/task.mjs +8 -3
- package/hub/team/cli/commands/tasks.mjs +13 -13
- package/hub/team/cli/index.mjs +2 -2
- package/hub/team/cli/manifest.mjs +38 -8
- package/hub/team/cli/render.mjs +52 -30
- package/hub/team/cli/services/attach-fallback.mjs +74 -54
- package/hub/team/cli/services/hub-client.mjs +44 -16
- package/hub/team/cli/services/member-selector.mjs +37 -30
- package/hub/team/cli/services/native-control.mjs +144 -116
- package/hub/team/cli/services/runtime-mode.mjs +55 -1
- package/hub/team/cli/services/state-store.mjs +66 -9
- package/hub/team/cli/services/task-model.mjs +40 -30
- package/hub/team/codex-review.mjs +521 -0
- package/hub/team/conductor-mesh-bridge.mjs +25 -24
- package/hub/team/conductor-registry.mjs +121 -0
- package/hub/team/conductor.mjs +1379 -671
- package/hub/team/consensus-meta.mjs +176 -0
- package/hub/team/dashboard-anchor.mjs +4 -5
- package/hub/team/dashboard-layout.mjs +3 -1
- package/hub/team/dashboard-open.mjs +20 -118
- package/hub/team/dashboard.mjs +76 -28
- package/hub/team/event-log.mjs +18 -10
- package/hub/team/execution-mode.mjs +241 -0
- package/hub/team/extract-completion-payload.mjs +110 -0
- package/hub/team/git-preflight.mjs +286 -0
- package/hub/team/handoff.mjs +49 -18
- package/hub/team/headless-bridge-session.mjs +17 -0
- package/hub/team/headless.mjs +2065 -1156
- package/hub/team/health-probe.mjs +232 -69
- package/hub/team/launcher-template.mjs +23 -13
- package/hub/team/lead-control.mjs +91 -4
- package/hub/team/mcp-selector.mjs +145 -0
- package/hub/team/native-supervisor.mjs +90 -26
- package/hub/team/native.mjs +74 -35
- package/hub/team/nativeProxy.mjs +184 -113
- package/hub/team/notify.mjs +147 -78
- package/hub/team/orchestrator.mjs +173 -161
- package/hub/team/pane.mjs +43 -15
- package/hub/team/process-cleanup.mjs +69 -25
- package/hub/team/psmux.mjs +1020 -279
- package/hub/team/recovery-store.mjs +149 -0
- package/hub/team/remote-probe.mjs +68 -52
- package/hub/team/remote-session.mjs +131 -59
- package/hub/team/remote-watcher.mjs +61 -33
- package/hub/team/retry-state-machine.mjs +267 -0
- package/hub/team/routing.mjs +51 -25
- package/hub/team/runtime-strategy.mjs +200 -0
- package/hub/team/schemas/worker-completion.json +39 -0
- package/hub/team/sentinel-capture.mjs +127 -0
- package/hub/team/session-sync.mjs +153 -6
- package/hub/team/session.mjs +605 -611
- package/hub/team/shared.mjs +13 -13
- package/hub/team/staleState.mjs +72 -30
- package/hub/team/swarm-cli.mjs +390 -0
- package/hub/team/swarm-hypervisor.mjs +1522 -193
- package/hub/team/swarm-intent.mjs +216 -0
- package/hub/team/swarm-locks.mjs +140 -25
- package/hub/team/swarm-planner.mjs +121 -21
- package/hub/team/swarm-preflight.mjs +317 -0
- package/hub/team/swarm-reconciler.mjs +128 -23
- package/hub/team/synapse-cli.mjs +458 -0
- package/hub/team/synapse-http.mjs +60 -0
- package/hub/team/synapse-registry.mjs +329 -0
- package/hub/team/terminal-opener.mjs +216 -0
- package/hub/team/tui-core.mjs +288 -0
- package/hub/team/tui-lite.mjs +234 -146
- package/hub/team/tui-remote-adapter.mjs +18 -34
- package/hub/team/tui-synapse.mjs +223 -0
- package/hub/team/tui-viewer.mjs +104 -44
- package/hub/team/tui-widgets.mjs +262 -0
- package/hub/team/tui.mjs +927 -437
- package/hub/team/worker-completion-validator.mjs +84 -0
- package/hub/team/worker-sandbox.mjs +93 -0
- package/hub/team/worker-signal.mjs +263 -0
- package/hub/team/worker-signal.types.d.ts +52 -0
- package/hub/team/worktree-lifecycle.mjs +517 -63
- package/hub/team/wt-manager.mjs +297 -26
- package/hub/tools.mjs +474 -252
- package/hub/tray.mjs +411 -368
- package/hub/workers/claude-worker.mjs +162 -118
- package/hub/workers/codex-app-server-worker.mjs +1015 -0
- package/hub/workers/codex-mcp.mjs +724 -507
- package/hub/workers/delegator-mcp.mjs +662 -335
- package/hub/workers/factory.mjs +100 -21
- package/hub/workers/gemini-worker.mjs +179 -86
- package/hub/workers/interface.mjs +23 -2
- package/hub/workers/lib/jsonrpc-stdio.mjs +463 -0
- package/hub/workers/worker-utils.mjs +22 -16
- package/package.json +4 -3
- package/scripts/lib/agent-json.mjs +27 -0
- package/scripts/lib/async.mjs +174 -0
- package/scripts/lib/claudemd-scanner.mjs +223 -0
- package/scripts/lib/cli-agy.mjs +62 -0
- package/scripts/lib/cli-claude.mjs +78 -0
- package/scripts/lib/cli-codex.mjs +199 -0
- package/scripts/lib/cli-gemini.mjs +67 -0
- package/scripts/lib/codex-recovery.sh +50 -0
- package/scripts/lib/context.mjs +67 -0
- package/scripts/lib/cross-review-utils.mjs +59 -0
- package/scripts/lib/doctor-env-checks.mjs +121 -0
- package/scripts/lib/dynamic-route-cli.mjs +107 -0
- package/scripts/lib/env-probe.mjs +269 -0
- package/scripts/lib/env.mjs +14 -0
- package/scripts/lib/gemini-profiles.mjs +119 -0
- package/scripts/lib/handoff.mjs +187 -0
- package/scripts/lib/hook-utils.mjs +16 -0
- package/scripts/lib/hub.mjs +48 -0
- package/scripts/lib/keyword-rules.mjs +195 -0
- package/scripts/lib/logger.mjs +105 -0
- package/scripts/lib/mcp-filter.mjs +883 -0
- package/scripts/lib/mcp-gateway-servers.mjs +70 -0
- package/scripts/lib/mcp-guard-engine.mjs +2120 -0
- package/scripts/lib/mcp-health.mjs +773 -0
- package/scripts/lib/mcp-manifest.mjs +89 -0
- package/scripts/lib/mcp-server-catalog.mjs +355 -0
- package/scripts/lib/pid.mjs +63 -0
- package/scripts/lib/process-utils.mjs +26 -0
- package/scripts/lib/psmux-info.mjs +179 -0
- package/scripts/lib/quota.mjs +47 -0
- package/scripts/lib/remote-spawn-transfer.mjs +226 -0
- package/scripts/lib/skill-state.mjs +220 -0
- package/scripts/lib/skill-template.mjs +375 -0
- package/scripts/lib/team.mjs +75 -0
- package/scripts/lib/timeout.mjs +66 -0
- package/scripts/lib/tmp.mjs +26 -0
- package/scripts/lib/toml.mjs +113 -0
|
@@ -1,69 +1,86 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
export async function startWtTeam({
|
|
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
|
-
|
|
1
|
+
import { buildCliCommand } from "../../../pane.mjs";
|
|
2
|
+
import { createWtSession } from "../../../session.mjs";
|
|
3
|
+
import { warn } from "../../render.mjs";
|
|
4
|
+
import { toAgentId } from "../../services/member-selector.mjs";
|
|
5
|
+
import { buildTasks } from "../../services/task-model.mjs";
|
|
6
|
+
|
|
7
|
+
export async function startWtTeam({
|
|
8
|
+
sessionId,
|
|
9
|
+
task,
|
|
10
|
+
lead,
|
|
11
|
+
agents,
|
|
12
|
+
subtasks,
|
|
13
|
+
layout,
|
|
14
|
+
hubUrl,
|
|
15
|
+
}) {
|
|
16
|
+
const paneCount = agents.length + 1;
|
|
17
|
+
const effectiveLayout = layout === "Nx1" ? "Nx1" : "1xN";
|
|
18
|
+
if (layout !== effectiveLayout)
|
|
19
|
+
warn(`wt 모드에서 ${layout} 레이아웃은 미지원 — ${effectiveLayout}로 대체`);
|
|
20
|
+
console.log(` 레이아웃: ${effectiveLayout} (${paneCount} panes)`);
|
|
21
|
+
|
|
22
|
+
const session = await createWtSession(sessionId, {
|
|
23
|
+
layout: effectiveLayout,
|
|
24
|
+
paneCommands: [
|
|
25
|
+
{ title: `${sessionId}-lead`, command: buildCliCommand(lead) },
|
|
26
|
+
...agents.map((cli, index) => ({
|
|
27
|
+
title: `${sessionId}-${cli}-${index + 1}`,
|
|
28
|
+
command: buildCliCommand(cli),
|
|
29
|
+
})),
|
|
30
|
+
],
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const members = [
|
|
34
|
+
{
|
|
35
|
+
role: "lead",
|
|
36
|
+
name: "lead",
|
|
37
|
+
cli: lead,
|
|
38
|
+
pane: session.panes[0] || "wt:0",
|
|
39
|
+
agentId: toAgentId(lead, session.panes[0] || "wt:0"),
|
|
40
|
+
},
|
|
41
|
+
...agents.map((cli, index) => {
|
|
42
|
+
const pane = session.panes[index + 1] || `wt:${index + 1}`;
|
|
43
|
+
return {
|
|
44
|
+
role: "worker",
|
|
45
|
+
name: `${cli}-${index + 1}`,
|
|
46
|
+
cli,
|
|
47
|
+
pane,
|
|
48
|
+
subtask: subtasks[index],
|
|
49
|
+
agentId: toAgentId(cli, pane),
|
|
50
|
+
};
|
|
51
|
+
}),
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
sessionName: sessionId,
|
|
56
|
+
task,
|
|
57
|
+
lead,
|
|
58
|
+
agents,
|
|
59
|
+
layout: effectiveLayout,
|
|
60
|
+
teammateMode: "wt",
|
|
61
|
+
startedAt: Date.now(),
|
|
62
|
+
hubUrl,
|
|
63
|
+
members,
|
|
64
|
+
panes: Object.fromEntries(
|
|
65
|
+
members.map((member) => [
|
|
66
|
+
member.pane,
|
|
67
|
+
{
|
|
68
|
+
role: member.role,
|
|
69
|
+
name: member.name,
|
|
70
|
+
cli: member.cli,
|
|
71
|
+
agentId: member.agentId,
|
|
72
|
+
subtask: member.subtask || null,
|
|
73
|
+
},
|
|
74
|
+
]),
|
|
75
|
+
),
|
|
76
|
+
tasks: buildTasks(
|
|
77
|
+
subtasks,
|
|
78
|
+
members.filter((member) => member.role === "worker"),
|
|
79
|
+
),
|
|
80
|
+
wt: {
|
|
81
|
+
windowId: 0,
|
|
82
|
+
layout: effectiveLayout,
|
|
83
|
+
paneCount: session.paneCount,
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
@@ -1,16 +1,22 @@
|
|
|
1
|
-
import { AMBER, BOLD, DIM, GRAY, GREEN, RED, RESET } from "../../shared.mjs";
|
|
2
1
|
import { hasWindowsTerminalSession } from "../../session.mjs";
|
|
2
|
+
import { AMBER, BOLD, DIM, GRAY, GREEN, RED, RESET } from "../../shared.mjs";
|
|
3
|
+
import { formatCompletionSuffix } from "../render.mjs";
|
|
3
4
|
import { fetchHubTaskList, nativeGetStatus } from "../services/hub-client.mjs";
|
|
4
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
isNativeMode,
|
|
7
|
+
isTeamAlive,
|
|
8
|
+
isWtMode,
|
|
9
|
+
} from "../services/runtime-mode.mjs";
|
|
5
10
|
import { loadTeamState } from "../services/state-store.mjs";
|
|
6
|
-
import { formatCompletionSuffix } from "../render.mjs";
|
|
7
11
|
|
|
8
12
|
export async function teamStatus(args = []) {
|
|
9
13
|
const json = process.env.TFX_OUTPUT_JSON === "1" || args.includes("--json");
|
|
10
14
|
const state = loadTeamState();
|
|
11
15
|
if (!state) {
|
|
12
16
|
if (json) {
|
|
13
|
-
process.stdout.write(
|
|
17
|
+
process.stdout.write(
|
|
18
|
+
`${JSON.stringify({ status: "offline", sessionName: null, alive: false }, null, 2)}\n`,
|
|
19
|
+
);
|
|
14
20
|
return;
|
|
15
21
|
}
|
|
16
22
|
console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
|
|
@@ -48,37 +54,60 @@ export async function teamStatus(args = []) {
|
|
|
48
54
|
return;
|
|
49
55
|
}
|
|
50
56
|
|
|
51
|
-
console.log(
|
|
57
|
+
console.log(
|
|
58
|
+
`\n ${AMBER}${BOLD}⬡ tfx multi${RESET} ${alive ? `${GREEN}● active${RESET}` : `${RED}● dead${RESET}`}\n`,
|
|
59
|
+
);
|
|
52
60
|
console.log(` 세션: ${state.sessionName}`);
|
|
53
61
|
console.log(` 모드: ${state.teammateMode || "tmux"}`);
|
|
54
62
|
console.log(` 리드: ${state.lead || "claude"}`);
|
|
55
63
|
console.log(` 워커: ${(state.agents || []).join(", ")}`);
|
|
56
|
-
console.log(
|
|
64
|
+
console.log(
|
|
65
|
+
` Uptime: ${alive ? `${Math.round((Date.now() - state.startedAt) / 60000)}분` : "-"}`,
|
|
66
|
+
);
|
|
57
67
|
console.log(` 태스크: ${(state.tasks || []).length}`);
|
|
58
68
|
if (isWtMode(state) && !hasWindowsTerminalSession()) {
|
|
59
|
-
console.log(
|
|
69
|
+
console.log(
|
|
70
|
+
` ${DIM}WT_SESSION 미감지: 생존성은 heuristics로 판정됨${RESET}`,
|
|
71
|
+
);
|
|
60
72
|
}
|
|
61
73
|
|
|
62
74
|
for (const member of state.members || []) {
|
|
63
|
-
console.log(
|
|
75
|
+
console.log(
|
|
76
|
+
` - ${member.name} (${member.cli}) ${DIM}${member.role}${RESET} ${DIM}${member.pane}${RESET}`,
|
|
77
|
+
);
|
|
64
78
|
}
|
|
65
79
|
|
|
66
80
|
if (isNativeMode(state) && alive) {
|
|
67
81
|
for (const member of payload.nativeMembers) {
|
|
68
|
-
console.log(
|
|
82
|
+
console.log(
|
|
83
|
+
` • ${member.name}: ${member.status}${formatCompletionSuffix(member)}${member.lastPreview ? ` ${DIM}${member.lastPreview}${RESET}` : ""}`,
|
|
84
|
+
);
|
|
69
85
|
}
|
|
70
86
|
}
|
|
71
87
|
|
|
72
88
|
if (alive) {
|
|
73
|
-
const hubTasks = payload.hubTasks || await fetchHubTaskList(state);
|
|
89
|
+
const hubTasks = payload.hubTasks || (await fetchHubTaskList(state));
|
|
74
90
|
if (hubTasks.length) {
|
|
75
|
-
const completed = hubTasks.filter(
|
|
91
|
+
const completed = hubTasks.filter(
|
|
92
|
+
(task) => task.status === "completed",
|
|
93
|
+
).length;
|
|
76
94
|
const failed = hubTasks.filter((task) => task.status === "failed").length;
|
|
77
|
-
console.log(
|
|
95
|
+
console.log(
|
|
96
|
+
`\n ${BOLD}Hub Tasks${RESET} ${DIM}(${completed}/${hubTasks.length} done)${RESET}`,
|
|
97
|
+
);
|
|
78
98
|
for (const task of hubTasks) {
|
|
79
|
-
const icon =
|
|
99
|
+
const icon =
|
|
100
|
+
task.status === "completed"
|
|
101
|
+
? `${GREEN}✓${RESET}`
|
|
102
|
+
: task.status === "in_progress"
|
|
103
|
+
? `${AMBER}●${RESET}`
|
|
104
|
+
: task.status === "failed"
|
|
105
|
+
? `${RED}✗${RESET}`
|
|
106
|
+
: `${GRAY}○${RESET}`;
|
|
80
107
|
const owner = task.owner ? ` ${GRAY}[${task.owner}]${RESET}` : "";
|
|
81
|
-
console.log(
|
|
108
|
+
console.log(
|
|
109
|
+
` ${icon} ${task.subject || task.description?.slice(0, 50) || ""}${owner}`,
|
|
110
|
+
);
|
|
82
111
|
}
|
|
83
112
|
if (failed > 0) console.log(` ${RED}⚠ ${failed}건 실패${RESET}`);
|
|
84
113
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { closeWtSession, killSession, sessionExists } from "../../session.mjs";
|
|
2
2
|
import { DIM, RESET } from "../../shared.mjs";
|
|
3
|
+
import { ok } from "../render.mjs";
|
|
3
4
|
import { nativeRequest } from "../services/native-control.mjs";
|
|
4
5
|
import { isNativeMode, isWtMode } from "../services/runtime-mode.mjs";
|
|
5
6
|
import { clearTeamState, loadTeamState } from "../services/state-store.mjs";
|
|
6
|
-
import { ok } from "../render.mjs";
|
|
7
7
|
|
|
8
8
|
export async function teamStop() {
|
|
9
9
|
const state = loadTeamState();
|
|
@@ -14,11 +14,18 @@ export async function teamStop() {
|
|
|
14
14
|
|
|
15
15
|
if (isNativeMode(state)) {
|
|
16
16
|
await nativeRequest(state, "/stop", {});
|
|
17
|
-
try {
|
|
17
|
+
try {
|
|
18
|
+
process.kill(state.native.supervisorPid, "SIGTERM");
|
|
19
|
+
} catch {}
|
|
18
20
|
ok(`세션 종료: ${state.sessionName}`);
|
|
19
21
|
} else if (isWtMode(state)) {
|
|
20
|
-
const closed = closeWtSession({
|
|
21
|
-
|
|
22
|
+
const closed = closeWtSession({
|
|
23
|
+
layout: state?.wt?.layout || state?.layout || "1xN",
|
|
24
|
+
paneCount: state?.wt?.paneCount ?? (state.members || []).length,
|
|
25
|
+
});
|
|
26
|
+
ok(
|
|
27
|
+
`세션 종료: ${state.sessionName}${closed ? ` (${closed} panes closed)` : ""}`,
|
|
28
|
+
);
|
|
22
29
|
} else if (sessionExists(state.sessionName)) {
|
|
23
30
|
killSession(state.sessionName);
|
|
24
31
|
ok(`세션 종료: ${state.sessionName}`);
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { DIM, RESET, WHITE } from "../../shared.mjs";
|
|
2
|
+
import { ok } from "../render.mjs";
|
|
2
3
|
import { isTeamAlive } from "../services/runtime-mode.mjs";
|
|
3
4
|
import { loadTeamState, saveTeamState } from "../services/state-store.mjs";
|
|
4
|
-
import {
|
|
5
|
-
|
|
5
|
+
import {
|
|
6
|
+
normalizeTaskStatus,
|
|
7
|
+
updateTaskStatus,
|
|
8
|
+
} from "../services/task-model.mjs";
|
|
6
9
|
|
|
7
10
|
export function teamTaskUpdate(args = []) {
|
|
8
11
|
const state = loadTeamState();
|
|
@@ -14,7 +17,9 @@ export function teamTaskUpdate(args = []) {
|
|
|
14
17
|
const nextStatus = normalizeTaskStatus(args[0]);
|
|
15
18
|
const taskId = String(args[1] || "").toUpperCase();
|
|
16
19
|
if (!nextStatus || !taskId) {
|
|
17
|
-
console.log(
|
|
20
|
+
console.log(
|
|
21
|
+
`\n 사용법: ${WHITE}tfx multi task <pending|progress|done> <T1>${RESET}\n`,
|
|
22
|
+
);
|
|
18
23
|
return;
|
|
19
24
|
}
|
|
20
25
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { DIM, RESET } from "../../shared.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
export function teamTasks() {
|
|
7
|
-
const state = loadTeamState();
|
|
8
|
-
if (!state || !isTeamAlive(state)) {
|
|
9
|
-
console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
renderTasks(state.tasks || []);
|
|
13
|
-
}
|
|
1
|
+
import { DIM, RESET } from "../../shared.mjs";
|
|
2
|
+
import { renderTasks } from "../render.mjs";
|
|
3
|
+
import { isTeamAlive } from "../services/runtime-mode.mjs";
|
|
4
|
+
import { loadTeamState } from "../services/state-store.mjs";
|
|
5
|
+
|
|
6
|
+
export function teamTasks() {
|
|
7
|
+
const state = loadTeamState();
|
|
8
|
+
if (!state || !isTeamAlive(state)) {
|
|
9
|
+
console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
renderTasks(state.tasks || []);
|
|
13
|
+
}
|
package/hub/team/cli/index.mjs
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { renderTeamHelp } from "./help.mjs";
|
|
2
|
-
import { resolveTeamCommand } from "./manifest.mjs";
|
|
3
1
|
import { teamAttach } from "./commands/attach.mjs";
|
|
4
2
|
import { teamControl } from "./commands/control.mjs";
|
|
5
3
|
import { teamDebug } from "./commands/debug.mjs";
|
|
@@ -13,6 +11,8 @@ import { teamStatus } from "./commands/status.mjs";
|
|
|
13
11
|
import { teamStop } from "./commands/stop.mjs";
|
|
14
12
|
import { teamTaskUpdate } from "./commands/task.mjs";
|
|
15
13
|
import { teamTasks } from "./commands/tasks.mjs";
|
|
14
|
+
import { renderTeamHelp } from "./help.mjs";
|
|
15
|
+
import { resolveTeamCommand } from "./manifest.mjs";
|
|
16
16
|
|
|
17
17
|
const handlers = {
|
|
18
18
|
attach: teamAttach,
|
|
@@ -1,18 +1,46 @@
|
|
|
1
1
|
export const TEAM_COMMANDS = [
|
|
2
2
|
{ name: "status", usage: "tfx multi status", desc: "현재 팀 상태" },
|
|
3
|
-
{
|
|
3
|
+
{
|
|
4
|
+
name: "debug",
|
|
5
|
+
usage: "tfx multi debug [--lines 30]",
|
|
6
|
+
desc: "강화 디버그 출력",
|
|
7
|
+
},
|
|
4
8
|
{ name: "tasks", usage: "tfx multi tasks", desc: "공유 태스크 목록" },
|
|
5
|
-
{
|
|
9
|
+
{
|
|
10
|
+
name: "task",
|
|
11
|
+
usage: "tfx multi task <pending|progress|done> <T1>",
|
|
12
|
+
desc: "태스크 상태 갱신",
|
|
13
|
+
},
|
|
6
14
|
{ name: "attach", usage: "tfx multi attach [--wt]", desc: "세션 재연결" },
|
|
7
|
-
{
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
15
|
+
{
|
|
16
|
+
name: "focus",
|
|
17
|
+
usage: "tfx multi focus <lead|이름|번호> [--wt]",
|
|
18
|
+
desc: "특정 팀메이트 포커스",
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: "send",
|
|
22
|
+
usage: 'tfx multi send <lead|이름|번호> "msg"',
|
|
23
|
+
desc: "팀메이트에 메시지 주입",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: "interrupt",
|
|
27
|
+
usage: "tfx multi interrupt <대상>",
|
|
28
|
+
desc: "팀메이트 인터럽트(C-c)",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: "control",
|
|
32
|
+
usage: "tfx multi control <대상> <cmd> [사유]",
|
|
33
|
+
desc: "리드 제어명령 전송",
|
|
34
|
+
},
|
|
11
35
|
{ name: "stop", usage: "tfx multi stop", desc: "graceful 종료" },
|
|
12
36
|
{ name: "kill", usage: "tfx multi kill", desc: "모든 팀 세션 강제 종료" },
|
|
13
37
|
{ name: "list", usage: "tfx multi list", desc: "활성 세션 목록" },
|
|
14
38
|
{ name: "help", usage: "tfx multi help", desc: "도움말" },
|
|
15
|
-
{
|
|
39
|
+
{
|
|
40
|
+
name: "start",
|
|
41
|
+
usage: "tfx multi start [options]",
|
|
42
|
+
desc: "새 팀 세션 시작 (기본 커맨드)",
|
|
43
|
+
},
|
|
16
44
|
];
|
|
17
45
|
|
|
18
46
|
export const TEAM_COMMAND_ALIASES = new Map([
|
|
@@ -25,5 +53,7 @@ export const TEAM_SUBCOMMANDS = new Set(TEAM_COMMANDS.map(({ name }) => name));
|
|
|
25
53
|
export function resolveTeamCommand(raw) {
|
|
26
54
|
if (typeof raw !== "string") return null;
|
|
27
55
|
const command = raw.toLowerCase();
|
|
28
|
-
return TEAM_SUBCOMMANDS.has(command)
|
|
56
|
+
return TEAM_SUBCOMMANDS.has(command)
|
|
57
|
+
? command
|
|
58
|
+
: TEAM_COMMAND_ALIASES.get(command) || null;
|
|
29
59
|
}
|
package/hub/team/cli/render.mjs
CHANGED
|
@@ -1,30 +1,52 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
1
|
+
import {
|
|
2
|
+
AMBER,
|
|
3
|
+
BOLD,
|
|
4
|
+
DIM,
|
|
5
|
+
GRAY,
|
|
6
|
+
GREEN,
|
|
7
|
+
RED,
|
|
8
|
+
RESET,
|
|
9
|
+
WHITE,
|
|
10
|
+
YELLOW,
|
|
11
|
+
} from "../shared.mjs";
|
|
12
|
+
|
|
13
|
+
export function ok(msg) {
|
|
14
|
+
console.log(` ${GREEN}✓${RESET} ${msg}`);
|
|
15
|
+
}
|
|
16
|
+
export function warn(msg) {
|
|
17
|
+
console.log(` ${YELLOW}⚠${RESET} ${msg}`);
|
|
18
|
+
}
|
|
19
|
+
export function fail(msg) {
|
|
20
|
+
console.log(` ${RED}✗${RESET} ${msg}`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function renderTasks(tasks = []) {
|
|
24
|
+
if (!tasks.length) {
|
|
25
|
+
console.log(`\n ${DIM}태스크 없음${RESET}\n`);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
console.log(`\n ${AMBER}${BOLD}⬡ Team Tasks${RESET}\n`);
|
|
30
|
+
for (const task of tasks) {
|
|
31
|
+
const dep = task.depends_on?.length
|
|
32
|
+
? ` ${DIM}(deps: ${task.depends_on.join(",")})${RESET}`
|
|
33
|
+
: "";
|
|
34
|
+
const owner = task.owner ? ` ${GRAY}[${task.owner}]${RESET}` : "";
|
|
35
|
+
console.log(
|
|
36
|
+
` ${WHITE}${task.id}${RESET} ${String(task.status || "").padEnd(11)} ${task.title}${owner}${dep}`,
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
console.log("");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function formatCompletionSuffix(member) {
|
|
43
|
+
if (!member?.completionStatus) return "";
|
|
44
|
+
if (member.completionStatus === "abnormal") {
|
|
45
|
+
return ` ${RED}[abnormal:${member.completionReason || "unknown"}]${RESET}`;
|
|
46
|
+
}
|
|
47
|
+
if (member.completionStatus === "normal")
|
|
48
|
+
return ` ${GREEN}[route-ok]${RESET}`;
|
|
49
|
+
if (member.completionStatus === "unchecked")
|
|
50
|
+
return ` ${GRAY}[route-unchecked]${RESET}`;
|
|
51
|
+
return "";
|
|
52
|
+
}
|
|
@@ -1,54 +1,74 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
getSessionAttachedCount,
|
|
5
|
-
hasWindowsTerminal,
|
|
6
|
-
resolveAttachCommand,
|
|
7
|
-
} from "../../session.mjs";
|
|
8
|
-
import { PKG_ROOT } from "./state-store.mjs";
|
|
9
|
-
|
|
10
|
-
export async function launchAttachInWindowsTerminal(sessionName) {
|
|
11
|
-
if (!hasWindowsTerminal()) return false;
|
|
12
|
-
|
|
13
|
-
let attachSpec;
|
|
14
|
-
try {
|
|
15
|
-
attachSpec = resolveAttachCommand(sessionName);
|
|
16
|
-
} catch {
|
|
17
|
-
return false;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const beforeAttached = getSessionAttachedCount(sessionName);
|
|
21
|
-
try {
|
|
22
|
-
const child = spawn(
|
|
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
|
-
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
getSessionAttachedCount,
|
|
5
|
+
hasWindowsTerminal,
|
|
6
|
+
resolveAttachCommand,
|
|
7
|
+
} from "../../session.mjs";
|
|
8
|
+
import { PKG_ROOT } from "./state-store.mjs";
|
|
9
|
+
|
|
10
|
+
export async function launchAttachInWindowsTerminal(sessionName) {
|
|
11
|
+
if (!hasWindowsTerminal()) return false;
|
|
12
|
+
|
|
13
|
+
let attachSpec;
|
|
14
|
+
try {
|
|
15
|
+
attachSpec = resolveAttachCommand(sessionName);
|
|
16
|
+
} catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const beforeAttached = getSessionAttachedCount(sessionName);
|
|
21
|
+
try {
|
|
22
|
+
const child = spawn(
|
|
23
|
+
"wt",
|
|
24
|
+
[
|
|
25
|
+
"-w",
|
|
26
|
+
"0",
|
|
27
|
+
"split-pane",
|
|
28
|
+
"-V",
|
|
29
|
+
"-d",
|
|
30
|
+
PKG_ROOT,
|
|
31
|
+
attachSpec.command,
|
|
32
|
+
...attachSpec.args,
|
|
33
|
+
],
|
|
34
|
+
{
|
|
35
|
+
detached: true,
|
|
36
|
+
stdio: "ignore",
|
|
37
|
+
windowsHide: false,
|
|
38
|
+
},
|
|
39
|
+
);
|
|
40
|
+
child.unref();
|
|
41
|
+
|
|
42
|
+
if (beforeAttached == null) return true;
|
|
43
|
+
const deadline = Date.now() + 3500;
|
|
44
|
+
while (Date.now() < deadline) {
|
|
45
|
+
await new Promise((resolve) => setTimeout(resolve, 120));
|
|
46
|
+
const nowAttached = getSessionAttachedCount(sessionName);
|
|
47
|
+
if (typeof nowAttached === "number" && nowAttached > beforeAttached)
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
} catch {}
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function buildManualAttachCommand(sessionName) {
|
|
55
|
+
try {
|
|
56
|
+
const spec = resolveAttachCommand(sessionName);
|
|
57
|
+
return [spec.command, ...spec.args]
|
|
58
|
+
.map((value) => {
|
|
59
|
+
const text = String(value);
|
|
60
|
+
return /\s/.test(text) ? `"${text.replace(/"/g, '\\"')}"` : text;
|
|
61
|
+
})
|
|
62
|
+
.join(" ");
|
|
63
|
+
} catch {
|
|
64
|
+
return `tmux attach-session -t ${sessionName}`;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function wantsWtAttachFallback(args = [], env = process.env) {
|
|
69
|
+
return (
|
|
70
|
+
args.includes("--wt") ||
|
|
71
|
+
args.includes("--spawn-wt") ||
|
|
72
|
+
env.TFX_ATTACH_WT_AUTO === "1"
|
|
73
|
+
);
|
|
74
|
+
}
|