triflux 10.3.4 → 10.7.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 (329) hide show
  1. package/LICENSE +21 -21
  2. package/bin/tfx-doctor-tui.mjs +1 -1
  3. package/bin/tfx-doctor.mjs +6 -1
  4. package/bin/tfx-profile.mjs +1 -1
  5. package/bin/tfx-setup-tui.mjs +1 -1
  6. package/bin/tfx-setup.mjs +6 -1
  7. package/bin/triflux.mjs +2396 -1140
  8. package/hooks/agent-route-guard.mjs +12 -8
  9. package/hooks/cross-review-tracker.mjs +21 -8
  10. package/hooks/error-context.mjs +19 -7
  11. package/hooks/hook-adaptive-collector.mjs +18 -16
  12. package/hooks/hook-manager.mjs +93 -32
  13. package/hooks/hook-orchestrator.mjs +108 -24
  14. package/hooks/hook-registry.json +11 -0
  15. package/hooks/keyword-rules.json +6 -10
  16. package/hooks/lib/resolve-root.mjs +1 -1
  17. package/hooks/mcp-config-watcher.mjs +6 -2
  18. package/hooks/pipeline-stop.mjs +3 -6
  19. package/hooks/safety-guard.mjs +99 -28
  20. package/hooks/session-start-fast.mjs +143 -0
  21. package/hooks/subagent-verifier.mjs +5 -4
  22. package/hub/account-broker.mjs +256 -60
  23. package/hub/adaptive-diagnostic.mjs +75 -48
  24. package/hub/adaptive-inject.mjs +95 -57
  25. package/hub/adaptive-memory.mjs +156 -42
  26. package/hub/adaptive.mjs +60 -31
  27. package/hub/assign-callbacks.mjs +67 -30
  28. package/hub/bridge.mjs +0 -1
  29. package/hub/cli-adapter-base.mjs +200 -48
  30. package/hub/codex-adapter.mjs +76 -96
  31. package/hub/codex-compat.mjs +3 -3
  32. package/hub/codex-preflight.mjs +63 -37
  33. package/hub/delegator/contracts.mjs +19 -23
  34. package/hub/delegator/index.mjs +3 -3
  35. package/hub/delegator/service.mjs +88 -64
  36. package/hub/delegator/tool-definitions.mjs +5 -5
  37. package/hub/fullcycle.mjs +33 -17
  38. package/hub/gemini-adapter.mjs +69 -94
  39. package/hub/hitl.mjs +89 -30
  40. package/hub/intent.mjs +161 -38
  41. package/hub/lib/cache-guard.mjs +43 -17
  42. package/hub/lib/mcp-response-cache.mjs +66 -32
  43. package/hub/lib/memory-store.mjs +285 -111
  44. package/hub/lib/path-utils.mjs +35 -37
  45. package/hub/lib/process-utils.mjs +106 -37
  46. package/hub/lib/spawn-trace.mjs +527 -0
  47. package/hub/lib/ssh-command.mjs +34 -4
  48. package/hub/lib/ssh-retry.mjs +5 -1
  49. package/hub/lib/uuidv7.mjs +4 -3
  50. package/hub/memory-doctor.mjs +266 -106
  51. package/hub/middleware/request-logger.mjs +61 -34
  52. package/hub/paths.mjs +9 -9
  53. package/hub/pipeline/gates/confidence.mjs +34 -15
  54. package/hub/pipeline/gates/consensus.mjs +27 -15
  55. package/hub/pipeline/gates/index.mjs +7 -3
  56. package/hub/pipeline/gates/selfcheck.mjs +57 -19
  57. package/hub/pipeline/index.mjs +77 -42
  58. package/hub/pipeline/state.mjs +10 -10
  59. package/hub/pipeline/transitions.mjs +40 -23
  60. package/hub/platform.mjs +57 -48
  61. package/hub/promote-penalties.mjs +25 -7
  62. package/hub/quality/deslop.mjs +70 -49
  63. package/hub/research.mjs +32 -25
  64. package/hub/router.mjs +240 -107
  65. package/hub/routing/complexity.mjs +132 -29
  66. package/hub/routing/index.mjs +17 -12
  67. package/hub/routing/q-learning.mjs +76 -28
  68. package/hub/server.mjs +4 -4
  69. package/hub/session-fingerprint.mjs +126 -60
  70. package/hub/state.mjs +84 -43
  71. package/hub/store-adapter.mjs +59 -26
  72. package/hub/store.mjs +356 -153
  73. package/hub/team/agent-map.json +22 -7
  74. package/hub/team/ansi.mjs +186 -122
  75. package/hub/team/backend.mjs +28 -10
  76. package/hub/team/cli/commands/attach.mjs +29 -9
  77. package/hub/team/cli/commands/control.mjs +29 -8
  78. package/hub/team/cli/commands/debug.mjs +32 -11
  79. package/hub/team/cli/commands/focus.mjs +38 -11
  80. package/hub/team/cli/commands/interrupt.mjs +18 -6
  81. package/hub/team/cli/commands/kill.mjs +16 -5
  82. package/hub/team/cli/commands/list.mjs +11 -4
  83. package/hub/team/cli/commands/send.mjs +19 -6
  84. package/hub/team/cli/commands/start/index.mjs +154 -31
  85. package/hub/team/cli/commands/start/parse-args.mjs +38 -11
  86. package/hub/team/cli/commands/start/start-headless.mjs +112 -36
  87. package/hub/team/cli/commands/start/start-in-process.mjs +12 -2
  88. package/hub/team/cli/commands/start/start-mux.mjs +70 -21
  89. package/hub/team/cli/commands/start/start-wt.mjs +29 -12
  90. package/hub/team/cli/commands/status.mjs +43 -14
  91. package/hub/team/cli/commands/stop.mjs +11 -4
  92. package/hub/team/cli/commands/task.mjs +8 -3
  93. package/hub/team/cli/commands/tasks.mjs +1 -1
  94. package/hub/team/cli/index.mjs +2 -2
  95. package/hub/team/cli/manifest.mjs +38 -8
  96. package/hub/team/cli/render.mjs +30 -8
  97. package/hub/team/cli/services/attach-fallback.mjs +31 -11
  98. package/hub/team/cli/services/hub-client.mjs +42 -14
  99. package/hub/team/cli/services/member-selector.mjs +11 -4
  100. package/hub/team/cli/services/native-control.mjs +48 -21
  101. package/hub/team/cli/services/runtime-mode.mjs +2 -1
  102. package/hub/team/cli/services/state-store.mjs +25 -8
  103. package/hub/team/cli/services/task-model.mjs +16 -6
  104. package/hub/team/conductor-mesh-bridge.mjs +24 -23
  105. package/hub/team/conductor.mjs +8 -4
  106. package/hub/team/dashboard-anchor.mjs +4 -5
  107. package/hub/team/dashboard-layout.mjs +3 -1
  108. package/hub/team/dashboard-open.mjs +41 -21
  109. package/hub/team/dashboard.mjs +76 -28
  110. package/hub/team/event-log.mjs +18 -10
  111. package/hub/team/handoff.mjs +31 -15
  112. package/hub/team/headless.mjs +2 -1
  113. package/hub/team/health-probe.mjs +69 -54
  114. package/hub/team/launcher-template.mjs +16 -13
  115. package/hub/team/native-supervisor.mjs +65 -21
  116. package/hub/team/native.mjs +74 -35
  117. package/hub/team/nativeProxy.mjs +184 -113
  118. package/hub/team/notify.mjs +119 -76
  119. package/hub/team/orchestrator.mjs +9 -4
  120. package/hub/team/pane.mjs +12 -7
  121. package/hub/team/process-cleanup.mjs +25 -16
  122. package/hub/team/psmux.mjs +491 -201
  123. package/hub/team/remote-probe.mjs +68 -52
  124. package/hub/team/remote-session.mjs +117 -59
  125. package/hub/team/remote-watcher.mjs +61 -33
  126. package/hub/team/routing.mjs +51 -25
  127. package/hub/team/runtime-strategy.mjs +3 -1
  128. package/hub/team/session.mjs +98 -34
  129. package/hub/team/staleState.mjs +72 -30
  130. package/hub/team/swarm-locks.mjs +15 -13
  131. package/hub/team/swarm-planner.mjs +32 -21
  132. package/hub/team/swarm-reconciler.mjs +48 -23
  133. package/hub/team/tui-lite.mjs +266 -68
  134. package/hub/team/tui-remote-adapter.mjs +14 -10
  135. package/hub/team/tui-viewer.mjs +99 -43
  136. package/hub/team/tui.mjs +708 -271
  137. package/hub/team/worktree-lifecycle.mjs +152 -58
  138. package/hub/team/wt-manager.mjs +24 -14
  139. package/hub/token-mode.mjs +71 -71
  140. package/hub/tray.mjs +66 -23
  141. package/hub/workers/claude-worker.mjs +162 -118
  142. package/hub/workers/codex-mcp.mjs +192 -141
  143. package/hub/workers/delegator-mcp.mjs +507 -333
  144. package/hub/workers/factory.mjs +8 -8
  145. package/hub/workers/gemini-worker.mjs +115 -84
  146. package/hub/workers/interface.mjs +6 -1
  147. package/hub/workers/worker-utils.mjs +21 -14
  148. package/hud/colors.mjs +27 -9
  149. package/hud/constants.mjs +162 -26
  150. package/hud/context-monitor.mjs +82 -41
  151. package/hud/hud-qos-status.mjs +129 -49
  152. package/hud/mission-board.mjs +6 -3
  153. package/hud/providers/claude.mjs +226 -115
  154. package/hud/providers/codex.mjs +62 -22
  155. package/hud/providers/gemini.mjs +168 -56
  156. package/hud/renderers.mjs +384 -119
  157. package/hud/terminal.mjs +101 -31
  158. package/hud/utils.mjs +78 -38
  159. package/mesh/index.mjs +11 -5
  160. package/mesh/mesh-budget.mjs +18 -9
  161. package/mesh/mesh-heartbeat.mjs +1 -1
  162. package/mesh/mesh-queue.mjs +3 -5
  163. package/mesh/mesh-router.mjs +5 -4
  164. package/package.json +2 -1
  165. package/scripts/__tests__/gen-skill-docs.test.mjs +36 -7
  166. package/scripts/__tests__/keyword-detector.test.mjs +77 -28
  167. package/scripts/__tests__/mcp-guard-engine.test.mjs +58 -20
  168. package/scripts/__tests__/remote-spawn-transfer.test.mjs +30 -19
  169. package/scripts/__tests__/remote-spawn.test.mjs +10 -4
  170. package/scripts/__tests__/session-start-fast.test.mjs +36 -0
  171. package/scripts/__tests__/skill-template.test.mjs +98 -50
  172. package/scripts/__tests__/smoke.test.mjs +1 -1
  173. package/scripts/__tests__/spawn-trace.test.mjs +102 -0
  174. package/scripts/__tests__/tfx-doctor-diagnose.test.mjs +48 -0
  175. package/scripts/cache-doctor.mjs +11 -4
  176. package/scripts/cache-warmup.mjs +96 -37
  177. package/scripts/claudemd-sync.mjs +27 -17
  178. package/scripts/codex-gateway-preflight.mjs +52 -37
  179. package/scripts/codex-mcp-gateway-sync.mjs +59 -39
  180. package/scripts/completions/tfx.bash +47 -47
  181. package/scripts/completions/tfx.fish +44 -44
  182. package/scripts/completions/tfx.zsh +83 -83
  183. package/scripts/config-audit.mjs +232 -0
  184. package/scripts/convert-to-tmpl.mjs +54 -0
  185. package/scripts/cross-review-gate.mjs +35 -12
  186. package/scripts/cross-review-tracker.mjs +21 -8
  187. package/scripts/demo.mjs +35 -17
  188. package/scripts/doctor-diagnose.mjs +284 -0
  189. package/scripts/gen-skill-docs.mjs +7 -2
  190. package/scripts/gen-skill-manifest.mjs +2 -1
  191. package/scripts/headless-guard.mjs +86 -48
  192. package/scripts/hub-ensure.mjs +45 -26
  193. package/scripts/keyword-detector.mjs +41 -20
  194. package/scripts/keyword-rules-expander.mjs +47 -30
  195. package/scripts/lib/claudemd-scanner.mjs +6 -1
  196. package/scripts/lib/context.mjs +3 -3
  197. package/scripts/lib/cross-review-utils.mjs +6 -3
  198. package/scripts/lib/env-probe.mjs +47 -28
  199. package/scripts/lib/gemini-profiles.mjs +44 -10
  200. package/scripts/lib/handoff.mjs +33 -17
  201. package/scripts/lib/hook-utils.mjs +8 -6
  202. package/scripts/lib/keyword-rules.mjs +43 -19
  203. package/scripts/lib/logger.mjs +24 -24
  204. package/scripts/lib/mcp-filter.mjs +377 -239
  205. package/scripts/lib/mcp-guard-engine.mjs +194 -79
  206. package/scripts/lib/mcp-manifest.mjs +23 -13
  207. package/scripts/lib/mcp-server-catalog.mjs +300 -63
  208. package/scripts/lib/psmux-info.mjs +11 -6
  209. package/scripts/lib/remote-spawn-transfer.mjs +44 -14
  210. package/scripts/lib/skill-template.mjs +30 -7
  211. package/scripts/mcp-check.mjs +58 -39
  212. package/scripts/mcp-gateway-config.mjs +83 -39
  213. package/scripts/mcp-gateway-ensure.mjs +43 -35
  214. package/scripts/mcp-gateway-integration-test.mjs +70 -58
  215. package/scripts/mcp-gateway-start.mjs +126 -60
  216. package/scripts/mcp-gateway-verify.mjs +24 -22
  217. package/scripts/mcp-safety-guard.mjs +44 -11
  218. package/scripts/notion-read.mjs +199 -84
  219. package/scripts/pack.mjs +94 -89
  220. package/scripts/preflight-cache.mjs +27 -10
  221. package/scripts/preinstall.mjs +42 -13
  222. package/scripts/remote-spawn.mjs +309 -94
  223. package/scripts/run.cjs +8 -5
  224. package/scripts/session-spawn-helper.mjs +130 -39
  225. package/scripts/session-stale-cleanup.mjs +123 -0
  226. package/scripts/setup.mjs +941 -492
  227. package/scripts/test-lock.mjs +20 -7
  228. package/scripts/test-tfx-route-no-claude-native.mjs +16 -12
  229. package/scripts/tfx-batch-stats.mjs +32 -11
  230. package/scripts/tfx-gate-activate.mjs +11 -4
  231. package/scripts/tfx-route-post.mjs +87 -20
  232. package/scripts/tfx-route-worker.mjs +57 -51
  233. package/scripts/tfx-route.sh +41 -124
  234. package/scripts/tmp-cleanup.mjs +21 -7
  235. package/scripts/token-snapshot.mjs +204 -85
  236. package/skills/.omc/state/agent-replay-8f0e10a9-9693-4410-96f5-a6b07e8ed995.jsonl +1 -0
  237. package/skills/.omc/state/idle-notif-cooldown.json +3 -0
  238. package/skills/.omc/state/last-tool-error.json +7 -0
  239. package/skills/.omc/state/subagent-tracking.json +7 -0
  240. package/skills/_templates/base.md +1 -6
  241. package/skills/merge-worktree/SKILL.md.tmpl +144 -0
  242. package/skills/shared/telemetry-segment.md +6 -0
  243. package/skills/star-prompt/SKILL.md.tmpl +222 -0
  244. package/skills/tfx-analysis/SKILL.md.tmpl +107 -0
  245. package/skills/tfx-analysis/skill.json +1 -6
  246. package/skills/tfx-auto/SKILL.md +1 -0
  247. package/skills/tfx-auto-codex/SKILL.md.tmpl +106 -0
  248. package/skills/tfx-auto-codex/skill.json +1 -3
  249. package/skills/tfx-autopilot/SKILL.md.tmpl +116 -0
  250. package/skills/tfx-autopilot/skill.json +1 -5
  251. package/skills/tfx-autoresearch/SKILL.md.tmpl +136 -0
  252. package/skills/tfx-autoroute/SKILL.md.tmpl +189 -0
  253. package/skills/tfx-autoroute/skill.json +1 -7
  254. package/skills/tfx-codex/SKILL.md +1 -0
  255. package/skills/tfx-codex/skill.json +1 -3
  256. package/skills/tfx-codex-swarm/SKILL.md.tmpl +16 -0
  257. package/skills/tfx-codex-swarm/evals/evals.json +1 -1
  258. package/skills/tfx-codex-swarm/skill.json +1 -4
  259. package/skills/tfx-codex-swarm-workspace/iteration-1/benchmark.json +54 -12
  260. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/grading.json +35 -7
  261. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/grading.json +35 -7
  262. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/grading.json +25 -5
  263. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/grading.json +25 -5
  264. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/grading.json +20 -4
  265. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/grading.json +16 -4
  266. package/skills/tfx-consensus/SKILL.md.tmpl +146 -0
  267. package/skills/tfx-debate/SKILL.md.tmpl +192 -0
  268. package/skills/tfx-debate/skill.json +1 -7
  269. package/skills/tfx-deep-analysis/SKILL.md.tmpl +228 -0
  270. package/skills/tfx-deep-analysis/skill.json +1 -5
  271. package/skills/tfx-deep-interview/SKILL.md.tmpl +203 -0
  272. package/skills/tfx-deep-plan/SKILL.md.tmpl +282 -0
  273. package/skills/tfx-deep-qa/SKILL.md.tmpl +165 -0
  274. package/skills/tfx-deep-qa/skill.json +1 -6
  275. package/skills/tfx-deep-research/SKILL.md.tmpl +217 -0
  276. package/skills/tfx-deep-review/SKILL.md.tmpl +179 -0
  277. package/skills/tfx-doctor/SKILL.md +21 -0
  278. package/skills/tfx-doctor/SKILL.md.tmpl +172 -0
  279. package/skills/tfx-doctor/skill.json +1 -3
  280. package/skills/tfx-find/SKILL.md +1 -0
  281. package/skills/tfx-forge/SKILL.md.tmpl +187 -0
  282. package/skills/tfx-fullcycle/SKILL.md.tmpl +286 -0
  283. package/skills/tfx-fullcycle/skill.json +1 -6
  284. package/skills/tfx-gemini/SKILL.md.tmpl +91 -0
  285. package/skills/tfx-gemini/skill.json +1 -3
  286. package/skills/tfx-hooks/SKILL.md.tmpl +216 -0
  287. package/skills/tfx-hooks/skill.json +1 -3
  288. package/skills/tfx-hub/SKILL.md.tmpl +212 -0
  289. package/skills/tfx-hub/skill.json +1 -3
  290. package/skills/tfx-index/SKILL.md +1 -0
  291. package/skills/tfx-index/skill.json +1 -6
  292. package/skills/tfx-interview/SKILL.md.tmpl +285 -0
  293. package/skills/tfx-multi/SKILL.md.tmpl +183 -0
  294. package/skills/tfx-multi/skill.json +1 -3
  295. package/skills/tfx-panel/SKILL.md.tmpl +189 -0
  296. package/skills/tfx-panel/skill.json +1 -7
  297. package/skills/tfx-persist/SKILL.md.tmpl +270 -0
  298. package/skills/tfx-persist/skill.json +1 -7
  299. package/skills/tfx-plan/SKILL.md +1 -0
  300. package/skills/tfx-plan/skill.json +1 -6
  301. package/skills/tfx-profile/SKILL.md.tmpl +239 -0
  302. package/skills/tfx-profile/skill.json +1 -3
  303. package/skills/tfx-prune/SKILL.md.tmpl +200 -0
  304. package/skills/tfx-prune/skill.json +1 -7
  305. package/skills/tfx-psmux-rules/SKILL.md.tmpl +326 -0
  306. package/skills/tfx-psmux-rules/skill.json +1 -4
  307. package/skills/tfx-qa/SKILL.md +1 -0
  308. package/skills/tfx-qa/skill.json +1 -6
  309. package/skills/tfx-ralph/SKILL.md.tmpl +28 -0
  310. package/skills/tfx-ralph/skill.json +1 -4
  311. package/skills/tfx-remote-setup/SKILL.md.tmpl +576 -0
  312. package/skills/tfx-remote-setup/skill.json +1 -3
  313. package/skills/tfx-remote-spawn/SKILL.md.tmpl +263 -0
  314. package/skills/tfx-remote-spawn/references/hosts.json +16 -0
  315. package/skills/tfx-remote-spawn/skill.json +1 -4
  316. package/skills/tfx-research/SKILL.md +1 -0
  317. package/skills/tfx-review/SKILL.md +1 -0
  318. package/skills/tfx-review/skill.json +1 -6
  319. package/skills/tfx-setup/SKILL.md.tmpl +504 -0
  320. package/skills/tfx-setup/skill.json +1 -3
  321. package/skills/tfx-swarm/SKILL.md +22 -0
  322. package/skills/tfx-swarm/SKILL.md.tmpl +218 -0
  323. package/tui/codex-profile.mjs +88 -33
  324. package/tui/core.mjs +45 -15
  325. package/tui/doctor.mjs +75 -28
  326. package/tui/gemini-profile.mjs +74 -29
  327. package/tui/monitor-data.mjs +8 -4
  328. package/tui/monitor.mjs +71 -27
  329. package/tui/setup.mjs +133 -42
