@deimoscloud/coreai 0.1.9 → 0.1.11

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 (196) hide show
  1. package/dist/cli/index.js +7 -0
  2. package/dist/cli/index.js.map +1 -1
  3. package/dist/index.js +7 -0
  4. package/dist/index.js.map +1 -1
  5. package/package.json +6 -1
  6. package/.prettierrc +0 -9
  7. package/AGENT_SPEC.md +0 -347
  8. package/ARCHITECTURE.md +0 -547
  9. package/DRAFT_PRD.md +0 -1440
  10. package/IMPLEMENTATION_PLAN.md +0 -256
  11. package/PRODUCT.md +0 -473
  12. package/WORKFLOWS.md +0 -295
  13. package/commands/core/check-inbox.md +0 -34
  14. package/commands/core/delegate.md +0 -30
  15. package/commands/core/git-commit.md +0 -144
  16. package/commands/core/pr-create.md +0 -193
  17. package/commands/core/review.md +0 -56
  18. package/commands/core/sprint-status.md +0 -65
  19. package/commands/optional/docs-update.md +0 -200
  20. package/commands/optional/jira-create.md +0 -200
  21. package/commands/optional/jira-transition.md +0 -184
  22. package/commands/optional/worktree-cleanup.md +0 -167
  23. package/commands/optional/worktree-setup.md +0 -110
  24. package/eslint.config.js +0 -29
  25. package/jest.config.js +0 -22
  26. package/knowledge-library/README.md +0 -118
  27. package/knowledge-library/android-engineer/context/current.txt +0 -42
  28. package/knowledge-library/android-engineer/control/decisions.txt +0 -9
  29. package/knowledge-library/android-engineer/control/dependencies.txt +0 -19
  30. package/knowledge-library/android-engineer/control/objectives.txt +0 -26
  31. package/knowledge-library/android-engineer/history/.gitkeep +0 -0
  32. package/knowledge-library/android-engineer/inbox/processed/.gitkeep +0 -0
  33. package/knowledge-library/android-engineer/outbox/.gitkeep +0 -0
  34. package/knowledge-library/android-engineer/tech/.gitkeep +0 -0
  35. package/knowledge-library/architecture.txt +0 -61
  36. package/knowledge-library/backend-engineer/context/current.txt +0 -42
  37. package/knowledge-library/backend-engineer/control/decisions.txt +0 -9
  38. package/knowledge-library/backend-engineer/control/dependencies.txt +0 -19
  39. package/knowledge-library/backend-engineer/control/objectives.txt +0 -26
  40. package/knowledge-library/backend-engineer/history/.gitkeep +0 -0
  41. package/knowledge-library/backend-engineer/inbox/processed/.gitkeep +0 -0
  42. package/knowledge-library/backend-engineer/outbox/.gitkeep +0 -0
  43. package/knowledge-library/backend-engineer/tech/.gitkeep +0 -0
  44. package/knowledge-library/context.txt +0 -52
  45. package/knowledge-library/devops-engineer/context/current.txt +0 -42
  46. package/knowledge-library/devops-engineer/control/decisions.txt +0 -9
  47. package/knowledge-library/devops-engineer/control/dependencies.txt +0 -19
  48. package/knowledge-library/devops-engineer/control/objectives.txt +0 -26
  49. package/knowledge-library/devops-engineer/history/.gitkeep +0 -0
  50. package/knowledge-library/devops-engineer/inbox/processed/.gitkeep +0 -0
  51. package/knowledge-library/devops-engineer/outbox/.gitkeep +0 -0
  52. package/knowledge-library/devops-engineer/tech/.gitkeep +0 -0
  53. package/knowledge-library/engineering-manager/context/current.txt +0 -40
  54. package/knowledge-library/engineering-manager/control/decisions.txt +0 -9
  55. package/knowledge-library/engineering-manager/control/objectives.txt +0 -27
  56. package/knowledge-library/engineering-manager/history/.gitkeep +0 -0
  57. package/knowledge-library/engineering-manager/inbox/processed/.gitkeep +0 -0
  58. package/knowledge-library/engineering-manager/outbox/.gitkeep +0 -0
  59. package/knowledge-library/engineering-manager/tech/.gitkeep +0 -0
  60. package/knowledge-library/prd.txt +0 -81
  61. package/knowledge-library/product-manager/context/current.txt +0 -42
  62. package/knowledge-library/product-manager/control/decisions.txt +0 -9
  63. package/knowledge-library/product-manager/control/dependencies.txt +0 -19
  64. package/knowledge-library/product-manager/control/objectives.txt +0 -26
  65. package/knowledge-library/product-manager/history/.gitkeep +0 -0
  66. package/knowledge-library/product-manager/inbox/processed/.gitkeep +0 -0
  67. package/knowledge-library/product-manager/outbox/.gitkeep +0 -0
  68. package/knowledge-library/product-manager/tech/.gitkeep +0 -0
  69. package/knowledge-library/qa-engineer/context/current.txt +0 -42
  70. package/knowledge-library/qa-engineer/control/decisions.txt +0 -9
  71. package/knowledge-library/qa-engineer/control/dependencies.txt +0 -19
  72. package/knowledge-library/qa-engineer/control/objectives.txt +0 -26
  73. package/knowledge-library/qa-engineer/history/.gitkeep +0 -0
  74. package/knowledge-library/qa-engineer/inbox/processed/.gitkeep +0 -0
  75. package/knowledge-library/qa-engineer/outbox/.gitkeep +0 -0
  76. package/knowledge-library/qa-engineer/tech/.gitkeep +0 -0
  77. package/knowledge-library/security-engineer/context/current.txt +0 -42
  78. package/knowledge-library/security-engineer/control/decisions.txt +0 -9
  79. package/knowledge-library/security-engineer/control/dependencies.txt +0 -19
  80. package/knowledge-library/security-engineer/control/objectives.txt +0 -26
  81. package/knowledge-library/security-engineer/history/.gitkeep +0 -0
  82. package/knowledge-library/security-engineer/inbox/processed/.gitkeep +0 -0
  83. package/knowledge-library/security-engineer/outbox/.gitkeep +0 -0
  84. package/knowledge-library/security-engineer/tech/.gitkeep +0 -0
  85. package/knowledge-library/solutions-architect/context/current.txt +0 -42
  86. package/knowledge-library/solutions-architect/control/decisions.txt +0 -9
  87. package/knowledge-library/solutions-architect/control/dependencies.txt +0 -19
  88. package/knowledge-library/solutions-architect/control/objectives.txt +0 -26
  89. package/knowledge-library/solutions-architect/history/.gitkeep +0 -0
  90. package/knowledge-library/solutions-architect/inbox/processed/.gitkeep +0 -0
  91. package/knowledge-library/solutions-architect/outbox/.gitkeep +0 -0
  92. package/knowledge-library/solutions-architect/tech/.gitkeep +0 -0
  93. package/knowledge-library/wearos-engineer/context/current.txt +0 -42
  94. package/knowledge-library/wearos-engineer/control/decisions.txt +0 -9
  95. package/knowledge-library/wearos-engineer/control/dependencies.txt +0 -19
  96. package/knowledge-library/wearos-engineer/control/objectives.txt +0 -26
  97. package/knowledge-library/wearos-engineer/history/.gitkeep +0 -0
  98. package/knowledge-library/wearos-engineer/inbox/processed/.gitkeep +0 -0
  99. package/knowledge-library/wearos-engineer/outbox/.gitkeep +0 -0
  100. package/knowledge-library/wearos-engineer/tech/.gitkeep +0 -0
  101. package/scripts/add-agent.sh +0 -323
  102. package/scripts/install.sh +0 -354
  103. package/src/adapters/factory.test.ts +0 -386
  104. package/src/adapters/factory.ts +0 -305
  105. package/src/adapters/index.ts +0 -113
  106. package/src/adapters/interfaces.ts +0 -268
  107. package/src/adapters/mcp/client.test.ts +0 -130
  108. package/src/adapters/mcp/client.ts +0 -451
  109. package/src/adapters/mcp/discovery.test.ts +0 -315
  110. package/src/adapters/mcp/discovery.ts +0 -340
  111. package/src/adapters/mcp/index.ts +0 -66
  112. package/src/adapters/mcp/mapper.test.ts +0 -218
  113. package/src/adapters/mcp/mapper.ts +0 -536
  114. package/src/adapters/mcp/registry.test.ts +0 -433
  115. package/src/adapters/mcp/registry.ts +0 -550
  116. package/src/adapters/mcp/types.ts +0 -258
  117. package/src/adapters/native/filesystem.test.ts +0 -350
  118. package/src/adapters/native/filesystem.ts +0 -393
  119. package/src/adapters/native/github.test.ts +0 -173
  120. package/src/adapters/native/github.ts +0 -627
  121. package/src/adapters/native/index.ts +0 -22
  122. package/src/adapters/native/selector.test.ts +0 -224
  123. package/src/adapters/native/selector.ts +0 -150
  124. package/src/adapters/types.ts +0 -270
  125. package/src/agents/compiler.test.ts +0 -410
  126. package/src/agents/compiler.ts +0 -424
  127. package/src/agents/index.ts +0 -37
  128. package/src/agents/loader.test.ts +0 -319
  129. package/src/agents/loader.ts +0 -143
  130. package/src/agents/resolver.test.ts +0 -282
  131. package/src/agents/resolver.ts +0 -262
  132. package/src/agents/types.ts +0 -97
  133. package/src/cache/index.ts +0 -38
  134. package/src/cache/interfaces.ts +0 -283
  135. package/src/cache/manager.test.ts +0 -266
  136. package/src/cache/manager.ts +0 -388
  137. package/src/cache/provider.test.ts +0 -485
  138. package/src/cache/provider.ts +0 -745
  139. package/src/cache/types.test.ts +0 -192
  140. package/src/cache/types.ts +0 -313
  141. package/src/cli/commands/build.test.ts +0 -248
  142. package/src/cli/commands/build.ts +0 -284
  143. package/src/cli/commands/cache.test.ts +0 -221
  144. package/src/cli/commands/cache.ts +0 -229
  145. package/src/cli/commands/index.ts +0 -63
  146. package/src/cli/commands/init.test.ts +0 -173
  147. package/src/cli/commands/init.ts +0 -296
  148. package/src/cli/commands/skills.test.ts +0 -272
  149. package/src/cli/commands/skills.ts +0 -348
  150. package/src/cli/commands/status.test.ts +0 -392
  151. package/src/cli/commands/status.ts +0 -332
  152. package/src/cli/commands/sync.test.ts +0 -213
  153. package/src/cli/commands/sync.ts +0 -251
  154. package/src/cli/commands/validate.test.ts +0 -216
  155. package/src/cli/commands/validate.ts +0 -340
  156. package/src/cli/index.test.ts +0 -190
  157. package/src/cli/index.ts +0 -493
  158. package/src/commands/context.test.ts +0 -163
  159. package/src/commands/context.ts +0 -111
  160. package/src/commands/index.ts +0 -56
  161. package/src/commands/loader.test.ts +0 -273
  162. package/src/commands/loader.ts +0 -355
  163. package/src/commands/registry.test.ts +0 -384
  164. package/src/commands/registry.ts +0 -248
  165. package/src/commands/runner.test.ts +0 -297
  166. package/src/commands/runner.ts +0 -222
  167. package/src/commands/types.ts +0 -361
  168. package/src/config/index.ts +0 -19
  169. package/src/config/loader.test.ts +0 -262
  170. package/src/config/loader.ts +0 -188
  171. package/src/config/types.ts +0 -154
  172. package/src/context/index.ts +0 -14
  173. package/src/context/loader.test.ts +0 -334
  174. package/src/context/loader.ts +0 -357
  175. package/src/index.test.ts +0 -13
  176. package/src/index.ts +0 -268
  177. package/src/knowledge-library/index.ts +0 -44
  178. package/src/knowledge-library/manager.test.ts +0 -536
  179. package/src/knowledge-library/manager.ts +0 -804
  180. package/src/knowledge-library/types.ts +0 -432
  181. package/src/skills/generator.test.ts +0 -602
  182. package/src/skills/generator.ts +0 -491
  183. package/src/skills/index.ts +0 -27
  184. package/src/skills/templates.ts +0 -520
  185. package/src/skills/types.ts +0 -251
  186. package/templates/completion-report.md +0 -72
  187. package/templates/feedback.md +0 -56
  188. package/templates/project-files/CLAUDE.md.template +0 -109
  189. package/templates/project-files/coreai.json.example +0 -47
  190. package/templates/project-files/mcp.json.template +0 -20
  191. package/templates/review-complete.md +0 -64
  192. package/templates/review-request.md +0 -67
  193. package/templates/task-assignment.md +0 -51
  194. package/tsconfig.build.json +0 -4
  195. package/tsconfig.json +0 -26
  196. package/tsup.config.ts +0 -23
