pruff-skills 0.13.0 → 1.0.1
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 +59 -4
- package/dist/cli.mjs +110 -58
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -131,6 +131,8 @@ npx pruff-skills add <skill> -g -a claude-code -y
|
|
|
131
131
|
| `npx pruff-skills remove [skills]` | Remove installed skills from agents |
|
|
132
132
|
| `npx pruff-skills update [skills]` | Update installed skills to latest versions |
|
|
133
133
|
| `npx pruff-skills init [name]` | Create a new `SKILL.md` template |
|
|
134
|
+
| `npx pruff-skills restore` | Reinstall everything recorded in `skills-lock.json` |
|
|
135
|
+
| `npx pruff-skills sync` | Install skills found in `node_modules` into agent dirs |
|
|
134
136
|
|
|
135
137
|
```bash
|
|
136
138
|
npx pruff-skills list # list installed skills
|
|
@@ -174,10 +176,63 @@ Scaffold one with `npx pruff-skills init my-skill`, then publish it by pushing t
|
|
|
174
176
|
|
|
175
177
|
## Environment Variables
|
|
176
178
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
|
180
|
-
|
|
|
179
|
+
Read by pruff-skills itself:
|
|
180
|
+
|
|
181
|
+
| Variable | Description |
|
|
182
|
+
| -------- | ----------- |
|
|
183
|
+
| `PRUFF_SKILLS_CATALOG` | Source a bare skill name resolves to (default `Pruff-Tech/pruff-skills`). A GitHub `owner/repo`, git URL, or local path. Set to a local checkout for offline installs. |
|
|
184
|
+
| `PRUFF_SKILLS_NO_ANIM` | Disable the animated logo. Also implied by `NO_COLOR` and `CI`. |
|
|
185
|
+
| `PRUFF_SKILLS_DOWNLOAD_URL` | Base URL for blob downloads. Empty by default. |
|
|
186
|
+
| `INSTALL_INTERNAL_SKILLS` | Set to `1` or `true` to show and install skills marked as `internal: true`. |
|
|
187
|
+
| `SKILLS_CLONE_TIMEOUT_MS` | Timeout for a `git clone`, in milliseconds. |
|
|
188
|
+
| `BUZZ_NEST_DIR` | Where `buzz setup` provisions its nest (default `~/.buzz`). |
|
|
189
|
+
| `BUZZ_APP_DATA_DIR` | Override the Buzz application-data directory. |
|
|
190
|
+
| `BUZZ_PACKS_DIR` | Where persona packs are read from (default: the `packs/` directory inside this package). |
|
|
191
|
+
|
|
192
|
+
Read from the surrounding environment, not owned by this CLI:
|
|
193
|
+
|
|
194
|
+
| Variable | Description |
|
|
195
|
+
| -------- | ----------- |
|
|
196
|
+
| `CLAUDE_CONFIG_DIR` | Claude Code's config directory (default `~/.claude`); determines where global skills, subagents and rules are installed. |
|
|
197
|
+
| `XDG_STATE_HOME` | When set, relocates the global lock to `$XDG_STATE_HOME/skills/.skill-lock.json` instead of `~/.agents/`. |
|
|
198
|
+
| `GITHUB_TOKEN` / `GH_TOKEN` | Used by `git`/`gh` when cloning a private catalog or skill repo. |
|
|
199
|
+
| `NO_COLOR` / `CI` | Suppress the animated logo. |
|
|
200
|
+
|
|
201
|
+
`CODEX_HOME`, `VIBE_HOME`, `HERMES_HOME`, `AUTOHAND_HOME`, `APPDATA`,
|
|
202
|
+
`FLATPAK_XDG_CONFIG_HOME` and `XDG_DATA_HOME` are read the same way, to locate the skills
|
|
203
|
+
directory of the corresponding agent.
|
|
204
|
+
|
|
205
|
+
## Stability
|
|
206
|
+
|
|
207
|
+
pruff-skills follows [semantic versioning](https://semver.org/). From 1.0.0 onward, a
|
|
208
|
+
breaking change to anything in **Covered** below means a major version bump.
|
|
209
|
+
|
|
210
|
+
**Covered:**
|
|
211
|
+
|
|
212
|
+
- Command names and their documented aliases, and the flags listed in `--help`
|
|
213
|
+
- Exit codes: `0` on success, non-zero on failure
|
|
214
|
+
- The `PRUFF_SKILLS_*` and `INSTALL_INTERNAL_SKILLS` environment variables
|
|
215
|
+
- The `skills-lock.json` format, including its `version` field
|
|
216
|
+
- The `schemaVersion` of `list --json`
|
|
217
|
+
- The `schemaVersion` of `.well-known/agent-skills/index.json`, the catalog discovery manifest
|
|
218
|
+
- The required `SKILL.md` frontmatter fields: `name` and `description`
|
|
219
|
+
|
|
220
|
+
**Not covered** — these may change in any release:
|
|
221
|
+
|
|
222
|
+
- Human-readable output: log lines, colours, spinners, table layout, the banner. Parse
|
|
223
|
+
`list --json`, not stdout.
|
|
224
|
+
- The mapping from an agent to its on-disk skills directory. These track each third-party
|
|
225
|
+
tool's own layout, so they change when that tool changes, not on our schedule.
|
|
226
|
+
- The `buzz` command and its `BUZZ_*` variables — Pruff-internal team tooling.
|
|
227
|
+
- The contents of the catalog. Skills, subagents and rules are added, reworded, and
|
|
228
|
+
occasionally renamed or removed; the CLI's contract is how you install them, not which
|
|
229
|
+
ones exist. Pin a skill by committing `skills-lock.json`.
|
|
230
|
+
- Anything named `experimental_*`. `experimental_install` and `experimental_sync` are
|
|
231
|
+
retained as undocumented aliases of `restore` and `sync` and may be dropped.
|
|
232
|
+
|
|
233
|
+
**On version fields.** Both lock files and both JSON schemas carry an explicit version, and
|
|
234
|
+
a version this CLI cannot read is an error with instructions — never a silent reset. If you
|
|
235
|
+
see one, the message names the file, both versions, and what to run.
|
|
181
236
|
|
|
182
237
|
## Telemetry
|
|
183
238
|
|
package/dist/cli.mjs
CHANGED
|
@@ -494,13 +494,12 @@ function isAuthFailure(message) {
|
|
|
494
494
|
function createGitClient(extraEnv) {
|
|
495
495
|
return esm_default({
|
|
496
496
|
timeout: { block: CLONE_TIMEOUT_MS },
|
|
497
|
-
env: {
|
|
498
|
-
...process.env,
|
|
499
|
-
GIT_TERMINAL_PROMPT: "0",
|
|
500
|
-
GIT_LFS_SKIP_SMUDGE: "1",
|
|
501
|
-
...extraEnv
|
|
502
|
-
},
|
|
503
497
|
config: ["filter.lfs.required=false"]
|
|
498
|
+
}).env({
|
|
499
|
+
...process.env,
|
|
500
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
501
|
+
GIT_LFS_SKIP_SMUDGE: "1",
|
|
502
|
+
...extraEnv
|
|
504
503
|
});
|
|
505
504
|
}
|
|
506
505
|
async function resetTempDir(dir) {
|
|
@@ -682,6 +681,8 @@ async function getPluginGroupings(basePath) {
|
|
|
682
681
|
} catch {}
|
|
683
682
|
return groupings;
|
|
684
683
|
}
|
|
684
|
+
var UserFacingError = class extends Error {};
|
|
685
|
+
var LockVersionError = class extends UserFacingError {};
|
|
685
686
|
const LOCAL_LOCK_FILE = "skills-lock.json";
|
|
686
687
|
const CURRENT_VERSION$1 = 1;
|
|
687
688
|
function getLocalLockPath(cwd) {
|
|
@@ -689,15 +690,23 @@ function getLocalLockPath(cwd) {
|
|
|
689
690
|
}
|
|
690
691
|
async function readLocalLock(cwd) {
|
|
691
692
|
const lockPath = getLocalLockPath(cwd);
|
|
693
|
+
let content;
|
|
692
694
|
try {
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
if (
|
|
696
|
-
|
|
697
|
-
return parsed;
|
|
698
|
-
} catch {
|
|
699
|
-
return createEmptyLocalLock();
|
|
695
|
+
content = await readFile(lockPath, "utf-8");
|
|
696
|
+
} catch (error) {
|
|
697
|
+
if (error.code === "ENOENT") return createEmptyLocalLock();
|
|
698
|
+
throw new LockVersionError(`Could not read ${lockPath}: ${error.message}`);
|
|
700
699
|
}
|
|
700
|
+
let parsed;
|
|
701
|
+
try {
|
|
702
|
+
parsed = JSON.parse(content);
|
|
703
|
+
} catch (error) {
|
|
704
|
+
throw new LockVersionError(/^<{7} |^={7}$|^>{7} /m.test(content) ? `${lockPath} still has unresolved merge-conflict markers.\n Resolve them (skill entries are independent keys, so both sides can usually be kept) and re-run.` : `${lockPath} is not valid JSON: ${error.message}`);
|
|
705
|
+
}
|
|
706
|
+
if (typeof parsed.version !== "number" || !parsed.skills) throw new LockVersionError(`${lockPath} is missing a numeric "version" or a "skills" object.\n If it was hand-edited, restore it from git; otherwise delete it and re-add your skills.`);
|
|
707
|
+
if (parsed.version > CURRENT_VERSION$1) throw new LockVersionError(`${lockPath} is at lock schema v${parsed.version}, but this pruff-skills reads up to v${CURRENT_VERSION$1}.\n A newer pruff-skills wrote it. Upgrade with: npm i -g pruff-skills@latest`);
|
|
708
|
+
if (parsed.version < CURRENT_VERSION$1) throw new LockVersionError(`${lockPath} is at lock schema v${parsed.version}, which predates every released format (v1 is the oldest).\n Restore it from git, or delete it and re-add your skills.`);
|
|
709
|
+
return parsed;
|
|
701
710
|
}
|
|
702
711
|
async function writeLocalLock(lock, cwd) {
|
|
703
712
|
const lockPath = getLocalLockPath(cwd);
|
|
@@ -803,13 +812,14 @@ async function parseSkillMd(skillMdPath, options) {
|
|
|
803
812
|
const { data } = parseFrontmatter(content);
|
|
804
813
|
if (!data.name || !data.description) return null;
|
|
805
814
|
if (typeof data.name !== "string" || typeof data.description !== "string") return null;
|
|
806
|
-
|
|
815
|
+
const metadata = typeof data.metadata === "object" && data.metadata !== null && !Array.isArray(data.metadata) ? data.metadata : void 0;
|
|
816
|
+
if (metadata?.internal === true && !shouldInstallInternalSkills() && !options?.includeInternal) return null;
|
|
807
817
|
return {
|
|
808
818
|
name: sanitizeMetadata(data.name),
|
|
809
819
|
description: sanitizeMetadata(data.description),
|
|
810
820
|
path: dirname(skillMdPath),
|
|
811
821
|
rawContent: content,
|
|
812
|
-
metadata
|
|
822
|
+
metadata
|
|
813
823
|
};
|
|
814
824
|
} catch {
|
|
815
825
|
return null;
|
|
@@ -2125,6 +2135,18 @@ const PRUFF_CATALOG = process.env.PRUFF_SKILLS_CATALOG || "Pruff-Tech/pruff-skil
|
|
|
2125
2135
|
function isBareSkillName(value) {
|
|
2126
2136
|
return !!value && /^[a-z0-9][a-z0-9-]*$/.test(value) && !value.includes("/") && !value.includes(".") && !value.includes(":");
|
|
2127
2137
|
}
|
|
2138
|
+
var CatalogSchemaError = class extends UserFacingError {};
|
|
2139
|
+
function parseCatalogIndex(raw, sourceLabel) {
|
|
2140
|
+
let parsed;
|
|
2141
|
+
try {
|
|
2142
|
+
parsed = JSON.parse(raw);
|
|
2143
|
+
} catch (error) {
|
|
2144
|
+
throw new CatalogSchemaError(`Could not parse the catalog index from ${sourceLabel}: ${error.message}`);
|
|
2145
|
+
}
|
|
2146
|
+
const declared = parsed?.schemaVersion;
|
|
2147
|
+
if (typeof declared === "number" && declared > 1) throw new CatalogSchemaError(`The catalog at ${sourceLabel} declares index schema v${declared}, but this pruff-skills understands up to v1.\n Upgrade with: npm i -g pruff-skills@latest`);
|
|
2148
|
+
return parsed ?? {};
|
|
2149
|
+
}
|
|
2128
2150
|
var import_dist = require_dist();
|
|
2129
2151
|
let cachedResult = null;
|
|
2130
2152
|
const agentNameToType = {
|
|
@@ -2628,15 +2650,23 @@ function getSkillLockPath() {
|
|
|
2628
2650
|
}
|
|
2629
2651
|
async function readSkillLock() {
|
|
2630
2652
|
const lockPath = getSkillLockPath();
|
|
2653
|
+
let content;
|
|
2654
|
+
try {
|
|
2655
|
+
content = await readFile(lockPath, "utf-8");
|
|
2656
|
+
} catch (error) {
|
|
2657
|
+
if (error.code === "ENOENT") return createEmptyLockFile();
|
|
2658
|
+
throw new LockVersionError(`Could not read ${lockPath}: ${error.message}`);
|
|
2659
|
+
}
|
|
2660
|
+
let parsed;
|
|
2631
2661
|
try {
|
|
2632
|
-
|
|
2633
|
-
const parsed = JSON.parse(content);
|
|
2634
|
-
if (typeof parsed.version !== "number" || !parsed.skills) return createEmptyLockFile();
|
|
2635
|
-
if (parsed.version < CURRENT_VERSION) return createEmptyLockFile();
|
|
2636
|
-
return parsed;
|
|
2662
|
+
parsed = JSON.parse(content);
|
|
2637
2663
|
} catch (error) {
|
|
2638
|
-
|
|
2664
|
+
throw new LockVersionError(`${lockPath} is not valid JSON: ${error.message}\n This file only records what is already installed. Deleting it is safe — you will just lose update tracking until each skill is re-added.`);
|
|
2639
2665
|
}
|
|
2666
|
+
if (typeof parsed.version !== "number" || !parsed.skills) throw new LockVersionError(`${lockPath} is missing a numeric "version" or a "skills" object.\n Deleting it is safe — you will just lose update tracking until each skill is re-added.`);
|
|
2667
|
+
if (parsed.version > CURRENT_VERSION) throw new LockVersionError(`${lockPath} is at lock schema v${parsed.version}, but this pruff-skills reads up to v${CURRENT_VERSION}.\n A newer pruff-skills wrote it. Upgrade with: npm i -g pruff-skills@latest`);
|
|
2668
|
+
if (parsed.version < CURRENT_VERSION) throw new LockVersionError(`${lockPath} is at lock schema v${parsed.version}; this pruff-skills needs v${CURRENT_VERSION}, and the missing folder hashes cannot be reconstructed offline.\n Delete it and re-add your skills: rm ${lockPath}\n Nothing installed is removed by that — only the update-tracking record.`);
|
|
2669
|
+
return parsed;
|
|
2640
2670
|
}
|
|
2641
2671
|
async function writeSkillLock(lock) {
|
|
2642
2672
|
const lockPath = getSkillLockPath();
|
|
@@ -4216,11 +4246,7 @@ const fetchRuleFile = (source, name) => fetchFromCatalog(source, `rules/${name}.
|
|
|
4216
4246
|
async function fetchRuleIndex(source) {
|
|
4217
4247
|
const raw = await fetchFromCatalog(source, ".well-known/agent-skills/index.json");
|
|
4218
4248
|
if (!raw) return [];
|
|
4219
|
-
|
|
4220
|
-
return JSON.parse(raw).rules ?? [];
|
|
4221
|
-
} catch {
|
|
4222
|
-
return [];
|
|
4223
|
-
}
|
|
4249
|
+
return parseCatalogIndex(raw, source).rules ?? [];
|
|
4224
4250
|
}
|
|
4225
4251
|
function expandScopes(scopes) {
|
|
4226
4252
|
const set = new Set(scopes);
|
|
@@ -4335,10 +4361,7 @@ const UNCHECKED = "◻";
|
|
|
4335
4361
|
const CATALOG_SOURCE = process.env.PRUFF_SKILLS_CATALOG || "Pruff-Tech/pruff-skills";
|
|
4336
4362
|
async function fetchCatalogIndex(source) {
|
|
4337
4363
|
const rel = ".well-known/agent-skills/index.json";
|
|
4338
|
-
if (source.startsWith(".") || isAbsolute(source))
|
|
4339
|
-
const raw = readFileSync(join(source, rel), "utf8");
|
|
4340
|
-
return JSON.parse(raw);
|
|
4341
|
-
}
|
|
4364
|
+
if (source.startsWith(".") || isAbsolute(source)) return parseCatalogIndex(readFileSync(join(source, rel), "utf8"), source);
|
|
4342
4365
|
if (source.match(/^[^/]+\/[^/]+$/)) {
|
|
4343
4366
|
const { stdout } = await execFileAsync$2("gh", [
|
|
4344
4367
|
"api",
|
|
@@ -4349,8 +4372,7 @@ async function fetchCatalogIndex(source) {
|
|
|
4349
4372
|
...process.env,
|
|
4350
4373
|
GIT_TERMINAL_PROMPT: "0"
|
|
4351
4374
|
} });
|
|
4352
|
-
|
|
4353
|
-
return JSON.parse(json);
|
|
4375
|
+
return parseCatalogIndex(Buffer.from(stdout.trim(), "base64").toString("utf8"), source);
|
|
4354
4376
|
}
|
|
4355
4377
|
return {};
|
|
4356
4378
|
}
|
|
@@ -4400,7 +4422,8 @@ async function loadCatalog() {
|
|
|
4400
4422
|
...(index.agents ?? []).map((e) => toEntry(e, "agent")),
|
|
4401
4423
|
...(index.rules ?? []).map((e) => toEntry(e, "rule"))
|
|
4402
4424
|
];
|
|
4403
|
-
} catch {
|
|
4425
|
+
} catch (error) {
|
|
4426
|
+
if (error instanceof CatalogSchemaError) throw error;
|
|
4404
4427
|
catalogCache = [];
|
|
4405
4428
|
}
|
|
4406
4429
|
return catalogCache;
|
|
@@ -5026,6 +5049,7 @@ const BOLD$2 = "\x1B[1m";
|
|
|
5026
5049
|
const DIM$2 = "\x1B[38;5;102m";
|
|
5027
5050
|
const CYAN = "\x1B[36m";
|
|
5028
5051
|
const YELLOW = "\x1B[33m";
|
|
5052
|
+
const LIST_JSON_SCHEMA_VERSION = 1;
|
|
5029
5053
|
function shortenPath(fullPath, cwd) {
|
|
5030
5054
|
const home = homedir();
|
|
5031
5055
|
if (fullPath.startsWith(home)) return fullPath.replace(home, "~");
|
|
@@ -5070,12 +5094,15 @@ async function runList(args) {
|
|
|
5070
5094
|
agentFilter
|
|
5071
5095
|
});
|
|
5072
5096
|
if (options.json) {
|
|
5073
|
-
const jsonOutput =
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5097
|
+
const jsonOutput = {
|
|
5098
|
+
schemaVersion: LIST_JSON_SCHEMA_VERSION,
|
|
5099
|
+
skills: installedSkills.map((skill) => ({
|
|
5100
|
+
name: skill.name,
|
|
5101
|
+
path: skill.canonicalPath,
|
|
5102
|
+
scope: skill.scope,
|
|
5103
|
+
agents: skill.agents
|
|
5104
|
+
}))
|
|
5105
|
+
};
|
|
5079
5106
|
console.log(JSON.stringify(jsonOutput, null, 2));
|
|
5080
5107
|
return;
|
|
5081
5108
|
}
|
|
@@ -5083,10 +5110,6 @@ async function runList(args) {
|
|
|
5083
5110
|
const cwd = process.cwd();
|
|
5084
5111
|
const scopeLabel = scope ? "Global" : "Project";
|
|
5085
5112
|
if (installedSkills.length === 0) {
|
|
5086
|
-
if (options.json) {
|
|
5087
|
-
console.log("[]");
|
|
5088
|
-
return;
|
|
5089
|
-
}
|
|
5090
5113
|
console.log(`${DIM$2}No ${scopeLabel.toLowerCase()} skills found.${RESET$2}`);
|
|
5091
5114
|
if (scope) console.log(`${DIM$2}Try listing project skills without -g${RESET$2}`);
|
|
5092
5115
|
else console.log(`${DIM$2}Try listing global skills with -g${RESET$2}`);
|
|
@@ -5190,6 +5213,7 @@ async function removeCommand(skillNames, options) {
|
|
|
5190
5213
|
Se(import_picocolors.default.yellow("No skills found to remove."));
|
|
5191
5214
|
return;
|
|
5192
5215
|
}
|
|
5216
|
+
if (options.agent?.includes("*")) options.agent = Object.keys(agents);
|
|
5193
5217
|
if (options.agent && options.agent.length > 0) {
|
|
5194
5218
|
const validAgents = Object.keys(agents);
|
|
5195
5219
|
const invalidAgents = options.agent.filter((a) => !validAgents.includes(a));
|
|
@@ -5200,7 +5224,7 @@ async function removeCommand(skillNames, options) {
|
|
|
5200
5224
|
}
|
|
5201
5225
|
}
|
|
5202
5226
|
let selectedSkills = [];
|
|
5203
|
-
if (options.all) selectedSkills = installedSkills;
|
|
5227
|
+
if (options.all || skillNames.includes("*")) selectedSkills = installedSkills;
|
|
5204
5228
|
else if (skillNames.length > 0) {
|
|
5205
5229
|
selectedSkills = installedSkills.filter((s) => skillNames.some((name) => name.toLowerCase() === s.toLowerCase()));
|
|
5206
5230
|
if (selectedSkills.length === 0) {
|
|
@@ -5339,6 +5363,15 @@ function parseRemoveOptions(args) {
|
|
|
5339
5363
|
nextArg = args[i];
|
|
5340
5364
|
}
|
|
5341
5365
|
i--;
|
|
5366
|
+
} else if (arg === "-s" || arg === "--skill") {
|
|
5367
|
+
i++;
|
|
5368
|
+
let nextArg = args[i];
|
|
5369
|
+
while (i < args.length && nextArg && !nextArg.startsWith("-")) {
|
|
5370
|
+
skills.push(nextArg);
|
|
5371
|
+
i++;
|
|
5372
|
+
nextArg = args[i];
|
|
5373
|
+
}
|
|
5374
|
+
i--;
|
|
5342
5375
|
} else if (arg && !arg.startsWith("-")) skills.push(arg);
|
|
5343
5376
|
}
|
|
5344
5377
|
return {
|
|
@@ -8846,9 +8879,9 @@ async function showBanner() {
|
|
|
8846
8879
|
console.log();
|
|
8847
8880
|
console.log(` ${DIM}$${RESET} ${TEXT}npx pruff-skills update${RESET} ${DIM}Update installed skills + subagents${RESET}`);
|
|
8848
8881
|
console.log();
|
|
8849
|
-
console.log(` ${DIM}$${RESET} ${TEXT}npx pruff-skills
|
|
8882
|
+
console.log(` ${DIM}$${RESET} ${TEXT}npx pruff-skills restore${RESET} ${DIM}Restore from skills-lock.json${RESET}`);
|
|
8850
8883
|
console.log(` ${DIM}$${RESET} ${TEXT}npx pruff-skills init ${DIM}[name]${RESET} ${DIM}Create a new skill${RESET}`);
|
|
8851
|
-
console.log(` ${DIM}$${RESET} ${TEXT}npx pruff-skills
|
|
8884
|
+
console.log(` ${DIM}$${RESET} ${TEXT}npx pruff-skills sync${RESET} ${DIM}Sync skills from node_modules${RESET}`);
|
|
8852
8885
|
console.log();
|
|
8853
8886
|
console.log(`${DIM}try:${RESET} ${BLUE}npx pruff-skills add <skill>${RESET}`);
|
|
8854
8887
|
console.log();
|
|
@@ -8883,6 +8916,13 @@ ${BOLD}Usage analytics:${RESET}
|
|
|
8883
8916
|
${DIM}Reports token/cost by skill, MCP tool, subagent and model.${RESET}
|
|
8884
8917
|
${DIM}Never exports prompts, responses or file contents.${RESET}
|
|
8885
8918
|
|
|
8919
|
+
${BOLD}Buzz nest:${RESET}
|
|
8920
|
+
buzz setup <team> Provision a Buzz agent nest (~/.buzz): vault, team skills,
|
|
8921
|
+
subagents and persona packs
|
|
8922
|
+
buzz sync [team] Re-sync an existing nest with the catalog
|
|
8923
|
+
buzz packs list List the persona packs shipped with this CLI
|
|
8924
|
+
buzz packs init <t> Scaffold a new persona pack for a team
|
|
8925
|
+
|
|
8886
8926
|
${BOLD}Onboarding:${RESET}
|
|
8887
8927
|
onboarding [dir] Set up a new machine (macOS), end to end: prerequisites +
|
|
8888
8928
|
GitHub auth, Claude Code, Obsidian, Bun, clone the
|
|
@@ -8908,9 +8948,9 @@ ${BOLD}Update Options:${RESET}
|
|
|
8908
8948
|
-y, --yes Skip scope prompt (auto-detect: project if in a project, else global)
|
|
8909
8949
|
|
|
8910
8950
|
${BOLD}Project:${RESET}
|
|
8911
|
-
|
|
8951
|
+
restore Restore skills from skills-lock.json
|
|
8912
8952
|
init [name] Initialize a skill (creates <name>/SKILL.md or ./SKILL.md)
|
|
8913
|
-
|
|
8953
|
+
sync Sync skills from node_modules into agent directories
|
|
8914
8954
|
|
|
8915
8955
|
${BOLD}Add Options:${RESET}
|
|
8916
8956
|
-g, --global Install skill globally (user-level) instead of project-level
|
|
@@ -8936,9 +8976,10 @@ ${BOLD}Remove Options:${RESET}
|
|
|
8936
8976
|
-y, --yes Skip confirmation prompts
|
|
8937
8977
|
--all Shorthand for --skill '*' --agent '*' -y
|
|
8938
8978
|
|
|
8939
|
-
${BOLD}
|
|
8979
|
+
${BOLD}Sync Options:${RESET}
|
|
8940
8980
|
-a, --agent <agents> Specify agents to install to (use '*' for all agents)
|
|
8941
8981
|
-y, --yes Skip confirmation prompts
|
|
8982
|
+
-f, --force Re-copy skills already present in the agent directory
|
|
8942
8983
|
|
|
8943
8984
|
${BOLD}List Options:${RESET}
|
|
8944
8985
|
-g, --global List global skills (default: project)
|
|
@@ -8968,10 +9009,10 @@ ${BOLD}Examples:${RESET}
|
|
|
8968
9009
|
${DIM}$${RESET} pruff-skills update
|
|
8969
9010
|
${DIM}$${RESET} pruff-skills update <skill> ${DIM}# update a single skill${RESET}
|
|
8970
9011
|
${DIM}$${RESET} pruff-skills update -g ${DIM}# update global skills only${RESET}
|
|
8971
|
-
${DIM}$${RESET} pruff-skills
|
|
9012
|
+
${DIM}$${RESET} pruff-skills restore ${DIM}# restore from skills-lock.json${RESET}
|
|
8972
9013
|
${DIM}$${RESET} pruff-skills init my-skill
|
|
8973
|
-
${DIM}$${RESET} pruff-skills
|
|
8974
|
-
${DIM}$${RESET} pruff-skills
|
|
9014
|
+
${DIM}$${RESET} pruff-skills sync ${DIM}# sync from node_modules${RESET}
|
|
9015
|
+
${DIM}$${RESET} pruff-skills sync -y ${DIM}# sync without prompts${RESET}
|
|
8975
9016
|
|
|
8976
9017
|
Browse the catalog with ${BLUE}npx pruff-skills find${RESET}
|
|
8977
9018
|
`);
|
|
@@ -9058,7 +9099,7 @@ async function main() {
|
|
|
9058
9099
|
const inAgent = await isRunningInAgent();
|
|
9059
9100
|
if (args.length === 0) {
|
|
9060
9101
|
if (!inAgent) await showBanner();
|
|
9061
|
-
return;
|
|
9102
|
+
return 0;
|
|
9062
9103
|
}
|
|
9063
9104
|
const command = args[0];
|
|
9064
9105
|
const restArgs = args.slice(1);
|
|
@@ -9076,6 +9117,7 @@ async function main() {
|
|
|
9076
9117
|
console.log();
|
|
9077
9118
|
runInit(restArgs);
|
|
9078
9119
|
break;
|
|
9120
|
+
case "restore":
|
|
9079
9121
|
case "experimental_install":
|
|
9080
9122
|
if (!inAgent) await showLogo();
|
|
9081
9123
|
await runInstallFromLock(restArgs);
|
|
@@ -9130,6 +9172,7 @@ async function main() {
|
|
|
9130
9172
|
const { skills, options: removeOptions } = parseRemoveOptions(restArgs);
|
|
9131
9173
|
await removeCommand(skills, removeOptions);
|
|
9132
9174
|
break;
|
|
9175
|
+
case "sync":
|
|
9133
9176
|
case "experimental_sync": {
|
|
9134
9177
|
if (!inAgent) await showLogo();
|
|
9135
9178
|
const { options: syncOptions } = parseSyncOptions(restArgs);
|
|
@@ -9154,9 +9197,18 @@ async function main() {
|
|
|
9154
9197
|
console.log(VERSION);
|
|
9155
9198
|
break;
|
|
9156
9199
|
default:
|
|
9157
|
-
console.
|
|
9158
|
-
console.
|
|
9159
|
-
|
|
9160
|
-
}
|
|
9161
|
-
|
|
9200
|
+
console.error(`Unknown command: ${command}`);
|
|
9201
|
+
console.error(`Run ${BOLD}pruff-skills --help${RESET} for usage.`);
|
|
9202
|
+
return 1;
|
|
9203
|
+
}
|
|
9204
|
+
return 0;
|
|
9205
|
+
}
|
|
9206
|
+
main().then((code) => code ?? 0).catch((error) => {
|
|
9207
|
+
if (error instanceof UserFacingError) console.error(error.message);
|
|
9208
|
+
else console.error(error instanceof Error ? error.stack ?? error.message : String(error));
|
|
9209
|
+
return 1;
|
|
9210
|
+
}).then(async (code) => {
|
|
9211
|
+
await flushTelemetry();
|
|
9212
|
+
process.exit(code);
|
|
9213
|
+
});
|
|
9162
9214
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pruff-skills",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "The official Pruff agent skills CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
"sync-catalog:check": "node scripts/sync-catalog.mjs --check",
|
|
23
23
|
"exec:test": "node scripts/execute-tests.ts",
|
|
24
24
|
"prepublishOnly": "npm run build",
|
|
25
|
-
"format": "prettier --write \"src/**/*.ts\" \"scripts/**/*.ts\"",
|
|
26
|
-
"format:check": "prettier --check \"src/**/*.ts\" \"scripts/**/*.ts\"",
|
|
25
|
+
"format": "prettier --write \"src/**/*.ts\" \"scripts/**/*.ts\" \"tests/**/*.ts\"",
|
|
26
|
+
"format:check": "prettier --check \"src/**/*.ts\" \"scripts/**/*.ts\" \"tests/**/*.ts\"",
|
|
27
27
|
"prepare": "husky || true",
|
|
28
28
|
"test": "vitest",
|
|
29
29
|
"type-check": "tsc --noEmit",
|