A TUI for multi-clauding without losing your marbles.
You type a description, it reads or creates an issue in Linear/Jira, spawns a configured git worktree, builds a PR/MR, and starts a Claude Code session alongside a companion pane (gitui by default, configurable to any command) — all wired together in a 3-pane tmux layout you can navigate with simple, customizable keystrokes.
Video:
pappardelle-demo.mp4
- Installation and Getting Started
- Understanding tmux in Pappardelle
- Spawning New Sessions
- Spec-Driven Development Mindset
- Customizing Your Configuration
- Advanced: Doom-coding with Pappardelle
- Advanced: Wrangling Multi-Repo Changes
- Reference
The fastest way to get set up is with /init-pappardelle.
Add the marketplace:
claude plugin marketplace add chardigio/pappardelleInstall the plugin. This installs four skills — /init-pappardelle (setup wizard), /update-pappardelle (update to latest), /configure-pappardelle (interactive config editor), and /sous-chef (optional workspace coordinator):
claude plugin install pappardelle@pappardelle-marketplaceRun the setup wizard from any repo where you want to use Pappardelle. This installs pappardelle configures your .pappardelle.yml:
claude /init-pappardelleFor manual installation, see Section 8: Reference. For the full config format, see the configuration reference.
pappardelleWhen you launch pappardelle, it creates a tmux session with three panes:
- Left pane shows the ticket rail. This is where you navigate workspaces, create new ones, and trigger actions.
- Center pane shows the Claude Code session for whichever workspace is highlighted.
- Right pane runs the companion command for the highlighted workspace's worktree — gitui by default, or any command you set via
companion_command(a different git UI, a dev server, a log tailer, …).
Each workspace creates two independent tmux sessions:
claude-{repo}-{issue-key}— runs Claude Codecompanion-{repo}-{issue-key}— runs the companion command (gitui by default)
The center and right panes in the Pappardelle session are "viewers" — they run nested tmux clients that attach to these independent sessions. When you highlight a different workspace in the list, Pappardelle uses tmux switch-client to instantly swap which session the viewer pane displays.
This means:
- Workspaces are independent. Each Claude session runs in its own tmux session. If Pappardelle crashes, your Claude sessions keep running.
- Attach from anywhere. You can attach to any workspace's Claude session from a separate terminal:
tmux attach -t claude-stardust-labs-STA-631. - Switching is instant. After the first attachment, switching between workspaces uses tmux's fast
switch-clientpath — no process restart, no visible flash.
Pappardelle works with any tmux configuration, but these settings improve the experience — mouse support, Ctrl+Shift+arrow pane navigation, and a clean status bar. See examples/tmux.conf and append to your ~/.tmux.conf. If you don't have one yet:
curl -fsSL https://raw.githubusercontent.com/chardigio/pappardelle/main/examples/tmux.conf -o ~/.tmux.confPress q in the workspace list pane to quit. This kills the Pappardelle tmux session and all its viewer panes, returning you to your original terminal. Your Claude and companion workspace sessions are not affected — they run in independent tmux sessions and will keep going after Pappardelle exits. To reattach, just run pappardelle again.
To also kill all workspace sessions, use Delete on each workspace from the TUI before quitting, or nuke everything with:
tmux kill-serverPress n in the workspace list to open the prompt dialog.
When you create a workspace, Pappardelle runs through these steps:
-
Profile selection — Your input is keyword-matched against a profile in
.pappardelle.yml. -
Issue creation/fetch — For new descriptions, a Linear (or Jira) issue is created with a WIP title. For existing issue keys, the issue is fetched.
-
Git worktree — An isolated worktree is created at
~/.worktrees/{repo-name}/{issue-key}/. This is a full working copy of your repo on a new branch, completely isolated from your main checkout. -
PR/MR creation — A placeholder PR (GitHub) or MR (GitLab) is created from the new branch.
-
Project setup — Profile
commandsare executed (e.g.,xcodegen generate, dependency installs). Top-levelpost_workspace_initcommands also run after the worktree is created (e.g., copying.envfiles). -
Claude & companion sessions spawned — A named tmux session is created and Claude Code is launched inside it. If
claude.initialization_commandis set in.pappardelle.yml(e.g.,/do), that command is passed to Claude along with the issue key. A companion session rooted at the worktree dir is also spawned, running thecompanion_command(gitui by default).
Pappardelle's recommended /do skill (set via claude.initialization_command in .pappardelle.yml) starts every Claude session with a planning-first workflow. Before writing any code, the agent researches and uses Claude Code's AskUserQuestion tool to clarify requirements — asking about ambiguous scope, confirming design decisions, and validating edge cases. The goal is to turn a rough prompt into a detailed, unambiguous spec — written back to the issue description — before the first line of code is written.
A one-line prompt like "add dark mode to settings" leaves a lot of questions open: which settings screen? System preference or manual toggle? What colors? The /do skill instructs the agent to surface these questions upfront rather than guessing. This front-loaded clarification produces better code on the first pass and fewer revision cycles.
Every AskUserQuestion exchange is automatically posted as a comment on the Linear or Jira issue by the comment-question-answered.py hook. This means:
- The issue becomes the single source of truth. The original prompt, every clarifying question, and every answer are all captured in one place — not scattered across chat windows or terminal scrollback.
- Anyone reviewing the PR can see the full context. The issue thread shows exactly what the agent asked, what the developer decided, and why.
- Record-keeping is automatic. You don't need to manually document decisions or copy-paste from the terminal. The hook handles it silently in the background.
Use /configure-pappardelle to interactively edit your config — it walks you through adding profiles, keybindings, hooks, and more using AskUserQuestion. Available via the plugin marketplace or by asking Claude directly (it's model-invocable).
Pappardelle is configured via a .pappardelle.yml file at your repo root. The key concepts:
- Issue watchlist — Auto-discover issues assigned to you and spawn workspaces for them. Pappardelle polls your issue tracker and creates workspaces for new matching issues. Filter by status, labels, or
key_prefixes(e.g. watchSTA-*but notWAB-*when one account spans multiple workspaces). A profile can also declare its ownissue_watchlist, polled in addition to the top-level one and auto-scoped to that profile'steam_prefix— so you can watch one status everywhere and a bespoke status (e.g. "For Pappardelle") for a single project. - Auto-remove when done — Opt-in flag (
auto_remove_when_done: true) that drops a space from the rail as soon as the tracker reports its issue as completed or canceled. Same teardown as pressingd; the on-disk worktree is left untouched. - Companion pane command — The right pane runs
companion_command(defaults togitui). Set it to any shell command — a different git UI (companion_command: lazygit), a dev server, a log tailer — or""to leave a plain shell. A profile can override the top-level value, so per-project profiles can each launch their own process. - Profiles — Per-project-type config (keywords, setup commands, VCS labels, optional
emoji:shown in the ticket rail). Pappardelle keyword-matches your input to auto-select the right profile. Each profile'stracker_projectslist both routes existing issues to the right profile (Linear project names; Jira project names or keys) and (Linear only) lands brand-new issues intracker_projects[0]. - Template variables — All string values support
${VAR_NAME}expansion (${ISSUE_KEY},${WORKTREE_PATH},${PR_URL}, profilevars, env vars, etc.). - Custom keybindings — Bind single keys to bash commands (
run) or Claude directives (send_to_claude). - Providers — Pluggable issue trackers (Linear, Jira) and VCS hosts (GitHub, GitLab). Defaults to Linear + GitHub.
- Built-in file copies —
.pappardelle.local.ymland.claude/settings.local.jsonare automatically copied from the main repo to new worktrees (if they exist). - Workspace lifecycle hooks —
post_workspace_initcommands run after worktree creation (e.g., copying.envfiles, installing dependencies).pre_workspace_deinitcommands run before workspace deletion (e.g., closing issues, removing worktrees).
For the full schema, all fields, and examples, see the configuration reference.
For a production .pappardelle.yml used across a polyglot monorepo (Python backends + Swift iOS apps), see examples/monorepo-pappardelle.yml.
Video:
pappardelle-mobile-demo.mp4
Because Pappardelle runs entirely inside tmux, you can access your full workspace setup from anywhere — all you need is an SSH connection to the machine running it.
- A machine that stays on — I'm not a Mac Mini guy (yet), I just keep my MacBook plugged in. macOS won't sleep with the lid closed as long as it has power and an active SSH session.
- Tailscale — A mesh VPN that makes your dev machine accessible from any network without port forwarding or firewall configuration. Install on both your dev machine and your mobile device.
- Termius (iOS) — A full-featured SSH client for iPhone and iPad with good tmux support, copy/paste, and keyboard shortcuts.
- ntfy — Push notifications to your phone when Claude needs input. Pappardelle ships with a
zap-notification.pyhook that sends a push via ntfy whenever Claude asks a question or hits a permission prompt. To get it working, set thePAPPARDELLE_NTFY_TOPICenvironment variable and subscribe to the same topic in the ntfy app on your phone — the hook only fires when an Tailscale SSH session is active. This way you don't have to babysit the terminal — just wait for the buzz. - Wispr Flow — Voice-to-text dictation that works system-wide, including inside Termius. Lets you talk to Claude instead of thumb-typing on a phone keyboard.
Termius lets you customize the shortcut bar above the on-screen keyboard — these are my favorite groups for Pappardelle:
- Arrow keys — scroll through Claude output and navigate the workspace list
- pgUp / pgDn — cycle between tmux panes (requires the
PageUp/PageDownbindings fromexamples/tmux.conf) - esc / ^C — cancel prompts, exit modes
- paste / tab / ctrl / alt — general terminal essentials
Also recommended: toggle on Settings > Hide the AI widget in Termius for extra screen real estate.
When you're doom-coding from your phone, you want one-tap access to open the PR on the device in your hand. Bind a key that sends an ntfy notification with a clickable link:
keybindings:
- key: 'z'
name: 'Zap PR'
# Use `--search "head:${ISSUE_KEY}"` (tokenized prefix match) rather than
# `--head ${ISSUE_KEY}` (exact match) so follow-up branches like
# ${ISSUE_KEY}-FOLLOW-1 are also discoverable from the parent issue key.
# Sort by updatedAt desc — when a branch name has been reused (e.g. an
# old merged PR + a freshly opened one), surface the PR you're actually
# working on, not the oldest stale match.
run: >
PR_JSON=$(gh pr list --search "head:${ISSUE_KEY}" --state all --json number,url,updatedAt
-q 'sort_by(.updatedAt) | reverse | .[0]' 2>/dev/null);
PR_NUM=$(echo "$PR_JSON" | jq -r '.number // empty');
PR_URL=$(echo "$PR_JSON" | jq -r '.url // empty');
if [ -n "$PR_NUM" ]; then
curl -d "${ISSUE_KEY} GitHub PR #$PR_NUM"
-H "Click: $PR_URL"
ntfy.sh/${PAPPARDELLE_NTFY_TOPIC};
fiPress z on a workspace and your phone buzzes with a notification — tap it and the PR opens in the GitHub app.
Pappardelle is designed for single-repo workflows, but (experimentally) you can extend it to orchestrate changes across multiple repositories using a parent (pappa) repo.
Create a parent repository that serves as the orchestration hub:
my-workspace/
├── .pappardelle.yml
├── .claude/
│ ├── settings.json # shared settings + plugins
│ └── skills/
│ ├── do/
│ │ └── SKILL.md # initialization skill
│ └── address-mr-feedbacks/
│ └── SKILL.md # orchestration skill
└── CLAUDE.md
The parent repo's primary purpose is to share settings, context, and orchestration skills to coordinate work across child repos. Child repos are not committed to the parent — they're shallow-cloned on demand during workspace setup.
Multi-repo work has been an achilles heel for Claude Code in the past, but I'm hoping Agent Teams can help solve this. One key unlock with agent teams is that teammates can be spawned in separate directories, meaning we can have a parent repo, but then spawn an agent per relevant child repo, which is nice because it automatically loads that repo's CLAUDE.md, skills, settings, etc.
Repos are pulled down as needed, not upfront. During the planning phase, use a search tool like SourceBot's codesearch MCP to identify which repos are relevant, then shallow-clone only what you need:
git clone --depth 1 https://github.com/org/repo-a.gitThis keeps initialization fast and reduces noise for the agent while it greps and globs. Because we use --depth 1, only the latest commit is fetched — no full history.
One key distinction for multi-repo work is between plugin skills and parent repo skills:
-
Plugin skills (added in the parent repo's
settings.jsonbut defined elsewhere) are skills that can be used by any repo / agent teammate receives automatically. These handle single-repo concerns.Example: An
/address-mr-feedbackplugin that lets any agent look at its own repo's MR and address reviewer comments. -
Parent repo skills (in the parent repo's
.claude/skills/) are orchestration skills that spawn agent teams across child repos.Example: An
/address-mr-feedbacks(plural) skill that spins up an agent team, spawning one agent per relevant child repo — each agent calls the plugin's singular skill for its own MR.
A starter /do skill tailored for multi-repo workflows is available at examples/skills/do-multi-repo/SKILL.md. It covers shallow cloning, agent team spin-up, per-repo QA, and coordinated PR creation. Install it into your parent repo with:
mkdir -p .claude/skills/do && curl -fsSL https://raw.githubusercontent.com/chardigio/pappardelle/main/examples/skills/do-multi-repo/SKILL.md -o .claude/skills/do/SKILL.mdBind keys to open specific child repos in your editor for quick navigation:
keybindings:
- key: 's'
name: 'Open repo-a in Cursor'
run: 'open -a "Cursor" "${WORKTREE_PATH}/repo-a" 2>/dev/null || open -a "Cursor" "${REPO_ROOT}/repo-a"'Note the fallback to ${REPO_ROOT}/repo-a here ensures this shortcut works in the master/main space.
| Tool | Required | Install |
|---|---|---|
| Node.js >= 18 | Yes | brew install node |
| npm | Yes | Comes with Node.js |
| git | Yes | brew install git |
| tmux | Yes | brew install tmux |
| jq | Yes | brew install jq |
| Claude Code | Yes | curl -fsSL https://claude.ai/install.sh | bash |
| linctl | Optional | brew tap raegislabs/linctl && brew install linctl (for Linear) |
| gh | Optional | brew install gh (for GitHub) |
| glab | Optional | brew install glab (for GitLab) |
| acli | Optional | brew tap atlassian/homebrew-acli && brew install acli (for Jira) |
If you prefer to install without the /init-pappardelle skill:
One-line install:
curl -fsSL https://raw.githubusercontent.com/chardigio/pappardelle/main/install.sh | bashFrom a local clone:
git clone https://github.com/chardigio/pappardelle.git
cd pappardelle
./install.shNode pinning: the installer pins the
pappardellecommand to the node binary it verified at install time — PATH in non-interactive shells often differs (nvm/volta don't load there), which used to silently bind the shim to a stale system node. If you later remove that node version (e.g.nvm uninstall), the shim falls back to PATH and refuses to run anything below Node 18 — just re-run the installer (or pressUin the TUI) to re-pin.
Manual install:
git clone https://github.com/chardigio/pappardelle.git
cd pappardelle
npm install
npm run build
npm link # makes `pappardelle` available globally
./hooks/install.sh # installs Claude Code hooks for status trackingDirectories created by the installer:
| Directory / File | Purpose |
|---|---|
~/.pappardelle/ |
Config, hooks, logs, and Claude status files |
~/.pappardelle/repos/{repoName}/open-spaces.json |
Persisted workspace registry (per-repo, survives reboots) |
~/.pappardelle/repos/{repoName}/issue-meta/ |
Issue metadata for hook tracking (per-repo) |
~/.pappardelle/claude-status/ |
Real-time status JSON files from Claude hooks |
~/.pappardelle/logs/ |
Daily log files (7-day retention) |
~/.worktrees/ |
Git worktrees for all your workspaces |
Multi-repo support: State is namespaced per repository under
~/.pappardelle/repos/{repoName}/. Running pappardelle in two different repos keeps their workspace registries completely separate. On first run, existing state is automatically migrated from the legacy global location.
Pappardelle installs three Claude Code hooks that provide integration between Claude sessions and the TUI:
| Hook | Trigger | What it does |
|---|---|---|
update-status.py |
PreToolUse, PostToolUse, Stop |
Writes session status to ~/.pappardelle/claude-status/ for live TUI updates |
comment-question-answered.py |
PostToolUse (AskUserQuestion) |
Posts Q&A exchanges as comments on the issue (Linear or Jira) |
zap-notification.py |
PreToolUse, PermissionRequest |
Sends push notifications via ntfy when Claude needs user input |
Pappardelle is versioned via git tags on chardigio/pappardelle (vX.Y.Z) — package.json is not bumped on release. Every push to main cuts a new tag + GitHub Release via the release workflow. Bump type is driven by PR labels:
| Label | Effect |
|---|---|
release:major |
X.0.0 bump |
release:minor |
x.Y.0 bump |
release:patch |
x.y.Z bump |
| (no label) | patch bump (default) |
Two paths feed the release workflow:
- Direct merge on
chardigio/pappardelle— an external contributor opens a PR here and you merge it. The release workflow reads therelease:*label on the chardigio-side PR directly. - Forward sync from
stardust-labs-io/stardust-labs— rsync pushes straight tomainwith no PR on this side. The monorepo's sync workflow reads the merged monorepo PR'srelease:*label and propagates it to the synced commit as aRelease-bump: <major|minor|patch>trailer, which the release workflow here consumes.
On startup, the TUI checks the GitHub Releases API (cached for 24h at ~/.pappardelle/update-check.json) and, if a newer tag is available, renders a magenta banner above the workspace list:
Update available: v0.1.0 → v0.2.0 · U to update · X to dismiss
The update-check's installed version is read from git describe --tags --abbrev=0 --match 'v*.*.*' on the install clone. Press U to update to the latest release. U is always live — not just while the banner is showing: the 24h cache can lag a fresh release by hours, so you can pull a release that landed minutes ago without restarting and waiting out the cache. U opens an "are you sure?" confirm dialog (same style as closing a space); confirming exits the TUI, re-runs the install script in-place, and relaunches. Press X to dismiss the banner for the current session — the next launch re-reads the cache.
The version shown in the help (?) overlay follows the same source-of-truth but degrades differently, since package.json is never bumped on release and so is always a stale 0.1.0:
- Released install (run from
~/.pappardelle/repo, which carries the tag) →pappardelle v0.7.9 (<sha>). - Dev / worktree build (run from the monorepo, where no
v*.*.*tag is reachable) → reports the latest release you have installed, flagged-dev:pappardelle v0.7.9-dev (<sha>). This signals "ahead of v0.7.9" rather than leaking the stale0.1.0. - Neither resolvable → the line degrades to the sha-only form
pappardelle (<sha>).
When running out of the stardust-labs monorepo (LOCAL_MODE), the automatic update check (and its banner) is skipped — the monorepo's version is a dev placeholder, so the comparison would false-positive on every launch. The manual U update still works everywhere: the installer writes to ~/.pappardelle/repo, never to your monorepo checkout, so it can't clobber unpushed work there.
Logs are written to ~/.pappardelle/logs/ with daily rotation (7-day retention):
# View today's log
cat ~/.pappardelle/logs/pappardelle-$(date +%Y-%m-%d).log
# Tail logs in real-time
tail -f ~/.pappardelle/logs/pappardelle-*.log
# View errors only
grep '\[ERROR\]' ~/.pappardelle/logs/*.logWarnings and errors also appear in a red box at the bottom of the TUI. Press e to view them.
npm run dev # Watch mode (auto-rebuild on changes)
npm run build # Build once
npm start # Run without building
npm test # Lint + format check + testsStandalone scripts in integration-tests/ verify providers against real instances. They are not ava tests and are never run in CI — use them for local verification after making provider changes.
npx tsx integration-tests/verify-linear.ts # Linear provider (linctl)
npx tsx integration-tests/verify-jira.ts # Jira provider (acli)
npx tsx integration-tests/verify-github.ts # GitHub PR detection (gh)
npx tsx integration-tests/verify-gitlab.ts # GitLab MR detection (glab)
npx tsx integration-tests/verify-config.ts # Config loading + validation
npx tsx integration-tests/verify-watchlist.ts # Full watchlist pipeline end-to-end
npx tsx integration-tests/verify-comments.ts # Comment posting (creates real comments)See integration-tests/README.md for env vars and prerequisites.
Pappardelle ships a Claude Code plugin marketplace with a single pappardelle plugin containing four skills:
| Skill | Description | Model-invocable |
|---|---|---|
/init-pappardelle |
Interactive setup wizard — installs Pappardelle, checks prerequisites, generates .pappardelle.yml |
No |
/update-pappardelle |
Re-runs the install script to update Pappardelle to the latest version | No |
/configure-pappardelle |
Interactive config editor for .pappardelle.yml and .pappardelle.local.yml — profiles, keybindings, hooks, watchlists, and more |
Yes |
/sous-chef |
Kitchen-style coordinator — quick overview of active spaces, drill into any space for a sitrep, relay instructions to running Claude sessions via tmux | No |
Install the plugin:
claude /plugin marketplace add chardigio/pappardelleclaude /plugin install pappardelle@pappardelle-marketplaceBecause /configure-pappardelle is model-invocable, Claude will automatically offer to use it when you ask about configuring Pappardelle — no need to invoke it explicitly.
- Ink — React for CLIs
- tmux — Terminal multiplexer
- gitui — Terminal git UI (default companion pane; swap via
companion_command) - Claude Code — AI coding assistant



