-
Notifications
You must be signed in to change notification settings - Fork 0
[rig-tasks] Add 10 rig samples — 2026-07-25 #109
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| # 68 - Changelog Generator | ||
|
|
||
| ```rig | ||
| import { agent, p, s, defineTool } from "rig"; | ||
| import { repair } from "rig/addons"; | ||
|
|
||
| const validateSemver = defineTool("validateSemver", { | ||
| description: "Validate that a bump type is major, minor, or patch", | ||
| parameters: s.object({ bump: s.string }), | ||
| handler({ bump }) { | ||
| const valid = ["major", "minor", "patch"].includes(bump); | ||
| return { valid }; | ||
| }, | ||
| }); | ||
|
|
||
| // Agent role: read recent git changes and produce a structured changelog entry with semver bump classification. | ||
| const changelogGenerator = agent({ | ||
| model: "small", | ||
| instructions: p`Review ${p.bash("git diff HEAD~1 HEAD --stat")} and ${p.bash("git log HEAD~1..HEAD --oneline")} to produce a changelog entry. Classify each change by category and determine the semver bump type. Use the validateSemver tool to confirm the bump value. Write the markdown changelog to CHANGELOG.md via ${p.write("CHANGELOG.md", "<!-- changelog -->")}`, | ||
| output: s.object({ | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [/grill-with-docs] 💡 Fix: use `p.writeOutput`Replace the write intent so the generated instructions: p`...via ${p.writeOutput("markdown", "CHANGELOG.md")}`,
|
||
| version: s.string, | ||
| bump: s.enum("major", "minor", "patch"), | ||
| entries: s.array(s.object({ | ||
| category: s.enum("feat", "fix", "chore", "docs", "refactor"), | ||
| description: s.string, | ||
| })), | ||
| markdown: s.string, | ||
| }), | ||
| tools: [validateSemver], | ||
| maxTurns: 6, | ||
| addons: repair(), | ||
| }); | ||
|
|
||
| export default changelogGenerator; | ||
|
|
||
| ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| # 69 - Todo Comment Tracker | ||
|
|
||
| ```rig | ||
| import { agent, p, s } from "rig"; | ||
|
|
||
| // Agent role: scan source files for TODO/FIXME/HACK comments and produce a structured report. | ||
| const todoCommentTracker = agent({ | ||
| model: "small", | ||
| instructions: p`Scan source files using ${p.bash("grep -rn 'TODO\\|FIXME\\|HACK' --include='*.ts' . 2>/dev/null || true")} and produce a structured list of all found comments. Write a markdown report to todo-report.md via ${p.write("todo-report.md", "<!-- report -->")}`, | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [/grill-with-docs] Two issues on this line:
💡 Suggested fixinstructions: p`Scan source files using ${p.bashRaw`grep -rn 'TODO\|FIXME\|HACK' --include='*.ts' . 2>/dev/null || true`} and produce a structured list. Write a markdown report via ${p.writeOutput("markdown", "todo-report.md")}`, |
||
| output: s.object({ | ||
| items: s.array(s.object({ | ||
| file: s.path, | ||
| line: s.int, | ||
| kind: s.enum("TODO", "FIXME", "HACK"), | ||
| message: s.string, | ||
| })), | ||
| totalCount: s.int, | ||
| markdown: s.string, | ||
| }), | ||
| }); | ||
|
|
||
| export default todoCommentTracker; | ||
|
|
||
| ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| # 70 - Multi File Subagent Summarizer | ||
|
|
||
| ```rig | ||
| import { agent, p, s } from "rig"; | ||
|
|
||
| // Agent role: summarize a single TypeScript file. | ||
| const fileSummarizer = agent({ | ||
| name: "fileSummarizer", | ||
| model: "nano", | ||
| input: s.object({ filePath: s.path }), | ||
| instructions: p`Summarize the TypeScript file at ${p.readInput("filePath")} in one concise sentence.`, | ||
| output: s.object({ summary: s.string }), | ||
| }); | ||
|
|
||
| // Agent role: find TypeScript source files and delegate to fileSummarizer to summarize each one, then aggregate results. | ||
| const multiFileSummarizer = agent({ | ||
| model: "small", | ||
| instructions: p`Find TypeScript files using ${p.bash("find src -name '*.ts' -not -path '*/node_modules/*' 2>/dev/null | head -10 || echo 'no files'")} then delegate each file path to the fileSummarizer subagent and collect summaries keyed by file path.`, | ||
| output: s.record(s.string), | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [/grill-with-docs] 💡 Suggested fixinstructions: p`Find TypeScript files: ${p.glob("src/**/*.ts")}. For each path delegate to fileSummarizer and collect summaries keyed by file path.`,
|
||
| agents: { fileSummarizer }, | ||
| }); | ||
|
|
||
| export default multiFileSummarizer; | ||
|
|
||
| ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| # 71 - Package Script Health | ||
|
|
||
| ```rig | ||
| import { agent, p, s, defineTool } from "rig"; | ||
| import { repair } from "rig/addons"; | ||
|
|
||
| const validateScriptName = defineTool("validateScriptName", { | ||
| description: "Validate that a package.json script name follows conventional naming (lowercase, hyphens only)", | ||
| parameters: s.object({ name: s.string }), | ||
| handler({ name }) { | ||
| const valid = /^[a-z][a-z0-9:-]*$/.test(name); | ||
| const reason = valid ? "Name is conventional" : "Name should be lowercase with hyphens/colons only"; | ||
| return { valid, reason }; | ||
| }, | ||
| }); | ||
|
|
||
| // Agent role: analyze package.json scripts for naming and structural health issues. | ||
| const packageScriptHealth = agent({ | ||
| model: "small", | ||
| instructions: p`Read ${p.read("package.json")} and analyze all scripts entries. Use the validateScriptName tool for each script name. Classify each issue by severity and determine overall health.`, | ||
| output: s.object({ | ||
| issues: s.array(s.object({ | ||
| script: s.string, | ||
| issue: s.string, | ||
| status: s.enum("error", "warning", "ok"), | ||
| })), | ||
| overallHealth: s.enum("healthy", "degraded", "critical"), | ||
| }), | ||
| tools: [validateScriptName], | ||
| maxTurns: 6, | ||
| addons: repair(), | ||
| }); | ||
|
|
||
| export default packageScriptHealth; | ||
|
|
||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| # 72 - Ts Function Signatures | ||
|
|
||
| ```rig | ||
| import { agent, p, s, defineTool } from "rig"; | ||
|
|
||
| const parseSignatures = defineTool("parseSignatures", { | ||
| description: "Extract function signatures from TypeScript source content using regex", | ||
| parameters: s.object({ content: s.string }), | ||
| handler({ content }) { | ||
| const pattern = /(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(([^)]*)\)/g; | ||
| const functions: { name: string; paramCount: number; isExported: boolean }[] = []; | ||
| let match; | ||
| while ((match = pattern.exec(content)) !== null) { | ||
| const [full, name, params] = match; | ||
| const paramCount = params.trim() === "" ? 0 : params.split(",").length; | ||
| const isExported = full.trimStart().startsWith("export"); | ||
| functions.push({ name, paramCount, isExported }); | ||
| } | ||
| return { functions }; | ||
| }, | ||
| }); | ||
|
|
||
| // Agent role: extract TypeScript function signatures from source files and return them keyed by file path. | ||
| const tsFunctionSignatures = agent({ | ||
| model: "small", | ||
| instructions: p`Find TypeScript files using ${p.bash("find src -name '*.ts' 2>/dev/null | head -5 || echo 'no files'")} then read each file and use the parseSignatures tool to extract function signatures. Return results keyed by file path.`, | ||
| output: s.record(s.array(s.object({ | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [/grill-with-docs] File discovery uses 💡 Suggested fixinstructions: p`Find TypeScript files: ${p.glob("src/**/*.ts")}. Read each file and use the parseSignatures tool to extract function signatures. Return results keyed by file path.`, |
||
| name: s.string, | ||
| paramCount: s.int, | ||
| isExported: s.boolean, | ||
| }))), | ||
| tools: [parseSignatures], | ||
| }); | ||
|
|
||
| export default tsFunctionSignatures; | ||
|
|
||
| ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| # 73 - Git Branch Pruner | ||
|
|
||
| ```rig | ||
| import { agent, p, s } from "rig"; | ||
| import { repair } from "rig/addons"; | ||
|
|
||
| // Agent role: identify git branches that are candidates for pruning based on merge status and last commit date. | ||
| const gitBranchPruner = agent({ | ||
| model: "small", | ||
| instructions: p`Analyze git branches using ${p.bash("git branch --merged HEAD 2>/dev/null || echo 'no branches'")} and ${p.bash("git for-each-ref --format='%(refname:short) %(committerdate:short)' refs/heads/ 2>/dev/null || echo 'no refs'")}. For each branch determine if it should be pruned (merged and not main/master/develop) or kept. Exclude the current branch from prune candidates.`, | ||
| output: s.object({ | ||
| candidates: s.array(s.object({ | ||
| branch: s.string, | ||
| lastCommitDate: s.optional(s.string), | ||
| action: s.enum("keep", "prune"), | ||
| reason: s.string, | ||
| })), | ||
| totalBranches: s.int, | ||
| pruneCount: s.int, | ||
| }), | ||
| maxTurns: 6, | ||
| addons: repair(), | ||
| }); | ||
|
|
||
| export default gitBranchPruner; | ||
|
|
||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| # 74 - Prettier Eslint Compat | ||
|
|
||
| ```rig | ||
| import { agent, p, s, defineTool } from "rig"; | ||
| import { repair } from "rig/addons"; | ||
|
|
||
| const detectConflicts = defineTool("detectConflicts", { | ||
| description: "Detect rule conflicts between Prettier and ESLint configs", | ||
| parameters: s.object({ prettierConfig: s.string, eslintConfig: s.string }), | ||
| handler({ prettierConfig, eslintConfig }) { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [/grill-with-docs] The 💡 Suggested fixRename the local variable to avoid the shadow: const prettier = JSON.parse(prettierConfig || "{}");
const eslint = JSON.parse(eslintConfig || "{}");
if (prettier.printWidth && eslint.rules?.["max-len"]) { ... } |
||
| const conflicts: { rule: string; prettier: string; eslint: string; fixable: boolean }[] = []; | ||
| try { | ||
| const p = JSON.parse(prettierConfig || "{}"); | ||
| const e = JSON.parse(eslintConfig || "{}"); | ||
| const rules = (e.rules || {}); | ||
| if (p.printWidth && rules["max-len"]) { | ||
| conflicts.push({ rule: "line-length", prettier: `printWidth: ${p.printWidth}`, eslint: `max-len: ${JSON.stringify(rules["max-len"])}`, fixable: true }); | ||
| } | ||
| if (p.singleQuote !== undefined && rules["quotes"]) { | ||
| conflicts.push({ rule: "quotes", prettier: `singleQuote: ${p.singleQuote}`, eslint: `quotes: ${JSON.stringify(rules["quotes"])}`, fixable: true }); | ||
| } | ||
| } catch { | ||
| // invalid JSON — model will handle | ||
| } | ||
| return { conflicts }; | ||
| }, | ||
| }); | ||
|
|
||
| // Agent role: check Prettier and ESLint configs for rule conflicts and report compatibility issues. | ||
| const prettierEslintCompat = agent({ | ||
| model: "small", | ||
| instructions: p`Read the Prettier config ${p.readOptional(".prettierrc")} and ESLint config ${p.readOptional(".eslintrc.json")} then use the detectConflicts tool to find rule conflicts. Classify each conflict by severity.`, | ||
| output: s.object({ | ||
| conflicts: s.array(s.object({ | ||
| rule: s.string, | ||
| prettier: s.string, | ||
| eslint: s.string, | ||
| fixable: s.boolean, | ||
| severity: s.enum("error", "warning", "info"), | ||
| })), | ||
| compatible: s.boolean, | ||
| }), | ||
| tools: [detectConflicts], | ||
| maxTurns: 6, | ||
| addons: repair(), | ||
| }); | ||
|
|
||
| export default prettierEslintCompat; | ||
|
|
||
| ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| # 75 - Workflow Validator | ||
|
|
||
| ```rig | ||
| import { agent, p, s } from "rig"; | ||
|
|
||
| // Agent role: analyze a single GitHub Actions workflow file for structural issues and job count. | ||
| const workflowAnalyzer = agent({ | ||
| name: "workflowAnalyzer", | ||
| model: "nano", | ||
| input: s.object({ filePath: s.path }), | ||
| instructions: p`Analyze the GitHub Actions workflow file at ${p.readInput("filePath")} for issues such as missing permissions, deprecated actions, hardcoded secrets, or missing timeout-minutes. Count the number of jobs defined.`, | ||
| output: s.object({ | ||
| issues: s.array(s.object({ | ||
| step: s.string, | ||
| problem: s.string, | ||
| severity: s.enum("error", "warning", "info"), | ||
| })), | ||
| jobCount: s.int, | ||
| }), | ||
| }); | ||
|
|
||
| // Agent role: find all GitHub Actions workflow files and delegate analysis to workflowAnalyzer subagent, then aggregate results. | ||
| const workflowValidator = agent({ | ||
| model: "small", | ||
| instructions: p`Find GitHub Actions workflow files using ${p.bash("find .github/workflows -name '*.yml' -o -name '*.yaml' 2>/dev/null | head -10 || echo 'no workflows'")} then delegate each file to the workflowAnalyzer subagent. Aggregate results keyed by filename, adding a pass/warn/fail status based on issue severity.`, | ||
| output: s.record(s.object({ | ||
| issues: s.array(s.object({ | ||
| step: s.string, | ||
| problem: s.string, | ||
| severity: s.enum("error", "warning", "info"), | ||
| })), | ||
| jobCount: s.int, | ||
| status: s.enum("pass", "warn", "fail"), | ||
| })), | ||
| agents: { workflowAnalyzer }, | ||
| }); | ||
|
|
||
| export default workflowValidator; | ||
|
|
||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| # 76 - Commit Msg Rewriter | ||
|
|
||
| ```rig | ||
| import { agent, p, s } from "rig"; | ||
| import { steering, repair } from "rig/addons"; | ||
|
|
||
| // Agent role: rewrite recent git commit messages into conventional commit format with imperative mood. | ||
| const commitMsgRewriter = agent({ | ||
| model: "small", | ||
| instructions: p`Review recent commits from ${p.bash("git log --oneline -20 2>/dev/null || echo 'no commits'")} and rewrite each message in conventional commit format (feat:/fix:/chore:/docs:/test:/refactor:/style: prefix) with imperative mood. Write a markdown summary of all rewrites via ${p.writeOutput("markdown", "commit-rewrites.md")}`, | ||
| output: s.object({ | ||
| rewrites: s.array(s.object({ | ||
| hash: s.string, | ||
| original: s.string, | ||
| revised: s.string, | ||
| category: s.enum("feat", "fix", "chore", "docs", "test", "refactor", "style"), | ||
| })), | ||
| markdown: s.string, | ||
| }), | ||
| maxTurns: 6, | ||
| addons: [steering({ message: "Use imperative mood and conventional commit prefixes. Be consistent." }), repair()], | ||
| }); | ||
|
|
||
| export default commitMsgRewriter; | ||
|
|
||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| # 77 - Env Key Checker | ||
|
|
||
| ```rig | ||
| import { agent, p, s, defineTool } from "rig"; | ||
|
|
||
| const parseEnvKeys = defineTool("parseEnvKeys", { | ||
| description: "Extract KEY names from dotenv-style file content", | ||
| parameters: s.object({ content: s.string }), | ||
| handler({ content }) { | ||
| const keys = (content.match(/^([A-Z_][A-Z0-9_]*)=/gm) || []) | ||
| .map(line => line.replace("=", "")); | ||
| return { keys }; | ||
| }, | ||
| }); | ||
|
|
||
| // Agent role: compare .env.example required keys against .env actual keys to find missing and extra entries. | ||
| const envKeyChecker = agent({ | ||
| model: "small", | ||
| instructions: p`Read the required env keys from ${p.readOptional(".env.example")} and the present keys from ${p.readOptional(".env")}. Use the parseEnvKeys tool on each file content to extract key names. Compare to find missing keys (in example but not env) and extra keys (in env but not example).`, | ||
| output: s.object({ | ||
| missing: s.array(s.string), | ||
| extra: s.array(s.string), | ||
| requiredCount: s.int, | ||
| presentCount: s.int, | ||
| status: s.enum("complete", "partial", "empty"), | ||
| }), | ||
| tools: [parseEnvKeys], | ||
| }); | ||
|
|
||
| export default envKeyChecker; | ||
|
|
||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[/grill-with-docs] All 10 new samples use
model: "small", but the SKILL.md construction rules say to use"large","mini", or"nano"in examples, and the two immediately preceding samples (66, 67) both use"mini". Using"small"drifts from the documented vocabulary and could confuse readers learning from these samples.Consider updating all new samples to
"mini"(or"nano"for the subagent delegates) to stay consistent with the project's example conventions.