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
package/tui/doctor.mjs CHANGED
@@ -1,15 +1,32 @@
1
1
  #!/usr/bin/env node
2
2
  // tui/doctor.mjs — Interactive triflux doctor TUI
3
3
  import { execFileSync } from "node:child_process";
4
- import { existsSync, readFileSync, unlinkSync, readdirSync } from "node:fs";
5
- import { join, dirname } from "node:path";
4
+ import { existsSync, readdirSync, readFileSync, unlinkSync } from "node:fs";
6
5
  import { homedir } from "node:os";
6
+ import { dirname, join } from "node:path";
7
7
  import { fileURLToPath } from "node:url";
8
8
  import {
9
- clear, box, table, divider, label, ok, warn, fail, info,
10
- select, confirm, spinner, sleep,
11
- RESET, DIM, BOLD, CYAN, AMBER, GREEN, RED, YELLOW, WHITE, GRAY,
12
- onExit, showCursor,
9
+ BOLD,
10
+ box,
11
+ clear,
12
+ confirm,
13
+ DIM,
14
+ divider,
15
+ fail,
16
+ GRAY,
17
+ GREEN,
18
+ info,
19
+ label,
20
+ ok,
21
+ onExit,
22
+ RED,
23
+ RESET,
24
+ select,
25
+ showCursor,
26
+ spinner,
27
+ table,
28
+ warn,
29
+ YELLOW,
13
30
  } from "./core.mjs";
14
31
 
15
32
  const PKG_ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
@@ -75,8 +92,12 @@ function showReport(report) {
75
92
  }
76
93
 
77
94
  console.log();
78
- const statusColor = report.issue_count === 0 ? GREEN : report.issue_count <= 2 ? YELLOW : RED;
79
- label("상태", `${statusColor}${report.issue_count === 0 ? "정상" : `${report.issue_count}개 이슈`}${RESET}`);
95
+ const statusColor =
96
+ report.issue_count === 0 ? GREEN : report.issue_count <= 2 ? YELLOW : RED;
97
+ label(
98
+ "상태",
99
+ `${statusColor}${report.issue_count === 0 ? "정상" : `${report.issue_count}개 이슈`}${RESET}`,
100
+ );
80
101
  label("모드", report.mode);
81
102
  console.log();
82
103
 
