@polymorphism-tech/morph-spec 4.2.0 → 4.3.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 (140) hide show
  1. package/CLAUDE.md +108 -946
  2. package/bin/morph-spec.js +284 -9
  3. package/bin/task-manager.cjs +102 -14
  4. package/bin/validate.js +4 -4
  5. package/docs/{v3.0 → next-generation}/AGENTS.md +1 -1
  6. package/docs/next-generation/CONTEXT-OPTIMIZATION.md +267 -0
  7. package/docs/next-generation/EXECUTION-FLOW.md +274 -0
  8. package/docs/next-generation/META-PROMPTS.md +235 -0
  9. package/docs/next-generation/MIGRATION-GUIDE.md +253 -0
  10. package/docs/next-generation/THREAD-MANAGEMENT.md +240 -0
  11. package/package.json +5 -5
  12. package/src/commands/agents/agents-fuse.js +97 -0
  13. package/src/commands/agents/micro-agent.js +112 -0
  14. package/src/commands/agents/spawn-team.js +69 -4
  15. package/src/commands/agents/squad-template.js +146 -0
  16. package/src/commands/analytics/analytics.js +176 -0
  17. package/src/commands/context/context-prime.js +63 -0
  18. package/src/commands/context/core-four.js +54 -0
  19. package/src/commands/mcp/mcp.js +102 -0
  20. package/src/commands/project/detect-agents.js +32 -2
  21. package/src/commands/project/detect.js +11 -1
  22. package/src/commands/project/doctor.js +573 -356
  23. package/src/commands/project/init.js +9 -2
  24. package/src/commands/project/update.js +13 -3
  25. package/src/commands/state/advance-phase.js +448 -416
  26. package/src/commands/state/state.js +14 -12
  27. package/src/commands/tasks/task.js +1 -1
  28. package/src/commands/templates/template-render.js +80 -1
  29. package/src/commands/threads/thread-template.js +103 -0
  30. package/src/commands/threads/threads.js +261 -0
  31. package/src/commands/trust/trust.js +205 -0
  32. package/src/{orchestrator.js → core/orchestrator.js} +8 -8
  33. package/src/core/state/state-manager.js +37 -17
  34. package/src/core/workflows/workflow-detector.js +114 -3
  35. package/src/lib/agents/micro-agent-factory.js +161 -0
  36. package/src/lib/analytics/analytics-engine.js +345 -0
  37. package/src/lib/checkpoints/checkpoint-hooks.js +298 -258
  38. package/src/lib/context/context-bundler.js +240 -0
  39. package/src/lib/context/context-optimizer.js +212 -0
  40. package/src/lib/context/context-tracker.js +273 -0
  41. package/src/lib/context/core-four-tracker.js +201 -0
  42. package/src/lib/context/mcp-optimizer.js +200 -0
  43. package/src/lib/detectors/index.js +1 -1
  44. package/src/lib/detectors/standards-generator.js +77 -17
  45. package/src/lib/detectors/structure-detector.js +67 -39
  46. package/src/lib/execution/fusion-executor.js +304 -0
  47. package/src/lib/execution/parallel-executor.js +270 -0
  48. package/src/lib/generators/context-generator.js +3 -3
  49. package/src/lib/generators/recap-generator.js +32 -12
  50. package/src/lib/hooks/hook-executor.js +169 -0
  51. package/src/lib/hooks/stop-hook-executor.js +286 -0
  52. package/src/lib/hops/hop-composer.js +221 -0
  53. package/src/lib/threads/thread-coordinator.js +238 -0
  54. package/src/lib/threads/thread-manager.js +317 -0
  55. package/src/lib/tracking/artifact-trail.js +202 -0
  56. package/src/lib/trust/trust-manager.js +269 -0
  57. package/src/lib/validators/design-system/design-system-validator.js +2 -2
  58. package/src/lib/validators/validation-runner.js +14 -30
  59. package/src/utils/hooks-installer.js +69 -0
  60. package/stacks/blazor-azure/.morph/config/agents.json +72 -3
  61. package/stacks/nextjs-supabase/.morph/config/agents.json +3 -3
  62. package/docs/llm-interaction-config.md +0 -735
  63. package/docs/v3.0/EXECUTION-FLOW.md +0 -1304
  64. package/src/commands/utils/migrate-state.js +0 -158
  65. package/src/commands/utils/upgrade.js +0 -346
  66. package/src/lib/validators/architecture-validator.js +0 -60
  67. package/src/lib/validators/content-validator.js +0 -164
  68. package/src/lib/validators/package-validator.js +0 -61
  69. package/src/lib/validators/ui-contrast-validator.js +0 -44
  70. package/stacks/blazor-azure/.claude/commands/morph-apply.md +0 -221
  71. package/stacks/blazor-azure/.claude/commands/morph-archive.md +0 -79
  72. package/stacks/blazor-azure/.claude/commands/morph-deploy.md +0 -529
  73. package/stacks/blazor-azure/.claude/commands/morph-infra.md +0 -209
  74. package/stacks/blazor-azure/.claude/commands/morph-preflight.md +0 -227
  75. package/stacks/blazor-azure/.claude/commands/morph-proposal.md +0 -122
  76. package/stacks/blazor-azure/.claude/commands/morph-status.md +0 -86
  77. package/stacks/blazor-azure/.claude/commands/morph-troubleshoot.md +0 -122
  78. package/stacks/blazor-azure/.claude/skills/level-0-meta/README.md +0 -7
  79. package/stacks/blazor-azure/.claude/skills/level-0-meta/code-review.md +0 -226
  80. package/stacks/blazor-azure/.claude/skills/level-0-meta/morph-checklist.md +0 -117
  81. package/stacks/blazor-azure/.claude/skills/level-0-meta/simulation-checklist.md +0 -77
  82. package/stacks/blazor-azure/.claude/skills/level-1-workflows/README.md +0 -7
  83. package/stacks/blazor-azure/.claude/skills/level-1-workflows/morph-replicate.md +0 -213
  84. package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-clarify.md +0 -131
  85. package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-design.md +0 -213
  86. package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-setup.md +0 -106
  87. package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-tasks.md +0 -164
  88. package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-uiux.md +0 -169
  89. package/stacks/blazor-azure/.claude/skills/level-2-domains/README.md +0 -14
  90. package/stacks/blazor-azure/.claude/skills/level-2-domains/ai-agents/ai-system-architect.md +0 -192
  91. package/stacks/blazor-azure/.claude/skills/level-2-domains/architecture/po-pm-advisor.md +0 -197
  92. package/stacks/blazor-azure/.claude/skills/level-2-domains/architecture/prompt-engineer.md +0 -189
  93. package/stacks/blazor-azure/.claude/skills/level-2-domains/architecture/seo-growth-hacker.md +0 -320
  94. package/stacks/blazor-azure/.claude/skills/level-2-domains/architecture/standards-architect.md +0 -156
  95. package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/api-designer.md +0 -59
  96. package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/dotnet-senior.md +0 -77
  97. package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/ef-modeler.md +0 -58
  98. package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/hangfire-orchestrator.md +0 -126
  99. package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/ms-agent-expert.md +0 -45
  100. package/stacks/blazor-azure/.claude/skills/level-2-domains/frontend/blazor-builder.md +0 -210
  101. package/stacks/blazor-azure/.claude/skills/level-2-domains/frontend/nextjs-expert.md +0 -154
  102. package/stacks/blazor-azure/.claude/skills/level-2-domains/frontend/ui-ux-designer.md +0 -191
  103. package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/azure-architect.md +0 -142
  104. package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/azure-deploy-specialist.md +0 -699
  105. package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/bicep-architect.md +0 -126
  106. package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/container-specialist.md +0 -131
  107. package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/devops-engineer.md +0 -119
  108. package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/asaas-financial.md +0 -130
  109. package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/azure-identity.md +0 -142
  110. package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/clerk-auth.md +0 -108
  111. package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/hangfire-orchestrator.md +0 -64
  112. package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/resend-email.md +0 -119
  113. package/stacks/blazor-azure/.claude/skills/level-2-domains/quality/code-analyzer.md +0 -235
  114. package/stacks/blazor-azure/.claude/skills/level-2-domains/quality/testing-specialist.md +0 -126
  115. package/stacks/blazor-azure/.claude/skills/level-3-technologies/README.md +0 -7
  116. package/stacks/blazor-azure/.claude/skills/level-4-patterns/README.md +0 -7
  117. package/stacks/blazor-azure/.morph/archive/.gitkeep +0 -25
  118. package/stacks/blazor-azure/.morph/features/.gitkeep +0 -25
  119. package/stacks/blazor-azure/.morph/schemas/agent.schema.json +0 -296
  120. package/stacks/blazor-azure/.morph/schemas/tasks.schema.json +0 -220
  121. package/stacks/blazor-azure/.morph/specs/.gitkeep +0 -20
  122. package/stacks/blazor-azure/.morph/test-infra/example.bicep +0 -59
  123. package/stacks/nextjs-supabase/.claude/commands/morph-apply.md +0 -221
  124. package/stacks/nextjs-supabase/.claude/commands/morph-archive.md +0 -79
  125. package/stacks/nextjs-supabase/.claude/commands/morph-deploy.md +0 -529
  126. package/stacks/nextjs-supabase/.claude/commands/morph-infra.md +0 -209
  127. package/stacks/nextjs-supabase/.claude/commands/morph-preflight.md +0 -227
  128. package/stacks/nextjs-supabase/.claude/commands/morph-proposal.md +0 -122
  129. package/stacks/nextjs-supabase/.claude/commands/morph-status.md +0 -86
  130. package/stacks/nextjs-supabase/.claude/commands/morph-troubleshoot.md +0 -122
  131. package/stacks/nextjs-supabase/.claude/settings.local.json +0 -6
  132. package/stacks/nextjs-supabase/.claude/skills/level-2-domains/backend/dotnet-supabase.md +0 -244
  133. package/stacks/nextjs-supabase/.claude/skills/level-2-domains/frontend/nextjs-supabase.md +0 -335
  134. package/stacks/nextjs-supabase/.claude/skills/level-2-domains/infrastructure/easypanel-deployer.md +0 -189
  135. package/stacks/nextjs-supabase/.claude/skills/level-2-domains/integrations/supabase-expert.md +0 -50
  136. /package/docs/{v3.0 → next-generation}/ANALYSIS.md +0 -0
  137. /package/docs/{v3.0 → next-generation}/ARCHITECTURE.md +0 -0
  138. /package/docs/{v3.0 → next-generation}/FEATURES.md +0 -0
  139. /package/docs/{v3.0 → next-generation}/README.md +0 -0
  140. /package/docs/{v3.0 → next-generation}/ROADMAP.md +0 -0
