Skip to content
Draft
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
55 changes: 55 additions & 0 deletions skills/rig/samples/431-git-file-size-tracker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# 431 - Git File Size Tracker

```rig
import { agent, p, s, defineTool, repair } from "rig";
import { execSync } from "node:child_process";

const getFileSizeAtRevision = defineTool("getFileSizeAtRevision", {
description: "Get the byte size of a file at a specific git revision.",
parameters: s.object({ revision: s.string, filePath: s.string }),
handler({ revision, filePath }: { revision: string; filePath: string }) {
try {
const size = parseInt(
execSync(`git cat-file -s ${revision}:${filePath} 2>/dev/null || echo '0'`, { encoding: "utf-8" }).trim(),
10
);
return { size: isNaN(size) ? 0 : size };
} catch {
return { size: 0 };
}
},
});

// Agent role: track file size changes between the previous commit and HEAD.
const gitFileSizeTracker = agent({
model: "small",
instructions: p`Track file size changes between the previous commit and HEAD.

Files changed in the last commit:
${p.bash("git diff --name-only HEAD~1 HEAD 2>/dev/null || echo ''")}

For each changed file, call getFileSizeAtRevision with revision "HEAD~1" and again with "HEAD".
Compute delta = currentSize - previousSize.
Classify change as "grew" if delta > 0, "shrank" if delta < 0, "unchanged" if delta == 0.
Set largestGrowth to the path with highest positive delta (omit if none grew).
Set largestShrink to the path with most negative delta (omit if none shrank).`,
output: s.object({
files: s.array(
s.object({
path: s.path,
previousSize: s.int,
currentSize: s.int,
delta: s.int,
change: s.enum("grew", "shrank", "unchanged"),
})
),
totalFiles: s.int,
largestGrowth: s.optional(s.string),
largestShrink: s.optional(s.string),
}),
tools: [getFileSizeAtRevision],
addons: [repair()],
});

export default gitFileSizeTracker;
```
67 changes: 67 additions & 0 deletions skills/rig/samples/432-json-pretty-printer-stats.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# 432 - Json Pretty Printer Stats

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

const analyzeJsonStructure = defineTool("analyzeJsonStructure", {
description: "Parse a JSON string and compute key count, max nesting depth, array count, and object count.",
parameters: s.object({ json: s.string }),
handler({ json }: { json: string }) {
function analyze(val: unknown, depth: number): { totalKeys: number; maxDepth: number; arrayCount: number; objectCount: number } {
if (Array.isArray(val)) {
const results = val.map((v: unknown) => analyze(v, depth + 1));
return {
totalKeys: results.reduce((a, r) => a + r.totalKeys, 0),
maxDepth: Math.max(depth, ...results.map(r => r.maxDepth)),
arrayCount: 1 + results.reduce((a, r) => a + r.arrayCount, 0),
objectCount: results.reduce((a, r) => a + r.objectCount, 0),
};
} else if (val !== null && typeof val === "object") {
const keys = Object.keys(val as object);
const results = keys.map(k => analyze((val as Record<string, unknown>)[k], depth + 1));
return {
totalKeys: keys.length + results.reduce((a, r) => a + r.totalKeys, 0),
maxDepth: Math.max(depth, ...results.map(r => r.maxDepth)),
arrayCount: results.reduce((a, r) => a + r.arrayCount, 0),
objectCount: 1 + results.reduce((a, r) => a + r.objectCount, 0),
};
}
return { totalKeys: 0, maxDepth: depth, arrayCount: 0, objectCount: 0 };
}
try {
const parsed = JSON.parse(json);
return analyze(parsed, 0);
} catch {
return { totalKeys: 0, maxDepth: 0, arrayCount: 0, objectCount: 0 };
}
},
});

// Agent role: pretty-print a JSON file and report structural statistics.
const jsonPrettyPrinterStats = agent({
model: "small",
input: s.object({ inputFile: s.path, outputFile: s.optional(s.path) }),
instructions: p`Pretty-print the JSON file and report structural statistics.

File contents:
${p.readInput("inputFile")}

1. Call analyzeJsonStructure with the raw file content.
2. Parse the JSON and re-serialize it with 2-space indentation as "formatted".
3. If outputFile was provided, set outputWritten to true; otherwise false.
4. Return formatted, stats, and outputWritten.`,
output: s.object({
formatted: s.string,
stats: s.object({
totalKeys: s.int,
maxDepth: s.int,
arrayCount: s.int,
objectCount: s.int,
}),
outputWritten: s.boolean,
}),
tools: [analyzeJsonStructure],
});

