@polymorphism-tech/morph-spec 4.8.19 → 4.10.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 +21 -0
- package/README.md +2 -2
- package/bin/morph-spec.js +44 -55
- package/bin/task-manager.js +133 -20
- package/bin/validate.js +67 -33
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +201 -203
- package/docs/QUICKSTART.md +2 -2
- package/framework/CLAUDE.md +99 -77
- package/framework/agents.json +734 -182
- package/framework/commands/commit.md +166 -0
- package/framework/commands/morph-apply.md +13 -2
- package/framework/commands/morph-archive.md +8 -2
- package/framework/commands/morph-infra.md +6 -0
- package/framework/commands/morph-preflight.md +6 -0
- package/framework/commands/morph-proposal.md +56 -7
- package/framework/commands/morph-status.md +6 -0
- package/framework/commands/morph-troubleshoot.md +6 -0
- package/framework/hooks/claude-code/notification/approval-reminder.js +3 -2
- package/framework/hooks/claude-code/post-tool-use/context-refresh.js +1 -1
- package/framework/hooks/claude-code/post-tool-use/dispatch.js +155 -32
- package/framework/hooks/claude-code/post-tool-use/skill-reminder.js +78 -0
- package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +8 -17
- package/framework/hooks/claude-code/pre-compact/save-morph-context.js +16 -3
- package/framework/hooks/claude-code/pre-tool-use/enforce-phase-writes.js +4 -3
- package/framework/hooks/claude-code/pre-tool-use/protect-spec-files.js +4 -3
- package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +60 -0
- package/framework/hooks/claude-code/session-start/inject-morph-context.js +124 -2
- package/framework/hooks/claude-code/session-start/post-compact-restore.js +41 -0
- package/framework/hooks/claude-code/statusline.py +76 -30
- package/framework/hooks/claude-code/stop/validate-completion.js +2 -15
- package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +23 -5
- package/framework/hooks/claude-code/user-prompt/set-terminal-title.js +14 -6
- package/framework/hooks/shared/activity-logger.js +0 -24
- package/framework/hooks/shared/compact-restore.js +100 -0
- package/framework/hooks/shared/dispatch-helpers.js +116 -0
- package/framework/hooks/shared/phase-utils.js +12 -5
- package/framework/hooks/shared/skill-reminder-helpers.js +79 -0
- package/framework/hooks/shared/stale-task-reset.js +57 -0
- package/framework/hooks/shared/state-reader.js +29 -5
- package/framework/hooks/shared/worktree-helpers.js +53 -0
- package/framework/phases.json +69 -14
- package/framework/rules/morph-workflow.md +88 -86
- package/framework/skills/level-0-meta/mcp-registry.json +86 -51
- package/framework/skills/level-0-meta/{brainstorming → morph-brainstorming}/SKILL.md +14 -17
- package/framework/skills/level-0-meta/morph-checklist/SKILL.md +2 -2
- package/framework/skills/level-0-meta/{code-review → morph-code-review}/SKILL.md +2 -2
- package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/SKILL.md +163 -163
- package/framework/skills/level-0-meta/{frontend-review → morph-frontend-review}/SKILL.md +9 -9
- package/framework/skills/level-0-meta/morph-init/SKILL.md +77 -12
- package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/SKILL.md +62 -15
- package/framework/skills/level-0-meta/morph-replicate/SKILL.md +5 -5
- package/framework/skills/level-0-meta/morph-replicate/references/blazor-html-mapping.md +1 -1
- package/framework/skills/level-0-meta/{simulation-checklist → morph-simulation-checklist}/SKILL.md +1 -1
- package/framework/skills/level-0-meta/{terminal-title → morph-terminal-title}/SKILL.md +2 -2
- package/framework/skills/level-0-meta/{tool-usage-guide → morph-tool-usage-guide}/SKILL.md +3 -4
- package/framework/skills/level-0-meta/{tool-usage-guide → morph-tool-usage-guide}/references/tools-per-phase.md +7 -7
- package/framework/skills/level-0-meta/{verification-before-completion → morph-verification-before-completion}/SKILL.md +2 -2
- package/framework/skills/level-0-meta/{verification-before-completion → morph-verification-before-completion}/scripts/check-phase-outputs.mjs +2 -2
- package/framework/skills/level-1-workflows/morph-phase-clarify/SKILL.md +238 -0
- package/framework/skills/level-1-workflows/{phase-codebase-analysis → morph-phase-codebase-analysis}/SKILL.md +3 -3
- package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +507 -0
- package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/SKILL.md +168 -27
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/code-quality-reviewer-prompt.md +50 -0
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +45 -0
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/spec-reviewer-prompt.md +47 -0
- package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +254 -0
- package/framework/skills/level-1-workflows/{phase-setup → morph-phase-setup}/SKILL.md +50 -3
- package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/SKILL.md +48 -11
- package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/scripts/validate-tasks.mjs +3 -3
- package/framework/skills/level-1-workflows/{phase-uiux → morph-phase-uiux}/SKILL.md +46 -11
- package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +97 -0
- package/framework/standards/STANDARDS.json +640 -88
- package/framework/standards/infrastructure/vercel/vercel-database.md +106 -0
- package/framework/standards/integration/mcp/mcp-tools.md +25 -7
- package/framework/templates/REGISTRY.json +1825 -1909
- package/framework/templates/context/CONTEXT-FEATURE.md +276 -276
- package/framework/templates/docs/onboarding.md +3 -7
- package/package.json +2 -7
- package/src/commands/agents/dispatch-agents.js +104 -6
- package/src/commands/mcp/mcp-setup.js +39 -2
- package/src/commands/phase/phase-reset.js +74 -0
- package/src/commands/project/doctor.js +34 -51
- package/src/commands/project/init.js +1 -1
- package/src/commands/project/status.js +2 -2
- package/src/commands/project/update.js +381 -365
- package/src/commands/project/worktree.js +154 -0
- package/src/commands/scope/escalate.js +215 -0
- package/src/commands/state/advance-phase.js +132 -68
- package/src/commands/state/approve.js +2 -2
- package/src/commands/state/index.js +7 -8
- package/src/commands/state/phase-runner.js +1 -1
- package/src/commands/state/state.js +61 -6
- package/src/commands/task/expand.js +100 -0
- package/src/commands/tasks/task.js +78 -99
- package/src/commands/templates/template-render.js +93 -173
- package/src/commands/trust/trust.js +26 -21
- package/src/core/paths/output-schema.js +19 -3
- package/src/core/state/phase-state-machine.js +7 -4
- package/src/core/state/state-manager.js +32 -57
- package/src/core/workflows/workflow-detector.js +9 -87
- package/src/lib/detectors/claude-config-detector.js +93 -347
- package/src/lib/detectors/design-system-detector.js +189 -189
- package/src/lib/detectors/index.js +155 -57
- package/src/lib/generators/context-generator.js +2 -2
- package/src/lib/installers/mcp-installer.js +37 -5
- package/src/lib/phase-chain/phase-validator.js +336 -0
- package/src/lib/scope/impact-analyzer.js +106 -0
- package/src/lib/stack/stack-profile.js +88 -0
- package/src/lib/tasks/task-classifier.js +16 -0
- package/src/lib/tasks/task-parser.js +1 -1
- package/src/lib/tasks/test-runner.js +77 -0
- package/src/lib/trust/trust-manager.js +32 -144
- package/src/lib/validators/shared/emit-validator-dispatch.js +64 -0
- package/src/lib/validators/spec-validator.js +58 -4
- package/src/lib/validators/validation-runner.js +23 -11
- package/src/scripts/setup-infra.js +255 -224
- package/src/utils/agents-installer.js +34 -14
- package/src/utils/banner.js +1 -1
- package/src/utils/claude-settings-manager.js +1 -1
- package/src/utils/file-copier.js +1 -1
- package/src/utils/hooks-installer.js +272 -8
- package/framework/hooks/dev/check-sync-health.js +0 -117
- package/framework/hooks/dev/guard-version-numbers.js +0 -57
- package/framework/hooks/dev/sync-standards-registry.js +0 -60
- package/framework/hooks/dev/sync-template-registry.js +0 -60
- package/framework/hooks/dev/validate-skill-format.js +0 -70
- package/framework/hooks/dev/validate-standard-format.js +0 -73
- package/framework/skills/level-1-workflows/phase-clarify/SKILL.md +0 -190
- package/framework/skills/level-1-workflows/phase-design/SKILL.md +0 -366
- package/framework/templates/meta-prompts/hops/hop-retry.md +0 -78
- package/framework/templates/meta-prompts/hops/hop-validation.md +0 -97
- package/framework/templates/meta-prompts/hops/hop-wrapper.md +0 -36
- package/framework/workflows/configs/design-impl.json +0 -49
- package/framework/workflows/configs/express.json +0 -45
- package/framework/workflows/configs/fast-track.json +0 -42
- package/framework/workflows/configs/full-morph.json +0 -79
- package/framework/workflows/configs/fusion.json +0 -39
- package/framework/workflows/configs/long-running.json +0 -33
- package/framework/workflows/configs/spec-only.json +0 -43
- package/framework/workflows/configs/ui-refresh.json +0 -49
- package/framework/workflows/configs/zero-touch.json +0 -82
- package/src/commands/project/index.js +0 -8
- package/src/commands/project/monitor.js +0 -295
- package/src/commands/project/tutorial.js +0 -115
- package/src/commands/state/validate-phase.js +0 -238
- package/src/commands/templates/generate-contracts.js +0 -445
- package/src/core/index.js +0 -10
- package/src/core/orchestrator.js +0 -171
- package/src/core/registry/command-registry.js +0 -28
- package/src/core/registry/index.js +0 -8
- package/src/core/registry/validator-registry.js +0 -204
- package/src/core/state/index.js +0 -8
- package/src/core/templates/index.js +0 -9
- package/src/core/templates/template-data-sources.js +0 -325
- package/src/core/templates/template-validator.js +0 -296
- package/src/core/workflows/index.js +0 -7
- package/src/generator/config-generator.js +0 -206
- package/src/generator/templates/config.json.template +0 -40
- package/src/generator/templates/project.md.template +0 -67
- package/src/lib/agents/micro-agent-factory.js +0 -161
- package/src/lib/analysis/complexity-analyzer.js +0 -441
- package/src/lib/analysis/index.js +0 -7
- package/src/lib/analytics/analytics-engine.js +0 -345
- package/src/lib/checkpoints/checkpoint-hooks.js +0 -298
- package/src/lib/checkpoints/index.js +0 -7
- package/src/lib/context/context-bundler.js +0 -241
- package/src/lib/context/context-optimizer.js +0 -212
- package/src/lib/context/context-tracker.js +0 -273
- package/src/lib/context/core-four-tracker.js +0 -201
- package/src/lib/context/mcp-optimizer.js +0 -200
- package/src/lib/detectors/config-detector.js +0 -223
- package/src/lib/detectors/standards-generator.js +0 -335
- package/src/lib/detectors/structure-detector.js +0 -275
- package/src/lib/execution/fusion-executor.js +0 -304
- package/src/lib/execution/parallel-executor.js +0 -270
- package/src/lib/hooks/stop-hook-executor.js +0 -286
- package/src/lib/hops/hop-composer.js +0 -221
- package/src/lib/monitor/agent-resolver.js +0 -144
- package/src/lib/monitor/renderer.js +0 -230
- package/src/lib/orchestration/index.js +0 -7
- package/src/lib/orchestration/team-orchestrator.js +0 -404
- package/src/lib/phase-chain/eligibility-checker.js +0 -243
- package/src/lib/threads/thread-coordinator.js +0 -238
- package/src/lib/threads/thread-manager.js +0 -317
- package/src/lib/tracking/artifact-trail.js +0 -202
- package/src/sanitizer/context-sanitizer.js +0 -221
- package/src/sanitizer/patterns.js +0 -163
- package/src/scanner/project-scanner.js +0 -242
- package/src/ui/diff-display.js +0 -91
- package/src/ui/interactive-wizard.js +0 -96
- package/src/ui/user-review.js +0 -211
- package/src/ui/wizard-questions.js +0 -188
- package/src/utils/color-utils.js +0 -70
- package/src/utils/process-handler.js +0 -97
- package/src/writer/file-writer.js +0 -86
- /package/framework/skills/level-0-meta/{brainstorming → morph-brainstorming}/references/proposal-example.md +0 -0
- /package/framework/skills/level-0-meta/{code-review → morph-code-review}/references/review-example.md +0 -0
- /package/framework/skills/level-0-meta/{code-review → morph-code-review}/references/review-guidelines.md +0 -0
- /package/framework/skills/level-0-meta/{code-review → morph-code-review}/scripts/scan-csharp.mjs +0 -0
- /package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/references/review-example-nextjs.md +0 -0
- /package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/scripts/scan-nextjs.mjs +0 -0
- /package/framework/skills/level-0-meta/{frontend-review → morph-frontend-review}/scripts/scan-accessibility.mjs +0 -0
- /package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/scripts/detect-dev-server.mjs +0 -0
- /package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/scripts/detect-stack.mjs +0 -0
- /package/framework/skills/level-0-meta/{terminal-title → morph-terminal-title}/scripts/set_title.sh +0 -0
- /package/framework/skills/level-1-workflows/{phase-clarify → morph-phase-clarify}/references/clarifications-example.md +0 -0
- /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/architecture-analysis-guide.md +0 -0
- /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/spec-authoring-guide.md +0 -0
- /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/spec-example.md +0 -0
- /package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/references/recap-example.md +0 -0
- /package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/references/vsa-implementation-guide.md +0 -0
- /package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/references/task-planning-patterns.md +0 -0
- /package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/references/tasks-example.md +0 -0
|
@@ -1,296 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Template Validator - Validates Handlebars templates
|
|
3
|
-
* Checks for:
|
|
4
|
-
* - Required placeholders are present
|
|
5
|
-
* - No deprecated pre-computed variables ({FEATURE_NAME_PASCAL})
|
|
6
|
-
* - Valid Handlebars syntax
|
|
7
|
-
* - Proper helper usage
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { readFileSync } from 'fs';
|
|
11
|
-
import { getTemplateById, getAllTemplates } from './template-registry.js';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Deprecated placeholder patterns (v1.0 pre-computed variables)
|
|
15
|
-
*/
|
|
16
|
-
const DEPRECATED_PATTERNS = [
|
|
17
|
-
{ pattern: /\{\{FEATURE_NAME_PASCAL\}\}/g, replacement: '{{pascalCase FEATURE_NAME}}' },
|
|
18
|
-
{ pattern: /\{\{FEATURE_NAME_CAMEL\}\}/g, replacement: '{{camelCase FEATURE_NAME}}' },
|
|
19
|
-
{ pattern: /\{\{FEATURE_NAME_SNAKE\}\}/g, replacement: '{{snakeCase FEATURE_NAME}}' },
|
|
20
|
-
{ pattern: /\{\{FEATURE_NAME_UPPER_SNAKE\}\}/g, replacement: '{{upperSnakeCase FEATURE_NAME}}' },
|
|
21
|
-
{ pattern: /\{\{FEATURE_NAME_TITLE\}\}/g, replacement: '{{titleCase FEATURE_NAME}}' },
|
|
22
|
-
{ pattern: /\{\{FEATURE_NAME_KEBAB\}\}/g, replacement: '{{kebabCase FEATURE_NAME}}' },
|
|
23
|
-
];
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Old template syntax (non-Handlebars)
|
|
27
|
-
*/
|
|
28
|
-
const OLD_SYNTAX_PATTERNS = [
|
|
29
|
-
{ pattern: /\{Feature\}/g, description: 'Old {Feature} syntax (use {{pascalCase FEATURE_NAME}})' },
|
|
30
|
-
{ pattern: /\{feature\}/g, description: 'Old {feature} syntax (use {{kebabCase FEATURE_NAME}})' },
|
|
31
|
-
];
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Validation result
|
|
35
|
-
*/
|
|
36
|
-
class ValidationResult {
|
|
37
|
-
constructor(templateId, templatePath) {
|
|
38
|
-
this.templateId = templateId;
|
|
39
|
-
this.templatePath = templatePath;
|
|
40
|
-
this.valid = true;
|
|
41
|
-
this.errors = [];
|
|
42
|
-
this.warnings = [];
|
|
43
|
-
this.info = [];
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
addError(message) {
|
|
47
|
-
this.errors.push(message);
|
|
48
|
-
this.valid = false;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
addWarning(message) {
|
|
52
|
-
this.warnings.push(message);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
addInfo(message) {
|
|
56
|
-
this.info.push(message);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
get hasIssues() {
|
|
60
|
-
return this.errors.length > 0 || this.warnings.length > 0;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Validates a single template
|
|
66
|
-
* @param {string} templateId - Template ID
|
|
67
|
-
* @param {string} projectPath - Project root path
|
|
68
|
-
* @returns {ValidationResult}
|
|
69
|
-
*/
|
|
70
|
-
export function validateTemplate(templateId, projectPath = process.cwd()) {
|
|
71
|
-
const template = getTemplateById(templateId, projectPath);
|
|
72
|
-
|
|
73
|
-
if (!template) {
|
|
74
|
-
const result = new ValidationResult(templateId, null);
|
|
75
|
-
result.addError(`Template not found: ${templateId}`);
|
|
76
|
-
return result;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const result = new ValidationResult(templateId, template.path);
|
|
80
|
-
|
|
81
|
-
// Read template content
|
|
82
|
-
let content;
|
|
83
|
-
try {
|
|
84
|
-
const fullPath = `${projectPath}/framework/templates/${template.path}`;
|
|
85
|
-
content = readFileSync(fullPath, 'utf-8');
|
|
86
|
-
} catch (error) {
|
|
87
|
-
result.addError(`Failed to read template: ${error.message}`);
|
|
88
|
-
return result;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Check for deprecated pre-computed variables
|
|
92
|
-
for (const { pattern, replacement } of DEPRECATED_PATTERNS) {
|
|
93
|
-
const matches = content.match(pattern);
|
|
94
|
-
if (matches) {
|
|
95
|
-
result.addError(
|
|
96
|
-
`Deprecated placeholder found: ${matches[0]} (use ${replacement} instead)`
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Check for old non-Handlebars syntax
|
|
102
|
-
for (const { pattern, description } of OLD_SYNTAX_PATTERNS) {
|
|
103
|
-
const matches = content.match(pattern);
|
|
104
|
-
if (matches) {
|
|
105
|
-
const count = matches.length;
|
|
106
|
-
result.addWarning(
|
|
107
|
-
`${count} occurrence(s) of old syntax: ${description}`
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Check for required placeholders if specified
|
|
113
|
-
if (template.placeholders && template.placeholders.length > 0) {
|
|
114
|
-
const missingPlaceholders = [];
|
|
115
|
-
|
|
116
|
-
for (const placeholder of template.placeholders) {
|
|
117
|
-
// Check for exact placeholder or helper usage
|
|
118
|
-
const exactMatch = new RegExp(`\\{\\{${placeholder}\\}\\}`, 'g');
|
|
119
|
-
const helperMatch = new RegExp(`\\{\\{\\w+\\s+${placeholder}\\}\\}`, 'g');
|
|
120
|
-
|
|
121
|
-
if (!exactMatch.test(content) && !helperMatch.test(content)) {
|
|
122
|
-
missingPlaceholders.push(placeholder);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (missingPlaceholders.length > 0) {
|
|
127
|
-
result.addWarning(
|
|
128
|
-
`Template declares placeholders but they're not used: ${missingPlaceholders.join(', ')}`
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Check for balanced Handlebars blocks
|
|
134
|
-
const openBlocks = (content.match(/\{\{#\w+/g) || []).length;
|
|
135
|
-
const closeBlocks = (content.match(/\{\{\/\w+/g) || []).length;
|
|
136
|
-
|
|
137
|
-
if (openBlocks !== closeBlocks) {
|
|
138
|
-
result.addError(
|
|
139
|
-
`Unbalanced Handlebars blocks: ${openBlocks} opening, ${closeBlocks} closing`
|
|
140
|
-
);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Info: Count Handlebars placeholders
|
|
144
|
-
const placeholderMatches = content.match(/\{\{[^}]+\}\}/g) || [];
|
|
145
|
-
const uniquePlaceholders = [...new Set(placeholderMatches)];
|
|
146
|
-
result.addInfo(`Found ${uniquePlaceholders.length} unique Handlebars expressions`);
|
|
147
|
-
|
|
148
|
-
// Info: Check if template uses helpers
|
|
149
|
-
const helperMatches = content.match(/\{\{(pascalCase|camelCase|snakeCase|titleCase|kebabCase|upperSnakeCase|pluralize|singularize|formatDate|uppercase|lowercase|capitalize|trim|replace|concat|substr|startsWith|endsWith|contains|length|add|subtract|multiply|divide|mod|round|join|first|last|slice|now|year|default|json)/g) || [];
|
|
150
|
-
if (helperMatches.length > 0) {
|
|
151
|
-
const uniqueHelpers = [...new Set(helperMatches.map(m => m.replace('{{', '')))];
|
|
152
|
-
result.addInfo(`Uses helpers: ${uniqueHelpers.join(', ')}`);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return result;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Validates all templates
|
|
160
|
-
* @param {string} projectPath - Project root path
|
|
161
|
-
* @param {Object} options - Validation options
|
|
162
|
-
* @returns {Array<ValidationResult>}
|
|
163
|
-
*/
|
|
164
|
-
export function validateAllTemplates(projectPath = process.cwd(), options = {}) {
|
|
165
|
-
const {
|
|
166
|
-
skipDeprecated = true,
|
|
167
|
-
category = null,
|
|
168
|
-
technology = null,
|
|
169
|
-
} = options;
|
|
170
|
-
|
|
171
|
-
const templates = getAllTemplates(projectPath);
|
|
172
|
-
const results = [];
|
|
173
|
-
|
|
174
|
-
for (const template of templates) {
|
|
175
|
-
// Skip deprecated if requested
|
|
176
|
-
if (skipDeprecated && template.deprecated) {
|
|
177
|
-
continue;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// Filter by category if specified
|
|
181
|
-
if (category && template.category !== category) {
|
|
182
|
-
continue;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// Filter by technology if specified
|
|
186
|
-
if (technology && template.technology !== technology) {
|
|
187
|
-
continue;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const result = validateTemplate(template.id, projectPath);
|
|
191
|
-
results.push(result);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
return results;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Generates a validation summary
|
|
199
|
-
* @param {Array<ValidationResult>} results - Validation results
|
|
200
|
-
* @returns {Object}
|
|
201
|
-
*/
|
|
202
|
-
export function summarizeValidation(results) {
|
|
203
|
-
const summary = {
|
|
204
|
-
total: results.length,
|
|
205
|
-
valid: 0,
|
|
206
|
-
hasErrors: 0,
|
|
207
|
-
hasWarnings: 0,
|
|
208
|
-
errors: [],
|
|
209
|
-
warnings: [],
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
for (const result of results) {
|
|
213
|
-
if (result.valid && result.warnings.length === 0) {
|
|
214
|
-
summary.valid++;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
if (result.errors.length > 0) {
|
|
218
|
-
summary.hasErrors++;
|
|
219
|
-
summary.errors.push({
|
|
220
|
-
templateId: result.templateId,
|
|
221
|
-
errors: result.errors,
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
if (result.warnings.length > 0) {
|
|
226
|
-
summary.hasWarnings++;
|
|
227
|
-
summary.warnings.push({
|
|
228
|
-
templateId: result.templateId,
|
|
229
|
-
warnings: result.warnings,
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
return summary;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Formats validation results for display
|
|
239
|
-
* @param {Array<ValidationResult>} results - Validation results
|
|
240
|
-
* @param {Object} options - Display options
|
|
241
|
-
* @returns {string}
|
|
242
|
-
*/
|
|
243
|
-
export function formatValidationResults(results, options = {}) {
|
|
244
|
-
const { showInfo = false, showValid = false } = options;
|
|
245
|
-
|
|
246
|
-
let output = '';
|
|
247
|
-
let validCount = 0;
|
|
248
|
-
let errorCount = 0;
|
|
249
|
-
let warningCount = 0;
|
|
250
|
-
|
|
251
|
-
for (const result of results) {
|
|
252
|
-
if (result.valid && result.warnings.length === 0) {
|
|
253
|
-
validCount++;
|
|
254
|
-
if (showValid) {
|
|
255
|
-
output += `✅ ${result.templateId}\n`;
|
|
256
|
-
}
|
|
257
|
-
continue;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
if (result.errors.length > 0) {
|
|
261
|
-
errorCount++;
|
|
262
|
-
output += `\n❌ ${result.templateId}\n`;
|
|
263
|
-
output += ` Path: ${result.templatePath}\n`;
|
|
264
|
-
for (const error of result.errors) {
|
|
265
|
-
output += ` ERROR: ${error}\n`;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
if (result.warnings.length > 0) {
|
|
270
|
-
warningCount++;
|
|
271
|
-
if (result.errors.length === 0) {
|
|
272
|
-
output += `\n⚠️ ${result.templateId}\n`;
|
|
273
|
-
output += ` Path: ${result.templatePath}\n`;
|
|
274
|
-
}
|
|
275
|
-
for (const warning of result.warnings) {
|
|
276
|
-
output += ` WARNING: ${warning}\n`;
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
if (showInfo && result.info.length > 0) {
|
|
281
|
-
for (const info of result.info) {
|
|
282
|
-
output += ` INFO: ${info}\n`;
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// Summary
|
|
288
|
-
output += `\n${'='.repeat(60)}\n`;
|
|
289
|
-
output += `SUMMARY:\n`;
|
|
290
|
-
output += ` Total templates: ${results.length}\n`;
|
|
291
|
-
output += ` ✅ Valid: ${validCount}\n`;
|
|
292
|
-
output += ` ❌ Errors: ${errorCount}\n`;
|
|
293
|
-
output += ` ⚠️ Warnings: ${warningCount}\n`;
|
|
294
|
-
|
|
295
|
-
return output;
|
|
296
|
-
}
|
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview ConfigGenerator - Renders templates and generates config files
|
|
3
|
-
* @module morph-spec/generator/config-generator
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { readFile, access, copyFile } from 'fs/promises';
|
|
7
|
-
import { join, dirname } from 'path';
|
|
8
|
-
import Handlebars from 'handlebars';
|
|
9
|
-
import { fileURLToPath } from 'url';
|
|
10
|
-
import Ajv from 'ajv';
|
|
11
|
-
|
|
12
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
-
const __dirname = dirname(__filename);
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @typedef {import('../types/index.js').ProjectConfig} ProjectConfig
|
|
17
|
-
* @typedef {import('../types/index.js').GeneratedConfigs} GeneratedConfigs
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Validation Error
|
|
22
|
-
*/
|
|
23
|
-
export class ValidationError extends Error {
|
|
24
|
-
constructor(message, field, value) {
|
|
25
|
-
super(message);
|
|
26
|
-
this.name = 'ValidationError';
|
|
27
|
-
this.field = field;
|
|
28
|
-
this.value = value;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* ConfigGenerator - Generates configuration files from ProjectConfig
|
|
34
|
-
* @class
|
|
35
|
-
*/
|
|
36
|
-
export class ConfigGenerator {
|
|
37
|
-
constructor() {
|
|
38
|
-
this.projectMdTemplate = null;
|
|
39
|
-
this.configJsonTemplate = null;
|
|
40
|
-
this.ajv = new Ajv({ allErrors: true });
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Load templates from filesystem
|
|
45
|
-
* @private
|
|
46
|
-
*/
|
|
47
|
-
async loadTemplates() {
|
|
48
|
-
if (this.projectMdTemplate && this.configJsonTemplate) {
|
|
49
|
-
return; // Already loaded
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const templatesDir = join(__dirname, 'templates');
|
|
53
|
-
|
|
54
|
-
const [projectMdSource, configJsonSource] = await Promise.all([
|
|
55
|
-
readFile(join(templatesDir, 'project.md.template'), 'utf-8'),
|
|
56
|
-
readFile(join(templatesDir, 'config.json.template'), 'utf-8')
|
|
57
|
-
]);
|
|
58
|
-
|
|
59
|
-
this.projectMdTemplate = Handlebars.compile(projectMdSource);
|
|
60
|
-
this.configJsonTemplate = Handlebars.compile(configJsonSource);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Generate configuration files from project config
|
|
65
|
-
* @param {ProjectConfig} projectConfig - Detected project config
|
|
66
|
-
* @returns {Promise<GeneratedConfigs>}
|
|
67
|
-
*/
|
|
68
|
-
async generate(projectConfig) {
|
|
69
|
-
// Load templates if not already loaded
|
|
70
|
-
await this.loadTemplates();
|
|
71
|
-
|
|
72
|
-
// Add generation timestamp
|
|
73
|
-
const context = {
|
|
74
|
-
...projectConfig,
|
|
75
|
-
generatedAt: new Date().toISOString()
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
// Render templates
|
|
79
|
-
const projectMd = this.renderProjectMd(context);
|
|
80
|
-
const configJson = this.renderConfigJson(context);
|
|
81
|
-
|
|
82
|
-
// Parse config.json to object
|
|
83
|
-
const configObject = JSON.parse(configJson);
|
|
84
|
-
|
|
85
|
-
// Validate config.json (optional, but good practice)
|
|
86
|
-
// Note: agent-schema.json validation would happen here if we had the schema
|
|
87
|
-
// For now, we just ensure it's valid JSON
|
|
88
|
-
|
|
89
|
-
return {
|
|
90
|
-
projectMd,
|
|
91
|
-
configJson,
|
|
92
|
-
configObject
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Render project.md template
|
|
98
|
-
* @param {ProjectConfig} config - Project config
|
|
99
|
-
* @returns {string} Rendered markdown
|
|
100
|
-
*/
|
|
101
|
-
renderProjectMd(config) {
|
|
102
|
-
if (!this.projectMdTemplate) {
|
|
103
|
-
throw new Error('Templates not loaded. Call loadTemplates() first.');
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return this.projectMdTemplate(config);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Render config.json template
|
|
111
|
-
* @param {ProjectConfig} config - Project config
|
|
112
|
-
* @returns {string} Rendered JSON string
|
|
113
|
-
*/
|
|
114
|
-
renderConfigJson(config) {
|
|
115
|
-
if (!this.configJsonTemplate) {
|
|
116
|
-
throw new Error('Templates not loaded. Call loadTemplates() first.');
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const rendered = this.configJsonTemplate(config);
|
|
120
|
-
|
|
121
|
-
// Validate that rendered output is valid JSON
|
|
122
|
-
try {
|
|
123
|
-
JSON.parse(rendered);
|
|
124
|
-
return rendered;
|
|
125
|
-
} catch (error) {
|
|
126
|
-
throw new ValidationError(
|
|
127
|
-
`Rendered config.json is not valid JSON: ${error.message}`,
|
|
128
|
-
'configJson',
|
|
129
|
-
rendered
|
|
130
|
-
);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Validate config.json against agent schema
|
|
136
|
-
* @param {string} configJson - JSON string
|
|
137
|
-
* @returns {boolean} True if valid
|
|
138
|
-
* @throws {ValidationError} If validation fails
|
|
139
|
-
*/
|
|
140
|
-
validateConfigJson(configJson) {
|
|
141
|
-
// Parse JSON
|
|
142
|
-
let parsed;
|
|
143
|
-
try {
|
|
144
|
-
parsed = JSON.parse(configJson);
|
|
145
|
-
} catch (error) {
|
|
146
|
-
throw new ValidationError(
|
|
147
|
-
`Invalid JSON: ${error.message}`,
|
|
148
|
-
'configJson',
|
|
149
|
-
configJson
|
|
150
|
-
);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Basic validation - ensure required fields exist
|
|
154
|
-
const requiredFields = ['name', 'type', 'description', 'stack', 'architecture'];
|
|
155
|
-
for (const field of requiredFields) {
|
|
156
|
-
if (!parsed[field]) {
|
|
157
|
-
throw new ValidationError(
|
|
158
|
-
`Missing required field: ${field}`,
|
|
159
|
-
field,
|
|
160
|
-
parsed
|
|
161
|
-
);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Validate stack.backend is required
|
|
166
|
-
if (!parsed.stack || !parsed.stack.backend) {
|
|
167
|
-
throw new ValidationError(
|
|
168
|
-
'stack.backend is required',
|
|
169
|
-
'stack.backend',
|
|
170
|
-
parsed
|
|
171
|
-
);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
return true;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Backup existing configuration files
|
|
179
|
-
* @param {string} cwd - Current working directory
|
|
180
|
-
* @returns {Promise<void>}
|
|
181
|
-
*/
|
|
182
|
-
async backupExisting(cwd) {
|
|
183
|
-
const projectMdPath = join(cwd, '.morph', 'project.md');
|
|
184
|
-
const configJsonPath = join(cwd, '.morph', 'config', 'config.json');
|
|
185
|
-
|
|
186
|
-
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
187
|
-
|
|
188
|
-
// Backup project.md if exists
|
|
189
|
-
try {
|
|
190
|
-
await access(projectMdPath);
|
|
191
|
-
const backupPath = join(cwd, '.morph', `project.md.${timestamp}.backup`);
|
|
192
|
-
await copyFile(projectMdPath, backupPath);
|
|
193
|
-
} catch (error) {
|
|
194
|
-
// File doesn't exist, no need to backup
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Backup config.json if exists
|
|
198
|
-
try {
|
|
199
|
-
await access(configJsonPath);
|
|
200
|
-
const backupPath = join(cwd, '.morph', 'config', `config.json.${timestamp}.backup`);
|
|
201
|
-
await copyFile(configJsonPath, backupPath);
|
|
202
|
-
} catch (error) {
|
|
203
|
-
// File doesn't exist, no need to backup
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "../schema/agent-schema.json",
|
|
3
|
-
"name": "{{name}}",
|
|
4
|
-
"type": "{{type}}",
|
|
5
|
-
"description": "{{description}}",
|
|
6
|
-
"stack": {
|
|
7
|
-
{{#if stack.frontend}}
|
|
8
|
-
"frontend": {
|
|
9
|
-
"tech": "{{stack.frontend.tech}}",
|
|
10
|
-
"version": "{{stack.frontend.version}}"{{#if stack.frontend.details}},
|
|
11
|
-
"details": "{{stack.frontend.details}}"{{/if}}
|
|
12
|
-
},
|
|
13
|
-
{{/if}}
|
|
14
|
-
"backend": {
|
|
15
|
-
"tech": "{{stack.backend.tech}}",
|
|
16
|
-
"version": "{{stack.backend.version}}"{{#if stack.backend.details}},
|
|
17
|
-
"details": "{{stack.backend.details}}"{{/if}}
|
|
18
|
-
}{{#if stack.database}},
|
|
19
|
-
"database": {
|
|
20
|
-
"tech": "{{stack.database.tech}}",
|
|
21
|
-
"version": "{{stack.database.version}}"{{#if stack.database.details}},
|
|
22
|
-
"details": "{{stack.database.details}}"{{/if}}
|
|
23
|
-
}{{/if}}{{#if stack.hosting}},
|
|
24
|
-
"hosting": "{{stack.hosting}}"{{/if}}
|
|
25
|
-
},
|
|
26
|
-
"architecture": "{{architecture}}",
|
|
27
|
-
"conventions": "{{conventions}}",
|
|
28
|
-
"infrastructure": {
|
|
29
|
-
"azure": {{hasAzure}},
|
|
30
|
-
"docker": {{hasDocker}},
|
|
31
|
-
"devops": {{hasDevOps}}
|
|
32
|
-
}{{#if repository}},
|
|
33
|
-
"repository": "{{repository}}"{{/if}},
|
|
34
|
-
"meta": {
|
|
35
|
-
"generatedBy": "morph-spec-cli",
|
|
36
|
-
"generatedAt": "{{generatedAt}}",
|
|
37
|
-
"llmConfidence": {{confidence}},
|
|
38
|
-
"autoDetected": true
|
|
39
|
-
}
|
|
40
|
-
}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
# {{name}}
|
|
2
|
-
|
|
3
|
-
> {{description}}
|
|
4
|
-
|
|
5
|
-
## Stack
|
|
6
|
-
|
|
7
|
-
| Component | Technology |
|
|
8
|
-
|-----------|------------|
|
|
9
|
-
{{#if stack.frontend}}
|
|
10
|
-
| **Frontend** | {{stack.frontend.tech}} {{stack.frontend.version}}{{#if stack.frontend.details}} - {{stack.frontend.details}}{{/if}} |
|
|
11
|
-
{{/if}}
|
|
12
|
-
| **Backend** | {{stack.backend.tech}} {{stack.backend.version}}{{#if stack.backend.details}} - {{stack.backend.details}}{{/if}} |
|
|
13
|
-
{{#if stack.database}}
|
|
14
|
-
| **Database** | {{stack.database.tech}} {{stack.database.version}}{{#if stack.database.details}} - {{stack.database.details}}{{/if}} |
|
|
15
|
-
{{/if}}
|
|
16
|
-
{{#if stack.hosting}}
|
|
17
|
-
| **Hosting** | {{stack.hosting}} |
|
|
18
|
-
{{/if}}
|
|
19
|
-
|
|
20
|
-
## Architecture
|
|
21
|
-
|
|
22
|
-
**Pattern:** {{architecture}}
|
|
23
|
-
|
|
24
|
-
{{projectStructure}}
|
|
25
|
-
|
|
26
|
-
## Code Conventions
|
|
27
|
-
|
|
28
|
-
{{conventions}}
|
|
29
|
-
|
|
30
|
-
## Infrastructure
|
|
31
|
-
|
|
32
|
-
{{#if hasAzure}}
|
|
33
|
-
- ✅ **Azure** - Uses Azure infrastructure (Bicep files detected)
|
|
34
|
-
{{else}}
|
|
35
|
-
- ❌ **Azure** - No Azure resources detected
|
|
36
|
-
{{/if}}
|
|
37
|
-
|
|
38
|
-
{{#if hasDocker}}
|
|
39
|
-
- ✅ **Docker** - Containerized (Dockerfile/docker-compose.yml detected)
|
|
40
|
-
{{else}}
|
|
41
|
-
- ❌ **Docker** - Not containerized
|
|
42
|
-
{{/if}}
|
|
43
|
-
|
|
44
|
-
{{#if hasDevOps}}
|
|
45
|
-
- ✅ **CI/CD** - Pipelines detected
|
|
46
|
-
{{else}}
|
|
47
|
-
- ❌ **CI/CD** - No pipelines detected
|
|
48
|
-
{{/if}}
|
|
49
|
-
|
|
50
|
-
{{#if repository}}
|
|
51
|
-
## Repository
|
|
52
|
-
|
|
53
|
-
{{repository}}
|
|
54
|
-
{{/if}}
|
|
55
|
-
|
|
56
|
-
{{#if warnings.length}}
|
|
57
|
-
## ⚠️ Warnings
|
|
58
|
-
|
|
59
|
-
{{#each warnings}}
|
|
60
|
-
- {{this}}
|
|
61
|
-
{{/each}}
|
|
62
|
-
{{/if}}
|
|
63
|
-
|
|
64
|
-
---
|
|
65
|
-
|
|
66
|
-
*Auto-generated by MORPH-SPEC CLI on {{generatedAt}}*
|
|
67
|
-
*LLM Confidence: {{confidence}}%*
|