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,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
+ }
@@ -0,0 +1,74 @@
1
+ import {
2
+ createPsmuxSession,
3
+ killPsmuxSession,
4
+ psmuxSessionExists,
5
+ } from "./psmux.mjs";
6
+
7
+ /**
8
+ * @typedef {object} RuntimeStatus
9
+ * @property {string} name
10
+ * @property {string} sessionName
11
+ * @property {boolean} alive
12
+ */
13
+
14
+ /**
15
+ * @typedef {object} TeamRuntime
16
+ * @property {(sessionName: string, opts?: object) => unknown} start
17
+ * @property {(sessionName: string) => void} stop
18
+ * @property {(sessionName: string) => boolean} isAlive
19
+ * @property {(sessionName: string) => RuntimeStatus} getStatus
20
+ */
21
+
22
+ const defaultPsmuxAdapter = {
23
+ createSession: createPsmuxSession,
24
+ killSession: killPsmuxSession,
25
+ hasSession: psmuxSessionExists,
26
+ };
27
+
28
+ /**
29
+ * @param {{
30
+ * createSession: typeof createPsmuxSession,
31
+ * killSession: typeof killPsmuxSession,
32
+ * hasSession: typeof psmuxSessionExists,
33
+ * }} [adapter]
34
+ * @returns {TeamRuntime & { name: "psmux" }}
35
+ */
36
+ export function createPsmuxRuntime(adapter = defaultPsmuxAdapter) {
37
+ return {
38
+ name: "psmux",
39
+ start(sessionName, opts = {}) {
40
+ return adapter.createSession(sessionName, opts);
41
+ },
42
+ stop(sessionName) {
43
+ adapter.killSession(sessionName);
44
+ },
45
+ isAlive(sessionName) {
46
+ return adapter.hasSession(sessionName);
47
+ },
48
+ getStatus(sessionName) {
49
+ return {
50
+ name: "psmux",
51
+ sessionName,
52
+ alive: adapter.hasSession(sessionName),
53
+ };
54
+ },
55
+ };
56
+ }
57
+
58
+ /**
59
+ * @param {string} mode
60
+ * @returns {TeamRuntime & { name: string }}
61
+ */
62
+ export function createRuntime(mode) {
63
+ const normalizedMode = String(mode || "").trim().toLowerCase();
64
+
65
+ if (normalizedMode === "psmux") {
66
+ return createPsmuxRuntime();
67
+ }
68
+
69
+ if (normalizedMode === "native" || normalizedMode === "wt") {
70
+ throw new Error(`Runtime mode "${normalizedMode}" is not implemented yet.`);
71
+ }
72
+
73
+ throw new Error(`Unsupported runtime mode: ${mode}`);
74
+ }