@polymorphism-tech/morph-spec 4.8.19 → 4.9.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 (137) hide show
  1. package/CLAUDE.md +21 -0
  2. package/README.md +2 -2
  3. package/bin/morph-spec.js +15 -56
  4. package/bin/task-manager.js +115 -14
  5. package/bin/validate.js +67 -33
  6. package/claude-plugin.json +1 -1
  7. package/docs/CHEATSHEET.md +201 -203
  8. package/docs/QUICKSTART.md +2 -2
  9. package/framework/CLAUDE.md +21 -0
  10. package/framework/agents.json +698 -176
  11. package/framework/hooks/claude-code/post-tool-use/context-refresh.js +1 -1
  12. package/framework/hooks/claude-code/post-tool-use/dispatch.js +2 -2
  13. package/framework/hooks/claude-code/post-tool-use/skill-reminder.js +155 -0
  14. package/framework/hooks/claude-code/pre-tool-use/protect-spec-files.js +1 -1
  15. package/framework/hooks/claude-code/session-start/inject-morph-context.js +71 -2
  16. package/framework/hooks/claude-code/statusline.py +76 -30
  17. package/framework/hooks/claude-code/user-prompt/set-terminal-title.js +14 -6
  18. package/framework/hooks/shared/activity-logger.js +0 -24
  19. package/framework/hooks/shared/phase-utils.js +3 -0
  20. package/framework/hooks/shared/skill-reminder-helpers.js +79 -0
  21. package/framework/hooks/shared/stale-task-reset.js +57 -0
  22. package/framework/hooks/shared/state-reader.js +2 -2
  23. package/framework/hooks/shared/worktree-helpers.js +53 -0
  24. package/framework/phases.json +40 -8
  25. package/framework/skills/level-0-meta/brainstorming/SKILL.md +1 -1
  26. package/framework/skills/level-0-meta/code-review/SKILL.md +1 -1
  27. package/framework/skills/level-0-meta/code-review-nextjs/SKILL.md +163 -163
  28. package/framework/skills/level-0-meta/frontend-review/SKILL.md +5 -5
  29. package/framework/skills/level-0-meta/morph-checklist/SKILL.md +2 -2
  30. package/framework/skills/level-0-meta/morph-init/SKILL.md +5 -5
  31. package/framework/skills/level-0-meta/morph-replicate/SKILL.md +4 -4
  32. package/framework/skills/level-0-meta/morph-replicate/references/blazor-html-mapping.md +1 -1
  33. package/framework/skills/level-0-meta/post-implementation/SKILL.md +59 -12
  34. package/framework/skills/level-0-meta/simulation-checklist/SKILL.md +1 -1
  35. package/framework/skills/level-0-meta/terminal-title/SKILL.md +1 -1
  36. package/framework/skills/level-0-meta/tool-usage-guide/SKILL.md +1 -1
  37. package/framework/skills/level-0-meta/tool-usage-guide/references/tools-per-phase.md +6 -5
  38. package/framework/skills/level-0-meta/verification-before-completion/SKILL.md +1 -1
  39. package/framework/skills/level-1-workflows/phase-clarify/SKILL.md +215 -189
  40. package/framework/skills/level-1-workflows/phase-codebase-analysis/SKILL.md +251 -251
  41. package/framework/skills/level-1-workflows/phase-design/SKILL.md +382 -365
  42. package/framework/skills/level-1-workflows/phase-implement/SKILL.md +492 -450
  43. package/framework/skills/level-1-workflows/phase-setup/SKILL.md +194 -190
  44. package/framework/skills/level-1-workflows/phase-tasks/SKILL.md +270 -270
  45. package/framework/skills/level-1-workflows/phase-uiux/SKILL.md +285 -285
  46. package/framework/standards/STANDARDS.json +640 -88
  47. package/framework/standards/infrastructure/vercel/vercel-database.md +106 -0
  48. package/framework/templates/REGISTRY.json +1825 -1909
  49. package/framework/templates/context/CONTEXT-FEATURE.md +276 -276
  50. package/framework/templates/docs/onboarding.md +1 -5
  51. package/package.json +2 -6
  52. package/src/commands/agents/dispatch-agents.js +55 -4
  53. package/src/commands/project/doctor.js +16 -47
  54. package/src/commands/project/init.js +1 -1
  55. package/src/commands/project/status.js +2 -2
  56. package/src/commands/project/update.js +381 -365
  57. package/src/commands/project/worktree.js +154 -0
  58. package/src/commands/state/advance-phase.js +120 -30
  59. package/src/commands/state/approve.js +2 -2
  60. package/src/commands/state/index.js +7 -8
  61. package/src/commands/state/phase-runner.js +1 -1
  62. package/src/commands/state/state.js +61 -6
  63. package/src/commands/tasks/task.js +78 -99
  64. package/src/commands/templates/template-render.js +93 -173
  65. package/src/commands/trust/trust.js +26 -21
  66. package/src/core/paths/output-schema.js +15 -0
  67. package/src/core/state/state-manager.js +28 -54
  68. package/src/core/workflows/workflow-detector.js +9 -87
  69. package/src/lib/phase-chain/phase-validator.js +330 -0
  70. package/src/lib/stack/stack-profile.js +88 -0
  71. package/src/lib/tasks/task-classifier.js +16 -0
  72. package/src/lib/tasks/test-runner.js +77 -0
  73. package/src/lib/trust/trust-manager.js +32 -144
  74. package/src/lib/validators/spec-validator.js +58 -4
  75. package/src/lib/validators/validation-runner.js +23 -11
  76. package/src/scripts/setup-infra.js +240 -224
  77. package/src/utils/agents-installer.js +2 -2
  78. package/src/utils/banner.js +1 -1
  79. package/src/utils/claude-settings-manager.js +1 -1
  80. package/src/utils/file-copier.js +1 -0
  81. package/src/utils/hooks-installer.js +258 -8
  82. package/framework/hooks/dev/check-sync-health.js +0 -117
  83. package/framework/hooks/dev/guard-version-numbers.js +0 -57
  84. package/framework/hooks/dev/sync-standards-registry.js +0 -60
  85. package/framework/hooks/dev/sync-template-registry.js +0 -60
  86. package/framework/hooks/dev/validate-skill-format.js +0 -70
  87. package/framework/hooks/dev/validate-standard-format.js +0 -73
  88. package/framework/templates/meta-prompts/hops/hop-retry.md +0 -78
  89. package/framework/templates/meta-prompts/hops/hop-validation.md +0 -97
  90. package/framework/templates/meta-prompts/hops/hop-wrapper.md +0 -36
  91. package/framework/workflows/configs/design-impl.json +0 -49
  92. package/framework/workflows/configs/express.json +0 -45
  93. package/framework/workflows/configs/fast-track.json +0 -42
  94. package/framework/workflows/configs/full-morph.json +0 -79
  95. package/framework/workflows/configs/fusion.json +0 -39
  96. package/framework/workflows/configs/long-running.json +0 -33
  97. package/framework/workflows/configs/spec-only.json +0 -43
  98. package/framework/workflows/configs/ui-refresh.json +0 -49
  99. package/framework/workflows/configs/zero-touch.json +0 -82
  100. package/src/commands/project/monitor.js +0 -295
  101. package/src/commands/project/tutorial.js +0 -115
  102. package/src/commands/state/validate-phase.js +0 -238
  103. package/src/commands/templates/generate-contracts.js +0 -445
  104. package/src/core/orchestrator.js +0 -171
  105. package/src/core/registry/command-registry.js +0 -28
  106. package/src/core/registry/index.js +0 -8
  107. package/src/core/registry/validator-registry.js +0 -204
  108. package/src/core/templates/template-validator.js +0 -296
  109. package/src/generator/config-generator.js +0 -206
  110. package/src/generator/templates/config.json.template +0 -40
  111. package/src/generator/templates/project.md.template +0 -67
  112. package/src/lib/agents/micro-agent-factory.js +0 -161
  113. package/src/lib/analysis/complexity-analyzer.js +0 -441
  114. package/src/lib/analysis/index.js +0 -7
  115. package/src/lib/analytics/analytics-engine.js +0 -345
  116. package/src/lib/checkpoints/checkpoint-hooks.js +0 -298
  117. package/src/lib/checkpoints/index.js +0 -7
  118. package/src/lib/context/context-bundler.js +0 -241
  119. package/src/lib/context/context-optimizer.js +0 -212
  120. package/src/lib/context/context-tracker.js +0 -273
  121. package/src/lib/context/core-four-tracker.js +0 -201
  122. package/src/lib/context/mcp-optimizer.js +0 -200
  123. package/src/lib/execution/fusion-executor.js +0 -304
  124. package/src/lib/execution/parallel-executor.js +0 -270
  125. package/src/lib/hooks/stop-hook-executor.js +0 -286
  126. package/src/lib/hops/hop-composer.js +0 -221
  127. package/src/lib/phase-chain/eligibility-checker.js +0 -243
  128. package/src/lib/threads/thread-coordinator.js +0 -238
  129. package/src/lib/threads/thread-manager.js +0 -317
  130. package/src/lib/tracking/artifact-trail.js +0 -202
  131. package/src/scanner/project-scanner.js +0 -242
  132. package/src/ui/diff-display.js +0 -91
  133. package/src/ui/interactive-wizard.js +0 -96
  134. package/src/ui/user-review.js +0 -211
  135. package/src/ui/wizard-questions.js +0 -188
  136. package/src/utils/color-utils.js +0 -70
  137. package/src/utils/process-handler.js +0 -97
