codeep 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.
- package/LICENSE +201 -0
- package/README.md +576 -0
- package/dist/api/index.d.ts +8 -0
- package/dist/api/index.js +421 -0
- package/dist/app.d.ts +2 -0
- package/dist/app.js +1406 -0
- package/dist/components/AgentProgress.d.ts +33 -0
- package/dist/components/AgentProgress.js +97 -0
- package/dist/components/Export.d.ts +8 -0
- package/dist/components/Export.js +27 -0
- package/dist/components/Help.d.ts +2 -0
- package/dist/components/Help.js +3 -0
- package/dist/components/Input.d.ts +9 -0
- package/dist/components/Input.js +89 -0
- package/dist/components/Loading.d.ts +9 -0
- package/dist/components/Loading.js +31 -0
- package/dist/components/Login.d.ts +7 -0
- package/dist/components/Login.js +77 -0
- package/dist/components/Logo.d.ts +8 -0
- package/dist/components/Logo.js +89 -0
- package/dist/components/LogoutPicker.d.ts +8 -0
- package/dist/components/LogoutPicker.js +61 -0
- package/dist/components/Message.d.ts +10 -0
- package/dist/components/Message.js +234 -0
- package/dist/components/MessageList.d.ts +10 -0
- package/dist/components/MessageList.js +8 -0
- package/dist/components/ProjectPermission.d.ts +7 -0
- package/dist/components/ProjectPermission.js +52 -0
- package/dist/components/Search.d.ts +10 -0
- package/dist/components/Search.js +30 -0
- package/dist/components/SessionPicker.d.ts +9 -0
- package/dist/components/SessionPicker.js +88 -0
- package/dist/components/Sessions.d.ts +12 -0
- package/dist/components/Sessions.js +102 -0
- package/dist/components/Settings.d.ts +7 -0
- package/dist/components/Settings.js +162 -0
- package/dist/components/Status.d.ts +2 -0
- package/dist/components/Status.js +12 -0
- package/dist/config/config.test.d.ts +1 -0
- package/dist/config/config.test.js +157 -0
- package/dist/config/index.d.ts +121 -0
- package/dist/config/index.js +555 -0
- package/dist/config/providers.d.ts +43 -0
- package/dist/config/providers.js +82 -0
- package/dist/config/providers.test.d.ts +1 -0
- package/dist/config/providers.test.js +132 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +38 -0
- package/dist/utils/agent.d.ts +37 -0
- package/dist/utils/agent.js +627 -0
- package/dist/utils/codeReview.d.ts +36 -0
- package/dist/utils/codeReview.js +390 -0
- package/dist/utils/context.d.ts +49 -0
- package/dist/utils/context.js +216 -0
- package/dist/utils/diffPreview.d.ts +57 -0
- package/dist/utils/diffPreview.js +335 -0
- package/dist/utils/export.d.ts +19 -0
- package/dist/utils/export.js +94 -0
- package/dist/utils/git.d.ts +85 -0
- package/dist/utils/git.js +399 -0
- package/dist/utils/git.test.d.ts +1 -0
- package/dist/utils/git.test.js +193 -0
- package/dist/utils/history.d.ts +93 -0
- package/dist/utils/history.js +348 -0
- package/dist/utils/interactive.d.ts +34 -0
- package/dist/utils/interactive.js +206 -0
- package/dist/utils/keychain.d.ts +17 -0
- package/dist/utils/keychain.js +160 -0
- package/dist/utils/learning.d.ts +89 -0
- package/dist/utils/learning.js +330 -0
- package/dist/utils/logger.d.ts +33 -0
- package/dist/utils/logger.js +130 -0
- package/dist/utils/project.d.ts +86 -0
- package/dist/utils/project.js +415 -0
- package/dist/utils/project.test.d.ts +1 -0
- package/dist/utils/project.test.js +212 -0
- package/dist/utils/ratelimit.d.ts +26 -0
- package/dist/utils/ratelimit.js +132 -0
- package/dist/utils/ratelimit.test.d.ts +1 -0
- package/dist/utils/ratelimit.test.js +131 -0
- package/dist/utils/retry.d.ts +28 -0
- package/dist/utils/retry.js +109 -0
- package/dist/utils/retry.test.d.ts +1 -0
- package/dist/utils/retry.test.js +163 -0
- package/dist/utils/search.d.ts +11 -0
- package/dist/utils/search.js +29 -0
- package/dist/utils/shell.d.ts +45 -0
- package/dist/utils/shell.js +242 -0
- package/dist/utils/skills.d.ts +144 -0
- package/dist/utils/skills.js +1137 -0
- package/dist/utils/smartContext.d.ts +29 -0
- package/dist/utils/smartContext.js +441 -0
- package/dist/utils/tools.d.ts +224 -0
- package/dist/utils/tools.js +731 -0
- package/dist/utils/update.d.ts +22 -0
- package/dist/utils/update.js +128 -0
- package/dist/utils/validation.d.ts +28 -0
- package/dist/utils/validation.js +141 -0
- package/dist/utils/validation.test.d.ts +1 -0
- package/dist/utils/validation.test.js +164 -0
- package/dist/utils/verify.d.ts +78 -0
- package/dist/utils/verify.js +464 -0
- package/package.json +68 -0
|
@@ -0,0 +1,1137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skills System - predefined workflows and commands
|
|
3
|
+
*/
|
|
4
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync } from 'fs';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
import { homedir } from 'os';
|
|
7
|
+
// Skills directory
|
|
8
|
+
const SKILLS_DIR = join(homedir(), '.codeep', 'skills');
|
|
9
|
+
const SKILLS_HISTORY_FILE = join(homedir(), '.codeep', 'skill-history.json');
|
|
10
|
+
// Built-in skills
|
|
11
|
+
const BUILT_IN_SKILLS = [
|
|
12
|
+
// ==================== GIT SKILLS ====================
|
|
13
|
+
{
|
|
14
|
+
name: 'commit',
|
|
15
|
+
description: 'Generate commit message and commit changes',
|
|
16
|
+
shortcut: 'c',
|
|
17
|
+
category: 'git',
|
|
18
|
+
requiresGit: true,
|
|
19
|
+
parameters: [
|
|
20
|
+
{ name: 'message', description: 'Optional commit message (skips AI generation)', required: false },
|
|
21
|
+
],
|
|
22
|
+
steps: [
|
|
23
|
+
{ type: 'prompt', content: 'Analyze the git diff and generate a conventional commit message following this format: type(scope): description. Types: feat, fix, docs, style, refactor, test, chore. Be concise.' },
|
|
24
|
+
{ type: 'confirm', content: 'Commit with this message?' },
|
|
25
|
+
{ type: 'command', content: 'git add -A && git commit -m "${message}"' },
|
|
26
|
+
{ type: 'notify', content: 'Changes committed successfully!' },
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: 'amend',
|
|
31
|
+
description: 'Amend the last commit',
|
|
32
|
+
category: 'git',
|
|
33
|
+
requiresGit: true,
|
|
34
|
+
steps: [
|
|
35
|
+
{ type: 'command', content: 'git add -A' },
|
|
36
|
+
{ type: 'confirm', content: 'Amend the last commit with staged changes?' },
|
|
37
|
+
{ type: 'command', content: 'git commit --amend --no-edit' },
|
|
38
|
+
{ type: 'notify', content: 'Commit amended!' },
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: 'push',
|
|
43
|
+
description: 'Push changes to remote',
|
|
44
|
+
shortcut: 'p',
|
|
45
|
+
category: 'git',
|
|
46
|
+
requiresGit: true,
|
|
47
|
+
steps: [
|
|
48
|
+
{ type: 'command', content: 'git push' },
|
|
49
|
+
{ type: 'notify', content: 'Changes pushed!' },
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: 'pull',
|
|
54
|
+
description: 'Pull latest changes from remote',
|
|
55
|
+
category: 'git',
|
|
56
|
+
requiresGit: true,
|
|
57
|
+
steps: [
|
|
58
|
+
{ type: 'command', content: 'git pull' },
|
|
59
|
+
{ type: 'notify', content: 'Changes pulled!' },
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: 'pr',
|
|
64
|
+
description: 'Create a pull request description',
|
|
65
|
+
category: 'git',
|
|
66
|
+
requiresGit: true,
|
|
67
|
+
steps: [
|
|
68
|
+
{ type: 'prompt', content: 'Analyze the commits since main/master branch and generate a pull request description. Include: title, summary of changes, any breaking changes, and testing notes.' },
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: 'changelog',
|
|
73
|
+
description: 'Generate changelog from recent commits',
|
|
74
|
+
category: 'git',
|
|
75
|
+
requiresGit: true,
|
|
76
|
+
steps: [
|
|
77
|
+
{ type: 'command', content: 'git log --oneline -20' },
|
|
78
|
+
{ type: 'prompt', content: 'Based on these commits, generate a changelog entry in Keep a Changelog format. Group by: Added, Changed, Fixed, Removed.' },
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: 'branch',
|
|
83
|
+
description: 'Create a new branch with smart naming',
|
|
84
|
+
category: 'git',
|
|
85
|
+
requiresGit: true,
|
|
86
|
+
parameters: [
|
|
87
|
+
{ name: 'description', description: 'What the branch is for', required: true },
|
|
88
|
+
],
|
|
89
|
+
steps: [
|
|
90
|
+
{ type: 'prompt', content: 'Based on the description "${description}", suggest a branch name following convention: type/short-description. Types: feature, fix, hotfix, refactor, chore.' },
|
|
91
|
+
{ type: 'confirm', content: 'Create this branch?' },
|
|
92
|
+
{ type: 'command', content: 'git checkout -b ${branch}' },
|
|
93
|
+
],
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: 'stash',
|
|
97
|
+
description: 'Stash changes with a meaningful message',
|
|
98
|
+
category: 'git',
|
|
99
|
+
requiresGit: true,
|
|
100
|
+
steps: [
|
|
101
|
+
{ type: 'prompt', content: 'Analyze the current changes and suggest a meaningful stash message.' },
|
|
102
|
+
{ type: 'command', content: 'git stash push -m "${message}"' },
|
|
103
|
+
{ type: 'notify', content: 'Changes stashed!' },
|
|
104
|
+
],
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: 'unstash',
|
|
108
|
+
description: 'Apply and drop the most recent stash',
|
|
109
|
+
category: 'git',
|
|
110
|
+
requiresGit: true,
|
|
111
|
+
steps: [
|
|
112
|
+
{ type: 'command', content: 'git stash pop' },
|
|
113
|
+
{ type: 'notify', content: 'Stash applied!' },
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
// ==================== TESTING SKILLS ====================
|
|
117
|
+
{
|
|
118
|
+
name: 'test',
|
|
119
|
+
description: 'Generate tests for current file or function',
|
|
120
|
+
shortcut: 't',
|
|
121
|
+
category: 'testing',
|
|
122
|
+
requiresWriteAccess: true,
|
|
123
|
+
parameters: [
|
|
124
|
+
{ name: 'file', description: 'File or function to test', required: false },
|
|
125
|
+
],
|
|
126
|
+
steps: [
|
|
127
|
+
{ type: 'agent', content: 'Analyze the current file and generate comprehensive unit tests. Use the existing test framework in the project. Cover edge cases and error scenarios. Write tests in a separate test file following project conventions.' },
|
|
128
|
+
],
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
name: 'test-fix',
|
|
132
|
+
description: 'Fix failing tests',
|
|
133
|
+
category: 'testing',
|
|
134
|
+
requiresWriteAccess: true,
|
|
135
|
+
steps: [
|
|
136
|
+
{ type: 'command', content: 'npm test 2>&1 || true' },
|
|
137
|
+
{ type: 'agent', content: 'Analyze the test failures above and fix them. Either fix the tests if they are incorrect, or fix the code if the tests are correct.' },
|
|
138
|
+
],
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: 'coverage',
|
|
142
|
+
description: 'Analyze test coverage and suggest improvements',
|
|
143
|
+
category: 'testing',
|
|
144
|
+
steps: [
|
|
145
|
+
{ type: 'command', content: 'npm run test:coverage 2>&1 || npm test -- --coverage 2>&1 || true' },
|
|
146
|
+
{ type: 'prompt', content: 'Analyze the test coverage report and suggest which files/functions need more tests. Prioritize by importance and complexity.' },
|
|
147
|
+
],
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
name: 'e2e',
|
|
151
|
+
description: 'Generate end-to-end tests',
|
|
152
|
+
category: 'testing',
|
|
153
|
+
requiresWriteAccess: true,
|
|
154
|
+
parameters: [
|
|
155
|
+
{ name: 'feature', description: 'Feature to test', required: true },
|
|
156
|
+
],
|
|
157
|
+
steps: [
|
|
158
|
+
{ type: 'agent', content: 'Generate end-to-end tests for the feature "${feature}". Use Playwright, Cypress, or the e2e framework already in the project. Cover the main user flows and edge cases.' },
|
|
159
|
+
],
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
name: 'mock',
|
|
163
|
+
description: 'Generate mock data for testing',
|
|
164
|
+
category: 'testing',
|
|
165
|
+
requiresWriteAccess: true,
|
|
166
|
+
parameters: [
|
|
167
|
+
{ name: 'type', description: 'Type/interface to mock', required: true },
|
|
168
|
+
],
|
|
169
|
+
steps: [
|
|
170
|
+
{ type: 'agent', content: 'Generate realistic mock data for the type "${type}". Create a mock factory function that can generate multiple variations. Include edge cases (empty strings, null values, special characters).' },
|
|
171
|
+
],
|
|
172
|
+
},
|
|
173
|
+
// ==================== DOCUMENTATION SKILLS ====================
|
|
174
|
+
{
|
|
175
|
+
name: 'docs',
|
|
176
|
+
description: 'Generate documentation for code',
|
|
177
|
+
shortcut: 'd',
|
|
178
|
+
category: 'documentation',
|
|
179
|
+
requiresWriteAccess: true,
|
|
180
|
+
parameters: [
|
|
181
|
+
{ name: 'file', description: 'File to document', required: false },
|
|
182
|
+
],
|
|
183
|
+
steps: [
|
|
184
|
+
{ type: 'agent', content: 'Add JSDoc/docstring documentation to all exported functions, classes, and interfaces in the current file. Include parameter descriptions, return types, and examples where helpful.' },
|
|
185
|
+
],
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
name: 'readme',
|
|
189
|
+
description: 'Generate or update README',
|
|
190
|
+
category: 'documentation',
|
|
191
|
+
requiresWriteAccess: true,
|
|
192
|
+
steps: [
|
|
193
|
+
{ type: 'agent', content: 'Analyze the project structure and generate a comprehensive README.md. Include: project description, installation, usage, configuration, API documentation if applicable, and contributing guidelines.' },
|
|
194
|
+
],
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
name: 'explain',
|
|
198
|
+
description: 'Explain how the code works',
|
|
199
|
+
shortcut: 'e',
|
|
200
|
+
category: 'documentation',
|
|
201
|
+
parameters: [
|
|
202
|
+
{ name: 'file', description: 'File to explain', required: false },
|
|
203
|
+
],
|
|
204
|
+
steps: [
|
|
205
|
+
{ type: 'prompt', content: 'Explain how this code works in detail. Cover: purpose, data flow, key functions, dependencies, and potential gotchas. Use simple language.' },
|
|
206
|
+
],
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: 'api-docs',
|
|
210
|
+
description: 'Generate API documentation',
|
|
211
|
+
category: 'documentation',
|
|
212
|
+
requiresWriteAccess: true,
|
|
213
|
+
steps: [
|
|
214
|
+
{ type: 'agent', content: 'Generate API documentation for all endpoints in the project. For each endpoint, document: HTTP method, path, parameters, request body, response format, status codes, and examples.' },
|
|
215
|
+
],
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
name: 'translate',
|
|
219
|
+
description: 'Translate code comments to English',
|
|
220
|
+
category: 'documentation',
|
|
221
|
+
requiresWriteAccess: true,
|
|
222
|
+
parameters: [
|
|
223
|
+
{ name: 'file', description: 'File to translate', required: false },
|
|
224
|
+
],
|
|
225
|
+
steps: [
|
|
226
|
+
{ type: 'agent', content: 'Translate all comments in this file to English. Keep the code unchanged, only translate comments and documentation strings.' },
|
|
227
|
+
],
|
|
228
|
+
},
|
|
229
|
+
// ==================== REFACTORING SKILLS ====================
|
|
230
|
+
{
|
|
231
|
+
name: 'refactor',
|
|
232
|
+
description: 'Refactor code for better quality',
|
|
233
|
+
shortcut: 'r',
|
|
234
|
+
category: 'refactoring',
|
|
235
|
+
requiresWriteAccess: true,
|
|
236
|
+
parameters: [
|
|
237
|
+
{ name: 'file', description: 'File to refactor', required: false },
|
|
238
|
+
],
|
|
239
|
+
steps: [
|
|
240
|
+
{ type: 'agent', content: 'Refactor the current file to improve code quality. Focus on: extracting functions, reducing complexity, improving naming, removing duplication, and following best practices. Keep functionality identical.' },
|
|
241
|
+
],
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
name: 'types',
|
|
245
|
+
description: 'Add or improve TypeScript types',
|
|
246
|
+
category: 'refactoring',
|
|
247
|
+
requiresWriteAccess: true,
|
|
248
|
+
steps: [
|
|
249
|
+
{ type: 'agent', content: 'Improve TypeScript types in this file. Replace any types with proper types, add missing type annotations, create interfaces for complex objects, and ensure strict type safety.' },
|
|
250
|
+
],
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
name: 'optimize',
|
|
254
|
+
description: 'Optimize code performance',
|
|
255
|
+
shortcut: 'o',
|
|
256
|
+
category: 'refactoring',
|
|
257
|
+
requiresWriteAccess: true,
|
|
258
|
+
steps: [
|
|
259
|
+
{ type: 'prompt', content: 'Analyze this code for performance issues. Identify: unnecessary re-renders, memory leaks, inefficient algorithms, missing caching opportunities, and N+1 queries.' },
|
|
260
|
+
{ type: 'confirm', content: 'Apply optimizations?' },
|
|
261
|
+
{ type: 'agent', content: 'Apply the suggested performance optimizations.' },
|
|
262
|
+
],
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
name: 'cleanup',
|
|
266
|
+
description: 'Clean up code (remove unused, format)',
|
|
267
|
+
category: 'refactoring',
|
|
268
|
+
requiresWriteAccess: true,
|
|
269
|
+
steps: [
|
|
270
|
+
{ type: 'agent', content: 'Clean up this file: remove unused imports, remove dead code, remove console.logs, fix formatting inconsistencies, and organize imports.' },
|
|
271
|
+
],
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
name: 'modernize',
|
|
275
|
+
description: 'Update code to use modern syntax',
|
|
276
|
+
category: 'refactoring',
|
|
277
|
+
requiresWriteAccess: true,
|
|
278
|
+
steps: [
|
|
279
|
+
{ type: 'agent', content: 'Update this code to use modern JavaScript/TypeScript syntax. Convert: var to const/let, callbacks to async/await, .then chains to async/await, class components to functional (React), and old APIs to modern equivalents.' },
|
|
280
|
+
],
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
name: 'migrate',
|
|
284
|
+
description: 'Migrate code to newer version',
|
|
285
|
+
category: 'refactoring',
|
|
286
|
+
requiresWriteAccess: true,
|
|
287
|
+
parameters: [
|
|
288
|
+
{ name: 'target', description: 'Target version or framework (e.g., React 18, Node 20)', required: true },
|
|
289
|
+
],
|
|
290
|
+
steps: [
|
|
291
|
+
{ type: 'agent', content: 'Migrate the codebase to ${target}. Update deprecated APIs, fix breaking changes, and update dependencies as needed. Follow the official migration guide.' },
|
|
292
|
+
],
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
name: 'split',
|
|
296
|
+
description: 'Split a large file into smaller modules',
|
|
297
|
+
category: 'refactoring',
|
|
298
|
+
requiresWriteAccess: true,
|
|
299
|
+
parameters: [
|
|
300
|
+
{ name: 'file', description: 'File to split', required: true },
|
|
301
|
+
],
|
|
302
|
+
steps: [
|
|
303
|
+
{ type: 'agent', content: 'Split the file "${file}" into smaller, focused modules. Group related functions together, create proper exports, and update all imports across the project.' },
|
|
304
|
+
],
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
name: 'rename',
|
|
308
|
+
description: 'Rename a symbol across the codebase',
|
|
309
|
+
category: 'refactoring',
|
|
310
|
+
requiresWriteAccess: true,
|
|
311
|
+
parameters: [
|
|
312
|
+
{ name: 'old', description: 'Current name', required: true },
|
|
313
|
+
{ name: 'new', description: 'New name', required: true },
|
|
314
|
+
],
|
|
315
|
+
steps: [
|
|
316
|
+
{ type: 'agent', content: 'Rename "${old}" to "${new}" across the entire codebase. Update all references, imports, and documentation.' },
|
|
317
|
+
],
|
|
318
|
+
},
|
|
319
|
+
// ==================== DEBUGGING SKILLS ====================
|
|
320
|
+
{
|
|
321
|
+
name: 'debug',
|
|
322
|
+
description: 'Debug and fix issues',
|
|
323
|
+
shortcut: 'b',
|
|
324
|
+
category: 'debugging',
|
|
325
|
+
requiresWriteAccess: true,
|
|
326
|
+
steps: [
|
|
327
|
+
{ type: 'agent', content: 'Analyze the code and identify potential bugs. Check for: null pointer errors, race conditions, incorrect logic, missing error handling, and edge cases. Fix any issues found.' },
|
|
328
|
+
],
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
name: 'fix',
|
|
332
|
+
description: 'Fix a specific error or issue',
|
|
333
|
+
shortcut: 'f',
|
|
334
|
+
category: 'debugging',
|
|
335
|
+
requiresWriteAccess: true,
|
|
336
|
+
parameters: [
|
|
337
|
+
{ name: 'error', description: 'Error message or description', required: false },
|
|
338
|
+
],
|
|
339
|
+
steps: [
|
|
340
|
+
{ type: 'agent', content: 'Fix the error or issue described. Read relevant files, understand the problem, implement a fix, and verify it works.' },
|
|
341
|
+
],
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
name: 'security',
|
|
345
|
+
description: 'Security audit',
|
|
346
|
+
category: 'debugging',
|
|
347
|
+
steps: [
|
|
348
|
+
{ type: 'prompt', content: 'Perform a security audit on this code. Check for: SQL injection, XSS, CSRF, insecure dependencies, hardcoded secrets, authentication issues, and authorization flaws. Provide specific recommendations.' },
|
|
349
|
+
],
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
name: 'profile',
|
|
353
|
+
description: 'Profile code for performance issues',
|
|
354
|
+
category: 'debugging',
|
|
355
|
+
steps: [
|
|
356
|
+
{ type: 'prompt', content: 'Analyze this code for performance bottlenecks. Identify: slow database queries, memory-intensive operations, blocking I/O, unnecessary computations, and missing indexes. Suggest specific optimizations.' },
|
|
357
|
+
],
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
name: 'log',
|
|
361
|
+
description: 'Add logging to code',
|
|
362
|
+
category: 'debugging',
|
|
363
|
+
requiresWriteAccess: true,
|
|
364
|
+
parameters: [
|
|
365
|
+
{ name: 'file', description: 'File to add logging to', required: false },
|
|
366
|
+
],
|
|
367
|
+
steps: [
|
|
368
|
+
{ type: 'agent', content: 'Add appropriate logging to this code. Use the project\'s logging library. Add logs for: function entry/exit, error conditions, important state changes, and external API calls. Include relevant context in each log.' },
|
|
369
|
+
],
|
|
370
|
+
},
|
|
371
|
+
// ==================== DEPLOYMENT SKILLS ====================
|
|
372
|
+
{
|
|
373
|
+
name: 'build',
|
|
374
|
+
description: 'Build the project',
|
|
375
|
+
category: 'deployment',
|
|
376
|
+
steps: [
|
|
377
|
+
{ type: 'command', content: 'npm run build' },
|
|
378
|
+
{ type: 'notify', content: 'Build completed!' },
|
|
379
|
+
],
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
name: 'deploy',
|
|
383
|
+
description: 'Build and deploy',
|
|
384
|
+
category: 'deployment',
|
|
385
|
+
steps: [
|
|
386
|
+
{ type: 'command', content: 'npm run build' },
|
|
387
|
+
{ type: 'command', content: 'npm test' },
|
|
388
|
+
{ type: 'confirm', content: 'All checks passed. Deploy to production?' },
|
|
389
|
+
{ type: 'command', content: 'npm run deploy' },
|
|
390
|
+
{ type: 'notify', content: 'Deployed successfully!' },
|
|
391
|
+
],
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
name: 'release',
|
|
395
|
+
description: 'Create a new release',
|
|
396
|
+
category: 'deployment',
|
|
397
|
+
requiresGit: true,
|
|
398
|
+
parameters: [
|
|
399
|
+
{ name: 'version', description: 'Version number (major/minor/patch or specific)', required: false },
|
|
400
|
+
],
|
|
401
|
+
steps: [
|
|
402
|
+
{ type: 'prompt', content: 'Based on commits since last tag, suggest a version bump (major/minor/patch) following semver.' },
|
|
403
|
+
{ type: 'confirm', content: 'Create this release?' },
|
|
404
|
+
{ type: 'command', content: 'npm version ${version}' },
|
|
405
|
+
{ type: 'command', content: 'git push --tags' },
|
|
406
|
+
{ type: 'notify', content: 'Release created!' },
|
|
407
|
+
],
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
name: 'publish',
|
|
411
|
+
description: 'Publish package to npm',
|
|
412
|
+
category: 'deployment',
|
|
413
|
+
steps: [
|
|
414
|
+
{ type: 'command', content: 'npm run build' },
|
|
415
|
+
{ type: 'command', content: 'npm test' },
|
|
416
|
+
{ type: 'confirm', content: 'Publish to npm?' },
|
|
417
|
+
{ type: 'command', content: 'npm publish' },
|
|
418
|
+
{ type: 'notify', content: 'Package published!' },
|
|
419
|
+
],
|
|
420
|
+
},
|
|
421
|
+
// ==================== GENERATION SKILLS ====================
|
|
422
|
+
{
|
|
423
|
+
name: 'component',
|
|
424
|
+
description: 'Generate a React/Vue component',
|
|
425
|
+
category: 'generation',
|
|
426
|
+
requiresWriteAccess: true,
|
|
427
|
+
parameters: [
|
|
428
|
+
{ name: 'name', description: 'Component name', required: true },
|
|
429
|
+
{ name: 'type', description: 'Component type (page, form, list, card, modal)', required: false },
|
|
430
|
+
],
|
|
431
|
+
steps: [
|
|
432
|
+
{ type: 'agent', content: 'Generate a React/Vue component named "${name}". Type: ${type}. Follow the project\'s component conventions. Include: TypeScript types, props interface, styles (CSS/Tailwind/styled-components based on project), and a test file.' },
|
|
433
|
+
],
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
name: 'api',
|
|
437
|
+
description: 'Generate an API endpoint',
|
|
438
|
+
category: 'generation',
|
|
439
|
+
requiresWriteAccess: true,
|
|
440
|
+
parameters: [
|
|
441
|
+
{ name: 'name', description: 'Endpoint name', required: true },
|
|
442
|
+
{ name: 'method', description: 'HTTP method (GET, POST, PUT, DELETE)', required: false, default: 'GET' },
|
|
443
|
+
],
|
|
444
|
+
steps: [
|
|
445
|
+
{ type: 'agent', content: 'Generate an API endpoint for "${name}" with ${method} method. Include: route handler, input validation, error handling, TypeScript types, and a test file. Follow the project\'s API conventions.' },
|
|
446
|
+
],
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
name: 'model',
|
|
450
|
+
description: 'Generate a database model',
|
|
451
|
+
category: 'generation',
|
|
452
|
+
requiresWriteAccess: true,
|
|
453
|
+
parameters: [
|
|
454
|
+
{ name: 'name', description: 'Model name', required: true },
|
|
455
|
+
{ name: 'fields', description: 'Comma-separated fields (e.g., name:string,age:number)', required: false },
|
|
456
|
+
],
|
|
457
|
+
steps: [
|
|
458
|
+
{ type: 'agent', content: 'Generate a database model for "${name}". Fields: ${fields}. Use the project\'s ORM (Prisma, TypeORM, Mongoose, etc.). Include: schema definition, TypeScript types, validation, and any necessary migrations.' },
|
|
459
|
+
],
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
name: 'hook',
|
|
463
|
+
description: 'Generate a React hook',
|
|
464
|
+
category: 'generation',
|
|
465
|
+
requiresWriteAccess: true,
|
|
466
|
+
parameters: [
|
|
467
|
+
{ name: 'name', description: 'Hook name (without use prefix)', required: true },
|
|
468
|
+
],
|
|
469
|
+
steps: [
|
|
470
|
+
{ type: 'agent', content: 'Generate a React hook named "use${name}". Include: proper TypeScript types, cleanup logic, error handling, and a test file. Follow React hooks best practices.' },
|
|
471
|
+
],
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
name: 'service',
|
|
475
|
+
description: 'Generate a service/utility module',
|
|
476
|
+
category: 'generation',
|
|
477
|
+
requiresWriteAccess: true,
|
|
478
|
+
parameters: [
|
|
479
|
+
{ name: 'name', description: 'Service name', required: true },
|
|
480
|
+
],
|
|
481
|
+
steps: [
|
|
482
|
+
{ type: 'agent', content: 'Generate a service module for "${name}". Include: class or functions, TypeScript interfaces, error handling, and a test file. Follow the project\'s service patterns.' },
|
|
483
|
+
],
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
name: 'page',
|
|
487
|
+
description: 'Generate a new page/route',
|
|
488
|
+
category: 'generation',
|
|
489
|
+
requiresWriteAccess: true,
|
|
490
|
+
parameters: [
|
|
491
|
+
{ name: 'name', description: 'Page name', required: true },
|
|
492
|
+
{ name: 'path', description: 'Route path', required: false },
|
|
493
|
+
],
|
|
494
|
+
steps: [
|
|
495
|
+
{ type: 'agent', content: 'Generate a new page named "${name}" at path "${path}". Include: page component, route registration, any required data fetching, loading states, and error handling. Follow the project\'s page conventions.' },
|
|
496
|
+
],
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
name: 'form',
|
|
500
|
+
description: 'Generate a form with validation',
|
|
501
|
+
category: 'generation',
|
|
502
|
+
requiresWriteAccess: true,
|
|
503
|
+
parameters: [
|
|
504
|
+
{ name: 'name', description: 'Form name', required: true },
|
|
505
|
+
{ name: 'fields', description: 'Comma-separated fields', required: false },
|
|
506
|
+
],
|
|
507
|
+
steps: [
|
|
508
|
+
{ type: 'agent', content: 'Generate a form component for "${name}". Fields: ${fields}. Include: form validation (using project\'s validation library), error messages, submit handling, loading state, TypeScript types, and a test file.' },
|
|
509
|
+
],
|
|
510
|
+
},
|
|
511
|
+
{
|
|
512
|
+
name: 'crud',
|
|
513
|
+
description: 'Generate full CRUD for an entity',
|
|
514
|
+
category: 'generation',
|
|
515
|
+
requiresWriteAccess: true,
|
|
516
|
+
parameters: [
|
|
517
|
+
{ name: 'name', description: 'Entity name', required: true },
|
|
518
|
+
{ name: 'fields', description: 'Comma-separated fields', required: false },
|
|
519
|
+
],
|
|
520
|
+
steps: [
|
|
521
|
+
{ type: 'agent', content: 'Generate complete CRUD functionality for "${name}". Fields: ${fields}. Include: database model, API endpoints (list, get, create, update, delete), list page, detail page, create/edit form, TypeScript types, and tests.' },
|
|
522
|
+
],
|
|
523
|
+
},
|
|
524
|
+
// ==================== DEVOPS SKILLS ====================
|
|
525
|
+
{
|
|
526
|
+
name: 'docker',
|
|
527
|
+
description: 'Generate Dockerfile and docker-compose',
|
|
528
|
+
category: 'devops',
|
|
529
|
+
requiresWriteAccess: true,
|
|
530
|
+
steps: [
|
|
531
|
+
{ type: 'agent', content: 'Generate a Dockerfile for this project. Include: multi-stage build for smaller image, proper base image, dependency caching, security best practices (non-root user, minimal image). Also generate docker-compose.yml for local development with any required services (database, redis, etc.).' },
|
|
532
|
+
],
|
|
533
|
+
},
|
|
534
|
+
{
|
|
535
|
+
name: 'ci',
|
|
536
|
+
description: 'Generate CI/CD configuration',
|
|
537
|
+
category: 'devops',
|
|
538
|
+
requiresWriteAccess: true,
|
|
539
|
+
parameters: [
|
|
540
|
+
{ name: 'platform', description: 'CI platform (github, gitlab, bitbucket)', required: false, default: 'github' },
|
|
541
|
+
],
|
|
542
|
+
steps: [
|
|
543
|
+
{ type: 'agent', content: 'Generate CI/CD configuration for ${platform}. Include: install dependencies, lint, type check, test, build, and optional deploy steps. Add caching for faster builds. Follow ${platform} best practices.' },
|
|
544
|
+
],
|
|
545
|
+
},
|
|
546
|
+
{
|
|
547
|
+
name: 'env',
|
|
548
|
+
description: 'Setup environment configuration',
|
|
549
|
+
category: 'devops',
|
|
550
|
+
requiresWriteAccess: true,
|
|
551
|
+
steps: [
|
|
552
|
+
{ type: 'agent', content: 'Create environment configuration. Generate: .env.example with all required variables, environment validation using zod or joi, typed config module, and update .gitignore to exclude .env files.' },
|
|
553
|
+
],
|
|
554
|
+
},
|
|
555
|
+
{
|
|
556
|
+
name: 'k8s',
|
|
557
|
+
description: 'Generate Kubernetes manifests',
|
|
558
|
+
category: 'devops',
|
|
559
|
+
requiresWriteAccess: true,
|
|
560
|
+
steps: [
|
|
561
|
+
{ type: 'agent', content: 'Generate Kubernetes manifests for this project. Include: Deployment, Service, ConfigMap, Secrets template, Ingress (optional), HPA for auto-scaling. Follow Kubernetes best practices (resource limits, health checks, security context).' },
|
|
562
|
+
],
|
|
563
|
+
},
|
|
564
|
+
{
|
|
565
|
+
name: 'terraform',
|
|
566
|
+
description: 'Generate Terraform configuration',
|
|
567
|
+
category: 'devops',
|
|
568
|
+
requiresWriteAccess: true,
|
|
569
|
+
parameters: [
|
|
570
|
+
{ name: 'provider', description: 'Cloud provider (aws, gcp, azure)', required: false, default: 'aws' },
|
|
571
|
+
],
|
|
572
|
+
steps: [
|
|
573
|
+
{ type: 'agent', content: 'Generate Terraform configuration for ${provider}. Include: main infrastructure (compute, storage, networking), variables with sensible defaults, outputs for important values, and a README for usage instructions.' },
|
|
574
|
+
],
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
name: 'nginx',
|
|
578
|
+
description: 'Generate Nginx configuration',
|
|
579
|
+
category: 'devops',
|
|
580
|
+
requiresWriteAccess: true,
|
|
581
|
+
steps: [
|
|
582
|
+
{ type: 'agent', content: 'Generate Nginx configuration for this project. Include: reverse proxy setup, SSL configuration template, gzip compression, caching headers, security headers, and rate limiting.' },
|
|
583
|
+
],
|
|
584
|
+
},
|
|
585
|
+
{
|
|
586
|
+
name: 'monitor',
|
|
587
|
+
description: 'Add monitoring and observability',
|
|
588
|
+
category: 'devops',
|
|
589
|
+
requiresWriteAccess: true,
|
|
590
|
+
steps: [
|
|
591
|
+
{ type: 'agent', content: 'Add monitoring and observability to the project. Include: health check endpoint, metrics endpoint (Prometheus format), structured logging, error tracking integration (Sentry-compatible), and performance monitoring hooks.' },
|
|
592
|
+
],
|
|
593
|
+
},
|
|
594
|
+
];
|
|
595
|
+
/**
|
|
596
|
+
* Ensure skills directory exists
|
|
597
|
+
*/
|
|
598
|
+
function ensureSkillsDir() {
|
|
599
|
+
if (!existsSync(SKILLS_DIR)) {
|
|
600
|
+
mkdirSync(SKILLS_DIR, { recursive: true });
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Get all built-in skills
|
|
605
|
+
*/
|
|
606
|
+
export function getBuiltInSkills() {
|
|
607
|
+
return BUILT_IN_SKILLS;
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Load custom skills from disk
|
|
611
|
+
*/
|
|
612
|
+
export function loadCustomSkills() {
|
|
613
|
+
ensureSkillsDir();
|
|
614
|
+
const skills = [];
|
|
615
|
+
try {
|
|
616
|
+
const files = readdirSync(SKILLS_DIR).filter(f => f.endsWith('.json'));
|
|
617
|
+
for (const file of files) {
|
|
618
|
+
try {
|
|
619
|
+
const content = readFileSync(join(SKILLS_DIR, file), 'utf-8');
|
|
620
|
+
const skill = JSON.parse(content);
|
|
621
|
+
skill.category = 'custom';
|
|
622
|
+
skills.push(skill);
|
|
623
|
+
}
|
|
624
|
+
catch { }
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
catch { }
|
|
628
|
+
return skills;
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
* Get all skills (built-in + custom)
|
|
632
|
+
*/
|
|
633
|
+
export function getAllSkills() {
|
|
634
|
+
return [...getBuiltInSkills(), ...loadCustomSkills()];
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Find skill by name or shortcut
|
|
638
|
+
*/
|
|
639
|
+
export function findSkill(nameOrShortcut) {
|
|
640
|
+
const skills = getAllSkills();
|
|
641
|
+
const lower = nameOrShortcut.toLowerCase();
|
|
642
|
+
return skills.find(s => s.name.toLowerCase() === lower ||
|
|
643
|
+
s.shortcut?.toLowerCase() === lower) || null;
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* Parse skill chain (e.g., "commit+push" → ["commit", "push"])
|
|
647
|
+
*/
|
|
648
|
+
export function parseSkillChain(input) {
|
|
649
|
+
if (!input.includes('+')) {
|
|
650
|
+
return null;
|
|
651
|
+
}
|
|
652
|
+
const parts = input.split('+').map(s => s.trim()).filter(Boolean);
|
|
653
|
+
if (parts.length < 2) {
|
|
654
|
+
return null;
|
|
655
|
+
}
|
|
656
|
+
// Verify all skills exist
|
|
657
|
+
for (const part of parts) {
|
|
658
|
+
if (!findSkill(part)) {
|
|
659
|
+
return null;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
return {
|
|
663
|
+
skills: parts,
|
|
664
|
+
stopOnError: true,
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Parse skill parameters from args string
|
|
669
|
+
* Supports: "value" for first param, key=value, key="value with spaces"
|
|
670
|
+
*/
|
|
671
|
+
export function parseSkillArgs(args, skill) {
|
|
672
|
+
const result = {};
|
|
673
|
+
if (!args.trim()) {
|
|
674
|
+
return result;
|
|
675
|
+
}
|
|
676
|
+
// Pattern to match key=value or key="value with spaces" or just "value"
|
|
677
|
+
const keyValuePattern = /(\w+)=(?:"([^"]+)"|'([^']+)'|(\S+))/g;
|
|
678
|
+
const quotedPattern = /^["'](.+)["']$/;
|
|
679
|
+
// First, try to parse key=value pairs
|
|
680
|
+
let match;
|
|
681
|
+
let remainingArgs = args;
|
|
682
|
+
while ((match = keyValuePattern.exec(args)) !== null) {
|
|
683
|
+
const key = match[1];
|
|
684
|
+
const value = match[2] || match[3] || match[4];
|
|
685
|
+
result[key] = value;
|
|
686
|
+
remainingArgs = remainingArgs.replace(match[0], '').trim();
|
|
687
|
+
}
|
|
688
|
+
// If there's remaining text and skill has parameters, use as first param
|
|
689
|
+
if (remainingArgs.trim() && skill.parameters && skill.parameters.length > 0) {
|
|
690
|
+
const firstParam = skill.parameters[0];
|
|
691
|
+
// Check if it's quoted
|
|
692
|
+
const quotedMatch = remainingArgs.match(quotedPattern);
|
|
693
|
+
if (quotedMatch) {
|
|
694
|
+
result[firstParam.name] = quotedMatch[1];
|
|
695
|
+
}
|
|
696
|
+
else {
|
|
697
|
+
result[firstParam.name] = remainingArgs.trim();
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
// Apply defaults
|
|
701
|
+
if (skill.parameters) {
|
|
702
|
+
for (const param of skill.parameters) {
|
|
703
|
+
if (param.default && !result[param.name]) {
|
|
704
|
+
result[param.name] = param.default;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
return result;
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* Interpolate parameters into skill step content
|
|
712
|
+
*/
|
|
713
|
+
export function interpolateParams(content, params) {
|
|
714
|
+
let result = content;
|
|
715
|
+
for (const [key, value] of Object.entries(params)) {
|
|
716
|
+
result = result.replace(new RegExp(`\\$\\{${key}\\}`, 'g'), value);
|
|
717
|
+
}
|
|
718
|
+
return result;
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* Save a custom skill
|
|
722
|
+
*/
|
|
723
|
+
export function saveCustomSkill(skill) {
|
|
724
|
+
ensureSkillsDir();
|
|
725
|
+
skill.category = 'custom';
|
|
726
|
+
const filename = `${skill.name}.json`;
|
|
727
|
+
writeFileSync(join(SKILLS_DIR, filename), JSON.stringify(skill, null, 2));
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* Delete a custom skill
|
|
731
|
+
*/
|
|
732
|
+
export function deleteCustomSkill(name) {
|
|
733
|
+
const filepath = join(SKILLS_DIR, `${name}.json`);
|
|
734
|
+
if (existsSync(filepath)) {
|
|
735
|
+
require('fs').unlinkSync(filepath);
|
|
736
|
+
return true;
|
|
737
|
+
}
|
|
738
|
+
return false;
|
|
739
|
+
}
|
|
740
|
+
/**
|
|
741
|
+
* Generate prompt for a skill with parameters
|
|
742
|
+
*/
|
|
743
|
+
export function generateSkillPrompt(skill, context, additionalContext, params) {
|
|
744
|
+
const lines = [];
|
|
745
|
+
lines.push(`# Skill: ${skill.name}`);
|
|
746
|
+
lines.push(`Description: ${skill.description}`);
|
|
747
|
+
lines.push('');
|
|
748
|
+
if (additionalContext) {
|
|
749
|
+
lines.push(`Context: ${additionalContext}`);
|
|
750
|
+
lines.push('');
|
|
751
|
+
}
|
|
752
|
+
// Collect all prompt/agent steps and interpolate params
|
|
753
|
+
for (const step of skill.steps) {
|
|
754
|
+
if (step.type === 'prompt' || step.type === 'agent') {
|
|
755
|
+
let content = step.content;
|
|
756
|
+
if (params) {
|
|
757
|
+
content = interpolateParams(content, params);
|
|
758
|
+
}
|
|
759
|
+
lines.push(content);
|
|
760
|
+
lines.push('');
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
return lines.join('\n');
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* Get skill steps that need execution
|
|
767
|
+
*/
|
|
768
|
+
export function getExecutableSteps(skill) {
|
|
769
|
+
return skill.steps.filter(s => s.type === 'command' || s.type === 'agent');
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Format skills list for display
|
|
773
|
+
*/
|
|
774
|
+
export function formatSkillsList(skills) {
|
|
775
|
+
const byCategory = new Map();
|
|
776
|
+
for (const skill of skills) {
|
|
777
|
+
const existing = byCategory.get(skill.category) || [];
|
|
778
|
+
existing.push(skill);
|
|
779
|
+
byCategory.set(skill.category, existing);
|
|
780
|
+
}
|
|
781
|
+
const lines = ['# Available Skills', ''];
|
|
782
|
+
const categoryOrder = [
|
|
783
|
+
'git', 'testing', 'documentation', 'refactoring', 'debugging', 'deployment', 'generation', 'devops', 'custom'
|
|
784
|
+
];
|
|
785
|
+
const categoryNames = {
|
|
786
|
+
git: 'Git',
|
|
787
|
+
testing: 'Testing',
|
|
788
|
+
documentation: 'Documentation',
|
|
789
|
+
refactoring: 'Refactoring',
|
|
790
|
+
debugging: 'Debugging',
|
|
791
|
+
deployment: 'Deployment',
|
|
792
|
+
generation: 'Code Generation',
|
|
793
|
+
devops: 'DevOps',
|
|
794
|
+
custom: 'Custom',
|
|
795
|
+
};
|
|
796
|
+
for (const category of categoryOrder) {
|
|
797
|
+
const categorySkills = byCategory.get(category);
|
|
798
|
+
if (!categorySkills || categorySkills.length === 0)
|
|
799
|
+
continue;
|
|
800
|
+
lines.push(`## ${categoryNames[category]}`);
|
|
801
|
+
for (const skill of categorySkills) {
|
|
802
|
+
const shortcut = skill.shortcut ? ` (/${skill.shortcut})` : '';
|
|
803
|
+
const params = skill.parameters?.map(p => p.required ? `<${p.name}>` : `[${p.name}]`).join(' ') || '';
|
|
804
|
+
lines.push(`- **/${skill.name}**${shortcut}${params ? ' ' + params : ''} - ${skill.description}`);
|
|
805
|
+
}
|
|
806
|
+
lines.push('');
|
|
807
|
+
}
|
|
808
|
+
lines.push('## Skill Chaining');
|
|
809
|
+
lines.push('Chain multiple skills with `+`: `/commit+push`, `/test+commit+push`');
|
|
810
|
+
lines.push('');
|
|
811
|
+
return lines.join('\n');
|
|
812
|
+
}
|
|
813
|
+
/**
|
|
814
|
+
* Format skill help
|
|
815
|
+
*/
|
|
816
|
+
export function formatSkillHelp(skill) {
|
|
817
|
+
const lines = [];
|
|
818
|
+
lines.push(`# /${skill.name}`);
|
|
819
|
+
if (skill.shortcut) {
|
|
820
|
+
lines.push(`Shortcut: /${skill.shortcut}`);
|
|
821
|
+
}
|
|
822
|
+
lines.push('');
|
|
823
|
+
lines.push(skill.description);
|
|
824
|
+
lines.push('');
|
|
825
|
+
if (skill.parameters && skill.parameters.length > 0) {
|
|
826
|
+
lines.push('## Parameters:');
|
|
827
|
+
for (const param of skill.parameters) {
|
|
828
|
+
const required = param.required ? ' (required)' : '';
|
|
829
|
+
const defaultVal = param.default ? ` [default: ${param.default}]` : '';
|
|
830
|
+
lines.push(`- **${param.name}**${required}${defaultVal} - ${param.description}`);
|
|
831
|
+
}
|
|
832
|
+
lines.push('');
|
|
833
|
+
}
|
|
834
|
+
if (skill.requiresWriteAccess) {
|
|
835
|
+
lines.push('Requires write access');
|
|
836
|
+
}
|
|
837
|
+
if (skill.requiresGit) {
|
|
838
|
+
lines.push('Requires git repository');
|
|
839
|
+
}
|
|
840
|
+
lines.push('');
|
|
841
|
+
lines.push('## Steps:');
|
|
842
|
+
for (let i = 0; i < skill.steps.length; i++) {
|
|
843
|
+
const step = skill.steps[i];
|
|
844
|
+
const optional = step.optional ? ' (optional)' : '';
|
|
845
|
+
switch (step.type) {
|
|
846
|
+
case 'prompt':
|
|
847
|
+
lines.push(`${i + 1}. AI Analysis${optional}`);
|
|
848
|
+
break;
|
|
849
|
+
case 'command':
|
|
850
|
+
lines.push(`${i + 1}. Run: \`${step.content}\`${optional}`);
|
|
851
|
+
break;
|
|
852
|
+
case 'confirm':
|
|
853
|
+
lines.push(`${i + 1}. Confirm: ${step.content}${optional}`);
|
|
854
|
+
break;
|
|
855
|
+
case 'agent':
|
|
856
|
+
lines.push(`${i + 1}. Agent Action${optional}`);
|
|
857
|
+
break;
|
|
858
|
+
case 'notify':
|
|
859
|
+
lines.push(`${i + 1}. Notify: ${step.content}${optional}`);
|
|
860
|
+
break;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
lines.push('');
|
|
864
|
+
lines.push('## Examples:');
|
|
865
|
+
// Generate example usage
|
|
866
|
+
if (skill.parameters && skill.parameters.length > 0) {
|
|
867
|
+
const example1 = skill.parameters[0];
|
|
868
|
+
lines.push(`\`/${skill.name} example-value\``);
|
|
869
|
+
lines.push(`\`/${skill.name} ${example1.name}="example value"\``);
|
|
870
|
+
}
|
|
871
|
+
else {
|
|
872
|
+
lines.push(`\`/${skill.name}\``);
|
|
873
|
+
}
|
|
874
|
+
return lines.join('\n');
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* Create a custom skill from template
|
|
878
|
+
*/
|
|
879
|
+
export function createSkillTemplate(name) {
|
|
880
|
+
return {
|
|
881
|
+
name,
|
|
882
|
+
description: 'Custom skill description',
|
|
883
|
+
category: 'custom',
|
|
884
|
+
steps: [
|
|
885
|
+
{ type: 'prompt', content: 'Describe what this skill should do' },
|
|
886
|
+
],
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
export const WIZARD_STEPS = [
|
|
890
|
+
{
|
|
891
|
+
field: 'name',
|
|
892
|
+
prompt: 'Skill name (lowercase, no spaces):',
|
|
893
|
+
validate: (input) => {
|
|
894
|
+
if (!input.match(/^[a-z][a-z0-9-]*$/)) {
|
|
895
|
+
return 'Name must be lowercase letters, numbers, and hyphens. Must start with a letter.';
|
|
896
|
+
}
|
|
897
|
+
if (findSkill(input)) {
|
|
898
|
+
return 'A skill with this name already exists.';
|
|
899
|
+
}
|
|
900
|
+
return null;
|
|
901
|
+
},
|
|
902
|
+
},
|
|
903
|
+
{
|
|
904
|
+
field: 'description',
|
|
905
|
+
prompt: 'Description (what does this skill do?):',
|
|
906
|
+
validate: (input) => input.length < 5 ? 'Description too short' : null,
|
|
907
|
+
},
|
|
908
|
+
{
|
|
909
|
+
field: 'shortcut',
|
|
910
|
+
prompt: 'Shortcut (single letter, or empty to skip):',
|
|
911
|
+
validate: (input) => {
|
|
912
|
+
if (!input)
|
|
913
|
+
return null;
|
|
914
|
+
if (!input.match(/^[a-z]$/)) {
|
|
915
|
+
return 'Shortcut must be a single lowercase letter';
|
|
916
|
+
}
|
|
917
|
+
if (findSkill(input)) {
|
|
918
|
+
return 'This shortcut is already used';
|
|
919
|
+
}
|
|
920
|
+
return null;
|
|
921
|
+
},
|
|
922
|
+
},
|
|
923
|
+
{
|
|
924
|
+
field: 'step_type',
|
|
925
|
+
prompt: 'Step type (prompt/command/agent/confirm/notify):',
|
|
926
|
+
validate: (input) => {
|
|
927
|
+
const valid = ['prompt', 'command', 'agent', 'confirm', 'notify'];
|
|
928
|
+
if (!valid.includes(input.toLowerCase())) {
|
|
929
|
+
return `Must be one of: ${valid.join(', ')}`;
|
|
930
|
+
}
|
|
931
|
+
return null;
|
|
932
|
+
},
|
|
933
|
+
},
|
|
934
|
+
{
|
|
935
|
+
field: 'step_content',
|
|
936
|
+
prompt: 'Step content:',
|
|
937
|
+
validate: (input) => input.length < 2 ? 'Content too short' : null,
|
|
938
|
+
},
|
|
939
|
+
{
|
|
940
|
+
field: 'add_another',
|
|
941
|
+
prompt: 'Add another step? (y/n):',
|
|
942
|
+
validate: (input) => {
|
|
943
|
+
if (!['y', 'n', 'yes', 'no'].includes(input.toLowerCase())) {
|
|
944
|
+
return 'Enter y or n';
|
|
945
|
+
}
|
|
946
|
+
return null;
|
|
947
|
+
},
|
|
948
|
+
},
|
|
949
|
+
];
|
|
950
|
+
/**
|
|
951
|
+
* Parse skill definition from YAML-like string
|
|
952
|
+
*/
|
|
953
|
+
export function parseSkillDefinition(content) {
|
|
954
|
+
try {
|
|
955
|
+
// Simple parser for skill definitions
|
|
956
|
+
const lines = content.split('\n');
|
|
957
|
+
const skill = {
|
|
958
|
+
steps: [],
|
|
959
|
+
category: 'custom',
|
|
960
|
+
};
|
|
961
|
+
for (const line of lines) {
|
|
962
|
+
const trimmed = line.trim();
|
|
963
|
+
if (trimmed.startsWith('name:')) {
|
|
964
|
+
skill.name = trimmed.replace('name:', '').trim();
|
|
965
|
+
}
|
|
966
|
+
else if (trimmed.startsWith('description:')) {
|
|
967
|
+
skill.description = trimmed.replace('description:', '').trim();
|
|
968
|
+
}
|
|
969
|
+
else if (trimmed.startsWith('shortcut:')) {
|
|
970
|
+
skill.shortcut = trimmed.replace('shortcut:', '').trim();
|
|
971
|
+
}
|
|
972
|
+
else if (trimmed.startsWith('- prompt:')) {
|
|
973
|
+
skill.steps.push({ type: 'prompt', content: trimmed.replace('- prompt:', '').trim() });
|
|
974
|
+
}
|
|
975
|
+
else if (trimmed.startsWith('- command:') || trimmed.startsWith('- run:')) {
|
|
976
|
+
skill.steps.push({ type: 'command', content: trimmed.replace(/- (?:command|run):/, '').trim() });
|
|
977
|
+
}
|
|
978
|
+
else if (trimmed.startsWith('- confirm:')) {
|
|
979
|
+
skill.steps.push({ type: 'confirm', content: trimmed.replace('- confirm:', '').trim() });
|
|
980
|
+
}
|
|
981
|
+
else if (trimmed.startsWith('- agent:')) {
|
|
982
|
+
skill.steps.push({ type: 'agent', content: trimmed.replace('- agent:', '').trim() });
|
|
983
|
+
}
|
|
984
|
+
else if (trimmed.startsWith('- notify:')) {
|
|
985
|
+
skill.steps.push({ type: 'notify', content: trimmed.replace('- notify:', '').trim() });
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
if (skill.name && skill.description && skill.steps.length > 0) {
|
|
989
|
+
return skill;
|
|
990
|
+
}
|
|
991
|
+
return null;
|
|
992
|
+
}
|
|
993
|
+
catch {
|
|
994
|
+
return null;
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
/**
|
|
998
|
+
* Get skill categories summary
|
|
999
|
+
*/
|
|
1000
|
+
export function getSkillsSummary() {
|
|
1001
|
+
const skills = getAllSkills();
|
|
1002
|
+
const summary = {
|
|
1003
|
+
git: 0,
|
|
1004
|
+
testing: 0,
|
|
1005
|
+
documentation: 0,
|
|
1006
|
+
refactoring: 0,
|
|
1007
|
+
debugging: 0,
|
|
1008
|
+
deployment: 0,
|
|
1009
|
+
generation: 0,
|
|
1010
|
+
devops: 0,
|
|
1011
|
+
custom: 0,
|
|
1012
|
+
};
|
|
1013
|
+
for (const skill of skills) {
|
|
1014
|
+
summary[skill.category]++;
|
|
1015
|
+
}
|
|
1016
|
+
return summary;
|
|
1017
|
+
}
|
|
1018
|
+
/**
|
|
1019
|
+
* Search skills by keyword
|
|
1020
|
+
*/
|
|
1021
|
+
export function searchSkills(query) {
|
|
1022
|
+
const skills = getAllSkills();
|
|
1023
|
+
const lower = query.toLowerCase();
|
|
1024
|
+
return skills.filter(s => s.name.toLowerCase().includes(lower) ||
|
|
1025
|
+
s.description.toLowerCase().includes(lower) ||
|
|
1026
|
+
s.category.toLowerCase().includes(lower));
|
|
1027
|
+
}
|
|
1028
|
+
/**
|
|
1029
|
+
* Load skill usage history from disk
|
|
1030
|
+
*/
|
|
1031
|
+
function loadSkillHistory() {
|
|
1032
|
+
try {
|
|
1033
|
+
if (existsSync(SKILLS_HISTORY_FILE)) {
|
|
1034
|
+
const content = readFileSync(SKILLS_HISTORY_FILE, 'utf-8');
|
|
1035
|
+
return JSON.parse(content);
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
catch {
|
|
1039
|
+
// Ignore errors, return default
|
|
1040
|
+
}
|
|
1041
|
+
return { entries: [], maxEntries: 100 };
|
|
1042
|
+
}
|
|
1043
|
+
/**
|
|
1044
|
+
* Save skill usage history to disk
|
|
1045
|
+
*/
|
|
1046
|
+
function saveSkillHistory(history) {
|
|
1047
|
+
try {
|
|
1048
|
+
const dir = join(homedir(), '.codeep');
|
|
1049
|
+
if (!existsSync(dir)) {
|
|
1050
|
+
mkdirSync(dir, { recursive: true });
|
|
1051
|
+
}
|
|
1052
|
+
writeFileSync(SKILLS_HISTORY_FILE, JSON.stringify(history, null, 2));
|
|
1053
|
+
}
|
|
1054
|
+
catch {
|
|
1055
|
+
// Ignore errors
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
/**
|
|
1059
|
+
* Track skill usage
|
|
1060
|
+
*/
|
|
1061
|
+
export function trackSkillUsage(skillName, success = true) {
|
|
1062
|
+
const history = loadSkillHistory();
|
|
1063
|
+
// Add new entry
|
|
1064
|
+
history.entries.push({
|
|
1065
|
+
skillName,
|
|
1066
|
+
timestamp: Date.now(),
|
|
1067
|
+
success,
|
|
1068
|
+
});
|
|
1069
|
+
// Trim old entries
|
|
1070
|
+
if (history.entries.length > history.maxEntries) {
|
|
1071
|
+
history.entries = history.entries.slice(-history.maxEntries);
|
|
1072
|
+
}
|
|
1073
|
+
saveSkillHistory(history);
|
|
1074
|
+
}
|
|
1075
|
+
/**
|
|
1076
|
+
* Get recently used skills
|
|
1077
|
+
*/
|
|
1078
|
+
export function getRecentSkills(limit = 10) {
|
|
1079
|
+
const history = loadSkillHistory();
|
|
1080
|
+
// Get unique skills ordered by most recent usage
|
|
1081
|
+
const seen = new Set();
|
|
1082
|
+
const recent = [];
|
|
1083
|
+
// Iterate from newest to oldest
|
|
1084
|
+
for (let i = history.entries.length - 1; i >= 0 && recent.length < limit; i--) {
|
|
1085
|
+
const entry = history.entries[i];
|
|
1086
|
+
if (!seen.has(entry.skillName)) {
|
|
1087
|
+
seen.add(entry.skillName);
|
|
1088
|
+
recent.push(entry.skillName);
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
return recent;
|
|
1092
|
+
}
|
|
1093
|
+
/**
|
|
1094
|
+
* Get most frequently used skills
|
|
1095
|
+
*/
|
|
1096
|
+
export function getMostUsedSkills(limit = 10) {
|
|
1097
|
+
const history = loadSkillHistory();
|
|
1098
|
+
// Count usage
|
|
1099
|
+
const counts = new Map();
|
|
1100
|
+
for (const entry of history.entries) {
|
|
1101
|
+
counts.set(entry.skillName, (counts.get(entry.skillName) || 0) + 1);
|
|
1102
|
+
}
|
|
1103
|
+
// Sort by count
|
|
1104
|
+
return Array.from(counts.entries())
|
|
1105
|
+
.map(([name, count]) => ({ name, count }))
|
|
1106
|
+
.sort((a, b) => b.count - a.count)
|
|
1107
|
+
.slice(0, limit);
|
|
1108
|
+
}
|
|
1109
|
+
/**
|
|
1110
|
+
* Get skill usage statistics
|
|
1111
|
+
*/
|
|
1112
|
+
export function getSkillStats() {
|
|
1113
|
+
const history = loadSkillHistory();
|
|
1114
|
+
const uniqueSkills = new Set(history.entries.map(e => e.skillName)).size;
|
|
1115
|
+
const successCount = history.entries.filter(e => e.success).length;
|
|
1116
|
+
const successRate = history.entries.length > 0
|
|
1117
|
+
? Math.round((successCount / history.entries.length) * 100)
|
|
1118
|
+
: 100;
|
|
1119
|
+
return {
|
|
1120
|
+
totalUsage: history.entries.length,
|
|
1121
|
+
uniqueSkills,
|
|
1122
|
+
successRate,
|
|
1123
|
+
};
|
|
1124
|
+
}
|
|
1125
|
+
/**
|
|
1126
|
+
* Clear skill usage history
|
|
1127
|
+
*/
|
|
1128
|
+
export function clearSkillHistory() {
|
|
1129
|
+
try {
|
|
1130
|
+
if (existsSync(SKILLS_HISTORY_FILE)) {
|
|
1131
|
+
require('fs').unlinkSync(SKILLS_HISTORY_FILE);
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
catch {
|
|
1135
|
+
// Ignore errors
|
|
1136
|
+
}
|
|
1137
|
+
}
|