Interactive design reasoning and .pen file creation using Pencil
Config check: Before anything else, verify .claude/config.json exists by reading it. If the file does not exist, stop immediately and tell the user:
"ccflow is not configured for this project. Run /ccflow:configure first to set up."
Read .claude/config.json.
Pencil gating: Check pencil.enabled in .claude/config.json. If pencil is absent or pencil.enabled is not , and tell the user:
"Pencil design workflows are not enabled for this project. Run and enable Pencil when prompted."
true/ccflow:configureRead pencil.designPath from the config to determine where design files belong. If the project is a monorepo with pencil.shared: false, determine the per-project designPath from the affected project's entry in the projects array.
Read pencil.mode from .claude/config.json and store as $PENCIL_MODE. Default: "editor" if absent.
Convention: All Pencil tool calls in this skill follow $PENCIL_MODE:
"cli-app" (default for new installs): Execute tool calls via pencil interactive -a desktop heredoc using the Bash tool. Multiple independent commands can be batched in a single heredoc.
pencil interactive -a desktop <<'EOF'
tool_name({ key: value })
another_tool({ key: value })
EOF
Split into separate heredoc invocations at decision boundaries — where you need to read output before choosing the next action.
"editor" (legacy MCP fallback): Call the equivalent mcp__pencil__<tool> MCP tool directly (e.g., mcp__pencil__batch_design). One tool call per invocation.
Special cases in CLI mode:
| Operation | CLI mode | Editor (MCP) mode |
|---|---|---|
| Screenshots | Use export_nodes({ nodeIds: [...], outputDir: "<path>", format: "png" }) — writes to disk. Then Read the exported PNG with the Read tool. | Use get_screenshot(nodeId) — returns image inline. |
| Batch reads | Combine multiple batch_get + get_variables calls in one heredoc | One MCP call per tool |
| Batch writes | Combine multiple batch_design calls in one heredoc (when independent) | One MCP call per tool |
When this skill says "Call <tool_name>(...)", execute it according to $PENCIL_MODE. Explicit CLI/MCP examples are only given where the modes diverge.
Before parsing arguments, verify that Pencil is reachable.
CLI-app mode ($PENCIL_MODE is "cli-app"):
pencil interactive -a desktop <<'EOF'
get_editor_state({ include_schema: false })
EOF
$PENCIL_OPEN_DOC (set to the file path string if a document is open, or empty if no document is open). Proceed to argument parsing.pencil & to launch Pencil in the background, then retry the probe up to 3 times with 3-second pauses between attempts.
Editor mode ($PENCIL_MODE is "editor"):
get_editor_state(include_schema: false) as an MCP connectivity probe.$PENCIL_OPEN_DOC. Proceed to argument parsing.which pencil 2>/dev/null to check if the pencil command is available.
b. If found: Run pencil & to launch Pencil in the background, then retry get_editor_state(include_schema: false) up to 3 times with 3-second pauses between attempts.
pencil command is not in PATH. Either:pencil command from within the Pencil app (File → Install pencil command into PATH) for auto-launch support."
Stop.Parse $ARGUMENTS — Mode Detection:
Extract the first whitespace-delimited token from $ARGUMENTS and determine the mode:
If the first token matches ^\d+$ or ^#\d+$ → ticket mode
# prefix to get the numeric ticket ID.#1 focus on layout → ID 1, context focus on layout; 7 → ID 7, no context.Otherwise → ticketless mode
$ARGUMENTS string is the design description.If ticket mode: Fetch the ticket:
Shell rules: Read the shell-rules skill before running any gh commands (covers heredoc temp-file pattern).
Extract owner/repo from git remote get-url origin (e.g. [email protected]:owner/repo.git → owner/repo), then run:
gh issue view <number> --repo <owner>/<repo> --json number,title,body,labels,state,assignees,milestone,comments
Read the ticket body and look for a Design Direction section (produced by /ccflow:refine for frontend tickets). Store it for use in Phase 2.
If ticketless mode: Skip ticket fetching. The design description from $ARGUMENTS is the primary input.
Read .claude/rules/lessons-learned.md for any entries related to design or this feature area.
If ticketless mode: Skip this section entirely and proceed to Phase 2.
If ticket mode: Read the attachments reference skill and follow its 4-step procedure to discover, present, download, and load ticket attachments. If no attachments are found or the user selects none, proceed to Phase 2.
This is the forced reasoning phase. Do not create or modify any .pen files yet.
Based on the ticket description (or design description in ticketless mode), classify what needs designing:
| Type | Examples |
|---|---|
| screen/page | Settings page, profile page, checkout flow |
| component | Date picker, card, notification banner |
| dashboard | Analytics dashboard, admin panel |
| landing-page | Marketing page, product page, hero section |
| form/wizard | Multi-step form, signup wizard, onboarding |
| slides/presentation | Pitch deck, project update, onboarding slides |
Call get_guidelines with the topic most relevant to the classification:
| Design Type | Guideline Topic |
|---|---|
| landing-page | landing-page |
| dashboard, screen/page, form/wizard | design-system |
| component | design-system |
| slides/presentation | slides |
get_guidelines({ category: "style" }) to list available stylesget_guidelines({ category: "style", name: "<selected-style>" }) to load the full style definition (pass any required params if the style requests them)Ask questions one at a time using AskUserQuestion. Propose specific answers rather than asking open-ended questions. Limit to 3–5 questions total. Skip any question already answered by the ticket's Design Direction section.
Question 1 — Scope validation:
"Based on [the ticket / your description], I'll design [specific thing] containing [proposed elements]. Does this match your expectations?"
Options: "Yes, that's right", "Adjust scope" (+ description field)
Question 2 — Design system discovery:
.pen file path in $ARGUMENTS, skip scanning and use that file directly.designPath for existing .pen files using Glob (<designPath>/*.pen)..pen files found in designPath, fall back to a repo-wide scan: Glob (**/*.pen).Then:
.pen files found → designing from scratch. Mention this to the user..pen file found → propose using it: "Found existing design file <path>. Should I use its components as the design system?".pen files found → present via AskUserQuestion:
"Found N design files. Which should I use as the design system (or start fresh)?" Options: one per
.penfile path, plus "Start fresh (no design system)"
If using an existing .pen file:
$PENCIL_OPEN_DOC is empty (no document currently open) → call open_document with the .pen file path, then read its reusable components with batch_get using {reusable: true} to understand what's available.$PENCIL_OPEN_DOC is set (a document is already open) → do NOT call open_document (calling it with an editor already open spawns a new Pencil instance and disconnects the MCP server). Instead, ask the user via AskUserQuestion:
"Pencil already has
<$PENCIL_OPEN_DOC>open. Please switch to<target .pen file>in Pencil (File → Open), then confirm here." Options: "Done, file is open", "Cancel"
get_editor_state(include_schema: false) to confirm the correct file is now open and update $PENCIL_OPEN_DOC. Then read its reusable components with batch_get using {reusable: true}.Question 3 — Visual direction:
If the ticket has a Design Direction section from /ccflow:refine, propose using it:
"The ticket specifies this design direction: [summary]. I'll follow this. Any adjustments?"
If no Design Direction exists, propose a direction from the style guide:
"Based on the style guide, I'd suggest [specific aesthetic tone, e.g., 'editorial with high-contrast typography and generous whitespace']. Does this work, or do you have a different direction?"
Options: "Use this direction", "Different direction" (+ description field)
Question 4 — Screen states (conditional — only for screens/pages/forms):
"Which states should I design? I'd suggest [empty, populated, error] at minimum."
Options (multiSelect=true): "Empty state", "Populated / default", "Error state", "Loading state"
Question 5 — Responsive (conditional — only for screens/pages/landing pages):
"Desktop only, or should I also design for mobile/tablet?"
Options: "Desktop only", "Desktop + Mobile", "Desktop + Tablet + Mobile"
After all design questions are answered, create a feature branch before any file creation or modification.
Why not a worktree? Pencil resolves file paths within its editor. Worktree paths (.worktrees/<name>/...) can cause issues with saves, autosaves, and MCP file operations. The design skill works directly in the main worktree on a feature branch instead.
git rev-parse HEAD 2>/dev/null
If this fails (no commits exist), create an initial commit:
git add -A && git commit -m "chore: initial commit" --allow-empty
git checkout -b feature/<ticket-id>-design
git checkout -b feature/<auto-slug>-design
mkdir -p <designPath>
Now create the design using Pencil tools. All file paths in this phase must be absolute paths within the repository root.
.pen FileFirst, call get_editor_state(include_schema: false) and update $PENCIL_OPEN_DOC from the response (the document may have changed since Phase 0.5, e.g., user switched files during Phase 2).
If $PENCIL_OPEN_DOC is empty (no document currently open):
.pen file was selected in Phase 2 → call open_document with the absolute path of the .pen file (e.g., <repo-root>/<designPath>/<file>.pen). Use get_editor_state to confirm.open_document with "new" to create a new empty document. After creation, the file will be saved to <designPath>.If $PENCIL_OPEN_DOC is set (a document is already open):
.pen file path or "new" (if designing from scratch).$PENCIL_OPEN_DOC already matches the target file path → no action needed, proceed.open_document (calling it with an editor already open spawns a new Pencil instance and breaks the connection). Ask the user via AskUserQuestion:
"Pencil already has
<$PENCIL_OPEN_DOC>open. I need to open<target file or 'a new document'>instead. Please close the current file in Pencil (File → Close) or switch to the target file (File → Open), then confirm here." Options: "Done, ready to proceed", "Cancel design"
get_editor_state(include_schema: false) to verify. Update $PENCIL_OPEN_DOC.
open_document with the target path or "new".open_document.Important (editor mode only): Pass the explicit filePath parameter pointing into the repository for all subsequent Pencil MCP tool calls. In CLI mode, the file path is managed by the Pencil desktop app and does not need to be passed explicitly.
Call get_editor_state with include_schema: true to understand the document structure and schema.
If a design system file was selected:
batch_get with patterns: [{reusable: true}] and readDepth: 2 to discover all reusable componentsUse batch_design to create the design. Follow these rules:
batch_design call — split larger designs into multiple calls by logical section (e.g., header first, then content area, then footer)type: "ref")find_empty_space_on_canvas when positioning new screens to avoid overlapping existing contentG() operation where needed (hero images, avatars, illustrations)set_variables if creating a new design system or extending an existing oneBuild order:
If the user requested responsive designs:
For each screen/component created:
export_nodes to save screenshots to disk, then Read the exported PNG:
pencil interactive -a desktop <<'EOF'
export_nodes({ nodeIds: ["<node-id>"], outputDir: "$WORKTREE_PATH/<designPath>/screenshots", format: "png" })
snapshot_layout({ parentId: "<node-id>", problemsOnly: true })
EOF
Then: Read("$WORKTREE_PATH/<designPath>/screenshots/<node-id>.png") to view and analyze.get_screenshot(nodeId) to receive the image inline.snapshot_layout output (captured alongside the screenshot) for programmatic layout problemsbatch_design callsAfter validation passes, present the design to the user via AskUserQuestion:
"Here's the design for [description]. I've verified alignment, readability, and completeness. What do you think?"
Options:
If "Request Changes":
AskUserQuestion if the user didn't specify inline)batch_designIf "Start Over":
Only proceed to Phase 5 after the user selects "Approve".
After the user approves the design in Phase 4, generate a DESIGN.md spec that documents the design for implementation.
In CLI mode, batch all reads into a single invocation:
pencil interactive -a desktop <<'EOF'
batch_get({ patterns: [{ name: "Screen/.*" }] })
batch_get({ patterns: [{ reusable: true }], readDepth: 2 })
batch_get({ patterns: [{ name: "Note:.*" }] })
get_variables()
EOF
In editor mode, call each tool separately via MCP.
Parse the output into:
Screen/.* results — extract name, node ID. Derive route from the screen name (e.g., Screen/training-plan → /training-plan). Add a brief description based on the screen content.reusable: true results — extract name, node ID. Derive the framework component name from the Pencil component name (e.g., Component/ExerciseCard → ExerciseCardComponent for Angular, ExerciseCard for React). Determine UI library usage (e.g., PrimeNG, Material UI, custom) from component structure. Note which screens use each component.Note:.* results — extract name, node ID, and topic from the note content.get_variables() — categorize variables into Colors, Typography, Radii, and Spacing. Map each to a CSS custom property name (e.g., $bg-card → --bg-card).Read stack.frontend (or per-project stack) from .claude/config.json to determine:
<Name>Component for Angular, <Name> for React)Use the template at ${CLAUDE_PLUGIN_ROOT}/templates/design-spec.md as the base. Fill all parameterized sections with extracted data:
<design-path> with the configured designPath<pen-file-name> with the actual .pen file name<framework> and select the matching Components table variant<designPath>/DESIGN.mdIf DESIGN.md already exists at that path, ask the user via AskUserQuestion:
"A DESIGN.md already exists at
<designPath>/DESIGN.md. What should I do?"
Options: "Overwrite with new spec", "Merge (add new entries, keep existing)"
If ticketless mode: Skip this step.
If ticket mode: Append a ### Design Reference section to the ticket body:
gh issue edit <number> --repo <owner>/<repo> --body "$UPDATED_BODY"
Where the updated body appends:
### Design Reference
- Design file: `<designPath>/<pen-file-name>`
- Design spec: `<designPath>/DESIGN.md`
Summarize what was created:
.pen file path(s) created or modifiedDESIGN.md path, screen count, component count, token countInclude this note at the end of the report:
"Note: The design file remains open in Pencil. Close it manually when done reviewing."
If ticketless mode: Skip this.
If ticket mode: Before starting design work (at the beginning of Phase 2), add the "Working" label:
gh issue edit <number> --repo <owner>/<repo> --add-label "Working"
After Phase 5 reporting is complete, create a pull request containing the design artifacts.
mkdir -p $WORKTREE_PATH/<designPath>/screenshots
CLI mode: Export all screens in a single call:
pencil interactive -a desktop <<'EOF'
export_nodes({ nodeIds: ["<id1>", "<id2>", ...], outputDir: "$WORKTREE_PATH/<designPath>/screenshots", format: "png" })
EOF
Files are written as <node-id>.png. Rename them to human-readable names:
mv $WORKTREE_PATH/<designPath>/screenshots/<node-id>.png $WORKTREE_PATH/<designPath>/screenshots/<screen-name>.png
Editor mode: Export all screens via the export_nodes MCP tool:
Call export_nodes with filePath, nodeIds (all screen/component IDs), outputDir: "$WORKTREE_PATH/<designPath>/screenshots", and format: "png". Files are written as <node-id>.png. Rename them to human-readable names:
mv $WORKTREE_PATH/<designPath>/screenshots/<node-id>.png $WORKTREE_PATH/<designPath>/screenshots/<screen-name>.png
Slides/presentation PDF export: If the design type is slides/presentation, also export a combined PDF:
export_nodes({ nodeIds: [<all-slide-ids>], outputDir: "$WORKTREE_PATH/<designPath>/screenshots", format: "pdf" }) — all slides are combined into a single multi-page PDF.export_nodes with format: "pdf" and all slide node IDs. The tool combines them into one PDF document.Both modes: If screenshots cannot be saved to files, prepare textual descriptions of each screen for the PR body instead. For each screen, write a brief textual description (2–3 sentences) covering layout, key elements, and visual style — these go in the PR body regardless of whether image files are available.
Stage and commit all design artifacts:
git add <designPath>/ && git commit -m "feat(design): <description>"
#<ticket-id>Push the branch to the remote:
git push -u origin feature/<branch-name>
If push fails (sandbox network restriction or auth issue):
Shell rules: Use the temp-file pattern from the shell-rules skill — no heredocs.
printf '%s' '<pr-body-content>' > /tmp/claude/design-pr-body.md
PR body template:
## Summary
<1-3 bullet points describing the design>
## Ticket
<ticket-mode only: Closes #<ticket-id>>
## Design Files
- Design file: `<designPath>/<pen-file-name>`
- Design spec: `<designPath>/DESIGN.md`
## Design Preview
<For each screen: textual description. If screenshot files were committed, also include:>

## Screens Designed
<Bulleted list of each screen/component with brief description>
## Design Decisions
<Key choices: aesthetic tone, color palette, typography, layout approach, component library>
## Notes
<Any caveats, open questions, or implementation guidance>
🎨 Generated with [Claude Code](https://claude.com/claude-code) using ccflow design skill
BODY=$(cat /tmp/claude/design-pr-body.md) && gh pr create --title "feat(design): <short-description>" --body "$BODY" --repo <owner>/<repo>
If ticketless mode: Skip labeling.
If ticket mode: Replace "Working" with "Designed":
gh issue edit <number> --repo <owner>/<repo> --add-label "Designed" --remove-label "Working"
gh pr create command for the user to run manuallyAskUserQuestion: "Branch feature/<name> already exists. Reuse it or recreate?" Options: "Reuse existing (checkout)", "Delete and recreate"Switch back to main:
git checkout main
Report the PR URL to the user. Then STOP. Do not:
/implement or start implementation/ccflow:implement when readyTell the user: "Design PR created: <PR-URL>. Run /ccflow:implement <ticket-id> when ready to implement."