@polymorphism-tech/morph-spec 4.7.1 → 4.8.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.
- package/.morph/analytics/threads-log.jsonl +54 -0
- package/.morph/state.json +198 -0
- package/LICENSE +1 -2
- package/README.md +379 -414
- package/bin/morph-spec.js +57 -403
- package/bin/validate.js +2 -26
- package/claude-plugin.json +2 -2
- package/docs/ARCHITECTURE.md +43 -46
- package/docs/CHEATSHEET.md +203 -221
- package/docs/COMMAND-FLOWS.md +319 -289
- package/docs/QUICKSTART.md +2 -8
- package/docs/plans/2026-02-22-claude-docs-morph-alignment-analysis.md +2 -0
- package/docs/plans/2026-02-22-claude-settings.md +2 -0
- package/docs/plans/2026-02-22-morph-cc-alignment-impl.md +2 -0
- package/docs/plans/2026-02-22-morph-spec-next.md +2 -0
- package/docs/plans/2026-02-22-native-alignment-design.md +2 -0
- package/docs/plans/2026-02-22-native-alignment-impl.md +2 -0
- package/docs/plans/2026-02-22-native-enrichment-design.md +2 -0
- package/docs/plans/2026-02-22-native-enrichment.md +2 -0
- package/docs/plans/2026-02-23-ddd-architecture-refactor.md +2 -0
- package/docs/plans/2026-02-23-ddd-nextsteps.md +2 -0
- package/docs/plans/2026-02-23-infra-architect-refactor.md +2 -0
- package/docs/plans/2026-02-23-nextjs-code-review-design.md +2 -1
- package/docs/plans/2026-02-23-nextjs-code-review-impl.md +2 -0
- package/docs/plans/2026-02-23-nextjs-standards-design.md +2 -1
- package/docs/plans/2026-02-23-nextjs-standards-impl.md +2 -0
- package/docs/plans/2026-02-24-cli-radical-simplification.md +592 -0
- package/docs/plans/2026-02-24-framework-failure-points.md +125 -0
- package/docs/plans/2026-02-24-morph-init-design.md +337 -0
- package/docs/plans/2026-02-24-morph-init-impl.md +1269 -0
- package/docs/plans/2026-02-24-tutorial-command-design.md +71 -0
- package/docs/plans/2026-02-24-tutorial-command.md +298 -0
- package/framework/CLAUDE.md +2 -2
- package/framework/commands/morph-proposal.md +3 -3
- package/framework/hooks/README.md +11 -10
- package/framework/hooks/claude-code/notification/approval-reminder.js +2 -0
- package/framework/hooks/claude-code/post-tool-use/dispatch.js +1 -1
- package/framework/hooks/claude-code/pre-tool-use/protect-readonly-files.js +4 -55
- package/framework/hooks/claude-code/session-start/inject-morph-context.js +20 -5
- package/framework/hooks/claude-code/statusline.py +6 -1
- package/framework/hooks/claude-code/stop/validate-completion.js +1 -1
- package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +1 -1
- package/framework/hooks/dev/check-sync-health.js +117 -0
- package/framework/hooks/dev/guard-version-numbers.js +57 -0
- package/framework/hooks/dev/sync-standards-registry.js +60 -0
- package/framework/hooks/dev/sync-template-registry.js +60 -0
- package/framework/hooks/dev/validate-skill-format.js +70 -0
- package/framework/hooks/dev/validate-standard-format.js +73 -0
- package/framework/hooks/shared/payload-utils.js +39 -0
- package/framework/hooks/shared/state-reader.js +25 -1
- package/framework/rules/morph-workflow.md +1 -1
- package/framework/skills/level-0-meta/morph-init/SKILL.md +216 -0
- package/framework/skills/level-0-meta/morph-replicate/SKILL.md +4 -4
- package/framework/skills/level-0-meta/tool-usage-guide/SKILL.md +4 -4
- package/framework/skills/level-0-meta/verification-before-completion/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/phase-clarify/SKILL.md +192 -191
- package/framework/skills/level-1-workflows/phase-codebase-analysis/SKILL.md +181 -180
- package/framework/skills/level-1-workflows/phase-design/SKILL.md +339 -338
- package/framework/skills/level-1-workflows/phase-implement/SKILL.md +254 -253
- package/framework/skills/level-1-workflows/phase-setup/SKILL.md +168 -170
- package/framework/skills/level-1-workflows/phase-tasks/SKILL.md +284 -283
- package/framework/skills/level-1-workflows/phase-uiux/SKILL.md +246 -245
- package/framework/templates/examples/design-system-examples.md +1 -1
- package/framework/templates/ui/FluentDesignTheme.cs +1 -1
- package/framework/templates/ui/MudTheme.cs +1 -1
- package/framework/templates/ui/design-system.css +1 -1
- package/package.json +4 -2
- package/scripts/bump-version.js +248 -0
- package/scripts/install-dev-hooks.js +138 -0
- package/src/commands/agents/index.js +1 -2
- package/src/commands/index.js +13 -16
- package/src/commands/project/doctor.js +100 -14
- package/src/commands/project/index.js +7 -10
- package/src/commands/project/init.js +398 -555
- package/src/commands/project/install-plugin-cmd.js +28 -0
- package/src/commands/project/setup-infra-cmd.js +12 -0
- package/src/commands/project/tutorial.js +115 -0
- package/src/commands/project/update.js +22 -37
- package/src/commands/state/approve.js +213 -221
- package/src/commands/state/index.js +0 -1
- package/src/commands/state/state.js +337 -365
- package/src/commands/templates/index.js +0 -4
- package/src/commands/trust/trust.js +1 -93
- package/src/commands/utils/index.js +1 -5
- package/src/commands/validation/index.js +1 -5
- package/src/core/registry/command-registry.js +11 -285
- package/src/core/state/state-manager.js +5 -2
- package/src/lib/detectors/index.js +81 -87
- package/src/lib/detectors/structure-detector.js +275 -273
- package/src/lib/generators/recap-generator.js +232 -225
- package/src/lib/installers/mcp-installer.js +18 -3
- package/src/scripts/global-install.js +34 -0
- package/src/scripts/install-plugin.js +126 -0
- package/src/scripts/setup-infra.js +203 -0
- package/src/utils/agents-installer.js +10 -1
- package/src/utils/hooks-installer.js +70 -17
- package/CLAUDE.md +0 -77
- package/docs/claude-alignment-report.md +0 -137
- package/docs/examples/order-management/contracts.cs +0 -84
- package/docs/examples/order-management/proposal.md +0 -24
- package/docs/examples/order-management/spec.md +0 -162
- package/src/commands/feature/create-story.js +0 -362
- package/src/commands/feature/index.js +0 -6
- package/src/commands/feature/shard-spec.js +0 -225
- package/src/commands/feature/sprint-status.js +0 -250
- package/src/commands/generation/generate-onboarding.js +0 -169
- package/src/commands/generation/generate.js +0 -276
- package/src/commands/generation/index.js +0 -5
- package/src/commands/learning/capture-pattern.js +0 -121
- package/src/commands/learning/index.js +0 -5
- package/src/commands/learning/search-patterns.js +0 -126
- package/src/commands/mcp/mcp.js +0 -102
- package/src/commands/project/changes.js +0 -66
- package/src/commands/project/cost.js +0 -179
- package/src/commands/project/detect.js +0 -114
- package/src/commands/project/diff.js +0 -278
- package/src/commands/project/revert.js +0 -173
- package/src/commands/project/standards.js +0 -80
- package/src/commands/project/sync.js +0 -167
- package/src/commands/project/update-agents.js +0 -23
- package/src/commands/state/rollback-phase.js +0 -185
- package/src/commands/templates/template-customize.js +0 -87
- package/src/commands/templates/template-list.js +0 -114
- package/src/commands/templates/template-show.js +0 -129
- package/src/commands/templates/template-validate.js +0 -91
- package/src/commands/utils/troubleshoot.js +0 -222
- package/src/commands/validation/analyze-blazor-concurrency.js +0 -193
- package/src/commands/validation/lint-fluent.js +0 -352
- package/src/commands/validation/validate-blazor-state.js +0 -210
- package/src/commands/validation/validate-blazor.js +0 -156
- package/src/commands/validation/validate-css.js +0 -84
- package/src/lib/detectors/conversation-analyzer.js +0 -163
- package/src/lib/learning/index.js +0 -7
- package/src/lib/learning/learning-system.js +0 -520
- package/src/lib/troubleshooting/index.js +0 -8
- package/src/lib/troubleshooting/troubleshoot-grep.js +0 -198
- package/src/lib/troubleshooting/troubleshoot-index.js +0 -144
- package/src/llm/environment-detector.js +0 -43
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Template Show Command - Display detailed information about a template
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import chalk from 'chalk';
|
|
6
|
-
import { logger } from '../../utils/logger.js';
|
|
7
|
-
import { getTemplateById, resolveTemplatePathById } from '../../core/templates/template-registry.js';
|
|
8
|
-
import { readFileSync, existsSync } from 'fs';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Show template details command
|
|
12
|
-
*/
|
|
13
|
-
export async function templateShowCommand(templateId, options) {
|
|
14
|
-
logger.header(`Template: ${templateId}`);
|
|
15
|
-
logger.blank();
|
|
16
|
-
|
|
17
|
-
try {
|
|
18
|
-
const template = getTemplateById(templateId);
|
|
19
|
-
|
|
20
|
-
if (!template) {
|
|
21
|
-
logger.error(`Template not found: ${templateId}`);
|
|
22
|
-
logger.dim('\n💡 Use `morph-spec template list` to see available templates');
|
|
23
|
-
process.exit(1);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Display template metadata
|
|
27
|
-
logger.info(chalk.bold('📋 Metadata'));
|
|
28
|
-
console.log(` ID: ${chalk.cyan(template.id)}`);
|
|
29
|
-
console.log(` Name: ${chalk.green(template.name)}`);
|
|
30
|
-
console.log(` Path: ${chalk.dim(template.path)}`);
|
|
31
|
-
console.log(` Location: ${template.location === 'framework' ? chalk.cyan('framework') : chalk.yellow('stack')}`);
|
|
32
|
-
console.log(` Category: ${template.category}`);
|
|
33
|
-
console.log(` Phase: ${template.phase || 'N/A'}`);
|
|
34
|
-
console.log(` Required: ${template.required ? chalk.green('Yes') : chalk.dim('No')}`);
|
|
35
|
-
console.log(` Stack-Specific: ${template.stackSpecific ? chalk.yellow('Yes') : chalk.cyan('No (universal)')}`);
|
|
36
|
-
|
|
37
|
-
if (template.stackSpecific && template.stacks) {
|
|
38
|
-
console.log(` Stacks: ${chalk.yellow(template.stacks.join(', '))}`);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
console.log(` Engine: ${template.engine === 'handlebars' ? chalk.blue('Handlebars') : chalk.dim('Custom')}`);
|
|
42
|
-
|
|
43
|
-
if (template.deprecated) {
|
|
44
|
-
console.log(` ${chalk.red.bold('⚠️ DEPRECATED')}`);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (template.autoGenerated) {
|
|
48
|
-
console.log(` ${chalk.dim('Auto-generated (not user-facing)')}`);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
logger.blank();
|
|
52
|
-
|
|
53
|
-
// Description
|
|
54
|
-
if (template.description) {
|
|
55
|
-
logger.info(chalk.bold('📝 Description'));
|
|
56
|
-
console.log(` ${template.description}`);
|
|
57
|
-
logger.blank();
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Placeholders
|
|
61
|
-
if (template.placeholders && template.placeholders.length > 0) {
|
|
62
|
-
logger.info(chalk.bold('🔑 Variables'));
|
|
63
|
-
template.placeholders.forEach(p => {
|
|
64
|
-
console.log(` ${chalk.cyan(`{{${p}}}`)}`);
|
|
65
|
-
});
|
|
66
|
-
logger.blank();
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Output name
|
|
70
|
-
if (template.outputName) {
|
|
71
|
-
logger.info(chalk.bold('📄 Output Name'));
|
|
72
|
-
console.log(` ${chalk.dim(template.outputName)}`);
|
|
73
|
-
logger.blank();
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Resolve actual path
|
|
77
|
-
if (options.showPath) {
|
|
78
|
-
const projectPath = options.projectPath || process.cwd();
|
|
79
|
-
const resolvedPath = resolveTemplatePathById(templateId, projectPath);
|
|
80
|
-
|
|
81
|
-
logger.info(chalk.bold('📂 Resolved Path'));
|
|
82
|
-
if (resolvedPath) {
|
|
83
|
-
console.log(` ${chalk.green(resolvedPath)}`);
|
|
84
|
-
console.log(` ${chalk.dim(`(${existsSync(resolvedPath) ? 'exists' : 'not found'})`)}`);
|
|
85
|
-
} else {
|
|
86
|
-
console.log(` ${chalk.red('Not found in .morph/framework/templates/ or framework/templates/')}`);
|
|
87
|
-
}
|
|
88
|
-
logger.blank();
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Show template preview
|
|
92
|
-
if (options.preview) {
|
|
93
|
-
const projectPath = options.projectPath || process.cwd();
|
|
94
|
-
const resolvedPath = resolveTemplatePathById(templateId, projectPath);
|
|
95
|
-
|
|
96
|
-
if (resolvedPath && existsSync(resolvedPath)) {
|
|
97
|
-
logger.info(chalk.bold('👁️ Preview (first 20 lines)'));
|
|
98
|
-
logger.blank();
|
|
99
|
-
|
|
100
|
-
const content = readFileSync(resolvedPath, 'utf-8');
|
|
101
|
-
const lines = content.split('\n').slice(0, 20);
|
|
102
|
-
|
|
103
|
-
lines.forEach((line, i) => {
|
|
104
|
-
console.log(chalk.dim(`${String(i + 1).padStart(3)} ${line}`));
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
if (content.split('\n').length > 20) {
|
|
108
|
-
logger.blank();
|
|
109
|
-
logger.dim(` ... (${content.split('\n').length - 20} more lines)`);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
logger.blank();
|
|
113
|
-
} else {
|
|
114
|
-
logger.warn('Template file not found - cannot preview');
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Show usage hint
|
|
119
|
-
if (template.engine === 'handlebars') {
|
|
120
|
-
logger.info(chalk.bold('💡 Usage Hint'));
|
|
121
|
-
console.log(chalk.dim(` morph-spec template render ${templateId} output.md '{"FEATURE_NAME":"my-feature"}'`));
|
|
122
|
-
logger.blank();
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
} catch (error) {
|
|
126
|
-
logger.error(`Failed to show template: ${error.message}`);
|
|
127
|
-
process.exit(1);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Template Validate Command - Validate Handlebars templates
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import chalk from 'chalk';
|
|
6
|
-
import { logger } from '../../utils/logger.js';
|
|
7
|
-
import { validateTemplate, validateAllTemplates, formatValidationResults, summarizeValidation } from '../../core/templates/template-validator.js';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Validate template(s) command
|
|
11
|
-
*/
|
|
12
|
-
export async function templateValidateCommand(templateId, options) {
|
|
13
|
-
const projectPath = options.projectPath || process.cwd();
|
|
14
|
-
|
|
15
|
-
if (templateId) {
|
|
16
|
-
// Validate single template
|
|
17
|
-
logger.header(`Validating Template: ${templateId}`);
|
|
18
|
-
logger.blank();
|
|
19
|
-
|
|
20
|
-
const result = validateTemplate(templateId, projectPath);
|
|
21
|
-
|
|
22
|
-
// Display results
|
|
23
|
-
if (result.errors.length === 0 && result.warnings.length === 0) {
|
|
24
|
-
logger.success(`✅ Template is valid`);
|
|
25
|
-
logger.blank();
|
|
26
|
-
|
|
27
|
-
if (result.info.length > 0 && options.verbose) {
|
|
28
|
-
logger.info(chalk.bold('ℹ️ Information'));
|
|
29
|
-
result.info.forEach(info => console.log(chalk.dim(` ${info}`)));
|
|
30
|
-
logger.blank();
|
|
31
|
-
}
|
|
32
|
-
} else {
|
|
33
|
-
if (result.errors.length > 0) {
|
|
34
|
-
logger.error(chalk.bold('❌ Errors'));
|
|
35
|
-
result.errors.forEach(error => console.log(chalk.red(` - ${error}`)));
|
|
36
|
-
logger.blank();
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (result.warnings.length > 0) {
|
|
40
|
-
logger.warn(chalk.bold('⚠️ Warnings'));
|
|
41
|
-
result.warnings.forEach(warning => console.log(chalk.yellow(` - ${warning}`)));
|
|
42
|
-
logger.blank();
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (result.info.length > 0 && options.verbose) {
|
|
46
|
-
logger.info(chalk.bold('ℹ️ Information'));
|
|
47
|
-
result.info.forEach(info => console.log(chalk.dim(` ${info}`)));
|
|
48
|
-
logger.blank();
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (result.errors.length > 0) {
|
|
52
|
-
process.exit(1);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
} else {
|
|
56
|
-
// Validate all templates
|
|
57
|
-
logger.header('Validating All Templates');
|
|
58
|
-
logger.blank();
|
|
59
|
-
|
|
60
|
-
const validationOptions = {
|
|
61
|
-
skipDeprecated: !options.includeDeprecated,
|
|
62
|
-
category: options.category,
|
|
63
|
-
technology: options.technology,
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
const results = validateAllTemplates(projectPath, validationOptions);
|
|
67
|
-
const summary = summarizeValidation(results);
|
|
68
|
-
|
|
69
|
-
// Display results
|
|
70
|
-
const formatted = formatValidationResults(results, {
|
|
71
|
-
showInfo: options.verbose,
|
|
72
|
-
showValid: options.showValid,
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
console.log(formatted);
|
|
76
|
-
logger.blank();
|
|
77
|
-
|
|
78
|
-
// Exit with error if any templates have errors
|
|
79
|
-
if (summary.hasErrors > 0) {
|
|
80
|
-
logger.error(`${summary.hasErrors} template(s) failed validation`);
|
|
81
|
-
process.exit(1);
|
|
82
|
-
} else if (summary.hasWarnings > 0) {
|
|
83
|
-
logger.warn(`${summary.hasWarnings} template(s) have warnings`);
|
|
84
|
-
if (options.strict) {
|
|
85
|
-
process.exit(1);
|
|
86
|
-
}
|
|
87
|
-
} else {
|
|
88
|
-
logger.success(`All templates are valid! ✅`);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Troubleshoot Command
|
|
3
|
-
* Search for solutions to common problems
|
|
4
|
-
*
|
|
5
|
-
* Usage: morph-spec troubleshoot <keywords...>
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { searchIndex, getCategories } from '../../lib/troubleshooting/troubleshoot-index.js';
|
|
9
|
-
import { searchGrep } from '../../lib/troubleshooting/troubleshoot-grep.js';
|
|
10
|
-
import chalk from 'chalk';
|
|
11
|
-
|
|
12
|
-
// Severity colors and icons
|
|
13
|
-
const SEVERITY_CONFIG = {
|
|
14
|
-
critical: { color: chalk.red, icon: '🔴', label: 'CRITICAL' },
|
|
15
|
-
high: { color: chalk.yellow, icon: '🟠', label: 'HIGH' },
|
|
16
|
-
medium: { color: chalk.blue, icon: '🔵', label: 'MEDIUM' },
|
|
17
|
-
low: { color: chalk.gray, icon: '⚪', label: 'LOW' }
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Format a single result for display
|
|
22
|
-
* @param {Object} result - Result object
|
|
23
|
-
* @param {boolean} verbose - Show full content
|
|
24
|
-
* @returns {string} Formatted output
|
|
25
|
-
*/
|
|
26
|
-
function formatResult(result, verbose = false) {
|
|
27
|
-
const severity = SEVERITY_CONFIG[result.severity] || SEVERITY_CONFIG.medium;
|
|
28
|
-
const lines = [];
|
|
29
|
-
|
|
30
|
-
lines.push(chalk.gray('━'.repeat(50)));
|
|
31
|
-
lines.push('');
|
|
32
|
-
lines.push(`${severity.icon} ${severity.color.bold(severity.label)}: ${chalk.white.bold(result.title)}`);
|
|
33
|
-
lines.push('');
|
|
34
|
-
|
|
35
|
-
// File and section
|
|
36
|
-
lines.push(`${chalk.gray('📁 Arquivo:')} ${chalk.cyan(result.file)}`);
|
|
37
|
-
if (result.section) {
|
|
38
|
-
lines.push(`${chalk.gray('📂 Seção:')} ${result.section}`);
|
|
39
|
-
}
|
|
40
|
-
lines.push('');
|
|
41
|
-
|
|
42
|
-
// Cause
|
|
43
|
-
if (result.cause) {
|
|
44
|
-
lines.push(`${chalk.red('💡 Causa:')}`);
|
|
45
|
-
lines.push(` ${result.cause}`);
|
|
46
|
-
lines.push('');
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Solution
|
|
50
|
-
if (result.solution) {
|
|
51
|
-
lines.push(`${chalk.green('✅ Solução:')}`);
|
|
52
|
-
lines.push(` ${chalk.white(result.solution)}`);
|
|
53
|
-
lines.push('');
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Content preview (for grep results or verbose mode)
|
|
57
|
-
if (verbose && result.content) {
|
|
58
|
-
lines.push(`${chalk.gray('📝 Conteúdo:')}`);
|
|
59
|
-
const contentLines = result.content.split('\n').slice(0, 15);
|
|
60
|
-
contentLines.forEach(line => {
|
|
61
|
-
lines.push(` ${chalk.gray(line)}`);
|
|
62
|
-
});
|
|
63
|
-
lines.push('');
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Keywords matched
|
|
67
|
-
if (result.keywords && result.keywords.length > 0) {
|
|
68
|
-
const keywords = Array.isArray(result.keywords)
|
|
69
|
-
? result.keywords.slice(0, 5).join(', ')
|
|
70
|
-
: result.keywords;
|
|
71
|
-
lines.push(`${chalk.gray('🏷️ Keywords:')} ${chalk.dim(keywords)}`);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Source (index or grep)
|
|
75
|
-
if (result.source === 'grep') {
|
|
76
|
-
lines.push(`${chalk.gray('🔍 Fonte:')} ${chalk.dim('Busca em arquivos (grep)')}`);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
lines.push('');
|
|
80
|
-
|
|
81
|
-
return lines.join('\n');
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Format the summary footer
|
|
86
|
-
* @param {Object} stats - Search statistics
|
|
87
|
-
* @returns {string} Formatted summary
|
|
88
|
-
*/
|
|
89
|
-
function formatSummary(stats) {
|
|
90
|
-
const lines = [];
|
|
91
|
-
|
|
92
|
-
lines.push(chalk.gray('━'.repeat(50)));
|
|
93
|
-
lines.push('');
|
|
94
|
-
|
|
95
|
-
if (stats.total === 0) {
|
|
96
|
-
lines.push(chalk.yellow('⚠️ Nenhum resultado encontrado no índice ou arquivos.'));
|
|
97
|
-
lines.push('');
|
|
98
|
-
lines.push(chalk.dim('💡 Dicas:'));
|
|
99
|
-
lines.push(chalk.dim(' • Tente termos mais específicos (ex: "NU1605" em vez de "error")'));
|
|
100
|
-
lines.push(chalk.dim(' • Use parte da mensagem de erro exata'));
|
|
101
|
-
lines.push(chalk.dim(' • Consulte a documentação: morph-spec read frontend/blazor/pitfalls.md'));
|
|
102
|
-
} else {
|
|
103
|
-
lines.push(`${chalk.green('✅')} Encontrado ${chalk.bold(stats.total)} resultado(s)`);
|
|
104
|
-
|
|
105
|
-
if (stats.fromIndex > 0) {
|
|
106
|
-
lines.push(` ${chalk.dim(`• ${stats.fromIndex} do índice de problemas conhecidos`)}`);
|
|
107
|
-
}
|
|
108
|
-
if (stats.fromGrep > 0) {
|
|
109
|
-
lines.push(` ${chalk.dim(`• ${stats.fromGrep} da busca em arquivos`)}`);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
lines.push('');
|
|
114
|
-
|
|
115
|
-
return lines.join('\n');
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* List all categories
|
|
120
|
-
*/
|
|
121
|
-
function listCategories() {
|
|
122
|
-
const categories = getCategories();
|
|
123
|
-
|
|
124
|
-
console.log('');
|
|
125
|
-
console.log(chalk.bold('📂 Categorias disponíveis:'));
|
|
126
|
-
console.log('');
|
|
127
|
-
|
|
128
|
-
for (const [id, category] of Object.entries(categories)) {
|
|
129
|
-
const name = typeof category === 'object' ? category.name : category;
|
|
130
|
-
const desc = typeof category === 'object' ? category.description : '';
|
|
131
|
-
console.log(` ${chalk.cyan(id.padEnd(12))} ${name}`);
|
|
132
|
-
if (desc) {
|
|
133
|
-
console.log(` ${' '.repeat(12)} ${chalk.dim(desc)}`);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
console.log('');
|
|
138
|
-
console.log(chalk.dim('Uso: morph-spec troubleshoot --category efcore "sua busca"'));
|
|
139
|
-
console.log('');
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Main troubleshoot command
|
|
144
|
-
* @param {string[]} keywords - Search keywords
|
|
145
|
-
* @param {Object} options - Command options
|
|
146
|
-
*/
|
|
147
|
-
export async function troubleshootCommand(keywords, options = {}) {
|
|
148
|
-
// Handle --list-categories flag
|
|
149
|
-
if (options.listCategories) {
|
|
150
|
-
listCategories();
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Validate keywords
|
|
155
|
-
if (!keywords || keywords.length === 0) {
|
|
156
|
-
console.log('');
|
|
157
|
-
console.log(chalk.yellow('⚠️ Por favor, forneça termos de busca.'));
|
|
158
|
-
console.log('');
|
|
159
|
-
console.log(chalk.dim('Exemplos:'));
|
|
160
|
-
console.log(chalk.dim(' morph-spec troubleshoot "blazor.web.js 404"'));
|
|
161
|
-
console.log(chalk.dim(' morph-spec troubleshoot DbContext concurrent'));
|
|
162
|
-
console.log(chalk.dim(' morph-spec troubleshoot NU1605 Azure.Identity'));
|
|
163
|
-
console.log('');
|
|
164
|
-
console.log(chalk.dim('Opções:'));
|
|
165
|
-
console.log(chalk.dim(' --category <cat> Filtrar por categoria (blazor, efcore, azure)'));
|
|
166
|
-
console.log(chalk.dim(' --verbose, -v Mostrar conteúdo completo'));
|
|
167
|
-
console.log(chalk.dim(' --list-categories Listar categorias disponíveis'));
|
|
168
|
-
console.log('');
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const searchTerms = Array.isArray(keywords) ? keywords : [keywords];
|
|
173
|
-
|
|
174
|
-
console.log('');
|
|
175
|
-
console.log(`${chalk.cyan('🔍')} Buscando soluções para: ${chalk.bold(`"${searchTerms.join(' ')}"`)}`);
|
|
176
|
-
console.log('');
|
|
177
|
-
|
|
178
|
-
// Search options
|
|
179
|
-
const searchOptions = {};
|
|
180
|
-
if (options.category) {
|
|
181
|
-
searchOptions.category = options.category;
|
|
182
|
-
console.log(chalk.dim(` Filtrando por categoria: ${options.category}`));
|
|
183
|
-
console.log('');
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// Level 1: Search index
|
|
187
|
-
let indexResults = searchIndex(searchTerms, searchOptions);
|
|
188
|
-
indexResults = indexResults.map(r => ({ ...r, source: 'index' }));
|
|
189
|
-
|
|
190
|
-
// Level 2: Grep fallback (if few results)
|
|
191
|
-
let grepResults = [];
|
|
192
|
-
if (indexResults.length < 3) {
|
|
193
|
-
grepResults = searchGrep(searchTerms, searchOptions);
|
|
194
|
-
// Filter out duplicates (same file + section)
|
|
195
|
-
const indexKeys = new Set(indexResults.map(r => `${r.file}:${r.section}`));
|
|
196
|
-
grepResults = grepResults.filter(r => !indexKeys.has(`${r.file}:${r.section}`));
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// Combine and limit results
|
|
200
|
-
const allResults = [...indexResults, ...grepResults].slice(0, 10);
|
|
201
|
-
|
|
202
|
-
// Display results
|
|
203
|
-
for (const result of allResults) {
|
|
204
|
-
console.log(formatResult(result, options.verbose));
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Display summary
|
|
208
|
-
console.log(formatSummary({
|
|
209
|
-
total: allResults.length,
|
|
210
|
-
fromIndex: indexResults.length,
|
|
211
|
-
fromGrep: grepResults.slice(0, 10 - indexResults.length).length
|
|
212
|
-
}));
|
|
213
|
-
|
|
214
|
-
// Hint for web search (handled by Claude Code slash command)
|
|
215
|
-
if (allResults.length === 0) {
|
|
216
|
-
console.log(chalk.dim('💡 Use /morph-troubleshoot no Claude Code para busca web em'));
|
|
217
|
-
console.log(chalk.dim(' GitHub Issues, Stack Overflow e Microsoft Learn.'));
|
|
218
|
-
console.log('');
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
export default troubleshootCommand;
|
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* analyze-blazor-concurrency command
|
|
3
|
-
*
|
|
4
|
-
* Analyzes Blazor Server code for DbContext concurrency issues.
|
|
5
|
-
*
|
|
6
|
-
* Usage:
|
|
7
|
-
* morph-spec analyze-blazor-concurrency [path]
|
|
8
|
-
* morph-spec analyze-blazor-concurrency src/Services --verbose
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { glob } from 'glob';
|
|
12
|
-
import { readFileSync, existsSync, statSync } from 'fs';
|
|
13
|
-
import { join, resolve } from 'path';
|
|
14
|
-
import chalk from 'chalk';
|
|
15
|
-
import ora from 'ora';
|
|
16
|
-
|
|
17
|
-
import {
|
|
18
|
-
analyzeConcurrency,
|
|
19
|
-
} from '../../lib/validators/blazor/blazor-concurrency-analyzer.js';
|
|
20
|
-
import { countIssues } from '../../lib/validators/shared/index.js';
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Main command handler for analyze-blazor-concurrency.
|
|
24
|
-
*
|
|
25
|
-
* @param {string} [targetPath] - Path to analyze (file or directory)
|
|
26
|
-
* @param {Object} options - Command options
|
|
27
|
-
* @param {boolean} [options.verbose] - Show detailed output
|
|
28
|
-
*/
|
|
29
|
-
export async function analyzeBlazorConcurrencyCommand(targetPath, options = {}) {
|
|
30
|
-
const { verbose = false } = options;
|
|
31
|
-
|
|
32
|
-
console.log('');
|
|
33
|
-
console.log(
|
|
34
|
-
chalk.cyan('\uD83D\uDD0D Analisando concorrencia DbContext em Blazor Server...')
|
|
35
|
-
);
|
|
36
|
-
console.log('');
|
|
37
|
-
|
|
38
|
-
// Determine target path
|
|
39
|
-
const basePath = targetPath ? resolve(targetPath) : process.cwd();
|
|
40
|
-
|
|
41
|
-
if (!existsSync(basePath)) {
|
|
42
|
-
console.log(chalk.red(`\u274C Path not found: ${basePath}`));
|
|
43
|
-
process.exit(1);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const spinner = ora('Scanning for C# files...').start();
|
|
47
|
-
|
|
48
|
-
let files = [];
|
|
49
|
-
|
|
50
|
-
try {
|
|
51
|
-
const stats = statSync(basePath);
|
|
52
|
-
|
|
53
|
-
if (stats.isFile()) {
|
|
54
|
-
if (basePath.endsWith('.cs')) {
|
|
55
|
-
files = [basePath];
|
|
56
|
-
} else {
|
|
57
|
-
spinner.fail('File must be .cs');
|
|
58
|
-
process.exit(1);
|
|
59
|
-
}
|
|
60
|
-
} else {
|
|
61
|
-
// Find all C# files (services, repositories, handlers)
|
|
62
|
-
const pattern = join(basePath, '**/*.cs');
|
|
63
|
-
files = await glob(pattern, {
|
|
64
|
-
ignore: [
|
|
65
|
-
'**/node_modules/**',
|
|
66
|
-
'**/bin/**',
|
|
67
|
-
'**/obj/**',
|
|
68
|
-
'**/Migrations/**',
|
|
69
|
-
],
|
|
70
|
-
windowsPathsNoEscape: true,
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
spinner.succeed(`Found ${files.length} C# file(s)`);
|
|
75
|
-
} catch (error) {
|
|
76
|
-
spinner.fail(`Error scanning files: ${error.message}`);
|
|
77
|
-
process.exit(1);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (files.length === 0) {
|
|
81
|
-
console.log(chalk.yellow('\n\u26A0\uFE0F No C# files found to analyze'));
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Analyze each file
|
|
86
|
-
const allIssues = [];
|
|
87
|
-
const fileResults = [];
|
|
88
|
-
|
|
89
|
-
for (const file of files) {
|
|
90
|
-
try {
|
|
91
|
-
const content = readFileSync(file, 'utf-8');
|
|
92
|
-
const issues = analyzeConcurrency(content, file);
|
|
93
|
-
|
|
94
|
-
if (issues.length > 0) {
|
|
95
|
-
allIssues.push(...issues);
|
|
96
|
-
fileResults.push({ file, issues });
|
|
97
|
-
}
|
|
98
|
-
} catch (error) {
|
|
99
|
-
if (verbose) {
|
|
100
|
-
console.log(chalk.red(`\u274C Error reading ${file}: ${error.message}`));
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Output results
|
|
106
|
-
console.log('');
|
|
107
|
-
|
|
108
|
-
if (allIssues.length === 0) {
|
|
109
|
-
console.log(
|
|
110
|
-
chalk.green(
|
|
111
|
-
'\u2705 Nenhum problema de concorrencia detectado!'
|
|
112
|
-
)
|
|
113
|
-
);
|
|
114
|
-
console.log(
|
|
115
|
-
chalk.gray(
|
|
116
|
-
' Dica: Certifique-se de usar IDbContextFactory para operacoes background.'
|
|
117
|
-
)
|
|
118
|
-
);
|
|
119
|
-
console.log('');
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Display issues
|
|
124
|
-
if (verbose) {
|
|
125
|
-
fileResults.forEach(({ file, issues }) => {
|
|
126
|
-
console.log(chalk.white.bold(`\n${file}`));
|
|
127
|
-
console.log(chalk.gray('-'.repeat(60)));
|
|
128
|
-
|
|
129
|
-
issues.forEach((issue) => {
|
|
130
|
-
const icon =
|
|
131
|
-
issue.type === 'error'
|
|
132
|
-
? '\u274C'
|
|
133
|
-
: issue.type === 'warning'
|
|
134
|
-
? '\u26A0\uFE0F'
|
|
135
|
-
: '\u2139\uFE0F';
|
|
136
|
-
|
|
137
|
-
console.log(`${icon} ${issue.message}`);
|
|
138
|
-
if (issue.suggestion) {
|
|
139
|
-
console.log(chalk.gray(` \u2192 ${issue.suggestion}`));
|
|
140
|
-
}
|
|
141
|
-
if (issue.line) {
|
|
142
|
-
console.log(chalk.dim(` Linha: ${issue.line}`));
|
|
143
|
-
}
|
|
144
|
-
if (issue.code) {
|
|
145
|
-
console.log(chalk.dim(` Codigo: ${issue.code}`));
|
|
146
|
-
}
|
|
147
|
-
console.log('');
|
|
148
|
-
});
|
|
149
|
-
});
|
|
150
|
-
} else {
|
|
151
|
-
allIssues.forEach((issue) => {
|
|
152
|
-
const icon =
|
|
153
|
-
issue.type === 'error'
|
|
154
|
-
? '\u274C'
|
|
155
|
-
: issue.type === 'warning'
|
|
156
|
-
? '\u26A0\uFE0F'
|
|
157
|
-
: '\u2139\uFE0F';
|
|
158
|
-
|
|
159
|
-
const location = issue.line ? `${issue.file}:${issue.line}` : issue.file;
|
|
160
|
-
|
|
161
|
-
console.log(`${icon} ${issue.message}`);
|
|
162
|
-
if (issue.suggestion) {
|
|
163
|
-
console.log(chalk.gray(` \u2192 ${issue.suggestion}`));
|
|
164
|
-
}
|
|
165
|
-
console.log(chalk.dim(` ${location}`));
|
|
166
|
-
console.log('');
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Summary
|
|
171
|
-
const { errors, warnings, infos } = countIssues(allIssues);
|
|
172
|
-
const summaryColor = errors > 0 ? chalk.red : chalk.yellow;
|
|
173
|
-
|
|
174
|
-
console.log(chalk.gray('-'.repeat(60)));
|
|
175
|
-
console.log(
|
|
176
|
-
summaryColor(
|
|
177
|
-
`\n${files.length} arquivo(s) analisado(s) | ${errors} erro(s) | ${warnings} warning(s) | ${infos} info(s)\n`
|
|
178
|
-
)
|
|
179
|
-
);
|
|
180
|
-
|
|
181
|
-
// Recommendation
|
|
182
|
-
if (errors > 0) {
|
|
183
|
-
console.log(
|
|
184
|
-
chalk.cyan(
|
|
185
|
-
'\uD83D\uDCD6 Consulte: framework/standards/backend/database/ef-core.md para padroes corretos'
|
|
186
|
-
)
|
|
187
|
-
);
|
|
188
|
-
console.log('');
|
|
189
|
-
process.exit(1);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
export default analyzeBlazorConcurrencyCommand;
|