@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.
Files changed (51) hide show
  1. package/CLAUDE.md +534 -0
  2. package/README.md +78 -4
  3. package/bin/morph-spec.js +50 -1
  4. package/bin/render-template.js +56 -10
  5. package/bin/task-manager.cjs +101 -7
  6. package/docs/cli-auto-detection.md +219 -0
  7. package/docs/llm-interaction-config.md +735 -0
  8. package/docs/troubleshooting.md +269 -0
  9. package/package.json +5 -1
  10. package/src/commands/advance-phase.js +93 -2
  11. package/src/commands/approve.js +221 -0
  12. package/src/commands/capture-pattern.js +121 -0
  13. package/src/commands/generate.js +128 -1
  14. package/src/commands/init.js +37 -0
  15. package/src/commands/migrate-state.js +158 -0
  16. package/src/commands/search-patterns.js +126 -0
  17. package/src/commands/spawn-team.js +172 -0
  18. package/src/commands/task.js +2 -2
  19. package/src/commands/update.js +36 -0
  20. package/src/commands/upgrade.js +346 -0
  21. package/src/generator/.gitkeep +0 -0
  22. package/src/generator/config-generator.js +206 -0
  23. package/src/generator/templates/config.json.template +40 -0
  24. package/src/generator/templates/project.md.template +67 -0
  25. package/src/lib/checkpoint-hooks.js +258 -0
  26. package/src/lib/metadata-extractor.js +380 -0
  27. package/src/lib/phase-state-machine.js +214 -0
  28. package/src/lib/state-manager.js +120 -0
  29. package/src/lib/template-data-sources.js +325 -0
  30. package/src/lib/validators/content-validator.js +351 -0
  31. package/src/llm/.gitkeep +0 -0
  32. package/src/llm/analyzer.js +215 -0
  33. package/src/llm/environment-detector.js +43 -0
  34. package/src/llm/few-shot-examples.js +216 -0
  35. package/src/llm/project-config-schema.json +188 -0
  36. package/src/llm/prompt-builder.js +96 -0
  37. package/src/llm/schema-validator.js +121 -0
  38. package/src/orchestrator.js +206 -0
  39. package/src/sanitizer/.gitkeep +0 -0
  40. package/src/sanitizer/context-sanitizer.js +221 -0
  41. package/src/sanitizer/patterns.js +163 -0
  42. package/src/scanner/.gitkeep +0 -0
  43. package/src/scanner/project-scanner.js +242 -0
  44. package/src/types/index.js +477 -0
  45. package/src/ui/.gitkeep +0 -0
  46. package/src/ui/diff-display.js +91 -0
  47. package/src/ui/interactive-wizard.js +96 -0
  48. package/src/ui/user-review.js +211 -0
  49. package/src/ui/wizard-questions.js +190 -0
  50. package/src/writer/.gitkeep +0 -0
  51. 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();
@@ -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 with provided variables (provided takes precedence)
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 Show this help message
209
- --verbose, -v Show detailed replacement information
210
- --dry-run, -d Preview output without writing file
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
+ });
@@ -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 validation and include summary in checkpoint
236
- if (featureName) {
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
- // Validation not available, skip
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
- console.log(chalk.magenta(`\n🎉 Auto-checkpoint reached! ${tasks.length} tasks completed`));
259
- if (validationNote) {
260
- console.log(chalk.gray(` ${validationNote.trim().replace(/^ \| /, '')}`));
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).