agent-skills-standard 1.3.2 → 1.4.1

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 (47) hide show
  1. package/README.md +48 -4
  2. package/dist/commands/__tests__/InitCommand.spec.d.ts +1 -0
  3. package/dist/commands/__tests__/InitCommand.spec.js +66 -0
  4. package/dist/commands/__tests__/ListSkillsCommand.spec.d.ts +1 -0
  5. package/dist/commands/__tests__/ListSkillsCommand.spec.js +51 -0
  6. package/dist/commands/__tests__/SyncCommand.spec.d.ts +1 -0
  7. package/dist/commands/__tests__/SyncCommand.spec.js +70 -0
  8. package/dist/commands/init.d.ts +4 -2
  9. package/dist/commands/init.js +17 -42
  10. package/dist/commands/list-skills.d.ts +6 -2
  11. package/dist/commands/list-skills.js +19 -61
  12. package/dist/commands/sync.d.ts +6 -4
  13. package/dist/commands/sync.js +15 -128
  14. package/dist/constants/enums.d.ts +4 -1
  15. package/dist/constants/enums.js +3 -0
  16. package/dist/constants/index.d.ts +1 -0
  17. package/dist/constants/index.js +57 -0
  18. package/dist/constants/skills.js +77 -0
  19. package/dist/index.js +1 -1
  20. package/dist/models/types.d.ts +8 -1
  21. package/dist/services/ConfigService.d.ts +6 -0
  22. package/dist/services/ConfigService.js +36 -0
  23. package/dist/services/DetectionService.d.ts +7 -0
  24. package/dist/services/DetectionService.js +157 -39
  25. package/dist/services/GithubService.d.ts +4 -0
  26. package/dist/services/GithubService.js +17 -6
  27. package/dist/services/InitService.d.ts +37 -0
  28. package/dist/services/InitService.js +73 -0
  29. package/dist/services/RegistryService.d.ts +1 -1
  30. package/dist/services/RegistryService.js +21 -8
  31. package/dist/services/SkillService.d.ts +8 -0
  32. package/dist/services/SkillService.js +31 -0
  33. package/dist/services/SkillValidator.js +8 -1
  34. package/dist/services/SyncService.d.ts +26 -0
  35. package/dist/services/SyncService.js +197 -0
  36. package/dist/services/__tests__/ConfigService.spec.js +135 -0
  37. package/dist/services/__tests__/DetectionService.spec.js +301 -0
  38. package/dist/services/__tests__/GithubService.spec.js +26 -0
  39. package/dist/services/__tests__/InitService.spec.d.ts +1 -0
  40. package/dist/services/__tests__/InitService.spec.js +84 -0
  41. package/dist/services/__tests__/RegistryService.spec.js +56 -0
  42. package/dist/services/__tests__/SkillService.spec.d.ts +1 -0
  43. package/dist/services/__tests__/SkillService.spec.js +54 -0
  44. package/dist/services/__tests__/SkillValidator.spec.js +104 -3
  45. package/dist/services/__tests__/SyncService.spec.d.ts +1 -0
  46. package/dist/services/__tests__/SyncService.spec.js +237 -0
  47. package/package.json +1 -1
package/README.md CHANGED
@@ -33,6 +33,36 @@ Modern AI coding agents are powerful, but they have major flaws:
33
33
 
34
34
  ---
35
35
 
