@devrev-computer/skills 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/install.mjs +435 -93
- package/package.json +5 -1
package/bin/install.mjs
CHANGED
|
@@ -1,158 +1,500 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { readdir, cp, mkdir, stat, readFile } from "node:fs/promises";
|
|
3
|
+
import { readdir, cp, mkdir, stat, readFile, rm } from "node:fs/promises";
|
|
4
4
|
import { join, dirname } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { homedir } from "node:os";
|
|
7
|
+
import { existsSync } from "node:fs";
|
|
6
8
|
import { parseArgs } from "node:util";
|
|
9
|
+
import * as p from "@clack/prompts";
|
|
10
|
+
import pc from "picocolors";
|
|
7
11
|
|
|
8
12
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
13
|
const skillsSource = join(__dirname, "..", "skills");
|
|
10
14
|
|
|
15
|
+
// ── Skill catalog ──────────────────────────────────────────────
|
|
16
|
+
|
|
11
17
|
const SKILLS = [
|
|
12
|
-
"account-evaluation",
|
|
13
|
-
"account-research",
|
|
14
|
-
"create-workflow-template",
|
|
15
|
-
"customer-brief",
|
|
16
|
-
"deal-review-meddpicc",
|
|
17
|
-
"next-step-for-opportunity",
|
|
18
|
-
"opportunity-feature-prioritizer",
|
|
19
|
-
"sales-call-plan-coach",
|
|
20
|
-
"sales-context",
|
|
21
|
-
"sales-search-and-lookup",
|
|
22
|
-
"skill-creator",
|
|
23
|
-
"trace-diagnosis",
|
|
18
|
+
{ name: "account-evaluation", category: "sales", desc: "Account health report — pipeline, engagement, and external data" },
|
|
19
|
+
{ name: "account-research", category: "sales", desc: "Company/person research — web, DevRev, and enrichment layers" },
|
|
20
|
+
{ name: "create-workflow-template", category: "platform", desc: "Generate DevRev workflow template JSON from natural language" },
|
|
21
|
+
{ name: "customer-brief", category: "sales", desc: "Pre-meeting briefing with internal and external context" },
|
|
22
|
+
{ name: "deal-review-meddpicc", category: "sales", desc: "Full MEDDPICC deal assessment with evidence-tagged scoring" },
|
|
23
|
+
{ name: "next-step-for-opportunity", category: "sales", desc: "Extract planned actions from meetings, emails, discussions" },
|
|
24
|
+
{ name: "opportunity-feature-prioritizer", category: "product", desc: "Prioritize features based on open opportunity pipeline" },
|
|
25
|
+
{ name: "sales-call-plan-coach", category: "sales", desc: "Interactive call plan coaching — Franklin Covey methodology" },
|
|
26
|
+
{ name: "sales-context", category: "sales", desc: "Always-active skill for consistent sales terminology" },
|
|
27
|
+
{ name: "sales-search-and-lookup", category: "sales", desc: "Query pipeline, forecast, accounts via natural language" },
|
|
28
|
+
{ name: "skill-creator", category: "meta", desc: "Create, modify, evaluate, and benchmark skills" },
|
|
29
|
+
{ name: "trace-diagnosis", category: "eng", desc: "Debug Computer agent traces and auto-create tickets" },
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
const SKILL_NAMES = SKILLS.map((s) => s.name);
|
|
33
|
+
|
|
34
|
+
// ── Logo ───────────────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
const GRAYS = [
|
|
37
|
+
"\x1b[38;5;251m",
|
|
38
|
+
"\x1b[38;5;248m",
|
|
39
|
+
"\x1b[38;5;245m",
|
|
40
|
+
"\x1b[38;5;242m",
|
|
41
|
+
"\x1b[38;5;239m",
|
|
42
|
+
];
|
|
43
|
+
const RESET = "\x1b[0m";
|
|
44
|
+
|
|
45
|
+
const LOGO = [
|
|
46
|
+
" ██████╗ ██████╗ ███╗ ███╗██████╗ ██╗ ██╗████████╗███████╗██████╗ ",
|
|
47
|
+
"██╔════╝██╔═══██╗████╗ ████║██╔══██╗██║ ██║╚══██╔══╝██╔════╝██╔══██╗",
|
|
48
|
+
"██║ ██║ ██║██╔████╔██║██████╔╝██║ ██║ ██║ █████╗ ██████╔╝",
|
|
49
|
+
"██║ ██║ ██║██║╚██╔╝██║██╔═══╝ ██║ ██║ ██║ ██╔══╝ ██╔══██╗",
|
|
50
|
+
"╚██████╗╚██████╔╝██║ ╚═╝ ██║██║ ╚██████╔╝ ██║ ███████╗██║ ██║",
|
|
24
51
|
];
|
|
25
52
|
|
|
53
|
+
function showLogo() {
|
|
54
|
+
console.log();
|
|
55
|
+
LOGO.forEach((line, i) => {
|
|
56
|
+
console.log(` ${GRAYS[i]}${line}${RESET}`);
|
|
57
|
+
});
|
|
58
|
+
console.log();
|
|
59
|
+
console.log(
|
|
60
|
+
` ${pc.dim("DevRev Computer Skills")} ${pc.dim("v")}${pc.dim(getVersion())}`
|
|
61
|
+
);
|
|
62
|
+
console.log();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function getVersion() {
|
|
66
|
+
try {
|
|
67
|
+
const pkg = JSON.parse(
|
|
68
|
+
require("node:fs").readFileSync(join(__dirname, "..", "package.json"), "utf-8")
|
|
69
|
+
);
|
|
70
|
+
return pkg.version;
|
|
71
|
+
} catch {
|
|
72
|
+
return "1.0.0";
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ── Helpers ────────────────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
function shortenPath(fullPath) {
|
|
79
|
+
const home = homedir();
|
|
80
|
+
if (fullPath.startsWith(home)) {
|
|
81
|
+
return "~" + fullPath.slice(home.length);
|
|
82
|
+
}
|
|
83
|
+
return fullPath;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function getGlobalSkillsDir() {
|
|
87
|
+
return join(homedir(), ".claude", "skills");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function getProjectSkillsDir(cwd) {
|
|
91
|
+
return join(cwd, ".claude", "skills");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function hasProjectClaude(cwd) {
|
|
95
|
+
return existsSync(join(cwd, ".claude")) || existsSync(join(cwd, "CLAUDE.md"));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function getInstalledSkills(dir) {
|
|
99
|
+
try {
|
|
100
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
101
|
+
return entries
|
|
102
|
+
.filter((e) => e.isDirectory() && SKILL_NAMES.includes(e.name))
|
|
103
|
+
.map((e) => e.name);
|
|
104
|
+
} catch {
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ── Non-interactive (flags) ────────────────────────────────────
|
|
110
|
+
|
|
26
111
|
function usage() {
|
|
27
112
|
console.log(`
|
|
28
|
-
Usage: computer-skills [options]
|
|
113
|
+
${pc.bold("Usage:")} computer-skills ${pc.dim("[command] [options]")}
|
|
29
114
|
|
|
30
|
-
|
|
115
|
+
${pc.bold("Commands:")}
|
|
116
|
+
${pc.cyan("add")} Install skills ${pc.dim("(default, interactive)")}
|
|
117
|
+
${pc.cyan("remove")} Uninstall skills
|
|
118
|
+
${pc.cyan("list")} List installed and available skills
|
|
31
119
|
|
|
32
|
-
Options:
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
--dir
|
|
36
|
-
--
|
|
37
|
-
--help
|
|
120
|
+
${pc.bold("Options:")}
|
|
121
|
+
${pc.dim("-s, --skill <name>")} Specific skill(s) ${pc.dim("(repeatable)")}
|
|
122
|
+
${pc.dim("-g, --global")} Install to ~/.claude/skills/
|
|
123
|
+
${pc.dim("-d, --dir <path>")} Target project directory
|
|
124
|
+
${pc.dim("-y, --yes")} Skip prompts, install all
|
|
125
|
+
${pc.dim("-h, --help")} Show this help
|
|
126
|
+
${pc.dim("-v, --version")} Show version
|
|
127
|
+
|
|
128
|
+
${pc.bold("Examples:")}
|
|
129
|
+
${pc.dim("$")} npx @devrev-computer/skills
|
|
130
|
+
${pc.dim("$")} npx @devrev-computer/skills add -g
|
|
131
|
+
${pc.dim("$")} npx @devrev-computer/skills add -s skill-creator -s trace-diagnosis
|
|
132
|
+
${pc.dim("$")} npx @devrev-computer/skills remove
|
|
133
|
+
${pc.dim("$")} npx @devrev-computer/skills list
|
|
38
134
|
`);
|
|
39
135
|
}
|
|
40
136
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
137
|
+
// ── List command ───────────────────────────────────────────────
|
|
138
|
+
|
|
139
|
+
async function runList(cwd) {
|
|
140
|
+
const globalDir = getGlobalSkillsDir();
|
|
141
|
+
const projectDir = getProjectSkillsDir(cwd);
|
|
142
|
+
const globalInstalled = await getInstalledSkills(globalDir);
|
|
143
|
+
const projectInstalled = await getInstalledSkills(projectDir);
|
|
144
|
+
|
|
145
|
+
console.log();
|
|
146
|
+
console.log(pc.bold(" Available skills:\n"));
|
|
147
|
+
|
|
148
|
+
const categories = { sales: "Sales", platform: "Platform", product: "Product", meta: "Meta", eng: "Engineering" };
|
|
149
|
+
|
|
150
|
+
for (const [cat, label] of Object.entries(categories)) {
|
|
151
|
+
const skills = SKILLS.filter((s) => s.category === cat);
|
|
152
|
+
if (skills.length === 0) continue;
|
|
153
|
+
|
|
154
|
+
console.log(` ${pc.dim("──")} ${pc.bold(label)} ${pc.dim("─".repeat(50))}`);
|
|
155
|
+
for (const skill of skills) {
|
|
156
|
+
const inGlobal = globalInstalled.includes(skill.name);
|
|
157
|
+
const inProject = projectInstalled.includes(skill.name);
|
|
158
|
+
let badge = "";
|
|
159
|
+
if (inGlobal && inProject) badge = pc.green(" [global + project]");
|
|
160
|
+
else if (inGlobal) badge = pc.cyan(" [global]");
|
|
161
|
+
else if (inProject) badge = pc.blue(" [project]");
|
|
162
|
+
|
|
163
|
+
console.log(` ${pc.cyan(skill.name)}${badge}`);
|
|
164
|
+
console.log(` ${pc.dim(skill.desc)}`);
|
|
53
165
|
}
|
|
166
|
+
console.log();
|
|
54
167
|
}
|
|
55
|
-
|
|
168
|
+
|
|
169
|
+
console.log(
|
|
170
|
+
` ${pc.dim(`${SKILLS.length} available`)}${globalInstalled.length ? pc.dim(` · ${globalInstalled.length} global`) : ""}${projectInstalled.length ? pc.dim(` · ${projectInstalled.length} project`) : ""}`
|
|
171
|
+
);
|
|
172
|
+
console.log();
|
|
56
173
|
}
|
|
57
174
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
175
|
+
// ── Interactive add ────────────────────────────────────────────
|
|
176
|
+
|
|
177
|
+
async function runAdd(cwd, flags) {
|
|
178
|
+
showLogo();
|
|
179
|
+
p.intro(pc.inverse(" Install Skills "));
|
|
180
|
+
|
|
181
|
+
// ── Step 1: Scope ──
|
|
182
|
+
|
|
183
|
+
let targetDir;
|
|
184
|
+
const isInProject = hasProjectClaude(cwd);
|
|
185
|
+
|
|
186
|
+
if (flags.global) {
|
|
187
|
+
targetDir = homedir();
|
|
188
|
+
} else if (flags.dir) {
|
|
189
|
+
targetDir = flags.dir;
|
|
190
|
+
} else if (flags.yes) {
|
|
191
|
+
targetDir = isInProject ? cwd : homedir();
|
|
192
|
+
} else {
|
|
193
|
+
const scopeOptions = [];
|
|
194
|
+
if (isInProject) {
|
|
195
|
+
scopeOptions.push({
|
|
196
|
+
value: "project",
|
|
197
|
+
label: "Project",
|
|
198
|
+
hint: shortenPath(getProjectSkillsDir(cwd)),
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
scopeOptions.push({
|
|
202
|
+
value: "global",
|
|
203
|
+
label: "Global",
|
|
204
|
+
hint: shortenPath(getGlobalSkillsDir()),
|
|
205
|
+
});
|
|
206
|
+
if (isInProject) {
|
|
207
|
+
scopeOptions.push({
|
|
208
|
+
value: "both",
|
|
209
|
+
label: "Both",
|
|
210
|
+
hint: "Project + Global",
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const scope = await p.select({
|
|
215
|
+
message: "Where do you want to install skills?",
|
|
216
|
+
options: scopeOptions,
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
if (p.isCancel(scope)) {
|
|
220
|
+
p.cancel("Installation cancelled.");
|
|
221
|
+
process.exit(0);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (scope === "global") {
|
|
225
|
+
targetDir = homedir();
|
|
226
|
+
} else if (scope === "both") {
|
|
227
|
+
// Install to both — we'll handle this after skill selection
|
|
228
|
+
targetDir = "__both__";
|
|
229
|
+
} else {
|
|
230
|
+
targetDir = cwd;
|
|
231
|
+
}
|
|
63
232
|
}
|
|
64
|
-
console.log(`\n ${SKILLS.length} skills total\n`);
|
|
65
|
-
}
|
|
66
233
|
|
|
67
|
-
|
|
68
|
-
const skillsDir = join(targetDir, ".claude", "skills");
|
|
69
|
-
await mkdir(skillsDir, { recursive: true });
|
|
234
|
+
// ── Step 2: Skill selection ──
|
|
70
235
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
236
|
+
let selectedSkills;
|
|
237
|
+
|
|
238
|
+
if (flags.skill && flags.skill.length > 0) {
|
|
239
|
+
const invalid = flags.skill.filter((s) => !SKILL_NAMES.includes(s));
|
|
240
|
+
if (invalid.length > 0) {
|
|
241
|
+
p.cancel(`Unknown skill(s): ${invalid.join(", ")}`);
|
|
242
|
+
process.exit(1);
|
|
243
|
+
}
|
|
244
|
+
selectedSkills = flags.skill;
|
|
245
|
+
} else if (flags.yes) {
|
|
246
|
+
selectedSkills = SKILL_NAMES;
|
|
247
|
+
} else {
|
|
248
|
+
const alreadyInstalled = targetDir === "__both__"
|
|
249
|
+
? await getInstalledSkills(getProjectSkillsDir(cwd))
|
|
250
|
+
: await getInstalledSkills(
|
|
251
|
+
targetDir === homedir()
|
|
252
|
+
? getGlobalSkillsDir()
|
|
253
|
+
: getProjectSkillsDir(targetDir)
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
const selected = await p.multiselect({
|
|
257
|
+
message: "Which skills do you want to install?",
|
|
258
|
+
options: SKILLS.map((s) => ({
|
|
259
|
+
value: s.name,
|
|
260
|
+
label: s.name,
|
|
261
|
+
hint: alreadyInstalled.includes(s.name)
|
|
262
|
+
? `${s.desc} ${pc.yellow("(installed, will update)")}`
|
|
263
|
+
: s.desc,
|
|
264
|
+
})),
|
|
265
|
+
required: true,
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
if (p.isCancel(selected)) {
|
|
269
|
+
p.cancel("Installation cancelled.");
|
|
270
|
+
process.exit(0);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
selectedSkills = selected;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// ── Step 3: Confirmation ──
|
|
277
|
+
|
|
278
|
+
const targets =
|
|
279
|
+
targetDir === "__both__"
|
|
280
|
+
? [getProjectSkillsDir(cwd), getGlobalSkillsDir()]
|
|
281
|
+
: [
|
|
282
|
+
targetDir === homedir()
|
|
283
|
+
? getGlobalSkillsDir()
|
|
284
|
+
: getProjectSkillsDir(targetDir),
|
|
285
|
+
];
|
|
286
|
+
|
|
287
|
+
if (!flags.yes) {
|
|
288
|
+
const summaryLines = [
|
|
289
|
+
`${pc.bold("Skills:")} ${selectedSkills.length === SKILL_NAMES.length ? "All (" + SKILL_NAMES.length + ")" : selectedSkills.join(", ")}`,
|
|
290
|
+
`${pc.bold("Target:")} ${targets.map(shortenPath).join(" + ")}`,
|
|
291
|
+
];
|
|
292
|
+
|
|
293
|
+
p.note(summaryLines.join("\n"), "Installation Summary");
|
|
294
|
+
|
|
295
|
+
const confirmed = await p.confirm({
|
|
296
|
+
message: "Proceed with installation?",
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
if (p.isCancel(confirmed) || !confirmed) {
|
|
300
|
+
p.cancel("Installation cancelled.");
|
|
301
|
+
process.exit(0);
|
|
302
|
+
}
|
|
77
303
|
}
|
|
78
304
|
|
|
79
|
-
|
|
80
|
-
let skipped = 0;
|
|
305
|
+
// ── Step 4: Install ──
|
|
81
306
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const dest = join(skillsDir, skill);
|
|
307
|
+
const s = p.spinner();
|
|
308
|
+
s.start(`Installing ${selectedSkills.length} skill(s)`);
|
|
85
309
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
310
|
+
let totalInstalled = 0;
|
|
311
|
+
for (const dest of targets) {
|
|
312
|
+
await mkdir(dest, { recursive: true });
|
|
313
|
+
for (const skill of selectedSkills) {
|
|
314
|
+
const src = join(skillsSource, skill);
|
|
315
|
+
try {
|
|
316
|
+
await stat(src);
|
|
317
|
+
await cp(src, join(dest, skill), { recursive: true, force: true });
|
|
318
|
+
totalInstalled++;
|
|
319
|
+
} catch {
|
|
320
|
+
// skip missing
|
|
321
|
+
}
|
|
92
322
|
}
|
|
323
|
+
}
|
|
93
324
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
325
|
+
s.stop(`Installed ${selectedSkills.length} skill(s)`);
|
|
326
|
+
|
|
327
|
+
// ── Step 5: Summary ──
|
|
328
|
+
|
|
329
|
+
const installed = selectedSkills;
|
|
330
|
+
console.log();
|
|
331
|
+
for (const skill of installed) {
|
|
332
|
+
const info = SKILLS.find((s) => s.name === skill);
|
|
333
|
+
console.log(` ${pc.green("+")} ${pc.bold(skill)} ${pc.dim("—")} ${pc.dim(info?.desc || "")}`);
|
|
97
334
|
}
|
|
335
|
+
console.log();
|
|
98
336
|
|
|
99
|
-
|
|
100
|
-
|
|
337
|
+
for (const dest of targets) {
|
|
338
|
+
console.log(` ${pc.dim("Installed to")} ${pc.cyan(shortenPath(dest))}`);
|
|
339
|
+
}
|
|
340
|
+
console.log();
|
|
341
|
+
|
|
342
|
+
p.outro(
|
|
343
|
+
`${pc.green("Done!")} Skills are ready to use in Claude Code.`
|
|
101
344
|
);
|
|
102
345
|
}
|
|
103
346
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
347
|
+
// ── Interactive remove ─────────────────────────────────────────
|
|
348
|
+
|
|
349
|
+
async function runRemove(cwd, flags) {
|
|
350
|
+
showLogo();
|
|
351
|
+
p.intro(pc.inverse(" Remove Skills "));
|
|
352
|
+
|
|
353
|
+
// Find installed skills
|
|
354
|
+
const globalDir = getGlobalSkillsDir();
|
|
355
|
+
const projectDir = getProjectSkillsDir(cwd);
|
|
356
|
+
const globalInstalled = await getInstalledSkills(globalDir);
|
|
357
|
+
const projectInstalled = await getInstalledSkills(projectDir);
|
|
358
|
+
|
|
359
|
+
if (globalInstalled.length === 0 && projectInstalled.length === 0) {
|
|
360
|
+
p.log.warn("No skills are currently installed.");
|
|
361
|
+
p.outro("Nothing to remove.");
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Build options with location badges
|
|
366
|
+
const allInstalled = new Map();
|
|
367
|
+
for (const s of projectInstalled) {
|
|
368
|
+
allInstalled.set(s, { project: true, global: globalInstalled.includes(s) });
|
|
369
|
+
}
|
|
370
|
+
for (const s of globalInstalled) {
|
|
371
|
+
if (!allInstalled.has(s)) {
|
|
372
|
+
allInstalled.set(s, { project: false, global: true });
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
let toRemove;
|
|
377
|
+
if (flags.skill && flags.skill.length > 0) {
|
|
378
|
+
toRemove = flags.skill;
|
|
379
|
+
} else if (flags.yes) {
|
|
380
|
+
toRemove = [...allInstalled.keys()];
|
|
381
|
+
} else {
|
|
382
|
+
const selected = await p.multiselect({
|
|
383
|
+
message: "Which skills do you want to remove?",
|
|
384
|
+
options: [...allInstalled.entries()].map(([name, loc]) => {
|
|
385
|
+
const where = [loc.project && "project", loc.global && "global"]
|
|
386
|
+
.filter(Boolean)
|
|
387
|
+
.join(" + ");
|
|
388
|
+
return {
|
|
389
|
+
value: name,
|
|
390
|
+
label: name,
|
|
391
|
+
hint: where,
|
|
392
|
+
};
|
|
393
|
+
}),
|
|
394
|
+
required: true,
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
if (p.isCancel(selected)) {
|
|
398
|
+
p.cancel("Removal cancelled.");
|
|
399
|
+
process.exit(0);
|
|
400
|
+
}
|
|
401
|
+
toRemove = selected;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const confirmed = flags.yes || await (async () => {
|
|
405
|
+
const c = await p.confirm({
|
|
406
|
+
message: `Remove ${toRemove.length} skill(s)?`,
|
|
407
|
+
});
|
|
408
|
+
return !p.isCancel(c) && c;
|
|
409
|
+
})();
|
|
410
|
+
|
|
411
|
+
if (!confirmed) {
|
|
412
|
+
p.cancel("Removal cancelled.");
|
|
413
|
+
process.exit(0);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const s = p.spinner();
|
|
417
|
+
s.start(`Removing ${toRemove.length} skill(s)`);
|
|
108
418
|
|
|
109
419
|
let removed = 0;
|
|
110
420
|
for (const skill of toRemove) {
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
421
|
+
for (const dir of [projectDir, globalDir]) {
|
|
422
|
+
const dest = join(dir, skill);
|
|
423
|
+
try {
|
|
424
|
+
await stat(dest);
|
|
425
|
+
await rm(dest, { recursive: true, force: true });
|
|
426
|
+
removed++;
|
|
427
|
+
} catch {
|
|
428
|
+
// not there
|
|
429
|
+
}
|
|
119
430
|
}
|
|
120
431
|
}
|
|
121
|
-
|
|
432
|
+
|
|
433
|
+
s.stop(`Removed ${toRemove.length} skill(s)`);
|
|
434
|
+
|
|
435
|
+
console.log();
|
|
436
|
+
for (const skill of toRemove) {
|
|
437
|
+
console.log(` ${pc.red("-")} ${skill}`);
|
|
438
|
+
}
|
|
439
|
+
console.log();
|
|
440
|
+
|
|
441
|
+
p.outro(pc.green("Done!"));
|
|
122
442
|
}
|
|
123
443
|
|
|
444
|
+
// ── Main ───────────────────────────────────────────────────────
|
|
445
|
+
|
|
124
446
|
async function main() {
|
|
125
|
-
const { values } = parseArgs({
|
|
447
|
+
const { values, positionals } = parseArgs({
|
|
126
448
|
options: {
|
|
127
|
-
list: { type: "boolean", short: "l", default: false },
|
|
128
449
|
skill: { type: "string", short: "s", multiple: true, default: [] },
|
|
129
|
-
dir: { type: "string", short: "d"
|
|
130
|
-
|
|
450
|
+
dir: { type: "string", short: "d" },
|
|
451
|
+
global: { type: "boolean", short: "g", default: false },
|
|
452
|
+
yes: { type: "boolean", short: "y", default: false },
|
|
131
453
|
help: { type: "boolean", short: "h", default: false },
|
|
454
|
+
version: { type: "boolean", short: "v", default: false },
|
|
132
455
|
},
|
|
456
|
+
allowPositionals: true,
|
|
133
457
|
strict: true,
|
|
134
458
|
});
|
|
135
459
|
|
|
136
|
-
if (values.
|
|
137
|
-
|
|
460
|
+
if (values.version) {
|
|
461
|
+
console.log(getVersion());
|
|
138
462
|
process.exit(0);
|
|
139
463
|
}
|
|
140
464
|
|
|
141
|
-
if (values.
|
|
142
|
-
|
|
465
|
+
if (values.help) {
|
|
466
|
+
showLogo();
|
|
467
|
+
usage();
|
|
143
468
|
process.exit(0);
|
|
144
469
|
}
|
|
145
470
|
|
|
146
|
-
|
|
471
|
+
const command = positionals[0] || "add";
|
|
472
|
+
const cwd = values.dir || process.cwd();
|
|
147
473
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
474
|
+
switch (command) {
|
|
475
|
+
case "add":
|
|
476
|
+
case "install":
|
|
477
|
+
case "i":
|
|
478
|
+
await runAdd(cwd, values);
|
|
479
|
+
break;
|
|
480
|
+
case "remove":
|
|
481
|
+
case "rm":
|
|
482
|
+
case "uninstall":
|
|
483
|
+
await runRemove(cwd, values);
|
|
484
|
+
break;
|
|
485
|
+
case "list":
|
|
486
|
+
case "ls":
|
|
487
|
+
showLogo();
|
|
488
|
+
await runList(cwd);
|
|
489
|
+
break;
|
|
490
|
+
default:
|
|
491
|
+
// No command = interactive add
|
|
492
|
+
await runAdd(cwd, values);
|
|
493
|
+
break;
|
|
152
494
|
}
|
|
153
495
|
}
|
|
154
496
|
|
|
155
497
|
main().catch((err) => {
|
|
156
|
-
|
|
498
|
+
p.cancel(err.message);
|
|
157
499
|
process.exit(1);
|
|
158
500
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devrev-computer/skills",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "AI-powered skills for Claude Code — sales intelligence, deal management, workflow automation, and agent debugging, built on DevRev.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -29,5 +29,9 @@
|
|
|
29
29
|
"author": "DevRev",
|
|
30
30
|
"engines": {
|
|
31
31
|
"node": ">=18"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@clack/prompts": "^1.2.0",
|
|
35
|
+
"picocolors": "^1.1.1"
|
|
32
36
|
}
|
|
33
37
|
}
|