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
@@ -1,202 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- deep_compress.py - Deep surgical compression for .agent/ markdown files (skills, agents, workflows).
4
- Targets: repetitive boilerplate, verbose code comments, redundant example sections.
5
- Safe: never removes H1/H2 headers, never removes HALLUCINATION TRAP lines, never removes code blocks entirely.
6
- """
7
-
8
- import os, re, sys
9
-
10
- BASE_DIRS = ['.agent/skills', '.agent/agents', '.agent/workflows']
11
-
12
- # ─── SECTION REMOVAL ─────────────────────────────────────────────────────────
13
- # These are globally covered by GEMINI.md — strip from all files
14
-
15
- REMOVE_SECTIONS = [
16
- r'(?ms)^## 🏛️ Tribunal Integration.*?(?=^## |\Z)',
17
- r'(?ms)^## Tribunal Integration.*?(?=^## |\Z)',
18
- r'(?ms)^### ✅ Pre-Flight Self-Audit.*?(?=^### |^## |\Z)',
19
- r'(?ms)^## Pre-Flight Self-Audit.*?(?=^## |\Z)',
20
- r'(?ms)^## Cross-Workflow Navigation.*?(?=^## |\Z)',
21
- r'(?ms)^## LLM Traps.*?(?=^## |\Z)',
22
- r'(?ms)^## VBC Protocol.*?(?=^## |\Z)',
23
- r'(?ms)^## Output Format\n```[\s\S]*?```\n',
24
- r'(?ms)^## 🤖 LLM-Specific Traps.*?(?=^## |\Z)', # covered in GEMINI.md
25
- ]
26
-
27
- # ─── VERBOSE COMMENT REMOVAL IN CODE BLOCKS ──────────────────────────────────
28
- # Removes lines that just restate the concept being shown — pure padding
29
-
30
- VERBOSE_COMMENT_PATTERNS = [
31
- # e.g. "// Any HTML or SVG element can be prefixed with `motion.`"
32
- r"(?m)^(\s*)//\s*(?:Any HTML or SVG element|motion\.div, motion\.span|The MAGIC of|This is the key performance|The pattern that|Compound components share|Note that children|The action receives|Children inherit the|Import first|Parent controls when|It's always motion)\b[^\n]*\n",
33
- # e.g. "# TypedDict gives you autocomplete..."
34
- r"(?m)^(\s*)#\s*(?:TypedDict gives you|Usage:|Note:|Return user|Return None|Automatically)\b[^\n]*\n",
35
- # Obvious standalone "// Usage:" label inside code blocks
36
- r"(?m)^\s*//\s*Usage:\s*\n(?=\s*[<{])",
37
- r"(?m)^\s*#\s*Usage:\s*\n(?=\s*[{])",
38
- # "// When server responds..." style obvious follow-on comment
39
- r"(?m)^\s*//\s*When (?:server responds|a component|React can interrupt|the React Compiler)[^\n]*\n",
40
- ]
41
-
42
- # ─── CHATTY SENTENCE REMOVAL BEFORE FIRST CODE BLOCK ─────────────────────────
43
- # Removes motivational opener sentences: "X is a paradigm shift. If you..."
44
-
45
- def strip_chatty_openers(content):
46
- """Remove 2-3 sentence filler paragraphs between H1 and first ---"""
47
- return re.sub(
48
- r'(^# .+\n)\n.{60,}\n.{30,}\n(?:\n---)',
49
- r'\1\n---',
50
- content,
51
- flags=re.MULTILINE
52
- )
53
-
54
- # ─── COLLAPSE NESTED IDENTICAL EXAMPLES ──────────────────────────────────────
55
- # Many files have "// Legacy" vs "// Modern" comments — compress to 1 block
56
-
57
- def compress_legacy_modern_blocks(content):
58
- """Convert // LEGACY + // MODERN dual examples into compact diff-style"""
59
- # Pattern: code block with ❌ LEGACY followed immediately by ✅ MODERN
60
- pattern = re.compile(
61
- r'```(\w+)\n((?:.*\n)*?.*// ❌ LEGACY[^\n]*\n(?:.*\n)*?)```\n\n```\w+\n((?:.*\n)*?.*// ✅ MODERN[^\n]*\n(?:.*\n)*?)```',
62
- re.MULTILINE
63
- )
64
- # Only compress if the whole block is < 30 lines total
65
- def compress(m):
66
- total_lines = m.group(2).count('\n') + m.group(3).count('\n')
67
- if total_lines > 28:
68
- return m.group(0) # too big to safely merge — leave alone
69
- lang = m.group(1)
70
- legacy = m.group(2).strip()
71
- modern = m.group(3).strip()
72
- return f'```{lang}\n// ❌ LEGACY\n{legacy}\n\n// ✅ MODERN\n{modern}\n```'
73
- return pattern.sub(compress, content)
74
-
75
- # ─── STRIP EMPTY COMMENT LINES ───────────────────────────────────────────────
76
- def strip_empty_comments(content):
77
- """Remove lines that are ONLY // or #"""
78
- content = re.sub(r'(?m)^\s*//\s*$\n', '', content)
79
- content = re.sub(r'(?m)^\s*#\s*$\n', '', content)
80
- return content
81
-
82
- # ─── TRIM REPEATED RULES (dedup consecutive bullet points in same section) ───
83
- def dedup_bullet_points(content):
84
- """Remove exact duplicate bullet point lines (often copy-pasted across sections)."""
85
- lines = content.split('\n')
86
- seen_bullets = {}
87
- output = []
88
- for i, line in enumerate(lines):
89
- stripped = line.strip()
90
- if stripped.startswith(('✅', '❌', '- ✅', '- ❌')):
91
- if stripped in seen_bullets and i - seen_bullets[stripped] < 80:
92
- continue # skip duplicate within 80 lines
93
- seen_bullets[stripped] = i
94
- output.append(line)
95
- return '\n'.join(output)
96
-
97
- # ─── COLLAPSE BLANKS ─────────────────────────────────────────────────────────
98
- def collapse_blanks(content):
99
- return re.sub(r'\n{3,}', '\n\n', content)
100
-
101
- # ─── REMOVE REDUNDANT IMPORTS IN COMMENTS ────────────────────────────────────
102
- # Some files have // import { x } from "y" repeated in every example
103
-
104
- def compress_import_repetition(content):
105
- """If same import line appears in 3+ separate code blocks, add a note and strip repetitions."""
106
- import_line = re.compile(r'import \{[^}]+\} from "[^"]+";')
107
- matches = import_line.findall(content)
108
- freq = {}
109
- for m in matches:
110
- freq[m] = freq.get(m, 0) + 1
111
-
112
- for imp, count in freq.items():
113
- if count >= 4:
114
- # First occurrence: keep it. Subsequent ones: replace with comment
115
- first_done = [False]
116
- def replacer(m, imp=imp, first_done=first_done):
117
- if m.group(0) == imp:
118
- if not first_done[0]:
119
- first_done[0] = True
120
- return m.group(0)
121
- return f'// {imp} ← (already imported above)'
122
- return m.group(0)
123
- content = import_line.sub(replacer, content)
124
- return content
125
-
126
- # ─── MAIN PIPELINE ──────────────────────────────────────────────────────────
127
-
128
- def compress_file(path):
129
- with open(path, 'r', encoding='utf-8', errors='ignore') as f:
130
- original = f.read()
131
-
132
- content = original
133
-
134
- # 1. Strip global boilerplate sections
135
- for pattern in REMOVE_SECTIONS:
136
- content = re.sub(pattern, '', content)
137
-
138
- # 2. Strip chatty openers
139
- content = strip_chatty_openers(content)
140
-
141
- # 3. Compress legacy/modern dual blocks
142
- content = compress_legacy_modern_blocks(content)
143
-
144
- # 4. Verbose inline code comment removal
145
- for pattern in VERBOSE_COMMENT_PATTERNS:
146
- content = re.sub(pattern, '', content)
147
-
148
- # 5. Strip empty comment lines
149
- content = strip_empty_comments(content)
150
-
151
- # 6. Deduplicate bullet points
152
- content = dedup_bullet_points(content)
153
-
154
- # 7. Collapse 3+ blank lines
155
- content = collapse_blanks(content)
156
-
157
- # Write back only if changed
158
- if content.strip() != original.strip():
159
- with open(path, 'w', encoding='utf-8') as f:
160
- f.write(content.strip() + '\n')
161
-
162
- return len(original), len(content)
163
-
164
- def main():
165
- total_orig = 0
166
- total_new = 0
167
- file_results = []
168
-
169
- for base in BASE_DIRS:
170
- if not os.path.exists(base):
171
- continue
172
- for root, _, files in os.walk(base):
173
- for fname in files:
174
- if fname.endswith('.md'):
175
- path = os.path.join(root, fname)
176
- orig, new = compress_file(path)
177
- total_orig += orig
178
- total_new += new
179
- saved = orig - new
180
- if saved > 200:
181
- rel = os.path.relpath(path, '.')
182
- file_results.append((saved, rel))
183
-
184
- file_results.sort(reverse=True)
185
-
186
- saved_total = total_orig - total_new
187
- pct = saved_total / total_orig * 100 if total_orig else 0
188
-
189
- print(f"\n{'='*58}")
190
- print(f" Deep Compression Complete")
191
- print(f"{'='*58}")
192
- print(f" Original : {total_orig:,} bytes ({total_orig//1024}KB)")
193
- print(f" After : {total_new:,} bytes ({total_new//1024}KB)")
194
- print(f" Saved : {saved_total:,} bytes ({saved_total//1024}KB) — {pct:.1f}%")
195
- print(f"\n Top savings:")
196
- for saved, path in file_results[:20]:
197
- skill = '/'.join(path.replace('\\','/').split('/')[-2:])
198
- print(f" -{saved//1024:2}KB {skill}")
199
- print()
200
-
201
- if __name__ == '__main__':
202
- main()
@@ -1,247 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- dependency_analyzer.py — Dependency health checker for the Tribunal Agent Kit.
4
-
5
- Analyzes project dependencies for:
6
- - Unused packages (in package.json but never imported)
7
- - Phantom imports (imported but not in package.json)
8
- - npm audit / pip-audit results
9
- - Duplicate/overlapping packages
10
-
11
- Usage:
12
- python .agent/scripts/dependency_analyzer.py .
13
- python .agent/scripts/dependency_analyzer.py . --audit
14
- python .agent/scripts/dependency_analyzer.py . --check-unused
15
- """
16
-
17
- import os
18
- import sys
19
- import re
20
- import json
21
- import subprocess
22
- import argparse
23
- from pathlib import Path
24
-
25
- RED = "\033[91m"
26
- GREEN = "\033[92m"
27
- YELLOW = "\033[93m"
28
- BLUE = "\033[94m"
29
- BOLD = "\033[1m"
30
- RESET = "\033[0m"
31
-
32
- SOURCE_EXTENSIONS = {".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"}
33
- SKIP_DIRS = {"node_modules", ".git", "dist", "build", ".next", ".agent", "__pycache__"}
34
-
35
- # Built-in Node.js modules that don't require packages
36
- NODE_BUILTINS = {
37
- "fs", "path", "os", "crypto", "http", "https", "url", "util",
38
- "stream", "events", "child_process", "cluster", "net", "dns",
39
- "tls", "readline", "zlib", "buffer", "querystring", "string_decoder",
40
- "assert", "perf_hooks", "worker_threads", "timers", "v8",
41
- "node:fs", "node:path", "node:os", "node:crypto", "node:http",
42
- "node:https", "node:url", "node:util", "node:stream", "node:events",
43
- "node:child_process", "node:net", "node:dns", "node:tls",
44
- "node:readline", "node:zlib", "node:buffer", "node:assert",
45
- "node:perf_hooks", "node:worker_threads", "node:timers",
46
- }
47
-
48
-
49
- def header(title: str) -> None:
50
- print(f"\n{BOLD}{BLUE}━━━ {title} ━━━{RESET}")
51
-
52
-
53
- def ok(msg: str) -> None:
54
- print(f" {GREEN}✅ {msg}{RESET}")
55
-
56
-
57
- def fail(msg: str) -> None:
58
- print(f" {RED}❌ {msg}{RESET}")
59
-
60
-
61
- def warn(msg: str) -> None:
62
- print(f" {YELLOW}⚠️ {msg}{RESET}")
63
-
64
-
65
- def skip(msg: str) -> None:
66
- print(f" {YELLOW}⏭️ {msg}{RESET}")
67
-
68
-
69
- def load_package_json(project_root: str) -> dict | None:
70
- """Load and return package.json contents."""
71
- pkg_path = Path(project_root) / "package.json"
72
- if not pkg_path.exists():
73
- return None
74
- try:
75
- with open(pkg_path) as f:
76
- return json.load(f)
77
- except (json.JSONDecodeError, IOError):
78
- return None
79
-
80
-
81
- def extract_imports(project_root: str) -> set[str]:
82
- """Extract all external package imports from source files."""
83
- imports: set[str] = set()
84
- import_patterns = [
85
- re.compile(r'(?:import|export)\s+.*?\s+from\s+["\']([^"\'\.][^"\']*)["\']'),
86
- re.compile(r'require\s*\(\s*["\']([^"\'\.][^"\']*)["\']'),
87
- re.compile(r'import\s*\(\s*["\']([^"\'\.][^"\']*)["\']'),
88
- ]
89
-
90
- for root, dirs, files in os.walk(project_root):
91
- dirs[:] = [d for d in dirs if d not in SKIP_DIRS]
92
- for filename in files:
93
- ext = Path(filename).suffix
94
- if ext not in SOURCE_EXTENSIONS:
95
- continue
96
- filepath = os.path.join(root, filename)
97
- try:
98
- with open(filepath, "r", encoding="utf-8", errors="ignore") as f:
99
- content = f.read()
100
- for pattern in import_patterns:
101
- for match in pattern.finditer(content):
102
- pkg = match.group(1)
103
- # Normalize scoped packages: @scope/pkg/subpath → @scope/pkg
104
- if pkg.startswith("@"):
105
- parts = pkg.split("/")
106
- pkg = "/".join(parts[:2]) if len(parts) >= 2 else pkg
107
- else:
108
- pkg = pkg.split("/")[0]
109
- imports.add(pkg)
110
- except (IOError, PermissionError):
111
- pass
112
-
113
- return imports
114
-
115
-
116
- def check_unused(pkg: dict, used_imports: set[str]) -> list[str]:
117
- """Find packages listed in package.json but never imported."""
118
- all_deps = set(pkg.get("dependencies", {}).keys()) | set(pkg.get("devDependencies", {}).keys())
119
- # Some packages are used implicitly (build tools, configs, types)
120
- implicit_packages = {
121
- "typescript", "eslint", "prettier", "vitest", "jest", "ts-node",
122
- "@types/node", "@types/react", "tailwindcss", "postcss", "autoprefixer",
123
- "nodemon", "tsx", "vite", "next", "webpack", "babel", "@babel/core",
124
- }
125
- checkable = all_deps - implicit_packages
126
- # Also skip @types/ packages — they're type-only
127
- checkable = {d for d in checkable if not d.startswith("@types/")}
128
-
129
- unused = checkable - used_imports
130
- return sorted(unused)
131
-
132
-
133
- def check_phantom(pkg: dict, used_imports: set[str]) -> list[str]:
134
- """Find packages imported but not listed in package.json."""
135
- all_deps = set(pkg.get("dependencies", {}).keys()) | set(pkg.get("devDependencies", {}).keys())
136
- external_imports = used_imports - NODE_BUILTINS
137
- phantom = external_imports - all_deps
138
- return sorted(phantom)
139
-
140
-
141
- def run_npm_audit(project_root: str) -> bool:
142
- """Run npm audit and report results."""
143
- try:
144
- result = subprocess.run(
145
- ["npm", "audit", "--json"],
146
- cwd=project_root,
147
- capture_output=True,
148
- text=True,
149
- timeout=60,
150
- )
151
- try:
152
- audit_data = json.loads(result.stdout)
153
- vulns = audit_data.get("metadata", {}).get("vulnerabilities", {})
154
- critical = vulns.get("critical", 0)
155
- high = vulns.get("high", 0)
156
- moderate = vulns.get("moderate", 0)
157
- low = vulns.get("low", 0)
158
-
159
- if critical + high > 0:
160
- fail(f"npm audit: {critical} critical, {high} high, {moderate} moderate, {low} low")
161
- return False
162
- elif moderate + low > 0:
163
- warn(f"npm audit: {moderate} moderate, {low} low vulnerabilities")
164
- return True
165
- else:
166
- ok("npm audit — no known vulnerabilities")
167
- return True
168
- except (json.JSONDecodeError, KeyError):
169
- if result.returncode == 0:
170
- ok("npm audit — clean")
171
- return True
172
- fail("npm audit returned errors")
173
- return False
174
- except FileNotFoundError:
175
- skip("npm not installed — skipping audit")
176
- return True
177
- except subprocess.TimeoutExpired:
178
- fail("npm audit timed out after 60s")
179
- return False
180
-
181
-
182
- def main() -> None:
183
- parser = argparse.ArgumentParser(
184
- description="Tribunal dependency analyzer — checks for unused, phantom, and vulnerable packages"
185
- )
186
- parser.add_argument("path", help="Project root directory")
187
- parser.add_argument("--audit", action="store_true", help="Also run npm audit / pip-audit")
188
- parser.add_argument("--check-unused", action="store_true", help="Only check for unused dependencies")
189
- args = parser.parse_args()
190
-
191
- project_root = os.path.abspath(args.path)
192
- if not os.path.isdir(project_root):
193
- fail(f"Directory not found: {project_root}")
194
- sys.exit(1)
195
-
196
- print(f"{BOLD}Tribunal — dependency_analyzer.py{RESET}")
197
- print(f"Project: {project_root}")
198
-
199
- pkg = load_package_json(project_root)
200
- if not pkg:
201
- skip("No package.json found — dependency analysis requires a Node.js project")
202
- sys.exit(0)
203
-
204
- issues = 0
205
-
206
- # Extract imports from source code
207
- used_imports = extract_imports(project_root)
208
- print(f"\n Found {len(used_imports)} unique external imports in source code")
209
-
210
- # Phantom imports (imported but not in package.json)
211
- if not args.check_unused:
212
- header("Phantom Imports (not in package.json)")
213
- phantom = check_phantom(pkg, used_imports)
214
- if phantom:
215
- for p in phantom:
216
- fail(f"'{p}' is imported but not in package.json — possible hallucination")
217
- issues += len(phantom)
218
- else:
219
- ok("All imports found in package.json")
220
-
221
- # Unused dependencies
222
- header("Unused Dependencies")
223
- unused = check_unused(pkg, used_imports)
224
- if unused:
225
- for u in unused:
226
- warn(f"'{u}' is in package.json but never imported — may be unused")
227
- else:
228
- ok("No obviously unused dependencies found")
229
-
230
- # npm audit
231
- if args.audit:
232
- header("Vulnerability Audit")
233
- if not run_npm_audit(project_root):
234
- issues += 1
235
-
236
- # Summary
237
- print(f"\n{BOLD}━━━ Dependency Analysis Summary ━━━{RESET}")
238
- if issues == 0:
239
- ok("All dependency checks passed")
240
- else:
241
- fail(f"{issues} issue(s) found — review above")
242
-
243
- sys.exit(1 if issues > 0 else 0)
244
-
245
-
246
- if __name__ == "__main__":
247
- main()
@@ -1,188 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- lint_runner.py — Standalone lint runner for the Tribunal Agent Kit.
4
-
5
- Detects and runs available linters in the project:
6
- - ESLint (JS/TS)
7
- - Prettier (formatting)
8
- - Ruff / Flake8 (Python)
9
-
10
- Usage:
11
- python .agent/scripts/lint_runner.py .
12
- python .agent/scripts/lint_runner.py . --fix
13
- python .agent/scripts/lint_runner.py . --files src/index.ts src/utils.ts
14
- """
15
-
16
- import os
17
- import sys
18
- import subprocess
19
- import argparse
20
- from pathlib import Path
21
-
22
- RED = "\033[91m"
23
- GREEN = "\033[92m"
24
- YELLOW = "\033[93m"
25
- BLUE = "\033[94m"
26
- BOLD = "\033[1m"
27
- RESET = "\033[0m"
28
-
29
-
30
- def header(title: str) -> None:
31
- print(f"\n{BOLD}{BLUE}━━━ {title} ━━━{RESET}")
32
-
33
-
34
- def ok(msg: str) -> None:
35
- print(f" {GREEN}✅ {msg}{RESET}")
36
-
37
-
38
- def fail(msg: str) -> None:
39
- print(f" {RED}❌ {msg}{RESET}")
40
-
41
-
42
- def skip(msg: str) -> None:
43
- print(f" {YELLOW}⏭️ {msg}{RESET}")
44
-
45
-
46
- def run_linter(label: str, cmd: list[str], cwd: str) -> bool:
47
- """Run a linter command and return True if it passes."""
48
- try:
49
- result = subprocess.run(
50
- cmd, cwd=cwd, capture_output=True, text=True, timeout=120
51
- )
52
- if result.returncode == 0:
53
- ok(f"{label} — clean")
54
- return True
55
- fail(f"{label} — issues found")
56
- output = (result.stdout + result.stderr).strip()
57
- if output:
58
- for line in output.split("\n")[:15]:
59
- print(f" {line}")
60
- total_lines = len(output.split("\n"))
61
- if total_lines > 15:
62
- print(f" ... and {total_lines - 15} more lines")
63
- return False
64
- except FileNotFoundError:
65
- skip(f"{label} — tool not installed")
66
- return True
67
- except subprocess.TimeoutExpired:
68
- fail(f"{label} — timed out after 120s")
69
- return False
70
-
71
-
72
- def detect_linters(project_root: str) -> dict[str, bool]:
73
- """Detect which linters are available in the project."""
74
- root = Path(project_root)
75
- available: dict[str, bool] = {}
76
-
77
- # JS/TS linters
78
- pkg_json = root / "package.json"
79
- if pkg_json.exists():
80
- available["eslint"] = any(
81
- (root / f).exists()
82
- for f in [".eslintrc", ".eslintrc.js", ".eslintrc.json", ".eslintrc.yml", "eslint.config.js", "eslint.config.mjs"]
83
- ) or pkg_json.exists() # ESLint can be configured in package.json too
84
- available["prettier"] = any(
85
- (root / f).exists()
86
- for f in [".prettierrc", ".prettierrc.js", ".prettierrc.json", "prettier.config.js"]
87
- )
88
-
89
- # Python linters
90
- available["ruff"] = (root / "pyproject.toml").exists() or (root / "ruff.toml").exists()
91
- available["flake8"] = (root / ".flake8").exists() or (root / "setup.cfg").exists()
92
-
93
- return available
94
-
95
-
96
- def main() -> None:
97
- parser = argparse.ArgumentParser(
98
- description="Tribunal lint runner — detects and runs available linters"
99
- )
100
- parser.add_argument("path", help="Project root directory")
101
- parser.add_argument("--fix", action="store_true", help="Auto-fix issues where supported")
102
- parser.add_argument("--files", nargs="*", help="Specific files to lint (instead of entire project)")
103
- args = parser.parse_args()
104
-
105
- project_root = os.path.abspath(args.path)
106
- if not os.path.isdir(project_root):
107
- fail(f"Directory not found: {project_root}")
108
- sys.exit(1)
109
-
110
- print(f"{BOLD}Tribunal — lint_runner.py{RESET}")
111
- print(f"Project: {project_root}")
112
-
113
- available = detect_linters(project_root)
114
- if not any(available.values()):
115
- skip("No linter configuration detected in this project")
116
- sys.exit(0)
117
-
118
- failures = 0
119
- file_args = args.files or []
120
-
121
- # ESLint
122
- if available.get("eslint"):
123
- header("ESLint")
124
- cmd = ["npx", "eslint"]
125
- if args.fix:
126
- cmd.append("--fix")
127
- if file_args:
128
- cmd.extend(file_args)
129
- else:
130
- cmd.extend([".", "--max-warnings=0"])
131
- if not run_linter("ESLint", cmd, project_root):
132
- failures += 1
133
-
134
- # Prettier
135
- if available.get("prettier"):
136
- header("Prettier")
137
- cmd = ["npx", "prettier"]
138
- if args.fix:
139
- cmd.extend(["--write", "."])
140
- else:
141
- cmd.extend(["--check", "."])
142
- if file_args:
143
- cmd = ["npx", "prettier", "--write" if args.fix else "--check"] + file_args
144
- if not run_linter("Prettier", cmd, project_root):
145
- failures += 1
146
-
147
- # Ruff (Python)
148
- if available.get("ruff"):
149
- header("Ruff (Python)")
150
- cmd = ["ruff", "check"]
151
- if args.fix:
152
- cmd.append("--fix")
153
- if file_args:
154
- cmd.extend(file_args)
155
- else:
156
- cmd.append(".")
157
- if not run_linter("Ruff", cmd, project_root):
158
- failures += 1
159
-
160
- # Flake8 (Python fallback)
161
- if available.get("flake8") and not available.get("ruff"):
162
- header("Flake8 (Python)")
163
- cmd = ["flake8"]
164
- if file_args:
165
- cmd.extend(file_args)
166
- else:
167
- cmd.append(".")
168
- if not run_linter("Flake8", cmd, project_root):
169
- failures += 1
170
-
171
- # TypeScript type check (always run if package.json exists)
172
- if Path(project_root, "package.json").exists():
173
- header("TypeScript")
174
- if not run_linter("tsc --noEmit", ["npx", "tsc", "--noEmit"], project_root):
175
- failures += 1
176
-
177
- # Summary
178
- print(f"\n{BOLD}━━━ Lint Summary ━━━{RESET}")
179
- if failures == 0:
180
- ok("All linters passed")
181
- else:
182
- fail(f"{failures} linter(s) reported issues")
183
-
184
- sys.exit(1 if failures > 0 else 0)
185
-
186
-
187
- if __name__ == "__main__":
188
- main()