triflux 10.3.4 → 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 (120) 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/router.mjs +791 -791
  10. package/hub/session-fingerprint.mjs +1 -1
  11. package/hub/team/cli/commands/attach.mjs +37 -37
  12. package/hub/team/cli/commands/debug.mjs +74 -74
  13. package/hub/team/cli/commands/focus.mjs +53 -53
  14. package/hub/team/cli/commands/list.mjs +24 -24
  15. package/hub/team/cli/commands/start/start-in-process.mjs +40 -40
  16. package/hub/team/cli/commands/start/start-mux.mjs +73 -73
  17. package/hub/team/cli/commands/start/start-wt.mjs +69 -69
  18. package/hub/team/cli/commands/tasks.mjs +13 -13
  19. package/hub/team/cli/render.mjs +30 -30
  20. package/hub/team/cli/services/attach-fallback.mjs +54 -54
  21. package/hub/team/cli/services/member-selector.mjs +30 -30
  22. package/hub/team/cli/services/native-control.mjs +116 -116
  23. package/hub/team/cli/services/task-model.mjs +30 -30
  24. package/hub/team/notify.mjs +1 -1
  25. package/hub/team/orchestrator.mjs +161 -161
  26. package/hub/team/session.mjs +611 -611
  27. package/hub/team/shared.mjs +13 -13
  28. package/hub/tray.mjs +368 -368
  29. package/hub/workers/codex-mcp.mjs +507 -507
  30. package/hub/workers/factory.mjs +21 -21
  31. package/package.json +21 -55
  32. package/references/hosts.json +33 -0
  33. package/scripts/completions/tfx.bash +47 -47
  34. package/scripts/completions/tfx.fish +44 -44
  35. package/scripts/completions/tfx.zsh +83 -83
  36. package/scripts/hub-ensure.mjs +120 -120
  37. package/scripts/keyword-detector.mjs +272 -272
  38. package/scripts/keyword-rules-expander.mjs +521 -521
  39. package/scripts/lib/mcp-server-catalog.mjs +118 -118
  40. package/scripts/notion-read.mjs +553 -553
  41. package/scripts/test-tfx-route-no-claude-native.mjs +57 -57
  42. package/scripts/tfx-batch-stats.mjs +96 -96
  43. package/skills/.omc/state/agent-replay-8f0e10a9-9693-4410-96f5-a6b07e8ed995.jsonl +1 -0
  44. package/skills/.omc/state/idle-notif-cooldown.json +3 -0
  45. package/skills/.omc/state/last-tool-error.json +7 -0
  46. package/skills/.omc/state/subagent-tracking.json +7 -0
  47. package/skills/tfx-remote-spawn/references/hosts.json +16 -0
  48. package/skills/tfx-workspace/async-tests/run-tests.sh +203 -0
  49. package/skills/tfx-workspace/evals/evals.json +79 -0
  50. package/skills/tfx-workspace/iteration-1/benchmark.json +162 -0
  51. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/eval_metadata.json +11 -0
  52. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/grading.json +9 -0
  53. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/outputs/analysis.md +154 -0
  54. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/timing.json +5 -0
  55. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/grading.json +9 -0
  56. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/outputs/analysis.md +126 -0
  57. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/timing.json +5 -0
  58. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/eval_metadata.json +11 -0
  59. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/grading.json +9 -0
  60. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/outputs/analysis.md +119 -0
  61. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/timing.json +5 -0
  62. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/grading.json +9 -0
  63. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/outputs/analysis.md +115 -0
  64. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/timing.json +5 -0
  65. package/skills/tfx-workspace/iteration-1/hub-start-sequence/eval_metadata.json +10 -0
  66. package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/grading.json +8 -0
  67. package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/outputs/analysis.md +86 -0
  68. package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/timing.json +5 -0
  69. package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/grading.json +8 -0
  70. package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/outputs/analysis.md +81 -0
  71. package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/timing.json +5 -0
  72. package/skills/tfx-workspace/iteration-1/multi-team-creation/eval_metadata.json +12 -0
  73. package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/grading.json +10 -0
  74. package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/outputs/analysis.md +316 -0
  75. package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/timing.json +5 -0
  76. package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/grading.json +10 -0
  77. package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/outputs/analysis.md +352 -0
  78. package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/timing.json +5 -0
  79. package/skills/tfx-workspace/iteration-1/review.html +1325 -0
  80. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/eval_metadata.json +12 -0
  81. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/grading.json +10 -0
  82. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/outputs/analysis.md +97 -0
  83. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/timing.json +5 -0
  84. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/grading.json +10 -0
  85. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/outputs/analysis.md +94 -0
  86. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/timing.json +5 -0
  87. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/eval_metadata.json +12 -0
  88. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/grading.json +10 -0
  89. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/outputs/analysis.md +209 -0
  90. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/timing.json +5 -0
  91. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/grading.json +10 -0
  92. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/outputs/analysis.md +193 -0
  93. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/timing.json +5 -0
  94. package/skills/tfx-workspace/iteration-2/benchmark.json +62 -0
  95. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/eval_metadata.json +13 -0
  96. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/grading.json +11 -0
  97. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/outputs/analysis.md +382 -0
  98. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/timing.json +5 -0
  99. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/grading.json +11 -0
  100. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/outputs/analysis.md +333 -0
  101. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/timing.json +5 -0
  102. package/skills/tfx-workspace/iteration-2/review.html +1325 -0
  103. package/skills/tfx-workspace/skill-snapshot/tfx-auto/SKILL.md +217 -0
  104. package/skills/tfx-workspace/skill-snapshot/tfx-auto-codex/SKILL.md +77 -0
  105. package/skills/tfx-workspace/skill-snapshot/tfx-codex/SKILL.md +65 -0
  106. package/skills/tfx-workspace/skill-snapshot/tfx-doctor/SKILL.md +94 -0
  107. package/skills/tfx-workspace/skill-snapshot/tfx-gemini/SKILL.md +82 -0
  108. package/skills/tfx-workspace/skill-snapshot/tfx-hub/SKILL.md +133 -0
  109. package/skills/tfx-workspace/skill-snapshot/tfx-multi/SKILL.md +426 -0
  110. package/skills/tfx-workspace/skill-snapshot/tfx-setup/SKILL.md +101 -0
  111. package/.claude-plugin/marketplace.json +0 -34
  112. package/.claude-plugin/plugin.json +0 -22
  113. package/config/mcp-registry.json +0 -29
  114. package/tui/codex-profile.mjs +0 -402
  115. package/tui/core.mjs +0 -236
  116. package/tui/doctor.mjs +0 -328
  117. package/tui/gemini-profile.mjs +0 -254
  118. package/tui/monitor-data.mjs +0 -148
  119. package/tui/monitor.mjs +0 -295
  120. package/tui/setup.mjs +0 -442
