@emmraan/ai-skills 0.0.3 → 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 (41) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +41 -0
  3. package/dist/commands/install.d.ts +10 -1
  4. package/dist/commands/install.d.ts.map +1 -1
  5. package/dist/commands/install.js +123 -5
  6. package/dist/commands/install.js.map +1 -1
  7. package/dist/commands/remove.d.ts +10 -1
  8. package/dist/commands/remove.d.ts.map +1 -1
  9. package/dist/commands/remove.js +126 -6
  10. package/dist/commands/remove.js.map +1 -1
  11. package/dist/core/config.d.ts +13 -2
  12. package/dist/core/config.d.ts.map +1 -1
  13. package/dist/core/config.js +28 -4
  14. package/dist/core/config.js.map +1 -1
  15. package/dist/core/installer.d.ts +3 -2
  16. package/dist/core/installer.d.ts.map +1 -1
  17. package/dist/core/installer.js +10 -8
  18. package/dist/core/installer.js.map +1 -1
  19. package/dist/core/lockfile.d.ts +1 -0
  20. package/dist/core/lockfile.d.ts.map +1 -1
  21. package/dist/core/lockfile.js +10 -0
  22. package/dist/core/lockfile.js.map +1 -1
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +30 -8
  25. package/dist/index.js.map +1 -1
  26. package/dist/utils/logger.d.ts.map +1 -1
  27. package/dist/utils/logger.js +5 -4
  28. package/dist/utils/logger.js.map +1 -1
  29. package/dist/utils/ui.d.ts +9 -0
  30. package/dist/utils/ui.d.ts.map +1 -0
  31. package/dist/utils/ui.js +48 -0
  32. package/dist/utils/ui.js.map +1 -0
  33. package/package.json +6 -1
  34. package/src/commands/install.ts +149 -5
  35. package/src/commands/remove.ts +156 -6
  36. package/src/core/config.ts +50 -4
  37. package/src/core/installer.ts +18 -9
  38. package/src/core/lockfile.ts +15 -0
  39. package/src/index.ts +31 -8
  40. package/src/utils/logger.ts +6 -4
  41. package/src/utils/ui.ts +57 -0
