@polymorphism-tech/morph-spec 4.8.11 → 4.8.14

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 (29) hide show
  1. package/README.md +379 -379
  2. package/bin/{task-manager.cjs → task-manager.js} +47 -158
  3. package/claude-plugin.json +14 -14
  4. package/docs/CHEATSHEET.md +203 -203
  5. package/docs/QUICKSTART.md +1 -1
  6. package/framework/agents.json +111 -24
  7. package/framework/hooks/README.md +202 -202
  8. package/framework/hooks/claude-code/pre-tool-use/enforce-phase-writes.js +6 -0
  9. package/framework/hooks/claude-code/session-start/inject-morph-context.js +7 -0
  10. package/framework/hooks/claude-code/statusline.py +6 -0
  11. package/framework/hooks/claude-code/stop/validate-completion.js +21 -2
  12. package/framework/hooks/dev/guard-version-numbers.js +1 -1
  13. package/framework/hooks/shared/phase-utils.js +3 -0
  14. package/framework/skills/level-0-meta/tool-usage-guide/SKILL.md +55 -0
  15. package/framework/skills/level-1-workflows/phase-clarify/SKILL.md +1 -1
  16. package/framework/skills/level-1-workflows/phase-codebase-analysis/SKILL.md +1 -1
  17. package/framework/skills/level-1-workflows/phase-design/SKILL.md +57 -1
  18. package/framework/skills/level-1-workflows/phase-implement/SKILL.md +23 -1
  19. package/framework/skills/level-1-workflows/phase-setup/SKILL.md +1 -1
  20. package/framework/skills/level-1-workflows/phase-tasks/SKILL.md +25 -2
  21. package/framework/skills/level-1-workflows/phase-uiux/SKILL.md +1 -1
  22. package/package.json +87 -87
  23. package/src/commands/project/update.js +12 -2
  24. package/src/commands/state/advance-phase.js +32 -13
  25. package/src/commands/tasks/task.js +2 -2
  26. package/src/core/paths/output-schema.js +1 -0
  27. package/src/lib/detectors/design-system-detector.js +5 -4
  28. package/src/lib/tasks/task-parser.js +94 -0
  29. package/src/lib/validators/content/content-validator.js +34 -106
@@ -4,7 +4,7 @@ description: MORPH-SPEC Phase 2 (Design). Analyzes codebase/schema, then produce
4
4
  argument-hint: "[feature-name]"
5
5
  user-invocable: false
6
6
  allowed-tools: Read, Write, Edit, Bash, Glob, Grep
7
- cliVersion: "4.8.11"
7
+ cliVersion: "4.8.14"
8
8
  ---
9
9
 
10
10
  # MORPH Design - FASE 2
@@ -58,6 +58,62 @@ Expanda a proposta em especificação técnica completa, contracts, decisões ar
58
58
 
59
59
  ---
60
60
 
61
+ ## ✅ PRÉ-VOO OBRIGATÓRIO (antes de gerar qualquer artefato)
62
+
63
+ ### 1. Verificar contexto injetado pelo SessionStart
64
+
65
+ O hook já injetou o estado atual. Confirme que você leu:
66
+ - Feature ativa, fase, status de approvals
67
+ - Task progress e pending gates
68
+ - Spec snippet (se disponível)
69
+
70
+ ### 2. Ler todos os prerequisitos em PARALELO
71
+
72
+ ```
73
+ # Uma única chamada, não sequencial:
74
+ Read: .morph/features/{feature}/0-proposal/proposal.md
75
+ + Read: .morph/config/config.json (→ architecture.style)
76
+ + Read: .morph/context/README.md (→ project context)
77
+ + Read: framework/agents.json (→ activeAgents do feature)
78
+ ```
79
+
80
+ ### 3. Avaliar escopo → EnterPlanMode se necessário
81
+
82
+ Se QUALQUER uma das condições abaixo for verdadeira:
83
+ - [ ] Refactor toca ≥5 arquivos existentes de domínio
84
+ - [ ] Estimativa de tasks ≥20
85
+ - [ ] Mudança de arquitetura (ex: migrar de DDD Level 1 → Level 2)
86
+
87
+ → **USE EnterPlanMode** antes de gerar contratos. Explore o codebase, apresente opções, aguarde aprovação.
88
+
89
+ ### 4. Dispatch paralelo de subagents para artefatos independentes
90
+
91
+ Os 4 artefatos de design são **independentes entre si**. Dispatch em paralelo:
92
+
93
+ ```
94
+ # Executar TODOS ao mesmo tempo (uma mensagem, múltiplos Task tool calls):
95
+ Task(subagent=general): gerar spec.md (endpoints, regras de negócio)
96
+ Task(subagent=general): gerar contracts-vsa.cs ou contracts-levelN.cs
97
+ Task(subagent=general): gerar decisions.md (ADRs)
98
+ Task(subagent=general): gerar tasks.md skeleton
99
+ ```
100
+
101
+ Ou obter config de dispatch:
102
+ ```bash
103
+ npx morph-spec dispatch-agents {feature} design --table
104
+ ```
105
+
106
+ ### 5. Criar tasks de sessão para visibilidade
107
+
108
+ ```
109
+ TaskCreate: "Gerar spec.md" → activeForm: "Gerando spec.md"
110
+ TaskCreate: "Gerar contracts.cs" → activeForm: "Gerando contracts.cs"
111
+ TaskCreate: "Gerar decisions.md" → activeForm: "Gerando decisions.md"
112
+ TaskCreate: "Avanço de fase" → activeForm: "Avançando fase"
113
+ ```
114
+
115
+ ---
116
+
61
117
  ## Workflow
