skillstogether 0.1.9 → 0.1.11

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 +27 -7
  2. package/dist/index.js +126 -11
  3. package/package.json +3 -3
package/README.md CHANGED
@@ -3,12 +3,18 @@
3
3
  CLI tool to install organization skills from SkillsTogether.
4
4
 
5
5
  ```
6
- ███████╗██╗ ██╗██╗██╗ ██╗ ███████╗████████╗ ██████╗ ██████╗ ███████╗████████╗██╗ ██╗███████╗██████╗
7
- ██╔════╝██║ ██╔╝██║██║ ██║ ██╔════╝╚══██╔══╝██╔═══██╗██╔════╝ ██╔════╝╚══██╔══╝██║ ██║██╔════╝██╔══██╗
8
- ███████╗█████╔╝ ██║██║ ██║ ███████╗ ██║ ██║ ██║██║ ███╗█████╗ ██║ ███████║█████╗ ██████╔╝
9
- ╚════██║██╔═██╗ ██║██║ ██║ ╚════██║ ██║ ██║ ██║██║ ██║██╔══╝ ██║ ██╔══██║██╔══╝ ██╔══██╗
10
- ███████║██║ ██╗██║███████╗███████╗███████║ ██║ ╚██████╔╝╚██████╔╝███████╗ ██║ ██║ ██║███████╗██║ ██║
11
- ╚══════╝╚═╝ ╚═╝╚═╝╚══════╝╚══════╝╚══════╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝
6
+ ███████╗██╗ ██╗██╗██╗ ██╗ ███████╗
7
+ ██╔════╝██║ ██╔╝██║██║ ██║ ██╔════╝
8
+ ███████╗█████╔╝ ██║██║ ██║ ███████╗
9
+ ╚════██║██╔═██╗ ██║██║ ██║ ╚════██║
10
+ ███████║██║ ██╗██║███████╗███████╗███████║
11
+ ╚══════╝╚═╝ ╚═╝╚═╝╚══════╝╚══════╝╚══════╝
12
+ ████████╗ ██████╗ ██████╗ ███████╗████████╗██╗ ██╗███████╗██████╗
13
+ ╚══██╔══╝██╔═══██╗██╔════╝ ██╔════╝╚══██╔══╝██║ ██║██╔════╝██╔══██╗
14
+ ██║ ██║ ██║██║ ███╗█████╗ ██║ ███████║█████╗ ██████╔╝
15
+ ██║ ██║ ██║██║ ██║██╔══╝ ██║ ██╔══██║██╔══╝ ██╔══██╗
16
+ ██║ ╚██████╔╝╚██████╔╝███████╗ ██║ ██║ ██║███████╗██║ ██║
17
+ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝
12
18
  ```
13
19
 
14
20
  ## Installation
@@ -69,6 +75,20 @@ npx skillstogether uninstall <organization-slug> --skill <skill-slug>
69
75
 
