initial: opc-agents standalone tool
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.DS_Store
|
||||
69
README.md
Normal file
69
README.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# opc-agents
|
||||
|
||||
Small shell utility for toggling opencode **agents** between a curated "active" set and a full "parked" catalog. Keeps `~/.config/opencode/agents/` small and intentional while preserving the full agent library offline.
|
||||
|
||||
Companion to [`opc-skills`](https://gitea.taygun.net.tr/salvacybersec/opc-skills) — same UX, file-based unit (each agent is a single `<name>.md` file rather than a folder).
|
||||
|
||||
## Why
|
||||
|
||||
opencode injects every agent's frontmatter (`description`, `mode`, …) into the calling agent's tool registry. With 100+ agents the registry alone burns ~280K tokens of context before the user even types a prompt. Park the ones you don't need this week.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
ln -s ~/Documents/opc-agents/bin/opc-agents ~/.local/bin/opc-agents
|
||||
```
|
||||
|
||||
Ensure `~/.local/bin` is on your `PATH`.
|
||||
|
||||
## Layout it expects
|
||||
|
||||
```
|
||||
~/Documents/opencode-agents-parked/ # full catalog — untracked data dir
|
||||
<name>.md
|
||||
INDEX.json
|
||||
INDEX.md
|
||||
~/.config/opencode/agents/ # only currently-enabled agents
|
||||
<name>.md
|
||||
```
|
||||
|
||||
Override via env:
|
||||
- `OPC_AGENTS_PARKED` — parked catalog root (default `~/Documents/opencode-agents-parked`)
|
||||
- `OPC_AGENTS_ACTIVE` — active agents root (default `~/.config/opencode/agents`)
|
||||
|
||||
## Commands
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| `status` | counts of active vs parked + mode breakdown |
|
||||
| `list {active\|parked\|all}` | list agent names |
|
||||
| `categories` / `cats` | prefix-based category counts (PARKED) |
|
||||
| `enable <name>` | enable single agent (copy parked → active) |
|
||||
| `disable <name>` | disable single agent (remove from active; keep parked) |
|
||||
| `disable-all [-y\|--yes] [--keep-primary]` | disable every active agent (asks for confirmation) |
|
||||
| `enable-category <prefix>` | fzf multi-pick within a prefix, then enable |
|
||||
| `disable-category <prefix>` | fzf multi-pick of ACTIVE agents with prefix, then disable |
|
||||
| `pick` | fzf: choose category → multi-select → enable |
|
||||
| `disable-pick` | fzf: choose ACTIVE category → multi-select → disable |
|
||||
| `search [query]` | fzf fuzzy search across name+description (enable) |
|
||||
| `disable-search [query]` | fzf fuzzy search ACTIVE agents only (disable) |
|
||||
| `reindex` | rebuild `INDEX.json` / `INDEX.md` |
|
||||
|
||||
`fzf` is required for the interactive pickers; `jq` for the search variants; `python3` for `reindex`.
|
||||
|
||||
## Notes
|
||||
|
||||
- Each agent is a single `<name>.md` file with YAML frontmatter (typically `description`, `mode`, `temperature`, …).
|
||||
- `disable*` commands never delete data — they remove the active copy and keep (or restore) the parked copy. Re-enable with `opc-agents enable <name>`.
|
||||
- `--keep-primary` on `disable-all` skips agents with `mode: primary` (those are loaded as top-level personas in opencode).
|
||||
- Common agent prefixes seen in personas: `frodo-`, `marshal-`, `sentinel-`, `bastion-`, `neo-`, `oracle-`, `warden-`, `polyglot-`, etc.
|
||||
|
||||
## Differences from `opc-skills`
|
||||
|
||||
| | `opc-skills` | `opc-agents` |
|
||||
|---|---|---|
|
||||
| Unit | directory containing `SKILL.md` | single `<name>.md` file |
|
||||
| Frontmatter fields used | `name`, `description` | `description`, `mode` |
|
||||
| Shared refs sync | yes (`_platform-mapping.md`) | n/a |
|
||||
| `disable-all` flags | `-y` | `-y`, `--keep-primary` |
|
||||
| Status breakdown | active/parked | + primary/subagent counts |
|
||||
432
bin/opc-agents
Executable file
432
bin/opc-agents
Executable file
@@ -0,0 +1,432 @@
|
||||
#!/usr/bin/env bash
|
||||
# opc-agents — opencode agent enable/disable manager
|
||||
# Parked: ~/Documents/opencode-agents-parked
|
||||
# Active: ~/.config/opencode/agents
|
||||
#
|
||||
# Mirrors opc-skills, but each unit is a single <name>.md file (not a folder).
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
PARKED="${OPC_AGENTS_PARKED:-$HOME/Documents/opencode-agents-parked}"
|
||||
ACTIVE="${OPC_AGENTS_ACTIVE:-$HOME/.config/opencode/agents}"
|
||||
INDEX_JSON="$PARKED/INDEX.json"
|
||||
INDEX_MD="$PARKED/INDEX.md"
|
||||
|
||||
require_dirs() {
|
||||
[ -d "$PARKED" ] || { echo "parked dir missing: $PARKED" >&2; exit 1; }
|
||||
mkdir -p "$ACTIVE"
|
||||
}
|
||||
|
||||
# list agent base-names (without .md) in a given dir
|
||||
agent_names_in() {
|
||||
local base="$1"
|
||||
[ -d "$base" ] || return 0
|
||||
find "$base" -mindepth 1 -maxdepth 1 -type f -name '*.md' \
|
||||
! -name 'INDEX.md' ! -name 'README.md' -printf '%f\n' \
|
||||
| sed 's/\.md$//' | sort
|
||||
}
|
||||
|
||||
cmd_status() {
|
||||
require_dirs
|
||||
local active parked
|
||||
active=$(agent_names_in "$ACTIVE" | wc -l)
|
||||
parked=$(agent_names_in "$PARKED" | wc -l)
|
||||
printf "active : %5d (%s)\n" "$active" "$ACTIVE"
|
||||
printf "parked : %5d (%s)\n" "$parked" "$PARKED"
|
||||
|
||||
# mode breakdown for active (primary vs subagent) — primary stays in main context
|
||||
if [ "$active" -gt 0 ]; then
|
||||
local primary subagent other
|
||||
primary=$(grep -l '^mode: primary' "$ACTIVE"/*.md 2>/dev/null | wc -l)
|
||||
subagent=$(grep -l '^mode: subagent' "$ACTIVE"/*.md 2>/dev/null | wc -l)
|
||||
other=$((active - primary - subagent))
|
||||
echo
|
||||
printf " primary : %5d\n" "$primary"
|
||||
printf " subagent : %5d\n" "$subagent"
|
||||
[ "$other" -gt 0 ] && printf " (other) : %5d\n" "$other"
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_list() {
|
||||
require_dirs
|
||||
local which="${1:-parked}"
|
||||
case "$which" in
|
||||
active) agent_names_in "$ACTIVE" ;;
|
||||
parked) agent_names_in "$PARKED" ;;
|
||||
all) { agent_names_in "$ACTIVE"; agent_names_in "$PARKED"; } | sort -u ;;
|
||||
*) echo "usage: opc-agents list {active|parked|all}" >&2; exit 2 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
cmd_categories() {
|
||||
require_dirs
|
||||
agent_names_in "$PARKED" \
|
||||
| awk -F'-' '{print $1}' \
|
||||
| sort | uniq -c | sort -rn
|
||||
}
|
||||
|
||||
ensure_fzf() {
|
||||
command -v fzf >/dev/null || { echo "fzf is required for this command" >&2; exit 1; }
|
||||
}
|
||||
|
||||
# enable one agent by base-name (no .md)
|
||||
cmd_enable() {
|
||||
require_dirs
|
||||
local name="${1:-}"
|
||||
[ -n "$name" ] || { echo "usage: opc-agents enable <name>" >&2; exit 2; }
|
||||
name="${name%.md}"
|
||||
local src="$PARKED/$name.md" dst="$ACTIVE/$name.md"
|
||||
[ -f "$src" ] || { echo "not parked: $name" >&2; exit 1; }
|
||||
[ -f "$dst" ] && { echo "already active: $name"; exit 0; }
|
||||
cp "$src" "$dst"
|
||||
echo "enabled: $name"
|
||||
}
|
||||
|
||||
# disable one active agent (keep parked copy)
|
||||
cmd_disable() {
|
||||
require_dirs
|
||||
local name="${1:-}"
|
||||
[ -n "$name" ] || { echo "usage: opc-agents disable <name>" >&2; exit 2; }
|
||||
name="${name%.md}"
|
||||
local src="$ACTIVE/$name.md" dst="$PARKED/$name.md"
|
||||
[ -f "$src" ] || { echo "not active: $name" >&2; exit 1; }
|
||||
if [ -f "$dst" ]; then
|
||||
rm -f "$src"
|
||||
echo "removed active copy (parked version kept): $name"
|
||||
else
|
||||
mv "$src" "$dst"
|
||||
echo "disabled (moved to parked): $name"
|
||||
fi
|
||||
}
|
||||
|
||||
# disable all active agents
|
||||
cmd_disable_all() {
|
||||
require_dirs
|
||||
local force=0 keep_primary=0 a
|
||||
for a in "$@"; do
|
||||
case "$a" in
|
||||
-y|--yes) force=1 ;;
|
||||
--keep-primary) keep_primary=1 ;;
|
||||
*) echo "unknown flag: $a" >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
local -a actives
|
||||
if [ "$keep_primary" -eq 1 ]; then
|
||||
mapfile -t actives < <(
|
||||
agent_names_in "$ACTIVE" | while read -r n; do
|
||||
grep -q '^mode: primary' "$ACTIVE/$n.md" 2>/dev/null || echo "$n"
|
||||
done
|
||||
)
|
||||
else
|
||||
mapfile -t actives < <(agent_names_in "$ACTIVE")
|
||||
fi
|
||||
[ "${#actives[@]}" -gt 0 ] || { echo "no active agents to disable"; exit 0; }
|
||||
echo "Will disable ${#actives[@]} active agent(s):"
|
||||
printf ' %s\n' "${actives[@]}"
|
||||
if [ "$force" -ne 1 ]; then
|
||||
printf "Proceed? [y/N] "
|
||||
read -r ans </dev/tty || ans=""
|
||||
case "$ans" in y|Y|yes|YES) ;; *) echo "cancelled"; exit 0 ;; esac
|
||||
fi
|
||||
local n
|
||||
for n in "${actives[@]}"; do
|
||||
cmd_disable "$n" || true
|
||||
done
|
||||
}
|
||||
|
||||
# enable category (by prefix)
|
||||
cmd_enable_category() {
|
||||
require_dirs
|
||||
ensure_fzf
|
||||
local prefix="${1:-}"
|
||||
[ -n "$prefix" ] || { echo "usage: opc-agents enable-category <prefix>" >&2; exit 2; }
|
||||
local -a matches
|
||||
mapfile -t matches < <(agent_names_in "$PARKED" | awk -v p="$prefix" '$0 ~ "^" p "(-|$)"')
|
||||
[ "${#matches[@]}" -gt 0 ] || { echo "no parked agents match prefix: $prefix" >&2; exit 1; }
|
||||
echo "Matches (${#matches[@]}):"
|
||||
local selection
|
||||
selection=$(printf '%s\n' "${matches[@]}" | fzf --multi --height=70% \
|
||||
--prompt="enable-category $prefix > " \
|
||||
--header="TAB: toggle | ENTER: confirm | ESC: cancel" \
|
||||
--preview="sed -n '1,40p' \"$PARKED/{}.md\" 2>/dev/null" \
|
||||
--preview-window=right:60%:wrap)
|
||||
[ -n "$selection" ] || { echo "cancelled"; exit 0; }
|
||||
local n
|
||||
while IFS= read -r n; do
|
||||
[ -z "$n" ] && continue
|
||||
cmd_enable "$n" || true
|
||||
done <<< "$selection"
|
||||
}
|
||||
|
||||
# disable category (by prefix, only ACTIVE)
|
||||
cmd_disable_category() {
|
||||
require_dirs
|
||||
ensure_fzf
|
||||
local prefix="${1:-}"
|
||||
[ -n "$prefix" ] || { echo "usage: opc-agents disable-category <prefix>" >&2; exit 2; }
|
||||
local -a matches
|
||||
mapfile -t matches < <(agent_names_in "$ACTIVE" | awk -v p="$prefix" '$0 ~ "^" p "(-|$)"')
|
||||
[ "${#matches[@]}" -gt 0 ] || { echo "no active agents match prefix: $prefix" >&2; exit 1; }
|
||||
echo "Active matches (${#matches[@]}):"
|
||||
local selection
|
||||
selection=$(printf '%s\n' "${matches[@]}" | fzf --multi --height=70% \
|
||||
--prompt="disable-category $prefix > " \
|
||||
--header="TAB: toggle | ENTER: confirm | ESC: cancel" \
|
||||
--preview="sed -n '1,40p' \"$ACTIVE/{}.md\" 2>/dev/null" \
|
||||
--preview-window=right:60%:wrap)
|
||||
[ -n "$selection" ] || { echo "cancelled"; exit 0; }
|
||||
local n
|
||||
while IFS= read -r n; do
|
||||
[ -z "$n" ] && continue
|
||||
cmd_disable "$n" || true
|
||||
done <<< "$selection"
|
||||
}
|
||||
|
||||
# interactive pick: parked category → multi-select → enable
|
||||
cmd_pick() {
|
||||
require_dirs
|
||||
ensure_fzf
|
||||
local category
|
||||
category=$(cmd_categories | fzf --prompt="category > " --height=50% \
|
||||
--header="Pick a category (first column = count)" \
|
||||
| awk '{print $2}')
|
||||
[ -n "$category" ] || { echo "cancelled"; exit 0; }
|
||||
local selection
|
||||
selection=$(agent_names_in "$PARKED" \
|
||||
| awk -v p="$category" '$0 ~ "^" p "(-|$)"' \
|
||||
| fzf --multi --height=80% \
|
||||
--prompt="$category > " \
|
||||
--header="TAB: toggle | ENTER: enable selected" \
|
||||
--preview="sed -n '1,40p' \"$PARKED/{}.md\" 2>/dev/null" \
|
||||
--preview-window=right:60%:wrap)
|
||||
[ -n "$selection" ] || { echo "cancelled"; exit 0; }
|
||||
local n
|
||||
while IFS= read -r n; do
|
||||
[ -z "$n" ] && continue
|
||||
cmd_enable "$n" || true
|
||||
done <<< "$selection"
|
||||
}
|
||||
|
||||
# interactive pick: ACTIVE category → multi-select → disable
|
||||
cmd_disable_pick() {
|
||||
require_dirs
|
||||
ensure_fzf
|
||||
local active_count
|
||||
active_count=$(agent_names_in "$ACTIVE" | wc -l)
|
||||
[ "$active_count" -gt 0 ] || { echo "no active agents"; exit 0; }
|
||||
local cats category
|
||||
cats=$(agent_names_in "$ACTIVE" | awk -F'-' '{print $1}' | sort | uniq -c | sort -rn)
|
||||
category=$(printf '%s\n' "$cats" | fzf --prompt="active-category > " --height=50% \
|
||||
--header="Pick a category of ACTIVE agents to disable" \
|
||||
| awk '{print $2}')
|
||||
[ -n "$category" ] || { echo "cancelled"; exit 0; }
|
||||
local selection
|
||||
selection=$(agent_names_in "$ACTIVE" \
|
||||
| awk -v p="$category" '$0 ~ "^" p "(-|$)"' \
|
||||
| fzf --multi --height=80% \
|
||||
--prompt="disable $category > " \
|
||||
--header="TAB: toggle | ENTER: disable selected" \
|
||||
--preview="sed -n '1,40p' \"$ACTIVE/{}.md\" 2>/dev/null" \
|
||||
--preview-window=right:60%:wrap)
|
||||
[ -n "$selection" ] || { echo "cancelled"; exit 0; }
|
||||
local n
|
||||
while IFS= read -r n; do
|
||||
[ -z "$n" ] && continue
|
||||
cmd_disable "$n" || true
|
||||
done <<< "$selection"
|
||||
}
|
||||
|
||||
# fzf search (parked) by name+description (uses INDEX.json)
|
||||
cmd_search() {
|
||||
require_dirs
|
||||
ensure_fzf
|
||||
command -v jq >/dev/null || { echo "jq is required" >&2; exit 1; }
|
||||
[ -f "$INDEX_JSON" ] || { echo "INDEX.json missing — run: opc-agents reindex" >&2; exit 1; }
|
||||
local query="${1:-}"
|
||||
local tmp; tmp=$(mktemp)
|
||||
jq -r '.[] | "\(.name)\t\(.mode)\t\(.description)"' "$INDEX_JSON" > "$tmp"
|
||||
local selection
|
||||
if [ -n "$query" ]; then
|
||||
selection=$(fzf --query="$query" --multi --height=80% \
|
||||
--delimiter='\t' --with-nth=1,2,3 \
|
||||
--prompt="search > " \
|
||||
--header="TAB: toggle | ENTER: enable selected" \
|
||||
--preview="sed -n '1,40p' \"$PARKED/{1}.md\" 2>/dev/null || echo '(not parked — maybe already active)'" \
|
||||
--preview-window=right:60%:wrap < "$tmp")
|
||||
else
|
||||
selection=$(fzf --multi --height=80% \
|
||||
--delimiter='\t' --with-nth=1,2,3 \
|
||||
--prompt="search > " \
|
||||
--header="TAB: toggle | ENTER: enable selected" \
|
||||
--preview="sed -n '1,40p' \"$PARKED/{1}.md\" 2>/dev/null || echo '(not parked — maybe already active)'" \
|
||||
--preview-window=right:60%:wrap < "$tmp")
|
||||
fi
|
||||
rm -f "$tmp"
|
||||
[ -n "$selection" ] || { echo "cancelled"; exit 0; }
|
||||
local name
|
||||
while IFS=$'\t' read -r name _ _; do
|
||||
[ -z "$name" ] && continue
|
||||
cmd_enable "$name" || true
|
||||
done <<< "$selection"
|
||||
}
|
||||
|
||||
# fzf search (ACTIVE only) by name+description
|
||||
cmd_disable_search() {
|
||||
require_dirs
|
||||
ensure_fzf
|
||||
command -v jq >/dev/null || { echo "jq is required" >&2; exit 1; }
|
||||
[ -f "$INDEX_JSON" ] || { echo "INDEX.json missing — run: opc-agents reindex" >&2; exit 1; }
|
||||
local query="${1:-}"
|
||||
local tmp; tmp=$(mktemp)
|
||||
jq -r '.[] | select(.status=="active" or .status=="both") | "\(.name)\t\(.mode)\t\(.description)"' "$INDEX_JSON" > "$tmp"
|
||||
[ -s "$tmp" ] || { rm -f "$tmp"; echo "no active agents indexed (try: opc-agents reindex)"; exit 0; }
|
||||
local selection
|
||||
if [ -n "$query" ]; then
|
||||
selection=$(fzf --query="$query" --multi --height=80% \
|
||||
--delimiter='\t' --with-nth=1,2,3 \
|
||||
--prompt="disable-search > " \
|
||||
--header="TAB: toggle | ENTER: disable selected" \
|
||||
--preview="sed -n '1,40p' \"$ACTIVE/{1}.md\" 2>/dev/null" \
|
||||
--preview-window=right:60%:wrap < "$tmp")
|
||||
else
|
||||
selection=$(fzf --multi --height=80% \
|
||||
--delimiter='\t' --with-nth=1,2,3 \
|
||||
--prompt="disable-search > " \
|
||||
--header="TAB: toggle | ENTER: disable selected" \
|
||||
--preview="sed -n '1,40p' \"$ACTIVE/{1}.md\" 2>/dev/null" \
|
||||
--preview-window=right:60%:wrap < "$tmp")
|
||||
fi
|
||||
rm -f "$tmp"
|
||||
[ -n "$selection" ] || { echo "cancelled"; exit 0; }
|
||||
local name
|
||||
while IFS=$'\t' read -r name _ _; do
|
||||
[ -z "$name" ] && continue
|
||||
cmd_disable "$name" || true
|
||||
done <<< "$selection"
|
||||
}
|
||||
|
||||
# rebuild INDEX.json + INDEX.md by scanning parked + active
|
||||
cmd_reindex() {
|
||||
require_dirs
|
||||
python3 - "$PARKED" "$ACTIVE" "$INDEX_JSON" "$INDEX_MD" <<'PY'
|
||||
import sys, re, json
|
||||
from pathlib import Path
|
||||
|
||||
parked = Path(sys.argv[1])
|
||||
active = Path(sys.argv[2])
|
||||
out_json = Path(sys.argv[3])
|
||||
out_md = Path(sys.argv[4])
|
||||
|
||||
def read_agent(p: Path):
|
||||
name = p.stem
|
||||
desc = ""
|
||||
mode = ""
|
||||
try:
|
||||
txt = p.read_text(errors="replace")
|
||||
m = re.match(r"^---\s*\n(.*?)\n---\s*\n", txt, re.DOTALL)
|
||||
if m:
|
||||
fm = m.group(1)
|
||||
d = re.search(r'^description:\s*"?(.+?)"?\s*$', fm, re.MULTILINE | re.DOTALL)
|
||||
mo = re.search(r'^mode:\s*"?([^"\n]+?)"?\s*$', fm, re.MULTILINE)
|
||||
if d: desc = d.group(1).strip().splitlines()[0]
|
||||
if mo: mode = mo.group(1).strip()
|
||||
except Exception as e:
|
||||
desc = f"(read error: {e})"
|
||||
return name, desc, mode
|
||||
|
||||
seen = {}
|
||||
def scan(base: Path, status: str):
|
||||
if not base.exists(): return
|
||||
for p in sorted(base.glob("*.md")):
|
||||
if p.name in ("INDEX.md", "README.md"): continue
|
||||
n, d, m = read_agent(p)
|
||||
if n in seen:
|
||||
seen[n]["status"] = "both"
|
||||
else:
|
||||
seen[n] = {"name": n, "mode": m or "?", "description": d, "status": status}
|
||||
|
||||
scan(active, "active")
|
||||
scan(parked, "parked")
|
||||
|
||||
items = sorted(seen.values(), key=lambda x: x["name"])
|
||||
out_json.write_text(json.dumps(items, ensure_ascii=False, indent=2))
|
||||
|
||||
active_n = sum(1 for i in items if i["status"] in ("active", "both"))
|
||||
parked_n = sum(1 for i in items if i["status"] in ("parked", "both"))
|
||||
prim = sum(1 for i in items if i["mode"] == "primary")
|
||||
sub = sum(1 for i in items if i["mode"] == "subagent")
|
||||
|
||||
lines = [
|
||||
"# Opencode Agents — Index",
|
||||
"",
|
||||
f"**{len(items)} unique agents** — active: {active_n}, parked: {parked_n}.",
|
||||
f"Modes — primary: {prim}, subagent: {sub}.",
|
||||
"",
|
||||
f"Active: `{active}` | Parked: `{parked}`",
|
||||
"",
|
||||
"| # | Status | Mode | Name | Description |",
|
||||
"|---|--------|------|------|-------------|",
|
||||
]
|
||||
for i, it in enumerate(items, 1):
|
||||
d = it["description"].replace("\n", " ").replace("|", "\\|")
|
||||
if len(d) > 200: d = d[:197] + "..."
|
||||
lines.append(f"| {i} | {it['status']} | {it['mode']} | `{it['name']}` | {d} |")
|
||||
out_md.write_text("\n".join(lines) + "\n")
|
||||
print(f"reindexed: {len(items)} agents (active={active_n}, parked={parked_n}, primary={prim}, subagent={sub})")
|
||||
PY
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<'USG'
|
||||
opc-agents — opencode agent manager (file-based: <name>.md)
|
||||
|
||||
status counts of active vs parked (+ mode breakdown)
|
||||
list {active|parked|all} list agent names
|
||||
categories prefix-based category counts (PARKED)
|
||||
enable <name> enable single agent (copy parked → active)
|
||||
disable <name> disable single agent (remove from active; keep parked)
|
||||
disable-all [-y|--yes] [--keep-primary]
|
||||
disable every active agent (asks for confirmation)
|
||||
enable-category <prefix> fzf multi-pick within a prefix, then enable
|
||||
disable-category <prefix> fzf multi-pick of ACTIVE agents with prefix, then disable
|
||||
pick fzf: choose category → multi-select → enable
|
||||
disable-pick fzf: choose ACTIVE category → multi-select → disable
|
||||
search [query] fzf fuzzy search across name+description (enable)
|
||||
disable-search [query] fzf fuzzy search ACTIVE agents only (disable)
|
||||
reindex rebuild INDEX.json / INDEX.md
|
||||
|
||||
Notes:
|
||||
- Each agent is a single <name>.md file (frontmatter + body).
|
||||
- disable* never deletes data — removes active copy and keeps (or restores) parked.
|
||||
- --keep-primary on disable-all skips agents with `mode: primary` (those stay loaded).
|
||||
|
||||
Environment:
|
||||
OPC_AGENTS_PARKED override parked dir (default: ~/Documents/opencode-agents-parked)
|
||||
OPC_AGENTS_ACTIVE override active dir (default: ~/.config/opencode/agents)
|
||||
USG
|
||||
}
|
||||
|
||||
main() {
|
||||
local cmd="${1:-}"
|
||||
[ -n "$cmd" ] || { usage; exit 2; }
|
||||
shift || true
|
||||
case "$cmd" in
|
||||
status) cmd_status "$@" ;;
|
||||
list) cmd_list "$@" ;;
|
||||
categories|cats) cmd_categories "$@" ;;
|
||||
enable) cmd_enable "$@" ;;
|
||||
disable) cmd_disable "$@" ;;
|
||||
disable-all) cmd_disable_all "$@" ;;
|
||||
enable-category|enable-cat) cmd_enable_category "$@" ;;
|
||||
disable-category|disable-cat) cmd_disable_category "$@" ;;
|
||||
pick) cmd_pick "$@" ;;
|
||||
disable-pick) cmd_disable_pick "$@" ;;
|
||||
search) cmd_search "$@" ;;
|
||||
disable-search) cmd_disable_search "$@" ;;
|
||||
reindex) cmd_reindex "$@" ;;
|
||||
-h|--help|help) usage ;;
|
||||
*) echo "unknown command: $cmd" >&2; usage; exit 2 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user