kit/plugins/plan-agent/skills/plans-library/SKILL.md
Builds and opens a filterable HTML gallery of all plans in the plans directory. Scans HTML plans, parses metadata, and writes index.html. Use when asked to browse plans or view plan history.
npx skillsauth add shawn-sandy/agentics plans-libraryInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Scan every HTML plan in the plans directory, parse each plan's metadata, populate the gallery template, write index.html, and open it in the browser.
ExitPlanMode is a deferred tool. Only call it if currently in plan mode — skip this step entirely when not in plan mode. When calling: use ToolSearch with select:ExitPlanMode first, then call ExitPlanMode silently.
Read plansDirectory from .claude/settings.json (project first, then global ~/.claude/settings.json). Fall back to ${PWD}/docs/plans if neither file sets it.
PLANS_DIR=$(python3 - <<'EOF'
import json, os, sys
project = os.path.join(os.getcwd(), '.claude', 'settings.json')
global_ = os.path.join(os.path.expanduser('~'), '.claude', 'settings.json')
for path in (project, global_):
try:
v = json.load(open(path)).get('plansDirectory', '').strip()
if v:
print(v); sys.exit(0)
except Exception:
pass
print(os.path.join(os.getcwd(), 'docs', 'plans'))
EOF
)
If the directory does not exist or contains no .html files (other than index.html), tell the user:
"No HTML plans found in
<PLANS_DIR>. Run/plan-agent:implementation-planto create your first plan."
STOP.
The plugin may be installed as a versioned cached copy (e.g. …/plan-agent/0.11.0/templates) or loaded directly (e.g. …/plan-agent/templates). Both patterns must be tried:
TEMPLATES_DIR=$( { \
find ~/.claude/plugins -path "*/plan-agent/*/templates" -type d 2>/dev/null | sort -rV; \
find ~/.claude/plugins -path "*/plan-agent/templates" -type d 2>/dev/null; \
find "$PWD" -path "*/plan-agent/templates" -type d 2>/dev/null; \
} | head -1 )
If TEMPLATES_DIR is empty, output:
"Templates not found. Install the plugin or load it with --plugin-dir." and STOP.
Collect all .html files in PLANS_DIR, excluding index.html. The -maxdepth 1 flag prevents recursion into archive/ or any other subdirectory. Sort newest-modified first and capture the result in PLAN_FILES.
PLAN_FILES=$(find "$PLANS_DIR" -maxdepth 1 -name "*.html" ! -name "index.html" -print0 2>/dev/null \
| xargs -0 ls -t 2>/dev/null)
{{GALLERY_ENTRIES}}Iterate over each file $f from $PLAN_FILES (one path per line). For each, parse its metadata using Python 3. Output is JSON to safely handle titles that contain | or other special characters:
while IFS= read -r f; do
python3 - "$f" <<'EOF'
import re, sys, os, json
f = sys.argv[1]
try:
content = open(f, encoding='utf-8', errors='replace').read()
except Exception:
sys.exit(0)
def meta(name, fallback=''):
m = re.search(r'<meta\s+name="' + name + r'"\s+content="([^"]*)"', content)
return m.group(1).strip() if m else fallback
def get_title():
m = re.search(r'<title>(?:Plan:\s*)?([^<]+)</title>', content, re.I)
return m.group(1).strip() if m else os.path.basename(f)
print(json.dumps({
'status': meta('plan-status', 'todo'),
'type': meta('plan-type', 'untyped'),
'created': meta('plan-created', ''),
'title': get_title(),
}))
EOF
done <<< "$PLAN_FILES"
Parse the JSON output with json.loads(). From each result, generate one <a> block:
<a class="gallery-card" href="{BASENAME}"
data-status="{STATUS}" data-type="{TYPE}" data-title="{TITLE_LOWER}">
<div class="card-badges">
<span class="status-chip status-{STATUS}">{STATUS_DISPLAY}</span>
<span class="type-chip type-{TYPE}">{TYPE}</span>
</div>
<div class="card-title">{TITLE}</div>
<div class="card-meta">
<span class="card-date">{CREATED}</span>
<span class="card-file">{BASENAME}</span>
</div>
</a>
Where:
{BASENAME} = filename without path (e.g. add-dark-mode-toggle.html){STATUS} = plan-status value, lowercased (e.g. todo, in-progress, completed){STATUS_DISPLAY} = {STATUS} with hyphens replaced by spaces (e.g. in progress){TYPE} = plan-type value, lowercased (e.g. feature, fix){TITLE_LOWER} = title lowercased (used by search filter){TITLE} = title text (strip a leading "Plan: " prefix if present){CREATED} = plan-created value (e.g. 2026-05-30); omit the <span class="card-date"> if emptyHTML-escape all values before inserting: replace & → &, < → <, > → >, " → ", ' → '.
GENERATED_AT=$(date '+%Y-%m-%d %H:%M')
Read the gallery template from $TEMPLATES_DIR/plans-gallery.html.
Substitute in the template:
{{GALLERY_ENTRIES}} → concatenated <a> blocks from Step 4{{PLAN_COUNT}} → total number of plans found{{GENERATED_AT}} → value of $GENERATED_ATWrite the result to $PLANS_DIR/index.html.
GALLERY_PATH=$(realpath "$PLANS_DIR/index.html" 2>/dev/null || echo "$PLANS_DIR/index.html")
open "$GALLERY_PATH" 2>/dev/null || xdg-open "$GALLERY_PATH" 2>/dev/null || true
Tell the user:
"Plans library generated at
<PLANS_DIR>/index.htmlwith {count} plans — opened in your browser. Click any card to open that plan."
STOP. Do not run git commands or invoke other skills after delivering the gallery.
development
Turns a React component into a social card with preview, code, and props table. Builds a static preview and screenshots react-card.html via Playwright. Use when asked to share a React component.
data-ai
Refine-prompt: interviews users and assembles a structured AI prompt using Anthropic best-practice techniques. Use when the user runs /plan-agent:refine-prompt or asks to refine a prompt.
development
Plan review Agent Team. Reviews HTML implementation plans in parallel, synthesizes findings, and applies improvements in place. Use when the user asks to review or improve an implementation plan.
data-ai
Craft-prompt: interviews users and assembles a structured AI prompt using Anthropic best-practice techniques. Use when the user runs /plan-agent:craft-prompt or asks to craft a prompt.