@polymorphism-tech/morph-spec 4.10.3 → 4.10.5
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 +3 -1
- package/bin/task-manager.js +21 -5
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/hooks/claude-code/post-tool-use/context-refresh.js +1 -1
- package/framework/phases.json +17 -17
- package/framework/skills/level-0-meta/morph-brainstorming/SKILL.md +1 -1
- package/framework/skills/level-0-meta/morph-checklist/SKILL.md +2 -2
- package/framework/skills/level-0-meta/morph-code-review/SKILL.md +1 -1
- package/framework/skills/level-0-meta/morph-code-review-nextjs/SKILL.md +1 -1
- package/framework/skills/level-0-meta/morph-frontend-review/SKILL.md +3 -3
- package/framework/skills/level-0-meta/morph-init/SKILL.md +6 -6
- package/framework/skills/level-0-meta/morph-post-implementation/SKILL.md +7 -7
- package/framework/skills/level-0-meta/morph-replicate/SKILL.md +4 -4
- package/framework/skills/level-0-meta/morph-replicate/references/blazor-html-mapping.md +1 -1
- package/framework/skills/level-0-meta/morph-simulation-checklist/SKILL.md +1 -1
- package/framework/skills/level-0-meta/morph-tool-usage-guide/SKILL.md +1 -1
- package/framework/skills/level-0-meta/morph-verification-before-completion/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-clarify/SKILL.md +4 -4
- package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +2 -2
- package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +4 -4
- package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +4 -5
- package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +5 -6
- package/framework/skills/level-1-workflows/morph-phase-setup/SKILL.md +3 -3
- package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +4 -5
- package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +4 -4
- package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +2 -2
- package/framework/workflows/docs/STORY-DRIVEN-DEVELOPMENT.md +1 -1
- package/package.json +1 -1
- package/src/commands/state/advance-phase.js +17 -4
- package/src/commands/state/approve.js +1 -1
- package/src/commands/state/state.js +26 -5
- package/src/lib/detectors/index.js +46 -28
- package/src/lib/phase-chain/phase-validator.js +6 -1
- package/src/lib/tasks/task-parser.js +4 -0
- package/src/scripts/setup-infra.js +1 -1
- package/src/utils/agents-installer.js +20 -11
- package/src/utils/banner.js +1 -1
- package/src/utils/hooks-installer.js +739 -739
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: morph
|
|
2
|
+
name: morph-phase-uiux
|
|
3
3
|
description: MORPH-SPEC Phase 1.5 (UI/UX). Creates design-system.md, mockups.md, components.md, and flows.md using Playwright/Context7 MCPs. Use after setup for features with frontend UI components, pages, dashboards, forms, wizards, or visual interactions requiring design documentation.
|
|
4
4
|
argument-hint: "[feature-name]"
|
|
5
5
|
user-invocable: false
|
|
6
6
|
allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion
|
|
7
|
-
cliVersion: "4.10.
|
|
7
|
+
cliVersion: "4.10.5"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# MORPH UI/UX Design - Phase 1.5
|
|
@@ -310,14 +310,14 @@ Use `AskUserQuestion` to capture explicit approval before advancing:
|
|
|
310
310
|
|
|
311
311
|
## Phase Outputs
|
|
312
312
|
|
|
313
|
-
<!-- morph
|
|
313
|
+
<!-- morph-outputs:uiux -->
|
|
314
314
|
| Output | Path |
|
|
315
315
|
|--------|------|
|
|
316
316
|
| `uiDesignSystem` | `.morph/features/{feature}/2-ui/design-system.md` |
|
|
317
317
|
| `uiMockups` | `.morph/features/{feature}/2-ui/mockups.md` |
|
|
318
318
|
| `uiComponents` | `.morph/features/{feature}/2-ui/components.md` |
|
|
319
319
|
| `uiFlows` | `.morph/features/{feature}/2-ui/flows.md` |
|
|
320
|
-
<!-- /morph
|
|
320
|
+
<!-- /morph-outputs -->
|
|
321
321
|
|
|
322
322
|
---
|
|
323
323
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: morph
|
|
2
|
+
name: morph-scope-escalation
|
|
3
3
|
description: Guided workflow for mid-implementation scope escalation — analyzes complexity discovery, recommends action, and executes phase regression or task expansion
|
|
4
4
|
user-invocable: true
|
|
5
5
|
argument-hint: "[feature-name]"
|
|
6
6
|
allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion
|
|
7
|
-
cliVersion: "4.10.
|
|
7
|
+
cliVersion: "4.10.5"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# Scope Escalation Workflow
|
package/package.json
CHANGED
|
@@ -20,7 +20,7 @@ import { validateTransition } from '../../core/state/phase-state-machine.js';
|
|
|
20
20
|
import { validateSpecContent, validateTasksContent } from '../../lib/validators/content/content-validator.js';
|
|
21
21
|
import { getWorkflowConfig } from '../../core/workflows/workflow-detector.js';
|
|
22
22
|
import { getStackProfile } from '../../lib/stack/stack-profile.js';
|
|
23
|
-
import { readFileSync, existsSync } from 'fs';
|
|
23
|
+
import { readFileSync, existsSync, mkdirSync } from 'fs';
|
|
24
24
|
import { join, dirname } from 'path';
|
|
25
25
|
import { fileURLToPath } from 'url';
|
|
26
26
|
import { emitValidatorDispatch } from '../../lib/validators/shared/emit-validator-dispatch.js';
|
|
@@ -339,12 +339,12 @@ export async function advancePhaseCommand(feature, options = {}) {
|
|
|
339
339
|
|
|
340
340
|
// === GATE 6: Clarifications Required ===
|
|
341
341
|
// When advancing FROM clarify, clarifications.md must exist.
|
|
342
|
-
// This enforces that morph
|
|
342
|
+
// This enforces that morph-phase-clarify was actually run (not just skipped).
|
|
343
343
|
if (currentPhase === 'clarify') {
|
|
344
344
|
const clarPath = join(process.cwd(), '.morph', 'features', feature, '1-design', 'clarifications.md');
|
|
345
345
|
if (!existsSync(clarPath)) {
|
|
346
346
|
console.log(chalk.red('\n✗ Cannot advance from clarify — clarifications.md is missing'));
|
|
347
|
-
console.log(chalk.yellow(' Run the phase-clarify skill first: /morph
|
|
347
|
+
console.log(chalk.yellow(' Run the phase-clarify skill first: /morph-phase-clarify'));
|
|
348
348
|
console.log(chalk.gray(' Output expected at: .morph/features/' + feature + '/1-design/clarifications.md\n'));
|
|
349
349
|
process.exit(1);
|
|
350
350
|
}
|
|
@@ -373,6 +373,19 @@ export async function advancePhaseCommand(feature, options = {}) {
|
|
|
373
373
|
const state = loadState();
|
|
374
374
|
state.features[feature].phase = nextPhase;
|
|
375
375
|
state.features[feature].updatedAt = new Date().toISOString();
|
|
376
|
+
|
|
377
|
+
// Create the target phase directory so derivePhase() reflects the new phase
|
|
378
|
+
const PHASE_DIR_MAP = {
|
|
379
|
+
'proposal': '0-proposal', 'design': '1-design', 'uiux': '2-ui',
|
|
380
|
+
'plan': '3-plan', 'tasks': '4-tasks', 'implement': '5-implement',
|
|
381
|
+
};
|
|
382
|
+
const targetDir = PHASE_DIR_MAP[nextPhase];
|
|
383
|
+
if (targetDir) {
|
|
384
|
+
const dirPath = join(featureFolderPath, targetDir);
|
|
385
|
+
if (!existsSync(dirPath)) {
|
|
386
|
+
mkdirSync(dirPath, { recursive: true });
|
|
387
|
+
}
|
|
388
|
+
}
|
|
376
389
|
if (skippedPhases.length > 0) {
|
|
377
390
|
const existingSkipped = state.features[feature].skippedPhases || [];
|
|
378
391
|
state.features[feature].skippedPhases = [...new Set([...existingSkipped, ...skippedPhases])];
|
|
@@ -543,7 +556,7 @@ function designSystemGate(feature, projectPath = '.') {
|
|
|
543
556
|
'Create a design system with one of these options:',
|
|
544
557
|
' 1. Project-level: .morph/context/design-system.md (shared across features)',
|
|
545
558
|
` 2. Feature-level: ${getOutputPath(feature, 'uiDesignSystem')} (feature-specific)`,
|
|
546
|
-
' 3. Run /morph
|
|
559
|
+
' 3. Run /morph-phase-uiux skill to auto-generate from existing codebase',
|
|
547
560
|
'',
|
|
548
561
|
'Or remove UI agents if they are not needed:',
|
|
549
562
|
` morph-spec state remove-agent ${feature} blazor-builder`
|
|
@@ -54,7 +54,7 @@ const program = new Command();
|
|
|
54
54
|
/**
|
|
55
55
|
* Valid approval gate names
|
|
56
56
|
*/
|
|
57
|
-
const VALID_GATES = ['proposal', 'uiux', 'design', 'tasks'];
|
|
57
|
+
const VALID_GATES = ['proposal', 'uiux', 'design', 'plan', 'tasks'];
|
|
58
58
|
|
|
59
59
|
/**
|
|
60
60
|
* Approve action function - Mark a gate as approved
|
|
@@ -11,6 +11,24 @@ import { logger } from '../../utils/logger.js';
|
|
|
11
11
|
import * as StateManager from '../../core/state/state-manager.js';
|
|
12
12
|
import { derivePhase } from '../../core/state/state-manager.js';
|
|
13
13
|
|
|
14
|
+
// ============================================================================
|
|
15
|
+
// Phase Resolution
|
|
16
|
+
// ============================================================================
|
|
17
|
+
|
|
18
|
+
const PHASE_ORDER_LIST = ['setup', 'proposal', 'uiux', 'design', 'clarify', 'plan', 'tasks', 'implement', 'sync', 'archived'];
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Resolve the effective phase: pick whichever is further ahead between the
|
|
22
|
+
* stored phase (set by `phase advance`) and the derived phase (from filesystem).
|
|
23
|
+
*/
|
|
24
|
+
function resolvePhase(storedPhase, derivedPhase) {
|
|
25
|
+
if (!storedPhase) return derivedPhase;
|
|
26
|
+
if (!derivedPhase) return storedPhase;
|
|
27
|
+
const storedIdx = PHASE_ORDER_LIST.indexOf(storedPhase);
|
|
28
|
+
const derivedIdx = PHASE_ORDER_LIST.indexOf(derivedPhase);
|
|
29
|
+
return storedIdx > derivedIdx ? storedPhase : derivedPhase;
|
|
30
|
+
}
|
|
31
|
+
|
|
14
32
|
// ============================================================================
|
|
15
33
|
// Phase Protection
|
|
16
34
|
// ============================================================================
|
|
@@ -167,7 +185,9 @@ async function getCommand(featureName, options) {
|
|
|
167
185
|
logger.header(`Feature: ${featureName}`);
|
|
168
186
|
logger.blank();
|
|
169
187
|
const featurePath = join(process.cwd(), '.morph/features', featureName);
|
|
170
|
-
const
|
|
188
|
+
const derivedPhase = derivePhase(featurePath);
|
|
189
|
+
// Use stored phase when it's ahead of derived (directory may not exist yet)
|
|
190
|
+
const phase = resolvePhase(feature.phase, derivedPhase);
|
|
171
191
|
const tasks = feature.tasks || { completed: 0, total: 0 };
|
|
172
192
|
// Prefer taskList.length as total when tasks.total is stale/zero (Problem 3 complement)
|
|
173
193
|
const total = tasks.total || (Array.isArray(feature.taskList) ? feature.taskList.length : 0);
|
|
@@ -293,13 +313,14 @@ async function listCommand(options) {
|
|
|
293
313
|
}[feature.status] || '❓';
|
|
294
314
|
|
|
295
315
|
const tasks = feature.tasks || {};
|
|
296
|
-
const
|
|
297
|
-
|
|
298
|
-
|
|
316
|
+
const total = tasks.total || (Array.isArray(feature.taskList) ? feature.taskList.length : 0);
|
|
317
|
+
const progress = total > 0
|
|
318
|
+
? `${tasks.completed || 0}/${total}`
|
|
319
|
+
: '-';
|
|
299
320
|
|
|
300
321
|
logger.info(`${statusEmoji} ${chalk.bold(name)}`);
|
|
301
322
|
const featurePath = join(process.cwd(), '.morph/features', name);
|
|
302
|
-
const featurePhase = derivePhase(featurePath);
|
|
323
|
+
const featurePhase = resolvePhase(feature.phase, derivePhase(featurePath));
|
|
303
324
|
logger.dim(` Phase: ${featurePhase.padEnd(16)} │ Tasks: ${progress}`);
|
|
304
325
|
logger.dim(` Agents: ${(feature.activeAgents || []).slice(0, 5).join(', ') || 'None'}`);
|
|
305
326
|
logger.blank();
|
|
@@ -29,21 +29,28 @@ export function detectProject(targetPath, opts = {}) {
|
|
|
29
29
|
|
|
30
30
|
try {
|
|
31
31
|
// ── Package.json analysis ──────────────────────────────────────────────
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
// Collect deps from root and common monorepo subfolders
|
|
33
|
+
const pkgLocations = ['.', 'src/frontend', 'frontend', 'apps/web', 'packages/app'];
|
|
34
|
+
const allDeps = {};
|
|
35
|
+
let rootPkg = null;
|
|
36
|
+
let scripts = [];
|
|
37
|
+
for (const loc of pkgLocations) {
|
|
38
|
+
const pkgPath = join(targetPath, loc, 'package.json');
|
|
39
|
+
if (!existsSync(pkgPath)) continue;
|
|
35
40
|
try {
|
|
36
|
-
pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
41
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
42
|
+
if (loc === '.') rootPkg = pkg;
|
|
43
|
+
Object.assign(allDeps, pkg.dependencies, pkg.devDependencies);
|
|
44
|
+
scripts.push(...Object.keys(pkg.scripts || {}));
|
|
45
|
+
// Track frontend path for config
|
|
46
|
+
if (loc !== '.' && (pkg.dependencies?.next || pkg.dependencies?.react || pkg.dependencies?.vue)) {
|
|
47
|
+
structure.frontendPath = loc;
|
|
48
|
+
}
|
|
37
49
|
} catch { /* invalid JSON */ }
|
|
38
50
|
}
|
|
51
|
+
const depNames = Object.keys(allDeps);
|
|
39
52
|
|
|
40
|
-
if (
|
|
41
|
-
const allDeps = {
|
|
42
|
-
...pkg.dependencies,
|
|
43
|
-
...pkg.devDependencies,
|
|
44
|
-
};
|
|
45
|
-
const depNames = Object.keys(allDeps);
|
|
46
|
-
const scripts = Object.keys(pkg.scripts || {});
|
|
53
|
+
if (depNames.length > 0) {
|
|
47
54
|
|
|
48
55
|
// Stack detection from deps
|
|
49
56
|
if (depNames.includes('next')) {
|
|
@@ -54,12 +61,12 @@ export function detectProject(targetPath, opts = {}) {
|
|
|
54
61
|
structure.stack = 'vue';
|
|
55
62
|
} else if (depNames.includes('express') || depNames.includes('fastify') || depNames.includes('hono')) {
|
|
56
63
|
structure.stack = 'nodejs';
|
|
57
|
-
} else if (
|
|
64
|
+
} else if (rootPkg?.name) {
|
|
58
65
|
structure.stack = 'nodejs';
|
|
59
66
|
}
|
|
60
67
|
|
|
61
68
|
// UI library detection
|
|
62
|
-
if (existsSync(join(targetPath, 'components.json')) || depNames.includes('@shadcn/ui')) {
|
|
69
|
+
if (existsSync(join(targetPath, 'components.json')) || existsSync(join(targetPath, structure.frontendPath || '.', 'components.json')) || depNames.includes('@shadcn/ui')) {
|
|
63
70
|
structure.uiLibrary = 'shadcn';
|
|
64
71
|
} else if (depNames.some(d => d.includes('mudblazor'))) {
|
|
65
72
|
structure.uiLibrary = 'mudblazor';
|
|
@@ -79,8 +86,7 @@ export function detectProject(targetPath, opts = {}) {
|
|
|
79
86
|
}
|
|
80
87
|
|
|
81
88
|
// Vercel detection
|
|
82
|
-
if (depNames.includes('vercel') || scripts.includes('vercel')
|
|
83
|
-
scripts.some(s => (pkg.scripts[s] || '').includes('vercel'))) {
|
|
89
|
+
if (depNames.includes('vercel') || scripts.includes('vercel')) {
|
|
84
90
|
structure.hasVercel = true;
|
|
85
91
|
}
|
|
86
92
|
}
|
|
@@ -89,22 +95,34 @@ export function detectProject(targetPath, opts = {}) {
|
|
|
89
95
|
let hasDotnet = false;
|
|
90
96
|
let hasBlazer = false;
|
|
91
97
|
const csprojLocations = ['.', 'src', 'src/Api', 'src/Web', 'src/backend'];
|
|
98
|
+
|
|
99
|
+
function checkCsproj(filePath) {
|
|
100
|
+
hasDotnet = true;
|
|
101
|
+
try {
|
|
102
|
+
const content = readFileSync(filePath, 'utf8');
|
|
103
|
+
if (content.includes('Microsoft.AspNetCore.Components') || content.includes('Blazor')) {
|
|
104
|
+
hasBlazer = true;
|
|
105
|
+
}
|
|
106
|
+
if (content.includes('Hangfire')) {
|
|
107
|
+
if (!structure.integrations.includes('hangfire')) structure.integrations.push('hangfire');
|
|
108
|
+
}
|
|
109
|
+
} catch { /* ignore */ }
|
|
110
|
+
}
|
|
111
|
+
|
|
92
112
|
for (const loc of csprojLocations) {
|
|
93
113
|
try {
|
|
94
114
|
const dir = join(targetPath, loc);
|
|
95
|
-
if (existsSync(dir))
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
} catch { /* ignore */ }
|
|
115
|
+
if (!existsSync(dir)) continue;
|
|
116
|
+
const entries = readdirSyncSafe(dir);
|
|
117
|
+
// Direct .csproj files
|
|
118
|
+
for (const e of entries.filter(f => f.endsWith('.csproj'))) {
|
|
119
|
+
checkCsproj(join(dir, e));
|
|
120
|
+
}
|
|
121
|
+
// One level deeper (e.g., src/backend/MyProject/*.csproj)
|
|
122
|
+
for (const sub of entries) {
|
|
123
|
+
const subDir = join(dir, sub);
|
|
124
|
+
for (const e of readdirSyncSafe(subDir).filter(f => f.endsWith('.csproj'))) {
|
|
125
|
+
checkCsproj(join(subDir, e));
|
|
108
126
|
}
|
|
109
127
|
}
|
|
110
128
|
} catch { /* ignore */ }
|
|
@@ -99,7 +99,12 @@ const OUTPUT_PHASE_MAP = {
|
|
|
99
99
|
function checkOutput(featurePath, outputFile) {
|
|
100
100
|
const phaseDir = OUTPUT_PHASE_MAP[outputFile] || '';
|
|
101
101
|
const filePath = path.join(featurePath, phaseDir, outputFile);
|
|
102
|
-
|
|
102
|
+
if (fs.existsSync(filePath)) return true;
|
|
103
|
+
// Fallback: check feature root (some generators place files there instead of phase subdirs)
|
|
104
|
+
if (phaseDir) {
|
|
105
|
+
return fs.existsSync(path.join(featurePath, outputFile));
|
|
106
|
+
}
|
|
107
|
+
return false;
|
|
103
108
|
}
|
|
104
109
|
|
|
105
110
|
/**
|
|
@@ -86,6 +86,10 @@ export async function ensureTaskList(feature, featureName, cwd = process.cwd())
|
|
|
86
86
|
*/
|
|
87
87
|
export function syncCounters(feature) {
|
|
88
88
|
if (Array.isArray(feature.tasks)) return; // v2 format — nothing to sync
|
|
89
|
+
// Safety: ensure feature.tasks is an object (may be undefined after migration or corruption)
|
|
90
|
+
if (!feature.tasks || typeof feature.tasks !== 'object') {
|
|
91
|
+
feature.tasks = { total: 0, completed: 0, inProgress: 0, pending: 0 };
|
|
92
|
+
}
|
|
89
93
|
const list = feature.taskList || [];
|
|
90
94
|
feature.tasks.total = list.length;
|
|
91
95
|
feature.tasks.completed = list.filter(t => t.status === 'completed').length;
|
|
@@ -141,7 +141,7 @@ export async function setupInfra(targetPath, { _exec = execSync } = {}) {
|
|
|
141
141
|
const contextReadmePath = join(contextDir, 'README.md');
|
|
142
142
|
if (!(await pathExists(contextReadmePath))) {
|
|
143
143
|
const dirName = targetPath.split(/[\/]/).pop();
|
|
144
|
-
const readmeContent = `# ${dirName} — Project Context\n\nRun /morph
|
|
144
|
+
const readmeContent = `# ${dirName} — Project Context\n\nRun /morph-init to generate project context.\n`;
|
|
145
145
|
await writeFile(contextReadmePath, readmeContent);
|
|
146
146
|
}
|
|
147
147
|
|
|
@@ -39,9 +39,9 @@ export async function installAgents(projectDir, frameworkDir = 'framework', opti
|
|
|
39
39
|
mkdirSync(targetDir, { recursive: true });
|
|
40
40
|
|
|
41
41
|
const eligible = agents.filter(a => {
|
|
42
|
-
if (a.tier
|
|
43
|
-
//
|
|
44
|
-
if (a.tier
|
|
42
|
+
if (a.tier < 1 || a.tier > 4) return false;
|
|
43
|
+
// Must have a teammate with spawn_prompt (except tier-1 orchestrators which always have one)
|
|
44
|
+
if (a.tier >= 3 && !a.teammate?.spawn_prompt) return false;
|
|
45
45
|
// Filter by stacks if the agent declares them
|
|
46
46
|
const stacks = a.stacks ?? [];
|
|
47
47
|
if (!shouldInstall(stacks, projectTags)) return false;
|
|
@@ -50,13 +50,14 @@ export async function installAgents(projectDir, frameworkDir = 'framework', opti
|
|
|
50
50
|
|
|
51
51
|
let tier1 = 0;
|
|
52
52
|
let tier2 = 0;
|
|
53
|
+
let tier3 = 0;
|
|
53
54
|
let tier4 = 0;
|
|
54
55
|
const installed = new Set();
|
|
55
56
|
|
|
56
57
|
// Build the full set of possible filenames for orphan cleanup
|
|
57
58
|
const allPossible = new Set();
|
|
58
59
|
for (const a of agents) {
|
|
59
|
-
if (a.tier
|
|
60
|
+
if (a.tier < 1 || a.tier > 4) continue;
|
|
60
61
|
const slug = a.id ?? a.name?.toLowerCase().replace(/\s+/g, '-');
|
|
61
62
|
const prefix = a.tier === 4 ? 'morph-validator-' : 'morph-';
|
|
62
63
|
allPossible.add(`${prefix}${slug}.md`);
|
|
@@ -78,6 +79,7 @@ export async function installAgents(projectDir, frameworkDir = 'framework', opti
|
|
|
78
79
|
|
|
79
80
|
if (agent.tier === 1) tier1++;
|
|
80
81
|
else if (agent.tier === 2) tier2++;
|
|
82
|
+
else if (agent.tier === 3) tier3++;
|
|
81
83
|
else if (agent.tier === 4) tier4++;
|
|
82
84
|
}
|
|
83
85
|
|
|
@@ -88,7 +90,7 @@ export async function installAgents(projectDir, frameworkDir = 'framework', opti
|
|
|
88
90
|
}
|
|
89
91
|
}
|
|
90
92
|
|
|
91
|
-
return { tier1, tier2, tier4 };
|
|
93
|
+
return { tier1, tier2, tier3, tier4 };
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
/**
|
|
@@ -100,16 +102,23 @@ export async function installAgents(projectDir, frameworkDir = 'framework', opti
|
|
|
100
102
|
const TIER_DEFAULTS = {
|
|
101
103
|
1: {
|
|
102
104
|
model: 'inherit',
|
|
103
|
-
tools: 'Read, Grep, Glob, Bash, Task',
|
|
105
|
+
tools: 'Read, Grep, Glob, Bash, Task, Write, Edit',
|
|
104
106
|
maxTurns: 30,
|
|
105
|
-
skills: ['morph
|
|
107
|
+
skills: ['morph-checklist'],
|
|
106
108
|
memory: 'project',
|
|
107
109
|
},
|
|
108
110
|
2: {
|
|
109
111
|
model: 'inherit',
|
|
110
|
-
tools: 'Read,
|
|
112
|
+
tools: 'Read, Write, Edit, Bash, Glob, Grep',
|
|
111
113
|
maxTurns: 20,
|
|
112
|
-
skills: ['morph
|
|
114
|
+
skills: ['morph-checklist'],
|
|
115
|
+
memory: 'local',
|
|
116
|
+
},
|
|
117
|
+
3: {
|
|
118
|
+
model: 'sonnet',
|
|
119
|
+
tools: 'Read, Write, Edit, Bash, Glob, Grep',
|
|
120
|
+
maxTurns: 15,
|
|
121
|
+
skills: [],
|
|
113
122
|
memory: 'local',
|
|
114
123
|
},
|
|
115
124
|
4: {
|
|
@@ -309,8 +318,8 @@ export function checkMissingAgents(projectDir, frameworkDir, projectTags = []) {
|
|
|
309
318
|
.map(([id, data]) => ({ id, ...data }));
|
|
310
319
|
|
|
311
320
|
for (const a of agents) {
|
|
312
|
-
if (a.tier
|
|
313
|
-
if (a.tier
|
|
321
|
+
if (a.tier < 1 || a.tier > 4) continue;
|
|
322
|
+
if (a.tier >= 3 && !a.teammate?.spawn_prompt) continue;
|
|
314
323
|
const stacks = a.stacks ?? [];
|
|
315
324
|
if (!shouldInstall(stacks, projectTags)) continue;
|
|
316
325
|
|
package/src/utils/banner.js
CHANGED
|
@@ -45,7 +45,7 @@ export function printInstallBanner({ version, nodeVersion, results = [] }) {
|
|
|
45
45
|
'',
|
|
46
46
|
sep('Next'),
|
|
47
47
|
` ${chalk.cyan('1.')} Open your project in Claude Code`,
|
|
48
|
-
` ${chalk.cyan('2.')} Run ${chalk.bold.white('/morph
|
|
48
|
+
` ${chalk.cyan('2.')} Run ${chalk.bold.white('/morph-init')}`,
|
|
49
49
|
'',
|
|
50
50
|
].join('\n'));
|
|
51
51
|
}
|