triflux 10.50.2 → 10.52.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 (263) hide show
  1. package/CLAUDE.md +57 -41
  2. package/README.ko.md +41 -45
  3. package/README.md +41 -45
  4. package/adapters/codex/skills/tfx-live/SKILL.md +25 -11
  5. package/bin/tfx-live.mjs +341 -104
  6. package/bin/triflux.mjs +147 -2624
  7. package/config/mcp-registry.json +3 -12
  8. package/cto/brief.mjs +0 -20
  9. package/cto/collect.mjs +1 -200
  10. package/cto/current.schema.json +2 -23
  11. package/cto/hygiene.mjs +2 -61
  12. package/cto/lake-root.mjs +0 -56
  13. package/cto/status.mjs +5 -187
  14. package/hooks/agy-session-hook.mjs +11 -56
  15. package/hooks/codex-session-hook.mjs +11 -101
  16. package/hub/bridge.mjs +65 -1250
  17. package/hub/cli-adapter-base.mjs +12 -90
  18. package/hub/codex-adapter.mjs +0 -1
  19. package/hub/codex-preflight.mjs +5 -28
  20. package/hub/lib/process-utils.mjs +1 -156
  21. package/hub/lib/spawn-trace.mjs +3 -2
  22. package/hub/lib/timeout-defaults.mjs +0 -1
  23. package/hub/team/agent-map.json +0 -1
  24. package/hub/team/agents-row-attach.py +120 -0
  25. package/hub/team/agents-row.mjs +200 -0
  26. package/hub/team/ansi.mjs +1 -35
  27. package/hub/team/backend.mjs +9 -145
  28. package/hub/team/claude-daemon-control.mjs +1 -2
  29. package/hub/team/claude-session-projection.mjs +5 -73
  30. package/hub/team/cli/commands/attach.mjs +1 -17
  31. package/hub/team/cli/commands/control.mjs +12 -38
  32. package/hub/team/cli/commands/debug.mjs +2 -39
  33. package/hub/team/cli/commands/focus.mjs +5 -31
  34. package/hub/team/cli/commands/interrupt.mjs +6 -25
  35. package/hub/team/cli/commands/kill.mjs +1 -30
  36. package/hub/team/cli/commands/list.mjs +0 -16
  37. package/hub/team/cli/commands/send.mjs +6 -26
  38. package/hub/team/cli/commands/start/index.mjs +42 -133
  39. package/hub/team/cli/commands/start/parse-args.mjs +15 -40
  40. package/hub/team/cli/commands/start/start-headless.mjs +138 -105
  41. package/hub/team/cli/commands/start/start-mux.mjs +2 -7
  42. package/hub/team/cli/commands/status.mjs +2 -57
  43. package/hub/team/cli/commands/stop.mjs +14 -19
  44. package/hub/team/cli/help.mjs +4 -5
  45. package/hub/team/cli/manifest.mjs +5 -1
  46. package/hub/team/cli/render.mjs +0 -12
  47. package/hub/team/cli/services/runtime-mode.mjs +27 -98
  48. package/hub/team/cli/services/state-store.mjs +5 -20
  49. package/hub/team/codex-queue.mjs +385 -0
  50. package/hub/team/handoff.mjs +3 -34
  51. package/hub/team/headless.mjs +108 -1296
  52. package/hub/team/orchestrator.mjs +4 -30
  53. package/hub/team/pane.mjs +6 -28
  54. package/hub/team/psmux.mjs +3 -34
  55. package/hub/team/remote-session.mjs +1 -2
  56. package/hub/team/session-context.mjs +1 -1
  57. package/hub/team/session.mjs +7 -91
  58. package/hub/team/tui-core.mjs +1 -1
  59. package/hub/team/tui-viewer.mjs +11 -58
  60. package/hub/team/tui.mjs +8 -306
  61. package/hub/team/uds-orchestrator.mjs +2 -580
  62. package/hub/team/wt-manager.mjs +2 -2
  63. package/hub/workers/factory.mjs +6 -84
  64. package/hub/workers/interface.mjs +1 -7
  65. package/hub/workers/lib/jsonrpc-core.mjs +11 -0
  66. package/hub/workers/lib/jsonrpc-ws-uds.mjs +5 -8
  67. package/hud/cli-policy.mjs +0 -22
  68. package/hud/constants.mjs +13 -26
  69. package/hud/hud-qos-status.mjs +42 -54
  70. package/hud/providers/gemini.mjs +125 -20
  71. package/hud/renderers.mjs +101 -122
  72. package/hud/utils.mjs +10 -6
  73. package/package.json +7 -12
  74. package/scripts/__tests__/mcp-guard-engine-http-headers.test.mjs +6 -6
  75. package/scripts/__tests__/mcp-guard-engine-policy-sync.test.mjs +1 -4
  76. package/scripts/__tests__/mcp-guard-engine-proactive-sync.test.mjs +16 -21
  77. package/scripts/__tests__/mcp-guard-engine-stdio-sync.test.mjs +1 -1
  78. package/scripts/__tests__/mcp-guard-engine-sync-http-headers.test.mjs +2 -2
  79. package/scripts/__tests__/mcp-guard-engine.test.mjs +56 -63
  80. package/scripts/__tests__/spawn-trace.test.mjs +16 -0
  81. package/scripts/check-codex-config-stable.mjs +4 -37
  82. package/scripts/claude-login-detect.mjs +0 -1
  83. package/scripts/claudemd-sync.mjs +1 -28
  84. package/scripts/lib/agent-route-policy.mjs +143 -62
  85. package/scripts/lib/cli-claude.mjs +0 -14
  86. package/scripts/lib/env-probe.mjs +2 -131
  87. package/scripts/lib/gemini-profiles.mjs +2 -69
  88. package/scripts/lib/legacy-mcp-cleanup.mjs +283 -0
  89. package/scripts/lib/machine-profile.mjs +3 -17
  90. package/scripts/lib/mcp-filter.mjs +109 -53
  91. package/scripts/lib/mcp-guard-engine.mjs +40 -98
  92. package/scripts/lib/mcp-health.mjs +2 -2
  93. package/scripts/lib/mcp-server-catalog.mjs +2 -2
  94. package/scripts/lib/psmux-info.mjs +11 -59
  95. package/scripts/lib/session-presence.mjs +2 -100
  96. package/scripts/lib/skill-template.mjs +0 -245
  97. package/scripts/pack.mjs +8 -83
  98. package/scripts/preflight-cache.mjs +5 -18
  99. package/scripts/release/check-packages-mirror.mjs +2 -8
  100. package/scripts/release/version-manifest.json +9 -1
  101. package/scripts/setup.mjs +100 -417
  102. package/scripts/test-lock.mjs +0 -1
  103. package/scripts/tfx-route-post.mjs +5 -69
  104. package/scripts/tfx-route-worker.mjs +4 -79
  105. package/scripts/tfx-route.sh +169 -1495
  106. package/scripts/wt-cli.mjs +1 -2
  107. package/skills/tfx-auto/SKILL.md +123 -741
  108. package/skills/tfx-auto/references/consensus-schema.md +161 -0
  109. package/skills/tfx-auto/references/tmux-observation.md +97 -0
  110. package/skills/tfx-doctor/SKILL.md +5 -6
  111. package/skills/tfx-lead/SKILL.md +195 -0
  112. package/skills/tfx-live/SKILL.md +25 -11
  113. package/skills/tfx-remote/SKILL.md +24 -87
  114. package/skills/tfx-research/SKILL.md +13 -20
  115. package/skills/tfx-review/SKILL.md +33 -35
  116. package/skills/tfx-setup/SKILL.md +1 -1
  117. package/skills/tfx-ship/SKILL.md +6 -27
  118. package/skills/tfx-wt/SKILL.md +34 -68
  119. package/bin/tfx-doctor-tui.mjs +0 -7
  120. package/bin/tfx-setup-tui.mjs +0 -7
  121. package/config/routing-policy.json +0 -124
  122. package/docs/assets/consensus-flow.svg +0 -125
  123. package/docs/assets/deep-vs-light.svg +0 -193
  124. package/docs/assets/demo-dark.gif +0 -0
  125. package/docs/assets/demo-light.gif +0 -0
  126. package/docs/assets/demo-psmux.gif +0 -0
  127. package/hub/adaptive-diagnostic.mjs +0 -350
  128. package/hub/adaptive-inject.mjs +0 -224
  129. package/hub/adaptive-memory.mjs +0 -277
  130. package/hub/adaptive.mjs +0 -172
  131. package/hub/assign-callbacks.mjs +0 -170
  132. package/hub/delegator/contracts.mjs +0 -38
  133. package/hub/delegator/index.mjs +0 -14
  134. package/hub/delegator/schema/delegator-tools.schema.json +0 -250
  135. package/hub/delegator/service.mjs +0 -336
  136. package/hub/delegator/tool-definitions.mjs +0 -35
  137. package/hub/dynamic-routing-engine.mjs +0 -511
  138. package/hub/hitl.mjs +0 -202
  139. package/hub/hub-lifecycle.mjs +0 -248
  140. package/hub/lib/known-errors.json +0 -72
  141. package/hub/lib/memory-store.mjs +0 -1001
  142. package/hub/lib/phase-manager.mjs +0 -493
  143. package/hub/lib/risk-tier.mjs +0 -53
  144. package/hub/lib/ssh-command.mjs +0 -231
  145. package/hub/lib/staged-file-detect.mjs +0 -43
  146. package/hub/lib/state-snapshot.mjs +0 -293
  147. package/hub/lib/tfx-route-args.mjs +0 -353
  148. package/hub/lib/trace-recorder.mjs +0 -153
  149. package/hub/lib/uuidv7.mjs +0 -45
  150. package/hub/memory-doctor.mjs +0 -640
  151. package/hub/middleware/quota-middleware.mjs +0 -70
  152. package/hub/middleware/request-logger.mjs +0 -191
  153. package/hub/paths.mjs +0 -24
  154. package/hub/pipe.mjs +0 -825
  155. package/hub/pipeline/index.mjs +0 -163
  156. package/hub/pipeline/state.mjs +0 -191
  157. package/hub/pipeline/transitions.mjs +0 -131
  158. package/hub/promote-penalties.mjs +0 -138
  159. package/hub/public/dashboard.html +0 -416
  160. package/hub/reflexion.mjs +0 -290
  161. package/hub/role-contract.mjs +0 -314
  162. package/hub/router.mjs +0 -2026
  163. package/hub/routing-snapshot.mjs +0 -261
  164. package/hub/schema.sql +0 -209
  165. package/hub/server.mjs +0 -3048
  166. package/hub/session-fingerprint.mjs +0 -418
  167. package/hub/state.mjs +0 -300
  168. package/hub/store-adapter.mjs +0 -182
  169. package/hub/store.mjs +0 -1736
  170. package/hub/team/build-worker-prompt.mjs +0 -92
  171. package/hub/team/check-mcp-hub.mjs +0 -49
  172. package/hub/team/claude-native-bridge.mjs +0 -987
  173. package/hub/team/cli/commands/start/start-in-process.mjs +0 -50
  174. package/hub/team/cli/commands/start/start-wt.mjs +0 -86
  175. package/hub/team/cli/services/hub-client.mjs +0 -264
  176. package/hub/team/cli/services/native-control.mjs +0 -145
  177. package/hub/team/codex-review.mjs +0 -520
  178. package/hub/team/conductor-mesh-bridge.mjs +0 -122
  179. package/hub/team/conductor-registry.mjs +0 -121
  180. package/hub/team/conductor.mjs +0 -1529
  181. package/hub/team/consensus-meta.mjs +0 -176
  182. package/hub/team/cto-auto-collect.mjs +0 -115
  183. package/hub/team/cto-pull.mjs +0 -31
  184. package/hub/team/daemon-pty-tmux-bridge.mjs +0 -146
  185. package/hub/team/dashboard.mjs +0 -322
  186. package/hub/team/event-log.mjs +0 -84
  187. package/hub/team/execution-mode.mjs +0 -324
  188. package/hub/team/extract-completion-payload.mjs +0 -110
  189. package/hub/team/headless-bridge-session.mjs +0 -17
  190. package/hub/team/health-probe.mjs +0 -435
  191. package/hub/team/interactive-native-launcher.mjs +0 -119
  192. package/hub/team/interactive-native-worker.mjs +0 -80
  193. package/hub/team/interactive-tui-transport.mjs +0 -479
  194. package/hub/team/launcher-template.mjs +0 -144
  195. package/hub/team/lead-control.mjs +0 -191
  196. package/hub/team/mcp-selector.mjs +0 -145
  197. package/hub/team/native-supervisor.mjs +0 -460
  198. package/hub/team/native.mjs +0 -727
  199. package/hub/team/recovery-store.mjs +0 -149
  200. package/hub/team/remote-probe.mjs +0 -292
  201. package/hub/team/remote-watcher.mjs +0 -506
  202. package/hub/team/runtime-strategy.mjs +0 -200
  203. package/hub/team/schemas/worker-completion.json +0 -39
  204. package/hub/team/sentinel-capture.mjs +0 -127
  205. package/hub/team/session-sync.mjs +0 -316
  206. package/hub/team/swarm-cli.mjs +0 -390
  207. package/hub/team/swarm-hypervisor.mjs +0 -2206
  208. package/hub/team/swarm-intent.mjs +0 -216
  209. package/hub/team/swarm-locks.mjs +0 -335
  210. package/hub/team/swarm-planner.mjs +0 -371
  211. package/hub/team/swarm-preflight.mjs +0 -366
  212. package/hub/team/swarm-reconciler.mjs +0 -242
  213. package/hub/team/synapse-cli.mjs +0 -458
  214. package/hub/team/synapse-http.mjs +0 -311
  215. package/hub/team/synapse-registry.mjs +0 -618
  216. package/hub/team/tui-remote-adapter.mjs +0 -377
  217. package/hub/team/tui-synapse.mjs +0 -223
  218. package/hub/team/worker-completion-validator.mjs +0 -79
  219. package/hub/team/worker-sandbox.mjs +0 -125
  220. package/hub/team/worktree-lifecycle.mjs +0 -639
  221. package/hub/team/wt-templates.json +0 -43
  222. package/hub/team-bridge.mjs +0 -27
  223. package/hub/tools.mjs +0 -900
  224. package/hub/workers/antigravity-route-worker.mjs +0 -179
  225. package/hub/workers/codex-app-server-worker.mjs +0 -1067
  226. package/hub/workers/delegator-mcp.mjs +0 -1098
  227. package/hub/workers/lib/jsonrpc-stdio.mjs +0 -204
  228. package/mesh/index.mjs +0 -69
  229. package/mesh/mesh-budget.mjs +0 -137
  230. package/mesh/mesh-heartbeat.mjs +0 -100
  231. package/mesh/mesh-protocol.mjs +0 -96
  232. package/mesh/mesh-queue.mjs +0 -163
  233. package/mesh/mesh-registry.mjs +0 -78
  234. package/mesh/mesh-router.mjs +0 -77
  235. package/scripts/__tests__/skill-surface.test.mjs +0 -93
  236. package/scripts/cache-doctor.mjs +0 -156
  237. package/scripts/cache-warmup.mjs +0 -630
  238. package/scripts/completions/tfx.bash +0 -74
  239. package/scripts/completions/tfx.fish +0 -57
  240. package/scripts/completions/tfx.zsh +0 -98
  241. package/scripts/demo.mjs +0 -197
  242. package/scripts/doctor-dynamic-routing.mjs +0 -87
  243. package/scripts/hub-ensure.mjs +0 -842
  244. package/scripts/hub-watchdog.mjs +0 -90
  245. package/scripts/lib/claudemd-scanner.mjs +0 -223
  246. package/scripts/lib/context.mjs +0 -67
  247. package/scripts/lib/dynamic-route-cli.mjs +0 -107
  248. package/scripts/lib/handoff.mjs +0 -187
  249. package/scripts/lib/logger.mjs +0 -122
  250. package/scripts/lib/stealth-fetch.mjs +0 -176
  251. package/scripts/notion-read.mjs +0 -686
  252. package/scripts/snapshot-codex-state.mjs +0 -37
  253. package/scripts/snapshot-gemini-state.mjs +0 -37
  254. package/scripts/sync-codex-auth.mjs +0 -71
  255. package/scripts/sync-hub-mcp-settings.mjs +0 -697
  256. package/skills/_templates/base.md +0 -4
  257. package/skills/_templates/deep.md +0 -6
  258. package/skills/shared/telemetry-segment.md +0 -6
  259. package/tui/core.mjs +0 -266
  260. package/tui/doctor.mjs +0 -375
  261. package/tui/monitor-data.mjs +0 -152
  262. package/tui/monitor.mjs +0 -318
  263. package/tui/setup.mjs +0 -506
