pruff-skills 1.8.0 → 1.9.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 +3 -0
- package/dist/_chunks/libs/simple-git.mjs +1 -1
- package/dist/cli.mjs +550 -77
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -147,6 +147,9 @@ npx pruff-skills add <skill> -g -a claude-code -y
|
|
|
147
147
|
| `npx pruff-skills restore` | Reinstall everything recorded in `skills-lock.json` |
|
|
148
148
|
| `npx pruff-skills sync` | Install skills found in `node_modules` into agent dirs |
|
|
149
149
|
| `npx pruff-skills onboarding --db-access` | Set up personal read-only database access (team members) |
|
|
150
|
+
| `npx pruff-skills doctor` | Print a diagnostic block to paste when something fails |
|
|
151
|
+
|
|
152
|
+
Troubleshooting: if a command fails, run `npx pruff-skills doctor` and paste the block it prints when you ask for help.
|
|
150
153
|
|
|
151
154
|
```bash
|
|
152
155
|
npx pruff-skills list # list installed skills
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { r as __toESM } from "../rolldown-runtime.mjs";
|
|
2
2
|
import { n as require_src, t as require_dist } from "./@kwsites/file-exists.mjs";
|
|
3
3
|
import { t as require_dist$1 } from "./@kwsites/promise-deferred.mjs";
|
|
4
|
-
import { Buffer as Buffer$1 } from "node:buffer";
|
|
5
4
|
import { spawn } from "child_process";
|
|
5
|
+
import { Buffer as Buffer$1 } from "node:buffer";
|
|
6
6
|
import { normalize } from "node:path";
|
|
7
7
|
import { EventEmitter } from "node:events";
|
|
8
8
|
var import_dist = require_dist();
|
package/dist/cli.mjs
CHANGED
|
@@ -7,20 +7,378 @@ import "./_chunks/libs/@kwsites/promise-deferred.mjs";
|
|
|
7
7
|
import { t as esm_default } from "./_chunks/libs/simple-git.mjs";
|
|
8
8
|
import { t as xdgConfig } from "./_chunks/libs/xdg-basedir.mjs";
|
|
9
9
|
import { t as require_dist } from "./_chunks/libs/@vercel/detect-agent.mjs";
|
|
10
|
-
import { chmodSync, copyFileSync, cpSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from "fs";
|
|
10
|
+
import { accessSync, chmodSync, constants, copyFileSync, cpSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from "fs";
|
|
11
11
|
import { basename, delimiter, dirname, isAbsolute, join, normalize, relative, resolve, sep } from "path";
|
|
12
12
|
import { fileURLToPath } from "url";
|
|
13
13
|
import { stripVTControlCharacters } from "node:util";
|
|
14
|
+
import { execFile, execSync, spawn, spawnSync } from "child_process";
|
|
14
15
|
import { homedir, platform, tmpdir } from "os";
|
|
15
16
|
import * as readline from "readline";
|
|
16
17
|
import { Writable } from "stream";
|
|
17
18
|
import { promisify } from "util";
|
|
18
|
-
import { execFile, execSync, spawn, spawnSync } from "child_process";
|
|
19
19
|
import { access, cp, lstat, mkdir, mkdtemp, readFile, readdir, readlink, realpath, rm, stat, symlink, writeFile } from "fs/promises";
|
|
20
20
|
import { parse } from "yaml";
|
|
21
21
|
import { createHash, randomBytes } from "crypto";
|
|
22
22
|
import { createHash as createHash$1 } from "node:crypto";
|
|
23
23
|
import { gunzipSync, inflateRawSync } from "node:zlib";
|
|
24
|
+
var UserFacingError = class extends Error {};
|
|
25
|
+
var LockVersionError = class extends UserFacingError {};
|
|
26
|
+
const DIAGNOSTICS_HEADER = "Diagnóstico — copiá y pegá esto en #dev-tools o a Ramiro";
|
|
27
|
+
const DIAGNOSTICS_FOOTER = "Si esto falla otra vez: pruff-skills doctor";
|
|
28
|
+
const DEFAULT_CATALOG = "Pruff-Tech/pruff-skills";
|
|
29
|
+
const PROBE_TIMEOUT_MS = 2500;
|
|
30
|
+
const probeCache = /* @__PURE__ */ new Map();
|
|
31
|
+
let currentStep = "start";
|
|
32
|
+
let failure = null;
|
|
33
|
+
const tail = [];
|
|
34
|
+
function setStep(step) {
|
|
35
|
+
currentStep = step;
|
|
36
|
+
}
|
|
37
|
+
function recordFailure(message) {
|
|
38
|
+
failure ??= {
|
|
39
|
+
step: currentStep,
|
|
40
|
+
message
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function resetDiagnosticsState() {
|
|
44
|
+
currentStep = "start";
|
|
45
|
+
failure = null;
|
|
46
|
+
tail.length = 0;
|
|
47
|
+
}
|
|
48
|
+
function recordedFailure() {
|
|
49
|
+
return failure;
|
|
50
|
+
}
|
|
51
|
+
const ANSI = /\x1b\[[0-9;?]*[A-Za-z]/g;
|
|
52
|
+
function redact(text, home = homedir()) {
|
|
53
|
+
let out = text.replace(ANSI, "");
|
|
54
|
+
if (home && home !== "/") out = out.split(home).join("~");
|
|
55
|
+
return out.replace(/(https?:\/\/)[^\s/@]+@/gi, "$1[redacted]@").replace(/\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{10,}/g, "[redacted]").replace(/\bgithub_pat_[A-Za-z0-9_]{10,}/g, "[redacted]").replace(/\bAKIA[A-Z0-9]{12,}/g, "[redacted]").replace(/\bBearer\s+[A-Za-z0-9._~+/=-]+/gi, "Bearer [redacted]").replace(/([?&]token=)[^&\s]+/gi, "$1[redacted]").replace(/(x-api-key["']?\s*[:=]?\s*)[^\s"',;]+/gi, "$1[redacted]").replace(/[A-Za-z0-9._-]+@[a-z0-9.-]+\.[a-z]{2,}/g, "[email]");
|
|
56
|
+
}
|
|
57
|
+
function catalogKind(source) {
|
|
58
|
+
if (/^[^/\s]+\/[^/\s]+$/.test(source) && !source.startsWith(".")) return "owner/repo";
|
|
59
|
+
if (source.startsWith(".") || isAbsolute(source) || source.startsWith("~")) return "local path";
|
|
60
|
+
try {
|
|
61
|
+
return `git URL (${new URL(source).host})`;
|
|
62
|
+
} catch {
|
|
63
|
+
const scp = source.match(/^[^@\s]+@([^:\s]+):/);
|
|
64
|
+
return scp ? `git URL (${scp[1]})` : "git URL";
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const yn = (v) => v ? "yes" : "no";
|
|
68
|
+
function probe(cmd, args) {
|
|
69
|
+
const key = [cmd, ...args].join(" ");
|
|
70
|
+
const cached = probeCache.get(key);
|
|
71
|
+
if (cached) return cached;
|
|
72
|
+
const r = spawnSync(cmd, args, {
|
|
73
|
+
encoding: "utf-8",
|
|
74
|
+
timeout: PROBE_TIMEOUT_MS,
|
|
75
|
+
stdio: "pipe",
|
|
76
|
+
env: {
|
|
77
|
+
...process.env,
|
|
78
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
79
|
+
COREPACK_ENABLE_DOWNLOAD_PROMPT: "0"
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
const result = {
|
|
83
|
+
ok: r.status === 0,
|
|
84
|
+
out: `${r.stdout ?? ""}\n${r.stderr ?? ""}`.trim()
|
|
85
|
+
};
|
|
86
|
+
probeCache.set(key, result);
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
const onPath = (cmd) => {
|
|
90
|
+
return yn((process.env.PATH ?? "").split(delimiter).filter(Boolean).some((dir) => {
|
|
91
|
+
try {
|
|
92
|
+
accessSync(join(dir, cmd), constants.X_OK);
|
|
93
|
+
return true;
|
|
94
|
+
} catch {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
}));
|
|
98
|
+
};
|
|
99
|
+
function version$2() {
|
|
100
|
+
try {
|
|
101
|
+
const dir = dirname(fileURLToPath(import.meta.url));
|
|
102
|
+
return JSON.parse(readFileSync(join(dir, "..", "package.json"), "utf-8")).version;
|
|
103
|
+
} catch {
|
|
104
|
+
return "unknown";
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function timestamp(d = /* @__PURE__ */ new Date()) {
|
|
108
|
+
const pad = (n) => String(Math.trunc(Math.abs(n))).padStart(2, "0");
|
|
109
|
+
const off = -d.getTimezoneOffset();
|
|
110
|
+
const sign = off >= 0 ? "+" : "-";
|
|
111
|
+
return `${(/* @__PURE__ */ new Date(d.getTime() - d.getTimezoneOffset() * 6e4)).toISOString().slice(0, 19)}${sign}${pad(off / 60)}:${pad(off % 60)}`;
|
|
112
|
+
}
|
|
113
|
+
function packageManager() {
|
|
114
|
+
const pnpm = probe("pnpm", ["-v"]);
|
|
115
|
+
const npm = probe("npm", ["-v"]);
|
|
116
|
+
const v = (r) => r.ok && r.out.match(/\d+\.\d+\.\d+\S*/g)?.pop() || "n/a";
|
|
117
|
+
return `pnpm ${v(pnpm)}, npm ${v(npm)}`;
|
|
118
|
+
}
|
|
119
|
+
function ghStatus() {
|
|
120
|
+
if (onPath("gh") === "no") return {
|
|
121
|
+
summary: "n/a (gh not installed)",
|
|
122
|
+
loggedIn: false
|
|
123
|
+
};
|
|
124
|
+
const r = probe("gh", ["auth", "status"]);
|
|
125
|
+
const text = r.out;
|
|
126
|
+
const loggedIn = r.ok || /Logged in to/i.test(text);
|
|
127
|
+
const host = text.match(/Logged in to (\S+)/i)?.[1] ?? text.match(/^(\S+\.\S+)\s*$/m)?.[1];
|
|
128
|
+
const account = text.match(/account (\S+)/i)?.[1];
|
|
129
|
+
const scopes = text.match(/Token scopes:\s*(.+)/i)?.[1]?.replace(/['"]/g, "").trim();
|
|
130
|
+
if (!loggedIn) return {
|
|
131
|
+
summary: "logged_in=no",
|
|
132
|
+
loggedIn
|
|
133
|
+
};
|
|
134
|
+
return {
|
|
135
|
+
summary: redact(`logged_in=yes host=${host ?? "?"} account=${account ?? "?"} scopes=${scopes ?? "?"}`),
|
|
136
|
+
loggedIn
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function checkCatalog(source) {
|
|
140
|
+
const kind = catalogKind(source);
|
|
141
|
+
if (kind === "local path") {
|
|
142
|
+
const root = resolve(source.replace(/^~(?=\/|$)/, homedir()));
|
|
143
|
+
if (!existsSync(root)) return {
|
|
144
|
+
ok: false,
|
|
145
|
+
detail: "fail (path does not exist)"
|
|
146
|
+
};
|
|
147
|
+
return existsSync(join(root, ".well-known", "agent-skills", "index.json")) ? {
|
|
148
|
+
ok: true,
|
|
149
|
+
detail: "ok (local path, index.json present)"
|
|
150
|
+
} : {
|
|
151
|
+
ok: false,
|
|
152
|
+
detail: "fail (index.json missing)"
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
if (kind === "owner/repo") {
|
|
156
|
+
const r = probe("gh", [
|
|
157
|
+
"api",
|
|
158
|
+
`repos/${source}`,
|
|
159
|
+
"--jq",
|
|
160
|
+
".default_branch"
|
|
161
|
+
]);
|
|
162
|
+
return r.ok ? {
|
|
163
|
+
ok: true,
|
|
164
|
+
detail: "ok (gh api)"
|
|
165
|
+
} : {
|
|
166
|
+
ok: false,
|
|
167
|
+
detail: `fail (gh api: ${redact(r.out.split("\n")[0] ?? "")})`
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
const r = probe("git", [
|
|
171
|
+
"ls-remote",
|
|
172
|
+
"--exit-code",
|
|
173
|
+
source,
|
|
174
|
+
"HEAD"
|
|
175
|
+
]);
|
|
176
|
+
return r.ok ? {
|
|
177
|
+
ok: true,
|
|
178
|
+
detail: "ok (git ls-remote)"
|
|
179
|
+
} : {
|
|
180
|
+
ok: false,
|
|
181
|
+
detail: `fail (git ls-remote: ${redact(r.out.split("\n").pop() ?? "")})`
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function nearestExisting(path) {
|
|
185
|
+
let dir = path;
|
|
186
|
+
while (!existsSync(dir) && dirname(dir) !== dir) dir = dirname(dir);
|
|
187
|
+
return dir;
|
|
188
|
+
}
|
|
189
|
+
function writable(path) {
|
|
190
|
+
try {
|
|
191
|
+
accessSync(nearestExisting(path), constants.W_OK);
|
|
192
|
+
return true;
|
|
193
|
+
} catch {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
function target(ctx) {
|
|
198
|
+
const global = ctx.args.includes("-g") || ctx.args.includes("--global");
|
|
199
|
+
if (ctx.command === "onboarding") {
|
|
200
|
+
const arg = ctx.args.find((a) => !a.startsWith("-"));
|
|
201
|
+
return {
|
|
202
|
+
scope: "workspace",
|
|
203
|
+
dir: arg ? resolve(arg.replace(/^~(?=\/|$)/, homedir())) : join(homedir(), "pruff")
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
if (global) return {
|
|
207
|
+
scope: "global (-g)",
|
|
208
|
+
dir: process.env.CLAUDE_CONFIG_DIR?.trim() || homedir()
|
|
209
|
+
};
|
|
210
|
+
return {
|
|
211
|
+
scope: "project",
|
|
212
|
+
dir: process.cwd()
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
function settingsStatus() {
|
|
216
|
+
const file = join(process.env.CLAUDE_CONFIG_DIR?.trim() || join(homedir(), ".claude"), "settings.json");
|
|
217
|
+
if (!existsSync(file)) return "missing";
|
|
218
|
+
try {
|
|
219
|
+
JSON.parse(readFileSync(file, "utf-8"));
|
|
220
|
+
return "valid JSON";
|
|
221
|
+
} catch {
|
|
222
|
+
return "NOT valid JSON";
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
function collectDiagnostics(ctx) {
|
|
226
|
+
const envCatalog = process.env.PRUFF_SKILLS_CATALOG;
|
|
227
|
+
const source = envCatalog || DEFAULT_CATALOG;
|
|
228
|
+
const gh = ghStatus();
|
|
229
|
+
const catalog = ctx.checkCatalog ? checkCatalog(source) : null;
|
|
230
|
+
const t = target(ctx);
|
|
231
|
+
const configDir = join(homedir(), ".config", "pruff");
|
|
232
|
+
const failed = recordedFailure();
|
|
233
|
+
const errorLine = ctx.error ?? failed?.message ?? "";
|
|
234
|
+
return {
|
|
235
|
+
cli: version$2(),
|
|
236
|
+
node: process.version,
|
|
237
|
+
packageManager: packageManager(),
|
|
238
|
+
os: `${process.platform} ${process.arch}`,
|
|
239
|
+
time: timestamp(),
|
|
240
|
+
command: redact([
|
|
241
|
+
"pruff-skills",
|
|
242
|
+
ctx.command,
|
|
243
|
+
...ctx.args
|
|
244
|
+
].join(" ")),
|
|
245
|
+
failedStep: ctx.step ?? failed?.step ?? (errorLine ? currentStep : "none"),
|
|
246
|
+
error: errorLine ? redact(errorLine.split("\n")[0] ?? "").slice(0, 200) : "none",
|
|
247
|
+
exitCode: ctx.exitCode === void 0 ? "n/a" : String(ctx.exitCode),
|
|
248
|
+
catalogEnv: envCatalog ? `PRUFF_SKILLS_CATALOG set (${catalogKind(envCatalog)})` : "not set",
|
|
249
|
+
catalogSource: envCatalog ? catalogKind(envCatalog) : `default (${DEFAULT_CATALOG})`,
|
|
250
|
+
catalogCheck: catalog ? catalog.detail : "not checked",
|
|
251
|
+
gh: onPath("gh"),
|
|
252
|
+
ghAuth: gh.summary,
|
|
253
|
+
git: onPath("git"),
|
|
254
|
+
mysql: onPath("mysql"),
|
|
255
|
+
security: onPath("security"),
|
|
256
|
+
scope: t.scope,
|
|
257
|
+
targetDir: `${redact(t.dir)} (writable=${yn(writable(t.dir))})`,
|
|
258
|
+
dbReaderConfig: yn(existsSync(join(configDir, "db-reader.json"))),
|
|
259
|
+
dbReaderCa: yn(existsSync(join(configDir, "rds-us-east-1-bundle.pem"))),
|
|
260
|
+
claudeSettings: settingsStatus(),
|
|
261
|
+
ghLoggedIn: gh.loggedIn,
|
|
262
|
+
catalogOk: catalog?.ok ?? false
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
function renderDiagnostics(d) {
|
|
266
|
+
return [
|
|
267
|
+
"```",
|
|
268
|
+
...[
|
|
269
|
+
DIAGNOSTICS_HEADER,
|
|
270
|
+
`cli: ${d.cli}`,
|
|
271
|
+
`node: ${d.node}`,
|
|
272
|
+
`package_manager: ${d.packageManager}`,
|
|
273
|
+
`os: ${d.os}`,
|
|
274
|
+
`time: ${d.time}`,
|
|
275
|
+
`command: ${d.command}`,
|
|
276
|
+
`failed_step: ${d.failedStep}`,
|
|
277
|
+
`error: ${d.error}`,
|
|
278
|
+
`exit_code: ${d.exitCode}`,
|
|
279
|
+
`catalog_env: ${d.catalogEnv}`,
|
|
280
|
+
`catalog_source: ${d.catalogSource}`,
|
|
281
|
+
`catalog_check: ${d.catalogCheck}`,
|
|
282
|
+
`gh_on_path: ${d.gh}`,
|
|
283
|
+
`gh_auth: ${d.ghAuth}`,
|
|
284
|
+
`git_on_path: ${d.git}`,
|
|
285
|
+
`mysql_on_path: ${d.mysql}`,
|
|
286
|
+
`security_on_path: ${d.security}`,
|
|
287
|
+
`install_scope: ${d.scope}`,
|
|
288
|
+
`target_dir: ${d.targetDir}`,
|
|
289
|
+
`db_reader_config: ${d.dbReaderConfig}`,
|
|
290
|
+
`db_reader_ca_bundle: ${d.dbReaderCa}`,
|
|
291
|
+
`claude_settings_json: ${d.claudeSettings}`,
|
|
292
|
+
DIAGNOSTICS_FOOTER
|
|
293
|
+
],
|
|
294
|
+
"```"
|
|
295
|
+
].join("\n");
|
|
296
|
+
}
|
|
297
|
+
function printDiagnostics(ctx) {
|
|
298
|
+
console.error("");
|
|
299
|
+
console.error(renderDiagnostics(collectDiagnostics(ctx)));
|
|
300
|
+
}
|
|
301
|
+
function lastLoggedError() {
|
|
302
|
+
return lastErrorLine(tail);
|
|
303
|
+
}
|
|
304
|
+
function lastErrorLine(lines) {
|
|
305
|
+
for (let i = lines.length - 1; i >= 0; i--) if (/✖|\bERROR\b|\bfailed\b|\bnot found\b|\bcould not\b|\bdoes not exist\b|\bcannot\b|\bnot authenticated\b/i.test(lines[i])) return lines[i];
|
|
306
|
+
return lines[lines.length - 1] ?? "";
|
|
307
|
+
}
|
|
308
|
+
async function withDiagnostics(command, args, fn) {
|
|
309
|
+
resetDiagnosticsState();
|
|
310
|
+
const realExit = process.exit;
|
|
311
|
+
const realOut = process.stdout.write.bind(process.stdout);
|
|
312
|
+
const realErr = process.stderr.write.bind(process.stderr);
|
|
313
|
+
const remember = (chunk) => {
|
|
314
|
+
if (typeof chunk !== "string") return;
|
|
315
|
+
for (const raw of chunk.replace(ANSI, "").split("\n")) {
|
|
316
|
+
const line = raw.replace(/^[\s│└├┌◇◆■▲●○]+/, "").trim();
|
|
317
|
+
if (line) tail.push(line);
|
|
318
|
+
}
|
|
319
|
+
if (tail.length > 60) tail.splice(0, tail.length - 60);
|
|
320
|
+
};
|
|
321
|
+
process.stdout.write = (chunk, ...rest) => {
|
|
322
|
+
remember(chunk);
|
|
323
|
+
return realOut(chunk, ...rest);
|
|
324
|
+
};
|
|
325
|
+
process.stderr.write = (chunk, ...rest) => {
|
|
326
|
+
remember(chunk);
|
|
327
|
+
return realErr(chunk, ...rest);
|
|
328
|
+
};
|
|
329
|
+
const restore = () => {
|
|
330
|
+
process.exit = realExit;
|
|
331
|
+
process.stdout.write = realOut;
|
|
332
|
+
process.stderr.write = realErr;
|
|
333
|
+
};
|
|
334
|
+
let printed = false;
|
|
335
|
+
const emit = (exitCode, error) => {
|
|
336
|
+
if (printed) return;
|
|
337
|
+
printed = true;
|
|
338
|
+
const message = error ?? recordedFailure()?.message ?? lastErrorLine(tail);
|
|
339
|
+
restore();
|
|
340
|
+
printDiagnostics({
|
|
341
|
+
command,
|
|
342
|
+
args,
|
|
343
|
+
exitCode,
|
|
344
|
+
error: message
|
|
345
|
+
});
|
|
346
|
+
};
|
|
347
|
+
process.exit = (code) => {
|
|
348
|
+
const n = Number(code ?? process.exitCode ?? 0);
|
|
349
|
+
if (n !== 0) emit(n);
|
|
350
|
+
restore();
|
|
351
|
+
return realExit(code);
|
|
352
|
+
};
|
|
353
|
+
try {
|
|
354
|
+
await fn();
|
|
355
|
+
restore();
|
|
356
|
+
if (recordedFailure()) {
|
|
357
|
+
emit(1);
|
|
358
|
+
return 1;
|
|
359
|
+
}
|
|
360
|
+
return 0;
|
|
361
|
+
} catch (error) {
|
|
362
|
+
restore();
|
|
363
|
+
const text = error instanceof Error ? error.message : String(error);
|
|
364
|
+
if (error instanceof UserFacingError) console.error(error.message);
|
|
365
|
+
else console.error(error instanceof Error ? error.stack ?? error.message : String(error));
|
|
366
|
+
emit(1, text);
|
|
367
|
+
return 1;
|
|
368
|
+
} finally {
|
|
369
|
+
restore();
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
async function runDoctor() {
|
|
373
|
+
resetDiagnosticsState();
|
|
374
|
+
const d = collectDiagnostics({
|
|
375
|
+
command: "doctor",
|
|
376
|
+
args: [],
|
|
377
|
+
checkCatalog: true
|
|
378
|
+
});
|
|
379
|
+
console.log(renderDiagnostics(d));
|
|
380
|
+
return d.ghLoggedIn && d.catalogOk ? 0 : 1;
|
|
381
|
+
}
|
|
24
382
|
var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
25
383
|
function getOwnerRepo(parsed) {
|
|
26
384
|
if (parsed.type === "local") return null;
|
|
@@ -436,7 +794,7 @@ const CLONE_TIMEOUT_MS = (() => {
|
|
|
436
794
|
const parsed = Number.parseInt(raw, 10);
|
|
437
795
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_CLONE_TIMEOUT_MS;
|
|
438
796
|
})();
|
|
439
|
-
const execFileAsync$
|
|
797
|
+
const execFileAsync$4 = promisify(execFile);
|
|
440
798
|
var GitCloneError = class extends Error {
|
|
441
799
|
url;
|
|
442
800
|
isTimeout;
|
|
@@ -514,7 +872,7 @@ async function resetTempDir(dir) {
|
|
|
514
872
|
async function tryGhClone(repo, tempDir, ref) {
|
|
515
873
|
let cloneTarget = repo.slug;
|
|
516
874
|
try {
|
|
517
|
-
const { stdout, stderr } = await execFileAsync$
|
|
875
|
+
const { stdout, stderr } = await execFileAsync$4("gh", [
|
|
518
876
|
"auth",
|
|
519
877
|
"status",
|
|
520
878
|
"-h",
|
|
@@ -531,7 +889,7 @@ async function tryGhClone(repo, tempDir, ref) {
|
|
|
531
889
|
} catch {
|
|
532
890
|
return false;
|
|
533
891
|
}
|
|
534
|
-
await execFileAsync$
|
|
892
|
+
await execFileAsync$4("gh", [
|
|
535
893
|
"repo",
|
|
536
894
|
"clone",
|
|
537
895
|
cloneTarget,
|
|
@@ -683,8 +1041,6 @@ async function getPluginGroupings(basePath) {
|
|
|
683
1041
|
} catch {}
|
|
684
1042
|
return groupings;
|
|
685
1043
|
}
|
|
686
|
-
var UserFacingError = class extends Error {};
|
|
687
|
-
var LockVersionError = class extends UserFacingError {};
|
|
688
1044
|
const LOCAL_LOCK_FILE = "skills-lock.json";
|
|
689
1045
|
const CURRENT_VERSION$1 = 1;
|
|
690
1046
|
function getLocalLockPath(cwd) {
|
|
@@ -1695,6 +2051,27 @@ async function createSymlink(target, linkPath) {
|
|
|
1695
2051
|
return false;
|
|
1696
2052
|
}
|
|
1697
2053
|
}
|
|
2054
|
+
const canonicalCopies = /* @__PURE__ */ new Map();
|
|
2055
|
+
async function copyCanonicalOnce(src, canonicalDir) {
|
|
2056
|
+
const key = `${src}\0${canonicalDir}`;
|
|
2057
|
+
let entry = canonicalCopies.get(key);
|
|
2058
|
+
if (!entry || entry.done && !existsSync(canonicalDir)) {
|
|
2059
|
+
const fresh = {
|
|
2060
|
+
done: false,
|
|
2061
|
+
promise: Promise.resolve()
|
|
2062
|
+
};
|
|
2063
|
+
fresh.promise = (async () => {
|
|
2064
|
+
await cleanAndCreateDirectory(canonicalDir);
|
|
2065
|
+
await copyDirectory(src, canonicalDir);
|
|
2066
|
+
})();
|
|
2067
|
+
fresh.promise.then(() => {
|
|
2068
|
+
fresh.done = true;
|
|
2069
|
+
}, () => canonicalCopies.delete(key));
|
|
2070
|
+
canonicalCopies.set(key, fresh);
|
|
2071
|
+
entry = fresh;
|
|
2072
|
+
}
|
|
2073
|
+
await entry.promise;
|
|
2074
|
+
}
|
|
1698
2075
|
async function installSkillForAgent(skill, agentType, options = {}) {
|
|
1699
2076
|
const agent = agents[agentType];
|
|
1700
2077
|
const isGlobal = options.global ?? false;
|
|
@@ -1733,8 +2110,7 @@ async function installSkillForAgent(skill, agentType, options = {}) {
|
|
|
1733
2110
|
mode: "copy"
|
|
1734
2111
|
};
|
|
1735
2112
|
}
|
|
1736
|
-
await
|
|
1737
|
-
await copyDirectory(skill.path, canonicalDir);
|
|
2113
|
+
await copyCanonicalOnce(skill.path, canonicalDir);
|
|
1738
2114
|
if (isGlobal && isUniversalAgent(agentType)) return {
|
|
1739
2115
|
success: true,
|
|
1740
2116
|
path: canonicalDir,
|
|
@@ -2768,7 +3144,8 @@ async function fetchTreeBranch(ownerRepo, branch, token) {
|
|
|
2768
3144
|
}
|
|
2769
3145
|
return {
|
|
2770
3146
|
tree: null,
|
|
2771
|
-
rateLimited: response.status === 403 && response.headers.get("x-ratelimit-remaining") === "0"
|
|
3147
|
+
rateLimited: response.status === 403 && response.headers.get("x-ratelimit-remaining") === "0",
|
|
3148
|
+
notFound: response.status === 404
|
|
2772
3149
|
};
|
|
2773
3150
|
} catch {
|
|
2774
3151
|
return {
|
|
@@ -2793,6 +3170,7 @@ async function fetchRepoTree(ownerRepo, ref, getToken) {
|
|
|
2793
3170
|
return null;
|
|
2794
3171
|
}
|
|
2795
3172
|
let rateLimited = false;
|
|
3173
|
+
let privateSuspected = false;
|
|
2796
3174
|
for (const branch of branches) {
|
|
2797
3175
|
const result = await fetchTreeBranch(ownerRepo, branch, null);
|
|
2798
3176
|
if (result.tree) return result.tree;
|
|
@@ -2800,9 +3178,13 @@ async function fetchRepoTree(ownerRepo, ref, getToken) {
|
|
|
2800
3178
|
rateLimited = true;
|
|
2801
3179
|
break;
|
|
2802
3180
|
}
|
|
3181
|
+
if (result.notFound && getToken) {
|
|
3182
|
+
privateSuspected = true;
|
|
3183
|
+
break;
|
|
3184
|
+
}
|
|
2803
3185
|
}
|
|
2804
|
-
if (!rateLimited || !getToken) return null;
|
|
2805
|
-
_rateLimitedThisSession = true;
|
|
3186
|
+
if (!(rateLimited || privateSuspected) || !getToken) return null;
|
|
3187
|
+
if (rateLimited) _rateLimitedThisSession = true;
|
|
2806
3188
|
const token = getToken();
|
|
2807
3189
|
if (!token) return null;
|
|
2808
3190
|
for (const branch of branches) {
|
|
@@ -3120,6 +3502,7 @@ async function selectAgentsInteractive(options) {
|
|
|
3120
3502
|
});
|
|
3121
3503
|
}
|
|
3122
3504
|
async function handleWellKnownSkills(source, url, options, spinner) {
|
|
3505
|
+
setStep("add:discover-skills");
|
|
3123
3506
|
spinner.start("Discovering skills from well-known endpoint...");
|
|
3124
3507
|
const skills = await wellKnownProvider.fetchAllSkills(url);
|
|
3125
3508
|
if (skills.length === 0) {
|
|
@@ -3194,6 +3577,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
3194
3577
|
}
|
|
3195
3578
|
targetAgents = options.agent;
|
|
3196
3579
|
} else {
|
|
3580
|
+
setStep("add:load-agents");
|
|
3197
3581
|
spinner.start("Loading agents...");
|
|
3198
3582
|
const installedAgents = await detectInstalledAgents();
|
|
3199
3583
|
const totalAgents = Object.keys(agents).length;
|
|
@@ -3304,6 +3688,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
3304
3688
|
}
|
|
3305
3689
|
const sourceIdentifier = wellKnownProvider.getSourceIdentifier(url);
|
|
3306
3690
|
const wellKnownPrivacyPromise = isSourcePrivate(sourceIdentifier).catch(() => null);
|
|
3691
|
+
setStep("add:install");
|
|
3307
3692
|
spinner.start("Installing skills...");
|
|
3308
3693
|
const results = [];
|
|
3309
3694
|
for (const skill of selectedSkills) for (const agent of targetAgents) {
|
|
@@ -3437,6 +3822,7 @@ async function runAdd(args, options = {}) {
|
|
|
3437
3822
|
let tempDir = null;
|
|
3438
3823
|
try {
|
|
3439
3824
|
const spinner = Y();
|
|
3825
|
+
setStep("add:parse-source");
|
|
3440
3826
|
spinner.start("Parsing source...");
|
|
3441
3827
|
const parsed = parseSource(source);
|
|
3442
3828
|
spinner.stop(`Source: ${parsed.type === "local" ? parsed.localPath : parsed.url}${parsed.ref ? ` @ ${import_picocolors.default.yellow(parsed.ref)}` : ""}${parsed.subpath ? ` (${parsed.subpath})` : ""}${parsed.skillFilter ? ` ${import_picocolors.default.dim("@")}${import_picocolors.default.cyan(parsed.skillFilter)}` : ""}`);
|
|
@@ -3469,6 +3855,7 @@ async function runAdd(args, options = {}) {
|
|
|
3469
3855
|
let skills;
|
|
3470
3856
|
let blobResult = null;
|
|
3471
3857
|
if (parsed.type === "local") {
|
|
3858
|
+
setStep("add:validate-path");
|
|
3472
3859
|
spinner.start("Validating local path...");
|
|
3473
3860
|
if (!existsSync(parsed.localPath)) {
|
|
3474
3861
|
spinner.stop(import_picocolors.default.red("Path not found"));
|
|
@@ -3476,6 +3863,7 @@ async function runAdd(args, options = {}) {
|
|
|
3476
3863
|
process.exit(1);
|
|
3477
3864
|
}
|
|
3478
3865
|
spinner.stop("Local path validated");
|
|
3866
|
+
setStep("add:discover-skills");
|
|
3479
3867
|
spinner.start("Discovering skills...");
|
|
3480
3868
|
skills = await discoverSkills(parsed.localPath, parsed.subpath, {
|
|
3481
3869
|
includeInternal,
|
|
@@ -3486,6 +3874,7 @@ async function runAdd(args, options = {}) {
|
|
|
3486
3874
|
const ownerRepo = getOwnerRepo(parsed);
|
|
3487
3875
|
const owner = ownerRepo?.split("/")[0]?.toLowerCase();
|
|
3488
3876
|
if (ownerRepo && owner && BLOB_ALLOWED_OWNERS.includes(owner)) {
|
|
3877
|
+
setStep("add:fetch-skills");
|
|
3489
3878
|
spinner.start("Fetching skills...");
|
|
3490
3879
|
blobResult = await tryBlobInstall(ownerRepo, {
|
|
3491
3880
|
subpath: parsed.subpath,
|
|
@@ -3500,9 +3889,11 @@ async function runAdd(args, options = {}) {
|
|
|
3500
3889
|
skills = blobResult.skills;
|
|
3501
3890
|
spinner.stop(`Found ${import_picocolors.default.green(skills.length)} skill${skills.length > 1 ? "s" : ""}`);
|
|
3502
3891
|
} else {
|
|
3892
|
+
setStep("add:clone");
|
|
3503
3893
|
spinner.start("Cloning repository...");
|
|
3504
3894
|
tempDir = await cloneRepo$1(parsed.url, parsed.ref);
|
|
3505
3895
|
spinner.stop("Repository cloned");
|
|
3896
|
+
setStep("add:discover-skills");
|
|
3506
3897
|
spinner.start("Discovering skills...");
|
|
3507
3898
|
skills = await discoverSkills(tempDir, parsed.subpath, {
|
|
3508
3899
|
includeInternal,
|
|
@@ -3510,9 +3901,11 @@ async function runAdd(args, options = {}) {
|
|
|
3510
3901
|
});
|
|
3511
3902
|
}
|
|
3512
3903
|
} else {
|
|
3904
|
+
setStep("add:clone");
|
|
3513
3905
|
spinner.start("Cloning repository...");
|
|
3514
3906
|
tempDir = await cloneRepo$1(parsed.url, parsed.ref);
|
|
3515
3907
|
spinner.stop("Repository cloned");
|
|
3908
|
+
setStep("add:discover-skills");
|
|
3516
3909
|
spinner.start("Discovering skills...");
|
|
3517
3910
|
skills = await discoverSkills(tempDir, parsed.subpath, {
|
|
3518
3911
|
includeInternal,
|
|
@@ -3639,6 +4032,7 @@ async function runAdd(args, options = {}) {
|
|
|
3639
4032
|
}
|
|
3640
4033
|
targetAgents = options.agent;
|
|
3641
4034
|
} else {
|
|
4035
|
+
setStep("add:load-agents");
|
|
3642
4036
|
spinner.start("Loading agents...");
|
|
3643
4037
|
const installedAgents = await detectInstalledAgents();
|
|
3644
4038
|
const totalAgents = Object.keys(agents).length;
|
|
@@ -3784,6 +4178,7 @@ async function runAdd(args, options = {}) {
|
|
|
3784
4178
|
process.exit(0);
|
|
3785
4179
|
}
|
|
3786
4180
|
}
|
|
4181
|
+
setStep("add:install");
|
|
3787
4182
|
spinner.start("Installing skills...");
|
|
3788
4183
|
const results = [];
|
|
3789
4184
|
for (const skill of selectedSkills) for (const agent of targetAgents) {
|
|
@@ -4031,20 +4426,42 @@ function parseAddOptions(args) {
|
|
|
4031
4426
|
options
|
|
4032
4427
|
};
|
|
4033
4428
|
}
|
|
4034
|
-
const execFileAsync$
|
|
4429
|
+
const execFileAsync$3 = promisify(execFile);
|
|
4035
4430
|
const isLocalSource = (source) => source.startsWith(".") || isAbsolute(source);
|
|
4036
4431
|
const isGitHubSource = (source) => /^[^/]+\/[^/]+$/.test(source);
|
|
4037
4432
|
const ghEnv = () => ({
|
|
4038
4433
|
...process.env,
|
|
4039
4434
|
GIT_TERMINAL_PROMPT: "0"
|
|
4040
4435
|
});
|
|
4436
|
+
const clones = /* @__PURE__ */ new Map();
|
|
4437
|
+
let exitHookInstalled = false;
|
|
4438
|
+
const settledDirs = /* @__PURE__ */ new Set();
|
|
4439
|
+
function cloneOnce(source) {
|
|
4440
|
+
let pending = clones.get(source);
|
|
4441
|
+
if (!pending) {
|
|
4442
|
+
pending = cloneRepo$1(source);
|
|
4443
|
+
clones.set(source, pending);
|
|
4444
|
+
pending.catch(() => clones.delete(source));
|
|
4445
|
+
if (!exitHookInstalled) {
|
|
4446
|
+
exitHookInstalled = true;
|
|
4447
|
+
process.on("exit", () => {
|
|
4448
|
+
for (const dir of settledDirs) rmSync(dir, {
|
|
4449
|
+
recursive: true,
|
|
4450
|
+
force: true
|
|
4451
|
+
});
|
|
4452
|
+
});
|
|
4453
|
+
}
|
|
4454
|
+
pending.then((dir) => settledDirs.add(dir)).catch(() => {});
|
|
4455
|
+
}
|
|
4456
|
+
return pending;
|
|
4457
|
+
}
|
|
4041
4458
|
async function fetchCatalogFile(source, rel) {
|
|
4042
4459
|
if (isLocalSource(source)) {
|
|
4043
4460
|
const file = join(source, rel);
|
|
4044
4461
|
return existsSync(file) ? readFile(file, "utf8") : null;
|
|
4045
4462
|
}
|
|
4046
4463
|
if (isGitHubSource(source)) try {
|
|
4047
|
-
const { stdout } = await execFileAsync$
|
|
4464
|
+
const { stdout } = await execFileAsync$3("gh", [
|
|
4048
4465
|
"api",
|
|
4049
4466
|
`repos/${source}/contents/${rel}`,
|
|
4050
4467
|
"--jq",
|
|
@@ -4055,7 +4472,7 @@ async function fetchCatalogFile(source, rel) {
|
|
|
4055
4472
|
return null;
|
|
4056
4473
|
}
|
|
4057
4474
|
try {
|
|
4058
|
-
const file = join(await
|
|
4475
|
+
const file = join(await cloneOnce(source), rel);
|
|
4059
4476
|
return existsSync(file) ? readFile(file, "utf8") : null;
|
|
4060
4477
|
} catch {
|
|
4061
4478
|
return null;
|
|
@@ -4068,7 +4485,7 @@ async function listCatalogDir(source, dir) {
|
|
|
4068
4485
|
return null;
|
|
4069
4486
|
}
|
|
4070
4487
|
if (isGitHubSource(source)) try {
|
|
4071
|
-
const { stdout } = await execFileAsync$
|
|
4488
|
+
const { stdout } = await execFileAsync$3("gh", [
|
|
4072
4489
|
"api",
|
|
4073
4490
|
`repos/${source}/contents/${dir}`,
|
|
4074
4491
|
"--jq",
|
|
@@ -4079,7 +4496,7 @@ async function listCatalogDir(source, dir) {
|
|
|
4079
4496
|
return null;
|
|
4080
4497
|
}
|
|
4081
4498
|
try {
|
|
4082
|
-
return await readdir(join(await
|
|
4499
|
+
return await readdir(join(await cloneOnce(source), dir));
|
|
4083
4500
|
} catch {
|
|
4084
4501
|
return null;
|
|
4085
4502
|
}
|
|
@@ -4088,7 +4505,7 @@ const claudeAgentsDir = (global) => global ? join(process.env.CLAUDE_CONFIG_DIR?
|
|
|
4088
4505
|
const fetchAgentFile = (source, name) => fetchCatalogFile(source, `agents/${name}.md`);
|
|
4089
4506
|
async function listCatalogAgents(source) {
|
|
4090
4507
|
const files = await listCatalogDir(source, "agents");
|
|
4091
|
-
return files ? files.filter((f) => f.endsWith(".md")).map((f) => f.slice(0, -3)) : null;
|
|
4508
|
+
return files ? files.filter((f) => f.endsWith(".md") && !f.startsWith("_")).map((f) => f.slice(0, -3)) : null;
|
|
4092
4509
|
}
|
|
4093
4510
|
async function listInstalledAgents(targetDir) {
|
|
4094
4511
|
try {
|
|
@@ -4102,6 +4519,7 @@ async function updateAgents(options = {}) {
|
|
|
4102
4519
|
const targetDir = claudeAgentsDir(options.global ?? false);
|
|
4103
4520
|
const result = {
|
|
4104
4521
|
updated: [],
|
|
4522
|
+
unchanged: [],
|
|
4105
4523
|
failed: [],
|
|
4106
4524
|
localOnly: [],
|
|
4107
4525
|
available: [],
|
|
@@ -4125,12 +4543,18 @@ async function updateAgents(options = {}) {
|
|
|
4125
4543
|
result.localOnly.push(name);
|
|
4126
4544
|
continue;
|
|
4127
4545
|
}
|
|
4546
|
+
setStep(`agents:fetch:${name}`);
|
|
4128
4547
|
const content = await fetchAgentFile(source, name);
|
|
4129
4548
|
if (content === null) {
|
|
4130
4549
|
result.failed.push(name);
|
|
4131
4550
|
continue;
|
|
4132
4551
|
}
|
|
4133
|
-
|
|
4552
|
+
const dest = join(targetDir, `${sanitizeName(name)}.md`);
|
|
4553
|
+
if (await readFile(dest, "utf8").catch(() => null) === content) {
|
|
4554
|
+
result.unchanged.push(name);
|
|
4555
|
+
continue;
|
|
4556
|
+
}
|
|
4557
|
+
await writeFile(dest, content);
|
|
4134
4558
|
result.updated.push(name);
|
|
4135
4559
|
}
|
|
4136
4560
|
return result;
|
|
@@ -4155,6 +4579,7 @@ async function addAgents(names, options) {
|
|
|
4155
4579
|
M.message(import_picocolors.default.dim("Browse them with `npx pruff-skills find`."));
|
|
4156
4580
|
process.exit(1);
|
|
4157
4581
|
}
|
|
4582
|
+
setStep("agents:write");
|
|
4158
4583
|
await mkdir(targetDir, { recursive: true });
|
|
4159
4584
|
const installed = [];
|
|
4160
4585
|
for (const name of names) {
|
|
@@ -4212,7 +4637,6 @@ async function runAgents(args) {
|
|
|
4212
4637
|
console.log(`Unknown agents command: ${sub}`);
|
|
4213
4638
|
console.log(AGENTS_HELP);
|
|
4214
4639
|
}
|
|
4215
|
-
const execFileAsync$3 = promisify(execFile);
|
|
4216
4640
|
const RULE_SCOPES = [
|
|
4217
4641
|
"client",
|
|
4218
4642
|
"server",
|
|
@@ -4223,35 +4647,9 @@ function stripFrontmatter(md) {
|
|
|
4223
4647
|
const m = md.match(/^---\n[\s\S]*?\n---\n+/);
|
|
4224
4648
|
return m ? md.slice(m[0].length) : md;
|
|
4225
4649
|
}
|
|
4226
|
-
|
|
4227
|
-
if (source.startsWith(".") || isAbsolute(source)) {
|
|
4228
|
-
const file = join(source, rel);
|
|
4229
|
-
return existsSync(file) ? readFile(file, "utf8") : null;
|
|
4230
|
-
}
|
|
4231
|
-
if (/^[^/]+\/[^/]+$/.test(source)) try {
|
|
4232
|
-
const { stdout } = await execFileAsync$3("gh", [
|
|
4233
|
-
"api",
|
|
4234
|
-
`repos/${source}/contents/${rel}`,
|
|
4235
|
-
"--jq",
|
|
4236
|
-
".content"
|
|
4237
|
-
], { env: {
|
|
4238
|
-
...process.env,
|
|
4239
|
-
GIT_TERMINAL_PROMPT: "0"
|
|
4240
|
-
} });
|
|
4241
|
-
return Buffer.from(stdout.trim(), "base64").toString("utf8");
|
|
4242
|
-
} catch {
|
|
4243
|
-
return null;
|
|
4244
|
-
}
|
|
4245
|
-
try {
|
|
4246
|
-
const file = join(await cloneRepo$1(source), rel);
|
|
4247
|
-
return existsSync(file) ? readFile(file, "utf8") : null;
|
|
4248
|
-
} catch {
|
|
4249
|
-
return null;
|
|
4250
|
-
}
|
|
4251
|
-
}
|
|
4252
|
-
const fetchRuleFile = (source, name) => fetchFromCatalog(source, `rules/${name}.md`);
|
|
4650
|
+
const fetchRuleFile = (source, name) => fetchCatalogFile(source, `rules/${name}.md`);
|
|
4253
4651
|
async function fetchRuleIndex(source) {
|
|
4254
|
-
const raw = await
|
|
4652
|
+
const raw = await fetchCatalogFile(source, ".well-known/agent-skills/index.json");
|
|
4255
4653
|
if (!raw) return [];
|
|
4256
4654
|
return parseCatalogIndex(raw, source).rules ?? [];
|
|
4257
4655
|
}
|
|
@@ -4287,6 +4685,7 @@ async function addRules(names, options) {
|
|
|
4287
4685
|
Ie(import_picocolors.default.cyan("pruff-skills rules"));
|
|
4288
4686
|
let toInstall = names.filter(Boolean);
|
|
4289
4687
|
if (options.all || options.scopes?.length) {
|
|
4688
|
+
setStep("rules:fetch-index");
|
|
4290
4689
|
const index = await fetchRuleIndex(source);
|
|
4291
4690
|
if (index.length === 0) {
|
|
4292
4691
|
M.error(`Could not read the rules catalog (${source}).`);
|
|
@@ -4303,6 +4702,7 @@ async function addRules(names, options) {
|
|
|
4303
4702
|
M.message(import_picocolors.default.dim("Browse them with `npx pruff-skills find`."));
|
|
4304
4703
|
process.exit(1);
|
|
4305
4704
|
}
|
|
4705
|
+
setStep("rules:write");
|
|
4306
4706
|
await mkdir(targetDir, { recursive: true });
|
|
4307
4707
|
const installed = [];
|
|
4308
4708
|
for (const name of toInstall) {
|
|
@@ -4310,6 +4710,7 @@ async function addRules(names, options) {
|
|
|
4310
4710
|
M.warn(`Skipping invalid rule name: ${import_picocolors.default.yellow(name)}`);
|
|
4311
4711
|
continue;
|
|
4312
4712
|
}
|
|
4713
|
+
setStep(`rules:fetch:${name}`);
|
|
4313
4714
|
const content = await fetchRuleFile(source, name);
|
|
4314
4715
|
if (!content) {
|
|
4315
4716
|
M.error(`Rule ${import_picocolors.default.yellow(name)} not found in the catalog (${source}).`);
|
|
@@ -5728,6 +6129,7 @@ async function updateProjectSkills(options = {}) {
|
|
|
5728
6129
|
const projectSkills = await getProjectSkillsForUpdate(options.skills);
|
|
5729
6130
|
let successCount = 0;
|
|
5730
6131
|
let failCount = 0;
|
|
6132
|
+
let upToDateCount = 0;
|
|
5731
6133
|
if (projectSkills.length === 0) {
|
|
5732
6134
|
if (!options.skills) {
|
|
5733
6135
|
console.log(`${DIM$1}No project skills to update.${RESET$1}`);
|
|
@@ -5736,7 +6138,8 @@ async function updateProjectSkills(options = {}) {
|
|
|
5736
6138
|
return {
|
|
5737
6139
|
successCount,
|
|
5738
6140
|
failCount,
|
|
5739
|
-
foundCount: 0
|
|
6141
|
+
foundCount: 0,
|
|
6142
|
+
upToDateCount
|
|
5740
6143
|
};
|
|
5741
6144
|
}
|
|
5742
6145
|
const updatable = projectSkills.filter((s) => s.entry.skillPath);
|
|
@@ -5747,7 +6150,8 @@ async function updateProjectSkills(options = {}) {
|
|
|
5747
6150
|
return {
|
|
5748
6151
|
successCount,
|
|
5749
6152
|
failCount,
|
|
5750
|
-
foundCount: projectSkills.length
|
|
6153
|
+
foundCount: projectSkills.length,
|
|
6154
|
+
upToDateCount
|
|
5751
6155
|
};
|
|
5752
6156
|
}
|
|
5753
6157
|
const cwd = process.cwd();
|
|
@@ -5779,7 +6183,8 @@ async function updateProjectSkills(options = {}) {
|
|
|
5779
6183
|
return {
|
|
5780
6184
|
successCount,
|
|
5781
6185
|
failCount: updatable.length,
|
|
5782
|
-
foundCount: projectSkills.length
|
|
6186
|
+
foundCount: projectSkills.length,
|
|
6187
|
+
upToDateCount
|
|
5783
6188
|
};
|
|
5784
6189
|
}
|
|
5785
6190
|
for (const [source, skillsForSource] of bySource) {
|
|
@@ -5790,12 +6195,19 @@ async function updateProjectSkills(options = {}) {
|
|
|
5790
6195
|
const allLockedForSource = Object.entries(localLock.skills).filter(([_, entry]) => entry.source === source).map(([name, _]) => name);
|
|
5791
6196
|
let tempDir = null;
|
|
5792
6197
|
let deletedSkills = [];
|
|
6198
|
+
const unchangedSkills = /* @__PURE__ */ new Set();
|
|
5793
6199
|
try {
|
|
5794
6200
|
tempDir = await cloneRepo$1(sourceUrl, ref);
|
|
5795
6201
|
const discoveredPaths = (await discoverSkills(tempDir)).map((s) => {
|
|
5796
6202
|
return join(relative(tempDir, s.path), "SKILL.md").split(sep).join("/");
|
|
5797
6203
|
});
|
|
5798
6204
|
deletedSkills = await checkAndPromptForDeletions(source, allLockedForSource, localLock.skills, false, options, discoveredPaths);
|
|
6205
|
+
for (const skill of skillsForSource) {
|
|
6206
|
+
const { skillPath, computedHash } = skill.entry;
|
|
6207
|
+
if (!skillPath || !computedHash || deletedSkills.includes(skill.name)) continue;
|
|
6208
|
+
const currentHash = await computeSkillFolderHash(join(tempDir, dirname(skillPath))).catch(() => "");
|
|
6209
|
+
if (currentHash && currentHash === computedHash) unchangedSkills.add(skill.name);
|
|
6210
|
+
}
|
|
5799
6211
|
} catch (error) {
|
|
5800
6212
|
const reason = error instanceof Error ? error.message : String(error);
|
|
5801
6213
|
console.log(`${DIM$1}✗ Failed to check for deleted skills from ${source}: ${sanitizeMetadata(reason)}${RESET$1}`);
|
|
@@ -5805,6 +6217,11 @@ async function updateProjectSkills(options = {}) {
|
|
|
5805
6217
|
const remainingSkills = skillsForSource.filter((s) => !deletedSkills.includes(s.name));
|
|
5806
6218
|
for (const skill of remainingSkills) {
|
|
5807
6219
|
const safeName = sanitizeMetadata(skill.name);
|
|
6220
|
+
if (unchangedSkills.has(skill.name)) {
|
|
6221
|
+
upToDateCount++;
|
|
6222
|
+
console.log(`${TEXT$1}✓${RESET$1} ${safeName} ${DIM$1}is up to date${RESET$1}`);
|
|
6223
|
+
continue;
|
|
6224
|
+
}
|
|
5808
6225
|
console.log(`${TEXT$1}Updating ${safeName}...${RESET$1}`);
|
|
5809
6226
|
const installUrl = formatSourceInput(skill.entry.source, skill.entry.ref);
|
|
5810
6227
|
if (spawnSync(process.execPath, [
|
|
@@ -5835,7 +6252,8 @@ async function updateProjectSkills(options = {}) {
|
|
|
5835
6252
|
return {
|
|
5836
6253
|
successCount,
|
|
5837
6254
|
failCount,
|
|
5838
|
-
foundCount: projectSkills.length
|
|
6255
|
+
foundCount: projectSkills.length,
|
|
6256
|
+
upToDateCount
|
|
5839
6257
|
};
|
|
5840
6258
|
}
|
|
5841
6259
|
function printLegacyProjectSkills(legacy) {
|
|
@@ -5859,6 +6277,7 @@ async function syncAgentsForScope(isGlobal, options) {
|
|
|
5859
6277
|
return result;
|
|
5860
6278
|
}
|
|
5861
6279
|
for (const name of result.updated) console.log(` ${TEXT$1}✓${RESET$1} Synced ${sanitizeMetadata(name)}`);
|
|
6280
|
+
for (const name of result.unchanged) console.log(` ${TEXT$1}✓${RESET$1} ${sanitizeMetadata(name)} ${DIM$1}is up to date${RESET$1}`);
|
|
5862
6281
|
for (const name of result.failed) console.log(` ${DIM$1}✗ Failed to sync ${sanitizeMetadata(name)}${RESET$1}`);
|
|
5863
6282
|
if (result.localOnly.length > 0) {
|
|
5864
6283
|
const names = result.localOnly.map((n) => sanitizeMetadata(n)).join(", ");
|
|
@@ -5873,6 +6292,7 @@ async function syncAgentsForScope(isGlobal, options) {
|
|
|
5873
6292
|
}
|
|
5874
6293
|
async function runUpdate(args = []) {
|
|
5875
6294
|
const options = parseUpdateOptions(args);
|
|
6295
|
+
setStep("update:resolve-scope");
|
|
5876
6296
|
const scope = await resolveUpdateScope(options);
|
|
5877
6297
|
if (options.skills) console.log(`${TEXT$1}Updating ${options.skills.join(", ")}...${RESET$1}`);
|
|
5878
6298
|
else console.log(`${TEXT$1}Checking for skill updates...${RESET$1}`);
|
|
@@ -5880,8 +6300,10 @@ async function runUpdate(args = []) {
|
|
|
5880
6300
|
let totalSuccess = 0;
|
|
5881
6301
|
let totalFail = 0;
|
|
5882
6302
|
let totalFound = 0;
|
|
6303
|
+
let totalUpToDate = 0;
|
|
5883
6304
|
if (scope === "global" || scope === "both") {
|
|
5884
6305
|
if (scope === "both" && !options.skills) console.log(`${BOLD$1}Global Skills${RESET$1}`);
|
|
6306
|
+
setStep("update:global-skills");
|
|
5885
6307
|
const { successCount, failCount, checkedCount } = await updateGlobalSkills(options);
|
|
5886
6308
|
totalSuccess += successCount;
|
|
5887
6309
|
totalFail += failCount;
|
|
@@ -5890,7 +6312,9 @@ async function runUpdate(args = []) {
|
|
|
5890
6312
|
}
|
|
5891
6313
|
if (scope === "project" || scope === "both") {
|
|
5892
6314
|
if (scope === "both" && !options.skills) console.log(`${BOLD$1}Project Skills${RESET$1}`);
|
|
5893
|
-
|
|
6315
|
+
setStep("update:project-skills");
|
|
6316
|
+
const { successCount, failCount, foundCount, upToDateCount } = await updateProjectSkills(options);
|
|
6317
|
+
totalUpToDate += upToDateCount;
|
|
5894
6318
|
totalSuccess += successCount;
|
|
5895
6319
|
totalFail += failCount;
|
|
5896
6320
|
totalFound += foundCount;
|
|
@@ -5898,22 +6322,30 @@ async function runUpdate(args = []) {
|
|
|
5898
6322
|
console.log();
|
|
5899
6323
|
console.log(`${BOLD$1}Subagents${RESET$1}`);
|
|
5900
6324
|
console.log(`${DIM$1}ALL installed subagents will be updated and synced with the repo — local edits are overwritten.${RESET$1}`);
|
|
6325
|
+
setStep("update:sync-agents");
|
|
5901
6326
|
const agentScopes = scope === "both" ? [true, false] : scope === "global" ? [true] : [false];
|
|
5902
6327
|
let agentsSynced = 0;
|
|
5903
6328
|
let agentsFailed = 0;
|
|
6329
|
+
let agentsUnchanged = 0;
|
|
5904
6330
|
for (const isGlobal of agentScopes) {
|
|
5905
6331
|
const agentResult = await syncAgentsForScope(isGlobal, options);
|
|
5906
6332
|
agentsSynced += agentResult.updated.length;
|
|
6333
|
+
agentsUnchanged += agentResult.unchanged.length;
|
|
5907
6334
|
agentsFailed += agentResult.failed.length;
|
|
5908
6335
|
}
|
|
5909
|
-
if (agentsSynced === 0 && agentsFailed === 0) console.log(` ${DIM$1}No installed subagents to sync.${RESET$1}`);
|
|
6336
|
+
if (agentsSynced === 0 && agentsFailed === 0 && agentsUnchanged === 0) console.log(` ${DIM$1}No installed subagents to sync.${RESET$1}`);
|
|
5910
6337
|
totalSuccess += agentsSynced;
|
|
5911
6338
|
totalFail += agentsFailed;
|
|
5912
|
-
totalFound += agentsSynced + agentsFailed;
|
|
6339
|
+
totalFound += agentsSynced + agentsFailed + agentsUnchanged;
|
|
6340
|
+
totalUpToDate += agentsUnchanged;
|
|
5913
6341
|
if (options.skills && totalFound === 0) console.log(`${DIM$1}No installed skills found matching: ${options.skills.join(", ")}${RESET$1}`);
|
|
5914
6342
|
console.log();
|
|
5915
6343
|
if (totalSuccess > 0) console.log(`${TEXT$1}✓ Updated ${totalSuccess} item(s)${RESET$1}`);
|
|
5916
|
-
if (
|
|
6344
|
+
else if (totalUpToDate > 0 && totalFail === 0) console.log(`${TEXT$1}✓ Everything is up to date${RESET$1}`);
|
|
6345
|
+
if (totalFail > 0) {
|
|
6346
|
+
recordFailure(`Failed to update ${totalFail} item(s)`);
|
|
6347
|
+
console.log(`${DIM$1}Failed to update ${totalFail} item(s)${RESET$1}`);
|
|
6348
|
+
}
|
|
5917
6349
|
String(totalSuccess + totalFail);
|
|
5918
6350
|
console.log();
|
|
5919
6351
|
}
|
|
@@ -6836,6 +7268,7 @@ async function installReaderSkill(workspace, note) {
|
|
|
6836
7268
|
}
|
|
6837
7269
|
async function setupDbReader({ workspace, note }) {
|
|
6838
7270
|
M.step("Setting up read-only prod database access…");
|
|
7271
|
+
setStep("db-reader:aws-identity");
|
|
6839
7272
|
const identity = aws([
|
|
6840
7273
|
"sts",
|
|
6841
7274
|
"get-caller-identity",
|
|
@@ -6854,6 +7287,7 @@ async function setupDbReader({ workspace, note }) {
|
|
|
6854
7287
|
return;
|
|
6855
7288
|
}
|
|
6856
7289
|
const secretId = `${DB_READER_SECRET_PREFIX}${iamUser}`;
|
|
7290
|
+
setStep("db-reader:fetch-secret");
|
|
6857
7291
|
const fetched = aws([
|
|
6858
7292
|
"secretsmanager",
|
|
6859
7293
|
"get-secret-value",
|
|
@@ -6885,16 +7319,19 @@ async function setupDbReader({ workspace, note }) {
|
|
|
6885
7319
|
note(`Prod DB access not set up — ${secretId} does not hold a valid username and password.`);
|
|
6886
7320
|
return;
|
|
6887
7321
|
}
|
|
7322
|
+
setStep("db-reader:keychain");
|
|
6888
7323
|
if (!storeInKeychain(secret.username, secret.password)) {
|
|
6889
7324
|
note(`Prod DB access not set up — the Keychain write failed. Add it by hand: ${import_picocolors.default.cyan(`security add-generic-password -U -s ${DB_READER_SERVICE} -a ${secret.username} -w`)} (prompts for the password).`);
|
|
6890
7325
|
return;
|
|
6891
7326
|
}
|
|
6892
7327
|
writeConfig(secret.username);
|
|
6893
7328
|
M.success(`Keychain entry ${DB_READER_SERVICE} stored for ${secret.username}`);
|
|
7329
|
+
setStep("db-reader:ca-bundle");
|
|
6894
7330
|
if (!downloadCaBundle()) {
|
|
6895
7331
|
note(`Prod DB access not set up — the RDS CA bundle download failed. Run: ${import_picocolors.default.cyan(`curl -sSfo ${dbReaderCaPath()} ${RDS_CA_URL}`)}`);
|
|
6896
7332
|
return;
|
|
6897
7333
|
}
|
|
7334
|
+
setStep("db-reader:install-scripts");
|
|
6898
7335
|
const installError = await installScripts();
|
|
6899
7336
|
if (installError) {
|
|
6900
7337
|
note(`Prod DB access not set up — ${installError}. Re-run once the catalog is reachable.`);
|
|
@@ -6902,6 +7339,7 @@ async function setupDbReader({ workspace, note }) {
|
|
|
6902
7339
|
}
|
|
6903
7340
|
M.success(`Installed ${DB_READER_SCRIPTS.join(" and ")} in ${dbReaderBinDir()}`);
|
|
6904
7341
|
if (!(process.env.PATH ?? "").split(delimiter).includes(dbReaderBinDir())) M.warn(`${dbReaderBinDir()} is not on your PATH. Add ${import_picocolors.default.cyan("export PATH=\"$HOME/.local/bin:$PATH\"")} to ~/.zshrc.`);
|
|
7342
|
+
setStep("db-reader:verify");
|
|
6905
7343
|
const verify = runVerify$1();
|
|
6906
7344
|
if (!verify.ok) {
|
|
6907
7345
|
note(`Prod DB access not enabled — ${import_picocolors.default.cyan("pruff-db-verify")} failed. Run ${import_picocolors.default.cyan(`${join(dbReaderBinDir(), "pruff-db-verify")} ${DB_READER_VERIFY_DATABASE}`)} and share the output with the infra owner.`);
|
|
@@ -6909,6 +7347,7 @@ async function setupDbReader({ workspace, note }) {
|
|
|
6909
7347
|
return;
|
|
6910
7348
|
}
|
|
6911
7349
|
M.success("Read-only access verified");
|
|
7350
|
+
setStep("db-reader:settings");
|
|
6912
7351
|
if (mergeAllowlist(workspace) === "invalid") note(`Add ${import_picocolors.default.cyan(DB_READER_ALLOW_ENTRY)} to permissions.allow in ${join(workspace, ".claude", "settings.json")} by hand — the file is not valid JSON.`);
|
|
6913
7352
|
const hooks = await installHooks(["prod-db-guard"]);
|
|
6914
7353
|
for (const failure of hooks.failed) note(`Hook ${failure.name} not installed: ${failure.reason}`);
|
|
@@ -7041,9 +7480,9 @@ function renderBlockLogoFrame(lines, palette, pos, band) {
|
|
|
7041
7480
|
return out + RESET;
|
|
7042
7481
|
}).join("\n");
|
|
7043
7482
|
}
|
|
7044
|
-
async function showBlockLogo(lines, letterCuts, { slow = false, palette = BLUES } = {}) {
|
|
7483
|
+
async function showBlockLogo(lines, letterCuts, { slow = false, palette = BLUES, animate = true } = {}) {
|
|
7045
7484
|
console.log();
|
|
7046
|
-
if (!canAnimate()) {
|
|
7485
|
+
if (!animate || !canAnimate()) {
|
|
7047
7486
|
printStaticBlockLogo(lines, palette);
|
|
7048
7487
|
return;
|
|
7049
7488
|
}
|
|
@@ -7815,6 +8254,7 @@ function catalogInstallNames(rawIndex) {
|
|
|
7815
8254
|
}
|
|
7816
8255
|
const issues = [];
|
|
7817
8256
|
function note(issue) {
|
|
8257
|
+
recordFailure(issue);
|
|
7818
8258
|
issues.push(issue);
|
|
7819
8259
|
}
|
|
7820
8260
|
async function runOnboarding(args) {
|
|
@@ -7829,6 +8269,7 @@ async function runOnboarding(args) {
|
|
|
7829
8269
|
Ie(import_picocolors.default.bold("Pruff onboarding"));
|
|
7830
8270
|
issues.length = 0;
|
|
7831
8271
|
if (args.includes("--db-access")) {
|
|
8272
|
+
setStep("db-access");
|
|
7832
8273
|
await setupDbReader({
|
|
7833
8274
|
workspace: await resolveWorkspace(args),
|
|
7834
8275
|
note
|
|
@@ -7837,28 +8278,51 @@ async function runOnboarding(args) {
|
|
|
7837
8278
|
Se(issues.length === 0 ? import_picocolors.default.green("Done.") : import_picocolors.default.yellow("Prod DB access is not complete — fix the items above and re-run."));
|
|
7838
8279
|
return;
|
|
7839
8280
|
}
|
|
8281
|
+
setStep("preflight");
|
|
7840
8282
|
if (!await preflight()) {
|
|
8283
|
+
recordFailure(lastLoggedError() || "Preflight failed");
|
|
7841
8284
|
Se(import_picocolors.default.yellow("Onboarding stopped — fix the items above and re-run."));
|
|
7842
8285
|
return;
|
|
7843
8286
|
}
|
|
8287
|
+
setStep("install-claude-code");
|
|
7844
8288
|
installClaudeCode();
|
|
8289
|
+
setStep("obsidian");
|
|
7845
8290
|
ensureObsidian();
|
|
8291
|
+
setStep("bun");
|
|
7846
8292
|
ensureBun();
|
|
8293
|
+
setStep("workspace");
|
|
7847
8294
|
const workspace = await resolveWorkspace(args);
|
|
7848
8295
|
mkdirSync(workspace, { recursive: true });
|
|
7849
8296
|
M.success(`Workspace ${import_picocolors.default.cyan(workspace)}`);
|
|
8297
|
+
setStep("clone-knowledge");
|
|
7850
8298
|
cloneKnowledge(workspace);
|
|
7851
|
-
for (const repo of MAIN_REPOS)
|
|
7852
|
-
|
|
8299
|
+
for (const repo of MAIN_REPOS) {
|
|
8300
|
+
setStep(`clone:${repo.name}`);
|
|
8301
|
+
cloneRepo(repo, workspace);
|
|
8302
|
+
}
|
|
8303
|
+
for (const repo of MAIN_REPOS) {
|
|
8304
|
+
setStep(`install-deps:${repo.name}`);
|
|
8305
|
+
installDeps(repo, workspace);
|
|
8306
|
+
}
|
|
8307
|
+
setStep("workspace-config");
|
|
7853
8308
|
writeWorkspaceClaudeMd(workspace);
|
|
7854
8309
|
writeHarnessConfig(workspace);
|
|
8310
|
+
setStep("install-catalog");
|
|
7855
8311
|
await installCatalog(workspace);
|
|
8312
|
+
setStep("install-harness");
|
|
7856
8313
|
await installHarness(workspace);
|
|
7857
|
-
if (await shouldSetupDatabase(args))
|
|
7858
|
-
|
|
7859
|
-
workspace,
|
|
7860
|
-
|
|
7861
|
-
|
|
8314
|
+
if (await shouldSetupDatabase(args)) {
|
|
8315
|
+
setStep("local-database");
|
|
8316
|
+
await setupLocalEnvironment(join(workspace, "Pruff-server"));
|
|
8317
|
+
}
|
|
8318
|
+
if (await shouldSetupDbAccess(args)) {
|
|
8319
|
+
setStep("db-access");
|
|
8320
|
+
await setupDbReader({
|
|
8321
|
+
workspace,
|
|
8322
|
+
note
|
|
8323
|
+
});
|
|
8324
|
+
}
|
|
8325
|
+
setStep("guidance");
|
|
7862
8326
|
await showGuidance(workspace);
|
|
7863
8327
|
showTeamCredentialsAsk(workspace);
|
|
7864
8328
|
showIssues();
|
|
@@ -7867,6 +8331,7 @@ async function runOnboarding(args) {
|
|
|
7867
8331
|
else Se(`${import_picocolors.default.yellow(`Finished with ${issues.length} issue${issues.length === 1 ? "" : "s"}.`)} Fix them and re-run — every step is idempotent.`);
|
|
7868
8332
|
}
|
|
7869
8333
|
async function preflight() {
|
|
8334
|
+
setStep("preflight:tools");
|
|
7870
8335
|
const missing = [
|
|
7871
8336
|
"git",
|
|
7872
8337
|
"node",
|
|
@@ -7877,9 +8342,13 @@ async function preflight() {
|
|
|
7877
8342
|
return false;
|
|
7878
8343
|
}
|
|
7879
8344
|
M.success("git · node · npm present");
|
|
8345
|
+
setStep("preflight:gh");
|
|
7880
8346
|
if (!ensureGh()) return false;
|
|
8347
|
+
setStep("preflight:gh-auth");
|
|
7881
8348
|
if (!await ensureGitHubAuth()) return false;
|
|
8349
|
+
setStep("preflight:git-credentials");
|
|
7882
8350
|
if (!ensureGitCredentialHelper()) return false;
|
|
8351
|
+
setStep("preflight:packages-scope");
|
|
7883
8352
|
ensurePackagesScope();
|
|
7884
8353
|
return true;
|
|
7885
8354
|
}
|
|
@@ -9609,7 +10078,10 @@ const LETTER_CUTS = [
|
|
|
9609
10078
|
41
|
|
9610
10079
|
];
|
|
9611
10080
|
async function showLogo(slow = false) {
|
|
9612
|
-
await showBlockLogo(LOGO_LINES, LETTER_CUTS, {
|
|
10081
|
+
await showBlockLogo(LOGO_LINES, LETTER_CUTS, {
|
|
10082
|
+
slow,
|
|
10083
|
+
animate: slow
|
|
10084
|
+
});
|
|
9613
10085
|
}
|
|
9614
10086
|
const BUILDING_LINES = [
|
|
9615
10087
|
"██████╗ ██╗ ██╗██╗██╗ ██████╗ ██╗███╗ ██╗ ██████╗ ",
|
|
@@ -9757,6 +10229,12 @@ ${BOLD}Onboarding Options:${RESET}
|
|
|
9757
10229
|
--no-db Skip the local database step
|
|
9758
10230
|
--db-access Only set up read-only prod database access (needs AWS credentials)
|
|
9759
10231
|
|
|
10232
|
+
${BOLD}Troubleshooting:${RESET}
|
|
10233
|
+
doctor Print a copy-pasteable diagnostic block (versions, GitHub auth,
|
|
10234
|
+
catalog reachability, install target). Exit 1 if gh is not logged
|
|
10235
|
+
in or the catalog does not resolve. The same block is printed when
|
|
10236
|
+
add, update, rules add, agents add or onboarding fails.
|
|
10237
|
+
|
|
9760
10238
|
${BOLD}Updates:${RESET}
|
|
9761
10239
|
update [skills...] Update skills to latest versions (alias: upgrade)
|
|
9762
10240
|
Also syncs ALL installed subagents with the catalog,
|
|
@@ -9948,8 +10426,7 @@ async function main() {
|
|
|
9948
10426
|
case "add": {
|
|
9949
10427
|
if (!inAgent) await showLogo();
|
|
9950
10428
|
const { source: addSource, options: addOpts } = parseAddOptions(restArgs);
|
|
9951
|
-
|
|
9952
|
-
break;
|
|
10429
|
+
return withDiagnostics("add", restArgs, () => runAdd(addSource, addOpts));
|
|
9953
10430
|
}
|
|
9954
10431
|
case "use": {
|
|
9955
10432
|
const { source: useSource, options: useOptions, errors: useErrors } = parseUseOptions(restArgs);
|
|
@@ -9959,13 +10436,11 @@ async function main() {
|
|
|
9959
10436
|
case "agents":
|
|
9960
10437
|
case "agent":
|
|
9961
10438
|
if (!inAgent) await showLogo();
|
|
9962
|
-
|
|
9963
|
-
break;
|
|
10439
|
+
return withDiagnostics("agents", restArgs, () => runAgents(restArgs));
|
|
9964
10440
|
case "rules":
|
|
9965
10441
|
case "rule":
|
|
9966
10442
|
if (!inAgent) await showLogo();
|
|
9967
|
-
|
|
9968
|
-
break;
|
|
10443
|
+
return withDiagnostics("rules", restArgs, () => runRules(restArgs));
|
|
9969
10444
|
case "hooks":
|
|
9970
10445
|
case "hook":
|
|
9971
10446
|
if (!inAgent) await showLogo();
|
|
@@ -9985,8 +10460,8 @@ async function main() {
|
|
|
9985
10460
|
await showLogo(true);
|
|
9986
10461
|
await showTagline(true);
|
|
9987
10462
|
}
|
|
9988
|
-
|
|
9989
|
-
|
|
10463
|
+
return withDiagnostics("onboarding", restArgs, () => runOnboarding(restArgs));
|
|
10464
|
+
case "doctor": return runDoctor();
|
|
9990
10465
|
case "remove":
|
|
9991
10466
|
case "rm":
|
|
9992
10467
|
case "r":
|
|
@@ -10010,9 +10485,7 @@ async function main() {
|
|
|
10010
10485
|
break;
|
|
10011
10486
|
case "check":
|
|
10012
10487
|
case "update":
|
|
10013
|
-
case "upgrade":
|
|
10014
|
-
await runUpdate(restArgs);
|
|
10015
|
-
break;
|
|
10488
|
+
case "upgrade": return withDiagnostics("update", restArgs, () => runUpdate(restArgs));
|
|
10016
10489
|
case "--help":
|
|
10017
10490
|
case "-h":
|
|
10018
10491
|
showHelp();
|