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/hud/terminal.mjs CHANGED
@@ -2,51 +2,111 @@
2
2
  // 터미널 감지 / 4-Tier 적응형 렌더링
3
3
  // ============================================================================
4
4
  import { execSync } from "node:child_process";
5
- import { DIM, RESET, GAUGE_WIDTH, coloredBar } from "./colors.mjs";
5
+ import { coloredBar, DIM, GAUGE_WIDTH, RESET } from "./colors.mjs";
6
+ import {
7
+ COMPACT_COLS_THRESHOLD,
8
+ HUD_CONFIG_PATH,
9
+ MINIMAL_COLS_THRESHOLD,
10
+ } from "./constants.mjs";
6
11
  import { readJson } from "./utils.mjs";
7
- import { HUD_CONFIG_PATH, COMPACT_COLS_THRESHOLD, MINIMAL_COLS_THRESHOLD } from "./constants.mjs";
8
12
 
9
13
  let _cachedColumns = 0;
10
14
  export function getTerminalColumns() {
11
15
  if (_cachedColumns > 0) return _cachedColumns;
12
16
  const envCols = Number(process.env.COLUMNS);
13
- if (envCols > 0) { _cachedColumns = envCols; return _cachedColumns; }
14
- if (process.stdout.columns) { _cachedColumns = process.stdout.columns; return _cachedColumns; }
15
- if (process.stderr.columns) { _cachedColumns = process.stderr.columns; return _cachedColumns; }
17
+ if (envCols > 0) {
18
+ _cachedColumns = envCols;
19
+ return _cachedColumns;
20
+ }
21
+ if (process.stdout.columns) {
22
+ _cachedColumns = process.stdout.columns;
23
+ return _cachedColumns;
24
+ }
25
+ if (process.stderr.columns) {
26
+ _cachedColumns = process.stderr.columns;
27
+ return _cachedColumns;
28
+ }
16
29
  try {
17
30
  if (process.platform === "win32") {
18
- const raw = execSync("mode con", { timeout: 2000, stdio: ["pipe", "pipe", "pipe"], windowsHide: true }).toString();
19
- const m = raw.match(/Columns[^:]*:\s*(\d+)/i) || raw.match(/열[^:]*:\s*(\d+)/);
20
- if (m) { _cachedColumns = Number(m[1]); return _cachedColumns; }
31
+ const raw = execSync("mode con", {
32
+ timeout: 2000,
33
+ stdio: ["pipe", "pipe", "pipe"],
34
+ windowsHide: true,
35
+ }).toString();
36
+ const m =
37
+ raw.match(/Columns[^:]*:\s*(\d+)/i) || raw.match(/열[^:]*:\s*(\d+)/);
38
+ if (m) {
39
+ _cachedColumns = Number(m[1]);
40
+ return _cachedColumns;
41
+ }
21
42
  } else {
22
- const raw = execSync("tput cols 2>/dev/null || stty size 2>/dev/null | awk '{print $2}'", {
23
- timeout: 2000, stdio: ["pipe", "pipe", "pipe"],
24
- }).toString().trim();
25
- if (raw && !isNaN(Number(raw))) { _cachedColumns = Number(raw); return _cachedColumns; }
43
+ const raw = execSync(
44
+ "tput cols 2>/dev/null || stty size 2>/dev/null | awk '{print $2}'",
45
+ {
46
+ timeout: 2000,
47
+ stdio: ["pipe", "pipe", "pipe"],
48
+ },
49
+ )
50
+ .toString()
51
+ .trim();
52
+ if (raw && !Number.isNaN(Number(raw))) {
53
+ _cachedColumns = Number(raw);
54
+ return _cachedColumns;
55
+ }
26
56
  }
27
- } catch { /* 감지 실패 */ }
57
+ } catch {
58
+ /* 감지 실패 */
59
+ }
28
60
  return 0;
29
61
  }
30
62
 