@@ -1,30 +1,30 @@
1
- export function buildTasks(subtasks, workers) {
2
- return subtasks.map((subtask, index) => ({
3
- id: `T${index + 1}`,
4
- title: subtask,
5
- owner: workers[index]?.name || null,
6
- status: "pending",
7
- depends_on: index === 0 ? [] : [`T${index}`],
8
- }));
9
- }
10
-
11
- export function normalizeTaskStatus(action) {
12
- const value = String(action || "").toLowerCase();
13
- if (value === "done" || value === "complete" || value === "completed") return "completed";
14
- if (value === "progress" || value === "in-progress" || value === "in_progress") return "in_progress";
15
- if (value === "pending") return "pending";
16
- return null;
17
- }
18
-
19
- export function updateTaskStatus(tasks = [], taskId, nextStatus) {
20
- const normalizedId = String(taskId || "").toUpperCase();
21
- const target = tasks.find((task) => String(task.id).toUpperCase() === normalizedId);
22
- if (!target) return { tasks, target: null };
23
-
24
- return {
25
- target: { ...target, status: nextStatus },
26
- tasks: tasks.map((task) => (
27
- String(task.id).toUpperCase() === normalizedId ? { ...task, status: nextStatus } : task
28
- )),
29
- };
30
- }
1
+ export function buildTasks(subtasks, workers) {
2
+ return subtasks.map((subtask, index) => ({
3
+ id: `T${index + 1}`,
4
+ title: subtask,
5
+ owner: workers[index]?.name || null,
6
+ status: "pending",
7
+ depends_on: index === 0 ? [] : [`T${index}`],
8
+ }));
9
+ }
10
+
11
+ export function normalizeTaskStatus(action) {
12
+ const value = String(action || "").toLowerCase();
13
+ if (value === "done" || value === "complete" || value === "completed") return "completed";
14
+ if (value === "progress" || value === "in-progress" || value === "in_progress") return "in_progress";
15
+ if (value === "pending") return "pending";
16
+ return null;
17
+ }
18
+
19
+ export function updateTaskStatus(tasks = [], taskId, nextStatus) {
20
+ const normalizedId = String(taskId || "").toUpperCase();
21
+ const target = tasks.find((task) => String(task.id).toUpperCase() === normalizedId);
22
+ if (!target) return { tasks, target: null };
23
+
24
+ return {
25
+ target: { ...target, status: nextStatus },
26
+ tasks: tasks.map((task) => (
27
+ String(task.id).toUpperCase() === normalizedId ? { ...task, status: nextStatus } : task
28
+ )),
29
+ };
30
+ }
@@ -290,4 +290,4 @@ export function createNotifier(opts = {}) {
290
290
  });
