-
Notifications
You must be signed in to change notification settings - Fork 0
[rig-tasks] Add 10 rig samples — 2026-07-30 #294
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
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| # 310 - Lock File Drift Detector | ||
|
|
||
| ```rig | ||
| import { agent, p, s, defineTool, repair } from "rig"; | ||
|
|
||
| // Agent role: detect version drift between package.json dependencies and package-lock.json locked versions. | ||
| const lockFileDriftDetector = agent({ | ||
| model: "small", | ||
| instructions: p`You are a lock file drift detector. | ||
|
|
||
| Read the package files: | ||
| ${p.bash("cat package.json 2>/dev/null || echo '{}'")} | ||
| ${p.bash("cat package-lock.json 2>/dev/null || echo '{}'")} | ||
|
|
||
| Use the checkVersionDrift tool to cross-check declared vs locked versions for each dependency. | ||
| Return the declared output.`, | ||
| tools: [ | ||
| defineTool("checkVersionDrift", { | ||
| description: "Parse package.json and package-lock.json content and return drifted packages", | ||
| parameters: s.object({ | ||
| packageJsonContent: s.string, | ||
| lockFileContent: s.string, | ||
| }), | ||
| handler({ packageJsonContent, lockFileContent }) { | ||
| let pkg: Record<string, unknown>; | ||
| let lock: Record<string, unknown>; | ||
| try { | ||
| pkg = JSON.parse(packageJsonContent) as Record<string, unknown>; | ||
| } catch { | ||
| return { error: "Failed to parse package.json" }; | ||
| } | ||
| try { | ||
| lock = JSON.parse(lockFileContent) as Record<string, unknown>; | ||
| } catch { | ||
| return { error: "Failed to parse package-lock.json" }; | ||
| } | ||
| const deps = { | ||
| ...((pkg["dependencies"] as Record<string, string>) ?? {}), | ||
| ...((pkg["devDependencies"] as Record<string, string>) ?? {}), | ||
| }; | ||
| const lockPackages = (lock["packages"] as Record<string, { version?: string }>) ?? {}; | ||
| const drifted: Array<{ name: string; declared: string; locked: string }> = []; | ||
| for (const [name, declared] of Object.entries(deps)) { | ||
| const lockKey = `node_modules/${name}`; | ||
| const lockedEntry = lockPackages[lockKey]; | ||
| const locked = lockedEntry?.version ?? "missing"; | ||
| const declaredClean = (declared as string).replace(/^[\^~>=<]/, ""); | ||
| if (locked !== declaredClean && locked !== "missing") continue; | ||
| if (locked === "missing") { | ||
| drifted.push({ name, declared: declared as string, locked }); | ||
| } | ||
| } | ||
| return { drifted }; | ||
| }, | ||
| }), | ||
| ], | ||
| output: s.object({ | ||
| driftedPackages: s.array(s.object({ | ||
| name: s.string, | ||
| declared: s.string, | ||
| locked: s.string, | ||
| })), | ||
| driftCount: s.int, | ||
| allInSync: s.boolean, | ||
| }), | ||
| addons: [repair()], | ||
| }); | ||
|
|
||
| export default lockFileDriftDetector; | ||
| ``` | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| # 311 - Git Conflict Marker Scanner | ||
|
|
||
| ```rig | ||
| import { agent, p, s, steering } from "rig"; | ||
|
|
||
| // Agent role: scan tracked files for unresolved git conflict markers and report affected locations. | ||
| const gitConflictMarkerScanner = agent({ | ||
| model: "small", | ||
| instructions: p`You are a git conflict marker scanner. | ||
|
|
||
| Check for conflict markers in tracked files: | ||
| ${p.bash("git ls-files | xargs grep -lrn '<<<<<<< ' 2>/dev/null || echo 'no conflicts found'")} | ||
|
|
||
| For files with conflict markers, get the details: | ||
| ${p.bash("git ls-files | xargs grep -n '^<<<<<<< \\|^=======$\\|^>>>>>>> ' 2>/dev/null || echo 'none'")} | ||
|
|
||
| Analyze the results and return the declared output listing all conflict markers found.`, | ||
| output: s.object({ | ||
| conflicts: s.array(s.object({ | ||
| file: s.path, | ||
| line: s.int, | ||
| markerType: s.enum("start", "middle", "end"), | ||
| })), | ||
| affectedFiles: s.array(s.path), | ||
| conflictCount: s.int, | ||
| isClean: s.boolean, | ||
| }), | ||
| addons: [steering()], | ||
| }); | ||
|
|
||
| export default gitConflictMarkerScanner; | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| # 312 - Proto Field Extractor | ||
|
|
||
| ```rig | ||
| import { agent, p, s, defineTool, repair } from "rig"; | ||
| import { readFile } from "node:fs/promises"; | ||
|
|
||
| // Agent role: extract field definitions from protobuf message types in .proto files. | ||
| const protoFieldExtractor = agent({ | ||
| model: "small", | ||
| instructions: p`You are a protobuf field extractor. | ||
|
|
||
| Find .proto files in the workspace: | ||
| ${p.bash("find . -name '*.proto' -not -path '*/node_modules/*' 2>/dev/null || echo 'no .proto files found'")} | ||
|
|
||
| For each .proto file found, call extractProtoFields with its path to parse message definitions. | ||
| Return the declared output keyed by message name.`, | ||
| tools: [ | ||
| defineTool("extractProtoFields", { | ||
| description: "Read a .proto file and extract message field definitions", | ||
| parameters: s.object({ filePath: s.path }), | ||
| async handler({ filePath }) { | ||
| const content = await readFile(filePath, "utf8"); | ||
| const result: Record<string, Array<{ fieldName: string; fieldType: string; fieldNumber: number; isRepeated: boolean }>> = {}; | ||
| const messageRegex = /message\s+(\w+)\s*\{([^}]*)\}/gs; | ||
| const fieldRegex = /^\s*(repeated\s+)?(\w+)\s+(\w+)\s*=\s*(\d+)/gm; | ||
| for (const msgMatch of content.matchAll(messageRegex)) { | ||
| const messageName = msgMatch[1]; | ||
| const body = msgMatch[2]; | ||
| const fields: Array<{ fieldName: string; fieldType: string; fieldNumber: number; isRepeated: boolean }> = []; | ||
| for (const fldMatch of body.matchAll(fieldRegex)) { | ||
| fields.push({ | ||
| isRepeated: !!fldMatch[1], | ||
| fieldType: fldMatch[2], | ||
| fieldName: fldMatch[3], | ||
| fieldNumber: parseInt(fldMatch[4], 10), | ||
| }); | ||
| } | ||
| result[messageName] = fields; | ||
| } | ||
| return result; | ||
| }, | ||
| }), | ||
| ], | ||
| output: s.record(s.array(s.object({ | ||
| fieldName: s.string, | ||
| fieldType: s.string, | ||
| fieldNumber: s.int, | ||
| isRepeated: s.boolean, | ||
| }))), | ||
| addons: [repair()], | ||
| }); | ||
|
|
||
| export default protoFieldExtractor; | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| # 313 - Git Remote Url Inspector | ||
|
|
||
| ```rig | ||
| import { agent, p, s, defineTool, repair } from "rig"; | ||
|
|
||
| // Agent role: inspect git remote URLs and classify their protocol and type. | ||
| const gitRemoteUrlInspector = agent({ | ||
| model: "small", | ||
| instructions: p`You are a git remote URL inspector. | ||
|
|
||
| List all remotes and their URLs: | ||
| ${p.bash("git remote -v 2>/dev/null || echo 'no remotes'")} | ||
|
|
||
| Count remote branches on origin: | ||
| ${p.bash("git ls-remote --heads origin 2>/dev/null | wc -l || echo '0'")} | ||
|
|
||
| Use the parseRemoteLine tool for each remote line to extract protocol and type. | ||
| Return the declared output.`, | ||
| tools: [ | ||
| defineTool("parseRemoteLine", { | ||
| description: "Parse a git remote -v output line and extract name, url, protocol, and type", | ||
| parameters: s.object({ line: s.string }), | ||
| handler({ line }) { | ||
| const match = line.match(/^(\S+)\s+(\S+)\s+\((\w+)\)$/); | ||
| if (!match) return null; | ||
| const [, name, url, type] = match; | ||
| let protocol: "https" | "ssh" | "git" | "file"; | ||
| if (url.startsWith("https://")) protocol = "https" as const; | ||
| else if (url.startsWith("git@") || url.startsWith("ssh://")) protocol = "ssh" as const; | ||
| else if (url.startsWith("git://")) protocol = "git" as const; | ||
| else protocol = "file" as const; | ||
| return { name, url, type, protocol }; | ||
| }, | ||
| }), | ||
| ], | ||
| output: s.object({ | ||
| remotes: s.array(s.object({ | ||
| name: s.string, | ||
| url: s.string, | ||
| type: s.string, | ||
| protocol: s.enum("https", "ssh", "git", "file"), | ||
| })), | ||
| remoteBranchCount: s.int, | ||
| hasOrigin: s.boolean, | ||
| }), | ||
| addons: [repair()], | ||
| }); | ||
|
|
||
| export default gitRemoteUrlInspector; | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| # 314 - Ts Enum Value Extractor | ||
|
|
||
| ```rig | ||
| import { agent, p, s, defineTool, steering } from "rig"; | ||
| import { readFile } from "node:fs/promises"; | ||
|
|
||
| // Agent role: extract TypeScript enum declarations and their members from source files. | ||
| const tsEnumValueExtractor = agent({ | ||
| model: "small", | ||
| instructions: p`You are a TypeScript enum value extractor. | ||
|
|
||
| Find TypeScript files with enum declarations: | ||
| ${p.bash("grep -rln 'enum ' --include='*.ts' . 2>/dev/null | head -20 || echo 'no enums found'")} | ||
|
|
||
| For each file found, call extractEnumValues with the file path to parse all enum declarations. | ||
| Return the declared output keyed by enum name.`, | ||
| tools: [ | ||
| defineTool("extractEnumValues", { | ||
| description: "Read a TypeScript file and extract all enum declarations with their members", | ||
| parameters: s.object({ filePath: s.path }), | ||
| async handler({ filePath }) { | ||
| const content = await readFile(filePath, "utf8"); | ||
| const result: Record<string, { members: string[]; isConst: boolean; memberCount: number }> = {}; | ||
| const enumRegex = /(const\s+)?enum\s+(\w+)\s*\{([^}]*)\}/gs; | ||
| for (const match of content.matchAll(enumRegex)) { | ||
| const isConst = !!match[1]; | ||
| const enumName = match[2]; | ||
| const body = match[3]; | ||
| const members = body | ||
| .split(",") | ||
| .map((m: string) => m.trim().split(/\s*=\s*/)[0].trim()) | ||
| .filter((m: string) => m.length > 0 && !m.startsWith("//")); | ||
| result[enumName] = { members, isConst, memberCount: members.length }; | ||
| } | ||
| return result; | ||
| }, | ||
| }), | ||
| ], | ||
| output: s.record(s.object({ | ||
| members: s.array(s.string), | ||
| isConst: s.boolean, | ||
| memberCount: s.int, | ||
| })), | ||
| addons: [steering()], | ||
| }); | ||
|
|
||
| export default tsEnumValueExtractor; | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| # 315 - Json Config Merger | ||
|
|
||
| ```rig | ||
| import { agent, p, s, defineTool, repair } from "rig"; | ||
|
|
||
| // Agent role: deeply merge two JSON config files and write the result to an output path. | ||
| const jsonConfigMerger = agent({ | ||
| model: "small", | ||
| input: s.object({ | ||
| baseConfig: s.path, | ||
| overrideConfig: s.path, | ||
| outputPath: s.path, | ||
| }), | ||
| instructions: p`You are a JSON config merger. | ||
|
|
||
| Base config contents: | ||
| ${p.readInput("baseConfig")} | ||
|
|
||
| Override config contents: | ||
| ${p.readInput("overrideConfig")} | ||
|
|
||
| Use the mergeJsonObjects tool to deeply merge the override config into the base config. | ||
| Then write the merged result to ${p.writeInput("outputPath", "mergedContent")}. | ||
| Return the declared output.`, | ||
| tools: [ | ||
| defineTool("mergeJsonObjects", { | ||
| description: "Deep merge two JSON strings, returning the merged JSON and counts of added/overridden keys", | ||
| parameters: s.object({ | ||
| baseJson: s.string, | ||
| overrideJson: s.string, | ||
| }), | ||
| handler({ baseJson, overrideJson }) { | ||
| const base = JSON.parse(baseJson) as Record<string, unknown>; | ||
|
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] 💡 Fixhandler({ baseJson, overrideJson }) {
let base: Record<string, unknown>;
let override: Record<string, unknown>;
try { base = JSON.parse(baseJson); } catch { return { error: "Failed to parse base config" }; }
try { override = JSON.parse(overrideJson); } catch { return { error: "Failed to parse override config" }; }
// ... rest of merge logic
} |
||
| const override = JSON.parse(overrideJson) as Record<string, unknown>; | ||
| let keysAdded = 0; | ||
| let keysOverridden = 0; | ||
| function deepMerge( | ||
| target: Record<string, unknown>, | ||
| source: Record<string, unknown> | ||
| ): Record<string, unknown> { | ||
| const result = { ...target }; | ||
| for (const [key, value] of Object.entries(source)) { | ||
| if (key in result) { | ||
| if ( | ||
| typeof result[key] === "object" && | ||
| result[key] !== null && | ||
| typeof value === "object" && | ||
| value !== null | ||
| ) { | ||
| result[key] = deepMerge( | ||
| result[key] as Record<string, unknown>, | ||
| value as Record<string, unknown> | ||
| ); | ||
| } else { | ||
| result[key] = value; | ||
| keysOverridden++; | ||
| } | ||
| } else { | ||
| result[key] = value; | ||
| keysAdded++; | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
| const merged = deepMerge(base, override); | ||
| return { | ||
| mergedJson: JSON.stringify(merged, null, 2), | ||
| keysAdded, | ||
| keysOverridden, | ||
| totalKeys: Object.keys(merged).length, | ||
| }; | ||
| }, | ||
| }), | ||
| ], | ||
| output: s.object({ | ||
| keysAdded: s.int, | ||
| keysOverridden: s.int, | ||
| totalKeys: s.int, | ||
| outputPath: s.path, | ||
| }), | ||
| addons: [repair()], | ||
| }); | ||
|
|
||
| export default jsonConfigMerger; | ||
| ``` | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| # 316 - Markdown Toc Generator | ||
|
|
||
| ```rig | ||
| import { agent, p, s, defineTool, repair } from "rig"; | ||
| import { readFile } from "node:fs/promises"; | ||
|
|
||
| // Agent role: generate a table of contents markdown file from all headings found in workspace .md files. | ||
| const markdownTocGenerator = agent({ | ||
| model: "small", | ||
| instructions: p`You are a markdown table of contents generator. | ||
|
|
||
| Find all markdown files in the workspace: | ||
| ${p.glob("**/*.md")} | ||
|
|
||
| For each file, call extractHeadings to parse its heading structure. | ||
| Then write the combined TOC to ${p.write("TOC.md", "tocContent")}. | ||
| Return the declared output.`, | ||
| tools: [ | ||
| defineTool("extractHeadings", { | ||
| description: "Read a markdown file and extract all headings with their levels and anchor text", | ||
| parameters: s.object({ filePath: s.path }), | ||
| async handler({ filePath }) { | ||
| const content = await readFile(filePath, "utf8"); | ||
| const headings: Array<{ level: number; text: string; anchor: string }> = []; | ||
| const headingRegex = /^(#{1,6})\s+(.+)$/gm; | ||
| for (const match of content.matchAll(headingRegex)) { | ||
| const level = match[1].length; | ||
| const text = match[2].trim(); | ||
| const anchor = text.toLowerCase().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-"); | ||
| headings.push({ level, text, anchor }); | ||
| } | ||
| return { filePath, headings }; | ||
| }, | ||
| }), | ||
| ], | ||
| output: s.object({ | ||
| processedFiles: s.array(s.path), | ||
| headingCount: s.int, | ||
| outputPath: s.path, | ||
| tocGenerated: s.boolean, | ||
| }), | ||
| addons: [repair()], | ||
| }); | ||
|
|
||
| export default markdownTocGenerator; | ||
| ``` |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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] The drift detection logic is inverted — version mismatches are silently skipped, so the tool only ever reports missing packages, never actual version differences.
💡 Bug explanation
Line 48:
if (locked !== declaredClean && locked !== "missing") continue;This skips any entry where the locked version differs from the declared version and it is not "missing" — i.e., it skips every real drift case. Only the subsequent
if (locked === "missing")block ever appends todrifted.Fix the condition: