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.
Files changed (3) hide show
  1. package/README.md +152 -0
  2. package/dist/index.js +1577 -0
  3. 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();