@polymorphism-tech/morph-spec 4.8.19 → 4.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (214) hide show
  1. package/CLAUDE.md +21 -0
  2. package/README.md +2 -2
  3. package/bin/morph-spec.js +44 -55
  4. package/bin/task-manager.js +133 -20
  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 +99 -77
  10. package/framework/agents.json +734 -182
  11. package/framework/commands/commit.md +166 -0
  12. package/framework/commands/morph-apply.md +13 -2
  13. package/framework/commands/morph-archive.md +8 -2
  14. package/framework/commands/morph-infra.md +6 -0
  15. package/framework/commands/morph-preflight.md +6 -0
  16. package/framework/commands/morph-proposal.md +56 -7
  17. package/framework/commands/morph-status.md +6 -0
  18. package/framework/commands/morph-troubleshoot.md +6 -0
  19. package/framework/hooks/claude-code/notification/approval-reminder.js +3 -2
  20. package/framework/hooks/claude-code/post-tool-use/context-refresh.js +1 -1
  21. package/framework/hooks/claude-code/post-tool-use/dispatch.js +155 -32
  22. package/framework/hooks/claude-code/post-tool-use/skill-reminder.js +78 -0
  23. package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +8 -17
  24. package/framework/hooks/claude-code/pre-compact/save-morph-context.js +16 -3
  25. package/framework/hooks/claude-code/pre-tool-use/enforce-phase-writes.js +4 -3
  26. package/framework/hooks/claude-code/pre-tool-use/protect-spec-files.js +4 -3
  27. package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +60 -0
  28. package/framework/hooks/claude-code/session-start/inject-morph-context.js +124 -2
  29. package/framework/hooks/claude-code/session-start/post-compact-restore.js +41 -0
  30. package/framework/hooks/claude-code/statusline.py +76 -30
  31. package/framework/hooks/claude-code/stop/validate-completion.js +2 -15
  32. package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +23 -5
  33. package/framework/hooks/claude-code/user-prompt/set-terminal-title.js +14 -6
  34. package/framework/hooks/shared/activity-logger.js +0 -24
  35. package/framework/hooks/shared/compact-restore.js +100 -0
  36. package/framework/hooks/shared/dispatch-helpers.js +116 -0
  37. package/framework/hooks/shared/phase-utils.js +12 -5
  38. package/framework/hooks/shared/skill-reminder-helpers.js +79 -0
  39. package/framework/hooks/shared/stale-task-reset.js +57 -0
  40. package/framework/hooks/shared/state-reader.js +29 -5
  41. package/framework/hooks/shared/worktree-helpers.js +53 -0
  42. package/framework/phases.json +69 -14
  43. package/framework/rules/morph-workflow.md +88 -86
  44. package/framework/skills/level-0-meta/mcp-registry.json +86 -51
  45. package/framework/skills/level-0-meta/{brainstorming → morph-brainstorming}/SKILL.md +14 -17
  46. package/framework/skills/level-0-meta/morph-checklist/SKILL.md +2 -2
  47. package/framework/skills/level-0-meta/{code-review → morph-code-review}/SKILL.md +2 -2
  48. package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/SKILL.md +163 -163
  49. package/framework/skills/level-0-meta/{frontend-review → morph-frontend-review}/SKILL.md +9 -9
  50. package/framework/skills/level-0-meta/morph-init/SKILL.md +77 -12
  51. package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/SKILL.md +62 -15
  52. package/framework/skills/level-0-meta/morph-replicate/SKILL.md +5 -5
  53. package/framework/skills/level-0-meta/morph-replicate/references/blazor-html-mapping.md +1 -1
  54. package/framework/skills/level-0-meta/{simulation-checklist → morph-simulation-checklist}/SKILL.md +1 -1
  55. package/framework/skills/level-0-meta/{terminal-title → morph-terminal-title}/SKILL.md +2 -2
  56. package/framework/skills/level-0-meta/{tool-usage-guide → morph-tool-usage-guide}/SKILL.md +3 -4
  57. package/framework/skills/level-0-meta/{tool-usage-guide → morph-tool-usage-guide}/references/tools-per-phase.md +7 -7
  58. package/framework/skills/level-0-meta/{verification-before-completion → morph-verification-before-completion}/SKILL.md +2 -2
  59. package/framework/skills/level-0-meta/{verification-before-completion → morph-verification-before-completion}/scripts/check-phase-outputs.mjs +2 -2
  60. package/framework/skills/level-1-workflows/morph-phase-clarify/SKILL.md +238 -0
  61. package/framework/skills/level-1-workflows/{phase-codebase-analysis → morph-phase-codebase-analysis}/SKILL.md +3 -3
  62. package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +507 -0
  63. package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/SKILL.md +168 -27
  64. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/code-quality-reviewer-prompt.md +50 -0
  65. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +45 -0
  66. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/spec-reviewer-prompt.md +47 -0
  67. package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +254 -0
  68. package/framework/skills/level-1-workflows/{phase-setup → morph-phase-setup}/SKILL.md +50 -3
  69. package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/SKILL.md +48 -11
  70. package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/scripts/validate-tasks.mjs +3 -3
  71. package/framework/skills/level-1-workflows/{phase-uiux → morph-phase-uiux}/SKILL.md +46 -11
  72. package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +97 -0
  73. package/framework/standards/STANDARDS.json +640 -88
  74. package/framework/standards/infrastructure/vercel/vercel-database.md +106 -0
  75. package/framework/standards/integration/mcp/mcp-tools.md +25 -7
  76. package/framework/templates/REGISTRY.json +1825 -1909
  77. package/framework/templates/context/CONTEXT-FEATURE.md +276 -276
  78. package/framework/templates/docs/onboarding.md +3 -7
  79. package/package.json +2 -7
  80. package/src/commands/agents/dispatch-agents.js +104 -6
  81. package/src/commands/mcp/mcp-setup.js +39 -2
  82. package/src/commands/phase/phase-reset.js +74 -0
  83. package/src/commands/project/doctor.js +34 -51
  84. package/src/commands/project/init.js +1 -1
  85. package/src/commands/project/status.js +2 -2
  86. package/src/commands/project/update.js +381 -365
  87. package/src/commands/project/worktree.js +154 -0
  88. package/src/commands/scope/escalate.js +215 -0
  89. package/src/commands/state/advance-phase.js +132 -68
  90. package/src/commands/state/approve.js +2 -2
  91. package/src/commands/state/index.js +7 -8
  92. package/src/commands/state/phase-runner.js +1 -1
  93. package/src/commands/state/state.js +61 -6
  94. package/src/commands/task/expand.js +100 -0
  95. package/src/commands/tasks/task.js +78 -99
  96. package/src/commands/templates/template-render.js +93 -173
  97. package/src/commands/trust/trust.js +26 -21
  98. package/src/core/paths/output-schema.js +19 -3
  99. package/src/core/state/phase-state-machine.js +7 -4
  100. package/src/core/state/state-manager.js +32 -57
  101. package/src/core/workflows/workflow-detector.js +9 -87
  102. package/src/lib/detectors/claude-config-detector.js +93 -347
  103. package/src/lib/detectors/design-system-detector.js +189 -189
  104. package/src/lib/detectors/index.js +155 -57
  105. package/src/lib/generators/context-generator.js +2 -2
  106. package/src/lib/installers/mcp-installer.js +37 -5
  107. package/src/lib/phase-chain/phase-validator.js +336 -0
  108. package/src/lib/scope/impact-analyzer.js +106 -0
  109. package/src/lib/stack/stack-profile.js +88 -0
  110. package/src/lib/tasks/task-classifier.js +16 -0
  111. package/src/lib/tasks/task-parser.js +1 -1
  112. package/src/lib/tasks/test-runner.js +77 -0
  113. package/src/lib/trust/trust-manager.js +32 -144
  114. package/src/lib/validators/shared/emit-validator-dispatch.js +64 -0
  115. package/src/lib/validators/spec-validator.js +58 -4
  116. package/src/lib/validators/validation-runner.js +23 -11
  117. package/src/scripts/setup-infra.js +255 -224
  118. package/src/utils/agents-installer.js +34 -14
  119. package/src/utils/banner.js +1 -1
  120. package/src/utils/claude-settings-manager.js +1 -1
  121. package/src/utils/file-copier.js +1 -1
  122. package/src/utils/hooks-installer.js +272 -8
  123. package/framework/hooks/dev/check-sync-health.js +0 -117
  124. package/framework/hooks/dev/guard-version-numbers.js +0 -57
  125. package/framework/hooks/dev/sync-standards-registry.js +0 -60
  126. package/framework/hooks/dev/sync-template-registry.js +0 -60
  127. package/framework/hooks/dev/validate-skill-format.js +0 -70
  128. package/framework/hooks/dev/validate-standard-format.js +0 -73
  129. package/framework/skills/level-1-workflows/phase-clarify/SKILL.md +0 -190
  130. package/framework/skills/level-1-workflows/phase-design/SKILL.md +0 -366
  131. package/framework/templates/meta-prompts/hops/hop-retry.md +0 -78
  132. package/framework/templates/meta-prompts/hops/hop-validation.md +0 -97
  133. package/framework/templates/meta-prompts/hops/hop-wrapper.md +0 -36
  134. package/framework/workflows/configs/design-impl.json +0 -49
  135. package/framework/workflows/configs/express.json +0 -45
  136. package/framework/workflows/configs/fast-track.json +0 -42
  137. package/framework/workflows/configs/full-morph.json +0 -79
  138. package/framework/workflows/configs/fusion.json +0 -39
  139. package/framework/workflows/configs/long-running.json +0 -33
  140. package/framework/workflows/configs/spec-only.json +0 -43
  141. package/framework/workflows/configs/ui-refresh.json +0 -49
  142. package/framework/workflows/configs/zero-touch.json +0 -82
  143. package/src/commands/project/index.js +0 -8
  144. package/src/commands/project/monitor.js +0 -295
  145. package/src/commands/project/tutorial.js +0 -115
  146. package/src/commands/state/validate-phase.js +0 -238
  147. package/src/commands/templates/generate-contracts.js +0 -445
  148. package/src/core/index.js +0 -10
  149. package/src/core/orchestrator.js +0 -171
  150. package/src/core/registry/command-registry.js +0 -28
  151. package/src/core/registry/index.js +0 -8
  152. package/src/core/registry/validator-registry.js +0 -204
  153. package/src/core/state/index.js +0 -8
  154. package/src/core/templates/index.js +0 -9
  155. package/src/core/templates/template-data-sources.js +0 -325
  156. package/src/core/templates/template-validator.js +0 -296
  157. package/src/core/workflows/index.js +0 -7
  158. package/src/generator/config-generator.js +0 -206
  159. package/src/generator/templates/config.json.template +0 -40
  160. package/src/generator/templates/project.md.template +0 -67
  161. package/src/lib/agents/micro-agent-factory.js +0 -161
  162. package/src/lib/analysis/complexity-analyzer.js +0 -441
  163. package/src/lib/analysis/index.js +0 -7
  164. package/src/lib/analytics/analytics-engine.js +0 -345
  165. package/src/lib/checkpoints/checkpoint-hooks.js +0 -298
  166. package/src/lib/checkpoints/index.js +0 -7
  167. package/src/lib/context/context-bundler.js +0 -241
  168. package/src/lib/context/context-optimizer.js +0 -212
  169. package/src/lib/context/context-tracker.js +0 -273
  170. package/src/lib/context/core-four-tracker.js +0 -201
  171. package/src/lib/context/mcp-optimizer.js +0 -200
  172. package/src/lib/detectors/config-detector.js +0 -223
  173. package/src/lib/detectors/standards-generator.js +0 -335
  174. package/src/lib/detectors/structure-detector.js +0 -275
  175. package/src/lib/execution/fusion-executor.js +0 -304
  176. package/src/lib/execution/parallel-executor.js +0 -270
  177. package/src/lib/hooks/stop-hook-executor.js +0 -286
  178. package/src/lib/hops/hop-composer.js +0 -221
  179. package/src/lib/monitor/agent-resolver.js +0 -144
  180. package/src/lib/monitor/renderer.js +0 -230
  181. package/src/lib/orchestration/index.js +0 -7
  182. package/src/lib/orchestration/team-orchestrator.js +0 -404
  183. package/src/lib/phase-chain/eligibility-checker.js +0 -243
  184. package/src/lib/threads/thread-coordinator.js +0 -238
  185. package/src/lib/threads/thread-manager.js +0 -317
  186. package/src/lib/tracking/artifact-trail.js +0 -202
  187. package/src/sanitizer/context-sanitizer.js +0 -221
  188. package/src/sanitizer/patterns.js +0 -163
  189. package/src/scanner/project-scanner.js +0 -242
  190. package/src/ui/diff-display.js +0 -91
  191. package/src/ui/interactive-wizard.js +0 -96
  192. package/src/ui/user-review.js +0 -211
  193. package/src/ui/wizard-questions.js +0 -188
  194. package/src/utils/color-utils.js +0 -70
  195. package/src/utils/process-handler.js +0 -97
  196. package/src/writer/file-writer.js +0 -86
  197. /package/framework/skills/level-0-meta/{brainstorming → morph-brainstorming}/references/proposal-example.md +0 -0
  198. /package/framework/skills/level-0-meta/{code-review → morph-code-review}/references/review-example.md +0 -0
  199. /package/framework/skills/level-0-meta/{code-review → morph-code-review}/references/review-guidelines.md +0 -0
  200. /package/framework/skills/level-0-meta/{code-review → morph-code-review}/scripts/scan-csharp.mjs +0 -0
  201. /package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/references/review-example-nextjs.md +0 -0
  202. /package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/scripts/scan-nextjs.mjs +0 -0
  203. /package/framework/skills/level-0-meta/{frontend-review → morph-frontend-review}/scripts/scan-accessibility.mjs +0 -0
  204. /package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/scripts/detect-dev-server.mjs +0 -0
  205. /package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/scripts/detect-stack.mjs +0 -0
  206. /package/framework/skills/level-0-meta/{terminal-title → morph-terminal-title}/scripts/set_title.sh +0 -0
  207. /package/framework/skills/level-1-workflows/{phase-clarify → morph-phase-clarify}/references/clarifications-example.md +0 -0
  208. /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/architecture-analysis-guide.md +0 -0
  209. /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/spec-authoring-guide.md +0 -0
  210. /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/spec-example.md +0 -0
  211. /package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/references/recap-example.md +0 -0
  212. /package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/references/vsa-implementation-guide.md +0 -0
  213. /package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/references/task-planning-patterns.md +0 -0
  214. /package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/references/tasks-example.md +0 -0
