triflux 10.9.21 → 10.9.23

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 (100) hide show
  1. package/.claude-plugin/marketplace.json +34 -0
  2. package/.claude-plugin/plugin.json +22 -0
  3. package/config/mcp-registry.json +29 -0
  4. package/hub/account-broker.mjs +6 -4
  5. package/hub/cli-adapter-base.mjs +14 -14
  6. package/hub/lib/env-detect.mjs +47 -20
  7. package/hub/server.mjs +17 -15
  8. package/hub/team/headless.mjs +10 -0
  9. package/hub/team/swarm-hypervisor.mjs +2 -2
  10. package/hub/workers/delegator-mcp.mjs +129 -1
  11. package/hud/constants.mjs +24 -13
  12. package/hud/renderers.mjs +2 -1
  13. package/package.json +62 -21
  14. package/scripts/__tests__/keyword-detector.test.mjs +4 -4
  15. package/scripts/__tests__/release-governance.test.mjs +148 -0
  16. package/scripts/doctor-diagnose.mjs +6 -7
  17. package/scripts/lib/cross-review-utils.mjs +2 -2
  18. package/scripts/lib/mcp-filter.mjs +12 -24
  19. package/scripts/release/bump-version.mjs +77 -0
  20. package/scripts/release/check-sync.mjs +51 -0
  21. package/scripts/release/lib.mjs +303 -0
  22. package/scripts/release/prepare.mjs +85 -0
  23. package/scripts/release/publish.mjs +87 -0
  24. package/scripts/release/verify.mjs +81 -0
  25. package/scripts/release/version-manifest.json +26 -0
  26. package/scripts/remote-spawn.mjs +3 -3
  27. package/scripts/setup.mjs +18 -15
  28. package/scripts/tfx-route.sh +64 -8
  29. package/tui/codex-profile.mjs +457 -0
  30. package/tui/core.mjs +266 -0
  31. package/tui/doctor.mjs +375 -0
  32. package/tui/gemini-profile.mjs +299 -0
  33. package/tui/monitor-data.mjs +152 -0
  34. package/tui/monitor.mjs +339 -0
  35. package/tui/setup.mjs +598 -0
  36. package/CLAUDE.md +0 -212
  37. package/references/hosts.json +0 -46
  38. package/skills/tfx-workspace/async-tests/run-tests.sh +0 -203
  39. package/skills/tfx-workspace/evals/evals.json +0 -79
  40. package/skills/tfx-workspace/iteration-1/benchmark.json +0 -524
  41. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/eval_metadata.json +0 -11
  42. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/grading.json +0 -25
  43. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/outputs/analysis.md +0 -154
  44. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/timing.json +0 -5
  45. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/grading.json +0 -25
  46. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/outputs/analysis.md +0 -126
  47. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/timing.json +0 -5
  48. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/eval_metadata.json +0 -11
  49. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/grading.json +0 -25
  50. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/outputs/analysis.md +0 -119
  51. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/timing.json +0 -5
  52. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/grading.json +0 -25
  53. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/outputs/analysis.md +0 -115
  54. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/timing.json +0 -5
  55. package/skills/tfx-workspace/iteration-1/hub-start-sequence/eval_metadata.json +0 -10
  56. package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/grading.json +0 -20
  57. package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/outputs/analysis.md +0 -86
  58. package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/timing.json +0 -5
  59. package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/grading.json +0 -20
  60. package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/outputs/analysis.md +0 -81
  61. package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/timing.json +0 -5
  62. package/skills/tfx-workspace/iteration-1/multi-team-creation/eval_metadata.json +0 -12
  63. package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/grading.json +0 -30
  64. package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/outputs/analysis.md +0 -316
  65. package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/timing.json +0 -5
  66. package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/grading.json +0 -30
  67. package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/outputs/analysis.md +0 -352
  68. package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/timing.json +0 -5
  69. package/skills/tfx-workspace/iteration-1/review.html +0 -1325
  70. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/eval_metadata.json +0 -12
  71. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/grading.json +0 -30
  72. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/outputs/analysis.md +0 -97
  73. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/timing.json +0 -5
  74. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/grading.json +0 -30
  75. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/outputs/analysis.md +0 -94
  76. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/timing.json +0 -5
  77. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/eval_metadata.json +0 -12
  78. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/grading.json +0 -30
  79. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/outputs/analysis.md +0 -209
  80. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/timing.json +0 -5
  81. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/grading.json +0 -30
  82. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/outputs/analysis.md +0 -193
  83. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/timing.json +0 -5
  84. package/skills/tfx-workspace/iteration-2/benchmark.json +0 -144
  85. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/eval_metadata.json +0 -13
  86. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/grading.json +0 -35
  87. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/outputs/analysis.md +0 -382
  88. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/timing.json +0 -5
  89. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/grading.json +0 -35
  90. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/outputs/analysis.md +0 -333
  91. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/timing.json +0 -5
  92. package/skills/tfx-workspace/iteration-2/review.html +0 -1325
  93. package/skills/tfx-workspace/skill-snapshot/tfx-auto/SKILL.md +0 -217
  94. package/skills/tfx-workspace/skill-snapshot/tfx-auto-codex/SKILL.md +0 -77
  95. package/skills/tfx-workspace/skill-snapshot/tfx-codex/SKILL.md +0 -65
  96. package/skills/tfx-workspace/skill-snapshot/tfx-doctor/SKILL.md +0 -94
  97. package/skills/tfx-workspace/skill-snapshot/tfx-gemini/SKILL.md +0 -82
  98. package/skills/tfx-workspace/skill-snapshot/tfx-hub/SKILL.md +0 -133
  99. package/skills/tfx-workspace/skill-snapshot/tfx-multi/SKILL.md +0 -426
  100. package/skills/tfx-workspace/skill-snapshot/tfx-setup/SKILL.md +0 -101
