@polymorphism-tech/morph-spec 3.1.0 → 3.2.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 +534 -0
- package/README.md +78 -4
- package/bin/morph-spec.js +50 -1
- package/bin/render-template.js +56 -10
- package/bin/task-manager.cjs +101 -7
- package/docs/cli-auto-detection.md +219 -0
- package/docs/llm-interaction-config.md +735 -0
- package/docs/troubleshooting.md +269 -0
- package/package.json +5 -1
- package/src/commands/advance-phase.js +93 -2
- package/src/commands/approve.js +221 -0
- package/src/commands/capture-pattern.js +121 -0
- package/src/commands/generate.js +128 -1
- package/src/commands/init.js +37 -0
- package/src/commands/migrate-state.js +158 -0
- package/src/commands/search-patterns.js +126 -0
- package/src/commands/spawn-team.js +172 -0
- package/src/commands/task.js +2 -2
- package/src/commands/update.js +36 -0
- package/src/commands/upgrade.js +346 -0
- package/src/generator/.gitkeep +0 -0
- package/src/generator/config-generator.js +206 -0
- package/src/generator/templates/config.json.template +40 -0
- package/src/generator/templates/project.md.template +67 -0
- package/src/lib/checkpoint-hooks.js +258 -0
- package/src/lib/metadata-extractor.js +380 -0
- package/src/lib/phase-state-machine.js +214 -0
- package/src/lib/state-manager.js +120 -0
- package/src/lib/template-data-sources.js +325 -0
- package/src/lib/validators/content-validator.js +351 -0
- package/src/llm/.gitkeep +0 -0
- package/src/llm/analyzer.js +215 -0
- package/src/llm/environment-detector.js +43 -0
- package/src/llm/few-shot-examples.js +216 -0
- package/src/llm/project-config-schema.json +188 -0
- package/src/llm/prompt-builder.js +96 -0
- package/src/llm/schema-validator.js +121 -0
- package/src/orchestrator.js +206 -0
- package/src/sanitizer/.gitkeep +0 -0
- package/src/sanitizer/context-sanitizer.js +221 -0
- package/src/sanitizer/patterns.js +163 -0
- package/src/scanner/.gitkeep +0 -0
- package/src/scanner/project-scanner.js +242 -0
- package/src/types/index.js +477 -0
- package/src/ui/.gitkeep +0 -0
- package/src/ui/diff-display.js +91 -0
- package/src/ui/interactive-wizard.js +96 -0
- package/src/ui/user-review.js +211 -0
- package/src/ui/wizard-questions.js +190 -0
- package/src/writer/.gitkeep +0 -0
- package/src/writer/file-writer.js +86 -0
package/bin/morph-spec.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { program } from 'commander';
|
|
4
4
|
import chalk from 'chalk';
|
|
@@ -32,6 +32,13 @@ import { validatePhaseCommand } from '../src/commands/validate-phase.js';
|
|
|
32
32
|
import { sessionSummaryCommand } from '../src/commands/session-summary.js';
|
|
33
33
|
import { rollbackPhaseCommand } from '../src/commands/rollback-phase.js';
|
|
34
34
|
import { advancePhaseCommand } from '../src/commands/advance-phase.js';
|
|
35
|
+
import { approveCommand, rejectCommand, approvalStatusCommand } from '../src/commands/approve.js';
|
|
36
|
+
import { spawnTeamCommand } from '../src/commands/spawn-team.js';
|
|
37
|
+
import capturePatternProgram from '../src/commands/capture-pattern.js';
|
|
38
|
+
import searchPatternsProgram from '../src/commands/search-patterns.js';
|
|
39
|
+
import { generateMetadataCommand } from '../src/commands/generate.js';
|
|
40
|
+
import migrateStateProgram from '../src/commands/migrate-state.js';
|
|
41
|
+
import upgradeProgram from '../src/commands/upgrade.js';
|
|
35
42
|
|
|
36
43
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
37
44
|
const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
|
|
@@ -189,6 +196,13 @@ generateCommand
|
|
|
189
196
|
await generateContextCommand('.', feature);
|
|
190
197
|
});
|
|
191
198
|
|
|
199
|
+
generateCommand
|
|
200
|
+
.command('metadata <feature>')
|
|
201
|
+
.description('Generate metadata.json summary from markdown outputs')
|
|
202
|
+
.option('--output <path>', 'Custom output path')
|
|
203
|
+
.option('--verbose', 'Show detailed extraction info')
|
|
204
|
+
.action((feature, options) => generateMetadataCommand(feature, options));
|
|
205
|
+
|
|
192
206
|
// Validation commands (Sprint 4: Continuous Validation)
|
|
193
207
|
program
|
|
194
208
|
.command('validate [validator]')
|
|
@@ -365,6 +379,41 @@ program
|
|
|
365
379
|
.option('-v, --verbose', 'Show detailed output')
|
|
366
380
|
.action(rollbackPhaseCommand);
|
|
367
381
|
|
|
382
|
+
// Approval workflow commands (MORPH-SPEC 3.0)
|
|
383
|
+
program
|
|
384
|
+
.command('approve <feature> <gate>')
|
|
385
|
+
.description('Approve a phase gate (design, tasks, uiux, proposal)')
|
|
386
|
+
.option('--approver <name>', 'Name of approver (default: current user)')
|
|
387
|
+
.option('--notes <notes>', 'Approval notes')
|
|
388
|
+
.action((feature, gate, options) => approveCommand(feature, gate, options));
|
|
389
|
+
|
|
390
|
+
program
|
|
391
|
+
.command('reject <feature> <gate> [reason]')
|
|
392
|
+
.description('Reject a phase gate with optional reason')
|
|
393
|
+
.action((feature, gate, reason, options) => rejectCommand(feature, gate, reason, options));
|
|
394
|
+
|
|
395
|
+
program
|
|
396
|
+
.command('approval-status <feature>')
|
|
397
|
+
.description('Show approval status for all gates')
|
|
398
|
+
.option('--json', 'Output as JSON')
|
|
399
|
+
.action((feature, options) => approvalStatusCommand(feature, options));
|
|
400
|
+
|
|
401
|
+
// Agent team spawning (MORPH-SPEC 3.0)
|
|
402
|
+
program
|
|
403
|
+
.command('spawn-team <feature>')
|
|
404
|
+
.description('Generate agent team configuration for Task tool')
|
|
405
|
+
.option('--json', 'Output as JSON')
|
|
406
|
+
.option('--verbose', 'Show detailed team configuration')
|
|
407
|
+
.action((feature, options) => spawnTeamCommand(feature, options));
|
|
408
|
+
|
|
409
|
+
// Pattern learning system (MORPH-SPEC 3.0) - Add as subcommands
|
|
410
|
+
program.addCommand(capturePatternProgram);
|
|
411
|
+
program.addCommand(searchPatternsProgram);
|
|
412
|
+
|
|
413
|
+
// Migration and upgrade commands (MORPH-SPEC 3.0) - Add as subcommands
|
|
414
|
+
program.addCommand(migrateStateProgram);
|
|
415
|
+
program.addCommand(upgradeProgram);
|
|
416
|
+
|
|
368
417
|
// Wire global --stack flag to stack-resolver before any command executes
|
|
369
418
|
program.hook('preAction', () => {
|
|
370
419
|
const opts = program.opts();
|
package/bin/render-template.js
CHANGED
|
@@ -19,6 +19,7 @@ import fs from 'fs';
|
|
|
19
19
|
import path from 'path';
|
|
20
20
|
import { fileURLToPath } from 'url';
|
|
21
21
|
import { resolveConfigDir } from '../src/lib/stack-resolver.js';
|
|
22
|
+
import { getAllTemplatePlaceholders } from '../src/lib/template-data-sources.js';
|
|
22
23
|
|
|
23
24
|
const __filename = fileURLToPath(import.meta.url);
|
|
24
25
|
const __dirname = path.dirname(__filename);
|
|
@@ -111,7 +112,7 @@ function transformCase(str) {
|
|
|
111
112
|
/**
|
|
112
113
|
* Render template by replacing placeholders
|
|
113
114
|
*/
|
|
114
|
-
function renderTemplate(templatePath, variables) {
|
|
115
|
+
function renderTemplate(templatePath, variables, mcpData = null) {
|
|
115
116
|
if (!fs.existsSync(templatePath)) {
|
|
116
117
|
throw new Error(`Template file not found: ${templatePath}`);
|
|
117
118
|
}
|
|
@@ -121,8 +122,8 @@ function renderTemplate(templatePath, variables) {
|
|
|
121
122
|
// Get default variables
|
|
122
123
|
const defaults = getDefaultVariables();
|
|
123
124
|
|
|
124
|
-
// Merge
|
|
125
|
-
const allVariables = { ...defaults, ...variables };
|
|
125
|
+
// Merge: defaults → MCP data → provided variables (rightmost takes precedence)
|
|
126
|
+
const allVariables = { ...defaults, ...(mcpData || {}), ...variables };
|
|
126
127
|
|
|
127
128
|
// Auto-generate case transformations for FEATURE_NAME if provided
|
|
128
129
|
if (allVariables.FEATURE_NAME) {
|
|
@@ -205,9 +206,29 @@ ${colors.green}Example:${colors.reset}
|
|
|
205
206
|
'{"FEATURE_NAME":"scheduled-reports","STACK":"Blazor"}'
|
|
206
207
|
|
|
207
208
|
${colors.green}Flags:${colors.reset}
|
|
208
|
-
--help, -h
|
|
209
|
-
--verbose, -v
|
|
210
|
-
--dry-run, -d
|
|
209
|
+
--help, -h Show this help message
|
|
210
|
+
--verbose, -v Show detailed replacement information
|
|
211
|
+
--dry-run, -d Preview output without writing file
|
|
212
|
+
--with-mcp-data Inject dynamic MCP data (project stats, compliance, activity)
|
|
213
|
+
|
|
214
|
+
${colors.green}MCP Data Placeholders:${colors.reset} (Available with --with-mcp-data flag)
|
|
215
|
+
{{MCP_PROJECT_FILES}} - Total files in project
|
|
216
|
+
{{MCP_TEST_COVERAGE}} - Test coverage percentage (or N/A)
|
|
217
|
+
{{MCP_COMPLIANCE_SCORE}} - Overall compliance score (0-100)
|
|
218
|
+
{{MCP_LAST_FEATURE}} - Name of last feature worked on
|
|
219
|
+
{{MCP_LAST_COMMIT}} - Last git commit message
|
|
220
|
+
{{MCP_LAST_COMMIT_AUTHOR}} - Author of last commit
|
|
221
|
+
{{MCP_LAST_COMMIT_TIME}} - Relative time of last commit
|
|
222
|
+
{{MCP_CS_FILES}} - Number of .cs files
|
|
223
|
+
{{MCP_RAZOR_FILES}} - Number of .razor files
|
|
224
|
+
{{MCP_TS_FILES}} - Number of .ts files
|
|
225
|
+
{{MCP_JS_FILES}} - Number of .js files
|
|
226
|
+
{{MCP_NUGET_PACKAGES}} - Number of NuGet packages
|
|
227
|
+
{{MCP_NPM_PACKAGES}} - Number of npm packages
|
|
228
|
+
{{MCP_ARCHITECTURE_VIOLATIONS}} - Architecture validator errors
|
|
229
|
+
{{MCP_PACKAGE_CONFLICTS}} - Package conflict count
|
|
230
|
+
{{MCP_SECURITY_ISSUES}} - Security validator issues
|
|
231
|
+
{{MCP_RECENT_BRANCHES}} - Recent git branches (up to 3)
|
|
211
232
|
|
|
212
233
|
${colors.yellow}Note:${colors.reset} DATE, YEAR, AUTHOR, PROJECT_NAME, STACK, NAMESPACE are auto-populated from config.json
|
|
213
234
|
`);
|
|
@@ -216,7 +237,7 @@ ${colors.yellow}Note:${colors.reset} DATE, YEAR, AUTHOR, PROJECT_NAME, STACK, NA
|
|
|
216
237
|
/**
|
|
217
238
|
* Main CLI logic
|
|
218
239
|
*/
|
|
219
|
-
function main() {
|
|
240
|
+
async function main() {
|
|
220
241
|
const args = process.argv.slice(2);
|
|
221
242
|
|
|
222
243
|
// Check for help flag
|
|
@@ -228,9 +249,10 @@ function main() {
|
|
|
228
249
|
// Parse flags
|
|
229
250
|
const verbose = args.includes('--verbose') || args.includes('-v');
|
|
230
251
|
const dryRun = args.includes('--dry-run') || args.includes('-d');
|
|
252
|
+
const withMcpData = args.includes('--with-mcp-data');
|
|
231
253
|
|
|
232
254
|
// Filter out flags
|
|
233
|
-
const flags = ['--verbose', '-v', '--dry-run', '-d', '--help', '-h'];
|
|
255
|
+
const flags = ['--verbose', '-v', '--dry-run', '-d', '--help', '-h', '--with-mcp-data'];
|
|
234
256
|
const cleanArgs = args.filter(arg => !flags.includes(arg));
|
|
235
257
|
|
|
236
258
|
// Validate arguments
|
|
@@ -253,10 +275,31 @@ function main() {
|
|
|
253
275
|
}
|
|
254
276
|
|
|
255
277
|
try {
|
|
278
|
+
// Get MCP data if requested
|
|
279
|
+
let mcpData = null;
|
|
280
|
+
if (withMcpData) {
|
|
281
|
+
try {
|
|
282
|
+
console.log(`${colors.cyan}🔍 Gathering MCP data from project...${colors.reset}`);
|
|
283
|
+
mcpData = await getAllTemplatePlaceholders(process.cwd());
|
|
284
|
+
|
|
285
|
+
if (verbose) {
|
|
286
|
+
console.log(`${colors.green}✅ MCP data loaded:${colors.reset}`);
|
|
287
|
+
console.log(` Files: ${mcpData.MCP_PROJECT_FILES}`);
|
|
288
|
+
console.log(` Coverage: ${mcpData.MCP_TEST_COVERAGE}`);
|
|
289
|
+
console.log(` Compliance: ${mcpData.MCP_COMPLIANCE_SCORE}%`);
|
|
290
|
+
console.log(` Last Feature: ${mcpData.MCP_LAST_FEATURE}\n`);
|
|
291
|
+
}
|
|
292
|
+
} catch (error) {
|
|
293
|
+
console.error(`${colors.yellow}⚠️ Warning: Failed to load MCP data - ${error.message}${colors.reset}`);
|
|
294
|
+
console.error(` Continuing with standard placeholders only...\n`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
256
298
|
// Render template
|
|
257
299
|
const { content, replacedPlaceholders, unreplacedPlaceholders } = renderTemplate(
|
|
258
300
|
templatePath,
|
|
259
|
-
variables
|
|
301
|
+
variables,
|
|
302
|
+
mcpData
|
|
260
303
|
);
|
|
261
304
|
|
|
262
305
|
// Dry run: just preview
|
|
@@ -300,4 +343,7 @@ function main() {
|
|
|
300
343
|
}
|
|
301
344
|
|
|
302
345
|
// Run CLI
|
|
303
|
-
main()
|
|
346
|
+
main().catch(error => {
|
|
347
|
+
console.error(`${colors.red}❌ Fatal error: ${error.message}${colors.reset}`);
|
|
348
|
+
process.exit(1);
|
|
349
|
+
});
|
package/bin/task-manager.cjs
CHANGED
|
@@ -138,6 +138,9 @@ class TaskManager {
|
|
|
138
138
|
// Save state
|
|
139
139
|
await this.saveState(state);
|
|
140
140
|
|
|
141
|
+
// Auto-generate metadata.json (for quick LLM access)
|
|
142
|
+
await this.generateMetadata(featureName, feature);
|
|
143
|
+
|
|
141
144
|
// Display progress
|
|
142
145
|
this.displayProgress(feature);
|
|
143
146
|
|
|
@@ -230,10 +233,40 @@ class TaskManager {
|
|
|
230
233
|
* Auto-checkpoint every 3 tasks (includes validation summary)
|
|
231
234
|
*/
|
|
232
235
|
async autoCheckpoint(feature, tasks, featureName) {
|
|
236
|
+
const tasksCompleted = feature.tasks?.completed || tasks.length;
|
|
237
|
+
const checkpointNum = Math.floor(tasksCompleted / 3);
|
|
238
|
+
|
|
239
|
+
console.log(chalk.magenta(`\n🔍 CHECKPOINT ${checkpointNum} - Running automated validation...`));
|
|
240
|
+
|
|
241
|
+
let checkpointResult = null;
|
|
233
242
|
let validationNote = '';
|
|
234
243
|
|
|
235
|
-
// Run
|
|
236
|
-
|
|
244
|
+
// Run checkpoint hooks (new enhanced validation system)
|
|
245
|
+
try {
|
|
246
|
+
const { runCheckpointHooks, shouldRunCheckpoint } = await import('../src/lib/checkpoint-hooks.js');
|
|
247
|
+
|
|
248
|
+
if (shouldRunCheckpoint(tasksCompleted, 3)) {
|
|
249
|
+
checkpointResult = await runCheckpointHooks(featureName, checkpointNum);
|
|
250
|
+
|
|
251
|
+
if (checkpointResult.passed) {
|
|
252
|
+
validationNote = ' | ✓ All validations passed';
|
|
253
|
+
} else {
|
|
254
|
+
validationNote = ` | ✗ ${checkpointResult.summary.errors} errors, ${checkpointResult.summary.warnings} warnings`;
|
|
255
|
+
|
|
256
|
+
// Check if we should block progress
|
|
257
|
+
const config = await this.loadCheckpointConfig();
|
|
258
|
+
if (config.checkpoints?.onFailure?.blockProgress && checkpointResult.summary.errors > 0) {
|
|
259
|
+
console.log(chalk.red('\n❌ Checkpoint FAILED - Fix violations before proceeding'));
|
|
260
|
+
throw new Error('Checkpoint validation failed');
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
} catch (error) {
|
|
265
|
+
if (error.message === 'Checkpoint validation failed') {
|
|
266
|
+
throw error; // Re-throw to block task completion
|
|
267
|
+
}
|
|
268
|
+
// Fallback to old validation if checkpoint-hooks not available
|
|
269
|
+
console.log(chalk.yellow('⚠️ Checkpoint hooks not available, using legacy validation'));
|
|
237
270
|
try {
|
|
238
271
|
const { runValidation } = await import('../src/lib/validation-runner.js');
|
|
239
272
|
const result = await runValidation('.', featureName, { verbose: false });
|
|
@@ -241,23 +274,84 @@ class TaskManager {
|
|
|
241
274
|
? ' | Validation: PASSED'
|
|
242
275
|
: ` | Validation: ${result.errors.length} errors, ${result.warnings.length} warnings`;
|
|
243
276
|
} catch {
|
|
244
|
-
//
|
|
277
|
+
// No validation available
|
|
245
278
|
}
|
|
246
279
|
}
|
|
247
280
|
|
|
281
|
+
// Create checkpoint record
|
|
248
282
|
const checkpoint = {
|
|
249
283
|
id: `CHECKPOINT_AUTO_${Date.now()}`,
|
|
250
284
|
timestamp: new Date().toISOString(),
|
|
251
285
|
tasksCompleted: tasks.map(t => t.id),
|
|
252
|
-
note: `Auto-checkpoint: ${tasks.length} tasks completed${validationNote}
|
|
286
|
+
note: `Auto-checkpoint: ${tasks.length} tasks completed${validationNote}`,
|
|
287
|
+
validationResults: checkpointResult?.results || null
|
|
253
288
|
};
|
|
254
289
|
|
|
255
290
|
feature.checkpoints = feature.checkpoints || [];
|
|
256
291
|
feature.checkpoints.push(checkpoint);
|
|
257
292
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
293
|
+
if (checkpointResult?.passed) {
|
|
294
|
+
console.log(chalk.green(`\n✅ Checkpoint ${checkpointNum} PASSED`));
|
|
295
|
+
} else if (checkpointResult) {
|
|
296
|
+
console.log(chalk.yellow(`\n⚠️ Checkpoint ${checkpointNum} completed with warnings`));
|
|
297
|
+
} else {
|
|
298
|
+
console.log(chalk.magenta(`\n🎉 Auto-checkpoint ${checkpointNum} reached!`));
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Load checkpoint configuration from llm-interaction.json
|
|
304
|
+
*/
|
|
305
|
+
async loadCheckpointConfig() {
|
|
306
|
+
try {
|
|
307
|
+
const configPath = path.join(process.cwd(), '.morph/config/llm-interaction.json');
|
|
308
|
+
const content = await fs.readFile(configPath, 'utf-8');
|
|
309
|
+
return JSON.parse(content);
|
|
310
|
+
} catch {
|
|
311
|
+
// Return defaults if config doesn't exist
|
|
312
|
+
return {
|
|
313
|
+
checkpoints: {
|
|
314
|
+
frequency: 3,
|
|
315
|
+
autoValidate: true,
|
|
316
|
+
onFailure: {
|
|
317
|
+
blockProgress: false
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Auto-generate metadata.json after task completion
|
|
326
|
+
*/
|
|
327
|
+
async generateMetadata(featureName, feature) {
|
|
328
|
+
try {
|
|
329
|
+
const config = await this.loadCheckpointConfig();
|
|
330
|
+
|
|
331
|
+
// Check if auto-generation is enabled
|
|
332
|
+
if (!config.metadata?.autoGenerate) {
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const { extractFeatureMetadata } = await import('../src/lib/metadata-extractor.js');
|
|
337
|
+
const metadata = extractFeatureMetadata(feature);
|
|
338
|
+
|
|
339
|
+
const outputPath = path.join(
|
|
340
|
+
process.cwd(),
|
|
341
|
+
`.morph/project/outputs/${featureName}/metadata.json`
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
// Ensure directory exists
|
|
345
|
+
const outputDir = path.dirname(outputPath);
|
|
346
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
347
|
+
|
|
348
|
+
// Write metadata
|
|
349
|
+
await fs.writeFile(outputPath, JSON.stringify(metadata, null, 2), 'utf-8');
|
|
350
|
+
|
|
351
|
+
console.log(chalk.gray(` 📊 Metadata updated: ${path.relative(process.cwd(), outputPath)}`));
|
|
352
|
+
} catch (error) {
|
|
353
|
+
// Don't block task completion if metadata generation fails
|
|
354
|
+
console.log(chalk.yellow(` ⚠️ Metadata generation failed: ${error.message}`));
|
|
261
355
|
}
|
|
262
356
|
}
|
|
263
357
|
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# CLI Auto Context Detection
|
|
2
|
+
|
|
3
|
+
The MORPH-SPEC CLI now includes intelligent auto-detection that analyzes your project and automatically generates accurate configuration files.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **🤖 LLM-Powered Analysis**: Uses Claude Code's embedded LLM to analyze your project
|
|
8
|
+
- **📊 Multi-Stack Support**: Detects Next.js, Blazor, .NET APIs, monorepos, and more
|
|
9
|
+
- **🔒 Privacy-First**: All data stays local (no external API calls)
|
|
10
|
+
- **🎯 90%+ Accuracy**: Few-shot learning with real project examples
|
|
11
|
+
- **🛡️ Secret Sanitization**: Automatically redacts API keys, passwords, and sensitive data
|
|
12
|
+
- **🧙 Fallback Wizard**: Manual 7-question wizard if auto-detection fails
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
### New Projects
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# Initialize MORPH with auto-detection
|
|
20
|
+
morph-spec init
|
|
21
|
+
|
|
22
|
+
# Skip auto-detection (manual config later)
|
|
23
|
+
morph-spec init --skip-detection
|
|
24
|
+
|
|
25
|
+
# Force wizard mode (skip LLM)
|
|
26
|
+
morph-spec init --wizard
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Existing Projects
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# Re-analyze and update configs
|
|
33
|
+
morph-spec update
|
|
34
|
+
|
|
35
|
+
# Skip auto-detection during update
|
|
36
|
+
morph-spec update --skip-detection
|
|
37
|
+
|
|
38
|
+
# Force wizard mode
|
|
39
|
+
morph-spec update --wizard
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## How It Works
|
|
43
|
+
|
|
44
|
+
### 1. Project Scanning
|
|
45
|
+
|
|
46
|
+
Scans your project for:
|
|
47
|
+
- `package.json` (dependencies, scripts)
|
|
48
|
+
- `.csproj` and `.sln` files
|
|
49
|
+
- `README.md` and `CLAUDE.md`
|
|
50
|
+
- Directory structure (src/, frontend/, backend/, etc.)
|
|
51
|
+
- Infrastructure files (Dockerfile, *.bicep, CI/CD pipelines)
|
|
52
|
+
- Git remote URL
|
|
53
|
+
|
|
54
|
+
### 2. Context Sanitization
|
|
55
|
+
|
|
56
|
+
Removes sensitive data before LLM analysis:
|
|
57
|
+
- **Whitelisted files**: Only reads safe files (package.json, README, etc.)
|
|
58
|
+
- **Blacklisted directories**: Never scans node_modules/, .git/, bin/, obj/
|
|
59
|
+
- **Secret redaction**: Removes API keys, passwords, connection strings
|
|
60
|
+
- **File truncation**: Limits README/CLAUDE.md to first 500 chars
|
|
61
|
+
|
|
62
|
+
### 3. LLM Analysis
|
|
63
|
+
|
|
64
|
+
Uses Claude Code's LLM with few-shot learning:
|
|
65
|
+
- 3 example projects (Next.js, Blazor, Monorepo)
|
|
66
|
+
- Structured JSON output
|
|
67
|
+
- Schema validation with Ajv
|
|
68
|
+
- Confidence scoring (0-100%)
|
|
69
|
+
|
|
70
|
+
### 4. User Review
|
|
71
|
+
|
|
72
|
+
Interactive preview with options:
|
|
73
|
+
- ✅ **Approve** - Save configs as-is
|
|
74
|
+
- ✏️ **Edit** - Open in $EDITOR before saving
|
|
75
|
+
- ❌ **Cancel** - Abort without changes
|
|
76
|
+
|
|
77
|
+
Shows diff if updating existing configs.
|
|
78
|
+
|
|
79
|
+
### 5. Fallback to Wizard
|
|
80
|
+
|
|
81
|
+
If LLM fails (no Claude Code, timeout, invalid response):
|
|
82
|
+
- Automatically launches 7-question interactive wizard
|
|
83
|
+
- Asks: name, description, type, frontend, backend, database, infrastructure
|
|
84
|
+
- Generates valid config from manual input
|
|
85
|
+
|
|
86
|
+
## Requirements
|
|
87
|
+
|
|
88
|
+
- **Claude Code CLI**: Auto-detection requires Claude Code environment
|
|
89
|
+
- **Node.js 18+**: For running the CLI
|
|
90
|
+
- **Git (optional)**: For remote URL detection
|
|
91
|
+
|
|
92
|
+
## Configuration Files Generated
|
|
93
|
+
|
|
94
|
+
### `.morph/project.md`
|
|
95
|
+
|
|
96
|
+
Human-readable project overview:
|
|
97
|
+
- Stack summary table
|
|
98
|
+
- Architecture pattern
|
|
99
|
+
- Code conventions
|
|
100
|
+
- Infrastructure (Azure, Docker, CI/CD)
|
|
101
|
+
- LLM confidence score
|
|
102
|
+
- Warnings (if any)
|
|
103
|
+
|
|
104
|
+
### `.morph/config/config.json`
|
|
105
|
+
|
|
106
|
+
Machine-readable config for agents:
|
|
107
|
+
- Project name, type, description
|
|
108
|
+
- Stack (frontend, backend, database, hosting)
|
|
109
|
+
- Architecture pattern
|
|
110
|
+
- Infrastructure flags
|
|
111
|
+
- Generated metadata (timestamp, confidence)
|
|
112
|
+
|
|
113
|
+
## Supported Project Types
|
|
114
|
+
|
|
115
|
+
| Type | Detection Criteria | Example |
|
|
116
|
+
|------|-------------------|---------|
|
|
117
|
+
| `nextjs` | package.json has `next` dependency | Next.js app |
|
|
118
|
+
| `blazor-server` | .csproj with Blazor.Server | Blazor Server app |
|
|
119
|
+
| `dotnet-api` | .csproj without Blazor | .NET Web API |
|
|
120
|
+
| `cli-tool` | package.json with `bin` field | CLI tool |
|
|
121
|
+
| `monorepo` | frontend/ and backend/ directories | Full-stack monorepo |
|
|
122
|
+
| `other` | Fallback for unknown projects | Custom setups |
|
|
123
|
+
|
|
124
|
+
## Troubleshooting
|
|
125
|
+
|
|
126
|
+
### "Claude Code environment not detected"
|
|
127
|
+
|
|
128
|
+
Auto-detection requires Claude Code CLI. Options:
|
|
129
|
+
1. Run `morph-spec init --wizard` for manual configuration
|
|
130
|
+
2. Install Claude Code and retry
|
|
131
|
+
3. Use `--skip-detection` to configure later
|
|
132
|
+
|
|
133
|
+
### "LLM analysis timeout"
|
|
134
|
+
|
|
135
|
+
Default timeout is 60 seconds for interactive use. If analysis takes too long:
|
|
136
|
+
1. Reduce project size (clean node_modules, dist)
|
|
137
|
+
2. Use `--wizard` for manual configuration
|
|
138
|
+
3. Check Claude Code performance
|
|
139
|
+
|
|
140
|
+
### "Schema validation failed"
|
|
141
|
+
|
|
142
|
+
LLM returned invalid JSON. Possible causes:
|
|
143
|
+
- Project structure too complex
|
|
144
|
+
- Missing critical files (no package.json or .csproj)
|
|
145
|
+
- Solution: Use `--wizard` mode
|
|
146
|
+
|
|
147
|
+
### Configs overwriting customizations
|
|
148
|
+
|
|
149
|
+
Auto-detection creates backups:
|
|
150
|
+
- `.morph/project.md.YYYY-MM-DDTHH-MM-SS.backup`
|
|
151
|
+
- `.morph/config/config.json.YYYY-MM-DDTHH-MM-SS.backup`
|
|
152
|
+
|
|
153
|
+
Review diffs before approving updates.
|
|
154
|
+
|
|
155
|
+
## Edge Cases
|
|
156
|
+
|
|
157
|
+
| Edge Case | Behavior |
|
|
158
|
+
|-----------|----------|
|
|
159
|
+
| Empty project (no files) | Falls back to wizard |
|
|
160
|
+
| Large project (1000+ files) | Only whitelisted files read |
|
|
161
|
+
| Monorepo with multiple stacks | Detects all stacks, shows in config |
|
|
162
|
+
| No database detected | `stack.database` set to `null` |
|
|
163
|
+
| No frontend detected | `stack.frontend` set to `null` |
|
|
164
|
+
| Git repo without remote | `gitRemote` set to `null` |
|
|
165
|
+
| User cancels (Ctrl+C) | Clean exit, no files modified |
|
|
166
|
+
|
|
167
|
+
## Privacy & Security
|
|
168
|
+
|
|
169
|
+
✅ **Local-only processing**: LLM runs in Claude Code (no external API calls)
|
|
170
|
+
✅ **Secret redaction**: 10+ secret patterns detected and removed
|
|
171
|
+
✅ **Whitelist-only reads**: Only safe files are analyzed
|
|
172
|
+
✅ **No telemetry**: No data sent to Anthropic or third parties
|
|
173
|
+
|
|
174
|
+
## Examples
|
|
175
|
+
|
|
176
|
+
### Example 1: Next.js + Supabase
|
|
177
|
+
|
|
178
|
+
**Detected:**
|
|
179
|
+
```json
|
|
180
|
+
{
|
|
181
|
+
"name": "my-nextjs-app",
|
|
182
|
+
"type": "nextjs",
|
|
183
|
+
"stack": {
|
|
184
|
+
"frontend": { "tech": "Next.js", "version": "15" },
|
|
185
|
+
"backend": { "tech": "Supabase", "version": "latest" },
|
|
186
|
+
"database": { "tech": "PostgreSQL", "version": "16" }
|
|
187
|
+
},
|
|
188
|
+
"confidence": 95
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Example 2: Blazor + Azure SQL
|
|
193
|
+
|
|
194
|
+
**Detected:**
|
|
195
|
+
```json
|
|
196
|
+
{
|
|
197
|
+
"name": "MyApp",
|
|
198
|
+
"type": "blazor-server",
|
|
199
|
+
"stack": {
|
|
200
|
+
"frontend": { "tech": "Blazor", "version": "10" },
|
|
201
|
+
"backend": { "tech": ".NET", "version": "10" },
|
|
202
|
+
"database": { "tech": "Azure SQL", "version": "latest" }
|
|
203
|
+
},
|
|
204
|
+
"hasAzure": true,
|
|
205
|
+
"confidence": 98
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Command Flags
|
|
210
|
+
|
|
211
|
+
| Flag | Description |
|
|
212
|
+
|------|-------------|
|
|
213
|
+
| `--skip-detection` | Skip auto-detection (manual config) |
|
|
214
|
+
| `--wizard` | Force interactive wizard mode |
|
|
215
|
+
| `--force` | Overwrite existing configs without prompt |
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
For more help, see the [Troubleshooting Guide](./troubleshooting.md).
|