# whisper (Unified Skill)

## Core Instructions (SKILL.md)

---
name: whisper
description: "Manage the ~/.whisper/ knowledge workspace: init, check, status, link plan to beads, decommission, and knowledge routing. Trigger when the user says '/whisper', '/w', 'init workspace', 'check workspace', 'workspace status', 'link plan', or 'decommission worktree'."
tools: Read, Write, Edit, Bash
---

# Whisper — Accumulated Operational Knowledge

Manage `~/.whisper/` — a global, tiered knowledge directory that accumulates operational knowledge and planning context across all your work. Works with or without beads issue tracking.

## Directory Layout

```
~/.whisper/
  rules.md                          ← Global agent behavior rules (bd remember equiv)

  repos/
    github.com/user/repo/
      env.md                        ← Repo-wide: deploy commands, infra facts, auth quirks
      branches/
        branch-slug/                ← One slot per branch (/ in name → -- in slug)
          context.md                ← Branch anchor: repo, branch, beads epic ID, status
          plan.md                   ← Narrative implementation plan
          notes.md                  ← Free-form scratch and findings
          agents/                   ← Optional sub-agent files when work is split across agents
      worktrees/
        worktree-name/              ← One slot per worktree (basename of worktree path)
          env.md                    ← Worktree-specific: ports, venvs, credentials, local-only setup
```

**Branch slug rule:** `git rev-parse --abbrev-ref HEAD | sed 's|/|--|g'`

**Worktree slot rule:** `basename $(git rev-parse --git-common-dir 2>/dev/null)` (the `.git` worktree dir name), or `basename $(pwd)` when not in a worktree.

## Knowledge Routing

When a session learns something worth keeping, route it by scope:

| Scope | Test | Destination |
|---|---|---|
| **Global** | True for every repo and every project | `~/.whisper/rules.md` — append, don't overwrite |
| **Repo-wide infra** | True regardless of which branch or feature you're on (deploy behavior, gateway/timeout limits, log retention, auth quirks) | `~/.whisper/repos/<host>/<repo>/env.md` — create or extend |
| **Branch-specific** | Only matters for the work on *this* epic/branch | `~/.whisper/repos/<host>/<repo>/branches/<slug>/notes.md`, or `bd note <epic-id>` if beads is available |
| **Worktree setup** | Credentials, service IDs, ports, venv/docker specifics for *this checkout* | `~/.whisper/repos/<host>/<repo>/worktrees/<name>/env.md` |
| **Universal agent rule** | A standing instruction for how the agent should behave, independent of any branch or repo | `bd remember` if available, else `~/.whisper/rules.md` |

**Rules:**
- **No secrets anywhere.** Never write tokens, keys, passwords, or PII into `~/.whisper/`.
- **Extend, don't duplicate.** Append to or correct an existing note rather than creating a new one that says the same thing.
- **Search first.** Before creating a new entry, check if one already exists for the same topic.
- If `bd` is not available, everything falls back to `~/.whisper/` files — there's no beads-backed alternative.

## Modes

Determine mode from how the skill is triggered:

| Trigger | Mode |
|---|---|
| `/w init` or `~/.whisper/` does not exist | **init** |
| `/w` or `/w status` | **status** |
| `/w check` or called by `renew` | **check** |
| `/w link [<path>]` or "link plan" | **link** |
| `/w decommission` | **decommission** |

See each reference file for the full procedure:

- `references/init.md`
- `references/check.md`
- `references/status.md`
- `references/link.md`
- `references/decommission.md`

## Rules

- Never run `git worktree remove` automatically — always print the command for the user to execute.
- Never close beads issues during decommission — only defer. Closing is intentional.
- **One epic per branch, children via `--parent`, not flat siblings.**
- **`env.md` is shared and immutable across branch switches.** Never overwrite it during init or check — only create if absent.
- **Branch slug sanitization is mandatory.** Always run `sed 's|/|--|g'` on the branch name before using it as a directory path.
- **Worktree reuse across branches is normal, not an edge case.** Expect multiple slots to accumulate in `branches/` over time.
- **Legacy layout is still the common case.** Most worktrees never needed multiple branch slots — only migrate when a mismatch is detected.
- This skill is self-contained. Do not rely on `create-plan`, `park`, or `create-handoff` having `~/.whisper/` awareness.
- If `bd` is not initialized in the repo, skip beads steps gracefully and note what was skipped.

---

## Reference: check.md

# Check

Validate the workspace before starting work. Detects stale branch slots and worktree reuse. Called automatically by `renew` when `~/.whisper/` exists.

## Steps

1. Get repo URL and branch slug:
   ```bash
   repo_url=$(git remote get-url origin 2>/dev/null | sed 's|https://||;s|git@||;s|\.git$||')
   branch=$(git rev-parse --abbrev-ref HEAD)
   branch_slug=$(echo "$branch" | sed 's|/|--|g')
   context_path=~/.whisper/repos/"${repo_url}"/branches/"${branch_slug}"/context.md
   ```