62
118
 
63
119
  ### Passo 1: Carregar Contexto, Agentes e Dispatch Config
@@ -6,7 +6,7 @@ disable-model-invocation: true
6
6
  context: fork
7
7
  agent: general-purpose
8
8
  user-invocable: false
9
- cliVersion: "4.8.11"
9
+ cliVersion: "4.8.14"
10
10
  allowed-tools: Read, Write, Edit, Bash, Glob, Grep
11
11
  ---
12
12
 
@@ -65,6 +65,28 @@ Implemente as tasks definidas na FASE 4, com checkpoints a cada 3 tasks e recap
65
65
 
66
66
  ---
67
67
 
68
+ ## ✅ PRÉ-VOO OBRIGATÓRIO (antes de iniciar implementação)
69
+
70
+ ### 1. Ler contexto completo em PARALELO
71
+
72
+ ```
73
+ # Uma única chamada, não sequencial:
74
+ Read: .morph/features/{feature}/3-tasks/tasks.md
75
+ + Read: .morph/features/{feature}/1-design/spec.md
76
+ + Read: .morph/features/{feature}/1-design/contracts.cs
77
+ + Read: .morph/config/config.json (→ architecture.style)
78
+ ```
79
+
80
+ ### 2. Criar tasks de sessão por grupo de implementação
81
+
82
+ ```
83
+ TaskCreate: "Implementar [domínio] (T001-T00N)" → activeForm: "Implementando [domínio]"
84
+ TaskCreate: "Checkpoints e validação" → activeForm: "Validando checkpoint"
85
+ TaskCreate: "Gerar recap.md" → activeForm: "Gerando recap"
86
+ ```
87
+
88
+ ---
89
+
68
90
  ## Workflow
69
91
 
70
92
  ### Passo 0.5: Planejar Paralelização
@@ -4,7 +4,7 @@ description: MORPH-SPEC Phase 1 (Setup). Reads project context, detects tech sta
4
4
  argument-hint: "[feature-name]"
5
5
  user-invocable: false
6
6
  allowed-tools: Read, Write, Edit, Bash, Glob, Grep
7
- cliVersion: "4.8.11"
7
+ cliVersion: "4.8.14"
8
8
  ---
9
9
 
10
10
  # MORPH Setup - FASE 1
@@ -5,7 +5,7 @@ argument-hint: "[feature-name]"
5
5
  disable-model-invocation: true
6
6
  user-invocable: false
7
7
  allowed-tools: Read, Write, Edit, Bash, Glob, Grep
8
- cliVersion: "4.8.11"
8
+ cliVersion: "4.8.14"
9
9
  ---
10
10
 
11
11
  # MORPH Tasks - FASE 4
@@ -50,6 +50,29 @@ Quebre a especificação em tasks executáveis, defina ordem de execução e est
50
50
 
51
51
  ---
52
52
 
