Browse Source

feat(skills): Add auto-skill - self-learning skill creation (v2.4.0)

- PostToolUse hook silently tracks tool calls per session
- Stop hook evaluates via 5 gates: 8+ mutating ops, 4+ tool type
  diversity, no existing skill loaded, per-session cooldown, toggle
- Suggests skill creation via systemMessage while context is fresh
- Agent Skills spec compliant with quality gates and duplicate detection
- Toggle with /auto-skill on/off/status (global and per-project)
- Both hooks fail silently (2>/dev/null, exit 0) - zero noise
- Inspired by Hermes Agent and Forma's auto-skill system
- Rename agentmail to pigeon (pmail) - avoids AgentMail YC collision
- Bump version to 2.4.0

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
0xDarkMatter 1 month ago
parent
commit
b1a8161a53

+ 3 - 2
.claude-plugin/plugin.json

@@ -1,7 +1,7 @@
 {
   "name": "claude-mods",
-  "version": "2.3.0",
-  "description": "Custom commands, skills, and agents for Claude Code - session continuity, 23 expert agents, 67 skills, 3 commands, 5 rules, 4 hooks, 5 output styles, modern CLI tools",
+  "version": "2.4.0",
+  "description": "Custom commands, skills, and agents for Claude Code - session continuity, 23 expert agents, 68 skills, 3 commands, 5 rules, 4 hooks, 5 output styles, modern CLI tools",
   "author": "0xDarkMatter",
   "repository": "https://github.com/0xDarkMatter/claude-mods",
   "license": "MIT",
@@ -49,6 +49,7 @@
     ],
     "skills": [
       "skills/api-design-ops",
+      "skills/auto-skill",
       "skills/pigeon",
       "skills/astro-ops",
       "skills/atomise",

+ 2 - 1
AGENTS.md

@@ -5,7 +5,7 @@
 This is **claude-mods** - a collection of custom extensions for Claude Code:
 - **23 expert agents** for specialized domains (React, Python, Go, Rust, AWS, git, etc.)
 - **3 commands** for session management (/sync, /save) and experimental features (/canvas)
-- **67 skills** for CLI tools, patterns, workflows, and development tasks
+- **68 skills** for CLI tools, patterns, workflows, and development tasks
 - **5 output styles** for response personality (Vesper, Spartan, Mentor, Executive, Pair)
 - **4 hooks** for pre-commit linting, post-edit formatting, dangerous command warnings, and pmail notifications
 - **Pigeon** inter-session messaging (`pigeon send/read/reply`) - SQLite-backed pmail at `~/.claude/pmail.db`
@@ -57,6 +57,7 @@ On "INIT:" message at session start:
 | `skills/tool-discovery/` | Find the right library for any task |
 | `hooks/README.md` | Pre/post execution hook examples |
 | `skills/pigeon/` | Inter-session pmail - send, read, reply, broadcast, search across projects |
+| `skills/auto-skill/` | Auto-detect skill-worthy workflows; Stop hook suggests after complex sessions. `/auto-skill on/off/status` to toggle |
 
 ## Quick Reference
 

File diff suppressed because it is too large
+ 7 - 3
README.md


+ 37 - 0
scripts/install.ps1

@@ -211,6 +211,43 @@ if ((Test-Path $settingsPath) -and (Select-String -Path $settingsPath -Pattern "
 Write-Host ""
 
 # =============================================================================
+# AUTO-SKILL - Global install (tracking + evaluation hooks)
+# =============================================================================
+Write-Host "Installing auto-skill..." -ForegroundColor Cyan
+
+$autoSkillDir = Join-Path $claudeDir "auto-skill"
+New-Item -ItemType Directory -Force -Path $autoSkillDir | Out-Null
+
+$scripts = @("track-tools.sh", "evaluate.sh")
+foreach ($script in $scripts) {
+    $src = Join-Path $projectRoot "skills\auto-skill\scripts\$script"
+    if (Test-Path $src) {
+        Copy-Item $src -Destination "$autoSkillDir\" -Force
+        Write-Host "  $script" -ForegroundColor Green
+    }
+}
+
+$settingsPath = Join-Path $claudeDir "settings.json"
+if ((Test-Path $settingsPath) -and (Select-String -Path $settingsPath -Pattern "auto-skill" -Quiet)) {
+    Write-Host "  Hooks already configured in settings.json" -ForegroundColor Green
+} else {
+    Write-Host ""
+    Write-Host '  To enable automatic skill suggestions, add these hooks to ~/.claude/settings.json:' -ForegroundColor Yellow
+    Write-Host ""
+    Write-Host '  "PostToolUse": [{ "matcher": "*", "hooks": [{'
+    Write-Host '    "type": "command",'
+    Write-Host '    "command": "bash \"$HOME/.claude/auto-skill/track-tools.sh\"", "timeout": 2'
+    Write-Host '  }] }],'
+    Write-Host '  "Stop": [{ "hooks": [{'
+    Write-Host '    "type": "command",'
+    Write-Host '    "command": "bash \"$HOME/.claude/auto-skill/evaluate.sh\"", "timeout": 5'
+    Write-Host '  }] }]'
+    Write-Host ""
+    Write-Host "  Without this, /auto-skill still works but won't suggest automatically." -ForegroundColor Yellow
+}
+Write-Host ""
+
+# =============================================================================
 # SUMMARY
 # =============================================================================
 Write-Host "================================================================" -ForegroundColor Cyan

+ 34 - 0
scripts/install.sh

@@ -217,6 +217,40 @@ fi
 echo ""
 
 # =============================================================================
+# AUTO-SKILL - Global install (tracking + evaluation hooks)
+# =============================================================================
+echo -e "${BLUE}Installing auto-skill...${NC}"
+
+mkdir -p "$CLAUDE_DIR/auto-skill"
+for script in track-tools.sh evaluate.sh; do
+    if [ -f "$PROJECT_ROOT/skills/auto-skill/scripts/$script" ]; then
+        cp "$PROJECT_ROOT/skills/auto-skill/scripts/$script" "$CLAUDE_DIR/auto-skill/"
+        chmod +x "$CLAUDE_DIR/auto-skill/$script"
+        echo -e "  ${GREEN}$script${NC}"
+    fi
+done
+
+# Check if hooks are already configured
+if grep -q "auto-skill" "$CLAUDE_DIR/settings.json" 2>/dev/null; then
+    echo -e "  ${GREEN}Hooks already configured in settings.json${NC}"
+else
+    echo ""
+    echo -e "  ${YELLOW}To enable automatic skill suggestions, add these hooks to ~/.claude/settings.json:${NC}"
+    echo ""
+    echo '  "PostToolUse": [{ "matcher": "*", "hooks": [{'
+    echo '    "type": "command",'
+    echo '    "command": "bash \"$HOME/.claude/auto-skill/track-tools.sh\"", "timeout": 2'
+    echo '  }] }],'
+    echo '  "Stop": [{ "hooks": [{'
+    echo '    "type": "command",'
+    echo '    "command": "bash \"$HOME/.claude/auto-skill/evaluate.sh\"", "timeout": 5'
+    echo '  }] }]'
+    echo ""
+    echo -e "  ${YELLOW}Without this, /auto-skill still works but won't suggest automatically.${NC}"
+fi
+echo ""
+
+# =============================================================================
 # SUMMARY
 # =============================================================================
 echo -e "${BLUE}════════════════════════════════════════════════════════════════${NC}"

+ 250 - 0
skills/auto-skill/SKILL.md

@@ -0,0 +1,250 @@
+---
+name: auto-skill
+description: "Evaluate current session for skill-worthy workflows and create reusable skills. Triggers on: auto-skill, create skill from session, save workflow, capture this as a skill."
+license: MIT
+allowed-tools: "Read Glob Grep Bash Write Edit"
+metadata:
+  author: claude-mods
+  depends-on: "skill-creator"
+  related-skills: "skill-creator, introspect"
+---
+
+# Auto-Skill
+
+Evaluate the current session and create a reusable skill from complex workflows. Enforces the [Agent Skills specification](https://agentskills.io/specification) and quality gates.
+
+## When This Triggers
+
+- User runs `/auto-skill`
+- Stop hook suggests it after a complex session (8+ mutating ops across 4+ tool types)
+- User says "save this as a skill", "capture this workflow", etc.
+
+## Command Router
+
+Parse arguments after `auto-skill` (or `/auto-skill`):
+
+| User says | Action |
+|-----------|--------|
+| `auto-skill` (no args) | Run the full evaluation procedure below |
+| `auto-skill off` | Disable globally: `touch ~/.claude/auto-skill.disable` and confirm |
+| `auto-skill on` | Enable globally: `rm -f ~/.claude/auto-skill.disable` and confirm |
+| `auto-skill off --project` | Disable for this project: `mkdir -p .claude && touch .claude/auto-skill.disable` |
+| `auto-skill on --project` | Enable for this project: `rm -f .claude/auto-skill.disable` |
+| `auto-skill status` | Show current state (see Status section below) |
+
+### Status
+
+When the user runs `auto-skill status`, check and report:
+
+```bash
+# Global toggle
+[ -f "$HOME/.claude/auto-skill.disable" ] && echo "Global: OFF" || echo "Global: ON"
+
+# Project toggle
+[ -f ".claude/auto-skill.disable" ] && echo "Project: OFF" || echo "Project: ON"
+
+# Hook scripts installed?
+[ -x "$HOME/.claude/auto-skill/track-tools.sh" ] && echo "Hooks: installed" || echo "Hooks: not installed"
+
+# Active session tracking?
+ls /tmp/claude_autoskill_* 2>/dev/null | head -1 && echo "Tracking: active" || echo "Tracking: idle"
+```
+
+Report results in a brief table.
+
+## Procedure
+
+### Step 1: Evaluate the Session
+
+Review the conversation history in the current session. Ask yourself:
+
+1. **Was this a multi-step workflow?** (3+ distinct actions, not just read/search)
+2. **Is it reusable?** Would someone do this again - in this project or another?
+3. **Is it novel?** Does an existing skill already cover this? Check with:
+   ```bash
+   ls ~/.claude/skills/ 2>/dev/null; ls .claude/skills/ 2>/dev/null
+   ```
+4. **Is it teachable?** Can it be described as a clear procedure with steps?
+
+If ANY answer is no, tell the user: "This session doesn't look like a good skill candidate" and explain which criterion failed. **Stop here.**
+
+### Step 2: Duplicate Detection
+
+Before creating, check for overlapping skills:
+
+```bash
+# List existing skill names and descriptions
+for f in ~/.claude/skills/*/SKILL.md .claude/skills/*/SKILL.md 2>/dev/null; do
+  [ -f "$f" ] || continue
+  name=$(head -10 "$f" | grep '^name:' | sed 's/name: *//')
+  desc=$(head -10 "$f" | grep '^description:' | sed 's/description: *//' | tr -d '"')
+  echo "$name: $desc"
+done
+```
+
+**Block if:**
+- Exact name match exists
+- 60%+ word overlap in proposed name vs existing name
+- 50%+ word overlap in proposed description vs existing description
+
+If overlap detected, suggest extending the existing skill instead.
+
+### Step 3: Draft the Skill
+
+Propose a skill to the user with:
+
+| Field | Value |
+|-------|-------|
+| **Name** | kebab-case, descriptive, matches what it does |
+| **Description** | 1-2 sentences with trigger keywords |
+| **Procedure** | Numbered steps extracted from the session workflow |
+| **Tools needed** | Which tools the skill requires |
+
+Ask the user to confirm or adjust before creating.
+
+### Step 4: Quality Gates
+
+Before writing, validate:
+
+| Gate | Requirement | Why |
+|------|-------------|-----|
+| **Name format** | `^[a-z][a-z0-9-]*$`, 1-64 chars | Agent Skills spec |
+| **Description** | Non-empty, 1-1024 chars, includes trigger phrases | Spec + discovery |
+| **Procedure** | Must contain numbered steps, `## Procedure`/`## Steps`, or checkboxes | Ensures actionable content |
+| **Min content** | 200+ characters in body (after frontmatter) | Rejects trivial stubs |
+| **License** | `license: MIT` | claude-mods convention |
+| **Metadata** | `metadata.author: claude-mods` | claude-mods convention |
+| **No non-standard top-level keys** | Only `name`, `description`, `license`, `compatibility`, `allowed-tools`, `metadata` | Agent Skills spec |
+
+If any gate fails, explain which one and help the user fix it.
+
+### Step 5: Create the Skill
+
+Write the skill to the project's skill directory:
+
+```
+.claude/skills/<skill-name>/
+  SKILL.md
+  scripts/.gitkeep
+  references/.gitkeep
+  assets/.gitkeep
+```
+
+**SKILL.md frontmatter template** (Agent Skills spec compliant):
+
+```yaml
+---
+name: <kebab-case-name>
+description: "<what it does>. Triggers on: <keyword1>, <keyword2>, <keyword3>."
+license: MIT
+allowed-tools: "<space-delimited tool list>"
+metadata:
+  author: claude-mods
+---
+```
+
+**Body structure:**
+
+```markdown
+# <Skill Title>
+
+<1-2 sentence overview>
+
+## When to Use
+
+- <trigger condition 1>
+- <trigger condition 2>
+
+## Procedure
+
+1. <Step one>
+2. <Step two>
+3. <Step three>
+...
+
+## Notes
+
+<Edge cases, caveats, or tips>
+```
+
+### Step 6: Verify
+
+After creating, verify the skill:
+
+1. Check the file was written correctly:
+   ```bash
+   head -20 .claude/skills/<name>/SKILL.md
+   ```
+2. Validate frontmatter has only spec-compliant top-level keys
+3. Confirm the procedure section exists and has steps
+4. Tell the user the skill is ready and how to invoke it
+
+## Per-Project Disable
+
+```bash
+touch .claude/auto-skill.disable    # Disable Stop hook suggestions
+rm .claude/auto-skill.disable       # Re-enable
+```
+
+The skill itself can always be invoked manually regardless of this setting.
+
+## Hook Setup
+
+Auto-skill uses two hooks for automatic suggestions. These are installed globally:
+
+```
+~/.claude/auto-skill/
+  track-tools.sh     # PostToolUse: counts tool calls per session
+  evaluate.sh        # Stop: suggests skill creation if complex enough
+```
+
+Both hooks fail silently - they will never produce error output or block Claude.
+
+### Hook Configuration
+
+Add to `~/.claude/settings.json` (merge with existing hooks):
+
+```json
+{
+  "hooks": {
+    "PostToolUse": [{
+      "matcher": "*",
+      "hooks": [{
+        "type": "command",
+        "command": "bash \"$HOME/.claude/auto-skill/track-tools.sh\"",
+        "timeout": 2
+      }]
+    }],
+    "Stop": [{
+      "hooks": [{
+        "type": "command",
+        "command": "bash \"$HOME/.claude/auto-skill/evaluate.sh\"",
+        "timeout": 5
+      }]
+    }]
+  }
+}
+```
+
+## Suggestion Gates
+
+The Stop hook only suggests skill creation when ALL of these pass:
+
+| Gate | Threshold | Rationale |
+|------|-----------|-----------|
+| **Mutating ops** | 8+ | High bar reduces noise from routine edits |
+| **Tool diversity** | 4+ distinct types | Write+Edit+Bash+Agent = workflow; Write*20 = repetitive |
+| **No skill loaded** | `Skill` tool absent | If following a skill, work isn't novel |
+| **Per-session** | Once per session | Never nags on resume/continue |
+| **Not disabled** | No `.disable` file | Global or per-project toggle |
+
+Read-only tools (Read, Glob, Grep, LS, Task*) are excluded from counts.
+
+## Design Decisions
+
+- **Stop hook, not in-loop**: Claude Code doesn't expose the agent loop. The Stop hook fires while context is still in memory, which is the best we can get.
+- **systemMessage output**: The Stop hook outputs JSON that Claude Code displays to the user. Non-blocking, dismissible.
+- **Diversity over volume**: Tool type count matters more than raw call count. A 20-file rename isn't a skill; a workflow using Write+Edit+Bash+Agent probably is.
+- **Per-session cooldown**: Uses a temp file keyed by session ID. No harsh time-based cooldown - each new session gets a fresh chance.
+- **500-line cap on tracking**: Prevents runaway sessions from filling /tmp.
+- **Silent failures**: Both hooks wrap everything in `2>/dev/null` and always `exit 0`.

+ 0 - 0
skills/auto-skill/assets/.gitkeep


+ 0 - 0
skills/auto-skill/references/.gitkeep


+ 0 - 0
skills/auto-skill/scripts/.gitkeep


+ 105 - 0
skills/auto-skill/scripts/evaluate.sh

@@ -0,0 +1,105 @@
+#!/bin/bash
+# evaluate.sh - Stop hook: evaluate session for skill-worthy workflows
+#
+# Suggests skill creation only when a session shows genuine workflow complexity:
+#   - 8+ mutating tool calls (high threshold, reduces noise)
+#   - 4+ distinct mutating tool types (diversity = workflow, not repetitive edits)
+#   - No skill was loaded (novel work, not following existing procedure)
+#   - Per-session cooldown file prevents re-fire on resume
+#
+# Toggle: touch ~/.claude/auto-skill.disable   (global off)
+#         touch .claude/auto-skill.disable      (project off)
+#         rm either file to re-enable
+#
+# CRITICAL: This hook must NEVER fail visibly. All errors suppressed.
+
+{
+  INPUT=$(cat)
+  SESSION_ID=$(printf '%s' "$INPUT" | jq -r '.session_id // empty' 2>/dev/null)
+
+  [ -z "$SESSION_ID" ] && exit 0
+
+  SHORT_ID="${SESSION_ID:0:8}"
+  TRACK_FILE="/tmp/claude_autoskill_${SHORT_ID}"
+
+  # No tracking file = no tool calls recorded
+  [ -f "$TRACK_FILE" ] || exit 0
+
+  # --- Toggle: global or project disable ---
+  if [ -f "$HOME/.claude/auto-skill.disable" ] || [ -f ".claude/auto-skill.disable" ]; then
+    rm -f "$TRACK_FILE"
+    exit 0
+  fi
+
+  # --- Per-session cooldown: only suggest once per session ---
+  SUGGESTED_FILE="/tmp/claude_autoskill_suggested_${SHORT_ID}"
+  if [ -f "$SUGGESTED_FILE" ]; then
+    rm -f "$TRACK_FILE"
+    exit 0
+  fi
+
+  # --- Classify tools ---
+  READ_ONLY_LIST=" Read Glob Grep LS NotebookRead TaskList TaskGet TaskCreate TaskUpdate TaskOutput TaskStop "
+  SKILL_LOADED=false
+  TOTAL=0
+  WRITES=0
+  UNIQUE_TYPES=""
+
+  while IFS= read -r tool; do
+    [ -z "$tool" ] && continue
+    TOTAL=$((TOTAL + 1))
+
+    # Check if a skill was loaded
+    [ "$tool" = "Skill" ] && SKILL_LOADED=true
+
+    # Check if read-only (space-padded list for exact word match)
+    case "$READ_ONLY_LIST" in
+      *" ${tool} "*) continue ;;
+    esac
+
+    # Skip Skill tool itself
+    [ "$tool" = "Skill" ] && continue
+
+    WRITES=$((WRITES + 1))
+
+    # Track unique mutating tool types
+    case " $UNIQUE_TYPES " in
+      *" ${tool} "*) ;;  # already seen
+      *) UNIQUE_TYPES="${UNIQUE_TYPES} ${tool}" ;;
+    esac
+  done < "$TRACK_FILE"
+
+  # Count unique types (count words in UNIQUE_TYPES)
+  UNIQUE_COUNT=0
+  for _ in $UNIQUE_TYPES; do
+    UNIQUE_COUNT=$((UNIQUE_COUNT + 1))
+  done
+
+  # Build tool summary before cleanup
+  TOOL_SUMMARY=$(sort "$TRACK_FILE" | uniq -c | sort -rn | head -6 | awk '{printf "%s(%d) ", $2, $1}')
+
+  # Clean up tracking file
+  rm -f "$TRACK_FILE"
+
+  # --- Gate 1: Skill was loaded = not novel work ---
+  [ "$SKILL_LOADED" = true ] && exit 0
+
+  # --- Gate 2: Minimum 8 mutating operations ---
+  [ "$WRITES" -lt 8 ] && exit 0
+
+  # --- Gate 3: Minimum 4 distinct mutating tool types ---
+  [ "$UNIQUE_COUNT" -lt 4 ] && exit 0
+
+  # --- All gates passed: suggest ---
+
+  # Mark this session as suggested (prevents repeat on resume)
+  touch "$SUGGESTED_FILE" 2>/dev/null
+
+  MSG="Skill-worthy session: ${WRITES} mutating ops across ${UNIQUE_COUNT} tool types (${TOTAL} total): ${TOOL_SUMMARY}- run /auto-skill to capture this workflow."
+
+  ESCAPED=$(printf '%s' "$MSG" | sed 's/"/\\"/g' | tr '\n' ' ')
+  printf '{"systemMessage":"%s"}\n' "$ESCAPED"
+
+} 2>/dev/null
+
+exit 0

+ 25 - 0
skills/auto-skill/scripts/track-tools.sh

@@ -0,0 +1,25 @@
+#!/bin/bash
+# track-tools.sh - PostToolUse hook: lightweight tool call counter
+# Appends tool name to a session-specific temp file.
+# Designed to be fast (<5ms) - no SQLite, no network, just a file append.
+#
+# CRITICAL: This hook must NEVER fail visibly. All errors suppressed.
+
+{
+  INPUT=$(cat)
+  TOOL_NAME=$(printf '%s' "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
+  SESSION_ID=$(printf '%s' "$INPUT" | jq -r '.session_id // empty' 2>/dev/null)
+
+  [ -z "$TOOL_NAME" ] && exit 0
+  [ -z "$SESSION_ID" ] && exit 0
+
+  SHORT_ID="${SESSION_ID:0:8}"
+  TRACK_FILE="/tmp/claude_autoskill_${SHORT_ID}"
+
+  # Append tool name (one per line). Cap at 500 lines to prevent runaway.
+  if [ ! -f "$TRACK_FILE" ] || [ "$(wc -l < "$TRACK_FILE" 2>/dev/null)" -lt 500 ]; then
+    echo "$TOOL_NAME" >> "$TRACK_FILE"
+  fi
+} 2>/dev/null
+
+exit 0