</ >

Claude Code + Git Worktrees + Portless — One Skill to Start Any Ticket

How to build a Claude Code skill that takes a Jira ticket ID, spins up an isolated Claude worktree, names the branch after the ticket, and launches a Portless dev server at a URL you can actually remember.


Every ticket-driven workflow starts with the same ritual: copy the ticket ID, create a branch, update your local env, remember which port you used. It’s not hard — it’s just friction that adds up.

This post shows a Claude Code skill that collapses the whole thing into one command:

/start-ticket ENG-247

What it does:

  1. Fetches the Jira ticket via the Jira MCP and shows you a summary
  2. Creates a Claude worktree with a branch named ENG-247-<slug>
  3. Starts the dev server via Portless at https://ENG-247.localhost
  4. Configures git to prefix every commit in that worktree with [ENG-247]

The tools involved

Claude worktrees are Claude Code’s built-in worktree command. When you ask Claude to create a worktree, it uses the EnterWorktree tool, which spins up an isolated git worktree automatically — no manual git worktree add needed. Each worktree gets its own branch and directory, so you can work on multiple tickets simultaneously without any context switching.

The Jira MCP connects Claude Code directly to your Atlassian account. Instead of managing API tokens in .env and making raw HTTP calls, you configure the MCP once and Claude can query Jira natively — getJiraIssue, transitionJiraIssue, editJiraIssue — as first-class tools. No credentials in your project, no HTTP boilerplate in your skill.

Portless replaces localhost:3000 with a stable named URL for each running app. Name it after the ticket ID and you get https://ENG-247.localhost — readable, collision-free, memorable.

Claude Code skills encode multi-step workflows as a SKILL.md file that Claude executes as a slash command.

Setting up the Jira MCP

Add the Atlassian MCP to your Claude Code config (~/.claude/mcp.json or via the Claude Code settings UI):

{
  "mcpServers": {
    "atlassian": {
      "command": "npx",
      "args": ["-y", "@atlassian/mcp"]
    }
  }
}

On first use, Claude will prompt you to authenticate via browser OAuth — no token copying, no .env entries. After that, Claude has access to your Jira and Confluence workspaces natively.

Also install Portless globally if you haven’t:

npm install -g portless
portless proxy start --https

The skill

Create .claude/skills/start-ticket/SKILL.md in your project:

---
name: start-ticket
description: >
  Use this skill to start work on a Jira ticket.
  Trigger on: "start ticket", "work on ticket", "/start-ticket", "pick up ENG-", "start ENG-".
---

## Start Ticket Workflow

### 1. Get the ticket ID

If the user provided a ticket ID (e.g. `ENG-247`), use it.
If not, ask:
> Which ticket do you want to start? (e.g. ENG-247)

### 2. Fetch the ticket via the Jira MCP

Use the `getJiraIssue` MCP tool with the ticket ID.

Extract:
- `summary` — the ticket title
- `status` — current status
- `assignee` — assignee display name (or "Unassigned")
- `description` — first paragraph only, if present

If the ticket is not found, stop and say:
> Ticket {TICKET_ID} not found. Check the ID and try again.

### 3. Show the ticket summary and confirm

Show:
```
Ticket:   {TICKET_ID}
Title:    {summary}
Status:   {status}
Assignee: {assignee}
```

Then ask:
> Start work on this ticket? This will create a worktree and a Portless dev server. [y/N]

Wait for confirmation. If the user says no, stop.

### 4. Derive the branch name

Convert the ticket summary to a slug:
- Lowercase
- Replace spaces and special characters with hyphens
- Strip leading/trailing hyphens
- Truncate to 40 characters

Final branch name: `{TICKET_ID}-{slug}`

Example: `ENG-247` + `Fix auth redirect loop``ENG-247-fix-auth-redirect-loop`

### 5. Create the Claude worktree

Use the `EnterWorktree` tool to create a new worktree with the branch name from step 4.

Claude will automatically:
- Create the worktree directory alongside the repo
- Check out a new branch named `{branch-name}`
- Set up the isolated environment

### 6. Configure commit prefix for the worktree

