CtrlK
BlogDocsLog inGet started
Tessl Logo

punkdev/cc2oc

Add and ship OpenCode support for one Claude Code plugin at a time. Includes core migration to a reviewable `opencode-plugin/` adapter, maintainer-facing docs, and follow-up CI/versioning setup for package publishing or skill-copy drift checks.

92

1.25x
Quality

92%

Does it follow best practices?

Impact

97%

1.25x

Average score across 2 eval scenarios

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

_common.pyskills/migrate-plugin/scripts/

#!/usr/bin/env python3
"""Shared helpers for migrate-plugin skill scripts."""

from __future__ import annotations

import json
import os
import re
from pathlib import Path
from typing import Any


IGNORED_DIRS = {
    ".git",
    ".hg",
    ".svn",
    "node_modules",
    "__pycache__",
    ".venv",
    "venv",
    "dist",
    "build",
    ".next",
    ".cache",
    ".tessl",
}

SKILL_NAME_RE = re.compile(r"^[a-z0-9]+(-[a-z0-9]+)*$")


def rel(path: Path, root: Path) -> str:
    try:
        return path.relative_to(root).as_posix()
    except ValueError:
        return path.as_posix()


def iter_files(root: Path):
    for dirpath, dirnames, filenames in os.walk(root):
        dirnames[:] = [d for d in dirnames if d not in IGNORED_DIRS]
        current = Path(dirpath)
        for filename in filenames:
            yield current / filename


def iter_skill_files(root: Path) -> list[Path]:
    return sorted(path for path in iter_files(root) if path.name == "SKILL.md")


def parse_frontmatter(path: Path) -> tuple[dict[str, str], list[str]]:
    errors: list[str] = []
    try:
        text = path.read_text(encoding="utf-8")
    except UnicodeDecodeError:
        text = path.read_text(errors="replace")
    lines = text.splitlines()
    if not lines or lines[0].strip() != "---":
        return {}, ["missing opening YAML frontmatter delimiter"]

    end = None
    for index, line in enumerate(lines[1:], start=1):
        if line.strip() == "---":
            end = index
            break
    if end is None:
        return {}, ["missing closing YAML frontmatter delimiter"]

    data: dict[str, str] = {}
    frontmatter = lines[1:end]
    index = 0
    while index < len(frontmatter):
        line = frontmatter[index]
        if not line.strip() or line.lstrip().startswith("#"):
            index += 1
            continue
        if ":" not in line or line.startswith((" ", "\t")):
            errors.append(f"unsupported frontmatter line: {line}")
            index += 1
            continue
        key, raw_value = line.split(":", 1)
        key = key.strip()
        value = raw_value.strip()
        if value in {">", "|-", "|", ">-"}:
            parts: list[str] = []
            index += 1
            while index < len(frontmatter) and frontmatter[index].startswith((" ", "\t")):
                parts.append(frontmatter[index].strip())
                index += 1
            data[key] = " ".join(parts).strip()
            continue
        data[key] = value.strip('"').strip("'")
        index += 1
    return data, errors


def print_result(result: dict[str, Any], output_format: str) -> None:
    if output_format == "json":
        print(json.dumps(result, indent=2, sort_keys=True))
        return
    print(markdown_result(result))


def markdown_result(result: dict[str, Any]) -> str:
    lines: list[str] = []
    title = result.get("title") or "Report"
    lines.append(f"# {title}")

    summary = result.get("summary")
    if isinstance(summary, dict) and summary:
        lines.append("")
        lines.append("## Summary")
        for key, value in summary.items():
            lines.append(f"- {key}: {value}")

    for section in ("assets", "checks", "warnings", "errors"):
        values = result.get(section)
        if not values:
            continue
        lines.append("")
        lines.append(f"## {section.title()}")
        if isinstance(values, list):
            for item in values:
                if isinstance(item, dict):
                    bits = [f"{key}={value}" for key, value in item.items()]
                    lines.append(f"- {'; '.join(bits)}")
                else:
                    lines.append(f"- {item}")

    return "\n".join(lines)

README.md

tile.json