tribunal-kit 6.0.0 → 7.0.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 (353) hide show
  1. package/.agent/.manifest.json +78 -0
  2. package/.agent/ARCHITECTURE.md +19 -241
  3. package/.agent/agents/accessibility-reviewer.md +23 -16
  4. package/.agent/agents/ai-code-reviewer.md +25 -21
  5. package/.agent/agents/anti-pattern-reviewer.md +12 -7
  6. package/.agent/agents/api-architect.md +1 -0
  7. package/.agent/agents/backend-specialist.md +23 -22
  8. package/.agent/agents/cloud-engineer.md +1 -0
  9. package/.agent/agents/code-archaeologist.md +1 -0
  10. package/.agent/agents/complexity-reviewer.md +10 -7
  11. package/.agent/agents/database-architect.md +3 -2
  12. package/.agent/agents/db-latency-auditor.md +6 -2
  13. package/.agent/agents/debugger.md +3 -2
  14. package/.agent/agents/dependency-reviewer.md +1 -0
  15. package/.agent/agents/devops-engineer.md +9 -8
  16. package/.agent/agents/documentation-writer.md +6 -5
  17. package/.agent/agents/explorer-agent.md +1 -0
  18. package/.agent/agents/frontend-reviewer.md +21 -17
  19. package/.agent/agents/frontend-specialist.md +23 -20
  20. package/.agent/agents/game-developer.md +1 -0
  21. package/.agent/agents/interaction-reviewer.md +10 -5
  22. package/.agent/agents/logic-reviewer.md +3 -0
  23. package/.agent/agents/minimalist-reviewer.md +6 -0
  24. package/.agent/agents/mobile-developer.md +7 -6
  25. package/.agent/agents/mobile-reviewer.md +14 -13
  26. package/.agent/agents/orchestrator.md +3 -0
  27. package/.agent/agents/penetration-tester.md +1 -0
  28. package/.agent/agents/performance-optimizer.md +6 -5
  29. package/.agent/agents/performance-reviewer.md +6 -6
  30. package/.agent/agents/pipeline-reviewer.md +144 -0
  31. package/.agent/agents/precedence-reviewer.md +1 -0
  32. package/.agent/agents/product-manager.md +1 -0
  33. package/.agent/agents/product-owner.md +1 -0
  34. package/.agent/agents/product-reviewer.md +12 -7
  35. package/.agent/agents/project-planner.md +3 -0
  36. package/.agent/agents/qa-automation-engineer.md +50 -43
  37. package/.agent/agents/resilience-reviewer.md +1 -0
  38. package/.agent/agents/schema-reviewer.md +1 -0
  39. package/.agent/agents/security-auditor.md +24 -24
  40. package/.agent/agents/seo-specialist.md +33 -23
  41. package/.agent/agents/sql-reviewer.md +5 -4
  42. package/.agent/agents/supervisor-agent.md +12 -2
  43. package/.agent/agents/swarm-worker-contracts.md +13 -13
  44. package/.agent/agents/swarm-worker-registry.md +51 -51
  45. package/.agent/agents/system-architect.md +1 -0
  46. package/.agent/agents/test-coverage-reviewer.md +25 -22
  47. package/.agent/agents/test-engineer.md +12 -11
  48. package/.agent/agents/throughput-optimizer.md +45 -40
  49. package/.agent/agents/type-safety-reviewer.md +5 -4
  50. package/.agent/agents/ui-ux-auditor.md +6 -4
  51. package/.agent/agents/ui-visual-auditor.md +3 -0
  52. package/.agent/agents/ux-reviewer.md +11 -6
  53. package/.agent/agents/visual-reviewer.md +11 -6
  54. package/.agent/agents/vitals-reviewer.md +5 -4
  55. package/.agent/history/memory/.memory.idx +3716 -1
  56. package/.agent/history/memory/MEMORY.md +246 -1
  57. package/.agent/mcp_config.json +1 -6
  58. package/.agent/routing_index.json +2034 -263
  59. package/.agent/rules/GEMINI.md +12 -10
  60. package/.agent/rules/GEMINI_PLANNER.md +19 -21
  61. package/.agent/scripts/_colors.js +52 -54
  62. package/.agent/scripts/_utils.js +52 -53
  63. package/.agent/scripts/auto_preview.js +39 -43
  64. package/.agent/scripts/bundle_analyzer.js +47 -60
  65. package/.agent/scripts/case_law_manager.js +200 -294
  66. package/.agent/scripts/checklist.js +73 -94
  67. package/.agent/scripts/cicd_validator.js +305 -0
  68. package/.agent/scripts/compile_router.py +4 -111
  69. package/.agent/scripts/context_broker.js +317 -412
  70. package/.agent/scripts/contract_engine.js +402 -0
  71. package/.agent/scripts/dependency_analyzer.js +112 -124
  72. package/.agent/scripts/graph_builder.js +75 -103
  73. package/.agent/scripts/graph_visualizer.js +10 -12
  74. package/.agent/scripts/graph_zoom.js +38 -55
  75. package/.agent/scripts/guardrail_engine.js +195 -145
  76. package/.agent/scripts/impact_classifier.js +55 -30
  77. package/.agent/scripts/inner_loop_validator.js +108 -159
  78. package/.agent/scripts/integrity_manifest.js +59 -67
  79. package/.agent/scripts/lint_runner.js +65 -75
  80. package/.agent/scripts/marathon_harness.js +131 -208
  81. package/.agent/scripts/migrate_skills_frontmatter.py +4 -63
  82. package/.agent/scripts/minify_context.js +29 -33
  83. package/.agent/scripts/minimal_change_engine.js +95 -87
  84. package/.agent/scripts/mutation_runner.js +46 -62
  85. package/.agent/scripts/pipeline_engine.js +320 -209
  86. package/.agent/scripts/prompt_compiler.js +118 -93
  87. package/.agent/scripts/schema_validator.js +73 -121
  88. package/.agent/scripts/security_scan.js +88 -117
  89. package/.agent/scripts/session_manager.js +52 -69
  90. package/.agent/scripts/signal_detector.js +24 -22
  91. package/.agent/scripts/skill_evolution.js +184 -245
  92. package/.agent/scripts/skill_integrator.js +35 -48
  93. package/.agent/scripts/socratic_gate_policy.js +15 -12
  94. package/.agent/scripts/strengthen_skills.js +29 -33
  95. package/.agent/scripts/swarm_dispatcher.js +120 -151
  96. package/.agent/scripts/test_runner.js +54 -56
  97. package/.agent/scripts/token_budget_broker.js +9 -7
  98. package/.agent/scripts/trace_engine.js +126 -0
  99. package/.agent/scripts/verify_all.js +76 -93
  100. package/.agent/scripts/visual_audit.js +16 -21
  101. package/.agent/skill_topic_map.json +237 -0
  102. package/.agent/skills/12-principles-of-animation/SKILL.md +7 -0
  103. package/.agent/skills/60fps-animation/SKILL.md +7 -0
  104. package/.agent/skills/accessible-animation/SKILL.md +5 -1
  105. package/.agent/skills/adapt/SKILL.md +7 -1
  106. package/.agent/skills/advanced-rag-pipelines/SKILL.md +1 -0
  107. package/.agent/skills/agent-organizer/SKILL.md +1 -0
  108. package/.agent/skills/agentic-patterns/SKILL.md +33 -25
  109. package/.agent/skills/agentic-workflows-2026/SKILL.md +80 -0
  110. package/.agent/skills/ai-app-hardening/SKILL.md +59 -0
  111. package/.agent/skills/ai-prompt-injection-defense/SKILL.md +7 -6
  112. package/.agent/skills/animation-on-scroll/SKILL.md +24 -15
  113. package/.agent/skills/animation-systems/SKILL.md +11 -10
  114. package/.agent/skills/antfu-conventions/SKILL.md +7 -2
  115. package/.agent/skills/api-patterns/SKILL.md +14 -10
  116. package/.agent/skills/api-security-auditor/SKILL.md +15 -14
  117. package/.agent/skills/app-builder/SKILL.md +1 -0
  118. package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +2 -2
  119. package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +1 -1
  120. package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +12 -12
  121. package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +3 -3
  122. package/.agent/skills/apple-design/SKILL.md +8 -7
  123. package/.agent/skills/architecture/SKILL.md +3 -2
  124. package/.agent/skills/audit-and-fix/SKILL.md +8 -5
  125. package/.agent/skills/authentication-best-practices/SKILL.md +9 -8
  126. package/.agent/skills/backend-security-expert/SKILL.md +1 -0
  127. package/.agent/skills/baseline-ui/SKILL.md +8 -7
  128. package/.agent/skills/bash-linux/SKILL.md +1 -0
  129. package/.agent/skills/behavioral-modes/SKILL.md +1 -0
  130. package/.agent/skills/better-colors/SKILL.md +11 -7
  131. package/.agent/skills/better-typography/SKILL.md +30 -16
  132. package/.agent/skills/better-ui/SKILL.md +17 -10
  133. package/.agent/skills/bolder/SKILL.md +6 -0
  134. package/.agent/skills/brainstorming/SKILL.md +6 -4
  135. package/.agent/skills/browser-native-ai/SKILL.md +5 -4
  136. package/.agent/skills/build-primitive/SKILL.md +5 -0
  137. package/.agent/skills/building-native-ui/SKILL.md +1 -0
  138. package/.agent/skills/cicd-pro/SKILL.md +2 -1
  139. package/.agent/skills/clarify/SKILL.md +19 -14
  140. package/.agent/skills/clean-code/SKILL.md +16 -15
  141. package/.agent/skills/cloud-architect/SKILL.md +1 -0
  142. package/.agent/skills/cobejs/SKILL.md +7 -9
  143. package/.agent/skills/code-review-checklist/SKILL.md +1 -0
  144. package/.agent/skills/codebase-design/SKILL.md +7 -2
  145. package/.agent/skills/colorize/SKILL.md +8 -3
  146. package/.agent/skills/compact-landing/SKILL.md +8 -3
  147. package/.agent/skills/company-logos/SKILL.md +6 -0
  148. package/.agent/skills/config-validator/SKILL.md +10 -9
  149. package/.agent/skills/containerization-pro/SKILL.md +6 -5
  150. package/.agent/skills/context-engineering-pro/SKILL.md +70 -0
  151. package/.agent/skills/create-design-md/SKILL.md +13 -5
  152. package/.agent/skills/critique/SKILL.md +10 -8
  153. package/.agent/skills/csharp-developer/SKILL.md +1 -0
  154. package/.agent/skills/data-validation-schemas/SKILL.md +28 -23
  155. package/.agent/skills/database-design/SKILL.md +2 -1
  156. package/.agent/skills/delight/SKILL.md +6 -0
  157. package/.agent/skills/deployment-procedures/SKILL.md +2 -1
  158. package/.agent/skills/design-lab/SKILL.md +5 -0
  159. package/.agent/skills/devops-engineer/SKILL.md +3 -2
  160. package/.agent/skills/devops-incident-responder/SKILL.md +2 -1
  161. package/.agent/skills/diagnosing-bugs/SKILL.md +12 -2
  162. package/.agent/skills/distill/SKILL.md +7 -2
  163. package/.agent/skills/documentation-templates/SKILL.md +3 -2
  164. package/.agent/skills/domain-modeling/SKILL.md +12 -4
  165. package/.agent/skills/duckdb-analytical-sql/SKILL.md +59 -0
  166. package/.agent/skills/edge-ai-mobile/SKILL.md +50 -0
  167. package/.agent/skills/edge-computing/SKILL.md +9 -8
  168. package/.agent/skills/emil-design-eng/SKILL.md +43 -27
  169. package/.agent/skills/error-resilience/SKILL.md +34 -16
  170. package/.agent/skills/expo-router-v4/SKILL.md +81 -0
  171. package/.agent/skills/extract-design-system/SKILL.md +12 -11
  172. package/.agent/skills/fabel-protocol/SKILL.md +24 -17
  173. package/.agent/skills/fixing-accessibility/SKILL.md +23 -16
  174. package/.agent/skills/fixing-metadata/SKILL.md +24 -13
  175. package/.agent/skills/fixing-motion-performance/SKILL.md +7 -5
  176. package/.agent/skills/framer-motion-expert/SKILL.md +29 -15
  177. package/.agent/skills/frontend-design/SKILL.md +21 -13
  178. package/.agent/skills/frontend-security-expert/SKILL.md +1 -0
  179. package/.agent/skills/game-design-expert/SKILL.md +1 -0
  180. package/.agent/skills/game-engineering-expert/SKILL.md +1 -0
  181. package/.agent/skills/generative-ui-expert/SKILL.md +3 -2
  182. package/.agent/skills/geo-fundamentals/SKILL.md +2 -1
  183. package/.agent/skills/git-pro/SKILL.md +10 -9
  184. package/.agent/skills/github-operations/SKILL.md +2 -1
  185. package/.agent/skills/gpt-taste/SKILL.md +7 -0
  186. package/.agent/skills/gsap-core/SKILL.md +26 -22
  187. package/.agent/skills/gsap-frameworks/SKILL.md +6 -5
  188. package/.agent/skills/gsap-performance/SKILL.md +4 -3
  189. package/.agent/skills/gsap-plugins/SKILL.md +34 -33
  190. package/.agent/skills/gsap-react/SKILL.md +9 -8
  191. package/.agent/skills/gsap-scrolltrigger/SKILL.md +34 -33
  192. package/.agent/skills/gsap-timeline/SKILL.md +18 -15
  193. package/.agent/skills/gsap-utils/SKILL.md +25 -24
  194. package/.agent/skills/harden/SKILL.md +9 -3
  195. package/.agent/skills/harness-protocol/SKILL.md +1 -0
  196. package/.agent/skills/i18n-localization/SKILL.md +10 -9
  197. package/.agent/skills/impeccable/SKILL.md +5 -0
  198. package/.agent/skills/improve-codebase-architecture/SKILL.md +10 -5
  199. package/.agent/skills/improve-ui/SKILL.md +12 -5
  200. package/.agent/skills/intelligent-routing/SKILL.md +7 -6
  201. package/.agent/skills/knowledge-graph/SKILL.md +1 -0
  202. package/.agent/skills/landing-page/SKILL.md +1 -0
  203. package/.agent/skills/lint-and-validate/SKILL.md +9 -4
  204. package/.agent/skills/llm-engineering/SKILL.md +55 -54
  205. package/.agent/skills/local-first/SKILL.md +20 -19
  206. package/.agent/skills/local-first-architecture/SKILL.md +6 -1
  207. package/.agent/skills/lottie-animation/SKILL.md +7 -3
  208. package/.agent/skills/marquee-loop/SKILL.md +1 -0
  209. package/.agent/skills/masked-reveal/SKILL.md +17 -4
  210. package/.agent/skills/mcp-builder/SKILL.md +13 -10
  211. package/.agent/skills/micro-interaction/SKILL.md +20 -5
  212. package/.agent/skills/mobile-design/SKILL.md +26 -15
  213. package/.agent/skills/monorepo-management/SKILL.md +3 -2
  214. package/.agent/skills/morphing-icons/SKILL.md +16 -6
  215. package/.agent/skills/motion-engineering/SKILL.md +19 -15
  216. package/.agent/skills/nextjs-react-expert/SKILL.md +26 -21
  217. package/.agent/skills/nodejs-best-practices/SKILL.md +89 -76
  218. package/.agent/skills/observability/SKILL.md +45 -44
  219. package/.agent/skills/opentelemetry-observability/SKILL.md +59 -0
  220. package/.agent/skills/page-transition-animation/SKILL.md +6 -3
  221. package/.agent/skills/parallel-agents/SKILL.md +7 -2
  222. package/.agent/skills/performance-profiling/SKILL.md +24 -13
  223. package/.agent/skills/plan-writing/SKILL.md +1 -0
  224. package/.agent/skills/platform-engineer/SKILL.md +3 -2
  225. package/.agent/skills/platform-engineering-opentofu/SKILL.md +68 -0
  226. package/.agent/skills/playwright-ai-e2e/SKILL.md +60 -0
  227. package/.agent/skills/playwright-best-practices/SKILL.md +29 -28
  228. package/.agent/skills/polish/SKILL.md +13 -12
  229. package/.agent/skills/powershell-windows/SKILL.md +1 -0
  230. package/.agent/skills/pricing-page/SKILL.md +8 -3
  231. package/.agent/skills/product-aware-heuristics/SKILL.md +122 -72
  232. package/.agent/skills/progressive-blur/SKILL.md +9 -4
  233. package/.agent/skills/project-idioms/SKILL.md +1 -0
  234. package/.agent/skills/property-based-testing/SKILL.md +58 -0
  235. package/.agent/skills/python-patterns/SKILL.md +1 -0
  236. package/.agent/skills/python-pro/SKILL.md +1 -0
  237. package/.agent/skills/quieter/SKILL.md +6 -0
  238. package/.agent/skills/react-doctor/SKILL.md +6 -0
  239. package/.agent/skills/react-specialist/SKILL.md +32 -28
  240. package/.agent/skills/readme-builder/SKILL.md +1 -0
  241. package/.agent/skills/realtime-patterns/SKILL.md +34 -33
  242. package/.agent/skills/red-team-tactics/SKILL.md +1 -0
  243. package/.agent/skills/redesign-skill/SKILL.md +6 -0
  244. package/.agent/skills/review-animations/SKILL.md +5 -2
  245. package/.agent/skills/review-animations/STANDARDS.md +37 -23
  246. package/.agent/skills/rust-pro/SKILL.md +1 -0
  247. package/.agent/skills/seo-fundamentals/SKILL.md +4 -3
  248. package/.agent/skills/server-management/SKILL.md +1 -0
  249. package/.agent/skills/shadcn-ui-expert/SKILL.md +28 -22
  250. package/.agent/skills/shape/SKILL.md +9 -2
  251. package/.agent/skills/skill-creator/SKILL.md +1 -0
  252. package/.agent/skills/soft-skill/SKILL.md +6 -0
  253. package/.agent/skills/sounds-on-the-web/SKILL.md +1 -0
  254. package/.agent/skills/sql-pro/SKILL.md +1 -0
  255. package/.agent/skills/supabase-postgres-best-practices/SKILL.md +1 -0
  256. package/.agent/skills/svg-animation/SKILL.md +16 -7
  257. package/.agent/skills/swiftui-expert/SKILL.md +1 -0
  258. package/.agent/skills/swiss-design/SKILL.md +5 -0
  259. package/.agent/skills/system-design-pro/SKILL.md +1 -0
  260. package/.agent/skills/systematic-debugging/SKILL.md +1 -0
  261. package/.agent/skills/tailwind-patterns/SKILL.md +25 -11
  262. package/.agent/skills/taste-skill/SKILL.md +6 -0
  263. package/.agent/skills/tdd-workflow/SKILL.md +5 -0
  264. package/.agent/skills/test-result-analyzer/SKILL.md +1 -0
  265. package/.agent/skills/testing-patterns/SKILL.md +123 -115
  266. package/.agent/skills/thermo-nuclear-code-quality-review/SKILL.md +5 -0
  267. package/.agent/skills/thinking-protocol/SKILL.md +3 -0
  268. package/.agent/skills/to-spring-or-not-to-spring/SKILL.md +9 -8
  269. package/.agent/skills/transitions-dev/SKILL.md +19 -6
  270. package/.agent/skills/trend-researcher/SKILL.md +1 -0
  271. package/.agent/skills/typescript-advanced/SKILL.md +23 -18
  272. package/.agent/skills/typeset/SKILL.md +10 -1
  273. package/.agent/skills/ui-reasoning-engine/SKILL.md +67 -48
  274. package/.agent/skills/ui-skill-packs/SKILL.md +6 -1
  275. package/.agent/skills/ui-skills-root/SKILL.md +39 -38
  276. package/.agent/skills/ui-ux-pro-max/SKILL.md +34 -26
  277. package/.agent/skills/ui-ux-researcher/SKILL.md +1 -0
  278. package/.agent/skills/vector-search-pgvector/SKILL.md +78 -0
  279. package/.agent/skills/vue-expert/SKILL.md +22 -19
  280. package/.agent/skills/vulnerability-scanner/SKILL.md +22 -21
  281. package/.agent/skills/web-accessibility-auditor/SKILL.md +2 -1
  282. package/.agent/skills/web-design-guidelines/SKILL.md +16 -10
  283. package/.agent/skills/web-quality-audit/SKILL.md +5 -0
  284. package/.agent/skills/webapp-testing/SKILL.md +14 -13
  285. package/.agent/skills/webgpu-performance/SKILL.md +1 -0
  286. package/.agent/skills/whimsy-injector/SKILL.md +9 -5
  287. package/.agent/skills/workflow-optimizer/SKILL.md +1 -0
  288. package/.agent/skills/zero-trust-passkeys/SKILL.md +90 -0
  289. package/.agent/templates/DESIGN.md +119 -110
  290. package/.agent/templates/design-tokens.json +9 -6
  291. package/.agent/workflows/acf.md +1 -0
  292. package/.agent/workflows/api-tester.md +1 -0
  293. package/.agent/workflows/audit.md +1 -0
  294. package/.agent/workflows/brainstorm.md +1 -0
  295. package/.agent/workflows/changelog.md +1 -0
  296. package/.agent/workflows/contract.md +60 -0
  297. package/.agent/workflows/create.md +1 -0
  298. package/.agent/workflows/debug.md +1 -0
  299. package/.agent/workflows/deploy.md +1 -0
  300. package/.agent/workflows/enhance.md +1 -0
  301. package/.agent/workflows/fix-ci.md +89 -0
  302. package/.agent/workflows/fix.md +1 -0
  303. package/.agent/workflows/generate.md +1 -0
  304. package/.agent/workflows/marathon.md +1 -0
  305. package/.agent/workflows/migrate.md +1 -0
  306. package/.agent/workflows/minimal.md +9 -7
  307. package/.agent/workflows/orchestrate.md +1 -0
  308. package/.agent/workflows/performance-benchmarker.md +1 -0
  309. package/.agent/workflows/pipeline.md +26 -21
  310. package/.agent/workflows/plan.md +1 -0
  311. package/.agent/workflows/preview.md +1 -0
  312. package/.agent/workflows/refactor.md +3 -2
  313. package/.agent/workflows/review-ai.md +4 -3
  314. package/.agent/workflows/review.md +1 -0
  315. package/.agent/workflows/session.md +1 -0
  316. package/.agent/workflows/status.md +1 -0
  317. package/.agent/workflows/strengthen-skills.md +1 -0
  318. package/.agent/workflows/super-prompt.md +1 -0
  319. package/.agent/workflows/swarm.md +15 -3
  320. package/.agent/workflows/test.md +8 -7
  321. package/.agent/workflows/tribunal-backend.md +3 -2
  322. package/.agent/workflows/tribunal-cicd.md +104 -0
  323. package/.agent/workflows/tribunal-database.md +1 -0
  324. package/.agent/workflows/tribunal-frontend.md +8 -6
  325. package/.agent/workflows/tribunal-full.md +15 -14
  326. package/.agent/workflows/tribunal-mobile.md +6 -5
  327. package/.agent/workflows/tribunal-performance.md +1 -0
  328. package/.agent/workflows/tribunal-speed.md +1 -0
  329. package/.agent/workflows/tribunal-ui.md +29 -21
  330. package/.agent/workflows/ui-ux-pro-max.md +19 -15
  331. package/CONTRIBUTING.md +3 -3
  332. package/README.md +160 -88
  333. package/SECURITY.md +11 -11
  334. package/bin/mcp-server.js +512 -233
  335. package/bin/tribunal-kit.js +29 -30
  336. package/bin/wrapper.js +45 -48
  337. package/dist/cli.js +26 -0
  338. package/dist/commands/case.js +2 -0
  339. package/dist/commands/contract.js +440 -0
  340. package/dist/commands/memory.js +3 -2
  341. package/dist/commands/native.js +36 -0
  342. package/dist/commands/status.js +6 -0
  343. package/dist/esm/index.mjs +32 -0
  344. package/dist/index.d.ts +58 -3
  345. package/package.json +20 -16
  346. package/scripts/benchmark.js +62 -86
  347. package/scripts/changelog.js +67 -73
  348. package/scripts/fix-vbc.js +13 -15
  349. package/scripts/fix-vbc.ps1 +2 -118
  350. package/scripts/stress_benchmark.js +358 -0
  351. package/scripts/sync-version.js +71 -41
  352. package/scripts/validate-payload.js +23 -30
  353. package/scripts/visual_audit.js +23 -13