2. **If `~/.whisper/` doesn't exist:** offer full init:
   ```
   ⚠ ~/.whisper/ not found. Run `/w init` to create the workspace.
   ```

3. **If `context.md` exists:** output one confirmation line and return.
   ```
   ✓ Workspace active: ~/.whisper/repos/<repo>/branches/<slug>/
   ```

4. **If the branch directory doesn't exist:**
   ```
   ⚠ No workspace slot for branch: <branch>
     ~/.whisper/repos/<repo>/branches/<slug>/ does not exist yet.

   Activate new slot? [Y/n]
   ```
   - Y → run **init** for the current branch.
   - N → print "Staying without a slot. Run `/w init` when ready." and stop.

---

## Reference: decommission.md

# Decommission

Safely wind down a worktree or branch: defer open work under the branch epic, record a final note, then print the removal command.

## Steps

1. Run **check** first. Confirm slot exists before decommissioning.

2. Get repo URL and branch slug:
   ```bash
   repo_url=$(git remote get-url origin 2>/dev/null | sed 's|https://||;s|git@||;s|\.git$||')
   branch=$(git rev-parse --abbrev-ref HEAD)
   branch_slug=$(echo "$branch" | sed 's|/|--|g')
   branch_dir=~/.whisper/repos/"${repo_url}"/branches/"${branch_slug}"
   context_path="$branch_dir/context.md"
   ```

3. Read `beads-epic:` from `context.md`.

4. If beads is available, get all open work under that epic:
   ```bash
   bd children <epic-id> --json 2>/dev/null
   ```

5. If any open children found, defer them:
   ```bash
   bd defer <id>
   ```

6. Defer the epic itself:
   ```bash
   bd defer <epic-id>
   ```

7. Record the decommission as a note on the epic:
   ```bash
   bd note <epic-id> "Decommissioned: branch <branch> on <date>. State: <summary>. Resume via: /w init on branch <branch> — bd search '<branch>' will find this epic."
   ```

8. Print the removal command — do NOT run it:
   ```
   Ready to remove worktree. Run:
     git worktree remove <path>

   Deferred epic <epic-id> and <n> children. To find them later:
     bd list --status deferred --label branch:<branch>
   ```

## Rules

- Never run `git worktree remove` automatically — always print the command.
- Never close beads issues during decommission — only defer. Closing is intentional.
- Branch slots in `~/.whisper/` are not automatically removed when a branch is merged. The slot serves as an archive; the epic (found via `bd search`) can be reused if the same branch name comes back.

---

## Reference: init.md

# Init

Create `~/.whisper/` and the current workspace slot.

## Steps

1. Get context:
   ```bash
   pwd
   git rev-parse --abbrev-ref HEAD
   basename $(git rev-parse --show-toplevel 2>/dev/null)   # repo name
   git rev-parse --git-common-dir 2>/dev/null              # confirms worktree vs main
   date +%Y-%m-%d
   ```

2. Create `~/.whisper/` if it doesn't exist:
   ```bash
   mkdir -p ~/.whisper/repos
   ```

3. Compute the repo path and branch slug:
   ```bash
   repo_url=$(git remote get-url origin 2>/dev/null | sed 's|https://||;s|git@||;s|\.git$||')
   branch=$(git rev-parse --abbrev-ref HEAD)
   branch_slug=$(echo "$branch" | sed 's|/|--|g')
   ```

4. Create the repo & branch directory:
   ```bash
   mkdir -p ~/.whisper/repos/"${repo_url}"/branches/"${branch_slug}"
   wk_dir=$(basename "$(git rev-parse --git-common-dir 2>/dev/null || echo '.git')" | sed 's|\.git$||')
   [ -n "$wk_dir" ] && mkdir -p ~/.whisper/repos/"${repo_url}"/worktrees/"${wk_dir}"
   ```

5. Create `~/.whisper/rules.md` if it doesn't exist:
   ```markdown
   # Global Agent Rules

   > Standing instructions for the agent, independent of any repo or branch.
   > Append here for rules that apply everywhere.
   ```

6. Create the repo `env.md` if it doesn't exist:
   ```markdown
   # Environment — <repo-name>

   > Repo-wide setup knowledge. True regardless of which branch or worktree.
   > Deploy commands, gateway limits, auth quirks that apply everywhere.
   > Worktree-specific setup (ports, venvs, local creds) goes in worktrees/<name>/env.md
   ```

7. Create the worktree `env.md` if it doesn't exist:
   ```markdown
   # Worktree — <worktree-name>

   > Worktree-specific setup. Credentials, ports, venv/docker specifics for THIS checkout only.
   ```

8. Create `context.md`:
   ```markdown
   # Workspace Context — <full-branch-name>

   repo: <repo-url>
   branch: <full-branch-name>
   worktree: <worktree-dir-name>
   path: <absolute-path>
   created: <YYYY-MM-DD>
   beads-epic:
   ```

9. Create `plan.md` stub:
   ```markdown
   # Plan — <full-branch-name>

   > No plan yet. Write a plan here, then run `/w link` to create beads issues.
   ```

