@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,99 +1,78 @@
1
- /**
2
- * Task management commands
3
- * Spawns bin/task-manager.js (ESM) as a subprocess.
4
- */
5
-
6
- import { spawn } from 'child_process';
7
- import { fileURLToPath } from 'url';
8
- import { dirname, join } from 'path';
9
- import chalk from 'chalk';
10
-
11
- const __dirname = dirname(fileURLToPath(import.meta.url));
12
- const taskManagerPath = join(__dirname, '..', '..', '..', 'bin', 'task-manager.js');
13
-
14
- /**
15
- * Execute task-manager.js with given arguments
16
- */
17
- function executeTaskManager(args) {
18
- return new Promise((resolve, reject) => {
19
- // Note: shell: false (default) is intentional to preserve arguments with spaces
20
- const child = spawn('node', [taskManagerPath, ...args], {
21
- stdio: 'inherit'
22
- });
23
-
24
- child.on('close', (code) => {
25
- if (code === 0) {
26
- resolve();
27
- } else {
28
- reject(new Error(`Task manager exited with code ${code}`));
29
- }
30
- });
31
-
32
- child.on('error', (error) => {
33
- reject(error);
34
- });
35
- });
36
- }
37
-
38
- /**
39
- * Complete tasks command
40
- */
41
- export async function taskDoneCommand(featureName, taskIds, options) {
42
- try {
43
- const args = ['done', featureName, ...taskIds];
44
- if (options.skipValidation) args.push('--skip-validation');
45
- if (options.dryRun) args.push('--dry-run');
46
- await executeTaskManager(args);
47
- } catch (error) {
48
- console.error(chalk.red(`Error: ${error.message}`));
49
- process.exit(1);
50
- }
51
- }
52
-
53
- /**
54
- * Bulk-complete tasks command
55
- */
56
- export async function taskBulkDoneCommand(featureName, rangeArg, options) {
57
- try {
58
- const args = ['bulk-done', featureName];
59
- if (options.all) {
60
- args.push('--all');
61
- } else if (options.from && options.to) {
62
- args.push('--from', options.from, '--to', options.to);
63
- } else if (rangeArg) {
64
- args.push(rangeArg);
65
- }
66
- if (options.skipValidation) args.push('--skip-validation');
67
- if (options.dryRun) args.push('--dry-run');
68
- await executeTaskManager(args);
69
- } catch (error) {
70
- console.error(chalk.red(`Error: ${error.message}`));
71
- process.exit(1);
72
- }
73
- }
74
-
75
- /**
76
- * Start task command
77
- */
78
- export async function taskStartCommand(featureName, taskId, options) {
79
- try {
80
- const args = ['start', featureName, taskId];
81
- await executeTaskManager(args);
82
- } catch (error) {
83
- console.error(chalk.red(`Error: ${error.message}`));
84
- process.exit(1);
85
- }
86
- }
87
-
88
- /**
89
- * Get next task command
90
- */
91
- export async function taskNextCommand(featureName, options) {
92
- try {
93
- const args = ['next', featureName];
94
- await executeTaskManager(args);
95
- } catch (error) {
96
- console.error(chalk.red(`Error: ${error.message}`));
97
- process.exit(1);
98
- }
99
- }
1
+ /**
2
+ * Task management commands
3
+ * Spawns bin/task-manager.js (ESM) as a subprocess.
4
+ */
5
+
6
+ import { spawn } from 'child_process';
7
+ import { fileURLToPath } from 'url';
8
+ import { dirname, join } from 'path';
9
+ import chalk from 'chalk';
10
+
11
+ const __dirname = dirname(fileURLToPath(import.meta.url));
12
+ const taskManagerPath = join(__dirname, '..', '..', '..', 'bin', 'task-manager.js');
13
+
14
+ /**
15
+ * Execute task-manager.js with given arguments
16
+ */
17
+ function executeTaskManager(args) {
18
+ return new Promise((resolve, reject) => {
19
+ // Note: shell: false (default) is intentional to preserve arguments with spaces
20
+ const child = spawn('node', [taskManagerPath, ...args], {
21
+ stdio: 'inherit'
22
+ });
23
+
24
+ child.on('close', (code) => {
25
+ if (code === 0) {
26
+ resolve();
27
+ } else {
28
+ reject(new Error(`Task manager exited with code ${code}`));
29
+ }
30
+ });
31
+
32
+ child.on('error', (error) => {
33
+ reject(error);
34
+ });
35
+ });
36
+ }
37
+
38
+ /**
39
+ * Complete tasks command
40
+ */
41
+ export async function taskDoneCommand(featureName, taskIds, options) {
42
+ try {
43
+ const args = ['done', featureName, ...taskIds];
44
+ if (options.skipValidation) args.push('--skip-validation');
45
+ if (options.dryRun) args.push('--dry-run');
46
+ await executeTaskManager(args);
47
+ } catch (error) {
48
+ console.error(chalk.red(`Error: ${error.message}`));
49
+ process.exit(1);
50
+ }
51
+ }
52
+
53
+ /**
54
+ /**
55
+ * Start task command
56
+ */
57
+ export async function taskStartCommand(featureName, taskId, options) {
58
+ try {
59
+ const args = ['start', featureName, taskId];
60
+ await executeTaskManager(args);
61
+ } catch (error) {
62
+ console.error(chalk.red(`Error: ${error.message}`));
63
+ process.exit(1);
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Get next task command
69
+ */
70
+ export async function taskNextCommand(featureName, options) {
71
+ try {
72
+ const args = ['next', featureName];
73
+ await executeTaskManager(args);
74
+ } catch (error) {
75
+ console.error(chalk.red(`Error: ${error.message}`));
76
+ process.exit(1);
77
+ }
78
+ }
@@ -1,173 +1,93 @@
1
- /**
2
- * Template Render Command - Render a template by ID with variables
3
- */
4
-
5
- import { writeFileSync, mkdirSync } from 'fs';
6
- import { dirname, join } from 'path';
7
- import chalk from 'chalk';
8
- import { logger } from '../../utils/logger.js';
9
- import { getTemplateById, resolveTemplatePathById } from '../../core/templates/template-registry.js';
10
- import { renderTemplate } from '../../core/templates/template-renderer.js';
11
- import { renderHOP, listAvailableHOPs } from '../../lib/hops/hop-composer.js';
12
-
13
- /**
14
- * Render HOP (Higher-Order Prompt) template command
15
- */
16
- export async function hopRenderCommand(hopId, outputPath, variablesJson, options) {
17
- logger.header(`Rendering HOP: ${hopId}`);
18
- logger.blank();
19
-
20
- try {
21
- // Parse variables
22
- let variables = {};
23
- try {
24
- variables = JSON.parse(variablesJson || '{}');
25
- } catch (error) {
26
- logger.error('Invalid JSON in variables argument');
27
- logger.dim(` ${error.message}`);
28
- process.exit(1);
29
- }
30
-
31
- // Add defaults
32
- variables.DATE = variables.DATE || new Date().toISOString().split('T')[0];
33
- variables.YEAR = variables.YEAR || new Date().getFullYear().toString();
34
- variables.TIMESTAMP = variables.TIMESTAMP || new Date().toISOString();
35
-
36
- // Render via hop-composer
37
- const frameworkRoot = join(process.cwd(), 'framework');
38
- const rendered = renderHOP(hopId, variables, { frameworkRoot });
39
-
40
- if (options?.dryRun || !outputPath) {
41
- logger.info(chalk.cyan('📋 HOP Preview:'));
42
- logger.blank();
43
- console.log(rendered);
44
- logger.blank();
45
- if (outputPath) logger.dim(`Would be written to: ${outputPath}`);
46
- } else {
47
- mkdirSync(dirname(outputPath), { recursive: true });
48
- writeFileSync(outputPath, rendered, 'utf-8');
49
- logger.success(`✅ HOP rendered: ${chalk.cyan(outputPath)}`);
50
- }
51
- } catch (error) {
52
- logger.error(`Failed to render HOP: ${error.message}`);
53
- process.exit(1);
54
- }
55
- }
56
-
57
- /**
58
- * List available HOPs
59
- */
60
- export async function hopListCommand(options) {
61
- logger.header('Available HOPs (Higher-Order Prompts)');
62
- logger.blank();
63
-
64
- const frameworkRoot = join(process.cwd(), 'framework');
65
- const hops = listAvailableHOPs(frameworkRoot);
66
-
67
- if (hops.length === 0) {
68
- logger.warn('No HOPs found in framework/templates/meta-prompts/');
69
- return;
70
- }
71
-
72
- const byType = {};
73
- for (const hop of hops) {
74
- const type = hop.type || 'other';
75
- if (!byType[type]) byType[type] = [];
76
- byType[type].push(hop);
77
- }
78
-
79
- for (const [type, list] of Object.entries(byType)) {
80
- console.log(chalk.cyan(`\n ${type.toUpperCase()}`));
81
- for (const hop of list) {
82
- console.log(` ${chalk.white(hop.id.padEnd(30))} ${chalk.gray(hop.description || hop.path)}`);
83
- }
84
- }
85
-
86
- logger.blank();
87
- logger.dim(`Total: ${hops.length} HOPs`);
88
- logger.dim(`Render: morph-spec hop render <hop-id> <output-file> '<variables-json>'`);
89
- }
90
-
91
- /**
92
- * Render template command
93
- */
94
- export async function templateRenderCommand(templateId, outputPath, variablesJson, options) {
95
- logger.header(`Rendering Template: ${templateId}`);
96
- logger.blank();
97
-
98
- try {
99
- // 1. Validate template exists
100
- const template = getTemplateById(templateId);
101
-
102
- if (!template) {
103
- logger.error(`Template not found: ${templateId}`);
104
- logger.dim('\n💡 Use `morph-spec template list` to see available templates');
105
- process.exit(1);
106
- }
107
-
108
- // 2. Resolve template path
109
- const projectPath = options.projectPath || process.cwd();
110
- const templatePath = resolveTemplatePathById(templateId, projectPath);
111
-
112
- if (!templatePath) {
113
- logger.error(`Template file not found for ${templateId}`);
114
- logger.dim(' Searched in: .morph/framework/templates/ and framework/templates/');
115
- process.exit(1);
116
- }
117
-
118
- logger.dim(`Template: ${templatePath}`);
119
-
120
- // 3. Parse variables
121
- let variables = {};
122
- try {
123
- variables = JSON.parse(variablesJson);
124
- } catch (error) {
125
- logger.error('Invalid JSON in variables argument');
126
- logger.dim(` ${error.message}`);
127
- process.exit(1);
128
- }
129
-
130
- // 4. Add default variables
131
- const defaults = {
132
- DATE: new Date().toISOString().split('T')[0],
133
- YEAR: new Date().getFullYear().toString(),
134
- TIMESTAMP: new Date().toISOString()
135
- };
136
-
137
- const allVariables = { ...defaults, ...variables };
138
-
139
- logger.dim(`Variables: ${Object.keys(allVariables).length} provided`);
140
- logger.blank();
141
-
142
- // 5. Render template
143
- const renderedContent = renderTemplate(templatePath, allVariables);
144
-
145
- // 6. Output
146
- if (options.dryRun) {
147
- logger.info(chalk.cyan('📋 DRY RUN - Preview (file will NOT be written)'));
148
- logger.blank();
149
- console.log(renderedContent);
150
- logger.blank();
151
- logger.dim(`Would be written to: ${outputPath}`);
152
- } else {
153
- // Create output directory if needed
154
- const outputDir = dirname(outputPath);
155
- mkdirSync(outputDir, { recursive: true });
156
-
157
- // Write file
158
- writeFileSync(outputPath, renderedContent, 'utf-8');
159
-
160
- logger.success(`✅ Template rendered successfully`);
161
- logger.dim(` Output: ${chalk.cyan(outputPath)}`);
162
- }
163
-
164
- logger.blank();
165
-
166
- } catch (error) {
167
- logger.error(`Failed to render template: ${error.message}`);
168
- if (options.verbose) {
169
- console.error(error.stack);
170
- }
171
- process.exit(1);
172
- }
173
- }
1
+ /**
2
+ * Template Render Command - Render a template by ID with variables
3
+ */
4
+
5
+ import { writeFileSync, mkdirSync } from 'fs';
6
+ import { dirname, join } from 'path';
7
+ import chalk from 'chalk';
8
+ import { logger } from '../../utils/logger.js';
9
+ import { getTemplateById, resolveTemplatePathById } from '../../core/templates/template-registry.js';
10
+ import { renderTemplate } from '../../core/templates/template-renderer.js';
11
+ /**
12
+ * Render template command
13
+ */
14
+ export async function templateRenderCommand(templateId, outputPath, variablesJson, options) {
15
+ logger.header(`Rendering Template: ${templateId}`);
16
+ logger.blank();
17
+
18
+ try {
19
+ // 1. Validate template exists
20
+ const template = getTemplateById(templateId);
21
+
22
+ if (!template) {
23
+ logger.error(`Template not found: ${templateId}`);
24
+ logger.dim('\n💡 Available template IDs are listed in framework/templates/ — check subdirectories code/, frontend/, meta-prompts/');
25
+ process.exit(1);
26
+ }
27
+
28
+ // 2. Resolve template path
29
+ const projectPath = options.projectPath || process.cwd();
30
+ const templatePath = resolveTemplatePathById(templateId, projectPath);
31
+
32
+ if (!templatePath) {
33
+ logger.error(`Template file not found for ${templateId}`);
34
+ logger.dim(' Searched in: .morph/framework/templates/ and framework/templates/');
35
+ process.exit(1);
36
+ }
37
+
38
+ logger.dim(`Template: ${templatePath}`);
39
+
40
+ // 3. Parse variables
41
+ let variables = {};
42
+ try {
43
+ variables = JSON.parse(variablesJson);
44
+ } catch (error) {
45
+ logger.error('Invalid JSON in variables argument');
46
+ logger.dim(` ${error.message}`);
47
+ process.exit(1);
48
+ }
49
+
50
+ // 4. Add default variables
51
+ const defaults = {
52
+ DATE: new Date().toISOString().split('T')[0],
53
+ YEAR: new Date().getFullYear().toString(),
54
+ TIMESTAMP: new Date().toISOString()
55
+ };
56
+
57
+ const allVariables = { ...defaults, ...variables };
58
+
59
+ logger.dim(`Variables: ${Object.keys(allVariables).length} provided`);
60
+ logger.blank();
61
+
62
+ // 5. Render template
63
+ const renderedContent = renderTemplate(templatePath, allVariables);
64
+
65
+ // 6. Output
66
+ if (options.dryRun) {
67
+ logger.info(chalk.cyan('📋 DRY RUN - Preview (file will NOT be written)'));
68
+ logger.blank();
69
+ console.log(renderedContent);
70
+ logger.blank();
71
+ logger.dim(`Would be written to: ${outputPath}`);
72
+ } else {
73
+ // Create output directory if needed
74
+ const outputDir = dirname(outputPath);
75
+ mkdirSync(outputDir, { recursive: true });
76
+
77
+ // Write file
78
+ writeFileSync(outputPath, renderedContent, 'utf-8');
79
+
80
+ logger.success(`✅ Template rendered successfully`);
81
+ logger.dim(` Output: ${chalk.cyan(outputPath)}`);
82
+ }
83
+
84
+ logger.blank();
85
+
86
+ } catch (error) {
87
+ logger.error(`Failed to render template: ${error.message}`);
88
+ if (options.verbose) {
89
+ console.error(error.stack);
90
+ }
91
+ process.exit(1);
92
+ }
93
+ }
@@ -4,6 +4,11 @@
4
4
  * Usage:
5
5
  * morph-spec trust status [feature]
6
6
  * morph-spec trust set <feature> <level> [reason]
7
+ *
8
+ * Levels:
9
+ * manual — nothing auto-approved (default)
10
+ * high — auto-approve low-risk gates (design, tasks)
11
+ * auto — auto-approve all gates
7
12
  */
8
13
 
9
14
  import chalk from 'chalk';
@@ -11,10 +16,15 @@ import {
11
16
  getTrust,
12
17
  setTrust,
13
18
  getTrustHistory,
14
- TRUST_THRESHOLDS,
15
19
  AUTO_APPROVE_GATES
16
20
  } from '../../lib/trust/trust-manager.js';
17
21
 
22
+ const LEVEL_DESCRIPTIONS = {
23
+ manual: 'manual approval required for all gates',
24
+ high: 'auto-approves low-risk gates (design, tasks)',
25
+ auto: 'auto-approves all gates'
26
+ };
27
+
18
28
  /**
19
29
  * Trust status — current trust level for a feature or all features
20
30
  */
@@ -25,15 +35,13 @@ export async function trustStatusCommand(featureName, options) {
25
35
  console.log(chalk.bold(`\n Trust Status — ${featureName}`));
26
36
  console.log(' ' + '─'.repeat(50));
27
37
 
28
- const levelColor = trust.level === 'maximum' ? chalk.green
38
+ const levelColor = trust.level === 'auto' ? chalk.green
29
39
  : trust.level === 'high' ? chalk.cyan
30
- : trust.level === 'medium' ? chalk.yellow
31
- : chalk.red;
40
+ : chalk.yellow;
32
41
 
33
42
  console.log(` Level: ${levelColor(trust.level.toUpperCase())}`);
34
- console.log(` Pass Rate: ${Math.round((trust.passRate || 0) * 100)}%`);
35
- console.log(` Checkpoints: ${trust.passed || 0}/${trust.total || 0} passed`);
36
- console.log(` Source: ${trust.source || 'calculated'}`);
43
+ console.log(` Description: ${LEVEL_DESCRIPTIONS[trust.level] || trust.level}`);
44
+ console.log(` Source: ${trust.source || 'default'}`);
37
45
 
38
46
  if (trust.source === 'manual') {
39
47
  console.log(chalk.yellow(` Override: ${trust.overrideReason || 'No reason provided'}`));
@@ -46,11 +54,12 @@ export async function trustStatusCommand(featureName, options) {
46
54
  trust.autoApprove.forEach(g => console.log(chalk.green(` ✓ ${g}`)));
47
55
  }
48
56
 
49
- console.log(`\n Trust thresholds:`);
50
- for (const [level, threshold] of Object.entries(TRUST_THRESHOLDS)) {
57
+ console.log(`\n Available trust levels:`);
58
+ for (const [level, desc] of Object.entries(LEVEL_DESCRIPTIONS)) {
51
59
  const gates = AUTO_APPROVE_GATES[level] || [];
52
60
  const marker = trust.level === level ? chalk.bold(' ← current') : '';
53
- console.log(` ${level.padEnd(10)} ${Math.round(threshold * 100)}% auto-approves: ${gates.join(', ') || 'none'}${marker}`);
61
+ const gateList = gates.length > 0 ? gates.join(', ') : 'none';
62
+ console.log(` ${level.padEnd(10)} auto-approves: ${gateList}${marker}`);
54
63
  }
55
64
 
56
65
  console.log();
@@ -63,21 +72,18 @@ export async function trustStatusCommand(featureName, options) {
63
72
  }
64
73
 
65
74
  console.log(chalk.bold('\n Trust Overview'));
66
- console.log(' ' + '─'.repeat(70));
67
- console.log(` ${'Feature'.padEnd(25)} ${'Level'.padEnd(10)} ${'Pass%'.padEnd(8)} ${'CPs'.padEnd(8)} Phase`);
68
- console.log(' ' + '─'.repeat(70));
75
+ console.log(' ' + '─'.repeat(60));
76
+ console.log(` ${'Feature'.padEnd(25)} ${'Level'.padEnd(10)} Phase`);
77
+ console.log(' ' + '─'.repeat(60));
69
78
 
70
79
  for (const f of history) {
71
- const levelColor = f.level === 'maximum' ? chalk.green
80
+ const levelColor = f.level === 'auto' ? chalk.green
72
81
  : f.level === 'high' ? chalk.cyan
73
- : f.level === 'medium' ? chalk.yellow
74
- : chalk.red;
82
+ : chalk.yellow;
75
83
 
76
84
  console.log(
77
85
  ` ${f.feature.padEnd(25)} ` +
78
86
  `${levelColor(f.level.padEnd(10))} ` +
79
- `${String(Math.round(f.passRate * 100) + '%').padEnd(8)} ` +
80
- `${String(f.checkpointsTotal).padEnd(8)} ` +
81
87
  `${f.phase || 'N/A'}`
82
88
  );
83
89
  }
@@ -91,7 +97,7 @@ export async function trustStatusCommand(featureName, options) {
91
97
  export async function trustSetCommand(featureName, level, reason, options) {
92
98
  if (!featureName || !level) {
93
99
  console.error(chalk.red('Usage: morph-spec trust set <feature> <level> [reason]'));
94
- console.error(chalk.gray('Levels: low, medium, high, maximum'));
100
+ console.error(chalk.gray('Levels: manual, high, auto'));
95
101
  process.exit(1);
96
102
  }
97
103
 
@@ -104,10 +110,9 @@ export async function trustSetCommand(featureName, level, reason, options) {
104
110
  console.log(` Level: ${result.level}`);
105
111
  console.log(` Auto-approves: ${result.autoApprove.join(', ') || 'none'}`);
106
112
  console.log(` Reason: ${result.reason}`);
107
- console.log(chalk.gray('\n Note: Trust reverts to calculated level after next checkpoint.\n'));
113
+ console.log();
108
114
  } catch (err) {
109
115
  console.error(chalk.red(`Error: ${err.message}`));
110
116
  process.exit(1);
111
117
  }
112
118
  }
113
-
@@ -28,6 +28,7 @@ export const OUTPUT_SCHEMA = {
28
28
  spec: { filename: 'spec.md', phaseDir: '1-design', phase: 'design', protected: true, approvalGate: 'design' },
29
29
  clarifications:{ filename: 'clarifications.md', phaseDir: '2-clarify', phase: 'clarify', protected: false },
30
30
  contracts: { filename: 'contracts.cs', phaseDir: '1-design', phase: 'design', protected: true, approvalGate: 'design' },
31
+ contractsTs: { filename: 'contracts.ts', phaseDir: '1-design', phase: 'design', protected: true, approvalGate: 'design' },
31
32
  contractsVsa: { filename: 'contracts-vsa.cs', phaseDir: '1-design', phase: 'design', protected: true, approvalGate: 'design' },
32
33
  tasks: { filename: 'tasks.md', phaseDir: '3-tasks', phase: 'tasks', protected: true, approvalGate: 'tasks' },
33
34
  uiDesignSystem:{ filename: 'design-system.md', phaseDir: '2-ui', phase: 'uiux', protected: true, approvalGate: 'uiux' },
@@ -134,3 +135,17 @@ export const PROTECTED_SPEC_FILES = Object.fromEntries(
134
135
  .filter(([, entry]) => entry.protected && entry.approvalGate)
135
136
  .map(([, entry]) => [entry.filename, entry.approvalGate])
136
137
  );
138
+
139
+ // ============================================================================
140
+ // Stack-aware Helpers
141
+ // ============================================================================
142
+
143
+ /**
144
+ * Resolve the correct contracts output type key for a given stack.
145
+ * @param {string|null} stack
146
+ * @returns {'contracts'|'contractsTs'}
147
+ */
148
+ export function resolveContractsOutputType(stack) {
149
+ const NEXTJS_STACKS = new Set(['nextjs', 'next', 'next.js']);
150
+ return NEXTJS_STACKS.has(stack?.toLowerCase?.()) ? 'contractsTs' : 'contracts';
151
+ }