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.
Files changed (90) hide show
  1. package/.agent/agents/api-architect.md +66 -66
  2. package/.agent/agents/db-latency-auditor.md +216 -216
  3. package/.agent/agents/precedence-reviewer.md +250 -250
  4. package/.agent/agents/resilience-reviewer.md +88 -88
  5. package/.agent/agents/schema-reviewer.md +67 -67
  6. package/.agent/agents/throughput-optimizer.md +299 -299
  7. package/.agent/agents/ui-ux-auditor.md +292 -292
  8. package/.agent/agents/vitals-reviewer.md +223 -223
  9. package/.agent/history/architecture-graph.yaml +32 -1
  10. package/.agent/history/graph-cache.json +66 -19
  11. package/.agent/history/snapshots/bin__tribunal-kit.js.json +19 -0
  12. package/.agent/history/snapshots/eslint.config.js.json +9 -0
  13. package/.agent/history/snapshots/migrate_refs.js.json +3 -3
  14. package/.agent/history/snapshots/scripts__changelog.js.json +2 -1
  15. package/.agent/history/snapshots/scripts__sync-version.js.json +2 -1
  16. package/.agent/history/snapshots/scripts__validate-payload.js.json +1 -0
  17. package/.agent/history/snapshots/test__integration__bridges.test.js.json +2 -1
  18. package/.agent/history/snapshots/test__integration__init.test.js.json +1 -0
  19. package/.agent/history/snapshots/test__integration__routing.test.js.json +1 -0
  20. package/.agent/history/snapshots/test__integration__swarm_dispatcher.test.js.json +2 -1
  21. package/.agent/history/snapshots/test__integration__wave2.test.js.json +2 -1
  22. package/.agent/history/snapshots/test__unit__args.test.js.json +11 -1
  23. package/.agent/history/snapshots/test__unit__case_law_manager.test.js.json +1 -0
  24. package/.agent/history/snapshots/test__unit__context_broker.test.js.json +11 -0
  25. package/.agent/history/snapshots/test__unit__copyDir.test.js.json +11 -1
  26. package/.agent/history/snapshots/test__unit__graph_tools.test.js.json +1 -0
  27. package/.agent/history/snapshots/test__unit__inner_loop_validator.test.js.json +11 -0
  28. package/.agent/history/snapshots/test__unit__selfInstall.test.js.json +11 -1
  29. package/.agent/history/snapshots/test__unit__semver.test.js.json +11 -1
  30. package/.agent/history/snapshots/test__unit__swarm_dispatcher.test.js.json +1 -0
  31. package/.agent/scripts/_colors.js +154 -2
  32. package/.agent/scripts/_utils.js +205 -3
  33. package/.agent/scripts/append_flow.js +72 -72
  34. package/.agent/scripts/auto_preview.js +197 -197
  35. package/.agent/scripts/bundle_analyzer.js +90 -119
  36. package/.agent/scripts/case_law_manager.js +18 -13
  37. package/.agent/scripts/checklist.js +100 -88
  38. package/.agent/scripts/colors.js +7 -13
  39. package/.agent/scripts/compress_skills.js +141 -141
  40. package/.agent/scripts/consolidate_skills.js +149 -149
  41. package/.agent/scripts/context_broker.js +605 -609
  42. package/.agent/scripts/deep_compress.js +150 -150
  43. package/.agent/scripts/dependency_analyzer.js +68 -106
  44. package/.agent/scripts/graph_builder.js +341 -311
  45. package/.agent/scripts/graph_visualizer.js +390 -384
  46. package/.agent/scripts/graph_zoom.js +6 -4
  47. package/.agent/scripts/inner_loop_validator.js +445 -465
  48. package/.agent/scripts/lint_runner.js +27 -28
  49. package/.agent/scripts/minify_context.js +100 -100
  50. package/.agent/scripts/mutation_runner.js +280 -280
  51. package/.agent/scripts/patch_skills_meta.js +156 -156
  52. package/.agent/scripts/patch_skills_output.js +244 -244
  53. package/.agent/scripts/schema_validator.js +280 -297
  54. package/.agent/scripts/security_scan.js +37 -64
  55. package/.agent/scripts/session_manager.js +270 -276
  56. package/.agent/scripts/skill_evolution.js +637 -644
  57. package/.agent/scripts/skill_integrator.js +307 -313
  58. package/.agent/scripts/strengthen_skills.js +193 -193
  59. package/.agent/scripts/strip_tribunal.js +47 -47
  60. package/.agent/scripts/swarm_dispatcher.js +360 -360
  61. package/.agent/scripts/test_runner.js +32 -39
  62. package/.agent/scripts/utils.js +10 -25
  63. package/.agent/scripts/verify_all.js +84 -92
  64. package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +1 -1
  65. package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +1 -1
  66. package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +1 -1
  67. package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +1 -1
  68. package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +1 -1
  69. package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +1 -1
  70. package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +1 -1
  71. package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +1 -1
  72. package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +1 -1
  73. package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +1 -1
  74. package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +1 -1
  75. package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +1 -1
  76. package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +1 -1
  77. package/.agent/skills/doc.md +1 -1
  78. package/.agent/skills/knowledge-graph/SKILL.md +52 -52
  79. package/.agent/skills/ui-ux-pro-max/SKILL.md +562 -562
  80. package/.agent/workflows/generate.md +183 -183
  81. package/.agent/workflows/tribunal-speed.md +183 -183
  82. package/README.md +1 -1
  83. package/bin/tribunal-kit.js +76 -87
  84. package/package.json +6 -3
  85. package/scripts/changelog.js +167 -167
  86. package/scripts/sync-version.js +81 -81
  87. package/.agent/history/architecture-explorer.html +0 -352
  88. package/.agent/scripts/__pycache__/_colors.cpython-311.pyc +0 -0
  89. package/.agent/scripts/__pycache__/_utils.cpython-311.pyc +0 -0
  90. package/.agent/scripts/__pycache__/case_law_manager.cpython-311.pyc +0 -0
