feat: feynman target + interactive target picker + fzf preview fix
- Add feynman as third target (~/.feynman/agent/agents) alongside opencode
and claude. Default targets now: opencode,claude,feynman. Each target
gets its own parked source dir (formats differ: opencode permission/mode,
claude PascalCase tools, feynman lowercase tools + thinking/output).
- Fix fzf {} preview shell-escape bug: {} in fzf is shell-escaped, so
"$PARKED/{}.md" embedded literal quotes into the path and broke preview.
Switched to "$PARKED"/{}.md across all 8 preview lines.
- pick: union-of-parked across all 3 targets for category list (so a
feynman-only agent like researcher is reachable). After agent multi-select
(rows annotated [oc,cl,fy] showing parked-in targets), prompt target
picker (all / opencode / claude / feynman, TAB for multi).
- disable-pick: rewritten to show union of actives across all 3 targets
with [oc,cl,fy] indicators, then target picker for which to disable from.
- cmd_status: 3-target counts (parked + active) with all-3 intersection.
- reindex: tracks parked.{opencode,claude,feynman} + active list across 3.
- New env vars: OPC_AGENTS_FEYNMAN_PARKED, OPC_AGENTS_FEYNMAN_ACTIVE.
This commit is contained in:
64
README.md
64
README.md
@@ -1,12 +1,20 @@
|
|||||||
# opc-agents
|
# opc-agents
|
||||||
|
|
||||||
**Multi-target** agent manager for **opencode** + **Claude Code**. Per-target parked sources (different formats), shared CLI surface. Companion to [`opc-skills`](https://gitea.taygun.net.tr/salvacybersec/opc-skills).
|
**Multi-target** agent manager for **opencode** + **Claude Code** + **Feynman**. Per-target parked sources (different formats), shared CLI surface. Companion to [`opc-skills`](https://gitea.taygun.net.tr/salvacybersec/opc-skills).
|
||||||
|
|
||||||
## Why
|
## Why
|
||||||
|
|
||||||
opencode injects every agent's frontmatter into the calling agent's tool registry. With 100+ agents the registry alone burns ~280K tokens of context before the user types a prompt. Park the ones you don't need this week.
|
opencode injects every agent's frontmatter into the calling agent's tool registry. With 100+ agents the registry alone burns ~280K tokens of context before the user types a prompt. Park the ones you don't need this week.
|
||||||
|
|
||||||
opencode and Claude Code use **different agent frontmatter formats** (opencode: `permission: { … }`, claude: `tools: Read, Glob, …`), so opc-agents keeps a separate parked source per target.
|
opencode, Claude Code, and Feynman use **three different agent frontmatter formats**:
|
||||||
|
|
||||||
|
| target | tools field | extra fields |
|
||||||
|
|---|---|---|
|
||||||
|
| opencode | `permission: { edit, bash, webfetch, … }` | `mode:` (primary/subagent), `temperature:` |
|
||||||
|
| claude | `tools: Read, Glob, Grep, …` (PascalCase) | `color:` |
|
||||||
|
| feynman | `tools: read, write, grep, …` (lowercase) | `thinking:`, `output:`, `defaultProgress:`, `inheritProjectContext:` |
|
||||||
|
|
||||||
|
opc-agents keeps a separate parked source per target and copies the appropriate format for each `--target` selection. The upstream `personas/build.py` pipeline can produce all three formats from one persona definition, so a `--target all` enable can deliver the same persona to all three platforms in their native format.
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
@@ -21,13 +29,17 @@ Ensure `~/.local/bin` is on your `PATH`.
|
|||||||
```
|
```
|
||||||
~/Documents/personas/agents-opencode-archive/ # parked, opencode format
|
~/Documents/personas/agents-opencode-archive/ # parked, opencode format
|
||||||
<name>.md (frontmatter: mode, permission, …)
|
<name>.md (frontmatter: mode, permission, …)
|
||||||
INDEX.json + INDEX.md (canonical index — also tracks claude side)
|
INDEX.json + INDEX.md (canonical index — also tracks claude+feynman parked/active)
|
||||||
|
|
||||||
~/Documents/personas/agents-claude-archive/ # parked, claude format
|
~/Documents/personas/agents-claude-archive/ # parked, claude format
|
||||||
<name>.md (frontmatter: tools: Read, Glob, …)
|
<name>.md (frontmatter: tools: Read, Glob, …)
|
||||||
|
|
||||||
|
~/Documents/personas/agents-feynman-archive/ # parked, feynman format
|
||||||
|
<name>.md (frontmatter: tools: read,write,… thinking, output, defaultProgress)
|
||||||
|
|
||||||
~/.config/opencode/agents/ # active, opencode target
|
~/.config/opencode/agents/ # active, opencode target
|
||||||
~/.claude/agents/ # active, claude target
|
~/.claude/agents/ # active, claude target
|
||||||
|
~/.feynman/agent/agents/ # active, feynman target
|
||||||
```
|
```
|
||||||
|
|
||||||
(`~/Documents/opencode-agents-parked` is symlinked to `personas/agents-opencode-archive` for backward compat.)
|
(`~/Documents/opencode-agents-parked` is symlinked to `personas/agents-opencode-archive` for backward compat.)
|
||||||
@@ -35,13 +47,17 @@ Ensure `~/.local/bin` is on your `PATH`.
|
|||||||
Override via env:
|
Override via env:
|
||||||
- `OPC_AGENTS_PARKED` — opencode parked source (default `personas/agents-opencode-archive`)
|
- `OPC_AGENTS_PARKED` — opencode parked source (default `personas/agents-opencode-archive`)
|
||||||
- `OPC_AGENTS_CLAUDE_PARKED` — claude parked source (default `personas/agents-claude-archive`)
|
- `OPC_AGENTS_CLAUDE_PARKED` — claude parked source (default `personas/agents-claude-archive`)
|
||||||
|
- `OPC_AGENTS_FEYNMAN_PARKED` — feynman parked source (default `personas/agents-feynman-archive`)
|
||||||
- `OPC_AGENTS_ACTIVE` — opencode active dir (default `~/.config/opencode/agents`)
|
- `OPC_AGENTS_ACTIVE` — opencode active dir (default `~/.config/opencode/agents`)
|
||||||
- `OPC_AGENTS_CLAUDE_ACTIVE` — claude active dir (default `~/.claude/agents`)
|
- `OPC_AGENTS_CLAUDE_ACTIVE` — claude active dir (default `~/.claude/agents`)
|
||||||
- `OPC_AGENTS_TARGETS` — comma-separated default targets (default `opencode,claude`)
|
- `OPC_AGENTS_FEYNMAN_ACTIVE` — feynman active dir (default `~/.feynman/agent/agents`)
|
||||||
|
- `OPC_AGENTS_TARGETS` — comma-separated default targets (default `opencode,claude,feynman`)
|
||||||
|
|
||||||
## Targets
|
## Targets
|
||||||
|
|
||||||
`enable`, `disable`, and `disable-all` accept `--target` / `-t` (`opencode`, `claude`, `both`/`all`). Default is `opencode,claude`. An agent missing in a target's parked source is skipped with a clear message — there is **no automatic format conversion**.
|
`enable`, `disable`, and `disable-all` accept `--target` / `-t` (`opencode`, `claude`, `feynman`, `all`, or comma-separated subset). Default is `opencode,claude,feynman`. `both` is preserved as a legacy alias for `opencode,claude` (no feynman).
|
||||||
|
|
||||||
|
An agent missing in a target's parked source is skipped with a clear message — there is **no automatic format conversion** at the opc-agents layer. Cross-format conversion happens upstream in `personas/build.py`.
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
@@ -49,7 +65,7 @@ Override via env:
|
|||||||
|
|
||||||
| | |
|
| | |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `status` | counts of active vs parked + primary/subagent breakdown |
|
| `status` | counts of active vs parked across all 3 targets + opencode primary/subagent breakdown |
|
||||||
| `list {active\|parked\|all}` | list agent names |
|
| `list {active\|parked\|all}` | list agent names |
|
||||||
| `categories` / `cats` | prefix-based base-persona counts (PARKED) |
|
| `categories` / `cats` | prefix-based base-persona counts (PARKED) |
|
||||||
| `variants` / `vars` | suffix-based variant counts (PARKED) — eg. `salva`, `iran` |
|
| `variants` / `vars` | suffix-based variant counts (PARKED) — eg. `salva`, `iran` |
|
||||||
@@ -58,9 +74,9 @@ Override via env:
|
|||||||
|
|
||||||
| | |
|
| | |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `enable <name>` | enable single agent (copy parked → active) |
|
| `enable [--target T] <name>` | enable single agent (copy parked → active) in selected targets |
|
||||||
| `disable <name>` | disable single agent (remove from active; keep parked) |
|
| `disable [--target T] <name>` | disable single agent (remove from active; keep parked) |
|
||||||
| `disable-all [-y\|--yes] [--keep-primary]` | disable every active agent (asks for confirmation) |
|
| `disable-all [--target T] [-y\|--yes] [--keep-primary]` | disable every active agent (asks for confirmation) |
|
||||||
|
|
||||||
### Bulk by axis
|
### Bulk by axis
|
||||||
|
|
||||||
@@ -74,25 +90,43 @@ Override via env:
|
|||||||
|
|
||||||
| | |
|
| | |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `pick` / `disable-pick` | fzf: pick category → multi-select |
|
| `pick` | fzf: union-parked category → multi-select agents → target picker → enable |
|
||||||
|
| `disable-pick` | fzf: union-of-actives → category → multi-select → target picker → disable |
|
||||||
| `search [query]` / `disable-search` | fzf fuzzy search across name + mode + domain + variant + description |
|
| `search [query]` / `disable-search` | fzf fuzzy search across name + mode + domain + variant + description |
|
||||||
| `reindex` | rebuild `INDEX.json` / `INDEX.md` (extracts persona/variant/domain from frontmatter) |
|
| `reindex` | rebuild `INDEX.json` / `INDEX.md` (extracts persona/variant/domain from frontmatter; tracks all 3 targets) |
|
||||||
|
|
||||||
`fzf` is required for the interactive pickers; `jq` for the search variants; `python3` for `reindex`.
|
`fzf` is required for the interactive pickers; `jq` for the search variants; `python3` for `reindex`.
|
||||||
|
|
||||||
|
#### `pick` flow
|
||||||
|
|
||||||
|
1. Choose a category from the **union of parked agents across all 3 targets** (so a feynman-only agent like `researcher` is reachable via `pick`)
|
||||||
|
2. Multi-select agents — each row shows `[parked-in:oc,cl,fy]` indicators
|
||||||
|
3. **Target picker** appears: `all` / `opencode` / `claude` / `feynman` — `TAB` for multi-select; `ENTER` on highlighted `all` (default) sends to all three
|
||||||
|
4. cmd_enable copies the appropriate parked file per target; targets without that agent in their parked dir are skipped with `[t] not parked` message
|
||||||
|
|
||||||
|
#### `disable-pick` flow
|
||||||
|
|
||||||
|
Lists the **union of all active agents across the three targets**, with each entry annotated `[oc,cl,fy]` showing where it's currently active. After multi-select, the target picker decides which target(s) to remove from.
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
- Each agent is a single `<name>.md` file with YAML frontmatter (typically `description`, `mode`, `temperature`, …).
|
- Each agent is a single `<name>.md` file with YAML frontmatter; format depends on target (see table above).
|
||||||
- `disable*` commands never delete data — they remove the active copy and keep (or restore) the parked copy. Re-enable with `opc-agents enable <name>`.
|
- `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).
|
- `--keep-primary` on `disable-all` skips agents with `mode: primary` (opencode-format only — claude and feynman don't have a `mode:` field).
|
||||||
- Common agent prefixes seen in personas: `frodo-`, `marshal-`, `sentinel-`, `bastion-`, `neo-`, `oracle-`, `warden-`, `polyglot-`, etc.
|
- Common agent prefixes seen in personas: `frodo-`, `marshal-`, `sentinel-`, `bastion-`, `neo-`, `oracle-`, `warden-`, `polyglot-`, etc.
|
||||||
|
|
||||||
|
## Implementation notes
|
||||||
|
|
||||||
|
- `fzf` placeholder `{}` is shell-escaped automatically — preview commands use `"$PARKED"/{}.md` (variable quoted, `{}` unquoted) so bash concatenation produces a clean path.
|
||||||
|
|
||||||
## Differences from `opc-skills`
|
## Differences from `opc-skills`
|
||||||
|
|
||||||
| | `opc-skills` | `opc-agents` |
|
| | `opc-skills` | `opc-agents` |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Unit | directory containing `SKILL.md` | single `<name>.md` file |
|
| Unit | directory containing `SKILL.md` | single `<name>.md` file |
|
||||||
| Frontmatter fields used | `name`, `description` | `description`, `mode` |
|
| Frontmatter fields used | `name`, `description`, `domain`, `subdomain`, `tags` | `description`, `mode`, `tools`, `permission` |
|
||||||
|
| Format per target | identical (folder + SKILL.md) | **different per target** (oc / claude / feynman) |
|
||||||
|
| Parked source-of-truth | one (`skills-archive`) | three (one per target format) |
|
||||||
| Shared refs sync | yes (`_platform-mapping.md`) | n/a |
|
| Shared refs sync | yes (`_platform-mapping.md`) | n/a |
|
||||||
| `disable-all` flags | `-y` | `-y`, `--keep-primary` |
|
| `disable-all` flags | `-y` | `-y`, `--keep-primary` |
|
||||||
| Status breakdown | active/parked | + primary/subagent counts |
|
| Status breakdown | active/parked | + opencode primary/subagent counts |
|
||||||
|
|||||||
243
bin/opc-agents
243
bin/opc-agents
@@ -1,35 +1,40 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# opc-agents — multi-target agent enable/disable manager (opencode + claude)
|
# opc-agents — multi-target agent enable/disable manager (opencode + claude + feynman)
|
||||||
#
|
#
|
||||||
# Parked sources (per-target — formats differ):
|
# Parked sources (per-target — formats differ):
|
||||||
# opencode → ~/Documents/personas/agents-opencode-archive (permission: { ... })
|
# opencode → ~/Documents/personas/agents-opencode-archive (permission: { ... })
|
||||||
# claude → ~/Documents/personas/agents-claude-archive (tools: Read, Glob, ...)
|
# claude → ~/Documents/personas/agents-claude-archive (tools: Read, Glob, ...)
|
||||||
|
# feynman → ~/Documents/personas/agents-feynman-archive (tools: lowercase, thinking, output)
|
||||||
#
|
#
|
||||||
# Active targets:
|
# Active targets:
|
||||||
# opencode → ~/.config/opencode/agents
|
# opencode → ~/.config/opencode/agents
|
||||||
# claude → ~/.claude/agents
|
# claude → ~/.claude/agents
|
||||||
|
# feynman → ~/.feynman/agent/agents
|
||||||
#
|
#
|
||||||
# Default targets: opencode,claude (override via OPC_AGENTS_TARGETS or --target)
|
# Default targets: opencode,claude,feynman (override via OPC_AGENTS_TARGETS or --target)
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# Backward compat: OPC_AGENTS_PARKED still works for opencode (legacy)
|
# Backward compat: OPC_AGENTS_PARKED still works for opencode (legacy)
|
||||||
PARKED_OPENCODE="${OPC_AGENTS_PARKED:-$HOME/Documents/personas/agents-opencode-archive}"
|
PARKED_OPENCODE="${OPC_AGENTS_PARKED:-$HOME/Documents/personas/agents-opencode-archive}"
|
||||||
PARKED_CLAUDE="${OPC_AGENTS_CLAUDE_PARKED:-$HOME/Documents/personas/agents-claude-archive}"
|
PARKED_CLAUDE="${OPC_AGENTS_CLAUDE_PARKED:-$HOME/Documents/personas/agents-claude-archive}"
|
||||||
|
PARKED_FEYNMAN="${OPC_AGENTS_FEYNMAN_PARKED:-$HOME/Documents/personas/agents-feynman-archive}"
|
||||||
ACTIVE_OPENCODE="${OPC_AGENTS_ACTIVE:-$HOME/.config/opencode/agents}"
|
ACTIVE_OPENCODE="${OPC_AGENTS_ACTIVE:-$HOME/.config/opencode/agents}"
|
||||||
ACTIVE_CLAUDE="${OPC_AGENTS_CLAUDE_ACTIVE:-$HOME/.claude/agents}"
|
ACTIVE_CLAUDE="${OPC_AGENTS_CLAUDE_ACTIVE:-$HOME/.claude/agents}"
|
||||||
|
ACTIVE_FEYNMAN="${OPC_AGENTS_FEYNMAN_ACTIVE:-$HOME/.feynman/agent/agents}"
|
||||||
|
|
||||||
# Index lives under opencode-archive (canonical) but tracks BOTH targets
|
# Index lives under opencode-archive (canonical) but tracks ALL targets
|
||||||
INDEX_JSON="$PARKED_OPENCODE/INDEX.json"
|
INDEX_JSON="$PARKED_OPENCODE/INDEX.json"
|
||||||
INDEX_MD="$PARKED_OPENCODE/INDEX.md"
|
INDEX_MD="$PARKED_OPENCODE/INDEX.md"
|
||||||
|
|
||||||
DEFAULT_TARGETS="${OPC_AGENTS_TARGETS:-opencode,claude}"
|
DEFAULT_TARGETS="${OPC_AGENTS_TARGETS:-opencode,claude,feynman}"
|
||||||
|
|
||||||
# resolve target → parked dir / active dir
|
# resolve target → parked dir / active dir
|
||||||
target_parked() {
|
target_parked() {
|
||||||
case "$1" in
|
case "$1" in
|
||||||
opencode) printf '%s\n' "$PARKED_OPENCODE" ;;
|
opencode) printf '%s\n' "$PARKED_OPENCODE" ;;
|
||||||
claude) printf '%s\n' "$PARKED_CLAUDE" ;;
|
claude) printf '%s\n' "$PARKED_CLAUDE" ;;
|
||||||
|
feynman) printf '%s\n' "$PARKED_FEYNMAN" ;;
|
||||||
*) echo "unknown target: $1" >&2; return 1 ;;
|
*) echo "unknown target: $1" >&2; return 1 ;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
@@ -37,13 +42,17 @@ target_active() {
|
|||||||
case "$1" in
|
case "$1" in
|
||||||
opencode) printf '%s\n' "$ACTIVE_OPENCODE" ;;
|
opencode) printf '%s\n' "$ACTIVE_OPENCODE" ;;
|
||||||
claude) printf '%s\n' "$ACTIVE_CLAUDE" ;;
|
claude) printf '%s\n' "$ACTIVE_CLAUDE" ;;
|
||||||
|
feynman) printf '%s\n' "$ACTIVE_FEYNMAN" ;;
|
||||||
*) echo "unknown target: $1" >&2; return 1 ;;
|
*) echo "unknown target: $1" >&2; return 1 ;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve_targets() {
|
resolve_targets() {
|
||||||
local raw="${1:-$DEFAULT_TARGETS}"
|
local raw="${1:-$DEFAULT_TARGETS}"
|
||||||
case "$raw" in both|all) raw="opencode,claude" ;; esac
|
case "$raw" in
|
||||||
|
all) raw="opencode,claude,feynman" ;;
|
||||||
|
both) raw="opencode,claude" ;;
|
||||||
|
esac
|
||||||
printf '%s\n' "$raw" | tr ',' '\n' | awk 'NF' | awk '!seen[$0]++'
|
printf '%s\n' "$raw" | tr ',' '\n' | awk 'NF' | awk '!seen[$0]++'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +77,8 @@ parse_target_flag() {
|
|||||||
require_dirs() {
|
require_dirs() {
|
||||||
[ -d "$PARKED_OPENCODE" ] || mkdir -p "$PARKED_OPENCODE"
|
[ -d "$PARKED_OPENCODE" ] || mkdir -p "$PARKED_OPENCODE"
|
||||||
[ -d "$PARKED_CLAUDE" ] || mkdir -p "$PARKED_CLAUDE"
|
[ -d "$PARKED_CLAUDE" ] || mkdir -p "$PARKED_CLAUDE"
|
||||||
mkdir -p "$ACTIVE_OPENCODE" "$ACTIVE_CLAUDE"
|
[ -d "$PARKED_FEYNMAN" ] || mkdir -p "$PARKED_FEYNMAN"
|
||||||
|
mkdir -p "$ACTIVE_OPENCODE" "$ACTIVE_CLAUDE" "$ACTIVE_FEYNMAN"
|
||||||
}
|
}
|
||||||
|
|
||||||
# legacy aliases for backward compat
|
# legacy aliases for backward compat
|
||||||
@@ -89,11 +99,12 @@ cmd_status() {
|
|||||||
echo "Parked sources (per-target — different formats):"
|
echo "Parked sources (per-target — different formats):"
|
||||||
printf " %-10s %5d (%s)\n" "opencode" "$(agent_names_in "$PARKED_OPENCODE" | wc -l)" "$PARKED_OPENCODE"
|
printf " %-10s %5d (%s)\n" "opencode" "$(agent_names_in "$PARKED_OPENCODE" | wc -l)" "$PARKED_OPENCODE"
|
||||||
printf " %-10s %5d (%s)\n" "claude" "$(agent_names_in "$PARKED_CLAUDE" | wc -l)" "$PARKED_CLAUDE"
|
printf " %-10s %5d (%s)\n" "claude" "$(agent_names_in "$PARKED_CLAUDE" | wc -l)" "$PARKED_CLAUDE"
|
||||||
|
printf " %-10s %5d (%s)\n" "feynman" "$(agent_names_in "$PARKED_FEYNMAN" | wc -l)" "$PARKED_FEYNMAN"
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Active targets:"
|
echo "Active targets:"
|
||||||
local t base n
|
local t base n
|
||||||
for t in opencode claude; do
|
for t in opencode claude feynman; do
|
||||||
base=$(target_active "$t")
|
base=$(target_active "$t")
|
||||||
n=$(agent_names_in "$base" | wc -l)
|
n=$(agent_names_in "$base" | wc -l)
|
||||||
printf " %-10s %5d (%s)\n" "$t" "$n" "$base"
|
printf " %-10s %5d (%s)\n" "$t" "$n" "$base"
|
||||||
@@ -110,7 +121,7 @@ cmd_status() {
|
|||||||
echo "opencode mode breakdown:"
|
echo "opencode mode breakdown:"
|
||||||
printf " primary : %5d\n" "$primary"
|
printf " primary : %5d\n" "$primary"
|
||||||
printf " subagent : %5d\n" "$subagent"
|
printf " subagent : %5d\n" "$subagent"
|
||||||
[ "$other" -gt 0 ] && printf " (other) : %5d\n" "$other"
|
[ "$other" -gt 0 ] && printf " (other) : %5d\n" "$other" || true
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,7 +251,7 @@ cmd_enable() {
|
|||||||
set -- "${REMAINING_ARGS[@]}"
|
set -- "${REMAINING_ARGS[@]}"
|
||||||
require_dirs
|
require_dirs
|
||||||
local name="${1:-}"
|
local name="${1:-}"
|
||||||
[ -n "$name" ] || { echo "usage: opc-agents enable [--target opencode|claude|both] <name>" >&2; exit 2; }
|
[ -n "$name" ] || { echo "usage: opc-agents enable [--target opencode|claude|feynman|all] <name>" >&2; exit 2; }
|
||||||
name="${name%.md}"
|
name="${name%.md}"
|
||||||
local t pdir adir src dst
|
local t pdir adir src dst
|
||||||
IFS=',' read -ra tgts <<< "$PARSED_TARGETS"
|
IFS=',' read -ra tgts <<< "$PARSED_TARGETS"
|
||||||
@@ -268,7 +279,7 @@ cmd_disable() {
|
|||||||
set -- "${REMAINING_ARGS[@]}"
|
set -- "${REMAINING_ARGS[@]}"
|
||||||
require_dirs
|
require_dirs
|
||||||
local name="${1:-}"
|
local name="${1:-}"
|
||||||
[ -n "$name" ] || { echo "usage: opc-agents disable [--target opencode|claude|both] <name>" >&2; exit 2; }
|
[ -n "$name" ] || { echo "usage: opc-agents disable [--target opencode|claude|feynman|all] <name>" >&2; exit 2; }
|
||||||
name="${name%.md}"
|
name="${name%.md}"
|
||||||
local t adir src
|
local t adir src
|
||||||
IFS=',' read -ra tgts <<< "$PARSED_TARGETS"
|
IFS=',' read -ra tgts <<< "$PARSED_TARGETS"
|
||||||
@@ -387,7 +398,7 @@ cmd_enable_category() {
|
|||||||
selection=$(printf '%s\n' "${matches[@]}" | fzf --multi --height=70% \
|
selection=$(printf '%s\n' "${matches[@]}" | fzf --multi --height=70% \
|
||||||
--prompt="enable-category $prefix > " \
|
--prompt="enable-category $prefix > " \
|
||||||
--header="TAB: toggle | ENTER: confirm | ESC: cancel" \
|
--header="TAB: toggle | ENTER: confirm | ESC: cancel" \
|
||||||
--preview="sed -n '1,40p' \"$PARKED/{}.md\" 2>/dev/null" \
|
--preview="sed -n '1,40p' \"$PARKED\"/{}.md 2>/dev/null" \
|
||||||
--preview-window=right:60%:wrap)
|
--preview-window=right:60%:wrap)
|
||||||
[ -n "$selection" ] || { echo "cancelled"; exit 0; }
|
[ -n "$selection" ] || { echo "cancelled"; exit 0; }
|
||||||
local n
|
local n
|
||||||
@@ -411,7 +422,7 @@ cmd_disable_category() {
|
|||||||
selection=$(printf '%s\n' "${matches[@]}" | fzf --multi --height=70% \
|
selection=$(printf '%s\n' "${matches[@]}" | fzf --multi --height=70% \
|
||||||
--prompt="disable-category $prefix > " \
|
--prompt="disable-category $prefix > " \
|
||||||
--header="TAB: toggle | ENTER: confirm | ESC: cancel" \
|
--header="TAB: toggle | ENTER: confirm | ESC: cancel" \
|
||||||
--preview="sed -n '1,40p' \"$ACTIVE/{}.md\" 2>/dev/null" \
|
--preview="sed -n '1,40p' \"$ACTIVE\"/{}.md 2>/dev/null" \
|
||||||
--preview-window=right:60%:wrap)
|
--preview-window=right:60%:wrap)
|
||||||
[ -n "$selection" ] || { echo "cancelled"; exit 0; }
|
[ -n "$selection" ] || { echo "cancelled"; exit 0; }
|
||||||
local n
|
local n
|
||||||
@@ -421,57 +432,143 @@ cmd_disable_category() {
|
|||||||
done <<< "$selection"
|
done <<< "$selection"
|
||||||
}
|
}
|
||||||
|
|
||||||
# interactive pick: parked category → multi-select → enable
|
# interactive target picker — returns comma-separated targets on stdout, exit 1 on cancel.
|
||||||
|
pick_targets() {
|
||||||
|
local action="${1:-enable}"
|
||||||
|
ensure_fzf
|
||||||
|
local oc cl fy
|
||||||
|
oc=$(agent_names_in "$ACTIVE_OPENCODE" | wc -l)
|
||||||
|
cl=$(agent_names_in "$ACTIVE_CLAUDE" | wc -l)
|
||||||
|
fy=$(agent_names_in "$ACTIVE_FEYNMAN" | wc -l)
|
||||||
|
local sel
|
||||||
|
sel=$(printf '%s\n' \
|
||||||
|
"all → opencode + claude + feynman" \
|
||||||
|
"opencode ($oc active)" \
|
||||||
|
"claude ($cl active)" \
|
||||||
|
"feynman ($fy active)" \
|
||||||
|
| fzf --multi --height=30% \
|
||||||
|
--prompt="$action target > " \
|
||||||
|
--header="TAB: multi-select | ENTER: confirm (default highlighted = all)" \
|
||||||
|
| awk '{print $1}')
|
||||||
|
[ -n "$sel" ] || return 1
|
||||||
|
if grep -qx all <<< "$sel"; then
|
||||||
|
echo "opencode,claude,feynman"
|
||||||
|
else
|
||||||
|
echo "$sel" | paste -sd ','
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Helper: union of parked agent names across all 3 targets (deduped)
|
||||||
|
parked_union() {
|
||||||
|
{ agent_names_in "$PARKED_OPENCODE"
|
||||||
|
agent_names_in "$PARKED_CLAUDE"
|
||||||
|
agent_names_in "$PARKED_FEYNMAN"; } | sort -u
|
||||||
|
}
|
||||||
|
|
||||||
|
# Helper: union of active agent names with target indicators "name\t[targets]"
|
||||||
|
active_union_with_targets() {
|
||||||
|
{
|
||||||
|
for t in opencode claude feynman; do
|
||||||
|
while IFS= read -r n; do
|
||||||
|
[ -z "$n" ] && continue
|
||||||
|
printf '%s\t%s\n' "$n" "$t"
|
||||||
|
done < <(agent_names_in "$(target_active "$t")")
|
||||||
|
done
|
||||||
|
} | awk -F'\t' '
|
||||||
|
{ agents[$1] = (agents[$1] ? agents[$1]"," : "") $2 }
|
||||||
|
END { for (a in agents) printf "%s\t[%s]\n", a, agents[a] }
|
||||||
|
' | sort
|
||||||
|
}
|
||||||
|
|
||||||
|
# Resolve a parked source path for an agent name (first target where parked file exists)
|
||||||
|
parked_path_for() {
|
||||||
|
local name="$1" t pdir
|
||||||
|
for t in opencode claude feynman; do
|
||||||
|
pdir=$(target_parked "$t")
|
||||||
|
[ -f "$pdir/$name.md" ] && { echo "$pdir/$name.md"; return 0; }
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# interactive pick: union-parked category → multi-select → pick target(s)
|
||||||
cmd_pick() {
|
cmd_pick() {
|
||||||
require_dirs
|
require_dirs
|
||||||
ensure_fzf
|
ensure_fzf
|
||||||
local category
|
# Categories from union of parked across all 3 targets
|
||||||
category=$(cmd_categories | fzf --prompt="category > " --height=50% \
|
local cats category
|
||||||
--header="Pick a category (first column = count)" \
|
cats=$(parked_union | awk -F'-' '{print $1}' | sort | uniq -c | sort -rn)
|
||||||
|
category=$(printf '%s\n' "$cats" | fzf --prompt="category > " --height=50% \
|
||||||
|
--header="Pick a category (first column = count) • parked = union of all 3 targets" \
|
||||||
| awk '{print $2}')
|
| awk '{print $2}')
|
||||||
[ -n "$category" ] || { echo "cancelled"; exit 0; }
|
[ -n "$category" ] || { echo "cancelled"; exit 0; }
|
||||||
|
|
||||||
|
# Build {name}\t{parked-in:[oc,cl,fy]} lines
|
||||||
|
local tmp; tmp=$(mktemp)
|
||||||
|
{
|
||||||
|
for t in opencode claude feynman; do
|
||||||
|
while IFS= read -r n; do
|
||||||
|
[ -z "$n" ] && continue
|
||||||
|
printf '%s\t%s\n' "$n" "$t"
|
||||||
|
done < <(agent_names_in "$(target_parked "$t")")
|
||||||
|
done
|
||||||
|
} | awk -F'\t' '
|
||||||
|
{ p[$1] = (p[$1] ? p[$1]"," : "") $2 }
|
||||||
|
END { for (a in p) printf "%s\t[%s]\n", a, p[a] }
|
||||||
|
' | awk -F'\t' -v p="$category" '$1 ~ "^"p"(-|$)"' | sort > "$tmp"
|
||||||
|
|
||||||
local selection
|
local selection
|
||||||
selection=$(agent_names_in "$PARKED" \
|
selection=$(fzf --multi --height=80% \
|
||||||
| awk -v p="$category" '$0 ~ "^" p "(-|$)"' \
|
--prompt="$category > " \
|
||||||
| fzf --multi --height=80% \
|
--header="TAB: toggle | ENTER: confirm → pick target" \
|
||||||
--prompt="$category > " \
|
--delimiter='\t' --with-nth=1,2 \
|
||||||
--header="TAB: toggle | ENTER: enable selected" \
|
--preview="for d in \"$PARKED_OPENCODE\" \"$PARKED_CLAUDE\" \"$PARKED_FEYNMAN\"; do [ -f \"\$d\"/{1}.md ] && { echo \"=== \$d ===\"; sed -n '1,40p' \"\$d\"/{1}.md; break; }; done 2>/dev/null" \
|
||||||
--preview="sed -n '1,40p' \"$PARKED/{}.md\" 2>/dev/null" \
|
--preview-window=right:60%:wrap < "$tmp")
|
||||||
--preview-window=right:60%:wrap)
|
rm -f "$tmp"
|
||||||
[ -n "$selection" ] || { echo "cancelled"; exit 0; }
|
[ -n "$selection" ] || { echo "cancelled"; exit 0; }
|
||||||
|
|
||||||
|
local targets
|
||||||
|
targets=$(pick_targets "enable") || { echo "cancelled (no target)"; exit 0; }
|
||||||
|
|
||||||
local n
|
local n
|
||||||
while IFS= read -r n; do
|
while IFS=$'\t' read -r n _; do
|
||||||
[ -z "$n" ] && continue
|
[ -z "$n" ] && continue
|
||||||
cmd_enable "$n" || true
|
cmd_enable --target "$targets" "$n" || true
|
||||||
done <<< "$selection"
|
done <<< "$selection"
|
||||||
}
|
}
|
||||||
|
|
||||||
# interactive pick: ACTIVE category → multi-select → disable
|
# interactive disable-pick: union of actives across all 3 targets → pick target(s) to remove from
|
||||||
cmd_disable_pick() {
|
cmd_disable_pick() {
|
||||||
require_dirs
|
require_dirs
|
||||||
ensure_fzf
|
ensure_fzf
|
||||||
local active_count
|
local tmp; tmp=$(mktemp)
|
||||||
active_count=$(agent_names_in "$ACTIVE" | wc -l)
|
active_union_with_targets > "$tmp"
|
||||||
[ "$active_count" -gt 0 ] || { echo "no active agents"; exit 0; }
|
[ -s "$tmp" ] || { rm -f "$tmp"; echo "no active agents across any target"; exit 0; }
|
||||||
|
|
||||||
local cats category
|
local cats category
|
||||||
cats=$(agent_names_in "$ACTIVE" | awk -F'-' '{print $1}' | sort | uniq -c | sort -rn)
|
cats=$(awk -F'\t' '{print $1}' "$tmp" | awk -F'-' '{print $1}' | sort | uniq -c | sort -rn)
|
||||||
category=$(printf '%s\n' "$cats" | fzf --prompt="active-category > " --height=50% \
|
category=$(printf '%s\n' "$cats" | fzf --prompt="active-category > " --height=50% \
|
||||||
--header="Pick a category of ACTIVE agents to disable" \
|
--header="Pick category from ACTIVE agents (any target)" \
|
||||||
| awk '{print $2}')
|
| awk '{print $2}')
|
||||||
[ -n "$category" ] || { echo "cancelled"; exit 0; }
|
[ -n "$category" ] || { rm -f "$tmp"; echo "cancelled"; exit 0; }
|
||||||
|
|
||||||
local selection
|
local selection
|
||||||
selection=$(agent_names_in "$ACTIVE" \
|
selection=$(awk -F'\t' -v p="$category" '$1 ~ "^"p"(-|$)"' "$tmp" \
|
||||||
| awk -v p="$category" '$0 ~ "^" p "(-|$)"' \
|
|
||||||
| fzf --multi --height=80% \
|
| fzf --multi --height=80% \
|
||||||
--prompt="disable $category > " \
|
--prompt="disable $category > " \
|
||||||
--header="TAB: toggle | ENTER: disable selected" \
|
--header="TAB: toggle | ENTER: pick target(s) to disable from" \
|
||||||
--preview="sed -n '1,40p' \"$ACTIVE/{}.md\" 2>/dev/null" \
|
--delimiter='\t' --with-nth=1,2 \
|
||||||
|
--preview="for d in \"$ACTIVE_OPENCODE\" \"$ACTIVE_CLAUDE\" \"$ACTIVE_FEYNMAN\"; do [ -f \"\$d\"/{1}.md ] && { echo \"=== \$d ===\"; sed -n '1,40p' \"\$d\"/{1}.md; break; }; done 2>/dev/null" \
|
||||||
--preview-window=right:60%:wrap)
|
--preview-window=right:60%:wrap)
|
||||||
|
rm -f "$tmp"
|
||||||
[ -n "$selection" ] || { echo "cancelled"; exit 0; }
|
[ -n "$selection" ] || { echo "cancelled"; exit 0; }
|
||||||
|
|
||||||
|
local targets
|
||||||
|
targets=$(pick_targets "disable") || { echo "cancelled (no target)"; exit 0; }
|
||||||
|
|
||||||
local n
|
local n
|
||||||
while IFS= read -r n; do
|
while IFS=$'\t' read -r n _; do
|
||||||
[ -z "$n" ] && continue
|
[ -z "$n" ] && continue
|
||||||
cmd_disable "$n" || true
|
cmd_disable --target "$targets" "$n" || true
|
||||||
done <<< "$selection"
|
done <<< "$selection"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -490,14 +587,14 @@ cmd_search() {
|
|||||||
--delimiter='\t' --with-nth=1,2,3,4,5 \
|
--delimiter='\t' --with-nth=1,2,3,4,5 \
|
||||||
--prompt="search > " \
|
--prompt="search > " \
|
||||||
--header="TAB: toggle | ENTER: enable selected" \
|
--header="TAB: toggle | ENTER: enable selected" \
|
||||||
--preview="sed -n '1,40p' \"$PARKED/{1}.md\" 2>/dev/null || echo '(not parked — maybe already active)'" \
|
--preview="sed -n '1,40p' \"$PARKED\"/{1}.md 2>/dev/null || echo '(not parked — maybe already active)'" \
|
||||||
--preview-window=right:60%:wrap < "$tmp")
|
--preview-window=right:60%:wrap < "$tmp")
|
||||||
else
|
else
|
||||||
selection=$(fzf --multi --height=80% \
|
selection=$(fzf --multi --height=80% \
|
||||||
--delimiter='\t' --with-nth=1,2,3,4,5 \
|
--delimiter='\t' --with-nth=1,2,3,4,5 \
|
||||||
--prompt="search > " \
|
--prompt="search > " \
|
||||||
--header="TAB: toggle | ENTER: enable selected" \
|
--header="TAB: toggle | ENTER: enable selected" \
|
||||||
--preview="sed -n '1,40p' \"$PARKED/{1}.md\" 2>/dev/null || echo '(not parked — maybe already active)'" \
|
--preview="sed -n '1,40p' \"$PARKED\"/{1}.md 2>/dev/null || echo '(not parked — maybe already active)'" \
|
||||||
--preview-window=right:60%:wrap < "$tmp")
|
--preview-window=right:60%:wrap < "$tmp")
|
||||||
fi
|
fi
|
||||||
rm -f "$tmp"
|
rm -f "$tmp"
|
||||||
@@ -525,14 +622,14 @@ cmd_disable_search() {
|
|||||||
--delimiter='\t' --with-nth=1,2,3,4,5 \
|
--delimiter='\t' --with-nth=1,2,3,4,5 \
|
||||||
--prompt="disable-search > " \
|
--prompt="disable-search > " \
|
||||||
--header="TAB: toggle | ENTER: disable selected" \
|
--header="TAB: toggle | ENTER: disable selected" \
|
||||||
--preview="sed -n '1,40p' \"$ACTIVE/{1}.md\" 2>/dev/null" \
|
--preview="sed -n '1,40p' \"$ACTIVE\"/{1}.md 2>/dev/null" \
|
||||||
--preview-window=right:60%:wrap < "$tmp")
|
--preview-window=right:60%:wrap < "$tmp")
|
||||||
else
|
else
|
||||||
selection=$(fzf --multi --height=80% \
|
selection=$(fzf --multi --height=80% \
|
||||||
--delimiter='\t' --with-nth=1,2,3,4,5 \
|
--delimiter='\t' --with-nth=1,2,3,4,5 \
|
||||||
--prompt="disable-search > " \
|
--prompt="disable-search > " \
|
||||||
--header="TAB: toggle | ENTER: disable selected" \
|
--header="TAB: toggle | ENTER: disable selected" \
|
||||||
--preview="sed -n '1,40p' \"$ACTIVE/{1}.md\" 2>/dev/null" \
|
--preview="sed -n '1,40p' \"$ACTIVE\"/{1}.md 2>/dev/null" \
|
||||||
--preview-window=right:60%:wrap < "$tmp")
|
--preview-window=right:60%:wrap < "$tmp")
|
||||||
fi
|
fi
|
||||||
rm -f "$tmp"
|
rm -f "$tmp"
|
||||||
@@ -547,16 +644,18 @@ cmd_disable_search() {
|
|||||||
# rebuild INDEX.json + INDEX.md by scanning parked + active
|
# rebuild INDEX.json + INDEX.md by scanning parked + active
|
||||||
cmd_reindex() {
|
cmd_reindex() {
|
||||||
require_dirs
|
require_dirs
|
||||||
python3 - "$PARKED_OPENCODE" "$PARKED_CLAUDE" "$ACTIVE_OPENCODE" "$ACTIVE_CLAUDE" "$INDEX_JSON" "$INDEX_MD" <<'PY'
|
python3 - "$PARKED_OPENCODE" "$PARKED_CLAUDE" "$PARKED_FEYNMAN" "$ACTIVE_OPENCODE" "$ACTIVE_CLAUDE" "$ACTIVE_FEYNMAN" "$INDEX_JSON" "$INDEX_MD" <<'PY'
|
||||||
import sys, re, json
|
import sys, re, json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
parked_oc = Path(sys.argv[1])
|
parked_oc = Path(sys.argv[1])
|
||||||
parked_cl = Path(sys.argv[2])
|
parked_cl = Path(sys.argv[2])
|
||||||
active_oc = Path(sys.argv[3])
|
parked_fy = Path(sys.argv[3])
|
||||||
active_cl = Path(sys.argv[4])
|
active_oc = Path(sys.argv[4])
|
||||||
out_json = Path(sys.argv[5])
|
active_cl = Path(sys.argv[5])
|
||||||
out_md = Path(sys.argv[6])
|
active_fy = Path(sys.argv[6])
|
||||||
|
out_json = Path(sys.argv[7])
|
||||||
|
out_md = Path(sys.argv[8])
|
||||||
|
|
||||||
def read_agent(p: Path):
|
def read_agent(p: Path):
|
||||||
name = p.stem
|
name = p.stem
|
||||||
@@ -590,7 +689,7 @@ def upsert(rec):
|
|||||||
n = rec["name"]
|
n = rec["name"]
|
||||||
cur = agents.get(n)
|
cur = agents.get(n)
|
||||||
if cur is None:
|
if cur is None:
|
||||||
rec.setdefault("parked", {"opencode": False, "claude": False})
|
rec.setdefault("parked", {"opencode": False, "claude": False, "feynman": False})
|
||||||
rec.setdefault("active", [])
|
rec.setdefault("active", [])
|
||||||
agents[n] = rec
|
agents[n] = rec
|
||||||
else:
|
else:
|
||||||
@@ -619,17 +718,21 @@ def scan_active(base: Path, target: str):
|
|||||||
|
|
||||||
scan_parked(parked_oc, "opencode")
|
scan_parked(parked_oc, "opencode")
|
||||||
scan_parked(parked_cl, "claude")
|
scan_parked(parked_cl, "claude")
|
||||||
|
scan_parked(parked_fy, "feynman")
|
||||||
scan_active(active_oc, "opencode")
|
scan_active(active_oc, "opencode")
|
||||||
scan_active(active_cl, "claude")
|
scan_active(active_cl, "claude")
|
||||||
|
scan_active(active_fy, "feynman")
|
||||||
|
|
||||||
items = sorted(agents.values(), key=lambda x: x["name"])
|
items = sorted(agents.values(), key=lambda x: x["name"])
|
||||||
out_json.write_text(json.dumps(items, ensure_ascii=False, indent=2))
|
out_json.write_text(json.dumps(items, ensure_ascii=False, indent=2))
|
||||||
|
|
||||||
n_oc_park = sum(1 for i in items if i["parked"]["opencode"])
|
n_oc_park = sum(1 for i in items if i["parked"]["opencode"])
|
||||||
n_cl_park = sum(1 for i in items if i["parked"]["claude"])
|
n_cl_park = sum(1 for i in items if i["parked"]["claude"])
|
||||||
|
n_fy_park = sum(1 for i in items if i["parked"]["feynman"])
|
||||||
in_oc = sum(1 for i in items if "opencode" in i["active"])
|
in_oc = sum(1 for i in items if "opencode" in i["active"])
|
||||||
in_cl = sum(1 for i in items if "claude" in i["active"])
|
in_cl = sum(1 for i in items if "claude" in i["active"])
|
||||||
both_park = sum(1 for i in items if i["parked"]["opencode"] and i["parked"]["claude"])
|
in_fy = sum(1 for i in items if "feynman" in i["active"])
|
||||||
|
all_park = sum(1 for i in items if all(i["parked"].get(t) for t in ("opencode","claude","feynman")))
|
||||||
prim = sum(1 for i in items if i["mode"] == "primary")
|
prim = sum(1 for i in items if i["mode"] == "primary")
|
||||||
sub = sum(1 for i in items if i["mode"] == "subagent")
|
sub = sum(1 for i in items if i["mode"] == "subagent")
|
||||||
|
|
||||||
@@ -640,19 +743,21 @@ top_doms = ", ".join(f"{k}:{v}" for k,v in dom_c.most_common(8)) or "-"
|
|||||||
top_vars = ", ".join(f"{k}:{v}" for k,v in var_c.most_common(8)) or "-"
|
top_vars = ", ".join(f"{k}:{v}" for k,v in var_c.most_common(8)) or "-"
|
||||||
|
|
||||||
lines = [
|
lines = [
|
||||||
"# Opencode + Claude Agents — Index",
|
"# Opencode + Claude + Feynman Agents — Index",
|
||||||
"",
|
"",
|
||||||
f"**{len(items)} unique agents.**",
|
f"**{len(items)} unique agents.**",
|
||||||
f"Parked sources — opencode: {n_oc_park}, claude: {n_cl_park}, both: {both_park}.",
|
f"Parked sources — opencode: {n_oc_park}, claude: {n_cl_park}, feynman: {n_fy_park}, all-three: {all_park}.",
|
||||||
f"Active — opencode: {in_oc}, claude: {in_cl}.",
|
f"Active — opencode: {in_oc}, claude: {in_cl}, feynman: {in_fy}.",
|
||||||
f"Modes — primary: {prim}, subagent: {sub}.",
|
f"Modes — primary: {prim}, subagent: {sub}.",
|
||||||
f"Top domains — {top_doms}.",
|
f"Top domains — {top_doms}.",
|
||||||
f"Top variants — {top_vars}.",
|
f"Top variants — {top_vars}.",
|
||||||
"",
|
"",
|
||||||
f"Parked opencode: `{parked_oc}`",
|
f"Parked opencode: `{parked_oc}`",
|
||||||
f"Parked claude : `{parked_cl}`",
|
f"Parked claude : `{parked_cl}`",
|
||||||
|
f"Parked feynman : `{parked_fy}`",
|
||||||
f"Active opencode: `{active_oc}`",
|
f"Active opencode: `{active_oc}`",
|
||||||
f"Active claude : `{active_cl}`",
|
f"Active claude : `{active_cl}`",
|
||||||
|
f"Active feynman : `{active_fy}`",
|
||||||
"",
|
"",
|
||||||
"| # | Parked | Active | Mode | Persona | Variant | Domain | Name | Description |",
|
"| # | Parked | Active | Mode | Persona | Variant | Domain | Name | Description |",
|
||||||
"|---|--------|--------|------|---------|---------|--------|------|-------------|",
|
"|---|--------|--------|------|---------|---------|--------|------|-------------|",
|
||||||
@@ -660,7 +765,7 @@ lines = [
|
|||||||
for i, it in enumerate(items, 1):
|
for i, it in enumerate(items, 1):
|
||||||
d = it["description"].replace("\n", " ").replace("|", "\\|")
|
d = it["description"].replace("\n", " ").replace("|", "\\|")
|
||||||
if len(d) > 140: d = d[:137] + "..."
|
if len(d) > 140: d = d[:137] + "..."
|
||||||
pk = ",".join(t for t in ("opencode","claude") if it["parked"][t]) or "-"
|
pk = ",".join(t for t in ("opencode","claude","feynman") if it["parked"][t]) or "-"
|
||||||
ac = ",".join(it.get("active", [])) or "-"
|
ac = ",".join(it.get("active", [])) or "-"
|
||||||
lines.append(
|
lines.append(
|
||||||
f"| {i} | {pk} | {ac} | {it['mode']} | {it['persona']} | "
|
f"| {i} | {pk} | {ac} | {it['mode']} | {it['persona']} | "
|
||||||
@@ -668,17 +773,17 @@ for i, it in enumerate(items, 1):
|
|||||||
)
|
)
|
||||||
out_md.write_text("\n".join(lines) + "\n")
|
out_md.write_text("\n".join(lines) + "\n")
|
||||||
print(f"reindexed: {len(items)} agents "
|
print(f"reindexed: {len(items)} agents "
|
||||||
f"(parked oc={n_oc_park} cl={n_cl_park} both={both_park}, "
|
f"(parked oc={n_oc_park} cl={n_cl_park} fy={n_fy_park} all3={all_park}, "
|
||||||
f"active oc={in_oc} cl={in_cl}, primary={prim}, subagent={sub})")
|
f"active oc={in_oc} cl={in_cl} fy={in_fy}, primary={prim}, subagent={sub})")
|
||||||
PY
|
PY
|
||||||
}
|
}
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
cat <<'USG'
|
cat <<'USG'
|
||||||
opc-agents — multi-target agent manager (opencode + claude)
|
opc-agents — multi-target agent manager (opencode + claude + feynman)
|
||||||
|
|
||||||
Targets: opencode, claude (or "both"/"all"; default = opencode,claude)
|
Targets: opencode, claude, feynman ("all"=all three, "both"=opencode+claude legacy)
|
||||||
Use --target / -t with enable/disable/enable-all/disable-all to scope the operation.
|
Default = opencode,claude,feynman. Use --target / -t to scope ops.
|
||||||
|
|
||||||
status per-target counts (parked + active) + mode breakdown
|
status per-target counts (parked + active) + mode breakdown
|
||||||
list {active|parked|all} list agent names (active = opencode set)
|
list {active|parked|all} list agent names (active = opencode set)
|
||||||
@@ -703,18 +808,24 @@ Use --target / -t with enable/disable/enable-all/disable-all to scope the operat
|
|||||||
reindex rebuild INDEX.json / INDEX.md (per-target parked + active)
|
reindex rebuild INDEX.json / INDEX.md (per-target parked + active)
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
- Agent format differs between targets: opencode uses `permission: {...}`,
|
- Agent format differs between targets:
|
||||||
claude uses `tools: Read, Glob, ...`. Each target reads from its own parked dir.
|
opencode → `permission: {...}` + `mode:` field
|
||||||
|
claude → `tools: Read, Glob, ...` (PascalCase)
|
||||||
|
feynman → `tools: read, write, ...` (lowercase) + `thinking`/`output` fields
|
||||||
|
Each target reads from its own parked dir; cross-target enable copies as-is.
|
||||||
- disable* never deletes parked sources.
|
- disable* never deletes parked sources.
|
||||||
- --keep-primary skips agents with `mode: primary` (opencode-format only).
|
- --keep-primary skips agents with `mode: primary` (opencode-format only).
|
||||||
- Single source-of-truth: ~/Documents/personas/agents-{opencode,claude}-archive
|
- Single source-of-truth: ~/Documents/personas/agents-{opencode,claude,feynman}-archive
|
||||||
|
|
||||||
Environment:
|
Environment:
|
||||||
OPC_AGENTS_PARKED opencode parked (default: personas/agents-opencode-archive)
|
OPC_AGENTS_PARKED opencode parked (default: personas/agents-opencode-archive)
|
||||||
OPC_AGENTS_CLAUDE_PARKED claude parked (default: personas/agents-claude-archive)
|
OPC_AGENTS_CLAUDE_PARKED claude parked (default: personas/agents-claude-archive)
|
||||||
OPC_AGENTS_ACTIVE opencode active (default: ~/.config/opencode/agents)
|
OPC_AGENTS_FEYNMAN_PARKED feynman parked (default: personas/agents-feynman-archive)
|
||||||
OPC_AGENTS_CLAUDE_ACTIVE claude active (default: ~/.claude/agents)
|
OPC_AGENTS_ACTIVE opencode active (default: ~/.config/opencode/agents)
|
||||||
OPC_AGENTS_TARGETS default targets when --target omitted (default: opencode,claude)
|
OPC_AGENTS_CLAUDE_ACTIVE claude active (default: ~/.claude/agents)
|
||||||
|
OPC_AGENTS_FEYNMAN_ACTIVE feynman active (default: ~/.feynman/agent/agents)
|
||||||
|
OPC_AGENTS_TARGETS default targets when --target omitted
|
||||||
|
(default: opencode,claude,feynman)
|
||||||
USG
|
USG
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user