export default jsonPrettyPrinterStats;
```
50 changes: 50 additions & 0 deletions skills/rig/samples/433-ts-interface-method-counter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# 433 - TS Interface Method Counter

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

const countInterfaceMethods = defineTool("countInterfaceMethods", {
description: "Count method signatures in TypeScript interfaces within a source 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 interfaces: Array<{ name: string; methodCount: number }> = [];
const ifaceRe = /interface\s+(\w+)[^{]*\{([^}]*)\}/gs;
let match: RegExpExecArray | null;
while ((match = ifaceRe.exec(content)) !== null) {
const name = match[1];
const body = match[2];
const methods = body.match(/\w+\??\s*\([^)]*\)/g) || [];
interfaces.push({ name, methodCount: methods.length });
}
return { interfaceCount: interfaces.length, totalMethods: interfaces.reduce((a, i) => a + i.methodCount, 0), interfaces };
},
});

// Agent role: count method signatures in TypeScript interfaces across all source files.
const tsInterfaceMethodCounter = agent({
model: "small",
instructions: p`Count TypeScript interface method signatures across source files.

TypeScript source files:
${p.glob("src/**/*.ts")}

For each file path, call countInterfaceMethods and collect the results.
Compute totalFiles (files processed), totalInterfaces (sum of interfaceCount across all files),
totalMethods (sum of totalMethods across all files).
Build files array with file, interfaceCount, methodCount per file.
Set topInterface to the interface name with most methods across all files, or omit if none found.`,
tools: [countInterfaceMethods],
output: s.object({
totalFiles: s.int,
totalInterfaces: s.int,
totalMethods: s.int,
files: s.array(s.object({ file: s.path, interfaceCount: s.int, methodCount: s.int })),
topInterface: s.optional(s.string),
}),
maxTurns: 6,
});

export default tsInterfaceMethodCounter;
```
52 changes: 52 additions & 0 deletions skills/rig/samples/434-git-tag-annotation-extractor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# 434 - Git Tag Annotation Extractor

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

const classifyTagKind = defineTool("classifyTagKind", {
description: "Classify a git tag as release, pre-release, milestone, or other based on its name.",
parameters: s.object({ tagName: s.string, subject: s.string }),
handler({ tagName, subject }: { tagName: string; subject: string }) {
const name = tagName.toLowerCase();
if (/-(alpha|beta|rc|preview|pre|next|canary)\b/.test(name)) {
return { kind: "pre-release" as const };
}
if (/^v?\d+\.\d+(\.\d+)?$/.test(name)) {
return { kind: "release" as const };
}
if (/milestone|sprint|phase/.test(name) || /milestone|sprint|phase/.test(subject.toLowerCase())) {
return { kind: "milestone" as const };
}
return { kind: "other" as const };
},
});

// Agent role: extract and classify git tag annotations in the repository.
const gitTagAnnotationExtractor = agent({
model: "small",
instructions: p`Extract and classify git tag annotations.

Tag listing with subject and date:
${p.bash("git tag -l --format='%(refname:short)|%(subject)|%(taggerdate:short)' 2>/dev/null | head -50")}

For each line (format: name|subject|date), call classifyTagKind with tagName and subject.
Build a tags array with name, subject, date (omit if empty), and kind.
Count totalTags (total lines processed), releaseTags (kind === "release").`,
tools: [classifyTagKind],
output: s.object({
tags: s.array(
s.object({
name: s.string,
subject: s.string,
date: s.optional(s.string),
kind: s.enum("release", "pre-release", "milestone", "other"),
})
),
totalTags: s.int,
releaseTags: s.int,
}),
addons: [repair()],
});

export default gitTagAnnotationExtractor;
```
55 changes: 55 additions & 0 deletions skills/rig/samples/435-jsonl-file-analyzer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# 435 - JSONL File Analyzer

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

const parseJSONLFile = defineTool("parseJSONLFile", {
description: "Parse JSONL content: count valid/invalid lines and extract sample top-level keys.",
parameters: s.object({ content: s.string }),
handler({ content }: { content: string }) {
const lines = content.split("\n").filter((l: string) => l.trim().length > 0);
let validLines = 0;
let invalidLines = 0;
let sampleKeys: string[] = [];
for (const line of lines) {
try {
const obj = JSON.parse(line);
validLines++;
if (sampleKeys.length === 0 && obj !== null && typeof obj === "object" && !Array.isArray(obj)) {
sampleKeys = Object.keys(obj).slice(0, 10);
}
} catch {
invalidLines++;
}
}
return { lineCount: lines.length, validLines, invalidLines, sampleKeys };
},
});

// Agent role: analyze a JSONL file and report parse statistics.
const jsonlFileAnalyzer = agent({
model: "small",
input: s.object({ inputFile: s.path }),
instructions: p`Analyze the JSONL (JSON Lines) file.