@@ -23,29 +23,34 @@ const fs = require('fs');
23
23
  const path = require('path');
24
24
  const { execFileSync } = require('child_process');
25
25
 
26
- // ━━━ ANSI color output ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
27
- const RED = '\x1b[91m';
28
- const GREEN = '\x1b[92m';
29
- const YELLOW = '\x1b[93m';
30
- const BLUE = '\x1b[94m';
31
- const BOLD = '\x1b[1m';
32
- const RESET = '\x1b[0m';
26
+ const {
27
+ RED, GREEN, YELLOW, BLUE, BOLD, DIM, CYAN, RESET,
28
+ banner, sectionHeader, summaryTable, timer, formatMs,
29
+ ok, fail, skip,
30
+ } = require('./_colors');
33
31
 
32
+ const { walkDir } = require('./_utils');
34
33
 
35
- function printHeader(title) {
36
- console.log(`\n${BOLD}${BLUE}━━━ ${title} ━━━${RESET}`);
37
- }
34
+ // ── Results Tracking ────────────────────────────────────────────────────────
35
+
36
+ const RESULTS = [];
38
37
 
39
- function printOk(msg) {
40
- console.log(` ${GREEN}${msg}${RESET}`);
38
+ function trackOk(label, ms) {
39
+ const timing = ms != null ? `${DIM}(${formatMs(ms)})${RESET}` : '';
40
+ console.log(` ${GREEN}✅ ${label}${RESET} ${timing}`);
41
+ RESULTS.push({ name: label, status: 'pass', ms });
41
42
  }
42
43
 
43
- function printFail(msg) {
44
- console.log(` ${RED}${msg}${RESET}`);
44
+ function trackFail(label, ms, note) {
45
+ const timing = ms != null ? `${DIM}(${formatMs(ms)})${RESET}` : '';
46
+ console.log(` ${RED}❌ ${label}${RESET} ${timing}`);
47
+ if (note) console.log(` ${note.slice(0, 500)}`);
48
+ RESULTS.push({ name: label, status: 'fail', ms });
45
49
  }
46
50
 
47
- function printSkip(msg) {
48
- console.log(` ${YELLOW}⏭️ Skipped: ${msg}${RESET}`);
51
+ function trackSkip(label, reason) {
52
+ console.log(` ${YELLOW}⏭️ ${label} ${reason}${RESET}`);
53
+ RESULTS.push({ name: label, status: 'skip' });
49
54
  }
50
55
 
51
56
 
@@ -57,29 +62,29 @@ function printSkip(msg) {
57
62
  * @returns {boolean}
58
63
  */
59
64
  function runCheck(label, cmd, cwd) {
65
+ const elapsed = timer();
60
66
  try {
61
67
  execFileSync(cmd[0], cmd.slice(1), {
62
68
  cwd,
63
69
  stdio: 'pipe',
64
70
  timeout: 60000,
65
71
  encoding: 'utf8',
72
+ shell: process.platform === 'win32',
66
73
  });
67
- printOk(`${label} passed`);
74
+ trackOk(`${label} passed`, elapsed());
68
75
  return true;
69
76
  } catch (err) {
77
+ const ms = elapsed();
70
78
  if (err.code === 'ENOENT') {
71
- printSkip(`${label} command not found (tool not installed)`);
79
+ trackSkip(label, 'command not found (tool not installed)');
72
80
  return true; // Don't block on tools that aren't installed
73
81
  }
74
82
  if (err.killed) {
75
- printFail(`${label} timed out after 60s`);
83
+ trackFail(label, ms, 'timed out after 60s');
76
84
  return false;
77
85
  }
78
- printFail(`${label} failed`);
79
- const output = (err.stdout || '') + (err.stderr || '');
80
- if (output.trim()) {
81
- console.log(` ${output.trim().slice(0, 500)}`);
82
- }
86
+ const output = ((err.stdout || '') + (err.stderr || '')).trim();
87
+ trackFail(`${label} failed`, ms, output || 'non-zero exit code');
83
88
  return false;
84
89
  }
85
90
  }
@@ -87,53 +92,45 @@ function runCheck(label, cmd, cwd) {
87
92
 
88
93
  /**
89
94
  * Scan for hardcoded secrets in source files.
95
+ * Uses shared walkDir from _utils.js.
90
96
  * @param {string} projectRoot - Project root directory.
91
97
  * @returns {boolean} True if no secrets found.
92
98
  */
93
99
  function checkSecrets(projectRoot) {
94
- printHeader('Security Secret Scan');
100
+ const elapsed = timer();
95
101
  const dangerousPatterns = [
96
102
  'password=', 'secret=', 'api_key=',
97
103
  'apikey=', 'auth_token=', 'private_key=',
98
104
  ];
99
105
  let foundIssues = false;
100
- const sourceExtensions = new Set(['.ts', '.tsx', '.js', '.jsx', '.py', '.env']);
101
- const skipDirs = new Set(['node_modules', '.git', '.agent', 'dist', '__pycache__']);
102
-
103
- function walk(dir) {
104
- let entries;
105
- try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
106
-
107
- for (const entry of entries) {
108
- const fullPath = path.join(dir, entry.name);
109
- if (entry.isDirectory()) {
110
- if (!skipDirs.has(entry.name)) walk(fullPath);
111
- } else if (entry.isFile()) {
112
- const ext = path.extname(entry.name);
113
- if (!sourceExtensions.has(ext)) continue;
114
- if (entry.name.startsWith('.env')) continue; // .env files are allowed
115
-
116
- let content;
117
- try { content = fs.readFileSync(fullPath, 'utf8'); } catch { continue; }
118
-
119
- const lines = content.split('\n');
120
- for (let i = 0; i < lines.length; i++) {
121
- const lineLower = lines[i].toLowerCase().trim();
122
- const hasPattern = dangerousPatterns.some(p => lineLower.includes(p));
123
- if (hasPattern && lineLower.includes('=') && !lineLower.startsWith('#')) {
124
- const rel = path.relative(projectRoot, fullPath);
125
- printFail(`Possible secret: ${rel}:${i + 1} → ${lines[i].trim().slice(0, 80)}`);
126
- foundIssues = true;
127
- }
128
- }
106
+ const sourceExtensions = new Set(['.ts', '.tsx', '.js', '.jsx', '.py']);
107
+
108
+ const files = walkDir(projectRoot, { extensions: sourceExtensions });
109
+
110
+ for (const fullPath of files) {
111
+ // Skip .env files they are allowed to contain secrets
112
+ if (path.basename(fullPath).startsWith('.env')) continue;
113
+
114
+ let content;
115
+ try { content = fs.readFileSync(fullPath, 'utf8'); } catch { continue; }
116
+
117
+ const lines = content.split('\n');
118
+ for (let i = 0; i < lines.length; i++) {
119
+ const lineLower = lines[i].toLowerCase().trim();
120
+ const hasPattern = dangerousPatterns.some(p => lineLower.includes(p));
121
+ if (hasPattern && lineLower.includes('=') && !lineLower.startsWith('#')) {
122
+ const rel = path.relative(projectRoot, fullPath);
123
+ fail(`Possible secret: ${rel}:${i + 1} → ${lines[i].trim().slice(0, 80)}`);
124
+ foundIssues = true;
129
125
  }
130
126
  }
131
127
  }
132
128
 
133
- walk(projectRoot);
134
-
129
+ const ms = elapsed();
135
130
  if (!foundIssues) {
136
- printOk('No hardcoded secrets detected');
131
+ trackOk(`Secret scan ${files.length} files clean`, ms);
132
+ } else {
133
+ trackFail('Secret scan — hardcoded credentials detected', ms);
137
134
  }
138
135
  return !foundIssues;
139
136
  }
@@ -143,76 +140,90 @@ function checkSecrets(projectRoot) {
143
140
  * Run all checklist tiers. Returns number of failures.
144
141
  * @param {string} projectRoot - Project root.
145
142
  * @param {string|null} url - URL for Lighthouse/E2E checks.
146
- * @param {string[]} skip - Tiers to skip.
143
+ * @param {string[]} skipTiers - Tiers to skip.
147
144
  * @returns {number}
148
145
  */
149
- function runAll(projectRoot, url, skip) {
146
+ function runAll(projectRoot, url, skipTiers) {
150
147
  let failures = 0;
148
+ RESULTS.length = 0;
149
+ const totalTimer = timer();
151
150
 
152
151
  // Priority 1 — Security
153
- if (!skip.includes('security')) {
154
- printHeader('Priority 1 Security');
152
+ if (!skipTiers.includes('security')) {
153
+ console.log(sectionHeader('SecuritySecret Scan', 1));
155
154
  if (!checkSecrets(projectRoot)) failures++;
156
155
  } else {
157
- printSkip('Security tier');
156
+ trackSkip('Security', 'skipped by flag');
158
157
  }
159
158
 
160
159
  // Priority 2 — Lint
161
- if (!skip.includes('lint')) {
162
- printHeader('Priority 2 — Lint');
160
+ if (!skipTiers.includes('lint')) {
161
+ console.log(sectionHeader('Lint', 2));
163
162
  if (!runCheck('ESLint', ['npx', 'eslint', '.', '--max-warnings=0'], projectRoot)) failures++;
164
163
  if (!runCheck('TypeScript', ['npx', 'tsc', '--noEmit'], projectRoot)) failures++;
165
164
  } else {
166
- printSkip('Lint tier');
165
+ trackSkip('Lint', 'skipped by flag');
167
166
  }
168
167
 
169
168
  // Priority 3 — Schema
170
- if (!skip.includes('schema')) {
171
- printHeader('Priority 3 — Schema');
172
- printSkip('Schema check — run manually if you have DB migrations');
169
+ if (!skipTiers.includes('schema')) {
170
+ console.log(sectionHeader('Schema Validation', 3));
171
+ trackSkip('Schema', 'run manually if you have DB migrations');
173
172
  } else {
174
- printSkip('Schema tier');
173
+ trackSkip('Schema', 'skipped by flag');
175
174
  }
176
175
 
177
176
  // Priority 4 — Tests
178
- if (!skip.includes('tests')) {
179
- printHeader('Priority 4 — Tests');
177
+ if (!skipTiers.includes('tests')) {
178
+ console.log(sectionHeader('Tests', 4));
180
179
  if (!runCheck('Test suite', ['npm', 'test', '--', '--passWithNoTests'], projectRoot)) failures++;
181
180
  } else {
182
- printSkip('Tests tier');
181
+ trackSkip('Tests', 'skipped by flag');
183
182
  }
184
183
 
185
184
  // Priority 5 — UX
186
- if (!skip.includes('ux')) {
187
- printHeader('Priority 5 — UX / Accessibility');
188
- printSkip('UX audit run /preview start then check manually or with Lighthouse');
185
+ if (!skipTiers.includes('ux')) {
186
+ console.log(sectionHeader('UX / Accessibility', 5));
187
+ trackSkip('UX audit', 'run /preview start then check with Lighthouse');
189
188
  } else {
190
- printSkip('UX tier');
189
+ trackSkip('UX', 'skipped by flag');
191
190
  }
192
191
 
193
192
  // Priority 6 — SEO
194
- if (!skip.includes('seo')) {
195
- printHeader('Priority 6 — SEO');
196
- printSkip('SEO check use /ui-ux-pro-max for SEO-sensitive pages');
193
+ if (!skipTiers.includes('seo')) {
194
+ console.log(sectionHeader('SEO', 6));
195
+ trackSkip('SEO check', 'use /ui-ux-pro-max for SEO-sensitive pages');
197
196
  } else {
198
- printSkip('SEO tier');
197
+ trackSkip('SEO', 'skipped by flag');
199
198
  }
200
199
 
201
200
  // Priority 7 — Lighthouse / E2E
202
- if (url && !skip.includes('e2e')) {
203
- printHeader('Priority 7 — Lighthouse / E2E');
201
+ if (url && !skipTiers.includes('e2e')) {
202
+ console.log(sectionHeader('Lighthouse / E2E', 7));
204
203
  if (!runCheck('Playwright E2E', ['npx', 'playwright', 'test'], projectRoot)) failures++;
205
204
  } else if (!url) {
206
- printSkip('E2E / Lighthouse pass --url to enable');
205
+ trackSkip('E2E / Lighthouse', 'pass --url to enable');
207
206
  }
208
207
 
209
208
  // ━━━ Summary ━━━
210
- console.log(`\n${BOLD}━━━ Checklist Summary ━━━${RESET}`);
209
+ const totalMs = totalTimer();
210
+ console.log(`\n${BOLD}${CYAN}━━━ Checklist Summary ━━━${RESET}`);
211
+ summaryTable(RESULTS);
212
+
213
+ const passCount = RESULTS.filter(r => r.status === 'pass').length;
214
+ const failCount = RESULTS.filter(r => r.status === 'fail').length;
215
+ const skipCount = RESULTS.filter(r => r.status === 'skip').length;
216
+
217
+ console.log(`\n ${DIM}Total: ${RESULTS.length} checks in ${formatMs(totalMs)}${RESET}`);
218
+ console.log(` ${GREEN}${passCount} passed${RESET} ${failCount > 0 ? `${RED}${failCount} failed${RESET} ` : ''}${skipCount > 0 ? `${YELLOW}${skipCount} skipped${RESET}` : ''}`);
219
+
220
+ console.log();
211
221
  if (failures === 0) {
212
- printOk('All checks passed — ready to proceed');
222
+ console.log(`${GREEN}${BOLD} ✔ All checks passed — ready to proceed.${RESET}`);
213
223
  } else {
214
- printFail(`${failures} tier(s) failed — fix Critical issues before proceeding`);
224
+ console.log(`${RED}${BOLD} ✖ ${failures} tier(s) failed — fix critical issues before proceeding.${RESET}`);
215
225
  }
226
+ console.log();
216
227
 
217
228
  return failures;
218
229
  }
@@ -248,11 +259,12 @@ function main() {
248
259
 
249
260
  const projectRoot = path.resolve(args.path);
250
261
  if (!fs.existsSync(projectRoot) || !fs.statSync(projectRoot).isDirectory()) {
251
- printFail(`Directory not found: ${projectRoot}`);
262
+ fail(`Directory not found: ${projectRoot}`);
252
263
  process.exit(1);
253
264
  }
254
265
 
255
- console.log(`${BOLD}Tribunal Checklist ${projectRoot}${RESET}`);
266
+ console.log(banner('checklist.js', { Project: projectRoot }));
267
+
256
268
  const failures = runAll(projectRoot, args.url, args.skip);
257
269
  process.exit(failures > 0 ? 1 : 0);
258
270
  }
@@ -1,17 +1,11 @@
1
1
  /**
2
- * colors.js
3
- * Shared ANSI color constants for Tribunal Kit Node scripts.
2
+ * colors.js — Backward-compatible re-export of _colors.js
3
+ * ════════════════════════════════════════════════════════
4
+ * All color constants and helpers are defined in _colors.js.
5
+ * This file re-exports them for scripts that import from './colors.js'.
6
+ *
7
+ * New scripts should import from './_colors' directly.
4
8
  */
5
9
  'use strict';
6
10
 
7
- module.exports = {
8
- GREEN: "\x1b[92m",
9
- YELLOW: "\x1b[93m",
10
- CYAN: "\x1b[96m",
11
- RED: "\x1b[91m",
12
- BLUE: "\x1b[94m",
13
- MAGENTA: "\x1b[95m",
14
- BOLD: "\x1b[1m",
15
- DIM: "\x1b[2m",
16
- RESET: "\x1b[0m"
17
- };
11
+ module.exports = require('./_colors');
@@ -1,141 +1,141 @@
1
- #!/usr/bin/env node
2
- // compress_skills.js - Aggressive token reduction for .agent/skills/**/*.md files
3
-
4
- 'use strict';
5
-
6
- const fs = require('fs');
7
- const path = require('path');
8
-
9
- const SECTION_PATTERNS = [
10
- /^## 🏛️ Tribunal Integration[\s\S]*?(?=\n## |\n# |$)/gm,
11
- /^## Tribunal Integration[\s\S]*?(?=\n## |\n# |$)/gm,
12
- /^### ✅ Pre-Flight Self-Audit[\s\S]*?(?=\n## |\n### |\n# |$)/gm,
13
- /^## Pre-Flight Self-Audit[\s\S]*?(?=\n## |\n# |$)/gm,
14
- /^## Cross-Workflow Navigation[\s\S]*?(?=\n## |\n# |$)/gm,
15
- /^## Output Format\s*\n```[\s\S]*?```\s*\n/gm,
16
- /^## VBC Protocol[\s\S]*?(?=\n## |\n# |$)/gm,
17
- /^## LLM Traps[\s\S]*?(?=\n## |\n# |$)/gm,
18
- ];
19
-
20
- const CHATTY_INTRO = /(^# .+\n)\n[A-Z][^#\n]{60,}\n[A-Z][^#\n]{40,}\n\n---/gm;
21
-
22
- function stripChattyIntro(content) {
23
- return content.replace(CHATTY_INTRO, '$1\n---');
24
- }
25
-
26
- const OBVIOUS_COMMENT = /^(\s*)(\/\/ (default for most properties|shorthand|number of repeats|default: \d+|spring tension|resistance|weight|approximate duration|deceleration rate)[^\n]*)\n/gm;
27
-
28
- function stripObviousComments(content) {
29
- return content.replace(OBVIOUS_COMMENT, '');
30
- }
31
-
32
- const PERF_TEXT_BLOCK = /^```\n(✅ Use \w[^\n]*\n → [^\n]*\n\n?){3,}```\n/gm;
33
-
34
- function compressPerfBlocks(content) {
35
- return content.replace(PERF_TEXT_BLOCK, (match) => {
36
- const lines = match.replace(/`|\n$/g, '').split('\n');
37
- const bullets = [];
38
- for (const line of lines) {
39
- const stripped = line.trim();
40
- if (stripped.startsWith('✅') || stripped.startsWith('❌')) {
41
- bullets.push(`- ${stripped}`);
42
- } else if (stripped.startsWith('→')) {
43
- bullets[bullets.length - 1] += ` (${stripped.substring(1).trim()})`;
44
- }
45
- }
46
- return bullets.join('\n') + '\n';
47
- });
48
- }
49
-
50
- function collapseBlanks(content) {
51
- return content.replace(/\n{3,}/g, '\n\n');
52
- }
53
-
54
- const FILLER_BEFORE_SECTION = /(^# .+\n\n)([A-Z][^\n]+\n){1,4}(\n---\n)/gm;
55
-
56
- function removeFillerBetweenTitleAndHr(content) {
57
- return content.replace(FILLER_BEFORE_SECTION, '$1$3');
58
- }
59
-
60
- const REDUNDANT_NOTE = /^\/\/ (motion\.\w+|Any HTML|Note:|Variant names propagate|\/\/ )[^\n]*\n/gm;
61
-
62
- function stripRedundantNotes(content) {
63
- return content.replace(REDUNDANT_NOTE, '');
64
- }
65
-
66
- function compressFile(filePath) {
67
- const original = fs.readFileSync(filePath, 'utf8');
68
- let content = original;
69
-
70
- for (const pattern of SECTION_PATTERNS) {
71
- content = content.replace(pattern, '');
72
- }
73
-
74
- content = stripChattyIntro(content);
75
- content = removeFillerBetweenTitleAndHr(content);
76
- content = stripObviousComments(content);
77
- content = stripRedundantNotes(content);
78
- content = compressPerfBlocks(content);
79
- content = collapseBlanks(content);
80
-
81
- const originalLen = Buffer.byteLength(original, 'utf8');
82
- const newLen = Buffer.byteLength(content.trim() + '\n', 'utf8');
83
-
84
- fs.writeFileSync(filePath, content.trim() + '\n', 'utf8');
85
- return [originalLen, newLen, originalLen - newLen];
86
- }
87
-
88
- function main() {
89
- const base = '.agent/skills';
90
- if (!fs.existsSync(base)) {
91
- console.error(`ERROR: '${base}' not found. Run from tribunal-kit root.`);
92
- process.exit(1);
93
- }
94
-
95
- let totalOrig = 0;
96
- let totalNew = 0;
97
- const results = [];
98
-
99
- function walkDir(dir) {
100
- const items = fs.readdirSync(dir, { withFileTypes: true });
101
- for (const item of items) {
102
- const fpath = path.join(dir, item.name);
103
- if (item.isDirectory()) {
104
- walkDir(fpath);
105
- } else if (item.name.endsWith('.md')) {
106
- const [orig, newL, saved] = compressFile(fpath);
107
- totalOrig += orig;
108
- totalNew += newL;
109
- if (saved > 0) {
110
- results.push([saved, fpath]);
111
- }
112
- }
113
- }
114
- }
115
-
116
- walkDir(base);
117
- results.sort((a, b) => b[0] - a[0]);
118
-
119
- console.log(`\n${'='.repeat(55)}`);
120
- console.log(` Skill Compression Complete`);
121
- console.log(`${'='.repeat(55)}`);
122
- console.log(` Original : ${totalOrig} bytes (${Math.floor(totalOrig / 1024)}KB)`);
123
- console.log(` After : ${totalNew} bytes (${Math.floor(totalNew / 1024)}KB)`);
124
-
125
- const savedTotal = totalOrig - totalNew;
126
- const pct = totalOrig ? (savedTotal / totalOrig * 100) : 0;
127
- console.log(` Saved : ${savedTotal} bytes (${Math.floor(savedTotal / 1024)}KB) — ${pct.toFixed(1)}%`);
128
- console.log(`\n Top savings:`);
129
-
130
- for (const [saved, filePath] of results.slice(0, 15)) {
131
- const parts = filePath.split(path.sep);
132
- const skill = parts[parts.length - 2];
133
- const fname = parts[parts.length - 1];
134
- console.log(` -${Math.floor(saved / 1024).toString().padStart(2, ' ')}KB ${skill}/${fname}`);
135
- }
136
- console.log();
137
- }
138
-
139
- if (require.main === module) {
140
- main();
141
- }
1
+ #!/usr/bin/env node
2
+ // compress_skills.js - Aggressive token reduction for .agent/skills/**/*.md files
3
+
4
+ 'use strict';
5
+
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+
9
+ const SECTION_PATTERNS = [
10
+ /^## 🏛️ Tribunal Integration[\s\S]*?(?=\n## |\n# |$)/gm,
11
+ /^## Tribunal Integration[\s\S]*?(?=\n## |\n# |$)/gm,
12
+ /^### ✅ Pre-Flight Self-Audit[\s\S]*?(?=\n## |\n### |\n# |$)/gm,
13
+ /^## Pre-Flight Self-Audit[\s\S]*?(?=\n## |\n# |$)/gm,
14
+ /^## Cross-Workflow Navigation[\s\S]*?(?=\n## |\n# |$)/gm,
15
+ /^## Output Format\s*\n```[\s\S]*?```\s*\n/gm,
16
+ /^## VBC Protocol[\s\S]*?(?=\n## |\n# |$)/gm,
17
+ /^## LLM Traps[\s\S]*?(?=\n## |\n# |$)/gm,
18
+ ];
19
+
20
+ const CHATTY_INTRO = /(^# .+\n)\n[A-Z][^#\n]{60,}\n[A-Z][^#\n]{40,}\n\n---/gm;
21
+
22
+ function stripChattyIntro(content) {
23
+ return content.replace(CHATTY_INTRO, '$1\n---');
24
+ }
25
+
26
+ const OBVIOUS_COMMENT = /^(\s*)(\/\/ (default for most properties|shorthand|number of repeats|default: \d+|spring tension|resistance|weight|approximate duration|deceleration rate)[^\n]*)\n/gm;
27
+
28
+ function stripObviousComments(content) {
29
+ return content.replace(OBVIOUS_COMMENT, '');
30
+ }
31
+
32
+ const PERF_TEXT_BLOCK = /^```\n(✅ Use \w[^\n]*\n → [^\n]*\n\n?){3,}```\n/gm;
33
+
34
+ function compressPerfBlocks(content) {
35
+ return content.replace(PERF_TEXT_BLOCK, (match) => {
36
+ const lines = match.replace(/`|\n$/g, '').split('\n');
37
+ const bullets = [];
38
+ for (const line of lines) {
39
+ const stripped = line.trim();
40
+ if (stripped.startsWith('✅') || stripped.startsWith('❌')) {
41
+ bullets.push(`- ${stripped}`);
42
+ } else if (stripped.startsWith('→')) {
43
+ bullets[bullets.length - 1] += ` (${stripped.substring(1).trim()})`;
44
+ }
45
+ }
46
+ return bullets.join('\n') + '\n';
47
+ });
48
+ }
49
+
50
+ function collapseBlanks(content) {
51
+ return content.replace(/\n{3,}/g, '\n\n');
52
+ }
53
+
54
+ const FILLER_BEFORE_SECTION = /(^# .+\n\n)([A-Z][^\n]+\n){1,4}(\n---\n)/gm;
55
+
56
+ function removeFillerBetweenTitleAndHr(content) {
57
+ return content.replace(FILLER_BEFORE_SECTION, '$1$3');
58
+ }
59
+
60
+ const REDUNDANT_NOTE = /^\/\/ (motion\.\w+|Any HTML|Note:|Variant names propagate|\/\/ )[^\n]*\n/gm;
61
+
62
+ function stripRedundantNotes(content) {
63
+ return content.replace(REDUNDANT_NOTE, '');
64
+ }
65
+
66
+ function compressFile(filePath) {
67
+ const original = fs.readFileSync(filePath, 'utf8');
68
+ let content = original;
69
+
70
+ for (const pattern of SECTION_PATTERNS) {
71
+ content = content.replace(pattern, '');
72
+ }
73
+
74
+ content = stripChattyIntro(content);
75
+ content = removeFillerBetweenTitleAndHr(content);
76
+ content = stripObviousComments(content);
77
+ content = stripRedundantNotes(content);
78
+ content = compressPerfBlocks(content);
79
+ content = collapseBlanks(content);
80
+
81
+ const originalLen = Buffer.byteLength(original, 'utf8');
82
+ const newLen = Buffer.byteLength(content.trim() + '\n', 'utf8');
83
+
84
+ fs.writeFileSync(filePath, content.trim() + '\n', 'utf8');
85
+ return [originalLen, newLen, originalLen - newLen];
86
+ }
87
+
88
+ function main() {
89
+ const base = '.agent/skills';
90
+ if (!fs.existsSync(base)) {
91
+ console.error(`ERROR: '${base}' not found. Run from tribunal-kit root.`);
92
+ process.exit(1);
93
+ }
94
+
95
+ let totalOrig = 0;
96
+ let totalNew = 0;
97
+ const results = [];
98
+
99
+ function walkDir(dir) {
100
+ const items = fs.readdirSync(dir, { withFileTypes: true });
101
+ for (const item of items) {
102
+ const fpath = path.join(dir, item.name);
103
+ if (item.isDirectory()) {
104
+ walkDir(fpath);
105
+ } else if (item.name.endsWith('.md')) {
106
+ const [orig, newL, saved] = compressFile(fpath);
107
+ totalOrig += orig;
108
+ totalNew += newL;
109
+ if (saved > 0) {
110
+ results.push([saved, fpath]);
111
+ }
112
+ }
113
+ }
114
+ }
115
+
116
+ walkDir(base);
117
+ results.sort((a, b) => b[0] - a[0]);
118
+
119
+ console.log(`\n${'='.repeat(55)}`);
120
+ console.log(` Skill Compression Complete`);
121
+ console.log(`${'='.repeat(55)}`);
122
+ console.log(` Original : ${totalOrig} bytes (${Math.floor(totalOrig / 1024)}KB)`);
123
+ console.log(` After : ${totalNew} bytes (${Math.floor(totalNew / 1024)}KB)`);
124
+
125
+ const savedTotal = totalOrig - totalNew;
126
+ const pct = totalOrig ? (savedTotal / totalOrig * 100) : 0;
127
+ console.log(` Saved : ${savedTotal} bytes (${Math.floor(savedTotal / 1024)}KB) — ${pct.toFixed(1)}%`);
128
+ console.log(`\n Top savings:`);
129
+
130
+ for (const [saved, filePath] of results.slice(0, 15)) {
131
+ const parts = filePath.split(path.sep);
132
+ const skill = parts[parts.length - 2];
133
+ const fname = parts[parts.length - 1];
134
+ console.log(` -${Math.floor(saved / 1024).toString().padStart(2, ' ')}KB ${skill}/${fname}`);
135
+ }
136
+ console.log();
137
+ }
138
+
139
+ if (require.main === module) {
140
+ main();
141
+ }