Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions skills/rig/samples.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Generated by `npm run sample:catalog`; do not edit the inventory by hand.

690 samples: 632 Markdown programs and 58 TypeScript fixtures.
700 samples: 642 Markdown programs and 58 TypeScript fixtures.
Every sample has one primary **pattern bucket**, a **topic cluster**, and a normalized task **family**.
Buckets come from program syntax, not filenames. A workflow with tools stays in workflows; a coordinator with tools stays in delegation.
Markdown and TypeScript versions are kept separately because they exercise different launcher contracts; provider integration fixtures are also intentional.
Expand All @@ -13,11 +13,11 @@ Legacy filenames are stable identifiers, not an authoritative description of the
| Bucket | Samples | Read when |
|--------|---------|-----------|
| [Schema-first agents](#schemas) | 61 | Typed caller inputs and constrained outputs without workspace intents. |
| [Prompt context and side effects](#context) | 70 | Reading or discovering workspace context and declaring output side effects. |
| [Prompt context and side effects](#context) | 73 | Reading or discovering workspace context and declaring output side effects. |
| [Runtime and addons](#runtime) | 43 | Invocation options, engines, timeouts, repair, steering, and addon hooks. |
| [Custom tools](#tools) | 419 | Model-callable deterministic operations, parsing, and Node.js APIs. |
| [Custom tools](#tools) | 423 | Model-callable deterministic operations, parsing, and Node.js APIs. |
| [Model-driven delegation](#delegation) | 56 | Named specialists coordinated by a model-driven root agent. |
| [Deterministic workflows](#workflows) | 41 | Explicit sequencing, bounded fan-out, pipelines, budgets, and nested workflows. |
| [Deterministic workflows](#workflows) | 44 | Explicit sequencing, bounded fan-out, pipelines, budgets, and nested workflows. |

Use `node scripts/sample-catalog.ts --json` for the complete inventory with role comments, task families, and exercised APIs.
For a focused search, append `--family <slug>` or `--bucket <key>`; bucket keys are `schemas`, `context`, `runtime`, `tools`, `delegation`, and `workflows`.
Expand Down Expand Up @@ -167,6 +167,7 @@ For a focused search, append `--family <slug>` or `--bucket <key>`; bucket keys
| genaiscript-list-files-port | [61-genaiscript-list-files-port.md](samples/61-genaiscript-list-files-port.md) |
| genaiscript-todo-port | [62-genaiscript-todo-port.md](samples/62-genaiscript-todo-port.md) |
| line-count-report-writer | [537-line-count-report-writer.md](samples/537-line-count-report-writer.md) |
| nested-catalog-budget-allocator | [558-nested-catalog-budget-allocator.md](samples/558-nested-catalog-budget-allocator.md) |
| prompt-intents | [47-prompt-intents.ts](../../src/samples/47-prompt-intents.ts) |
| readonly-investigator | [34-intent-options.ts](../../src/samples/34-intent-options.ts) |
| species-observation-digest | [544-species-observation-digest.md](samples/544-species-observation-digest.md) |
Expand All @@ -192,7 +193,9 @@ For a focused search, append `--family <slug>` or `--bucket <key>`; bucket keys

| Family | Samples |
|--------|---------|
| conflicting-source-timeline-reconciler | [554-conflicting-source-timeline-reconciler.md](samples/554-conflicting-source-timeline-reconciler.md) |
| list-source-files | [06-list-source-files.md](samples/06-list-source-files.md) |
| tabular-measurement-source-priority-reconciler | [556-tabular-measurement-source-priority-reconciler.md](samples/556-tabular-measurement-source-priority-reconciler.md) |
| ts-export-reporter | [438-ts-export-reporter.md](samples/438-ts-export-reporter.md) |

<a id="runtime"></a>
Expand Down Expand Up @@ -349,12 +352,14 @@ For a focused search, append `--family <slug>` or `--bucket <key>`; bucket keys
| json-schema-structure-validator | [329-json-schema-structure-validator.md](samples/329-json-schema-structure-validator.md), [402-json-schema-structure-validator.md](samples/402-json-schema-structure-validator.md) |
| json-schema-validator | [396-json-schema-validator.md](samples/396-json-schema-validator.md) |
| jsonl-file-analyzer | [379-jsonl-file-analyzer.md](samples/379-jsonl-file-analyzer.md), [431-jsonl-file-analyzer.md](samples/431-jsonl-file-analyzer.md), [435-jsonl-file-analyzer.md](samples/435-jsonl-file-analyzer.md) |
| linked-record-foreign-key-join | [555-linked-record-foreign-key-join.md](samples/555-linked-record-foreign-key-join.md) |
| markdown-frontmatter-checker | [139-markdown-frontmatter-checker.md](samples/139-markdown-frontmatter-checker.md) |
| markdown-frontmatter-extractor | [337-markdown-frontmatter-extractor.md](samples/337-markdown-frontmatter-extractor.md), [403-markdown-frontmatter-extractor.md](samples/403-markdown-frontmatter-extractor.md), [440-markdown-frontmatter-extractor.md](samples/440-markdown-frontmatter-extractor.md) |
| markdown-heading-validator | [289-markdown-heading-validator.md](samples/289-markdown-heading-validator.md), [376-markdown-heading-validator.md](samples/376-markdown-heading-validator.md) |
| markdown-link-checker | [129-markdown-link-checker.md](samples/129-markdown-link-checker.md), [242-markdown-link-checker.md](samples/242-markdown-link-checker.md) |
| markdown-table-writer | [438-markdown-table-writer.md](samples/438-markdown-table-writer.md) |
| markdown-toc-generator | [316-markdown-toc-generator.md](samples/316-markdown-toc-generator.md) |
| multilingual-linked-record-manifest | [559-multilingual-linked-record-manifest.md](samples/559-multilingual-linked-record-manifest.md) |
| proto-field-extractor | [189-proto-field-extractor.md](samples/189-proto-field-extractor.md), [312-proto-field-extractor.md](samples/312-proto-field-extractor.md) |
| readme-badge-analyzer | [350-readme-badge-analyzer.md](samples/350-readme-badge-analyzer.md), [412-readme-badge-analyzer.md](samples/412-readme-badge-analyzer.md), [416-readme-badge-analyzer-v2.md](samples/416-readme-badge-analyzer-v2.md) |
| workspace-symlink-inventory | [448-workspace-symlink-inventory.md](samples/448-workspace-symlink-inventory.md), [458-workspace-symlink-inventory.md](samples/458-workspace-symlink-inventory.md) |
Expand Down Expand Up @@ -437,6 +442,7 @@ For a focused search, append `--family <slug>` or `--bucket <key>`; bucket keys
| makefile-phony-extractor | [348-makefile-phony-extractor.md](samples/348-makefile-phony-extractor.md), [414-makefile-phony-extractor-v2.md](samples/414-makefile-phony-extractor-v2.md) |
| makefile-target-extractor | [167-makefile-target-extractor.md](samples/167-makefile-target-extractor.md), [284-makefile-target-extractor.md](samples/284-makefile-target-extractor.md), [294-makefile-target-extractor.md](samples/294-makefile-target-extractor.md) |
| monorepo-workspace-lister | [249-monorepo-workspace-lister.md](samples/249-monorepo-workspace-lister.md), [356-monorepo-workspace-lister-v2.md](samples/356-monorepo-workspace-lister-v2.md), [364-monorepo-workspace-lister-v3.md](samples/364-monorepo-workspace-lister-v3.md) |
| nested-catalog-partial-rollup | [551-nested-catalog-partial-rollup.md](samples/551-nested-catalog-partial-rollup.md) |
| node-module-path-resolver | [477-node-module-path-resolver.md](samples/477-node-module-path-resolver.md) |
| openapi-spec-validator | [161-openapi-spec-validator.md](samples/161-openapi-spec-validator.md) |
| os-path-structure-analyzer | [415-os-path-structure-analyzer.md](samples/415-os-path-structure-analyzer.md) |
Expand Down Expand Up @@ -489,6 +495,7 @@ For a focused search, append `--family <slug>` or `--bucket <key>`; bucket keys
| json-schema-type-distribution | [500-json-schema-type-distribution.md](samples/500-json-schema-type-distribution.md) |
| json-union-type-inferrer | [440-json-union-type-inferrer.md](samples/440-json-union-type-inferrer.md) |
| markdown-link-classifier | [428-markdown-link-classifier.md](samples/428-markdown-link-classifier.md) |
| resource-schedule-partial-lookup | [552-resource-schedule-partial-lookup.md](samples/552-resource-schedule-partial-lookup.md) |
| source-comment-density-reporter | [350-source-comment-density-reporter.md](samples/350-source-comment-density-reporter.md), [416-source-comment-density-reporter.md](samples/416-source-comment-density-reporter.md) |
| source-complexity-heatmap | [199-source-complexity-heatmap.md](samples/199-source-complexity-heatmap.md) |
| source-file-checksum-reporter | [478-source-file-checksum-reporter.md](samples/478-source-file-checksum-reporter.md) |
Expand Down Expand Up @@ -626,6 +633,13 @@ For a focused search, append `--family <slug>` or `--bucket <key>`; bucket keys
| npm-script-dependency-workflow | [420-npm-script-dependency-workflow.md](samples/420-npm-script-dependency-workflow.md) |
| pkg-scripts-trio-workflow | [399-pkg-scripts-trio-workflow.md](samples/399-pkg-scripts-trio-workflow.md), [461-pkg-scripts-trio-workflow.md](samples/461-pkg-scripts-trio-workflow.md) |

### Docs and structured data

| Family | Samples |
|--------|---------|
| linked-record-chain-budget-walk | [553-linked-record-chain-budget-walk.md](samples/553-linked-record-chain-budget-walk.md) |
| linked-record-recency-reconciler-workflow | [560-linked-record-recency-reconciler-workflow.md](samples/560-linked-record-recency-reconciler-workflow.md) |

### Git history and workspace

| Family | Samples |
Expand All @@ -652,6 +666,7 @@ For a focused search, append `--family <slug>` or `--bucket <key>`; bucket keys
| parallel-dep-audit-workflow | [488-parallel-dep-audit-workflow.md](samples/488-parallel-dep-audit-workflow.md) |
| parallel-multi-tool-workflow | [390-parallel-multi-tool-workflow.md](samples/390-parallel-multi-tool-workflow.md), [444-parallel-multi-tool-workflow.md](samples/444-parallel-multi-tool-workflow.md), [454-parallel-multi-tool-workflow.md](samples/454-parallel-multi-tool-workflow.md) |
| parallel-project-health | [439-parallel-project-health.md](samples/439-parallel-project-health.md) |
| parallel-timeline-conflict-flagger | [557-parallel-timeline-conflict-flagger.md](samples/557-parallel-timeline-conflict-flagger.md) |
| parallel-wordfreq-aggregator | [529-parallel-wordfreq-aggregator.md](samples/529-parallel-wordfreq-aggregator.md) |
| todo-fixme-parallel-workflow | [539-todo-fixme-parallel-workflow.md](samples/539-todo-fixme-parallel-workflow.md) |
| workflow-audit-verify | [310-workflow-audit-verify.md](samples/310-workflow-audit-verify.md) |
Expand Down
65 changes: 65 additions & 0 deletions skills/rig/samples/551-nested-catalog-partial-rollup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# 551 - Nested Catalog Partial Rollup

```rig
import { agent, defineTool, p, s } from "rig";

// Tool: recursively roll up completion stats from a nested catalog, counting
// only "observed" lessons; subtrees with none observed stay out of the
// denominator (missing data) instead of counting as zero progress.
const rollupCatalog = defineTool("rollup_catalog", {
description: "Recursively compute per-program completion percentages from a nested catalog, using only observed lessons.",
parameters: s.object({ catalogJson: s.string }),
handler: ({ catalogJson }: { catalogJson: string }) => {
type Lesson = { observed: boolean; completed: boolean };
type Node = { lessons?: Lesson[]; submodules?: Record<string, Node> };
const catalog: Record<string, { modules: Record<string, Node> }> = JSON.parse(catalogJson);
const result: Record<string, { completionPct: number; incompleteModules: string[] }> = {};
const walk = (path: string, node: Node, miss: string[]): { obs: number; done: number } => {
const own = (node.lessons ?? []).filter((l: Lesson) => l.observed);
let obs = own.length, done = own.filter((l: Lesson) => l.completed).length;
for (const [name, child] of Object.entries(node.submodules ?? {})) {
const st = walk(`${path}/${name}`, child, miss);
obs += st.obs; done += st.done;
}
if (obs === 0) miss.push(path);
return { obs, done };
};
for (const [prog, data] of Object.entries(catalog)) {
const miss: string[] = [];
let obs = 0, done = 0;
for (const [name, mod] of Object.entries(data.modules)) {
const st = walk(name, mod, miss);
obs += st.obs; done += st.done;
}
result[prog] = { completionPct: obs > 0 ? Math.round((done / obs) * 1000) / 10 : 0, incompleteModules: miss };
}
return { result };
},
});

// Agent role: read an embedded nested course catalog (programs > modules >
// submodules > lessons, arbitrary depth) and compute per-program completion
// from only observed lessons, leaving unobserved branches out of the math.
const nestedCatalogPartialRollup = agent({
model: "small",
tools: [rollupCatalog],
instructions: p`Nested language-learning course catalog as JSON (a module's
"lessons" each have an "observed" flag meaning a learner attempted it,
and a "completed" flag; modules may nest "submodules" to any depth):
${p.bash(`cat <<'EOF'
{"Spanish Fluency":{"modules":{"Foundations":{"lessons":[{"name":"Greetings","observed":true,"completed":true},{"name":"Numbers","observed":true,"completed":false},{"name":"Colors","observed":false,"completed":false}],"submodules":{"Pronunciation":{"lessons":[{"name":"Vowels","observed":true,"completed":true}]}}},"Conversation":{"lessons":[{"name":"Ordering Food","observed":false,"completed":false}],"submodules":{"Travel Phrases":{"lessons":[{"name":"Directions","observed":false,"completed":false}]}}}}},"Japanese for Travel":{"modules":{"Scripts":{"lessons":[{"name":"Hiragana","observed":true,"completed":true},{"name":"Katakana","observed":true,"completed":true}],"submodules":{"Kanji Basics":{"lessons":[{"name":"Radicals","observed":true,"completed":false}],"submodules":{"Stroke Order":{"lessons":[{"name":"Numbers Kanji","observed":false,"completed":false}]}}}}},"Etiquette":{"lessons":[{"name":"Bowing","observed":false,"completed":false}]}}}}
EOF`)}

Call rollup_catalog once with this entire JSON object serialized exactly
as shown (as the catalogJson string argument), then return its "result"
field directly as your output.`,
output: s.record(
s.object({
completionPct: s.number,
incompleteModules: s.array(s.string),
}),
),
});

export default nestedCatalogPartialRollup;
```
63 changes: 63 additions & 0 deletions skills/rig/samples/552-resource-schedule-partial-lookup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# 552 - Resource Schedule Partial Lookup

```rig
import { agent, defineTool, p, s } from "rig";

// Tool: look up a reservoir's scheduled release window by station id from an embedded table.
const lookupReleaseWindow = defineTool("lookup_release_window", {
description: "Look up the scheduled release window for a reservoir station by its station id.",
parameters: s.object({
stationId: s.string,
}),
handler: async ({ stationId }: { stationId: string }) => {
const schedule: Record<string, string> = {
"res-01": "06:00-08:00",
"res-02": "08:00-10:00",
"res-03": "10:00-12:00",
"res-04": "12:00-14:00",
"res-05": "14:00-16:00",
"res-06": "16:00-18:00",
"res-07": "18:00-20:00",
"res-08": "20:00-22:00",
"res-09": "22:00-00:00",
"res-10": "00:00-02:00",
};
const window = schedule[stationId];
return { window: window === undefined ? undefined : window };
},
});

// Agent role: reconcile a fixed irrigation schedule against only the stations
// it manages to query via the lookup tool before its turn budget runs out.
const resourceScheduleReconciler = agent({
model: "small",
maxTurns: 4,
tools: [lookupReleaseWindow],
instructions: p`The irrigation resource schedule requires release windows for
these ten station ids, in this exact order:
res-01, res-02, res-03, res-04, res-05, res-06, res-07, res-08, res-09, res-10

Every one of these station ids exists in the lookup table, so a resolved
window is always available once you query it. Call lookup_release_window
once per station id, strictly in the listed order, to fetch its window.
You have a strict turn budget and will run out before you can query every
station — this is expected. Do not rush or combine calls: query as many
stations in order as your remaining turns allow, then stop calling the
tool and produce your final answer immediately.

Return resolvedWindows as one entry per station you successfully queried
(stationId and window). Return unresolvedStationIds as the remaining
station ids, in original order, that you ran out of turns to query before
the budget ended — these are unresolved purely due to the turn budget, not
because they are missing from the table.`,
output: s.object({
resolvedWindows: s.array(s.object({
stationId: s.string,
window: s.string,
})),
unresolvedStationIds: s.array(s.string),
}),
});

export default resourceScheduleReconciler;
```
66 changes: 66 additions & 0 deletions skills/rig/samples/553-linked-record-chain-budget-walk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# 553 - Linked Record Chain Budget Walk

```rig
import { s, workflow } from "rig";

const record = s.object({
id: s.string,
kind: s.string,
cost: s.number,
nextId: s.nullable(s.string),
});

const CHAIN_DATA =
"flight-001: kind=flight, cost=320, nextId=hotel-002 | "
+ "hotel-002: kind=hotel, cost=410, nextId=transfer-003 | "
+ "transfer-003: kind=local-transfer, cost=85, nextId=excursion-004 | "
+ "excursion-004: kind=excursion, cost=150, nextId=transfer-005 | "
+ "transfer-005: kind=local-transfer, cost=60, nextId=null";

const BUDGET = 900;

// Workflow role: walk a linked chain of travel records one id at a time, then
// enforce a fixed dollar budget over the resolved chain and report the cut point.
const linkedRecordChainBudgetWalk = workflow({
meta: {
name: "linked-record-chain-budget-walk",
description: "Resolve a linked travel-record chain, then trim it to a fixed budget",
phases: [{ title: "Resolve" }, { title: "Budget" }],
whenToUse: "Walking cross-referencing records while enforcing a cumulative cost cap.",
},
body: async ({ call, phase }) => {
phase("Resolve");
const resolved: { id: string; kind: string; cost: number }[] = [];
let currentId: string | null = "flight-001";

while (currentId) {
const step: { id: string; kind: string; cost: number; nextId: string | null } | null = await call.json(
`Travel record chain data: ${CHAIN_DATA}\nLook up the record whose id is "${currentId}" and return its id, kind, cost, and nextId (or null if it is the last record).`,
record,
{ label: currentId },
);
if (!step) break;
resolved.push({ id: step.id, kind: step.kind, cost: step.cost });
currentId = step.nextId;
}

phase("Budget");
const itinerary: { id: string; kind: string; cost: number }[] = [];
let totalCost = 0;
let cutAtId: string | null = null;

for (const item of resolved) {
if (totalCost + item.cost > BUDGET) {
cutAtId = item.id;
break;
}
itinerary.push(item);
totalCost += item.cost;
}

return { itinerary, totalCost, cutAtId };
},
});

export default linkedRecordChainBudgetWalk;
```
Loading
Loading