mirror of
https://github.com/garrytan/gstack.git
synced 2026-05-18 18:32:28 +08:00
feat: DESIGN_SETUP + DESIGN_MOCKUP template resolvers
Add generateDesignSetup() and generateDesignMockup() to the existing design.ts resolver file. Add designDir to HostPaths (claude + codex). Register DESIGN_SETUP and DESIGN_MOCKUP in the resolver index. DESIGN_SETUP: $D binary discovery (mirrors $B browse setup pattern). Falls back to DESIGN_SKETCH if binary not available. DESIGN_MOCKUP: full visual exploration workflow template — construct brief from DESIGN.md context, generate 3 variants, open comparison board in Chrome, poll for user feedback, save approved mockup to docs/designs/, generate HTML wireframe for implementation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -719,3 +719,132 @@ ${slopItems}
|
|||||||
|
|
||||||
Source: [OpenAI "Designing Delightful Frontends with GPT-5.4"](https://developers.openai.com/blog/designing-delightful-frontends-with-gpt-5-4) (Mar 2026) + gstack design methodology.`;
|
Source: [OpenAI "Designing Delightful Frontends with GPT-5.4"](https://developers.openai.com/blog/designing-delightful-frontends-with-gpt-5-4) (Mar 2026) + gstack design methodology.`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function generateDesignSetup(ctx: TemplateContext): string {
|
||||||
|
return `## DESIGN SETUP (run this check BEFORE any design mockup command)
|
||||||
|
|
||||||
|
\`\`\`bash
|
||||||
|
_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
|
||||||
|
D=""
|
||||||
|
[ -n "$_ROOT" ] && [ -x "$_ROOT/${ctx.paths.localSkillRoot}/design/dist/design" ] && D="$_ROOT/${ctx.paths.localSkillRoot}/design/dist/design"
|
||||||
|
[ -z "$D" ] && D=${ctx.paths.designDir}/design
|
||||||
|
if [ -x "$D" ]; then
|
||||||
|
echo "DESIGN_READY: $D"
|
||||||
|
else
|
||||||
|
echo "DESIGN_NOT_AVAILABLE"
|
||||||
|
fi
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
If \`DESIGN_NOT_AVAILABLE\`: skip visual mockup generation and fall back to the
|
||||||
|
existing HTML wireframe approach (\`DESIGN_SKETCH\`). Design mockups are a
|
||||||
|
progressive enhancement, not a hard requirement.
|
||||||
|
|
||||||
|
If \`DESIGN_READY\`: the design binary is available for visual mockup generation.
|
||||||
|
Commands:
|
||||||
|
- \`$D generate --brief "..." --output /path.png\` — generate a single mockup
|
||||||
|
- \`$D variants --brief "..." --count 3 --output-dir /path/\` — generate N style variants
|
||||||
|
- \`$D compare --images "a.png,b.png,c.png" --output /path/board.html\` — comparison board
|
||||||
|
- \`$D check --image /path.png --brief "..."\` — vision quality gate
|
||||||
|
- \`$D iterate --session /path/session.json --feedback "..." --output /path.png\` — iterate`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateDesignMockup(ctx: TemplateContext): string {
|
||||||
|
return `## Visual Design Exploration
|
||||||
|
|
||||||
|
\`\`\`bash
|
||||||
|
_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
|
||||||
|
D=""
|
||||||
|
[ -n "$_ROOT" ] && [ -x "$_ROOT/${ctx.paths.localSkillRoot}/design/dist/design" ] && D="$_ROOT/${ctx.paths.localSkillRoot}/design/dist/design"
|
||||||
|
[ -z "$D" ] && D=${ctx.paths.designDir}/design
|
||||||
|
[ -x "$D" ] && echo "DESIGN_READY" || echo "DESIGN_NOT_AVAILABLE"
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
**If \`DESIGN_NOT_AVAILABLE\`:** Fall back to the HTML wireframe approach below
|
||||||
|
(the existing DESIGN_SKETCH section). Visual mockups require the design binary.
|
||||||
|
|
||||||
|
**If \`DESIGN_READY\`:** Generate visual mockup explorations for the user.
|
||||||
|
|
||||||
|
Generating visual mockups of the proposed design... (say "skip" if you don't need visuals)
|
||||||
|
|
||||||
|
**Step 1: Construct the design brief**
|
||||||
|
|
||||||
|
Read DESIGN.md if it exists — use it to constrain the visual style. If no DESIGN.md,
|
||||||
|
explore wide across diverse directions.
|
||||||
|
|
||||||
|
Assemble a structured brief as a JSON file:
|
||||||
|
\`\`\`bash
|
||||||
|
cat > /tmp/gstack-design-brief.json << 'BRIEF_EOF'
|
||||||
|
{
|
||||||
|
"goal": "<what this screen/page does>",
|
||||||
|
"audience": "<who uses it>",
|
||||||
|
"style": "<visual style from DESIGN.md or from the user's description>",
|
||||||
|
"elements": ["<list>", "<of>", "<key UI elements>"],
|
||||||
|
"constraints": "<any size/layout constraints>",
|
||||||
|
"screenType": "<desktop-dashboard|mobile-app|landing-page|settings|etc>"
|
||||||
|
}
|
||||||
|
BRIEF_EOF
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
**Step 2: Generate 3 variants**
|
||||||
|
|
||||||
|
\`\`\`bash
|
||||||
|
$D variants --brief-file /tmp/gstack-design-brief.json --count 3 --output-dir /tmp/gstack-mockups/
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
This generates 3 style variations of the same brief (~40 seconds total).
|
||||||
|
|
||||||
|
**Step 3: Show the comparison board**
|
||||||
|
|
||||||
|
\`\`\`bash
|
||||||
|
$D compare --images "/tmp/gstack-mockups/variant-A.png,/tmp/gstack-mockups/variant-B.png,/tmp/gstack-mockups/variant-C.png" --output /tmp/gstack-design-board.html
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
Open the comparison board in headed Chrome for user review:
|
||||||
|
|
||||||
|
\`\`\`bash
|
||||||
|
$B goto file:///tmp/gstack-design-board.html
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
Tell the user: "I've generated 3 design directions and opened them in Chrome.
|
||||||
|
Pick your favorite, rate the others, and click Submit when you're done."
|
||||||
|
|
||||||
|
**Step 4: Poll for user feedback**
|
||||||
|
|
||||||
|
Poll the page for the user's submission:
|
||||||
|
|
||||||
|
\`\`\`bash
|
||||||
|
$B eval document.getElementById('status').textContent
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
- If empty: user hasn't submitted yet. Wait 10 seconds and poll again.
|
||||||
|
- If "submitted": read the feedback.
|
||||||
|
- If "regenerate": user wants new variants. Read the regeneration request,
|
||||||
|
generate new variants with the updated brief, and refresh the comparison board.
|
||||||
|
|
||||||
|
When status is "submitted", read the structured feedback:
|
||||||
|
|
||||||
|
\`\`\`bash
|
||||||
|
$B eval document.getElementById('feedback-result').textContent
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
This returns JSON with the user's preferred variant, star ratings, comments,
|
||||||
|
and overall direction.
|
||||||
|
|
||||||
|
**Step 5: Save approved mockup**
|
||||||
|
|
||||||
|
Copy the user's preferred variant to \`docs/designs/\` (create if needed):
|
||||||
|
|
||||||
|
\`\`\`bash
|
||||||
|
mkdir -p docs/designs
|
||||||
|
cp /tmp/gstack-mockups/variant-<PREFERRED>.png docs/designs/<skill>-<description>-$(date +%Y%m%d).png
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
Reference the saved mockup in the design doc or plan.
|
||||||
|
|
||||||
|
**Step 6: Generate HTML wireframe**
|
||||||
|
|
||||||
|
After the mockup is approved, generate an HTML wireframe matching the approved
|
||||||
|
direction using the existing DESIGN_SKETCH approach. The wireframe is what the
|
||||||
|
agent implements from — the mockup is what the human approved.`;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import type { TemplateContext } from './types';
|
|||||||
import { generatePreamble } from './preamble';
|
import { generatePreamble } from './preamble';
|
||||||
import { generateTestFailureTriage } from './preamble';
|
import { generateTestFailureTriage } from './preamble';
|
||||||
import { generateCommandReference, generateSnapshotFlags, generateBrowseSetup } from './browse';
|
import { generateCommandReference, generateSnapshotFlags, generateBrowseSetup } from './browse';
|
||||||
import { generateDesignMethodology, generateDesignHardRules, generateDesignOutsideVoices, generateDesignReviewLite, generateDesignSketch } from './design';
|
import { generateDesignMethodology, generateDesignHardRules, generateDesignOutsideVoices, generateDesignReviewLite, generateDesignSketch, generateDesignSetup, generateDesignMockup } from './design';
|
||||||
import { generateTestBootstrap, generateTestCoverageAuditPlan, generateTestCoverageAuditShip, generateTestCoverageAuditReview } from './testing';
|
import { generateTestBootstrap, generateTestCoverageAuditPlan, generateTestCoverageAuditShip, generateTestCoverageAuditReview } from './testing';
|
||||||
import { generateReviewDashboard, generatePlanFileReviewReport, generateSpecReviewLoop, generateBenefitsFrom, generateCodexSecondOpinion, generateAdversarialStep, generateCodexPlanReview, generatePlanCompletionAuditShip, generatePlanCompletionAuditReview, generatePlanVerificationExec } from './review';
|
import { generateReviewDashboard, generatePlanFileReviewReport, generateSpecReviewLoop, generateBenefitsFrom, generateCodexSecondOpinion, generateAdversarialStep, generateCodexPlanReview, generatePlanCompletionAuditShip, generatePlanCompletionAuditReview, generatePlanVerificationExec } from './review';
|
||||||
import { generateSlugEval, generateSlugSetup, generateBaseBranchDetect, generateDeployBootstrap, generateQAMethodology } from './utility';
|
import { generateSlugEval, generateSlugSetup, generateBaseBranchDetect, generateDeployBootstrap, generateQAMethodology } from './utility';
|
||||||
@@ -36,6 +36,8 @@ export const RESOLVERS: Record<string, (ctx: TemplateContext) => string> = {
|
|||||||
TEST_FAILURE_TRIAGE: generateTestFailureTriage,
|
TEST_FAILURE_TRIAGE: generateTestFailureTriage,
|
||||||
SPEC_REVIEW_LOOP: generateSpecReviewLoop,
|
SPEC_REVIEW_LOOP: generateSpecReviewLoop,
|
||||||
DESIGN_SKETCH: generateDesignSketch,
|
DESIGN_SKETCH: generateDesignSketch,
|
||||||
|
DESIGN_SETUP: generateDesignSetup,
|
||||||
|
DESIGN_MOCKUP: generateDesignMockup,
|
||||||
BENEFITS_FROM: generateBenefitsFrom,
|
BENEFITS_FROM: generateBenefitsFrom,
|
||||||
CODEX_SECOND_OPINION: generateCodexSecondOpinion,
|
CODEX_SECOND_OPINION: generateCodexSecondOpinion,
|
||||||
ADVERSARIAL_STEP: generateAdversarialStep,
|
ADVERSARIAL_STEP: generateAdversarialStep,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export interface HostPaths {
|
|||||||
localSkillRoot: string;
|
localSkillRoot: string;
|
||||||
binDir: string;
|
binDir: string;
|
||||||
browseDir: string;
|
browseDir: string;
|
||||||
|
designDir: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const HOST_PATHS: Record<Host, HostPaths> = {
|
export const HOST_PATHS: Record<Host, HostPaths> = {
|
||||||
@@ -13,12 +14,14 @@ export const HOST_PATHS: Record<Host, HostPaths> = {
|
|||||||
localSkillRoot: '.claude/skills/gstack',
|
localSkillRoot: '.claude/skills/gstack',
|
||||||
binDir: '~/.claude/skills/gstack/bin',
|
binDir: '~/.claude/skills/gstack/bin',
|
||||||
browseDir: '~/.claude/skills/gstack/browse/dist',
|
browseDir: '~/.claude/skills/gstack/browse/dist',
|
||||||
|
designDir: '~/.claude/skills/gstack/design/dist',
|
||||||
},
|
},
|
||||||
codex: {
|
codex: {
|
||||||
skillRoot: '$GSTACK_ROOT',
|
skillRoot: '$GSTACK_ROOT',
|
||||||
localSkillRoot: '.agents/skills/gstack',
|
localSkillRoot: '.agents/skills/gstack',
|
||||||
binDir: '$GSTACK_BIN',
|
binDir: '$GSTACK_BIN',
|
||||||
browseDir: '$GSTACK_BROWSE',
|
browseDir: '$GSTACK_BROWSE',
|
||||||
|
designDir: '$GSTACK_DESIGN',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user