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
@@ -17,11 +17,11 @@
17
17
  * node .agent/scripts/checklist.js . --skip security,seo
18
18
  */
19
19
 
20
- "use strict";
20
+ 'use strict';
21
21
 
22
- const fs = require("fs");
23
- const path = require("path");
24
- const { execFileSync } = require("child_process");
22
+ const fs = require('fs');
23
+ const path = require('path');
24
+ const { execFileSync } = require('child_process');
25
25
 
26
26
  const {
27
27
  RED,
@@ -37,30 +37,30 @@ const {
37
37
  timer,
38
38
  formatMs,
39
39
  fail,
40
- } = require("./_colors");
40
+ } = require('./_colors');
41
41
 
42
- const { walkDir } = require("./_utils");
42
+ const { walkDir } = require('./_utils');
43
43
 
44
44
  // ── Results Tracking ────────────────────────────────────────────────────────
45
45
 
46
46
  const RESULTS = [];
47
47
 
48
48
  function trackOk(label, ms) {
49
- const timing = ms != null ? `${DIM}(${formatMs(ms)})${RESET}` : "";
49
+ const timing = ms != null ? `${DIM}(${formatMs(ms)})${RESET}` : '';
50
50
  console.log(` ${GREEN}✅ ${label}${RESET} ${timing}`);
51
- RESULTS.push({ name: label, status: "pass", ms });
51
+ RESULTS.push({ name: label, status: 'pass', ms });
52
52
  }
53
53
 
54
54
  function trackFail(label, ms, note) {
55
- const timing = ms != null ? `${DIM}(${formatMs(ms)})${RESET}` : "";
55
+ const timing = ms != null ? `${DIM}(${formatMs(ms)})${RESET}` : '';
56
56
  console.log(` ${RED}❌ ${label}${RESET} ${timing}`);
57
57
  if (note) console.log(` ${note.slice(0, 500)}`);
58
- RESULTS.push({ name: label, status: "fail", ms });
58
+ RESULTS.push({ name: label, status: 'fail', ms });
59
59
  }
60
60
 
61
61
  function trackSkip(label, reason) {
62
62
  console.log(` ${YELLOW}⏭️ ${label} — ${reason}${RESET}`);
63
- RESULTS.push({ name: label, status: "skip" });
63
+ RESULTS.push({ name: label, status: 'skip' });
64
64
  }
65
65
 
