tribunal-kit 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agent/.shared/ui-ux-pro-max/README.md +4 -0
- package/.agent/ARCHITECTURE.md +75 -0
- package/.agent/GEMINI.md +89 -0
- package/.agent/agents/backend-specialist.md +178 -0
- package/.agent/agents/code-archaeologist.md +119 -0
- package/.agent/agents/database-architect.md +164 -0
- package/.agent/agents/debugger.md +151 -0
- package/.agent/agents/dependency-reviewer.md +55 -0
- package/.agent/agents/devops-engineer.md +175 -0
- package/.agent/agents/documentation-writer.md +137 -0
- package/.agent/agents/explorer-agent.md +142 -0
- package/.agent/agents/frontend-reviewer.md +80 -0
- package/.agent/agents/frontend-specialist.md +185 -0
- package/.agent/agents/game-developer.md +184 -0
- package/.agent/agents/logic-reviewer.md +66 -0
- package/.agent/agents/mobile-developer.md +152 -0
- package/.agent/agents/orchestrator.md +140 -0
- package/.agent/agents/penetration-tester.md +131 -0
- package/.agent/agents/performance-optimizer.md +139 -0
- package/.agent/agents/performance-reviewer.md +72 -0
- package/.agent/agents/product-manager.md +108 -0
- package/.agent/agents/product-owner.md +99 -0
- package/.agent/agents/project-planner.md +142 -0
- package/.agent/agents/qa-automation-engineer.md +138 -0
- package/.agent/agents/security-auditor.md +170 -0
- package/.agent/agents/seo-specialist.md +132 -0
- package/.agent/agents/sql-reviewer.md +73 -0
- package/.agent/agents/test-coverage-reviewer.md +81 -0
- package/.agent/agents/test-engineer.md +139 -0
- package/.agent/agents/type-safety-reviewer.md +65 -0
- package/.agent/mcp_config.json +40 -0
- package/.agent/rules/GEMINI.md +206 -0
- package/.agent/scripts/auto_preview.py +180 -0
- package/.agent/scripts/checklist.py +209 -0
- package/.agent/scripts/session_manager.py +120 -0
- package/.agent/scripts/verify_all.py +195 -0
- package/.agent/skills/api-patterns/SKILL.md +81 -0
- package/.agent/skills/api-patterns/api-style.md +42 -0
- package/.agent/skills/api-patterns/auth.md +24 -0
- package/.agent/skills/api-patterns/documentation.md +26 -0
- package/.agent/skills/api-patterns/graphql.md +41 -0
- package/.agent/skills/api-patterns/rate-limiting.md +31 -0
- package/.agent/skills/api-patterns/response.md +37 -0
- package/.agent/skills/api-patterns/rest.md +40 -0
- package/.agent/skills/api-patterns/scripts/api_validator.py +211 -0
- package/.agent/skills/api-patterns/security-testing.md +122 -0
- package/.agent/skills/api-patterns/trpc.md +41 -0
- package/.agent/skills/api-patterns/versioning.md +22 -0
- package/.agent/skills/app-builder/SKILL.md +75 -0
- package/.agent/skills/app-builder/agent-coordination.md +71 -0
- package/.agent/skills/app-builder/feature-building.md +53 -0
- package/.agent/skills/app-builder/project-detection.md +34 -0
- package/.agent/skills/app-builder/scaffolding.md +118 -0
- package/.agent/skills/app-builder/tech-stack.md +40 -0
- package/.agent/skills/app-builder/templates/SKILL.md +39 -0
- package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +76 -0
- package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +92 -0
- package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +88 -0
- package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +88 -0
- package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +83 -0
- package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +90 -0
- package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +90 -0
- package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +122 -0
- package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +122 -0
- package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +169 -0
- package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +134 -0
- package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +83 -0
- package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +119 -0
- package/.agent/skills/architecture/SKILL.md +55 -0
- package/.agent/skills/architecture/context-discovery.md +43 -0
- package/.agent/skills/architecture/examples.md +94 -0
- package/.agent/skills/architecture/pattern-selection.md +68 -0
- package/.agent/skills/architecture/patterns-reference.md +50 -0
- package/.agent/skills/architecture/trade-off-analysis.md +77 -0
- package/.agent/skills/bash-linux/SKILL.md +199 -0
- package/.agent/skills/behavioral-modes/SKILL.md +242 -0
- package/.agent/skills/brainstorming/SKILL.md +163 -0
- package/.agent/skills/brainstorming/dynamic-questioning.md +350 -0
- package/.agent/skills/clean-code/SKILL.md +201 -0
- package/.agent/skills/code-review-checklist/SKILL.md +109 -0
- package/.agent/skills/database-design/SKILL.md +52 -0
- package/.agent/skills/database-design/database-selection.md +43 -0
- package/.agent/skills/database-design/indexing.md +39 -0
- package/.agent/skills/database-design/migrations.md +48 -0
- package/.agent/skills/database-design/optimization.md +36 -0
- package/.agent/skills/database-design/orm-selection.md +30 -0
- package/.agent/skills/database-design/schema-design.md +56 -0
- package/.agent/skills/database-design/scripts/schema_validator.py +172 -0
- package/.agent/skills/deployment-procedures/SKILL.md +241 -0
- package/.agent/skills/doc.md +177 -0
- package/.agent/skills/documentation-templates/SKILL.md +194 -0
- package/.agent/skills/frontend-design/SKILL.md +418 -0
- package/.agent/skills/frontend-design/animation-guide.md +331 -0
- package/.agent/skills/frontend-design/color-system.md +311 -0
- package/.agent/skills/frontend-design/decision-trees.md +418 -0
- package/.agent/skills/frontend-design/motion-graphics.md +306 -0
- package/.agent/skills/frontend-design/scripts/accessibility_checker.py +183 -0
- package/.agent/skills/frontend-design/scripts/ux_audit.py +722 -0
- package/.agent/skills/frontend-design/typography-system.md +345 -0
- package/.agent/skills/frontend-design/ux-psychology.md +1116 -0
- package/.agent/skills/frontend-design/visual-effects.md +383 -0
- package/.agent/skills/game-development/2d-games/SKILL.md +119 -0
- package/.agent/skills/game-development/3d-games/SKILL.md +135 -0
- package/.agent/skills/game-development/SKILL.md +167 -0
- package/.agent/skills/game-development/game-art/SKILL.md +185 -0
- package/.agent/skills/game-development/game-audio/SKILL.md +190 -0
- package/.agent/skills/game-development/game-design/SKILL.md +129 -0
- package/.agent/skills/game-development/mobile-games/SKILL.md +108 -0
- package/.agent/skills/game-development/multiplayer/SKILL.md +132 -0
- package/.agent/skills/game-development/pc-games/SKILL.md +144 -0
- package/.agent/skills/game-development/vr-ar/SKILL.md +123 -0
- package/.agent/skills/game-development/web-games/SKILL.md +150 -0
- package/.agent/skills/geo-fundamentals/SKILL.md +156 -0
- package/.agent/skills/geo-fundamentals/scripts/geo_checker.py +289 -0
- package/.agent/skills/i18n-localization/SKILL.md +154 -0
- package/.agent/skills/i18n-localization/scripts/i18n_checker.py +241 -0
- package/.agent/skills/intelligent-routing/SKILL.md +335 -0
- package/.agent/skills/lint-and-validate/SKILL.md +45 -0
- package/.agent/skills/lint-and-validate/scripts/lint_runner.py +184 -0
- package/.agent/skills/lint-and-validate/scripts/type_coverage.py +173 -0
- package/.agent/skills/mcp-builder/SKILL.md +176 -0
- package/.agent/skills/mobile-design/SKILL.md +394 -0
- package/.agent/skills/mobile-design/decision-trees.md +516 -0
- package/.agent/skills/mobile-design/mobile-backend.md +491 -0
- package/.agent/skills/mobile-design/mobile-color-system.md +420 -0
- package/.agent/skills/mobile-design/mobile-debugging.md +122 -0
- package/.agent/skills/mobile-design/mobile-design-thinking.md +357 -0
- package/.agent/skills/mobile-design/mobile-navigation.md +458 -0
- package/.agent/skills/mobile-design/mobile-performance.md +767 -0
- package/.agent/skills/mobile-design/mobile-testing.md +356 -0
- package/.agent/skills/mobile-design/mobile-typography.md +433 -0
- package/.agent/skills/mobile-design/platform-android.md +666 -0
- package/.agent/skills/mobile-design/platform-ios.md +561 -0
- package/.agent/skills/mobile-design/scripts/mobile_audit.py +670 -0
- package/.agent/skills/mobile-design/touch-psychology.md +537 -0
- package/.agent/skills/nextjs-react-expert/1-async-eliminating-waterfalls.md +312 -0
- package/.agent/skills/nextjs-react-expert/2-bundle-bundle-size-optimization.md +240 -0
- package/.agent/skills/nextjs-react-expert/3-server-server-side-performance.md +490 -0
- package/.agent/skills/nextjs-react-expert/4-client-client-side-data-fetching.md +264 -0
- package/.agent/skills/nextjs-react-expert/5-rerender-re-render-optimization.md +581 -0
- package/.agent/skills/nextjs-react-expert/6-rendering-rendering-performance.md +432 -0
- package/.agent/skills/nextjs-react-expert/7-js-javascript-performance.md +684 -0
- package/.agent/skills/nextjs-react-expert/8-advanced-advanced-patterns.md +150 -0
- package/.agent/skills/nextjs-react-expert/SKILL.md +286 -0
- package/.agent/skills/nextjs-react-expert/scripts/convert_rules.py +222 -0
- package/.agent/skills/nextjs-react-expert/scripts/react_performance_checker.py +252 -0
- package/.agent/skills/nodejs-best-practices/SKILL.md +333 -0
- package/.agent/skills/parallel-agents/SKILL.md +175 -0
- package/.agent/skills/performance-profiling/SKILL.md +143 -0
- package/.agent/skills/performance-profiling/scripts/lighthouse_audit.py +76 -0
- package/.agent/skills/plan-writing/SKILL.md +152 -0
- package/.agent/skills/powershell-windows/SKILL.md +167 -0
- package/.agent/skills/python-patterns/SKILL.md +441 -0
- package/.agent/skills/red-team-tactics/SKILL.md +199 -0
- package/.agent/skills/rust-pro/SKILL.md +176 -0
- package/.agent/skills/seo-fundamentals/SKILL.md +129 -0
- package/.agent/skills/seo-fundamentals/scripts/seo_checker.py +219 -0
- package/.agent/skills/server-management/SKILL.md +161 -0
- package/.agent/skills/systematic-debugging/SKILL.md +109 -0
- package/.agent/skills/tailwind-patterns/SKILL.md +269 -0
- package/.agent/skills/tdd-workflow/SKILL.md +149 -0
- package/.agent/skills/testing-patterns/SKILL.md +178 -0
- package/.agent/skills/testing-patterns/scripts/test_runner.py +219 -0
- package/.agent/skills/vulnerability-scanner/SKILL.md +276 -0
- package/.agent/skills/vulnerability-scanner/checklists.md +121 -0
- package/.agent/skills/vulnerability-scanner/scripts/security_scan.py +458 -0
- package/.agent/skills/web-design-guidelines/SKILL.md +57 -0
- package/.agent/skills/webapp-testing/SKILL.md +187 -0
- package/.agent/skills/webapp-testing/scripts/playwright_runner.py +173 -0
- package/.agent/workflows/brainstorm.md +100 -0
- package/.agent/workflows/create.md +86 -0
- package/.agent/workflows/debug.md +104 -0
- package/.agent/workflows/deploy.md +102 -0
- package/.agent/workflows/enhance.md +107 -0
- package/.agent/workflows/generate.md +100 -0
- package/.agent/workflows/orchestrate.md +102 -0
- package/.agent/workflows/plan.md +108 -0
- package/.agent/workflows/preview.md +81 -0
- package/.agent/workflows/review.md +88 -0
- package/.agent/workflows/status.md +69 -0
- package/.agent/workflows/test.md +117 -0
- package/.agent/workflows/tribunal-backend.md +69 -0
- package/.agent/workflows/tribunal-database.md +88 -0
- package/.agent/workflows/tribunal-frontend.md +69 -0
- package/.agent/workflows/tribunal-full.md +77 -0
- package/.agent/workflows/ui-ux-pro-max.md +153 -0
- package/LICENSE +21 -0
- package/README.md +136 -0
- package/bin/tribunal-kit.js +289 -0
- package/package.json +34 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
auto_preview.py — Start, stop, or check a local development server.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
python .agent/scripts/auto_preview.py start
|
|
7
|
+
python .agent/scripts/auto_preview.py stop
|
|
8
|
+
python .agent/scripts/auto_preview.py status
|
|
9
|
+
python .agent/scripts/auto_preview.py restart
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
import sys
|
|
14
|
+
import json
|
|
15
|
+
import time
|
|
16
|
+
import signal
|
|
17
|
+
import socket
|
|
18
|
+
import subprocess
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
PID_FILE = ".preview.pid"
|
|
22
|
+
DEFAULT_PORT = 3000
|
|
23
|
+
TIMEOUT_SECONDS = 30
|
|
24
|
+
|
|
25
|
+
GREEN = "\033[92m"
|
|
26
|
+
RED = "\033[91m"
|
|
27
|
+
YELLOW = "\033[93m"
|
|
28
|
+
BOLD = "\033[1m"
|
|
29
|
+
RESET = "\033[0m"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def find_start_command() -> tuple[list[str], bool]:
|
|
33
|
+
"""
|
|
34
|
+
Read package.json for a dev/start script.
|
|
35
|
+
Returns (command, found) — found=False if no package.json or no scripts.
|
|
36
|
+
"""
|
|
37
|
+
pkg_path = Path("package.json")
|
|
38
|
+
if not pkg_path.exists():
|
|
39
|
+
return [], False
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
with open(pkg_path) as f:
|
|
43
|
+
pkg = json.load(f)
|
|
44
|
+
scripts = pkg.get("scripts", {})
|
|
45
|
+
if "dev" in scripts:
|
|
46
|
+
return ["npm", "run", "dev"], True
|
|
47
|
+
elif "start" in scripts:
|
|
48
|
+
return ["npm", "run", "start"], True
|
|
49
|
+
else:
|
|
50
|
+
return [], False
|
|
51
|
+
except (json.JSONDecodeError, IOError):
|
|
52
|
+
return [], False
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def get_port_from_env() -> int:
|
|
56
|
+
env_path = Path(".env")
|
|
57
|
+
if env_path.exists():
|
|
58
|
+
try:
|
|
59
|
+
with open(env_path) as f:
|
|
60
|
+
for line in f:
|
|
61
|
+
if line.startswith("PORT="):
|
|
62
|
+
return int(line.split("=")[1].strip())
|
|
63
|
+
except (ValueError, IOError):
|
|
64
|
+
pass
|
|
65
|
+
return DEFAULT_PORT
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def is_port_open(port: int) -> bool:
|
|
69
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
70
|
+
s.settimeout(1)
|
|
71
|
+
return s.connect_ex(("localhost", port)) == 0
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def read_pid() -> int | None:
|
|
75
|
+
pid_file = Path(PID_FILE)
|
|
76
|
+
if pid_file.exists():
|
|
77
|
+
try:
|
|
78
|
+
return int(pid_file.read_text().strip())
|
|
79
|
+
except (ValueError, IOError):
|
|
80
|
+
pass
|
|
81
|
+
return None
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def write_pid(pid: int) -> None:
|
|
85
|
+
Path(PID_FILE).write_text(str(pid))
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def clear_pid() -> None:
|
|
89
|
+
pid_file = Path(PID_FILE)
|
|
90
|
+
if pid_file.exists():
|
|
91
|
+
pid_file.unlink()
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def start_server() -> None:
|
|
95
|
+
port = get_port_from_env()
|
|
96
|
+
|
|
97
|
+
if is_port_open(port):
|
|
98
|
+
print(f"{YELLOW}⚠️ Port {port} is already in use.{RESET}")
|
|
99
|
+
pid = read_pid()
|
|
100
|
+
if pid:
|
|
101
|
+
print(f" Known PID: {pid}")
|
|
102
|
+
return
|
|
103
|
+
|
|
104
|
+
cmd, found = find_start_command()
|
|
105
|
+
if not found:
|
|
106
|
+
print(f"{RED}❌ No dev/start script found.{RESET}")
|
|
107
|
+
print(f" This project has no package.json, or its package.json has no 'dev' or 'start' script.")
|
|
108
|
+
print(f" Add a script to package.json, or start your server manually.")
|
|
109
|
+
return
|
|
110
|
+
|
|
111
|
+
print(f"{BOLD}Starting: {' '.join(cmd)}{RESET}")
|
|
112
|
+
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
|
|
113
|
+
write_pid(proc.pid)
|
|
114
|
+
|
|
115
|
+
print(f"Waiting for port {port}…", end="", flush=True)
|
|
116
|
+
for _ in range(TIMEOUT_SECONDS):
|
|
117
|
+
if is_port_open(port):
|
|
118
|
+
print()
|
|
119
|
+
print(f"\n{GREEN}✅ Server started{RESET}")
|
|
120
|
+
print(f" URL: http://localhost:{port}")
|
|
121
|
+
print(f" PID: {proc.pid}")
|
|
122
|
+
print(f" Command: {' '.join(cmd)}")
|
|
123
|
+
print(f"\nStop with: python .agent/scripts/auto_preview.py stop")
|
|
124
|
+
return
|
|
125
|
+
print(".", end="", flush=True)
|
|
126
|
+
time.sleep(1)
|
|
127
|
+
|
|
128
|
+
print()
|
|
129
|
+
print(f"{RED}❌ Server did not start within {TIMEOUT_SECONDS}s{RESET}")
|
|
130
|
+
proc.terminate()
|
|
131
|
+
clear_pid()
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def stop_server() -> None:
|
|
135
|
+
pid = read_pid()
|
|
136
|
+
if not pid:
|
|
137
|
+
print(f"{YELLOW}⚠️ No stored server PID found{RESET}")
|
|
138
|
+
return
|
|
139
|
+
try:
|
|
140
|
+
os.kill(pid, signal.SIGTERM)
|
|
141
|
+
time.sleep(1)
|
|
142
|
+
print(f"{GREEN}✅ Server stopped (PID {pid}){RESET}")
|
|
143
|
+
except ProcessLookupError:
|
|
144
|
+
print(f"{YELLOW}Process {pid} was not running{RESET}")
|
|
145
|
+
finally:
|
|
146
|
+
clear_pid()
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def show_status() -> None:
|
|
150
|
+
port = get_port_from_env()
|
|
151
|
+
pid = read_pid()
|
|
152
|
+
if is_port_open(port):
|
|
153
|
+
print(f"{GREEN}🟢 Running — http://localhost:{port}{RESET}")
|
|
154
|
+
if pid:
|
|
155
|
+
print(f" PID: {pid}")
|
|
156
|
+
else:
|
|
157
|
+
print(f"{RED}🔴 Not running on port {port}{RESET}")
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def main() -> None:
|
|
161
|
+
actions = {"start", "stop", "status", "restart"}
|
|
162
|
+
if len(sys.argv) < 2 or sys.argv[1] not in actions:
|
|
163
|
+
print(f"Usage: auto_preview.py [start|stop|status|restart]")
|
|
164
|
+
sys.exit(1)
|
|
165
|
+
|
|
166
|
+
action = sys.argv[1]
|
|
167
|
+
if action == "start":
|
|
168
|
+
start_server()
|
|
169
|
+
elif action == "stop":
|
|
170
|
+
stop_server()
|
|
171
|
+
elif action == "status":
|
|
172
|
+
show_status()
|
|
173
|
+
elif action == "restart":
|
|
174
|
+
stop_server()
|
|
175
|
+
time.sleep(1)
|
|
176
|
+
start_server()
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
if __name__ == "__main__":
|
|
180
|
+
main()
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
checklist.py — Priority-based project audit runner for the Tribunal Agent Kit.
|
|
4
|
+
|
|
5
|
+
Runs a tiered audit sequence:
|
|
6
|
+
Priority 1: Security
|
|
7
|
+
Priority 2: Lint
|
|
8
|
+
Priority 3: Schema validation
|
|
9
|
+
Priority 4: Tests
|
|
10
|
+
Priority 5: UX / Accessibility
|
|
11
|
+
Priority 6: SEO
|
|
12
|
+
Priority 7: Lighthouse / E2E (requires --url)
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
python .agent/scripts/checklist.py .
|
|
16
|
+
python .agent/scripts/checklist.py . --url http://localhost:3000
|
|
17
|
+
python .agent/scripts/checklist.py . --skip security,seo
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import os
|
|
21
|
+
import sys
|
|
22
|
+
import subprocess
|
|
23
|
+
import argparse
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
|
|
26
|
+
# ━━━ ANSI color output ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
27
|
+
RED = "\033[91m"
|
|
28
|
+
GREEN = "\033[92m"
|
|
29
|
+
YELLOW = "\033[93m"
|
|
30
|
+
BLUE = "\033[94m"
|
|
31
|
+
RESET = "\033[0m"
|
|
32
|
+
BOLD = "\033[1m"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def print_header(title: str) -> None:
|
|
36
|
+
print(f"\n{BOLD}{BLUE}━━━ {title} ━━━{RESET}")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def print_ok(msg: str) -> None:
|
|
40
|
+
print(f" {GREEN}✅ {msg}{RESET}")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def print_fail(msg: str) -> None:
|
|
44
|
+
print(f" {RED}❌ {msg}{RESET}")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def print_skip(msg: str) -> None:
|
|
48
|
+
print(f" {YELLOW}⏭️ Skipped: {msg}{RESET}")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def run_check(label: str, cmd: list[str], cwd: str) -> bool:
|
|
52
|
+
"""Run a shell command and return True if it exits with code 0."""
|
|
53
|
+
try:
|
|
54
|
+
result = subprocess.run(
|
|
55
|
+
cmd,
|
|
56
|
+
cwd=cwd,
|
|
57
|
+
capture_output=True,
|
|
58
|
+
text=True,
|
|
59
|
+
timeout=60,
|
|
60
|
+
)
|
|
61
|
+
if result.returncode == 0:
|
|
62
|
+
print_ok(f"{label} passed")
|
|
63
|
+
return True
|
|
64
|
+
else:
|
|
65
|
+
print_fail(f"{label} failed")
|
|
66
|
+
if result.stdout.strip():
|
|
67
|
+
print(f" {result.stdout.strip()[:500]}")
|
|
68
|
+
if result.stderr.strip():
|
|
69
|
+
print(f" {result.stderr.strip()[:500]}")
|
|
70
|
+
return False
|
|
71
|
+
except FileNotFoundError:
|
|
72
|
+
print_skip(f"{label} — command not found (tool not installed)")
|
|
73
|
+
return True # Don't block on tools that aren't installed
|
|
74
|
+
except subprocess.TimeoutExpired:
|
|
75
|
+
print_fail(f"{label} — timed out after 60s")
|
|
76
|
+
return False
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def check_secrets(project_root: str) -> bool:
|
|
80
|
+
"""Scan for hardcoded secrets in source files."""
|
|
81
|
+
print_header("Security — Secret Scan")
|
|
82
|
+
dangerous_patterns = [
|
|
83
|
+
"password=",
|
|
84
|
+
"secret=",
|
|
85
|
+
"api_key=",
|
|
86
|
+
"apikey=",
|
|
87
|
+
"AUTH_TOKEN=",
|
|
88
|
+
"PRIVATE_KEY=",
|
|
89
|
+
]
|
|
90
|
+
found_issues = False
|
|
91
|
+
source_extensions = {".ts", ".tsx", ".js", ".jsx", ".py", ".env"}
|
|
92
|
+
|
|
93
|
+
for root, dirs, files in os.walk(project_root):
|
|
94
|
+
# Skip known-safe directories
|
|
95
|
+
dirs[:] = [d for d in dirs if d not in {"node_modules", ".git", ".agent", "dist", "__pycache__"}]
|
|
96
|
+
for filename in files:
|
|
97
|
+
if not any(filename.endswith(ext) for ext in source_extensions):
|
|
98
|
+
continue
|
|
99
|
+
if filename.startswith(".env"):
|
|
100
|
+
continue # .env files are allowed to have these
|
|
101
|
+
filepath = os.path.join(root, filename)
|
|
102
|
+
try:
|
|
103
|
+
with open(filepath, "r", encoding="utf-8", errors="ignore") as f:
|
|
104
|
+
for line_num, line in enumerate(f, 1):
|
|
105
|
+
line_lower = line.lower().strip()
|
|
106
|
+
if any(pattern in line_lower for pattern in dangerous_patterns):
|
|
107
|
+
# Only flag if it looks like an actual value (contains = and a non-empty value)
|
|
108
|
+
if "=" in line and not line_lower.strip().startswith("#"):
|
|
109
|
+
rel = os.path.relpath(filepath, project_root)
|
|
110
|
+
print_fail(f"Possible secret: {rel}:{line_num} → {line.strip()[:80]}")
|
|
111
|
+
found_issues = True
|
|
112
|
+
except (IOError, PermissionError):
|
|
113
|
+
pass
|
|
114
|
+
|
|
115
|
+
if not found_issues:
|
|
116
|
+
print_ok("No hardcoded secrets detected")
|
|
117
|
+
return not found_issues
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def run_all(project_root: str, url: str | None, skip: list[str]) -> int:
|
|
121
|
+
"""Run all checklist tiers. Returns number of failures."""
|
|
122
|
+
failures = 0
|
|
123
|
+
|
|
124
|
+
# Priority 1 — Security
|
|
125
|
+
if "security" not in skip:
|
|
126
|
+
print_header("Priority 1 — Security")
|
|
127
|
+
if not check_secrets(project_root):
|
|
128
|
+
failures += 1
|
|
129
|
+
else:
|
|
130
|
+
print_skip("Security tier")
|
|
131
|
+
|
|
132
|
+
# Priority 2 — Lint
|
|
133
|
+
if "lint" not in skip:
|
|
134
|
+
print_header("Priority 2 — Lint")
|
|
135
|
+
if not run_check("ESLint", ["npx", "eslint", ".", "--max-warnings=0"], project_root):
|
|
136
|
+
failures += 1
|
|
137
|
+
if not run_check("TypeScript", ["npx", "tsc", "--noEmit"], project_root):
|
|
138
|
+
failures += 1
|
|
139
|
+
else:
|
|
140
|
+
print_skip("Lint tier")
|
|
141
|
+
|
|
142
|
+
# Priority 3 — Schema
|
|
143
|
+
if "schema" not in skip:
|
|
144
|
+
print_header("Priority 3 — Schema")
|
|
145
|
+
print_skip("Schema check — run manually if you have DB migrations")
|
|
146
|
+
else:
|
|
147
|
+
print_skip("Schema tier")
|
|
148
|
+
|
|
149
|
+
# Priority 4 — Tests
|
|
150
|
+
if "tests" not in skip:
|
|
151
|
+
print_header("Priority 4 — Tests")
|
|
152
|
+
if not run_check("Test suite", ["npm", "test", "--", "--passWithNoTests"], project_root):
|
|
153
|
+
failures += 1
|
|
154
|
+
else:
|
|
155
|
+
print_skip("Tests tier")
|
|
156
|
+
|
|
157
|
+
# Priority 5 — UX
|
|
158
|
+
if "ux" not in skip:
|
|
159
|
+
print_header("Priority 5 — UX / Accessibility")
|
|
160
|
+
print_skip("UX audit — run /preview start then check manually or with Lighthouse")
|
|
161
|
+
else:
|
|
162
|
+
print_skip("UX tier")
|
|
163
|
+
|
|
164
|
+
# Priority 6 — SEO
|
|
165
|
+
if "seo" not in skip:
|
|
166
|
+
print_header("Priority 6 — SEO")
|
|
167
|
+
print_skip("SEO check — use /ui-ux-pro-max for SEO-sensitive pages")
|
|
168
|
+
else:
|
|
169
|
+
print_skip("SEO tier")
|
|
170
|
+
|
|
171
|
+
# Priority 7 — Lighthouse / E2E
|
|
172
|
+
if url and "e2e" not in skip:
|
|
173
|
+
print_header("Priority 7 — Lighthouse / E2E")
|
|
174
|
+
if not run_check("Playwright E2E", ["npx", "playwright", "test"], project_root):
|
|
175
|
+
failures += 1
|
|
176
|
+
elif not url:
|
|
177
|
+
print_skip("E2E / Lighthouse — pass --url to enable")
|
|
178
|
+
|
|
179
|
+
# ━━━ Summary ━━━
|
|
180
|
+
print(f"\n{BOLD}━━━ Checklist Summary ━━━{RESET}")
|
|
181
|
+
if failures == 0:
|
|
182
|
+
print_ok(f"All checks passed — ready to proceed")
|
|
183
|
+
else:
|
|
184
|
+
print_fail(f"{failures} tier(s) failed — fix Critical issues before proceeding")
|
|
185
|
+
|
|
186
|
+
return failures
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def main() -> None:
|
|
190
|
+
parser = argparse.ArgumentParser(description="Tribunal project checklist runner")
|
|
191
|
+
parser.add_argument("path", help="Project root directory")
|
|
192
|
+
parser.add_argument("--url", help="Local server URL for Lighthouse/E2E checks", default=None)
|
|
193
|
+
parser.add_argument("--skip", help="Comma-separated tiers to skip (security,lint,schema,tests,ux,seo,e2e)", default="")
|
|
194
|
+
args = parser.parse_args()
|
|
195
|
+
|
|
196
|
+
project_root = os.path.abspath(args.path)
|
|
197
|
+
if not os.path.isdir(project_root):
|
|
198
|
+
print_fail(f"Directory not found: {project_root}")
|
|
199
|
+
sys.exit(1)
|
|
200
|
+
|
|
201
|
+
skip = [s.strip().lower() for s in args.skip.split(",") if s.strip()]
|
|
202
|
+
|
|
203
|
+
print(f"{BOLD}Tribunal Checklist — {project_root}{RESET}")
|
|
204
|
+
failures = run_all(project_root, args.url, skip)
|
|
205
|
+
sys.exit(1 if failures > 0 else 0)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
if __name__ == "__main__":
|
|
209
|
+
main()
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
session_manager.py — Agent session state tracking for multi-conversation work.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
python .agent/scripts/session_manager.py save "working on auth"
|
|
7
|
+
python .agent/scripts/session_manager.py load
|
|
8
|
+
python .agent/scripts/session_manager.py show
|
|
9
|
+
python .agent/scripts/session_manager.py clear
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
import sys
|
|
14
|
+
import json
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from datetime import datetime
|
|
17
|
+
|
|
18
|
+
STATE_FILE = ".agent_session.json"
|
|
19
|
+
|
|
20
|
+
GREEN = "\033[92m"
|
|
21
|
+
YELLOW = "\033[93m"
|
|
22
|
+
BLUE = "\033[94m"
|
|
23
|
+
RED = "\033[91m"
|
|
24
|
+
BOLD = "\033[1m"
|
|
25
|
+
RESET = "\033[0m"
|
|
26
|
+
|
|
27
|
+
VALID_COMMANDS = {"save", "load", "show", "clear"}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def load_state() -> dict:
|
|
31
|
+
path = Path(STATE_FILE)
|
|
32
|
+
if not path.exists():
|
|
33
|
+
return {}
|
|
34
|
+
try:
|
|
35
|
+
with open(path) as f:
|
|
36
|
+
return json.load(f)
|
|
37
|
+
except (json.JSONDecodeError, IOError):
|
|
38
|
+
return {}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def save_state(state: dict) -> None:
|
|
42
|
+
with open(STATE_FILE, "w") as f:
|
|
43
|
+
json.dump(state, f, indent=2)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def cmd_save(note: str) -> None:
|
|
47
|
+
state = load_state()
|
|
48
|
+
entry = {
|
|
49
|
+
"timestamp": datetime.now().isoformat(),
|
|
50
|
+
"note": note,
|
|
51
|
+
"session": len(state.get("history", [])) + 1,
|
|
52
|
+
}
|
|
53
|
+
state.setdefault("history", []).append(entry)
|
|
54
|
+
state["current"] = entry
|
|
55
|
+
save_state(state)
|
|
56
|
+
print(f"{GREEN}✅ Session saved:{RESET} {note}")
|
|
57
|
+
print(f" Time: {entry['timestamp']}")
|
|
58
|
+
print(f" Session: #{entry['session']}")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def cmd_load() -> None:
|
|
62
|
+
state = load_state()
|
|
63
|
+
current = state.get("current")
|
|
64
|
+
if not current:
|
|
65
|
+
print(f"{YELLOW}No active session — use 'save' first.{RESET}")
|
|
66
|
+
return
|
|
67
|
+
print(f"{BOLD}Current session:{RESET}")
|
|
68
|
+
print(f" Session: #{current['session']}")
|
|
69
|
+
print(f" Time: {current['timestamp']}")
|
|
70
|
+
print(f" Note: {current['note']}")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def cmd_show() -> None:
|
|
74
|
+
state = load_state()
|
|
75
|
+
history = state.get("history", [])
|
|
76
|
+
if not history:
|
|
77
|
+
print(f"{YELLOW}No session history.{RESET}")
|
|
78
|
+
return
|
|
79
|
+
print(f"{BOLD}Session History ({len(history)} total):{RESET}")
|
|
80
|
+
for entry in reversed(history[-10:]):
|
|
81
|
+
print(f"\n {BLUE}#{entry['session']}{RESET} — {entry['timestamp'][:16]}")
|
|
82
|
+
print(f" {entry['note']}")
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def cmd_clear() -> None:
|
|
86
|
+
path = Path(STATE_FILE)
|
|
87
|
+
if path.exists():
|
|
88
|
+
path.unlink()
|
|
89
|
+
print(f"{GREEN}✅ Session state cleared.{RESET}")
|
|
90
|
+
else:
|
|
91
|
+
print(f"{YELLOW}No session file found — nothing to clear.{RESET}")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def main() -> None:
|
|
95
|
+
if len(sys.argv) < 2:
|
|
96
|
+
print(f"Usage: session_manager.py [save <note>|load|show|clear]")
|
|
97
|
+
sys.exit(1)
|
|
98
|
+
|
|
99
|
+
cmd = sys.argv[1].lower()
|
|
100
|
+
|
|
101
|
+
if cmd not in VALID_COMMANDS:
|
|
102
|
+
print(f"{RED}Unknown command: '{cmd}'{RESET}")
|
|
103
|
+
print(f"Valid commands: {', '.join(sorted(VALID_COMMANDS))}")
|
|
104
|
+
sys.exit(1)
|
|
105
|
+
|
|
106
|
+
if cmd == "save":
|
|
107
|
+
note = " ".join(sys.argv[2:]).strip()
|
|
108
|
+
if not note:
|
|
109
|
+
note = f"session {datetime.now().strftime('%Y-%m-%d %H:%M')}"
|
|
110
|
+
cmd_save(note)
|
|
111
|
+
elif cmd == "load":
|
|
112
|
+
cmd_load()
|
|
113
|
+
elif cmd == "show":
|
|
114
|
+
cmd_show()
|
|
115
|
+
elif cmd == "clear":
|
|
116
|
+
cmd_clear()
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
if __name__ == "__main__":
|
|
120
|
+
main()
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
verify_all.py — Full pre-deploy validation suite for the Tribunal Agent Kit.
|
|
4
|
+
|
|
5
|
+
Runs comprehensive checks before any production deployment.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
python .agent/scripts/verify_all.py
|
|
9
|
+
python .agent/scripts/verify_all.py --skip build,deps
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
import sys
|
|
14
|
+
import subprocess
|
|
15
|
+
import argparse
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
RED = "\033[91m"
|
|
19
|
+
GREEN = "\033[92m"
|
|
20
|
+
YELLOW = "\033[93m"
|
|
21
|
+
BLUE = "\033[94m"
|
|
22
|
+
BOLD = "\033[1m"
|
|
23
|
+
RESET = "\033[0m"
|
|
24
|
+
|
|
25
|
+
RESULTS: list[tuple[str, bool, str]] = []
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def section(title: str) -> None:
|
|
29
|
+
print(f"\n{BOLD}{BLUE}━━━ {title} ━━━{RESET}")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def ok(label: str, note: str = "") -> None:
|
|
33
|
+
msg = f"{GREEN}✅ {label}{RESET}" + (f" {YELLOW}({note}){RESET}" if note else "")
|
|
34
|
+
print(f" {msg}")
|
|
35
|
+
RESULTS.append((label, True, note))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def fail(label: str, note: str = "") -> None:
|
|
39
|
+
note_str = f"\n {note}" if note else ""
|
|
40
|
+
print(f" {RED}❌ {label}{RESET}{note_str}")
|
|
41
|
+
RESULTS.append((label, False, note))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def skip(label: str, reason: str) -> None:
|
|
45
|
+
print(f" {YELLOW}⏭️ {label} — {reason}{RESET}")
|
|
46
|
+
RESULTS.append((label, True, f"skipped: {reason}"))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def run(label: str, cmd: list[str], cwd: str) -> bool:
|
|
50
|
+
try:
|
|
51
|
+
result = subprocess.run(cmd, cwd=cwd, capture_output=True, text=True, timeout=120)
|
|
52
|
+
if result.returncode == 0:
|
|
53
|
+
ok(label)
|
|
54
|
+
return True
|
|
55
|
+
output = (result.stdout + result.stderr).strip()
|
|
56
|
+
fail(label, output[:500] if output else "non-zero exit code")
|
|
57
|
+
return False
|
|
58
|
+
except FileNotFoundError:
|
|
59
|
+
skip(label, "tool not installed — skipping")
|
|
60
|
+
return True
|
|
61
|
+
except subprocess.TimeoutExpired:
|
|
62
|
+
fail(label, "timed out after 120s")
|
|
63
|
+
return False
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def scan_secrets(cwd: str) -> bool:
|
|
67
|
+
"""Scan source files for obviously hardcoded credentials."""
|
|
68
|
+
patterns = ["password=", "secret=", "api_key=", "private_key=", "auth_token="]
|
|
69
|
+
found = []
|
|
70
|
+
for root, dirs, files in os.walk(cwd):
|
|
71
|
+
dirs[:] = [d for d in dirs if d not in {"node_modules", ".git", "dist", "__pycache__", ".agent"}]
|
|
72
|
+
for fname in files:
|
|
73
|
+
if not fname.endswith((".ts", ".js", ".tsx", ".jsx", ".py")):
|
|
74
|
+
continue
|
|
75
|
+
fpath = os.path.join(root, fname)
|
|
76
|
+
try:
|
|
77
|
+
with open(fpath, encoding="utf-8", errors="ignore") as f:
|
|
78
|
+
for i, line in enumerate(f, 1):
|
|
79
|
+
low = line.lower().strip()
|
|
80
|
+
if any(p in low for p in patterns) and not low.startswith("#") and "=" in low:
|
|
81
|
+
rel = os.path.relpath(fpath, cwd)
|
|
82
|
+
found.append(f"{rel}:{i}")
|
|
83
|
+
except (IOError, PermissionError):
|
|
84
|
+
pass
|
|
85
|
+
if found:
|
|
86
|
+
fail("Secret scan", "\n ".join(found[:5]))
|
|
87
|
+
return False
|
|
88
|
+
ok("Secret scan — no hardcoded credentials found")
|
|
89
|
+
return True
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def has_npm(cwd: str) -> bool:
|
|
93
|
+
"""Check if there's a package.json to run npm commands against."""
|
|
94
|
+
return Path(cwd, "package.json").exists()
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def verify_all(cwd: str, skipped: list[str]) -> int:
|
|
98
|
+
failures = 0
|
|
99
|
+
|
|
100
|
+
section("1 — Secret Scan")
|
|
101
|
+
if "secrets" not in skipped:
|
|
102
|
+
if not scan_secrets(cwd):
|
|
103
|
+
failures += 1
|
|
104
|
+
else:
|
|
105
|
+
skip("Secret scan", "skipped by flag")
|
|
106
|
+
|
|
107
|
+
section("2 — TypeScript")
|
|
108
|
+
if "typescript" not in skipped:
|
|
109
|
+
if has_npm(cwd):
|
|
110
|
+
if not run("tsc --noEmit", ["npx", "tsc", "--noEmit"], cwd):
|
|
111
|
+
failures += 1
|
|
112
|
+
else:
|
|
113
|
+
skip("TypeScript", "no package.json found in project")
|
|
114
|
+
else:
|
|
115
|
+
skip("TypeScript", "skipped by flag")
|
|
116
|
+
|
|
117
|
+
section("3 — ESLint")
|
|
118
|
+
if "lint" not in skipped:
|
|
119
|
+
if has_npm(cwd):
|
|
120
|
+
if not run("ESLint", ["npx", "eslint", ".", "--max-warnings=0"], cwd):
|
|
121
|
+
failures += 1
|
|
122
|
+
else:
|
|
123
|
+
skip("ESLint", "no package.json found in project")
|
|
124
|
+
else:
|
|
125
|
+
skip("ESLint", "skipped by flag")
|
|
126
|
+
|
|
127
|
+
section("4 — Unit Tests")
|
|
128
|
+
if "tests" not in skipped:
|
|
129
|
+
if has_npm(cwd):
|
|
130
|
+
if not run("Test suite", ["npm", "test", "--", "--passWithNoTests"], cwd):
|
|
131
|
+
failures += 1
|
|
132
|
+
else:
|
|
133
|
+
skip("Tests", "no package.json found in project")
|
|
134
|
+
else:
|
|
135
|
+
skip("Tests", "skipped by flag")
|
|
136
|
+
|
|
137
|
+
section("5 — Build")
|
|
138
|
+
if "build" not in skipped:
|
|
139
|
+
if has_npm(cwd):
|
|
140
|
+
if not run("npm run build", ["npm", "run", "build"], cwd):
|
|
141
|
+
failures += 1
|
|
142
|
+
else:
|
|
143
|
+
skip("Build", "no package.json found in project")
|
|
144
|
+
else:
|
|
145
|
+
skip("Build", "skipped by flag")
|
|
146
|
+
|
|
147
|
+
section("6 — Dependency Audit")
|
|
148
|
+
if "deps" not in skipped:
|
|
149
|
+
if has_npm(cwd):
|
|
150
|
+
if not run("npm audit", ["npm", "audit", "--audit-level=high"], cwd):
|
|
151
|
+
failures += 1
|
|
152
|
+
else:
|
|
153
|
+
skip("Dependency audit", "no package.json found in project")
|
|
154
|
+
else:
|
|
155
|
+
skip("Dependency audit", "skipped by flag")
|
|
156
|
+
|
|
157
|
+
print(f"\n{BOLD}━━━ Summary ━━━{RESET}")
|
|
158
|
+
for label, passed, note in RESULTS:
|
|
159
|
+
status = f"{GREEN}✅{RESET}" if passed else f"{RED}❌{RESET}"
|
|
160
|
+
note_str = f" {YELLOW}({note}){RESET}" if not passed and note else ""
|
|
161
|
+
print(f" {status} {label}{note_str}")
|
|
162
|
+
|
|
163
|
+
print()
|
|
164
|
+
if failures == 0:
|
|
165
|
+
print(f"{GREEN}{BOLD}All checks passed — safe to deploy.{RESET}")
|
|
166
|
+
else:
|
|
167
|
+
print(f"{RED}{BOLD}{failures} check(s) failed — fix before deploying.{RESET}")
|
|
168
|
+
|
|
169
|
+
return failures
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def main() -> None:
|
|
173
|
+
parser = argparse.ArgumentParser(
|
|
174
|
+
description="Tribunal pre-deploy validation suite",
|
|
175
|
+
usage="%(prog)s [--skip CHECKS]"
|
|
176
|
+
)
|
|
177
|
+
parser.add_argument(
|
|
178
|
+
"--skip",
|
|
179
|
+
default="",
|
|
180
|
+
help="Comma-separated checks to skip: secrets,typescript,lint,tests,build,deps"
|
|
181
|
+
)
|
|
182
|
+
args = parser.parse_args()
|
|
183
|
+
|
|
184
|
+
cwd = os.getcwd()
|
|
185
|
+
skipped = [s.strip().lower() for s in args.skip.split(",") if s.strip()]
|
|
186
|
+
|
|
187
|
+
print(f"{BOLD}Tribunal — verify_all.py{RESET}")
|
|
188
|
+
print(f"Project: {cwd}\n")
|
|
189
|
+
|
|
190
|
+
failures = verify_all(cwd, skipped)
|
|
191
|
+
sys.exit(1 if failures > 0 else 0)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
if __name__ == "__main__":
|
|
195
|
+
main()
|