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
@@ -19,12 +19,12 @@
19
19
  * node .agent/scripts/marathon_harness.js add-feature "category" "description" "step1" "step2" ...
20
20
  */
21
21
 
22
- "use strict";
22
+ 'use strict';
23
23
 
24
- const fs = require("fs");
25
- const path = require("path");
26
- const { execSync } = require("child_process");
27
- const { findAgentDir } = require("./_utils");
24
+ const fs = require('fs');
25
+ const path = require('path');
26
+ const { execSync } = require('child_process');
27
+ const { findAgentDir } = require('./_utils');
28
28
 
29
29
  const {
30
30
  GREEN,
@@ -40,29 +40,29 @@ const {
40
40
  ok,
41
41
  warn,
42
42
  info,
43
- } = require("./_colors");
43
+ } = require('./_colors');
44
44
 
45
45
  // ── Paths ────────────────────────────────────────────────────────────────────
46
46
  function getMarathonDir(startDir = process.cwd()) {
47
- return path.join(findAgentDir(startDir), "history", "marathon");
47
+ return path.join(findAgentDir(startDir), 'history', 'marathon');
48
48
  }
49
49
 
50
50
  const MARATHON_DIR = getMarathonDir();
51
- const FEATURE_LIST_FILE = path.join(MARATHON_DIR, "feature_list.json");
52
- const PROGRESS_FILE = path.join(MARATHON_DIR, "progress.json");
53
- const ARCHIVE_DIR = path.join(MARATHON_DIR, "archive");
51
+ const FEATURE_LIST_FILE = path.join(MARATHON_DIR, 'feature_list.json');
52
+ const PROGRESS_FILE = path.join(MARATHON_DIR, 'progress.json');
53
+ const ARCHIVE_DIR = path.join(MARATHON_DIR, 'archive');
54
54
 
55
55
  const VALID_COMMANDS = new Set([
56
- "init",
57
- "status",
58
- "next",
59
- "mark",
60
- "log",
61
- "session-start",
62
- "session-end",
63
- "reset",
64
- "add-feature",
65
- "distill",
56
+ 'init',
57
+ 'status',
58
+ 'next',
59
+ 'mark',
60
+ 'log',
61
+ 'session-start',
62
+ 'session-end',
63
+ 'reset',
64
+ 'add-feature',
65
+ 'distill',
66
66
  ]);
67
67
 
68
68
  // ── Schema Defaults ──────────────────────────────────────────────────────────
