返回 Skills 目录
lllllllama/rigorpilot-skills包含需要注意的行为

SKILL DETAIL

repo-intake-and-plan

lllllllama/rigorpilot-skills/repo-intake-and-plan

Rigor Intake helper for README-first deep learning repo reproduction. Use when the task is specifically to scan a repository, read the README and common project files, extract documented commands, classify inference, evaluation, and training candidates, and return the smallest trustworthy reproduction plan to the main orchestrator. Do not use for environment setup, asset download, command execution, final reporting, paper lookup, or end-to-end orchestration.

安装量 · 1,506查看来源

Installation

npx skills add https://github.com/lllllllama/rigorpilot-skills --skill repo-intake-and-plan

技能文件

SKILL.md

最近同步 · 2026年10月22日

agents/openai.yaml›
display_name: Rigor Intake
short_description: Rigor Intake helper for scanning a repo and recommending the smallest trustworthy reproduction target.
default_prompt: Scan this repository, read the README and common project files, extract documented commands, classify inference evaluation and training paths, and recommend the smallest trustworthy reproduction target.
references/repo-scan-rules.md›
# Repo Scan Rules

## Primary files

Always check these first when present:

- `README.md`
- `README`
- `requirements.txt`
- `environment.yml`
- `environment.yaml`
- `pyproject.toml`
- `setup.py`
- `setup.cfg`
- `Dockerfile`

## High-signal directories

Inspect for command or configuration clues:

- `configs/`
- `config/`
- `scripts/`
- `tools/`
- `examples/`
- `notebooks/`
- `checkpoints/`

## Extraction priorities

1. explicit README commands
2. setup instructions
3. documented inference or demo entrypoints
4. documented evaluation entrypoints
5. documented training entrypoints
6. config references and asset path hints

## Classification guidance

- `inference`: demo, predict, generate, sample, infer, test-time forward use
- `evaluation`: eval, validate, benchmark, score, reproduce metrics
- `training`: train, finetune, pretrain, launch long-running experiments
- `other`: install, download, preprocess, export, convert, utility

## Conservative behavior

- prefer explicit README evidence over filename guesses
- mark guessed classifications as inferred
- record ambiguity instead of overcommitting
scripts/extract_commands.py›
#!/usr/bin/env python3
"""Extract shell-like commands from README content and classify them."""

from __future__ import annotations

import argparse
import json
import re
from pathlib import Path
from typing import Dict, List, Optional


CODE_BLOCK_RE = re.compile(r"```(?P<lang>[^\n`]*)\n(?P<body>.*?)```", re.DOTALL | re.IGNORECASE)
# Bare ">" is deliberately excluded: it marks markdown blockquotes (prose),
# not shell prompts, and matching it turns README notes into commands.
INLINE_CMD_RE = re.compile(r"^\s*(?:\$|PS> )\s*(.+)$")
# Angle-bracket placeholders (<PATH/TO/DATASET>, <NUM_NODES>) mean the command
# cannot run verbatim: it needs researcher-supplied values first.
PLACEHOLDER_RE = re.compile(r"<[A-Za-z][A-Za-z0-9 _./:-]*>")
HEADING_RE = re.compile(r"^(?P<marks>#{1,6})\s+(?P<title>.+?)\s*$")
COMMAND_PREFIXES = (
    "python ",
    "python3 ",
    "pip ",
    "pip3 ",
    "conda ",
    "bash ",
    "sh ",
    "chmod ",
    "export ",
    "set ",
    "CUDA_VISIBLE_DEVICES=",
    "./",
    "accelerate ",
    "torchrun ",
    "deepspeed ",
    "make ",
    "docker ",
)


