docs: add Architecture section to README
ASCII diagrams + prose covering:
- Storage model: one parked source-of-truth fanned out to 3 lazy mirrors
- Data flow for enable/disable (incremental INDEX updates, no global reindex)
- INDEX.json schema with per-target active[] array
- Interactive pick / disable-pick flows incl. target-picker UX
- The fzf {} shell-escape gotcha and the correct quoting pattern
This commit is contained in:
114
README.md
114
README.md
@@ -8,6 +8,120 @@ opencode (≥1.14) auto-scans `~/.claude/skills/` as well as its own `~/.config/
|
||||
|
||||
For full agent / skill / persona generation across multiple AI platforms, see the upstream [`personas`](https://gitea.taygun.net.tr/salvacybersec/personas) repo.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Storage model — one source-of-truth, three lazy mirrors
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ ~/Documents/personas/skills-archive/ │
|
||||
│ (single source-of-truth, 1072+ skills) │
|
||||
│ │
|
||||
│ <skill>/SKILL.md ← frontmatter: │
|
||||
│ name, desc, │
|
||||
│ domain, sub, │
|
||||
│ tags │
|
||||
│ INDEX.json ← per-target active map │
|
||||
│ INDEX.md ← human-readable table │
|
||||
└─────────────────┬───────────────────────┘
|
||||
│
|
||||
│ cp -r (lazy, on demand,
|
||||
│ per --target)
|
||||
▼
|
||||
┌─────────────────────────┼─────────────────────────┐
|
||||
▼ ▼ ▼
|
||||
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
|
||||
│ opencode │ │ claude │ │ feynman │
|
||||
│ ~/.config/ │ │ ~/.claude/ │ │ ~/.feynman/ │
|
||||
│ opencode/ │ │ skills/ │ │ agent/ │
|
||||
│ skills/ │ │ │ │ skills/ │
|
||||
└───────────────┘ └───────────────┘ └───────────────┘
|
||||
```
|
||||
|
||||
The parked archive is the **only** dir that's authoritative. The three target dirs are mutable mirrors — `enable` copies in, `disable` removes from. No skill is ever deleted from parked by opc-skills.
|
||||
|
||||
### Data flow — enable / disable
|
||||
|
||||
```
|
||||
enable <name> --target T1,T2,...
|
||||
│
|
||||
├──► for each target T:
|
||||
│ cp -r PARKED/<name> → ACTIVE_T/<name>
|
||||
│ sync shared refs (e.g. _platform-mapping.md)
|
||||
│ INDEX.json: append T to record's `active[]` array
|
||||
│
|
||||
└──► no global reindex required (incremental update)
|
||||
|
||||
disable <name> --target T1,T2,...
|
||||
│
|
||||
├──► for each target T:
|
||||
│ rm -rf ACTIVE_T/<name>
|
||||
│ INDEX.json: remove T from `active[]`
|
||||
│
|
||||
└──► PARKED copy stays intact
|
||||
```
|
||||
|
||||
### INDEX schema
|
||||
|
||||
Each entry is a deduped record across all 3 targets:
|
||||
|
||||
```json
|
||||
{
|
||||
"folder": "performing-network-traffic-analysis-with-tshark",
|
||||
"name": "performing-network-traffic-analysis-with-tshark",
|
||||
"description": "Automate network traffic analysis using tshark…",
|
||||
"domain": "cybersecurity",
|
||||
"subdomain": "network-security",
|
||||
"tags": ["network-analysis", "pcap", "tshark"],
|
||||
"active": ["claude", "feynman"], // per-target presence
|
||||
"status": "active" // "active" if any target, else "parked"
|
||||
}
|
||||
```
|
||||
|
||||
`reindex` rebuilds this from filesystem state across all 4 dirs (parked + 3 actives). `enable`/`disable` mutate `active[]` and `status` incrementally so bulk axis-ops (`enable-domain`, `enable-tag`) stay accurate without rescanning.
|
||||
|
||||
### Interactive flows (`pick` / `disable-pick`)
|
||||
|
||||
```
|
||||
pick: disable-pick:
|
||||
───── ────────────
|
||||
① fzf: pick category ① fzf: pick category
|
||||
(verb-prefix: performing, (from union-of-actives across
|
||||
detecting, analyzing…) opencode + claude + feynman)
|
||||
|
||||
② fzf --multi: pick skills ② fzf --multi: pick skills
|
||||
preview: PARKED/<skill>/SKILL.md preview: same
|
||||
[TAB toggles, ENTER confirms] rows annotated [oc,cl,fy]
|
||||
|
||||
③ fzf: pick target(s) ③ fzf: pick target(s) to remove from
|
||||
┌───────────────────────────┐ same picker
|
||||
│ all (default) │ ENTER on highlighted "all"
|
||||
│ opencode (N active) │ = remove from every target
|
||||
│ claude (N active) │ where it's currently active
|
||||
│ feynman (N active) │
|
||||
└───────────────────────────┘
|
||||
[TAB multi-select supported]
|
||||
|
||||
④ cp -r per-target ④ rm -rf per-target
|
||||
```
|
||||
|
||||
The target picker leverages an fzf quirk: `--multi` returns the highlighted line as the sole selection if the user never pressed `TAB`. Putting `all` first means a default `ENTER` falls through to all three targets.
|
||||
|
||||
### fzf `{}` quoting (gotcha)
|
||||
|
||||
fzf shell-escapes `{}` substitutions automatically (wraps in single quotes). Wrapping `{}` again in outer quotes embeds those quote literals into the path:
|
||||
|
||||
```bash
|
||||
# ❌ becomes: sed -n '1,40p' ".../skills-archive/'name'/SKILL.md"
|
||||
--preview="sed -n '1,40p' \"$PARKED/{}/SKILL.md\" 2>/dev/null"
|
||||
|
||||
# ✅ becomes: sed -n '1,40p' ".../skills-archive"/'name'/SKILL.md
|
||||
# (bash concatenates adjacent quoted/unquoted strings cleanly)
|
||||
--preview="sed -n '1,40p' \"$PARKED\"/{}/SKILL.md 2>/dev/null"
|
||||
```
|
||||
|
||||
All preview lines in `bin/opc-skills` follow the second pattern.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
|
||||
Reference in New Issue
Block a user