@@ -1,13 +1,19 @@
1
1
  // hub/team/staleState.mjs
2
2
  // .omc/state 아래에 남은 stale team 상태를 탐지/정리한다.
3
3
 
4
- import { existsSync, readFileSync, readdirSync, rmSync, statSync, unlinkSync } from "node:fs";
5
4
  import { execFileSync } from "node:child_process";
6
- import { dirname, join, resolve } from "node:path";
5
+ import {
6
+ existsSync,
7
+ readdirSync,
8
+ readFileSync,
9
+ rmSync,
10
+ statSync,
11
+ unlinkSync,
12
+ } from "node:fs";
7
13
  import { homedir } from "node:os";
8
-
9
- import { forceCleanupTeam } from "./nativeProxy.mjs";
14
+ import { dirname, join, resolve } from "node:path";
10
15
  import { isPidAlive } from "../lib/process-utils.mjs";
16
+ import { forceCleanupTeam } from "./nativeProxy.mjs";
11
17
 
12
18
  export const TEAM_STATE_FILE_NAME = "team-state.json";
13
19
  export const STALE_TEAM_MAX_AGE_MS = 60 * 60 * 1000;
@@ -99,7 +105,9 @@ function normalizeProcessEntries(processEntries = []) {
99
105
 
100
106
  return processEntries.map((entry) => ({
101
107
  pid: Number(entry?.pid ?? entry?.ProcessId ?? 0),
102
- command: String(entry?.command ?? entry?.CommandLine ?? entry?.Name ?? "").toLowerCase(),
108
+ command: String(
109
+ entry?.command ?? entry?.CommandLine ?? entry?.Name ?? "",
110
+ ).toLowerCase(),
103
111
  }));
104
112
  }
