tribunal-kit 4.2.0 → 4.3.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 (182) hide show
  1. package/.agent/ARCHITECTURE.md +21 -14
  2. package/.agent/agents/swarm-worker-contracts.md +5 -5
  3. package/.agent/agents/ui-ux-auditor.md +292 -0
  4. package/.agent/rules/GEMINI.md +8 -8
  5. package/.agent/scripts/__pycache__/_colors.cpython-311.pyc +0 -0
  6. package/.agent/scripts/__pycache__/_utils.cpython-311.pyc +0 -0
  7. package/.agent/scripts/__pycache__/case_law_manager.cpython-311.pyc +0 -0
  8. package/.agent/scripts/_colors.js +18 -0
  9. package/.agent/scripts/_utils.js +42 -0
  10. package/.agent/scripts/auto_preview.js +197 -0
  11. package/.agent/scripts/bundle_analyzer.js +290 -0
  12. package/.agent/scripts/case_law_manager.js +684 -0
  13. package/.agent/scripts/checklist.js +266 -0
  14. package/.agent/scripts/colors.js +17 -0
  15. package/.agent/scripts/compress_skills.js +141 -0
  16. package/.agent/scripts/consolidate_skills.js +149 -0
  17. package/.agent/scripts/context_broker.js +609 -0
  18. package/.agent/scripts/deep_compress.js +150 -0
  19. package/.agent/scripts/dependency_analyzer.js +272 -0
  20. package/.agent/scripts/inner_loop_validator.js +465 -0
  21. package/.agent/scripts/lint_runner.js +187 -0
  22. package/.agent/scripts/minify_context.js +100 -0
  23. package/.agent/scripts/patch_skills_meta.js +156 -0
  24. package/.agent/scripts/patch_skills_output.js +244 -0
  25. package/.agent/scripts/schema_validator.js +297 -0
  26. package/.agent/scripts/security_scan.js +303 -0
  27. package/.agent/scripts/session_manager.js +276 -0
  28. package/.agent/scripts/skill_evolution.js +644 -0
  29. package/.agent/scripts/skill_integrator.js +313 -0
  30. package/.agent/scripts/strengthen_skills.js +193 -0
  31. package/.agent/scripts/strip_tribunal.js +47 -0
  32. package/.agent/scripts/swarm_dispatcher.js +360 -0
  33. package/.agent/scripts/test_runner.js +193 -0
  34. package/.agent/scripts/utils.js +32 -0
  35. package/.agent/scripts/verify_all.js +256 -0
  36. package/.agent/skills/agent-organizer/SKILL.md +3 -3
  37. package/.agent/skills/agentic-patterns/SKILL.md +3 -3
  38. package/.agent/skills/ai-prompt-injection-defense/SKILL.md +3 -3
  39. package/.agent/skills/api-patterns/SKILL.md +3 -3
  40. package/.agent/skills/api-security-auditor/SKILL.md +3 -3
  41. package/.agent/skills/app-builder/SKILL.md +3 -3
  42. package/.agent/skills/app-builder/templates/SKILL.md +1 -1
  43. package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +1 -1
  44. package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +1 -1
  45. package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +1 -1
  46. package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +1 -1
  47. package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +1 -1
  48. package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +1 -1
  49. package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +1 -1
  50. package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +1 -1
  51. package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +1 -1
  52. package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +1 -1
  53. package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +1 -1
  54. package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +1 -1
  55. package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +1 -1
  56. package/.agent/skills/appflow-wireframe/SKILL.md +3 -3
  57. package/.agent/skills/architecture/SKILL.md +3 -3
  58. package/.agent/skills/authentication-best-practices/SKILL.md +3 -3
  59. package/.agent/skills/bash-linux/SKILL.md +3 -3
  60. package/.agent/skills/behavioral-modes/SKILL.md +3 -3
  61. package/.agent/skills/brainstorming/SKILL.md +3 -3
  62. package/.agent/skills/building-native-ui/SKILL.md +3 -3
  63. package/.agent/skills/clean-code/SKILL.md +3 -3
  64. package/.agent/skills/code-review-checklist/SKILL.md +3 -3
  65. package/.agent/skills/config-validator/SKILL.md +3 -3
  66. package/.agent/skills/csharp-developer/SKILL.md +3 -3
  67. package/.agent/skills/data-validation-schemas/SKILL.md +3 -3
  68. package/.agent/skills/database-design/SKILL.md +3 -3
  69. package/.agent/skills/deployment-procedures/SKILL.md +3 -3
  70. package/.agent/skills/devops-engineer/SKILL.md +3 -3
  71. package/.agent/skills/devops-incident-responder/SKILL.md +3 -3
  72. package/.agent/skills/doc.md +1 -1
  73. package/.agent/skills/documentation-templates/SKILL.md +3 -3
  74. package/.agent/skills/edge-computing/SKILL.md +3 -3
  75. package/.agent/skills/error-resilience/SKILL.md +3 -3
  76. package/.agent/skills/extract-design-system/SKILL.md +3 -3
  77. package/.agent/skills/framer-motion-expert/SKILL.md +3 -4
  78. package/.agent/skills/frontend-design/SKILL.md +3 -3
  79. package/.agent/skills/game-design-expert/SKILL.md +3 -3
  80. package/.agent/skills/game-engineering-expert/SKILL.md +3 -3
  81. package/.agent/skills/geo-fundamentals/SKILL.md +3 -3
  82. package/.agent/skills/github-operations/SKILL.md +3 -3
  83. package/.agent/skills/gsap-core/SKILL.md +0 -2
  84. package/.agent/skills/gsap-frameworks/SKILL.md +0 -2
  85. package/.agent/skills/gsap-performance/SKILL.md +0 -2
  86. package/.agent/skills/gsap-plugins/SKILL.md +0 -2
  87. package/.agent/skills/gsap-react/SKILL.md +0 -2
  88. package/.agent/skills/gsap-scrolltrigger/SKILL.md +0 -2
  89. package/.agent/skills/gsap-timeline/SKILL.md +0 -2
  90. package/.agent/skills/gsap-utils/SKILL.md +0 -2
  91. package/.agent/skills/i18n-localization/SKILL.md +3 -3
  92. package/.agent/skills/intelligent-routing/SKILL.md +3 -3
  93. package/.agent/skills/lint-and-validate/SKILL.md +3 -3
  94. package/.agent/skills/llm-engineering/SKILL.md +3 -3
  95. package/.agent/skills/local-first/SKILL.md +3 -3
  96. package/.agent/skills/mcp-builder/SKILL.md +3 -3
  97. package/.agent/skills/mobile-design/SKILL.md +3 -3
  98. package/.agent/skills/monorepo-management/SKILL.md +3 -3
  99. package/.agent/skills/motion-engineering/SKILL.md +4 -4
  100. package/.agent/skills/nextjs-react-expert/SKILL.md +3 -3
  101. package/.agent/skills/nodejs-best-practices/SKILL.md +3 -3
  102. package/.agent/skills/observability/SKILL.md +3 -3
  103. package/.agent/skills/parallel-agents/SKILL.md +3 -3
  104. package/.agent/skills/performance-profiling/SKILL.md +3 -3
  105. package/.agent/skills/plan-writing/SKILL.md +3 -3
  106. package/.agent/skills/platform-engineer/SKILL.md +3 -3
  107. package/.agent/skills/playwright-best-practices/SKILL.md +3 -3
  108. package/.agent/skills/powershell-windows/SKILL.md +3 -3
  109. package/.agent/skills/project-idioms/SKILL.md +3 -3
  110. package/.agent/skills/python-patterns/SKILL.md +3 -3
  111. package/.agent/skills/python-pro/SKILL.md +3 -3
  112. package/.agent/skills/react-specialist/SKILL.md +3 -3
  113. package/.agent/skills/readme-builder/SKILL.md +3 -3
  114. package/.agent/skills/realtime-patterns/SKILL.md +3 -3
  115. package/.agent/skills/red-team-tactics/SKILL.md +3 -3
  116. package/.agent/skills/rust-pro/SKILL.md +3 -3
  117. package/.agent/skills/seo-fundamentals/SKILL.md +3 -3
  118. package/.agent/skills/server-management/SKILL.md +3 -3
  119. package/.agent/skills/shadcn-ui-expert/SKILL.md +3 -3
  120. package/.agent/skills/skill-creator/SKILL.md +3 -3
  121. package/.agent/skills/sql-pro/SKILL.md +3 -3
  122. package/.agent/skills/supabase-postgres-best-practices/SKILL.md +3 -3
  123. package/.agent/skills/swiftui-expert/SKILL.md +3 -3
  124. package/.agent/skills/systematic-debugging/SKILL.md +3 -3
  125. package/.agent/skills/tailwind-patterns/SKILL.md +3 -3
  126. package/.agent/skills/tdd-workflow/SKILL.md +3 -3
  127. package/.agent/skills/test-result-analyzer/SKILL.md +3 -3
  128. package/.agent/skills/testing-patterns/SKILL.md +3 -3
  129. package/.agent/skills/trend-researcher/SKILL.md +3 -3
  130. package/.agent/skills/typescript-advanced/SKILL.md +3 -3
  131. package/.agent/skills/ui-ux-pro-max/SKILL.md +3 -3
  132. package/.agent/skills/ui-ux-researcher/SKILL.md +3 -3
  133. package/.agent/skills/vue-expert/SKILL.md +3 -3
  134. package/.agent/skills/vulnerability-scanner/SKILL.md +3 -3
  135. package/.agent/skills/web-accessibility-auditor/SKILL.md +3 -3
  136. package/.agent/skills/web-design-guidelines/SKILL.md +3 -3
  137. package/.agent/skills/webapp-testing/SKILL.md +3 -3
  138. package/.agent/skills/whimsy-injector/SKILL.md +3 -3
  139. package/.agent/skills/workflow-optimizer/SKILL.md +3 -3
  140. package/.agent/workflows/audit.md +6 -6
  141. package/.agent/workflows/deploy.md +1 -1
  142. package/.agent/workflows/generate.md +23 -6
  143. package/.agent/workflows/session.md +5 -5
  144. package/.agent/workflows/swarm.md +2 -2
  145. package/README.md +64 -8
  146. package/bin/tribunal-kit.js +277 -45
  147. package/package.json +9 -6
  148. package/scripts/changelog.js +167 -0
  149. package/scripts/sync-version.js +81 -0
  150. package/.agent/scripts/__pycache__/auto_preview.cpython-311.pyc +0 -0
  151. package/.agent/scripts/__pycache__/bundle_analyzer.cpython-311.pyc +0 -0
  152. package/.agent/scripts/__pycache__/checklist.cpython-311.pyc +0 -0
  153. package/.agent/scripts/__pycache__/dependency_analyzer.cpython-311.pyc +0 -0
  154. package/.agent/scripts/__pycache__/security_scan.cpython-311.pyc +0 -0
  155. package/.agent/scripts/__pycache__/session_manager.cpython-311.pyc +0 -0
  156. package/.agent/scripts/__pycache__/skill_integrator.cpython-311.pyc +0 -0
  157. package/.agent/scripts/__pycache__/swarm_dispatcher.cpython-311.pyc +0 -0
  158. package/.agent/scripts/__pycache__/test_runner.cpython-311.pyc +0 -0
  159. package/.agent/scripts/__pycache__/verify_all.cpython-311.pyc +0 -0
  160. package/.agent/scripts/auto_preview.py +0 -180
  161. package/.agent/scripts/bundle_analyzer.py +0 -259
  162. package/.agent/scripts/case_law_manager.py +0 -755
  163. package/.agent/scripts/checklist.py +0 -209
  164. package/.agent/scripts/compress_skills.py +0 -167
  165. package/.agent/scripts/consolidate_skills.py +0 -173
  166. package/.agent/scripts/deep_compress.py +0 -202
  167. package/.agent/scripts/dependency_analyzer.py +0 -247
  168. package/.agent/scripts/lint_runner.py +0 -188
  169. package/.agent/scripts/minify_context.py +0 -80
  170. package/.agent/scripts/patch_skills_meta.py +0 -177
  171. package/.agent/scripts/patch_skills_output.py +0 -285
  172. package/.agent/scripts/schema_validator.py +0 -279
  173. package/.agent/scripts/security_scan.py +0 -224
  174. package/.agent/scripts/session_manager.py +0 -261
  175. package/.agent/scripts/skill_evolution.py +0 -563
  176. package/.agent/scripts/skill_integrator.py +0 -234
  177. package/.agent/scripts/strengthen_skills.py +0 -220
  178. package/.agent/scripts/strip_tribunal.py +0 -41
  179. package/.agent/scripts/swarm_dispatcher.py +0 -350
  180. package/.agent/scripts/test_runner.py +0 -192
  181. package/.agent/scripts/test_swarm_dispatcher.py +0 -163
  182. package/.agent/scripts/verify_all.py +0 -195
