From bc87341ea8864b3b2df06903533852f6517e95f8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 29 Jul 2026 08:22:55 +0000 Subject: [PATCH] Add 10 rig samples 300-309 (2026-07-29) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../samples/300-server-uptime-log-parser.md | 46 +++++++++++++++ .../rig/samples/301-css-variable-extractor.md | 41 ++++++++++++++ .../rig/samples/302-openapi-route-coverage.md | 51 +++++++++++++++++ .../rig/samples/303-import-style-checker.md | 44 +++++++++++++++ .../samples/304-git-tag-semver-validator.md | 51 +++++++++++++++++ .../305-shell-script-safety-analyzer.md | 48 ++++++++++++++++ .../samples/306-git-diff-stats-summarizer.md | 49 ++++++++++++++++ .../samples/307-dotenv-template-generator.md | 50 +++++++++++++++++ .../308-ts-class-hierarchy-extractor.md | 56 +++++++++++++++++++ skills/rig/samples/309-git-bisect-helper.md | 45 +++++++++++++++ 10 files changed, 481 insertions(+) create mode 100644 skills/rig/samples/300-server-uptime-log-parser.md create mode 100644 skills/rig/samples/301-css-variable-extractor.md create mode 100644 skills/rig/samples/302-openapi-route-coverage.md create mode 100644 skills/rig/samples/303-import-style-checker.md create mode 100644 skills/rig/samples/304-git-tag-semver-validator.md create mode 100644 skills/rig/samples/305-shell-script-safety-analyzer.md create mode 100644 skills/rig/samples/306-git-diff-stats-summarizer.md create mode 100644 skills/rig/samples/307-dotenv-template-generator.md create mode 100644 skills/rig/samples/308-ts-class-hierarchy-extractor.md create mode 100644 skills/rig/samples/309-git-bisect-helper.md diff --git a/skills/rig/samples/300-server-uptime-log-parser.md b/skills/rig/samples/300-server-uptime-log-parser.md new file mode 100644 index 0000000..c8b9d70 --- /dev/null +++ b/skills/rig/samples/300-server-uptime-log-parser.md @@ -0,0 +1,46 @@ +# 300 - Server Uptime Log Parser + +```rig +import { agent, p, s, defineTool, steering, repair } from "rig"; + +const parseUptimeEvent = defineTool("parseUptimeEvent", { + description: "Parse a log line to extract uptime lifecycle events", + parameters: s.object({ line: s.string }), + handler({ line }) { + const startedMatch = line.match(/(\S+\s+\S+\s+\S+).*\s(\S+(?:\[\d+\])?): .*[Ss]tarted/); + const stoppedMatch = line.match(/(\S+\s+\S+\s+\S+).*\s(\S+(?:\[\d+\])?): .*[Ss]topped/); + const crashMatch = line.match(/(\S+\s+\S+\s+\S+).*\s(\S+(?:\[\d+\])?): .*(?:[Cc]rash|[Kk]ill|exit.*code [^0])/); + if (crashMatch) return { timestamp: crashMatch[1] ?? "", service: crashMatch[2] ?? "", event: "crashed" as const }; + if (startedMatch) return { timestamp: startedMatch[1] ?? "", service: startedMatch[2] ?? "", event: "started" as const }; + if (stoppedMatch) return { timestamp: stoppedMatch[1] ?? "", service: stoppedMatch[2] ?? "", event: "stopped" as const }; + return { timestamp: "", service: "", event: "unknown" as const }; + }, +}); + +// Agent role: Parse system logs to identify service lifecycle events, crash counts, and uptime statistics. +const uptimeLogParser = agent({ + model: "small", + instructions: p`Analyze the following system log lines to identify service lifecycle events. +Log lines: +${p.bash("journalctl -n 200 --no-pager 2>/dev/null || tail -n 200 /var/log/syslog 2>/dev/null || echo 'no logs'")} + +Use the parseUptimeEvent tool on relevant log lines that mention service starts, stops, or crashes. +Calculate totalUptimeHours as a rough estimate based on timestamps found. +Set hasCrashLoop to true if crashCount >= 3. +Return the structured output.`, + output: s.object({ + uptimeEvents: s.array(s.object({ + timestamp: s.string, + service: s.string, + event: s.enum("started", "stopped", "crashed", "unknown"), + })), + crashCount: s.int, + totalUptimeHours: s.number, + hasCrashLoop: s.boolean, + }), + tools: [parseUptimeEvent], + addons: [steering(), repair()], +}); + +export default uptimeLogParser; +``` diff --git a/skills/rig/samples/301-css-variable-extractor.md b/skills/rig/samples/301-css-variable-extractor.md new file mode 100644 index 0000000..0edb0d5 --- /dev/null +++ b/skills/rig/samples/301-css-variable-extractor.md @@ -0,0 +1,41 @@ +# 301 - CSS Variable Extractor + +```rig +import { agent, p, s, defineTool, steering, repair } from "rig"; + +const parseCssVar = defineTool("parseCssVar", { + description: "Parse a CSS line to extract custom property (CSS variable) declarations", + parameters: s.object({ line: s.string }), + handler({ line }) { + const match = line.match(/^\s*(--[\w-]+)\s*:\s*(.+?)\s*;?\s*$/); + if (!match) return { name: "", value: "", found: false }; + return { name: match[1] ?? "", value: match[2] ?? "", found: true }; + }, +}); + +// Agent role: Scan CSS files to extract CSS custom property declarations and detect unused variables. +const cssVariableExtractor = agent({ + model: "small", + instructions: p`Scan CSS files and extract all custom property (CSS variable) declarations. + +CSS file content (declarations and usages): +${p.bash("find . -name '*.css' -not -path '*/node_modules/*' | head -20 | xargs grep -h -- '--' 2>/dev/null | head -100 || echo 'no css files'")} + +Use the parseCssVar tool on each line that may contain a CSS variable declaration (--variable: value). +For each variable found, count how many times it appears as a usage (var(--name)). +A variable is unused if it is declared but never used via var(). +Return the structured output.`, + output: s.object({ + variables: s.record(s.object({ + value: s.string, + usageCount: s.int, + })), + unusedVars: s.array(s.string), + totalVars: s.int, + }), + tools: [parseCssVar], + addons: [steering(), repair()], +}); + +export default cssVariableExtractor; +``` diff --git a/skills/rig/samples/302-openapi-route-coverage.md b/skills/rig/samples/302-openapi-route-coverage.md new file mode 100644 index 0000000..b998fd5 --- /dev/null +++ b/skills/rig/samples/302-openapi-route-coverage.md @@ -0,0 +1,51 @@ +# 302 - OpenAPI Route Coverage + +```rig +import { agent, p, s, defineTool, repair } from "rig"; + +const extractRoutes = defineTool("extractRoutes", { + description: "Extract HTTP route patterns from router handler code", + parameters: s.object({ content: s.string }), + handler({ content }) { + const routes: Array<{ path: string; method: string }> = []; + const pattern = /router\.(get|post|put|delete|patch)\s*\(\s*['"`]([^'"`]+)['"`]/gi; + let m: RegExpExecArray | null; + while ((m = pattern.exec(content)) !== null) { + routes.push({ method: (m[1] ?? "get").toUpperCase(), path: m[2] ?? "/" }); + } + return routes; + }, +}); + +// Agent role: Compare OpenAPI spec routes against implemented router handlers to measure coverage. +const openapiRouteCoverage = agent({ + model: "small", + instructions: p`Compare OpenAPI spec routes against implemented router handlers to determine coverage. + +OpenAPI spec: +${p.readOptional("openapi.json", "{}")} + +Router handler code: +${p.bash("grep -rn 'router\\.' --include='*.ts' --include='*.js' . 2>/dev/null | head -50 || echo 'no routes'")} + +Use the extractRoutes tool on the router handler code to discover implemented routes. +Cross-reference with paths defined in the OpenAPI spec. +Return routes array showing which are defined (in spec) vs implemented (in code). +Calculate coveragePercent as (totalImplemented / totalDefined * 100) or 0 if no spec.`, + output: s.object({ + routes: s.array(s.object({ + path: s.string, + method: s.string, + defined: s.boolean, + implemented: s.boolean, + })), + coveragePercent: s.number, + totalDefined: s.int, + totalImplemented: s.int, + }), + tools: [extractRoutes], + addons: [repair()], +}); + +export default openapiRouteCoverage; +``` diff --git a/skills/rig/samples/303-import-style-checker.md b/skills/rig/samples/303-import-style-checker.md new file mode 100644 index 0000000..0e2577f --- /dev/null +++ b/skills/rig/samples/303-import-style-checker.md @@ -0,0 +1,44 @@ +# 303 - Import Style Checker + +```rig +import { agent, p, s, defineTool, repair } from "rig"; + +const classifyStyle = defineTool("classifyStyle", { + description: "Classify a file's module style based on require vs import counts", + parameters: s.object({ file: s.string, requireCount: s.int, importCount: s.int }), + handler({ requireCount, importCount }) { + if (requireCount === 0 && importCount === 0) return "unknown" as const; + if (requireCount > 0 && importCount > 0) return "mixed" as const; + if (requireCount > 0) return "cjs" as const; + return "esm" as const; + }, +}); + +// Agent role: Classify each JavaScript file by its module system (ESM vs CJS) based on import/require usage. +const importStyleChecker = agent({ + model: "small", + instructions: p`Analyze JavaScript files to classify their module system style. + +Files with require/import patterns: +${p.bash("find . -name '*.js' -not -path '*/node_modules/*' | head -10 | xargs grep -hn '^require\\|^import ' 2>/dev/null | head -80 || echo 'no imports'")} + +For each file found, count occurrences of require(...) and import statements. +Use the classifyStyle tool to determine the module style per file. +Return counts per file and summary totals.`, + output: s.object({ + files: s.record(s.object({ + style: s.enum("esm", "cjs", "mixed", "unknown"), + requireCount: s.int, + importCount: s.int, + })), + esmCount: s.int, + cjsCount: s.int, + mixedCount: s.int, + unknownCount: s.int, + }), + tools: [classifyStyle], + addons: [repair()], +}); + +export default importStyleChecker; +``` diff --git a/skills/rig/samples/304-git-tag-semver-validator.md b/skills/rig/samples/304-git-tag-semver-validator.md new file mode 100644 index 0000000..45f495f --- /dev/null +++ b/skills/rig/samples/304-git-tag-semver-validator.md @@ -0,0 +1,51 @@ +# 304 - Git Tag Semver Validator + +```rig +import { agent, p, s, defineTool, steering } from "rig"; + +const parseSemver = defineTool("parseSemver", { + description: "Parse a git tag to determine if it follows semver and extract components", + parameters: s.object({ tag: s.string }), + handler({ tag }) { + const match = tag.match(/^v?(\d+)\.(\d+)\.(\d+)/); + if (!match) return { valid: false }; + return { + valid: true, + major: parseInt(match[1] ?? "0", 10), + minor: parseInt(match[2] ?? "0", 10), + patch: parseInt(match[3] ?? "0", 10), + }; + }, +}); + +// Agent role: Validate git tags against semver format and identify the latest valid version. +const gitTagSemverValidator = agent({ + model: "small", + instructions: p`Validate git tags to check semver compliance. + +Git tags: +${p.bash("git tag --list 2>/dev/null | head -30 || echo 'no tags'")} + +Use the parseSemver tool on each tag to validate format and extract major/minor/patch. +Identify invalidCount (tags that don't follow semver). +Set latestValid to the highest valid semver tag, or omit if none. +Return the structured output.`, + output: s.object({ + tags: s.array(s.object({ + name: s.string, + valid: s.boolean, + parsed: s.optional(s.object({ + major: s.int, + minor: s.int, + patch: s.int, + })), + })), + invalidCount: s.int, + latestValid: s.optional(s.string), + }), + tools: [parseSemver], + addons: [steering()], +}); + +export default gitTagSemverValidator; +``` diff --git a/skills/rig/samples/305-shell-script-safety-analyzer.md b/skills/rig/samples/305-shell-script-safety-analyzer.md new file mode 100644 index 0000000..1f82264 --- /dev/null +++ b/skills/rig/samples/305-shell-script-safety-analyzer.md @@ -0,0 +1,48 @@ +# 305 - Shell Script Safety Analyzer + +```rig +import { agent, p, s, defineTool, steering } from "rig"; +import { readFile } from "node:fs/promises"; + +const analyzeShellScript = defineTool("analyzeShellScript", { + description: "Analyze a shell script file for safety flags (set -e, set -u, set -o pipefail) and shebang", + parameters: s.object({ filePath: s.string }), + async handler({ filePath }) { + const content = await readFile(filePath, "utf8").catch(() => ""); + const lines = content.split("\n"); + const shebangLine = lines[0] ?? ""; + const shebang = shebangLine.startsWith("#!") ? shebangLine.slice(2).trim() : undefined; + const hasSetE = /\bset\b.*-[a-zA-Z]*e/.test(content) || /\bset\b.*-e/.test(content); + const hasSetU = /\bset\b.*-[a-zA-Z]*u/.test(content) || /\bset\b.*-u/.test(content); + const hasPipefail = /set\s+-o\s+pipefail/.test(content); + let safetyLevel: "strict" | "partial" | "unsafe"; + if (hasSetE && hasSetU && hasPipefail) safetyLevel = "strict"; + else if (hasSetE || hasSetU || hasPipefail) safetyLevel = "partial"; + else safetyLevel = "unsafe"; + return { shebang, hasSetE, hasSetU, hasPipefail, safetyLevel }; + }, +}); + +// Agent role: Scan shell scripts for safety flags and classify each by safety level. +const shellScriptSafetyAnalyzer = agent({ + model: "small", + instructions: p`Find all shell scripts and analyze each for safety flags. + +Shell script files: +${p.bash("find . -name '*.sh' -not -path '*/node_modules/*' | head -20 2>/dev/null || echo 'no shell scripts'")} + +Use the analyzeShellScript tool on each .sh file path listed above. +Return a record keyed by file path with safety analysis for each script.`, + output: s.record(s.object({ + shebang: s.optional(s.string), + hasSetE: s.boolean, + hasSetU: s.boolean, + hasPipefail: s.boolean, + safetyLevel: s.enum("strict", "partial", "unsafe"), + })), + tools: [analyzeShellScript], + addons: [steering()], +}); + +export default shellScriptSafetyAnalyzer; +``` diff --git a/skills/rig/samples/306-git-diff-stats-summarizer.md b/skills/rig/samples/306-git-diff-stats-summarizer.md new file mode 100644 index 0000000..cf1e855 --- /dev/null +++ b/skills/rig/samples/306-git-diff-stats-summarizer.md @@ -0,0 +1,49 @@ +# 306 - Git Diff Stats Summarizer + +```rig +import { agent, p, s, defineTool, repair } from "rig"; + +const classifyDiffEntry = defineTool("classifyDiffEntry", { + description: "Classify a diff entry based on addition/deletion counts", + parameters: s.object({ path: s.string, additions: s.int, deletions: s.int }), + handler({ additions, deletions }) { + if (additions > 0 && deletions === 0) return "added" as const; + if (additions === 0 && deletions > 0) return "deleted" as const; + if (additions > 0 && deletions > 0) return "modified" as const; + return "renamed" as const; + }, +}); + +// Agent role: Summarize git diff statistics between the last two commits, classifying each changed file. +const gitDiffStatsSummarizer = agent({ + model: "small", + instructions: p`Analyze git diff statistics for the most recent commit. + +Diff numstat (additions deletions file): +${p.bash("git diff --numstat HEAD~1 HEAD 2>/dev/null | head -30 || echo 'no diff'")} + +Diff summary: +${p.bash("git diff --stat HEAD~1 HEAD 2>/dev/null | tail -5 || echo 'no stat'")} + +Parse the numstat output (format: additionsdeletionspath). +Use the classifyDiffEntry tool on each file entry. +Sum up totalAdditions and totalDeletions. +Set mostChangedFile to the file with the highest combined additions+deletions, or omit if empty. +Return the structured output.`, + output: s.object({ + files: s.array(s.object({ + path: s.string, + additions: s.int, + deletions: s.int, + change: s.enum("added", "modified", "deleted", "renamed"), + })), + totalAdditions: s.int, + totalDeletions: s.int, + mostChangedFile: s.optional(s.string), + }), + tools: [classifyDiffEntry], + addons: [repair()], +}); + +export default gitDiffStatsSummarizer; +``` diff --git a/skills/rig/samples/307-dotenv-template-generator.md b/skills/rig/samples/307-dotenv-template-generator.md new file mode 100644 index 0000000..3eb5caf --- /dev/null +++ b/skills/rig/samples/307-dotenv-template-generator.md @@ -0,0 +1,50 @@ +# 307 - Dotenv Template Generator + +```rig +import { agent, p, s, defineTool, repair } from "rig"; +import { readFile } from "node:fs/promises"; + +const extractEnvReferences = defineTool("extractEnvReferences", { + description: "Extract all process.env.VAR_NAME references from a TypeScript source file", + parameters: s.object({ filePath: s.string }), + async handler({ filePath }) { + const content = await readFile(filePath, "utf8").catch(() => ""); + const pattern = /process\.env\.([A-Z_][A-Z0-9_]*)/g; + const keys = new Set(); + let m: RegExpExecArray | null; + while ((m = pattern.exec(content)) !== null) { + if (m[1]) keys.add(m[1]); + } + return Array.from(keys); + }, +}); + +// Agent role: Generate a .env.template file by scanning source files for process.env references and comparing against existing .env keys. +const dotenvTemplateGenerator = agent({ + model: "small", + instructions: p`Generate a .env.template file documenting all required environment variables. + +Existing .env file: +${p.readOptional(".env", "# no .env file found")} + +TypeScript source files to scan: +${p.glob("src/**/*.ts")} + +Use the extractEnvReferences tool on each source file to find process.env.VAR references. +Collect all unique env keys found across all files (envKeys). +Compare with keys present in .env — undocumentedKeys are those referenced in code but missing from .env. +Write a .env.template with each key as KEY=. +${p.write(".env.template", "# Generated .env.template\n# Replace with actual values\n")} +Return templatePath as ".env.template", envKeys array, undocumentedKeys array, and templateGenerated as true.`, + output: s.object({ + templatePath: s.path, + envKeys: s.array(s.string), + undocumentedKeys: s.array(s.string), + templateGenerated: s.boolean, + }), + tools: [extractEnvReferences], + addons: [repair()], +}); + +export default dotenvTemplateGenerator; +``` diff --git a/skills/rig/samples/308-ts-class-hierarchy-extractor.md b/skills/rig/samples/308-ts-class-hierarchy-extractor.md new file mode 100644 index 0000000..bee16cf --- /dev/null +++ b/skills/rig/samples/308-ts-class-hierarchy-extractor.md @@ -0,0 +1,56 @@ +# 308 - TS Class Hierarchy Extractor + +```rig +import { agent, p, s, defineTool, steering } from "rig"; +import { readFile } from "node:fs/promises"; + +const extractClassInfo = defineTool("extractClassInfo", { + description: "Extract class declarations, inheritance, and interface implementations from a TypeScript file", + parameters: s.object({ filePath: s.string }), + async handler({ filePath }) { + const content = await readFile(filePath, "utf8").catch(() => ""); + const pattern = /(?:(abstract)\s+)?class\s+(\w+)(?:\s+extends\s+(\w+))?(?:\s+implements\s+([\w,\s]+))?/g; + const classes: Array<{ className: string; parent?: string; interfaces: string[]; isAbstract: boolean }> = []; + let m: RegExpExecArray | null; + while ((m = pattern.exec(content)) !== null) { + const interfaces = m[4] ? m[4].split(",").map((s: string) => s.trim()).filter(Boolean) : []; + classes.push({ + className: m[2] ?? "", + parent: m[3], + interfaces, + isAbstract: !!m[1], + }); + } + return classes; + }, +}); + +// Agent role: Build a class hierarchy map from TypeScript files showing inheritance and interface relationships. +const tsClassHierarchyExtractor = agent({ + model: "small", + instructions: p`Extract and map TypeScript class hierarchy from all source files. + +TypeScript files: +${p.bash("find . -name '*.ts' -not -path '*/node_modules/*' -not -name '*.d.ts' | head -20 2>/dev/null || echo 'no ts files'")} + +Use the extractClassInfo tool on each file to get class declarations with inheritance info. +For each class, compute depth: root classes (no parent) have depth 0; derived classes add 1 per level. +Set maxDepth to the deepest class depth found. +rootClasses are class names with no parent. +Return the structured output.`, + output: s.object({ + classes: s.record(s.object({ + parent: s.optional(s.string), + interfaces: s.array(s.string), + isAbstract: s.boolean, + depth: s.int, + })), + maxDepth: s.int, + rootClasses: s.array(s.string), + }), + tools: [extractClassInfo], + addons: [steering()], +}); + +export default tsClassHierarchyExtractor; +``` diff --git a/skills/rig/samples/309-git-bisect-helper.md b/skills/rig/samples/309-git-bisect-helper.md new file mode 100644 index 0000000..ee0b5d8 --- /dev/null +++ b/skills/rig/samples/309-git-bisect-helper.md @@ -0,0 +1,45 @@ +# 309 - Git Bisect Helper + +```rig +import { agent, p, s, defineTool, steering } from "rig"; + +const selectMidpoint = defineTool("selectMidpoint", { + description: "Select the midpoint commit from a list for binary search bisection", + parameters: s.object({ commits: s.array(s.string), currentBad: s.string, currentGood: s.string }), + handler({ commits }) { + if (commits.length === 0) return { midpoint: null, remaining: 0 }; + const mid = Math.floor(commits.length / 2); + return { midpoint: commits[mid] ?? null, remaining: commits.length }; + }, +}); + +// Agent role: Analyze recent git commit history to identify the most likely suspect commit for a regression using binary search logic. +const gitBisectHelper = agent({ + model: "small", + maxTurns: 8, + instructions: p`Analyze recent git commits to identify a suspect regression commit using binary search logic. + +Recent commits (newest first): +${p.bash("git log --oneline -20 2>/dev/null | head -20 || echo 'no commits'")} + +Commit count in range: +${p.bash("git log --oneline HEAD~10..HEAD 2>/dev/null | wc -l || echo '0'")} + +Use the selectMidpoint tool to perform binary search steps over the commit list. +Assume HEAD is the bad commit and HEAD~10 is good. +Narrow down to the most likely suspect commit. +Set confidence based on how precisely the range was narrowed: + high = 1 commit identified, medium = 2-3 candidates, low = 4+ or unknown. +Return suspectCommit (the SHA/oneline of the suspect), stepsRun, commitRange of the final narrowed set.`, + output: s.object({ + suspectCommit: s.optional(s.string), + stepsRun: s.int, + commitRange: s.array(s.string), + confidence: s.enum("high", "medium", "low"), + }), + tools: [selectMidpoint], + addons: [steering()], +}); + +export default gitBisectHelper; +```