Skip to content

Commit a8ea2e1

Browse files
Add 10 rig samples — 2026-10-09 (#602)
Add rig sample programs demonstrating commit classification, tsconfig validation, repair/timeout/steering addons, sequential and parallel workflow composition, glob-based subagent fan-out, p.write side effects, and a pure-logic custom tool. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent a189032 commit a8ea2e1

10 files changed

Lines changed: 312 additions & 0 deletions
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# 531 - Commit Type Classifier
2+
3+
```rig
4+
import { agent, p, s } from "rig";
5+
6+
// Agent role: classify recent commit subjects into conventional-commit types.
7+
const classifyCommits = agent({
8+
model: "small",
9+
instructions: p`Classify each commit subject from ${p.bash("git log --oneline -n 30 --pretty=format:'%s'")}
10+
into a conventional-commit type and group subjects by type.`,
11+
output: s.record(
12+
s.array(s.string),
13+
"commit subjects grouped by conventional-commit type",
14+
),
15+
});
16+
17+
export default classifyCommits;
18+
```
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# 532 - Tsconfig Deep Validator
2+
3+
```rig
4+
import { agent, p, s } from "rig";
5+
6+
// Agent role: validate tsconfig.json compiler options and report issues.
7+
const validateTsconfig = agent({
8+
model: "small",
9+
instructions: p`Validate ${p.read("tsconfig.json")} and report whether strict mode
10+
is enabled, the configured target, whether path mappings are configured, the
11+
path mappings themselves if present, and any validation issues found.`,
12+
output: s.object({
13+
hasStrict: s.boolean,
14+
target: s.optional(s.string),
15+
pathsConfigured: s.boolean,
16+
pathMappings: s.optional(s.record(s.array(s.string))),
17+
issues: s.array(s.string),
18+
}),
19+
});
20+
21+
export default validateTsconfig;
22+
```
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# 533 - Repair Addon Strict Output
2+
3+
```rig
4+
import { agent, p, repair, s } from "rig";
5+
6+
// Agent role: assess working tree status and return a risk verdict.
7+
const assessStatus = agent({
8+
model: "small",
9+
maxTurns: 6,
10+
instructions: p`Review ${p.bash("git status --short")} and return a concise summary,
11+
an overall risk level, and concrete action items.`,
12+
output: s.object({
13+
summary: s.string,
14+
riskLevel: s.enum("low", "medium", "high"),
15+
actionItems: s.array(s.string),
16+
}),
17+
addons: repair(),
18+
});
19+
20+
export default assessStatus;
21+
```
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# 534 - Healthcheck Timeout Status
2+
3+
```rig
4+
import { agent, p, s, timeout } from "rig";
5+
6+
// Agent role: run a health-check command and report its status within a time budget.
7+
const healthCheck = agent({
8+
model: "small",
9+
instructions: p`Run ${p.bash("node --version")} as a health-check probe. Report
10+
"ok" if it succeeded, "error" if it failed, or "timeout" if it did not
11+
complete, along with the approximate duration in milliseconds.`,
12+
output: s.object({
13+
status: s.enum("ok", "timeout", "error"),
14+
durationMs: s.number,
15+
}),
16+
addons: timeout({ timeout: 15_000 }),
17+
});
18+
19+
export default healthCheck;
20+
```
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# 535 - Lint Test Gate Workflow
2+
3+
```rig
4+
import { agent, p, s, workflow } from "rig";
5+
6+
const checkResult = s.object({
7+
passed: s.boolean,
8+
issues: s.array(s.string),
9+
});
10+
11+
// Agent role: run a typecheck-style lint pass and report issues.
12+
const lintCheck = agent({
13+
model: "small",
14+
instructions: p`Run ${p.bash("npx tsc --noEmit")} and report whether it passed
15+
and list any issues found.`,
16+
output: checkResult,
17+
});
18+
19+
// Agent role: run the test suite and report issues.
20+
const testCheck = agent({
21+
model: "small",
22+
instructions: p`Run ${p.bash("npx vitest run")} and report whether it passed
23+
and list any issues found.`,
24+
output: checkResult,
25+
});
26+
27+
// Agent role: combine lint and test results into an approval decision.
28+
const gate = agent({
29+
model: "small",
30+
input: s.object({ lint: checkResult, test: checkResult }),
31+
instructions:
32+
"Given input.lint and input.test, decide whether to approve or block, and explain why.",
33+
output: s.object({
34+
decision: s.enum("approved", "blocked"),
35+
reasons: s.array(s.string),
36+
}),
37+
});
38+
39+
// Workflow role: sequentially gate changes on lint and test results.
40+
const releaseGate = workflow({
41+
meta: {
42+
name: "release-gate",
43+
description: "Chain lint-check and test-check results into a gate decision",
44+
phases: ["Lint", "Test", "Gate"],
45+
},
46+
body: async ({ call, phase }) => {
47+
phase("Lint");
48+
const lint = await call(lintCheck, "");
49+
phase("Test");
50+
const test = await call(testCheck, "");
51+
phase("Gate");
52+
return call(gate, {
53+
lint: lint ?? { passed: false, issues: ["lint check failed to run"] },
54+
test: test ?? { passed: false, issues: ["test check failed to run"] },
55+
});
56+
},
57+
});
58+
59+
export default releaseGate;
60+
```
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# 536 - Glob Tagger Fanout
2+
3+
```rig
4+
import { agent, p, s } from "rig";
5+
6+
// Agent role: assign a topical tag to a single source file.
7+
const tagger = agent({
8+
model: "small",
9+
input: s.object({ path: s.path }),
10+
instructions: p`Read ${p.readInput("path")} and assign the single most fitting tag.`,
11+
output: s.object({
12+
path: s.path,
13+
tag: s.enum("test", "schema", "engine", "prompt", "utility", "other"),
14+
}),
15+
});
16+
17+
// Agent role: discover source files, delegate tagging, and aggregate by tag.
18+
const coordinator = agent({
19+
model: "small",
20+
agents: { tagger },
21+
instructions: p`Find source files with ${p.glob("src/**/*.ts")}. For each
22+
discovered path, delegate to tagger, then return a record mapping each
23+
tag to the list of file paths assigned that tag.`,
24+
output: s.record(s.array(s.path), "file paths grouped by assigned tag"),
25+
});
26+
27+
export default coordinator;
28+
```
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# 537 - Line Count Report Writer
2+
3+
```rig
4+
import { agent, p, s } from "rig";
5+
6+
// Agent role: count lines per TypeScript source file and write a markdown report.
7+
const lineCountReport = agent({
8+
model: "small",
9+
instructions: p`Run ${p.bash("find src -name '*.ts' -exec wc -l {} +")} to get
10+
per-file line counts. Compose a markdown report summarizing the counts per
11+
file and the total, then ${p.writeOutput("report", "/tmp/gh-aw/agent/line-count-report.md")}.
12+
Return the report path, the total line count across all files, and the
13+
number of files counted.`,
14+
output: s.object({
15+
report: s.string,
16+
reportPath: s.path,
17+
totalLines: s.int,
18+
fileCount: s.int,
19+
}),
20+
});
21+
22+
export default lineCountReport;
23+
```
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# 538 - Semver Satisfies Tool
2+
3+
```rig
4+
import { agent, defineTool, p, s } from "rig";
5+
6+
// Tool: check whether a version satisfies a minimum-version range like ">=1.0.0".
7+
const checkSemverSatisfies = defineTool("check_semver_satisfies", {
8+
description: "Check whether a semantic version satisfies a minimum-version range.",
9+
parameters: s.object({
10+
version: s.string,
11+
range: s.string,
12+
}),
13+
handler: async ({ version, range }: { version: string; range: string }) => {
14+
const parse = (v: string) =>
15+
v
16+
.replace(/^[^0-9]*/, "")
17+
.split(".")
18+
.map((part: string) => Number.parseInt(part, 10) || 0);
19+
const match = range.match(/^>=\s*([\d.]+)/);
20+
if (!match) return { satisfies: false };
21+
const [vMajor, vMinor, vPatch] = parse(version);
22+
const [rMajor, rMinor, rPatch] = parse(match[1]);
23+
const satisfies =
24+
vMajor > rMajor ||
25+
(vMajor === rMajor && vMinor > rMinor) ||
26+
(vMajor === rMajor && vMinor === rMinor && vPatch >= rPatch);
27+
return { satisfies };
28+
},
29+
});
30+
31+
// Agent role: check each package.json dependency against a minimum-version range.
32+
const dependencyRangeCheck = agent({
33+
model: "small",
34+
instructions: p`Read ${p.read("package.json")} and list its dependencies. For
35+
each dependency, call check_semver_satisfies with its declared version and
36+
range ">=1.0.0", then return the combined results.`,
37+
tools: [checkSemverSatisfies],
38+
output: s.array(
39+
s.object({
40+
name: s.string,
41+
version: s.string,
42+
satisfiesRange: s.boolean,
43+
}),
44+
),
45+
});
46+
47+
export default dependencyRangeCheck;
48+
```
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# 539 - Todo Fixme Parallel Workflow
2+
3+
```rig
4+
import { agent, p, s, workflow } from "rig";
5+
6+
// Agent role: count TODO comments across the repository.
7+
const countTodos = agent({
8+
model: "small",
9+
instructions: p`Run ${p.bash("grep -ro 'TODO' src/ | wc -l")} and return the count.`,
10+
output: s.object({ count: s.int }),
11+
});
12+
13+
// Agent role: count FIXME comments across the repository.
14+
const countFixmes = agent({
15+
model: "small",
16+
instructions: p`Run ${p.bash("grep -ro 'FIXME' src/ | wc -l")} and return the count.`,
17+
output: s.object({ count: s.int }),
18+
});
19+
20+
// Agent role: combine TODO and FIXME counts into a total.
21+
const aggregate = agent({
22+
model: "small",
23+
input: s.object({ todoCount: s.int, fixmeCount: s.int }),
24+
instructions: "Combine input.todoCount and input.fixmeCount into a total.",
25+
output: s.object({ todoCount: s.int, fixmeCount: s.int, total: s.int }),
26+
});
27+
28+
// Workflow role: count TODO and FIXME comments in parallel, then aggregate.
29+
const commentAudit = workflow({
30+
meta: {
31+
name: "comment-audit",
32+
description: "Count TODO and FIXME comments in parallel and aggregate totals",
33+
phases: ["Count", "Aggregate"],
34+
},
35+
body: async ({ call, parallel, phase }) => {
36+
phase("Count");
37+
const [todo, fixme] = await parallel([
38+
() => call(countTodos, ""),
39+
() => call(countFixmes, ""),
40+
]);
41+
phase("Aggregate");
42+
return call(aggregate, {
43+
todoCount: todo?.count ?? 0,
44+
fixmeCount: fixme?.count ?? 0,
45+
});
46+
},
47+
});
48+
49+
export default commentAudit;
50+
```
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# 540 - Steering Nullable Commit
2+
3+
```rig
4+
import { agent, p, repair, s, steering } from "rig";
5+
6+
// Agent role: report the most recent commit message, or null if there is none.
7+
const lastCommit = agent({
8+
model: "small",
9+
maxTurns: 4,
10+
instructions: p`Run ${p.bash("git log -n 1 --pretty=format:'%s'")}. If the
11+
output is empty, return null for lastCommitMessage and 0 for commitCount.
12+
Otherwise return the commit subject as lastCommitMessage and 1 for
13+
commitCount.`,
14+
output: s.object({
15+
lastCommitMessage: s.nullable(s.string),
16+
commitCount: s.int,
17+
}),
18+
addons: [steering(), repair()],
19+
});
20+
21+
export default lastCommit;
22+
```

0 commit comments

Comments
 (0)