tribunal-kit 4.3.1 → 4.4.1

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 (67) 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/scripts/_colors.js +18 -18
  10. package/.agent/scripts/_utils.js +42 -42
  11. package/.agent/scripts/append_flow.js +72 -72
  12. package/.agent/scripts/auto_preview.js +197 -197
  13. package/.agent/scripts/bundle_analyzer.js +290 -290
  14. package/.agent/scripts/case_law_manager.js +17 -6
  15. package/.agent/scripts/checklist.js +266 -266
  16. package/.agent/scripts/colors.js +17 -17
  17. package/.agent/scripts/compress_skills.js +141 -141
  18. package/.agent/scripts/consolidate_skills.js +149 -149
  19. package/.agent/scripts/context_broker.js +611 -609
  20. package/.agent/scripts/deep_compress.js +150 -150
  21. package/.agent/scripts/dependency_analyzer.js +272 -272
  22. package/.agent/scripts/graph_builder.js +151 -37
  23. package/.agent/scripts/graph_visualizer.js +384 -0
  24. package/.agent/scripts/inner_loop_validator.js +451 -465
  25. package/.agent/scripts/lint_runner.js +187 -187
  26. package/.agent/scripts/minify_context.js +100 -100
  27. package/.agent/scripts/mutation_runner.js +280 -0
  28. package/.agent/scripts/patch_skills_meta.js +156 -156
  29. package/.agent/scripts/patch_skills_output.js +244 -244
  30. package/.agent/scripts/schema_validator.js +297 -297
  31. package/.agent/scripts/security_scan.js +303 -303
  32. package/.agent/scripts/session_manager.js +276 -276
  33. package/.agent/scripts/skill_evolution.js +644 -644
  34. package/.agent/scripts/skill_integrator.js +313 -313
  35. package/.agent/scripts/strengthen_skills.js +193 -193
  36. package/.agent/scripts/strip_tribunal.js +47 -47
  37. package/.agent/scripts/swarm_dispatcher.js +360 -360
  38. package/.agent/scripts/test_runner.js +193 -193
  39. package/.agent/scripts/utils.js +32 -32
  40. package/.agent/scripts/verify_all.js +257 -256
  41. package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +1 -1
  42. package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +1 -1
  43. package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +1 -1
  44. package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +1 -1
  45. package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +1 -1
  46. package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +1 -1
  47. package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +1 -1
  48. package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +1 -1
  49. package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +1 -1
  50. package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +1 -1
  51. package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +1 -1
  52. package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +1 -1
  53. package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +1 -1
  54. package/.agent/skills/doc.md +1 -1
  55. package/.agent/skills/knowledge-graph/SKILL.md +32 -16
  56. package/.agent/skills/testing-patterns/SKILL.md +19 -2
  57. package/.agent/skills/ui-ux-pro-max/SKILL.md +480 -43
  58. package/.agent/workflows/generate.md +183 -183
  59. package/.agent/workflows/tribunal-speed.md +183 -183
  60. package/README.md +1 -1
  61. package/bin/tribunal-kit.js +134 -17
  62. package/package.json +6 -3
  63. package/scripts/changelog.js +167 -167
  64. package/scripts/sync-version.js +81 -81
  65. package/.agent/scripts/__pycache__/_colors.cpython-311.pyc +0 -0
  66. package/.agent/scripts/__pycache__/_utils.cpython-311.pyc +0 -0
  67. package/.agent/scripts/__pycache__/case_law_manager.cpython-311.pyc +0 -0
