@polymorphism-tech/morph-spec 4.2.0 → 4.3.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/bin/morph-spec.js +283 -8
- package/bin/validate.js +4 -4
- package/docs/{v3.0 → next-generation}/AGENTS.md +1 -1
- package/docs/next-generation/CONTEXT-OPTIMIZATION.md +267 -0
- package/docs/next-generation/EXECUTION-FLOW.md +274 -0
- package/docs/next-generation/META-PROMPTS.md +235 -0
- package/docs/next-generation/MIGRATION-GUIDE.md +253 -0
- package/docs/next-generation/THREAD-MANAGEMENT.md +240 -0
- package/package.json +5 -5
- package/src/commands/agents/agents-fuse.js +96 -0
- package/src/commands/agents/micro-agent.js +112 -0
- package/src/commands/agents/spawn-team.js +69 -4
- package/src/commands/agents/squad-template.js +146 -0
- package/src/commands/analytics/analytics.js +176 -0
- package/src/commands/context/context-prime.js +63 -0
- package/src/commands/context/core-four.js +54 -0
- package/src/commands/mcp/mcp.js +102 -0
- package/src/commands/project/detect-agents.js +1 -1
- package/src/commands/project/doctor.js +573 -356
- package/src/commands/project/init.js +1 -1
- package/src/commands/project/update.js +1 -1
- package/src/commands/state/advance-phase.js +433 -416
- package/src/commands/templates/template-render.js +80 -1
- package/src/commands/threads/thread-template.js +103 -0
- package/src/commands/threads/threads.js +261 -0
- package/src/commands/trust/trust.js +205 -0
- package/src/{orchestrator.js → core/orchestrator.js} +8 -8
- package/src/core/state/state-manager.js +18 -2
- package/src/core/workflows/workflow-detector.js +100 -2
- package/src/lib/agents/micro-agent-factory.js +161 -0
- package/src/lib/analytics/analytics-engine.js +345 -0
- package/src/lib/checkpoints/checkpoint-hooks.js +293 -258
- package/src/lib/context/context-bundler.js +240 -0
- package/src/lib/context/context-optimizer.js +212 -0
- package/src/lib/context/context-tracker.js +273 -0
- package/src/lib/context/core-four-tracker.js +201 -0
- package/src/lib/context/mcp-optimizer.js +200 -0
- package/src/lib/execution/fusion-executor.js +304 -0
- package/src/lib/execution/parallel-executor.js +270 -0
- package/src/lib/generators/context-generator.js +3 -3
- package/src/lib/generators/recap-generator.js +2 -2
- package/src/lib/hooks/hook-executor.js +169 -0
- package/src/lib/hooks/stop-hook-executor.js +286 -0
- package/src/lib/hops/hop-composer.js +221 -0
- package/src/lib/threads/thread-coordinator.js +238 -0
- package/src/lib/threads/thread-manager.js +317 -0
- package/src/lib/tracking/artifact-trail.js +202 -0
- package/src/lib/trust/trust-manager.js +269 -0
- package/src/lib/validators/design-system/design-system-validator.js +2 -2
- package/src/lib/validators/validation-runner.js +6 -6
- package/stacks/blazor-azure/.morph/config/agents.json +72 -3
- package/stacks/nextjs-supabase/.morph/config/agents.json +3 -3
- package/CLAUDE.md +0 -993
- package/docs/llm-interaction-config.md +0 -735
- package/docs/v3.0/EXECUTION-FLOW.md +0 -1304
- package/src/commands/utils/migrate-state.js +0 -158
- package/src/commands/utils/upgrade.js +0 -346
- package/src/lib/validators/architecture-validator.js +0 -60
- package/src/lib/validators/content-validator.js +0 -164
- package/src/lib/validators/package-validator.js +0 -61
- package/src/lib/validators/ui-contrast-validator.js +0 -44
- package/stacks/blazor-azure/.claude/commands/morph-apply.md +0 -221
- package/stacks/blazor-azure/.claude/commands/morph-archive.md +0 -79
- package/stacks/blazor-azure/.claude/commands/morph-deploy.md +0 -529
- package/stacks/blazor-azure/.claude/commands/morph-infra.md +0 -209
- package/stacks/blazor-azure/.claude/commands/morph-preflight.md +0 -227
- package/stacks/blazor-azure/.claude/commands/morph-proposal.md +0 -122
- package/stacks/blazor-azure/.claude/commands/morph-status.md +0 -86
- package/stacks/blazor-azure/.claude/commands/morph-troubleshoot.md +0 -122
- package/stacks/blazor-azure/.claude/skills/level-0-meta/README.md +0 -7
- package/stacks/blazor-azure/.claude/skills/level-0-meta/code-review.md +0 -226
- package/stacks/blazor-azure/.claude/skills/level-0-meta/morph-checklist.md +0 -117
- package/stacks/blazor-azure/.claude/skills/level-0-meta/simulation-checklist.md +0 -77
- package/stacks/blazor-azure/.claude/skills/level-1-workflows/README.md +0 -7
- package/stacks/blazor-azure/.claude/skills/level-1-workflows/morph-replicate.md +0 -213
- package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-clarify.md +0 -131
- package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-design.md +0 -213
- package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-setup.md +0 -106
- package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-tasks.md +0 -164
- package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-uiux.md +0 -169
- package/stacks/blazor-azure/.claude/skills/level-2-domains/README.md +0 -14
- package/stacks/blazor-azure/.claude/skills/level-2-domains/ai-agents/ai-system-architect.md +0 -192
- package/stacks/blazor-azure/.claude/skills/level-2-domains/architecture/po-pm-advisor.md +0 -197
- package/stacks/blazor-azure/.claude/skills/level-2-domains/architecture/prompt-engineer.md +0 -189
- package/stacks/blazor-azure/.claude/skills/level-2-domains/architecture/seo-growth-hacker.md +0 -320
- package/stacks/blazor-azure/.claude/skills/level-2-domains/architecture/standards-architect.md +0 -156
- package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/api-designer.md +0 -59
- package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/dotnet-senior.md +0 -77
- package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/ef-modeler.md +0 -58
- package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/hangfire-orchestrator.md +0 -126
- package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/ms-agent-expert.md +0 -45
- package/stacks/blazor-azure/.claude/skills/level-2-domains/frontend/blazor-builder.md +0 -210
- package/stacks/blazor-azure/.claude/skills/level-2-domains/frontend/nextjs-expert.md +0 -154
- package/stacks/blazor-azure/.claude/skills/level-2-domains/frontend/ui-ux-designer.md +0 -191
- package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/azure-architect.md +0 -142
- package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/azure-deploy-specialist.md +0 -699
- package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/bicep-architect.md +0 -126
- package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/container-specialist.md +0 -131
- package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/devops-engineer.md +0 -119
- package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/asaas-financial.md +0 -130
- package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/azure-identity.md +0 -142
- package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/clerk-auth.md +0 -108
- package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/hangfire-orchestrator.md +0 -64
- package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/resend-email.md +0 -119
- package/stacks/blazor-azure/.claude/skills/level-2-domains/quality/code-analyzer.md +0 -235
- package/stacks/blazor-azure/.claude/skills/level-2-domains/quality/testing-specialist.md +0 -126
- package/stacks/blazor-azure/.claude/skills/level-3-technologies/README.md +0 -7
- package/stacks/blazor-azure/.claude/skills/level-4-patterns/README.md +0 -7
- package/stacks/blazor-azure/.morph/archive/.gitkeep +0 -25
- package/stacks/blazor-azure/.morph/features/.gitkeep +0 -25
- package/stacks/blazor-azure/.morph/schemas/agent.schema.json +0 -296
- package/stacks/blazor-azure/.morph/schemas/tasks.schema.json +0 -220
- package/stacks/blazor-azure/.morph/specs/.gitkeep +0 -20
- package/stacks/blazor-azure/.morph/test-infra/example.bicep +0 -59
- package/stacks/nextjs-supabase/.claude/commands/morph-apply.md +0 -221
- package/stacks/nextjs-supabase/.claude/commands/morph-archive.md +0 -79
- package/stacks/nextjs-supabase/.claude/commands/morph-deploy.md +0 -529
- package/stacks/nextjs-supabase/.claude/commands/morph-infra.md +0 -209
- package/stacks/nextjs-supabase/.claude/commands/morph-preflight.md +0 -227
- package/stacks/nextjs-supabase/.claude/commands/morph-proposal.md +0 -122
- package/stacks/nextjs-supabase/.claude/commands/morph-status.md +0 -86
- package/stacks/nextjs-supabase/.claude/commands/morph-troubleshoot.md +0 -122
- package/stacks/nextjs-supabase/.claude/settings.local.json +0 -6
- package/stacks/nextjs-supabase/.claude/skills/level-2-domains/backend/dotnet-supabase.md +0 -244
- package/stacks/nextjs-supabase/.claude/skills/level-2-domains/frontend/nextjs-supabase.md +0 -335
- package/stacks/nextjs-supabase/.claude/skills/level-2-domains/infrastructure/easypanel-deployer.md +0 -189
- package/stacks/nextjs-supabase/.claude/skills/level-2-domains/integrations/supabase-expert.md +0 -50
- /package/docs/{v3.0 → next-generation}/ANALYSIS.md +0 -0
- /package/docs/{v3.0 → next-generation}/ARCHITECTURE.md +0 -0
- /package/docs/{v3.0 → next-generation}/FEATURES.md +0 -0
- /package/docs/{v3.0 → next-generation}/README.md +0 -0
- /package/docs/{v3.0 → next-generation}/ROADMAP.md +0 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Bundler — Checkpoint compression (180K → 15K)
|
|
3
|
+
*
|
|
4
|
+
* Compresses checkpoint state into a compact bundle for session resumption.
|
|
5
|
+
* Saves bundles to .morph/bundles/{bundleId}.json
|
|
6
|
+
*
|
|
7
|
+
* A bundle contains:
|
|
8
|
+
* - decisions[]: key architectural decisions made
|
|
9
|
+
* - artifacts[]: paths to important generated files
|
|
10
|
+
* - nextSteps[]: what to implement next
|
|
11
|
+
* - metadata: original vs bundled token counts
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from 'fs';
|
|
15
|
+
import { join } from 'path';
|
|
16
|
+
import { randomUUID } from 'crypto';
|
|
17
|
+
|
|
18
|
+
const BUNDLES_DIR = join(process.cwd(), '.morph/bundles');
|
|
19
|
+
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// Bundle Creation
|
|
22
|
+
// ============================================================================
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Create a context bundle from checkpoint data
|
|
26
|
+
* @param {Object} opts
|
|
27
|
+
* @param {string} opts.feature - Feature name
|
|
28
|
+
* @param {number} opts.checkpointNum - Checkpoint number this bundle captures
|
|
29
|
+
* @param {Array} opts.decisions - Key decisions made: [{ id, title, summary }]
|
|
30
|
+
* @param {Array} opts.artifacts - Important file paths: [{ path, description }]
|
|
31
|
+
* @param {Array} opts.nextSteps - Next tasks to implement: [string]
|
|
32
|
+
* @param {number} [opts.originalTokens=180000] - Estimated original context size
|
|
33
|
+
* @param {Object} [opts.meta] - Additional metadata
|
|
34
|
+
* @returns {Object} Bundle object with ID
|
|
35
|
+
*/
|
|
36
|
+
export function createBundle({
|
|
37
|
+
feature,
|
|
38
|
+
checkpointNum,
|
|
39
|
+
decisions = [],
|
|
40
|
+
artifacts = [],
|
|
41
|
+
nextSteps = [],
|
|
42
|
+
originalTokens = 180000,
|
|
43
|
+
meta = {}
|
|
44
|
+
}) {
|
|
45
|
+
const bundleId = `${feature}-cp${checkpointNum}-${randomUUID().substring(0, 8)}`;
|
|
46
|
+
const now = new Date().toISOString();
|
|
47
|
+
|
|
48
|
+
// Estimate bundle size (decisions + artifacts + nextSteps summary)
|
|
49
|
+
const bundledContent = JSON.stringify({ decisions, artifacts, nextSteps, meta });
|
|
50
|
+
const bundledTokens = Math.round(bundledContent.length / 4); // ~4 chars/token
|
|
51
|
+
|
|
52
|
+
const bundle = {
|
|
53
|
+
bundleId,
|
|
54
|
+
feature,
|
|
55
|
+
checkpointNum,
|
|
56
|
+
createdAt: now,
|
|
57
|
+
decisions,
|
|
58
|
+
artifacts,
|
|
59
|
+
nextSteps,
|
|
60
|
+
meta,
|
|
61
|
+
tokenMetrics: {
|
|
62
|
+
originalTokens,
|
|
63
|
+
bundledTokens,
|
|
64
|
+
compressionRatio: Math.round(originalTokens / Math.max(bundledTokens, 1)),
|
|
65
|
+
savingsTokens: originalTokens - bundledTokens
|
|
66
|
+
},
|
|
67
|
+
resumeInstructions: buildResumeInstructions(feature, checkpointNum, decisions, nextSteps)
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
return bundle;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Save a bundle to disk
|
|
75
|
+
* @param {Object} bundle - Bundle object (from createBundle)
|
|
76
|
+
* @returns {string} Path to saved bundle file
|
|
77
|
+
*/
|
|
78
|
+
export function saveBundle(bundle) {
|
|
79
|
+
if (!existsSync(BUNDLES_DIR)) {
|
|
80
|
+
mkdirSync(BUNDLES_DIR, { recursive: true });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const filePath = join(BUNDLES_DIR, `${bundle.bundleId}.json`);
|
|
84
|
+
writeFileSync(filePath, JSON.stringify(bundle, null, 2), 'utf8');
|
|
85
|
+
|
|
86
|
+
// Also update feature state with bundle reference
|
|
87
|
+
try {
|
|
88
|
+
const statePath = join(process.cwd(), '.morph/state.json');
|
|
89
|
+
if (existsSync(statePath)) {
|
|
90
|
+
const state = JSON.parse(readFileSync(statePath, 'utf8'));
|
|
91
|
+
if (state.features?.[bundle.feature]) {
|
|
92
|
+
state.features[bundle.feature].contextBundles = state.features[bundle.feature].contextBundles || [];
|
|
93
|
+
state.features[bundle.feature].contextBundles.push({
|
|
94
|
+
bundleId: bundle.bundleId,
|
|
95
|
+
checkpointNum: bundle.checkpointNum,
|
|
96
|
+
createdAt: bundle.createdAt,
|
|
97
|
+
originalTokens: bundle.tokenMetrics.originalTokens,
|
|
98
|
+
bundledTokens: bundle.tokenMetrics.bundledTokens,
|
|
99
|
+
path: filePath
|
|
100
|
+
});
|
|
101
|
+
state.metadata = state.metadata || {};
|
|
102
|
+
state.metadata.lastUpdated = new Date().toISOString();
|
|
103
|
+
writeFileSync(statePath, JSON.stringify(state, null, 2), 'utf8');
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
} catch {
|
|
107
|
+
// Non-fatal if state update fails
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return filePath;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Load a bundle from disk
|
|
115
|
+
* @param {string} bundleId - Bundle ID
|
|
116
|
+
* @returns {Object|null} Bundle object or null if not found
|
|
117
|
+
*/
|
|
118
|
+
export function loadBundle(bundleId) {
|
|
119
|
+
const filePath = join(BUNDLES_DIR, `${bundleId}.json`);
|
|
120
|
+
|
|
121
|
+
if (!existsSync(filePath)) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
return JSON.parse(readFileSync(filePath, 'utf8'));
|
|
127
|
+
} catch {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* List all bundles, optionally filtered by feature
|
|
134
|
+
* @param {string} [feature] - Filter by feature name
|
|
135
|
+
* @returns {Array} Array of bundle summaries
|
|
136
|
+
*/
|
|
137
|
+
export function listBundles(feature = null) {
|
|
138
|
+
if (!existsSync(BUNDLES_DIR)) return [];
|
|
139
|
+
|
|
140
|
+
const files = readdirSync(BUNDLES_DIR).filter(f => f.endsWith('.json'));
|
|
141
|
+
const bundles = [];
|
|
142
|
+
|
|
143
|
+
for (const file of files) {
|
|
144
|
+
try {
|
|
145
|
+
const bundle = JSON.parse(readFileSync(join(BUNDLES_DIR, file), 'utf8'));
|
|
146
|
+
if (!feature || bundle.feature === feature) {
|
|
147
|
+
bundles.push({
|
|
148
|
+
bundleId: bundle.bundleId,
|
|
149
|
+
feature: bundle.feature,
|
|
150
|
+
checkpointNum: bundle.checkpointNum,
|
|
151
|
+
createdAt: bundle.createdAt,
|
|
152
|
+
originalTokens: bundle.tokenMetrics?.originalTokens,
|
|
153
|
+
bundledTokens: bundle.tokenMetrics?.bundledTokens,
|
|
154
|
+
compressionRatio: bundle.tokenMetrics?.compressionRatio,
|
|
155
|
+
nextStepsCount: bundle.nextSteps?.length || 0
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
} catch {
|
|
159
|
+
// Skip malformed files
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return bundles.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Get the latest bundle for a feature at or before a checkpoint number
|
|
168
|
+
* @param {string} feature - Feature name
|
|
169
|
+
* @param {number} [checkpointNum] - Target checkpoint (latest if not specified)
|
|
170
|
+
* @returns {Object|null} Bundle or null
|
|
171
|
+
*/
|
|
172
|
+
export function getLatestBundle(feature, checkpointNum = Infinity) {
|
|
173
|
+
const bundles = listBundles(feature)
|
|
174
|
+
.filter(b => b.checkpointNum <= checkpointNum)
|
|
175
|
+
.sort((a, b) => b.checkpointNum - a.checkpointNum);
|
|
176
|
+
|
|
177
|
+
if (bundles.length === 0) return null;
|
|
178
|
+
return loadBundle(bundles[0].bundleId);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// ============================================================================
|
|
182
|
+
// Session Replay
|
|
183
|
+
// ============================================================================
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Generate minimal context for session replay from a bundle
|
|
187
|
+
* @param {Object} bundle - Bundle object
|
|
188
|
+
* @returns {string} Compact context string (~15K tokens) for session resumption
|
|
189
|
+
*/
|
|
190
|
+
export function generateResumeContext(bundle) {
|
|
191
|
+
const lines = [];
|
|
192
|
+
lines.push(`# Session Resume — ${bundle.feature} @ Checkpoint ${bundle.checkpointNum}`);
|
|
193
|
+
lines.push(`> Bundle ID: ${bundle.bundleId} | Created: ${bundle.createdAt}`);
|
|
194
|
+
lines.push(`> Context compression: ${bundle.tokenMetrics?.originalTokens?.toLocaleString()} → ${bundle.tokenMetrics?.bundledTokens?.toLocaleString()} tokens`);
|
|
195
|
+
lines.push('');
|
|
196
|
+
|
|
197
|
+
if (bundle.decisions?.length > 0) {
|
|
198
|
+
lines.push('## Key Decisions Made');
|
|
199
|
+
for (const d of bundle.decisions) {
|
|
200
|
+
lines.push(`- **${d.id || ''}${d.id ? ':' : ''} ${d.title}** — ${d.summary}`);
|
|
201
|
+
if (d.fullPath) lines.push(` *(Full: ${d.fullPath})*`);
|
|
202
|
+
}
|
|
203
|
+
lines.push('');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (bundle.artifacts?.length > 0) {
|
|
207
|
+
lines.push('## Created Artifacts');
|
|
208
|
+
for (const a of bundle.artifacts) {
|
|
209
|
+
lines.push(`- \`${a.path}\` — ${a.description || ''}`);
|
|
210
|
+
}
|
|
211
|
+
lines.push('');
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (bundle.nextSteps?.length > 0) {
|
|
215
|
+
lines.push('## Next Steps (continue from here)');
|
|
216
|
+
bundle.nextSteps.forEach((step, i) => {
|
|
217
|
+
lines.push(`${i + 1}. ${step}`);
|
|
218
|
+
});
|
|
219
|
+
lines.push('');
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
lines.push(bundle.resumeInstructions || '');
|
|
223
|
+
|
|
224
|
+
return lines.join('\n');
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// ============================================================================
|
|
228
|
+
// Helpers
|
|
229
|
+
// ============================================================================
|
|
230
|
+
|
|
231
|
+
function buildResumeInstructions(feature, checkpointNum, decisions, nextSteps) {
|
|
232
|
+
return `## Resume Instructions
|
|
233
|
+
|
|
234
|
+
1. Load minimal standards: run \`morph-spec prime feature\`
|
|
235
|
+
2. Read spec: \`.morph/project/outputs/${feature}/spec.md\` (key sections only)
|
|
236
|
+
3. Check tasks: \`morph-spec state get ${feature}\`
|
|
237
|
+
4. Continue from: ${nextSteps[0] || `task after checkpoint ${checkpointNum}`}
|
|
238
|
+
|
|
239
|
+
> This bundle was created at checkpoint ${checkpointNum}. All decisions above are final.`;
|
|
240
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Optimizer — Selective context loading strategy
|
|
3
|
+
*
|
|
4
|
+
* Provides intelligent recommendations for reducing context window usage:
|
|
5
|
+
* - selectStandards: pick only relevant standards for a feature type
|
|
6
|
+
* - selectTemplates: pick only relevant templates
|
|
7
|
+
* - suggestOptimizations: compare current vs optimal context, return savings
|
|
8
|
+
*
|
|
9
|
+
* Connects to standards resolver and template registry.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { readFileSync, existsSync } from 'fs';
|
|
13
|
+
import { join } from 'path';
|
|
14
|
+
|
|
15
|
+
const STANDARDS_DIR = join(process.cwd(), 'framework/standards');
|
|
16
|
+
const TEMPLATES_REGISTRY = join(process.cwd(), 'framework/templates/REGISTRY.json');
|
|
17
|
+
|
|
18
|
+
// Average token cost estimates per file type
|
|
19
|
+
const TOKEN_ESTIMATES = {
|
|
20
|
+
standard_file: 2000,
|
|
21
|
+
template_file: 1500,
|
|
22
|
+
claude_md: 23000,
|
|
23
|
+
spec_file: 8000,
|
|
24
|
+
decisions_file: 3000
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// Feature type → relevant standards mapping
|
|
28
|
+
const STANDARDS_BY_FEATURE_TYPE = {
|
|
29
|
+
backend: [
|
|
30
|
+
'coding.md', 'architecture.md', 'dotnet10-compatibility.md',
|
|
31
|
+
'integration/api/rest-design.md'
|
|
32
|
+
],
|
|
33
|
+
frontend: [
|
|
34
|
+
'coding.md', 'blazor-components.md', 'blazor-state.md',
|
|
35
|
+
'css-architecture.md', 'css-design-system.md'
|
|
36
|
+
],
|
|
37
|
+
database: [
|
|
38
|
+
'coding.md', 'ef-core.md', 'architecture.md'
|
|
39
|
+
],
|
|
40
|
+
infrastructure: [
|
|
41
|
+
'azure.md', 'bicep.md', 'devops.md'
|
|
42
|
+
],
|
|
43
|
+
authentication: [
|
|
44
|
+
'coding.md', 'architecture.md', 'dotnet10-compatibility.md',
|
|
45
|
+
'integration/api/rest-design.md'
|
|
46
|
+
],
|
|
47
|
+
fullstack: [
|
|
48
|
+
'coding.md', 'architecture.md', 'blazor-components.md',
|
|
49
|
+
'ef-core.md', 'dotnet10-compatibility.md'
|
|
50
|
+
],
|
|
51
|
+
observability: [
|
|
52
|
+
'observability/monitoring.md', 'observability/logging.md',
|
|
53
|
+
'observability/tracing.md', 'observability/metrics.md'
|
|
54
|
+
],
|
|
55
|
+
event_driven: [
|
|
56
|
+
'integration/event-driven/service-bus.md',
|
|
57
|
+
'integration/event-driven/cqrs.md'
|
|
58
|
+
]
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// ============================================================================
|
|
62
|
+
// Standards Selection
|
|
63
|
+
// ============================================================================
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Select minimal relevant standards for a given feature type
|
|
67
|
+
* @param {string} featureType - Feature type keyword
|
|
68
|
+
* @param {Object} [opts]
|
|
69
|
+
* @param {string[]} [opts.extraKeywords] - Additional keywords for selection
|
|
70
|
+
* @returns {Object} { selected: string[], skipped: string[], estimatedTokens: number }
|
|
71
|
+
*/
|
|
72
|
+
export function selectStandards(featureType, { extraKeywords = [] } = {}) {
|
|
73
|
+
const typeKey = detectFeatureCategory(featureType, extraKeywords);
|
|
74
|
+
const selected = STANDARDS_BY_FEATURE_TYPE[typeKey] || STANDARDS_BY_FEATURE_TYPE.fullstack;
|
|
75
|
+
|
|
76
|
+
// Check which selected files actually exist
|
|
77
|
+
const existing = selected.filter(f => {
|
|
78
|
+
const fullPath = join(STANDARDS_DIR, f);
|
|
79
|
+
return existsSync(fullPath);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Estimate all standard files count for skipped calculation
|
|
83
|
+
const allStandards = getAllStandardsCount();
|
|
84
|
+
const skippedCount = allStandards - existing.length;
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
selected: existing,
|
|
88
|
+
skipped: skippedCount,
|
|
89
|
+
featureType: typeKey,
|
|
90
|
+
estimatedTokens: existing.length * TOKEN_ESTIMATES.standard_file,
|
|
91
|
+
fullLoadTokens: allStandards * TOKEN_ESTIMATES.standard_file,
|
|
92
|
+
savings: skippedCount * TOKEN_ESTIMATES.standard_file
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Select relevant templates for a feature type
|
|
98
|
+
* @param {string} featureType - Feature type keyword
|
|
99
|
+
* @returns {Object} { selected: string[], estimatedTokens: number }
|
|
100
|
+
*/
|
|
101
|
+
export function selectTemplates(featureType) {
|
|
102
|
+
const TEMPLATES_BY_TYPE = {
|
|
103
|
+
backend: ['dotnet-backend-service', 'dotnet-repository', 'dotnet-api-contracts'],
|
|
104
|
+
frontend: ['dotnet-blazor-component', 'dotnet-design-system'],
|
|
105
|
+
database: ['dotnet-migration', 'sql-migration'],
|
|
106
|
+
infrastructure: ['azure-container-app', 'azure-key-vault', 'azure-main-bicep'],
|
|
107
|
+
fullstack: ['dotnet-backend-service', 'dotnet-blazor-component', 'dotnet-migration']
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const typeKey = detectFeatureCategory(featureType, []);
|
|
111
|
+
const selected = TEMPLATES_BY_TYPE[typeKey] || TEMPLATES_BY_TYPE.fullstack;
|
|
112
|
+
|
|
113
|
+
// Load registry to get full template count
|
|
114
|
+
let totalTemplates = 50;
|
|
115
|
+
try {
|
|
116
|
+
const registry = JSON.parse(readFileSync(TEMPLATES_REGISTRY, 'utf8'));
|
|
117
|
+
totalTemplates = (registry.templates || []).length;
|
|
118
|
+
} catch {
|
|
119
|
+
// Use estimate
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
selected,
|
|
124
|
+
skipped: totalTemplates - selected.length,
|
|
125
|
+
estimatedTokens: selected.length * TOKEN_ESTIMATES.template_file,
|
|
126
|
+
fullLoadTokens: totalTemplates * TOKEN_ESTIMATES.template_file,
|
|
127
|
+
savings: (totalTemplates - selected.length) * TOKEN_ESTIMATES.template_file
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Suggest context optimizations based on current vs optimal load
|
|
133
|
+
* @param {Object} current - Current context state
|
|
134
|
+
* @param {number} current.totalTokens - Current token count
|
|
135
|
+
* @param {number} current.standardsCount - Number of standards currently loaded
|
|
136
|
+
* @param {number} current.templatesCount - Number of templates currently loaded
|
|
137
|
+
* @param {string} featureType - Feature type for optimal selection
|
|
138
|
+
* @returns {Object} Optimization suggestions with savings estimates
|
|
139
|
+
*/
|
|
140
|
+
export function suggestOptimizations(current, featureType) {
|
|
141
|
+
const optimalStandards = selectStandards(featureType);
|
|
142
|
+
const optimalTemplates = selectTemplates(featureType);
|
|
143
|
+
const suggestions = [];
|
|
144
|
+
|
|
145
|
+
// Claude.md optimization
|
|
146
|
+
if (current.totalTokens > 50000) {
|
|
147
|
+
suggestions.push({
|
|
148
|
+
type: 'priming_file',
|
|
149
|
+
title: 'Use priming file instead of full CLAUDE.md',
|
|
150
|
+
description: 'Replace 23K token CLAUDE.md with a 500-token priming file',
|
|
151
|
+
savings: TOKEN_ESTIMATES.claude_md - 500,
|
|
152
|
+
priority: 'high',
|
|
153
|
+
command: 'morph-spec prime feature'
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Standards optimization
|
|
158
|
+
if (current.standardsCount > optimalStandards.selected.length + 3) {
|
|
159
|
+
suggestions.push({
|
|
160
|
+
type: 'standards_subset',
|
|
161
|
+
title: `Load only ${optimalStandards.selected.length} relevant standards`,
|
|
162
|
+
description: `Currently loading ${current.standardsCount} standards. Only ${optimalStandards.selected.length} are needed for ${featureType} features.`,
|
|
163
|
+
savings: optimalStandards.savings,
|
|
164
|
+
priority: 'medium',
|
|
165
|
+
selectedStandards: optimalStandards.selected
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Context bundle for large sessions
|
|
170
|
+
if (current.totalTokens > 100000) {
|
|
171
|
+
suggestions.push({
|
|
172
|
+
type: 'context_bundle',
|
|
173
|
+
title: 'Create context bundle checkpoint',
|
|
174
|
+
description: 'Compress current context (180K → ~15K tokens) for session resume',
|
|
175
|
+
savings: current.totalTokens - 15000,
|
|
176
|
+
priority: 'critical',
|
|
177
|
+
command: 'morph-spec bundle create'
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const totalSavings = suggestions.reduce((sum, s) => sum + s.savings, 0);
|
|
182
|
+
const optimizedTokens = Math.max(current.totalTokens - totalSavings, 5000);
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
currentTokens: current.totalTokens,
|
|
186
|
+
optimizedTokens,
|
|
187
|
+
totalSavings,
|
|
188
|
+
savingsPercent: Math.round(totalSavings / current.totalTokens * 100),
|
|
189
|
+
suggestions: suggestions.sort((a, b) => b.savings - a.savings)
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// ============================================================================
|
|
194
|
+
// Helpers
|
|
195
|
+
// ============================================================================
|
|
196
|
+
|
|
197
|
+
function detectFeatureCategory(featureType, extraKeywords) {
|
|
198
|
+
const text = (featureType + ' ' + extraKeywords.join(' ')).toLowerCase();
|
|
199
|
+
|
|
200
|
+
if (/auth|login|jwt|oauth|identity/.test(text)) return 'authentication';
|
|
201
|
+
if (/blazor|frontend|ui|component|razor/.test(text)) return 'frontend';
|
|
202
|
+
if (/migration|ef.core|database|entity/.test(text)) return 'database';
|
|
203
|
+
if (/bicep|azure|infra|container|devops/.test(text)) return 'infrastructure';
|
|
204
|
+
if (/service.bus|cqrs|event.sourcing|pub.sub/.test(text)) return 'event_driven';
|
|
205
|
+
if (/monitor|logging|trace|metric|appinsight/.test(text)) return 'observability';
|
|
206
|
+
if (/api|endpoint|controller|rest/.test(text)) return 'backend';
|
|
207
|
+
return 'fullstack';
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function getAllStandardsCount() {
|
|
211
|
+
return 30; // Known approximate count
|
|
212
|
+
}
|