triflux 10.3.3 → 10.4.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/CLAUDE.md +193 -0
- 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/lib/path-utils.mjs +167 -0
- package/hub/router.mjs +791 -791
- package/hub/session-fingerprint.mjs +1 -1
- 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/notify.mjs +1 -1
- package/hub/team/orchestrator.mjs +161 -161
- package/hub/team/runtime-strategy.mjs +74 -0
- package/hub/team/session.mjs +611 -611
- package/hub/team/shared.mjs +13 -13
- package/hub/team/worktree-lifecycle.mjs +61 -2
- package/hub/tray.mjs +368 -368
- package/hub/workers/codex-mcp.mjs +507 -507
- package/hub/workers/factory.mjs +21 -21
- package/hud/hud-qos-status.mjs +17 -3
- package/hud/mission-board.mjs +53 -0
- package/hud/providers/claude.mjs +95 -22
- package/hud/renderers.mjs +39 -5
- package/package.json +21 -55
- package/references/hosts.json +33 -0
- package/scripts/completions/tfx.bash +47 -47
- package/scripts/completions/tfx.fish +44 -44
- package/scripts/completions/tfx.zsh +83 -83
- package/scripts/demo.mjs +169 -0
- package/scripts/headless-guard.mjs +16 -4
- 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/lib/skill-state.mjs +220 -0
- package/scripts/notion-read.mjs +553 -553
- 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/skills/tfx-workspace/async-tests/run-tests.sh +203 -0
- package/skills/tfx-workspace/evals/evals.json +79 -0
- package/skills/tfx-workspace/iteration-1/benchmark.json +162 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/eval_metadata.json +11 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/grading.json +9 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/outputs/analysis.md +154 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/grading.json +9 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/outputs/analysis.md +126 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/eval_metadata.json +11 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/grading.json +9 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/outputs/analysis.md +119 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/grading.json +9 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/outputs/analysis.md +115 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/eval_metadata.json +10 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/grading.json +8 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/outputs/analysis.md +86 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/grading.json +8 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/outputs/analysis.md +81 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/eval_metadata.json +12 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/grading.json +10 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/outputs/analysis.md +316 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/grading.json +10 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/outputs/analysis.md +352 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/review.html +1325 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/eval_metadata.json +12 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/grading.json +10 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/outputs/analysis.md +97 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/grading.json +10 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/outputs/analysis.md +94 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/eval_metadata.json +12 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/grading.json +10 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/outputs/analysis.md +209 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/grading.json +10 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/outputs/analysis.md +193 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-2/benchmark.json +62 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/eval_metadata.json +13 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/grading.json +11 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/outputs/analysis.md +382 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/grading.json +11 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/outputs/analysis.md +333 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-2/review.html +1325 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-auto/SKILL.md +217 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-auto-codex/SKILL.md +77 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-codex/SKILL.md +65 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-doctor/SKILL.md +94 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-gemini/SKILL.md +82 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-hub/SKILL.md +133 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-multi/SKILL.md +426 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-setup/SKILL.md +101 -0
- package/.claude-plugin/marketplace.json +0 -34
- package/.claude-plugin/plugin.json +0 -22
- package/config/mcp-registry.json +0 -29
- package/tui/codex-profile.mjs +0 -402
- package/tui/core.mjs +0 -236
- package/tui/doctor.mjs +0 -328
- package/tui/gemini-profile.mjs +0 -254
- package/tui/monitor-data.mjs +0 -148
- package/tui/monitor.mjs +0 -295
- package/tui/setup.mjs +0 -442
|
@@ -1,69 +1,69 @@
|
|
|
1
|
-
import { createWtSession } from "../../../session.mjs";
|
|
2
|
-
import { buildCliCommand } from "../../../pane.mjs";
|
|
3
|
-
import { toAgentId } from "../../services/member-selector.mjs";
|
|
4
|
-
import { buildTasks } from "../../services/task-model.mjs";
|
|
5
|
-
import { warn } from "../../render.mjs";
|
|
6
|
-
|
|
7
|
-
export async function startWtTeam({ sessionId, task, lead, agents, subtasks, layout, hubUrl }) {
|
|
8
|
-
const paneCount = agents.length + 1;
|
|
9
|
-
const effectiveLayout = layout === "Nx1" ? "Nx1" : "1xN";
|
|
10
|
-
if (layout !== effectiveLayout) warn(`wt 모드에서 ${layout} 레이아웃은 미지원 — ${effectiveLayout}로 대체`);
|
|
11
|
-
console.log(` 레이아웃: ${effectiveLayout} (${paneCount} panes)`);
|
|
12
|
-
|
|
13
|
-
const session = createWtSession(sessionId, {
|
|
14
|
-
layout: effectiveLayout,
|
|
15
|
-
paneCommands: [
|
|
16
|
-
{ title: `${sessionId}-lead`, command: buildCliCommand(lead) },
|
|
17
|
-
...agents.map((cli, index) => ({
|
|
18
|
-
title: `${sessionId}-${cli}-${index + 1}`,
|
|
19
|
-
command: buildCliCommand(cli),
|
|
20
|
-
})),
|
|
21
|
-
],
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
const members = [
|
|
25
|
-
{
|
|
26
|
-
role: "lead",
|
|
27
|
-
name: "lead",
|
|
28
|
-
cli: lead,
|
|
29
|
-
pane: session.panes[0] || "wt:0",
|
|
30
|
-
agentId: toAgentId(lead, session.panes[0] || "wt:0"),
|
|
31
|
-
},
|
|
32
|
-
...agents.map((cli, index) => {
|
|
33
|
-
const pane = session.panes[index + 1] || `wt:${index + 1}`;
|
|
34
|
-
return {
|
|
35
|
-
role: "worker",
|
|
36
|
-
name: `${cli}-${index + 1}`,
|
|
37
|
-
cli,
|
|
38
|
-
pane,
|
|
39
|
-
subtask: subtasks[index],
|
|
40
|
-
agentId: toAgentId(cli, pane),
|
|
41
|
-
};
|
|
42
|
-
}),
|
|
43
|
-
];
|
|
44
|
-
|
|
45
|
-
return {
|
|
46
|
-
sessionName: sessionId,
|
|
47
|
-
task,
|
|
48
|
-
lead,
|
|
49
|
-
agents,
|
|
50
|
-
layout: effectiveLayout,
|
|
51
|
-
teammateMode: "wt",
|
|
52
|
-
startedAt: Date.now(),
|
|
53
|
-
hubUrl,
|
|
54
|
-
members,
|
|
55
|
-
panes: Object.fromEntries(members.map((member) => [member.pane, {
|
|
56
|
-
role: member.role,
|
|
57
|
-
name: member.name,
|
|
58
|
-
cli: member.cli,
|
|
59
|
-
agentId: member.agentId,
|
|
60
|
-
subtask: member.subtask || null,
|
|
61
|
-
}])),
|
|
62
|
-
tasks: buildTasks(subtasks, members.filter((member) => member.role === "worker")),
|
|
63
|
-
wt: {
|
|
64
|
-
windowId: 0,
|
|
65
|
-
layout: effectiveLayout,
|
|
66
|
-
paneCount: session.paneCount,
|
|
67
|
-
},
|
|
68
|
-
};
|
|
69
|
-
}
|
|
1
|
+
import { createWtSession } from "../../../session.mjs";
|
|
2
|
+
import { buildCliCommand } from "../../../pane.mjs";
|
|
3
|
+
import { toAgentId } from "../../services/member-selector.mjs";
|
|
4
|
+
import { buildTasks } from "../../services/task-model.mjs";
|
|
5
|
+
import { warn } from "../../render.mjs";
|
|
6
|
+
|
|
7
|
+
export async function startWtTeam({ sessionId, task, lead, agents, subtasks, layout, hubUrl }) {
|
|
8
|
+
const paneCount = agents.length + 1;
|
|
9
|
+
const effectiveLayout = layout === "Nx1" ? "Nx1" : "1xN";
|
|
10
|
+
if (layout !== effectiveLayout) warn(`wt 모드에서 ${layout} 레이아웃은 미지원 — ${effectiveLayout}로 대체`);
|
|
11
|
+
console.log(` 레이아웃: ${effectiveLayout} (${paneCount} panes)`);
|
|
12
|
+
|
|
13
|
+
const session = createWtSession(sessionId, {
|
|
14
|
+
layout: effectiveLayout,
|
|
15
|
+
paneCommands: [
|
|
16
|
+
{ title: `${sessionId}-lead`, command: buildCliCommand(lead) },
|
|
17
|
+
...agents.map((cli, index) => ({
|
|
18
|
+
title: `${sessionId}-${cli}-${index + 1}`,
|
|
19
|
+
command: buildCliCommand(cli),
|
|
20
|
+
})),
|
|
21
|
+
],
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const members = [
|
|
25
|
+
{
|
|
26
|
+
role: "lead",
|
|
27
|
+
name: "lead",
|
|
28
|
+
cli: lead,
|
|
29
|
+
pane: session.panes[0] || "wt:0",
|
|
30
|
+
agentId: toAgentId(lead, session.panes[0] || "wt:0"),
|
|
31
|
+
},
|
|
32
|
+
...agents.map((cli, index) => {
|
|
33
|
+
const pane = session.panes[index + 1] || `wt:${index + 1}`;
|
|
34
|
+
return {
|
|
35
|
+
role: "worker",
|
|
36
|
+
name: `${cli}-${index + 1}`,
|
|
37
|
+
cli,
|
|
38
|
+
pane,
|
|
39
|
+
subtask: subtasks[index],
|
|
40
|
+
agentId: toAgentId(cli, pane),
|
|
41
|
+
};
|
|
42
|
+
}),
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
sessionName: sessionId,
|
|
47
|
+
task,
|
|
48
|
+
lead,
|
|
49
|
+
agents,
|
|
50
|
+
layout: effectiveLayout,
|
|
51
|
+
teammateMode: "wt",
|
|
52
|
+
startedAt: Date.now(),
|
|
53
|
+
hubUrl,
|
|
54
|
+
members,
|
|
55
|
+
panes: Object.fromEntries(members.map((member) => [member.pane, {
|
|
56
|
+
role: member.role,
|
|
57
|
+
name: member.name,
|
|
58
|
+
cli: member.cli,
|
|
59
|
+
agentId: member.agentId,
|
|
60
|
+
subtask: member.subtask || null,
|
|
61
|
+
}])),
|
|
62
|
+
tasks: buildTasks(subtasks, members.filter((member) => member.role === "worker")),
|
|
63
|
+
wt: {
|
|
64
|
+
windowId: 0,
|
|
65
|
+
layout: effectiveLayout,
|
|
66
|
+
paneCount: session.paneCount,
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { DIM, RESET } from "../../shared.mjs";
|
|
2
|
-
import { isTeamAlive } from "../services/runtime-mode.mjs";
|
|
3
|
-
import { loadTeamState } from "../services/state-store.mjs";
|
|
4
|
-
import { renderTasks } from "../render.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
|
-
}
|
|
1
|
+
import { DIM, RESET } from "../../shared.mjs";
|
|
2
|
+
import { isTeamAlive } from "../services/runtime-mode.mjs";
|
|
3
|
+
import { loadTeamState } from "../services/state-store.mjs";
|
|
4
|
+
import { renderTasks } from "../render.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/render.mjs
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
import { AMBER, BOLD, DIM, GRAY, GREEN, RED, RESET, WHITE, YELLOW } from "../shared.mjs";
|
|
2
|
-
|
|
3
|
-
export function ok(msg) { console.log(` ${GREEN}✓${RESET} ${msg}`); }
|
|
4
|
-
export function warn(msg) { console.log(` ${YELLOW}⚠${RESET} ${msg}`); }
|
|
5
|
-
export function fail(msg) { console.log(` ${RED}✗${RESET} ${msg}`); }
|
|
6
|
-
|
|
7
|
-
export function renderTasks(tasks = []) {
|
|
8
|
-
if (!tasks.length) {
|
|
9
|
-
console.log(`\n ${DIM}태스크 없음${RESET}\n`);
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
console.log(`\n ${AMBER}${BOLD}⬡ Team Tasks${RESET}\n`);
|
|
14
|
-
for (const task of tasks) {
|
|
15
|
-
const dep = task.depends_on?.length ? ` ${DIM}(deps: ${task.depends_on.join(",")})${RESET}` : "";
|
|
16
|
-
const owner = task.owner ? ` ${GRAY}[${task.owner}]${RESET}` : "";
|
|
17
|
-
console.log(` ${WHITE}${task.id}${RESET} ${String(task.status || "").padEnd(11)} ${task.title}${owner}${dep}`);
|
|
18
|
-
}
|
|
19
|
-
console.log("");
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function formatCompletionSuffix(member) {
|
|
23
|
-
if (!member?.completionStatus) return "";
|
|
24
|
-
if (member.completionStatus === "abnormal") {
|
|
25
|
-
return ` ${RED}[abnormal:${member.completionReason || "unknown"}]${RESET}`;
|
|
26
|
-
}
|
|
27
|
-
if (member.completionStatus === "normal") return ` ${GREEN}[route-ok]${RESET}`;
|
|
28
|
-
if (member.completionStatus === "unchecked") return ` ${GRAY}[route-unchecked]${RESET}`;
|
|
29
|
-
return "";
|
|
30
|
-
}
|
|
1
|
+
import { AMBER, BOLD, DIM, GRAY, GREEN, RED, RESET, WHITE, YELLOW } from "../shared.mjs";
|
|
2
|
+
|
|
3
|
+
export function ok(msg) { console.log(` ${GREEN}✓${RESET} ${msg}`); }
|
|
4
|
+
export function warn(msg) { console.log(` ${YELLOW}⚠${RESET} ${msg}`); }
|
|
5
|
+
export function fail(msg) { console.log(` ${RED}✗${RESET} ${msg}`); }
|
|
6
|
+
|
|
7
|
+
export function renderTasks(tasks = []) {
|
|
8
|
+
if (!tasks.length) {
|
|
9
|
+
console.log(`\n ${DIM}태스크 없음${RESET}\n`);
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
console.log(`\n ${AMBER}${BOLD}⬡ Team Tasks${RESET}\n`);
|
|
14
|
+
for (const task of tasks) {
|
|
15
|
+
const dep = task.depends_on?.length ? ` ${DIM}(deps: ${task.depends_on.join(",")})${RESET}` : "";
|
|
16
|
+
const owner = task.owner ? ` ${GRAY}[${task.owner}]${RESET}` : "";
|
|
17
|
+
console.log(` ${WHITE}${task.id}${RESET} ${String(task.status || "").padEnd(11)} ${task.title}${owner}${dep}`);
|
|
18
|
+
}
|
|
19
|
+
console.log("");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function formatCompletionSuffix(member) {
|
|
23
|
+
if (!member?.completionStatus) return "";
|
|
24
|
+
if (member.completionStatus === "abnormal") {
|
|
25
|
+
return ` ${RED}[abnormal:${member.completionReason || "unknown"}]${RESET}`;
|
|
26
|
+
}
|
|
27
|
+
if (member.completionStatus === "normal") return ` ${GREEN}[route-ok]${RESET}`;
|
|
28
|
+
if (member.completionStatus === "unchecked") return ` ${GRAY}[route-unchecked]${RESET}`;
|
|
29
|
+
return "";
|
|
30
|
+
}
|
|
@@ -1,54 +1,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("wt", ["-w", "0", "split-pane", "-V", "-d", PKG_ROOT, attachSpec.command, ...attachSpec.args], {
|
|
23
|
-
detached: true,
|
|
24
|
-
stdio: "ignore",
|
|
25
|
-
windowsHide: false,
|
|
26
|
-
});
|
|
27
|
-
child.unref();
|
|
28
|
-
|
|
29
|
-
if (beforeAttached == null) return true;
|
|
30
|
-
const deadline = Date.now() + 3500;
|
|
31
|
-
while (Date.now() < deadline) {
|
|
32
|
-
await new Promise((resolve) => setTimeout(resolve, 120));
|
|
33
|
-
const nowAttached = getSessionAttachedCount(sessionName);
|
|
34
|
-
if (typeof nowAttached === "number" && nowAttached > beforeAttached) return true;
|
|
35
|
-
}
|
|
36
|
-
} catch {}
|
|
37
|
-
return false;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function buildManualAttachCommand(sessionName) {
|
|
41
|
-
try {
|
|
42
|
-
const spec = resolveAttachCommand(sessionName);
|
|
43
|
-
return [spec.command, ...spec.args].map((value) => {
|
|
44
|
-
const text = String(value);
|
|
45
|
-
return /\s/.test(text) ? `"${text.replace(/"/g, '\\"')}"` : text;
|
|
46
|
-
}).join(" ");
|
|
47
|
-
} catch {
|
|
48
|
-
return `tmux attach-session -t ${sessionName}`;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export function wantsWtAttachFallback(args = [], env = process.env) {
|
|
53
|
-
return args.includes("--wt") || args.includes("--spawn-wt") || env.TFX_ATTACH_WT_AUTO === "1";
|
|
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("wt", ["-w", "0", "split-pane", "-V", "-d", PKG_ROOT, attachSpec.command, ...attachSpec.args], {
|
|
23
|
+
detached: true,
|
|
24
|
+
stdio: "ignore",
|
|
25
|
+
windowsHide: false,
|
|
26
|
+
});
|
|
27
|
+
child.unref();
|
|
28
|
+
|
|
29
|
+
if (beforeAttached == null) return true;
|
|
30
|
+
const deadline = Date.now() + 3500;
|
|
31
|
+
while (Date.now() < deadline) {
|
|
32
|
+
await new Promise((resolve) => setTimeout(resolve, 120));
|
|
33
|
+
const nowAttached = getSessionAttachedCount(sessionName);
|
|
34
|
+
if (typeof nowAttached === "number" && nowAttached > beforeAttached) return true;
|
|
35
|
+
}
|
|
36
|
+
} catch {}
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function buildManualAttachCommand(sessionName) {
|
|
41
|
+
try {
|
|
42
|
+
const spec = resolveAttachCommand(sessionName);
|
|
43
|
+
return [spec.command, ...spec.args].map((value) => {
|
|
44
|
+
const text = String(value);
|
|
45
|
+
return /\s/.test(text) ? `"${text.replace(/"/g, '\\"')}"` : text;
|
|
46
|
+
}).join(" ");
|
|
47
|
+
} catch {
|
|
48
|
+
return `tmux attach-session -t ${sessionName}`;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function wantsWtAttachFallback(args = [], env = process.env) {
|
|
53
|
+
return args.includes("--wt") || args.includes("--spawn-wt") || env.TFX_ATTACH_WT_AUTO === "1";
|
|
54
|
+
}
|
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
export function resolveMember(state, selector) {
|
|
2
|
-
const members = state?.members || [];
|
|
3
|
-
if (!selector) return null;
|
|
4
|
-
|
|
5
|
-
const direct = members.find((member) => (
|
|
6
|
-
member.name === selector || member.role === selector || member.agentId === selector
|
|
7
|
-
));
|
|
8
|
-
if (direct) return direct;
|
|
9
|
-
|
|
10
|
-
const workerAlias = /^worker-(\d+)$/i.exec(selector);
|
|
11
|
-
if (workerAlias) {
|
|
12
|
-
const index = parseInt(workerAlias[1], 10) - 1;
|
|
13
|
-
const workers = members.filter((member) => member.role === "worker");
|
|
14
|
-
if (index >= 0 && index < workers.length) return workers[index];
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const numeric = parseInt(selector, 10);
|
|
18
|
-
if (!Number.isNaN(numeric)) {
|
|
19
|
-
const byPane = members.find((member) => member.pane?.endsWith(`.${numeric}`) || member.pane?.endsWith(`:${numeric}`));
|
|
20
|
-
if (byPane) return byPane;
|
|
21
|
-
if (numeric >= 1 && numeric <= members.length) return members[numeric - 1];
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function toAgentId(cli, target) {
|
|
28
|
-
const suffix = String(target).split(/[:.]/).pop();
|
|
29
|
-
return `${cli}-${suffix}`;
|
|
30
|
-
}
|
|
1
|
+
export function resolveMember(state, selector) {
|
|
2
|
+
const members = state?.members || [];
|
|
3
|
+
if (!selector) return null;
|
|
4
|
+
|
|
5
|
+
const direct = members.find((member) => (
|
|
6
|
+
member.name === selector || member.role === selector || member.agentId === selector
|
|
7
|
+
));
|
|
8
|
+
if (direct) return direct;
|
|
9
|
+
|
|
10
|
+
const workerAlias = /^worker-(\d+)$/i.exec(selector);
|
|
11
|
+
if (workerAlias) {
|
|
12
|
+
const index = parseInt(workerAlias[1], 10) - 1;
|
|
13
|
+
const workers = members.filter((member) => member.role === "worker");
|
|
14
|
+
if (index >= 0 && index < workers.length) return workers[index];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const numeric = parseInt(selector, 10);
|
|
18
|
+
if (!Number.isNaN(numeric)) {
|
|
19
|
+
const byPane = members.find((member) => member.pane?.endsWith(`.${numeric}`) || member.pane?.endsWith(`:${numeric}`));
|
|
20
|
+
if (byPane) return byPane;
|
|
21
|
+
if (numeric >= 1 && numeric <= members.length) return members[numeric - 1];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function toAgentId(cli, target) {
|
|
28
|
+
const suffix = String(target).split(/[:.]/).pop();
|
|
29
|
+
return `${cli}-${suffix}`;
|
|
30
|
+
}
|
|
@@ -1,117 +1,117 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import { spawn } from "node:child_process";
|
|
4
|
-
|
|
5
|
-
import { buildLeadPrompt, buildPrompt } from "../../orchestrator.mjs";
|
|
6
|
-
import { HUB_PID_DIR, PKG_ROOT } from "./state-store.mjs";
|
|
7
|
-
|
|
8
|
-
import { buildExecArgs } from "../../../codex-adapter.mjs";
|
|
9
|
-
|
|
10
|
-
export function buildNativeCliCommand(cli) {
|
|
11
|
-
switch (cli) {
|
|
12
|
-
case "codex":
|
|
13
|
-
return buildExecArgs({});
|
|
14
|
-
case "gemini":
|
|
15
|
-
return "gemini";
|
|
16
|
-
case "claude":
|
|
17
|
-
return "claude";
|
|
18
|
-
default:
|
|
19
|
-
return cli;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export async function startNativeSupervisor({ sessionId, task, lead, agents, subtasks, hubUrl }) {
|
|
24
|
-
const configPath = join(HUB_PID_DIR, `team-native-${sessionId}.config.json`);
|
|
25
|
-
const runtimePath = join(HUB_PID_DIR, `team-native-${sessionId}.runtime.json`);
|
|
26
|
-
const logsDir = join(HUB_PID_DIR, "team-logs", sessionId);
|
|
27
|
-
mkdirSync(logsDir, { recursive: true });
|
|
28
|
-
|
|
29
|
-
const leadMember = {
|
|
30
|
-
role: "lead",
|
|
31
|
-
name: "lead",
|
|
32
|
-
cli: lead,
|
|
33
|
-
agentId: `${lead}-lead`,
|
|
34
|
-
command: buildNativeCliCommand(lead),
|
|
35
|
-
};
|
|
36
|
-
const workers = agents.map((cli, index) => ({
|
|
37
|
-
role: "worker",
|
|
38
|
-
name: `${cli}-${index + 1}`,
|
|
39
|
-
cli,
|
|
40
|
-
agentId: `${cli}-w${index + 1}`,
|
|
41
|
-
command: buildNativeCliCommand(cli),
|
|
42
|
-
subtask: subtasks[index],
|
|
43
|
-
}));
|
|
44
|
-
const members = [
|
|
45
|
-
{
|
|
46
|
-
...leadMember,
|
|
47
|
-
prompt: buildLeadPrompt(task, {
|
|
48
|
-
agentId: leadMember.agentId,
|
|
49
|
-
hubUrl,
|
|
50
|
-
teammateMode: "in-process",
|
|
51
|
-
workers: workers.map((worker) => ({
|
|
52
|
-
agentId: worker.agentId,
|
|
53
|
-
cli: worker.cli,
|
|
54
|
-
subtask: worker.subtask,
|
|
55
|
-
})),
|
|
56
|
-
}),
|
|
57
|
-
},
|
|
58
|
-
...workers.map((worker) => ({
|
|
59
|
-
...worker,
|
|
60
|
-
prompt: buildPrompt(worker.subtask, { cli: worker.cli, agentId: worker.agentId, hubUrl }),
|
|
61
|
-
})),
|
|
62
|
-
];
|
|
63
|
-
|
|
64
|
-
writeFileSync(configPath, JSON.stringify({
|
|
65
|
-
sessionName: sessionId,
|
|
66
|
-
hubUrl,
|
|
67
|
-
startupDelayMs: 3000,
|
|
68
|
-
logsDir,
|
|
69
|
-
runtimeFile: runtimePath,
|
|
70
|
-
members,
|
|
71
|
-
}, null, 2) + "\n");
|
|
72
|
-
|
|
73
|
-
const child = spawn(process.execPath, [join(PKG_ROOT, "hub", "team", "native-supervisor.mjs"), "--config", configPath], {
|
|
74
|
-
detached: true,
|
|
75
|
-
stdio: "ignore",
|
|
76
|
-
env: { ...process.env },
|
|
77
|
-
windowsHide: true,
|
|
78
|
-
});
|
|
79
|
-
child.unref();
|
|
80
|
-
|
|
81
|
-
const deadline = Date.now() + 5000;
|
|
82
|
-
while (Date.now() < deadline) {
|
|
83
|
-
if (existsSync(runtimePath)) {
|
|
84
|
-
try {
|
|
85
|
-
const runtime = JSON.parse(readFileSync(runtimePath, "utf8"));
|
|
86
|
-
return { runtime, members };
|
|
87
|
-
} catch {}
|
|
88
|
-
}
|
|
89
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return { runtime: null, members };
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export async function nativeRequest(state, path, body = {}) {
|
|
96
|
-
if (!state?.native?.controlUrl) return null;
|
|
97
|
-
try {
|
|
98
|
-
const res = await fetch(`${state.native.controlUrl}${path}`, {
|
|
99
|
-
method: "POST",
|
|
100
|
-
headers: { "Content-Type": "application/json" },
|
|
101
|
-
body: JSON.stringify(body),
|
|
102
|
-
});
|
|
103
|
-
return await res.json();
|
|
104
|
-
} catch {
|
|
105
|
-
return null;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export async function nativeGetStatus(state) {
|
|
110
|
-
if (!state?.native?.controlUrl) return null;
|
|
111
|
-
try {
|
|
112
|
-
const res = await fetch(`${state.native.controlUrl}/status`);
|
|
113
|
-
return await res.json();
|
|
114
|
-
} catch {
|
|
115
|
-
return null;
|
|
116
|
-
}
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
4
|
+
|
|
5
|
+
import { buildLeadPrompt, buildPrompt } from "../../orchestrator.mjs";
|
|
6
|
+
import { HUB_PID_DIR, PKG_ROOT } from "./state-store.mjs";
|
|
7
|
+
|
|
8
|
+
import { buildExecArgs } from "../../../codex-adapter.mjs";
|
|
9
|
+
|
|
10
|
+
export function buildNativeCliCommand(cli) {
|
|
11
|
+
switch (cli) {
|
|
12
|
+
case "codex":
|
|
13
|
+
return buildExecArgs({});
|
|
14
|
+
case "gemini":
|
|
15
|
+
return "gemini";
|
|
16
|
+
case "claude":
|
|
17
|
+
return "claude";
|
|
18
|
+
default:
|
|
19
|
+
return cli;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function startNativeSupervisor({ sessionId, task, lead, agents, subtasks, hubUrl }) {
|
|
24
|
+
const configPath = join(HUB_PID_DIR, `team-native-${sessionId}.config.json`);
|
|
25
|
+
const runtimePath = join(HUB_PID_DIR, `team-native-${sessionId}.runtime.json`);
|
|
26
|
+
const logsDir = join(HUB_PID_DIR, "team-logs", sessionId);
|
|
27
|
+
mkdirSync(logsDir, { recursive: true });
|
|
28
|
+
|
|
29
|
+
const leadMember = {
|
|
30
|
+
role: "lead",
|
|
31
|
+
name: "lead",
|
|
32
|
+
cli: lead,
|
|
33
|
+
agentId: `${lead}-lead`,
|
|
34
|
+
command: buildNativeCliCommand(lead),
|
|
35
|
+
};
|
|
36
|
+
const workers = agents.map((cli, index) => ({
|
|
37
|
+
role: "worker",
|
|
38
|
+
name: `${cli}-${index + 1}`,
|
|
39
|
+
cli,
|
|
40
|
+
agentId: `${cli}-w${index + 1}`,
|
|
41
|
+
command: buildNativeCliCommand(cli),
|
|
42
|
+
subtask: subtasks[index],
|
|
43
|
+
}));
|
|
44
|
+
const members = [
|
|
45
|
+
{
|
|
46
|
+
...leadMember,
|
|
47
|
+
prompt: buildLeadPrompt(task, {
|
|
48
|
+
agentId: leadMember.agentId,
|
|
49
|
+
hubUrl,
|
|
50
|
+
teammateMode: "in-process",
|
|
51
|
+
workers: workers.map((worker) => ({
|
|
52
|
+
agentId: worker.agentId,
|
|
53
|
+
cli: worker.cli,
|
|
54
|
+
subtask: worker.subtask,
|
|
55
|
+
})),
|
|
56
|
+
}),
|
|
57
|
+
},
|
|
58
|
+
...workers.map((worker) => ({
|
|
59
|
+
...worker,
|
|
60
|
+
prompt: buildPrompt(worker.subtask, { cli: worker.cli, agentId: worker.agentId, hubUrl }),
|
|
61
|
+
})),
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
writeFileSync(configPath, JSON.stringify({
|
|
65
|
+
sessionName: sessionId,
|
|
66
|
+
hubUrl,
|
|
67
|
+
startupDelayMs: 3000,
|
|
68
|
+
logsDir,
|
|
69
|
+
runtimeFile: runtimePath,
|
|
70
|
+
members,
|
|
71
|
+
}, null, 2) + "\n");
|
|
72
|
+
|
|
73
|
+
const child = spawn(process.execPath, [join(PKG_ROOT, "hub", "team", "native-supervisor.mjs"), "--config", configPath], {
|
|
74
|
+
detached: true,
|
|
75
|
+
stdio: "ignore",
|
|
76
|
+
env: { ...process.env },
|
|
77
|
+
windowsHide: true,
|
|
78
|
+
});
|
|
79
|
+
child.unref();
|
|
80
|
+
|
|
81
|
+
const deadline = Date.now() + 5000;
|
|
82
|
+
while (Date.now() < deadline) {
|
|
83
|
+
if (existsSync(runtimePath)) {
|
|
84
|
+
try {
|
|
85
|
+
const runtime = JSON.parse(readFileSync(runtimePath, "utf8"));
|
|
86
|
+
return { runtime, members };
|
|
87
|
+
} catch {}
|
|
88
|
+
}
|
|
89
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return { runtime: null, members };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export async function nativeRequest(state, path, body = {}) {
|
|
96
|
+
if (!state?.native?.controlUrl) return null;
|
|
97
|
+
try {
|
|
98
|
+
const res = await fetch(`${state.native.controlUrl}${path}`, {
|
|
99
|
+
method: "POST",
|
|
100
|
+
headers: { "Content-Type": "application/json" },
|
|
101
|
+
body: JSON.stringify(body),
|
|
102
|
+
});
|
|
103
|
+
return await res.json();
|
|
104
|
+
} catch {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export async function nativeGetStatus(state) {
|
|
110
|
+
if (!state?.native?.controlUrl) return null;
|
|
111
|
+
try {
|
|
112
|
+
const res = await fetch(`${state.native.controlUrl}/status`);
|
|
113
|
+
return await res.json();
|
|
114
|
+
} catch {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
117
|
}
|