def collect_headings(readme_text: str) -> List[Dict[str, object]]:
    headings: List[Dict[str, object]] = []
    offset = 0
    inside_fence = False
    for line in readme_text.splitlines(keepends=True):
        if line.lstrip().startswith("```"):
            inside_fence = not inside_fence
            offset += len(line)
            continue
        if inside_fence:
            # "# comment" lines inside fenced code blocks are not headings.
            offset += len(line)
            continue
        matched = HEADING_RE.match(line.strip())
        if matched:
            headings.append(
                {
                    "offset": offset,
                    "level": len(matched.group("marks")),
                    "title": matched.group("title").strip(),
                }
            )
        offset += len(line)
    return headings


def nearest_heading(headings: List[Dict[str, object]], offset: int) -> Optional[str]:
    current: Optional[str] = None
    for heading in headings:
        if int(heading["offset"]) > offset:
            break
        current = str(heading["title"])
    return current


def infer_section_category(section: Optional[str]) -> Optional[str]:
    if not section:
        return None
    lowered = section.lower()
    # Training is the highest-risk interpretation. Check it before generic
    # headings such as "example" or "usage" so they cannot bypass training
    # authorization when both appear in the same title.
    if any(word in lowered for word in ["training", "train", "finetune", "fine-tune", "pretrain"]):
        return "training"
    if any(word in lowered for word in ["evaluation", "evaluate", "benchmark", "metrics", "validation"]) or re.search(
        r"\b(?:test|tests|testing)\b", lowered
    ):
        return "evaluation"
    if any(word in lowered for word in ["inference", "usage", "demo", "example", "text-to-image", "image-to-image", "transcribe"]):
        return "inference"
    return None


def infer_section_kind(section: Optional[str]) -> Optional[str]:
    if not section:
        return None
    lowered = section.lower()
    if any(word in lowered for word in ["install", "installation", "setup", "environment", "requirements"]):
        return "setup"
    if any(word in lowered for word in ["download", "checkpoint", "weights", "dataset", "data preparation"]):
        return "asset"
    if any(word in lowered for word in ["usage", "demo", "example", "inference", "evaluation", "training", "text-to-image", "image-to-image", "quick start", "quickstart", "getting started"]):
        return "run"
    return None


# The entrypoint script name is the strongest category signal: flags like
# --eval_iters on a train.py command must not flip training into evaluation
# (that would bypass the training-authorization gate downstream).
SCRIPT_CATEGORY_HINTS = [
    (re.compile(r"\b(?:pre)?train\w*\.py\b"), "training"),
    (re.compile(r"\b(?:eval\w*|benchmark\w*|validate|test)\.py\b"), "evaluation"),
    (re.compile(r"\b(?:sample|generate|infer\w*|predict|demo)\w*\.py\b"), "inference"),
]

SETUP_PREFIXES = (
    "pip install",
    "pip3 install",
    "conda install",
    "conda env create",
    "conda create",
    "conda activate",
    "python -m pip install",
    "git clone",
    "cd ",
)
ASSET_PREFIXES = ("wget ", "curl ", "mkdir ", "tar ", "unzip ", "7z ", "aria2c ")


def classify(command: str, section: Optional[str] = None) -> str:
    lowered = command.lower().strip()
    # Unambiguous setup/asset syntax outranks generic headings such as
    # "Basic Example", which otherwise makes installation look like inference.
    if lowered.startswith(SETUP_PREFIXES + ASSET_PREFIXES):
        return "other"

    section_category = infer_section_category(section)
    if section_category:
        return section_category

    for pattern, category in SCRIPT_CATEGORY_HINTS:
        if pattern.search(lowered):
            return category

    def any_word(words: List[str]) -> bool:
        return any(re.search(rf"\b{re.escape(word)}\b", lowered) for word in words)

    if any_word(["infer", "inference", "predict", "generate", "sample", "demo", "transcribe"]) or any(
        token in lowered for token in ["txt2img", "img2img", "whisper ", "amg.py"]
    ):
        return "inference"
    if any_word(["eval", "evaluate", "evaluation", "validation", "validate", "benchmark", "score", "pytest", "test"]):
        return "evaluation"
    if any_word(["train", "training", "finetune", "pretrain"]) or any(
        token in lowered for token in ["fine-tune", "pre-train"]
    ):
        return "training"
    return "other"


