@skillshub-labs/cli 0.1.17 → 0.1.19

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 (28) hide show
  1. package/README.md +28 -12
  2. package/bin/skills-hub +193 -15
  3. package/data/official-presets/catalog.json +436 -0
  4. package/data/official-presets/policies/policy-azure-cloud.md +18 -0
  5. package/data/official-presets/policies/policy-cloudflare-edge.md +18 -0
  6. package/data/official-presets/policies/policy-fastapi-py.md +31 -0
  7. package/data/official-presets/policies/policy-fullstack-web.md +24 -0
  8. package/data/official-presets/policies/policy-go-service.md +31 -0
  9. package/data/official-presets/policies/policy-hf-ml.md +18 -0
  10. package/data/official-presets/policies/policy-langchain-apps.md +18 -0
  11. package/data/official-presets/policies/policy-literature-review.md +18 -0
  12. package/data/official-presets/policies/policy-monorepo-turbo.md +31 -0
  13. package/data/official-presets/policies/policy-nextjs-ts-strict.md +31 -0
  14. package/data/official-presets/policies/policy-node-api-ts.md +31 -0
  15. package/data/official-presets/policies/policy-python-api.md +18 -0
  16. package/data/official-presets/policies/policy-release-ci.md +18 -0
  17. package/data/official-presets/policies/policy-release-maintainer.md +31 -0
  18. package/data/official-presets/policies/policy-scientific-discovery.md +18 -0
  19. package/data/official-presets/policies/policy-scientific-python.md +31 -0
  20. package/data/official-presets/policies/policy-security-audit.md +18 -0
  21. package/data/official-presets/policies/policy-web-frontend.md +18 -0
  22. package/lib/core/kit-core.d.ts +14 -3
  23. package/lib/core/kit-core.mjs +327 -20
  24. package/lib/core/kit-types.ts +128 -3
  25. package/lib/services/kit-loadout-import.mjs +599 -0
  26. package/lib/services/kit-service.d.ts +90 -2
  27. package/lib/services/kit-service.mjs +665 -38
  28. package/package.json +9 -1
package/README.md CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  <p align="center">
6
6
  <img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License">
7
+ <img src="https://github.com/PotatoDog1669/skills-hub/actions/workflows/ci.yml/badge.svg" alt="CI">
7
8
  <img src="https://img.shields.io/badge/Tauri-2.x-24C8D8" alt="Tauri">
8
9
  <img src="https://img.shields.io/badge/TypeScript-5.0-blue" alt="TypeScript">
9
10
  <img src="https://img.shields.io/badge/status-active-success" alt="Status">
@@ -39,36 +40,36 @@ Skills Hub supports synchronization with a wide range of AI agents, including An
39
40
  - Rust toolchain (`rustup`) for Desktop (Tauri) source build
