@polymorphism-tech/morph-spec 4.9.0 → 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.
- 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 +99 -98
- package/framework/agents.json +37 -7
- package/framework/commands/commit.md +166 -0
- package/framework/commands/morph-apply.md +13 -2
- package/framework/commands/morph-archive.md +8 -2
- package/framework/commands/morph-infra.md +6 -0
- package/framework/commands/morph-preflight.md +6 -0
- package/framework/commands/morph-proposal.md +56 -7
- package/framework/commands/morph-status.md +6 -0
- package/framework/commands/morph-troubleshoot.md +6 -0
- 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/morph-workflow.md +88 -86
- package/framework/skills/level-0-meta/mcp-registry.json +86 -51
- package/framework/skills/level-0-meta/{brainstorming → morph-brainstorming}/SKILL.md +13 -16
- package/framework/skills/level-0-meta/{code-review → morph-code-review}/SKILL.md +1 -1
- package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/SKILL.md +2 -2
- package/framework/skills/level-0-meta/{frontend-review → morph-frontend-review}/SKILL.md +5 -5
- package/framework/skills/level-0-meta/morph-init/SKILL.md +72 -7
- package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/SKILL.md +9 -9
- package/framework/skills/level-0-meta/morph-replicate/SKILL.md +1 -1
- package/framework/skills/level-0-meta/{terminal-title → morph-terminal-title}/SKILL.md +1 -1
- package/framework/skills/level-0-meta/{tool-usage-guide → morph-tool-usage-guide}/SKILL.md +2 -3
- 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 +1 -1
- 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 +238 -0
- package/framework/skills/level-1-workflows/{phase-codebase-analysis → morph-phase-codebase-analysis}/SKILL.md +251 -251
- package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +507 -0
- package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/SKILL.md +590 -491
- 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 +254 -0
- package/framework/skills/level-1-workflows/{phase-setup → morph-phase-setup}/SKILL.md +237 -194
- package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/SKILL.md +307 -270
- package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/scripts/validate-tasks.mjs +3 -3
- package/framework/skills/level-1-workflows/{phase-uiux → morph-phase-uiux}/SKILL.md +320 -285
- package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +97 -0
- package/framework/standards/integration/mcp/mcp-tools.md +25 -7
- package/framework/templates/docs/onboarding.md +2 -2
- package/package.json +1 -2
- 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 +19 -5
- 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/tasks/task-parser.js +1 -1
- package/src/lib/validators/shared/emit-validator-dispatch.js +64 -0
- package/src/scripts/setup-infra.js +15 -0
- package/src/utils/agents-installer.js +32 -12
- package/src/utils/file-copier.js +0 -1
- package/src/utils/hooks-installer.js +15 -1
- package/framework/skills/level-1-workflows/phase-clarify/SKILL.md +0 -216
- package/framework/skills/level-1-workflows/phase-design/SKILL.md +0 -383
- 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-0-meta/{simulation-checklist → morph-simulation-checklist}/SKILL.md +0 -0
- /package/framework/skills/level-0-meta/{terminal-title → morph-terminal-title}/scripts/set_title.sh +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,97 @@
|
|
|
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
|
+
---
|
|
7
|
+
|
|
8
|
+
# Scope Escalation Workflow
|
|
9
|
+
|
|
10
|
+
Use this skill when a task during implementation reveals significantly more complexity than estimated.
|
|
11
|
+
|
|
12
|
+
## Prerequisites
|
|
13
|
+
|
|
14
|
+
- Feature must be in `implement` phase
|
|
15
|
+
- You must have identified a specific task that triggered the discovery
|
|
16
|
+
|
|
17
|
+
## Workflow
|
|
18
|
+
|
|
19
|
+
### Step 1: COLLECT Evidence
|
|
20
|
+
|
|
21
|
+
Ask the user (using `AskUserQuestion`):
|
|
22
|
+
|
|
23
|
+
1. **Which task triggered the discovery?** (task ID from tasks.md)
|
|
24
|
+
2. **What did you discover?** (specific complexity: hidden dependencies, wrong assumptions, missing abstractions)
|
|
25
|
+
3. **Show evidence** — read the relevant code files to understand the actual complexity
|
|
26
|
+
|
|
27
|
+
### Step 2: ANALYZE Impact
|
|
28
|
+
|
|
29
|
+
Run dry-run analysis:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npx morph-spec scope escalate <feature> --task <id> --reason "<reason>" --dry-run
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Read the recommendation output. Additionally:
|
|
36
|
+
|
|
37
|
+
- Read `tasks.md` to understand which tasks are affected
|
|
38
|
+
- Read `spec.md` to check if the spec assumed simpler architecture
|
|
39
|
+
- Check completed tasks for potential impact
|
|
40
|
+
|
|
41
|
+
### Step 3: CLASSIFY and PROPOSE
|
|
42
|
+
|
|
43
|
+
Present the recommendation to the user:
|
|
44
|
+
|
|
45
|
+
| Impact | Action | When |
|
|
46
|
+
|--------|--------|------|
|
|
47
|
+
| **Low** (1-3 tasks) | Task Expansion | The task is bigger than expected, but scope is contained |
|
|
48
|
+
| **Medium** (4+ tasks, spec ok) | Regress to tasks | Multiple tasks need rewriting, but the spec is correct |
|
|
49
|
+
| **High** (spec wrong) | Regress to design | The spec made incorrect assumptions about the codebase |
|
|
50
|
+
|
|
51
|
+
Explain WHY you recommend this classification based on the evidence.
|
|
52
|
+
|
|
53
|
+
### Step 4: APPROVE
|
|
54
|
+
|
|
55
|
+
Pause and wait for user confirmation using `AskUserQuestion`:
|
|
56
|
+
|
|
57
|
+
- "Do you agree with the [impact] classification?"
|
|
58
|
+
- "Should we proceed with [action]?"
|
|
59
|
+
- Allow override: "Or would you prefer a different target phase?"
|
|
60
|
+
|
|
61
|
+
**DO NOT proceed without explicit user approval.**
|
|
62
|
+
|
|
63
|
+
### Step 5: EXECUTE
|
|
64
|
+
|
|
65
|
+
Based on approved action:
|
|
66
|
+
|
|
67
|
+
**For expansion (low impact):**
|
|
68
|
+
```bash
|
|
69
|
+
npx morph-spec task expand <feature> <task-id> --into "T017a: <title>" "T017b: <title>" ...
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**For regression (medium/high impact):**
|
|
73
|
+
```bash
|
|
74
|
+
npx morph-spec scope escalate <feature> --task <id> --reason "<reason>"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Or with target override:
|
|
78
|
+
```bash
|
|
79
|
+
npx morph-spec scope escalate <feature> --task <id> --reason "<reason>" --target design
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Step 6: GUIDE Next Steps
|
|
83
|
+
|
|
84
|
+
After escalation:
|
|
85
|
+
|
|
86
|
+
- **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."
|
|
87
|
+
- **If regressed to design:** "Re-analyze the spec. Update spec.md with the discovered architecture. Then regenerate tasks."
|
|
88
|
+
- **If expanded:** "Continue implementing. Use `morph-spec task start <feature> <sub-task-id>` to start the first sub-task."
|
|
89
|
+
|
|
90
|
+
Always remind: "Completed tasks flagged with `needsReview` should be checked for compatibility with the new scope."
|
|
91
|
+
|
|
92
|
+
## Anti-Patterns
|
|
93
|
+
|
|
94
|
+
- **DO NOT** skip the approval step
|
|
95
|
+
- **DO NOT** escalate without evidence — read the actual code first
|
|
96
|
+
- **DO NOT** regress to design when only tasks need rewriting
|
|
97
|
+
- **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.0",
|
|
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
|
},
|
|
@@ -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
|
+
}
|
|
@@ -84,7 +84,11 @@ const REQUIRED_COMMAND_FILES = [
|
|
|
84
84
|
'src/commands/validation/validate-feature.js',
|
|
85
85
|
'src/commands/templates/template-render.js',
|
|
86
86
|
'src/commands/agents/dispatch-agents.js',
|
|
87
|
-
'src/commands/project/worktree.js'
|
|
87
|
+
'src/commands/project/worktree.js',
|
|
88
|
+
'src/commands/phase/phase-reset.js',
|
|
89
|
+
'src/commands/scope/escalate.js',
|
|
90
|
+
'src/commands/task/expand.js',
|
|
91
|
+
'src/lib/scope/impact-analyzer.js'
|
|
88
92
|
];
|
|
89
93
|
|
|
90
94
|
// framework standards
|
|
@@ -296,10 +300,18 @@ async function doctorMcpCommand(targetPath) {
|
|
|
296
300
|
for (const [name, config] of Object.entries(allMcpServers)) {
|
|
297
301
|
const issues = [];
|
|
298
302
|
let status = 'ok';
|
|
303
|
+
let isRemote = false;
|
|
299
304
|
|
|
300
|
-
// ──
|
|
305
|
+
// ── 0. Remote MCP detection ────────────────────────────────────────────
|
|
306
|
+
// Remote MCPs use HTTP transport (type: "http" or url property) — no binary to check
|
|
307
|
+
if (config.type === 'http' || (config.url && !config.command)) {
|
|
308
|
+
isRemote = true;
|
|
309
|
+
// Remote MCPs are always "ok" from a config perspective — auth happens at runtime
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// ── 1. Binary check (stdio MCPs only) ──────────────────────────────────
|
|
301
313
|
const cmd = config.command || '';
|
|
302
|
-
if (cmd && !KNOWN_RUNTIMES.has(cmd)) {
|
|
314
|
+
if (!isRemote && cmd && !KNOWN_RUNTIMES.has(cmd)) {
|
|
303
315
|
try {
|
|
304
316
|
execSync(isWindows ? `where "${cmd}"` : `which "${cmd}"`, { stdio: 'ignore' });
|
|
305
317
|
} catch {
|
|
@@ -329,12 +341,14 @@ async function doctorMcpCommand(targetPath) {
|
|
|
329
341
|
}
|
|
330
342
|
}
|
|
331
343
|
|
|
332
|
-
checks.push({ name, status, issues, cmd });
|
|
344
|
+
checks.push({ name, status, issues, cmd, isRemote });
|
|
333
345
|
}
|
|
334
346
|
|
|
335
347
|
// ── Display ────────────────────────────────────────────────────────────────
|
|
336
348
|
for (const c of checks) {
|
|
337
|
-
const typeLabel = c.
|
|
349
|
+
const typeLabel = c.isRemote
|
|
350
|
+
? chalk.gray(` [remote: ${allMcpServers[c.name]?.url || 'http'}]`)
|
|
351
|
+
: c.cmd ? chalk.gray(` [${c.cmd}]`) : '';
|
|
338
352
|
|
|
339
353
|
if (c.status === 'ok') {
|
|
340
354
|
console.log(chalk.green(` ✓ ${c.name}`) + typeLabel);
|