Theme Factory Docs
Complete reference for the Theme Factory CLI — installation, commands, task format, super agent, and TF rules.
Getting Started
Prerequisites
| Tool | Version | Purpose |
|---|---|---|
| Node.js | > 20 | Runtime for the pipeline |
| Shopify CLI | Latest | Theme pushing and validation |
| Git | Any | Repo cloning and branch management (only for # GIT repo tasks — not needed for local # Project Name runs) |
| Claude | See below | Powers all AI agents |
Claude Authentication
Theme Factory agents run via the Claude Agent SDK. You need one of:
- Local (recommended): Claude Code installed and logged in — uses your Claude subscription
- Cloud / CI: Set the
ANTHROPIC_API_KEYenvironment variable — uses API billing
$ claude login
Shopify CLI
Install and authenticate the Shopify CLI:
$ npm install -g @shopify/cli
$ shopify auth login --store your-store.myshopify.com
Git Access
Your machine needs SSH or HTTPS access to the GitHub repos you'll be working with. The pipeline clones repos via the URL in your input file.
Git is only required for # GIT repo tasks. If your task uses # Project Name instead, the run is fully local — no clone, branch, commit, or push — and no Git access is needed. See Git repo vs. local project.
Installation
Follow the installation guide or ask Jarvis for help:
Verify installation:
$ tf --version
$ tf --help
Update to latest stable, or switch release channels:
$ tf update # latest stable
$ tf update alpha # latest alpha release
$ tf update beta # latest beta release
Authentication
Google (Required)
Theme Factory requires Google authentication with an @anattadesign.com account. This is used for identity tracking and telemetry.
$ tf auth login
# Check status
$ tf auth status
# Logout
$ tf auth logout
Figma
Required for Figma-linked tasks. Run the OAuth flow once per user:
$ tf auth figma
This opens your browser to authorize Theme Factory with your Figma account. Tokens auto-refresh — no manual renewal needed.
Shopify Store Access
Required for each Shopify store you work with. No tokens to copy — just install the app and register your email:
- Install the Theme Factory app — Open the Theme Factory install link in your browser and follow the prompts to install the app on your store.
- Add your email — Once installed, open the app's home page in your Shopify admin and add the email you use with Theme Factory. That's it — the CLI is now authorized to work with this store.
Pre-flight check: The pipeline aborts early if a task references a Figma link without Figma auth, or a Shopify store where the Theme Factory app isn't installed for your email.
First Run
1. Create an Input File
Create a markdown file anywhere on your machine:
# GIT repo
https://github.com/your-org/your-theme.git
# Store
your-store.myshopify.com
# Task ID
my-first-task
# Theme Base
Horizon
# Task
Build a hero banner section with a background image,
headline, and CTA button.
No Git repo? Swap the # GIT repo section for a # Project Name to run fully locally — no clone, branch, commit, or push (Shopify push still works):
# Project Name
my-first-task
# Store
your-store.myshopify.com
# Theme Base
Horizon
# Task
Build a hero banner section with a background image,
headline, and CTA button.
2. Run the Pipeline
# Full run — builds, commits, pushes to GitHub + Shopify
$ tf run ./my-first-task.md
# Local only — skip pushing
$ tf run ./my-first-task.md --no-push
# Preview with `shopify theme dev`, then confirm before pushing to Shopify
$ tf run ./my-first-task.md --local
3. Check the Output
- Logs: A log file is created next to your input file
- Git: Changes committed on a feature branch (skipped for
# Project Nameruns) - Shopify: Theme pushed as unpublished (unless
--no-pushor--local)
CLI Reference
Quick Reference
# Authenticate
$ tf auth login
$ tf auth figma
# Run pipeline
$ tf run my-task.md
$ tf run my-batch/ --from 2 --to 4
# Interactive super agent
$ tf chat --latest
$ tf chat --repo my-store-theme
$ tf chat --repo 3
# Split large tasks
$ tf split my-task.md
$ tf split my-task.md --text
# Push changes
$ tf push git my-repo
$ tf push shopify my-repo --store my-store.myshopify.com
# List repos & history
$ tf list
$ tf list my-repo
$ tf list my-repo --logs
# Revert last run
$ tf revert --latest
# Clear session memory or Figma cache
$ tf clear memory --latest
$ tf clear figma --latest
# Delete a repo
$ tf delete my-repo --hard
# Audit & auto-fix translation keys
$ tf translations my-repo
$ tf translations my-repo --fix
# Update CLI (optionally pick a channel)
$ tf update
$ tf update alpha
$ tf update beta
# Manage optional plugins
$ tf plugin list
$ tf plugin install website-flow
$ tf plugin uninstall website-flow
tf auth
Authenticate with external services.
tf auth login
Sign in via Google OAuth. Restricts to @anattadesign.com accounts. Also registers with the TF backend for run telemetry.
$ tf auth login
tf auth status
Show current Google authentication status.
$ tf auth status
tf auth logout
Clear stored Google authentication.
$ tf auth logout
tf auth figma
Run the Figma OAuth flow. Opens your browser to authorize Theme Factory with your Figma account. Tokens auto-refresh — no manual renewal needed.
$ tf auth figma
tf run
Execute a task file or batch directory through the full agent pipeline.
tf run <file-or-dir> [options]
| Option | Required | Description |
|---|---|---|
<path> | Yes | Input markdown file or split directory |
--input <path> | No | Alias for the positional argument |
--branch <name> | No | Override git branch |
--no-push | No | Skip GitHub and Shopify push |
--local | No | Preview the built theme locally with shopify theme dev, then confirm before pushing to Shopify |
--no-shopify-pull | No | Skip syncing customizer changes before agents start |
--from <N> | No | Start from sub-task N (batch mode only) |
--to <N> | No | Stop after sub-task N (batch mode only) |
--restart | No | Force fresh start, ignore previous session state |
--interactive | No | Review and approve the plan before development starts |
--show-browser | No | Show the browser window during website captures (slow-mo enabled, cache bypassed) — for debugging |
Single vs Batch mode is auto-detected: a file runs in single mode, a directory runs in batch mode (sub-tasks sequentially).
Batch Mode
Point tf run at a folder and it will execute every task file inside, one by one, through the full agent pipeline. Useful for splits generated by tf split, or any directory of related task files you want to run as a sequence.
- File selection — all
.mdfiles in the directory are picked up (non-recursive). - Order — files are sorted alphabetically by filename, so prefixes like
01-,02-,03-determine execution order. Name your files accordingly. - Sequential — each sub-task runs to completion before the next one starts. A failure halts the batch.
Use --from and --to to run a slice of the batch (1-indexed, inclusive on both ends):
# Run every task in the folder, in alphabetical order
$ tf run ./my-batch/
# Run only sub-tasks 2 through 4
$ tf run ./my-batch/ --from 2 --to 4
# Start from sub-task 3 and continue to the end
$ tf run ./my-batch/ --from 3
# Run from the beginning up to and including sub-task 5
$ tf run ./my-batch/ --to 5
Auto-resume — In batch mode, completed sub-tasks are tracked. Re-running the same directory skips completed tasks. Use --from 1 to force a full restart.
Debugging website captures
If a website capture step (click, wait_for, etc.) isn't doing what you expect, run with --show-browser:
$ tf run ./my-task.md --show-browser
This launches a visible Chromium window with slowMo: 250 so you can watch each step execute. The website cache is bypassed in this mode so captures always re-run. Useful for diagnosing whether a third-party widget (e.g. a Rebuy cart drawer) is responding to clicks or being blocked by anti-bot detection.
Interactive Mode
When --interactive is set, the pipeline pauses after the Analyzer Agent creates the implementation plan. You can review and provide feedback before development begins.
$ tf run ./my-task.md --interactive
▶ Phase 2/4: Creating implementation plan...
✔ Plan created — 5 file changes (4m 46s)
📋 Plan Review
Enter feedback to revise, or press Enter to approve.
Feedback> move the hero into a snippet instead
▶ Revising implementation plan (revision #1)...
✔ Plan revised — 6 file changes (45s)
Feedback>
✔ Plan approved — proceeding to development
- Conversational revisions — each round continues the same session
- Approve by pressing Enter, or typing
approve,yes,ok,lgtm - Not available in batch mode
tf chat
Interactive super agent — a Claude session with full read/write access to your theme.
tf chat [target] [options]
| Option | Description |
|---|---|
--new | Start a new task from scratch (generates task files) |
--latest | Open super agent on the latest pipeline repo |
--repo <name> | Target a specific repo by name or number from tf list |
--limit <n> | How many recent tasks to show in the picker (default: 10) |
--no-push | Auto-commit but ask before pushing to GitHub |
<target> | Repo name (positional, same as --repo) |
By default, the agent automatically commits and pushes to GitHub after every change. Use --no-push to auto-commit but require confirmation before pushing.
# Super agent on the latest repo
$ tf chat --latest
# Target by name or number
$ tf chat --repo my-store-theme
$ tf chat --repo 3
# Show more recent tasks in the picker
$ tf chat --latest --limit 25
# Auto-commit, ask before push
$ tf chat --latest --no-push
# Create a new task file interactively
$ tf chat --new
tf split
Break a large task into ordered sub-tasks. Defaults to Figma-based splitting.
tf split <file> [--figma|--text] [--new-theme]
| Option | Required | Description |
|---|---|---|
<file> | Yes | Markdown task file to split |
--figma | No | Figma-based split (default) — fetches page structure from the Figma API |
--text | No | Text-based LLM split — analyzes the task description with AI |
--new-theme | No | Include style-guide + global-sections tasks (Figma mode only) |
The split directory is created next to the input file. Inspect the generated tasks, then execute the batch:
$ tf split ./full-redesign.md
$ tf run full-redesign/
tf push
tf push git
Push workspace commits to GitHub.
tf push git <repo> [--branch <name>]
tf push git --latest
tf push shopify
Push theme to a Shopify store. If a theme with the same name already exists, it updates it. Otherwise, it creates a new unpublished theme.
tf push shopify <repo> --store <store> [--name <name>] [--publish]
tf push shopify --latest
| Option | Description |
|---|---|
--store <store> | Shopify store domain (auto-resolved with --latest) |
--name <name> | Display name on Shopify (default: repo name) |
--publish | Publish the theme. Without this flag, the theme is never published. |
--latest | Use the latest pipeline repo |
tf translations
Audit translation key usage in a theme. Scans every .liquid file for {{ 'key' | t }} references, marks each as resolved against locales/en.default.json, and flags Liquid antipatterns that produce visible "Translation missing" errors on the storefront. With --fix, infers English defaults via LLM, rewrites the antipatterns, commits, pushes to git, and deploys to Shopify in one shot.
tf translations <repo> [--missing] [--fix] [--store <store>] [--name <name>] [--no-shopify]
| Option | Description |
|---|---|
<repo> | Repo name or number from tf list |
--missing | Show only missing keys (skip resolved ones) |
--fix | Auto-fill missing keys, rewrite antipatterns, commit + push to git, deploy to Shopify |
--store <store> | Shopify store domain (overrides auto-resolved value from session metadata) |
--name <name> | Theme name on Shopify (overrides auto-resolved value, defaults to repo name) |
--no-shopify | Skip the Shopify deploy step (git push only) |
What --fix does
- Rewrites
| default: '...' | tantipatterns. The chain{{ section.settings.x | default: 'some.key' | t }}looks reasonable but is a bug: when the merchant has set the variable, its value (not the key) gets fed to| t, producingTranslation missing: en.<value>on the storefront. Each occurrence is rewritten to an explicitif/elseso the merchant's value is shown verbatim, falling back to the translated default only when blank. Deterministic — no LLM call. - Fills missing keys by reading 2 lines of context around each
| treference, then asking the configured dev model for short English defaults. Output is cached to.tf-translations-cache.jsonbefore the merge so a failed write never costs you a second LLM call. - Merges into
locales/en.default.jsonwhile preserving CLDR pluralization objects ({ one: "...", other: "..." }) — these are recognized as already-present leaves rather than false-flagged as missing. - Commits with a
chore:prefix, pushes to the current git branch, and deploys to Shopify. Store and theme name are auto-resolved from the most recent session metadata — pass--store/--nameto override, or--no-shopifyto skip the deploy.
# Audit only — print the listing, no changes
$ tf translations my-store-theme
# Show only missing keys
$ tf translations my-store-theme --missing
# Full fix: rewrite antipatterns, fill missing keys, commit, push, deploy
$ tf translations my-store-theme --fix
# Repo number works too
$ tf translations 3 --fix
# Deploy to a different store than the last run used
$ tf translations my-store-theme --fix --store other-store.myshopify.com
# Local cleanup only — skip the Shopify deploy
$ tf translations my-store-theme --fix --no-shopify
tf list
List repos in the workspace, or show run history for a specific repo. Alias: tf ls
tf list
tf list <repo>
tf list --latest
tf list <repo> --logs [N]
| Option | Description |
|---|---|
| (no args) | List all repos in workspace |
<repo> | Show run history (name or number from tf list) |
--latest | Show run history for the last pipeline repo |
--logs [N] | View log file for run #N (default: latest) |
tf delete
Delete a repo from the workspace. Accepts a repo name or number from tf list.
tf delete <repo-or-number> [--hard]
| Option | Description |
|---|---|
<repo-or-number> | Repo name or number from tf list |
--hard | Also delete session data and Figma cache |
Without --hard, only the workspace folder is removed. Session data is preserved so you can re-clone and resume later.
tf revert
Undo the last pipeline run. Resets the branch to its pre-run state, then force-pushes to GitHub and re-pushes to Shopify if they were pushed in the original run.
tf revert --latest [--no-push]
| Option | Description |
|---|---|
--latest | Required — revert the most recent run |
--no-push | Revert locally without pushing to GitHub/Shopify |
Constraints:
- Only the latest run can be reverted
- Only one revert per run
- Reverted runs appear dimmed in
tf listoutput
tf clear
Clear session memory or Figma cache for a repo. Useful when agents are repeating mistakes from stale context, or when Figma designs have changed significantly.
tf clear memory <repo-or-number>
tf clear memory --latest
tf clear figma <repo-or-number>
tf clear figma --latest
| Command | Description |
|---|---|
tf clear memory | Deletes session memory and accumulated learnings for a repo |
tf clear figma | Clears cached Figma data (images, SVGs, component trees) |
When to use:
- Clear memory — when agents keep making the same mistakes based on stale learnings
- Clear figma — when Figma designs have been updated and you need fresh data
tf update
Update Theme Factory to the latest version.
$ tf update
Pulls the latest published version. Valid tags: latest, alpha, beta.
$ tf update # latest stable
$ tf update alpha # latest alpha release
$ tf update beta # latest beta release
tf plugin
Manage optional feature plugins. Each plugin installs its dependencies into an isolated ~/.theme-factory/plugins/<name>/ directory. See the Plugins page for the full list and what each one unlocks.
tf plugin list
tf plugin install <name>
tf plugin uninstall <name>
tf plugin status [<name>]
| Sub-command | Description |
|---|---|
list | List available plugins and their install status |
install <name> | Install a plugin's dependencies (runs npm install + post-install steps) |
uninstall <name> | Remove an installed plugin's directory |
status [<name>] | Show install state for one or all plugins |
$ tf plugin list
$ tf plugin install website-flow
$ tf plugin status website-flow
$ tf plugin uninstall website-flow
The plugin registry ships with tf source — run tf update to fetch newer plugins as they're added.
Running a Task
Task File Format
Tasks are defined as markdown files that tell the pipeline what to build. Each file uses # headings as section keys — ## and deeper are treated as body content within their parent section.
Required Sections
You must provide one of # GIT repo or # Project Name (see Git repo vs. local project below), plus a # Task.
| Section | Aliases | Description |
|---|---|---|
# GIT repo | # Repo, # GitHub repo, # Repository | GitHub repo URL (HTTPS). Cloned, branched, committed, and pushed as usual. |
# Project Name | # Project, # Workspace Name, # Workspace | Local-only alternative to a Git repo. The run happens entirely in a workspace folder of this name — no clone, branch, commit, or push. Provide this or # GIT repo. |
# Task | # Description | What to build or fix |
Optional Sections
| Section | Aliases | Description |
|---|---|---|
# Store | # Shopify store | Shopify store domain |
# Client | # Client name, # Store URL | Client identifier |
# Theme Base | # Base theme, # Base | Base theme to scaffold from (Dawn, Horizon). Supports Name => repo-url override syntax (see below) |
# Figma | # Figma link | Figma URLs (one per line) |
# Task ID | # TaskID, # ID | Unique task identifier (defaults to the input filename) |
# Theme Name | # Theme, # Shopify Theme Name | Name for the Shopify theme (targets an existing theme by name, or names a new one) |
# Theme ID | # Theme-ID, # Shopify Theme ID | Numeric Shopify theme ID to target directly. Takes precedence over # Theme Name if both are set. Omit both to create a new unpublished theme named [TF] <branch>. |
# Git Branch | # Branch | Branch name (auto-generated if not set). Ignored in local-project mode. |
# Base Branch | # From Branch | Branch to cut the new branch from (defaults to repo's default branch). Errors if it doesn't exist locally or on origin |
# Difficulty | # Task Difficulty, # Complexity | easy, medium (default), or hard — tunes how much effort agents spend |
# Commit Message | # Commit, # Commit Msg | Custom description for the run's git commits (first line only — anything after it is ignored). It's inserted into the standard format theme-factory(<feature|fix>): <message> [iteration N]. When absent, the task text is used instead. Ignored in local # Project Name mode (no commits). |
# Acceptance Criteria | # Criteria, # AC | Bullet list the validator checks against |
# Custom Font | # Custom Fonts, # Font, # Fonts | Path to a font folder or individual font file |
# References | # Reference, # Reference Images | Local image paths fed to agents as visual references (label: path, path — caption, or bare path) |
# Website Options | # Website, # URL Options | Live-site captures (screenshots + HTML) fed to agents — requires the website-flow plugin |
Git repo vs. local project
Every task targets a working copy. There are two ways to define it, and you must use exactly one:
# GIT repo— the full Git-backed flow. The pipeline clones the repo, cuts a branch, syncs customizer changes from Shopify, commits the result, and pushes to both GitHub and Shopify.# Project Name— a fully local run, no Git at all. The workspace is~/.theme-factory/workspace/<project name>(no clone). All Git operations are skipped — no branch setup, commits, GitHub push, diff, or the Git-based Shopify customizer sync. A greenfield scaffold runs without the initial commit. The project name is used as the run label (telemetry, run-state id) and as the default Shopify theme name. Shopify push still works — set a# Store(and# Theme Name/# Theme ID) to deploy.
# Project Name
acme-landing
# Store
acme.myshopify.com
# Theme Base
Horizon
# Task
Build a seasonal landing page section with a hero, three
feature cards, and a newsletter signup.
If a file provides neither # GIT repo nor # Project Name, the parser aborts with a "missing required section" error.
Previewing a local run: tf serve requires a Git-backed workspace and will not preview a local-project run. To preview a # Project Name build before pushing, use tf run … --local instead.
Custom Theme Base Repo
If your team uses a modified version of a base theme (e.g., a cloned and customized Horizon), you can override the scaffolding repo while keeping the original theme's rules:
# Theme Base
Horizon => git@github.com:my-org/my-custom-horizon.git
This tells Theme Factory to:
- Apply Horizon's theme rules (schema standards, block patterns, section conventions)
- Scaffold from your custom repo instead of the official Horizon repository
Without the => override, the standard syntax still works:
# Theme Base
Horizon
Task Example
# GIT repo
https://github.com/my-org/my-store.git
# Store
my-store.myshopify.com
# Theme Base
Horizon
# Task ID
homepage-hero
# Theme Name
My Store Theme
# Git Branch
feature/homepage-hero
# Task
Build a full-width hero banner section with:
- Background image from Figma
- Headline and subheadline text
- CTA button linking to a collection
- Mobile-responsive layout
Split Task Example
Same format, but with a larger # Task section. The task splitter decomposes it into sub-tasks automatically.
# GIT repo
https://github.com/my-org/my-store.git
# Store
my-store.myshopify.com
# Theme Base
Horizon
# Git Branch
feature/full-homepage
# Task
Build the complete homepage with these sections:
## Hero Banner
Full-width hero with background image, headline, CTA button.
## Featured Collections
Two collection cards side by side with product previews.
## Testimonials
Customer testimonial carousel with star ratings.
## Footer
Multi-column footer with newsletter signup form.
Figma Split Config
Used with tf split to auto-generate task files from a Figma design. Label each URL with Desktop or Mobile prefix for automatic pairing.
# GIT repo
https://github.com/my-org/my-store.git
# Store
my-store.myshopify.com
# Theme Base
Dawn
# Task ID Prefix
MYSTORE
# Theme Name
my-store-redesign
# Git Branch
feature/full-redesign
# Figma
Desktop Homepage: https://figma.com/design/ABC123/...?node-id=10-100
Mobile Homepage: https://figma.com/design/ABC123/...?node-id=10-200
Desktop PLP: https://figma.com/design/ABC123/...?node-id=20-100
Mobile PLP: https://figma.com/design/ABC123/...?node-id=20-200
| Section | Required | Description |
|---|---|---|
# GIT repo | Yes | GitHub repo URL |
# Task ID Prefix | Yes | Prefix for generated task IDs |
# Figma | Yes | Labeled Figma URLs — Desktop/Mobile pairs |
Figma URLs
Figma URLs can be provided in an explicit # Figma section (one URL per line), or inline in the # Task body (auto-extracted).
https://www.figma.com/design/<fileKey>/<fileName>?node-id=<nodeId>
- With
?node-id=— fetches the specific node tree (recommended) - Without
?node-id=— fetches file-level overview (depth=2, no frame images)
URLs are deduplicated by fileKey:nodeId pair.
Website Options
When you want agents to reference a live site instead of (or in addition to) a Figma design — for example, when redesigning an existing storefront or porting another brand's layout — declare captures under # Website Options. Each capture launches a headless Chromium browser, navigates to the URL, runs a list of steps, and feeds the resulting screenshots + extracted HTML into the agent prompts.
Requires the website-flow plugin. Without it, tasks that use # Website Options abort with tf plugin install website-flow. See the Plugins page.
Format
A capture starts with a name on its own line ending with :. Indented keys configure it:
# Website Options
home-desktop:
url: https://acme.com
viewport: 1440
steps:
- wait: "main"
- dismiss: "#cookie-banner .accept"
- screenshot
- extract
mobile-menu:
url: https://acme.com
viewport: 390
steps:
- click: "button#open-menu"
- wait_for: "#mobile-menu.is-open"
- screenshot: "menu-open"
- extract: "#mobile-menu"
Capture Properties
| Key | Required | Type | Description |
|---|---|---|---|
<name>: | Yes | string | Capture identifier (used as the screenshot label and shown in logs) |
url: | Yes | string | Full URL to navigate to |
viewport: | No | integer | Viewport width in pixels. Default: 1440 |
steps: | No | list | Ordered actions to run on the page. Default: wait body → screenshot → extract |
Captures without a url: are silently dropped.
Step Actions
Each step is a bullet under steps:. Two forms are supported: - action (no argument) or - action: value. String values can be quoted with single or double quotes.
| Action | Form | Description |
|---|---|---|
wait | - wait: <selector> | Wait until the CSS selector appears in the DOM before continuing |
wait_for | - wait_for: <selector> | Alias for wait |
sleep | - sleep: <ms> | Hard pause for N milliseconds. Use sparingly — prefer wait/wait_for on a selector when possible |
click | - click: <selector> | Click the matched element |
hover | - hover: <selector> | Hover the matched element (triggers :hover styles, dropdowns, etc.) |
fill | - fill: "<selector>, <text>" | Type text into an input. Comma separates selector from value |
scroll | - scroll: <pixels-or-bottom> | Scroll to a pixel offset, or pass bottom to scroll to the page end |
dismiss | - dismiss: <selector> | Click to close (cookie banners, modals). Use <iframe-selector> >>> <inner-selector> to pierce into an iframe |
screenshot | - screenshot or - screenshot: <label> | Capture a full-page screenshot. With a label, the file is named accordingly |
extract | - extract or - extract: <selector> | Extract HTML/text. Without a selector, captures the whole page; with one, scopes to that element |
Steps that need a selector (wait, wait_for, click, hover, dismiss) are dropped if the value is missing. Unknown actions are silently ignored.
Defaults
If steps: is omitted entirely, the default sequence runs:
- wait: "body"
- screenshot
- extract
If viewport: is omitted, the browser opens at 1440px wide.
Where the Captures Go
- Screenshots — saved under
~/.theme-factory/.cache/<repo>/website/and surfaced to multimodal agent prompts (the Dev and Analyzer agents see the images directly) - Extracted HTML — formatted into the agent prompt as structured markdown
- Media uploads — when a
# Storeis set, images and videos found in the captured DOM are uploaded to Shopify Files and CDN URLs are substituted into the agent context - Failures are fatal — a missing selector or unreachable URL aborts the run rather than letting agents work against blank capture data
Debugging Captures
When a step doesn't behave as expected (e.g. click doesn't open a drawer, wait_for selector never appears), re-run with --show-browser:
$ tf run ./my-task.md --show-browser
This launches Chromium with the window visible and slowMo: 250 so each step's action is watchable in real time. The website cache is bypassed in this mode so captures always re-run instead of returning a cached result. While the browser is open, you can also open DevTools to inspect the live DOM, confirm selectors, or run document.querySelector(...) checks against the page Playwright is interacting with.
Split Modes
Splitter mode is selected with CLI flags on tf split:
| Flag | Description |
|---|---|
--figma (default) | Figma design analysis — fetches page structure, generates per-section tasks |
--text | Text-based LLM analysis of the task description |
--new-theme | Figma mode with extra style-guide and global-sections tasks (greenfield builds) |
Generated sub-task files are placed in a folder next to the input file. Each inherits shared metadata and gets its own # Task, # Task ID, and # Acceptance Criteria.
Super Agent
What Is It
The Super Agent is an interactive Claude session with full read/write access to your theme. Instead of writing a task file and running the 4-agent pipeline, you talk to Claude directly — describe what you want, paste screenshots, and it edits files, validates, commits, and pushes in real time.
It's the fastest way to make small changes, fix bugs, and iterate on a theme.
# Open super agent on the repo from your last pipeline run
$ tf chat --latest
# Or target a specific repo
$ tf chat --repo my-store-theme
$ tf chat --repo 3
# Auto-commit, ask before push
$ tf chat --latest --no-push
By default, the agent automatically commits and pushes to GitHub after every change. Use --no-push to auto-commit but require confirmation before pushing.
How it differs from tf run:
| tf run (Pipeline) | tf chat (Super Agent) | |
|---|---|---|
| Input | Markdown task file | Conversation |
| Execution | 4-agent pipeline (Context, Analyzer, Dev, Validator) | Single agent, direct file editing |
| Speed | Minutes per iteration | Seconds per edit |
| Screenshots | Not supported | Paste images, agent analyzes and fixes |
| Best for | Large features, new sections | Quick fixes, tweaks, visual debugging |
When to Use
The Super Agent is ideal for smaller, quicker work where the full pipeline would be overkill:
- Visual tweaks — spacing, colors, font sizes, alignment
- Bug fixes — broken layouts, missing elements, CSS issues
- Quick iterations — adjusting what the pipeline built
- Content updates — copy changes, link updates, schema tweaks
- Experimentation — try ideas without writing a formal task
For large features (new sections, full page builds, complex logic), use tf run with a task file — the multi-agent pipeline provides structured planning and validation.
Typical Workflow
# 1. Run the pipeline to build a feature
$ tf run my-task.md
# 2. Open super agent to fix/tweak the result
$ tf chat --latest
# 3. Describe issues or paste screenshots
You: The hero section has too much padding on mobile
# 4. Agent edits the file, validates, and shows preview
✔ Updated sections/hero.liquid — reduced mobile padding
# 5. Use --no-push to keep changes local
$ tf chat --latest --no-push
Fixing with Screenshots
One of the most powerful features — paste or drag a screenshot directly into the terminal. The agent analyzes the image visually, identifies the problem, finds the responsible file, and fixes it.
How it works:
- Take a screenshot of the visual issue in your browser
- Paste it into the terminal (or drag the image file in)
- Optionally describe what's wrong — or let the agent figure it out
- The agent cross-references the screenshot with the actual theme files
- It edits the code, runs
shopify theme check, and pushes
Example: Paste a screenshot showing a misaligned product grid. The agent identifies it's caused by a CSS flex issue in assets/collection.css, fixes the alignment, validates the change, and pushes to Shopify — all in one conversation turn.
This makes the Super Agent especially effective for QA workflows — review the theme in the browser, screenshot any issues, and the agent fixes them one by one.
Capabilities
The Super Agent is loaded with full context about your repo and has access to everything it needs:
What It Can Do
- Read and edit any file — Liquid, CSS, JS, JSON, schemas
- Run theme check —
shopify theme checkfor validation - Commit and push to GitHub — with branch awareness
- Push to Shopify — and show a preview URL after each push
- Fetch Figma data — if authenticated, inspect designs and download images
- Upload images to Shopify — from Figma exports or local files
- Analyze screenshots — visual debugging from pasted images
What It Knows
- Learnings — accumulated knowledge from all previous pipeline runs and chat sessions
- Theme inventory — complete file listing with section schemas and capabilities
- Agent rules — CSS, Liquid, JS, and responsive design standards
- TF rules — client-specific component specs from the repo's
.tf-rules/directory
Revert Safety
Before the Super Agent starts, a revert pointer is saved. If anything goes wrong, tf revert --latest resets to the state before the chat session — not the entire repo history.
# Undo everything the super agent changed
$ tf revert --latest
TF Rules
What Are TF Rules
TF Rules are markdown files that live inside your client's theme repository. They teach Theme Factory about client-specific components, patterns, and requirements — so the agents don't have to figure them out from scratch every time.
When you run a task, Theme Factory reads the .tf-rules/ directory in the repo and injects relevant rules into the agent's context based on keyword matching.
Theme Factory assembles every agent prompt from 4 layers, from universal to specific:
- Identity — core agent role (built into Theme Factory)
- Agent Rules — universal coding standards for all themes (built into Theme Factory)
- Theme Rules — base-theme-specific rules, e.g. Dawn vs Horizon (built into Theme Factory)
- TF Rules — your client-specific rules (lives in the client repo)
Layers 1-3 are maintained by Theme Factory. Layer 4 is yours — it's where you capture everything unique about a client's theme.
Setting Up
Create a .tf-rules/ directory in the root of your client's theme repository:
$ mkdir -p .tf-rules/shared .tf-rules/dev-agent .tf-rules/validator-agent
| Directory | Loaded For |
|---|---|
.tf-rules/shared/ | All agents |
.tf-rules/dev-agent/ | Dev agent only |
.tf-rules/analyzer-agent/ | Analyzer agent only |
.tf-rules/validator-agent/ | Validator agent only |
.tf-rules/context-agent/ | Context agent only |
No rebuilds needed. Rules are read at runtime from markdown files — just commit them to the repo and they're active on the next run.
Rule Format
Each rule is a markdown file with YAML frontmatter that controls when it gets loaded:
---
alwaysApply: false
triggers:
- header
- navigation
- nav
- sticky
- mega-menu
---
# Header Component
The header uses sections/header.liquid with sticky positioning.
It has a mega-menu powered by a nested block structure.
Key files: sections/header.liquid, assets/header.js
Mobile: collapses to hamburger at 990px breakpoint.
The sticky behavior uses position: sticky with a
100px offset. Do not change to position: fixed.
Triggers
The triggers field contains lowercase keywords. When your task description mentions any of these words, the rule is automatically injected into the agent's prompt.
- Case-insensitive — triggers are lowercased for matching
- Multi-word triggers work — "sticky header" matches if the task contains that phrase
- Per-task filtering — different rules load for different tasks, keeping context focused
- Set
alwaysApply: truefor rules that should load on every task (e.g., global design system conventions) - Files without frontmatter are treated as
alwaysApply: true
Keep triggers specific. Too many generic triggers cause noise. "header" and "mega-menu" are good. "style" or "section" are too broad.
What to Define
Good candidates for TF Rules:
| Rule | Why |
|---|---|
| Complex headers | Mega-menus, sticky behavior, mobile drawers — things that aren't obvious from the code alone |
| Custom PDP layouts | Non-standard product page structures, custom blocks, variant behavior |
| Design system rules | Client-specific spacing, color usage, typography conventions (set alwaysApply: true) |
| Non-standard filters | Custom filter implementations, third-party integrations |
| Component behavior | Click interactions, animation patterns, state management that isn't visible in Figma |
Think of TF Rules as onboarding notes. If a new developer would need to know something specific about a component before working on it, that's a good candidate for a TF Rule.
Learnings
Theme Factory also stores a learnings.md file inside .tf-rules/. This file is automatically populated with knowledge discovered during pipeline runs and chat sessions.
- Pipeline adds schema constraints (valid value ranges, select options) when it encounters errors
- Super Agent adds conventions and client preferences when you correct it during a chat session
[pipeline] Setting 'popover_border_radius' range is 0-16, clamp values exceeding this
[chat] Client uses scheme-3 for green backgrounds, scheme-1 for white
Since it's committed to git, learnings are shared with anyone cloning the repo. All agents read this file on every run.
Plugins
What Are Plugins
Plugins are optional feature packs that aren't bundled with Theme Factory by default. They typically wrap heavy dependencies (large npm packages, headless browser binaries, native modules) that not every user needs.
Installing a plugin downloads its dependencies into an isolated directory under ~/.theme-factory/plugins/<name>/ — each plugin has its own node_modules/, so plugins can't conflict with each other or with tf itself. Uninstalling deletes the whole directory.
When a feature that depends on a plugin runs, tf loads the plugin's modules lazily. If the plugin isn't installed, the feature aborts with the exact install command.
Available Plugins
| Plugin | Unlocks | Heavy Deps |
|---|---|---|
website-flow | # Website Options in task files, live-site capture during tf chat | playwright + Chromium browser (~150 MB on disk) |
The registry ships with tf source — run tf update to fetch newer plugins as they're added.
Commands
$ tf plugin list # list plugins with install status
$ tf plugin install <name> # install a plugin's dependencies
$ tf plugin uninstall <name> # remove an installed plugin
$ tf plugin status [<name>] # install state for one or all plugins
website-flow
Adds live-website capture using Playwright + headless Chromium.
Unlocks:
# Website Optionsin task files — declarative captures that take screenshots and extract HTML from a live site, feeding both into agent prompts. See Website Options for the full property reference.- Live-site capture in
tf chat— the super agent can navigate, screenshot, and extract from real pages on demand.
Install:
$ tf plugin install website-flow
The first install downloads a Chromium browser binary (~150 MB) — this is a one-time cost. Subsequent runs reuse the same browser.
Without it: any task using # Website Options, or any tf chat session that asks for a live capture, aborts with:
Playwright is not installed. Website capture requires the `website-flow` plugin.
Install: tf plugin install website-flow
Where Plugins Live
~/.theme-factory/
└── plugins/
└── website-flow/
├── package.json # Synthetic manifest (auto-generated by tf)
└── node_modules/ # Isolated dependencies
Safe to delete by hand. tf plugin install <name> rebuilds it from scratch.
Plugin Development
Building a Plugin
Integration plugins teach the Theme Factory pipeline how to integrate a third-party app (Recharge, Klaviyo, Yotpo, …) into a Shopify theme without modifying core code.
A plugin contributes knowledge (what the app is + how to build it), optional tools (MCP tools the agents can call), and optional store credentials (per-store IDs/keys fetched at build time). When a task is relevant, an LLM selects the plugin, its knowledge is injected into the analyzer/dev/validator agents, and the theme is built accordingly.
Plugins live in a separate repo (tf-plugins) under plugins/<name>/, or in any local directory used via --with-plugin during development.
What a Plugin Contributes
| Contribution | File(s) | What it does | Risk |
|---|---|---|---|
| Knowledge | knowledge/*.md | Markdown injected into the analyzer/dev/validator prompts when engaged — "how this app is built into a theme." | none (text) |
| Store credentials | storeSettings in plugins.json | Per-store IDs/keys fetched from the store-settings API and baked into the theme (no merchant theme settings). | none (values only) |
| Tools | tools.ts | MCP tools the agents can call during a run (e.g. query a 3rd-party API). | code (sandbox later) |
| SDK script | sdk in plugins.json | A storefront <script> URL surfaced to the dev agent to include. | low |
Knowledge is the core contribution — most integrations are 80% knowledge. Reach for tools.ts only when the build genuinely needs to call something.
Anatomy
A plugin is a directory. Only plugins.json is required.
my-plugin/
plugins.json # manifest — the contract (required)
knowledge/ # how-it-works markdown (one file per topic)
overview.md
gotchas.md
tools.ts # OPTIONAL — MCP tools (export const tools = [...])
README.md # OPTIONAL — human notes
plugins.json Reference
{
"name": "yotpo-reviews", // unique id (kebab-case)
"version": "0.3.0", // semver
"kind": "integration", // "integration" | "development" | "runtime-dep"
"coreApi": "^1.0.0", // host API semver range this plugin targets
"description": "…", // REQUIRED — natural language: what it is + WHEN to use it
// (this is what the LLM selector reads to decide relevance)
"dependsOn": ["theme"], // other plugins required (resolved + ordered first)
"storeSettings": ["yotpo_instance_id"], // per-store Setting keys to fetch + inject
"sdk": "https://…/loader.js", // OPTIONAL storefront script URL surfaced to the dev agent
"npmDeps": {}, // OPTIONAL npm deps (for tools.ts)
"provides": {
"knowledge": "knowledge/", // dir of .md files (default "knowledge/")
"tools": "tools.ts" // OPTIONAL path to the tools module
}
}
The description is the most important field. Detection is LLM-based (no keywords) — the model reads your description against the task to decide whether to engage the plugin. Write it as what the integration is and when it should be used, and scope it explicitly. Example:
"Yotpo product reviews for Shopify themes — the Reviews Widget and Star Rating widget. Use when a task involves Yotpo reviews, star ratings, or review counts. REVIEWS ONLY — not Yotpo Loyalty, SMS, or Subscriptions."
Knowledge
knowledge/ is a folder of Markdown files. When the plugin is engaged, all of them are concatenated and injected into the agents' prompts (analyzer, dev, validator). You do not categorize knowledge by agent — the host routes it.
Write knowledge as directives the planner can act on, not passive reference. This matters: the analyzer turns your knowledge into plan steps + acceptance criteria, and the plan wins over vague hints. If you want a specific outcome, say so explicitly and name the exact markup. Good knowledge:
- names the exact Liquid/markup to emit;
- says when it overrides the base theme's own pattern (e.g. "use the Yotpo widget, NOT the theme's native review block, NOT static stars");
- documents real gotchas (load order, empty states, double-render, etc.).
Frontmatter is optional. A leading --- … --- block is stripped before injection, so you can keep notes there.
Store Credentials
Third-party integrations usually need per-store IDs/keys (a Yotpo instance id, an app key, …). Do not expose these as merchant-editable theme settings. Instead:
- Store the value per shop in the backend Setting table (key → value).
- Declare the key(s) in
plugins.json:plugins.json"storeSettings": ["yotpo_instance_id"] - At build time the host calls
GET /api/store-settings?shop=<shop>, and injects only your declared keys' values into your plugin's knowledge block under "Store-provided credentials". - Your knowledge tells the agent to bake the literal value in (and to fall back to a merchant setting only if the value is absent).
Only the keys you declare are injected — unrelated store settings never reach the prompt.
Tools (tools.ts)
Use a tool when the build needs to call something at runtime (e.g. fetch live data from a 3rd-party API). A tool is an MCP tool the analyzer/dev/validator can invoke.
tools.ts must export const tools = [...] using the Agent SDK's tool() primitive:
import { tool } from "@anthropic-ai/claude-agent-sdk";
import { z } from "zod/v4";
export const tools = [
tool(
"verify_widget_selectors",
"Check the theme renders the required widget mount points",
{ theme_path: z.string().describe("Absolute path to the theme directory") },
async (args) => {
// …do work…
return { content: [{ type: "text" as const, text: "ok" }] };
},
),
];
- Authored in TypeScript; the host transpiles it with esbuild on load (you ship
.ts, never a build artifact). - The Agent SDK and
zodare provided by the host — import them normally; they are resolved from Theme Factory's own dependencies. - Tools are namespaced per plugin (
plugin-<name>), so they can't collide with core tools. - Need a secret/token? Read it from a store credential (see above) — never commit secrets.
How a Plugin Gets Engaged
- Discovery — the host finds installed plugins and any
--with-plugindirs. - Selection — an LLM reads each candidate's
description+ the task and returns the plugins to engage. (No keyword triggers.) - Dependencies — each engaged plugin's
dependsOnis resolved. - Contribution — engaged plugins' knowledge (+ injected store creds) is added to the agent prompts; their tools are registered as MCP servers.
Unengaged plugins cost ~nothing — only their one-line description is read.
Developing & Testing a Plugin
Author a plugin anywhere, then run Theme Factory against it with --with-plugin pointing at the directory (it just needs a valid plugins.json):
$ tf run <task.md> --with-plugin ./integrations/my-plugin
# absolute paths and multiple plugins work too:
$ tf run <task.md> --with-plugin /abs/path/a --with-plugin ./b
- Works in single and batch runs.
- A
--with-pluginplugin is unverified (trust: local) — fine for dev. - It shadows an installed plugin with the same
name, so you can test a local copy over a published one.
Confirm it engaged: the run prints [plugins] Engaged: <name> and writes an INTEGRATION PLUGINS section to logs/<runId>.log. If your plugin should have applied but didn't, check that section first.
What Is Not Supported Yet
These are planned but not implemented — don't rely on them:
- Lifecycle hooks (
webhook.tswithatStart()/beforeContext()/ …) — deferred. Plugins are knowledge + tools + store creds for now. tf plugin install/ registry index / checksums — there is no installer or verification yet for integration plugins. They are consumed via--with-pluginor by being present in the plugins dir.- Sandboxed tool execution —
tools.tsruns in-process for now. Only ship tools you trust. - Deterministic
<script>injection forsdk— the URL is surfaced to the dev agent rather than spliced in by the host.
Reference Plugin: yotpo-reviews
The yotpo-reviews plugin (in the tf-plugins repo) is the canonical example — a knowledge-only integration that:
- engages on review/rating tasks via its
description; - pulls the store's
yotpo_instance_idfrom store settings and bakes it into the modernyotpo-widget-instancemarkup; - relies on the Yotpo App Embed for the loader (never hardcodes a loader URL, which would go stale);
- explicitly overrides the base theme's native review pattern (no static stars, no
metafields.reviews.rating).
Read its knowledge/ files for the directive style that makes the planner actually produce the integration.