Inside the worktree, create `.git/hooks/prepare-commit-msg`:

```bash
#!/bin/sh
TICKET="[{TICKET_ID}]"
MSG=$(cat "$1")
if ! echo "$MSG" | grep -q "^\[{TICKET_ID}\]"; then
  echo "$TICKET $MSG" > "$1"
fi
```

Make it executable:
```bash
chmod +x .git/hooks/prepare-commit-msg
```

### 7. Transition the ticket in Jira

Use the `transitionJiraIssue` MCP tool to move the ticket to "In Progress".

If no "In Progress" transition is available, skip this step silently.

### 8. Start the Portless dev server

In the worktree directory, run:
```bash
portless {TICKET_ID} pnpm dev
```

Run in the background. Wait 3 seconds, then verify with:
```bash
portless list
```

If Portless is not found, skip this step and tell the user to install it:
> Install Portless with `npm install -g portless` to get the dev URL.

### 9. Report success

Show:
```
✓ Worktree:   {worktree-path}
✓ Branch:     {branch-name}
✓ Jira:       {TICKET_ID} moved to In Progress
✓ Dev server: https://{TICKET_ID}.localhost
✓ Commits will be prefixed with [{TICKET_ID}]
```

## Notes

- Never modify the main worktree's files when running this skill
- The `pnpm dev` command assumes pnpm — substitute `npm run dev` or `yarn dev` if needed
- The Jira MCP must be configured in Claude Code settings before using this skill

What the workflow looks like

/start-ticket ENG-247

Fetching ENG-247 via Jira MCP...

Ticket:   ENG-247
Title:    Fix auth redirect loop on OAuth callback
Status:   To Do
Assignee: Francisco Iglesias

Start work on this ticket? This will create a worktree
and a Portless dev server. [y/N] y

✓ Worktree:   ../my-app-ENG-247
✓ Branch:     ENG-247-fix-auth-redirect-loop
✓ Jira:       ENG-247 moved to In Progress
✓ Dev server: https://ENG-247.localhost
✓ Commits will be prefixed with [ENG-247]

From that point, every commit made in that worktree automatically starts with [ENG-247]:

[ENG-247] Fix redirect URI mismatch on OAuth callback
[ENG-247] Add test for expired state parameter
[ENG-247] Update env docs

And the dev server lives at https://ENG-247.localhost — no port to remember, no collision with your main branch server.

Why the Jira MCP beats raw API calls

The raw API approach requires a token in .env, base64 encoding your credentials, building the Authorization header, and handling HTTP error codes manually. The MCP handles all of that at the configuration layer — once.

More importantly, it means your skill has no credentials to manage. The MCP connection is per-user, not per-project. You can share the SKILL.md with your team and everyone uses their own Jira account automatically.

Running multiple tickets in parallel

Because each ticket gets its own Claude worktree and its own Portless name, you can work on two tickets simultaneously:

# Worktree 1 — ENG-247
https://ENG-247.localhost

# Worktree 2 — ENG-251
https://ENG-251.localhost

# Main repo — still on main
http://localhost:4321

No port juggling. No stashing. Each environment is fully isolated.

Extending the skill

Teardown — A matching /finish-ticket ENG-247 skill that pushes the branch, opens a PR, removes the worktree, and uses transitionJiraIssue to move the ticket to In Review.

Open in editor — Add code {worktree-path} to open the worktree in a new VS Code window.

Assign yourself — Use editJiraIssue to assign the ticket to the current user before starting work.

The teardown is where the full payoff lands — the same ticket ID that named the branch, the worktree, and the dev server also becomes the PR title prefix and the Jira transition trigger. One ID, zero manual coordination.

Key Takeaways

  • Claude worktrees create isolated branch environments with one tool call — no manual git worktree add
  • The Jira MCP gives Claude native access to your tickets — no tokens in .env, no HTTP boilerplate in the skill
  • Portless turns ticket IDs into dev URLs — https://ENG-247.localhost instead of localhost:4322
  • The prepare-commit-msg hook enforces ticket prefixes automatically — no discipline required
  • Claude Code skills encode the whole setup as a single command — the skill knows your conventions so you don’t have to repeat them