@polymorphism-tech/morph-spec 4.2.0 → 4.3.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 (140) hide show
  1. package/CLAUDE.md +108 -946
  2. package/bin/morph-spec.js +284 -9
  3. package/bin/task-manager.cjs +102 -14
  4. package/bin/validate.js +4 -4
  5. package/docs/{v3.0 → next-generation}/AGENTS.md +1 -1
  6. package/docs/next-generation/CONTEXT-OPTIMIZATION.md +267 -0
  7. package/docs/next-generation/EXECUTION-FLOW.md +274 -0
  8. package/docs/next-generation/META-PROMPTS.md +235 -0
  9. package/docs/next-generation/MIGRATION-GUIDE.md +253 -0
  10. package/docs/next-generation/THREAD-MANAGEMENT.md +240 -0
  11. package/package.json +5 -5
  12. package/src/commands/agents/agents-fuse.js +97 -0
  13. package/src/commands/agents/micro-agent.js +112 -0
  14. package/src/commands/agents/spawn-team.js +69 -4
  15. package/src/commands/agents/squad-template.js +146 -0
  16. package/src/commands/analytics/analytics.js +176 -0
  17. package/src/commands/context/context-prime.js +63 -0
  18. package/src/commands/context/core-four.js +54 -0
  19. package/src/commands/mcp/mcp.js +102 -0
  20. package/src/commands/project/detect-agents.js +32 -2
  21. package/src/commands/project/detect.js +11 -1
  22. package/src/commands/project/doctor.js +573 -356
  23. package/src/commands/project/init.js +9 -2
  24. package/src/commands/project/update.js +13 -3
  25. package/src/commands/state/advance-phase.js +448 -416
  26. package/src/commands/state/state.js +14 -12
  27. package/src/commands/tasks/task.js +1 -1
  28. package/src/commands/templates/template-render.js +80 -1
  29. package/src/commands/threads/thread-template.js +103 -0
  30. package/src/commands/threads/threads.js +261 -0
  31. package/src/commands/trust/trust.js +205 -0
  32. package/src/{orchestrator.js → core/orchestrator.js} +8 -8
  33. package/src/core/state/state-manager.js +37 -17
  34. package/src/core/workflows/workflow-detector.js +114 -3
  35. package/src/lib/agents/micro-agent-factory.js +161 -0
  36. package/src/lib/analytics/analytics-engine.js +345 -0
  37. package/src/lib/checkpoints/checkpoint-hooks.js +298 -258
  38. package/src/lib/context/context-bundler.js +240 -0
  39. package/src/lib/context/context-optimizer.js +212 -0
  40. package/src/lib/context/context-tracker.js +273 -0
  41. package/src/lib/context/core-four-tracker.js +201 -0
  42. package/src/lib/context/mcp-optimizer.js +200 -0
  43. package/src/lib/detectors/index.js +1 -1
  44. package/src/lib/detectors/standards-generator.js +77 -17
  45. package/src/lib/detectors/structure-detector.js +67 -39
  46. package/src/lib/execution/fusion-executor.js +304 -0
  47. package/src/lib/execution/parallel-executor.js +270 -0
  48. package/src/lib/generators/context-generator.js +3 -3
  49. package/src/lib/generators/recap-generator.js +32 -12
  50. package/src/lib/hooks/hook-executor.js +169 -0
  51. package/src/lib/hooks/stop-hook-executor.js +286 -0
  52. package/src/lib/hops/hop-composer.js +221 -0
  53. package/src/lib/threads/thread-coordinator.js +238 -0
  54. package/src/lib/threads/thread-manager.js +317 -0
  55. package/src/lib/tracking/artifact-trail.js +202 -0
  56. package/src/lib/trust/trust-manager.js +269 -0
  57. package/src/lib/validators/design-system/design-system-validator.js +2 -2
  58. package/src/lib/validators/validation-runner.js +14 -30
  59. package/src/utils/hooks-installer.js +69 -0
  60. package/stacks/blazor-azure/.morph/config/agents.json +72 -3
  61. package/stacks/nextjs-supabase/.morph/config/agents.json +3 -3
  62. package/docs/llm-interaction-config.md +0 -735
  63. package/docs/v3.0/EXECUTION-FLOW.md +0 -1304
  64. package/src/commands/utils/migrate-state.js +0 -158
  65. package/src/commands/utils/upgrade.js +0 -346
  66. package/src/lib/validators/architecture-validator.js +0 -60
  67. package/src/lib/validators/content-validator.js +0 -164
  68. package/src/lib/validators/package-validator.js +0 -61
  69. package/src/lib/validators/ui-contrast-validator.js +0 -44
  70. package/stacks/blazor-azure/.claude/commands/morph-apply.md +0 -221
  71. package/stacks/blazor-azure/.claude/commands/morph-archive.md +0 -79
  72. package/stacks/blazor-azure/.claude/commands/morph-deploy.md +0 -529
  73. package/stacks/blazor-azure/.claude/commands/morph-infra.md +0 -209
  74. package/stacks/blazor-azure/.claude/commands/morph-preflight.md +0 -227
  75. package/stacks/blazor-azure/.claude/commands/morph-proposal.md +0 -122
  76. package/stacks/blazor-azure/.claude/commands/morph-status.md +0 -86
  77. package/stacks/blazor-azure/.claude/commands/morph-troubleshoot.md +0 -122
  78. package/stacks/blazor-azure/.claude/skills/level-0-meta/README.md +0 -7
  79. package/stacks/blazor-azure/.claude/skills/level-0-meta/code-review.md +0 -226
  80. package/stacks/blazor-azure/.claude/skills/level-0-meta/morph-checklist.md +0 -117
  81. package/stacks/blazor-azure/.claude/skills/level-0-meta/simulation-checklist.md +0 -77
  82. package/stacks/blazor-azure/.claude/skills/level-1-workflows/README.md +0 -7
  83. package/stacks/blazor-azure/.claude/skills/level-1-workflows/morph-replicate.md +0 -213
  84. package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-clarify.md +0 -131
  85. package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-design.md +0 -213
  86. package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-setup.md +0 -106
  87. package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-tasks.md +0 -164
  88. package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-uiux.md +0 -169
  89. package/stacks/blazor-azure/.claude/skills/level-2-domains/README.md +0 -14
  90. package/stacks/blazor-azure/.claude/skills/level-2-domains/ai-agents/ai-system-architect.md +0 -192
  91. package/stacks/blazor-azure/.claude/skills/level-2-domains/architecture/po-pm-advisor.md +0 -197
  92. package/stacks/blazor-azure/.claude/skills/level-2-domains/architecture/prompt-engineer.md +0 -189
  93. package/stacks/blazor-azure/.claude/skills/level-2-domains/architecture/seo-growth-hacker.md +0 -320
  94. package/stacks/blazor-azure/.claude/skills/level-2-domains/architecture/standards-architect.md +0 -156
  95. package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/api-designer.md +0 -59
  96. package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/dotnet-senior.md +0 -77
  97. package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/ef-modeler.md +0 -58
  98. package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/hangfire-orchestrator.md +0 -126
  99. package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/ms-agent-expert.md +0 -45
  100. package/stacks/blazor-azure/.claude/skills/level-2-domains/frontend/blazor-builder.md +0 -210
  101. package/stacks/blazor-azure/.claude/skills/level-2-domains/frontend/nextjs-expert.md +0 -154
  102. package/stacks/blazor-azure/.claude/skills/level-2-domains/frontend/ui-ux-designer.md +0 -191
  103. package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/azure-architect.md +0 -142
  104. package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/azure-deploy-specialist.md +0 -699
  105. package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/bicep-architect.md +0 -126
  106. package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/container-specialist.md +0 -131
  107. package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/devops-engineer.md +0 -119
  108. package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/asaas-financial.md +0 -130
  109. package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/azure-identity.md +0 -142
  110. package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/clerk-auth.md +0 -108
  111. package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/hangfire-orchestrator.md +0 -64
  112. package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/resend-email.md +0 -119
  113. package/stacks/blazor-azure/.claude/skills/level-2-domains/quality/code-analyzer.md +0 -235
  114. package/stacks/blazor-azure/.claude/skills/level-2-domains/quality/testing-specialist.md +0 -126
  115. package/stacks/blazor-azure/.claude/skills/level-3-technologies/README.md +0 -7
  116. package/stacks/blazor-azure/.claude/skills/level-4-patterns/README.md +0 -7
  117. package/stacks/blazor-azure/.morph/archive/.gitkeep +0 -25
  118. package/stacks/blazor-azure/.morph/features/.gitkeep +0 -25
  119. package/stacks/blazor-azure/.morph/schemas/agent.schema.json +0 -296
  120. package/stacks/blazor-azure/.morph/schemas/tasks.schema.json +0 -220
  121. package/stacks/blazor-azure/.morph/specs/.gitkeep +0 -20
  122. package/stacks/blazor-azure/.morph/test-infra/example.bicep +0 -59
  123. package/stacks/nextjs-supabase/.claude/commands/morph-apply.md +0 -221
  124. package/stacks/nextjs-supabase/.claude/commands/morph-archive.md +0 -79
  125. package/stacks/nextjs-supabase/.claude/commands/morph-deploy.md +0 -529
  126. package/stacks/nextjs-supabase/.claude/commands/morph-infra.md +0 -209
  127. package/stacks/nextjs-supabase/.claude/commands/morph-preflight.md +0 -227
  128. package/stacks/nextjs-supabase/.claude/commands/morph-proposal.md +0 -122
  129. package/stacks/nextjs-supabase/.claude/commands/morph-status.md +0 -86
  130. package/stacks/nextjs-supabase/.claude/commands/morph-troubleshoot.md +0 -122
  131. package/stacks/nextjs-supabase/.claude/settings.local.json +0 -6
  132. package/stacks/nextjs-supabase/.claude/skills/level-2-domains/backend/dotnet-supabase.md +0 -244
  133. package/stacks/nextjs-supabase/.claude/skills/level-2-domains/frontend/nextjs-supabase.md +0 -335
  134. package/stacks/nextjs-supabase/.claude/skills/level-2-domains/infrastructure/easypanel-deployer.md +0 -189
  135. package/stacks/nextjs-supabase/.claude/skills/level-2-domains/integrations/supabase-expert.md +0 -50
  136. /package/docs/{v3.0 → next-generation}/ANALYSIS.md +0 -0
  137. /package/docs/{v3.0 → next-generation}/ARCHITECTURE.md +0 -0
  138. /package/docs/{v3.0 → next-generation}/FEATURES.md +0 -0
  139. /package/docs/{v3.0 → next-generation}/README.md +0 -0
  140. /package/docs/{v3.0 → next-generation}/ROADMAP.md +0 -0