53
+ ## ✅ PRÉ-VOO OBRIGATÓRIO (antes de iniciar breakdown de tasks)
54
+
55
+ ### 1. Ler todos os prerequisitos em PARALELO
56
+
57
+ ```
58
+ # Uma única chamada, não sequencial:
59
+ Read: .morph/features/{feature}/1-design/spec.md
60
+ + Read: .morph/features/{feature}/1-design/contracts.cs
61
+ + Read: .morph/features/{feature}/1-design/decisions.md
62
+ + Read: .morph/features/{feature}/1-design/schema-analysis.md (se existir)
63
+ + Read: .morph/config/config.json (→ architecture.style)
64
+ ```
65
+
66
+ ### 2. Criar tasks de sessão para visibilidade
67
+
68
+ ```
69
+ TaskCreate: "Analisar spec e definir tasks" → activeForm: "Analisando spec"
70
+ TaskCreate: "Gerar tasks.md" → activeForm: "Gerando tasks.md"
71
+ TaskCreate: "Avanço de fase" → activeForm: "Avançando fase"
72
+ ```
73
+
74
+ ---
75
+
53
76
  ## Workflow
54
77
 
55
78
  ### CHECKPOINT DE ENTRADA: Verificar Pré-requisitos
@@ -329,7 +352,7 @@ Para cada task, estime tempo em minutos:
329
352
  | Média (Business logic, validações) | 60-120 min |
330
353
  | Complexa (Integrações, AI) | 120-240 min |
331
354
 
332
- ### Passo 6: Gerar `tasks.json`
355
+ ### Passo 6: Gerar `tasks.md`
333
356
 
334
357
  Crie `.morph/features/$ARGUMENTS/3-tasks/tasks.md` com a estrutura completa de tasks, checkpoints e estimativas.
335
358
 
@@ -4,7 +4,7 @@ description: MORPH-SPEC Phase 1.5 (UI/UX). Creates design-system.md, mockups.md,
4
4
  argument-hint: "[feature-name]"
5
5
  user-invocable: false
6
6
  allowed-tools: Read, Write, Edit, Bash, Glob, Grep
7
- cliVersion: "4.8.11"
7
+ cliVersion: "4.8.14"
8
8
  ---
9
9
 
10
10
  # MORPH UI/UX Design - FASE 1.5
package/package.json CHANGED
@@ -1,87 +1,87 @@
1
- {
2
- "name": "@polymorphism-tech/morph-spec",
3
- "version": "4.8.11",
4
- "description": "MORPH-SPEC: AI-First development framework with validation pipeline and multi-stack support",
5
- "keywords": [
6
- "claude-code",
7
- "claude",
8
- "ai-coding",
9
- "ai-first",
10
- "spec-driven",
11
- "dotnet",
12
- "dotnet10",
13
- "blazor",
14
- "agent-framework",
15
- "fluent-ui",
16
- "framework",
17
- "developer-tools",
18
- "morph",
19
- "polymorphism",
20
- "micro-saas"
21
- ],
22
- "main": "src/index.js",
23
- "bin": {
24
- "morph-spec": "bin/morph-spec.js"
25
- },
26
- "files": [
27
- "bin/",
28
- "src/",
29
- "framework/",
30
- "claude-plugin.json",
31
- "docs/QUICKSTART.md",
32
- "docs/CHEATSHEET.md",
33
- "README.md",
34
- "LICENSE",
35
- "CLAUDE.md"
36
- ],
37
- "type": "module",
38
- "engines": {
39
- "node": ">=18.0.0"
40
- },
41
- "scripts": {
42
- "start": "node bin/morph-spec.js",
43
- "generate": "node scripts/generate-refs.js",
44
- "test": "node --test --test-concurrency=1",
45
- "test:coverage": "c8 --reporter=text --reporter=html --reporter=lcov node --test --test-concurrency=1",
46
- "test:coverage:summary": "c8 --reporter=text-summary node --test --test-concurrency=1",
47
- "docs": "jsdoc -c jsdoc.json",
48
- "docs:watch": "jsdoc -c jsdoc.json --watch",
49
- "docs:serve": "npx http-server docs/api -p 8080 -o",
50
- "version:bump": "node scripts/bump-version.js",
51
- "release": "node scripts/release.js",
52
- "postinstall": "node src/scripts/global-install.js"
53
- },
54
- "dependencies": {
55
- "ajv": "^8.12.0",
56
- "ajv-formats": "^3.0.1",
57
- "chalk": "^5.3.0",
58
- "commander": "^12.0.0",
59
- "diff": "^5.2.0",
60
- "fs-extra": "^11.2.0",
61
- "glob": "^10.3.0",
62
- "handlebars": "^4.7.8",
63
- "inquirer": "^9.2.0",
64
- "minimatch": "^9.0.5",
65
- "ora": "^8.0.0",
66
- "yaml": "^2.3.4"
67
- },
68
- "repository": {
69
- "type": "git",
70
- "url": "git+https://github.com/lucasPolymorphism/morph-spec-framework.git"
71
- },
72
- "author": "Polymorphism Tech <contact@polymorphism.com.br>",
73
- "license": "SEE LICENSE IN LICENSE",
74
- "homepage": "https://polymorphism.com.br/morph-spec",
75
- "bugs": {
76
- "email": "support@polymorphism.com.br"
77
- },
78
- "private": false,
79
- "publishConfig": {
80
- "access": "public"
81
- },
82
- "devDependencies": {
83
- "c8": "^10.1.3",
84
- "docdash": "^2.0.2",
85
- "jsdoc": "^4.0.5"
86
- }
87
- }
1
+ {
2
+ "name": "@polymorphism-tech/morph-spec",
3
+ "version": "4.8.14",
4
+ "description": "MORPH-SPEC: AI-First development framework with validation pipeline and multi-stack support",
5
+ "keywords": [
6
+ "claude-code",
7
+ "claude",
8
+ "ai-coding",
9
+ "ai-first",
10
+ "spec-driven",
11
+ "dotnet",
12
+ "dotnet10",
13
+ "blazor",
14
+ "agent-framework",
15
+ "fluent-ui",
16
+ "framework",
17
+ "developer-tools",
18
+ "morph",
19
+ "polymorphism",
20
+ "micro-saas"
21
+ ],
22
+ "main": "src/index.js",
23
+ "bin": {
24
+ "morph-spec": "bin/morph-spec.js"
25
+ },
26
+ "files": [
27
+ "bin/",
28
+ "src/",
29
+ "framework/",
30
+ "claude-plugin.json",
31
+ "docs/QUICKSTART.md",
32
+ "docs/CHEATSHEET.md",
33
+ "README.md",
34
+ "LICENSE",
35
+ "CLAUDE.md"
36
+ ],
37
+ "type": "module",
38
+ "engines": {
39
+ "node": ">=18.0.0"
40
+ },
41
+ "scripts": {
42
+ "start": "node bin/morph-spec.js",
43
+ "generate": "node scripts/generate-refs.js",
44
+ "test": "node --test --test-concurrency=1",
45
+ "test:coverage": "c8 --reporter=text --reporter=html --reporter=lcov node --test --test-concurrency=1",
46
+ "test:coverage:summary": "c8 --reporter=text-summary node --test --test-concurrency=1",
47
+ "docs": "jsdoc -c jsdoc.json",
48
+ "docs:watch": "jsdoc -c jsdoc.json --watch",
49
+ "docs:serve": "npx http-server docs/api -p 8080 -o",
50
+ "version:bump": "node scripts/bump-version.js",
51
+ "release": "node scripts/release.js",
52
+ "postinstall": "node src/scripts/global-install.js"
53
+ },
54
+ "dependencies": {
55
+ "ajv": "^8.12.0",
56
+ "ajv-formats": "^3.0.1",
57
+ "chalk": "^5.3.0",
58
+ "commander": "^12.0.0",
59
+ "diff": "^5.2.0",
60
+ "fs-extra": "^11.2.0",
61
+ "glob": "^10.3.0",
62
+ "handlebars": "^4.7.8",
63
+ "inquirer": "^9.2.0",
64
+ "minimatch": "^9.0.5",
65
+ "ora": "^8.0.0",
66
+ "yaml": "^2.3.4"
67
+ },
68
+ "repository": {
69
+ "type": "git",
70
+ "url": "git+https://github.com/lucasPolymorphism/morph-spec-framework.git"
71
+ },
72
+ "author": "Polymorphism Tech <contact@polymorphism.com.br>",
73
+ "license": "SEE LICENSE IN LICENSE",
74
+ "homepage": "https://polymorphism.com.br/morph-spec",
75
+ "bugs": {
76
+ "email": "support@polymorphism.com.br"
77
+ },
78
+ "private": false,
79
+ "publishConfig": {
80
+ "access": "public"
81
+ },
82
+ "devDependencies": {
83
+ "c8": "^10.1.3",
84
+ "docdash": "^2.0.2",
85
+ "jsdoc": "^4.0.5"
86
+ }
87
+ }
@@ -15,7 +15,8 @@ import {
15
15
  pathExists,
16
16
  ensureDir,
17
17
  readJson,
18
- writeJson
18
+ writeJson,
19
+ updateGitignore
19
20
  } from '../../utils/file-copier.js';
