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.
- package/.agent/ARCHITECTURE.md +21 -14
- package/.agent/agents/swarm-worker-contracts.md +5 -5
- package/.agent/agents/ui-ux-auditor.md +292 -0
- package/.agent/rules/GEMINI.md +8 -8
- 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 +3 -3
- package/.agent/skills/agentic-patterns/SKILL.md +3 -3
- package/.agent/skills/ai-prompt-injection-defense/SKILL.md +3 -3
- package/.agent/skills/api-patterns/SKILL.md +3 -3
- package/.agent/skills/api-security-auditor/SKILL.md +3 -3
- package/.agent/skills/app-builder/SKILL.md +3 -3
- package/.agent/skills/app-builder/templates/SKILL.md +1 -1
- 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 +3 -3
- package/.agent/skills/architecture/SKILL.md +3 -3
- package/.agent/skills/authentication-best-practices/SKILL.md +3 -3
- package/.agent/skills/bash-linux/SKILL.md +3 -3
- package/.agent/skills/behavioral-modes/SKILL.md +3 -3
- package/.agent/skills/brainstorming/SKILL.md +3 -3
- package/.agent/skills/building-native-ui/SKILL.md +3 -3
- package/.agent/skills/clean-code/SKILL.md +3 -3
- package/.agent/skills/code-review-checklist/SKILL.md +3 -3
- package/.agent/skills/config-validator/SKILL.md +3 -3
- package/.agent/skills/csharp-developer/SKILL.md +3 -3
- package/.agent/skills/data-validation-schemas/SKILL.md +3 -3
- package/.agent/skills/database-design/SKILL.md +3 -3
- package/.agent/skills/deployment-procedures/SKILL.md +3 -3
- package/.agent/skills/devops-engineer/SKILL.md +3 -3
- package/.agent/skills/devops-incident-responder/SKILL.md +3 -3
- package/.agent/skills/doc.md +1 -1
- package/.agent/skills/documentation-templates/SKILL.md +3 -3
- package/.agent/skills/edge-computing/SKILL.md +3 -3
- package/.agent/skills/error-resilience/SKILL.md +3 -3
- package/.agent/skills/extract-design-system/SKILL.md +3 -3
- package/.agent/skills/framer-motion-expert/SKILL.md +3 -4
- package/.agent/skills/frontend-design/SKILL.md +3 -3
- package/.agent/skills/game-design-expert/SKILL.md +3 -3
- package/.agent/skills/game-engineering-expert/SKILL.md +3 -3
- package/.agent/skills/geo-fundamentals/SKILL.md +3 -3
- package/.agent/skills/github-operations/SKILL.md +3 -3
- package/.agent/skills/gsap-core/SKILL.md +0 -2
- package/.agent/skills/gsap-frameworks/SKILL.md +0 -2
- package/.agent/skills/gsap-performance/SKILL.md +0 -2
- package/.agent/skills/gsap-plugins/SKILL.md +0 -2
- package/.agent/skills/gsap-react/SKILL.md +0 -2
- package/.agent/skills/gsap-scrolltrigger/SKILL.md +0 -2
- package/.agent/skills/gsap-timeline/SKILL.md +0 -2
- package/.agent/skills/gsap-utils/SKILL.md +0 -2
- package/.agent/skills/i18n-localization/SKILL.md +3 -3
- package/.agent/skills/intelligent-routing/SKILL.md +3 -3
- package/.agent/skills/lint-and-validate/SKILL.md +3 -3
- package/.agent/skills/llm-engineering/SKILL.md +3 -3
- package/.agent/skills/local-first/SKILL.md +3 -3
- package/.agent/skills/mcp-builder/SKILL.md +3 -3
- package/.agent/skills/mobile-design/SKILL.md +3 -3
- package/.agent/skills/monorepo-management/SKILL.md +3 -3
- package/.agent/skills/motion-engineering/SKILL.md +4 -4
- package/.agent/skills/nextjs-react-expert/SKILL.md +3 -3
- package/.agent/skills/nodejs-best-practices/SKILL.md +3 -3
- package/.agent/skills/observability/SKILL.md +3 -3
- package/.agent/skills/parallel-agents/SKILL.md +3 -3
- package/.agent/skills/performance-profiling/SKILL.md +3 -3
- package/.agent/skills/plan-writing/SKILL.md +3 -3
- package/.agent/skills/platform-engineer/SKILL.md +3 -3
- package/.agent/skills/playwright-best-practices/SKILL.md +3 -3
- package/.agent/skills/powershell-windows/SKILL.md +3 -3
- package/.agent/skills/project-idioms/SKILL.md +3 -3
- package/.agent/skills/python-patterns/SKILL.md +3 -3
- package/.agent/skills/python-pro/SKILL.md +3 -3
- package/.agent/skills/react-specialist/SKILL.md +3 -3
- package/.agent/skills/readme-builder/SKILL.md +3 -3
- package/.agent/skills/realtime-patterns/SKILL.md +3 -3
- package/.agent/skills/red-team-tactics/SKILL.md +3 -3
- package/.agent/skills/rust-pro/SKILL.md +3 -3
- package/.agent/skills/seo-fundamentals/SKILL.md +3 -3
- package/.agent/skills/server-management/SKILL.md +3 -3
- package/.agent/skills/shadcn-ui-expert/SKILL.md +3 -3
- package/.agent/skills/skill-creator/SKILL.md +3 -3
- package/.agent/skills/sql-pro/SKILL.md +3 -3
- package/.agent/skills/supabase-postgres-best-practices/SKILL.md +3 -3
- package/.agent/skills/swiftui-expert/SKILL.md +3 -3
- package/.agent/skills/systematic-debugging/SKILL.md +3 -3
- package/.agent/skills/tailwind-patterns/SKILL.md +3 -3
- package/.agent/skills/tdd-workflow/SKILL.md +3 -3
- package/.agent/skills/test-result-analyzer/SKILL.md +3 -3
- package/.agent/skills/testing-patterns/SKILL.md +3 -3
- package/.agent/skills/trend-researcher/SKILL.md +3 -3
- package/.agent/skills/typescript-advanced/SKILL.md +3 -3
- package/.agent/skills/ui-ux-pro-max/SKILL.md +3 -3
- package/.agent/skills/ui-ux-researcher/SKILL.md +3 -3
- package/.agent/skills/vue-expert/SKILL.md +3 -3
- package/.agent/skills/vulnerability-scanner/SKILL.md +3 -3
- package/.agent/skills/web-accessibility-auditor/SKILL.md +3 -3
- package/.agent/skills/web-design-guidelines/SKILL.md +3 -3
- package/.agent/skills/webapp-testing/SKILL.md +3 -3
- package/.agent/skills/whimsy-injector/SKILL.md +3 -3
- package/.agent/skills/workflow-optimizer/SKILL.md +3 -3
- 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/README.md +64 -8
- package/bin/tribunal-kit.js +277 -45
- 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 -755
- 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
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* auto_preview.js — Start, stop, or check a local development server.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* node .agent/scripts/auto_preview.js start
|
|
7
|
+
* node .agent/scripts/auto_preview.js stop
|
|
8
|
+
* node .agent/scripts/auto_preview.js status
|
|
9
|
+
* node .agent/scripts/auto_preview.js restart
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
const { spawn, execSync } = require('child_process');
|
|
17
|
+
const net = require('net');
|
|
18
|
+
|
|
19
|
+
const PID_FILE = ".preview.pid";
|
|
20
|
+
const DEFAULT_PORT = 3000;
|
|
21
|
+
const TIMEOUT_SECONDS = 30;
|
|
22
|
+
|
|
23
|
+
const { GREEN, RED, YELLOW, BOLD, RESET } = require('./colors.js');
|
|
24
|
+
|
|
25
|
+
function findStartCommand() {
|
|
26
|
+
const pkgPath = path.resolve("package.json");
|
|
27
|
+
if (!fs.existsSync(pkgPath)) return { cmd: [], found: false };
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
31
|
+
const scripts = pkg.scripts || {};
|
|
32
|
+
if (scripts.dev) return { cmd: ["npm", "run", "dev"], found: true };
|
|
33
|
+
if (scripts.start) return { cmd: ["npm", "run", "start"], found: true };
|
|
34
|
+
} catch {
|
|
35
|
+
// Ignore
|
|
36
|
+
}
|
|
37
|
+
return { cmd: [], found: false };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getPortFromEnv() {
|
|
41
|
+
const envPath = path.resolve(".env");
|
|
42
|
+
if (fs.existsSync(envPath)) {
|
|
43
|
+
try {
|
|
44
|
+
const data = fs.readFileSync(envPath, 'utf8');
|
|
45
|
+
for (const line of data.split('\n')) {
|
|
46
|
+
if (line.trim().startsWith("PORT=")) {
|
|
47
|
+
return parseInt(line.split("=")[1].trim(), 10);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
} catch {}
|
|
51
|
+
}
|
|
52
|
+
return DEFAULT_PORT;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function isPortOpen(port) {
|
|
56
|
+
return new Promise(resolve => {
|
|
57
|
+
const client = new net.Socket();
|
|
58
|
+
client.setTimeout(1000);
|
|
59
|
+
client.once('connect', () => {
|
|
60
|
+
client.destroy();
|
|
61
|
+
resolve(true);
|
|
62
|
+
}).once('timeout', () => {
|
|
63
|
+
client.destroy();
|
|
64
|
+
resolve(false);
|
|
65
|
+
}).once('error', () => {
|
|
66
|
+
resolve(false);
|
|
67
|
+
}).connect(port, 'localhost');
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function readPid() {
|
|
72
|
+
if (fs.existsSync(PID_FILE)) {
|
|
73
|
+
try {
|
|
74
|
+
return parseInt(fs.readFileSync(PID_FILE, 'utf8').trim(), 10);
|
|
75
|
+
} catch {}
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function writePid(pid) {
|
|
81
|
+
fs.writeFileSync(PID_FILE, String(pid), 'utf8');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function clearPid() {
|
|
85
|
+
if (fs.existsSync(PID_FILE)) {
|
|
86
|
+
fs.unlinkSync(PID_FILE);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function startServer() {
|
|
91
|
+
const port = getPortFromEnv();
|
|
92
|
+
|
|
93
|
+
if (await isPortOpen(port)) {
|
|
94
|
+
console.log(`${YELLOW}⚠️ Port ${port} is already in use.${RESET}`);
|
|
95
|
+
const pid = readPid();
|
|
96
|
+
if (pid) console.log(` Known PID: ${pid}`);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const { cmd, found } = findStartCommand();
|
|
101
|
+
if (!found) {
|
|
102
|
+
console.log(`${RED}❌ No dev/start script found.${RESET}`);
|
|
103
|
+
console.log(` This project has no package.json, or its package.json has no 'dev' or 'start' script.`);
|
|
104
|
+
console.log(` Add a script to package.json, or start your server manually.`);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
console.log(`${BOLD}Starting: ${cmd.join(' ')}${RESET}`);
|
|
109
|
+
// Adjust command for windows (npm.cmd instead of npm)
|
|
110
|
+
const executable = process.platform === 'win32' ? `${cmd[0]}.cmd` : cmd[0];
|
|
111
|
+
|
|
112
|
+
const proc = spawn(executable, cmd.slice(1), {
|
|
113
|
+
stdio: 'pipe',
|
|
114
|
+
detached: true
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Ignore children stdout inside detached mode to let node exit
|
|
118
|
+
proc.stdout.unref();
|
|
119
|
+
proc.stderr.unref();
|
|
120
|
+
proc.unref();
|
|
121
|
+
|
|
122
|
+
writePid(proc.pid);
|
|
123
|
+
|
|
124
|
+
process.stdout.write(`Waiting for port ${port}…`);
|
|
125
|
+
for (let i = 0; i < TIMEOUT_SECONDS; i++) {
|
|
126
|
+
if (await isPortOpen(port)) {
|
|
127
|
+
console.log(`\n${GREEN}✅ Server started${RESET}`);
|
|
128
|
+
console.log(` URL: http://localhost:${port}`);
|
|
129
|
+
console.log(` PID: ${proc.pid}`);
|
|
130
|
+
console.log(` Command: ${cmd.join(' ')}`);
|
|
131
|
+
console.log(`\nStop with: node .agent/scripts/auto_preview.js stop`);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
process.stdout.write(".");
|
|
135
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
console.log(`\n${RED}❌ Server did not start within ${TIMEOUT_SECONDS}s${RESET}`);
|
|
139
|
+
try {
|
|
140
|
+
process.kill(proc.pid, 'SIGTERM');
|
|
141
|
+
} catch {}
|
|
142
|
+
clearPid();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function stopServer() {
|
|
146
|
+
const pid = readPid();
|
|
147
|
+
if (!pid) {
|
|
148
|
+
console.log(`${YELLOW}⚠️ No stored server PID found${RESET}`);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
process.kill(pid, 'SIGTERM');
|
|
153
|
+
console.log(`${GREEN}✅ Server stopped (PID ${pid})${RESET}`);
|
|
154
|
+
} catch {
|
|
155
|
+
console.log(`${YELLOW}Process ${pid} was not running${RESET}`);
|
|
156
|
+
} finally {
|
|
157
|
+
clearPid();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async function showStatus() {
|
|
162
|
+
const port = getPortFromEnv();
|
|
163
|
+
const pid = readPid();
|
|
164
|
+
if (await isPortOpen(port)) {
|
|
165
|
+
console.log(`${GREEN}🟢 Running — http://localhost:${port}${RESET}`);
|
|
166
|
+
if (pid) console.log(` PID: ${pid}`);
|
|
167
|
+
} else {
|
|
168
|
+
console.log(`${RED}🔴 Not running on port ${port}${RESET}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async function main() {
|
|
173
|
+
const args = process.argv.slice(2);
|
|
174
|
+
const actions = new Set(["start", "stop", "status", "restart"]);
|
|
175
|
+
|
|
176
|
+
if (args.length < 1 || !actions.has(args[0])) {
|
|
177
|
+
console.log(`Usage: node auto_preview.js [start|stop|status|restart]`);
|
|
178
|
+
process.exit(1);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const action = args[0];
|
|
182
|
+
if (action === "start") {
|
|
183
|
+
await startServer();
|
|
184
|
+
} else if (action === "stop") {
|
|
185
|
+
stopServer();
|
|
186
|
+
} else if (action === "status") {
|
|
187
|
+
await showStatus();
|
|
188
|
+
} else if (action === "restart") {
|
|
189
|
+
stopServer();
|
|
190
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
191
|
+
await startServer();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (require.main === module) {
|
|
196
|
+
main();
|
|
197
|
+
}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* bundle_analyzer.js — JS/TS bundle size analyzer for the Tribunal Agent Kit.
|
|
4
|
+
*
|
|
5
|
+
* Analyzes build output for:
|
|
6
|
+
* - Total bundle size
|
|
7
|
+
* - Largest files in dist/
|
|
8
|
+
* - Suggested tree-shaking opportunities
|
|
9
|
+
* - Bundler-specific analysis (Vite / Webpack)
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* node .agent/scripts/bundle_analyzer.js .
|
|
13
|
+
* node .agent/scripts/bundle_analyzer.js . --build
|
|
14
|
+
* node .agent/scripts/bundle_analyzer.js . --threshold 500
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
'use strict';
|
|
18
|
+
|
|
19
|
+
const fs = require('fs');
|
|
20
|
+
const path = require('path');
|
|
21
|
+
const { spawnSync } = require('child_process');
|
|
22
|
+
|
|
23
|
+
const { RED, GREEN, YELLOW, BLUE, BOLD, RESET } = require('./colors.js');
|
|
24
|
+
|
|
25
|
+
const HEAVY_PACKAGES = {
|
|
26
|
+
"moment": "Use date-fns or dayjs instead (~2KB vs ~230KB)",
|
|
27
|
+
"lodash": "Import specific functions: lodash/debounce instead of full lodash",
|
|
28
|
+
"rxjs": "Import specific operators to enable tree-shaking",
|
|
29
|
+
"aws-sdk": "Use @aws-sdk/client-* v3 modular imports",
|
|
30
|
+
"firebase": "Use modular imports: firebase/auth, firebase/firestore",
|
|
31
|
+
"chart.js": "Register only needed components",
|
|
32
|
+
"three": "Import specific modules from three/examples/jsm/",
|
|
33
|
+
"@mui/material": "Ensure babel-plugin-import or modular imports",
|
|
34
|
+
"@mui/icons-material": "Import specific icons, never the barrel",
|
|
35
|
+
"antd": "Use modular imports with babel-plugin-import",
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
function header(title) {
|
|
39
|
+
console.log(`\n${BOLD}${BLUE}━━━ ${title} ━━━${RESET}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function ok(msg) {
|
|
43
|
+
console.log(` ${GREEN}✅ ${msg}${RESET}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function failPrint(msg) {
|
|
47
|
+
console.log(` ${RED}❌ ${msg}${RESET}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function warn(msg) {
|
|
51
|
+
console.log(` ${YELLOW}⚠️ ${msg}${RESET}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function skip(msg) {
|
|
55
|
+
console.log(` ${YELLOW}⏭️ ${msg}${RESET}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function formatSize(sizeBytes) {
|
|
59
|
+
if (sizeBytes < 1024) return `${sizeBytes}B`;
|
|
60
|
+
if (sizeBytes < 1024 * 1024) return `${(sizeBytes / 1024).toFixed(1)}KB`;
|
|
61
|
+
return `${(sizeBytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function detectBundler(projectRoot) {
|
|
65
|
+
const pkgPath = path.join(projectRoot, "package.json");
|
|
66
|
+
if (!fs.existsSync(pkgPath)) return null;
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
70
|
+
const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
|
|
71
|
+
|
|
72
|
+
if (deps.vite) return "vite";
|
|
73
|
+
if (deps.next) return "next";
|
|
74
|
+
if (deps.webpack) return "webpack";
|
|
75
|
+
|
|
76
|
+
if (fs.existsSync(path.join(projectRoot, "webpack.config.js")) ||
|
|
77
|
+
fs.existsSync(path.join(projectRoot, "webpack.config.ts"))) {
|
|
78
|
+
return "webpack";
|
|
79
|
+
}
|
|
80
|
+
} catch {}
|
|
81
|
+
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function findDistDir(projectRoot) {
|
|
86
|
+
const candidates = ["dist", "build", ".next", "out", "public/build"];
|
|
87
|
+
for (const c of candidates) {
|
|
88
|
+
const d = path.join(projectRoot, c);
|
|
89
|
+
if (fs.existsSync(d) && fs.statSync(d).isDirectory()) return d;
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function analyzeDist(distDir, thresholdKb) {
|
|
95
|
+
const files = [];
|
|
96
|
+
let total = 0;
|
|
97
|
+
|
|
98
|
+
function walkDir(dir) {
|
|
99
|
+
const items = fs.readdirSync(dir, { withFileTypes: true });
|
|
100
|
+
for (const item of items) {
|
|
101
|
+
const fpath = path.join(dir, item.name);
|
|
102
|
+
if (item.isDirectory()) {
|
|
103
|
+
walkDir(fpath);
|
|
104
|
+
} else {
|
|
105
|
+
const size = fs.statSync(fpath).size;
|
|
106
|
+
total += size;
|
|
107
|
+
files.push([path.relative(distDir, fpath), size]);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
walkDir(distDir);
|
|
114
|
+
} catch {}
|
|
115
|
+
|
|
116
|
+
files.sort((a, b) => b[1] - a[1]);
|
|
117
|
+
return { total, files };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function checkHeavyDependencies(projectRoot) {
|
|
121
|
+
const pkgPath = path.join(projectRoot, "package.json");
|
|
122
|
+
if (!fs.existsSync(pkgPath)) return [];
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
126
|
+
const deps = Object.keys(pkg.dependencies || {});
|
|
127
|
+
const found = [];
|
|
128
|
+
|
|
129
|
+
for (const [pkgName, suggestion] of Object.entries(HEAVY_PACKAGES)) {
|
|
130
|
+
if (deps.includes(pkgName)) {
|
|
131
|
+
found.push([pkgName, suggestion]);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return found;
|
|
135
|
+
} catch {
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function runBuild(projectRoot) {
|
|
141
|
+
const pkgPath = path.join(projectRoot, "package.json");
|
|
142
|
+
if (fs.existsSync(pkgPath)) {
|
|
143
|
+
try {
|
|
144
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
145
|
+
if (!pkg.scripts || !pkg.scripts.build) {
|
|
146
|
+
skip("No 'build' script found in package.json");
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
} catch {}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const executable = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
154
|
+
const result = spawnSync(executable, ["run", "build"], {
|
|
155
|
+
cwd: projectRoot,
|
|
156
|
+
encoding: 'utf8',
|
|
157
|
+
timeout: 300000,
|
|
158
|
+
shell: process.platform === 'win32'
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
if (result.status === 0) {
|
|
162
|
+
ok("Build completed successfully");
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
failPrint("Build failed");
|
|
167
|
+
if (result.error) {
|
|
168
|
+
console.log(` Error: ${result.error.message}`);
|
|
169
|
+
}
|
|
170
|
+
const out = result.stdout ? result.stdout.toString() : '';
|
|
171
|
+
const err = result.stderr ? result.stderr.toString() : '';
|
|
172
|
+
const output = (out + "\n" + err).trim();
|
|
173
|
+
if (output) {
|
|
174
|
+
for (const line of output.split("\n").slice(0, 10)) {
|
|
175
|
+
console.log(` ${line}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return false;
|
|
179
|
+
} catch (e) {
|
|
180
|
+
failPrint(`Execution error: ${e.message}`);
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function main() {
|
|
186
|
+
const args = process.argv.slice(2);
|
|
187
|
+
|
|
188
|
+
let targetPath = null;
|
|
189
|
+
let buildFlag = false;
|
|
190
|
+
let threshold = 250;
|
|
191
|
+
|
|
192
|
+
for (let i = 0; i < args.length; i++) {
|
|
193
|
+
if (args[i] === '--build') buildFlag = true;
|
|
194
|
+
else if (args[i] === '--threshold' && i + 1 < args.length) {
|
|
195
|
+
threshold = parseInt(args[++i], 10);
|
|
196
|
+
} else if (args[i].startsWith('-')) {
|
|
197
|
+
console.log("Usage: node bundle_analyzer.js <path> [--build] [--threshold <kb>]");
|
|
198
|
+
process.exit(1);
|
|
199
|
+
} else if (!targetPath) {
|
|
200
|
+
targetPath = args[i];
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (!targetPath) {
|
|
205
|
+
console.log("Usage: node bundle_analyzer.js <path> [--build] [--threshold <kb>]");
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const projectRoot = path.resolve(targetPath);
|
|
210
|
+
if (!fs.existsSync(projectRoot) || !fs.statSync(projectRoot).isDirectory()) {
|
|
211
|
+
failPrint(`Directory not found: ${projectRoot}`);
|
|
212
|
+
process.exit(1);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
console.log(`${BOLD}Tribunal — bundle_analyzer.js${RESET}`);
|
|
216
|
+
console.log(`Project: ${projectRoot}`);
|
|
217
|
+
|
|
218
|
+
const bundler = detectBundler(projectRoot);
|
|
219
|
+
if (bundler) console.log(` Bundler: ${bundler}`);
|
|
220
|
+
|
|
221
|
+
if (buildFlag) {
|
|
222
|
+
header("Building project");
|
|
223
|
+
if (!runBuild(projectRoot)) {
|
|
224
|
+
process.exit(1);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const distDir = findDistDir(projectRoot);
|
|
229
|
+
const heavy = checkHeavyDependencies(projectRoot);
|
|
230
|
+
|
|
231
|
+
if (!distDir) {
|
|
232
|
+
skip("No build output directory found (dist/, build/, .next/, out/)");
|
|
233
|
+
skip("Run with --build to create a build first, or build manually");
|
|
234
|
+
} else {
|
|
235
|
+
header(`Bundle Size Analysis (${path.relative(projectRoot, distDir)}/)`);
|
|
236
|
+
const { total, files } = analyzeDist(distDir, threshold);
|
|
237
|
+
console.log(`\n Total bundle size: ${BOLD}${formatSize(total)}${RESET}`);
|
|
238
|
+
|
|
239
|
+
const thresholdBytes = threshold * 1024;
|
|
240
|
+
console.log(`\n ${BOLD}Top files by size:${RESET}`);
|
|
241
|
+
let count = 0;
|
|
242
|
+
for (const [filepath, size] of files) {
|
|
243
|
+
if (count++ >= 10) break;
|
|
244
|
+
const sizeStr = formatSize(size).padStart(10, ' ');
|
|
245
|
+
if (size > thresholdBytes) {
|
|
246
|
+
warn(`${sizeStr} ${filepath}`);
|
|
247
|
+
} else {
|
|
248
|
+
console.log(` ${sizeStr} ${filepath}`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const largeJs = files.filter(([f, s]) => (f.endsWith('.js') || f.endsWith('.mjs')) && s > thresholdBytes);
|
|
253
|
+
if (largeJs.length > 0) {
|
|
254
|
+
console.log(`\n ${YELLOW}${largeJs.length} JS file(s) exceed ${threshold}KB threshold${RESET}`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
header("Dependency Weight Check");
|
|
259
|
+
if (heavy.length > 0) {
|
|
260
|
+
for (const [pkgName, suggestion] of heavy) {
|
|
261
|
+
warn(`'${pkgName}' is a heavy dependency`);
|
|
262
|
+
console.log(` → ${suggestion}`);
|
|
263
|
+
}
|
|
264
|
+
} else {
|
|
265
|
+
ok("No known-heavy packages detected");
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
console.log(`\n${BOLD}━━━ Bundle Analysis Summary ━━━${RESET}`);
|
|
269
|
+
if (distDir) {
|
|
270
|
+
const { total } = analyzeDist(distDir, threshold);
|
|
271
|
+
const sizeStr = formatSize(total);
|
|
272
|
+
if (total > 5 * 1024 * 1024) {
|
|
273
|
+
failPrint(`Total bundle: ${sizeStr} — consider code splitting`);
|
|
274
|
+
} else if (total > 2 * 1024 * 1024) {
|
|
275
|
+
warn(`Total bundle: ${sizeStr} — review for optimization opportunities`);
|
|
276
|
+
} else {
|
|
277
|
+
ok(`Total bundle: ${sizeStr}`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (heavy.length > 0) {
|
|
282
|
+
warn(`${heavy.length} heavy dependency suggestion(s) — see above`);
|
|
283
|
+
} else if (distDir && heavy.length === 0) {
|
|
284
|
+
ok("No optimization suggestions");
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (require.main === module) {
|
|
289
|
+
main();
|
|
290
|
+
}
|