Split Pencil File
Split a large Pencil .pen file into reusable per-frame folders.
Use When
- A
.fig file has been imported into Pencil and saved as one large .pen
- The user wants each top-level Layer/Frame as a separate Pencil file
- The user wants to split the current Pencil selection exactly
- The user describes selected modules, selected areas, or selected regions and expects exactly those selected Pencil nodes to be split
- The desired output is:
output-dir/<frame-name>/index.pen
- copied local image assets beside that
index.pen
- The user wants repeated
Container or duplicate names preserved with numbered folders
Do Not Use
- For editing the visual design itself
- For splitting every nested text, icon, button, or child layer
- For source Figma files that have not been imported into Pencil yet
- When the user needs multi-page Figma import behavior confirmed in Figma rather than in the
.pen file
Core Rules
- Confirm the real
.pen structure before splitting. Do not assume Page or Frame structure.
- Split only top-level
frame nodes by default.
- If the user says "current selection", "selected modules", "selected areas", or "selected regions", use Pencil MCP
get_editor_state first and pass selected frame ids to --node-ids.
- Do not convert current Pencil selections into rectangular bounds. A Pencil selection can contain multiple independent frames; splitting by node id preserves the exact selection intent.
- Skip non-frame top-level nodes such as background rectangles and annotation groups unless the user explicitly asks otherwise.
- Preserve each frame node as-is; do not rewrite child layout, styles, text, image references, ids, or coordinates.
- Sanitize folder names for the local filesystem and append
-2, -3, etc. for duplicates.
- Copy only local image files referenced by each frame into that frame folder.
- If the
.pen contains multiple pages, select the requested page by exact name or index before splitting. If no target page is clear, ask before proceeding.
Script
Use the bundled script for deterministic splitting:
node skills/split-pencil-file/scripts/split-pencil-file.js \
--input dev_design/source.pen \
--output-dir dev_design \
--node-ids frameA,frameB \
--force
Useful options:
--input <file>: required source .pen
--output-dir <dir>: output root; defaults to the source file directory
--page <name-or-index>: target page when the .pen has a top-level pages array
--node-ids <id,id>: only split top-level frames whose ids are in this comma-separated list; output order follows the id order
--force: overwrite existing generated frame folders
--try-run: print the plan without writing files
Current Pencil selection
When the user asks to split the current Pencil selection:
- Use Pencil MCP
get_editor_state on the active editor.
- Confirm the active editor file matches the requested
.pen path.
- Read selected element ids from
Selected Elements.
- Pass top-level selected frame ids to
--node-ids.
- If the selection includes nested child nodes or non-frame nodes, inspect with
batch_get or jq; split only top-level frames unless the user explicitly asks otherwise.
Example:
node skills/split-pencil-file/scripts/split-pencil-file.js \
--input dev_design/source.pen \
--output-dir dev_design/selected \
--node-ids rRd0Z,zVChy \
--try-run
Workflow
Inspect the source:
jq -r 'keys_unsorted[]' <source.pen>
jq -r '.children | to_entries[] | "\(.key)\t\(.value.type)\t\(.value.id)\t\(.value.name // "")"' <source.pen>
If the file has pages, inspect pages and pick the target page explicitly:
jq -r '.pages | to_entries[] | "\(.key)\t\(.value.name // "")\t\((.value.children // []) | length)"' <source.pen>
For current Pencil selection, read selected ids with Pencil MCP and run with --node-ids.
Run the script with --try-run if the output structure or count is uncertain.
Run the script for real.
Validate:
- generated folder count equals the number of selected top-level frames
- every folder has
index.pen
- every
index.pen parses as JSON and contains one top-level frame
- local image references exist beside their
index.pen
Report:
- output root
- generated frame count
- selected node ids if used
- skipped non-frame top-level nodes
- manifest path
- any copied image assets
Output
The script writes:
<output-dir>/
<frame-name>/
index.pen
image-import.png
<frame-name-2>/
index.pen
frame-split-manifest.json
The manifest records selected node ids, original frame names, ids, output directories, index.pen paths, copied images, filtered frames, and skipped top-level nodes.
1---2name: split-pencil-file3description: Split an imported Pencil `.pen` design file into one folder per top-level Frame, including exact current Pencil selections by node IDs, each containing `index.pen` and local image assets referenced by that frame.4---56# Split Pencil File78Split a large Pencil `.pen` file into reusable per-frame folders.910## Use When1112- A `.fig` file has been imported into Pencil and saved as one large `.pen`13- The user wants each top-level Layer/Frame as a separate Pencil file14- The user wants to split the current Pencil selection exactly15- The user describes selected modules, selected areas, or selected regions and expects exactly those selected Pencil nodes to be split16- The desired output is:17 - `output-dir/<frame-name>/index.pen`18 - copied local image assets beside that `index.pen`19- The user wants repeated `Container` or duplicate names preserved with numbered folders2021## Do Not Use2223- For editing the visual design itself24- For splitting every nested text, icon, button, or child layer25- For source Figma files that have not been imported into Pencil yet26- When the user needs multi-page Figma import behavior confirmed in Figma rather than in the `.pen` file2728## Core Rules2930- Confirm the real `.pen` structure before splitting. Do not assume Page or Frame structure.31- Split only top-level `frame` nodes by default.32- If the user says "current selection", "selected modules", "selected areas", or "selected regions", use Pencil MCP `get_editor_state` first and pass selected frame ids to `--node-ids`.33- Do not convert current Pencil selections into rectangular bounds. A Pencil selection can contain multiple independent frames; splitting by node id preserves the exact selection intent.34- Skip non-frame top-level nodes such as background rectangles and annotation groups unless the user explicitly asks otherwise.35- Preserve each frame node as-is; do not rewrite child layout, styles, text, image references, ids, or coordinates.36- Sanitize folder names for the local filesystem and append `-2`, `-3`, etc. for duplicates.37- Copy only local image files referenced by each frame into that frame folder.38- If the `.pen` contains multiple pages, select the requested page by exact name or index before splitting. If no target page is clear, ask before proceeding.3940## Script4142Use the bundled script for deterministic splitting:4344```bash45node skills/split-pencil-file/scripts/split-pencil-file.js \46 --input dev_design/source.pen \47 --output-dir dev_design \48 --node-ids frameA,frameB \49 --force50```5152Useful options:5354- `--input <file>`: required source `.pen`55- `--output-dir <dir>`: output root; defaults to the source file directory56- `--page <name-or-index>`: target page when the `.pen` has a top-level `pages` array57- `--node-ids <id,id>`: only split top-level frames whose ids are in this comma-separated list; output order follows the id order58- `--force`: overwrite existing generated frame folders59- `--try-run`: print the plan without writing files6061### Current Pencil selection6263When the user asks to split the current Pencil selection:64651. Use Pencil MCP `get_editor_state` on the active editor.662. Confirm the active editor file matches the requested `.pen` path.673. Read selected element ids from `Selected Elements`.684. Pass top-level selected frame ids to `--node-ids`.695. If the selection includes nested child nodes or non-frame nodes, inspect with `batch_get` or `jq`; split only top-level frames unless the user explicitly asks otherwise.7071Example:7273```bash74node skills/split-pencil-file/scripts/split-pencil-file.js \75 --input dev_design/source.pen \76 --output-dir dev_design/selected \77 --node-ids rRd0Z,zVChy \78 --try-run79```8081## Workflow82831. Inspect the source:8485 ```bash86 jq -r 'keys_unsorted[]' <source.pen>87 jq -r '.children | to_entries[] | "\(.key)\t\(.value.type)\t\(.value.id)\t\(.value.name // "")"' <source.pen>88 ```89902. If the file has `pages`, inspect pages and pick the target page explicitly:9192 ```bash93 jq -r '.pages | to_entries[] | "\(.key)\t\(.value.name // "")\t\((.value.children // []) | length)"' <source.pen>94 ```95963. For current Pencil selection, read selected ids with Pencil MCP and run with `--node-ids`.974. Run the script with `--try-run` if the output structure or count is uncertain.985. Run the script for real.996. Validate:100 - generated folder count equals the number of selected top-level frames101 - every folder has `index.pen`102 - every `index.pen` parses as JSON and contains one top-level frame103 - local image references exist beside their `index.pen`1047. Report:105 - output root106 - generated frame count107 - selected node ids if used108 - skipped non-frame top-level nodes109 - manifest path110 - any copied image assets111112## Output113114The script writes:115116```text117<output-dir>/118 <frame-name>/119 index.pen120 image-import.png121 <frame-name-2>/122 index.pen123 frame-split-manifest.json124```125126The manifest records selected node ids, original frame names, ids, output directories, `index.pen` paths, copied images, filtered frames, and skipped top-level nodes.