def command_kind(command: str, section: Optional[str] = None) -> str:
    lowered = command.lower().strip()
    if lowered.startswith(SETUP_PREFIXES):
        return "setup"
    if lowered.startswith(ASSET_PREFIXES):
        return "asset"

    section_kind = infer_section_kind(section)
    if section_kind:
        return section_kind
    if "--help" in lowered or " -h" in lowered:
        return "smoke"
    return "run"


def looks_like_command(line: str) -> bool:
    candidate = re.sub(r"^(?:\$|PS> )\s*", "", line.strip())
    if not candidate or candidate.startswith("#"):
        return False
    if candidate.startswith(("python", "pip", "conda", "bash", "sh", "make", "docker")):
        return True
    if candidate.startswith(COMMAND_PREFIXES):
        return True
    if re.search(r"\s--[A-Za-z0-9_-]+", candidate):
        return True
    if re.search(r"\b(?:python|pip|conda|torchrun|deepspeed|accelerate|bash|sh)\b", candidate):
        return True
    if re.search(r"[\\/].+\.(?:py|sh|bat)", candidate):
        return True
    if candidate.startswith(("cd ", "ls ", "mkdir ", "wget ", "curl ", "git ")):
        return True
    return False


def join_continuations(block: str) -> List[str]:
    """Join backslash-continued shell lines into single logical commands."""
    joined: List[str] = []
    buffer = ""
    for raw_line in block.splitlines():
        line = raw_line.strip()
        buffer = f"{buffer} {line}".strip() if buffer else line
        if buffer.endswith("\\"):
            buffer = buffer[:-1].rstrip()
            continue
        joined.append(buffer)
        buffer = ""
    if buffer:
        joined.append(buffer)
    return joined


def clean_lines(block: str) -> List[str]:
    commands: List[str] = []
    for line in join_continuations(block):
        if not line or line.startswith("#"):
            continue
        if not looks_like_command(line):
            continue
        line = re.sub(r"^(?:\$|PS> )\s*", "", line)
        commands.append(line)
    return commands


PYTHON_ENTRYPOINT_RE = re.compile(
    r"(?:^|\s)(?P<path>(?:(?:\.?\.?)[\\/])?(?:[A-Za-z0-9_.-]+[\\/])*[A-Za-z0-9_.-]+\.py)(?=\s|$)"
)
TRAINING_STRUCTURE_SIGNALS = [
    ("optimizer-step", re.compile(r"\boptimizer\s*\.\s*step\s*\(", re.IGNORECASE), 3),
    ("backward-pass", re.compile(r"\.\s*backward\s*\(", re.IGNORECASE), 3),
    ("model-train-mode", re.compile(r"\.\s*train\s*\(", re.IGNORECASE), 2),
    ("train-function", re.compile(r"\bdef\s+train\w*\s*\(", re.IGNORECASE), 1),
    ("epoch-loop", re.compile(r"\bfor\s+\w*epoch\w*\s+in\b", re.IGNORECASE), 1),
]


def referenced_python_script(command: str, readme_dir: Path) -> Optional[Path]:
    matched = PYTHON_ENTRYPOINT_RE.search(command)
    if not matched:
        return None
    root = readme_dir.resolve()
    candidate = (root / matched.group("path")).resolve()
    try:
        candidate.relative_to(root)
    except ValueError:
        return None
    if not candidate.is_file() or candidate.stat().st_size > 524_288:
        return None
    return candidate


def training_structure_evidence(script: Path) -> List[str]:
    try:
        content = script.read_text(encoding="utf-8", errors="replace")
    except OSError:
        return []
    evidence: List[str] = []
    score = 0
    for label, pattern, weight in TRAINING_STRUCTURE_SIGNALS:
        if pattern.search(content):
            evidence.append(label)
            score += weight
    return evidence if score >= 4 else []