@@ -84,12 +105,17 @@ function showReport(report) {
84
105
  const rows = (report.checks || []).map((c) => {
85
106
  let note = "";
86
107
  if (c.version) note = `v${c.version}`;
87
- if (c.missing_profiles?.length) note = `누락: ${c.missing_profiles.join(", ")}`;
108
+ if (c.missing_profiles?.length)
109
+ note = `누락: ${c.missing_profiles.join(", ")}`;
88
110
  if (c.fix) note += note ? ` → ${c.fix}` : c.fix;
89
111
  if (c.path && !c.fix) note = c.path;
90
112
 
91
- const icon = c.status === "ok" ? statusIcon("ok") :
92
- c.optional ? statusIcon("optional_missing") : statusIcon(c.status);
113
+ const icon =
114
+ c.status === "ok"
115
+ ? statusIcon("ok")
116
+ : c.optional
117
+ ? statusIcon("optional_missing")
118
+ : statusIcon(c.status);
93
119
 
94
120
  return [
95
121
  `${icon} ${c.name}`,
@@ -105,8 +131,11 @@ function showReport(report) {
105
131
  console.log();
106
132
  info(`수행된 작업: ${report.actions.length}개`);
107
133
  for (const action of report.actions) {
108
- const icon = action.status === "ok" ? `${GREEN}✓${RESET}` : `${RED}✗${RESET}`;
109
- console.log(` ${icon} ${action.type}: ${action.name || action.path || ""}`);
134
+ const icon =
135
+ action.status === "ok" ? `${GREEN}✓${RESET}` : `${RED}✗${RESET}`;
136
+ console.log(
137
+ ` ${icon} ${action.type}: ${action.name || action.path || ""}`,
138
+ );
110
139
  }
111
140
  }
112
141
  }
@@ -119,12 +148,16 @@ function getCacheStatus() {
119
148
  const fp = join(CACHE_DIR, name);
120
149
  if (existsSync(fp)) {
121
150
  let size = 0;
122
- try { size = readFileSync(fp).length; } catch {}
151
+ try {
152
+ size = readFileSync(fp).length;
153
+ } catch {}
123
154
  let hasError = false;
124
155
  try {
125
156
  const parsed = JSON.parse(readFileSync(fp, "utf8"));
126
157
  hasError = !!parsed.error;
127
- } catch { hasError = true; }
158
+ } catch {
159
+ hasError = true;
160
+ }
128
161
  results.push({ name, desc, exists: true, size, hasError });
129
162
  } else {
130
163
  results.push({ name, desc, exists: false, size: 0, hasError: false });
@@ -187,7 +220,8 @@ async function selectiveReset() {
187
220
 
188
221
  if (targets.length === 0) return;
189
222
 
190
- if (!(await confirm(`${targets.length}개 캐시 파일을 삭제하시겠습니까?`))) return;
223
+ if (!(await confirm(`${targets.length}개 캐시 파일을 삭제하시겠습니까?`)))
224
+ return;
191
225
 
192
226
  let deleted = 0;
193
227
  for (const c of targets) {
@@ -227,9 +261,15 @@ async function checkOrphanTeams() {
227
261
  const spin = spinner("팀 정리 중...");
228
262
  try {
229
263
  // Delegate to triflux's cleanup
230
- execFileSync(process.execPath, [join(PKG_ROOT, "bin", "triflux.mjs"), "doctor", "--fix"], {
231
- timeout: 30000, stdio: "ignore", windowsHide: true,
232
- });
264
+ execFileSync(
265
+ process.execPath,
266
+ [join(PKG_ROOT, "bin", "triflux.mjs"), "doctor", "--fix"],
267
+ {
268
+ timeout: 30000,
269
+ stdio: "ignore",
270
+ windowsHide: true,
271
+ },
272
+ );
233
273
  spin.stop();
234
274
  ok("팀 정리 완료");
235
275
  } catch {
@@ -242,12 +282,12 @@ async function checkOrphanTeams() {
242
282
  // ── Main Menu ──
243
283
 
244
284
  const MENU = [
245
- { label: "진단 (Diagnose)", hint: "읽기 전용 검사" },
246
- { label: "수정 (Fix)", hint: "자동 수정 + 진단" },
247
- { label: "캐시 관리 (Cache)", hint: "캐시 조회/선택 삭제" },
248
- { label: "팀 세션 정리 (Teams)", hint: "잔존 팀 감지/정리" },
249
- { label: "전체 초기화 (Reset)", hint: "캐시 전체 삭제 + 재생성" },
250
- { label: "종료", hint: "Ctrl+C" },
285
+ { label: "진단 (Diagnose)", hint: "읽기 전용 검사" },
286
+ { label: "수정 (Fix)", hint: "자동 수정 + 진단" },
287
+ { label: "캐시 관리 (Cache)", hint: "캐시 조회/선택 삭제" },
288
+ { label: "팀 세션 정리 (Teams)", hint: "잔존 팀 감지/정리" },
289
+ { label: "전체 초기화 (Reset)", hint: "캐시 전체 삭제 + 재생성" },
290
+ { label: "종료", hint: "Ctrl+C" },
251
291
  ];
252
292
 
253
293
  async function main() {
@@ -299,12 +339,19 @@ async function main() {
299
339
  }
300
340
 
301
341
  case 4: {
302
- if (!(await confirm(`${RED}전체 캐시를 초기화${RESET}하시겠습니까?`, false))) break;
342
+ if (
343
+ !(await confirm(
344
+ `${RED}전체 캐시를 초기화${RESET}하시겠습니까?`,
345
+ false,
346
+ ))
347
+ )
348
+ break;
303
349
  const spin = spinner("초기화 + 재생성 중...");
304
350
  try {
305
- execFileSync(process.execPath,
351
+ execFileSync(
352
+ process.execPath,
306
353
  [join(PKG_ROOT, "bin", "triflux.mjs"), "doctor", "--reset"],
307
- { timeout: 60000, encoding: "utf8", windowsHide: true }
354
+ { timeout: 60000, encoding: "utf8", windowsHide: true },
308
355
  );
309
356
  spin.stop();
310
357
  ok("전체 초기화 + 재생성 완료");
@@ -1,36 +1,66 @@
1
1
  #!/usr/bin/env node
2
2
  // tui/gemini-profile.mjs — Interactive Gemini Profile Manager
3
3
  // Codex config.toml 대칭 구조 — JSON 기반 프로필 CRUD
4
- import { readFileSync, writeFileSync, existsSync, mkdirSync, copyFileSync } from "node:fs";
5
- import { join } from "node:path";
4
+ import {
5
+ copyFileSync,
6
+ existsSync,
7
+ mkdirSync,
8
+ readFileSync,
9
+ writeFileSync,
10
+ } from "node:fs";
6
11
  import { homedir } from "node:os";
12
+ import { join } from "node:path";
7
13
  import {
8
- clear, box, table, divider, label, ok, warn, fail, info,
9
- select, confirm, input, spinner,
10
- RESET, DIM, BOLD, CYAN, AMBER, GREEN, RED, YELLOW, WHITE, GRAY,
11
- onExit, showCursor,
14
+ BOLD,
15
+ box,
16
+ CYAN,
17
+ clear,
18
+ confirm,
19
+ DIM,
20
+ divider,
21
+ fail,
22
+ GREEN,
23
+ info,
24
+ input,
25
+ label,
26
+ ok,
27
+ onExit,
28
+ RED,
29
+ RESET,
30
+ select,
31
+ showCursor,
32
+ table,
33
+ WHITE,
34
+ warn,
35
+ YELLOW,
12
36
  } from "./core.mjs";
13
37
 
14
38
  const GEMINI_DIR = join(homedir(), ".gemini");
15
39
  const CONFIG_PATH = join(GEMINI_DIR, "triflux-profiles.json");
16
40
 
17
41
  const KNOWN_MODELS = [
18
- { label: "gemini-3.1-pro-preview", hint: "3.1 Pro — 플래그십" },
19
- { label: "gemini-3-flash-preview", hint: "3.0 Flash — 빠른 응답" },
20
- { label: "gemini-2.5-pro", hint: "2.5 Pro — 안정" },
21
- { label: "gemini-2.5-flash", hint: "2.5 Flash — 경량" },
22
- { label: "gemini-2.5-flash-lite", hint: "2.5 Flash Lite — 최경량" },
23
- { label: "직접 입력", hint: "" },
42
+ { label: "gemini-3.1-pro-preview", hint: "3.1 Pro — 플래그십" },
43
+ { label: "gemini-3-flash-preview", hint: "3.0 Flash — 빠른 응답" },
44
+ { label: "gemini-2.5-pro", hint: "2.5 Pro — 안정" },
45
+ { label: "gemini-2.5-flash", hint: "2.5 Flash — 경량" },
46
+ { label: "gemini-2.5-flash-lite", hint: "2.5 Flash Lite — 최경량" },
47
+ { label: "직접 입력", hint: "" },
24
48
  ];
25
49
 
26
50
  const DEFAULT_CONFIG = {
27
51
  model: "gemini-3.1-pro-preview",
28
52
  profiles: {
29
- pro31: { model: "gemini-3.1-pro-preview", hint: "3.1 Pro — 플래그십 (1M ctx, 멀티모달)" },
30
- flash3: { model: "gemini-3-flash-preview", hint: "3.0 Flash — 빠른 응답, 비용 효율" },
31
- pro25: { model: "gemini-2.5-pro", hint: "2.5 Pro — 안정 (추론 강화)" },
32
- flash25: { model: "gemini-2.5-flash", hint: "2.5 Flash — 경량 범용" },
33
- lite25: { model: "gemini-2.5-flash-lite", hint: "2.5 Flash Lite — 최경량" },
53
+ pro31: {
54
+ model: "gemini-3.1-pro-preview",
55
+ hint: "3.1 Pro — 플래그십 (1M ctx, 멀티모달)",
56
+ },
57
+ flash3: {
58
+ model: "gemini-3-flash-preview",
59
+ hint: "3.0 Flash — 빠른 응답, 비용 효율",
60
+ },
61
+ pro25: { model: "gemini-2.5-pro", hint: "2.5 Pro — 안정 (추론 강화)" },
62
+ flash25: { model: "gemini-2.5-flash", hint: "2.5 Flash — 경량 범용" },
63
+ lite25: { model: "gemini-2.5-flash-lite", hint: "2.5 Flash Lite — 최경량" },
34
64
  },
35
65
  };
36
66
 
@@ -88,7 +118,9 @@ function modelColor(model) {
88
118
 
89
119
  async function pickModel(current) {
90
120
  const idx = KNOWN_MODELS.findIndex((m) => m.label === current);
91
- const choice = await select("모델 선택", KNOWN_MODELS, { initial: Math.max(0, idx) });
121
+ const choice = await select("모델 선택", KNOWN_MODELS, {
122
+ initial: Math.max(0, idx),
123
+ });
92
124
  if (!choice) return null;
93
125
  if (choice.value.label === "직접 입력") {
94
126
  return await input("모델 ID", current || "");
@@ -125,7 +157,10 @@ async function editProfile(config) {
125
157
 
126
158
  if (!(await confirm("저장하시겠습니까?"))) return config;
127
159
 
128
- config.profiles[profile.name] = { model: newModel, hint: hint || profile.hint };
160
+ config.profiles[profile.name] = {
161
+ model: newModel,
162
+ hint: hint || profile.hint,
163
+ };
129
164
  save(config);
130
165
  ok(`${profile.name} 프로필 저장 완료`);
131
166
  return readConfig();
@@ -184,7 +219,9 @@ async function removeProfile(config) {
184
219
  if (!picked) return config;
185
220
 
186
221
  const name = profiles[picked.index].name;
187
- if (!(await confirm(`${RED}${name}${RESET} 프로필을 삭제하시겠습니까?`, false))) {
222
+ if (
223
+ !(await confirm(`${RED}${name}${RESET} 프로필을 삭제하시겠습니까?`, false))
224
+ ) {
188
225
  return config;
189
226
  }
190
227
 
@@ -208,11 +245,11 @@ function save(config) {
208
245
  // ── Main Loop ──
209
246
 
210
247
  const MENU = [
211
- { label: "프로필 모델 변경", hint: "모델 수정" },
212
- { label: "기본 모델 변경", hint: "top-level default" },
213
- { label: "프로필 추가", hint: "새 프로필 생성" },
214
- { label: "프로필 삭제", hint: "기존 프로필 제거" },
215
- { label: "종료", hint: "Ctrl+C" },
248
+ { label: "프로필 모델 변경", hint: "모델 수정" },
249
+ { label: "기본 모델 변경", hint: "top-level default" },
250
+ { label: "프로필 추가", hint: "새 프로필 생성" },
251
+ { label: "프로필 삭제", hint: "기존 프로필 제거" },
252
+ { label: "종료", hint: "Ctrl+C" },
216
253
  ];
217
254
 
218
255
  async function main() {
@@ -236,10 +273,18 @@ async function main() {
236
273
 
237
274
  console.log();
238
275
  switch (choice.index) {
239
- case 0: config = await editProfile(config); break;
240
- case 1: config = await editDefault(config); break;
241
- case 2: config = await addProfile(config); break;
242
- case 3: config = await removeProfile(config); break;
276
+ case 0:
277
+ config = await editProfile(config);
278
+ break;
279
+ case 1:
280
+ config = await editDefault(config);
281
+ break;
282
+ case 2:
283
+ config = await addProfile(config);
284
+ break;
285
+ case 3:
286
+ config = await removeProfile(config);
287
+ break;
243
288
  }
244
289
 
245
290
  console.log();
@@ -1,6 +1,6 @@
1
1
  import { readdirSync, readFileSync, unlinkSync } from "node:fs";
2
- import { join } from "node:path";
3
2
  import { get as httpGet } from "node:http";
3
+ import { join } from "node:path";
4
4
 
5
5
  const HUB_URL = "http://127.0.0.1:27888";
6
6
  const AGENT_FILE_PREFIX = "tfx-agent-";
@@ -126,10 +126,14 @@ function fetchHubStatus(hubUrl = HUB_URL, deps = {}) {
126
126
  const req = get(buildStatusUrl(hubUrl), (res) => {
127
127
  let body = "";
128
128
  res.setEncoding?.("utf8");
129
- res.on("data", (chunk) => { body += chunk; });
129
+ res.on("data", (chunk) => {
130
+ body += chunk;
131
+ });
130
132
  res.on("end", () => {
131
133
  try {
132
- finish(res.statusCode === 200 ? parseStatus(body) : { online: false });
134
+ finish(
135
+ res.statusCode === 200 ? parseStatus(body) : { online: false },
136
+ );
133
137
  } catch {
134
138
  finish({ online: false });
135
139
  }
@@ -145,4 +149,4 @@ function fetchHubStatus(hubUrl = HUB_URL, deps = {}) {
145
149
  });
146
150
  }
147
151
 
148
- export { pollAgents, fetchHubStatus };
152
+ export { fetchHubStatus, pollAgents };
package/tui/monitor.mjs CHANGED
@@ -26,7 +26,9 @@ function clamp(value, min, max) {
26
26
 
27
27
  function pad(text, width) {
28
28
  const value = String(text ?? "");
29
- return value.length >= width ? value : value + " ".repeat(width - value.length);
29
+ return value.length >= width
30
+ ? value
31
+ : value + " ".repeat(width - value.length);
30
32
  }
31
33
 
32
34
  function formatElapsed(ms) {
@@ -34,7 +36,8 @@ function formatElapsed(ms) {
34
36
  const hours = Math.floor(total / 3600);
35
37
  const minutes = Math.floor((total % 3600) / 60);
36
38
  const seconds = total % 60;
37
- if (hours > 0) return `${hours}:${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
39
+ if (hours > 0)
40
+ return `${hours}:${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
38
41
  return `${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
39
42
  }
40
43
 
@@ -49,20 +52,25 @@ function escapePs(value) {
49
52
  }
50
53
 
51
54
  function sanitizeTitle(value, fallback = "agent") {
52
- const safe = String(value || "").replace(/[\r\n<>:"/\\|?*\x00-\x1f]/g, " ").trim();
55
+ const safe = String(value || "")
56
+ .replace(/[\r\n<>:"/\\|?*\x00-\x1f]/g, " ")
57
+ .trim();
53
58
  return safe || fallback;
54
59
  }
55
60
 
56
61
  function stripUnsafeText(value) {
57
- return String(value || "").replace(/[\r\n\t]+/g, " ").trim();
62
+ return String(value || "")
63
+ .replace(/[\r\n\t]+/g, " ")
64
+ .trim();
58
65
  }
59
66
 
60
67
  function buildOpenCommand(agent) {
61
68
  const sessionName = escapePs(agent.agent || "");
62
69
  const pid = Number(agent.pid) || 0;
63
- const processInfo = pid > 0
64
- ? `Get-Process -Id ${pid} -ErrorAction SilentlyContinue | Format-List Id,ProcessName,StartTime`
65
- : "Write-Host 'PID 정보가 없습니다.'";
70
+ const processInfo =
71
+ pid > 0
72
+ ? `Get-Process -Id ${pid} -ErrorAction SilentlyContinue | Format-List Id,ProcessName,StartTime`
73
+ : "Write-Host 'PID 정보가 없습니다.'";
66
74
  return [
67
75
  "$ErrorActionPreference = 'Continue'",
68
76
  `if (Get-Command psmux -ErrorAction SilentlyContinue) { try { psmux attach-session -t '${sessionName}' } catch { Write-Host 'psmux attach 실패:' $_.Exception.Message } }`,
@@ -79,7 +87,9 @@ function resolveRatio(agent, maxElapsed) {
79
87
  export function createMonitor(opts = {}) {
80
88
  const stream = opts.stream || process.stdout;
81
89
  const input = opts.input || process.stdin;
82
- const refreshMs = Number.isFinite(opts.refreshMs) ? Math.max(0, opts.refreshMs) : 1000;
90
+ const refreshMs = Number.isFinite(opts.refreshMs)
91
+ ? Math.max(0, opts.refreshMs)
92
+ : 1000;
83
93
  const deps = {
84
94
  pollAgents,
85
95
  fetchHubStatus,
@@ -116,25 +126,45 @@ export function createMonitor(opts = {}) {
116
126
  return false;
117
127
  }
118
128
 
119
- const title = sanitizeTitle(`tfx ${agent.agent || agent.cli || agent.pid}`, "tfx-agent");
129
+ const title = sanitizeTitle(
130
+ `tfx ${agent.agent || agent.cli || agent.pid}`,
131
+ "tfx-agent",
132
+ );
120
133
  const command = buildOpenCommand(agent);
121
134
 
122
135
  try {
123
136
  try {
124
- const { createWtManager } = await deps.importModule("../hub/team/wt-manager.mjs");
137
+ const { createWtManager } = await deps.importModule(
138
+ "../hub/team/wt-manager.mjs",
139
+ );
125
140
  const manager = createWtManager();
126
- await manager.createTab({ title, command, cwd: process.cwd(), profile: "triflux" });
127
- } catch {
128
- const child = deps.spawn("wt.exe", [
129
- "-w", "new", "nt",
130
- "--title", title,
131
- "--",
132
- "powershell.exe", "-NoExit", "-Command", command,
133
- ], {
134
- detached: true,
135
- stdio: "ignore",
136
- windowsHide: false,
141
+ await manager.createTab({
142
+ title,
143
+ command,
144
+ cwd: process.cwd(),
145
+ profile: "triflux",
137
146
  });
147
+ } catch {
148
+ const child = deps.spawn(
149
+ "wt.exe",
150
+ [
151
+ "-w",
152
+ "new",
153
+ "nt",
154
+ "--title",
155
+ title,
156
+ "--",
157
+ "powershell.exe",
158
+ "-NoExit",
159
+ "-Command",
160
+ command,
161
+ ],
162
+ {
163
+ detached: true,
164
+ stdio: "ignore",
165
+ windowsHide: false,
166
+ },
167
+ );
138
168
  child?.unref?.();
139
169
  }
140
170
  statusMessage = `${GREEN}${stripUnsafeText(agent.agent || "agent")} 열기 시도 완료${RESET}`;
@@ -152,11 +182,17 @@ export function createMonitor(opts = {}) {
152
182
  ]);
153
183
 
154
184
  agents = Array.isArray(nextAgents) ? nextAgents : [];
155
- hubStatus = nextHubStatus && typeof nextHubStatus === "object" ? nextHubStatus : { online: false };
185
+ hubStatus =
186
+ nextHubStatus && typeof nextHubStatus === "object"
187
+ ? nextHubStatus
188
+ : { online: false };
156
189
  syncCursor();
157
190
 
158
191
  const width = viewportWidth();
159
- const maxElapsed = agents.reduce((max, agent) => Math.max(max, Number(agent.elapsed) || 0), 0);
192
+ const maxElapsed = agents.reduce(
193
+ (max, agent) => Math.max(max, Number(agent.elapsed) || 0),
194
+ 0,
195
+ );
160
196
  const hubLabel = hubStatus.online
161
197
  ? `${GREEN}online${RESET}`
162
198
  : `${RED}offline${RESET}`;
@@ -177,10 +213,14 @@ export function createMonitor(opts = {}) {
177
213
  const cli = stripUnsafeText(agent.cli || "unknown");
178
214
  const name = stripUnsafeText(agent.agent || `pid:${agent.pid || "?"}`);
179
215
  const elapsed = formatElapsed(agent.elapsed);
180
- const alive = agent.alive ? `${GREEN}alive${RESET}` : `${RED}dead${RESET}`;
216
+ const alive = agent.alive
217
+ ? `${GREEN}alive${RESET}`
218
+ : `${RED}dead${RESET}`;
181
219
  const left = `${marker} ${colorCli(cli)}${cli}${RESET} ${BOLD}${name}${RESET} ${GRAY}${elapsed}${RESET}`;
182
220
  if (hubStatus.online) {
183
- lines.push(`${left} ${BLUE}${progressBar(resolveRatio(agent, maxElapsed))}${RESET}`);
221
+ lines.push(
222
+ `${left} ${BLUE}${progressBar(resolveRatio(agent, maxElapsed))}${RESET}`,
223
+ );
184
224
  } else {
185
225
  lines.push(`${left} ${alive}`);
186
226
  }
@@ -198,7 +238,10 @@ export function createMonitor(opts = {}) {
198
238
  }
199
239
 
200
240
  if (statusMessage) lines.push("", statusMessage);
201
- lines.push("", `${DIM}[Enter] open [j/k] select [r] refresh [h] help [q] quit${RESET}`);
241
+ lines.push(
242
+ "",
243
+ `${DIM}[Enter] open [j/k] select [r] refresh [h] help [q] quit${RESET}`,
244
+ );
202
245
 
203
246
  write("\x1b[H");
204
247
  write(lines.join("\n"));
@@ -210,7 +253,8 @@ export function createMonitor(opts = {}) {
210
253
  const name = key?.name || "";
211
254
  if (str === "j" || name === "down") {
212
255
  syncCursor();
213
- cursor = agents.length === 0 ? 0 : Math.min(cursor + 1, agents.length - 1);
256
+ cursor =
257
+ agents.length === 0 ? 0 : Math.min(cursor + 1, agents.length - 1);
214
258
  await renderFrame();
215
259
  return;
216
260
  }