diff --git a/skills/rig/samples/411-package-json-completeness-scorer.md b/skills/rig/samples/411-package-json-completeness-scorer.md new file mode 100644 index 0000000..18d1a73 --- /dev/null +++ b/skills/rig/samples/411-package-json-completeness-scorer.md @@ -0,0 +1,53 @@ +# 411 - Package Json Completeness Scorer + +```rig +import { agent, defineTool, p, repair, s } from "rig"; + +const scoreField = defineTool("scoreField", { + description: "Score a package.json field for completeness and return its category.", + parameters: s.object({ fieldName: s.string, value: s.unknown }), + handler: ({ fieldName, value }: { fieldName: string; value: unknown }) => { + const required = ["name", "version"]; + const recommended = ["description", "main", "scripts", "license", "repository", "keywords"]; + const category = required.includes(fieldName) + ? ("required" as const) + : recommended.includes(fieldName) + ? ("recommended" as const) + : ("optional" as const); + const present = value !== undefined && value !== null && value !== ""; + const score = present ? 1 : 0; + return { present, category, score }; + }, +}); + +// Agent role: score the completeness of package.json against required and recommended fields. +const packageJsonCompletenessScorer = agent({ + model: "small", + instructions: p`Score the completeness of package.json. + +Content: +${p.read("package.json")} + +Required fields: name, version. +Recommended fields: description, main, scripts, license, repository, keywords. +Optional fields: author, bugs, homepage, engines, files, types. + +For each field from all three categories, call scoreField with fieldName and value (or undefined if absent). +Build fields record. totalScore = sum of scores. maxScore = total fields. completenessPercent = (totalScore / maxScore) * 100. missingRequired = required fields where present is false.`, + output: s.object({ + fields: s.record(s.object({ + present: s.boolean, + category: s.enum("required", "recommended", "optional"), + score: s.number, + })), + totalScore: s.number, + maxScore: s.number, + completenessPercent: s.number, + missingRequired: s.array(s.string), + }), + tools: [scoreField], + addons: [repair()], +}); + +export default packageJsonCompletenessScorer; +``` diff --git a/skills/rig/samples/412-parallel-branch-analysis-workflow.md b/skills/rig/samples/412-parallel-branch-analysis-workflow.md new file mode 100644 index 0000000..866398c --- /dev/null +++ b/skills/rig/samples/412-parallel-branch-analysis-workflow.md @@ -0,0 +1,54 @@ +# 412 - Parallel Branch Analysis Workflow + +```rig +import { agent, p, s, workflow } from "rig"; + +const branchMetric = s.object({ + totalBranches: s.number, + staleCount: s.number, + activeBranches: s.number, +}); + +const commitMetric = s.object({ + totalCommits: s.number, + activeDays: s.number, + averagePerDay: s.number, +}); + +// Agent role: measure branch count and identify stale branches. +const branchHealthAgent = agent({ + model: "small", + output: branchMetric, + instructions: p`Count total, stale (>30 days old), and active branches. +${p.bash("git branch -a 2>/dev/null || echo ''")} +${p.bash("git for-each-ref --format='%(refname:short) %(committerdate:relative)' refs/heads/ 2>/dev/null || echo ''")}`, +}); + +// Agent role: measure commit frequency over the last 30 days. +const commitFrequencyAgent = agent({ + model: "small", + output: commitMetric, + instructions: p`Count total commits in last 30 days and estimate active days and average per day. +${p.bash("git log --oneline --since='30 days ago' 2>/dev/null || echo ''")}`, +}); + +// Workflow role: run branch health and commit frequency agents in parallel, then classify overall health. +const parallelBranchAnalysis = workflow({ + meta: { name: "parallelBranchAnalysis", description: "Parallel branch analysis", phases: ["Analyze", "Rate"] }, + body: async ({ call, phase }) => { + phase("Analyze"); + const [branchHealth, commitFrequency] = await Promise.all([ + call(branchHealthAgent, "analyze"), + call(commitFrequencyAgent, "analyze"), + ]); + phase("Rate"); + const overallHealth = await call.json( + `Branch health: ${JSON.stringify(branchHealth)}. Commit frequency: ${JSON.stringify(commitFrequency)}. Classify overall health as "healthy", "needs-attention", or "critical".`, + s.enum("healthy", "needs-attention", "critical"), + ); + return { branchHealth, commitFrequency, overallHealth }; + }, +}); + +export default parallelBranchAnalysis; +``` diff --git a/skills/rig/samples/413-lockfile-integrity-checker.md b/skills/rig/samples/413-lockfile-integrity-checker.md new file mode 100644 index 0000000..441d93d --- /dev/null +++ b/skills/rig/samples/413-lockfile-integrity-checker.md @@ -0,0 +1,46 @@ +# 413 - Lockfile Integrity Checker + +```rig +import { agent, defineTool, p, repair, s } from "rig"; + +const verifyLockEntry = defineTool("verifyLockEntry", { + description: "Verify a package entry in package-lock.json has required integrity and resolved fields.", + parameters: s.object({ packageName: s.string, entryJson: s.string }), + handler({ entryJson }: { packageName: string; entryJson: string }) { + try { + const entry = JSON.parse(entryJson) as Record; + const hasIntegrity = typeof entry["integrity"] === "string" && (entry["integrity"] as string).length > 0; + const hasResolved = typeof entry["resolved"] === "string" && (entry["resolved"] as string).length > 0; + const valid = hasIntegrity && hasResolved; + return { valid, hasIntegrity, hasResolved }; + } catch { + return { valid: false, hasIntegrity: false, hasResolved: false }; + } + }, +}); + +// Agent role: check package-lock.json entries for integrity and resolved fields. +const lockfileIntegrityChecker = agent({ + model: "small", + instructions: p`Check integrity of package-lock.json entries. + +Content: +${p.read("package-lock.json")} + +For each package in the "packages" or "dependencies" section, call verifyLockEntry with the package name and its JSON entry (as a string). Build the packages record. mismatchCount = number of invalid entries. isClean = mismatchCount === 0. totalChecked = total packages checked.`, + output: s.object({ + packages: s.record(s.object({ + valid: s.boolean, + hasIntegrity: s.boolean, + hasResolved: s.boolean, + })), + mismatchCount: s.number, + isClean: s.boolean, + totalChecked: s.number, + }), + tools: [verifyLockEntry], + addons: [repair()], +}); + +export default lockfileIntegrityChecker; +``` diff --git a/skills/rig/samples/414-graphql-schema-type-extractor.md b/skills/rig/samples/414-graphql-schema-type-extractor.md new file mode 100644 index 0000000..278c840 --- /dev/null +++ b/skills/rig/samples/414-graphql-schema-type-extractor.md @@ -0,0 +1,47 @@ +# 414 - Graphql Schema Type Extractor + +```rig +import { agent, defineTool, p, s } from "rig"; +import { steering } from "rig"; + +const extractGraphqlTypes = defineTool("extractGraphqlTypes", { + description: "Extract type declarations from a GraphQL schema file.", + parameters: s.object({ filePath: s.path }), + async handler({ filePath }: { filePath: string }) { + const { readFile } = await import("node:fs/promises"); + const content = await readFile(filePath, "utf8"); + const pattern = /^(type|input|enum|interface|union)\s+(\w+)[^{]*\{([^}]*)\}/gm; + const results: Record = {}; + let match: RegExpExecArray | null; + while ((match = pattern.exec(content)) !== null) { + const kind = match[1]; + const name = match[2]; + const body = match[3]; + const fields = body.split("\n").map((l: string) => l.trim()).filter((l: string) => l.length > 0 && !l.startsWith("#")); + results[name] = { kind, fieldCount: fields.length, fields, sourceFile: filePath }; + } + return results; + }, +}); + +// Agent role: discover and extract all GraphQL type declarations across .graphql files. +const graphqlSchemaTypeExtractor = agent({ + model: "small", + instructions: p`Extract GraphQL type declarations from .graphql files. + +Files found: +${p.bash("find . -name '*.graphql' -not -path '*/node_modules/*' 2>/dev/null || echo '(none)'")} + +For each .graphql file found, call extractGraphqlTypes with the file path. Merge all results into a single record keyed by type name.`, + output: s.record(s.object({ + kind: s.enum("type", "input", "enum", "interface", "union"), + fieldCount: s.number, + fields: s.array(s.string), + sourceFile: s.string, + })), + tools: [extractGraphqlTypes], + addons: [steering()], +}); + +export default graphqlSchemaTypeExtractor; +``` diff --git a/skills/rig/samples/415-os-path-structure-analyzer.md b/skills/rig/samples/415-os-path-structure-analyzer.md new file mode 100644 index 0000000..326f4c4 --- /dev/null +++ b/skills/rig/samples/415-os-path-structure-analyzer.md @@ -0,0 +1,51 @@ +# 415 - Os Path Structure Analyzer + +```rig +import { agent, defineTool, p, repair, s } from "rig"; +import { basename } from "node:path"; + +const classifyDirectory = defineTool("classifyDirectory", { + description: "Classify a directory path into a category based on its basename.", + parameters: s.object({ dirPath: s.path, rootDir: s.string }), + handler({ dirPath, rootDir }: { dirPath: string; rootDir: string }) { + const name = basename(dirPath); + const depth = dirPath.replace(rootDir, "").split("/").filter(Boolean).length; + const srcNames = new Set(["src", "lib", "source"]); + const testNames = new Set(["test", "tests", "__tests__", "spec", "specs"]); + const configNames = new Set(["config", "configs", "conf", ".config"]); + const buildNames = new Set(["dist", "build", "out", "output", ".next", ".cache"]); + const vendorNames = new Set(["node_modules", "vendor", "third_party"]); + const category = srcNames.has(name) ? "src" + : testNames.has(name) ? "test" + : configNames.has(name) ? "config" + : buildNames.has(name) ? "build" + : vendorNames.has(name) ? "vendor" + : "other"; + return { depth, category }; + }, +}); + +// Agent role: analyze the directory structure of a given root path and classify each directory. +const osPathStructureAnalyzer = agent({ + model: "small", + input: s.object({ rootDir: s.string }), + instructions: p`Analyze the directory structure of the given rootDir. + +Directories found: +${p.bash("find . -maxdepth 3 -type d -not -path '*/node_modules/*' 2>/dev/null || echo '(none)'")} + +For each directory, call classifyDirectory with its path and the rootDir. Build the directories record. Compute maxDepth and categoryCounts.`, + output: s.object({ + directories: s.record(s.object({ + depth: s.number, + category: s.string, + })), + maxDepth: s.number, + categoryCounts: s.record(s.number), + }), + tools: [classifyDirectory], + addons: [repair()], +}); + +export default osPathStructureAnalyzer; +``` diff --git a/skills/rig/samples/416-ci-log-error-classifier.md b/skills/rig/samples/416-ci-log-error-classifier.md new file mode 100644 index 0000000..5a70ff6 --- /dev/null +++ b/skills/rig/samples/416-ci-log-error-classifier.md @@ -0,0 +1,56 @@ +# 416 - Ci Log Error Classifier + +```rig +import { agent, defineTool, p, repair, s } from "rig"; + +const classifyLogLine = defineTool("classifyLogLine", { + description: "Classify a CI log line into an error class and severity.", + parameters: s.object({ line: s.string }), + handler({ line }: { line: string }) { + const lower = line.toLowerCase(); + const errorClass = + lower.includes("error") && (lower.includes("compil") || lower.includes("syntax") || lower.includes("tsc")) + ? ("compile" as const) + : lower.includes("test") && (lower.includes("fail") || lower.includes("error")) + ? ("test" as const) + : lower.includes("eslint") || lower.includes("lint") + ? ("lint" as const) + : lower.includes("econnrefused") || lower.includes("enotfound") || lower.includes("network") + ? ("network" as const) + : lower.includes("permission") || lower.includes("eacces") + ? ("permission" as const) + : ("unknown" as const); + const severity = + lower.includes("error") ? ("error" as const) + : lower.includes("warn") ? ("warning" as const) + : ("info" as const); + return { errorClass, severity }; + }, +}); + +// Agent role: classify each line of a CI log file by error class and severity. +const ciLogErrorClassifier = agent({ + model: "small", + input: s.object({ logFile: s.string }), + instructions: p`Classify lines in the CI log file at the path provided in logFile. + +Log content: +${p.readInput("logFile")} + +For each non-empty line, call classifyLogLine. Build the lines array. Compute errorCount (lines with severity "error"), warningCount (lines with severity "warning"). dominantError = the most frequent errorClass among error-severity lines (omit if none).`, + output: s.object({ + lines: s.array(s.object({ + line: s.string, + errorClass: s.enum("compile", "test", "lint", "network", "permission", "unknown"), + severity: s.enum("error", "warning", "info"), + })), + errorCount: s.number, + warningCount: s.number, + dominantError: s.optional(s.string), + }), + tools: [classifyLogLine], + addons: [repair()], +}); + +export default ciLogErrorClassifier; +``` diff --git a/skills/rig/samples/417-ini-config-parser.md b/skills/rig/samples/417-ini-config-parser.md new file mode 100644 index 0000000..4b23568 --- /dev/null +++ b/skills/rig/samples/417-ini-config-parser.md @@ -0,0 +1,52 @@ +# 417 - Ini Config Parser + +```rig +import { agent, defineTool, p, repair, s } from "rig"; + +const parseIniSection = defineTool("parseIniSection", { + description: "Parse an INI file content and return sections with key-value pairs.", + parameters: s.object({ content: s.string }), + handler({ content }: { content: string }) { + const sections: Record> = {}; + let currentSection = "__default__"; + sections[currentSection] = {}; + for (const rawLine of content.split("\n")) { + const line = rawLine.trim(); + if (!line || line.startsWith(";") || line.startsWith("#")) continue; + const sectionMatch = /^\[(.+)\]$/.exec(line); + if (sectionMatch) { + currentSection = sectionMatch[1]; + sections[currentSection] = {}; + continue; + } + const kvMatch = /^([^=]+)=(.*)$/.exec(line); + if (kvMatch) { + sections[currentSection][kvMatch[1].trim()] = kvMatch[2].trim(); + } + } + return sections; + }, +}); + +// Agent role: parse an INI config file and report its sections and key-value pairs. +const iniConfigParser = agent({ + model: "small", + input: s.object({ configFile: s.string }), + instructions: p`Parse the INI config file at the path provided in configFile. + +Content: +${p.readInput("configFile")} + +Call parseIniSection with the full file content. Build the sections record. totalSections = number of sections (excluding __default__ if empty). totalKeys = total key-value pairs across all sections. hasDefaultSection = true if there are keys outside any named section.`, + output: s.object({ + sections: s.record(s.record(s.string)), + totalKeys: s.number, + totalSections: s.number, + hasDefaultSection: s.boolean, + }), + tools: [parseIniSection], + addons: [repair()], +}); + +export default iniConfigParser; +``` diff --git a/skills/rig/samples/418-git-reflog-inspector.md b/skills/rig/samples/418-git-reflog-inspector.md new file mode 100644 index 0000000..692a53b --- /dev/null +++ b/skills/rig/samples/418-git-reflog-inspector.md @@ -0,0 +1,50 @@ +# 418 - Git Reflog Inspector + +```rig +import { agent, defineTool, p, s } from "rig"; +import { steering } from "rig"; + +const classifyReflogEntry = defineTool("classifyReflogEntry", { + description: "Parse a git reflog line and classify its action type.", + parameters: s.object({ reflogLine: s.string }), + handler({ reflogLine }: { reflogLine: string }) { + const parts = reflogLine.trim().split(/\s+/); + const hash = parts[0] ?? ""; + const rest = parts.slice(1).join(" "); + const lower = rest.toLowerCase(); + const action = + lower.includes("merge") ? ("merge" as const) + : lower.includes("rebase") ? ("rebase" as const) + : lower.includes("reset") ? ("reset" as const) + : lower.includes("checkout") ? ("checkout" as const) + : lower.includes("commit") ? ("commit" as const) + : ("other" as const); + const message = rest.replace(/^HEAD@\{\d+\}:\s*/, "").trim(); + return { hash, action, message }; + }, +}); + +// Agent role: inspect git reflog entries and classify each by action type. +const gitReflogInspector = agent({ + model: "small", + instructions: p`Inspect recent git reflog entries. + +Reflog: +${p.bash("git reflog --oneline -50 2>/dev/null || echo '(no git history)'")} + +For each reflog line, call classifyReflogEntry. Build entries array with hash, action, message. Compute actionCounts as a record from action → count. totalEntries = total lines processed.`, + output: s.object({ + entries: s.array(s.object({ + hash: s.string, + action: s.enum("commit", "merge", "rebase", "reset", "checkout", "other"), + message: s.string, + })), + actionCounts: s.record(s.number), + totalEntries: s.number, + }), + tools: [classifyReflogEntry], + addons: [steering()], +}); + +export default gitReflogInspector; +``` diff --git a/skills/rig/samples/419-source-line-length-auditor.md b/skills/rig/samples/419-source-line-length-auditor.md new file mode 100644 index 0000000..3ea6259 --- /dev/null +++ b/skills/rig/samples/419-source-line-length-auditor.md @@ -0,0 +1,54 @@ +# 419 - Source Line Length Auditor + +```rig +import { agent, defineTool, p, repair, s } from "rig"; + +const auditLineLengths = defineTool("auditLineLengths", { + description: "Audit line lengths in a source file and return statistics.", + parameters: s.object({ filePath: s.path }), + async handler({ filePath }: { filePath: string }) { + const { readFile } = await import("node:fs/promises"); + const content = await readFile(filePath, "utf8"); + const lines = content.split("\n"); + let totalLength = 0; + let maxLength = 0; + let longLineCount = 0; + let veryLongLineCount = 0; + for (const line of lines) { + const len = line.length; + totalLength += len; + if (len > maxLength) maxLength = len; + if (len > 120) veryLongLineCount++; + else if (len > 80) longLineCount++; + } + const avgLength = lines.length > 0 ? totalLength / lines.length : 0; + return { avgLength, maxLength, longLineCount, veryLongLineCount }; + }, +}); + +// Agent role: audit line lengths across TypeScript source files and identify verbose files. +const sourceLineLengthAuditor = agent({ + model: "small", + instructions: p`Audit line lengths across TypeScript source files. + +Files: +${p.bash("find src -name '*.ts' -not -path '*/node_modules/*' 2>/dev/null || echo '(none)'")} + +For each .ts file found, call auditLineLengths with the file path. Build the files record keyed by file path. Compute totalFiles, globalMaxLength (max across all files), mostVerboseFile (path with highest maxLength, omit if no files found).`, + output: s.object({ + files: s.record(s.object({ + avgLength: s.number, + maxLength: s.number, + longLineCount: s.number, + veryLongLineCount: s.number, + })), + totalFiles: s.number, + globalMaxLength: s.number, + mostVerboseFile: s.optional(s.string), + }), + tools: [auditLineLengths], + addons: [repair()], +}); + +export default sourceLineLengthAuditor; +``` diff --git a/skills/rig/samples/420-npm-script-dependency-workflow.md b/skills/rig/samples/420-npm-script-dependency-workflow.md new file mode 100644 index 0000000..ad19e12 --- /dev/null +++ b/skills/rig/samples/420-npm-script-dependency-workflow.md @@ -0,0 +1,61 @@ +# 420 - Npm Script Dependency Workflow + +```rig +import { agent, p, s, workflow } from "rig"; + +const scriptSchema = s.record(s.object({ + command: s.string, + deps: s.array(s.string), +})); + +// Agent role: build a dependency graph of npm scripts by parsing cross-references. +const scriptGraphBuilder = agent({ + model: "small", + output: s.object({ scripts: scriptSchema }), + instructions: p`Read package.json and build a dependency graph of npm scripts. + +${p.read("package.json")} + +For each script, identify which other scripts it references (e.g. via "npm run X" or "yarn X"). Return a scripts record where each entry has the command string and a deps array of referenced script names.`, +}); + +// Agent role: detect cycles in the npm script dependency graph using DFS. +const cycleDetector = agent({ + model: "small", + input: s.object({ scripts: scriptSchema }), + output: s.object({ + hasCycles: s.boolean, + cycles: s.array(s.array(s.string)), + totalNodes: s.number, + }), + instructions: p`Detect cycles in the npm script dependency graph provided in input.scripts using DFS. Return hasCycles, cycles (each cycle as an array of script names), and totalNodes.`, +}); + +// Workflow role: build npm script dependency graph, detect cycles, and produce a recommendation. +const npmScriptDependencyWorkflow = workflow({ + meta: { name: "npmScriptDependency", description: "NPM script dependency cycle detector", phases: ["Build", "Detect", "Recommend"] }, + body: async ({ call, phase }) => { + phase("Build"); + const graph = await call(scriptGraphBuilder, "build graph"); + if (!graph) return { scriptCount: 0, hasCycles: false, cycles: [], recommendation: "empty" as const }; + phase("Detect"); + const cycleResult = await call(cycleDetector, { scripts: graph.scripts }); + if (!cycleResult) return { scriptCount: Object.keys(graph.scripts).length, hasCycles: false, cycles: [], recommendation: "safe" as const }; + phase("Recommend"); + const scriptCount = Object.keys(graph.scripts).length; + const recommendation = scriptCount === 0 + ? ("empty" as const) + : cycleResult.hasCycles + ? ("has-cycles" as const) + : ("safe" as const); + return { + scriptCount, + hasCycles: cycleResult.hasCycles, + cycles: cycleResult.cycles, + recommendation, + }; + }, +}); + +export default npmScriptDependencyWorkflow; +```