@@ -106,12 +106,10 @@ function createProgress(spec) {
106
106
  function readJSON(filePath) {
107
107
  if (!fs.existsSync(filePath)) return null;
108
108
  try {
109
- const content = fs.readFileSync(filePath, "utf8");
109
+ const content = fs.readFileSync(filePath, 'utf8');
110
110
  return JSON.parse(content);
111
111
  } catch (e) {
112
- console.error(
113
- `${RED}Error reading ${path.basename(filePath)}: ${e.message}${RESET}`,
114
- );
112
+ console.error(`${RED}Error reading ${path.basename(filePath)}: ${e.message}${RESET}`);
115
113
  return null;
116
114
  }
117
115
  }
@@ -123,7 +121,7 @@ function readJSON(filePath) {
123
121
  */
124
122
  function writeJSON(filePath, data) {
125
123
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
126
- fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf8");
124
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf8');
127
125
  }
128
126
 
129
127
  /**
@@ -151,10 +149,10 @@ function isActive() {
151
149
  function getGitLog(count = 20) {
152
150
  try {
153
151
  const output = execSync(`git log --oneline -${count}`, {
154
- encoding: "utf8",
155
- stdio: ["pipe", "pipe", "pipe"],
152
+ encoding: 'utf8',
153
+ stdio: ['pipe', 'pipe', 'pipe'],
156
154
  });
157
- return output.trim().split("\n").filter(Boolean);
155
+ return output.trim().split('\n').filter(Boolean);
158
156
  } catch {
159
157
  return [];
160
158
  }
@@ -166,12 +164,12 @@ function getGitLog(count = 20) {
166
164
  */
167
165
  function getGitBranch() {
168
166
  try {
169
- return execSync("git branch --show-current", {
170
- encoding: "utf8",
171
- stdio: ["pipe", "pipe", "pipe"],
167
+ return execSync('git branch --show-current', {
168
+ encoding: 'utf8',
169
+ stdio: ['pipe', 'pipe', 'pipe'],
172
170
  }).trim();
173
171
  } catch {
174
- return "unknown";
172
+ return 'unknown';
175
173
  }
176
174
  }
177
175
 
@@ -185,13 +183,13 @@ function getGitBranch() {
185
183
  function countFeatures(featureList) {
186
184
  const features = featureList.features || [];
187
185
  const total = features.length;
188
- const passing = features.filter((f) => f.passes === true).length;
186
+ const passing = features.filter(f => f.passes === true).length;
189
187
  let blocked = 0;
190
188
 
191
- features.forEach((f) => {
189
+ features.forEach(f => {
192
190
  if (!f.passes && f.dependencies && f.dependencies.length > 0) {
193
- const allPassed = f.dependencies.every((depId) => {
194
- const dep = features.find((d) => d.id === depId);
191
+ const allPassed = f.dependencies.every(depId => {
192
+ const dep = features.find(d => d.id === depId);
195
193
  return dep && dep.passes === true;
196
194
  });
197
195
  if (!allPassed) blocked++;
@@ -209,13 +207,13 @@ function countFeatures(featureList) {
209
207
  function getNextFeature(featureList) {
210
208
  const features = featureList.features || [];
211
209
  return (
212
- features.find((f) => {
210
+ features.find(f => {
213
211
  if (f.passes === true) return false;
214
212
 
215
213
  // Check dependencies (DAG)
216
214
  if (f.dependencies && f.dependencies.length > 0) {
217
- const allPassed = f.dependencies.every((depId) => {
218
- const dep = features.find((d) => d.id === depId);
215
+ const allPassed = f.dependencies.every(depId => {
216
+ const dep = features.find(d => d.id === depId);
219
217
  return dep && dep.passes === true;
220
218
  });
221
219
  if (!allPassed) return false; // Feature is blocked
@@ -234,7 +232,7 @@ function getNextFeature(featureList) {
234
232
  * @returns {string}
235
233
  */
236
234
  function progressBar(current, total, width = 30) {
237
- if (total === 0) return `${DIM}[${"".repeat(width)}]${RESET} 0%`;
235
+ if (total === 0) return `${DIM}[${''.repeat(width)}]${RESET} 0%`;
238
236
  const pct = Math.round((current / total) * 100);
239
237
  const filled = Math.round((current / total) * width);
240
238
  const empty = width - filled;
@@ -244,7 +242,7 @@ function progressBar(current, total, width = 30) {
244
242
  else if (pct >= 40) color = YELLOW;
245
243
  else if (pct >= 15) color = CYAN;
246
244
 
247
- return `${color}[${"".repeat(filled)}${"".repeat(empty)}]${RESET} ${BOLD}${pct}%${RESET}`;
245
+ return `${color}[${''.repeat(filled)}${''.repeat(empty)}]${RESET} ${BOLD}${pct}%${RESET}`;
248
246
  }
249
247
 
250
248
  // ── Commands ─────────────────────────────────────────────────────────────────
@@ -277,23 +275,19 @@ function cmdInit(spec) {
277
275
  writeJSON(FEATURE_LIST_FILE, featureList);
278
276
  writeJSON(PROGRESS_FILE, progress);
279
277
 
280
- console.log(banner("marathon_harness.js", { Mode: "INIT" }));
278
+ console.log(banner('marathon_harness.js', { Mode: 'INIT' }));
281
279
  console.log();
282
280
  ok(`Marathon initialized for: ${BOLD}${spec}${RESET}`);
283
281
  console.log();
284
- info("Next steps for the agent:");
285
- console.log(
286
- ` ${DIM}1.${RESET} Decompose the spec into 30-200 atomic features`,
287
- );
282
+ info('Next steps for the agent:');
283
+ console.log(` ${DIM}1.${RESET} Decompose the spec into 30-200 atomic features`);
288
284
  console.log(
289
285
  ` ${DIM}2.${RESET} Add each feature with: ${CYAN}add-feature "category" "description" "step1" "step2" ...${RESET}`,
290
286
  );
291
287
  console.log(
292
288
  ` ${DIM}3.${RESET} Make an initial git commit: ${CYAN}git commit -m "marathon: initial scaffold"${RESET}`,
293
289
  );
294
- console.log(
295
- ` ${DIM}4.${RESET} Start the first session: ${CYAN}session-start${RESET}`,
296
- );
290
+ console.log(` ${DIM}4.${RESET} Start the first session: ${CYAN}session-start${RESET}`);
297
291
  console.log();
298
292
  console.log(` ${DIM}State directory: ${MARATHON_DIR}${RESET}`);
299
293
  console.log();
@@ -308,9 +302,7 @@ function cmdInit(spec) {
308
302
  */
309
303
  function cmdAddFeature(category, description, steps, deps = []) {
310
304
  if (!isActive()) {
311
- console.error(
312
- `${RED}❌ No active marathon. Run ${CYAN}init${RED} first.${RESET}`,
313
- );
305
+ console.error(`${RED}❌ No active marathon. Run ${CYAN}init${RED} first.${RESET}`);
314
306
  process.exit(1);
315
307
  }
316
308
 
@@ -325,15 +317,13 @@ function cmdAddFeature(category, description, steps, deps = []) {
325
317
  if (!featureList) process.exit(1);
326
318
 
327
319
  const newId =
328
- featureList.features.length > 0
329
- ? Math.max(...featureList.features.map((f) => f.id)) + 1
330
- : 1;
320
+ featureList.features.length > 0 ? Math.max(...featureList.features.map(f => f.id)) + 1 : 1;
331
321
 
332
322
  const feature = {
333
323
  id: newId,
334
324
  category: category.toLowerCase(),
335
325
  description,
336
- steps: steps.length > 0 ? steps : ["Implement and verify"],
326
+ steps: steps.length > 0 ? steps : ['Implement and verify'],
337
327
  dependencies: deps,
338
328
  attempts: 0,
339
329
  failureReasons: [],
@@ -371,7 +361,7 @@ function cmdStatus() {
371
361
  const sessions = progress.sessions || [];
372
362
  const lastSession = sessions[sessions.length - 1] || null;
373
363
 
374
- console.log(banner("marathon_harness.js", { Mode: "STATUS" }));
364
+ console.log(banner('marathon_harness.js', { Mode: 'STATUS' }));
375
365
  console.log();
376
366
 
377
367
  // ── Spec ──
@@ -380,8 +370,7 @@ function cmdStatus() {
380
370
  console.log();
381
371
 
382
372
  // ── Progress Bar ──
383
- const blockedInfo =
384
- blocked > 0 ? ` (${YELLOW}${blocked} blocked${RESET})` : "";
373
+ const blockedInfo = blocked > 0 ? ` (${YELLOW}${blocked} blocked${RESET})` : '';
385
374
  console.log(
386
375
  ` ${BOLD}Progress:${RESET} ${progressBar(passing, total)} ${GREEN}${passing}${RESET}/${total} features${blockedInfo}`,
387
376
  );
@@ -390,7 +379,7 @@ function cmdStatus() {
390
379
  // ── Category Breakdown ──
391
380
  const categories = {};
392
381
  for (const f of featureList.features) {
393
- const cat = f.category || "uncategorized";
382
+ const cat = f.category || 'uncategorized';
394
383
  if (!categories[cat]) categories[cat] = { total: 0, passing: 0 };
395
384
  categories[cat].total++;
396
385
  if (f.passes) categories[cat].passing++;
@@ -399,10 +388,7 @@ function cmdStatus() {
399
388
  if (Object.keys(categories).length > 0) {
400
389
  console.log(` ${BOLD}By Category:${RESET}`);
401
390
  for (const [cat, counts] of Object.entries(categories)) {
402
- const catPct =
403
- counts.total > 0
404
- ? Math.round((counts.passing / counts.total) * 100)
405
- : 0;
391
+ const catPct = counts.total > 0 ? Math.round((counts.passing / counts.total) * 100) : 0;
406
392
  const catColor = catPct === 100 ? GREEN : catPct >= 50 ? YELLOW : RED;
407
393
  console.log(
408
394
  ` ${MAGENTA}${cat.padEnd(18)}${RESET} ${catColor}${counts.passing}/${counts.total}${RESET} (${catPct}%)`,
@@ -415,18 +401,14 @@ function cmdStatus() {
415
401
  console.log(` ${BOLD}Sessions:${RESET} ${sessions.length} completed`);
416
402
  if (lastSession) {
417
403
  console.log(
418
- ` ${DIM}Last session:${RESET} #${lastSession.session} — ${lastSession.endedAt?.slice(0, 16) || "in progress"}`,
404
+ ` ${DIM}Last session:${RESET} #${lastSession.session} — ${lastSession.endedAt?.slice(0, 16) || 'in progress'}`,
419
405
  );
420
406
  if (lastSession.notes) {
421
407
  console.log(` ${DIM}Notes:${RESET} ${lastSession.notes.slice(0, 80)}`);
422
408
  }
423
409
  if (lastSession.featuresAtEnd) {
424
- const delta =
425
- lastSession.featuresAtEnd.passing -
426
- (lastSession.featuresAtStart?.passing || 0);
427
- console.log(
428
- ` ${DIM}Features completed:${RESET} ${GREEN}+${delta}${RESET}`,
429
- );
410
+ const delta = lastSession.featuresAtEnd.passing - (lastSession.featuresAtStart?.passing || 0);
411
+ console.log(` ${DIM}Features completed:${RESET} ${GREEN}+${delta}${RESET}`);
430
412
  }
431
413
  }
432
414
  console.log();
@@ -444,9 +426,7 @@ function cmdStatus() {
444
426
  }
445
427
  }
446
428
  } else if (total > 0) {
447
- console.log(
448
- ` ${GREEN}${BOLD}🎉 All ${total} features are passing!${RESET}`,
449
- );
429
+ console.log(` ${GREEN}${BOLD}🎉 All ${total} features are passing!${RESET}`);
450
430
  }
451
431
  console.log();
452
432
 
@@ -512,18 +492,14 @@ function cmdNext() {
512
492
  }
513
493
 
514
494
  if (nextFeature.failureReasons && nextFeature.failureReasons.length > 0) {
515
- console.log(
516
- ` ${RED}${BOLD}Previous Failures (${nextFeature.attempts} attempts):${RESET}`,
517
- );
495
+ console.log(` ${RED}${BOLD}Previous Failures (${nextFeature.attempts} attempts):${RESET}`);
518
496
  for (const reason of nextFeature.failureReasons) {
519
497
  console.log(` ${DIM}* ${reason}${RESET}`);
520
498
  }
521
499
  console.log();
522
500
  }
523
501
 
524
- console.log(
525
- ` ${DIM}When done: marathon_harness.js mark ${nextFeature.id} pass${RESET}`,
526
- );
502
+ console.log(` ${DIM}When done: marathon_harness.js mark ${nextFeature.id} pass${RESET}`);
527
503
  console.log();
528
504
  }
529
505
 
@@ -539,18 +515,16 @@ function cmdMark(id, verdict, reason) {
539
515
  process.exit(1);
540
516
  }
541
517
 
542
- const validVerdicts = ["pass", "fail"];
518
+ const validVerdicts = ['pass', 'fail'];
543
519
  if (!validVerdicts.includes(verdict)) {
544
- console.error(
545
- `${RED}❌ Invalid verdict "${verdict}". Use: pass | fail${RESET}`,
546
- );
520
+ console.error(`${RED}❌ Invalid verdict "${verdict}". Use: pass | fail${RESET}`);
547
521
  process.exit(1);
548
522
  }
549
523
 
550
524
  const featureList = readJSON(FEATURE_LIST_FILE);
551
525
  if (!featureList) process.exit(1);
552
526
 
553
- const feature = featureList.features.find((f) => f.id === id);
527
+ const feature = featureList.features.find(f => f.id === id);
554
528
  if (!feature) {
555
529
  console.error(
556
530
  `${RED}❌ Feature #${id} not found. Valid IDs: 1-${featureList.features.length}${RESET}`,
@@ -558,7 +532,7 @@ function cmdMark(id, verdict, reason) {
558
532
  process.exit(1);
559
533
  }
560
534
 
561
- const newPasses = verdict === "pass";
535
+ const newPasses = verdict === 'pass';
562
536
  const oldPasses = feature.passes;
563
537
 
564
538
  // Guard: don't allow editing description or steps
@@ -582,15 +556,11 @@ function cmdMark(id, verdict, reason) {
582
556
  } else if (!newPasses && oldPasses) {
583
557
  warn(`Feature #${id} marked as ${RED}FAILING${RESET}`);
584
558
  } else {
585
- info(
586
- `Feature #${id} unchanged (already ${newPasses ? "passing" : "failing"})`,
587
- );
559
+ info(`Feature #${id} unchanged (already ${newPasses ? 'passing' : 'failing'})`);
588
560
  }
589
561
 
590
562
  console.log(` ${DIM}${feature.description}${RESET}`);
591
- console.log(
592
- ` ${progressBar(passing, total)} ${GREEN}${passing}${RESET}/${total}`,
593
- );
563
+ console.log(` ${progressBar(passing, total)} ${GREEN}${passing}${RESET}/${total}`);
594
564
  console.log();
595
565
  }
596
566
 
@@ -605,9 +575,7 @@ function cmdLog(message) {
605
575
  }
606
576
 
607
577
  if (!message) {
608
- console.error(
609
- `${RED}❌ Message required. Usage: log "Your progress note"${RESET}`,
610
- );
578
+ console.error(`${RED}❌ Message required. Usage: log "Your progress note"${RESET}`);
611
579
  process.exit(1);
612
580
  }
613
581
 
@@ -635,18 +603,16 @@ function cmdDistill(lesson) {
635
603
  }
636
604
 
637
605
  if (!lesson) {
638
- console.error(
639
- `${RED}❌ Lesson required. Usage: distill "Your architectural lesson"${RESET}`,
640
- );
606
+ console.error(`${RED}❌ Lesson required. Usage: distill "Your architectural lesson"${RESET}`);
641
607
  process.exit(1);
642
608
  }
643
609
 
644
610
  ensureDir();
645
- const DISTILL_FILE = path.join(MARATHON_DIR, "distilled_context.md");
611
+ const DISTILL_FILE = path.join(MARATHON_DIR, 'distilled_context.md');
646
612
  const timestamp = new Date().toISOString().slice(0, 16);
647
613
  const entry = `- [${timestamp}] ${lesson}\n`;
648
614
 
649
- fs.appendFileSync(DISTILL_FILE, entry, "utf8");
615
+ fs.appendFileSync(DISTILL_FILE, entry, 'utf8');
650
616
  ok(`Distilled memory saved: ${lesson}`);
651
617
  }
652
618
 
@@ -685,8 +651,8 @@ function cmdSessionStart() {
685
651
 
686
652
  // Display bearings
687
653
  console.log(
688
- banner("marathon_harness.js", {
689
- Mode: "SESSION START",
654
+ banner('marathon_harness.js', {
655
+ Mode: 'SESSION START',
690
656
  Session: `#${sessionNum}`,
691
657
  }),
692
658
  );
@@ -724,9 +690,7 @@ function cmdSessionStart() {
724
690
  if (recentLogs.length > 0) {
725
691
  console.log(` ${BOLD}Recent Log:${RESET}`);
726
692
  for (const entry of recentLogs) {
727
- console.log(
728
- ` ${DIM}${entry.timestamp.slice(0, 16)}${RESET} ${entry.message}`,
729
- );
693
+ console.log(` ${DIM}${entry.timestamp.slice(0, 16)}${RESET} ${entry.message}`);
730
694
  }
731
695
  console.log();
732
696
  }
@@ -744,9 +708,7 @@ function cmdSessionStart() {
744
708
  }
745
709
  } else {
746
710
  if (passing === total) {
747
- console.log(
748
- ` ${GREEN}${BOLD}🎉 All features passing! Nothing to implement.${RESET}`,
749
- );
711
+ console.log(` ${GREEN}${BOLD}🎉 All features passing! Nothing to implement.${RESET}`);
750
712
  } else {
751
713
  console.log(
752
714
  ` ${RED}${BOLD}⚠️ Deadlock: ${total - passing} features are blocked by failing dependencies.${RESET}`,
@@ -760,13 +722,9 @@ function cmdSessionStart() {
760
722
  console.log(
761
723
  ` ${DIM}1.${RESET} Start dev server (if applicable): ${CYAN}node .agent/scripts/auto_preview.js start${RESET}`,
762
724
  );
763
- console.log(
764
- ` ${DIM}2.${RESET} Smoke test the app to verify it's not broken`,
765
- );
725
+ console.log(` ${DIM}2.${RESET} Smoke test the app to verify it's not broken`);
766
726
  console.log(` ${DIM}3.${RESET} Implement the next feature shown above`);
767
- console.log(
768
- ` ${DIM}4.${RESET} Test, mark as passing, commit, then pick next feature`,
769
- );
727
+ console.log(` ${DIM}4.${RESET} Test, mark as passing, commit, then pick next feature`);
770
728
  console.log();
771
729
  }
772
730
 
@@ -786,9 +744,7 @@ function cmdSessionEnd(summary) {
786
744
 
787
745
  const sessions = progress.sessions || [];
788
746
  if (sessions.length === 0) {
789
- console.error(
790
- `${RED}❌ No active session. Run ${CYAN}session-start${RED} first.${RESET}`,
791
- );
747
+ console.error(`${RED}❌ No active session. Run ${CYAN}session-start${RED} first.${RESET}`);
792
748
  process.exit(1);
793
749
  }
794
750
 
@@ -802,27 +758,22 @@ function cmdSessionEnd(summary) {
802
758
  // Find which features were completed (have sessionCompleted in this session range)
803
759
  const sessionStartTime = currentSession.startedAt;
804
760
  const completedIds = featureList.features
805
- .filter(
806
- (f) =>
807
- f.passes &&
808
- f.sessionCompleted &&
809
- f.sessionCompleted >= sessionStartTime,
810
- )
811
- .map((f) => f.id);
761
+ .filter(f => f.passes && f.sessionCompleted && f.sessionCompleted >= sessionStartTime)
762
+ .map(f => f.id);
812
763
 
813
764
  // Get git commits since session start
814
765
  let sessionCommits = [];
815
766
  try {
816
767
  const since = currentSession.startedAt;
817
768
  const output = execSync(`git log --oneline --since="${since}"`, {
818
- encoding: "utf8",
819
- stdio: ["pipe", "pipe", "pipe"],
769
+ encoding: 'utf8',
770
+ stdio: ['pipe', 'pipe', 'pipe'],
820
771
  });
821
772
  sessionCommits = output
822
773
  .trim()
823
- .split("\n")
774
+ .split('\n')
824
775
  .filter(Boolean)
825
- .map((l) => l.split(" ")[0]);
776
+ .map(l => l.split(' ')[0]);
826
777
  } catch {
827
778
  // Git not available or no commits
828
779
  }
@@ -832,16 +783,15 @@ function cmdSessionEnd(summary) {
832
783
  currentSession.featuresAtEnd = { total, passing };
833
784
  currentSession.featuresCompleted = completedIds;
834
785
  currentSession.notes =
835
- summary ||
836
- `Session ${currentSession.session}: ${completedThisSession} features completed`;
786
+ summary || `Session ${currentSession.session}: ${completedThisSession} features completed`;
837
787
  currentSession.gitCommits = sessionCommits;
838
788
 
839
789
  writeJSON(PROGRESS_FILE, progress);
840
790
 
841
791
  // Display summary
842
792
  console.log(
843
- banner("marathon_harness.js", {
844
- Mode: "SESSION END",
793
+ banner('marathon_harness.js', {
794
+ Mode: 'SESSION END',
845
795
  Session: `#${currentSession.session}`,
846
796
  }),
847
797
  );
@@ -851,7 +801,7 @@ function cmdSessionEnd(summary) {
851
801
  console.log(` Started: ${currentSession.startedAt.slice(0, 16)}`);
852
802
  console.log(` Ended: ${currentSession.endedAt.slice(0, 16)}`);
853
803
  console.log(
854
- ` Features: ${GREEN}+${completedThisSession}${RESET} completed (${completedIds.map((id) => `#${id}`).join(", ") || "none"})`,
804
+ ` Features: ${GREEN}+${completedThisSession}${RESET} completed (${completedIds.map(id => `#${id}`).join(', ') || 'none'})`,
855
805
  );
856
806
  console.log(` Commits: ${sessionCommits.length}`);
857
807
  if (summary) {
@@ -867,17 +817,13 @@ function cmdSessionEnd(summary) {
867
817
  const remaining = total - passing;
868
818
  if (remaining > 0) {
869
819
  const avgPerSession =
870
- sessions.length > 0
871
- ? Math.max(1, Math.round(passing / sessions.length))
872
- : 1;
820
+ sessions.length > 0 ? Math.max(1, Math.round(passing / sessions.length)) : 1;
873
821
  const estRemaining = Math.ceil(remaining / avgPerSession);
874
822
  console.log(
875
823
  ` ${DIM}Estimated sessions remaining: ~${estRemaining} (avg ${avgPerSession} features/session)${RESET}`,
876
824
  );
877
825
  } else {
878
- console.log(
879
- ` ${GREEN}${BOLD}🎉 Marathon complete! All features passing.${RESET}`,
880
- );
826
+ console.log(` ${GREEN}${BOLD}🎉 Marathon complete! All features passing.${RESET}`);
881
827
  }
882
828
  console.log();
883
829
  }
@@ -892,23 +838,18 @@ function cmdReset() {
892
838
  }
893
839
 
894
840
  const featureList = readJSON(FEATURE_LIST_FILE);
895
- const { total, passing } = featureList
896
- ? countFeatures(featureList)
897
- : { total: 0, passing: 0 };
841
+ const { total, passing } = featureList ? countFeatures(featureList) : { total: 0, passing: 0 };
898
842
 
899
843
  // Archive current state
900
- const archiveTimestamp = new Date()
901
- .toISOString()
902
- .replace(/[:.]/g, "-")
903
- .slice(0, 19);
844
+ const archiveTimestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
904
845
  const archivePath = path.join(ARCHIVE_DIR, archiveTimestamp);
905
846
  fs.mkdirSync(archivePath, { recursive: true });
906
847
 
907
848
  if (fs.existsSync(FEATURE_LIST_FILE)) {
908
- fs.cpSync(FEATURE_LIST_FILE, path.join(archivePath, "feature_list.json"));
849
+ fs.cpSync(FEATURE_LIST_FILE, path.join(archivePath, 'feature_list.json'));
909
850
  }
910
851
  if (fs.existsSync(PROGRESS_FILE)) {
911
- fs.cpSync(PROGRESS_FILE, path.join(archivePath, "progress.json"));
852
+ fs.cpSync(PROGRESS_FILE, path.join(archivePath, 'progress.json'));
912
853
  }
913
854
 
914
855
  // Remove current state files
@@ -916,19 +857,15 @@ function cmdReset() {
916
857
  if (fs.existsSync(PROGRESS_FILE)) fs.unlinkSync(PROGRESS_FILE);
917
858
 
918
859
  ok(`Marathon archived to: ${archivePath}`);
919
- console.log(
920
- ` ${DIM}Progress at archive: ${passing}/${total} features passing${RESET}`,
921
- );
922
- console.log(
923
- ` ${DIM}Start a new marathon with: marathon_harness.js init "new spec"${RESET}`,
924
- );
860
+ console.log(` ${DIM}Progress at archive: ${passing}/${total} features passing${RESET}`);
861
+ console.log(` ${DIM}Start a new marathon with: marathon_harness.js init "new spec"${RESET}`);
925
862
  console.log();
926
863
  }
927
864
 
928
865
  // ── Help ─────────────────────────────────────────────────────────────────────
929
866
 
930
867
  function showHelp() {
931
- console.log(banner("marathon_harness.js", { Mode: "HELP" }));
868
+ console.log(banner('marathon_harness.js', { Mode: 'HELP' }));
932
869
  console.log();
933
870
  console.log(` ${BOLD}Long-Running Agent Harness${RESET}`);
934
871
  console.log(
@@ -936,26 +873,19 @@ function showHelp() {
936
873
  );
937
874
  console.log();
938
875
 
939
- const cmd = (name, desc) =>
940
- console.log(` ${CYAN}${name.padEnd(16)}${RESET} ${desc}`);
941
-
942
- cmd('init "spec"', "Start a new marathon with the given specification");
943
- cmd("status", "Show progress dashboard");
944
- cmd("next", "Show the next unfinished feature");
945
- cmd("mark <id> pass", "Mark a feature as passing");
946
- cmd("mark <id> fail", 'Mark a feature as failing (optional: "reason")');
947
- cmd('log "note"', "Add a timestamped progress note");
948
- cmd('distill "rule"', "Save an architectural rule or lesson to memory");
949
- cmd(
950
- "session-start",
951
- "Begin a new work session (reads state, shows bearings)",
952
- );
953
- cmd("session-end", "End session with optional summary");
954
- cmd(
955
- "add-feature",
956
- "Add a feature (supports --deps=1,2,3 for DAG dependencies)",
957
- );
958
- cmd("reset", "Archive current marathon and start fresh");
876
+ const cmd = (name, desc) => console.log(` ${CYAN}${name.padEnd(16)}${RESET} ${desc}`);
877
+
878
+ cmd('init "spec"', 'Start a new marathon with the given specification');
879
+ cmd('status', 'Show progress dashboard');
880
+ cmd('next', 'Show the next unfinished feature');
881
+ cmd('mark <id> pass', 'Mark a feature as passing');
882
+ cmd('mark <id> fail', 'Mark a feature as failing (optional: "reason")');
883
+ cmd('log "note"', 'Add a timestamped progress note');
884
+ cmd('distill "rule"', 'Save an architectural rule or lesson to memory');
885
+ cmd('session-start', 'Begin a new work session (reads state, shows bearings)');
886
+ cmd('session-end', 'End session with optional summary');
887
+ cmd('add-feature', 'Add a feature (supports --deps=1,2,3 for DAG dependencies)');
888
+ cmd('reset', 'Archive current marathon and start fresh');
959
889
  console.log();
960
890
  }
961
891
 
@@ -964,12 +894,7 @@ function showHelp() {
964
894
  function main() {
965
895
  const args = process.argv.slice(2);
966
896
 
967
- if (
968
- args.length === 0 ||
969
- args[0] === "help" ||
970
- args[0] === "--help" ||
971
- args[0] === "-h"
972
- ) {
897
+ if (args.length === 0 || args[0] === 'help' || args[0] === '--help' || args[0] === '-h') {
973
898
  showHelp();
974
899
  return;
975
900
  }
@@ -978,61 +903,59 @@ function main() {
978
903
 
979
904
  if (!VALID_COMMANDS.has(cmd)) {
980
905
  console.error(`${RED}Unknown command: "${cmd}"${RESET}`);
981
- console.error(`Valid commands: ${[...VALID_COMMANDS].sort().join(", ")}`);
906
+ console.error(`Valid commands: ${[...VALID_COMMANDS].sort().join(', ')}`);
982
907
  process.exit(1);
983
908
  }
984
909
 
985
910
  switch (cmd) {
986
- case "init": {
987
- const spec = args.slice(1).join(" ").trim();
911
+ case 'init': {
912
+ const spec = args.slice(1).join(' ').trim();
988
913
  cmdInit(spec);
989
914
  break;
990
915
  }
991
- case "status":
916
+ case 'status':
992
917
  cmdStatus();
993
918
  break;
994
- case "next":
919
+ case 'next':
995
920
  cmdNext();
996
921
  break;
997
- case "mark": {
922
+ case 'mark': {
998
923
  const id = parseInt(args[1], 10);
999
- const verdict = (args[2] || "").toLowerCase();
1000
- const reason = args.slice(3).join(" ").trim();
924
+ const verdict = (args[2] || '').toLowerCase();
925
+ const reason = args.slice(3).join(' ').trim();
1001
926
  if (isNaN(id)) {
1002
- console.error(
1003
- `${RED}❌ Feature ID required. Usage: mark <id> pass|fail "reason"${RESET}`,
1004
- );
927
+ console.error(`${RED}❌ Feature ID required. Usage: mark <id> pass|fail "reason"${RESET}`);
1005
928
  process.exit(1);
1006
929
  }
1007
930
  cmdMark(id, verdict, reason);
1008
931
  break;
1009
932
  }
1010
- case "log": {
1011
- const message = args.slice(1).join(" ").trim();
933
+ case 'log': {
934
+ const message = args.slice(1).join(' ').trim();
1012
935
  cmdLog(message);
1013
936
  break;
1014
937
  }
1015
- case "session-start":
938
+ case 'session-start':
1016
939
  cmdSessionStart();
1017
940
  break;
1018
- case "session-end": {
1019
- const summary = args.slice(1).join(" ").trim() || null;
941
+ case 'session-end': {
942
+ const summary = args.slice(1).join(' ').trim() || null;
1020
943
  cmdSessionEnd(summary);
1021
944
  break;
1022
945
  }
1023
- case "add-feature": {
1024
- const category = args[1] || "";
1025
- const description = args[2] || "";
946
+ case 'add-feature': {
947
+ const category = args[1] || '';
948
+ const description = args[2] || '';
1026
949
  let steps = args.slice(3);
1027
950
  let deps = [];
1028
951
 
1029
- steps = steps.filter((step) => {
1030
- if (step.startsWith("--deps=")) {
952
+ steps = steps.filter(step => {
953
+ if (step.startsWith('--deps=')) {
1031
954
  deps = step
1032
- .replace("--deps=", "")
1033
- .split(",")
955
+ .replace('--deps=', '')
956
+ .split(',')
1034
957
  .map(Number)
1035
- .filter((n) => !isNaN(n));
958
+ .filter(n => !isNaN(n));
1036
959
  return false;
1037
960
  }
1038
961
  return true;
@@ -1041,12 +964,12 @@ function main() {
1041
964
  cmdAddFeature(category, description, steps, deps);
1042
965
  break;
1043
966
  }
1044
- case "distill": {
1045
- const lesson = args.slice(1).join(" ").trim();
967
+ case 'distill': {
968
+ const lesson = args.slice(1).join(' ').trim();
1046
969
  cmdDistill(lesson);
1047
970
  break;
1048
971
  }
1049
- case "reset":
972
+ case 'reset':
1050
973
  cmdReset();
1051
974
  break;
1052
975
  default: