Skip to content

Commit 6a4b7f2

Browse files
Add 10 rig samples 200-209 (2026-07-27) (#211)
1 parent 68e8107 commit 6a4b7f2

10 files changed

Lines changed: 317 additions & 0 deletions
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# 200 - Npm Audit Simplifier
2+
3+
```rig
4+
import { agent, p, s, repair } from "rig";
5+
6+
// Agent role: run npm audit and produce a simplified grouped vulnerability report with remediation advice.
7+
const npmAuditSimplifierV2 = agent({
8+
model: "small",
9+
instructions: p`Run ${p.bash("npm audit --json 2>/dev/null || echo '{}'")} to get the vulnerability report. Parse the JSON output: extract each vulnerability's package name and severity (critical, high, moderate, low, info). Group package names by severity level into the vulnerabilitiesByLevel map. Count all vulnerabilities for totalCount. Write a one-sentence recommendation.`,
10+
output: s.object({
11+
vulnerabilitiesByLevel: s.record(s.array(s.string)),
12+
totalCount: s.number,
13+
recommendation: s.string,
14+
}),
15+
maxTurns: 5,
16+
addons: repair(),
17+
});
18+
19+
export default npmAuditSimplifierV2;
20+
```
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# 201 - Git Stash Inventory
2+
3+
```rig
4+
import { agent, p, s, repair } from "rig";
5+
6+
// Agent role: inventory all git stashes, listing changed files and estimating staleness.
7+
const gitStashInventoryV2 = agent({
8+
model: "small",
9+
instructions: p`List all stashes with timestamps: ${p.bash("git stash list --format='%gd|%ci|%s' 2>/dev/null || echo 'no stashes'")}. For the first three stashes show changed files: ${p.bash("git stash show --name-only stash@{0} 2>/dev/null || true")} ${p.bash("git stash show --name-only stash@{1} 2>/dev/null || true")} ${p.bash("git stash show --name-only stash@{2} 2>/dev/null || true")}. Classify staleness by commit date: fresh (< 1 week), aging (1–4 weeks), stale (1–3 months), ancient (> 3 months).`,
10+
output: s.array(s.object({
11+
stashRef: s.string,
12+
description: s.string,
13+
changedFiles: s.array(s.string),
14+
staleness: s.enum("fresh", "aging", "stale", "ancient"),
15+
})),
16+
maxTurns: 5,
17+
addons: repair(),
18+
});
19+
20+
export default gitStashInventoryV2;
21+
```
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# 202 - Ts Interface Conflict Checker
2+
3+
```rig
4+
import { agent, p, s, defineTool, repair } from "rig";
5+
6+
const scanInterfaces = defineTool("scanInterfaces", {
7+
description: "Scan a TypeScript file for exported interface names using grep",
8+
parameters: s.object({ filePath: s.string }),
9+
async handler({ filePath }) {
10+
const { execSync } = await import("node:child_process");
11+
try {
12+
const result = execSync(
13+
`grep -n "^export interface\\|^interface " "${filePath}" 2>/dev/null || true`,
14+
{ encoding: "utf8" }
15+
);
16+
const names = result
17+
.split("\n")
18+
.filter(Boolean)
19+
.map((line) => {
20+
const m = line.match(/interface\s+(\w+)/);
21+
return m ? m[1] : null;
22+
})
23+
.filter(Boolean) as string[];
24+
return { filePath, names };
25+
} catch {
26+
return { filePath, names: [] };
27+
}
28+
},
29+
});
30+
31+
// Agent role: find duplicate TypeScript interface names across all source files.
32+
const tsInterfaceConflictCheckerV2 = agent({
33+
model: "small",
34+
instructions: p`Discover TypeScript source files: ${p.bash("find . -name '*.ts' -not -path '*/node_modules/*' -not -path '*/.git/*' | head -80")}. Use the scanInterfaces tool on each file to collect interface names. Identify interface names declared in more than one file. Classify each conflict as error when names are likely to clash at runtime, warning otherwise. Set hasConflicts to true if any conflicts exist.`,
35+
output: s.object({
36+
conflicts: s.array(s.object({
37+
interfaceName: s.string,
38+
files: s.array(s.string),
39+
severity: s.enum("warning", "error"),
40+
})),
41+
hasConflicts: s.boolean,
42+
}),
43+
tools: [scanInterfaces],
44+
maxTurns: 6,
45+
addons: repair(),
46+
});
47+
48+
export default tsInterfaceConflictCheckerV2;
49+
```
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# 203 - Git Worktree Mapper
2+
3+
```rig
4+
import { agent, p, s, defineTool } from "rig";
5+
6+
const parseWorktreePorcelain = defineTool("parseWorktreePorcelain", {
7+
description: "Parse the porcelain output of git worktree list into structured entries",
8+
parameters: s.object({ output: s.string }),
9+
handler({ output }) {
10+
const entries: Array<{ path: string; branch?: string; state: string }> = [];
11+
const blocks = output.trim().split("\n\n");
12+
for (const block of blocks) {
13+
const lines = block.split("\n");
14+
const pathLine = lines.find((l) => l.startsWith("worktree "));
15+
const branchLine = lines.find((l) => l.startsWith("branch "));
16+
const isLocked = lines.some((l) => l.startsWith("locked"));
17+
const isBare = lines.some((l) => l.startsWith("bare"));
18+
const entry: { path: string; branch?: string; state: string } = {
19+
path: pathLine ? pathLine.replace("worktree ", "") : "",
20+
state: isLocked ? "locked" : isBare ? "bare" : "clean",
21+
};
22+
if (branchLine) entry.branch = branchLine.replace("branch refs/heads/", "");
23+
if (entry.path) entries.push(entry);
24+
}
25+
return entries;
26+
},
27+
});
28+
29+
// Agent role: map all git worktrees and summarize active vs total count.
30+
const gitWorktreeMapperV2 = agent({
31+
model: "small",
32+
instructions: p`Get all git worktrees: ${p.bash("git worktree list --porcelain 2>/dev/null || echo ''")}. Use the parseWorktreePorcelain tool to parse the output into structured entries. For each worktree check if there are uncommitted changes (set state to dirty). Count all worktrees for totalCount and non-bare ones for activeCount.`,
33+
output: s.object({
34+
worktrees: s.array(s.object({
35+
path: s.string,
36+
branch: s.optional(s.string),
37+
state: s.enum("locked", "bare", "clean", "dirty"),
38+
})),
39+
summary: s.object({
40+
totalCount: s.int,
41+
activeCount: s.int,
42+
}),
43+
}),
44+
tools: [parseWorktreePorcelain],
45+
});
46+
47+
export default gitWorktreeMapperV2;
48+
```
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# 204 - Test Naming Enforcer
2+
3+
```rig
4+
import { agent, p, s, steering } from "rig";
5+
6+
// Agent role: audit test file naming conventions and report files that violate the standard pattern.
7+
const testNamingEnforcerV2 = agent({
8+
model: "small",
9+
instructions: p`Find all test files: ${p.bash("find . \\( -name '*.test.ts' -o -name '*.spec.ts' -o -name '*.test.js' -o -name '*.spec.js' -o -name '*.test.tsx' -o -name '*.spec.tsx' \\) -not -path '*/node_modules/*' | head -80")}. For each file classify the naming convention: correct if the filename follows <subject>.test.<ext> or <subject>.spec.<ext>, wrong-prefix if the prefix before the dot is unusual, wrong-suffix if the extension or suffix is non-standard, missing-spec if the file appears to be a test but lacks a .test. or .spec. marker. Provide suggestedName only when a correction is needed. Set allConform to true only when every file is classified as correct.`,
10+
output: s.object({
11+
files: s.record(s.object({
12+
convention: s.enum("correct", "wrong-prefix", "wrong-suffix", "missing-spec"),
13+
suggestedName: s.optional(s.string),
14+
})),
15+
allConform: s.boolean,
16+
}),
17+
maxTurns: 5,
18+
addons: steering({ message: "Ensure every discovered test file has an entry and allConform reflects all entries." }),
19+
});
20+
21+
export default testNamingEnforcerV2;
22+
```
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# 205 - Pkg Dependency Graph
2+
3+
```rig
4+
import { agent, p, s, defineTool } from "rig";
5+
6+
const classifyDependency = defineTool("classifyDependency", {
7+
description: "Classify a dependency as runtime, dev, or peer based on presence in package.json sections",
8+
parameters: s.object({
9+
name: s.string,
10+
inDependencies: s.boolean,
11+
inDevDependencies: s.boolean,
12+
inPeerDependencies: s.boolean,
13+
}),
14+
handler({ inDependencies, inDevDependencies, inPeerDependencies }) {
15+
if (inPeerDependencies) return "peer";
16+
if (inDevDependencies) return "dev";
17+
if (inDependencies) return "runtime";
18+
return "dev";
19+
},
20+
});
21+
22+
// Agent role: extract and classify direct dependencies from package.json and describe the dependency tree shape.
23+
const pkgDependencyGraphV2 = agent({
24+
model: "small",
25+
instructions: p`Read the project manifest: ${p.read("package.json")}. Get the resolved dependency tree: ${p.bash("npm ls --json --depth=1 2>/dev/null || echo '{}'")}. Use the classifyDependency tool for each direct dependency. List all devDependency names. Classify treeShape as flat (< 5 total deps), shallow (5–20), or deep (> 20). Set depthScore to the total direct dependency count.`,
26+
output: s.object({
27+
directDeps: s.array(s.object({
28+
name: s.string,
29+
version: s.string,
30+
type: s.enum("runtime", "dev", "peer"),
31+
})),
32+
devDeps: s.array(s.string),
33+
treeShape: s.enum("flat", "shallow", "deep"),
34+
depthScore: s.number,
35+
}),
36+
tools: [classifyDependency],
37+
});
38+
39+
export default pkgDependencyGraphV2;
40+
```
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# 206 - Ci Flake Triager
2+
3+
```rig
4+
import { agent, p, s, repair } from "rig";
5+
6+
// Agent role: triage the most recent CI test failure and classify its root cause.
7+
const ciFlakeTriager = agent({
8+
model: "small",
9+
instructions: p`Inspect available CI logs: ${p.bash("cat .github/test-results.log 2>/dev/null || cat test-results.log 2>/dev/null || echo 'no test log found'")}. Also read the CI workflow configuration: ${p.readOptional(".github/workflows/ci.yml", "no ci workflow found")}. Classify the failure type: infrastructure (runner issue, network, timeout on setup), assertion (test logic failed), timeout (test exceeded time limit), or unknown. Estimate confidence. List the names of affected tests if identifiable. Provide a concrete one-sentence retry advice.`,
10+
output: s.object({
11+
failureClass: s.enum("infrastructure", "assertion", "timeout", "unknown"),
12+
confidence: s.enum("high", "medium", "low"),
13+
retryAdvice: s.string,
14+
affectedTests: s.array(s.string),
15+
}),
16+
maxTurns: 4,
17+
addons: repair(),
18+
});
19+
20+
export default ciFlakeTriager;
21+
```
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# 207 - Config Drift Reconciler
2+
3+
```rig
4+
import { agent, p, s } from "rig";
5+
6+
// Agent role: compare two config files, identify drifted settings, and write a corrected patch.
7+
const configDriftReconciler = agent({
8+
model: "small",
9+
instructions: p`Read the baseline ESLint config: ${p.readOptional(".eslintrc.json", "{}")}. Read the active config: ${p.readOptional(".eslintrc.js", "module.exports = {}")}. Also check for any local overrides: ${p.bash("find . -maxdepth 2 -name '.eslintrc*' -not -path '*/node_modules/*' 2>/dev/null || true")}. Identify keys that differ between the baseline and active configs. Write a normalized patch to ${p.write("config-patch.json", "PATCH_CONTENT")} showing the corrected settings. Report each drifted key with its baseline and actual value. Set normalized to true if you were able to resolve all differences.`,
10+
output: s.object({
11+
changedKeys: s.record(s.object({
12+
baseline: s.unknown,
13+
actual: s.unknown,
14+
})),
15+
summary: s.object({
16+
totalDrifted: s.int,
17+
totalChecked: s.int,
18+
normalized: s.boolean,
19+
}),
20+
}),
21+
});
22+
23+
export default configDriftReconciler;
24+
```
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# 208 - Release Note Enricher
2+
3+
```rig
4+
import { agent, p, s, defineTool, repair, steering } from "rig";
5+
6+
const lookupTicketMetadata = defineTool("lookupTicketMetadata", {
7+
description: "Extract ticket references like #123 or PROJ-456 from text",
8+
parameters: s.object({ text: s.string }),
9+
handler({ text }) {
10+
const githubRefs = [...text.matchAll(/#(\d+)/g)].map((m) => `#${m[1]}`);
11+
const jiraRefs = [...text.matchAll(/\b([A-Z]+-\d+)\b/g)].map((m) => m[1]);
12+
return { githubRefs, jiraRefs, all: [...githubRefs, ...jiraRefs] };
13+
},
14+
});
15+
16+
// Agent role: enrich raw release notes with structured sections, a risk label, and missing reference warnings.
17+
const releaseNoteEnricher = agent({
18+
model: "small",
19+
input: s.object({
20+
rawNotes: s.string,
21+
}),
22+
instructions: p`You have raw release notes in the input. Use the lookupTicketMetadata tool to extract any ticket references from the text. Group the notes into logical sections (e.g., Features, Bug Fixes, Breaking Changes, Chores). Assign a riskLabel of low, medium, high, or critical based on whether there are breaking changes or critical fixes. List any ticket references that appear in the notes but could not be resolved or verified.`,
23+
output: s.object({
24+
sections: s.array(s.object({
25+
heading: s.string,
26+
items: s.array(s.string),
27+
})),
28+
riskLabel: s.enum("low", "medium", "high", "critical"),
29+
missingReferences: s.array(s.string),
30+
}),
31+
tools: [lookupTicketMetadata],
32+
maxTurns: 5,
33+
addons: [steering(), repair()],
34+
});
35+
36+
export default releaseNoteEnricher;
37+
```
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# 209 - Docs Refactor Coordinator
2+
3+
```rig
4+
import { agent, p, s } from "rig";
5+
6+
// Agent role: extract API names mentioned in the documentation.
7+
const apiExtractor = agent({
8+
name: "apiExtractor",
9+
model: "nano",
10+
instructions: p`Read the documentation: ${p.read("README.md")}. Extract every API name, function signature, and method reference mentioned in the document. Return a flat list of unique API identifiers.`,
11+
output: s.array(s.string),
12+
});
13+
14+
// Agent role: rewrite documentation prose for clarity and conciseness.
15+
const proseCleanup = agent({
16+
name: "proseCleanup",
17+
model: "nano",
18+
instructions: p`Read the documentation: ${p.read("README.md")}. Rewrite the prose to be clearer, more concise, and better structured. Return only the improved text.`,
19+
output: s.string,
20+
});
21+
22+
// Agent role: coordinate docs refactoring by extracting APIs and cleaning prose, then writing the result.
23+
const docsRefactorCoordinator = agent({
24+
model: "small",
25+
instructions: p`Delegate API extraction and prose cleanup to the named subagents. Merge their results: the apiExtractor returns the list of API names, and proseCleanup returns rewritten prose. Combine into a final output and write the refactored content to ${p.write("docs/refactored.md", "REFACTORED_CONTENT")}. Count the changes made to the prose.`,
26+
output: s.object({
27+
extractedApis: s.array(s.string),
28+
changesApplied: s.int,
29+
outputPath: s.path,
30+
}),
31+
agents: { apiExtractor, proseCleanup },
32+
});
33+
34+
export default docsRefactorCoordinator;
35+
```

0 commit comments

Comments
 (0)