@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
package/CLAUDE.md CHANGED
@@ -18,6 +18,7 @@
18
18
  - Ignore standards in `.morph/framework/standards/`
19
19
  - Create infrastructure manually
20
20
  - Generate code without defined contracts
21
+ - List questions as plain text — always use the `AskUserQuestion` tool
21
22
 
22
23
  **ALWAYS:**
23
24
  - Follow the mandatory phases
@@ -25,6 +26,7 @@
25
26
  - Document decisions in `decisions.md`
26
27
  - Checkpoint every 3 implemented tasks
27
28
  - Use Infrastructure as Code
29
+ - Use `AskUserQuestion` tool whenever asking the user anything (1–4 questions per call; split into sequential calls if more)
28
30
 
29
31
  ---
30
32
 
@@ -48,6 +50,25 @@
48
50
  | `.morph/framework/` | **READ-ONLY** — framework files managed by morph-spec |
49
51
  | `.morph/config/config.json` | Project configuration (editable) |
50
52
 
53
+ ### mark-output types
54
+
55
+ Use `morph-spec state mark-output <feature> <type>` with one of these exact type names:
56
+
57
+ | Type | Phase | kebab alias |
58
+ |------|-------|-------------|
59
+ | `proposal` | proposal | — |
60
+ | `schemaAnalysis` | design | `schema-analysis` |
61
+ | `spec` | design | — |
62
+ | `contracts` | design | — |
63
+ | `contractsVsa` | design | `contracts-vsa` |
64
+ | `decisions` | design | — |
65
+ | `clarifications` | clarify | — |
66
+ | `tasks` | tasks | — |
67
+ | `uiDesignSystem` | uiux | `ui-design-system` |
68
+ | `uiMockups` | uiux | `ui-mockups` |
69
+ | `uiComponents` | uiux | `ui-components` |
70
+ | `uiFlows` | uiux | `ui-flows` |
71
+ | `recap` | implement | — |
51
72
  ---
52
73
 
53
74
  ## Phase Sequence
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  > Spec-driven development framework for multi-stack projects. Turns feature requests into implementation-ready code through structured, AI-orchestrated phases.
4
4
 
5
5
  **Package:** `@polymorphism-tech/morph-spec`
6
- **Version:** 4.8.19
6
+ **Version:** 4.10.0
7
7
  **Requires:** Node.js 18+, Claude Code
8
8
 
9
9
  ---
@@ -376,4 +376,4 @@ Code generated by morph-spec (contracts, templates, implementation output) belon
376
376
 
377
377
  ---
378
378
 
379
- *morph-spec v4.8.19 by [Polymorphism Tech](https://polymorphism.tech)*
379
+ *morph-spec v4.10.0 by [Polymorphism Tech](https://polymorphism.tech)*
package/bin/morph-spec.js CHANGED
@@ -12,24 +12,27 @@ import { setupInfraCommand } from '../src/commands/project/setup-infra-cmd.js';
12
12
  import { installPluginCommand } from '../src/commands/project/install-plugin-cmd.js';
13
13
  import { updateCommand } from '../src/commands/project/update.js';
14
14
  import { doctorCommand } from '../src/commands/project/doctor.js';
15
- import { tutorialCommand } from '../src/commands/project/tutorial.js';
16
15
 
17
16
  import { statusCommand } from '../src/commands/project/status.js';
18
- import { monitorCommand } from '../src/commands/project/monitor.js';
19
- import { checkpointSaveCommand, checkpointRestoreCommand, checkpointListCommand } from '../src/commands/project/checkpoint.js';
17
+ import { worktreeSetupCommand } from '../src/commands/project/worktree.js';
18
+ import { checkpointSaveCommand } from '../src/commands/project/checkpoint.js';
20
19
 
21
20
  // State commands
22
21
  import { stateCommand } from '../src/commands/state/state.js';
23
- import { validatePhaseCommand } from '../src/commands/state/validate-phase.js';
24
22
  import { advancePhaseCommand } from '../src/commands/state/advance-phase.js';
25
23
  import { approveCommand, approvalStatusCommand, unapproveCommand } from '../src/commands/state/approve.js';
26
24
  import { phaseRunCommand } from '../src/commands/state/phase-runner.js';
25
+ import { phaseResetCommand } from '../src/commands/phase/phase-reset.js';
27
26
 
28
27
  // Agent commands
29
28
  import { dispatchAgentsCommand } from '../src/commands/agents/dispatch-agents.js';
30
29
 
31
30
  // Task commands
32
- import { taskDoneCommand, taskStartCommand, taskNextCommand, taskBulkDoneCommand } from '../src/commands/tasks/task.js';
31
+ import { taskDoneCommand, taskStartCommand, taskNextCommand } from '../src/commands/tasks/task.js';
32
+
33
+ // Scope escalation commands
34
+ import { scopeEscalateCommand } from '../src/commands/scope/escalate.js';
35
+ import { taskExpandCommand } from '../src/commands/task/expand.js';
33
36
 
34
37
  // Validation commands
35
38
  import { validateCommand } from './validate.js';
@@ -37,8 +40,6 @@ import { validateFeatureCommand } from '../src/commands/validation/validate-feat
37
40
 
38
41
  // Template commands
39
42
  import { templateRenderCommand } from '../src/commands/templates/template-render.js';
40
- import { generateContractsCommand } from '../src/commands/templates/generate-contracts.js';
41
-
42
43
  // MCP commands
43
44
  import { mcpSetupCommand } from '../src/commands/mcp/mcp-setup.js';
44
45
 
@@ -100,11 +101,6 @@ program
100
101
  .option('--reset', 'Remove morph-managed entries from .claude/settings.local.json')
101
102
  .action(doctorCommand);
102
103
 
103
- program
104
- .command('tutorial')
105
- .description('Learn the MORPH-SPEC workflow — phase pipeline and getting started')
106
- .action(tutorialCommand);
107
-
108
104
  program
109
105
  .command('status <feature>')
110
106
  .description('Show comprehensive feature status dashboard')
@@ -112,18 +108,22 @@ program
112
108
  .option('-v, --verbose', 'Show detailed output')
113
109
  .action(statusCommand);
114
110
 
115
- program
116
- .command('monitor [feature]')
117
- .description('Live TUI dashboard — hooks, agents, skills, rules in real time')
118
- .option('--compact', 'Minimal single-view output')
119
- .option('--mode <mode>', 'Start view: overview|hooks|agents|skills|rules', 'overview')
120
- .action(monitorCommand);
111
+ // Worktree commands
112
+ const worktreeCommand = program
113
+ .command('worktree')
114
+ .description('Manage git worktrees for feature isolation');
115
+
116
+ worktreeCommand
117
+ .command('setup <feature>')
118
+ .description('Create a worktree at .worktrees/{feature}/ on branch morph/{feature}')
119
+ .option('--fresh', 'Create a new dated branch even if one already exists')
120
+ .action((feature, options) => worktreeSetupCommand(feature, options));
121
121
 
122
122
  // State management commands
123
123
  program
124
124
  .command('state <action> [args...]')
125
125
  .description('Manage state.json (init | get | set | list | add-agent | remove-agent | mark-output)')
126
- .option('--force', 'Force overwrite (init command)')
126
+ .option('--force', 'Force overwrite (init) or bypass phase protection gates (set phase)')
127
127
  .option('--project <name>', 'Project name (init command)')
128
128
  .option('--type <type>', 'Project type (init command)')
129
129
  .option('--json', 'Output as JSON (get command)')
@@ -141,16 +141,6 @@ taskCommand
141
141
  .option('--dry-run', 'Show validation results without marking tasks as complete')
142
142
  .action((feature, taskIds, options) => taskDoneCommand(feature, taskIds, options));
143
143
 
144
- taskCommand
145
- .command('bulk-done <feature> [range]')
146
- .description('Bulk-complete tasks (--all | --from T001 --to T053 | T001..T082)')
147
- .option('--all', 'Complete all pending tasks')
148
- .option('--from <id>', 'Start task ID for range')
149
- .option('--to <id>', 'End task ID for range')
150
- .option('--skip-validation', 'Skip code validation (not recommended)')
151
- .option('--dry-run', 'Show validation results without marking tasks as complete')
152
- .action((feature, range, options) => taskBulkDoneCommand(feature, range, options));
153
-
154
144
  taskCommand
155
145
  .command('start <feature> <task-id>')
156
146
  .description('Start a task (mark as in_progress)')
@@ -161,6 +151,12 @@ taskCommand
161
151
  .description('Show next suggested task')
162
152
  .action((feature, options) => taskNextCommand(feature, options));
163
153
 
154
+ taskCommand
155
+ .command('expand <feature> <task-id>')
156
+ .description('Expand a task into sub-tasks (low-impact scope escalation)')
157
+ .option('--into <subtasks...>', 'Sub-task definitions ("T017a: Title")')
158
+ .action((feature, taskId, options) => taskExpandCommand(feature, taskId, options));
159
+
164
160
  // Generation commands
165
161
  const generateCommand = program
166
162
  .command('generate')
@@ -175,14 +171,6 @@ generateCommand
175
171
  await generateRecap('.', feature, options);
176
172
  });
