---
name: magic:continue
description: This skill should be used when the user mentions a ticket ID like "PROJ-123", "#456", says "continue", "reprendre", "resume work on", "je reprends", "switch to", "basculer sur", or indicates they want to resume working on an existing task.
argument-hint: <TICKET-ID>
allowed-tools: Bash(*), Read, Write, AskUserQuestion, mcp__atlassian__*, mcp__github__*
---

# magic-slash v0.86.6 - /continue

You are an assistant that helps resume work on a Jira ticket or GitHub issue that was already started (by you, a colleague, or in a previous session).

Follow each step in order. Each step builds on the previous one.

## References

- `references/messages.md` — All bilingual messages (MSG_*). Read relevant sections as needed (not the whole file at once).
- `references/node-setup.md` — Node.js version manager detection. Read before installing dependencies (Step 6.2).
- `references/glossary.md` — EN/FR terminology for git concepts. When communicating in French, use the FR terms from this glossary for consistency.
- `references/api.md` — Magic Slash Desktop API reference (endpoints `/metadata` and `/repositories`).
- `references/jira-custom-fields.md` — Jira custom-field discovery: the `*all` re-read, its volume guards, and the empty-ticket options. Read in Step 2A, only when the ticket description carries no usable spec.

## Step 0: Configuration

### 0.1: Check config file exists

```bash
# Magic Slash Desktop is the single source of truth (Supabase). The port comes from the
# environment inside an app terminal, and from the file the app publishes anywhere else —
# so a Claude started from a plain terminal reaches the same live config.
MS_PORT="${MAGIC_SLASH_PORT:-$(cat ~/.config/magic-slash/port 2>/dev/null)}"
CONFIG_FILE=""
if [ -n "$MS_PORT" ]; then
  MS_TMP_CONFIG="$(mktemp)"
  trap 'rm -f "$MS_TMP_CONFIG"' EXIT
  # A published port may name a server that has since died: -sf turns that into a failure.
  if curl -sf --max-time 5 "http://127.0.0.1:$MS_PORT/config" -o "$MS_TMP_CONFIG" 2>/dev/null \
     && [ "$(jq '.repositories | length' "$MS_TMP_CONFIG" 2>/dev/null || echo 0)" -gt 0 ]; then
    CONFIG_FILE="$MS_TMP_CONFIG"
  fi
fi
[ -z "$CONFIG_FILE" ] && echo "APP_NOT_RUNNING" || echo "OK"
```

If `APP_NOT_RUNNING`, the app is not running and the cloud config is unreachable: display `MSG_APP_NOT_RUNNING` and stop. Never proceed on a guessed config.

### 0.2: Determine language

Once the repo is identified (Step 5), read `.repositories.<name>.languages.discussion` from config. Default: `"en"`. Until the repo is identified, use English for all messages.

### 0.3: Check Atlassian integration

Read `integrations.atlassian` from config. Default: `true` (backward compatibility).

```bash
# Every bash block runs in its own shell: $MS_PORT does not survive from Step 0,
# so resolve it again here. One line, and it costs nothing to repeat.
MS_PORT="${MAGIC_SLASH_PORT:-$(cat ~/.config/magic-slash/port 2>/dev/null)}"
curl -sf --max-time 5 "http://127.0.0.1:$MS_PORT/config" | jq -r '.integrations.atlassian // true'
```

Store the result as `$ATLASSIAN_ENABLED`. If `false`, only GitHub issue format is accepted in Step 1.

### 0.4: Determine development branch (execute after repo is identified in Step 5)

Read `.repositories.<name>.branches.development` from config.

- **If configured**: Display `MSG_BRANCH_CONFIRM` with the configured value. User can press Enter to accept, type "yes/ok" to accept, or type a branch name to override.
- **If not configured**: Display `MSG_BRANCH_ASK`.

**Handling the user's response:**
- **Empty response** (user just pressed Enter without typing): Use the configured default branch
- **Short confirmation** ("oui", "yes", "ok", "go"): Use the configured default branch
- **Another branch name** (e.g., "develop", "staging"): Use that branch instead

