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,19 +1,28 @@
1
1
  #!/usr/bin/env node
2
2
  // MCP inventory cache for dynamic MCP filtering.
3
3
 
4
- import { execSync } from 'node:child_process';
5
- import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
6
- import { homedir } from 'node:os';
7
- import { dirname, join, resolve } from 'node:path';
8
- import { fileURLToPath } from 'node:url';
9
-
10
- import { MCP_SERVER_TOOL_CATALOG, normalizeServerMetadata } from './lib/mcp-server-catalog.mjs';
11
-
12
- const CACHE_DIR = join(homedir(), '.claude', 'cache');
13
- const CACHE_FILE = join(CACHE_DIR, 'mcp-inventory.json');
4
+ import { execSync } from "node:child_process";
5
+ import {
6
+ existsSync,
7
+ mkdirSync,
8
+ readdirSync,
9
+ readFileSync,
10
+ writeFileSync,
11
+ } from "node:fs";
12
+ import { homedir } from "node:os";
13
+ import { dirname, join, resolve } from "node:path";
14
+ import { fileURLToPath } from "node:url";
15
+
16
+ import {
17
+ MCP_SERVER_TOOL_CATALOG,
18
+ normalizeServerMetadata,
19
+ } from "./lib/mcp-server-catalog.mjs";
20
+
21
+ const CACHE_DIR = join(homedir(), ".claude", "cache");
22
+ const CACHE_FILE = join(CACHE_DIR, "mcp-inventory.json");
14
23
 
