---
name: conductor-spawn
version: 0.1.0
description: |
  Spawn or open a Conductor workspace via the conductor:// URL scheme. Wraps all
  four documented routes: prompt, prompt+path, linear_id, and async/plan. Handles
  URL-encoding and base64-encoding so you can hand off work to a parallel agent
  without leaving the current chat. Use when asked to "spawn a workspace",
  "hand off to a conductor agent", "open this in a new workspace", "kick off a
  parallel agent on X", or "create a conductor workspace for".
triggers:
  - spawn a workspace
  - spawn workspace
  - hand off to a conductor agent
  - open in a new conductor workspace
  - kick off a parallel agent
  - create a conductor workspace
  - spawn an agent on this
allowed-tools:
  - Bash
  - Read
  - AskUserQuestion
---

## What this does

Constructs a `conductor://` deep-link URL and invokes `open` on macOS, which the
Conductor desktop app handles by spawning or opening a workspace. Reference:
<https://www.conductor.build/docs/reference/deep-links>.

Four routes are supported. Pick by what the user supplied:

| User intent | Route | URL form |
|---|---|---|
| Just a prompt, any repo | Prompt | `conductor://prompt=<encoded>` |
| Prompt for a specific repo | Prompt + path | `conductor://prompt=<encoded>&path=<encoded path>` |
| Work on a Linear issue | Linear | `conductor://linear_id=<id>&prompt=<encoded optional>` |
| Hand off a full plan/spec | Async plan | `conductor://async?repo=<name>&plan=<base64>` |

**Default route**: if the user is inside a git repo and didn't specify a target,
use **prompt + path** with the current repo root. That's almost always what they
mean.

## How to run it

### 1. Resolve inputs

- `PROMPT` — the prompt text to pre-fill. For the async route, this is irrelevant
  (plan is the payload). For Linear, it's optional context.
- `REPO_PATH` — absolute path to the target repo. Default:
  `git -C "$PWD" rev-parse --show-toplevel 2>/dev/null` (current repo).
- `LINEAR_ID` — only for the linear route. Whatever the user gave you (e.g.
  `ENG-1234`).
- `PLAN_SOURCE` — only for the async route. Either inline markdown text or a
  path to a `.md` file the user pointed at.
- `REPO_NAME` — only for the async route. The repo's short name, not the full
  path. Default: `basename` of the current repo root.

### 2. Encode

```bash
# URL-encode any free-text field (prompt, path). jq handles all edge cases.
encode_uri() { jq -rn --arg s "$1" '$s|@uri'; }

# Base64-encode a plan AND URL-encode the result. The URL-encode step is
# required, not optional — see "base64 needs URL-encoding" in gotchas below.
encode_plan_file() {
  jq -rn --arg s "$(base64 < "$1" | tr -d '\n')" '$s|@uri'
}
encode_plan_str() {
  jq -rn --arg s "$(printf '%s' "$1" | base64 | tr -d '\n')" '$s|@uri'
}
```

`path=` isn't explicitly documented as URL-encoded, but real repo paths contain
spaces and other chars that would break the URL. Encoding it defensively is
correct.

### 3. Build the URL

```bash
# Route 1 — prompt only
URL="conductor://prompt=$(encode_uri "$PROMPT")"

# Route 2 — prompt + path (default when inside a git repo)
URL="conductor://prompt=$(encode_uri "$PROMPT")&path=$(encode_uri "$REPO_PATH")"

# Route 3 — Linear issue (prompt optional)
URL="conductor://linear_id=$LINEAR_ID"
[ -n "$PROMPT" ] && URL="${URL}&prompt=$(encode_uri "$PROMPT")"

# Route 4 — async plan (plan helper already URL-encodes)
URL="conductor://async?repo=$(encode_uri "$REPO_NAME")&plan=$(encode_plan_file "$PLAN_FILE")"
```

### 4. Confirm before opening

Spawning a workspace is a visible side-effect — it pops a new entry in the
user's Conductor app. Before invoking `open`, show the user:
- Which route you picked and why
- Target repo (name + path for routes 2/4)
- A one-line summary of the prompt (truncate at ~80 chars)
- The full URL (so they can copy/paste it elsewhere)

Use `AskUserQuestion` to confirm with options like "Open it" / "Just print the
URL, don't open" / "Adjust the prompt first".

### 5. Open

```bash
open "$URL"
echo "Opened: $URL"
```

`open` returns immediately; the Conductor app handles the rest. If the URL
scheme isn't registered (Conductor not installed), `open` will print an error to
stderr — surface that to the user.

## Edge cases & gotchas

- **Multi-line prompts**: `jq -rn --arg s "…" '$s|@uri'` handles newlines
  correctly (encodes as `%0A`). Don't strip them.
- **Path doesn't exist or isn't a registered repo**: per the docs, Conductor
  "falls back to the first repo if the path doesn't match" — so the spawn still
  works, just in the wrong repo. If you can `[ -d "$REPO_PATH" ]` check first
  and warn the user, do it.
- **Linear route without a connected Linear account**: the spawn opens but
  Conductor can't resolve the issue. There's no way to detect this
  pre-emptively from the URL side; if the user reports it didn't work, tell
  them to check their Conductor → Linear connection.
- **Plan size**: base64 inflates by ~33%. Very long plans may bump against URL
  length limits in the OS handler. If a plan is >100KB raw, warn the user and
  suggest trimming.
- **Stripping newlines from base64 is required** — some URL handlers truncate
  at the first newline.
- **Base64 needs URL-encoding** — standard base64 produces `+`, `/`, and `=`
  characters. In a URL query value, `+` decodes to a space, so an unencoded
  base64 payload arrives at Conductor corrupted and the workspace silently
  doesn't spawn. Confirmed empirically 2026-05-27 (~1.6KB plan, 2 `+`/4 `/`/2
  `=` in the base64; spawn failed with raw base64, succeeded after URL-encoding
  the base64 with `jq @uri`). The `encode_plan_file` / `encode_plan_str`
  helpers above do this for you — don't bypass them by base64-ing inline.

## What this skill does NOT do

- Does not query the Linear API. The user supplies the issue ID; we just pass
  it through.
- Does not switch the active workspace, send follow-up prompts to a running
  agent, or list existing workspaces. The `conductor://` scheme doesn't expose
  routes for any of that today.
- Does not write to memory or modify project files.

## Quick reference

```bash
# Spawn in current repo with a prompt
PROMPT="investigate why the auto-save is racing"
REPO_PATH="$(git rev-parse --show-toplevel)"
URL="conductor://prompt=$(jq -rn --arg s "$PROMPT" '$s|@uri')&path=$(jq -rn --arg s "$REPO_PATH" '$s|@uri')"
open "$URL"

# Hand off a written plan (base64 MUST be URL-encoded — see gotchas)
PLAN_FILE=/tmp/handoff-plan.md
REPO_NAME=$(basename "$(git rev-parse --show-toplevel)")
PLAN_B64=$(base64 < "$PLAN_FILE" | tr -d '\n')
URL="conductor://async?repo=$(jq -rn --arg s "$REPO_NAME" '$s|@uri')&plan=$(jq -rn --arg s "$PLAN_B64" '$s|@uri')"
open "$URL"
```
