Skip to content
Back to skills

Semble Setup

FSecurity

Installs, audits, repairs, updates, enables, reindexes or removes the semble_code semantic code-search MCP for a project. Triggers: semble, semantic code search, semble status, настрой semble, статус semble, переиндексируй, удали semble.

  • 32 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 5, 2026
ai-agentsgoshellbashgitbackend

Works with

  • claude code
  • cli
  • mcp

Security analysis

F27/100
  • criticalPipes output to a shell interpreter
  • mediumUses curl or wget to download content
  • highPerforms destructive filesystem operations
  • criticalDownloads and executes remote scripts — classic supply chain attack

Pro scans all 20 files and shows the line behind each finding

Scanned October 1, 2026

npx -y skills add kochetkov-ma/claude-brewcode --skill semble-setup --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Semble Setup?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Semble Setup
[![Security: F — Skills Directory](https://www.skillsdirectory.com/api/skills/kochetkov-ma-semble-setup/badge)](https://www.skillsdirectory.com/skills/kochetkov-ma-semble-setup)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: semble-setup
description: "Installs, audits, repairs, updates, enables, reindexes or removes the semble_code semantic code-search MCP for a project. Triggers: semble, semantic code search, semble status, настрой semble, статус semble, переиндексируй, удали semble."
user-invocable: true
disable-model-invocation: true
argument-hint: "[prompt] [status|install|upgrade|enable|disable|uninstall|purge|reindex|optimize|resume]"
allowed-tools: [Read, Bash, AskUserQuestion]
model: opus
---

# Semble

> Lifecycle router for **`semble_code`** — a semantic repository-search MCP server (semble `0.5.5`, pinned) registered at **user scope** with `alwaysLoad: true`, indexing the corpus named by `SEMBLE_CONTENT_ARGS` into one **shared cache root**. This skill decides the **mode**, prints the state **before** touching anything, and delegates every mutation to the scripts under `scripts/`. No mutation logic lives in this file.

Two tools become available once it is wired:

| Tool | Purpose |
|------|---------|
| `mcp__semble_code__search` | find code by intent / behavior / name |
| `mcp__semble_code__find_related` | neighbors of a KNOWN location, after a useful seed |

Both take a **required `repo`** parameter — the absolute project root, or an explicit `https://`/`http://` git URL. It is never inferred. Since 0.5.5 both also accept optional `content=code|docs|config|all`; omitting it uses the server's registered `code docs config` corpus. Results carry `file_path`, `start_line`, `end_line`, `score` and optional result `content` — **there is no result `line` field**. MCP defaults are `top_k=5`, `max_snippet_lines=10`.

## Honest limits — state these to the user, never oversell

| Fact | Consequence |
|------|-------------|
| **There is no watcher and no daemon.** semble 0.5.5 has no background thread, no service, nothing to start or stop. | Never report a daemon as running, starting or stopped. Staleness is re-checked *inside each tool call*, behind a `3x last-build-duration` cooldown. |
| The embedding model is pre-loaded when the MCP server starts, and tool calls block until it is ready | The **first query on a cold cache downloads the embedding model (hundreds of MB) and is slow** — allow up to 600 s and say so before starting. Offline with a cold HuggingFace cache = every call errors. |
| The corpus is whatever `SEMBLE_CONTENT_ARGS` says — read the constant, never retype it | `.json`/`.json5`/`.csv`/`.tsv`/`.psv` are **excluded from every content type** — unreachable even with `--content all` — and `.mdx`/`.txt` are in no bucket at all. Use `rg` for those. Full table: `references/language-coverage.md`. |
| …with **one hole**: a `.gitignore`/`.sembleignore` `!` negation whose text ends in a file extension bypasses the extension filter entirely (`_is_ignored`'s `found` flag) | An un-ignored `.json` or `.png` **is** indexed, at any `--content` setting, and reads as decoded binary. In the Semble 0.5.4 measurement from 2026-08-08, one negated lockfile was 5.9% of the index; that share was not rerun on 0.5.5. The cure is a re-ignore in `.sembleignore`, which wins because its lines are appended last. Never propose a content-set change to fix it. |
| Retrieval split — Semble 0.5.4 measurement from 2026-08-08, not rerun on 0.5.5 | At `k=5`, semble took behaviour and vocabulary-mismatch questions **8 of 9**; `rg` took exhaustive enumeration (semble lost 2 of 5) and exact identifiers. A question containing "every"/"all"/"how many" is an `rg` question — say so instead of running a search. |
| 0.5.5 stores each exact content selection separately below one hashed repo directory | The registered combined corpus uses `index-code-config-docs`; code-only remains `index`; per-call `docs` and `config` use their narrower sibling variants; and `all` resolves to the same `code config docs` set and `index-code-config-docs` variant as the registered corpus. Never treat a pre-0.5.5 combined corpus left at bare `index` as the current variant. |
| A newly registered MCP server is unavailable until a **NEW session** | `install` therefore stops at a reload checkpoint written to `.claude/semble/state.json` and resumes at verification via `/brewcode:semble-setup resume`. |
| The PreToolUse reminder is insurance, not a measured adoption mechanism | Measured 0 nudges over 7 sessions / 59 **Bash** calls (not 59 judged searches - zero of them had the multi-word behaviour shape the gate fires on). Conversion after a nudge is undefined (0/0), never report a percentage, and there is no control figure - the published "8/59" is withdrawn. `SubagentStart` is proven to **deliver** (8/8 installed vs 0/6 removed), not to change tool choice: both arms' spawn prompts already ordered semble first, 14/14. |
| `semble` has no `status` and no `serve`; any argv outside `_CLI_DISPATCH_ARGS` starts a **blocking** stdio server. `--version`/`-V` joined that set in **0.5.4** (`cli.py:25`, `:215`) — on `0.5.3` and older it is unrecognized argv and hangs | Never run bare `semble`. Resolvability comes from `uvx --from 'semble[mcp]==0.5.5' semble --version` -> prints `0.5.5`, exit 0. The argv is chosen **from the pin** by `sc_semble_probe_arg`, which degrades to the always-safe `--help` for any pin below 0.5.4 — a hang is not recoverable by a fallback. Version of a `uv tool install`ed copy still comes from `uv tool list` first, for the same reason. |
| `semble install` writes an **unpinned** server named `semble` into `~/.claude.json` | This skill never runs it. An existing `semble` server is *detected* and reported as a conflict, never auto-removed. |
| `semble clear index` wipes **every** repo and content variant under the shared cache root | Per-repo rebuild has no CLI. `reindex` stages, verifies and replaces exactly the selected `index-code-config-docs` variant; other variants survive. |
| Windows is unsupported by this skill | On a non-macOS/Linux platform: print `⚠️ Windows is unsupported by this skill` and refuse every mutation. |
| Stock macOS ships no `timeout` binary | Every shell-out is bounded regardless: `sc_timeout` uses `timeout`/`gtimeout` when one exists and a pure-bash watchdog when none does — `.timeout.bounded` is always `true`. `coreutils` (for `gtimeout`) is an **optional** upgrade, never a requirement, and never a reason to fail a run. |

<instructions>

## Constants

| Const | Value |
|-------|-------|
| MCP server name | `semble_code` |
| Pin | `semble[mcp]==0.5.5` — **always single-quoted** (`zsh` globs `[ ]`) |
| Scope | `user` (the `claude mcp` CLI default is `local` — `-s user` is mandatory) |
| Corpus | `SEMBLE_CONTENT_ARGS` in `scripts/lib/semble-common.sh` — the single source of truth for the server/default CLI `--content` argv. Every default-corpus consumer passes it verbatim so both select `index-code-config-docs`; a per-call MCP content override selects the corresponding exact variant. Never copy the token list into a new invocation |
| `alwaysLoad` | `true`, written by `add-json` only (`claude mcp add` has no flag for it). Without it the two tools stay deferred behind `ToolSearch` and are effectively uncallable |
| Shared cache root | macOS `$HOME/Library/Caches/semble-code` · Linux `${XDG_CACHE_HOME:-$HOME/.cache}/semble-code` |
| State file | `<projectRoot>/.claude/semble/state.json` |
| Tools | `mcp__semble_code__search`, `mcp__semble_code__find_related` (exact, never a wildcard) |

References — read the one you need, not all of them:

| File | When |
|------|------|
| `references/intent-routing.md` | **always** — the normative routing table + the 5-step resolution algorithm |
| `references/output-contract.md` | **always** — the final report template, verbatim |
| `references/mcp-and-cache.md` | MCP detection states, exact registration commands, cache layout |
| `references/language-coverage.md` | which suffixes land in which bucket, and what is uncovered |
| `references/project-agent-migration.md` | how project agent frontmatter is patched |

---

## Prompt contract

Position 1 of `$ARGUMENTS` is a **free-form prompt** (RU/EN) — modes are optional and may follow in
any order. Nobody types keys: resolve mode + scope FROM the prompt.

Routing table, keyword scoring and the 5-step resolution algorithm are **not duplicated here** — read
`references/intent-routing.md` (already listed **always** above) and apply it literally in Step 2.

Before Step 0 executes, resolve the prompt against known context and print one **PLAN**. If checkpoint
state is unknown, label the mode provisional and include the Step 1 status probe in DO. Step 2 refines
from that probe with one short update, never a second PLAN. `status` asks nothing.

```
PLAN — brewcode:semble-setup
INPUT:  <$ARGUMENTS verbatim, or "(empty)">
MODE:   <resolved mode> — <matched keyword: X | default | checkpoint resume | no keyword matched>
SCOPE:  <project root, cache dir, MCP scope, pinned semble version>
DO:     <2-5 imperative bullets — the steps this mode actually runs>
RESULT: <what the user ends up holding: MCP registration, rule + hooks, warm index, report>
```

---

## Step 0 — Resolve the skill directory

`$CLAUDE_PLUGIN_ROOT` is **empty** in skill bash blocks. Resolve `${CLAUDE_SKILL_DIR}` with a plugin-cache fallback, and repeat these two lines at the top of **every** later block (a new Bash call inherits nothing).

`${CLAUDE_SKILL_DIR}` is a **text substitution on the skill prompt**, not an environment variable: `getPromptForCommand` runs `W.replace(/\$\{CLAUDE_SKILL_DIR\}/g, <skill dir>)` (verified in the CC 2.1.226 binary), and that regex matches the **bare literal only**. Write it bare and test for emptiness on the next line. A brace-modifier spelling such as `${CLAUDE_SKILL_DIR:-<fallback>}` is never matched, reaches the shell verbatim, and — since the name is genuinely unset in the Bash tool environment — makes the fallback win on *every* run.

The fallback matches **two** leaf names — `semble-setup` (current) and `semble` (the pre-rename layout still sitting in installed caches, e.g. `brewcode/4.10.1/skills/semble`) — and it uses `find`, not a shell glob: an unmatched glob is a hard error in `zsh` and an empty string in `bash`, and both spellings resolved to a silent miss. `sort -V | tail -1` keeps the newest version, and within one version prefers `semble-setup` over `semble`.

**EXECUTE** using Bash tool:

```bash
SD="${CLAUDE_SKILL_DIR}"
[ -n "$SD" ] || SD="$(find "$HOME/.claude/plugins/cache/claude-brewcode/brewcode" -maxdepth 3 -type d -path '*/skills/*' \( -name semble-setup -o -name semble \) 2>/dev/null | sort -V | tail -1)"
case "$SD" in "$HOME"/.claude/plugins/cache/*) echo "⚠️  SD came from the plugin CACHE, not \$CLAUDE_SKILL_DIR — assets may be older than the marketplace HEAD" ;; esac
test -d "$SD/scripts" && test -f "$SD/scripts/semble-status.sh" && echo "SD=$SD" && echo "✅" || echo "❌ FAILED — skill dir unresolved: SD='$SD'"
```

> **STOP if ❌** — the plugin cache is incomplete. Run `/brewtools:plugin-update` (or `claude plugin update brewcode@claude-brewcode`) and retry.
>
> **The ⚠️ line is load-bearing, not cosmetic.** It fires whenever the substitution did not happen — the skill body was copied into an agent prompt, or the block was re-run outside skill mode — and the cache then answers instead of the checkout. A run from an up-to-date working tree would silently install the cache's older hook assets over the newer ones. Whenever the ⚠️ fires, print `SD` in the report and say which version directory it names. Note `--plugin-dir <working tree>` alone does **not** redirect this resolution: the substituted value is the directory the skill was *loaded* from.

---

## Step 1 — STATUS FIRST, in every mode, before any decision

Never install, repair, reindex or remove blind. This block is read-only: `semble-status.sh` writes nothing under the project, the cache root or `~/.claude/settings.json`, not even a throttle marker (verified by full-tree snapshots).

Two side effects belong to the tools it shells out to, not to this skill — say them once, do not hide them:

| Side effect | Detail |
|-------------|--------|
| `claude mcp get semble_code` (MCP detection) | the real `claude` CLI may touch its own `~/.claude.json` / statsig files. The skill itself writes neither. |
| `uvx --from 'semble[mcp]==0.5.5' semble --version` (pin resolvability) | an **uncached network fetch on the first run** — slow on a cold uv cache, and it fails offline. Nothing is installed by it. |

**EXECUTE** using Bash tool:

```bash
SD="${CLAUDE_SKILL_DIR}"
[ -n "$SD" ] || SD="$(find "$HOME/.claude/plugins/cache/claude-brewcode/brewcode" -maxdepth 3 -type d -path '*/skills/*' \( -name semble-setup -o -name semble \) 2>/dev/null | sort -V | tail -1)"
bash "$SD/scripts/semble-status.sh" --section all --json; RC=$?
echo "RC=$RC"
[ "$RC" -eq 0 ] && echo "✅" || echo "❌ FAILED"
```

> **STOP if ❌** — a status run that cannot even produce a report means the scripts are broken or the platform is unsupported. Report the raw output; change nothing.

Read from the JSON: `.verdict`, `.state.phase`, `.state.enabled`, `.mcp.state`, `.mcp.scopes`, `.cache.staleness`, `.guidance`, `.agents.summary`, `.prereq`, `.nextStep`. A section that came back as `{"error": ...}` is reported as unknown for that section — it never suppresses the rest.

> **An unmeasurable half must never report `ready` with `--strict` exiting 0.** An **errored** `guidance` probe therefore always downgrades the verdict to `partial` (`guidance probe failed: <error>`), whatever the cause — "unit not installed" included, because that leaves the rule, the hooks and the permissions exactly as unproven as a probe that crashed. A **null** measurement never downgrades anything: null means the section was not requested (`--section mcp`), and treating an absent measurement as a defect would fail `--strict` on a healthy repo. `cache` and `agents` stay lenient by design — each witnesses one optional fact and carries its own error for the reader.

Keep this JSON as the immutable **Before** snapshot. Step 2 prints Detection/Before after final routing;
the initial PLAN already preceded Step 0/1 execution. Never refresh Before after a mutation.

---

## Step 2 — Resolve the mode

Read `references/intent-routing.md` and apply its 5-step algorithm to `$ARGUMENTS` **literally**. The load-bearing parts:

1. Empty / whitespace-only input -> **`status`**, read-only, no questions. Never a mutation.
2. `phase == "awaiting_reload"` and no mode named -> **`resume`** (checked before scoring).
3. Highest count of distinct matched keywords wins.
4. Ties: destructive involved -> ask; `status` involved -> `status`; two mutating modes -> first keyword;
   all zero -> `status`, two plausible modes in **Detection**, one concrete **Next Step**.
5. Bundle known choices into one `AskUserQuestion` (max 4 questions): routing/removal, MCP scope/repair,
   machine prerequisites, reindex, pin change, or concrete managed-file replacement. New destructive or
   permission decisions discovered later require a separate explicit gate unless already authorized.
   The `uv` (3.1b) and `coreutils` (3.1d) offers remain mutually exclusive; never treat a backup or `--yes`
   as user consent.

Refine the provisional mode if Step 1 changes it; report the reason. Print **Detection**/**Before** now,
before mutations, using the saved Step 1 JSON. Do not repeat the initial PLAN; routine choices need no question.

### Early exit

If the resolved mode is `status`, or if everything is already `ready` and the intent is vague, print the report and **stop**. Do not re-install and do not open a chain of questions.

### Mode -> script routing (every mode terminates in a script subcommand)

| Mode | Route |
|------|-------|
| `status` | `semble-status.sh --section all --json` (Step 1 output; nothing further) |
| `install` | Step 3 chain: `semble-install.sh all --json` (probe: `check -> uv -> coreutils -> semble`; exit 4 = confirm) -> confirm -> `semble-install.sh all --yes --json`, or on exit 0 the report-driven `semble-install.sh coreutils --yes --json` offer -> `semble-mcp.sh detect`/`add`/`repair` -> `semble-guidance.sh install` + `semble-agents.sh apply` (Step 3.2b) -> **reload checkpoint** |
| `upgrade` | independently check prerequisites + MCP -> repair only when needed and approved -> always run project guidance/agents after successful preflight -> reload checkpoint when registration changed or already awaiting reload |
| `enable` | `semble-project.sh enable --yes --json` |
| `disable` | `semble-project.sh disable --yes --json` |
| `uninstall` | `AskUserQuestion` flavour -> `semble-remove.sh <integration\|mcp\|cli> --yes --json` |
| `purge` | `semble-remove.sh purge --yes --confirm-text "purge semble code cache" --json` |
| `reindex` | `semble-project.sh reindex --json` (dry, prints the target dir) -> confirm -> `semble-project.sh reindex --yes --json` |
| `optimize` | read-only fan-out: `semble-project.sh audit --json` + `semble-cache.sh info --json` + `semble-guidance.sh status --json` + `semble-agents.sh audit --json` |
| `resume` | Step 4: `semble-state.sh phase verifying` -> `semble-project.sh smoke` -> `semble-guidance.sh install` -> `semble-agents.sh apply` -> `semble-state.sh phase ready` |

Exit codes: `0` ok · `1` hard failure (earlier writes may survive) · `2` bad usage · `3` recoverable
precondition/conflict/skipped verification · `4` confirmation required. Read each script's JSON and final
status; exit 1 is never a universal rollback guarantee. Record actual changed/failed/skipped surfaces.

---

## Step 3 — `install` chain (also the repair path)

State the concrete plan first — exact commands, exact paths — then run the blocks in order. Stop at the first ❌.

### 3.1 Prerequisites — one hard gate (`uv`), one soft gate (`coreutils`)

`semble-install.sh all` runs `check -> uv -> coreutils -> semble`. Two machine-level `brew install`s can appear in it and they are gated **differently** — never collapse them into one rule:

| Step | Gate | Effect on the run's exit code |
|------|------|-------------------------------|
| `brew install uv` | **hard** — exit `4`, nothing runs without an explicit confirmation (same gate as `reindex`, `purge`, `agents apply`) | drives it: `4` confirm · `3` no brew · `1` failed |
| `brew install coreutils` -> `gtimeout` | **soft** — inside `all` a missing `--yes`, a missing `brew`, a failed brew, `SEMBLE_NO_NETWORK` and `SEMBLE_DRY_RUN` all stay a *note* | none. It can never make `all` non-zero |

Only `brew` is used to obtain `uv`; the pin itself comes from `uvx`. Narrating a plan is not a gate. Default mode is **uvx-ephemeral** — no `uv tool install`, because a `semble` on `PATH` is a hazard (a bare invocation blocks).

Why the soft step still gets offered: `sc_timeout` is bounded **either way** (`.timeout.bounded` is always `true` — a real binary when one exists, the pure-bash watchdog when it does not), so a missing `gtimeout` is a degradation, not a failure. But on a machine that already has `uv`, `all` exits `0` and the note would scroll past unseen. **Therefore the coreutils decision is read from the report, never from the exit code.**

Keys to read from the probe JSON — decide on these, not on `RC` alone:

| Key | Meaning |
|-----|---------|
| `.timeout.backend` | `timeout` / `gtimeout` = a binary backs `sc_timeout`; `none` = the bash watchdog does |
| `.timeout.bounded` | always `true` — the invariant. Never report semble's shell-outs as unbounded |
| `.timeout.coreutils.status` | `present` · `installed` · `needs_confirmation` · `skipped` · `failed` |
| `.timeout.coreutils.reason` | the one clause to quote when the step did not run |
| `.brew.present` | whether `brew install coreutils` is even possible |
| `.commands` | the exact command list — print it verbatim, never retyped |

**3.1a — probe. `--yes` is absent, so nothing can be installed.**

**EXECUTE** using Bash tool:

```bash
SD="${CLAUDE_SKILL_DIR}"
[ -n "$SD" ] || SD="$(find "$HOME/.claude/plugins/cache/claude-brewcode/brewcode" -maxdepth 3 -type d -path '*/skills/*' \( -name semble-setup -o -name semble \) 2>/dev/null | sort -V | tail -1)"
bash "$SD/scripts/semble-install.sh" all --json; RC=$?
echo "RC=$RC   # 0 = uv already present | 3 = precondition | 4 = confirmation required, nothing installed"
{ [ "$RC" -eq 0 ] || [ "$RC" -eq 3 ] || [ "$RC" -eq 4 ]; } && echo "✅" || echo "❌ FAILED"
```

> **STOP if ❌** — exit 1/2 means nothing was installed; report the raw output.
> `RC=3` is not a failure: a precondition is unmet (no `brew`) — print the manual fallback `curl -LsSf https://astral.sh/uv/install.sh | sh` **without running it**, and stop.
> `RC=0` means `uv`/`uvx` are already on `PATH` and the pin resolved: skip 3.1b/3.1c and go to **3.1d** — `RC=0` does *not* mean there is nothing to confirm.

**3.1b — ask, only on `RC=4`.** Print the `.commands` array from the probe JSON **verbatim** — that is the exact list that would run. On a machine with neither `uv` nor a timeout binary it is all three lines; the coreutils line is absent when a binary already backs `sc_timeout`:

```text
brew install uv
brew install coreutils
uvx --from 'semble[mcp]==0.5.5' semble --version
```

Then one `AskUserQuestion`: *"Run these Homebrew installs now?"* — options `Install` (runs exactly the commands printed above) / `Cancel` (nothing runs; `install` stops and the manual fallback `curl -LsSf https://astral.sh/uv/install.sh | sh` is printed, not run). Say plainly that `brew install` writes to the machine, outside this project, and that `coreutils` is the optional half: it only upgrades `sc_timeout` from its bash watchdog to `gtimeout`. On `Cancel`: emit the report with `Actions -> skipped: brew install uv (declined)` and end the invocation.

> Bundle this with known MCP scope/repair choices from Step 1. It covers both installs, so skip 3.1d
> after 3.1c; a newly discovered required permission/replacement gate follows Step 2 rule 5.

**3.1c — apply, only after an explicit `Install`.**

**EXECUTE** using Bash tool:

```bash
SD="${CLAUDE_SKILL_DIR}"
[ -n "$SD" ] || SD="$(find "$HOME/.claude/plugins/cache/claude-brewcode/brewcode" -maxdepth 3 -type d -path '*/skills/*' \( -name semble-setup -o -name semble \) 2>/dev/null | sort -V | tail -1)"
bash "$SD/scripts/semble-install.sh" all --yes --json; RC=$?
echo "RC=$RC"
{ [ "$RC" -eq 0 ] || [ "$RC" -eq 3 ]; } && echo "✅" || echo "❌ FAILED"
```

> **STOP if ❌** — exit 1/2 means the install failed; report the raw output. `RC=3` is still not a failure: `brew` is missing — print the manual fallback `curl -LsSf https://astral.sh/uv/install.sh | sh` **without running it**, and stop.
> Read `.timeout.coreutils.status` from this run too and put it in `Actions` (`installed` -> changed, anything else -> skipped with its `reason`). It never changes the verdict. Then go to 3.2 — 3.1d does not run on this path.

**3.1d — the coreutils offer, only when 3.1b did not fire (`RC=0`).** Take the FIRST matching row and act; there is no fallthrough:

| # | Condition (from the 3.1a JSON) | Action |
|---|--------------------------------|--------|
| 1 | `.timeout.backend != "none"` | Nothing to install. One line: `timeout: <backend> <path> — sc_timeout is bound by a binary.` -> 3.2 |
| 2 | `.timeout.backend == "none"` **and** `.timeout.coreutils.status == "needs_confirmation"` **and** `.brew.present == true` | Include the optional offer in the bundled question; apply only if approved. -> 3.1e |
| 3 | `.timeout.backend == "none"` **and** `.brew.present == false` | One line: `no brew — gtimeout cannot be installed; sc_timeout keeps its bash watchdog (still bounded).` -> 3.2 |
| 4 | anything else (`skipped` / `failed` / dry / no-network) | One line quoting `.timeout.coreutils.reason`. -> 3.2 |

Row 2 prints the `brew install coreutils` line from `.commands` verbatim and asks: *"Install `coreutils` for `gtimeout`? Optional — `sc_timeout` is already bounded by a bash watchdog; `gtimeout` just makes the bound a real binary."* — options `Install` / `Skip`. `Skip` is a first-class answer: record `Actions -> skipped: brew install coreutils (declined)` and go to 3.2.

Rows 1, 3 and 4 **never ask and never block**. This step cannot fail the `install` chain: whatever happens, `sc_timeout` stays bounded.

**3.1e — apply coreutils, only after an explicit `Install`.** Soft by construction — with `--yes` this subcommand exits `0` on every path, including a failed `brew`.

**EXECUTE** using Bash tool:

```bash
SD="${CLAUDE_SKILL_DIR}"
[ -n "$SD" ] || SD="$(find "$HOME/.claude/plugins/cache/claude-brewcode/brewcode" -maxdepth 3 -type d -path '*/skills/*' \( -name semble-setup -o -name semble \) 2>/dev/null | sort -V | tail -1)"
bash "$SD/scripts/semble-install.sh" coreutils --yes --json; RC=$?
echo "RC=$RC"
[ "$RC" -eq 0 ] && echo "✅" || echo "❌ FAILED"
```

> **Not a stop — continue to 3.2 either way.** `.timeout.coreutils.status` is the outcome: `installed` -> `Actions -> changed`, `failed`/`skipped` -> `Actions -> skipped` with its `reason`. A ❌ here is reported and nothing else; the `install` chain carries on and the verdict is unaffected.

### 3.2 Register the MCP server

`semble-mcp.sh detect` returns exactly one state; the response is fixed. Read `references/mcp-and-cache.md` for the full matrix before acting on anything other than `absent`.

| State | Action |
|-------|--------|
| `absent` | `semble-mcp.sh add --scope user --yes` |
| `correct` | no MCP mutation — `add` still writes the project checkpoint itself (see below); no extra step |
| `stale_args` | show the before/after diff, confirm once, `semble-mcp.sh repair --yes` |
| `wrong_scope` | use the bundled migrate-to-`user`/keep decision; repair only if migration was approved |
| `duplicate` | show every scope repair removes and its user-scoped replacement; migrate only if approved. Keeping another scope skips repair |
| `upstream_unpinned` | **never auto-remove** the upstream `semble` server. Report the conflict; removal is an explicit user choice |
| `malformed` | **STOP.** Never write. Report the exact file and parser offset; the user fixes it by hand |

Precedence when several apply: `malformed` > `duplicate` > `wrong_scope` > `stale_args` > `upstream_unpinned` > `correct` > `absent`.

**EXECUTE** using Bash tool:

```bash
SD="${CLAUDE_SKILL_DIR}"
[ -n "$SD" ] || SD="$(find "$HOME/.claude/plugins/cache/claude-brewcode/brewcode" -maxdepth 3 -type d -path '*/skills/*' \( -name semble-setup -o -name semble \) 2>/dev/null | sort -V | tail -1)"
MCP_ACTION=skip   # add for absent/correct; repair only for the approved concrete transition
if [ "$MCP_ACTION" = add ]; then
  bash "$SD/scripts/semble-mcp.sh" add --scope user --yes --json; RC=$?
elif [ "$MCP_ACTION" = repair ]; then
  bash "$SD/scripts/semble-mcp.sh" repair --yes --json; RC=$?
else echo "skipped: MCP registration kept by user decision"; RC=0; fi
echo "RC=$RC"
[ "$RC" -eq 0 ] && echo "✅" || echo "❌ FAILED"
```

> **STOP if ❌** — report surviving state/config changes. `add` writes its checkpoint before config;
> it tries `add-json` first, then `add`. Both failing can leave `prereq_ready` plus failure notes.
> `repair` restores MCP config backups on failure; neither path promises no prior state write.
> Set MCP_ACTION from the matrix, not by guessing. `repair` always targets `user`, removing every
> existing semble_code scope; keeping local/project registration leaves it unchanged and reports the gap.

> **`correct`: `add` writes/refreshes the project checkpoint even when registration is unchanged.**
> Absent/`prereq_ready`/`error` -> `awaiting_reload`; `verifying`/`ready`/`disabled` retain phase while
> refreshing the three installer-owned fields (`cacheRoot`, `repoHash`, `resumePrompt`). Its `note`
> names the resulting phase; a second project needs no remembered follow-up. `MCP_ACTION=skip` stops
> after the report: do not wire the project or claim a reload checkpoint for a declined transition.

### 3.2b Wire the guidance, permissions and agents — `install` does this itself

Everything that does **not** need a live MCP server is wired now, not left hostage to the user coming back for `resume`. This is the same block as Step 4.2 and every step in it is idempotent, so `resume` re-runs it harmlessly and repairs any drift it finds.

The three nudge hooks fire at `awaiting_reload`, with "Verification has not finished (phase=awaiting_reload)
— the first call rebuilds the index and may take minutes". Only `enabled:false`, phase `disabled`/`error`/
`prereq_ready`, or `completed` without `mcp` silence them. Wiring before 3.3 avoids a registered-but-unhooked project.

What is **not** done here and cannot be: the smoke query (Step 4.1) — the server does not exist for this session — and therefore `phase ready`.

**EXECUTE** using Bash tool:

```bash
SD="${CLAUDE_SKILL_DIR}"
[ -n "$SD" ] || SD="$(find "$HOME/.claude/plugins/cache/claude-brewcode/brewcode" -maxdepth 3 -type d -path '*/skills/*' \( -name semble-setup -o -name semble \) 2>/dev/null | sort -V | tail -1)"
RC=0
if bash "$SD/scripts/semble-guidance.sh" install --part all --json; then
  ARC=0; bash "$SD/scripts/semble-agents.sh" apply --scope project --yes --json || ARC=$?
  echo "agents apply RC=$ARC   # 3 = reported conflict, not a failure"
  { [ "$ARC" -eq 0 ] || [ "$ARC" -eq 3 ]; } || RC=1
else RC=1; fi
[ "$RC" -eq 0 ] && echo "✅" || echo "❌ FAILED"
```

> **STOP if ❌** — report which step failed and what it left behind. The MCP registration from 3.2 stands either way; do not roll it back. `agents apply` exiting `3` is a reported outcome, not a failure.
> Record `guidance` and `agents` in **Actions**; they are marked complete on the state file in Step 4.3, after `resume` has confirmed them.

**`skipped: rule: user_modified` requires review, not automatic replacement.** Read the printed `diff -u`:

| The diff shows | Do |
|----------------|----|
| corpus / `--content` / "Not in this corpus" drift, possibly mixed with local additions | Show the exact file, template diff and local keys/prose that replacement would discard. Preserve it unless that concrete full replacement is explicitly approved; otherwise report the stale fact and proposed targeted correction |
| only local additions (extra frontmatter, project prose) | keep them; record `rule: user_modified, kept`; name any independently confirmed stale section |

```bash
SD="${CLAUDE_SKILL_DIR}"
[ -n "$SD" ] || SD="$(find "$HOME/.claude/plugins/cache/claude-brewcode/brewcode" -maxdepth 3 -type d -path '*/skills/*' \( -name semble-setup -o -name semble \) 2>/dev/null | sort -V | tail -1)"
APPROVED_REPLACEMENT=0   # set to 1 only for this exact approved full-file replacement
if [ "$APPROVED_REPLACEMENT" = 1 ]; then
  bash "$SD/scripts/semble-guidance.sh" install --part rule --force --json
else echo "skipped: rule: user_modified, kept; replacement not approved"; fi
```

> `--force` replaces the whole template byte for byte, including frontmatter/local prose; backup:
> `<rule>.bak.<epoch>`. A backup does not authorize data loss. Use only the concrete approval above;
> record `rule: overwritten (backup <path>)` when it runs. The same policy covers `.sembleignore`.
>
> The rule is copied verbatim and is never stamped at install time. `doc_type`, `version` and `generated_by` are baked into the plugin's own template by `.claude/scripts/bump-version.sh` at release; there is no `last_updated`, and nothing is substituted here. That is what lets `setup-status` `cmp` the installed rule against the plugin asset and read them as identical (`brewcode/skills/setup-status/references/artifact-metadata.md`, mechanism `a`).
>
> The managed/user_modified verdict still ignores the four metadata keys on both sides, so a rule installed by an older version of this skill — which did stamp `last_updated` — is not mistaken for a user edit. Such a rule is **re-synced to the plugin bytes without `--force` and without a backup** and reported as `rule: re-synced <path> (metadata only)` — record it in **Actions** as a re-sync, not an overwrite.

> **Size budget — HARD, enforced by test J9.** The rule carries `paths: ["**/*"]`, so it is auto-loaded into
> every request of every session in the installed repo forever: a word here is not a word, it is a per-prompt
> tax. Cap: **≤ 45 lines and ≤ 2000 bytes** including frontmatter (it stood at 111 lines / 4634 bytes until
> 5.5.1, ~3x over). Facts only, and each fact in its shortest correct form — a table row, not a paragraph;
> no worked examples beyond the single `search` JSON; no rationale, no measurement narrative, no
> justification of the doctrine. Prose that explains *why* the split is what it is belongs in
> `references/engine-landscape.md`, not in the rule. Adding a fact means **displacing** one, not appending:
> if the budget is exceeded the answer is to cut, never to raise the cap. The same budget governs any
> hand-compression of an already-installed rule.

### 3.3 Reload checkpoint — `install` STOPS here

The server does not exist for this session. Do not attempt a smoke query, do not claim success, do not continue. Print the **Next Step** exactly as `references/output-contract.md` requires:

```text
Reload Claude Code (new session), then run: /brewcode:semble-setup resume
Checkpoint: <abs projectRoot>/.claude/semble/state.json
```

Then emit the full report and end the invocation.

---

## Step 4 — `resume` (runs in the NEW session)

Re-run Step 1 first. If `.mcp.state` is not `correct`, go back to Step 3 instead of verifying.

### 4.0 Enter `verifying` — before anything is verified

`ready` is reachable **only** from `verifying`; there is no `awaiting_reload -> ready` edge and there must not be one, because `phase` is what `semble-session.mjs` and `semble-prefetch.mjs` read to decide whether to advertise the MCP tools. Writing `verifying` first is what makes the claim honest: a resume that dies half-way leaves the file saying *verification started and never finished*, which is the truth, instead of `awaiting_reload` (as if nothing happened) or `ready` (a verification that never ran).

`phase verifying` also self-heals a **missing** state file — it is created at `prereq_ready` and walked `prereq_ready -> awaiting_reload -> verifying` through the legal chain. That is the case where the MCP was already registered at user scope by an earlier project, so `add` reported `unchanged` and no checkpoint was ever written.

**EXECUTE** using Bash tool:

```bash
SD="${CLAUDE_SKILL_DIR}"
[ -n "$SD" ] || SD="$(find "$HOME/.claude/plugins/cache/claude-brewcode/brewcode" -maxdepth 3 -type d -path '*/skills/*' \( -name semble-setup -o -name semble \) 2>/dev/null | sort -V | tail -1)"
bash "$SD/scripts/semble-state.sh" phase verifying --json; RC=$?
echo "RC=$RC"
[ "$RC" -eq 0 ] && echo "✅" || echo "❌ FAILED"
```

> **STOP if ❌** — the only way this refuses is an illegal source phase (`prereq_ready`: the MCP was never registered, so go back to Step 3) or an unparseable state file. Report the message verbatim; nothing was written.

### 4.1 Smoke query — slow on a cold cache

The first run downloads the embedding model. Tell the user that **before** starting, then run this block with the Bash tool timeout raised to **600000 ms**.

`smoke` shells out to `uvx --from 'semble[mcp]==0.5.5' semble search` — the **CLI**, not the MCP server — with the registered content set and shared root, so it builds and queries the same `index-code-config-docs` variant without needing the server to be loaded in this session. That is why `resume` is fully scriptable: `claude -p "/brewcode:semble-setup resume"` in a fresh process completes end to end. The reload checkpoint exists so that *Claude* gets the two MCP tools, not because anything here is unverifiable before a restart.

**EXECUTE** using Bash tool:

```bash
SD="${CLAUDE_SKILL_DIR}"
[ -n "$SD" ] || SD="$(find "$HOME/.claude/plugins/cache/claude-brewcode/brewcode" -maxdepth 3 -type d -path '*/skills/*' \( -name semble-setup -o -name semble \) 2>/dev/null | sort -V | tail -1)"
OUT="$(bash "$SD/scripts/semble-project.sh" smoke --json)"; RC=$?
printf '%s\n' "$OUT"
STATUS="$(printf '%s' "$OUT" | grep -o '"status":"[a-z]*"' | head -1 | cut -d'"' -f4 || true)"
echo "RC=$RC status=${STATUS:-unknown}   # 3 + skipped = verified nothing, not a failure"
if [ "$RC" -eq 0 ]; then echo "✅"
elif [ "$RC" -eq 3 ] && [ "$STATUS" = "skipped" ]; then echo "⏭️ SKIPPED"
else echo "❌ FAILED"; fi
```

> **STOP if ❌** — verification failed; the state stays at `error` with a note. Report the raw output.
> **⏭️ SKIPPED is not ❌** — `RC=3` with `"status":"skipped"` (`SEMBLE_NO_NETWORK=1`, `SEMBLE_DRY_RUN=1`, no `uvx`) means the query never ran: nothing is broken and nothing is proven. Report `skipped (<reason>)`, set `SMOKE_OK=0` for Step 4.3 and continue.

### 4.2 Guidance, permissions and agents

`install --part all` writes the `semble-first` rule (never blind-overwriting a user-edited file — the `--force` rule of Step 3.2b applies here too), writes `<repo>/.sembleignore` under that same managed-file policy (`--part ignore`; it keeps generated trees such as `.claude/tmp/` and `.claude/reports/` out of the index — on this workspace that was 32% of all indexed files), and refreshes the `<!-- BEGIN brewcode:semble -->` block in `CLAUDE.md` **and then reconciles the search doctrine around it.** Scope: root CLAUDE.md only, outside the semble markers. Whole lines only, never a clause, because the confirmed instance mixes a true fact with a false one in one sentence. Whole file backed up to a timestamped `.bak` first. Idempotent — a second run finds nothing.

| Line matches | Action |
|---|---|
| **denies** semantic search outright | removed **even when the line also scopes the tool** — a denial is false regardless of what else it says |
| puts grep/Bash/rg **first** | removed, **unless** the same line scopes the tool (see the row below) |
| a search-titled heading the removal leaves empty | removed with it |
| merely mentions a search tool | reported and left untouched |
| scopes the tool to exact identifiers / regexes / paths / exhaustive enumeration (the skill's own correct guidance) | never touched, not even reported — this exemption shields the **first**-tier match only, never a denial |

Removal is whole lines only, echoed verbatim in the report so re-adding the true half is a paste.

This has **no measured effect on tool choice** — an A/B on the hypothesis that a contradicting CLAUDE.md line suppresses semble adoption came back negative: with the directive present, 5/14 searches used semble (36%); with it removed, 4/18 (22%); Fisher exact two-sided p = 0.45, n = 32, and the "clean" arm was nominally worse. The scan is kept anyway, on honest ground: a false statement in CLAUDE.md — this repo's own copy said *"Search via the **Bash** tool"* — is worth removing because it is false, not because removing it converts anyone.

Then it copies the five hook files into `.claude/hooks/` (`semble-session.mjs`, `semble-prefetch.mjs`, `semble-stats.mjs`, `semble-reminder.mjs`, `semble-subagent.mjs`), **deletes the one retired hook** (`semble-explore.mjs`, superseded for good by `semble-subagent.mjs`) and merges the **six** settings entries they wire — SessionStart, UserPromptSubmit, PostToolUse and PostToolUseFailure on the stats matcher, PreToolUse on `Bash|Grep`, and an **unmatched** SubagentStart (an absent matcher matches every agent type), each with `"timeout": 5` (**seconds**) — plus the two exact permission entries. The merge is a reconcile, not an append: a v1-shaped `settings.json` has its two `PreToolUse` rows collapsed into the single `Bash|Grep` row and its `SubagentStart`/`Explore` row **replaced** by the unmatched `semble-subagent.mjs` row — purged by the stale-triple filter, then re-pushed by the want loop, never left side by side — while the retired `.claude/semble/.reminder-ts` ignore line and marker file are dropped (the restored reminder fires on every eligible search — `N = state.reminderEvery`, DEF 1, so the counter in `.claude/semble/reminder.json` throttles nothing until a user raises N; it stays project-global and persisted across sessions, because at N>1 a per-session counter would burn the first N-1 eligible searches of every session in silence; already covered by the directory ignore). The `.gitignore` line install writes is the DIRECTORY `.claude/semble/`, not a single marker: `state.json` and `telemetry.jsonl` (verbatim commands + distilled prompt text, trimmed at 2 MB) live there too. Both narrower lines it supersedes — `.reminder-ts` and `.prefetch-ts` — are stripped by install and by remove. **`--part ignore` also MEASURES the repo** (`semble-project.sh candidates`: byte-identical duplicate trees, and directories or single files carrying a disproportionate share of the corpus, with exact chunk counts once an index exists) and writes what it found into a delimited block at the end of `.sembleignore`, **commented out**. Nothing is excluded until the user uncomments a line: a wrong exclusion removes code from the index silently, which is the worse error, so the scan proposes and the user decides. Re-running only ever adds paths it has never proposed, and the block is stripped before the managed-file compare, so an annotated file still reads `managed`. Measured: `.codex/` at 13.3% + `RELEASE-NOTES.md` at 5.5% here, `data/` at 26.5% on a second repo where nothing in the generic template matched anything. Every step is idempotent. Then the project agents are audited and patched — **project scope only**; global agents are never touched by `install`/`resume`.

**EXECUTE** using Bash tool:

```bash
SD="${CLAUDE_SKILL_DIR}"
[ -n "$SD" ] || SD="$(find "$HOME/.claude/plugins/cache/claude-brewcode/brewcode" -maxdepth 3 -type d -path '*/skills/*' \( -name semble-setup -o -name semble \) 2>/dev/null | sort -V | tail -1)"
RC=0
if bash "$SD/scripts/semble-guidance.sh" install --part all --json \
    && bash "$SD/scripts/semble-agents.sh" audit --scope project --json; then
  ARC=0; bash "$SD/scripts/semble-agents.sh" apply --scope project --yes --json || ARC=$?
  echo "agents apply RC=$ARC   # 3 = reported conflict, not a failure"
  { [ "$ARC" -eq 0 ] || [ "$ARC" -eq 3 ]; } || RC=1
else RC=1; fi
[ "$RC" -eq 0 ] && echo "✅" || echo "❌ FAILED"
```

> **STOP if ❌** — report which of the three steps failed and what it left behind. `agents apply` exiting `3` is a *reported outcome*, not a failure: those files are left byte-identical, named in the report, and the run continues (same handling as the recoverable `3`/`4` codes elsewhere in this skill). Record `SMOKE_OK` from Step 4.1 before running 4.3.

### 4.3 Close the state

Only steps that actually ran may be marked complete. `warm` and `smoke` are marked **only** when Step 4.1 exited `0` with a real result — a `"status":"skipped"` smoke (`SEMBLE_NO_NETWORK=1`, `SEMBLE_DRY_RUN=1`, or no `uvx`) verified nothing, so those two stay incomplete and the gap is reported in **Actions -> skipped**.

`complete` takes the whole list in one call. `phase ready` is legal here because Step 4.0 already moved the file to `verifying` — that hop is mandatory, never skipped, and `ready` has no other legal source. (`phase ready` self-heals a missing file the same way 4.0 does, so a state file deleted mid-resume still closes out.) The last line prints what the **state file** actually holds, read back after the writes — never the shell variable.

Both `semble-state.sh` writes also refresh the installer-owned fields — `resumePrompt`, `cacheRoot`, `repoHash` — from the current constants, so a state file written by an older version of this skill stops advertising a command that no longer exists. `enabled`, `phase` and `completed` are never reset as a side effect.

**EXECUTE** using Bash tool:

```bash
SD="${CLAUDE_SKILL_DIR}"
[ -n "$SD" ] || SD="$(find "$HOME/.claude/plugins/cache/claude-brewcode/brewcode" -maxdepth 3 -type d -path '*/skills/*' \( -name semble-setup -o -name semble \) 2>/dev/null | sort -V | tail -1)"
SMOKE_OK=1   # set to 0 when Step 4.1 reported "status":"skipped" — never guess
STEPS="prereq mcp permissions guidance agents"
[ "$SMOKE_OK" = "1" ] && STEPS="$STEPS warm smoke"
RC=0
bash "$SD/scripts/semble-state.sh" complete $STEPS || RC=1
if [ "$RC" -eq 0 ] && [ "$SMOKE_OK" = 1 ]; then
  bash "$SD/scripts/semble-state.sh" phase ready || RC=1
fi
RECORDED="$(bash "$SD/scripts/semble-state.sh" get completed 2>/dev/null || true)"
echo "recorded: ${RECORDED:-NOTHING — no state file was written}"
[ "$RC" -eq 0 ] && echo "✅" || echo "❌ FAILED"
```

> **STOP if ❌** — report the illegal phase/unknown step/parser error and actual surviving writes;
> quote `recorded:`. A successful earlier state write is not rolled back by a later failure.
> Report `recorded:` verbatim — it is the state file read back. Never restate `$STEPS` as if it were the outcome.
> With `SMOKE_OK=0`, retain `verifying`; Current Status is `partial - warm and smoke not recorded in
> state.completed`. Status names each missing member: `warm` alone -> `partial - smoke not recorded in
> state.completed`. Next Step: `/brewcode:semble-setup resume` once the skip reason is gone.

---

## Step 5 — The other modes

Use the initial PLAN and Step 1 snapshot, then the selected block; do not reprint the PLAN.

### `enable` / `disable`

`disable` deletes nothing: `state.enabled=false`, phase -> `disabled`; the MCP registration, the rule, the CLAUDE.md block, the hooks and the whole cache stay. The hooks read `enabled` and go silent.

**EXECUTE** using Bash tool:

```bash
SD="${CLAUDE_SKILL_DIR}"
[ -n "$SD" ] || SD="$(find "$HOME/.claude/plugins/cache/claude-brewcode/brewcode" -maxdepth 3 -type d -path '*/skills/*' \( -name semble-setup -o -name semble \) 2>/dev/null | sort -V | tail -1)"
ACTION=disable   # or: enable
OUT="$(bash "$SD/scripts/semble-project.sh" "$ACTION" --yes --json)"; RC=$?
printf '%s\n' "$OUT"
STATUS="$(printf '%s' "$OUT" | grep -o '"status":"[a-z]*"' | head -1 | cut -d'"' -f4 || true)"
echo "RC=$RC status=${STATUS:-unknown}   # 3 + skipped = warm never ran, phase left at verifying"
if [ "$RC" -eq 0 ]; then echo "✅"
elif [ "$RC" -eq 3 ] && [ "$STATUS" = "skipped" ]; then echo "⏭️ SKIPPED"
else echo "❌ FAILED"; fi
```

> **STOP if ❌** — report the raw output and surviving state/guidance/config changes; no rollback is implied.
> **⏭️ SKIPPED is not ❌** — `enable` with a skipped warm (offline, dry run, no `uvx`) exits `3` with `"status":"skipped"`: guidance, agents and permissions are done, but no successful search proves readiness, so the phase stops at `verifying` — deliberately neither `ready` nor `error`. Report the `skipped:` lines verbatim and tell the user to re-run `/brewcode:semble-setup resume` once the reason is gone. `status` reports the same gap as `partial - warm and smoke not recorded in state.completed`.

### `reindex`

There is no per-repo rebuild CLI. Run it **without** `--yes` first: it exits `4`, changes nothing, and prints the exact `<shared root>/<64-hex>/index-code-config-docs` variant it would replace, plus a verified pre-0.5.5 combined `index` only when present. Show those paths and the size, ask the single confirmation permitted by rule 5d, then re-run with `--yes`. The implementation builds in a private staging root, proves a real query, and swaps only the selected variant; sibling content variants survive. A warm-only request (`"warm"`, `"прогрей"`) uses `semble-project.sh warm` and deletes nothing.

**EXECUTE** using Bash tool:

```bash
SD="${CLAUDE_SKILL_DIR}"
[ -n "$SD" ] || SD="$(find "$HOME/.claude/plugins/cache/claude-brewcode/brewcode" -maxdepth 3 -type d -path '*/skills/*' \( -name semble-setup -o -name semble \) 2>/dev/null | sort -V | tail -1)"
bash "$SD/scripts/semble-project.sh" reindex --json; RC=$?
echo "RC=$RC   # 4 = confirmation required, nothing deleted"
[ "$RC" -eq 4 ] || [ "$RC" -eq 0 ] && echo "✅" || echo "❌ FAILED"
```

> **STOP if ❌** — the cache variant could not be resolved; never fall back to a broader delete.

After the user confirms, re-run the same command with `--yes` (Bash tool timeout **600000 ms** — the rebuild warms the index).

### `optimize` — read-only by default

Fan out the four audits, then report findings and offer concrete actions. It mutates nothing on its own; each recommendation names the mode that would apply it.

**EXECUTE** using Bash tool:

```bash
SD="${CLAUDE_SKILL_DIR}"
[ -n "$SD" ] || SD="$(find "$HOME/.claude/plugins/cache/claude-brewcode/brewcode" -maxdepth 3 -type d -path '*/skills/*' \( -name semble-setup -o -name semble \) 2>/dev/null | sort -V | tail -1)"
RC=0
bash "$SD/scripts/semble-project.sh"  audit --json                || RC=1
bash "$SD/scripts/semble-cache.sh"    info  --json                || RC=1
bash "$SD/scripts/semble-guidance.sh" status --json               || RC=1
bash "$SD/scripts/semble-agents.sh"   audit --scope project --json || RC=1
[ "$RC" -eq 0 ] && echo "✅" || echo "❌ FAILED"
```

> **STOP if ❌** — report which audit failed; nothing was changed either way.

### `upgrade`

After successful preflight, run both halves: a correct MCP at approved `0.5.5` is unchanged;
otherwise show the exact pin/scope/args transition and repair only when approved. Do not jump to the
reload checkpoint before the project half. Never `@latest` or unpinned `--from semble[mcp]`.
Historical 0.5.4 -> 0.5.5: combined bare `index` is legacy; resume builds the selected variant;
reindex removes the old one only after metadata matching and verified replacement.

**EXECUTE** using Bash tool:

```bash
SD="${CLAUDE_SKILL_DIR}"
[ -n "$SD" ] || SD="$(find "$HOME/.claude/plugins/cache/claude-brewcode/brewcode" -maxdepth 3 -type d -path '*/skills/*' \( -name semble-setup -o -name semble \) 2>/dev/null | sort -V | tail -1)"
IRC=0; bash "$SD/scripts/semble-install.sh" check --json || IRC=$?
MRC=0; bash "$SD/scripts/semble-mcp.sh" detect --json || MRC=$?
echo "prerequisites RC=$IRC | MCP detect RC=$MRC"
if [ "$IRC" -eq 0 ] && [ "$MRC" -eq 0 ]; then echo "✅"
elif [ "$IRC" -eq 3 ] && [ "$MRC" -eq 0 ]; then echo "⏭️ PRECONDITION — uv/uvx absent"
else echo "❌ FAILED"; exit 1; fi
```

> **STOP if ❌** — neither probe may hide the other's error; malformed MCP state also stops.
> `IRC=3` -> report prerequisites and use Step 3.1's explicit machine gate before retrying upgrade.
> Apply `semble-mcp.sh repair --yes --json` only for the approved concrete transition.

The project half is **unconditional and runs even when the pin is unchanged** — it is the only thing that moves this install's version stamp. Re-run the Step 3.2b block verbatim: `semble-guidance.sh install --part all` re-copies the rule, `.sembleignore` and the five live hooks from the plugin's assets (a byte-copy: identical files report `unchanged`, a file whose only delta is the release stamp takes the metadata-only re-sync branch, a hand-edited one is skipped and diffed to stderr), **deletes the retired `semble-explore.mjs`** if the install predates the migration, and re-merges the settings entries and permissions.

**EXECUTE** the Step 3.2b block, including its user-modified-file policy. It does not depend on pin drift.
After both halves, use Step 3.3 only if registration changed or state remains `awaiting_reload`;
otherwise report the post-write status and any remaining verification gap.

> **STOP if ❌** — report which half failed and what it left behind; the MCP registration stands either way.
>
> **Without this block `upgrade` could never clear a `stale` verdict.** `setup-status` reads this install's version out of the frontmatter of `.claude/rules/semble-first.md`, and `semble-guidance.sh install` is the only writer of that file — an `upgrade` that skipped it reported success and left the stamp exactly where it was, so the next `status` printed `stale` again forever.
>
> `skipped: rule: user_modified` can leave the stamp stale by design: keep the edit and apply Step 3.2b's
> concrete replacement gate; generic upgrade approval never authorizes `--force` or loss of local keys/prose.
>
> **Stray cache root, installs that ran a pre-5.0.1 prefetch hook.** That hook spawned `semble search` without `SEMBLE_CACHE_LOCATION`, so the child used semble's own default root — `~/Library/Caches/semble` on macOS, `$XDG_CACHE_HOME/semble` elsewhere — and built a SECOND copy of every index the MCP server had already built under `semble-code` (measured: 62 MB here). The hook now passes the registered root, so nothing writes there any more, but the old copy is not deleted by any mode: it sits outside the project and this skill does not remove machine-level directories on its own. Report it and hand the user the command — `du -sh ~/Library/Caches/semble` to size it, `rm -rf ~/Library/Caches/semble` to drop it. **The registered shared root is `semble-code`; that single missing suffix is the whole bug.** A pre-0.5.5 `semble-docs/RESERVED-FOR-DOCS.txt` marker is only legacy residue; `purge` removes that root only when the marker is its sole entry.

### `uninstall` — four flavours, always an explicit choice

| Flavour | Rule / hooks / CLAUDE.md | MCP | Agent frontmatter | Cache | uv tool |
|---------|--------------------------|-----|-------------------|-------|---------|
| `integration` | removed | **kept** | semble entries removed | kept | kept |
| `mcp` | kept | removed | kept | kept | kept |
| `cli` | kept | kept | kept | kept | `uv tool uninstall semble` |
| `purge` | removed | removed | removed | **shared root removed** | confirmation |

`state.json` and `.claude/semble/` are deleted by `integration` and `purge`, kept by `mcp` and `cli`. Use the one `AskUserQuestion` here, listing per option exactly what is deleted and what survives.

**EXECUTE** using Bash tool:

```bash
SD="${CLAUDE_SKILL_DIR}"
[ -n "$SD" ] || SD="$(find "$HOME/.claude/plugins/cache/claude-brewcode/brewcode" -maxdepth 3 -type d -path '*/skills/*' \( -name semble-setup -o -name semble \) 2>/dev/null | sort -V | tail -1)"
FLAVOUR=integration   # or: mcp | cli  — chosen by the user, never guessed
bash "$SD/scripts/semble-remove.sh" "$FLAVOUR" --yes --json && echo "✅" || echo "❌ FAILED"
```

> **STOP if ❌** — report exactly which surface was left behind so the user can finish by hand.

### `purge` — destructive

Requires `--yes` **and** the literal `--confirm-text "purge semble code cache"`. Run it without those first: it exits `4`, deletes nothing, and prints every directory it would remove. Show that list, get the typed confirmation, then run the block. The upstream `semble` server (if any) is still never removed automatically.

**EXECUTE** using Bash tool:

```bash
SD="${CLAUDE_SKILL_DIR}"
[ -n "$SD" ] || SD="$(find "$HOME/.claude/plugins/cache/claude-brewcode/brewcode" -maxdepth 3 -type d -path '*/skills/*' \( -name semble-setup -o -name semble \) 2>/dev/null | sort -V | tail -1)"
bash "$SD/scripts/semble-remove.sh" purge --yes --confirm-text "purge semble code cache" --json && echo "✅" || echo "❌ FAILED"
```

> **STOP if ❌** — report what survived. Never retry a purge with a broader target.

---

## Step 6 — Report

Re-run Step 1 (`semble-status.sh --section all --json`) after the last write and emit the **full** report from `references/output-contract.md` — all six sections, in order, empty ones printing `none`:

`Detection` · `Before` (the Step 1 snapshot, not refreshed) · `Actions` · `Verification` · `Current Status` (the **post**-mutation `.verdict`) · `Next Step` (`.nextStep`).

`commands:` lists every command actually run, verbatim, including the failures. The `uncovered:` line is printed on every invocation.

When `phase == awaiting_reload`, **Next Step** is exactly the two-line reload text in Step 3.3 — no paraphrase.

</instructions>

Files in this skill

  • LICENSE1.1 KB
  • README.md21.4 KB
  • SKILL.md54.4 KB
  • assets/INSTALL.md58.3 KB
  • assets/semble-first.md.template1.8 KB
  • assets/semble-prefetch.mjs29.9 KB
  • assets/semble-reminder.mjs20.4 KB
  • assets/semble-session.mjs7.4 KB
  • assets/semble-stats.mjs11.2 KB
  • assets/semble-subagent.mjs8.3 KB
  • assets/sembleignore.template6.5 KB
  • references/engine-landscape.md58.1 KB
  • references/hooks-roadmap.md43.7 KB
  • references/intent-routing.md10.9 KB
  • references/language-coverage.md12.8 KB
  • references/mcp-and-cache.md12.1 KB
  • references/output-contract.md9 KB
  • references/project-agent-migration.md10.1 KB
  • scripts/lib/semble-common.sh27.5 KB
  • scripts/semble-agents.sh24.5 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.