@@ -1,602 +0,0 @@
1
- /**
2
- * Skill Generator Tests
3
- */
4
-
5
- import { promises as fs } from 'fs';
6
- import { join } from 'path';
7
- import { tmpdir } from 'os';
8
- import type { ResolvedCoreAIConfig } from '../config/types.js';
9
- import {
10
- extractVariables,
11
- substituteVariables,
12
- checkDependencies,
13
- loadCustomTemplates,
14
- generateSkills,
15
- formatGenerateResult,
16
- } from './generator.js';
17
- import type { SkillTemplate, GenerateSkillsResult } from './types.js';
18
-
19
- describe('Skill Generator', () => {
20
- describe('extractVariables', () => {
21
- it('should return empty object for null config', () => {
22
- const vars = extractVariables(null);
23
- expect(vars).toEqual({});
24
- });
25
-
26
- it('should return empty object for undefined config', () => {
27
- const vars = extractVariables(undefined);
28
- expect(vars).toEqual({});
29
- });
30
-
31
- it('should extract project variables', () => {
32
- const config = {
33
- project: {
34
- name: 'TestProject',
35
- root: '/path/to/project',
36
- type: 'software' as const,
37
- },
38
- } as ResolvedCoreAIConfig;
39
-
40
- const vars = extractVariables(config);
41
- expect(vars.PROJECT_NAME).toBe('TestProject');
42
- expect(vars.PROJECT_ROOT).toBe('/path/to/project');
43
- expect(vars.PROJECT_TYPE).toBe('software');
44
- });
45
-
46
- it('should extract issue tracker variables', () => {
47
- const config = {
48
- project: { name: 'Test', root: '/test' },
49
- integrations: {
50
- issue_tracker: {
51
- provider: 'jira',
52
- config: {
53
- project_key: 'TEST',
54
- base_url: 'https://jira.example.com',
55
- },
56
- },
57
- },
58
- } as ResolvedCoreAIConfig;
59
-
60
- const vars = extractVariables(config);
61
- expect(vars.JIRA_PROJECT).toBe('TEST');
62
- expect(vars.JIRA_URL).toBe('https://jira.example.com');
63
- });
64
-
65
- it('should extract git variables', () => {
66
- const config = {
67
- project: { name: 'Test', root: '/test' },
68
- integrations: {
69
- git: {
70
- provider: 'github',
71
- config: {
72
- owner: 'testowner',
73
- repo: 'testrepo',
74
- default_branch: 'main',
75
- },
76
- },
77
- },
78
- } as ResolvedCoreAIConfig;
79
-
80
- const vars = extractVariables(config);
81
- expect(vars.GITHUB_OWNER).toBe('testowner');
82
- expect(vars.GITHUB_REPO).toBe('testrepo');
83
- expect(vars.DEFAULT_BRANCH).toBe('main');
84
- });
85
-
86
- it('should extract documentation variables', () => {
87
- const config = {
88
- project: { name: 'Test', root: '/test' },
89
- integrations: {
90
- documentation: {
91
- provider: 'confluence',
92
- config: {
93
- space_key: 'DOCS',
94
- base_url: 'https://wiki.example.com',
95
- base_path: '/docs',
96
- },
97
- },
98
- },
99
- } as ResolvedCoreAIConfig;
100
-
101
- const vars = extractVariables(config);
102
- expect(vars.CONFLUENCE_SPACE).toBe('DOCS');
103
- expect(vars.CONFLUENCE_URL).toBe('https://wiki.example.com');
104
- expect(vars.DOCS_PATH).toBe('/docs');
105
- });
106
-
107
- it('should extract quality gate variables', () => {
108
- const config = {
109
- project: { name: 'Test', root: '/test' },
110
- quality_gates: {
111
- lint: { command: 'npm run lint', required: true },
112
- test: { command: 'npm test', required: true },
113
- build: { command: 'npm run build', required: true },
114
- static_analysis: { command: 'npm run analyze', required: false },
115
- },
116
- } as ResolvedCoreAIConfig;
117
-
118
- const vars = extractVariables(config);
119
- expect(vars.LINT_CMD).toBe('npm run lint');
120
- expect(vars.TEST_CMD).toBe('npm test');
121
- expect(vars.BUILD_CMD).toBe('npm run build');
122
- expect(vars.STATIC_ANALYSIS_CMD).toBe('npm run analyze');
123
- });
124
-
125
- it('should extract tech stack variables', () => {
126
- const config = {
127
- project: { name: 'Test', root: '/test' },
128
- tech_stack: {
129
- primary_language: 'typescript',
130
- frameworks: ['node.js', 'express'],
131
- },
132
- } as ResolvedCoreAIConfig;
133
-
134
- const vars = extractVariables(config);
135
- expect(vars.PRIMARY_LANGUAGE).toBe('typescript');
136
- });
137
- });
138
-
139
- describe('substituteVariables', () => {
140
- it('should replace single variable', () => {
141
- const content = 'Hello, {{NAME}}!';
142
- const result = substituteVariables(content, { NAME: 'World' });
143
- expect(result).toBe('Hello, World!');
144
- });
145
-
146
- it('should replace multiple occurrences', () => {
147
- const content = '{{NAME}} says {{NAME}}';
148
- const result = substituteVariables(content, { NAME: 'Alice' });
149
- expect(result).toBe('Alice says Alice');
150
- });
151
-
152
- it('should replace multiple variables', () => {
153
- const content = '{{PROJECT}} by {{AUTHOR}}';
154
- const result = substituteVariables(content, {
155
- PROJECT: 'CoreAI',
156
- AUTHOR: 'Team',
157
- });
158
- expect(result).toBe('CoreAI by Team');
159
- });
160
-
161
- it('should leave unreplaced variables intact', () => {
162
- const content = '{{KNOWN}} and {{UNKNOWN}}';
163
- const result = substituteVariables(content, { KNOWN: 'value' });
164
- expect(result).toBe('value and {{UNKNOWN}}');
165
- });
166
-
167
- it('should handle empty variables', () => {
168
- const content = 'No variables here';
169
- const result = substituteVariables(content, {});
170
- expect(result).toBe('No variables here');
171
- });
172
-
173
- it('should skip undefined values', () => {
174
- const content = '{{A}} {{B}}';
175
- const result = substituteVariables(content, {
176
- A: 'defined',
177
- B: undefined,
178
- });
179
- expect(result).toBe('defined {{B}}');
180
- });
181
- });
182
-
183
- describe('checkDependencies', () => {
184
- it('should return satisfied for skill without dependencies', () => {
185
- const skill: SkillTemplate = {
186
- name: 'test',
187
- description: 'Test skill',
188
- category: 'core',
189
- content: 'content',
190
- };
191
-
192
- const result = checkDependencies(skill, null);
193
- expect(result.satisfied).toBe(true);
194
- expect(result.missing).toEqual([]);
195
- });
196
-
197
- it('should return satisfied for empty dependencies array', () => {
198
- const skill: SkillTemplate = {
199
- name: 'test',
200
- description: 'Test skill',
201
- category: 'core',
202
- dependencies: [],
203
- content: 'content',
204
- };
205
-
206
- const result = checkDependencies(skill, null);
207
- expect(result.satisfied).toBe(true);
208
- });
209
-
210
- it('should skip optional dependencies', () => {
211
- const skill: SkillTemplate = {
212
- name: 'test',
213
- description: 'Test skill',
214
- category: 'core',
215
- dependencies: [{ type: 'issue_tracker', required: false }],
216
- content: 'content',
217
- };
218
-
219
- const result = checkDependencies(skill, null);
220
- expect(result.satisfied).toBe(true);
221
- });
222
-
223
- it('should detect missing required integration', () => {
224
- const skill: SkillTemplate = {
225
- name: 'test',
226
- description: 'Test skill',
227
- category: 'optional',
228
- dependencies: [{ type: 'issue_tracker', required: true }],
229
- content: 'content',
230
- };
231
-
232
- const config = {
233
- project: { name: 'Test', root: '/test' },
234
- } as ResolvedCoreAIConfig;
235
-
236
- const result = checkDependencies(skill, config);
237
- expect(result.satisfied).toBe(false);
238
- expect(result.missing).toHaveLength(1);
239
- expect(result.missing[0]?.type).toBe('issue_tracker');
240
- });
241
-
242
- it('should satisfy when integration exists', () => {
243
- const skill: SkillTemplate = {
244
- name: 'test',
245
- description: 'Test skill',
246
- category: 'optional',
247
- dependencies: [{ type: 'git', required: true }],
248
- content: 'content',
249
- };
250
-
251
- const config = {
252
- project: { name: 'Test', root: '/test' },
253
- integrations: {
254
- git: { provider: 'github', config: {} },
255
- },
256
- } as ResolvedCoreAIConfig;
257
-
258
- const result = checkDependencies(skill, config);
259
- expect(result.satisfied).toBe(true);
260
- });
261
-
262
- it('should check documentation dependency', () => {
263
- const skill: SkillTemplate = {
264
- name: 'test',
265
- description: 'Test skill',
266
- category: 'optional',
267
- dependencies: [{ type: 'documentation', required: true }],
268
- content: 'content',
269
- };
270
-
271
- const config = {
272
- project: { name: 'Test', root: '/test' },
273
- integrations: {
274
- documentation: { provider: 'confluence', config: {} },
275
- },
276
- } as ResolvedCoreAIConfig;
277
-
278
- const result = checkDependencies(skill, config);
279
- expect(result.satisfied).toBe(true);
280
- });
281
-
282
- it('should check state dependency', () => {
283
- const skill: SkillTemplate = {
284
- name: 'test',
285
- description: 'Test skill',
286
- category: 'optional',
287
- dependencies: [{ type: 'state', required: true }],
288
- content: 'content',
289
- };
290
-
291
- const config = {
292
- project: { name: 'Test', root: '/test' },
293
- integrations: {
294
- state: { provider: 'filesystem', config: {} },
295
- },
296
- } as ResolvedCoreAIConfig;
297
-
298
- const result = checkDependencies(skill, config);
299
- expect(result.satisfied).toBe(true);
300
- });
301
- });
302
-
303
- describe('loadCustomTemplates', () => {
304
- let testDir: string;
305
-
306
- beforeEach(async () => {
307
- testDir = join(
308
- tmpdir(),
309
- `skill-templates-test-${Date.now()}-${Math.random().toString(36).slice(2)}`
310
- );
311
- await fs.mkdir(testDir, { recursive: true });
312
- });
313
-
314
- afterEach(async () => {
315
- try {
316
- await fs.rm(testDir, { recursive: true, force: true });
317
- } catch {
318
- // Ignore cleanup errors
319
- }
320
- });
321
-
322
- it('should return empty array for non-existent directory', () => {
323
- const templates = loadCustomTemplates('/non/existent/path');
324
- expect(templates).toEqual([]);
325
- });
326
-
327
- it('should load skill template from markdown file', async () => {
328
- const content = `---
329
- description: Custom skill
330
- argument-hint: <argument>
331
- ---
332
-
333
- # Custom Skill
334
-
335
- This is a custom skill.
336
- `;
337
- await fs.writeFile(join(testDir, 'custom-skill.md'), content);
338
-
339
- const templates = loadCustomTemplates(testDir);
340
- expect(templates).toHaveLength(1);
341
- expect(templates[0]?.name).toBe('custom-skill');
342
- expect(templates[0]?.description).toBe('Custom skill');
343
- expect(templates[0]?.argumentHint).toBe('<argument>');
344
- expect(templates[0]?.category).toBe('custom');
345
- });
346
-
347
- it('should parse required dependencies', async () => {
348
- const content = `---
349
- description: Skill with deps
350
- requires: [jira, github]
351
- ---
352
-
353
- Content
354
- `;
355
- await fs.writeFile(join(testDir, 'with-deps.md'), content);
356
-
357
- const templates = loadCustomTemplates(testDir);
358
- expect(templates).toHaveLength(1);
359
- expect(templates[0]?.dependencies).toBeDefined();
360
- expect(templates[0]?.dependencies).toHaveLength(2);
361
- expect(templates[0]?.dependencies?.[0]?.type).toBe('issue_tracker');
362
- expect(templates[0]?.dependencies?.[0]?.required).toBe(true);
363
- expect(templates[0]?.dependencies?.[1]?.type).toBe('git');
364
- });
365
-
366
- it('should parse optional dependencies', async () => {
367
- const content = `---
368
- description: Skill with optional deps
369
- optional: [docs]
370
- ---
371
-
372
- Content
373
- `;
374
- await fs.writeFile(join(testDir, 'optional-deps.md'), content);
375
-
376
- const templates = loadCustomTemplates(testDir);
377
- expect(templates).toHaveLength(1);
378
- expect(templates[0]?.dependencies).toHaveLength(1);
379
- expect(templates[0]?.dependencies?.[0]?.type).toBe('documentation');
380
- expect(templates[0]?.dependencies?.[0]?.required).toBe(false);
381
- });
382
-
383
- it('should skip non-markdown files', async () => {
384
- await fs.writeFile(join(testDir, 'readme.txt'), 'Not a template');
385
- await fs.writeFile(join(testDir, 'skill.md'), '---\ndescription: Test\n---\nContent');
386
-
387
- const templates = loadCustomTemplates(testDir);
388
- expect(templates).toHaveLength(1);
389
- expect(templates[0]?.name).toBe('skill');
390
- });
391
- });
392
-
393
- describe('generateSkills', () => {
394
- let testDir: string;
395
-
396
- beforeEach(async () => {
397
- testDir = join(
398
- tmpdir(),
399
- `skill-gen-test-${Date.now()}-${Math.random().toString(36).slice(2)}`
400
- );
401
- await fs.mkdir(testDir, { recursive: true });
402
- });
403
-
404
- afterEach(async () => {
405
- try {
406
- await fs.rm(testDir, { recursive: true, force: true });
407
- } catch {
408
- // Ignore cleanup errors
409
- }
410
- });
411
-
412
- it('should generate skills to output directory', () => {
413
- const result = generateSkills(undefined, {
414
- projectRoot: testDir,
415
- includeCoreSkills: true,
416
- includeOptionalSkills: false,
417
- });
418
-
419
- expect(result.generated.length).toBeGreaterThan(0);
420
- expect(result.errors).toEqual([]);
421
-
422
- // Check a file was created
423
- const created = result.generated.filter((g) => g.action === 'created');
424
- expect(created.length).toBeGreaterThan(0);
425
- });
426
-
427
- it('should skip existing files without overwrite', async () => {
428
- const outputDir = join(testDir, '.claude', 'commands');
429
- await fs.mkdir(outputDir, { recursive: true });
430
- await fs.writeFile(join(outputDir, 'check-inbox.md'), 'existing content');
431
-
432
- const result = generateSkills(undefined, {
433
- projectRoot: testDir,
434
- includeCoreSkills: true,
435
- includeOptionalSkills: false,
436
- skills: ['check-inbox'],
437
- });
438
-
439
- const skipped = result.generated.filter((g) => g.action === 'skipped');
440
- expect(skipped).toHaveLength(1);
441
- expect(skipped[0]?.name).toBe('check-inbox');
442
- expect(skipped[0]?.skipReason).toContain('already exists');
443
- });
444
-
445
- it('should overwrite existing files with overwrite option', async () => {
446
- const outputDir = join(testDir, '.claude', 'commands');
447
- await fs.mkdir(outputDir, { recursive: true });
448
- await fs.writeFile(join(outputDir, 'check-inbox.md'), 'old content');
449
-
450
- const result = generateSkills(undefined, {
451
- projectRoot: testDir,
452
- includeCoreSkills: true,
453
- includeOptionalSkills: false,
454
- skills: ['check-inbox'],
455
- overwrite: true,
456
- });
457
-
458
- const updated = result.generated.filter((g) => g.action === 'updated');
459
- expect(updated).toHaveLength(1);
460
- expect(updated[0]?.name).toBe('check-inbox');
461
- });
462
-
463
- it('should filter to specific skills', () => {
464
- const result = generateSkills(undefined, {
465
- projectRoot: testDir,
466
- skills: ['check-inbox', 'delegate'],
467
- });
468
-
469
- expect(result.generated).toHaveLength(2);
470
- const names = result.generated.map((g) => g.name);
471
- expect(names).toContain('check-inbox');
472
- expect(names).toContain('delegate');
473
- });
474
-
475
- it('should skip skills with missing dependencies', () => {
476
- const config = {
477
- project: { name: 'Test', root: testDir },
478
- } as ResolvedCoreAIConfig;
479
-
480
- const result = generateSkills(config, {
481
- projectRoot: testDir,
482
- includeCoreSkills: false,
483
- includeOptionalSkills: true,
484
- skills: ['jira-create'], // Requires issue_tracker
485
- });
486
-
487
- const skipped = result.generated.filter((g) => g.action === 'skipped');
488
- expect(skipped.length).toBeGreaterThan(0);
489
- const jiraSkill = skipped.find((s) => s.name === 'jira-create');
490
- expect(jiraSkill?.skipReason).toContain('Missing required integrations');
491
- });
492
-
493
- it('should substitute variables in templates', () => {
494
- const config = {
495
- project: { name: 'TestProject', root: testDir },
496
- quality_gates: {
497
- lint: { command: 'npm run lint', required: true },
498
- test: { command: 'npm test', required: true },
499
- build: { command: 'npm run build', required: true },
500
- },
501
- } as ResolvedCoreAIConfig;
502
-
503
- const result = generateSkills(config, {
504
- projectRoot: testDir,
505
- includeCoreSkills: true,
506
- includeOptionalSkills: false,
507
- skills: ['git-commit'],
508
- });
509
-
510
- expect(result.generated).toHaveLength(1);
511
- expect(result.variables.LINT_CMD).toBe('npm run lint');
512
- });
513
-
514
- it('should merge custom variables', () => {
515
- const result = generateSkills(undefined, {
516
- projectRoot: testDir,
517
- includeCoreSkills: true,
518
- includeOptionalSkills: false,
519
- variables: {
520
- CUSTOM_VAR: 'custom value',
521
- },
522
- });
523
-
524
- expect(result.variables.CUSTOM_VAR).toBe('custom value');
525
- });
526
- });
527
-
528
- describe('formatGenerateResult', () => {
529
- it('should format result with created skills', () => {
530
- const result: GenerateSkillsResult = {
531
- generated: [
532
- { name: 'skill1', category: 'core', outputPath: '/out/skill1.md', action: 'created' },
533
- { name: 'skill2', category: 'core', outputPath: '/out/skill2.md', action: 'created' },
534
- ],
535
- errors: [],
536
- variables: {},
537
- };
538
-
539
- const output = formatGenerateResult(result);
540
- expect(output).toContain('Created 2 skill(s)');
541
- expect(output).toContain('skill1');
542
- expect(output).toContain('skill2');
543
- });
544
-
545
- it('should format result with updated skills', () => {
546
- const result: GenerateSkillsResult = {
547
- generated: [
548
- { name: 'skill1', category: 'core', outputPath: '/out/skill1.md', action: 'updated' },
549
- ],
550
- errors: [],
551
- variables: {},
552
- };
553
-
554
- const output = formatGenerateResult(result);
555
- expect(output).toContain('Updated 1 skill(s)');
556
- });
557
-
558
- it('should format result with skipped skills', () => {
559
- const result: GenerateSkillsResult = {
560
- generated: [
561
- {
562
- name: 'skill1',
563
- category: 'core',
564
- outputPath: '/out/skill1.md',
565
- action: 'skipped',
566
- skipReason: 'File exists',
567
- },
568
- ],
569
- errors: [],
570
- variables: {},
571
- };
572
-
573
- const output = formatGenerateResult(result);
574
- expect(output).toContain('Skipped 1 skill(s)');
575
- expect(output).toContain('File exists');
576
- });
577
-
578
- it('should format result with errors', () => {
579
- const result: GenerateSkillsResult = {
580
- generated: [],
581
- errors: [{ name: 'bad-skill', error: 'Parse error' }],
582
- variables: {},
583
- };
584
-
585
- const output = formatGenerateResult(result);
586
- expect(output).toContain('Failed to generate 1 skill(s)');
587
- expect(output).toContain('bad-skill');
588
- expect(output).toContain('Parse error');
589
- });
590
-
591
- it('should show message when nothing to generate', () => {
592
- const result: GenerateSkillsResult = {
593
- generated: [],
594
- errors: [],
595
- variables: {},
596
- };
597
-
598
- const output = formatGenerateResult(result);
599
- expect(output).toContain('No skills to generate');
600
- });
601
- });
602
- });