10. Create `notes.md` stub:
    ```markdown
    # Notes — <full-branch-name>
    ```

11. If beads is available, find or create the branch epic:
    ```bash
    bd search "<branch>" --status all 2>/dev/null
    ```
    - If an epic already matches, reuse its ID and update `beads-epic:` in `context.md`.
    - If the work is substantial enough, create one: `bd create "<branch>" --type epic --label "branch:<branch>" --silent`
    - For a single-ticket fix, skip the epic and let the ticket ID stand in for `beads-epic:`.

12. Confirm:
    ```
    ✓ ~/.whisper/ initialized

    Repo:  <repo-url>
    Branch: <branch> → branches/<slug>/
    Epic:  <id | none — will be created by /w link>
    rules.md:     [created | exists]
    env.md:       [created | exists]
    worktree/env.md: [created | exists]

    Run `/w link` after writing a plan.
    ```

---

## Reference: link.md

# Link

Attach a plan file to the workspace and create beads issues for each phase, as children of the branch epic. Run this after writing or generating a plan.

This mode is self-contained — it does NOT require `create-plan` to have been used. The plan can come from anywhere: manually written, generated by any tool, or copied in.

## Steps

1. Run **check** first to confirm the slot exists.

2. Get repo URL and branch slug:
   ```bash
   repo_url=$(git remote get-url origin 2>/dev/null | sed 's|https://||;s|git@||;s|\.git$||')
   branch=$(git rev-parse --abbrev-ref HEAD)
   branch_slug=$(echo "$branch" | sed 's|/|--|g')
   branch_dir=~/.whisper/repos/"${repo_url}"/branches/"${branch_slug}"
   context_path="$branch_dir/context.md"
   plan_path="$branch_dir/plan.md"
   ```

3. Determine the plan file path:
   - If the user provided a path (`/w link path/to/plan.md`): use that.
   - Otherwise: use `plan.md`.
   - If neither exists: tell the user to write a plan first and stop.

4. If the source path is not `plan.md`, copy it in:
   ```bash
   cp <source-path> "$branch_dir/plan.md"
   ```

5. Read `plan.md` and extract phase headings.

6. Ensure the branch epic exists:
   - If `context.md`'s `beads-epic:` is already set, use it.
   - If blank and the plan has more than one phase: create it.
   - If blank and the plan is a single task: skip the epic.

7. For each phase/task, create a beads issue as a child of the branch epic:
   ```bash
   bd create "<phase-title>" --type task --parent <epic-id>
   ```

8. Only if phases are genuinely sequential, chain dependencies:
   ```bash
   bd dep <id-N> --blocks <id-N+1>
   ```

9. Update `context.md` — set `beads-epic:` to the epic (or single-ticket) ID.

10. **After each successful creation**, extract the `**Files / Systems:**` bullet list from the issue description and store it as structured metadata:
    ```bash
    bd update <id> --metadata '{"files": ["path/to/file1.py", "path/to/file2.py"]}'
    ```
    This enables automated file-conflict detection by concurrent agents. If the Files/Systems section is empty or contains only subsystem names without concrete paths, **stop and ask for specific file paths** — a ticket without concrete paths cannot participate in conflict detection.

11. Confirm:
    ```
    Linked plan to workspace: branches/<slug>/
    Epic: <epic-id> — <branch>
    Created <n> child issues:
      <id>  Phase 1: ...
    ```

### Relinking

If `context.md` already has a `beads-epic:` set, ask before creating new children:
```
context.md already links epic <id> with <n> children.
Add new phases as more children of the same epic? [Y/n]
```

---

## Reference: status.md

# Status

Show the state of the current workspace.

## Steps

1. Get repo URL and branch slug:
   ```bash
   repo_url=$(git remote get-url origin 2>/dev/null | sed 's|https://||;s|git@||;s|\.git$||')
   branch=$(git rev-parse --abbrev-ref HEAD)
   branch_slug=$(echo "$branch" | sed 's|/|--|g')
   context_path=~/.whisper/repos/"${repo_url}"/branches/"${branch_slug}"/context.md
   ```

2. If `context.md` doesn't exist, delegate to **check** mode and stop.

3. Read `context.md` and extract: repo, branch, path, beads-epic.

4. Show `env.md` info:
   ```bash
   date -r ~/.whisper/repos/"${repo_url}"/env.md "+%Y-%m-%d %H:%M" 2>/dev/null
   ```

5. Show the branch epic's tree if beads is available:
   ```bash
   bd children <beads-epic> --pretty 2>/dev/null
   ```

6. Show plan status: is `plan.md` a stub or does it have real content?

7. Show all active branch slots for this repo (most recently modified first):
   ```bash
   ls -t ~/.whisper/repos/"${repo_url}"/branches/ 2>/dev/null
   ```

### Output format

```
── Whisper: <repo> ──
Branch:    <branch>  (branches/<slug>/)
Plan:      [exists | stub]
Epic:      <id> — <n> issues (<open> open)   [or: none linked yet]

Active branch slots:
  <ls output>

Epic tree:
  <bd children --pretty output, or "none">
```

---

