From fcff190c32b3a568fd12f6272a32d5cb8436ef3e Mon Sep 17 00:00:00 2001 From: vitya Date: Wed, 12 Aug 2026 13:20:11 +0300 Subject: [PATCH] =?UTF-8?q?fix(skills):=20frontmatter=20lint=20=E2=80=94?= =?UTF-8?q?=20using-interns=20v0.3.1=20(YAML+=E2=89=A41024),=20using-wiki-?= =?UTF-8?q?graph=20v0.1.1=20(=E2=89=A41024),=20ralph-loop-execution=20fron?= =?UTF-8?q?tmatter,=20remove=20using-yt-tools=20stub,=20add=20scripts/lint?= =?UTF-8?q?-skills.py=20+=20wire=20into=20update.{sh,ps1}?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/lint-skills.py | 85 ++++++++++++++++++++++++++++ scripts/update.ps1 | 4 ++ scripts/update.sh | 5 +- skills/ralph-loop-execution/SKILL.md | 10 ++++ skills/using-interns/SKILL.md | 13 ++++- skills/using-wiki-graph/SKILL.md | 13 ++++- skills/using-yt-tools/SKILL.md | 59 ------------------- 7 files changed, 125 insertions(+), 64 deletions(-) create mode 100644 scripts/lint-skills.py delete mode 100644 skills/using-yt-tools/SKILL.md diff --git a/scripts/lint-skills.py b/scripts/lint-skills.py new file mode 100644 index 0000000..78eb41b --- /dev/null +++ b/scripts/lint-skills.py @@ -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()) diff --git a/scripts/update.ps1 b/scripts/update.ps1 index 19e2d38..786ec03 100644 --- a/scripts/update.ps1 +++ b/scripts/update.ps1 @@ -133,6 +133,10 @@ if (Test-Path (Join-Path $internsMcp '.git')) { Pop-Location 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" Write-Host "[ok] Skills installed." -ForegroundColor Green diff --git a/scripts/update.sh b/scripts/update.sh index b445722..91638f9 100644 --- a/scripts/update.sh +++ b/scripts/update.sh @@ -123,7 +123,10 @@ fi cd "$ROOT" 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." # ── Step 5: Version diff ─────────────────────────────────────────────────── diff --git a/skills/ralph-loop-execution/SKILL.md b/skills/ralph-loop-execution/SKILL.md index 5dbc5d1..14d8665 100644 --- a/skills/ralph-loop-execution/SKILL.md +++ b/skills/ralph-loop-execution/SKILL.md @@ -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:** ` + or `**Max-Attempts:**`. Activates at the start of any verifier-gated task. +--- + # ralph-loop-execution ## When to Use diff --git a/skills/using-interns/SKILL.md b/skills/using-interns/SKILL.md index 19fa835..eb9eea4 100644 --- a/skills/using-interns/SKILL.md +++ b/skills/using-interns/SKILL.md @@ -1,7 +1,16 @@ --- name: using-interns -version: 0.3.0 -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. +version: 0.3.1 +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 diff --git a/skills/using-wiki-graph/SKILL.md b/skills/using-wiki-graph/SKILL.md index edbebe9..b9917f2 100644 --- a/skills/using-wiki-graph/SKILL.md +++ b/skills/using-wiki-graph/SKILL.md @@ -1,7 +1,16 @@ --- name: using-wiki-graph -version: 0.1.0 -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]]`." +version: 0.1.1 +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 diff --git a/skills/using-yt-tools/SKILL.md b/skills/using-yt-tools/SKILL.md deleted file mode 100644 index a618b0d..0000000 --- a/skills/using-yt-tools/SKILL.md +++ /dev/null @@ -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: -- Marketplace catalog: -- Bundled canonical SKILL: `skills/using-yt-tools/SKILL.md` in the plugin repo -- PyPI: (deferred post-v1; not yet published) -- Design rationale: `OpeItcLoc03/common/.wiki/concepts/yt-tools-distribution.md`