skillstogether 0.1.4 → 0.1.6

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 (3) hide show
  1. package/README.md +13 -4
  2. package/dist/index.js +269 -169
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -45,9 +45,7 @@ npx skillstogether add <organization-slug>
45
45
  # Install all skills without prompts
46
46
  npx skillstogether add <organization-slug> -y
47
47
 
48
- # Install a specific skill
49
- npx skillstogether add <organization-slug>/<skill-slug>
50
- # or
48
+ # Install a specific skill (skill slugs already include the org prefix)
51
49
  npx skillstogether add <organization-slug> --skill <skill-slug>
52
50
 
53
51
  # Install globally (in home directory)
@@ -60,6 +58,17 @@ npx skillstogether add <organization-slug> --force
60
58
  npx skillstogether add <organization-slug> --dir ./my-skills
61
59
  ```
62
60
 
61
+ ### Uninstalling Skills
62
+
63
+ ```bash
64
+ # Uninstall all skills from an organization
65
+ npx skillstogether uninstall <organization-slug>
66
+
67
+ # Uninstall a specific skill (skill slug already includes the org prefix)
68
+ npx skillstogether uninstall <organization-slug> --skill <skill-slug>
69
+
70
+ ```
71
+
63
72
  ### Options
64
73
 
65
74
  | Option | Alias | Description |
@@ -67,7 +76,7 @@ npx skillstogether add <organization-slug> --dir ./my-skills
67
76
  | `--dir <path>` | `-d` | Custom installation directory |
68
77
  | `--force` | `-f` | Overwrite existing skill files |
69
78
  | `--yes` | `-y` | Skip interactive prompts, install all |
70
- | `--skill <slug>` | — | Install a specific skill by slug |
79
+ | `--skill <slug>` | — | Install a specific skill by slug (org-prefixed) |
71
80
  | `--global` | — | Install globally (in home directory) |
72
81
 
73
82
  ---
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { Command as Command8 } from "commander";
4
+ import { Command as Command9 } from "commander";
5
5
 
6
6
  // src/commands/add.ts
7
7
  import * as p2 from "@clack/prompts";
@@ -278,6 +278,13 @@ function trackDownloads(events) {
278
278
  }).catch(() => {
279
279
  });
280
280
  }
281
+ async function submitSkillResult(organizationSlug, skillSlug, result) {
282
+ return apiPost(`/cli/skills/${encodeURIComponent(skillSlug)}/results`, {
283
+ organizationSlug,
284
+ skillSlug,
285
+ body: result
286
+ });
287
+ }
281
288
 
282
289
  // src/lib/errors.ts
283
290
  var DEFAULT_ERROR_MESSAGE = "Unknown error";
@@ -309,9 +316,11 @@ var skillSlugSchema = z.string().min(1, "Skill slug is required").max(100).regex
309
316
  var addTargetSchema = z.string().min(1, "Target is required").refine(
310
317
  (val) => {
311
318
  const parts = val.split("/");
312
- return parts.length >= 1 && parts.length <= 2;
319
+ return parts.length === 1;
313
320
  },
314
- { message: "Target must be 'org-slug' or 'org-slug/skill-slug'" }
321
+ {
322
+ message: "Target must be an organization slug (no '/'). Use --skill to target a specific skill."
323
+ }
315
324
  ).refine(
316
325
  (val) => {
317
326
  const parts = val.split("/");
@@ -330,20 +339,17 @@ var updateTargetSchema = z.string().max(200).optional().refine(
330
339
  var uninstallTargetSchema = z.string().min(1, "Target is required").refine(
331
340
  (val) => {
332
341
  const parts = val.split("/");
333
- return parts.length >= 1 && parts.length <= 2 && parts.every((part) => slugRegex.test(part));
342
+ return parts.length === 1 && parts.every((part) => slugRegex.test(part));
334
343
  },
335
- { message: "Target must be 'org-slug' or 'org-slug/skill-slug'" }
344
+ {
345
+ message: "Target must be an organization slug (no '/'). Use --skill to target a specific skill."
346
+ }
336
347
  );
337
- var agentSchema = z.string().min(1).max(50);
338
348
  var customDirSchema = z.string().min(1).max(4096);
339
349
  function parseAddTarget(target) {
340
350
  const parsed = addTargetSchema.parse(target);
341
- const parts = parsed.split("/");
342
- const organizationSlug = organizationSlugSchema.parse(parts[0]);
343
- return {
344
- organizationSlug,
345
- skillSlug: parts.length === 2 ? parts[1] : void 0
346
- };
351
+ const organizationSlug = organizationSlugSchema.parse(parsed);
352
+ return { organizationSlug };
347
353
  }
348
354
  function parseUpdateTarget(target) {
349
355
  if (target === void 0 || target === "") return {};
@@ -357,10 +363,8 @@ function parseUpdateTarget(target) {
357
363
  }
358
364
  function parseUninstallTarget(target) {
359
365
  const parsed = uninstallTargetSchema.parse(target);
360
- const parts = parsed.split("/");
361
366
  return {
362
- organizationSlug: parts[0],
363
- skillSlug: parts.length === 2 ? parts[1] : void 0
367
+ organizationSlug: parsed
364
368
  };
365
369
  }
366
370
  function isValidSlug(slug) {
@@ -1213,7 +1217,10 @@ function buildDownloadEvents(allResults, skills, scopeLabel) {
1213
1217
  }
1214
1218
  return events;
1215
1219
  }
1216
- var addCommand = new Command("add").description("Install skills from an organization").argument("<target>", "Organization slug or organization/skill slug").option("-d, --dir <path>", "Custom installation directory").option("-f, --force", "Overwrite existing skill files", false).option("-y, --yes", "Skip interactive prompts and install all skills", false).option("--skill <slug>", "Install a specific skill by slug").option("--global", "Install globally (in home directory)", false).option(
1220
+ var addCommand = new Command("add").description("Install skills from an organization").argument("<target>", "Organization slug").option("-d, --dir <path>", "Custom installation directory").option("-f, --force", "Overwrite existing skill files", false).option("-y, --yes", "Skip interactive prompts and install all skills", false).option(
1221
+ "--skill <slug>",
1222
+ "Install a specific skill by slug (already org-prefixed, e.g. acme-onboarding)"
1223
+ ).option("--global", "Install globally (in home directory)", false).option(
1217
1224
  "--target <agents>",
1218
1225
  "Comma-separated list of agent IDs to install to (e.g., claude-code,cursor)"
1219
1226
  ).action(
@@ -1237,20 +1244,22 @@ var addCommand = new Command("add").description("Install skills from an organiza
1237
1244
  p2.outro(pc3.red("Invalid format"));
1238
1245
  process.exit(1);
1239
1246
  }
1240
- if (parsed.skillSlug !== void 0) {
1241
- await installSingleSkill(
1242
- parsed.organizationSlug,
1243
- parsed.skillSlug,
1244
- options
1245
- );
1246
- } else if (options.skill) {
1247
+ if (options.skill) {
1247
1248
  const skillResult = skillSlugSchema.safeParse(options.skill);
1248
1249
  if (!skillResult.success) {
1249
- const msg = skillResult.error.errors?.[0]?.message ?? skillResult.error.message ?? "Invalid skill slug";
1250
+ const msg = skillResult.error.issues?.[0]?.message ?? skillResult.error.message ?? "Invalid skill slug";
1250
1251
  p2.log.error(msg);
1251
1252
  p2.outro(pc3.red("Invalid format"));
1252
1253
  process.exit(1);
1253
1254
  }
1255
+ const expectedPrefix = `${parsed.organizationSlug}-`;
1256
+ if (!skillResult.data.startsWith(expectedPrefix)) {
1257
+ p2.log.error(
1258
+ `Skill slug must be prefixed with the organization slug (e.g., ${pc3.cyan(`${expectedPrefix}my-skill`)})`
1259
+ );
1260
+ p2.outro(pc3.red("Invalid format"));
1261
+ process.exit(1);
1262
+ }
1254
1263
  await installSingleSkill(
1255
1264
  parsed.organizationSlug,
1256
1265
  skillResult.data,
@@ -2108,33 +2117,115 @@ var doctorCommand = new Command3("doctor").description("Diagnose and fix common
2108
2117
  }
2109
2118
  });
2110
2119
 
2111
- // src/commands/list.ts
2120
+ // src/commands/feedback.ts
2112
2121
  import * as p5 from "@clack/prompts";
2113
2122
  import { Command as Command4 } from "commander";
2114
2123
  import pc6 from "picocolors";
2124
+ var feedbackCommand = new Command4("feedback").description("Submit feedback for a skill after task completion").argument(
2125
+ "<skillSlug>",
2126
+ "Skill slug (already org-prefixed, e.g., 'acme-onboarding')"
2127
+ ).option("-s, --success", "Task completed successfully", true).option("-f, --failed", "Task failed").option("-q, --quality <rating>", "Quality rating 1-5", parseInt).option("-t, --time <seconds>", "Time spent in seconds", parseInt).option("-c, --files-count <count>", "Number of files modified", parseInt).option("-m, --summary <text>", "Brief summary of what was accomplished").option("--challenges <text>", "Difficulties encountered").option("-a, --agent <agent>", "Agent used (e.g., 'cursor', 'claude')").action(async (skillSlug, options) => {
2128
+ printCompactBanner();
2129
+ p5.intro(pc6.bgCyan(pc6.black(" Skill Feedback ")));
2130
+ const token = getToken();
2131
+ if (!token) {
2132
+ p5.log.error("Not authenticated. Run 'auth login' first.");
2133
+ p5.outro(pc6.red("\u2717 Failed"));
2134
+ process.exit(1);
2135
+ }
2136
+ const slugResult = skillSlugSchema.safeParse(skillSlug);
2137
+ if (!slugResult.success) {
2138
+ const msg = slugResult.error.issues?.[0]?.message ?? slugResult.error.message ?? "Invalid skill slug";
2139
+ p5.log.error(msg);
2140
+ p5.outro(pc6.red("\u2717 Failed"));
2141
+ process.exit(1);
2142
+ }
2143
+ const installedSkills = scanInstalledSkills({});
2144
+ const matches = installedSkills.filter((s2) => s2.slug === skillSlug);
2145
+ const organizations = new Set(
2146
+ matches.map((match) => match.organization).filter(Boolean)
2147
+ );
2148
+ if (organizations.size === 0) {
2149
+ p5.log.error(`Skill ${pc6.cyan(skillSlug)} not found in installed skills.`);
2150
+ p5.log.info(
2151
+ `Install it first, then retry. Example: ${pc6.cyan(`npx skillstogether add <org-slug> --skill ${skillSlug}`)}`
2152
+ );
2153
+ p5.outro(pc6.red("\u2717 Failed"));
2154
+ process.exit(1);
2155
+ }
2156
+ if (organizations.size > 1) {
2157
+ p5.log.error(
2158
+ `Multiple organizations found for ${pc6.cyan(skillSlug)}: ${pc6.cyan(
2159
+ Array.from(organizations).join(", ")
2160
+ )}`
2161
+ );
2162
+ p5.log.info("Use the org-prefixed skill slug to disambiguate.");
2163
+ p5.outro(pc6.red("\u2717 Failed"));
2164
+ process.exit(1);
2165
+ }
2166
+ const organizationSlug = Array.from(organizations)[0];
2167
+ if (options.quality !== void 0 && (options.quality < 1 || options.quality > 5)) {
2168
+ p5.log.error("Quality rating must be between 1 and 5");
2169
+ p5.outro(pc6.red("\u2717 Failed"));
2170
+ process.exit(1);
2171
+ }
2172
+ const result = {
2173
+ success: !options.failed,
2174
+ quality: options.quality,
2175
+ timeSeconds: options.time,
2176
+ filesCount: options.filesCount,
2177
+ summary: options.summary,
2178
+ challenges: options.challenges,
2179
+ agent: options.agent
2180
+ };
2181
+ const s = p5.spinner();
2182
+ s.start(
2183
+ `Submitting feedback for ${pc6.cyan(`${organizationSlug}/${skillSlug}`)}`
2184
+ );
2185
+ try {
2186
+ const response = await submitSkillResult(
2187
+ organizationSlug,
2188
+ skillSlug,
2189
+ result
2190
+ );
2191
+ s.stop(`Feedback submitted`);
2192
+ p5.log.success(response.message);
2193
+ p5.outro(pc6.green("\u2713 Feedback recorded!"));
2194
+ } catch (error) {
2195
+ s.stop("Failed to submit feedback");
2196
+ p5.log.error(error instanceof Error ? error.message : "Unknown error");
2197
+ p5.outro(pc6.red("\u2717 Failed"));
2198
+ process.exit(1);
2199
+ }
2200
+ });
2201
+
2202
+ // src/commands/list.ts
2203
+ import * as p6 from "@clack/prompts";
2204
+ import { Command as Command5 } from "commander";
2205
+ import pc7 from "picocolors";
2115
2206
  function displaySkillsTable(skills) {
2116
2207
  if (skills.length === 0) {
2117
- p5.log.info(pc6.dim("No skills installed"));
2208
+ p6.log.info(pc7.dim("No skills installed"));
2118
2209
  return;
2119
2210
  }
2120
2211
  const byOrg = groupSkillsByOrganization(skills);
2121
2212
  for (const [org, orgSkills] of byOrg) {
2122
2213
  console.log();
2123
- console.log(pc6.cyan(` ${org}/`));
2214
+ console.log(pc7.cyan(` ${org}/`));
2124
2215
  for (const skill of orgSkills) {
2125
- const agentBadge = pc6.dim(`[${skill.agent.name}]`);
2126
- const scopeBadge = skill.scope === "global" ? pc6.yellow("(global)") : pc6.dim("(project)");
2127
- const versionStr = skill.version ? pc6.dim(`v${skill.version}`) : "";
2216
+ const agentBadge = pc7.dim(`[${skill.agent.name}]`);
2217
+ const scopeBadge = skill.scope === "global" ? pc7.yellow("(global)") : pc7.dim("(project)");
2218
+ const versionStr = skill.version ? pc7.dim(`v${skill.version}`) : "";
2128
2219
  console.log(
2129
- ` ${pc6.white(skill.name)} ${versionStr} ${agentBadge} ${scopeBadge}`
2220
+ ` ${pc7.white(skill.name)} ${versionStr} ${agentBadge} ${scopeBadge}`
2130
2221
  );
2131
- console.log(pc6.dim(` ${shortenPath(skill.path)}`));
2222
+ console.log(pc7.dim(` ${shortenPath(skill.path)}`));
2132
2223
  }
2133
2224
  }
2134
2225
  }
2135
2226
  function displaySkillsByAgent(skills) {
2136
2227
  if (skills.length === 0) {
2137
- p5.log.info(pc6.dim("No skills installed"));
2228
+ p6.log.info(pc7.dim("No skills installed"));
2138
2229
  return;
2139
2230
  }
2140
2231
  const byAgent = groupSkillsByAgent(skills);
@@ -2142,26 +2233,26 @@ function displaySkillsByAgent(skills) {
2142
2233
  const agentConfig = AGENTS.find((a) => a.id === agent);
2143
2234
  const agentName = agentConfig?.name || agent;
2144
2235
  console.log();
2145
- console.log(pc6.cyan(` ${agentName}`));
2236
+ console.log(pc7.cyan(` ${agentName}`));
2146
2237
  for (const skill of agentSkills) {
2147
- const scopeBadge = skill.scope === "global" ? pc6.yellow("(global)") : pc6.dim("(project)");
2238
+ const scopeBadge = skill.scope === "global" ? pc7.yellow("(global)") : pc7.dim("(project)");
2148
2239
  console.log(
2149
- ` ${pc6.white(`${skill.organization}/${skill.slug}`)} ${scopeBadge}`
2240
+ ` ${pc7.white(`${skill.organization}/${skill.slug}`)} ${scopeBadge}`
2150
2241
  );
2151
2242
  }
2152
2243
  }
2153
2244
  }
2154
- var listCommand = new Command4("list").description("List installed skills").option("--global", "Show only globally installed skills").option("--project", "Show only project-installed skills").option("--agent <id>", "Show only skills for a specific agent").option("--by-agent", "Group output by agent instead of organization").action(
2245
+ var listCommand = new Command5("list").description("List installed skills").option("--global", "Show only globally installed skills").option("--project", "Show only project-installed skills").option("--agent <id>", "Show only skills for a specific agent").option("--by-agent", "Group output by agent instead of organization").action(
2155
2246
  async (options) => {
2156
2247
  printCompactBanner();
2157
- p5.intro(pc6.bgCyan(pc6.black(" Installed Skills ")));
2248
+ p6.intro(pc7.bgCyan(pc7.black(" Installed Skills ")));
2158
2249
  let scope;
2159
2250
  if (options.global && !options.project) {
2160
2251
  scope = "global";
2161
2252
  } else if (options.project && !options.global) {
2162
2253
  scope = "project";
2163
2254
  }
2164
- const s = p5.spinner();
2255
+ const s = p6.spinner();
2165
2256
  s.start("Scanning for installed skills...");
2166
2257
  const skills = scanInstalledSkills({
2167
2258
  scope,
@@ -2170,15 +2261,15 @@ var listCommand = new Command4("list").description("List installed skills").opti
2170
2261
  const uniqueSkills = deduplicateSkills(skills);
2171
2262
  const uniqueCount = uniqueSkills.length;
2172
2263
  const totalInstances = skills.length;
2173
- const countDisplay = uniqueCount === totalInstances ? `${pc6.green(uniqueCount.toString())} skill${uniqueCount !== 1 ? "s" : ""}` : `${pc6.green(uniqueCount.toString())} unique skill${uniqueCount !== 1 ? "s" : ""} (${totalInstances} installations)`;
2264
+ const countDisplay = uniqueCount === totalInstances ? `${pc7.green(uniqueCount.toString())} skill${uniqueCount !== 1 ? "s" : ""}` : `${pc7.green(uniqueCount.toString())} unique skill${uniqueCount !== 1 ? "s" : ""} (${totalInstances} installations)`;
2174
2265
  s.stop(`Found ${countDisplay}`);
2175
2266
  if (skills.length === 0) {
2176
- p5.log.info(
2177
- pc6.dim(
2267
+ p6.log.info(
2268
+ pc7.dim(
2178
2269
  "No skills found. Install skills with: npx skillstogether add <org-slug>"
2179
2270
  )
2180
2271
  );
2181
- p5.outro(pc6.dim("Done"));
2272
+ p6.outro(pc7.dim("Done"));
2182
2273
  return;
2183
2274
  }
2184
2275
  if (options.byAgent) {
@@ -2193,21 +2284,21 @@ var listCommand = new Command4("list").description("List installed skills").opti
2193
2284
  ).length;
2194
2285
  const orgCount = new Set(uniqueSkills.map((s2) => s2.organization)).size;
2195
2286
  const agentCount = new Set(skills.map((s2) => s2.agent.id)).size;
2196
- p5.log.info(
2197
- pc6.dim(
2287
+ p6.log.info(
2288
+ pc7.dim(
2198
2289
  `${uniqueCount} unique skill${uniqueCount !== 1 ? "s" : ""}, ${orgCount} organization${orgCount !== 1 ? "s" : ""}, ${agentCount} agent${agentCount !== 1 ? "s" : ""}, ${globalCount} global, ${projectCount} project`
2199
2290
  )
2200
2291
  );
2201
- p5.outro(pc6.dim("Done"));
2292
+ p6.outro(pc7.dim("Done"));
2202
2293
  }
2203
2294
  );
2204
2295
 
2205
2296
  // src/commands/status.ts
2206
- import * as p6 from "@clack/prompts";
2207
- import { Command as Command5 } from "commander";
2208
- import pc7 from "picocolors";
2297
+ import * as p7 from "@clack/prompts";
2298
+ import { Command as Command6 } from "commander";
2299
+ import pc8 from "picocolors";
2209
2300
  function formatDate(dateStr) {
2210
- if (!dateStr) return pc7.dim("unknown");
2301
+ if (!dateStr) return pc8.dim("unknown");
2211
2302
  try {
2212
2303
  const date = new Date(dateStr);
2213
2304
  return date.toLocaleDateString("en-US", {
@@ -2218,7 +2309,7 @@ function formatDate(dateStr) {
2218
2309
  minute: "2-digit"
2219
2310
  });
2220
2311
  } catch {
2221
- return pc7.dim("invalid");
2312
+ return pc8.dim("invalid");
2222
2313
  }
2223
2314
  }
2224
2315
  function groupByOrganization(skills) {
@@ -2230,16 +2321,16 @@ function groupByOrganization(skills) {
2230
2321
  }
2231
2322
  return groups;
2232
2323
  }
2233
- var statusCommand = new Command5("status").description("Check for skill updates").argument("[org-slug]", "Organization to check (optional)").option("--global", "Check only globally installed skills").option("--project", "Check only project-installed skills").action(
2324
+ var statusCommand = new Command6("status").description("Check for skill updates").argument("[org-slug]", "Organization to check (optional)").option("--global", "Check only globally installed skills").option("--project", "Check only project-installed skills").action(
2234
2325
  async (organizationSlug, options) => {
2235
2326
  printCompactBanner();
2236
- p6.intro(pc7.bgCyan(pc7.black(" Check for Updates ")));
2327
+ p7.intro(pc8.bgCyan(pc8.black(" Check for Updates ")));
2237
2328
  const token = getToken();
2238
2329
  if (!token) {
2239
- p6.log.error(
2240
- `Not authenticated. Run ${pc7.cyan("npx skillstogether auth login")} first.`
2330
+ p7.log.error(
2331
+ `Not authenticated. Run ${pc8.cyan("npx skillstogether auth login")} first.`
2241
2332
  );
2242
- p6.outro(pc7.red("Authentication required"));
2333
+ p7.outro(pc8.red("Authentication required"));
2243
2334
  process.exit(1);
2244
2335
  }
2245
2336
  let scope;
@@ -2248,7 +2339,7 @@ var statusCommand = new Command5("status").description("Check for skill updates"
2248
2339
  } else if (options.project && !options.global) {
2249
2340
  scope = "project";
2250
2341
  }
2251
- const s = p6.spinner();
2342
+ const s = p7.spinner();
2252
2343
  s.start("Scanning installed skills...");
2253
2344
  const installedSkills = scanUniqueSkills({
2254
2345
  scope,
@@ -2256,17 +2347,17 @@ var statusCommand = new Command5("status").description("Check for skill updates"
2256
2347
  });
2257
2348
  if (installedSkills.length === 0) {
2258
2349
  s.stop("No installed skills found");
2259
- p6.log.info(
2260
- pc7.dim("Install skills with: npx skillstogether add <org-slug>")
2350
+ p7.log.info(
2351
+ pc8.dim("Install skills with: npx skillstogether add <org-slug>")
2261
2352
  );
2262
- p6.outro(pc7.dim("Done"));
2353
+ p7.outro(pc8.dim("Done"));
2263
2354
  return;
2264
2355
  }
2265
2356
  s.stop(
2266
- `Found ${pc7.green(installedSkills.length.toString())} installed skill${installedSkills.length !== 1 ? "s" : ""}`
2357
+ `Found ${pc8.green(installedSkills.length.toString())} installed skill${installedSkills.length !== 1 ? "s" : ""}`
2267
2358
  );
2268
2359
  const byOrg = groupByOrganization(installedSkills);
2269
- const checkSpinner = p6.spinner();
2360
+ const checkSpinner = p7.spinner();
2270
2361
  checkSpinner.start("Checking for updates...");
2271
2362
  const allUpdates = [];
2272
2363
  const errors = [];
@@ -2291,57 +2382,57 @@ var statusCommand = new Command5("status").description("Check for skill updates"
2291
2382
  );
2292
2383
  if (outdatedSkills.length > 0) {
2293
2384
  console.log();
2294
- p6.log.warn(
2295
- `${pc7.yellow(outdatedSkills.length.toString())} skill${outdatedSkills.length !== 1 ? "s" : ""} ${outdatedSkills.length !== 1 ? "have" : "has"} updates available:`
2385
+ p7.log.warn(
2386
+ `${pc8.yellow(outdatedSkills.length.toString())} skill${outdatedSkills.length !== 1 ? "s" : ""} ${outdatedSkills.length !== 1 ? "have" : "has"} updates available:`
2296
2387
  );
2297
2388
  for (const { org, updates } of allUpdates) {
2298
2389
  const orgOutdated = updates.filter((u) => u.hasUpdate);
2299
2390
  if (orgOutdated.length === 0) continue;
2300
2391
  console.log();
2301
- console.log(pc7.cyan(` ${org}/`));
2392
+ console.log(pc8.cyan(` ${org}/`));
2302
2393
  for (const update of orgOutdated) {
2303
- console.log(` ${pc7.yellow("\u2191")} ${pc7.white(update.name)}`);
2394
+ console.log(` ${pc8.yellow("\u2191")} ${pc8.white(update.name)}`);
2304
2395
  console.log(
2305
- ` Installed: ${pc7.dim(formatDate(update.installedAt))}`
2396
+ ` Installed: ${pc8.dim(formatDate(update.installedAt))}`
2306
2397
  );
2307
2398
  console.log(
2308
- ` Available: ${pc7.green(formatDate(update.updatedAt))} ${pc7.dim(`(v${update.currentVersion})`)}`
2399
+ ` Available: ${pc8.green(formatDate(update.updatedAt))} ${pc8.dim(`(v${update.currentVersion})`)}`
2309
2400
  );
2310
2401
  }
2311
2402
  }
2312
2403
  console.log();
2313
- p6.log.info(
2314
- `Run ${pc7.cyan("npx skillstogether update")} to update all skills`
2404
+ p7.log.info(
2405
+ `Run ${pc8.cyan("npx skillstogether update")} to update all skills`
2315
2406
  );
2316
2407
  if (organizationSlug) {
2317
- p6.log.info(
2318
- `Or ${pc7.cyan(`npx skillstogether update ${organizationSlug}`)} to update this organization only`
2408
+ p7.log.info(
2409
+ `Or ${pc8.cyan(`npx skillstogether update ${organizationSlug}`)} to update this organization only`
2319
2410
  );
2320
2411
  }
2321
2412
  } else {
2322
- p6.log.success(
2323
- `All ${pc7.green(upToDateSkills.length.toString())} skill${upToDateSkills.length !== 1 ? "s are" : " is"} up to date!`
2413
+ p7.log.success(
2414
+ `All ${pc8.green(upToDateSkills.length.toString())} skill${upToDateSkills.length !== 1 ? "s are" : " is"} up to date!`
2324
2415
  );
2325
2416
  }
2326
2417
  if (errors.length > 0) {
2327
2418
  console.log();
2328
- p6.log.error(
2419
+ p7.log.error(
2329
2420
  `Failed to check ${errors.length} organization${errors.length !== 1 ? "s" : ""}:`
2330
2421
  );
2331
2422
  for (const error of errors) {
2332
- console.log(` ${pc7.red("\u2022")} ${error}`);
2423
+ console.log(` ${pc8.red("\u2022")} ${error}`);
2333
2424
  }
2334
2425
  }
2335
- p6.outro(pc7.dim("Done"));
2426
+ p7.outro(pc8.dim("Done"));
2336
2427
  }
2337
2428
  );
2338
2429
 
2339
2430
  // src/commands/uninstall.ts
2340
- import * as p7 from "@clack/prompts";
2341
- import { Command as Command6 } from "commander";
2431
+ import * as p8 from "@clack/prompts";
2432
+ import { Command as Command7 } from "commander";
2342
2433
  import { rmSync } from "fs";
2343
2434
  import { dirname as dirname2 } from "path";
2344
- import pc8 from "picocolors";
2435
+ import pc9 from "picocolors";
2345
2436
  function removeSkill(skill) {
2346
2437
  try {
2347
2438
  const skillDir = dirname2(skill.path);
@@ -2354,10 +2445,13 @@ function removeSkill(skill) {
2354
2445
  };
2355
2446
  }
2356
2447
  }
2357
- var uninstallCommand = new Command6("uninstall").description("Remove installed skills").argument("<target>", "Organization slug or organization/skill slug").option("--all", "Remove all skills from the organization").option("--global", "Remove from global installation only").option("--project", "Remove from project installation only").option("-y, --yes", "Skip confirmation prompts").action(
2448
+ var uninstallCommand = new Command7("uninstall").description("Remove installed skills").argument("<target>", "Organization slug").option("--all", "Remove all skills from the organization").option(
2449
+ "--skill <slug>",
2450
+ "Remove a specific skill by slug (already org-prefixed, e.g. acme-onboarding)"
2451
+ ).option("--global", "Remove from global installation only").option("--project", "Remove from project installation only").option("-y, --yes", "Skip confirmation prompts").action(
2358
2452
  async (target, options) => {
2359
2453
  printCompactBanner();
2360
- p7.intro(pc8.bgCyan(pc8.black(" Uninstall Skills ")));
2454
+ p8.intro(pc9.bgCyan(pc9.black(" Uninstall Skills ")));
2361
2455
  let scope;
2362
2456
  if (options.global && !options.project) {
2363
2457
  scope = "global";
@@ -2369,64 +2463,69 @@ var uninstallCommand = new Command6("uninstall").description("Remove installed s
2369
2463
  parsed = parseUninstallTarget(target);
2370
2464
  } catch (err) {
2371
2465
  const msg = err instanceof Error && "message" in err ? err.message : "Invalid target format";
2372
- p7.log.error(msg);
2373
- p7.outro(pc8.red("Invalid format"));
2466
+ p8.log.error(msg);
2467
+ p8.outro(pc9.red("Invalid format"));
2374
2468
  process.exit(1);
2375
2469
  }
2376
- if (parsed.skillSlug === void 0) {
2377
- if (!options.all) {
2378
- p7.log.error(
2379
- `To uninstall all skills from ${pc8.cyan(parsed.organizationSlug)}, use the ${pc8.cyan("--all")} flag`
2380
- );
2381
- p7.log.info(
2382
- `Or specify a skill: ${pc8.cyan(`npx skillstogether uninstall ${parsed.organizationSlug}/<skill-slug>`)}`
2470
+ if (options.skill) {
2471
+ const skillResult = skillSlugSchema.safeParse(options.skill);
2472
+ if (!skillResult.success) {
2473
+ const msg = skillResult.error.issues?.[0]?.message ?? skillResult.error.message ?? "Invalid skill slug";
2474
+ p8.log.error(msg);
2475
+ p8.outro(pc9.red("Invalid format"));
2476
+ process.exit(1);
2477
+ }
2478
+ const expectedPrefix = `${parsed.organizationSlug}-`;
2479
+ if (!skillResult.data.startsWith(expectedPrefix)) {
2480
+ p8.log.error(
2481
+ `Skill slug must be prefixed with the organization slug (e.g., ${pc9.cyan(`${expectedPrefix}my-skill`)})`
2383
2482
  );
2384
- p7.outro(pc8.red("Aborted"));
2483
+ p8.outro(pc9.red("Invalid format"));
2385
2484
  process.exit(1);
2386
2485
  }
2387
- await uninstallOrganization(parsed.organizationSlug, scope, options.yes);
2388
- } else {
2389
2486
  await uninstallSkill(
2390
2487
  parsed.organizationSlug,
2391
- parsed.skillSlug,
2488
+ skillResult.data,
2392
2489
  scope,
2393
2490
  options.yes
2394
2491
  );
2492
+ return;
2395
2493
  }
2494
+ await uninstallOrganization(parsed.organizationSlug, scope, options.yes);
2396
2495
  }
2397
2496
  );
2398
2497
  async function uninstallOrganization(organizationSlug, scope, skipConfirm) {
2399
- const s = p7.spinner();
2400
- s.start(`Finding skills from ${pc8.cyan(organizationSlug)}...`);
2498
+ const s = p8.spinner();
2499
+ s.start(`Finding skills from ${pc9.cyan(organizationSlug)}...`);
2401
2500
  const skills = scanInstalledSkills({ organization: organizationSlug, scope });
2402
2501
  if (skills.length === 0) {
2403
- s.stop(`No skills found from ${pc8.cyan(organizationSlug)}`);
2404
- p7.outro(pc8.yellow("Nothing to uninstall"));
2502
+ s.stop(`No skills found from ${pc9.cyan(organizationSlug)}`);
2503
+ p8.outro(pc9.yellow("Nothing to uninstall"));
2405
2504
  return;
2406
2505
  }
2407
2506
  s.stop(
2408
- `Found ${pc8.green(skills.length.toString())} skill${skills.length !== 1 ? "s" : ""} from ${pc8.cyan(organizationSlug)}`
2507
+ `Found ${pc9.green(skills.length.toString())} skill${skills.length !== 1 ? "s" : ""} from ${pc9.cyan(organizationSlug)}`
2409
2508
  );
2410
2509
  console.log();
2411
- p7.log.info("Skills to remove:");
2510
+ p8.log.info("Skills to remove:");
2412
2511
  for (const skill of skills) {
2413
- const scopeBadge = skill.scope === "global" ? pc8.yellow("(global)") : pc8.dim("(project)");
2512
+ const scopeBadge = skill.scope === "global" ? pc9.yellow("(global)") : pc9.dim("(project)");
2414
2513
  console.log(
2415
- ` ${pc8.red("\u2022")} ${skill.name} ${pc8.dim(`[${skill.agent.name}]`)} ${scopeBadge}`
2514
+ ` ${pc9.red("\u2022")} ${skill.name} ${pc9.dim(`[${skill.agent.name}]`)} ${scopeBadge}`
2416
2515
  );
2417
2516
  }
2418
2517
  console.log();
2419
2518
  if (!skipConfirm) {
2420
- const confirmed = await p7.confirm({
2519
+ const confirmed = await p8.confirm({
2421
2520
  message: `Remove all ${skills.length} skill${skills.length !== 1 ? "s" : ""} from ${organizationSlug}?`,
2422
2521
  initialValue: false
2423
2522
  });
2424
- if (p7.isCancel(confirmed) || !confirmed) {
2425
- p7.cancel("Uninstall cancelled");
2523
+ if (p8.isCancel(confirmed) || !confirmed) {
2524
+ p8.cancel("Uninstall cancelled");
2426
2525
  process.exit(0);
2427
2526
  }
2428
2527
  }
2429
- const removeSpinner = p7.spinner();
2528
+ const removeSpinner = p8.spinner();
2430
2529
  removeSpinner.start("Removing skills...");
2431
2530
  let removed = 0;
2432
2531
  let failed = 0;
@@ -2442,53 +2541,53 @@ async function uninstallOrganization(organizationSlug, scope, skipConfirm) {
2442
2541
  }
2443
2542
  removeSpinner.stop("Removal complete");
2444
2543
  if (removed > 0) {
2445
- p7.log.success(
2446
- `Removed ${pc8.green(removed.toString())} skill${removed !== 1 ? "s" : ""}`
2544
+ p8.log.success(
2545
+ `Removed ${pc9.green(removed.toString())} skill${removed !== 1 ? "s" : ""}`
2447
2546
  );
2448
2547
  }
2449
2548
  if (failed > 0) {
2450
- p7.log.error(
2451
- `Failed to remove ${pc8.red(failed.toString())} skill${failed !== 1 ? "s" : ""}`
2549
+ p8.log.error(
2550
+ `Failed to remove ${pc9.red(failed.toString())} skill${failed !== 1 ? "s" : ""}`
2452
2551
  );
2453
2552
  for (const error of errors) {
2454
- p7.log.error(pc8.dim(` ${error}`));
2553
+ p8.log.error(pc9.dim(` ${error}`));
2455
2554
  }
2456
2555
  }
2457
- p7.outro(pc8.dim("Done"));
2556
+ p8.outro(pc9.dim("Done"));
2458
2557
  }
2459
2558
  async function uninstallSkill(organizationSlug, skillSlug, scope, skipConfirm) {
2460
- const s = p7.spinner();
2461
- s.start(`Finding ${pc8.cyan(`${organizationSlug}/${skillSlug}`)}...`);
2559
+ const s = p8.spinner();
2560
+ s.start(`Finding ${pc9.cyan(`${organizationSlug}/${skillSlug}`)}...`);
2462
2561
  const instances = findSkillInstances(organizationSlug, skillSlug, { scope });
2463
2562
  if (instances.length === 0) {
2464
- s.stop(`Skill ${pc8.cyan(`${organizationSlug}/${skillSlug}`)} not found`);
2465
- p7.outro(pc8.yellow("Nothing to uninstall"));
2563
+ s.stop(`Skill ${pc9.cyan(`${organizationSlug}/${skillSlug}`)} not found`);
2564
+ p8.outro(pc9.yellow("Nothing to uninstall"));
2466
2565
  return;
2467
2566
  }
2468
2567
  s.stop(
2469
- `Found ${pc8.green(instances.length.toString())} instance${instances.length !== 1 ? "s" : ""} of ${pc8.cyan(`${organizationSlug}/${skillSlug}`)}`
2568
+ `Found ${pc9.green(instances.length.toString())} instance${instances.length !== 1 ? "s" : ""} of ${pc9.cyan(`${organizationSlug}/${skillSlug}`)}`
2470
2569
  );
2471
2570
  console.log();
2472
- p7.log.info("Instances to remove:");
2571
+ p8.log.info("Instances to remove:");
2473
2572
  for (const skill of instances) {
2474
- const scopeBadge = skill.scope === "global" ? pc8.yellow("(global)") : pc8.dim("(project)");
2573
+ const scopeBadge = skill.scope === "global" ? pc9.yellow("(global)") : pc9.dim("(project)");
2475
2574
  console.log(
2476
- ` ${pc8.red("\u2022")} ${pc8.dim(`[${skill.agent.name}]`)} ${scopeBadge}`
2575
+ ` ${pc9.red("\u2022")} ${pc9.dim(`[${skill.agent.name}]`)} ${scopeBadge}`
2477
2576
  );
2478
- console.log(` ${pc8.dim(shortenPath(skill.path))}`);
2577
+ console.log(` ${pc9.dim(shortenPath(skill.path))}`);
2479
2578
  }
2480
2579
  console.log();
2481
2580
  if (!skipConfirm) {
2482
- const confirmed = await p7.confirm({
2581
+ const confirmed = await p8.confirm({
2483
2582
  message: `Remove ${instances.length} instance${instances.length !== 1 ? "s" : ""} of ${organizationSlug}/${skillSlug}?`,
2484
2583
  initialValue: false
2485
2584
  });
2486
- if (p7.isCancel(confirmed) || !confirmed) {
2487
- p7.cancel("Uninstall cancelled");
2585
+ if (p8.isCancel(confirmed) || !confirmed) {
2586
+ p8.cancel("Uninstall cancelled");
2488
2587
  process.exit(0);
2489
2588
  }
2490
2589
  }
2491
- const removeSpinner = p7.spinner();
2590
+ const removeSpinner = p8.spinner();
2492
2591
  removeSpinner.start("Removing skill...");
2493
2592
  let removed = 0;
2494
2593
  let failed = 0;
@@ -2504,26 +2603,26 @@ async function uninstallSkill(organizationSlug, skillSlug, scope, skipConfirm) {
2504
2603
  }
2505
2604
  removeSpinner.stop("Removal complete");
2506
2605
  if (removed > 0) {
2507
- p7.log.success(
2508
- `Removed ${pc8.green(removed.toString())} instance${removed !== 1 ? "s" : ""}`
2606
+ p8.log.success(
2607
+ `Removed ${pc9.green(removed.toString())} instance${removed !== 1 ? "s" : ""}`
2509
2608
  );
2510
2609
  }
2511
2610
  if (failed > 0) {
2512
- p7.log.error(
2513
- `Failed to remove ${pc8.red(failed.toString())} instance${failed !== 1 ? "s" : ""}`
2611
+ p8.log.error(
2612
+ `Failed to remove ${pc9.red(failed.toString())} instance${failed !== 1 ? "s" : ""}`
2514
2613
  );
2515
2614
  for (const error of errors) {
2516
- p7.log.error(pc8.dim(` ${error}`));
2615
+ p8.log.error(pc9.dim(` ${error}`));
2517
2616
  }
2518
2617
  }
2519
- p7.outro(pc8.dim("Done"));
2618
+ p8.outro(pc9.dim("Done"));
2520
2619
  }
2521
2620
 
2522
2621
  // src/commands/update.ts
2523
- import * as p8 from "@clack/prompts";
2524
- import { Command as Command7 } from "commander";
2622
+ import * as p9 from "@clack/prompts";
2623
+ import { Command as Command8 } from "commander";
2525
2624
  import { writeFileSync as writeFileSync3 } from "fs";
2526
- import pc9 from "picocolors";
2625
+ import pc10 from "picocolors";
2527
2626
  function generateUpdatedContent(skill, organizationSlug) {
2528
2627
  const frontmatter = generateFrontmatter({
2529
2628
  name: skill.name,
@@ -2535,16 +2634,16 @@ function generateUpdatedContent(skill, organizationSlug) {
2535
2634
  });
2536
2635
  return frontmatter + "\n" + (skill.content || "");
2537
2636
  }
2538
- var updateCommand = new Command7("update").description("Update installed skills to latest version").argument("[target]", "Organization slug or organization/skill slug").option("--global", "Update only globally installed skills").option("--project", "Update only project-installed skills").option("-y, --yes", "Skip confirmation prompts").option("--dry-run", "Show what would be updated without making changes").action(
2637
+ var updateCommand = new Command8("update").description("Update installed skills to latest version").argument("[target]", "Organization slug or organization/skill slug").option("--global", "Update only globally installed skills").option("--project", "Update only project-installed skills").option("-y, --yes", "Skip confirmation prompts").option("--dry-run", "Show what would be updated without making changes").action(
2539
2638
  async (target, options) => {
2540
2639
  printCompactBanner();
2541
- p8.intro(pc9.bgCyan(pc9.black(" Update Skills ")));
2640
+ p9.intro(pc10.bgCyan(pc10.black(" Update Skills ")));
2542
2641
  const token = getToken();
2543
2642
  if (!token) {
2544
- p8.log.error(
2545
- `Not authenticated. Run ${pc9.cyan("npx skillstogether auth login")} first.`
2643
+ p9.log.error(
2644
+ `Not authenticated. Run ${pc10.cyan("npx skillstogether auth login")} first.`
2546
2645
  );
2547
- p8.outro(pc9.red("Authentication required"));
2646
+ p9.outro(pc10.red("Authentication required"));
2548
2647
  process.exit(1);
2549
2648
  }
2550
2649
  let scope;
@@ -2562,12 +2661,12 @@ var updateCommand = new Command7("update").description("Update installed skills
2562
2661
  skillSlug = parsed.skillSlug;
2563
2662
  } catch (err) {
2564
2663
  const msg = err instanceof Error && "message" in err ? err.message : "Invalid target format";
2565
- p8.log.error(msg);
2566
- p8.outro(pc9.red("Invalid format"));
2664
+ p9.log.error(msg);
2665
+ p9.outro(pc10.red("Invalid format"));
2567
2666
  process.exit(1);
2568
2667
  }
2569
2668
  }
2570
- const s = p8.spinner();
2669
+ const s = p9.spinner();
2571
2670
  s.start("Scanning installed skills...");
2572
2671
  const allInstalledSkills = scanInstalledSkills({
2573
2672
  scope,
@@ -2579,15 +2678,15 @@ var updateCommand = new Command7("update").description("Update installed skills
2579
2678
  }
2580
2679
  if (skillsToCheck.length === 0) {
2581
2680
  s.stop("No installed skills found");
2582
- p8.log.info(
2583
- pc9.dim("Install skills with: npx skillstogether add <org-slug>")
2681
+ p9.log.info(
2682
+ pc10.dim("Install skills with: npx skillstogether add <org-slug>")
2584
2683
  );
2585
- p8.outro(pc9.dim("Done"));
2684
+ p9.outro(pc10.dim("Done"));
2586
2685
  return;
2587
2686
  }
2588
2687
  const uniqueSkills = deduplicateSkills(skillsToCheck);
2589
2688
  s.stop(
2590
- `Found ${pc9.green(uniqueSkills.length.toString())} installed skill${uniqueSkills.length !== 1 ? "s" : ""}`
2689
+ `Found ${pc10.green(uniqueSkills.length.toString())} installed skill${uniqueSkills.length !== 1 ? "s" : ""}`
2591
2690
  );
2592
2691
  const byOrg = /* @__PURE__ */ new Map();
2593
2692
  for (const skill of uniqueSkills) {
@@ -2595,7 +2694,7 @@ var updateCommand = new Command7("update").description("Update installed skills
2595
2694
  existing.push(skill);
2596
2695
  byOrg.set(skill.organization, existing);
2597
2696
  }
2598
- const checkSpinner = p8.spinner();
2697
+ const checkSpinner = p9.spinner();
2599
2698
  checkSpinner.start("Checking for updates...");
2600
2699
  const skillsToUpdate = [];
2601
2700
  const errors = [];
@@ -2624,17 +2723,17 @@ var updateCommand = new Command7("update").description("Update installed skills
2624
2723
  }
2625
2724
  checkSpinner.stop("Update check complete");
2626
2725
  if (skillsToUpdate.length === 0) {
2627
- p8.log.success("All skills are up to date!");
2726
+ p9.log.success("All skills are up to date!");
2628
2727
  if (errors.length > 0) {
2629
2728
  console.log();
2630
- p8.log.error(
2729
+ p9.log.error(
2631
2730
  `Failed to check ${errors.length} organization${errors.length !== 1 ? "s" : ""}:`
2632
2731
  );
2633
2732
  for (const error of errors) {
2634
- console.log(` ${pc9.red("\u2022")} ${error}`);
2733
+ console.log(` ${pc10.red("\u2022")} ${error}`);
2635
2734
  }
2636
2735
  }
2637
- p8.outro(pc9.dim("Done"));
2736
+ p9.outro(pc10.dim("Done"));
2638
2737
  return;
2639
2738
  }
2640
2739
  const totalInstances = skillsToUpdate.reduce(
@@ -2642,33 +2741,33 @@ var updateCommand = new Command7("update").description("Update installed skills
2642
2741
  0
2643
2742
  );
2644
2743
  console.log();
2645
- const updateMsg = skillsToUpdate.length === totalInstances ? `${pc9.yellow(skillsToUpdate.length.toString())} skill${skillsToUpdate.length !== 1 ? "s" : ""} will be updated:` : `${pc9.yellow(skillsToUpdate.length.toString())} skill${skillsToUpdate.length !== 1 ? "s" : ""} (${totalInstances} installations) will be updated:`;
2646
- p8.log.info(updateMsg);
2744
+ const updateMsg = skillsToUpdate.length === totalInstances ? `${pc10.yellow(skillsToUpdate.length.toString())} skill${skillsToUpdate.length !== 1 ? "s" : ""} will be updated:` : `${pc10.yellow(skillsToUpdate.length.toString())} skill${skillsToUpdate.length !== 1 ? "s" : ""} (${totalInstances} installations) will be updated:`;
2745
+ p9.log.info(updateMsg);
2647
2746
  for (const { unique, remoteVersion } of skillsToUpdate) {
2648
2747
  const agentNames = unique.agents.map((a) => a.name).join(", ");
2649
- const scopeBadge = unique.scope === "global" ? pc9.yellow("(global)") : pc9.dim("(project)");
2748
+ const scopeBadge = unique.scope === "global" ? pc10.yellow("(global)") : pc10.dim("(project)");
2650
2749
  console.log(
2651
- ` ${pc9.yellow("\u2191")} ${pc9.white(`${unique.organization}/${unique.slug}`)} ${pc9.dim(`v${unique.version || "?"}`)} \u2192 ${pc9.green(`v${remoteVersion}`)} ${pc9.dim(`[${agentNames}]`)} ${scopeBadge}`
2750
+ ` ${pc10.yellow("\u2191")} ${pc10.white(`${unique.organization}/${unique.slug}`)} ${pc10.dim(`v${unique.version || "?"}`)} \u2192 ${pc10.green(`v${remoteVersion}`)} ${pc10.dim(`[${agentNames}]`)} ${scopeBadge}`
2652
2751
  );
2653
2752
  }
2654
2753
  console.log();
2655
2754
  if (options.dryRun) {
2656
- p8.log.info(pc9.dim("Dry run - no changes made"));
2657
- p8.outro(pc9.dim("Done"));
2755
+ p9.log.info(pc10.dim("Dry run - no changes made"));
2756
+ p9.outro(pc10.dim("Done"));
2658
2757
  return;
2659
2758
  }
2660
2759
  if (!options.yes) {
2661
2760
  const confirmMsg = skillsToUpdate.length === totalInstances ? `Update ${skillsToUpdate.length} skill${skillsToUpdate.length !== 1 ? "s" : ""}?` : `Update ${skillsToUpdate.length} skill${skillsToUpdate.length !== 1 ? "s" : ""} (${totalInstances} installations)?`;
2662
- const confirmed = await p8.confirm({
2761
+ const confirmed = await p9.confirm({
2663
2762
  message: confirmMsg,
2664
2763
  initialValue: true
2665
2764
  });
2666
- if (p8.isCancel(confirmed) || !confirmed) {
2667
- p8.cancel("Update cancelled");
2765
+ if (p9.isCancel(confirmed) || !confirmed) {
2766
+ p9.cancel("Update cancelled");
2668
2767
  process.exit(0);
2669
2768
  }
2670
2769
  }
2671
- const updateSpinner = p8.spinner();
2770
+ const updateSpinner = p9.spinner();
2672
2771
  updateSpinner.start("Updating skills...");
2673
2772
  let updatedSkills = 0;
2674
2773
  let updatedInstances = 0;
@@ -2692,23 +2791,23 @@ var updateCommand = new Command7("update").description("Update installed skills
2692
2791
  }
2693
2792
  updateSpinner.stop("Update complete");
2694
2793
  if (updatedSkills > 0) {
2695
- const summaryMsg = updatedSkills === updatedInstances ? `Updated ${pc9.green(updatedSkills.toString())} skill${updatedSkills !== 1 ? "s" : ""}` : `Updated ${pc9.green(updatedSkills.toString())} skill${updatedSkills !== 1 ? "s" : ""} (${updatedInstances} installations)`;
2696
- p8.log.success(summaryMsg);
2794
+ const summaryMsg = updatedSkills === updatedInstances ? `Updated ${pc10.green(updatedSkills.toString())} skill${updatedSkills !== 1 ? "s" : ""}` : `Updated ${pc10.green(updatedSkills.toString())} skill${updatedSkills !== 1 ? "s" : ""} (${updatedInstances} installations)`;
2795
+ p9.log.success(summaryMsg);
2697
2796
  }
2698
2797
  if (failed > 0) {
2699
- p8.log.error(
2700
- `Failed to update ${pc9.red(failed.toString())} skill${failed !== 1 ? "s" : ""}`
2798
+ p9.log.error(
2799
+ `Failed to update ${pc10.red(failed.toString())} skill${failed !== 1 ? "s" : ""}`
2701
2800
  );
2702
2801
  for (const error of updateErrors) {
2703
- console.log(` ${pc9.red("\u2022")} ${error}`);
2802
+ console.log(` ${pc10.red("\u2022")} ${error}`);
2704
2803
  }
2705
2804
  }
2706
- p8.outro(pc9.dim("Done"));
2805
+ p9.outro(pc10.dim("Done"));
2707
2806
  }
2708
2807
  );
2709
2808
 
2710
2809
  // src/index.ts
2711
- var program = new Command8();
2810
+ var program = new Command9();
2712
2811
  program.name("skillstogether").description("CLI tool to install organization skills").version("0.1.0").hook("preAction", (thisCommand) => {
2713
2812
  const commandName = thisCommand.name();
2714
2813
  if (commandName === "skillstogether") {
@@ -2724,6 +2823,7 @@ program.helpInformation = function() {
2724
2823
  program.addCommand(authCommand);
2725
2824
  program.addCommand(addCommand);
2726
2825
  program.addCommand(doctorCommand);
2826
+ program.addCommand(feedbackCommand);
2727
2827
  program.addCommand(listCommand);
2728
2828
  program.addCommand(statusCommand);
2729
2829
  program.addCommand(uninstallCommand);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillstogether",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "CLI tool to install skills",
5
5
  "keywords": [
6
6
  "cli",