tribunal-kit 5.7.0 → 5.8.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 +6 -7
- package/.agent/agents/frontend-reviewer.md +13 -0
- package/.agent/agents/frontend-specialist.md +14 -0
- package/.agent/agents/logic-reviewer.md +11 -0
- package/.agent/agents/orchestrator.md +15 -0
- package/.agent/agents/security-auditor.md +13 -0
- package/.agent/agents/ui-ux-auditor.md +7 -31
- package/.agent/history/memory/.memory.idx +766 -0
- package/.agent/history/memory/MEMORY.md +62 -0
- package/.agent/routing_index.json +694 -714
- package/.agent/rules/GEMINI.md +58 -8
- package/.agent/scripts/_colors.js +131 -89
- package/.agent/scripts/_utils.js +163 -128
- package/.agent/scripts/auto_preview.js +207 -197
- package/.agent/scripts/bundle_analyzer.js +227 -192
- package/.agent/scripts/case_law_manager.js +991 -689
- package/.agent/scripts/checklist.js +233 -190
- package/.agent/scripts/context_broker.js +930 -605
- package/.agent/scripts/dependency_analyzer.js +275 -184
- package/.agent/scripts/graph_builder.js +412 -341
- package/.agent/scripts/graph_visualizer.js +392 -390
- package/.agent/scripts/graph_zoom.js +198 -156
- package/.agent/scripts/inner_loop_validator.js +523 -445
- package/.agent/scripts/lint_runner.js +199 -157
- package/.agent/scripts/marathon_harness.js +819 -661
- package/.agent/scripts/minify_context.js +115 -100
- package/.agent/scripts/mutation_runner.js +321 -280
- package/.agent/scripts/prompt_compiler.js +62 -42
- package/.agent/scripts/schema_validator.js +373 -280
- package/.agent/scripts/security_scan.js +333 -190
- package/.agent/scripts/session_manager.js +306 -270
- package/.agent/scripts/skill_evolution.js +810 -637
- package/.agent/scripts/skill_integrator.js +327 -307
- package/.agent/scripts/strengthen_skills.js +203 -193
- package/.agent/scripts/swarm_dispatcher.js +558 -457
- package/.agent/scripts/test_runner.js +178 -152
- package/.agent/scripts/verify_all.js +200 -168
- package/.agent/skills/fabel-protocol/SKILL.md +235 -0
- package/.agent/skills/thinking-protocol/SKILL.md +27 -0
- package/.agent/workflows/generate.md +1 -1
- package/.agent/workflows/tribunal-speed.md +1 -1
- package/README.md +53 -53
- package/bin/mcp-server.js +460 -175
- package/bin/tribunal-kit.js +1245 -987
- package/bin/wrapper.js +104 -74
- package/dist/cli.js +31 -0
- package/dist/commands/case.js +23 -0
- package/dist/commands/compile.js +84 -0
- package/dist/commands/init.js +42 -0
- package/dist/commands/learn.js +57 -0
- package/dist/commands/memory.js +456 -0
- package/package.json +2 -2
- package/scripts/benchmark.js +162 -125
- package/scripts/changelog.js +196 -168
- package/scripts/sync-version.js +94 -81
- package/scripts/validate-payload.js +85 -78
|
@@ -14,63 +14,75 @@
|
|
|
14
14
|
* node .agent/scripts/bundle_analyzer.js . --threshold 500
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
"use strict";
|
|
18
18
|
|
|
19
|
-
const fs = require(
|
|
20
|
-
const path = require(
|
|
19
|
+
const fs = require("fs");
|
|
20
|
+
const path = require("path");
|
|
21
21
|
|
|
22
22
|
const {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
YELLOW,
|
|
24
|
+
BOLD,
|
|
25
|
+
DIM,
|
|
26
|
+
CYAN,
|
|
27
|
+
RESET,
|
|
28
|
+
banner,
|
|
29
|
+
sectionHeader,
|
|
30
|
+
timer,
|
|
31
|
+
formatMs,
|
|
32
|
+
ok,
|
|
33
|
+
fail,
|
|
34
|
+
warn,
|
|
35
|
+
skip,
|
|
36
|
+
} = require("./_colors");
|
|
37
|
+
|
|
38
|
+
const { loadJson, runCommand } = require("./_utils");
|
|
29
39
|
|
|
30
40
|
const HEAVY_PACKAGES = {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
moment: "Use date-fns or dayjs instead (~2KB vs ~230KB)",
|
|
42
|
+
lodash: "Import specific functions: lodash/debounce instead of full lodash",
|
|
43
|
+
rxjs: "Import specific operators to enable tree-shaking",
|
|
44
|
+
"aws-sdk": "Use @aws-sdk/client-* v3 modular imports",
|
|
45
|
+
firebase: "Use modular imports: firebase/auth, firebase/firestore",
|
|
46
|
+
"chart.js": "Register only needed components",
|
|
47
|
+
three: "Import specific modules from three/examples/jsm/",
|
|
48
|
+
"@mui/material": "Ensure babel-plugin-import or modular imports",
|
|
49
|
+
"@mui/icons-material": "Import specific icons, never the barrel",
|
|
50
|
+
antd: "Use modular imports with babel-plugin-import",
|
|
41
51
|
};
|
|
42
52
|
|
|
43
53
|
function formatSize(sizeBytes) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
54
|
+
if (sizeBytes < 1024) return `${sizeBytes}B`;
|
|
55
|
+
if (sizeBytes < 1024 * 1024) return `${(sizeBytes / 1024).toFixed(1)}KB`;
|
|
56
|
+
return `${(sizeBytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
47
57
|
}
|
|
48
58
|
|
|
49
59
|
function detectBundler(projectRoot) {
|
|
50
|
-
|
|
51
|
-
|
|
60
|
+
const pkg = loadJson(path.join(projectRoot, "package.json"));
|
|
61
|
+
if (!pkg) return null;
|
|
52
62
|
|
|
53
|
-
|
|
63
|
+
const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
|
|
54
64
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
65
|
+
if (deps.vite) return "vite";
|
|
66
|
+
if (deps.next) return "next";
|
|
67
|
+
if (deps.webpack) return "webpack";
|
|
58
68
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
69
|
+
if (
|
|
70
|
+
fs.existsSync(path.join(projectRoot, "webpack.config.js")) ||
|
|
71
|
+
fs.existsSync(path.join(projectRoot, "webpack.config.ts"))
|
|
72
|
+
) {
|
|
73
|
+
return "webpack";
|
|
74
|
+
}
|
|
63
75
|
|
|
64
|
-
|
|
76
|
+
return null;
|
|
65
77
|
}
|
|
66
78
|
|
|
67
79
|
function findDistDir(projectRoot) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
80
|
+
const candidates = ["dist", "build", ".next", "out", "public/build"];
|
|
81
|
+
for (const c of candidates) {
|
|
82
|
+
const d = path.join(projectRoot, c);
|
|
83
|
+
if (fs.existsSync(d) && fs.statSync(d).isDirectory()) return d;
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
74
86
|
}
|
|
75
87
|
|
|
76
88
|
/**
|
|
@@ -78,184 +90,207 @@ function findDistDir(projectRoot) {
|
|
|
78
90
|
* because it needs to collect file sizes and totals in one pass.
|
|
79
91
|
*/
|
|
80
92
|
function analyzeDist(distDir) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
_walk(fpath);
|
|
91
|
-
} else {
|
|
92
|
-
const size = fs.statSync(fpath).size;
|
|
93
|
-
total += size;
|
|
94
|
-
files.push([path.relative(distDir, fpath), size]);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
93
|
+
const files = [];
|
|
94
|
+
let total = 0;
|
|
95
|
+
|
|
96
|
+
function _walk(dir) {
|
|
97
|
+
let items;
|
|
98
|
+
try {
|
|
99
|
+
items = fs.readdirSync(dir, { withFileTypes: true });
|
|
100
|
+
} catch {
|
|
101
|
+
return;
|
|
97
102
|
}
|
|
103
|
+
for (const item of items) {
|
|
104
|
+
const fpath = path.join(dir, item.name);
|
|
105
|
+
if (item.isDirectory()) {
|
|
106
|
+
_walk(fpath);
|
|
107
|
+
} else {
|
|
108
|
+
const size = fs.statSync(fpath).size;
|
|
109
|
+
total += size;
|
|
110
|
+
files.push([path.relative(distDir, fpath), size]);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
98
114
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
115
|
+
_walk(distDir);
|
|
116
|
+
files.sort((a, b) => b[1] - a[1]);
|
|
117
|
+
return { total, files };
|
|
102
118
|
}
|
|
103
119
|
|
|
104
120
|
function checkHeavyDependencies(projectRoot) {
|
|
105
|
-
|
|
106
|
-
|
|
121
|
+
const pkg = loadJson(path.join(projectRoot, "package.json"));
|
|
122
|
+
if (!pkg) return [];
|
|
107
123
|
|
|
108
|
-
|
|
109
|
-
|
|
124
|
+
const deps = Object.keys(pkg.dependencies || {});
|
|
125
|
+
const found = [];
|
|
110
126
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
127
|
+
for (const [pkgName, suggestion] of Object.entries(HEAVY_PACKAGES)) {
|
|
128
|
+
if (deps.includes(pkgName)) {
|
|
129
|
+
found.push([pkgName, suggestion]);
|
|
115
130
|
}
|
|
116
|
-
|
|
131
|
+
}
|
|
132
|
+
return found;
|
|
117
133
|
}
|
|
118
134
|
|
|
119
135
|
function runBuild(projectRoot) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
+
const pkg = loadJson(path.join(projectRoot, "package.json"));
|
|
137
|
+
if (pkg && (!pkg.scripts || !pkg.scripts.build)) {
|
|
138
|
+
skip("No 'build' script found in package.json");
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const elapsed = timer();
|
|
143
|
+
const result = runCommand("npm", ["run", "build"], {
|
|
144
|
+
cwd: projectRoot,
|
|
145
|
+
timeout: 300000,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const ms = elapsed();
|
|
149
|
+
if (result.ok) {
|
|
150
|
+
ok(`Build completed successfully ${DIM}(${formatMs(ms)})${RESET}`);
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
fail(`Build failed ${DIM}(${formatMs(ms)})${RESET}`);
|
|
155
|
+
const output = (result.stdout + "\n" + result.stderr).trim();
|
|
156
|
+
if (output) {
|
|
157
|
+
for (const line of output.split("\n").slice(0, 10)) {
|
|
158
|
+
console.log(` ${line}`);
|
|
136
159
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const output = (result.stdout + "\n" + result.stderr).trim();
|
|
140
|
-
if (output) {
|
|
141
|
-
for (const line of output.split("\n").slice(0, 10)) {
|
|
142
|
-
console.log(` ${line}`);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
return false;
|
|
160
|
+
}
|
|
161
|
+
return false;
|
|
146
162
|
}
|
|
147
163
|
|
|
148
164
|
function main() {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
165
|
+
const args = process.argv.slice(2);
|
|
166
|
+
|
|
167
|
+
let targetPath = null;
|
|
168
|
+
let buildFlag = false;
|
|
169
|
+
let threshold = 250;
|
|
170
|
+
|
|
171
|
+
for (let i = 0; i < args.length; i++) {
|
|
172
|
+
if (args[i] === "--build") buildFlag = true;
|
|
173
|
+
else if (args[i] === "--threshold" && i + 1 < args.length) {
|
|
174
|
+
threshold = parseInt(args[++i], 10);
|
|
175
|
+
} else if (args[i] === "-h" || args[i] === "--help") {
|
|
176
|
+
console.log(
|
|
177
|
+
"Usage: node bundle_analyzer.js <path> [--build] [--threshold <kb>]",
|
|
178
|
+
);
|
|
179
|
+
process.exit(0);
|
|
180
|
+
} else if (args[i].startsWith("-")) {
|
|
181
|
+
console.log(
|
|
182
|
+
"Usage: node bundle_analyzer.js <path> [--build] [--threshold <kb>]",
|
|
183
|
+
);
|
|
184
|
+
process.exit(1);
|
|
185
|
+
} else if (!targetPath) {
|
|
186
|
+
targetPath = args[i];
|
|
168
187
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (!targetPath) {
|
|
191
|
+
console.log(
|
|
192
|
+
"Usage: node bundle_analyzer.js <path> [--build] [--threshold <kb>]",
|
|
193
|
+
);
|
|
194
|
+
process.exit(1);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const projectRoot = path.resolve(targetPath);
|
|
198
|
+
if (!fs.existsSync(projectRoot) || !fs.statSync(projectRoot).isDirectory()) {
|
|
199
|
+
fail(`Directory not found: ${projectRoot}`);
|
|
200
|
+
process.exit(1);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const bundler = detectBundler(projectRoot);
|
|
204
|
+
console.log(
|
|
205
|
+
banner("bundle_analyzer.js", {
|
|
206
|
+
Project: projectRoot,
|
|
207
|
+
Bundler: bundler || "auto-detect",
|
|
208
|
+
Threshold: `${threshold}KB`,
|
|
209
|
+
}),
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
if (buildFlag) {
|
|
213
|
+
console.log(sectionHeader("Building Project"));
|
|
214
|
+
if (!runBuild(projectRoot)) {
|
|
215
|
+
process.exit(1);
|
|
173
216
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const distDir = findDistDir(projectRoot);
|
|
220
|
+
const heavy = checkHeavyDependencies(projectRoot);
|
|
221
|
+
|
|
222
|
+
// PERFORMANCE FIX: Cache analyzeDist result — was called TWICE before
|
|
223
|
+
let distResult = null;
|
|
224
|
+
|
|
225
|
+
if (!distDir) {
|
|
226
|
+
skip("No build output directory found (dist/, build/, .next/, out/)");
|
|
227
|
+
skip("Run with --build to create a build first, or build manually");
|
|
228
|
+
} else {
|
|
229
|
+
console.log(
|
|
230
|
+
sectionHeader(
|
|
231
|
+
`Bundle Size Analysis (${path.relative(projectRoot, distDir)}/)`,
|
|
232
|
+
),
|
|
233
|
+
);
|
|
234
|
+
distResult = analyzeDist(distDir);
|
|
235
|
+
console.log(
|
|
236
|
+
`\n Total bundle size: ${BOLD}${formatSize(distResult.total)}${RESET}`,
|
|
237
|
+
);
|
|
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 distResult.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
|
+
}
|
|
179
250
|
}
|
|
180
251
|
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
console.log(sectionHeader('Building Project'));
|
|
190
|
-
if (!runBuild(projectRoot)) {
|
|
191
|
-
process.exit(1);
|
|
192
|
-
}
|
|
252
|
+
const largeJs = distResult.files.filter(
|
|
253
|
+
([f, s]) =>
|
|
254
|
+
(f.endsWith(".js") || f.endsWith(".mjs")) && s > thresholdBytes,
|
|
255
|
+
);
|
|
256
|
+
if (largeJs.length > 0) {
|
|
257
|
+
console.log(
|
|
258
|
+
`\n ${YELLOW}${largeJs.length} JS file(s) exceed ${threshold}KB threshold${RESET}`,
|
|
259
|
+
);
|
|
193
260
|
}
|
|
261
|
+
}
|
|
194
262
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
if (!distDir) {
|
|
202
|
-
skip("No build output directory found (dist/, build/, .next/, out/)");
|
|
203
|
-
skip("Run with --build to create a build first, or build manually");
|
|
204
|
-
} else {
|
|
205
|
-
console.log(sectionHeader(`Bundle Size Analysis (${path.relative(projectRoot, distDir)}/)`));
|
|
206
|
-
distResult = analyzeDist(distDir);
|
|
207
|
-
console.log(`\n Total bundle size: ${BOLD}${formatSize(distResult.total)}${RESET}`);
|
|
208
|
-
|
|
209
|
-
const thresholdBytes = threshold * 1024;
|
|
210
|
-
console.log(`\n ${BOLD}Top files by size:${RESET}`);
|
|
211
|
-
let count = 0;
|
|
212
|
-
for (const [filepath, size] of distResult.files) {
|
|
213
|
-
if (count++ >= 10) break;
|
|
214
|
-
const sizeStr = formatSize(size).padStart(10, ' ');
|
|
215
|
-
if (size > thresholdBytes) {
|
|
216
|
-
warn(`${sizeStr} ${filepath}`);
|
|
217
|
-
} else {
|
|
218
|
-
console.log(` ${sizeStr} ${filepath}`);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
const largeJs = distResult.files.filter(([f, s]) => (f.endsWith('.js') || f.endsWith('.mjs')) && s > thresholdBytes);
|
|
223
|
-
if (largeJs.length > 0) {
|
|
224
|
-
console.log(`\n ${YELLOW}${largeJs.length} JS file(s) exceed ${threshold}KB threshold${RESET}`);
|
|
225
|
-
}
|
|
263
|
+
console.log(sectionHeader("Dependency Weight Check"));
|
|
264
|
+
if (heavy.length > 0) {
|
|
265
|
+
for (const [pkgName, suggestion] of heavy) {
|
|
266
|
+
warn(`'${pkgName}' is a heavy dependency`);
|
|
267
|
+
console.log(` → ${suggestion}`);
|
|
226
268
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
269
|
+
} else {
|
|
270
|
+
ok("No known-heavy packages detected");
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// ━━━ Summary ━━━ (reuses cached distResult instead of re-scanning)
|
|
274
|
+
console.log(`\n${BOLD}${CYAN}━━━ Bundle Analysis Summary ━━━${RESET}`);
|
|
275
|
+
if (distResult) {
|
|
276
|
+
const sizeStr = formatSize(distResult.total);
|
|
277
|
+
if (distResult.total > 5 * 1024 * 1024) {
|
|
278
|
+
fail(`Total bundle: ${sizeStr} — consider code splitting`);
|
|
279
|
+
} else if (distResult.total > 2 * 1024 * 1024) {
|
|
280
|
+
warn(`Total bundle: ${sizeStr} — review for optimization opportunities`);
|
|
234
281
|
} else {
|
|
235
|
-
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// ━━━ Summary ━━━ (reuses cached distResult instead of re-scanning)
|
|
239
|
-
console.log(`\n${BOLD}${CYAN}━━━ Bundle Analysis Summary ━━━${RESET}`);
|
|
240
|
-
if (distResult) {
|
|
241
|
-
const sizeStr = formatSize(distResult.total);
|
|
242
|
-
if (distResult.total > 5 * 1024 * 1024) {
|
|
243
|
-
fail(`Total bundle: ${sizeStr} — consider code splitting`);
|
|
244
|
-
} else if (distResult.total > 2 * 1024 * 1024) {
|
|
245
|
-
warn(`Total bundle: ${sizeStr} — review for optimization opportunities`);
|
|
246
|
-
} else {
|
|
247
|
-
ok(`Total bundle: ${sizeStr}`);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
if (heavy.length > 0) {
|
|
252
|
-
warn(`${heavy.length} heavy dependency suggestion(s) — see above`);
|
|
253
|
-
} else if (distResult && heavy.length === 0) {
|
|
254
|
-
ok("No optimization suggestions");
|
|
282
|
+
ok(`Total bundle: ${sizeStr}`);
|
|
255
283
|
}
|
|
256
|
-
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (heavy.length > 0) {
|
|
287
|
+
warn(`${heavy.length} heavy dependency suggestion(s) — see above`);
|
|
288
|
+
} else if (distResult && heavy.length === 0) {
|
|
289
|
+
ok("No optimization suggestions");
|
|
290
|
+
}
|
|
291
|
+
console.log();
|
|
257
292
|
}
|
|
258
293
|
|
|
259
294
|
if (require.main === module) {
|
|
260
|
-
|
|
295
|
+
main();
|
|
261
296
|
}
|