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,9 +1,9 @@
1
1
  import assert from "node:assert/strict";
2
- import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
2
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
3
  import { tmpdir } from "node:os";
4
4
  import { join } from "node:path";
5
5
  import { describe, it } from "node:test";
6
-
6
+ import { generateSkillDocs } from "../gen-skill-docs.mjs";
7
7
  import {
8
8
  buildSkillTemplateContext,
9
9
  loadSkillManifest,
@@ -12,7 +12,6 @@ import {
12
12
  parseFrontmatterWithManifest,
13
13
  renderSkillTemplate,
14
14
  } from "../lib/skill-template.mjs";
15
- import { generateSkillDocs } from "../gen-skill-docs.mjs";
16
15
 
17
16
  function makeTempDir() {
18
17
  return mkdtempSync(join(tmpdir(), "tfx-skill-template-"));
@@ -30,16 +29,20 @@ describe("skill-template engine", () => {
30
29
  "name={{SKILL_NAME}}",
31
30
  ].join("\n");
32
31
 
33
- const output = renderSkillTemplate(template, {
34
- SKILL_NAME: "tfx-deep-plan",
35
- SKILL_DESCRIPTION: "consensus planner",
36
- DEEP: true,
37
- }, {
38
- partials: {
39
- base: "desc={{SKILL_DESCRIPTION}}",
40
- deep: "deep-enabled",
32
+ const output = renderSkillTemplate(
33
+ template,
34
+ {
35
+ SKILL_NAME: "tfx-deep-plan",
36
+ SKILL_DESCRIPTION: "consensus planner",
37
+ DEEP: true,
41
38
  },
42
- });
39
+ {
40
+ partials: {
41
+ base: "desc={{SKILL_DESCRIPTION}}",
42
+ deep: "deep-enabled",
43
+ },
44
+ },
45
+ );
43
46
 
44
47
  assert.match(output, /desc=consensus planner/);
45
48
  assert.match(output, /deep-enabled/);
@@ -48,7 +51,11 @@ describe("skill-template engine", () => {
48
51
 
49
52
  it("조건부가 false면 블록을 제거한다", () => {
50
53
  const template = "start\n{{#if DEEP}}hidden{{/if}}\nend";
51
- const output = renderSkillTemplate(template, { DEEP: false }, { partials: {} });
54
+ const output = renderSkillTemplate(
55
+ template,
56
+ { DEEP: false },
57
+ { partials: {} },
58
+ );
52
59
  assert.equal(output, "start\n\nend");
53
60
  });
54
61
 
@@ -94,7 +101,11 @@ describe("skill-template engine", () => {
94
101
  try {
95
102
  mkdirSync(join(root, "nested"), { recursive: true });
96
103
  writeFileSync(join(root, "base.md"), "base-partial", "utf8");
97
- writeFileSync(join(root, "nested", "telemetry.md"), "telemetry-partial", "utf8");
104
+ writeFileSync(
105
+ join(root, "nested", "telemetry.md"),
106
+ "telemetry-partial",
107
+ "utf8",
108
+ );
98
109
 
99
110
  const partials = loadTemplatePartials(root);
100
111
  assert.equal(partials.base, "base-partial");
@@ -108,12 +119,16 @@ describe("skill-template engine", () => {
108
119
  it("순환 partial include를 감지한다", () => {
109
120
  assert.throws(
110
121
  () =>
111
- renderSkillTemplate("{{> A}}", {}, {
112
- partials: {
113
- A: "A -> {{> B}}",
114
- B: "B -> {{> A}}",
122
+ renderSkillTemplate(
123
+ "{{> A}}",
124
+ {},
125
+ {
126
+ partials: {
127
+ A: "A -> {{> B}}",
128
+ B: "B -> {{> A}}",
129
+ },
115
130
  },
116
- }),
131
+ ),
117
132
  /Circular partial include: A -> B -> A/,
118
133
  );
119
134
  });
@@ -125,35 +140,48 @@ describe("skill-template engine", () => {
125
140
  });
126
141
 
127
142
  it("중복 frontmatter 키는 마지막 값을 우선한다", () => {
128
- const parsed = parseFrontmatter([
129
- "---",
130
- "name: first",
131
- "name: second",
132
- "---",
133
- "x",
134
- ].join("\n"));
143
+ const parsed = parseFrontmatter(
144
+ ["---", "name: first", "name: second", "---", "x"].join("\n"),
145
+ );
135
146
  assert.equal(parsed.data.name, "second");
136
147
  });
137
148
 
138
149
  it("특수문자 변수 키(점/하이픈)를 치환한다", () => {
139
- const output = renderSkillTemplate("{{FOO.BAR}}/{{FOO-BAR}}", {
140
- "FOO.BAR": "dot",
141
- "FOO-BAR": "dash",
142
- }, { partials: {} });
150
+ const output = renderSkillTemplate(
151
+ "{{FOO.BAR}}/{{FOO-BAR}}",
152
+ {
153
+ "FOO.BAR": "dot",
154
+ "FOO-BAR": "dash",
155
+ },
156
+ { partials: {} },
157
+ );
143
158
  assert.equal(output, "dot/dash");
144
159
  });
145
160
 
146
161
  it("중첩 조건 블록을 안쪽 조건값에 따라 렌더링한다", () => {
147
162
  const template = "{{#if A}}open-{{#if B}}inner{{/if}}-close{{/if}}";
148
- assert.equal(renderSkillTemplate(template, { A: true, B: true }, { partials: {} }), "open-inner-close");
149
- assert.equal(renderSkillTemplate(template, { A: true, B: false }, { partials: {} }), "open--close");
150
- assert.equal(renderSkillTemplate(template, { A: false, B: true }, { partials: {} }), "");
163
+ assert.equal(
164
+ renderSkillTemplate(template, { A: true, B: true }, { partials: {} }),
165
+ "open-inner-close",
166
+ );
167
+ assert.equal(
168
+ renderSkillTemplate(template, { A: true, B: false }, { partials: {} }),
169
+ "open--close",
170
+ );
171
+ assert.equal(
172
+ renderSkillTemplate(template, { A: false, B: true }, { partials: {} }),
173
+ "",
174
+ );
151
175
  });
152
176
 
153
177
  it("1000줄 이상 대형 템플릿도 렌더링한다", () => {
154
178
  const lines = Array.from({ length: 1_200 }, (_v, i) => `line-${i}`);
155
179
  const template = `${lines.join("\n")}\nname={{SKILL_NAME}}`;
156
- const output = renderSkillTemplate(template, { SKILL_NAME: "big-template" }, { partials: {} });
180
+ const output = renderSkillTemplate(
181
+ template,
182
+ { SKILL_NAME: "big-template" },
183
+ { partials: {} },
184
+ );
157
185
 
158
186
  const renderedLines = output.split("\n");
159
187
  assert.equal(renderedLines.length, 1_201);
@@ -166,13 +194,21 @@ describe("skill-template engine", () => {
166
194
  try {
167
195
  const sharedDir = join(root, "shared");
168
196
  mkdirSync(sharedDir, { recursive: true });
169
- writeFileSync(join(sharedDir, "telemetry.md"), "TEL={{SKILL_NAME}}", "utf8");
197
+ writeFileSync(
198
+ join(sharedDir, "telemetry.md"),
199
+ "TEL={{SKILL_NAME}}",
200
+ "utf8",
201
+ );
170
202
 
171
203
  const template = "before\n{{#include shared/telemetry.md}}\nafter";
172
- const output = renderSkillTemplate(template, { SKILL_NAME: "test-skill" }, {
173
- partials: {},
174
- includeBaseDir: root,
175
- });
204
+ const output = renderSkillTemplate(
205
+ template,
206
+ { SKILL_NAME: "test-skill" },
207
+ {
208
+ partials: {},
209
+ includeBaseDir: root,
210
+ },
211
+ );
176
212
 
177
213
  assert.match(output, /before/);
178
214
  assert.match(output, /TEL=test-skill/);
@@ -185,11 +221,15 @@ describe("skill-template engine", () => {
185
221
  it("loadSkillManifest는 skill.json이 있으면 파싱한다", () => {
186
222
  const root = makeTempDir();
187
223
  try {
188
- writeFileSync(join(root, "skill.json"), JSON.stringify({
189
- name: "tfx-test",
190
- description: "test skill",
191
- triggers: ["test"],
192
- }), "utf8");
224
+ writeFileSync(
225
+ join(root, "skill.json"),
226
+ JSON.stringify({
227
+ name: "tfx-test",
228
+ description: "test skill",
229
+ triggers: ["test"],
230
+ }),
231
+ "utf8",
232
+ );
193
233
 
194
234
  const manifest = loadSkillManifest(root);
195
235
  assert.equal(manifest.name, "tfx-test");
@@ -211,11 +251,15 @@ describe("skill-template engine", () => {
211
251
  it("parseFrontmatterWithManifest는 skill.json 우선으로 병합한다", () => {
212
252
  const root = makeTempDir();
213
253
  try {
214
- writeFileSync(join(root, "skill.json"), JSON.stringify({
215
- name: "manifest-name",
216
- description: "manifest-desc",
217
- internal: true,
218
- }), "utf8");
254
+ writeFileSync(
255
+ join(root, "skill.json"),
256
+ JSON.stringify({
257
+ name: "manifest-name",
258
+ description: "manifest-desc",
259
+ internal: true,
260
+ }),
261
+ "utf8",
262
+ );
219
263
 
220
264
  const source = [
221
265
  "---",
@@ -258,7 +302,11 @@ describe("skill-template engine", () => {
258
302
  mkdirSync(templatesDir, { recursive: true });
259
303
  mkdirSync(skillDir, { recursive: true });
260
304
 
261
- writeFileSync(join(templatesDir, "base.md"), "base={{SKILL_NAME}}", "utf8");
305
+ writeFileSync(
306
+ join(templatesDir, "base.md"),
307
+ "base={{SKILL_NAME}}",
308
+ "utf8",
309
+ );
262
310
  writeFileSync(
263
311
  join(skillDir, "SKILL.md.tmpl"),
264
312
  [
@@ -1,5 +1,5 @@
1
- import { describe, it } from "node:test";
2
1
  import assert from "node:assert/strict";
2
+ import { describe, it } from "node:test";
3
3
 
4
4
  describe("smoke: 주요 모듈 import 검증", () => {
5
5
  it("scripts/lib/keyword-rules.mjs — 순수 함수 export", async () => {
@@ -0,0 +1,102 @@
1
+ import assert from "node:assert/strict";
2
+ import { after, before, describe, it, mock } from "node:test";
3
+ import { join } from "node:path";
4
+ import { mkdirSync, rmSync, readFileSync, existsSync } from "node:fs";
5
+ import { tmpdir } from "node:os";
6
+
7
+ const TEST_LOG_DIR = join(tmpdir(), `spawn-trace-test-${Date.now()}`);
8
+
9
+ describe("spawn-trace", () => {
10
+ before(() => {
11
+ mkdirSync(TEST_LOG_DIR, { recursive: true });
12
+ });
13
+
14
+ after(() => {
15
+ try { rmSync(TEST_LOG_DIR, { recursive: true, force: true }); } catch { /* ignore */ }
16
+ });
17
+
18
+ it("exports child_process-compatible API surface", async () => {
19
+ const mod = await import("../../hub/lib/spawn-trace.mjs");
20
+ assert.equal(typeof mod.spawn, "function");
21
+ assert.equal(typeof mod.execFile, "function");
22
+ assert.equal(typeof mod.execFileSync, "function");
23
+ assert.equal(typeof mod.exec, "function");
24
+ assert.equal(typeof mod.execSync, "function");
25
+ assert.equal(typeof mod.fork, "function");
26
+ assert.equal(typeof mod.spawnSync, "function");
27
+ });
28
+
29
+ it("exports guard constants", async () => {
30
+ const mod = await import("../../hub/lib/spawn-trace.mjs");
31
+ assert.equal(typeof mod.MAX_WT_TABS, "number");
32
+ assert.ok(mod.MAX_WT_TABS > 0);
33
+ assert.equal(typeof mod.MAX_SPAWN_PER_SEC, "number");
34
+ assert.equal(typeof mod.MAX_TOTAL_DESCENDANTS, "number");
35
+ });
36
+
37
+ it("spawn returns a ChildProcess-like object", async () => {
38
+ const mod = await import("../../hub/lib/spawn-trace.mjs");
39
+ const child = mod.spawn("node", ["-e", "process.exit(0)"], {
40
+ windowsHide: true,
41
+ });
42
+ assert.ok(child);
43
+ assert.equal(typeof child.pid, "number");
44
+ assert.equal(typeof child.kill, "function");
45
+
46
+ await new Promise((resolve) => child.once("close", resolve));
47
+ });
48
+
49
+ it("execFileSync returns stdout buffer", async () => {
50
+ const mod = await import("../../hub/lib/spawn-trace.mjs");
51
+ const result = mod.execFileSync("node", ["-e", 'process.stdout.write("hello")'], {
52
+ encoding: "utf8",
53
+ windowsHide: true,
54
+ });
55
+ assert.equal(result.trim(), "hello");
56
+ });
57
+
58
+ it("execFileSync throws on non-zero exit", async () => {
59
+ const mod = await import("../../hub/lib/spawn-trace.mjs");
60
+ assert.throws(() => {
61
+ mod.execFileSync("node", ["-e", "process.exit(1)"], {
62
+ windowsHide: true,
63
+ });
64
+ });
65
+ });
66
+
67
+ it("execFile with callback receives stdout", async () => {
68
+ const mod = await import("../../hub/lib/spawn-trace.mjs");
69
+ const result = await new Promise((resolve, reject) => {
70
+ mod.execFile(
71
+ "node",
72
+ ["-e", 'process.stdout.write("world")'],
73
+ { encoding: "utf8", windowsHide: true },
74
+ (err, stdout) => {
75
+ if (err) reject(err);
76
+ else resolve(stdout);
77
+ },
78
+ );
79
+ });
80
+ assert.equal(result.trim(), "world");
81
+ });
82
+
83
+ it("strips trace-specific options before passing to child_process", async () => {
84
+ const mod = await import("../../hub/lib/spawn-trace.mjs");
85
+ // reason and dedupe should not cause child_process to error
86
+ const result = mod.execFileSync("node", ["-e", 'process.stdout.write("ok")'], {
87
+ encoding: "utf8",
88
+ windowsHide: true,
89
+ reason: "test:strip-options",
90
+ dedupe: "test-key",
91
+ });
92
+ assert.equal(result.trim(), "ok");
93
+ });
94
+
95
+ it("default export includes spawn/execFile/execFileSync", async () => {
96
+ const mod = await import("../../hub/lib/spawn-trace.mjs");
97
+ assert.equal(typeof mod.default.spawn, "function");
98
+ assert.equal(typeof mod.default.execFile, "function");
99
+ assert.equal(typeof mod.default.execFileSync, "function");
100
+ assert.equal(typeof mod.default.MAX_WT_TABS, "number");
101
+ });
102
+ });
@@ -0,0 +1,48 @@
1
+ import assert from "node:assert/strict";
2
+ import { after, describe, it } from "node:test";
3
+ import { existsSync, rmSync } from "node:fs";
4
+ import { join } from "node:path";
5
+ import { homedir } from "node:os";
6
+
7
+ describe("doctor-diagnose: 진단 번들 생성", () => {
8
+ let result;
9
+
10
+ it("diagnose() 실행 성공", async () => {
11
+ const { diagnose } = await import("../../scripts/doctor-diagnose.mjs");
12
+ result = await diagnose({ json: false });
13
+ assert.ok(result);
14
+ assert.equal(result.ok, true);
15
+ });
16
+
17
+ it("zip 파일 생성됨", () => {
18
+ assert.ok(result.zipPath);
19
+ assert.ok(existsSync(result.zipPath), `zip not found: ${result.zipPath}`);
20
+ assert.ok(result.zipPath.endsWith(".zip"));
21
+ });
22
+
23
+ it("stats 필드 포함", () => {
24
+ assert.equal(typeof result.stats.total, "number");
25
+ assert.equal(typeof result.stats.peakRatePerSec, "number");
26
+ assert.equal(typeof result.stats.maxConcurrent, "number");
27
+ assert.equal(typeof result.stats.blocked, "number");
28
+ });
29
+
30
+ it("sysInfo 필드 포함", () => {
31
+ assert.equal(typeof result.sysInfo.platform, "string");
32
+ assert.equal(typeof result.sysInfo.nodeVersion, "string");
33
+ assert.equal(typeof result.sysInfo.cpuCores, "number");
34
+ assert.equal(typeof result.sysInfo.totalMemMB, "number");
35
+ });
36
+
37
+ it("traceCount/hookTimingCount 숫자", () => {
38
+ assert.equal(typeof result.traceCount, "number");
39
+ assert.equal(typeof result.hookTimingCount, "number");
40
+ });
41
+
42
+ after(() => {
43
+ // 테스트 생성 zip 정리
44
+ if (result?.zipPath && existsSync(result.zipPath)) {
45
+ try { rmSync(result.zipPath); } catch { /* ignore */ }
46
+ }
47
+ });
48
+ });
@@ -82,7 +82,9 @@ function inspectTarget(target, options = {}) {
82
82
  }
83
83
 
84
84
  export function verifyCaches(options = {}) {
85
- const targets = options.targets?.length ? options.targets : Object.keys(CACHE_TARGETS);
85
+ const targets = options.targets?.length
86
+ ? options.targets
87
+ : Object.keys(CACHE_TARGETS);
86
88
  const results = targets.map((target) => inspectTarget(target, options));
87
89
  const issueCount = results.filter((result) => result.status !== "ok").length;
88
90
 
@@ -123,7 +125,9 @@ export async function fixCaches(options = {}) {
123
125
 
124
126
  function formatVerificationSummary(verification) {
125
127
  const label = verification.ok ? "cache-doctor: ok" : "cache-doctor: issues";
126
- const details = verification.results.map((result) => `${result.target}:${result.status}`);
128
+ const details = verification.results.map(
129
+ (result) => `${result.target}:${result.status}`,
130
+ );
127
131
  return `${label} (${details.join(", ")})`;
128
132
  }
129
133
 
@@ -138,8 +142,11 @@ async function main() {
138
142
  }
139
143
 
140
144
  const repair = await fixCaches({ verification });
141
- const repaired = repair.fixed.length > 0 ? `fixed:${repair.fixed.join(",")}` : "fixed:none";
142
- const suffix = repair.summary.results.map((result) => `${result.target}:${result.status}`).join(", ");
145
+ const repaired =
146
+ repair.fixed.length > 0 ? `fixed:${repair.fixed.join(",")}` : "fixed:none";
147
+ const suffix = repair.summary.results
148
+ .map((result) => `${result.target}:${result.status}`)
149
+ .join(", ");
143
150
  console.log(`cache-doctor: fix (${repaired}${suffix ? `, ${suffix}` : ""})`);
144
151
  if (!repair.ok) process.exitCode = 1;
145
152
  }