specforge-mcp 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. package/dist/index.js +0 -0
  2. package/package.json +1 -1
  3. package/dist/engine/agent-generator.test.d.ts +0 -2
  4. package/dist/engine/agent-generator.test.d.ts.map +0 -1
  5. package/dist/engine/agent-generator.test.js +0 -556
  6. package/dist/engine/agent-generator.test.js.map +0 -1
  7. package/dist/engine/analyzer.test.d.ts +0 -2
  8. package/dist/engine/analyzer.test.d.ts.map +0 -1
  9. package/dist/engine/analyzer.test.js +0 -1461
  10. package/dist/engine/analyzer.test.js.map +0 -1
  11. package/dist/engine/auditor.test.d.ts +0 -2
  12. package/dist/engine/auditor.test.d.ts.map +0 -1
  13. package/dist/engine/auditor.test.js +0 -2075
  14. package/dist/engine/auditor.test.js.map +0 -1
  15. package/dist/engine/doc-generator.test.d.ts +0 -2
  16. package/dist/engine/doc-generator.test.d.ts.map +0 -1
  17. package/dist/engine/doc-generator.test.js +0 -961
  18. package/dist/engine/doc-generator.test.js.map +0 -1
  19. package/dist/engine/estimator.test.d.ts +0 -2
  20. package/dist/engine/estimator.test.d.ts.map +0 -1
  21. package/dist/engine/estimator.test.js +0 -334
  22. package/dist/engine/estimator.test.js.map +0 -1
  23. package/dist/engine/skill-generator.test.d.ts +0 -2
  24. package/dist/engine/skill-generator.test.d.ts.map +0 -1
  25. package/dist/engine/skill-generator.test.js +0 -742
  26. package/dist/engine/skill-generator.test.js.map +0 -1
  27. package/dist/engine/validator.test.d.ts +0 -2
  28. package/dist/engine/validator.test.d.ts.map +0 -1
  29. package/dist/engine/validator.test.js +0 -2371
  30. package/dist/engine/validator.test.js.map +0 -1
  31. package/dist/engine/web-fetcher.test.d.ts +0 -2
  32. package/dist/engine/web-fetcher.test.d.ts.map +0 -1
  33. package/dist/engine/web-fetcher.test.js +0 -360
  34. package/dist/engine/web-fetcher.test.js.map +0 -1
  35. package/dist/i18n/index.test.d.ts +0 -2
  36. package/dist/i18n/index.test.d.ts.map +0 -1
  37. package/dist/i18n/index.test.js +0 -375
  38. package/dist/i18n/index.test.js.map +0 -1
  39. package/dist/index.test.d.ts +0 -2
  40. package/dist/index.test.d.ts.map +0 -1
  41. package/dist/index.test.js +0 -124
  42. package/dist/index.test.js.map +0 -1
  43. package/dist/resources/patterns.test.d.ts +0 -2
  44. package/dist/resources/patterns.test.d.ts.map +0 -1
  45. package/dist/resources/patterns.test.js +0 -142
  46. package/dist/resources/patterns.test.js.map +0 -1
  47. package/dist/resources/process.test.d.ts +0 -2
  48. package/dist/resources/process.test.d.ts.map +0 -1
  49. package/dist/resources/process.test.js +0 -48
  50. package/dist/resources/process.test.js.map +0 -1
  51. package/dist/resources/registry.test.d.ts +0 -2
  52. package/dist/resources/registry.test.d.ts.map +0 -1
  53. package/dist/resources/registry.test.js +0 -138
  54. package/dist/resources/registry.test.js.map +0 -1
  55. package/dist/resources/specs.test.d.ts +0 -2
  56. package/dist/resources/specs.test.d.ts.map +0 -1
  57. package/dist/resources/specs.test.js +0 -130
  58. package/dist/resources/specs.test.js.map +0 -1
  59. package/dist/resources/templates.test.d.ts +0 -2
  60. package/dist/resources/templates.test.d.ts.map +0 -1
  61. package/dist/resources/templates.test.js +0 -119
  62. package/dist/resources/templates.test.js.map +0 -1
  63. package/dist/smoke.test.d.ts +0 -2
  64. package/dist/smoke.test.d.ts.map +0 -1
  65. package/dist/smoke.test.js +0 -229
  66. package/dist/smoke.test.js.map +0 -1
  67. package/dist/storage/base-store.test.d.ts +0 -2
  68. package/dist/storage/base-store.test.d.ts.map +0 -1
  69. package/dist/storage/base-store.test.js +0 -180
  70. package/dist/storage/base-store.test.js.map +0 -1
  71. package/dist/storage/global-store.test.d.ts +0 -2
  72. package/dist/storage/global-store.test.d.ts.map +0 -1
  73. package/dist/storage/global-store.test.js +0 -327
  74. package/dist/storage/global-store.test.js.map +0 -1
  75. package/dist/storage/index.test.d.ts +0 -2
  76. package/dist/storage/index.test.d.ts.map +0 -1
  77. package/dist/storage/index.test.js +0 -56
  78. package/dist/storage/index.test.js.map +0 -1
  79. package/dist/storage/knowledge-store.test.d.ts +0 -2
  80. package/dist/storage/knowledge-store.test.d.ts.map +0 -1
  81. package/dist/storage/knowledge-store.test.js +0 -368
  82. package/dist/storage/knowledge-store.test.js.map +0 -1
  83. package/dist/storage/metrics-store.test.d.ts +0 -2
  84. package/dist/storage/metrics-store.test.d.ts.map +0 -1
  85. package/dist/storage/metrics-store.test.js +0 -212
  86. package/dist/storage/metrics-store.test.js.map +0 -1
  87. package/dist/storage/pattern-store.test.d.ts +0 -2
  88. package/dist/storage/pattern-store.test.d.ts.map +0 -1
  89. package/dist/storage/pattern-store.test.js +0 -224
  90. package/dist/storage/pattern-store.test.js.map +0 -1
  91. package/dist/storage/spec-store.test.d.ts +0 -2
  92. package/dist/storage/spec-store.test.d.ts.map +0 -1
  93. package/dist/storage/spec-store.test.js +0 -227
  94. package/dist/storage/spec-store.test.js.map +0 -1
  95. package/dist/tools/audit.test.d.ts +0 -2
  96. package/dist/tools/audit.test.d.ts.map +0 -1
  97. package/dist/tools/audit.test.js +0 -169
  98. package/dist/tools/audit.test.js.map +0 -1
  99. package/dist/tools/challenge-spec.test.d.ts +0 -2
  100. package/dist/tools/challenge-spec.test.d.ts.map +0 -1
  101. package/dist/tools/challenge-spec.test.js +0 -782
  102. package/dist/tools/challenge-spec.test.js.map +0 -1
  103. package/dist/tools/check-versions.test.d.ts +0 -2
  104. package/dist/tools/check-versions.test.d.ts.map +0 -1
  105. package/dist/tools/check-versions.test.js +0 -214
  106. package/dist/tools/check-versions.test.js.map +0 -1
  107. package/dist/tools/clarify-requirements.test.d.ts +0 -2
  108. package/dist/tools/clarify-requirements.test.d.ts.map +0 -1
  109. package/dist/tools/clarify-requirements.test.js +0 -161
  110. package/dist/tools/clarify-requirements.test.js.map +0 -1
  111. package/dist/tools/consult-docs.test.d.ts +0 -2
  112. package/dist/tools/consult-docs.test.d.ts.map +0 -1
  113. package/dist/tools/consult-docs.test.js +0 -140
  114. package/dist/tools/consult-docs.test.js.map +0 -1
  115. package/dist/tools/create-spec.test.d.ts +0 -2
  116. package/dist/tools/create-spec.test.d.ts.map +0 -1
  117. package/dist/tools/create-spec.test.js +0 -233
  118. package/dist/tools/create-spec.test.js.map +0 -1
  119. package/dist/tools/define-ui-contract.test.d.ts +0 -2
  120. package/dist/tools/define-ui-contract.test.d.ts.map +0 -1
  121. package/dist/tools/define-ui-contract.test.js +0 -479
  122. package/dist/tools/define-ui-contract.test.js.map +0 -1
  123. package/dist/tools/design-schema.test.d.ts +0 -2
  124. package/dist/tools/design-schema.test.d.ts.map +0 -1
  125. package/dist/tools/design-schema.test.js +0 -301
  126. package/dist/tools/design-schema.test.js.map +0 -1
  127. package/dist/tools/detect-agent.test.d.ts +0 -2
  128. package/dist/tools/detect-agent.test.d.ts.map +0 -1
  129. package/dist/tools/detect-agent.test.js +0 -133
  130. package/dist/tools/detect-agent.test.js.map +0 -1
  131. package/dist/tools/detect-drift.test.d.ts +0 -2
  132. package/dist/tools/detect-drift.test.d.ts.map +0 -1
  133. package/dist/tools/detect-drift.test.js +0 -312
  134. package/dist/tools/detect-drift.test.js.map +0 -1
  135. package/dist/tools/discover-mcps.test.d.ts +0 -2
  136. package/dist/tools/discover-mcps.test.d.ts.map +0 -1
  137. package/dist/tools/discover-mcps.test.js +0 -345
  138. package/dist/tools/discover-mcps.test.js.map +0 -1
  139. package/dist/tools/estimate.test.d.ts +0 -2
  140. package/dist/tools/estimate.test.d.ts.map +0 -1
  141. package/dist/tools/estimate.test.js +0 -137
  142. package/dist/tools/estimate.test.js.map +0 -1
  143. package/dist/tools/generate-adr.test.d.ts +0 -2
  144. package/dist/tools/generate-adr.test.d.ts.map +0 -1
  145. package/dist/tools/generate-adr.test.js +0 -206
  146. package/dist/tools/generate-adr.test.js.map +0 -1
  147. package/dist/tools/generate-checklist.test.d.ts +0 -2
  148. package/dist/tools/generate-checklist.test.d.ts.map +0 -1
  149. package/dist/tools/generate-checklist.test.js +0 -201
  150. package/dist/tools/generate-checklist.test.js.map +0 -1
  151. package/dist/tools/generate-docs.test.d.ts +0 -2
  152. package/dist/tools/generate-docs.test.d.ts.map +0 -1
  153. package/dist/tools/generate-docs.test.js +0 -183
  154. package/dist/tools/generate-docs.test.js.map +0 -1
  155. package/dist/tools/generate-execution-plan.test.d.ts +0 -2
  156. package/dist/tools/generate-execution-plan.test.d.ts.map +0 -1
  157. package/dist/tools/generate-execution-plan.test.js +0 -643
  158. package/dist/tools/generate-execution-plan.test.js.map +0 -1
  159. package/dist/tools/generate-rules.test.d.ts +0 -2
  160. package/dist/tools/generate-rules.test.d.ts.map +0 -1
  161. package/dist/tools/generate-rules.test.js +0 -148
  162. package/dist/tools/generate-rules.test.js.map +0 -1
  163. package/dist/tools/generate-skill.test.d.ts +0 -2
  164. package/dist/tools/generate-skill.test.d.ts.map +0 -1
  165. package/dist/tools/generate-skill.test.js +0 -138
  166. package/dist/tools/generate-skill.test.js.map +0 -1
  167. package/dist/tools/generate-sub-agent.test.d.ts +0 -2
  168. package/dist/tools/generate-sub-agent.test.d.ts.map +0 -1
  169. package/dist/tools/generate-sub-agent.test.js +0 -162
  170. package/dist/tools/generate-sub-agent.test.js.map +0 -1
  171. package/dist/tools/generate-tests.test.d.ts +0 -2
  172. package/dist/tools/generate-tests.test.d.ts.map +0 -1
  173. package/dist/tools/generate-tests.test.js +0 -222
  174. package/dist/tools/generate-tests.test.js.map +0 -1
  175. package/dist/tools/init-constitution.test.d.ts +0 -2
  176. package/dist/tools/init-constitution.test.d.ts.map +0 -1
  177. package/dist/tools/init-constitution.test.js +0 -398
  178. package/dist/tools/init-constitution.test.js.map +0 -1
  179. package/dist/tools/init-project.test.d.ts +0 -2
  180. package/dist/tools/init-project.test.d.ts.map +0 -1
  181. package/dist/tools/init-project.test.js +0 -158
  182. package/dist/tools/init-project.test.js.map +0 -1
  183. package/dist/tools/integrate-pm.test.d.ts +0 -2
  184. package/dist/tools/integrate-pm.test.d.ts.map +0 -1
  185. package/dist/tools/integrate-pm.test.js +0 -558
  186. package/dist/tools/integrate-pm.test.js.map +0 -1
  187. package/dist/tools/learn.test.d.ts +0 -2
  188. package/dist/tools/learn.test.d.ts.map +0 -1
  189. package/dist/tools/learn.test.js +0 -123
  190. package/dist/tools/learn.test.js.map +0 -1
  191. package/dist/tools/list-specs.test.d.ts +0 -2
  192. package/dist/tools/list-specs.test.d.ts.map +0 -1
  193. package/dist/tools/list-specs.test.js +0 -110
  194. package/dist/tools/list-specs.test.js.map +0 -1
  195. package/dist/tools/manage-context.test.d.ts +0 -2
  196. package/dist/tools/manage-context.test.d.ts.map +0 -1
  197. package/dist/tools/manage-context.test.js +0 -359
  198. package/dist/tools/manage-context.test.js.map +0 -1
  199. package/dist/tools/manage-git.test.d.ts +0 -2
  200. package/dist/tools/manage-git.test.d.ts.map +0 -1
  201. package/dist/tools/manage-git.test.js +0 -882
  202. package/dist/tools/manage-git.test.js.map +0 -1
  203. package/dist/tools/orchestrate.test.d.ts +0 -2
  204. package/dist/tools/orchestrate.test.d.ts.map +0 -1
  205. package/dist/tools/orchestrate.test.js +0 -1117
  206. package/dist/tools/orchestrate.test.js.map +0 -1
  207. package/dist/tools/reconcile-spec.test.d.ts +0 -2
  208. package/dist/tools/reconcile-spec.test.d.ts.map +0 -1
  209. package/dist/tools/reconcile-spec.test.js +0 -259
  210. package/dist/tools/reconcile-spec.test.js.map +0 -1
  211. package/dist/tools/register-platform-tools.test.d.ts +0 -2
  212. package/dist/tools/register-platform-tools.test.d.ts.map +0 -1
  213. package/dist/tools/register-platform-tools.test.js +0 -404
  214. package/dist/tools/register-platform-tools.test.js.map +0 -1
  215. package/dist/tools/register-spec-tools.test.d.ts +0 -2
  216. package/dist/tools/register-spec-tools.test.d.ts.map +0 -1
  217. package/dist/tools/register-spec-tools.test.js +0 -407
  218. package/dist/tools/register-spec-tools.test.js.map +0 -1
  219. package/dist/tools/reverse-engineer.test.d.ts +0 -2
  220. package/dist/tools/reverse-engineer.test.d.ts.map +0 -1
  221. package/dist/tools/reverse-engineer.test.js +0 -206
  222. package/dist/tools/reverse-engineer.test.js.map +0 -1
  223. package/dist/tools/schemas.d.ts +0 -20
  224. package/dist/tools/schemas.d.ts.map +0 -1
  225. package/dist/tools/schemas.js +0 -133
  226. package/dist/tools/schemas.js.map +0 -1
  227. package/dist/tools/schemas.test.d.ts +0 -2
  228. package/dist/tools/schemas.test.d.ts.map +0 -1
  229. package/dist/tools/schemas.test.js +0 -245
  230. package/dist/tools/schemas.test.js.map +0 -1
  231. package/dist/tools/set-locale.test.d.ts +0 -2
  232. package/dist/tools/set-locale.test.d.ts.map +0 -1
  233. package/dist/tools/set-locale.test.js +0 -74
  234. package/dist/tools/set-locale.test.js.map +0 -1
  235. package/dist/tools/suggest-mcps.test.d.ts +0 -2
  236. package/dist/tools/suggest-mcps.test.d.ts.map +0 -1
  237. package/dist/tools/suggest-mcps.test.js +0 -198
  238. package/dist/tools/suggest-mcps.test.js.map +0 -1
  239. package/dist/tools/suggest-stack.test.d.ts +0 -2
  240. package/dist/tools/suggest-stack.test.d.ts.map +0 -1
  241. package/dist/tools/suggest-stack.test.js +0 -181
  242. package/dist/tools/suggest-stack.test.js.map +0 -1
  243. package/dist/tools/suggest-tooling.test.d.ts +0 -2
  244. package/dist/tools/suggest-tooling.test.d.ts.map +0 -1
  245. package/dist/tools/suggest-tooling.test.js +0 -213
  246. package/dist/tools/suggest-tooling.test.js.map +0 -1
  247. package/dist/tools/summarize-spec.test.d.ts +0 -2
  248. package/dist/tools/summarize-spec.test.d.ts.map +0 -1
  249. package/dist/tools/summarize-spec.test.js +0 -180
  250. package/dist/tools/summarize-spec.test.js.map +0 -1
  251. package/dist/tools/update-status.test.d.ts +0 -2
  252. package/dist/tools/update-status.test.d.ts.map +0 -1
  253. package/dist/tools/update-status.test.js +0 -142
  254. package/dist/tools/update-status.test.js.map +0 -1
  255. package/dist/tools/validate.test.d.ts +0 -2
  256. package/dist/tools/validate.test.d.ts.map +0 -1
  257. package/dist/tools/validate.test.js +0 -137
  258. package/dist/tools/validate.test.js.map +0 -1
