# Skills Manager

> >-

- **Type:** Skill
- **Install:** `agentstack add skill-efanwang-skills-manager-skills-manager`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [EfanWang](https://agentstack.voostack.com/s/efanwang)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [EfanWang](https://github.com/EfanWang)
- **Source:** https://github.com/EfanWang/skills-manager

## Install

```sh
agentstack add skill-efanwang-skills-manager-skills-manager
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Skills Manager

Manage the **sibling skills** of this directory for one installed agent at a
time. A single skills-manager instance scans only its own parent directory,
whether it is installed under a Claude Code, Cursor, or Codex CLI skills root.
It does not aggregate skills across Claude Code, Cursor, and Codex CLI. Install
a separate copy in each agent's skills root when the user wants separate
per-agent inventories.

Supported installation roots include:
- Claude Code: `~/.claude/skills/` or project `.claude/skills/`
- Cursor: `~/.cursor/skills/`, project `.cursor/skills/`, or a shared project
  skills directory the Cursor agent discovers
- Codex CLI: `~/.codex/skills/`, `$HOME/.agents/skills/`, or project
  `.agents/skills/`

## Mental model

- Each skill is a sibling directory under `/` containing a
  `SKILL.md`.
- `sources.json` (in this directory) records where each remote skill came from.
  Without a record, a skill is **unclaimed** — we don't know its origin.
- A skill is one of three types:
  - **remote** — has a `url` in `sources.json`; can be checked / updated.
  - **local** — has an empty `{}` record; user marked it as private; skip checks.
  - **unclaimed** — no record at all; user hasn't told us where it came from.

## When to trigger (and what to run)

### Scenario A: list skills (and show update status)

Triggers: "list my skills", "what skills do I have", "show skills", "which
skills are outdated", "check for updates".

Interpret "all my skills" / "我的所有 skill" as all sibling skill directories
under this skills-manager installation's parent directory. Do not expand that
to every programming tool's skills (Claude Code, Cursor, Codex CLI, etc.)
unless the user explicitly asks for a cross-tool inventory.

Run full inventory by default. This keeps the list command ergonomic: if the
first cheap scan finds unclaimed skills, inventory runs the source audit once,
writes high-confidence claims, and returns the final post-audit table data.

```powershell
python /scripts/inventory.py --check-remote --audit-unclaimed
```

If the user explicitly says "preview only", "just list, don't write", or "no
audit", use the read-only command instead:

```powershell
python /scripts/inventory.py --check-remote
```

Output shapes:
- Without `--audit-unclaimed`: JSON array of inventory entries.
- With `--audit-unclaimed`: JSON object `{ "entries": [...], "audit": {...} }`.

Parse `entries` (or the bare array for read-only output) and render as a
markdown table with exactly four semantic columns: skill name, type,
description, and status. Localize the column labels to the user's response
language instead of forcing Chinese.

Example for a Chinese response:

| Skill名称 | 类型 | 功能描述 | 状态 |
|---|---|---|---|
| brainstorming | Git | ... | 🟢 最新 / 🔴 过期 |
| my-private | 本地 | ... | 🟢 最新 |
| unknown-thing | 未知 | ... | 🟡 未知 |

Example for an English response:

| Skill name | Type | Description | Status |
|---|---|---|---|
| brainstorming | Git | ... | 🟢 Latest / 🔴 Outdated |
| my-private | Local | ... | 🟢 Latest |
| unknown-thing | Unknown | ... | 🟡 Unknown |

Output contract: always render all four semantic columns. Do not omit the
description column to make the answer shorter, even on repeat list requests,
after source-audit follow-ups, or when every skill has a known source. Use the
inventory JSON `description` field as the source of the description, but choose
whether to keep it as written, translate it, or summarize it according to the
user's response language.

Status indicator contract:
- The status column must include one of these exact visual indicators:
  `🟢` for current, `🔴` for outdated, or `🟡` for unknown.
- These symbols are semantic status markers required by this skill's output
  contract, not decorative emoji. They intentionally override generic style
  rules such as "avoid emoji" for this table's status column only.
- If the runtime, terminal, or user explicitly forbids emoji display, fall back
  to text labels: `[latest]`, `[outdated]`, `[unknown]` (localized to the
  user's response language).

Display mappings:
- Type: `remote` → `Git`; `local` → localized "local"; `unclaimed` →
  localized "unknown".
- Status:
  - `up_to_date` → localized "🟢 latest"
  - `update_available` → localized "🔴 outdated"
  - `local` → localized "🟢 latest"
  - `unclaimed` / `unknown` / `error` / `null` → localized "🟡 unknown"

After the table, summarize in the user's response language, for example in
Chinese: "共 X 个，最新 Y 个，过期 Z 个，本地 W 个，未知 V 个"; or in English:
"Total X, latest Y, outdated Z, local W, unknown V." If `audit.ran` is true,
also summarize how many were auto-claimed, need review, or had no match.

If `audit.ran` is true and any report has `decision: "no_match"`,
`decision: "needs_review"`, or `decision: "git_remote_unsupported"`, do **not**
stop after explaining that the script does not run web search. Continue
immediately into Scenario E's agent web-search handoff in the same turn, unless
the user explicitly said "just list", "no audit", "preview only", "do not
search", or "no network". The intended default for list/check requests is:

```text
inventory --check-remote --audit-unclaimed
→ auto-claim .git/config / embedded GitHub URL evidence
→ agent web search for remaining unknown/needs_review skills
→ sources.py claim-remote / claim-local for clear decisions
→ final table + concise summary
```

Do not offer "要处理未识别来源吗？" as the stopping point when unresolved reports
already include `search_query_hint`; use those hints now.

If the user later asks something like "just show me the ones with updates",
re-filter the same JSON output — do **not** re-run inventory.

### Scenario B: update skill(s)

Triggers: "update X", "update all outdated", "refresh brainstorming", "pull
latest".

1. If user said "all outdated", first run inventory `--check-remote` to find
   them.
2. List the target skills back to the user for confirmation (destructive
   action).
3. For each, run:
   ```powershell
   python /scripts/update_skill.py 
   ```
4. Each successful update writes a backup under `.backup/--/`.
   Report the backup path in your summary so the user can roll back manually.

### Scenario D: install a new skill from GitHub

Triggers: "install X from ", "add this skill: ", "下载这个
skill: ", or any time the user hands over a GitHub URL and asks you to set
it up locally.

**Always go through `install_skill.py`. Do not `git clone` manually** — the
script bundles cloning, atomic placement, and `sources.json` registration so
the skill is correctly tracked from day one.

```powershell
python /scripts/install_skill.py  [--name ]
```

Accepted URL forms (the script normalizes all of these):
- `https://github.com//` (only valid if SKILL.md is at the repo root)
- `https://github.com///tree//`
- `https://github.com///blob///SKILL.md`
- `https://raw.githubusercontent.com/////SKILL.md`
- `git@github.com:/.git`

Defaults and behavior worth knowing:
- Skill name defaults to `name` in the upstream SKILL.md frontmatter. Pass
  `--name` to override (useful when names collide).
- Branch defaults to the repo's default branch (auto-detected via
  `ls-remote --symref`). Pass `--branch` if the URL omits it AND the default
  is wrong, or if the branch name contains `/`.
- If a skill with the resolved name already exists, the existing copy is moved
  to `.backup/--/` before installing. Report this path back to
  the user.
- On any failure after the swap, the script tries to roll back. If rollback
  also fails, it surfaces both error strings — pass them to the user verbatim.

Confirm with the user before installing (destructive if it overwrites an
existing skill). After success, summarize: name, source repo, installed
revision, backup path (if any).

### Scenario C: delete a skill

Triggers: "delete X", "remove X", "uninstall X".

This is destructive; do **not** automate it.

1. Confirm with the user: show the absolute path that will be moved.
2. Backup using PowerShell (same drive, atomic on Windows):
   ```powershell
   $ts = Get-Date -Format yyyyMMdd-HHmmss
   Move-Item / /.backup/-$ts
   ```
3. Remove the sources.json record (only if it had one):
   ```powershell
   python /scripts/sources.py remove 
   ```
4. Report success and the backup path.

Never edit `sources.json` directly. Always go through `sources.py`.

### Scenario E: audit unclaimed skills (batch)

Triggers: "figure out where all my skills came from", "batch claim unknown
skills", "auto-detect sources for installed skills", or proactively offer when
Scenario A surfaces several `unclaimed` rows.

This is an **agent-assisted source tracing** workflow. The scripts collect
mechanical evidence; the agent decides source identity; `sources.py` registers
confirmed sources. Open-world source search belongs to the agent because it can
use page, repository, owner, README, and search-result context to distinguish
official sources from mirrors, dotfiles, registries, and marketplace copies.

**Default invocation — DO NOT add `--dry-run` unless the user explicitly asks
for a preview.** Audit is not destructive: it only writes `sources.json` for
low-risk evidence (`.git/config` or embedded source URL).
No skill files are touched. Mistakes are reversible (`sources.py remove `
and re-run). Treat audit like `update_skill.py`, not like `install_skill.py` or
delete — no confirmation needed beforehand, just report results afterwards.

```powershell
python /scripts/audit_unclaimed.py            # collect evidence and auto-claim only trusted/explicit sources
python /scripts/audit_unclaimed.py --dry-run  # ONLY when user says "先看看不要动" / "preview only"
```

If you (agent) reflexively add `--dry-run` "to be safe", you'll surprise the
user the same way they were surprised before this note was added: the script
will dutifully report high-similarity trusted hits but write nothing.

Per skill the workflow has three gates:

1. **`.git/config` inspection** (offline). If the skill directory is itself a
   git checkout pointing at github.com, claim it with the live branch +
   `rev-parse HEAD`.
2. **SKILL.md local GitHub URL hint**. A GitHub URL written inside the local
   skill is treated as explicit evidence, then verified via raw + similarity.
3. **Agent web search**. For unresolved skills, the script emits a
   `search_query_hint`; the agent uses its own web search to find and judge the
   source, then registers confirmed results through `sources.py`.

Confidence rules:
- Trusted/explicit gates (`.git/config`, local GitHub URL hint):
  - `high` (both ratios ≥ 0.90) → auto-claim.
  - `installed_revision` is the upstream HEAD SHA **only when similarity is
    exactly 1.0**. Otherwise it is recorded as `null`, so inventory reports
    `update_available` and `update_skill.py` can re-align it.
- Agent web search:
  - The script does not run web search itself.
  - The agent **must** use its available web search capability for unresolved
    audit reports during Scenario A / Scenario E, unless the user explicitly
    opted out of search.
  - Agent search results are evidence for source identity, not automatic claims.
  - Once source identity is clear, run `sources.py claim-remote` or
    `sources.py claim-local`.

When `installed_revision` is `null`, both `inventory --check-remote` and
`check_remote.py` surface the skill as `update_available` so the user/agent
is prompted to run `update_skill.py`, which overwrites the local copy with
upstream HEAD and writes the now-correct `installed_revision`. This is the
self-healing path: claim makes a conservative record, update aligns it.

**Agent web-search handoff.** Every unresolved report carries a
`search_query_hint`
when the local description is long enough. This is a mandatory continuation
step for the agent, not a note to pass back to the user. Search with
combinations such as:

```text
 SKILL.md GitHub
"" "SKILL.md"
"" GitHub
```

When reviewing web search results:
1. Prefer official or purpose-built source repos, standard `skills//`
   paths, and repos whose owner/name clearly match the skill family.
