diff --git a/skills/rig/samples/68-changelog-generator.md b/skills/rig/samples/68-changelog-generator.md new file mode 100644 index 0000000..9e984ae --- /dev/null +++ b/skills/rig/samples/68-changelog-generator.md @@ -0,0 +1,36 @@ +# 68 - Changelog Generator + +```rig +import { agent, p, s, defineTool } from "rig"; +import { repair } from "rig/addons"; + +const validateSemver = defineTool("validateSemver", { + description: "Validate that a bump type is major, minor, or patch", + parameters: s.object({ bump: s.string }), + handler({ bump }) { + const valid = ["major", "minor", "patch"].includes(bump); + return { valid }; + }, +}); + +// Agent role: read recent git changes and produce a structured changelog entry with semver bump classification. +const changelogGenerator = agent({ + model: "small", + instructions: p`Review ${p.bash("git diff HEAD~1 HEAD --stat")} and ${p.bash("git log HEAD~1..HEAD --oneline")} to produce a changelog entry. Classify each change by category and determine the semver bump type. Use the validateSemver tool to confirm the bump value. Write the markdown changelog to CHANGELOG.md via ${p.write("CHANGELOG.md", "")}`, + output: s.object({ + version: s.string, + bump: s.enum("major", "minor", "patch"), + entries: s.array(s.object({ + category: s.enum("feat", "fix", "chore", "docs", "refactor"), + description: s.string, + })), + markdown: s.string, + }), + tools: [validateSemver], + maxTurns: 6, + addons: repair(), +}); + +export default changelogGenerator; + +``` diff --git a/skills/rig/samples/69-todo-comment-tracker.md b/skills/rig/samples/69-todo-comment-tracker.md new file mode 100644 index 0000000..b5278dc --- /dev/null +++ b/skills/rig/samples/69-todo-comment-tracker.md @@ -0,0 +1,24 @@ +# 69 - Todo Comment Tracker + +```rig +import { agent, p, s } from "rig"; + +// Agent role: scan source files for TODO/FIXME/HACK comments and produce a structured report. +const todoCommentTracker = agent({ + model: "small", + instructions: p`Scan source files using ${p.bash("grep -rn 'TODO\\|FIXME\\|HACK' --include='*.ts' . 2>/dev/null || true")} and produce a structured list of all found comments. Write a markdown report to todo-report.md via ${p.write("todo-report.md", "")}`, + output: s.object({ + items: s.array(s.object({ + file: s.path, + line: s.int, + kind: s.enum("TODO", "FIXME", "HACK"), + message: s.string, + })), + totalCount: s.int, + markdown: s.string, + }), +}); + +export default todoCommentTracker; + +``` diff --git a/skills/rig/samples/70-multi-file-subagent-summarizer.md b/skills/rig/samples/70-multi-file-subagent-summarizer.md new file mode 100644 index 0000000..438995b --- /dev/null +++ b/skills/rig/samples/70-multi-file-subagent-summarizer.md @@ -0,0 +1,25 @@ +# 70 - Multi File Subagent Summarizer + +```rig +import { agent, p, s } from "rig"; + +// Agent role: summarize a single TypeScript file. +const fileSummarizer = agent({ + name: "fileSummarizer", + model: "nano", + input: s.object({ filePath: s.path }), + instructions: p`Summarize the TypeScript file at ${p.readInput("filePath")} in one concise sentence.`, + output: s.object({ summary: s.string }), +}); + +// Agent role: find TypeScript source files and delegate to fileSummarizer to summarize each one, then aggregate results. +const multiFileSummarizer = agent({ + model: "small", + instructions: p`Find TypeScript files using ${p.bash("find src -name '*.ts' -not -path '*/node_modules/*' 2>/dev/null | head -10 || echo 'no files'")} then delegate each file path to the fileSummarizer subagent and collect summaries keyed by file path.`, + output: s.record(s.string), + agents: { fileSummarizer }, +}); + +export default multiFileSummarizer; + +``` diff --git a/skills/rig/samples/71-package-script-health.md b/skills/rig/samples/71-package-script-health.md new file mode 100644 index 0000000..800fc59 --- /dev/null +++ b/skills/rig/samples/71-package-script-health.md @@ -0,0 +1,36 @@ +# 71 - Package Script Health + +```rig +import { agent, p, s, defineTool } from "rig"; +import { repair } from "rig/addons"; + +const validateScriptName = defineTool("validateScriptName", { + description: "Validate that a package.json script name follows conventional naming (lowercase, hyphens only)", + parameters: s.object({ name: s.string }), + handler({ name }) { + const valid = /^[a-z][a-z0-9:-]*$/.test(name); + const reason = valid ? "Name is conventional" : "Name should be lowercase with hyphens/colons only"; + return { valid, reason }; + }, +}); + +// Agent role: analyze package.json scripts for naming and structural health issues. +const packageScriptHealth = agent({ + model: "small", + instructions: p`Read ${p.read("package.json")} and analyze all scripts entries. Use the validateScriptName tool for each script name. Classify each issue by severity and determine overall health.`, + output: s.object({ + issues: s.array(s.object({ + script: s.string, + issue: s.string, + status: s.enum("error", "warning", "ok"), + })), + overallHealth: s.enum("healthy", "degraded", "critical"), + }), + tools: [validateScriptName], + maxTurns: 6, + addons: repair(), +}); + +export default packageScriptHealth; + +``` diff --git a/skills/rig/samples/72-ts-function-signatures.md b/skills/rig/samples/72-ts-function-signatures.md new file mode 100644 index 0000000..f6da406 --- /dev/null +++ b/skills/rig/samples/72-ts-function-signatures.md @@ -0,0 +1,37 @@ +# 72 - Ts Function Signatures + +```rig +import { agent, p, s, defineTool } from "rig"; + +const parseSignatures = defineTool("parseSignatures", { + description: "Extract function signatures from TypeScript source content using regex", + parameters: s.object({ content: s.string }), + handler({ content }) { + const pattern = /(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(([^)]*)\)/g; + const functions: { name: string; paramCount: number; isExported: boolean }[] = []; + let match; + while ((match = pattern.exec(content)) !== null) { + const [full, name, params] = match; + const paramCount = params.trim() === "" ? 0 : params.split(",").length; + const isExported = full.trimStart().startsWith("export"); + functions.push({ name, paramCount, isExported }); + } + return { functions }; + }, +}); + +// Agent role: extract TypeScript function signatures from source files and return them keyed by file path. +const tsFunctionSignatures = agent({ + model: "small", + instructions: p`Find TypeScript files using ${p.bash("find src -name '*.ts' 2>/dev/null | head -5 || echo 'no files'")} then read each file and use the parseSignatures tool to extract function signatures. Return results keyed by file path.`, + output: s.record(s.array(s.object({ + name: s.string, + paramCount: s.int, + isExported: s.boolean, + }))), + tools: [parseSignatures], +}); + +export default tsFunctionSignatures; + +``` diff --git a/skills/rig/samples/73-git-branch-pruner.md b/skills/rig/samples/73-git-branch-pruner.md new file mode 100644 index 0000000..e1015fe --- /dev/null +++ b/skills/rig/samples/73-git-branch-pruner.md @@ -0,0 +1,27 @@ +# 73 - Git Branch Pruner + +```rig +import { agent, p, s } from "rig"; +import { repair } from "rig/addons"; + +// Agent role: identify git branches that are candidates for pruning based on merge status and last commit date. +const gitBranchPruner = agent({ + model: "small", + instructions: p`Analyze git branches using ${p.bash("git branch --merged HEAD 2>/dev/null || echo 'no branches'")} and ${p.bash("git for-each-ref --format='%(refname:short) %(committerdate:short)' refs/heads/ 2>/dev/null || echo 'no refs'")}. For each branch determine if it should be pruned (merged and not main/master/develop) or kept. Exclude the current branch from prune candidates.`, + output: s.object({ + candidates: s.array(s.object({ + branch: s.string, + lastCommitDate: s.optional(s.string), + action: s.enum("keep", "prune"), + reason: s.string, + })), + totalBranches: s.int, + pruneCount: s.int, + }), + maxTurns: 6, + addons: repair(), +}); + +export default gitBranchPruner; + +``` diff --git a/skills/rig/samples/74-prettier-eslint-compat.md b/skills/rig/samples/74-prettier-eslint-compat.md new file mode 100644 index 0000000..2ca647e --- /dev/null +++ b/skills/rig/samples/74-prettier-eslint-compat.md @@ -0,0 +1,50 @@ +# 74 - Prettier Eslint Compat + +```rig +import { agent, p, s, defineTool } from "rig"; +import { repair } from "rig/addons"; + +const detectConflicts = defineTool("detectConflicts", { + description: "Detect rule conflicts between Prettier and ESLint configs", + parameters: s.object({ prettierConfig: s.string, eslintConfig: s.string }), + handler({ prettierConfig, eslintConfig }) { + const conflicts: { rule: string; prettier: string; eslint: string; fixable: boolean }[] = []; + try { + const p = JSON.parse(prettierConfig || "{}"); + const e = JSON.parse(eslintConfig || "{}"); + const rules = (e.rules || {}); + if (p.printWidth && rules["max-len"]) { + conflicts.push({ rule: "line-length", prettier: `printWidth: ${p.printWidth}`, eslint: `max-len: ${JSON.stringify(rules["max-len"])}`, fixable: true }); + } + if (p.singleQuote !== undefined && rules["quotes"]) { + conflicts.push({ rule: "quotes", prettier: `singleQuote: ${p.singleQuote}`, eslint: `quotes: ${JSON.stringify(rules["quotes"])}`, fixable: true }); + } + } catch { + // invalid JSON — model will handle + } + return { conflicts }; + }, +}); + +// Agent role: check Prettier and ESLint configs for rule conflicts and report compatibility issues. +const prettierEslintCompat = agent({ + model: "small", + instructions: p`Read the Prettier config ${p.readOptional(".prettierrc")} and ESLint config ${p.readOptional(".eslintrc.json")} then use the detectConflicts tool to find rule conflicts. Classify each conflict by severity.`, + output: s.object({ + conflicts: s.array(s.object({ + rule: s.string, + prettier: s.string, + eslint: s.string, + fixable: s.boolean, + severity: s.enum("error", "warning", "info"), + })), + compatible: s.boolean, + }), + tools: [detectConflicts], + maxTurns: 6, + addons: repair(), +}); + +export default prettierEslintCompat; + +``` diff --git a/skills/rig/samples/75-workflow-validator.md b/skills/rig/samples/75-workflow-validator.md new file mode 100644 index 0000000..4b7df2b --- /dev/null +++ b/skills/rig/samples/75-workflow-validator.md @@ -0,0 +1,40 @@ +# 75 - Workflow Validator + +```rig +import { agent, p, s } from "rig"; + +// Agent role: analyze a single GitHub Actions workflow file for structural issues and job count. +const workflowAnalyzer = agent({ + name: "workflowAnalyzer", + model: "nano", + input: s.object({ filePath: s.path }), + instructions: p`Analyze the GitHub Actions workflow file at ${p.readInput("filePath")} for issues such as missing permissions, deprecated actions, hardcoded secrets, or missing timeout-minutes. Count the number of jobs defined.`, + output: s.object({ + issues: s.array(s.object({ + step: s.string, + problem: s.string, + severity: s.enum("error", "warning", "info"), + })), + jobCount: s.int, + }), +}); + +// Agent role: find all GitHub Actions workflow files and delegate analysis to workflowAnalyzer subagent, then aggregate results. +const workflowValidator = agent({ + model: "small", + instructions: p`Find GitHub Actions workflow files using ${p.bash("find .github/workflows -name '*.yml' -o -name '*.yaml' 2>/dev/null | head -10 || echo 'no workflows'")} then delegate each file to the workflowAnalyzer subagent. Aggregate results keyed by filename, adding a pass/warn/fail status based on issue severity.`, + output: s.record(s.object({ + issues: s.array(s.object({ + step: s.string, + problem: s.string, + severity: s.enum("error", "warning", "info"), + })), + jobCount: s.int, + status: s.enum("pass", "warn", "fail"), + })), + agents: { workflowAnalyzer }, +}); + +export default workflowValidator; + +``` diff --git a/skills/rig/samples/76-commit-msg-rewriter.md b/skills/rig/samples/76-commit-msg-rewriter.md new file mode 100644 index 0000000..39c9b28 --- /dev/null +++ b/skills/rig/samples/76-commit-msg-rewriter.md @@ -0,0 +1,26 @@ +# 76 - Commit Msg Rewriter + +```rig +import { agent, p, s } from "rig"; +import { steering, repair } from "rig/addons"; + +// Agent role: rewrite recent git commit messages into conventional commit format with imperative mood. +const commitMsgRewriter = agent({ + model: "small", + instructions: p`Review recent commits from ${p.bash("git log --oneline -20 2>/dev/null || echo 'no commits'")} and rewrite each message in conventional commit format (feat:/fix:/chore:/docs:/test:/refactor:/style: prefix) with imperative mood. Write a markdown summary of all rewrites via ${p.writeOutput("markdown", "commit-rewrites.md")}`, + output: s.object({ + rewrites: s.array(s.object({ + hash: s.string, + original: s.string, + revised: s.string, + category: s.enum("feat", "fix", "chore", "docs", "test", "refactor", "style"), + })), + markdown: s.string, + }), + maxTurns: 6, + addons: [steering({ message: "Use imperative mood and conventional commit prefixes. Be consistent." }), repair()], +}); + +export default commitMsgRewriter; + +``` diff --git a/skills/rig/samples/77-env-key-checker.md b/skills/rig/samples/77-env-key-checker.md new file mode 100644 index 0000000..ffaff3d --- /dev/null +++ b/skills/rig/samples/77-env-key-checker.md @@ -0,0 +1,32 @@ +# 77 - Env Key Checker + +```rig +import { agent, p, s, defineTool } from "rig"; + +const parseEnvKeys = defineTool("parseEnvKeys", { + description: "Extract KEY names from dotenv-style file content", + parameters: s.object({ content: s.string }), + handler({ content }) { + const keys = (content.match(/^([A-Z_][A-Z0-9_]*)=/gm) || []) + .map(line => line.replace("=", "")); + return { keys }; + }, +}); + +// Agent role: compare .env.example required keys against .env actual keys to find missing and extra entries. +const envKeyChecker = agent({ + model: "small", + instructions: p`Read the required env keys from ${p.readOptional(".env.example")} and the present keys from ${p.readOptional(".env")}. Use the parseEnvKeys tool on each file content to extract key names. Compare to find missing keys (in example but not env) and extra keys (in env but not example).`, + output: s.object({ + missing: s.array(s.string), + extra: s.array(s.string), + requiredCount: s.int, + presentCount: s.int, + status: s.enum("complete", "partial", "empty"), + }), + tools: [parseEnvKeys], +}); + +export default envKeyChecker; + +```