@@ -129,12 +129,15 @@ Based on detected patterns in your codebase:
129
129
  }
130
130
 
131
131
  if (config?.language === 'javascript') {
132
+ const techLine = config.technologies?.length > 0
133
+ ? `\n- **Framework**: ${config.technologies.join(', ')}`
134
+ : '';
135
+
132
136
  return `### JavaScript / TypeScript Conventions
133
137
 
134
138
  Based on detected patterns in your codebase:
135
139
 
136
- - **Package Manager**: ${config.packageManager}
137
- - **Framework**: ${config.technologies.join(', ')}
140
+ - **Package Manager**: ${config.packageManager}${techLine}
138
141
 
139
142
  **Recommendation**: Refer to framework standards for JavaScript best practices.`;
140
143
  }
@@ -149,6 +152,37 @@ function generateArchitectureSection(structure) {
149
152
  const { architecture } = structure;
150
153
 
151
154
  const descriptions = {
155
+ 'cli-library': `### CLI / Library Architecture
156
+
157
+ Your project follows a CLI/Library pattern:
158
+
159
+ - ✅ **bin/** entry points detected
160
+ - ✅ **src/** source directory detected
161
+ - ✅ **package.json** present
162
+
163
+ **Key principles**:
164
+ - Commands exposed via bin/
165
+ - Core logic in src/commands/ and src/lib/
166
+ - Public API exported from src/index.js`,
167
+
168
+ 'nextjs-app-router': `### Next.js App Router Architecture
169
+
170
+ Your project uses the Next.js App Router pattern:
171
+
172
+ - ✅ **app/** directory detected (React Server Components)
173
+
174
+ **Key principles**:
175
+ - Server Components by default
176
+ - Client Components with 'use client' directive
177
+ - Route handlers in app/api/`,
178
+
179
+ 'express-mvc': `### Express MVC Architecture
180
+
181
+ Your project uses Express with MVC pattern:
182
+
183
+ - ✅ **routes/** detected
184
+ - ✅ **controllers/** detected`,
185
+
152
186
  'clean-architecture': `### Clean Architecture
153
187
 
154
188
  Your project follows Clean Architecture pattern:
@@ -203,19 +237,29 @@ function generateRecommendations(structure, config, conversation) {
203
237
  }
204
238
  }
205
239
 
206
- // Recommend tests
207
- if (!structure?.patterns?.includes('Unit Tests')) {
208
- recommendations.push('No unit tests detected - consider adding test project');
209
- }
240
+ if (structure?.stack === 'nodejs' || config?.language === 'javascript') {
241
+ if (!structure?.patterns?.includes('Unit Tests')) {
242
+ recommendations.push('No unit tests detected - consider adding test coverage with Jest or Vitest');
243
+ }
210
244
 
211
- // Recommend DI
212
- if (!structure?.patterns?.includes('Dependency Injection')) {
213
- recommendations.push('Consider implementing Dependency Injection pattern');
214
- }
245
+ if (structure?.architecture === 'unknown') {
246
+ recommendations.push('No clear architecture detected - consider organizing code into src/commands/, src/lib/, src/core/');
247
+ }
248
+ } else {
249
+ // Recommend tests (generic / .NET)
250
+ if (!structure?.patterns?.includes('Unit Tests')) {
251
+ recommendations.push('No unit tests detected - consider adding test project');
252
+ }
215
253
 
216
- // Architecture recommendations
217
- if (structure?.architecture === 'unknown' && structure?.folders?.hasServices) {
218
- recommendations.push('Service layer detected but no clear architecture - consider Clean Architecture or CQRS');
254
+ // Recommend DI (only meaningful for C# / .NET)
255
+ if (config?.language === 'csharp' && !structure?.patterns?.includes('Dependency Injection')) {
256
+ recommendations.push('Consider implementing Dependency Injection pattern');
257
+ }
258
+
259
+ // Architecture recommendations (.NET)
260
+ if (structure?.architecture === 'unknown' && structure?.folders?.hasServices) {
261
+ recommendations.push('Service layer detected but no clear architecture - consider Clean Architecture or CQRS');
262
+ }
219
263
  }
220
264
 
221
265
  return recommendations;
@@ -250,14 +294,30 @@ function identifyGaps(structure, config) {
250
294
  */
251
295
  function filterImportantDependencies(deps) {
252
296
  const important = deps.filter(dep => {
253
- // Framework packages
297
+ // .NET Framework packages
254
298
  if (dep.includes('Microsoft.') || dep.includes('System.')) return true;
255
- // UI libraries
299
+ // Blazor UI libraries
256
300
  if (dep.includes('Blazor') || dep.includes('FluentUI') || dep.includes('Mud')) return true;
257
- // Common tools
301
+ // .NET common tools
258
302
  if (dep.includes('Hangfire') || dep.includes('Serilog') || dep.includes('AutoMapper')) return true;
259
- // AI/ML
303
+ // AI/ML (.NET)
260
304
  if (dep.includes('Agents') || dep.includes('AI') || dep.includes('OpenAI')) return true;
305
+ // JS: frontend frameworks
306
+ if (['next', 'react', 'react-dom', 'vue', 'nuxt', 'svelte', 'astro'].includes(dep)) return true;
307
+ // JS: backend frameworks
308
+ if (['express', 'fastify', 'hono', 'koa', 'nestjs', '@nestjs/core'].includes(dep)) return true;
309
+ // JS: databases / ORMs
310
+ if (['prisma', '@prisma/client', 'supabase', '@supabase/supabase-js', 'drizzle-orm', 'mongoose', 'typeorm'].includes(dep)) return true;
311
+ // JS: auth
312
+ if (['next-auth', 'clerk', '@clerk/nextjs', 'lucia', 'better-auth'].includes(dep)) return true;
313
+ // JS: validation / schema
314
+ if (['zod', 'yup', 'joi', 'valibot'].includes(dep)) return true;
315
+ // JS: testing
316
+ if (['vitest', 'jest', '@jest/core', 'mocha', 'cypress', 'playwright'].includes(dep)) return true;
317
+ // JS: UI / styling
318
+ if (['tailwindcss', '@mui/material', 'shadcn-ui', 'radix-ui', 'chakra-ui'].includes(dep)) return true;
319
+ // JS: AI
320
+ if (dep.includes('openai') || dep.includes('anthropic') || dep.includes('langchain') || dep.includes('ai')) return true;
261
321
  return false;
262
322
  });
263
323
 
@@ -97,9 +97,11 @@ async function detectStack(projectPath) {
97
97
  ]
98
98
  };
99
99
 
100
+ const globIgnore = ['stacks/**', 'framework/**', 'test/**', 'node_modules/**', '.morph/**'];
101
+
100
102
  for (const [stack, globs] of Object.entries(patterns)) {
101
103
  for (const pattern of globs) {
102
- const files = await glob(pattern, { cwd: projectPath, nodir: true });
104
+ const files = await glob(pattern, { cwd: projectPath, nodir: true, ignore: globIgnore });
103
105
  if (files.length > 0) {
104
106
  return stack;
105
107
  }
@@ -113,7 +115,10 @@ async function detectStack(projectPath) {
113
115
  * Detect architecture pattern
114
116
  */
115
117
  async function detectArchitecture(projectPath) {
118
+ const globIgnore = ['stacks/**', 'framework/**', 'test/**', 'node_modules/**', '.morph/**'];
119
+
116
120
  const checks = [
121
+ // .NET: Clean Architecture
117
122
  {
118
123
  pattern: 'clean-architecture',
119
124
  indicators: [
@@ -122,13 +127,15 @@ async function detectArchitecture(projectPath) {
122
127
  existsSync(join(projectPath, 'src', 'Infrastructure'))
123
128
  ]
124
129
  },
130
+ // .NET: CQRS
125
131
  {
126
132
  pattern: 'cqrs',
127
133
  indicators: [
128
- await glob('**/Commands/**/*.cs', { cwd: projectPath }).then(f => f.length > 0),
129
- await glob('**/Queries/**/*.cs', { cwd: projectPath }).then(f => f.length > 0)
134
+ await glob('**/Commands/**/*.cs', { cwd: projectPath, ignore: globIgnore }).then(f => f.length > 0),
135
+ await glob('**/Queries/**/*.cs', { cwd: projectPath, ignore: globIgnore }).then(f => f.length > 0)
130
136
  ]
131
137
  },
138
+ // .NET: MVC
132
139
  {
133
140
  pattern: 'mvc',
134
141
  indicators: [
@@ -136,6 +143,31 @@ async function detectArchitecture(projectPath) {
136
143
  existsSync(join(projectPath, 'Models')),
137
144
  existsSync(join(projectPath, 'Views'))
138
145
  ]
146
+ },
147
+ // JS: CLI / Library (bin + src + lib)
148
+ {
149
+ pattern: 'cli-library',
150
+ indicators: [
151
+ existsSync(join(projectPath, 'bin')),
152
+ existsSync(join(projectPath, 'src')),
153
+ existsSync(join(projectPath, 'package.json'))
154
+ ]
155
+ },
156
+ // JS: Next.js App Router
157
+ {
158
+ pattern: 'nextjs-app-router',
159
+ indicators: [
160
+ existsSync(join(projectPath, 'app')),
161
+ await glob('app/**/*.{js,ts,jsx,tsx}', { cwd: projectPath, ignore: globIgnore }).then(f => f.length > 0)
162
+ ]
163
+ },
164
+ // JS: Express MVC
165
+ {
166
+ pattern: 'express-mvc',
167
+ indicators: [
168
+ existsSync(join(projectPath, 'src', 'routes')) || existsSync(join(projectPath, 'routes')),
169
+ existsSync(join(projectPath, 'src', 'controllers')) || existsSync(join(projectPath, 'controllers'))
170
+ ]
139
171
  }
140
172
  ];
141
173
 
@@ -176,47 +208,43 @@ async function detectUILibrary(projectPath) {
176
208
  */
177
209
  async function detectPatterns(projectPath) {
178
210
  const patterns = [];
211
+ // Ignore template/vendor directories; keep test/ accessible for JS test detection
212
+ const globIgnore = ['stacks/**', 'framework/**', 'test/fixtures/**', 'node_modules/**', '.morph/**'];
179
213
 
180
- // Check for specific patterns
181
- const checks = [
182
- {
183
- name: 'Repository Pattern',
184
- glob: '**/*Repository.cs'
185
- },
186
- {
187
- name: 'Service Layer',
188
- glob: '**/*Service.cs'
189
- },
190
- {
191
- name: 'DTOs',
192
- glob: '**/*Dto.cs'
193
- },
194
- {
195
- name: 'Entity Framework',
196
- glob: '**/Migrations/**/*.cs'
197
- },
198
- {
199
- name: 'Dependency Injection',
200
- glob: '**/DependencyInjection.cs'
201
- },
202
- {
203
- name: 'Hangfire Jobs',
204
- glob: '**/*Job.cs'
205
- },
206
- {
207
- name: 'AI Agents',
208
- glob: '**/*Agent.cs'
209
- },
210
- {
211
- name: 'Unit Tests',
212
- glob: '**/*.Tests/**/*.cs'
213
- }
214
+ // C# / .NET patterns
215
+ const dotnetChecks = [
216
+ { name: 'Repository Pattern', glob: '**/*Repository.cs' },
217
+ { name: 'Service Layer', glob: '**/*Service.cs' },
218
+ { name: 'DTOs', glob: '**/*Dto.cs' },
219
+ { name: 'Entity Framework', glob: '**/Migrations/**/*.cs' },
220
+ { name: 'Dependency Injection', glob: '**/DependencyInjection.cs' },
221
+ { name: 'Hangfire Jobs', glob: '**/*Job.cs' },
222
+ { name: 'AI Agents', glob: '**/*Agent.cs' },
223
+ { name: 'Unit Tests', glob: '**/*.Tests/**/*.cs' }
224
+ ];
225
+
226
+ // JavaScript / TypeScript patterns
227
+ const jsChecks = [
228
+ { name: 'Service Layer', glob: 'src/**/*Service.js' },
229
+ { name: 'Service Layer', glob: 'src/**/*Service.ts' },
230
+ { name: 'Repository Pattern', glob: 'src/**/*Repository.js' },
231
+ { name: 'Repository Pattern', glob: 'src/**/*Repository.ts' },
232
+ { name: 'API Routes', glob: 'src/**/routes/**/*.{js,ts}' },
233
+ { name: 'API Routes', glob: 'src/commands/**/*.{js,ts}' },
234
+ { name: 'Unit Tests', glob: 'test/**/*.test.{js,ts}' },
235
+ { name: 'Unit Tests', glob: 'src/**/*.test.{js,ts}' },
236
+ { name: 'Unit Tests', glob: 'src/**/*.spec.{js,ts}' }
214
237
  ];
215
238
 
216
- for (const { name, glob: pattern } of checks) {
217
- const files = await glob(pattern, { cwd: projectPath, nodir: true });
239
+ const allChecks = [...dotnetChecks, ...jsChecks];
240
+ const seen = new Set();
241
+
242
+ for (const { name, glob: pattern } of allChecks) {
243
+ if (seen.has(name)) continue;
244
+ const files = await glob(pattern, { cwd: projectPath, nodir: true, ignore: globIgnore });
218
245
  if (files.length > 0) {
219
246
  patterns.push(name);
247
+ seen.add(name);
220
248
  }
221
249
  }
222
250
 
@@ -0,0 +1,304 @@
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
+ }