31
63
  let _cachedRows = 0;
32
64
  export function getTerminalRows() {
33
65
  if (_cachedRows > 0) return _cachedRows;
34
- if (process.stdout.rows) { _cachedRows = process.stdout.rows; return _cachedRows; }
35
- if (process.stderr.rows) { _cachedRows = process.stderr.rows; return _cachedRows; }
66
+ if (process.stdout.rows) {
67
+ _cachedRows = process.stdout.rows;
68
+ return _cachedRows;
69
+ }
70
+ if (process.stderr.rows) {
71
+ _cachedRows = process.stderr.rows;
72
+ return _cachedRows;
73
+ }
36
74
  const envLines = Number(process.env.LINES);
37
- if (envLines > 0) { _cachedRows = envLines; return _cachedRows; }
75
+ if (envLines > 0) {
76
+ _cachedRows = envLines;
77
+ return _cachedRows;
78
+ }
38
79
  try {
39
80
  if (process.platform === "win32") {
40
- const raw = execSync("mode con", { timeout: 2000, stdio: ["pipe", "pipe", "pipe"], windowsHide: true }).toString();
41
- const m = raw.match(/Lines[^:]*:\s*(\d+)/i) || raw.match(/줄[^:]*:\s*(\d+)/);
42
- if (m) { _cachedRows = Number(m[1]); return _cachedRows; }
81
+ const raw = execSync("mode con", {
82
+ timeout: 2000,
83
+ stdio: ["pipe", "pipe", "pipe"],
84
+ windowsHide: true,
85
+ }).toString();
86
+ const m =
87
+ raw.match(/Lines[^:]*:\s*(\d+)/i) || raw.match(/줄[^:]*:\s*(\d+)/);
88
+ if (m) {
89
+ _cachedRows = Number(m[1]);
90
+ return _cachedRows;
91
+ }
43
92
  } else {
44
- const raw = execSync("tput lines 2>/dev/null || stty size 2>/dev/null | awk '{print $1}'", {
45
- timeout: 2000, stdio: ["pipe", "pipe", "pipe"],
46
- }).toString().trim();
47
- if (raw && !isNaN(Number(raw))) { _cachedRows = Number(raw); return _cachedRows; }
93
+ const raw = execSync(
94
+ "tput lines 2>/dev/null || stty size 2>/dev/null | awk '{print $1}'",
95
+ {
96
+ timeout: 2000,
97
+ stdio: ["pipe", "pipe", "pipe"],
98
+ },
99
+ )
100
+ .toString()
101
+ .trim();
102
+ if (raw && !Number.isNaN(Number(raw))) {
103
+ _cachedRows = Number(raw);
104
+ return _cachedRows;
105
+ }
48
106
  }
49
- } catch { /* 감지 실패 */ }
107
+ } catch {
108
+ /* 감지 실패 */
109
+ }
50
110
  return 0;
51
111
  }
52
112
 