177
173
 
178
- generateCommand
179
- .command('contracts <feature>')
180
- .description('Generate contracts.cs from schema-analysis.md using real field names and types')
181
- .option('--dry-run', 'Preview output without writing file')
182
- .option('--output <path>', 'Override output path for contracts.cs')
183
- .option('-v, --verbose', 'Show stack trace on error')
184
- .action((feature, options) => generateContractsCommand(feature, options));
185
-
186
174
  // Validation commands (Sprint 4: Continuous Validation)
187
175
  program
188
176
  .command('validate [validator]')
@@ -219,12 +207,10 @@ phaseCommand
219
207
  .option('--skip-approval', 'Skip approval gate checks')
220
208
  .action((feature, options) => phaseRunCommand(feature, options));
221
209
 
222
- // Phase validation command (also available as standalone)
223
- program
224
- .command('validate-phase <feature> <phase>')
225
- .description('Validate prerequisites before advancing to a phase')
226
- .option('-v, --verbose', 'Show detailed output')
227
- .action(validatePhaseCommand);
210
+ phaseCommand
211
+ .command('reset <feature> <phase>')
212
+ .description('Reset feature phase (recovery tool — bypasses approval gates)')
213
+ .action((feature, phase) => phaseResetCommand(feature, phase));
228
214
 
229
215
  // Feature validation command (content-aware)
230
216
  program
@@ -243,17 +229,6 @@ program
243
229
  .option('--label <label>', 'Custom label for checkpoint')
244
230
  .action(checkpointSaveCommand);
245
231
 
246
- program
247
- .command('checkpoint-restore <feature> [checkpoint]')
248
- .description('Restore feature from a checkpoint (latest if no name given)')
249
- .action(checkpointRestoreCommand);
250
-
251
- program
252
- .command('checkpoint-list <feature>')
253
- .description('List all checkpoints for a feature')
254
- .option('--json', 'Output as JSON')
255
- .action(checkpointListCommand);
256
-
257
232
  // Approval workflow commands
258
233
  program
259
234
  .command('approve <feature> <gate>')
@@ -300,7 +275,7 @@ trustCommand
300
275
 
301
276
  trustCommand
302
277
  .command('set <feature> <level> [reason]')
303
- .description('Manually set trust level (low|medium|high|maximum)')
278
+ .description('Manually set trust level (manual|high|auto)')
304
279
  .action(trustSetCommand);
305
280
 
306
281
  // ─────────────────────────────────────────────────────────────────────────────
@@ -326,4 +301,18 @@ program
326
301
  .option('-v, --verbose', 'Show stack trace on error')
327
302
  .action((feature, phase, options) => dispatchAgentsCommand(feature, phase, options));
328
303
 
304
+ // Scope escalation commands
305
+ const scopeCommand = program
306
+ .command('scope')
307
+ .description('Scope management (escalate)');
308
+
309
+ scopeCommand
310
+ .command('escalate <feature>')
311
+ .description('Escalate scope when implementation reveals unexpected complexity')
312
+ .option('--task <id>', 'Trigger task ID (required)')
313
+ .option('--reason <text>', 'Reason for escalation (required)')
314
+ .option('--target <phase>', 'Override target phase (tasks | design)')
315
+ .option('--dry-run', 'Show recommendation without executing')
316
+ .action((feature, options) => scopeEscalateCommand(feature, options));
317
+
329
318
  program.parse();
@@ -15,6 +15,9 @@ import chalk from 'chalk';
15
15
 
16
16
  import { loadState, saveState } from '../src/core/state/state-manager.js';
17
17
  import { parseTasksMd, ensureTaskList, syncCounters } from '../src/lib/tasks/task-parser.js';