40
41
  - Tauri platform prerequisites for your OS: [Tauri v2 prerequisites](https://v2.tauri.app/start/prerequisites/)
41
42
 
42
- ### Option A: Homebrew CLI (macOS/Linux)
43
+ ### App (macOS)
43
44
 
44
45
  ```bash
45
- brew tap PotatoDog1669/skillshub
46
- brew install skills-hub
47
- skills-hub --version
46
+ curl -fLsS https://raw.githubusercontent.com/PotatoDog1669/skills-hub/main/install.sh | sh
48
47
  ```
49
48
 
50
49
  Upgrade:
51
50
 
52
51
  ```bash
53
- brew update
54
- brew upgrade skills-hub
52
+ curl -fLsS https://raw.githubusercontent.com/PotatoDog1669/skills-hub/main/install.sh | sh
55
53
  ```
56
54
 
57
- ### Option B: Homebrew Desktop App (macOS)
55
+ ### CLI
56
+
57
+ #### CLI via Homebrew (macOS/Linux)
58
58
 
59
59
  ```bash
60
60
  brew tap PotatoDog1669/skillshub
61
- brew install --cask skills-hub
61
+ brew install skills-hub
62
+ skills-hub --version
62
63
  ```
63
64
 
64
65
  Upgrade:
65
66
 
66
67
  ```bash
67
68
  brew update
68
- brew upgrade --cask skills-hub
69
+ brew upgrade skills-hub
69
70
  ```
70
71
 
71
- ### Option C: CLI via npm
72
+ #### CLI via npm
72
73
 
73
74
  Install globally:
74
75
 
@@ -89,7 +90,7 @@ Upgrade:
89
90
  npm i -g @skillshub-labs/cli@latest
90
91
  ```
91
92
 
92
- ### Option D: Build Desktop App from Source
93
+ ### Build from Source (Desktop App)
93
94
 
94
95
  ```bash
95
96
  git clone https://github.com/PotatoDog1669/skills-hub.git
@@ -111,7 +112,9 @@ Output directory:
111
112
 
112
113
  - Latest releases: [GitHub Releases](https://github.com/PotatoDog1669/skills-hub/releases)
113
114
  - Current releases include changelog + source archives (`zipball` / `tarball`).
114
- - Desktop release assets include Homebrew cask-ready DMGs:
115
+ - Desktop release assets include installer archives plus DMG fallbacks:
116
+ - `skills-hub_X.Y.Z_macos_aarch64.tar.gz`
117
+ - `skills-hub_X.Y.Z_macos_x64.tar.gz`
115
118
  - `skills-hub_X.Y.Z_macos_aarch64.dmg`
116
119
  - `skills-hub_X.Y.Z_macos_x64.dmg`
117
120
 
@@ -136,6 +139,8 @@ Output directory:
136
139
  | `skills-hub kit loadout-*` | Manage skill packages (`loadout-list/add/update/delete`) |
137
140
  | `skills-hub kit add/update/delete/apply` | Compose Kit and apply it to target project + agent |
138
141
 
142
+ Note: `skills-hub kit ...` stays local and does not auto-install official presets. Use `skills-hub official install --id <presetId>` when you explicitly want to install curated official content.
143
+
139
144
  ### Import/List/Remove Quick Examples
140
145
 
141
146
  ```bash
@@ -175,6 +180,15 @@ npm ci
175
180
  npm run tauri:dev
176
181
  ```
177
182
 
183
+ Quality checks:
184
+
185
+ ```bash
186
+ npm run lint
187
+ npm run typecheck
188
+ npm run test -- --run
189
+ npm run build
190
+ ```
191
+
178
192
  For maintainers, a reusable release notes template is available at:
179
193
  - `docs/release-notes-template.md`
180
194
  - `docs/homebrew-tap-setup.md`
@@ -185,6 +199,8 @@ We welcome contributions! Please see our [CONTRIBUTING.md](docs/CONTRIBUTING.md)
185
199
 
186
200
  Please adhere to our [Code of Conduct](docs/CODE_OF_CONDUCT.md) in all interactions.
187
201
 
202
+ Please report vulnerabilities privately according to [SECURITY.md](SECURITY.md).
203
+
188
204
  ## License
189
205
 
190
206
  This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
package/bin/skills-hub CHANGED
@@ -13,7 +13,7 @@ const commandAliases = new Map([
13
13
  ['ls', 'list'],
14
14
  ['rm', 'remove'],
15
15
  ]);
16
- const commands = new Set(['import', 'list', 'ls', 'remove', 'rm', 'sync', 'provider', 'kit']);
16
+ const commands = new Set(['import', 'list', 'ls', 'remove', 'rm', 'sync', 'provider', 'kit', 'official']);
17
17
  const flagsWithValues = new Set([
18
18
  '-b',
19
19
  '--branch',
@@ -38,12 +38,15 @@ const flagsWithValues = new Set([
38
38
  '--loadout-id',
39
39
  '--description',
40
40
  '--skills',
41
+ '--url',
41
42
  '--project',
42
43
  '--agent',
43
44
  '-a',
44
45
  '--mode',
45
46
  '--content',
46
47
  '--content-file',
48
+ '--with',
49
+ '--without',
47
50
  ]);
48
51
  let providerCorePromise = null;
49
52
  let kitServicePromise = null;
@@ -108,6 +111,11 @@ Commands:
108
111
  sync --all | --target <name> Sync hub skills to agent targets
109
112
  provider <subcommand> Manage provider profiles and switching
110
113
  kit <subcommand> Manage AGENTS templates, loadouts, and kits
114
+ official <subcommand> Manage official preset catalog installs
115
+
116
+ Behavior:
117
+ kit commands are local-only and do not auto-install official presets
118
+ official install ... Explicitly installs official preset content
111
119
 
112
120
  Import options:
113
121
  -b, --branch <branch> Use branch when importing from Git
@@ -150,12 +158,19 @@ Kit subcommands:
150
158
  kit loadout-list
151
159
  kit loadout-add --name <name> --skills <path1,path2,...> [--description <text>] [--mode copy|link]
152
160
  kit loadout-update --id <id> [--name <name>] [--description <text>] [--skills <path1,path2,...>] [--mode copy|link]
161
+ kit loadout-import --url <repoOrTreeUrl> [--name <packageName>] [--description <text>] [--yes]
153
162
  kit loadout-delete --id <id>
154
163
  kit list
155
- kit add --name <name> --policy-id <id> --loadout-id <id> [--description <text>]
164
+ kit add --name <name> [--policy-id <id>] [--loadout-id <id>] [--description <text>]
156
165
  kit update --id <id> [--name <name>] [--policy-id <id>] [--loadout-id <id>] [--description <text>]
157
166
  kit delete --id <id>
158
- kit apply --id <id> --project <path> --agent <name> [--mode copy|link] [--overwrite-agents-md]
167
+ kit apply --id <id> --project <path> --agent <name> [--mode copy|link] [--overwrite-agents-md] [--with <skill>] [--without <skill>]
168
+
169
+ Official subcommands:
170
+ official list
171
+ official search <query>
172
+ official inspect --id <presetId>
173
+ official install --id <presetId> [--yes]
159
174
 
160
175
  -v, --version Show version number
161
176
  -h, --help Show this help message
@@ -183,6 +198,30 @@ function readArgValue(argv, flag, shortFlag) {
183
198
  return null;
184
199
  }
185
200
 
201
+ function readArgValues(argv, flag, shortFlag) {
202
+ const values = [];
203
+
204
+ for (let index = 0; index < argv.length; index += 1) {
205
+ const arg = argv[index];
206
+ if (arg === flag || (shortFlag && arg === shortFlag)) {
207
+ const next = argv[index + 1];
208
+ if (next && !next.startsWith('-')) {
209
+ values.push(next);
210
+ }
211
+ continue;
212
+ }
213
+
214
+ if (arg.startsWith(`${flag}=`)) {
215
+ values.push(arg.split('=').slice(1).join('='));
216
+ }
217
+ }
218
+
219
+ return values
220
+ .flatMap((value) => String(value || '').split(','))
221
+ .map((value) => value.trim())
222
+ .filter(Boolean);
223
+ }
224
+
186
225
  function expandHome(inputPath) {
187
226
  if (!inputPath) return inputPath;
188
227
  if (inputPath.startsWith('~')) {
@@ -290,6 +329,9 @@ async function runCommand(commandName, commandArgs) {
290
329
  case 'kit':
291
330
  await handleKit(commandArgs);
292
331
  return;
332
+ case 'official':
333
+ await handleOfficial(commandArgs);
334
+ return;
293
335
  default:
294
336
  console.error(`Unknown command: ${commandName}`);
295
337
  process.exit(1);
@@ -706,13 +748,21 @@ function printKitPolicyRow(policy) {
706
748
  }
707
749
 
708
750
  function printKitLoadoutRow(loadout) {
709
- console.log(`- ${loadout.id} | ${loadout.name} | skills=${loadout.items.length}`);
751
+ const importLabel = loadout.importSource ? ` | imported=${loadout.importSource.repoWebUrl}` : '';
752
+ console.log(`- ${loadout.id} | ${loadout.name} | skills=${loadout.items.length}${importLabel}`);
710
753
  }
711
754
 
712
755
  function printKitRow(kit) {
713
756
  console.log(`- ${kit.id} | ${kit.name} | policy=${kit.policyId} | loadout=${kit.loadoutId}`);
714
757
  }
715
758
 
759
+ function printOfficialPresetRow(preset) {
760
+ console.log(
761
+ `- ${preset.id} | ${preset.name} | policy=${preset.policyName} | ` +
762
+ `skills=${preset.skillCount} | sources=${preset.sourceCount}`
763
+ );
764
+ }
765
+
716
766
  async function handleKit(commandArgs) {
717
767
  const subcommand = commandArgs[0];
718
768
  if (!subcommand) {
@@ -841,6 +891,34 @@ async function handleKit(commandArgs) {
841
891
  console.log(`Loadout updated: ${loadout.id} (${loadout.name})`);
842
892
  return;
843
893
  }
894
+ case 'loadout-import': {
895
+ const rest = commandArgs.slice(1);
896
+ const url = readArgValue(rest, '--url');
897
+ const name = readArgValue(rest, '--name');
898
+ const description = readArgValue(rest, '--description');
899
+ const overwrite = hasFlag(rest, '--yes') || hasFlag(rest, '-y');
900
+
901
+ if (!url) {
902
+ throw new Error('kit loadout-import requires --url');
903
+ }
904
+
905
+ const result = await service.importKitLoadoutFromRepo({
906
+ url,
907
+ name: name || undefined,
908
+ description: description || undefined,
909
+ overwrite,
910
+ });
911
+
912
+ console.log(`Remote source: ${result.source.repoWebUrl}`);
913
+ console.log(`Root subdir: ${result.source.rootSubdir}`);
914
+ console.log(`Resolved branch: ${result.source.branch || 'unknown'}`);
915
+ console.log(
916
+ `Imported skills: discovered=${result.discoveredCount}, overwritten=${result.overwrittenCount}, removed=${result.removedCount}`
917
+ );
918
+ const loadoutLabel = result.loadoutStatus === 'updated' ? 'Loadout updated' : 'Loadout created';
919
+ console.log(`${loadoutLabel}: ${result.loadout.id} (${result.loadout.name})`);
920
+ return;
921
+ }
844
922
  case 'loadout-delete': {
845
923
  const rest = commandArgs.slice(1);
846
924
  const id = readArgValue(rest, '--id');
@@ -867,15 +945,18 @@ async function handleKit(commandArgs) {
867
945
  const policyId = readArgValue(rest, '--policy-id');
868
946
  const loadoutId = readArgValue(rest, '--loadout-id');
869
947
 
870
- if (!name || !policyId || !loadoutId) {
871
- throw new Error('kit add requires --name --policy-id --loadout-id');
948
+ if (!name) {
949
+ throw new Error('kit add requires --name');
950
+ }
951
+ if (!policyId && !loadoutId) {
952
+ throw new Error('kit add requires at least one of --policy-id / --loadout-id');
872
953
  }
873
954
 
874
955
  const kit = service.addKit({
875
956
  name,
876
957
  description: description || undefined,
877
- policyId,
878
- loadoutId,
958
+ policyId: policyId || undefined,
959
+ loadoutId: loadoutId || undefined,
879
960
  });
880
961
  console.log(`Kit created: ${kit.id} (${kit.name})`);
881
962
  return;
@@ -919,6 +1000,8 @@ async function handleKit(commandArgs) {
919
1000
  const agentName = readArgValue(rest, '--agent');
920
1001
  const mode = normalizeKitMode(readArgValue(rest, '--mode'));
921
1002
  const overwriteAgentsMd = hasFlag(rest, '--overwrite-agents-md');
1003
+ const includeSkills = readArgValues(rest, '--with');
1004
+ const excludeSkills = readArgValues(rest, '--without');
922
1005
 
923
1006
  if (!kitId || !projectPath || !agentName) {
924
1007
  throw new Error('kit apply requires --id --project --agent');
@@ -930,12 +1013,15 @@ async function handleKit(commandArgs) {
930
1013
  agentName,
931
1014
  mode,
932
1015
  overwriteAgentsMd,
1016
+ includeSkills,
1017
+ excludeSkills,
933
1018
  });
934
1019
 
935
1020
  const successCount = result.loadoutResults.filter((item) => item.status === 'success').length;
1021
+ const policySummary = result.policyPath ? `, policy=${result.policyPath}` : '';
936
1022
  console.log(
937
1023
  `Kit applied: ${result.kitId} -> ${result.projectPath} (${result.agentName}), ` +
938
- `skills=${successCount}, policy=${result.policyPath}`
1024
+ `skills=${successCount}${policySummary}`
939
1025
  );
940
1026
  return;
941
1027
  }
@@ -944,6 +1030,97 @@ async function handleKit(commandArgs) {
944
1030
  }
945
1031
  }
946
1032
 
1033
+ async function handleOfficial(commandArgs) {
1034
+ const subcommand = commandArgs[0];
1035
+ if (!subcommand) {
1036
+ throw new Error('Missing official subcommand. Try: official list|install');
1037
+ }
1038
+
1039
+ const service = await loadKitService();
1040
+
1041
+ switch (subcommand) {
1042
+ case 'list': {
1043
+ const presets = await service.listOfficialPresets();
1044
+ if (presets.length === 0) {
1045
+ console.log('No official presets found.');
1046
+ return;
1047
+ }
1048
+ presets.forEach(printOfficialPresetRow);
1049
+ return;
1050
+ }
1051
+ case 'search': {
1052
+ const query = commandArgs.slice(1).join(' ').trim();
1053
+ const presets = await service.searchOfficialPresets({
1054
+ query,
1055
+ });
1056
+ if (presets.length === 0) {
1057
+ console.log('No official presets matched.');
1058
+ return;
1059
+ }
1060
+ presets.forEach(printOfficialPresetRow);
1061
+ return;
1062
+ }
1063
+ case 'inspect': {
1064
+ const rest = commandArgs.slice(1);
1065
+ const id = readArgValue(rest, '--id');
1066
+
1067
+ if (!id) {
1068
+ throw new Error('official inspect requires --id');
1069
+ }
1070
+
1071
+ const preset = await service.getOfficialPreset({
1072
+ id,
1073
+ });
1074
+
1075
+ console.log(`${preset.id} | ${preset.name}`);
1076
+ if (preset.description) {
1077
+ console.log(preset.description);
1078
+ }
1079
+ console.log(`Policy: ${preset.policy.name}`);
1080
+ if (preset.policy.description) {
1081
+ console.log(`Policy description: ${preset.policy.description}`);
1082
+ }
1083
+ console.log(`Policy template: ${preset.policy.template}`);
1084
+ console.log(`Total selected skills: ${preset.skillCount}`);
1085
+ console.log('Sources:');
1086
+ for (const source of preset.sources) {
1087
+ console.log(`- ${source.id} | ${source.name}`);
1088
+ console.log(` url: ${source.url}`);
1089
+ if (source.description) {
1090
+ console.log(` description: ${source.description}`);
1091
+ }
1092
+ console.log(` skills: ${source.selectedSkills.join(', ')}`);
1093
+ }
1094
+ return;
1095
+ }
1096
+ case 'install': {
1097
+ const rest = commandArgs.slice(1);
1098
+ const id = readArgValue(rest, '--id');
1099
+ const overwrite = hasFlag(rest, '--yes') || hasFlag(rest, '-y');
1100
+
1101
+ if (!id) {
1102
+ throw new Error('official install requires --id');
1103
+ }
1104
+
1105
+ const result = await service.installOfficialPreset({
1106
+ id,
1107
+ overwrite,
1108
+ });
1109
+
1110
+ console.log(`Official preset installed: ${result.preset.id} (${result.preset.name})`);
1111
+ console.log(`Policy: ${result.policy.id} (${result.policy.name})`);
1112
+ console.log(`Loadout: ${result.loadout.id} (${result.loadout.name}) skills=${result.loadout.items.length}`);
1113
+ console.log(`Kit: ${result.kit.id} (${result.kit.name})`);
1114
+ console.log(
1115
+ `Sources imported: ${result.importedSources.map((source) => `${source.name}:${source.selectedSkillCount}`).join(', ')}`
1116
+ );
1117
+ return;
1118
+ }
1119
+ default:
1120
+ throw new Error(`Unknown official subcommand: ${subcommand}`);
1121
+ }
1122
+ }
1123
+
947
1124
  async function handleProvider(commandArgs) {
948
1125
  const subcommand = commandArgs[0];
949
1126
  if (!subcommand) {
@@ -1470,19 +1647,20 @@ const DEFAULT_AGENTS = [
1470
1647
  { name: 'Antigravity', globalPath: path.join(os.homedir(), '.gemini/antigravity/skills'), projectPath: '.agent/skills', enabled: true, isCustom: false },
1471
1648
  { name: 'Claude Code', globalPath: path.join(os.homedir(), '.claude/skills'), projectPath: '.claude/skills', enabled: true, isCustom: false },
1472
1649
  { name: 'Cursor', globalPath: path.join(os.homedir(), '.cursor/skills'), projectPath: '.cursor/skills', enabled: true, isCustom: false },
1473
- { name: 'OpenCode', globalPath: path.join(os.homedir(), '.config/opencode/skill'), projectPath: '.opencode/skill', enabled: false, isCustom: false },
1650
+ { name: 'OpenClaw', globalPath: path.join(os.homedir(), '.openclaw/skills'), projectPath: 'skills', enabled: false, isCustom: false },
1651
+ { name: 'CodeBuddy', globalPath: path.join(os.homedir(), '.codebuddy/skills'), projectPath: '.codebuddy/skills', enabled: false, isCustom: false },
1652
+ { name: 'OpenCode', globalPath: path.join(os.homedir(), '.config/opencode/skills'), projectPath: '.agents/skills', enabled: false, isCustom: false },
1474
1653
  { name: 'Codex', globalPath: path.join(os.homedir(), '.codex/skills'), projectPath: '.codex/skills', enabled: false, isCustom: false },
1475
- { name: 'Amp', globalPath: path.join(os.homedir(), '.config/agents/skills'), projectPath: '.agents/skills', enabled: false, isCustom: false },
1654
+ { name: 'Kimi Code CLI', globalPath: path.join(os.homedir(), '.config/agents/skills'), projectPath: '.agents/skills', enabled: false, isCustom: false },
1476
1655
  { name: 'Kilo Code', globalPath: path.join(os.homedir(), '.kilocode/skills'), projectPath: '.kilocode/skills', enabled: false, isCustom: false },
1477
- { name: 'Roo Code', globalPath: path.join(os.homedir(), '.roo/skills'), projectPath: '.roo/skills', enabled: false, isCustom: false },
1478
- { name: 'Goose', globalPath: path.join(os.homedir(), '.config/goose/skills'), projectPath: '.goose/skills', enabled: false, isCustom: false },
1656
+ { name: 'Kiro CLI', globalPath: path.join(os.homedir(), '.kiro/skills'), projectPath: '.kiro/skills', enabled: false, isCustom: false },
1479
1657
  { name: 'Gemini CLI', globalPath: path.join(os.homedir(), '.gemini/skills'), projectPath: '.gemini/skills', enabled: false, isCustom: false },
1480
1658
  { name: 'GitHub Copilot', globalPath: path.join(os.homedir(), '.copilot/skills'), projectPath: '.github/skills', enabled: false, isCustom: false },
1481
- { name: 'Clawdbot', globalPath: path.join(os.homedir(), '.clawdbot/skills'), projectPath: 'skills', enabled: false, isCustom: false },
1482
- { name: 'Droid', globalPath: path.join(os.homedir(), '.factory/skills'), projectPath: '.factory/skills', enabled: false, isCustom: false },
1483
1659
  { name: 'Windsurf', globalPath: path.join(os.homedir(), '.codeium/windsurf/skills'), projectPath: '.windsurf/skills', enabled: false, isCustom: false },
1484
1660
  { name: 'Trae', globalPath: path.join(os.homedir(), '.trae/skills'), projectPath: '.trae/skills', enabled: false, isCustom: false },
1661
+ { name: 'Trae CN', globalPath: path.join(os.homedir(), '.trae-cn/skills'), projectPath: '.trae/skills', enabled: false, isCustom: false },
1485
1662
  { name: 'Qoder', globalPath: path.join(os.homedir(), '.qoder/skills'), projectPath: '.qoder/skills', enabled: false, isCustom: false },
1663
+ { name: 'Qwen Code', globalPath: path.join(os.homedir(), '.qwen/skills'), projectPath: '.qwen/skills', enabled: false, isCustom: false },
1486
1664
  ];
1487
1665
 
1488
1666
  const DEFAULT_CONFIG = {