@@ -60,12 +120,15 @@ export function detectCompactMode() {
60
120
  if (process.env.OMC_HUD_COMPACT === "0") return false;
61
121
  // 3. 설정 파일 (~/.omc/config/hud.json)
62
122
  const hudConfig = readJson(HUD_CONFIG_PATH, null);
63
- if (hudConfig?.compact === true || hudConfig?.compact === "always") return true;
64
- if (hudConfig?.compact === false || hudConfig?.compact === "never") return false;
123
+ if (hudConfig?.compact === true || hudConfig?.compact === "always")
124
+ return true;
125
+ if (hudConfig?.compact === false || hudConfig?.compact === "never")
126
+ return false;
65
127
  // 4. maxLines < 3이면 자동 컴팩트 (알림 배너 공존 대응)
66
128
  if (Number(hudConfig?.lines) > 0 && Number(hudConfig?.lines) < 3) return true;
67
129
  // 5. 터미널 폭 자동 감지 (TTY 있을 때만 유효)
68
- const threshold = Number(hudConfig?.compactThreshold) || COMPACT_COLS_THRESHOLD;
130
+ const threshold =
131
+ Number(hudConfig?.compactThreshold) || COMPACT_COLS_THRESHOLD;
69
132
  const cols = getTerminalColumns();
70
133
  if (cols > 0 && cols < threshold) return true;
71
134
  return false;
@@ -95,7 +158,8 @@ export function selectTier(stdin, claudeUsage = null) {
95
158
 
96
159
  // 1) 명시적 tier 강제 설정
97
160
  const forcedTier = hudConfig?.tier;
98
- if (["full", "compact", "minimal", "micro", "nano"].includes(forcedTier)) return forcedTier;
161
+ if (["full", "compact", "minimal", "micro", "nano"].includes(forcedTier))
162
+ return forcedTier;
99
163
 
100
164
  // 1.5) maxLines=1 → nano (1줄 모드: 알림 배너/분할화면 대응)
101
165
  if (Number(hudConfig?.lines) === 1) return "nano";
@@ -123,14 +187,20 @@ export function selectTier(stdin, claudeUsage = null) {
123
187
 
124
188
  // full tier 전용: 게이지 바 접두사 (normal 이하 tier에서는 빈 문자열)
125
189
  export function tierBar(currentTier, percent, baseColor = null) {
126
- return currentTier === "full" ? coloredBar(percent, GAUGE_WIDTH, baseColor) + " " : "";
190
+ return currentTier === "full"
191
+ ? coloredBar(percent, GAUGE_WIDTH, baseColor) + " "
192
+ : "";
127
193
  }
128
194
  export function tierDimBar(currentTier) {
129
- return currentTier === "full" ? DIM + "░".repeat(GAUGE_WIDTH) + RESET + " " : "";
195
+ return currentTier === "full"
196
+ ? DIM + "░".repeat(GAUGE_WIDTH) + RESET + " "
197
+ : "";
130
198
  }
131
199
  // Gemini ∞% 전용: 무한 쿼터이므로 dim 회색 바
132
200
  export function tierInfBar(currentTier) {
133
- return currentTier === "full" ? DIM + "█".repeat(GAUGE_WIDTH) + RESET + " " : "";
201
+ return currentTier === "full"
202
+ ? DIM + "█".repeat(GAUGE_WIDTH) + RESET + " "
203
+ : "";
134
204
  }
135
205
 
136
206
  // 테스트 지원: 캐시 초기화
package/hud/utils.mjs CHANGED
@@ -1,11 +1,14 @@
1
1
  // ============================================================================
2
2
  // 유틸리티 함수
3
3
  // ============================================================================
4
- import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
5
- import { dirname } from "node:path";
4
+
6
5
  import { createHash } from "node:crypto";
6
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
7
+ import { dirname } from "node:path";
7
8
  import {
8
- PERCENT_CELL_WIDTH, TIME_CELL_INNER_WIDTH, SV_CELL_WIDTH,
9
+ PERCENT_CELL_WIDTH,
10
+ SV_CELL_WIDTH,
11
+ TIME_CELL_INNER_WIDTH,
9
12
  } from "./constants.mjs";
10
13
 
11
14
  export async function readStdinJson() {
@@ -20,8 +23,15 @@ export async function readStdinJson() {
20
23
  process.stdin.on("end", () => {
21
24
  clearTimeout(timeout);
22
25
  const raw = chunks.join("").trim();
23
- if (!raw) { resolve({}); return; }
24
- try { resolve(JSON.parse(raw)); } catch { resolve({}); }
26
+ if (!raw) {
27
+ resolve({});
28
+ return;
29
+ }
30
+ try {
31
+ resolve(JSON.parse(raw));
32
+ } catch {
33
+ resolve({});
34
+ }
25
35
  });
26
36
  process.stdin.on("error", () => {
27
37
  clearTimeout(timeout);
@@ -33,14 +43,20 @@ export async function readStdinJson() {
33
43
 
34
44
  export function readJson(filePath, fallback) {
35
45
  if (!existsSync(filePath)) return fallback;
36
- try { return JSON.parse(readFileSync(filePath, "utf-8")); } catch { return fallback; }
46
+ try {
47
+ return JSON.parse(readFileSync(filePath, "utf-8"));
48
+ } catch {
49
+ return fallback;
50
+ }
37
51
  }
38
52
 
39
53
  export function writeJsonSafe(filePath, data) {
40
54
  try {
41
55
  mkdirSync(dirname(filePath), { recursive: true });
42
56
  writeFileSync(filePath, JSON.stringify(data), { mode: 0o600 });
43
- } catch { /* 쓰기 실패 무시 */ }
57
+ } catch {
58
+ /* 쓰기 실패 무시 */
59
+ }
44
60
  }
45
61
 
46
62
  // .omc/ → .claude/cache/ 마이그레이션: 새 경로 우선, 없으면 레거시 읽고 복사
@@ -48,7 +64,10 @@ export function readJsonMigrate(newPath, legacyPath, fallback) {
48
64
  const data = readJson(newPath, null);
49
65
  if (data != null) return data;
50
66
  const legacy = readJson(legacyPath, null);
51
- if (legacy != null) { writeJsonSafe(newPath, legacy); return legacy; }
67
+ if (legacy != null) {
68
+ writeJsonSafe(newPath, legacy);
69
+ return legacy;
70
+ }
52
71
  return fallback;
53
72
  }
54
73
 
@@ -109,7 +128,10 @@ export function truncateAnsi(text, maxWidth) {
109
128
  }
110
129
 
111
130
  export function makeHash(text) {
112
- return createHash("sha256").update(String(text || ""), "utf8").digest("hex").slice(0, 16);
131
+ return createHash("sha256")
132
+ .update(String(text || ""), "utf8")
133
+ .digest("hex")
134
+ .slice(0, 16);
113
135
  }
114
136
 
115
137
  export function clampPercent(value) {
@@ -177,11 +199,13 @@ export function formatTokenCount(n) {
177
199
 
178
200
  export function getContextPercent(stdin) {
179
201
  const nativePercent = stdin?.context_window?.used_percentage;
180
- if (typeof nativePercent === "number" && Number.isFinite(nativePercent)) return clampPercent(nativePercent);
202
+ if (typeof nativePercent === "number" && Number.isFinite(nativePercent))
203
+ return clampPercent(nativePercent);
181
204
  const usage = stdin?.context_window?.current_usage || {};
182
- const totalTokens = Number(usage.input_tokens || 0)
183
- + Number(usage.cache_creation_input_tokens || 0)
184
- + Number(usage.cache_read_input_tokens || 0);
205
+ const totalTokens =
206
+ Number(usage.input_tokens || 0) +
207
+ Number(usage.cache_creation_input_tokens || 0) +
208
+ Number(usage.cache_read_input_tokens || 0);
185
209
  const capacity = Number(stdin?.context_window?.context_window_size || 0);
186
210
  if (!capacity || capacity <= 0) return 0;
187
211
  return clampPercent((totalTokens / capacity) * 100);
@@ -197,9 +221,10 @@ export function advanceToNextCycle(epochMs, cycleMs) {
197
221
 
198
222
  function parseResetDate(isoOrUnix) {
199
223
  if (!isoOrUnix) return null;
200
- const date = typeof isoOrUnix === "string"
201
- ? new Date(isoOrUnix)
202
- : new Date(isoOrUnix * 1000);
224
+ const date =
225
+ typeof isoOrUnix === "string"
226
+ ? new Date(isoOrUnix)
227
+ : new Date(isoOrUnix * 1000);
203
228
  return Number.isNaN(date.getTime()) ? null : date;
204
229
  }
205
230
 
@@ -261,9 +286,13 @@ export function decodeJwtEmail(idToken) {
261
286
  let payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
262
287
  while (payload.length % 4) payload += "=";
263
288
  try {
264
- const decoded = JSON.parse(Buffer.from(payload, "base64").toString("utf-8"));
289
+ const decoded = JSON.parse(
290
+ Buffer.from(payload, "base64").toString("utf-8"),
291
+ );
265
292
  return decoded.email || null;
266
- } catch { return null; }
293
+ } catch {
294
+ return null;
295
+ }
267
296
  }
268
297
 
269
298
  // HTTPS POST (타임아웃 포함) — https 모듈은 호출자가 주입
@@ -272,26 +301,35 @@ export function createHttpsPost(https, timeoutMs) {
272
301
  return new Promise((resolve) => {
273
302
  const urlObj = new URL(url);
274
303
  const data = JSON.stringify(body);
275
- const req = https.request({
276
- hostname: urlObj.hostname,
277
- path: urlObj.pathname + urlObj.search,
278
- method: "POST",
279
- headers: {
280
- "Content-Type": "application/json",
281
- "Authorization": `Bearer ${accessToken}`,
282
- "Content-Length": Buffer.byteLength(data),
304
+ const req = https.request(
305
+ {
306
+ hostname: urlObj.hostname,
307
+ path: urlObj.pathname + urlObj.search,
308
+ method: "POST",
309
+ headers: {
310
+ "Content-Type": "application/json",
311
+ Authorization: `Bearer ${accessToken}`,
312
+ "Content-Length": Buffer.byteLength(data),
313
+ },
314
+ timeout: timeoutMs,
283
315
  },
284
- timeout: timeoutMs,
285
- }, (res) => {
286
- const chunks = [];
287
- res.on("data", (c) => chunks.push(c));
288
- res.on("end", () => {
289
- try { resolve(JSON.parse(Buffer.concat(chunks).toString())); }
290
- catch { resolve(null); }
291
- });
292
- });
316
+ (res) => {
317
+ const chunks = [];
318
+ res.on("data", (c) => chunks.push(c));
319
+ res.on("end", () => {
320
+ try {
321
+ resolve(JSON.parse(Buffer.concat(chunks).toString()));
322
+ } catch {
323
+ resolve(null);
324
+ }
325
+ });
326
+ },
327
+ );
293
328
  req.on("error", () => resolve(null));
294
- req.on("timeout", () => { req.destroy(); resolve(null); });
329
+ req.on("timeout", () => {
330
+ req.destroy();
331
+ resolve(null);
332
+ });
295
333
  req.write(data);
296
334
  req.end();
297
335
  });
@@ -301,8 +339,10 @@ export function createHttpsPost(https, timeoutMs) {
301
339
  // sv 퍼센트 포맷 (1000+ → k 표기, 5자 고정폭)
302
340
  export function formatSvPct(value) {
303
341
  if (value == null) return "--%".padStart(SV_CELL_WIDTH);
304
- if (value >= 10000) return `${Math.round(value / 1000)}k%`.padStart(SV_CELL_WIDTH);
305
- if (value >= 1000) return `${(value / 1000).toFixed(1)}k%`.padStart(SV_CELL_WIDTH);
342
+ if (value >= 10000)
343
+ return `${Math.round(value / 1000)}k%`.padStart(SV_CELL_WIDTH);
344
+ if (value >= 1000)
345
+ return `${(value / 1000).toFixed(1)}k%`.padStart(SV_CELL_WIDTH);
306
346
  return `${value}%`.padStart(SV_CELL_WIDTH);
307
347
  }
308
348
 
package/mesh/index.mjs CHANGED
@@ -1,12 +1,18 @@
1
1
  import { readdirSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
 
4
- export { MSG_TYPES, createMessage, serialize, deserialize, validate } from "./mesh-protocol.mjs";
5
- export { createRegistry } from "./mesh-registry.mjs";
6
4
  export { createMeshBudget } from "./mesh-budget.mjs";
7
- export { routeMessage, routeOrDeadLetter } from "./mesh-router.mjs";
8
- export { createMessageQueue } from "./mesh-queue.mjs";
9
5
  export { createHeartbeatMonitor } from "./mesh-heartbeat.mjs";
6
+ export {
7
+ createMessage,
8
+ deserialize,
9
+ MSG_TYPES,
10
+ serialize,
11
+ validate,
12
+ } from "./mesh-protocol.mjs";
13
+ export { createMessageQueue } from "./mesh-queue.mjs";
14
+ export { createRegistry } from "./mesh-registry.mjs";
15
+ export { routeMessage, routeOrDeadLetter } from "./mesh-router.mjs";
10
16
 
11
17
  /**
12
18
  * Loads skills assigned to a specific agent from a skills directory.
@@ -54,7 +60,7 @@ export async function loadSkillsForAgent(agentId, skillsDir) {
54
60
  continue;
55
61
  }
56
62
 
57
- if (skillContent && skillContent.includes(agentId)) {
63
+ if (skillContent?.includes(agentId)) {
58
64
  skills.push(skillName);
59
65
  }
60
66
  }
@@ -28,9 +28,11 @@ function clampPercent(value) {
28
28
  */
29
29
  function classifyLevel(percent) {
30
30
  const p = clampPercent(percent);
31
- if (p >= WARNING_LEVELS.critical) return { level: "critical", message: "에이전트 분할 또는 세션 교체 권장" };
31
+ if (p >= WARNING_LEVELS.critical)
32
+ return { level: "critical", message: "에이전트 분할 또는 세션 교체 권장" };
32
33
  if (p >= WARNING_LEVELS.warn) return { level: "warn", message: "압축 권장" };
33
- if (p >= WARNING_LEVELS.info) return { level: "info", message: "컨텍스트 절반 이상 사용" };
34
+ if (p >= WARNING_LEVELS.info)
35
+ return { level: "info", message: "컨텍스트 절반 이상 사용" };
34
36
  return { level: "ok", message: "" };
35
37
  }
36
38
 
@@ -78,9 +80,10 @@ export function createMeshBudget() {
78
80
  budgets.set(agentId, updated);
79
81
 
80
82
  const remaining = Math.max(0, updated.allocated - updated.consumed);
81
- const percent = updated.allocated > 0
82
- ? clampPercent((updated.consumed / updated.allocated) * 100)
83
- : 100;
83
+ const percent =
84
+ updated.allocated > 0
85
+ ? clampPercent((updated.consumed / updated.allocated) * 100)
86
+ : 100;
84
87
  const { level } = classifyLevel(percent);
85
88
  return { remaining, percent, level };
86
89
  }
@@ -96,11 +99,17 @@ export function createMeshBudget() {
96
99
  return { allocated: 0, consumed: 0, remaining: 0, level: "ok" };
97
100
  }
98
101
  const remaining = Math.max(0, budget.allocated - budget.consumed);
99
- const percent = budget.allocated > 0
100
- ? clampPercent((budget.consumed / budget.allocated) * 100)
101
- : 0;
102
+ const percent =
103
+ budget.allocated > 0
104
+ ? clampPercent((budget.consumed / budget.allocated) * 100)
105
+ : 0;
102
106
  const { level } = classifyLevel(percent);
103
- return { allocated: budget.allocated, consumed: budget.consumed, remaining, level };
107
+ return {
108
+ allocated: budget.allocated,
109
+ consumed: budget.consumed,
110
+ remaining,
111
+ level,
112
+ };
104
113
  }
105
114
 
106
115
  /**
@@ -48,7 +48,7 @@ export function createHeartbeatMonitor(registry, opts = {}) {
48
48
 
49
49
  for (const agent of registered) {
50
50
  const lastBeat = heartbeats.get(agent.agentId);
51
- if (lastBeat === undefined || (now - lastBeat) >= threshold) {
51
+ if (lastBeat === undefined || now - lastBeat >= threshold) {
52
52
  stale.push(agent.agentId);
53
53
  }
54
54
  }
@@ -10,10 +10,8 @@ const DEFAULT_TTL_MS = 0; // 0 = no expiry
10
10
  * @returns {object} Queue API
11
11
  */
12
12
  export function createMessageQueue(opts = {}) {
13
- const {
14
- maxQueueSize = DEFAULT_MAX_QUEUE_SIZE,
15
- ttlMs = DEFAULT_TTL_MS,
16
- } = opts;
13
+ const { maxQueueSize = DEFAULT_MAX_QUEUE_SIZE, ttlMs = DEFAULT_TTL_MS } =
14
+ opts;
17
15
 
18
16
  /** @type {Map<string, Array<{ message: object, enqueuedAt: number }>>} */
19
17
  const queues = new Map();
@@ -39,7 +37,7 @@ export function createMessageQueue(opts = {}) {
39
37
  */
40
38
  function purgeExpired(q, now) {
41
39
  if (ttlMs <= 0) return;
42
- while (q.length > 0 && (now - q[0].enqueuedAt) > ttlMs) {
40
+ while (q.length > 0 && now - q[0].enqueuedAt > ttlMs) {
43
41
  q.shift();
44
42
  }
45
43
  }
@@ -23,9 +23,7 @@ export function routeMessage(message, registry) {
23
23
  // Broadcast
24
24
  if (to === "*") {
25
25
  const all = registry.listAll();
26
- const targets = all
27
- .map((a) => a.agentId)
28
- .filter((id) => id !== from);
26
+ const targets = all.map((a) => a.agentId).filter((id) => id !== from);
29
27
  if (targets.length === 0) {
30
28
  return { routed: false, reason: "broadcast: no agents registered" };
31
29
  }
@@ -40,7 +38,10 @@ export function routeMessage(message, registry) {
40
38
  }
41
39
  const targets = registry.discover(capability);
42
40
  if (targets.length === 0) {
43
- return { routed: false, reason: `capability: no agents with "${capability}"` };
41
+ return {
42
+ routed: false,
43
+ reason: `capability: no agents with "${capability}"`,
44
+ };
44
45
  }
45
46
  return { routed: true, targets };
46
47
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "triflux",
3
- "version": "10.3.4",
3
+ "version": "10.7.0",
4
4
  "description": "CLI-first multi-model orchestrator for Claude Code — route tasks to Codex, Gemini, and Claude",
5
5
  "type": "module",
6
6
  "bin": {
@@ -50,6 +50,7 @@
50
50
  "test:integration": "node scripts/test-lock.mjs --test --test-force-exit --test-concurrency=8 tests/integration/**/*.test.mjs",
51
51
  "test:route-smoke": "node scripts/test-lock.mjs --test scripts/test-tfx-route-no-claude-native.mjs",
52
52
  "test:contract": "node scripts/test-lock.mjs --test --test-force-exit --test-concurrency=8 tests/contract/**/*.test.mjs",
53
+ "test:coverage": "node --experimental-test-coverage --test-coverage-lines=60 --test-coverage-functions=60 --test --test-force-exit --test-concurrency=8 \"tests/**/*.test.mjs\"",
53
54
  "gen:skill-docs": "node scripts/gen-skill-docs.mjs",
54
55
  "gen:skill-manifest": "node scripts/gen-skill-manifest.mjs"
55
56
  },
@@ -1,5 +1,12 @@
1
1
  import assert from "node:assert/strict";
2
- import { existsSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2
+ import {
3
+ existsSync,
4
+ mkdirSync,
5
+ mkdtempSync,
6
+ readFileSync,
7
+ rmSync,
8
+ writeFileSync,
9
+ } from "node:fs";
3
10
  import { tmpdir } from "node:os";
4
11
  import { join } from "node:path";
5
12
  import { describe, it } from "node:test";
@@ -22,7 +29,11 @@ describe("gen-skill-docs", () => {
22
29
  mkdirSync(templatesDir, { recursive: true });
23
30
  mkdirSync(skillDir, { recursive: true });
24
31
 
25
- writeFileSync(join(templatesDir, "base.md"), "BASE {{SKILL_NAME}}", "utf8");
32
+ writeFileSync(
33
+ join(templatesDir, "base.md"),
34
+ "BASE {{SKILL_NAME}}",
35
+ "utf8",
36
+ );
26
37
  writeFileSync(join(templatesDir, "deep.md"), "DEEP BLOCK", "utf8");
27
38
 
28
39
  writeFileSync(
@@ -40,7 +51,11 @@ describe("gen-skill-docs", () => {
40
51
  "utf8",
41
52
  );
42
53
 
43
- const result = generateSkillDocs({ skillsDir, templatesDir, write: true });
54
+ const result = generateSkillDocs({
55
+ skillsDir,
56
+ templatesDir,
57
+ write: true,
58
+ });
44
59
  assert.equal(result.count, 1);
45
60
 
46
61
  const output = readFileSync(join(skillDir, "SKILL.md"), "utf8");
@@ -62,7 +77,11 @@ describe("gen-skill-docs", () => {
62
77
  mkdirSync(skillDir, { recursive: true });
63
78
 
64
79
  writeFileSync(join(templatesDir, "base.md"), "BASE", "utf8");
65
- writeFileSync(join(templatesDir, "deep.md"), "DEEP {{SKILL_NAME}}", "utf8");
80
+ writeFileSync(
81
+ join(templatesDir, "deep.md"),
82
+ "DEEP {{SKILL_NAME}}",
83
+ "utf8",
84
+ );
66
85
  writeFileSync(
67
86
  join(skillDir, "SKILL.md.tmpl"),
68
87
  [
@@ -99,14 +118,22 @@ describe("gen-skill-docs", () => {
99
118
  mkdirSync(skillDir, { recursive: true });
100
119
 
101
120
  writeFileSync(join(sharedDir, "args.md"), "ARGS={{SKILL_NAME}}", "utf8");
102
- writeFileSync(join(templatesDir, "base.md"), "{{#include shared/args.md}}", "utf8");
121
+ writeFileSync(
122
+ join(templatesDir, "base.md"),
123
+ "{{#include shared/args.md}}",
124
+ "utf8",
125
+ );
103
126
  writeFileSync(
104
127
  join(skillDir, "SKILL.md.tmpl"),
105
128
  "---\nname: tfx-inc\ndescription: inc test\n---\n{{> base}}\nend",
106
129
  "utf8",
107
130
  );
108
131
 
109
- const result = generateSkillDocs({ skillsDir, templatesDir, write: true });
132
+ const result = generateSkillDocs({
133
+ skillsDir,
134
+ templatesDir,
135
+ write: true,
136
+ });
110
137
  assert.equal(result.count, 1);
111
138
 
112
139
  const output = readFileSync(join(skillDir, "SKILL.md"), "utf8");
@@ -145,7 +172,9 @@ describe("gen-skill-manifest", () => {
145
172
  const result = generateSkillManifests({ skillsDir, write: true });
146
173
  assert.equal(result.count, 1);
147
174
 
148
- const manifest = JSON.parse(readFileSync(join(skillDir, "skill.json"), "utf8"));
175
+ const manifest = JSON.parse(
176
+ readFileSync(join(skillDir, "skill.json"), "utf8"),
177
+ );
149
178
  assert.equal(manifest.name, "tfx-manifest-test");
150
179
  assert.equal(manifest.description, "test manifest");
151
180
  assert.deepEqual(manifest.triggers, ["test", "manifest"]);