70
76
  ```
71
77
 
78
+ ### Updating Skills
79
+
80
+ ```bash
81
+ # Update all installed skills
82
+ npx skillstogether update
83
+
84
+ # Update all skills from an organization
85
+ npx skillstogether update <organization-slug>
86
+
87
+ # Update a specific skill (skill slug already includes the org prefix)
88
+ npx skillstogether update <organization-slug> --skill <skill-slug>
89
+
90
+ ```
91
+
72
92
  ### Options
73
93
 
74
94
  | Option | Alias | Description |
@@ -76,7 +96,7 @@ npx skillstogether uninstall <organization-slug> --skill <skill-slug>
76
96
  | `--dir <path>` | `-d` | Custom installation directory |
77
97
  | `--force` | `-f` | Overwrite existing skill files |
78
98
  | `--yes` | `-y` | Skip interactive prompts, install all |
79
- | `--skill <slug>` | — | Install a specific skill by slug (org-prefixed) |
99
+ | `--skill <slug>` | — | Target a specific skill by slug (org-prefixed) |
80
100
  | `--global` | — | Install globally (in home directory) |
81
101
 
82
102
  ---
package/dist/index.js CHANGED
@@ -302,6 +302,78 @@ function formatErrorMessage(err, fallback = DEFAULT_ERROR_MESSAGE) {
302
302
  import { existsSync as existsSync2, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
303
303
  import { dirname, join as join2 } from "path";
304
304
 
305
+ // src/lib/feedback-content.ts
306
+ var FEEDBACK_SECTION_HEADER = "## Post-Task Reporting";
307
+ function detectEol(content) {
308
+ return content.includes("\r\n") ? "\r\n" : "\n";
309
+ }
310
+ function removeTrailingFeedbackSection(content) {
311
+ const trailingSectionPattern = /^([\s\S]*)(?:\r?\n---\r?\n\r?\n## Post-Task Reporting\b[\s\S]*)$/;
312
+ const withPrefixMatch = content.match(trailingSectionPattern);
313
+ if (withPrefixMatch) {
314
+ return withPrefixMatch[1]?.trimEnd() ?? "";
315
+ }
316
+ const fullSectionPattern = /^---\r?\n\r?\n## Post-Task Reporting\b[\s\S]*$/;
317
+ if (fullSectionPattern.test(content)) {
318
+ return "";
319
+ }
320
+ return content;
321
+ }
322
+ function getFeedbackReportingSection(skillSlug) {
323
+ return [
324
+ "---",
325
+ "",
326
+ FEEDBACK_SECTION_HEADER,
327
+ "",
328
+ "After completing this skill's task, report your results using the CLI:",
329
+ "",
330
+ "```bash",
331
+ `npx skillstogether feedback ${skillSlug} \\`,
332
+ " --success \\",
333
+ " --quality 4 \\",
334
+ ' --summary "Brief description of what was accomplished" \\',
335
+ ' --agent "cursor"',
336
+ "```",
337
+ "",
338
+ "### Options",
339
+ "| Flag | Description |",
340
+ "|------|-------------|",
341
+ "| `--success` / `--failed` | Whether the task succeeded |",
342
+ "| `-q, --quality <1-5>` | Quality self-assessment |",
343
+ "| `-t, --time <seconds>` | Time spent |",
344
+ "| `-c, --files-count <n>` | Files modified |",
345
+ "| `-m, --summary <text>` | Summary of work |",
346
+ "| `--challenges <text>` | Difficulties encountered |",
347
+ "| `-a, --agent <name>` | Agent name (cursor, claude, codex) |",
348
+ "",
349
+ "If not authenticated, skip reporting silently."
350
+ ].join("\n");
351
+ }
352
+ function hasFeedbackSection(content) {
353
+ return (content ?? "").includes(FEEDBACK_SECTION_HEADER);
354
+ }
355
+ function normalizeFeedbackContent(content, skillSlug, feedbackEnabled) {
356
+ const text = content ?? "";
357
+ if (!feedbackEnabled) {
358
+ return removeTrailingFeedbackSection(text);
359
+ }
360
+ if (hasFeedbackSection(text)) {
361
+ return text;
362
+ }
363
+ const eol = detectEol(text);
364
+ const section = getFeedbackReportingSection(skillSlug).replace(/\n/g, eol);
365
+ if (text.length === 0) {
366
+ return section;
367
+ }
368
+ if (/(?:\r?\n){2}$/.test(text)) {
369
+ return text + section;
370
+ }
371
+ if (/\r?\n$/.test(text)) {
372
+ return text + eol + section;
373
+ }
374
+ return text + eol + eol + section;
375
+ }
376
+
305
377
  // src/lib/validation.ts
306
378
  import { z } from "zod";
307
379
  var slugRegex = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
@@ -328,13 +400,23 @@ var addTargetSchema = z.string().min(1, "Target is required").refine(
328
400
  },
329
401
  { message: "Each part must be a valid slug (lowercase, numbers, hyphens)" }
330
402
  );
