@polymorphism-tech/morph-spec 4.22.0 → 6.0.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/CLAUDE.md +57 -71
- package/README.md +2 -2
- package/bin/morph-spec.js +224 -491
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +55 -38
- package/framework/MORPH.md +244 -0
- package/framework/agents.json +127 -1198
- package/framework/commands/morph-apply.md +56 -240
- package/framework/commands/morph-archive.md +48 -85
- package/framework/commands/morph-preflight.md +7 -7
- package/framework/commands/morph-proposal.md +35 -191
- package/framework/commands/morph-status.md +19 -50
- package/framework/commands/morph-troubleshoot.md +2 -2
- package/framework/evals/rubrics/README.md +44 -0
- package/framework/evals/rubrics/architecture.md +46 -0
- package/framework/evals/rubrics/code-quality.md +50 -0
- package/framework/evals/rubrics/contracts.md +47 -0
- package/framework/evals/rubrics/test-coverage.md +50 -0
- package/framework/evals/scenarios/dotnet-crud.yaml +42 -0
- package/framework/evals/scenarios/nextjs-auth.yaml +38 -0
- package/framework/evals/suites/standard.yaml +5 -0
- package/framework/evals/trace-schema.md +67 -0
- package/framework/hooks/claude-code/core/README.md +3 -0
- package/framework/hooks/claude-code/core/denylist.json +14 -0
- package/framework/hooks/claude-code/{pre-tool-use → core}/enforce-phase-writes.js +5 -5
- package/framework/hooks/claude-code/core/post-edit-typecheck.js +69 -0
- package/framework/hooks/claude-code/core/pre-bash-denylist.js +29 -0
- package/framework/hooks/claude-code/{pre-tool-use → core}/protect-spec-files.js +4 -6
- package/framework/hooks/claude-code/{stop → core}/validate-completion.js +13 -82
- package/framework/hooks/shared/phase-utils.js +20 -31
- package/framework/hooks/shared/state-reader.js +4 -5
- package/framework/{skills/level-0-meta/mcp-registry.json → mcp/registry.json} +254 -243
- package/framework/phases.json +41 -115
- package/framework/rubrics/eval-rubric.json +66 -0
- package/framework/skills/README.md +39 -64
- package/framework/skills/core/README.md +3 -0
- package/framework/skills/core/morph-brainstorming/SKILL.md +102 -0
- package/framework/skills/core/morph-eval/SKILL.md +57 -0
- package/framework/skills/core/morph-implement/SKILL.md +83 -0
- package/framework/skills/core/morph-plan/SKILL.md +64 -0
- package/framework/skills/core/morph-review/SKILL.md +62 -0
- package/framework/skills/core/morph-stack-scan/SKILL.md +109 -0
- package/framework/skills/core/morph-standards/SKILL.md +49 -0
- package/framework/templates/CLAUDE.md.core.guide.md +29 -0
- package/framework/templates/CLAUDE.md.core.template +22 -0
- package/framework/templates/REGISTRY.json +4 -4
- package/framework/workflows/configs/fast-track.json +5 -10
- package/framework/workflows/configs/full.json +4 -8
- package/framework/workflows/configs/quick.json +32 -0
- package/framework/workflows/configs/standard.json +57 -64
- package/package.json +7 -6
- package/src/commands/mcp/mcp-setup.js +58 -31
- package/src/commands/project/doctor.js +69 -70
- package/src/commands/project/init.js +26 -5
- package/src/commands/project/status.js +18 -19
- package/src/commands/state/approve.js +1 -1
- package/src/commands/validation/validate-feature.js +18 -112
- package/src/core/paths/output-schema.js +133 -152
- package/src/core/state/phase-state-machine.js +10 -24
- package/src/core/state/state-manager.js +17 -17
- package/src/core/workflows/workflow-detector.js +214 -31
- package/src/core/workflows/workflow-router.js +204 -0
- package/src/dashboard/app/pages/AgentMap.jsx +32 -33
- package/src/dashboard/app/pages/LiveState.jsx +1 -1
- package/src/dashboard/app/pages/PhasePipeline.jsx +2 -4
- package/src/dashboard/app/pages/SkillsLibrary.jsx +2 -3
- package/src/dashboard/app/pages/Workflows.jsx +2 -2
- package/src/dashboard/scanner/agents-scanner.js +30 -35
- package/src/lib/agents/metrics-aggregator.js +1 -1
- package/src/lib/agents/output-collector.js +7 -9
- package/src/lib/context/context-builder.js +289 -0
- package/src/lib/detectors/claude-config-detector.js +19 -3
- package/src/lib/generators/context-generator.js +20 -42
- package/src/lib/generators/index.js +1 -0
- package/src/lib/generators/mandate-generator.js +512 -0
- package/src/lib/generators/metadata-extractor.js +1 -1
- package/src/lib/generators/recap-generator.js +3 -4
- package/src/lib/installers/mcp-installer.js +300 -92
- package/src/lib/phase-chain/phase-validator.js +36 -40
- package/src/lib/stack/stack-profile.js +74 -91
- package/src/lib/tasks/task-parser.js +1 -1
- package/src/lib/tasks/wave-resolver.js +353 -0
- package/src/lib/validators/checkpoint-persistence.js +2 -2
- package/src/lib/validators/content/cross-phase-validator.js +281 -0
- package/src/lib/validators/content/index.js +1 -0
- package/src/lib/validators/shared/emit-validator-dispatch.js +6 -56
- package/src/scripts/global-install.js +2 -27
- package/src/utils/agents-installer.js +224 -447
- package/src/utils/claude-settings-manager.js +57 -15
- package/src/utils/file-copier.js +1 -0
- package/src/utils/hooks-installer.js +15 -117
- package/src/utils/skills-installer.js +12 -18
- package/bin/detect-agents.js +0 -226
- package/bin/task-manager.js +0 -1036
- package/framework/agents/README.md +0 -13
- package/framework/agents/ai-agents/ai-system-architect.md +0 -194
- package/framework/agents/ai-agents/vector-search-expert.md +0 -247
- package/framework/agents/architecture/architecture-expert.md +0 -98
- package/framework/agents/architecture/morph-spec-architect.md +0 -66
- package/framework/agents/architecture/packages-validator.md +0 -89
- package/framework/agents/architecture/security-expert.md +0 -79
- package/framework/agents/architecture/seo-growth-hacker.md +0 -168
- package/framework/agents/architecture/standards-architect.md +0 -153
- package/framework/agents/backend/dotnet-senior.md +0 -163
- package/framework/agents/backend/ef-modeler.md +0 -119
- package/framework/agents/backend/hangfire-orchestrator.md +0 -133
- package/framework/agents/backend/ms-agent-expert.md +0 -55
- package/framework/agents/backend/neon-expert.md +0 -379
- package/framework/agents/frontend/css-specialist.md +0 -89
- package/framework/agents/frontend/design-system-validator.md +0 -100
- package/framework/agents/frontend/gsap-animator.md +0 -131
- package/framework/agents/frontend/nextjs-component-validator.md +0 -97
- package/framework/agents/frontend/nextjs-expert.md +0 -201
- package/framework/agents/frontend/ui-designer.md +0 -259
- package/framework/agents/infrastructure/infra-architect.md +0 -215
- package/framework/agents/integrations/asaas-financial.md +0 -138
- package/framework/agents/integrations/neon-auth.md +0 -151
- package/framework/agents/integrations/resend-email.md +0 -127
- package/framework/hooks/claude-code/post-tool-use/agent-complete.js +0 -122
- package/framework/hooks/claude-code/post-tool-use/dispatch.js +0 -363
- package/framework/hooks/claude-code/post-tool-use/handle-tool-failure.js +0 -78
- package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +0 -194
- package/framework/hooks/claude-code/pre-compact/save-morph-context.js +0 -199
- package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +0 -55
- package/framework/hooks/claude-code/session-start/inject-morph-context.js +0 -323
- package/framework/hooks/claude-code/session-start/post-compact-restore.js +0 -41
- package/framework/hooks/claude-code/session-start/resume-context.js +0 -77
- package/framework/hooks/claude-code/teammate-idle/teammate-idle.js +0 -90
- package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +0 -114
- package/framework/hooks/shared/compact-restore.js +0 -123
- package/framework/hooks/shared/dispatch-helpers.js +0 -147
- package/framework/hooks/shared/skill-reminder-helpers.js +0 -79
- package/framework/hooks/shared/stale-task-reset.js +0 -57
- package/framework/hooks/shared/worktree-helpers.js +0 -75
- package/framework/skills/level-0-meta/README.md +0 -7
- package/framework/skills/level-0-meta/morph-agent-teams/SKILL.md +0 -472
- package/framework/skills/level-0-meta/morph-brainstorming/SKILL.md +0 -174
- package/framework/skills/level-0-meta/morph-brainstorming/references/proposal-example.md +0 -138
- package/framework/skills/level-0-meta/morph-checklist/SKILL.md +0 -145
- package/framework/skills/level-0-meta/morph-code-review/SKILL.md +0 -250
- package/framework/skills/level-0-meta/morph-code-review/references/review-example.md +0 -164
- package/framework/skills/level-0-meta/morph-code-review/references/review-guidelines.md +0 -100
- package/framework/skills/level-0-meta/morph-code-review/scripts/scan-csharp.mjs +0 -151
- package/framework/skills/level-0-meta/morph-code-review-nextjs/SKILL.md +0 -165
- package/framework/skills/level-0-meta/morph-code-review-nextjs/references/review-example-nextjs.md +0 -254
- package/framework/skills/level-0-meta/morph-code-review-nextjs/scripts/scan-nextjs.mjs +0 -189
- package/framework/skills/level-0-meta/morph-frontend-review/SKILL.md +0 -344
- package/framework/skills/level-0-meta/morph-frontend-review/scripts/scan-accessibility.mjs +0 -376
- package/framework/skills/level-0-meta/morph-gemini-image/SKILL.md +0 -290
- package/framework/skills/level-0-meta/morph-init/SKILL.md +0 -537
- package/framework/skills/level-0-meta/morph-parallel-dispatch/SKILL.md +0 -225
- package/framework/skills/level-0-meta/morph-post-implementation/SKILL.md +0 -401
- package/framework/skills/level-0-meta/morph-post-implementation/scripts/detect-dev-server.mjs +0 -153
- package/framework/skills/level-0-meta/morph-post-implementation/scripts/detect-stack.mjs +0 -234
- package/framework/skills/level-0-meta/morph-simulation-checklist/SKILL.md +0 -106
- package/framework/skills/level-0-meta/morph-tool-usage-guide/SKILL.md +0 -243
- package/framework/skills/level-0-meta/morph-tool-usage-guide/references/tools-per-phase.md +0 -169
- package/framework/skills/level-0-meta/morph-verification-before-completion/SKILL.md +0 -148
- package/framework/skills/level-0-meta/morph-verification-before-completion/scripts/check-phase-outputs.mjs +0 -110
- package/framework/skills/level-1-workflows/README.md +0 -7
- package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +0 -269
- package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +0 -453
- package/framework/skills/level-1-workflows/morph-phase-design/references/architecture-analysis-guide.md +0 -44
- package/framework/skills/level-1-workflows/morph-phase-design/references/spec-authoring-guide.md +0 -55
- package/framework/skills/level-1-workflows/morph-phase-design/references/spec-example.md +0 -253
- package/framework/skills/level-1-workflows/morph-phase-implement/.morph/logs/activity.json +0 -38
- package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +0 -634
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/code-quality-reviewer-prompt.md +0 -50
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +0 -54
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/spec-reviewer-prompt.md +0 -47
- package/framework/skills/level-1-workflows/morph-phase-implement/references/recap-example.md +0 -132
- package/framework/skills/level-1-workflows/morph-phase-implement/references/vsa-implementation-guide.md +0 -92
- package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +0 -257
- package/framework/skills/level-1-workflows/morph-phase-tasks/.morph/logs/activity.json +0 -14
- package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +0 -291
- package/framework/skills/level-1-workflows/morph-phase-tasks/references/task-planning-patterns.md +0 -143
- package/framework/skills/level-1-workflows/morph-phase-tasks/references/tasks-example.md +0 -404
- package/framework/skills/level-1-workflows/morph-phase-tasks/scripts/validate-tasks.mjs +0 -112
- package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +0 -995
- package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +0 -147
- package/framework/squad-templates/backend-only.json +0 -47
- package/framework/squad-templates/frontend-only.json +0 -56
- package/framework/squad-templates/full-stack.json +0 -74
- package/src/commands/agents/agent-metrics.js +0 -174
- package/src/commands/agents/audit-dispatch.js +0 -82
- package/src/commands/agents/detect-agents.js +0 -275
- package/src/commands/agents/dispatch-agents.js +0 -461
- package/src/commands/agents/index.js +0 -13
- package/src/commands/agents/manage-agents.js +0 -352
- package/src/commands/agents/project-metrics.js +0 -74
- package/src/commands/phase/phase-reset.js +0 -74
- package/src/commands/scope/escalate.js +0 -217
- package/src/commands/state/advance-phase.js +0 -600
- package/src/commands/state/phase-runner.js +0 -215
- package/src/commands/state/state.js +0 -444
- package/src/commands/task/expand.js +0 -100
- package/src/commands/tasks/index.js +0 -4
- package/src/commands/tasks/task.js +0 -105
- package/src/commands/templates/index.js +0 -5
- package/src/commands/templates/template-render.js +0 -93
- package/src/commands/trust/trust.js +0 -118
- package/src/lib/agents/agents-manager.js +0 -349
- package/src/lib/build/build-runner.js +0 -99
- package/src/lib/checkpoints/checkpoint-hooks.js +0 -111
- package/src/lib/contracts/sprint-contract-generator.js +0 -137
- package/src/lib/scope/impact-analyzer.js +0 -106
- package/src/lib/trust/trust-manager.js +0 -157
- package/src/utils/tier-helpers.js +0 -24
- /package/framework/hooks/claude-code/{post-tool-use → core}/loop-detect.js +0 -0
|
@@ -1,376 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* scan-accessibility.mjs
|
|
4
|
-
*
|
|
5
|
-
* Scans .tsx/.ts (Next.js) and .razor (Blazor) files for static accessibility violations.
|
|
6
|
-
* Executed locally by Claude via Bash — output returned to Claude.
|
|
7
|
-
*
|
|
8
|
-
* Usage:
|
|
9
|
-
* node scan-accessibility.mjs [path] # scan directory or file (default: '.')
|
|
10
|
-
* node scan-accessibility.mjs src/ # scan src/ recursively
|
|
11
|
-
* node scan-accessibility.mjs --summary # counts only, no details
|
|
12
|
-
*
|
|
13
|
-
* Checks (7 total):
|
|
14
|
-
* IMG_MISSING_ALT CRITICAL .tsx/.razor <img without alt= attribute
|
|
15
|
-
* INPUT_MISSING_LABEL HIGH .tsx/.razor <input without associated label or aria-label
|
|
16
|
-
* <InputText/<FluentTextField without Label=
|
|
17
|
-
* HEADING_HIERARCHY HIGH .tsx/.razor Heading level skip (H1→H3 skipping H2)
|
|
18
|
-
* LINK_NO_TEXT HIGH .tsx/.razor <a> empty or icon-only without aria-label
|
|
19
|
-
* BUTTON_NO_TEXT HIGH .tsx/.razor <button>/<FluentButton> without accessible text
|
|
20
|
-
* AUTOPLAY_MEDIA MEDIUM .tsx/.razor <video>/<audio> with autoPlay without muted
|
|
21
|
-
* FLUENT_CHECKBOX_NO_LABEL MEDIUM .razor <FluentCheckbox without Label=
|
|
22
|
-
*
|
|
23
|
-
* Output JSON: { summary: { scanned, findings, bySeverity, clean }, findings: [...] }
|
|
24
|
-
* Exit code: 1 if CRITICAL findings, 0 otherwise
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
import { readdirSync, readFileSync, statSync, existsSync } from 'fs';
|
|
28
|
-
import { join, extname, relative } from 'path';
|
|
29
|
-
|
|
30
|
-
const targetPath = process.argv[2] ?? '.';
|
|
31
|
-
const summaryOnly = process.argv.includes('--summary');
|
|
32
|
-
|
|
33
|
-
// ─── File collectors ────────────────────────────────────────────────────────
|
|
34
|
-
|
|
35
|
-
const IGNORED_DIRS = ['node_modules', '.git', '.next', 'dist', 'build', '.turbo', 'bin', 'obj'];
|
|
36
|
-
|
|
37
|
-
function collectFrontendFiles(dir) {
|
|
38
|
-
if (!existsSync(dir)) return [];
|
|
39
|
-
const stat = statSync(dir);
|
|
40
|
-
if (stat.isFile()) {
|
|
41
|
-
const ext = extname(dir);
|
|
42
|
-
return ['.tsx', '.ts', '.razor'].includes(ext) ? [dir] : [];
|
|
43
|
-
}
|
|
44
|
-
const results = [];
|
|
45
|
-
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
46
|
-
if (entry.isDirectory() && !IGNORED_DIRS.includes(entry.name)) {
|
|
47
|
-
results.push(...collectFrontendFiles(join(dir, entry.name)));
|
|
48
|
-
} else if (entry.isFile()) {
|
|
49
|
-
const ext = extname(entry.name);
|
|
50
|
-
if (['.tsx', '.ts', '.razor'].includes(ext)) {
|
|
51
|
-
results.push(join(dir, entry.name));
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return results;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// ─── Checks ─────────────────────────────────────────────────────────────────
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Check: IMG_MISSING_ALT — CRITICAL
|
|
62
|
-
* Detect <img without alt= in .tsx and .razor files.
|
|
63
|
-
* Matches both JSX <img ... /> and HTML <img ...> forms.
|
|
64
|
-
*/
|
|
65
|
-
function checkImgMissingAlt(content, lines, relFile, findings) {
|
|
66
|
-
// Match <img tags that do NOT contain alt=
|
|
67
|
-
const imgRegex = /<img\b([^>]*?)(?:\/>|>)/g;
|
|
68
|
-
let match;
|
|
69
|
-
while ((match = imgRegex.exec(content)) !== null) {
|
|
70
|
-
const attrs = match[1];
|
|
71
|
-
if (!/\balt\s*=/i.test(attrs)) {
|
|
72
|
-
const lineNum = content.slice(0, match.index).split('\n').length;
|
|
73
|
-
findings.push({
|
|
74
|
-
severity: 'CRITICAL',
|
|
75
|
-
id: 'IMG_MISSING_ALT',
|
|
76
|
-
file: relFile,
|
|
77
|
-
line: lineNum,
|
|
78
|
-
message: '<img> element missing alt attribute — required for screen readers',
|
|
79
|
-
snippet: lines[lineNum - 1]?.trim().slice(0, 100),
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Check: INPUT_MISSING_LABEL — HIGH
|
|
87
|
-
* Detect <input>, <InputText>, <FluentTextField without accessible label.
|
|
88
|
-
* Accepts: aria-label=, aria-labelledby=, or associated <label for=
|
|
89
|
-
* For Blazor: Label= attribute on <InputText> and <FluentTextField>
|
|
90
|
-
*/
|
|
91
|
-
function checkInputMissingLabel(content, lines, relFile, findings) {
|
|
92
|
-
const isRazor = relFile.endsWith('.razor');
|
|
93
|
-
|
|
94
|
-
// --- Standard <input> (both tsx and razor HTML sections) ---
|
|
95
|
-
const inputRegex = /<input\b([^>]*?)(?:\/>|>)/g;
|
|
96
|
-
let match;
|
|
97
|
-
while ((match = inputRegex.exec(content)) !== null) {
|
|
98
|
-
const attrs = match[1];
|
|
99
|
-
// Skip hidden inputs — they don't need labels
|
|
100
|
-
if (/type\s*=\s*['"]hidden['"]/i.test(attrs)) continue;
|
|
101
|
-
const hasLabel =
|
|
102
|
-
/\baria-label\s*=/i.test(attrs) ||
|
|
103
|
-
/\baria-labelledby\s*=/i.test(attrs) ||
|
|
104
|
-
/\bid\s*=\s*['"]([^'"]+)['"]/i.test(attrs); // id present → could be associated by <label for>
|
|
105
|
-
if (!hasLabel) {
|
|
106
|
-
const lineNum = content.slice(0, match.index).split('\n').length;
|
|
107
|
-
findings.push({
|
|
108
|
-
severity: 'HIGH',
|
|
109
|
-
id: 'INPUT_MISSING_LABEL',
|
|
110
|
-
file: relFile,
|
|
111
|
-
line: lineNum,
|
|
112
|
-
message: '<input> element missing accessible label (aria-label, aria-labelledby, or <label for=)',
|
|
113
|
-
snippet: lines[lineNum - 1]?.trim().slice(0, 100),
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (isRazor) {
|
|
119
|
-
// --- Blazor <InputText> without Label= ---
|
|
120
|
-
const inputTextRegex = /<InputText\b([^>]*?)(?:\/>|>)/g;
|
|
121
|
-
while ((match = inputTextRegex.exec(content)) !== null) {
|
|
122
|
-
const attrs = match[1];
|
|
123
|
-
if (!/\bLabel\s*=/i.test(attrs) && !/\baria-label\s*=/i.test(attrs)) {
|
|
124
|
-
const lineNum = content.slice(0, match.index).split('\n').length;
|
|
125
|
-
findings.push({
|
|
126
|
-
severity: 'HIGH',
|
|
127
|
-
id: 'INPUT_MISSING_LABEL',
|
|
128
|
-
file: relFile,
|
|
129
|
-
line: lineNum,
|
|
130
|
-
message: '<InputText> component missing Label= attribute (or aria-label)',
|
|
131
|
-
snippet: lines[lineNum - 1]?.trim().slice(0, 100),
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// --- Fluent UI <FluentTextField> without Label= ---
|
|
137
|
-
const fluentTextRegex = /<FluentTextField\b([^>]*?)(?:\/>|>)/g;
|
|
138
|
-
while ((match = fluentTextRegex.exec(content)) !== null) {
|
|
139
|
-
const attrs = match[1];
|
|
140
|
-
if (!/\bLabel\s*=/i.test(attrs) && !/\baria-label\s*=/i.test(attrs)) {
|
|
141
|
-
const lineNum = content.slice(0, match.index).split('\n').length;
|
|
142
|
-
findings.push({
|
|
143
|
-
severity: 'HIGH',
|
|
144
|
-
id: 'INPUT_MISSING_LABEL',
|
|
145
|
-
file: relFile,
|
|
146
|
-
line: lineNum,
|
|
147
|
-
message: '<FluentTextField> component missing Label= attribute (or aria-label)',
|
|
148
|
-
snippet: lines[lineNum - 1]?.trim().slice(0, 100),
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Check: HEADING_HIERARCHY — HIGH
|
|
157
|
-
* Detect heading level skips (e.g. H1 → H3 without H2) within a file.
|
|
158
|
-
* Scans for h1–h6 / H1–H6 tags in order and reports any skip > 1 level.
|
|
159
|
-
*/
|
|
160
|
-
function checkHeadingHierarchy(content, lines, relFile, findings) {
|
|
161
|
-
// Collect all headings in document order
|
|
162
|
-
const headingRegex = /<[Hh]([1-6])\b/g;
|
|
163
|
-
const headings = [];
|
|
164
|
-
let match;
|
|
165
|
-
while ((match = headingRegex.exec(content)) !== null) {
|
|
166
|
-
headings.push({
|
|
167
|
-
level: parseInt(match[1], 10),
|
|
168
|
-
lineNum: content.slice(0, match.index).split('\n').length,
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Check for level skips
|
|
173
|
-
for (let i = 1; i < headings.length; i++) {
|
|
174
|
-
const prev = headings[i - 1];
|
|
175
|
-
const curr = headings[i];
|
|
176
|
-
// Only flag increases that skip a level (e.g. h1 → h3)
|
|
177
|
-
if (curr.level > prev.level + 1) {
|
|
178
|
-
findings.push({
|
|
179
|
-
severity: 'HIGH',
|
|
180
|
-
id: 'HEADING_HIERARCHY',
|
|
181
|
-
file: relFile,
|
|
182
|
-
line: curr.lineNum,
|
|
183
|
-
message: `Heading level skip: H${prev.level} (line ${prev.lineNum}) → H${curr.level} (line ${curr.lineNum}) — missing H${prev.level + 1}`,
|
|
184
|
-
snippet: lines[curr.lineNum - 1]?.trim().slice(0, 100),
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Check: LINK_NO_TEXT — HIGH
|
|
192
|
-
* Detect <a> elements that are empty or contain only an icon without aria-label.
|
|
193
|
-
* Empty: <a href="..."></a> or <a .../>
|
|
194
|
-
* Icon-only: <a href="..."><SomeIcon /></a> or <a href="..."><i class="..."></i></a>
|
|
195
|
-
*/
|
|
196
|
-
function checkLinkNoText(content, lines, relFile, findings) {
|
|
197
|
-
// Match opening <a ...> tag and capture attributes, then check for aria-label
|
|
198
|
-
// We use a simplified approach: find <a> tags and look for aria-label in attrs
|
|
199
|
-
const linkRegex = /<a\b([^>]*?)>([\s\S]*?)<\/a>/g;
|
|
200
|
-
let match;
|
|
201
|
-
while ((match = linkRegex.exec(content)) !== null) {
|
|
202
|
-
const attrs = match[1];
|
|
203
|
-
const innerContent = match[2].trim();
|
|
204
|
-
|
|
205
|
-
// Has aria-label → OK
|
|
206
|
-
if (/\baria-label\s*=/i.test(attrs) || /\baria-labelledby\s*=/i.test(attrs)) continue;
|
|
207
|
-
|
|
208
|
-
// Check if inner content has visible text (not just whitespace, tags, or expressions)
|
|
209
|
-
// Strip JSX expressions {}, HTML tags, and whitespace
|
|
210
|
-
const textOnly = innerContent
|
|
211
|
-
.replace(/\{[^}]*\}/g, '') // strip {expressions}
|
|
212
|
-
.replace(/<[^>]+>/g, '') // strip HTML/JSX tags
|
|
213
|
-
.trim();
|
|
214
|
-
|
|
215
|
-
if (!textOnly) {
|
|
216
|
-
const lineNum = content.slice(0, match.index).split('\n').length;
|
|
217
|
-
findings.push({
|
|
218
|
-
severity: 'HIGH',
|
|
219
|
-
id: 'LINK_NO_TEXT',
|
|
220
|
-
file: relFile,
|
|
221
|
-
line: lineNum,
|
|
222
|
-
message: '<a> element has no accessible text — add aria-label or visible text content',
|
|
223
|
-
snippet: lines[lineNum - 1]?.trim().slice(0, 100),
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Check: BUTTON_NO_TEXT — HIGH
|
|
231
|
-
* Detect <button> and <FluentButton> elements without accessible text or aria-label.
|
|
232
|
-
*/
|
|
233
|
-
function checkButtonNoText(content, lines, relFile, findings) {
|
|
234
|
-
// <button> elements
|
|
235
|
-
const buttonRegex = /<button\b([^>]*?)>([\s\S]*?)<\/button>/g;
|
|
236
|
-
let match;
|
|
237
|
-
while ((match = buttonRegex.exec(content)) !== null) {
|
|
238
|
-
const attrs = match[1];
|
|
239
|
-
const innerContent = match[2].trim();
|
|
240
|
-
|
|
241
|
-
if (/\baria-label\s*=/i.test(attrs) || /\baria-labelledby\s*=/i.test(attrs)) continue;
|
|
242
|
-
|
|
243
|
-
const textOnly = innerContent
|
|
244
|
-
.replace(/\{[^}]*\}/g, '')
|
|
245
|
-
.replace(/<[^>]+>/g, '')
|
|
246
|
-
.trim();
|
|
247
|
-
|
|
248
|
-
if (!textOnly) {
|
|
249
|
-
const lineNum = content.slice(0, match.index).split('\n').length;
|
|
250
|
-
findings.push({
|
|
251
|
-
severity: 'HIGH',
|
|
252
|
-
id: 'BUTTON_NO_TEXT',
|
|
253
|
-
file: relFile,
|
|
254
|
-
line: lineNum,
|
|
255
|
-
message: '<button> element has no accessible text — add aria-label or visible text content',
|
|
256
|
-
snippet: lines[lineNum - 1]?.trim().slice(0, 100),
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// <FluentButton> (Blazor Fluent UI)
|
|
262
|
-
const fluentBtnRegex = /<FluentButton\b([^>]*?)>([\s\S]*?)<\/FluentButton>/g;
|
|
263
|
-
while ((match = fluentBtnRegex.exec(content)) !== null) {
|
|
264
|
-
const attrs = match[1];
|
|
265
|
-
const innerContent = match[2].trim();
|
|
266
|
-
|
|
267
|
-
if (/\baria-label\s*=/i.test(attrs) || /\bTitle\s*=/i.test(attrs)) continue;
|
|
268
|
-
|
|
269
|
-
const textOnly = innerContent
|
|
270
|
-
.replace(/\{[^}]*\}/g, '')
|
|
271
|
-
.replace(/<[^>]+>/g, '')
|
|
272
|
-
.trim();
|
|
273
|
-
|
|
274
|
-
if (!textOnly) {
|
|
275
|
-
const lineNum = content.slice(0, match.index).split('\n').length;
|
|
276
|
-
findings.push({
|
|
277
|
-
severity: 'HIGH',
|
|
278
|
-
id: 'BUTTON_NO_TEXT',
|
|
279
|
-
file: relFile,
|
|
280
|
-
line: lineNum,
|
|
281
|
-
message: '<FluentButton> has no accessible text — add aria-label, Title=, or visible text',
|
|
282
|
-
snippet: lines[lineNum - 1]?.trim().slice(0, 100),
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
/**
|
|
289
|
-
* Check: AUTOPLAY_MEDIA — MEDIUM
|
|
290
|
-
* Detect <video> or <audio> with autoPlay attribute but without muted.
|
|
291
|
-
* Autoplay without muted violates WCAG 1.4.2 (Audio Control).
|
|
292
|
-
*/
|
|
293
|
-
function checkAutoplayMedia(content, lines, relFile, findings) {
|
|
294
|
-
const mediaRegex = /<(video|audio)\b([^>]*?)(?:\/>|>)/gi;
|
|
295
|
-
let match;
|
|
296
|
-
while ((match = mediaRegex.exec(content)) !== null) {
|
|
297
|
-
const attrs = match[2];
|
|
298
|
-
// JSX: autoPlay (camelCase), HTML: autoplay
|
|
299
|
-
if (/\bautoPlay\b|\bautoplay\b/i.test(attrs) && !/\bmuted\b/i.test(attrs)) {
|
|
300
|
-
const lineNum = content.slice(0, match.index).split('\n').length;
|
|
301
|
-
findings.push({
|
|
302
|
-
severity: 'MEDIUM',
|
|
303
|
-
id: 'AUTOPLAY_MEDIA',
|
|
304
|
-
file: relFile,
|
|
305
|
-
line: lineNum,
|
|
306
|
-
message: `<${match[1]}> has autoPlay without muted — violates WCAG 1.4.2 (Audio Control)`,
|
|
307
|
-
snippet: lines[lineNum - 1]?.trim().slice(0, 100),
|
|
308
|
-
});
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
/**
|
|
314
|
-
* Check: FLUENT_CHECKBOX_NO_LABEL — MEDIUM
|
|
315
|
-
* Detect <FluentCheckbox without Label= in .razor files.
|
|
316
|
-
*/
|
|
317
|
-
function checkFluentCheckboxNoLabel(content, lines, relFile, findings) {
|
|
318
|
-
if (!relFile.endsWith('.razor')) return;
|
|
319
|
-
|
|
320
|
-
const checkboxRegex = /<FluentCheckbox\b([^>]*?)(?:\/>|>)/g;
|
|
321
|
-
let match;
|
|
322
|
-
while ((match = checkboxRegex.exec(content)) !== null) {
|
|
323
|
-
const attrs = match[1];
|
|
324
|
-
if (!/\bLabel\s*=/i.test(attrs) && !/\baria-label\s*=/i.test(attrs)) {
|
|
325
|
-
const lineNum = content.slice(0, match.index).split('\n').length;
|
|
326
|
-
findings.push({
|
|
327
|
-
severity: 'MEDIUM',
|
|
328
|
-
id: 'FLUENT_CHECKBOX_NO_LABEL',
|
|
329
|
-
file: relFile,
|
|
330
|
-
line: lineNum,
|
|
331
|
-
message: '<FluentCheckbox> missing Label= attribute — screen readers cannot identify this checkbox',
|
|
332
|
-
snippet: lines[lineNum - 1]?.trim().slice(0, 100),
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
// ─── Main ────────────────────────────────────────────────────────────────────
|
|
339
|
-
|
|
340
|
-
const files = collectFrontendFiles(targetPath);
|
|
341
|
-
const findings = [];
|
|
342
|
-
|
|
343
|
-
for (const file of files) {
|
|
344
|
-
const content = readFileSync(file, 'utf-8');
|
|
345
|
-
const lines = content.split('\n');
|
|
346
|
-
const relFile = relative(process.cwd(), file).replace(/\\/g, '/');
|
|
347
|
-
|
|
348
|
-
checkImgMissingAlt(content, lines, relFile, findings);
|
|
349
|
-
checkInputMissingLabel(content, lines, relFile, findings);
|
|
350
|
-
checkHeadingHierarchy(content, lines, relFile, findings);
|
|
351
|
-
checkLinkNoText(content, lines, relFile, findings);
|
|
352
|
-
checkButtonNoText(content, lines, relFile, findings);
|
|
353
|
-
checkAutoplayMedia(content, lines, relFile, findings);
|
|
354
|
-
checkFluentCheckboxNoLabel(content, lines, relFile, findings);
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
const bySeverity = { CRITICAL: 0, HIGH: 0, MEDIUM: 0, LOW: 0 };
|
|
358
|
-
for (const f of findings) {
|
|
359
|
-
bySeverity[f.severity] = (bySeverity[f.severity] ?? 0) + 1;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
const summary = {
|
|
363
|
-
scanned: files.length,
|
|
364
|
-
findings: findings.length,
|
|
365
|
-
bySeverity,
|
|
366
|
-
clean: findings.length === 0,
|
|
367
|
-
};
|
|
368
|
-
|
|
369
|
-
if (summaryOnly) {
|
|
370
|
-
console.log(JSON.stringify(summary, null, 2));
|
|
371
|
-
} else {
|
|
372
|
-
console.log(JSON.stringify({ summary, findings }, null, 2));
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// Exit 1 if any CRITICAL findings
|
|
376
|
-
process.exit(findings.some(f => f.severity === 'CRITICAL') ? 1 : 0);
|
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: morph-gemini-image
|
|
3
|
-
description: |
|
|
4
|
-
Expert at generating and editing images via the Gemini Image MCP (gemini-image-mcp).
|
|
5
|
-
Constructs high-quality prompts for Gemini image models using narrative natural language.
|
|
6
|
-
Use when implementing UI components that need generated images, or when assets.md
|
|
7
|
-
specifies image requirements. Also use for standalone image generation requests.
|
|
8
|
-
Triggers: /morph-gemini-image, "generate image", "create image", "edit image",
|
|
9
|
-
"make a logo", "generate assets", image generation for components.
|
|
10
|
-
argument-hint: "[optional: image description or assets.md IMG-ID]"
|
|
11
|
-
user-invocable: true
|
|
12
|
-
allowed-tools: Read, Write, Glob, Grep, AskUserQuestion
|
|
13
|
-
cliVersion: "4.22.0"
|
|
14
|
-
---
|
|
15
|
-
|
|
16
|
-
# Gemini Image — Prompt Specialist
|
|
17
|
-
|
|
18
|
-
You are an expert at communicating with the Gemini 2.5 Flash Image model (Nano Banana) through the `gemini-image-mcp` MCP server. Your job is to transform the user's intent into the best possible prompt and then execute it.
|
|
19
|
-
|
|
20
|
-
## The Golden Rules
|
|
21
|
-
|
|
22
|
-
**1. Describe scenes narratively, never list keywords.** The Gemini image model has deep language comprehension — coherent paragraphs consistently outperform disconnected keyword lists. Think of it as briefing a photographer or illustrator, not filling in search tags.
|
|
23
|
-
|
|
24
|
-
**2. Generation demands depth; editing demands restraint.** These are opposite strategies:
|
|
25
|
-
- **Generation**: Go deep. Multi-layered compositions, rich textures, dramatic lighting contrasts, visual storytelling. Simple prompts produce flat, generic images. Push for complexity — foreground interest, mid-ground subject, background atmosphere. Think in layers.
|
|
26
|
-
- **Editing**: Go minimal. Short, direct instructions targeting exactly what changes. The more you describe in an edit prompt, the more the model "re-imagines" the image and the more artificial it looks. 1-2 sentences max for edits.
|
|
27
|
-
|
|
28
|
-
## Important: JSON Does NOT Work
|
|
29
|
-
|
|
30
|
-
The Gemini image models do NOT support JSON/structured output mode for image generation. Sending JSON-formatted prompts provides no benefit and can confuse the model. Always use natural language.
|
|
31
|
-
|
|
32
|
-
## Available MCP Tools
|
|
33
|
-
|
|
34
|
-
| Tool | When to use |
|
|
35
|
-
|------|-------------|
|
|
36
|
-
| `generate_image` | Create new images from text description |
|
|
37
|
-
| `edit_image` | Modify an existing image (needs `imagePath`) |
|
|
38
|
-
| `continue_editing` | Iterate on the last generated/edited image |
|
|
39
|
-
| `list_images` | Browse image history |
|
|
40
|
-
| `get_image_info` | Get metadata about a specific image |
|
|
41
|
-
| `get_configuration_status` | Check API key and server health |
|
|
42
|
-
|
|
43
|
-
## How to Build Prompts
|
|
44
|
-
|
|
45
|
-
### Step 1: Identify the Category
|
|
46
|
-
|
|
47
|
-
Determine what kind of image the user wants. Each category has a different prompt template that produces optimal results:
|
|
48
|
-
|
|
49
|
-
**Photorealistic** — Real-world scenes, portraits, product photos, landscapes
|
|
50
|
-
**Illustration** — Stickers, icons, cartoon, kawaii, branded designs
|
|
51
|
-
**Text Rendering** — Logos, infographics, menus, diagrams with legible text
|
|
52
|
-
**Product Mockup** — E-commerce, studio product shots
|
|
53
|
-
**Minimalist / Negative Space** — Clean compositions for overlays or branding
|
|
54
|
-
**Sequential Art** — Comics, storyboards, panels
|
|
55
|
-
|
|
56
|
-
### Step 2: Construct the Prompt
|
|
57
|
-
|
|
58
|
-
Build the prompt by layering these elements (order matters — put the most important things first).
|
|
59
|
-
|
|
60
|
-
The key to images that feel deep and impactful rather than flat and generic is **visual layering** — describe what's happening in the foreground, mid-ground, and background separately. A flat description ("neon city at night") produces a flat image. A layered description creates depth that pulls the viewer in.
|
|
61
|
-
|
|
62
|
-
1. **Subject** — Who/what is the main focus? Be hyper-specific.
|
|
63
|
-
- Bad: "a warrior in armor"
|
|
64
|
-
- Good: "a tall elven warrior in ornate silver plate armor with falcon-wing pauldrons and a high collar etched with leaf patterns"
|
|
65
|
-
|
|
66
|
-
2. **Action / Pose** — What is the subject doing? Expression? Gesture?
|
|
67
|
-
|
|
68
|
-
3. **Environment / Setting** — Where is this happening? Describe in **layers**:
|
|
69
|
-
- **Foreground**: What's closest to camera? (objects, textures, particles, rain drops)
|
|
70
|
-
- **Mid-ground**: Where is the subject? What surrounds them?
|
|
71
|
-
- **Background**: What's far away? (sky, distant buildings, horizon, atmospheric haze)
|
|
72
|
-
- This layered approach creates depth of field and visual richness that flat descriptions miss.
|
|
73
|
-
|
|
74
|
-
4. **Lighting** — This dramatically affects mood and is the single biggest quality lever. Use photography terms and describe **multiple light sources** for complexity:
|
|
75
|
-
- "golden hour key light from the left, with cool blue fill from ambient sky on the right, and warm rim light from behind catching hair edges"
|
|
76
|
-
- "harsh overhead spotlight casting dramatic shadows, with neon signs reflecting colored light off wet surfaces"
|
|
77
|
-
- "soft diffused window light as primary, with practical warm bulbs adding depth in the background"
|
|
78
|
-
- Single-source flat lighting = flat image. Multiple sources with different colors/directions = cinematic depth.
|
|
79
|
-
|
|
80
|
-
5. **Style / Medium** — How should it look?
|
|
81
|
-
- Photography: mention camera, lens, film stock ("85mm portrait lens, shallow depth of field, Kodak Portra 400 film grain")
|
|
82
|
-
- Illustration: name the style ("kawaii", "cel-shaded", "watercolor", "ink wash", "art nouveau")
|
|
83
|
-
- Reference artists when useful: "in the style of Studio Ghibli", "Van Gogh's Starry Night brushwork"
|
|
84
|
-
|
|
85
|
-
6. **Color Palette** — Specify dominant colors and **contrast relationships**
|
|
86
|
-
- "warm amber highlights against deep teal shadows"
|
|
87
|
-
- "desaturated cool tones with a single pop of crimson red on the subject"
|
|
88
|
-
- Color contrast draws the eye and creates visual hierarchy.
|
|
89
|
-
|
|
90
|
-
7. **Composition** — Camera angle, framing, negative space
|
|
91
|
-
- "low angle looking up", "bird's eye view", "close-up macro", "rule of thirds with subject on left"
|
|
92
|
-
- Describe **depth cues**: objects overlapping, foreground blur, atmospheric perspective (distant objects fading into haze)
|
|
93
|
-
|
|
94
|
-
8. **Texture and Detail** — What makes it tactile and real?
|
|
95
|
-
- "rain droplets on glass", "worn leather with visible stitching", "dust particles floating in light beams", "condensation on cold metal"
|
|
96
|
-
- These micro-details transform flat images into immersive scenes.
|
|
97
|
-
|
|
98
|
-
9. **Aspect Ratio** — Choose based on purpose:
|
|
99
|
-
- `1:1` — Social media posts, avatars, icons
|
|
100
|
-
- `16:9` — Presentations, YouTube thumbnails, desktop wallpapers
|
|
101
|
-
- `9:16` — Stories, reels, mobile wallpapers
|
|
102
|
-
- `3:2` — Landscape photography, blog headers
|
|
103
|
-
- `2:3` — Portrait photography, book covers
|
|
104
|
-
- `4:3` — Standard display, documents
|
|
105
|
-
- `3:4` — Pinterest, portrait posters
|
|
106
|
-
|
|
107
|
-
### Step 3: Apply Category-Specific Templates
|
|
108
|
-
|
|
109
|
-
#### Photorealistic
|
|
110
|
-
```
|
|
111
|
-
A [shot type] of [subject with specific details], [action/pose],
|
|
112
|
-
set in [environment with atmosphere], illuminated by [lighting description],
|
|
113
|
-
captured with [camera/lens], [mood/feeling], [aspect ratio orientation].
|
|
114
|
-
```
|
|
115
|
-
Example: "A close-up portrait of an elderly ceramicist inspecting a hand-glazed tea bowl in his sun-drenched workshop, golden hour light streaming through dusty windows, 85mm portrait lens with soft bokeh, serene and contemplative atmosphere, vertical orientation."
|
|
116
|
-
|
|
117
|
-
#### Illustration / Sticker
|
|
118
|
-
```
|
|
119
|
-
[Art style] [subject with personality traits], [key visual details],
|
|
120
|
-
[line style] outlines, [shading approach], [color palette],
|
|
121
|
-
[background specification].
|
|
122
|
-
```
|
|
123
|
-
Example: "Kawaii-style red panda sticker wearing a tiny bamboo hat and holding a matcha cup, bold clean outlines, cel-shading with soft highlights, vibrant warm colors with pink accents, white background."
|
|
124
|
-
|
|
125
|
-
#### Text Rendering (Logos, Infographics)
|
|
126
|
-
```
|
|
127
|
-
[Image type] for [brand/concept context], featuring the text "[exact text]"
|
|
128
|
-
in [font style description], [visual elements around text],
|
|
129
|
-
[overall color scheme and style], [format].
|
|
130
|
-
```
|
|
131
|
-
Example: "Minimalist coffee shop logo for a premium artisan brand, featuring the text 'The Daily Grind' in clean geometric sans-serif, with a stylized coffee bean icon integrated into the letter 'D', black and white with a single copper accent, square format."
|
|
132
|
-
|
|
133
|
-
**Important: Text rendering often contains spelling errors.** The model frequently misspells words, swaps letters, or drops characters — especially in longer words. After generating any image with text, always verify the spelling by viewing the result. If text is wrong, use `continue_editing` with a prompt like: "Fix the text spelling — it should read '[correct text]' exactly. Keep everything else the same." You may need 2-3 iterations to get text perfect.
|
|
134
|
-
|
|
135
|
-
#### Product Mockup
|
|
136
|
-
```
|
|
137
|
-
High-resolution studio photograph of [product with materials],
|
|
138
|
-
on [surface/background], [lighting setup], [camera angle] for [purpose],
|
|
139
|
-
sharp focus on [detail], [realism level], [aspect ratio].
|
|
140
|
-
```
|
|
141
|
-
Example: "High-resolution studio photograph of a minimalist black ceramic mug with a matte finish on a polished concrete surface, three-point softbox lighting with a subtle warm key light, 45-degree elevated angle for e-commerce listing, sharp focus on the steam rising from dark coffee, photorealistic, square format."
|
|
142
|
-
|
|
143
|
-
#### Minimalist / Negative Space
|
|
144
|
-
```
|
|
145
|
-
[Single subject] positioned [placement], [vast background description],
|
|
146
|
-
generous negative space [purpose], [subtle lighting direction], [aspect ratio].
|
|
147
|
-
```
|
|
148
|
-
Example: "A single red maple leaf positioned in the bottom-right third, vast off-white linen-textured background, generous negative space ideal for text overlay, soft diffused light from top-left casting a gentle shadow, square format."
|
|
149
|
-
|
|
150
|
-
## How to Handle Image Editing
|
|
151
|
-
|
|
152
|
-
**The cardinal rule of editing: LESS IS MORE.** Verbose edit prompts cause the model to "re-imagine" the entire image, producing an artificial, over-processed look. Keep edit prompts short, direct, and focused on exactly what changes. Let the model figure out how to integrate the change naturally — it's very good at this when you don't over-constrain it.
|
|
153
|
-
|
|
154
|
-
### Adding / Removing Elements
|
|
155
|
-
One sentence describing the change. Don't describe how to integrate it — the model handles that.
|
|
156
|
-
```
|
|
157
|
-
"Add a knitted wizard hat to the cat."
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
### Background Replacement
|
|
161
|
-
State the new background simply. Don't over-describe — the model matches lighting automatically.
|
|
162
|
-
```
|
|
163
|
-
"Replace the background with a tropical beach at sunset."
|
|
164
|
-
```
|
|
165
|
-
Avoid: "Replace the background with a beautiful tropical beach with golden sand, palm trees swaying, turquoise waves, warm orange and pink sunset colors reflecting on the water, ensuring the subject's lighting matches the warm sunset glow..." — this level of detail makes the result look MORE artificial, not less.
|
|
166
|
-
|
|
167
|
-
### Inpainting (Targeted Changes)
|
|
168
|
-
Name what changes and state what stays in one line.
|
|
169
|
-
```
|
|
170
|
-
"Change the blue sofa to a brown leather chesterfield. Keep everything else."
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
### Style Transfer
|
|
174
|
-
Name the target style. Keep it simple.
|
|
175
|
-
```
|
|
176
|
-
"Transform this into Van Gogh's Starry Night style."
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
### Multi-Image Composition
|
|
180
|
-
Use `referenceImages` parameter to supply source images.
|
|
181
|
-
```
|
|
182
|
-
"Put the woman from the second image in the blue dress from the first image."
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
### Iterative Refinement
|
|
186
|
-
Use `continue_editing` for small adjustments. One change at a time:
|
|
187
|
-
- "Make the lighting warmer"
|
|
188
|
-
- "More contrast in the shadows"
|
|
189
|
-
- "Zoom out a bit"
|
|
190
|
-
- "More serious expression"
|
|
191
|
-
|
|
192
|
-
## Critical Best Practices
|
|
193
|
-
|
|
194
|
-
1. **Be hyper-specific** — Generic descriptions produce generic images. Replace "fantasy armor" with "ornate elven plate armor, etched with silver leaf patterns, high collar, falcon-wing pauldrons."
|
|
195
|
-
|
|
196
|
-
2. **Frame positively** — The model handles positive instructions better than negations. Say "empty deserted street with no traffic, completely still" instead of "street without cars." Describe what IS there, not what isn't.
|
|
197
|
-
|
|
198
|
-
3. **Use photography language** — Camera terms give precise control: "85mm portrait lens", "wide-angle", "macro", "Dutch angle", "low-angle looking up", "bird's eye view."
|
|
199
|
-
|
|
200
|
-
4. **Specify lighting explicitly** — Lighting is the single biggest quality lever. Always include it. "Golden hour", "overcast diffused", "dramatic Rembrandt", "neon rim-lit", "three-point studio."
|
|
201
|
-
|
|
202
|
-
5. **Include context and intent** — "Logo for a high-end minimalist skincare brand" produces better results than just "make a logo." The model uses context to make style decisions.
|
|
203
|
-
|
|
204
|
-
6. **One concept per generation** — Complex multi-scene compositions work less reliably. Break them into separate generations and compose with editing tools.
|
|
205
|
-
|
|
206
|
-
7. **Character consistency** — If features drift after multiple edits, restart the conversation with a detailed character description upfront.
|
|
207
|
-
|
|
208
|
-
8. **Aspect ratio matters** — Choose deliberately based on final use. The model preserves input image ratio during editing. Be explicit if you need a specific ratio.
|
|
209
|
-
|
|
210
|
-
## Workflow
|
|
211
|
-
|
|
212
|
-
When the user asks for an image:
|
|
213
|
-
|
|
214
|
-
1. **Understand intent** — What is this image for? What style/mood? What will it be used for?
|
|
215
|
-
2. **Ask clarifying questions if needed** — Use AskUserQuestion for ambiguous requests. Ask about style, mood, aspect ratio, specific elements.
|
|
216
|
-
3. **Build the prompt** — Apply the templates above. Be detailed and narrative.
|
|
217
|
-
4. **Choose the right tool** — `generate_image` for new, `edit_image` for modifying existing, `continue_editing` for iterating.
|
|
218
|
-
5. **Set `outputPath`** — If the user specifies where to save, use absolute paths. Otherwise let the MCP use its default directory.
|
|
219
|
-
6. **Execute and review** — After generation, read the image file to show the user the result.
|
|
220
|
-
7. **Iterate** — If the user wants changes, use `continue_editing` with targeted adjustment prompts.
|
|
221
|
-
|
|
222
|
-
## Design System Integration
|
|
223
|
-
|
|
224
|
-
When generating images for a morph-spec project:
|
|
225
|
-
|
|
226
|
-
1. **Read design tokens first:**
|
|
227
|
-
```
|
|
228
|
-
Read: .morph/context/design-system.md
|
|
229
|
-
```
|
|
230
|
-
Extract: background color (`--color-background`), primary (`--color-primary`), accent (`--color-accent`), font pair, aesthetic direction.
|
|
231
|
-
|
|
232
|
-
2. **Inject tokens into prompts:**
|
|
233
|
-
Replace generic color references with actual project values:
|
|
234
|
-
- "dark background" → "dark charcoal background ({--color-background value})"
|
|
235
|
-
- "accent colors" → "{--color-primary value} and {--color-accent value} accents"
|
|
236
|
-
- "premium aesthetic" → "{aesthetic direction from design-system.md}"
|
|
237
|
-
|
|
238
|
-
3. **If `globals.css` exists**, read it for additional token values:
|
|
239
|
-
```
|
|
240
|
-
Grep: "--color-" in src/app/globals.css (or similar)
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
## Assets.md Integration
|
|
244
|
-
|
|
245
|
-
When invoked from the implement phase with an assets.md reference:
|
|
246
|
-
|
|
247
|
-
1. **Read the assets spec:**
|
|
248
|
-
```
|
|
249
|
-
Read: .morph/features/{feature}/1-ui/assets.md
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
2. **For each IMG-NNN entry assigned to the current task:**
|
|
253
|
-
- Use the `Prompt` field as the base prompt
|
|
254
|
-
- Use the `Aspect Ratio` field for the aspectRatio parameter
|
|
255
|
-
- Use the `Output Path` field for the outputPath parameter
|
|
256
|
-
- Apply design system token injection (section above)
|
|
257
|
-
|
|
258
|
-
3. **Generate and verify:**
|
|
259
|
-
```
|
|
260
|
-
mcp__gemini-image-mcp__generate_image({
|
|
261
|
-
prompt: "{enhanced prompt with design tokens}",
|
|
262
|
-
aspectRatio: "{from assets.md}",
|
|
263
|
-
outputPath: "{from assets.md}"
|
|
264
|
-
})
|
|
265
|
-
```
|
|
266
|
-
Read the generated image to verify quality. If it needs refinement, use `continue_editing`.
|
|
267
|
-
|
|
268
|
-
## Output Path Conventions
|
|
269
|
-
|
|
270
|
-
- **morph-spec projects:** `public/images/{feature}/{purpose}.png`
|
|
271
|
-
- **Custom path:** Use `outputPath` parameter with absolute path
|
|
272
|
-
- **Default:** saves to `generated-images/` in the MCP project root
|
|
273
|
-
- Supported formats: PNG (most common), JPG, WEBP, GIF, BMP
|
|
274
|
-
|
|
275
|
-
## Model Options
|
|
276
|
-
|
|
277
|
-
The default model is `gemini-2.5-flash-image`. Alternative models can be specified via the `model` parameter:
|
|
278
|
-
- `gemini-2.5-flash-image` — Default, fastest, great quality
|
|
279
|
-
- `gemini-3.1-flash-image-preview` — Newer, supports up to 14 reference images
|
|
280
|
-
- `gemini-3-pro-image-preview` — Higher quality, slower
|
|
281
|
-
|
|
282
|
-
## Common Pitfalls to Avoid
|
|
283
|
-
|
|
284
|
-
- **Keyword spam** — "beautiful, stunning, amazing, 4k, ultra HD, masterpiece" adds noise, not quality
|
|
285
|
-
- **JSON prompts** — The model does not support structured output; natural language only
|
|
286
|
-
- **Negative prompts with "no/don't"** — Frame what you want, not what you don't want
|
|
287
|
-
- **Vague lighting** — "good lighting" means nothing; specify the type
|
|
288
|
-
- **Missing context** — Always explain what the image is for (social media, website, print, etc.)
|
|
289
|
-
- **Too many subjects** — Keep compositions focused; split complex scenes into steps
|
|
290
|
-
- **Forgetting aspect ratio** — Always set it deliberately based on the intended use
|