diff --git a/.mdproof/lessons-learned.md b/.mdproof/lessons-learned.md index 0a6e3e9c..9a819d82 100644 --- a/.mdproof/lessons-learned.md +++ b/.mdproof/lessons-learned.md @@ -72,6 +72,13 @@ - **Fix**: Use `>/dev/null 2>&1` (redirect both stdout AND stderr) for cleanup commands in steps that need pure JSON output - **Runbooks affected**: extras_flatten_runbook.md +### [gotcha] chmod 0444 does not block writes when running as root + +- **Context**: `TestLog_SyncPartialStatus` used `os.Chmod(dir, 0444)` to make a target directory read-only, expecting sync to fail on that target and log `"status":"partial"` +- **Discovery**: The devcontainer runs as root. Root ignores POSIX permission bits — `chmod 0444` has no effect. The "broken" target synced successfully, so the oplog recorded `"status":"ok"` instead of `"partial"` +- **Fix**: Use a **dangling symlink** instead: `os.Symlink("/nonexistent/path", targetPath)`. This makes `os.Stat` return "not exist" (passes config validation) but `os.MkdirAll` fails because the symlink entry blocks directory creation. Works regardless of UID +- **Runbooks affected**: `tests/integration/log_test.go` (`TestLog_SyncPartialStatus`) + ### [gotcha] Full-directory mdproof runs cause inter-runbook state leakage - **Context**: Running `mdproof --report json /path/to/tests/` executes all runbooks sequentially in the same environment (same ssenv). Earlier runbooks install skills, modify config, fill trash — this state persists for later runbooks diff --git a/CHANGELOG.md b/CHANGELOG.md index c3b0b8e1..0a3b3a92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,131 @@ # Changelog +## [0.19.0] - 2026-04-11 + +### New Features + +#### Agent Management + +Agents are now a first-class resource type alongside skills. You can install, sync, audit, and manage agent files (`.md`) across agent-capable targets (Claude, Cursor, OpenCode, Augment) with the same workflow as skills. + +- **Agents source directory** — agents live in `~/.config/skillshare/agents/` (or `.skillshare/agents/` in project mode). `skillshare init` creates the directory automatically, and `agents_source` is a new config field that can be customized + ```bash + skillshare init # creates skills/ and agents/ + skillshare init -p # same for project mode + ``` + +- **Positional kind filter** — most commands accept an `agents` keyword to scope the operation to agents only. Without it, commands operate on skills (existing behavior is unchanged) + ```bash + skillshare sync agents # sync agents only + skillshare sync --all # sync skills + agents + extras + skillshare list agents # list installed agents + skillshare check agents # detect drift on agent repos + skillshare update agents # update agents + skillshare audit agents # scan agents for security issues + skillshare uninstall agents # uninstall by kind + skillshare enable foo --kind agent + skillshare disable foo --kind agent + ``` + +- **Install agents from repos** — `install` auto-detects agents in three layouts: + - `agents/` convention subdirectory + - mixed-kind repos with both `SKILL.md` and `agents/` + - pure-agent repos (root `.md` files, no `SKILL.md`) + ```bash + skillshare install github.com/team/agents # auto-detect + skillshare install github.com/team/repo --kind agent # force agent mode + skillshare install github.com/team/repo --agent cr # specific agents + ``` + Conventional files (`README.md`, `LICENSE.md`, `CHANGELOG.md`) are automatically excluded + +- **Tracked agent repos** — agents can be installed with `--track` for git-pull updates, including nested discovery. `check`, `update`, `doctor`, and `uninstall` all recognise tracked agent repos, and the `--group` / `-G` flag filters by repo group + +- **Agent sync modes** — merge (default, per-file symlink), symlink (whole directory), and copy are all supported. `skillshare sync agents` skips targets that don't declare an `agents:` path and prints a warning + +- **`.agentignore` support** — agents can be excluded via `.agentignore` and `.agentignore.local` using the same gitignore-style patterns as `.skillignore`. The Web UI Config page now has a dedicated `.agentignore` tab + +- **Agent audit** — `skillshare audit` scans agent files individually against the full audit rule set, with Skills/Agents tab switching in both the TUI and Web UI. Audit results carry a `kind` field so tooling can filter by resource type + +- **Agent backup and restore** — sync automatically backs up agents before applying changes, in both global and project mode. The backup TUI and trash TUI tag agents with an `[A]` badge and route restores to the correct source directory + +- **Project-mode agent support** — every agent command works in project mode with `-p`. Agents are reconciled alongside skills into `.skillshare/` + +- **JSON output for agents** — `install --json` and `update --json` now emit agent-aware payloads and apply the same audit block-threshold gate as skills. Useful for scripted agent workflows + ```bash + skillshare update agents --json --audit-threshold high + ``` + +- **Kind badges** — TUI and Web UI surface `[S]` / `[A]` badges throughout (list, diff, audit, trash, backup, detail, update, targets pages) so you can tell at a glance what kind of resource you're looking at + +#### Unified Web UI Resources + +- **`/resources` route** — the old `/skills` page is now `/resources`, with Skills and Agents tabs. Tab state persists to localStorage, and the underline tab style follows the active theme (playful mode gets wobble borders) + +- **Targets page redesign** — equal Skills and Agents sections, with a modal picker for adding targets. Filter Studio links include a `?kind=` param so you jump directly to the right context + +- **Update page redesign** — a new three-phase flow (selecting → updating → done) with skills/agents tabs, group-based sorting, and status cards. EventSource streaming is properly cleaned up on page change + +- **Filter Studio agent support** — agent filters can be edited via `PATCH /api/targets/:name` (`agent_include`, `agent_exclude`, `agent_mode`) and via the CLI (`targets edit --add-agent-include`, `--remove-agent-include`, `--agent-mode`, and so on). The UI Filter Studio is a single-context view driven by `?kind=skill|agent` + +- **Audit cache** — audit results are now cached with React Query and invalidated on mutation. The audit card icon colour follows the max severity, and the count no longer mixes agent totals with finding counts + +- **Collect page scope switcher** — a new segmented control lets you collect skills or agents from targets + +#### Theme System + +- **`internal/theme` package** — unified light/dark terminal palette with WCAG-AA-compliant light colours and softened dark primary. Resolution order: `NO_COLOR` > `SKILLSHARE_THEME` > OSC 11 terminal probe > dark fallback. All TUIs, list output, audit output, and plain CLI output now route through the theme + ```bash + SKILLSHARE_THEME=light skillshare list + SKILLSHARE_THEME=dark skillshare audit + ``` + `skillshare doctor` includes a theme check to help debug unreadable colours + +#### Install & TUI Polish + +- **Explicit `SKILL.md` URLs resolve to one skill** — pasting a direct `blob/.../SKILL.md` URL now installs only that skill, bypassing the orchestrator pack prompt. Previously, the URL would trigger the full multi-select picker even though the intent was clear + ```bash + skillshare install https://github.com/team/repo/blob/main/frontend/tdd/SKILL.md + ``` + Refs: #124 + +- **Radio checklist follows the cursor** — the single-select TUI (used for orchestrator selection, branch selection, and similar flows) now auto-selects the focused row. No more confusing empty-selection state — pressing Enter always confirms the item your cursor is on + +- **Diff TUI** — single-line items with group headers instead of the old verbose-per-item layout. Agent diffs are shown with an `[A]` badge + +- **List TUI** — entries are now grouped by tracked repo root and local top directory, with a new `k:kind` filter tag for quick agent/skill filtering inside the fuzzy filter + +#### Centralized Metadata Store + +- **`.metadata.json` replaces sidecar files and `registry.yaml`** — installation metadata is now stored in a single atomic file per source (`~/.config/skillshare/skills/.metadata.json`). This fixes long-standing issues with grouped skill collisions (e.g. two skills both named `dev` in different folders) where the old basename-keyed registry would mix them up + - **Automatic migration** — the first load after upgrade reads any existing `registry.yaml` and per-skill `.skillshare-meta.json` sidecars, merges them into `.metadata.json`, and cleans up the old files. Idempotent — safe to run repeatedly + - **Full-path keys** — lookups use the full source-relative path, so nested skills never collide + - No user action required; existing installs continue to work + +### Bug Fixes + +- **Sync extras no longer flood when `agents` target overlaps** — targets that declare an extras entry called `agents` are now skipped automatically when agent sync is active, preventing duplicate file writes +- **Nested agent discovery** — `check agents` now uses the recursive discovery engine, so agents in sub-folders (e.g. `demo/code-reviewer.md`) are detected correctly +- **Doctor drift count excludes disabled agents** — agents disabled via `.agentignore` no longer count toward the drift total reported by `skillshare doctor` +- **Audit card mixes counts** — the Web UI audit card no longer mixes agent counts with finding counts, and excludes `_cross-skill` from the card total (shown separately) +- **Audit scans disabled agents too** — the audit scan walks every agent file regardless of `.agentignore` state, so hidden agents still get checked +- **List TUI tab bar clipping** — the tab bar no longer gets cut off in the split-detail layout on narrow terminals +- **Sync extras indent** — removed the stray space between the checkmark and the path in `sync` extras output; summary headers are now consistent across skills, agents, and extras +- **UI skill detail agent mode** — the detail page hides the Files section for agents (single-file resources), remembers the selected tab via localStorage, and shows the correct folder-view labels +- **Sync page layout** — the stats row and ignored-skills grouping are now easier to scan +- **Button warning variant** — the shared `Button` component now supports a `warning` variant that was already referenced by several pages +- **Check progress bar pop-in** — removed the loading progress bar that caused layout shift on the Skills page +- **Tracked repo check status** — the propagated check status is now applied to every item within the repo, not just the root +- **Target name colouring in doctor** — only the status word is coloured in `doctor` target output, not the full line + +### Breaking Changes + +- **`audit --all` flag removed** — use the positional kind filter instead: + ```bash + skillshare audit # skills (default, unchanged) + skillshare audit agents # agents only + ``` + The old `--all` flag is gone because audit now runs per kind and the Web UI has dedicated tabs + ## [0.18.9] - 2026-04-07 ### New Features diff --git a/README.md b/README.md index 0c791333..6d2a5ba9 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@
- One source of truth for AI CLI skills, rules, commands & more. Sync everywhere with one command — from personal to organization-wide.
+ One source of truth for AI CLI skills, agents, rules, commands & more. Sync everywhere with one command — from personal to organization-wide.
Codex, Claude Code, OpenClaw, OpenCode & 50+ more.
+
+
---
diff --git a/ai_docs/tests/agents_commands_runbook.md b/ai_docs/tests/agents_commands_runbook.md
new file mode 100644
index 00000000..60c19cea
--- /dev/null
+++ b/ai_docs/tests/agents_commands_runbook.md
@@ -0,0 +1,445 @@
+# CLI E2E Runbook: Agents Commands
+
+Validates all agent-related CLI commands: list, sync, status, diff,
+collect, uninstall, trash, update, backup, and doctor.
+
+**Origin**: v0.17.0 — agents support added as a new resource kind alongside skills.
+
+## Scope
+
+- Agent CRUD lifecycle (create source → sync → uninstall → trash → restore)
+- Kind filter: `agents`, `--all`, default (skills-only)
+- JSON output for all commands that support it
+- Diff and collect workflows
+- Backup and restore round-trip
+- Doctor agent checks
+- Update with no tracked agents (local-only)
+
+## Environment
+
+Run inside devcontainer via mdproof (no ssenv wrapper needed).
+All commands use `-g` to force global mode since `/workspace/.skillshare/` triggers project mode auto-detection.
+
+## Steps
+
+### 1. Setup: init global config and create agent source files
+
+```bash
+ss init -g --force --no-copy --all-targets --no-git --no-skill
+AGENTS_DIR=~/.config/skillshare/agents
+mkdir -p "$AGENTS_DIR"
+cat > "$AGENTS_DIR/tutor.md" <<'EOF'
+---
+name: tutor
+description: A tutoring agent
+---
+# Tutor Agent
+Helps with learning.
+EOF
+cat > "$AGENTS_DIR/reviewer.md" <<'EOF'
+---
+name: reviewer
+description: A code review agent
+---
+# Reviewer Agent
+Reviews code for quality.
+EOF
+cat > "$AGENTS_DIR/debugger.md" <<'EOF'
+---
+name: debugger
+description: A debugging agent
+---
+# Debugger Agent
+Helps debug issues.
+EOF
+ls "$AGENTS_DIR"
+```
+
+Expected:
+- exit_code: 0
+- tutor.md
+- reviewer.md
+- debugger.md
+
+### 2. List agents — shows source agents
+
+```bash
+ss list agents --no-tui -g
+```
+
+Expected:
+- exit_code: 0
+- tutor
+- reviewer
+- debugger
+
+### 3. List agents — JSON includes kind field
+
+```bash
+ss list agents --json -g
+```
+
+Expected:
+- exit_code: 0
+- jq: length == 3
+- jq: all(.[]; .kind == "agent")
+- jq: [.[].name] | sort | . == ["debugger","reviewer","tutor"]
+
+### 4. List default — skills only, no agents
+
+```bash
+ss list --json -g
+```
+
+Expected:
+- exit_code: 0
+- Not tutor
+- Not reviewer
+
+### 5. Sync agents — creates symlinks
+
+```bash
+ss sync agents -g
+```
+
+Expected:
+- exit_code: 0
+- regex: linked|synced
+
+Verify:
+
+```bash
+CLAUDE_AGENTS=~/.claude/agents
+test -L "$CLAUDE_AGENTS/tutor.md" && echo "tutor: symlinked" || echo "tutor: MISSING"
+test -L "$CLAUDE_AGENTS/reviewer.md" && echo "reviewer: symlinked" || echo "reviewer: MISSING"
+test -L "$CLAUDE_AGENTS/debugger.md" && echo "debugger: symlinked" || echo "debugger: MISSING"
+```
+
+Expected:
+- exit_code: 0
+- tutor: symlinked
+- reviewer: symlinked
+- debugger: symlinked
+- Not MISSING
+
+### 6. Sync agents — dry-run JSON shows no errors
+
+```bash
+ss sync agents --dry-run --json -g
+```
+
+Expected:
+- exit_code: 0
+
+### 7. Sync default — does NOT sync agents to unconfigured targets
+
+```bash
+CURSOR_AGENTS=~/.cursor/agents
+rm -rf "$CURSOR_AGENTS" 2>/dev/null || true
+ss sync -g
+test -d "$CURSOR_AGENTS" && echo "cursor agents dir: EXISTS" || echo "cursor agents dir: not created"
+```
+
+Expected:
+- exit_code: 0
+- cursor agents dir: not created
+
+### 8. Sync all — syncs both skills and agents
+
+```bash
+ss sync --all -g
+```
+
+Expected:
+- exit_code: 0
+
+### 9. Status — shows skills and agents by default
+
+```bash
+ss status -g
+```
+
+Expected:
+- exit_code: 0
+- regex: [Aa]gent
+- regex: [Ss]ource
+
+### 10. Status JSON — includes agents by default
+
+```bash
+ss status --json -g
+```
+
+Expected:
+- exit_code: 0
+- jq: .agents.exists == true
+- jq: .agents.count == 3
+
+### 12. Diff agents — no drift after sync
+
+```bash
+ss diff agents --no-tui -g
+```
+
+Expected:
+- exit_code: 0
+
+### 13. Diff agents — JSON output
+
+```bash
+ss diff agents --json -g
+```
+
+Expected:
+- exit_code: 0
+
+### 14. Collect agents — no local agents to collect
+
+```bash
+ss collect agents --force -g
+```
+
+Expected:
+- exit_code: 0
+- regex: [Nn]o local agents
+
+### 15. Collect agents — collects a local agent file
+
+```bash
+CLAUDE_AGENTS=~/.claude/agents
+mkdir -p "$CLAUDE_AGENTS"
+rm -f "$CLAUDE_AGENTS/local-agent.md"
+cat > "$CLAUDE_AGENTS/local-agent.md" <<'EOF'
+---
+name: local-agent
+description: A locally created agent
+---
+# Local Agent
+Created directly in target.
+EOF
+ss collect agents --force -g
+```
+
+Expected:
+- exit_code: 0
+- regex: [Cc]ollected
+
+Verify:
+
+```bash
+AGENTS_DIR=~/.config/skillshare/agents
+test -f "$AGENTS_DIR/local-agent.md" && echo "local-agent: collected to source" || echo "local-agent: NOT IN SOURCE"
+```
+
+Expected:
+- exit_code: 0
+- local-agent: collected to source
+- Not NOT IN SOURCE
+
+### 16. Uninstall agents — force remove single agent
+
+```bash
+ss uninstall agents local-agent --force -g
+```
+
+Expected:
+- exit_code: 0
+- regex: [Rr]emov|local-agent
+
+### 17. Verify agent was removed by JSON uninstall (step 16)
+
+```bash
+AGENTS_DIR=~/.config/skillshare/agents
+test -f "$AGENTS_DIR/local-agent.md" && echo "FAIL: still exists" || echo "local-agent: removed"
+```
+
+Expected:
+- exit_code: 0
+- local-agent: removed
+- Not FAIL
+
+### 18. Trash agents — list shows uninstalled agent
+
+```bash
+ss trash agents list --no-tui -g
+```
+
+Expected:
+- exit_code: 0
+- local-agent
+
+### 19. Trash agents — restore from trash
+
+```bash
+ss trash agents restore local-agent -g
+```
+
+Expected:
+- exit_code: 0
+- regex: [Rr]estor
+
+Verify:
+
+```bash
+AGENTS_DIR=~/.config/skillshare/agents
+test -f "$AGENTS_DIR/local-agent.md" && echo "local-agent: restored" || echo "FAIL: not restored"
+```
+
+Expected:
+- exit_code: 0
+- local-agent: restored
+- Not FAIL
+
+### 20. Uninstall agents --all
+
+```bash
+ss uninstall agents --all --force -g
+```
+
+Expected:
+- exit_code: 0
+- regex: [Rr]emov|[Uu]ninstall
+
+Verify:
+
+```bash
+AGENTS_DIR=~/.config/skillshare/agents
+COUNT=$(ls "$AGENTS_DIR"/*.md 2>/dev/null | wc -l | tr -d ' ')
+echo "Remaining agents: $COUNT (expected: 0)"
+```
+
+Expected:
+- exit_code: 0
+- Remaining agents: 0 (expected: 0)
+
+### 21. Uninstall agents — validation errors
+
+```bash
+ss uninstall agents -g 2>&1 || true
+```
+
+Expected:
+- regex: name|--all|required|specify
+
+### 22. Sync agents after uninstall — targets cleaned
+
+```bash
+ss sync agents -g
+CLAUDE_AGENTS=~/.claude/agents
+COUNT=$(ls "$CLAUDE_AGENTS"/*.md 2>/dev/null | wc -l | tr -d ' ')
+echo "Remaining symlinks: $COUNT"
+```
+
+Expected:
+- exit_code: 0
+- regex: prun|[Nn]o agents
+
+### 23. Update agents — no agents found
+
+```bash
+ss update agents --all -g
+```
+
+Expected:
+- regex: [Nn]o agents|[Nn]o project agents
+
+### 24. Re-create agents and test update — local only
+
+```bash
+AGENTS_DIR=~/.config/skillshare/agents
+mkdir -p "$AGENTS_DIR"
+cat > "$AGENTS_DIR/helper.md" <<'EOF'
+---
+name: helper
+description: A helper agent
+---
+# Helper
+EOF
+ss update agents --all -g
+```
+
+Expected:
+- regex: local|no tracked|up to date|[Nn]o agents
+
+### 25. Update agents — --group not supported
+
+```bash
+ss update agents --group mygroup -g 2>&1 || true
+```
+
+Expected:
+- regex: not supported|--group
+
+### 26. Backup agents
+
+```bash
+ss sync agents -g
+ss backup agents -g
+```
+
+Expected:
+- exit_code: 0
+- regex: [Bb]ackup|created|nothing
+
+### 27. Backup agents — list shows backup entries
+
+```bash
+ss backup --list -g
+```
+
+Expected:
+- exit_code: 0
+
+### 28. Doctor — includes agent checks
+
+```bash
+ss doctor -g
+```
+
+Expected:
+- exit_code: 0
+- regex: [Aa]gent
+
+### 29. List all — shows both skills and agents
+
+```bash
+ss list --all --json -g
+```
+
+Expected:
+- exit_code: 0
+- jq: map(select(.kind == "agent")) | length > 0
+
+### 30. Cleanup remaining agents
+
+```bash
+ss uninstall agents --all --force -g 2>/dev/null || true
+ss sync agents -g 2>/dev/null || true
+```
+
+Expected:
+- exit_code: 0
+
+## Pass Criteria
+
+- [ ] `list agents` shows only agents, not skills
+- [ ] `list agents --json` includes `kind: "agent"` for all entries
+- [ ] Default `list` (no kind) excludes agents
+- [ ] `sync agents` creates symlinks in agent target directories
+- [ ] `sync agents --dry-run` makes no changes
+- [ ] Default `sync` does NOT sync agents
+- [ ] `sync all` syncs both skills and agents
+- [ ] `status` shows both skills and agents by default
+- [ ] `status --json` includes agents section
+- [ ] `diff agents` shows drift status
+- [ ] `collect agents` collects local agent files to source
+- [ ] `uninstall agents Only applies to single skill install
+Only applies to single resource install
- Track keeps the git repo linked for updates · Force overwrites existing skills · Skip audit bypasses security scan + Track keeps the git repo linked for updates · Force overwrites existing resources · Skip audit bypasses security scan
@@ -505,6 +551,55 @@ export default function InstallForm({ /> ); + const kindSelectorDialog = ( ++ This repository contains both skills and agents. What would you like to install? +
+{source} @@ -101,7 +110,7 @@ export default function SkillPickerModal({ /> setFilter(e.target.value)} className="!pl-8 !py-1.5 !text-sm font-mono" @@ -119,7 +128,7 @@ export default function SkillPickerModal({ /> {filter && ( - {filtered.length} of {skills.length} skills + {filtered.length} of {skills.length} {pluralLabel} )}