@triflux/remote 10.50.2 → 10.52.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/cto/brief.mjs +0 -20
- package/cto/collect.mjs +1 -200
- package/cto/current.schema.json +2 -23
- package/cto/hygiene.mjs +2 -61
- package/cto/lake-root.mjs +0 -56
- package/cto/status.mjs +5 -187
- package/hub/index.mjs +0 -17
- package/hub/team/agent-map.json +0 -1
- package/hub/team/agents-row-attach.py +120 -0
- package/hub/team/agents-row.mjs +200 -0
- package/hub/team/ansi.mjs +1 -35
- package/hub/team/backend.mjs +9 -145
- package/hub/team/claude-daemon-control.mjs +1 -2
- package/hub/team/claude-session-projection.mjs +5 -73
- package/hub/team/cli/commands/attach.mjs +1 -17
- package/hub/team/cli/commands/control.mjs +12 -38
- package/hub/team/cli/commands/debug.mjs +2 -39
- package/hub/team/cli/commands/focus.mjs +5 -31
- package/hub/team/cli/commands/interrupt.mjs +6 -25
- package/hub/team/cli/commands/kill.mjs +1 -30
- package/hub/team/cli/commands/list.mjs +0 -16
- package/hub/team/cli/commands/send.mjs +6 -26
- package/hub/team/cli/commands/start/index.mjs +42 -133
- package/hub/team/cli/commands/start/parse-args.mjs +15 -40
- package/hub/team/cli/commands/start/start-headless.mjs +138 -105
- package/hub/team/cli/commands/start/start-mux.mjs +2 -7
- package/hub/team/cli/commands/status.mjs +2 -57
- package/hub/team/cli/commands/stop.mjs +14 -19
- package/hub/team/cli/help.mjs +4 -5
- package/hub/team/cli/manifest.mjs +5 -1
- package/hub/team/cli/render.mjs +0 -12
- package/hub/team/cli/services/runtime-mode.mjs +27 -98
- package/hub/team/cli/services/state-store.mjs +5 -20
- package/hub/team/handoff.mjs +3 -34
- package/hub/team/headless.mjs +108 -1296
- package/hub/team/orchestrator.mjs +4 -30
- package/hub/team/pane.mjs +6 -28
- package/hub/team/psmux.mjs +3 -34
- package/hub/team/remote-session.mjs +1 -2
- package/hub/team/session-context.mjs +1 -1
- package/hub/team/session.mjs +7 -91
- package/hub/team/tui-core.mjs +1 -1
- package/hub/team/tui-viewer.mjs +11 -58
- package/hub/team/tui.mjs +8 -306
- package/hub/team/uds-orchestrator.mjs +2 -580
- package/hub/team/wt-manager.mjs +2 -2
- package/hub/workers/factory.mjs +6 -84
- package/hub/workers/interface.mjs +1 -7
- package/hub/workers/lib/jsonrpc-core.mjs +11 -0
- package/hub/workers/lib/jsonrpc-ws-uds.mjs +5 -8
- package/package.json +3 -8
- package/scripts/lib/agent-route-policy.mjs +143 -62
- package/scripts/lib/cli-claude.mjs +0 -14
- package/scripts/lib/env-probe.mjs +2 -131
- package/scripts/lib/gemini-profiles.mjs +2 -69
- package/scripts/lib/legacy-mcp-cleanup.mjs +283 -0
- package/scripts/lib/machine-profile.mjs +3 -17
- package/scripts/lib/mcp-filter.mjs +109 -53
- package/scripts/lib/mcp-guard-engine.mjs +40 -98
- package/scripts/lib/mcp-health.mjs +2 -2
- package/scripts/lib/mcp-server-catalog.mjs +2 -2
- package/scripts/lib/psmux-info.mjs +11 -59
- package/scripts/lib/skill-template.mjs +0 -245
- package/hub/hub-lifecycle.mjs +0 -248
- package/hub/pipe.mjs +0 -825
- package/hub/promote-penalties.mjs +0 -138
- package/hub/public/dashboard.html +0 -416
- package/hub/schema.sql +0 -209
- package/hub/server.mjs +0 -3048
- package/hub/store-adapter.mjs +0 -182
- package/hub/store.mjs +0 -1736
- package/hub/team/build-worker-prompt.mjs +0 -92
- package/hub/team/check-mcp-hub.mjs +0 -49
- package/hub/team/claude-native-bridge.mjs +0 -987
- package/hub/team/cli/commands/start/start-in-process.mjs +0 -50
- package/hub/team/cli/commands/start/start-wt.mjs +0 -86
- package/hub/team/cli/services/hub-client.mjs +0 -264
- package/hub/team/cli/services/native-control.mjs +0 -145
- package/hub/team/codex-review.mjs +0 -520
- package/hub/team/conductor-mesh-bridge.mjs +0 -122
- package/hub/team/conductor-registry.mjs +0 -121
- package/hub/team/conductor.mjs +0 -1529
- package/hub/team/consensus-meta.mjs +0 -176
- package/hub/team/cto-auto-collect.mjs +0 -115
- package/hub/team/cto-pull.mjs +0 -31
- package/hub/team/daemon-pty-tmux-bridge.mjs +0 -146
- package/hub/team/dashboard.mjs +0 -322
- package/hub/team/event-log.mjs +0 -84
- package/hub/team/execution-mode.mjs +0 -324
- package/hub/team/extract-completion-payload.mjs +0 -110
- package/hub/team/headless-bridge-session.mjs +0 -17
- package/hub/team/health-probe.mjs +0 -435
- package/hub/team/interactive-native-launcher.mjs +0 -119
- package/hub/team/interactive-native-worker.mjs +0 -80
- package/hub/team/interactive-tui-transport.mjs +0 -479
- package/hub/team/launcher-template.mjs +0 -144
- package/hub/team/lead-control.mjs +0 -191
- package/hub/team/mcp-selector.mjs +0 -145
- package/hub/team/native-supervisor.mjs +0 -460
- package/hub/team/native.mjs +0 -727
- package/hub/team/recovery-store.mjs +0 -149
- package/hub/team/remote-probe.mjs +0 -292
- package/hub/team/remote-watcher.mjs +0 -506
- package/hub/team/runtime-strategy.mjs +0 -200
- package/hub/team/schemas/worker-completion.json +0 -39
- package/hub/team/sentinel-capture.mjs +0 -127
- package/hub/team/session-sync.mjs +0 -316
- package/hub/team/swarm-cli.mjs +0 -390
- package/hub/team/swarm-hypervisor.mjs +0 -2206
- package/hub/team/swarm-intent.mjs +0 -216
- package/hub/team/swarm-locks.mjs +0 -335
- package/hub/team/swarm-planner.mjs +0 -371
- package/hub/team/swarm-preflight.mjs +0 -366
- package/hub/team/swarm-reconciler.mjs +0 -242
- package/hub/team/synapse-cli.mjs +0 -458
- package/hub/team/synapse-http.mjs +0 -311
- package/hub/team/synapse-registry.mjs +0 -618
- package/hub/team/tui-remote-adapter.mjs +0 -377
- package/hub/team/tui-synapse.mjs +0 -223
- package/hub/team/worker-completion-validator.mjs +0 -79
- package/hub/team/worker-sandbox.mjs +0 -125
- package/hub/team/worktree-lifecycle.mjs +0 -639
- package/hub/team/wt-templates.json +0 -43
- package/hub/tools.mjs +0 -900
- package/hub/workers/antigravity-route-worker.mjs +0 -179
- package/hub/workers/codex-app-server-worker.mjs +0 -1067
- package/hub/workers/delegator-mcp.mjs +0 -1098
- package/hub/workers/lib/jsonrpc-stdio.mjs +0 -204
- package/scripts/lib/claudemd-scanner.mjs +0 -223
- package/scripts/lib/context.mjs +0 -67
- package/scripts/lib/dynamic-route-cli.mjs +0 -107
- package/scripts/lib/handoff.mjs +0 -187
- package/scripts/lib/logger.mjs +0 -122
- package/scripts/lib/stealth-fetch.mjs +0 -176
|
@@ -1,29 +1,14 @@
|
|
|
1
1
|
import { decomposeTask } from "../../../orchestrator.mjs";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
BOLD,
|
|
5
|
-
DIM,
|
|
6
|
-
GREEN,
|
|
7
|
-
RED,
|
|
8
|
-
RESET,
|
|
9
|
-
WHITE,
|
|
10
|
-
} from "../../../shared.mjs";
|
|
11
|
-
import { fail, ok, warn } from "../../render.mjs";
|
|
12
|
-
import {
|
|
13
|
-
getDefaultHubUrl,
|
|
14
|
-
getHubInfo,
|
|
15
|
-
startHubDaemon,
|
|
16
|
-
} from "../../services/hub-client.mjs";
|
|
2
|
+
import { AMBER, BOLD, DIM, RESET, WHITE } from "../../../shared.mjs";
|
|
3
|
+
import { fail } from "../../render.mjs";
|
|
17
4
|
import {
|
|
18
5
|
ensureTmuxOrExit,
|
|
19
|
-
|
|
6
|
+
normalizeTeammateMode,
|
|
20
7
|
} from "../../services/runtime-mode.mjs";
|
|
21
8
|
import { saveTeamState } from "../../services/state-store.mjs";
|
|
22
9
|
import { parseTeamArgs } from "./parse-args.mjs";
|
|
23
10
|
import { startHeadlessTeam } from "./start-headless.mjs";
|
|
24
|
-
import { startInProcessTeam } from "./start-in-process.mjs";
|
|
25
11
|
import { startMuxTeam } from "./start-mux.mjs";
|
|
26
|
-
import { startWtTeam } from "./start-wt.mjs";
|
|
27
12
|
|
|
28
13
|
function printStartUsage() {
|
|
29
14
|
console.log(`\n ${AMBER}${BOLD}⬡ tfx multi${RESET}\n`);
|
|
@@ -43,12 +28,6 @@ function printStartUsage() {
|
|
|
43
28
|
console.log(
|
|
44
29
|
` ${WHITE}tfx multi --dashboard-anchor window "작업"${RESET} ${DIM}(dashboard anchor: window|tab, 기본 window)${RESET}`,
|
|
45
30
|
);
|
|
46
|
-
console.log(
|
|
47
|
-
` ${WHITE}tfx multi --teammate-mode wt "작업"${RESET} ${DIM}(Windows Terminal split-pane)${RESET}`,
|
|
48
|
-
);
|
|
49
|
-
console.log(
|
|
50
|
-
` ${WHITE}tfx multi --teammate-mode in-process "작업"${RESET} ${DIM}(mux 불필요)${RESET}\n`,
|
|
51
|
-
);
|
|
52
31
|
}
|
|
53
32
|
|
|
54
33
|
function printWorkerPreview(agents, subtasks) {
|
|
@@ -62,14 +41,6 @@ function printWorkerPreview(agents, subtasks) {
|
|
|
62
41
|
console.log("");
|
|
63
42
|
}
|
|
64
43
|
|
|
65
|
-
function renderTmuxInstallHelp() {
|
|
66
|
-
console.log(`\n ${RED}${BOLD}tmux 미발견${RESET}\n`);
|
|
67
|
-
console.log(" 현재 선택한 모드는 tmux 기반 팀세션이 필요합니다.\n");
|
|
68
|
-
console.log(
|
|
69
|
-
` 설치:\n Windows: ${WHITE}winget install psmux${RESET}\n macOS: ${WHITE}brew install tmux${RESET}\n Linux: ${WHITE}apt install tmux${RESET}\n`,
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
44
|
export { parseTeamArgs };
|
|
74
45
|
|
|
75
46
|
export function resolveTeamWorkers({ agents, task, assigns = [] }) {
|
|
@@ -116,37 +87,15 @@ export async function teamStart(args = []) {
|
|
|
116
87
|
assigns,
|
|
117
88
|
});
|
|
118
89
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const
|
|
123
|
-
|
|
90
|
+
const effectiveMode = normalizeTeammateMode(teammateMode);
|
|
91
|
+
// headless 워커도 tmux 방에서 돈다. 방이 없으면 claude agents 행도 열 수 없다.
|
|
92
|
+
ensureTmuxOrExit();
|
|
93
|
+
const effectiveNativeBridge =
|
|
94
|
+
effectiveMode === "headless" && !nativeBridgeUiOptOut ? true : nativeBridge;
|
|
124
95
|
|
|
125
|
-
|
|
126
|
-
if (needsHub) {
|
|
127
|
-
hub = await getHubInfo();
|
|
128
|
-
if (!hub) {
|
|
129
|
-
process.stdout.write(" Hub 시작 중...");
|
|
130
|
-
try {
|
|
131
|
-
hub = await startHubDaemon();
|
|
132
|
-
} catch (error) {
|
|
133
|
-
if (error?.code === "HUB_SERVER_MISSING")
|
|
134
|
-
fail("hub/server.mjs 없음 — hub 모듈이 설치되지 않음");
|
|
135
|
-
}
|
|
136
|
-
console.log(` ${hub ? `${GREEN}✓${RESET}` : `${RED}✗${RESET}`}`);
|
|
137
|
-
if (!hub) warn("Hub 시작 실패 — 수동으로 실행: tfx hub start");
|
|
138
|
-
} else ok(`Hub: ${DIM}${hub.url}${RESET}`);
|
|
139
|
-
} else {
|
|
140
|
-
ok(`Hub: ${DIM}건너뜀 (단일 워커 headless)${RESET}`);
|
|
141
|
-
}
|
|
96
|
+
console.log(`\n ${AMBER}${BOLD}⬡ tfx multi${RESET}\n`);
|
|
142
97
|
|
|
143
98
|
const sessionId = `tfx-multi-${Date.now().toString(36).slice(-4)}${Math.random().toString(36).slice(2, 6)}`;
|
|
144
|
-
const hubUrl = hub?.url || getDefaultHubUrl();
|
|
145
|
-
const { mode: effectiveMode, warnings: modeWarnings } =
|
|
146
|
-
resolveEffectiveMode(teammateMode);
|
|
147
|
-
for (const message of modeWarnings) warn(message);
|
|
148
|
-
const effectiveNativeBridge =
|
|
149
|
-
effectiveMode === "headless" && !nativeBridgeUiOptOut ? true : nativeBridge;
|
|
150
99
|
|
|
151
100
|
console.log(` 세션: ${WHITE}${sessionId}${RESET}`);
|
|
152
101
|
console.log(` 모드: ${effectiveMode}`);
|
|
@@ -156,83 +105,43 @@ export async function teamStart(args = []) {
|
|
|
156
105
|
);
|
|
157
106
|
printWorkerPreview(agents, subtasks);
|
|
158
107
|
|
|
159
|
-
if (effectiveMode === "
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
108
|
+
if (effectiveMode === "headless") {
|
|
109
|
+
return startHeadlessTeam({
|
|
110
|
+
sessionId,
|
|
111
|
+
task,
|
|
112
|
+
lead,
|
|
113
|
+
agents,
|
|
114
|
+
subtasks,
|
|
115
|
+
layout,
|
|
116
|
+
assigns,
|
|
117
|
+
autoAttach,
|
|
118
|
+
progressive,
|
|
119
|
+
timeoutSec,
|
|
120
|
+
verbose,
|
|
121
|
+
dashboard,
|
|
122
|
+
dashboardLayout,
|
|
123
|
+
dashboardSize,
|
|
124
|
+
dashboardAnchor,
|
|
125
|
+
mcpProfile,
|
|
126
|
+
model,
|
|
127
|
+
cwd,
|
|
128
|
+
nativeBridge: effectiveNativeBridge,
|
|
129
|
+
nativeBridgeMode,
|
|
130
|
+
});
|
|
165
131
|
}
|
|
166
132
|
|
|
167
|
-
const state =
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
})
|
|
177
|
-
: effectiveMode === "headless"
|
|
178
|
-
? await startHeadlessTeam({
|
|
179
|
-
sessionId,
|
|
180
|
-
task,
|
|
181
|
-
lead,
|
|
182
|
-
agents,
|
|
183
|
-
subtasks,
|
|
184
|
-
layout,
|
|
185
|
-
assigns,
|
|
186
|
-
autoAttach,
|
|
187
|
-
progressive,
|
|
188
|
-
timeoutSec,
|
|
189
|
-
verbose,
|
|
190
|
-
dashboard,
|
|
191
|
-
dashboardLayout,
|
|
192
|
-
dashboardSize,
|
|
193
|
-
dashboardAnchor,
|
|
194
|
-
mcpProfile,
|
|
195
|
-
model,
|
|
196
|
-
cwd,
|
|
197
|
-
nativeBridge: effectiveNativeBridge,
|
|
198
|
-
nativeBridgeMode,
|
|
199
|
-
})
|
|
200
|
-
: effectiveMode === "wt"
|
|
201
|
-
? await startWtTeam({
|
|
202
|
-
sessionId,
|
|
203
|
-
task,
|
|
204
|
-
lead,
|
|
205
|
-
agents,
|
|
206
|
-
subtasks,
|
|
207
|
-
layout,
|
|
208
|
-
hubUrl,
|
|
209
|
-
})
|
|
210
|
-
: await startMuxTeam({
|
|
211
|
-
sessionId,
|
|
212
|
-
task,
|
|
213
|
-
lead,
|
|
214
|
-
agents,
|
|
215
|
-
subtasks,
|
|
216
|
-
layout,
|
|
217
|
-
hubUrl,
|
|
218
|
-
teammateMode: effectiveMode,
|
|
219
|
-
});
|
|
133
|
+
const state = await startMuxTeam({
|
|
134
|
+
sessionId,
|
|
135
|
+
task,
|
|
136
|
+
lead,
|
|
137
|
+
agents,
|
|
138
|
+
subtasks,
|
|
139
|
+
layout,
|
|
140
|
+
teammateMode: effectiveMode,
|
|
141
|
+
});
|
|
220
142
|
|
|
221
|
-
if (!state) return fail("
|
|
143
|
+
if (!state) return fail("팀 세션 시작 실패");
|
|
222
144
|
state.sessionId = sessionId;
|
|
223
145
|
saveTeamState(state, sessionId);
|
|
224
146
|
if (typeof state.postSave === "function") state.postSave();
|
|
225
|
-
if (effectiveMode === "in-process") {
|
|
226
|
-
ok("네이티브 in-process 팀 시작 완료");
|
|
227
|
-
console.log(` ${DIM}tmux 없이 실행됨 (직접 CLI 프로세스)${RESET}`);
|
|
228
|
-
console.log(` ${DIM}제어: tfx multi send/control/tasks/status${RESET}\n`);
|
|
229
|
-
} else if (effectiveMode === "wt") {
|
|
230
|
-
ok("Windows Terminal wt 팀 시작 완료");
|
|
231
|
-
console.log(
|
|
232
|
-
` ${DIM}현재 pane 기준으로 ${state.layout} 분할 생성됨${RESET}`,
|
|
233
|
-
);
|
|
234
|
-
console.log(
|
|
235
|
-
` ${DIM}wt 모드는 자동 프롬프트 주입/Hub direct 제어(send/control)가 제한됩니다.${RESET}\n`,
|
|
236
|
-
);
|
|
237
|
-
}
|
|
238
147
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
1
2
|
import { resolve } from "node:path";
|
|
2
3
|
import { parseDashboardAnchor } from "../../../dashboard-anchor.mjs";
|
|
3
4
|
import { parseDashboardLayout } from "../../../dashboard-layout.mjs";
|
|
@@ -6,35 +7,7 @@ import {
|
|
|
6
7
|
normalizeTeammateMode,
|
|
7
8
|
} from "../../services/runtime-mode.mjs";
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
const KNOWN_ROLES = new Set([
|
|
11
|
-
"codex",
|
|
12
|
-
"antigravity",
|
|
13
|
-
"agy",
|
|
14
|
-
"gemini",
|
|
15
|
-
"claude",
|
|
16
|
-
"executor",
|
|
17
|
-
"architect",
|
|
18
|
-
"planner",
|
|
19
|
-
"analyst",
|
|
20
|
-
"critic",
|
|
21
|
-
"debugger",
|
|
22
|
-
"verifier",
|
|
23
|
-
"code-reviewer",
|
|
24
|
-
"security-reviewer",
|
|
25
|
-
"test-engineer",
|
|
26
|
-
"designer",
|
|
27
|
-
"writer",
|
|
28
|
-
"researcher",
|
|
29
|
-
"scientist",
|
|
30
|
-
]);
|
|
31
|
-
|
|
32
|
-
const NATIVE_BRIDGE_MODES = new Set([
|
|
33
|
-
"roster",
|
|
34
|
-
"agents",
|
|
35
|
-
"interactive-attach",
|
|
36
|
-
"claude-wrapper",
|
|
37
|
-
]);
|
|
10
|
+
const AGENT_TO_CLI = createRequire(import.meta.url)("../../../agent-map.json");
|
|
38
11
|
|
|
39
12
|
/**
|
|
40
13
|
* --assign "cli:prompt:role" 형식을 콜론-안전하게 파싱한다.
|
|
@@ -42,7 +15,7 @@ const NATIVE_BRIDGE_MODES = new Set([
|
|
|
42
15
|
*
|
|
43
16
|
* 규칙:
|
|
44
17
|
* 1. 첫 번째 콜론 앞 = CLI 이름
|
|
45
|
-
* 2. 마지막 콜론 뒤가
|
|
18
|
+
* 2. 마지막 콜론 뒤가 agent-map.json에 있으면 role, 나머지가 prompt
|
|
46
19
|
* 3. 그 외에는 첫 콜론 뒤 전체가 prompt, role은 빈 문자열
|
|
47
20
|
*/
|
|
48
21
|
function parseAssignValue(raw) {
|
|
@@ -58,7 +31,7 @@ function parseAssignValue(raw) {
|
|
|
58
31
|
.slice(lastColon + 1)
|
|
59
32
|
.trim()
|
|
60
33
|
.toLowerCase();
|
|
61
|
-
if (
|
|
34
|
+
if (Object.hasOwn(AGENT_TO_CLI, candidate)) {
|
|
62
35
|
return { cli, prompt: rest.slice(0, lastColon).trim(), role: candidate };
|
|
63
36
|
}
|
|
64
37
|
}
|
|
@@ -72,7 +45,7 @@ export function parseTeamArgs(args = []) {
|
|
|
72
45
|
let layout = "2x2";
|
|
73
46
|
let teammateMode = "auto";
|
|
74
47
|
const taskParts = [];
|
|
75
|
-
const assigns = [];
|
|
48
|
+
const assigns = [];
|
|
76
49
|
let autoAttach = true;
|
|
77
50
|
let progressive = true;
|
|
78
51
|
let timeoutSec = 0;
|
|
@@ -109,14 +82,10 @@ export function parseTeamArgs(args = []) {
|
|
|
109
82
|
} else if (current === "--assign" && args[index + 1]) {
|
|
110
83
|
const parsed = parseAssignValue(args[++index]);
|
|
111
84
|
if (parsed) assigns.push(parsed);
|
|
112
|
-
} else if (current === "--auto-attach") {
|
|
113
|
-
autoAttach = true;
|
|
114
85
|
} else if (current === "--no-auto-attach") {
|
|
115
86
|
autoAttach = false;
|
|
116
87
|
} else if (current === "--verbose") {
|
|
117
88
|
verbose = true;
|
|
118
|
-
} else if (current === "--dashboard") {
|
|
119
|
-
dashboard = true;
|
|
120
89
|
} else if (current === "--no-dashboard") {
|
|
121
90
|
dashboard = false;
|
|
122
91
|
} else if (current === "--dashboard-layout" && args[index + 1]) {
|
|
@@ -159,9 +128,9 @@ export function parseTeamArgs(args = []) {
|
|
|
159
128
|
nativeBridgeUiOptOut = true;
|
|
160
129
|
} else if (current === "--native-bridge-mode") {
|
|
161
130
|
const mode = args[++index];
|
|
162
|
-
if (
|
|
131
|
+
if (mode !== "agents") {
|
|
163
132
|
throw new Error(
|
|
164
|
-
`unknown native bridge mode: ${mode || ""}; expected
|
|
133
|
+
`unknown native bridge mode: ${mode || ""}; expected agents`,
|
|
165
134
|
);
|
|
166
135
|
}
|
|
167
136
|
nativeBridge = true;
|
|
@@ -174,6 +143,8 @@ export function parseTeamArgs(args = []) {
|
|
|
174
143
|
p = p[1].toUpperCase() + ":" + p.slice(2);
|
|
175
144
|
}
|
|
176
145
|
cwd = resolve(p);
|
|
146
|
+
} else if (current === "--auto-attach" || current === "--dashboard") {
|
|
147
|
+
// 이전 설치 스킬이 넘기는 기본값 플래그는 무시한다.
|
|
177
148
|
} else if (current.startsWith("-")) {
|
|
178
149
|
console.warn(` ⚠ 미인식 플래그 무시: ${current}`);
|
|
179
150
|
} else {
|
|
@@ -181,7 +152,11 @@ export function parseTeamArgs(args = []) {
|
|
|
181
152
|
}
|
|
182
153
|
}
|
|
183
154
|
|
|
184
|
-
const
|
|
155
|
+
const task = taskParts.join(" ").trim();
|
|
156
|
+
const normalizedTeammateMode =
|
|
157
|
+
task || assigns.length > 0
|
|
158
|
+
? normalizeTeammateMode(teammateMode)
|
|
159
|
+
: teammateMode;
|
|
185
160
|
if (
|
|
186
161
|
!nativeBridgeExplicit &&
|
|
187
162
|
!nativeBridgeUiOptOut &&
|
|
@@ -195,7 +170,7 @@ export function parseTeamArgs(args = []) {
|
|
|
195
170
|
lead,
|
|
196
171
|
layout: normalizeLayout(layout),
|
|
197
172
|
teammateMode: normalizedTeammateMode,
|
|
198
|
-
task
|
|
173
|
+
task,
|
|
199
174
|
assigns,
|
|
200
175
|
autoAttach,
|
|
201
176
|
progressive,
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
} from "../../../headless.mjs";
|
|
7
7
|
import { AMBER, DIM, GREEN, RESET } from "../../../shared.mjs";
|
|
8
8
|
import { ok } from "../../render.mjs";
|
|
9
|
-
import { clearTeamState } from "../../services/state-store.mjs";
|
|
9
|
+
import { clearTeamState, saveTeamState } from "../../services/state-store.mjs";
|
|
10
10
|
import { buildTasks } from "../../services/task-model.mjs";
|
|
11
11
|
|
|
12
12
|
export function buildHeadlessAssignments({
|
|
@@ -66,6 +66,7 @@ export async function startHeadlessTeam({
|
|
|
66
66
|
cwd,
|
|
67
67
|
nativeBridge,
|
|
68
68
|
nativeBridgeMode,
|
|
69
|
+
_deps = {},
|
|
69
70
|
}) {
|
|
70
71
|
const assignments = buildHeadlessAssignments({
|
|
71
72
|
agents,
|
|
@@ -76,30 +77,59 @@ export async function startHeadlessTeam({
|
|
|
76
77
|
cwd,
|
|
77
78
|
});
|
|
78
79
|
|
|
79
|
-
const
|
|
80
|
+
const state = {
|
|
81
|
+
sessionId,
|
|
82
|
+
sessionName: sessionId,
|
|
83
|
+
task,
|
|
84
|
+
lead,
|
|
85
|
+
agents: assignments.map((assignment) => assignment.cli),
|
|
86
|
+
layout,
|
|
87
|
+
teammateMode: "headless",
|
|
88
|
+
ownerPid: process.pid,
|
|
89
|
+
startedAt: Date.now(),
|
|
90
|
+
members: assignments.map((assignment, index) => ({
|
|
91
|
+
role: "worker",
|
|
92
|
+
name: `worker-${index + 1}`,
|
|
93
|
+
cli: assignment.cli,
|
|
94
|
+
pane: "",
|
|
95
|
+
subtask: assignment.prompt,
|
|
96
|
+
})),
|
|
97
|
+
};
|
|
98
|
+
state.tasks = buildTasks(
|
|
99
|
+
assignments.map((assignment) => assignment.prompt),
|
|
100
|
+
state.members,
|
|
101
|
+
);
|
|
102
|
+
saveTeamState(state, sessionId);
|
|
80
103
|
ok(`headless ${assignments.length}워커 시작`);
|
|
81
104
|
if (nativeBridge) {
|
|
82
105
|
process.stderr.write(
|
|
83
|
-
`[headless] Claude native bridge mode: ${nativeBridgeMode || "
|
|
106
|
+
`[headless] Claude native bridge mode: ${nativeBridgeMode || "agents"}\n`,
|
|
84
107
|
);
|
|
85
108
|
}
|
|
86
109
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
110
|
+
let handle;
|
|
111
|
+
try {
|
|
112
|
+
const run = _deps.runHeadlessInteractive || runHeadlessInteractive;
|
|
113
|
+
handle = await run(sessionId, assignments, {
|
|
114
|
+
timeoutSec: timeoutSec || 900,
|
|
115
|
+
layout,
|
|
116
|
+
autoAttach: !!autoAttach,
|
|
117
|
+
leadPane: process.env.TMUX_PANE,
|
|
118
|
+
leadTmux: process.env.TMUX,
|
|
119
|
+
dashboard: !!dashboard,
|
|
120
|
+
dashboardLayout,
|
|
121
|
+
dashboardSize: dashboardSize ?? 0.5,
|
|
122
|
+
dashboardAnchor,
|
|
123
|
+
progressive: progressive !== false,
|
|
124
|
+
nativeBridge,
|
|
125
|
+
nativeBridgeMode,
|
|
126
|
+
progressIntervalSec: verbose ? 10 : 0,
|
|
127
|
+
onProgress(event) {
|
|
128
|
+
if (event.type === "session_created") {
|
|
129
|
+
state.sessionName = event.sessionName;
|
|
130
|
+
saveTeamState(state, sessionId);
|
|
131
|
+
}
|
|
132
|
+
if (verbose) {
|
|
103
133
|
if (event.type === "session_created") {
|
|
104
134
|
console.log(` ${DIM}세션: ${event.sessionName}${RESET}`);
|
|
105
135
|
} else if (event.type === "worker_added") {
|
|
@@ -128,104 +158,107 @@ export async function startHeadlessTeam({
|
|
|
128
158
|
);
|
|
129
159
|
}
|
|
130
160
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
// 최소 결과 요약
|
|
135
|
-
const results = handle.results;
|
|
136
|
-
const succeeded = results.filter((r) => r.matched && r.exitCode === 0);
|
|
137
|
-
const failed = results.filter((r) => !r.matched || r.exitCode !== 0);
|
|
161
|
+
},
|
|
162
|
+
});
|
|
138
163
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
console.log(
|
|
144
|
-
`=== HEADLESS_COMPLETE succeeded=${succeeded.length} failed=${failed.length} total=${results.length} ===`,
|
|
145
|
-
);
|
|
164
|
+
// 최소 결과 요약
|
|
165
|
+
const results = handle.results;
|
|
166
|
+
const succeeded = results.filter((r) => r.matched && r.exitCode === 0);
|
|
167
|
+
const failed = results.filter((r) => !r.matched || r.exitCode !== 0);
|
|
146
168
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
169
|
+
ok(
|
|
170
|
+
`헤드리스 완료: ${succeeded.length}성공 / ${failed.length}실패 / ${results.length}전체`,
|
|
171
|
+
);
|
|
172
|
+
// background task가 이 완료 마커로 결과 회수 시점을 판단한다.
|
|
173
|
+
console.log(
|
|
174
|
+
`=== HEADLESS_COMPLETE succeeded=${succeeded.length} failed=${failed.length} total=${results.length} ===`,
|
|
175
|
+
);
|
|
153
176
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
const tag = r.handoffFallback ? `${DIM}(fallback)${RESET}` : "";
|
|
160
|
-
console.log(` ${icon} ${r.paneName} ${tag}`);
|
|
161
|
-
for (const line of r.handoffFormatted.split("\n")) {
|
|
162
|
-
console.log(` ${DIM}${line}${RESET}`);
|
|
163
|
-
}
|
|
164
|
-
} else {
|
|
165
|
-
if (r.resultFile) console.log(` ${icon} ${r.paneName}: ${r.resultFile}`);
|
|
177
|
+
if (failed.length > 0) {
|
|
178
|
+
for (const r of failed)
|
|
179
|
+
console.log(
|
|
180
|
+
` ${AMBER}✗${RESET} ${r.paneName} (${r.cli}) exit=${r.exitCode}`,
|
|
181
|
+
);
|
|
166
182
|
}
|
|
167
|
-
}
|
|
168
183
|
|
|
169
|
-
|
|
170
|
-
if (verbose) {
|
|
184
|
+
// handoff 요약 (Lead 토큰 절약 포맷)
|
|
171
185
|
for (const r of results) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
);
|
|
178
|
-
console.log(` ${
|
|
186
|
+
const icon =
|
|
187
|
+
r.matched && r.exitCode === 0
|
|
188
|
+
? `${GREEN}✓${RESET}`
|
|
189
|
+
: `${AMBER}✗${RESET}`;
|
|
190
|
+
if (r.handoffFormatted) {
|
|
191
|
+
const tag = r.handoffFallback ? `${DIM}(fallback)${RESET}` : "";
|
|
192
|
+
console.log(` ${icon} ${r.paneName} ${tag}`);
|
|
193
|
+
for (const line of r.handoffFormatted.split("\n")) {
|
|
194
|
+
console.log(` ${DIM}${line}${RESET}`);
|
|
195
|
+
}
|
|
196
|
+
} else {
|
|
197
|
+
if (r.resultFile)
|
|
198
|
+
console.log(` ${icon} ${r.paneName}: ${r.resultFile}`);
|
|
179
199
|
}
|
|
180
200
|
}
|
|
181
|
-
}
|
|
182
201
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
202
|
+
// --verbose: 기존 장황한 출력 (200자 preview)
|
|
203
|
+
if (verbose) {
|
|
204
|
+
for (const r of results) {
|
|
205
|
+
if (r.output) {
|
|
206
|
+
const preview =
|
|
207
|
+
r.output.length > 200 ? `${r.output.slice(0, 200)}…` : r.output;
|
|
208
|
+
console.log(
|
|
209
|
+
`\n ${DIM}── ${r.paneName} (${r.cli}${r.role ? `, ${r.role}` : ""}) ──${RESET}`,
|
|
210
|
+
);
|
|
211
|
+
console.log(` ${preview}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
186
215
|
|
|
187
|
-
|
|
188
|
-
|
|
216
|
+
// dashboard 모드: tui-viewer가 최종 상태를 렌더링할 시간 확보
|
|
217
|
+
// WT pane spawn (~1s) + node 기동 (~500ms) + 첫 폴링 (~500ms) + 렌더 여유
|
|
218
|
+
if (dashboard) await new Promise((r) => setTimeout(r, 5000));
|
|
189
219
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
220
|
+
const members = [
|
|
221
|
+
{
|
|
222
|
+
role: "lead",
|
|
223
|
+
name: "lead",
|
|
224
|
+
cli: lead,
|
|
225
|
+
pane: `${handle.sessionName}:0.0`,
|
|
226
|
+
},
|
|
227
|
+
...results.map((r, i) => ({
|
|
228
|
+
role: "worker",
|
|
229
|
+
name: r.paneName,
|
|
230
|
+
cli: r.cli,
|
|
231
|
+
pane: r.paneId || "",
|
|
232
|
+
subtask: assignments[i]?.prompt,
|
|
233
|
+
})),
|
|
234
|
+
];
|
|
205
235
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
236
|
+
return {
|
|
237
|
+
...state,
|
|
238
|
+
sessionName: handle.sessionName,
|
|
239
|
+
task,
|
|
240
|
+
lead,
|
|
241
|
+
agents: assignments.map((a) => a.cli),
|
|
242
|
+
layout,
|
|
243
|
+
teammateMode: "headless",
|
|
244
|
+
members,
|
|
245
|
+
headlessResults: results,
|
|
246
|
+
handoffs: results.map((r) => ({
|
|
247
|
+
paneName: r.paneName,
|
|
248
|
+
cli: r.cli,
|
|
249
|
+
...r.handoff,
|
|
250
|
+
})),
|
|
251
|
+
tasks: buildTasks(
|
|
252
|
+
assignments.map((a) => a.prompt),
|
|
253
|
+
members.filter((m) => m.role === "worker"),
|
|
254
|
+
),
|
|
255
|
+
};
|
|
256
|
+
} finally {
|
|
257
|
+
try {
|
|
258
|
+
handle?.kill();
|
|
259
|
+
} finally {
|
|
227
260
|
clearTeamState(sessionId);
|
|
228
261
|
console.log(`\n ${DIM}세션 정리 완료.${RESET}\n`);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
231
264
|
}
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
import { BOLD, DIM, GREEN, RESET } from "../../../shared.mjs";
|
|
11
11
|
import { ok, warn } from "../../render.mjs";
|
|
12
12
|
import { toAgentId } from "../../services/member-selector.mjs";
|
|
13
|
-
import { PKG_ROOT
|
|
13
|
+
import { PKG_ROOT } from "../../services/state-store.mjs";
|
|
14
14
|
import { buildTasks } from "../../services/task-model.mjs";
|
|
15
15
|
|
|
16
16
|
export async function startMuxTeam({
|
|
@@ -20,7 +20,6 @@ export async function startMuxTeam({
|
|
|
20
20
|
agents,
|
|
21
21
|
subtasks,
|
|
22
22
|
layout,
|
|
23
|
-
hubUrl,
|
|
24
23
|
teammateMode,
|
|
25
24
|
}) {
|
|
26
25
|
const paneCount = agents.length + 1;
|
|
@@ -64,7 +63,6 @@ export async function startMuxTeam({
|
|
|
64
63
|
ok("CLI 초기화 대기 (3초)...");
|
|
65
64
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
66
65
|
const promptInjectionFailures = await orchestrate(sessionId, assignments, {
|
|
67
|
-
hubUrl,
|
|
68
66
|
teammateMode,
|
|
69
67
|
lead: { target: leadTarget, cli: lead, task },
|
|
70
68
|
onInjectionFailure(failure) {
|
|
@@ -89,7 +87,6 @@ export async function startMuxTeam({
|
|
|
89
87
|
layout: effectiveLayout,
|
|
90
88
|
teammateMode,
|
|
91
89
|
startedAt: Date.now(),
|
|
92
|
-
hubUrl,
|
|
93
90
|
promptInjectionFailures,
|
|
94
91
|
members,
|
|
95
92
|
panes: Object.fromEntries(
|
|
@@ -109,9 +106,7 @@ export async function startMuxTeam({
|
|
|
109
106
|
members.filter((member) => member.role === "worker"),
|
|
110
107
|
),
|
|
111
108
|
postSave() {
|
|
112
|
-
const
|
|
113
|
-
TEAM_PROFILE === "team" ? "" : `TFX_TEAM_PROFILE=${TEAM_PROFILE} `;
|
|
114
|
-
const taskListCommand = `${profilePrefix}${process.execPath} ${join(PKG_ROOT, "bin", "triflux.mjs")} team tasks`;
|
|
109
|
+
const taskListCommand = `${process.execPath} ${join(PKG_ROOT, "bin", "triflux.mjs")} team tasks`;
|
|
115
110
|
configureTeammateKeybindings(sessionId, {
|
|
116
111
|
inProcess: false,
|
|
117
112
|
taskListCommand,
|