Store the result as `$DEV_BRANCH`.

## Step 1: Detect ticket type

Analyze `$ARGUMENTS`:

- **Jira**: Alphabetic prefix + hyphen + digits (regex: `^[A-Za-z]+-\d+$`, normalize to uppercase) → Step 2A
  - **If `$ATLASSIAN_ENABLED` is `false`**: Do not match Jira format. If the user provides a Jira ID (e.g., `PROJ-123`), display:
    > ⚠️ Atlassian integration is not configured. Only GitHub issues (#123) are supported.
    > To enable Atlassian, open the Magic Slash app → Settings → Integrations.
    
    Then stop.
- **GitHub**: Number with optional `#` (regex: `^#?\d+$`) → Step 2B
- **Unrecognized**: Display `MSG_FORMAT_UNRECOGNIZED`.

## Step 2A: Retrieve the Jira ticket

Use `mcp__atlassian__getJiraIssue` to retrieve ticket details. If you don't know the `cloudId`, use `mcp__atlassian__getAccessibleAtlassianResources` first.

If the MCP call fails (timeout, auth error), retry once. If it fails again, ask the user to provide the ticket title and description manually so the workflow can continue.

**Completeness check.** The ticket's real spec may sit in a custom field. Read `references/jira-custom-fields.md` and follow it whenever `fields.description` does not state what to build: it is absent or null; or under **80 characters** of useful text once markup is stripped and not a complete one-liner ("Bump the Stripe SDK to v14" is a spec); or longer, yet stating neither what to build nor any acceptance criterion (every heading present with an empty or placeholder body, pure boilerplate, a deferral to another field, a bare link with no prose). A description that does say what to build never triggers it, however short — in doubt, skip, so this does not become a second full-issue call on every ticket. That file owns the discovery call, the volume guards, what the discovered text feeds into, and the handling of a ticket still empty afterwards. If it is missing on disk, skip discovery and degrade to the warning alone: say in one line that the ticket looks underspecified, ask the user for the missing context, and never fill the gap from the title alone.

→ Continue to Step 2.5, then Step 2.6.

## Step 2B: Retrieve the GitHub issue

### 2B.1: Read repos configuration

Read the live config fetched in Step 0 (kept in memory — `$CONFIG_FILE` does not survive into later bash blocks) to get the list of configured repos.

### 2B.2: Identify GitHub repos

For each configured repo, get owner/repo from the remote URL:

```bash
cd {REPO_PATH} && git remote get-url origin
```

Parse `owner/repo` from either `git@github.com:owner/repo.git` or `https://github.com/owner/repo.git`.

### 2B.3: Search for the issue

Use `mcp__github__get_issue` for each repo — launch all calls in parallel for speed. Collect all found issues. If an MCP call fails, retry once; if still failing, skip that repo and continue with the others.

### 2B.4: Resolution

- **No issue found**: Display `MSG_NO_ISSUE_FOUND`.
- **Single issue**: Use it. Scope = that repo.
- **Multiple issues**: Display `MSG_GITHUB_MULTI_ISSUE` and ask user to choose.

→ Continue to Step 2.5, then Step 2.6.

## Step 2.5: Update Magic Slash Desktop metadata

This step updates the Desktop sidebar so the user sees their task context. Without it, the UI shows a blank entry.

### 2.5.1: Generate ticket description

Generate a concise description (2-3 sentences max) in the configured language, based on the ticket title, description, and acceptance criteria.

### 2.5.2: Send metadata

```bash
[ -n "$MAGIC_SLASH_PORT" ] && [ -n "$MAGIC_SLASH_TERMINAL_ID" ] && curl -s "http://127.0.0.1:$MAGIC_SLASH_PORT/metadata?id=$MAGIC_SLASH_TERMINAL_ID&title=$(echo -n '{TICKET_ID}: {TICKET_TITLE}' | jq -sRr @uri)&ticketId={TICKET_ID}&description=$(echo -n '{DESCRIPTION}' | jq -sRr @uri)&status=in%20progress&type=coder&baseBranch={DEV_BRANCH}" > /dev/null 2>&1 || true
```

Replace `{TICKET_ID}`, `{TICKET_TITLE}` (max 30 chars), `{DESCRIPTION}`, `{DEV_BRANCH}`.

## Step 2.6: Update ticket status to "In Progress"

Updating ticket status keeps the board accurate for teammates. This step never blocks the process — on failure, display a warning and continue.

### 2.6A: Jira ticket

1. Retrieve transitions with `mcp__atlassian__getTransitionsForJiraIssue`
2. Look for: "In Progress", "En cours", "In Development", "Started", "In Work"
3. Apply with `mcp__atlassian__transitionJiraIssue`
4. On failure: Display `MSG_TRANSITION_FAILED`

### 2.6B: GitHub issue

1. Check if a progress label exists: "in-progress", "wip", "in progress", "working"
2. If found: Add via `mcp__github__update_issue` (keep existing labels)
3. If not found: Continue without modification (do not create a label)
4. On failure: Display `MSG_LABEL_FAILED`

→ Continue to Step 3.

## Step 3: Search for existing worktrees

Searching worktrees first avoids creating duplicates if work already started.

For **each configured repo** in config.json:

```bash
cd {REPO_PATH}
REPO_NAME=$(basename "$PWD")
WORKTREE_PATH="../${REPO_NAME}-$TICKET_ID"

# Check if a worktree exists with the magic-slash naming convention
if [ -d "$WORKTREE_PATH" ]; then
  echo "WORKTREE_FOUND: $(cd "$WORKTREE_PATH" && pwd)"
fi

# Also check via git worktree list (handles worktrees at other locations)
git worktree list | grep -i "$TICKET_ID"
```

Collect all found worktrees with their absolute paths.

- **If worktree(s) found** → Go to Step 5 (Resolution)
- **If no worktree found** → Go to Step 4 (Search branches)

## Step 4: Search for existing branches (if no worktree found)

Searching branches preserves commit history from previous sessions.

For **each configured repo**:

```bash
cd {REPO_PATH}
# Local branches
git branch --list "*${TICKET_ID}*"
# Remote branches (fetch first)
git fetch origin
git branch -r --list "*${TICKET_ID}*"
```

If `git fetch` fails, display `MSG_FETCH_FAILED` and continue with local state only.

If multiple branches match the ticket ID in the **same repo**, list them and ask the user to choose.

Collect all found branches (local and remote) with their associated repo.

- **If branch(es) found** → Go to Step 5 (Resolution)
- **If nothing found** → Go to Step 5, Case 3

## Step 5: Resolution and action

The rest of the skill operates from inside the worktree — all subsequent commands must target the right directory.

### Case 1 — Worktree(s) found

- **Single worktree**: `cd` into it directly.

- **Multiple worktrees (multi-repo)**: Display `MSG_MULTI_WORKTREE` with the list. For "all" → multi-repo flow (Step 6.5).

### Case 2 — Branch(es) found but no worktree

Display `MSG_BRANCH_FOUND_NO_WORKTREE` with branch details.

Create the worktree from the branch:

```bash
cd {REPO_PATH}
REPO_NAME=$(basename "$PWD")
git fetch origin
# If local branch exists
git worktree add ../${REPO_NAME}-$TICKET_ID {BRANCH_NAME}
# If remote branch only
git worktree add ../${REPO_NAME}-$TICKET_ID origin/{BRANCH_NAME}
cd ../${REPO_NAME}-$TICKET_ID
```

If `git fetch` fails, display `MSG_FETCH_FAILED` and continue with local state.

If multiple branches in different repos → propose creating worktrees for each (multi-repo).

### Case 3 — Nothing found

Display `MSG_NOTHING_FOUND`. → Stop the skill.

## Step 6: Attach repo to the agent

This tells the Desktop sidebar which project this terminal belongs to, so the user sees it grouped correctly.

```bash
[ -n "$MAGIC_SLASH_PORT" ] && [ -n "$MAGIC_SLASH_TERMINAL_ID" ] && curl -s "http://127.0.0.1:$MAGIC_SLASH_PORT/repositories?id=$MAGIC_SLASH_TERMINAL_ID&repos=$(echo -n '["'$(pwd)'"]' | jq -sRr @uri)" > /dev/null 2>&1 || true
```

**Report the branch** — reported HERE and not in step 2.5 alongside the rest of the metadata, because step 2.5 runs from the main repo: the worktree is only located in step 3 and entered in step 5, so `git branch --show-current` would there return the development branch and fill `branch_name` with the wrong value. Null is recoverable, a plausible wrong branch is not.

Read from git rather than from `{BRANCH_NAME}`: whichever of Case 1 or Case 2 got here, this reports the branch actually checked out.

```bash
[ -n "$MAGIC_SLASH_PORT" ] && [ -n "$MAGIC_SLASH_TERMINAL_ID" ] && curl -s "http://127.0.0.1:$MAGIC_SLASH_PORT/metadata?id=$MAGIC_SLASH_TERMINAL_ID&branchName=$(echo -n "$(git branch --show-current)" | jq -sRr @uri)" > /dev/null 2>&1 || true
```

## Step 6.1: Copy worktree files (only if worktree was newly created in Case 2)

If the worktree already existed (Case 1), skip this step.

Check if the repository has `worktreeFiles` configured (`.repositories.<name>.worktreeFiles`).

### Case A: `worktreeFiles` is non-empty

Copy each file from the **main repo** to the **worktree**:

```bash
MAIN_REPO="{REPO_PATH}"
for FILE in {WORKTREE_FILES_LIST}; do
  if [ -f "$MAIN_REPO/$FILE" ]; then
    cp "$MAIN_REPO/$FILE" "./$FILE"
  fi
done
```

Only copy files that exist; silently skip missing ones. Display `MSG_WORKTREE_FILES_COPIED`.

### Case B: Not configured — auto-detect

Scan the **main repo** for common untracked files:

```bash
MAIN_REPO="{REPO_PATH}"
CANDIDATES=(.env .env.local .env.development .env.development.local .env.test .env.test.local .env.production.local .npmrc .yarnrc .yarnrc.yml)
DETECTED=()
for f in "${CANDIDATES[@]}"; do
  if [ -f "$MAIN_REPO/$f" ]; then
    if ! git -C "$MAIN_REPO" ls-files --error-unmatch "$f" > /dev/null 2>&1; then
      DETECTED+=("$f")
    fi
  fi
done
```

If files detected: Display `MSG_WORKTREE_FILES_DETECTED`. If user says yes, persist the choice to the cloud:

```bash
# The app owns the write: it is the only process holding the cloud session. Silent and
# non-blocking, as every write endpoint is. Its own shell, so resolve the port again.
MS_PORT="${MAGIC_SLASH_PORT:-$(cat ~/.config/magic-slash/port 2>/dev/null)}"
curl -s "http://127.0.0.1:$MS_PORT/config/worktree-files?path=$(echo -n "$PWD" | jq -sRr @uri)&files=$(echo -n '["file1","file2"]' | jq -sRr @uri)" > /dev/null 2>&1 || true
```

Then copy files either way. If no files detected, skip silently.

## Step 6.2: Install dependencies (only if worktree was newly created in Case 2)

Read `references/node-setup.md` to detect the Node.js version manager and set `$NODE_PREFIX`.

**Detect package manager** — check lock files, first match wins:

| Lock file | Package manager | Install command |
|-----------|----------------|-----------------|
| `bun.lockb` or `bun.lock` | bun | `bun install` |
| `yarn.lock` | yarn | `yarn install` |
| `pnpm-lock.yaml` | pnpm | `pnpm install` |
| `package-lock.json` | npm | `npm install` |

If no lock file but `package.json` exists, default to `npm install`. If no `package.json`, skip.

For Node.js projects, prepend `$NODE_PREFIX` to the install command.

Display `MSG_INSTALLING_DEPS`. On failure, display `MSG_INSTALL_FAILED` and continue.

## Step 6.5: Report context and attach repos (multi-repo only)

Linking worktrees in the Desktop UI lets the user see them as one task.

If multiple worktrees:

1. Send full-stack metadata:

```bash
[ -n "$MAGIC_SLASH_PORT" ] && [ -n "$MAGIC_SLASH_TERMINAL_ID" ] && curl -s "http://127.0.0.1:$MAGIC_SLASH_PORT/metadata?id=$MAGIC_SLASH_TERMINAL_ID&fullStackTaskId={TICKET_ID}&relatedWorktrees=$(echo -n '["{WORKTREE_PATH_1}","{WORKTREE_PATH_2}"]' | jq -sRr @uri)" > /dev/null 2>&1 || true
```

2. Attach all worktrees:

```bash
[ -n "$MAGIC_SLASH_PORT" ] && [ -n "$MAGIC_SLASH_TERMINAL_ID" ] && curl -s "http://127.0.0.1:$MAGIC_SLASH_PORT/repositories?id=$MAGIC_SLASH_TERMINAL_ID&repos=$(echo -n '["{WORKTREE_PATH_1}","{WORKTREE_PATH_2}"]' | jq -sRr @uri)" > /dev/null 2>&1 || true
```

3. Create `CLAUDE.local.md` in each worktree (if missing) using `MSG_MULTI_REPO_CONTEXT`. Adapt language to `.languages.discussion`.

4. `cd` into the **first** worktree to begin work.

## Step 7: Check for existing Pull Request

Detecting an existing PR lets the summary show its status and updates the Desktop UI with a clickable link.

For each worktree/repo:

### 7.1: Get branch and repo info

```bash
BRANCH=$(git branch --show-current)
REMOTE_URL=$(git remote get-url origin)
# Parse owner/repo from REMOTE_URL
```

### 7.2: Search for an open PR

Use `mcp__github__list_pull_requests`:
- `owner`: repo owner
- `repo`: repo name
- `head`: `"owner:branch-name"`
- `state`: `"open"`

If the MCP call fails, retry once. If still failing, skip PR detection and continue.

### 7.3: If PR found

Update Desktop metadata with PR info:

```bash
[ -n "$MAGIC_SLASH_PORT" ] && [ -n "$MAGIC_SLASH_TERMINAL_ID" ] && curl -s "http://127.0.0.1:$MAGIC_SLASH_PORT/metadata?id=$MAGIC_SLASH_TERMINAL_ID&prUrl=$(echo -n '{PR_URL}' | jq -sRr @uri)&prRepo=$(echo -n "$PWD" | jq -sRr @uri)" > /dev/null 2>&1 || true
```

### 7.4: If no PR found

Continue without PR info.

## Step 8: Quick status summary

Display a summary of the current state:

```bash
# Commits on the branch (compared to dev branch)
git log --oneline origin/$DEV_BRANCH..HEAD

# Uncommitted modified files
git status --short

# Current diff summary
git diff --stat
```

Display `MSG_RESUME_SUMMARY` (or `MSG_RESUME_SUMMARY_FULLSTACK` for multi-repo). No planning/exploration step — just display git state + PR status. The user can ask for a deeper analysis if needed.

## Step 9: Record the run

**Always run this, as the very last thing you do — including when the workflow stopped early.**

Magic Slash opened a run record when this skill started. This closes it. Without it the run stays open and is counted as *abandoned*, so finished work disappears from the usage statistics.

Set `outcome` to `success` when the workflow completed, or `failed` when it stopped on an error you could not resolve.

This writes to a file instead of calling the desktop app, so it works whether or not the app is running.

```bash
MS_DIR="$HOME/.config/magic-slash"; mkdir -p "$MS_DIR" 2>/dev/null
printf '{"type":"end","skill":"magic-continue","agentId":"%s","outcome":"success","occurredAt":%s000}\n' \
  "$MAGIC_SLASH_TERMINAL_ID" "$(date +%s)" >> "$MS_DIR/pending-skills.ndjson" 2>/dev/null || true
```

---

For the Magic Slash Desktop API reference (endpoints `/metadata` and `/repositories`), see `references/api.md`.
