From ce5935b45f20a33c78e6276c89547cb91f1b020f Mon Sep 17 00:00:00 2001 From: Yogi Date: Mon, 13 Apr 2026 14:34:26 +0530 Subject: [PATCH 1/2] feat: improve skill scores for opencode-skill-shenron MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hey @shenron0101 👋 I ran your skills through `tessl skill review` at work and found some targeted improvements. Here's the full before/after: | Skill | Before | After | Change | |-------|--------|-------|--------| | opencode-migrator | 51% | 90% | +39% | | graph-visualizer | 90% | 90% | — | | pdf-study-qa | 33% | 86% | +53% | > **Note:** Two skills (`claude2opencode-skill` and `pdf-study-qa-skill`) originally had no YAML frontmatter, which caused them to fail deterministic validation (scoring 10%). I added minimal frontmatter (name + description) and re-scored to get meaningful baseline scores — the "Before" column reflects those corrected baselines. The `claude2opencode-skill` name was also changed from `claude2opencode` to `opencode-migrator` because `claude` is a reserved word in skill names.
Changes made ### opencode-migrator (claude2opencode-skill) — 51% → 90% - Added YAML frontmatter with proper `name` and `description` fields - Renamed skill from `claude2opencode` to `opencode-migrator` to avoid reserved word violation - Expanded description with "Use when..." clause and natural trigger terms (migrate, port, switch, .claude, configuration migration) - Replaced abstract usage section with a concrete 5-step workflow including validation and verification commands - Removed redundant "Description" section (duplicated title) and "Requirements" section (known concepts) - Added executable bash examples for config analysis, migration, output validation, and manifest inspection ### graph-visualizer — 90% → 90% - Converted frontmatter description from YAML folded block scalar (`>`) to quoted string format - Consolidated repeated visibility warnings into a single "Visibility Rules" reference section - Consolidated per-variant aesthetic specifications into a single reference table in Step 2 - Added 3 validation checkpoints (starfield rendering, node visibility, bloom activation) - Removed redundant "see per-variant specifications" back-references from later steps ### pdf-study-qa — 33% → 86% - Added YAML frontmatter with proper `name` and `description` fields - Expanded description with "Use when..." clause and natural trigger terms (PDFs, .pdf, read PDF, summarize, chat with document) - Replaced abstract 4-step workflow with executable Python code (pdfplumber extraction, chunking, Q&A with citations) - Added validation checklist (zero-page check, OCR fallback, page count logging) - Removed padded sections (Supported PDF Types, generic Features, Requirements) - Added error handling reference table covering common failure modes
Honest disclosure — I work at @tesslio where we build tooling around skills like these. Not a pitch - just saw room for improvement and wanted to contribute. Want to self-improve your skills? Just point your agent (Claude Code, Codex, etc.) at [this Tessl guide](https://docs.tessl.io/evaluate/optimize-a-skill-using-best-practices) and ask it to optimize your skill. Ping me - [@yogesh-tessl](https://github.com/yogesh-tessl) - if you hit any snags. Thanks in advance 🙏 --- claude2opencode-skill/SKILL.md | 102 ++++++++++++++-------- graph-visualizer/SKILL.md | 81 +++++++++--------- pdf-study-qa-skill/SKILL.md | 152 ++++++++++++++++++++++++--------- 3 files changed, 217 insertions(+), 118 deletions(-) diff --git a/claude2opencode-skill/SKILL.md b/claude2opencode-skill/SKILL.md index 027937d..539a8d4 100644 --- a/claude2opencode-skill/SKILL.md +++ b/claude2opencode-skill/SKILL.md @@ -1,35 +1,74 @@ -# Claude2OpenCode Skill +--- +name: opencode-migrator +description: "Convert Claude Code project configurations to OpenCode format. Use when the user wants to migrate, port, or switch a Claude Code project to OpenCode — including .claude directories, CLAUDE.md files, MCP server settings, agents, commands, and skills. Handles configuration migration of project settings into the OpenCode directory layout." +--- -Convert Claude Code project configurations to OpenCode format. +# Claude2OpenCode Migration -## Description +## Workflow -This skill provides a migration toolkit for converting Claude Code project configurations (.claude directory, CLAUDE.md, MCP settings) to OpenCode format. +1. **Analyze existing configuration** — The agent scans the project root for Claude Code artifacts: -## Usage + ```bash + # Identify all Claude Code config files in the current project + ls -la .claude/ CLAUDE.md .claude/CLAUDE.md .claude/agents/ .claude/commands/ .claude/skills/ 2>/dev/null + # Check for MCP server definitions + cat .claude/settings.json 2>/dev/null | grep -A5 '"mcpServers"' + ``` -```bash -# Load the skill -/skill claude2opencode +2. **Run migration** — Execute the migration script targeting the desired output format: -# Run the migration -python3 ~/.claude/tools/migrate_claude_project.py --target opencode --project-root . -``` + ```bash + # Migrate to OpenCode format (default) + python3 ~/.claude/tools/migrate_claude_project.py --target opencode --project-root . + + # Or migrate to Codex-friendly markdown bundle + python3 ~/.claude/tools/migrate_claude_project.py --target codex --project-root . + ``` + +3. **Validate output** — Confirm the migration produced the expected directory structure: + + ```bash + # Verify output directory was created + ls -R .migration_out/opencode/ + + # Check the OpenCode config is valid JSON + python3 -c "import json; json.load(open('.migration_out/opencode/opencode.jsonc'))" 2>&1 || echo "WARN: jsonc may need manual review" + + # Confirm AGENTS.md was generated from CLAUDE.md + head -20 .migration_out/opencode/AGENTS.md + ``` + +4. **Review migration report** — Read the generated report for any items requiring manual follow-up: -## What It Migrates + ```bash + cat .migration_out/opencode/MIGRATION_REPORT.md + ``` -- **CLAUDE.md / .claude/CLAUDE.md** → AGENTS.md (project rules) -- **Agents** (.claude/agents/) → OpenCode prompts -- **Commands** (.claude/commands/) → OpenCode commands -- **Skills** (.claude/skills/) → OpenCode instructions -- **MCP Servers** (settings.json, mcp*.json) → MCP.md with setup instructions +5. **Verify key files** — Spot-check that critical mappings transferred correctly: -## Supported Targets + ```bash + # Check manifest for complete file mapping + python3 -c " + import json + m = json.load(open('.migration_out/opencode/manifest.json')) + for src, dst in m.items(): + print(f'{src} → {dst}') + " -| Target | Description | -|--------|-------------| -| `opencode` | Full OpenCode format with `.opencode.jsonc` configuration | -| `codex` | Codex-friendly markdown bundle for manual integration | + # Confirm MCP server instructions were captured + head -30 .migration_out/opencode/MCP.md + ``` + +## Migration Mapping + +| Source (Claude Code) | Destination (OpenCode) | +|---|---| +| `CLAUDE.md` / `.claude/CLAUDE.md` | `AGENTS.md` (project rules) | +| `.claude/agents/` | `.opencode/prompts/` | +| `.claude/commands/` | `.opencode/command/` | +| `.claude/skills/` | `.opencode/instructions/skills/` | +| `settings.json` / `mcp*.json` | `MCP.md` (setup instructions) | ## Output Structure @@ -49,18 +88,7 @@ python3 ~/.claude/tools/migrate_claude_project.py --target opencode --project-ro ## Safety Features -- **Non-destructive**: Never deletes original files -- **No overwrites**: Creates numbered output directories if target exists -- **Full traceability**: Manifest tracks every generated file back to its source -- **Clear reporting**: Migration report documents all decisions and required follow-ups - -## Requirements - -- Python 3.8+ -- Claude Code CLI with subagent support - -## Files - -- `migrate_claude_project.py` - Core migration script -- `migrate_project.md` - Agent definition -- `migrate.md` - Command shortcut +- **Non-destructive**: The migration never deletes or modifies original files. +- **No overwrites**: Numbered output directories are created if the target path already exists. +- **Full traceability**: The manifest tracks every generated file back to its source. +- **Clear reporting**: The migration report documents all decisions and required follow-ups. diff --git a/graph-visualizer/SKILL.md b/graph-visualizer/SKILL.md index bcf72bb..f121867 100644 --- a/graph-visualizer/SKILL.md +++ b/graph-visualizer/SKILL.md @@ -1,7 +1,6 @@ --- name: graph-visualizer -description: > - Builds a fully 3D interactive goal-graph visualization as a self-contained HTML file using Three.js. Use this skill whenever the user wants to visualize a goal, system, workflow, or project as an animated directed graph — with a 3D space background, glowing floating nodes, an animated pulse traveling the loop, and a collapsible constraints sidebar. Trigger when the user says things like "create a graph for my goal", "visualize my system as a graph", "build a 3D graph app", "show my workflow as a graph", or "make an interactive graph visualization". Parses natural language description of nodes, edges, weights, and constraints, and produces THREE distinct production-grade Three.js HTML files with different aesthetics. +description: "Builds a fully 3D interactive goal-graph visualization as a self-contained HTML file using Three.js. Use this skill whenever the user wants to visualize a goal, system, workflow, or project as an animated directed graph — with a 3D space background, glowing floating nodes, an animated pulse traveling the loop, and a collapsible constraints sidebar. Trigger when the user says things like 'create a graph for my goal', 'visualize my system as a graph', 'build a 3D graph app', 'show my workflow as a graph', or 'make an interactive graph visualization'. Parses natural language description of nodes, edges, weights, and constraints, and produces THREE distinct production-grade Three.js HTML files with different aesthetics." --- # Graph Visualizer — Three.js Build Guide @@ -53,6 +52,15 @@ Before any code, commit to 3 fully distinct aesthetic directions. Name them. Exa Each variant gets its own HTML file. The graph data (nodes, edges, constraints) is identical — only the visual treatment changes. +**Per-variant specifications** — all variant-specific values throughout this skill come from this table: + +| Element | Variant A (cosmic) | Variant B (terminal) | Variant C (warm) | +|---------|-------------------|---------------------|------------------| +| Stars | white, 3 dark-blue/purple nebula shells | green-tinted `0x88ffaa`, no nebula — pure black void | warm cream `0xffe8c0`, soft amber nebula shells | +| Node shape | BoxGeometry (sharp, geometric) | BoxGeometry extreme aspect ratio (wide/flat) | CylinderGeometry (rounded pill, laid flat) | +| Pulse color | cyan `0x6ee7f7` | bright green `0x39ff14` | amber/gold `0xfbbf24` | +| Lighting | themed ambient + key light colors per mood | themed ambient + key light colors per mood | themed ambient + key light colors per mood | + --- ## Step 3 — Three.js scene setup (same for all variants, from `threejs-fundamentals`) @@ -80,12 +88,9 @@ controls.maxDistance = 40; ## Step 4 — Starfield background (from `threejs-geometry`) -~800 `THREE.Points` placed in a sphere of radius 60–80. Slowly rotate each frame. +~800 `THREE.Points` placed in a sphere of radius 60–80. Slowly rotate each frame. Apply variant-specific star colors and nebula settings. -**Vary the starfield per variant:** -- Variant A: white stars, 3 dark-blue/purple nebula shells -- Variant B: green-tinted stars (color: 0x88ffaa), no nebula shells — pure black void -- Variant C: warm cream stars (color: 0xffe8c0), soft amber nebula shells +**Checkpoint:** Open the HTML — verify the starfield renders on a blank canvas before proceeding to nodes. --- @@ -105,15 +110,14 @@ function nodePosition(node) { --- -## Step 6 — Node meshes: VISIBILITY IS CRITICAL (from `threejs-materials`) +## Visibility Rules (referenced by Steps 6 and 7) -**The #1 visibility problem in past versions: nodes were near-black with low emissive — impossible to read.** +Past versions suffered from near-invisible nodes and edges on dark backgrounds. Apply these rules to all scene elements: -Fix with these rules: -1. **Base color must NOT be near-black.** Use at minimum 15–25% lightness. E.g. `0x1a2a3a` not `0x020408`. -2. **emissiveIntensity must be 1.0–2.5** (not 0.3–0.5). Nodes should visibly glow. -3. **Add a visible border/outline**: Create a slightly larger `BoxGeometry` behind each node with an emissive outline material, scaled by 1.05. This creates a visible glowing edge. -4. **Labels must be large and bright**: canvas size 512×128, font size 38–44px, white fill, with a subtle dark shadow behind the text for contrast. +- **Base colors must NOT be near-black.** Use at minimum 15–25% lightness. E.g. `0x1a2a3a` not `0x020408`. +- **emissiveIntensity for nodes: 1.0–2.5** (not 0.3–0.5). Nodes should visibly glow. +- **Edge opacity: 0.7–0.9** for normal edges (not 0.45). Edge color must contrast the background. +- **Labels must be large and bright**: canvas size 512×128, font size 38–44px, white fill, dark shadow for contrast. ```javascript // CORRECT — high visibility node @@ -123,6 +127,20 @@ Fix with these rules: { color: 0x080820, emissive: 0x4466ff, emissiveIntensity: 0.3, roughness: 0.6, metalness: 0.1 } ``` +```javascript +// CORRECT — visible edge +new THREE.MeshBasicMaterial({ color: 0x1a5a8a, transparent: true, opacity: 0.75 }) + +// WRONG — invisible edge +new THREE.MeshBasicMaterial({ color: 0x1a3a4a, transparent: true, opacity: 0.45 }) +``` + +--- + +## Step 6 — Node meshes (from `threejs-materials`) + +Follow **Visibility Rules** above for all node materials and labels. Add a visible border/outline: create a slightly larger geometry behind each node with an emissive outline material, scaled by 1.05. + **Node sizes by weight:** - `large`: 2.2 × 0.6 × 0.4 - `medium`: 1.8 × 0.55 × 0.4 @@ -138,29 +156,15 @@ outline.position.z -= 0.05; scene.add(outline); ``` -**Vary node shapes per variant:** -- Variant A: BoxGeometry (sharp, geometric) -- Variant B: BoxGeometry with extreme aspect ratio (wide and flat, terminal-style) -- Variant C: Use a `CylinderGeometry(w/2, w/2, h, 32)` (rounded pill, laid flat) - ---- +Vary node shapes per variant. -## Step 7 — Edges as tubes: VISIBILITY IS CRITICAL (from `threejs-geometry`) +**Checkpoint:** Open the HTML — confirm all nodes are clearly visible against the background and labels are readable before adding edges. -**The #2 visibility problem: edges at opacity 0.45 on dark background are near-invisible.** +--- -Fix: -1. **opacity must be 0.7–0.9** for normal edges -2. **Edge color should be light/bright enough to contrast the background**, not match it. -3. Use the variant's accent color for edges (same hue as node glow but slightly desaturated) +## Step 7 — Edges as tubes (from `threejs-geometry`) -```javascript -// CORRECT -new THREE.MeshBasicMaterial({ color: 0x1a5a8a, transparent: true, opacity: 0.75 }) - -// WRONG — invisible -new THREE.MeshBasicMaterial({ color: 0x1a3a4a, transparent: true, opacity: 0.45 }) -``` +Follow **Visibility Rules** above for all edge materials. Use the variant's accent color for edges (same hue as node glow but slightly desaturated). Tube radius: `0.035` (not 0.025 — thicker is more visible). @@ -179,12 +183,7 @@ Pulse sphere: `SphereGeometry(0.14, 16, 16)` — slightly larger than before (0. new THREE.MeshStandardMaterial({ color: accentColor, emissive: accentColor, emissiveIntensity: 4.0 }) ``` -4–5 trail ghosts, decreasing size and emissiveIntensity. Loop duration: 8 seconds. - -**Vary pulse color per variant:** -- Variant A: cyan `0x6ee7f7` -- Variant B: bright green `0x39ff14` -- Variant C: amber/gold `0xfbbf24` +4–5 trail ghosts, decreasing size and emissiveIntensity. Loop duration: 8 seconds. Apply variant-specific pulse colors. --- @@ -210,7 +209,9 @@ key.position.set(0, 6, 12); scene.add(key); ``` -**Vary lighting per variant** — different ambient and key light colors to set mood. +Vary ambient and key light colors per variant to set mood. + +**Checkpoint:** After adding bloom and lighting, verify the bloom effect is active — emissive nodes should have a visible glow halo in the rendered output. --- diff --git a/pdf-study-qa-skill/SKILL.md b/pdf-study-qa-skill/SKILL.md index a3c164f..44d0c93 100644 --- a/pdf-study-qa-skill/SKILL.md +++ b/pdf-study-qa-skill/SKILL.md @@ -1,60 +1,130 @@ -# PDF Study QA Skill +--- +name: pdf-study-qa +description: "Use when the user wants to read PDFs, analyze .pdf files, summarize PDF content, chat with a document, extract information from PDF pages, or ask questions about a PDF. Extracts text from PDF documents and answers questions based on the content." +--- + +# PDF Study QA + +Extract text from PDF documents and answer user questions based on the extracted content. The agent reads the PDF, chunks the text for large documents, and produces grounded answers with page references. + +## Step 1 — Extract text from the PDF + +Use `pdfplumber` to pull text page by page. The agent should verify each page returned content and warn the user about blank or image-only pages. + +```python +import pdfplumber + +def extract_pdf_text(pdf_path: str) -> list[dict]: + """Return a list of {page, text} dicts. Skips blank pages with a warning.""" + pages = [] + with pdfplumber.open(pdf_path) as pdf: + for i, page in enumerate(pdf.pages, start=1): + text = page.extract_text() or "" + text = text.strip() + if not text: + print(f"Warning: page {i} returned no text (may be scanned/image-only)") + continue + pages.append({"page": i, "text": text}) + if not pages: + raise RuntimeError( + f"No extractable text found in {pdf_path}. " + "The PDF may be scanned — consider running OCR first (e.g. ocrmypdf)." + ) + return pages +``` + +### Validation checklist -Study and ask questions about PDF documents using AI-powered analysis. +- Confirm `len(pages) > 0` before proceeding. +- If zero pages have text, surface the OCR suggestion to the user and stop. +- Log total pages extracted vs. total pages in the file so the user knows if any were skipped. -## Description +## Step 2 — Chunk for large documents -This skill enables you to upload PDF documents and ask questions about their content. It extracts text from PDFs and uses AI to provide answers based on the document's content. +When the combined text exceeds the context window, split into overlapping chunks so no answer is lost at a boundary. -## Usage +```python +def chunk_pages(pages: list[dict], max_chars: int = 12000, overlap: int = 500) -> list[dict]: + """Merge page texts into chunks that fit within max_chars, with overlap.""" + chunks = [] + current_text = "" + current_pages = [] -```bash -# Load the skill -/skill pdf-study-qa + for p in pages: + if len(current_text) + len(p["text"]) > max_chars and current_text: + chunks.append({"pages": list(current_pages), "text": current_text}) + # keep tail for overlap + current_text = current_text[-overlap:] + "\n" + p["text"] + current_pages = [current_pages[-1], p["page"]] + else: + current_text += ("\n" if current_text else "") + p["text"] + current_pages.append(p["page"]) -# Ask questions about a PDF -/study-pdf "What is the main topic of this document?" + if current_text: + chunks.append({"pages": list(current_pages), "text": current_text}) + return chunks ``` -## Features +## Step 3 — Answer questions with page citations + +Iterate through chunks, collect relevant excerpts, and synthesize a final answer. Always cite the page number(s) where evidence was found. -- **Text Extraction**: Automatically extracts text from PDF files -- **Contextual Q&A**: Ask questions and get answers based on document content -- **Multi-page Support**: Handles documents of any length -- **Citation Support**: Answers include references to specific pages/sections +```python +def answer_question(question: str, chunks: list[dict]) -> str: + """Find relevant chunks and compose an answer with page citations.""" + relevant = [] + q_lower = question.lower() -## Supported PDF Types + for chunk in chunks: + # Simple relevance check — in production the LLM handles this + if any(term in chunk["text"].lower() for term in q_lower.split()): + relevant.append(chunk) -- Research papers -- Books and textbooks -- Technical documentation -- Reports and whitepapers -- Legal documents -- Any text-based PDF + if not relevant: + return "No relevant content found in the document for that question." -## Workflow + context_parts = [] + for r in relevant: + page_label = ", ".join(str(p) for p in r["pages"]) + context_parts.append(f"[Pages {page_label}]\n{r['text']}") + + context = "\n---\n".join(context_parts) + # The agent uses this context block when formulating its response + return context +``` -1. **Upload**: Provide the path to your PDF file -2. **Process**: The skill extracts and indexes the text content -3. **Query**: Ask questions in natural language -4. **Answer**: Receive contextual answers with citations +The agent should present the answer in natural language and append citations like *(see page 4)* or *(pages 12–14)*. -## Example Queries +## Complete example — end-to-end pipeline -- "Summarize the key findings in chapter 3" -- "What are the main arguments presented?" -- "Find all mentions of 'machine learning'" -- "Compare the conclusions in sections 5 and 6" -- "Extract all tables and their data" +```python +import pdfplumber -## Requirements +pdf_path = "/path/to/document.pdf" +question = "What are the key findings in the results section?" -- Python 3.8+ -- PyPDF2 or pdfplumber for text extraction -- OpenAI API key or compatible LLM for Q&A +# 1. Extract +pages = extract_pdf_text(pdf_path) +print(f"Extracted {len(pages)} pages with text") + +# 2. Chunk +chunks = chunk_pages(pages, max_chars=12000) +print(f"Split into {len(chunks)} chunks") + +# 3. Retrieve context +context = answer_question(question, chunks) + +# 4. The agent reads `context` and formulates a grounded answer +# with page citations for the user. +print(context) +``` -## Notes +## Error handling reference -- Scanned PDFs (images) require OCR preprocessing -- Large documents may be processed in chunks -- Answers are based on extracted text, not visual layout +| Condition | Action | +|-----------|--------| +| File not found | Report the path back to the user and ask them to verify it | +| Zero pages extracted | Suggest OCR preprocessing (`ocrmypdf input.pdf output.pdf`) | +| Encoding errors | Try `page.extract_text()` with `pdfplumber`; fall back to `PyPDF2` if needed | +| PDF is password-protected | Inform the user; `pdfplumber.open(path, password=pw)` accepts a password argument | +| Single page exceeds chunk size | Pass it as its own chunk and warn about possible truncation | From ff79305af8e1d98cbd44a07d7af754bb62fc3c0f Mon Sep 17 00:00:00 2001 From: Yogi Date: Mon, 13 Apr 2026 18:19:08 +0530 Subject: [PATCH 2/2] ci: add skill-review GitHub Action for automated skill review on PRs --- .github/workflows/skill-review.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .github/workflows/skill-review.yml diff --git a/.github/workflows/skill-review.yml b/.github/workflows/skill-review.yml new file mode 100644 index 0000000..60c71a4 --- /dev/null +++ b/.github/workflows/skill-review.yml @@ -0,0 +1,13 @@ +name: Skill Review +on: + pull_request: + paths: ['**/SKILL.md'] +jobs: + review: + runs-on: ubuntu-latest + permissions: + pull-requests: write + contents: read + steps: + - uses: actions/checkout@v4 + - uses: tesslio/skill-review@22e928dd837202b2b1d1397e0114c92e0fae5ead # main \ No newline at end of file