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,12 +1,12 @@
1
- import { readFileSync } from 'node:fs';
2
- import { dirname, resolve } from 'node:path';
3
- import { fileURLToPath } from 'node:url';
1
+ import { readFileSync } from "node:fs";
2
+ import { dirname, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
4
 
5
- import { normalizeError } from './reflexion.mjs';
5
+ import { normalizeError } from "./reflexion.mjs";
6
6
 
7
7
  const DEFAULT_KNOWN_ERRORS_PATH = resolve(
8
8
  dirname(fileURLToPath(import.meta.url)),
9
- 'lib/known-errors.json',
9
+ "lib/known-errors.json",
10
10
  );
11
11
  const DEFAULT_CONFIDENCE = 0.5;
12
12
  const ADAPTIVE_CONFIDENCE_STEP = 0.1;
@@ -18,15 +18,17 @@ function clone(value) {
18
18
 
19
19
  function pickString(...values) {
20
20
  for (const value of values) {
21
- if (typeof value === 'string' && value.trim()) return value.trim();
21
+ if (typeof value === "string" && value.trim()) return value.trim();
22
22
  }
23
- return '';
23
+ return "";
24
24
  }
25
25
 
26
26
  function pickObject(...values) {
27
- return values.find(
28
- (value) => value && typeof value === 'object' && !Array.isArray(value),
29
- ) || {};
27
+ return (
28
+ values.find(
29
+ (value) => value && typeof value === "object" && !Array.isArray(value),
30
+ ) || {}
31
+ );
30
32
  }
31
33
 
32
34
  function clampConfidence(value, fallback = DEFAULT_CONFIDENCE) {
@@ -36,25 +38,27 @@ function clampConfidence(value, fallback = DEFAULT_CONFIDENCE) {
36
38
  }
37
39
 
38
40
  function normalizeLookup(text) {
39
- return String(text || '').trim().toLowerCase();
41
+ return String(text || "")
42
+ .trim()
43
+ .toLowerCase();
40
44
  }
41
45
 
42
46
  function readPathValue(source, path) {
43
47
  if (!source || !path) return undefined;
44
48
  return String(path)
45
- .split('.')
49
+ .split(".")
46
50
  .filter(Boolean)
47
51
  .reduce((current, key) => current?.[key], source);
48
52
  }
49
53
 
50
54
  function renderTemplate(template, observation, signature) {
51
- if (!template) return '';
55
+ if (!template) return "";
52
56
  const context = pickObject(observation.context);
53
57
  const dna = pickObject(observation.dna);
54
58
  const dnaValue = signature.dna_factor
55
- ? readPathValue(dna, signature.dna_factor) ??
59
+ ? (readPathValue(dna, signature.dna_factor) ??
56
60
  readPathValue(context, signature.dna_factor) ??
57
- readPathValue(observation, signature.dna_factor)
61
+ readPathValue(observation, signature.dna_factor))
58
62
  : undefined;
59
63
  const variables = {
60
64
  ...context,
@@ -64,7 +68,7 @@ function renderTemplate(template, observation, signature) {
64
68
  };
65
69
  return String(template).replace(/\{([^}]+)\}/gu, (match, key) => {
66
70
  const value = variables[key];
67
- return value == null || value === '' ? match : String(value);
71
+ return value == null || value === "" ? match : String(value);
68
72
  });
69
73
  }
70
74
 
@@ -76,7 +80,7 @@ function buildKnownContextText(observation) {
76
80
  pickString(observation.step),
77
81
  ]
78
82
  .filter(Boolean)
79
- .join(' '),
83
+ .join(" "),
80
84
  );
81
85
  }
82
86
 
@@ -88,7 +92,7 @@ function buildErrorText(observation = {}) {
88
92
  pickString(observation.message),
89
93
  ]
90
94
  .filter(Boolean)
91
- .join('\n')
95
+ .join("\n")
92
96
  .trim();
93
97
  }
94
98
 
@@ -114,13 +118,13 @@ function compileSignatures(raw = {}) {
114
118
  return Object.entries(raw.signatures || {}).map(([id, signature]) => ({
115
119
  id,
116
120
  ...clone(signature),
117
- matcher: new RegExp(String(signature.pattern || ''), 'iu'),
121
+ matcher: new RegExp(String(signature.pattern || ""), "iu"),
118
122
  }));
119
123
  }
120
124
 
121
125
  export function loadKnownErrors(filePath = DEFAULT_KNOWN_ERRORS_PATH) {
122
126
  try {
123
- const parsed = JSON.parse(readFileSync(filePath, 'utf8'));
127
+ const parsed = JSON.parse(readFileSync(filePath, "utf8"));
124
128
  return {
125
129
  path: filePath,
126
130
  version: parsed.version ?? 1,
@@ -132,7 +136,10 @@ export function loadKnownErrors(filePath = DEFAULT_KNOWN_ERRORS_PATH) {
132
136
  }
133
137
 
134
138
  function scoreKnownMatch(signature, observation) {
135
- if (!observation.errorText || !signature.matcher.test(observation.errorText)) {
139
+ if (
140
+ !observation.errorText ||
141
+ !signature.matcher.test(observation.errorText)
142
+ ) {
136
143
  return null;
137
144
  }
138
145
  if (
@@ -157,10 +164,10 @@ function scoreKnownMatch(signature, observation) {
157
164
  );
158
165
  }
159
166
 
160
- function severityFromConfidence(confidence, fallback = 'medium') {
161
- if (confidence >= 0.9) return 'critical';
162
- if (confidence >= 0.75) return 'high';
163
- if (confidence >= 0.55) return 'medium';
167
+ function severityFromConfidence(confidence, fallback = "medium") {
168
+ if (confidence >= 0.9) return "critical";
169
+ if (confidence >= 0.75) return "high";
170
+ if (confidence >= 0.55) return "medium";
164
171
  return fallback;
165
172
  }
166
173
 
@@ -178,7 +185,7 @@ export function matchKnownError(catalog, observationInput = {}) {
178
185
  const { signature, confidence } = matched;
179
186
  return {
180
187
  matched: true,
181
- source: 'known',
188
+ source: "known",
182
189
  signature_id: signature.id,
183
190
  project_slug: observation.projectSlug,
184
191
  error_pattern: observation.errorPattern,
@@ -187,7 +194,7 @@ export function matchKnownError(catalog, observationInput = {}) {
187
194
  severity: signature.severity || severityFromConfidence(confidence),
188
195
  tool: signature.tool || observation.tool,
189
196
  context: signature.context || observation.contextText || null,
190
- root_cause: signature.root_cause || 'known failure pattern',
197
+ root_cause: signature.root_cause || "known failure pattern",
191
198
  rule: renderTemplate(signature.rule_template, observation, signature),
192
199
  fix: signature.fix || null,
193
200
  dna_factor: signature.dna_factor || null,
@@ -199,14 +206,21 @@ function resolveRuleStore(options = {}) {
199
206
  }
200
207
 
201
208
  function ensureAdaptiveRule(store, observation) {
202
- if (!store?.findAdaptiveRule || !store?.addAdaptiveRule || !observation.projectSlug) {
209
+ if (
210
+ !store?.findAdaptiveRule ||
211
+ !store?.addAdaptiveRule ||
212
+ !observation.projectSlug
213
+ ) {
203
214
  return null;
204
215
  }
205
216
  const identity = {
206
217
  project_slug: observation.projectSlug,
207
218
  pattern: observation.errorPattern,
208
219
  };
209
- const current = store.findAdaptiveRule(identity.project_slug, identity.pattern);
220
+ const current = store.findAdaptiveRule(
221
+ identity.project_slug,
222
+ identity.pattern,
223
+ );
210
224
  if (!current) {
211
225
  return store.addAdaptiveRule(identity);
212
226
  }
@@ -214,7 +228,10 @@ function ensureAdaptiveRule(store, observation) {
214
228
  return store.updateRuleConfidence(
215
229
  identity.project_slug,
216
230
  identity.pattern,
217
- Math.min(MAX_ADAPTIVE_CONFIDENCE, current.confidence + ADAPTIVE_CONFIDENCE_STEP),
231
+ Math.min(
232
+ MAX_ADAPTIVE_CONFIDENCE,
233
+ current.confidence + ADAPTIVE_CONFIDENCE_STEP,
234
+ ),
218
235
  { hit_count_increment: 1 },
219
236
  );
220
237
  }
@@ -223,41 +240,45 @@ function buildAdaptiveDiagnosis(rule, observation) {
223
240
  if (!rule) {
224
241
  return {
225
242
  matched: false,
226
- source: 'novel',
243
+ source: "novel",
227
244
  project_slug: observation.projectSlug,
228
245
  error_pattern: observation.errorPattern,
229
246
  error_message: observation.errorText,
230
247
  confidence: DEFAULT_CONFIDENCE,
231
- severity: 'low',
232
- root_cause: '새로운 실패 패턴으로 분류됨',
233
- rule: '',
248
+ severity: "low",
249
+ root_cause: "새로운 실패 패턴으로 분류됨",
250
+ rule: "",
234
251
  fix: null,
235
252
  };
236
253
  }
237
- const matched = Number(rule.hit_count || 0) > 1 || Number(rule.confidence || 0) > DEFAULT_CONFIDENCE;
254
+ const matched =
255
+ Number(rule.hit_count || 0) > 1 ||
256
+ Number(rule.confidence || 0) > DEFAULT_CONFIDENCE;
238
257
  const confidence = clampConfidence(rule.confidence, DEFAULT_CONFIDENCE);
239
258
  return {
240
259
  matched,
241
- source: matched ? 'adaptive' : 'novel',
260
+ source: matched ? "adaptive" : "novel",
242
261
  project_slug: rule.project_slug,
243
262
  error_pattern: rule.pattern,
244
263
  error_message: observation.errorText,
245
264
  confidence,
246
- severity: severityFromConfidence(confidence, matched ? 'medium' : 'low'),
265
+ severity: severityFromConfidence(confidence, matched ? "medium" : "low"),
247
266
  root_cause: matched
248
- ? '반복 관측된 adaptive rule과 일치'
249
- : 'adaptive memory에 첫 관측으로 저장됨',
267
+ ? "반복 관측된 adaptive rule과 일치"
268
+ : "adaptive memory에 첫 관측으로 저장됨",
250
269
  rule: matched
251
270
  ? `프로젝트 ${rule.project_slug}에서 동일 패턴이 ${rule.hit_count}회 관측되었습니다.`
252
271
  : `프로젝트 ${rule.project_slug}의 adaptive memory에 패턴을 기록했습니다.`,
253
- fix: matched ? '최근 성공한 수정/회피 전략을 재적용하세요.' : '추가 관측 후 adaptive rule을 승격하세요.',
272
+ fix: matched
273
+ ? "최근 성공한 수정/회피 전략을 재적용하세요."
274
+ : "추가 관측 후 adaptive rule을 승격하세요.",
254
275
  adaptive_rule: clone(rule),
255
276
  };
256
277
  }
257
278
 
258
279
  function createHealthyState(catalog) {
259
280
  return {
260
- state: 'healthy',
281
+ state: "healthy",
261
282
  known_errors_count: catalog.signatures.length,
262
283
  last_error: null,
263
284
  };
@@ -265,24 +286,28 @@ function createHealthyState(catalog) {
265
286
 
266
287
  function createDegradedState(error) {
267
288
  return {
268
- state: 'degraded',
289
+ state: "degraded",
269
290
  known_errors_count: 0,
270
291
  last_error: {
271
- name: error?.name || 'Error',
272
- message: error?.message || 'unknown adaptive diagnostic error',
292
+ name: error?.name || "Error",
293
+ message: error?.message || "unknown adaptive diagnostic error",
273
294
  },
274
295
  };
275
296
  }
276
297
 
277
298
  export function createDiagnosticPipeline(options = {}) {
278
299
  const store = resolveRuleStore(options);
279
- let catalog = options.knownErrors ? { signatures: compileSignatures({ signatures: options.knownErrors }) } : null;
300
+ let catalog = options.knownErrors
301
+ ? { signatures: compileSignatures({ signatures: options.knownErrors }) }
302
+ : null;
280
303
  let health = catalog ? createHealthyState(catalog) : null;
281
304
 
282
305
  function ensureCatalog() {
283
306
  if (catalog) return catalog;
284
307
  try {
285
- catalog = loadKnownErrors(options.knownErrorsPath || DEFAULT_KNOWN_ERRORS_PATH);
308
+ catalog = loadKnownErrors(
309
+ options.knownErrorsPath || DEFAULT_KNOWN_ERRORS_PATH,
310
+ );
286
311
  health = createHealthyState(catalog);
287
312
  } catch (error) {
288
313
  catalog = { signatures: [] };
@@ -303,11 +328,13 @@ export function createDiagnosticPipeline(options = {}) {
303
328
  }
304
329
 
305
330
  function getHealth() {
306
- return clone(health || ensureCatalog() && health);
331
+ return clone(health || (ensureCatalog() && health));
307
332
  }
308
333
 
309
334
  function listKnownErrors() {
310
- return (ensureCatalog().signatures || []).map(({ matcher, ...signature }) => clone(signature));
335
+ return (ensureCatalog().signatures || []).map(({ matcher, ...signature }) =>
336
+ clone(signature),
337
+ );
311
338
  }
312
339
 
313
340
  return Object.freeze({
@@ -1,10 +1,11 @@
1
- import { existsSync, readFileSync, writeFileSync } from 'node:fs';
2
- import { join, resolve } from 'node:path';
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join, resolve } from "node:path";
3
3
 
4
- const SECTION_HEADING = '## Adaptive Rules (triflux auto-generated)';
4
+ const SECTION_HEADING = "## Adaptive Rules (triflux auto-generated)";
5
5
  const DEFAULT_MAX_RULES = 10;
6
6
  const SECTION_RE = /^## Adaptive Rules \(triflux auto-generated\)$/mu;
7
- const BLOCK_RE = /<!-- tfx-adaptive:start rule_id="([^"]+)" confidence=([0-9.]+) occurrences=(\d+) first_seen=([0-9-]+) last_seen=([0-9-]+) -->\r?\n([^\r\n]*)\r?\n<!-- tfx-adaptive:end -->/gu;
7
+ const BLOCK_RE =
8
+ /<!-- tfx-adaptive:start rule_id="([^"]+)" confidence=([0-9.]+) occurrences=(\d+) first_seen=([0-9-]+) last_seen=([0-9-]+) -->\r?\n([^\r\n]*)\r?\n<!-- tfx-adaptive:end -->/gu;
8
9
 
9
10
  function cloneRule(rule) {
10
11
  return Object.freeze({ ...rule });
@@ -27,20 +28,23 @@ function formatConfidence(value) {
27
28
  }
28
29
 
29
30
  function normalizeDate(value, fallback) {
30
- const text = String(value ?? fallback ?? '').trim();
31
+ const text = String(value ?? fallback ?? "").trim();
31
32
  return text || fallback;
32
33
  }
33
34
 
34
35
  function normalizeRuleInput(rule, fallback = {}) {
35
- if (!rule || typeof rule !== 'object') return null;
36
+ if (!rule || typeof rule !== "object") return null;
36
37
 
37
- const id = String(rule.id ?? rule.rule_id ?? fallback.id ?? '').trim();
38
- const text = String(rule.rule ?? rule.text ?? fallback.rule ?? '').trim();
38
+ const id = String(rule.id ?? rule.rule_id ?? fallback.id ?? "").trim();
39
+ const text = String(rule.rule ?? rule.text ?? fallback.rule ?? "").trim();
39
40
  const firstSeen = normalizeDate(
40
41
  rule.firstSeen ?? rule.first_seen,
41
- fallback.firstSeen ?? fallback.lastSeen ?? '1970-01-01',
42
+ fallback.firstSeen ?? fallback.lastSeen ?? "1970-01-01",
43
+ );
44
+ const lastSeen = normalizeDate(
45
+ rule.lastSeen ?? rule.last_seen,
46
+ fallback.lastSeen ?? firstSeen,
42
47
  );
43
- const lastSeen = normalizeDate(rule.lastSeen ?? rule.last_seen, fallback.lastSeen ?? firstSeen);
44
48
  if (!id || !text || /["\r\n]/u.test(id) || /[\r\n]/u.test(text)) {
45
49
  return null;
46
50
  }
@@ -56,37 +60,43 @@ function normalizeRuleInput(rule, fallback = {}) {
56
60
  }
57
61
 
58
62
  function trimLeadingBlankLines(text) {
59
- return String(text ?? '').replace(/^(?:[ \t]*\r?\n)+/u, '');
63
+ return String(text ?? "").replace(/^(?:[ \t]*\r?\n)+/u, "");
60
64
  }
61
65
 
62
66
  function trimTrailingBlankLines(text) {
63
- return String(text ?? '').replace(/(?:\r?\n[ \t]*)+$/u, '');
67
+ return String(text ?? "").replace(/(?:\r?\n[ \t]*)+$/u, "");
64
68
  }
65
69
 
66
- function parseInjectedRules(sectionBody = '') {
70
+ function parseInjectedRules(sectionBody = "") {
67
71
  const matches = Array.from(String(sectionBody).matchAll(BLOCK_RE));
68
- return matches.map(([, id, confidence, occurrences, firstSeen, lastSeen, text]) => cloneRule({
69
- id,
70
- rule: text,
71
- confidence: clampConfidence(confidence),
72
- occurrences: normalizeOccurrences(occurrences),
73
- firstSeen,
74
- lastSeen,
75
- }));
72
+ return matches.map(
73
+ ([, id, confidence, occurrences, firstSeen, lastSeen, text]) =>
74
+ cloneRule({
75
+ id,
76
+ rule: text,
77
+ confidence: clampConfidence(confidence),
78
+ occurrences: normalizeOccurrences(occurrences),
79
+ firstSeen,
80
+ lastSeen,
81
+ }),
82
+ );
76
83
  }
77
84
 
78
85
  function readDocument(claudeMdPath) {
79
- const raw = existsSync(claudeMdPath) ? readFileSync(claudeMdPath, 'utf8') : '';
86
+ const raw = existsSync(claudeMdPath)
87
+ ? readFileSync(claudeMdPath, "utf8")
88
+ : "";
80
89
  const sectionStart = raw.search(SECTION_RE);
81
90
  if (sectionStart === -1) {
82
- return { before: raw, after: '', rules: [] };
91
+ return { before: raw, after: "", rules: [] };
83
92
  }
84
93
 
85
- const headingEnd = raw.indexOf('\n', sectionStart);
94
+ const headingEnd = raw.indexOf("\n", sectionStart);
86
95
  const bodyStart = headingEnd === -1 ? raw.length : headingEnd + 1;
87
96
  const rest = raw.slice(bodyStart);
88
97
  const nextHeadingOffset = rest.search(/^#{1,6}\s/mu);
89
- const sectionEnd = nextHeadingOffset === -1 ? raw.length : bodyStart + nextHeadingOffset;
98
+ const sectionEnd =
99
+ nextHeadingOffset === -1 ? raw.length : bodyStart + nextHeadingOffset;
90
100
  const body = raw.slice(bodyStart, sectionEnd);
91
101
 
92
102
  return {
@@ -100,36 +110,49 @@ function serializeRule(rule) {
100
110
  return [
101
111
  `<!-- tfx-adaptive:start rule_id="${rule.id}" confidence=${formatConfidence(rule.confidence)} occurrences=${rule.occurrences} first_seen=${rule.firstSeen} last_seen=${rule.lastSeen} -->`,
102
112
  rule.rule,
103
- '<!-- tfx-adaptive:end -->',
104
- ].join('\n');
113
+ "<!-- tfx-adaptive:end -->",
114
+ ].join("\n");
105
115
  }
106
116
 
107
117
  function serializeDocument(before, rules, after) {
108
- const section = rules.length > 0
109
- ? `${SECTION_HEADING}\n\n${rules.map(serializeRule).join('\n\n')}`
110
- : '';
111
- const parts = [trimTrailingBlankLines(before), section, trimLeadingBlankLines(after)].filter(Boolean);
112
- return parts.length > 0 ? `${parts.join('\n\n')}\n` : '';
118
+ const section =
119
+ rules.length > 0
120
+ ? `${SECTION_HEADING}\n\n${rules.map(serializeRule).join("\n\n")}`
121
+ : "";
122
+ const parts = [
123
+ trimTrailingBlankLines(before),
124
+ section,
125
+ trimLeadingBlankLines(after),
126
+ ].filter(Boolean);
127
+ return parts.length > 0 ? `${parts.join("\n\n")}\n` : "";
113
128
  }
114
129
 
115
130
  function enforceMaxRules(rules, maxRules) {
116
131
  if (rules.length <= maxRules) return rules.map(cloneRule);
117
132
  const ranked = rules
118
133
  .map((rule, index) => ({ ...rule, index }))
119
- .sort((left, right) => (
120
- left.confidence - right.confidence
121
- || left.occurrences - right.occurrences
122
- || left.lastSeen.localeCompare(right.lastSeen)
123
- || left.index - right.index
124
- || left.id.localeCompare(right.id)
125
- ));
126
- const removedIds = new Set(ranked.slice(0, rules.length - maxRules).map((rule) => rule.id));
134
+ .sort(
135
+ (left, right) =>
136
+ left.confidence - right.confidence ||
137
+ left.occurrences - right.occurrences ||
138
+ left.lastSeen.localeCompare(right.lastSeen) ||
139
+ left.index - right.index ||
140
+ left.id.localeCompare(right.id),
141
+ );
142
+ const removedIds = new Set(
143
+ ranked.slice(0, rules.length - maxRules).map((rule) => rule.id),
144
+ );
127
145
  return rules.filter((rule) => !removedIds.has(rule.id)).map(cloneRule);
128
146
  }
129
147
 
130
148
  export function createAdaptiveInjector(opts = {}) {
131
- const claudeMdPath = resolve(opts.claudeMdPath ?? join(process.cwd(), 'CLAUDE.md'));
132
- const maxRules = Number.isInteger(opts.maxRules) && opts.maxRules > 0 ? opts.maxRules : DEFAULT_MAX_RULES;
149
+ const claudeMdPath = resolve(
150
+ opts.claudeMdPath ?? join(process.cwd(), "CLAUDE.md"),
151
+ );
152
+ const maxRules =
153
+ Number.isInteger(opts.maxRules) && opts.maxRules > 0
154
+ ? opts.maxRules
155
+ : DEFAULT_MAX_RULES;
133
156
 
134
157
  function listInjected() {
135
158
  return readDocument(claudeMdPath).rules.map(cloneRule);
@@ -137,40 +160,55 @@ export function createAdaptiveInjector(opts = {}) {
137
160
 
138
161
  function inject(rule) {
139
162
  const document = readDocument(claudeMdPath);
140
- const targetId = String(rule?.id ?? rule?.rule_id ?? '').trim();
163
+ const targetId = String(rule?.id ?? rule?.rule_id ?? "").trim();
141
164
  const existing = document.rules.find((item) => item.id === targetId);
142
165
  const normalized = normalizeRuleInput(rule, existing ?? {});
143
166
  if (!normalized) return false;
144
167
 
145
168
  const nextRules = existing
146
- ? document.rules.map((item) => (item.id === normalized.id
147
- ? cloneRule({
148
- ...item,
149
- confidence: normalized.confidence,
150
- occurrences: normalized.occurrences,
151
- lastSeen: normalized.lastSeen,
152
- })
153
- : cloneRule(item)))
169
+ ? document.rules.map((item) =>
170
+ item.id === normalized.id
171
+ ? cloneRule({
172
+ ...item,
173
+ confidence: normalized.confidence,
174
+ occurrences: normalized.occurrences,
175
+ lastSeen: normalized.lastSeen,
176
+ })
177
+ : cloneRule(item),
178
+ )
154
179
  : [...document.rules.map(cloneRule), normalized];
155
180
  const limitedRules = enforceMaxRules(nextRules, maxRules);
156
- writeFileSync(claudeMdPath, serializeDocument(document.before, limitedRules, document.after), 'utf8');
181
+ writeFileSync(
182
+ claudeMdPath,
183
+ serializeDocument(document.before, limitedRules, document.after),
184
+ "utf8",
185
+ );
157
186
  return limitedRules.some((item) => item.id === normalized.id);
158
187
  }
159
188
 
160
189
  function remove(ruleId) {
161
- const targetId = String(ruleId ?? '').trim();
190
+ const targetId = String(ruleId ?? "").trim();
162
191
  if (!targetId || !existsSync(claudeMdPath)) return false;
163
192
  const document = readDocument(claudeMdPath);
164
193
  if (!document.rules.some((rule) => rule.id === targetId)) return false;
165
- const nextRules = document.rules.filter((rule) => rule.id !== targetId).map(cloneRule);
166
- writeFileSync(claudeMdPath, serializeDocument(document.before, nextRules, document.after), 'utf8');
194
+ const nextRules = document.rules
195
+ .filter((rule) => rule.id !== targetId)
196
+ .map(cloneRule);
197
+ writeFileSync(
198
+ claudeMdPath,
199
+ serializeDocument(document.before, nextRules, document.after),
200
+ "utf8",
201
+ );
167
202
  return true;
168
203
  }
169
204
 
170
205
  function cleanup(activeRuleIds = []) {
171
- const activeIds = new Set(Array.isArray(activeRuleIds) ? activeRuleIds : Array.from(activeRuleIds));
206
+ const activeIds = new Set(
207
+ Array.isArray(activeRuleIds) ? activeRuleIds : Array.from(activeRuleIds),
208
+ );
172
209
  return listInjected().reduce(
173
- (count, rule) => count + (activeIds.has(rule.id) ? 0 : Number(remove(rule.id))),
210
+ (count, rule) =>
211
+ count + (activeIds.has(rule.id) ? 0 : Number(remove(rule.id))),
174
212
  0,
175
213
  );
176
214
  }