@polymorphism-tech/morph-spec 4.9.0 → 4.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/bin/morph-spec.js +30 -0
- package/bin/task-manager.js +34 -22
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +35 -98
- package/framework/agents/backend/api-designer.md +3 -0
- package/framework/agents/backend/dotnet-senior.md +3 -0
- package/framework/agents/backend/ef-modeler.md +2 -0
- package/framework/agents/backend/hangfire-orchestrator.md +2 -0
- package/framework/agents/backend/ms-agent-expert.md +2 -0
- package/framework/agents/frontend/blazor-builder.md +2 -0
- package/framework/agents/frontend/nextjs-expert.md +2 -0
- package/framework/agents/infrastructure/azure-architect.md +2 -0
- package/framework/agents/infrastructure/azure-deploy-specialist.md +2 -0
- package/framework/agents/infrastructure/bicep-architect.md +2 -0
- package/framework/agents/infrastructure/container-specialist.md +2 -0
- package/framework/agents/infrastructure/devops-engineer.md +3 -0
- package/framework/agents/infrastructure/infra-architect.md +3 -0
- package/framework/agents/integrations/asaas-financial.md +2 -0
- package/framework/agents/integrations/azure-identity.md +2 -0
- package/framework/agents/integrations/clerk-auth.md +3 -0
- package/framework/agents/integrations/hangfire-integration.md +2 -0
- package/framework/agents/integrations/resend-email.md +2 -0
- package/framework/agents.json +37 -7
- package/framework/commands/commit.md +166 -0
- package/framework/commands/morph-apply.md +156 -155
- package/framework/commands/morph-archive.md +33 -27
- package/framework/commands/morph-infra.md +83 -77
- package/framework/commands/morph-preflight.md +97 -55
- package/framework/commands/morph-proposal.md +131 -58
- package/framework/commands/morph-status.md +36 -30
- package/framework/commands/morph-troubleshoot.md +68 -59
- package/framework/hooks/claude-code/notification/approval-reminder.js +3 -2
- package/framework/hooks/claude-code/post-tool-use/dispatch.js +154 -31
- package/framework/hooks/claude-code/post-tool-use/skill-reminder.js +7 -84
- package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +8 -17
- package/framework/hooks/claude-code/pre-compact/save-morph-context.js +16 -3
- package/framework/hooks/claude-code/pre-tool-use/enforce-phase-writes.js +4 -3
- package/framework/hooks/claude-code/pre-tool-use/protect-spec-files.js +3 -2
- package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +60 -0
- package/framework/hooks/claude-code/session-start/inject-morph-context.js +55 -2
- package/framework/hooks/claude-code/session-start/post-compact-restore.js +41 -0
- package/framework/hooks/claude-code/stop/validate-completion.js +2 -15
- package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +23 -5
- package/framework/hooks/shared/compact-restore.js +100 -0
- package/framework/hooks/shared/dispatch-helpers.js +116 -0
- package/framework/hooks/shared/phase-utils.js +9 -5
- package/framework/hooks/shared/state-reader.js +27 -3
- package/framework/phases.json +30 -7
- package/framework/rules/csharp-standards.md +3 -0
- package/framework/rules/frontend-standards.md +2 -0
- package/framework/rules/infrastructure-standards.md +3 -0
- package/framework/rules/morph-workflow.md +143 -86
- package/framework/rules/nextjs-standards.md +2 -0
- package/framework/rules/testing-standards.md +3 -0
- package/framework/skills/level-0-meta/mcp-registry.json +86 -51
- package/framework/skills/level-0-meta/morph-brainstorming/SKILL.md +139 -0
- package/framework/skills/level-0-meta/morph-checklist/SKILL.md +42 -19
- package/framework/skills/level-0-meta/{code-review → morph-code-review}/SKILL.md +8 -5
- package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/SKILL.md +8 -6
- package/framework/skills/level-0-meta/morph-frontend-review/SKILL.md +362 -0
- package/framework/skills/level-0-meta/morph-init/SKILL.md +114 -20
- package/framework/skills/level-0-meta/morph-post-implementation/SKILL.md +362 -0
- package/framework/skills/level-0-meta/morph-replicate/SKILL.md +95 -87
- package/framework/skills/level-0-meta/{simulation-checklist → morph-simulation-checklist}/SKILL.md +24 -0
- package/framework/skills/level-0-meta/{tool-usage-guide → morph-tool-usage-guide}/SKILL.md +43 -43
- package/framework/skills/level-0-meta/{tool-usage-guide → morph-tool-usage-guide}/references/tools-per-phase.md +1 -2
- package/framework/skills/level-0-meta/{verification-before-completion → morph-verification-before-completion}/SKILL.md +23 -12
- package/framework/skills/level-0-meta/{verification-before-completion → morph-verification-before-completion}/scripts/check-phase-outputs.mjs +2 -2
- package/framework/skills/level-1-workflows/morph-phase-clarify/SKILL.md +247 -0
- package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +270 -0
- package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +499 -0
- package/framework/skills/level-1-workflows/morph-phase-implement/.morph/logs/activity.json +38 -0
- package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +472 -0
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/code-quality-reviewer-prompt.md +50 -0
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +45 -0
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/spec-reviewer-prompt.md +47 -0
- package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +246 -0
- package/framework/skills/level-1-workflows/morph-phase-setup/SKILL.md +238 -0
- package/framework/skills/level-1-workflows/morph-phase-tasks/.morph/logs/activity.json +14 -0
- package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +312 -0
- package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/scripts/validate-tasks.mjs +3 -3
- package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +324 -0
- package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +146 -0
- package/framework/standards/integration/mcp/mcp-tools.md +25 -7
- package/framework/templates/docs/onboarding.md +2 -2
- package/package.json +3 -4
- package/src/commands/agents/dispatch-agents.js +50 -3
- package/src/commands/mcp/mcp-setup.js +39 -2
- package/src/commands/phase/phase-reset.js +74 -0
- package/src/commands/project/doctor.js +26 -7
- package/src/commands/project/update.js +4 -4
- package/src/commands/scope/escalate.js +215 -0
- package/src/commands/state/advance-phase.js +27 -53
- package/src/commands/state/state.js +1 -1
- package/src/commands/task/expand.js +100 -0
- package/src/core/paths/output-schema.js +4 -3
- package/src/core/state/phase-state-machine.js +7 -4
- package/src/core/state/state-manager.js +4 -3
- package/src/lib/detectors/claude-config-detector.js +93 -347
- package/src/lib/detectors/design-system-detector.js +189 -189
- package/src/lib/detectors/index.js +155 -57
- package/src/lib/generators/context-generator.js +2 -2
- package/src/lib/installers/mcp-installer.js +37 -5
- package/src/lib/phase-chain/phase-validator.js +22 -16
- package/src/lib/scope/impact-analyzer.js +106 -0
- package/src/lib/stack-filter.js +58 -0
- package/src/lib/tasks/task-parser.js +1 -1
- package/src/lib/validators/shared/emit-validator-dispatch.js +64 -0
- package/src/scripts/setup-infra.js +68 -18
- package/src/utils/agents-installer.js +51 -17
- package/src/utils/claude-md-injector.js +90 -0
- package/src/utils/file-copier.js +0 -1
- package/src/utils/hooks-installer.js +16 -5
- package/src/utils/skills-installer.js +67 -7
- package/CLAUDE.md +0 -98
- package/framework/memory/patterns-learned.md +0 -766
- package/framework/skills/level-0-meta/brainstorming/SKILL.md +0 -137
- package/framework/skills/level-0-meta/frontend-review/SKILL.md +0 -359
- package/framework/skills/level-0-meta/post-implementation/SKILL.md +0 -362
- package/framework/skills/level-0-meta/terminal-title/SKILL.md +0 -61
- package/framework/skills/level-0-meta/terminal-title/scripts/set_title.sh +0 -65
- package/framework/skills/level-1-workflows/phase-clarify/SKILL.md +0 -216
- package/framework/skills/level-1-workflows/phase-codebase-analysis/SKILL.md +0 -252
- package/framework/skills/level-1-workflows/phase-design/SKILL.md +0 -383
- package/framework/skills/level-1-workflows/phase-implement/SKILL.md +0 -492
- package/framework/skills/level-1-workflows/phase-setup/SKILL.md +0 -195
- package/framework/skills/level-1-workflows/phase-tasks/SKILL.md +0 -271
- package/framework/skills/level-1-workflows/phase-uiux/SKILL.md +0 -286
- package/src/commands/project/index.js +0 -8
- package/src/core/index.js +0 -10
- package/src/core/state/index.js +0 -8
- package/src/core/templates/index.js +0 -9
- package/src/core/templates/template-data-sources.js +0 -325
- package/src/core/workflows/index.js +0 -7
- package/src/lib/detectors/config-detector.js +0 -223
- package/src/lib/detectors/standards-generator.js +0 -335
- package/src/lib/detectors/structure-detector.js +0 -275
- package/src/lib/monitor/agent-resolver.js +0 -144
- package/src/lib/monitor/renderer.js +0 -230
- package/src/lib/orchestration/index.js +0 -7
- package/src/lib/orchestration/team-orchestrator.js +0 -404
- package/src/sanitizer/context-sanitizer.js +0 -221
- package/src/sanitizer/patterns.js +0 -163
- package/src/writer/file-writer.js +0 -86
- /package/framework/skills/level-0-meta/{brainstorming → morph-brainstorming}/references/proposal-example.md +0 -0
- /package/framework/skills/level-0-meta/{code-review → morph-code-review}/references/review-example.md +0 -0
- /package/framework/skills/level-0-meta/{code-review → morph-code-review}/references/review-guidelines.md +0 -0
- /package/framework/skills/level-0-meta/{code-review → morph-code-review}/scripts/scan-csharp.mjs +0 -0
- /package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/references/review-example-nextjs.md +0 -0
- /package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/scripts/scan-nextjs.mjs +0 -0
- /package/framework/skills/level-0-meta/{frontend-review → morph-frontend-review}/scripts/scan-accessibility.mjs +0 -0
- /package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/scripts/detect-dev-server.mjs +0 -0
- /package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/scripts/detect-stack.mjs +0 -0
- /package/framework/skills/level-1-workflows/{phase-clarify → morph-phase-clarify}/references/clarifications-example.md +0 -0
- /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/architecture-analysis-guide.md +0 -0
- /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/spec-authoring-guide.md +0 -0
- /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/spec-example.md +0 -0
- /package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/references/recap-example.md +0 -0
- /package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/references/vsa-implementation-guide.md +0 -0
- /package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/references/task-planning-patterns.md +0 -0
- /package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/references/tasks-example.md +0 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: morph:scope-escalation
|
|
3
|
+
description: Guided workflow for mid-implementation scope escalation — analyzes complexity discovery, recommends action, and executes phase regression or task expansion
|
|
4
|
+
user-invocable: true
|
|
5
|
+
argument-hint: "[feature-name]"
|
|
6
|
+
allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Scope Escalation Workflow
|
|
10
|
+
|
|
11
|
+
Use this skill when a task during implementation reveals significantly more complexity than estimated.
|
|
12
|
+
|
|
13
|
+
## Prerequisites
|
|
14
|
+
|
|
15
|
+
- Feature must be in `implement` phase
|
|
16
|
+
- You must have identified a specific task that triggered the discovery
|
|
17
|
+
|
|
18
|
+
## Workflow
|
|
19
|
+
|
|
20
|
+
### Step 1: COLLECT Evidence
|
|
21
|
+
|
|
22
|
+
Use `AskUserQuestion` to gather the details:
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"questions": [
|
|
27
|
+
{
|
|
28
|
+
"header": "Task ID",
|
|
29
|
+
"question": "Which task triggered the complexity discovery?",
|
|
30
|
+
"multiSelect": false,
|
|
31
|
+
"options": [
|
|
32
|
+
{ "label": "T001", "description": "First task" },
|
|
33
|
+
{ "label": "T002", "description": "Second task" },
|
|
34
|
+
{ "label": "Other", "description": "Type the task ID" }
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"header": "Discovery",
|
|
39
|
+
"question": "What complexity did you discover?",
|
|
40
|
+
"multiSelect": true,
|
|
41
|
+
"options": [
|
|
42
|
+
{ "label": "Hidden dependencies", "description": "More systems involved than spec assumed" },
|
|
43
|
+
{ "label": "Wrong assumptions", "description": "Spec assumed simpler architecture" },
|
|
44
|
+
{ "label": "Missing abstractions", "description": "Need new patterns/interfaces not in contracts" },
|
|
45
|
+
{ "label": "Other", "description": "Describe the discovery" }
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
> Populate the Task ID options dynamically from the feature's `tasks.md`. After receiving answers, **read the relevant code files** to verify the evidence before classifying.
|
|
53
|
+
|
|
54
|
+
### Step 2: ANALYZE Impact
|
|
55
|
+
|
|
56
|
+
Run dry-run analysis:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
npx morph-spec scope escalate <feature> --task <id> --reason "<reason>" --dry-run
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Read the recommendation output. Additionally:
|
|
63
|
+
|
|
64
|
+
- Read `tasks.md` to understand which tasks are affected
|
|
65
|
+
- Read `spec.md` to check if the spec assumed simpler architecture
|
|
66
|
+
- Check completed tasks for potential impact
|
|
67
|
+
|
|
68
|
+
### Step 3: CLASSIFY and PROPOSE
|
|
69
|
+
|
|
70
|
+
Present the recommendation to the user:
|
|
71
|
+
|
|
72
|
+
| Impact | Action | When |
|
|
73
|
+
|--------|--------|------|
|
|
74
|
+
| **Low** (1-3 tasks) | Task Expansion | The task is bigger than expected, but scope is contained |
|
|
75
|
+
| **Medium** (4+ tasks, spec ok) | Regress to tasks | Multiple tasks need rewriting, but the spec is correct |
|
|
76
|
+
| **High** (spec wrong) | Regress to design | The spec made incorrect assumptions about the codebase |
|
|
77
|
+
|
|
78
|
+
Explain WHY you recommend this classification based on the evidence.
|
|
79
|
+
|
|
80
|
+
### Step 4: APPROVE
|
|
81
|
+
|
|
82
|
+
Pause and wait for user confirmation using `AskUserQuestion`:
|
|
83
|
+
|
|
84
|
+
```json
|
|
85
|
+
{
|
|
86
|
+
"questions": [
|
|
87
|
+
{
|
|
88
|
+
"header": "Severity",
|
|
89
|
+
"question": "Impact classified as {LOW|MEDIUM|HIGH}. Do you agree?",
|
|
90
|
+
"multiSelect": false,
|
|
91
|
+
"options": [
|
|
92
|
+
{ "label": "Agree", "description": "Classification is correct" },
|
|
93
|
+
{ "label": "Override", "description": "I think the impact is different" }
|
|
94
|
+
]
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"header": "Action",
|
|
98
|
+
"question": "Recommended action: {action}. Proceed?",
|
|
99
|
+
"multiSelect": false,
|
|
100
|
+
"options": [
|
|
101
|
+
{ "label": "Proceed", "description": "{action description}" },
|
|
102
|
+
{ "label": "Different target", "description": "I want to regress to a different phase" },
|
|
103
|
+
{ "label": "Cancel", "description": "Do not escalate — continue as-is" }
|
|
104
|
+
]
|
|
105
|
+
}
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**DO NOT proceed without explicit user approval.** If user chooses "Cancel", stop the workflow entirely.
|
|
111
|
+
|
|
112
|
+
### Step 5: EXECUTE
|
|
113
|
+
|
|
114
|
+
Based on approved action:
|
|
115
|
+
|
|
116
|
+
**For expansion (low impact):**
|
|
117
|
+
```bash
|
|
118
|
+
npx morph-spec task expand <feature> <task-id> --into "T017a: <title>" "T017b: <title>" ...
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**For regression (medium/high impact):**
|
|
122
|
+
```bash
|
|
123
|
+
npx morph-spec scope escalate <feature> --task <id> --reason "<reason>"
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Or with target override:
|
|
127
|
+
```bash
|
|
128
|
+
npx morph-spec scope escalate <feature> --task <id> --reason "<reason>" --target design
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Step 6: GUIDE Next Steps
|
|
132
|
+
|
|
133
|
+
After escalation:
|
|
134
|
+
|
|
135
|
+
- **If regressed to tasks:** "Re-analyze the affected tasks. Rewrite tasks.md with accurate scope. Run `/morph-proposal <feature>` to continue from the tasks phase."
|
|
136
|
+
- **If regressed to design:** "Re-analyze the spec. Update spec.md with the discovered architecture. Then regenerate tasks."
|
|
137
|
+
- **If expanded:** "Continue implementing. Use `morph-spec task start <feature> <sub-task-id>` to start the first sub-task."
|
|
138
|
+
|
|
139
|
+
Always remind: "Completed tasks flagged with `needsReview` should be checked for compatibility with the new scope."
|
|
140
|
+
|
|
141
|
+
## Anti-Patterns
|
|
142
|
+
|
|
143
|
+
- **DO NOT** skip the approval step
|
|
144
|
+
- **DO NOT** escalate without evidence — read the actual code first
|
|
145
|
+
- **DO NOT** regress to design when only tasks need rewriting
|
|
146
|
+
- **DO NOT** expand when 4+ tasks are affected — regress instead
|
|
@@ -49,15 +49,10 @@ const repo = await mcp__github__get_repo();
|
|
|
49
49
|
|
|
50
50
|
| MCP | Use Case | Example |
|
|
51
51
|
|-----|----------|---------|
|
|
52
|
-
| **Figma** | Extract design tokens, components | `mcp__figma__get_file({ fileKey })` |
|
|
53
52
|
| **Playwright** | Navigate, screenshot, inspect live pages | `mcp__playwright__browser_navigate({ url })` |
|
|
54
53
|
| **Context7** | Component library documentation | `mcp__context7__query_docs({ libraryId, query })` |
|
|
55
54
|
|
|
56
55
|
```javascript
|
|
57
|
-
// Get design tokens from Figma
|
|
58
|
-
const file = await mcp__figma__get_file({ fileKey: "abc123" });
|
|
59
|
-
// → colors, typography, spacing from design file
|
|
60
|
-
|
|
61
56
|
// Screenshot existing app for design reference
|
|
62
57
|
await mcp__playwright__browser_navigate({ url: "https://app.example.com/dashboard" });
|
|
63
58
|
const screenshot = await mcp__playwright__browser_take_screenshot();
|
|
@@ -180,6 +175,7 @@ for (const task of tasks) {
|
|
|
180
175
|
| **GitHub** | Create PR, push branches | `mcp__github__create_pull_request()` |
|
|
181
176
|
| **Context7** | Look up API usage during coding | `mcp__context7__query_docs()` |
|
|
182
177
|
| **Playwright** | Smoke test deployed features, verify UI | `mcp__playwright__browser_navigate()` |
|
|
178
|
+
| **Vercel** | Deploy, check logs, manage env vars | `mcp__vercel__list_projects()` |
|
|
183
179
|
| **Azure** | Provision resources, check deployment status | Provider-specific |
|
|
184
180
|
| **Docker** | Build images, manage containers | Provider-specific |
|
|
185
181
|
|
|
@@ -214,7 +210,25 @@ const logs = await mcp__playwright__browser_console_messages();
|
|
|
214
210
|
// Screenshot for recap.md documentation
|
|
215
211
|
const screenshot = await mcp__playwright__browser_take_screenshot();
|
|
216
212
|
|
|
217
|
-
//
|
|
213
|
+
// === VERCEL (Deployment & Environment) ===
|
|
214
|
+
|
|
215
|
+
// List projects
|
|
216
|
+
const projects = await mcp__vercel__list_projects();
|
|
217
|
+
|
|
218
|
+
// Check deployment status and logs
|
|
219
|
+
const deployments = await mcp__vercel__get_deployments({ projectId: 'my-project' });
|
|
220
|
+
const logs = await mcp__vercel__get_deployment_logs({ deploymentId: deployments[0].uid });
|
|
221
|
+
|
|
222
|
+
// Manage environment variables
|
|
223
|
+
await mcp__vercel__manage_env_vars({
|
|
224
|
+
projectId: 'my-project',
|
|
225
|
+
action: 'set',
|
|
226
|
+
key: 'DATABASE_URL',
|
|
227
|
+
value: process.env.DATABASE_URL,
|
|
228
|
+
target: ['production', 'preview']
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// Fallback: Bash for migrations, gh CLI for PRs, vercel CLI for deployments
|
|
218
232
|
```
|
|
219
233
|
|
|
220
234
|
---
|
|
@@ -225,7 +239,6 @@ const screenshot = await mcp__playwright__browser_take_screenshot();
|
|
|
225
239
|
|------|----------|--------------------|
|
|
226
240
|
| Database schema | Supabase/DB MCP | Grep queries + Read types |
|
|
227
241
|
| Repo metadata | GitHub MCP | Bash `gh` CLI |
|
|
228
|
-
| Design tokens | Figma MCP | Read CSS/SCSS variables |
|
|
229
242
|
| Library docs | Context7 | WebSearch + WebFetch |
|
|
230
243
|
| Live page preview | Playwright MCP | WebFetch URL |
|
|
231
244
|
| Page interaction (click, type, navigate) | Playwright MCP | Manual testing |
|
|
@@ -233,6 +246,7 @@ const screenshot = await mcp__playwright__browser_take_screenshot();
|
|
|
233
246
|
| Console error checking | Playwright MCP (`browser_console_messages`) | Browser DevTools |
|
|
234
247
|
| Container ops | Docker MCP | Bash `docker` CLI |
|
|
235
248
|
| Cloud resources | Azure MCP | Bash `az` CLI |
|
|
249
|
+
| Vercel deployments | Vercel MCP | Bash `vercel` CLI |
|
|
236
250
|
|
|
237
251
|
**Rule:** MCP tools provide structured data (JSON responses). Native tools require manual parsing. **Always prefer MCP when available** — fall back to native when not.
|
|
238
252
|
|
|
@@ -332,6 +346,8 @@ await mcp__playwright__browser_file_upload({ ref: "s1e20", paths: ["/path/to/fil
|
|
|
332
346
|
|
|
333
347
|
### Supabase: Full Schema Discovery
|
|
334
348
|
|
|
349
|
+
> **v4 config:** Remote OAuth — `{ "type": "http", "url": "https://mcp.supabase.com/mcp" }`. Authenticate via `/mcp` in Claude Code.
|
|
350
|
+
|
|
335
351
|
```javascript
|
|
336
352
|
// Complete workflow for Phase 2 schema analysis
|
|
337
353
|
const tables = await mcp__supabase__list_tables();
|
|
@@ -362,6 +378,8 @@ const docs = await mcp__context7__query_docs({
|
|
|
362
378
|
|
|
363
379
|
### GitHub: Code Search Across Repo
|
|
364
380
|
|
|
381
|
+
> **v4 config:** Docker — `{ "command": "docker", "args": ["run", "-i", "--rm", "-e", "GITHUB_PERSONAL_ACCESS_TOKEN", "ghcr.io/github/github-mcp-server"] }`. Requires Docker Desktop.
|
|
382
|
+
|
|
365
383
|
```javascript
|
|
366
384
|
// Find patterns in large codebase during Phase 2
|
|
367
385
|
const results = await mcp__github__search_code({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polymorphism-tech/morph-spec",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.10.1",
|
|
4
4
|
"description": "MORPH-SPEC: AI-First development framework with validation pipeline and multi-stack support",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude-code",
|
|
@@ -47,7 +47,6 @@
|
|
|
47
47
|
"docs": "jsdoc -c jsdoc.json",
|
|
48
48
|
"docs:watch": "jsdoc -c jsdoc.json --watch",
|
|
49
49
|
"docs:serve": "npx http-server docs/api -p 8080 -o",
|
|
50
|
-
"version:bump": "node scripts/bump-version.js",
|
|
51
50
|
"release": "node scripts/release.js",
|
|
52
51
|
"postinstall": "node src/scripts/global-install.js"
|
|
53
52
|
},
|
|
@@ -55,7 +54,7 @@
|
|
|
55
54
|
"chalk": "^5.3.0",
|
|
56
55
|
"commander": "^12.0.0",
|
|
57
56
|
"fs-extra": "^11.2.0",
|
|
58
|
-
"glob": "^
|
|
57
|
+
"glob": "^13.0.6",
|
|
59
58
|
"handlebars": "^4.7.8",
|
|
60
59
|
"inquirer": "^9.2.0",
|
|
61
60
|
"minimatch": "^9.0.5",
|
|
@@ -76,7 +75,7 @@
|
|
|
76
75
|
"access": "public"
|
|
77
76
|
},
|
|
78
77
|
"devDependencies": {
|
|
79
|
-
"c8": "^
|
|
78
|
+
"c8": "^11.0.0",
|
|
80
79
|
"docdash": "^2.0.2",
|
|
81
80
|
"jsdoc": "^4.0.5"
|
|
82
81
|
}
|
|
@@ -124,6 +124,51 @@ export function buildSkillsBlock(phase, phasesData) {
|
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
128
|
+
// MCP Awareness Injection
|
|
129
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Build an MCP availability block for the given phase.
|
|
133
|
+
* Reads recommendedMCPs from phases.json and MCP usage info from mcp-registry.json.
|
|
134
|
+
* Returns null if no MCPs recommended for this phase.
|
|
135
|
+
*
|
|
136
|
+
* @param {string} phase - Phase id (e.g. 'implement')
|
|
137
|
+
* @param {Object|null} phasesData - Parsed phases.json content
|
|
138
|
+
* @param {Object} [opts] - Options
|
|
139
|
+
* @param {string} [opts.registryPath] - Override path to mcp-registry.json
|
|
140
|
+
* @returns {string|null}
|
|
141
|
+
*/
|
|
142
|
+
export function buildMcpBlock(phase, phasesData, opts = {}) {
|
|
143
|
+
try {
|
|
144
|
+
const mcps = phasesData?.phases?.[phase]?.recommendedMCPs;
|
|
145
|
+
if (!mcps || mcps.length === 0) return null;
|
|
146
|
+
|
|
147
|
+
// Try to load registry for usage descriptions
|
|
148
|
+
let registry = null;
|
|
149
|
+
const registryPath = opts.registryPath || join(__dirname, '../../../framework/skills/level-0-meta/mcp-registry.json');
|
|
150
|
+
try {
|
|
151
|
+
if (existsSync(registryPath)) {
|
|
152
|
+
registry = JSON.parse(readFileSync(registryPath, 'utf8'));
|
|
153
|
+
}
|
|
154
|
+
} catch { /* non-blocking */ }
|
|
155
|
+
|
|
156
|
+
const lines = mcps.map(name => {
|
|
157
|
+
const usage = registry?.mcps?.[name]?.usage || '';
|
|
158
|
+
return usage ? `- ${name}: ${usage}` : `- ${name}`;
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
return [
|
|
162
|
+
`Available MCPs for ${phase} phase:`,
|
|
163
|
+
...lines,
|
|
164
|
+
'',
|
|
165
|
+
'Use MCP tools when available; fall back to native tools (Bash, Read, Grep) when not.',
|
|
166
|
+
].join('\n');
|
|
167
|
+
} catch {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
127
172
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
128
173
|
// Helpers
|
|
129
174
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -201,7 +246,7 @@ export function parseAndGroupTasks(tasksPath) {
|
|
|
201
246
|
const groups = {};
|
|
202
247
|
|
|
203
248
|
// Match task headings: ### T001 — Title or ### T001: Title
|
|
204
|
-
const taskHeadingRe = /^###\s+(T\d{3,})\s*[
|
|
249
|
+
const taskHeadingRe = /^###\s+(T\d{3,})\s*[—–:\-]\s*(.+)$/gm;
|
|
205
250
|
// Match category line within a task block: **category**: `domain` or - category: application
|
|
206
251
|
const categoryRe = /(?:\*\*category\*\*\s*:\s*`?([a-z-]+)`?|category:\s*([a-z-]+))/i;
|
|
207
252
|
|
|
@@ -344,13 +389,15 @@ export async function buildDispatchConfig(projectPath, featureName, phase, opts
|
|
|
344
389
|
rawPrompt = agentData.teammate.spawn_prompt;
|
|
345
390
|
}
|
|
346
391
|
|
|
347
|
-
// Append standards digest as Constraints block, then
|
|
392
|
+
// Append standards digest as Constraints block, then skills block, then MCP block
|
|
348
393
|
const briefing = buildAgentBriefing(agentId, phase);
|
|
349
394
|
const skillsBlock = buildSkillsBlock(phase, phasesData);
|
|
395
|
+
const mcpBlock = buildMcpBlock(phase, phasesData);
|
|
350
396
|
const fullTaskPrompt = [
|
|
351
397
|
rawPrompt,
|
|
352
398
|
briefing ? `\n\nConstraints:\n${briefing}` : '',
|
|
353
399
|
skillsBlock ? `\n\n${skillsBlock}` : '',
|
|
400
|
+
mcpBlock ? `\n\n${mcpBlock}` : '',
|
|
354
401
|
].join('');
|
|
355
402
|
|
|
356
403
|
dispatchableAgents.push({
|
|
@@ -398,7 +445,7 @@ export async function buildDispatchConfig(projectPath, featureName, phase, opts
|
|
|
398
445
|
|
|
399
446
|
if (phase === 'implement') {
|
|
400
447
|
// Group tasks from tasks.md by domain category
|
|
401
|
-
const tasksPath = join(projectPath, `.morph/features/${featureName}/
|
|
448
|
+
const tasksPath = join(projectPath, `.morph/features/${featureName}/4-tasks/tasks.md`);
|
|
402
449
|
const tasksByGroup = parseAndGroupTasks(tasksPath);
|
|
403
450
|
|
|
404
451
|
for (const [group, taskIds] of Object.entries(tasksByGroup)) {
|
|
@@ -19,7 +19,8 @@ import {
|
|
|
19
19
|
installMcpWithCredentials,
|
|
20
20
|
generateSetupInstructions,
|
|
21
21
|
formatMcpStatusTable,
|
|
22
|
-
loadMcpRegistry
|
|
22
|
+
loadMcpRegistry,
|
|
23
|
+
isRemoteMcp
|
|
23
24
|
} from '../../lib/installers/mcp-installer.js';
|
|
24
25
|
import { detectClaudeConfig } from '../../lib/detectors/claude-config-detector.js';
|
|
25
26
|
import { detectProject } from '../../lib/detectors/index.js';
|
|
@@ -187,7 +188,43 @@ async function setupSpecificMcp(targetPath, name, stack, existingMcps) {
|
|
|
187
188
|
return;
|
|
188
189
|
}
|
|
189
190
|
|
|
190
|
-
//
|
|
191
|
+
// Remote HTTP MCP — show setup command and optionally auto-configure
|
|
192
|
+
if (isRemoteMcp(mcpEntry.install.config)) {
|
|
193
|
+
console.log(chalk.cyan(`\n Setting up ${name} MCP (remote)\n`));
|
|
194
|
+
console.log(chalk.gray(` Usage: ${mcpEntry.usage}`));
|
|
195
|
+
console.log('');
|
|
196
|
+
|
|
197
|
+
const instructions = generateSetupInstructions(name, mcpEntry);
|
|
198
|
+
console.log(chalk.white(` Run this command to add the MCP:`));
|
|
199
|
+
console.log(chalk.cyan(` ${instructions.cliCommand}`));
|
|
200
|
+
console.log('');
|
|
201
|
+
|
|
202
|
+
for (const warning of mcpEntry.install.warnings || []) {
|
|
203
|
+
console.log(chalk.yellow(` ⚠ ${warning}`));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const { installNow } = await inquirer.prompt([{
|
|
207
|
+
type: 'confirm',
|
|
208
|
+
name: 'installNow',
|
|
209
|
+
message: 'Add to .claude/settings.local.json now?',
|
|
210
|
+
default: true
|
|
211
|
+
}]);
|
|
212
|
+
|
|
213
|
+
if (installNow) {
|
|
214
|
+
const spinner = ora(`Configuring ${name}...`).start();
|
|
215
|
+
const result = await installAutoMcps(targetPath, { [name]: mcpEntry });
|
|
216
|
+
if (result.added.length > 0) {
|
|
217
|
+
spinner.succeed(`${name} MCP configured (authenticate via /mcp in Claude Code)`);
|
|
218
|
+
} else {
|
|
219
|
+
spinner.info(`${name} MCP already configured`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
console.log('');
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Needs credentials (stdio MCPs)
|
|
191
228
|
console.log(chalk.cyan(`\n Setting up ${name} MCP\n`));
|
|
192
229
|
console.log(chalk.gray(` Usage: ${mcpEntry.usage}`));
|
|
193
230
|
console.log('');
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MORPH-SPEC Phase Reset Command
|
|
3
|
+
*
|
|
4
|
+
* Recovery tool for corrupted phase state.
|
|
5
|
+
* Sets the phase directly without approval gate checks.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* morph-spec phase reset <feature> <phase>
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import chalk from 'chalk';
|
|
12
|
+
import { loadState, saveState, derivePhase } from '../../core/state/state-manager.js';
|
|
13
|
+
import { join } from 'path';
|
|
14
|
+
|
|
15
|
+
const VALID_PHASES = ['proposal', 'setup', 'uiux', 'design', 'clarify', 'plan', 'tasks', 'implement', 'sync'];
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Core reset logic — exported for testing.
|
|
19
|
+
* @param {string} featureName
|
|
20
|
+
* @param {string} targetPhase
|
|
21
|
+
* @returns {{ success: boolean, error?: string, derivedPhase?: string }}
|
|
22
|
+
*/
|
|
23
|
+
export function resetPhase(featureName, targetPhase) {
|
|
24
|
+
if (!VALID_PHASES.includes(targetPhase)) {
|
|
25
|
+
return {
|
|
26
|
+
success: false,
|
|
27
|
+
error: `Unknown phase: "${targetPhase}". Valid phases: ${VALID_PHASES.join(', ')}`
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const state = loadState();
|
|
32
|
+
if (!state?.features?.[featureName]) {
|
|
33
|
+
return {
|
|
34
|
+
success: false,
|
|
35
|
+
error: `Feature "${featureName}" not found in state.json`
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Get filesystem-derived phase for comparison
|
|
40
|
+
const featureFolderPath = join(process.cwd(), '.morph', 'features', featureName);
|
|
41
|
+
const derivedPhase = derivePhase(featureFolderPath);
|
|
42
|
+
|
|
43
|
+
// Set phase directly — no approval gate checks
|
|
44
|
+
state.features[featureName].phase = targetPhase;
|
|
45
|
+
state.features[featureName].updatedAt = new Date().toISOString();
|
|
46
|
+
saveState(state);
|
|
47
|
+
|
|
48
|
+
return { success: true, derivedPhase };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* CLI command handler
|
|
53
|
+
*/
|
|
54
|
+
export async function phaseResetCommand(feature, phase) {
|
|
55
|
+
console.log(chalk.cyan('\n╔════════════════════════════════════════════════╗'));
|
|
56
|
+
console.log(chalk.cyan('║ MORPH-SPEC PHASE RESET ║'));
|
|
57
|
+
console.log(chalk.cyan('╚════════════════════════════════════════════════╝\n'));
|
|
58
|
+
|
|
59
|
+
const result = resetPhase(feature, phase);
|
|
60
|
+
|
|
61
|
+
if (!result.success) {
|
|
62
|
+
console.log(chalk.red(`\n✗ ${result.error}`));
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
console.log(chalk.green(`✓ Phase reset to "${phase}" for feature "${feature}"`));
|
|
67
|
+
|
|
68
|
+
if (result.derivedPhase !== phase) {
|
|
69
|
+
console.log(chalk.yellow(`\n⚠ Filesystem-derived phase is "${result.derivedPhase}" (folders on disk differ from state)`));
|
|
70
|
+
console.log(chalk.gray(` This is expected during recovery. The state.json value takes priority.`));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
console.log('');
|
|
74
|
+
}
|
|
@@ -5,6 +5,7 @@ import fs from 'fs-extra';
|
|
|
5
5
|
import chalk from 'chalk';
|
|
6
6
|
import { logger } from '../../utils/logger.js';
|
|
7
7
|
import { pathExists, readJson } from '../../utils/file-copier.js';
|
|
8
|
+
import { checkClaudeMdImport } from '../../utils/claude-md-injector.js';
|
|
8
9
|
import {
|
|
9
10
|
checkCLIOutdated,
|
|
10
11
|
checkProjectOutdated,
|
|
@@ -84,7 +85,11 @@ const REQUIRED_COMMAND_FILES = [
|
|
|
84
85
|
'src/commands/validation/validate-feature.js',
|
|
85
86
|
'src/commands/templates/template-render.js',
|
|
86
87
|
'src/commands/agents/dispatch-agents.js',
|
|
87
|
-
'src/commands/project/worktree.js'
|
|
88
|
+
'src/commands/project/worktree.js',
|
|
89
|
+
'src/commands/phase/phase-reset.js',
|
|
90
|
+
'src/commands/scope/escalate.js',
|
|
91
|
+
'src/commands/task/expand.js',
|
|
92
|
+
'src/lib/scope/impact-analyzer.js'
|
|
88
93
|
];
|
|
89
94
|
|
|
90
95
|
// framework standards
|
|
@@ -296,10 +301,18 @@ async function doctorMcpCommand(targetPath) {
|
|
|
296
301
|
for (const [name, config] of Object.entries(allMcpServers)) {
|
|
297
302
|
const issues = [];
|
|
298
303
|
let status = 'ok';
|
|
304
|
+
let isRemote = false;
|
|
299
305
|
|
|
300
|
-
// ──
|
|
306
|
+
// ── 0. Remote MCP detection ────────────────────────────────────────────
|
|
307
|
+
// Remote MCPs use HTTP transport (type: "http" or url property) — no binary to check
|
|
308
|
+
if (config.type === 'http' || (config.url && !config.command)) {
|
|
309
|
+
isRemote = true;
|
|
310
|
+
// Remote MCPs are always "ok" from a config perspective — auth happens at runtime
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// ── 1. Binary check (stdio MCPs only) ──────────────────────────────────
|
|
301
314
|
const cmd = config.command || '';
|
|
302
|
-
if (cmd && !KNOWN_RUNTIMES.has(cmd)) {
|
|
315
|
+
if (!isRemote && cmd && !KNOWN_RUNTIMES.has(cmd)) {
|
|
303
316
|
try {
|
|
304
317
|
execSync(isWindows ? `where "${cmd}"` : `which "${cmd}"`, { stdio: 'ignore' });
|
|
305
318
|
} catch {
|
|
@@ -329,12 +342,14 @@ async function doctorMcpCommand(targetPath) {
|
|
|
329
342
|
}
|
|
330
343
|
}
|
|
331
344
|
|
|
332
|
-
checks.push({ name, status, issues, cmd });
|
|
345
|
+
checks.push({ name, status, issues, cmd, isRemote });
|
|
333
346
|
}
|
|
334
347
|
|
|
335
348
|
// ── Display ────────────────────────────────────────────────────────────────
|
|
336
349
|
for (const c of checks) {
|
|
337
|
-
const typeLabel = c.
|
|
350
|
+
const typeLabel = c.isRemote
|
|
351
|
+
? chalk.gray(` [remote: ${allMcpServers[c.name]?.url || 'http'}]`)
|
|
352
|
+
: c.cmd ? chalk.gray(` [${c.cmd}]`) : '';
|
|
338
353
|
|
|
339
354
|
if (c.status === 'ok') {
|
|
340
355
|
console.log(chalk.green(` ✓ ${c.name}`) + typeLabel);
|
|
@@ -490,10 +505,14 @@ export async function doctorCommand(options = {}) {
|
|
|
490
505
|
hasWarnings = true;
|
|
491
506
|
}
|
|
492
507
|
|
|
493
|
-
// Check CLAUDE.md
|
|
508
|
+
// Check CLAUDE.md has morph-spec @import
|
|
494
509
|
const claudeMd = join(targetPath, 'CLAUDE.md');
|
|
495
|
-
|
|
510
|
+
const claudeMdStatus = await checkClaudeMdImport(claudeMd);
|
|
511
|
+
if (claudeMdStatus === 'ok') {
|
|
496
512
|
checks.push({ name: 'CLAUDE.md', status: 'ok' });
|
|
513
|
+
} else if (claudeMdStatus === 'missing-import') {
|
|
514
|
+
checks.push({ name: 'CLAUDE.md', status: 'warn', msg: 'missing @.claude/CLAUDE.md import — run morph-spec update' });
|
|
515
|
+
hasWarnings = true;
|
|
497
516
|
} else {
|
|
498
517
|
checks.push({ name: 'CLAUDE.md', status: 'missing' });
|
|
499
518
|
hasErrors = true;
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
import { installClaudeHooks, installGlobalStatusline, installVSCodeTerminalSettings, installShellIntegration } from '../../utils/claude-settings-manager.js';
|
|
29
29
|
import { installSkills } from '../../utils/skills-installer.js';
|
|
30
30
|
import { installAgents, installDomainAgents } from '../../utils/agents-installer.js';
|
|
31
|
+
import { injectMorphImport } from '../../utils/claude-md-injector.js';
|
|
31
32
|
|
|
32
33
|
/**
|
|
33
34
|
* Backup user's config.json before cleaning
|
|
@@ -321,11 +322,10 @@ export async function updateCommand(options) {
|
|
|
321
322
|
logger.dim(' ⚠ Could not configure VS Code terminal settings (non-critical)');
|
|
322
323
|
}
|
|
323
324
|
|
|
324
|
-
//
|
|
325
|
-
updateSpinner.text = '
|
|
326
|
-
const claudeMdSrc = join(frameworkDir, 'CLAUDE.md');
|
|
325
|
+
// Ensure root CLAUDE.md has morph-spec @import (preserves user content)
|
|
326
|
+
updateSpinner.text = 'Checking CLAUDE.md import...';
|
|
327
327
|
const claudeMdDest = join(targetPath, 'CLAUDE.md');
|
|
328
|
-
await
|
|
328
|
+
await injectMorphImport(claudeMdDest);
|
|
329
329
|
|
|
330
330
|
// Restore user config after framework reinstallation
|
|
331
331
|
updateSpinner.text = 'Restoring user configuration...';
|