36
+ ## šŸ›  The Solution: Digital DNA for AI
37
+
38
+ Agent Skills Standard treats instructions as **versioned dependencies**, much like software libraries.
39
+
40
+ - **šŸŽÆ Smart Loading**: We use a "Search-on-Demand" pattern. The AI only looks at detailed examples when it specifically needs them, saving its memory for your actual code.
41
+ - **šŸš€ High-Density Language**: We use a specialized "Compressed Syntax" that is **40% more efficient** than normal English. This means the AI understands more while using fewer resources.
42
+ - **šŸ” One-Click Sync**: A single command ensures your AI tool stays up-to-date with your team's latest standards.
43
+
44
+ ---
45
+
46
+ ## ✨ Features
47
+
48
+ - **šŸ›”ļø Multi-Agent Support**: Out-of-the-box mapping for Cursor, Claude Dev, GitHub Copilot, and more.
49
+ - **šŸ“¦ Modular Registry**: Don't load everything. Only enable the skills your project actually uses (e.g., `Flutter + BLoC + Clean Architecture`).
50
+ - **šŸ”„ Dynamic Re-detection**: Automatically re-enables excluded skills if matching dependencies (like `Retrofit`, `Room`, or `Navigation`) are added to your project.
51
+ - **šŸ”’ Secure Overrides**: Lock specific files in your project so they never get overwritten by the central registry.
52
+ - **šŸ“Š Semantic Tagging**: Skills are tagged with triggers that tell the AI _exactly_ when to apply them.
53
+
54
+ ---
55
+
56
+ ## ļæ½ Token Economy & Optimization
57
+
58
+ To ensure AI efficiency, this project follows a strict **Token Economy**. Every skill is audited for its footprint in the AI's context window.
59
+
60
+ ### šŸ“ Our Standards
61
+
62
+ - **High-Density**: Core rules in `SKILL.md` are kept under **70 lines**.
63
+ - **Efficiency**: Target **< 500 tokens** per primary skill file.
64
+ - **Progressive Disclosure**: Heavy examples, checklists, and implementation guides are moved to the `references/` directory and are only loaded by the agent when specific context matches.
65
+
36
66
  ## šŸš€ Installation
37
67
 
38
68
  You can run the tool instantly without installing, or install it globally for convenience:
@@ -83,15 +113,24 @@ skills:
83
113
  ref: flutter-v1.1.0
84
114
  # 🚫 Exclude specific sub-skills from being synced
85
115
  exclude: ['getx-navigation']
116
+ # āž• Include specific skills (supports cross-category 'category/skill' or 'category/*' syntax)
117
+ include:
118
+ - 'bloc-state-management'
119
+ - 'react/hooks'
120
+ - 'common/*'
86
121
  # šŸ”’ Protect local modifications from being overwritten
87
122
  custom_overrides: ['bloc-state-management']
88
123
  ```
89
124
 
90
125
  ### Key Options
91
126
 
92
- - **`exclude`**: A list of skill IDs to skip during synchronization. Useful if you want the main framework guidelines but use a different library for a specific sub-task (e.g., excluding `auto-route` if you use `go_router`).
93
- - **`custom_overrides`**: A list of skill IDs that the CLI should **never** overwrite. Use this when you have modified a standard skill locally to fit your project's unique needs. The CLI will detect these and preserve your local version.
94
- - **`ref`**: Specify a specific version or tag of the skills from the registry.
127
+ - **`exclude`**: A list of skill IDs to skip during synchronization.
128
+ - **`include`**: A list of skill IDs to fetch. Supports:
129
+ - **Relative Path**: `bloc-state-management` (from current category)
130
+ - **Absolute Path**: `react/hooks` (pull specific skill from another category)
131
+ - **Glob Path**: `common/*` (pull ALL skills from another category)
132
+ - **`custom_overrides`**: A list of skill IDs that the CLI should **never** overwrite.
133
+ - **`ref`**: Specify a specific version or tag for the skills.
95
134
 
96
135
  ---
97
136
 
@@ -115,7 +154,12 @@ The CLI connects to the [Official Skills Registry](https://github.com/HoangNguye
115
154
  - **React**: Hooks, Patterns, Performance.
116
155
  - **NestJS**: Architecture, Microservices, Security.
117
156
  - **Next.js**: App Router, RSC, FSD Architecture.
118
- - **Golang**: (Coming Soon)
157
+ - **Golang**: Clean Architecture, API Design, Concurrency.
158
+ - **Angular**: Standalone, Signals, Control Flow.
159
+ - **Kotlin**: Idiomatic Patterns, Coroutines, Flow.
160
+ - **Spring Boot**: Architecture, Security, Data, Test.
161
+ - **Android Native**: Architecture, Compose, DI, Performance, Testing, WorkManager.
162
+ - **iOS Native**: Architecture, UI/Layout, Lifecycle, Security, Performance.
119
163
 
120
164
  ---
121
165
 
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const fs_extra_1 = __importDefault(require("fs-extra"));
7
+ const inquirer_1 = __importDefault(require("inquirer"));
8
+ const vitest_1 = require("vitest");
9
+ const init_1 = require("../init");
10
+ vitest_1.vi.mock('fs-extra');
11
+ vitest_1.vi.mock('inquirer');
12
+ (0, vitest_1.describe)('InitCommand', () => {
13
+ let initCommand;
14
+ let mockInitService;
15
+ let mockRegistryService;
16
+ (0, vitest_1.beforeEach)(() => {
17
+ vitest_1.vi.clearAllMocks();
18
+ // Create mock instances
19
+ mockInitService = {
20
+ getInitializationContext: vitest_1.vi.fn().mockResolvedValue({}),
21
+ getPromptChoices: vitest_1.vi.fn().mockReturnValue({
22
+ frameworkChoices: [],
23
+ agentChoices: [],
24
+ defaultFramework: 'flutter',
25
+ }),
26
+ buildAndSaveConfig: vitest_1.vi.fn().mockResolvedValue(undefined),
27
+ };
28
+ mockRegistryService = {
29
+ discoverRegistry: vitest_1.vi
30
+ .fn()
31
+ .mockResolvedValue({ categories: [], metadata: {} }),
32
+ };
33
+ initCommand = new init_1.InitCommand(mockInitService, mockRegistryService);
34
+ vitest_1.vi.mocked(inquirer_1.default.prompt).mockImplementation(async (questions) => {
35
+ const q = Array.isArray(questions) ? questions[0] : questions;
36
+ if (q.name === 'overwrite')
37
+ return { overwrite: true };
38
+ return { framework: 'flutter', agents: ['cursor'], registry: 'url' };
39
+ });
40
+ // Fix pathExists mock to avoid 'void' assignment lints
41
+ vitest_1.vi.mocked(fs_extra_1.default.pathExists).mockImplementation(() => Promise.resolve(false));
42
+ });
43
+ (0, vitest_1.describe)('run', () => {
44
+ (0, vitest_1.it)('should run initialization flow', async () => {
45
+ const consoleSpy = vitest_1.vi.spyOn(console, 'log').mockImplementation(() => { });
46
+ await initCommand.run();
47
+ (0, vitest_1.expect)(mockInitService.getInitializationContext).toHaveBeenCalled();
48
+ (0, vitest_1.expect)(mockInitService.buildAndSaveConfig).toHaveBeenCalled();
49
+ (0, vitest_1.expect)(consoleSpy).toHaveBeenCalledWith(vitest_1.expect.stringContaining('Initialized .skillsrc'));
50
+ consoleSpy.mockRestore();
51
+ });
52
+ (0, vitest_1.it)('should abort if config exists and user says no', async () => {
53
+ vitest_1.vi.mocked(fs_extra_1.default.pathExists).mockImplementation(() => Promise.resolve(true));
54
+ vitest_1.vi.mocked(inquirer_1.default.prompt).mockResolvedValue({ overwrite: false });
55
+ const consoleSpy = vitest_1.vi.spyOn(console, 'log').mockImplementation(() => { });
56
+ await initCommand.run();
57
+ (0, vitest_1.expect)(consoleSpy).toHaveBeenCalledWith(vitest_1.expect.stringContaining('Aborted'));
58
+ (0, vitest_1.expect)(mockInitService.buildAndSaveConfig).not.toHaveBeenCalled();
59
+ consoleSpy.mockRestore();
60
+ });
61
+ (0, vitest_1.it)('should use default services if not provided', () => {
62
+ const command = new init_1.InitCommand();
63
+ (0, vitest_1.expect)(command).toBeDefined();
64
+ });
65
+ });
66
+ });
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const inquirer_1 = __importDefault(require("inquirer"));
7
+ const vitest_1 = require("vitest");
8
+ const list_skills_1 = require("../list-skills");
9
+ vitest_1.vi.mock('inquirer');
10
+ (0, vitest_1.describe)('ListSkillsCommand', () => {
11
+ let listSkillsCommand;
12
+ let mockConfigService;
13
+ let mockDetectionService;
14
+ let mockSkillService;
15
+ (0, vitest_1.beforeEach)(() => {
16
+ vitest_1.vi.clearAllMocks();
17
+ mockConfigService = {
18
+ getRegistryUrl: vitest_1.vi
19
+ .fn()
20
+ .mockResolvedValue('https://github.com/owner/repo'),
21
+ };
22
+ mockDetectionService = {
23
+ getProjectDeps: vitest_1.vi.fn().mockResolvedValue(new Set(['dep1'])),
24
+ };
25
+ mockSkillService = {
26
+ getSkillsWithStatus: vitest_1.vi.fn().mockResolvedValue([
27
+ { name: 'skill1', status: 'detected' },
28
+ { name: 'skill2', status: 'not-detected' },
29
+ { name: 'skill3', status: 'no-rule' },
30
+ ]),
31
+ };
32
+ listSkillsCommand = new list_skills_1.ListSkillsCommand(mockConfigService, mockDetectionService, mockSkillService);
33
+ vitest_1.vi.mocked(inquirer_1.default.prompt).mockResolvedValue({ framework: 'flutter' });
34
+ });
35
+ (0, vitest_1.describe)('run', () => {
36
+ (0, vitest_1.it)('should prompt user and display skills', async () => {
37
+ const consoleSpy = vitest_1.vi.spyOn(console, 'log').mockImplementation(() => { });
38
+ await listSkillsCommand.run();
39
+ (0, vitest_1.expect)(inquirer_1.default.prompt).toHaveBeenCalled();
40
+ (0, vitest_1.expect)(mockSkillService.getSkillsWithStatus).toHaveBeenCalledWith('flutter', vitest_1.expect.any(String), vitest_1.expect.any(Set));
41
+ (0, vitest_1.expect)(consoleSpy).toHaveBeenCalledWith(vitest_1.expect.stringContaining('- skill1'));
42
+ (0, vitest_1.expect)(consoleSpy).toHaveBeenCalledWith(vitest_1.expect.stringContaining('- skill2'));
43
+ (0, vitest_1.expect)(consoleSpy).toHaveBeenCalledWith(vitest_1.expect.stringContaining('- skill3'));
44
+ consoleSpy.mockRestore();
45
+ });
46
+ (0, vitest_1.it)('should use default services if not provided', () => {
47
+ const command = new list_skills_1.ListSkillsCommand();
48
+ (0, vitest_1.expect)(command).toBeDefined();
49
+ });
50
+ });
51
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const sync_1 = require("../sync");
5
+ (0, vitest_1.describe)('SyncCommand', () => {
6
+ let syncCommand;
7
+ let mockConfigService;
8
+ let mockDetectionService;
9
+ let mockSyncService;
10
+ (0, vitest_1.beforeEach)(() => {
11
+ vitest_1.vi.clearAllMocks();
12
+ mockConfigService = {
13
+ loadConfig: vitest_1.vi.fn(),
14
+ saveConfig: vitest_1.vi.fn(),
15
+ };
16
+ mockDetectionService = {
17
+ getProjectDeps: vitest_1.vi.fn().mockResolvedValue(new Set()),
18
+ };
19
+ mockSyncService = {
20
+ reconcileConfig: vitest_1.vi.fn(),
21
+ assembleSkills: vitest_1.vi.fn(),
22
+ writeSkills: vitest_1.vi.fn(),
23
+ checkForUpdates: vitest_1.vi.fn().mockImplementation((c) => Promise.resolve(c)),
24
+ };
25
+ syncCommand = new sync_1.SyncCommand(mockConfigService, mockDetectionService, mockSyncService);
26
+ });
27
+ (0, vitest_1.describe)('run', () => {
28
+ (0, vitest_1.it)('should fail if .skillsrc is not found', async () => {
29
+ vitest_1.vi.mocked(mockConfigService.loadConfig).mockResolvedValue(null);
30
+ const consoleSpy = vitest_1.vi.spyOn(console, 'log').mockImplementation(() => { });
31
+ await syncCommand.run();
32
+ (0, vitest_1.expect)(consoleSpy).toHaveBeenCalledWith(vitest_1.expect.stringContaining('.skillsrc not found'));
33
+ consoleSpy.mockRestore();
34
+ });
35
+ (0, vitest_1.it)('should run successfully if config is valid', async () => {
36
+ const config = { registry: 'url', skills: { flutter: {} } };
37
+ vitest_1.vi.mocked(mockConfigService.loadConfig).mockResolvedValue(config);
38
+ vitest_1.vi.mocked(mockSyncService.assembleSkills).mockResolvedValue([]);
39
+ const consoleSpy = vitest_1.vi.spyOn(console, 'log').mockImplementation(() => { });
40
+ await syncCommand.run();
41
+ (0, vitest_1.expect)(mockSyncService.reconcileConfig).toHaveBeenCalled();
42
+ (0, vitest_1.expect)(mockSyncService.assembleSkills).toHaveBeenCalled();
43
+ (0, vitest_1.expect)(mockSyncService.writeSkills).toHaveBeenCalled();
44
+ (0, vitest_1.expect)(consoleSpy).toHaveBeenCalledWith(vitest_1.expect.stringContaining('All skills synced successfully!'));
45
+ consoleSpy.mockRestore();
46
+ });
47
+ (0, vitest_1.it)('should catch and log errors', async () => {
48
+ vitest_1.vi.mocked(mockConfigService.loadConfig).mockRejectedValue(new Error('Test error'));
49
+ const consoleSpy = vitest_1.vi
50
+ .spyOn(console, 'error')
51
+ .mockImplementation(() => { });
52
+ await syncCommand.run();
53
+ (0, vitest_1.expect)(consoleSpy).toHaveBeenCalledWith(vitest_1.expect.stringContaining('Sync failed:'), 'Test error');
54
+ consoleSpy.mockRestore();
55
+ });
56
+ (0, vitest_1.it)('should catch and log non-Error objects', async () => {
57
+ vitest_1.vi.mocked(mockConfigService.loadConfig).mockRejectedValue('String error');
58
+ const consoleSpy = vitest_1.vi
59
+ .spyOn(console, 'error')
60
+ .mockImplementation(() => { });
61
+ await syncCommand.run();
62
+ (0, vitest_1.expect)(consoleSpy).toHaveBeenCalledWith(vitest_1.expect.stringContaining('Sync failed:'), 'String error');
63
+ consoleSpy.mockRestore();
64
+ });
65
+ (0, vitest_1.it)('should use default services if not provided', () => {
66
+ const command = new sync_1.SyncCommand();
67
+ (0, vitest_1.expect)(command).toBeDefined();
68
+ });
69
+ });
70
+ });
@@ -1,6 +1,8 @@
1
+ import { InitService } from '../services/InitService';
2
+ import { RegistryService } from '../services/RegistryService';
1
3
  export declare class InitCommand {
2
- private detectionService;
4
+ private initService;
3
5
  private registryService;
4
- private configService;
6
+ constructor(initService?: InitService, registryService?: RegistryService);
5
7
  run(): Promise<void>;
6
8
  }
@@ -6,32 +6,19 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.InitCommand = void 0;
7
7
  const fs_extra_1 = __importDefault(require("fs-extra"));
8
8
  const inquirer_1 = __importDefault(require("inquirer"));
9
- const js_yaml_1 = __importDefault(require("js-yaml"));
10
9
  const path_1 = __importDefault(require("path"));
11
10
  const picocolors_1 = __importDefault(require("picocolors"));
12
11
  const constants_1 = require("../constants");
13
- const ConfigService_1 = require("../services/ConfigService");
14
- const DetectionService_1 = require("../services/DetectionService");
12
+ const InitService_1 = require("../services/InitService");
15
13
  const RegistryService_1 = require("../services/RegistryService");
16
14
  class InitCommand {
17
- constructor() {
18
- this.detectionService = new DetectionService_1.DetectionService();
19
- this.registryService = new RegistryService_1.RegistryService();
20
- this.configService = new ConfigService_1.ConfigService();
15
+ constructor(initService, registryService) {
16
+ this.initService = initService || new InitService_1.InitService();
17
+ this.registryService = registryService || new RegistryService_1.RegistryService();
21
18
  }
22
19
  async run() {
23
20
  const configPath = path_1.default.join(process.cwd(), '.skillsrc');
24
- const detectionResults = await this.detectionService.detectFrameworks();
25
- const agentDetection = await this.detectionService.detectAgents();
26
- const anyAgentDetected = Object.values(agentDetection).some((d) => d);
27
- const shouldCheck = (id) => anyAgentDetected ? agentDetection[id] : true;
28
- const { categories: supportedCategories, metadata } = await this.registryService.discoverRegistry(constants_1.DEFAULT_REGISTER);
29
- const choices = constants_1.SUPPORTED_FRAMEWORKS.map((f) => ({
30
- name: supportedCategories.includes(f.id)
31
- ? f.name
32
- : `${f.name} (Coming Soon)`,
33
- value: f.id,
34
- }));
21
+ // 1. Check for existing config
35
22
  if (await fs_extra_1.default.pathExists(configPath)) {
36
23
  const { overwrite } = await inquirer_1.default.prompt([
37
24
  {
@@ -46,24 +33,25 @@ class InitCommand {
46
33
  return;
47
34
  }
48
35
  }
36
+ // 2. Gather context data
37
+ const context = await this.initService.getInitializationContext();
38
+ const { categories, metadata } = await this.registryService.discoverRegistry(constants_1.DEFAULT_REGISTER);
39
+ // 3. Prepare Choices
40
+ const { frameworkChoices, agentChoices, defaultFramework } = this.initService.getPromptChoices(context, categories);
41
+ // 4. Prompt User
49
42
  const answers = await inquirer_1.default.prompt([
50
43
  {
51
44
  type: 'list',
52
45
  name: 'framework',
53
46
  message: 'Select Framework:',
54
- choices: choices,
55
- default: constants_1.SUPPORTED_FRAMEWORKS.find((f) => detectionResults[f.id])?.id ||
56
- 'flutter',
47
+ choices: frameworkChoices,
48
+ default: defaultFramework,
57
49
  },
58
50
  {
59
51
  type: 'checkbox',
60
52
  name: 'agents',
61
53
  message: 'Select AI Agents you use:',
62
- choices: constants_1.SUPPORTED_AGENTS.map((a) => ({
63
- name: `${a.name} (${a.path}/)`,
64
- value: a.id,
65
- checked: shouldCheck(a.id),
66
- })),
54
+ choices: agentChoices,
67
55
  },
68
56
  {
69
57
  type: 'input',
@@ -72,23 +60,10 @@ class InitCommand {
72
60
  default: constants_1.DEFAULT_REGISTER,
73
61
  },
74
62
  ]);
75
- const frameworkId = answers.framework;
76
- const frameworkDef = constants_1.SUPPORTED_FRAMEWORKS.find((f) => f.id === frameworkId);
77
- const config = this.configService.buildInitialConfig(frameworkId, answers.agents, answers.registry, metadata, frameworkDef?.languages || []);
78
- const projectDeps = await this.detectionService.getProjectDeps();
79
- this.configService.applyDependencyExclusions(config, frameworkId, projectDeps);
80
- const commentHeader = `# Auto-detected configuration generated by agent-skills-standard init
81
- #
82
- # Presence in 'skills' list = Active. To disable a skill, remove it from the list.
83
- # 'exclude': IDs of sub-skills to skip during sync (auto-populated with undetected skills).
84
- # 'custom_overrides': IDs of skills to PROTECT. Use this if you have modified a standard
85
- # skill locally and don't want the CLI to overwrite it.
86
- #
87
- # Run 'ags list-skills' to view all available skills.
88
- `;
89
- await fs_extra_1.default.writeFile(configPath, commentHeader + js_yaml_1.default.dump(config));
63
+ // 5. Build and Save
64
+ await this.initService.buildAndSaveConfig(answers, metadata);
90
65
  console.log(picocolors_1.default.green('\nāœ… Initialized .skillsrc with your preferences!'));
91
- console.log(picocolors_1.default.gray(` Selected framework: ${frameworkId}`));
66
+ console.log(picocolors_1.default.gray(` Selected framework: ${answers.framework}`));
92
67
  console.log(picocolors_1.default.cyan('\nNext step: Run `agent-skills-standard sync` to generate rule files.'));
93
68
  }
94
69
  }
@@ -1,6 +1,10 @@
1
+ import { ConfigService } from '../services/ConfigService';
2
+ import { DetectionService } from '../services/DetectionService';
3
+ import { SkillService } from '../services/SkillService';
1
4
  export declare class ListSkillsCommand {
5
+ private configService;
2
6
  private detectionService;
3
- private githubService;
7
+ private skillService;
8
+ constructor(configService?: ConfigService, detectionService?: DetectionService, skillService?: SkillService);
4
9
  run(): Promise<void>;
5
- private parseRegistryUrl;
6
10
  }
@@ -4,20 +4,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.ListSkillsCommand = void 0;
7
- const fs_extra_1 = __importDefault(require("fs-extra"));
8
7
  const inquirer_1 = __importDefault(require("inquirer"));
9
- const js_yaml_1 = __importDefault(require("js-yaml"));
10
- const path_1 = __importDefault(require("path"));
11
8
  const picocolors_1 = __importDefault(require("picocolors"));
12
9
  const constants_1 = require("../constants");
10
+ const ConfigService_1 = require("../services/ConfigService");
13
11
  const DetectionService_1 = require("../services/DetectionService");
14
- const GithubService_1 = require("../services/GithubService");
12
+ const SkillService_1 = require("../services/SkillService");
15
13
  class ListSkillsCommand {
16
- constructor() {
17
- this.detectionService = new DetectionService_1.DetectionService();
18
- this.githubService = new GithubService_1.GithubService(process.env.GITHUB_TOKEN);
14
+ constructor(configService, detectionService, skillService) {
15
+ this.configService = configService || new ConfigService_1.ConfigService();
16
+ this.detectionService = detectionService || new DetectionService_1.DetectionService();
17
+ this.skillService = skillService || new SkillService_1.SkillService();
19
18
  }
20
19
  async run() {
20
+ // 1. Get framework choice from user
21
21
  const choices = constants_1.SUPPORTED_FRAMEWORKS.map((f) => ({
22
22
  name: f.name,
23
23
  value: f.id,
@@ -31,64 +31,22 @@ class ListSkillsCommand {
31
31
  default: choices.find((c) => c.value === constants_1.Framework.Flutter)?.value,
32
32
  },
33
33
  ]);
34
+ // 2. Load context
34
35
  const projectDeps = await this.detectionService.getProjectDeps();
36
+ const registryUrl = await this.configService.getRegistryUrl();
35
37
  console.log(picocolors_1.default.green(`\nAvailable skills for ${framework}:`));
36
- // Determine registry URL (from .skillsrc or default)
37
- const defaultRegistry = 'https://github.com/HoangNguyen0403/agent-skills-standard';
38
- let registryUrl = defaultRegistry;
39
- const configPath = path_1.default.join(process.cwd(), '.skillsrc');
40
- if (await fs_extra_1.default.pathExists(configPath)) {
41
- const raw = await fs_extra_1.default.readFile(configPath, 'utf8');
42
- const conf = js_yaml_1.default.load(raw);
43
- if (conf && conf.registry)
44
- registryUrl = conf.registry;
45
- }
46
- // Try to fetch the repo tree to list all skills under skills/<framework>/
47
- let skillFolders = [];
48
- try {
49
- const parsed = this.parseRegistryUrl(registryUrl);
50
- if (parsed) {
51
- // We assume 'main' branch if not specified (legacy behavior of fetchRepoTree handled default,
52
- // but for listing skills, 'main' is a safe default for now or we could fetch repo info)
53
- // I'll stick to 'main' to keep it simple as in RegistryService refactor.
54
- const treeResult = await this.githubService.getRepoTree(parsed.owner, parsed.repo, 'main');
55
- if (treeResult && Array.isArray(treeResult.tree)) {
56
- const allFiles = treeResult.tree || [];
57
- skillFolders = Array.from(new Set(allFiles
58
- .filter((f) => f.path.startsWith(`skills/${framework}/`))
59
- .map((f) => {
60
- const parts = f.path.split('/');
61
- return parts[2];
62
- })
63
- .filter(Boolean)));
64
- }
65
- }
66
- }
67
- catch {
68
- // ignore network errors and fall back to detection registry
69
- }
70
- // Fallback: use detection registry skill ids if we couldn't fetch tree
71
- const detectSkills = constants_1.SKILL_DETECTION_REGISTRY[framework] || [];
72
- if (!skillFolders || skillFolders.length === 0) {
73
- skillFolders = detectSkills.map((s) => s.id);
74
- }
75
- // For each discovered skill, show whether it would be detected (if we have rules)
76
- for (const skillName of skillFolders.sort()) {
77
- const detectRule = detectSkills.find((x) => x.id === skillName);
78
- let mark = picocolors_1.default.yellow('no-rule');
79
- if (detectRule) {
80
- const present = detectRule.packages.some((p) => projectDeps.has(p));
81
- mark = present ? picocolors_1.default.green('detected') : picocolors_1.default.gray('not-detected');
82
- }
83
- console.log(`- ${skillName} (${mark})`);
38
+ // 3. Fetch skills with status
39
+ const skills = await this.skillService.getSkillsWithStatus(framework, registryUrl, projectDeps);
40
+ // 4. Output results
41
+ for (const skill of skills) {
42
+ const mark = skill.status === 'no-rule'
43
+ ? picocolors_1.default.yellow('no-rule')
44
+ : skill.status === 'detected'
45
+ ? picocolors_1.default.green('detected')
46
+ : picocolors_1.default.gray('not-detected');
47
+ console.log(`- ${skill.name} (${mark})`);
84
48
  }
85
49
  console.log('\nTip: Use the .skillsrc exclude array to disable/enable sub-skills before running sync.');
86
50
  }
87
- parseRegistryUrl(registryUrl) {
88
- const m = registryUrl.match(/github\.com\/([^/]+)\/([^/]+)/i);
89
- if (!m)
90
- return null;
91
- return { owner: m[1], repo: m[2].replace(/\.git$/, '') };
92
- }
93
51
  }
94
52
  exports.ListSkillsCommand = ListSkillsCommand;
@@ -1,8 +1,10 @@
1
+ import { ConfigService } from '../services/ConfigService';
2
+ import { DetectionService } from '../services/DetectionService';
3
+ import { SyncService } from '../services/SyncService';
1
4
  export declare class SyncCommand {
2
5
  private configService;
3
- private githubService;
6
+ private detectionService;
7
+ private syncService;
8
+ constructor(configService?: ConfigService, detectionService?: DetectionService, syncService?: SyncService);
4
9
  run(): Promise<void>;
5
- private checkForUpdates;
6
- private assembleFromRemote;
7
- private writeToTargets;
8
10
  }