File contents:
${p.readInput("inputFile")}

Call parseJSONLFile with the complete file content.
Compute parseRate as validLines / lineCount (0 if lineCount is 0).
Set isEmpty to true if lineCount === 0.
Return all fields from the tool result plus parseRate and isEmpty.`,
tools: [parseJSONLFile],
output: s.object({
lineCount: s.int,
validLines: s.int,
invalidLines: s.int,
parseRate: s.number,
sampleKeys: s.array(s.string),
isEmpty: s.boolean,
}),
addons: [repair()],
});

export default jsonlFileAnalyzer;
```
64 changes: 64 additions & 0 deletions skills/rig/samples/436-npm-peer-dep-conflict-checker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# 436 - NPM Peer Dep Conflict Checker

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

const detectPeerConflict = defineTool("detectPeerConflict", {
description: "Check if an installed package version satisfies the expected peer dependency range.",
parameters: s.object({
packageName: s.string,
requiredVersion: s.string,
installedVersion: s.string,
}),
handler({ requiredVersion, installedVersion }: { packageName: string; requiredVersion: string; installedVersion: string }) {
if (!installedVersion) {
return { hasConflict: true, severity: "error" as const, message: "Package not installed" };
}
const reqMajor = parseInt(requiredVersion.replace(/[^0-9]/, ""), 10);
const instMajor = parseInt(installedVersion.replace(/[^0-9]/, ""), 10);
if (!isNaN(reqMajor) && !isNaN(instMajor) && reqMajor !== instMajor) {
return { hasConflict: true, severity: "error" as const, message: `Expected major ${reqMajor}, found ${instMajor}` };
}
if (requiredVersion.startsWith(">") || requiredVersion.startsWith("^") || requiredVersion.startsWith("~")) {
return { hasConflict: false, severity: "none" as const, message: "Compatible" };
}
return { hasConflict: false, severity: "none" as const, message: "Satisfied" };
},
});

// Agent role: identify peer dependency conflicts in the current npm project.
const npmPeerDepConflictChecker = agent({
model: "small",
instructions: p`Check for peer dependency conflicts in the npm project.

package.json:
${p.read("package.json")}

npm dependency tree:
${p.bash("npm ls --json 2>&1 | head -200")}

1. Extract peerDependencies from package.json.
2. For each peer dep, find its installed version from the npm ls output.
3. Call detectPeerConflict with packageName, requiredVersion (from peerDependencies), and installedVersion.
4. Build conflicts array. Set totalConflicts to count of entries with hasConflict true.
5. summary: one-sentence overview of the conflict status.`,
tools: [detectPeerConflict],
output: s.object({
totalPeerDeps: s.int,
conflictsFound: s.int,
conflicts: s.array(
s.object({
package: s.string,
required: s.string,
installed: s.optional(s.string),
severity: s.enum("none", "warning", "error"),
})
),
summary: s.string,
}),
maxTurns: 5,
addons: [repair()],
});

export default npmPeerDepConflictChecker;
```
45 changes: 45 additions & 0 deletions skills/rig/samples/437-ts-symbol-frequency-recorder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# 437 - TS Symbol Frequency Recorder

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

const countSymbolUsage = defineTool("countSymbolUsage", {
description: "Count identifier frequency in a TypeScript source 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 freq: Record<string, number> = {};
const matches = content.match(/\b[a-zA-Z_$][a-zA-Z0-9_$]{2,}\b/g) || [];
for (const sym of matches) {
freq[sym] = (freq[sym] ?? 0) + 1;
}
return freq;
},
});

// Agent role: record identifier frequency across TypeScript source files.
const tsSymbolFrequencyRecorder = agent({
model: "small",
instructions: p`Record identifier frequency across TypeScript source files.

TypeScript files:
${p.glob("src/**/*.ts")}

For each file path, call countSymbolUsage to get a frequency record.
Merge all per-file records into a single global symbolFrequency map (sum counts for duplicates).
Compute totalFiles (count of files processed) and uniqueSymbols (number of distinct keys in symbolFrequency).
Build topSymbols as the top 10 entries by count sorted descending, each with symbol and count.`,
tools: [countSymbolUsage],
output: s.object({
totalFiles: s.int,
uniqueSymbols: s.int,
topSymbols: s.array(s.object({ symbol: s.string, count: s.int })),
symbolFrequency: s.record(s.int),
}),
maxTurns: 6,
addons: [repair()],
});

export default tsSymbolFrequencyRecorder;
```
Loading