bf-skills 1.0.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.
Files changed (2) hide show
  1. package/dist/cli.mjs +77 -58
  2. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -80,6 +80,7 @@ function copyDir(src, dest) {
80
80
  fs2.mkdirSync(dest, { recursive: true });
81
81
  const entries = fs2.readdirSync(src, { withFileTypes: true });
82
82
  for (const entry of entries) {
83
+ if (entry.name === ".git" || entry.name === ".DS_Store") continue;
83
84
  const srcPath = path2.join(src, entry.name);
84
85
  const destPath = path2.join(dest, entry.name);
85
86
  if (entry.isDirectory()) {
@@ -89,46 +90,70 @@ function copyDir(src, dest) {
89
90
  }
90
91
  }
91
92
  }
92
- function installSkill(skill, agent, global) {
93
- const basePath = global ? agent.globalPath : path2.join(process.cwd(), agent.projectPath);
94
- const destDir = path2.join(basePath, skill.name);
95
- fs2.mkdirSync(basePath, { recursive: true });
96
- if (fs2.existsSync(destDir)) {
97
- fs2.rmSync(destDir, { recursive: true, force: true });
93
+ function removePath(p4) {
94
+ if (fs2.existsSync(p4)) {
95
+ fs2.rmSync(p4, { recursive: true, force: true });
98
96
  }
99
- copyDir(skill.dir, destDir);
100
- return destDir;
101
97
  }
102
- function removeSkill(name, agent, global) {
103
- const basePath = global ? agent.globalPath : path2.join(process.cwd(), agent.projectPath);
104
- const destDir = path2.join(basePath, name);
105
- if (!fs2.existsSync(destDir)) return false;
106
- fs2.rmSync(destDir, { recursive: true, force: true });
107
- return true;
98
+ function installSkill(skillDir, skillName, agents, isGlobal) {
99
+ if (agents.length === 0) throw new Error("No install targets selected");
100
+ const resolvePath = (agent) => isGlobal ? path2.join(agent.globalPath, skillName) : path2.join(process.cwd(), agent.projectPath, skillName);
101
+ const primaryDest = resolvePath(agents[0]);
102
+ fs2.mkdirSync(path2.dirname(primaryDest), { recursive: true });
103
+ removePath(primaryDest);
104
+ copyDir(skillDir, primaryDest);
105
+ const installed = [primaryDest];
106
+ for (const agent of agents.slice(1)) {
107
+ const dest = resolvePath(agent);
108
+ fs2.mkdirSync(path2.dirname(dest), { recursive: true });
109
+ removePath(dest);
110
+ const rel = path2.relative(path2.dirname(dest), primaryDest);
111
+ fs2.symlinkSync(rel, dest);
112
+ installed.push(dest);
113
+ }
114
+ return installed;
108
115
  }
109
- function listInstalledSkills(agent, global) {
110
- const basePath = global ? agent.globalPath : path2.join(process.cwd(), agent.projectPath);
116
+ function removeSkill(name, agents, isGlobal) {
117
+ const removed = [];
118
+ for (const agent of agents) {
119
+ const p4 = isGlobal ? path2.join(agent.globalPath, name) : path2.join(process.cwd(), agent.projectPath, name);
120
+ if (fs2.existsSync(p4)) {
121
+ fs2.rmSync(p4, { recursive: true, force: true });
122
+ removed.push(p4);
123
+ }
124
+ }
125
+ return removed;
126
+ }
127
+ function listInstalledSkills(agent, isGlobal) {
128
+ const basePath = isGlobal ? agent.globalPath : path2.join(process.cwd(), agent.projectPath);
111
129
  if (!fs2.existsSync(basePath)) return [];
112
- return fs2.readdirSync(basePath, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
130
+ return fs2.readdirSync(basePath, { withFileTypes: true }).filter((e) => e.isDirectory() || e.isSymbolicLink()).map((e) => e.name);
113
131
  }
114
132
 
115
133
  // src/lib/agents.ts
116
134
  import os from "os";
117
135
  import path3 from "path";
118
136
  var AGENTS = [
137
+ {
138
+ id: "general",
139
+ label: ".agents/skills (general agents)",
140
+ projectPath: ".agents/skills",
141
+ globalPath: path3.join(os.homedir(), ".agents", "skills")
142
+ },
119
143
  {
120
144
  id: "claude-code",
121
- label: "Claude Code",
145
+ label: ".claude/skills (Claude Code)",
122
146
  projectPath: ".claude/skills",
123
147
  globalPath: path3.join(os.homedir(), ".claude", "skills")
124
148
  },
125
149
  {
126
- id: "general",
127
- label: "General agents",
128
- projectPath: ".agents/skills",
129
- globalPath: path3.join(os.homedir(), ".agents", "skills")
150
+ id: "root",
151
+ label: "skills/ (root folder)",
152
+ projectPath: "skills",
153
+ globalPath: path3.join(os.homedir(), "skills")
130
154
  }
131
155
  ];
156
+ var DEFAULT_AGENT_IDS = ["general", "claude-code"];
132
157
  function getAgentById(id) {
133
158
  return AGENTS.find((a) => a.id === id);
134
159
  }
@@ -231,11 +256,10 @@ async function runInstall(source, opts) {
231
256
  selectedSkills = allSkills;
232
257
  } else {
233
258
  const selected = await p.multiselect({
234
- message: "Select skills to install (space to toggle, enter to confirm)",
259
+ message: "Select skills to install (\u2191\u2193 move Space toggle Enter confirm)",
235
260
  options: allSkills.map((s) => ({
236
261
  value: s.name,
237
- label: pc.bold(s.name.padEnd(30)) + pc.dim(s.description),
238
- hint: s.description
262
+ label: pc.bold(s.name.padEnd(32)) + pc.dim(s.description)
239
263
  })),
240
264
  required: true
241
265
  });
@@ -248,50 +272,50 @@ async function runInstall(source, opts) {
248
272
  }
249
273
  let targetAgents;
250
274
  if (opts.agent) {
251
- if (opts.agent === "both") {
275
+ if (opts.agent === "all") {
252
276
  targetAgents = [...AGENTS];
253
277
  } else {
254
278
  const found = getAgentById(opts.agent);
255
279
  if (!found) {
256
- p.log.error(`Unknown agent "${opts.agent}". Use: claude-code, general, or both`);
280
+ p.log.error(`Unknown agent "${opts.agent}". Use: general, claude-code, root, or all`);
257
281
  cleanup(tmpDir);
258
282
  process.exit(1);
259
283
  }
260
284
  targetAgents = [found];
261
285
  }
262
286
  } else if (opts.yes) {
263
- targetAgents = [AGENTS[0]];
287
+ targetAgents = AGENTS.filter((a) => DEFAULT_AGENT_IDS.includes(a.id));
264
288
  } else {
265
- const agentChoice = await p.select({
266
- message: "Install to:",
267
- options: [
268
- { value: "claude-code", label: `${pc.bold("Claude Code")} ${pc.dim("(~/.claude/skills/)")}` },
269
- { value: "general", label: `${pc.bold("General agents")} ${pc.dim("(~/.agents/skills/)")}` },
270
- { value: "both", label: `${pc.bold("Both")}` }
271
- ]
289
+ const chosen = await p.multiselect({
290
+ message: "Install to: (\u2191\u2193 move Space toggle Enter confirm)",
291
+ options: AGENTS.map((a) => ({
292
+ value: a.id,
293
+ label: a.label,
294
+ hint: opts.global ? a.globalPath : path4.join(process.cwd(), a.projectPath)
295
+ })),
296
+ initialValues: DEFAULT_AGENT_IDS,
297
+ required: true
272
298
  });
273
- if (p.isCancel(agentChoice)) {
299
+ if (p.isCancel(chosen)) {
274
300
  p.cancel("Installation cancelled.");
275
301
  cleanup(tmpDir);
276
302
  process.exit(0);
277
303
  }
278
- targetAgents = agentChoice === "both" ? [...AGENTS] : [getAgentById(agentChoice)];
304
+ targetAgents = AGENTS.filter((a) => chosen.includes(a.id));
279
305
  }
280
306
  const spinner2 = p.spinner();
281
307
  spinner2.start(`Installing ${selectedSkills.length} skill${selectedSkills.length === 1 ? "" : "s"}...`);
282
- const installed = [];
308
+ const results = [];
283
309
  for (const skill of selectedSkills) {
284
- for (const agent of targetAgents) {
285
- const dest = installSkill(skill, agent, opts.global);
286
- installed.push(`${skill.name} \u2192 ${dest}`);
287
- }
310
+ const paths = installSkill(skill.dir, skill.name, targetAgents, opts.global);
311
+ results.push(...paths.map((p4, i) => `${skill.name} \u2192 ${p4}${i > 0 ? pc.dim(" (symlink)") : ""}`));
288
312
  }
289
313
  spinner2.stop(`Done \u2014 ${selectedSkills.length} skill${selectedSkills.length === 1 ? "" : "s"} installed`);
290
- for (const line of installed) {
314
+ for (const line of results) {
291
315
  p.log.step(line);
292
316
  }
293
317
  p.outro(
294
- `${pc.dim("$ npx bf-skills list")} ${pc.dim("$ npx bf-skills remove <name>")}`
318
+ `${pc.dim("$ npx bf-skills list")} ${pc.dim("$ npx bf-skills remove <name>")}`
295
319
  );
296
320
  cleanup(tmpDir);
297
321
  showFooter();
@@ -311,11 +335,11 @@ function runList(opts) {
311
335
  let total = 0;
312
336
  for (const agent of AGENTS) {
313
337
  const skills = listInstalledSkills(agent, opts.global);
314
- const pathLabel = opts.global ? agent.globalPath : `./${agent.projectPath}`;
338
+ const basePath = opts.global ? agent.globalPath : `./${agent.projectPath}`;
315
339
  if (skills.length === 0) {
316
- p2.log.info(`${pc2.bold(agent.label)} ${pc2.dim(pathLabel)} \u2014 no skills installed`);
340
+ p2.log.info(`${pc2.bold(agent.label)} ${pc2.dim(basePath)} \u2014 no skills installed`);
317
341
  } else {
318
- p2.log.step(`${pc2.bold(agent.label)} ${pc2.dim(pathLabel)}`);
342
+ p2.log.step(`${pc2.bold(agent.label)} ${pc2.dim(basePath)}`);
319
343
  for (const name of skills) {
320
344
  console.log(` ${pc2.dim("\u2022")} ${name}`);
321
345
  }
@@ -332,19 +356,14 @@ import pc3 from "picocolors";
332
356
  function runRemove(name, opts) {
333
357
  showLogo();
334
358
  p3.intro(`${pc3.bold("bf-skills remove")}`);
335
- let removed = false;
336
- for (const agent of AGENTS) {
337
- const didRemove = removeSkill(name, agent, opts.global);
338
- if (didRemove) {
339
- const pathLabel = opts.global ? agent.globalPath : `./${agent.projectPath}`;
340
- p3.log.step(`Removed ${pc3.bold(name)} from ${pc3.dim(pathLabel)}`);
341
- removed = true;
342
- }
343
- }
344
- if (!removed) {
345
- p3.log.error(`Skill "${name}" not found in any agent skills directory.`);
359
+ const removed = removeSkill(name, AGENTS, opts.global);
360
+ if (removed.length === 0) {
361
+ p3.log.error(`Skill "${name}" not found in any target directory.`);
346
362
  p3.outro("Nothing removed.");
347
363
  } else {
364
+ for (const p_ of removed) {
365
+ p3.log.step(`Removed ${pc3.bold(name)} from ${pc3.dim(p_)}`);
366
+ }
348
367
  p3.outro(`Done \u2014 ${pc3.bold(name)} removed`);
349
368
  }
350
369
  showFooter();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bf-skills",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "BF Skills installer — Building the Future of Business with AI",
5
5
  "type": "module",
6
6
  "engines": {