@@ -0,0 +1,202 @@
1
+ /**
2
+ * Artifact Trail — Debugging trail system
3
+ *
4
+ * Creates checkpoint artifact directories and saves:
5
+ * - Intermediate files (spec drafts, contract drafts)
6
+ * - Agent reasoning logs
7
+ * - Validator output logs
8
+ * - Failure artifacts on checkpoint failure
9
+ *
10
+ * Structure:
11
+ * .morph/features/{feature}/artifacts/
12
+ * checkpoint-{n}/
13
+ * spec-draft.md
14
+ * contracts-draft.cs
15
+ * agent-reasoning.json
16
+ * validator-output.json
17
+ * checkpoint-{n}/failures/
18
+ * failure-detail.json
19
+ */
20
+
21
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from 'fs';
22
+ import { join } from 'path';
23
+
24
+ const BASE_DIR = join(process.cwd(), '.morph/features');
25
+
26
+ // ============================================================================
27
+ // Path Helpers
28
+ // ============================================================================
29
+
30
+ function artifactsDir(feature) {
31
+ return join(BASE_DIR, feature, 'artifacts');
32
+ }
33
+
34
+ function checkpointDir(feature, checkpointNum) {
35
+ return join(artifactsDir(feature), `checkpoint-${checkpointNum}`);
36
+ }
37
+
38
+ function failuresDir(feature, checkpointNum) {
39
+ return join(checkpointDir(feature, checkpointNum), 'failures');
40
+ }
41
+
42
+ function ensureDir(dir) {
43
+ if (!existsSync(dir)) {
44
+ mkdirSync(dir, { recursive: true });
45
+ }
46
+ return dir;
47
+ }
48
+
49
+ // ============================================================================
50
+ // Checkpoint Artifacts
51
+ // ============================================================================
52
+
53
+ /**
54
+ * Initialize a checkpoint artifact directory
55
+ * @param {string} feature - Feature name
56
+ * @param {number} checkpointNum - Checkpoint number
57
+ * @returns {string} Path to checkpoint directory
58
+ */
59
+ export function initCheckpointArtifacts(feature, checkpointNum) {
60
+ const dir = checkpointDir(feature, checkpointNum);
61
+ ensureDir(dir);
62
+
63
+ const manifest = {
64
+ feature,
65
+ checkpointNum,
66
+ createdAt: new Date().toISOString(),
67
+ files: []
68
+ };
69
+
70
+ writeFileSync(join(dir, 'manifest.json'), JSON.stringify(manifest, null, 2), 'utf8');
71
+ return dir;
72
+ }
73
+
74
+ /**
75
+ * Save an intermediate artifact file to a checkpoint directory
76
+ * @param {string} feature - Feature name
77
+ * @param {number} checkpointNum - Checkpoint number
78
+ * @param {string} filename - File name (e.g., 'spec-draft.md')
79
+ * @param {string} content - File content
80
+ * @returns {string} Full path to saved file
81
+ */
82
+ export function saveArtifact(feature, checkpointNum, filename, content) {
83
+ const dir = ensureDir(checkpointDir(feature, checkpointNum));
84
+ const filePath = join(dir, filename);
85
+
86
+ writeFileSync(filePath, content, 'utf8');
87
+
88
+ // Update manifest
89
+ const manifestPath = join(dir, 'manifest.json');
90
+ if (existsSync(manifestPath)) {
91
+ const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
92
+ if (!manifest.files.includes(filename)) {
93
+ manifest.files.push(filename);
94
+ manifest.updatedAt = new Date().toISOString();
95
+ writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), 'utf8');
96
+ }
97
+ }
98
+
99
+ return filePath;
100
+ }
101
+
102
+ /**
103
+ * Save agent reasoning log
104
+ * @param {string} feature - Feature name
105
+ * @param {number} checkpointNum - Checkpoint number
106
+ * @param {Object} reasoning - Agent reasoning data
107
+ */
108
+ export function saveAgentReasoning(feature, checkpointNum, reasoning) {
109
+ const entry = {
110
+ timestamp: new Date().toISOString(),
111
+ ...reasoning
112
+ };
113
+ saveArtifact(feature, checkpointNum, 'agent-reasoning.json', JSON.stringify(entry, null, 2));
114
+ }
115
+
116
+ /**
117
+ * Save validator output log
118
+ * @param {string} feature - Feature name
119
+ * @param {number} checkpointNum - Checkpoint number
120
+ * @param {Object} validatorOutput - Validator results
121
+ */
122
+ export function saveValidatorOutput(feature, checkpointNum, validatorOutput) {
123
+ const entry = {
124
+ timestamp: new Date().toISOString(),
125
+ ...validatorOutput
126
+ };
127
+ saveArtifact(feature, checkpointNum, 'validator-output.json', JSON.stringify(entry, null, 2));
128
+ }
129
+
130
+ // ============================================================================
131
+ // Failure Artifacts
132
+ // ============================================================================
133
+
134
+ /**
135
+ * Save failure artifacts when a checkpoint fails
136
+ * @param {string} feature - Feature name
137
+ * @param {number} checkpointNum - Checkpoint number
138
+ * @param {Object} failure - Failure details
139
+ * @param {string} failure.reason - Failure reason
140
+ * @param {Array} failure.errors - Array of error objects
141
+ * @param {number} failure.attemptNumber - Which retry attempt (1-3)
142
+ */
143
+ export function saveFailureArtifacts(feature, checkpointNum, failure) {
144
+ const dir = ensureDir(failuresDir(feature, checkpointNum));
145
+ const filename = `failure-attempt-${failure.attemptNumber || 1}.json`;
146
+
147
+ const entry = {
148
+ feature,
149
+ checkpointNum,
150
+ timestamp: new Date().toISOString(),
151
+ ...failure
152
+ };
153
+
154
+ writeFileSync(join(dir, filename), JSON.stringify(entry, null, 2), 'utf8');
155
+ return join(dir, filename);
156
+ }
157
+
158
+ /**
159
+ * Get all failure artifacts for a checkpoint
160
+ * @param {string} feature - Feature name
161
+ * @param {number} checkpointNum - Checkpoint number
162
+ * @returns {Array} Array of failure records
163
+ */
164
+ export function getFailureArtifacts(feature, checkpointNum) {
165
+ const dir = failuresDir(feature, checkpointNum);
166
+ if (!existsSync(dir)) return [];
167
+
168
+ const files = readdirSync(dir).filter(f => f.endsWith('.json'));
169
+
170
+ return files.map(f => {
171
+ try {
172
+ return JSON.parse(readFileSync(join(dir, f), 'utf8'));
173
+ } catch {
174
+ return null;
175
+ }
176
+ }).filter(Boolean);
177
+ }
178
+
179
+ /**
180
+ * List all checkpoints for a feature
181
+ * @param {string} feature - Feature name
182
+ * @returns {Array} Array of checkpoint info objects
183
+ */
184
+ export function listCheckpointArtifacts(feature) {
185
+ const dir = artifactsDir(feature);
186
+ if (!existsSync(dir)) return [];
187
+
188
+ try {
189
+ return readdirSync(dir, { withFileTypes: true })
190
+ .filter(d => d.isDirectory() && d.name.startsWith('checkpoint-'))
191
+ .map(d => {
192
+ const checkpointPath = join(dir, d.name);
193
+ const manifestPath = join(checkpointPath, 'manifest.json');
194
+ if (existsSync(manifestPath)) {
195
+ return JSON.parse(readFileSync(manifestPath, 'utf8'));
196
+ }
197
+ return { name: d.name, path: checkpointPath };
198
+ });
199
+ } catch {
200
+ return [];
201
+ }
202
+ }
@@ -0,0 +1,269 @@
1
+ /**
2
+ * Trust Manager — Track-record and auto-approval logic
3
+ *
4
+ * Computes trust level from checkpoint pass rate history.
5
+ * Enables auto-approval gates for features with proven track records.
6
+ *
7
+ * Trust levels:
8
+ * low < 80% pass rate
9
+ * medium 80–90%
10
+ * high 90–95%
11
+ * maximum > 95%
12
+ */
13
+
14
+ import { readFileSync, writeFileSync, existsSync } from 'fs';
15
+ import { join } from 'path';
16
+
17
+ // Default trust config (matches llm-interaction.json thresholds)
18
+ const TRUST_THRESHOLDS = {
19
+ low: 0,
20
+ medium: 0.80,
21
+ high: 0.90,
22
+ maximum: 0.95
23
+ };
24
+
25
+ // Gates that can be auto-approved by trust level
26
+ const AUTO_APPROVE_GATES = {
27
+ medium: ['design'],
28
+ high: ['design', 'tasks'],
29
+ maximum: ['design', 'tasks', 'proposal']
30
+ };
31
+
32
+ /**
33
+ * Load state.json
34
+ * @returns {Object} State
35
+ */
36
+ function loadState() {
37
+ const statePath = join(process.cwd(), '.morph/state.json');
38
+ if (!existsSync(statePath)) return { features: {} };
39
+ try {
40
+ return JSON.parse(readFileSync(statePath, 'utf8'));
41
+ } catch {
42
+ return { features: {} };
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Save state.json
48
+ * @param {Object} state
49
+ */
50
+ function saveState(state) {
51
+ const statePath = join(process.cwd(), '.morph/state.json');
52
+ writeFileSync(statePath, JSON.stringify(state, null, 2), 'utf8');
53
+ }
54
+
55
+ /**
56
+ * Calculate trust level from checkpoint history
57
+ * @param {Array} checkpoints - Array of { passed: boolean, ... }
58
+ * @returns {{ level: string, passRate: number, total: number, passed: number }}
59
+ */
60
+ export function calculateTrust(checkpoints = []) {
61
+ if (checkpoints.length === 0) {
62
+ return { level: 'low', passRate: 0, total: 0, passed: 0 };
63
+ }
64
+
65
+ const total = checkpoints.length;
66
+ const passed = checkpoints.filter(c => c.passed).length;
67
+ const passRate = passed / total;
68
+
69
+ let level = 'low';
70
+ if (passRate >= TRUST_THRESHOLDS.maximum) level = 'maximum';
71
+ else if (passRate >= TRUST_THRESHOLDS.high) level = 'high';
72
+ else if (passRate >= TRUST_THRESHOLDS.medium) level = 'medium';
73
+
74
+ return { level, passRate, total, passed };
75
+ }
76
+
77
+ /**
78
+ * Get current trust config for a feature
79
+ * @param {string} featureName
80
+ * @returns {{ level: string, passRate: number, autoApprove: string[], override?: string }}
81
+ */
82
+ export function getTrust(featureName) {
83
+ const state = loadState();
84
+ const feature = state.features?.[featureName];
85
+
86
+ if (!feature) {
87
+ return { level: 'low', passRate: 0, autoApprove: [], source: 'default' };
88
+ }
89
+
90
+ // Check for manual override first
91
+ if (feature.trustConfig?.override) {
92
+ return {
93
+ level: feature.trustConfig.override.level,
94
+ passRate: feature.trustConfig.passRate || 0,
95
+ autoApprove: AUTO_APPROVE_GATES[feature.trustConfig.override.level] || [],
96
+ source: 'manual',
97
+ overrideReason: feature.trustConfig.override.reason,
98
+ overrideAt: feature.trustConfig.override.setAt
99
+ };
100
+ }
101
+
102
+ // Calculate from checkpoint history
103
+ const checkpoints = feature.checkpoints || [];
104
+ const { level, passRate, total, passed } = calculateTrust(checkpoints);
105
+
106
+ return {
107
+ level,
108
+ passRate,
109
+ total,
110
+ passed,
111
+ autoApprove: AUTO_APPROVE_GATES[level] || [],
112
+ source: 'calculated'
113
+ };
114
+ }
115
+
116
+ /**
117
+ * Manually set trust level with reason
118
+ * @param {string} featureName
119
+ * @param {string} level - 'low' | 'medium' | 'high' | 'maximum'
120
+ * @param {string} reason
121
+ */
122
+ export function setTrust(featureName, level, reason) {
123
+ const validLevels = ['low', 'medium', 'high', 'maximum'];
124
+ if (!validLevels.includes(level)) {
125
+ throw new Error(`Invalid trust level: ${level}. Valid: ${validLevels.join(', ')}`);
126
+ }
127
+
128
+ const state = loadState();
129
+ if (!state.features[featureName]) {
130
+ throw new Error(`Feature not found: ${featureName}`);
131
+ }
132
+
133
+ if (!state.features[featureName].trustConfig) {
134
+ state.features[featureName].trustConfig = {};
135
+ }
136
+
137
+ state.features[featureName].trustConfig.override = {
138
+ level,
139
+ reason,
140
+ setAt: new Date().toISOString(),
141
+ setBy: 'manual'
142
+ };
143
+
144
+ saveState(state);
145
+
146
+ return {
147
+ feature: featureName,
148
+ level,
149
+ autoApprove: AUTO_APPROVE_GATES[level] || [],
150
+ reason
151
+ };
152
+ }
153
+
154
+ /**
155
+ * Clear manual trust override (revert to calculated)
156
+ * @param {string} featureName
157
+ */
158
+ export function clearTrustOverride(featureName) {
159
+ const state = loadState();
160
+ if (state.features[featureName]?.trustConfig?.override) {
161
+ delete state.features[featureName].trustConfig.override;
162
+ saveState(state);
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Check if a gate should be auto-approved based on trust level
168
+ * @param {string} featureName
169
+ * @param {string} gate - 'design' | 'tasks' | 'proposal'
170
+ * @returns {{ autoApprove: boolean, level: string, reason: string }}
171
+ */
172
+ export function shouldAutoApprove(featureName, gate) {
173
+ const trust = getTrust(featureName);
174
+
175
+ if (!trust.autoApprove.includes(gate)) {
176
+ return {
177
+ autoApprove: false,
178
+ level: trust.level,
179
+ reason: `Trust level "${trust.level}" does not auto-approve "${gate}" gate (need: ${getMinLevelForGate(gate)})`
180
+ };
181
+ }
182
+
183
+ return {
184
+ autoApprove: true,
185
+ level: trust.level,
186
+ reason: `Auto-approved: ${trust.level} trust (${Math.round(trust.passRate * 100)}% pass rate, ${trust.total} checkpoints)`,
187
+ passRate: trust.passRate,
188
+ checkpointsTotal: trust.total
189
+ };
190
+ }
191
+
192
+ /**
193
+ * Get minimum trust level required for a gate
194
+ * @param {string} gate
195
+ * @returns {string}
196
+ */
197
+ function getMinLevelForGate(gate) {
198
+ for (const [level, gates] of Object.entries(AUTO_APPROVE_GATES)) {
199
+ if (gates.includes(gate)) return level;
200
+ }
201
+ return 'maximum';
202
+ }
203
+
204
+ /**
205
+ * Get trust history for all features
206
+ * @returns {Array} Feature trust summaries
207
+ */
208
+ export function getTrustHistory() {
209
+ const state = loadState();
210
+ const features = state.features || {};
211
+
212
+ return Object.entries(features).map(([name, feature]) => {
213
+ const trust = getTrust(name);
214
+ return {
215
+ feature: name,
216
+ level: trust.level,
217
+ passRate: trust.passRate,
218
+ checkpointsTotal: trust.total || 0,
219
+ checkpointsPassed: trust.passed || 0,
220
+ autoApprove: trust.autoApprove,
221
+ source: trust.source,
222
+ phase: feature.phase,
223
+ status: feature.status
224
+ };
225
+ });
226
+ }
227
+
228
+ /**
229
+ * Auto-calculate and update trust in state
230
+ * @param {string} featureName
231
+ * @returns {Object} Updated trust config
232
+ */
233
+ export function autoCalculateTrust(featureName) {
234
+ const state = loadState();
235
+ const feature = state.features?.[featureName];
236
+
237
+ if (!feature) {
238
+ throw new Error(`Feature not found: ${featureName}`);
239
+ }
240
+
241
+ const checkpoints = feature.checkpoints || [];
242
+ const { level, passRate, total, passed } = calculateTrust(checkpoints);
243
+
244
+ if (!state.features[featureName].trustConfig) {
245
+ state.features[featureName].trustConfig = {};
246
+ }
247
+
248
+ state.features[featureName].trustConfig = {
249
+ ...state.features[featureName].trustConfig,
250
+ level,
251
+ passRate,
252
+ checkpointsTotal: total,
253
+ checkpointsPassed: passed,
254
+ lastCalculated: new Date().toISOString()
255
+ };
256
+
257
+ saveState(state);
258
+
259
+ return {
260
+ feature: featureName,
261
+ level,
262
+ passRate,
263
+ total,
264
+ passed,
265
+ autoApprove: AUTO_APPROVE_GATES[level] || []
266
+ };
267
+ }
268
+
269
+ export { TRUST_THRESHOLDS, AUTO_APPROVE_GATES };
@@ -9,8 +9,8 @@
9
9
 
10
10
  import { existsSync, readFileSync, readdirSync, statSync } from 'fs';
11
11
  import { join, extname } from 'path';
12
- import { parseColors, parseTypography, parseSpacing } from '../design-system-generator.js';
13
- import { detectDesignSystem } from '../design-system-detector.js';
12
+ import { parseColors, parseTypography, parseSpacing } from '../../generators/design-system-generator.js';
13
+ import { detectDesignSystem } from '../../detectors/design-system-detector.js';
14
14
 
15
15
  /**
16
16
  * Validate design system compliance
@@ -10,7 +10,7 @@
10
10
  import { existsSync, readFileSync } from 'fs';
11
11
  import { join } from 'path';
12
12
  import chalk from 'chalk';
13
- import { loadState } from './state-manager.js';
13
+ import { loadState } from '../../core/state/state-manager.js';
14
14
  import { resolveAgentsConfigPath } from '../stacks/stack-resolver.js';
15
15
 
16
16
  /**
@@ -172,63 +172,47 @@ function detectValidators(featureName, projectPath = '.') {
172
172
  async function runSingleValidator(validatorId, projectPath, featureName, options) {
173
173
  switch (validatorId) {
174
174
  case 'architecture': {
175
- const { validateArchitecture } = await import('./validators/architecture-validator.js');
175
+ const { validateArchitecture } = await import('./architecture/architecture-validator.js');
176
176
  return await validateArchitecture(projectPath, options);
177
177
  }
178
178
 
179
179
  case 'packages': {
180
- const { validatePackages } = await import('./validators/package-validator.js');
180
+ const { validatePackages } = await import('./packages/package-validator.js');
181
181
  return await validatePackages(projectPath, options);
182
182
  }
183
183
 
184
184
  case 'contrast': {
185
- const { validateContrast } = await import('./validators/ui-contrast-validator.js');
185
+ const { validateContrast } = await import('./ui/ui-contrast-validator.js');
186
186
  return await validateContrast(projectPath, options);
187
187
  }
188
188
 
189
189
  case 'blazor': {
190
- try {
191
- const { validateBlazorPatterns } = await import('./blazor-validator.js');
192
- return await validateBlazorPatterns(projectPath, options);
193
- } catch {
194
- return null; // Validator may not exist or have different export
195
- }
190
+ const { validateBlazorPatterns } = await import('./blazor/blazor-validator.js');
191
+ return await validateBlazorPatterns(projectPath, options);
196
192
  }
197
193
 
198
194
  case 'blazor-concurrency': {
199
- try {
200
- const { analyzeConcurrency } = await import('./blazor-concurrency-analyzer.js');
201
- return await analyzeConcurrency(projectPath, options);
202
- } catch {
203
- return null;
204
- }
195
+ const { analyzeConcurrency } = await import('./blazor/blazor-concurrency-analyzer.js');
196
+ return await analyzeConcurrency(projectPath, options);
205
197
  }
206
198
 
207
199
  case 'blazor-state': {
208
- try {
209
- const { validateState } = await import('./blazor-state-validator.js');
210
- return await validateState(projectPath, options);
211
- } catch {
212
- return null;
213
- }
200
+ const { validateState } = await import('./blazor/blazor-state-validator.js');
201
+ return await validateState(projectPath, options);
214
202
  }
215
203
 
216
204
  case 'css': {
217
- try {
218
- const { validateCss } = await import('./css-validator.js');
219
- return await validateCss(projectPath, options);
220
- } catch {
221
- return null;
222
- }
205
+ const { validateCss } = await import('./css/css-validator.js');
206
+ return await validateCss(projectPath, options);
223
207
  }
224
208
 
225
209
  case 'contract-compliance': {
226
- const { validateContracts } = await import('./validators/contract-compliance-validator.js');
210
+ const { validateContracts } = await import('./contracts/contract-compliance-validator.js');
227
211
  return await validateContracts(projectPath, featureName, options);
228
212
  }
229
213
 
230
214
  case 'design-system': {
231
- const { validateDesignSystem } = await import('./validators/design-system-validator.js');
215
+ const { validateDesignSystem } = await import('./design-system/design-system-validator.js');
232
216
  return await validateDesignSystem(projectPath, featureName, options);
233
217
  }
234
218
 
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Claude Code Hooks Installer
3
+ *
4
+ * Merges the MORPH-SPEC agent-teams PostToolUse hook into
5
+ * .claude/settings.local.json without overwriting existing config.
6
+ *
7
+ * Called by `morph-spec init` and `morph-spec update`.
8
+ */
9
+
10
+ import { join } from 'path';
11
+ import { readFile, writeFile, mkdir } from 'fs/promises';
12
+ import { existsSync } from 'fs';
13
+
14
+ const DISPATCH_COMMAND = 'node framework/hooks/agent-teams/dispatch.js';
15
+
16
+ /**
17
+ * Install or update the MORPH-SPEC PostToolUse hook in .claude/settings.local.json.
18
+ *
19
+ * @param {string} targetPath - Project root directory
20
+ * @returns {boolean} true if the hook was newly added, false if it was already present
21
+ */
22
+ export async function installClaudeHooks(targetPath) {
23
+ const claudeDir = join(targetPath, '.claude');
24
+ const settingsPath = join(claudeDir, 'settings.local.json');
25
+
26
+ // Read existing settings or start empty
27
+ let settings = {};
28
+ if (existsSync(settingsPath)) {
29
+ try {
30
+ settings = JSON.parse(await readFile(settingsPath, 'utf-8'));
31
+ } catch {
32
+ // Malformed JSON — start fresh to avoid corrupting the file
33
+ settings = {};
34
+ }
35
+ }
36
+
37
+ // Ensure hooks.PostToolUse array exists
38
+ settings.hooks = settings.hooks || {};
39
+ settings.hooks.PostToolUse = settings.hooks.PostToolUse || [];
40
+
41
+ // Check if dispatch.js is already registered (avoid duplicate entries)
42
+ const alreadyRegistered = settings.hooks.PostToolUse.some(entry =>
43
+ Array.isArray(entry.hooks) &&
44
+ entry.hooks.some(h => h.command === DISPATCH_COMMAND)
45
+ );
46
+
47
+ if (alreadyRegistered) {
48
+ return false;
49
+ }
50
+
51
+ // Add the agent-teams dispatch hook
52
+ settings.hooks.PostToolUse.push({
53
+ matcher: 'Bash',
54
+ hooks: [
55
+ {
56
+ type: 'command',
57
+ command: DISPATCH_COMMAND
58
+ }
59
+ ]
60
+ });
61
+
62
+ // Ensure .claude directory exists
63
+ if (!existsSync(claudeDir)) {
64
+ await mkdir(claudeDir, { recursive: true });
65
+ }
66
+
67
+ await writeFile(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
68
+ return true;
69
+ }