tribunal-kit 4.0.1 → 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.
- package/.agent/ARCHITECTURE.md +21 -14
- package/.agent/GEMINI.md +4 -2
- package/.agent/agents/api-architect.md +66 -0
- package/.agent/agents/db-latency-auditor.md +216 -0
- package/.agent/agents/precedence-reviewer.md +41 -4
- package/.agent/agents/resilience-reviewer.md +88 -0
- package/.agent/agents/schema-reviewer.md +67 -0
- package/.agent/agents/swarm-worker-contracts.md +5 -5
- package/.agent/agents/throughput-optimizer.md +299 -0
- package/.agent/agents/ui-ux-auditor.md +292 -0
- package/.agent/agents/vitals-reviewer.md +223 -0
- package/.agent/history/case-law/cases/case-0001.json +33 -0
- package/.agent/history/case-law/index.json +35 -0
- package/.agent/rules/GEMINI.md +28 -11
- package/.agent/scripts/__pycache__/_colors.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/_utils.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/case_law_manager.cpython-311.pyc +0 -0
- package/.agent/scripts/_colors.js +18 -0
- package/.agent/scripts/_utils.js +42 -0
- package/.agent/scripts/auto_preview.js +197 -0
- package/.agent/scripts/bundle_analyzer.js +290 -0
- package/.agent/scripts/case_law_manager.js +684 -0
- package/.agent/scripts/checklist.js +266 -0
- package/.agent/scripts/colors.js +17 -0
- package/.agent/scripts/compress_skills.js +141 -0
- package/.agent/scripts/consolidate_skills.js +149 -0
- package/.agent/scripts/context_broker.js +609 -0
- package/.agent/scripts/deep_compress.js +150 -0
- package/.agent/scripts/dependency_analyzer.js +272 -0
- package/.agent/scripts/inner_loop_validator.js +465 -0
- package/.agent/scripts/lint_runner.js +187 -0
- package/.agent/scripts/minify_context.js +100 -0
- package/.agent/scripts/patch_skills_meta.js +156 -0
- package/.agent/scripts/patch_skills_output.js +244 -0
- package/.agent/scripts/schema_validator.js +297 -0
- package/.agent/scripts/security_scan.js +303 -0
- package/.agent/scripts/session_manager.js +276 -0
- package/.agent/scripts/skill_evolution.js +644 -0
- package/.agent/scripts/skill_integrator.js +313 -0
- package/.agent/scripts/strengthen_skills.js +193 -0
- package/.agent/scripts/strip_tribunal.js +47 -0
- package/.agent/scripts/swarm_dispatcher.js +360 -0
- package/.agent/scripts/test_runner.js +193 -0
- package/.agent/scripts/utils.js +32 -0
- package/.agent/scripts/verify_all.js +256 -0
- package/.agent/skills/agent-organizer/SKILL.md +42 -0
- package/.agent/skills/agentic-patterns/SKILL.md +42 -0
- package/.agent/skills/ai-prompt-injection-defense/SKILL.md +42 -0
- package/.agent/skills/api-patterns/SKILL.md +42 -0
- package/.agent/skills/api-security-auditor/SKILL.md +42 -0
- package/.agent/skills/app-builder/SKILL.md +42 -0
- package/.agent/skills/app-builder/templates/SKILL.md +70 -0
- package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +1 -1
- package/.agent/skills/appflow-wireframe/SKILL.md +42 -0
- package/.agent/skills/architecture/SKILL.md +42 -0
- package/.agent/skills/authentication-best-practices/SKILL.md +42 -0
- package/.agent/skills/bash-linux/SKILL.md +42 -0
- package/.agent/skills/behavioral-modes/SKILL.md +42 -0
- package/.agent/skills/brainstorming/SKILL.md +42 -0
- package/.agent/skills/building-native-ui/SKILL.md +42 -0
- package/.agent/skills/clean-code/SKILL.md +42 -0
- package/.agent/skills/code-review-checklist/SKILL.md +42 -0
- package/.agent/skills/config-validator/SKILL.md +42 -0
- package/.agent/skills/csharp-developer/SKILL.md +42 -0
- package/.agent/skills/data-validation-schemas/SKILL.md +320 -0
- package/.agent/skills/database-design/SKILL.md +42 -0
- package/.agent/skills/deployment-procedures/SKILL.md +42 -0
- package/.agent/skills/devops-engineer/SKILL.md +42 -0
- package/.agent/skills/devops-incident-responder/SKILL.md +42 -0
- package/.agent/skills/doc.md +1 -1
- package/.agent/skills/documentation-templates/SKILL.md +42 -0
- package/.agent/skills/edge-computing/SKILL.md +42 -0
- package/.agent/skills/error-resilience/SKILL.md +420 -0
- package/.agent/skills/extract-design-system/SKILL.md +42 -0
- package/.agent/skills/framer-motion-expert/SKILL.md +42 -1
- package/.agent/skills/frontend-design/SKILL.md +42 -0
- package/.agent/skills/game-design-expert/SKILL.md +42 -0
- package/.agent/skills/game-engineering-expert/SKILL.md +42 -0
- package/.agent/skills/geo-fundamentals/SKILL.md +42 -0
- package/.agent/skills/github-operations/SKILL.md +42 -0
- package/.agent/skills/gsap-core/SKILL.md +300 -0
- package/.agent/skills/gsap-frameworks/SKILL.md +199 -0
- package/.agent/skills/gsap-performance/SKILL.md +125 -0
- package/.agent/skills/gsap-plugins/SKILL.md +472 -0
- package/.agent/skills/gsap-react/SKILL.md +181 -0
- package/.agent/skills/gsap-scrolltrigger/SKILL.md +342 -0
- package/.agent/skills/gsap-timeline/SKILL.md +153 -0
- package/.agent/skills/gsap-utils/SKILL.md +330 -0
- package/.agent/skills/i18n-localization/SKILL.md +42 -0
- package/.agent/skills/intelligent-routing/SKILL.md +72 -1
- package/.agent/skills/lint-and-validate/SKILL.md +42 -0
- package/.agent/skills/llm-engineering/SKILL.md +42 -0
- package/.agent/skills/local-first/SKILL.md +42 -0
- package/.agent/skills/mcp-builder/SKILL.md +42 -0
- package/.agent/skills/mobile-design/SKILL.md +42 -0
- package/.agent/skills/monorepo-management/SKILL.md +326 -0
- package/.agent/skills/motion-engineering/SKILL.md +42 -0
- package/.agent/skills/nextjs-react-expert/SKILL.md +42 -0
- package/.agent/skills/nodejs-best-practices/SKILL.md +42 -0
- package/.agent/skills/observability/SKILL.md +42 -0
- package/.agent/skills/parallel-agents/SKILL.md +42 -0
- package/.agent/skills/performance-profiling/SKILL.md +42 -0
- package/.agent/skills/plan-writing/SKILL.md +42 -0
- package/.agent/skills/platform-engineer/SKILL.md +42 -0
- package/.agent/skills/playwright-best-practices/SKILL.md +42 -0
- package/.agent/skills/powershell-windows/SKILL.md +42 -0
- package/.agent/skills/project-idioms/SKILL.md +42 -0
- package/.agent/skills/python-patterns/SKILL.md +42 -0
- package/.agent/skills/python-pro/SKILL.md +42 -0
- package/.agent/skills/react-specialist/SKILL.md +42 -0
- package/.agent/skills/readme-builder/SKILL.md +42 -0
- package/.agent/skills/realtime-patterns/SKILL.md +42 -0
- package/.agent/skills/red-team-tactics/SKILL.md +42 -0
- package/.agent/skills/rust-pro/SKILL.md +42 -0
- package/.agent/skills/seo-fundamentals/SKILL.md +42 -0
- package/.agent/skills/server-management/SKILL.md +42 -0
- package/.agent/skills/shadcn-ui-expert/SKILL.md +42 -0
- package/.agent/skills/skill-creator/SKILL.md +42 -0
- package/.agent/skills/sql-pro/SKILL.md +42 -0
- package/.agent/skills/supabase-postgres-best-practices/SKILL.md +42 -0
- package/.agent/skills/swiftui-expert/SKILL.md +42 -0
- package/.agent/skills/systematic-debugging/SKILL.md +42 -0
- package/.agent/skills/tailwind-patterns/SKILL.md +42 -0
- package/.agent/skills/tdd-workflow/SKILL.md +42 -0
- package/.agent/skills/test-result-analyzer/SKILL.md +42 -0
- package/.agent/skills/testing-patterns/SKILL.md +42 -0
- package/.agent/skills/trend-researcher/SKILL.md +42 -0
- package/.agent/skills/typescript-advanced/SKILL.md +327 -0
- package/.agent/skills/ui-ux-pro-max/SKILL.md +42 -0
- package/.agent/skills/ui-ux-researcher/SKILL.md +42 -0
- package/.agent/skills/vue-expert/SKILL.md +42 -0
- package/.agent/skills/vulnerability-scanner/SKILL.md +42 -0
- package/.agent/skills/web-accessibility-auditor/SKILL.md +42 -0
- package/.agent/skills/web-design-guidelines/SKILL.md +42 -0
- package/.agent/skills/webapp-testing/SKILL.md +42 -0
- package/.agent/skills/whimsy-injector/SKILL.md +42 -0
- package/.agent/skills/workflow-optimizer/SKILL.md +42 -0
- package/.agent/workflows/audit.md +6 -6
- package/.agent/workflows/deploy.md +1 -1
- package/.agent/workflows/generate.md +23 -6
- package/.agent/workflows/session.md +5 -5
- package/.agent/workflows/swarm.md +2 -2
- package/.agent/workflows/tribunal-backend.md +13 -2
- package/.agent/workflows/tribunal-full.md +15 -8
- package/.agent/workflows/tribunal-speed.md +183 -0
- package/README.md +64 -8
- package/bin/tribunal-kit.js +281 -41
- package/package.json +9 -6
- package/scripts/changelog.js +167 -0
- package/scripts/sync-version.js +81 -0
- package/.agent/scripts/__pycache__/auto_preview.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/bundle_analyzer.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/checklist.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/dependency_analyzer.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/security_scan.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/session_manager.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/skill_integrator.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/swarm_dispatcher.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/test_runner.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/verify_all.cpython-311.pyc +0 -0
- package/.agent/scripts/auto_preview.py +0 -180
- package/.agent/scripts/bundle_analyzer.py +0 -259
- package/.agent/scripts/case_law_manager.py +0 -525
- package/.agent/scripts/checklist.py +0 -209
- package/.agent/scripts/compress_skills.py +0 -167
- package/.agent/scripts/consolidate_skills.py +0 -173
- package/.agent/scripts/deep_compress.py +0 -202
- package/.agent/scripts/dependency_analyzer.py +0 -247
- package/.agent/scripts/lint_runner.py +0 -188
- package/.agent/scripts/minify_context.py +0 -80
- package/.agent/scripts/patch_skills_meta.py +0 -177
- package/.agent/scripts/patch_skills_output.py +0 -285
- package/.agent/scripts/schema_validator.py +0 -279
- package/.agent/scripts/security_scan.py +0 -224
- package/.agent/scripts/session_manager.py +0 -261
- package/.agent/scripts/skill_evolution.py +0 -563
- package/.agent/scripts/skill_integrator.py +0 -234
- package/.agent/scripts/strengthen_skills.py +0 -220
- package/.agent/scripts/strip_tribunal.py +0 -41
- package/.agent/scripts/swarm_dispatcher.py +0 -350
- package/.agent/scripts/test_runner.py +0 -192
- package/.agent/scripts/test_swarm_dispatcher.py +0 -163
- package/.agent/scripts/verify_all.py +0 -195
- package/.agent/skills/gsap-expert/SKILL.md +0 -194
|
@@ -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()
|