tribunal-kit 2.4.0 → 2.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agent/skills/github-operations/SKILL.md +354 -0
- package/.agent/skills/readme-builder/SKILL.md +270 -0
- package/bin/tribunal-kit.js +535 -535
- package/package.json +1 -1
package/bin/tribunal-kit.js
CHANGED
|
@@ -1,274 +1,273 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* tribunal-kit CLI
|
|
4
|
-
*
|
|
5
|
-
* Commands:
|
|
6
|
-
* init — Install .agent/ into target project
|
|
7
|
-
* update — Re-install to get latest changes
|
|
8
|
-
* status — Check if .agent/ is installed
|
|
9
|
-
*
|
|
10
|
-
* Usage:
|
|
11
|
-
* npx tribunal-kit init
|
|
12
|
-
* npx tribunal-kit init --force
|
|
13
|
-
* npx tribunal-kit init --path ./myapp
|
|
14
|
-
* npx tribunal-kit init --quiet
|
|
15
|
-
* npx tribunal-kit init --dry-run
|
|
16
|
-
* tribunal-kit update
|
|
17
|
-
* tribunal-kit status
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
const fs = require('fs');
|
|
21
|
-
const path = require('path');
|
|
22
|
-
const https = require('https');
|
|
23
|
-
const { execSync } = require('child_process');
|
|
24
|
-
|
|
25
|
-
const PKG = require(path.resolve(__dirname, '..', 'package.json'));
|
|
26
|
-
const CURRENT_VERSION = PKG.version;
|
|
27
|
-
|
|
28
|
-
// ── Colors ───────────────────────────────────────────────
|
|
29
|
-
const C = {
|
|
30
|
-
reset: '\x1b[0m',
|
|
31
|
-
bold: '\x1b[1m',
|
|
32
|
-
dim: '\x1b[2m',
|
|
33
|
-
red: '\x1b[91m',
|
|
34
|
-
green: '\x1b[92m',
|
|
35
|
-
yellow: '\x1b[93m',
|
|
36
|
-
blue: '\x1b[94m',
|
|
37
|
-
magenta: '\x1b[95m',
|
|
38
|
-
cyan: '\x1b[96m',
|
|
39
|
-
white: '\x1b[97m',
|
|
40
|
-
gray: '\x1b[90m',
|
|
41
|
-
bgCyan: '\x1b[46m',
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
function colorize(color, text) {
|
|
45
|
-
return `${C[color]}${text}${C.reset}`;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function c(color, text) { return `${C[color]}${text}${C.reset}`; }
|
|
49
|
-
function bold(text) { return `${C.bold}${text}${C.reset}`; }
|
|
50
|
-
|
|
51
|
-
// ── Logging ──────────────────────────────────────────────
|
|
52
|
-
let quiet = false;
|
|
53
|
-
|
|
54
|
-
function log(msg) { if (!quiet) console.log(msg); }
|
|
55
|
-
function ok(msg) { if (!quiet) console.log(` ${c('green', '✔')} ${msg}`); }
|
|
56
|
-
function warn(msg) { if (!quiet) console.log(` ${c('yellow', '⚠')} ${msg}`); }
|
|
57
|
-
function err(msg) { console.error(` ${c('red', '✖')} ${msg}`); }
|
|
58
|
-
function dim(msg) { if (!quiet) console.log(` ${c('gray', msg)}`); }
|
|
59
|
-
|
|
60
|
-
// ── Arg Parser ───────────────────────────────────────────
|
|
61
|
-
function parseArgs(argv) {
|
|
62
|
-
const args = { command: null, flags: {} };
|
|
63
|
-
const raw = argv.slice(2);
|
|
64
|
-
|
|
65
|
-
// First non-flag arg is the command
|
|
66
|
-
for (const arg of raw) {
|
|
67
|
-
if (!arg.startsWith('--') && !args.command) {
|
|
68
|
-
args.command = arg;
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
if (arg === '--force') { args.flags.force = true; continue; }
|
|
72
|
-
if (arg === '--quiet') { args.flags.quiet = true; continue; }
|
|
73
|
-
if (arg === '--dry-run') { args.flags.dryRun = true; continue; }
|
|
74
|
-
if (arg === '--skip-update-check') { args.flags.skipUpdateCheck = true; continue; }
|
|
75
|
-
if (arg.startsWith('--path=')) {
|
|
76
|
-
args.flags.path = arg.split('=').slice(1).join('=');
|
|
77
|
-
}
|
|
78
|
-
if (arg === '--path') {
|
|
79
|
-
const idx = raw.indexOf('--path');
|
|
80
|
-
args.flags.path = raw[idx + 1] || null;
|
|
81
|
-
}
|
|
82
|
-
if (arg.startsWith('--branch=')) {
|
|
83
|
-
args.flags.branch = arg.split('=').slice(1).join('=');
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return args;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// ── File Utilities ────────────────────────────────────────
|
|
91
|
-
function copyDir(src, dest, dryRun = false) {
|
|
92
|
-
if (!dryRun) {
|
|
93
|
-
fs.mkdirSync(dest, { recursive: true });
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
97
|
-
let count = 0;
|
|
98
|
-
|
|
99
|
-
for (const entry of entries) {
|
|
100
|
-
const srcPath = path.join(src, entry.name);
|
|
101
|
-
const destPath = path.join(dest, entry.name);
|
|
102
|
-
|
|
103
|
-
if (entry.isDirectory()) {
|
|
104
|
-
count += copyDir(srcPath, destPath, dryRun);
|
|
105
|
-
} else {
|
|
106
|
-
if (!dryRun) {
|
|
107
|
-
fs.cpSync(srcPath, destPath, { force: true });
|
|
108
|
-
}
|
|
109
|
-
count++;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return count;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function countDir(dir) {
|
|
117
|
-
let count = 0;
|
|
118
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
119
|
-
for (const e of entries) {
|
|
120
|
-
if (e.isDirectory()) count += countDir(path.join(dir, e.name));
|
|
121
|
-
else count++;
|
|
122
|
-
}
|
|
123
|
-
return count;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// ── Version Check & Auto-Update ──────────────────────────
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Compare two semver strings. Returns:
|
|
130
|
-
* 1 if a > b, -1 if a < b, 0 if equal.
|
|
131
|
-
*/
|
|
132
|
-
function compareSemver(a, b) {
|
|
133
|
-
const pa = a.replace(/^v/, '').split('.').map(Number);
|
|
134
|
-
const pb = b.replace(/^v/, '').split('.').map(Number);
|
|
135
|
-
for (let i = 0; i < 3; i++) {
|
|
136
|
-
const na = pa[i] || 0;
|
|
137
|
-
const nb = pb[i] || 0;
|
|
138
|
-
if (na > nb) return 1;
|
|
139
|
-
if (na < nb) return -1;
|
|
140
|
-
}
|
|
141
|
-
return 0;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Fetch the latest version from GitHub Releases.
|
|
146
|
-
* Returns the version string (e.g. '2.4.0') or null on failure.
|
|
147
|
-
*/
|
|
148
|
-
function fetchLatestVersion() {
|
|
149
|
-
return new Promise((resolve) => {
|
|
150
|
-
const req = https.get(
|
|
151
|
-
'https://api.github.com/repos/Harmitx7/tribunal-kit/releases/latest',
|
|
152
|
-
{
|
|
153
|
-
headers: {
|
|
154
|
-
'Accept': 'application/vnd.github.v3+json',
|
|
155
|
-
'User-Agent': `tribunal-kit/${CURRENT_VERSION}`
|
|
156
|
-
},
|
|
157
|
-
timeout: 5000
|
|
158
|
-
},
|
|
159
|
-
(res) => {
|
|
160
|
-
let data = '';
|
|
161
|
-
res.on('data', (chunk) => { data += chunk; });
|
|
162
|
-
res.on('end', () => {
|
|
163
|
-
try {
|
|
164
|
-
const json = JSON.parse(data);
|
|
165
|
-
// GitHub tags usually have a 'v' prefix (e.g., 'v2.4.0')
|
|
166
|
-
const version = json.tag_name ? json.tag_name.replace(/^v/, '') : null;
|
|
167
|
-
resolve(version);
|
|
168
|
-
} catch {
|
|
169
|
-
resolve(null);
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
);
|
|
174
|
-
req.on('error', () => resolve(null));
|
|
175
|
-
req.on('timeout', () => { req.destroy(); resolve(null); });
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Check for a newer version and re-invoke with @latest if found.
|
|
181
|
-
* Uses TK_SKIP_UPDATE_CHECK env var as recursion guard.
|
|
182
|
-
* Returns true if a re-invoke happened (caller should exit), false otherwise.
|
|
183
|
-
*/
|
|
184
|
-
async function autoUpdateCheck(originalArgs) {
|
|
185
|
-
// Recursion guard: if we're already a re-invoked process, skip
|
|
186
|
-
if (process.env.TK_SKIP_UPDATE_CHECK === '1') {
|
|
187
|
-
return false;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const latestVersion = await fetchLatestVersion();
|
|
191
|
-
|
|
192
|
-
if (!latestVersion) {
|
|
193
|
-
// Network fail — proceed silently with current version
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
log('');
|
|
206
|
-
log(colorize('
|
|
207
|
-
log(
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
warn(
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
err(
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
if (quiet) return;
|
|
2
|
+
/**
|
|
3
|
+
* tribunal-kit CLI
|
|
4
|
+
*
|
|
5
|
+
* Commands:
|
|
6
|
+
* init — Install .agent/ into target project
|
|
7
|
+
* update — Re-install to get latest changes
|
|
8
|
+
* status — Check if .agent/ is installed
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* npx tribunal-kit init
|
|
12
|
+
* npx tribunal-kit init --force
|
|
13
|
+
* npx tribunal-kit init --path ./myapp
|
|
14
|
+
* npx tribunal-kit init --quiet
|
|
15
|
+
* npx tribunal-kit init --dry-run
|
|
16
|
+
* tribunal-kit update
|
|
17
|
+
* tribunal-kit status
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const fs = require('fs');
|
|
21
|
+
const path = require('path');
|
|
22
|
+
const https = require('https');
|
|
23
|
+
const { execSync } = require('child_process');
|
|
24
|
+
|
|
25
|
+
const PKG = require(path.resolve(__dirname, '..', 'package.json'));
|
|
26
|
+
const CURRENT_VERSION = PKG.version;
|
|
27
|
+
|
|
28
|
+
// ── Colors ───────────────────────────────────────────────
|
|
29
|
+
const C = {
|
|
30
|
+
reset: '\x1b[0m',
|
|
31
|
+
bold: '\x1b[1m',
|
|
32
|
+
dim: '\x1b[2m',
|
|
33
|
+
red: '\x1b[91m',
|
|
34
|
+
green: '\x1b[92m',
|
|
35
|
+
yellow: '\x1b[93m',
|
|
36
|
+
blue: '\x1b[94m',
|
|
37
|
+
magenta: '\x1b[95m',
|
|
38
|
+
cyan: '\x1b[96m',
|
|
39
|
+
white: '\x1b[97m',
|
|
40
|
+
gray: '\x1b[90m',
|
|
41
|
+
bgCyan: '\x1b[46m',
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
function colorize(color, text) {
|
|
45
|
+
return `${C[color]}${text}${C.reset}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function c(color, text) { return `${C[color]}${text}${C.reset}`; }
|
|
49
|
+
function bold(text) { return `${C.bold}${text}${C.reset}`; }
|
|
50
|
+
|
|
51
|
+
// ── Logging ──────────────────────────────────────────────
|
|
52
|
+
let quiet = false;
|
|
53
|
+
|
|
54
|
+
function log(msg) { if (!quiet) console.log(msg); }
|
|
55
|
+
function ok(msg) { if (!quiet) console.log(` ${c('green', '✔')} ${msg}`); }
|
|
56
|
+
function warn(msg) { if (!quiet) console.log(` ${c('yellow', '⚠')} ${msg}`); }
|
|
57
|
+
function err(msg) { console.error(` ${c('red', '✖')} ${msg}`); }
|
|
58
|
+
function dim(msg) { if (!quiet) console.log(` ${c('gray', msg)}`); }
|
|
59
|
+
|
|
60
|
+
// ── Arg Parser ───────────────────────────────────────────
|
|
61
|
+
function parseArgs(argv) {
|
|
62
|
+
const args = { command: null, flags: {} };
|
|
63
|
+
const raw = argv.slice(2);
|
|
64
|
+
|
|
65
|
+
// First non-flag arg is the command
|
|
66
|
+
for (const arg of raw) {
|
|
67
|
+
if (!arg.startsWith('--') && !args.command) {
|
|
68
|
+
args.command = arg;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (arg === '--force') { args.flags.force = true; continue; }
|
|
72
|
+
if (arg === '--quiet') { args.flags.quiet = true; continue; }
|
|
73
|
+
if (arg === '--dry-run') { args.flags.dryRun = true; continue; }
|
|
74
|
+
if (arg === '--skip-update-check') { args.flags.skipUpdateCheck = true; continue; }
|
|
75
|
+
if (arg.startsWith('--path=')) {
|
|
76
|
+
args.flags.path = arg.split('=').slice(1).join('=');
|
|
77
|
+
}
|
|
78
|
+
if (arg === '--path') {
|
|
79
|
+
const idx = raw.indexOf('--path');
|
|
80
|
+
args.flags.path = raw[idx + 1] || null;
|
|
81
|
+
}
|
|
82
|
+
if (arg.startsWith('--branch=')) {
|
|
83
|
+
args.flags.branch = arg.split('=').slice(1).join('=');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return args;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ── File Utilities ────────────────────────────────────────
|
|
91
|
+
function copyDir(src, dest, dryRun = false) {
|
|
92
|
+
if (!dryRun) {
|
|
93
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
97
|
+
let count = 0;
|
|
98
|
+
|
|
99
|
+
for (const entry of entries) {
|
|
100
|
+
const srcPath = path.join(src, entry.name);
|
|
101
|
+
const destPath = path.join(dest, entry.name);
|
|
102
|
+
|
|
103
|
+
if (entry.isDirectory()) {
|
|
104
|
+
count += copyDir(srcPath, destPath, dryRun);
|
|
105
|
+
} else {
|
|
106
|
+
if (!dryRun) {
|
|
107
|
+
fs.cpSync(srcPath, destPath, { force: true });
|
|
108
|
+
}
|
|
109
|
+
count++;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return count;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function countDir(dir) {
|
|
117
|
+
let count = 0;
|
|
118
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
119
|
+
for (const e of entries) {
|
|
120
|
+
if (e.isDirectory()) count += countDir(path.join(dir, e.name));
|
|
121
|
+
else count++;
|
|
122
|
+
}
|
|
123
|
+
return count;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ── Version Check & Auto-Update ──────────────────────────
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Compare two semver strings. Returns:
|
|
130
|
+
* 1 if a > b, -1 if a < b, 0 if equal.
|
|
131
|
+
*/
|
|
132
|
+
function compareSemver(a, b) {
|
|
133
|
+
const pa = a.replace(/^v/, '').split('.').map(Number);
|
|
134
|
+
const pb = b.replace(/^v/, '').split('.').map(Number);
|
|
135
|
+
for (let i = 0; i < 3; i++) {
|
|
136
|
+
const na = pa[i] || 0;
|
|
137
|
+
const nb = pb[i] || 0;
|
|
138
|
+
if (na > nb) return 1;
|
|
139
|
+
if (na < nb) return -1;
|
|
140
|
+
}
|
|
141
|
+
return 0;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Fetch the latest version from GitHub Releases.
|
|
146
|
+
* Returns the version string (e.g. '2.4.0') or null on failure.
|
|
147
|
+
*/
|
|
148
|
+
function fetchLatestVersion() {
|
|
149
|
+
return new Promise((resolve) => {
|
|
150
|
+
const req = https.get(
|
|
151
|
+
'https://api.github.com/repos/Harmitx7/tribunal-kit/releases/latest',
|
|
152
|
+
{
|
|
153
|
+
headers: {
|
|
154
|
+
'Accept': 'application/vnd.github.v3+json',
|
|
155
|
+
'User-Agent': `tribunal-kit/${CURRENT_VERSION}`
|
|
156
|
+
},
|
|
157
|
+
timeout: 5000
|
|
158
|
+
},
|
|
159
|
+
(res) => {
|
|
160
|
+
let data = '';
|
|
161
|
+
res.on('data', (chunk) => { data += chunk; });
|
|
162
|
+
res.on('end', () => {
|
|
163
|
+
try {
|
|
164
|
+
const json = JSON.parse(data);
|
|
165
|
+
// GitHub tags usually have a 'v' prefix (e.g., 'v2.4.0')
|
|
166
|
+
const version = json.tag_name ? json.tag_name.replace(/^v/, '') : null;
|
|
167
|
+
resolve(version);
|
|
168
|
+
} catch {
|
|
169
|
+
resolve(null);
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
);
|
|
174
|
+
req.on('error', () => resolve(null));
|
|
175
|
+
req.on('timeout', () => { req.destroy(); resolve(null); });
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Check for a newer version and re-invoke with @latest if found.
|
|
181
|
+
* Uses TK_SKIP_UPDATE_CHECK env var as recursion guard.
|
|
182
|
+
* Returns true if a re-invoke happened (caller should exit), false otherwise.
|
|
183
|
+
*/
|
|
184
|
+
async function autoUpdateCheck(originalArgs) {
|
|
185
|
+
// Recursion guard: if we're already a re-invoked process, skip
|
|
186
|
+
if (process.env.TK_SKIP_UPDATE_CHECK === '1') {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const latestVersion = await fetchLatestVersion();
|
|
191
|
+
|
|
192
|
+
if (!latestVersion) {
|
|
193
|
+
// Network fail — proceed silently with current version
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (compareSemver(latestVersion, CURRENT_VERSION) <= 0) {
|
|
198
|
+
// Already up to date
|
|
199
|
+
dim(`Version ${CURRENT_VERSION} is up to date.`);
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Newer version available — re-invoke
|
|
204
|
+
log('');
|
|
205
|
+
log(colorize('cyan', ` ⬆ New version available: ${colorize('bold', CURRENT_VERSION)} → ${colorize('bold', latestVersion)}`));
|
|
206
|
+
log(colorize('gray', ' Re-invoking with latest version...'));
|
|
207
|
+
log('');
|
|
208
|
+
|
|
209
|
+
try {
|
|
210
|
+
// Build the command pulling directly from GitHub
|
|
211
|
+
const args = originalArgs.join(' ');
|
|
212
|
+
const cmd = `npx -y github:Harmitx7/tribunal-kit#v${latestVersion} ${args}`;
|
|
213
|
+
|
|
214
|
+
execSync(cmd, {
|
|
215
|
+
stdio: 'inherit',
|
|
216
|
+
env: { ...process.env, TK_SKIP_UPDATE_CHECK: '1' },
|
|
217
|
+
});
|
|
218
|
+
return true; // Re-invoke succeeded, caller should exit
|
|
219
|
+
} catch (e) {
|
|
220
|
+
warn(`Auto-update failed: ${e.message}`);
|
|
221
|
+
warn('Continuing with current version...');
|
|
222
|
+
return false; // Fall through to current version
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ── Kit Source Location ───────────────────────────────────
|
|
227
|
+
function getKitAgent() {
|
|
228
|
+
// When installed via npm, the .agent/ folder is next to this script's package
|
|
229
|
+
const kitRoot = path.resolve(__dirname, '..');
|
|
230
|
+
const agentDir = path.join(kitRoot, '.agent');
|
|
231
|
+
|
|
232
|
+
if (!fs.existsSync(agentDir)) {
|
|
233
|
+
err(`Kit .agent/ folder not found at: ${agentDir}`);
|
|
234
|
+
err('The package may be corrupted. Try: npm install -g tribunal-kit');
|
|
235
|
+
process.exit(1);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return agentDir;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// ── Self-Install Guard ────────────────────────────────────
|
|
242
|
+
/**
|
|
243
|
+
* Returns true if the target directory IS the tribunal-kit package itself.
|
|
244
|
+
* This prevents `init --force` / `update` from deleting the package's own files
|
|
245
|
+
* when run from inside the project directory.
|
|
246
|
+
*/
|
|
247
|
+
function isSelfInstall(targetDir) {
|
|
248
|
+
const kitRoot = path.resolve(__dirname, '..');
|
|
249
|
+
const resolvedTarget = path.resolve(targetDir);
|
|
250
|
+
|
|
251
|
+
// Direct path match
|
|
252
|
+
if (resolvedTarget === kitRoot) return true;
|
|
253
|
+
|
|
254
|
+
// Check if the target's package.json is this package
|
|
255
|
+
const targetPkg = path.join(resolvedTarget, 'package.json');
|
|
256
|
+
if (fs.existsSync(targetPkg)) {
|
|
257
|
+
try {
|
|
258
|
+
const targetName = JSON.parse(fs.readFileSync(targetPkg, 'utf8')).name;
|
|
259
|
+
if (targetName === PKG.name) return true;
|
|
260
|
+
} catch {
|
|
261
|
+
// Unreadable package.json — not a match
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// ── Banner ────────────────────────────────────────────────
|
|
269
|
+
function banner() {
|
|
270
|
+
if (quiet) return;
|
|
272
271
|
// Big ASCII art (TRIBUNAL-KIT)
|
|
273
272
|
const art = String.raw`
|
|
274
273
|
___________ ._____. .__ ____ __.__ __
|
|
@@ -276,268 +275,269 @@ ___________ ._____. .__ ____ __.__ __
|
|
|
276
275
|
| | \_ __ \ || __ \| | \/ \\__ \ | | ______ | < | \ __\
|
|
277
276
|
| | | | \/ || \_\ \ | / | \/ __ \| |__ /_____/ | | \| || |
|
|
278
277
|
|____| |__| |__||___ /____/|___| (____ /____/ |____|__ \__||__|
|
|
279
|
-
\/ \/ \/ \/ `.split('\n').filter(Boolean);
|
|
280
|
-
console.log();
|
|
281
|
-
for (const line of art) log(` ${c('cyan', bold(line))}`);
|
|
282
|
-
console.log();
|
|
283
|
-
// Subtitle strip
|
|
284
|
-
const W = 80;
|
|
285
|
-
const sub = 'Anti-Hallucination Agent System';
|
|
286
|
-
const sp = Math.max(0, W - sub.length);
|
|
287
|
-
const centred = ' '.repeat(Math.floor(sp / 2)) + sub + ' '.repeat(Math.ceil(sp / 2));
|
|
288
|
-
console.log(` ${c('cyan', '╔' + '═'.repeat(W) + '╗')}`);
|
|
289
|
-
console.log(` ${c('cyan', '║')}${c('gray', centred)}${c('cyan', '║')}`);
|
|
290
|
-
console.log(` ${c('cyan', '╚' + '═'.repeat(W) + '╝')}`);
|
|
291
|
-
console.log();
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// ── Commands ──────────────────────────────────────────────
|
|
295
|
-
function cmdInit(flags) {
|
|
296
|
-
const agentSrc = getKitAgent();
|
|
297
|
-
const targetDir = flags.path ? path.resolve(flags.path) : process.cwd();
|
|
298
|
-
const agentDest = path.join(targetDir, '.agent');
|
|
299
|
-
const dryRun = flags.dryRun || false;
|
|
300
|
-
|
|
301
|
-
// ── Self-install guard ──────────────────────────────────
|
|
302
|
-
if (isSelfInstall(targetDir)) {
|
|
303
|
-
err('Cannot run init/update inside the tribunal-kit package itself.');
|
|
304
|
-
err(`Target: ${targetDir}`);
|
|
305
|
-
err(`Package: ${path.resolve(__dirname, '..')}`);
|
|
306
|
-
console.log();
|
|
307
|
-
dim('This command is designed to install .agent/ into OTHER projects.');
|
|
308
|
-
dim('Run it from the root of the project you want to set up:');
|
|
309
|
-
dim(' cd /path/to/your-project');
|
|
310
|
-
dim(' npx tribunal-kit init');
|
|
311
|
-
console.log();
|
|
312
|
-
process.exit(1);
|
|
313
|
-
}
|
|
314
|
-
// ────────────────────────────────────────────────────────
|
|
315
|
-
|
|
316
|
-
banner();
|
|
317
|
-
|
|
318
|
-
if (dryRun) {
|
|
319
|
-
log(colorize('yellow', ' DRY RUN — no files will be written'));
|
|
320
|
-
console.log();
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// Check target exists
|
|
324
|
-
if (!fs.existsSync(targetDir)) {
|
|
325
|
-
err(`Target directory not found: ${targetDir}`);
|
|
326
|
-
process.exit(1);
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// Check if .agent already exists
|
|
330
|
-
if (fs.existsSync(agentDest) && !flags.force) {
|
|
331
|
-
warn('.agent/ already exists in this project.');
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
const
|
|
362
|
-
const
|
|
363
|
-
const
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
const
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
const
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
console.log(` ${c('
|
|
387
|
-
console.log();
|
|
388
|
-
console.log(
|
|
389
|
-
console.log(
|
|
390
|
-
console.log(`
|
|
391
|
-
console.log(
|
|
392
|
-
console.log(statRow('
|
|
393
|
-
console.log(statRow('
|
|
394
|
-
console.log(statRow('
|
|
395
|
-
console.log(
|
|
396
|
-
console.log(
|
|
397
|
-
console.log(plainRow(
|
|
398
|
-
console.log(
|
|
399
|
-
console.log(stepRow('/
|
|
400
|
-
console.log(stepRow('/
|
|
401
|
-
console.log(
|
|
402
|
-
console.log(
|
|
403
|
-
console.log();
|
|
404
|
-
log(
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
err(
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
dim('
|
|
423
|
-
dim('
|
|
424
|
-
dim('
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
const
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
case '
|
|
465
|
-
case '
|
|
466
|
-
case
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
const
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
log(
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
const
|
|
496
|
-
const
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
const
|
|
511
|
-
const
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
log(
|
|
515
|
-
log(
|
|
516
|
-
log(cmd('
|
|
517
|
-
log(cmd('
|
|
518
|
-
|
|
519
|
-
log(
|
|
520
|
-
log(
|
|
521
|
-
log(
|
|
522
|
-
log(opt('--
|
|
523
|
-
log(opt('--
|
|
524
|
-
log(opt('--
|
|
525
|
-
log(opt('--
|
|
526
|
-
|
|
527
|
-
log(
|
|
528
|
-
log(
|
|
529
|
-
log(
|
|
530
|
-
log(ex('npx tribunal-kit init
|
|
531
|
-
log(ex('npx tribunal-kit init --
|
|
532
|
-
log(ex('npx tribunal-kit init --
|
|
533
|
-
log(ex('npx tribunal-kit
|
|
534
|
-
log(ex('npx tribunal-kit
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
278
|
+
\/ \/ \/ \/ `.split('\n').filter(Boolean);
|
|
279
|
+
console.log();
|
|
280
|
+
for (const line of art) log(` ${c('cyan', bold(line))}`);
|
|
281
|
+
console.log();
|
|
282
|
+
// Subtitle strip
|
|
283
|
+
const W = 80;
|
|
284
|
+
const sub = 'Anti-Hallucination Agent System';
|
|
285
|
+
const sp = Math.max(0, W - sub.length);
|
|
286
|
+
const centred = ' '.repeat(Math.floor(sp / 2)) + sub + ' '.repeat(Math.ceil(sp / 2));
|
|
287
|
+
console.log(` ${c('cyan', '╔' + '═'.repeat(W) + '╗')}`);
|
|
288
|
+
console.log(` ${c('cyan', '║')}${c('gray', centred)}${c('cyan', '║')}`);
|
|
289
|
+
console.log(` ${c('cyan', '╚' + '═'.repeat(W) + '╝')}`);
|
|
290
|
+
console.log();
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// ── Commands ──────────────────────────────────────────────
|
|
294
|
+
function cmdInit(flags) {
|
|
295
|
+
const agentSrc = getKitAgent();
|
|
296
|
+
const targetDir = flags.path ? path.resolve(flags.path) : process.cwd();
|
|
297
|
+
const agentDest = path.join(targetDir, '.agent');
|
|
298
|
+
const dryRun = flags.dryRun || false;
|
|
299
|
+
|
|
300
|
+
// ── Self-install guard ──────────────────────────────────
|
|
301
|
+
if (isSelfInstall(targetDir)) {
|
|
302
|
+
err('Cannot run init/update inside the tribunal-kit package itself.');
|
|
303
|
+
err(`Target: ${targetDir}`);
|
|
304
|
+
err(`Package: ${path.resolve(__dirname, '..')}`);
|
|
305
|
+
console.log();
|
|
306
|
+
dim('This command is designed to install .agent/ into OTHER projects.');
|
|
307
|
+
dim('Run it from the root of the project you want to set up:');
|
|
308
|
+
dim(' cd /path/to/your-project');
|
|
309
|
+
dim(' npx tribunal-kit init');
|
|
310
|
+
console.log();
|
|
311
|
+
process.exit(1);
|
|
312
|
+
}
|
|
313
|
+
// ────────────────────────────────────────────────────────
|
|
314
|
+
|
|
315
|
+
banner();
|
|
316
|
+
|
|
317
|
+
if (dryRun) {
|
|
318
|
+
log(colorize('yellow', ' DRY RUN — no files will be written'));
|
|
319
|
+
console.log();
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Check target exists
|
|
323
|
+
if (!fs.existsSync(targetDir)) {
|
|
324
|
+
err(`Target directory not found: ${targetDir}`);
|
|
325
|
+
process.exit(1);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Check if .agent already exists
|
|
329
|
+
if (fs.existsSync(agentDest) && !flags.force) {
|
|
330
|
+
warn('.agent/ already exists in this project.');
|
|
331
|
+
log(` ${c('gray', '▸')} To refresh or update it, run: ${colorize('white', 'tribunal-kit init --force')}`);
|
|
332
|
+
log(` ${c('gray', '▸')} Or check status with: ${colorize('cyan', 'tribunal-kit status')}`);
|
|
333
|
+
console.log();
|
|
334
|
+
process.exit(0);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (!dryRun && fs.existsSync(agentDest) && flags.force) {
|
|
338
|
+
const subdirs = ['agents', 'workflows', 'skills', 'scripts', '.shared'];
|
|
339
|
+
for (const sub of subdirs) {
|
|
340
|
+
const subPath = path.join(agentDest, sub);
|
|
341
|
+
if (fs.existsSync(subPath)) {
|
|
342
|
+
fs.rmSync(subPath, { recursive: true, force: true });
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Count what we're installing
|
|
348
|
+
const totalFiles = countDir(agentSrc);
|
|
349
|
+
log(` ${c('gray','▸')} Scanning ${c('white', String(totalFiles))} files ${c('gray','→')} ${c('gray', agentDest)}`);
|
|
350
|
+
|
|
351
|
+
try {
|
|
352
|
+
const copied = copyDir(agentSrc, agentDest, dryRun);
|
|
353
|
+
|
|
354
|
+
console.log();
|
|
355
|
+
if (dryRun) {
|
|
356
|
+
ok(`${bold('DRY RUN')} complete — would install ${c('cyan', String(copied))} files`);
|
|
357
|
+
dim(`Target: ${agentDest}`);
|
|
358
|
+
} else {
|
|
359
|
+
// ── Success card — W=62, rows padded by plain-text length ──
|
|
360
|
+
const W = 62;
|
|
361
|
+
const agentsCount = fs.readdirSync(path.join(agentDest, 'agents')).length;
|
|
362
|
+
const workflowsCount = fs.readdirSync(path.join(agentDest, 'workflows')).length;
|
|
363
|
+
const skillsCount = fs.readdirSync(path.join(agentDest, 'skills')).length;
|
|
364
|
+
const scriptsCount = fs.readdirSync(path.join(agentDest, 'scripts')).length;
|
|
365
|
+
|
|
366
|
+
// Stat rows: compute trailing spaces from plain text so right ║ aligns
|
|
367
|
+
const statRow = (icon, label, val, col) => {
|
|
368
|
+
// emoji JS .length===2 == terminal display width 2 ✓
|
|
369
|
+
const plain = ` ${icon} ${label.padEnd(10)}${String(val).padStart(3)} installed`;
|
|
370
|
+
const trail = ' '.repeat(Math.max(0, W - plain.length));
|
|
371
|
+
return ` ${c('cyan','║')} ${icon} ${c('white',label.padEnd(10))}${c(col,String(val).padStart(3))} ${c('gray','installed')}${trail}${c('cyan','║')}`;
|
|
372
|
+
};
|
|
373
|
+
// Plain-text rows (header / blank)
|
|
374
|
+
const plainRow = (text, wrapFn) => {
|
|
375
|
+
const trail = ' '.repeat(Math.max(0, W - text.length));
|
|
376
|
+
return ` ${c('cyan','║')}${wrapFn(text)}${trail}${c('cyan','║')}`;
|
|
377
|
+
};
|
|
378
|
+
// Next-step rows: fixed cmd column + description
|
|
379
|
+
const stepRow = (cmd, desc) => {
|
|
380
|
+
const plain = ` ${cmd.padEnd(16)}${desc}`;
|
|
381
|
+
const trail = ' '.repeat(Math.max(0, W - plain.length));
|
|
382
|
+
return ` ${c('cyan','║')} ${c('white',cmd.padEnd(16))}${c('gray',desc)}${trail}${c('cyan','║')}`;
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
console.log(` ${c('green','✔')} ${bold(c('green','Installation complete'))} ${c('gray','—')} ${c('white',String(copied))} files`);
|
|
386
|
+
console.log(` ${c('gray',' ╰─')} ${c('gray', agentDest)}`);
|
|
387
|
+
console.log();
|
|
388
|
+
console.log(` ${c('cyan', '╔' + '═'.repeat(W) + '╗')}`);
|
|
389
|
+
console.log(plainRow(` What's inside:`, s => c('bold', c('white', s))));
|
|
390
|
+
console.log(` ${c('cyan', '╠' + '═'.repeat(W) + '╣')}`);
|
|
391
|
+
console.log(statRow('🤖', 'Agents', agentsCount, 'magenta'));
|
|
392
|
+
console.log(statRow('⚡', 'Workflows', workflowsCount, 'yellow'));
|
|
393
|
+
console.log(statRow('🧠', 'Skills', skillsCount, 'blue'));
|
|
394
|
+
console.log(statRow('🔧', 'Scripts', scriptsCount, 'green'));
|
|
395
|
+
console.log(` ${c('cyan', '╠' + '═'.repeat(W) + '╣')}`);
|
|
396
|
+
console.log(plainRow('', () => ''));
|
|
397
|
+
console.log(plainRow(` Next steps:`, s => c('gray', s)));
|
|
398
|
+
console.log(stepRow('/generate', 'Generate code with anti-hallucination'));
|
|
399
|
+
console.log(stepRow('/review', 'Audit existing code for issues'));
|
|
400
|
+
console.log(stepRow('/tribunal-full', 'Run all 11 reviewers in parallel'));
|
|
401
|
+
console.log(plainRow('', () => ''));
|
|
402
|
+
console.log(` ${c('cyan', '╚' + '═'.repeat(W) + '╝')}`);
|
|
403
|
+
console.log();
|
|
404
|
+
log(` ${c('gray', '✦ Your AI IDE will pick up changes automatically.')}`);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
console.log();
|
|
408
|
+
} catch (e) {
|
|
409
|
+
err(`Failed to install: ${e.message}`);
|
|
410
|
+
process.exit(1);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function cmdUpdate(flags) {
|
|
415
|
+
// ── Self-install guard (early, before banner) ───────────
|
|
416
|
+
const targetDir = flags.path ? path.resolve(flags.path) : process.cwd();
|
|
417
|
+
if (isSelfInstall(targetDir)) {
|
|
418
|
+
err('Cannot run update inside the tribunal-kit package itself.');
|
|
419
|
+
err(`Target: ${targetDir}`);
|
|
420
|
+
console.log();
|
|
421
|
+
dim('This command is designed to update .agent/ in OTHER projects.');
|
|
422
|
+
dim('Run it from the root of the project you want to update:');
|
|
423
|
+
dim(' cd /path/to/your-project');
|
|
424
|
+
dim(' npx tribunal-kit update');
|
|
425
|
+
console.log();
|
|
426
|
+
process.exit(1);
|
|
427
|
+
}
|
|
428
|
+
// ────────────────────────────────────────────────────────
|
|
429
|
+
|
|
430
|
+
// Update = init with --force
|
|
431
|
+
flags.force = true;
|
|
432
|
+
if (!quiet) {
|
|
433
|
+
log(` ${c('cyan','↻')} ${bold('Updating')} ${c('white','.agent/')} to latest version...`);
|
|
434
|
+
console.log();
|
|
435
|
+
}
|
|
436
|
+
cmdInit(flags);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// ── Async Main Wrapper ───────────────────────────────────
|
|
440
|
+
async function runWithUpdateCheck(command, flags) {
|
|
441
|
+
const shouldSkip = flags.skipUpdateCheck || process.env.TK_SKIP_UPDATE_CHECK === '1';
|
|
442
|
+
|
|
443
|
+
if (!shouldSkip && (command === 'init' || command === 'update')) {
|
|
444
|
+
// Pass through the original args (minus the node/script path)
|
|
445
|
+
const originalArgs = process.argv.slice(2);
|
|
446
|
+
const didReInvoke = await autoUpdateCheck(originalArgs);
|
|
447
|
+
if (didReInvoke) {
|
|
448
|
+
process.exit(0); // Latest version handled it
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Proceed with current version
|
|
453
|
+
switch (command) {
|
|
454
|
+
case 'init':
|
|
455
|
+
cmdInit(flags);
|
|
456
|
+
break;
|
|
457
|
+
case 'update':
|
|
458
|
+
cmdUpdate(flags);
|
|
459
|
+
break;
|
|
460
|
+
case 'status':
|
|
461
|
+
cmdStatus(flags);
|
|
462
|
+
break;
|
|
463
|
+
case 'help':
|
|
464
|
+
case '--help':
|
|
465
|
+
case '-h':
|
|
466
|
+
case null:
|
|
467
|
+
cmdHelp();
|
|
468
|
+
break;
|
|
469
|
+
default:
|
|
470
|
+
err(`Unknown command: "${command}"`);
|
|
471
|
+
console.log();
|
|
472
|
+
dim('Run tribunal-kit --help for usage');
|
|
473
|
+
process.exit(1);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function cmdStatus(flags) {
|
|
478
|
+
const targetDir = flags.path ? path.resolve(flags.path) : process.cwd();
|
|
479
|
+
const agentDest = path.join(targetDir, '.agent');
|
|
480
|
+
|
|
481
|
+
banner();
|
|
482
|
+
|
|
483
|
+
if (!fs.existsSync(agentDest)) {
|
|
484
|
+
log(` ${c('red','✖')} ${bold('Not installed')} in this project`);
|
|
485
|
+
console.log();
|
|
486
|
+
log(` ${c('gray','Run:')} ${c('cyan','npx tribunal-kit init')}`);
|
|
487
|
+
console.log();
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
log(` ${c('green','✔')} ${bold(c('green','Installed'))} ${c('gray','→')} ${c('gray', agentDest)}`);
|
|
492
|
+
console.log();
|
|
493
|
+
|
|
494
|
+
const icons = { agents: '🤖', workflows: '⚡', skills: '🧠', scripts: '🔧' };
|
|
495
|
+
const colors = { agents: 'magenta', workflows: 'yellow', skills: 'blue', scripts: 'green' };
|
|
496
|
+
const subdirs = ['agents', 'workflows', 'skills', 'scripts'];
|
|
497
|
+
for (const sub of subdirs) {
|
|
498
|
+
const subPath = path.join(agentDest, sub);
|
|
499
|
+
if (fs.existsSync(subPath)) {
|
|
500
|
+
const count = fs.readdirSync(subPath).filter(f => !fs.statSync(path.join(subPath, f)).isDirectory()).length;
|
|
501
|
+
log(` ${icons[sub]} ${c(colors[sub], sub.padEnd(12))}${c('white', String(count).padStart(3))} files`);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
console.log();
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
function cmdHelp() {
|
|
508
|
+
banner();
|
|
509
|
+
const cmd = (name, desc) => ` ${c('cyan', name.padEnd(10))} ${c('gray', desc)}`;
|
|
510
|
+
const opt = (flag, desc) => ` ${c('yellow', flag.padEnd(22))} ${c('gray', desc)}`;
|
|
511
|
+
const ex = (s) => ` ${c('gray', '▸')} ${c('white', s)}`;
|
|
512
|
+
|
|
513
|
+
log(bold(' Commands'));
|
|
514
|
+
log(` ${c('gray','─'.repeat(40))}`);
|
|
515
|
+
log(cmd('init', 'Install .agent/ into current project'));
|
|
516
|
+
log(cmd('update', 'Re-install to get latest version'));
|
|
517
|
+
log(cmd('status', 'Check if .agent/ is installed'));
|
|
518
|
+
console.log();
|
|
519
|
+
log(bold(' Options'));
|
|
520
|
+
log(` ${c('gray','─'.repeat(40))}`);
|
|
521
|
+
log(opt('--force', 'Overwrite existing .agent/ folder'));
|
|
522
|
+
log(opt('--path <dir>', 'Install in specific directory'));
|
|
523
|
+
log(opt('--quiet', 'Suppress all output'));
|
|
524
|
+
log(opt('--dry-run', 'Preview actions without executing'));
|
|
525
|
+
log(opt('--skip-update-check', 'Skip auto-update version check'));
|
|
526
|
+
console.log();
|
|
527
|
+
log(bold(' Examples'));
|
|
528
|
+
log(` ${c('gray','─'.repeat(40))}`);
|
|
529
|
+
log(ex('npx tribunal-kit init'));
|
|
530
|
+
log(ex('npx tribunal-kit init --force'));
|
|
531
|
+
log(ex('npx tribunal-kit init --path ./my-app'));
|
|
532
|
+
log(ex('npx tribunal-kit init --dry-run'));
|
|
533
|
+
log(ex('npx tribunal-kit update'));
|
|
534
|
+
log(ex('npx tribunal-kit status'));
|
|
535
|
+
console.log();
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// ── Main ──────────────────────────────────────────────────
|
|
539
|
+
const { command, flags } = parseArgs(process.argv);
|
|
540
|
+
|
|
541
|
+
if (flags.quiet) quiet = true;
|
|
542
|
+
|
|
543
|
+
runWithUpdateCheck(command, flags);
|