@polymorphism-tech/morph-spec 4.8.19 → 4.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +21 -0
- package/README.md +2 -2
- package/bin/morph-spec.js +44 -55
- package/bin/task-manager.js +133 -20
- package/bin/validate.js +67 -33
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +201 -203
- package/docs/QUICKSTART.md +2 -2
- package/framework/CLAUDE.md +99 -77
- package/framework/agents.json +734 -182
- package/framework/commands/commit.md +166 -0
- package/framework/commands/morph-apply.md +13 -2
- package/framework/commands/morph-archive.md +8 -2
- package/framework/commands/morph-infra.md +6 -0
- package/framework/commands/morph-preflight.md +6 -0
- package/framework/commands/morph-proposal.md +56 -7
- package/framework/commands/morph-status.md +6 -0
- package/framework/commands/morph-troubleshoot.md +6 -0
- package/framework/hooks/claude-code/notification/approval-reminder.js +3 -2
- package/framework/hooks/claude-code/post-tool-use/context-refresh.js +1 -1
- package/framework/hooks/claude-code/post-tool-use/dispatch.js +155 -32
- package/framework/hooks/claude-code/post-tool-use/skill-reminder.js +78 -0
- package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +8 -17
- package/framework/hooks/claude-code/pre-compact/save-morph-context.js +16 -3
- package/framework/hooks/claude-code/pre-tool-use/enforce-phase-writes.js +4 -3
- package/framework/hooks/claude-code/pre-tool-use/protect-spec-files.js +4 -3
- package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +60 -0
- package/framework/hooks/claude-code/session-start/inject-morph-context.js +124 -2
- package/framework/hooks/claude-code/session-start/post-compact-restore.js +41 -0
- package/framework/hooks/claude-code/statusline.py +76 -30
- package/framework/hooks/claude-code/stop/validate-completion.js +2 -15
- package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +23 -5
- package/framework/hooks/claude-code/user-prompt/set-terminal-title.js +14 -6
- package/framework/hooks/shared/activity-logger.js +0 -24
- package/framework/hooks/shared/compact-restore.js +100 -0
- package/framework/hooks/shared/dispatch-helpers.js +116 -0
- package/framework/hooks/shared/phase-utils.js +12 -5
- package/framework/hooks/shared/skill-reminder-helpers.js +79 -0
- package/framework/hooks/shared/stale-task-reset.js +57 -0
- package/framework/hooks/shared/state-reader.js +29 -5
- package/framework/hooks/shared/worktree-helpers.js +53 -0
- package/framework/phases.json +69 -14
- package/framework/rules/morph-workflow.md +88 -86
- package/framework/skills/level-0-meta/mcp-registry.json +86 -51
- package/framework/skills/level-0-meta/{brainstorming → morph-brainstorming}/SKILL.md +14 -17
- package/framework/skills/level-0-meta/morph-checklist/SKILL.md +2 -2
- package/framework/skills/level-0-meta/{code-review → morph-code-review}/SKILL.md +2 -2
- package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/SKILL.md +163 -163
- package/framework/skills/level-0-meta/{frontend-review → morph-frontend-review}/SKILL.md +9 -9
- package/framework/skills/level-0-meta/morph-init/SKILL.md +77 -12
- package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/SKILL.md +62 -15
- package/framework/skills/level-0-meta/morph-replicate/SKILL.md +5 -5
- package/framework/skills/level-0-meta/morph-replicate/references/blazor-html-mapping.md +1 -1
- package/framework/skills/level-0-meta/{simulation-checklist → morph-simulation-checklist}/SKILL.md +1 -1
- package/framework/skills/level-0-meta/{terminal-title → morph-terminal-title}/SKILL.md +2 -2
- package/framework/skills/level-0-meta/{tool-usage-guide → morph-tool-usage-guide}/SKILL.md +3 -4
- package/framework/skills/level-0-meta/{tool-usage-guide → morph-tool-usage-guide}/references/tools-per-phase.md +7 -7
- package/framework/skills/level-0-meta/{verification-before-completion → morph-verification-before-completion}/SKILL.md +2 -2
- package/framework/skills/level-0-meta/{verification-before-completion → morph-verification-before-completion}/scripts/check-phase-outputs.mjs +2 -2
- package/framework/skills/level-1-workflows/morph-phase-clarify/SKILL.md +238 -0
- package/framework/skills/level-1-workflows/{phase-codebase-analysis → morph-phase-codebase-analysis}/SKILL.md +3 -3
- package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +507 -0
- package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/SKILL.md +168 -27
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/code-quality-reviewer-prompt.md +50 -0
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +45 -0
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/spec-reviewer-prompt.md +47 -0
- package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +254 -0
- package/framework/skills/level-1-workflows/{phase-setup → morph-phase-setup}/SKILL.md +50 -3
- package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/SKILL.md +48 -11
- package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/scripts/validate-tasks.mjs +3 -3
- package/framework/skills/level-1-workflows/{phase-uiux → morph-phase-uiux}/SKILL.md +46 -11
- package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +97 -0
- package/framework/standards/STANDARDS.json +640 -88
- package/framework/standards/infrastructure/vercel/vercel-database.md +106 -0
- package/framework/standards/integration/mcp/mcp-tools.md +25 -7
- package/framework/templates/REGISTRY.json +1825 -1909
- package/framework/templates/context/CONTEXT-FEATURE.md +276 -276
- package/framework/templates/docs/onboarding.md +3 -7
- package/package.json +2 -7
- package/src/commands/agents/dispatch-agents.js +104 -6
- package/src/commands/mcp/mcp-setup.js +39 -2
- package/src/commands/phase/phase-reset.js +74 -0
- package/src/commands/project/doctor.js +34 -51
- package/src/commands/project/init.js +1 -1
- package/src/commands/project/status.js +2 -2
- package/src/commands/project/update.js +381 -365
- package/src/commands/project/worktree.js +154 -0
- package/src/commands/scope/escalate.js +215 -0
- package/src/commands/state/advance-phase.js +132 -68
- package/src/commands/state/approve.js +2 -2
- package/src/commands/state/index.js +7 -8
- package/src/commands/state/phase-runner.js +1 -1
- package/src/commands/state/state.js +61 -6
- package/src/commands/task/expand.js +100 -0
- package/src/commands/tasks/task.js +78 -99
- package/src/commands/templates/template-render.js +93 -173
- package/src/commands/trust/trust.js +26 -21
- package/src/core/paths/output-schema.js +19 -3
- package/src/core/state/phase-state-machine.js +7 -4
- package/src/core/state/state-manager.js +32 -57
- package/src/core/workflows/workflow-detector.js +9 -87
- package/src/lib/detectors/claude-config-detector.js +93 -347
- package/src/lib/detectors/design-system-detector.js +189 -189
- package/src/lib/detectors/index.js +155 -57
- package/src/lib/generators/context-generator.js +2 -2
- package/src/lib/installers/mcp-installer.js +37 -5
- package/src/lib/phase-chain/phase-validator.js +336 -0
- package/src/lib/scope/impact-analyzer.js +106 -0
- package/src/lib/stack/stack-profile.js +88 -0
- package/src/lib/tasks/task-classifier.js +16 -0
- package/src/lib/tasks/task-parser.js +1 -1
- package/src/lib/tasks/test-runner.js +77 -0
- package/src/lib/trust/trust-manager.js +32 -144
- package/src/lib/validators/shared/emit-validator-dispatch.js +64 -0
- package/src/lib/validators/spec-validator.js +58 -4
- package/src/lib/validators/validation-runner.js +23 -11
- package/src/scripts/setup-infra.js +255 -224
- package/src/utils/agents-installer.js +34 -14
- package/src/utils/banner.js +1 -1
- package/src/utils/claude-settings-manager.js +1 -1
- package/src/utils/file-copier.js +1 -1
- package/src/utils/hooks-installer.js +272 -8
- package/framework/hooks/dev/check-sync-health.js +0 -117
- package/framework/hooks/dev/guard-version-numbers.js +0 -57
- package/framework/hooks/dev/sync-standards-registry.js +0 -60
- package/framework/hooks/dev/sync-template-registry.js +0 -60
- package/framework/hooks/dev/validate-skill-format.js +0 -70
- package/framework/hooks/dev/validate-standard-format.js +0 -73
- package/framework/skills/level-1-workflows/phase-clarify/SKILL.md +0 -190
- package/framework/skills/level-1-workflows/phase-design/SKILL.md +0 -366
- package/framework/templates/meta-prompts/hops/hop-retry.md +0 -78
- package/framework/templates/meta-prompts/hops/hop-validation.md +0 -97
- package/framework/templates/meta-prompts/hops/hop-wrapper.md +0 -36
- package/framework/workflows/configs/design-impl.json +0 -49
- package/framework/workflows/configs/express.json +0 -45
- package/framework/workflows/configs/fast-track.json +0 -42
- package/framework/workflows/configs/full-morph.json +0 -79
- package/framework/workflows/configs/fusion.json +0 -39
- package/framework/workflows/configs/long-running.json +0 -33
- package/framework/workflows/configs/spec-only.json +0 -43
- package/framework/workflows/configs/ui-refresh.json +0 -49
- package/framework/workflows/configs/zero-touch.json +0 -82
- package/src/commands/project/index.js +0 -8
- package/src/commands/project/monitor.js +0 -295
- package/src/commands/project/tutorial.js +0 -115
- package/src/commands/state/validate-phase.js +0 -238
- package/src/commands/templates/generate-contracts.js +0 -445
- package/src/core/index.js +0 -10
- package/src/core/orchestrator.js +0 -171
- package/src/core/registry/command-registry.js +0 -28
- package/src/core/registry/index.js +0 -8
- package/src/core/registry/validator-registry.js +0 -204
- package/src/core/state/index.js +0 -8
- package/src/core/templates/index.js +0 -9
- package/src/core/templates/template-data-sources.js +0 -325
- package/src/core/templates/template-validator.js +0 -296
- package/src/core/workflows/index.js +0 -7
- package/src/generator/config-generator.js +0 -206
- package/src/generator/templates/config.json.template +0 -40
- package/src/generator/templates/project.md.template +0 -67
- package/src/lib/agents/micro-agent-factory.js +0 -161
- package/src/lib/analysis/complexity-analyzer.js +0 -441
- package/src/lib/analysis/index.js +0 -7
- package/src/lib/analytics/analytics-engine.js +0 -345
- package/src/lib/checkpoints/checkpoint-hooks.js +0 -298
- package/src/lib/checkpoints/index.js +0 -7
- package/src/lib/context/context-bundler.js +0 -241
- package/src/lib/context/context-optimizer.js +0 -212
- package/src/lib/context/context-tracker.js +0 -273
- package/src/lib/context/core-four-tracker.js +0 -201
- package/src/lib/context/mcp-optimizer.js +0 -200
- package/src/lib/detectors/config-detector.js +0 -223
- package/src/lib/detectors/standards-generator.js +0 -335
- package/src/lib/detectors/structure-detector.js +0 -275
- package/src/lib/execution/fusion-executor.js +0 -304
- package/src/lib/execution/parallel-executor.js +0 -270
- package/src/lib/hooks/stop-hook-executor.js +0 -286
- package/src/lib/hops/hop-composer.js +0 -221
- package/src/lib/monitor/agent-resolver.js +0 -144
- package/src/lib/monitor/renderer.js +0 -230
- package/src/lib/orchestration/index.js +0 -7
- package/src/lib/orchestration/team-orchestrator.js +0 -404
- package/src/lib/phase-chain/eligibility-checker.js +0 -243
- package/src/lib/threads/thread-coordinator.js +0 -238
- package/src/lib/threads/thread-manager.js +0 -317
- package/src/lib/tracking/artifact-trail.js +0 -202
- package/src/sanitizer/context-sanitizer.js +0 -221
- package/src/sanitizer/patterns.js +0 -163
- package/src/scanner/project-scanner.js +0 -242
- package/src/ui/diff-display.js +0 -91
- package/src/ui/interactive-wizard.js +0 -96
- package/src/ui/user-review.js +0 -211
- package/src/ui/wizard-questions.js +0 -188
- package/src/utils/color-utils.js +0 -70
- package/src/utils/process-handler.js +0 -97
- package/src/writer/file-writer.js +0 -86
- /package/framework/skills/level-0-meta/{brainstorming → morph-brainstorming}/references/proposal-example.md +0 -0
- /package/framework/skills/level-0-meta/{code-review → morph-code-review}/references/review-example.md +0 -0
- /package/framework/skills/level-0-meta/{code-review → morph-code-review}/references/review-guidelines.md +0 -0
- /package/framework/skills/level-0-meta/{code-review → morph-code-review}/scripts/scan-csharp.mjs +0 -0
- /package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/references/review-example-nextjs.md +0 -0
- /package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/scripts/scan-nextjs.mjs +0 -0
- /package/framework/skills/level-0-meta/{frontend-review → morph-frontend-review}/scripts/scan-accessibility.mjs +0 -0
- /package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/scripts/detect-dev-server.mjs +0 -0
- /package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/scripts/detect-stack.mjs +0 -0
- /package/framework/skills/level-0-meta/{terminal-title → morph-terminal-title}/scripts/set_title.sh +0 -0
- /package/framework/skills/level-1-workflows/{phase-clarify → morph-phase-clarify}/references/clarifications-example.md +0 -0
- /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/architecture-analysis-guide.md +0 -0
- /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/spec-authoring-guide.md +0 -0
- /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/spec-example.md +0 -0
- /package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/references/recap-example.md +0 -0
- /package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/references/vsa-implementation-guide.md +0 -0
- /package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/references/task-planning-patterns.md +0 -0
- /package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/references/tasks-example.md +0 -0
|
@@ -1,275 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Structure Detector - Analyzes folder structure to infer stack and architecture
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { glob } from 'glob';
|
|
6
|
-
import { join } from 'path';
|
|
7
|
-
import { existsSync } from 'fs';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Detect project structure
|
|
11
|
-
* @param {string} projectPath - Project root path
|
|
12
|
-
* @returns {Promise<Object>} Structure detection results
|
|
13
|
-
*/
|
|
14
|
-
export async function detectStructure(projectPath) {
|
|
15
|
-
const result = {
|
|
16
|
-
stack: 'unknown',
|
|
17
|
-
architecture: 'unknown',
|
|
18
|
-
uiLibrary: null,
|
|
19
|
-
patterns: [],
|
|
20
|
-
folders: {
|
|
21
|
-
hasPages: false,
|
|
22
|
-
hasComponents: false,
|
|
23
|
-
hasServices: false,
|
|
24
|
-
hasRepositories: false,
|
|
25
|
-
hasAgents: false
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
// Detect stack
|
|
30
|
-
result.stack = await detectStack(projectPath);
|
|
31
|
-
|
|
32
|
-
// Detect architecture pattern
|
|
33
|
-
result.architecture = await detectArchitecture(projectPath);
|
|
34
|
-
|
|
35
|
-
// Detect UI library
|
|
36
|
-
if (result.stack === 'blazor') {
|
|
37
|
-
result.uiLibrary = await detectUILibrary(projectPath);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Detect patterns
|
|
41
|
-
result.patterns = await detectPatterns(projectPath);
|
|
42
|
-
|
|
43
|
-
// Analyze folders
|
|
44
|
-
result.folders = await analyzeFolders(projectPath);
|
|
45
|
-
|
|
46
|
-
return result;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Detect stack type
|
|
51
|
-
*/
|
|
52
|
-
async function detectStack(projectPath) {
|
|
53
|
-
// Order matters: more specific patterns first
|
|
54
|
-
const patterns = {
|
|
55
|
-
blazor: [
|
|
56
|
-
'**/*.razor',
|
|
57
|
-
'**/Pages/**/*.razor',
|
|
58
|
-
'**/Components/**/*.razor'
|
|
59
|
-
],
|
|
60
|
-
nextjs: [
|
|
61
|
-
'**/next.config.js',
|
|
62
|
-
'**/next.config.mjs',
|
|
63
|
-
'**/next.config.ts',
|
|
64
|
-
'pages/**/*.tsx',
|
|
65
|
-
'app/**/*.tsx',
|
|
66
|
-
'**/pages/**/*.tsx',
|
|
67
|
-
'**/app/**/*.tsx'
|
|
68
|
-
],
|
|
69
|
-
react: [
|
|
70
|
-
'src/App.jsx',
|
|
71
|
-
'src/App.tsx',
|
|
72
|
-
'vite.config.js',
|
|
73
|
-
'craco.config.js'
|
|
74
|
-
],
|
|
75
|
-
vue: [
|
|
76
|
-
'vite.config.js', // Vue 3 + Vite
|
|
77
|
-
'vue.config.js', // Vue 2
|
|
78
|
-
'src/App.vue'
|
|
79
|
-
],
|
|
80
|
-
dotnet: [
|
|
81
|
-
'**/*.csproj',
|
|
82
|
-
'**/Program.cs'
|
|
83
|
-
],
|
|
84
|
-
typescript: [
|
|
85
|
-
'tsconfig.json',
|
|
86
|
-
'src/**/*.ts'
|
|
87
|
-
],
|
|
88
|
-
nodejs: [
|
|
89
|
-
'package.json' // Fallback: generic Node.js
|
|
90
|
-
],
|
|
91
|
-
python: [
|
|
92
|
-
'requirements.txt',
|
|
93
|
-
'pyproject.toml',
|
|
94
|
-
'setup.py'
|
|
95
|
-
],
|
|
96
|
-
go: [
|
|
97
|
-
'go.mod',
|
|
98
|
-
'main.go'
|
|
99
|
-
]
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
const globIgnore = ['framework/**', 'test/**', 'node_modules/**', '.morph/**'];
|
|
103
|
-
|
|
104
|
-
for (const [stack, globs] of Object.entries(patterns)) {
|
|
105
|
-
for (const pattern of globs) {
|
|
106
|
-
const files = await glob(pattern, { cwd: projectPath, nodir: true, ignore: globIgnore });
|
|
107
|
-
if (files.length > 0) {
|
|
108
|
-
return stack;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return 'unknown';
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Detect architecture pattern
|
|
118
|
-
*/
|
|
119
|
-
async function detectArchitecture(projectPath) {
|
|
120
|
-
const globIgnore = ['framework/**', 'test/**', 'node_modules/**', '.morph/**'];
|
|
121
|
-
|
|
122
|
-
const checks = [
|
|
123
|
-
// .NET: Clean Architecture
|
|
124
|
-
{
|
|
125
|
-
pattern: 'clean-architecture',
|
|
126
|
-
indicators: [
|
|
127
|
-
existsSync(join(projectPath, 'src', 'Application')),
|
|
128
|
-
existsSync(join(projectPath, 'src', 'Domain')),
|
|
129
|
-
existsSync(join(projectPath, 'src', 'Infrastructure'))
|
|
130
|
-
]
|
|
131
|
-
},
|
|
132
|
-
// .NET: CQRS
|
|
133
|
-
{
|
|
134
|
-
pattern: 'cqrs',
|
|
135
|
-
indicators: [
|
|
136
|
-
await glob('**/Commands/**/*.cs', { cwd: projectPath, ignore: globIgnore }).then(f => f.length > 0),
|
|
137
|
-
await glob('**/Queries/**/*.cs', { cwd: projectPath, ignore: globIgnore }).then(f => f.length > 0)
|
|
138
|
-
]
|
|
139
|
-
},
|
|
140
|
-
// .NET: MVC
|
|
141
|
-
{
|
|
142
|
-
pattern: 'mvc',
|
|
143
|
-
indicators: [
|
|
144
|
-
existsSync(join(projectPath, 'Controllers')),
|
|
145
|
-
existsSync(join(projectPath, 'Models')),
|
|
146
|
-
existsSync(join(projectPath, 'Views'))
|
|
147
|
-
]
|
|
148
|
-
},
|
|
149
|
-
// JS: CLI / Library (bin + src + lib)
|
|
150
|
-
{
|
|
151
|
-
pattern: 'cli-library',
|
|
152
|
-
indicators: [
|
|
153
|
-
existsSync(join(projectPath, 'bin')),
|
|
154
|
-
existsSync(join(projectPath, 'src')),
|
|
155
|
-
existsSync(join(projectPath, 'package.json'))
|
|
156
|
-
]
|
|
157
|
-
},
|
|
158
|
-
// JS: Next.js App Router
|
|
159
|
-
{
|
|
160
|
-
pattern: 'nextjs-app-router',
|
|
161
|
-
indicators: [
|
|
162
|
-
existsSync(join(projectPath, 'app')),
|
|
163
|
-
await glob('app/**/*.{js,ts,jsx,tsx}', { cwd: projectPath, ignore: globIgnore }).then(f => f.length > 0)
|
|
164
|
-
]
|
|
165
|
-
},
|
|
166
|
-
// JS: Express MVC
|
|
167
|
-
{
|
|
168
|
-
pattern: 'express-mvc',
|
|
169
|
-
indicators: [
|
|
170
|
-
existsSync(join(projectPath, 'src', 'routes')) || existsSync(join(projectPath, 'routes')),
|
|
171
|
-
existsSync(join(projectPath, 'src', 'controllers')) || existsSync(join(projectPath, 'controllers'))
|
|
172
|
-
]
|
|
173
|
-
}
|
|
174
|
-
];
|
|
175
|
-
|
|
176
|
-
for (const { pattern, indicators } of checks) {
|
|
177
|
-
if (indicators.every(Boolean)) {
|
|
178
|
-
return pattern;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
return 'unknown';
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Detect UI library (Blazor projects)
|
|
187
|
-
*/
|
|
188
|
-
async function detectUILibrary(projectPath) {
|
|
189
|
-
const csprojFiles = await glob('**/*.csproj', { cwd: projectPath });
|
|
190
|
-
|
|
191
|
-
for (const csprojFile of csprojFiles) {
|
|
192
|
-
const content = await import('fs').then(fs =>
|
|
193
|
-
fs.promises.readFile(join(projectPath, csprojFile), 'utf8')
|
|
194
|
-
);
|
|
195
|
-
|
|
196
|
-
if (content.includes('FluentUI.Blazor') || content.includes('Microsoft.FluentUI.AspNetCore.Components')) {
|
|
197
|
-
return 'fluent-ui';
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
if (content.includes('MudBlazor')) {
|
|
201
|
-
return 'mudblazor';
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
return null;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Detect common patterns
|
|
210
|
-
*/
|
|
211
|
-
async function detectPatterns(projectPath) {
|
|
212
|
-
const patterns = [];
|
|
213
|
-
// Ignore template/vendor directories; keep test/ accessible for JS test detection
|
|
214
|
-
const globIgnore = ['framework/**', 'test/fixtures/**', 'node_modules/**', '.morph/**'];
|
|
215
|
-
|
|
216
|
-
// C# / .NET patterns
|
|
217
|
-
const dotnetChecks = [
|
|
218
|
-
{ name: 'Repository Pattern', glob: '**/*Repository.cs' },
|
|
219
|
-
{ name: 'Service Layer', glob: '**/*Service.cs' },
|
|
220
|
-
{ name: 'DTOs', glob: '**/*Dto.cs' },
|
|
221
|
-
{ name: 'Entity Framework', glob: '**/Migrations/**/*.cs' },
|
|
222
|
-
{ name: 'Dependency Injection', glob: '**/DependencyInjection.cs' },
|
|
223
|
-
{ name: 'Hangfire Jobs', glob: '**/*Job.cs' },
|
|
224
|
-
{ name: 'AI Agents', glob: '**/*Agent.cs' },
|
|
225
|
-
{ name: 'Unit Tests', glob: '**/*.Tests/**/*.cs' }
|
|
226
|
-
];
|
|
227
|
-
|
|
228
|
-
// JavaScript / TypeScript patterns
|
|
229
|
-
const jsChecks = [
|
|
230
|
-
{ name: 'Service Layer', glob: 'src/**/*Service.js' },
|
|
231
|
-
{ name: 'Service Layer', glob: 'src/**/*Service.ts' },
|
|
232
|
-
{ name: 'Repository Pattern', glob: 'src/**/*Repository.js' },
|
|
233
|
-
{ name: 'Repository Pattern', glob: 'src/**/*Repository.ts' },
|
|
234
|
-
{ name: 'API Routes', glob: 'src/**/routes/**/*.{js,ts}' },
|
|
235
|
-
{ name: 'API Routes', glob: 'src/commands/**/*.{js,ts}' },
|
|
236
|
-
{ name: 'Unit Tests', glob: 'test/**/*.test.{js,ts}' },
|
|
237
|
-
{ name: 'Unit Tests', glob: 'src/**/*.test.{js,ts}' },
|
|
238
|
-
{ name: 'Unit Tests', glob: 'src/**/*.spec.{js,ts}' }
|
|
239
|
-
];
|
|
240
|
-
|
|
241
|
-
const allChecks = [...dotnetChecks, ...jsChecks];
|
|
242
|
-
const seen = new Set();
|
|
243
|
-
|
|
244
|
-
for (const { name, glob: pattern } of allChecks) {
|
|
245
|
-
if (seen.has(name)) continue;
|
|
246
|
-
const files = await glob(pattern, { cwd: projectPath, nodir: true, ignore: globIgnore });
|
|
247
|
-
if (files.length > 0) {
|
|
248
|
-
patterns.push(name);
|
|
249
|
-
seen.add(name);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
return patterns;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* Analyze folder structure
|
|
258
|
-
*/
|
|
259
|
-
async function analyzeFolders(projectPath) {
|
|
260
|
-
const folders = {
|
|
261
|
-
hasPages: false,
|
|
262
|
-
hasComponents: false,
|
|
263
|
-
hasServices: false,
|
|
264
|
-
hasRepositories: false,
|
|
265
|
-
hasAgents: false
|
|
266
|
-
};
|
|
267
|
-
|
|
268
|
-
folders.hasPages = await glob('**/Pages/**/*', { cwd: projectPath }).then(f => f.length > 0);
|
|
269
|
-
folders.hasComponents = await glob('**/Components/**/*', { cwd: projectPath }).then(f => f.length > 0);
|
|
270
|
-
folders.hasServices = await glob('**/Services/**/*', { cwd: projectPath }).then(f => f.length > 0);
|
|
271
|
-
folders.hasRepositories = await glob('**/Repositories/**/*', { cwd: projectPath }).then(f => f.length > 0);
|
|
272
|
-
folders.hasAgents = await glob('**/Agents/**/*', { cwd: projectPath }).then(f => f.length > 0);
|
|
273
|
-
|
|
274
|
-
return folders;
|
|
275
|
-
}
|
|
@@ -1,304 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Fusion Executor — F-Thread best-of-N aggregation
|
|
3
|
-
*
|
|
4
|
-
* Runs N parallel agents on the same prompt, collects results,
|
|
5
|
-
* then aggregates using one of three strategies:
|
|
6
|
-
* - best-of-n: score each result, pick highest (automated)
|
|
7
|
-
* - consensus: merge common elements from all results
|
|
8
|
-
* - manual-select: present all N to user for selection
|
|
9
|
-
*
|
|
10
|
-
* Returns: { winner, allResults, scores }
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { createThread, startThread, completeThread, failThread, THREAD_TYPES } from '../threads/thread-manager.js';
|
|
14
|
-
import { recordEvent, generateAsciiChart } from '../analytics/analytics-engine.js';
|
|
15
|
-
|
|
16
|
-
// ============================================================================
|
|
17
|
-
// Scoring
|
|
18
|
-
// ============================================================================
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Score a fusion result based on completeness, compliance, and quality
|
|
22
|
-
* @param {Object} result - Agent result object
|
|
23
|
-
* @param {Object} [rubric] - Scoring rubric
|
|
24
|
-
* @returns {number} Score 0-100
|
|
25
|
-
*/
|
|
26
|
-
export function scoreResult(result, rubric = {}) {
|
|
27
|
-
let score = 0;
|
|
28
|
-
const weights = {
|
|
29
|
-
completeness: rubric.completenessWeight || 40,
|
|
30
|
-
checkpointCompliance: rubric.complianceWeight || 30,
|
|
31
|
-
codeQuality: rubric.qualityWeight || 30
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
// Completeness: does it have all required deliverables?
|
|
35
|
-
if (result.deliverables) {
|
|
36
|
-
const required = rubric.requiredDeliverables?.length || 1;
|
|
37
|
-
const provided = Object.keys(result.deliverables).length;
|
|
38
|
-
score += weights.completeness * Math.min(provided / required, 1.0);
|
|
39
|
-
} else if (result.content) {
|
|
40
|
-
// Estimate completeness from content length
|
|
41
|
-
const targetLength = rubric.targetLength || 1000;
|
|
42
|
-
score += weights.completeness * Math.min(result.content.length / targetLength, 1.0);
|
|
43
|
-
} else {
|
|
44
|
-
score += weights.completeness * 0.5; // Partial credit
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Checkpoint compliance: did it follow patterns?
|
|
48
|
-
if (result.checkpointsPassed !== undefined) {
|
|
49
|
-
const total = (result.checkpointsPassed || 0) + (result.checkpointsFailed || 0);
|
|
50
|
-
const passRate = total > 0 ? result.checkpointsPassed / total : 0.8; // Assume 80% if unknown
|
|
51
|
-
score += weights.checkpointCompliance * passRate;
|
|
52
|
-
} else {
|
|
53
|
-
score += weights.checkpointCompliance * 0.8; // Default: assume compliant
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Code quality: based on error count and warnings
|
|
57
|
-
const errors = result.errors || 0;
|
|
58
|
-
const warnings = result.warnings || 0;
|
|
59
|
-
const qualityScore = Math.max(0, 1.0 - (errors * 0.2) - (warnings * 0.05));
|
|
60
|
-
score += weights.codeQuality * qualityScore;
|
|
61
|
-
|
|
62
|
-
return Math.round(score);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// ============================================================================
|
|
66
|
-
// Aggregation Strategies
|
|
67
|
-
// ============================================================================
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Best-of-N aggregation: score each result, return highest scorer
|
|
71
|
-
* @param {Array} results - Array of agent results
|
|
72
|
-
* @param {Object} rubric - Scoring rubric
|
|
73
|
-
* @returns {Object} { winner, scores }
|
|
74
|
-
*/
|
|
75
|
-
export function aggregateBestOfN(results, rubric = {}) {
|
|
76
|
-
const scored = results.map((result, idx) => ({
|
|
77
|
-
index: idx,
|
|
78
|
-
agentId: result.agentId || `agent-${idx}`,
|
|
79
|
-
result,
|
|
80
|
-
score: scoreResult(result, rubric)
|
|
81
|
-
}));
|
|
82
|
-
|
|
83
|
-
scored.sort((a, b) => b.score - a.score);
|
|
84
|
-
const winner = scored[0];
|
|
85
|
-
|
|
86
|
-
return {
|
|
87
|
-
winner: winner.result,
|
|
88
|
-
winnerId: winner.agentId,
|
|
89
|
-
winnerScore: winner.score,
|
|
90
|
-
scores: scored.map(s => ({
|
|
91
|
-
agentId: s.agentId,
|
|
92
|
-
score: s.score,
|
|
93
|
-
rank: scored.indexOf(s) + 1
|
|
94
|
-
}))
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Consensus aggregation: merge common elements from all results
|
|
100
|
-
* @param {Array} results - Array of agent results
|
|
101
|
-
* @returns {Object} { merged, commonElements }
|
|
102
|
-
*/
|
|
103
|
-
export function aggregateConsensus(results) {
|
|
104
|
-
if (results.length === 0) return { merged: {}, commonElements: [] };
|
|
105
|
-
if (results.length === 1) return { merged: results[0], commonElements: [] };
|
|
106
|
-
|
|
107
|
-
// Find common keys across all results
|
|
108
|
-
const allKeys = results.map(r => Object.keys(r));
|
|
109
|
-
const commonKeys = allKeys[0].filter(key =>
|
|
110
|
-
allKeys.every(keys => keys.includes(key))
|
|
111
|
-
);
|
|
112
|
-
|
|
113
|
-
const merged = {};
|
|
114
|
-
const commonElements = [];
|
|
115
|
-
|
|
116
|
-
for (const key of commonKeys) {
|
|
117
|
-
const values = results.map(r => r[key]);
|
|
118
|
-
|
|
119
|
-
// For strings: use longest (most complete) version
|
|
120
|
-
if (values.every(v => typeof v === 'string')) {
|
|
121
|
-
merged[key] = values.reduce((longest, v) =>
|
|
122
|
-
v.length > longest.length ? v : longest, ''
|
|
123
|
-
);
|
|
124
|
-
commonElements.push(key);
|
|
125
|
-
}
|
|
126
|
-
// For arrays: union of all arrays
|
|
127
|
-
else if (values.every(v => Array.isArray(v))) {
|
|
128
|
-
merged[key] = [...new Set(values.flat())];
|
|
129
|
-
commonElements.push(key);
|
|
130
|
-
}
|
|
131
|
-
// For objects: merge recursively (simple one level)
|
|
132
|
-
else if (values.every(v => v && typeof v === 'object')) {
|
|
133
|
-
merged[key] = Object.assign({}, ...values);
|
|
134
|
-
commonElements.push(key);
|
|
135
|
-
}
|
|
136
|
-
// For primitives: use majority value
|
|
137
|
-
else {
|
|
138
|
-
const valueCounts = values.reduce((acc, v) => {
|
|
139
|
-
const key2 = String(v);
|
|
140
|
-
acc[key2] = (acc[key2] || 0) + 1;
|
|
141
|
-
return acc;
|
|
142
|
-
}, {});
|
|
143
|
-
const [majority] = Object.entries(valueCounts).sort(([, a], [, b]) => b - a);
|
|
144
|
-
merged[key] = majority ? JSON.parse(majority[0]) : values[0];
|
|
145
|
-
commonElements.push(key);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return { merged, commonElements };
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// ============================================================================
|
|
153
|
-
// Main Fusion Runner
|
|
154
|
-
// ============================================================================
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Run fusion execution: spawn N agents in parallel, collect and aggregate results
|
|
158
|
-
* @param {Object} opts
|
|
159
|
-
* @param {string} opts.feature - Feature name
|
|
160
|
-
* @param {string} opts.prompt - Prompt/mission for all agents
|
|
161
|
-
* @param {number} [opts.count=3] - Number of agents to run
|
|
162
|
-
* @param {string} [opts.strategy='best-of-n'] - Aggregation strategy
|
|
163
|
-
* @param {string[]} [opts.agents] - Agent IDs to use (will use thread types if not specified)
|
|
164
|
-
* @param {Object} [opts.rubric] - Scoring rubric for best-of-n
|
|
165
|
-
* @returns {Promise<Object>} { winner, allResults, scores, strategy, threads }
|
|
166
|
-
*/
|
|
167
|
-
export async function runFusion(prompt, {
|
|
168
|
-
feature,
|
|
169
|
-
count = 3,
|
|
170
|
-
strategy = 'best-of-n',
|
|
171
|
-
agents = null,
|
|
172
|
-
rubric = {}
|
|
173
|
-
} = {}) {
|
|
174
|
-
const { randomUUID } = await import('crypto');
|
|
175
|
-
const sessionId = randomUUID();
|
|
176
|
-
const agentList = agents || Array.from({ length: count }, (_, i) => `fusion-agent-${i + 1}`);
|
|
177
|
-
const threads = [];
|
|
178
|
-
const results = [];
|
|
179
|
-
|
|
180
|
-
recordEvent({
|
|
181
|
-
type: 'fusion_started',
|
|
182
|
-
feature,
|
|
183
|
-
data: { count, strategy, agents: agentList }
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
// Create and start all fusion threads simultaneously
|
|
187
|
-
for (let i = 0; i < agentList.length; i++) {
|
|
188
|
-
const thread = createThread({
|
|
189
|
-
feature,
|
|
190
|
-
type: THREAD_TYPES.FUSION,
|
|
191
|
-
agent: agentList[i],
|
|
192
|
-
mission: prompt,
|
|
193
|
-
meta: { fusionIndex: i, strategy }
|
|
194
|
-
});
|
|
195
|
-
startThread(thread.id);
|
|
196
|
-
threads.push(thread);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
console.log(` [fusion] ${threads.length} agents spawned. Collecting results...`);
|
|
200
|
-
|
|
201
|
-
// In a real system, results would come from actual sub-agent execution.
|
|
202
|
-
// Here we return thread configs for the calling code to use with Task tool.
|
|
203
|
-
// The aggregation is designed to be called after results are collected.
|
|
204
|
-
|
|
205
|
-
return {
|
|
206
|
-
id: sessionId,
|
|
207
|
-
threads,
|
|
208
|
-
strategy,
|
|
209
|
-
feature,
|
|
210
|
-
prompt,
|
|
211
|
-
agentConfigs: agentList.map((agentId, i) => ({
|
|
212
|
-
agentId,
|
|
213
|
-
threadId: threads[i].id,
|
|
214
|
-
mission: prompt
|
|
215
|
-
})),
|
|
216
|
-
aggregate: (collectedResults) => aggregateResults(collectedResults, strategy, rubric)
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Aggregate collected results from N agents
|
|
222
|
-
* @param {Array} results - Array of result objects from agents
|
|
223
|
-
* @param {string} strategy - Aggregation strategy
|
|
224
|
-
* @param {Object} [rubric] - Scoring rubric for best-of-n
|
|
225
|
-
* @returns {Object} Aggregated result
|
|
226
|
-
*/
|
|
227
|
-
export function aggregateResults(results, strategy = 'best-of-n', rubric = {}) {
|
|
228
|
-
if (!results || results.length === 0) {
|
|
229
|
-
throw new Error('No results to aggregate');
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
let aggregated;
|
|
233
|
-
|
|
234
|
-
switch (strategy) {
|
|
235
|
-
case 'best-of-n':
|
|
236
|
-
aggregated = aggregateBestOfN(results, rubric);
|
|
237
|
-
break;
|
|
238
|
-
|
|
239
|
-
case 'consensus':
|
|
240
|
-
const { merged, commonElements } = aggregateConsensus(results);
|
|
241
|
-
aggregated = {
|
|
242
|
-
winner: merged,
|
|
243
|
-
winnerId: 'consensus',
|
|
244
|
-
scores: results.map((_, i) => ({
|
|
245
|
-
agentId: `agent-${i}`,
|
|
246
|
-
score: 0,
|
|
247
|
-
rank: i + 1
|
|
248
|
-
})),
|
|
249
|
-
commonElements
|
|
250
|
-
};
|
|
251
|
-
break;
|
|
252
|
-
|
|
253
|
-
case 'manual-select':
|
|
254
|
-
aggregated = {
|
|
255
|
-
winner: null,
|
|
256
|
-
winnerId: null,
|
|
257
|
-
allResults: results,
|
|
258
|
-
requiresUserSelection: true,
|
|
259
|
-
scores: results.map((r, i) => ({
|
|
260
|
-
agentId: r.agentId || `agent-${i}`,
|
|
261
|
-
score: scoreResult(r, rubric),
|
|
262
|
-
rank: i + 1
|
|
263
|
-
}))
|
|
264
|
-
};
|
|
265
|
-
break;
|
|
266
|
-
|
|
267
|
-
default:
|
|
268
|
-
throw new Error(`Unknown aggregation strategy: ${strategy}`);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
return {
|
|
272
|
-
...aggregated,
|
|
273
|
-
strategy,
|
|
274
|
-
totalAgents: results.length,
|
|
275
|
-
allResults: results
|
|
276
|
-
};
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* Display fusion results summary
|
|
281
|
-
* @param {Object} fusionResult - Result from aggregateResults
|
|
282
|
-
*/
|
|
283
|
-
export function displayFusionSummary(fusionResult) {
|
|
284
|
-
const { strategy, totalAgents, scores, winner, requiresUserSelection } = fusionResult;
|
|
285
|
-
|
|
286
|
-
console.log(`\n Fusion Results — ${strategy} (${totalAgents} agents)\n`);
|
|
287
|
-
console.log(' ' + '─'.repeat(50));
|
|
288
|
-
|
|
289
|
-
if (scores) {
|
|
290
|
-
const scoreData = {};
|
|
291
|
-
scores.forEach(s => { scoreData[s.agentId] = s.score; });
|
|
292
|
-
console.log(generateAsciiChart(scoreData, { title: 'Agent Scores' }));
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
if (requiresUserSelection) {
|
|
296
|
-
console.log('\n Manual selection required — review results above');
|
|
297
|
-
} else if (winner) {
|
|
298
|
-
console.log(`\n Winner: ${fusionResult.winnerId || 'consensus'}`);
|
|
299
|
-
if (fusionResult.winnerScore !== undefined) {
|
|
300
|
-
console.log(` Score: ${fusionResult.winnerScore}/100`);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
console.log('');
|
|
304
|
-
}
|