@@ -1,171 +0,0 @@
1
- /**
2
- * @fileoverview AutoContextOrchestrator - Main orchestrator for CLI auto-detection
3
- * @module morph-spec/orchestrator
4
- */
5
-
6
- import chalk from 'chalk';
7
- import { ProjectScanner } from '../scanner/project-scanner.js';
8
- import { ContextSanitizer } from '../sanitizer/context-sanitizer.js';
9
- import { ConfigGenerator } from '../generator/config-generator.js';
10
- import { UserReview } from '../ui/user-review.js';
11
- import { InteractiveWizard } from '../ui/interactive-wizard.js';
12
- import { FileWriter } from '../writer/file-writer.js';
13
- import { readFile, access } from 'fs/promises';
14
- import { join } from 'path';
15
-
16
- /**
17
- * @typedef {import('../types/index.js').GeneratedConfigs} GeneratedConfigs
18
- */
19
-
20
- /**
21
- * AutoContextOrchestrator - Orchestrates the complete auto-detection flow
22
- * @class
23
- */
24
- export class AutoContextOrchestrator {
25
- constructor() {
26
- this.scanner = new ProjectScanner();
27
- this.sanitizer = new ContextSanitizer();
28
- this.configGenerator = new ConfigGenerator();
29
- this.userReview = new UserReview();
30
- this.wizard = new InteractiveWizard();
31
- this.fileWriter = new FileWriter();
32
-
33
- // Handle Ctrl+C gracefully
34
- this.setupSignalHandlers();
35
- }
36
-
37
- /**
38
- * Execute the complete auto-detection flow
39
- * @param {string} cwd - Current working directory
40
- * @param {Object} [options] - Orchestration options
41
- * @param {boolean} [options.skipReview] - Skip user review (auto-approve)
42
- * @returns {Promise<{success: boolean, configs: GeneratedConfigs|null}>}
43
- */
44
- async execute(cwd, options = {}) {
45
- const {
46
- skipReview = false,
47
- } = options;
48
-
49
- try {
50
- console.log(chalk.bold.cyan('\n🔍 MORPH-SPEC Auto Context Detection\n'));
51
-
52
- let projectConfig;
53
-
54
- // Use interactive wizard (LLM invocation not implemented; wizard provides all context)
55
- console.log(chalk.yellow(' Using interactive wizard mode\n'));
56
- projectConfig = await this.wizard.run();
57
-
58
- // Step 2: Generate configs
59
- console.log(chalk.dim(' [4/6] Generating configuration files...'));
60
- const configs = await this.configGenerator.generate(projectConfig);
61
-
62
- // Step 3: User review (unless skipped)
63
- let finalConfigs = configs;
64
-
65
- if (!skipReview) {
66
- console.log(chalk.dim(' [5/6] Requesting user approval...'));
67
-
68
- // Check if updating existing configs
69
- const existingConfigs = await this.readExistingConfigs(cwd);
70
-
71
- const approvalResponse = await this.userReview.promptForApproval(
72
- configs,
73
- projectConfig,
74
- existingConfigs
75
- );
76
-
77
- if (approvalResponse.action === 'cancel') {
78
- console.log(chalk.yellow('\n❌ Operation canceled by user\n'));
79
- console.log(chalk.dim(` Reason: ${approvalResponse.cancelReason || 'User canceled'}\n`));
80
- return { success: false, configs: null };
81
- }
82
-
83
- if (approvalResponse.editedConfigs) {
84
- finalConfigs = approvalResponse.editedConfigs;
85
- }
86
- } else {
87
- console.log(chalk.dim(' [5/6] Skipping user review (auto-approve)'));
88
- }
89
-
90
- // Step 4: Save configs
91
- console.log(chalk.dim(' [6/6] Saving configuration files...'));
92
-
93
- // Backup existing configs if they exist
94
- await this.configGenerator.backupExisting(cwd);
95
-
96
- // Write new configs
97
- await this.fileWriter.save(cwd, finalConfigs);
98
-
99
- console.log(chalk.bold.green('🎉 Auto-detection complete!\n'));
100
-
101
- return { success: true, configs: finalConfigs };
102
- } catch (error) {
103
- console.log(chalk.bold.red('\n❌ Auto-detection failed\n'));
104
- console.log(chalk.red(` Error: ${error.message}\n`));
105
-
106
- if (error.stack && process.env.DEBUG) {
107
- console.log(chalk.dim(error.stack));
108
- }
109
-
110
- return { success: false, configs: null };
111
- }
112
- }
113
-
114
- /**
115
- * Read existing configs (if they exist)
116
- * @param {string} cwd - Current working directory
117
- * @returns {Promise<Object|null>} Existing configs or null
118
- */
119
- async readExistingConfigs(cwd) {
120
- try {
121
- const projectMdPath = join(cwd, '.morph', 'project.md');
122
- const configJsonPath = join(cwd, '.morph', 'config', 'config.json');
123
-
124
- const [projectMdExists, configJsonExists] = await Promise.all([
125
- this.fileExists(projectMdPath),
126
- this.fileExists(configJsonPath)
127
- ]);
128
-
129
- if (!projectMdExists && !configJsonExists) {
130
- return null; // No existing configs
131
- }
132
-
133
- const [projectMd, configJson] = await Promise.all([
134
- projectMdExists ? readFile(projectMdPath, 'utf-8') : null,
135
- configJsonExists ? readFile(configJsonPath, 'utf-8') : null
136
- ]);
137
-
138
- return { projectMd, configJson };
139
- } catch (error) {
140
- return null; // Error reading configs, treat as non-existent
141
- }
142
- }
143
-
144
- /**
145
- * Check if file exists
146
- * @param {string} filepath - File path
147
- * @returns {Promise<boolean>}
148
- */
149
- async fileExists(filepath) {
150
- try {
151
- await access(filepath);
152
- return true;
153
- } catch {
154
- return false;
155
- }
156
- }
157
-
158
- /**
159
- * Setup signal handlers for graceful shutdown
160
- */
161
- setupSignalHandlers() {
162
- const handleExit = () => {
163
- console.log(chalk.yellow('\n\n⚠️ Operation interrupted by user (Ctrl+C)\n'));
164
- console.log(chalk.dim(' No files were modified\n'));
165
- process.exit(0);
166
- };
167
-
168
- process.on('SIGINT', handleExit);
169
- process.on('SIGTERM', handleExit);
170
- }
171
- }
@@ -1,28 +0,0 @@
1
- /**
2
- * Command Registry — DEPRECATED
3
- *
4
- * This module is no longer used. CLI commands are registered directly
5
- * in bin/morph-spec.js via Commander.js. This file is kept as an empty
6
- * stub to prevent import errors from any remaining references.
7
- *
8
- * @module command-registry
9
- * @deprecated
10
- */
11
-
12
- export const commandMetadata = {};
13
-
14
- export async function loadCommand(commandName) {
15
- throw new Error(`Command registry is deprecated. Use bin/morph-spec.js directly.`);
16
- }
17
-
18
- export function getAllCommandNames() {
19
- return [];
20
- }
21
-
22
- export function getCommandsByCategory() {
23
- return [];
24
- }
25
-
26
- export function getAllCategories() {
27
- return [];
28
- }
@@ -1,8 +0,0 @@
1
- /**
2
- * Core Registry System
3
- *
4
- * Auto-discovery for commands and validators.
5
- */
6
-
7
- export * from './command-registry.js';
8
- export * from './validator-registry.js';
@@ -1,204 +0,0 @@
1
- /**
2
- * Validator Registry - Technology-Based Auto-Discovery
3
- *
4
- * Maps validator types to their modules for dynamic loading.
5
- * Organized by technology (blazor, css, architecture, etc.)
6
- *
7
- * @module validator-registry
8
- */
9
-
10
- /**
11
- * Validator Metadata Registry
12
- *
13
- * Technology-scoped validator discovery.
14
- */
15
- export const validatorMetadata = {
16
- // Blazor Validators
17
- 'blazor': {
18
- technology: 'blazor',
19
- validators: [
20
- {
21
- id: 'blazor-validator',
22
- name: 'Blazor Patterns Validator',
23
- description: 'Validates Fluent UI Blazor patterns and components',
24
- module: () => import('../../lib/validators/blazor/blazor-validator.js'),
25
- },
26
- {
27
- id: 'blazor-state',
28
- name: 'Blazor State Validator',
29
- description: 'Validates Blazor state management patterns',
30
- module: () => import('../../lib/validators/blazor/blazor-state-validator.js'),
31
- },
32
- {
33
- id: 'blazor-concurrency',
34
- name: 'Blazor Concurrency Analyzer',
35
- description: 'Analyzes Blazor concurrency and DbContext issues',
36
- module: () => import('../../lib/validators/blazor/blazor-concurrency-analyzer.js'),
37
- },
38
- ],
39
- },
40
-
41
- // CSS Validators
42
- 'css': {
43
- technology: 'css',
44
- validators: [
45
- {
46
- id: 'css-validator',
47
- name: 'CSS Validator',
48
- description: 'Validates CSS classes and design system compliance',
49
- module: () => import('../../lib/validators/css/css-validator.js'),
50
- },
51
- ],
52
- },
53
-
54
- // Architecture Validators
55
- 'architecture': {
56
- technology: 'architecture',
57
- validators: [
58
- {
59
- id: 'architecture-validator',
60
- name: 'Architecture Validator',
61
- description: 'Validates architectural patterns (DI, CQRS, etc.)',
62
- module: () => import('../../lib/validators/architecture/architecture-validator.js'),
63
- },
64
- ],
65
- },
66
-
67
- // Design System Validators
68
- 'design-system': {
69
- technology: 'design-system',
70
- validators: [
71
- {
72
- id: 'design-system-validator',
73
- name: 'Design System Validator',
74
- description: 'Validates design system compliance',
75
- module: () => import('../../lib/validators/design-system/design-system-validator.js'),
76
- },
77
- ],
78
- },
79
-
80
- // Content Validators
81
- 'content': {
82
- technology: 'content',
83
- validators: [
84
- {
85
- id: 'content-validator',
86
- name: 'Content Validator',
87
- description: 'Validates content and specification format',
88
- module: () => import('../../lib/validators/content/content-validator.js'),
89
- },
90
- ],
91
- },
92
-
93
- // Contract Validators
94
- 'contracts': {
95
- technology: 'contracts',
96
- validators: [
97
- {
98
- id: 'contract-compliance-validator',
99
- name: 'Contract Compliance Validator',
100
- description: 'Validates API contract compliance',
101
- module: () => import('../../lib/validators/contracts/contract-compliance-validator.js'),
102
- },
103
- ],
104
- },
105
-
106
- // Package Validators
107
- 'packages': {
108
- technology: 'packages',
109
- validators: [
110
- {
111
- id: 'package-validator',
112
- name: 'Package Validator',
113
- description: 'Validates package dependencies and versions',
114
- module: () => import('../../lib/validators/packages/package-validator.js'),
115
- },
116
- ],
117
- },
118
-
119
- // UI Validators
120
- 'ui': {
121
- technology: 'ui',
122
- validators: [
123
- {
124
- id: 'ui-contrast-validator',
125
- name: 'UI Contrast Validator',
126
- description: 'Validates UI contrast and accessibility',
127
- module: () => import('../../lib/validators/ui/ui-contrast-validator.js'),
128
- },
129
- ],
130
- },
131
- };
132
-
133
- /**
134
- * Load validator by ID
135
- *
136
- * @param {string} validatorId - Validator ID (e.g., 'blazor-validator')
137
- * @returns {Promise<Object>} Validator module
138
- */
139
- export async function loadValidator(validatorId) {
140
- for (const techMeta of Object.values(validatorMetadata)) {
141
- const validator = techMeta.validators.find(v => v.id === validatorId);
142
- if (validator) {
143
- return await validator.module();
144
- }
145
- }
146
-
147
- throw new Error(`Validator not found: ${validatorId}`);
148
- }
149
-
150
- /**
151
- * Load all validators for a technology
152
- *
153
- * @param {string} technology - Technology name (e.g., 'blazor', 'css')
154
- * @returns {Promise<Object[]>} Array of validator modules
155
- */
156
- export async function loadValidatorsForTechnology(technology) {
157
- const techMeta = validatorMetadata[technology];
158
-
159
- if (!techMeta) {
160
- throw new Error(`No validators found for technology: ${technology}`);
161
- }
162
-
163
- return await Promise.all(
164
- techMeta.validators.map(v => v.module())
165
- );
166
- }
167
-
168
- /**
169
- * Get all validator IDs
170
- *
171
- * @returns {string[]} Array of validator IDs
172
- */
173
- export function getAllValidatorIds() {
174
- const ids = [];
175
- for (const techMeta of Object.values(validatorMetadata)) {
176
- ids.push(...techMeta.validators.map(v => v.id));
177
- }
178
- return ids;
179
- }
180
-
181
- /**
182
- * Get all technologies
183
- *
184
- * @returns {string[]} Array of technology names
185
- */
186
- export function getAllTechnologies() {
187
- return Object.keys(validatorMetadata);
188
- }
189
-
190
- /**
191
- * Get validator metadata by ID
192
- *
193
- * @param {string} validatorId - Validator ID
194
- * @returns {Object|null} Validator metadata or null
195
- */
196
- export function getValidatorMetadata(validatorId) {
197
- for (const techMeta of Object.values(validatorMetadata)) {
198
- const validator = techMeta.validators.find(v => v.id === validatorId);
199
- if (validator) {
200
- return { ...validator, technology: techMeta.technology };
201
- }
202
- }
203
- return null;
204
- }
@@ -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
- }