18
+ import { isTestTask } from '../src/lib/tasks/task-classifier.js';
19
+ import { runTestSuite } from '../src/lib/tasks/test-runner.js';
20
+ import { STALE_MS, isStaleTask } from '../framework/hooks/shared/stale-task-reset.js';
18
21
 
19
22
  const __filename = fileURLToPath(import.meta.url);
20
23
  const __dirname = dirname(__filename);
@@ -96,6 +99,8 @@ class TaskManager {
96
99
  * @param {string[]} taskIds - Task IDs to complete
97
100
  * @param {Object} options - Options
98
101
  * @param {boolean} options.skipValidation - Skip code validation
102
+ * @param {boolean} options.dryRun - Dry run (no state changes)
103
+ * @param {Function} [options._runTestSuite] - Inject a mock runTestSuite for testing
99
104
  */
100
105
  async completeTasks(featureName, taskIds, options = {}) {
101
106
  const state = loadState();
@@ -108,7 +113,7 @@ class TaskManager {
108
113
  const taskList = await ensureTaskList(feature, featureName);
109
114
 
110
115
  if (taskList.length === 0) {
111
- const tasksPath = join(process.cwd(), `.morph/features/${featureName}/3-tasks/tasks.md`);
116
+ const tasksPath = join(process.cwd(), `.morph/features/${featureName}/4-tasks/tasks.md`);
112
117
  const tasksExist = await access(tasksPath).then(() => true).catch(() => false);
113
118
  if (!tasksExist) {
114
119
  throw new Error(`No tasks found for '${featureName}' — tasks.md not generated yet.\n Complete the tasks phase first: run /phase-tasks`);
@@ -142,6 +147,12 @@ class TaskManager {
142
147
  tasksToComplete.push(task);
143
148
  }
144
149
 
150
+ // ─── Test Task Guard ────────────────────────────────────────────────────────
151
+ // When any of the tasks being completed is a test/validation task (e.g.
152
+ // "Testes e Validação"), run the project's test suite BEFORE code validation.
153
+ // The guard is skipped when --skip-validation or --dry-run is active.
154
+ runTestGuard(tasksToComplete, process.cwd(), options);
155
+
145
156
  // Run validation BEFORE marking tasks as complete
146
157
  if (tasksToComplete.length > 0 && !options.skipValidation) {
147
158
  const validationResult = await this.runValidation(featureName);
@@ -183,26 +194,32 @@ class TaskManager {
183
194
  process.exit(1);
184
195
  }
185
196
 
186
- // Validation passed — mark any pending history as passed
187
- const successState = loadState();
188
- const successFeature = successState.features[featureName];
189
- if (successFeature) {
197
+ // Validation passed — mark any pending history as passed.
198
+ // Update in-place on `feature` (same object as state.features[featureName]) so
199
+ // the single saveState(state) at the end of the function persists everything
200
+ // atomically. A separate saveState here would trigger a second renameSync on
201
+ // Windows, risking EPERM and leaving state.json without the task completions
202
+ // (the count bug), and would also be clobbered by the final save anyway.
203
+ if (feature.validationHistory) {
190
204
  for (const task of tasksToComplete) {
191
- if (successFeature.validationHistory?.[task.id]) {
192
- successFeature.validationHistory[task.id].status = 'passed';
193
- successFeature.validationHistory[task.id].updatedAt = new Date().toISOString();
205
+ if (feature.validationHistory[task.id]) {
206
+ feature.validationHistory[task.id].status = 'passed';
207
+ feature.validationHistory[task.id].updatedAt = new Date().toISOString();
194
208
  }
195
209
  }
196
- saveState(successState);
197
210
  }
198
211
  } else if (options.dryRun) {
199
212
  console.log(chalk.cyan('\n ℹ️ Dry-run — tasks NOT marked as complete (validation skipped)'));
200
213
  return [];
214
+ } else if (shouldLogSkipValidationWarning(tasksToComplete, options.skipValidation, options.dryRun)) {
215
+ console.log(chalk.yellow('\n⚠ --skip-validation: Tier-4 code validators bypassed — tasks will be marked complete without validation'));
216
+ console.log(chalk.gray(' Re-run without --skip-validation before committing.\n'));
201
217
  }
202
218
 
203
219
  // Breaking change detection (non-blocking warning)
204
220
  if (tasksToComplete.length > 0) {
205
- const breakingChanges = await detectBreakingChanges();
221
+ const _detectBreaking = options._detectBreakingChanges ?? detectBreakingChanges;
222
+ const breakingChanges = await _detectBreaking();
206
223
  if (breakingChanges && breakingChanges.length > 0) {
207
224
  console.log(chalk.yellow('\n⚠️ BREAKING CHANGE DETECTION:'));
208
225
  console.log(chalk.yellow(' Removed exports with active consumers found — review before completing:\n'));
@@ -274,6 +291,18 @@ class TaskManager {
274
291
  // Display progress
275
292
  this.displayProgress(feature);
276
293
 
294
+ // Emit VALIDATION DISPATCH for PostToolUse hook (strategy 1 parsing).
295
+ // Skipped when --skip-validation is active (no validators needed).
296
+ if (!options.skipValidation && results.length > 0) {
297
+ try {
298
+ const { emitValidatorDispatch } = await import('../src/lib/validators/shared/emit-validator-dispatch.js');
299
+ const phase = feature.phase || 'implement';
300
+ await emitValidatorDispatch(featureName, phase, process.cwd());
301
+ } catch {
302
+ // Non-blocking
303
+ }
304
+ }
305
+
277
306
  // Suggest next task
278
307
  const nextTask = this.getNextTask(taskList);
279
308
  if (nextTask) {
@@ -343,7 +372,7 @@ class TaskManager {
343
372
  // This is fail-open by design: a broken validator shouldn't block commits.
344
373
  // Common cause: missing optional deps.
345
374
  console.log(chalk.yellow(`\n⚠️ Validation skipped (${error.message})`));
346
- console.log(chalk.gray(' Run manually: npx morph-spec validate --verbose'));
375
+ console.log(chalk.gray(` Run manually: npx morph-spec validate-feature ${featureName}`));
347
376
  return { passed: true, validators: {}, passRate: 1.0 };
348
377
  }
349
378
  }
@@ -391,10 +420,12 @@ class TaskManager {
391
420
  * Calculate progress
392
421
  */
393
422
  calculateProgress(tasks) {
394
- const total = tasks.length;
395
- const completed = tasks.filter(t => t.status === 'completed').length;
396
- const inProgress = tasks.filter(t => t.status === 'in_progress').length;
397
- const pending = tasks.filter(t => t.status === 'pending').length;
423
+ // Exclude expanded tasks — they are replaced by their sub-tasks
424
+ const activeTasks = tasks.filter(t => t.status !== 'expanded');
425
+ const total = activeTasks.length;
426
+ const completed = activeTasks.filter(t => t.status === 'completed').length;
427
+ const inProgress = activeTasks.filter(t => t.status === 'in_progress').length;
428
+ const pending = activeTasks.filter(t => t.status === 'pending').length;
398
429
  const percentage = total > 0 ? Math.round((completed / total) * 100) : 0;
399
430
 
400
431
  return { total, completed, inProgress, pending, percentage };
@@ -408,7 +439,8 @@ class TaskManager {
408
439
  id: task.checkpoint,
409
440
  timestamp: new Date().toISOString(),
410
441
  tasksCompleted: [task.id],
411
- note: `Checkpoint: ${task.title}`
442
+ note: `Checkpoint: ${task.title}`,
443
+ passed: true // reaching a named checkpoint means the task completed successfully
412
444
  };
413
445
 
414
446
  feature.checkpoints = feature.checkpoints || [];
@@ -469,6 +501,7 @@ class TaskManager {
469
501
  timestamp: new Date().toISOString(),
470
502
  tasksCompleted: tasks.map(t => t.id),
471
503
  note: `Auto-checkpoint: ${tasks.length} tasks completed${validationNote}`,
504
+ passed: checkpointResult?.passed ?? true, // null checkpointResult means no validator ran → assume passed
472
505
  validationResults: checkpointResult?.results || null
473
506
  };
474
507
 
@@ -542,12 +575,22 @@ class TaskManager {
542
575
  }
543
576
 
544
577
  /**
545
- * Get next pending task (based on dependencies)
578
+ * Get next available task (based on dependencies).
579
+ * Stale in_progress tasks (older than 1 hour, or missing startedAt) are
580
+ * treated as effectively pending and included as candidates.
546
581
  */
547
582
  getNextTask(tasks) {
548
- const pending = tasks.filter(t => t.status === 'pending');
583
+ // Include stale in_progress tasks as effectively pending candidates
584
+ const isEffectivelyAvailable = (t) => {
585
+ if (t.status === 'expanded') return false;
586
+ if (t.status === 'pending') return true;
587
+ if (t.status === 'in_progress') return isStaleTask(t);
588
+ return false;
589
+ };
590
+
591
+ const candidates = tasks.filter(isEffectivelyAvailable);
549
592
 
550
- for (const task of pending) {
593
+ for (const task of candidates) {
551
594
  const missingDeps = this.checkDependencies(task, tasks);
552
595
  if (missingDeps.length === 0) {
553
596
  return task;
@@ -570,6 +613,13 @@ class TaskManager {
570
613
  const filledLength = Math.round((percentage / 100) * barLength);
571
614
  const bar = '█'.repeat(filledLength) + '░'.repeat(barLength - filledLength);
572
615
  console.log(chalk.cyan(` [${bar}] ${percentage}%`));
616
+
617
+ // Warn about tasks flagged for review (from scope escalation)
618
+ const taskList = feature.taskList || [];
619
+ const needsReviewTasks = taskList.filter(t => t.needsReview && (t.status === 'done' || t.status === 'completed'));
620
+ if (needsReviewTasks.length > 0) {
621
+ console.log(chalk.yellow(`\n ⚠ ${needsReviewTasks.length} completed task(s) flagged for review (scope escalation)`));
622
+ }
573
623
  }
574
624
 
575
625
  /**
@@ -586,7 +636,7 @@ class TaskManager {
586
636
  const taskList = await ensureTaskList(feature, featureName);
587
637
 
588
638
  if (taskList.length === 0) {
589
- const tasksPath = join(process.cwd(), `.morph/features/${featureName}/3-tasks/tasks.md`);
639
+ const tasksPath = join(process.cwd(), `.morph/features/${featureName}/4-tasks/tasks.md`);
590
640
  const tasksExist = await access(tasksPath).then(() => true).catch(() => false);
591
641
  if (!tasksExist) {
592
642
  throw new Error(`No tasks found for '${featureName}' — tasks.md not generated yet.\n Complete the tasks phase first: run /phase-tasks`);
@@ -610,6 +660,16 @@ class TaskManager {
610
660
  throw new Error(`Cannot start ${taskId}: missing dependencies: ${missingDeps.join(', ')}`);
611
661
  }
612
662
 
663
+ // Reset any sibling tasks stuck in in_progress from a previous session.
664
+ // Only tasks OTHER than the one being started, and only if stale (>1h).
665
+ for (const t of taskList) {
666
+ if (t.id !== taskId && isStaleTask(t)) {
667
+ t.status = 'pending';
668
+ delete t.startedAt;
669
+ console.log(chalk.yellow(`⚠️ Task ${t.id} auto-reset to pending (stale from previous session)`));
670
+ }
671
+ }
672
+
613
673
  task.status = 'in_progress';
614
674
  task.startedAt = new Date().toISOString();
615
675
  syncCounters(feature);
@@ -800,4 +860,57 @@ if (process.argv[1] === fileURLToPath(import.meta.url)) {
800
860
  main();
801
861
  }
802
862
 
863
+ /**
864
+ * Pure predicate: should a --skip-validation bypass warning be logged?
865
+ *
866
+ * @param {Array} tasksToComplete - Tasks that will be marked complete
867
+ * @param {boolean} skipValidation - Whether --skip-validation flag is active
868
+ * @param {boolean} dryRun - Whether --dry-run is active (has its own message)
869
+ * @returns {boolean}
870
+ */
871
+ export function shouldLogSkipValidationWarning(tasksToComplete, skipValidation, dryRun) {
872
+ return tasksToComplete.length > 0 && !!skipValidation && !dryRun;
873
+ }
874
+
875
+ /**
876
+ * Test Task Guard — run project test suite before completing test-keyword tasks.
877
+ *
878
+ * Extracted as a pure, injectable function so it can be unit-tested without
879
+ * spawning a full TaskManager or writing state to disk.
880
+ *
881
+ * @param {Array} tasksToComplete - Tasks about to be marked complete
882
+ * @param {string} projectPath - Project root (process.cwd() in production)
883
+ * @param {object} [opts]
884
+ * @param {boolean} [opts.skipValidation] - Skip the guard entirely
885
+ * @param {boolean} [opts.dryRun] - Skip the guard entirely
886
+ * @param {Function} [opts._runTestSuite] - Override runTestSuite for testing
887
+ * @param {Function} [opts._exit] - Override process.exit for testing
888
+ * @returns {void} Calls _exit/process.exit(1) on failure; returns normally otherwise
889
+ */
890
+ export function runTestGuard(tasksToComplete, projectPath, opts = {}) {
891
+ if (!tasksToComplete.length || opts.skipValidation || opts.dryRun) return;
892
+
893
+ const _runTS = opts._runTestSuite ?? runTestSuite;
894
+ const hasTestTask = tasksToComplete.some(t => isTestTask(t.title));
895
+ if (!hasTestTask) return;
896
+
897
+ const suiteResult = _runTS(projectPath);
898
+
899
+ if (suiteResult.passed === false) {
900
+ console.error(chalk.red('\n❌ Test suite FAILED — tasks NOT marked as complete'));
901
+ if (suiteResult.output) {
902
+ console.error(chalk.gray(suiteResult.output.slice(-2000)));
903
+ }
904
+ console.log(chalk.gray(' Fix failing tests, then run task done again'));
905
+ console.log(chalk.gray(' Or use --skip-validation to bypass (not recommended)\n'));
906
+ (opts._exit ?? process.exit)(1);
907
+ } else if (suiteResult.skipped) {
908
+ console.log(chalk.yellow('\n⚠️ Test suite skipped — no testCommand configured'));
909
+ console.log(chalk.gray(` Reason: ${suiteResult.reason}`));
910
+ console.log(chalk.gray(' Set project.testCommand in .morph/config/config.json to enable auto-run\n'));
911
+ } else {
912
+ console.log(chalk.green('\n✅ Test suite passed'));
913
+ }
914
+ }
915
+
803
916
  export default TaskManager;
package/bin/validate.js CHANGED
@@ -3,46 +3,75 @@
3
3
  /**
4
4
  * Validation CLI
5
5
  *
6
- * Runs all validators (package, architecture, contrast) on the project.
7
- * Part of Sprint 4: Continuous Validation
6
+ * Runs validators for the current project stack (detected from .morph/config/config.json).
7
+ *
8
+ * .NET/Blazor stacks: packages, architecture, contrast, spec-tasks
9
+ * Next.js stack: next-components, spec-tasks
10
+ * Other stacks: spec-tasks
8
11
  *
9
12
  * Usage:
10
13
  * morph-spec validate [options]
11
- * morph-spec validate packages
12
- * morph-spec validate architecture
13
- * morph-spec validate contrast
14
14
  * morph-spec validate all
15
+ * morph-spec validate next-components (Next.js projects)
16
+ * morph-spec validate packages (.NET projects)
15
17
  *
16
18
  * MORPH-SPEC 3.0
17
19
  */
18
20
 
19
21
  import chalk from 'chalk';
20
- import { validatePackages } from '../src/lib/validators/packages/package-validator.js';
21
- import { validateArchitecture } from '../src/lib/validators/architecture/architecture-validator.js';
22
- import { validateContrast } from '../src/lib/validators/ui/ui-contrast-validator.js';
23
-
24
- const VALIDATORS = {
25
- packages: {
26
- name: 'Package Compatibility',
27
- description: 'Validates NuGet package versions against .NET compatibility matrix',
28
- run: validatePackages
29
- },
30
- architecture: {
31
- name: 'Architecture Patterns',
32
- description: 'Detects DI anti-patterns and lifecycle issues in Blazor',
33
- run: validateArchitecture
34
- },
35
- contrast: {
36
- name: 'UI Contrast (WCAG)',
37
- description: 'Validates color contrast ratios for accessibility',
38
- run: validateContrast
39
- },
40
- 'spec-tasks': {
22
+ import { getStackProfile } from '../src/lib/stack/stack-profile.js';
23
+
24
+ /**
25
+ * Build the VALIDATORS registry for the current stack.
26
+ * .NET stacks → packages, architecture, contrast, spec-tasks
27
+ * nextjs stack → next-components, spec-tasks
28
+ * other stacks → spec-tasks only
29
+ */
30
+ async function buildValidators(stack) {
31
+ const validators = {};
32
+
33
+ const isDotnet = !stack || ['dotnet', 'blazor', 'aspnet', 'dotnet-blazor'].includes(stack.toLowerCase());
34
+ const isNextjs = stack && ['nextjs', 'next', 'next.js'].includes(stack.toLowerCase());
35
+
36
+ if (isDotnet) {
37
+ const { validatePackages } = await import('../src/lib/validators/packages/package-validator.js');
38
+ const { validateArchitecture } = await import('../src/lib/validators/architecture/architecture-validator.js');
39
+ const { validateContrast } = await import('../src/lib/validators/ui/ui-contrast-validator.js');
40
+
41
+ validators.packages = {
42
+ name: 'Package Compatibility',
43
+ description: 'Validates NuGet package versions against .NET compatibility matrix',
44
+ run: validatePackages
45
+ };
46
+ validators.architecture = {
47
+ name: 'Architecture Patterns',
48
+ description: 'Detects DI anti-patterns and lifecycle issues in Blazor',
49
+ run: validateArchitecture
50
+ };
51
+ validators.contrast = {
52
+ name: 'UI Contrast (WCAG)',
53
+ description: 'Validates color contrast ratios for accessibility',
54
+ run: validateContrast
55
+ };
56
+ }
57
+
58
+ if (isNextjs) {
59
+ const { validateNextComponentFiles } = await import('../src/lib/validators/nextjs/next-component-validator.js');
60
+ validators['next-components'] = {
61
+ name: 'Next.js Components',
62
+ description: "Validates 'use client' directives, hook usage, and file naming conventions",
63
+ run: (projectPath, opts) => validateNextComponentFiles(projectPath, opts)
64
+ };
65
+ }
66
+
67
+ validators['spec-tasks'] = {
41
68
  name: 'Spec vs Tasks Coverage',
42
69
  description: 'Validates that tasks.json covers all requirements from spec.md',
43
70
  run: validateSpecVsTasks
44
- }
45
- };
71
+ };
72
+
73
+ return validators;
74
+ }
46
75
 
47
76
  /**
48
77
  * Validate spec.md requirements are covered by tasks
@@ -147,12 +176,18 @@ export async function validateCommand(args = []) {
147
176
  const validatorName = args[0] || 'all';
148
177
  const options = parseOptions(args);
149
178
 
179
+ const { stack } = getStackProfile();
180
+ const VALIDATORS = await buildValidators(stack);
181
+
150
182
  console.log(chalk.bold.cyan('\n🔍 MORPH-SPEC Validator\n'));
183
+ if (stack) {
184
+ console.log(chalk.gray(`Stack: ${stack}\n`));
185
+ }
151
186
 
152
187
  if (validatorName === 'all') {
153
- await runAllValidators(options);
188
+ await runAllValidators(VALIDATORS, options);
154
189
  } else if (VALIDATORS[validatorName]) {
155
- await runValidator(validatorName, options);
190
+ await runValidator(VALIDATORS, validatorName, options);
156
191
  } else {
157
192
  console.error(chalk.red(`❌ Unknown validator: ${validatorName}`));
158
193
  console.log(chalk.gray('\nAvailable validators:'));
@@ -162,13 +197,12 @@ export async function validateCommand(args = []) {
162
197
  console.log(chalk.gray(' - all: Run all validators'));
163
198
  process.exit(1);
164
199
  }
165
-
166
200
  }
167
201
 
168
202
  /**
169
203
  * Run all validators
170
204
  */
171
- async function runAllValidators(options) {
205
+ async function runAllValidators(VALIDATORS, options) {
172
206
  const results = {};
173
207
  let totalErrors = 0;
174
208
  let totalWarnings = 0;
@@ -230,7 +264,7 @@ async function runAllValidators(options) {
230
264
  /**
231
265
  * Run single validator
232
266
  */
233
- async function runValidator(name, options) {
267
+ async function runValidator(VALIDATORS, name, options) {
234
268
  const validator = VALIDATORS[name];
235
269
 
236
270
  console.log(chalk.cyan(`▸ ${validator.name}`));
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "morph-spec",
3
- "version": "4.8.19",
3
+ "version": "4.10.0",
4
4
  "displayName": "MORPH-SPEC Framework",
5
5
  "description": "Spec-driven development with 38 agents and 8-phase workflow for .NET/Blazor/Next.js/Azure",
6
6
  "publisher": "polymorphism-tech",