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,19 @@
1
1
  // hub/workers/factory.mjs — Worker 생성 팩토리
2
2
 
3
- import { GeminiWorker } from './gemini-worker.mjs';
4
- import { ClaudeWorker } from './claude-worker.mjs';
5
- import { CodexMcpWorker } from './codex-mcp.mjs';
6
- import { DelegatorMcpWorker } from './delegator-mcp.mjs';
3
+ import { ClaudeWorker } from "./claude-worker.mjs";
4
+ import { CodexMcpWorker } from "./codex-mcp.mjs";
5
+ import { DelegatorMcpWorker } from "./delegator-mcp.mjs";
6
+ import { GeminiWorker } from "./gemini-worker.mjs";
7
7
 
8
8
  export function createWorker(type, opts = {}) {
9
9
  switch (type) {
10
- case 'gemini':
10
+ case "gemini":
11
11
  return new GeminiWorker(opts);
12
- case 'claude':
12
+ case "claude":
13
13
  return new ClaudeWorker(opts);
14
- case 'codex':
14
+ case "codex":
15
15
  return new CodexMcpWorker(opts);
16
- case 'delegator':
16
+ case "delegator":
17
17
  return new DelegatorMcpWorker(opts);
18
18
  default:
19
19
  throw new Error(`Unknown worker type: ${type}`);
@@ -1,20 +1,18 @@
1
1
  // hub/workers/gemini-worker.mjs — Gemini headless subprocess 래퍼
2
2
  // ADR-006: --output-format stream-json 기반 단발 실행 워커.
3
3
 
4
- import { spawn } from 'node:child_process';
5
- import { isAbsolute } from 'node:path';
6
- import readline from 'node:readline';
4
+ import { spawn } from "node:child_process";
5
+ import { isAbsolute } from "node:path";
6
+ import readline from "node:readline";
7
7
 
8
- import { extractText, terminateChild, withRetry } from './worker-utils.mjs';
8
+ import { extractText, terminateChild, withRetry } from "./worker-utils.mjs";
9
9
 
10
10
  const DEFAULT_TIMEOUT_MS = 15 * 60 * 1000;
11
11
  const DEFAULT_KILL_GRACE_MS = 1000;
12
12
 
13
13
  function toStringList(value) {
14
14
  if (!Array.isArray(value)) return [];
15
- return value
16
- .map((item) => String(item ?? '').trim())
17
- .filter(Boolean);
15
+ return value.map((item) => String(item ?? "").trim()).filter(Boolean);
18
16
  }
19
17
 
20
18
  function safeJsonParse(line) {
@@ -36,25 +34,25 @@ function buildGeminiArgs(options) {
36
34
  const args = [];
37
35
 
38
36
  if (options.model) {
39
- args.push('--model', options.model);
37
+ args.push("--model", options.model);
40
38
  }
41
39
 
42
40
  if (options.approvalMode) {
43
- args.push('--approval-mode', options.approvalMode);
41
+ args.push("--approval-mode", options.approvalMode);
44
42
  } else if (options.yolo !== false) {
45
- args.push('--yolo');
43
+ args.push("--yolo");
46
44
  }
47
45
 
48
46
  const allowedMcpServers = toStringList(options.allowedMcpServerNames);
49
47
  if (allowedMcpServers.length) {
50
- args.push('--allowed-mcp-server-names', ...allowedMcpServers);
48
+ args.push("--allowed-mcp-server-names", ...allowedMcpServers);
51
49
  }
52
50
 
53
51
  const extraArgs = toStringList(options.extraArgs);
54
52
  if (extraArgs.length) args.push(...extraArgs);
55
53
 
56
- args.push('--prompt', options.promptArgument ?? '');
57
- args.push('--output-format', 'stream-json');
54
+ args.push("--prompt", options.promptArgument ?? "");
55
+ args.push("--output-format", "stream-json");
58
56
 
59
57
  return args;
60
58
  }
@@ -66,49 +64,59 @@ function createWorkerError(message, details = {}) {
66
64
  }
67
65
 
68
66
  function normalizeRetryOptions(retryOptions) {
69
- if (!retryOptions || typeof retryOptions !== 'object') {
67
+ if (!retryOptions || typeof retryOptions !== "object") {
70
68
  return Object.freeze({});
71
69
  }
72
70
  return Object.freeze({ ...retryOptions });
73
71
  }
74
72
 
75
73
  function isGeminiRetryable(error) {
76
- return error?.code === 'WORKER_EXIT'
77
- && error?.result?.exitCode !== 0
78
- && error?.result?.exitCode !== 2;
74
+ return (
75
+ error?.code === "WORKER_EXIT" &&
76
+ error?.result?.exitCode !== 0 &&
77
+ error?.result?.exitCode !== 2
78
+ );
79
79
  }
80
80
 
81
81
  function detectGeminiCategory(error) {
82
- const combined = `${error?.message || ''}\n${error?.stderr || ''}`.toLowerCase();
83
-
84
- if (/(unauthorized|forbidden|auth|login|token|credential|apikey|api key)/.test(combined)) {
85
- return 'auth';
82
+ const combined =
83
+ `${error?.message || ""}\n${error?.stderr || ""}`.toLowerCase();
84
+
85
+ if (
86
+ /(unauthorized|forbidden|auth|login|token|credential|apikey|api key)/.test(
87
+ combined,
88
+ )
89
+ ) {
90
+ return "auth";
86
91
  }
87
- if (error?.result?.exitCode === 2 || /expected stream-json|unknown option|invalid option|config/.test(combined)) {
88
- return 'config';
92
+ if (
93
+ error?.result?.exitCode === 2 ||
94
+ /expected stream-json|unknown option|invalid option|config/.test(combined)
95
+ ) {
96
+ return "config";
89
97
  }
90
- if (error?.code === 'WORKER_EVENT_ERROR') {
91
- return 'input';
98
+ if (error?.code === "WORKER_EVENT_ERROR") {
99
+ return "input";
92
100
  }
93
101
 
94
- return 'transient';
102
+ return "transient";
95
103
  }
96
104
 
97
105
  function buildGeminiErrorInfo(error, attempts) {
98
106
  const category = detectGeminiCategory(error);
99
107
  const retryable = isGeminiRetryable(error);
100
- let recovery = 'Retry the Gemini worker after correcting the reported issue.';
101
-
102
- if (category === 'auth') {
103
- recovery = 'Refresh the Gemini authentication state and retry.';
104
- } else if (category === 'config') {
105
- recovery = 'Check the Gemini CLI flags and worker configuration.';
106
- } else if (category === 'input') {
107
- recovery = 'Check the Gemini request payload and streamed event format.';
108
+ let recovery = "Retry the Gemini worker after correcting the reported issue.";
109
+
110
+ if (category === "auth") {
111
+ recovery = "Refresh the Gemini authentication state and retry.";
112
+ } else if (category === "config") {
113
+ recovery = "Check the Gemini CLI flags and worker configuration.";
114
+ } else if (category === "input") {
115
+ recovery = "Check the Gemini request payload and streamed event format.";
108
116
  }
109
117
 
110
118
  return Object.freeze({
111
- code: error?.code || 'GEMINI_EXECUTION_ERROR',
119
+ code: error?.code || "GEMINI_EXECUTION_ERROR",
112
120
  retryable,
113
121
  attempts,
114
122
  category,
@@ -120,10 +128,10 @@ function buildGeminiErrorInfo(error, attempts) {
120
128
  * Gemini stream-json 래퍼
121
129
  */
122
130
  export class GeminiWorker {
123
- type = 'gemini';
131
+ type = "gemini";
124
132
 
125
133
  constructor(options = {}) {
126
- this.command = options.command || 'gemini';
134
+ this.command = options.command || "gemini";
127
135
  this.commandArgs = toStringList(options.commandArgs || options.args);
128
136
  this.cwd = options.cwd || process.cwd();
129
137
  this.env = { ...process.env, ...(options.env || {}) };
@@ -132,19 +140,26 @@ export class GeminiWorker {
132
140
  this.yolo = options.yolo !== false;
133
141
  this.allowedMcpServerNames = toStringList(options.allowedMcpServerNames);
134
142
  this.extraArgs = toStringList(options.extraArgs);
135
- this.timeoutMs = Number(options.timeoutMs) > 0 ? Number(options.timeoutMs) : DEFAULT_TIMEOUT_MS;
136
- this.killGraceMs = Number(options.killGraceMs) > 0 ? Number(options.killGraceMs) : DEFAULT_KILL_GRACE_MS;
143
+ this.timeoutMs =
144
+ Number(options.timeoutMs) > 0
145
+ ? Number(options.timeoutMs)
146
+ : DEFAULT_TIMEOUT_MS;
147
+ this.killGraceMs =
148
+ Number(options.killGraceMs) > 0
149
+ ? Number(options.killGraceMs)
150
+ : DEFAULT_KILL_GRACE_MS;
137
151
  this.retryOptions = normalizeRetryOptions(options.retryOptions);
138
- this.onEvent = typeof options.onEvent === 'function' ? options.onEvent : null;
152
+ this.onEvent =
153
+ typeof options.onEvent === "function" ? options.onEvent : null;
139
154
 
140
- this.state = 'idle';
155
+ this.state = "idle";
141
156
  this.child = null;
142
157
  this.lastRun = null;
143
158
  }
144
159
 
145
160
  getStatus() {
146
161
  return {
147
- type: 'gemini',
162
+ type: "gemini",
148
163
  state: this.state,
149
164
  pid: this.child?.pid || null,
150
165
  last_run_at_ms: this.lastRun?.finishedAtMs || null,
@@ -153,42 +168,47 @@ export class GeminiWorker {
153
168
  }
154
169
 
155
170
  async start() {
156
- if (this.state === 'stopped') {
157
- this.state = 'idle';
171
+ if (this.state === "stopped") {
172
+ this.state = "idle";
158
173
  }
159
174
  return this.getStatus();
160
175
  }
161
176
 
162
177
  async stop() {
163
178
  if (!this.child) {
164
- this.state = 'stopped';
179
+ this.state = "stopped";
165
180
  return this.getStatus();
166
181
  }
167
182
  const child = this.child;
168
183
  terminateChild(child, this.killGraceMs);
169
184
  await new Promise((resolve) => {
170
- child.once('close', resolve);
185
+ child.once("close", resolve);
171
186
  setTimeout(resolve, this.killGraceMs + 50).unref?.();
172
187
  });
173
188
  this.child = null;
174
- this.state = 'stopped';
189
+ this.state = "stopped";
175
190
  return this.getStatus();
176
191
  }
177
192
 
178
193
  async restart() {
179
194
  await this.stop();
180
- this.state = 'idle';
195
+ this.state = "idle";
181
196
  return this.getStatus();
182
197
  }
183
198
 
184
199
  async run(prompt, options = {}) {
185
200
  if (this.child) {
186
- throw createWorkerError('GeminiWorker is already running', { code: 'WORKER_BUSY' });
201
+ throw createWorkerError("GeminiWorker is already running", {
202
+ code: "WORKER_BUSY",
203
+ });
187
204
  }
188
205
 
189
206
  await this.start();
190
207
 
191
- const timeoutMs = Number(options.timeoutMs) > 0 ? Number(options.timeoutMs) : this.timeoutMs;
208
+ const timeoutMs =
209
+ Number(options.timeoutMs) > 0
210
+ ? Number(options.timeoutMs)
211
+ : this.timeoutMs;
192
212
  const startedAtMs = Date.now();
193
213
  const args = [
194
214
  ...this.commandArgs,
@@ -196,22 +216,23 @@ export class GeminiWorker {
196
216
  model: options.model || this.model,
197
217
  approvalMode: options.approvalMode || this.approvalMode,
198
218
  yolo: options.yolo ?? this.yolo,
199
- allowedMcpServerNames: options.allowedMcpServerNames || this.allowedMcpServerNames,
219
+ allowedMcpServerNames:
220
+ options.allowedMcpServerNames || this.allowedMcpServerNames,
200
221
  extraArgs: options.extraArgs || this.extraArgs,
201
- promptArgument: options.promptArgument ?? '',
222
+ promptArgument: options.promptArgument ?? "",
202
223
  }),
203
224
  ];
204
225
 
205
226
  const child = spawn(this.command, args, {
206
227
  cwd: options.cwd || this.cwd,
207
228
  env: { ...this.env, ...(options.env || {}) },
208
- stdio: ['pipe', 'pipe', 'pipe'],
229
+ stdio: ["pipe", "pipe", "pipe"],
209
230
  windowsHide: true,
210
- shell: process.platform === 'win32' && !isAbsolute(this.command),
231
+ shell: process.platform === "win32" && !isAbsolute(this.command),
211
232
  });
212
233
 
213
234
  this.child = child;
214
- this.state = 'running';
235
+ this.state = "running";
215
236
 
216
237
  const events = [];
217
238
  const stdoutLines = [];
@@ -230,7 +251,7 @@ export class GeminiWorker {
230
251
  crlfDelay: Infinity,
231
252
  });
232
253
 
233
- stdoutReader.on('line', (line) => {
254
+ stdoutReader.on("line", (line) => {
234
255
  if (!line) return;
235
256
  const event = safeJsonParse(line);
236
257
  if (!event) {
@@ -239,20 +260,22 @@ export class GeminiWorker {
239
260
  }
240
261
 
241
262
  events.push(event);
242
- if (event.type === 'error') lastErrorEvent = event;
263
+ if (event.type === "error") lastErrorEvent = event;
243
264
  if (this.onEvent) {
244
- try { this.onEvent(event); } catch {}
265
+ try {
266
+ this.onEvent(event);
267
+ } catch {}
245
268
  }
246
269
  });
247
270
 
248
- stderrReader.on('line', (line) => {
271
+ stderrReader.on("line", (line) => {
249
272
  if (!line) return;
250
273
  stderrLines.push(line);
251
274
  });
252
275
 
253
276
  const closePromise = new Promise((resolve, reject) => {
254
- child.once('error', reject);
255
- child.once('close', (code, signal) => {
277
+ child.once("error", reject);
278
+ child.once("close", (code, signal) => {
256
279
  exitCode = code;
257
280
  exitSignal = signal;
258
281
  resolve();
@@ -265,8 +288,8 @@ export class GeminiWorker {
265
288
  }, timeoutMs);
266
289
  timeout.unref?.();
267
290
 
268
- child.stdin.on('error', () => {});
269
- child.stdin.end(String(prompt ?? ''));
291
+ child.stdin.on("error", () => {});
292
+ child.stdin.end(String(prompt ?? ""));
270
293
 
271
294
  try {
272
295
  await closePromise;
@@ -277,32 +300,37 @@ export class GeminiWorker {
277
300
  if (this.child === child) {
278
301
  this.child = null;
279
302
  }
280
- this.state = 'idle';
303
+ this.state = "idle";
281
304
  }
282
305
 
283
- const resultEvent = findLastEvent(events, (event) => event?.type === 'result');
306
+ const resultEvent = findLastEvent(
307
+ events,
308
+ (event) => event?.type === "result",
309
+ );
284
310
  const response = [
285
311
  extractText(resultEvent),
286
312
  ...events
287
- .filter((event) => event?.type === 'message' || event?.type === 'assistant')
313
+ .filter(
314
+ (event) => event?.type === "message" || event?.type === "assistant",
315
+ )
288
316
  .map((event) => extractText(event))
289
317
  .filter(Boolean),
290
318
  ...stdoutLines,
291
319
  ]
292
320
  .filter(Boolean)
293
- .join('\n')
321
+ .join("\n")
294
322
  .trim();
295
323
 
296
324
  const result = {
297
- type: 'gemini',
325
+ type: "gemini",
298
326
  command: this.command,
299
327
  args,
300
328
  response,
301
329
  events,
302
330
  resultEvent,
303
331
  usage: resultEvent?.usage || null,
304
- stdout: stdoutLines.join('\n').trim(),
305
- stderr: stderrLines.join('\n').trim(),
332
+ stdout: stdoutLines.join("\n").trim(),
333
+ stderr: stderrLines.join("\n").trim(),
306
334
  exitCode,
307
335
  exitSignal,
308
336
  timedOut,
@@ -314,7 +342,7 @@ export class GeminiWorker {
314
342
 
315
343
  if (timedOut) {
316
344
  throw createWorkerError(`Gemini worker timed out after ${timeoutMs}ms`, {
317
- code: 'ETIMEDOUT',
345
+ code: "ETIMEDOUT",
318
346
  result,
319
347
  stderr: result.stderr,
320
348
  });
@@ -322,15 +350,15 @@ export class GeminiWorker {
322
350
 
323
351
  if (exitCode !== 0) {
324
352
  throw createWorkerError(`Gemini worker exited with code ${exitCode}`, {
325
- code: 'WORKER_EXIT',
353
+ code: "WORKER_EXIT",
326
354
  result,
327
355
  stderr: result.stderr,
328
356
  });
329
357
  }
330
358
 
331
359
  if (lastErrorEvent) {
332
- throw createWorkerError('Gemini worker emitted an error event', {
333
- code: 'WORKER_EVENT_ERROR',
360
+ throw createWorkerError("Gemini worker emitted an error event", {
361
+ code: "WORKER_EVENT_ERROR",
334
362
  result,
335
363
  stderr: result.stderr,
336
364
  });
@@ -340,20 +368,23 @@ export class GeminiWorker {
340
368
  }
341
369
 
342
370
  isReady() {
343
- return this.state !== 'stopped';
371
+ return this.state !== "stopped";
344
372
  }
345
373
 
346
374
  async execute(prompt, options = {}) {
347
375
  let attempts = 0;
348
376
 
349
377
  try {
350
- const result = await withRetry(async () => {
351
- attempts += 1;
352
- return this.run(prompt, options);
353
- }, {
354
- ...this.retryOptions,
355
- shouldRetry: (error) => isGeminiRetryable(error),
356
- });
378
+ const result = await withRetry(
379
+ async () => {
380
+ attempts += 1;
381
+ return this.run(prompt, options);
382
+ },
383
+ {
384
+ ...this.retryOptions,
385
+ shouldRetry: (error) => isGeminiRetryable(error),
386
+ },
387
+ );
357
388
 
358
389
  return {
359
390
  output: result.response,
@@ -363,8 +394,8 @@ export class GeminiWorker {
363
394
  };
364
395
  } catch (error) {
365
396
  return {
366
- output: error.stderr || error.message || 'Gemini worker failed',
367
- exitCode: error.code === 'ETIMEDOUT' ? 124 : 1,
397
+ output: error.stderr || error.message || "Gemini worker failed",
398
+ exitCode: error.code === "ETIMEDOUT" ? 124 : 1,
368
399
  sessionKey: options.sessionKey || null,
369
400
  error: buildGeminiErrorInfo(error, attempts || 1),
370
401
  raw: error.result || null,
@@ -49,4 +49,9 @@
49
49
  * @property {string} type - 'codex' | 'gemini' | 'claude' | 'delegator'
50
50
  */
51
51
 
52
- export const WORKER_TYPES = Object.freeze(['codex', 'gemini', 'claude', 'delegator']);
52
+ export const WORKER_TYPES = Object.freeze([
53
+ "codex",
54
+ "gemini",
55
+ "claude",
56
+ "delegator",
57
+ ]);
@@ -6,9 +6,7 @@ export const DEFAULT_KILL_GRACE_MS = 1000;
6
6
 
7
7
  export function toStringList(value) {
8
8
  if (!Array.isArray(value)) return [];
9
- return value
10
- .map((item) => String(item ?? '').trim())
11
- .filter(Boolean);
9
+ return value.map((item) => String(item ?? "").trim()).filter(Boolean);
12
10
  }
13
11
 
14
12
  export function safeJsonParse(line) {
@@ -51,8 +49,9 @@ export async function withRetry(fn, opts = {}) {
51
49
  throw error;
52
50
  }
53
51
 
54
- const delay = Math.min(baseDelayMs * 2 ** (attempt - 1), maxDelayMs)
55
- * (0.5 + Math.random() * 0.5);
52
+ const delay =
53
+ Math.min(baseDelayMs * 2 ** (attempt - 1), maxDelayMs) *
54
+ (0.5 + Math.random() * 0.5);
56
55
  await sleep(delay);
57
56
  }
58
57
  }
@@ -63,7 +62,7 @@ export async function withRetry(fn, opts = {}) {
63
62
  export function appendTextFragments(value, parts) {
64
63
  if (value == null) return;
65
64
 
66
- if (typeof value === 'string') {
65
+ if (typeof value === "string") {
67
66
  const trimmed = value.trim();
68
67
  if (trimmed) parts.push(trimmed);
69
68
  return;
@@ -74,11 +73,13 @@ export function appendTextFragments(value, parts) {
74
73
  return;
75
74
  }
76
75
 
77
- if (typeof value !== 'object') return;
76
+ if (typeof value !== "object") return;
78
77
 
79
- if (typeof value.text === 'string') appendTextFragments(value.text, parts);
80
- if (typeof value.response === 'string') appendTextFragments(value.response, parts);
81
- if (typeof value.result === 'string') appendTextFragments(value.result, parts);
78
+ if (typeof value.text === "string") appendTextFragments(value.text, parts);
79
+ if (typeof value.response === "string")
80
+ appendTextFragments(value.response, parts);
81
+ if (typeof value.result === "string")
82
+ appendTextFragments(value.result, parts);
82
83
  if (value.content != null) appendTextFragments(value.content, parts);
83
84
  if (value.message != null) appendTextFragments(value.message, parts);
84
85
  }
@@ -86,18 +87,24 @@ export function appendTextFragments(value, parts) {
86
87
  export function extractText(value) {
87
88
  const parts = [];
88
89
  appendTextFragments(value, parts);
89
- return parts.join('\n').trim();
90
+ return parts.join("\n").trim();
90
91
  }
91
92
 
92
93
  export function terminateChild(child, killGraceMs) {
93
94
  if (!child || child.exitCode !== null || child.killed) return;
94
95
 
95
- try { child.stdin.end(); } catch {}
96
- try { child.kill(); } catch {}
96
+ try {
97
+ child.stdin.end();
98
+ } catch {}
99
+ try {
100
+ child.kill();
101
+ } catch {}
97
102
 
98
103
  const timer = setTimeout(() => {
99
104
  if (child.exitCode === null) {
100
- try { child.kill('SIGKILL'); } catch {}
105
+ try {
106
+ child.kill("SIGKILL");
107
+ } catch {}
101
108
  }
102
109
  }, killGraceMs);
103
110
  timer.unref?.();
package/hud/colors.mjs CHANGED
@@ -12,15 +12,33 @@ export const CLAUDE_ORANGE = "\x1b[38;2;232;112;64m"; // #E87040 (Claude 공식
12
12
  export const CODEX_WHITE = "\x1b[97m"; // bright white (SGR 37은 Windows Terminal에서 연회색 매핑)
13
13
  export const GEMINI_BLUE = "\x1b[38;5;39m";
14
14
 
15
- export function green(t) { return `${GREEN}${t}${RESET}`; }
16
- export function yellow(t) { return `${YELLOW}${t}${RESET}`; }
17
- export function red(t) { return `${RED}${t}${RESET}`; }
18
- export function cyan(t) { return `${CYAN}${t}${RESET}`; }
19
- export function dim(t) { return `${DIM}${t}${RESET}`; }
20
- export function bold(t) { return `${BOLD}${t}${RESET}`; }
21
- export function claudeOrange(t) { return `${CLAUDE_ORANGE}${t}${RESET}`; }
22
- export function codexWhite(t) { return `${CODEX_WHITE}${t}${RESET}`; }
23
- export function geminiBlue(t) { return `${GEMINI_BLUE}${t}${RESET}`; }
15
+ export function green(t) {
16
+ return `${GREEN}${t}${RESET}`;
17
+ }
18
+ export function yellow(t) {
19
+ return `${YELLOW}${t}${RESET}`;
20
+ }
21
+ export function red(t) {
22
+ return `${RED}${t}${RESET}`;
23
+ }
24
+ export function cyan(t) {
25
+ return `${CYAN}${t}${RESET}`;
26
+ }
27
+ export function dim(t) {
28
+ return `${DIM}${t}${RESET}`;
29
+ }
30
+ export function bold(t) {
31
+ return `${BOLD}${t}${RESET}`;
32
+ }
33
+ export function claudeOrange(t) {
34
+ return `${CLAUDE_ORANGE}${t}${RESET}`;
35
+ }
36
+ export function codexWhite(t) {
37
+ return `${CODEX_WHITE}${t}${RESET}`;
38
+ }
39
+ export function geminiBlue(t) {
40
+ return `${GEMINI_BLUE}${t}${RESET}`;
41
+ }
24
42
 
25
43
  export function colorByPercent(value, text) {
26
44
  if (value >= 85) return red(text);