@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,12 +1,16 @@
1
1
  import { writeFile, mkdir, readFile, rm } from 'fs/promises';
2
2
  import { dirname } from 'path';
3
- import { getSkillInstallPaths, getSkillMetadataPaths } from './config.js';
3
+ import { getSkillInstallPaths, getSkillMetadataPaths, type InstallOptions } from './config.js';
4
4
  import { sha256 } from '../utils/hash.js';
5
5
  import { info, warn } from '../utils/logger.js';
6
6
 
7
- export async function installSkill(skillName: string, skillContent: string): Promise<string[]> {
8
- const installPaths = getSkillInstallPaths(skillName);
9
- const metadataPaths = getSkillMetadataPaths(skillName);
7
+ export async function installSkill(
8
+ skillName: string,
9
+ skillContent: string,
10
+ options: InstallOptions = {}
11
+ ): Promise<string[]> {
12
+ const installPaths = getSkillInstallPaths(skillName, options);
13
+ const metadataPaths = getSkillMetadataPaths(skillName, options);
10
14
  const installedPaths: string[] = [];
11
15
  const installedMetadataPaths: string[] = [];
12
16
  const hash = sha256(skillContent);
@@ -50,21 +54,26 @@ export async function installSkill(skillName: string, skillContent: string): Pro
50
54
  return installedPaths;
51
55
  }
52
56
 
53
- export async function removeSkill(skillName: string): Promise<string[]> {
54
- const installPaths = getSkillInstallPaths(skillName);
55
- const metadataPaths = getSkillMetadataPaths(skillName);
57
+ export async function removeSkill(
58
+ skillName: string,
59
+ options: InstallOptions = {}
60
+ ): Promise<string[]> {
61
+ const installPaths = getSkillInstallPaths(skillName, options);
62
+ const metadataPaths = getSkillMetadataPaths(skillName, options);
56
63
  const removedPaths: string[] = [];
57
64
 
58
65
  for (let i = 0; i < installPaths.length; i++) {
59
66
  const skillPath = installPaths[i];
60
67
  const metadataPath = metadataPaths[i];
68
+ const skillDir = dirname(skillPath);
61
69
  try {
62
70
  await rm(skillPath, { force: true });
63
71
  await rm(metadataPath, { force: true });
72
+ await rm(skillDir, { recursive: true, force: true });
64
73
  removedPaths.push(skillPath);
65
- info(`Removed ${skillPath}`);
74
+ info(`Removed ${skillDir}`);
66
75
  } catch (err) {
67
- warn(`Failed to remove ${skillPath}: ${err instanceof Error ? err.message : String(err)}`);
76
+ warn(`Failed to remove ${skillDir}: ${err instanceof Error ? err.message : String(err)}`);
68
77
  }
69
78
  }
70
79
 
@@ -59,6 +59,21 @@ export async function removeSkillFromLockfile(skillName: string): Promise<void>
59
59
  await writeLockfile(lockfile);
60
60
  }
61
61
 
62
+ export async function updateSkillInstallPathsInLockfile(
63
+ skillName: string,
64
+ installPaths: string[]
65
+ ): Promise<void> {
66
+ const lockfile = await readLockfile();
67
+ const entry = lockfile.installedSkills[skillName];
68
+ if (!entry) {
69
+ return;
70
+ }
71
+
72
+ entry.installPaths = installPaths;
73
+ entry.timestamp = new Date().toISOString();
74
+ await writeLockfile(lockfile);
75
+ }
76
+
62
77
  export async function getInstalledSkills(): Promise<LockfileEntry[]> {
63
78
  const lockfile = await readLockfile();
64
79
  return Object.values(lockfile.installedSkills);
package/src/index.ts CHANGED
@@ -1,13 +1,16 @@
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
 
8
9
  const args = process.argv.slice(2);
9
10
 
10
11
  export async function main(): Promise<void> {
12
+ ui.printBanner();
13
+
11
14
  if (args.length === 0) {
12
15
  showHelp();
13
16
  return;
@@ -18,11 +21,15 @@ export async function main(): Promise<void> {
18
21
  try {
19
22
  if (command === 'install' && args.length >= 2) {
20
23
  const skill = args[1];
21
- const success = await install(skill);
24
+ const flags = parseInstallFlags(args.slice(2));
25
+ const options = await resolveInstallOptionsFromFlags(flags, true);
26
+ const success = await install(skill, options);
22
27
  process.exit(success ? 0 : 1);
23
28
  } else if (command === 'remove' && args.length >= 2) {
24
29
  const skill = args[1];
25
- const success = await remove(skill);
30
+ const flags = parseRemoveFlags(args.slice(2));
31
+ const options = await resolveRemoveOptionsFromFlags(flags, true);
32
+ const success = await remove(skill, options);
26
33
  process.exit(success ? 0 : 1);
27
34
  } else if (command === 'list') {
28
35
  const jsonFlag = args.includes('--json');
@@ -43,7 +50,9 @@ export async function main(): Promise<void> {
43
50
  process.exit(success ? 0 : 1);
44
51
  } else if (!command.startsWith('-')) {
45
52
  // Default behavior: treat first arg as skill name to install
46
- 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);
47
56
  process.exit(success ? 0 : 1);
48
57
  } else {
49
58
  showHelp();
@@ -59,16 +68,30 @@ function showHelp() {
59
68
  AI Skills CLI - Install framework-agnostic SKILLS.md files
60
69
 
61
70
  Usage:
62
- ai-skills <skill> Install a skill
63
- ai-skills install <skill> Install a skill
71
+ ai-skills <skill> [options] Install a skill
72
+ ai-skills install <skill> [options] Install a skill
64
73
  ai-skills list [--json] List installed and available skills
65
- ai-skills remove <skill> Remove an installed skill
74
+ ai-skills remove <skill> [options] Remove an installed skill
66
75
  ai-skills update [--force] Update all installed skills
67
76
  [--skill <name>] Update a specific skill
68
77
  ai-skills generate-local [skill] Run local backend generator
69
78
 
79
+ Install options:
80
+ --local Install in current project (.ai-skills/skills)
81
+ --global Install in home agent folders
82
+ --all Install to all global platforms
83
+ --platform <a,b,c> Install to selected global platforms
84
+
85
+ Remove options:
86
+ --local Remove from current project platforms
87
+ --global Remove from home agent platforms
88
+ --all Remove from all platforms for selected scope
89
+ --platform <a,b,c> Remove from selected platforms for selected scope
90
+
70
91
  Examples:
71
92
  ai-skills react
93
+ ai-skills react --local
94
+ ai-skills react --platform claude,gemini,opencode,vscode,codex,agents
72
95
  ai-skills install typescript
73
96
  ai-skills list
74
97
  ai-skills remove python
@@ -1,21 +1,23 @@
1
1
  /* eslint-disable no-console */
2
2
 
3
+ import chalk from 'chalk';
4
+
3
5
  export function log(message: string) {
4
6
  console.log(message);
5
7
  }
6
8
 
7
9
  export function info(message: string) {
8
- console.log(`ℹ ${message}`);
10
+ console.log(`${chalk.cyan('ℹ')} ${chalk.gray(message)}`);
9
11
  }
10
12
 
11
13
  export function success(message: string) {
12
- console.log(`✓ ${message}`);
14
+ console.log(`${chalk.green('✓')} ${chalk.green(message)}`);
13
15
  }
14
16
 
15
17
  export function error(message: string) {
16
- console.error(`✗ ${message}`);
18
+ console.error(`${chalk.red('✗')} ${chalk.red(message)}`);
17
19
  }
18
20
 
19
21
  export function warn(message: string) {
20
- console.warn(`⚠ ${message}`);
22
+ console.warn(`${chalk.yellow('⚠')} ${chalk.yellow(message)}`);
21
23
  }
@@ -0,0 +1,57 @@
1
+ import chalk from 'chalk';
2
+ import ora, { type Ora } from 'ora';
3
+ import { info } from '../utils/logger.js';
4
+
5
+ let activeSpinner: Ora | null = null;
6
+
7
+ export const ui = {
8
+ bannerShown: false,
9
+
10
+ printBanner(): void {
11
+ if (this.bannerShown || !process.stdout.isTTY) {
12
+ return;
13
+ }
14
+
15
+ const title = chalk.bold.cyan('AI Skills CLI');
16
+ const subtitle = chalk.gray('Install framework-agnostic SKILLS.md files');
17
+ // Subtle, single-line banner to keep npx output concise
18
+ info(`${title} ${chalk.gray('•')} ${subtitle}`);
19
+ this.bannerShown = true;
20
+ },
21
+
22
+ startSpinner(text: string): void {
23
+ if (!process.stdout.isTTY) {
24
+ return;
25
+ }
26
+
27
+ if (activeSpinner) {
28
+ activeSpinner.stop();
29
+ }
30
+ activeSpinner = ora({ text, spinner: 'dots' }).start();
31
+ },
32
+
33
+ stopSpinnerSuccess(text?: string): void {
34
+ if (!activeSpinner) {
35
+ return;
36
+ }
37
+ activeSpinner.succeed(text);
38
+ activeSpinner = null;
39
+ },
40
+
41
+ stopSpinnerFail(text?: string): void {
42
+ if (!activeSpinner) {
43
+ return;
44
+ }
45
+ activeSpinner.fail(text);
46
+ activeSpinner = null;
47
+ },
48
+
49
+ stopSpinner(): void {
50
+ if (!activeSpinner) {
51
+ return;
52
+ }
53
+ activeSpinner.stop();
54
+ activeSpinner = null;
55
+ },
56
+ };
57
+