agent-loadout 0.1.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/README.md +152 -0
- package/dist/index.js +1577 -0
- package/package.json +39 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1577 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
import chalk6 from "chalk";
|
|
6
|
+
|
|
7
|
+
// src/catalog.ts
|
|
8
|
+
var PRESETS = [
|
|
9
|
+
{
|
|
10
|
+
id: "core",
|
|
11
|
+
name: "Core",
|
|
12
|
+
description: "Fundamentals every terminal should have",
|
|
13
|
+
defaultOn: true
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
id: "agent",
|
|
17
|
+
name: "Agent",
|
|
18
|
+
description: "Tools that specifically improve LLM workflows",
|
|
19
|
+
defaultOn: true
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: "media",
|
|
23
|
+
name: "Media",
|
|
24
|
+
description: "Image, audio, and video pipeline tools",
|
|
25
|
+
defaultOn: false
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: "dx",
|
|
29
|
+
name: "DX",
|
|
30
|
+
description: "Developer experience and quality of life",
|
|
31
|
+
defaultOn: false
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: "security",
|
|
35
|
+
name: "Security",
|
|
36
|
+
description: "Scanning and CI hygiene",
|
|
37
|
+
defaultOn: false
|
|
38
|
+
}
|
|
39
|
+
];
|
|
40
|
+
var TOOLS = [
|
|
41
|
+
// ── Core ──────────────────────────────────────────────
|
|
42
|
+
{
|
|
43
|
+
id: "rg",
|
|
44
|
+
name: "ripgrep",
|
|
45
|
+
package: "ripgrep",
|
|
46
|
+
installMethod: "brew",
|
|
47
|
+
verify: "rg --version",
|
|
48
|
+
description: "Fast code search",
|
|
49
|
+
preset: "core"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: "fd",
|
|
53
|
+
name: "fd",
|
|
54
|
+
package: "fd",
|
|
55
|
+
installMethod: "brew",
|
|
56
|
+
verify: "fd --version",
|
|
57
|
+
description: "Fast file finder",
|
|
58
|
+
preset: "core"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
id: "jq",
|
|
62
|
+
name: "jq",
|
|
63
|
+
package: "jq",
|
|
64
|
+
installMethod: "brew",
|
|
65
|
+
verify: "jq --version",
|
|
66
|
+
description: "JSON processor",
|
|
67
|
+
preset: "core"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: "yq",
|
|
71
|
+
name: "yq",
|
|
72
|
+
package: "yq",
|
|
73
|
+
installMethod: "brew",
|
|
74
|
+
verify: "yq --version",
|
|
75
|
+
description: "YAML processor",
|
|
76
|
+
preset: "core"
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
id: "bat",
|
|
80
|
+
name: "bat",
|
|
81
|
+
package: "bat",
|
|
82
|
+
installMethod: "brew",
|
|
83
|
+
verify: "bat --version",
|
|
84
|
+
description: "Cat with syntax highlighting",
|
|
85
|
+
preset: "core"
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
id: "tree",
|
|
89
|
+
name: "tree",
|
|
90
|
+
package: "tree",
|
|
91
|
+
installMethod: "brew",
|
|
92
|
+
verify: "tree --version",
|
|
93
|
+
description: "Directory structure viewer",
|
|
94
|
+
preset: "core"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
id: "gh",
|
|
98
|
+
name: "GitHub CLI",
|
|
99
|
+
package: "gh",
|
|
100
|
+
installMethod: "brew",
|
|
101
|
+
verify: "gh --version",
|
|
102
|
+
description: "GitHub CLI for PRs, issues, releases",
|
|
103
|
+
preset: "core"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: "fzf",
|
|
107
|
+
name: "fzf",
|
|
108
|
+
package: "fzf",
|
|
109
|
+
installMethod: "brew",
|
|
110
|
+
verify: "fzf --version",
|
|
111
|
+
description: "Fuzzy finder",
|
|
112
|
+
preset: "core"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
id: "xh",
|
|
116
|
+
name: "xh",
|
|
117
|
+
package: "xh",
|
|
118
|
+
installMethod: "brew",
|
|
119
|
+
verify: "xh --version",
|
|
120
|
+
description: "Friendly HTTP client",
|
|
121
|
+
preset: "core"
|
|
122
|
+
},
|
|
123
|
+
// ── Agent ─────────────────────────────────────────────
|
|
124
|
+
{
|
|
125
|
+
id: "shellcheck",
|
|
126
|
+
name: "shellcheck",
|
|
127
|
+
package: "shellcheck",
|
|
128
|
+
installMethod: "brew",
|
|
129
|
+
verify: "shellcheck --version",
|
|
130
|
+
description: "Static analysis for shell scripts",
|
|
131
|
+
preset: "agent"
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
id: "ast-grep",
|
|
135
|
+
name: "ast-grep",
|
|
136
|
+
package: "ast-grep",
|
|
137
|
+
installMethod: "brew",
|
|
138
|
+
verify: "sg --version",
|
|
139
|
+
description: "Structural code search/replace",
|
|
140
|
+
preset: "agent"
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
id: "just",
|
|
144
|
+
name: "just",
|
|
145
|
+
package: "just",
|
|
146
|
+
installMethod: "brew",
|
|
147
|
+
verify: "just --version",
|
|
148
|
+
description: "Command runner (agent-readable task menu)",
|
|
149
|
+
preset: "agent"
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
id: "grex",
|
|
153
|
+
name: "grex",
|
|
154
|
+
package: "grex",
|
|
155
|
+
installMethod: "brew",
|
|
156
|
+
verify: "grex --version",
|
|
157
|
+
description: "Generate regex from examples",
|
|
158
|
+
preset: "agent"
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
id: "knip",
|
|
162
|
+
name: "knip",
|
|
163
|
+
package: "knip",
|
|
164
|
+
installMethod: "npm",
|
|
165
|
+
verify: "knip --version",
|
|
166
|
+
description: "Find unused code/deps in TS/JS",
|
|
167
|
+
preset: "agent"
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
id: "sd",
|
|
171
|
+
name: "sd",
|
|
172
|
+
package: "sd",
|
|
173
|
+
installMethod: "brew",
|
|
174
|
+
verify: "sd --version",
|
|
175
|
+
description: "Simpler sed replacement",
|
|
176
|
+
preset: "agent"
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
id: "hyperfine",
|
|
180
|
+
name: "hyperfine",
|
|
181
|
+
package: "hyperfine",
|
|
182
|
+
installMethod: "brew",
|
|
183
|
+
verify: "hyperfine --version",
|
|
184
|
+
description: "CLI benchmarking",
|
|
185
|
+
preset: "agent"
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
id: "tokei",
|
|
189
|
+
name: "tokei",
|
|
190
|
+
package: "tokei",
|
|
191
|
+
installMethod: "brew",
|
|
192
|
+
verify: "tokei --version",
|
|
193
|
+
description: "Code statistics",
|
|
194
|
+
preset: "agent"
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
id: "tldr",
|
|
198
|
+
name: "tldr",
|
|
199
|
+
package: "tldr",
|
|
200
|
+
installMethod: "brew",
|
|
201
|
+
verify: "tldr --version",
|
|
202
|
+
description: "Quick man page summaries",
|
|
203
|
+
preset: "agent"
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
id: "biome",
|
|
207
|
+
name: "biome",
|
|
208
|
+
package: "biome",
|
|
209
|
+
installMethod: "brew",
|
|
210
|
+
verify: "biome --version",
|
|
211
|
+
description: "Lint + format JS/TS",
|
|
212
|
+
preset: "agent"
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
id: "difftastic",
|
|
216
|
+
name: "difftastic",
|
|
217
|
+
package: "difftastic",
|
|
218
|
+
installMethod: "brew",
|
|
219
|
+
verify: "difft --version",
|
|
220
|
+
description: "Structural/AST diff",
|
|
221
|
+
preset: "agent"
|
|
222
|
+
},
|
|
223
|
+
// ── Media ─────────────────────────────────────────────
|
|
224
|
+
{
|
|
225
|
+
id: "ffmpeg",
|
|
226
|
+
name: "ffmpeg",
|
|
227
|
+
package: "ffmpeg",
|
|
228
|
+
installMethod: "brew",
|
|
229
|
+
verify: "ffmpeg -version",
|
|
230
|
+
description: "Audio/video Swiss army knife",
|
|
231
|
+
preset: "media"
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
id: "exiftool",
|
|
235
|
+
name: "exiftool",
|
|
236
|
+
package: "exiftool",
|
|
237
|
+
installMethod: "brew",
|
|
238
|
+
verify: "exiftool -ver",
|
|
239
|
+
description: "Image/media metadata",
|
|
240
|
+
preset: "media"
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
id: "imagemagick",
|
|
244
|
+
name: "ImageMagick",
|
|
245
|
+
package: "imagemagick",
|
|
246
|
+
installMethod: "brew",
|
|
247
|
+
verify: "magick -version",
|
|
248
|
+
description: "Image transforms",
|
|
249
|
+
preset: "media"
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
id: "svgo",
|
|
253
|
+
name: "svgo",
|
|
254
|
+
package: "svgo",
|
|
255
|
+
installMethod: "npm",
|
|
256
|
+
verify: "svgo --version",
|
|
257
|
+
description: "SVG optimiser",
|
|
258
|
+
preset: "media"
|
|
259
|
+
},
|
|
260
|
+
// ── DX ────────────────────────────────────────────────
|
|
261
|
+
{
|
|
262
|
+
id: "eza",
|
|
263
|
+
name: "eza",
|
|
264
|
+
package: "eza",
|
|
265
|
+
installMethod: "brew",
|
|
266
|
+
verify: "eza --version",
|
|
267
|
+
description: "Modern ls replacement",
|
|
268
|
+
preset: "dx"
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
id: "zoxide",
|
|
272
|
+
name: "zoxide",
|
|
273
|
+
package: "zoxide",
|
|
274
|
+
installMethod: "brew",
|
|
275
|
+
verify: "zoxide --version",
|
|
276
|
+
description: "Smarter cd",
|
|
277
|
+
preset: "dx"
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
id: "delta",
|
|
281
|
+
name: "delta",
|
|
282
|
+
package: "git-delta",
|
|
283
|
+
installMethod: "brew",
|
|
284
|
+
verify: "delta --version",
|
|
285
|
+
description: "Better git diffs",
|
|
286
|
+
preset: "dx"
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
id: "glow",
|
|
290
|
+
name: "glow",
|
|
291
|
+
package: "glow",
|
|
292
|
+
installMethod: "brew",
|
|
293
|
+
verify: "glow --version",
|
|
294
|
+
description: "Terminal markdown renderer",
|
|
295
|
+
preset: "dx"
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
id: "mise",
|
|
299
|
+
name: "mise",
|
|
300
|
+
package: "mise",
|
|
301
|
+
installMethod: "brew",
|
|
302
|
+
verify: "mise --version",
|
|
303
|
+
description: "Runtime version manager",
|
|
304
|
+
preset: "dx"
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
id: "watchexec",
|
|
308
|
+
name: "watchexec",
|
|
309
|
+
package: "watchexec",
|
|
310
|
+
installMethod: "brew",
|
|
311
|
+
verify: "watchexec --version",
|
|
312
|
+
description: "Run commands on file change",
|
|
313
|
+
preset: "dx"
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
id: "mkcert",
|
|
317
|
+
name: "mkcert",
|
|
318
|
+
package: "mkcert",
|
|
319
|
+
installMethod: "brew",
|
|
320
|
+
verify: "mkcert --version",
|
|
321
|
+
description: "Local HTTPS certs",
|
|
322
|
+
preset: "dx"
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
id: "lazygit",
|
|
326
|
+
name: "lazygit",
|
|
327
|
+
package: "lazygit",
|
|
328
|
+
installMethod: "brew",
|
|
329
|
+
verify: "lazygit --version",
|
|
330
|
+
description: "TUI git client",
|
|
331
|
+
preset: "dx"
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
id: "dust",
|
|
335
|
+
name: "dust",
|
|
336
|
+
package: "dust",
|
|
337
|
+
installMethod: "brew",
|
|
338
|
+
verify: "dust --version",
|
|
339
|
+
description: "Disk usage tree",
|
|
340
|
+
preset: "dx"
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
id: "btm",
|
|
344
|
+
name: "bottom",
|
|
345
|
+
package: "bottom",
|
|
346
|
+
installMethod: "brew",
|
|
347
|
+
verify: "btm --version",
|
|
348
|
+
description: "System monitor TUI",
|
|
349
|
+
preset: "dx"
|
|
350
|
+
},
|
|
351
|
+
// ── Security ──────────────────────────────────────────
|
|
352
|
+
{
|
|
353
|
+
id: "trivy",
|
|
354
|
+
name: "trivy",
|
|
355
|
+
package: "trivy",
|
|
356
|
+
installMethod: "brew",
|
|
357
|
+
verify: "trivy --version",
|
|
358
|
+
description: "Vulnerability scanner",
|
|
359
|
+
preset: "security"
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
id: "act",
|
|
363
|
+
name: "act",
|
|
364
|
+
package: "act",
|
|
365
|
+
installMethod: "brew",
|
|
366
|
+
verify: "act --version",
|
|
367
|
+
description: "Run GitHub Actions locally",
|
|
368
|
+
preset: "security"
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
id: "gitleaks",
|
|
372
|
+
name: "gitleaks",
|
|
373
|
+
package: "gitleaks",
|
|
374
|
+
installMethod: "brew",
|
|
375
|
+
verify: "gitleaks version",
|
|
376
|
+
description: "Secrets scanner",
|
|
377
|
+
preset: "security"
|
|
378
|
+
}
|
|
379
|
+
];
|
|
380
|
+
function getToolsByPreset(presetId) {
|
|
381
|
+
return TOOLS.filter((t) => t.preset === presetId);
|
|
382
|
+
}
|
|
383
|
+
function getToolsByIds(ids) {
|
|
384
|
+
return TOOLS.filter((t) => ids.includes(t.id));
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// src/brew.ts
|
|
388
|
+
import { writeFile } from "fs/promises";
|
|
389
|
+
import { execa } from "execa";
|
|
390
|
+
import chalk from "chalk";
|
|
391
|
+
|
|
392
|
+
// src/paths.ts
|
|
393
|
+
import { homedir } from "os";
|
|
394
|
+
import { join } from "path";
|
|
395
|
+
import { mkdir } from "fs/promises";
|
|
396
|
+
var BASE_DIR = join(homedir(), ".agent-loadout");
|
|
397
|
+
var SKILLS_DIR = join(homedir(), ".claude", "skills");
|
|
398
|
+
var paths = {
|
|
399
|
+
base: BASE_DIR,
|
|
400
|
+
receipt: join(BASE_DIR, "receipt.json"),
|
|
401
|
+
brewfile: join(BASE_DIR, "Brewfile"),
|
|
402
|
+
skills: SKILLS_DIR
|
|
403
|
+
};
|
|
404
|
+
async function ensureDir() {
|
|
405
|
+
await mkdir(paths.base, { recursive: true });
|
|
406
|
+
}
|
|
407
|
+
async function ensureSkillsDir() {
|
|
408
|
+
await mkdir(paths.skills, { recursive: true });
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// src/brew.ts
|
|
412
|
+
async function checkBrewInstalled() {
|
|
413
|
+
try {
|
|
414
|
+
await execa("brew", ["--version"]);
|
|
415
|
+
return true;
|
|
416
|
+
} catch {
|
|
417
|
+
return false;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
function generateBrewfile(tools) {
|
|
421
|
+
const brewTools = tools.filter((t) => t.installMethod === "brew");
|
|
422
|
+
if (brewTools.length === 0) return "";
|
|
423
|
+
const lines = brewTools.map((t) => `brew "${t.package}"`);
|
|
424
|
+
return lines.join("\n") + "\n";
|
|
425
|
+
}
|
|
426
|
+
async function writeBrewfile(content) {
|
|
427
|
+
await ensureDir();
|
|
428
|
+
await writeFile(paths.brewfile, content);
|
|
429
|
+
}
|
|
430
|
+
async function runBrewBundle() {
|
|
431
|
+
try {
|
|
432
|
+
await execa("brew", ["bundle", "--file", paths.brewfile], {
|
|
433
|
+
stdio: "inherit"
|
|
434
|
+
});
|
|
435
|
+
} catch {
|
|
436
|
+
console.log(
|
|
437
|
+
chalk.yellow(
|
|
438
|
+
"\n Some brew packages may have failed to install. Run 'brew bundle --file ~/.agent-loadout/Brewfile' to retry."
|
|
439
|
+
)
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// src/npm.ts
|
|
445
|
+
import { execa as execa2 } from "execa";
|
|
446
|
+
import chalk2 from "chalk";
|
|
447
|
+
async function checkNpmInstalled() {
|
|
448
|
+
try {
|
|
449
|
+
await execa2("npm", ["--version"]);
|
|
450
|
+
return true;
|
|
451
|
+
} catch {
|
|
452
|
+
return false;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
function getNpmTools(tools) {
|
|
456
|
+
return tools.filter((t) => t.installMethod === "npm");
|
|
457
|
+
}
|
|
458
|
+
function getNpmInstallCommand(tools) {
|
|
459
|
+
const npmTools = getNpmTools(tools);
|
|
460
|
+
if (npmTools.length === 0) return [];
|
|
461
|
+
return npmTools.map((t) => t.package);
|
|
462
|
+
}
|
|
463
|
+
async function runNpmInstall(packages) {
|
|
464
|
+
if (packages.length === 0) return true;
|
|
465
|
+
try {
|
|
466
|
+
await execa2("npm", ["install", "-g", ...packages], { stdio: "inherit" });
|
|
467
|
+
return true;
|
|
468
|
+
} catch {
|
|
469
|
+
console.log(
|
|
470
|
+
chalk2.yellow(
|
|
471
|
+
`
|
|
472
|
+
npm install failed. Try manually: npm install -g ${packages.join(" ")}`
|
|
473
|
+
)
|
|
474
|
+
);
|
|
475
|
+
return false;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// src/verify.ts
|
|
480
|
+
import { execa as execa3 } from "execa";
|
|
481
|
+
import chalk3 from "chalk";
|
|
482
|
+
async function checkTool(tool) {
|
|
483
|
+
try {
|
|
484
|
+
const [cmd, ...args] = tool.verify.split(" ");
|
|
485
|
+
const result = await execa3(cmd, args, { timeout: 5e3 });
|
|
486
|
+
const version = result.stdout.split("\n")[0].trim();
|
|
487
|
+
return { id: tool.id, name: tool.name, installed: true, version };
|
|
488
|
+
} catch {
|
|
489
|
+
return { id: tool.id, name: tool.name, installed: false, version: "" };
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
async function verifyTools(tools) {
|
|
493
|
+
return Promise.all(tools.map(checkTool));
|
|
494
|
+
}
|
|
495
|
+
function printVerifyResults(results) {
|
|
496
|
+
const maxName = Math.max(...results.map((r) => r.name.length));
|
|
497
|
+
for (const r of results) {
|
|
498
|
+
const name = r.name.padEnd(maxName);
|
|
499
|
+
if (r.installed) {
|
|
500
|
+
console.log(` ${chalk3.green("\u2713")} ${name} ${chalk3.dim(r.version)}`);
|
|
501
|
+
} else {
|
|
502
|
+
console.log(` ${chalk3.red("\u2717")} ${name} ${chalk3.red("not found")}`);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
const installed = results.filter((r) => r.installed).length;
|
|
506
|
+
const total = results.length;
|
|
507
|
+
console.log();
|
|
508
|
+
console.log(
|
|
509
|
+
installed === total ? chalk3.green(` All ${total} tools installed.`) : chalk3.yellow(` ${installed}/${total} tools installed.`)
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
function verifyResultsToJson(results) {
|
|
513
|
+
const out = {};
|
|
514
|
+
for (const r of results) {
|
|
515
|
+
if (r.installed) out[r.id] = r.version;
|
|
516
|
+
}
|
|
517
|
+
return out;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// src/receipt.ts
|
|
521
|
+
import { readFile, writeFile as writeFile2 } from "fs/promises";
|
|
522
|
+
import chalk4 from "chalk";
|
|
523
|
+
async function readReceipt() {
|
|
524
|
+
try {
|
|
525
|
+
const raw = await readFile(paths.receipt, "utf-8");
|
|
526
|
+
return JSON.parse(raw);
|
|
527
|
+
} catch (err) {
|
|
528
|
+
if (err instanceof SyntaxError) {
|
|
529
|
+
console.log(
|
|
530
|
+
chalk4.yellow(
|
|
531
|
+
" Warning: ~/.agent-loadout/receipt.json is corrupted. Ignoring."
|
|
532
|
+
)
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
return null;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
async function writeReceipt(receipt) {
|
|
539
|
+
await ensureDir();
|
|
540
|
+
await writeFile2(paths.receipt, JSON.stringify(receipt, null, 2) + "\n");
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// src/ui.ts
|
|
544
|
+
import { checkbox, confirm } from "@inquirer/prompts";
|
|
545
|
+
import chalk5 from "chalk";
|
|
546
|
+
async function selectPresets() {
|
|
547
|
+
return checkbox({
|
|
548
|
+
message: "Which presets do you want to install?",
|
|
549
|
+
choices: PRESETS.map((p) => ({
|
|
550
|
+
name: `${p.name} \u2014 ${p.description}`,
|
|
551
|
+
value: p.id,
|
|
552
|
+
checked: p.defaultOn
|
|
553
|
+
}))
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
async function selectTools(presetIds) {
|
|
557
|
+
const available = presetIds.flatMap(getToolsByPreset);
|
|
558
|
+
const status = await verifyTools(available);
|
|
559
|
+
const installedIds = new Set(
|
|
560
|
+
status.filter((r) => r.installed).map((r) => r.id)
|
|
561
|
+
);
|
|
562
|
+
const selectedIds = await checkbox({
|
|
563
|
+
message: "Toggle individual tools (all selected by default)",
|
|
564
|
+
choices: available.map((t) => {
|
|
565
|
+
const badge = installedIds.has(t.id) ? chalk5.green(" (installed)") : "";
|
|
566
|
+
return {
|
|
567
|
+
name: `${t.name}${badge} \u2014 ${chalk5.dim(t.description)}`,
|
|
568
|
+
value: t.id,
|
|
569
|
+
checked: true
|
|
570
|
+
};
|
|
571
|
+
})
|
|
572
|
+
});
|
|
573
|
+
return TOOLS.filter((t) => selectedIds.includes(t.id));
|
|
574
|
+
}
|
|
575
|
+
function printPreview(tools) {
|
|
576
|
+
const brewfile = generateBrewfile(tools);
|
|
577
|
+
const npmPackages = getNpmInstallCommand(tools);
|
|
578
|
+
console.log();
|
|
579
|
+
if (brewfile) {
|
|
580
|
+
console.log(chalk5.bold("Brewfile:"));
|
|
581
|
+
console.log(chalk5.dim(brewfile));
|
|
582
|
+
console.log(
|
|
583
|
+
chalk5.dim(" \u2192 brew bundle --file ~/.agent-loadout/Brewfile")
|
|
584
|
+
);
|
|
585
|
+
console.log();
|
|
586
|
+
}
|
|
587
|
+
if (npmPackages.length > 0) {
|
|
588
|
+
console.log(chalk5.bold("npm globals:"));
|
|
589
|
+
console.log(chalk5.dim(` \u2192 npm install -g ${npmPackages.join(" ")}`));
|
|
590
|
+
console.log();
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
async function confirmInstall() {
|
|
594
|
+
return confirm({ message: "Install now?", default: true });
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// src/skills.ts
|
|
598
|
+
import { writeFile as writeFile3 } from "fs/promises";
|
|
599
|
+
import { join as join2 } from "path";
|
|
600
|
+
|
|
601
|
+
// src/skills/rg.ts
|
|
602
|
+
var rg_default = `
|
|
603
|
+
# ripgrep (rg) \u2014 Fast code search
|
|
604
|
+
|
|
605
|
+
## When to use
|
|
606
|
+
Search file contents across a codebase. Faster than grep, respects .gitignore by default.
|
|
607
|
+
|
|
608
|
+
## Trusted commands
|
|
609
|
+
- Search for a pattern: \`rg "pattern" path/\`
|
|
610
|
+
- Fixed string (no regex): \`rg -F "exact string"\`
|
|
611
|
+
- File type filter: \`rg "pattern" -t ts\` (ts, js, py, rust, go, etc.)
|
|
612
|
+
- Glob filter: \`rg "pattern" -g '*.tsx'\`
|
|
613
|
+
- Show context lines: \`rg "pattern" -C 3\`
|
|
614
|
+
- Files only (no content): \`rg -l "pattern"\`
|
|
615
|
+
- Count matches: \`rg -c "pattern"\`
|
|
616
|
+
- Case insensitive: \`rg -i "pattern"\`
|
|
617
|
+
|
|
618
|
+
## Output format
|
|
619
|
+
\`file:line:column:matched_text\` \u2014 one match per line, stable and parseable.
|
|
620
|
+
|
|
621
|
+
## Gotchas
|
|
622
|
+
- Skips hidden files and .gitignore'd paths by default. Use \`--hidden\` or \`--no-ignore\` to include them.
|
|
623
|
+
- For literal dots, brackets etc. in patterns, use \`-F\` (fixed string) to avoid regex escaping issues.
|
|
624
|
+
`.trim();
|
|
625
|
+
|
|
626
|
+
// src/skills/fd.ts
|
|
627
|
+
var fd_default = `
|
|
628
|
+
# fd \u2014 Fast file finder
|
|
629
|
+
|
|
630
|
+
## When to use
|
|
631
|
+
Find files by name pattern. Faster than \`find\`, respects .gitignore, sane defaults.
|
|
632
|
+
|
|
633
|
+
## Trusted commands
|
|
634
|
+
- Find by name: \`fd "pattern"\`
|
|
635
|
+
- Find by extension: \`fd -e ts\`
|
|
636
|
+
- Find exact filename: \`fd -g "package.json"\`
|
|
637
|
+
- Include hidden files: \`fd --hidden "pattern"\`
|
|
638
|
+
- Exec on each result: \`fd -e tmp -x rm {}\`
|
|
639
|
+
- Type filter (file/dir/symlink): \`fd -t f "pattern"\`
|
|
640
|
+
|
|
641
|
+
## Output format
|
|
642
|
+
One path per line, relative to search root.
|
|
643
|
+
|
|
644
|
+
## Gotchas
|
|
645
|
+
- Regex by default. Use \`-g\` for glob patterns.
|
|
646
|
+
- Ignores .gitignore'd files by default. Use \`--no-ignore\` to include.
|
|
647
|
+
`.trim();
|
|
648
|
+
|
|
649
|
+
// src/skills/jq.ts
|
|
650
|
+
var jq_default = `
|
|
651
|
+
# jq \u2014 JSON processor
|
|
652
|
+
|
|
653
|
+
## When to use
|
|
654
|
+
Filter, transform, and extract data from JSON. Essential for working with API responses and config files.
|
|
655
|
+
|
|
656
|
+
## Trusted commands
|
|
657
|
+
- Pretty print: \`cat file.json | jq .\`
|
|
658
|
+
- Extract field: \`jq '.fieldName'\`
|
|
659
|
+
- Pick multiple fields: \`jq '{id, name, status}'\`
|
|
660
|
+
- Map over array: \`jq '[.items[] | {id, name}]'\`
|
|
661
|
+
- Count array: \`jq '.items | length'\`
|
|
662
|
+
- Filter: \`jq '.items[] | select(.status == "active")'\`
|
|
663
|
+
- Default for missing: \`jq '.name // "unknown"'\`
|
|
664
|
+
- Validate JSON (fail on error): \`jq -e .\`
|
|
665
|
+
|
|
666
|
+
## Gotchas
|
|
667
|
+
- Use \`-e\` flag to get non-zero exit code on null/false results.
|
|
668
|
+
- Use \`-r\` for raw string output (no quotes).
|
|
669
|
+
- Missing fields return null, not an error. Use \`//\` for defaults.
|
|
670
|
+
`.trim();
|
|
671
|
+
|
|
672
|
+
// src/skills/yq.ts
|
|
673
|
+
var yq_default = `
|
|
674
|
+
# yq \u2014 YAML processor
|
|
675
|
+
|
|
676
|
+
## When to use
|
|
677
|
+
Same as jq but for YAML files. Query, filter, and transform YAML.
|
|
678
|
+
|
|
679
|
+
## Trusted commands
|
|
680
|
+
- Read field: \`yq '.spec.replicas' file.yaml\`
|
|
681
|
+
- Convert YAML to JSON: \`yq -o json file.yaml\`
|
|
682
|
+
- Convert JSON to YAML: \`yq -P file.json\`
|
|
683
|
+
- Update in place: \`yq -i '.version = "2.0"' file.yaml\`
|
|
684
|
+
- Merge files: \`yq eval-all 'select(fi == 0) * select(fi == 1)' a.yaml b.yaml\`
|
|
685
|
+
|
|
686
|
+
## Gotchas
|
|
687
|
+
- There are multiple tools called yq. This refers to the Go version (mikefarah/yq), installed via brew.
|
|
688
|
+
- Use \`-i\` carefully \u2014 it modifies files in place.
|
|
689
|
+
`.trim();
|
|
690
|
+
|
|
691
|
+
// src/skills/bat.ts
|
|
692
|
+
var bat_default = `
|
|
693
|
+
# bat \u2014 Cat with syntax highlighting
|
|
694
|
+
|
|
695
|
+
## When to use
|
|
696
|
+
View file contents with syntax highlighting, line numbers, and git diff indicators.
|
|
697
|
+
|
|
698
|
+
## Trusted commands
|
|
699
|
+
- View file: \`bat file.ts\`
|
|
700
|
+
- Plain output (no decorations): \`bat --plain file.ts\`
|
|
701
|
+
- Show line range: \`bat -r 10:20 file.ts\`
|
|
702
|
+
- Force language: \`bat -l json file.txt\`
|
|
703
|
+
- Use as pager for other commands: \`command | bat -l json\`
|
|
704
|
+
`.trim();
|
|
705
|
+
|
|
706
|
+
// src/skills/tree.ts
|
|
707
|
+
var tree_default = `
|
|
708
|
+
# tree \u2014 Directory structure viewer
|
|
709
|
+
|
|
710
|
+
## When to use
|
|
711
|
+
Visualise directory structure as a tree. Useful for understanding project layout.
|
|
712
|
+
|
|
713
|
+
## Trusted commands
|
|
714
|
+
- Current directory: \`tree\`
|
|
715
|
+
- With depth limit: \`tree -L 2\`
|
|
716
|
+
- Show only directories: \`tree -d\`
|
|
717
|
+
- Ignore patterns: \`tree -I 'node_modules|dist'\`
|
|
718
|
+
- With file sizes: \`tree -sh\`
|
|
719
|
+
`.trim();
|
|
720
|
+
|
|
721
|
+
// src/skills/gh.ts
|
|
722
|
+
var gh_default = `
|
|
723
|
+
# GitHub CLI (gh) \u2014 GitHub from the terminal
|
|
724
|
+
|
|
725
|
+
## When to use
|
|
726
|
+
Create PRs, manage issues, check CI status, manage releases \u2014 all without leaving the terminal.
|
|
727
|
+
|
|
728
|
+
## Trusted commands
|
|
729
|
+
- Create PR: \`gh pr create --title "title" --body "body"\`
|
|
730
|
+
- View PR: \`gh pr view 123\`
|
|
731
|
+
- List PRs: \`gh pr list\`
|
|
732
|
+
- Check CI status: \`gh pr checks 123\`
|
|
733
|
+
- Create issue: \`gh issue create --title "title" --body "body"\`
|
|
734
|
+
- List issues: \`gh issue list\`
|
|
735
|
+
- View repo: \`gh repo view\`
|
|
736
|
+
- API calls: \`gh api repos/owner/repo/pulls/123/comments\`
|
|
737
|
+
- Clone: \`gh repo clone owner/repo\`
|
|
738
|
+
|
|
739
|
+
## Output format
|
|
740
|
+
Supports \`--json\` on most commands for structured output. E.g. \`gh pr list --json number,title,state\`.
|
|
741
|
+
|
|
742
|
+
## Gotchas
|
|
743
|
+
- Requires authentication: \`gh auth login\`
|
|
744
|
+
- \`gh api\` is very powerful for anything not covered by built-in commands.
|
|
745
|
+
`.trim();
|
|
746
|
+
|
|
747
|
+
// src/skills/fzf.ts
|
|
748
|
+
var fzf_default = `
|
|
749
|
+
# fzf \u2014 Fuzzy finder
|
|
750
|
+
|
|
751
|
+
## When to use
|
|
752
|
+
Interactive fuzzy search for files, command history, git branches \u2014 anything with a list.
|
|
753
|
+
|
|
754
|
+
## Trusted commands
|
|
755
|
+
- Find files: \`fzf\`
|
|
756
|
+
- Pipe any list: \`git branch | fzf\`
|
|
757
|
+
- Preview files: \`fzf --preview 'bat --color=always {}'\`
|
|
758
|
+
- With fd: \`fd -t f | fzf\`
|
|
759
|
+
|
|
760
|
+
## Gotchas
|
|
761
|
+
- Primarily interactive \u2014 less useful for non-interactive agent workflows, but great when you're driving.
|
|
762
|
+
`.trim();
|
|
763
|
+
|
|
764
|
+
// src/skills/shellcheck.ts
|
|
765
|
+
var shellcheck_default = `
|
|
766
|
+
# shellcheck \u2014 Static analysis for shell scripts
|
|
767
|
+
|
|
768
|
+
## When to use
|
|
769
|
+
Lint shell scripts (bash, sh, dash) for common mistakes: quoting issues, unset variables, deprecated syntax, portability problems. Agents frequently generate shell scripts \u2014 shellcheck catches errors before they run.
|
|
770
|
+
|
|
771
|
+
## Trusted commands
|
|
772
|
+
- Check a script: \`shellcheck script.sh\`
|
|
773
|
+
- Check with specific shell: \`shellcheck --shell=bash script.sh\`
|
|
774
|
+
- JSON output: \`shellcheck --format=json script.sh\`
|
|
775
|
+
- GCC-style output: \`shellcheck --format=gcc script.sh\`
|
|
776
|
+
- Exclude specific rules: \`shellcheck --exclude=SC2034 script.sh\`
|
|
777
|
+
- Check from stdin: \`echo '#!/bin/bash' | shellcheck -\`
|
|
778
|
+
|
|
779
|
+
## Output format
|
|
780
|
+
Default output is human-readable with line numbers and fix suggestions. Use \`--format=json\` for structured output.
|
|
781
|
+
|
|
782
|
+
## Gotchas
|
|
783
|
+
- Scripts need a shebang (\`#!/bin/bash\`) or use \`--shell=\` flag.
|
|
784
|
+
- SC codes (e.g. SC2086) link to detailed wiki explanations.
|
|
785
|
+
`.trim();
|
|
786
|
+
|
|
787
|
+
// src/skills/ast-grep.ts
|
|
788
|
+
var ast_grep_default = `
|
|
789
|
+
# ast-grep (sg) \u2014 Structural code search/replace
|
|
790
|
+
|
|
791
|
+
## When to use
|
|
792
|
+
Search and replace code using syntax tree patterns instead of regex. Far safer for refactors because it understands code structure.
|
|
793
|
+
|
|
794
|
+
## Trusted commands
|
|
795
|
+
- Search for pattern: \`sg --pattern 'console.log($ARG)' --lang ts\`
|
|
796
|
+
- Search in directory: \`sg --pattern 'useEffect($FN, [])' --lang tsx src/\`
|
|
797
|
+
- Replace: \`sg --pattern 'console.log($ARG)' --rewrite 'logger.info($ARG)' --lang ts\`
|
|
798
|
+
- Interactive replace: \`sg --pattern '$X' --rewrite '$Y' --lang ts --interactive\`
|
|
799
|
+
|
|
800
|
+
## When to prefer over regex
|
|
801
|
+
- Renaming function calls or method calls
|
|
802
|
+
- Finding patterns that span multiple lines
|
|
803
|
+
- Replacing with structural awareness (e.g. moving arguments)
|
|
804
|
+
- Any refactor where brackets/nesting matters
|
|
805
|
+
|
|
806
|
+
## Gotchas
|
|
807
|
+
- The binary is called \`sg\`, not \`ast-grep\`.
|
|
808
|
+
- \`$ARG\` is a metavariable that matches any single node. \`$$$ARGS\` matches multiple.
|
|
809
|
+
- Always specify \`--lang\` for predictable results.
|
|
810
|
+
`.trim();
|
|
811
|
+
|
|
812
|
+
// src/skills/just.ts
|
|
813
|
+
var just_default = `
|
|
814
|
+
# just \u2014 Command runner
|
|
815
|
+
|
|
816
|
+
## When to use
|
|
817
|
+
Define and run project commands via a \`justfile\`. Like \`make\` but modern, with better syntax and no tab issues.
|
|
818
|
+
|
|
819
|
+
## Trusted commands
|
|
820
|
+
- List available recipes: \`just --list\`
|
|
821
|
+
- Run a recipe: \`just recipe-name\`
|
|
822
|
+
- Run with args: \`just deploy staging\`
|
|
823
|
+
- Dry run (show commands): \`just --dry-run recipe-name\`
|
|
824
|
+
- Choose recipe interactively: \`just --choose\` (requires fzf)
|
|
825
|
+
|
|
826
|
+
## Why it matters for agents
|
|
827
|
+
A justfile is an agent-readable task menu. \`just --list\` gives the agent a complete map of available project commands.
|
|
828
|
+
|
|
829
|
+
## Gotchas
|
|
830
|
+
- Uses spaces for indentation (not tabs like Makefile).
|
|
831
|
+
- Recipes are independent by default (no implicit dependencies like make).
|
|
832
|
+
`.trim();
|
|
833
|
+
|
|
834
|
+
// src/skills/grex.ts
|
|
835
|
+
var grex_default = `
|
|
836
|
+
# grex \u2014 Generate regex from examples
|
|
837
|
+
|
|
838
|
+
## When to use
|
|
839
|
+
Generate a regular expression from a set of example strings. Useful when you know what you want to match but not the pattern.
|
|
840
|
+
|
|
841
|
+
## Trusted commands
|
|
842
|
+
- Generate regex: \`grex "foo-123" "bar-456" "baz-789"\`
|
|
843
|
+
- With anchors: \`grex --with-anchors "foo-123" "bar-456"\`
|
|
844
|
+
- Case insensitive: \`grex --ignore-case "Foo" "FOO" "foo"\`
|
|
845
|
+
- Verbose regex: \`grex --verbose "foo-123" "bar-456"\`
|
|
846
|
+
|
|
847
|
+
## Gotchas
|
|
848
|
+
- Output is a regex string, not a replacement. Useful as input for rg, sd, or code.
|
|
849
|
+
`.trim();
|
|
850
|
+
|
|
851
|
+
// src/skills/knip.ts
|
|
852
|
+
var knip_default = `
|
|
853
|
+
# knip \u2014 Find unused code/deps in TS/JS
|
|
854
|
+
|
|
855
|
+
## When to use
|
|
856
|
+
Detect unused files, exports, dependencies, and types in TypeScript/JavaScript projects. Run at project root.
|
|
857
|
+
|
|
858
|
+
## Trusted commands
|
|
859
|
+
- Full scan: \`knip\`
|
|
860
|
+
- Unused files only: \`knip --include files\`
|
|
861
|
+
- Unused exports only: \`knip --include exports\`
|
|
862
|
+
- Unused deps only: \`knip --include dependencies\`
|
|
863
|
+
- JSON output: \`knip --reporter json\`
|
|
864
|
+
|
|
865
|
+
## Gotchas
|
|
866
|
+
- Must be run at project root (where package.json lives).
|
|
867
|
+
- May need a knip.json config for monorepos or non-standard project structures.
|
|
868
|
+
- Some frameworks have plugins (Next.js, Remix, etc.) \u2014 check docs if results seem wrong.
|
|
869
|
+
`.trim();
|
|
870
|
+
|
|
871
|
+
// src/skills/sd.ts
|
|
872
|
+
var sd_default = `
|
|
873
|
+
# sd \u2014 Simpler sed
|
|
874
|
+
|
|
875
|
+
## When to use
|
|
876
|
+
Find and replace in files. Like sed but with intuitive syntax \u2014 no escaping nightmares.
|
|
877
|
+
|
|
878
|
+
## Trusted commands
|
|
879
|
+
- Replace in file: \`sd 'old' 'new' file.ts\`
|
|
880
|
+
- Preview changes: \`sd -p 'old' 'new' file.ts\`
|
|
881
|
+
- Regex replace: \`sd 'v(\\d+)' 'version-$1' file.txt\`
|
|
882
|
+
- Replace across files (with fd): \`fd -e ts -x sd 'old' 'new' {}\`
|
|
883
|
+
|
|
884
|
+
## Gotchas
|
|
885
|
+
- Uses regex by default. Use \`-F\` for fixed/literal strings.
|
|
886
|
+
- Modifies files in place when given a filename. Use \`-p\` to preview first.
|
|
887
|
+
`.trim();
|
|
888
|
+
|
|
889
|
+
// src/skills/hyperfine.ts
|
|
890
|
+
var hyperfine_default = `
|
|
891
|
+
# hyperfine \u2014 CLI benchmarking
|
|
892
|
+
|
|
893
|
+
## When to use
|
|
894
|
+
Benchmark commands to compare performance. Runs commands multiple times and reports stats.
|
|
895
|
+
|
|
896
|
+
## Trusted commands
|
|
897
|
+
- Benchmark single command: \`hyperfine 'command'\`
|
|
898
|
+
- Compare two commands: \`hyperfine 'command-a' 'command-b'\`
|
|
899
|
+
- With warmup: \`hyperfine --warmup 3 'command'\`
|
|
900
|
+
- Export results: \`hyperfine --export-json results.json 'command'\`
|
|
901
|
+
- Min runs: \`hyperfine --min-runs 20 'command'\`
|
|
902
|
+
|
|
903
|
+
## Gotchas
|
|
904
|
+
- Wrap commands in quotes.
|
|
905
|
+
- Use \`--warmup\` for commands that benefit from caching.
|
|
906
|
+
`.trim();
|
|
907
|
+
|
|
908
|
+
// src/skills/tokei.ts
|
|
909
|
+
var tokei_default = `
|
|
910
|
+
# tokei \u2014 Code statistics
|
|
911
|
+
|
|
912
|
+
## When to use
|
|
913
|
+
Get a quick overview of a codebase: languages, lines of code, comments, blanks.
|
|
914
|
+
|
|
915
|
+
## Trusted commands
|
|
916
|
+
- Current directory: \`tokei\`
|
|
917
|
+
- Specific path: \`tokei src/\`
|
|
918
|
+
- JSON output: \`tokei --output json\`
|
|
919
|
+
- Sort by lines: \`tokei --sort lines\`
|
|
920
|
+
|
|
921
|
+
## Output format
|
|
922
|
+
Table with language, files, lines, code, comments, blanks.
|
|
923
|
+
Use \`--output json\` for structured output.
|
|
924
|
+
`.trim();
|
|
925
|
+
|
|
926
|
+
// src/skills/ffmpeg.ts
|
|
927
|
+
var ffmpeg_default = `
|
|
928
|
+
# ffmpeg \u2014 Audio/video Swiss army knife
|
|
929
|
+
|
|
930
|
+
## When to use
|
|
931
|
+
Transcode, trim, concatenate, normalise audio, extract streams, generate waveforms \u2014 anything media.
|
|
932
|
+
|
|
933
|
+
## Trusted commands
|
|
934
|
+
- Inspect media: \`ffprobe -hide_banner -of json -show_format -show_streams file.mp4\`
|
|
935
|
+
- Convert format: \`ffmpeg -i input.wav output.mp3\`
|
|
936
|
+
- Trim: \`ffmpeg -i input.mp4 -ss 00:01:00 -t 00:00:30 -c copy output.mp4\`
|
|
937
|
+
- Extract audio: \`ffmpeg -i video.mp4 -vn -acodec copy audio.aac\`
|
|
938
|
+
- Normalise loudness: \`ffmpeg -i input.wav -af loudnorm=I=-16 output.wav\`
|
|
939
|
+
- Resize video: \`ffmpeg -i input.mp4 -vf scale=1280:720 output.mp4\`
|
|
940
|
+
- Generate waveform: \`ffmpeg -i audio.wav -filter_complex showwavespic=s=1920x200 waveform.png\`
|
|
941
|
+
|
|
942
|
+
## Safety rules
|
|
943
|
+
- Never overwrite input files. Always write to a different output path.
|
|
944
|
+
- Use \`-n\` flag to skip if output exists (never overwrite silently).
|
|
945
|
+
- Always inspect with \`ffprobe\` before transcoding to understand the source.
|
|
946
|
+
|
|
947
|
+
## Gotchas
|
|
948
|
+
- Argument order matters: input flags before \`-i\`, output flags after.
|
|
949
|
+
- \`-c copy\` copies streams without re-encoding (fast, lossless).
|
|
950
|
+
- ffprobe is installed alongside ffmpeg.
|
|
951
|
+
`.trim();
|
|
952
|
+
|
|
953
|
+
// src/skills/exiftool.ts
|
|
954
|
+
var exiftool_default = `
|
|
955
|
+
# exiftool \u2014 Image/media metadata
|
|
956
|
+
|
|
957
|
+
## When to use
|
|
958
|
+
Read, write, and strip metadata (EXIF, IPTC, XMP) from images and media files.
|
|
959
|
+
|
|
960
|
+
## Trusted commands
|
|
961
|
+
- Read all metadata: \`exiftool file.jpg\`
|
|
962
|
+
- Read as JSON: \`exiftool -json file.jpg\`
|
|
963
|
+
- Read specific fields: \`exiftool -Make -Model -DateTimeOriginal file.jpg\`
|
|
964
|
+
- Strip GPS data: \`exiftool -gps:all= file.jpg\`
|
|
965
|
+
- Strip all metadata: \`exiftool -all= file.jpg\`
|
|
966
|
+
- Rename by date: \`exiftool '-FileName<DateTimeOriginal' -d '%Y%m%d_%H%M%S.%%e' dir/\`
|
|
967
|
+
- Batch read: \`exiftool -json dir/\`
|
|
968
|
+
|
|
969
|
+
## Safety rules
|
|
970
|
+
- exiftool creates backup files (.jpg_original) by default when modifying. Use \`-overwrite_original\` only when sure.
|
|
971
|
+
|
|
972
|
+
## Gotchas
|
|
973
|
+
- Field names are case-insensitive.
|
|
974
|
+
- Use \`-json\` for structured output.
|
|
975
|
+
`.trim();
|
|
976
|
+
|
|
977
|
+
// src/skills/imagemagick.ts
|
|
978
|
+
var imagemagick_default = `
|
|
979
|
+
# ImageMagick (magick) \u2014 Image transforms
|
|
980
|
+
|
|
981
|
+
## When to use
|
|
982
|
+
Resize, crop, convert, and manipulate images from the command line.
|
|
983
|
+
|
|
984
|
+
## Trusted commands
|
|
985
|
+
- Convert format: \`magick input.png output.jpg\`
|
|
986
|
+
- Resize: \`magick input.jpg -resize 800x600 output.jpg\`
|
|
987
|
+
- Resize to width (maintain aspect): \`magick input.jpg -resize 800x output.jpg\`
|
|
988
|
+
- Generate thumbnail: \`magick input.jpg -thumbnail 200x200^ -gravity center -extent 200x200 thumb.jpg\`
|
|
989
|
+
- Get dimensions: \`magick identify -format '%wx%h' input.jpg\`
|
|
990
|
+
- Batch convert: \`magick mogrify -format webp *.png\`
|
|
991
|
+
|
|
992
|
+
## Safety rules
|
|
993
|
+
- \`magick mogrify\` modifies files in place. Use \`magick convert\` (or just \`magick in out\`) for safe transforms.
|
|
994
|
+
|
|
995
|
+
## Gotchas
|
|
996
|
+
- The binary is \`magick\` (ImageMagick 7). Older versions used \`convert\`.
|
|
997
|
+
`.trim();
|
|
998
|
+
|
|
999
|
+
// src/skills/svgo.ts
|
|
1000
|
+
var svgo_default = `
|
|
1001
|
+
# svgo \u2014 SVG optimiser
|
|
1002
|
+
|
|
1003
|
+
## When to use
|
|
1004
|
+
Optimise SVG files by removing unnecessary metadata, comments, and reducing precision.
|
|
1005
|
+
|
|
1006
|
+
## Trusted commands
|
|
1007
|
+
- Optimise file: \`svgo input.svg -o output.svg\`
|
|
1008
|
+
- Optimise in place: \`svgo input.svg\`
|
|
1009
|
+
- Folder: \`svgo -f ./icons/ -o ./icons-optimised/\`
|
|
1010
|
+
- Show savings: \`svgo input.svg --pretty\`
|
|
1011
|
+
|
|
1012
|
+
## Gotchas
|
|
1013
|
+
- Default plugins are usually fine. Override with \`--config svgo.config.js\` if needed.
|
|
1014
|
+
- In-place by default when no \`-o\` specified. Pipe or use \`-o\` for safety.
|
|
1015
|
+
`.trim();
|
|
1016
|
+
|
|
1017
|
+
// src/skills/eza.ts
|
|
1018
|
+
var eza_default = `
|
|
1019
|
+
# eza \u2014 Modern ls replacement
|
|
1020
|
+
|
|
1021
|
+
## When to use
|
|
1022
|
+
List files with better defaults: colours, git status, icons, tree view built in.
|
|
1023
|
+
|
|
1024
|
+
## Trusted commands
|
|
1025
|
+
- List: \`eza\`
|
|
1026
|
+
- Long format: \`eza -l\`
|
|
1027
|
+
- With git status: \`eza -l --git\`
|
|
1028
|
+
- Tree view: \`eza --tree\`
|
|
1029
|
+
- Tree with depth limit: \`eza --tree --level 2\`
|
|
1030
|
+
- All files (including hidden): \`eza -la\`
|
|
1031
|
+
`.trim();
|
|
1032
|
+
|
|
1033
|
+
// src/skills/zoxide.ts
|
|
1034
|
+
var zoxide_default = `
|
|
1035
|
+
# zoxide \u2014 Smarter cd
|
|
1036
|
+
|
|
1037
|
+
## When to use
|
|
1038
|
+
Jump to frequently used directories without typing full paths. Learns from your usage.
|
|
1039
|
+
|
|
1040
|
+
## Setup
|
|
1041
|
+
Add to ~/.zshrc: \`eval "$(zoxide init zsh)"\`
|
|
1042
|
+
Then use \`z\` instead of \`cd\`: \`z projects\` jumps to your most-used match.
|
|
1043
|
+
|
|
1044
|
+
## Trusted commands
|
|
1045
|
+
- Jump: \`z partial-dirname\`
|
|
1046
|
+
- Interactive: \`zi\` (requires fzf)
|
|
1047
|
+
- Add path manually: \`zoxide add /path/to/dir\`
|
|
1048
|
+
- List known paths: \`zoxide query --list\`
|
|
1049
|
+
`.trim();
|
|
1050
|
+
|
|
1051
|
+
// src/skills/delta.ts
|
|
1052
|
+
var delta_default = `
|
|
1053
|
+
# delta \u2014 Better git diffs
|
|
1054
|
+
|
|
1055
|
+
## When to use
|
|
1056
|
+
Syntax-highlighted, side-by-side diffs. Configure as your git pager for automatic use.
|
|
1057
|
+
|
|
1058
|
+
## Setup
|
|
1059
|
+
Add to ~/.gitconfig:
|
|
1060
|
+
\`\`\`
|
|
1061
|
+
[core]
|
|
1062
|
+
pager = delta
|
|
1063
|
+
[interactive]
|
|
1064
|
+
diffFilter = delta --color-only
|
|
1065
|
+
\`\`\`
|
|
1066
|
+
|
|
1067
|
+
## Trusted commands
|
|
1068
|
+
- View diff: \`git diff\` (uses delta automatically once configured)
|
|
1069
|
+
- Side by side: set \`delta --side-by-side\` in config
|
|
1070
|
+
|
|
1071
|
+
## Gotchas
|
|
1072
|
+
- The brew package is called \`git-delta\`, but the binary is \`delta\`.
|
|
1073
|
+
`.trim();
|
|
1074
|
+
|
|
1075
|
+
// src/skills/glow.ts
|
|
1076
|
+
var glow_default = `
|
|
1077
|
+
# glow \u2014 Terminal markdown renderer
|
|
1078
|
+
|
|
1079
|
+
## When to use
|
|
1080
|
+
Render markdown files beautifully in the terminal. Great for reading READMEs, docs, changelogs.
|
|
1081
|
+
|
|
1082
|
+
## Trusted commands
|
|
1083
|
+
- Render file: \`glow README.md\`
|
|
1084
|
+
- Render with pager: \`glow -p README.md\`
|
|
1085
|
+
- Render from stdin: \`cat CHANGELOG.md | glow\`
|
|
1086
|
+
`.trim();
|
|
1087
|
+
|
|
1088
|
+
// src/skills/mise.ts
|
|
1089
|
+
var mise_default = `
|
|
1090
|
+
# mise \u2014 Runtime version manager
|
|
1091
|
+
|
|
1092
|
+
## When to use
|
|
1093
|
+
Manage Node, Python, Ruby, Go (etc.) versions per project. Replaces nvm, pyenv, rbenv, asdf.
|
|
1094
|
+
|
|
1095
|
+
## Trusted commands
|
|
1096
|
+
- List available tools: \`mise ls-remote node\`
|
|
1097
|
+
- Install a version: \`mise install node@20\`
|
|
1098
|
+
- Use in current dir: \`mise use node@20\`
|
|
1099
|
+
- Check current: \`mise current\`
|
|
1100
|
+
- Install all from config: \`mise install\`
|
|
1101
|
+
- Trust a config file: \`mise trust\`
|
|
1102
|
+
|
|
1103
|
+
## Config
|
|
1104
|
+
Uses \`.mise.toml\` or \`.tool-versions\` in project root. This ensures deterministic versions for all contributors.
|
|
1105
|
+
|
|
1106
|
+
## Gotchas
|
|
1107
|
+
- Run \`mise activate zsh\` (or bash/fish) in your shell profile for automatic version switching.
|
|
1108
|
+
`.trim();
|
|
1109
|
+
|
|
1110
|
+
// src/skills/watchexec.ts
|
|
1111
|
+
var watchexec_default = `
|
|
1112
|
+
# watchexec \u2014 Run commands on file change
|
|
1113
|
+
|
|
1114
|
+
## When to use
|
|
1115
|
+
Watch files for changes and re-run a command. Language-agnostic alternative to nodemon.
|
|
1116
|
+
|
|
1117
|
+
## Trusted commands
|
|
1118
|
+
- Watch and run: \`watchexec -e ts,tsx "pnpm typecheck"\`
|
|
1119
|
+
- Watch specific path: \`watchexec -w src/ "pnpm test"\`
|
|
1120
|
+
- Clear screen on change: \`watchexec --clear -e ts "pnpm typecheck"\`
|
|
1121
|
+
- Restart long-running process: \`watchexec --restart -e ts "node server.js"\`
|
|
1122
|
+
|
|
1123
|
+
## Gotchas
|
|
1124
|
+
- Use \`-e\` to filter by extension, \`-w\` to filter by directory.
|
|
1125
|
+
- Use \`--restart\` for long-running processes (servers), otherwise it waits for completion.
|
|
1126
|
+
`.trim();
|
|
1127
|
+
|
|
1128
|
+
// src/skills/mkcert.ts
|
|
1129
|
+
var mkcert_default = `
|
|
1130
|
+
# mkcert \u2014 Local HTTPS certificates
|
|
1131
|
+
|
|
1132
|
+
## When to use
|
|
1133
|
+
Generate locally-trusted HTTPS certificates for development. No more "insecure" warnings.
|
|
1134
|
+
|
|
1135
|
+
## Trusted commands
|
|
1136
|
+
- First-time setup: \`mkcert -install\` (installs local CA)
|
|
1137
|
+
- Generate cert: \`mkcert localhost 127.0.0.1 ::1\`
|
|
1138
|
+
- Generate for custom domain: \`mkcert "myapp.local" "*.myapp.local"\`
|
|
1139
|
+
|
|
1140
|
+
## Gotchas
|
|
1141
|
+
- \`mkcert -install\` only needs to run once per machine.
|
|
1142
|
+
- Output is two files: cert.pem and key.pem. Point your dev server at them.
|
|
1143
|
+
`.trim();
|
|
1144
|
+
|
|
1145
|
+
// src/skills/trivy.ts
|
|
1146
|
+
var trivy_default = `
|
|
1147
|
+
# trivy \u2014 Vulnerability scanner
|
|
1148
|
+
|
|
1149
|
+
## When to use
|
|
1150
|
+
Scan filesystems, container images, and code repos for known vulnerabilities.
|
|
1151
|
+
|
|
1152
|
+
## Trusted commands
|
|
1153
|
+
- Scan current directory: \`trivy fs .\`
|
|
1154
|
+
- Scan with JSON output: \`trivy fs --format json .\`
|
|
1155
|
+
- Scan a container image: \`trivy image myapp:latest\`
|
|
1156
|
+
- Only critical/high: \`trivy fs --severity CRITICAL,HIGH .\`
|
|
1157
|
+
- Scan for secrets: \`trivy fs --scanners secret .\`
|
|
1158
|
+
|
|
1159
|
+
## Gotchas
|
|
1160
|
+
- First run downloads a vulnerability database (can be slow).
|
|
1161
|
+
- Use \`--format json\` for structured output.
|
|
1162
|
+
`.trim();
|
|
1163
|
+
|
|
1164
|
+
// src/skills/act.ts
|
|
1165
|
+
var act_default = `
|
|
1166
|
+
# act \u2014 Run GitHub Actions locally
|
|
1167
|
+
|
|
1168
|
+
## When to use
|
|
1169
|
+
Test GitHub Actions workflows without pushing. Runs workflows in Docker containers locally.
|
|
1170
|
+
|
|
1171
|
+
## Trusted commands
|
|
1172
|
+
- List available workflows: \`act -l\`
|
|
1173
|
+
- Run default workflow: \`act\`
|
|
1174
|
+
- Run specific event: \`act push\`
|
|
1175
|
+
- Run specific job: \`act -j build\`
|
|
1176
|
+
- Dry run: \`act -n\`
|
|
1177
|
+
|
|
1178
|
+
## Gotchas
|
|
1179
|
+
- Requires Docker to be running.
|
|
1180
|
+
- Not all GitHub Actions features are supported locally (secrets, some contexts).
|
|
1181
|
+
- Use \`-n\` (dry run) first to see what would happen.
|
|
1182
|
+
`.trim();
|
|
1183
|
+
|
|
1184
|
+
// src/skills/xh.ts
|
|
1185
|
+
var xh_default = `
|
|
1186
|
+
# xh \u2014 Friendly HTTP client
|
|
1187
|
+
|
|
1188
|
+
## When to use
|
|
1189
|
+
Send HTTP requests from the terminal. Cleaner syntax than curl, JSON-first, coloured output.
|
|
1190
|
+
|
|
1191
|
+
## Trusted commands
|
|
1192
|
+
- GET request: \`xh get httpbin.org/json\`
|
|
1193
|
+
- POST JSON body: \`xh post httpbin.org/post name=Alice age:=30\`
|
|
1194
|
+
- Set headers: \`xh get api.example.com Authorization:"Bearer $TOKEN"\`
|
|
1195
|
+
- Force JSON output: \`xh --json get api.example.com\`
|
|
1196
|
+
- Follow redirects: \`xh --follow get example.com\`
|
|
1197
|
+
- Save response to file: \`xh get example.com/file.zip > file.zip\`
|
|
1198
|
+
- Show request/response headers: \`xh --print=hHbB get httpbin.org/get\`
|
|
1199
|
+
|
|
1200
|
+
## Gotchas
|
|
1201
|
+
- \`key=value\` sends as JSON string; \`key:=value\` sends raw JSON (numbers, bools, arrays).
|
|
1202
|
+
- Defaults to HTTPS if scheme is omitted.
|
|
1203
|
+
- Use \`--check-status\` to exit non-zero on 4xx/5xx responses.
|
|
1204
|
+
`.trim();
|
|
1205
|
+
|
|
1206
|
+
// src/skills/tldr.ts
|
|
1207
|
+
var tldr_default = `
|
|
1208
|
+
# tldr \u2014 Quick man page summaries
|
|
1209
|
+
|
|
1210
|
+
## When to use
|
|
1211
|
+
Get practical, example-driven command summaries without reading full man pages.
|
|
1212
|
+
|
|
1213
|
+
## Trusted commands
|
|
1214
|
+
- Look up a command: \`tldr rg\`
|
|
1215
|
+
- Look up a subcommand: \`tldr git commit\`
|
|
1216
|
+
- Update the local cache: \`tldr --update\`
|
|
1217
|
+
- List all available pages: \`tldr --list\`
|
|
1218
|
+
- Search for a topic: \`tldr --search "compress files"\`
|
|
1219
|
+
|
|
1220
|
+
## Gotchas
|
|
1221
|
+
- First run requires internet to fetch the page cache. Run \`tldr --update\` after install.
|
|
1222
|
+
- Not every obscure tool has a page \u2014 fall back to \`man\` or \`--help\` when missing.
|
|
1223
|
+
- Pages are community-written; they cover common usage, not edge cases.
|
|
1224
|
+
`.trim();
|
|
1225
|
+
|
|
1226
|
+
// src/skills/biome.ts
|
|
1227
|
+
var biome_default = `
|
|
1228
|
+
# biome \u2014 Lint and format JS/TS
|
|
1229
|
+
|
|
1230
|
+
## When to use
|
|
1231
|
+
Fast, zero-config linter and formatter for JavaScript/TypeScript projects. Replaces ESLint + Prettier in one binary.
|
|
1232
|
+
|
|
1233
|
+
## Trusted commands
|
|
1234
|
+
- Check (lint + format): \`biome check .\`
|
|
1235
|
+
- Format only (write): \`biome format --write .\`
|
|
1236
|
+
- Lint only: \`biome lint .\`
|
|
1237
|
+
- CI mode (no writes, exits 1 on issues): \`biome ci .\`
|
|
1238
|
+
- Init config: \`biome init\`
|
|
1239
|
+
- Check single file: \`biome check src/index.ts\`
|
|
1240
|
+
|
|
1241
|
+
## Gotchas
|
|
1242
|
+
- Requires a \`biome.json\` config or \`--config-path\` flag; \`biome init\` generates a sensible default.
|
|
1243
|
+
- Not 100% compatible with all ESLint rules \u2014 check the migration guide when switching existing projects.
|
|
1244
|
+
- \`biome check\` is read-only by default; pass \`--write\` to apply fixes.
|
|
1245
|
+
`.trim();
|
|
1246
|
+
|
|
1247
|
+
// src/skills/difftastic.ts
|
|
1248
|
+
var difftastic_default = `
|
|
1249
|
+
# difftastic (difft) \u2014 Structural/AST diff
|
|
1250
|
+
|
|
1251
|
+
## When to use
|
|
1252
|
+
Compare files by syntax tree, not line-by-line. Understands code structure so renaming a variable shows intent, not noise.
|
|
1253
|
+
|
|
1254
|
+
## Trusted commands
|
|
1255
|
+
- Diff two files: \`difft old.ts new.ts\`
|
|
1256
|
+
- Use as git diff driver: \`GIT_EXTERNAL_DIFF=difft git diff\`
|
|
1257
|
+
- Set as permanent git difftool: \`git config --global diff.external difft\`
|
|
1258
|
+
- Diff staged changes: \`GIT_EXTERNAL_DIFF=difft git diff --cached\`
|
|
1259
|
+
- Plain text mode (no syntax): \`difft --display side-by-side-show-both old.txt new.txt\`
|
|
1260
|
+
|
|
1261
|
+
## Gotchas
|
|
1262
|
+
- Supports most languages automatically via file extension detection.
|
|
1263
|
+
- Output is always side-by-side; pipe width matters \u2014 use a wide terminal.
|
|
1264
|
+
- Falls back to line-diff for unsupported file types.
|
|
1265
|
+
`.trim();
|
|
1266
|
+
|
|
1267
|
+
// src/skills/lazygit.ts
|
|
1268
|
+
var lazygit_default = `
|
|
1269
|
+
# lazygit \u2014 TUI git client
|
|
1270
|
+
|
|
1271
|
+
## When to use
|
|
1272
|
+
Interactive terminal UI for git \u2014 stage hunks, commit, branch, rebase, and push without typing git commands.
|
|
1273
|
+
|
|
1274
|
+
## Trusted commands
|
|
1275
|
+
- Open in current repo: \`lazygit\`
|
|
1276
|
+
- Open for a specific path: \`lazygit -p /path/to/repo\`
|
|
1277
|
+
|
|
1278
|
+
## Key bindings (inside lazygit)
|
|
1279
|
+
- \`?\` \u2014 show full keybinding help
|
|
1280
|
+
- \`space\` \u2014 stage/unstage file or hunk
|
|
1281
|
+
- \`c\` \u2014 commit staged changes
|
|
1282
|
+
- \`P\` \u2014 push
|
|
1283
|
+
- \`p\` \u2014 pull
|
|
1284
|
+
- \`b\` \u2014 branch panel; \`n\` to create, \`space\` to checkout
|
|
1285
|
+
- \`R\` \u2014 interactive rebase
|
|
1286
|
+
- \`q\` \u2014 quit
|
|
1287
|
+
|
|
1288
|
+
## Gotchas
|
|
1289
|
+
- Requires git to be installed (it's a UI wrapper, not a replacement).
|
|
1290
|
+
- Config lives at \`~/.config/lazygit/config.yml\`.
|
|
1291
|
+
- Mouse support is on by default \u2014 click panels to navigate.
|
|
1292
|
+
`.trim();
|
|
1293
|
+
|
|
1294
|
+
// src/skills/dust.ts
|
|
1295
|
+
var dust_default = `
|
|
1296
|
+
# dust \u2014 Disk usage tree
|
|
1297
|
+
|
|
1298
|
+
## When to use
|
|
1299
|
+
Visualise what's eating disk space in a directory tree. Faster and more readable than \`du\`.
|
|
1300
|
+
|
|
1301
|
+
## Trusted commands
|
|
1302
|
+
- Current directory overview: \`dust\`
|
|
1303
|
+
- Limit tree depth: \`dust -d 2\`
|
|
1304
|
+
- Show top N entries: \`dust -n 20\`
|
|
1305
|
+
- Reverse order (smallest first): \`dust -r\`
|
|
1306
|
+
- Specific path: \`dust /var/log\`
|
|
1307
|
+
- Ignore a directory: \`dust --ignore-directory node_modules\`
|
|
1308
|
+
|
|
1309
|
+
## Gotchas
|
|
1310
|
+
- Output is proportional bars + sizes; percentages are relative to the scanned root, not total disk.
|
|
1311
|
+
- Use \`-d 1\` for a quick top-level summary before drilling down.
|
|
1312
|
+
- Symlinks are not followed by default \u2014 add \`-L\` to follow them.
|
|
1313
|
+
`.trim();
|
|
1314
|
+
|
|
1315
|
+
// src/skills/btm.ts
|
|
1316
|
+
var btm_default = `
|
|
1317
|
+
# bottom (btm) \u2014 System monitor
|
|
1318
|
+
|
|
1319
|
+
## When to use
|
|
1320
|
+
Real-time TUI system monitor \u2014 CPU, memory, network, disk, and process list in one view.
|
|
1321
|
+
|
|
1322
|
+
## Trusted commands
|
|
1323
|
+
- Open monitor: \`btm\`
|
|
1324
|
+
- Basic mode (simpler layout): \`btm --basic\`
|
|
1325
|
+
- Show all processes (no grouping): \`btm --process_command\`
|
|
1326
|
+
|
|
1327
|
+
## Key bindings (inside btm)
|
|
1328
|
+
- \`?\` \u2014 show help overlay
|
|
1329
|
+
- \`q\` / \`Ctrl+C\` \u2014 quit
|
|
1330
|
+
- \`dd\` \u2014 kill selected process
|
|
1331
|
+
- \`s\` \u2014 sort column selector
|
|
1332
|
+
- \`/\` \u2014 filter processes by name
|
|
1333
|
+
- \`Tab\` \u2014 switch between widgets
|
|
1334
|
+
- \`e\` \u2014 expand focused widget to full screen
|
|
1335
|
+
|
|
1336
|
+
## Gotchas
|
|
1337
|
+
- Config lives at \`~/.config/bottom/bottom.toml\` \u2014 customise colours and layout there.
|
|
1338
|
+
- \`--basic\` mode is useful in constrained terminals or for quick checks.
|
|
1339
|
+
`.trim();
|
|
1340
|
+
|
|
1341
|
+
// src/skills/gitleaks.ts
|
|
1342
|
+
var gitleaks_default = `
|
|
1343
|
+
# gitleaks \u2014 Secrets scanner
|
|
1344
|
+
|
|
1345
|
+
## When to use
|
|
1346
|
+
Detect hardcoded secrets (API keys, tokens, passwords) in git repos before they reach remote.
|
|
1347
|
+
|
|
1348
|
+
## Trusted commands
|
|
1349
|
+
- Scan entire repo history: \`gitleaks detect\`
|
|
1350
|
+
- Scan only staged changes (pre-commit): \`gitleaks protect --staged\`
|
|
1351
|
+
- Scan a specific path: \`gitleaks detect --source /path/to/repo\`
|
|
1352
|
+
- Output as JSON: \`gitleaks detect --report-format json --report-path leaks.json\`
|
|
1353
|
+
- Verbose (show findings inline): \`gitleaks detect -v\`
|
|
1354
|
+
|
|
1355
|
+
## CI usage
|
|
1356
|
+
Add to a pre-commit hook or CI step:
|
|
1357
|
+
\`\`\`sh
|
|
1358
|
+
gitleaks protect --staged # pre-commit hook
|
|
1359
|
+
gitleaks detect # CI full scan
|
|
1360
|
+
\`\`\`
|
|
1361
|
+
|
|
1362
|
+
## Gotchas
|
|
1363
|
+
- Findings include file, line, rule, and matched secret fragment \u2014 review before dismissing.
|
|
1364
|
+
- Use a \`.gitleaksignore\` file to whitelist known false positives.
|
|
1365
|
+
- Does not redact secrets from history \u2014 use \`git filter-repo\` to remove them.
|
|
1366
|
+
`.trim();
|
|
1367
|
+
|
|
1368
|
+
// src/skills.ts
|
|
1369
|
+
var PREFIX = "agent-loadout";
|
|
1370
|
+
var SKILL_CONTENT = {
|
|
1371
|
+
rg: rg_default,
|
|
1372
|
+
fd: fd_default,
|
|
1373
|
+
jq: jq_default,
|
|
1374
|
+
yq: yq_default,
|
|
1375
|
+
bat: bat_default,
|
|
1376
|
+
tree: tree_default,
|
|
1377
|
+
gh: gh_default,
|
|
1378
|
+
fzf: fzf_default,
|
|
1379
|
+
shellcheck: shellcheck_default,
|
|
1380
|
+
"ast-grep": ast_grep_default,
|
|
1381
|
+
just: just_default,
|
|
1382
|
+
grex: grex_default,
|
|
1383
|
+
knip: knip_default,
|
|
1384
|
+
sd: sd_default,
|
|
1385
|
+
hyperfine: hyperfine_default,
|
|
1386
|
+
tokei: tokei_default,
|
|
1387
|
+
ffmpeg: ffmpeg_default,
|
|
1388
|
+
exiftool: exiftool_default,
|
|
1389
|
+
imagemagick: imagemagick_default,
|
|
1390
|
+
svgo: svgo_default,
|
|
1391
|
+
eza: eza_default,
|
|
1392
|
+
zoxide: zoxide_default,
|
|
1393
|
+
delta: delta_default,
|
|
1394
|
+
glow: glow_default,
|
|
1395
|
+
mise: mise_default,
|
|
1396
|
+
watchexec: watchexec_default,
|
|
1397
|
+
mkcert: mkcert_default,
|
|
1398
|
+
trivy: trivy_default,
|
|
1399
|
+
act: act_default,
|
|
1400
|
+
xh: xh_default,
|
|
1401
|
+
tldr: tldr_default,
|
|
1402
|
+
biome: biome_default,
|
|
1403
|
+
difftastic: difftastic_default,
|
|
1404
|
+
lazygit: lazygit_default,
|
|
1405
|
+
dust: dust_default,
|
|
1406
|
+
btm: btm_default,
|
|
1407
|
+
gitleaks: gitleaks_default
|
|
1408
|
+
};
|
|
1409
|
+
function skillPath(toolId) {
|
|
1410
|
+
return join2(paths.skills, `${PREFIX}-${toolId}.md`);
|
|
1411
|
+
}
|
|
1412
|
+
async function writeSkills(tools) {
|
|
1413
|
+
await ensureSkillsDir();
|
|
1414
|
+
let written = 0;
|
|
1415
|
+
for (const tool of tools) {
|
|
1416
|
+
const content = SKILL_CONTENT[tool.id];
|
|
1417
|
+
if (!content) continue;
|
|
1418
|
+
await writeFile3(skillPath(tool.id), content + "\n");
|
|
1419
|
+
written++;
|
|
1420
|
+
}
|
|
1421
|
+
return written;
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
// src/index.ts
|
|
1425
|
+
var program = new Command();
|
|
1426
|
+
program.name("agent-loadout").description("One command to load out your terminal for agentic coding").version("0.1.0");
|
|
1427
|
+
process.on("SIGINT", () => {
|
|
1428
|
+
console.log(chalk6.dim("\n Cancelled."));
|
|
1429
|
+
process.exit(0);
|
|
1430
|
+
});
|
|
1431
|
+
program.command("install", { isDefault: true }).description("Install tools (interactive by default)").option("--preset <presets...>", "Install specific presets without prompts").option("--all", "Install everything").option("--apply", "Actually run the install (default is preview only)").action(async (opts) => {
|
|
1432
|
+
const hasBrew = await checkBrewInstalled();
|
|
1433
|
+
if (!hasBrew) {
|
|
1434
|
+
console.log(chalk6.red("Homebrew is required but not installed."));
|
|
1435
|
+
console.log(
|
|
1436
|
+
chalk6.dim(
|
|
1437
|
+
'Install it: https://brew.sh \u2192 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"'
|
|
1438
|
+
)
|
|
1439
|
+
);
|
|
1440
|
+
process.exit(1);
|
|
1441
|
+
}
|
|
1442
|
+
let tools;
|
|
1443
|
+
if (opts.all) {
|
|
1444
|
+
tools = TOOLS;
|
|
1445
|
+
} else if (opts.preset) {
|
|
1446
|
+
const rawIds = opts.preset;
|
|
1447
|
+
const validIds = PRESETS.map((p) => p.id);
|
|
1448
|
+
const invalid = rawIds.filter((p) => !validIds.includes(p));
|
|
1449
|
+
if (invalid.length > 0) {
|
|
1450
|
+
console.log(
|
|
1451
|
+
chalk6.red(
|
|
1452
|
+
`Unknown preset${invalid.length > 1 ? "s" : ""}: ${invalid.map((p) => `'${p}'`).join(", ")}. Available: ${validIds.join(", ")}`
|
|
1453
|
+
)
|
|
1454
|
+
);
|
|
1455
|
+
process.exit(1);
|
|
1456
|
+
}
|
|
1457
|
+
const presetIds = rawIds;
|
|
1458
|
+
tools = TOOLS.filter((t) => presetIds.includes(t.preset));
|
|
1459
|
+
} else {
|
|
1460
|
+
const presetIds = await selectPresets();
|
|
1461
|
+
if (presetIds.length === 0) {
|
|
1462
|
+
console.log(chalk6.dim("No presets selected. Nothing to install."));
|
|
1463
|
+
return;
|
|
1464
|
+
}
|
|
1465
|
+
tools = await selectTools(presetIds);
|
|
1466
|
+
if (tools.length === 0) {
|
|
1467
|
+
console.log(chalk6.dim("No tools selected. Nothing to install."));
|
|
1468
|
+
return;
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
printPreview(tools);
|
|
1472
|
+
if (!opts.apply && (opts.all || opts.preset)) {
|
|
1473
|
+
console.log(
|
|
1474
|
+
chalk6.yellow("Dry run \u2014 add --apply to install. Example:")
|
|
1475
|
+
);
|
|
1476
|
+
console.log(
|
|
1477
|
+
chalk6.dim(
|
|
1478
|
+
` npx agent-loadout install ${opts.all ? "--all" : `--preset ${opts.preset.join(" ")}`} --apply`
|
|
1479
|
+
)
|
|
1480
|
+
);
|
|
1481
|
+
return;
|
|
1482
|
+
}
|
|
1483
|
+
if (!opts.all && !opts.preset) {
|
|
1484
|
+
const proceed = await confirmInstall();
|
|
1485
|
+
if (!proceed) {
|
|
1486
|
+
console.log(chalk6.dim("Cancelled."));
|
|
1487
|
+
return;
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
const npmPackages = getNpmInstallCommand(tools);
|
|
1491
|
+
if (npmPackages.length > 0) {
|
|
1492
|
+
const hasNpm = await checkNpmInstalled();
|
|
1493
|
+
if (!hasNpm) {
|
|
1494
|
+
console.log(
|
|
1495
|
+
chalk6.yellow(
|
|
1496
|
+
`npm is required for: ${npmPackages.join(", ")}. Skipping npm packages (install Node.js to include them).`
|
|
1497
|
+
)
|
|
1498
|
+
);
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
console.log();
|
|
1502
|
+
const brewfile = generateBrewfile(tools);
|
|
1503
|
+
if (brewfile) {
|
|
1504
|
+
console.log(chalk6.bold("Installing brew packages..."));
|
|
1505
|
+
await writeBrewfile(brewfile);
|
|
1506
|
+
await runBrewBundle();
|
|
1507
|
+
}
|
|
1508
|
+
if (npmPackages.length > 0 && await checkNpmInstalled()) {
|
|
1509
|
+
console.log(chalk6.bold("Installing npm globals..."));
|
|
1510
|
+
await runNpmInstall(npmPackages);
|
|
1511
|
+
}
|
|
1512
|
+
console.log();
|
|
1513
|
+
console.log(chalk6.bold("Verifying..."));
|
|
1514
|
+
const results = await verifyTools(tools);
|
|
1515
|
+
printVerifyResults(results);
|
|
1516
|
+
const skillCount = await writeSkills(tools);
|
|
1517
|
+
if (skillCount > 0) {
|
|
1518
|
+
console.log(
|
|
1519
|
+
chalk6.dim(`
|
|
1520
|
+
${skillCount} skill files written to ~/.claude/skills/`)
|
|
1521
|
+
);
|
|
1522
|
+
}
|
|
1523
|
+
await writeReceipt({
|
|
1524
|
+
selections: tools.map((t) => t.id),
|
|
1525
|
+
installed: verifyResultsToJson(results),
|
|
1526
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1527
|
+
});
|
|
1528
|
+
console.log(chalk6.dim(" Receipt saved to ~/.agent-loadout/receipt.json"));
|
|
1529
|
+
});
|
|
1530
|
+
program.command("verify").alias("doctor").description("Check which tools are installed").option("--json", "Output as JSON").action(async (opts) => {
|
|
1531
|
+
const receipt = await readReceipt();
|
|
1532
|
+
const toolIds = receipt?.selections ?? TOOLS.map((t) => t.id);
|
|
1533
|
+
const tools = getToolsByIds(toolIds);
|
|
1534
|
+
const results = await verifyTools(tools);
|
|
1535
|
+
const installed = results.filter((r) => r.installed).length;
|
|
1536
|
+
const allInstalled = installed === results.length;
|
|
1537
|
+
if (opts.json) {
|
|
1538
|
+
console.log(
|
|
1539
|
+
JSON.stringify(
|
|
1540
|
+
{
|
|
1541
|
+
ok: allInstalled,
|
|
1542
|
+
installed,
|
|
1543
|
+
total: results.length,
|
|
1544
|
+
tools: results
|
|
1545
|
+
},
|
|
1546
|
+
null,
|
|
1547
|
+
2
|
|
1548
|
+
)
|
|
1549
|
+
);
|
|
1550
|
+
} else {
|
|
1551
|
+
printVerifyResults(results);
|
|
1552
|
+
}
|
|
1553
|
+
process.exit(allInstalled ? 0 : 1);
|
|
1554
|
+
});
|
|
1555
|
+
program.command("list").description("Print the tool catalog").option("--json", "Output as JSON").action((opts) => {
|
|
1556
|
+
if (opts.json) {
|
|
1557
|
+
console.log(JSON.stringify({ presets: PRESETS, tools: TOOLS }, null, 2));
|
|
1558
|
+
return;
|
|
1559
|
+
}
|
|
1560
|
+
for (const preset of PRESETS) {
|
|
1561
|
+
const marker = preset.defaultOn ? chalk6.green("\u25CF") : chalk6.dim("\u25CB");
|
|
1562
|
+
console.log(
|
|
1563
|
+
`
|
|
1564
|
+
${marker} ${chalk6.bold(preset.name)} \u2014 ${preset.description}`
|
|
1565
|
+
);
|
|
1566
|
+
const presetTools = TOOLS.filter((t) => t.preset === preset.id);
|
|
1567
|
+
const maxName = Math.max(...presetTools.map((t) => t.name.length));
|
|
1568
|
+
for (const t of presetTools) {
|
|
1569
|
+
const method = t.installMethod === "npm" ? chalk6.cyan("npm") : chalk6.yellow("brew");
|
|
1570
|
+
console.log(
|
|
1571
|
+
` ${t.name.padEnd(maxName)} ${method} ${chalk6.dim(t.description)}`
|
|
1572
|
+
);
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
console.log();
|
|
1576
|
+
});
|
|
1577
|
+
program.parse();
|