agent-loadout 0.1.0 → 1.0.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 +84 -51
- package/dist/catalog-PTLCQEDW.js +17 -0
- package/dist/chunk-JYIPAISH.js +723 -0
- package/dist/index.js +877 -517
- package/package.json +7 -4
package/dist/index.js
CHANGED
|
@@ -1,392 +1,79 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
PRESETS,
|
|
4
|
+
TOOLS,
|
|
5
|
+
getToolsByIds,
|
|
6
|
+
getToolsByPreset,
|
|
7
|
+
validateToolIds
|
|
8
|
+
} from "./chunk-JYIPAISH.js";
|
|
2
9
|
|
|
3
10
|
// src/index.ts
|
|
4
11
|
import { Command } from "commander";
|
|
5
|
-
import
|
|
12
|
+
import chalk10 from "chalk";
|
|
6
13
|
|
|
7
|
-
// src/
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
|
14
|
+
// src/platform.ts
|
|
15
|
+
import { execa } from "execa";
|
|
16
|
+
async function hasCommand(cmd) {
|
|
17
|
+
try {
|
|
18
|
+
await execa(cmd, ["--version"], { timeout: 5e3 });
|
|
19
|
+
return true;
|
|
20
|
+
} catch {
|
|
21
|
+
return false;
|
|
38
22
|
}
|
|
39
|
-
|
|
40
|
-
var
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
{
|
|
79
|
-
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"
|
|
23
|
+
}
|
|
24
|
+
var CANDIDATES = {
|
|
25
|
+
darwin: ["brew", "npm"],
|
|
26
|
+
linux: ["apt", "npm", "cargo"],
|
|
27
|
+
win32: ["scoop", "npm", "cargo"]
|
|
28
|
+
};
|
|
29
|
+
async function detectPlatform() {
|
|
30
|
+
const platform = process.platform;
|
|
31
|
+
const arch = process.arch === "arm64" ? "arm64" : "x64";
|
|
32
|
+
const candidates = CANDIDATES[platform] ?? [];
|
|
33
|
+
const checks = await Promise.all(
|
|
34
|
+
candidates.map(async (mgr) => {
|
|
35
|
+
const cmd = mgr === "apt" ? "apt-get" : mgr;
|
|
36
|
+
const found = await hasCommand(cmd);
|
|
37
|
+
return found ? mgr : null;
|
|
38
|
+
})
|
|
39
|
+
);
|
|
40
|
+
const available = checks.filter((m) => m !== null);
|
|
41
|
+
if (!available.includes("npm")) available.push("npm");
|
|
42
|
+
return { platform, arch, available };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// src/resolve.ts
|
|
46
|
+
function resolveInstallPlan(tools, info) {
|
|
47
|
+
const resolved = [];
|
|
48
|
+
const skipped = [];
|
|
49
|
+
for (const tool of tools) {
|
|
50
|
+
const platformInstalls = tool.install[info.platform];
|
|
51
|
+
if (platformInstalls === null) {
|
|
52
|
+
skipped.push({ tool, reason: "not available on this platform" });
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
const match = platformInstalls.find((pi) => info.available.includes(pi.method));
|
|
56
|
+
if (!match) {
|
|
57
|
+
const required = platformInstalls.map((pi) => pi.method).join(" or ");
|
|
58
|
+
skipped.push({ tool, reason: `requires ${required}` });
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
resolved.push({ tool, method: match.method, package: match.package });
|
|
378
62
|
}
|
|
379
|
-
|
|
380
|
-
function getToolsByPreset(presetId) {
|
|
381
|
-
return TOOLS.filter((t) => t.preset === presetId);
|
|
63
|
+
return { resolved, skipped };
|
|
382
64
|
}
|
|
383
|
-
function
|
|
384
|
-
|
|
65
|
+
function getVerifyCommand(tool, platform) {
|
|
66
|
+
const v = tool.verify;
|
|
67
|
+
if (typeof v === "string") return v;
|
|
68
|
+
return v[platform] ?? v.darwin ?? Object.values(v)[0] ?? "";
|
|
385
69
|
}
|
|
386
70
|
|
|
387
|
-
// src/
|
|
71
|
+
// src/installers/index.ts
|
|
72
|
+
import chalk6 from "chalk";
|
|
73
|
+
|
|
74
|
+
// src/installers/brew.ts
|
|
388
75
|
import { writeFile } from "fs/promises";
|
|
389
|
-
import { execa } from "execa";
|
|
76
|
+
import { execa as execa2 } from "execa";
|
|
390
77
|
import chalk from "chalk";
|
|
391
78
|
|
|
392
79
|
// src/paths.ts
|
|
@@ -394,34 +81,33 @@ import { homedir } from "os";
|
|
|
394
81
|
import { join } from "path";
|
|
395
82
|
import { mkdir } from "fs/promises";
|
|
396
83
|
var BASE_DIR = join(homedir(), ".agent-loadout");
|
|
397
|
-
var
|
|
84
|
+
var GENERIC_SKILLS = join(BASE_DIR, "skills");
|
|
85
|
+
var SKILL_TARGETS = {
|
|
86
|
+
claude: join(homedir(), ".claude", "skills")
|
|
87
|
+
};
|
|
398
88
|
var paths = {
|
|
399
89
|
base: BASE_DIR,
|
|
400
90
|
receipt: join(BASE_DIR, "receipt.json"),
|
|
401
|
-
|
|
402
|
-
|
|
91
|
+
/** null on non-darwin platforms */
|
|
92
|
+
brewfile: process.platform === "darwin" ? join(BASE_DIR, "Brewfile") : null,
|
|
93
|
+
localBin: join(homedir(), ".local", "bin"),
|
|
94
|
+
skillTargets: SKILL_TARGETS,
|
|
95
|
+
genericSkills: GENERIC_SKILLS
|
|
403
96
|
};
|
|
404
97
|
async function ensureDir() {
|
|
405
98
|
await mkdir(paths.base, { recursive: true });
|
|
406
99
|
}
|
|
407
|
-
async function
|
|
408
|
-
|
|
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;
|
|
100
|
+
async function ensureSkillDirs() {
|
|
101
|
+
for (const dir of Object.values(paths.skillTargets)) {
|
|
102
|
+
await mkdir(dir, { recursive: true });
|
|
418
103
|
}
|
|
104
|
+
await mkdir(paths.genericSkills, { recursive: true });
|
|
419
105
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
return
|
|
106
|
+
|
|
107
|
+
// src/installers/brew.ts
|
|
108
|
+
function generateBrewfile(packages) {
|
|
109
|
+
if (packages.length === 0) return "";
|
|
110
|
+
return packages.map((r) => `brew "${r.package}"`).join("\n") + "\n";
|
|
425
111
|
}
|
|
426
112
|
async function writeBrewfile(content) {
|
|
427
113
|
await ensureDir();
|
|
@@ -429,84 +115,192 @@ async function writeBrewfile(content) {
|
|
|
429
115
|
}
|
|
430
116
|
async function runBrewBundle() {
|
|
431
117
|
try {
|
|
432
|
-
await
|
|
118
|
+
await execa2("brew", ["bundle", "--file", paths.brewfile], {
|
|
433
119
|
stdio: "inherit"
|
|
434
120
|
});
|
|
435
121
|
} catch {
|
|
436
122
|
console.log(
|
|
437
123
|
chalk.yellow(
|
|
438
|
-
"\n Some brew packages may have failed
|
|
124
|
+
"\n Some brew packages may have failed. Run 'brew bundle --file ~/.agent-loadout/Brewfile' to retry."
|
|
439
125
|
)
|
|
440
126
|
);
|
|
441
127
|
}
|
|
442
128
|
}
|
|
129
|
+
async function runBrewInstall(packages) {
|
|
130
|
+
if (packages.length === 0) return;
|
|
131
|
+
const brewfile = generateBrewfile(packages);
|
|
132
|
+
await writeBrewfile(brewfile);
|
|
133
|
+
await runBrewBundle();
|
|
134
|
+
}
|
|
443
135
|
|
|
444
|
-
// src/npm.ts
|
|
445
|
-
import { execa as
|
|
136
|
+
// src/installers/npm.ts
|
|
137
|
+
import { execa as execa3 } from "execa";
|
|
446
138
|
import chalk2 from "chalk";
|
|
447
|
-
async function
|
|
139
|
+
async function runNpmInstall(packages) {
|
|
140
|
+
if (packages.length === 0) return;
|
|
141
|
+
const pkgNames = packages.map((r) => r.package);
|
|
448
142
|
try {
|
|
449
|
-
await
|
|
450
|
-
return true;
|
|
143
|
+
await execa3("npm", ["install", "-g", ...pkgNames], { stdio: "inherit" });
|
|
451
144
|
} catch {
|
|
452
|
-
|
|
145
|
+
console.log(
|
|
146
|
+
chalk2.yellow(
|
|
147
|
+
`
|
|
148
|
+
npm install failed. Try manually: npm install -g ${pkgNames.join(" ")}`
|
|
149
|
+
)
|
|
150
|
+
);
|
|
453
151
|
}
|
|
454
152
|
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
if (
|
|
461
|
-
|
|
153
|
+
|
|
154
|
+
// src/installers/apt.ts
|
|
155
|
+
import { execa as execa4 } from "execa";
|
|
156
|
+
import chalk3 from "chalk";
|
|
157
|
+
async function runAptInstall(packages) {
|
|
158
|
+
if (packages.length === 0) return;
|
|
159
|
+
const pkgNames = packages.map((r) => r.package);
|
|
160
|
+
try {
|
|
161
|
+
console.log(chalk3.dim(" Running apt-get update..."));
|
|
162
|
+
await execa4("sudo", ["apt-get", "update", "-qq"], { stdio: "inherit" });
|
|
163
|
+
await execa4("sudo", ["apt-get", "install", "-y", "-qq", ...pkgNames], {
|
|
164
|
+
stdio: "inherit"
|
|
165
|
+
});
|
|
166
|
+
} catch {
|
|
167
|
+
console.log(
|
|
168
|
+
chalk3.yellow(
|
|
169
|
+
`
|
|
170
|
+
apt install failed. Try manually: sudo apt-get install -y ${pkgNames.join(" ")}`
|
|
171
|
+
)
|
|
172
|
+
);
|
|
173
|
+
}
|
|
462
174
|
}
|
|
463
|
-
|
|
464
|
-
|
|
175
|
+
|
|
176
|
+
// src/installers/scoop.ts
|
|
177
|
+
import { execa as execa5 } from "execa";
|
|
178
|
+
import chalk4 from "chalk";
|
|
179
|
+
var EXTRAS_PACKAGES = /* @__PURE__ */ new Set([
|
|
180
|
+
"lazygit",
|
|
181
|
+
"delta",
|
|
182
|
+
"bottom",
|
|
183
|
+
"glow",
|
|
184
|
+
"gum",
|
|
185
|
+
"mkcert"
|
|
186
|
+
]);
|
|
187
|
+
async function runScoopInstall(packages) {
|
|
188
|
+
if (packages.length === 0) return;
|
|
189
|
+
const needsExtras = packages.some((r) => EXTRAS_PACKAGES.has(r.package));
|
|
190
|
+
if (needsExtras) {
|
|
191
|
+
try {
|
|
192
|
+
await execa5("scoop", ["bucket", "add", "extras"], { stdio: "inherit" });
|
|
193
|
+
} catch {
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const pkgNames = packages.map((r) => r.package);
|
|
465
197
|
try {
|
|
466
|
-
await
|
|
467
|
-
return true;
|
|
198
|
+
await execa5("scoop", ["install", ...pkgNames], { stdio: "inherit" });
|
|
468
199
|
} catch {
|
|
469
200
|
console.log(
|
|
470
|
-
|
|
201
|
+
chalk4.yellow(
|
|
471
202
|
`
|
|
472
|
-
|
|
203
|
+
scoop install failed. Try manually: scoop install ${pkgNames.join(" ")}`
|
|
473
204
|
)
|
|
474
205
|
);
|
|
475
|
-
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// src/installers/cargo.ts
|
|
210
|
+
import { execa as execa6 } from "execa";
|
|
211
|
+
import chalk5 from "chalk";
|
|
212
|
+
async function runCargoInstall(packages) {
|
|
213
|
+
if (packages.length === 0) return;
|
|
214
|
+
console.log(chalk5.dim(" Note: cargo installs compile from source \u2014 this may take a while."));
|
|
215
|
+
for (const { package: pkg } of packages) {
|
|
216
|
+
try {
|
|
217
|
+
await execa6("cargo", ["install", pkg], { stdio: "inherit" });
|
|
218
|
+
} catch {
|
|
219
|
+
console.log(
|
|
220
|
+
chalk5.yellow(`
|
|
221
|
+
cargo install ${pkg} failed. Try manually: cargo install ${pkg}`)
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// src/installers/index.ts
|
|
228
|
+
var INSTALLERS = {
|
|
229
|
+
brew: runBrewInstall,
|
|
230
|
+
npm: runNpmInstall,
|
|
231
|
+
apt: runAptInstall,
|
|
232
|
+
scoop: runScoopInstall,
|
|
233
|
+
cargo: runCargoInstall
|
|
234
|
+
};
|
|
235
|
+
function getInstallerFn(method) {
|
|
236
|
+
return INSTALLERS[method];
|
|
237
|
+
}
|
|
238
|
+
async function runInstallPlan(plan) {
|
|
239
|
+
const groups = /* @__PURE__ */ new Map();
|
|
240
|
+
for (const item of plan.resolved) {
|
|
241
|
+
const group = groups.get(item.method) ?? [];
|
|
242
|
+
group.push(item);
|
|
243
|
+
groups.set(item.method, group);
|
|
244
|
+
}
|
|
245
|
+
for (const [method, packages] of groups) {
|
|
246
|
+
console.log(chalk6.bold(`
|
|
247
|
+
Installing via ${method}...`));
|
|
248
|
+
await getInstallerFn(method)(packages);
|
|
476
249
|
}
|
|
477
250
|
}
|
|
478
251
|
|
|
479
252
|
// src/verify.ts
|
|
480
|
-
import { execa as
|
|
481
|
-
import
|
|
482
|
-
|
|
253
|
+
import { execa as execa7 } from "execa";
|
|
254
|
+
import chalk7 from "chalk";
|
|
255
|
+
function getExtraPaths(platform) {
|
|
256
|
+
if (platform === "darwin") {
|
|
257
|
+
return "/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin";
|
|
258
|
+
}
|
|
259
|
+
if (platform === "linux") {
|
|
260
|
+
const home = process.env.HOME ?? "";
|
|
261
|
+
return `${home}/.local/bin:${home}/.cargo/bin`;
|
|
262
|
+
}
|
|
263
|
+
if (platform === "win32") {
|
|
264
|
+
const userProfile = process.env.USERPROFILE ?? "";
|
|
265
|
+
return `${userProfile}\\scoop\\shims:${userProfile}\\.cargo\\bin`;
|
|
266
|
+
}
|
|
267
|
+
return "";
|
|
268
|
+
}
|
|
269
|
+
async function checkTool(tool, platform) {
|
|
483
270
|
try {
|
|
484
|
-
const
|
|
485
|
-
const
|
|
271
|
+
const cmd = getVerifyCommand(tool, platform);
|
|
272
|
+
const [bin, ...args] = cmd.split(" ");
|
|
273
|
+
const extraPaths = getExtraPaths(platform);
|
|
274
|
+
const pathSep = platform === "win32" ? ";" : ":";
|
|
275
|
+
const env = {
|
|
276
|
+
...process.env,
|
|
277
|
+
PATH: `${extraPaths}${pathSep}${process.env.PATH}`
|
|
278
|
+
};
|
|
279
|
+
const result = await execa7(bin, args, { timeout: 5e3, env });
|
|
486
280
|
const version = result.stdout.split("\n")[0].trim();
|
|
487
281
|
return { id: tool.id, name: tool.name, installed: true, version };
|
|
488
282
|
} catch {
|
|
489
283
|
return { id: tool.id, name: tool.name, installed: false, version: "" };
|
|
490
284
|
}
|
|
491
285
|
}
|
|
492
|
-
async function verifyTools(tools) {
|
|
493
|
-
return Promise.all(tools.map(checkTool));
|
|
286
|
+
async function verifyTools(tools, platform = process.platform) {
|
|
287
|
+
return Promise.all(tools.map((t) => checkTool(t, platform)));
|
|
494
288
|
}
|
|
495
289
|
function printVerifyResults(results) {
|
|
496
290
|
const maxName = Math.max(...results.map((r) => r.name.length));
|
|
497
291
|
for (const r of results) {
|
|
498
292
|
const name = r.name.padEnd(maxName);
|
|
499
293
|
if (r.installed) {
|
|
500
|
-
console.log(` ${
|
|
294
|
+
console.log(` ${chalk7.green("\u2713")} ${name} ${chalk7.dim(r.version)}`);
|
|
501
295
|
} else {
|
|
502
|
-
console.log(` ${
|
|
296
|
+
console.log(` ${chalk7.red("\u2717")} ${name} ${chalk7.red("not found")}`);
|
|
503
297
|
}
|
|
504
298
|
}
|
|
505
299
|
const installed = results.filter((r) => r.installed).length;
|
|
506
300
|
const total = results.length;
|
|
507
301
|
console.log();
|
|
508
302
|
console.log(
|
|
509
|
-
installed === total ?
|
|
303
|
+
installed === total ? chalk7.green(` All ${total} tools installed.`) : chalk7.yellow(` ${installed}/${total} tools installed.`)
|
|
510
304
|
);
|
|
511
305
|
}
|
|
512
306
|
function verifyResultsToJson(results) {
|
|
@@ -519,7 +313,7 @@ function verifyResultsToJson(results) {
|
|
|
519
313
|
|
|
520
314
|
// src/receipt.ts
|
|
521
315
|
import { readFile, writeFile as writeFile2 } from "fs/promises";
|
|
522
|
-
import
|
|
316
|
+
import chalk8 from "chalk";
|
|
523
317
|
async function readReceipt() {
|
|
524
318
|
try {
|
|
525
319
|
const raw = await readFile(paths.receipt, "utf-8");
|
|
@@ -527,7 +321,7 @@ async function readReceipt() {
|
|
|
527
321
|
} catch (err) {
|
|
528
322
|
if (err instanceof SyntaxError) {
|
|
529
323
|
console.log(
|
|
530
|
-
|
|
324
|
+
chalk8.yellow(
|
|
531
325
|
" Warning: ~/.agent-loadout/receipt.json is corrupted. Ignoring."
|
|
532
326
|
)
|
|
533
327
|
);
|
|
@@ -542,7 +336,7 @@ async function writeReceipt(receipt) {
|
|
|
542
336
|
|
|
543
337
|
// src/ui.ts
|
|
544
338
|
import { checkbox, confirm } from "@inquirer/prompts";
|
|
545
|
-
import
|
|
339
|
+
import chalk9 from "chalk";
|
|
546
340
|
async function selectPresets() {
|
|
547
341
|
return checkbox({
|
|
548
342
|
message: "Which presets do you want to install?",
|
|
@@ -562,9 +356,9 @@ async function selectTools(presetIds) {
|
|
|
562
356
|
const selectedIds = await checkbox({
|
|
563
357
|
message: "Toggle individual tools (all selected by default)",
|
|
564
358
|
choices: available.map((t) => {
|
|
565
|
-
const badge = installedIds.has(t.id) ?
|
|
359
|
+
const badge = installedIds.has(t.id) ? chalk9.green(" (installed)") : "";
|
|
566
360
|
return {
|
|
567
|
-
name: `${t.name}${badge} \u2014 ${
|
|
361
|
+
name: `${t.name}${badge} \u2014 ${chalk9.dim(t.description)}`,
|
|
568
362
|
value: t.id,
|
|
569
363
|
checked: true
|
|
570
364
|
};
|
|
@@ -572,22 +366,51 @@ async function selectTools(presetIds) {
|
|
|
572
366
|
});
|
|
573
367
|
return TOOLS.filter((t) => selectedIds.includes(t.id));
|
|
574
368
|
}
|
|
575
|
-
function printPreview(tools) {
|
|
576
|
-
const
|
|
577
|
-
const
|
|
369
|
+
function printPreview(tools, plan, platformInfo) {
|
|
370
|
+
const platformLabel = chalk9.cyan(platformInfo.platform);
|
|
371
|
+
const archLabel = chalk9.dim(platformInfo.arch);
|
|
372
|
+
console.log(`
|
|
373
|
+
Platform: ${platformLabel} ${archLabel}`);
|
|
374
|
+
if (plan.skipped.length > 0) {
|
|
375
|
+
console.log(chalk9.yellow(`
|
|
376
|
+
Skipped (${plan.skipped.length} tools unavailable on this platform):`));
|
|
377
|
+
for (const { tool, reason } of plan.skipped) {
|
|
378
|
+
console.log(chalk9.dim(` \u2022 ${tool.name} \u2014 ${reason}`));
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
const groups = /* @__PURE__ */ new Map();
|
|
382
|
+
for (const { method, package: pkg } of plan.resolved) {
|
|
383
|
+
const list = groups.get(method) ?? [];
|
|
384
|
+
list.push(pkg);
|
|
385
|
+
groups.set(method, list);
|
|
386
|
+
}
|
|
387
|
+
if (groups.size === 0) {
|
|
388
|
+
console.log(chalk9.dim("\nNothing to install."));
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
578
391
|
console.log();
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
console.log(
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
);
|
|
392
|
+
for (const [method, packages] of groups) {
|
|
393
|
+
const label = chalk9.bold(`${method}:`);
|
|
394
|
+
console.log(label);
|
|
395
|
+
const preview = getInstallPreview(method, packages);
|
|
396
|
+
console.log(chalk9.dim(` ${preview}`));
|
|
585
397
|
console.log();
|
|
586
398
|
}
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
399
|
+
}
|
|
400
|
+
function getInstallPreview(method, packages) {
|
|
401
|
+
switch (method) {
|
|
402
|
+
case "brew":
|
|
403
|
+
return `brew bundle --file ~/.agent-loadout/Brewfile`;
|
|
404
|
+
case "npm":
|
|
405
|
+
return `npm install -g ${packages.join(" ")}`;
|
|
406
|
+
case "apt":
|
|
407
|
+
return `sudo apt-get install -y ${packages.join(" ")}`;
|
|
408
|
+
case "scoop":
|
|
409
|
+
return `scoop install ${packages.join(" ")}`;
|
|
410
|
+
case "cargo":
|
|
411
|
+
return `cargo install ${packages.join(" ")}`;
|
|
412
|
+
default:
|
|
413
|
+
return packages.join(" ");
|
|
591
414
|
}
|
|
592
415
|
}
|
|
593
416
|
async function confirmInstall() {
|
|
@@ -595,7 +418,7 @@ async function confirmInstall() {
|
|
|
595
418
|
}
|
|
596
419
|
|
|
597
420
|
// src/skills.ts
|
|
598
|
-
import { writeFile as writeFile3 } from "fs/promises";
|
|
421
|
+
import { access, writeFile as writeFile3 } from "fs/promises";
|
|
599
422
|
import { join as join2 } from "path";
|
|
600
423
|
|
|
601
424
|
// src/skills/rg.ts
|
|
@@ -614,13 +437,19 @@ Search file contents across a codebase. Faster than grep, respects .gitignore by
|
|
|
614
437
|
- Files only (no content): \`rg -l "pattern"\`
|
|
615
438
|
- Count matches: \`rg -c "pattern"\`
|
|
616
439
|
- Case insensitive: \`rg -i "pattern"\`
|
|
440
|
+
- JSON output (agent-parseable): \`rg "pattern" --json\`
|
|
441
|
+
- Exit non-zero if no matches: \`rg --exit-status "pattern"\`
|
|
617
442
|
|
|
618
443
|
## Output format
|
|
619
444
|
\`file:line:column:matched_text\` \u2014 one match per line, stable and parseable.
|
|
620
445
|
|
|
446
|
+
## Why it matters for agents
|
|
447
|
+
\`--json\` output is directly parseable; rg is the backbone of fast codebase search. \`--exit-status\` makes it composable in CI pipelines and shell conditionals.
|
|
448
|
+
|
|
621
449
|
## Gotchas
|
|
622
450
|
- Skips hidden files and .gitignore'd paths by default. Use \`--hidden\` or \`--no-ignore\` to include them.
|
|
623
451
|
- For literal dots, brackets etc. in patterns, use \`-F\` (fixed string) to avoid regex escaping issues.
|
|
452
|
+
- \`--json\` emits structured events (match, begin, end, summary) \u2014 parse with \`jq\` for agent workflows.
|
|
624
453
|
`.trim();
|
|
625
454
|
|
|
626
455
|
// src/skills/fd.ts
|
|
@@ -641,9 +470,13 @@ Find files by name pattern. Faster than \`find\`, respects .gitignore, sane defa
|
|
|
641
470
|
## Output format
|
|
642
471
|
One path per line, relative to search root.
|
|
643
472
|
|
|
473
|
+
## Why it matters for agents
|
|
474
|
+
\`-x\` flag enables batch file operations without shell loops \u2014 agents can use \`fd -e ts -x sd 'old' 'new' {}\` to safely refactor across many files.
|
|
475
|
+
|
|
644
476
|
## Gotchas
|
|
645
477
|
- Regex by default. Use \`-g\` for glob patterns.
|
|
646
478
|
- Ignores .gitignore'd files by default. Use \`--no-ignore\` to include.
|
|
479
|
+
- On Debian/Ubuntu the binary is \`fdfind\`, not \`fd\`. Use \`alias fd=fdfind\` or install via cargo.
|
|
647
480
|
`.trim();
|
|
648
481
|
|
|
649
482
|
// src/skills/jq.ts
|
|
@@ -701,6 +534,14 @@ View file contents with syntax highlighting, line numbers, and git diff indicato
|
|
|
701
534
|
- Show line range: \`bat -r 10:20 file.ts\`
|
|
702
535
|
- Force language: \`bat -l json file.txt\`
|
|
703
536
|
- Use as pager for other commands: \`command | bat -l json\`
|
|
537
|
+
- Disable pager for scripts: \`bat --paging=never file.ts\`
|
|
538
|
+
|
|
539
|
+
## Why it matters for agents
|
|
540
|
+
\`-l\` flag lets agents force syntax for stdin \u2014 useful for highlighting API responses or generated content piped through bat.
|
|
541
|
+
|
|
542
|
+
## Gotchas
|
|
543
|
+
- On Debian/Ubuntu the binary is \`batcat\`, not \`bat\`. Use \`alias bat=batcat\` or install via cargo.
|
|
544
|
+
- Use \`--paging=never\` in scripts to suppress the interactive pager.
|
|
704
545
|
`.trim();
|
|
705
546
|
|
|
706
547
|
// src/skills/tree.ts
|
|
@@ -716,6 +557,14 @@ Visualise directory structure as a tree. Useful for understanding project layout
|
|
|
716
557
|
- Show only directories: \`tree -d\`
|
|
717
558
|
- Ignore patterns: \`tree -I 'node_modules|dist'\`
|
|
718
559
|
- With file sizes: \`tree -sh\`
|
|
560
|
+
- JSON output: \`tree -J path/\`
|
|
561
|
+
|
|
562
|
+
## Why it matters for agents
|
|
563
|
+
\`-J\` gives a JSON file tree \u2014 lets agents understand project structure in one call without recursive \`ls\` loops.
|
|
564
|
+
|
|
565
|
+
## Gotchas
|
|
566
|
+
- Use \`--charset ascii\` for plain output in non-unicode terminals or CI logs.
|
|
567
|
+
- \`-J\` JSON output includes type, name, and children \u2014 structured project map in one call.
|
|
719
568
|
`.trim();
|
|
720
569
|
|
|
721
570
|
// src/skills/gh.ts
|
|
@@ -735,13 +584,20 @@ Create PRs, manage issues, check CI status, manage releases \u2014 all without l
|
|
|
735
584
|
- View repo: \`gh repo view\`
|
|
736
585
|
- API calls: \`gh api repos/owner/repo/pulls/123/comments\`
|
|
737
586
|
- Clone: \`gh repo clone owner/repo\`
|
|
587
|
+
- List workflow runs: \`gh run list --json name,status,conclusion\`
|
|
588
|
+
- Watch a run live: \`gh run watch\`
|
|
589
|
+
- Cross-repo PR list: \`gh pr list -R owner/repo --json number,title,state\`
|
|
738
590
|
|
|
739
591
|
## Output format
|
|
740
592
|
Supports \`--json\` on most commands for structured output. E.g. \`gh pr list --json number,title,state\`.
|
|
741
593
|
|
|
594
|
+
## Why it matters for agents
|
|
595
|
+
\`--json\` on every command + \`gh api\` unlocks full GitHub automation without parsing HTML. Agents can open PRs, check CI, and comment on issues programmatically.
|
|
596
|
+
|
|
742
597
|
## Gotchas
|
|
743
598
|
- Requires authentication: \`gh auth login\`
|
|
744
599
|
- \`gh api\` is very powerful for anything not covered by built-in commands.
|
|
600
|
+
- In CI, set \`GH_TOKEN\` env var instead of running \`gh auth login\`.
|
|
745
601
|
`.trim();
|
|
746
602
|
|
|
747
603
|
// src/skills/fzf.ts
|
|
@@ -775,10 +631,14 @@ Lint shell scripts (bash, sh, dash) for common mistakes: quoting issues, unset v
|
|
|
775
631
|
- GCC-style output: \`shellcheck --format=gcc script.sh\`
|
|
776
632
|
- Exclude specific rules: \`shellcheck --exclude=SC2034 script.sh\`
|
|
777
633
|
- Check from stdin: \`echo '#!/bin/bash' | shellcheck -\`
|
|
634
|
+
- Errors only (no warnings): \`shellcheck --severity error script.sh\`
|
|
778
635
|
|
|
779
636
|
## Output format
|
|
780
637
|
Default output is human-readable with line numbers and fix suggestions. Use \`--format=json\` for structured output.
|
|
781
638
|
|
|
639
|
+
## Why it matters for agents
|
|
640
|
+
Agents frequently generate shell scripts \u2014 shellcheck catches quoting bugs, unset variables, and portability issues before they cause failures. Use \`--format=json\` to parse results programmatically.
|
|
641
|
+
|
|
782
642
|
## Gotchas
|
|
783
643
|
- Scripts need a shebang (\`#!/bin/bash\`) or use \`--shell=\` flag.
|
|
784
644
|
- SC codes (e.g. SC2086) link to detailed wiki explanations.
|
|
@@ -845,7 +705,10 @@ Generate a regular expression from a set of example strings. Useful when you kno
|
|
|
845
705
|
- Verbose regex: \`grex --verbose "foo-123" "bar-456"\`
|
|
846
706
|
|
|
847
707
|
## Gotchas
|
|
848
|
-
- Output is a regex string
|
|
708
|
+
- Output is a raw regex string \u2014 pipe directly into \`rg\`, \`sd\`, or save to a variable.
|
|
709
|
+
|
|
710
|
+
## Why it matters for agents
|
|
711
|
+
Lets agents generate correct regex from test cases rather than hallucinating patterns \u2014 eliminates a whole class of regex bugs.
|
|
849
712
|
`.trim();
|
|
850
713
|
|
|
851
714
|
// src/skills/knip.ts
|
|
@@ -860,12 +723,16 @@ Detect unused files, exports, dependencies, and types in TypeScript/JavaScript p
|
|
|
860
723
|
- Unused files only: \`knip --include files\`
|
|
861
724
|
- Unused exports only: \`knip --include exports\`
|
|
862
725
|
- Unused deps only: \`knip --include dependencies\`
|
|
863
|
-
-
|
|
726
|
+
- Machine-readable with scopes: \`knip --reporter json --include files,exports,dependencies\`
|
|
727
|
+
|
|
728
|
+
## Why it matters for agents
|
|
729
|
+
Identifies dead code before large refactors \u2014 agents can safely delete unused files and exports flagged by knip without breaking the build.
|
|
864
730
|
|
|
865
731
|
## Gotchas
|
|
866
732
|
- Must be run at project root (where package.json lives).
|
|
867
733
|
- May need a knip.json config for monorepos or non-standard project structures.
|
|
868
734
|
- Some frameworks have plugins (Next.js, Remix, etc.) \u2014 check docs if results seem wrong.
|
|
735
|
+
- Exits 1 if issues found \u2014 CI-friendly; pipe to \`jq\` to filter specific categories.
|
|
869
736
|
`.trim();
|
|
870
737
|
|
|
871
738
|
// src/skills/sd.ts
|
|
@@ -884,6 +751,9 @@ Find and replace in files. Like sed but with intuitive syntax \u2014 no escaping
|
|
|
884
751
|
## Gotchas
|
|
885
752
|
- Uses regex by default. Use \`-F\` for fixed/literal strings.
|
|
886
753
|
- Modifies files in place when given a filename. Use \`-p\` to preview first.
|
|
754
|
+
|
|
755
|
+
## Why it matters for agents
|
|
756
|
+
Safer than sed for multi-line replacements \u2014 predictable escaping without shell quoting nightmares. Combine with \`fd -x\` for safe bulk refactors across many files.
|
|
887
757
|
`.trim();
|
|
888
758
|
|
|
889
759
|
// src/skills/hyperfine.ts
|
|
@@ -899,6 +769,11 @@ Benchmark commands to compare performance. Runs commands multiple times and repo
|
|
|
899
769
|
- With warmup: \`hyperfine --warmup 3 'command'\`
|
|
900
770
|
- Export results: \`hyperfine --export-json results.json 'command'\`
|
|
901
771
|
- Min runs: \`hyperfine --min-runs 20 'command'\`
|
|
772
|
+
- Non-interactive + export: \`hyperfine --style basic --export-json results.json 'cmd1' 'cmd2'\`
|
|
773
|
+
- With prepare step: \`hyperfine --prepare 'make clean' 'make build'\`
|
|
774
|
+
|
|
775
|
+
## Why it matters for agents
|
|
776
|
+
\`--export-json\` lets agents compare builds and commands quantitatively \u2014 structured results include mean, stddev, min, max per command.
|
|
902
777
|
|
|
903
778
|
## Gotchas
|
|
904
779
|
- Wrap commands in quotes.
|
|
@@ -917,10 +792,16 @@ Get a quick overview of a codebase: languages, lines of code, comments, blanks.
|
|
|
917
792
|
- Specific path: \`tokei src/\`
|
|
918
793
|
- JSON output: \`tokei --output json\`
|
|
919
794
|
- Sort by lines: \`tokei --sort lines\`
|
|
795
|
+
- Exclude dirs: \`tokei --output json --exclude node_modules,dist src/\`
|
|
796
|
+
- Filter languages: \`tokei --languages TypeScript,Rust\`
|
|
797
|
+
- Per-file breakdown: \`tokei --files src/\`
|
|
920
798
|
|
|
921
799
|
## Output format
|
|
922
800
|
Table with language, files, lines, code, comments, blanks.
|
|
923
801
|
Use \`--output json\` for structured output.
|
|
802
|
+
|
|
803
|
+
## Why it matters for agents
|
|
804
|
+
Gives agents a language/complexity map before large refactors \u2014 quickly understand the scale and dominant languages of an unfamiliar codebase.
|
|
924
805
|
`.trim();
|
|
925
806
|
|
|
926
807
|
// src/skills/ffmpeg.ts
|
|
@@ -972,6 +853,9 @@ Read, write, and strip metadata (EXIF, IPTC, XMP) from images and media files.
|
|
|
972
853
|
## Gotchas
|
|
973
854
|
- Field names are case-insensitive.
|
|
974
855
|
- Use \`-json\` for structured output.
|
|
856
|
+
|
|
857
|
+
## Why it matters for agents
|
|
858
|
+
\`-json\` output enables structured metadata extraction from any media file \u2014 agents can batch-read EXIF data, filter by GPS coordinates, or rename files by capture date programmatically.
|
|
975
859
|
`.trim();
|
|
976
860
|
|
|
977
861
|
// src/skills/imagemagick.ts
|
|
@@ -994,6 +878,9 @@ Resize, crop, convert, and manipulate images from the command line.
|
|
|
994
878
|
|
|
995
879
|
## Gotchas
|
|
996
880
|
- The binary is \`magick\` (ImageMagick 7). Older versions used \`convert\`.
|
|
881
|
+
|
|
882
|
+
## Why it matters for agents
|
|
883
|
+
Batch image processing without opening apps \u2014 useful for automated asset pipelines. Agents can resize, convert formats, and generate thumbnails in a single \`mogrify\` invocation.
|
|
997
884
|
`.trim();
|
|
998
885
|
|
|
999
886
|
// src/skills/svgo.ts
|
|
@@ -1008,10 +895,15 @@ Optimise SVG files by removing unnecessary metadata, comments, and reducing prec
|
|
|
1008
895
|
- Optimise in place: \`svgo input.svg\`
|
|
1009
896
|
- Folder: \`svgo -f ./icons/ -o ./icons-optimised/\`
|
|
1010
897
|
- Show savings: \`svgo input.svg --pretty\`
|
|
898
|
+
- Multipass (better compression): \`svgo --multipass input.svg -o output.svg\`
|
|
899
|
+
- Check savings without writing: \`svgo --dry-run input.svg\`
|
|
1011
900
|
|
|
1012
901
|
## Gotchas
|
|
1013
902
|
- Default plugins are usually fine. Override with \`--config svgo.config.js\` if needed.
|
|
1014
903
|
- In-place by default when no \`-o\` specified. Pipe or use \`-o\` for safety.
|
|
904
|
+
|
|
905
|
+
## Why it matters for agents
|
|
906
|
+
Automated SVG optimisation in build pipelines \u2014 measurable, reproducible size savings. Agents can run \`svgo -f ./icons/\` after any icon library update.
|
|
1015
907
|
`.trim();
|
|
1016
908
|
|
|
1017
909
|
// src/skills/eza.ts
|
|
@@ -1070,6 +962,9 @@ Add to ~/.gitconfig:
|
|
|
1070
962
|
|
|
1071
963
|
## Gotchas
|
|
1072
964
|
- The brew package is called \`git-delta\`, but the binary is \`delta\`.
|
|
965
|
+
|
|
966
|
+
## Why it matters for agents
|
|
967
|
+
Makes \`git diff\` and \`git log -p\` output readable \u2014 useful when agents are reviewing code changes or summarising commits for users.
|
|
1073
968
|
`.trim();
|
|
1074
969
|
|
|
1075
970
|
// src/skills/glow.ts
|
|
@@ -1083,6 +978,11 @@ Render markdown files beautifully in the terminal. Great for reading READMEs, do
|
|
|
1083
978
|
- Render file: \`glow README.md\`
|
|
1084
979
|
- Render with pager: \`glow -p README.md\`
|
|
1085
980
|
- Render from stdin: \`cat CHANGELOG.md | glow\`
|
|
981
|
+
- Disable pager: \`glow --no-pager README.md\`
|
|
982
|
+
- Fixed width: \`glow --width 100 README.md\`
|
|
983
|
+
|
|
984
|
+
## Why it matters for agents
|
|
985
|
+
Renders markdown cleanly in terminal output \u2014 useful for displaying skill files, changelogs, or generated docs to users without raw markdown symbols.
|
|
1086
986
|
`.trim();
|
|
1087
987
|
|
|
1088
988
|
// src/skills/mise.ts
|
|
@@ -1105,6 +1005,9 @@ Uses \`.mise.toml\` or \`.tool-versions\` in project root. This ensures determin
|
|
|
1105
1005
|
|
|
1106
1006
|
## Gotchas
|
|
1107
1007
|
- Run \`mise activate zsh\` (or bash/fish) in your shell profile for automatic version switching.
|
|
1008
|
+
|
|
1009
|
+
## Why it matters for agents
|
|
1010
|
+
Manages runtime versions declaratively via \`.mise.toml\` \u2014 agents can use \`mise exec -- node script.js\` to pin the exact Node/Python/Ruby version without modifying global state.
|
|
1108
1011
|
`.trim();
|
|
1109
1012
|
|
|
1110
1013
|
// src/skills/watchexec.ts
|
|
@@ -1123,6 +1026,9 @@ Watch files for changes and re-run a command. Language-agnostic alternative to n
|
|
|
1123
1026
|
## Gotchas
|
|
1124
1027
|
- Use \`-e\` to filter by extension, \`-w\` to filter by directory.
|
|
1125
1028
|
- Use \`--restart\` for long-running processes (servers), otherwise it waits for completion.
|
|
1029
|
+
|
|
1030
|
+
## Why it matters for agents
|
|
1031
|
+
Enables live-reload dev loops \u2014 agents can set up reactive pipelines (\`watchexec -e ts "pnpm typecheck"\`) and report on each change without polling.
|
|
1126
1032
|
`.trim();
|
|
1127
1033
|
|
|
1128
1034
|
// src/skills/mkcert.ts
|
|
@@ -1136,10 +1042,15 @@ Generate locally-trusted HTTPS certificates for development. No more "insecure"
|
|
|
1136
1042
|
- First-time setup: \`mkcert -install\` (installs local CA)
|
|
1137
1043
|
- Generate cert: \`mkcert localhost 127.0.0.1 ::1\`
|
|
1138
1044
|
- Generate for custom domain: \`mkcert "myapp.local" "*.myapp.local"\`
|
|
1045
|
+
- Custom output paths: \`mkcert -cert-file cert.pem -key-file key.pem localhost\`
|
|
1046
|
+
- Wildcard + Docker/VM: \`mkcert -install && mkcert "*.local" localhost 127.0.0.1\`
|
|
1139
1047
|
|
|
1140
1048
|
## Gotchas
|
|
1141
1049
|
- \`mkcert -install\` only needs to run once per machine.
|
|
1142
1050
|
- Output is two files: cert.pem and key.pem. Point your dev server at them.
|
|
1051
|
+
|
|
1052
|
+
## Why it matters for agents
|
|
1053
|
+
Enables local HTTPS in one command \u2014 no CA setup complexity. Agents scaffolding full-stack dev environments can run \`mkcert -install && mkcert localhost\` to get HTTPS working immediately.
|
|
1143
1054
|
`.trim();
|
|
1144
1055
|
|
|
1145
1056
|
// src/skills/trivy.ts
|
|
@@ -1155,10 +1066,17 @@ Scan filesystems, container images, and code repos for known vulnerabilities.
|
|
|
1155
1066
|
- Scan a container image: \`trivy image myapp:latest\`
|
|
1156
1067
|
- Only critical/high: \`trivy fs --severity CRITICAL,HIGH .\`
|
|
1157
1068
|
- Scan for secrets: \`trivy fs --scanners secret .\`
|
|
1069
|
+
- CI gate (fail on findings): \`trivy fs --severity CRITICAL,HIGH --exit-code 1 --no-progress .\`
|
|
1070
|
+
- Offline (skip DB update): \`trivy fs --skip-update --format json .\`
|
|
1071
|
+
|
|
1072
|
+
## Why it matters for agents
|
|
1073
|
+
Gives agents a security gate before deployments. \`--format json --exit-code 1\` creates a composable CI step \u2014 agents can parse findings and summarise critical vulnerabilities.
|
|
1158
1074
|
|
|
1159
1075
|
## Gotchas
|
|
1160
1076
|
- First run downloads a vulnerability database (can be slow).
|
|
1161
1077
|
- Use \`--format json\` for structured output.
|
|
1078
|
+
- Use \`--exit-code 1\` to fail CI pipelines on findings; omit for reporting-only mode.
|
|
1079
|
+
- \`--no-progress\` keeps CI logs clean.
|
|
1162
1080
|
`.trim();
|
|
1163
1081
|
|
|
1164
1082
|
// src/skills/act.ts
|
|
@@ -1174,11 +1092,16 @@ Test GitHub Actions workflows without pushing. Runs workflows in Docker containe
|
|
|
1174
1092
|
- Run specific event: \`act push\`
|
|
1175
1093
|
- Run specific job: \`act -j build\`
|
|
1176
1094
|
- Dry run: \`act -n\`
|
|
1095
|
+
- Pass secrets: \`act push --secret MY_TOKEN="value"\`
|
|
1096
|
+
- List available jobs: \`act --list\`
|
|
1177
1097
|
|
|
1178
1098
|
## Gotchas
|
|
1179
1099
|
- Requires Docker to be running.
|
|
1180
1100
|
- Not all GitHub Actions features are supported locally (secrets, some contexts).
|
|
1181
1101
|
- Use \`-n\` (dry run) first to see what would happen.
|
|
1102
|
+
|
|
1103
|
+
## Why it matters for agents
|
|
1104
|
+
Test CI workflows locally before pushing \u2014 saves agent roundtrips to GitHub. Agents can run \`act -n\` to validate workflow YAML without Docker overhead.
|
|
1182
1105
|
`.trim();
|
|
1183
1106
|
|
|
1184
1107
|
// src/skills/xh.ts
|
|
@@ -1196,11 +1119,17 @@ Send HTTP requests from the terminal. Cleaner syntax than curl, JSON-first, colo
|
|
|
1196
1119
|
- Follow redirects: \`xh --follow get example.com\`
|
|
1197
1120
|
- Save response to file: \`xh get example.com/file.zip > file.zip\`
|
|
1198
1121
|
- Show request/response headers: \`xh --print=hHbB get httpbin.org/get\`
|
|
1122
|
+
- Fail on 4xx/5xx: \`xh --check-status get api.example.com\`
|
|
1123
|
+
- Response body only: \`xh -b get api.example.com\`
|
|
1124
|
+
- Headers only: \`xh -h get api.example.com\`
|
|
1125
|
+
- Download file: \`xh --download get example.com/file.zip\`
|
|
1126
|
+
|
|
1127
|
+
## Why it matters for agents
|
|
1128
|
+
Cleaner than curl for API testing \u2014 \`key=value\` JSON syntax removes quoting complexity. \`--check-status\` makes error handling trivial: non-zero exit on any 4xx/5xx.
|
|
1199
1129
|
|
|
1200
1130
|
## Gotchas
|
|
1201
1131
|
- \`key=value\` sends as JSON string; \`key:=value\` sends raw JSON (numbers, bools, arrays).
|
|
1202
1132
|
- Defaults to HTTPS if scheme is omitted.
|
|
1203
|
-
- Use \`--check-status\` to exit non-zero on 4xx/5xx responses.
|
|
1204
1133
|
`.trim();
|
|
1205
1134
|
|
|
1206
1135
|
// src/skills/tldr.ts
|
|
@@ -1242,6 +1171,9 @@ Fast, zero-config linter and formatter for JavaScript/TypeScript projects. Repla
|
|
|
1242
1171
|
- Requires a \`biome.json\` config or \`--config-path\` flag; \`biome init\` generates a sensible default.
|
|
1243
1172
|
- Not 100% compatible with all ESLint rules \u2014 check the migration guide when switching existing projects.
|
|
1244
1173
|
- \`biome check\` is read-only by default; pass \`--write\` to apply fixes.
|
|
1174
|
+
|
|
1175
|
+
## Why it matters for agents
|
|
1176
|
+
Zero-config lint+format in one command \u2014 drop-in for eslint+prettier in CI. \`biome ci .\` exits 1 on issues and is non-destructive.
|
|
1245
1177
|
`.trim();
|
|
1246
1178
|
|
|
1247
1179
|
// src/skills/difftastic.ts
|
|
@@ -1262,6 +1194,9 @@ Compare files by syntax tree, not line-by-line. Understands code structure so re
|
|
|
1262
1194
|
- Supports most languages automatically via file extension detection.
|
|
1263
1195
|
- Output is always side-by-side; pipe width matters \u2014 use a wide terminal.
|
|
1264
1196
|
- Falls back to line-diff for unsupported file types.
|
|
1197
|
+
|
|
1198
|
+
## Why it matters for agents
|
|
1199
|
+
Understands code structure \u2014 avoids false-positive diffs from formatting changes. Agents using \`GIT_EXTERNAL_DIFF=difft git diff\` get semantic change summaries, not noise.
|
|
1265
1200
|
`.trim();
|
|
1266
1201
|
|
|
1267
1202
|
// src/skills/lazygit.ts
|
|
@@ -1289,6 +1224,9 @@ Interactive terminal UI for git \u2014 stage hunks, commit, branch, rebase, and
|
|
|
1289
1224
|
- Requires git to be installed (it's a UI wrapper, not a replacement).
|
|
1290
1225
|
- Config lives at \`~/.config/lazygit/config.yml\`.
|
|
1291
1226
|
- Mouse support is on by default \u2014 click panels to navigate.
|
|
1227
|
+
|
|
1228
|
+
## Why it matters for agents
|
|
1229
|
+
Note: interactive TUI only \u2014 not suitable for agent automation. For scripted git operations use \`git\` CLI directly; for GitHub automation use \`gh\`.
|
|
1292
1230
|
`.trim();
|
|
1293
1231
|
|
|
1294
1232
|
// src/skills/dust.ts
|
|
@@ -1310,6 +1248,9 @@ Visualise what's eating disk space in a directory tree. Faster and more readable
|
|
|
1310
1248
|
- Output is proportional bars + sizes; percentages are relative to the scanned root, not total disk.
|
|
1311
1249
|
- Use \`-d 1\` for a quick top-level summary before drilling down.
|
|
1312
1250
|
- Symlinks are not followed by default \u2014 add \`-L\` to follow them.
|
|
1251
|
+
|
|
1252
|
+
## Why it matters for agents
|
|
1253
|
+
Identifies large directories before disk operations \u2014 agents can quickly find what to clean up with \`dust -d 1\` before running builds or copying large directories.
|
|
1313
1254
|
`.trim();
|
|
1314
1255
|
|
|
1315
1256
|
// src/skills/btm.ts
|
|
@@ -1336,6 +1277,9 @@ Real-time TUI system monitor \u2014 CPU, memory, network, disk, and process list
|
|
|
1336
1277
|
## Gotchas
|
|
1337
1278
|
- Config lives at \`~/.config/bottom/bottom.toml\` \u2014 customise colours and layout there.
|
|
1338
1279
|
- \`--basic\` mode is useful in constrained terminals or for quick checks.
|
|
1280
|
+
|
|
1281
|
+
## Why it matters for agents
|
|
1282
|
+
Note: interactive TUI \u2014 not suitable for scripting. For programmatic process info use \`procs --json\`; for one-shot CPU/memory snapshots use \`ps\` or \`top -l 1\`.
|
|
1339
1283
|
`.trim();
|
|
1340
1284
|
|
|
1341
1285
|
// src/skills/gitleaks.ts
|
|
@@ -1359,10 +1303,336 @@ gitleaks protect --staged # pre-commit hook
|
|
|
1359
1303
|
gitleaks detect # CI full scan
|
|
1360
1304
|
\`\`\`
|
|
1361
1305
|
|
|
1306
|
+
## Why it matters for agents
|
|
1307
|
+
Agents editing configuration files or adding credentials must scan before committing. Exit code 1 on findings makes it trivially composable as a pre-commit gate.
|
|
1308
|
+
|
|
1362
1309
|
## Gotchas
|
|
1363
1310
|
- Findings include file, line, rule, and matched secret fragment \u2014 review before dismissing.
|
|
1364
1311
|
- Use a \`.gitleaksignore\` file to whitelist known false positives.
|
|
1365
1312
|
- Does not redact secrets from history \u2014 use \`git filter-repo\` to remove them.
|
|
1313
|
+
- Returns exit code 1 if leaks found, 0 if clean \u2014 use directly in CI pipelines.
|
|
1314
|
+
- Custom rules: \`gitleaks detect --config custom-rules.toml --report-format json\`
|
|
1315
|
+
`.trim();
|
|
1316
|
+
|
|
1317
|
+
// src/skills/pandoc.ts
|
|
1318
|
+
var pandoc_default = `
|
|
1319
|
+
# pandoc \u2014 Universal document converter
|
|
1320
|
+
|
|
1321
|
+
## When to use
|
|
1322
|
+
Convert between document formats: markdown, HTML, PDF, docx, LaTeX, RST, EPUB, and dozens more. The single tool for any "convert X to Y" task.
|
|
1323
|
+
|
|
1324
|
+
## Trusted commands
|
|
1325
|
+
- Markdown to HTML: \`pandoc input.md -o output.html\`
|
|
1326
|
+
- Markdown to PDF: \`pandoc input.md -o output.pdf\` (requires LaTeX or --pdf-engine=weasyprint)
|
|
1327
|
+
- Markdown to docx: \`pandoc input.md -o output.docx\`
|
|
1328
|
+
- HTML to markdown: \`pandoc input.html -o output.md\`
|
|
1329
|
+
- With standalone template: \`pandoc input.md -s -o output.html\`
|
|
1330
|
+
- Custom CSS: \`pandoc input.md -s --css=style.css -o output.html\`
|
|
1331
|
+
- List supported formats: \`pandoc --list-input-formats\` / \`pandoc --list-output-formats\`
|
|
1332
|
+
|
|
1333
|
+
## Why it matters for agents
|
|
1334
|
+
Agents frequently need to transform content between formats for delivery. Pandoc handles the conversion so the agent focuses on content.
|
|
1335
|
+
|
|
1336
|
+
## Gotchas
|
|
1337
|
+
- PDF output requires a PDF engine (wkhtmltopdf, weasyprint, or LaTeX). Use \`--pdf-engine=weasyprint\` if LaTeX isn't installed.
|
|
1338
|
+
- Output format is inferred from the file extension.
|
|
1339
|
+
- Use \`-s\` (standalone) for complete documents with headers, not fragments.
|
|
1340
|
+
`.trim();
|
|
1341
|
+
|
|
1342
|
+
// src/skills/duckdb.ts
|
|
1343
|
+
var duckdb_default = `
|
|
1344
|
+
# duckdb \u2014 SQL analytics on files
|
|
1345
|
+
|
|
1346
|
+
## When to use
|
|
1347
|
+
Query CSV, JSON, Parquet, or Excel files with SQL. No database setup, no import step \u2014 just point and query.
|
|
1348
|
+
|
|
1349
|
+
## Trusted commands
|
|
1350
|
+
- Query a CSV: \`duckdb -c "SELECT * FROM 'data.csv' LIMIT 10"\`
|
|
1351
|
+
- Aggregate: \`duckdb -c "SELECT col, COUNT(*) FROM 'data.csv' GROUP BY col"\`
|
|
1352
|
+
- Query JSON: \`duckdb -c "SELECT * FROM read_json_auto('data.json')"\`
|
|
1353
|
+
- Query Parquet: \`duckdb -c "SELECT * FROM 'data.parquet'"\`
|
|
1354
|
+
- Join files: \`duckdb -c "SELECT a.*, b.name FROM 'users.csv' a JOIN 'orders.csv' b ON a.id = b.user_id"\`
|
|
1355
|
+
- Export result: \`duckdb -c "COPY (SELECT * FROM 'data.csv') TO 'out.parquet' (FORMAT PARQUET)"\`
|
|
1356
|
+
- Interactive REPL: \`duckdb\`
|
|
1357
|
+
|
|
1358
|
+
## Why it matters for agents
|
|
1359
|
+
When an agent encounters a data file, duckdb lets it explore and answer questions via SQL instead of writing custom Python/JS parsing code. Massive time saver for any data task.
|
|
1360
|
+
|
|
1361
|
+
## Gotchas
|
|
1362
|
+
- Auto-detects CSV headers and types. Use \`read_csv('file.csv', header=false)\` to disable.
|
|
1363
|
+
- For one-off queries, use \`-c\`. For multiple, use the REPL or a .sql file.
|
|
1364
|
+
- Outputs to stdout in table format by default. Use \`-csv\` or \`-json\` for machine-readable output.
|
|
1365
|
+
`.trim();
|
|
1366
|
+
|
|
1367
|
+
// src/skills/htmlq.ts
|
|
1368
|
+
var htmlq_default = `
|
|
1369
|
+
# htmlq \u2014 jq for HTML
|
|
1370
|
+
|
|
1371
|
+
## When to use
|
|
1372
|
+
Extract content from HTML files or piped HTML using CSS selectors. Like jq but for HTML/web content.
|
|
1373
|
+
|
|
1374
|
+
## Trusted commands
|
|
1375
|
+
- Extract by selector: \`cat page.html | htmlq '.article-title'\`
|
|
1376
|
+
- Get attribute: \`cat page.html | htmlq 'a' --attribute href\`
|
|
1377
|
+
- Text only (strip tags): \`cat page.html | htmlq '.content' --text\`
|
|
1378
|
+
- Pipe from curl: \`curl -s https://example.com | htmlq 'h1'\`
|
|
1379
|
+
- Multiple selectors: \`cat page.html | htmlq 'h1, h2, h3'\`
|
|
1380
|
+
- Pretty print: \`cat page.html | htmlq --pretty 'body'\`
|
|
1381
|
+
|
|
1382
|
+
## Why it matters for agents
|
|
1383
|
+
When agents fetch web content or work with HTML files, htmlq extracts exactly what's needed without writing a parser.
|
|
1384
|
+
|
|
1385
|
+
## Gotchas
|
|
1386
|
+
- Reads from stdin \u2014 always pipe input to it.
|
|
1387
|
+
- CSS selectors only (not XPath).
|
|
1388
|
+
- Use \`--text\` to get text content without HTML tags.
|
|
1389
|
+
`.trim();
|
|
1390
|
+
|
|
1391
|
+
// src/skills/typos.ts
|
|
1392
|
+
var typos_default = `
|
|
1393
|
+
# typos \u2014 Source code spell checker
|
|
1394
|
+
|
|
1395
|
+
## When to use
|
|
1396
|
+
Find and fix typos in source code, comments, docs, filenames, and variable names. Runs in milliseconds across entire codebases.
|
|
1397
|
+
|
|
1398
|
+
## Trusted commands
|
|
1399
|
+
- Check current directory: \`typos\`
|
|
1400
|
+
- Check specific path: \`typos src/\`
|
|
1401
|
+
- Auto-fix typos: \`typos --write-changes\`
|
|
1402
|
+
- Diff mode (show what would change): \`typos --diff\`
|
|
1403
|
+
- Check specific file types: \`typos --type rust src/\`
|
|
1404
|
+
- Ignore a word: add to \`_typos.toml\`: \`[default.extend-words]\` \u2192 \`teh = "teh"\`
|
|
1405
|
+
|
|
1406
|
+
## Why it matters for agents
|
|
1407
|
+
Agents generate a lot of code. Running typos as a final pass catches misspellings in variable names, comments, and docs that slip past linters.
|
|
1408
|
+
|
|
1409
|
+
## Gotchas
|
|
1410
|
+
- Configure exceptions in \`_typos.toml\` or \`.typos.toml\` at project root.
|
|
1411
|
+
- Understands common programming conventions (camelCase, snake_case, etc.).
|
|
1412
|
+
- Very fast \u2014 designed to run on every commit.
|
|
1413
|
+
`.trim();
|
|
1414
|
+
|
|
1415
|
+
// src/skills/gum.ts
|
|
1416
|
+
var gum_default = `
|
|
1417
|
+
# gum \u2014 Interactive UI components for shell scripts
|
|
1418
|
+
|
|
1419
|
+
## When to use
|
|
1420
|
+
Add interactive prompts, spinners, confirmations, file pickers, and styled text to shell scripts without complex TUI code.
|
|
1421
|
+
|
|
1422
|
+
## Trusted commands
|
|
1423
|
+
- Confirm (y/n): \`gum confirm "Deploy to production?"\`
|
|
1424
|
+
- Choose from list: \`gum choose "option1" "option2" "option3"\`
|
|
1425
|
+
- Text input: \`gum input --placeholder "Enter name"\`
|
|
1426
|
+
- Multi-line input: \`gum write --placeholder "Description"\`
|
|
1427
|
+
- Spinner while running: \`gum spin --title "Building..." -- make build\`
|
|
1428
|
+
- Filter list (fuzzy): \`echo "a\\nb\\nc" | gum filter\`
|
|
1429
|
+
- Styled text: \`gum style --foreground 212 --bold "Done!"\`
|
|
1430
|
+
- File picker: \`gum file .\`
|
|
1431
|
+
|
|
1432
|
+
## Why it matters for agents
|
|
1433
|
+
When agents create shell scripts or automation, gum adds polished interactivity with single commands. No dependency on dialog, whiptail, or complex TUI libraries.
|
|
1434
|
+
|
|
1435
|
+
## Gotchas
|
|
1436
|
+
- Each command returns the selected/entered value to stdout \u2014 capture with \`$(gum ...)\`.
|
|
1437
|
+
- Part of the Charm ecosystem (same as glow, lazygit).
|
|
1438
|
+
- \`gum confirm\` returns exit code 0 (yes) or 1 (no).
|
|
1439
|
+
`.trim();
|
|
1440
|
+
|
|
1441
|
+
// src/skills/direnv.ts
|
|
1442
|
+
var direnv_default = `
|
|
1443
|
+
# direnv \u2014 Auto-load env vars per directory
|
|
1444
|
+
|
|
1445
|
+
## When to use
|
|
1446
|
+
Automatically load and unload environment variables when entering/leaving a directory. Each project gets its own env without polluting your shell.
|
|
1447
|
+
|
|
1448
|
+
## Trusted commands
|
|
1449
|
+
- Allow current .envrc: \`direnv allow\`
|
|
1450
|
+
- Block current .envrc: \`direnv deny\`
|
|
1451
|
+
- Edit .envrc: \`direnv edit .\`
|
|
1452
|
+
- Check status: \`direnv status\`
|
|
1453
|
+
- Reload: \`direnv reload\`
|
|
1454
|
+
|
|
1455
|
+
## Common .envrc patterns
|
|
1456
|
+
- Set vars: \`export API_KEY=xxx\`
|
|
1457
|
+
- Add to PATH: \`PATH_add ./bin\`
|
|
1458
|
+
- Use a specific Node version: \`use node 20\` (with mise/nvm layout)
|
|
1459
|
+
- Load .env file: \`dotenv\`
|
|
1460
|
+
|
|
1461
|
+
## Gotchas
|
|
1462
|
+
- Requires shell hook \u2014 add \`eval "$(direnv hook zsh)"\` to .zshrc (or bash equivalent).
|
|
1463
|
+
- New .envrc files must be explicitly allowed with \`direnv allow\` (security feature).
|
|
1464
|
+
- Changes to .envrc require re-allowing.
|
|
1465
|
+
`.trim();
|
|
1466
|
+
|
|
1467
|
+
// src/skills/procs.ts
|
|
1468
|
+
var procs_default = `
|
|
1469
|
+
# procs \u2014 Modern ps replacement
|
|
1470
|
+
|
|
1471
|
+
## When to use
|
|
1472
|
+
List and search running processes with structured, colourful output. Replacement for \`ps aux | grep\`.
|
|
1473
|
+
|
|
1474
|
+
## Trusted commands
|
|
1475
|
+
- List all processes: \`procs\`
|
|
1476
|
+
- Search by name: \`procs node\`
|
|
1477
|
+
- Tree view: \`procs --tree\`
|
|
1478
|
+
- Watch mode: \`procs --watch\`
|
|
1479
|
+
- Sort by CPU: \`procs --sortd cpu\`
|
|
1480
|
+
- Sort by memory: \`procs --sortd mem\`
|
|
1481
|
+
|
|
1482
|
+
## Why it matters for agents
|
|
1483
|
+
Agents diagnosing "port in use" or runaway process issues get structured output without pipe chains.
|
|
1484
|
+
|
|
1485
|
+
## Gotchas
|
|
1486
|
+
- Output is human-readable by default. For scripting, pipe through \`--color=never\` or use standard \`ps\` + jq.
|
|
1487
|
+
- Search matches against command name and full command line.
|
|
1488
|
+
`.trim();
|
|
1489
|
+
|
|
1490
|
+
// src/skills/uv.ts
|
|
1491
|
+
var uv_default = `
|
|
1492
|
+
# uv \u2014 Fast Python package and env manager
|
|
1493
|
+
|
|
1494
|
+
## When to use
|
|
1495
|
+
Manage Python virtual environments and install packages. 10-100x faster than pip. Drop-in replacement for pip, pip-tools, and virtualenv.
|
|
1496
|
+
|
|
1497
|
+
## Trusted commands
|
|
1498
|
+
- Create venv: \`uv venv\`
|
|
1499
|
+
- Install package: \`uv pip install requests\`
|
|
1500
|
+
- Install from requirements: \`uv pip install -r requirements.txt\`
|
|
1501
|
+
- Compile requirements (lock): \`uv pip compile requirements.in -o requirements.txt\`
|
|
1502
|
+
- Run a script with deps: \`uv run --with requests script.py\`
|
|
1503
|
+
- Init a new project: \`uv init my-project\`
|
|
1504
|
+
- Add dependency: \`uv add requests\`
|
|
1505
|
+
- Sync project: \`uv sync\`
|
|
1506
|
+
|
|
1507
|
+
## Why it matters for agents
|
|
1508
|
+
When agents set up Python projects or install dependencies, uv makes it near-instant. No waiting for pip's dependency resolver.
|
|
1509
|
+
|
|
1510
|
+
## Gotchas
|
|
1511
|
+
- Creates \`.venv/\` by default (same as standard venv).
|
|
1512
|
+
- \`uv pip\` commands are pip-compatible \u2014 same flags and syntax.
|
|
1513
|
+
- \`uv run\` can run scripts with inline dependencies without a project.
|
|
1514
|
+
`.trim();
|
|
1515
|
+
|
|
1516
|
+
// src/skills/hexyl.ts
|
|
1517
|
+
var hexyl_default = `
|
|
1518
|
+
# hexyl \u2014 Hex viewer
|
|
1519
|
+
|
|
1520
|
+
## When to use
|
|
1521
|
+
Inspect binary files, check file headers/magic bytes, debug encoding issues, or examine corrupted data.
|
|
1522
|
+
|
|
1523
|
+
## Trusted commands
|
|
1524
|
+
- View file: \`hexyl file.bin\`
|
|
1525
|
+
- First N bytes: \`hexyl -n 256 file.bin\`
|
|
1526
|
+
- Skip to offset: \`hexyl -s 0x100 file.bin\`
|
|
1527
|
+
- Pipe from stdin: \`echo "hello" | hexyl\`
|
|
1528
|
+
- Specific range: \`hexyl -r 0x10..0x20 file.bin\`
|
|
1529
|
+
|
|
1530
|
+
## Why it matters for agents
|
|
1531
|
+
Agents encountering unknown binary files can quickly identify format (via magic bytes), spot encoding issues, or verify file integrity.
|
|
1532
|
+
|
|
1533
|
+
## Gotchas
|
|
1534
|
+
- Output is always to stdout \u2014 not interactive/scrollable. Pipe to \`less -R\` for large files.
|
|
1535
|
+
- Colour codes bytes by type: NULL, ASCII printable, ASCII whitespace, non-ASCII.
|
|
1536
|
+
`.trim();
|
|
1537
|
+
|
|
1538
|
+
// src/skills/taplo.ts
|
|
1539
|
+
var taplo_default = `
|
|
1540
|
+
# taplo \u2014 TOML toolkit
|
|
1541
|
+
|
|
1542
|
+
## When to use
|
|
1543
|
+
Lint, format, and query TOML files. Completes the config format toolkit: jq (JSON) + yq (YAML) + taplo (TOML).
|
|
1544
|
+
|
|
1545
|
+
## Trusted commands
|
|
1546
|
+
- Format a file: \`taplo fmt Cargo.toml\`
|
|
1547
|
+
- Format all TOML: \`taplo fmt\`
|
|
1548
|
+
- Check formatting: \`taplo fmt --check\`
|
|
1549
|
+
- Lint: \`taplo lint Cargo.toml\`
|
|
1550
|
+
- Get a value: \`taplo get -f pyproject.toml "project.name"\`
|
|
1551
|
+
|
|
1552
|
+
## Why it matters for agents
|
|
1553
|
+
Agents editing Cargo.toml, pyproject.toml, or any .toml config can validate and format cleanly. Prevents syntax errors in config files.
|
|
1554
|
+
|
|
1555
|
+
## Gotchas
|
|
1556
|
+
- Config via \`taplo.toml\` or \`.taplo.toml\` at project root.
|
|
1557
|
+
- \`taplo get\` uses dot-notation paths for querying.
|
|
1558
|
+
- Supports schema validation for known TOML formats (Cargo, pyproject, etc.).
|
|
1559
|
+
- \`taplo fmt --check\` exits 1 on formatting diffs \u2014 CI-friendly.
|
|
1560
|
+
- Use \`--colored=off\` for clean CI output.
|
|
1561
|
+
`.trim();
|
|
1562
|
+
|
|
1563
|
+
// src/skills/semgrep.ts
|
|
1564
|
+
var semgrep_default = `
|
|
1565
|
+
# semgrep \u2014 Multi-language static analysis
|
|
1566
|
+
|
|
1567
|
+
## When to use
|
|
1568
|
+
Scan code for security vulnerabilities, bugs, and anti-patterns across 30+ languages. Write custom rules or use community rulesets.
|
|
1569
|
+
|
|
1570
|
+
## Trusted commands
|
|
1571
|
+
- Scan with auto rules: \`semgrep scan --config auto\`
|
|
1572
|
+
- Scan specific directory: \`semgrep scan --config auto src/\`
|
|
1573
|
+
- Security-focused scan: \`semgrep scan --config p/security-audit\`
|
|
1574
|
+
- OWASP top 10: \`semgrep scan --config p/owasp-top-ten\`
|
|
1575
|
+
- Specific language: \`semgrep scan --config p/typescript\`
|
|
1576
|
+
- Scan single file: \`semgrep scan --config auto path/to/file.ts\`
|
|
1577
|
+
- JSON output: \`semgrep scan --config auto --json\`
|
|
1578
|
+
|
|
1579
|
+
## Why it matters for agents
|
|
1580
|
+
Agents can run security and quality scans before committing code. Much broader language coverage than shellcheck or biome alone.
|
|
1581
|
+
|
|
1582
|
+
## Gotchas
|
|
1583
|
+
- First run downloads rules (needs internet). Subsequent runs use cache.
|
|
1584
|
+
- \`--config auto\` selects rules based on detected languages.
|
|
1585
|
+
- Community rules at semgrep.dev/explore \u2014 use \`--config p/RULESET\` to reference them.
|
|
1586
|
+
- Can write custom rules in YAML for project-specific patterns.
|
|
1587
|
+
`.trim();
|
|
1588
|
+
|
|
1589
|
+
// src/skills/age.ts
|
|
1590
|
+
var age_default = `
|
|
1591
|
+
# age \u2014 Simple file encryption
|
|
1592
|
+
|
|
1593
|
+
## When to use
|
|
1594
|
+
Encrypt and decrypt files with a passphrase or public key. Modern replacement for GPG \u2014 much simpler API.
|
|
1595
|
+
|
|
1596
|
+
## Trusted commands
|
|
1597
|
+
- Encrypt with passphrase: \`age -p -o secret.age secret.txt\`
|
|
1598
|
+
- Decrypt: \`age -d -o secret.txt secret.age\`
|
|
1599
|
+
- Generate key pair: \`age-keygen -o key.txt\`
|
|
1600
|
+
- Encrypt to recipient: \`age -r age1... -o secret.age secret.txt\`
|
|
1601
|
+
- Encrypt to multiple recipients: \`age -r age1... -r age1... -o secret.age secret.txt\`
|
|
1602
|
+
- Pipe: \`echo "secret" | age -p > encrypted.age\`
|
|
1603
|
+
|
|
1604
|
+
## Why it matters for agents
|
|
1605
|
+
When agents handle .env files, API keys, or sensitive configs, age provides simple encrypt/decrypt without GPG complexity.
|
|
1606
|
+
|
|
1607
|
+
## Gotchas
|
|
1608
|
+
- Passphrase mode (\`-p\`) prompts interactively. For scripting, pipe passphrase or use recipient mode.
|
|
1609
|
+
- Output file must be specified with \`-o\` (doesn't overwrite input).
|
|
1610
|
+
- age-keygen outputs private key to file, prints public key to stderr.
|
|
1611
|
+
`.trim();
|
|
1612
|
+
|
|
1613
|
+
// src/skills/doggo.ts
|
|
1614
|
+
var doggo_default = `
|
|
1615
|
+
# doggo \u2014 Modern DNS client
|
|
1616
|
+
|
|
1617
|
+
## When to use
|
|
1618
|
+
Look up DNS records for domains. Modern alternative to dig/nslookup with coloured output and JSON support.
|
|
1619
|
+
|
|
1620
|
+
## Trusted commands
|
|
1621
|
+
- Basic lookup: \`doggo example.com\`
|
|
1622
|
+
- Specific record type: \`doggo example.com MX\`
|
|
1623
|
+
- All common types: \`doggo example.com A AAAA MX TXT CNAME\`
|
|
1624
|
+
- Use specific nameserver: \`doggo example.com @8.8.8.8\`
|
|
1625
|
+
- DNS over HTTPS: \`doggo example.com @https://dns.google/dns-query\`
|
|
1626
|
+
- JSON output: \`doggo example.com --json\`
|
|
1627
|
+
- Short output: \`doggo example.com --short\`
|
|
1628
|
+
|
|
1629
|
+
## Why it matters for agents
|
|
1630
|
+
Agents verifying domain setups, debugging DNS issues, or checking propagation get structured output they can parse.
|
|
1631
|
+
|
|
1632
|
+
## Gotchas
|
|
1633
|
+
- Defaults to system resolver. Specify \`@server\` to use a specific nameserver.
|
|
1634
|
+
- Supports DoH (DNS over HTTPS) and DoT (DNS over TLS).
|
|
1635
|
+
- JSON output with \`--json\` is ideal for piping to jq.
|
|
1366
1636
|
`.trim();
|
|
1367
1637
|
|
|
1368
1638
|
// src/skills.ts
|
|
@@ -1404,18 +1674,67 @@ var SKILL_CONTENT = {
|
|
|
1404
1674
|
lazygit: lazygit_default,
|
|
1405
1675
|
dust: dust_default,
|
|
1406
1676
|
btm: btm_default,
|
|
1407
|
-
gitleaks: gitleaks_default
|
|
1677
|
+
gitleaks: gitleaks_default,
|
|
1678
|
+
pandoc: pandoc_default,
|
|
1679
|
+
duckdb: duckdb_default,
|
|
1680
|
+
htmlq: htmlq_default,
|
|
1681
|
+
typos: typos_default,
|
|
1682
|
+
gum: gum_default,
|
|
1683
|
+
direnv: direnv_default,
|
|
1684
|
+
procs: procs_default,
|
|
1685
|
+
uv: uv_default,
|
|
1686
|
+
hexyl: hexyl_default,
|
|
1687
|
+
taplo: taplo_default,
|
|
1688
|
+
semgrep: semgrep_default,
|
|
1689
|
+
age: age_default,
|
|
1690
|
+
doggo: doggo_default
|
|
1408
1691
|
};
|
|
1409
|
-
function
|
|
1410
|
-
return
|
|
1692
|
+
function skillFilename(toolId) {
|
|
1693
|
+
return `${PREFIX}-${toolId}.md`;
|
|
1694
|
+
}
|
|
1695
|
+
function buildFrontmatter(tool) {
|
|
1696
|
+
const lines = [
|
|
1697
|
+
"---",
|
|
1698
|
+
`tool: ${tool.id}`,
|
|
1699
|
+
`name: ${tool.name}`,
|
|
1700
|
+
`description: ${tool.description}`,
|
|
1701
|
+
`category: ${tool.preset}`
|
|
1702
|
+
];
|
|
1703
|
+
if (tool.tags?.length) {
|
|
1704
|
+
lines.push(`tags: [${tool.tags.join(", ")}]`);
|
|
1705
|
+
}
|
|
1706
|
+
if (tool.seeAlso?.length) {
|
|
1707
|
+
lines.push(`see-also: [${tool.seeAlso.join(", ")}]`);
|
|
1708
|
+
}
|
|
1709
|
+
lines.push("source: agent-loadout", "---", "");
|
|
1710
|
+
return lines.join("\n");
|
|
1711
|
+
}
|
|
1712
|
+
async function findToolsMissingSkills(toolIds, dir = paths.skillTargets.claude) {
|
|
1713
|
+
const results = await Promise.all(
|
|
1714
|
+
toolIds.map(async (id) => {
|
|
1715
|
+
const filePath = join2(dir, skillFilename(id));
|
|
1716
|
+
try {
|
|
1717
|
+
await access(filePath);
|
|
1718
|
+
return null;
|
|
1719
|
+
} catch {
|
|
1720
|
+
return id;
|
|
1721
|
+
}
|
|
1722
|
+
})
|
|
1723
|
+
);
|
|
1724
|
+
return results.filter((id) => id !== null);
|
|
1411
1725
|
}
|
|
1412
1726
|
async function writeSkills(tools) {
|
|
1413
|
-
await
|
|
1727
|
+
await ensureSkillDirs();
|
|
1728
|
+
const allDirs = [...Object.values(paths.skillTargets), paths.genericSkills];
|
|
1414
1729
|
let written = 0;
|
|
1415
1730
|
for (const tool of tools) {
|
|
1416
1731
|
const content = SKILL_CONTENT[tool.id];
|
|
1417
1732
|
if (!content) continue;
|
|
1418
|
-
|
|
1733
|
+
const filename = skillFilename(tool.id);
|
|
1734
|
+
const frontmatter = buildFrontmatter(tool);
|
|
1735
|
+
for (const dir of allDirs) {
|
|
1736
|
+
await writeFile3(join2(dir, filename), frontmatter + content + "\n");
|
|
1737
|
+
}
|
|
1419
1738
|
written++;
|
|
1420
1739
|
}
|
|
1421
1740
|
return written;
|
|
@@ -1423,24 +1742,26 @@ async function writeSkills(tools) {
|
|
|
1423
1742
|
|
|
1424
1743
|
// src/index.ts
|
|
1425
1744
|
var program = new Command();
|
|
1426
|
-
program.name("agent-loadout").description("One command to load out your terminal for agentic coding").version("
|
|
1745
|
+
program.name("agent-loadout").description("One command to load out your terminal for agentic coding").version("1.0.0");
|
|
1427
1746
|
process.on("SIGINT", () => {
|
|
1428
|
-
console.log(
|
|
1747
|
+
console.log(chalk10.dim("\n Cancelled."));
|
|
1429
1748
|
process.exit(0);
|
|
1430
1749
|
});
|
|
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
|
|
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
|
-
}
|
|
1750
|
+
program.command("install", { isDefault: true }).description("Install tools (interactive by default)").option("--preset <presets...>", "Install specific presets without prompts").option("--tool <tools...>", "Install specific tools by ID").option("--skip <tools...>", "Exclude specific tools by ID").option("--all", "Install everything").option("--apply", "Actually run the install (default is preview only)").action(async (opts) => {
|
|
1751
|
+
const platformInfo = await detectPlatform();
|
|
1442
1752
|
let tools;
|
|
1443
|
-
if (opts.
|
|
1753
|
+
if (opts.tool) {
|
|
1754
|
+
const { valid, invalid } = validateToolIds(opts.tool);
|
|
1755
|
+
if (invalid.length > 0) {
|
|
1756
|
+
console.log(
|
|
1757
|
+
chalk10.red(
|
|
1758
|
+
`Unknown tool${invalid.length > 1 ? "s" : ""}: ${invalid.map((t) => `'${t}'`).join(", ")}. Run ${chalk10.dim("agent-loadout list --json")} to see all tool IDs.`
|
|
1759
|
+
)
|
|
1760
|
+
);
|
|
1761
|
+
process.exit(1);
|
|
1762
|
+
}
|
|
1763
|
+
tools = getToolsByIds(valid);
|
|
1764
|
+
} else if (opts.all) {
|
|
1444
1765
|
tools = TOOLS;
|
|
1445
1766
|
} else if (opts.preset) {
|
|
1446
1767
|
const rawIds = opts.preset;
|
|
@@ -1448,7 +1769,7 @@ program.command("install", { isDefault: true }).description("Install tools (inte
|
|
|
1448
1769
|
const invalid = rawIds.filter((p) => !validIds.includes(p));
|
|
1449
1770
|
if (invalid.length > 0) {
|
|
1450
1771
|
console.log(
|
|
1451
|
-
|
|
1772
|
+
chalk10.red(
|
|
1452
1773
|
`Unknown preset${invalid.length > 1 ? "s" : ""}: ${invalid.map((p) => `'${p}'`).join(", ")}. Available: ${validIds.join(", ")}`
|
|
1453
1774
|
)
|
|
1454
1775
|
);
|
|
@@ -1459,79 +1780,76 @@ program.command("install", { isDefault: true }).description("Install tools (inte
|
|
|
1459
1780
|
} else {
|
|
1460
1781
|
const presetIds = await selectPresets();
|
|
1461
1782
|
if (presetIds.length === 0) {
|
|
1462
|
-
console.log(
|
|
1783
|
+
console.log(chalk10.dim("No presets selected. Nothing to install."));
|
|
1463
1784
|
return;
|
|
1464
1785
|
}
|
|
1465
1786
|
tools = await selectTools(presetIds);
|
|
1466
1787
|
if (tools.length === 0) {
|
|
1467
|
-
console.log(
|
|
1788
|
+
console.log(chalk10.dim("No tools selected. Nothing to install."));
|
|
1468
1789
|
return;
|
|
1469
1790
|
}
|
|
1470
1791
|
}
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1792
|
+
if (opts.skip) {
|
|
1793
|
+
const skipSet = new Set(opts.skip);
|
|
1794
|
+
tools = tools.filter((t) => !skipSet.has(t.id));
|
|
1795
|
+
if (tools.length === 0) {
|
|
1796
|
+
console.log(chalk10.dim("All tools were skipped. Nothing to install."));
|
|
1797
|
+
return;
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
const plan = resolveInstallPlan(tools, platformInfo);
|
|
1801
|
+
printPreview(tools, plan, platformInfo);
|
|
1802
|
+
if (!opts.apply && (opts.all || opts.preset || opts.tool)) {
|
|
1803
|
+
const parts = [];
|
|
1804
|
+
if (opts.tool) parts.push(`--tool ${opts.tool.join(" ")}`);
|
|
1805
|
+
else if (opts.all) parts.push("--all");
|
|
1806
|
+
else parts.push(`--preset ${opts.preset.join(" ")}`);
|
|
1807
|
+
if (opts.skip) parts.push(`--skip ${opts.skip.join(" ")}`);
|
|
1808
|
+
console.log(chalk10.yellow("Dry run \u2014 add --apply to install. Example:"));
|
|
1476
1809
|
console.log(
|
|
1477
|
-
|
|
1478
|
-
` npx agent-loadout install ${opts.all ? "--all" : `--preset ${opts.preset.join(" ")}`} --apply`
|
|
1479
|
-
)
|
|
1810
|
+
chalk10.dim(` npx agent-loadout install ${parts.join(" ")} --apply`)
|
|
1480
1811
|
);
|
|
1481
1812
|
return;
|
|
1482
1813
|
}
|
|
1483
|
-
if (
|
|
1814
|
+
if (plan.resolved.length === 0) {
|
|
1815
|
+
console.log(chalk10.dim("No installable tools for this platform."));
|
|
1816
|
+
return;
|
|
1817
|
+
}
|
|
1818
|
+
if (!opts.all && !opts.preset && !opts.tool) {
|
|
1484
1819
|
const proceed = await confirmInstall();
|
|
1485
1820
|
if (!proceed) {
|
|
1486
|
-
console.log(
|
|
1821
|
+
console.log(chalk10.dim("Cancelled."));
|
|
1487
1822
|
return;
|
|
1488
1823
|
}
|
|
1489
1824
|
}
|
|
1490
|
-
|
|
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
|
-
}
|
|
1825
|
+
await runInstallPlan(plan);
|
|
1512
1826
|
console.log();
|
|
1513
|
-
console.log(
|
|
1514
|
-
const
|
|
1827
|
+
console.log(chalk10.bold("Verifying..."));
|
|
1828
|
+
const resolvedTools = plan.resolved.map((r) => r.tool);
|
|
1829
|
+
const results = await verifyTools(resolvedTools, platformInfo.platform);
|
|
1515
1830
|
printVerifyResults(results);
|
|
1516
|
-
const skillCount = await writeSkills(
|
|
1831
|
+
const skillCount = await writeSkills(resolvedTools);
|
|
1517
1832
|
if (skillCount > 0) {
|
|
1833
|
+
const targets = Object.keys(paths.skillTargets).join(", ");
|
|
1518
1834
|
console.log(
|
|
1519
|
-
|
|
1520
|
-
${skillCount} skill files written to ~/.
|
|
1835
|
+
chalk10.dim(`
|
|
1836
|
+
${skillCount} skill files written to ${targets} + ~/.agent-loadout/skills/`)
|
|
1521
1837
|
);
|
|
1522
1838
|
}
|
|
1523
1839
|
await writeReceipt({
|
|
1840
|
+
platform: platformInfo.platform,
|
|
1524
1841
|
selections: tools.map((t) => t.id),
|
|
1525
1842
|
installed: verifyResultsToJson(results),
|
|
1526
1843
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1527
1844
|
});
|
|
1528
|
-
console.log(
|
|
1845
|
+
console.log(chalk10.dim(" Receipt saved to ~/.agent-loadout/receipt.json"));
|
|
1529
1846
|
});
|
|
1530
1847
|
program.command("verify").alias("doctor").description("Check which tools are installed").option("--json", "Output as JSON").action(async (opts) => {
|
|
1848
|
+
const platformInfo = await detectPlatform();
|
|
1531
1849
|
const receipt = await readReceipt();
|
|
1532
1850
|
const toolIds = receipt?.selections ?? TOOLS.map((t) => t.id);
|
|
1533
1851
|
const tools = getToolsByIds(toolIds);
|
|
1534
|
-
const results = await verifyTools(tools);
|
|
1852
|
+
const results = await verifyTools(tools, platformInfo.platform);
|
|
1535
1853
|
const installed = results.filter((r) => r.installed).length;
|
|
1536
1854
|
const allInstalled = installed === results.length;
|
|
1537
1855
|
if (opts.json) {
|
|
@@ -1539,6 +1857,7 @@ program.command("verify").alias("doctor").description("Check which tools are ins
|
|
|
1539
1857
|
JSON.stringify(
|
|
1540
1858
|
{
|
|
1541
1859
|
ok: allInstalled,
|
|
1860
|
+
platform: platformInfo.platform,
|
|
1542
1861
|
installed,
|
|
1543
1862
|
total: results.length,
|
|
1544
1863
|
tools: results
|
|
@@ -1552,26 +1871,67 @@ program.command("verify").alias("doctor").description("Check which tools are ins
|
|
|
1552
1871
|
}
|
|
1553
1872
|
process.exit(allInstalled ? 0 : 1);
|
|
1554
1873
|
});
|
|
1555
|
-
program.command("list").description("Print the tool catalog").option("--json", "Output as JSON").action((opts) => {
|
|
1874
|
+
program.command("list").description("Print the tool catalog").option("--json", "Output as JSON").option("--brewfile", "Output macOS Brewfile (darwin only)").action(async (opts) => {
|
|
1875
|
+
if (opts.brewfile) {
|
|
1876
|
+
const { generateBrewfileFromCatalog } = await import("./catalog-PTLCQEDW.js");
|
|
1877
|
+
console.log(generateBrewfileFromCatalog());
|
|
1878
|
+
return;
|
|
1879
|
+
}
|
|
1556
1880
|
if (opts.json) {
|
|
1557
1881
|
console.log(JSON.stringify({ presets: PRESETS, tools: TOOLS }, null, 2));
|
|
1558
1882
|
return;
|
|
1559
1883
|
}
|
|
1884
|
+
const platformInfo = await detectPlatform();
|
|
1560
1885
|
for (const preset of PRESETS) {
|
|
1561
|
-
const marker = preset.defaultOn ?
|
|
1886
|
+
const marker = preset.defaultOn ? chalk10.green("\u25CF") : chalk10.dim("\u25CB");
|
|
1562
1887
|
console.log(
|
|
1563
1888
|
`
|
|
1564
|
-
${marker} ${
|
|
1889
|
+
${marker} ${chalk10.bold(preset.name)} \u2014 ${preset.description}`
|
|
1565
1890
|
);
|
|
1566
1891
|
const presetTools = TOOLS.filter((t) => t.preset === preset.id);
|
|
1567
1892
|
const maxName = Math.max(...presetTools.map((t) => t.name.length));
|
|
1568
1893
|
for (const t of presetTools) {
|
|
1569
|
-
const
|
|
1894
|
+
const platformInstalls = t.install[platformInfo.platform];
|
|
1895
|
+
const method = platformInstalls ? chalk10.yellow(platformInstalls[0].method) : chalk10.dim("n/a");
|
|
1570
1896
|
console.log(
|
|
1571
|
-
` ${t.name.padEnd(maxName)} ${method} ${
|
|
1897
|
+
` ${t.name.padEnd(maxName)} ${method} ${chalk10.dim(t.description)}`
|
|
1572
1898
|
);
|
|
1573
1899
|
}
|
|
1574
1900
|
}
|
|
1575
1901
|
console.log();
|
|
1576
1902
|
});
|
|
1903
|
+
program.command("skills").description("Write skill files for installed tools (fills gaps by default)").option("--force", "Rewrite skill files for all installed tools").action(async (opts) => {
|
|
1904
|
+
const platformInfo = await detectPlatform();
|
|
1905
|
+
const results = await verifyTools(TOOLS, platformInfo.platform);
|
|
1906
|
+
const installedIds = new Set(results.filter((r) => r.installed).map((r) => r.id));
|
|
1907
|
+
const installedTools = TOOLS.filter((t) => installedIds.has(t.id));
|
|
1908
|
+
if (installedTools.length === 0) {
|
|
1909
|
+
console.log(chalk10.dim(" No installed tools found."));
|
|
1910
|
+
return;
|
|
1911
|
+
}
|
|
1912
|
+
if (opts.force) {
|
|
1913
|
+
console.log(chalk10.dim(` Writing skills for ${installedTools.length} installed tools...`));
|
|
1914
|
+
const written2 = await writeSkills(installedTools);
|
|
1915
|
+
const targets2 = Object.keys(paths.skillTargets).join(", ");
|
|
1916
|
+
console.log(
|
|
1917
|
+
chalk10.green(` ${written2} skill files written to ${targets2} + ~/.agent-loadout/skills/`)
|
|
1918
|
+
);
|
|
1919
|
+
return;
|
|
1920
|
+
}
|
|
1921
|
+
console.log(chalk10.dim(` Scanning ${installedTools.length} installed tools...`));
|
|
1922
|
+
const missingIds = await findToolsMissingSkills(installedTools.map((t) => t.id));
|
|
1923
|
+
if (missingIds.length === 0) {
|
|
1924
|
+
console.log(chalk10.green(` \u2713 All ${installedTools.length} installed tools already have skill files.`));
|
|
1925
|
+
return;
|
|
1926
|
+
}
|
|
1927
|
+
const alreadyHave = installedTools.length - missingIds.length;
|
|
1928
|
+
console.log(chalk10.dim(` ${alreadyHave} already have skill files`));
|
|
1929
|
+
console.log(chalk10.dim(` Writing ${missingIds.length} missing: ${missingIds.join(", ")}`));
|
|
1930
|
+
const toolsToWrite = installedTools.filter((t) => missingIds.includes(t.id));
|
|
1931
|
+
const written = await writeSkills(toolsToWrite);
|
|
1932
|
+
const targets = Object.keys(paths.skillTargets).join(", ");
|
|
1933
|
+
console.log(
|
|
1934
|
+
chalk10.green(` ${written} skill files written to ${targets} + ~/.agent-loadout/skills/`)
|
|
1935
|
+
);
|
|
1936
|
+
});
|
|
1577
1937
|
program.parse();
|