tribunal-kit 4.6.1 → 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 +61 -47
- package/bin/mcp-server.js +476 -121
- package/bin/tribunal-kit.js +1245 -987
- package/bin/wrapper.js +104 -73
- package/dist/cli.js +265 -0
- package/dist/commands/case.js +71 -0
- package/dist/commands/compile.js +84 -0
- package/dist/commands/context.js +66 -0
- package/dist/commands/graph.js +38 -0
- package/dist/commands/hook.js +28 -0
- package/dist/commands/init.js +339 -0
- package/dist/commands/learn.js +117 -0
- package/dist/commands/marathon.js +45 -0
- package/dist/commands/memory.js +456 -0
- package/dist/commands/mutate.js +30 -0
- package/dist/commands/status.js +35 -0
- package/dist/commands/sync.js +25 -0
- package/dist/commands/uninstall.js +42 -0
- package/dist/commands/update.js +37 -0
- package/dist/mcp/server.js +142 -0
- package/dist/types.js +8 -0
- package/dist/utils/fs.js +96 -0
- package/dist/utils/hasher.js +142 -0
- package/dist/utils/helpers.js +68 -0
- package/dist/utils/logger.js +54 -0
- package/dist/utils/version.js +150 -0
- package/package.json +3 -2
- package/scripts/benchmark.js +197 -0
- package/scripts/changelog.js +196 -168
- package/scripts/sync-version.js +94 -81
- package/scripts/validate-payload.js +85 -78
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Tribunal-Kit Performance Benchmark
|
|
4
|
+
*
|
|
5
|
+
* Measures and reports performance metrics for key operations.
|
|
6
|
+
* Run: node scripts/benchmark.js
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const { spawnSync } = require("child_process");
|
|
10
|
+
const path = require("path");
|
|
11
|
+
const fs = require("fs");
|
|
12
|
+
const os = require("os");
|
|
13
|
+
|
|
14
|
+
// ANSI colors
|
|
15
|
+
const C = {
|
|
16
|
+
reset: "\x1b[0m",
|
|
17
|
+
bold: "\x1b[1m",
|
|
18
|
+
dim: "\x1b[2m",
|
|
19
|
+
red: "\x1b[91m",
|
|
20
|
+
green: "\x1b[92m",
|
|
21
|
+
yellow: "\x1b[93m",
|
|
22
|
+
cyan: "\x1b[96m",
|
|
23
|
+
white: "\x1b[97m",
|
|
24
|
+
gray: "\x1b[90m",
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function c(color, text) {
|
|
28
|
+
return `${C[color]}${text}${C.reset}`;
|
|
29
|
+
}
|
|
30
|
+
function bold(text) {
|
|
31
|
+
return `${C.bold}${text}${C.reset}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Time a command execution in milliseconds.
|
|
36
|
+
* @param {string} label - Description of the benchmark
|
|
37
|
+
* @param {function} fn - Function to benchmark
|
|
38
|
+
* @param {number} [runs=3] - Number of runs for averaging
|
|
39
|
+
* @returns {{ label: string, avg: number, min: number, max: number, runs: number }}
|
|
40
|
+
*/
|
|
41
|
+
async function benchmark(label, fn, runs = 3) {
|
|
42
|
+
const times = [];
|
|
43
|
+
for (let i = 0; i < runs; i++) {
|
|
44
|
+
const start = performance.now();
|
|
45
|
+
await fn();
|
|
46
|
+
const end = performance.now();
|
|
47
|
+
times.push(end - start);
|
|
48
|
+
}
|
|
49
|
+
const avg = times.reduce((a, b) => a + b, 0) / times.length;
|
|
50
|
+
const min = Math.min(...times);
|
|
51
|
+
const max = Math.max(...times);
|
|
52
|
+
return { label, avg, min, max, runs };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Time a shell command.
|
|
57
|
+
*/
|
|
58
|
+
function benchmarkCommand(label, command, runs = 3) {
|
|
59
|
+
return benchmark(
|
|
60
|
+
label,
|
|
61
|
+
() => {
|
|
62
|
+
spawnSync("node", command.split(" "), {
|
|
63
|
+
stdio: "pipe",
|
|
64
|
+
encoding: "utf8",
|
|
65
|
+
env: { ...process.env, TK_SKIP_UPDATE_CHECK: "1" },
|
|
66
|
+
});
|
|
67
|
+
},
|
|
68
|
+
runs,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function main() {
|
|
73
|
+
console.log();
|
|
74
|
+
console.log(bold(` ⚡ Tribunal-Kit Performance Benchmark`));
|
|
75
|
+
console.log(c("gray", ` ─────────────────────────────────────────`));
|
|
76
|
+
console.log(c("gray", ` Platform: ${os.platform()} ${os.arch()}`));
|
|
77
|
+
console.log(c("gray", ` Node: ${process.version}`));
|
|
78
|
+
console.log(
|
|
79
|
+
c(
|
|
80
|
+
"gray",
|
|
81
|
+
` CPUs: ${os.cpus().length}x ${os.cpus()[0]?.model || "unknown"}`,
|
|
82
|
+
),
|
|
83
|
+
);
|
|
84
|
+
console.log(c("gray", ` ─────────────────────────────────────────`));
|
|
85
|
+
console.log();
|
|
86
|
+
|
|
87
|
+
const cliPath = path.resolve(__dirname, "../bin/wrapper.js");
|
|
88
|
+
const tempDir = path.join(os.tmpdir(), `tribunal-bench-${Date.now()}`);
|
|
89
|
+
fs.mkdirSync(tempDir, { recursive: true });
|
|
90
|
+
|
|
91
|
+
const results = [];
|
|
92
|
+
|
|
93
|
+
// 1. Cold start (help)
|
|
94
|
+
console.log(c("cyan", " ▸ Benchmarking: CLI cold-start (--help)"));
|
|
95
|
+
const helpResult = await benchmarkCommand(
|
|
96
|
+
"CLI cold-start (--help)",
|
|
97
|
+
`${cliPath} --help`,
|
|
98
|
+
5,
|
|
99
|
+
);
|
|
100
|
+
results.push(helpResult);
|
|
101
|
+
|
|
102
|
+
// 2. Status check
|
|
103
|
+
console.log(c("cyan", " ▸ Benchmarking: tk status"));
|
|
104
|
+
const statusResult = await benchmarkCommand(
|
|
105
|
+
"Status check",
|
|
106
|
+
`${cliPath} status --quiet`,
|
|
107
|
+
5,
|
|
108
|
+
);
|
|
109
|
+
results.push(statusResult);
|
|
110
|
+
|
|
111
|
+
// 3. Init (dry-run)
|
|
112
|
+
console.log(c("cyan", " ▸ Benchmarking: tk init --dry-run"));
|
|
113
|
+
const initResult = await benchmarkCommand(
|
|
114
|
+
"Init (dry-run)",
|
|
115
|
+
`${cliPath} init --dry-run --quiet --skip-update-check --path=${tempDir}`,
|
|
116
|
+
3,
|
|
117
|
+
);
|
|
118
|
+
results.push(initResult);
|
|
119
|
+
|
|
120
|
+
// 4. Init (real, to temp dir)
|
|
121
|
+
console.log(c("cyan", " ▸ Benchmarking: tk init (real copy)"));
|
|
122
|
+
const initRealResult = await benchmark(
|
|
123
|
+
"Init (full copy)",
|
|
124
|
+
() => {
|
|
125
|
+
const runDir = path.join(tempDir, `run-${Date.now()}`);
|
|
126
|
+
fs.mkdirSync(runDir, { recursive: true });
|
|
127
|
+
spawnSync(
|
|
128
|
+
"node",
|
|
129
|
+
[cliPath, "init", "--quiet", "--skip-update-check", `--path=${runDir}`],
|
|
130
|
+
{
|
|
131
|
+
stdio: "pipe",
|
|
132
|
+
encoding: "utf8",
|
|
133
|
+
env: { ...process.env, TK_SKIP_UPDATE_CHECK: "1" },
|
|
134
|
+
},
|
|
135
|
+
);
|
|
136
|
+
// Cleanup
|
|
137
|
+
try {
|
|
138
|
+
fs.rmSync(runDir, { recursive: true, force: true });
|
|
139
|
+
} catch {}
|
|
140
|
+
},
|
|
141
|
+
3,
|
|
142
|
+
);
|
|
143
|
+
results.push(initRealResult);
|
|
144
|
+
|
|
145
|
+
// Print results table
|
|
146
|
+
console.log();
|
|
147
|
+
console.log(bold(` Results`));
|
|
148
|
+
console.log(
|
|
149
|
+
c("gray", ` ─────────────────────────────────────────────────────────`),
|
|
150
|
+
);
|
|
151
|
+
console.log(
|
|
152
|
+
` ${c("white", "Operation".padEnd(30))} ${c("white", "Avg (ms)".padStart(10))} ${c("white", "Min".padStart(8))} ${c("white", "Max".padStart(8))}`,
|
|
153
|
+
);
|
|
154
|
+
console.log(
|
|
155
|
+
c("gray", ` ─────────────────────────────────────────────────────────`),
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
for (const r of results) {
|
|
159
|
+
const avgColor = r.avg < 100 ? "green" : r.avg < 500 ? "yellow" : "red";
|
|
160
|
+
console.log(
|
|
161
|
+
` ${c("white", r.label.padEnd(30))} ${c(avgColor, String(Math.round(r.avg)).padStart(10))} ${c("gray", String(Math.round(r.min)).padStart(8))} ${c("gray", String(Math.round(r.max)).padStart(8))}`,
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
console.log(
|
|
166
|
+
c("gray", ` ─────────────────────────────────────────────────────────`),
|
|
167
|
+
);
|
|
168
|
+
console.log();
|
|
169
|
+
|
|
170
|
+
// Write results to JSON for CI/comparison
|
|
171
|
+
const outputPath = path.resolve(__dirname, "../benchmark-results.json");
|
|
172
|
+
const outputData = {
|
|
173
|
+
timestamp: new Date().toISOString(),
|
|
174
|
+
platform: `${os.platform()}-${os.arch()}`,
|
|
175
|
+
node: process.version,
|
|
176
|
+
results: results.map((r) => ({
|
|
177
|
+
label: r.label,
|
|
178
|
+
avg_ms: Math.round(r.avg),
|
|
179
|
+
min_ms: Math.round(r.min),
|
|
180
|
+
max_ms: Math.round(r.max),
|
|
181
|
+
runs: r.runs,
|
|
182
|
+
})),
|
|
183
|
+
};
|
|
184
|
+
fs.writeFileSync(outputPath, JSON.stringify(outputData, null, 2));
|
|
185
|
+
console.log(c("green", ` ✔ Results saved to benchmark-results.json`));
|
|
186
|
+
|
|
187
|
+
// Cleanup temp
|
|
188
|
+
try {
|
|
189
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
190
|
+
} catch {}
|
|
191
|
+
console.log();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
main().catch((err) => {
|
|
195
|
+
console.error(`Benchmark failed: ${err.message}`);
|
|
196
|
+
process.exit(1);
|
|
197
|
+
});
|
package/scripts/changelog.js
CHANGED
|
@@ -1,168 +1,196 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* changelog.js — Auto-generate CHANGELOG from git history
|
|
4
|
-
*
|
|
5
|
-
* Categorizes commits by conventional commit type:
|
|
6
|
-
* feat: → ✨ Features
|
|
7
|
-
* fix: → 🐛 Bug Fixes
|
|
8
|
-
* perf: → ⚡ Performance
|
|
9
|
-
* docs: → 📝 Documentation
|
|
10
|
-
* test: → ✅ Tests
|
|
11
|
-
* refactor: → ♻️ Refactors
|
|
12
|
-
* chore: → 🔧 Chores
|
|
13
|
-
* BREAKING: → 💥 Breaking Changes
|
|
14
|
-
*
|
|
15
|
-
* Usage:
|
|
16
|
-
* node scripts/changelog.js → Generate full changelog
|
|
17
|
-
* node scripts/changelog.js --preview → Preview unreleased changes
|
|
18
|
-
* node scripts/changelog.js --since v4.1.0 → Changes since a specific tag
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const { execSync } = require(
|
|
24
|
-
const fs = require(
|
|
25
|
-
const path = require(
|
|
26
|
-
|
|
27
|
-
const PKG = require(path.resolve(__dirname,
|
|
28
|
-
const CHANGELOG_PATH = path.resolve(__dirname,
|
|
29
|
-
|
|
30
|
-
// ── Commit Categories ────────────────────────────────────
|
|
31
|
-
const CATEGORIES = {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
// ── Git Helpers ──────────────────────────────────────────
|
|
45
|
-
function git(cmd) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function getLatestTag() {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
return
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
//
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* changelog.js — Auto-generate CHANGELOG from git history
|
|
4
|
+
*
|
|
5
|
+
* Categorizes commits by conventional commit type:
|
|
6
|
+
* feat: → ✨ Features
|
|
7
|
+
* fix: → 🐛 Bug Fixes
|
|
8
|
+
* perf: → ⚡ Performance
|
|
9
|
+
* docs: → 📝 Documentation
|
|
10
|
+
* test: → ✅ Tests
|
|
11
|
+
* refactor: → ♻️ Refactors
|
|
12
|
+
* chore: → 🔧 Chores
|
|
13
|
+
* BREAKING: → 💥 Breaking Changes
|
|
14
|
+
*
|
|
15
|
+
* Usage:
|
|
16
|
+
* node scripts/changelog.js → Generate full changelog
|
|
17
|
+
* node scripts/changelog.js --preview → Preview unreleased changes
|
|
18
|
+
* node scripts/changelog.js --since v4.1.0 → Changes since a specific tag
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
"use strict";
|
|
22
|
+
|
|
23
|
+
const { execSync } = require("child_process");
|
|
24
|
+
const fs = require("fs");
|
|
25
|
+
const path = require("path");
|
|
26
|
+
|
|
27
|
+
const PKG = require(path.resolve(__dirname, "..", "package.json"));
|
|
28
|
+
const CHANGELOG_PATH = path.resolve(__dirname, "..", "CHANGELOG.md");
|
|
29
|
+
|
|
30
|
+
// ── Commit Categories ────────────────────────────────────
|
|
31
|
+
const CATEGORIES = {
|
|
32
|
+
feat: { emoji: "✨", title: "Features" },
|
|
33
|
+
fix: { emoji: "🐛", title: "Bug Fixes" },
|
|
34
|
+
perf: { emoji: "⚡", title: "Performance" },
|
|
35
|
+
docs: { emoji: "📝", title: "Documentation" },
|
|
36
|
+
test: { emoji: "✅", title: "Tests" },
|
|
37
|
+
refactor: { emoji: "♻️", title: "Refactors" },
|
|
38
|
+
chore: { emoji: "🔧", title: "Chores" },
|
|
39
|
+
ci: { emoji: "🏗️", title: "CI/CD" },
|
|
40
|
+
style: { emoji: "🎨", title: "Style" },
|
|
41
|
+
breaking: { emoji: "💥", title: "Breaking Changes" },
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// ── Git Helpers ──────────────────────────────────────────
|
|
45
|
+
function git(cmd) {
|
|
46
|
+
try {
|
|
47
|
+
return execSync(`git ${cmd}`, { encoding: "utf8", timeout: 10000 }).trim();
|
|
48
|
+
} catch {
|
|
49
|
+
return "";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function getLatestTag() {
|
|
54
|
+
return (
|
|
55
|
+
git("describe --tags --abbrev=0 2>nul") ||
|
|
56
|
+
git("describe --tags --abbrev=0 2>/dev/null") ||
|
|
57
|
+
""
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function getCommits(since) {
|
|
62
|
+
const range = since ? `${since}..HEAD` : "HEAD";
|
|
63
|
+
const MAX_COMMITS = 500;
|
|
64
|
+
const format = "--format=%H||%s||%an||%ai";
|
|
65
|
+
const raw = git(`log ${range} ${format} --no-merges -n ${MAX_COMMITS}`);
|
|
66
|
+
if (!raw) return [];
|
|
67
|
+
|
|
68
|
+
return raw
|
|
69
|
+
.split("\n")
|
|
70
|
+
.filter(Boolean)
|
|
71
|
+
.map((line) => {
|
|
72
|
+
const [hash, subject, author, date] = line.split("||");
|
|
73
|
+
return {
|
|
74
|
+
hash: hash?.replace(/^"/, "").slice(0, 7),
|
|
75
|
+
subject,
|
|
76
|
+
author,
|
|
77
|
+
date: date?.replace(/"$/, "").slice(0, 10),
|
|
78
|
+
};
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function categorize(subject) {
|
|
83
|
+
const lower = subject.toLowerCase();
|
|
84
|
+
|
|
85
|
+
// Check for BREAKING CHANGE
|
|
86
|
+
if (lower.includes("breaking") || lower.includes("!:")) {
|
|
87
|
+
return "breaking";
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Match conventional commit prefix
|
|
91
|
+
const match = subject.match(/^(\w+)(?:\(.+?\))?:\s*/);
|
|
92
|
+
if (match) {
|
|
93
|
+
const type = match[1].toLowerCase();
|
|
94
|
+
if (CATEGORIES[type]) return type;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Heuristic fallback
|
|
98
|
+
if (lower.includes("fix") || lower.includes("bug")) return "fix";
|
|
99
|
+
if (lower.includes("add") || lower.includes("new") || lower.includes("feat"))
|
|
100
|
+
return "feat";
|
|
101
|
+
if (lower.includes("doc") || lower.includes("readme")) return "docs";
|
|
102
|
+
if (lower.includes("test")) return "test";
|
|
103
|
+
if (lower.includes("refactor") || lower.includes("clean")) return "refactor";
|
|
104
|
+
if (lower.includes("perf") || lower.includes("optim")) return "perf";
|
|
105
|
+
if (lower.includes("ci") || lower.includes("workflow")) return "ci";
|
|
106
|
+
|
|
107
|
+
return "chore";
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ── Changelog Generation ─────────────────────────────────
|
|
111
|
+
function generateChangelog(commits, version, date) {
|
|
112
|
+
const grouped = {};
|
|
113
|
+
for (const commit of commits) {
|
|
114
|
+
const cat = categorize(commit.subject);
|
|
115
|
+
if (!grouped[cat]) grouped[cat] = [];
|
|
116
|
+
// Strip conventional prefix for cleaner display
|
|
117
|
+
const clean = commit.subject.replace(/^\w+(\(.+?\))?:\s*/, "");
|
|
118
|
+
grouped[cat].push({ ...commit, clean });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
let md = `## [${version}] — ${date}\n\n`;
|
|
122
|
+
|
|
123
|
+
// Breaking changes first
|
|
124
|
+
const order = [
|
|
125
|
+
"breaking",
|
|
126
|
+
"feat",
|
|
127
|
+
"fix",
|
|
128
|
+
"perf",
|
|
129
|
+
"refactor",
|
|
130
|
+
"docs",
|
|
131
|
+
"test",
|
|
132
|
+
"ci",
|
|
133
|
+
"style",
|
|
134
|
+
"chore",
|
|
135
|
+
];
|
|
136
|
+
for (const cat of order) {
|
|
137
|
+
if (!grouped[cat] || grouped[cat].length === 0) continue;
|
|
138
|
+
const { emoji, title } = CATEGORIES[cat];
|
|
139
|
+
md += `### ${emoji} ${title}\n\n`;
|
|
140
|
+
for (const c of grouped[cat]) {
|
|
141
|
+
md += `- ${c.clean} (\`${c.hash}\`)\n`;
|
|
142
|
+
}
|
|
143
|
+
md += "\n";
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return md;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ── Main ─────────────────────────────────────────────────
|
|
150
|
+
function main() {
|
|
151
|
+
const args = process.argv.slice(2);
|
|
152
|
+
const isPreview = args.includes("--preview");
|
|
153
|
+
const sinceIdx = args.indexOf("--since");
|
|
154
|
+
const sinceTag = sinceIdx !== -1 ? args[sinceIdx + 1] : null;
|
|
155
|
+
|
|
156
|
+
const since = sinceTag || getLatestTag();
|
|
157
|
+
const commits = getCommits(since);
|
|
158
|
+
|
|
159
|
+
if (commits.length === 0) {
|
|
160
|
+
console.log(" ℹ️ No new commits found since", since || "beginning");
|
|
161
|
+
process.exit(0);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
165
|
+
const version = isPreview ? "Unreleased" : PKG.version;
|
|
166
|
+
|
|
167
|
+
const changelog = generateChangelog(commits, version, today);
|
|
168
|
+
|
|
169
|
+
if (isPreview) {
|
|
170
|
+
console.log("\n 📋 Changelog Preview\n " + "─".repeat(40) + "\n");
|
|
171
|
+
console.log(changelog);
|
|
172
|
+
console.log(
|
|
173
|
+
` 📊 ${commits.length} commits since ${since || "initial commit"}`,
|
|
174
|
+
);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Write or prepend to CHANGELOG.md
|
|
179
|
+
const header = `# Changelog\n\nAll notable changes to Tribunal Kit are documented here.\nFormat follows [Keep a Changelog](https://keepachangelog.com/).\n\n`;
|
|
180
|
+
|
|
181
|
+
let existing = "";
|
|
182
|
+
if (fs.existsSync(CHANGELOG_PATH)) {
|
|
183
|
+
existing = fs.readFileSync(CHANGELOG_PATH, "utf8");
|
|
184
|
+
// Remove existing header
|
|
185
|
+
existing = existing.replace(/^# Changelog[\s\S]*?(?=## )/, "");
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const full = header + changelog + existing;
|
|
189
|
+
fs.writeFileSync(CHANGELOG_PATH, full, "utf8");
|
|
190
|
+
|
|
191
|
+
console.log(
|
|
192
|
+
` ✔ CHANGELOG.md updated — v${version} (${commits.length} commits)`,
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
main();
|