@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.
Files changed (138) hide show
  1. package/.morph/analytics/threads-log.jsonl +54 -0
  2. package/.morph/state.json +198 -0
  3. package/LICENSE +1 -2
  4. package/README.md +379 -414
  5. package/bin/morph-spec.js +57 -403
  6. package/bin/validate.js +2 -26
  7. package/claude-plugin.json +2 -2
  8. package/docs/ARCHITECTURE.md +43 -46
  9. package/docs/CHEATSHEET.md +203 -221
  10. package/docs/COMMAND-FLOWS.md +319 -289
  11. package/docs/QUICKSTART.md +2 -8
  12. package/docs/plans/2026-02-22-claude-docs-morph-alignment-analysis.md +2 -0
  13. package/docs/plans/2026-02-22-claude-settings.md +2 -0
  14. package/docs/plans/2026-02-22-morph-cc-alignment-impl.md +2 -0
  15. package/docs/plans/2026-02-22-morph-spec-next.md +2 -0
  16. package/docs/plans/2026-02-22-native-alignment-design.md +2 -0
  17. package/docs/plans/2026-02-22-native-alignment-impl.md +2 -0
  18. package/docs/plans/2026-02-22-native-enrichment-design.md +2 -0
  19. package/docs/plans/2026-02-22-native-enrichment.md +2 -0
  20. package/docs/plans/2026-02-23-ddd-architecture-refactor.md +2 -0
  21. package/docs/plans/2026-02-23-ddd-nextsteps.md +2 -0
  22. package/docs/plans/2026-02-23-infra-architect-refactor.md +2 -0
  23. package/docs/plans/2026-02-23-nextjs-code-review-design.md +2 -1
  24. package/docs/plans/2026-02-23-nextjs-code-review-impl.md +2 -0
  25. package/docs/plans/2026-02-23-nextjs-standards-design.md +2 -1
  26. package/docs/plans/2026-02-23-nextjs-standards-impl.md +2 -0
  27. package/docs/plans/2026-02-24-cli-radical-simplification.md +592 -0
  28. package/docs/plans/2026-02-24-framework-failure-points.md +125 -0
  29. package/docs/plans/2026-02-24-morph-init-design.md +337 -0
  30. package/docs/plans/2026-02-24-morph-init-impl.md +1269 -0
  31. package/docs/plans/2026-02-24-tutorial-command-design.md +71 -0
  32. package/docs/plans/2026-02-24-tutorial-command.md +298 -0
  33. package/framework/CLAUDE.md +2 -2
  34. package/framework/commands/morph-proposal.md +3 -3
  35. package/framework/hooks/README.md +11 -10
  36. package/framework/hooks/claude-code/notification/approval-reminder.js +2 -0
  37. package/framework/hooks/claude-code/post-tool-use/dispatch.js +1 -1
  38. package/framework/hooks/claude-code/pre-tool-use/protect-readonly-files.js +4 -55
  39. package/framework/hooks/claude-code/session-start/inject-morph-context.js +20 -5
  40. package/framework/hooks/claude-code/statusline.py +6 -1
  41. package/framework/hooks/claude-code/stop/validate-completion.js +1 -1
  42. package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +1 -1
  43. package/framework/hooks/dev/check-sync-health.js +117 -0
  44. package/framework/hooks/dev/guard-version-numbers.js +57 -0
  45. package/framework/hooks/dev/sync-standards-registry.js +60 -0
  46. package/framework/hooks/dev/sync-template-registry.js +60 -0
  47. package/framework/hooks/dev/validate-skill-format.js +70 -0
  48. package/framework/hooks/dev/validate-standard-format.js +73 -0
  49. package/framework/hooks/shared/payload-utils.js +39 -0
  50. package/framework/hooks/shared/state-reader.js +25 -1
  51. package/framework/rules/morph-workflow.md +1 -1
  52. package/framework/skills/level-0-meta/morph-init/SKILL.md +216 -0
  53. package/framework/skills/level-0-meta/morph-replicate/SKILL.md +4 -4
  54. package/framework/skills/level-0-meta/tool-usage-guide/SKILL.md +4 -4
  55. package/framework/skills/level-0-meta/verification-before-completion/SKILL.md +1 -1
  56. package/framework/skills/level-1-workflows/phase-clarify/SKILL.md +192 -191
  57. package/framework/skills/level-1-workflows/phase-codebase-analysis/SKILL.md +181 -180
  58. package/framework/skills/level-1-workflows/phase-design/SKILL.md +339 -338
  59. package/framework/skills/level-1-workflows/phase-implement/SKILL.md +254 -253
  60. package/framework/skills/level-1-workflows/phase-setup/SKILL.md +168 -170
  61. package/framework/skills/level-1-workflows/phase-tasks/SKILL.md +284 -283
  62. package/framework/skills/level-1-workflows/phase-uiux/SKILL.md +246 -245
  63. package/framework/templates/examples/design-system-examples.md +1 -1
  64. package/framework/templates/ui/FluentDesignTheme.cs +1 -1
  65. package/framework/templates/ui/MudTheme.cs +1 -1
  66. package/framework/templates/ui/design-system.css +1 -1
  67. package/package.json +4 -2
  68. package/scripts/bump-version.js +248 -0
  69. package/scripts/install-dev-hooks.js +138 -0
  70. package/src/commands/agents/index.js +1 -2
  71. package/src/commands/index.js +13 -16
  72. package/src/commands/project/doctor.js +100 -14
  73. package/src/commands/project/index.js +7 -10
  74. package/src/commands/project/init.js +398 -555
  75. package/src/commands/project/install-plugin-cmd.js +28 -0
  76. package/src/commands/project/setup-infra-cmd.js +12 -0
  77. package/src/commands/project/tutorial.js +115 -0
  78. package/src/commands/project/update.js +22 -37
  79. package/src/commands/state/approve.js +213 -221
  80. package/src/commands/state/index.js +0 -1
  81. package/src/commands/state/state.js +337 -365
  82. package/src/commands/templates/index.js +0 -4
  83. package/src/commands/trust/trust.js +1 -93
  84. package/src/commands/utils/index.js +1 -5
  85. package/src/commands/validation/index.js +1 -5
  86. package/src/core/registry/command-registry.js +11 -285
  87. package/src/core/state/state-manager.js +5 -2
  88. package/src/lib/detectors/index.js +81 -87
  89. package/src/lib/detectors/structure-detector.js +275 -273
  90. package/src/lib/generators/recap-generator.js +232 -225
  91. package/src/lib/installers/mcp-installer.js +18 -3
  92. package/src/scripts/global-install.js +34 -0
  93. package/src/scripts/install-plugin.js +126 -0
  94. package/src/scripts/setup-infra.js +203 -0
  95. package/src/utils/agents-installer.js +10 -1
  96. package/src/utils/hooks-installer.js +70 -17
  97. package/CLAUDE.md +0 -77
  98. package/docs/claude-alignment-report.md +0 -137
  99. package/docs/examples/order-management/contracts.cs +0 -84
  100. package/docs/examples/order-management/proposal.md +0 -24
  101. package/docs/examples/order-management/spec.md +0 -162
  102. package/src/commands/feature/create-story.js +0 -362
  103. package/src/commands/feature/index.js +0 -6
  104. package/src/commands/feature/shard-spec.js +0 -225
  105. package/src/commands/feature/sprint-status.js +0 -250
  106. package/src/commands/generation/generate-onboarding.js +0 -169
  107. package/src/commands/generation/generate.js +0 -276
  108. package/src/commands/generation/index.js +0 -5
  109. package/src/commands/learning/capture-pattern.js +0 -121
  110. package/src/commands/learning/index.js +0 -5
  111. package/src/commands/learning/search-patterns.js +0 -126
  112. package/src/commands/mcp/mcp.js +0 -102
  113. package/src/commands/project/changes.js +0 -66
  114. package/src/commands/project/cost.js +0 -179
  115. package/src/commands/project/detect.js +0 -114
  116. package/src/commands/project/diff.js +0 -278
  117. package/src/commands/project/revert.js +0 -173
  118. package/src/commands/project/standards.js +0 -80
  119. package/src/commands/project/sync.js +0 -167
  120. package/src/commands/project/update-agents.js +0 -23
  121. package/src/commands/state/rollback-phase.js +0 -185
  122. package/src/commands/templates/template-customize.js +0 -87
  123. package/src/commands/templates/template-list.js +0 -114
  124. package/src/commands/templates/template-show.js +0 -129
  125. package/src/commands/templates/template-validate.js +0 -91
  126. package/src/commands/utils/troubleshoot.js +0 -222
  127. package/src/commands/validation/analyze-blazor-concurrency.js +0 -193
  128. package/src/commands/validation/lint-fluent.js +0 -352
  129. package/src/commands/validation/validate-blazor-state.js +0 -210
  130. package/src/commands/validation/validate-blazor.js +0 -156
  131. package/src/commands/validation/validate-css.js +0 -84
  132. package/src/lib/detectors/conversation-analyzer.js +0 -163
  133. package/src/lib/learning/index.js +0 -7
  134. package/src/lib/learning/learning-system.js +0 -520
  135. package/src/lib/troubleshooting/index.js +0 -8
  136. package/src/lib/troubleshooting/troubleshoot-grep.js +0 -198
  137. package/src/lib/troubleshooting/troubleshoot-index.js +0 -144
  138. package/src/llm/environment-detector.js +0 -43
