specforge-mcp 0.1.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (287) hide show
  1. package/README.md +31 -1
  2. package/dist/config/version.d.ts +2 -0
  3. package/dist/config/version.d.ts.map +1 -0
  4. package/dist/config/version.js +9 -0
  5. package/dist/config/version.js.map +1 -0
  6. package/dist/config/version.ts +11 -0
  7. package/dist/engine/detectors/library-detector-langs2.d.ts.map +1 -1
  8. package/dist/engine/detectors/library-detector-langs2.js.map +1 -1
  9. package/dist/engine/mermaid-generator.d.ts.map +1 -1
  10. package/dist/engine/mermaid-generator.js +12 -2
  11. package/dist/engine/mermaid-generator.js.map +1 -1
  12. package/dist/index.js +2 -1
  13. package/dist/index.js.map +1 -1
  14. package/dist/tools/create-spec-hu/hu-body-generators.d.ts.map +1 -1
  15. package/dist/tools/create-spec-hu/hu-body-generators.js +7 -1
  16. package/dist/tools/create-spec-hu/hu-body-generators.js.map +1 -1
  17. package/dist/tools/create-spec-tech/ficha-content.d.ts.map +1 -1
  18. package/dist/tools/create-spec-tech/ficha-content.js +4 -2
  19. package/dist/tools/create-spec-tech/ficha-content.js.map +1 -1
  20. package/dist/tools/create-spec.d.ts.map +1 -1
  21. package/dist/tools/create-spec.js +4 -3
  22. package/dist/tools/create-spec.js.map +1 -1
  23. package/dist/tools/register-spec-tools/core-spec-tools.d.ts.map +1 -1
  24. package/dist/tools/register-spec-tools/core-spec-tools.js +4 -0
  25. package/dist/tools/register-spec-tools/core-spec-tools.js.map +1 -1
  26. package/dist/types/spec/core.d.ts +1 -0
  27. package/dist/types/spec/core.d.ts.map +1 -1
  28. package/dist/types/spec/inputs.d.ts +1 -0
  29. package/dist/types/spec/inputs.d.ts.map +1 -1
  30. package/package.json +27 -18
  31. package/src/config/version.ts +11 -0
  32. package/dist/engine/agent-generator.test.d.ts +0 -2
  33. package/dist/engine/agent-generator.test.d.ts.map +0 -1
  34. package/dist/engine/agent-generator.test.js +0 -556
  35. package/dist/engine/agent-generator.test.js.map +0 -1
  36. package/dist/engine/analyzer.test.d.ts +0 -2
  37. package/dist/engine/analyzer.test.d.ts.map +0 -1
  38. package/dist/engine/analyzer.test.js +0 -1461
  39. package/dist/engine/analyzer.test.js.map +0 -1
  40. package/dist/engine/auditor.test.d.ts +0 -2
  41. package/dist/engine/auditor.test.d.ts.map +0 -1
  42. package/dist/engine/auditor.test.js +0 -2075
  43. package/dist/engine/auditor.test.js.map +0 -1
  44. package/dist/engine/doc-generator.test.d.ts +0 -2
  45. package/dist/engine/doc-generator.test.d.ts.map +0 -1
  46. package/dist/engine/doc-generator.test.js +0 -961
  47. package/dist/engine/doc-generator.test.js.map +0 -1
  48. package/dist/engine/estimator.test.d.ts +0 -2
  49. package/dist/engine/estimator.test.d.ts.map +0 -1
  50. package/dist/engine/estimator.test.js +0 -334
  51. package/dist/engine/estimator.test.js.map +0 -1
  52. package/dist/engine/skill-generator.test.d.ts +0 -2
  53. package/dist/engine/skill-generator.test.d.ts.map +0 -1
  54. package/dist/engine/skill-generator.test.js +0 -742
  55. package/dist/engine/skill-generator.test.js.map +0 -1
  56. package/dist/engine/validator.test.d.ts +0 -2
  57. package/dist/engine/validator.test.d.ts.map +0 -1
  58. package/dist/engine/validator.test.js +0 -2371
  59. package/dist/engine/validator.test.js.map +0 -1
  60. package/dist/engine/web-fetcher.test.d.ts +0 -2
  61. package/dist/engine/web-fetcher.test.d.ts.map +0 -1
  62. package/dist/engine/web-fetcher.test.js +0 -360
  63. package/dist/engine/web-fetcher.test.js.map +0 -1
  64. package/dist/i18n/index.test.d.ts +0 -2
  65. package/dist/i18n/index.test.d.ts.map +0 -1
  66. package/dist/i18n/index.test.js +0 -375
  67. package/dist/i18n/index.test.js.map +0 -1
  68. package/dist/index.test.d.ts +0 -2
  69. package/dist/index.test.d.ts.map +0 -1
  70. package/dist/index.test.js +0 -124
  71. package/dist/index.test.js.map +0 -1
  72. package/dist/resources/patterns.test.d.ts +0 -2
  73. package/dist/resources/patterns.test.d.ts.map +0 -1
  74. package/dist/resources/patterns.test.js +0 -142
  75. package/dist/resources/patterns.test.js.map +0 -1
  76. package/dist/resources/process.test.d.ts +0 -2
  77. package/dist/resources/process.test.d.ts.map +0 -1
  78. package/dist/resources/process.test.js +0 -48
  79. package/dist/resources/process.test.js.map +0 -1
  80. package/dist/resources/registry.test.d.ts +0 -2
  81. package/dist/resources/registry.test.d.ts.map +0 -1
  82. package/dist/resources/registry.test.js +0 -138
  83. package/dist/resources/registry.test.js.map +0 -1
  84. package/dist/resources/specs.test.d.ts +0 -2
  85. package/dist/resources/specs.test.d.ts.map +0 -1
  86. package/dist/resources/specs.test.js +0 -130
  87. package/dist/resources/specs.test.js.map +0 -1
  88. package/dist/resources/templates.test.d.ts +0 -2
  89. package/dist/resources/templates.test.d.ts.map +0 -1
  90. package/dist/resources/templates.test.js +0 -119
  91. package/dist/resources/templates.test.js.map +0 -1
  92. package/dist/smoke.test.d.ts +0 -2
  93. package/dist/smoke.test.d.ts.map +0 -1
  94. package/dist/smoke.test.js +0 -229
  95. package/dist/smoke.test.js.map +0 -1
  96. package/dist/storage/base-store.test.d.ts +0 -2
  97. package/dist/storage/base-store.test.d.ts.map +0 -1
  98. package/dist/storage/base-store.test.js +0 -180
  99. package/dist/storage/base-store.test.js.map +0 -1
  100. package/dist/storage/global-store.test.d.ts +0 -2
  101. package/dist/storage/global-store.test.d.ts.map +0 -1
  102. package/dist/storage/global-store.test.js +0 -327
  103. package/dist/storage/global-store.test.js.map +0 -1
  104. package/dist/storage/index.test.d.ts +0 -2
  105. package/dist/storage/index.test.d.ts.map +0 -1
  106. package/dist/storage/index.test.js +0 -56
  107. package/dist/storage/index.test.js.map +0 -1
  108. package/dist/storage/knowledge-store.test.d.ts +0 -2
  109. package/dist/storage/knowledge-store.test.d.ts.map +0 -1
  110. package/dist/storage/knowledge-store.test.js +0 -368
  111. package/dist/storage/knowledge-store.test.js.map +0 -1
  112. package/dist/storage/metrics-store.test.d.ts +0 -2
  113. package/dist/storage/metrics-store.test.d.ts.map +0 -1
  114. package/dist/storage/metrics-store.test.js +0 -212
  115. package/dist/storage/metrics-store.test.js.map +0 -1
  116. package/dist/storage/pattern-store.test.d.ts +0 -2
  117. package/dist/storage/pattern-store.test.d.ts.map +0 -1
  118. package/dist/storage/pattern-store.test.js +0 -224
  119. package/dist/storage/pattern-store.test.js.map +0 -1
  120. package/dist/storage/spec-store.test.d.ts +0 -2
  121. package/dist/storage/spec-store.test.d.ts.map +0 -1
  122. package/dist/storage/spec-store.test.js +0 -227
  123. package/dist/storage/spec-store.test.js.map +0 -1
  124. package/dist/tools/audit.test.d.ts +0 -2
  125. package/dist/tools/audit.test.d.ts.map +0 -1
  126. package/dist/tools/audit.test.js +0 -169
  127. package/dist/tools/audit.test.js.map +0 -1
  128. package/dist/tools/challenge-spec.test.d.ts +0 -2
  129. package/dist/tools/challenge-spec.test.d.ts.map +0 -1
  130. package/dist/tools/challenge-spec.test.js +0 -782
  131. package/dist/tools/challenge-spec.test.js.map +0 -1
  132. package/dist/tools/check-versions.test.d.ts +0 -2
  133. package/dist/tools/check-versions.test.d.ts.map +0 -1
  134. package/dist/tools/check-versions.test.js +0 -214
  135. package/dist/tools/check-versions.test.js.map +0 -1
  136. package/dist/tools/clarify-requirements.test.d.ts +0 -2
  137. package/dist/tools/clarify-requirements.test.d.ts.map +0 -1
  138. package/dist/tools/clarify-requirements.test.js +0 -161
  139. package/dist/tools/clarify-requirements.test.js.map +0 -1
  140. package/dist/tools/consult-docs.test.d.ts +0 -2
  141. package/dist/tools/consult-docs.test.d.ts.map +0 -1
  142. package/dist/tools/consult-docs.test.js +0 -140
  143. package/dist/tools/consult-docs.test.js.map +0 -1
  144. package/dist/tools/create-spec.test.d.ts +0 -2
  145. package/dist/tools/create-spec.test.d.ts.map +0 -1
  146. package/dist/tools/create-spec.test.js +0 -233
  147. package/dist/tools/create-spec.test.js.map +0 -1
  148. package/dist/tools/define-ui-contract.test.d.ts +0 -2
  149. package/dist/tools/define-ui-contract.test.d.ts.map +0 -1
  150. package/dist/tools/define-ui-contract.test.js +0 -479
  151. package/dist/tools/define-ui-contract.test.js.map +0 -1
  152. package/dist/tools/design-schema.test.d.ts +0 -2
  153. package/dist/tools/design-schema.test.d.ts.map +0 -1
  154. package/dist/tools/design-schema.test.js +0 -301
  155. package/dist/tools/design-schema.test.js.map +0 -1
  156. package/dist/tools/detect-agent.test.d.ts +0 -2
  157. package/dist/tools/detect-agent.test.d.ts.map +0 -1
  158. package/dist/tools/detect-agent.test.js +0 -133
  159. package/dist/tools/detect-agent.test.js.map +0 -1
  160. package/dist/tools/detect-drift.test.d.ts +0 -2
  161. package/dist/tools/detect-drift.test.d.ts.map +0 -1
  162. package/dist/tools/detect-drift.test.js +0 -312
  163. package/dist/tools/detect-drift.test.js.map +0 -1
  164. package/dist/tools/discover-mcps.test.d.ts +0 -2
  165. package/dist/tools/discover-mcps.test.d.ts.map +0 -1
  166. package/dist/tools/discover-mcps.test.js +0 -345
  167. package/dist/tools/discover-mcps.test.js.map +0 -1
  168. package/dist/tools/estimate.test.d.ts +0 -2
  169. package/dist/tools/estimate.test.d.ts.map +0 -1
  170. package/dist/tools/estimate.test.js +0 -137
  171. package/dist/tools/estimate.test.js.map +0 -1
  172. package/dist/tools/generate-adr.test.d.ts +0 -2
  173. package/dist/tools/generate-adr.test.d.ts.map +0 -1
  174. package/dist/tools/generate-adr.test.js +0 -206
  175. package/dist/tools/generate-adr.test.js.map +0 -1
  176. package/dist/tools/generate-checklist.test.d.ts +0 -2
  177. package/dist/tools/generate-checklist.test.d.ts.map +0 -1
  178. package/dist/tools/generate-checklist.test.js +0 -201
  179. package/dist/tools/generate-checklist.test.js.map +0 -1
  180. package/dist/tools/generate-docs.test.d.ts +0 -2
  181. package/dist/tools/generate-docs.test.d.ts.map +0 -1
  182. package/dist/tools/generate-docs.test.js +0 -183
  183. package/dist/tools/generate-docs.test.js.map +0 -1
  184. package/dist/tools/generate-execution-plan.test.d.ts +0 -2
  185. package/dist/tools/generate-execution-plan.test.d.ts.map +0 -1
  186. package/dist/tools/generate-execution-plan.test.js +0 -643
  187. package/dist/tools/generate-execution-plan.test.js.map +0 -1
  188. package/dist/tools/generate-rules.test.d.ts +0 -2
  189. package/dist/tools/generate-rules.test.d.ts.map +0 -1
  190. package/dist/tools/generate-rules.test.js +0 -148
  191. package/dist/tools/generate-rules.test.js.map +0 -1
  192. package/dist/tools/generate-skill.test.d.ts +0 -2
  193. package/dist/tools/generate-skill.test.d.ts.map +0 -1
  194. package/dist/tools/generate-skill.test.js +0 -138
  195. package/dist/tools/generate-skill.test.js.map +0 -1
  196. package/dist/tools/generate-sub-agent.test.d.ts +0 -2
  197. package/dist/tools/generate-sub-agent.test.d.ts.map +0 -1
  198. package/dist/tools/generate-sub-agent.test.js +0 -162
  199. package/dist/tools/generate-sub-agent.test.js.map +0 -1
  200. package/dist/tools/generate-tests.test.d.ts +0 -2
  201. package/dist/tools/generate-tests.test.d.ts.map +0 -1
  202. package/dist/tools/generate-tests.test.js +0 -222
  203. package/dist/tools/generate-tests.test.js.map +0 -1
  204. package/dist/tools/init-constitution.test.d.ts +0 -2
  205. package/dist/tools/init-constitution.test.d.ts.map +0 -1
  206. package/dist/tools/init-constitution.test.js +0 -398
  207. package/dist/tools/init-constitution.test.js.map +0 -1
  208. package/dist/tools/init-project.test.d.ts +0 -2
  209. package/dist/tools/init-project.test.d.ts.map +0 -1
  210. package/dist/tools/init-project.test.js +0 -158
  211. package/dist/tools/init-project.test.js.map +0 -1
  212. package/dist/tools/integrate-pm.test.d.ts +0 -2
  213. package/dist/tools/integrate-pm.test.d.ts.map +0 -1
  214. package/dist/tools/integrate-pm.test.js +0 -558
  215. package/dist/tools/integrate-pm.test.js.map +0 -1
  216. package/dist/tools/learn.test.d.ts +0 -2
  217. package/dist/tools/learn.test.d.ts.map +0 -1
  218. package/dist/tools/learn.test.js +0 -123
  219. package/dist/tools/learn.test.js.map +0 -1
  220. package/dist/tools/list-specs.test.d.ts +0 -2
  221. package/dist/tools/list-specs.test.d.ts.map +0 -1
  222. package/dist/tools/list-specs.test.js +0 -110
  223. package/dist/tools/list-specs.test.js.map +0 -1
  224. package/dist/tools/manage-context.test.d.ts +0 -2
  225. package/dist/tools/manage-context.test.d.ts.map +0 -1
  226. package/dist/tools/manage-context.test.js +0 -359
  227. package/dist/tools/manage-context.test.js.map +0 -1
  228. package/dist/tools/manage-git.test.d.ts +0 -2
  229. package/dist/tools/manage-git.test.d.ts.map +0 -1
  230. package/dist/tools/manage-git.test.js +0 -882
  231. package/dist/tools/manage-git.test.js.map +0 -1
  232. package/dist/tools/orchestrate.test.d.ts +0 -2
  233. package/dist/tools/orchestrate.test.d.ts.map +0 -1
  234. package/dist/tools/orchestrate.test.js +0 -1117
  235. package/dist/tools/orchestrate.test.js.map +0 -1
  236. package/dist/tools/reconcile-spec.test.d.ts +0 -2
  237. package/dist/tools/reconcile-spec.test.d.ts.map +0 -1
  238. package/dist/tools/reconcile-spec.test.js +0 -259
  239. package/dist/tools/reconcile-spec.test.js.map +0 -1
  240. package/dist/tools/register-platform-tools.test.d.ts +0 -2
  241. package/dist/tools/register-platform-tools.test.d.ts.map +0 -1
  242. package/dist/tools/register-platform-tools.test.js +0 -404
  243. package/dist/tools/register-platform-tools.test.js.map +0 -1
  244. package/dist/tools/register-spec-tools.test.d.ts +0 -2
  245. package/dist/tools/register-spec-tools.test.d.ts.map +0 -1
  246. package/dist/tools/register-spec-tools.test.js +0 -407
  247. package/dist/tools/register-spec-tools.test.js.map +0 -1
  248. package/dist/tools/reverse-engineer.test.d.ts +0 -2
  249. package/dist/tools/reverse-engineer.test.d.ts.map +0 -1
  250. package/dist/tools/reverse-engineer.test.js +0 -206
  251. package/dist/tools/reverse-engineer.test.js.map +0 -1
  252. package/dist/tools/schemas.d.ts +0 -20
  253. package/dist/tools/schemas.d.ts.map +0 -1
  254. package/dist/tools/schemas.js +0 -133
  255. package/dist/tools/schemas.js.map +0 -1
  256. package/dist/tools/schemas.test.d.ts +0 -2
  257. package/dist/tools/schemas.test.d.ts.map +0 -1
  258. package/dist/tools/schemas.test.js +0 -245
  259. package/dist/tools/schemas.test.js.map +0 -1
  260. package/dist/tools/set-locale.test.d.ts +0 -2
  261. package/dist/tools/set-locale.test.d.ts.map +0 -1
  262. package/dist/tools/set-locale.test.js +0 -74
  263. package/dist/tools/set-locale.test.js.map +0 -1
  264. package/dist/tools/suggest-mcps.test.d.ts +0 -2
  265. package/dist/tools/suggest-mcps.test.d.ts.map +0 -1
  266. package/dist/tools/suggest-mcps.test.js +0 -198
  267. package/dist/tools/suggest-mcps.test.js.map +0 -1
  268. package/dist/tools/suggest-stack.test.d.ts +0 -2
  269. package/dist/tools/suggest-stack.test.d.ts.map +0 -1
  270. package/dist/tools/suggest-stack.test.js +0 -181
  271. package/dist/tools/suggest-stack.test.js.map +0 -1
  272. package/dist/tools/suggest-tooling.test.d.ts +0 -2
  273. package/dist/tools/suggest-tooling.test.d.ts.map +0 -1
  274. package/dist/tools/suggest-tooling.test.js +0 -213
  275. package/dist/tools/suggest-tooling.test.js.map +0 -1
  276. package/dist/tools/summarize-spec.test.d.ts +0 -2
  277. package/dist/tools/summarize-spec.test.d.ts.map +0 -1
  278. package/dist/tools/summarize-spec.test.js +0 -180
  279. package/dist/tools/summarize-spec.test.js.map +0 -1
  280. package/dist/tools/update-status.test.d.ts +0 -2
  281. package/dist/tools/update-status.test.d.ts.map +0 -1
  282. package/dist/tools/update-status.test.js +0 -142
  283. package/dist/tools/update-status.test.js.map +0 -1
  284. package/dist/tools/validate.test.d.ts +0 -2
  285. package/dist/tools/validate.test.d.ts.map +0 -1
  286. package/dist/tools/validate.test.js +0 -137
  287. package/dist/tools/validate.test.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specforge-mcp",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