@@ -1,742 +0,0 @@
1
- // SpecForge — Skill & Rules Generator Engine Tests
2
- import { describe, it, expect, vi, beforeEach } from 'vitest';
3
- vi.mock('node:fs/promises', () => ({
4
- readFile: vi.fn(),
5
- }));
6
- import { readFile } from 'node:fs/promises';
7
- import { generateSkill, generateRules, getAgentConfig, detectAgentPlatform, } from './skill-generator.js';
8
- const mockedReadFile = vi.mocked(readFile);
9
- // === Helper factories ===
10
- function makeKnowledge(overrides = {}) {
11
- return {
12
- projectId: 'proj-1',
13
- projectPath: '/home/user/my-project',
14
- locale: 'en',
15
- experienceLevel: 'intermediate',
16
- language: 'typescript',
17
- framework: 'express',
18
- packageManager: 'npm',
19
- buildCommand: 'npm run build',
20
- testCommand: 'npm test',
21
- stack: ['typescript', 'express', 'postgresql'],
22
- database: 'postgresql',
23
- architecture: {
24
- primary: 'layered',
25
- secondary: [],
26
- layers: [
27
- { name: 'API', directories: ['src/routes'], responsibility: 'HTTP handling', dependsOn: ['Service'], neverDependsOn: ['Database'] },
28
- { name: 'Service', directories: ['src/services'], responsibility: 'Business logic', dependsOn: [], neverDependsOn: [] },
29
- ],
30
- boundaries: [
31
- { name: 'Auth', directories: ['src/auth'], publicApi: ['src/auth/index.ts'], internalOnly: [] },
32
- ],
33
- communicationPatterns: ['REST'],
34
- deploymentUnits: ['docker'],
35
- },
36
- apps: [],
37
- conventions: { 'naming-files': 'kebab-case', 'naming-variables': 'camelCase' },
38
- layers: ['routes', 'services'],
39
- environments: ['development'],
40
- apiContracts: [],
41
- specLocation: 'docs/sdd/specs',
42
- envSetup: {
43
- projectId: 'proj-1',
44
- variables: [],
45
- missing: [],
46
- security: { gitignoreHasEnv: true, noSecretsInCode: true, noEnvInGit: true, noSecretsInLogs: true, usesSecretManager: false, issues: [] },
47
- setupSteps: [],
48
- },
49
- linting: {
50
- projectId: 'proj-1',
51
- language: 'typescript',
52
- detectedLinters: [],
53
- missingRecommended: [],
54
- rulesConflicts: [],
55
- customRulesFromProject: [],
56
- maturityScore: 0.5,
57
- },
58
- availableMcps: [],
59
- docsRegistry: {},
60
- qualityProfile: {
61
- enabledCategories: ['solid', 'clean-code'],
62
- customRules: [],
63
- principles: ['SOLID', 'DRY'],
64
- strictness: 'strict',
65
- },
66
- lastAnalyzed: '2025-01-01T00:00:00Z',
67
- ...overrides,
68
- };
69
- }
70
- function makePlatformsConfig() {
71
- return {
72
- platforms: {
73
- 'claude-code': {
74
- name: 'Claude Code',
75
- rulesFile: 'CLAUDE.md',
76
- skillsDir: '.claude/skills/',
77
- format: 'markdown',
78
- capabilities: ['mcp', 'file-edit', 'bash'],
79
- mcpSupport: true,
80
- },
81
- cursor: {
82
- name: 'Cursor',
83
- rulesFile: '.cursorrules',
84
- skillsDir: null,
85
- format: 'markdown',
86
- capabilities: ['file-edit'],
87
- mcpSupport: false,
88
- },
89
- windsurf: {
90
- name: 'Windsurf',
91
- rulesFile: '.windsurfrules',
92
- skillsDir: null,
93
- format: 'markdown',
94
- capabilities: ['file-edit'],
95
- mcpSupport: false,
96
- },
97
- aider: {
98
- name: 'Aider',
99
- rulesFile: '.aider.conf.yml',
100
- skillsDir: null,
101
- format: 'yaml',
102
- capabilities: ['file-edit'],
103
- mcpSupport: false,
104
- },
105
- copilot: {
106
- name: 'GitHub Copilot',
107
- rulesFile: '.github/copilot-instructions.md',
108
- skillsDir: null,
109
- format: 'markdown',
110
- capabilities: ['completion'],
111
- mcpSupport: false,
112
- },
113
- cline: {
114
- name: 'Cline',
115
- rulesFile: '.clinerules',
116
- skillsDir: null,
117
- format: 'markdown',
118
- capabilities: ['file-edit', 'bash'],
119
- mcpSupport: true,
120
- },
121
- continue: {
122
- name: 'Continue.dev',
123
- rulesFile: '.continue/config.json',
124
- skillsDir: null,
125
- format: 'json',
126
- capabilities: ['completion'],
127
- mcpSupport: false,
128
- },
129
- },
130
- subAgentFrameworks: {},
131
- };
132
- }
133
- function makeConstitution() {
134
- return {
135
- projectId: 'proj-1',
136
- principles: [
137
- {
138
- id: 'p-1',
139
- category: 'architecture',
140
- principle: 'Use layered architecture',
141
- rationale: 'Separation of concerns',
142
- enforceLevel: 'strict',
143
- autoEnforceable: true,
144
- source: 'auto-detected',
145
- immutable: false,
146
- },
147
- {
148
- id: 'p-2',
149
- category: 'security',
150
- principle: 'No hardcoded secrets',
151
- rationale: 'Security best practice',
152
- enforceLevel: 'recommended',
153
- autoEnforceable: true,
154
- source: 'user-defined',
155
- immutable: true,
156
- },
157
- ],
158
- createdAt: '2025-01-01T00:00:00Z',
159
- updatedAt: '2025-01-01T00:00:00Z',
160
- version: 1,
161
- };
162
- }
163
- // === Setup ===
164
- function setupConfigMock() {
165
- mockedReadFile.mockResolvedValue(JSON.stringify(makePlatformsConfig()));
166
- }
167
- beforeEach(() => {
168
- vi.resetAllMocks();
169
- });
170
- // === Tests for generateSkill ===
171
- describe('generateSkill', () => {
172
- describe('unsupported platform', () => {
173
- it('returns error for unknown platform', async () => {
174
- setupConfigMock();
175
- const knowledge = makeKnowledge();
176
- const result = await generateSkill('nonexistent-platform', 'project-rules', 'My Rules', 'Some description', knowledge);
177
- expect(result.files).toEqual([]);
178
- expect(result.message).toContain('Unknown platform');
179
- expect(result.installInstructions).toContain('not yet supported');
180
- });
181
- });
182
- describe('project-rules skill type', () => {
183
- it('generates project rules for claude-code', async () => {
184
- setupConfigMock();
185
- const knowledge = makeKnowledge();
186
- const result = await generateSkill('claude-code', 'project-rules', 'My Project Rules', 'Rules for the project', knowledge);
187
- expect(result.platform).toBe('claude-code');
188
- expect(result.files).toHaveLength(1);
189
- expect(result.files[0].path).toContain('.claude/skills/my-project-rules.md');
190
- expect(result.files[0].content).toContain('My Project Rules');
191
- expect(result.files[0].content).toContain('Rules for the project');
192
- expect(result.files[0].content).toContain('typescript');
193
- expect(result.files[0].content).toContain('express');
194
- expect(result.files[0].content).toContain('postgresql');
195
- expect(result.files[0].content).toContain('layered');
196
- expect(result.files[0].content).toContain('SOLID');
197
- expect(result.files[0].content).toContain('API');
198
- expect(result.message).toContain('project-rules');
199
- });
200
- it('uses rulesFile when no skillsDir', async () => {
201
- setupConfigMock();
202
- const knowledge = makeKnowledge();
203
- const result = await generateSkill('cursor', 'project-rules', 'My Rules', 'Description', knowledge);
204
- expect(result.files[0].path).toBe('.cursorrules');
205
- });
206
- it('handles null framework', async () => {
207
- setupConfigMock();
208
- const knowledge = makeKnowledge({ framework: null });
209
- const result = await generateSkill('claude-code', 'project-rules', 'Rules', 'Desc', knowledge);
210
- expect(result.files[0].content).toContain('Framework: none');
211
- });
212
- });
213
- describe('domain-skill skill type', () => {
214
- it('generates domain skill with specContent', async () => {
215
- setupConfigMock();
216
- const knowledge = makeKnowledge();
217
- const result = await generateSkill('claude-code', 'domain-skill', 'Auth Domain', 'Authentication domain skill', knowledge, 'This spec covers JWT authentication with refresh tokens.');
218
- expect(result.files).toHaveLength(1);
219
- expect(result.files[0].path).toContain('.claude/skills/auth-domain.md');
220
- expect(result.files[0].content).toContain('Auth Domain');
221
- expect(result.files[0].content).toContain('Authentication domain skill');
222
- expect(result.files[0].content).toContain('Spec Content');
223
- expect(result.files[0].content).toContain('JWT authentication');
224
- });
225
- it('generates domain skill without specContent', async () => {
226
- setupConfigMock();
227
- const knowledge = makeKnowledge();
228
- const result = await generateSkill('claude-code', 'domain-skill', 'Auth Domain', 'Authentication domain skill', knowledge);
229
- expect(result.files[0].content).not.toContain('Spec Content');
230
- });
231
- it('uses skills/ fallback path when no skillsDir', async () => {
232
- setupConfigMock();
233
- const knowledge = makeKnowledge();
234
- const result = await generateSkill('cursor', 'domain-skill', 'Auth Domain', 'Desc', knowledge);
235
- expect(result.files[0].path).toBe('skills/auth-domain.md');
236
- });
237
- it('handles null framework in context', async () => {
238
- setupConfigMock();
239
- const knowledge = makeKnowledge({ framework: null });
240
- const result = await generateSkill('claude-code', 'domain-skill', 'Auth', 'Desc', knowledge);
241
- expect(result.files[0].content).toContain('typescript/native');
242
- });
243
- });
244
- describe('workflow skill type', () => {
245
- it('generates workflow skill', async () => {
246
- setupConfigMock();
247
- const knowledge = makeKnowledge();
248
- const result = await generateSkill('claude-code', 'workflow', 'SDD Workflow', 'Follow the SDD process', knowledge);
249
- expect(result.files).toHaveLength(1);
250
- expect(result.files[0].content).toContain('Workflow Steps');
251
- expect(result.files[0].content).toContain('npm run build');
252
- expect(result.files[0].content).toContain('npm test');
253
- expect(result.files[0].content).toContain('docs/sdd/specs');
254
- });
255
- it('omits build/test when null', async () => {
256
- setupConfigMock();
257
- const knowledge = makeKnowledge({ buildCommand: null, testCommand: null });
258
- const result = await generateSkill('claude-code', 'workflow', 'Workflow', 'Desc', knowledge);
259
- expect(result.files[0].content).not.toContain('Build:');
260
- expect(result.files[0].content).not.toContain('Test:');
261
- });
262
- it('uses skills/ fallback path when no skillsDir', async () => {
263
- setupConfigMock();
264
- const knowledge = makeKnowledge();
265
- const result = await generateSkill('cursor', 'workflow', 'My Workflow', 'Desc', knowledge);
266
- expect(result.files[0].path).toBe('skills/my-workflow.md');
267
- });
268
- });
269
- describe('convention skill type', () => {
270
- it('generates convention skill', async () => {
271
- setupConfigMock();
272
- const knowledge = makeKnowledge();
273
- const result = await generateSkill('claude-code', 'convention', 'Code Conventions', 'Team coding conventions', knowledge);
274
- expect(result.files).toHaveLength(1);
275
- expect(result.files[0].content).toContain('Code Conventions');
276
- expect(result.files[0].content).toContain('naming-files');
277
- expect(result.files[0].content).toContain('kebab-case');
278
- expect(result.files[0].content).toContain('Strictness: strict');
279
- expect(result.files[0].content).toContain('SOLID');
280
- expect(result.files[0].content).toContain('solid');
281
- expect(result.files[0].content).toContain('clean-code');
282
- });
283
- it('uses skills/ fallback path when no skillsDir', async () => {
284
- setupConfigMock();
285
- const knowledge = makeKnowledge();
286
- const result = await generateSkill('cursor', 'convention', 'My Conventions', 'Desc', knowledge);
287
- expect(result.files[0].path).toBe('skills/my-conventions.md');
288
- });
289
- });
290
- describe('install instructions', () => {
291
- it('adds claude-code specific instructions', async () => {
292
- setupConfigMock();
293
- const knowledge = makeKnowledge();
294
- const result = await generateSkill('claude-code', 'project-rules', 'Rules', 'Desc', knowledge);
295
- expect(result.installInstructions).toContain('Claude Code');
296
- expect(result.installInstructions).toContain('CLAUDE.md');
297
- });
298
- it('adds cursor specific instructions', async () => {
299
- setupConfigMock();
300
- const knowledge = makeKnowledge();
301
- const result = await generateSkill('cursor', 'project-rules', 'Rules', 'Desc', knowledge);
302
- expect(result.installInstructions).toContain('Cursor');
303
- expect(result.installInstructions).toContain('.cursorrules');
304
- });
305
- it('adds windsurf specific instructions', async () => {
306
- setupConfigMock();
307
- const knowledge = makeKnowledge();
308
- const result = await generateSkill('windsurf', 'project-rules', 'Rules', 'Desc', knowledge);
309
- expect(result.installInstructions).toContain('Windsurf');
310
- expect(result.installInstructions).toContain('.windsurfrules');
311
- });
312
- it('generates generic instructions for other platforms', async () => {
313
- setupConfigMock();
314
- const knowledge = makeKnowledge();
315
- const result = await generateSkill('cline', 'project-rules', 'Rules', 'Desc', knowledge);
316
- expect(result.installInstructions).toContain('Cline');
317
- expect(result.installInstructions).not.toContain('automatically detect');
318
- });
319
- });
320
- describe('all platforms', () => {
321
- const platforms = ['claude-code', 'cursor', 'windsurf', 'aider', 'copilot', 'cline', 'continue'];
322
- for (const platform of platforms) {
323
- it(`generates skill for ${platform}`, async () => {
324
- setupConfigMock();
325
- const knowledge = makeKnowledge();
326
- const result = await generateSkill(platform, 'project-rules', 'Rules', 'Description', knowledge);
327
- expect(result.platform).toBe(platform);
328
- expect(result.files).toHaveLength(1);
329
- expect(result.message).toContain('project-rules');
330
- });
331
- }
332
- });
333
- });
334
- // === Tests for generateRules ===
335
- describe('generateRules', () => {
336
- describe('unsupported platform', () => {
337
- it('returns empty rules for unknown platform', async () => {
338
- setupConfigMock();
339
- const knowledge = makeKnowledge();
340
- const result = await generateRules('nonexistent', knowledge);
341
- expect(result.rulesFile.path).toBe('');
342
- expect(result.rulesFile.content).toBe('Platform not supported');
343
- expect(result.rulesCount).toBe(0);
344
- expect(result.categories).toEqual([]);
345
- });
346
- });
347
- describe('markdown format platforms', () => {
348
- it('generates full rules for claude-code with all sections', async () => {
349
- setupConfigMock();
350
- const knowledge = makeKnowledge();
351
- const result = await generateRules('claude-code', knowledge);
352
- expect(result.platform).toBe('claude-code');
353
- expect(result.rulesFile.path).toBe('CLAUDE.md');
354
- expect(result.rulesFile.format).toBe('markdown');
355
- expect(result.rulesCount).toBeGreaterThan(0);
356
- expect(result.categories).toContain('project');
357
- expect(result.categories).toContain('architecture');
358
- expect(result.categories).toContain('conventions');
359
- expect(result.categories).toContain('quality');
360
- expect(result.categories).toContain('workflow');
361
- });
362
- it('includes project overview rules', async () => {
363
- setupConfigMock();
364
- const knowledge = makeKnowledge();
365
- const result = await generateRules('claude-code', knowledge);
366
- expect(result.rulesFile.content).toContain('typescript');
367
- expect(result.rulesFile.content).toContain('express');
368
- expect(result.rulesFile.content).toContain('postgresql');
369
- expect(result.rulesFile.content).toContain('npm');
370
- expect(result.rulesFile.content).toContain('typescript, express, postgresql');
371
- });
372
- it('omits database rule when unknown', async () => {
373
- setupConfigMock();
374
- const knowledge = makeKnowledge({ database: 'unknown' });
375
- const result = await generateRules('claude-code', knowledge);
376
- expect(result.rulesFile.content).not.toContain('Database: unknown');
377
- });
378
- it('omits package manager rule when null', async () => {
379
- setupConfigMock();
380
- const knowledge = makeKnowledge({ packageManager: null });
381
- const result = await generateRules('claude-code', knowledge);
382
- expect(result.rulesFile.content).not.toContain('Package manager:');
383
- });
384
- it('omits stack rule when empty', async () => {
385
- setupConfigMock();
386
- const knowledge = makeKnowledge({ stack: [] });
387
- const result = await generateRules('claude-code', knowledge);
388
- expect(result.rulesFile.content).not.toContain('Tech stack:');
389
- });
390
- it('omits framework in project overview when null', async () => {
391
- setupConfigMock();
392
- const knowledge = makeKnowledge({ framework: null });
393
- const result = await generateRules('claude-code', knowledge);
394
- expect(result.rulesFile.content).toContain('typescript project');
395
- expect(result.rulesFile.content).not.toContain('using express');
396
- });
397
- it('includes architecture rules with layers and boundaries', async () => {
398
- setupConfigMock();
399
- const knowledge = makeKnowledge();
400
- const result = await generateRules('claude-code', knowledge);
401
- expect(result.rulesFile.content).toContain('Architecture');
402
- expect(result.rulesFile.content).toContain('layered');
403
- expect(result.rulesFile.content).toContain('API');
404
- expect(result.rulesFile.content).toContain('NEVER import from');
405
- expect(result.rulesFile.content).toContain('Auth');
406
- });
407
- it('omits neverDependsOn when empty', async () => {
408
- setupConfigMock();
409
- const knowledge = makeKnowledge();
410
- // Service layer has no neverDependsOn
411
- const result = await generateRules('claude-code', knowledge);
412
- // Only API has neverDependsOn Database
413
- expect(result.rulesFile.content).toContain('NEVER import from Database');
414
- });
415
- it('includes conventions', async () => {
416
- setupConfigMock();
417
- const knowledge = makeKnowledge();
418
- const result = await generateRules('claude-code', knowledge);
419
- expect(result.rulesFile.content).toContain('naming-files');
420
- expect(result.rulesFile.content).toContain('kebab-case');
421
- expect(result.rulesFile.content).toContain('descriptive');
422
- expect(result.rulesFile.content).toContain('pure functions');
423
- });
424
- it('includes quality rules', async () => {
425
- setupConfigMock();
426
- const knowledge = makeKnowledge();
427
- const result = await generateRules('claude-code', knowledge);
428
- expect(result.rulesFile.content).toContain('Quality');
429
- expect(result.rulesFile.content).toContain('SOLID');
430
- expect(result.rulesFile.content).toContain('security issues');
431
- expect(result.rulesFile.content).toContain('Handle all errors');
432
- });
433
- it('uses strict enforcement for strict quality profile', async () => {
434
- setupConfigMock();
435
- const knowledge = makeKnowledge();
436
- const result = await generateRules('claude-code', knowledge);
437
- expect(result.rulesFile.content).toContain('[STRICT]');
438
- });
439
- it('uses recommended enforcement for non-strict profile', async () => {
440
- setupConfigMock();
441
- const knowledge = makeKnowledge({
442
- qualityProfile: {
443
- enabledCategories: [],
444
- customRules: [],
445
- principles: ['KISS'],
446
- strictness: 'relaxed',
447
- },
448
- });
449
- const result = await generateRules('claude-code', knowledge);
450
- // KISS should be recommended, not strict
451
- const kissLine = result.rulesFile.content.split('\n').find((l) => l.includes('KISS'));
452
- expect(kissLine).toBeDefined();
453
- expect(kissLine).not.toContain('[STRICT]');
454
- });
455
- it('includes workflow rules with build/test commands', async () => {
456
- setupConfigMock();
457
- const knowledge = makeKnowledge();
458
- const result = await generateRules('claude-code', knowledge);
459
- expect(result.rulesFile.content).toContain('Workflow');
460
- expect(result.rulesFile.content).toContain('docs/sdd/specs');
461
- expect(result.rulesFile.content).toContain('npm run build');
462
- expect(result.rulesFile.content).toContain('npm test');
463
- });
464
- it('omits build/test rules when null', async () => {
465
- setupConfigMock();
466
- const knowledge = makeKnowledge({ buildCommand: null, testCommand: null });
467
- const result = await generateRules('claude-code', knowledge);
468
- expect(result.rulesFile.content).not.toContain('Build command:');
469
- expect(result.rulesFile.content).not.toContain('Test command:');
470
- });
471
- it('includes constitution when provided', async () => {
472
- setupConfigMock();
473
- const knowledge = makeKnowledge();
474
- const constitution = makeConstitution();
475
- const result = await generateRules('claude-code', knowledge, constitution);
476
- expect(result.categories).toContain('constitution');
477
- expect(result.rulesFile.content).toContain('Project Constitution');
478
- expect(result.rulesFile.content).toContain('Use layered architecture');
479
- expect(result.rulesFile.content).toContain('Separation of concerns');
480
- });
481
- it('includes custom rules when provided', async () => {
482
- setupConfigMock();
483
- const knowledge = makeKnowledge();
484
- const result = await generateRules('claude-code', knowledge, undefined, {
485
- customRules: ['Always use TypeScript strict mode', 'No any types'],
486
- });
487
- expect(result.categories).toContain('custom');
488
- expect(result.rulesFile.content).toContain('Custom Rules');
489
- expect(result.rulesFile.content).toContain('Always use TypeScript strict mode');
490
- expect(result.rulesFile.content).toContain('No any types');
491
- });
492
- it('respects include flags - exclude architecture', async () => {
493
- setupConfigMock();
494
- const knowledge = makeKnowledge();
495
- const result = await generateRules('claude-code', knowledge, undefined, {
496
- includeArchitecture: false,
497
- });
498
- expect(result.categories).not.toContain('architecture');
499
- });
500
- it('respects include flags - exclude conventions', async () => {
501
- setupConfigMock();
502
- const knowledge = makeKnowledge();
503
- const result = await generateRules('claude-code', knowledge, undefined, {
504
- includeConventions: false,
505
- });
506
- expect(result.categories).not.toContain('conventions');
507
- });
508
- it('respects include flags - exclude quality', async () => {
509
- setupConfigMock();
510
- const knowledge = makeKnowledge();
511
- const result = await generateRules('claude-code', knowledge, undefined, {
512
- includeQuality: false,
513
- });
514
- expect(result.categories).not.toContain('quality');
515
- });
516
- it('respects include flags - exclude workflow', async () => {
517
- setupConfigMock();
518
- const knowledge = makeKnowledge();
519
- const result = await generateRules('claude-code', knowledge, undefined, {
520
- includeWorkflow: false,
521
- });
522
- expect(result.categories).not.toContain('workflow');
523
- });
524
- it('excludes all optional sections', async () => {
525
- setupConfigMock();
526
- const knowledge = makeKnowledge();
527
- const result = await generateRules('claude-code', knowledge, undefined, {
528
- includeArchitecture: false,
529
- includeConventions: false,
530
- includeQuality: false,
531
- includeWorkflow: false,
532
- });
533
- expect(result.categories).toEqual(['project']);
534
- });
535
- it('generates additional SDD workflow file for claude-code', async () => {
536
- setupConfigMock();
537
- const knowledge = makeKnowledge();
538
- const result = await generateRules('claude-code', knowledge);
539
- expect(result.additionalFiles).toHaveLength(1);
540
- expect(result.additionalFiles[0].path).toContain('.claude/skills/sdd-workflow.md');
541
- expect(result.additionalFiles[0].content).toContain('SDD Workflow Skill');
542
- expect(result.additionalFiles[0].content).toContain('typescript');
543
- expect(result.additionalFiles[0].content).toContain('layered');
544
- expect(result.additionalFiles[0].content).toContain('docs/sdd/specs');
545
- });
546
- it('handles null framework in additional file', async () => {
547
- setupConfigMock();
548
- const knowledge = makeKnowledge({ framework: null });
549
- const result = await generateRules('claude-code', knowledge);
550
- expect(result.additionalFiles[0].content).toContain('Framework: none');
551
- });
552
- it('does not generate additional files for non-claude-code platforms', async () => {
553
- setupConfigMock();
554
- const knowledge = makeKnowledge();
555
- const result = await generateRules('cursor', knowledge);
556
- expect(result.additionalFiles).toHaveLength(0);
557
- });
558
- it('includes platform title in markdown header', async () => {
559
- setupConfigMock();
560
- const knowledge = makeKnowledge();
561
- const result = await generateRules('claude-code', knowledge);
562
- expect(result.rulesFile.content).toContain('# Claude Code Rules');
563
- });
564
- it('uses correct platform title for cursor', async () => {
565
- setupConfigMock();
566
- const knowledge = makeKnowledge();
567
- const result = await generateRules('cursor', knowledge);
568
- expect(result.rulesFile.content).toContain('# Cursor Rules');
569
- });
570
- it('uses correct platform title for windsurf', async () => {
571
- setupConfigMock();
572
- const knowledge = makeKnowledge();
573
- const result = await generateRules('windsurf', knowledge);
574
- expect(result.rulesFile.content).toContain('# Windsurf Rules');
575
- });
576
- it('uses correct platform title for copilot', async () => {
577
- setupConfigMock();
578
- const knowledge = makeKnowledge();
579
- const result = await generateRules('copilot', knowledge);
580
- expect(result.rulesFile.content).toContain('# GitHub Copilot Rules');
581
- });
582
- it('uses correct platform title for cline', async () => {
583
- setupConfigMock();
584
- const knowledge = makeKnowledge();
585
- const result = await generateRules('cline', knowledge);
586
- expect(result.rulesFile.content).toContain('# Cline Rules');
587
- });
588
- it('uses correct platform title for continue', async () => {
589
- setupConfigMock();
590
- const knowledge = makeKnowledge();
591
- // Continue uses JSON format, so the title won't be in markdown
592
- // but the JSON output should be valid
593
- const result = await generateRules('continue', knowledge);
594
- // JSON format doesn't have platform title in same way
595
- expect(result.rulesFile.format).toBe('json');
596
- });
597
- it('uses correct platform title for aider', async () => {
598
- setupConfigMock();
599
- const knowledge = makeKnowledge();
600
- const result = await generateRules('aider', knowledge);
601
- // aider uses yaml format, check that it is YAML and has auto-generated header
602
- expect(result.rulesFile.content).toContain('# Auto-generated by SpecForge');
603
- });
604
- });
605
- describe('json format platforms', () => {
606
- it('generates rules in JSON format for continue', async () => {
607
- setupConfigMock();
608
- const knowledge = makeKnowledge();
609
- const result = await generateRules('continue', knowledge);
610
- expect(result.rulesFile.format).toBe('json');
611
- const parsed = JSON.parse(result.rulesFile.content);
612
- expect(parsed).toBeDefined();
613
- expect(parsed.project_overview).toBeDefined();
614
- expect(parsed.architecture).toBeDefined();
615
- expect(parsed.conventions).toBeDefined();
616
- expect(parsed.quality).toBeDefined();
617
- expect(parsed.workflow).toBeDefined();
618
- });
619
- it('JSON format includes enforcement level', async () => {
620
- setupConfigMock();
621
- const knowledge = makeKnowledge();
622
- const result = await generateRules('continue', knowledge);
623
- const parsed = JSON.parse(result.rulesFile.content);
624
- expect(parsed.project_overview[0]).toHaveProperty('rule');
625
- expect(parsed.project_overview[0]).toHaveProperty('enforcement');
626
- });
627
- });
628
- describe('yaml format platforms', () => {
629
- it('generates rules in YAML format for aider', async () => {
630
- setupConfigMock();
631
- const knowledge = makeKnowledge();
632
- const result = await generateRules('aider', knowledge);
633
- expect(result.rulesFile.format).toBe('yaml');
634
- expect(result.rulesFile.content).toContain('# Auto-generated by SpecForge');
635
- expect(result.rulesFile.content).toContain('project_overview:');
636
- expect(result.rulesFile.content).toContain('enforcement: strict');
637
- });
638
- it('escapes double quotes in YAML', async () => {
639
- setupConfigMock();
640
- const knowledge = makeKnowledge({
641
- conventions: { 'quote-test': 'Use "double" quotes carefully' },
642
- });
643
- const result = await generateRules('aider', knowledge);
644
- expect(result.rulesFile.content).toContain('\\"double\\"');
645
- });
646
- });
647
- describe('all platforms generate rules', () => {
648
- const platforms = ['claude-code', 'cursor', 'windsurf', 'aider', 'copilot', 'cline', 'continue'];
649
- for (const platform of platforms) {
650
- it(`generates rules for ${platform}`, async () => {
651
- setupConfigMock();
652
- const knowledge = makeKnowledge();
653
- const result = await generateRules(platform, knowledge);
654
- expect(result.platform).toBe(platform);
655
- expect(result.rulesCount).toBeGreaterThan(0);
656
- expect(result.categories).toContain('project');
657
- });
658
- }
659
- });
660
- });
661
- // === Tests for getAgentConfig ===
662
- describe('getAgentConfig', () => {
663
- it('returns config for known platform', async () => {
664
- setupConfigMock();
665
- const config = await getAgentConfig('claude-code');
666
- expect(config.platform).toBe('claude-code');
667
- expect(config.rulesFile).toBe('CLAUDE.md');
668
- expect(config.skillsDir).toBe('.claude/skills/');
669
- expect(config.format).toBe('markdown');
670
- expect(config.capabilities).toContain('mcp');
671
- expect(config.mcpSupport).toBe(true);
672
- });
673
- it('returns empty config for unknown platform', async () => {
674
- setupConfigMock();
675
- const config = await getAgentConfig('nonexistent');
676
- expect(config.platform).toBe('nonexistent');
677
- expect(config.rulesFile).toBe('');
678
- expect(config.format).toBe('markdown');
679
- expect(config.capabilities).toEqual([]);
680
- expect(config.mcpSupport).toBe(false);
681
- });
682
- it('returns undefined skillsDir when platform has null', async () => {
683
- setupConfigMock();
684
- const config = await getAgentConfig('cursor');
685
- expect(config.skillsDir).toBeUndefined();
686
- });
687
- });
688
- // === Tests for detectAgentPlatform ===
689
- describe('detectAgentPlatform', () => {
690
- it('detects claude-code from CLAUDE.md', async () => {
691
- setupConfigMock();
692
- const result = await detectAgentPlatform({ 'ai-rules': 'CLAUDE.md' });
693
- expect(result.platform).toBe('claude-code');
694
- expect(result.confidence).toBeGreaterThan(0);
695
- expect(result.detectedFrom.length).toBeGreaterThan(0);
696
- });
697
- it('detects cursor from cursor-rules', async () => {
698
- setupConfigMock();
699
- const result = await detectAgentPlatform({ 'cursor-rules': '.cursorrules' });
700
- expect(result.platform).toBe('cursor');
701
- expect(result.confidence).toBeGreaterThan(0);
702
- expect(result.detectedFrom).toContain('.cursorrules detected');
703
- });
704
- it('detects from rules file convention', async () => {
705
- setupConfigMock();
706
- const result = await detectAgentPlatform({ 'ai-rules': '.clinerules' });
707
- expect(result.platform).toBe('cline');
708
- expect(result.detectedFrom).toContain('Rules file: .clinerules');
709
- });
710
- it('defaults to claude-code with 0 confidence when no match', async () => {
711
- setupConfigMock();
712
- const result = await detectAgentPlatform({});
713
- expect(result.platform).toBe('claude-code');
714
- expect(result.confidence).toBe(0);
715
- expect(result.detectedFrom).toEqual([]);
716
- });
717
- it('prefers higher score platform', async () => {
718
- setupConfigMock();
719
- // CLAUDE.md gives score 8 (3+5), so it should win
720
- const result = await detectAgentPlatform({
721
- 'ai-rules': 'CLAUDE.md',
722
- 'cursor-rules': '.cursorrules',
723
- });
724
- expect(result.platform).toBe('claude-code');
725
- });
726
- it('caps confidence at 1', async () => {
727
- setupConfigMock();
728
- const result = await detectAgentPlatform({ 'ai-rules': 'CLAUDE.md' });
729
- expect(result.confidence).toBeLessThanOrEqual(1);
730
- });
731
- });
732
- // === Tests for loadPlatforms cache and error handling ===
733
- describe('loadPlatforms', () => {
734
- it('uses cache on subsequent calls', async () => {
735
- setupConfigMock();
736
- await generateSkill('claude-code', 'project-rules', 'R', 'D', makeKnowledge());
737
- await generateSkill('cursor', 'project-rules', 'R', 'D', makeKnowledge());
738
- // readFile should have been called at most once due to caching
739
- expect(mockedReadFile.mock.calls.length).toBeLessThanOrEqual(1);
740
- });
741
- });
742
- //# sourceMappingURL=skill-generator.test.js.map