15
24
  function countConfiguredTools(config = {}, fallbackToolCount = 0) {
16
- const directKeys = ['tools', 'toolNames', 'allowedTools', 'includeTools'];
25
+ const directKeys = ["tools", "toolNames", "allowedTools", "includeTools"];
17
26
  for (const key of directKeys) {
18
27
  if (Array.isArray(config[key])) return config[key].length;
19
28
  }
@@ -26,7 +35,7 @@ function countConfiguredTools(config = {}, fallbackToolCount = 0) {
26
35
  }
27
36
 
28
37
  export function createServerRecord(name, status, config = {}) {
29
- const normalizedName = typeof name === 'string' ? name.trim() : '';
38
+ const normalizedName = typeof name === "string" ? name.trim() : "";
30
39
  const fallback = normalizeServerMetadata(normalizedName, {});
31
40
  const toolCount = countConfiguredTools(config, fallback.tool_count);
32
41
  const domainTags = Array.isArray(config.domain_tags)
@@ -51,13 +60,16 @@ export function createServerRecord(name, status, config = {}) {
51
60
 
52
61
  export function getCodexMcp() {
53
62
  try {
54
- const output = execSync('codex mcp list', {
55
- encoding: 'utf8',
63
+ const output = execSync("codex mcp list", {
64
+ encoding: "utf8",
56
65
  timeout: 15000,
57
- stdio: ['pipe', 'pipe', 'ignore'],
66
+ stdio: ["pipe", "pipe", "ignore"],
58
67
  windowsHide: true,
59
68
  });
60
- const lines = output.trim().split(/\r?\n/).filter((line) => line.trim());
69
+ const lines = output
70
+ .trim()
71
+ .split(/\r?\n/)
72
+ .filter((line) => line.trim());
61
73
  if (lines.length < 2) return [];
62
74
 
63
75
  const servers = [];
@@ -67,8 +79,8 @@ export function getCodexMcp() {
67
79
 
68
80
  const name = cols[0].trim();
69
81
  const statusMatch = lines[i].match(/\b(enabled|disabled)\b/i);
70
- const status = statusMatch ? statusMatch[1].toLowerCase() : 'unknown';
71
- if (!name || name.startsWith('-')) continue;
82
+ const status = statusMatch ? statusMatch[1].toLowerCase() : "unknown";
83
+ if (!name || name.startsWith("-")) continue;
72
84
  servers.push(createServerRecord(name, status));
73
85
  }
74
86
  return servers;
@@ -79,12 +91,14 @@ export function getCodexMcp() {
79
91
 
80
92
  export function getGeminiMcp() {
81
93
  try {
82
- const settingsPath = join(homedir(), '.gemini', 'settings.json');
94
+ const settingsPath = join(homedir(), ".gemini", "settings.json");
83
95
  if (!existsSync(settingsPath)) return null;
84
96
 
85
- const settings = JSON.parse(readFileSync(settingsPath, 'utf8'));
97
+ const settings = JSON.parse(readFileSync(settingsPath, "utf8"));
86
98
  const mcpServers = settings.mcpServers || {};
87
- return Object.entries(mcpServers).map(([name, config]) => createServerRecord(name, 'configured', config || {}));
99
+ return Object.entries(mcpServers).map(([name, config]) =>
100
+ createServerRecord(name, "configured", config || {}),
101
+ );
88
102
  } catch {
89
103
  return null;
90
104
  }
@@ -92,20 +106,20 @@ export function getGeminiMcp() {
92
106
 
93
107
  // ── Claude MCP 서버 발견 ──
94
108
 
95
- const CLAUDE_DIR = join(homedir(), '.claude');
109
+ const CLAUDE_DIR = join(homedir(), ".claude");
96
110
 
97
111
  // Anthropic 클라우드 호스팅 MCP 서버 — Claude Code 런타임에서 항상 가용.
98
112
  // mcp-server-catalog.mjs의 MCP_SERVER_TOOL_CATALOG에 정의된 서버 중
99
113
  // 로컬 설치가 불필요한 클라우드 제공 서버.
100
114
  const WELL_KNOWN_CLOUD_SERVERS = Object.freeze([
101
- 'brave-search',
102
- 'exa',
103
- 'context7',
115
+ "brave-search",
116
+ "exa",
117
+ "context7",
104
118
  ]);
105
119
 
106
120
  function readJsonSafe(filePath) {
107
121
  try {
108
- return JSON.parse(readFileSync(filePath, 'utf8'));
122
+ return JSON.parse(readFileSync(filePath, "utf8"));
109
123
  } catch {
110
124
  return null;
111
125
  }
@@ -113,9 +127,9 @@ function readJsonSafe(filePath) {
113
127
 
114
128
  function extractMcpServerNames(mcpJsonPath) {
115
129
  const data = readJsonSafe(mcpJsonPath);
116
- if (!data?.mcpServers || typeof data.mcpServers !== 'object') return [];
130
+ if (!data?.mcpServers || typeof data.mcpServers !== "object") return [];
117
131
  return Object.entries(data.mcpServers)
118
- .filter(([name]) => typeof name === 'string' && name.trim())
132
+ .filter(([name]) => typeof name === "string" && name.trim())
119
133
  .map(([name, config]) => ({ name: name.trim(), config: config || {} }));
120
134
  }
121
135
 
@@ -123,7 +137,7 @@ function walkUpForMcpJson(startDir, maxDepth = 5) {
123
137
  const found = [];
124
138
  let dir = resolve(startDir);
125
139
  for (let i = 0; i < maxDepth; i += 1) {
126
- const candidate = join(dir, '.mcp.json');
140
+ const candidate = join(dir, ".mcp.json");
127
141
  if (existsSync(candidate)) found.push(candidate);
128
142
  const parent = dirname(dir);
129
143
  if (parent === dir) break;
@@ -133,22 +147,24 @@ function walkUpForMcpJson(startDir, maxDepth = 5) {
133
147
  }
134
148
 
135
149
  function scanPluginsMcpJson() {
136
- const pluginsDir = join(CLAUDE_DIR, 'plugins');
150
+ const pluginsDir = join(CLAUDE_DIR, "plugins");
137
151
  if (!existsSync(pluginsDir)) return [];
138
152
  const found = [];
139
153
  const walk = (dir, depth = 0) => {
140
154
  if (depth > 4) return;
141
155
  try {
142
156
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
143
- if (entry.name === 'node_modules' || entry.name === '.git') continue;
157
+ if (entry.name === "node_modules" || entry.name === ".git") continue;
144
158
  const full = join(dir, entry.name);
145
- if (entry.isFile() && entry.name === '.mcp.json') {
159
+ if (entry.isFile() && entry.name === ".mcp.json") {
146
160
  found.push(full);
147
161
  } else if (entry.isDirectory()) {
148
162
  walk(full, depth + 1);
149
163
  }
150
164
  }
151
- } catch { /* permission errors */ }
165
+ } catch {
166
+ /* permission errors */
167
+ }
152
168
  };
153
169
  walk(pluginsDir);
154
170
  return found;
@@ -161,7 +177,7 @@ export function getClaudeMcp(cwd = process.cwd()) {
161
177
  for (const mcpJson of walkUpForMcpJson(cwd)) {
162
178
  for (const { name, config } of extractMcpServerNames(mcpJson)) {
163
179
  if (!serverMap.has(name)) {
164
- serverMap.set(name, createServerRecord(name, 'configured', config));
180
+ serverMap.set(name, createServerRecord(name, "configured", config));
165
181
  }
166
182
  }
167
183
  }
@@ -170,19 +186,22 @@ export function getClaudeMcp(cwd = process.cwd()) {
170
186
  for (const mcpJson of scanPluginsMcpJson()) {
171
187
  for (const { name, config } of extractMcpServerNames(mcpJson)) {
172
188
  if (!serverMap.has(name)) {
173
- serverMap.set(name, createServerRecord(name, 'configured', config));
189
+ serverMap.set(name, createServerRecord(name, "configured", config));
174
190
  }
175
191
  }
176
192
  }
177
193
 
178
194
  // 3) ~/.claude/settings.json + settings.local.json의 mcpServers
179
- for (const settingsFile of ['settings.json', 'settings.local.json']) {
195
+ for (const settingsFile of ["settings.json", "settings.local.json"]) {
180
196
  const data = readJsonSafe(join(CLAUDE_DIR, settingsFile));
181
197
  if (!data?.mcpServers) continue;
182
198
  for (const [name, config] of Object.entries(data.mcpServers)) {
183
- if (typeof name !== 'string' || !name.trim()) continue;
199
+ if (typeof name !== "string" || !name.trim()) continue;
184
200
  if (!serverMap.has(name.trim())) {
185
- serverMap.set(name.trim(), createServerRecord(name.trim(), 'configured', config || {}));
201
+ serverMap.set(
202
+ name.trim(),
203
+ createServerRecord(name.trim(), "configured", config || {}),
204
+ );
186
205
  }
187
206
  }
188
207
  }
@@ -191,7 +210,7 @@ export function getClaudeMcp(cwd = process.cwd()) {
191
210
  for (const name of WELL_KNOWN_CLOUD_SERVERS) {
192
211
  if (serverMap.has(name)) continue;
193
212
  if (!MCP_SERVER_TOOL_CATALOG[name]) continue;
194
- serverMap.set(name, createServerRecord(name, 'available'));
213
+ serverMap.set(name, createServerRecord(name, "available"));
195
214
  }
196
215
 
197
216
  return [...serverMap.values()];
@@ -3,37 +3,63 @@
3
3
  // Usage: node mcp-gateway-config.mjs --enable # stdio → SSE
4
4
  // node mcp-gateway-config.mjs --disable # SSE → stdio (복원)
5
5
 
6
- import { execSync } from 'node:child_process';
7
- import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
8
- import { homedir } from 'node:os';
9
- import { join, dirname } from 'node:path';
10
- import { isServerEnabled } from './lib/mcp-manifest.mjs';
6
+ import { execSync } from "node:child_process";
7
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
8
+ import { homedir } from "node:os";
9
+ import { dirname, join } from "node:path";
10
+ import { isServerEnabled } from "./lib/mcp-manifest.mjs";
11
11
 
12
- const BACKUP_FILE = join(homedir(), '.claude', 'cache', 'mcp-pre-gateway.json');
12
+ const BACKUP_FILE = join(homedir(), ".claude", "cache", "mcp-pre-gateway.json");
13
13
 
14
14
  export const GATEWAY_SERVERS = [
15
- { name: 'context7', port: 8100, stdioCmd: 'cmd /c npx -y @upstash/context7-mcp@latest' },
16
- { name: 'brave-search', port: 8101, stdioCmd: 'cmd /c npx -y @brave/brave-search-mcp-server' },
17
- { name: 'exa', port: 8102, stdioCmd: 'cmd /c npx -y exa-mcp-server' },
18
- { name: 'tavily', port: 8103, stdioCmd: 'cmd /c npx -y tavily-mcp@latest' },
19
- { name: 'jira', port: 8104, stdioCmd: 'cmd /c npx -y mcp-jira-cloud@latest' },
20
- { name: 'serena', port: 8105, stdioCmd: 'uvx --from git+https://github.com/oraios/serena serena start-mcp-server' },
21
- { name: 'notion', port: 8106, stdioCmd: 'cmd /c npx -y @notionhq/notion-mcp-server' },
22
- { name: 'notion-guest', port: 8107, stdioCmd: 'cmd /c npx -y @notionhq/notion-mcp-server' },
15
+ {
16
+ name: "context7",
17
+ port: 8100,
18
+ stdioCmd: "cmd /c npx -y @upstash/context7-mcp@latest",
19
+ },
20
+ {
21
+ name: "brave-search",
22
+ port: 8101,
23
+ stdioCmd: "cmd /c npx -y @brave/brave-search-mcp-server",
24
+ },
25
+ { name: "exa", port: 8102, stdioCmd: "cmd /c npx -y exa-mcp-server" },
26
+ { name: "tavily", port: 8103, stdioCmd: "cmd /c npx -y tavily-mcp@latest" },
27
+ { name: "jira", port: 8104, stdioCmd: "cmd /c npx -y mcp-jira-cloud@latest" },
28
+ {
29
+ name: "serena",
30
+ port: 8105,
31
+ stdioCmd:
32
+ "uvx --from git+https://github.com/oraios/serena serena start-mcp-server",
33
+ },
34
+ {
35
+ name: "notion",
36
+ port: 8106,
37
+ stdioCmd: "cmd /c npx -y @notionhq/notion-mcp-server",
38
+ },
39
+ {
40
+ name: "notion-guest",
41
+ port: 8107,
42
+ stdioCmd: "cmd /c npx -y @notionhq/notion-mcp-server",
43
+ },
23
44
  ];
24
45
 
25
46
  const SKIP_SERVERS = new Set([
26
- 'playwright',
27
- 'claude.ai Notion',
28
- 'plugin:oh-my-claudecode:t',
47
+ "playwright",
48
+ "claude.ai Notion",
49
+ "plugin:oh-my-claudecode:t",
29
50
  ]);
30
51
 
31
52
  // Git Bash에서 /c → C:/ 경로 변환 방지
32
- const EXEC_ENV = { ...process.env, MSYS_NO_PATHCONV: '1' };
53
+ const EXEC_ENV = { ...process.env, MSYS_NO_PATHCONV: "1" };
33
54
 
34
55
  function run(cmd) {
35
56
  try {
36
- execSync(cmd, { stdio: 'pipe', encoding: 'utf8', timeout: 15000, env: EXEC_ENV });
57
+ execSync(cmd, {
58
+ stdio: "pipe",
59
+ encoding: "utf8",
60
+ timeout: 15000,
61
+ env: EXEC_ENV,
62
+ });
37
63
  return true;
38
64
  } catch {
39
65
  return false;
@@ -54,16 +80,22 @@ function captureCurrentMcpState() {
54
80
  // claude mcp get으로 현재 등록 상태 확인
55
81
  try {
56
82
  const out = execSync(`claude mcp get "${name}" -s user`, {
57
- stdio: 'pipe', encoding: 'utf8', timeout: 5000, env: EXEC_ENV,
83
+ stdio: "pipe",
84
+ encoding: "utf8",
85
+ timeout: 5000,
86
+ env: EXEC_ENV,
58
87
  }).trim();
59
- servers[name] = { scope: 'user', raw: out };
88
+ servers[name] = { scope: "user", raw: out };
60
89
  } catch {
61
90
  // user에 없으면 local 확인
62
91
  try {
63
92
  const out = execSync(`claude mcp get "${name}" -s local`, {
64
- stdio: 'pipe', encoding: 'utf8', timeout: 5000, env: EXEC_ENV,
93
+ stdio: "pipe",
94
+ encoding: "utf8",
95
+ timeout: 5000,
96
+ env: EXEC_ENV,
65
97
  }).trim();
66
- servers[name] = { scope: 'local', raw: out };
98
+ servers[name] = { scope: "local", raw: out };
67
99
  } catch {
68
100
  servers[name] = null; // 기존에 없었음
69
101
  }
@@ -83,14 +115,16 @@ function saveBackup(servers) {
83
115
  function loadBackup() {
84
116
  if (!existsSync(BACKUP_FILE)) return null;
85
117
  try {
86
- return JSON.parse(readFileSync(BACKUP_FILE, 'utf8'));
87
- } catch { return null; }
118
+ return JSON.parse(readFileSync(BACKUP_FILE, "utf8"));
119
+ } catch {
120
+ return null;
121
+ }
88
122
  }
89
123
 
90
124
  // ── enable: 스냅샷 → remove → SSE add (실패 시 rollback) ──
91
125
 
92
126
  function enableSse() {
93
- console.log('Switching MCP servers to SSE mode...\n');
127
+ console.log("Switching MCP servers to SSE mode...\n");
94
128
 
95
129
  // 1) 현재 상태 스냅샷
96
130
  const snapshot = captureCurrentMcpState();
@@ -108,7 +142,9 @@ function enableSse() {
108
142
 
109
143
  removeMcp(name);
110
144
  const url = `http://localhost:${port}/sse`;
111
- const success = run(`claude mcp add --transport sse -s user "${name}" ${url}`);
145
+ const success = run(
146
+ `claude mcp add --transport sse -s user "${name}" ${url}`,
147
+ );
112
148
 
113
149
  if (success) {
114
150
  console.log(` [SSE] ${name} → ${url}`);
@@ -119,9 +155,11 @@ function enableSse() {
119
155
  const orig = snapshot[name];
120
156
  if (orig) {
121
157
  // H1 fix: orig.raw를 shell-escape하여 injection 방지
122
- const safeRaw = (orig.raw || '').replace(/[;`$(){}|&<>]/g, '');
123
- const restored = safeRaw ? run(`claude mcp add "${name}" -s ${orig.scope} -- ${safeRaw}`) : false;
124
- console.error(` [ROLLBACK] ${name}: ${restored ? 'ok' : 'FAIL'}`);
158
+ const safeRaw = (orig.raw || "").replace(/[;`$(){}|&<>]/g, "");
159
+ const restored = safeRaw
160
+ ? run(`claude mcp add "${name}" -s ${orig.scope} -- ${safeRaw}`)
161
+ : false;
162
+ console.error(` [ROLLBACK] ${name}: ${restored ? "ok" : "FAIL"}`);
125
163
  }
126
164
  fail++;
127
165
  }
@@ -133,12 +171,14 @@ function enableSse() {
133
171
  // ── disable: 백업에서 서버별 원복 ──
134
172
 
135
173
  function disableSse() {
136
- console.log('Restoring MCP servers from backup...\n');
174
+ console.log("Restoring MCP servers from backup...\n");
137
175
  const backup = loadBackup();
138
176
 
139
177
  // C1 fix: 백업 없으면 전체 삭제 방지
140
178
  if (!backup) {
141
- console.error('No backup found — cannot restore. Run --enable first to create a backup.');
179
+ console.error(
180
+ "No backup found — cannot restore. Run --enable first to create a backup.",
181
+ );
142
182
  process.exit(1);
143
183
  }
144
184
 
@@ -160,9 +200,11 @@ function disableSse() {
160
200
 
161
201
  // H2 fix: 백업의 scope/raw를 사용하여 원본 복원, fallback으로 stdioCmd
162
202
  removeMcp(name);
163
- const restoreScope = orig.scope || 'user';
203
+ const restoreScope = orig.scope || "user";
164
204
  const restoreCmd = orig.raw?.trim() ? orig.raw.trim() : stdioCmd;
165
- const success = run(`claude mcp add "${name}" -s ${restoreScope} -- ${restoreCmd}`);
205
+ const success = run(
206
+ `claude mcp add "${name}" -s ${restoreScope} -- ${restoreCmd}`,
207
+ );
166
208
 
167
209
  if (success) {
168
210
  console.log(` [RESTORE] ${name} → scope=${restoreScope}`);
@@ -171,7 +213,9 @@ function disableSse() {
171
213
  // fallback: stdioCmd로 재시도
172
214
  const fallback = run(`claude mcp add "${name}" -s user -- ${stdioCmd}`);
173
215
  if (fallback) {
174
- console.log(` [FALLBACK] ${name} → stdio (원본 복원 실패, 기본값 사용)`);
216
+ console.log(
217
+ ` [FALLBACK] ${name} → stdio (원본 복원 실패, 기본값 사용)`,
218
+ );
175
219
  ok++;
176
220
  } else {
177
221
  console.error(` [FAIL] ${name}`);
@@ -190,16 +234,16 @@ function printUsage() {
190
234
  --enable Switch Claude Code MCP servers from stdio to SSE (supergateway)
191
235
  --disable Restore Claude Code MCP servers to original stdio mode
192
236
 
193
- Servers managed: ${GATEWAY_SERVERS.map((s) => s.name).join(', ')}
194
- Servers skipped: ${[...SKIP_SERVERS].join(', ')}`);
237
+ Servers managed: ${GATEWAY_SERVERS.map((s) => s.name).join(", ")}
238
+ Servers skipped: ${[...SKIP_SERVERS].join(", ")}`);
195
239
  }
196
240
 
197
241
  // ── main ──
198
242
  const flag = process.argv[2];
199
243
 
200
- if (flag === '--enable') {
244
+ if (flag === "--enable") {
201
245
  enableSse();
202
- } else if (flag === '--disable') {
246
+ } else if (flag === "--disable") {
203
247
  disableSse();
204
248
  } else {
205
249
  printUsage();
@@ -1,24 +1,21 @@
1
1
  #!/usr/bin/env node
2
+
2
3
  // mcp-gateway-ensure.mjs — SessionStart 훅에서 supergateway MCP 서비스 보장
3
4
  // hub-ensure.mjs 패턴을 따름. 가볍게 헬스체크만 수행하고 필요시 기동.
4
5
 
5
- import { existsSync, } from 'node:fs';
6
- import { join, dirname } from 'node:path';
7
- import { execSync } from 'node:child_process';
8
- import { tmpdir } from 'node:os';
9
- import { fileURLToPath } from 'node:url';
6
+ import { execSync } from "node:child_process";
7
+ import { existsSync } from "node:fs";
8
+ import { tmpdir } from "node:os";
9
+ import { dirname, join } from "node:path";
10
+ import { fileURLToPath } from "node:url";
10
11
 
11
12
  const PLUGIN_ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
12
- const PID_FILE = join(tmpdir(), 'tfx-gateway-pids.json');
13
+ const PID_FILE = join(tmpdir(), "tfx-gateway-pids.json");
13
14
  const PROBE_PORT = 8100; // context7 — 첫 번째 게이트웨이 포트로 alive 프로브
14
15
  const PROBE_TIMEOUT_MS = 1500;
15
16
  const STARTUP_WAIT_MS = 4000;
16
17
  const POLL_INTERVAL_MS = 500;
17
18
 
18
- /**
19
- * 단일 포트 /healthz 프로브로 게이트웨이 클러스터 alive 판정.
20
- * 모든 포트를 체크하면 hook timeout(8s)에 걸리므로 대표 포트 1개만 확인.
21
- */
22
19
  async function isGatewayAlive() {
23
20
  try {
24
21
  const res = await fetch(`http://127.0.0.1:${PROBE_PORT}/healthz`, {
@@ -30,56 +27,67 @@ async function isGatewayAlive() {
30
27
  }
31
28
  }
32
29
 
33
- /** 매니페스트 파일 존재 여부로 gateway 설치 판정 (빠른 경로) */
34
30
  function hasManifest() {
35
31
  return existsSync(PID_FILE);
36
32
  }
37
33
 
38
- /** mcp-gateway-start.mjs를 독립 프로세스로 기동 */
39
34
  function startGateway() {
40
- const scriptPath = join(PLUGIN_ROOT, 'scripts', 'mcp-gateway-start.mjs');
35
+ const scriptPath = join(PLUGIN_ROOT, "scripts", "mcp-gateway-start.mjs");
41
36
  if (!existsSync(scriptPath)) return false;
42
37
 
43
38
  try {
44
- // PowerShell Start-Process: Windows Job Object에서 벗어나 부모 종료 후 생존
45
39
  execSync(
46
- `powershell -NoProfile -Command "Start-Process -WindowStyle Hidden -FilePath '${process.execPath}' -ArgumentList '${scriptPath.replaceAll("'", "''")}'"`
47
- , { stdio: 'ignore', timeout: 10000 });
40
+ `powershell -NoProfile -Command "Start-Process -WindowStyle Hidden -FilePath '${process.execPath}' -ArgumentList '${scriptPath.replaceAll("'", "''")}'"`,
41
+ { stdio: "ignore", timeout: 10000 },
42
+ );
48
43
  return true;
49
44
  } catch {
50
45
  return false;
51
46
  }
52
47
  }
53
48
 
54
- /** 게이트웨이 기동 후 프로브 포트 ready 대기 */
55
49
  async function waitForGatewayReady(maxWaitMs = STARTUP_WAIT_MS) {
56
50
  const deadline = Date.now() + maxWaitMs;
57
51
  while (Date.now() < deadline) {
58
52
  if (await isGatewayAlive()) return true;
59
- await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
53
+ await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
60
54
  }
61
55
  return false;
62
56
  }
63
57
 
64
- // ── main ──
58
+ export async function run(stdinData) {
59
+ void stdinData;
65
60
 
66
- // 빠른 경로: 매니페스트 존재 + 프로브 포트 살아있으면 즉시 OK
67
- if (hasManifest() && await isGatewayAlive()) {
68
- process.stdout.write('gateway: ok');
69
- process.exit(0);
70
- }
61
+ if (hasManifest() && (await isGatewayAlive())) {
62
+ return { code: 0, stdout: "gateway: ok", stderr: "" };
63
+ }
71
64
 
72
- // 매니페스트 없으면 gateway가 설정되지 않은 상태 — 조용히 스킵
73
- if (!hasManifest()) {
74
- process.stdout.write('gateway: not configured');
75
- process.exit(0);
76
- }
65
+ if (!hasManifest()) {
66
+ return { code: 0, stdout: "gateway: not configured", stderr: "" };
67
+ }
68
+
69
+ const started = startGateway();
70
+ if (!started) {
71
+ return { code: 0, stdout: "", stderr: "[gateway-ensure] start failed" };
72
+ }
77
73
 
78
- // 느린 경로: 게이트웨이 기동 시도
79
- const started = startGateway();
80
- if (started) {
81
74
  const ready = await waitForGatewayReady();
82
- process.stdout.write(ready ? 'gateway: ok' : 'gateway: starting');
83
- } else {
84
- process.stderr.write('[gateway-ensure] start failed');
75
+ return {
76
+ code: 0,
77
+ stdout: ready ? "gateway: ok" : "gateway: starting",
78
+ stderr: "",
79
+ };
80
+ }
81
+
82
+ const isMain =
83
+ process.argv[1] &&
84
+ import.meta.url.endsWith(
85
+ process.argv[1].replace(/\\/g, "/").split("/").pop(),
86
+ );
87
+
88
+ if (isMain) {
89
+ const result = await run();
90
+ if (result.stdout) process.stdout.write(result.stdout);
91
+ if (result.stderr) process.stderr.write(result.stderr);
92
+ process.exit(result.code);
85
93
  }