@@ -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
-
@@ -26,16 +26,18 @@ export const OUTPUT_SCHEMA = {
26
26
  proposal: { filename: 'proposal.md', phaseDir: '0-proposal', phase: 'proposal', protected: false },
27
27
  schemaAnalysis:{ filename: 'schema-analysis.md', phaseDir: '1-design', phase: 'design', protected: true, approvalGate: 'design' },
28
28
  spec: { filename: 'spec.md', phaseDir: '1-design', phase: 'design', protected: true, approvalGate: 'design' },
29
- clarifications:{ filename: 'clarifications.md', phaseDir: '2-clarify', phase: 'clarify', protected: false },
29
+ clarifications:{ filename: 'clarifications.md', phaseDir: '1-design', 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
- tasks: { filename: 'tasks.md', phaseDir: '3-tasks', phase: 'tasks', protected: true, approvalGate: 'tasks' },
33
+ plan: { filename: 'plan.md', phaseDir: '3-plan', phase: 'plan', protected: true, approvalGate: 'plan' },
34
+ tasks: { filename: 'tasks.md', phaseDir: '4-tasks', phase: 'tasks', protected: true, approvalGate: 'tasks' },
33
35
  uiDesignSystem:{ filename: 'design-system.md', phaseDir: '2-ui', phase: 'uiux', protected: true, approvalGate: 'uiux' },
34
36
  uiMockups: { filename: 'mockups.md', phaseDir: '2-ui', phase: 'uiux', protected: true, approvalGate: 'uiux' },
35
37
  uiComponents: { filename: 'components.md', phaseDir: '2-ui', phase: 'uiux', protected: true, approvalGate: 'uiux' },
36
38
  uiFlows: { filename: 'flows.md', phaseDir: '2-ui', phase: 'uiux', protected: true, approvalGate: 'uiux' },
37
39
  decisions: { filename: 'decisions.md', phaseDir: '1-design', phase: 'design', protected: false },
38
- recap: { filename: 'recap.md', phaseDir: '4-implement', phase: 'implement', protected: false },
40
+ recap: { filename: 'recap.md', phaseDir: '5-implement', phase: 'implement', protected: false },
39
41
  };
40
42
 
41
43
  // ============================================================================
@@ -134,3 +136,17 @@ export const PROTECTED_SPEC_FILES = Object.fromEntries(
134
136
  .filter(([, entry]) => entry.protected && entry.approvalGate)
135
137
  .map(([, entry]) => [entry.filename, entry.approvalGate])
136
138
  );
139
+
140
+ // ============================================================================
141
+ // Stack-aware Helpers
142
+ // ============================================================================
143
+
144
+ /**
145
+ * Resolve the correct contracts output type key for a given stack.
146
+ * @param {string|null} stack
147
+ * @returns {'contracts'|'contractsTs'}
148
+ */
149
+ export function resolveContractsOutputType(stack) {
150
+ const NEXTJS_STACKS = new Set(['nextjs', 'next', 'next.js']);
151
+ return NEXTJS_STACKS.has(stack?.toLowerCase?.()) ? 'contractsTs' : 'contracts';
152
+ }
@@ -14,7 +14,8 @@ const VALID_TRANSITIONS = {
14
14
  'setup': ['uiux', 'design'], // Can skip UI/UX if no frontend
15
15
  'uiux': ['design'],
16
16
  'design': ['clarify'],
17
- 'clarify': ['tasks'],
17
+ 'clarify': ['plan'],
18
+ 'plan': ['tasks'],
18
19
  'tasks': ['implement'],
19
20
  'implement': ['sync', 'archived'], // Can skip sync
20
21
  'sync': ['archived']
@@ -29,9 +30,10 @@ const PHASE_NAMES = {
29
30
  'uiux': 'UI/UX Design (Phase 1.5)',
30
31
  'design': 'Design (Phase 2)',
31
32
  'clarify': 'Clarify (Phase 3)',
32
- 'tasks': 'Tasks (Phase 4)',
33
- 'implement': 'Implement (Phase 5)',
34
- 'sync': 'Sync (Phase 6)',
33
+ 'plan': 'Plan (Phase 4)',
34
+ 'tasks': 'Tasks (Phase 5)',
35
+ 'implement': 'Implement (Phase 6)',
36
+ 'sync': 'Sync (Phase 7)',
35
37
  'archived': 'Archived'
36
38
  };
37
39
 
@@ -124,6 +126,7 @@ export function getPhaseSequence() {
124
126
  'uiux', // optional
125
127
  'design',
126
128
  'clarify',
129
+ 'plan',
127
130
  'tasks',
128
131
  'implement',
129
132
  'sync', // optional