@polymorphism-tech/morph-spec 4.18.0 → 4.20.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 +11 -0
- package/bin/task-manager.js +47 -10
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/agents/architecture/architecture-expert.md +98 -0
- package/framework/agents/architecture/morph-spec-architect.md +66 -0
- package/framework/agents/architecture/packages-validator.md +89 -0
- package/framework/agents/architecture/security-expert.md +79 -0
- package/framework/agents/backend/ms-agent-expert.md +1 -1
- package/framework/agents/frontend/design-system-validator.md +100 -0
- package/framework/agents/frontend/nextjs-component-validator.md +97 -0
- package/framework/agents/frontend/{ui-ux-designer.md → ui-designer.md} +31 -0
- package/framework/agents.json +1 -1
- package/framework/hooks/claude-code/post-tool-use/dispatch.js +56 -0
- package/framework/hooks/claude-code/post-tool-use/handle-tool-failure.js +78 -44
- package/framework/hooks/claude-code/post-tool-use/loop-detect.js +103 -0
- package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +60 -11
- package/framework/hooks/claude-code/session-start/inject-morph-context.js +21 -2
- package/framework/hooks/claude-code/stop/validate-completion.js +48 -1
- package/framework/hooks/shared/activity-logger.js +56 -1
- package/framework/hooks/shared/build-check.js +79 -0
- package/framework/hooks/shared/payload-utils.js +39 -0
- package/framework/hooks/shared/session-snapshot.js +105 -0
- package/framework/phases.json +1 -1
- package/framework/rubrics/backend.json +36 -0
- package/framework/rubrics/default.json +36 -0
- package/framework/rubrics/frontend.json +36 -0
- package/framework/skills/level-0-meta/mcp-registry.json +34 -1
- package/framework/skills/level-0-meta/morph-frontend-review/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +11 -1
- package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +12 -1
- package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +119 -11
- package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +1 -1
- package/framework/standards/ai-agents/production.md +5 -2
- package/framework/standards/ai-agents/setup.md +13 -14
- package/framework/standards/ai-agents/workflows.md +8 -6
- package/framework/standards/backend/database/vector-search-rag.md +1 -1
- package/framework/templates/code/dotnet/jobs/agent.cs +0 -1
- package/package.json +1 -1
- package/src/commands/dashboard/dashboard.js +53 -0
- package/src/commands/mcp/mcp-setup.js +21 -0
- package/src/commands/project/init.js +11 -0
- package/src/dashboard/app/App.jsx +31 -0
- package/src/dashboard/app/components/DetailPanel.jsx +11 -0
- package/src/dashboard/app/components/FlowGraph.jsx +17 -0
- package/src/dashboard/app/components/SearchFilter.jsx +22 -0
- package/src/dashboard/app/components/Sidebar.jsx +45 -0
- package/src/dashboard/app/hooks/useDetailPanel.js +15 -0
- package/src/dashboard/app/main.jsx +13 -0
- package/src/dashboard/app/pages/AgentMap.jsx +371 -0
- package/src/dashboard/app/pages/HookInspector.jsx +418 -0
- package/src/dashboard/app/pages/LiveState.jsx +239 -0
- package/src/dashboard/app/pages/PhasePipeline.jsx +410 -0
- package/src/dashboard/app/pages/RulesMap.jsx +273 -0
- package/src/dashboard/app/pages/SkillsLibrary.jsx +367 -0
- package/src/dashboard/app/pages/Standards.jsx +332 -0
- package/src/dashboard/app/pages/Workflows.jsx +346 -0
- package/src/dashboard/app/styles/index.css +47 -0
- package/src/dashboard/index.html +12 -0
- package/src/dashboard/package.json +24 -0
- package/src/dashboard/scanner/agents-scanner.js +35 -0
- package/src/dashboard/scanner/hooks-scanner.js +41 -0
- package/src/dashboard/scanner/index.js +21 -0
- package/src/dashboard/scanner/phases-scanner.js +15 -0
- package/src/dashboard/scanner/rubrics-scanner.js +12 -0
- package/src/dashboard/scanner/rules-scanner.js +37 -0
- package/src/dashboard/scanner/skills-scanner.js +43 -0
- package/src/dashboard/scanner/squads-scanner.js +13 -0
- package/src/dashboard/scanner/standards-scanner.js +26 -0
- package/src/dashboard/scanner/state-scanner.js +8 -0
- package/src/dashboard/scanner/workflows-scanner.js +18 -0
- package/src/dashboard/vite-plugin-morph-data.js +17 -0
- package/src/dashboard/vite.config.js +24 -0
- package/src/lib/checkpoints/checkpoint-hooks.js +2 -1
- package/src/lib/contracts/sprint-contract-generator.js +137 -0
- package/src/lib/generators/recap-generator.js +15 -0
- package/src/lib/installers/mcp-installer.js +25 -0
- package/src/lib/validators/score-aggregator.js +244 -0
- package/src/lib/validators/validation-runner.js +44 -0
- package/src/scripts/setup-infra.js +8 -0
- package/src/utils/hooks-installer.js +12 -1
|
@@ -20,11 +20,13 @@
|
|
|
20
20
|
|
|
21
21
|
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
|
22
22
|
import { join } from 'path';
|
|
23
|
+
import { runBuildCheck } from '../../shared/build-check.js';
|
|
24
|
+
import { saveSessionSnapshot } from '../../shared/session-snapshot.js';
|
|
23
25
|
import {
|
|
24
26
|
stateExists, getActiveFeature, getMostRecentFeature, getMissingOutputs, derivePhaseForFeature,
|
|
25
27
|
} from '../../shared/state-reader.js';
|
|
26
28
|
import { injectContext, pass } from '../../shared/hook-response.js';
|
|
27
|
-
import { logHookActivity } from '../../shared/activity-logger.js';
|
|
29
|
+
import { logHookActivity, logEvent } from '../../shared/activity-logger.js';
|
|
28
30
|
|
|
29
31
|
// Phases where compaction is strongly recommended before starting
|
|
30
32
|
const COMPACT_TRIGGER_PHASES = new Set(['implement']);
|
|
@@ -82,6 +84,50 @@ try {
|
|
|
82
84
|
);
|
|
83
85
|
}
|
|
84
86
|
|
|
87
|
+
// ── Build/typecheck verification ──────────────────────────────────────────
|
|
88
|
+
// Stack-aware build check — advisory warning if build fails.
|
|
89
|
+
// Success is silent (no output on pass).
|
|
90
|
+
try {
|
|
91
|
+
const configPath = join(cwd, '.morph', 'config', 'config.json');
|
|
92
|
+
if (existsSync(configPath)) {
|
|
93
|
+
const config = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
94
|
+
const stack = config?.project?.stack;
|
|
95
|
+
if (stack && currentPhase === 'implement') {
|
|
96
|
+
const buildResult = runBuildCheck(cwd, stack);
|
|
97
|
+
if (buildResult.status === 'fail') {
|
|
98
|
+
warnings.push('⚠ Build/typecheck failed:');
|
|
99
|
+
warnings.push(buildResult.output);
|
|
100
|
+
warnings.push('Fix build errors before ending session.');
|
|
101
|
+
}
|
|
102
|
+
// pass and skip → silent (success is silent)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
} catch {
|
|
106
|
+
// Fail-open — build check must never block Stop
|
|
107
|
+
}
|
|
108
|
+
// ────────────────────────────────────────────────────────────────────────
|
|
109
|
+
|
|
110
|
+
// ── Session snapshot ──────────────────────────────────────────────────────
|
|
111
|
+
// Save session state for next session's "getting up to speed" routine.
|
|
112
|
+
try {
|
|
113
|
+
if (active) {
|
|
114
|
+
const { name, feature } = active;
|
|
115
|
+
const taskList = feature.taskList || [];
|
|
116
|
+
saveSessionSnapshot({
|
|
117
|
+
feature: name,
|
|
118
|
+
phase: currentPhase,
|
|
119
|
+
tasksCompleted: taskList.filter(t => t.status === 'completed').map(t => t.id),
|
|
120
|
+
tasksRemaining: taskList.filter(t => t.status === 'pending').map(t => t.id),
|
|
121
|
+
tasksInProgress: taskList.filter(t => t.status === 'in_progress').map(t => t.id),
|
|
122
|
+
buildStatus: 'skip',
|
|
123
|
+
lastEditedFiles: [],
|
|
124
|
+
}, cwd);
|
|
125
|
+
}
|
|
126
|
+
} catch {
|
|
127
|
+
// Fail-open — snapshot save must never block Stop
|
|
128
|
+
}
|
|
129
|
+
// ────────────────────────────────────────────────────────────────────────
|
|
130
|
+
|
|
85
131
|
// ── Incomplete work checks ────────────────────────────────────────────────
|
|
86
132
|
|
|
87
133
|
// Incomplete tasks during implement phase
|
|
@@ -152,6 +198,7 @@ try {
|
|
|
152
198
|
].join('\n');
|
|
153
199
|
|
|
154
200
|
logHookActivity('validate-completion', 'Stop', `warnings(${warnings.length})`);
|
|
201
|
+
logEvent('session_end', { feature: name, phase: currentPhase, warnings: warnings.length });
|
|
155
202
|
injectContext(message);
|
|
156
203
|
} catch {
|
|
157
204
|
process.exit(0);
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* - Windows-safe paths via path.join
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
14
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, unlinkSync } from 'fs';
|
|
15
15
|
import { join } from 'path';
|
|
16
16
|
|
|
17
17
|
const LOGS_DIR = '.morph/logs';
|
|
@@ -103,3 +103,58 @@ export function resetActivity(sessionId, feature, phase, projectPath) {
|
|
|
103
103
|
// Fail-silent
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Log a structured event to the session activity log.
|
|
109
|
+
*
|
|
110
|
+
* @param {string} type - Event type (e.g., 'task_start', 'loop_detected')
|
|
111
|
+
* @param {Object} data - Event data payload
|
|
112
|
+
* @param {string} [projectPath] - Project root (defaults to cwd)
|
|
113
|
+
*/
|
|
114
|
+
export function logEvent(type, data, projectPath) {
|
|
115
|
+
try {
|
|
116
|
+
const activityPath = getActivityPath(projectPath);
|
|
117
|
+
ensureLogsDir(projectPath);
|
|
118
|
+
|
|
119
|
+
const now = new Date();
|
|
120
|
+
const ts = now.toTimeString().slice(0, 8); // HH:MM:SS
|
|
121
|
+
|
|
122
|
+
const activity = readRaw(activityPath) || { sessionId: '', feature: '', phase: '', hooks: [], events: [] };
|
|
123
|
+
activity.events = activity.events || [];
|
|
124
|
+
activity.events.push({ type, data, ts });
|
|
125
|
+
|
|
126
|
+
writeFileSync(activityPath, JSON.stringify(activity, null, 2), 'utf-8');
|
|
127
|
+
} catch {
|
|
128
|
+
// Fail-silent
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Archive current activity.json to sessions/ directory for historical logging.
|
|
134
|
+
* Called at the start of a new session.
|
|
135
|
+
*
|
|
136
|
+
* @param {string} [projectPath] - Project root (defaults to cwd)
|
|
137
|
+
*/
|
|
138
|
+
export function archiveSession(projectPath) {
|
|
139
|
+
try {
|
|
140
|
+
const activityPath = getActivityPath(projectPath);
|
|
141
|
+
if (!existsSync(activityPath)) return;
|
|
142
|
+
|
|
143
|
+
const data = readRaw(activityPath);
|
|
144
|
+
if (!data) return;
|
|
145
|
+
|
|
146
|
+
// Only archive if there are meaningful events
|
|
147
|
+
if ((!data.hooks || data.hooks.length === 0) && (!data.events || data.events.length === 0)) return;
|
|
148
|
+
|
|
149
|
+
const sessionsDir = join(projectPath || process.cwd(), LOGS_DIR, 'sessions');
|
|
150
|
+
mkdirSync(sessionsDir, { recursive: true });
|
|
151
|
+
|
|
152
|
+
const sessionId = (data.sessionId || new Date().toISOString()).replace(/[:.]/g, '-');
|
|
153
|
+
const archivePath = join(sessionsDir, `${sessionId}.json`);
|
|
154
|
+
writeFileSync(archivePath, JSON.stringify(data, null, 2), 'utf-8');
|
|
155
|
+
|
|
156
|
+
unlinkSync(activityPath);
|
|
157
|
+
} catch {
|
|
158
|
+
// Fail-silent
|
|
159
|
+
}
|
|
160
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build/Typecheck verification utility.
|
|
3
|
+
*
|
|
4
|
+
* Stack-aware: determines the correct build command based on project stack.
|
|
5
|
+
* Used by the Stop hook to verify code health before session ends.
|
|
6
|
+
*
|
|
7
|
+
* Pure functions — no I/O except the actual build execution.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { execSync } from 'child_process';
|
|
11
|
+
import { existsSync } from 'fs';
|
|
12
|
+
import { join } from 'path';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Determine the build command for a given stack.
|
|
16
|
+
*
|
|
17
|
+
* @param {string} stack - Project stack (e.g., 'dotnet-nextjs', 'nextjs')
|
|
18
|
+
* @param {boolean} [hasTsConfig] - Whether tsconfig.json exists (for TS projects)
|
|
19
|
+
* @returns {string|null} Build command or null if no check available
|
|
20
|
+
*/
|
|
21
|
+
export function getBuildCommand(stack, hasTsConfig = false) {
|
|
22
|
+
if (!stack) return null;
|
|
23
|
+
const s = stack.toLowerCase();
|
|
24
|
+
|
|
25
|
+
if (s.includes('dotnet')) {
|
|
26
|
+
return 'dotnet build --no-restore --nologo -v q 2>&1 | tail -20';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if ((s.includes('nextjs') || s.includes('node') || s.includes('react')) && hasTsConfig) {
|
|
30
|
+
return 'npx tsc --noEmit 2>&1 | tail -20';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Parse build command result into a structured object.
|
|
38
|
+
*
|
|
39
|
+
* @param {number} exitCode - Process exit code (0 = success)
|
|
40
|
+
* @param {string} output - stdout+stderr from build
|
|
41
|
+
* @returns {{ status: 'pass'|'fail', output: string }}
|
|
42
|
+
*/
|
|
43
|
+
export function parseBuildResult(exitCode, output) {
|
|
44
|
+
const trimmed = (output || '').trim();
|
|
45
|
+
if (exitCode === 0) {
|
|
46
|
+
return { status: 'pass', output: '' };
|
|
47
|
+
}
|
|
48
|
+
// Truncate to last 20 lines
|
|
49
|
+
const lines = trimmed.split('\n');
|
|
50
|
+
const truncated = lines.slice(-20).join('\n');
|
|
51
|
+
return { status: 'fail', output: truncated };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Run build check for a project.
|
|
56
|
+
*
|
|
57
|
+
* @param {string} cwd - Project root directory
|
|
58
|
+
* @param {string} stack - Project stack string
|
|
59
|
+
* @param {number} [timeout=30000] - Timeout in ms
|
|
60
|
+
* @returns {{ status: 'pass'|'fail'|'skip', output: string }}
|
|
61
|
+
*/
|
|
62
|
+
export function runBuildCheck(cwd, stack, timeout = 30000) {
|
|
63
|
+
const hasTsConfig = existsSync(join(cwd, 'tsconfig.json'));
|
|
64
|
+
const cmd = getBuildCommand(stack, hasTsConfig);
|
|
65
|
+
if (!cmd) return { status: 'skip', output: '' };
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
const output = execSync(cmd, {
|
|
69
|
+
cwd,
|
|
70
|
+
encoding: 'utf-8',
|
|
71
|
+
timeout,
|
|
72
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
73
|
+
});
|
|
74
|
+
return parseBuildResult(0, output);
|
|
75
|
+
} catch (err) {
|
|
76
|
+
const output = (err.stdout || '') + (err.stderr || '');
|
|
77
|
+
return parseBuildResult(err.status || 1, output);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -37,3 +37,42 @@ export function getContentToValidate(payload) {
|
|
|
37
37
|
export function isWriteOperation(payload) {
|
|
38
38
|
return typeof payload?.tool_input?.content === 'string';
|
|
39
39
|
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Get the tool name from a hook payload.
|
|
43
|
+
* @param {Object} payload - Hook payload from stdin
|
|
44
|
+
* @returns {string} Tool name or 'unknown'
|
|
45
|
+
*/
|
|
46
|
+
export function getToolName(payload) {
|
|
47
|
+
return payload?.tool_name || 'unknown';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Build a normalized signature for loop detection.
|
|
52
|
+
* - Edit/Write: "Edit:normalized/file/path"
|
|
53
|
+
* - Bash: "Bash:first two words" (e.g., "Bash:npm test")
|
|
54
|
+
* - Other: just the tool name
|
|
55
|
+
*
|
|
56
|
+
* @param {Object} payload - Hook payload from stdin
|
|
57
|
+
* @returns {string} Signature string
|
|
58
|
+
*/
|
|
59
|
+
export function getCommandSignature(payload) {
|
|
60
|
+
const tool = getToolName(payload);
|
|
61
|
+
if (tool === 'unknown') return 'unknown';
|
|
62
|
+
|
|
63
|
+
const input = payload?.tool_input;
|
|
64
|
+
if (!input) return tool;
|
|
65
|
+
|
|
66
|
+
if (tool === 'Edit' || tool === 'Write') {
|
|
67
|
+
const path = (input.file_path || input.path || '').replace(/\\/g, '/');
|
|
68
|
+
return path ? `${tool}:${path}` : tool;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (tool === 'Bash') {
|
|
72
|
+
const cmd = (input.command || '').trim();
|
|
73
|
+
const words = cmd.split(/\s+/).slice(0, 2).join(' ');
|
|
74
|
+
return words ? `Bash:${words}` : 'Bash';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return tool;
|
|
78
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Snapshot — cross-session persistence utility
|
|
3
|
+
*
|
|
4
|
+
* Saves and loads session state for "getting up to speed" on next session.
|
|
5
|
+
* Single file (overwrites) — history preserved via git.
|
|
6
|
+
*
|
|
7
|
+
* Fail-silent: never throws.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, renameSync, unlinkSync } from 'fs';
|
|
11
|
+
import { join } from 'path';
|
|
12
|
+
|
|
13
|
+
const SNAPSHOT_PATH = '.morph/memory/session-snapshot.json';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Save session snapshot atomically.
|
|
17
|
+
* @param {Object} data - Snapshot data
|
|
18
|
+
* @param {string} [projectPath] - Project root (defaults to cwd)
|
|
19
|
+
*/
|
|
20
|
+
export function saveSessionSnapshot(data, projectPath) {
|
|
21
|
+
try {
|
|
22
|
+
const root = projectPath || process.cwd();
|
|
23
|
+
const dir = join(root, '.morph', 'memory');
|
|
24
|
+
mkdirSync(dir, { recursive: true });
|
|
25
|
+
|
|
26
|
+
const snapshot = { ...data, savedAt: new Date().toISOString() };
|
|
27
|
+
const filePath = join(root, SNAPSHOT_PATH);
|
|
28
|
+
const tmpPath = `${filePath}.tmp.${process.pid}`;
|
|
29
|
+
|
|
30
|
+
writeFileSync(tmpPath, JSON.stringify(snapshot, null, 2), 'utf-8');
|
|
31
|
+
try {
|
|
32
|
+
renameSync(tmpPath, filePath);
|
|
33
|
+
} catch {
|
|
34
|
+
writeFileSync(filePath, JSON.stringify(snapshot, null, 2), 'utf-8');
|
|
35
|
+
try { unlinkSync(tmpPath); } catch { /* ignore */ }
|
|
36
|
+
}
|
|
37
|
+
} catch {
|
|
38
|
+
// Fail-silent
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Load session snapshot.
|
|
44
|
+
* @param {string} [projectPath] - Project root (defaults to cwd)
|
|
45
|
+
* @returns {Object|null} Parsed snapshot or null
|
|
46
|
+
*/
|
|
47
|
+
export function loadSessionSnapshot(projectPath) {
|
|
48
|
+
try {
|
|
49
|
+
const filePath = join(projectPath || process.cwd(), SNAPSHOT_PATH);
|
|
50
|
+
if (!existsSync(filePath)) return null;
|
|
51
|
+
return JSON.parse(readFileSync(filePath, 'utf-8'));
|
|
52
|
+
} catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Format snapshot as concise context string for SessionStart injection.
|
|
59
|
+
* Budget: max ~150 tokens (~600 chars).
|
|
60
|
+
*
|
|
61
|
+
* @param {Object|null} snapshot
|
|
62
|
+
* @returns {string|null} Formatted context or null
|
|
63
|
+
*/
|
|
64
|
+
export function formatPreviousSession(snapshot) {
|
|
65
|
+
if (!snapshot) return null;
|
|
66
|
+
|
|
67
|
+
const lines = ['=== PREVIOUS SESSION ==='];
|
|
68
|
+
|
|
69
|
+
// Header line
|
|
70
|
+
const date = snapshot.savedAt ? new Date(snapshot.savedAt).toISOString().slice(0, 16).replace('T', ' ') : '?';
|
|
71
|
+
const phase = snapshot.phase || '?';
|
|
72
|
+
const build = snapshot.buildStatus || 'unknown';
|
|
73
|
+
lines.push(`Last: ${date} | Phase: ${phase} | Build: ${build}`);
|
|
74
|
+
|
|
75
|
+
// Tasks (truncated)
|
|
76
|
+
const done = snapshot.tasksCompleted || [];
|
|
77
|
+
if (done.length > 0) {
|
|
78
|
+
const shown = done.slice(-5).join(', ');
|
|
79
|
+
const prefix = done.length > 5 ? `...${done.length} total, last: ` : '';
|
|
80
|
+
lines.push(`Done: ${prefix}${shown}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const wip = snapshot.tasksInProgress || [];
|
|
84
|
+
if (wip.length > 0) {
|
|
85
|
+
lines.push(`WIP: ${wip.join(', ')}`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const remaining = snapshot.tasksRemaining || [];
|
|
89
|
+
if (remaining.length > 0) {
|
|
90
|
+
const shown = remaining.slice(0, 5).join(', ');
|
|
91
|
+
const suffix = remaining.length > 5 ? ` +${remaining.length - 5} more` : '';
|
|
92
|
+
lines.push(`Remaining: ${shown}${suffix}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Files (truncated)
|
|
96
|
+
const files = snapshot.lastEditedFiles || [];
|
|
97
|
+
if (files.length > 0) {
|
|
98
|
+
const shown = files.slice(0, 3).join(', ');
|
|
99
|
+
const suffix = files.length > 3 ? ` +${files.length - 3} more` : '';
|
|
100
|
+
lines.push(`Files: ${shown}${suffix}`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
lines.push('===');
|
|
104
|
+
return lines.join('\n');
|
|
105
|
+
}
|
package/framework/phases.json
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
],
|
|
36
36
|
"requiredOutputs": ["uiDesignSystem"],
|
|
37
37
|
"optionalOutputs": ["uiMockups", "uiComponents", "uiFlows"],
|
|
38
|
-
"recommendedMCPs": ["playwright", "context7"],
|
|
38
|
+
"recommendedMCPs": ["playwright", "context7", "stitch"],
|
|
39
39
|
"pausePoints": [
|
|
40
40
|
{ "id": "uiux", "label": "Approve UI/UX Design" }
|
|
41
41
|
],
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "backend",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Backend-focused rubric. Higher weight on architecture, security, and package health.",
|
|
5
|
+
"categories": {
|
|
6
|
+
"functionality": {
|
|
7
|
+
"weight": 0.30,
|
|
8
|
+
"validators": ["contract-compliance"],
|
|
9
|
+
"description": "Core feature correctness and contract compliance"
|
|
10
|
+
},
|
|
11
|
+
"code_quality": {
|
|
12
|
+
"weight": 0.30,
|
|
13
|
+
"validators": ["architecture"],
|
|
14
|
+
"description": "Architecture patterns, dependency direction, and code structure"
|
|
15
|
+
},
|
|
16
|
+
"design_ui": {
|
|
17
|
+
"weight": 0.05,
|
|
18
|
+
"validators": ["design-system", "css", "contrast", "nextjs-component"],
|
|
19
|
+
"description": "Design system compliance (minimal weight for backend projects)"
|
|
20
|
+
},
|
|
21
|
+
"security": {
|
|
22
|
+
"weight": 0.15,
|
|
23
|
+
"validators": [],
|
|
24
|
+
"description": "Security posture (agent-evaluated, no automated validator)"
|
|
25
|
+
},
|
|
26
|
+
"packages": {
|
|
27
|
+
"weight": 0.20,
|
|
28
|
+
"validators": ["packages"],
|
|
29
|
+
"description": "Dependency health, version compatibility, and deprecated packages"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"thresholds": {
|
|
33
|
+
"approve": 8.0,
|
|
34
|
+
"retry": 6.0
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "default",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Default rubric for general-purpose projects. Balanced weights across all categories.",
|
|
5
|
+
"categories": {
|
|
6
|
+
"functionality": {
|
|
7
|
+
"weight": 0.30,
|
|
8
|
+
"validators": ["contract-compliance"],
|
|
9
|
+
"description": "Core feature correctness and contract compliance"
|
|
10
|
+
},
|
|
11
|
+
"code_quality": {
|
|
12
|
+
"weight": 0.25,
|
|
13
|
+
"validators": ["architecture"],
|
|
14
|
+
"description": "Architecture patterns and code structure"
|
|
15
|
+
},
|
|
16
|
+
"design_ui": {
|
|
17
|
+
"weight": 0.15,
|
|
18
|
+
"validators": ["design-system", "css", "contrast", "nextjs-component"],
|
|
19
|
+
"description": "Design system compliance, CSS quality, and UI correctness"
|
|
20
|
+
},
|
|
21
|
+
"security": {
|
|
22
|
+
"weight": 0.15,
|
|
23
|
+
"validators": [],
|
|
24
|
+
"description": "Security posture (agent-evaluated, no automated validator)"
|
|
25
|
+
},
|
|
26
|
+
"packages": {
|
|
27
|
+
"weight": 0.15,
|
|
28
|
+
"validators": ["packages"],
|
|
29
|
+
"description": "Dependency health and compatibility"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"thresholds": {
|
|
33
|
+
"approve": 8.0,
|
|
34
|
+
"retry": 6.0
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "frontend",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Frontend-focused rubric. Higher weight on design system compliance and UI quality.",
|
|
5
|
+
"categories": {
|
|
6
|
+
"functionality": {
|
|
7
|
+
"weight": 0.25,
|
|
8
|
+
"validators": ["contract-compliance"],
|
|
9
|
+
"description": "Core feature correctness and contract compliance"
|
|
10
|
+
},
|
|
11
|
+
"code_quality": {
|
|
12
|
+
"weight": 0.15,
|
|
13
|
+
"validators": ["architecture"],
|
|
14
|
+
"description": "Architecture patterns and code structure"
|
|
15
|
+
},
|
|
16
|
+
"design_ui": {
|
|
17
|
+
"weight": 0.35,
|
|
18
|
+
"validators": ["design-system", "css", "contrast", "nextjs-component"],
|
|
19
|
+
"description": "Design system compliance, CSS quality, contrast ratios, and component correctness"
|
|
20
|
+
},
|
|
21
|
+
"security": {
|
|
22
|
+
"weight": 0.10,
|
|
23
|
+
"validators": [],
|
|
24
|
+
"description": "Security posture (agent-evaluated, no automated validator)"
|
|
25
|
+
},
|
|
26
|
+
"packages": {
|
|
27
|
+
"weight": 0.15,
|
|
28
|
+
"validators": ["packages"],
|
|
29
|
+
"description": "Dependency health and compatibility"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"thresholds": {
|
|
33
|
+
"approve": 8.0,
|
|
34
|
+
"retry": 6.0
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -152,11 +152,44 @@
|
|
|
152
152
|
"warnings": ["API call execution requires your Asaas access_token. Doc search works without authentication."],
|
|
153
153
|
"setupGuide": "Run: claude mcp add --transport http asaas https://docs.asaas.com/mcp"
|
|
154
154
|
}
|
|
155
|
+
},
|
|
156
|
+
"stitch": {
|
|
157
|
+
"phases": ["uiux"],
|
|
158
|
+
"tools": ["create_project", "list_projects", "get_project", "list_screens", "get_screen", "generate_screen_from_text", "fetch_screen_code", "fetch_screen_image"],
|
|
159
|
+
"detection": "mcpServers includes stitch",
|
|
160
|
+
"fallback": "HTML mockup + Playwright screenshots (Step 3.5 workflow)",
|
|
161
|
+
"usage": "AI-generated screen designs, design.md export, visual design exploration",
|
|
162
|
+
"healthCheck": {
|
|
163
|
+
"toolPattern": "stitch",
|
|
164
|
+
"testCall": "Call list_projects to verify connectivity",
|
|
165
|
+
"testParams": {},
|
|
166
|
+
"successIndicator": "Returns a list of projects (possibly empty) without auth errors",
|
|
167
|
+
"failureHint": "Stitch MCP not responding. Ensure @_davideast/stitch-mcp is installed or configure official MCP: claude mcp add stitch --transport http https://stitch.googleapis.com/mcp"
|
|
168
|
+
},
|
|
169
|
+
"install": {
|
|
170
|
+
"autoInstall": true,
|
|
171
|
+
"transport": "stdio",
|
|
172
|
+
"config": {
|
|
173
|
+
"command": "cmd",
|
|
174
|
+
"args": ["/c", "npx", "-y", "@_davideast/stitch-mcp@latest", "proxy"]
|
|
175
|
+
},
|
|
176
|
+
"credentials": [],
|
|
177
|
+
"prerequisites": [],
|
|
178
|
+
"stackRelevance": ["nextjs", "nextjs-neon", "vite"],
|
|
179
|
+
"warnings": ["Community MCP tool. Official alternative: claude mcp add stitch --transport http https://stitch.googleapis.com/mcp (requires gcloud auth)"],
|
|
180
|
+
"setupGuide": "Auto-installed via npx. For official Google MCP: claude mcp add stitch --transport http https://stitch.googleapis.com/mcp"
|
|
181
|
+
},
|
|
182
|
+
"companionSkills": {
|
|
183
|
+
"repo": "google-labs-code/stitch-skills",
|
|
184
|
+
"installCommand": "npx skills add google-labs-code/stitch-skills",
|
|
185
|
+
"skills": ["stitch-design", "stitch-loop", "design-md", "prompt-enhance", "react-components", "shadcn-ui"],
|
|
186
|
+
"description": "Official Stitch agent skills for Claude Code — design generation, multi-page loops, design.md export"
|
|
187
|
+
}
|
|
155
188
|
}
|
|
156
189
|
},
|
|
157
190
|
"phaseMatrix": {
|
|
158
191
|
"proposal": ["context7"],
|
|
159
|
-
"uiux": ["playwright", "context7"],
|
|
192
|
+
"uiux": ["playwright", "context7", "stitch"],
|
|
160
193
|
"design": ["context7", "github"],
|
|
161
194
|
"tasks": ["context7", "github"],
|
|
162
195
|
"implement": ["context7", "playwright", "github", "vercel"]
|
|
@@ -10,7 +10,7 @@ allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion,
|
|
|
10
10
|
mcp__playwright__browser_navigate, mcp__playwright__browser_resize,
|
|
11
11
|
mcp__playwright__browser_take_screenshot, mcp__playwright__browser_snapshot,
|
|
12
12
|
mcp__playwright__browser_evaluate, mcp__playwright__browser_console_messages
|
|
13
|
-
cliVersion: "4.
|
|
13
|
+
cliVersion: "4.20.0"
|
|
14
14
|
stacks:
|
|
15
15
|
- nextjs
|
|
16
16
|
---
|
|
@@ -3,7 +3,7 @@ name: morph-phase-codebase-analysis
|
|
|
3
3
|
description: MORPH-SPEC Design sub-phase that analyzes existing codebase and database schema, producing schema-analysis.md with real column names, types, relationships, and field mismatches. Use at the start of Design phase before generating contracts.cs to prevent incorrect field names or types.
|
|
4
4
|
user-invocable: false
|
|
5
5
|
allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion
|
|
6
|
-
cliVersion: "4.
|
|
6
|
+
cliVersion: "4.20.0"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# MORPH Codebase Analysis - Design Sub-phase
|
|
@@ -4,7 +4,7 @@ description: MORPH-SPEC Phase 2 (Design). Schema-first interactive design: reads
|
|
|
4
4
|
argument-hint: "[feature-name]"
|
|
5
5
|
user-invocable: false
|
|
6
6
|
allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion, Agent
|
|
7
|
-
cliVersion: "4.
|
|
7
|
+
cliVersion: "4.20.0"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# MORPH Design — Phase 2
|
|
@@ -440,4 +440,14 @@ morph-spec state set $ARGUMENTS costs.estimated {X.XX}
|
|
|
440
440
|
| `decisions` | `.morph/features/{feature}/2-design/decisions.md` |
|
|
441
441
|
<!-- /morph-outputs -->
|
|
442
442
|
|
|
443
|
+
## Relevant Standards
|
|
444
|
+
|
|
445
|
+
Before starting the design phase, proactively read standards relevant to the project's stack:
|
|
446
|
+
- Architecture standards: `.morph/framework/standards/architecture/` — especially vertical-slice patterns
|
|
447
|
+
- Backend standards: `.morph/framework/standards/backend/` — for API contracts and patterns
|
|
448
|
+
- Data standards: `.morph/framework/standards/data/` — for database schema and access patterns
|
|
449
|
+
- These standards are also available as rules that auto-load when editing matching file types.
|
|
450
|
+
|
|
451
|
+
---
|
|
452
|
+
|
|
443
453
|
Continue automatically to Plan phase after approval.
|
|
@@ -5,7 +5,7 @@ argument-hint: "[feature-name]"
|
|
|
5
5
|
context: fork
|
|
6
6
|
agent: general-purpose
|
|
7
7
|
user-invocable: false
|
|
8
|
-
cliVersion: "4.
|
|
8
|
+
cliVersion: "4.20.0"
|
|
9
9
|
allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion, Agent
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -479,4 +479,15 @@ Agent(subagent_type=morph-standards-architect, prompt=<filled prompts/code-quali
|
|
|
479
479
|
|
|
480
480
|
---
|
|
481
481
|
|
|
482
|
+
## Relevant Standards
|
|
483
|
+
|
|
484
|
+
During implementation, standards auto-load via rules when editing relevant file types.
|
|
485
|
+
For proactive reference:
|
|
486
|
+
- Architecture: `.morph/framework/standards/architecture/` — structural compliance
|
|
487
|
+
- Backend/Frontend: `.morph/framework/standards/backend/` or `.morph/framework/standards/frontend/`
|
|
488
|
+
- Security: `.morph/framework/standards/security/` — authentication, authorization patterns
|
|
489
|
+
- Testing: follow the project's existing test patterns (check `test/` or `tests/` directory)
|
|
490
|
+
|
|
491
|
+
---
|
|
492
|
+
|
|
482
493
|
Feature complete! Consider creating a PR and running `morph-spec generate recap $ARGUMENTS`.
|
|
@@ -4,7 +4,7 @@ description: MORPH-SPEC Phase 3 (Plan). Generates a detailed implementation plan
|
|
|
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.
|
|
7
|
+
cliVersion: "4.20.0"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# MORPH Plan — Phase 3
|
|
@@ -4,7 +4,7 @@ description: MORPH-SPEC Phase 4 (Tasks). Breaks approved spec into bottom-up ord
|
|
|
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.
|
|
7
|
+
cliVersion: "4.20.0"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# MORPH Tasks — Phase 4
|