66
66
  /**
@@ -75,25 +75,25 @@ function runCheck(label, cmd, cwd) {
75
75
  try {
76
76
  execFileSync(cmd[0], cmd.slice(1), {
77
77
  cwd,
78
- stdio: "pipe",
78
+ stdio: 'pipe',
79
79
  timeout: 60000,
80
- encoding: "utf8",
81
- shell: process.platform === "win32",
80
+ encoding: 'utf8',
81
+ shell: process.platform === 'win32',
82
82
  });
83
83
  trackOk(`${label} passed`, elapsed());
84
84
  return true;
85
85
  } catch (err) {
86
86
  const ms = elapsed();
87
- if (err.code === "ENOENT") {
88
- trackSkip(label, "command not found (tool not installed)");
87
+ if (err.code === 'ENOENT') {
88
+ trackSkip(label, 'command not found (tool not installed)');
89
89
  return true; // Don't block on tools that aren't installed
90
90
  }
91
91
  if (err.killed) {
92
- trackFail(label, ms, "timed out after 60s");
92
+ trackFail(label, ms, 'timed out after 60s');
93
93
  return false;
94
94
  }
95
- const output = ((err.stdout || "") + (err.stderr || "")).trim();
96
- trackFail(`${label} failed`, ms, output || "non-zero exit code");
95
+ const output = ((err.stdout || '') + (err.stderr || '')).trim();
96
+ trackFail(`${label} failed`, ms, output || 'non-zero exit code');
97
97
  return false;
98
98
  }
99
99
  }
@@ -107,38 +107,36 @@ function runCheck(label, cmd, cwd) {
107
107
  function checkSecrets(projectRoot) {
108
108
  const elapsed = timer();
109
109
  const dangerousPatterns = [
110
- "password=",
111
- "secret=",
112
- "api_key=",
113
- "apikey=",
114
- "auth_token=",
115
- "private_key=",
110
+ 'password=',
111
+ 'secret=',
112
+ 'api_key=',
113
+ 'apikey=',
114
+ 'auth_token=',
115
+ 'private_key=',
116
116
  ];
117
117
  let foundIssues = false;
118
- const sourceExtensions = new Set([".ts", ".tsx", ".js", ".jsx", ".py"]);
118
+ const sourceExtensions = new Set(['.ts', '.tsx', '.js', '.jsx', '.py']);
119
119
 
120
120
  const files = walkDir(projectRoot, { extensions: sourceExtensions });
121
121
 
122
122
  for (const fullPath of files) {
123
123
  // Skip .env files — they are allowed to contain secrets
124
- if (path.basename(fullPath).startsWith(".env")) continue;
124
+ if (path.basename(fullPath).startsWith('.env')) continue;
125
125
 
126
126
  let content;
127
127
  try {
128
- content = fs.readFileSync(fullPath, "utf8");
128
+ content = fs.readFileSync(fullPath, 'utf8');
129
129
  } catch {
130
130
  continue;
131
131
  }
132
132
 
133
- const lines = content.split("\n");
133
+ const lines = content.split('\n');
134
134
  for (let i = 0; i < lines.length; i++) {
135
135
  const lineLower = lines[i].toLowerCase().trim();
136
- const hasPattern = dangerousPatterns.some((p) => lineLower.includes(p));
137
- if (hasPattern && lineLower.includes("=") && !lineLower.startsWith("#")) {
136
+ const hasPattern = dangerousPatterns.some(p => lineLower.includes(p));
137
+ if (hasPattern && lineLower.includes('=') && !lineLower.startsWith('#')) {
138
138
  const rel = path.relative(projectRoot, fullPath);
139
- fail(
140
- `Possible secret: ${rel}:${i + 1} → ${lines[i].trim().slice(0, 80)}`,
141
- );
139
+ fail(`Possible secret: ${rel}:${i + 1} → ${lines[i].trim().slice(0, 80)}`);
142
140
  foundIssues = true;
143
141
  }
144
142
  }
@@ -148,7 +146,7 @@ function checkSecrets(projectRoot) {
148
146
  if (!foundIssues) {
149
147
  trackOk(`Secret scan — ${files.length} files clean`, ms);
150
148
  } else {
151
- trackFail("Secret scan — hardcoded credentials detected", ms);
149
+ trackFail('Secret scan — hardcoded credentials detected', ms);
152
150
  }
153
151
  return !foundIssues;
154
152
  }
@@ -166,76 +164,61 @@ function runAll(projectRoot, url, skipTiers) {
166
164
  const totalTimer = timer();
167
165
 
168
166
  // Priority 1 — Security
169
- if (!skipTiers.includes("security")) {
170
- console.log(sectionHeader("Security — Secret Scan", 1));
167
+ if (!skipTiers.includes('security')) {
168
+ console.log(sectionHeader('Security — Secret Scan', 1));
171
169
  if (!checkSecrets(projectRoot)) failures++;
172
170
  } else {
173
- trackSkip("Security", "skipped by flag");
171
+ trackSkip('Security', 'skipped by flag');
174
172
  }
175
173
 
176
174
  // Priority 2 — Lint
177
- if (!skipTiers.includes("lint")) {
178
- console.log(sectionHeader("Lint", 2));
179
- if (
180
- !runCheck(
181
- "ESLint",
182
- ["npx", "eslint", ".", "--max-warnings=0"],
183
- projectRoot,
184
- )
185
- )
186
- failures++;
187
- if (!runCheck("TypeScript", ["npx", "tsc", "--noEmit"], projectRoot))
188
- failures++;
175
+ if (!skipTiers.includes('lint')) {
176
+ console.log(sectionHeader('Lint', 2));
177
+ if (!runCheck('ESLint', ['npx', 'eslint', '.', '--max-warnings=0'], projectRoot)) failures++;
178
+ if (!runCheck('TypeScript', ['npx', 'tsc', '--noEmit'], projectRoot)) failures++;
189
179
  } else {
190
- trackSkip("Lint", "skipped by flag");
180
+ trackSkip('Lint', 'skipped by flag');
191
181
  }
192
182
 
193
183
  // Priority 3 — Schema
194
- if (!skipTiers.includes("schema")) {
195
- console.log(sectionHeader("Schema Validation", 3));
196
- trackSkip("Schema", "run manually if you have DB migrations");
184
+ if (!skipTiers.includes('schema')) {
185
+ console.log(sectionHeader('Schema Validation', 3));
186
+ trackSkip('Schema', 'run manually if you have DB migrations');
197
187
  } else {
198
- trackSkip("Schema", "skipped by flag");
188
+ trackSkip('Schema', 'skipped by flag');
199
189
  }
200
190
 
201
191
  // Priority 4 — Tests
202
- if (!skipTiers.includes("tests")) {
203
- console.log(sectionHeader("Tests", 4));
204
- if (
205
- !runCheck(
206
- "Test suite",
207
- ["npm", "test", "--", "--passWithNoTests"],
208
- projectRoot,
209
- )
210
- )
192
+ if (!skipTiers.includes('tests')) {
193
+ console.log(sectionHeader('Tests', 4));
194
+ if (!runCheck('Test suite', ['npm', 'test', '--', '--passWithNoTests'], projectRoot))
211
195
  failures++;
212
196
  } else {
213
- trackSkip("Tests", "skipped by flag");
197
+ trackSkip('Tests', 'skipped by flag');
214
198
  }
215
199
 
216
200
  // Priority 5 — UX
217
- if (!skipTiers.includes("ux")) {
218
- console.log(sectionHeader("UX / Accessibility", 5));
219
- trackSkip("UX audit", "run /preview start then check with Lighthouse");
201
+ if (!skipTiers.includes('ux')) {
202
+ console.log(sectionHeader('UX / Accessibility', 5));
203
+ trackSkip('UX audit', 'run /preview start then check with Lighthouse');
220
204
  } else {
221
- trackSkip("UX", "skipped by flag");
205
+ trackSkip('UX', 'skipped by flag');
222
206
  }
223
207
 
224
208
  // Priority 6 — SEO
225
- if (!skipTiers.includes("seo")) {
226
- console.log(sectionHeader("SEO", 6));
227
- trackSkip("SEO check", "use /ui-ux-pro-max for SEO-sensitive pages");
209
+ if (!skipTiers.includes('seo')) {
210
+ console.log(sectionHeader('SEO', 6));
211
+ trackSkip('SEO check', 'use /ui-ux-pro-max for SEO-sensitive pages');
228
212
  } else {
229
- trackSkip("SEO", "skipped by flag");
213
+ trackSkip('SEO', 'skipped by flag');
230
214
  }
231
215
 
232
216
  // Priority 7 — Lighthouse / E2E
233
- if (url && !skipTiers.includes("e2e")) {
234
- console.log(sectionHeader("Lighthouse / E2E", 7));
235
- if (!runCheck("Playwright E2E", ["npx", "playwright", "test"], projectRoot))
236
- failures++;
217
+ if (url && !skipTiers.includes('e2e')) {
218
+ console.log(sectionHeader('Lighthouse / E2E', 7));
219
+ if (!runCheck('Playwright E2E', ['npx', 'playwright', 'test'], projectRoot)) failures++;
237
220
  } else if (!url) {
238
- trackSkip("E2E / Lighthouse", "pass --url to enable");
221
+ trackSkip('E2E / Lighthouse', 'pass --url to enable');
239
222
  }
240
223
 
241
224
  // ━━━ Summary ━━━
@@ -243,22 +226,18 @@ function runAll(projectRoot, url, skipTiers) {
243
226
  console.log(`\n${BOLD}${CYAN}━━━ Checklist Summary ━━━${RESET}`);
244
227
  summaryTable(RESULTS);
245
228
 
246
- const passCount = RESULTS.filter((r) => r.status === "pass").length;
247
- const failCount = RESULTS.filter((r) => r.status === "fail").length;
248
- const skipCount = RESULTS.filter((r) => r.status === "skip").length;
229
+ const passCount = RESULTS.filter(r => r.status === 'pass').length;
230
+ const failCount = RESULTS.filter(r => r.status === 'fail').length;
231
+ const skipCount = RESULTS.filter(r => r.status === 'skip').length;
249
232
 
233
+ console.log(`\n ${DIM}Total: ${RESULTS.length} checks in ${formatMs(totalMs)}${RESET}`);
250
234
  console.log(
251
- `\n ${DIM}Total: ${RESULTS.length} checks in ${formatMs(totalMs)}${RESET}`,
252
- );
253
- console.log(
254
- ` ${GREEN}${passCount} passed${RESET} ${failCount > 0 ? `${RED}${failCount} failed${RESET} ` : ""}${skipCount > 0 ? `${YELLOW}${skipCount} skipped${RESET}` : ""}`,
235
+ ` ${GREEN}${passCount} passed${RESET} ${failCount > 0 ? `${RED}${failCount} failed${RESET} ` : ''}${skipCount > 0 ? `${YELLOW}${skipCount} skipped${RESET}` : ''}`,
255
236
  );
256
237
 
257
238
  console.log();
258
239
  if (failures === 0) {
259
- console.log(
260
- `${GREEN}${BOLD} ✔ All checks passed — ready to proceed.${RESET}`,
261
- );
240
+ console.log(`${GREEN}${BOLD} ✔ All checks passed — ready to proceed.${RESET}`);
262
241
  } else {
263
242
  console.log(
264
243
  `${RED}${BOLD} ✖ ${failures} tier(s) failed — fix critical issues before proceeding.${RESET}`,
@@ -277,14 +256,14 @@ function parseArgs(argv) {
277
256
  const raw = argv.slice(2);
278
257
 
279
258
  for (let i = 0; i < raw.length; i++) {
280
- if (raw[i] === "--url" && raw[i + 1]) {
259
+ if (raw[i] === '--url' && raw[i + 1]) {
281
260
  args.url = raw[++i];
282
- } else if (raw[i] === "--skip" && raw[i + 1]) {
261
+ } else if (raw[i] === '--skip' && raw[i + 1]) {
283
262
  args.skip = raw[++i]
284
- .split(",")
285
- .map((s) => s.trim().toLowerCase())
263
+ .split(',')
264
+ .map(s => s.trim().toLowerCase())
286
265
  .filter(Boolean);
287
- } else if (!raw[i].startsWith("--") && !args.path) {
266
+ } else if (!raw[i].startsWith('--') && !args.path) {
288
267
  args.path = raw[i];
289
268
  }
290
269
  }
@@ -307,7 +286,7 @@ function main() {
307
286
  process.exit(1);
308
287
  }
309
288
 
310
- console.log(banner("checklist.js", { Project: projectRoot }));
289
+ console.log(banner('checklist.js', { Project: projectRoot }));
311
290
 
312
291
  const failures = runAll(projectRoot, args.url, args.skip);
313
292
  process.exit(failures > 0 ? 1 : 0);
@@ -0,0 +1,305 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * cicd_validator.js — Deterministic CI/CD & GitHub Actions Validator for Tribunal Kit.
4
+ *
5
+ * Scans .github/workflows/*.yml, .gitlab-ci.yml, and Dockerfiles for:
6
+ * - Deprecated Action versions (v1-v3)
7
+ * - Security vulnerabilities (pull_request_target injection, script injection)
8
+ * - Concurrency race conditions
9
+ * - Excessive permissions (permissions: write-all)
10
+ * - Non-deterministic lockfile usage (npm install vs npm ci)
11
+ * - Static credential patterns
12
+ *
13
+ * Usage:
14
+ * node .agent/scripts/cicd_validator.js [target-directory]
15
+ * node .agent/scripts/cicd_validator.js . --json
16
+ */
17
+
18
+ 'use strict';
19
+
20
+ const fs = require('fs');
21
+ const path = require('path');
22
+
23
+ const {
24
+ RED,
25
+ GREEN,
26
+ YELLOW,
27
+ BOLD,
28
+ RESET,
29
+ banner,
30
+ sectionHeader,
31
+ summaryTable,
32
+ timer,
33
+ formatMs,
34
+ ok,
35
+ fail,
36
+ warn,
37
+ skip,
38
+ } = require('./_colors');
39
+
40
+ const DEPRECATED_ACTIONS = [
41
+ { pattern: /actions\/checkout@(v[123]|master)/i, name: 'actions/checkout', recommended: '@v4' },
42
+ {
43
+ pattern: /actions\/setup-node@(v[123]|master)/i,
44
+ name: 'actions/setup-node',
45
+ recommended: '@v4',
46
+ },
47
+ {
48
+ pattern: /actions\/setup-python@(v[1234]|master)/i,
49
+ name: 'actions/setup-python',
50
+ recommended: '@v5',
51
+ },
52
+ {
53
+ pattern: /actions\/upload-artifact@(v[123]|master)/i,
54
+ name: 'actions/upload-artifact',
55
+ recommended: '@v4',
56
+ },
57
+ {
58
+ pattern: /actions\/download-artifact@(v[123]|master)/i,
59
+ name: 'actions/download-artifact',
60
+ recommended: '@v4',
61
+ },
62
+ {
63
+ pattern: /docker\/build-push-action@(v[1234]|master)/i,
64
+ name: 'docker/build-push-action',
65
+ recommended: '@v5',
66
+ },
67
+ {
68
+ pattern: /aws-actions\/configure-aws-credentials@(v[123]|master)/i,
69
+ name: 'aws-actions/configure-aws-credentials',
70
+ recommended: '@v4',
71
+ },
72
+ ];
73
+
74
+ function findWorkflowFiles(dir) {
75
+ const files = [];
76
+ const githubWorkflows = path.join(dir, '.github', 'workflows');
77
+ if (fs.existsSync(githubWorkflows)) {
78
+ try {
79
+ const entries = fs.readdirSync(githubWorkflows);
80
+ for (const entry of entries) {
81
+ if (entry.endsWith('.yml') || entry.endsWith('.yaml')) {
82
+ files.push(path.join(githubWorkflows, entry));
83
+ }
84
+ }
85
+ } catch (_e) {}
86
+ }
87
+
88
+ const gitlabCi = path.join(dir, '.gitlab-ci.yml');
89
+ if (fs.existsSync(gitlabCi)) {
90
+ files.push(gitlabCi);
91
+ }
92
+
93
+ const dockerfile = path.join(dir, 'Dockerfile');
94
+ if (fs.existsSync(dockerfile)) {
95
+ files.push(dockerfile);
96
+ }
97
+
98
+ return files;
99
+ }
100
+
101
+ function auditWorkflowContent(filePath, content) {
102
+ const issues = [];
103
+ const lines = content.split('\n');
104
+ const isGithubWorkflow = filePath.includes('.github');
105
+
106
+ if (isGithubWorkflow) {
107
+ // Check for concurrency block
108
+ if (!content.includes('concurrency:')) {
109
+ const hasDeploy = /deploy|publish|release/i.test(content);
110
+ if (hasDeploy) {
111
+ issues.push({
112
+ level: 'WARN',
113
+ code: 'CI-03',
114
+ message:
115
+ "Deploy workflow is missing a top-level 'concurrency:' block to prevent race conditions.",
116
+ });
117
+ }
118
+ }
119
+
120
+ // Check for dangerous pull_request_target with head checkout
121
+ if (
122
+ content.includes('pull_request_target') &&
123
+ /github\.event\.pull_request\.head\.sha|github\.head_ref/i.test(content)
124
+ ) {
125
+ issues.push({
126
+ level: 'FAIL',
127
+ code: 'CI-02',
128
+ message:
129
+ "CRITICAL: 'pull_request_target' trigger with PR head checkout is vulnerable to pwn-request code execution.",
130
+ });
131
+ }
132
+
133
+ // Check for excessive permissions
134
+ if (/permissions:\s*write-all/i.test(content)) {
135
+ issues.push({
136
+ level: 'FAIL',
137
+ code: 'CI-04',
138
+ message: "'permissions: write-all' violates the principle of least privilege.",
139
+ });
140
+ }
141
+
142
+ // Check line by line
143
+ for (let i = 0; i < lines.length; i++) {
144
+ const line = lines[i];
145
+ const lineNum = i + 1;
146
+
147
+ // Deprecated actions
148
+ for (const dep of DEPRECATED_ACTIONS) {
149
+ if (dep.pattern.test(line)) {
150
+ issues.push({
151
+ level: 'FAIL',
152
+ code: 'CI-01',
153
+ line: lineNum,
154
+ message: `Deprecated action version '${dep.name}'. Upgrade to ${dep.recommended}.`,
155
+ });
156
+ }
157
+ }
158
+
159
+ // Script injection in run:
160
+ if (
161
+ /run:\s*.*?\$\{\{\s*github\.event\.(issue\.title|issue\.body|comment\.body|pull_request\.title)\s*\}\}/i.test(
162
+ line,
163
+ )
164
+ ) {
165
+ issues.push({
166
+ level: 'FAIL',
167
+ code: 'CI-10',
168
+ line: lineNum,
169
+ message:
170
+ "Script injection risk: Untrusted GitHub event context embedded directly in 'run:' command. Use environment variables.",
171
+ });
172
+ }
173
+
174
+ // Non-deterministic npm install in CI
175
+ if (/\brun:\s*.*?\bnpm\s+install\b(?!.*--dry-run)/i.test(line) && !/npm\s+ci/i.test(line)) {
176
+ issues.push({
177
+ level: 'WARN',
178
+ code: 'CI-06',
179
+ line: lineNum,
180
+ message:
181
+ "Use 'npm ci' instead of 'npm install' in CI pipelines for deterministic builds.",
182
+ });
183
+ }
184
+
185
+ // Static secret hardcoding
186
+ if (/AKIA[0-9A-Z]{16}/.test(line)) {
187
+ issues.push({
188
+ level: 'FAIL',
189
+ code: 'CI-05',
190
+ line: lineNum,
191
+ message: 'Hardcoded AWS Access Key ID detected.',
192
+ });
193
+ }
194
+ }
195
+ }
196
+
197
+ return issues;
198
+ }
199
+
200
+ function main() {
201
+ const args = process.argv.slice(2);
202
+ const targetDir = args.find(a => !a.startsWith('-')) || process.cwd();
203
+ const jsonOutput = args.includes('--json');
204
+
205
+ const elapsed = timer();
206
+ const files = findWorkflowFiles(targetDir);
207
+
208
+ if (!jsonOutput) {
209
+ banner('TRIBUNAL CI/CD PIPELINE VALIDATOR');
210
+ sectionHeader(`Scanning workflows in: ${path.resolve(targetDir)}`);
211
+ }
212
+
213
+ if (files.length === 0) {
214
+ if (jsonOutput) {
215
+ console.log(JSON.stringify({ files: 0, passes: 0, failures: 0, warnings: 0, issues: [] }));
216
+ } else {
217
+ skip('No CI/CD workflow files (.github/workflows, .gitlab-ci.yml, Dockerfile) found.');
218
+ }
219
+ process.exit(0);
220
+ }
221
+
222
+ let totalFailures = 0;
223
+ let totalWarnings = 0;
224
+ const allIssues = [];
225
+ const resultsTable = [];
226
+
227
+ for (const file of files) {
228
+ const relPath = path.relative(targetDir, file);
229
+ try {
230
+ const content = fs.readFileSync(file, 'utf8');
231
+ const fileIssues = auditWorkflowContent(file, content);
232
+
233
+ const failures = fileIssues.filter(i => i.level === 'FAIL');
234
+ const warnings = fileIssues.filter(i => i.level === 'WARN');
235
+
236
+ totalFailures += failures.length;
237
+ totalWarnings += warnings.length;
238
+
239
+ if (!jsonOutput) {
240
+ if (failures.length === 0 && warnings.length === 0) {
241
+ ok(`${relPath} — clean`);
242
+ resultsTable.push({ name: relPath, status: 'pass' });
243
+ } else if (failures.length > 0) {
244
+ fail(`${relPath} — ${failures.length} errors, ${warnings.length} warnings`);
245
+ for (const issue of fileIssues) {
246
+ const loc = issue.line ? `Line ${issue.line}: ` : '';
247
+ const color = issue.level === 'FAIL' ? RED : YELLOW;
248
+ console.log(` ${color}[${issue.code}]${RESET} ${loc}${issue.message}`);
249
+ }
250
+ resultsTable.push({ name: relPath, status: 'fail' });
251
+ } else {
252
+ warn(`${relPath} — ${warnings.length} warnings`);
253
+ for (const issue of fileIssues) {
254
+ const loc = issue.line ? `Line ${issue.line}: ` : '';
255
+ console.log(` ${YELLOW}[${issue.code}]${RESET} ${loc}${issue.message}`);
256
+ }
257
+ resultsTable.push({ name: relPath, status: 'warn' });
258
+ }
259
+ }
260
+
261
+ for (const issue of fileIssues) {
262
+ allIssues.push({ file: relPath, ...issue });
263
+ }
264
+ } catch (err) {
265
+ totalFailures++;
266
+ if (!jsonOutput) {
267
+ fail(`${relPath} — failed to read/parse: ${err.message}`);
268
+ resultsTable.push({ name: relPath, status: 'fail' });
269
+ }
270
+ }
271
+ }
272
+
273
+ const ms = elapsed();
274
+
275
+ if (jsonOutput) {
276
+ console.log(
277
+ JSON.stringify(
278
+ {
279
+ files: files.length,
280
+ failures: totalFailures,
281
+ warnings: totalWarnings,
282
+ durationMs: ms,
283
+ issues: allIssues,
284
+ },
285
+ null,
286
+ 2,
287
+ ),
288
+ );
289
+ } else {
290
+ sectionHeader('Summary');
291
+ summaryTable(resultsTable);
292
+ console.log(
293
+ `\n${BOLD}Result:${RESET} ${totalFailures > 0 ? RED + 'FAILED' : GREEN + 'PASSED'}${RESET} ` +
294
+ `(${files.length} files scanned, ${totalFailures} errors, ${totalWarnings} warnings in ${formatMs(ms)})\n`,
295
+ );
296
+ }
297
+
298
+ process.exit(totalFailures > 0 ? 1 : 0);
299
+ }
300
+
301
+ if (require.main === module) {
302
+ main();
303
+ }
304
+
305
+ module.exports = { auditWorkflowContent, findWorkflowFiles };