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
43 changes: 43 additions & 0 deletions skills/rig/samples/501-dockerfile-env-inspector.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# 501 - Dockerfile ENV Inspector

```rig
import { agent, p, s, defineTool, repair } from "rig";
import { readFile } from "node:fs/promises";

const extractDockerfileEnv = defineTool("extractDockerfileEnv", {
description: "Extract ENV instructions from a Dockerfile",
parameters: s.object({ filePath: s.string }),
handler: async ({ filePath }: { filePath: string }) => {
const content = await readFile(filePath, "utf8");
const envVars: Record<string, string> = {};
const keyValueRe = /^ENV\s+(\w+)=(\S+)/gm;
const keySpaceRe = /^ENV\s+(\w+)\s+(.+)$/gm;
let m: RegExpExecArray | null;
while ((m = keyValueRe.exec(content)) !== null) {
envVars[m[1]] = m[2];
}
while ((m = keySpaceRe.exec(content)) !== null) {
if (!(m[1] in envVars)) envVars[m[1]] = m[2].trim();
}
return envVars;
},
});

// Agent role: Parse ENV instructions from all Dockerfiles in the workspace and classify each variable.
const dockerfileEnvInspector = agent({
model: "small",
instructions: p`You are given the output of: ${p.bash("find . -name Dockerfile -o -name 'Dockerfile.*' 2>/dev/null | head -20")}.
For each Dockerfile path found, call extractDockerfileEnv to extract its ENV variables.
Classify each variable as build-time (used during build, e.g. VERSION, BUILD_DATE) or runtime (passed to app at runtime).
Return the declared output.`,
output: s.object({
files: s.record(s.record(s.string)),
totalVars: s.int,
totalFiles: s.int,
}),
tools: [extractDockerfileEnv],
addons: [repair()],
});

export default dockerfileEnvInspector;
```
39 changes: 39 additions & 0 deletions skills/rig/samples/502-npm-script-prefix-analyzer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# 502 - NPM Script Prefix Analyzer

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

const classifyScriptPrefix = defineTool("classifyScriptPrefix", {
description: "Classify the command prefix of an npm script",
parameters: s.object({ command: s.string }),
handler: ({ command }: { command: string }) => {
const first = command.trim().split(/\s+/)[0] ?? "";
if (first === "node") return "node" as const;
if (first === "ts-node" || first === "ts-node-esm") return "ts-node" as const;
if (first === "npx") return "npx" as const;
if (first === "sh" || first === "bash" || first === "zsh" || first.startsWith("./")) return "shell" as const;
return "other" as const;
},
});

// Agent role: Analyze npm script command prefixes in package.json and summarize by prefix class.
const npmScriptPrefixAnalyzer = agent({
model: "small",
instructions: p`Analyze the npm scripts in: ${p.read("package.json")}.
For each script, call classifyScriptPrefix with the script command.
Return the declared output with per-script details and a summary of prefix counts.`,
output: s.object({
scripts: s.record(s.object({
command: s.string,
prefix: s.string,
prefixClass: s.enum("node", "ts-node", "npx", "shell", "other"),
})),
totalScripts: s.int,
prefixCounts: s.record(s.int),
}),
tools: [classifyScriptPrefix],
addons: [steering(), repair()],
});

export default npmScriptPrefixAnalyzer;
```
49 changes: 49 additions & 0 deletions skills/rig/samples/503-ini-config-parser.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# 503 - INI Config Parser

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

const parseIniSection = defineTool("parseIniSection", {
description: "Parse an INI config file into sections and key-value pairs",
parameters: s.object({ content: s.string }),
handler: ({ content }: { content: string }) => {
const sections: Record<string, Record<string, string>> = {};
let current = "__default__";
sections[current] = {};
for (const raw of content.split(/\r?\n/)) {
const line = raw.trim();
if (!line || line.startsWith(";") || line.startsWith("#")) continue;
const sectionMatch = line.match(/^\[(.+)\]$/);
if (sectionMatch) {
current = sectionMatch[1];
sections[current] = {};
continue;
}
const kvMatch = line.match(/^([^=]+)=(.*)$/);
if (kvMatch) {
sections[current][kvMatch[1].trim()] = kvMatch[2].trim();
}
}
return sections;
},
});

// Agent role: Parse a caller-supplied INI config file and return structured sections and key counts.
const iniConfigParser = agent({
model: "small",
input: s.object({ configFile: s.string }),
instructions: p`Parse the INI config file at the path provided in input.configFile: ${p.readInput("configFile")}.
Call parseIniSection with the file contents.
Return the declared output.`,
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;
```
43 changes: 43 additions & 0 deletions skills/rig/samples/504-git-reflog-inspector.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# 504 - Git Reflog Inspector

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

const classifyReflogEntry = defineTool("classifyReflogEntry", {
description: "Classify a single git reflog entry line",
parameters: s.object({ line: s.string }),
handler: ({ line }: { line: string }) => {
const hashMatch = line.match(/^([0-9a-f]+)/);
const hash = hashMatch ? hashMatch[1] : "";
const lower = line.toLowerCase();
let action: "commit" | "merge" | "rebase" | "reset" | "checkout" | "other" = "other";
if (lower.includes("merge")) action = "merge";
else if (lower.includes("rebase")) action = "rebase";
else if (lower.includes("reset")) action = "reset";
else if (lower.includes("checkout")) action = "checkout";
else if (lower.includes("commit")) action = "commit";
return { hash, action };
},
});

// Agent role: Inspect the last 50 git reflog entries, classify each action, and return counts.
const gitReflogInspector = agent({
model: "small",
instructions: p`Inspect git reflog: ${p.bash("git reflog --oneline -50 2>/dev/null || echo 'no reflog'")}.
For each line, call classifyReflogEntry to get the hash and action.
Return the declared output.`,
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;
```
51 changes: 51 additions & 0 deletions skills/rig/samples/505-source-line-length-auditor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# 505 - Source Line Length Auditor

```rig
import { agent, p, s, defineTool, repair } from "rig";
import { readFile } from "node:fs/promises";

const auditLineLengths = defineTool("auditLineLengths", {
description: "Audit line lengths in a source file",
parameters: s.object({ filePath: s.string }),
handler: async ({ filePath }: { filePath: string }) => {
const content = await readFile(filePath, "utf8");
const lines = content.split(/\r?\n/);
let total = 0;
let maxLength = 0;
let longLineCount = 0;
let veryLongLineCount = 0;
for (const line of lines) {
const len = line.length;
total += len;
if (len > maxLength) maxLength = len;
if (len > 80) longLineCount++;
if (len > 120) veryLongLineCount++;
}
const avgLength = lines.length > 0 ? total / lines.length : 0;
return { avgLength, maxLength, longLineCount, veryLongLineCount };
},
});

// Agent role: Audit line lengths across all TypeScript source files and identify the most verbose file.
const sourceLineLengthAuditor = agent({
model: "small",
instructions: p`Audit TypeScript source files found at: ${p.bash("find src -name '*.ts' 2>/dev/null | head -50 || echo 'no src dir'")}.
For each file path, call auditLineLengths to get per-file statistics.
Return the declared output.`,
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;
```
60 changes: 60 additions & 0 deletions skills/rig/samples/506-npm-script-dep-workflow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# 506 - NPM Script Dep Workflow

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

// Agent role: Read package.json and build a graph of npm script dependencies via pre/post hooks.
const scriptGraphBuilder = agent({
name: "scriptGraphBuilder",
model: "small",
instructions: p`Read package.json: ${p.read("package.json")}.
Extract all npm scripts. For each script, identify its pre/post hook dependencies (e.g., "prebuild" runs before "build").
Return the declared output.`,
output: s.object({
scripts: s.record(s.object({
command: s.string,
deps: s.array(s.string),
})),
totalScripts: s.int,
}),
});

// Agent role: Detect cycles in a script dependency graph using DFS.
const cycleDetector = agent({
name: "cycleDetector",
model: "small",
input: s.object({
scripts: s.record(s.object({
command: s.string,
deps: s.array(s.string),
})),
totalScripts: s.int,
}),
instructions: `Analyze the provided script dependency graph for cycles using DFS.
Return whether cycles exist, list any cycles found, and the total node count.`,
output: s.object({
hasCycles: s.boolean,
cycles: s.array(s.array(s.string)),
totalNodes: s.int,
}),
});

// Workflow role: Build npm script dependency graph then detect cycles.
const npmScriptDepWorkflow = workflow({
meta: { name: "npm-script-dep-workflow", description: "Analyze npm script pre/post dependency graph and detect cycles" },
body: async ({ call }) => {
const graph = await call(scriptGraphBuilder, "build script graph from package.json");
const scripts = graph?.scripts ?? {};
const totalScripts = graph?.totalScripts ?? 0;
const cycles = await call(cycleDetector, { scripts, totalScripts });
const hasCycles = cycles?.hasCycles ?? false;
const cycleList = cycles?.cycles ?? [];
let recommendation: "safe" | "has-cycles" | "empty" = "safe";
if (totalScripts === 0) recommendation = "empty";
else if (hasCycles) recommendation = "has-cycles";
return { scriptCount: totalScripts, hasCycles, cycles: cycleList, recommendation };
},
});

export default npmScriptDepWorkflow;
```
92 changes: 92 additions & 0 deletions skills/rig/samples/507-html-form-field-extractor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# 507 - HTML Form Field Extractor

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

const extractFormFields = defineTool("extractFormFields", {
description: "Extract HTML form fields from HTML content",
parameters: s.object({ content: s.string }),
handler: ({ content }: { content: string }) => {
type FieldType = "text" | "email" | "password" | "checkbox" | "radio" | "select" | "textarea" | "other";
const fields: Array<{ name?: string; type: FieldType; required: boolean; id?: string }> = [];
const inputRe = /<input([^>]*)>/gi;
const selectRe = /<select([^>]*)>/gi;
const textareaRe = /<textarea([^>]*)>/gi;
const getAttr = (attrs: string, attr: string): string | undefined => {
const m = attrs.match(new RegExp(`${attr}\\s*=\\s*["']?([^"'\\s>]*)`, "i"));
return m ? m[1] : undefined;
};
const classifyType = (t?: string): FieldType => {
if (!t) return "text";
const lower = t.toLowerCase();
if (lower === "email") return "email";
if (lower === "password") return "password";
if (lower === "checkbox") return "checkbox";
if (lower === "radio") return "radio";
if (lower === "text") return "text";
return "other";
};
let m: RegExpExecArray | null;
while ((m = inputRe.exec(content)) !== null) {
const attrs = m[1];
const entry: { name?: string; type: FieldType; required: boolean; id?: string } = {
type: classifyType(getAttr(attrs, "type")),
required: /\brequired\b/i.test(attrs),
};
const name = getAttr(attrs, "name");
if (name !== undefined) entry.name = name;
const id = getAttr(attrs, "id");
if (id !== undefined) entry.id = id;
fields.push(entry);
}
while ((m = selectRe.exec(content)) !== null) {
const attrs = m[1];
const entry: { name?: string; type: FieldType; required: boolean; id?: string } = {
type: "select" as const,
required: /\brequired\b/i.test(attrs),
};
const name = getAttr(attrs, "name");
if (name !== undefined) entry.name = name;
const id = getAttr(attrs, "id");
if (id !== undefined) entry.id = id;
fields.push(entry);
}
while ((m = textareaRe.exec(content)) !== null) {
const attrs = m[1];
const entry: { name?: string; type: FieldType; required: boolean; id?: string } = {
type: "textarea" as const,
required: /\brequired\b/i.test(attrs),
};
const name = getAttr(attrs, "name");
if (name !== undefined) entry.name = name;
const id = getAttr(attrs, "id");
if (id !== undefined) entry.id = id;
fields.push(entry);
}
return fields;
},
});

// Agent role: Extract and classify HTML form fields from a caller-supplied HTML file.
const htmlFormFieldExtractor = agent({
model: "small",
input: s.object({ htmlFile: s.string }),
instructions: p`Extract form fields from the HTML file at input.htmlFile: ${p.readInput("htmlFile")}.
Call extractFormFields with the file content.
Return the declared output.`,
output: s.object({
fields: s.array(s.object({
name: s.optional(s.string),
type: s.enum("text", "email", "password", "checkbox", "radio", "select", "textarea", "other"),
required: s.boolean,
id: s.optional(s.string),
})),
totalFields: s.int,
fieldTypeCounts: s.record(s.int),
}),
tools: [extractFormFields],
addons: [repair()],
});

export default htmlFormFieldExtractor;
```
Loading