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.
- package/README.md +48 -4
- package/dist/commands/__tests__/InitCommand.spec.d.ts +1 -0
- package/dist/commands/__tests__/InitCommand.spec.js +66 -0
- package/dist/commands/__tests__/ListSkillsCommand.spec.d.ts +1 -0
- package/dist/commands/__tests__/ListSkillsCommand.spec.js +51 -0
- package/dist/commands/__tests__/SyncCommand.spec.d.ts +1 -0
- package/dist/commands/__tests__/SyncCommand.spec.js +70 -0
- package/dist/commands/init.d.ts +4 -2
- package/dist/commands/init.js +17 -42
- package/dist/commands/list-skills.d.ts +6 -2
- package/dist/commands/list-skills.js +19 -61
- package/dist/commands/sync.d.ts +6 -4
- package/dist/commands/sync.js +15 -128
- package/dist/constants/enums.d.ts +4 -1
- package/dist/constants/enums.js +3 -0
- package/dist/constants/index.d.ts +1 -0
- package/dist/constants/index.js +57 -0
- package/dist/constants/skills.js +77 -0
- package/dist/index.js +1 -1
- package/dist/models/types.d.ts +8 -1
- package/dist/services/ConfigService.d.ts +6 -0
- package/dist/services/ConfigService.js +36 -0
- package/dist/services/DetectionService.d.ts +7 -0
- package/dist/services/DetectionService.js +157 -39
- package/dist/services/GithubService.d.ts +4 -0
- package/dist/services/GithubService.js +17 -6
- package/dist/services/InitService.d.ts +37 -0
- package/dist/services/InitService.js +73 -0
- package/dist/services/RegistryService.d.ts +1 -1
- package/dist/services/RegistryService.js +21 -8
- package/dist/services/SkillService.d.ts +8 -0
- package/dist/services/SkillService.js +31 -0
- package/dist/services/SkillValidator.js +8 -1
- package/dist/services/SyncService.d.ts +26 -0
- package/dist/services/SyncService.js +197 -0
- package/dist/services/__tests__/ConfigService.spec.js +135 -0
- package/dist/services/__tests__/DetectionService.spec.js +301 -0
- package/dist/services/__tests__/GithubService.spec.js +26 -0
- package/dist/services/__tests__/InitService.spec.d.ts +1 -0
- package/dist/services/__tests__/InitService.spec.js +84 -0
- package/dist/services/__tests__/RegistryService.spec.js +56 -0
- package/dist/services/__tests__/SkillService.spec.d.ts +1 -0
- package/dist/services/__tests__/SkillService.spec.js +54 -0
- package/dist/services/__tests__/SkillValidator.spec.js +104 -3
- package/dist/services/__tests__/SyncService.spec.d.ts +1 -0
- package/dist/services/__tests__/SyncService.spec.js +237 -0
- 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.
|
|
93
|
-
- **`
|
|
94
|
-
-
|
|
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**:
|
|
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 @@
|
|
|
1
|
+
export {};
|
|
@@ -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
|
+
});
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { InitService } from '../services/InitService';
|
|
2
|
+
import { RegistryService } from '../services/RegistryService';
|
|
1
3
|
export declare class InitCommand {
|
|
2
|
-
private
|
|
4
|
+
private initService;
|
|
3
5
|
private registryService;
|
|
4
|
-
|
|
6
|
+
constructor(initService?: InitService, registryService?: RegistryService);
|
|
5
7
|
run(): Promise<void>;
|
|
6
8
|
}
|
package/dist/commands/init.js
CHANGED
|
@@ -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
|
|
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.
|
|
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
|
-
|
|
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:
|
|
55
|
-
default:
|
|
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:
|
|
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
|
-
|
|
76
|
-
|
|
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: ${
|
|
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
|
|
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
|
|
12
|
+
const SkillService_1 = require("../services/SkillService");
|
|
15
13
|
class ListSkillsCommand {
|
|
16
|
-
constructor() {
|
|
17
|
-
this.
|
|
18
|
-
this.
|
|
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
|
-
//
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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;
|
package/dist/commands/sync.d.ts
CHANGED
|
@@ -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
|
|
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
|
}
|