@@ -0,0 +1,465 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * inner_loop_validator.js — Tribunal Kit Inner-Loop Self-Healing CI
4
+ * ==================================================================
5
+ * Orchestrates security_scan.js and lint heuristics on code snippets
6
+ * IN MEMORY before they are presented to the Human Gate.
7
+ *
8
+ * This is the "Phase 6 Auto-Correction Engine": it feeds structured
9
+ * JSON findings back to the Maker Agent for autonomous self-healing
10
+ * without requiring user involvement.
11
+ *
12
+ * Architecture:
13
+ * 1. Receive a code snippet (via stdin or --snippet flag)
14
+ * 2. Write to a temp file within the OS temp directory
15
+ * 3. Run OWASP security pattern scan (using PATTERNS from security_scan.js)
16
+ * 4. Run lightweight syntax heuristics (no external tools required)
17
+ * 5. Emit structured JSON verdict for the Maker Agent to consume
18
+ * 6. Clean up temp file
19
+ *
20
+ * Usage:
21
+ * node .agent/scripts/inner_loop_validator.js --snippet "const x = eval(input)"
22
+ * node .agent/scripts/inner_loop_validator.js --file ./output.js
23
+ * node .agent/scripts/inner_loop_validator.js --file ./output.js --lang ts
24
+ * node .agent/scripts/inner_loop_validator.js test-case
25
+ *
26
+ * Output (JSON to stdout):
27
+ * {
28
+ * "verdict": "APPROVED" | "WARNING" | "REJECTED",
29
+ * "passed": boolean,
30
+ * "issues": [{ "severity": "critical|high|medium|low", "category": string, "line": number, "message": string, "fix": string }],
31
+ * "summary": string,
32
+ * "self_healing_instructions": string | null ← fed back to Maker Agent
33
+ * }
34
+ */
35
+
36
+ 'use strict';
37
+
38
+ const fs = require('fs');
39
+ const path = require('path');
40
+ const os = require('os');
41
+ const crypto = require('crypto');
42
+
43
+ // ── Resolve security_scan patterns (reuse — do not duplicate) ─────────────
44
+ const SCRIPT_DIR = __dirname;
45
+ let SECURITY_PATTERNS = [];
46
+ let SEVERITY_RANK = {};
47
+
48
+ try {
49
+ const secScan = require(path.join(SCRIPT_DIR, 'security_scan.js'));
50
+ SECURITY_PATTERNS = secScan.PATTERNS || [];
51
+ SEVERITY_RANK = secScan.SEVERITY_RANK || { critical: 0, high: 1, medium: 2, low: 3 };
52
+ } catch {
53
+ // Fallback: minimal critical patterns only (never fail silently on missing module)
54
+ SECURITY_PATTERNS = [
55
+ [/(?:password|passwd)\s*=\s*["'][^"']+["']/i, 'critical', 'Hardcoded Secret', 'Hardcoded password'],
56
+ [/\beval\s*\(/, 'high', 'Code Injection', 'eval() is a code injection vector'],
57
+ [/\.innerHTML\s*=/, 'high', 'XSS', 'Direct innerHTML assignment'],
58
+ [/algorithms\s*:\s*\[\s*["']none["']/, 'critical', 'Auth Bypass', "JWT 'none' algorithm"],
59
+ ];
60
+ SEVERITY_RANK = { critical: 0, high: 1, medium: 2, low: 3 };
61
+ }
62
+
63
+ // ── Syntax heuristics (no external deps) ─────────────────────────────────
64
+ // These catch structural issues in generated code before a linter runs.
65
+ const SYNTAX_HEURISTICS = [
66
+ {
67
+ pattern: /\bconst\s+\w+\s*=\s*require\s*\(\s*["'](?!\.\/|\.\.\/|[a-zA-Z])/,
68
+ severity: 'medium',
69
+ category: 'Hallucination Risk',
70
+ message: 'Suspicious require() path — verify module exists in package.json',
71
+ fix: 'Check that this package is listed in package.json dependencies',
72
+ },
73
+ {
74
+ pattern: /\/\/\s*VERIFY:/,
75
+ severity: 'low',
76
+ category: 'Verification Flag',
77
+ message: 'Maker Agent flagged this line as uncertain — human review required',
78
+ fix: 'The Maker Agent marked this with // VERIFY: — confirm before approving',
79
+ },
80
+ {
81
+ pattern: /:\s*any\b(?!\s*=)/,
82
+ severity: 'low',
83
+ category: 'Type Safety',
84
+ message: 'TypeScript `any` type used without explanation comment',
85
+ fix: 'Replace :any with a specific type, or add // any: [reason] comment',
86
+ },
87
+ {
88
+ pattern: /process\.env\.\w+(?!\s*\?\?|\s*\|\|)/,
89
+ severity: 'low',
90
+ category: 'Config Safety',
91
+ message: 'process.env access without nullish fallback — may throw at runtime',
92
+ fix: 'Use: process.env.VAR ?? "default" — always guard env var access',
93
+ },
94
+ {
95
+ pattern: /throw\s+["'`]/,
96
+ severity: 'low',
97
+ category: 'Error Quality',
98
+ message: 'Throwing a string instead of an Error object — stack traces will be lost',
99
+ fix: 'Use: throw new Error("message") instead of throw "message"',
100
+ },
101
+ {
102
+ pattern: /catch\s*\(\s*\w+\s*\)\s*\{?\s*\}/,
103
+ severity: 'medium',
104
+ category: 'Error Handling',
105
+ message: 'Empty catch block swallows errors silently',
106
+ fix: 'Add at minimum: catch (err) { console.error(err); throw err; }',
107
+ },
108
+ {
109
+ pattern: /\.then\(\s*\)\s*\.catch\s*\(|\.catch\s*\(\s*\)/,
110
+ severity: 'medium',
111
+ category: 'Error Handling',
112
+ message: 'Empty .then() or .catch() handler — Promise errors may be silenced',
113
+ fix: 'Implement proper resolution and rejection handlers',
114
+ },
115
+ {
116
+ pattern: /window\.|document\.|navigator\./,
117
+ severity: 'low',
118
+ category: 'Environment Check',
119
+ message: 'Browser global access — may fail in SSR/Node environments',
120
+ fix: 'Guard with: typeof window !== "undefined" before accessing browser globals',
121
+ },
122
+ ];
123
+
124
+ // ── ANSI colors (inline to avoid deps on colors.js path) ─────────────────
125
+ const GREEN = '\x1b[92m';
126
+ const YELLOW = '\x1b[93m';
127
+ const RED = '\x1b[91m';
128
+ const CYAN = '\x1b[96m';
129
+ const BOLD = '\x1b[1m';
130
+ const DIM = '\x1b[2m';
131
+ const RESET = '\x1b[0m';
132
+
133
+ // ── Core scanning ─────────────────────────────────────────────────────────
134
+
135
+ /**
136
+ * Scan a code string for security and heuristic issues.
137
+ * Returns an array of structured finding objects.
138
+ *
139
+ * @param {string} code - Raw source code string
140
+ * @param {string} [lang] - Language hint ('js' | 'ts' | 'py' | 'jsx' | 'tsx')
141
+ * @returns {Array<{severity, category, line, message, fix, source}>}
142
+ */
143
+ function scanCode(code, lang = 'js') {
144
+ const findings = [];
145
+ const lines = code.split('\n');
146
+
147
+ for (let i = 0; i < lines.length; i++) {
148
+ const stripped = lines[i].trim();
149
+ const lineNum = i + 1;
150
+
151
+ // Skip pure comments
152
+ if (stripped.startsWith('//') || stripped.startsWith('#') || stripped.startsWith('*')) {
153
+ continue;
154
+ }
155
+
156
+ // Run OWASP security patterns
157
+ for (const [pattern, severity, category, message] of SECURITY_PATTERNS) {
158
+ if (pattern.test(stripped)) {
159
+ findings.push({
160
+ severity,
161
+ category,
162
+ line: lineNum,
163
+ message,
164
+ fix: buildSecurityFix(category),
165
+ source: 'security_scan',
166
+ });
167
+ }
168
+ }
169
+
170
+ // Run structural heuristics
171
+ for (const h of SYNTAX_HEURISTICS) {
172
+ if (h.pattern.test(stripped)) {
173
+ findings.push({
174
+ severity: h.severity,
175
+ category: h.category,
176
+ line: lineNum,
177
+ message: h.message,
178
+ fix: h.fix,
179
+ source: 'heuristic',
180
+ });
181
+ }
182
+ }
183
+ }
184
+
185
+ return findings;
186
+ }
187
+
188
+ /**
189
+ * Build a fix suggestion for known security categories.
190
+ * @param {string} category
191
+ * @returns {string}
192
+ */
193
+ function buildSecurityFix(category) {
194
+ const fixes = {
195
+ 'Hardcoded Secret': 'Move to environment variable: process.env.SECRET_NAME',
196
+ 'SQL Injection': 'Use parameterized queries. Never interpolate user input into SQL.',
197
+ 'XSS': 'Use textContent instead of innerHTML. Sanitize with DOMPurify if HTML is needed.',
198
+ 'Code Injection': 'Remove eval()/new Function(). Use a safe alternative or a JSON parser.',
199
+ 'Command Injection': 'Use execFile() with an args array instead of exec() with a shell string.',
200
+ 'Weak Crypto': 'Use crypto.createHash("sha256") or bcrypt for password hashing.',
201
+ 'Weak Randomness': 'Use crypto.randomBytes(n) or crypto.randomUUID() for security-sensitive values.',
202
+ 'Auth Bypass': 'Enforce JWT algorithm explicitly: { algorithms: ["HS256"] }',
203
+ 'Info Disclosure': 'Remove logging of sensitive values. Use structured logging with redaction.',
204
+ };
205
+ return fixes[category] || 'Review and remediate according to OWASP guidelines.';
206
+ }
207
+
208
+ /**
209
+ * Determine the overall verdict from a list of findings.
210
+ * REJECTED if any critical/high. WARNING if medium. APPROVED if low/clean.
211
+ *
212
+ * @param {Array} findings
213
+ * @returns {{ verdict: string, passed: boolean }}
214
+ */
215
+ function computeVerdict(findings) {
216
+ // Filter out VERIFY flags from blocking logic — they are informational
217
+ const blocking = findings.filter(f => f.category !== 'Verification Flag');
218
+ const maxSeverityRank = blocking.reduce((min, f) => {
219
+ const rank = SEVERITY_RANK[f.severity] ?? 3;
220
+ return rank < min ? rank : min;
221
+ }, 4); // 4 = no findings
222
+
223
+ if (maxSeverityRank <= 1) return { verdict: 'REJECTED', passed: false }; // critical or high
224
+ if (maxSeverityRank === 2) return { verdict: 'WARNING', passed: true }; // medium
225
+ return { verdict: 'APPROVED', passed: true };
226
+ }
227
+
228
+ /**
229
+ * Build a self-healing instruction string for the Maker Agent.
230
+ * This is what you paste back into the AI to trigger auto-correction.
231
+ *
232
+ * @param {Array} findings
233
+ * @returns {string|null}
234
+ */
235
+ function buildSelfHealingInstructions(findings) {
236
+ const blocking = findings.filter(f => {
237
+ const rank = SEVERITY_RANK[f.severity] ?? 3;
238
+ return rank <= 1; // critical + high only
239
+ });
240
+
241
+ if (!blocking.length) return null;
242
+
243
+ const lines = [
244
+ '⚠️ Inner-Loop Validator found blocking issues. Auto-correct the following before writing to disk:\n',
245
+ ];
246
+
247
+ for (const f of blocking) {
248
+ lines.push(`[${f.severity.toUpperCase()}] Line ${f.line} — ${f.category}`);
249
+ lines.push(` Issue: ${f.message}`);
250
+ lines.push(` Fix: ${f.fix}`);
251
+ lines.push('');
252
+ }
253
+
254
+ lines.push('Re-generate the affected lines only. Do not change unaffected code.');
255
+ return lines.join('\n');
256
+ }
257
+
258
+ /**
259
+ * Write code to a temp file, returning its path.
260
+ * Caller must clean up.
261
+ * @param {string} code
262
+ * @param {string} lang
263
+ * @returns {string} temp file path
264
+ */
265
+ function writeTempFile(code, lang = 'js') {
266
+ const extMap = { ts: '.ts', tsx: '.tsx', jsx: '.jsx', py: '.py', js: '.js' };
267
+ const ext = extMap[lang] || '.js';
268
+ const tmpPath = path.join(os.tmpdir(), `tk_ilv_${crypto.randomBytes(6).toString('hex')}${ext}`);
269
+ fs.writeFileSync(tmpPath, code, 'utf8');
270
+ return tmpPath;
271
+ }
272
+
273
+ // ── Output ────────────────────────────────────────────────────────────────
274
+
275
+ function printHumanReport(result) {
276
+ const { verdict, issues, summary, self_healing_instructions } = result;
277
+
278
+ const verdictColor = verdict === 'APPROVED' ? GREEN : verdict === 'WARNING' ? YELLOW : RED;
279
+ const verdictIcon = verdict === 'APPROVED' ? '✅' : verdict === 'WARNING' ? '⚠️' : '❌';
280
+
281
+ console.error(`\n${BOLD}${CYAN}━━━ Inner-Loop Validator ━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`);
282
+ console.error(` Verdict: ${verdictColor}${BOLD}${verdictIcon} ${verdict}${RESET}`);
283
+ console.error(` Summary: ${summary}`);
284
+
285
+ if (issues.length) {
286
+ console.error(`\n ${BOLD}Issues found:${RESET}`);
287
+ for (const iss of issues) {
288
+ const color = iss.severity === 'critical' || iss.severity === 'high' ? RED :
289
+ iss.severity === 'medium' ? YELLOW : DIM;
290
+ console.error(` ${color}[${iss.severity.toUpperCase()}]${RESET} Line ${iss.line} — ${iss.category}`);
291
+ console.error(` ${iss.message}`);
292
+ console.error(` ${DIM}Fix: ${iss.fix}${RESET}`);
293
+ }
294
+ }
295
+
296
+ if (self_healing_instructions) {
297
+ console.error(`\n ${YELLOW}${BOLD}Self-Healing Instructions (for Maker Agent):${RESET}`);
298
+ console.error(self_healing_instructions.split('\n').map(l => ` ${l}`).join('\n'));
299
+ }
300
+
301
+ console.error(`${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}\n`);
302
+ }
303
+
304
+ // ── Built-in test case ────────────────────────────────────────────────────
305
+
306
+ function runTestCase() {
307
+ console.error(`\n${BOLD}${CYAN}━━━ Inner-Loop Validator — Self-Test ━━━━━━━━━━━━━━${RESET}`);
308
+
309
+ const badCode = `
310
+ const password = "supersecret123";
311
+ const result = db.query("SELECT * FROM users WHERE id = " + req.params.id);
312
+ document.getElementById('output').innerHTML = userInput;
313
+ const token = eval(req.body.expr);
314
+ const rand = Math.random() * 1000;
315
+ `;
316
+
317
+ const result = validate(badCode, 'js');
318
+ printHumanReport(result);
319
+
320
+ const hasCritical = result.issues.some(i => i.severity === 'critical' || i.severity === 'high');
321
+ if (hasCritical && result.verdict === 'REJECTED') {
322
+ console.error(`${GREEN}✅ Self-test PASSED — validator correctly identified and blocked critical issues${RESET}\n`);
323
+ process.exit(0);
324
+ } else {
325
+ console.error(`${RED}❌ Self-test FAILED — expected REJECTED verdict for bad code${RESET}\n`);
326
+ process.exit(1);
327
+ }
328
+ }
329
+
330
+ // ── Public API ────────────────────────────────────────────────────────────
331
+
332
+ /**
333
+ * Validate a code string. Returns a structured result object.
334
+ * This is the primary programmatic API — call this from other scripts.
335
+ *
336
+ * @param {string} code - Source code to validate
337
+ * @param {string} [lang] - Language hint
338
+ * @param {object} [opts] - Options: { timeout: number }
339
+ * @returns {{ verdict, passed, issues, summary, self_healing_instructions }}
340
+ */
341
+ function validate(code, lang = 'js', opts = {}) {
342
+ if (!code || typeof code !== 'string') {
343
+ return {
344
+ verdict: 'APPROVED',
345
+ passed: true,
346
+ issues: [],
347
+ summary: 'No code provided — skipped.',
348
+ self_healing_instructions: null,
349
+ };
350
+ }
351
+
352
+ const issues = scanCode(code, lang);
353
+
354
+ // Sort by severity rank
355
+ issues.sort((a, b) => (SEVERITY_RANK[a.severity] ?? 3) - (SEVERITY_RANK[b.severity] ?? 3));
356
+
357
+ const { verdict, passed } = computeVerdict(issues);
358
+ const healingInstructions = buildSelfHealingInstructions(issues);
359
+
360
+ const critCount = issues.filter(i => i.severity === 'critical').length;
361
+ const highCount = issues.filter(i => i.severity === 'high').length;
362
+ const medCount = issues.filter(i => i.severity === 'medium').length;
363
+ const lowCount = issues.filter(i => i.severity === 'low').length;
364
+ const verifyCount = issues.filter(i => i.category === 'Verification Flag').length;
365
+
366
+ let summary = `${issues.length} issue(s) found`;
367
+ if (!issues.length) {
368
+ summary = 'No issues detected — code is clean';
369
+ } else {
370
+ const parts = [];
371
+ if (critCount) parts.push(`${critCount} critical`);
372
+ if (highCount) parts.push(`${highCount} high`);
373
+ if (medCount) parts.push(`${medCount} medium`);
374
+ if (lowCount) parts.push(`${lowCount} low`);
375
+ if (verifyCount) parts.push(`${verifyCount} VERIFY flag(s) need human review`);
376
+ summary = parts.join(', ');
377
+ }
378
+
379
+ return {
380
+ verdict,
381
+ passed,
382
+ issues,
383
+ summary,
384
+ self_healing_instructions: healingInstructions,
385
+ meta: {
386
+ lines_scanned: code.split('\n').length,
387
+ lang,
388
+ timestamp: new Date().toISOString(),
389
+ },
390
+ };
391
+ }
392
+
393
+ module.exports = { validate, scanCode, computeVerdict, buildSelfHealingInstructions };
394
+
395
+ // ── CLI Entry ─────────────────────────────────────────────────────────────
396
+
397
+ if (require.main === module) {
398
+ const argv = process.argv.slice(2);
399
+
400
+ if (!argv.length || argv.includes('--help') || argv.includes('-h')) {
401
+ console.log(`
402
+ ${BOLD}inner_loop_validator.js${RESET} — Tribunal Self-Healing CI
403
+
404
+ ${BOLD}Usage:${RESET}
405
+ node .agent/scripts/inner_loop_validator.js --snippet "<code>"
406
+ node .agent/scripts/inner_loop_validator.js --file ./output.js [--lang ts]
407
+ node .agent/scripts/inner_loop_validator.js test-case
408
+
409
+ ${BOLD}Output:${RESET}
410
+ JSON to stdout. Human-readable summary to stderr.
411
+ Use --json-only to suppress the human report.
412
+
413
+ ${BOLD}Verdict:${RESET}
414
+ APPROVED → No critical/high issues. Safe to proceed.
415
+ WARNING → Medium issues found. Human should review.
416
+ REJECTED → Critical/high issues. Maker Agent must self-correct.
417
+ `);
418
+ process.exit(0);
419
+ }
420
+
421
+ // Built-in self-test
422
+ if (argv[0] === 'test-case') {
423
+ runTestCase();
424
+ process.exit(0);
425
+ }
426
+
427
+ const jsonOnly = argv.includes('--json-only');
428
+ const fileFlagIdx = argv.indexOf('--file');
429
+ const snippetIdx = argv.indexOf('--snippet');
430
+ const langIdx = argv.indexOf('--lang');
431
+
432
+ const lang = langIdx !== -1 && argv[langIdx + 1] ? argv[langIdx + 1] : 'js';
433
+
434
+ let code = '';
435
+
436
+ if (fileFlagIdx !== -1 && argv[fileFlagIdx + 1]) {
437
+ const filePath = path.resolve(argv[fileFlagIdx + 1]);
438
+ if (!fs.existsSync(filePath)) {
439
+ console.error(`${RED}✖ File not found: ${filePath}${RESET}`);
440
+ process.exit(1);
441
+ }
442
+ code = fs.readFileSync(filePath, 'utf8');
443
+ } else if (snippetIdx !== -1 && argv[snippetIdx + 1]) {
444
+ code = argv[snippetIdx + 1];
445
+ } else if (!process.stdin.isTTY) {
446
+ // Read from stdin if piped
447
+ code = fs.readFileSync('/dev/stdin', 'utf8');
448
+ } else {
449
+ console.error(`${RED}✖ Provide --snippet "<code>" or --file <path>${RESET}`);
450
+ process.exit(1);
451
+ }
452
+
453
+ const result = validate(code, lang);
454
+
455
+ // Always emit JSON to stdout (for machine consumption)
456
+ console.log(JSON.stringify(result, null, 2));
457
+
458
+ // Emit human report to stderr (safe to suppress with 2>/dev/null)
459
+ if (!jsonOnly) {
460
+ printHumanReport(result);
461
+ }
462
+
463
+ // Exit code: 0 = passed (APPROVED or WARNING), 1 = REJECTED
464
+ process.exit(result.passed ? 0 : 1);
465
+ }
@@ -0,0 +1,187 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * lint_runner.js — Standalone lint runner for the Tribunal Agent Kit.
4
+ *
5
+ * Usage:
6
+ * node .agent/scripts/lint_runner.js .
7
+ * node .agent/scripts/lint_runner.js . --fix
8
+ * node .agent/scripts/lint_runner.js . --files src/index.ts src/utils.ts
9
+ */
10
+
11
+ 'use strict';
12
+
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+ const { spawnSync } = require('child_process');
16
+
17
+ const { RED, GREEN, YELLOW, BLUE, BOLD, RESET } = require('./colors.js');
18
+
19
+ function header(title) {
20
+ console.log(`\n${BOLD}${BLUE}━━━ ${title} ━━━${RESET}`);
21
+ }
22
+
23
+ function ok(msg) {
24
+ console.log(` ${GREEN}✅ ${msg}${RESET}`);
25
+ }
26
+
27
+ function fail(msg) {
28
+ console.log(` ${RED}❌ ${msg}${RESET}`);
29
+ }
30
+
31
+ function skip(msg) {
32
+ console.log(` ${YELLOW}⏭️ ${msg}${RESET}`);
33
+ }
34
+
35
+ function runLinter(label, cmd, cwd) {
36
+ try {
37
+ const executable = process.platform === 'win32' && cmd[0] === 'npx' ? 'npx.cmd' : cmd[0];
38
+ const result = spawnSync(executable, cmd.slice(1), {
39
+ cwd,
40
+ encoding: 'utf8',
41
+ timeout: 120000,
42
+ shell: process.platform === 'win32'
43
+ });
44
+
45
+ if (result.status === 0) {
46
+ ok(`${label} — clean`);
47
+ return true;
48
+ }
49
+
50
+ fail(`${label} — issues found`);
51
+ if (result.error) {
52
+ console.log(` Error: ${result.error.message}`);
53
+ }
54
+ const out = result.stdout ? result.stdout.toString() : '';
55
+ const err = result.stderr ? result.stderr.toString() : '';
56
+ const output = (out + "\n" + err).trim();
57
+ if (output) {
58
+ const lines = output.split('\n');
59
+ for (const line of lines.slice(0, 15)) {
60
+ console.log(` ${line}`);
61
+ }
62
+ if (lines.length > 15) {
63
+ console.log(` ... and ${lines.length - 15} more lines`);
64
+ }
65
+ }
66
+ return false;
67
+ } catch {
68
+ skip(`${label} — tool not installed`);
69
+ return true;
70
+ }
71
+ }
72
+
73
+ function detectLinters(projectRoot) {
74
+ const available = {};
75
+ const pkgJson = path.join(projectRoot, "package.json");
76
+
77
+ if (fs.existsSync(pkgJson)) {
78
+ const eslintFiles = [".eslintrc", ".eslintrc.js", ".eslintrc.json", ".eslintrc.yml", "eslint.config.js", "eslint.config.mjs"];
79
+ available.eslint = eslintFiles.some(f => fs.existsSync(path.join(projectRoot, f)));
80
+
81
+ const prettierFiles = [".prettierrc", ".prettierrc.js", ".prettierrc.json", "prettier.config.js"];
82
+ available.prettier = prettierFiles.some(f => fs.existsSync(path.join(projectRoot, f)));
83
+ }
84
+
85
+ available.ruff = fs.existsSync(path.join(projectRoot, "pyproject.toml")) || fs.existsSync(path.join(projectRoot, "ruff.toml"));
86
+ available.flake8 = fs.existsSync(path.join(projectRoot, ".flake8")) || fs.existsSync(path.join(projectRoot, "setup.cfg"));
87
+
88
+ return available;
89
+ }
90
+
91
+ function main() {
92
+ const args = process.argv.slice(2);
93
+ let targetPath = null;
94
+ let fixFlag = false;
95
+ let fileArgs = [];
96
+
97
+ let i = 0;
98
+ while (i < args.length) {
99
+ if (args[i] === '--fix') fixFlag = true;
100
+ else if (args[i] === '--files') {
101
+ i++;
102
+ while (i < args.length && !args[i].startsWith('--')) {
103
+ fileArgs.push(args[i++]);
104
+ }
105
+ continue;
106
+ } else if (!targetPath && !args[i].startsWith('-')) {
107
+ targetPath = args[i];
108
+ }
109
+ i++;
110
+ }
111
+
112
+ if (!targetPath) {
113
+ console.log("Usage: node lint_runner.js <path> [--fix] [--files <file1> <file2> ...]");
114
+ process.exit(1);
115
+ }
116
+
117
+ const projectRoot = path.resolve(targetPath);
118
+ if (!fs.existsSync(projectRoot) || !fs.statSync(projectRoot).isDirectory()) {
119
+ fail(`Directory not found: ${projectRoot}`);
120
+ process.exit(1);
121
+ }
122
+
123
+ console.log(`${BOLD}Tribunal — lint_runner.js${RESET}`);
124
+ console.log(`Project: ${projectRoot}`);
125
+
126
+ const available = detectLinters(projectRoot);
127
+ if (!Object.values(available).some(Boolean)) {
128
+ skip("No linter configuration detected in this project");
129
+ process.exit(0);
130
+ }
131
+
132
+ let failures = 0;
133
+
134
+ if (available.eslint) {
135
+ header("ESLint");
136
+ const cmd = ["npx", "eslint"];
137
+ if (fixFlag) cmd.push("--fix");
138
+ if (fileArgs.length) cmd.push(...fileArgs);
139
+ else cmd.push(".", "--max-warnings=0");
140
+ if (!runLinter("ESLint", cmd, projectRoot)) failures++;
141
+ }
142
+
143
+ if (available.prettier) {
144
+ header("Prettier");
145
+ const cmd = ["npx", "prettier"];
146
+ if (fixFlag) cmd.push("--write");
147
+ else cmd.push("--check");
148
+ if (fileArgs.length) cmd.push(...fileArgs);
149
+ else cmd.push(".");
150
+ if (!runLinter("Prettier", cmd, projectRoot)) failures++;
151
+ }
152
+
153
+ if (available.ruff) {
154
+ header("Ruff (Python)");
155
+ const cmd = ["ruff", "check"];
156
+ if (fixFlag) cmd.push("--fix");
157
+ if (fileArgs.length) cmd.push(...fileArgs);
158
+ else cmd.push(".");
159
+ if (!runLinter("Ruff", cmd, projectRoot)) failures++;
160
+ }
161
+
162
+ if (available.flake8 && !available.ruff) {
163
+ header("Flake8 (Python)");
164
+ const cmd = ["flake8"];
165
+ if (fileArgs.length) cmd.push(...fileArgs);
166
+ else cmd.push(".");
167
+ if (!runLinter("Flake8", cmd, projectRoot)) failures++;
168
+ }
169
+
170
+ if (fs.existsSync(path.join(projectRoot, "tsconfig.json"))) {
171
+ header("TypeScript");
172
+ if (!runLinter("TypeScript", ["npx", "tsc", "--noEmit"], projectRoot)) failures++;
173
+ }
174
+
175
+ console.log(`\n${BOLD}━━━ Lint Summary ━━━${RESET}`);
176
+ if (failures === 0) {
177
+ ok("All linters passed");
178
+ } else {
179
+ fail(`${failures} linter(s) reported issues`);
180
+ }
181
+
182
+ process.exit(failures > 0 ? 1 : 0);
183
+ }
184
+
185
+ if (require.main === module) {
186
+ main();
187
+ }