291
291
 
292
292
  return createNotifierInstance(normalizeChannels(opts.channels, env), deps);
293
- }
293
+ }
@@ -1,161 +1,161 @@
1
- // hub/team/orchestrator.mjs — 작업 분배 + 프롬프트 구성
2
- // 의존성: pane.mjs만 사용
3
- import { injectPrompt } from "./pane.mjs";
4
-
5
- /**
6
- * 작업 분해 (LLM 없이 구분자 기반)
7
- * @param {string} taskDescription — 전체 작업 설명
8
- * @param {number} agentCount — 에이전트 수
9
- * @returns {string[]} 각 에이전트의 서브태스크
10
- */
11
- export function decomposeTask(taskDescription, agentCount) {
12
- if (agentCount <= 0) return [];
13
- if (agentCount === 1) return [taskDescription];
14
-
15
- // '+', ',', '\n' 기준으로 분리
16
- const parts = taskDescription
17
- .split(/[+,\n]+/)
18
- .map((s) => s.trim())
19
- .filter(Boolean);
20
-
21
- if (parts.length === 0) return [taskDescription];
22
-
23
- // 에이전트보다 서브태스크가 적으면 마지막 에이전트에 전체 태스크 부여
24
- if (parts.length < agentCount) {
25
- const result = [...parts];
26
- while (result.length < agentCount) {
27
- result.push(taskDescription);
28
- }
29
- return result;
30
- }
31
-
32
- // 에이전트보다 서브태스크가 많으면 앞에서부터 N개, 나머지는 마지막에 합침
33
- if (parts.length > agentCount) {
34
- const result = parts.slice(0, agentCount - 1);
35
- result.push(parts.slice(agentCount - 1).join(" + "));
36
- return result;
37
- }
38
-
39
- return parts;
40
- }
41
-
42
- /**
43
- * 리드(보통 claude) 초기 프롬프트 생성
44
- * @param {string} taskDescription
45
- * @param {object} config
46
- * @param {string} config.agentId
47
- * @param {string} config.hubUrl
48
- * @param {string} config.teammateMode
49
- * @param {Array<{agentId:string, cli:string, subtask:string}>} config.workers
50
- * @returns {string}
51
- */
52
- export function buildLeadPrompt(taskDescription, config) {
53
- const { agentId, teammateMode = "tmux", workers = [] } = config;
54
-
55
- const roster = workers
56
- .map((w, i) => `${i + 1}. ${w.agentId} (${w.cli}) — ${w.subtask}`)
57
- .join("\n") || "- (워커 없음)";
58
-
59
- const workerIds = workers.map((w) => w.agentId).join(", ");
60
-
61
- const bridgePath = "node hub/bridge.mjs";
62
-
63
- return `리드 에이전트: ${agentId}
64
-
65
- 목표: ${taskDescription}
66
- 모드: ${teammateMode}
67
-
68
- 워커:
69
- ${roster}
70
-
71
- 규칙:
72
- - 가능한 짧고 핵심만 지시/요약(토큰 절약)
73
- - 워커 제어:
74
- ${bridgePath} result --agent ${agentId} --topic lead.control
75
- - 워커 결과 수집:
76
- ${bridgePath} context --agent ${agentId} --max 20
77
- - 최종 결과는 topic="task.result"를 모아 통합
78
-
79
- 워커 ID: ${workerIds || "(없음)"}
80
- 지금 즉시 워커를 배정하고 병렬 진행을 관리하라.`;
81
- }
82
-
83
- /**
84
- * 워커 초기 프롬프트 생성
85
- * @param {string} subtask — 이 에이전트의 서브태스크
86
- * @param {object} config
87
- * @param {string} config.cli — codex/gemini/claude
88
- * @param {string} config.agentId — 에이전트 식별자
89
- * @param {string} config.hubUrl — Hub URL
90
- * @returns {string}
91
- */
92
- export function buildPrompt(subtask, config) {
93
- const { cli, agentId, hubUrl } = config;
94
-
95
- const _hubBase = hubUrl.replace("/mcp", "");
96
-
97
- const bridgePath = "node hub/bridge.mjs";
98
-
99
- return `워커: ${agentId} (${cli})
100
- 작업: ${subtask}
101
-
102
- 필수 규칙:
103
- 1) 간결하게 작업(불필요한 장문 설명 금지)
104
- 2) 시작 즉시 등록:
105
- ${bridgePath} register --agent ${agentId} --cli ${cli} --topics lead.control,task.result
106
- 3) 주기적으로 수신함 확인:
107
- ${bridgePath} context --agent ${agentId} --max 10
108
- 4) lead.control 수신 시 즉시 반응 (interrupt/stop/pause/resume)
109
- 5) 완료 시 결과 발행:
110
- ${bridgePath} result --agent ${agentId} --topic task.result --file <출력파일>
111
-
112
- 지금 작업을 시작하라.`;
113
- }
114
-
115
- /**
116
- * 팀 오케스트레이션 실행 — 각 pane에 프롬프트 주입
117
- * @param {string} sessionName — tmux 세션 이름
118
- * @param {Array<{target: string, cli: string, subtask: string}>} assignments
119
- * @param {object} opts
120
- * @param {string} opts.hubUrl — Hub URL
121
- * @param {{target:string, cli:string, task:string}|null} opts.lead
122
- * @param {string} opts.teammateMode
123
- * @returns {Promise<void>}
124
- */
125
- export async function orchestrate(sessionName, assignments, opts = {}) {
126
- const {
127
- hubUrl = "http://127.0.0.1:27888/mcp",
128
- lead = null,
129
- teammateMode = "tmux",
130
- } = opts;
131
-
132
- const workers = assignments.map(({ target, cli, subtask }) => ({
133
- target,
134
- cli,
135
- subtask,
136
- agentId: `${cli}-${target.split(".").pop()}`,
137
- }));
138
-
139
- if (lead?.target) {
140
- const leadAgentId = `${lead.cli || "claude"}-${lead.target.split(".").pop()}`;
141
- const leadPrompt = buildLeadPrompt(lead.task || "팀 작업 조율", {
142
- agentId: leadAgentId,
143
- hubUrl,
144
- teammateMode,
145
- workers: workers.map((w) => ({ agentId: w.agentId, cli: w.cli, subtask: w.subtask })),
146
- });
147
- injectPrompt(lead.target, leadPrompt, { useFileRef: true });
148
- await new Promise((r) => setTimeout(r, 100));
149
- }
150
-
151
- for (const worker of workers) {
152
- const prompt = buildPrompt(worker.subtask, {
153
- cli: worker.cli,
154
- agentId: worker.agentId,
155
- hubUrl,
156
- sessionName,
157
- });
158
- injectPrompt(worker.target, prompt, { useFileRef: true });
159
- await new Promise((r) => setTimeout(r, 100));
160
- }
161
- }
1
+ // hub/team/orchestrator.mjs — 작업 분배 + 프롬프트 구성
2
+ // 의존성: pane.mjs만 사용
3
+ import { injectPrompt } from "./pane.mjs";
4
+
5
+ /**
6
+ * 작업 분해 (LLM 없이 구분자 기반)
7
+ * @param {string} taskDescription — 전체 작업 설명
8
+ * @param {number} agentCount — 에이전트 수
9
+ * @returns {string[]} 각 에이전트의 서브태스크
10
+ */
11
+ export function decomposeTask(taskDescription, agentCount) {
12
+ if (agentCount <= 0) return [];
13
+ if (agentCount === 1) return [taskDescription];
14
+
15
+ // '+', ',', '\n' 기준으로 분리
16
+ const parts = taskDescription
17
+ .split(/[+,\n]+/)
18
+ .map((s) => s.trim())
19
+ .filter(Boolean);
20
+
21
+ if (parts.length === 0) return [taskDescription];
22
+
23
+ // 에이전트보다 서브태스크가 적으면 마지막 에이전트에 전체 태스크 부여
24
+ if (parts.length < agentCount) {
25
+ const result = [...parts];
26
+ while (result.length < agentCount) {
27
+ result.push(taskDescription);
28
+ }
29
+ return result;
30
+ }
31
+
32
+ // 에이전트보다 서브태스크가 많으면 앞에서부터 N개, 나머지는 마지막에 합침
33
+ if (parts.length > agentCount) {
34
+ const result = parts.slice(0, agentCount - 1);
35
+ result.push(parts.slice(agentCount - 1).join(" + "));
36
+ return result;
37
+ }
38
+
39
+ return parts;
40
+ }
41
+
42
+ /**
43
+ * 리드(보통 claude) 초기 프롬프트 생성
44
+ * @param {string} taskDescription
45
+ * @param {object} config
46
+ * @param {string} config.agentId
47
+ * @param {string} config.hubUrl
48
+ * @param {string} config.teammateMode
49
+ * @param {Array<{agentId:string, cli:string, subtask:string}>} config.workers
50
+ * @returns {string}
51
+ */
52
+ export function buildLeadPrompt(taskDescription, config) {
53
+ const { agentId, teammateMode = "tmux", workers = [] } = config;
54
+
55
+ const roster = workers
56
+ .map((w, i) => `${i + 1}. ${w.agentId} (${w.cli}) — ${w.subtask}`)
57
+ .join("\n") || "- (워커 없음)";
58
+
59
+ const workerIds = workers.map((w) => w.agentId).join(", ");
60
+
61
+ const bridgePath = "node hub/bridge.mjs";
62
+
63
+ return `리드 에이전트: ${agentId}
64
+
65
+ 목표: ${taskDescription}
66
+ 모드: ${teammateMode}
67
+
68
+ 워커:
69
+ ${roster}
70
+
71
+ 규칙:
72
+ - 가능한 짧고 핵심만 지시/요약(토큰 절약)
73
+ - 워커 제어:
74
+ ${bridgePath} result --agent ${agentId} --topic lead.control
75
+ - 워커 결과 수집:
76
+ ${bridgePath} context --agent ${agentId} --max 20
77
+ - 최종 결과는 topic="task.result"를 모아 통합
78
+
79
+ 워커 ID: ${workerIds || "(없음)"}
80
+ 지금 즉시 워커를 배정하고 병렬 진행을 관리하라.`;
81
+ }
82
+
83
+ /**
84
+ * 워커 초기 프롬프트 생성
85
+ * @param {string} subtask — 이 에이전트의 서브태스크
86
+ * @param {object} config
87
+ * @param {string} config.cli — codex/gemini/claude
88
+ * @param {string} config.agentId — 에이전트 식별자
89
+ * @param {string} config.hubUrl — Hub URL
90
+ * @returns {string}
91
+ */
92
+ export function buildPrompt(subtask, config) {
93
+ const { cli, agentId, hubUrl } = config;
94
+
95
+ const _hubBase = hubUrl.replace("/mcp", "");
96
+
97
+ const bridgePath = "node hub/bridge.mjs";
98
+
99
+ return `워커: ${agentId} (${cli})
100
+ 작업: ${subtask}
101
+
102
+ 필수 규칙:
103
+ 1) 간결하게 작업(불필요한 장문 설명 금지)
104
+ 2) 시작 즉시 등록:
105
+ ${bridgePath} register --agent ${agentId} --cli ${cli} --topics lead.control,task.result
106
+ 3) 주기적으로 수신함 확인:
107
+ ${bridgePath} context --agent ${agentId} --max 10
108
+ 4) lead.control 수신 시 즉시 반응 (interrupt/stop/pause/resume)
109
+ 5) 완료 시 결과 발행:
110
+ ${bridgePath} result --agent ${agentId} --topic task.result --file <출력파일>
111
+
112
+ 지금 작업을 시작하라.`;
113
+ }
114
+
115
+ /**
116
+ * 팀 오케스트레이션 실행 — 각 pane에 프롬프트 주입
117
+ * @param {string} sessionName — tmux 세션 이름
118
+ * @param {Array<{target: string, cli: string, subtask: string}>} assignments
119
+ * @param {object} opts
120
+ * @param {string} opts.hubUrl — Hub URL
121
+ * @param {{target:string, cli:string, task:string}|null} opts.lead
122
+ * @param {string} opts.teammateMode
123
+ * @returns {Promise<void>}
124
+ */
125
+ export async function orchestrate(sessionName, assignments, opts = {}) {
126
+ const {
127
+ hubUrl = "http://127.0.0.1:27888/mcp",
128
+ lead = null,
129
+ teammateMode = "tmux",
130
+ } = opts;
131
+
132
+ const workers = assignments.map(({ target, cli, subtask }) => ({
133
+ target,
134
+ cli,
135
+ subtask,
136
+ agentId: `${cli}-${target.split(".").pop()}`,
137
+ }));
138
+
139
+ if (lead?.target) {
140
+ const leadAgentId = `${lead.cli || "claude"}-${lead.target.split(".").pop()}`;
141
+ const leadPrompt = buildLeadPrompt(lead.task || "팀 작업 조율", {
142
+ agentId: leadAgentId,
143
+ hubUrl,
144
+ teammateMode,
145
+ workers: workers.map((w) => ({ agentId: w.agentId, cli: w.cli, subtask: w.subtask })),
146
+ });
147
+ injectPrompt(lead.target, leadPrompt, { useFileRef: true });
148
+ await new Promise((r) => setTimeout(r, 100));
149
+ }
150
+
151
+ for (const worker of workers) {
152
+ const prompt = buildPrompt(worker.subtask, {
153
+ cli: worker.cli,
154
+ agentId: worker.agentId,
155
+ hubUrl,
156
+ sessionName,
157
+ });
158
+ injectPrompt(worker.target, prompt, { useFileRef: true });
159
+ await new Promise((r) => setTimeout(r, 100));
160
+ }
161
+ }