2. Treat dotfiles, `.agents/skills`, `.claude/skills`, registry, marketplace,
   mirror, awesome-list, and ordinary product repos as likely copies.
3. Compare the candidate `SKILL.md` name, description, and opening body against
   the local skill before registering it.
4. Once source identity is clear, run `sources.py claim-remote` with the
   candidate's url/branch/subpath. If multiple candidates still look plausible,
   ask the user instead of guessing.

When many skills remain unresolved, process them in a compact batch: search the
strongest query for each skill, record clear matches immediately, and ask the
user only about ambiguous or user-authored-looking skills. Do not final-answer
with only "these need web search" when web search is available to the agent.

Output is a JSON `{summary, reports, inventory_after}` payload. Render to the
user as:
- **"已自动登记 K 个"** — show each (name → repo+subpath, source: git_dir /
  embedded_url). These were trusted/explicit matches.
- **"未识别 M 个"** — before finalizing, use `search_query_hint` with the
  agent's web search capability. Once you have a source, **agent runs
  `sources.py claim-remote`**
  (if from GitHub) or `sources.py claim-local` (if user-authored). Do not re-run
  audit expecting web search to write records.
- Then re-render the Scenario A table from `inventory_after` so the user sees
  the new state in one shot — no need to invoke `inventory.py` separately.
  (`inventory_after` is `null` when `--dry-run` is used, because sources.json
  wasn't modified and a fresh inventory would just repeat the pre-audit state.)

See the "Claim wizard" section below for the exact `sources.py claim-remote
/ claim-local` parameter syntax — it's the single source of truth for those
commands.

**Important:** the script never auto `claim-local`. If a skill matched no
upstream, surface it to the user and ask if it's their own work.

## Claim wizard (handling unclaimed skills)

Triggers: "claim unknown skills", "I want to know where my skills came from",
"set up update tracking for all skills", or proactively offer it when the user
sees many `unclaimed` rows in scenario A.

For each unclaimed skill, in order:

1. **Read its SKILL.md** (first ~60 lines). Note name + description + style.

2. **Search the web** using the current agent's available web search
   capability. Start
   with `"" SKILL.md github`, the local title, and any distinctive
   phrase from the description. Prefer GitHub source pages and official docs
   over registries, dotfiles, mirrors, and marketplace copies.

3. **Compare**. For top 1–3 candidates, fetch the raw SKILL.md using the
   current agent's web fetch tool or `curl`, save it to
   `/.tmp/-candidate.md`, then:
   ```powershell
   python /scripts/similarity.py //SKILL.md /.tmp/-candidate.md
   ```

4. **Decide** based on `confidence`:
   - `high` → tell the user: "I'm confident this is from `/`. OK
     to register?"
   - `mid` → present candidates with ratios, ask user to pick.
   - `low` → ask: "Is this skill written by you (not from GitHub)? Or do you
     remember the source URL?"

5. **Register**. Once user confirms:
   ```powershell
   # From GitHub
   python /scripts/sources.py claim-remote  \
       --url  --branch  --subpath 

   # User-authored / private
   python /scripts/sources.py claim-local 
   ```

   `claim-remote` fetches upstream SKILL.md and compares it to local before
   recording `installed_revision`:
   - byte-equal → records the real HEAD SHA returned by `git ls-remote`
   - any difference → records `installed_revision = null` and emits a
     `verify_note` explaining the similarity ratio
   Pass `--no-resolve --assume-revision ` to skip the verification
   (offline / scripted scenarios); the caller is then responsible for the
   honesty of the recorded SHA.

   A `null` revision is **not** an error: it's the honest "I know the source
   repo but not which commit your local c

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [EfanWang](https://github.com/EfanWang)
- **Source:** [EfanWang/skills-manager](https://github.com/EfanWang/skills-manager)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-efanwang-skills-manager-skills-manager
- Seller: https://agentstack.voostack.com/s/efanwang
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
