fix(skills): frontmatter lint — using-interns v0.3.1 (YAML+≤1024), using-wiki-graph v0.1.1 (≤1024), ralph-loop-execution frontmatter, remove using-yt-tools stub, add scripts/lint-skills.py + wire into update.{sh,ps1}
This commit is contained in:
85
scripts/lint-skills.py
Normal file
85
scripts/lint-skills.py
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""lint-skills.py — Agent Skills spec lint over a skills catalog.
|
||||||
|
|
||||||
|
Checks every SKILL.md:
|
||||||
|
1. frontmatter parses as YAML
|
||||||
|
2. `description` present and <= 1024 chars (spec cap)
|
||||||
|
3. required fields present: name, description (version optional-but-encouraged)
|
||||||
|
|
||||||
|
Exit code 0 = clean, 1 = violations found (prints them).
|
||||||
|
Usage: python lint-skills.py [skills-dir] (default: ./skills)
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
try:
|
||||||
|
import yaml
|
||||||
|
except ImportError:
|
||||||
|
print("error: PyYAML required (pip install pyyaml)", file=sys.stderr)
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
MAX_DESC = 1024
|
||||||
|
REQUIRED = ("name", "description")
|
||||||
|
|
||||||
|
|
||||||
|
def lint_one(path):
|
||||||
|
"""Return list of violation strings for one SKILL.md."""
|
||||||
|
problems = []
|
||||||
|
txt = open(path, encoding="utf-8").read()
|
||||||
|
if not txt.startswith("---"):
|
||||||
|
return [f"{path}: missing frontmatter (must start with ---)"]
|
||||||
|
try:
|
||||||
|
fm = txt.split("---", 2)[1]
|
||||||
|
d = yaml.safe_load(fm)
|
||||||
|
except Exception as e:
|
||||||
|
return [f"{path}: frontmatter YAML parse error: {str(e)[:80]}"]
|
||||||
|
if not isinstance(d, dict):
|
||||||
|
return [f"{path}: frontmatter is not a mapping"]
|
||||||
|
for f in REQUIRED:
|
||||||
|
if not d.get(f):
|
||||||
|
problems.append(f"{path}: missing required field '{f}'")
|
||||||
|
desc = d.get("description") or ""
|
||||||
|
if len(desc) > MAX_DESC:
|
||||||
|
problems.append(f"{path}: description exceeds {MAX_DESC} chars ({len(desc)})")
|
||||||
|
return problems
|
||||||
|
|
||||||
|
|
||||||
|
def warn_one(path):
|
||||||
|
"""Soft findings (not spec violations)."""
|
||||||
|
warnings = []
|
||||||
|
txt = open(path, encoding="utf-8").read()
|
||||||
|
if not txt.startswith("---"):
|
||||||
|
return warnings
|
||||||
|
d = yaml.safe_load(txt.split("---", 2)[1])
|
||||||
|
if isinstance(d, dict) and not d.get("version"):
|
||||||
|
warnings.append(f"{path}: missing version (encouraged)")
|
||||||
|
return warnings
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
root = sys.argv[1] if len(sys.argv) > 1 else "skills"
|
||||||
|
if not os.path.isdir(root):
|
||||||
|
print(f"error: not a directory: {root}", file=sys.stderr)
|
||||||
|
return 2
|
||||||
|
all_problems = []
|
||||||
|
all_warnings = []
|
||||||
|
count = 0
|
||||||
|
for dirpath, dirnames, filenames in os.walk(root):
|
||||||
|
if "SKILL.md" in filenames:
|
||||||
|
count += 1
|
||||||
|
p = os.path.join(dirpath, "SKILL.md")
|
||||||
|
all_problems.extend(lint_one(p))
|
||||||
|
all_warnings.extend(warn_one(p))
|
||||||
|
for w in all_warnings:
|
||||||
|
print(" W: " + w)
|
||||||
|
if all_problems:
|
||||||
|
print(f"lint: {count} skills, {len(all_problems)} violations:")
|
||||||
|
for p in all_problems:
|
||||||
|
print(" " + p)
|
||||||
|
return 1
|
||||||
|
print(f"lint: {count} skills, 0 violations ({len(all_warnings)} warnings)")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
@@ -133,6 +133,10 @@ if (Test-Path (Join-Path $internsMcp '.git')) {
|
|||||||
|
|
||||||
Pop-Location
|
Pop-Location
|
||||||
Write-Host "[update] Installing skills..." -ForegroundColor Cyan
|
Write-Host "[update] Installing skills..." -ForegroundColor Cyan
|
||||||
|
# 0. Lint skills against Agent Skills spec (fail on violations)
|
||||||
|
& python "$root\scripts\lint-skills.py" "$root\skills" 2>$null
|
||||||
|
if ($LASTEXITCODE -ne 0) { Write-Host "[update] SKILL.md lint failed - fix before install" -ForegroundColor Red; exit 1 }
|
||||||
|
# 1. Install all skills via install.ps1
|
||||||
& "$root\scripts\install.ps1"
|
& "$root\scripts\install.ps1"
|
||||||
Write-Host "[ok] Skills installed." -ForegroundColor Green
|
Write-Host "[ok] Skills installed." -ForegroundColor Green
|
||||||
|
|
||||||
|
|||||||
@@ -123,7 +123,10 @@ fi
|
|||||||
|
|
||||||
cd "$ROOT"
|
cd "$ROOT"
|
||||||
info "Installing skills..."
|
info "Installing skills..."
|
||||||
bash "$ROOT/scripts/install.sh"
|
# 0. Lint skills against Agent Skills spec (fail on violations)
|
||||||
|
python "$ROOT/scripts/lint-skills.py" "$ROOT/skills" || { error "SKILL.md lint failed - fix before install"; exit 1; }
|
||||||
|
# 1. Install all skills via install.sh
|
||||||
|
bash "$ROOT/scripts/install.sh"
|
||||||
ok "Skills installed."
|
ok "Skills installed."
|
||||||
|
|
||||||
# ── Step 5: Version diff ───────────────────────────────────────────────────
|
# ── Step 5: Version diff ───────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -1,3 +1,13 @@
|
|||||||
|
---
|
||||||
|
name: ralph-loop-execution
|
||||||
|
version: 0.1.0
|
||||||
|
description: >
|
||||||
|
Execute a ralph-loop task: read the task's **Verifier:** oracle-command,
|
||||||
|
do the work, run the verifier, record the attempt (or harness-fail on
|
||||||
|
infra breakage). Use when a task block contains `**Verifier:** <command>`
|
||||||
|
or `**Max-Attempts:**`. Activates at the start of any verifier-gated task.
|
||||||
|
---
|
||||||
|
|
||||||
# ralph-loop-execution
|
# ralph-loop-execution
|
||||||
|
|
||||||
## When to Use
|
## When to Use
|
||||||
|
|||||||
@@ -1,7 +1,16 @@
|
|||||||
---
|
---
|
||||||
name: using-interns
|
name: using-interns
|
||||||
version: 0.3.0
|
version: 0.3.1
|
||||||
description: Use when delegating predictable bulk reads or summarization to cheap intern LLMs via the local `interns` MCP server (`mcp__interns__bulk_text_read`, `mcp__interns__transcript_distill`, etc.) so Claude saves Anthropic quota. Activated by `delegate to interns when allowed` in CLAUDE.md or explicit phrases like "use interns", "delegate to an intern", "разреши интернов", "allow interns". **Also activates proactively when Claude is about to read 3+ files for context, read a single file >400 lines for non-edit purposes, or distill a long transcript — surface the offer "знаю, что есть интерны — заюзать?" before proceeding, even without an explicit user phrase.** Per-session permission grant mirrors `project-discipline` Rule 4: ask-mode default, conversational grant / revoke, always-ask paths for `.env` / secrets / keys / SSH / credentials even with active grant, transitive rule (Claude can't bypass by reading the file itself and forwarding content), session-end reset. Skip for architecture, debugging, auth / payments, final commit messages, or final user-facing text.
|
description: >
|
||||||
|
Use when delegating predictable bulk reads or summarization to cheap intern LLMs via the l
|
||||||
|
ocal `interns` MCP server (`mcp__interns__bulk_text_read`, `mcp__interns__transcript_disti
|
||||||
|
ll`, etc.) so the main agent saves quota. Activated by `delegate to interns when allowed`
|
||||||
|
in CLAUDE.md or explicit phrases: «разреши интернов», "use interns", "allow interns". Also
|
||||||
|
activates proactively when about to read 3+ files for context, a single file >400 lines,
|
||||||
|
or distill a long transcript — surface the offer «знаю, что есть интерны — заюзать?» first
|
||||||
|
. Per-session grant mirrors project-discipline Rule 4: ask-mode default, always-ask for `.
|
||||||
|
env`/secrets/keys/SSH/credentials, transitive rule, session-end reset. Skip for architectu
|
||||||
|
re, debugging, auth/payments, final commits, final user-facing text.
|
||||||
---
|
---
|
||||||
|
|
||||||
# Using the `interns` MCP server
|
# Using the `interns` MCP server
|
||||||
|
|||||||
@@ -1,7 +1,16 @@
|
|||||||
---
|
---
|
||||||
name: using-wiki-graph
|
name: using-wiki-graph
|
||||||
version: 0.1.0
|
version: 0.1.1
|
||||||
description: "Use when a question is RELATIONAL about a wiki — «что связывает X и Y», «как связаны X и Y», «путь между X и Y», «через что X выходит на Y», «what connects X and Y», «how is X related to Y», «shortest path between pages» — or about wiki STRUCTURE/HEALTH — «что ссылается на X», «кто линкует X», «соседи страницы X», «сироты в вики», «битые/dangling ссылки», «сколько связных компонент», «backlinks of X», «orphan pages». Triggers the `wiki-graph` MCP server (`mcp__wiki-graph__path|neighbors|backlinks|orphans|stats`), which runs a deterministic BFS over `[[wikilinks]]` server-side. The failure-mode this guards: on a relational question the agent does a semantic read of one page and STOPS, never walking the multi-hop link chain (0% recall on such queries vs 67% for graph BFS). Precondition: only DENSE corpora (e.g. modulair-wiki, 150 linked pages) — skip on sparse wikis (the shared meta-wiki has ~1 link total, graph is empty). Each tool needs `corpus` = absolute path to the `.wiki/` dir. Read-only, no grant needed. Skip for content/semantic questions answerable by reading a single page, and for wikis with no `[[links]]`."
|
description: >
|
||||||
|
Use when a question is RELATIONAL about a wiki — «что связывает X и Y», «как связаны», «пу
|
||||||
|
ть между X и Y», «what connects X and Y», «shortest path» — or about STRUCTURE/HEALTH — «ч
|
||||||
|
то ссылается на X», «backlinks of X», «сироты», «битые ссылки», «orphan pages». Triggers `
|
||||||
|
wiki-graph` MCP (`mcp__wiki-graph__path|neighbors|backlinks|orphans|stats`) — deterministi
|
||||||
|
c BFS over `[[wikilinks]]` server-side. Guarded failure-mode: on relational questions the
|
||||||
|
agent reads one page and STOPS, never walking multi-hop chains. Precondition: DENSE corpor
|
||||||
|
a only (e.g. modulair-wiki, 150 pages); skip sparse wikis (shared meta-wiki ≈ empty graph)
|
||||||
|
. Each tool needs `corpus` = absolute path to `.wiki/`. Read-only, no grant. Skip for sing
|
||||||
|
le-page content questions and wikis without `[[links]]`.
|
||||||
---
|
---
|
||||||
|
|
||||||
# using-wiki-graph
|
# using-wiki-graph
|
||||||
|
|||||||
@@ -1,59 +0,0 @@
|
|||||||
---
|
|
||||||
name: using-yt-tools
|
|
||||||
version: 0.4.1
|
|
||||||
description: DEPRECATED — this skill has migrated to the `OpeItcLoc03/yt-tools` Claude Code plugin (canonical source). To restore yt-tools functionality, install the plugin via `/plugin marketplace add OpeItcLoc03/claude-plugins` followed by `/plugin install yt-tools@opeitcloc03-claude-plugins`. The plugin's bundled SessionStart hook auto-runs `pipx install --force "$CLAUDE_PLUGIN_ROOT[full]"` (from the plugin's local clone, fallback to core), and its bundled skill (full English, mixed RU/EN triggers) takes over from this stub. Once the plugin is installed, this `claude-skills/skills/using-yt-tools/` directory becomes redundant and can be deleted. This stub intentionally declares **no trigger phrases** to avoid double-activation with the plugin's skill — it remains inert until invoked by name.
|
|
||||||
---
|
|
||||||
|
|
||||||
# using-yt-tools — deprecated stub
|
|
||||||
|
|
||||||
This skill has migrated to the **`OpeItcLoc03/yt-tools` Claude Code plugin**.
|
|
||||||
It is no longer maintained in the `claude-skills` repository — all future
|
|
||||||
changes (CLI flag updates, new flows, bug fixes, version bumps) ship with
|
|
||||||
the plugin distribution.
|
|
||||||
|
|
||||||
## Why the move
|
|
||||||
|
|
||||||
Bundling the skill into a self-contained plugin (Python CLI + skill + hooks
|
|
||||||
+ LICENSE in one repo) lets one user-action install everything: the
|
|
||||||
plugin's `SessionStart` hook auto-runs
|
|
||||||
`pipx install --force "$CLAUDE_PLUGIN_ROOT[full]"` (from the plugin's local
|
|
||||||
clone, not PyPI) and probes `ffmpeg`, and the bundled skill activates the
|
|
||||||
same three flows
|
|
||||||
(iterative-watch / targeted-frames / audio-analysis) without any separate
|
|
||||||
`claude-skills` install step. See the design rationale in
|
|
||||||
`OpeItcLoc03/common/.wiki/concepts/yt-tools-distribution.md`.
|
|
||||||
|
|
||||||
## How to install the replacement
|
|
||||||
|
|
||||||
```text
|
|
||||||
/plugin marketplace add OpeItcLoc03/claude-plugins
|
|
||||||
/plugin install yt-tools@opeitcloc03-claude-plugins
|
|
||||||
```
|
|
||||||
|
|
||||||
The first session after install will run the plugin's `SessionStart` hook
|
|
||||||
to install yt-tools from the plugin's local clone (via
|
|
||||||
`pipx install --force "$CLAUDE_PLUGIN_ROOT[full]"`, falling back to core
|
|
||||||
if the `[full]` extras fail) and probe `ffmpeg`. From there the plugin's
|
|
||||||
bundled skill (canonical English version, mixed RU/EN triggers) takes
|
|
||||||
over.
|
|
||||||
|
|
||||||
## What to do with this stub
|
|
||||||
|
|
||||||
After `/plugin install yt-tools@opeitcloc03-claude-plugins` reports
|
|
||||||
success on your machine — delete this directory:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
rm -rf ~/projects/claude-skills/skills/using-yt-tools/
|
|
||||||
```
|
|
||||||
|
|
||||||
This stub has no trigger phrases, so it remains inert and will not
|
|
||||||
double-activate alongside the plugin's skill. It exists only as a sign
|
|
||||||
post for anyone still looking for the old location.
|
|
||||||
|
|
||||||
## Source pointers
|
|
||||||
|
|
||||||
- Plugin repository: <https://github.com/OpeItcLoc03/yt-tools>
|
|
||||||
- Marketplace catalog: <https://github.com/OpeItcLoc03/claude-plugins>
|
|
||||||
- Bundled canonical SKILL: `skills/using-yt-tools/SKILL.md` in the plugin repo
|
|
||||||
- PyPI: <https://pypi.org/project/yt-tools/> (deferred post-v1; not yet published)
|
|
||||||
- Design rationale: `OpeItcLoc03/common/.wiki/concepts/yt-tools-distribution.md`
|
|
||||||
Reference in New Issue
Block a user