Skip to content

feat(skills): add skill-stocktake skill#265

Merged
affaan-m merged 1 commit into
affaan-m:mainfrom
shimo4228:feat/skills/skill-stocktake
Feb 23, 2026
Merged

feat(skills): add skill-stocktake skill#265
affaan-m merged 1 commit into
affaan-m:mainfrom
shimo4228:feat/skills/skill-stocktake

Conversation

@shimo4228

@shimo4228 shimo4228 commented Feb 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds skill-stocktake, a slash command that audits all Claude skills and commands for quality using a checklist + AI holistic judgment. Supports two modes: Quick Scan (re-evaluates only changed skills, ~5–10 min) and Full Stocktake (complete review, ~20–30 min).

Type

  • Skill / [ ] Command / [ ] Agent / [ ] Hook

Structure

skills/skill-stocktake/
  SKILL.md              # Slash command definition
  scripts/
    scan.sh             # Enumerate skill files, extract frontmatter + UTC mtime
    quick-diff.sh       # Compare mtimes against results.json evaluated_at
    save-results.sh     # Persist evaluation results to results.json

Key Features

  • Quick Scan: reads results.json cache, emits only changed/new files (mtime-based), re-evaluates only those
  • Full Stocktake: full inventory via scan.sh, batched subagent evaluation (~20 skills per batch)
  • Auto project detection: detects {cwd}/.claude/skills/ automatically — works from any project directory
  • Results cache: ~/.claude/skills/skill-stocktake/results.json with resume support for interrupted runs

Testing

  • Tested locally with Claude Code CLI across multiple projects
  • Shell scripts covered by bats-core tests (17 tests passing)
  • Bug fix included: unregistered skills with old mtimes are now correctly detected as is_new: true

Checklist

  • Follows file naming conventions
  • Includes required sections (Scope, Modes, Workflow, Results File Schema)
  • No hardcoded paths or personal information
  • English only

Summary by CodeRabbit

  • Documentation

    • Added comprehensive guide for the skill-stocktake command, covering Quick Scan and Full Stocktake modes, evaluation phases, verdicts, and result file schema.
  • New Features

    • Added scripts to scan skill directories, detect changes since last evaluation, and persist evaluation results to storage.

@coderabbitai

coderabbitai Bot commented Feb 21, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Added skill-stocktake feature documentation and supporting shell scripts. SKILL.md defines operational modes (Quick Scan/Full Stocktake), evaluation phases, verdicts, and results schema. Three scripts handle scanning skills directories, detecting changes via modification times, and persisting evaluation results to JSON.

Changes

Cohort / File(s) Summary
Documentation
skills/skill-stocktake/SKILL.md
Comprehensive documentation of skill-stocktake command with operational modes, evaluation phases, verdict types, results schema, and example JSON structures.
Scanning & Processing Scripts
skills/skill-stocktake/scripts/scan.sh, skills/skill-stocktake/scripts/quick-diff.sh, skills/skill-stocktake/scripts/save-results.sh
Three complementary shell scripts: scan.sh enumerates skills and extracts metadata; quick-diff.sh compares against previous results to detect changes; save-results.sh merges and persists evaluation outcomes to results.json.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant scan.sh
    participant quick-diff.sh
    participant results.json
    participant save-results.sh

    User->>scan.sh: Trigger skill scan
    scan.sh->>scan.sh: Extract metadata & usage counts
    scan.sh-->>User: Output skills JSON

    User->>quick-diff.sh: Compare against results.json
    quick-diff.sh->>results.json: Read evaluated_at & known paths
    quick-diff.sh->>quick-diff.sh: Compute mtimes, detect changes
    quick-diff.sh-->>User: Output changes array

    User->>save-results.sh: Persist evaluation results
    save-results.sh->>results.json: Read existing skills (if present)
    save-results.sh->>save-results.sh: Merge new skills with existing
    save-results.sh->>results.json: Write merged results + timestamp
    save-results.sh-->>User: Confirm save
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • affaan-m

Poem

🐰 A stocktake quest, from scripts so spry,
Scanning skills both far and nigh,
Diffs detect what's changed with care,
Results persist with timestamps fair,
Inventory blooms with rabbit flair! 🌿

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(skills): add skill-stocktake skill' clearly summarizes the main change: introducing a new skill-stocktake feature to the skills directory with comprehensive documentation and supporting scripts.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
skills/skill-stocktake/scripts/save-results.sh (1)

31-36: Bootstrap write is not atomic — inconsistent with the merge path.

The merge path (line 43+) correctly uses mktemp + mv for atomicity, but the bootstrap path writes directly to $RESULTS_JSON. A jq failure or interrupted pipe on line 34 leaves a partially-written file that subsequent runs will treat as valid JSON.

🛡️ Apply the same mktemp+mv pattern to bootstrap
 if [[ ! -f "$RESULTS_JSON" ]]; then
   # Bootstrap: create new results.json from stdin JSON + current UTC timestamp
-  echo "$input_json" | jq --arg ea "$EVALUATED_AT" \
-    '. + { evaluated_at: $ea }' > "$RESULTS_JSON"
+  tmp=$(mktemp "${RESULTS_JSON}.XXXXXX")
+  trap 'rm -f "$tmp"' EXIT
+  echo "$input_json" | jq --arg ea "$EVALUATED_AT" \
+    '. + { evaluated_at: $ea }' > "$tmp" && mv "$tmp" "$RESULTS_JSON"
   exit 0
 fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/skill-stocktake/scripts/save-results.sh` around lines 31 - 36, The
bootstrap branch currently writes echo "$input_json" | jq ... > "$RESULTS_JSON"
directly, which is non-atomic; change it to write to a secure temporary file
(using mktemp) and then mv the temp into RESULTS_JSON after jq succeeds to
mirror the merge path's atomic mktemp+mv pattern; ensure you use the same
variables (input_json, EVALUATED_AT) and check jq's exit status before moving so
you never replace RESULTS_JSON with a partial file.
skills/skill-stocktake/scripts/scan.sh (1)

57-66: Dead code: count_obs is never called.

The function is fully superseded by the pre-aggregation approach introduced in scan_dir_to_json (lines 89–95). Safe to delete.

🗑️ Remove dead `count_obs` function
-# Count observations matching a file path since a cutoff timestamp
-count_obs() {
-  local file="$1" cutoff="$2"
-  if [[ ! -f "$OBSERVATIONS" ]]; then
-    echo 0
-    return
-  fi
-  jq -r --arg p "$file" --arg c "$cutoff" \
-    'select(.tool=="Read" and .path==$p and .timestamp>=$c) | 1' \
-    "$OBSERVATIONS" 2>/dev/null | wc -l | tr -d ' '
-}
-
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/skill-stocktake/scripts/scan.sh` around lines 57 - 66, Delete the dead
helper function count_obs since it's never invoked and has been superseded by
the pre-aggregation logic in scan_dir_to_json; remove the entire count_obs block
(which references OBSERVATIONS and accepts file/cutoff args) to avoid dead code
and confusion, ensuring no other code calls count_obs before committing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@skills/skill-stocktake/scripts/scan.sh`:
- Around line 103-108: The awk comparisons for u7 and u30 fail when file paths
contain spaces because they compare $2; change the awk invocation that reads
obs_7d_counts and obs_30d_counts to strip the leading count field and leading
whitespace and then compare the remaining line ($0 after removal) to the shell
variable file (i.e., instead of '$2 == f' perform something like reset $1 and
trim leading spaces and then compare $0 == f), so u7 and u30 correctly capture
counts for paths with spaces.

In `@skills/skill-stocktake/SKILL.md`:
- Around line 1-175: Add the three required markdown sections ("When to Use",
"How It Works", and "Examples") to SKILL.md with concise usage instructions and
at least one example invocation (e.g., quick scan and force-full invocation),
and also add a top-level frontmatter `name` field so scan.sh will populate the
skill's name in results.json; update the document's frontmatter (the YAML block
at top) to include `name: "skill-stocktake"` and insert brief content under the
three required headings describing triggers, high-level flow (Phase 1–4 summary)
and a short example showing `/skill-stocktake` and `/skill-stocktake full`.

---

Nitpick comments:
In `@skills/skill-stocktake/scripts/save-results.sh`:
- Around line 31-36: The bootstrap branch currently writes echo "$input_json" |
jq ... > "$RESULTS_JSON" directly, which is non-atomic; change it to write to a
secure temporary file (using mktemp) and then mv the temp into RESULTS_JSON
after jq succeeds to mirror the merge path's atomic mktemp+mv pattern; ensure
you use the same variables (input_json, EVALUATED_AT) and check jq's exit status
before moving so you never replace RESULTS_JSON with a partial file.

In `@skills/skill-stocktake/scripts/scan.sh`:
- Around line 57-66: Delete the dead helper function count_obs since it's never
invoked and has been superseded by the pre-aggregation logic in
scan_dir_to_json; remove the entire count_obs block (which references
OBSERVATIONS and accepts file/cutoff args) to avoid dead code and confusion,
ensuring no other code calls count_obs before committing.

Comment on lines +103 to +108
# Use awk exact field match to avoid substring false-positives from grep -F.
# uniq -c output format: " N /path/to/file" — path is always field 2.
u7=$(echo "$obs_7d_counts" | awk -v f="$file" '$2 == f {print $1}' | head -1)
u7="${u7:-0}"
u30=$(echo "$obs_30d_counts" | awk -v f="$file" '$2 == f {print $1}' | head -1)
u30="${u30:-0}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

awk '$2 == f' silently returns 0 for file paths that contain spaces.

uniq -c emits lines as N /path/to/file. Awk's default field splitting is whitespace, so $2 captures only the first space-delimited token of the path. Any skill living under a directory with a space in its name (e.g., ~/my projects/.claude/skills/) is never matched, yielding a false use_7d/use_30d of 0.

Strip the leading count before comparing $0:

🐛 Proposed fix for space-safe path matching
-    u7=$(echo "$obs_7d_counts" | awk -v f="$file" '$2 == f {print $1}' | head -1)
+    u7=$(echo "$obs_7d_counts" | awk -v f="$file" '{c=$1; sub(/^ *[0-9]+ /, ""); if ($0==f) {print c; exit}}' | head -1)
     u7="${u7:-0}"
-    u30=$(echo "$obs_30d_counts" | awk -v f="$file" '$2 == f {print $1}' | head -1)
+    u30=$(echo "$obs_30d_counts" | awk -v f="$file" '{c=$1; sub(/^ *[0-9]+ /, ""); if ($0==f) {print c; exit}}' | head -1)
     u30="${u30:-0}"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Use awk exact field match to avoid substring false-positives from grep -F.
# uniq -c output format: " N /path/to/file" — path is always field 2.
u7=$(echo "$obs_7d_counts" | awk -v f="$file" '$2 == f {print $1}' | head -1)
u7="${u7:-0}"
u30=$(echo "$obs_30d_counts" | awk -v f="$file" '$2 == f {print $1}' | head -1)
u30="${u30:-0}"
# Use awk exact field match to avoid substring false-positives from grep -F.
# uniq -c output format: " N /path/to/file" — path is always field 2.
u7=$(echo "$obs_7d_counts" | awk -v f="$file" '{c=$1; sub(/^ *[0-9]+ /, ""); if ($0==f) {print c; exit}}' | head -1)
u7="${u7:-0}"
u30=$(echo "$obs_30d_counts" | awk -v f="$file" '{c=$1; sub(/^ *[0-9]+ /, ""); if ($0==f) {print c; exit}}' | head -1)
u30="${u30:-0}"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/skill-stocktake/scripts/scan.sh` around lines 103 - 108, The awk
comparisons for u7 and u30 fail when file paths contain spaces because they
compare $2; change the awk invocation that reads obs_7d_counts and
obs_30d_counts to strip the leading count field and leading whitespace and then
compare the remaining line ($0 after removal) to the shell variable file (i.e.,
instead of '$2 == f' perform something like reset $1 and trim leading spaces and
then compare $0 == f), so u7 and u30 correctly capture counts for paths with
spaces.

Comment on lines +1 to +175
---
description: "Use when auditing Claude skills and commands for quality. Supports Quick Scan (changed skills only) and Full Stocktake modes with sequential subagent batch evaluation."
---

# skill-stocktake

Slash command (`/skill-stocktake`) that audits all Claude skills and commands using a quality checklist + AI holistic judgment. Supports two modes: Quick Scan for recently changed skills, and Full Stocktake for a complete review.

## Scope

The command targets the following paths **relative to the directory where it is invoked**:

| Path | Description |
|------|-------------|
| `~/.claude/skills/` | Global skills (all projects) |
| `{cwd}/.claude/skills/` | Project-level skills (if the directory exists) |

**At the start of Phase 1, the command explicitly lists which paths were found and scanned.**

### Targeting a specific project

To include project-level skills, run from that project's root directory:

```bash
cd ~/path/to/my-project
/skill-stocktake
```

If the project has no `.claude/skills/` directory, only global skills and commands are evaluated.

## Modes

| Mode | Trigger | Duration |
|------|---------|---------|
| Quick Scan | `results.json` exists (default) | 5–10 min |
| Full Stocktake | `results.json` absent, or `/skill-stocktake full` | 20–30 min |

**Results cache:** `~/.claude/skills/skill-stocktake/results.json`

## Quick Scan Flow

Re-evaluate only skills that have changed since the last run (5–10 min).

1. Read `~/.claude/skills/skill-stocktake/results.json`
2. Run: `bash ~/.claude/skills/skill-stocktake/scripts/quick-diff.sh \
~/.claude/skills/skill-stocktake/results.json`
(Project dir is auto-detected from `$PWD/.claude/skills`; pass it explicitly only if needed)
3. If output is `[]`: report "No changes since last run." and stop
4. Re-evaluate only those changed files using the same Phase 2 criteria
5. Carry forward unchanged skills from previous results
6. Output only the diff
7. Run: `bash ~/.claude/skills/skill-stocktake/scripts/save-results.sh \
~/.claude/skills/skill-stocktake/results.json <<< "$EVAL_RESULTS"`

## Full Stocktake Flow

### Phase 1 — Inventory

Run: `bash ~/.claude/skills/skill-stocktake/scripts/scan.sh`

The script enumerates skill files, extracts frontmatter, and collects UTC mtimes.
Project dir is auto-detected from `$PWD/.claude/skills`; pass it explicitly only if needed.
Present the scan summary and inventory table from the script output:

```
Scanning:
✓ ~/.claude/skills/ (17 files)
✗ {cwd}/.claude/skills/ (not found — global skills only)
```

| Skill | 7d use | 30d use | Description |
|-------|--------|---------|-------------|

### Phase 2 — Quality Evaluation

Launch a Task tool subagent (**Explore agent, model: opus**) with the full inventory and checklist.
The subagent reads each skill, applies the checklist, and returns per-skill JSON:

`{ "verdict": "Keep"|"Improve"|"Update"|"Retire"|"Merge into [X]", "reason": "..." }`

**Chunk guidance:** Process ~20 skills per subagent invocation to keep context manageable. Save intermediate results to `results.json` (`status: "in_progress"`) after each chunk.

After all skills are evaluated: set `status: "completed"`, proceed to Phase 3.

**Resume detection:** If `status: "in_progress"` is found on startup, resume from the first unevaluated skill.

Each skill is evaluated against this checklist:

```
- [ ] Content overlap with other skills checked
- [ ] Overlap with MEMORY.md / CLAUDE.md checked
- [ ] Freshness of technical references verified (use WebSearch if tool names / CLI flags / APIs are present)
- [ ] Usage frequency considered
```

Verdict criteria:

| Verdict | Meaning |
|---------|---------|
| Keep | Useful and current |
| Improve | Worth keeping, but specific improvements needed |
| Update | Referenced technology is outdated (verify with WebSearch) |
| Retire | Low quality, stale, or cost-asymmetric |
| Merge into [X] | Substantial overlap with another skill; name the merge target |

Evaluation is **holistic AI judgment** — not a numeric rubric. Guiding dimensions:
- **Actionability**: code examples, commands, or steps that let you act immediately
- **Scope fit**: name, trigger, and content are aligned; not too broad or narrow
- **Uniqueness**: value not replaceable by MEMORY.md / CLAUDE.md / another skill
- **Currency**: technical references work in the current environment

**Reason quality requirements** — the `reason` field must be self-contained and decision-enabling:
- Do NOT write "unchanged" alone — always restate the core evidence
- For **Retire**: state (1) what specific defect was found, (2) what covers the same need instead
- Bad: `"Superseded"`
- Good: `"disable-model-invocation: true already set; superseded by continuous-learning-v2 which covers all the same patterns plus confidence scoring. No unique content remains."`
- For **Merge**: name the target and describe what content to integrate
- Bad: `"Overlaps with X"`
- Good: `"42-line thin content; Step 4 of chatlog-to-article already covers the same workflow. Integrate the 'article angle' tip as a note in that skill."`
- For **Improve**: describe the specific change needed (what section, what action, target size if relevant)
- Bad: `"Too long"`
- Good: `"276 lines; Section 'Framework Comparison' (L80–140) duplicates ai-era-architecture-principles; delete it to reach ~150 lines."`
- For **Keep** (mtime-only change in Quick Scan): restate the original verdict rationale, do not write "unchanged"
- Bad: `"Unchanged"`
- Good: `"mtime updated but content unchanged. Unique Python reference explicitly imported by rules/python/; no overlap found."`

### Phase 3 — Summary Table

| Skill | 7d use | Verdict | Reason |
|-------|--------|---------|--------|

### Phase 4 — Consolidation

1. **Retire / Merge**: present detailed justification per file before confirming with user:
- What specific problem was found (overlap, staleness, broken references, etc.)
- What alternative covers the same functionality (for Retire: which existing skill/rule; for Merge: the target file and what content to integrate)
- Impact of removal (any dependent skills, MEMORY.md references, or workflows affected)
2. **Improve**: present specific improvement suggestions with rationale:
- What to change and why (e.g., "trim 430→200 lines because sections X/Y duplicate python-patterns")
- User decides whether to act
3. **Update**: present updated content with sources checked
4. Check MEMORY.md line count; propose compression if >100 lines

## Results File Schema

`~/.claude/skills/skill-stocktake/results.json`:

**`evaluated_at`**: Must be set to the actual UTC time of evaluation completion.
Obtain via Bash: `date -u +%Y-%m-%dT%H:%M:%SZ`. Never use a date-only approximation like `T00:00:00Z`.

```json
{
"evaluated_at": "2026-02-21T10:00:00Z",
"mode": "full",
"batch_progress": {
"total": 80,
"evaluated": 80,
"status": "completed"
},
"skills": {
"skill-name": {
"path": "~/.claude/skills/skill-name/SKILL.md",
"verdict": "Keep",
"reason": "Concrete, actionable, unique value for X workflow",
"mtime": "2026-01-15T08:30:00Z"
}
}
}
```

## Notes

- Evaluation is blind: the same checklist applies to all skills regardless of origin (ECC, self-authored, auto-extracted)
- Archive / delete operations always require explicit user confirmation
- No verdict branching by skill origin

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Missing required sections: When to Use, How It Works, and Examples.

The file has thorough content but does not include the three sections mandated for all skills/**/*.md files. As per coding guidelines and retrieved learnings: "Skills should be formatted as Markdown with clear sections for When to Use, How It Works, and Examples."

A minimal addition would be:

+## When to Use
+
+- Run a quick quality audit after editing or adding skills (`/skill-stocktake`)
+- Run a full audit when onboarding a new project or after bulk skill imports (`/skill-stocktake full`)
+- Periodically check for stale technical references or duplicate coverage
+
+## How It Works
+
+1. **Quick Scan** — `quick-diff.sh` detects skills changed since the last run; only those are re-evaluated.
+2. **Full Stocktake** — `scan.sh` inventories all skill directories; a subagent evaluates each skill against the checklist in batches of ~20.
+3. Results are merged atomically into `~/.claude/skills/skill-stocktake/results.json` via `save-results.sh`.
+
+## Examples
+
+```bash
+# Quick scan from any project root (default when results.json exists)
+/skill-stocktake
+
+# Force a full stocktake
+/skill-stocktake full
+```
+

Additionally, scan.sh extracts the name frontmatter field for each skill file. This file has no name field in its frontmatter, so its own entry in the stocktake results will have an empty name.

Based on coding guidelines: skills/**/*.md"Skills should be formatted as Markdown with clear sections for When to Use, How It Works, and Examples", and retrieved learnings: "Skills should be formatted as Markdown with clear sections for When to Use, How It Works, and Examples."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/skill-stocktake/SKILL.md` around lines 1 - 175, Add the three required
markdown sections ("When to Use", "How It Works", and "Examples") to SKILL.md
with concise usage instructions and at least one example invocation (e.g., quick
scan and force-full invocation), and also add a top-level frontmatter `name`
field so scan.sh will populate the skill's name in results.json; update the
document's frontmatter (the YAML block at top) to include `name:
"skill-stocktake"` and insert brief content under the three required headings
describing triggers, high-level flow (Phase 1–4 summary) and a short example
showing `/skill-stocktake` and `/skill-stocktake full`.

@affaan-m affaan-m merged commit 3c833d8 into affaan-m:main Feb 23, 2026
38 checks passed
FrancescoRosciano pushed a commit to FRosciano-Mambo/everything-claude-code that referenced this pull request Jun 1, 2026
…ktake

feat(skills): add skill-stocktake skill
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants