@quadient/skillshare 0.1.0 → 1.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.
Files changed (3) hide show
  1. package/README.md +3 -0
  2. package/dist/index.js +81 -16
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -9,6 +9,8 @@ bun run index.ts
9
9
  bun run index.ts login
10
10
  bun run index.ts login local
11
11
  bun run index.ts list
12
+ bun run index.ts install
13
+ bun run index.ts install "Prompt Lint"
12
14
  bun run index.ts upload ./examples/prompt-lint --version 0.1.0
13
15
  bun run index.ts install 12 0.1.0 --target ./installed
14
16
  bun run index.ts install --skill-version-id 42 --target ./installed
@@ -17,6 +19,7 @@ bun run index.ts install --skill-version-id 42 --target ./installed
17
19
  Interactive behavior:
18
20
 
19
21
  - If the current directory already contains SKILL.md, the TUI offers to upload it before showing the main action picker.
22
+ - `install` opens the guided installer. When you pass `<skillId|skillName>`, the installer opens with that skill already selected; adding `[version]` skips the version picker too.
20
23
  - After install or upload, the CLI writes skillshare.metadata.json into the skill directory with the skill id, name, description, and installed or published version.
21
24
  - When skillshare.metadata.json already exists, uploads publish a new version of that skill instead of creating a duplicate entry.
22
25
  - New skill creation derives name and description from SKILL.md frontmatter when available, so the user usually only needs to enter the initial version.
package/dist/index.js CHANGED
@@ -71,7 +71,9 @@ Commands:
71
71
  list
72
72
  show <skillId> [version]
73
73
  upload <directory> --version <version> [--skill-id <id>] [--name <name> --description <desc>]
74
- install <skillId> [version] --target <directory>
74
+ install
75
+ install <skillId|skillName> [version]
76
+ install <skillId|skillName> [version] --target <directory>
75
77
  install --skill-version-id <id> --target <directory>
76
78
 
77
79
  Options:
@@ -160,7 +162,7 @@ async function main() {
160
162
  }