@@ -1 +1 @@
1
- {"version":3,"file":"lockfile.js","sourceRoot":"","sources":["../../src/core/lockfile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAC/B,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAe9C,MAAM,gBAAgB,GAAa;IACjC,OAAO,EAAE,CAAC;IACV,eAAe,EAAE,EAAE;CACpB,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,MAAM,IAAI,GAAG,eAAe,EAAE,CAAC;IAC/B,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC9C,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,gBAAgB,CAAC;IAC1B,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,QAAkB;IACpD,MAAM,IAAI,GAAG,eAAe,EAAE,CAAC;IAC/B,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,MAAM,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AACpE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,SAAiB,EACjB,OAAe,EACf,IAAY,EACZ,YAAsB;IAEtB,MAAM,QAAQ,GAAG,MAAM,YAAY,EAAE,CAAC;IACtC,QAAQ,CAAC,eAAe,CAAC,SAAS,CAAC,GAAG;QACpC,IAAI,EAAE,SAAS;QACf,OAAO;QACP,IAAI;QACJ,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,YAAY;KACb,CAAC;IACF,MAAM,aAAa,CAAC,QAAQ,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,SAAiB;IAC7D,MAAM,QAAQ,GAAG,MAAM,YAAY,EAAE,CAAC;IACtC,OAAO,QAAQ,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC;IAC3C,MAAM,aAAa,CAAC,QAAQ,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,MAAM,QAAQ,GAAG,MAAM,YAAY,EAAE,CAAC;IACtC,OAAO,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,SAAiB;IACnD,MAAM,QAAQ,GAAG,MAAM,YAAY,EAAE,CAAC;IACtC,OAAO,QAAQ,CAAC,eAAe,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC;AACrD,CAAC"}
1
+ {"version":3,"file":"lockfile.js","sourceRoot":"","sources":["../../src/core/lockfile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAC/B,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAe9C,MAAM,gBAAgB,GAAa;IACjC,OAAO,EAAE,CAAC;IACV,eAAe,EAAE,EAAE;CACpB,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,MAAM,IAAI,GAAG,eAAe,EAAE,CAAC;IAC/B,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC9C,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,gBAAgB,CAAC;IAC1B,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,QAAkB;IACpD,MAAM,IAAI,GAAG,eAAe,EAAE,CAAC;IAC/B,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,MAAM,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AACpE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,SAAiB,EACjB,OAAe,EACf,IAAY,EACZ,YAAsB;IAEtB,MAAM,QAAQ,GAAG,MAAM,YAAY,EAAE,CAAC;IACtC,QAAQ,CAAC,eAAe,CAAC,SAAS,CAAC,GAAG;QACpC,IAAI,EAAE,SAAS;QACf,OAAO;QACP,IAAI;QACJ,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,YAAY;KACb,CAAC;IACF,MAAM,aAAa,CAAC,QAAQ,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,SAAiB;IAC7D,MAAM,QAAQ,GAAG,MAAM,YAAY,EAAE,CAAC;IACtC,OAAO,QAAQ,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC;IAC3C,MAAM,aAAa,CAAC,QAAQ,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iCAAiC,CACrD,SAAiB,EACjB,YAAsB;IAEtB,MAAM,QAAQ,GAAG,MAAM,YAAY,EAAE,CAAC;IACtC,MAAM,KAAK,GAAG,QAAQ,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC;IAClD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO;IACT,CAAC;IAED,KAAK,CAAC,YAAY,GAAG,YAAY,CAAC;IAClC,KAAK,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC3C,MAAM,aAAa,CAAC,QAAQ,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,MAAM,QAAQ,GAAG,MAAM,YAAY,EAAE,CAAC;IACtC,OAAO,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,SAAiB;IACnD,MAAM,QAAQ,GAAG,MAAM,YAAY,EAAE,CAAC;IACtC,OAAO,QAAQ,CAAC,eAAe,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC;AACrD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CA6C1C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAqD1C"}
package/dist/index.js CHANGED
@@ -1,11 +1,13 @@
1
- import { install } from './commands/install.js';
2
- import { remove } from './commands/remove.js';
1
+ import { install, parseInstallFlags, resolveInstallOptionsFromFlags } from './commands/install.js';
2
+ import { remove, parseRemoveFlags, resolveRemoveOptionsFromFlags } from './commands/remove.js';
3
3
  import { list } from './commands/list.js';
4
4
  import { update } from './commands/update.js';
5
5
  import { generateLocal } from './commands/generate-local.js';
6
6
  import { error, info } from './utils/logger.js';
7
+ import { ui } from './utils/ui.js';
7
8
  const args = process.argv.slice(2);
8
9
  export async function main() {
10
+ ui.printBanner();
9
11
  if (args.length === 0) {
10
12
  showHelp();
11
13
  return;
@@ -14,12 +16,16 @@ export async function main() {
14
16
  try {
15
17
  if (command === 'install' && args.length >= 2) {
16
18
  const skill = args[1];
17
- const success = await install(skill);
19
+ const flags = parseInstallFlags(args.slice(2));
20
+ const options = await resolveInstallOptionsFromFlags(flags, true);
21
+ const success = await install(skill, options);
18
22
  process.exit(success ? 0 : 1);
19
23
  }
20
24
  else if (command === 'remove' && args.length >= 2) {
21
25
  const skill = args[1];
22
- const success = await remove(skill);
26
+ const flags = parseRemoveFlags(args.slice(2));
27
+ const options = await resolveRemoveOptionsFromFlags(flags, true);
28
+ const success = await remove(skill, options);
23
29
  process.exit(success ? 0 : 1);
24
30
  }
25
31
  else if (command === 'list') {
@@ -44,7 +50,9 @@ export async function main() {
44
50
  }
45
51
  else if (!command.startsWith('-')) {
46
52
  // Default behavior: treat first arg as skill name to install
47
- const success = await install(command);
53
+ const flags = parseInstallFlags(args.slice(1));
54
+ const options = await resolveInstallOptionsFromFlags(flags, true);
55
+ const success = await install(command, options);
48
56
  process.exit(success ? 0 : 1);
49
57
  }
50
58
  else {
@@ -61,16 +69,30 @@ function showHelp() {
61
69
  AI Skills CLI - Install framework-agnostic SKILLS.md files
62
70
 
63
71
  Usage:
64
- ai-skills <skill> Install a skill
65
- ai-skills install <skill> Install a skill
72
+ ai-skills <skill> [options] Install a skill
73
+ ai-skills install <skill> [options] Install a skill
66
74
  ai-skills list [--json] List installed and available skills
67
- ai-skills remove <skill> Remove an installed skill
75
+ ai-skills remove <skill> [options] Remove an installed skill
68
76
  ai-skills update [--force] Update all installed skills
69
77
  [--skill <name>] Update a specific skill
70
78
  ai-skills generate-local [skill] Run local backend generator
71
79
 
80
+ Install options:
81
+ --local Install in current project (.ai-skills/skills)
82
+ --global Install in home agent folders
83
+ --all Install to all global platforms
84
+ --platform <a,b,c> Install to selected global platforms
85
+
86
+ Remove options:
87
+ --local Remove from current project platforms
88
+ --global Remove from home agent platforms
89
+ --all Remove from all platforms for selected scope
90
+ --platform <a,b,c> Remove from selected platforms for selected scope
91
+
72
92
  Examples:
73
93
  ai-skills react
94
+ ai-skills react --local
95
+ ai-skills react --platform claude,gemini,opencode,vscode,codex,agents
74
96
  ai-skills install typescript
75
97
  ai-skills list
76
98
  ai-skills remove python
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAEA,wBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,QAElC;AAED,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,QAEnC;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,QAEtC;AAED,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,QAEpC;AAED,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,QAEnC"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAIA,wBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,QAElC;AAED,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,QAEnC;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,QAEtC;AAED,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,QAEpC;AAED,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,QAEnC"}
@@ -1,17 +1,18 @@
1
1
  /* eslint-disable no-console */
2
+ import chalk from 'chalk';
2
3
  export function log(message) {
3
4
  console.log(message);
4
5
  }
5
6
  export function info(message) {
6
- console.log(`ℹ ${message}`);
7
+ console.log(`${chalk.cyan('ℹ')} ${chalk.gray(message)}`);
7
8
  }
8
9
  export function success(message) {
9
- console.log(`✓ ${message}`);
10
+ console.log(`${chalk.green('✓')} ${chalk.green(message)}`);
10
11
  }
11
12
  export function error(message) {
12
- console.error(`✗ ${message}`);
13
+ console.error(`${chalk.red('✗')} ${chalk.red(message)}`);
13
14
  }
14
15
  export function warn(message) {
15
- console.warn(`⚠ ${message}`);
16
+ console.warn(`${chalk.yellow('⚠')} ${chalk.yellow(message)}`);
16
17
  }
17
18
  //# sourceMappingURL=logger.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAE/B,MAAM,UAAU,GAAG,CAAC,OAAe;IACjC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,OAAe;IAClC,OAAO,CAAC,GAAG,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,OAAe;IACrC,OAAO,CAAC,GAAG,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,OAAe;IACnC,OAAO,CAAC,KAAK,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,OAAe;IAClC,OAAO,CAAC,IAAI,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC;AAC/B,CAAC"}
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAE/B,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,UAAU,GAAG,CAAC,OAAe;IACjC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,OAAe;IAClC,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,OAAe;IACrC,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,OAAe;IACnC,OAAO,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,OAAe;IAClC,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;AAChE,CAAC"}
@@ -0,0 +1,9 @@
1
+ export declare const ui: {
2
+ bannerShown: boolean;
3
+ printBanner(): void;
4
+ startSpinner(text: string): void;
5
+ stopSpinnerSuccess(text?: string): void;
6
+ stopSpinnerFail(text?: string): void;
7
+ stopSpinner(): void;
8
+ };
9
+ //# sourceMappingURL=ui.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../src/utils/ui.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,EAAE;;mBAGE,IAAI;uBAYA,MAAM,GAAG,IAAI;8BAWN,MAAM,GAAG,IAAI;2BAQhB,MAAM,GAAG,IAAI;mBAQrB,IAAI;CAOpB,CAAC"}
@@ -0,0 +1,48 @@
1
+ import chalk from 'chalk';
2
+ import ora from 'ora';
3
+ import { info } from '../utils/logger.js';
4
+ let activeSpinner = null;
5
+ export const ui = {
6
+ bannerShown: false,
7
+ printBanner() {
8
+ if (this.bannerShown || !process.stdout.isTTY) {
9
+ return;
10
+ }
11
+ const title = chalk.bold.cyan('AI Skills CLI');
12
+ const subtitle = chalk.gray('Install framework-agnostic SKILLS.md files');
13
+ // Subtle, single-line banner to keep npx output concise
14
+ info(`${title} ${chalk.gray('•')} ${subtitle}`);
15
+ this.bannerShown = true;
16
+ },
17
+ startSpinner(text) {
18
+ if (!process.stdout.isTTY) {
19
+ return;
20
+ }
21
+ if (activeSpinner) {
22
+ activeSpinner.stop();
23
+ }
24
+ activeSpinner = ora({ text, spinner: 'dots' }).start();
25
+ },
26
+ stopSpinnerSuccess(text) {
27
+ if (!activeSpinner) {
28
+ return;
29
+ }
30
+ activeSpinner.succeed(text);
31
+ activeSpinner = null;
32
+ },
33
+ stopSpinnerFail(text) {
34
+ if (!activeSpinner) {
35
+ return;
36
+ }
37
+ activeSpinner.fail(text);
38
+ activeSpinner = null;
39
+ },
40
+ stopSpinner() {
41
+ if (!activeSpinner) {
42
+ return;
43
+ }
44
+ activeSpinner.stop();
45
+ activeSpinner = null;
46
+ },
47
+ };
48
+ //# sourceMappingURL=ui.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui.js","sourceRoot":"","sources":["../../src/utils/ui.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,GAAiB,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAE1C,IAAI,aAAa,GAAe,IAAI,CAAC;AAErC,MAAM,CAAC,MAAM,EAAE,GAAG;IAChB,WAAW,EAAE,KAAK;IAElB,WAAW;QACT,IAAI,IAAI,CAAC,WAAW,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YAC9C,OAAO;QACT,CAAC;QAED,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAC/C,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;QAC1E,wDAAwD;QACxD,IAAI,CAAC,GAAG,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,QAAQ,EAAE,CAAC,CAAC;QAChD,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;IAC1B,CAAC;IAED,YAAY,CAAC,IAAY;QACvB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YAC1B,OAAO;QACT,CAAC;QAED,IAAI,aAAa,EAAE,CAAC;YAClB,aAAa,CAAC,IAAI,EAAE,CAAC;QACvB,CAAC;QACD,aAAa,GAAG,GAAG,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;IACzD,CAAC;IAED,kBAAkB,CAAC,IAAa;QAC9B,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,OAAO;QACT,CAAC;QACD,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC5B,aAAa,GAAG,IAAI,CAAC;IACvB,CAAC;IAED,eAAe,CAAC,IAAa;QAC3B,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,OAAO;QACT,CAAC;QACD,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzB,aAAa,GAAG,IAAI,CAAC;IACvB,CAAC;IAED,WAAW;QACT,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,OAAO;QACT,CAAC;QACD,aAAa,CAAC,IAAI,EAAE,CAAC;QACrB,aAAa,GAAG,IAAI,CAAC;IACvB,CAAC;CACF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@emmraan/ai-skills",
3
- "version": "0.0.3",
3
+ "version": "1.0.0",
4
4
  "description": "CLI tool for installing AI Skills across agent platforms",
5
5
  "type": "module",
6
6
  "bin": {
@@ -23,6 +23,11 @@
23
23
  "typecheck": "tsc --noEmit --project tsconfig.json",
24
24
  "clean": "rm -rf dist"
25
25
  },
26
+ "dependencies": {
27
+ "chalk": "^5.4.1",
28
+ "inquirer": "^12.9.6",
29
+ "ora": "^8.2.0"
30
+ },
26
31
  "devDependencies": {
27
32
  "@ai-skills/shared-types": "workspace:*",
28
33
  "@types/node": "^20.10.0",
@@ -1,30 +1,174 @@
1
1
  import { fetchSkill, fetchRegistryIndex } from '../core/downloader.js';
2
2
  import { installSkill } from '../core/installer.js';
3
3
  import { addSkillToLockfile } from '../core/lockfile.js';
4
+ import { AGENT_PLATFORMS, type InstallOptions, type InstallLocation } from '../core/config.js';
4
5
  import { sha256 } from '../utils/hash.js';
5
- import { error, success, info } from '../utils/logger.js';
6
+ import { error, success } from '../utils/logger.js';
7
+ import inquirer from 'inquirer';
8
+ import { ui } from '../utils/ui.js';
6
9
 
7
- export async function install(skillName: string): Promise<boolean> {
10
+ export interface CliInstallFlags {
11
+ local?: boolean;
12
+ global?: boolean;
13
+ all?: boolean;
14
+ platforms?: string[];
15
+ }
16
+
17
+ export function parseInstallFlags(args: string[]): CliInstallFlags {
18
+ const flags: CliInstallFlags = {};
19
+
20
+ for (let i = 0; i < args.length; i++) {
21
+ const arg = args[i];
22
+ if (arg === '--local') {
23
+ flags.local = true;
24
+ } else if (arg === '--global') {
25
+ flags.global = true;
26
+ } else if (arg === '--all') {
27
+ flags.all = true;
28
+ } else if (arg === '--platform') {
29
+ const value = args[i + 1];
30
+ if (!value || value.startsWith('--')) {
31
+ throw new Error('Missing value for --platform. Example: --platform claude,gemini');
32
+ }
33
+ i++;
34
+ const parsed = value
35
+ .split(',')
36
+ .map((p) => p.trim())
37
+ .filter(Boolean);
38
+ flags.platforms = [...(flags.platforms ?? []), ...parsed];
39
+ }
40
+ }
41
+
42
+ return flags;
43
+ }
44
+
45
+ export async function resolveInstallOptionsFromFlags(
46
+ flags: CliInstallFlags,
47
+ interactive: boolean
48
+ ): Promise<InstallOptions> {
49
+ if (flags.local && flags.global) {
50
+ throw new Error('Use only one of --local or --global');
51
+ }
52
+
53
+ const hasAnyFlag = Boolean(
54
+ flags.local || flags.global || flags.all || (flags.platforms ?? []).length
55
+ );
56
+
57
+ if (flags.local) {
58
+ return {
59
+ location: 'local',
60
+ all: flags.all || !flags.platforms || flags.platforms.length === 0,
61
+ platforms: flags.platforms,
62
+ };
63
+ }
64
+
65
+ if (flags.global) {
66
+ return {
67
+ location: 'global',
68
+ all: flags.all || !flags.platforms || flags.platforms.length === 0,
69
+ platforms: flags.platforms,
70
+ };
71
+ }
72
+
73
+ if ((flags.platforms ?? []).length > 0 || flags.all) {
74
+ return {
75
+ location: 'global',
76
+ all: flags.all,
77
+ platforms: flags.platforms,
78
+ };
79
+ }
80
+
81
+ if (!hasAnyFlag) {
82
+ if (interactive && process.stdin.isTTY && process.stdout.isTTY) {
83
+ return promptInstallOptions();
84
+ }
85
+ return { location: 'global', all: true };
86
+ }
87
+
88
+ return { location: 'global', all: true };
89
+ }
90
+
91
+ async function promptInstallOptions(): Promise<InstallOptions> {
92
+ const { location } = await inquirer.prompt<{ location: InstallLocation }>([
93
+ {
94
+ type: 'list',
95
+ name: 'location',
96
+ message: 'Install location',
97
+ choices: [
98
+ { name: 'Local (current project)', value: 'local' },
99
+ { name: 'Global (agent platforms)', value: 'global' },
100
+ ],
101
+ default: 'global',
102
+ },
103
+ ]);
104
+
105
+ const { target } = await inquirer.prompt<{ target: 'all' | 'specific' }>([
106
+ {
107
+ type: 'list',
108
+ name: 'target',
109
+ message: `${location === 'local' ? 'Local' : 'Global'} install target`,
110
+ choices: [
111
+ { name: 'All platforms', value: 'all' },
112
+ { name: 'Specific platforms', value: 'specific' },
113
+ ],
114
+ default: 'all',
115
+ },
116
+ ]);
117
+
118
+ if (target === 'all') {
119
+ return { location, all: true };
120
+ }
121
+
122
+ const platformChoices = AGENT_PLATFORMS.map((platform) => {
123
+ const value = platform.replace(/^\./, '');
124
+ return { name: value, value };
125
+ });
126
+
127
+ const { platforms } = await inquirer.prompt<{ platforms: string[] }>([
128
+ {
129
+ type: 'checkbox',
130
+ name: 'platforms',
131
+ message: 'Select target platforms',
132
+ choices: platformChoices,
133
+ pageSize: 10,
134
+ validate: (value: string[]) =>
135
+ value.length > 0 ? true : 'Please select at least one platform.',
136
+ },
137
+ ]);
138
+
139
+ return {
140
+ location,
141
+ all: false,
142
+ platforms,
143
+ };
144
+ }
145
+
146
+ export async function install(skillName: string, options: InstallOptions = {}): Promise<boolean> {
8
147
  try {
9
- info(`Fetching ${skillName}...`);
148
+ ui.startSpinner(`Fetching ${skillName}...`);
10
149
  const skillContent = await fetchSkill(skillName);
150
+ ui.stopSpinnerSuccess(`Fetched ${skillName}`);
11
151
  const hash = sha256(skillContent);
12
152
 
13
- info(`Installing ${skillName}...`);
14
- const installPaths = await installSkill(skillName, skillContent);
153
+ ui.startSpinner(`Installing ${skillName}...`);
154
+ const installPaths = await installSkill(skillName, skillContent, options);
155
+ ui.stopSpinnerSuccess(`Installed files for ${skillName}`);
15
156
 
16
157
  if (installPaths.length === 0) {
17
158
  error(`Failed to install ${skillName} to any location`);
18
159
  return false;
19
160
  }
20
161
 
162
+ ui.startSpinner(`Updating lockfile for ${skillName}...`);
21
163
  const index = await fetchRegistryIndex();
22
164
  const skillVersion = index.skills[skillName]?.version || 'unknown';
23
165
 
24
166
  await addSkillToLockfile(skillName, skillVersion, hash, installPaths);
167
+ ui.stopSpinnerSuccess(`Updated lockfile for ${skillName}`);
25
168
  success(`Installed ${skillName} (${skillVersion}) to ${installPaths.length} location(s)`);
26
169
  return true;
27
170
  } catch (err) {
171
+ ui.stopSpinner();
28
172
  error(`Failed to install ${skillName}: ${err instanceof Error ? err.message : String(err)}`);
29
173
  return false;
30
174
  }
@@ -1,8 +1,149 @@
1
1
  import { removeSkill } from '../core/installer.js';
2
- import { removeSkillFromLockfile, getSkillEntry } from '../core/lockfile.js';
2
+ import {
3
+ removeSkillFromLockfile,
4
+ getSkillEntry,
5
+ updateSkillInstallPathsInLockfile,
6
+ } from '../core/lockfile.js';
7
+ import { AGENT_PLATFORMS, type InstallLocation, type InstallOptions } from '../core/config.js';
3
8
  import { error, success, info } from '../utils/logger.js';
9
+ import { ui } from '../utils/ui.js';
10
+ import inquirer from 'inquirer';
4
11
 
5
- export async function remove(skillName: string): Promise<boolean> {
12
+ export interface CliRemoveFlags {
13
+ local?: boolean;
14
+ global?: boolean;
15
+ all?: boolean;
16
+ platforms?: string[];
17
+ }
18
+
19
+ export function parseRemoveFlags(args: string[]): CliRemoveFlags {
20
+ const flags: CliRemoveFlags = {};
21
+
22
+ for (let i = 0; i < args.length; i++) {
23
+ const arg = args[i];
24
+ if (arg === '--local') {
25
+ flags.local = true;
26
+ } else if (arg === '--global') {
27
+ flags.global = true;
28
+ } else if (arg === '--all') {
29
+ flags.all = true;
30
+ } else if (arg === '--platform') {
31
+ const value = args[i + 1];
32
+ if (!value || value.startsWith('--')) {
33
+ throw new Error('Missing value for --platform. Example: --platform claude,gemini');
34
+ }
35
+ i++;
36
+ const parsed = value
37
+ .split(',')
38
+ .map((p) => p.trim())
39
+ .filter(Boolean);
40
+ flags.platforms = [...(flags.platforms ?? []), ...parsed];
41
+ }
42
+ }
43
+
44
+ return flags;
45
+ }
46
+
47
+ export async function resolveRemoveOptionsFromFlags(
48
+ flags: CliRemoveFlags,
49
+ interactive: boolean
50
+ ): Promise<InstallOptions> {
51
+ if (flags.local && flags.global) {
52
+ throw new Error('Use only one of --local or --global');
53
+ }
54
+
55
+ const hasAnyFlag = Boolean(
56
+ flags.local || flags.global || flags.all || (flags.platforms ?? []).length
57
+ );
58
+
59
+ if (flags.local) {
60
+ return {
61
+ location: 'local',
62
+ all: flags.all || !flags.platforms || flags.platforms.length === 0,
63
+ platforms: flags.platforms,
64
+ };
65
+ }
66
+
67
+ if (flags.global) {
68
+ return {
69
+ location: 'global',
70
+ all: flags.all || !flags.platforms || flags.platforms.length === 0,
71
+ platforms: flags.platforms,
72
+ };
73
+ }
74
+
75
+ if ((flags.platforms ?? []).length > 0 || flags.all) {
76
+ return {
77
+ location: 'global',
78
+ all: flags.all,
79
+ platforms: flags.platforms,
80
+ };
81
+ }
82
+
83
+ if (!hasAnyFlag) {
84
+ if (interactive && process.stdin.isTTY && process.stdout.isTTY) {
85
+ return promptRemoveOptions();
86
+ }
87
+ return { location: 'global', all: true };
88
+ }
89
+
90
+ return { location: 'global', all: true };
91
+ }
92
+
93
+ async function promptRemoveOptions(): Promise<InstallOptions> {
94
+ const { location } = await inquirer.prompt<{ location: InstallLocation }>([
95
+ {
96
+ type: 'list',
97
+ name: 'location',
98
+ message: 'Remove from location',
99
+ choices: [
100
+ { name: 'Local (current project)', value: 'local' },
101
+ { name: 'Global (agent platforms)', value: 'global' },
102
+ ],
103
+ default: 'global',
104
+ },
105
+ ]);
106
+
107
+ const { target } = await inquirer.prompt<{ target: 'all' | 'specific' }>([
108
+ {
109
+ type: 'list',
110
+ name: 'target',
111
+ message: `${location === 'local' ? 'Local' : 'Global'} remove target`,
112
+ choices: [
113
+ { name: 'All platforms', value: 'all' },
114
+ { name: 'Specific platforms', value: 'specific' },
115
+ ],
116
+ default: 'all',
117
+ },
118
+ ]);
119
+
120
+ if (target === 'all') {
121
+ return { location, all: true };
122
+ }
123
+
124
+ const platformChoices = AGENT_PLATFORMS.map((platform) => {
125
+ const value = platform.replace(/^\./, '');
126
+ return { name: value, value };
127
+ });
128
+
129
+ const { platforms } = await inquirer.prompt<{ platforms: string[] }>([
130
+ {
131
+ type: 'checkbox',
132
+ name: 'platforms',
133
+ message: 'Select target platforms',
134
+ choices: platformChoices,
135
+ pageSize: 10,
136
+ },
137
+ ]);
138
+
139
+ return {
140
+ location,
141
+ all: platforms.length === 0,
142
+ platforms,
143
+ };
144
+ }
145
+
146
+ export async function remove(skillName: string, options: InstallOptions = {}): Promise<boolean> {
6
147
  try {
7
148
  const entry = await getSkillEntry(skillName);
8
149
  if (!entry) {
@@ -10,12 +151,21 @@ export async function remove(skillName: string): Promise<boolean> {
10
151
  return true;
11
152
  }
12
153
 
13
- info(`Removing ${skillName}...`);
14
- await removeSkill(skillName);
15
- await removeSkillFromLockfile(skillName);
16
- success(`Removed ${skillName}`);
154
+ ui.startSpinner(`Removing ${skillName}...`);
155
+ const removedPaths = await removeSkill(skillName, options);
156
+
157
+ const remaining = entry.installPaths.filter((p) => !removedPaths.includes(p));
158
+ if (remaining.length === 0) {
159
+ await removeSkillFromLockfile(skillName);
160
+ } else {
161
+ await updateSkillInstallPathsInLockfile(skillName, remaining);
162
+ }
163
+
164
+ ui.stopSpinnerSuccess(`Removed files for ${skillName}`);
165
+ success(`Removed ${skillName} from ${removedPaths.length} location(s)`);
17
166
  return true;
18
167
  } catch (err) {
168
+ ui.stopSpinner();
19
169
  error(`Failed to remove ${skillName}: ${err instanceof Error ? err.message : String(err)}`);
20
170
  return false;
21
171
  }
@@ -8,22 +8,68 @@ export const REGISTRY_INDEX_URL = `${REGISTRY_URL}/skills/.index.json`;
8
8
 
9
9
  export const AGENT_PLATFORMS = ['.claude', '.gemini', '.vscode', '.opencode', '.codex', '.agents'];
10
10
 
11
+ export type InstallLocation = 'local' | 'global';
12
+
13
+ export interface InstallOptions {
14
+ location?: InstallLocation;
15
+ platforms?: string[];
16
+ all?: boolean;
17
+ }
18
+
19
+ export interface ResolvedInstallOptions {
20
+ location: InstallLocation;
21
+ platforms: string[];
22
+ }
23
+
11
24
  export function getAgentPlatformDirs(): string[] {
12
25
  const home = homedir();
13
26
  return AGENT_PLATFORMS.map((p) => join(home, p, 'skills'));
14
27
  }
15
28
 
29
+ export function resolveInstallOptions(options: InstallOptions = {}): ResolvedInstallOptions {
30
+ const location: InstallLocation = options.location ?? 'global';
31
+
32
+ const normalized = (options.platforms ?? []).map((p) => normalizePlatformName(p));
33
+ const unique = [...new Set(normalized)];
34
+
35
+ if (options.all || unique.length === 0) {
36
+ return { location, platforms: [...AGENT_PLATFORMS] };
37
+ }
38
+
39
+ for (const p of unique) {
40
+ if (!AGENT_PLATFORMS.includes(p)) {
41
+ throw new Error(`Unknown platform: ${p}. Supported: ${AGENT_PLATFORMS.join(', ')}`);
42
+ }
43
+ }
44
+
45
+ return { location, platforms: unique };
46
+ }
47
+
48
+ function normalizePlatformName(name: string): string {
49
+ const trimmed = name.trim();
50
+ return trimmed.startsWith('.') ? trimmed : `.${trimmed}`;
51
+ }
52
+
53
+ function getSelectedPlatformDirs(resolved: ResolvedInstallOptions): string[] {
54
+ const baseDir = resolved.location === 'local' ? process.cwd() : homedir();
55
+ return resolved.platforms.map((p) => join(baseDir, p, 'skills'));
56
+ }
57
+
16
58
  export function getLockfilePath(): string {
17
59
  const home = homedir();
18
60
  return join(home, '.ai-skills', '.skill-lock.json');
19
61
  }
20
62
 
21
- export function getSkillInstallPaths(skillName: string): string[] {
22
- return getAgentPlatformDirs().map((dir) => join(dir, skillName, 'SKILLS.md'));
63
+ export function getSkillInstallPaths(skillName: string, options: InstallOptions = {}): string[] {
64
+ const resolved = resolveInstallOptions(options);
65
+ return getSelectedPlatformDirs(resolved).map((dir) => join(dir, skillName, 'SKILLS.md'));
23
66
  }
24
67
 
25
- export function getSkillMetadataPaths(skillName: string): string[] {
26
- return getAgentPlatformDirs().map((dir) => join(dir, skillName, '.skill-metadata.json'));
68
+ export function getSkillMetadataPaths(skillName: string, options: InstallOptions = {}): string[] {
69
+ const resolved = resolveInstallOptions(options);
70
+ return getSelectedPlatformDirs(resolved).map((dir) =>
71
+ join(dir, skillName, '.skill-metadata.json')
72
+ );
27
73
  }
28
74
 
29
75
  export const IS_WINDOWS = platform() === 'win32';