tribunal-kit 4.4.0 → 4.4.2
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/agents/api-architect.md +66 -66
- package/.agent/agents/db-latency-auditor.md +216 -216
- package/.agent/agents/precedence-reviewer.md +250 -250
- package/.agent/agents/resilience-reviewer.md +88 -88
- package/.agent/agents/schema-reviewer.md +67 -67
- package/.agent/agents/throughput-optimizer.md +299 -299
- package/.agent/agents/ui-ux-auditor.md +292 -292
- package/.agent/agents/vitals-reviewer.md +223 -223
- package/.agent/history/architecture-graph.yaml +32 -1
- package/.agent/history/graph-cache.json +66 -19
- package/.agent/history/snapshots/bin__tribunal-kit.js.json +19 -0
- package/.agent/history/snapshots/eslint.config.js.json +9 -0
- package/.agent/history/snapshots/migrate_refs.js.json +3 -3
- package/.agent/history/snapshots/scripts__changelog.js.json +2 -1
- package/.agent/history/snapshots/scripts__sync-version.js.json +2 -1
- package/.agent/history/snapshots/scripts__validate-payload.js.json +1 -0
- package/.agent/history/snapshots/test__integration__bridges.test.js.json +2 -1
- package/.agent/history/snapshots/test__integration__init.test.js.json +1 -0
- package/.agent/history/snapshots/test__integration__routing.test.js.json +1 -0
- package/.agent/history/snapshots/test__integration__swarm_dispatcher.test.js.json +2 -1
- package/.agent/history/snapshots/test__integration__wave2.test.js.json +2 -1
- package/.agent/history/snapshots/test__unit__args.test.js.json +11 -1
- package/.agent/history/snapshots/test__unit__case_law_manager.test.js.json +1 -0
- package/.agent/history/snapshots/test__unit__context_broker.test.js.json +11 -0
- package/.agent/history/snapshots/test__unit__copyDir.test.js.json +11 -1
- package/.agent/history/snapshots/test__unit__graph_tools.test.js.json +1 -0
- package/.agent/history/snapshots/test__unit__inner_loop_validator.test.js.json +11 -0
- package/.agent/history/snapshots/test__unit__selfInstall.test.js.json +11 -1
- package/.agent/history/snapshots/test__unit__semver.test.js.json +11 -1
- package/.agent/history/snapshots/test__unit__swarm_dispatcher.test.js.json +1 -0
- package/.agent/scripts/_colors.js +154 -2
- package/.agent/scripts/_utils.js +205 -3
- package/.agent/scripts/append_flow.js +72 -72
- package/.agent/scripts/auto_preview.js +197 -197
- package/.agent/scripts/bundle_analyzer.js +90 -119
- package/.agent/scripts/case_law_manager.js +18 -13
- package/.agent/scripts/checklist.js +100 -88
- package/.agent/scripts/colors.js +7 -13
- package/.agent/scripts/compress_skills.js +141 -141
- package/.agent/scripts/consolidate_skills.js +149 -149
- package/.agent/scripts/context_broker.js +605 -609
- package/.agent/scripts/deep_compress.js +150 -150
- package/.agent/scripts/dependency_analyzer.js +68 -106
- package/.agent/scripts/graph_builder.js +341 -311
- package/.agent/scripts/graph_visualizer.js +390 -384
- package/.agent/scripts/graph_zoom.js +6 -4
- package/.agent/scripts/inner_loop_validator.js +445 -465
- package/.agent/scripts/lint_runner.js +27 -28
- package/.agent/scripts/minify_context.js +100 -100
- package/.agent/scripts/mutation_runner.js +280 -280
- package/.agent/scripts/patch_skills_meta.js +156 -156
- package/.agent/scripts/patch_skills_output.js +244 -244
- package/.agent/scripts/schema_validator.js +280 -297
- package/.agent/scripts/security_scan.js +37 -64
- package/.agent/scripts/session_manager.js +270 -276
- package/.agent/scripts/skill_evolution.js +637 -644
- package/.agent/scripts/skill_integrator.js +307 -313
- package/.agent/scripts/strengthen_skills.js +193 -193
- package/.agent/scripts/strip_tribunal.js +47 -47
- package/.agent/scripts/swarm_dispatcher.js +360 -360
- package/.agent/scripts/test_runner.js +32 -39
- package/.agent/scripts/utils.js +10 -25
- package/.agent/scripts/verify_all.js +84 -92
- 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/doc.md +1 -1
- package/.agent/skills/knowledge-graph/SKILL.md +52 -52
- package/.agent/skills/ui-ux-pro-max/SKILL.md +562 -562
- package/.agent/workflows/generate.md +183 -183
- package/.agent/workflows/tribunal-speed.md +183 -183
- package/README.md +1 -1
- package/bin/tribunal-kit.js +76 -87
- package/package.json +6 -3
- package/scripts/changelog.js +167 -167
- package/scripts/sync-version.js +81 -81
- package/.agent/history/architecture-explorer.html +0 -352
- 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
|
@@ -1,150 +1,150 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* deep_compress.js - Deep surgical compression for .agent/ markdown files (skills, agents, workflows).
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
'use strict';
|
|
7
|
-
|
|
8
|
-
const fs = require('fs');
|
|
9
|
-
const path = require('path');
|
|
10
|
-
|
|
11
|
-
const BASE_DIRS = ['.agent/skills', '.agent/agents', '.agent/workflows'];
|
|
12
|
-
|
|
13
|
-
const REMOVE_SECTIONS = [
|
|
14
|
-
/^## 🏛️ Tribunal Integration[\s\S]*?(?=\n## |$)/gm,
|
|
15
|
-
/^## Tribunal Integration[\s\S]*?(?=\n## |$)/gm,
|
|
16
|
-
/^### ✅ Pre-Flight Self-Audit[\s\S]*?(?=\n### |\n## |$)/gm,
|
|
17
|
-
/^## Pre-Flight Self-Audit[\s\S]*?(?=\n## |$)/gm,
|
|
18
|
-
/^## Cross-Workflow Navigation[\s\S]*?(?=\n## |$)/gm,
|
|
19
|
-
/^## LLM Traps[\s\S]*?(?=\n## |$)/gm,
|
|
20
|
-
/^## VBC Protocol[\s\S]*?(?=\n## |$)/gm,
|
|
21
|
-
/^## Output Format\n```[\s\S]*?```\n/gm,
|
|
22
|
-
/^## 🤖 LLM-Specific Traps[\s\S]*?(?=\n## |$)/gm,
|
|
23
|
-
];
|
|
24
|
-
|
|
25
|
-
const VERBOSE_COMMENT_PATTERNS = [
|
|
26
|
-
/^(\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/gm,
|
|
27
|
-
/^(\s*)#\s*(?:TypedDict gives you|Usage:|Note:|Return user|Return None|Automatically)\b[^\n]*\n/gm,
|
|
28
|
-
/^\s*\/\/\s*Usage:\s*\n(?=\s*[<{])/gm,
|
|
29
|
-
/^\s*#\s*Usage:\s*\n(?=\s*[{])/gm,
|
|
30
|
-
/^\s*\/\/\s*When (?:server responds|a component|React can interrupt|the React Compiler)[^\n]*\n/gm,
|
|
31
|
-
];
|
|
32
|
-
|
|
33
|
-
function stripChattyOpeners(content) {
|
|
34
|
-
return content.replace(/(^# .+\n)\n.{60,}\n.{30,}\n(?:\n---)/gm, '$1\n---');
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function compressLegacyModernBlocks(content) {
|
|
38
|
-
const pattern = /```(\w+)\n((?:.*\n)*?.*(?:\/\/|#) ❌ LEGACY[^\n]*\n(?:.*\n)*?)```\n\n```\w+\n((?:.*\n)*?.*(?:\/\/|#) ✅ MODERN[^\n]*\n(?:.*\n)*?)```/gm;
|
|
39
|
-
return content.replace(pattern, (match, lang, legacy, modern) => {
|
|
40
|
-
const totalLines = (legacy.match(/\n/g) || []).length + (modern.match(/\n/g) || []).length;
|
|
41
|
-
if (totalLines > 28) return match;
|
|
42
|
-
return `\`\`\`${lang}\n// ❌ LEGACY\n${legacy.trim()}\n\n// ✅ MODERN\n${modern.trim()}\n\`\`\``;
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function stripEmptyComments(content) {
|
|
47
|
-
content = content.replace(/^\s*\/\/\s*$\n/gm, '');
|
|
48
|
-
content = content.replace(/^\s*#\s*$\n/gm, '');
|
|
49
|
-
return content;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function dedupBulletPoints(content) {
|
|
53
|
-
const lines = content.split('\n');
|
|
54
|
-
const seenBullets = {};
|
|
55
|
-
const output = [];
|
|
56
|
-
for (let i = 0; i < lines.length; i++) {
|
|
57
|
-
const line = lines[i];
|
|
58
|
-
const stripped = line.trim();
|
|
59
|
-
if (/^(✅|❌|- ✅|- ❌)/.test(stripped)) {
|
|
60
|
-
if (seenBullets[stripped] !== undefined && (i - seenBullets[stripped]) < 80) {
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
seenBullets[stripped] = i;
|
|
64
|
-
}
|
|
65
|
-
output.push(line);
|
|
66
|
-
}
|
|
67
|
-
return output.join('\n');
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function collapseBlanks(content) {
|
|
71
|
-
return content.replace(/\n{3,}/g, '\n\n');
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function compressFile(filePath) {
|
|
75
|
-
const original = fs.readFileSync(filePath, 'utf8');
|
|
76
|
-
let content = original;
|
|
77
|
-
|
|
78
|
-
for (const pattern of REMOVE_SECTIONS) {
|
|
79
|
-
content = content.replace(pattern, '');
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
content = stripChattyOpeners(content);
|
|
83
|
-
content = compressLegacyModernBlocks(content);
|
|
84
|
-
|
|
85
|
-
for (const pattern of VERBOSE_COMMENT_PATTERNS) {
|
|
86
|
-
content = content.replace(pattern, '');
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
content = stripEmptyComments(content);
|
|
90
|
-
content = dedupBulletPoints(content);
|
|
91
|
-
content = collapseBlanks(content);
|
|
92
|
-
|
|
93
|
-
if (content.trim() !== original.trim()) {
|
|
94
|
-
fs.writeFileSync(filePath, content.trim() + '\n', 'utf8');
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
return [Buffer.byteLength(original, 'utf8'), Buffer.byteLength(content, 'utf8')];
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function main() {
|
|
101
|
-
let totalOrig = 0;
|
|
102
|
-
let totalNew = 0;
|
|
103
|
-
const fileResults = [];
|
|
104
|
-
|
|
105
|
-
function walkDir(dir) {
|
|
106
|
-
let items;
|
|
107
|
-
try { items = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
|
|
108
|
-
for (const item of items) {
|
|
109
|
-
const fpath = path.join(dir, item.name);
|
|
110
|
-
if (item.isDirectory()) walkDir(fpath);
|
|
111
|
-
else if (item.name.endsWith('.md')) {
|
|
112
|
-
const [orig, newL] = compressFile(fpath);
|
|
113
|
-
totalOrig += orig;
|
|
114
|
-
totalNew += newL;
|
|
115
|
-
const saved = orig - newL;
|
|
116
|
-
if (saved > 200) {
|
|
117
|
-
fileResults.push([saved, fpath]);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
for (const base of BASE_DIRS) {
|
|
124
|
-
if (fs.existsSync(base)) walkDir(base);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
fileResults.sort((a, b) => b[0] - a[0]);
|
|
128
|
-
|
|
129
|
-
const savedTotal = totalOrig - totalNew;
|
|
130
|
-
const pct = totalOrig ? (savedTotal / totalOrig * 100) : 0;
|
|
131
|
-
|
|
132
|
-
console.log(`\n${'='.repeat(58)}`);
|
|
133
|
-
console.log(` Deep Compression Complete`);
|
|
134
|
-
console.log(`${'='.repeat(58)}`);
|
|
135
|
-
console.log(` Original : ${totalOrig} bytes (${Math.floor(totalOrig / 1024)}KB)`);
|
|
136
|
-
console.log(` After : ${totalNew} bytes (${Math.floor(totalNew / 1024)}KB)`);
|
|
137
|
-
console.log(` Saved : ${savedTotal} bytes (${Math.floor(savedTotal / 1024)}KB) — ${pct.toFixed(1)}%`);
|
|
138
|
-
console.log(`\n Top savings:`);
|
|
139
|
-
|
|
140
|
-
for (const [saved, filePath] of fileResults.slice(0, 20)) {
|
|
141
|
-
const parts = filePath.split(path.sep);
|
|
142
|
-
const skill = parts.length >= 2 ? `${parts[parts.length - 2]}/${parts[parts.length - 1]}` : filePath;
|
|
143
|
-
console.log(` -${Math.floor(saved / 1024).toString().padStart(2, ' ')}KB ${skill}`);
|
|
144
|
-
}
|
|
145
|
-
console.log();
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (require.main === module) {
|
|
149
|
-
main();
|
|
150
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* deep_compress.js - Deep surgical compression for .agent/ markdown files (skills, agents, workflows).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
const BASE_DIRS = ['.agent/skills', '.agent/agents', '.agent/workflows'];
|
|
12
|
+
|
|
13
|
+
const REMOVE_SECTIONS = [
|
|
14
|
+
/^## 🏛️ Tribunal Integration[\s\S]*?(?=\n## |$)/gm,
|
|
15
|
+
/^## Tribunal Integration[\s\S]*?(?=\n## |$)/gm,
|
|
16
|
+
/^### ✅ Pre-Flight Self-Audit[\s\S]*?(?=\n### |\n## |$)/gm,
|
|
17
|
+
/^## Pre-Flight Self-Audit[\s\S]*?(?=\n## |$)/gm,
|
|
18
|
+
/^## Cross-Workflow Navigation[\s\S]*?(?=\n## |$)/gm,
|
|
19
|
+
/^## LLM Traps[\s\S]*?(?=\n## |$)/gm,
|
|
20
|
+
/^## VBC Protocol[\s\S]*?(?=\n## |$)/gm,
|
|
21
|
+
/^## Output Format\n```[\s\S]*?```\n/gm,
|
|
22
|
+
/^## 🤖 LLM-Specific Traps[\s\S]*?(?=\n## |$)/gm,
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
const VERBOSE_COMMENT_PATTERNS = [
|
|
26
|
+
/^(\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/gm,
|
|
27
|
+
/^(\s*)#\s*(?:TypedDict gives you|Usage:|Note:|Return user|Return None|Automatically)\b[^\n]*\n/gm,
|
|
28
|
+
/^\s*\/\/\s*Usage:\s*\n(?=\s*[<{])/gm,
|
|
29
|
+
/^\s*#\s*Usage:\s*\n(?=\s*[{])/gm,
|
|
30
|
+
/^\s*\/\/\s*When (?:server responds|a component|React can interrupt|the React Compiler)[^\n]*\n/gm,
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
function stripChattyOpeners(content) {
|
|
34
|
+
return content.replace(/(^# .+\n)\n.{60,}\n.{30,}\n(?:\n---)/gm, '$1\n---');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function compressLegacyModernBlocks(content) {
|
|
38
|
+
const pattern = /```(\w+)\n((?:.*\n)*?.*(?:\/\/|#) ❌ LEGACY[^\n]*\n(?:.*\n)*?)```\n\n```\w+\n((?:.*\n)*?.*(?:\/\/|#) ✅ MODERN[^\n]*\n(?:.*\n)*?)```/gm;
|
|
39
|
+
return content.replace(pattern, (match, lang, legacy, modern) => {
|
|
40
|
+
const totalLines = (legacy.match(/\n/g) || []).length + (modern.match(/\n/g) || []).length;
|
|
41
|
+
if (totalLines > 28) return match;
|
|
42
|
+
return `\`\`\`${lang}\n// ❌ LEGACY\n${legacy.trim()}\n\n// ✅ MODERN\n${modern.trim()}\n\`\`\``;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function stripEmptyComments(content) {
|
|
47
|
+
content = content.replace(/^\s*\/\/\s*$\n/gm, '');
|
|
48
|
+
content = content.replace(/^\s*#\s*$\n/gm, '');
|
|
49
|
+
return content;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function dedupBulletPoints(content) {
|
|
53
|
+
const lines = content.split('\n');
|
|
54
|
+
const seenBullets = {};
|
|
55
|
+
const output = [];
|
|
56
|
+
for (let i = 0; i < lines.length; i++) {
|
|
57
|
+
const line = lines[i];
|
|
58
|
+
const stripped = line.trim();
|
|
59
|
+
if (/^(✅|❌|- ✅|- ❌)/.test(stripped)) {
|
|
60
|
+
if (seenBullets[stripped] !== undefined && (i - seenBullets[stripped]) < 80) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
seenBullets[stripped] = i;
|
|
64
|
+
}
|
|
65
|
+
output.push(line);
|
|
66
|
+
}
|
|
67
|
+
return output.join('\n');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function collapseBlanks(content) {
|
|
71
|
+
return content.replace(/\n{3,}/g, '\n\n');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function compressFile(filePath) {
|
|
75
|
+
const original = fs.readFileSync(filePath, 'utf8');
|
|
76
|
+
let content = original;
|
|
77
|
+
|
|
78
|
+
for (const pattern of REMOVE_SECTIONS) {
|
|
79
|
+
content = content.replace(pattern, '');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
content = stripChattyOpeners(content);
|
|
83
|
+
content = compressLegacyModernBlocks(content);
|
|
84
|
+
|
|
85
|
+
for (const pattern of VERBOSE_COMMENT_PATTERNS) {
|
|
86
|
+
content = content.replace(pattern, '');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
content = stripEmptyComments(content);
|
|
90
|
+
content = dedupBulletPoints(content);
|
|
91
|
+
content = collapseBlanks(content);
|
|
92
|
+
|
|
93
|
+
if (content.trim() !== original.trim()) {
|
|
94
|
+
fs.writeFileSync(filePath, content.trim() + '\n', 'utf8');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return [Buffer.byteLength(original, 'utf8'), Buffer.byteLength(content, 'utf8')];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function main() {
|
|
101
|
+
let totalOrig = 0;
|
|
102
|
+
let totalNew = 0;
|
|
103
|
+
const fileResults = [];
|
|
104
|
+
|
|
105
|
+
function walkDir(dir) {
|
|
106
|
+
let items;
|
|
107
|
+
try { items = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
|
|
108
|
+
for (const item of items) {
|
|
109
|
+
const fpath = path.join(dir, item.name);
|
|
110
|
+
if (item.isDirectory()) walkDir(fpath);
|
|
111
|
+
else if (item.name.endsWith('.md')) {
|
|
112
|
+
const [orig, newL] = compressFile(fpath);
|
|
113
|
+
totalOrig += orig;
|
|
114
|
+
totalNew += newL;
|
|
115
|
+
const saved = orig - newL;
|
|
116
|
+
if (saved > 200) {
|
|
117
|
+
fileResults.push([saved, fpath]);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
for (const base of BASE_DIRS) {
|
|
124
|
+
if (fs.existsSync(base)) walkDir(base);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
fileResults.sort((a, b) => b[0] - a[0]);
|
|
128
|
+
|
|
129
|
+
const savedTotal = totalOrig - totalNew;
|
|
130
|
+
const pct = totalOrig ? (savedTotal / totalOrig * 100) : 0;
|
|
131
|
+
|
|
132
|
+
console.log(`\n${'='.repeat(58)}`);
|
|
133
|
+
console.log(` Deep Compression Complete`);
|
|
134
|
+
console.log(`${'='.repeat(58)}`);
|
|
135
|
+
console.log(` Original : ${totalOrig} bytes (${Math.floor(totalOrig / 1024)}KB)`);
|
|
136
|
+
console.log(` After : ${totalNew} bytes (${Math.floor(totalNew / 1024)}KB)`);
|
|
137
|
+
console.log(` Saved : ${savedTotal} bytes (${Math.floor(savedTotal / 1024)}KB) — ${pct.toFixed(1)}%`);
|
|
138
|
+
console.log(`\n Top savings:`);
|
|
139
|
+
|
|
140
|
+
for (const [saved, filePath] of fileResults.slice(0, 20)) {
|
|
141
|
+
const parts = filePath.split(path.sep);
|
|
142
|
+
const skill = parts.length >= 2 ? `${parts[parts.length - 2]}/${parts[parts.length - 1]}` : filePath;
|
|
143
|
+
console.log(` -${Math.floor(saved / 1024).toString().padStart(2, ' ')}KB ${skill}`);
|
|
144
|
+
}
|
|
145
|
+
console.log();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (require.main === module) {
|
|
149
|
+
main();
|
|
150
|
+
}
|
|
@@ -18,12 +18,16 @@
|
|
|
18
18
|
|
|
19
19
|
const fs = require('fs');
|
|
20
20
|
const path = require('path');
|
|
21
|
-
const { spawnSync } = require('child_process');
|
|
22
21
|
|
|
23
|
-
const {
|
|
22
|
+
const {
|
|
23
|
+
RED, GREEN, YELLOW, BLUE, BOLD, DIM, CYAN, RESET,
|
|
24
|
+
banner, sectionHeader, timer, formatMs,
|
|
25
|
+
ok, fail, warn, skip,
|
|
26
|
+
} = require('./_colors');
|
|
27
|
+
|
|
28
|
+
const { walkDir, loadJson, runCommand } = require('./_utils');
|
|
24
29
|
|
|
25
30
|
const SOURCE_EXTENSIONS = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
26
|
-
const SKIP_DIRS = new Set(["node_modules", ".git", "dist", "build", ".next", ".agent", "__pycache__", "test", "tests", "__tests__"]);
|
|
27
31
|
|
|
28
32
|
const NODE_BUILTINS = new Set([
|
|
29
33
|
"fs", "path", "os", "crypto", "http", "https", "url", "util",
|
|
@@ -37,36 +41,6 @@ const NODE_BUILTINS = new Set([
|
|
|
37
41
|
"node:perf_hooks", "node:worker_threads", "node:timers"
|
|
38
42
|
]);
|
|
39
43
|
|
|
40
|
-
function header(title) {
|
|
41
|
-
console.log(`\n${BOLD}${BLUE}━━━ ${title} ━━━${RESET}`);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function ok(msg) {
|
|
45
|
-
console.log(` ${GREEN}✅ ${msg}${RESET}`);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function fail(msg) {
|
|
49
|
-
console.log(` ${RED}❌ ${msg}${RESET}`);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function warn(msg) {
|
|
53
|
-
console.log(` ${YELLOW}⚠️ ${msg}${RESET}`);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function skip(msg) {
|
|
57
|
-
console.log(` ${YELLOW}⏭️ ${msg}${RESET}`);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function loadPackageJson(projectRoot) {
|
|
61
|
-
const pkgPath = path.resolve(projectRoot, "package.json");
|
|
62
|
-
if (!fs.existsSync(pkgPath)) return null;
|
|
63
|
-
try {
|
|
64
|
-
return JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
65
|
-
} catch {
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
44
|
function extractImports(projectRoot) {
|
|
71
45
|
const imports = new Set();
|
|
72
46
|
const importPatterns = [
|
|
@@ -75,42 +49,30 @@ function extractImports(projectRoot) {
|
|
|
75
49
|
/import\s*\(\s*["']([^"'.][^"']*)["']/g
|
|
76
50
|
];
|
|
77
51
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if (
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
while ((match = pattern.exec(content)) !== null) {
|
|
97
|
-
let pkg = match[1];
|
|
98
|
-
if (pkg.startsWith("@")) {
|
|
99
|
-
const parts = pkg.split("/");
|
|
100
|
-
pkg = parts.length >= 2 ? `${parts[0]}/${parts[1]}` : pkg;
|
|
101
|
-
} else {
|
|
102
|
-
pkg = pkg.split("/")[0];
|
|
103
|
-
}
|
|
104
|
-
imports.add(pkg);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
} catch {}
|
|
52
|
+
// Use shared walkDir from _utils.js
|
|
53
|
+
const files = walkDir(projectRoot, { extensions: SOURCE_EXTENSIONS });
|
|
54
|
+
|
|
55
|
+
for (const fullPath of files) {
|
|
56
|
+
let content;
|
|
57
|
+
try { content = fs.readFileSync(fullPath, 'utf8'); } catch { continue; }
|
|
58
|
+
|
|
59
|
+
for (const pattern of importPatterns) {
|
|
60
|
+
// Reset regex state for each file
|
|
61
|
+
pattern.lastIndex = 0;
|
|
62
|
+
let match;
|
|
63
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
64
|
+
let pkg = match[1];
|
|
65
|
+
if (pkg.startsWith("@")) {
|
|
66
|
+
const parts = pkg.split("/");
|
|
67
|
+
pkg = parts.length >= 2 ? `${parts[0]}/${parts[1]}` : pkg;
|
|
68
|
+
} else {
|
|
69
|
+
pkg = pkg.split("/")[0];
|
|
108
70
|
}
|
|
71
|
+
imports.add(pkg);
|
|
109
72
|
}
|
|
110
73
|
}
|
|
111
74
|
}
|
|
112
75
|
|
|
113
|
-
walk(projectRoot);
|
|
114
76
|
return imports;
|
|
115
77
|
}
|
|
116
78
|
|
|
@@ -151,44 +113,41 @@ function checkPhantom(pkg, usedImports) {
|
|
|
151
113
|
}
|
|
152
114
|
|
|
153
115
|
function runNpmAudit(projectRoot) {
|
|
116
|
+
const result = runCommand('npm', ['audit', '--json'], {
|
|
117
|
+
cwd: projectRoot,
|
|
118
|
+
timeout: 60000,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
if (result.status === null && !result.stdout) {
|
|
122
|
+
skip("npm not installed — skipping audit");
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
|
|
154
126
|
try {
|
|
155
|
-
const
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
const auditData = JSON.parse(result.stdout);
|
|
165
|
-
const vulns = (auditData.metadata && auditData.metadata.vulnerabilities) || {};
|
|
166
|
-
const critical = vulns.critical || 0;
|
|
167
|
-
const high = vulns.high || 0;
|
|
168
|
-
const moderate = vulns.moderate || 0;
|
|
169
|
-
const low = vulns.low || 0;
|
|
170
|
-
|
|
171
|
-
if (critical + high > 0) {
|
|
172
|
-
fail(`npm audit: ${critical} critical, ${high} high, ${moderate} moderate, ${low} low`);
|
|
173
|
-
return false;
|
|
174
|
-
} else if (moderate + low > 0) {
|
|
175
|
-
warn(`npm audit: ${moderate} moderate, ${low} low vulnerabilities`);
|
|
176
|
-
return true;
|
|
177
|
-
} else {
|
|
178
|
-
ok("npm audit — no known vulnerabilities");
|
|
179
|
-
return true;
|
|
180
|
-
}
|
|
181
|
-
} catch {
|
|
182
|
-
if (result.status === 0) {
|
|
183
|
-
ok("npm audit — clean");
|
|
184
|
-
return true;
|
|
185
|
-
}
|
|
186
|
-
fail("npm audit returned errors");
|
|
127
|
+
const auditData = JSON.parse(result.stdout);
|
|
128
|
+
const vulns = (auditData.metadata && auditData.metadata.vulnerabilities) || {};
|
|
129
|
+
const critical = vulns.critical || 0;
|
|
130
|
+
const high = vulns.high || 0;
|
|
131
|
+
const moderate = vulns.moderate || 0;
|
|
132
|
+
const low = vulns.low || 0;
|
|
133
|
+
|
|
134
|
+
if (critical + high > 0) {
|
|
135
|
+
fail(`npm audit: ${critical} critical, ${high} high, ${moderate} moderate, ${low} low`);
|
|
187
136
|
return false;
|
|
137
|
+
} else if (moderate + low > 0) {
|
|
138
|
+
warn(`npm audit: ${moderate} moderate, ${low} low vulnerabilities`);
|
|
139
|
+
return true;
|
|
140
|
+
} else {
|
|
141
|
+
ok("npm audit — no known vulnerabilities");
|
|
142
|
+
return true;
|
|
188
143
|
}
|
|
189
144
|
} catch {
|
|
190
|
-
|
|
191
|
-
|
|
145
|
+
if (result.ok) {
|
|
146
|
+
ok("npm audit — clean");
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
fail("npm audit returned errors");
|
|
150
|
+
return false;
|
|
192
151
|
}
|
|
193
152
|
}
|
|
194
153
|
|
|
@@ -220,21 +179,23 @@ function main() {
|
|
|
220
179
|
process.exit(1);
|
|
221
180
|
}
|
|
222
181
|
|
|
223
|
-
console.log(
|
|
224
|
-
console.log(`Project: ${projectRoot}`);
|
|
182
|
+
console.log(banner('dependency_analyzer.js', { Project: projectRoot }));
|
|
225
183
|
|
|
226
|
-
const pkg =
|
|
184
|
+
const pkg = loadJson(path.join(projectRoot, 'package.json'));
|
|
227
185
|
if (!pkg) {
|
|
228
186
|
skip("No package.json found — dependency analysis requires a Node.js project");
|
|
229
187
|
process.exit(0);
|
|
230
188
|
}
|
|
231
189
|
|
|
232
190
|
let issues = 0;
|
|
191
|
+
const elapsed = timer();
|
|
233
192
|
const usedImports = extractImports(projectRoot);
|
|
234
|
-
|
|
193
|
+
const scanMs = elapsed();
|
|
194
|
+
|
|
195
|
+
console.log(`\n ${DIM}Found ${usedImports.size} unique external imports in ${formatMs(scanMs)}${RESET}`);
|
|
235
196
|
|
|
236
197
|
if (!checkUnusedFlag) {
|
|
237
|
-
|
|
198
|
+
console.log(sectionHeader('Phantom Imports (not in package.json)'));
|
|
238
199
|
const phantom = checkPhantom(pkg, usedImports);
|
|
239
200
|
if (phantom.length > 0) {
|
|
240
201
|
for (const p of phantom) fail(`'${p}' is imported but not in package.json — possible hallucination`);
|
|
@@ -244,7 +205,7 @@ function main() {
|
|
|
244
205
|
}
|
|
245
206
|
}
|
|
246
207
|
|
|
247
|
-
|
|
208
|
+
console.log(sectionHeader('Unused Dependencies'));
|
|
248
209
|
const unused = checkUnused(pkg, usedImports);
|
|
249
210
|
if (unused.length > 0) {
|
|
250
211
|
for (const u of unused) warn(`'${u}' is in package.json but never imported — may be unused`);
|
|
@@ -253,16 +214,17 @@ function main() {
|
|
|
253
214
|
}
|
|
254
215
|
|
|
255
216
|
if (auditFlag) {
|
|
256
|
-
|
|
217
|
+
console.log(sectionHeader('Vulnerability Audit'));
|
|
257
218
|
if (!runNpmAudit(projectRoot)) issues += 1;
|
|
258
219
|
}
|
|
259
220
|
|
|
260
|
-
console.log(`\n${BOLD}━━━ Dependency Analysis Summary ━━━${RESET}`);
|
|
221
|
+
console.log(`\n${BOLD}${CYAN}━━━ Dependency Analysis Summary ━━━${RESET}`);
|
|
261
222
|
if (issues === 0) {
|
|
262
223
|
ok("All dependency checks passed");
|
|
263
224
|
} else {
|
|
264
225
|
fail(`${issues} issue(s) found — review above`);
|
|
265
226
|
}
|
|
227
|
+
console.log();
|
|
266
228
|
|
|
267
229
|
process.exit(issues > 0 ? 1 : 0);
|
|
268
230
|
}
|