def apply_entrypoint_structure(commands: List[Dict[str, str]], readme_dir: Optional[Path]) -> None:
    if readme_dir is None:
        return
    for item in commands:
        if item.get("kind") in {"setup", "asset"} or item.get("category") == "training":
            continue
        script = referenced_python_script(item["command"], readme_dir)
        if script is None:
            continue
        evidence = training_structure_evidence(script)
        if not evidence:
            continue
        item["classification_previous_category"] = item["category"]
        item["category"] = "training"
        item["classification_source"] = "entrypoint-structure"
        item["classification_evidence"] = evidence


def extract_commands(readme_text: str, readme_dir: Optional[Path] = None) -> Dict[str, object]:
    commands: List[Dict[str, str]] = []
    warnings: List[str] = []
    seen = set()
    headings = collect_headings(readme_text)

    for match in CODE_BLOCK_RE.finditer(readme_text):
        lang = (match.group("lang") or "").strip().lower()
        if lang and lang not in {"bash", "shell", "sh", "zsh", "powershell", "cmd"}:
            continue

        section = nearest_heading(headings, match.start())
        lines = clean_lines(match.group("body"))
        if not lines:
            continue

        for line in lines:
            if line not in seen:
                commands.append(
                    {
                        "command": line,
                        "category": classify(line, section),
                        "kind": command_kind(line, section),
                        "section": section,
                        "source": "code_block",
                        "needs_substitution": bool(PLACEHOLDER_RE.search(line)),
                    }
                )
                seen.add(line)

    running_offset = 0
    for line in readme_text.splitlines(keepends=True):
        matched = INLINE_CMD_RE.match(line)
        if not matched:
            running_offset += len(line)
            continue
        command = matched.group(1).strip()
        if not looks_like_command(command):
            running_offset += len(line)
            continue
        section = nearest_heading(headings, running_offset)
        if command and command not in seen:
            commands.append(
                {
                    "command": command,
                    "category": classify(command, section),
                    "kind": command_kind(command, section),
                    "section": section,
                    "source": "inline",
                    "needs_substitution": bool(PLACEHOLDER_RE.search(command)),
                }
            )
            seen.add(command)
        running_offset += len(line)

    apply_entrypoint_structure(commands, readme_dir)

    if not commands:
        warnings.append("No shell-like commands were extracted from the README.")

    counts: Dict[str, int] = {}
    for item in commands:
        category = item["category"]
        counts[category] = counts.get(category, 0) + 1

    return {
        "commands": commands,
        "counts": counts,
        "warnings": warnings,
    }