@@ -1,390 +0,0 @@
1
- // hub/team/swarm-cli.mjs — `tfx swarm` CLI handlers (#93)
2
- // PRD를 planSwarm으로 분석하고 필요 시 createSwarmHypervisor로 실행한다.
3
-
4
- import { existsSync } from "node:fs";
5
- import { join, resolve } from "node:path";
6
- import { createSwarmHypervisor } from "./swarm-hypervisor.mjs";
7
- import { planSwarm } from "./swarm-planner.mjs";
8
- import {
9
- formatPreflightReport,
10
- runSwarmPreflight,
11
- } from "./swarm-preflight.mjs";
12
-
13
- const RESET = "\u001b[0m";
14
- const BOLD = "\u001b[1m";
15
- const GREEN = "\u001b[92m";
16
- const RED = "\u001b[91m";
17
- const YELLOW = "\u001b[93m";
18
- const GRAY = "\u001b[90m";
19
-
20
- /**
21
- * #116-C: non-TTY background 환경에서 `tfx swarm` 실행 시 codex worker spawn 이
22
- * 무한 hang 가능성이 있다 (stdin TTY 대기 또는 hub MCP lease race).
23
- *
24
- * Policy (v10.15+):
25
- * - stdout 또는 stdin 중 하나라도 TTY → silent OK (기존 동작).
26
- * - 양측 non-TTY → warning + 진행 (기본, 사용자 친화 + CI/background 호환).
27
- * - `TFX_BLOCK_NON_TTY_SWARM=1` opt-out → fail-fast + 복구 경로 안내 (안전 망).
28
- * - `TFX_ALLOW_NON_TTY_SWARM=1` 은 silent OK (호환 유지, warning suppress).
29
- *
30
- * 기존 fail-fast 정책은 첫 사용자에게 묻기 효과 (실제 user terminal 은 TTY 인데
31
- * Claude Code run_in_background 같은 spawn 환경에서 child stdio 는 non-TTY).
32
- * 다른 사용자도 동일 마찰 → 기본 동작을 "proceed with warning" 으로 변경.
33
- *
34
- * @param {{
35
- * stdoutIsTTY?: boolean,
36
- * stdinIsTTY?: boolean,
37
- * env?: Record<string,string|undefined>,
38
- * }} [deps]
39
- * @returns {{ ok: boolean, optIn: boolean, warnings: string[], reason?: string }}
40
- */
41
- export function assertTtyForSwarm(deps = {}) {
42
- const stdoutIsTTY =
43
- typeof deps.stdoutIsTTY === "boolean"
44
- ? deps.stdoutIsTTY
45
- : Boolean(process.stdout.isTTY);
46
- const stdinIsTTY =
47
- typeof deps.stdinIsTTY === "boolean"
48
- ? deps.stdinIsTTY
49
- : Boolean(process.stdin.isTTY);
50
- const env = deps.env || process.env;
51
- const warnings = [];
52
-
53
- if (stdoutIsTTY || stdinIsTTY) {
54
- return { ok: true, optIn: false, warnings };
55
- }
56
-
57
- // 양측 non-TTY 부터 적용되는 정책.
58
- if (env.TFX_BLOCK_NON_TTY_SWARM === "1") {
59
- const reason =
60
- "tfx swarm 이 차단됨 — non-TTY 환경 + TFX_BLOCK_NON_TTY_SWARM=1 (#116-C).\n" +
61
- " 복구 경로:\n" +
62
- " 1) 터미널에서 직접 실행: tfx swarm <prd>\n" +
63
- " 2) TFX_BLOCK_NON_TTY_SWARM=0 (또는 unset) 으로 차단 해제 후 재시도";
64
- return { ok: false, optIn: false, warnings, reason };
65
- }
66
-
67
- if (env.TFX_ALLOW_NON_TTY_SWARM === "1") {
68
- // 명시 opt-in — silent OK (기존 호환, warning 미출력).
69
- return { ok: true, optIn: true, warnings };
70
- }
71
-
72
- warnings.push(
73
- "non-TTY 환경 감지 — codex worker spawn hang 가능성 존재 (#116-C). 차단하려면 TFX_BLOCK_NON_TTY_SWARM=1.",
74
- );
75
- return { ok: true, optIn: true, warnings };
76
- }
77
-
78
- export function parseFlags(args) {
79
- const flags = {
80
- dryRun: false,
81
- planOnly: false,
82
- json: false,
83
- filter: null,
84
- maxRestarts: 2,
85
- logsDir: null,
86
- baseBranch: "main",
87
- nativeBridge: true,
88
- };
89
- const positional = [];
90
- let nativeBridgeUiEnabled = false;
91
- let nativeBridgeUiDisabled = false;
92
- for (let i = 0; i < args.length; i++) {
93
- const a = args[i];
94
- if (a === "--dry-run" || a === "--plan-only") flags.dryRun = true;
95
- else if (a === "--json") flags.json = true;
96
- else if (a === "--filter") flags.filter = args[++i];
97
- else if (a === "--max-restarts") flags.maxRestarts = Number(args[++i]) || 2;
98
- else if (a === "--logs-dir") flags.logsDir = args[++i];
99
- else if (a === "--base") {
100
- const v = args[++i];
101
- if (!v || /\s/.test(v)) {
102
- throw new Error(
103
- "--base requires a non-empty branch name without whitespace",
104
- );
105
- }
106
- flags.baseBranch = v;
107
- } else if (a === "--native-bridge-ui") {
108
- nativeBridgeUiEnabled = true;
109
- flags.nativeBridge = true;
110
- } else if (a === "--no-native-bridge-ui") {
111
- nativeBridgeUiDisabled = true;
112
- flags.nativeBridge = false;
113
- } else if (a.startsWith("--")) {
114
- // ignore unknown flags silently
115
- } else {
116
- positional.push(a);
117
- }
118
- }
119
- if (nativeBridgeUiEnabled && nativeBridgeUiDisabled) {
120
- throw new Error(
121
- "conflicting native bridge flags: --native-bridge-ui and --no-native-bridge-ui",
122
- );
123
- }
124
- return { flags, positional };
125
- }
126
-
127
- function planToJson(plan) {
128
- return {
129
- totalShards: plan.shards.length,
130
- shards: plan.shards.map((s) => ({
131
- name: s.name,
132
- agent: s.agent,
133
- host: s.host || null,
134
- files: s.files,
135
- depends: s.depends,
136
- critical: s.critical,
137
- })),
138
- leaseMap: Object.fromEntries(plan.leaseMap),
139
- mcpManifest: Object.fromEntries(plan.mcpManifest),
140
- mergeOrder: plan.mergeOrder,
141
- criticalShards: plan.criticalShards,
142
- conflicts: plan.conflicts,
143
- remoteSuggestion: plan.remoteSuggestion,
144
- };
145
- }
146
-
147
- function printPlan(plan) {
148
- console.log(`\n ${BOLD}Swarm plan${RESET}: ${plan.shards.length} shards`);
149
- for (const s of plan.shards) {
150
- const hostStr = s.host ? `@${s.host}` : "";
151
- const critStr = s.critical ? ` ${YELLOW}[critical]${RESET}` : "";
152
- const depsStr = s.depends.length > 0 ? ` ← ${s.depends.join(", ")}` : "";
153
- console.log(
154
- ` - ${BOLD}${s.name}${RESET} [${s.agent}${hostStr}] files=${s.files.length}${critStr}${depsStr}`,
155
- );
156
- }
157
- console.log(`\n ${GRAY}Merge order:${RESET} ${plan.mergeOrder.join(" → ")}`);
158
- if (plan.criticalShards.length > 0) {
159
- console.log(
160
- ` ${GRAY}Critical (redundant):${RESET} ${plan.criticalShards.join(", ")}`,
161
- );
162
- }
163
- if (plan.conflicts.length > 0) {
164
- console.log(`\n ${YELLOW}⚠ File conflicts:${RESET}`);
165
- for (const c of plan.conflicts) {
166
- console.log(` - ${c.file} → [${c.shards.join(", ")}]`);
167
- }
168
- }
169
- }
170
-
171
- function formatCell(value, width) {
172
- const text = value == null || value === "" ? "-" : String(value);
173
- return text.padEnd(width).slice(0, width);
174
- }
175
-
176
- export function formatIntegrationReport(report = []) {
177
- const rows = Array.isArray(report) ? report : [];
178
- const lines = [
179
- "=== INTEGRATION REPORT ===",
180
- "SHARD STRATEGY COMMITS FINAL_SHA NOTES",
181
- ];
182
-
183
- let integrated = 0;
184
- let recoveryPatches = 0;
185
- for (const row of rows) {
186
- if (row.strategy !== "failed") integrated += 1;
187
- if (row.recoveryPatch) recoveryPatches += 1;
188
- const finalSha = row.finalSha ? String(row.finalSha).slice(0, 7) : "-";
189
- const notes = row.recoveryPatch
190
- ? `${row.notes || "failed"}; recovery: ${row.recoveryPatch}`
191
- : row.notes || "-";
192
- lines.push(
193
- `${formatCell(row.shard, 9)} ${formatCell(row.strategy, 14)} ${formatCell(row.commits, 8)} ${formatCell(finalSha, 10)} ${notes}`,
194
- );
195
- }
196
-
197
- const total = rows.length;
198
- lines.push("");
199
- lines.push(
200
- `TOTAL: ${integrated}/${total} integrated, ${recoveryPatches} recovery patch${recoveryPatches === 1 ? "" : "es"} saved.`,
201
- );
202
- return lines.join("\n");
203
- }
204
-
205
- /**
206
- * tfx swarm <prd-path> — PRD 실행
207
- */
208
- export async function cmdSwarmRun(args, { json = false, deps = {} } = {}) {
209
- const { flags, positional } = parseFlags(args);
210
- flags.json = flags.json || json;
211
-
212
- const prdPath = positional[0];
213
- if (!prdPath) {
214
- throw new Error(
215
- "PRD path required. Usage: tfx swarm <prd-path> [--dry-run] [--filter <shard>] [--json] [--base <branch>]",
216
- );
217
- }
218
- const absPrd = resolve(prdPath);
219
- if (!existsSync(absPrd)) {
220
- throw new Error(`PRD file not found: ${absPrd}`);
221
- }
222
-
223
- if (flags.dryRun) {
224
- const plan = (deps.planSwarm || planSwarm)(absPrd, {
225
- repoRoot: process.cwd(),
226
- });
227
- if (flags.json) {
228
- process.stdout.write(JSON.stringify(planToJson(plan), null, 2) + "\n");
229
- } else {
230
- printPlan(plan);
231
- }
232
- return;
233
- }
234
-
235
- const preflight = (deps.runSwarmPreflight || runSwarmPreflight)(absPrd, {
236
- repoRoot: process.cwd(),
237
- deps,
238
- });
239
- if (!preflight.ok) {
240
- if (flags.json) {
241
- process.stdout.write(JSON.stringify(preflight, null, 2) + "\n");
242
- } else {
243
- process.stderr.write(formatPreflightReport(preflight));
244
- }
245
- process.exitCode = 1;
246
- return;
247
- }
248
-
249
- const plan = (deps.planSwarm || planSwarm)(absPrd, {
250
- repoRoot: process.cwd(),
251
- });
252
-
253
- const ttyGate = assertTtyForSwarm();
254
- for (const w of ttyGate.warnings) {
255
- console.error(` ${YELLOW}⚠${RESET} ${w}`);
256
- }
257
- if (!ttyGate.ok) {
258
- throw new Error(ttyGate.reason);
259
- }
260
-
261
- const logsDir =
262
- flags.logsDir ||
263
- join(process.cwd(), ".triflux", "swarm-logs", `run-${Date.now()}`);
264
- const hyper = (deps.createSwarmHypervisor || createSwarmHypervisor)({
265
- workdir: process.cwd(),
266
- logsDir,
267
- maxRestarts: flags.maxRestarts,
268
- baseBranch: flags.baseBranch,
269
- nativeBridge: flags.nativeBridge,
270
- });
271
-
272
- hyper.on("shardLaunched", ({ shardName, sessionId, remote }) => {
273
- const tag = remote ? ` ${GRAY}(remote)${RESET}` : "";
274
- console.log(
275
- ` ${GREEN}▸${RESET} launched: ${shardName}${tag} [${sessionId}]`,
276
- );
277
- });
278
- hyper.on("shardCompleted", ({ shardName, sessionId, isRedundant }) => {
279
- const tag = isRedundant ? ` ${GRAY}(redundant)${RESET}` : "";
280
- console.log(` ${GREEN}✓${RESET} ${shardName}${tag} [${sessionId}]`);
281
- });
282
- hyper.on("shardFailed", ({ shardName, failureMode, reason }) => {
283
- const reasonStr = reason ? ` ${GRAY}(${reason})${RESET}` : "";
284
- const modeStr = failureMode ? ` ${GRAY}[${failureMode}]${RESET}` : "";
285
- console.log(` ${RED}✗${RESET} ${shardName}${modeStr}${reasonStr}`);
286
- });
287
- hyper.on("warning", ({ type, ...rest }) => {
288
- console.error(
289
- ` ${YELLOW}⚠${RESET} ${type}: ${JSON.stringify(rest).slice(0, 200)}`,
290
- );
291
- });
292
-
293
- console.log(
294
- `\n ${BOLD}Launching swarm:${RESET} ${plan.shards.length} shards (logs: ${logsDir})`,
295
- );
296
- const run = await hyper.launch(plan);
297
-
298
- try {
299
- await run.done;
300
- } catch (err) {
301
- console.error(` ${RED}integration failed:${RESET} ${err.message}`);
302
- }
303
-
304
- const status = hyper.getStatus();
305
-
306
- // #126: surface integration outcome in summary + exit code so silent
307
- // success failures (worker exit 0 + integration_failed) can't mask state.
308
- const ip = status.integrationPromise || {};
309
- const integratedCount = Array.isArray(ip.integrated)
310
- ? ip.integrated.length
311
- : 0;
312
- const integrationFailureCount = Array.isArray(ip.integrationFailures)
313
- ? ip.integrationFailures.length
314
- : 0;
315
- const finalState = status.state || "unknown";
316
- const isFailure =
317
- status.failedShards > 0 ||
318
- integrationFailureCount > 0 ||
319
- finalState === "failed";
320
- const stateColor = isFailure ? RED : GREEN;
321
-
322
- console.log(
323
- `\n ${BOLD}Summary:${RESET} state=${stateColor}${finalState}${RESET} completed=${status.completedShards}/${status.totalShards} failed=${status.failedShards} integrated=${integratedCount} integration_failures=${integrationFailureCount}`,
324
- );
325
-
326
- if (integrationFailureCount > 0) {
327
- console.log(
328
- ` ${RED}integration failures:${RESET} ${ip.integrationFailures.join(", ")}`,
329
- );
330
- }
331
-
332
- if (Array.isArray(ip.report) && ip.report.length > 0) {
333
- console.log(`\n${formatIntegrationReport(ip.report)}`);
334
- }
335
-
336
- if (flags.json) {
337
- process.stdout.write(JSON.stringify(status, null, 2) + "\n");
338
- }
339
-
340
- await hyper.shutdown("completed");
341
- if (isFailure) process.exitCode = 1;
342
- }
343
-
344
- /**
345
- * tfx swarm plan <prd-path> — dry-run (실행 없이 계획만 출력)
346
- */
347
- export async function cmdSwarmPlan(args, { json = false } = {}) {
348
- return cmdSwarmRun(["--dry-run", ...args], { json });
349
- }
350
-
351
- /**
352
- * tfx swarm preflight <prd-path> — PRD 실행 전 no-go 리포트 출력
353
- */
354
- export async function cmdSwarmPreflight(
355
- args,
356
- { json = false, deps = {} } = {},
357
- ) {
358
- const { flags, positional } = parseFlags(args);
359
- flags.json = flags.json || json;
360
-
361
- const prdPath = positional[0];
362
- if (!prdPath) {
363
- throw new Error(
364
- "PRD path required. Usage: tfx swarm preflight <prd-path> [--json]",
365
- );
366
- }
367
- const absPrd = resolve(prdPath);
368
- if (!existsSync(absPrd)) {
369
- throw new Error(`PRD file not found: ${absPrd}`);
370
- }
371
-
372
- const report = (deps.runSwarmPreflight || runSwarmPreflight)(absPrd, {
373
- repoRoot: process.cwd(),
374
- deps,
375
- });
376
- if (flags.json) {
377
- process.stdout.write(JSON.stringify(report, null, 2) + "\n");
378
- } else {
379
- process.stdout.write(formatPreflightReport(report));
380
- }
381
- if (!report.ok) process.exitCode = 1;
382
- }
383
-
384
- /**
385
- * tfx swarm list — synapse status로 위임 (swarm 세션은 synapse registry에 등록)
386
- */
387
- export async function cmdSwarmList(args, { json = false } = {}) {
388
- const { cmdSynapseStatus } = await import("./synapse-cli.mjs");
389
- return cmdSynapseStatus(args, { json });
390
- }