triflux 6.0.5 → 6.0.6
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.
|
@@ -38,7 +38,9 @@ function renderTmuxInstallHelp() {
|
|
|
38
38
|
export { parseTeamArgs };
|
|
39
39
|
|
|
40
40
|
export async function teamStart(args = []) {
|
|
41
|
-
const { agents, lead, layout, teammateMode, task } = parseTeamArgs(args);
|
|
41
|
+
const { agents, lead, layout, teammateMode, task: rawTask, assigns, autoAttach, progressive, timeoutSec } = parseTeamArgs(args);
|
|
42
|
+
// --assign 사용 시 task를 자동 생성
|
|
43
|
+
const task = rawTask || (assigns.length > 0 ? assigns.map(a => a.prompt).join(" + ") : "");
|
|
42
44
|
if (!task) return printStartUsage();
|
|
43
45
|
|
|
44
46
|
console.log(`\n ${AMBER}${BOLD}⬡ tfx multi${RESET}\n`);
|
|
@@ -70,7 +72,7 @@ export async function teamStart(args = []) {
|
|
|
70
72
|
const state = effectiveMode === "in-process"
|
|
71
73
|
? await startInProcessTeam({ sessionId, task, lead, agents, subtasks, hubUrl })
|
|
72
74
|
: effectiveMode === "headless"
|
|
73
|
-
? await startHeadlessTeam({ sessionId, task, lead, agents, subtasks, layout })
|
|
75
|
+
? await startHeadlessTeam({ sessionId, task, lead, agents, subtasks, layout, assigns, autoAttach, progressive, timeoutSec })
|
|
74
76
|
: effectiveMode === "wt"
|
|
75
77
|
? await startWtTeam({ sessionId, task, lead, agents, subtasks, layout, hubUrl })
|
|
76
78
|
: await startMuxTeam({ sessionId, task, lead, agents, subtasks, layout, hubUrl, teammateMode: effectiveMode });
|
|
@@ -1,32 +1,52 @@
|
|
|
1
|
-
import { normalizeLayout, normalizeTeammateMode } from "../../services/runtime-mode.mjs";
|
|
2
|
-
|
|
3
|
-
export function parseTeamArgs(args = []) {
|
|
4
|
-
let agents = ["codex", "gemini"];
|
|
5
|
-
let lead = "claude";
|
|
6
|
-
let layout = "2x2";
|
|
7
|
-
let teammateMode = "auto";
|
|
8
|
-
const taskParts = [];
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
} else if (
|
|
19
|
-
|
|
20
|
-
} else if (
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
1
|
+
import { normalizeLayout, normalizeTeammateMode } from "../../services/runtime-mode.mjs";
|
|
2
|
+
|
|
3
|
+
export function parseTeamArgs(args = []) {
|
|
4
|
+
let agents = ["codex", "gemini"];
|
|
5
|
+
let lead = "claude";
|
|
6
|
+
let layout = "2x2";
|
|
7
|
+
let teammateMode = "auto";
|
|
8
|
+
const taskParts = [];
|
|
9
|
+
const assigns = []; // --assign "codex:프롬프트:역할" 형식
|
|
10
|
+
let autoAttach = false;
|
|
11
|
+
let progressive = true;
|
|
12
|
+
let timeoutSec = 300;
|
|
13
|
+
|
|
14
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
15
|
+
const current = args[index];
|
|
16
|
+
if (current === "--agents" && args[index + 1]) {
|
|
17
|
+
agents = args[++index].split(",").map((value) => value.trim().toLowerCase()).filter(Boolean);
|
|
18
|
+
} else if (current === "--lead" && args[index + 1]) {
|
|
19
|
+
lead = args[++index].trim().toLowerCase();
|
|
20
|
+
} else if (current === "--layout" && args[index + 1]) {
|
|
21
|
+
layout = args[++index];
|
|
22
|
+
} else if ((current === "--teammate-mode" || current === "--mode") && args[index + 1]) {
|
|
23
|
+
teammateMode = args[++index];
|
|
24
|
+
} else if (current === "--assign" && args[index + 1]) {
|
|
25
|
+
// "cli:prompt:role" 형식 파싱
|
|
26
|
+
const parts = args[++index].split(":");
|
|
27
|
+
if (parts.length >= 2) {
|
|
28
|
+
assigns.push({ cli: parts[0].trim(), prompt: parts.slice(1, -1).join(":").trim() || parts[1].trim(), role: parts[parts.length - 1]?.trim() || "" });
|
|
29
|
+
}
|
|
30
|
+
} else if (current === "--auto-attach") {
|
|
31
|
+
autoAttach = true;
|
|
32
|
+
} else if (current === "--no-progressive") {
|
|
33
|
+
progressive = false;
|
|
34
|
+
} else if (current === "--timeout" && args[index + 1]) {
|
|
35
|
+
timeoutSec = Number(args[++index]) || 300;
|
|
36
|
+
} else if (!current.startsWith("-")) {
|
|
37
|
+
taskParts.push(current);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
agents,
|
|
43
|
+
lead,
|
|
44
|
+
layout: normalizeLayout(layout),
|
|
45
|
+
teammateMode: normalizeTeammateMode(teammateMode),
|
|
46
|
+
task: taskParts.join(" ").trim(),
|
|
47
|
+
assigns,
|
|
48
|
+
autoAttach,
|
|
49
|
+
progressive,
|
|
50
|
+
timeoutSec,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -1,33 +1,43 @@
|
|
|
1
1
|
import { BOLD, DIM, GREEN, RESET, AMBER } from "../../../shared.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { killPsmuxSession } from "../../../psmux.mjs";
|
|
2
|
+
import { runHeadlessInteractive } from "../../../headless.mjs";
|
|
4
3
|
import { ok, warn } from "../../render.mjs";
|
|
5
4
|
import { buildTasks } from "../../services/task-model.mjs";
|
|
6
5
|
|
|
7
|
-
export async function startHeadlessTeam({ sessionId, task, lead, agents, subtasks, layout }) {
|
|
8
|
-
console.log(` ${AMBER}모드: headless (
|
|
6
|
+
export async function startHeadlessTeam({ sessionId, task, lead, agents, subtasks, layout, assigns, autoAttach, progressive, timeoutSec }) {
|
|
7
|
+
console.log(` ${AMBER}모드: headless (Lead-Direct v6.0.0)${RESET}`);
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
prompt:
|
|
13
|
-
role: `worker-${i + 1}
|
|
14
|
-
}));
|
|
9
|
+
// --assign이 있으면 그것을 사용, 없으면 agents+subtasks 조합
|
|
10
|
+
const assignments = assigns && assigns.length > 0
|
|
11
|
+
? assigns.map((a, i) => ({ cli: a.cli, prompt: a.prompt, role: a.role || `worker-${i + 1}` }))
|
|
12
|
+
: subtasks.map((subtask, i) => ({ cli: agents[i] || agents[0], prompt: subtask, role: `worker-${i + 1}` }));
|
|
15
13
|
|
|
16
|
-
ok(
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
ok(`헤드리스 실행 시작 (${assignments.length}워커, progressive=${progressive !== false})`);
|
|
15
|
+
|
|
16
|
+
const handle = await runHeadlessInteractive(sessionId, assignments, {
|
|
17
|
+
timeoutSec: timeoutSec || 300,
|
|
19
18
|
layout,
|
|
19
|
+
autoAttach: autoAttach !== false, // 기본 true
|
|
20
|
+
progressive: progressive !== false, // 기본 true
|
|
21
|
+
progressIntervalSec: 10,
|
|
20
22
|
onProgress(event) {
|
|
21
|
-
if (event.type === "
|
|
23
|
+
if (event.type === "session_created") {
|
|
24
|
+
console.log(` ${DIM}세션: ${event.sessionName}${RESET}`);
|
|
25
|
+
} else if (event.type === "worker_added") {
|
|
26
|
+
console.log(` ${DIM}[+] ${event.paneTitle}${RESET}`);
|
|
27
|
+
} else if (event.type === "dispatched") {
|
|
22
28
|
console.log(` ${DIM}[${event.paneName}] ${event.cli} dispatch${RESET}`);
|
|
29
|
+
} else if (event.type === "progress") {
|
|
30
|
+
const last = (event.snapshot || "").split("\n").filter(l => l.trim()).pop() || "";
|
|
31
|
+
if (last) console.log(` ${DIM}[${event.paneName}] ${last.slice(0, 60)}${RESET}`);
|
|
23
32
|
} else if (event.type === "completed") {
|
|
24
33
|
const icon = event.matched && event.exitCode === 0 ? `${GREEN}✓${RESET}` : `${AMBER}✗${RESET}`;
|
|
25
|
-
console.log(` ${icon} [${event.paneName}] ${event.cli} exit=${event.exitCode}${event.sessionDead ? " (
|
|
34
|
+
console.log(` ${icon} [${event.paneName}] ${event.cli} exit=${event.exitCode}${event.sessionDead ? " (dead)" : ""}`);
|
|
26
35
|
}
|
|
27
36
|
},
|
|
28
37
|
});
|
|
29
38
|
|
|
30
39
|
// 결과 요약
|
|
40
|
+
const results = handle.results;
|
|
31
41
|
const succeeded = results.filter((r) => r.matched && r.exitCode === 0);
|
|
32
42
|
const failed = results.filter((r) => !r.matched || r.exitCode !== 0);
|
|
33
43
|
|
|
@@ -36,41 +46,39 @@ export async function startHeadlessTeam({ sessionId, task, lead, agents, subtask
|
|
|
36
46
|
|
|
37
47
|
if (failed.length > 0) {
|
|
38
48
|
warn("실패 워커:");
|
|
39
|
-
for (const r of failed) {
|
|
40
|
-
console.log(` ${r.paneName} (${r.cli}): exit=${r.exitCode}${r.sessionDead ? " session dead" : ""}`);
|
|
41
|
-
}
|
|
49
|
+
for (const r of failed) console.log(` ${r.paneName} (${r.cli}): exit=${r.exitCode}`);
|
|
42
50
|
}
|
|
43
51
|
|
|
44
|
-
// 결과 출력
|
|
52
|
+
// 결과 출력 + JSON stdout
|
|
45
53
|
for (const r of results) {
|
|
46
54
|
if (r.output) {
|
|
47
55
|
const preview = r.output.length > 200 ? `${r.output.slice(0, 200)}…` : r.output;
|
|
48
|
-
console.log(`\n ${DIM}── ${r.paneName} (${r.cli}) ──${RESET}`);
|
|
56
|
+
console.log(`\n ${DIM}── ${r.paneName} (${r.cli}${r.role ? `, ${r.role}` : ""}) ──${RESET}`);
|
|
49
57
|
console.log(` ${preview}`);
|
|
50
58
|
}
|
|
51
59
|
}
|
|
52
60
|
|
|
53
61
|
// 세션 정리
|
|
54
|
-
|
|
62
|
+
handle.kill();
|
|
55
63
|
|
|
56
64
|
const members = [
|
|
57
|
-
{ role: "lead", name: "lead", cli: lead, pane: `${sessionName}:0.0` },
|
|
58
|
-
...results.map((r, i) => ({ role: "worker", name: r.paneName, cli: r.cli, pane:
|
|
65
|
+
{ role: "lead", name: "lead", cli: lead, pane: `${handle.sessionName}:0.0` },
|
|
66
|
+
...results.map((r, i) => ({ role: "worker", name: r.paneName, cli: r.cli, pane: r.paneId || "", subtask: assignments[i]?.prompt })),
|
|
59
67
|
];
|
|
60
68
|
|
|
61
69
|
return {
|
|
62
|
-
sessionName,
|
|
70
|
+
sessionName: handle.sessionName,
|
|
63
71
|
task,
|
|
64
72
|
lead,
|
|
65
|
-
agents,
|
|
73
|
+
agents: assignments.map(a => a.cli),
|
|
66
74
|
layout,
|
|
67
75
|
teammateMode: "headless",
|
|
68
76
|
startedAt: Date.now(),
|
|
69
77
|
members,
|
|
70
78
|
headlessResults: results,
|
|
71
|
-
tasks: buildTasks(
|
|
79
|
+
tasks: buildTasks(assignments.map(a => a.prompt), members.filter((m) => m.role === "worker")),
|
|
72
80
|
postSave() {
|
|
73
|
-
console.log(`\n ${DIM}세션
|
|
81
|
+
console.log(`\n ${DIM}세션 정리 완료.${RESET}\n`);
|
|
74
82
|
},
|
|
75
83
|
};
|
|
76
84
|
}
|
package/hub/team/headless.mjs
CHANGED
|
@@ -277,12 +277,14 @@ export function autoAttachTerminal(sessionName, opts = {}) {
|
|
|
277
277
|
}
|
|
278
278
|
|
|
279
279
|
// PowerShell 래핑 — wt가 psmux를 파일로 인식하는 문제 방지
|
|
280
|
+
// "--" 구분자 필수: -NoExit 등이 wt 옵션으로 해석되는 것 방지
|
|
280
281
|
// pwsh.exe (PS7) 우선, 없으면 powershell.exe (PS5.1) fallback
|
|
281
282
|
const shells = ["pwsh.exe", "powershell.exe"];
|
|
282
283
|
for (const shell of shells) {
|
|
283
284
|
try {
|
|
284
285
|
execFileSync("wt.exe", [
|
|
285
|
-
"nt",
|
|
286
|
+
"nt", "--title", "triflux", "--",
|
|
287
|
+
shell, "-NoExit", "-Command",
|
|
286
288
|
`psmux attach -t ${sessionName}`,
|
|
287
289
|
], { stdio: "ignore" });
|
|
288
290
|
return true;
|