diff --git a/skills/rig/samples/521-commit-type-tagger.md b/skills/rig/samples/521-commit-type-tagger.md new file mode 100644 index 0000000..b44b00c --- /dev/null +++ b/skills/rig/samples/521-commit-type-tagger.md @@ -0,0 +1,14 @@ +# 521 - Commit Type Tagger + +```rig +import { agent, p, s } from "rig"; + +// Agent role: classify recent commit messages by conventional-commit type. +const commitTagger = agent({ + model: "small", + instructions: p`Review the recent commit subjects: ${p.bash("git log -20 --pretty=%s")}. Classify each commit subject into a conventional-commit type (feat, fix, docs, chore, refactor, test, or other), then return a record mapping each type to the array of matching commit subjects. Include a key only when at least one commit matches that type.`, + output: s.record(s.array(s.string), "commit subjects keyed by conventional-commit type"), +}); + +export default commitTagger; +``` diff --git a/skills/rig/samples/522-tsconfig-nested-validator.md b/skills/rig/samples/522-tsconfig-nested-validator.md new file mode 100644 index 0000000..e105a44 --- /dev/null +++ b/skills/rig/samples/522-tsconfig-nested-validator.md @@ -0,0 +1,18 @@ +# 522 - Tsconfig Nested Validator + +```rig +import { agent, p, s } from "rig"; + +// Agent role: validate tsconfig.json structure and report issues. +const tsconfigValidator = agent({ + model: "small", + instructions: p`Read ${p.read("tsconfig.json")}. Validate its compilerOptions against the expected shape: strict should be a boolean if present, target should be a string if present, and paths (if present) should be a record mapping string keys to arrays of string path patterns. Report whether the file is valid, list any issues found (missing/malformed fields, wrong types), and give a one-sentence summary.`, + output: s.object({ + valid: s.boolean, + issues: s.array(s.string), + summary: s.string, + }), +}); + +export default tsconfigValidator; +``` diff --git a/skills/rig/samples/523-repair-addon-demo.md b/skills/rig/samples/523-repair-addon-demo.md new file mode 100644 index 0000000..bcba408 --- /dev/null +++ b/skills/rig/samples/523-repair-addon-demo.md @@ -0,0 +1,21 @@ +# 523 - Repair Addon Demo + +```rig +import { agent, repair, s } from "rig"; + +// Agent role: extract structured contact info from free-form text, with repair retries on malformed output. +const contactExtractor = agent({ + model: "small", + maxTurns: 5, + addons: repair(), + instructions: + "Extract the person's contact details from this text: \"Reach out to Dana Lee at dana.lee@example.com, she is 34 years old.\" Return exactly one name, one age, and one email, with no extra commentary.", + output: s.object({ + name: s.string, + age: s.number, + email: s.string, + }), +}); + +export default contactExtractor; +``` diff --git a/skills/rig/samples/524-healthcheck-timeout.md b/skills/rig/samples/524-healthcheck-timeout.md new file mode 100644 index 0000000..5bcfc08 --- /dev/null +++ b/skills/rig/samples/524-healthcheck-timeout.md @@ -0,0 +1,18 @@ +# 524 - Healthcheck Timeout + +```rig +import { agent, p, s, timeout } from "rig"; + +// Agent role: run a quick health-check command and report status within a timeout budget. +const healthProbe = agent({ + model: "small", + addons: timeout({ timeout: 10_000 }), + instructions: p`Run ${p.bash("curl -s -o /dev/null -w '%{http_code}' --max-time 5 https://example.com || echo 000")} to probe service availability, measuring how long the command took in milliseconds. Report status as "ok" if the command returned a 2xx or 3xx HTTP code, "timeout" if the command appeared to hang or time out, and "error" otherwise. Report the elapsed duration in milliseconds as durationMs.`, + output: s.object({ + status: s.enum("ok", "timeout", "error"), + durationMs: s.number, + }), +}); + +export default healthProbe; +``` diff --git a/skills/rig/samples/525-lint-test-gate-workflow.md b/skills/rig/samples/525-lint-test-gate-workflow.md new file mode 100644 index 0000000..e360e41 --- /dev/null +++ b/skills/rig/samples/525-lint-test-gate-workflow.md @@ -0,0 +1,66 @@ +# 525 - Lint Test Gate Workflow + +```rig +import { agent, p, s, workflow } from "rig"; + +// Agent role: run a lint-like check and report pass/fail with issues. +const lintCheck = agent({ + model: "small", + instructions: p`Run ${p.bash("npx tsc --noEmit 2>&1 | head -50 || true")} and report whether it passed (no errors), listing any issues found as short strings.`, + output: s.object({ + passed: s.boolean, + issues: s.array(s.string), + }), +}); + +// Agent role: run a test-like check and report pass/fail with failures. +const testCheck = agent({ + model: "small", + instructions: p`Run ${p.bash("npx vitest run --reporter=dot 2>&1 | tail -50 || true")} and report whether the tests passed, listing any failures found as short strings.`, + output: s.object({ + passed: s.boolean, + failures: s.array(s.string), + }), +}); + +// Agent role: combine lint and test results into a final approve/block decision. +const gate = agent({ + model: "small", + input: s.object({ + lint: s.object({ passed: s.boolean, issues: s.array(s.string) }), + test: s.object({ passed: s.boolean, failures: s.array(s.string) }), + }), + instructions: + "Given input.lint and input.test results, decide \"approved\" only if both passed; otherwise \"blocked\". Give a one-sentence reason referencing the specific failures.", + output: s.object({ + decision: s.enum("approved", "blocked"), + reason: s.string, + }), +}); + +// Workflow role: run lint and test checks, then gate on their combined result. +const lintTestGate = workflow({ + meta: { + name: "lint-test-gate", + description: "Run lint and test checks, then gate on the combined result", + phases: ["Lint", "Test", "Gate"], + }, + body: async ({ call, phase }) => { + phase("Lint"); + const lint = await call(lintCheck, "run lint check", { label: "lint" }); + phase("Test"); + const test = await call(testCheck, "run test check", { label: "test" }); + phase("Gate"); + return call( + gate, + { + lint: lint ?? { passed: false, issues: ["lint check failed to run"] }, + test: test ?? { passed: false, failures: ["test check failed to run"] }, + }, + { label: "gate" }, + ); + }, +}); + +export default lintTestGate; +``` diff --git a/skills/rig/samples/526-glob-tagger-aggregator.md b/skills/rig/samples/526-glob-tagger-aggregator.md new file mode 100644 index 0000000..5ca93ce --- /dev/null +++ b/skills/rig/samples/526-glob-tagger-aggregator.md @@ -0,0 +1,26 @@ +# 526 - Glob Tagger Aggregator + +```rig +import { agent, p, s } from "rig"; + +// Agent role: assign a topical tag to one source file. +const tagger = agent({ + model: "nano", + input: s.object({ path: s.path }), + instructions: p`Read ${p.readInput("path")} and assign exactly one topical tag describing its role.`, + output: s.object({ + path: s.path, + tag: s.enum("core", "test", "utility", "type", "config"), + }), +}); + +// Agent role: find all TypeScript source files, tag each one, and aggregate paths by tag. +const tagAggregator = agent({ + model: "small", + agents: { tagger }, + instructions: p`Find TypeScript source files: ${p.glob("src/**/*.ts")}. For each path, delegate to tagger to assign a topical tag, then return a record mapping each tag to the array of file paths that received that tag.`, + output: s.record(s.array(s.path), "file paths keyed by topical tag"), +}); + +export default tagAggregator; +``` diff --git a/skills/rig/samples/527-review-readiness-report.md b/skills/rig/samples/527-review-readiness-report.md new file mode 100644 index 0000000..8ffe91c --- /dev/null +++ b/skills/rig/samples/527-review-readiness-report.md @@ -0,0 +1,19 @@ +# 527 - Review Readiness Report + +```rig +import { agent, p, s } from "rig"; + +// Agent role: assess review readiness from recent diff stats and contributing guide presence. +const reviewReadiness = agent({ + model: "small", + instructions: p`Run ${p.bash("git diff --stat HEAD~5..HEAD 2>/dev/null || git diff --stat HEAD~1..HEAD 2>/dev/null || echo 'no history available'")} to see recent change volume, and read ${p.readOptional("CONTRIBUTING.md", "no contributing guide found")} to check for a contributing guide. Report the number of files changed, the total number of lines changed (insertions plus deletions), whether a contributing guide exists, and a short list of actionable recommendations to improve review readiness (e.g. add tests, split large diffs, add a contributing guide).`, + output: s.object({ + filesChanged: s.int, + linesChanged: s.int, + hasContributingGuide: s.boolean, + recommendations: s.array(s.string), + }), +}); + +export default reviewReadiness; +``` diff --git a/skills/rig/samples/528-basename-similarity-finder.md b/skills/rig/samples/528-basename-similarity-finder.md new file mode 100644 index 0000000..006c864 --- /dev/null +++ b/skills/rig/samples/528-basename-similarity-finder.md @@ -0,0 +1,40 @@ +# 528 - Basename Similarity Finder + +```rig +import { agent, defineTool, p, s } from "rig"; + +// Tool: compute a simple similarity score between two basenames using shared-character overlap. +const basenameSimilarity = defineTool("basename_similarity", { + description: "Compute a similarity score between two file basenames.", + parameters: s.object({ + a: s.string, + b: s.string, + }), + handler: async ({ a, b }: { a: string; b: string }) => { + const setA = new Set(a.toLowerCase()); + const setB = new Set(b.toLowerCase()); + let shared = 0; + for (const ch of setA) { + if (setB.has(ch)) shared += 1; + } + const union = new Set([...setA, ...setB]).size || 1; + return { score: shared / union }; + }, +}); + +// Agent role: find near-duplicate basenames among discovered source files using the similarity tool. +const duplicateFinder = agent({ + model: "small", + tools: [basenameSimilarity], + instructions: p`Find TypeScript source files: ${p.glob("src/**/*.ts")}. Extract each file's basename, then use basename_similarity to compare every distinct pair of basenames. Return the pairs with their full original paths and similarity score, sorted by score descending.`, + output: s.array( + s.object({ + pathA: s.path, + pathB: s.path, + score: s.number, + }), + ), +}); + +export default duplicateFinder; +``` diff --git a/skills/rig/samples/529-parallel-wordfreq-aggregator.md b/skills/rig/samples/529-parallel-wordfreq-aggregator.md new file mode 100644 index 0000000..af3fcfd --- /dev/null +++ b/skills/rig/samples/529-parallel-wordfreq-aggregator.md @@ -0,0 +1,59 @@ +# 529 - Parallel Wordfreq Aggregator + +```rig +import { agent, p, s, workflow } from "rig"; + +// Agent role: summarize word frequency across rig sample markdown files. +const sampleWordFreq = agent({ + model: "small", + instructions: p`Find sample files: ${p.glob("skills/rig/samples/*.md")}. Read a representative subset of them and compute the frequency of the most common meaningful words across their content. Return a record mapping each word to its occurrence count.`, + output: s.record(s.int, "word occurrence counts across sample markdown files"), +}); + +// Agent role: summarize word frequency across TypeScript source files. +const sourceWordFreq = agent({ + model: "small", + instructions: p`Find TypeScript source files: ${p.glob("src/**/*.ts")}. Read a representative subset of them and compute the frequency of the most common meaningful words (identifiers, comments) across their content. Return a record mapping each word to its occurrence count.`, + output: s.record(s.int, "word occurrence counts across TypeScript source files"), +}); + +// Agent role: merge two word-frequency records, summing counts for shared keys. +const wordFreqMerger = agent({ + model: "small", + input: s.object({ + samples: s.record(s.int), + source: s.record(s.int), + }), + instructions: + "Merge input.samples and input.source into one combined record of word to occurrence count, summing counts for words that appear in both.", + output: s.record(s.int, "combined word occurrence counts"), +}); + +// Workflow role: compute word frequencies for samples and source in parallel, then merge them. +// Uses Promise.all because both agent calls share the same output type (a record of string to number). +const parallelWordFreq = workflow({ + meta: { + name: "parallel-wordfreq-aggregator", + description: "Summarize word frequency in samples and source in parallel, then aggregate", + phases: ["Summarize", "Aggregate"], + }, + body: async ({ call, phase }) => { + phase("Summarize"); + const [samples, source] = await Promise.all([ + call(sampleWordFreq, "summarize sample word frequency"), + call(sourceWordFreq, "summarize source word frequency"), + ]); + phase("Aggregate"); + return call( + wordFreqMerger, + { + samples: samples ?? {}, + source: source ?? {}, + }, + { label: "merge" }, + ); + }, +}); + +export default parallelWordFreq; +``` diff --git a/skills/rig/samples/530-bug-template-stub-writer.md b/skills/rig/samples/530-bug-template-stub-writer.md new file mode 100644 index 0000000..fd1f797 --- /dev/null +++ b/skills/rig/samples/530-bug-template-stub-writer.md @@ -0,0 +1,18 @@ +# 530 - Bug Template Stub Writer + +```rig +import { agent, p, s } from "rig"; + +// Agent role: conditionally write a bug-report issue template stub if one does not already exist. +const bugTemplateWriter = agent({ + model: "small", + instructions: p`Read ${p.read("package.json")} to find the project name and description. Check whether a bug report template already exists at .github/ISSUE_TEMPLATE/bug_report.md: ${p.bash("test -f .github/ISSUE_TEMPLATE/bug_report.md && echo exists || echo missing")}. If it is missing, write a minimal bug-report template to /tmp/gh-aw/agent/bug_report.md: ${p.write("/tmp/gh-aw/agent/bug_report.md", "---\nname: Bug report\nabout: Report a problem\nlabels: bug\n---\n\n## Description\n\n## Steps to reproduce\n")}. If a template already exists, do not write anything. Report whether a template was written, the path considered, and which template fields were included (name, description, labels).`, + output: s.object({ + templateWritten: s.boolean, + path: s.path, + fieldsIncluded: s.array(s.enum("name", "description", "labels")), + }), +}); + +export default bugTemplateWriter; +```