---
name: discover-agent
description: Looks up subagents by keyword/domain across all installed plugins (voltagent, pr-review-toolkit, feature-dev, code-review, superpowers, etc.) plus user-level agents. Twin of discover-skill but searches kind='agent' in the FTS5 index. Cross-references with CLAUDE-agents.md cheatsheet for trusted-mappings.
when_to_use: |
  Trigger phrases: "qual subagente para X", "discover agent <keyword>", "find subagent that handles ...".
  Called by route-task when the cheatsheet doesn't match the user's task.
disable-model-invocation: false
allowed-tools: Read Bash(test *) Bash(sqlite3 *) Bash(jq *) Bash(grep *)
---

# Discover agent

Query: `$ARGUMENTS` — a keyword, domain, or task description.

## Workflow

### 1. Preflight
```bash
set -euo pipefail
INDEX_DB=~/.claude/cache/maxv-orchestration/index.db
test -f "$INDEX_DB" || {
  echo '{"matches":[],"error":"index missing","suggestion":"Run /maxvision-orchestration:index-catalog first"}'
  exit 1
}
```

### 2. Read CLAUDE-agents.md cheatsheet (for trusted-mappings)
```bash
set -euo pipefail
# Project-local first (overrides user-level for project-specific subagent rules),
# then user-level. Empty string when neither exists. (FU-8 in docs/FOLLOWUPS.md.)
CHEATSHEET=""
for p in .claude/CLAUDE-agents.md "$HOME/.claude/CLAUDE-agents.md"; do
  if [[ -f "$p" ]]; then
    CHEATSHEET="$p"
    break
  fi
done
```

If present, the cheatsheet is the authoritative trusted-mappings source. Agents listed there get a `★ in cheatsheet` badge in the output.

### 3. Build BM25 SQL with column weights and `kind='agent'` filter

Same pattern as `query-index` — heredoc + SQL-literal escape (NEVER raw `$ARGUMENTS` in SQL). Column weights identical: `name(1.5) > description(1.0) > when_to_use(1.0) > body(0.8) > allowed_tools(0.5)`. Filter `WHERE item.kind='agent'` and cap LIMIT at 10 to keep the candidate set manageable.

```bash
set -euo pipefail
QUERY="$ARGUMENTS"
Q_ESC=${QUERY//\'/\'\'}

RESULT=$(sqlite3 -json "$INDEX_DB" <<SQL
SELECT
  item.kind, item.name, item.source_id, item.path,
  substr(item.description, 1, 200) AS description,
  item.model, item.last_commit_iso, item.schema_valid,
  bm25(item_fts, 1.5, 1.0, 1.0, 0.8, 0.5) AS score
FROM item_fts JOIN item ON item.id = item_fts.rowid
WHERE item_fts MATCH '$Q_ESC'
  AND item.kind = 'agent'
  AND item.name != 'general-purpose'
ORDER BY score
LIMIT 10;
SQL
)
```

### 4. Cross-reference with cheatsheet (badge)

For each row in `$RESULT`, check whether `name` appears in `$CHEATSHEET` and inject `in_cheatsheet: true|false`. Pure bash + jq + grep loop (no python — keeps allowed-tools read-only):

```bash
set -euo pipefail
if [[ -n "$CHEATSHEET" ]]; then
  ENRICHED='[]'
  while IFS= read -r row; do
    name=$(echo "$row" | jq -r '.name')
    if grep -qiF "$name" "$CHEATSHEET" 2>/dev/null; then
      enriched_row=$(echo "$row" | jq '. + {in_cheatsheet: true}')
    else
      enriched_row=$(echo "$row" | jq '. + {in_cheatsheet: false}')
    fi
    ENRICHED=$(echo "$ENRICHED" | jq --argjson new "$enriched_row" '. + [$new]')
  done < <(echo "$RESULT" | jq -c '.[]')
  RESULT="$ENRICHED"
else
  RESULT=$(echo "$RESULT" | jq -c '[.[] | . + {in_cheatsheet: false}]')
fi
```

### 5. Cap results at 4 (parallelism budget)

Take the top 4 by `score` ASC (more-negative = better). Drop the rest with a marker `"pruned": "results 5-N pruned for parallelism budget; max 3-4 subagents in parallel per CLAUDE-agents.md"`.

### 6. Output format

**Note on `Model` column:** agents that don't declare `model:` in their frontmatter render an empty cell. Per `scripts/source_collectors.py`, the column is populated via `frontmatter.get("model")` and stays NULL for agents that defer to platform default. Display empty (no `null` literal) for cleanliness.

Markdown table with one row per top-4 match:

```
## Subagent matches for: <query>

| Agent name           | Source plugin                  | Model   | In cheatsheet | BM25  |
|----------------------|--------------------------------|---------|---------------|-------|
| react-specialist     | voltagent-lang                 | sonnet  | ★ yes         | -3.2  |
| frontend-developer   | voltagent-core-dev             | sonnet  | ★ yes         | -2.8  |
| ...                  |                                |         |               |       |

Then a one-line recommendation:
> Recommend: invoke `<top-1>` for <query>. If multi-skill needed, hand off to `analyze-candidates` with kind=agent.
```

### 7. Empty result handling

```bash
set -euo pipefail
if [[ -z "${RESULT:-}" || "$RESULT" == "[]" ]]; then
  printf '%s\n' '{"matches":[],"suggestion":"try broader keyword or run /maxvision-orchestration:discover-skill <kw> if you wanted a skill instead of an agent"}'
  exit 0
fi
```
Empty result is not an error — exit 0 with a JSON object pointing the user to `discover-skill` if they meant skills instead of agents.

## Guardrails

- **Read-only.** Never write, never auto-dispatch. This skill only **recommends**.
- **Never recommend `general-purpose` Agent.** Per user policy in `.claude/settings.local.json`, `general-purpose` is denied — excluded at the SQL level via `AND item.name != 'general-purpose'`.
- **Cap at 4.** Per CLAUDE-agents.md, 3-4 parallel subagents is the budget; emit only top-4.
- **Index missing.** Hard-fail at step 1 with JSON error + `exit 1`.
- **SQL-literal escape.** Shell single-quote doubling (`'` → `''`) applied to `QUERY` before interpolation into the heredoc SQL — per T17 pattern. Never substitute raw `$ARGUMENTS` into SQL.
- **Cheatsheet authoritative.** When CLAUDE-agents.md and FTS5 disagree on which agent to recommend, prefer the cheatsheet entry; FTS5 is fallback.