@@ -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
+ }
@@ -1,149 +1,149 @@
1
- #!/usr/bin/env node
2
- /**
3
- * consolidate_skills.js
4
- */
5
-
6
- 'use strict';
7
-
8
- const fs = require('fs');
9
- const path = require('path');
10
-
11
- const BASE = '.agent/skills';
12
-
13
- const STRIP_PATTERNS = [
14
- /^## šŸ›ļø Tribunal Integration.*?(?=\n## |$)/gms,
15
- /^## Tribunal Integration.*?(?=\n## |$)/gms,
16
- /^### āœ… Pre-Flight Self-Audit.*?(?=\n###|\n## |$)/gms,
17
- /^## Pre-Flight Self-Audit.*?(?=\n## |$)/gms,
18
- /^## Output Format\b.*?(?=\n## |$)/gms,
19
- /^## šŸ”§ Runtime Scripts.*?(?=\n## |$)/gms,
20
- /^## šŸ”“ MANDATORY.*?(?=\n## |$)/gms,
21
- /^## āš ļø CRITICAL: ASK BEFORE ASSUMING.*?(?=\n## |$)/gms,
22
- /^## šŸ“ CHECKPOINT \(MANDATORY.*?(?=\n## |$)/gms,
23
- /^## Output Format.*?```\n[^`]*```\n?(?=\n## |$)/gms,
24
- /^\*\*Execute these for validation.*?---\n/gms,
25
- /^\*\*VBC \(Verification-Before-Completion\).*?\n/gms,
26
- /^\*\*ā›” DO NOT start.*?---\n?/gms,
27
- /^> 🧠 \*\*mobile-design.*?\n/gms,
28
- /^> \*\*STOP.*?\n/gms,
29
- ];
30
-
31
- function adjustHeadings(content, offset = 1) {
32
- const lines = content.split('\n');
33
- const out = [];
34
- for (let line of lines) {
35
- const m = line.match(/^(#{1,5}) /);
36
- if (m) {
37
- const level = m[1].length;
38
- const newLevel = Math.min(level + offset, 6);
39
- line = '#'.repeat(newLevel) + line.substring(level);
40
- }
41
- out.push(line);
42
- }
43
- return out.join('\n');
44
- }
45
-
46
- function cleanContent(content) {
47
- for (const p of STRIP_PATTERNS) {
48
- content = content.replace(p, '');
49
- }
50
- content = content.replace(/\n{3,}/g, '\n\n');
51
- return content.trim();
52
- }
53
-
54
- function extractFrontmatter(content) {
55
- const m = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n/);
56
- if (m) {
57
- return [m[1], content.substring(m[0].length)];
58
- }
59
- return ['', content];
60
- }
61
-
62
- function consolidate(skillDir) {
63
- const skillName = path.basename(skillDir);
64
- const mainPath = path.join(skillDir, 'SKILL.md');
65
- if (!fs.existsSync(mainPath)) return false;
66
-
67
- const files = fs.readdirSync(skillDir);
68
- const subFiles = files.filter(f => f.endsWith('.md') && f !== 'SKILL.md').sort();
69
-
70
- if (subFiles.length === 0) return false;
71
-
72
- console.log(`\n → Consolidating: ${skillName} (${subFiles.length} sub-files)`);
73
-
74
- const mainContent = fs.readFileSync(mainPath, 'utf8');
75
- let [frontmatter, mainBody] = extractFrontmatter(mainContent);
76
-
77
- const fmLines = frontmatter.split('\n');
78
- const newFm = [];
79
- for (const line of fmLines) {
80
- if (line.startsWith('version:')) newFm.push('version: 3.1.0');
81
- else if (line.startsWith('last-updated:')) newFm.push('last-updated: 2026-04-06');
82
- else newFm.push(line);
83
- }
84
- frontmatter = newFm.join('\n');
85
-
86
- mainBody = cleanContent(mainBody);
87
- mainBody = mainBody.replace(/\|.*?\.md.*?\|.*?\|.*?\|\n/g, '');
88
- mainBody = mainBody.replace(/^\|[-| ]+\|\n/gm, '');
89
- mainBody = mainBody.replace(/\n{3,}/g, '\n\n');
90
-
91
- const mergedSections = [];
92
- for (const fname of subFiles) {
93
- const fpath = path.join(skillDir, fname);
94
- const raw = fs.readFileSync(fpath, 'utf8');
95
- let [, body] = extractFrontmatter(raw);
96
- body = cleanContent(body);
97
- body = adjustHeadings(body, 1);
98
- if (body.trim()) {
99
- mergedSections.push(body.trim());
100
- }
101
- }
102
-
103
- let combined = `---\n${frontmatter}\n---\n\n${mainBody}`;
104
- if (mergedSections.length > 0) {
105
- combined += '\n\n---\n\n' + mergedSections.join('\n\n---\n\n');
106
- }
107
-
108
- combined = combined.replace(/\n{3,}/g, '\n\n');
109
- combined = combined.trim() + '\n';
110
-
111
- let totalSubBytes = 0;
112
- for (const f of subFiles) totalSubBytes += fs.statSync(path.join(skillDir, f)).size;
113
- console.log(` Sub-files total: ${Math.floor(totalSubBytes / 1024)}KB`);
114
-
115
- fs.writeFileSync(mainPath, combined, 'utf8');
116
- const newSize = fs.statSync(mainPath).size;
117
- console.log(` New SKILL.md: ${Math.floor(newSize / 1024)}KB (from ${Math.floor(Buffer.byteLength(mainContent, 'utf8') / 1024)}KB main + ${Math.floor(totalSubBytes / 1024)}KB subs → ${Math.floor(newSize / 1024)}KB)`);
118
-
119
- for (const fname of subFiles) {
120
- fs.unlinkSync(path.join(skillDir, fname));
121
- console.log(` Deleted: ${fname}`);
122
- }
123
-
124
- return true;
125
- }
126
-
127
- function main() {
128
- const target = process.argv.length > 2 ? process.argv[2] : null;
129
-
130
- let processed = 0;
131
- if (!fs.existsSync(BASE)) return;
132
-
133
- for (const skillName of fs.readdirSync(BASE)) {
134
- const skillDir = path.join(BASE, skillName);
135
- if (!fs.statSync(skillDir).isDirectory()) continue;
136
- if (target && skillName !== target) continue;
137
- if (consolidate(skillDir)) processed++;
138
- }
139
-
140
- if (processed === 0) {
141
- console.log('No skills with sub-files found (or target not matched).');
142
- } else {
143
- console.log(`\nāœ… Consolidated ${processed} skills.`);
144
- }
145
- }
146
-
147
- if (require.main === module) {
148
- main();
149
- }
1
+ #!/usr/bin/env node
2
+ /**
3
+ * consolidate_skills.js
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+
11
+ const BASE = '.agent/skills';
12
+
13
+ const STRIP_PATTERNS = [
14
+ /^## šŸ›ļø Tribunal Integration.*?(?=\n## |$)/gms,
15
+ /^## Tribunal Integration.*?(?=\n## |$)/gms,
16
+ /^### āœ… Pre-Flight Self-Audit.*?(?=\n###|\n## |$)/gms,
17
+ /^## Pre-Flight Self-Audit.*?(?=\n## |$)/gms,
18
+ /^## Output Format\b.*?(?=\n## |$)/gms,
19
+ /^## šŸ”§ Runtime Scripts.*?(?=\n## |$)/gms,
20
+ /^## šŸ”“ MANDATORY.*?(?=\n## |$)/gms,
21
+ /^## āš ļø CRITICAL: ASK BEFORE ASSUMING.*?(?=\n## |$)/gms,
22
+ /^## šŸ“ CHECKPOINT \(MANDATORY.*?(?=\n## |$)/gms,
23
+ /^## Output Format.*?```\n[^`]*```\n?(?=\n## |$)/gms,
24
+ /^\*\*Execute these for validation.*?---\n/gms,
25
+ /^\*\*VBC \(Verification-Before-Completion\).*?\n/gms,
26
+ /^\*\*ā›” DO NOT start.*?---\n?/gms,
27
+ /^> 🧠 \*\*mobile-design.*?\n/gms,
28
+ /^> \*\*STOP.*?\n/gms,
29
+ ];
30
+
31
+ function adjustHeadings(content, offset = 1) {
32
+ const lines = content.split('\n');
33
+ const out = [];
34
+ for (let line of lines) {
35
+ const m = line.match(/^(#{1,5}) /);
36
+ if (m) {
37
+ const level = m[1].length;
38
+ const newLevel = Math.min(level + offset, 6);
39
+ line = '#'.repeat(newLevel) + line.substring(level);
40
+ }
41
+ out.push(line);
42
+ }
43
+ return out.join('\n');
44
+ }
45
+
46
+ function cleanContent(content) {
47
+ for (const p of STRIP_PATTERNS) {
48
+ content = content.replace(p, '');
49
+ }
50
+ content = content.replace(/\n{3,}/g, '\n\n');
51
+ return content.trim();
52
+ }
53
+
54
+ function extractFrontmatter(content) {
55
+ const m = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n/);
56
+ if (m) {
57
+ return [m[1], content.substring(m[0].length)];
58
+ }
59
+ return ['', content];
60
+ }
61
+
62
+ function consolidate(skillDir) {
63
+ const skillName = path.basename(skillDir);
64
+ const mainPath = path.join(skillDir, 'SKILL.md');
65
+ if (!fs.existsSync(mainPath)) return false;
66
+
67
+ const files = fs.readdirSync(skillDir);
68
+ const subFiles = files.filter(f => f.endsWith('.md') && f !== 'SKILL.md').sort();
69
+
70
+ if (subFiles.length === 0) return false;
71
+
72
+ console.log(`\n → Consolidating: ${skillName} (${subFiles.length} sub-files)`);
73
+
74
+ const mainContent = fs.readFileSync(mainPath, 'utf8');
75
+ let [frontmatter, mainBody] = extractFrontmatter(mainContent);
76
+
77
+ const fmLines = frontmatter.split('\n');
78
+ const newFm = [];
79
+ for (const line of fmLines) {
80
+ if (line.startsWith('version:')) newFm.push('version: 3.1.0');
81
+ else if (line.startsWith('last-updated:')) newFm.push('last-updated: 2026-04-06');
82
+ else newFm.push(line);
83
+ }
84
+ frontmatter = newFm.join('\n');
85
+
86
+ mainBody = cleanContent(mainBody);
87
+ mainBody = mainBody.replace(/\|.*?\.md.*?\|.*?\|.*?\|\n/g, '');
88
+ mainBody = mainBody.replace(/^\|[-| ]+\|\n/gm, '');
89
+ mainBody = mainBody.replace(/\n{3,}/g, '\n\n');
90
+
91
+ const mergedSections = [];
92
+ for (const fname of subFiles) {
93
+ const fpath = path.join(skillDir, fname);
94
+ const raw = fs.readFileSync(fpath, 'utf8');
95
+ let [, body] = extractFrontmatter(raw);
96
+ body = cleanContent(body);
97
+ body = adjustHeadings(body, 1);
98
+ if (body.trim()) {
99
+ mergedSections.push(body.trim());
100
+ }
101
+ }
102
+
103
+ let combined = `---\n${frontmatter}\n---\n\n${mainBody}`;
104
+ if (mergedSections.length > 0) {
105
+ combined += '\n\n---\n\n' + mergedSections.join('\n\n---\n\n');
106
+ }
107
+
108
+ combined = combined.replace(/\n{3,}/g, '\n\n');
109
+ combined = combined.trim() + '\n';
110
+
111
+ let totalSubBytes = 0;
112
+ for (const f of subFiles) totalSubBytes += fs.statSync(path.join(skillDir, f)).size;
113
+ console.log(` Sub-files total: ${Math.floor(totalSubBytes / 1024)}KB`);
114
+
115
+ fs.writeFileSync(mainPath, combined, 'utf8');
116
+ const newSize = fs.statSync(mainPath).size;
117
+ console.log(` New SKILL.md: ${Math.floor(newSize / 1024)}KB (from ${Math.floor(Buffer.byteLength(mainContent, 'utf8') / 1024)}KB main + ${Math.floor(totalSubBytes / 1024)}KB subs → ${Math.floor(newSize / 1024)}KB)`);
118
+
119
+ for (const fname of subFiles) {
120
+ fs.unlinkSync(path.join(skillDir, fname));
121
+ console.log(` Deleted: ${fname}`);
122
+ }
123
+
124
+ return true;
125
+ }
126
+
127
+ function main() {
128
+ const target = process.argv.length > 2 ? process.argv[2] : null;
129
+
130
+ let processed = 0;
131
+ if (!fs.existsSync(BASE)) return;
132
+
133
+ for (const skillName of fs.readdirSync(BASE)) {
134
+ const skillDir = path.join(BASE, skillName);
135
+ if (!fs.statSync(skillDir).isDirectory()) continue;
136
+ if (target && skillName !== target) continue;
137
+ if (consolidate(skillDir)) processed++;
138
+ }
139
+
140
+ if (processed === 0) {
141
+ console.log('No skills with sub-files found (or target not matched).');
142
+ } else {
143
+ console.log(`\nāœ… Consolidated ${processed} skills.`);
144
+ }
145
+ }
146
+
147
+ if (require.main === module) {
148
+ main();
149
+ }