@@ -22,40 +22,30 @@
22
22
  * node .agent/scripts/skill_evolution.js status
23
23
  */
24
24
 
25
- "use strict";
25
+ 'use strict';
26
26
 
27
- const fs = require("fs");
28
- const path = require("path");
29
- const https = require("https");
30
- const { execSync } = require("child_process");
31
- const readline = require("readline");
27
+ const fs = require('fs');
28
+ const path = require('path');
29
+ const https = require('https');
30
+ const { execSync } = require('child_process');
31
+ const readline = require('readline');
32
32
 
33
33
  // ── Colours ──────────────────────────────────────────────────────────────────
34
- const {
35
- GREEN,
36
- YELLOW,
37
- CYAN,
38
- RED,
39
- BLUE,
40
- BOLD,
41
- DIM,
42
- RESET,
43
- } = require("./_colors");
34
+ const { GREEN, YELLOW, CYAN, RED, BLUE, BOLD, DIM, RESET } = require('./_colors');
44
35
 
45
36
  // ── Shared Utilities ──────────────────────────────────────────────────────────
46
- const { findAgentDir } = require("./_utils");
47
-
37
+ const { findAgentDir } = require('./_utils');
48
38
 
49
39
  function getPaths(startDir = process.cwd()) {
50
40
  const agentDir = findAgentDir(startDir);
51
- const skillDir = path.join(agentDir, "skills", "project-idioms");
52
- const historyDir = path.join(agentDir, "history", "skill-evolution");
41
+ const skillDir = path.join(agentDir, 'skills', 'project-idioms');
42
+ const historyDir = path.join(agentDir, 'history', 'skill-evolution');
53
43
  return {
54
44
  agentDir,
55
45
  skillDir,
56
- skillFile: path.join(skillDir, "SKILL.md"),
46
+ skillFile: path.join(skillDir, 'SKILL.md'),
57
47
  historyDir,
58
- logFile: path.join(historyDir, "digest-log.json"),
48
+ logFile: path.join(historyDir, 'digest-log.json'),
59
49
  };
60
50
  }