4
4
  "description": "SpecForge — MCP Server for Spec Driven Development. Manages specs, estimations, reverse engineering, and auto-learning across any language/framework.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -14,6 +14,25 @@
14
14
  "engines": {
15
15
  "node": ">=22.0.0"
16
16
  },
17
+ "packageManager": "pnpm@10.28.2",
18
+ "scripts": {
19
+ "build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json && node -e \"import('node:fs').then(({cpSync})=>cpSync('src/config','dist/config',{recursive:true}))\"",
20
+ "dev": "tsc --watch",
21
+ "start": "node dist/index.js",
22
+ "clean": "rm -rf dist",
23
+ "lint": "eslint src/ tests/ --max-warnings 0",
24
+ "lint:fix": "eslint src/ tests/ --fix --max-warnings 0",
25
+ "format": "prettier --write 'src/**/*.ts' 'tests/**/*.test.ts'",
26
+ "format:check": "prettier --check 'src/**/*.ts' 'tests/**/*.test.ts'",
27
+ "typecheck": "tsc --noEmit",
28
+ "test": "vitest run",
29
+ "test:watch": "vitest",
30
+ "test:coverage": "vitest run --coverage",
31
+ "check": "pnpm typecheck && pnpm lint && pnpm format:check",
32
+ "setup:protection": "bash scripts/setup-branch-protection.sh",
33
+ "prepare": "husky || true",
34
+ "prepublishOnly": "pnpm build"
35
+ },
17
36
  "lint-staged": {
18
37
  "src/**/*.ts": [
19
38
  "eslint --fix --max-warnings 0",
@@ -48,6 +67,11 @@
48
67
  "@eslint/js": "^10.0.1",
49
68
  "@secretlint/secretlint-rule-no-homedir": "^11.3.1",
50
69
  "@secretlint/secretlint-rule-preset-recommend": "^11.3.1",
70
+ "@semantic-release/commit-analyzer": "^13.0.1",
71
+ "@semantic-release/git": "^10.0.1",
72
+ "@semantic-release/github": "^12.0.6",
73
+ "@semantic-release/npm": "^13.1.4",
74
+ "@semantic-release/release-notes-generator": "^14.1.0",
51
75
  "@types/node": "^25.3.0",
52
76
  "@vitest/coverage-v8": "^4.0.18",
53
77
  "eslint": "^10.0.2",
@@ -57,25 +81,10 @@
57
81
  "lint-staged": "^16.2.7",
58
82
  "prettier": "^3.8.1",
59
83
  "secretlint": "^11.3.1",
84
+ "semantic-release": "^25.0.3",
60
85
  "tsc-alias": "^1.8.16",
61
86
  "typescript": "^5.7.0",
62
87
  "typescript-eslint": "^8.56.1",
63
88
  "vitest": "^4.0.18"
64
- },
65
- "scripts": {
66
- "build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json && node -e \"import('node:fs').then(({cpSync})=>cpSync('src/config','dist/config',{recursive:true}))\"",
67
- "dev": "tsc --watch",
68
- "start": "node dist/index.js",
69
- "clean": "rm -rf dist",
70
- "lint": "eslint src/ tests/ --max-warnings 0",
71
- "lint:fix": "eslint src/ tests/ --fix --max-warnings 0",
72
- "format": "prettier --write 'src/**/*.ts' 'tests/**/*.test.ts'",
73
- "format:check": "prettier --check 'src/**/*.ts' 'tests/**/*.test.ts'",
74
- "typecheck": "tsc --noEmit",
75
- "test": "vitest run",
76
- "test:watch": "vitest",
77
- "test:coverage": "vitest run --coverage",
78
- "check": "pnpm typecheck && pnpm lint && pnpm format:check",
79
- "setup:protection": "bash scripts/setup-branch-protection.sh"
80
89
  }
81
- }
90
+ }
@@ -0,0 +1,11 @@
1
+ // version.ts — reads from package.json at runtime; updated automatically by semantic-release
2
+
3
+ import { readFileSync } from 'node:fs';
4
+ import { dirname, join } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+
7
+ const pkgPath = join(dirname(fileURLToPath(import.meta.url)), '../../package.json');
8
+ const raw: unknown = JSON.parse(readFileSync(pkgPath, 'utf8'));
9
+ const { version } = raw as { version: string };
10
+
11
+ export const SPECFORGE_VERSION = version;
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=agent-generator.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"agent-generator.test.d.ts","sourceRoot":"","sources":["../../src/engine/agent-generator.test.ts"],"names":[],"mappings":""}
@@ -1,556 +0,0 @@
1
- // SpecForge — Agent 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 { generateSubAgent, generateAgentSystem, getFrameworkConfig } from './agent-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'],
22
- database: 'postgresql',
23
- architecture: {
24
- primary: 'layered',
25
- secondary: [],
26
- layers: [
27
- { name: 'API', directories: ['src/routes'], responsibility: 'HTTP', dependsOn: ['Service'], neverDependsOn: [] },
28
- { name: 'Service', directories: ['src/services'], responsibility: 'Logic', dependsOn: [], neverDependsOn: [] },
29
- ],
30
- boundaries: [],
31
- communicationPatterns: ['REST'],
32
- deploymentUnits: ['docker'],
33
- },
34
- apps: [],
35
- conventions: {},
36
- layers: ['routes', 'services'],
37
- environments: ['development'],
38
- apiContracts: [],
39
- specLocation: 'docs/sdd/specs',
40
- envSetup: {
41
- projectId: 'proj-1',
42
- variables: [],
43
- missing: [],
44
- security: { gitignoreHasEnv: true, noSecretsInCode: true, noEnvInGit: true, noSecretsInLogs: true, usesSecretManager: false, issues: [] },
45
- setupSteps: [],
46
- },
47
- linting: {
48
- projectId: 'proj-1',
49
- language: 'typescript',
50
- detectedLinters: [],
51
- missingRecommended: [],
52
- rulesConflicts: [],
53
- customRulesFromProject: [],
54
- maturityScore: 0.5,
55
- },
56
- availableMcps: [
57
- { name: 'sdd-mcp', command: 'npx sdd-mcp', tools: ['sdd_init_project', 'sdd_create_spec'], relevantFor: ['sdd'] },
58
- ],
59
- docsRegistry: {},
60
- qualityProfile: {
61
- enabledCategories: ['solid'],
62
- customRules: [],
63
- principles: ['SOLID', 'DRY'],
64
- strictness: 'standard',
65
- },
66
- lastAnalyzed: '2025-01-01T00:00:00Z',
67
- ...overrides,
68
- };
69
- }
70
- function makePlatformsConfig() {
71
- return {
72
- platforms: {},
73
- subAgentFrameworks: {
74
- 'claude-agent-sdk': {
75
- name: 'Anthropic Agent SDK',
76
- languages: ['typescript', 'python'],
77
- docsUrl: 'https://docs.anthropic.com/en/docs/agents',
78
- supportsHandoffs: true,
79
- supportsMcp: true,
80
- },
81
- crewai: {
82
- name: 'CrewAI',
83
- languages: ['python'],
84
- docsUrl: 'https://docs.crewai.com',
85
- supportsHandoffs: true,
86
- supportsMcp: false,
87
- },
88
- langgraph: {
89
- name: 'LangGraph',
90
- languages: ['python'],
91
- docsUrl: 'https://langchain-ai.github.io/langgraph',
92
- supportsHandoffs: false,
93
- supportsMcp: true,
94
- },
95
- autogen: {
96
- name: 'AutoGen',
97
- languages: ['python'],
98
- docsUrl: 'https://microsoft.github.io/autogen',
99
- supportsHandoffs: false,
100
- supportsMcp: false,
101
- },
102
- mastra: {
103
- name: 'Mastra',
104
- languages: ['typescript'],
105
- docsUrl: 'https://mastra.ai/docs',
106
- supportsHandoffs: false,
107
- supportsMcp: true,
108
- },
109
- 'vercel-ai-sdk': {
110
- name: 'Vercel AI SDK',
111
- languages: ['typescript'],
112
- docsUrl: 'https://sdk.vercel.ai/docs',
113
- supportsHandoffs: false,
114
- supportsMcp: false,
115
- },
116
- },
117
- };
118
- }
119
- // === Setup ===
120
- beforeEach(() => {
121
- vi.resetAllMocks();
122
- // Reset the internal cache by reloading — we'll configure mock per test
123
- // Clear the module-level cache by forcing the config to reload via rejecting once
124
- // Actually, the module has a `frameworksCache` variable. Since we can't directly
125
- // reset it, we need to clear it via side-effects.
126
- // The cache is set once and sticks. Let's mock readFile to always succeed.
127
- });
128
- // Helper to set up readFile to return our config
129
- function setupConfigMock() {
130
- mockedReadFile.mockResolvedValue(JSON.stringify(makePlatformsConfig()));
131
- }
132
- // === Tests ===
133
- describe('getFrameworkConfig', () => {
134
- it('returns config from loaded file', async () => {
135
- setupConfigMock();
136
- const config = await getFrameworkConfig('claude-agent-sdk');
137
- // First call loads from file — but cache may exist from previous test
138
- // The important thing is the result
139
- expect(config.name).toBe('Anthropic Agent SDK');
140
- expect(config.languages).toContain('typescript');
141
- expect(config.supportsHandoffs).toBe(true);
142
- expect(config.supportsMcp).toBe(true);
143
- });
144
- it('returns fallback for unknown framework', async () => {
145
- setupConfigMock();
146
- const config = await getFrameworkConfig('unknown-framework');
147
- expect(config.name).toBe('unknown-framework');
148
- expect(config.languages).toEqual(['typescript']);
149
- expect(config.docsUrl).toBe('');
150
- expect(config.supportsHandoffs).toBe(false);
151
- expect(config.supportsMcp).toBe(false);
152
- });
153
- it('uses cache on second call', async () => {
154
- setupConfigMock();
155
- await getFrameworkConfig('claude-agent-sdk');
156
- await getFrameworkConfig('crewai');
157
- // readFile should only have been called at most once (due to caching)
158
- // If cache was already warm from a previous test, it might be 0
159
- expect(mockedReadFile.mock.calls.length).toBeLessThanOrEqual(1);
160
- });
161
- });
162
- describe('generateSubAgent', () => {
163
- beforeEach(() => {
164
- setupConfigMock();
165
- });
166
- describe('claude-agent-sdk framework', () => {
167
- it('generates agent files for claude-agent-sdk', async () => {
168
- const knowledge = makeKnowledge();
169
- const result = await generateSubAgent('claude-agent-sdk', 'analyzer', 'code-analyst', ['code-analysis', 'architecture-review'], knowledge, ['sdd_audit_code'], 'You are a code analyzer.');
170
- expect(result.framework).toBe('claude-agent-sdk');
171
- expect(result.files).toHaveLength(1);
172
- expect(result.files[0].path).toContain('agents/analyzer.ts');
173
- expect(result.files[0].content).toContain('Anthropic');
174
- expect(result.files[0].content).toContain('analyzer');
175
- expect(result.files[0].content).toContain('code-analyst');
176
- expect(result.agentDefinition.name).toBe('analyzer');
177
- expect(result.agentDefinition.role).toBe('code-analyst');
178
- expect(result.agentDefinition.tools).toContain('sdd_audit_code');
179
- expect(result.agentDefinition.systemPrompt).toBe('You are a code analyzer.');
180
- expect(result.installInstructions).toContain('npm install @anthropic-ai/sdk');
181
- });
182
- it('uses custom system prompt when provided', async () => {
183
- const knowledge = makeKnowledge();
184
- const result = await generateSubAgent('claude-agent-sdk', 'test-agent', 'orchestrator', ['task-routing'], knowledge, [], 'Custom prompt here.');
185
- expect(result.agentDefinition.systemPrompt).toBe('Custom prompt here.');
186
- });
187
- it('builds system prompt when not provided', async () => {
188
- const knowledge = makeKnowledge();
189
- const result = await generateSubAgent('claude-agent-sdk', 'test-agent', 'code-analyst', ['code-analysis'], knowledge);
190
- expect(result.agentDefinition.systemPrompt).toContain('test-agent');
191
- expect(result.agentDefinition.systemPrompt).toContain('code-analyst');
192
- expect(result.agentDefinition.systemPrompt).toContain('code-analysis');
193
- expect(result.agentDefinition.systemPrompt).toContain('typescript');
194
- expect(result.agentDefinition.systemPrompt).toContain('express');
195
- expect(result.agentDefinition.systemPrompt).toContain('layered');
196
- expect(result.agentDefinition.systemPrompt).toContain('postgresql');
197
- expect(result.agentDefinition.systemPrompt).toContain('SOLID');
198
- });
199
- it('omits framework in prompt when null', async () => {
200
- const knowledge = makeKnowledge({ framework: null });
201
- const result = await generateSubAgent('claude-agent-sdk', 'test-agent', 'code-analyst', ['code-analysis'], knowledge);
202
- expect(result.agentDefinition.systemPrompt).not.toContain('Framework:');
203
- });
204
- it('omits quality principles when empty', async () => {
205
- const knowledge = makeKnowledge({
206
- qualityProfile: {
207
- enabledCategories: [],
208
- customRules: [],
209
- principles: [],
210
- strictness: 'standard',
211
- },
212
- });
213
- const result = await generateSubAgent('claude-agent-sdk', 'test-agent', 'code-analyst', ['code-analysis'], knowledge);
214
- expect(result.agentDefinition.systemPrompt).not.toContain('Quality Principles');
215
- });
216
- it('infers tools when none provided', async () => {
217
- const knowledge = makeKnowledge();
218
- const result = await generateSubAgent('claude-agent-sdk', 'test-agent', 'code-analyst', ['code-analysis'], knowledge);
219
- expect(result.agentDefinition.tools).toContain('read_file');
220
- expect(result.agentDefinition.tools).toContain('write_file');
221
- expect(result.agentDefinition.tools).toContain('list_directory');
222
- expect(result.agentDefinition.tools).toContain('sdd_audit_code');
223
- // MCP tools
224
- expect(result.agentDefinition.tools).toContain('sdd_init_project');
225
- expect(result.agentDefinition.tools).toContain('sdd_create_spec');
226
- });
227
- it('deduplicates inferred tools', async () => {
228
- const knowledge = makeKnowledge();
229
- const result = await generateSubAgent('claude-agent-sdk', 'test-agent', 'code-analyst', ['code-analysis'], knowledge);
230
- const unique = new Set(result.agentDefinition.tools);
231
- expect(unique.size).toBe(result.agentDefinition.tools.length);
232
- });
233
- });
234
- describe('crewai framework', () => {
235
- it('generates CrewAI files', async () => {
236
- const knowledge = makeKnowledge();
237
- const result = await generateSubAgent('crewai', 'analyst', 'code-analyst', ['code-analysis'], knowledge, ['sdd_audit_code']);
238
- expect(result.framework).toBe('crewai');
239
- expect(result.files).toHaveLength(1);
240
- expect(result.files[0].path).toContain('agents/analyst_agent.py');
241
- expect(result.files[0].content).toContain('from crewai import Agent');
242
- expect(result.files[0].content).toContain('analyst_agent');
243
- expect(result.installInstructions).toContain('pip install crewai');
244
- });
245
- it('includes allow_delegation based on handoff conditions', async () => {
246
- const knowledge = makeKnowledge();
247
- const result = await generateSubAgent('crewai', 'coordinator', 'orchestrator', ['task-routing'], knowledge);
248
- // orchestrator has handoff conditions
249
- expect(result.files[0].content).toContain('allow_delegation=True');
250
- });
251
- it('sets allow_delegation=False when no handoff conditions', async () => {
252
- const knowledge = makeKnowledge();
253
- const result = await generateSubAgent('crewai', 'worker', 'documentation', ['doc-writing'], knowledge);
254
- // documentation role has handoffConditions=[] which is truthy in JS
255
- expect(result.files[0].content).toContain('allow_delegation=True');
256
- });
257
- });
258
- describe('langgraph framework', () => {
259
- it('generates LangGraph files', async () => {
260
- const knowledge = makeKnowledge();
261
- const result = await generateSubAgent('langgraph', 'processor', 'code-implementor', ['code-generation'], knowledge);
262
- expect(result.framework).toBe('langgraph');
263
- expect(result.files).toHaveLength(1);
264
- expect(result.files[0].path).toContain('agents/processor_graph.py');
265
- expect(result.files[0].content).toContain('from langgraph.graph import StateGraph');
266
- expect(result.files[0].content).toContain('ProcessorState');
267
- expect(result.files[0].content).toContain('processor_node');
268
- expect(result.installInstructions).toContain('pip install langgraph');
269
- });
270
- });
271
- describe('autogen framework', () => {
272
- it('generates AutoGen files', async () => {
273
- const knowledge = makeKnowledge();
274
- const result = await generateSubAgent('autogen', 'reviewer', 'code-reviewer', ['code-review'], knowledge);
275
- expect(result.framework).toBe('autogen');
276
- expect(result.files).toHaveLength(1);
277
- expect(result.files[0].path).toContain('agents/reviewer_autogen.py');
278
- expect(result.files[0].content).toContain('from autogen import AssistantAgent');
279
- expect(result.files[0].content).toContain('reviewer_agent');
280
- expect(result.installInstructions).toContain('pip install autogen-agentchat');
281
- });
282
- });
283
- describe('mastra framework', () => {
284
- it('generates Mastra files', async () => {
285
- const knowledge = makeKnowledge();
286
- const result = await generateSubAgent('mastra', 'test-agent', 'testing', ['test-generation'], knowledge);
287
- expect(result.framework).toBe('mastra');
288
- expect(result.files).toHaveLength(1);
289
- expect(result.files[0].path).toContain('agents/test-agent.ts');
290
- expect(result.files[0].content).toContain('from "@mastra/core"');
291
- expect(result.files[0].content).toContain('testAgentAgent');
292
- expect(result.installInstructions).toContain('npm install @mastra/core');
293
- });
294
- });
295
- describe('vercel-ai-sdk framework', () => {
296
- it('generates Vercel AI SDK files', async () => {
297
- const knowledge = makeKnowledge();
298
- const result = await generateSubAgent('vercel-ai-sdk', 'helper', 'documentation', ['doc-writing'], knowledge);
299
- expect(result.framework).toBe('vercel-ai-sdk');
300
- expect(result.files).toHaveLength(1);
301
- expect(result.files[0].path).toContain('agents/helper.ts');
302
- expect(result.files[0].content).toContain('from "ai"');
303
- expect(result.files[0].content).toContain('runHelper');
304
- expect(result.installInstructions).toContain('npm install ai @ai-sdk/anthropic');
305
- });
306
- });
307
- describe('custom/generic framework', () => {
308
- it('generates generic JSON files for custom framework', async () => {
309
- const knowledge = makeKnowledge();
310
- const result = await generateSubAgent('custom', 'my agent', 'orchestrator', ['task-routing'], knowledge);
311
- expect(result.framework).toBe('custom');
312
- expect(result.files).toHaveLength(1);
313
- expect(result.files[0].path).toContain('agents/my-agent.json');
314
- expect(result.files[0].format).toBe('json');
315
- const parsed = JSON.parse(result.files[0].content);
316
- expect(parsed.name).toBe('my agent');
317
- expect(parsed.role).toBe('orchestrator');
318
- });
319
- });
320
- describe('inferModel', () => {
321
- it('returns sonnet for complex roles', async () => {
322
- const knowledge = makeKnowledge();
323
- for (const role of ['orchestrator', 'code-analyst']) {
324
- const result = await generateSubAgent('custom', 'test', role, [], knowledge);
325
- expect(result.agentDefinition.model).toContain('claude-sonnet');
326
- }
327
- });
328
- it('returns sonnet for complex capabilities', async () => {
329
- const knowledge = makeKnowledge();
330
- const result = await generateSubAgent('custom', 'test', 'general', ['architecture-review'], knowledge);
331
- expect(result.agentDefinition.model).toContain('claude-sonnet');
332
- });
333
- it('returns sonnet for simple roles too (current implementation)', async () => {
334
- const knowledge = makeKnowledge();
335
- const result = await generateSubAgent('custom', 'test', 'simple-worker', ['simple-task'], knowledge);
336
- expect(result.agentDefinition.model).toContain('claude-sonnet');
337
- });
338
- });
339
- describe('inferMaxTurns', () => {
340
- it('returns correct turns for known roles', async () => {
341
- const knowledge = makeKnowledge();
342
- const roleTurns = [
343
- { role: 'orchestrator', expected: 20 },
344
- { role: 'code-analyst', expected: 10 },
345
- { role: 'code-implementor', expected: 30 },
346
- { role: 'code-reviewer', expected: 15 },
347
- { role: 'documentation', expected: 10 },
348
- { role: 'testing', expected: 15 },
349
- ];
350
- for (const { role, expected } of roleTurns) {
351
- const result = await generateSubAgent('custom', 'test', role, [], knowledge);
352
- expect(result.agentDefinition.maxTurns).toBe(expected);
353
- }
354
- });
355
- it('returns default 10 for unknown role', async () => {
356
- const knowledge = makeKnowledge();
357
- const result = await generateSubAgent('custom', 'test', 'unknown-role', [], knowledge);
358
- expect(result.agentDefinition.maxTurns).toBe(10);
359
- });
360
- });
361
- describe('inferHandoffConditions', () => {
362
- it('returns conditions for orchestrator', async () => {
363
- const knowledge = makeKnowledge();
364
- const result = await generateSubAgent('custom', 'test', 'orchestrator', [], knowledge);
365
- expect(result.agentDefinition.handoffConditions.length).toBe(3);
366
- });
367
- it('returns conditions for code-analyst', async () => {
368
- const knowledge = makeKnowledge();
369
- const result = await generateSubAgent('custom', 'test', 'code-analyst', [], knowledge);
370
- expect(result.agentDefinition.handoffConditions.length).toBe(2);
371
- });
372
- it('returns conditions for code-implementor', async () => {
373
- const knowledge = makeKnowledge();
374
- const result = await generateSubAgent('custom', 'test', 'code-implementor', [], knowledge);
375
- expect(result.agentDefinition.handoffConditions.length).toBe(2);
376
- });
377
- it('returns conditions for code-reviewer', async () => {
378
- const knowledge = makeKnowledge();
379
- const result = await generateSubAgent('custom', 'test', 'code-reviewer', [], knowledge);
380
- expect(result.agentDefinition.handoffConditions.length).toBe(2);
381
- });
382
- it('returns empty array for unknown role', async () => {
383
- const knowledge = makeKnowledge();
384
- const result = await generateSubAgent('custom', 'test', 'unknown', [], knowledge);
385
- expect(result.agentDefinition.handoffConditions).toEqual([]);
386
- });
387
- });
388
- describe('inferTools for different roles', () => {
389
- it('includes orchestrator tools', async () => {
390
- const knowledge = makeKnowledge({ availableMcps: [] });
391
- const result = await generateSubAgent('custom', 'test', 'orchestrator', [], knowledge);
392
- expect(result.agentDefinition.tools).toContain('sdd_init_project');
393
- expect(result.agentDefinition.tools).toContain('sdd_orchestrate');
394
- });
395
- it('includes code-implementor tools', async () => {
396
- const knowledge = makeKnowledge({ availableMcps: [] });
397
- const result = await generateSubAgent('custom', 'test', 'code-implementor', [], knowledge);
398
- expect(result.agentDefinition.tools).toContain('bash');
399
- expect(result.agentDefinition.tools).toContain('sdd_validate_spec');
400
- });
401
- it('includes documentation tools', async () => {
402
- const knowledge = makeKnowledge({ availableMcps: [] });
403
- const result = await generateSubAgent('custom', 'test', 'documentation', [], knowledge);
404
- expect(result.agentDefinition.tools).toContain('sdd_generate_docs');
405
- });
406
- it('includes testing tools', async () => {
407
- const knowledge = makeKnowledge({ availableMcps: [] });
408
- const result = await generateSubAgent('custom', 'test', 'testing', [], knowledge);
409
- expect(result.agentDefinition.tools).toContain('sdd_generate_test_plan');
410
- expect(result.agentDefinition.tools).toContain('bash');
411
- });
412
- it('includes code-reviewer tools', async () => {
413
- const knowledge = makeKnowledge({ availableMcps: [] });
414
- const result = await generateSubAgent('custom', 'test', 'code-reviewer', [], knowledge);
415
- expect(result.agentDefinition.tools).toContain('sdd_audit_code');
416
- expect(result.agentDefinition.tools).toContain('sdd_detect_drift');
417
- });
418
- it('returns only base tools for unknown role', async () => {
419
- const knowledge = makeKnowledge({ availableMcps: [] });
420
- const result = await generateSubAgent('custom', 'test', 'unknown-role', [], knowledge);
421
- expect(result.agentDefinition.tools).toEqual(['read_file', 'write_file', 'list_directory']);
422
- });
423
- });
424
- describe('install instructions', () => {
425
- it('includes handoff message when framework supports it', async () => {
426
- const knowledge = makeKnowledge();
427
- const result = await generateSubAgent('claude-agent-sdk', 'test', 'orchestrator', [], knowledge);
428
- expect(result.installInstructions).toContain('handoffs');
429
- });
430
- it('includes MCP message when framework supports it', async () => {
431
- const knowledge = makeKnowledge();
432
- const result = await generateSubAgent('claude-agent-sdk', 'test', 'orchestrator', [], knowledge);
433
- expect(result.installInstructions).toContain('MCP tools');
434
- });
435
- it('omits handoff message when not supported', async () => {
436
- const knowledge = makeKnowledge();
437
- const result = await generateSubAgent('autogen', 'test', 'orchestrator', [], knowledge);
438
- expect(result.installInstructions).not.toContain('handoffs');
439
- });
440
- it('omits MCP message when not supported', async () => {
441
- const knowledge = makeKnowledge();
442
- const result = await generateSubAgent('autogen', 'test', 'orchestrator', [], knowledge);
443
- expect(result.installInstructions).not.toContain('MCP tools');
444
- });
445
- it('includes generated files list', async () => {
446
- const knowledge = makeKnowledge();
447
- const result = await generateSubAgent('claude-agent-sdk', 'test', 'orchestrator', [], knowledge);
448
- expect(result.installInstructions).toContain('Generated files:');
449
- expect(result.installInstructions).toContain('agents/test.ts');
450
- });
451
- it('omits install command for custom framework', async () => {
452
- const knowledge = makeKnowledge();
453
- const result = await generateSubAgent('custom', 'test', 'orchestrator', [], knowledge);
454
- // No specific install command for custom
455
- expect(result.installInstructions).not.toContain('npm install');
456
- expect(result.installInstructions).not.toContain('pip install');
457
- });
458
- });
459
- });
460
- describe('generateAgentSystem', () => {
461
- beforeEach(() => {
462
- setupConfigMock();
463
- });
464
- it('generates a full multi-agent system with 4 agents', async () => {
465
- const knowledge = makeKnowledge();
466
- const agents = await generateAgentSystem('claude-agent-sdk', knowledge);
467
- expect(agents).toHaveLength(4);
468
- expect(agents[0].agentDefinition.name).toBe('coordinator');
469
- expect(agents[0].agentDefinition.role).toBe('orchestrator');
470
- expect(agents[1].agentDefinition.name).toBe('analyst');
471
- expect(agents[1].agentDefinition.role).toBe('code-analyst');
472
- expect(agents[2].agentDefinition.name).toBe('implementor');
473
- expect(agents[2].agentDefinition.role).toBe('code-implementor');
474
- expect(agents[3].agentDefinition.name).toBe('reviewer');
475
- expect(agents[3].agentDefinition.role).toBe('code-reviewer');
476
- });
477
- it('coordinator has a custom system prompt', async () => {
478
- const knowledge = makeKnowledge();
479
- const agents = await generateAgentSystem('claude-agent-sdk', knowledge);
480
- const coordinator = agents[0];
481
- expect(coordinator.agentDefinition.systemPrompt).toContain('coordinator agent');
482
- expect(coordinator.agentDefinition.systemPrompt).toContain('SDD workflow');
483
- expect(coordinator.agentDefinition.systemPrompt).toContain('typescript');
484
- expect(coordinator.agentDefinition.systemPrompt).toContain('express');
485
- expect(coordinator.agentDefinition.systemPrompt).toContain('layered');
486
- expect(coordinator.agentDefinition.systemPrompt).toContain('docs/sdd/specs');
487
- });
488
- it('coordinator prompt handles null framework', async () => {
489
- const knowledge = makeKnowledge({ framework: null });
490
- const agents = await generateAgentSystem('claude-agent-sdk', knowledge);
491
- expect(agents[0].agentDefinition.systemPrompt).toContain('Framework: none');
492
- });
493
- it('each agent has specific tools', async () => {
494
- const knowledge = makeKnowledge();
495
- const agents = await generateAgentSystem('claude-agent-sdk', knowledge);
496
- expect(agents[0].agentDefinition.tools).toContain('sdd_orchestrate');
497
- expect(agents[1].agentDefinition.tools).toContain('sdd_audit_code');
498
- expect(agents[2].agentDefinition.tools).toContain('sdd_validate_spec');
499
- expect(agents[3].agentDefinition.tools).toContain('sdd_generate_checklist');
500
- });
501
- it('works with crewai framework', async () => {
502
- const knowledge = makeKnowledge();
503
- const agents = await generateAgentSystem('crewai', knowledge);
504
- expect(agents).toHaveLength(4);
505
- for (const agent of agents) {
506
- expect(agent.framework).toBe('crewai');
507
- expect(agent.files[0].path).toContain('.py');
508
- }
509
- });
510
- });
511
- describe('loadFrameworks error handling', () => {
512
- it('returns fallback config when file read fails', async () => {
513
- // We need to force a fresh load. Since the cache is module-level,
514
- // and we already loaded successfully, the cache is already warm.
515
- // This test verifies the getFrameworkConfig fallback for unknown frameworks.
516
- const config = await getFrameworkConfig('totally-unknown');
517
- expect(config.name).toBe('totally-unknown');
518
- expect(config.languages).toEqual(['typescript']);
519
- });
520
- });
521
- describe('escapeTemplateLiteral', () => {
522
- it('escapes backticks and template expressions in generated code', async () => {
523
- setupConfigMock();
524
- const knowledge = makeKnowledge();
525
- const result = await generateSubAgent('claude-agent-sdk', 'test-agent', 'code-analyst', ['code-analysis'], knowledge, [], 'Use `code` and ${expression} in prompt');
526
- expect(result.files[0].content).toContain('\\`code\\`');
527
- expect(result.files[0].content).toContain('\\${expression}');
528
- });
529
- });
530
- describe('name transformations', () => {
531
- it('converts agent name to camelCase', async () => {
532
- setupConfigMock();
533
- const knowledge = makeKnowledge();
534
- const result = await generateSubAgent('claude-agent-sdk', 'my-cool-agent', 'orchestrator', [], knowledge);
535
- expect(result.files[0].content).toContain('myCoolAgentAgent');
536
- });
537
- it('converts agent name to PascalCase in LangGraph', async () => {
538
- setupConfigMock();
539
- const knowledge = makeKnowledge();
540
- const result = await generateSubAgent('langgraph', 'my-cool-agent', 'orchestrator', [], knowledge);
541
- expect(result.files[0].content).toContain('MyCoolAgentState');
542
- });
543
- it('converts spaces to underscores for Python frameworks', async () => {
544
- setupConfigMock();
545
- const knowledge = makeKnowledge();
546
- const result = await generateSubAgent('crewai', 'my cool agent', 'orchestrator', [], knowledge);
547
- expect(result.files[0].path).toContain('my_cool_agent');
548
- });
549
- it('converts spaces to hyphens for TS frameworks', async () => {
550
- setupConfigMock();
551
- const knowledge = makeKnowledge();
552
- const result = await generateSubAgent('mastra', 'my cool agent', 'orchestrator', [], knowledge);
553
- expect(result.files[0].path).toContain('my-cool-agent');
554
- });
555
- });
556
- //# sourceMappingURL=agent-generator.test.js.map