161
163
  case 'install': {
162
164
  const skillVersionId = options.flags['skill-version-id'];
163
- const skillId = options.positionals[0];
165
+ const skillRef = options.positionals[0];
164
166
  const version = options.positionals[1];
165
167
  const target = options.flags.target;
166
168
  if (skillVersionId) {
@@ -185,16 +187,24 @@ async function main() {
185
187
  console.log(`Installed skillVersion ${parsedSkillVersionId} (${detail.skillId}@${detail.version}) to ${target}`);
186
188
  return;
187
189
  }
188
- if (!skillId || !target) {
189
- throw new Error('install requires <skillId> [version] --target <directory> or --skill-version-id <id> --target <directory>');
190
+ if (!target) {
191
+ await runInteractiveInstall(client, {
192
+ skillRef,
193
+ version,
194
+ });
195
+ return;
190
196
  }
191
- const skill = await client.get(`/api/skills/${skillId}`);
197
+ if (!skillRef) {
198
+ throw new Error('install requires <skillId|skillName> [version] when --target <directory> is provided');
199
+ }
200
+ const skills = await client.get('/api/skills');
201
+ const skill = resolveSkillReference(skills, skillRef);
192
202
  const effectiveVersion = version ?? skill.latestVersion;
193
203
  if (!effectiveVersion) {
194
- throw new Error(`Skill '${skillId}' does not have a published version.`);
204
+ throw new Error(`Skill '${skill.name}' does not have a published version.`);
195
205
  }
196
- const detail = await client.get(`/api/skills/${skillId}/versions/${effectiveVersion}`);
197
- await client.post(`/api/skills/${skillId}/versions/${effectiveVersion}/downloads`);
206
+ const detail = await client.get(`/api/skills/${skill.id}/versions/${effectiveVersion}`);
207
+ await client.post(`/api/skills/${skill.id}/versions/${effectiveVersion}/downloads`);
198
208
  await writeSkillVersionToDirectory(client, {
199
209
  skillId: skill.id,
200
210
  skillName: skill.name,
@@ -203,7 +213,7 @@ async function main() {
203
213
  files: detail.files,
204
214
  targetRoot: target,
205
215
  });
206
- console.log(`Installed ${skillId}@${effectiveVersion} to ${target}`);
216
+ console.log(`Installed ${skill.name}@${effectiveVersion} to ${target}`);
207
217
  return;
208
218
  }
209
219
  }
@@ -266,14 +276,18 @@ async function ensureInteractiveLogin(baseUrl) {
266
276
  await login(baseUrl);
267
277
  }
268
278
  }
269
- async function runInteractiveInstall(client) {
279
+ async function runInteractiveInstall(client, options) {
270
280
  const skills = await client.get('/api/skills');
271
281
  if (skills.length === 0) {
272
282
  throw new Error('No skills available in the registry.');
273
283
  }
274
- const skill = await promptForSkill(skills);
284
+ const skill = options?.skillRef
285
+ ? resolveSkillReference(skills, options.skillRef)
286
+ : await promptForSkill(skills);
275
287
  const detail = await client.get(`/api/skills/${skill.id}`);
276
- const selectedVersion = await promptForVersion(detail);
288
+ const selectedVersion = options?.version
289
+ ? resolveSkillVersion(detail, options.version)
290
+ : await promptForVersion(detail);
277
291
  const scope = await promptForInstallScope();
278
292
  const selectedAgents = await promptForAgents(scope);
279
293
  if (selectedAgents.length === 0) {
@@ -723,6 +737,47 @@ function normalizeText(value) {
723
737
  const trimmed = value?.trim();
724
738
  return trimmed ? trimmed : null;
725
739
  }
740
+ function resolveSkillReference(skills, reference) {
741
+ const normalizedReference = normalizeText(reference)?.toLowerCase();
742
+ if (!normalizedReference) {
743
+ throw new Error('Skill reference is required.');
744
+ }
745
+ const idMatch = skills.find((skill) => String(skill.id) === normalizedReference);
746
+ if (idMatch) {
747
+ return idMatch;
748
+ }
749
+ const exactNameMatches = skills.filter((skill) => normalizeText(skill.name)?.toLowerCase() === normalizedReference);
750
+ const exactNameMatch = exactNameMatches[0];
751
+ if (exactNameMatches.length === 1 && exactNameMatch) {
752
+ return exactNameMatch;
753
+ }
754
+ if (exactNameMatches.length > 1) {
755
+ throw new Error(`Skill reference '${reference}' matches multiple skills: ${formatSkillMatches(exactNameMatches)}. Use the numeric skill id instead.`);
756
+ }
757
+ const slugMatches = skills.filter((skill) => createInstalledSkillDirectoryName(skill) === normalizedReference);
758
+ const slugMatch = slugMatches[0];
759
+ if (slugMatches.length === 1 && slugMatch) {
760
+ return slugMatch;
761
+ }
762
+ if (slugMatches.length > 1) {
763
+ throw new Error(`Skill reference '${reference}' matches multiple skills: ${formatSkillMatches(slugMatches)}. Use the numeric skill id instead.`);
764
+ }
765
+ throw new Error(`Skill '${reference}' was not found. Run "skillshare list" to inspect available skills.`);
766
+ }
767
+ function resolveSkillVersion(detail, version) {
768
+ const normalizedVersion = normalizeText(version);
769
+ if (!normalizedVersion) {
770
+ throw new Error('Version is required.');
771
+ }
772
+ const requestedVersion = detail.versions.find((item) => item.version === normalizedVersion);
773
+ if (!requestedVersion) {
774
+ throw new Error(`Skill '${detail.name}' does not have version '${normalizedVersion}'.`);
775
+ }
776
+ return requestedVersion.version;
777
+ }
778
+ function formatSkillMatches(skills) {
779
+ return skills.map((skill) => `${skill.id}:${skill.name}`).join(', ');
780
+ }
726
781
  function filterSkills(skills, query) {
727
782
  const normalized = query.trim().toLowerCase();
728
783
  return skills.filter((skill) => skill.name.toLowerCase().includes(normalized) ||
@@ -1032,11 +1087,21 @@ async function openBrowser(url) {
1032
1087
  return;
1033
1088
  }
1034
1089
  if (process.platform === 'win32') {
1035
- const result = await runProcess('cmd', ['/c', 'start', '', url], { allowFailure: true });
1036
- if (result.exitCode !== 0) {
1037
- throw new Error(result.stderr.trim() || 'Failed to open browser.');
1090
+ for (const [command, args] of [
1091
+ ['explorer.exe', [url]],
1092
+ ['rundll32.exe', ['url.dll,FileProtocolHandler', url]],
1093
+ ]) {
1094
+ try {
1095
+ const result = await runProcess(command, args, { allowFailure: true });
1096
+ if (result.exitCode === 0) {
1097
+ return;
1098
+ }
1099
+ }
1100
+ catch {
1101
+ // Try the next opener.
1102
+ }
1038
1103
  }
1039
- return;
1104
+ throw new Error('Failed to open browser.');
1040
1105
  }
1041
1106
  const result = await runProcess('xdg-open', [url], { allowFailure: true });
1042
1107
  if (result.exitCode !== 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quadient/skillshare",
3
- "version": "0.1.0",
3
+ "version": "1.0.0",
4
4
  "description": "Skillshare CLI for installing and publishing Quadient skills",
5
5
  "type": "module",
6
6
  "bin": {