331
- var updateTargetSchema = z.string().max(200).optional().refine(
403
+ var updateTargetSchema = z.string().max(100).optional().refine(
332
404
  (val) => {
333
405
  if (!val) return true;
334
406
  const parts = val.split("/");
335
- return parts.length >= 1 && parts.length <= 2 && parts.every((part) => slugRegex.test(part));
407
+ return parts.length === 1;
336
408
  },
337
- { message: "Target must be 'org-slug' or 'org-slug/skill-slug'" }
409
+ {
410
+ message: "Target must be an organization slug (no '/'). Use --skill to target a specific skill."
411
+ }
412
+ ).refine(
413
+ (val) => {
414
+ if (!val) return true;
415
+ return slugRegex.test(val);
416
+ },
417
+ {
418
+ message: "Organization slug must be lowercase letters, numbers, and hyphens only"
419
+ }
338
420
  );
339
421
  var uninstallTargetSchema = z.string().min(1, "Target is required").refine(
340
422
  (val) => {
@@ -355,10 +437,9 @@ function parseUpdateTarget(target) {
355
437
  if (target === void 0 || target === "") return {};
356
438
  const parsed = updateTargetSchema.parse(target);
357
439
  if (!parsed || typeof parsed !== "string") return {};
358
- const parts = parsed.split("/");
440
+ const organizationSlug = organizationSlugSchema.parse(parsed);
359
441
  return {
360
- organizationSlug: parts[0],
361
- skillSlug: parts.length === 2 ? parts[1] : void 0
442
+ organizationSlug
362
443
  };
363
444
  }
364
445
  function parseUninstallTarget(target) {
@@ -406,7 +487,11 @@ function generateSkillContent(skill, organizationSlug, filesChecksum) {
406
487
  filesChecksum ? `filesChecksum: ${filesChecksum}` : null,
407
488
  "---"
408
489
  ].filter((line) => line !== null && line !== void 0).join("\n");
409
- const body = skill.content || "";
490
+ const body = normalizeFeedbackContent(
491
+ skill.content,
492
+ skill.slug,
493
+ skill.feedbackEnabled
494
+ );
410
495
  const separator = body ? body.startsWith("\n") || body.startsWith("\r\n") ? "\n" : "\n\n" : "\n";
411
496
  const content = frontmatter + separator + body;
412
497
  return content.endsWith("\n") ? content : `${content}
@@ -2638,9 +2723,17 @@ function generateUpdatedContent(skill, organizationSlug) {
2638
2723
  createdBy: skill.createdBy.name,
2639
2724
  installedAt: (/* @__PURE__ */ new Date()).toISOString()
2640
2725
  });
2641
- return frontmatter + "\n" + (skill.content || "");
2726
+ const normalizedContent = normalizeFeedbackContent(
2727
+ skill.content,
2728
+ skill.slug,
2729
+ skill.feedbackEnabled
2730
+ );
2731
+ return frontmatter + "\n" + normalizedContent;
2642
2732
  }
2643
- 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(
2733
+ var updateCommand = new Command8("update").description("Update installed skills to latest version").argument("[target]", "Organization slug").option("--global", "Update only globally installed skills").option("--project", "Update only project-installed skills").option(
2734
+ "--skill <slug>",
2735
+ "Update a specific skill by slug (already org-prefixed, e.g. acme-onboarding)"
2736
+ ).option("-y, --yes", "Skip confirmation prompts").option("--dry-run", "Show what would be updated without making changes").action(
2644
2737
  async (target, options) => {
2645
2738
  printCompactBanner();
2646
2739
  p9.intro(pc10.bgCyan(pc10.black(" Update Skills ")));
@@ -2659,12 +2752,10 @@ var updateCommand = new Command8("update").description("Update installed skills
2659
2752
  scope = "project";
2660
2753
  }
2661
2754
  let organizationSlug;
2662
- let skillSlug;
2663
2755
  if (target) {
2664
2756
  try {
2665
2757
  const parsed = parseUpdateTarget(target);
2666
2758
  organizationSlug = parsed.organizationSlug;
2667
- skillSlug = parsed.skillSlug;
2668
2759
  } catch (err) {
2669
2760
  const msg = err instanceof Error && "message" in err ? err.message : "Invalid target format";
2670
2761
  p9.log.error(msg);
@@ -2672,6 +2763,30 @@ var updateCommand = new Command8("update").description("Update installed skills
2672
2763
  process.exit(1);
2673
2764
  }
2674
2765
  }
2766
+ if (options.skill && !organizationSlug) {
2767
+ p9.log.error("Organization slug is required when using --skill");
2768
+ p9.outro(pc10.red("Invalid format"));
2769
+ process.exit(1);
2770
+ }
2771
+ let skillSlug;
2772
+ if (options.skill) {
2773
+ const skillResult = skillSlugSchema.safeParse(options.skill);
2774
+ if (!skillResult.success) {
2775
+ const msg = skillResult.error.issues?.[0]?.message ?? skillResult.error.message ?? "Invalid skill slug";
2776
+ p9.log.error(msg);
2777
+ p9.outro(pc10.red("Invalid format"));
2778
+ process.exit(1);
2779
+ }
2780
+ const expectedPrefix = `${organizationSlug}-`;
2781
+ if (!skillResult.data.startsWith(expectedPrefix)) {
2782
+ p9.log.error(
2783
+ `Skill slug must be prefixed with the organization slug (e.g., ${pc10.cyan(`${expectedPrefix}my-skill`)})`
2784
+ );
2785
+ p9.outro(pc10.red("Invalid format"));
2786
+ process.exit(1);
2787
+ }
2788
+ skillSlug = skillResult.data;
2789
+ }
2675
2790
  const s = p9.spinner();
2676
2791
  s.start("Scanning installed skills...");
2677
2792
  const allInstalledSkills = scanInstalledSkills({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillstogether",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "CLI tool to install skills",
5
5
  "keywords": [
6
6
  "cli",
@@ -19,14 +19,14 @@
19
19
  "access": "public"
20
20
  },
21
21
  "dependencies": {
22
- "@clack/prompts": "^1.0.0",
22
+ "@clack/prompts": "^1.0.1",
23
23
  "commander": "^14.0.3",
24
24
  "open": "^11.0.0",
25
25
  "picocolors": "^1.1.1",
26
26
  "zod": "^4.3.6"
27
27
  },
28
28
  "devDependencies": {
29
- "@types/node": "^25.2.0",
29
+ "@types/node": "^25.2.3",
30
30
  "tsup": "^8.5.1",
31
31
  "typescript": "^5.9.3"
32
32
  },