def main() -> int:
    parser = argparse.ArgumentParser(description="Extract shell-like commands from a README.")
    parser.add_argument("--readme", required=True, help="Path to the README file.")
    parser.add_argument("--json", action="store_true", help="Emit JSON output.")
    args = parser.parse_args()

    readme_path = Path(args.readme)
    text = readme_path.read_text(encoding="utf-8", errors="replace")
    data = extract_commands(text, readme_path.parent)

    if args.json:
        print(json.dumps(data, indent=2, ensure_ascii=False))
    else:
        for item in data["commands"]:
            print(f"[{item['category']}] {item['command']}")
        if data["warnings"]:
            print("Warnings:")
            for warning in data["warnings"]:
                print(f"- {warning}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
scripts/scan_repo.py›
#!/usr/bin/env python3
"""Scan a repository for README-first reproduction signals."""

from __future__ import annotations

import argparse
import json
from datetime import datetime, timezone
from pathlib import Path
from typing import Dict, List, Optional


KEY_FILES = [
    "README.md",
    "README",
    "requirements.txt",
    "environment.yml",
    "environment.yaml",
    "pyproject.toml",
    "setup.py",
    "setup.cfg",
    "Dockerfile",
]

SIGNAL_DIRS = [
    "configs",
    "config",
    "scripts",
    "tools",
    "examples",
    "notebooks",
    "checkpoints",
]


def first_existing(root: Path, names: List[str]) -> Optional[Path]:
    for name in names:
        candidate = root / name
        if candidate.exists():
            return candidate
    return None


def scan_repo(root: Path) -> Dict[str, object]:
    if not root.exists():
        raise FileNotFoundError(f"Repository path does not exist: {root}")

    top_level = sorted(item.name for item in root.iterdir())
    detected_files = [name for name in KEY_FILES if (root / name).exists()]
    detected_dirs = [name for name in SIGNAL_DIRS if (root / name).exists()]
    readme = first_existing(root, ["README.md", "README"])

    warnings: List[str] = []
    if readme is None:
        warnings.append("No README file was found at the repository root.")
    if not detected_files:
        warnings.append("No common environment or packaging files were detected.")

    return {
        "generated_at": datetime.now(timezone.utc).isoformat(),
        "repo_path": str(root.resolve()),
        "readme_path": str(readme.resolve()) if readme else None,
        "detected_files": detected_files,
        "detected_dirs": detected_dirs,
        "structure": {
            "top_level": top_level,
            "top_level_file_count": sum(1 for item in root.iterdir() if item.is_file()),
            "top_level_dir_count": sum(1 for item in root.iterdir() if item.is_dir()),
        },
        "warnings": warnings,
    }


def main() -> int:
    parser = argparse.ArgumentParser(description="Scan a repository for key reproduction signals.")
    parser.add_argument("--repo", required=True, help="Path to the target repository.")
    parser.add_argument("--json", action="store_true", help="Emit JSON instead of a human summary.")
    args = parser.parse_args()

    data = scan_repo(Path(args.repo))
    if args.json:
        print(json.dumps(data, indent=2, ensure_ascii=False))
    else:
        print(f"Repository: {data['repo_path']}")
        print(f"README: {data['readme_path'] or 'not found'}")
        print("Detected files:", ", ".join(data["detected_files"]) or "none")
        print("Detected dirs:", ", ".join(data["detected_dirs"]) or "none")
        if data["warnings"]:
            print("Warnings:")
            for item in data["warnings"]:
                print(f"- {item}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
SKILL.md›
---
name: repo-intake-and-plan
description: Rigor Intake helper for README-first deep learning repo reproduction. Use when the task is specifically to scan a repository, read the README and common project files, extract documented commands, classify inference, evaluation, and training candidates, and return the smallest trustworthy reproduction plan to the main orchestrator. Do not use for environment setup, asset download, command execution, final reporting, paper lookup, or end-to-end orchestration.
---

# repo-intake-and-plan

Use this as the Rigor Intake helper. The installed slug remains
`repo-intake-and-plan` for compatibility.

## When to apply

- At the beginning of README-first reproduction work.
- When the main skill needs a fast map of repo structure and documented commands.
- When inference, evaluation, and training candidates must be classified conservatively.
- When the user explicitly wants to inspect the repo first and not run anything yet.

## When not to apply

- When execution has already started and the task is now about running commands or writing outputs.
- When the target is not a repository-backed reproduction task.
- When the user only wants paper interpretation without repo inspection.
- When the user already has a selected documented command and only needs setup or execution.

## Clear boundaries

- This skill scans and plans.
- This skill is helper-tier and should usually be orchestrator-invoked.
- It does not install environments.
- It does not prepare large assets.
- It does not execute substantive reproduction commands.
- It does not decide high-risk patching.

## Input expectations

- Target repository path.
- Access to README and common project files if present.
- Optional user hints about desired priority, such as inference-first.

## Output expectations

- concise repo structure summary
- documented command inventory
- inferred candidate categories: inference, evaluation, training, other
- minimum trustworthy reproduction recommendation
- notable ambiguity or risk list

## Notes

Use `references/repo-scan-rules.md` and helper scripts under `scripts/`.