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.
Files changed (130) hide show
  1. package/CLAUDE.md +193 -0
  2. package/LICENSE +21 -21
  3. package/hooks/hook-registry.json +256 -256
  4. package/hub/adaptive-inject.mjs +1 -1
  5. package/hub/assign-callbacks.mjs +120 -120
  6. package/hub/delegator/index.mjs +14 -14
  7. package/hub/delegator/tool-definitions.mjs +35 -35
  8. package/hub/hitl.mjs +143 -143
  9. package/hub/lib/path-utils.mjs +167 -0
  10. package/hub/router.mjs +791 -791
  11. package/hub/session-fingerprint.mjs +1 -1
  12. package/hub/team/cli/commands/attach.mjs +37 -37
  13. package/hub/team/cli/commands/debug.mjs +74 -74
  14. package/hub/team/cli/commands/focus.mjs +53 -53
  15. package/hub/team/cli/commands/list.mjs +24 -24
  16. package/hub/team/cli/commands/start/start-in-process.mjs +40 -40
  17. package/hub/team/cli/commands/start/start-mux.mjs +73 -73
  18. package/hub/team/cli/commands/start/start-wt.mjs +69 -69
  19. package/hub/team/cli/commands/tasks.mjs +13 -13
  20. package/hub/team/cli/render.mjs +30 -30
  21. package/hub/team/cli/services/attach-fallback.mjs +54 -54
  22. package/hub/team/cli/services/member-selector.mjs +30 -30
  23. package/hub/team/cli/services/native-control.mjs +116 -116
  24. package/hub/team/cli/services/task-model.mjs +30 -30
  25. package/hub/team/notify.mjs +1 -1
  26. package/hub/team/orchestrator.mjs +161 -161
  27. package/hub/team/runtime-strategy.mjs +74 -0
  28. package/hub/team/session.mjs +611 -611
  29. package/hub/team/shared.mjs +13 -13
  30. package/hub/team/worktree-lifecycle.mjs +61 -2
  31. package/hub/tray.mjs +368 -368
  32. package/hub/workers/codex-mcp.mjs +507 -507
  33. package/hub/workers/factory.mjs +21 -21
  34. package/hud/hud-qos-status.mjs +17 -3
  35. package/hud/mission-board.mjs +53 -0
  36. package/hud/providers/claude.mjs +95 -22
  37. package/hud/renderers.mjs +39 -5
  38. package/package.json +21 -55
  39. package/references/hosts.json +33 -0
  40. package/scripts/completions/tfx.bash +47 -47
  41. package/scripts/completions/tfx.fish +44 -44
  42. package/scripts/completions/tfx.zsh +83 -83
  43. package/scripts/demo.mjs +169 -0
  44. package/scripts/headless-guard.mjs +16 -4
  45. package/scripts/hub-ensure.mjs +120 -120
  46. package/scripts/keyword-detector.mjs +272 -272
  47. package/scripts/keyword-rules-expander.mjs +521 -521
  48. package/scripts/lib/mcp-server-catalog.mjs +118 -118
  49. package/scripts/lib/skill-state.mjs +220 -0
  50. package/scripts/notion-read.mjs +553 -553
  51. package/scripts/test-tfx-route-no-claude-native.mjs +57 -57
  52. package/scripts/tfx-batch-stats.mjs +96 -96
  53. package/skills/.omc/state/agent-replay-8f0e10a9-9693-4410-96f5-a6b07e8ed995.jsonl +1 -0
  54. package/skills/.omc/state/idle-notif-cooldown.json +3 -0
  55. package/skills/.omc/state/last-tool-error.json +7 -0
  56. package/skills/.omc/state/subagent-tracking.json +7 -0
  57. package/skills/tfx-remote-spawn/references/hosts.json +16 -0
  58. package/skills/tfx-workspace/async-tests/run-tests.sh +203 -0
  59. package/skills/tfx-workspace/evals/evals.json +79 -0
  60. package/skills/tfx-workspace/iteration-1/benchmark.json +162 -0
  61. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/eval_metadata.json +11 -0
  62. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/grading.json +9 -0
  63. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/outputs/analysis.md +154 -0
  64. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/timing.json +5 -0
  65. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/grading.json +9 -0
  66. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/outputs/analysis.md +126 -0
  67. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/timing.json +5 -0
  68. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/eval_metadata.json +11 -0
  69. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/grading.json +9 -0
  70. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/outputs/analysis.md +119 -0
  71. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/timing.json +5 -0
  72. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/grading.json +9 -0
  73. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/outputs/analysis.md +115 -0
  74. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/timing.json +5 -0
  75. package/skills/tfx-workspace/iteration-1/hub-start-sequence/eval_metadata.json +10 -0
  76. package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/grading.json +8 -0
  77. package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/outputs/analysis.md +86 -0
  78. package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/timing.json +5 -0
  79. package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/grading.json +8 -0
  80. package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/outputs/analysis.md +81 -0
  81. package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/timing.json +5 -0
  82. package/skills/tfx-workspace/iteration-1/multi-team-creation/eval_metadata.json +12 -0
  83. package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/grading.json +10 -0
  84. package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/outputs/analysis.md +316 -0
  85. package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/timing.json +5 -0
  86. package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/grading.json +10 -0
  87. package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/outputs/analysis.md +352 -0
  88. package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/timing.json +5 -0
  89. package/skills/tfx-workspace/iteration-1/review.html +1325 -0
  90. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/eval_metadata.json +12 -0
  91. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/grading.json +10 -0
  92. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/outputs/analysis.md +97 -0
  93. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/timing.json +5 -0
  94. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/grading.json +10 -0
  95. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/outputs/analysis.md +94 -0
  96. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/timing.json +5 -0
  97. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/eval_metadata.json +12 -0
  98. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/grading.json +10 -0
  99. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/outputs/analysis.md +209 -0
  100. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/timing.json +5 -0
  101. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/grading.json +10 -0
  102. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/outputs/analysis.md +193 -0
  103. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/timing.json +5 -0
  104. package/skills/tfx-workspace/iteration-2/benchmark.json +62 -0
  105. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/eval_metadata.json +13 -0
  106. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/grading.json +11 -0
  107. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/outputs/analysis.md +382 -0
  108. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/timing.json +5 -0
  109. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/grading.json +11 -0
  110. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/outputs/analysis.md +333 -0
  111. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/timing.json +5 -0
  112. package/skills/tfx-workspace/iteration-2/review.html +1325 -0
  113. package/skills/tfx-workspace/skill-snapshot/tfx-auto/SKILL.md +217 -0
  114. package/skills/tfx-workspace/skill-snapshot/tfx-auto-codex/SKILL.md +77 -0
  115. package/skills/tfx-workspace/skill-snapshot/tfx-codex/SKILL.md +65 -0
  116. package/skills/tfx-workspace/skill-snapshot/tfx-doctor/SKILL.md +94 -0
  117. package/skills/tfx-workspace/skill-snapshot/tfx-gemini/SKILL.md +82 -0
  118. package/skills/tfx-workspace/skill-snapshot/tfx-hub/SKILL.md +133 -0
  119. package/skills/tfx-workspace/skill-snapshot/tfx-multi/SKILL.md +426 -0
  120. package/skills/tfx-workspace/skill-snapshot/tfx-setup/SKILL.md +101 -0
  121. package/.claude-plugin/marketplace.json +0 -34
  122. package/.claude-plugin/plugin.json +0 -22
  123. package/config/mcp-registry.json +0 -29
  124. package/tui/codex-profile.mjs +0 -402
  125. package/tui/core.mjs +0 -236
  126. package/tui/doctor.mjs +0 -328
  127. package/tui/gemini-profile.mjs +0 -254
  128. package/tui/monitor-data.mjs +0 -148
  129. package/tui/monitor.mjs +0 -295
  130. 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
+ }
@@ -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
  }