105
113
 
@@ -166,9 +174,11 @@ function resolveLiveness(state, sessionId, liveSessionNames, processEntries) {
166
174
 
167
175
  const processTokens = findProcessTokens(state, sessionId);
168
176
  if (processTokens.length > 0) {
169
- const matched = processEntries.find((entry) => (
170
- entry.pid > 0 && processTokens.some((token) => entry.command.includes(token))
171
- ));
177
+ const matched = processEntries.find(
178
+ (entry) =>
179
+ entry.pid > 0 &&
180
+ processTokens.some((token) => entry.command.includes(token)),
181
+ );
172
182
  if (matched) {
173
183
  return { active: true, reason: `command:${matched.pid}` };
174
184
  }
@@ -258,14 +268,23 @@ export function findNearestOmcStateDir(startDir = process.cwd()) {
258
268
  }
259
269
 
260
270
  export function inspectStaleOmcTeams(options = {}) {
261
- const stateRoot = options.stateRoot !== undefined ? options.stateRoot : findNearestOmcStateDir(options.startDir || process.cwd());
271
+ const stateRoot =
272
+ options.stateRoot !== undefined
273
+ ? options.stateRoot
274
+ : findNearestOmcStateDir(options.startDir || process.cwd());
262
275
  const requestedTeamsRoot = options.teamsRoot || CLAUDE_TEAMS_ROOT;
263
- const teamsRoot = safeStat(requestedTeamsRoot)?.isDirectory() ? requestedTeamsRoot : null;
276
+ const teamsRoot = safeStat(requestedTeamsRoot)?.isDirectory()
277
+ ? requestedTeamsRoot
278
+ : null;
264
279
 
265
280
  const liveSessionNames = new Set(options.liveSessionNames || []);
266
- const processEntries = normalizeProcessEntries(options.processEntries || readProcessEntries());
281
+ const processEntries = normalizeProcessEntries(
282
+ options.processEntries || readProcessEntries(),
283
+ );
267
284
  const nowMs = Number.isFinite(options.nowMs) ? options.nowMs : Date.now();
268
- const maxAgeMs = Number.isFinite(options.maxAgeMs) ? options.maxAgeMs : STALE_TEAM_MAX_AGE_MS;
285
+ const maxAgeMs = Number.isFinite(options.maxAgeMs)
286
+ ? options.maxAgeMs
287
+ : STALE_TEAM_MAX_AGE_MS;
269
288
  const targets = [
270
289
  ...(stateRoot ? collectTeamStateTargets(stateRoot) : []),
271
290
  ...collectClaudeTeamTargets(teamsRoot),
@@ -291,25 +310,48 @@ export function inspectStaleOmcTeams(options = {}) {
291
310
 
292
311
  const fileStat = safeStat(target.stateFile);
293
312
  const teamDirStat = target.teamDir ? safeStat(target.teamDir) : null;
294
- const createdAtMs = Number.isFinite(state?.createdAt) ? state.createdAt : null;
295
- const startedAtMs = parseStartedAtMs(state?.started_at)
296
- ?? parseStartedAtMs(state?.startedAt)
297
- ?? createdAtMs
298
- ?? fileStat?.mtimeMs
299
- ?? teamDirStat?.mtimeMs
300
- ?? null;
313
+ const createdAtMs = Number.isFinite(state?.createdAt)
314
+ ? state.createdAt
315
+ : null;
316
+ const startedAtMs =
317
+ parseStartedAtMs(state?.started_at) ??
318
+ parseStartedAtMs(state?.startedAt) ??
319
+ createdAtMs ??
320
+ fileStat?.mtimeMs ??
321
+ teamDirStat?.mtimeMs ??
322
+ null;
301
323
  const ageMs = startedAtMs == null ? null : Math.max(0, nowMs - startedAtMs);
302
- const teamName = state?.teamName || state?.team_name || state?.native?.teamName || state?.name || target.teamName || null;
303
- const livenessState = target.scope === "claude_team"
304
- ? {
305
- ...(state || {}),
306
- name: teamName,
307
- teamName,
308
- sessionName: state?.leadSessionId || state?.lead_session_id || state?.sessionName || target.sessionId,
309
- sessionId: state?.leadSessionId || state?.lead_session_id || state?.sessionId || target.sessionId,
310
- }
311
- : state;
312
- const liveness = resolveLiveness(livenessState, target.sessionId, liveSessionNames, processEntries);
324
+ const teamName =
325
+ state?.teamName ||
326
+ state?.team_name ||
327
+ state?.native?.teamName ||
328
+ state?.name ||
329
+ target.teamName ||
330
+ null;
331
+ const livenessState =
332
+ target.scope === "claude_team"
333
+ ? {
334
+ ...(state || {}),
335
+ name: teamName,
336
+ teamName,
337
+ sessionName:
338
+ state?.leadSessionId ||
339
+ state?.lead_session_id ||
340
+ state?.sessionName ||
341
+ target.sessionId,
342
+ sessionId:
343
+ state?.leadSessionId ||
344
+ state?.lead_session_id ||
345
+ state?.sessionId ||
346
+ target.sessionId,
347
+ }
348
+ : state;
349
+ const liveness = resolveLiveness(
350
+ livenessState,
351
+ target.sessionId,
352
+ liveSessionNames,
353
+ processEntries,
354
+ );
313
355
  const stale = ageMs != null && ageMs >= maxAgeMs && !liveness.active;
314
356
 
315
357
  entries.push({
@@ -3,8 +3,8 @@
3
3
  // Lock state is kept in-memory (single-process hypervisor) with optional
4
4
  // JSON persistence to .triflux/swarm-locks.json for crash recovery.
5
5
 
6
- import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';
7
- import { dirname, resolve, relative } from 'node:path';
6
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
7
+ import { dirname, relative, resolve } from "node:path";
8
8
 
9
9
  const LOCK_TTL_MS = 10 * 60_000; // 10 minutes default TTL
10
10
 
@@ -17,11 +17,7 @@ const LOCK_TTL_MS = 10 * 60_000; // 10 minutes default TTL
17
17
  * @returns {SwarmLockManager}
18
18
  */
19
19
  export function createSwarmLocks(opts = {}) {
20
- const {
21
- repoRoot = process.cwd(),
22
- persistPath,
23
- ttlMs = LOCK_TTL_MS,
24
- } = opts;
20
+ const { repoRoot = process.cwd(), persistPath, ttlMs = LOCK_TTL_MS } = opts;
25
21
 
26
22
  /** @type {Map<string, LockEntry>} normalized relative path → lock */
27
23
  const locks = new Map();
@@ -30,7 +26,7 @@ export function createSwarmLocks(opts = {}) {
30
26
 
31
27
  function normalizePath(filePath) {
32
28
  const abs = resolve(repoRoot, filePath);
33
- return relative(repoRoot, abs).replace(/\\/g, '/');
29
+ return relative(repoRoot, abs).replace(/\\/g, "/");
34
30
  }
35
31
 
36
32
  function now() {
@@ -56,21 +52,25 @@ export function createSwarmLocks(opts = {}) {
56
52
  const data = Object.fromEntries(
57
53
  [...locks].map(([k, v]) => [k, { ...v }]),
58
54
  );
59
- writeFileSync(persistPath, JSON.stringify(data, null, 2), 'utf8');
60
- } catch { /* best-effort */ }
55
+ writeFileSync(persistPath, JSON.stringify(data, null, 2), "utf8");
56
+ } catch {
57
+ /* best-effort */
58
+ }
61
59
  }
62
60
 
63
61
  function restore() {
64
62
  if (!persistPath || !existsSync(persistPath)) return;
65
63
  try {
66
- const data = JSON.parse(readFileSync(persistPath, 'utf8'));
64
+ const data = JSON.parse(readFileSync(persistPath, "utf8"));
67
65
  const ts = now();
68
66
  for (const [path, entry] of Object.entries(data)) {
69
67
  if (ts - entry.acquiredAt <= ttlMs) {
70
68
  locks.set(path, entry);
71
69
  }
72
70
  }
73
- } catch { /* corrupted file — start fresh */ }
71
+ } catch {
72
+ /* corrupted file — start fresh */
73
+ }
74
74
  }
75
75
 
76
76
  // restore on creation
@@ -199,6 +199,8 @@ export function createSwarmLocks(opts = {}) {
199
199
  validateChanges,
200
200
  snapshot,
201
201
  releaseAll,
202
- get size() { return locks.size; },
202
+ get size() {
203
+ return locks.size;
204
+ },
203
205
  });
204
206
  }
@@ -14,17 +14,17 @@
14
14
  // - prompt: |
15
15
  // multi-line prompt text
16
16
 
17
- import { readFileSync } from 'node:fs';
17
+ import { readFileSync } from "node:fs";
18
18
 
19
19
  /** Shard schema defaults */
20
20
  const SHARD_DEFAULTS = Object.freeze({
21
- agent: 'codex',
21
+ agent: "codex",
22
22
  files: [],
23
23
  mcp: [],
24
24
  depends: [],
25
25
  critical: false,
26
- prompt: '',
27
- host: '',
26
+ prompt: "",
27
+ host: "",
28
28
  });
29
29
 
30
30
  /**
@@ -41,7 +41,7 @@ export function parseShards(content) {
41
41
 
42
42
  function flushPrompt() {
43
43
  if (current && promptLines.length > 0) {
44
- current.prompt = promptLines.join('\n').trim();
44
+ current.prompt = promptLines.join("\n").trim();
45
45
  promptLines = [];
46
46
  }
47
47
  inPrompt = false;
@@ -91,26 +91,35 @@ export function parseShards(content) {
91
91
  const value = rawValue.trim();
92
92
 
93
93
  switch (key.toLowerCase()) {
94
- case 'agent':
94
+ case "agent":
95
95
  current.agent = value.toLowerCase();
96
96
  break;
97
- case 'files':
98
- current.files = value.split(/,\s*/).map((f) => f.trim()).filter(Boolean);
97
+ case "files":
98
+ current.files = value
99
+ .split(/,\s*/)
100
+ .map((f) => f.trim())
101
+ .filter(Boolean);
99
102
  break;
100
- case 'mcp':
101
- current.mcp = value.split(/,\s*/).map((s) => s.trim()).filter(Boolean);
103
+ case "mcp":
104
+ current.mcp = value
105
+ .split(/,\s*/)
106
+ .map((s) => s.trim())
107
+ .filter(Boolean);
102
108
  break;
103
- case 'depends':
104
- current.depends = value.split(/,\s*/).map((d) => d.trim()).filter(Boolean);
109
+ case "depends":
110
+ current.depends = value
111
+ .split(/,\s*/)
112
+ .map((d) => d.trim())
113
+ .filter(Boolean);
105
114
  break;
106
- case 'critical':
115
+ case "critical":
107
116
  current.critical = /^(true|yes|1)$/i.test(value);
108
117
  break;
109
- case 'host':
118
+ case "host":
110
119
  current.host = value;
111
120
  break;
112
- case 'prompt':
113
- if (value && !value.startsWith('|')) {
121
+ case "prompt":
122
+ if (value && !value.startsWith("|")) {
114
123
  current.prompt = value;
115
124
  } else {
116
125
  inPrompt = true;
@@ -178,8 +187,8 @@ export function buildMcpManifest(shards) {
178
187
  */
179
188
  export function computeMergeOrder(shards) {
180
189
  const nameSet = new Set(shards.map((s) => s.name));
181
- const adj = new Map(); // name → [dependents]
182
- const inDeg = new Map(); // name → number
190
+ const adj = new Map(); // name → [dependents]
191
+ const inDeg = new Map(); // name → number
183
192
 
184
193
  for (const shard of shards) {
185
194
  adj.set(shard.name, []);
@@ -234,11 +243,13 @@ export function computeMergeOrder(shards) {
234
243
  * @returns {SwarmPlan}
235
244
  */
236
245
  export function planSwarm(prdPath, opts = {}) {
237
- const content = opts.content || readFileSync(prdPath, 'utf8');
246
+ const content = opts.content || readFileSync(prdPath, "utf8");
238
247
  const shards = parseShards(content);
239
248
 
240
249
  if (shards.length === 0) {
241
- throw new Error('No shards found in PRD. Expected "## Shard: <name>" sections.');
250
+ throw new Error(
251
+ 'No shards found in PRD. Expected "## Shard: <name>" sections.',
252
+ );
242
253
  }
243
254
 
244
255
  const { leaseMap, conflicts } = buildFileLeaseMap(shards);
@@ -246,7 +257,7 @@ export function planSwarm(prdPath, opts = {}) {
246
257
  const { order: mergeOrder, cycles } = computeMergeOrder(shards);
247
258
 
248
259
  if (cycles.length > 0) {
249
- throw new Error(`Dependency cycle detected: ${cycles[0].join('')}`);
260
+ throw new Error(`Dependency cycle detected: ${cycles[0].join("")}`);
250
261
  }
251
262
 
252
263
  return Object.freeze({
@@ -2,7 +2,7 @@
2
2
  // For critical shards: launches primary + verifier sessions, compares results,
3
3
  // applies conservative adoption (fewer changes wins) or HITL fallback.
4
4
 
5
- import { execFile } from 'node:child_process';
5
+ import { execFile } from "node:child_process";
6
6
 
7
7
  /**
8
8
  * Compare two shard results and decide which to accept.
@@ -21,14 +21,23 @@ export async function reconcile(primaryResult, verifierResult, opts = {}) {
21
21
  const { rootDir = process.cwd(), maxDivergenceFiles = 5 } = opts;
22
22
 
23
23
  // If either failed, pick the one that succeeded
24
- if (primaryResult.status !== 'completed' && verifierResult.status === 'completed') {
25
- return decision('verifier', 'primary_failed', verifierResult);
24
+ if (
25
+ primaryResult.status !== "completed" &&
26
+ verifierResult.status === "completed"
27
+ ) {
28
+ return decision("verifier", "primary_failed", verifierResult);
26
29
  }
27
- if (verifierResult.status !== 'completed' && primaryResult.status === 'completed') {
28
- return decision('primary', 'verifier_failed', primaryResult);
30
+ if (
31
+ verifierResult.status !== "completed" &&
32
+ primaryResult.status === "completed"
33
+ ) {
34
+ return decision("primary", "verifier_failed", primaryResult);
29
35
  }
30
- if (primaryResult.status !== 'completed' && verifierResult.status !== 'completed') {
31
- return decision('none', 'both_failed', null);
36
+ if (
37
+ primaryResult.status !== "completed" &&
38
+ verifierResult.status !== "completed"
39
+ ) {
40
+ return decision("none", "both_failed", null);
32
41
  }
33
42
 
34
43
  // Both completed — compare diffs
@@ -37,29 +46,37 @@ export async function reconcile(primaryResult, verifierResult, opts = {}) {
37
46
 
38
47
  // Identical diffs → accept primary (no divergence)
39
48
  if (primaryDiff.hash === verifierDiff.hash) {
40
- return decision('primary', 'identical', primaryResult);
49
+ return decision("primary", "identical", primaryResult);
41
50
  }
42
51
 
43
52
  // Compute divergence
44
- const divergence = Math.abs(primaryDiff.filesChanged - verifierDiff.filesChanged);
53
+ const divergence = Math.abs(
54
+ primaryDiff.filesChanged - verifierDiff.filesChanged,
55
+ );
45
56
 
46
57
  // High divergence → HITL
47
58
  if (divergence > maxDivergenceFiles) {
48
59
  return {
49
- selected: 'hitl',
60
+ selected: "hitl",
50
61
  reason: `divergence_too_high (${divergence} files differ)`,
51
62
  result: null,
52
63
  requiresManualReview: true,
53
- primary: { filesChanged: primaryDiff.filesChanged, linesChanged: primaryDiff.linesChanged },
54
- verifier: { filesChanged: verifierDiff.filesChanged, linesChanged: verifierDiff.linesChanged },
64
+ primary: {
65
+ filesChanged: primaryDiff.filesChanged,
66
+ linesChanged: primaryDiff.linesChanged,
67
+ },
68
+ verifier: {
69
+ filesChanged: verifierDiff.filesChanged,
70
+ linesChanged: verifierDiff.linesChanged,
71
+ },
55
72
  };
56
73
  }
57
74
 
58
75
  // Conservative adoption: fewer changes wins
59
76
  if (primaryDiff.linesChanged <= verifierDiff.linesChanged) {
60
- return decision('primary', 'conservative_adoption', primaryResult);
77
+ return decision("primary", "conservative_adoption", primaryResult);
61
78
  }
62
- return decision('verifier', 'conservative_adoption', verifierResult);
79
+ return decision("verifier", "conservative_adoption", verifierResult);
63
80
  }
64
81
 
65
82
  function decision(selected, reason, result) {
@@ -67,7 +84,7 @@ function decision(selected, reason, result) {
67
84
  selected,
68
85
  reason,
69
86
  result,
70
- requiresManualReview: selected === 'hitl' || selected === 'none',
87
+ requiresManualReview: selected === "hitl" || selected === "none",
71
88
  primary: null,
72
89
  verifier: null,
73
90
  };
@@ -82,8 +99,11 @@ function decision(selected, reason, result) {
82
99
  */
83
100
  async function getDiffStat(branch, cwd) {
84
101
  try {
85
- const stat = await gitExec(['diff', '--stat', '--numstat', `${branch}~1..${branch}`], cwd);
86
- const lines = stat.split('\n').filter(Boolean);
102
+ const stat = await gitExec(
103
+ ["diff", "--stat", "--numstat", `${branch}~1..${branch}`],
104
+ cwd,
105
+ );
106
+ const lines = stat.split("\n").filter(Boolean);
87
107
  let filesChanged = 0;
88
108
  let linesChanged = 0;
89
109
 
@@ -96,20 +116,25 @@ async function getDiffStat(branch, cwd) {
96
116
  }
97
117
 
98
118
  // Get tree hash for identity comparison
99
- const hash = await gitExec(['rev-parse', `${branch}^{tree}`], cwd);
119
+ const hash = await gitExec(["rev-parse", `${branch}^{tree}`], cwd);
100
120
 
101
121
  return { filesChanged, linesChanged, hash: hash.trim() };
102
122
  } catch {
103
- return { filesChanged: 0, linesChanged: 0, hash: '' };
123
+ return { filesChanged: 0, linesChanged: 0, hash: "" };
104
124
  }
105
125
  }
106
126
 
107
127
  function gitExec(args, cwd) {
108
128
  return new Promise((res, rej) => {
109
- execFile('git', args, { cwd, windowsHide: true, timeout: 15_000 }, (err, stdout) => {
110
- if (err) rej(err);
111
- else res(stdout);
112
- });
129
+ execFile(
130
+ "git",
131
+ args,
132
+ { cwd, windowsHide: true, timeout: 15_000 },
133
+ (err, stdout) => {
134
+ if (err) rej(err);
135
+ else res(stdout);
136
+ },
137
+ );
113
138
  });
114
139
  }
115
140