20
21
  import {
21
22
  checkCLIOutdated,
@@ -52,12 +53,16 @@ async function backupUserConfig(morphPath) {
52
53
  * @param {string} targetPath - Project root path
53
54
  */
54
55
  async function cleanFrameworkDirs(morphPath, targetPath) {
56
+ const claudeDir = join(targetPath, '.claude');
55
57
  const dirsToClean = [
56
58
  join(morphPath, 'framework', 'templates'),
57
59
  join(morphPath, 'framework', 'standards'),
58
60
  join(morphPath, 'framework', 'hooks'),
59
61
  join(morphPath, 'config'),
60
- join(targetPath, '.claude')
62
+ join(claudeDir, 'commands'),
63
+ join(claudeDir, 'rules'),
64
+ join(claudeDir, 'skills'),
65
+ join(claudeDir, 'agents'),
61
66
  ];
62
67
 
63
68
  for (const dir of dirsToClean) {
@@ -311,6 +316,10 @@ export async function updateCommand(options) {
311
316
  await restoreUserConfig(morphPath, targetPath, configBackup, cliCheck.current);
312
317
  logger.dim(' ✓ User config restored with updated framework version');
313
318
 
319
+ // Update .gitignore with current morph rules
320
+ updateSpinner.text = 'Updating .gitignore...';
321
+ await updateGitignore(targetPath);
322
+
314
323
  // Update .morphversion
315
324
  updateSpinner.text = 'Saving version info...';
316
325
  await saveProjectMorphVersion(targetPath, cliCheck.current);
@@ -342,6 +351,7 @@ export async function updateCommand(options) {
342
351
  logger.dim(' ✓ .claude/CLAUDE.md (runtime quick reference)');
343
352
  logger.dim(' ✓ ~/.claude/statusline.sh (global statusline synced)');
344
353
  logger.dim(' ✓ CLAUDE.md');
354
+ logger.dim(' ✓ .gitignore (morph rules updated)');
345
355
  logger.blank();
346
356
  logger.info('Your config.json was preserved.');
347
357
  logger.dim('Review the updated files for any new features.');
@@ -10,7 +10,7 @@
10
10
  */
11
11
 
12
12
  import chalk from 'chalk';
13
- import { loadState, saveState, getFeature, getApprovalGate } from '../../core/state/state-manager.js';
13
+ import { loadState, saveState, getFeature, getApprovalGate, derivePhase } from '../../core/state/state-manager.js';
14
14
  import { PHASES, validatePhase } from './validate-phase.js';
15
15
  import { detectDesignSystem, hasUIAgentsActive } from '../../lib/detectors/design-system-detector.js';
16
16
  import { getOutputPath } from '../../core/paths/output-schema.js';
@@ -29,7 +29,8 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
29
29
  const PHASE_ORDER = ['proposal', 'setup', 'uiux', 'design', 'clarify', 'tasks', 'implement', 'sync'];
30
30
 
31
31
  /**
32
- * Get the next phase after the current one
32
+ * Get the next phase after the current one.
33
+ * Returns { nextPhase, skippedPhases } so callers can persist skipped phases in state.
33
34
  */
34
35
  function getNextPhase(currentPhase, feature, skipOptional, featureName) {
35
36
  // Load workflow config if workflow is set
@@ -40,9 +41,16 @@ function getNextPhase(currentPhase, feature, skipOptional, featureName) {
40
41
 
41
42
  const currentIndex = PHASE_ORDER.indexOf(currentPhase);
42
43
  if (currentIndex === -1 || currentIndex >= PHASE_ORDER.length - 1) {
43
- return null;
44
+ return { nextPhase: null, skippedPhases: [] };
44
45
  }
45
46
 
47
+ // Agents that justify the uiux phase (wireframes, design system).
48
+ // Implementation-only agents (blazor-builder, nextjs-expert) are NOT included —
49
+ // they build components from existing designs but don't create the design artifacts.
50
+ const uiDesignAgents = ['uiux-designer', 'ui-ux-designer', 'ui-designer'];
51
+
52
+ const skippedPhases = [];
53
+
46
54
  for (let i = currentIndex + 1; i < PHASE_ORDER.length; i++) {
47
55
  const candidate = PHASE_ORDER[i];
48
56
  const phaseDef = PHASES[candidate];
@@ -52,6 +60,7 @@ function getNextPhase(currentPhase, feature, skipOptional, featureName) {
52
60
  // Check if workflow explicitly skips this phase
53
61
  if (workflowConfig.phases.skip && workflowConfig.phases.skip.includes(candidate)) {
54
62
  console.log(chalk.gray(` ⏩ Skipping ${candidate}: workflow ${feature.workflow} skips this phase`));
63
+ skippedPhases.push(candidate);
55
64
  continue;
56
65
  }
57
66
 
@@ -60,6 +69,7 @@ function getNextPhase(currentPhase, feature, skipOptional, featureName) {
60
69
  for (const [combinedName, phases] of Object.entries(workflowConfig.phases.combined)) {
61
70
  if (phases.includes(candidate) && candidate !== combinedName) {
62
71
  console.log(chalk.gray(` ⏩ Skipping ${candidate}: combined into ${combinedName} phase`));
72
+ skippedPhases.push(candidate);
63
73
  continue;
64
74
  }
65
75
  }
@@ -72,6 +82,7 @@ function getNextPhase(currentPhase, feature, skipOptional, featureName) {
72
82
  Object.keys(workflowConfig.phases.combined).includes(candidate);
73
83
  if (!isCombinedPhase) {
74
84
  console.log(chalk.gray(` ⏩ Skipping ${candidate}: not in workflow run list`));
85
+ skippedPhases.push(candidate);
75
86
  continue;
76
87
  }
77
88
  }
@@ -83,6 +94,7 @@ function getNextPhase(currentPhase, feature, skipOptional, featureName) {
83
94
  // Evaluate condition (simple conditions for now)
84
95
  if (condition === '!hasUIAgents' && !hasUIAgentsActive(process.cwd(), featureName)) {
85
96
  console.log(chalk.gray(` ⏩ Skipping ${candidate}: no UI agents active`));
97
+ skippedPhases.push(candidate);
86
98
  continue;
87
99
  }
88
100
  }
@@ -93,15 +105,16 @@ function getNextPhase(currentPhase, feature, skipOptional, featureName) {
93
105
  if (!workflowConfig) {
94
106
  // Skip optional phases if flag set or no relevant agents/outputs
95
107
  if (phaseDef?.optional && skipOptional) {
108
+ skippedPhases.push(candidate);
96
109
  continue;
97
110
  }
98
111
 
99
- // Skip uiux if no UI agents are active
112
+ // Skip uiux if no UI design agents are active
100
113
  if (candidate === 'uiux') {
101
- const uiAgents = ['blazor-builder', 'uiux-designer', 'nextjs-expert', 'ui-ux-designer'];
102
- const hasUiAgent = feature.activeAgents?.some(a => uiAgents.includes(a));
103
- if (!hasUiAgent && skipOptional !== false) {
104
- console.log(chalk.gray(` ⏩ Skipping ${candidate}: no UI agents active`));
114
+ const hasUiDesignAgent = feature.activeAgents?.some(a => uiDesignAgents.includes(a));
115
+ if (!hasUiDesignAgent && skipOptional !== false) {
116
+ console.log(chalk.gray(` ⏩ Skipping ${candidate}: no UI design agents active`));
117
+ skippedPhases.push(candidate);
105
118
  continue;
106
119
  }
107
120
  }
@@ -111,15 +124,16 @@ function getNextPhase(currentPhase, feature, skipOptional, featureName) {
111
124
  const isSimple = feature.workflow === 'fast-track';
112
125
  if (isSimple && skipOptional !== false) {
113
126
  console.log(chalk.gray(` ⏩ Skipping ${candidate}: fast-track workflow`));
127
+ skippedPhases.push(candidate);
114
128
  continue;
115
129
  }
116
130
  }
117
131
  }
118
132
 
119
- return candidate;
133
+ return { nextPhase: candidate, skippedPhases };
120
134
  }
121
135
 
122
- return null;
136
+ return { nextPhase: null, skippedPhases };
123
137
  }
124
138
 
125
139
  /**
@@ -138,7 +152,8 @@ export async function advancePhaseCommand(feature, options = {}) {
138
152
  process.exit(1);
139
153
  }
140
154
 
141
- const currentPhase = featureData.phase;
155
+ const featureFolderPath = join(process.cwd(), '.morph', 'features', feature);
156
+ const currentPhase = featureData.phase || derivePhase(featureFolderPath);
142
157
  const currentPhaseDef = PHASES[currentPhase];
143
158
 
144
159
  console.log(chalk.gray('Feature:'), feature);
@@ -146,7 +161,7 @@ export async function advancePhaseCommand(feature, options = {}) {
146
161
  console.log(chalk.gray('Workflow:'), featureData.workflow || 'auto');
147
162
 
148
163
  // Determine next phase
149
- const nextPhase = getNextPhase(currentPhase, featureData, options.skipOptional, feature);
164
+ const { nextPhase, skippedPhases } = getNextPhase(currentPhase, featureData, options.skipOptional, feature);
150
165
 
151
166
  if (!nextPhase) {
152
167
  console.log(chalk.green('\n✓ Feature is at the final phase!'));
@@ -277,7 +292,7 @@ export async function advancePhaseCommand(feature, options = {}) {
277
292
  }
278
293
 
279
294
  // === GATE 5: Tasks Content Validation ===
280
- // Validate tasks.json structure when advancing to implement
295
+ // Validate tasks.md structure when advancing to implement
281
296
  if (currentPhase === 'tasks' && nextPhase === 'implement') {
282
297
  if (featureData.outputs?.tasks?.created) {
283
298
  const tasksContentValidation = validateTasksContent(featureData.outputs.tasks.path);
@@ -326,6 +341,10 @@ export async function advancePhaseCommand(feature, options = {}) {
326
341
  const state = loadState();
327
342
  state.features[feature].phase = nextPhase;
328
343
  state.features[feature].updatedAt = new Date().toISOString();
344
+ if (skippedPhases.length > 0) {
345
+ const existingSkipped = state.features[feature].skippedPhases || [];
346
+ state.features[feature].skippedPhases = [...new Set([...existingSkipped, ...skippedPhases])];
347
+ }
329
348
  saveState(state);
330
349
 
331
350
  // Run PhaseAdvanced agent-teams hook (non-blocking)
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Task management commands
3
- * Wrapper for bin/task-manager.js (CommonJS) to ESM
3
+ * Spawns bin/task-manager.js (ESM) as a subprocess.
4
4
  */
5
5
 
6
6
  import { spawn } from 'child_process';
@@ -9,7 +9,7 @@ import { dirname, join } from 'path';
9
9
  import chalk from 'chalk';
10
10
 
11
11
  const __dirname = dirname(fileURLToPath(import.meta.url));
12
- const taskManagerPath = join(__dirname, '..', '..', '..', 'bin', 'task-manager.cjs');
12
+ const taskManagerPath = join(__dirname, '..', '..', '..', 'bin', 'task-manager.js');
13
13
 
14
14
  /**
15
15
  * Execute task-manager.js with given arguments
@@ -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: '1-design', phase: 'clarify', protected: false },
30
30
  contracts: { filename: 'contracts.cs', phaseDir: '1-design', phase: 'design', protected: true, approvalGate: 'design' },
31
+ contractsVsa: { filename: 'contracts-vsa.cs', phaseDir: '1-design', phase: 'design', protected: true, approvalGate: 'design' },
31
32
  tasks: { filename: 'tasks.md', phaseDir: '3-tasks', phase: 'tasks', protected: true, approvalGate: 'tasks' },
32
33
  uiDesignSystem:{ filename: 'design-system.md', phaseDir: '2-ui', phase: 'uiux', protected: true, approvalGate: 'uiux' },
33
34
  uiMockups: { filename: 'mockups.md', phaseDir: '2-ui', phase: 'uiux', protected: true, approvalGate: 'uiux' },
@@ -136,12 +136,13 @@ export function hasUIAgentsActive(projectPath, featureName) {
136
136
  return false;
137
137
  }
138
138
 
139
- // UI agents that require design system
139
+ // UI design agents that require a design system before implement phase.
140
+ // Implementation-only agents (blazor-builder, nextjs-expert, css-specialist)
141
+ // build from existing designs and do NOT trigger this gate.
140
142
  const uiAgents = [
141
- 'blazor-builder',
142
143
  'ui-designer',
143
- 'css-specialist',
144
- 'nextjs-expert'
144
+ 'uiux-designer',
145
+ 'ui-ux-designer',
145
146
  ];
146
147
 
147
148
  return feature.activeAgents.some(agentId => uiAgents.includes(agentId));