@@ -0,0 +1,339 @@
1
+ import { spawn } from "node:child_process";
2
+ import readline from "node:readline";
3
+
4
+ import { fetchHubStatus, pollAgents } from "./monitor-data.mjs";
5
+
6
+ const RESET = "\x1b[0m";
7
+ const DIM = "\x1b[2m";
8
+ const BOLD = "\x1b[1m";
9
+ const GREEN = "\x1b[32m";
10
+ const RED = "\x1b[31m";
11
+ const ORANGE = "\x1b[38;5;208m";
12
+ const BLUE = "\x1b[38;5;39m";
13
+ const WHITE = "\x1b[37m";
14
+ const GRAY = "\x1b[38;5;245m";
15
+ const FALLBACK_COLUMNS = 100;
16
+
17
+ function colorCli(cli) {
18
+ if (cli === "claude") return ORANGE;
19
+ if (cli === "gemini") return BLUE;
20
+ return WHITE;
21
+ }
22
+
23
+ function clamp(value, min, max) {
24
+ return Math.min(max, Math.max(min, value));
25
+ }
26
+
27
+ function pad(text, width) {
28
+ const value = String(text ?? "");
29
+ return value.length >= width
30
+ ? value
31
+ : value + " ".repeat(width - value.length);
32
+ }
33
+
34
+ function formatElapsed(ms) {
35
+ const total = Math.max(0, Math.floor((Number(ms) || 0) / 1000));
36
+ const hours = Math.floor(total / 3600);
37
+ const minutes = Math.floor((total % 3600) / 60);
38
+ const seconds = total % 60;
39
+ if (hours > 0)
40
+ return `${hours}:${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
41
+ return `${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
42
+ }
43
+
44
+ function progressBar(value, width = 16) {
45
+ const safe = clamp(Number(value) || 0, 0, 1);
46
+ const filled = Math.round(safe * width);
47
+ return `[${"█".repeat(filled)}${"░".repeat(Math.max(0, width - filled))}]`;
48
+ }
49
+
50
+ function escapePs(value) {
51
+ return String(value || "").replace(/'/g, "''");
52
+ }
53
+
54
+ function sanitizeTitle(value, fallback = "agent") {
55
+ const safe = String(value || "")
56
+ .replace(/[\r\n<>:"/\\|?*\x00-\x1f]/g, " ")
57
+ .trim();
58
+ return safe || fallback;
59
+ }
60
+
61
+ function stripUnsafeText(value) {
62
+ return String(value || "")
63
+ .replace(/[\r\n\t]+/g, " ")
64
+ .trim();
65
+ }
66
+
67
+ function buildOpenCommand(agent) {
68
+ const sessionName = escapePs(agent.agent || "");
69
+ const pid = Number(agent.pid) || 0;
70
+ const processInfo =
71
+ pid > 0
72
+ ? `Get-Process -Id ${pid} -ErrorAction SilentlyContinue | Format-List Id,ProcessName,StartTime`
73
+ : "Write-Host 'PID 정보가 없습니다.'";
74
+ return [
75
+ "$ErrorActionPreference = 'Continue'",
76
+ `if (Get-Command psmux -ErrorAction SilentlyContinue) { try { psmux attach-session -t '${sessionName}' } catch { Write-Host 'psmux attach 실패:' $_.Exception.Message } }`,
77
+ "else { Write-Host 'psmux 미설치 환경입니다.' }",
78
+ processInfo,
79
+ ].join("; ");
80
+ }
81
+
82
+ function resolveRatio(agent, maxElapsed) {
83
+ if (maxElapsed <= 0) return 1;
84
+ return clamp(agent.elapsed / maxElapsed, 0, 1);
85
+ }
86
+
87
+ export function createMonitor(opts = {}) {
88
+ const stream = opts.stream || process.stdout;
89
+ const input = opts.input || process.stdin;
90
+ const refreshMs = Number.isFinite(opts.refreshMs)
91
+ ? Math.max(0, opts.refreshMs)
92
+ : 1000;
93
+ const deps = {
94
+ pollAgents,
95
+ fetchHubStatus,
96
+ emitKeypressEvents: readline.emitKeypressEvents,
97
+ importModule: (specifier) => import(specifier),
98
+ setIntervalFn: setInterval,
99
+ clearIntervalFn: clearInterval,
100
+ spawn,
101
+ ...opts._deps,
102
+ };
103
+
104
+ let timer = null;
105
+ let started = false;
106
+ let agents = [];
107
+ let cursor = 0;
108
+ let helpVisible = false;
109
+ let hubStatus = { online: false };
110
+ let statusMessage = "";
111
+
112
+ const write = (chunk) => stream.write(String(chunk));
113
+
114
+ function viewportWidth() {
115
+ return Math.max(60, Number(stream?.columns) || FALLBACK_COLUMNS);
116
+ }
117
+
118
+ function syncCursor() {
119
+ cursor = agents.length === 0 ? 0 : clamp(cursor, 0, agents.length - 1);
120
+ }
121
+
122
+ async function openSelectedAgent() {
123
+ const agent = agents[cursor];
124
+ if (!agent) {
125
+ statusMessage = `${RED}선택된 에이전트가 없습니다.${RESET}`;
126
+ return false;
127
+ }
128
+
129
+ const title = sanitizeTitle(
130
+ `tfx ${agent.agent || agent.cli || agent.pid}`,
131
+ "tfx-agent",
132
+ );
133
+ const command = buildOpenCommand(agent);
134
+
135
+ try {
136
+ try {
137
+ const { createWtManager } = await deps.importModule(
138
+ "../hub/team/wt-manager.mjs",
139
+ );
140
+ const manager = createWtManager();
141
+ await manager.createTab({
142
+ title,
143
+ command,
144
+ cwd: process.cwd(),
145
+ profile: "triflux",
146
+ });
147
+ } catch {
148
+ const child = deps.spawn(
149
+ "wt.exe",
150
+ [
151
+ "-w",
152
+ "new",
153
+ "nt",
154
+ "--title",
155
+ title,
156
+ "--",
157
+ "powershell.exe",
158
+ "-NoExit",
159
+ "-Command",
160
+ command,
161
+ ],
162
+ {
163
+ detached: true,
164
+ stdio: "ignore",
165
+ windowsHide: false,
166
+ },
167
+ );
168
+ child?.unref?.();
169
+ }
170
+ statusMessage = `${GREEN}${stripUnsafeText(agent.agent || "agent")} 열기 시도 완료${RESET}`;
171
+ return true;
172
+ } catch (error) {
173
+ statusMessage = `${RED}열기 실패: ${stripUnsafeText(error?.message || "unknown error")}${RESET}`;
174
+ return false;
175
+ }
176
+ }
177
+
178
+ async function renderFrame() {
179
+ const [nextAgents, nextHubStatus] = await Promise.all([
180
+ Promise.resolve(deps.pollAgents()),
181
+ Promise.resolve(deps.fetchHubStatus(opts.hubUrl)),
182
+ ]);
183
+
184
+ agents = Array.isArray(nextAgents) ? nextAgents : [];
185
+ hubStatus =
186
+ nextHubStatus && typeof nextHubStatus === "object"
187
+ ? nextHubStatus
188
+ : { online: false };
189
+ syncCursor();
190
+
191
+ const width = viewportWidth();
192
+ const maxElapsed = agents.reduce(
193
+ (max, agent) => Math.max(max, Number(agent.elapsed) || 0),
194
+ 0,
195
+ );
196
+ const hubLabel = hubStatus.online
197
+ ? `${GREEN}online${RESET}`
198
+ : `${RED}offline${RESET}`;
199
+ const header = `${BOLD}triflux monitor${RESET} hub ${hubLabel}`;
200
+ const summary = hubStatus.online
201
+ ? `${DIM}queue ${hubStatus.queueDepth ?? "-"} · agents ${hubStatus.agents ?? agents.length}${RESET}`
202
+ : `${DIM}허브 연결 없음${RESET}`;
203
+
204
+ const lines = [pad(header, width), pad(summary, width), ""];
205
+
206
+ if (agents.length === 0) {
207
+ lines.push(`${DIM}에이전트 없음${RESET}`);
208
+ } else {
209
+ lines.push(`${BOLD}Agents${RESET}`);
210
+ for (const [index, agent] of agents.entries()) {
211
+ const selected = index === cursor;
212
+ const marker = selected ? `${GREEN}▶${RESET}` : " ";
213
+ const cli = stripUnsafeText(agent.cli || "unknown");
214
+ const name = stripUnsafeText(agent.agent || `pid:${agent.pid || "?"}`);
215
+ const elapsed = formatElapsed(agent.elapsed);
216
+ const alive = agent.alive
217
+ ? `${GREEN}alive${RESET}`
218
+ : `${RED}dead${RESET}`;
219
+ const left = `${marker} ${colorCli(cli)}${cli}${RESET} ${BOLD}${name}${RESET} ${GRAY}${elapsed}${RESET}`;
220
+ if (hubStatus.online) {
221
+ lines.push(
222
+ `${left} ${BLUE}${progressBar(resolveRatio(agent, maxElapsed))}${RESET}`,
223
+ );
224
+ } else {
225
+ lines.push(`${left} ${alive}`);
226
+ }
227
+ }
228
+ }
229
+
230
+ if (helpVisible) {
231
+ lines.push("", `${BOLD}Help${RESET}`);
232
+ lines.push(" j / ↓ : 아래 이동");
233
+ lines.push(" k / ↑ : 위로 이동");
234
+ lines.push(" Enter : 선택 에이전트 열기");
235
+ lines.push(" r : 즉시 새로고침");
236
+ lines.push(" h : 도움말 토글");
237
+ lines.push(" q / Ctrl+C : 종료");
238
+ }
239
+
240
+ if (statusMessage) lines.push("", statusMessage);
241
+ lines.push(
242
+ "",
243
+ `${DIM}[Enter] open [j/k] select [r] refresh [h] help [q] quit${RESET}`,
244
+ );
245
+
246
+ write("\x1b[H");
247
+ write(lines.join("\n"));
248
+ write("\x1b[J");
249
+ return { agents, hubStatus };
250
+ }
251
+
252
+ async function handleKey(str, key = {}) {
253
+ const name = key?.name || "";
254
+ if (str === "j" || name === "down") {
255
+ syncCursor();
256
+ cursor =
257
+ agents.length === 0 ? 0 : Math.min(cursor + 1, agents.length - 1);
258
+ await renderFrame();
259
+ return;
260
+ }
261
+ if (str === "k" || name === "up") {
262
+ syncCursor();
263
+ cursor = agents.length === 0 ? 0 : Math.max(cursor - 1, 0);
264
+ await renderFrame();
265
+ return;
266
+ }
267
+ if (name === "return" || name === "enter") {
268
+ await openSelectedAgent();
269
+ await renderFrame();
270
+ return;
271
+ }
272
+ if (str === "r") {
273
+ await renderFrame();
274
+ return;
275
+ }
276
+ if (str === "h") {
277
+ helpVisible = !helpVisible;
278
+ await renderFrame();
279
+ return;
280
+ }
281
+ if (str === "q" || (key?.ctrl && name === "c")) {
282
+ stop();
283
+ }
284
+ }
285
+
286
+ async function start() {
287
+ if (started) return;
288
+ started = true;
289
+ write("\x1b[?1049h");
290
+ write("\x1b[?25l");
291
+ if (typeof input?.setRawMode === "function") input.setRawMode(true);
292
+ deps.emitKeypressEvents(input);
293
+ if (typeof input?.resume === "function") input.resume();
294
+ input?.on?.("keypress", handleKey);
295
+ if (refreshMs > 0) {
296
+ timer = deps.setIntervalFn(() => {
297
+ void renderFrame();
298
+ }, refreshMs);
299
+ timer?.unref?.();
300
+ }
301
+ await renderFrame();
302
+ }
303
+
304
+ function stop() {
305
+ if (!started) return;
306
+ started = false;
307
+ if (timer) {
308
+ deps.clearIntervalFn(timer);
309
+ timer = null;
310
+ }
311
+ input?.removeListener?.("keypress", handleKey);
312
+ if (typeof input?.setRawMode === "function") input.setRawMode(false);
313
+ write("\x1b[?25h");
314
+ write("\x1b[?1049l");
315
+ }
316
+
317
+ function destroy() {
318
+ stop();
319
+ }
320
+
321
+ return {
322
+ start,
323
+ stop,
324
+ destroy,
325
+ renderFrame,
326
+ handleKey,
327
+ openSelectedAgent,
328
+ getState() {
329
+ return {
330
+ cursor,
331
+ helpVisible,
332
+ hubStatus: { ...hubStatus },
333
+ agents: agents.map((agent) => ({ ...agent })),
334
+ started,
335
+ statusMessage,
336
+ };
337
+ },
338
+ };
339
+ }