@iceinvein/agent-skills 0.1.27 → 0.1.29
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 +24 -1
- package/dist/cli/index.js +243 -16
- package/package.json +2 -1
- package/skills/index.json +153 -123
- package/skills/magpie/README.md +7 -8
- package/skills/magpie/install.sh +13 -22
- package/skills/magpie/skill.json +22 -3
- package/skills/magpie/uninstall.sh +31 -0
package/README.md
CHANGED
|
@@ -126,6 +126,29 @@ bunx @iceinvein/agent-skills info <skill>
|
|
|
126
126
|
| `--activation <mode>` | For skills that support it: `session` (manual `/skill`) or `global` (auto via `SessionStart` hook). Claude Code only. |
|
|
127
127
|
| `-g, --global` | Install to home directory (available in all projects) |
|
|
128
128
|
|
|
129
|
+
## Updating Skills
|
|
130
|
+
|
|
131
|
+
Each skill is versioned independently (see `skill.json` inside each skill). The CLI tracks what you installed in `.agent-skills.lock` and can pull newer versions from GitHub.
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
bunx @iceinvein/agent-skills update <skill> # update one skill
|
|
135
|
+
bunx @iceinvein/agent-skills update --all # update everything in the lockfile
|
|
136
|
+
bunx @iceinvein/agent-skills list # show installed versions
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
`update` re-fetches the latest manifest and files from this repo, removes the old install, and re-installs cleanly. Your chosen activation mode (session vs. global) is preserved across updates, as is the set of tools you installed for. Use `-g` to update globally-installed skills.
|
|
140
|
+
|
|
141
|
+
After any command other than `update`, the CLI runs a background freshness check (at most once every 24 hours) and prints a one-line notice if anything is out of date:
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
some-skill (v0.2.0 → v0.3.0)
|
|
145
|
+
Run: agent-skills update --all
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The check is best-effort: it silently skips on network failure and only hits GitHub once per day per project. To force a re-check sooner, delete the `lastUpdateCheck` field in `.agent-skills.lock` (or just run any command after 24 hours have elapsed).
|
|
149
|
+
|
|
150
|
+
To pin to the current version, do nothing: skills are not auto-updated. `update` is opt-in.
|
|
151
|
+
|
|
129
152
|
## Activation Modes (Claude Code)
|
|
130
153
|
|
|
131
154
|
Some skills — like `terse` — support activation modes. Pick one at install time:
|
|
@@ -159,7 +182,7 @@ Skills install to the **current project** by default. Use `-g` to install to you
|
|
|
159
182
|
|
|
160
183
|
## Contributing
|
|
161
184
|
|
|
162
|
-
- [Releasing](docs/RELEASING.md)
|
|
185
|
+
- [Releasing](docs/RELEASING.md): how versions are bumped, tagged, and published to npm
|
|
163
186
|
|
|
164
187
|
## License
|
|
165
188
|
|
package/dist/cli/index.js
CHANGED
|
@@ -248,6 +248,30 @@ function validateManifest(data) {
|
|
|
248
248
|
if (typeof d.install !== "object" || d.install === null) {
|
|
249
249
|
return { ok: false, error: "Missing 'install' configuration" };
|
|
250
250
|
}
|
|
251
|
+
if (d.bundle !== undefined) {
|
|
252
|
+
if (typeof d.bundle !== "object" || d.bundle === null) {
|
|
253
|
+
return { ok: false, error: "'bundle' must be an object" };
|
|
254
|
+
}
|
|
255
|
+
const b = d.bundle;
|
|
256
|
+
if (!Array.isArray(b.include) || b.include.length === 0) {
|
|
257
|
+
return { ok: false, error: "'bundle.include' must be a non-empty array" };
|
|
258
|
+
}
|
|
259
|
+
for (const p of b.include) {
|
|
260
|
+
if (typeof p !== "string") {
|
|
261
|
+
return { ok: false, error: "'bundle.include' entries must be strings" };
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (b.exclude !== undefined) {
|
|
265
|
+
if (!Array.isArray(b.exclude)) {
|
|
266
|
+
return { ok: false, error: "'bundle.exclude' must be an array" };
|
|
267
|
+
}
|
|
268
|
+
for (const p of b.exclude) {
|
|
269
|
+
if (typeof p !== "string") {
|
|
270
|
+
return { ok: false, error: "'bundle.exclude' entries must be strings" };
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
251
275
|
if (d.activation !== undefined) {
|
|
252
276
|
if (typeof d.activation !== "object" || d.activation === null) {
|
|
253
277
|
return { ok: false, error: "'activation' must be an object" };
|
|
@@ -318,12 +342,87 @@ async function fetchAllSkillFiles(skillName, manifest) {
|
|
|
318
342
|
files.set(supportFile, result.content);
|
|
319
343
|
}
|
|
320
344
|
}
|
|
345
|
+
if (manifest.bundle) {
|
|
346
|
+
const treeResult = await fetchSkillTree(skillName);
|
|
347
|
+
if (!treeResult.ok)
|
|
348
|
+
return { error: treeResult.error };
|
|
349
|
+
const bundlePaths = resolveBundlePaths(treeResult.entries, manifest.bundle);
|
|
350
|
+
for (const relPath of bundlePaths) {
|
|
351
|
+
if (files.has(relPath))
|
|
352
|
+
continue;
|
|
353
|
+
const result = await fetchSkillFile(skillName, relPath);
|
|
354
|
+
if (!result.ok)
|
|
355
|
+
return { error: result.error };
|
|
356
|
+
files.set(relPath, result.content);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
321
359
|
return files;
|
|
322
360
|
}
|
|
361
|
+
async function fetchSkillTree(skillName) {
|
|
362
|
+
const url = `https://api.github.com/repos/${REPO}/git/trees/${BRANCH}?recursive=1`;
|
|
363
|
+
const res = await fetch(url, {
|
|
364
|
+
headers: { Accept: "application/vnd.github+json" }
|
|
365
|
+
});
|
|
366
|
+
if (!res.ok) {
|
|
367
|
+
return { ok: false, error: `Failed to fetch repo tree: HTTP ${res.status}` };
|
|
368
|
+
}
|
|
369
|
+
const data = await res.json();
|
|
370
|
+
if (data.truncated) {
|
|
371
|
+
return { ok: false, error: "GitHub tree response was truncated; skill bundle too large to enumerate" };
|
|
372
|
+
}
|
|
373
|
+
const prefix = `skills/${skillName}/`;
|
|
374
|
+
const entries = data.tree.filter((e) => e.path.startsWith(prefix) && (e.type === "blob" || e.type === "tree")).map((e) => ({ path: e.path.slice(prefix.length), type: e.type }));
|
|
375
|
+
return { ok: true, entries };
|
|
376
|
+
}
|
|
377
|
+
function resolveBundlePaths(entries, bundle) {
|
|
378
|
+
const excludes = bundle.exclude ?? [];
|
|
379
|
+
const isExcluded = (p) => excludes.some((ex) => p === ex || p.startsWith(ex));
|
|
380
|
+
const dirs = new Set;
|
|
381
|
+
const files = new Set;
|
|
382
|
+
for (const entry of entries) {
|
|
383
|
+
if (entry.type === "tree")
|
|
384
|
+
dirs.add(entry.path);
|
|
385
|
+
else
|
|
386
|
+
files.add(entry.path);
|
|
387
|
+
}
|
|
388
|
+
const result = new Set;
|
|
389
|
+
for (const include of bundle.include) {
|
|
390
|
+
if (dirs.has(include)) {
|
|
391
|
+
const prefix = include.endsWith("/") ? include : include + "/";
|
|
392
|
+
for (const f of files) {
|
|
393
|
+
if (f.startsWith(prefix) && !isExcluded(f))
|
|
394
|
+
result.add(f);
|
|
395
|
+
}
|
|
396
|
+
} else if (files.has(include)) {
|
|
397
|
+
if (!isExcluded(include))
|
|
398
|
+
result.add(include);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
return [...result].sort();
|
|
402
|
+
}
|
|
323
403
|
|
|
324
404
|
// src/cli/adapters/claude.ts
|
|
325
|
-
import { existsSync as existsSync2, mkdirSync, rmSync, unlinkSync } from "node:fs";
|
|
405
|
+
import { chmodSync, existsSync as existsSync2, mkdirSync, rmSync, statSync, unlinkSync } from "node:fs";
|
|
326
406
|
import { dirname, join as join2 } from "node:path";
|
|
407
|
+
function shouldBeExecutable(relPath, content) {
|
|
408
|
+
if (relPath.endsWith(".sh"))
|
|
409
|
+
return true;
|
|
410
|
+
if (relPath.startsWith("bin/") || relPath.includes("/bin/"))
|
|
411
|
+
return true;
|
|
412
|
+
if (content.startsWith("#!"))
|
|
413
|
+
return true;
|
|
414
|
+
return false;
|
|
415
|
+
}
|
|
416
|
+
function runScript(scriptPath, cwd) {
|
|
417
|
+
if (!existsSync2(scriptPath))
|
|
418
|
+
return { ok: true, code: 0 };
|
|
419
|
+
const result = Bun.spawnSync(["bash", scriptPath], {
|
|
420
|
+
cwd,
|
|
421
|
+
stdout: "inherit",
|
|
422
|
+
stderr: "inherit"
|
|
423
|
+
});
|
|
424
|
+
return { ok: result.exitCode === 0, code: result.exitCode ?? 1 };
|
|
425
|
+
}
|
|
327
426
|
function matchesSkillDirective(command, skillName) {
|
|
328
427
|
return command.includes(`Activate ${skillName} skill`);
|
|
329
428
|
}
|
|
@@ -394,6 +493,21 @@ var claudeAdapter = {
|
|
|
394
493
|
}
|
|
395
494
|
}
|
|
396
495
|
}
|
|
496
|
+
if (config.bundleRoot && manifest.bundle) {
|
|
497
|
+
const promptPath = manifest.files?.prompt;
|
|
498
|
+
for (const [relPath, content] of files) {
|
|
499
|
+
if (relPath === promptPath)
|
|
500
|
+
continue;
|
|
501
|
+
const targetRel = join2(config.bundleRoot, relPath);
|
|
502
|
+
const targetPath = join2(cwd, targetRel);
|
|
503
|
+
mkdirSync(dirname(targetPath), { recursive: true });
|
|
504
|
+
await Bun.write(targetPath, content);
|
|
505
|
+
if (shouldBeExecutable(relPath, content)) {
|
|
506
|
+
chmodSync(targetPath, 493);
|
|
507
|
+
}
|
|
508
|
+
installed.push(targetRel);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
397
511
|
if (config.mcpServers) {
|
|
398
512
|
const settingsPath = join2(cwd, ".claude/settings.json");
|
|
399
513
|
let settings = {};
|
|
@@ -416,22 +530,49 @@ var claudeAdapter = {
|
|
|
416
530
|
installed.push(".claude/settings.json");
|
|
417
531
|
}
|
|
418
532
|
}
|
|
533
|
+
if (config.postinstall && config.bundleRoot) {
|
|
534
|
+
const scriptPath = join2(cwd, config.bundleRoot, config.postinstall);
|
|
535
|
+
const result = runScript(scriptPath, join2(cwd, config.bundleRoot));
|
|
536
|
+
if (!result.ok) {
|
|
537
|
+
console.error(`postinstall script '${config.postinstall}' exited with code ${result.code}`);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
419
540
|
return installed;
|
|
420
541
|
},
|
|
421
542
|
async remove(cwd, manifest, installedFiles) {
|
|
422
543
|
const config = manifest.install.claude;
|
|
423
544
|
if (!config)
|
|
424
545
|
return;
|
|
546
|
+
if (config.postremove && config.bundleRoot) {
|
|
547
|
+
const scriptPath = join2(cwd, config.bundleRoot, config.postremove);
|
|
548
|
+
const result = runScript(scriptPath, join2(cwd, config.bundleRoot));
|
|
549
|
+
if (!result.ok) {
|
|
550
|
+
console.error(`postremove script '${config.postremove}' exited with code ${result.code}`);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
425
553
|
for (const file of installedFiles) {
|
|
426
554
|
if (file === ".claude/settings.json")
|
|
427
555
|
continue;
|
|
428
556
|
const fullPath = join2(cwd, file);
|
|
429
557
|
if (existsSync2(fullPath)) {
|
|
430
|
-
unlinkSync(fullPath);
|
|
431
|
-
const dir = dirname(fullPath);
|
|
432
558
|
try {
|
|
433
|
-
|
|
559
|
+
const stat = statSync(fullPath);
|
|
560
|
+
if (stat.isDirectory()) {
|
|
561
|
+
rmSync(fullPath, { recursive: true, force: true });
|
|
562
|
+
} else {
|
|
563
|
+
unlinkSync(fullPath);
|
|
564
|
+
}
|
|
434
565
|
} catch {}
|
|
566
|
+
let dir = dirname(fullPath);
|
|
567
|
+
const stopAt = config.bundleRoot ? join2(cwd, config.bundleRoot, "..") : cwd;
|
|
568
|
+
while (dir !== stopAt && dir !== "/" && dir.startsWith(cwd)) {
|
|
569
|
+
try {
|
|
570
|
+
rmSync(dir, { recursive: false });
|
|
571
|
+
} catch {
|
|
572
|
+
break;
|
|
573
|
+
}
|
|
574
|
+
dir = dirname(dir);
|
|
575
|
+
}
|
|
435
576
|
}
|
|
436
577
|
}
|
|
437
578
|
if (config.mcpServers) {
|
|
@@ -768,8 +909,64 @@ async function fetchAllSkillFiles2(skillName, manifest) {
|
|
|
768
909
|
files.set(supportFile, result.content);
|
|
769
910
|
}
|
|
770
911
|
}
|
|
912
|
+
if (manifest.bundle) {
|
|
913
|
+
const treeResult = await fetchSkillTree2(skillName);
|
|
914
|
+
if (!treeResult.ok)
|
|
915
|
+
return { error: treeResult.error };
|
|
916
|
+
const bundlePaths = resolveBundlePaths2(treeResult.entries, manifest.bundle);
|
|
917
|
+
for (const relPath of bundlePaths) {
|
|
918
|
+
if (files.has(relPath))
|
|
919
|
+
continue;
|
|
920
|
+
const result = await fetchSkillFile2(skillName, relPath);
|
|
921
|
+
if (!result.ok)
|
|
922
|
+
return { error: result.error };
|
|
923
|
+
files.set(relPath, result.content);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
771
926
|
return files;
|
|
772
927
|
}
|
|
928
|
+
async function fetchSkillTree2(skillName) {
|
|
929
|
+
const url = `https://api.github.com/repos/${REPO2}/git/trees/${BRANCH2}?recursive=1`;
|
|
930
|
+
const res = await fetch(url, {
|
|
931
|
+
headers: { Accept: "application/vnd.github+json" }
|
|
932
|
+
});
|
|
933
|
+
if (!res.ok) {
|
|
934
|
+
return { ok: false, error: `Failed to fetch repo tree: HTTP ${res.status}` };
|
|
935
|
+
}
|
|
936
|
+
const data = await res.json();
|
|
937
|
+
if (data.truncated) {
|
|
938
|
+
return { ok: false, error: "GitHub tree response was truncated; skill bundle too large to enumerate" };
|
|
939
|
+
}
|
|
940
|
+
const prefix = `skills/${skillName}/`;
|
|
941
|
+
const entries = data.tree.filter((e) => e.path.startsWith(prefix) && (e.type === "blob" || e.type === "tree")).map((e) => ({ path: e.path.slice(prefix.length), type: e.type }));
|
|
942
|
+
return { ok: true, entries };
|
|
943
|
+
}
|
|
944
|
+
function resolveBundlePaths2(entries, bundle) {
|
|
945
|
+
const excludes = bundle.exclude ?? [];
|
|
946
|
+
const isExcluded = (p) => excludes.some((ex) => p === ex || p.startsWith(ex));
|
|
947
|
+
const dirs = new Set;
|
|
948
|
+
const files = new Set;
|
|
949
|
+
for (const entry of entries) {
|
|
950
|
+
if (entry.type === "tree")
|
|
951
|
+
dirs.add(entry.path);
|
|
952
|
+
else
|
|
953
|
+
files.add(entry.path);
|
|
954
|
+
}
|
|
955
|
+
const result = new Set;
|
|
956
|
+
for (const include of bundle.include) {
|
|
957
|
+
if (dirs.has(include)) {
|
|
958
|
+
const prefix = include.endsWith("/") ? include : include + "/";
|
|
959
|
+
for (const f of files) {
|
|
960
|
+
if (f.startsWith(prefix) && !isExcluded(f))
|
|
961
|
+
result.add(f);
|
|
962
|
+
}
|
|
963
|
+
} else if (files.has(include)) {
|
|
964
|
+
if (!isExcluded(include))
|
|
965
|
+
result.add(include);
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
return [...result].sort();
|
|
969
|
+
}
|
|
773
970
|
|
|
774
971
|
// src/cli/commands/remove.ts
|
|
775
972
|
async function removeSkill(cwd, skillName) {
|
|
@@ -1081,6 +1278,18 @@ async function checkForUpdates(cwd) {
|
|
|
1081
1278
|
}
|
|
1082
1279
|
}
|
|
1083
1280
|
|
|
1281
|
+
// src/cli/lockfile.ts
|
|
1282
|
+
import { join as join8 } from "node:path";
|
|
1283
|
+
var LOCKFILE_NAME2 = ".agent-skills.lock";
|
|
1284
|
+
async function readLockfile2(cwd) {
|
|
1285
|
+
const path = join8(cwd, LOCKFILE_NAME2);
|
|
1286
|
+
const file = Bun.file(path);
|
|
1287
|
+
if (!await file.exists()) {
|
|
1288
|
+
return { skills: {} };
|
|
1289
|
+
}
|
|
1290
|
+
return file.json();
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1084
1293
|
// src/cli/types.ts
|
|
1085
1294
|
var TOOL_NAMES2 = ["claude", "cursor", "codex", "gemini"];
|
|
1086
1295
|
|
|
@@ -2953,8 +3162,19 @@ async function pickActivation(skillName, modes) {
|
|
|
2953
3162
|
|
|
2954
3163
|
// src/cli/index.ts
|
|
2955
3164
|
import { mkdirSync as mkdirSync4 } from "fs";
|
|
2956
|
-
import { join as
|
|
3165
|
+
import { join as join9 } from "path";
|
|
2957
3166
|
import { homedir } from "os";
|
|
3167
|
+
async function otherScopeSkillCount(currentDir) {
|
|
3168
|
+
const home = homedir();
|
|
3169
|
+
const otherDir = currentDir === home ? process.cwd() : home;
|
|
3170
|
+
if (otherDir === currentDir)
|
|
3171
|
+
return { scope: "global", count: 0 };
|
|
3172
|
+
const lockfile = await readLockfile2(otherDir);
|
|
3173
|
+
return {
|
|
3174
|
+
scope: otherDir === home ? "global" : "local",
|
|
3175
|
+
count: Object.keys(lockfile.skills).length
|
|
3176
|
+
};
|
|
3177
|
+
}
|
|
2958
3178
|
function resolveInstallDir(flags) {
|
|
2959
3179
|
if (flags.global !== undefined || flags.g !== undefined) {
|
|
2960
3180
|
return homedir();
|
|
@@ -3046,7 +3266,7 @@ async function resolveTargetTools(installDir, isGlobal, flags) {
|
|
|
3046
3266
|
for (const tool of picked) {
|
|
3047
3267
|
const dir = TOOL_DIRS[tool];
|
|
3048
3268
|
if (dir)
|
|
3049
|
-
mkdirSync4(
|
|
3269
|
+
mkdirSync4(join9(installDir, dir), { recursive: true });
|
|
3050
3270
|
}
|
|
3051
3271
|
return picked;
|
|
3052
3272
|
}
|
|
@@ -3160,18 +3380,25 @@ Installing ${names.length} skills...
|
|
|
3160
3380
|
`);
|
|
3161
3381
|
const results = await updateAllSkills(updateDir);
|
|
3162
3382
|
if (results.length === 0) {
|
|
3163
|
-
console.log("No skills installed.");
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3383
|
+
console.log("No skills installed in this scope.");
|
|
3384
|
+
} else {
|
|
3385
|
+
for (const r of results) {
|
|
3386
|
+
if (!r.ok) {
|
|
3387
|
+
console.error(` \u2717 ${r.name}: ${r.error}`);
|
|
3388
|
+
} else if (r.from === r.to) {
|
|
3389
|
+
console.log(` \u2298 ${r.name} already up to date (v${r.to})`);
|
|
3390
|
+
} else {
|
|
3391
|
+
console.log(` \u2713 ${r.name} v${r.from} \u2192 v${r.to}`);
|
|
3392
|
+
}
|
|
3173
3393
|
}
|
|
3174
3394
|
}
|
|
3395
|
+
const other = await otherScopeSkillCount(updateDir);
|
|
3396
|
+
if (other.count > 0) {
|
|
3397
|
+
const command2 = other.scope === "global" ? "agent-skills update --all -g" : "agent-skills update --all";
|
|
3398
|
+
const noun = other.count === 1 ? "skill" : "skills";
|
|
3399
|
+
console.log(`
|
|
3400
|
+
Note: ${other.count} ${noun} installed in the ${other.scope} scope. Run \`${command2}\` to update those too.`);
|
|
3401
|
+
}
|
|
3175
3402
|
break;
|
|
3176
3403
|
}
|
|
3177
3404
|
const skillName = args[0];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iceinvein/agent-skills",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.29",
|
|
4
4
|
"description": "Install agent skills into AI coding tools",
|
|
5
5
|
"author": "iceinvein",
|
|
6
6
|
"license": "MIT",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"skill:bump": "bun run src/cli/index.ts bump",
|
|
20
20
|
"skill:bump:all": "bun run src/cli/index.ts bump --all",
|
|
21
21
|
"skill:bump:check": "bun run src/cli/index.ts bump --all --dry-run",
|
|
22
|
+
"build:index": "bun run scripts/build-index.ts",
|
|
22
23
|
"release": "bash scripts/release.sh"
|
|
23
24
|
},
|
|
24
25
|
"keywords": [
|
package/skills/index.json
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
[
|
|
2
2
|
{
|
|
3
|
-
"name": "
|
|
4
|
-
"description": "
|
|
5
|
-
"type": "prompt",
|
|
6
|
-
"version": "1.0.0",
|
|
7
|
-
"applies": ["any"],
|
|
8
|
-
"quick": false
|
|
9
|
-
},
|
|
10
|
-
{
|
|
11
|
-
"name": "codebase-architecture",
|
|
12
|
-
"description": "Architecture review for existing codebases or structured design for new projects, with patterns reference",
|
|
3
|
+
"name": "bounded-context-auditor",
|
|
4
|
+
"description": "Evans-inspired bounded context analysis — detect linguistic fractures, draw context maps, identify leaking language and shared model pollution",
|
|
13
5
|
"type": "prompt",
|
|
14
6
|
"version": "1.0.0",
|
|
15
|
-
"applies": [
|
|
7
|
+
"applies": [
|
|
8
|
+
"domain"
|
|
9
|
+
],
|
|
16
10
|
"quick": false
|
|
17
11
|
},
|
|
18
12
|
{
|
|
@@ -22,107 +16,124 @@
|
|
|
22
16
|
"version": "1.0.0"
|
|
23
17
|
},
|
|
24
18
|
{
|
|
25
|
-
"name": "
|
|
26
|
-
"description": "
|
|
19
|
+
"name": "codebase-architecture",
|
|
20
|
+
"description": "Architecture review for existing codebases or structured design for new projects, with patterns reference",
|
|
27
21
|
"type": "prompt",
|
|
28
22
|
"version": "1.0.0",
|
|
29
|
-
"applies": [
|
|
23
|
+
"applies": [
|
|
24
|
+
"any",
|
|
25
|
+
"architecture"
|
|
26
|
+
],
|
|
30
27
|
"quick": false
|
|
31
28
|
},
|
|
32
29
|
{
|
|
33
|
-
"name": "
|
|
34
|
-
"description": "
|
|
30
|
+
"name": "cognitive-load-auditor",
|
|
31
|
+
"description": "Jeff Johnson-inspired cognitive load analysis — evaluate UI against Miller's Law, Hick's Law, Fitts's Law, and working memory limits",
|
|
35
32
|
"type": "prompt",
|
|
36
33
|
"version": "1.0.0",
|
|
37
|
-
"applies": [
|
|
34
|
+
"applies": [
|
|
35
|
+
"ui"
|
|
36
|
+
],
|
|
38
37
|
"quick": false
|
|
39
38
|
},
|
|
40
39
|
{
|
|
41
|
-
"name": "
|
|
42
|
-
"description": "
|
|
43
|
-
"type": "prompt",
|
|
44
|
-
"version": "1.0.0",
|
|
45
|
-
"applies": ["architecture"],
|
|
46
|
-
"quick": true
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
"name": "seam-finder",
|
|
50
|
-
"description": "Feathers-inspired legacy code modification — find seams, make minimal incisions, preserve existing behavior",
|
|
40
|
+
"name": "cohesion-analyzer",
|
|
41
|
+
"description": "Constantine & Yourdon-inspired cohesion analysis — classify module focus on the 7-level spectrum, find split lines, reduce mixed responsibilities",
|
|
51
42
|
"type": "prompt",
|
|
52
43
|
"version": "1.0.0",
|
|
53
|
-
"applies": [
|
|
44
|
+
"applies": [
|
|
45
|
+
"architecture"
|
|
46
|
+
],
|
|
54
47
|
"quick": true
|
|
55
48
|
},
|
|
56
49
|
{
|
|
57
|
-
"name": "
|
|
58
|
-
"description": "
|
|
50
|
+
"name": "complexity-accountant",
|
|
51
|
+
"description": "Ousterhout-inspired complexity analysis — deep vs shallow modules, complexity budget, justified abstractions",
|
|
59
52
|
"type": "prompt",
|
|
60
53
|
"version": "1.0.0",
|
|
61
|
-
"applies": [
|
|
54
|
+
"applies": [
|
|
55
|
+
"any"
|
|
56
|
+
],
|
|
62
57
|
"quick": false
|
|
63
58
|
},
|
|
64
59
|
{
|
|
65
|
-
"name": "
|
|
66
|
-
"description": "
|
|
60
|
+
"name": "composability-auditor",
|
|
61
|
+
"description": "Unix Philosophy-inspired composability analysis — identify reuse blockers, decompose self-sufficient units into composable pieces with standard interfaces",
|
|
67
62
|
"type": "prompt",
|
|
68
63
|
"version": "1.0.0",
|
|
69
|
-
"applies": [
|
|
70
|
-
|
|
64
|
+
"applies": [
|
|
65
|
+
"any"
|
|
66
|
+
],
|
|
67
|
+
"quick": false
|
|
71
68
|
},
|
|
72
69
|
{
|
|
73
|
-
"name": "
|
|
74
|
-
"description": "
|
|
70
|
+
"name": "contract-enforcer",
|
|
71
|
+
"description": "Meyer-inspired Design by Contract — preconditions, postconditions, invariants, and failure contracts for non-trivial functions",
|
|
75
72
|
"type": "prompt",
|
|
76
73
|
"version": "1.0.0",
|
|
77
|
-
"applies": [
|
|
74
|
+
"applies": [
|
|
75
|
+
"any"
|
|
76
|
+
],
|
|
78
77
|
"quick": false
|
|
79
78
|
},
|
|
80
79
|
{
|
|
81
|
-
"name": "
|
|
82
|
-
"description": "
|
|
80
|
+
"name": "coupling-auditor",
|
|
81
|
+
"description": "Constantine & Yourdon-inspired coupling analysis — classify, measure, and reduce interdependence between modules",
|
|
83
82
|
"type": "prompt",
|
|
84
83
|
"version": "1.0.0",
|
|
85
|
-
"applies": [
|
|
86
|
-
|
|
84
|
+
"applies": [
|
|
85
|
+
"architecture"
|
|
86
|
+
],
|
|
87
|
+
"quick": true
|
|
87
88
|
},
|
|
88
89
|
{
|
|
89
|
-
"name": "
|
|
90
|
-
"description": "
|
|
90
|
+
"name": "cqs-auditor",
|
|
91
|
+
"description": "Meyer-inspired Command-Query Separation — classify functions as commands or queries, detect mixed violations, separate side effects from return values",
|
|
91
92
|
"type": "prompt",
|
|
92
93
|
"version": "1.0.0",
|
|
93
|
-
"applies": [
|
|
94
|
+
"applies": [
|
|
95
|
+
"architecture"
|
|
96
|
+
],
|
|
94
97
|
"quick": true
|
|
95
98
|
},
|
|
96
99
|
{
|
|
97
|
-
"name": "
|
|
98
|
-
"description": "
|
|
100
|
+
"name": "demeter-enforcer",
|
|
101
|
+
"description": "Lieberherr-inspired Law of Demeter analysis — detect chain violations, parameter drilling, and hidden traversal; fix with tell-don't-ask or parameter narrowing",
|
|
99
102
|
"type": "prompt",
|
|
100
103
|
"version": "1.0.0",
|
|
101
|
-
"applies": [
|
|
102
|
-
|
|
104
|
+
"applies": [
|
|
105
|
+
"architecture"
|
|
106
|
+
],
|
|
107
|
+
"quick": true
|
|
103
108
|
},
|
|
104
109
|
{
|
|
105
|
-
"name": "
|
|
106
|
-
"description": "
|
|
110
|
+
"name": "dependency-direction-auditor",
|
|
111
|
+
"description": "Martin-inspired dependency direction analysis — trace imports across layers, classify violations by severity, recommend inversion",
|
|
107
112
|
"type": "prompt",
|
|
108
113
|
"version": "1.0.0",
|
|
109
|
-
"applies": [
|
|
114
|
+
"applies": [
|
|
115
|
+
"architecture"
|
|
116
|
+
],
|
|
110
117
|
"quick": true
|
|
111
118
|
},
|
|
112
119
|
{
|
|
113
|
-
"name": "
|
|
114
|
-
"description": "
|
|
120
|
+
"name": "design-review",
|
|
121
|
+
"description": "Brooks-inspired design integrity review — tests conceptual integrity, constraint exploitation, removal discipline, and scope control",
|
|
115
122
|
"type": "prompt",
|
|
116
123
|
"version": "1.0.0",
|
|
117
|
-
"applies": [
|
|
124
|
+
"applies": [
|
|
125
|
+
"any"
|
|
126
|
+
],
|
|
118
127
|
"quick": false
|
|
119
128
|
},
|
|
120
129
|
{
|
|
121
|
-
"name": "
|
|
122
|
-
"description": "
|
|
130
|
+
"name": "error-strategist",
|
|
131
|
+
"description": "Duffy & Abrahams-inspired error handling — classify errors (bug/recoverable/fatal), assign safety guarantees, design recovery boundaries",
|
|
123
132
|
"type": "prompt",
|
|
124
133
|
"version": "1.0.0",
|
|
125
|
-
"applies": [
|
|
134
|
+
"applies": [
|
|
135
|
+
"errors"
|
|
136
|
+
],
|
|
126
137
|
"quick": false
|
|
127
138
|
},
|
|
128
139
|
{
|
|
@@ -130,129 +141,148 @@
|
|
|
130
141
|
"description": "Evans/Vernon/Dahan-inspired event design — domain-meaningful naming, fat payloads, schema evolution, and the domain expert test",
|
|
131
142
|
"type": "prompt",
|
|
132
143
|
"version": "1.0.0",
|
|
133
|
-
"applies": [
|
|
144
|
+
"applies": [
|
|
145
|
+
"integration",
|
|
146
|
+
"domain"
|
|
147
|
+
],
|
|
134
148
|
"quick": false
|
|
135
149
|
},
|
|
136
150
|
{
|
|
137
|
-
"name": "
|
|
138
|
-
"description": "
|
|
139
|
-
"type": "prompt",
|
|
140
|
-
"version": "1.0.0",
|
|
141
|
-
"applies": ["architecture"],
|
|
142
|
-
"quick": true
|
|
143
|
-
},
|
|
144
|
-
{
|
|
145
|
-
"name": "cohesion-analyzer",
|
|
146
|
-
"description": "Constantine & Yourdon-inspired cohesion analysis — classify module focus on the 7-level spectrum, find split lines, reduce mixed responsibilities",
|
|
151
|
+
"name": "evolution-analyzer",
|
|
152
|
+
"description": "Lehman-inspired software evolution analysis — trajectory assessment, debt visibility, and change impact on system health",
|
|
147
153
|
"type": "prompt",
|
|
148
154
|
"version": "1.0.0",
|
|
149
|
-
"applies": [
|
|
150
|
-
|
|
155
|
+
"applies": [
|
|
156
|
+
"any"
|
|
157
|
+
],
|
|
158
|
+
"quick": false
|
|
151
159
|
},
|
|
152
160
|
{
|
|
153
|
-
"name": "
|
|
154
|
-
"description": "
|
|
161
|
+
"name": "gestalt-reviewer",
|
|
162
|
+
"description": "Gestalt-inspired visual perception audit — proximity, similarity, closure, continuity, and figure-ground analysis for UI layouts",
|
|
155
163
|
"type": "prompt",
|
|
156
164
|
"version": "1.0.0",
|
|
157
|
-
"applies": [
|
|
165
|
+
"applies": [
|
|
166
|
+
"ui"
|
|
167
|
+
],
|
|
158
168
|
"quick": true
|
|
159
169
|
},
|
|
160
170
|
{
|
|
161
|
-
"name": "
|
|
162
|
-
"description": "
|
|
171
|
+
"name": "idempotency-guardian",
|
|
172
|
+
"description": "Helland-inspired idempotency analysis — classify mutation points, check protection mechanisms, evaluate side effect safety for retry-safe systems",
|
|
163
173
|
"type": "prompt",
|
|
164
174
|
"version": "1.0.0",
|
|
165
|
-
"applies": [
|
|
175
|
+
"applies": [
|
|
176
|
+
"integration"
|
|
177
|
+
],
|
|
166
178
|
"quick": false
|
|
167
179
|
},
|
|
168
180
|
{
|
|
169
|
-
"name": "
|
|
170
|
-
"description": "
|
|
181
|
+
"name": "improve-my-codebase",
|
|
182
|
+
"description": "Orchestrator skill that runs every applicable audit skill in parallel and produces a prioritized, convergence-ranked improvement report",
|
|
171
183
|
"type": "prompt",
|
|
172
|
-
"version": "1.0.
|
|
173
|
-
"applies": ["architecture"],
|
|
174
|
-
"quick": false
|
|
184
|
+
"version": "1.0.1"
|
|
175
185
|
},
|
|
176
186
|
{
|
|
177
|
-
"name": "
|
|
178
|
-
"description": "
|
|
187
|
+
"name": "integration-pattern-auditor",
|
|
188
|
+
"description": "Hohpe & Woolf-inspired messaging analysis — name the integration pattern, verify delivery guarantees, identify missing infrastructure",
|
|
179
189
|
"type": "prompt",
|
|
180
190
|
"version": "1.0.0",
|
|
181
|
-
"applies": [
|
|
191
|
+
"applies": [
|
|
192
|
+
"integration"
|
|
193
|
+
],
|
|
182
194
|
"quick": false
|
|
183
195
|
},
|
|
184
196
|
{
|
|
185
|
-
"name": "
|
|
186
|
-
"description": "
|
|
197
|
+
"name": "magpie",
|
|
198
|
+
"description": "Interactive PR review pipeline. Runs five parallel specialist subagents (security, bugs, performance, code-smells, architecture), dedupes findings, applies a critic rubric, peer-reviews via codex exec, and serves an interactive HTML report for selecting findings to post via gh. Bundles a Bun CLI installed onto PATH via the skill's postinstall step. Use when the user asks to review a GitHub pull request.",
|
|
187
199
|
"type": "prompt",
|
|
188
|
-
"version": "
|
|
189
|
-
"applies": ["any"],
|
|
190
|
-
"quick": false
|
|
200
|
+
"version": "0.3.0"
|
|
191
201
|
},
|
|
192
202
|
{
|
|
193
|
-
"name": "
|
|
194
|
-
"description": "
|
|
203
|
+
"name": "module-secret-auditor",
|
|
204
|
+
"description": "Parnas-inspired information hiding analysis — module boundaries drawn by change-reason, not by noun or technical layer",
|
|
195
205
|
"type": "prompt",
|
|
196
206
|
"version": "1.0.0",
|
|
197
|
-
"applies": [
|
|
207
|
+
"applies": [
|
|
208
|
+
"architecture"
|
|
209
|
+
],
|
|
198
210
|
"quick": true
|
|
199
211
|
},
|
|
200
212
|
{
|
|
201
|
-
"name": "
|
|
202
|
-
"description": "
|
|
213
|
+
"name": "port-adapter-auditor",
|
|
214
|
+
"description": "Cockburn-inspired hexagonal architecture analysis — identify ports and adapters, classify boundary health, ensure core testability and swappability",
|
|
203
215
|
"type": "prompt",
|
|
204
216
|
"version": "1.0.0",
|
|
205
|
-
"applies": [
|
|
217
|
+
"applies": [
|
|
218
|
+
"architecture"
|
|
219
|
+
],
|
|
206
220
|
"quick": false
|
|
207
221
|
},
|
|
208
222
|
{
|
|
209
|
-
"name": "
|
|
210
|
-
"description": "
|
|
223
|
+
"name": "rams-design-audit",
|
|
224
|
+
"description": "Dieter Rams-inspired design audit — every visual element must earn its presence, less but better, clarity through restraint",
|
|
211
225
|
"type": "prompt",
|
|
212
226
|
"version": "1.0.0",
|
|
213
|
-
"applies": [
|
|
227
|
+
"applies": [
|
|
228
|
+
"ui"
|
|
229
|
+
],
|
|
214
230
|
"quick": true
|
|
215
231
|
},
|
|
216
232
|
{
|
|
217
|
-
"name": "
|
|
218
|
-
"description": "
|
|
219
|
-
"type": "prompt",
|
|
220
|
-
"version": "1.1.0"
|
|
221
|
-
},
|
|
222
|
-
{
|
|
223
|
-
"name": "cover-letter",
|
|
224
|
-
"description": "Cover letter suite router. Routes to write, audit, rewrite, or persona subcommands; shares conventions for file I/O, output formats, and writing principles.",
|
|
233
|
+
"name": "seam-finder",
|
|
234
|
+
"description": "Feathers-inspired legacy code modification — find seams, make minimal incisions, preserve existing behavior",
|
|
225
235
|
"type": "prompt",
|
|
226
|
-
"version": "1.0.0"
|
|
236
|
+
"version": "1.0.0",
|
|
237
|
+
"applies": [
|
|
238
|
+
"legacy"
|
|
239
|
+
],
|
|
240
|
+
"quick": true
|
|
227
241
|
},
|
|
228
242
|
{
|
|
229
|
-
"name": "
|
|
230
|
-
"description": "
|
|
243
|
+
"name": "simplicity-razor",
|
|
244
|
+
"description": "Hickey-inspired simplicity analysis — simple vs easy, complecting detection, strand decomposition",
|
|
231
245
|
"type": "prompt",
|
|
232
|
-
"version": "1.0.0"
|
|
246
|
+
"version": "1.0.0",
|
|
247
|
+
"applies": [
|
|
248
|
+
"any"
|
|
249
|
+
],
|
|
250
|
+
"quick": false
|
|
233
251
|
},
|
|
234
252
|
{
|
|
235
|
-
"name": "
|
|
236
|
-
"description": "
|
|
253
|
+
"name": "temporal-coupling-detector",
|
|
254
|
+
"description": "Hidden ordering dependency analysis — detect two-phase init, method order dependencies, invisible preconditions, and resource lifecycle violations; fix with types, parameters, and factory patterns",
|
|
237
255
|
"type": "prompt",
|
|
238
|
-
"version": "1.0.0"
|
|
256
|
+
"version": "1.0.0",
|
|
257
|
+
"applies": [
|
|
258
|
+
"any"
|
|
259
|
+
],
|
|
260
|
+
"quick": true
|
|
239
261
|
},
|
|
240
262
|
{
|
|
241
|
-
"name": "
|
|
242
|
-
"description": "
|
|
263
|
+
"name": "terse",
|
|
264
|
+
"description": "Professional output compression. Cuts ~20-30% of output tokens with proper grammar and semantic accuracy. Three levels: clean, tight, sharp.",
|
|
243
265
|
"type": "prompt",
|
|
244
|
-
"version": "1.
|
|
266
|
+
"version": "1.2.0"
|
|
245
267
|
},
|
|
246
268
|
{
|
|
247
|
-
"name": "
|
|
248
|
-
"description": "
|
|
269
|
+
"name": "type-driven-designer",
|
|
270
|
+
"description": "Wlaschin & Minsky-inspired type design — make illegal states unrepresentable through branded types, discriminated unions, and domain-encoded constraints",
|
|
249
271
|
"type": "prompt",
|
|
250
|
-
"version": "1.0.0"
|
|
272
|
+
"version": "1.0.0",
|
|
273
|
+
"applies": [
|
|
274
|
+
"any"
|
|
275
|
+
],
|
|
276
|
+
"quick": false
|
|
251
277
|
},
|
|
252
278
|
{
|
|
253
|
-
"name": "
|
|
254
|
-
"description": "
|
|
279
|
+
"name": "unidirectional-flow-enforcer",
|
|
280
|
+
"description": "Elm Architecture-inspired data flow analysis — enforce unidirectional state flow, detect bidirectional mutations, trace state lifecycle in UI applications",
|
|
255
281
|
"type": "prompt",
|
|
256
|
-
"version": "1.0.0"
|
|
282
|
+
"version": "1.0.0",
|
|
283
|
+
"applies": [
|
|
284
|
+
"ui"
|
|
285
|
+
],
|
|
286
|
+
"quick": false
|
|
257
287
|
}
|
|
258
288
|
]
|
package/skills/magpie/README.md
CHANGED
|
@@ -15,15 +15,13 @@ Given a GitHub PR number, dispatches five specialist subagents in parallel (secu
|
|
|
15
15
|
|
|
16
16
|
## Install
|
|
17
17
|
|
|
18
|
-
This skill ships in two parts: the prompt (SKILL.md) and a companion Bun CLI (bin + scripts). Both are needed, and `install.sh` handles both in one step.
|
|
19
|
-
|
|
20
18
|
```
|
|
21
|
-
|
|
19
|
+
bunx @iceinvein/agent-skills install magpie -g
|
|
22
20
|
```
|
|
23
21
|
|
|
24
|
-
|
|
22
|
+
This skill ships in two parts: the prompt (SKILL.md) and a companion Bun CLI (bin + scripts). The agent-skills installer writes both into `~/.claude/skills/magpie/` and then runs the bundled `install.sh` as a postinstall step, which symlinks `bin/magpie` onto your PATH (preferring `/usr/local/bin`, falling back to `~/.local/bin`). Removing the skill with `agent-skills remove magpie -g` runs `uninstall.sh` first to undo the PATH symlink.
|
|
25
23
|
|
|
26
|
-
|
|
24
|
+
If you cloned this repo and want to run from source, you can also invoke `./install.sh` directly: it does the PATH-link step against the local source tree.
|
|
27
25
|
|
|
28
26
|
## Use
|
|
29
27
|
|
|
@@ -57,11 +55,12 @@ The fixture lives at `fixtures/example-pr/` (pr.json + findings.final.json + pos
|
|
|
57
55
|
|
|
58
56
|
- `SKILL.md` is the agent-facing prompt; installed by the agent-skills CLI.
|
|
59
57
|
- `skill.json` is the agent-skills manifest.
|
|
60
|
-
- `bin/magpie` is the CLI invoked by the agent during stages;
|
|
58
|
+
- `bin/magpie` is the CLI invoked by the agent during stages; symlinked onto PATH by `install.sh`.
|
|
61
59
|
- `scripts/` holds the implementation (server, dedupe, render, setup, cleanup, etc.).
|
|
62
60
|
- `templates/styles.css` is the HTML report stylesheet.
|
|
63
|
-
- `fixtures/` holds canned PR data for tests.
|
|
64
|
-
- `install.sh`
|
|
61
|
+
- `fixtures/` holds canned PR data for tests (not shipped to users via the registry).
|
|
62
|
+
- `install.sh` is run as a postinstall step by the agent-skills installer; symlinks `bin/magpie` onto PATH and records the location.
|
|
63
|
+
- `uninstall.sh` is run as a postremove step; removes the PATH symlink if it still points back into this bundle.
|
|
65
64
|
|
|
66
65
|
## Design docs
|
|
67
66
|
|
package/skills/magpie/install.sh
CHANGED
|
@@ -1,25 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
|
+
# This script is run by agent-skills as a postinstall step after the magpie
|
|
5
|
+
# skill bundle has been written to disk. Its only job is to put the magpie
|
|
6
|
+
# CLI on PATH and record where it linked, so uninstall.sh can clean up.
|
|
7
|
+
|
|
4
8
|
SOURCE_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
5
|
-
|
|
9
|
+
BIN_SOURCE="$SOURCE_DIR/bin/magpie"
|
|
10
|
+
STATE_FILE="$SOURCE_DIR/.installed-cli-path"
|
|
6
11
|
|
|
7
|
-
if [
|
|
8
|
-
echo "
|
|
9
|
-
echo "Remove or move it manually, then re-run."
|
|
12
|
+
if [ ! -f "$BIN_SOURCE" ]; then
|
|
13
|
+
echo "magpie postinstall: bin/magpie missing at $BIN_SOURCE" >&2
|
|
10
14
|
exit 1
|
|
11
15
|
fi
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
ln -snf "$SOURCE_DIR" "$TARGET"
|
|
15
|
-
|
|
16
|
-
echo "Installed magpie skill at $TARGET"
|
|
17
|
-
|
|
18
|
-
# Install the magpie CLI onto PATH. Try /usr/local/bin first (no sudo
|
|
19
|
-
# needed if the user owns it, common on Homebrew Macs), fall back to
|
|
20
|
-
# ~/.local/bin (which most modern shells already have on PATH; we print a
|
|
21
|
-
# nudge if it isn't).
|
|
22
|
-
BIN_SOURCE="$SOURCE_DIR/bin/magpie"
|
|
17
|
+
chmod +x "$BIN_SOURCE" || true
|
|
23
18
|
|
|
24
19
|
install_link() {
|
|
25
20
|
local dest_dir="$1"
|
|
@@ -28,11 +23,9 @@ install_link() {
|
|
|
28
23
|
if [ ! -d "$dest_dir" ]; then
|
|
29
24
|
mkdir -p "$dest_dir" 2>/dev/null || return 1
|
|
30
25
|
fi
|
|
31
|
-
|
|
32
26
|
if [ ! -w "$dest_dir" ]; then
|
|
33
27
|
return 1
|
|
34
28
|
fi
|
|
35
|
-
|
|
36
29
|
if [ -e "$dest" ] && [ ! -L "$dest" ]; then
|
|
37
30
|
echo " skipping $dest_dir (a non-symlink magpie already exists there)"
|
|
38
31
|
return 2
|
|
@@ -40,23 +33,21 @@ install_link() {
|
|
|
40
33
|
|
|
41
34
|
ln -snf "$BIN_SOURCE" "$dest"
|
|
42
35
|
echo " linked $dest -> $BIN_SOURCE"
|
|
43
|
-
|
|
36
|
+
printf '%s\n' "$dest" > "$STATE_FILE"
|
|
44
37
|
return 0
|
|
45
38
|
}
|
|
46
39
|
|
|
47
|
-
echo ""
|
|
48
40
|
echo "Installing magpie CLI on PATH:"
|
|
49
41
|
|
|
50
42
|
CLI_INSTALL_DIR=""
|
|
51
43
|
if install_link "/usr/local/bin"; then
|
|
52
|
-
|
|
44
|
+
CLI_INSTALL_DIR="/usr/local/bin"
|
|
53
45
|
elif install_link "$HOME/.local/bin"; then
|
|
54
|
-
|
|
46
|
+
CLI_INSTALL_DIR="$HOME/.local/bin"
|
|
55
47
|
else
|
|
56
48
|
echo " Could not write to /usr/local/bin or ~/.local/bin."
|
|
57
|
-
echo " Add this to your shell rc instead:"
|
|
49
|
+
echo " Add this alias to your shell rc instead:"
|
|
58
50
|
echo " alias magpie='bun $BIN_SOURCE.ts'"
|
|
59
|
-
CLI_INSTALL_DIR=""
|
|
60
51
|
fi
|
|
61
52
|
|
|
62
53
|
if [ -n "$CLI_INSTALL_DIR" ]; then
|
package/skills/magpie/skill.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "magpie",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Interactive PR review pipeline. Runs five parallel specialist subagents (security, bugs, performance, code-smells, architecture), dedupes findings, applies a critic rubric, peer-reviews via codex exec, and serves an interactive HTML report for selecting findings to post via gh. Bundles a Bun CLI installed
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Interactive PR review pipeline. Runs five parallel specialist subagents (security, bugs, performance, code-smells, architecture), dedupes findings, applies a critic rubric, peer-reviews via codex exec, and serves an interactive HTML report for selecting findings to post via gh. Bundles a Bun CLI installed onto PATH via the skill's postinstall step. Use when the user asks to review a GitHub pull request.",
|
|
5
5
|
"author": "iceinvein",
|
|
6
6
|
"type": "prompt",
|
|
7
7
|
"tools": [
|
|
@@ -10,9 +10,28 @@
|
|
|
10
10
|
"files": {
|
|
11
11
|
"prompt": "SKILL.md"
|
|
12
12
|
},
|
|
13
|
+
"bundle": {
|
|
14
|
+
"include": [
|
|
15
|
+
"bin",
|
|
16
|
+
"scripts",
|
|
17
|
+
"templates",
|
|
18
|
+
"install.sh",
|
|
19
|
+
"uninstall.sh",
|
|
20
|
+
"package.json",
|
|
21
|
+
"bun.lock",
|
|
22
|
+
"README.md",
|
|
23
|
+
"LICENSE"
|
|
24
|
+
],
|
|
25
|
+
"exclude": [
|
|
26
|
+
"scripts/__tests__/"
|
|
27
|
+
]
|
|
28
|
+
},
|
|
13
29
|
"install": {
|
|
14
30
|
"claude": {
|
|
15
|
-
"prompt": ".claude/skills/magpie/SKILL.md"
|
|
31
|
+
"prompt": ".claude/skills/magpie/SKILL.md",
|
|
32
|
+
"bundleRoot": ".claude/skills/magpie",
|
|
33
|
+
"postinstall": "install.sh",
|
|
34
|
+
"postremove": "uninstall.sh"
|
|
16
35
|
}
|
|
17
36
|
}
|
|
18
37
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Run by agent-skills as a postremove step before the magpie skill bundle is
|
|
5
|
+
# deleted. Reads the path recorded by install.sh and removes the PATH symlink
|
|
6
|
+
# if it still points back into this bundle.
|
|
7
|
+
|
|
8
|
+
SOURCE_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
9
|
+
STATE_FILE="$SOURCE_DIR/.installed-cli-path"
|
|
10
|
+
|
|
11
|
+
if [ ! -f "$STATE_FILE" ]; then
|
|
12
|
+
exit 0
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
CLI_PATH="$(head -n 1 "$STATE_FILE")"
|
|
16
|
+
[ -z "$CLI_PATH" ] && exit 0
|
|
17
|
+
|
|
18
|
+
if [ -L "$CLI_PATH" ]; then
|
|
19
|
+
TARGET="$(readlink "$CLI_PATH")"
|
|
20
|
+
case "$TARGET" in
|
|
21
|
+
"$SOURCE_DIR"/*)
|
|
22
|
+
rm -f "$CLI_PATH"
|
|
23
|
+
echo "Removed magpie CLI symlink at $CLI_PATH"
|
|
24
|
+
;;
|
|
25
|
+
*)
|
|
26
|
+
echo "magpie uninstall: leaving $CLI_PATH alone (points to $TARGET, not this bundle)"
|
|
27
|
+
;;
|
|
28
|
+
esac
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
rm -f "$STATE_FILE"
|