@@ -1,198 +0,0 @@
1
- /**
2
- * Troubleshoot Grep Fallback
3
- * Searches markdown files for matching content when index doesn't find results
4
- */
5
-
6
- import { readFileSync, readdirSync, existsSync } from 'fs';
7
- import { join, dirname, basename } from 'path';
8
- import { fileURLToPath } from 'url';
9
-
10
- const __filename = fileURLToPath(import.meta.url);
11
- const __dirname = dirname(__filename);
12
-
13
- // Directories to search
14
- const SEARCH_PATHS = [
15
- 'framework/standards',
16
- '.morph/framework/standards',
17
- '.wiki'
18
- ];
19
-
20
- function getSearchPaths(basePath) {
21
- return SEARCH_PATHS;
22
- }
23
-
24
- /**
25
- * Get all markdown files from search paths
26
- * @param {string} basePath - Base path of the project
27
- * @returns {string[]} Array of file paths
28
- */
29
- function getMarkdownFiles(basePath) {
30
- const files = [];
31
-
32
- for (const searchPath of getSearchPaths(basePath)) {
33
- const fullPath = join(basePath, searchPath);
34
-
35
- if (!existsSync(fullPath)) {
36
- continue;
37
- }
38
-
39
- try {
40
- const entries = readdirSync(fullPath, { withFileTypes: true });
41
-
42
- for (const entry of entries) {
43
- if (entry.isFile() && entry.name.endsWith('.md')) {
44
- files.push(join(fullPath, entry.name));
45
- }
46
- }
47
- } catch (error) {
48
- // Skip directories we can't read
49
- }
50
- }
51
-
52
- return files;
53
- }
54
-
55
- /**
56
- * Extract section containing the match
57
- * @param {string} content - File content
58
- * @param {number} matchIndex - Index of the match
59
- * @returns {Object} Section object with title and content
60
- */
61
- function extractSection(content, matchIndex) {
62
- const lines = content.split('\n');
63
- let charCount = 0;
64
- let matchLineIndex = 0;
65
-
66
- // Find the line containing the match
67
- for (let i = 0; i < lines.length; i++) {
68
- charCount += lines[i].length + 1; // +1 for newline
69
- if (charCount > matchIndex) {
70
- matchLineIndex = i;
71
- break;
72
- }
73
- }
74
-
75
- // Find the section header (## heading) above the match
76
- let sectionTitle = 'Unknown Section';
77
- let sectionStart = 0;
78
-
79
- for (let i = matchLineIndex; i >= 0; i--) {
80
- if (lines[i].startsWith('## ')) {
81
- sectionTitle = lines[i].replace('## ', '').trim();
82
- sectionStart = i;
83
- break;
84
- }
85
- }
86
-
87
- // Find the end of the section (next ## or end of file)
88
- let sectionEnd = lines.length;
89
- for (let i = sectionStart + 1; i < lines.length; i++) {
90
- if (lines[i].startsWith('## ')) {
91
- sectionEnd = i;
92
- break;
93
- }
94
- }
95
-
96
- // Extract section content (limit to ~20 lines around match)
97
- const contextStart = Math.max(sectionStart, matchLineIndex - 10);
98
- const contextEnd = Math.min(sectionEnd, matchLineIndex + 10);
99
- const sectionContent = lines.slice(contextStart, contextEnd).join('\n');
100
-
101
- return {
102
- title: sectionTitle,
103
- content: sectionContent,
104
- lineNumber: matchLineIndex + 1
105
- };
106
- }
107
-
108
- /**
109
- * Search a file for keywords
110
- * @param {string} filePath - Path to the file
111
- * @param {string[]} keywords - Keywords to search for
112
- * @returns {Object[]} Array of matches
113
- */
114
- function searchFile(filePath, keywords) {
115
- const matches = [];
116
-
117
- try {
118
- const content = readFileSync(filePath, 'utf-8');
119
- const contentLower = content.toLowerCase();
120
-
121
- for (const keyword of keywords) {
122
- const keywordLower = keyword.toLowerCase();
123
- let index = contentLower.indexOf(keywordLower);
124
-
125
- while (index !== -1) {
126
- const section = extractSection(content, index);
127
-
128
- // Check if we already have a match for this section
129
- const existingMatch = matches.find(m => m.section === section.title);
130
-
131
- if (existingMatch) {
132
- existingMatch.matchCount++;
133
- existingMatch.keywords.add(keyword);
134
- } else {
135
- matches.push({
136
- file: filePath,
137
- fileName: basename(filePath),
138
- section: section.title,
139
- content: section.content,
140
- lineNumber: section.lineNumber,
141
- matchCount: 1,
142
- keywords: new Set([keyword])
143
- });
144
- }
145
-
146
- index = contentLower.indexOf(keywordLower, index + 1);
147
- }
148
- }
149
- } catch (error) {
150
- // Skip files we can't read
151
- }
152
-
153
- return matches;
154
- }
155
-
156
- /**
157
- * Search all markdown files for keywords
158
- * @param {string[]} keywords - Keywords to search for
159
- * @param {Object} options - Search options
160
- * @returns {Object[]} Array of matches sorted by relevance
161
- */
162
- export function searchGrep(keywords, options = {}) {
163
- // Determine base path
164
- const basePath = options.basePath || join(__dirname, '../..');
165
-
166
- const files = getMarkdownFiles(basePath);
167
- let allMatches = [];
168
-
169
- for (const file of files) {
170
- const matches = searchFile(file, keywords);
171
- allMatches = allMatches.concat(matches);
172
- }
173
-
174
- // Convert keywords Set to Array and calculate score
175
- const results = allMatches.map(match => ({
176
- ...match,
177
- keywords: Array.from(match.keywords),
178
- score: match.matchCount * 5 + match.keywords.size * 3,
179
- source: 'grep'
180
- }));
181
-
182
- // Sort by score (higher is better) and deduplicate
183
- const seen = new Set();
184
- const uniqueResults = results
185
- .sort((a, b) => b.score - a.score)
186
- .filter(r => {
187
- const key = `${r.file}:${r.section}`;
188
- if (seen.has(key)) return false;
189
- seen.add(key);
190
- return true;
191
- });
192
-
193
- return uniqueResults.slice(0, 10); // Limit to top 10
194
- }
195
-
196
- export default {
197
- searchGrep
198
- };
@@ -1,144 +0,0 @@
1
- /**
2
- * Troubleshoot Index Search
3
- * Searches the troubleshooting-index.json for matching problems
4
- */
5
-
6
- import { readFileSync, existsSync } from 'fs';
7
- import { join, dirname } from 'path';
8
- import { fileURLToPath } from 'url';
9
-
10
- const __filename = fileURLToPath(import.meta.url);
11
- const __dirname = dirname(__filename);
12
-
13
- /**
14
- * Load the troubleshooting index
15
- * @returns {Object} The index object or null if not found
16
- */
17
- export function loadIndex() {
18
- const indexPath = join(__dirname, '../../framework/index/troubleshooting-index.json');
19
-
20
- if (!existsSync(indexPath)) {
21
- return null;
22
- }
23
-
24
- try {
25
- const content = readFileSync(indexPath, 'utf-8');
26
- return JSON.parse(content);
27
- } catch (error) {
28
- console.error('Error loading troubleshooting index:', error.message);
29
- return null;
30
- }
31
- }
32
-
33
- /**
34
- * Calculate relevance score for a problem based on keywords
35
- * @param {Object} problem - The problem object from index
36
- * @param {string[]} searchTerms - Search terms from user
37
- * @returns {number} Relevance score (higher is better)
38
- */
39
- function calculateScore(problem, searchTerms) {
40
- let score = 0;
41
- const searchLower = searchTerms.map(t => t.toLowerCase());
42
-
43
- // Check keywords
44
- for (const keyword of problem.keywords) {
45
- const keywordLower = keyword.toLowerCase();
46
- for (const term of searchLower) {
47
- if (keywordLower.includes(term) || term.includes(keywordLower)) {
48
- score += 10;
49
- }
50
- }
51
- }
52
-
53
- // Check title
54
- const titleLower = problem.title.toLowerCase();
55
- for (const term of searchLower) {
56
- if (titleLower.includes(term)) {
57
- score += 5;
58
- }
59
- }
60
-
61
- // Check error pattern (regex match on search string)
62
- if (problem.errorPattern) {
63
- try {
64
- const regex = new RegExp(problem.errorPattern, 'i');
65
- const searchString = searchTerms.join(' ');
66
- if (regex.test(searchString)) {
67
- score += 20; // High score for error pattern match
68
- }
69
- } catch (e) {
70
- // Invalid regex, skip
71
- }
72
- }
73
-
74
- // Boost critical severity
75
- if (problem.severity === 'critical') {
76
- score *= 1.2;
77
- }
78
-
79
- return score;
80
- }
81
-
82
- /**
83
- * Search the index for problems matching the keywords
84
- * @param {string[]} keywords - Search keywords
85
- * @param {Object} options - Search options
86
- * @param {string} options.category - Filter by category
87
- * @returns {Object[]} Array of matching problems with scores
88
- */
89
- export function searchIndex(keywords, options = {}) {
90
- const index = loadIndex();
91
-
92
- if (!index || !index.problems) {
93
- return [];
94
- }
95
-
96
- let problems = index.problems;
97
-
98
- // Filter by category if specified
99
- if (options.category) {
100
- problems = problems.filter(p => p.category === options.category);
101
- }
102
-
103
- // Score and filter problems
104
- const results = problems
105
- .map(problem => ({
106
- ...problem,
107
- score: calculateScore(problem, keywords)
108
- }))
109
- .filter(p => p.score > 0)
110
- .sort((a, b) => b.score - a.score);
111
-
112
- return results;
113
- }
114
-
115
- /**
116
- * Get all categories from the index
117
- * @returns {Object} Categories object
118
- */
119
- export function getCategories() {
120
- const index = loadIndex();
121
- return index?.categories || {};
122
- }
123
-
124
- /**
125
- * Get a specific problem by ID
126
- * @param {string} id - Problem ID
127
- * @returns {Object|null} Problem object or null
128
- */
129
- export function getProblemById(id) {
130
- const index = loadIndex();
131
-
132
- if (!index || !index.problems) {
133
- return null;
134
- }
135
-
136
- return index.problems.find(p => p.id === id) || null;
137
- }
138
-
139
- export default {
140
- loadIndex,
141
- searchIndex,
142
- getCategories,
143
- getProblemById
144
- };
@@ -1,43 +0,0 @@
1
- /**
2
- * @fileoverview Environment Detector - Detects if running in Claude Code
3
- * @module morph-spec/llm/environment-detector
4
- */
5
-
6
- /**
7
- * Detect if running inside Claude Code environment
8
- * @returns {boolean} True if Claude Code is available
9
- */
10
- export function detectClaudeCode() {
11
- // Check for environment variables that Claude Code sets
12
- const claudeEnvVars = [
13
- 'CLAUDE_CODE_SESSION',
14
- 'CLAUDE_API_KEY',
15
- 'ANTHROPIC_API_KEY'
16
- ];
17
-
18
- // Check if any Claude-specific env var is set
19
- const hasClaudeEnv = claudeEnvVars.some(varName => process.env[varName]);
20
-
21
- // Check if we can detect Claude Code CLI
22
- // (This is a heuristic - Claude Code sets these when running)
23
- const hasClaudeCLI = process.env.TERM_PROGRAM === 'claude' ||
24
- process.env.CLAUDE_CODE_VERSION;
25
-
26
- return hasClaudeEnv || hasClaudeCLI;
27
- }
28
-
29
- /**
30
- * Get Claude Code version if available
31
- * @returns {string|null} Version string or null
32
- */
33
- export function getClaudeCodeVersion() {
34
- return process.env.CLAUDE_CODE_VERSION || null;
35
- }
36
-
37
- /**
38
- * Check if running in interactive mode (user is present)
39
- * @returns {boolean} True if interactive
40
- */
41
- export function isInteractive() {
42
- return process.stdout.isTTY && process.stdin.isTTY;
43
- }