61
51
 
@@ -98,7 +88,7 @@ const NOISE_PATTERNS = [
98
88
  ];
99
89
 
100
90
  function architecturalWeight(line) {
101
- const code = line.replace(/^[+-]/, "").trim();
91
+ const code = line.replace(/^[+-]/, '').trim();
102
92
  for (const p of NOISE_PATTERNS) {
103
93
  if (p.test(code)) return 0;
104
94
  }
@@ -147,34 +137,30 @@ function normalizedSimilarity(a, b) {
147
137
  */
148
138
  function isDuplicateIdiom(newPattern, existingIdioms, threshold = 0.8) {
149
139
  const newLow = newPattern.toLowerCase();
150
- return existingIdioms.some((ex) => {
151
- const exLow = (ex.pattern || "").toLowerCase();
140
+ return existingIdioms.some(ex => {
141
+ const exLow = (ex.pattern || '').toLowerCase();
152
142
  return normalizedSimilarity(newLow, exLow) >= threshold;
153
143
  });
154
144
  }
155
145
 
156
146
  function semanticDelta(diffText, minWeight = 2) {
157
- const lines = diffText.split("\n");
147
+ const lines = diffText.split('\n');
158
148
  const kept = [];
159
149
  let currentHunkHasHigh = false;
160
150
  let hunkLines = [];
161
151
 
162
152
  for (const line of lines) {
163
- if (
164
- line.startsWith("---") ||
165
- line.startsWith("+++") ||
166
- line.startsWith("diff --git")
167
- ) {
153
+ if (line.startsWith('---') || line.startsWith('+++') || line.startsWith('diff --git')) {
168
154
  kept.push(line);
169
155
  continue;
170
156
  }
171
- if (line.startsWith("@@")) {
157
+ if (line.startsWith('@@')) {
172
158
  if (currentHunkHasHigh) kept.push(...hunkLines);
173
159
  currentHunkHasHigh = false;
174
160
  hunkLines = [line];
175
161
  continue;
176
162
  }
177
- if (line.startsWith("+") || line.startsWith("-")) {
163
+ if (line.startsWith('+') || line.startsWith('-')) {
178
164
  const w = architecturalWeight(line);
179
165
  hunkLines.push(line);
180
166
  if (w >= minWeight) currentHunkHasHigh = true;
@@ -184,25 +170,25 @@ function semanticDelta(diffText, minWeight = 2) {
184
170
  }
185
171
  if (currentHunkHasHigh) kept.push(...hunkLines);
186
172
 
187
- let result = kept.join("\n");
188
- result = result.replace(/\n( ?\n){3,}/g, "\n\n");
173
+ let result = kept.join('\n');
174
+ result = result.replace(/\n( ?\n){3,}/g, '\n\n');
189
175
  return result.trim();
190
176
  }
191
177
 
192
178
  // ── Git helpers ────────────────────────────────────────────────────────────────
193
- function getGitDiff(mode = "staged") {
179
+ function getGitDiff(mode = 'staged') {
194
180
  try {
195
181
  let cmd;
196
- if (mode === "staged") cmd = "git diff --cached --unified=3";
197
- else if (mode === "head") cmd = "git diff HEAD~1 HEAD --unified=3";
198
- else cmd = "git diff --unified=3";
182
+ if (mode === 'staged') cmd = 'git diff --cached --unified=3';
183
+ else if (mode === 'head') cmd = 'git diff HEAD~1 HEAD --unified=3';
184
+ else cmd = 'git diff --unified=3';
199
185
  return execSync(cmd, {
200
- encoding: "utf8",
186
+ encoding: 'utf8',
201
187
  timeout: 10000,
202
- stdio: ["pipe", "pipe", "pipe"],
188
+ stdio: ['pipe', 'pipe', 'pipe'],
203
189
  });
204
190
  } catch {
205
- return "";
191
+ return '';
206
192
  }
207
193
  }
208
194
 
@@ -214,10 +200,9 @@ function countTokensEstimate(text) {
214
200
  function loadExistingIdioms() {
215
201
  const { skillFile } = getPaths();
216
202
  if (!fs.existsSync(skillFile)) return [];
217
- const content = fs.readFileSync(skillFile, "utf8");
203
+ const content = fs.readFileSync(skillFile, 'utf8');
218
204
  const idioms = [];
219
- const pattern =
220
- /\|\s*(\d+)\s*\|\s*`([^`]+)`\s*\|\s*([^|]+)\|\s*([^|]+)\|\s*([^|]+)\|/g;
205
+ const pattern = /\|\s*(\d+)\s*\|\s*`([^`]+)`\s*\|\s*([^|]+)\|\s*([^|]+)\|\s*([^|]+)\|/g;
221
206
  let m;
222
207
  while ((m = pattern.exec(content)) !== null) {
223
208
  idioms.push({
@@ -233,16 +218,15 @@ function loadExistingIdioms() {
233
218
 
234
219
  function nextIdiomId(idioms) {
235
220
  if (!idioms.length) return 1;
236
- return Math.max(...idioms.map((i) => i.id)) + 1;
221
+ return Math.max(...idioms.map(i => i.id)) + 1;
237
222
  }
238
223
 
239
224
  function renderSkillMd(idioms, digestCount) {
240
225
  const now = new Date().toISOString().slice(0, 10);
241
226
  const rows = idioms.map(
242
- (i) =>
243
- `| ${i.id} | \`${i.pattern}\` | ${i.reason} | ${i.domain} | ${i.since} |`,
227
+ i => `| ${i.id} | \`${i.pattern}\` | ${i.reason} | ${i.domain} | ${i.since} |`,
244
228
  );
245
- const table = rows.length ? rows.join("\n") : "_No idioms recorded yet._";
229
+ const table = rows.length ? rows.join('\n') : '_No idioms recorded yet._';
246
230
 
247
231
  return `---
248
232
  name: project-idioms
@@ -330,22 +314,22 @@ function parseLlmYamlResponse(response) {
330
314
  let inIdioms = false;
331
315
  let current = {};
332
316
 
333
- for (const line of response.split("\n")) {
317
+ for (const line of response.split('\n')) {
334
318
  const stripped = line.trim();
335
- if (stripped === "idioms:") {
319
+ if (stripped === 'idioms:') {
336
320
  inIdioms = true;
337
321
  continue;
338
322
  }
339
323
  if (!inIdioms) continue;
340
- if (stripped.startsWith("- pattern:")) {
324
+ if (stripped.startsWith('- pattern:')) {
341
325
  if (current.pattern) idioms.push(current);
342
326
  current = {
343
- pattern: stripped.split(":", 2)[1].trim().replace(/^"|"$/g, ""),
327
+ pattern: stripped.split(':', 2)[1].trim().replace(/^"|"$/g, ''),
344
328
  };
345
- } else if (stripped.startsWith("reason:") && current.pattern) {
346
- current.reason = stripped.split(":", 2)[1].trim().replace(/^"|"$/g, "");
347
- } else if (stripped.startsWith("domain:") && current.pattern) {
348
- current.domain = stripped.split(":", 2)[1].trim().replace(/^"|"$/g, "");
329
+ } else if (stripped.startsWith('reason:') && current.pattern) {
330
+ current.reason = stripped.split(':', 2)[1].trim().replace(/^"|"$/g, '');
331
+ } else if (stripped.startsWith('domain:') && current.pattern) {
332
+ current.domain = stripped.split(':', 2)[1].trim().replace(/^"|"$/g, '');
349
333
  }
350
334
  }
351
335
  if (current.pattern) idioms.push(current);
@@ -358,7 +342,7 @@ function loadLog() {
358
342
  fs.mkdirSync(historyDir, { recursive: true });
359
343
  if (fs.existsSync(logFile)) {
360
344
  try {
361
- return JSON.parse(fs.readFileSync(logFile, "utf8"));
345
+ return JSON.parse(fs.readFileSync(logFile, 'utf8'));
362
346
  } catch {
363
347
  /* fallthrough */
364
348
  }
@@ -369,7 +353,7 @@ function loadLog() {
369
353
  function saveLog(log) {
370
354
  const { historyDir, logFile } = getPaths();
371
355
  fs.mkdirSync(historyDir, { recursive: true });
372
- fs.writeFileSync(logFile, JSON.stringify(log, null, 2), "utf8");
356
+ fs.writeFileSync(logFile, JSON.stringify(log, null, 2), 'utf8');
373
357
  }
374
358
 
375
359
  // ── Auto-LLM API Integration ─────────────────────────────────────────────────
@@ -379,11 +363,9 @@ function saveLog(log) {
379
363
 
380
364
  function detectLlmProvider() {
381
365
  if (process.env.ANTHROPIC_API_KEY)
382
- return { provider: "anthropic", key: process.env.ANTHROPIC_API_KEY };
383
- if (process.env.OPENAI_API_KEY)
384
- return { provider: "openai", key: process.env.OPENAI_API_KEY };
385
- if (process.env.GEMINI_API_KEY)
386
- return { provider: "gemini", key: process.env.GEMINI_API_KEY };
366
+ return { provider: 'anthropic', key: process.env.ANTHROPIC_API_KEY };
367
+ if (process.env.OPENAI_API_KEY) return { provider: 'openai', key: process.env.OPENAI_API_KEY };
368
+ if (process.env.GEMINI_API_KEY) return { provider: 'gemini', key: process.env.GEMINI_API_KEY };
387
369
  return null;
388
370
  }
389
371
 
@@ -404,23 +386,23 @@ async function callLlmApi(prompt, provider, apiKey) {
404
386
  const data = JSON.stringify(body);
405
387
  const req = https.request(
406
388
  {
407
- method: "POST",
389
+ method: 'POST',
408
390
  hostname,
409
391
  path,
410
- headers: { ...headers, "Content-Length": Buffer.byteLength(data) },
392
+ headers: { ...headers, 'Content-Length': Buffer.byteLength(data) },
411
393
  },
412
- (res) => {
413
- let raw = "";
414
- res.on("data", (c) => {
394
+ res => {
395
+ let raw = '';
396
+ res.on('data', c => {
415
397
  raw += c;
416
398
  });
417
- res.on("end", () => resolve(raw));
418
- res.on("error", reject);
399
+ res.on('end', () => resolve(raw));
400
+ res.on('error', reject);
419
401
  },
420
402
  );
421
- req.on("error", reject);
403
+ req.on('error', reject);
422
404
  req.setTimeout(timeout, () => {
423
- req.destroy(new Error("LLM API timeout"));
405
+ req.destroy(new Error('LLM API timeout'));
424
406
  });
425
407
  req.write(data);
426
408
  req.end();
@@ -428,37 +410,37 @@ async function callLlmApi(prompt, provider, apiKey) {
428
410
  }
429
411
 
430
412
  try {
431
- if (provider === "anthropic") {
413
+ if (provider === 'anthropic') {
432
414
  const raw = await httpsPost(
433
- "api.anthropic.com",
434
- "/v1/messages",
415
+ 'api.anthropic.com',
416
+ '/v1/messages',
435
417
  {
436
- "Content-Type": "application/json",
437
- "x-api-key": apiKey,
438
- "anthropic-version": "2023-06-01",
418
+ 'Content-Type': 'application/json',
419
+ 'x-api-key': apiKey,
420
+ 'anthropic-version': '2023-06-01',
439
421
  },
440
422
  {
441
- model: "claude-3-haiku-20240307", // Fastest/cheapest — idiom extraction
423
+ model: 'claude-3-haiku-20240307', // Fastest/cheapest — idiom extraction
442
424
  max_tokens: 512,
443
- messages: [{ role: "user", content: prompt }],
425
+ messages: [{ role: 'user', content: prompt }],
444
426
  },
445
427
  );
446
428
  const json = JSON.parse(raw);
447
429
  return json?.content?.[0]?.text ?? null;
448
430
  }
449
431
 
450
- if (provider === "openai") {
432
+ if (provider === 'openai') {
451
433
  const raw = await httpsPost(
452
- "api.openai.com",
453
- "/v1/chat/completions",
434
+ 'api.openai.com',
435
+ '/v1/chat/completions',
454
436
  {
455
- "Content-Type": "application/json",
437
+ 'Content-Type': 'application/json',
456
438
  Authorization: `Bearer ${apiKey}`,
457
439
  },
458
440
  {
459
- model: "gpt-4o-mini", // Cheapest capable model for YAML extraction
441
+ model: 'gpt-4o-mini', // Cheapest capable model for YAML extraction
460
442
  max_tokens: 512,
461
- messages: [{ role: "user", content: prompt }],
443
+ messages: [{ role: 'user', content: prompt }],
462
444
  temperature: 0.1,
463
445
  },
464
446
  );
@@ -466,11 +448,11 @@ async function callLlmApi(prompt, provider, apiKey) {
466
448
  return json?.choices?.[0]?.message?.content ?? null;
467
449
  }
468
450
 
469
- if (provider === "gemini") {
451
+ if (provider === 'gemini') {
470
452
  const raw = await httpsPost(
471
- "generativelanguage.googleapis.com",
453
+ 'generativelanguage.googleapis.com',
472
454
  `/v1beta/models/gemini-1.5-flash:generateContent?key=${apiKey}`,
473
- { "Content-Type": "application/json" },
455
+ { 'Content-Type': 'application/json' },
474
456
  {
475
457
  contents: [{ parts: [{ text: prompt }] }],
476
458
  generationConfig: { maxOutputTokens: 512, temperature: 0.1 },
@@ -488,8 +470,8 @@ async function callLlmApi(prompt, provider, apiKey) {
488
470
  // ── Commands ──────────────────────────────────────────────────────────────────
489
471
  // ── Strategy Filtering ────────────────────────────────────────────────────────
490
472
  function applyStrategyFilter(signals, strategy) {
491
- if (strategy === "repair-only") {
492
- return signals.filter((s) => s.type === "log_error");
473
+ if (strategy === 'repair-only') {
474
+ return signals.filter(s => s.type === 'log_error');
493
475
  }
494
476
  return signals;
495
477
  }
@@ -531,52 +513,64 @@ function parseEvolutionYaml(response) {
531
513
  let currentSection = null; // 'idioms' | 'cases'
532
514
  let current = null;
533
515
 
534
- const lines = response.split("\n");
516
+ const lines = response.split('\n');
535
517
  for (const line of lines) {
536
518
  const trimmed = line.trim();
537
- if (trimmed === "idioms:") {
519
+ if (trimmed === 'idioms:') {
538
520
  if (current) {
539
- if (currentSection === "idioms") idioms.push(current);
540
- if (currentSection === "cases") cases.push(current);
521
+ if (currentSection === 'idioms') idioms.push(current);
522
+ if (currentSection === 'cases') cases.push(current);
541
523
  }
542
- currentSection = "idioms";
524
+ currentSection = 'idioms';
543
525
  current = null;
544
526
  continue;
545
527
  }
546
- if (trimmed === "cases:") {
528
+ if (trimmed === 'cases:') {
547
529
  if (current) {
548
- if (currentSection === "idioms") idioms.push(current);
549
- if (currentSection === "cases") cases.push(current);
530
+ if (currentSection === 'idioms') idioms.push(current);
531
+ if (currentSection === 'cases') cases.push(current);
550
532
  }
551
- currentSection = "cases";
533
+ currentSection = 'cases';
552
534
  current = null;
553
535
  continue;
554
536
  }
555
537
 
556
- if (trimmed.startsWith("- pattern:") || (trimmed.startsWith("- ") && trimmed.includes("pattern:"))) {
538
+ if (
539
+ trimmed.startsWith('- pattern:') ||
540
+ (trimmed.startsWith('- ') && trimmed.includes('pattern:'))
541
+ ) {
557
542
  if (current) {
558
- if (currentSection === "idioms") idioms.push(current);
559
- if (currentSection === "cases") cases.push(current);
543
+ if (currentSection === 'idioms') idioms.push(current);
544
+ if (currentSection === 'cases') cases.push(current);
560
545
  }
561
- let pat = "";
562
- if (trimmed.startsWith("- pattern:")) {
563
- pat = trimmed.substring("- pattern:".length).trim();
546
+ let pat = '';
547
+ if (trimmed.startsWith('- pattern:')) {
548
+ pat = trimmed.substring('- pattern:'.length).trim();
564
549
  } else {
565
- pat = trimmed.split("pattern:", 2)[1].trim();
550
+ pat = trimmed.split('pattern:', 2)[1].trim();
566
551
  }
567
- current = { pattern: pat.replace(/^['"]|['"]$/g, "") };
568
- } else if (trimmed.startsWith("reason:") && current) {
569
- current.reason = trimmed.substring("reason:".length).trim().replace(/^['"]|['"]$/g, "");
570
- } else if (trimmed.startsWith("domain:") && current) {
571
- current.domain = trimmed.substring("domain:".length).trim().replace(/^['"]|['"]$/g, "");
572
- } else if (trimmed.startsWith("verdict:") && current) {
573
- current.verdict = trimmed.substring("verdict:".length).trim().replace(/^['"]|['"]$/g, "");
552
+ current = { pattern: pat.replace(/^['"]|['"]$/g, '') };
553
+ } else if (trimmed.startsWith('reason:') && current) {
554
+ current.reason = trimmed
555
+ .substring('reason:'.length)
556
+ .trim()
557
+ .replace(/^['"]|['"]$/g, '');
558
+ } else if (trimmed.startsWith('domain:') && current) {
559
+ current.domain = trimmed
560
+ .substring('domain:'.length)
561
+ .trim()
562
+ .replace(/^['"]|['"]$/g, '');
563
+ } else if (trimmed.startsWith('verdict:') && current) {
564
+ current.verdict = trimmed
565
+ .substring('verdict:'.length)
566
+ .trim()
567
+ .replace(/^['"]|['"]$/g, '');
574
568
  }
575
569
  }
576
570
 
577
571
  if (current) {
578
- if (currentSection === "idioms") idioms.push(current);
579
- if (currentSection === "cases") cases.push(current);
572
+ if (currentSection === 'idioms') idioms.push(current);
573
+ if (currentSection === 'cases') cases.push(current);
580
574
  }
581
575
 
582
576
  return { idioms, cases };
@@ -584,23 +578,20 @@ function parseEvolutionYaml(response) {
584
578
 
585
579
  // ── Commands ──────────────────────────────────────────────────────────────────
586
580
  async function cmdDigest(args) {
587
- const dryRun = args.includes("--dry-run");
588
- const diffMode = args.includes("--head") ? "head" : "staged";
581
+ const dryRun = args.includes('--dry-run');
582
+ const diffMode = args.includes('--head') ? 'head' : 'staged';
589
583
 
590
- const logArg = args.find((a) => a.startsWith("--log="));
591
- const logFile = logArg ? logArg.split("=").slice(1).join("=") : null;
584
+ const logArg = args.find(a => a.startsWith('--log='));
585
+ const logFile = logArg ? logArg.split('=').slice(1).join('=') : null;
592
586
 
593
- const strategyArg = args.find((a) => a.startsWith("--strategy="));
594
- const strategy = strategyArg ? strategyArg.split("=").slice(1).join("=") : "balanced";
587
+ const strategyArg = args.find(a => a.startsWith('--strategy='));
588
+ const strategy = strategyArg ? strategyArg.split('=').slice(1).join('=') : 'balanced';
595
589
 
596
- console.log(
597
- `\n${BOLD}${CYAN}━━━ Skill Evolution Digest Cycle ━━━━━━━━━━━━━━━━${RESET}`,
598
- );
599
- if (dryRun)
600
- console.log(` ${YELLOW}DRY RUN — no files will be written${RESET}\n`);
590
+ console.log(`\n${BOLD}${CYAN}━━━ Skill Evolution — Digest Cycle ━━━━━━━━━━━━━━━━${RESET}`);
591
+ if (dryRun) console.log(` ${YELLOW}DRY RUNno files will be written${RESET}\n`);
601
592
 
602
- let rawDiff = "";
603
- let delta = "";
593
+ let rawDiff = '';
594
+ let delta = '';
604
595
  let rawTokens = 0;
605
596
  let deltaTokens = 0;
606
597
  let logSignals = [];
@@ -612,28 +603,22 @@ async function cmdDigest(args) {
612
603
  console.log(` ${RED}✖ Log file not found: ${logFile}${RESET}\n`);
613
604
  return;
614
605
  }
615
- const logText = fs.readFileSync(logFile, "utf8");
606
+ const logText = fs.readFileSync(logFile, 'utf8');
616
607
  rawTokens = countTokensEstimate(logText);
617
608
 
618
- console.log(
619
- ` ${DIM}[2/5] Extracting signals from log (Signal Detector)...${RESET}`,
620
- );
621
- const { detectSignals } = require("./signal_detector");
609
+ console.log(` ${DIM}[2/5] Extracting signals from log (Signal Detector)...${RESET}`);
610
+ const { detectSignals } = require('./signal_detector');
622
611
  const signals = detectSignals(logText);
623
612
 
624
613
  if (signals.length === 0) {
625
- console.log(
626
- ` ${GREEN}✔ No errors or performance signals found in logs.${RESET}`,
627
- );
614
+ console.log(` ${GREEN}✔ No errors or performance signals found in logs.${RESET}`);
628
615
  console.log(` ${DIM} No self-evolution reflection needed.${RESET}\n`);
629
616
  return;
630
617
  }
631
618
 
632
619
  logSignals = applyStrategyFilter(signals, strategy);
633
620
  if (logSignals.length === 0) {
634
- console.log(
635
- ` ${YELLOW}⚠ Strategy filter [${strategy}] filtered out all signals.${RESET}\n`,
636
- );
621
+ console.log(` ${YELLOW}⚠ Strategy filter [${strategy}] filtered out all signals.${RESET}\n`);
637
622
  return;
638
623
  }
639
624
 
@@ -642,45 +627,33 @@ async function cmdDigest(args) {
642
627
  );
643
628
 
644
629
  delta = logSignals
645
- .map((s) => {
630
+ .map(s => {
646
631
  let chunk = `--- SIGNAL: ${s.type} ---\n`;
647
- if (s.file) chunk += `File: ${s.file}${s.line ? `:${s.line}` : ""}\n`;
632
+ if (s.file) chunk += `File: ${s.file}${s.line ? `:${s.line}` : ''}\n`;
648
633
  chunk += `Message: ${s.message}\n`;
649
634
  chunk += `Context:\n${s.context}\n`;
650
635
  return chunk;
651
636
  })
652
- .join("\n\n");
637
+ .join('\n\n');
653
638
 
654
639
  deltaTokens = countTokensEstimate(delta);
655
640
  } else {
656
641
  console.log(` ${DIM}[1/5] Fetching git diff (${diffMode})...${RESET}`);
657
642
  rawDiff = getGitDiff(diffMode);
658
643
  if (!rawDiff.trim()) {
659
- console.log(
660
- ` ${YELLOW} No diff found. Commit or stage changes first.${RESET}`,
661
- );
662
- console.log(
663
- ` ${DIM}Tip: Use --head to diff against the last commit.${RESET}\n`,
664
- );
644
+ console.log(` ${YELLOW}⚠ No diff found. Commit or stage changes first.${RESET}`);
645
+ console.log(` ${DIM}Tip: Use --head to diff against the last commit.${RESET}\n`);
665
646
  return;
666
647
  }
667
648
 
668
649
  rawTokens = countTokensEstimate(rawDiff);
669
- console.log(
670
- ` ${DIM} Raw diff: ~${rawTokens} tokens (${rawDiff.length} chars)${RESET}`,
671
- );
650
+ console.log(` ${DIM} Raw diff: ~${rawTokens} tokens (${rawDiff.length} chars)${RESET}`);
672
651
 
673
- console.log(
674
- ` ${DIM}[2/5] Extracting architectural delta (Semantic Filter)...${RESET}`,
675
- );
652
+ console.log(` ${DIM}[2/5] Extracting architectural delta (Semantic Filter)...${RESET}`);
676
653
  delta = semanticDelta(rawDiff, 2);
677
654
  if (!delta.trim()) {
678
- console.log(
679
- ` ${GREEN} Delta is 100% trivial (whitespace/comments/imports only).${RESET}`,
680
- );
681
- console.log(
682
- ` ${DIM} No LLM call needed. Zero tokens consumed.${RESET}\n`,
683
- );
655
+ console.log(` ${GREEN}✔ Delta is 100% trivial (whitespace/comments/imports only).${RESET}`);
656
+ console.log(` ${DIM} No LLM call needed. Zero tokens consumed.${RESET}\n`);
684
657
  return;
685
658
  }
686
659
 
@@ -694,25 +667,21 @@ async function cmdDigest(args) {
694
667
  );
695
668
 
696
669
  console.log(`\n ${BOLD}Architectural Delta Preview:${RESET}`);
697
- const previewLines = delta.split("\n").slice(0, 20);
670
+ const previewLines = delta.split('\n').slice(0, 20);
698
671
  for (const line of previewLines) {
699
- if (line.startsWith("+")) console.log(` ${GREEN}${line}${RESET}`);
700
- else if (line.startsWith("-")) console.log(` ${RED}${line}${RESET}`);
701
- else if (line.startsWith("@@")) console.log(` ${BLUE}${line}${RESET}`);
672
+ if (line.startsWith('+')) console.log(` ${GREEN}${line}${RESET}`);
673
+ else if (line.startsWith('-')) console.log(` ${RED}${line}${RESET}`);
674
+ else if (line.startsWith('@@')) console.log(` ${BLUE}${line}${RESET}`);
702
675
  else console.log(` ${DIM}${line}${RESET}`);
703
676
  }
704
- if (delta.split("\n").length > 20)
705
- console.log(
706
- ` ... (${delta.split("\n").length - 20} more lines)`,
707
- );
677
+ if (delta.split('\n').length > 20)
678
+ console.log(` ... (${delta.split('\n').length - 20} more lines)`);
708
679
 
709
680
  if (dryRun) {
710
681
  console.log(
711
682
  `\n ${YELLOW}[DRY RUN] Would send ${deltaTokens} tokens to LLM for reflection.${RESET}`,
712
683
  );
713
- console.log(
714
- ` ${DIM}Run without --dry-run to complete the digest.${RESET}\n`,
715
- );
684
+ console.log(` ${DIM}Run without --dry-run to complete the digest.${RESET}\n`);
716
685
  return;
717
686
  }
718
687
 
@@ -720,27 +689,19 @@ async function cmdDigest(args) {
720
689
  const reflectionPrompt = isLogMode
721
690
  ? generateLogReflectionPrompt(delta)
722
691
  : generateReflectionPrompt(delta);
723
- let llmResponse = "";
692
+ let llmResponse = '';
724
693
 
725
694
  let llmCreds = detectLlmProvider();
726
695
  if (llmCreds) {
727
- console.log(
728
- ` ${DIM}[3/5] LLM Reflection auto-calling ${llmCreds.provider} API...${RESET}`,
729
- );
730
- const autoResponse = await callLlmApi(
731
- reflectionPrompt,
732
- llmCreds.provider,
733
- llmCreds.key,
734
- );
696
+ console.log(` ${DIM}[3/5] LLM Reflection — auto-calling ${llmCreds.provider} API...${RESET}`);
697
+ const autoResponse = await callLlmApi(reflectionPrompt, llmCreds.provider, llmCreds.key);
735
698
  if (autoResponse) {
736
699
  llmResponse = autoResponse;
737
700
  console.log(
738
- ` ${GREEN}✔ Auto-response received (${llmCreds.provider}) — ${llmResponse.split("\n").length} lines${RESET}`,
701
+ ` ${GREEN}✔ Auto-response received (${llmCreds.provider}) — ${llmResponse.split('\n').length} lines${RESET}`,
739
702
  );
740
703
  } else {
741
- console.log(
742
- ` ${YELLOW}⚠ API call failed — falling back to manual mode${RESET}`,
743
- );
704
+ console.log(` ${YELLOW}⚠ API call failed — falling back to manual mode${RESET}`);
744
705
  llmCreds = null;
745
706
  }
746
707
  }
@@ -753,29 +714,27 @@ async function cmdDigest(args) {
753
714
  console.log(
754
715
  ` ${DIM} Tip: Set ANTHROPIC_API_KEY, OPENAI_API_KEY, or GEMINI_API_KEY to automate this step.${RESET}`,
755
716
  );
756
- console.log(`\n ${BOLD}${"".repeat(60)}${RESET}`);
717
+ console.log(`\n ${BOLD}${''.repeat(60)}${RESET}`);
757
718
  console.log(reflectionPrompt);
758
- console.log(` ${BOLD}${"".repeat(60)}${RESET}`);
759
- console.log(
760
- `\n ${BOLD}Paste LLM response below (type END_RESPONSE when done):${RESET}`,
761
- );
719
+ console.log(` ${BOLD}${''.repeat(60)}${RESET}`);
720
+ console.log(`\n ${BOLD}Paste LLM response below (type END_RESPONSE when done):${RESET}`);
762
721
 
763
722
  const rl = readline.createInterface({
764
723
  input: process.stdin,
765
724
  output: process.stdout,
766
725
  });
767
726
  const responseLines = [];
768
- await new Promise((resolve) => {
769
- const listener = (line) => {
770
- if (line.trim() === "END_RESPONSE") {
771
- rl.removeListener("line", listener);
727
+ await new Promise(resolve => {
728
+ const listener = line => {
729
+ if (line.trim() === 'END_RESPONSE') {
730
+ rl.removeListener('line', listener);
772
731
  resolve();
773
732
  } else responseLines.push(line);
774
733
  };
775
- rl.on("line", listener);
734
+ rl.on('line', listener);
776
735
  });
777
736
  rl.close();
778
- llmResponse = responseLines.join("\n");
737
+ llmResponse = responseLines.join('\n');
779
738
  }
780
739
 
781
740
  console.log(`\n ${DIM}[4/5] Parsing idioms/cases...${RESET}`);
@@ -791,9 +750,7 @@ async function cmdDigest(args) {
791
750
  }
792
751
 
793
752
  if (!newIdioms.length && !newCases.length) {
794
- console.log(
795
- ` ${YELLOW}⚠ No idioms or cases extracted from LLM response.${RESET}`,
796
- );
753
+ console.log(` ${YELLOW}⚠ No idioms or cases extracted from LLM response.${RESET}`);
797
754
  console.log(`\n`);
798
755
  return;
799
756
  }
@@ -801,24 +758,18 @@ async function cmdDigest(args) {
801
758
  if (newIdioms.length > 0) {
802
759
  console.log(` ${GREEN}✔ Extracted ${newIdioms.length} idiom(s)${RESET}`);
803
760
  for (const idiom of newIdioms) {
804
- console.log(
805
- ` ${CYAN}• ${idiom.pattern || "?"}${RESET} — ${idiom.reason || ""}`,
806
- );
761
+ console.log(` ${CYAN}• ${idiom.pattern || '?'}${RESET} — ${idiom.reason || ''}`);
807
762
  }
808
763
  }
809
764
 
810
765
  if (newCases.length > 0) {
811
766
  console.log(` ${GREEN}✔ Extracted ${newCases.length} case precedent(s)${RESET}`);
812
767
  for (const c of newCases) {
813
- console.log(
814
- ` ${RED}• ${c.pattern || "?"}${RESET} — ${c.reason || ""}`,
815
- );
768
+ console.log(` ${RED}• ${c.pattern || '?'}${RESET} — ${c.reason || ''}`);
816
769
  }
817
770
  }
818
771
 
819
- console.log(
820
- `\n ${DIM}[5/5] Merging into project-idioms/SKILL.md...${RESET}`,
821
- );
772
+ console.log(`\n ${DIM}[5/5] Merging into project-idioms/SKILL.md...${RESET}`);
822
773
  const existing = loadExistingIdioms();
823
774
  const log = loadLog();
824
775
  let nextId = nextIdiomId(existing);
@@ -827,15 +778,15 @@ async function cmdDigest(args) {
827
778
  let added = 0;
828
779
 
829
780
  for (const idiom of newIdioms) {
830
- if (isDuplicateIdiom(idiom.pattern || "", existing)) {
781
+ if (isDuplicateIdiom(idiom.pattern || '', existing)) {
831
782
  console.log(` ${DIM} Skipped near-duplicate idiom: ${idiom.pattern}${RESET}`);
832
783
  continue;
833
784
  }
834
785
  merged.push({
835
786
  id: nextId,
836
- pattern: idiom.pattern || "?",
837
- reason: idiom.reason || "No reason provided.",
838
- domain: idiom.domain || "general",
787
+ pattern: idiom.pattern || '?',
788
+ reason: idiom.reason || 'No reason provided.',
789
+ domain: idiom.domain || 'general',
839
790
  since: today,
840
791
  });
841
792
  nextId++;
@@ -847,7 +798,7 @@ async function cmdDigest(args) {
847
798
  const skillMd = renderSkillMd(merged, (log.cycles || []).length + 1);
848
799
  const { skillDir, skillFile } = getPaths();
849
800
  fs.mkdirSync(skillDir, { recursive: true });
850
- fs.writeFileSync(skillFile, skillMd, "utf8");
801
+ fs.writeFileSync(skillFile, skillMd, 'utf8');
851
802
  console.log(` ${GREEN}✔ ${added} new idiom(s) added to SKILL.md${RESET}`);
852
803
  } else {
853
804
  console.log(` ${DIM} No new unique idioms added to SKILL.md.${RESET}`);
@@ -856,16 +807,16 @@ async function cmdDigest(args) {
856
807
  // Record Case Precedents if present
857
808
  if (newCases && newCases.length > 0) {
858
809
  console.log(`\n ${DIM}Merging into Case Law precedents...${RESET}`);
859
- const caseLawManager = require("./case_law_manager");
810
+ const caseLawManager = require('./case_law_manager');
860
811
  const clIndex = caseLawManager.loadIndex();
861
812
  let clNextId = clIndex.next_id;
862
813
  let clAdded = 0;
863
814
 
864
815
  for (const c of newCases) {
865
- const diffText = c.pattern || "";
866
- const reason = c.reason || "";
867
- const domain = c.domain || "general";
868
- const verdict = c.verdict || "REJECTED";
816
+ const diffText = c.pattern || '';
817
+ const reason = c.reason || '';
818
+ const domain = c.domain || 'general';
819
+ const verdict = c.verdict || 'REJECTED';
869
820
  const fingerprint = caseLawManager.contentHash(diffText);
870
821
 
871
822
  let isDup = false;
@@ -876,14 +827,12 @@ async function cmdDigest(args) {
876
827
  }
877
828
  }
878
829
  if (isDup) {
879
- console.log(
880
- ` ${DIM} Skipped duplicate case: ${reason.slice(0, 50)}...${RESET}`,
881
- );
830
+ console.log(` ${DIM} Skipped duplicate case: ${reason.slice(0, 50)}...${RESET}`);
882
831
  continue;
883
832
  }
884
833
 
885
834
  const deltaCase = caseLawManager.semanticDelta(diffText);
886
- const tags = caseLawManager.extractTags(diffText + " " + reason);
835
+ const tags = caseLawManager.extractTags(diffText + ' ' + reason);
887
836
  const caseRecord = {
888
837
  id: clNextId,
889
838
  fingerprint,
@@ -891,8 +840,8 @@ async function cmdDigest(args) {
891
840
  domain,
892
841
  verdict,
893
842
  reason: reason.trim(),
894
- pr_ref: "auto-evolution",
895
- reviewer: "skill-evolution",
843
+ pr_ref: 'auto-evolution',
844
+ reviewer: 'skill-evolution',
896
845
  tags,
897
846
  stack_version: null,
898
847
  diff_raw: diffText.trim(),
@@ -937,20 +886,16 @@ async function cmdDigest(args) {
937
886
 
938
887
  console.log(`\n ${GREEN}✔ Learn cycle complete.${RESET}`);
939
888
  console.log(` ${DIM} Total idioms: ${merged.length}${RESET}`);
940
- console.log(
941
- ` ${DIM} Lifetime tokens saved: ${log.total_tokens_saved}${RESET}\n`,
942
- );
889
+ console.log(` ${DIM} Lifetime tokens saved: ${log.total_tokens_saved}${RESET}\n`);
943
890
  }
944
891
 
945
892
  function cmdShow() {
946
893
  const { skillFile } = getPaths();
947
894
  if (!fs.existsSync(skillFile)) {
948
- console.log(
949
- `${YELLOW}No project-idioms skill found. Run 'digest' first.${RESET}`,
950
- );
895
+ console.log(`${YELLOW}No project-idioms skill found. Run 'digest' first.${RESET}`);
951
896
  return;
952
897
  }
953
- console.log(fs.readFileSync(skillFile, "utf8"));
898
+ console.log(fs.readFileSync(skillFile, 'utf8'));
954
899
  }
955
900
 
956
901
  function cmdReset() {
@@ -974,34 +919,28 @@ function cmdStatus() {
974
919
  const { skillFile } = getPaths();
975
920
  const idiomsExist = fs.existsSync(skillFile);
976
921
 
977
- console.log(
978
- `\n${BOLD}${CYAN}━━━ Skill Evolution Status ━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`,
979
- );
922
+ console.log(`\n${BOLD}${CYAN}━━━ Skill Evolution Status ━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`);
980
923
  console.log(` Digest cycles : ${BOLD}${cycles.length}${RESET}`);
981
924
  console.log(` Total idioms : ${BOLD}${totalIdioms}${RESET}`);
982
925
  console.log(
983
926
  ` Tokens saved : ${GREEN}${totalSaved.toLocaleString()} tokens${RESET} (≈ $${((totalSaved / 1_000_000) * 3).toFixed(4)} at $3/M)`,
984
927
  );
985
- console.log(` SKILL.md exists : ${idiomsExist ? "" : ""}`);
928
+ console.log(` SKILL.md exists : ${idiomsExist ? '' : ''}`);
986
929
 
987
930
  if (cycles.length) {
988
931
  console.log(`\n ${BOLD}Last 5 digest cycles:${RESET}`);
989
932
  for (const cycle of cycles.slice(-5).reverse()) {
990
- const ts = (cycle.timestamp || "?").slice(0, 16);
933
+ const ts = (cycle.timestamp || '?').slice(0, 16);
991
934
  const deltaT = cycle.delta_tokens || 0;
992
935
  const saved = cycle.tokens_saved || 0;
993
936
  const addedCount = cycle.idioms_added || 0;
994
- const pct = Math.floor(
995
- (saved / Math.max(cycle.raw_tokens || 1, 1)) * 100,
996
- );
937
+ const pct = Math.floor((saved / Math.max(cycle.raw_tokens || 1, 1)) * 100);
997
938
  console.log(
998
939
  ` ${DIM}${ts}${RESET} delta=${deltaT}tok saved=${saved}tok (${pct}%) idioms+=${addedCount}`,
999
940
  );
1000
941
  }
1001
942
  }
1002
- console.log(
1003
- `${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}\n`,
1004
- );
943
+ console.log(`${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}\n`);
1005
944
  }
1006
945
 
1007
946
  // ── Main ──────────────────────────────────────────────────────────────────────
@@ -1014,7 +953,7 @@ const COMMANDS = {
1014
953
 
1015
954
  async function main() {
1016
955
  const argv = process.argv.slice(2);
1017
- if (!argv.length || ["-h", "--help", "help"].includes(argv[0])) {
956
+ if (!argv.length || ['-h', '--help', 'help'].includes(argv[0])) {
1018
957
  console.log(`
1019
958
  ${BOLD}skill_evolution.js${RESET} — Tribunal Skill Evolution Forge
1020
959
 
@@ -1051,7 +990,7 @@ module.exports = {
1051
990
  };
1052
991
 
1053
992
  if (require.main === module) {
1054
- main().catch((err) => {
993
+ main().catch(err => {
1055
994
  console.error(err);
1056
995
  process.exit(1);
1057
996
  });