diff --git a/skills/rig/samples/88-git-contributor-mapper.md b/skills/rig/samples/88-git-contributor-mapper.md new file mode 100644 index 0000000..a6f7e70 --- /dev/null +++ b/skills/rig/samples/88-git-contributor-mapper.md @@ -0,0 +1,18 @@ +# 88 - Git Contributor Mapper + +```rig +import { agent, p, s } from "rig"; + +// Agent role: map git contributors to their commit counts, primary work areas, and role classification. +const gitContributorMapper = agent({ + model: "small", + instructions: p`Analyze git contributors using: ${p.bash("git shortlog -sn --no-merges")} and ${p.bash("git log --no-merges --name-only --pretty=format:'%an' | head -500")}. For each contributor, count their commits, identify which directories they primarily touch, and classify their role as core (many commits across many files), peripheral (few commits or limited scope), or single-file.`, + output: s.record(s.object({ + commitCount: s.number, + primaryAreas: s.array(s.string), + role: s.enum("core", "peripheral", "single-file"), + })), +}); + +export default gitContributorMapper; +``` diff --git a/skills/rig/samples/89-markdown-doc-summarizer.md b/skills/rig/samples/89-markdown-doc-summarizer.md new file mode 100644 index 0000000..b3f22cb --- /dev/null +++ b/skills/rig/samples/89-markdown-doc-summarizer.md @@ -0,0 +1,27 @@ +# 89 - Markdown Doc Summarizer + +```rig +import { agent, p, s } from "rig"; + +// Agent role: summarize each top-level section of a markdown doc, then compile into a full report. +const sectionSummarizer = agent({ + name: "sectionSummarizer", + model: "nano", + instructions: p`Summarize the section of documentation provided in the input.`, + input: s.object({ heading: s.string, content: s.string }), + output: s.object({ heading: s.string, summary: s.string }), +}); + +// Agent role: read the project README, delegate per-section summarization, and write the final report. +const markdownDocSummarizer = agent({ + model: "small", + instructions: p`Read the README: ${p.readOptional("README.md", "No README found.")}. Identify each top-level heading (##) and its content. Delegate summarization of each section to the sectionSummarizer agent. Compile all summaries into a report and write it to summaries/README-summary.md via ${p.writeOutput("reportPath", "summaries/README-summary.md")}.`, + output: s.object({ + sections: s.array(s.object({ heading: s.string, summary: s.string })), + reportPath: s.string, + }), + agents: { sectionSummarizer }, +}); + +export default markdownDocSummarizer; +``` diff --git a/skills/rig/samples/90-workspace-config-drift.md b/skills/rig/samples/90-workspace-config-drift.md new file mode 100644 index 0000000..24baf69 --- /dev/null +++ b/skills/rig/samples/90-workspace-config-drift.md @@ -0,0 +1,34 @@ +# 90 - Workspace Config Drift + +```rig +import { agent, p, s, defineTool } from "rig"; +import { repair } from "rig/addons"; + +const parseJson = defineTool("parseJson", { + description: "Parse a JSON string and return it, or report a parse error", + parameters: s.object({ content: s.string, filename: s.string }), + handler({ content, filename }) { + try { + const parsed = JSON.parse(content); + return { ok: true, parsed }; + } catch (e) { + return { ok: false, error: String(e), filename }; + } + }, +}); + +// Agent role: detect drift in workspace config files by reading them and comparing against known defaults. +const workspaceConfigDrift = agent({ + model: "small", + instructions: p`Read project config files: ${p.readOptional("tsconfig.json", "{}")} (tsconfig.json), ${p.readOptional(".eslintrc.json", "{}")} (.eslintrc.json), ${p.readOptional(".prettierrc", "{}")} (.prettierrc). Use the parseJson tool to parse each file. For each config, identify fields that deviate from sensible defaults and report them as drifted. Assign status ok if no drift, warning for minor issues, error for significant mismatches.`, + output: s.record(s.object({ + driftedFields: s.array(s.string), + status: s.enum("ok", "warning", "error"), + })), + tools: [parseJson], + maxTurns: 4, + addons: repair(), +}); + +export default workspaceConfigDrift; +``` diff --git a/skills/rig/samples/91-commit-format-suggester.md b/skills/rig/samples/91-commit-format-suggester.md new file mode 100644 index 0000000..f645bd3 --- /dev/null +++ b/skills/rig/samples/91-commit-format-suggester.md @@ -0,0 +1,22 @@ +# 91 - Commit Format Suggester + +```rig +import { agent, p, s } from "rig"; +import { repair, steering } from "rig/addons"; + +// Agent role: review recent git commits and suggest conventional-format rewrites for each one. +const commitFormatSuggester = agent({ + model: "small", + instructions: p`Review recent git commits: ${p.bash("git log --oneline -20 --no-merges")}. For each commit, check whether its message follows conventional commit format (type: description). Suggest a rewritten message in conventional format. Classify each commit as one of: feat, fix, chore, docs, test, refactor, style. Write the full report to commit-report.md via ${p.writeOutput("reportWritten", "commit-report.md")}.`, + output: s.array(s.object({ + hash: s.string, + original: s.string, + suggested: s.string, + category: s.enum("feat", "fix", "chore", "docs", "test", "refactor", "style"), + })), + maxTurns: 5, + addons: [steering(), repair()], +}); + +export default commitFormatSuggester; +``` diff --git a/skills/rig/samples/92-runtime-env-checker.md b/skills/rig/samples/92-runtime-env-checker.md new file mode 100644 index 0000000..1257bd4 --- /dev/null +++ b/skills/rig/samples/92-runtime-env-checker.md @@ -0,0 +1,36 @@ +# 92 - Runtime Env Checker + +```rig +import { agent, p, s, defineTool } from "rig"; + +const checkThresholds = defineTool("checkThresholds", { + description: "Validate environment values against minimum thresholds and return a list of issues", + parameters: s.object({ + nodeVersion: s.string, + heapMB: s.number, + }), + handler({ nodeVersion, heapMB }) { + const issues: string[] = []; + const majorVersion = parseInt(nodeVersion.replace("v", "").split(".")[0], 10); + if (majorVersion < 18) issues.push(`Node.js ${nodeVersion} is below minimum v18`); + if (heapMB < 256) issues.push(`Heap ${heapMB}MB is below 256MB minimum`); + return { issues }; + }, +}); + +// Agent role: inspect the runtime environment and report overall health. +const runtimeEnvChecker = agent({ + model: "small", + instructions: p`Inspect the runtime environment using: ${p.bash("node --version")}, ${p.bash("uname -a")}, and ${p.bash("node -e \"console.log(Math.round(process.memoryUsage().heapTotal/1024/1024))\"")}. Use the checkThresholds tool to validate versions and memory. Determine overall health as ok, degraded, or critical based on issues found.`, + output: s.object({ + health: s.enum("ok", "degraded", "critical"), + nodeVersion: s.string, + os: s.string, + heapMB: s.number, + issues: s.array(s.string), + }), + tools: [checkThresholds], +}); + +export default runtimeEnvChecker; +``` diff --git a/skills/rig/samples/93-hotspot-file-analyzer.md b/skills/rig/samples/93-hotspot-file-analyzer.md new file mode 100644 index 0000000..96f581c --- /dev/null +++ b/skills/rig/samples/93-hotspot-file-analyzer.md @@ -0,0 +1,21 @@ +# 93 - Hotspot File Analyzer + +```rig +import { agent, p, s } from "rig"; +import { steering } from "rig/addons"; + +// Agent role: analyze which source files are hot-spots by measuring churn and top contributors. +const hotspotFileAnalyzer = agent({ + model: "small", + instructions: p`Analyze file churn in this repository. Get recently changed files using ${p.bash("git log --name-only --format='' HEAD~100..HEAD | sort | uniq -c | sort -rn | head -30")} and contributor data using ${p.bash("git shortlog -sn --no-merges HEAD~100..HEAD")}. For each hot-spot file, compute a churnScore 0–100 based on how often it changes, list topContributors, and classify riskLevel as low, medium, or high.`, + output: s.record(s.object({ + churnScore: s.number, + topContributors: s.array(s.string), + riskLevel: s.enum("low", "medium", "high"), + })), + maxTurns: 5, + addons: steering({ message: "Ensure every file entry has a numeric churnScore and at least one topContributor." }), +}); + +export default hotspotFileAnalyzer; +``` diff --git a/skills/rig/samples/94-ts-interface-conflict-checker.md b/skills/rig/samples/94-ts-interface-conflict-checker.md new file mode 100644 index 0000000..327b09e --- /dev/null +++ b/skills/rig/samples/94-ts-interface-conflict-checker.md @@ -0,0 +1,50 @@ +# 94 - Ts Interface Conflict Checker + +```rig +import { agent, p, s, defineTool } from "rig"; +import { repair } from "rig/addons"; + +const scanInterfaces = defineTool("scanInterfaces", { + description: "Scan a TypeScript file for exported interface names using grep", + parameters: s.object({ filePath: s.string }), + async handler({ filePath }) { + const { execSync } = await import("node:child_process"); + try { + const result = execSync( + `grep -n "^export interface\\|^interface " "${filePath}" 2>/dev/null || true`, + { encoding: "utf8" } + ); + const names = result + .split("\n") + .filter(Boolean) + .map((line) => { + const m = line.match(/interface\s+(\w+)/); + return m ? m[1] : null; + }) + .filter(Boolean) as string[]; + return { filePath, names }; + } catch { + return { filePath, names: [] }; + } + }, +}); + +// Agent role: find duplicate TypeScript interface names across all source files in the project. +const tsInterfaceConflictChecker = agent({ + model: "small", + instructions: p`Find all TypeScript source files using ${p.bash("find . -name '*.ts' -not -path '*/node_modules/*' | head -80")}. Use the scanInterfaces tool on each file to collect interface names. Identify any interface name declared in more than one file. Classify each conflict as warning (same name, compatible) or error (likely clash). Set hasConflicts to true if any conflicts exist.`, + output: s.object({ + conflicts: s.array(s.object({ + interfaceName: s.string, + files: s.array(s.string), + severity: s.enum("warning", "error"), + })), + hasConflicts: s.boolean, + }), + tools: [scanInterfaces], + maxTurns: 6, + addons: repair(), +}); + +export default tsInterfaceConflictChecker; +``` diff --git a/skills/rig/samples/95-git-worktree-mapper.md b/skills/rig/samples/95-git-worktree-mapper.md new file mode 100644 index 0000000..c68be4a --- /dev/null +++ b/skills/rig/samples/95-git-worktree-mapper.md @@ -0,0 +1,48 @@ +# 95 - Git Worktree Mapper + +```rig +import { agent, p, s, defineTool } from "rig"; + +const parseWorktreePorcelain = defineTool("parseWorktreePorcelain", { + description: "Parse the output of git worktree list --porcelain into structured entries", + parameters: s.object({ output: s.string }), + handler({ output }) { + const entries: Array<{ path: string; branch?: string; state: string }> = []; + const blocks = output.trim().split("\n\n"); + for (const block of blocks) { + const lines = block.split("\n"); + const pathLine = lines.find((l) => l.startsWith("worktree ")); + const branchLine = lines.find((l) => l.startsWith("branch ")); + const isLocked = lines.some((l) => l.startsWith("locked")); + const isBare = lines.some((l) => l.startsWith("bare")); + const entry: { path: string; branch?: string; state: string } = { + path: pathLine ? pathLine.replace("worktree ", "") : "", + state: isLocked ? "locked" : isBare ? "bare" : "clean", + }; + if (branchLine) entry.branch = branchLine.replace("branch refs/heads/", ""); + if (entry.path) entries.push(entry); + } + return entries; + }, +}); + +// Agent role: list and classify all git worktrees in the current repository. +const gitWorktreeMapper = agent({ + model: "small", + instructions: p`List all git worktrees using ${p.bash("git worktree list --porcelain")}. Use the parseWorktreePorcelain tool to parse the output. Determine the state of each worktree (locked, bare, clean, or dirty if there are uncommitted changes). Provide a summary with totalCount and activeCount (non-bare worktrees).`, + output: s.object({ + worktrees: s.array(s.object({ + path: s.string, + branch: s.optional(s.string), + state: s.enum("locked", "bare", "clean", "dirty"), + })), + summary: s.object({ + totalCount: s.number, + activeCount: s.number, + }), + }), + tools: [parseWorktreePorcelain], +}); + +export default gitWorktreeMapper; +``` diff --git a/skills/rig/samples/96-test-naming-enforcer.md b/skills/rig/samples/96-test-naming-enforcer.md new file mode 100644 index 0000000..9e0a20e --- /dev/null +++ b/skills/rig/samples/96-test-naming-enforcer.md @@ -0,0 +1,23 @@ +# 96 - Test Naming Enforcer + +```rig +import { agent, p, s } from "rig"; +import { steering } from "rig/addons"; + +// Agent role: audit test file naming conventions and report files that violate the standard pattern. +const testNamingEnforcer = agent({ + model: "small", + instructions: p`Find all test files in this project using ${p.bash("find . \\( -name '*.test.ts' -o -name '*.spec.ts' -o -name '*.test.js' -o -name '*.spec.js' \\) -not -path '*/node_modules/*' | head -60")}. For each file, check whether it follows the convention of .test.ts or .spec.ts. Classify as correct if it matches, wrong-prefix if the name before the extension separator is unusual, wrong-suffix if it ends differently, or missing-spec if it should be a test file but lacks the marker. Suggest a corrected name where applicable. Set allConform to true only if every file is classified as correct.`, + output: s.object({ + files: s.record(s.object({ + convention: s.enum("correct", "wrong-prefix", "wrong-suffix", "missing-spec"), + suggestedName: s.optional(s.string), + })), + allConform: s.boolean, + }), + maxTurns: 5, + addons: steering({ message: "Ensure every discovered test file has an entry in files and allConform is a boolean." }), +}); + +export default testNamingEnforcer; +``` diff --git a/skills/rig/samples/97-pkg-dependency-graph.md b/skills/rig/samples/97-pkg-dependency-graph.md new file mode 100644 index 0000000..d0948c9 --- /dev/null +++ b/skills/rig/samples/97-pkg-dependency-graph.md @@ -0,0 +1,40 @@ +# 97 - Pkg Dependency Graph + +```rig +import { agent, p, s, defineTool } from "rig"; + +const classifyDependency = defineTool("classifyDependency", { + description: "Classify a dependency as runtime, dev, or peer based on its presence in package.json sections", + parameters: s.object({ + name: s.string, + inDependencies: s.boolean, + inDevDependencies: s.boolean, + inPeerDependencies: s.boolean, + }), + handler({ inDependencies, inDevDependencies, inPeerDependencies }) { + if (inPeerDependencies) return "peer"; + if (inDevDependencies) return "dev"; + if (inDependencies) return "runtime"; + return "dev"; + }, +}); + +// Agent role: extract and classify direct dependencies from package.json, then describe the overall tree shape. +const pkgDependencyGraph = agent({ + model: "small", + instructions: p`Read the project manifest: ${p.read("package.json")}. Get the resolved dependency tree using ${p.bash("npm ls --json --depth=1 2>/dev/null || echo '{}'")}. Use the classifyDependency tool to classify each direct dependency as runtime, dev, or peer. Also list all devDependency names. Estimate the treeShape as flat (<5 deps), shallow (5–20), or deep (>20) and set depthScore to the total direct dependency count.`, + output: s.object({ + directDeps: s.array(s.object({ + name: s.string, + version: s.string, + type: s.enum("runtime", "dev", "peer"), + })), + devDeps: s.array(s.string), + treeShape: s.enum("flat", "shallow", "deep"), + depthScore: s.number, + }), + tools: [classifyDependency], +}); + +export default pkgDependencyGraph; +```