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.
- package/README.md +31 -1
- package/dist/config/version.d.ts +2 -0
- package/dist/config/version.d.ts.map +1 -0
- package/dist/config/version.js +9 -0
- package/dist/config/version.js.map +1 -0
- package/dist/config/version.ts +11 -0
- package/dist/engine/detectors/library-detector-langs2.d.ts.map +1 -1
- package/dist/engine/detectors/library-detector-langs2.js.map +1 -1
- package/dist/engine/mermaid-generator.d.ts.map +1 -1
- package/dist/engine/mermaid-generator.js +12 -2
- package/dist/engine/mermaid-generator.js.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/tools/create-spec-hu/hu-body-generators.d.ts.map +1 -1
- package/dist/tools/create-spec-hu/hu-body-generators.js +7 -1
- package/dist/tools/create-spec-hu/hu-body-generators.js.map +1 -1
- package/dist/tools/create-spec-tech/ficha-content.d.ts.map +1 -1
- package/dist/tools/create-spec-tech/ficha-content.js +4 -2
- package/dist/tools/create-spec-tech/ficha-content.js.map +1 -1
- package/dist/tools/create-spec.d.ts.map +1 -1
- package/dist/tools/create-spec.js +4 -3
- package/dist/tools/create-spec.js.map +1 -1
- package/dist/tools/register-spec-tools/core-spec-tools.d.ts.map +1 -1
- package/dist/tools/register-spec-tools/core-spec-tools.js +4 -0
- package/dist/tools/register-spec-tools/core-spec-tools.js.map +1 -1
- package/dist/types/spec/core.d.ts +1 -0
- package/dist/types/spec/core.d.ts.map +1 -1
- package/dist/types/spec/inputs.d.ts +1 -0
- package/dist/types/spec/inputs.d.ts.map +1 -1
- package/package.json +27 -18
- package/src/config/version.ts +11 -0
- package/dist/engine/agent-generator.test.d.ts +0 -2
- package/dist/engine/agent-generator.test.d.ts.map +0 -1
- package/dist/engine/agent-generator.test.js +0 -556
- package/dist/engine/agent-generator.test.js.map +0 -1
- package/dist/engine/analyzer.test.d.ts +0 -2
- package/dist/engine/analyzer.test.d.ts.map +0 -1
- package/dist/engine/analyzer.test.js +0 -1461
- package/dist/engine/analyzer.test.js.map +0 -1
- package/dist/engine/auditor.test.d.ts +0 -2
- package/dist/engine/auditor.test.d.ts.map +0 -1
- package/dist/engine/auditor.test.js +0 -2075
- package/dist/engine/auditor.test.js.map +0 -1
- package/dist/engine/doc-generator.test.d.ts +0 -2
- package/dist/engine/doc-generator.test.d.ts.map +0 -1
- package/dist/engine/doc-generator.test.js +0 -961
- package/dist/engine/doc-generator.test.js.map +0 -1
- package/dist/engine/estimator.test.d.ts +0 -2
- package/dist/engine/estimator.test.d.ts.map +0 -1
- package/dist/engine/estimator.test.js +0 -334
- package/dist/engine/estimator.test.js.map +0 -1
- package/dist/engine/skill-generator.test.d.ts +0 -2
- package/dist/engine/skill-generator.test.d.ts.map +0 -1
- package/dist/engine/skill-generator.test.js +0 -742
- package/dist/engine/skill-generator.test.js.map +0 -1
- package/dist/engine/validator.test.d.ts +0 -2
- package/dist/engine/validator.test.d.ts.map +0 -1
- package/dist/engine/validator.test.js +0 -2371
- package/dist/engine/validator.test.js.map +0 -1
- package/dist/engine/web-fetcher.test.d.ts +0 -2
- package/dist/engine/web-fetcher.test.d.ts.map +0 -1
- package/dist/engine/web-fetcher.test.js +0 -360
- package/dist/engine/web-fetcher.test.js.map +0 -1
- package/dist/i18n/index.test.d.ts +0 -2
- package/dist/i18n/index.test.d.ts.map +0 -1
- package/dist/i18n/index.test.js +0 -375
- package/dist/i18n/index.test.js.map +0 -1
- package/dist/index.test.d.ts +0 -2
- package/dist/index.test.d.ts.map +0 -1
- package/dist/index.test.js +0 -124
- package/dist/index.test.js.map +0 -1
- package/dist/resources/patterns.test.d.ts +0 -2
- package/dist/resources/patterns.test.d.ts.map +0 -1
- package/dist/resources/patterns.test.js +0 -142
- package/dist/resources/patterns.test.js.map +0 -1
- package/dist/resources/process.test.d.ts +0 -2
- package/dist/resources/process.test.d.ts.map +0 -1
- package/dist/resources/process.test.js +0 -48
- package/dist/resources/process.test.js.map +0 -1
- package/dist/resources/registry.test.d.ts +0 -2
- package/dist/resources/registry.test.d.ts.map +0 -1
- package/dist/resources/registry.test.js +0 -138
- package/dist/resources/registry.test.js.map +0 -1
- package/dist/resources/specs.test.d.ts +0 -2
- package/dist/resources/specs.test.d.ts.map +0 -1
- package/dist/resources/specs.test.js +0 -130
- package/dist/resources/specs.test.js.map +0 -1
- package/dist/resources/templates.test.d.ts +0 -2
- package/dist/resources/templates.test.d.ts.map +0 -1
- package/dist/resources/templates.test.js +0 -119
- package/dist/resources/templates.test.js.map +0 -1
- package/dist/smoke.test.d.ts +0 -2
- package/dist/smoke.test.d.ts.map +0 -1
- package/dist/smoke.test.js +0 -229
- package/dist/smoke.test.js.map +0 -1
- package/dist/storage/base-store.test.d.ts +0 -2
- package/dist/storage/base-store.test.d.ts.map +0 -1
- package/dist/storage/base-store.test.js +0 -180
- package/dist/storage/base-store.test.js.map +0 -1
- package/dist/storage/global-store.test.d.ts +0 -2
- package/dist/storage/global-store.test.d.ts.map +0 -1
- package/dist/storage/global-store.test.js +0 -327
- package/dist/storage/global-store.test.js.map +0 -1
- package/dist/storage/index.test.d.ts +0 -2
- package/dist/storage/index.test.d.ts.map +0 -1
- package/dist/storage/index.test.js +0 -56
- package/dist/storage/index.test.js.map +0 -1
- package/dist/storage/knowledge-store.test.d.ts +0 -2
- package/dist/storage/knowledge-store.test.d.ts.map +0 -1
- package/dist/storage/knowledge-store.test.js +0 -368
- package/dist/storage/knowledge-store.test.js.map +0 -1
- package/dist/storage/metrics-store.test.d.ts +0 -2
- package/dist/storage/metrics-store.test.d.ts.map +0 -1
- package/dist/storage/metrics-store.test.js +0 -212
- package/dist/storage/metrics-store.test.js.map +0 -1
- package/dist/storage/pattern-store.test.d.ts +0 -2
- package/dist/storage/pattern-store.test.d.ts.map +0 -1
- package/dist/storage/pattern-store.test.js +0 -224
- package/dist/storage/pattern-store.test.js.map +0 -1
- package/dist/storage/spec-store.test.d.ts +0 -2
- package/dist/storage/spec-store.test.d.ts.map +0 -1
- package/dist/storage/spec-store.test.js +0 -227
- package/dist/storage/spec-store.test.js.map +0 -1
- package/dist/tools/audit.test.d.ts +0 -2
- package/dist/tools/audit.test.d.ts.map +0 -1
- package/dist/tools/audit.test.js +0 -169
- package/dist/tools/audit.test.js.map +0 -1
- package/dist/tools/challenge-spec.test.d.ts +0 -2
- package/dist/tools/challenge-spec.test.d.ts.map +0 -1
- package/dist/tools/challenge-spec.test.js +0 -782
- package/dist/tools/challenge-spec.test.js.map +0 -1
- package/dist/tools/check-versions.test.d.ts +0 -2
- package/dist/tools/check-versions.test.d.ts.map +0 -1
- package/dist/tools/check-versions.test.js +0 -214
- package/dist/tools/check-versions.test.js.map +0 -1
- package/dist/tools/clarify-requirements.test.d.ts +0 -2
- package/dist/tools/clarify-requirements.test.d.ts.map +0 -1
- package/dist/tools/clarify-requirements.test.js +0 -161
- package/dist/tools/clarify-requirements.test.js.map +0 -1
- package/dist/tools/consult-docs.test.d.ts +0 -2
- package/dist/tools/consult-docs.test.d.ts.map +0 -1
- package/dist/tools/consult-docs.test.js +0 -140
- package/dist/tools/consult-docs.test.js.map +0 -1
- package/dist/tools/create-spec.test.d.ts +0 -2
- package/dist/tools/create-spec.test.d.ts.map +0 -1
- package/dist/tools/create-spec.test.js +0 -233
- package/dist/tools/create-spec.test.js.map +0 -1
- package/dist/tools/define-ui-contract.test.d.ts +0 -2
- package/dist/tools/define-ui-contract.test.d.ts.map +0 -1
- package/dist/tools/define-ui-contract.test.js +0 -479
- package/dist/tools/define-ui-contract.test.js.map +0 -1
- package/dist/tools/design-schema.test.d.ts +0 -2
- package/dist/tools/design-schema.test.d.ts.map +0 -1
- package/dist/tools/design-schema.test.js +0 -301
- package/dist/tools/design-schema.test.js.map +0 -1
- package/dist/tools/detect-agent.test.d.ts +0 -2
- package/dist/tools/detect-agent.test.d.ts.map +0 -1
- package/dist/tools/detect-agent.test.js +0 -133
- package/dist/tools/detect-agent.test.js.map +0 -1
- package/dist/tools/detect-drift.test.d.ts +0 -2
- package/dist/tools/detect-drift.test.d.ts.map +0 -1
- package/dist/tools/detect-drift.test.js +0 -312
- package/dist/tools/detect-drift.test.js.map +0 -1
- package/dist/tools/discover-mcps.test.d.ts +0 -2
- package/dist/tools/discover-mcps.test.d.ts.map +0 -1
- package/dist/tools/discover-mcps.test.js +0 -345
- package/dist/tools/discover-mcps.test.js.map +0 -1
- package/dist/tools/estimate.test.d.ts +0 -2
- package/dist/tools/estimate.test.d.ts.map +0 -1
- package/dist/tools/estimate.test.js +0 -137
- package/dist/tools/estimate.test.js.map +0 -1
- package/dist/tools/generate-adr.test.d.ts +0 -2
- package/dist/tools/generate-adr.test.d.ts.map +0 -1
- package/dist/tools/generate-adr.test.js +0 -206
- package/dist/tools/generate-adr.test.js.map +0 -1
- package/dist/tools/generate-checklist.test.d.ts +0 -2
- package/dist/tools/generate-checklist.test.d.ts.map +0 -1
- package/dist/tools/generate-checklist.test.js +0 -201
- package/dist/tools/generate-checklist.test.js.map +0 -1
- package/dist/tools/generate-docs.test.d.ts +0 -2
- package/dist/tools/generate-docs.test.d.ts.map +0 -1
- package/dist/tools/generate-docs.test.js +0 -183
- package/dist/tools/generate-docs.test.js.map +0 -1
- package/dist/tools/generate-execution-plan.test.d.ts +0 -2
- package/dist/tools/generate-execution-plan.test.d.ts.map +0 -1
- package/dist/tools/generate-execution-plan.test.js +0 -643
- package/dist/tools/generate-execution-plan.test.js.map +0 -1
- package/dist/tools/generate-rules.test.d.ts +0 -2
- package/dist/tools/generate-rules.test.d.ts.map +0 -1
- package/dist/tools/generate-rules.test.js +0 -148
- package/dist/tools/generate-rules.test.js.map +0 -1
- package/dist/tools/generate-skill.test.d.ts +0 -2
- package/dist/tools/generate-skill.test.d.ts.map +0 -1
- package/dist/tools/generate-skill.test.js +0 -138
- package/dist/tools/generate-skill.test.js.map +0 -1
- package/dist/tools/generate-sub-agent.test.d.ts +0 -2
- package/dist/tools/generate-sub-agent.test.d.ts.map +0 -1
- package/dist/tools/generate-sub-agent.test.js +0 -162
- package/dist/tools/generate-sub-agent.test.js.map +0 -1
- package/dist/tools/generate-tests.test.d.ts +0 -2
- package/dist/tools/generate-tests.test.d.ts.map +0 -1
- package/dist/tools/generate-tests.test.js +0 -222
- package/dist/tools/generate-tests.test.js.map +0 -1
- package/dist/tools/init-constitution.test.d.ts +0 -2
- package/dist/tools/init-constitution.test.d.ts.map +0 -1
- package/dist/tools/init-constitution.test.js +0 -398
- package/dist/tools/init-constitution.test.js.map +0 -1
- package/dist/tools/init-project.test.d.ts +0 -2
- package/dist/tools/init-project.test.d.ts.map +0 -1
- package/dist/tools/init-project.test.js +0 -158
- package/dist/tools/init-project.test.js.map +0 -1
- package/dist/tools/integrate-pm.test.d.ts +0 -2
- package/dist/tools/integrate-pm.test.d.ts.map +0 -1
- package/dist/tools/integrate-pm.test.js +0 -558
- package/dist/tools/integrate-pm.test.js.map +0 -1
- package/dist/tools/learn.test.d.ts +0 -2
- package/dist/tools/learn.test.d.ts.map +0 -1
- package/dist/tools/learn.test.js +0 -123
- package/dist/tools/learn.test.js.map +0 -1
- package/dist/tools/list-specs.test.d.ts +0 -2
- package/dist/tools/list-specs.test.d.ts.map +0 -1
- package/dist/tools/list-specs.test.js +0 -110
- package/dist/tools/list-specs.test.js.map +0 -1
- package/dist/tools/manage-context.test.d.ts +0 -2
- package/dist/tools/manage-context.test.d.ts.map +0 -1
- package/dist/tools/manage-context.test.js +0 -359
- package/dist/tools/manage-context.test.js.map +0 -1
- package/dist/tools/manage-git.test.d.ts +0 -2
- package/dist/tools/manage-git.test.d.ts.map +0 -1
- package/dist/tools/manage-git.test.js +0 -882
- package/dist/tools/manage-git.test.js.map +0 -1
- package/dist/tools/orchestrate.test.d.ts +0 -2
- package/dist/tools/orchestrate.test.d.ts.map +0 -1
- package/dist/tools/orchestrate.test.js +0 -1117
- package/dist/tools/orchestrate.test.js.map +0 -1
- package/dist/tools/reconcile-spec.test.d.ts +0 -2
- package/dist/tools/reconcile-spec.test.d.ts.map +0 -1
- package/dist/tools/reconcile-spec.test.js +0 -259
- package/dist/tools/reconcile-spec.test.js.map +0 -1
- package/dist/tools/register-platform-tools.test.d.ts +0 -2
- package/dist/tools/register-platform-tools.test.d.ts.map +0 -1
- package/dist/tools/register-platform-tools.test.js +0 -404
- package/dist/tools/register-platform-tools.test.js.map +0 -1
- package/dist/tools/register-spec-tools.test.d.ts +0 -2
- package/dist/tools/register-spec-tools.test.d.ts.map +0 -1
- package/dist/tools/register-spec-tools.test.js +0 -407
- package/dist/tools/register-spec-tools.test.js.map +0 -1
- package/dist/tools/reverse-engineer.test.d.ts +0 -2
- package/dist/tools/reverse-engineer.test.d.ts.map +0 -1
- package/dist/tools/reverse-engineer.test.js +0 -206
- package/dist/tools/reverse-engineer.test.js.map +0 -1
- package/dist/tools/schemas.d.ts +0 -20
- package/dist/tools/schemas.d.ts.map +0 -1
- package/dist/tools/schemas.js +0 -133
- package/dist/tools/schemas.js.map +0 -1
- package/dist/tools/schemas.test.d.ts +0 -2
- package/dist/tools/schemas.test.d.ts.map +0 -1
- package/dist/tools/schemas.test.js +0 -245
- package/dist/tools/schemas.test.js.map +0 -1
- package/dist/tools/set-locale.test.d.ts +0 -2
- package/dist/tools/set-locale.test.d.ts.map +0 -1
- package/dist/tools/set-locale.test.js +0 -74
- package/dist/tools/set-locale.test.js.map +0 -1
- package/dist/tools/suggest-mcps.test.d.ts +0 -2
- package/dist/tools/suggest-mcps.test.d.ts.map +0 -1
- package/dist/tools/suggest-mcps.test.js +0 -198
- package/dist/tools/suggest-mcps.test.js.map +0 -1
- package/dist/tools/suggest-stack.test.d.ts +0 -2
- package/dist/tools/suggest-stack.test.d.ts.map +0 -1
- package/dist/tools/suggest-stack.test.js +0 -181
- package/dist/tools/suggest-stack.test.js.map +0 -1
- package/dist/tools/suggest-tooling.test.d.ts +0 -2
- package/dist/tools/suggest-tooling.test.d.ts.map +0 -1
- package/dist/tools/suggest-tooling.test.js +0 -213
- package/dist/tools/suggest-tooling.test.js.map +0 -1
- package/dist/tools/summarize-spec.test.d.ts +0 -2
- package/dist/tools/summarize-spec.test.d.ts.map +0 -1
- package/dist/tools/summarize-spec.test.js +0 -180
- package/dist/tools/summarize-spec.test.js.map +0 -1
- package/dist/tools/update-status.test.d.ts +0 -2
- package/dist/tools/update-status.test.d.ts.map +0 -1
- package/dist/tools/update-status.test.js +0 -142
- package/dist/tools/update-status.test.js.map +0 -1
- package/dist/tools/validate.test.d.ts +0 -2
- package/dist/tools/validate.test.d.ts.map +0 -1
- package/dist/tools/validate.test.js +0 -137
- 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.
|
|
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 +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
|