From 84fcc29398fe99804abe1dd4cd88863ad3d894e1 Mon Sep 17 00:00:00 2001 From: Derek Palmer Date: Sun, 24 May 2026 16:49:38 -0400 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20multi-agent=20skill=20packaging=20?= =?UTF-8?q?=E2=80=94=2012=20slash=20commands=20+=20install.sh/ps1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each forerunner task becomes a native slash command for Claude Code, Codex, and Gemini CLI. Authentication flows through the user's existing agent subscription; no API key required. New skills (skills/ → plugins/codeforerunner/skills/): /forerunner-scan /forerunner-readme /forerunner-api-docs /forerunner-audit /forerunner-changelog /forerunner-check /forerunner-diagrams /forerunner-flows /forerunner-init /forerunner-review /forerunner-stack-docs /forerunner-version-audit New files: install.sh / install.ps1 — detect claude/codex/gemini, install all skills; --dry-run, --only, --list, --uninstall GEMINI.md — Gemini CLI autodiscovery + task table .codex/config.toml — Codex skills integration flag .claude-plugin/marketplace.json — Claude Code marketplace entry installer.py: TASK_SKILL_SLUGS tuple, resolve_skill_target(), install_all_skills(), --all flag on `forerunner install` README: restructured around two modes (agent skill vs direct API), slash-command table, install one-liners, curl|bash instructions. Co-Authored-By: Claude Sonnet 4.6 --- .claude-plugin/marketplace.json | 17 ++ .codex/config.toml | 5 + GEMINI.md | 48 ++++ README.md | 188 +++++++------ install.ps1 | 212 +++++++++++++++ install.sh | 256 ++++++++++++++++++ .../skills/forerunner-api-docs/SKILL.md | 33 +++ .../skills/forerunner-audit/SKILL.md | 32 +++ .../skills/forerunner-changelog/SKILL.md | 31 +++ .../skills/forerunner-check/SKILL.md | 37 +++ .../skills/forerunner-diagrams/SKILL.md | 32 +++ .../skills/forerunner-flows/SKILL.md | 33 +++ .../skills/forerunner-init/SKILL.md | 33 +++ .../skills/forerunner-readme/SKILL.md | 39 +++ .../skills/forerunner-review/SKILL.md | 30 ++ .../skills/forerunner-scan/SKILL.md | 37 +++ .../skills/forerunner-stack-docs/SKILL.md | 32 +++ .../skills/forerunner-version-audit/SKILL.md | 32 +++ skills/forerunner-api-docs/SKILL.md | 33 +++ skills/forerunner-audit/SKILL.md | 32 +++ skills/forerunner-changelog/SKILL.md | 31 +++ skills/forerunner-check/SKILL.md | 37 +++ skills/forerunner-diagrams/SKILL.md | 32 +++ skills/forerunner-flows/SKILL.md | 33 +++ skills/forerunner-init/SKILL.md | 33 +++ skills/forerunner-readme/SKILL.md | 39 +++ skills/forerunner-review/SKILL.md | 30 ++ skills/forerunner-scan/SKILL.md | 37 +++ skills/forerunner-stack-docs/SKILL.md | 32 +++ skills/forerunner-version-audit/SKILL.md | 32 +++ src/codeforerunner/installer.py | 93 ++++++- 31 files changed, 1543 insertions(+), 78 deletions(-) create mode 100644 .claude-plugin/marketplace.json create mode 100644 .codex/config.toml create mode 100644 GEMINI.md create mode 100644 install.ps1 create mode 100755 install.sh create mode 100644 plugins/codeforerunner/skills/forerunner-api-docs/SKILL.md create mode 100644 plugins/codeforerunner/skills/forerunner-audit/SKILL.md create mode 100644 plugins/codeforerunner/skills/forerunner-changelog/SKILL.md create mode 100644 plugins/codeforerunner/skills/forerunner-check/SKILL.md create mode 100644 plugins/codeforerunner/skills/forerunner-diagrams/SKILL.md create mode 100644 plugins/codeforerunner/skills/forerunner-flows/SKILL.md create mode 100644 plugins/codeforerunner/skills/forerunner-init/SKILL.md create mode 100644 plugins/codeforerunner/skills/forerunner-readme/SKILL.md create mode 100644 plugins/codeforerunner/skills/forerunner-review/SKILL.md create mode 100644 plugins/codeforerunner/skills/forerunner-scan/SKILL.md create mode 100644 plugins/codeforerunner/skills/forerunner-stack-docs/SKILL.md create mode 100644 plugins/codeforerunner/skills/forerunner-version-audit/SKILL.md create mode 100644 skills/forerunner-api-docs/SKILL.md create mode 100644 skills/forerunner-audit/SKILL.md create mode 100644 skills/forerunner-changelog/SKILL.md create mode 100644 skills/forerunner-check/SKILL.md create mode 100644 skills/forerunner-diagrams/SKILL.md create mode 100644 skills/forerunner-flows/SKILL.md create mode 100644 skills/forerunner-init/SKILL.md create mode 100644 skills/forerunner-readme/SKILL.md create mode 100644 skills/forerunner-review/SKILL.md create mode 100644 skills/forerunner-scan/SKILL.md create mode 100644 skills/forerunner-stack-docs/SKILL.md create mode 100644 skills/forerunner-version-audit/SKILL.md diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json new file mode 100644 index 0000000..5a49cd8 --- /dev/null +++ b/.claude-plugin/marketplace.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://anthropic.com/claude-code/marketplace.schema.json", + "name": "codeforerunner", + "description": "Repo documentation tooling for Claude Code. Adds /forerunner-scan, /forerunner-readme, /forerunner-check, and 9 other documentation slash commands.", + "owner": { + "name": "Derek Palmer", + "url": "https://github.com/derek-palmer" + }, + "plugins": [ + { + "name": "codeforerunner", + "description": "Model-agnostic repo documentation prompts. Slash commands for scan, readme, api-docs, diagrams, flows, stack-docs, version-audit, check, review, audit, changelog, and agent onboarding.", + "source": "./", + "category": "productivity" + } + ] +} diff --git a/.codex/config.toml b/.codex/config.toml new file mode 100644 index 0000000..27f2e0e --- /dev/null +++ b/.codex/config.toml @@ -0,0 +1,5 @@ +# codeforerunner Codex integration +# Skills installed here are available as Codex slash commands. + +[features] +skills = true diff --git a/GEMINI.md b/GEMINI.md new file mode 100644 index 0000000..5933de3 --- /dev/null +++ b/GEMINI.md @@ -0,0 +1,48 @@ +# codeforerunner — Gemini CLI context + +Model-agnostic repository documentation tooling. Ships prompt packs for codebase analysis, doc generation, drift detection, and agent onboarding. Python CLI + MCP server + agent skills. + +## Available tasks + +Run any task via `forerunner doc `, or use the installed Gemini extension skills: + +| Slash command | Task | Description | +|---|---|---| +| `/forerunner-scan` | `scan` | Scan repo; always run first | +| `/forerunner-readme` | `readme` | Generate or refresh README.md | +| `/forerunner-api-docs` | `api-docs` | Generate API reference | +| `/forerunner-diagrams` | `diagrams` | Generate Mermaid architecture diagrams | +| `/forerunner-flows` | `flows` | Document system flows | +| `/forerunner-stack-docs` | `stack-docs` | Stack-specific developer docs | +| `/forerunner-version-audit` | `version-audit` | Audit pinned versions vs EOL | +| `/forerunner-check` | `check` | Check docs for staleness | +| `/forerunner-review` | `review` | Doc-impact summary for PR review | +| `/forerunner-audit` | `audit` | Security and dependency audit | +| `/forerunner-changelog` | `changelog` | Generate changelog entry from git log | +| `/forerunner-init` | `init-agent-onboarding` | Bootstrap or refresh AGENTS.md | + +## Workflow + +1. Start with `/forerunner-scan` to collect repo evidence. +2. Run the documentation task you need. +3. Use `/forerunner-check` before commits to detect drift. + +## CLI quick reference + +```bash +forerunner doc # Get composed prompt for a task +forerunner generate # Call configured provider directly +forerunner check # Run drift-detection rules +forerunner doctor # Health report +forerunner install gemini # Install skills to Gemini config dir +``` + +## Config + +Drop a `forerunner.config.yaml` at repo root to enable drift rules. Run `forerunner doctor --fix` to generate a starter config. + +## Sources + +- Prompts: `src/codeforerunner/prompts/tasks/` +- Skills: `skills/` (source) → `plugins/codeforerunner/skills/` (distribution) +- Repo: https://github.com/derek-palmer/codeforerunner diff --git a/README.md b/README.md index a93bb84..204ab95 100644 --- a/README.md +++ b/README.md @@ -2,115 +2,160 @@ # codeForerunner -Model-agnostic repository documentation tooling. Ships a prompt pack for codebase analysis and doc generation, a thin Python CLI, an MCP server, a Codex marketplace plugin, and drift-detection rules that keep docs honest. +Model-agnostic repository documentation tooling. Ships a prompt pack for codebase analysis and doc generation, a thin Python CLI, an MCP server, drift-detection rules that keep docs honest — and native slash-command skills for Claude Code, Codex, Gemini CLI, and other agent CLIs. -## Install +## Two modes + +### Mode A — Agent skill (recommended, no API key required) + +Install forerunner's prompt pack as skills into your agent CLI. Each documentation task becomes a slash command (`/forerunner-readme`, `/forerunner-check`, etc.) available inside Claude Code, Codex, Gemini CLI, and other agents. Authentication is handled by your existing agent subscription — no separate API key needed. + +```bash +# From a cloned repo +./install.sh + +# One-liner (auto-detects Claude Code, Codex, Gemini CLI) +curl -fsSL https://raw.githubusercontent.com/derek-palmer/codeforerunner/main/install.sh | bash + +# Windows +irm https://raw.githubusercontent.com/derek-palmer/codeforerunner/main/install.ps1 | iex + +# Via forerunner CLI (after pip install) +forerunner install --all claude +forerunner install --all codex +``` + +Then in your agent: + +``` +/forerunner-scan ← scan the repo first +/forerunner-readme ← generate README +/forerunner-check ← detect doc drift +``` + +### Mode B — Direct API (needs API key or Ollama) + +Install the Python CLI and call your provider directly. Works without any agent CLI installed. ```bash -pipx install codeforerunner # recommended — isolated environment +pipx install codeforerunner # recommended pip install codeforerunner # alternative ``` -From source: +Configure a provider (or start Ollama for keyless local generation): ```bash -git clone https://github.com/derek-palmer/codeforerunner -cd codeforerunner -python -m pip install -e . +export ANTHROPIC_API_KEY=sk-... +forerunner generate readme --stream ``` -Verify: `forerunner --help` +If no API key and no `--provider` flag, forerunner auto-detects Ollama at `localhost:11434` and falls back to local mode. + +## Slash commands + +| Command | Task | Purpose | +|---------|------|---------| +| `/forerunner-scan` | `scan` | Collect repo evidence (run first) | +| `/forerunner-readme` | `readme` | Generate or refresh README.md | +| `/forerunner-api-docs` | `api-docs` | Generate API reference docs | +| `/forerunner-diagrams` | `diagrams` | Generate Mermaid architecture diagrams | +| `/forerunner-flows` | `flows` | Document system flows | +| `/forerunner-stack-docs` | `stack-docs` | Stack-specific developer docs | +| `/forerunner-version-audit` | `version-audit` | Audit pinned versions vs EOL | +| `/forerunner-check` | `check` | Check docs for staleness | +| `/forerunner-review` | `review` | Doc-impact summary for PR review | +| `/forerunner-audit` | `audit` | Security and dependency audit | +| `/forerunner-changelog` | `changelog` | Generate changelog from git log | +| `/forerunner-init` | `init-agent-onboarding` | Bootstrap or refresh AGENTS.md | + +Slash command availability depends on the agent CLI. Claude Code, Codex, and Gemini CLI support all 12 commands after install. + +## Skill install options + +| Flag | Effect | +|------|--------| +| `./install.sh` | Auto-detect all agents, install all skills | +| `./install.sh --only claude` | Claude Code only | +| `./install.sh --only codex` | Codex only | +| `./install.sh --only gemini` | Gemini CLI only | +| `./install.sh --dry-run` | Preview, write nothing | +| `./install.sh --list` | Show detected agents + skill list | +| `./install.sh --uninstall` | Remove all installed skills | ## CLI +```bash +pip install codeforerunner +``` + | Command | Purpose | |---------|---------| -| `forerunner init` | Resolve agent-onboarding bundle to stdout (`--full` prepends scan; `--agents-only` is the default scope). | +| `forerunner init` | Resolve agent-onboarding bundle to stdout. | | `forerunner scan` | Resolve scan bundle to stdout. | | `forerunner doc ` | Resolve `base + partials + task` bundle to stdout. | -| `forerunner check` | Run drift-detection rules; silent no-op without `forerunner.config.yaml`. | -| `forerunner generate ` | Resolve bundle for `` and call the configured provider. Add `--stream` to stream output token-by-token. | -| `forerunner doctor` | Single-screen health report: skill parity, marketplace validation, installed destinations, config, provider key. Add `--fix` to write a starter `forerunner.config.yaml` if absent. | +| `forerunner check` | Run drift-detection rules; no-op without `forerunner.config.yaml`. | +| `forerunner generate ` | Call configured provider directly. Add `--stream` for token-by-token output. Falls back to Ollama automatically when no API key is configured. | +| `forerunner doctor` | Health report: skill parity, config, provider key, local-mode status. Add `--fix` to write a starter config. | | `forerunner mcp-server` | Serve prompt bundles as MCP tools over stdio (JSON-RPC 2.0). | -| `forerunner install ` | Idempotently write the canonical skill into agent-specific directories. | +| `forerunner install ` | Install canonical skill into agent-specific directory. Add `--all` for all per-task skills. | -## Prompt Pack +## Prompt pack Prompts are bundled inside the package at `src/codeforerunner/prompts/`. ```text prompts/ -├── system/ -│ └── base.md +├── system/base.md ├── partials/ │ ├── context-format.md │ ├── output-rules.md │ └── stack-hints.md └── tasks/ - ├── scan.md - ├── init-agent-onboarding.md - ├── readme.md - ├── api-docs.md - ├── stack-docs.md - ├── diagrams.md - ├── flows.md - ├── version-audit.md - ├── check.md - ├── review.md - ├── audit.md - └── changelog.md + ├── scan.md api-docs.md audit.md + ├── readme.md diagrams.md changelog.md + ├── check.md flows.md version-audit.md + ├── review.md stack-docs.md + └── init-agent-onboarding.md ``` -| Task | Purpose | -|------|---------| -| `scan` | Structured repo scan used by downstream tasks. | -| `init-agent-onboarding` | Generates or updates `AGENTS.md` from repo evidence. | -| `readme` | Generates or rewrites a top-level README. | -| `api-docs` | Documents public APIs. | -| `stack-docs` | Documents stack-specific areas. | -| `diagrams` | Generates Mermaid architecture or flow diagrams. | -| `flows` | Documents user, request, job, or data flows. | -| `version-audit` | Audits pinned versions from manifests, lockfiles, workflows, IaC. | -| `check` | Checks existing docs for staleness against a fresh scan. | -| `review` | Summarizes documentation impact for review. | -| `audit` | Security and dependency audit report. | -| `changelog` | Generates a Keep-a-Changelog entry from git log. | - -## Quick Start +## Quick start (agent skill mode) ```bash -# 1. Point your agent at the scan prompt -forerunner scan +# Install skills into Claude Code +curl -fsSL https://raw.githubusercontent.com/derek-palmer/codeforerunner/main/install.sh | bash -# 2. Generate or update docs for a task -export FORERUNNER_SCAN_DONE=1 -forerunner doc readme - -# 3. Direct model call (needs provider config) -forerunner generate readme --stream +# In Claude Code: +# /forerunner-scan → scans your repo +# /forerunner-readme → generates README.md +# /forerunner-check → checks for doc drift ``` -## GitHub Action +## Quick start (direct API mode) -Use forerunner check as a reusable action in any workflow: +```bash +# 1. Install and configure +pip install codeforerunner +export ANTHROPIC_API_KEY=sk-... -```yaml -- uses: derek-palmer/codeforerunner@v0.3.2 +# 2. Run a task +forerunner generate readme --stream + +# 3. Enable drift detection +forerunner doctor --fix # writes forerunner.config.yaml +forerunner check # run any time or as pre-commit hook ``` -With a pinned version: +## GitHub Action ```yaml - uses: derek-palmer/codeforerunner@v0.3.2 - with: - version: '0.3.2' ``` No-op when `forerunner.config.yaml` is absent. ## Configuration -Copy `forerunner.config.yaml.example` to `forerunner.config.yaml` to opt in. Without that file, `forerunner check` is a silent no-op. Generate a starter config with: +Copy `forerunner.config.yaml.example` to `forerunner.config.yaml` to opt in to drift rules. Generate a starter config with: ```bash forerunner doctor --fix @@ -122,7 +167,7 @@ forerunner doctor --fix provider: anthropic # anthropic | openai | google | ollama model: claude-opus-4-7 api_key_env: - anthropic: ANTHROPIC_API_KEY # override per-provider env var name + anthropic: ANTHROPIC_API_KEY tasks: check: @@ -134,10 +179,10 @@ tasks: - R5-no-python-package - R7-no-mcp - R8-no-marketplace - - RI1-missing-cli # inverse: doc claims CLI but file absent + - RI1-missing-cli - RI5-missing-python-package - RI7-missing-mcp - - RV1-version-drift # pinned version in docs ≠ pyproject.toml + - RV1-version-drift ignore_paths: - docs/legacy/**/*.md ``` @@ -159,7 +204,7 @@ tasks: | `RI7-missing-mcp` | Doc references `forerunner mcp-server` but `mcp_server.py` absent | | `RV1-version-drift` | Doc pins `codeforerunner==X.Y.Z` differing from current version | -### MCP Server +## MCP Server `forerunner mcp-server` speaks JSON-RPC 2.0 over stdio and exposes one tool per `prompts/tasks/*.md`. A scan-first gate enforces SPEC V2: any tool except `scan` or `init-agent-onboarding` returns an error until `scan` has been called in the same session. @@ -167,7 +212,7 @@ See `examples/mcp/` for Claude Desktop and mcp-cli wiring examples. ## Providers -`forerunner generate` supports four providers. Set the appropriate env var before calling: +`forerunner generate` supports four providers. When no provider is explicitly configured and no API key is found, forerunner probes `localhost:11434` and falls back to Ollama automatically. | Provider | Env var | Default model | |----------|---------|---------------| @@ -176,20 +221,11 @@ See `examples/mcp/` for Claude Desktop and mcp-cli wiring examples. | `google` | `GOOGLE_API_KEY` | `gemini-2.5-pro` | | `ollama` | `OLLAMA_HOST` (optional) | `llama3` | -## Codex Plugin - -```bash -forerunner install codex --marketplace -``` - -Installs the Codex marketplace entry and skill. Or install manually: -`forerunner install ` copies the canonical skill into the agent-specific directory. - -## Docs and Spec +## Docs and spec - `SPEC.md` — canonical phase/task tracker - `docs/getting-started.md` — manual prompt use - `docs/prompt-guide.md` — how system, partial, and task prompts compose - `docs/editor-agent-setup.md` — adapting prompts to local agents - `docs/roadmap.md` — human-readable roadmap -- `docs/agent-distribution-design.md` — design backing Codex/Claude packages +- `docs/agent-distribution-design.md` — packaging and installer design diff --git a/install.ps1 b/install.ps1 new file mode 100644 index 0000000..340f755 --- /dev/null +++ b/install.ps1 @@ -0,0 +1,212 @@ +# codeforerunner skill installer (Windows PowerShell) +# Detects installed agent CLIs and drops forerunner skills into each one. +# +# Usage: +# .\install.ps1 # auto-detect all agents +# .\install.ps1 -Only claude +# .\install.ps1 -Only codex +# .\install.ps1 -DryRun +# .\install.ps1 -List +# .\install.ps1 -Uninstall + +param( + [switch]$DryRun, + [switch]$Uninstall, + [switch]$List, + [string[]]$Only = @() +) + +$ErrorActionPreference = "Stop" + +$RepoOwner = "derek-palmer" +$RepoName = "codeforerunner" +$RawBase = "https://raw.githubusercontent.com/$RepoOwner/$RepoName/main" +$GitHubUrl = "https://github.com/$RepoOwner/$RepoName" + +$SkillSlugs = @( + "codeforerunner", + "forerunner-scan", + "forerunner-readme", + "forerunner-api-docs", + "forerunner-audit", + "forerunner-changelog", + "forerunner-check", + "forerunner-diagrams", + "forerunner-flows", + "forerunner-init", + "forerunner-review", + "forerunner-stack-docs", + "forerunner-version-audit" +) + +# ── detect source ───────────────────────────────────────────────────────────── + +$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$LocalSkills = Join-Path $ScriptDir "plugins\codeforerunner\skills" +$IsLocal = Test-Path $LocalSkills + +# ── helpers ─────────────────────────────────────────────────────────────────── + +function Log { param($msg) Write-Host " $msg" } +function Ok { param($msg) Write-Host " ✓ $msg" -ForegroundColor Green } +function Skip { param($msg) Write-Host " – $msg (skipped)" -ForegroundColor DarkGray } +function Err { param($msg) Write-Host " ✗ $msg" -ForegroundColor Red } + +function ShouldInstall($agent) { + $Only.Count -eq 0 -or $Only -contains $agent +} + +function SkillDestClaude($slug) { + "$env:USERPROFILE\.claude\plugins\codeforerunner\skills\$slug\SKILL.md" +} + +function SkillDestCodex($slug) { + "$env:USERPROFILE\.codex\skills\$slug\SKILL.md" +} + +function CopySkill($slug, $dest) { + if ($IsLocal) { + $src = Join-Path $LocalSkills "$slug\SKILL.md" + if (-not (Test-Path $src)) { Err "source not found: $src"; return $false } + if (-not $DryRun) { + New-Item -ItemType Directory -Force -Path (Split-Path $dest) | Out-Null + Copy-Item $src $dest -Force + } + } else { + $url = "$RawBase/plugins/codeforerunner/skills/$slug/SKILL.md" + if (-not $DryRun) { + New-Item -ItemType Directory -Force -Path (Split-Path $dest) | Out-Null + Invoke-WebRequest -Uri $url -OutFile $dest -UseBasicParsing + } + } + return $true +} + +function HasCommand($name) { + $null -ne (Get-Command $name -ErrorAction SilentlyContinue) +} + +# ── detection ───────────────────────────────────────────────────────────────── + +$HasClaude = HasCommand "claude" +$HasCodex = HasCommand "codex" +$HasGemini = HasCommand "gemini" + +# ── list mode ───────────────────────────────────────────────────────────────── + +if ($List) { + Write-Host "codeforerunner skill installer — agent detection:" + Write-Host "" + Write-Host (" {0,-12} {1}" -f "claude", $(if ($HasClaude) {"detected ✓"} else {"not found"})) + Write-Host (" {0,-12} {1}" -f "codex", $(if ($HasCodex) {"detected ✓"} else {"not found"})) + Write-Host (" {0,-12} {1}" -f "gemini", $(if ($HasGemini) {"detected ✓"} else {"not found"})) + Write-Host "" + Write-Host "Skills ($($SkillSlugs.Count)):" + foreach ($s in $SkillSlugs) { Write-Host " /$s" } + exit 0 +} + +# ── uninstall ───────────────────────────────────────────────────────────────── + +if ($Uninstall) { + Write-Host "codeforerunner — uninstalling skills" + foreach ($slug in $SkillSlugs) { + if (ShouldInstall "claude") { + $d = SkillDestClaude $slug + if (Test-Path $d) { if (-not $DryRun) { Remove-Item $d -Force }; Ok "removed $d" } + } + if (ShouldInstall "codex") { + $d = SkillDestCodex $slug + if (Test-Path $d) { if (-not $DryRun) { Remove-Item $d -Force }; Ok "removed $d" } + } + } + Write-Host "done" + exit 0 +} + +# ── install ─────────────────────────────────────────────────────────────────── + +$Installed = @() +$Skipped = @() + +Write-Host "codeforerunner — installing skills" +Write-Host "" +if ($DryRun) { Write-Host " (dry-run — no files written)" } +Write-Host "" + +# Claude Code +if (ShouldInstall "claude") { + if ($HasClaude -or $Only.Count -gt 0) { + Write-Host "Claude Code:" + if (-not $DryRun) { + $pluginDir = "$env:USERPROFILE\.claude\plugins\codeforerunner" + New-Item -ItemType Directory -Force -Path $pluginDir | Out-Null + $manifestSrc = if ($IsLocal) { Join-Path $ScriptDir ".claude-plugin\plugin.json" } else { "$RawBase/.claude-plugin/plugin.json" } + if ($IsLocal) { Copy-Item $manifestSrc "$pluginDir\plugin.json" -Force } + else { Invoke-WebRequest -Uri $manifestSrc -OutFile "$pluginDir\plugin.json" -UseBasicParsing } + } + foreach ($slug in $SkillSlugs) { + $dest = SkillDestClaude $slug + if (CopySkill $slug $dest) { Ok $dest; $Installed += "claude/$slug" } + } + } else { + Skip "claude (not detected; use -Only claude to force)" + $Skipped += "claude" + } + Write-Host "" +} + +# Codex +if (ShouldInstall "codex") { + if ($HasCodex -or $Only.Count -gt 0) { + Write-Host "Codex CLI:" + foreach ($slug in $SkillSlugs) { + $dest = SkillDestCodex $slug + if (CopySkill $slug $dest) { Ok $dest; $Installed += "codex/$slug" } + } + } else { + Skip "codex (not detected; use -Only codex to force)" + $Skipped += "codex" + } + Write-Host "" +} + +# Gemini — delegates to native extension install +if (ShouldInstall "gemini") { + if ($HasGemini -or $Only.Count -gt 0) { + Write-Host "Gemini CLI:" + if (-not $DryRun) { + try { + & gemini extensions install $GitHubUrl + Ok "installed via gemini extensions install" + $Installed += "gemini" + } catch { + Err "gemini extensions install failed: $_" + } + } else { + Log "would run: gemini extensions install $GitHubUrl" + } + } else { + Skip "gemini (not detected; use -Only gemini to force)" + $Skipped += "gemini" + } + Write-Host "" +} + +# ── summary ─────────────────────────────────────────────────────────────────── + +Write-Host "Summary:" +if ($Installed.Count -gt 0) { + $agents = ($Installed | ForEach-Object { $_.Split('/')[0] } | Sort-Object -Unique) -join " " + Write-Host " installed for: $agents" +} +if ($Skipped.Count -gt 0) { + Write-Host " skipped: $($Skipped -join ', ')" +} +if ($Installed.Count -eq 0 -and $Skipped.Count -eq 0) { + Write-Host " no agents detected; use -Only to install for a specific agent" + Write-Host " supported: claude, codex, gemini" +} +Write-Host "" +Write-Host " To add forerunner to a project: forerunner doctor --fix" +Write-Host " Docs: $GitHubUrl" diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..c20dfbe --- /dev/null +++ b/install.sh @@ -0,0 +1,256 @@ +#!/usr/bin/env bash +# codeforerunner skill installer +# Detects installed agent CLIs and drops forerunner skills into each one. +# +# Usage: +# ./install.sh # auto-detect all agents +# ./install.sh --only claude +# ./install.sh --only codex +# ./install.sh --dry-run +# ./install.sh --list +# ./install.sh --uninstall +# +# One-liner (from anywhere): +# curl -fsSL https://raw.githubusercontent.com/derek-palmer/codeforerunner/main/install.sh | bash + +set -euo pipefail + +REPO_OWNER="derek-palmer" +REPO_NAME="codeforerunner" +RAW_BASE="https://raw.githubusercontent.com/${REPO_OWNER}/${REPO_NAME}/main" +GITHUB_URL="https://github.com/${REPO_OWNER}/${REPO_NAME}" + +SKILL_SLUGS=( + "codeforerunner" + "forerunner-scan" + "forerunner-readme" + "forerunner-api-docs" + "forerunner-audit" + "forerunner-changelog" + "forerunner-check" + "forerunner-diagrams" + "forerunner-flows" + "forerunner-init" + "forerunner-review" + "forerunner-stack-docs" + "forerunner-version-audit" +) + +# ── parse args ──────────────────────────────────────────────────────────────── + +DRY_RUN=false +UNINSTALL=false +LIST_ONLY=false +ONLY_AGENTS=() + +while [[ $# -gt 0 ]]; do + case "$1" in + --dry-run) DRY_RUN=true; shift ;; + --uninstall) UNINSTALL=true; shift ;; + --list) LIST_ONLY=true; shift ;; + --only) ONLY_AGENTS+=("$2"); shift 2 ;; + --only=*) ONLY_AGENTS+=("${1#--only=}"); shift ;; + -h|--help) + echo "Usage: install.sh [--dry-run] [--uninstall] [--list] [--only ]" + echo "Agents: claude, codex, gemini" + exit 0 ;; + *) echo "unknown flag: $1" >&2; exit 1 ;; + esac +done + +# ── detect source ───────────────────────────────────────────────────────────── + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-install.sh}")" 2>/dev/null && pwd || echo "")" +if [[ -n "$SCRIPT_DIR" && -d "${SCRIPT_DIR}/plugins/codeforerunner/skills" ]]; then + LOCAL=true + SKILLS_SRC="${SCRIPT_DIR}/plugins/codeforerunner/skills" +else + LOCAL=false + SKILLS_SRC="" +fi + +# ── helpers ─────────────────────────────────────────────────────────────────── + +log() { echo " $*"; } +ok() { echo " ✓ $*"; } +skip() { echo " – $* (skipped)"; } +err() { echo " ✗ $*" >&2; } + +_skill_dest_claude() { + local slug="$1" + echo "${HOME}/.claude/plugins/codeforerunner/skills/${slug}/SKILL.md" +} + +_skill_dest_codex() { + local slug="$1" + echo "${HOME}/.codex/skills/${slug}/SKILL.md" +} + +_copy_skill() { + local slug="$1" dest="$2" + if [[ "$LOCAL" == "true" ]]; then + local src="${SKILLS_SRC}/${slug}/SKILL.md" + if [[ ! -f "$src" ]]; then + err "source not found: $src" + return 1 + fi + if [[ "$DRY_RUN" == "false" ]]; then + mkdir -p "$(dirname "$dest")" + cp "$src" "$dest" + fi + else + local url="${RAW_BASE}/plugins/codeforerunner/skills/${slug}/SKILL.md" + if [[ "$DRY_RUN" == "false" ]]; then + mkdir -p "$(dirname "$dest")" + curl -fsSL "$url" -o "$dest" + fi + fi +} + +_remove_skill() { + local dest="$1" + if [[ -f "$dest" ]]; then + [[ "$DRY_RUN" == "false" ]] && rm -f "$dest" + ok "removed $dest" + fi +} + +_should_install() { + local agent="$1" + [[ ${#ONLY_AGENTS[@]} -eq 0 ]] || [[ " ${ONLY_AGENTS[*]} " =~ " ${agent} " ]] +} + +# ── detection ───────────────────────────────────────────────────────────────── + +HAS_CLAUDE=false +HAS_CODEX=false +HAS_GEMINI=false + +command -v claude &>/dev/null && HAS_CLAUDE=true +command -v codex &>/dev/null && HAS_CODEX=true +command -v gemini &>/dev/null && HAS_GEMINI=true + +# ── list mode ───────────────────────────────────────────────────────────────── + +if [[ "$LIST_ONLY" == "true" ]]; then + echo "codeforerunner skill installer — agent detection:" + echo "" + printf " %-12s %s\n" "claude" "$( [[ "$HAS_CLAUDE" == "true" ]] && echo "detected ✓" || echo "not found")" + printf " %-12s %s\n" "codex" "$( [[ "$HAS_CODEX" == "true" ]] && echo "detected ✓" || echo "not found")" + printf " %-12s %s\n" "gemini" "$( [[ "$HAS_GEMINI" == "true" ]] && echo "detected ✓" || echo "not found")" + echo "" + echo "Skills that will be installed (${#SKILL_SLUGS[@]}):" + for s in "${SKILL_SLUGS[@]}"; do echo " /$s"; done + exit 0 +fi + +# ── uninstall ───────────────────────────────────────────────────────────────── + +if [[ "$UNINSTALL" == "true" ]]; then + echo "codeforerunner — uninstalling skills" + for slug in "${SKILL_SLUGS[@]}"; do + _should_install "claude" && _remove_skill "$(_skill_dest_claude "$slug")" + _should_install "codex" && _remove_skill "$(_skill_dest_codex "$slug")" + done + _should_install "claude" && \ + [[ "$DRY_RUN" == "false" ]] && \ + rmdir "${HOME}/.claude/plugins/codeforerunner" 2>/dev/null || true + echo "done" + exit 0 +fi + +# ── install ─────────────────────────────────────────────────────────────────── + +INSTALLED=() +SKIPPED=() + +echo "codeforerunner — installing skills" +echo "" +[[ "$DRY_RUN" == "true" ]] && echo " (dry-run — no files written)" +echo "" + +# Claude Code +if _should_install "claude"; then + if [[ "$HAS_CLAUDE" == "true" ]] || [[ ${#ONLY_AGENTS[@]} -gt 0 ]]; then + echo "Claude Code:" + # Copy plugin manifest + if [[ "$DRY_RUN" == "false" ]]; then + mkdir -p "${HOME}/.claude/plugins/codeforerunner" + if [[ "$LOCAL" == "true" ]]; then + cp "${SCRIPT_DIR}/.claude-plugin/plugin.json" "${HOME}/.claude/plugins/codeforerunner/plugin.json" + cp "${SCRIPT_DIR}/.claude-plugin/marketplace.json" "${HOME}/.claude/plugins/codeforerunner/marketplace.json" 2>/dev/null || true + else + curl -fsSL "${RAW_BASE}/.claude-plugin/plugin.json" -o "${HOME}/.claude/plugins/codeforerunner/plugin.json" + curl -fsSL "${RAW_BASE}/.claude-plugin/marketplace.json" -o "${HOME}/.claude/plugins/codeforerunner/marketplace.json" 2>/dev/null || true + fi + fi + for slug in "${SKILL_SLUGS[@]}"; do + dest="$(_skill_dest_claude "$slug")" + if _copy_skill "$slug" "$dest"; then + ok "$dest" + INSTALLED+=("claude/$slug") + fi + done + else + skip "claude (not detected; use --only claude to force)" + SKIPPED+=("claude") + fi + echo "" +fi + +# Codex +if _should_install "codex"; then + if [[ "$HAS_CODEX" == "true" ]] || [[ ${#ONLY_AGENTS[@]} -gt 0 ]]; then + echo "Codex CLI:" + for slug in "${SKILL_SLUGS[@]}"; do + dest="$(_skill_dest_codex "$slug")" + if _copy_skill "$slug" "$dest"; then + ok "$dest" + INSTALLED+=("codex/$slug") + fi + done + else + skip "codex (not detected; use --only codex to force)" + SKIPPED+=("codex") + fi + echo "" +fi + +# Gemini CLI — delegates to native extension install +if _should_install "gemini"; then + if [[ "$HAS_GEMINI" == "true" ]] || [[ ${#ONLY_AGENTS[@]} -gt 0 ]]; then + echo "Gemini CLI:" + if [[ "$DRY_RUN" == "false" ]]; then + if gemini extensions install "${GITHUB_URL}" 2>/dev/null; then + ok "installed via gemini extensions install" + INSTALLED+=("gemini") + else + err "gemini extensions install failed; check gemini CLI version" + fi + else + log "would run: gemini extensions install ${GITHUB_URL}" + fi + else + skip "gemini (not detected; use --only gemini to force)" + SKIPPED+=("gemini") + fi + echo "" +fi + +# ── summary ─────────────────────────────────────────────────────────────────── + +echo "Summary:" +if [[ ${#INSTALLED[@]} -gt 0 ]]; then + unique_agents=$(printf '%s\n' "${INSTALLED[@]}" | cut -d/ -f1 | sort -u | tr '\n' ' ') + echo " installed for: ${unique_agents}" +fi +if [[ ${#SKIPPED[@]} -gt 0 ]]; then + echo " skipped: ${SKIPPED[*]}" +fi +if [[ ${#INSTALLED[@]} -eq 0 && ${#SKIPPED[@]} -eq 0 ]]; then + echo " no agents detected; use --only to install for a specific agent" + echo " supported: claude, codex, gemini" +fi +echo "" +echo " To add forerunner to a project: forerunner doctor --fix" +echo " Docs: ${GITHUB_URL}" diff --git a/plugins/codeforerunner/skills/forerunner-api-docs/SKILL.md b/plugins/codeforerunner/skills/forerunner-api-docs/SKILL.md new file mode 100644 index 0000000..1638ffc --- /dev/null +++ b/plugins/codeforerunner/skills/forerunner-api-docs/SKILL.md @@ -0,0 +1,33 @@ +--- +name: forerunner-api-docs +description: Generate endpoint-level API documentation from route and handler files. Use when the user wants to document a public API or REST endpoints. +--- + +# forerunner-api-docs + +Generates API reference documentation at the endpoint level. Requires scan result and all route/handler files in context. + +## Activate when + +User asks to: document the API, generate API reference, write endpoint docs, create OpenAPI-style documentation. + +## Collect this context + +- Scan result (run `/forerunner-scan` first) +- All route/handler files (every file the scan identified as an API entry point) +- Middleware files +- Auth and validation logic (for request/response shapes) +- Existing API docs (when updating) + +## Execute + +Run `forerunner doc api-docs` to compose the full prompt with system rules, then execute it. + +Without CLI, get the prompt from: +- `src/codeforerunner/prompts/tasks/api-docs.md` +- `src/codeforerunner/prompts/system/base.md` +- `src/codeforerunner/prompts/partials/output-rules.md` + +## Output + +Structured API reference covering: endpoint list, HTTP method, path, auth requirement, request parameters/body, response shape, status codes. Write to `docs/api.md` or return as Markdown. Append `## Gaps` for endpoints without sufficient source evidence. diff --git a/plugins/codeforerunner/skills/forerunner-audit/SKILL.md b/plugins/codeforerunner/skills/forerunner-audit/SKILL.md new file mode 100644 index 0000000..1018286 --- /dev/null +++ b/plugins/codeforerunner/skills/forerunner-audit/SKILL.md @@ -0,0 +1,32 @@ +--- +name: forerunner-audit +description: Run a security and dependency audit against a repository. Use when the user wants a security review, dependency vulnerability check, or supply-chain audit. +--- + +# forerunner-audit + +Produces a structured security and dependency audit report. Covers known vulnerability patterns, dependency hygiene, secret exposure risks, and supply-chain concerns. + +## Activate when + +User asks to: audit the repo, run a security check, check for vulnerabilities, review dependencies for security issues. + +## Collect this context + +- Scan result (run `/forerunner-scan` first) +- All manifest and lockfiles: `package.json`, `package-lock.json`, `yarn.lock`, `pyproject.toml`, `poetry.lock`, `requirements*.txt`, `go.mod`, `go.sum`, `Cargo.toml`, `Cargo.lock`, `Gemfile.lock` +- CI/CD workflow files +- Dockerfile and compose files +- `.env.example` or similar (never actual secret files) + +## Execute + +Run `forerunner doc audit` to compose the full prompt with system rules, then execute it. + +Without CLI, get the prompt from: +- `src/codeforerunner/prompts/tasks/audit.md` +- `src/codeforerunner/prompts/system/base.md` + +## Output + +Structured audit report covering: outdated/vulnerable dependencies, hardcoded secrets risk surface, CI security posture, supply-chain exposure. Severity-tagged findings (HIGH / MEDIUM / LOW). Write to `docs/audit.md` or return as Markdown. diff --git a/plugins/codeforerunner/skills/forerunner-changelog/SKILL.md b/plugins/codeforerunner/skills/forerunner-changelog/SKILL.md new file mode 100644 index 0000000..d684611 --- /dev/null +++ b/plugins/codeforerunner/skills/forerunner-changelog/SKILL.md @@ -0,0 +1,31 @@ +--- +name: forerunner-changelog +description: Generate a Keep-a-Changelog entry from git history since the last release tag. Use when the user wants to write a CHANGELOG entry or document what changed in a release. +--- + +# forerunner-changelog + +Produces a Keep-a-Changelog–style entry for changes since the last release tag. Does not require a full scan — operates on git log and diff output. + +## Activate when + +User asks to: write the changelog, generate a changelog entry, document the release, write what changed since vX.Y.Z. + +## Collect this context + +- `git log v...HEAD --oneline` output +- `git diff v...HEAD --stat` output +- (Optional) recent commit messages with full bodies for context +- (Optional) existing `CHANGELOG.md` for format reference + +## Execute + +Run `forerunner doc changelog` to compose the full prompt with system rules, then execute it. + +Without CLI, get the prompt from: +- `src/codeforerunner/prompts/tasks/changelog.md` +- `src/codeforerunner/prompts/system/base.md` + +## Output + +A formatted `## [X.Y.Z] — YYYY-MM-DD` section with `### Added`, `### Changed`, `### Fixed`, `### Removed` subsections. Infer the version from the tag pattern if not specified. Suitable for direct insertion into `CHANGELOG.md`. diff --git a/plugins/codeforerunner/skills/forerunner-check/SKILL.md b/plugins/codeforerunner/skills/forerunner-check/SKILL.md new file mode 100644 index 0000000..3fff0bf --- /dev/null +++ b/plugins/codeforerunner/skills/forerunner-check/SKILL.md @@ -0,0 +1,37 @@ +--- +name: forerunner-check +description: Check whether existing documentation is stale relative to the current codebase. Use when the user wants to detect doc drift before a commit, PR, or release. +--- + +# forerunner-check + +Evaluates documentation staleness against a fresh scan. Classifies each doc section as CURRENT, STALE, MISSING, or UNVERIFIABLE. Designed for pre-commit hooks, CI gates, and on-demand runs. + +## Activate when + +User asks to: check docs, detect stale documentation, verify README accuracy, find doc drift, validate docs before a PR/release. Also triggers when `forerunner check` exits non-zero. + +## Collect this context + +- Scan result — run fresh, not cached (run `/forerunner-scan` first) +- Existing documentation files: `README.md`, `docs/*.md` +- `.forerunner/state.json` (last-run checksums, if present) +- Git diff of changed files (for pre-commit mode) + +## Execute + +Run `forerunner doc check` to compose the full prompt with system rules, then execute it. + +Without CLI, also available as `forerunner check` (automated rule-based drift detection using `forerunner.config.yaml`). + +Get the manual-agent prompt from: +- `src/codeforerunner/prompts/tasks/check.md` +- `src/codeforerunner/prompts/system/base.md` + +## What to check + +README accuracy · API docs accuracy · Diagram accuracy · Version audit currency (stale after 30 days) · Undocumented new modules · Docs referencing removed files + +## Output + +Staleness report with file-level classification. STALE findings include specific mismatch descriptions. Write to stdout or `.forerunner/check-report.md`. Exit non-zero when STALE or MISSING findings exceed threshold. diff --git a/plugins/codeforerunner/skills/forerunner-diagrams/SKILL.md b/plugins/codeforerunner/skills/forerunner-diagrams/SKILL.md new file mode 100644 index 0000000..39c5ab6 --- /dev/null +++ b/plugins/codeforerunner/skills/forerunner-diagrams/SKILL.md @@ -0,0 +1,32 @@ +--- +name: forerunner-diagrams +description: Generate Mermaid architecture and flow diagrams from a repository's structure. Use when the user wants architecture diagrams, component diagrams, or visual documentation. +--- + +# forerunner-diagrams + +Generates Mermaid diagrams: one master architecture overview plus focused section diagrams for key subsystems. All diagrams grounded in scan evidence. + +## Activate when + +User asks to: generate diagrams, create architecture diagrams, visualize the system, draw component relationships, produce a Mermaid diagram. + +## Collect this context + +- Scan result (run `/forerunner-scan` first) +- Entry-point files (up to 5) +- Key module and interface files identified in the scan +- Existing `docs/diagrams.md` (when updating) + +## Execute + +Run `forerunner doc diagrams` to compose the full prompt with system rules, then execute it. + +Without CLI, get the prompt from: +- `src/codeforerunner/prompts/tasks/diagrams.md` +- `src/codeforerunner/prompts/system/base.md` +- `src/codeforerunner/prompts/partials/output-rules.md` + +## Output + +`docs/diagrams.md` containing fenced Mermaid code blocks: one top-level architecture diagram + one focused diagram per major subsystem detected. Each diagram preceded by a short prose caption. Never invent components not present in the provided files. diff --git a/plugins/codeforerunner/skills/forerunner-flows/SKILL.md b/plugins/codeforerunner/skills/forerunner-flows/SKILL.md new file mode 100644 index 0000000..0332e32 --- /dev/null +++ b/plugins/codeforerunner/skills/forerunner-flows/SKILL.md @@ -0,0 +1,33 @@ +--- +name: forerunner-flows +description: Generate narrative flow documentation for key system paths. Use when the user wants to document how data, requests, or jobs move through the system. +--- + +# forerunner-flows + +Generates prose documentation for key system flows: request/response cycles, data pipelines, background jobs, user journeys, and integration paths. Complements diagrams with narrative explanation. + +## Activate when + +User asks to: document flows, describe how the system works end-to-end, explain data flow, document request lifecycle, write flow documentation. + +## Collect this context + +- Scan result (run `/forerunner-scan` first) +- Entry-point and routing files +- Key middleware and service layer files +- Background job or queue handler files (if detected) +- External integration clients (if detected) + +## Execute + +Run `forerunner doc flows` to compose the full prompt with system rules, then execute it. + +Without CLI, get the prompt from: +- `src/codeforerunner/prompts/tasks/flows.md` +- `src/codeforerunner/prompts/system/base.md` +- `src/codeforerunner/prompts/partials/output-rules.md` + +## Output + +`docs/flows.md` with one section per major flow. Each section: flow name, narrative description, step-by-step trace (actor → component → component), and a Mermaid sequence or flowchart diagram. Append `## Gaps` for flows with insufficient source evidence. diff --git a/plugins/codeforerunner/skills/forerunner-init/SKILL.md b/plugins/codeforerunner/skills/forerunner-init/SKILL.md new file mode 100644 index 0000000..bd26bae --- /dev/null +++ b/plugins/codeforerunner/skills/forerunner-init/SKILL.md @@ -0,0 +1,33 @@ +--- +name: forerunner-init +description: Bootstrap or refresh AGENTS.md and per-agent instruction overlays from repo evidence. Use when the user wants to create or update onboarding instructions for coding agents. +--- + +# forerunner-init + +Generates or updates `AGENTS.md` (and per-agent overlays like `CLAUDE.md`, `GEMINI.md`, `.cursor/rules/`) from direct inspection of the repo. No scan required — derives everything from file tree evidence. + +## Activate when + +User asks to: create AGENTS.md, update agent onboarding, refresh coding agent instructions, generate CLAUDE.md, set up agent configuration for this repo. + +## Collect this context + +- Full file tree (respecting `.gitignore`) +- Root manifests and lockfiles +- Entry-point files +- Existing `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `.cursor/rules/` (when updating) +- Build and test commands +- CI configuration + +## Execute + +Run `forerunner init` (or `forerunner doc init-agent-onboarding`) to compose the full prompt, then execute it. + +Without CLI, get the prompt from: +- `src/codeforerunner/prompts/tasks/init-agent-onboarding.md` +- `src/codeforerunner/prompts/system/base.md` + +## Output + +`AGENTS.md` at repo root with: project overview, build/test commands, repo structure, agent-specific guidance. Optionally produces per-agent overlays. Minimal diff when updating existing files. diff --git a/plugins/codeforerunner/skills/forerunner-readme/SKILL.md b/plugins/codeforerunner/skills/forerunner-readme/SKILL.md new file mode 100644 index 0000000..9baf919 --- /dev/null +++ b/plugins/codeforerunner/skills/forerunner-readme/SKILL.md @@ -0,0 +1,39 @@ +--- +name: forerunner-readme +description: Generate or rewrite README.md from a repository's actual code. Use when the user wants to create, refresh, or update the README. +--- + +# forerunner-readme + +Generates or rewrites the top-level `README.md` from verified repo evidence. Every claim must be grounded in provided files — no placeholder text, no invented content. + +## Activate when + +User asks to: generate a README, write the README, refresh or update README.md, create project documentation. + +## Collect this context + +- Scan result (run `/forerunner-scan` first) +- Existing `README.md` (when updating) +- Entry-point files (up to 5) +- Key module files (up to 10) +- Build and test configuration + +## Execute + +Run `forerunner doc readme` to compose the full prompt with system rules, then execute it. + +Without CLI, get the prompt from: +- `src/codeforerunner/prompts/tasks/readme.md` +- `src/codeforerunner/prompts/system/base.md` +- `src/codeforerunner/prompts/partials/output-rules.md` + +## Required sections + +Title + one-line description · Stack table · Prerequisites · Setup (copy-pasteable) · Configuration (env vars table) · Usage · Project structure (file tree snippet) + +**Conditional:** Testing (if test framework detected) · Deployment (if CI/CD present) · Architecture (link to diagrams.md if exists) · Contributing (if open-source) + +## Output + +Write `README.md` to repo root. For existing files, produce a minimal reviewable diff. Append `## Gaps` for anything unverifiable. diff --git a/plugins/codeforerunner/skills/forerunner-review/SKILL.md b/plugins/codeforerunner/skills/forerunner-review/SKILL.md new file mode 100644 index 0000000..25d3dc4 --- /dev/null +++ b/plugins/codeforerunner/skills/forerunner-review/SKILL.md @@ -0,0 +1,30 @@ +--- +name: forerunner-review +description: Summarize documentation impact of pending changes for reviewer approval. Use when the user wants a doc-impact review before merging a PR. +--- + +# forerunner-review + +Produces a human-readable summary of documentation impact for a pending change. Tells reviewers: which docs are affected, what's now stale, what needs updating before merge. + +## Activate when + +User asks to: review docs impact, summarize documentation changes, check what docs need updating for this PR, generate a doc review summary. + +## Collect this context + +- Check report from `.forerunner/check-report.md` (or run `/forerunner-check` first) +- Git diff of staged or PR files (`git diff --staged` or `git diff main...HEAD`) +- Existing documentation files affected by the diff + +## Execute + +Run `forerunner doc review` to compose the full prompt with system rules, then execute it. + +Without CLI, get the prompt from: +- `src/codeforerunner/prompts/tasks/review.md` +- `src/codeforerunner/prompts/system/base.md` + +## Output + +Review summary with: list of affected docs, staleness classification per doc, recommended actions (update / skip / flag for later), and an overall merge-readiness verdict. Formatted for inclusion in a PR description or review comment. diff --git a/plugins/codeforerunner/skills/forerunner-scan/SKILL.md b/plugins/codeforerunner/skills/forerunner-scan/SKILL.md new file mode 100644 index 0000000..1877fad --- /dev/null +++ b/plugins/codeforerunner/skills/forerunner-scan/SKILL.md @@ -0,0 +1,37 @@ +--- +name: forerunner-scan +description: Scan a repository to collect structured evidence for documentation tasks. Always run this first — all other forerunner tasks depend on its output. +--- + +# forerunner-scan + +First task in every codeforerunner workflow. Produces a structured YAML scan result that all downstream tasks consume as input. + +## Activate when + +User asks to: scan a repo, analyze the codebase before generating docs, collect repo evidence, or run any forerunner task that says "requires scan result." + +## Collect this context + +- Full file tree (respecting `.gitignore`) +- Root manifests and lockfiles: `package.json`, `pyproject.toml`, `go.mod`, `Cargo.toml`, `requirements*.txt`, and matching lockfiles +- Entry-point files (up to 5) +- Build, test, lint, CI configuration files +- `forerunner.config.yaml` if present + +## Execute + +Run `forerunner doc scan` to compose the full prompt with system rules, then execute it. + +Without CLI, get the prompt from: +- `src/codeforerunner/prompts/tasks/scan.md` +- `src/codeforerunner/prompts/system/base.md` +- `src/codeforerunner/prompts/partials/context-format.md` + +## Output + +A YAML-structured scan result containing: detected stack, runtime versions, entry points, key module catalog, config file inventory, and test framework. Save as `.forerunner/scan.yaml` or pass inline to the next task. + +## Important + +Never skip this step for readme, api-docs, diagrams, flows, stack-docs, check, audit, or version-audit tasks. Only `changelog` and `init` can run without a prior scan. diff --git a/plugins/codeforerunner/skills/forerunner-stack-docs/SKILL.md b/plugins/codeforerunner/skills/forerunner-stack-docs/SKILL.md new file mode 100644 index 0000000..01ce43f --- /dev/null +++ b/plugins/codeforerunner/skills/forerunner-stack-docs/SKILL.md @@ -0,0 +1,32 @@ +--- +name: forerunner-stack-docs +description: Generate stack-specific developer documentation. Use when the user wants deep technical docs for a specific part of the stack — database schema, service interfaces, configuration reference, etc. +--- + +# forerunner-stack-docs + +Generates developer documentation tailored to the detected stack. Template selected based on scan result: backend API, frontend SPA, CLI tool, data pipeline, ML service, etc. + +## Activate when + +User asks to: generate developer docs, document the stack, write technical documentation, create a developer guide, document the database schema / service layer / config reference. + +## Collect this context + +- Scan result (run `/forerunner-scan` first) +- Key module files for the detected stack (schema files, service interfaces, config loaders, handler files) +- Type definitions or interface files +- Configuration documentation (env vars, config schemas) + +## Execute + +Run `forerunner doc stack-docs` to compose the full prompt with system rules, then execute it. + +Without CLI, get the prompt from: +- `src/codeforerunner/prompts/tasks/stack-docs.md` +- `src/codeforerunner/prompts/system/base.md` +- `src/codeforerunner/prompts/partials/stack-hints.md` + +## Output + +Stack-specific `docs/stack.md` covering: architecture decisions, key interfaces, configuration reference, data model, extension points. Format and depth matched to detected stack type. Append `## Gaps` for areas with insufficient source evidence. diff --git a/plugins/codeforerunner/skills/forerunner-version-audit/SKILL.md b/plugins/codeforerunner/skills/forerunner-version-audit/SKILL.md new file mode 100644 index 0000000..e87bc5d --- /dev/null +++ b/plugins/codeforerunner/skills/forerunner-version-audit/SKILL.md @@ -0,0 +1,32 @@ +--- +name: forerunner-version-audit +description: Audit all pinned runtime versions, base images, and key dependencies against end-of-life data. Use when the user wants to check for outdated or EOL software versions. +--- + +# forerunner-version-audit + +Scans every pinned version in the repository — runtimes, base images, language versions, container versions, key dependencies — and cross-references against end-of-life data from https://endoflife.date. + +## Activate when + +User asks to: audit versions, check for EOL software, find outdated dependencies, run a version audit, check if anything is past end-of-life. + +## Collect this context + +- All manifest and lockfiles: `package.json`, `package-lock.json`, `pyproject.toml`, `poetry.lock`, `go.mod`, `go.sum`, `Cargo.toml`, `Cargo.lock`, `requirements*.txt` +- `Dockerfile` and `docker-compose.yml` +- CI/CD workflow files (`.github/workflows/*.yml`, etc.) +- IaC files (Terraform, Pulumi, etc.) +- `.tool-versions`, `.nvmrc`, `.python-version`, `.ruby-version` + +## Execute + +Run `forerunner doc version-audit` to compose the full prompt with system rules, then execute it. + +Without CLI, get the prompt from: +- `src/codeforerunner/prompts/tasks/version-audit.md` +- `src/codeforerunner/prompts/system/base.md` + +## Output + +Version audit report: table of all detected versions with columns Version | Status | EOL Date | Latest | Notes. Severity-tagged findings (EOL = HIGH, approaching EOL within 6 months = MEDIUM, current = OK). Write to `docs/version-audit.md`. Re-run monthly — `forerunner check` flags this as stale after 30 days. diff --git a/skills/forerunner-api-docs/SKILL.md b/skills/forerunner-api-docs/SKILL.md new file mode 100644 index 0000000..1638ffc --- /dev/null +++ b/skills/forerunner-api-docs/SKILL.md @@ -0,0 +1,33 @@ +--- +name: forerunner-api-docs +description: Generate endpoint-level API documentation from route and handler files. Use when the user wants to document a public API or REST endpoints. +--- + +# forerunner-api-docs + +Generates API reference documentation at the endpoint level. Requires scan result and all route/handler files in context. + +## Activate when + +User asks to: document the API, generate API reference, write endpoint docs, create OpenAPI-style documentation. + +## Collect this context + +- Scan result (run `/forerunner-scan` first) +- All route/handler files (every file the scan identified as an API entry point) +- Middleware files +- Auth and validation logic (for request/response shapes) +- Existing API docs (when updating) + +## Execute + +Run `forerunner doc api-docs` to compose the full prompt with system rules, then execute it. + +Without CLI, get the prompt from: +- `src/codeforerunner/prompts/tasks/api-docs.md` +- `src/codeforerunner/prompts/system/base.md` +- `src/codeforerunner/prompts/partials/output-rules.md` + +## Output + +Structured API reference covering: endpoint list, HTTP method, path, auth requirement, request parameters/body, response shape, status codes. Write to `docs/api.md` or return as Markdown. Append `## Gaps` for endpoints without sufficient source evidence. diff --git a/skills/forerunner-audit/SKILL.md b/skills/forerunner-audit/SKILL.md new file mode 100644 index 0000000..1018286 --- /dev/null +++ b/skills/forerunner-audit/SKILL.md @@ -0,0 +1,32 @@ +--- +name: forerunner-audit +description: Run a security and dependency audit against a repository. Use when the user wants a security review, dependency vulnerability check, or supply-chain audit. +--- + +# forerunner-audit + +Produces a structured security and dependency audit report. Covers known vulnerability patterns, dependency hygiene, secret exposure risks, and supply-chain concerns. + +## Activate when + +User asks to: audit the repo, run a security check, check for vulnerabilities, review dependencies for security issues. + +## Collect this context + +- Scan result (run `/forerunner-scan` first) +- All manifest and lockfiles: `package.json`, `package-lock.json`, `yarn.lock`, `pyproject.toml`, `poetry.lock`, `requirements*.txt`, `go.mod`, `go.sum`, `Cargo.toml`, `Cargo.lock`, `Gemfile.lock` +- CI/CD workflow files +- Dockerfile and compose files +- `.env.example` or similar (never actual secret files) + +## Execute + +Run `forerunner doc audit` to compose the full prompt with system rules, then execute it. + +Without CLI, get the prompt from: +- `src/codeforerunner/prompts/tasks/audit.md` +- `src/codeforerunner/prompts/system/base.md` + +## Output + +Structured audit report covering: outdated/vulnerable dependencies, hardcoded secrets risk surface, CI security posture, supply-chain exposure. Severity-tagged findings (HIGH / MEDIUM / LOW). Write to `docs/audit.md` or return as Markdown. diff --git a/skills/forerunner-changelog/SKILL.md b/skills/forerunner-changelog/SKILL.md new file mode 100644 index 0000000..d684611 --- /dev/null +++ b/skills/forerunner-changelog/SKILL.md @@ -0,0 +1,31 @@ +--- +name: forerunner-changelog +description: Generate a Keep-a-Changelog entry from git history since the last release tag. Use when the user wants to write a CHANGELOG entry or document what changed in a release. +--- + +# forerunner-changelog + +Produces a Keep-a-Changelog–style entry for changes since the last release tag. Does not require a full scan — operates on git log and diff output. + +## Activate when + +User asks to: write the changelog, generate a changelog entry, document the release, write what changed since vX.Y.Z. + +## Collect this context + +- `git log v...HEAD --oneline` output +- `git diff v...HEAD --stat` output +- (Optional) recent commit messages with full bodies for context +- (Optional) existing `CHANGELOG.md` for format reference + +## Execute + +Run `forerunner doc changelog` to compose the full prompt with system rules, then execute it. + +Without CLI, get the prompt from: +- `src/codeforerunner/prompts/tasks/changelog.md` +- `src/codeforerunner/prompts/system/base.md` + +## Output + +A formatted `## [X.Y.Z] — YYYY-MM-DD` section with `### Added`, `### Changed`, `### Fixed`, `### Removed` subsections. Infer the version from the tag pattern if not specified. Suitable for direct insertion into `CHANGELOG.md`. diff --git a/skills/forerunner-check/SKILL.md b/skills/forerunner-check/SKILL.md new file mode 100644 index 0000000..3fff0bf --- /dev/null +++ b/skills/forerunner-check/SKILL.md @@ -0,0 +1,37 @@ +--- +name: forerunner-check +description: Check whether existing documentation is stale relative to the current codebase. Use when the user wants to detect doc drift before a commit, PR, or release. +--- + +# forerunner-check + +Evaluates documentation staleness against a fresh scan. Classifies each doc section as CURRENT, STALE, MISSING, or UNVERIFIABLE. Designed for pre-commit hooks, CI gates, and on-demand runs. + +## Activate when + +User asks to: check docs, detect stale documentation, verify README accuracy, find doc drift, validate docs before a PR/release. Also triggers when `forerunner check` exits non-zero. + +## Collect this context + +- Scan result — run fresh, not cached (run `/forerunner-scan` first) +- Existing documentation files: `README.md`, `docs/*.md` +- `.forerunner/state.json` (last-run checksums, if present) +- Git diff of changed files (for pre-commit mode) + +## Execute + +Run `forerunner doc check` to compose the full prompt with system rules, then execute it. + +Without CLI, also available as `forerunner check` (automated rule-based drift detection using `forerunner.config.yaml`). + +Get the manual-agent prompt from: +- `src/codeforerunner/prompts/tasks/check.md` +- `src/codeforerunner/prompts/system/base.md` + +## What to check + +README accuracy · API docs accuracy · Diagram accuracy · Version audit currency (stale after 30 days) · Undocumented new modules · Docs referencing removed files + +## Output + +Staleness report with file-level classification. STALE findings include specific mismatch descriptions. Write to stdout or `.forerunner/check-report.md`. Exit non-zero when STALE or MISSING findings exceed threshold. diff --git a/skills/forerunner-diagrams/SKILL.md b/skills/forerunner-diagrams/SKILL.md new file mode 100644 index 0000000..39c5ab6 --- /dev/null +++ b/skills/forerunner-diagrams/SKILL.md @@ -0,0 +1,32 @@ +--- +name: forerunner-diagrams +description: Generate Mermaid architecture and flow diagrams from a repository's structure. Use when the user wants architecture diagrams, component diagrams, or visual documentation. +--- + +# forerunner-diagrams + +Generates Mermaid diagrams: one master architecture overview plus focused section diagrams for key subsystems. All diagrams grounded in scan evidence. + +## Activate when + +User asks to: generate diagrams, create architecture diagrams, visualize the system, draw component relationships, produce a Mermaid diagram. + +## Collect this context + +- Scan result (run `/forerunner-scan` first) +- Entry-point files (up to 5) +- Key module and interface files identified in the scan +- Existing `docs/diagrams.md` (when updating) + +## Execute + +Run `forerunner doc diagrams` to compose the full prompt with system rules, then execute it. + +Without CLI, get the prompt from: +- `src/codeforerunner/prompts/tasks/diagrams.md` +- `src/codeforerunner/prompts/system/base.md` +- `src/codeforerunner/prompts/partials/output-rules.md` + +## Output + +`docs/diagrams.md` containing fenced Mermaid code blocks: one top-level architecture diagram + one focused diagram per major subsystem detected. Each diagram preceded by a short prose caption. Never invent components not present in the provided files. diff --git a/skills/forerunner-flows/SKILL.md b/skills/forerunner-flows/SKILL.md new file mode 100644 index 0000000..0332e32 --- /dev/null +++ b/skills/forerunner-flows/SKILL.md @@ -0,0 +1,33 @@ +--- +name: forerunner-flows +description: Generate narrative flow documentation for key system paths. Use when the user wants to document how data, requests, or jobs move through the system. +--- + +# forerunner-flows + +Generates prose documentation for key system flows: request/response cycles, data pipelines, background jobs, user journeys, and integration paths. Complements diagrams with narrative explanation. + +## Activate when + +User asks to: document flows, describe how the system works end-to-end, explain data flow, document request lifecycle, write flow documentation. + +## Collect this context + +- Scan result (run `/forerunner-scan` first) +- Entry-point and routing files +- Key middleware and service layer files +- Background job or queue handler files (if detected) +- External integration clients (if detected) + +## Execute + +Run `forerunner doc flows` to compose the full prompt with system rules, then execute it. + +Without CLI, get the prompt from: +- `src/codeforerunner/prompts/tasks/flows.md` +- `src/codeforerunner/prompts/system/base.md` +- `src/codeforerunner/prompts/partials/output-rules.md` + +## Output + +`docs/flows.md` with one section per major flow. Each section: flow name, narrative description, step-by-step trace (actor → component → component), and a Mermaid sequence or flowchart diagram. Append `## Gaps` for flows with insufficient source evidence. diff --git a/skills/forerunner-init/SKILL.md b/skills/forerunner-init/SKILL.md new file mode 100644 index 0000000..bd26bae --- /dev/null +++ b/skills/forerunner-init/SKILL.md @@ -0,0 +1,33 @@ +--- +name: forerunner-init +description: Bootstrap or refresh AGENTS.md and per-agent instruction overlays from repo evidence. Use when the user wants to create or update onboarding instructions for coding agents. +--- + +# forerunner-init + +Generates or updates `AGENTS.md` (and per-agent overlays like `CLAUDE.md`, `GEMINI.md`, `.cursor/rules/`) from direct inspection of the repo. No scan required — derives everything from file tree evidence. + +## Activate when + +User asks to: create AGENTS.md, update agent onboarding, refresh coding agent instructions, generate CLAUDE.md, set up agent configuration for this repo. + +## Collect this context + +- Full file tree (respecting `.gitignore`) +- Root manifests and lockfiles +- Entry-point files +- Existing `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `.cursor/rules/` (when updating) +- Build and test commands +- CI configuration + +## Execute + +Run `forerunner init` (or `forerunner doc init-agent-onboarding`) to compose the full prompt, then execute it. + +Without CLI, get the prompt from: +- `src/codeforerunner/prompts/tasks/init-agent-onboarding.md` +- `src/codeforerunner/prompts/system/base.md` + +## Output + +`AGENTS.md` at repo root with: project overview, build/test commands, repo structure, agent-specific guidance. Optionally produces per-agent overlays. Minimal diff when updating existing files. diff --git a/skills/forerunner-readme/SKILL.md b/skills/forerunner-readme/SKILL.md new file mode 100644 index 0000000..9baf919 --- /dev/null +++ b/skills/forerunner-readme/SKILL.md @@ -0,0 +1,39 @@ +--- +name: forerunner-readme +description: Generate or rewrite README.md from a repository's actual code. Use when the user wants to create, refresh, or update the README. +--- + +# forerunner-readme + +Generates or rewrites the top-level `README.md` from verified repo evidence. Every claim must be grounded in provided files — no placeholder text, no invented content. + +## Activate when + +User asks to: generate a README, write the README, refresh or update README.md, create project documentation. + +## Collect this context + +- Scan result (run `/forerunner-scan` first) +- Existing `README.md` (when updating) +- Entry-point files (up to 5) +- Key module files (up to 10) +- Build and test configuration + +## Execute + +Run `forerunner doc readme` to compose the full prompt with system rules, then execute it. + +Without CLI, get the prompt from: +- `src/codeforerunner/prompts/tasks/readme.md` +- `src/codeforerunner/prompts/system/base.md` +- `src/codeforerunner/prompts/partials/output-rules.md` + +## Required sections + +Title + one-line description · Stack table · Prerequisites · Setup (copy-pasteable) · Configuration (env vars table) · Usage · Project structure (file tree snippet) + +**Conditional:** Testing (if test framework detected) · Deployment (if CI/CD present) · Architecture (link to diagrams.md if exists) · Contributing (if open-source) + +## Output + +Write `README.md` to repo root. For existing files, produce a minimal reviewable diff. Append `## Gaps` for anything unverifiable. diff --git a/skills/forerunner-review/SKILL.md b/skills/forerunner-review/SKILL.md new file mode 100644 index 0000000..25d3dc4 --- /dev/null +++ b/skills/forerunner-review/SKILL.md @@ -0,0 +1,30 @@ +--- +name: forerunner-review +description: Summarize documentation impact of pending changes for reviewer approval. Use when the user wants a doc-impact review before merging a PR. +--- + +# forerunner-review + +Produces a human-readable summary of documentation impact for a pending change. Tells reviewers: which docs are affected, what's now stale, what needs updating before merge. + +## Activate when + +User asks to: review docs impact, summarize documentation changes, check what docs need updating for this PR, generate a doc review summary. + +## Collect this context + +- Check report from `.forerunner/check-report.md` (or run `/forerunner-check` first) +- Git diff of staged or PR files (`git diff --staged` or `git diff main...HEAD`) +- Existing documentation files affected by the diff + +## Execute + +Run `forerunner doc review` to compose the full prompt with system rules, then execute it. + +Without CLI, get the prompt from: +- `src/codeforerunner/prompts/tasks/review.md` +- `src/codeforerunner/prompts/system/base.md` + +## Output + +Review summary with: list of affected docs, staleness classification per doc, recommended actions (update / skip / flag for later), and an overall merge-readiness verdict. Formatted for inclusion in a PR description or review comment. diff --git a/skills/forerunner-scan/SKILL.md b/skills/forerunner-scan/SKILL.md new file mode 100644 index 0000000..1877fad --- /dev/null +++ b/skills/forerunner-scan/SKILL.md @@ -0,0 +1,37 @@ +--- +name: forerunner-scan +description: Scan a repository to collect structured evidence for documentation tasks. Always run this first — all other forerunner tasks depend on its output. +--- + +# forerunner-scan + +First task in every codeforerunner workflow. Produces a structured YAML scan result that all downstream tasks consume as input. + +## Activate when + +User asks to: scan a repo, analyze the codebase before generating docs, collect repo evidence, or run any forerunner task that says "requires scan result." + +## Collect this context + +- Full file tree (respecting `.gitignore`) +- Root manifests and lockfiles: `package.json`, `pyproject.toml`, `go.mod`, `Cargo.toml`, `requirements*.txt`, and matching lockfiles +- Entry-point files (up to 5) +- Build, test, lint, CI configuration files +- `forerunner.config.yaml` if present + +## Execute + +Run `forerunner doc scan` to compose the full prompt with system rules, then execute it. + +Without CLI, get the prompt from: +- `src/codeforerunner/prompts/tasks/scan.md` +- `src/codeforerunner/prompts/system/base.md` +- `src/codeforerunner/prompts/partials/context-format.md` + +## Output + +A YAML-structured scan result containing: detected stack, runtime versions, entry points, key module catalog, config file inventory, and test framework. Save as `.forerunner/scan.yaml` or pass inline to the next task. + +## Important + +Never skip this step for readme, api-docs, diagrams, flows, stack-docs, check, audit, or version-audit tasks. Only `changelog` and `init` can run without a prior scan. diff --git a/skills/forerunner-stack-docs/SKILL.md b/skills/forerunner-stack-docs/SKILL.md new file mode 100644 index 0000000..01ce43f --- /dev/null +++ b/skills/forerunner-stack-docs/SKILL.md @@ -0,0 +1,32 @@ +--- +name: forerunner-stack-docs +description: Generate stack-specific developer documentation. Use when the user wants deep technical docs for a specific part of the stack — database schema, service interfaces, configuration reference, etc. +--- + +# forerunner-stack-docs + +Generates developer documentation tailored to the detected stack. Template selected based on scan result: backend API, frontend SPA, CLI tool, data pipeline, ML service, etc. + +## Activate when + +User asks to: generate developer docs, document the stack, write technical documentation, create a developer guide, document the database schema / service layer / config reference. + +## Collect this context + +- Scan result (run `/forerunner-scan` first) +- Key module files for the detected stack (schema files, service interfaces, config loaders, handler files) +- Type definitions or interface files +- Configuration documentation (env vars, config schemas) + +## Execute + +Run `forerunner doc stack-docs` to compose the full prompt with system rules, then execute it. + +Without CLI, get the prompt from: +- `src/codeforerunner/prompts/tasks/stack-docs.md` +- `src/codeforerunner/prompts/system/base.md` +- `src/codeforerunner/prompts/partials/stack-hints.md` + +## Output + +Stack-specific `docs/stack.md` covering: architecture decisions, key interfaces, configuration reference, data model, extension points. Format and depth matched to detected stack type. Append `## Gaps` for areas with insufficient source evidence. diff --git a/skills/forerunner-version-audit/SKILL.md b/skills/forerunner-version-audit/SKILL.md new file mode 100644 index 0000000..e87bc5d --- /dev/null +++ b/skills/forerunner-version-audit/SKILL.md @@ -0,0 +1,32 @@ +--- +name: forerunner-version-audit +description: Audit all pinned runtime versions, base images, and key dependencies against end-of-life data. Use when the user wants to check for outdated or EOL software versions. +--- + +# forerunner-version-audit + +Scans every pinned version in the repository — runtimes, base images, language versions, container versions, key dependencies — and cross-references against end-of-life data from https://endoflife.date. + +## Activate when + +User asks to: audit versions, check for EOL software, find outdated dependencies, run a version audit, check if anything is past end-of-life. + +## Collect this context + +- All manifest and lockfiles: `package.json`, `package-lock.json`, `pyproject.toml`, `poetry.lock`, `go.mod`, `go.sum`, `Cargo.toml`, `Cargo.lock`, `requirements*.txt` +- `Dockerfile` and `docker-compose.yml` +- CI/CD workflow files (`.github/workflows/*.yml`, etc.) +- IaC files (Terraform, Pulumi, etc.) +- `.tool-versions`, `.nvmrc`, `.python-version`, `.ruby-version` + +## Execute + +Run `forerunner doc version-audit` to compose the full prompt with system rules, then execute it. + +Without CLI, get the prompt from: +- `src/codeforerunner/prompts/tasks/version-audit.md` +- `src/codeforerunner/prompts/system/base.md` + +## Output + +Version audit report: table of all detected versions with columns Version | Status | EOL Date | Latest | Notes. Severity-tagged findings (EOL = HIGH, approaching EOL within 6 months = MEDIUM, current = OK). Write to `docs/version-audit.md`. Re-run monthly — `forerunner check` flags this as stale after 30 days. diff --git a/src/codeforerunner/installer.py b/src/codeforerunner/installer.py index b35083e..65cd5c9 100644 --- a/src/codeforerunner/installer.py +++ b/src/codeforerunner/installer.py @@ -21,6 +21,23 @@ EXIT_BODY_MISMATCH = 3 EXIT_UNMANAGED_DEST = 4 +# Per-task skill slugs (source: skills//SKILL.md → plugins/codeforerunner/skills//SKILL.md) +TASK_SKILL_SLUGS: tuple[str, ...] = ( + "codeforerunner", + "forerunner-scan", + "forerunner-readme", + "forerunner-api-docs", + "forerunner-audit", + "forerunner-changelog", + "forerunner-check", + "forerunner-diagrams", + "forerunner-flows", + "forerunner-init", + "forerunner-review", + "forerunner-stack-docs", + "forerunner-version-audit", +) + @dataclass(frozen=True) class Target: @@ -44,9 +61,65 @@ def resolve_target(agent: str, override: Path | None) -> Target: return Target(agent, home / ".codex/skills/codeforerunner/SKILL.md") if agent == "claude": return Target(agent, home / ".claude/plugins/codeforerunner/skills/codeforerunner/SKILL.md") + if agent == "gemini": + raise ValueError( + "gemini install is handled via `gemini extensions install`; " + "run `./install.sh --only gemini` instead" + ) raise ValueError(f"unknown agent '{agent}' (expected: codex, claude, generic)") +def resolve_skill_target(agent: str, slug: str) -> Target: + """Return install target for a per-task skill slug.""" + home = _home() + if agent == "codex": + return Target(agent, home / f".codex/skills/{slug}/SKILL.md") + if agent == "claude": + return Target(agent, home / f".claude/plugins/codeforerunner/skills/{slug}/SKILL.md") + raise ValueError(f"install_all not supported for agent '{agent}' (expected: codex, claude)") + + +def install_all_skills( + *, + agent: str, + repo_root: Path, + check_only: bool, + out=None, + err=None, +) -> int: + """Install all per-task skills for the given agent. Returns 0 on full success.""" + out = out or sys.stdout + err = err or sys.stderr + any_error = False + for slug in TASK_SKILL_SLUGS: + src_path = repo_root / "plugins" / "codeforerunner" / "skills" / slug / "SKILL.md" + if not src_path.is_file(): + print(f"warning: skill source not found: {src_path}", file=err) + continue + try: + target = resolve_skill_target(agent, slug) + except ValueError as e: + print(f"error: {e}", file=err) + return EXIT_USAGE + # For per-task skills use simple copy (no body-parity check against canonical) + dest = target.path + prefix = "would " if check_only else "" + if dest.exists(): + src_trimmed = src_path.read_bytes().rstrip() + dest_trimmed = dest.read_bytes().rstrip() + if src_trimmed == dest_trimmed: + print(f"skip: {dest} (up-to-date)", file=out) + continue + action = "update" + else: + action = "create" + print(f"{prefix}{action}: {dest}", file=out) + if not check_only: + dest.parent.mkdir(parents=True, exist_ok=True) + dest.write_bytes(src_path.read_bytes()) + return EXIT_OK if not any_error else EXIT_BODY_MISMATCH + + def resolve_marketplace_target(agent: str, override: Path | None) -> Target: if agent == "generic": if override is None: @@ -277,8 +350,11 @@ def install( def add_subparser(sub: argparse._SubParsersAction) -> None: - p = sub.add_parser("install", help="install skill into agent-specific directory (D.installer)") - p.add_argument("agent", choices=["codex", "claude", "generic"]) + p = sub.add_parser("install", help="install skill(s) into agent-specific directories (D.installer)") + p.add_argument("agent", choices=["codex", "claude", "generic"], nargs="?", + help="target agent (omit with --all to install to all detected agents)") + p.add_argument("--all", action="store_true", + help="install all per-task skills for the specified agent") p.add_argument("--check", action="store_true", help="dry-run: print plan, write nothing") p.add_argument("--path", type=Path, help="dest path override (required for generic)") p.add_argument("--source", type=Path, help="source skill file (default: agent/codeforerunner.skill.md)") @@ -292,6 +368,19 @@ def add_subparser(sub: argparse._SubParsersAction) -> None: def _cli_entry(args: argparse.Namespace) -> int: root = Path(args.repo).resolve() if args.repo else Path.cwd() + + if getattr(args, "all", False): + agent = args.agent or "claude" + return install_all_skills( + agent=agent, + repo_root=root, + check_only=args.check, + ) + + if not args.agent: + print("error: specify an agent or use --all", file=sys.stderr) + return EXIT_USAGE + return install( agent=args.agent, repo_root=root, From 7e904a1e190aa2e3fcd0c948855fb062d5977c4c Mon Sep 17 00:00:00 2001 From: Derek Palmer Date: Sun, 24 May 2026 17:11:30 -0400 Subject: [PATCH 2/2] feat: rewrite installers to match caveman's full agent-matrix pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the hand-rolled bash/PowerShell installers with a thin Node.js shim model: install.sh and install.ps1 now just exec `node bin/install.js` locally (or `npx github:` for curl|bash). The real installer lives in bin/install.js and supports 33 agent CLIs with the same detection probes and install mechanisms as caveman: - Full PROVIDERS matrix (33 agents, 4 soft): claude, gemini, opencode, codex, cursor, windsurf, cline, continue, kilo, roo, augment, copilot, aider-desk, amp, bob, crush, devin, droid, forgecode, goose, iflow, kiro, mistral, openhands, qwen, rovodev, tabnine, trae, warp, replit, junie, qoder, antigravity - Detection probes: command:, dir:, file:, macapp:, vscode-ext:, cursor-ext:, jetbrains-plugin: (||‑delimited, bash-3.2 safe) - Install mechanisms: `claude plugin install`, `gemini extensions install`, `npx skills add -a --yes --all` - Flags: --dry-run, --force, --only (repeatable), --all, --minimal, --list, --no-color, --skip-skills, --uninstall, -h/--help - Colored summary matching caveman's ✓/–/✗ format - skills-lock.json with SHA-256 hashes for all 13 skill files - bin/package.json { "type": "commonjs" } Co-Authored-By: Claude Sonnet 4.6 --- bin/install.js | 451 +++++++++++++++++++++++++++++++++++++++++++++++ bin/package.json | 1 + install.ps1 | 219 ++--------------------- install.sh | 261 ++------------------------- skills-lock.json | 83 +++++++++ 5 files changed, 566 insertions(+), 449 deletions(-) create mode 100644 bin/install.js create mode 100644 bin/package.json create mode 100644 skills-lock.json diff --git a/bin/install.js b/bin/install.js new file mode 100644 index 0000000..dfad458 --- /dev/null +++ b/bin/install.js @@ -0,0 +1,451 @@ +#!/usr/bin/env node +// codeforerunner — unified cross-platform installer. +// +// Installs forerunner's prompt-pack skills into every detected agent CLI +// so documentation slash commands (/forerunner-scan, /forerunner-readme, +// etc.) are available without a separate API key. +// +// Distribution: +// Local clone: node bin/install.js [flags] +// curl|bash: delegated from install.sh shim → npx -y github:derek-palmer/codeforerunner -- [flags] +// Windows: pwsh install.ps1 [flags] → same npx delegation +// +// Pure stdlib, zero npm runtime deps. +// Modelled on JuliusBrussee/caveman bin/install.js. + +'use strict'; + +const fs = require('fs'); +const os = require('os'); +const path = require('path'); +const child_process = require('child_process'); + +const REPO = 'derek-palmer/codeforerunner'; +const RAW_BASE = `https://raw.githubusercontent.com/${REPO}/main`; + +// ── Argv ────────────────────────────────────────────────────────────────── + +function parseArgs(argv) { + const opts = { + dryRun: false, force: false, skipSkills: false, + all: false, minimal: false, listOnly: false, noColor: false, + only: [], uninstall: false, help: false, + }; + for (let i = 0; i < argv.length; i++) { + const a = argv[i]; + switch (a) { + case '--dry-run': opts.dryRun = true; break; + case '--force': opts.force = true; break; + case '--skip-skills': opts.skipSkills = true; break; + case '--all': opts.all = true; break; + case '--minimal': opts.minimal = true; break; + case '--list': opts.listOnly = true; break; + case '--no-color': opts.noColor = true; break; + case '--uninstall': case '-u': opts.uninstall = true; break; + case '-h': case '--help': opts.help = true; break; + case '--': break; // npx may forward a literal -- + case '--only': { + const v = argv[++i]; + if (!v) die('error: --only requires an argument'); + opts.only.push(v); + break; + } + default: + die(`error: unknown flag: ${a}\nrun with --help for usage`); + } + } + if (opts.all && opts.minimal) die('error: --all and --minimal are mutually exclusive'); + if (opts.only.length) { + const known = new Set(PROVIDERS.map(p => p.id)); + for (const id of opts.only) { + if (!known.has(id)) die(`error: unknown agent: ${id}\n see --list for valid ids`); + } + } + return opts; +} + +function die(msg) { process.stderr.write(msg + '\n'); process.exit(2); } + +// ── Color helpers ───────────────────────────────────────────────────────── + +function makeChalk(noColor) { + const use = !noColor && process.stdout.isTTY && !process.env.NO_COLOR; + const wrap = (c) => (s) => use ? `\x1b[${c}m${s}\x1b[0m` : s; + return { + green: wrap('32'), + yellow: wrap('33'), + red: wrap('31'), + dim: wrap('2'), + bold: wrap('1'), + cyan: wrap('36'), + }; +} + +// ── Provider matrix ─────────────────────────────────────────────────────── +// Same detection rules as caveman/bin/install.js: +// command: — binary on PATH +// dir: — directory exists (soft-only; avoid false positives) +// file: — file exists +// macapp: — /Applications/.app present +// vscode-ext: — VS Code / Cursor / Windsurf extension dir match +// cursor-ext: — Cursor extension dir match only +// jetbrains-plugin: — JetBrains plugin dir walk +// +// `soft: true` → excluded from auto-detect; only installs with --only . +// `profile` → npx skills add profile slug (https://github.com/vercel-labs/skills) + +const PROVIDERS = [ + { id: 'claude', label: 'Claude Code', mech: 'claude plugin install', detect: 'command:claude' }, + { id: 'gemini', label: 'Gemini CLI', mech: 'gemini extensions install', detect: 'command:gemini' }, + { id: 'opencode', label: 'opencode', mech: 'npx skills add (opencode)', detect: 'command:opencode', profile: 'opencode' }, + { id: 'codex', label: 'Codex CLI', mech: 'npx skills add (codex)', detect: 'command:codex', profile: 'codex' }, + + { id: 'cursor', label: 'Cursor', mech: 'npx skills add (cursor)', detect: 'command:cursor||macapp:Cursor', profile: 'cursor' }, + { id: 'windsurf', label: 'Windsurf', mech: 'npx skills add (windsurf)', detect: 'command:windsurf||macapp:Windsurf', profile: 'windsurf' }, + { id: 'cline', label: 'Cline', mech: 'npx skills add (cline)', detect: 'vscode-ext:cline', profile: 'cline' }, + { id: 'continue', label: 'Continue', mech: 'npx skills add (continue)', detect: 'vscode-ext:continue.continue||vscode-ext:continue', profile: 'continue' }, + { id: 'kilo', label: 'Kilo Code', mech: 'npx skills add (kilo)', detect: 'vscode-ext:kilocode', profile: 'kilo' }, + { id: 'roo', label: 'Roo Code', mech: 'npx skills add (roo)', detect: 'vscode-ext:roo||vscode-ext:rooveterinaryinc.roo-cline||cursor-ext:roo', profile: 'roo' }, + { id: 'augment', label: 'Augment Code', mech: 'npx skills add (augment)', detect: 'vscode-ext:augment||jetbrains-plugin:augment', profile: 'augment' }, + + { id: 'copilot', label: 'GitHub Copilot', mech: 'npx skills add (github-copilot)', detect: 'command:copilot', profile: 'github-copilot', soft: true }, + + { id: 'aider-desk', label: 'Aider Desk', mech: 'npx skills add (aider-desk)', detect: 'command:aider', profile: 'aider-desk' }, + { id: 'amp', label: 'Sourcegraph Amp', mech: 'npx skills add (amp)', detect: 'command:amp', profile: 'amp' }, + { id: 'bob', label: 'IBM Bob', mech: 'npx skills add (bob)', detect: 'command:bob', profile: 'bob' }, + { id: 'crush', label: 'Crush', mech: 'npx skills add (crush)', detect: 'command:crush', profile: 'crush' }, + { id: 'devin', label: 'Devin (terminal)', mech: 'npx skills add (devin)', detect: 'command:devin', profile: 'devin' }, + { id: 'droid', label: 'Droid (Factory)', mech: 'npx skills add (droid)', detect: 'command:droid', profile: 'droid' }, + { id: 'forgecode', label: 'ForgeCode', mech: 'npx skills add (forgecode)', detect: 'command:forge', profile: 'forgecode' }, + { id: 'goose', label: 'Block Goose', mech: 'npx skills add (goose)', detect: 'command:goose', profile: 'goose' }, + { id: 'iflow', label: 'iFlow CLI', mech: 'npx skills add (iflow-cli)', detect: 'command:iflow', profile: 'iflow-cli' }, + { id: 'kiro', label: 'Kiro CLI', mech: 'npx skills add (kiro-cli)', detect: 'command:kiro', profile: 'kiro-cli' }, + { id: 'mistral', label: 'Mistral Vibe', mech: 'npx skills add (mistral-vibe)', detect: 'command:mistral', profile: 'mistral-vibe' }, + { id: 'openhands', label: 'OpenHands', mech: 'npx skills add (openhands)', detect: 'command:openhands', profile: 'openhands' }, + { id: 'qwen', label: 'Qwen Code', mech: 'npx skills add (qwen-code)', detect: 'command:qwen', profile: 'qwen-code' }, + { id: 'rovodev', label: 'Atlassian Rovo Dev', mech: 'npx skills add (rovodev)', detect: 'command:rovodev', profile: 'rovodev' }, + { id: 'tabnine', label: 'Tabnine CLI', mech: 'npx skills add (tabnine-cli)', detect: 'command:tabnine', profile: 'tabnine-cli' }, + { id: 'trae', label: 'Trae', mech: 'npx skills add (trae)', detect: 'command:trae', profile: 'trae' }, + { id: 'warp', label: 'Warp', mech: 'npx skills add (warp)', detect: 'command:warp', profile: 'warp' }, + { id: 'replit', label: 'Replit Agent', mech: 'npx skills add (replit)', detect: 'command:replit', profile: 'replit' }, + + { id: 'junie', label: 'JetBrains Junie', mech: 'npx skills add (junie)', detect: 'jetbrains-plugin:junie', profile: 'junie', soft: true }, + { id: 'qoder', label: 'Qoder', mech: 'npx skills add (qoder)', detect: 'dir:$HOME/.qoder', profile: 'qoder', soft: true }, + { id: 'antigravity', label: 'Google Antigravity', mech: 'npx skills add (antigravity)', detect: 'dir:$HOME/.gemini/antigravity', profile: 'antigravity', soft: true }, +]; + +// ── Detection helpers ───────────────────────────────────────────────────── + +const IS_WIN = process.platform === 'win32'; + +function shellEscape(s) { return `'${String(s).replace(/'/g, `'\\''`)}`; } + +function expandHome(p) { + return String(p).replace(/^\$HOME(?=\/|$)/, os.homedir()).replace(/^~(?=\/|$)/, os.homedir()); +} + +function hasCmd(cmd) { + try { + if (IS_WIN) { + return child_process.spawnSync('where', [cmd], { stdio: 'ignore' }).status === 0; + } + return child_process.spawnSync('sh', ['-c', `command -v ${shellEscape(cmd)}`], { stdio: 'ignore' }).status === 0; + } catch (_) { return false; } +} + +function safeStat(p, method) { + try { return fs.statSync(p)[method](); } catch (_) { return false; } +} + +function macAppPresent(name) { + if (process.platform !== 'darwin') return false; + return [ + `/Applications/${name}.app`, + path.join(os.homedir(), 'Applications', `${name}.app`), + ].some(p => fs.existsSync(p)); +} + +function vscodeExtPresent(needle) { + const roots = [ + path.join(os.homedir(), '.vscode/extensions'), + path.join(os.homedir(), '.vscode-server/extensions'), + path.join(os.homedir(), '.cursor/extensions'), + path.join(os.homedir(), '.windsurf/extensions'), + ]; + const re = new RegExp(needle, 'i'); + for (const r of roots) { + if (!fs.existsSync(r)) continue; + let entries; try { entries = fs.readdirSync(r); } catch (_) { continue; } + if (entries.some(e => re.test(e))) return true; + } + return false; +} + +function cursorExtPresent(needle) { + const dir = path.join(os.homedir(), '.cursor/extensions'); + if (!fs.existsSync(dir)) return false; + const re = new RegExp(needle, 'i'); + try { return fs.readdirSync(dir).some(e => re.test(e)); } catch (_) { return false; } +} + +function walkDir(root, depth) { + if (depth < 0) return []; + const out = []; + let entries; try { entries = fs.readdirSync(root, { withFileTypes: true }); } catch (_) { return out; } + for (const e of entries) { + const full = path.join(root, e.name); + if (e.isDirectory()) { out.push(full); out.push(...walkDir(full, depth - 1)); } + } + return out; +} + +function jetbrainsPluginPresent(needle) { + const roots = [ + path.join(os.homedir(), 'Library/Application Support/JetBrains'), + path.join(os.homedir(), '.config/JetBrains'), + ]; + const re = new RegExp(needle, 'i'); + for (const r of roots) { + if (!fs.existsSync(r)) continue; + if (walkDir(r, 4).some(p => re.test(path.basename(p)))) return true; + } + return false; +} + +// detectMatch — parse `||`-delimited probe specs and return true on first match. +// Handles bash-3.2-style `||` splitting cleanly even with whitespace around delimiters. +function detectMatch(spec) { + if (!spec) return false; + for (const clause of spec.split('||')) { + const c = clause.trim(); + if (!c) continue; + const colon = c.indexOf(':'); + const kind = colon === -1 ? c : c.slice(0, colon); + const val = colon === -1 ? '' : expandHome(c.slice(colon + 1)); + let ok = false; + switch (kind) { + case 'command': ok = hasCmd(val); break; + case 'dir': ok = safeStat(val, 'isDirectory'); break; + case 'file': ok = safeStat(val, 'isFile'); break; + case 'macapp': ok = macAppPresent(val); break; + case 'vscode-ext': ok = vscodeExtPresent(val); break; + case 'cursor-ext': ok = cursorExtPresent(val); break; + case 'jetbrains-plugin': ok = jetbrainsPluginPresent(val); break; + } + if (ok) return true; + } + return false; +} + +// ── Platform spawn helpers ──────────────────────────────────────────────── + +function quoteWinArg(a) { + if (!IS_WIN) return a; + if (a === '' || /[\s"]/.test(a)) { + return '"' + String(a).replace(/\\(?=\\*"|$)/g, '\\\\').replace(/"/g, '\\"') + '"'; + } + return a; +} + +function spawnXplat(cmd, args, opts) { + if (IS_WIN) { + const quoted = args.map(quoteWinArg).join(' '); + return child_process.spawnSync(`${cmd} ${quoted}`, [], Object.assign({ shell: true }, opts || {})); + } + return child_process.spawnSync(cmd, args, opts || {}); +} + +function runSpawn(cmd, args, dryRun, c) { + const line = `${cmd} ${args.join(' ')}`; + if (dryRun) { process.stdout.write(` ${c.dim('would run:')} ${line}\n`); return { status: 0 }; } + process.stdout.write(` ${c.dim('$')} ${line}\n`); + return spawnXplat(cmd, args, { stdio: 'inherit' }); +} + +function captureSpawn(cmd, args) { + try { return spawnXplat(cmd, args, { encoding: 'utf8' }); } + catch (_) { return { status: 1, stdout: '', stderr: '' }; } +} + +// ── Repo root detection ─────────────────────────────────────────────────── + +function detectRepoRoot() { + const here = path.dirname(__filename); + const root = path.resolve(here, '..'); + if (fs.existsSync(path.join(root, 'skills')) && + fs.existsSync(path.join(root, 'plugins', 'codeforerunner', 'skills'))) { + return root; + } + return null; +} + +// ── Per-provider install logic ──────────────────────────────────────────── + +function installClaude(opts, results, c) { + results.detected++; + process.stdout.write(`\n${c.bold('→ Claude Code')}\n`); + + if (!opts.force) { + const r = captureSpawn('claude', ['plugin', 'list']); + if (r.status === 0 && /codeforerunner/i.test(r.stdout || '')) { + process.stdout.write(` ${c.dim('codeforerunner plugin already installed (use --force to reinstall)')}\n`); + results.skipped.push({ id: 'claude', why: 'already installed' }); + return; + } + } + const r1 = runSpawn('claude', ['plugin', 'marketplace', 'add', REPO], opts.dryRun, c); + const r2 = runSpawn('claude', ['plugin', 'install', 'codeforerunner@codeforerunner'], opts.dryRun, c); + if ((r1.status || 0) === 0 && (r2.status || 0) === 0) results.installed.push('claude'); + else results.failed.push({ id: 'claude', why: 'claude plugin install failed' }); +} + +function installGemini(opts, results, c) { + results.detected++; + process.stdout.write(`\n${c.bold('→ Gemini CLI')}\n`); + + if (!opts.force) { + const r = captureSpawn('gemini', ['extensions', 'list']); + if (r.status === 0 && /codeforerunner/i.test(r.stdout || '')) { + process.stdout.write(` ${c.dim('codeforerunner extension already installed (use --force to reinstall)')}\n`); + results.skipped.push({ id: 'gemini', why: 'already installed' }); + return; + } + } + const r = runSpawn('gemini', ['extensions', 'install', `https://github.com/${REPO}`], opts.dryRun, c); + if ((r.status || 0) === 0) results.installed.push('gemini'); + else results.failed.push({ id: 'gemini', why: 'gemini extensions install failed' }); +} + +function installViaSkills(prov, opts, results, c) { + results.detected++; + process.stdout.write(`\n${c.bold(`→ ${prov.label}`)}\n`); + // --yes --all: skip the upstream skill-selection TUI. Without these, curl|bash + // (no TTY on stdin) renders an empty checkbox list and exits 0 with nothing installed. + const args = ['-y', 'skills', 'add', REPO, '-a', prov.profile, '--yes', '--all']; + const r = runSpawn('npx', args, opts.dryRun, c); + if ((r.status || 0) === 0) results.installed.push(prov.id); + else results.failed.push({ id: prov.id, why: `npx skills add (${prov.profile}) failed` }); +} + +// ── Help / list ─────────────────────────────────────────────────────────── + +function printHelp() { + process.stdout.write(`\ +codeforerunner installer — adds /forerunner-* slash commands to agent CLIs + +Usage: + node bin/install.js [flags] + +Flags: + --all Install to every detected agent (default mode) + --minimal Install without any extras + --only Install to a specific agent only (repeatable) + --force Reinstall even if already installed + --skip-skills Skip the npx skills add step + --dry-run Print what would run; write nothing + --list Show all supported agents and detection status + --no-color Disable colored output + --uninstall, -u Remove codeforerunner from detected agents + -h, --help Show this help + +Agents (${PROVIDERS.length}): +${PROVIDERS.map(p => ` ${p.id.padEnd(14)} ${p.label}`).join('\n')} +`); +} + +function printList(c) { + process.stdout.write(`codeforerunner installer — ${PROVIDERS.length} supported agents\n\n`); + const maxId = Math.max(...PROVIDERS.map(p => p.id.length)); + const maxLabel = Math.max(...PROVIDERS.map(p => p.label.length)); + for (const p of PROVIDERS) { + const detected = detectMatch(p.detect); + const status = p.soft && !detected + ? c.dim('soft (--only ' + p.id + ')') + : detected + ? c.green('✓ detected') + : c.dim('not found'); + const soft = p.soft ? c.dim(' [soft]') : ''; + process.stdout.write( + ` ${p.id.padEnd(maxId + 2)}${p.label.padEnd(maxLabel + 2)}${status}${soft}\n` + ); + } + process.stdout.write('\n'); +} + +// ── Uninstall ───────────────────────────────────────────────────────────── + +function uninstall(opts, c) { + process.stdout.write('codeforerunner — uninstalling\n\n'); + const targets = opts.only.length ? opts.only : ['claude']; + for (const id of targets) { + if (id === 'claude') { + process.stdout.write(`${c.bold('→ Claude Code')}\n`); + runSpawn('claude', ['plugin', 'uninstall', 'codeforerunner'], opts.dryRun, c); + } else { + process.stdout.write(` ${c.yellow('warn:')} uninstall for ${id} not yet automated — remove manually\n`); + } + } + process.stdout.write('\ndone\n'); +} + +// ── Summary ─────────────────────────────────────────────────────────────── + +function printSummary(results, c) { + process.stdout.write('\n─────────────────────────────────\n'); + if (results.installed.length) { + process.stdout.write(c.green(`✓ installed: ${results.installed.join(', ')}\n`)); + } + if (results.skipped.length) { + const ids = results.skipped.map(s => `${s.id} (${s.why})`).join(', '); + process.stdout.write(c.yellow(`– skipped: ${ids}\n`)); + } + if (results.failed.length) { + const ids = results.failed.map(s => `${s.id} (${s.why})`).join(', '); + process.stdout.write(c.red(`✗ failed: ${ids}\n`)); + } + if (!results.installed.length && !results.skipped.length && !results.failed.length) { + process.stdout.write(c.yellow('no agents detected\n')); + process.stdout.write(c.dim(' use --list to see all supported agents\n')); + process.stdout.write(c.dim(' use --only to install for a specific agent\n')); + } + process.stdout.write('\n'); + process.stdout.write(c.dim(` docs: https://github.com/${REPO}\n`)); + process.stdout.write(c.dim(' to configure drift rules: forerunner doctor --fix\n')); +} + +// ── Main ────────────────────────────────────────────────────────────────── + +function main() { + const opts = parseArgs(process.argv.slice(2)); + const c = makeChalk(opts.noColor); + + if (opts.help) { printHelp(); return; } + if (opts.listOnly) { printList(c); return; } + if (opts.uninstall){ uninstall(opts,c); return; } + + const results = { detected: 0, installed: [], skipped: [], failed: [] }; + + process.stdout.write(c.bold('codeforerunner') + c.dim(' — installing skills into detected agents\n')); + if (opts.dryRun) process.stdout.write(c.yellow(' (dry-run — no files written)\n')); + + for (const prov of PROVIDERS) { + // soft providers only install when explicitly requested via --only + if (prov.soft && !opts.only.includes(prov.id)) continue; + // --only filter + if (opts.only.length && !opts.only.includes(prov.id)) continue; + + const detected = detectMatch(prov.detect); + if (!detected) continue; + + if (opts.skipSkills && prov.profile) continue; + + if (prov.id === 'claude') { installClaude(opts, results, c); continue; } + if (prov.id === 'gemini') { installGemini(opts, results, c); continue; } + if (prov.profile) { installViaSkills(prov, opts, results, c); continue; } + } + + printSummary(results, c); + if (results.failed.length) process.exit(1); +} + +main(); diff --git a/bin/package.json b/bin/package.json new file mode 100644 index 0000000..a3c15a7 --- /dev/null +++ b/bin/package.json @@ -0,0 +1 @@ +{ "type": "commonjs" } diff --git a/install.ps1 b/install.ps1 index 340f755..73e9f63 100644 --- a/install.ps1 +++ b/install.ps1 @@ -1,212 +1,23 @@ -# codeforerunner skill installer (Windows PowerShell) -# Detects installed agent CLIs and drops forerunner skills into each one. +# codeforerunner skill installer — thin Node.js shim (Windows PowerShell). +# Delegates to bin\install.js when run from a local clone, +# or fetches and runs it via npx from a remote invocation. # -# Usage: -# .\install.ps1 # auto-detect all agents -# .\install.ps1 -Only claude -# .\install.ps1 -Only codex -# .\install.ps1 -DryRun -# .\install.ps1 -List -# .\install.ps1 -Uninstall +# Usage (local clone): +# .\install.ps1 [flags] +# +# All flags (--dry-run, --force, --only, --all, --minimal, --list, --no-color, +# --skip-skills, --uninstall, -h/--help) are forwarded to bin\install.js. -param( - [switch]$DryRun, - [switch]$Uninstall, - [switch]$List, - [string[]]$Only = @() -) +param([Parameter(ValueFromRemainingArguments=$true)][string[]]$Args) $ErrorActionPreference = "Stop" -$RepoOwner = "derek-palmer" -$RepoName = "codeforerunner" -$RawBase = "https://raw.githubusercontent.com/$RepoOwner/$RepoName/main" -$GitHubUrl = "https://github.com/$RepoOwner/$RepoName" - -$SkillSlugs = @( - "codeforerunner", - "forerunner-scan", - "forerunner-readme", - "forerunner-api-docs", - "forerunner-audit", - "forerunner-changelog", - "forerunner-check", - "forerunner-diagrams", - "forerunner-flows", - "forerunner-init", - "forerunner-review", - "forerunner-stack-docs", - "forerunner-version-audit" -) - -# ── detect source ───────────────────────────────────────────────────────────── - +$Repo = "derek-palmer/codeforerunner" $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path -$LocalSkills = Join-Path $ScriptDir "plugins\codeforerunner\skills" -$IsLocal = Test-Path $LocalSkills - -# ── helpers ─────────────────────────────────────────────────────────────────── - -function Log { param($msg) Write-Host " $msg" } -function Ok { param($msg) Write-Host " ✓ $msg" -ForegroundColor Green } -function Skip { param($msg) Write-Host " – $msg (skipped)" -ForegroundColor DarkGray } -function Err { param($msg) Write-Host " ✗ $msg" -ForegroundColor Red } - -function ShouldInstall($agent) { - $Only.Count -eq 0 -or $Only -contains $agent -} - -function SkillDestClaude($slug) { - "$env:USERPROFILE\.claude\plugins\codeforerunner\skills\$slug\SKILL.md" -} - -function SkillDestCodex($slug) { - "$env:USERPROFILE\.codex\skills\$slug\SKILL.md" -} - -function CopySkill($slug, $dest) { - if ($IsLocal) { - $src = Join-Path $LocalSkills "$slug\SKILL.md" - if (-not (Test-Path $src)) { Err "source not found: $src"; return $false } - if (-not $DryRun) { - New-Item -ItemType Directory -Force -Path (Split-Path $dest) | Out-Null - Copy-Item $src $dest -Force - } - } else { - $url = "$RawBase/plugins/codeforerunner/skills/$slug/SKILL.md" - if (-not $DryRun) { - New-Item -ItemType Directory -Force -Path (Split-Path $dest) | Out-Null - Invoke-WebRequest -Uri $url -OutFile $dest -UseBasicParsing - } - } - return $true -} - -function HasCommand($name) { - $null -ne (Get-Command $name -ErrorAction SilentlyContinue) -} - -# ── detection ───────────────────────────────────────────────────────────────── - -$HasClaude = HasCommand "claude" -$HasCodex = HasCommand "codex" -$HasGemini = HasCommand "gemini" +$LocalJs = Join-Path $ScriptDir "bin\install.js" -# ── list mode ───────────────────────────────────────────────────────────────── - -if ($List) { - Write-Host "codeforerunner skill installer — agent detection:" - Write-Host "" - Write-Host (" {0,-12} {1}" -f "claude", $(if ($HasClaude) {"detected ✓"} else {"not found"})) - Write-Host (" {0,-12} {1}" -f "codex", $(if ($HasCodex) {"detected ✓"} else {"not found"})) - Write-Host (" {0,-12} {1}" -f "gemini", $(if ($HasGemini) {"detected ✓"} else {"not found"})) - Write-Host "" - Write-Host "Skills ($($SkillSlugs.Count)):" - foreach ($s in $SkillSlugs) { Write-Host " /$s" } - exit 0 -} - -# ── uninstall ───────────────────────────────────────────────────────────────── - -if ($Uninstall) { - Write-Host "codeforerunner — uninstalling skills" - foreach ($slug in $SkillSlugs) { - if (ShouldInstall "claude") { - $d = SkillDestClaude $slug - if (Test-Path $d) { if (-not $DryRun) { Remove-Item $d -Force }; Ok "removed $d" } - } - if (ShouldInstall "codex") { - $d = SkillDestCodex $slug - if (Test-Path $d) { if (-not $DryRun) { Remove-Item $d -Force }; Ok "removed $d" } - } - } - Write-Host "done" - exit 0 -} - -# ── install ─────────────────────────────────────────────────────────────────── - -$Installed = @() -$Skipped = @() - -Write-Host "codeforerunner — installing skills" -Write-Host "" -if ($DryRun) { Write-Host " (dry-run — no files written)" } -Write-Host "" - -# Claude Code -if (ShouldInstall "claude") { - if ($HasClaude -or $Only.Count -gt 0) { - Write-Host "Claude Code:" - if (-not $DryRun) { - $pluginDir = "$env:USERPROFILE\.claude\plugins\codeforerunner" - New-Item -ItemType Directory -Force -Path $pluginDir | Out-Null - $manifestSrc = if ($IsLocal) { Join-Path $ScriptDir ".claude-plugin\plugin.json" } else { "$RawBase/.claude-plugin/plugin.json" } - if ($IsLocal) { Copy-Item $manifestSrc "$pluginDir\plugin.json" -Force } - else { Invoke-WebRequest -Uri $manifestSrc -OutFile "$pluginDir\plugin.json" -UseBasicParsing } - } - foreach ($slug in $SkillSlugs) { - $dest = SkillDestClaude $slug - if (CopySkill $slug $dest) { Ok $dest; $Installed += "claude/$slug" } - } - } else { - Skip "claude (not detected; use -Only claude to force)" - $Skipped += "claude" - } - Write-Host "" -} - -# Codex -if (ShouldInstall "codex") { - if ($HasCodex -or $Only.Count -gt 0) { - Write-Host "Codex CLI:" - foreach ($slug in $SkillSlugs) { - $dest = SkillDestCodex $slug - if (CopySkill $slug $dest) { Ok $dest; $Installed += "codex/$slug" } - } - } else { - Skip "codex (not detected; use -Only codex to force)" - $Skipped += "codex" - } - Write-Host "" -} - -# Gemini — delegates to native extension install -if (ShouldInstall "gemini") { - if ($HasGemini -or $Only.Count -gt 0) { - Write-Host "Gemini CLI:" - if (-not $DryRun) { - try { - & gemini extensions install $GitHubUrl - Ok "installed via gemini extensions install" - $Installed += "gemini" - } catch { - Err "gemini extensions install failed: $_" - } - } else { - Log "would run: gemini extensions install $GitHubUrl" - } - } else { - Skip "gemini (not detected; use -Only gemini to force)" - $Skipped += "gemini" - } - Write-Host "" -} - -# ── summary ─────────────────────────────────────────────────────────────────── - -Write-Host "Summary:" -if ($Installed.Count -gt 0) { - $agents = ($Installed | ForEach-Object { $_.Split('/')[0] } | Sort-Object -Unique) -join " " - Write-Host " installed for: $agents" -} -if ($Skipped.Count -gt 0) { - Write-Host " skipped: $($Skipped -join ', ')" -} -if ($Installed.Count -eq 0 -and $Skipped.Count -eq 0) { - Write-Host " no agents detected; use -Only to install for a specific agent" - Write-Host " supported: claude, codex, gemini" +if (Test-Path $LocalJs) { + & node $LocalJs @Args +} else { + & npx -y "github:$Repo" -- @Args } -Write-Host "" -Write-Host " To add forerunner to a project: forerunner doctor --fix" -Write-Host " Docs: $GitHubUrl" diff --git a/install.sh b/install.sh index c20dfbe..98bce7f 100755 --- a/install.sh +++ b/install.sh @@ -1,256 +1,27 @@ #!/usr/bin/env bash -# codeforerunner skill installer -# Detects installed agent CLIs and drops forerunner skills into each one. +# codeforerunner skill installer — thin Node.js shim. +# Delegates to bin/install.js when run from a local clone, +# or fetches and runs it via npx from a curl|bash one-liner. # -# Usage: -# ./install.sh # auto-detect all agents -# ./install.sh --only claude -# ./install.sh --only codex -# ./install.sh --dry-run -# ./install.sh --list -# ./install.sh --uninstall +# Usage (local clone): +# ./install.sh [flags] # -# One-liner (from anywhere): +# One-liner: # curl -fsSL https://raw.githubusercontent.com/derek-palmer/codeforerunner/main/install.sh | bash +# +# All flags (--dry-run, --force, --only, --all, --minimal, --list, --no-color, +# --skip-skills, --uninstall, -h/--help) are forwarded to bin/install.js. set -euo pipefail -REPO_OWNER="derek-palmer" -REPO_NAME="codeforerunner" -RAW_BASE="https://raw.githubusercontent.com/${REPO_OWNER}/${REPO_NAME}/main" -GITHUB_URL="https://github.com/${REPO_OWNER}/${REPO_NAME}" - -SKILL_SLUGS=( - "codeforerunner" - "forerunner-scan" - "forerunner-readme" - "forerunner-api-docs" - "forerunner-audit" - "forerunner-changelog" - "forerunner-check" - "forerunner-diagrams" - "forerunner-flows" - "forerunner-init" - "forerunner-review" - "forerunner-stack-docs" - "forerunner-version-audit" -) - -# ── parse args ──────────────────────────────────────────────────────────────── - -DRY_RUN=false -UNINSTALL=false -LIST_ONLY=false -ONLY_AGENTS=() - -while [[ $# -gt 0 ]]; do - case "$1" in - --dry-run) DRY_RUN=true; shift ;; - --uninstall) UNINSTALL=true; shift ;; - --list) LIST_ONLY=true; shift ;; - --only) ONLY_AGENTS+=("$2"); shift 2 ;; - --only=*) ONLY_AGENTS+=("${1#--only=}"); shift ;; - -h|--help) - echo "Usage: install.sh [--dry-run] [--uninstall] [--list] [--only ]" - echo "Agents: claude, codex, gemini" - exit 0 ;; - *) echo "unknown flag: $1" >&2; exit 1 ;; - esac -done - -# ── detect source ───────────────────────────────────────────────────────────── +REPO="derek-palmer/codeforerunner" +# Locate bin/install.js relative to this script (works even when piped through bash) SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-install.sh}")" 2>/dev/null && pwd || echo "")" -if [[ -n "$SCRIPT_DIR" && -d "${SCRIPT_DIR}/plugins/codeforerunner/skills" ]]; then - LOCAL=true - SKILLS_SRC="${SCRIPT_DIR}/plugins/codeforerunner/skills" -else - LOCAL=false - SKILLS_SRC="" -fi - -# ── helpers ─────────────────────────────────────────────────────────────────── - -log() { echo " $*"; } -ok() { echo " ✓ $*"; } -skip() { echo " – $* (skipped)"; } -err() { echo " ✗ $*" >&2; } - -_skill_dest_claude() { - local slug="$1" - echo "${HOME}/.claude/plugins/codeforerunner/skills/${slug}/SKILL.md" -} - -_skill_dest_codex() { - local slug="$1" - echo "${HOME}/.codex/skills/${slug}/SKILL.md" -} - -_copy_skill() { - local slug="$1" dest="$2" - if [[ "$LOCAL" == "true" ]]; then - local src="${SKILLS_SRC}/${slug}/SKILL.md" - if [[ ! -f "$src" ]]; then - err "source not found: $src" - return 1 - fi - if [[ "$DRY_RUN" == "false" ]]; then - mkdir -p "$(dirname "$dest")" - cp "$src" "$dest" - fi - else - local url="${RAW_BASE}/plugins/codeforerunner/skills/${slug}/SKILL.md" - if [[ "$DRY_RUN" == "false" ]]; then - mkdir -p "$(dirname "$dest")" - curl -fsSL "$url" -o "$dest" - fi - fi -} - -_remove_skill() { - local dest="$1" - if [[ -f "$dest" ]]; then - [[ "$DRY_RUN" == "false" ]] && rm -f "$dest" - ok "removed $dest" - fi -} - -_should_install() { - local agent="$1" - [[ ${#ONLY_AGENTS[@]} -eq 0 ]] || [[ " ${ONLY_AGENTS[*]} " =~ " ${agent} " ]] -} - -# ── detection ───────────────────────────────────────────────────────────────── - -HAS_CLAUDE=false -HAS_CODEX=false -HAS_GEMINI=false - -command -v claude &>/dev/null && HAS_CLAUDE=true -command -v codex &>/dev/null && HAS_CODEX=true -command -v gemini &>/dev/null && HAS_GEMINI=true - -# ── list mode ───────────────────────────────────────────────────────────────── +LOCAL_JS="${SCRIPT_DIR}/bin/install.js" -if [[ "$LIST_ONLY" == "true" ]]; then - echo "codeforerunner skill installer — agent detection:" - echo "" - printf " %-12s %s\n" "claude" "$( [[ "$HAS_CLAUDE" == "true" ]] && echo "detected ✓" || echo "not found")" - printf " %-12s %s\n" "codex" "$( [[ "$HAS_CODEX" == "true" ]] && echo "detected ✓" || echo "not found")" - printf " %-12s %s\n" "gemini" "$( [[ "$HAS_GEMINI" == "true" ]] && echo "detected ✓" || echo "not found")" - echo "" - echo "Skills that will be installed (${#SKILL_SLUGS[@]}):" - for s in "${SKILL_SLUGS[@]}"; do echo " /$s"; done - exit 0 -fi - -# ── uninstall ───────────────────────────────────────────────────────────────── - -if [[ "$UNINSTALL" == "true" ]]; then - echo "codeforerunner — uninstalling skills" - for slug in "${SKILL_SLUGS[@]}"; do - _should_install "claude" && _remove_skill "$(_skill_dest_claude "$slug")" - _should_install "codex" && _remove_skill "$(_skill_dest_codex "$slug")" - done - _should_install "claude" && \ - [[ "$DRY_RUN" == "false" ]] && \ - rmdir "${HOME}/.claude/plugins/codeforerunner" 2>/dev/null || true - echo "done" - exit 0 -fi - -# ── install ─────────────────────────────────────────────────────────────────── - -INSTALLED=() -SKIPPED=() - -echo "codeforerunner — installing skills" -echo "" -[[ "$DRY_RUN" == "true" ]] && echo " (dry-run — no files written)" -echo "" - -# Claude Code -if _should_install "claude"; then - if [[ "$HAS_CLAUDE" == "true" ]] || [[ ${#ONLY_AGENTS[@]} -gt 0 ]]; then - echo "Claude Code:" - # Copy plugin manifest - if [[ "$DRY_RUN" == "false" ]]; then - mkdir -p "${HOME}/.claude/plugins/codeforerunner" - if [[ "$LOCAL" == "true" ]]; then - cp "${SCRIPT_DIR}/.claude-plugin/plugin.json" "${HOME}/.claude/plugins/codeforerunner/plugin.json" - cp "${SCRIPT_DIR}/.claude-plugin/marketplace.json" "${HOME}/.claude/plugins/codeforerunner/marketplace.json" 2>/dev/null || true - else - curl -fsSL "${RAW_BASE}/.claude-plugin/plugin.json" -o "${HOME}/.claude/plugins/codeforerunner/plugin.json" - curl -fsSL "${RAW_BASE}/.claude-plugin/marketplace.json" -o "${HOME}/.claude/plugins/codeforerunner/marketplace.json" 2>/dev/null || true - fi - fi - for slug in "${SKILL_SLUGS[@]}"; do - dest="$(_skill_dest_claude "$slug")" - if _copy_skill "$slug" "$dest"; then - ok "$dest" - INSTALLED+=("claude/$slug") - fi - done - else - skip "claude (not detected; use --only claude to force)" - SKIPPED+=("claude") - fi - echo "" -fi - -# Codex -if _should_install "codex"; then - if [[ "$HAS_CODEX" == "true" ]] || [[ ${#ONLY_AGENTS[@]} -gt 0 ]]; then - echo "Codex CLI:" - for slug in "${SKILL_SLUGS[@]}"; do - dest="$(_skill_dest_codex "$slug")" - if _copy_skill "$slug" "$dest"; then - ok "$dest" - INSTALLED+=("codex/$slug") - fi - done - else - skip "codex (not detected; use --only codex to force)" - SKIPPED+=("codex") - fi - echo "" -fi - -# Gemini CLI — delegates to native extension install -if _should_install "gemini"; then - if [[ "$HAS_GEMINI" == "true" ]] || [[ ${#ONLY_AGENTS[@]} -gt 0 ]]; then - echo "Gemini CLI:" - if [[ "$DRY_RUN" == "false" ]]; then - if gemini extensions install "${GITHUB_URL}" 2>/dev/null; then - ok "installed via gemini extensions install" - INSTALLED+=("gemini") - else - err "gemini extensions install failed; check gemini CLI version" - fi - else - log "would run: gemini extensions install ${GITHUB_URL}" - fi - else - skip "gemini (not detected; use --only gemini to force)" - SKIPPED+=("gemini") - fi - echo "" -fi - -# ── summary ─────────────────────────────────────────────────────────────────── - -echo "Summary:" -if [[ ${#INSTALLED[@]} -gt 0 ]]; then - unique_agents=$(printf '%s\n' "${INSTALLED[@]}" | cut -d/ -f1 | sort -u | tr '\n' ' ') - echo " installed for: ${unique_agents}" -fi -if [[ ${#SKIPPED[@]} -gt 0 ]]; then - echo " skipped: ${SKIPPED[*]}" -fi -if [[ ${#INSTALLED[@]} -eq 0 && ${#SKIPPED[@]} -eq 0 ]]; then - echo " no agents detected; use --only to install for a specific agent" - echo " supported: claude, codex, gemini" +if [[ -n "$SCRIPT_DIR" && -f "$LOCAL_JS" ]]; then + exec node "$LOCAL_JS" "$@" +else + exec npx -y "github:${REPO}" -- "$@" fi -echo "" -echo " To add forerunner to a project: forerunner doctor --fix" -echo " Docs: ${GITHUB_URL}" diff --git a/skills-lock.json b/skills-lock.json new file mode 100644 index 0000000..824734c --- /dev/null +++ b/skills-lock.json @@ -0,0 +1,83 @@ +{ + "version": 1, + "skills": { + "codeforerunner": { + "source": "plugins/codeforerunner/skills/codeforerunner/SKILL.md", + "sourceType": "local", + "skillPath": "skills/codeforerunner/SKILL.md", + "computedHash": "6ad73a8da10842b0ae9e642eabf91805dad295d825ae95bab1f663af829adb0d" + }, + "forerunner-scan": { + "source": "plugins/codeforerunner/skills/forerunner-scan/SKILL.md", + "sourceType": "local", + "skillPath": "skills/forerunner-scan/SKILL.md", + "computedHash": "3d7b0fe2ba54ade4b629751e227fd871f3215c0c69fd15fd5c588dc900fd1569" + }, + "forerunner-readme": { + "source": "plugins/codeforerunner/skills/forerunner-readme/SKILL.md", + "sourceType": "local", + "skillPath": "skills/forerunner-readme/SKILL.md", + "computedHash": "1c3e6c6ae9759c0dab6475a2f3f62cf12bce02dc9b6d4733951d3ffa47161a5d" + }, + "forerunner-api-docs": { + "source": "plugins/codeforerunner/skills/forerunner-api-docs/SKILL.md", + "sourceType": "local", + "skillPath": "skills/forerunner-api-docs/SKILL.md", + "computedHash": "9cb7636fe715e7b3024273f5dfe064fa57a35b09e1016b5692c68cdd9f7a9d43" + }, + "forerunner-audit": { + "source": "plugins/codeforerunner/skills/forerunner-audit/SKILL.md", + "sourceType": "local", + "skillPath": "skills/forerunner-audit/SKILL.md", + "computedHash": "cebb2b1c15b824994a8e0f6e9f51b682f9041641d9c1c189d8b1be173938a0fe" + }, + "forerunner-changelog": { + "source": "plugins/codeforerunner/skills/forerunner-changelog/SKILL.md", + "sourceType": "local", + "skillPath": "skills/forerunner-changelog/SKILL.md", + "computedHash": "e622f4563243207c277088b2f35b66df02d7218893e7253b77f821dafc9ccdbd" + }, + "forerunner-check": { + "source": "plugins/codeforerunner/skills/forerunner-check/SKILL.md", + "sourceType": "local", + "skillPath": "skills/forerunner-check/SKILL.md", + "computedHash": "a5f03599888640a90a71c2cec20418d9be39301fb5fa59f938feb01dec30149d" + }, + "forerunner-diagrams": { + "source": "plugins/codeforerunner/skills/forerunner-diagrams/SKILL.md", + "sourceType": "local", + "skillPath": "skills/forerunner-diagrams/SKILL.md", + "computedHash": "4040c91cb7c6c2646b1ee7f29d536881329db4dc212294905d52755bc4b9a07d" + }, + "forerunner-flows": { + "source": "plugins/codeforerunner/skills/forerunner-flows/SKILL.md", + "sourceType": "local", + "skillPath": "skills/forerunner-flows/SKILL.md", + "computedHash": "d7f01af2f125304a3b58e502ff106927f2ea0ebab95f05a92e87e4149b35d00d" + }, + "forerunner-init": { + "source": "plugins/codeforerunner/skills/forerunner-init/SKILL.md", + "sourceType": "local", + "skillPath": "skills/forerunner-init/SKILL.md", + "computedHash": "1a6122cb77f8ce7c68b6e189374e5e69179de7ec3682a39c24051243acd02ae3" + }, + "forerunner-review": { + "source": "plugins/codeforerunner/skills/forerunner-review/SKILL.md", + "sourceType": "local", + "skillPath": "skills/forerunner-review/SKILL.md", + "computedHash": "0d24cfbcc77c5d82509c2d701c73ce2c61a99ec61000b36ac22224f6976037a9" + }, + "forerunner-stack-docs": { + "source": "plugins/codeforerunner/skills/forerunner-stack-docs/SKILL.md", + "sourceType": "local", + "skillPath": "skills/forerunner-stack-docs/SKILL.md", + "computedHash": "1f608dbbd98da240beac45679fd456990626454d79ec7bfb9b56df049b6479c5" + }, + "forerunner-version-audit": { + "source": "plugins/codeforerunner/skills/forerunner-version-audit/SKILL.md", + "sourceType": "local", + "skillPath": "skills/forerunner-version-audit/SKILL.md", + "computedHash": "17d9367e2e3608b9a9fde05730a4199cb1674a1e4529aaf53364c02b76e5149a" + } + } +}