triflux 7.3.1 → 7.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/plugin.json +1 -1
- package/hub/team/backend.mjs +2 -1
- package/hub/team/cli/commands/start/index.mjs +2 -2
- package/hub/team/cli/commands/start/parse-args.mjs +41 -5
- package/hub/team/cli/commands/start/start-headless.mjs +3 -3
- package/hub/team/headless.mjs +4 -4
- package/package.json +1 -1
package/hub/team/backend.mjs
CHANGED
|
@@ -18,7 +18,8 @@ export class CodexBackend {
|
|
|
18
18
|
* @returns {string} PowerShell 명령 (cls 제외)
|
|
19
19
|
*/
|
|
20
20
|
buildArgs(prompt, resultFile, opts = {}) {
|
|
21
|
-
|
|
21
|
+
const modelFlag = opts.model ? ` --model '${opts.model}'` : "";
|
|
22
|
+
return `codex exec ${prompt} -o '${resultFile}' --color never${modelFlag}`;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
env() { return {}; }
|
|
@@ -38,7 +38,7 @@ function renderTmuxInstallHelp() {
|
|
|
38
38
|
export { parseTeamArgs };
|
|
39
39
|
|
|
40
40
|
export async function teamStart(args = []) {
|
|
41
|
-
const { agents, lead, layout, teammateMode, task: rawTask, assigns, autoAttach, progressive, timeoutSec, verbose, dashboard, mcpProfile } = parseTeamArgs(args);
|
|
41
|
+
const { agents, lead, layout, teammateMode, task: rawTask, assigns, autoAttach, progressive, timeoutSec, verbose, dashboard, mcpProfile, model } = parseTeamArgs(args);
|
|
42
42
|
// --assign 사용 시 task를 자동 생성
|
|
43
43
|
const task = rawTask || (assigns.length > 0 ? assigns.map(a => a.prompt).join(" + ") : "");
|
|
44
44
|
if (!task) return printStartUsage();
|
|
@@ -82,7 +82,7 @@ export async function teamStart(args = []) {
|
|
|
82
82
|
const state = effectiveMode === "in-process"
|
|
83
83
|
? await startInProcessTeam({ sessionId, task, lead, agents, subtasks, hubUrl })
|
|
84
84
|
: effectiveMode === "headless"
|
|
85
|
-
? await startHeadlessTeam({ sessionId, task, lead, agents, subtasks, layout, assigns, autoAttach, progressive, timeoutSec, verbose, dashboard, mcpProfile })
|
|
85
|
+
? await startHeadlessTeam({ sessionId, task, lead, agents, subtasks, layout, assigns, autoAttach, progressive, timeoutSec, verbose, dashboard, mcpProfile, model })
|
|
86
86
|
: effectiveMode === "wt"
|
|
87
87
|
? await startWtTeam({ sessionId, task, lead, agents, subtasks, layout, hubUrl })
|
|
88
88
|
: await startMuxTeam({ sessionId, task, lead, agents, subtasks, layout, hubUrl, teammateMode: effectiveMode });
|
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
import { normalizeLayout, normalizeTeammateMode } from "../../services/runtime-mode.mjs";
|
|
2
2
|
|
|
3
|
+
// --assign 파싱 시 마지막 콜론 뒤를 role로 인식할 알려진 역할/CLI 이름
|
|
4
|
+
const KNOWN_ROLES = new Set([
|
|
5
|
+
"codex", "gemini", "claude",
|
|
6
|
+
"executor", "architect", "planner", "analyst", "critic",
|
|
7
|
+
"debugger", "verifier", "code-reviewer", "security-reviewer",
|
|
8
|
+
"test-engineer", "designer", "writer", "scientist",
|
|
9
|
+
]);
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* --assign "cli:prompt:role" 형식을 콜론-안전하게 파싱한다.
|
|
13
|
+
* 프롬프트 내부의 콜론(:)은 구분자로 취급하지 않는다.
|
|
14
|
+
*
|
|
15
|
+
* 규칙:
|
|
16
|
+
* 1. 첫 번째 콜론 앞 = CLI 이름
|
|
17
|
+
* 2. 마지막 콜론 뒤가 KNOWN_ROLES에 있으면 role, 나머지가 prompt
|
|
18
|
+
* 3. 그 외에는 첫 콜론 뒤 전체가 prompt, role은 빈 문자열
|
|
19
|
+
*/
|
|
20
|
+
function parseAssignValue(raw) {
|
|
21
|
+
const firstColon = raw.indexOf(":");
|
|
22
|
+
if (firstColon < 0) return null;
|
|
23
|
+
|
|
24
|
+
const cli = raw.slice(0, firstColon).trim();
|
|
25
|
+
const rest = raw.slice(firstColon + 1);
|
|
26
|
+
|
|
27
|
+
const lastColon = rest.lastIndexOf(":");
|
|
28
|
+
if (lastColon > 0) {
|
|
29
|
+
const candidate = rest.slice(lastColon + 1).trim().toLowerCase();
|
|
30
|
+
if (KNOWN_ROLES.has(candidate)) {
|
|
31
|
+
return { cli, prompt: rest.slice(0, lastColon).trim(), role: candidate };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return { cli, prompt: rest.trim(), role: "" };
|
|
36
|
+
}
|
|
37
|
+
|
|
3
38
|
export function parseTeamArgs(args = []) {
|
|
4
39
|
let agents = ["codex", "gemini"];
|
|
5
40
|
let lead = "claude";
|
|
@@ -13,6 +48,7 @@ export function parseTeamArgs(args = []) {
|
|
|
13
48
|
let verbose = false;
|
|
14
49
|
let dashboard = false;
|
|
15
50
|
let mcpProfile = "";
|
|
51
|
+
let model = "";
|
|
16
52
|
|
|
17
53
|
for (let index = 0; index < args.length; index += 1) {
|
|
18
54
|
const current = args[index];
|
|
@@ -25,11 +61,8 @@ export function parseTeamArgs(args = []) {
|
|
|
25
61
|
} else if ((current === "--teammate-mode" || current === "--mode") && args[index + 1]) {
|
|
26
62
|
teammateMode = args[++index];
|
|
27
63
|
} else if (current === "--assign" && args[index + 1]) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if (parts.length >= 2) {
|
|
31
|
-
assigns.push({ cli: parts[0].trim(), prompt: parts.slice(1, -1).join(":").trim() || parts[1].trim(), role: parts[parts.length - 1]?.trim() || "" });
|
|
32
|
-
}
|
|
64
|
+
const parsed = parseAssignValue(args[++index]);
|
|
65
|
+
if (parsed) assigns.push(parsed);
|
|
33
66
|
} else if (current === "--auto-attach") {
|
|
34
67
|
autoAttach = true;
|
|
35
68
|
} else if (current === "--no-auto-attach") {
|
|
@@ -44,6 +77,8 @@ export function parseTeamArgs(args = []) {
|
|
|
44
77
|
timeoutSec = Number(args[++index]) || 300;
|
|
45
78
|
} else if (current === "--mcp-profile" && args[index + 1]) {
|
|
46
79
|
mcpProfile = args[++index].trim();
|
|
80
|
+
} else if ((current === "--model" || current === "-m") && args[index + 1]) {
|
|
81
|
+
model = args[++index].trim();
|
|
47
82
|
} else if (current.startsWith("-")) {
|
|
48
83
|
console.warn(` ⚠ 미인식 플래그 무시: ${current}`);
|
|
49
84
|
} else {
|
|
@@ -64,5 +99,6 @@ export function parseTeamArgs(args = []) {
|
|
|
64
99
|
verbose,
|
|
65
100
|
dashboard,
|
|
66
101
|
mcpProfile,
|
|
102
|
+
model,
|
|
67
103
|
};
|
|
68
104
|
}
|
|
@@ -4,11 +4,11 @@ import { ok, warn } from "../../render.mjs";
|
|
|
4
4
|
import { buildTasks } from "../../services/task-model.mjs";
|
|
5
5
|
import { clearTeamState } from "../../services/state-store.mjs";
|
|
6
6
|
|
|
7
|
-
export async function startHeadlessTeam({ sessionId, task, lead, agents, subtasks, layout, assigns, autoAttach, progressive, timeoutSec, verbose, dashboard, mcpProfile }) {
|
|
7
|
+
export async function startHeadlessTeam({ sessionId, task, lead, agents, subtasks, layout, assigns, autoAttach, progressive, timeoutSec, verbose, dashboard, mcpProfile, model }) {
|
|
8
8
|
// --assign이 있으면 그것을 사용, 없으면 agents+subtasks 조합
|
|
9
9
|
const assignments = assigns && assigns.length > 0
|
|
10
|
-
? assigns.map((a, i) => ({ cli: resolveCliType(a.cli), prompt: a.prompt, role: a.role || `worker-${i + 1}`, mcp: mcpProfile }))
|
|
11
|
-
: subtasks.map((subtask, i) => ({ cli: resolveCliType(agents[i] || agents[0]), prompt: subtask, role: `worker-${i + 1}`, mcp: mcpProfile }));
|
|
10
|
+
? assigns.map((a, i) => ({ cli: resolveCliType(a.cli), prompt: a.prompt, role: a.role || `worker-${i + 1}`, mcp: mcpProfile, model }))
|
|
11
|
+
: subtasks.map((subtask, i) => ({ cli: resolveCliType(agents[i] || agents[0]), prompt: subtask, role: `worker-${i + 1}`, mcp: mcpProfile, model }));
|
|
12
12
|
|
|
13
13
|
const startedAt = Date.now();
|
|
14
14
|
ok(`headless ${assignments.length}워커 시작`);
|
package/hub/team/headless.mjs
CHANGED
|
@@ -67,7 +67,7 @@ const MCP_PROFILE_HINTS = {
|
|
|
67
67
|
* @returns {string} PowerShell 명령
|
|
68
68
|
*/
|
|
69
69
|
export function buildHeadlessCommand(cli, prompt, resultFile, opts = {}) {
|
|
70
|
-
const { handoff = true, mcp, contextFile } = opts;
|
|
70
|
+
const { handoff = true, mcp, contextFile, model } = opts;
|
|
71
71
|
const resolvedCli = resolveCliType(cli);
|
|
72
72
|
|
|
73
73
|
// contextFile 처리: 32KB(32768 bytes) 초과 시 UTF-8 안전 절단
|
|
@@ -96,7 +96,7 @@ export function buildHeadlessCommand(cli, prompt, resultFile, opts = {}) {
|
|
|
96
96
|
|
|
97
97
|
const backend = getBackend(resolvedCli);
|
|
98
98
|
const promptExpr = `(Get-Content -Raw '${promptFile}')`;
|
|
99
|
-
return `${cls}${backend.buildArgs(promptExpr, resultFile, opts)}`;
|
|
99
|
+
return `${cls}${backend.buildArgs(promptExpr, resultFile, { ...opts, model })}`;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
/**
|
|
@@ -151,7 +151,7 @@ async function dispatchProgressive(sessionName, assignments, layout, safeProgres
|
|
|
151
151
|
|
|
152
152
|
// 캡처 시작 + 컬러 배너 + 명령 dispatch
|
|
153
153
|
const resultFile = join(RESULT_DIR, `${sessionName}-${paneName}.txt`).replace(/\\/g, "/");
|
|
154
|
-
const cmd = buildHeadlessCommand(assignment.cli, assignment.prompt, resultFile, { mcp: assignment.mcp });
|
|
154
|
+
const cmd = buildHeadlessCommand(assignment.cli, assignment.prompt, resultFile, { mcp: assignment.mcp, model: assignment.model });
|
|
155
155
|
startCapture(sessionName, newPaneId);
|
|
156
156
|
// pane 간 pipe-pane EBUSY 방지 — 이벤트 루프 해방하며 순차 대기
|
|
157
157
|
if (i > 0) await new Promise(r => setTimeout(r, 300));
|
|
@@ -182,7 +182,7 @@ function dispatchBatch(sessionName, assignments, layout, safeProgress) {
|
|
|
182
182
|
return assignments.map((assignment, i) => {
|
|
183
183
|
const paneName = `worker-${i + 1}`;
|
|
184
184
|
const resultFile = join(RESULT_DIR, `${sessionName}-${paneName}.txt`).replace(/\\/g, "/");
|
|
185
|
-
const cmd = buildHeadlessCommand(assignment.cli, assignment.prompt, resultFile, { mcp: assignment.mcp });
|
|
185
|
+
const cmd = buildHeadlessCommand(assignment.cli, assignment.prompt, resultFile, { mcp: assignment.mcp, model: assignment.model });
|
|
186
186
|
const scriptDir = join(RESULT_DIR, sessionName);
|
|
187
187
|
const dispatch = dispatchCommand(sessionName, paneName, cmd, { scriptDir, scriptName: paneName });
|
|
188
188
|
|