co-mono/packages/coding-agent/examples/extensions/claude-rules.ts
LiuVaayne 49c4fe0bc1
feat: add claude-rules extension example (#461)
Add an extension that scans .claude/rules/ folder for project-specific
rules and lists them in the system prompt. The agent can then load
relevant rules using the read tool when needed.
2026-01-05 16:59:07 +01:00

83 lines
2.4 KiB
TypeScript

/**
* Claude Rules Extension
*
* Scans the project's .claude/rules/ folder for rule files and lists them
* in the system prompt. The agent can then use the read tool to load
* specific rules when needed.
*
* Best practices for .claude/rules/:
* - Keep rules focused: Each file should cover one topic (e.g., testing.md, api-design.md)
* - Use descriptive filenames: The filename should indicate what the rules cover
* - Use conditional rules sparingly: Only add paths frontmatter when rules truly apply to specific file types
* - Organize with subdirectories: Group related rules (e.g., frontend/, backend/)
*
* Usage:
* 1. Copy this file to ~/.pi/agent/extensions/ or your project's .pi/extensions/
* 2. Create .claude/rules/ folder in your project root
* 3. Add .md files with your rules
*/
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
import * as fs from "node:fs";
import * as path from "node:path";
/**
* Recursively find all .md files in a directory
*/
function findMarkdownFiles(dir: string, basePath: string = ""): string[] {
const results: string[] = [];
if (!fs.existsSync(dir)) {
return results;
}
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
const relativePath = basePath ? `${basePath}/${entry.name}` : entry.name;
if (entry.isDirectory()) {
results.push(...findMarkdownFiles(path.join(dir, entry.name), relativePath));
} else if (entry.isFile() && entry.name.endsWith(".md")) {
results.push(relativePath);
}
}
return results;
}
export default function claudeRulesExtension(pi: ExtensionAPI) {
let ruleFiles: string[] = [];
let rulesDir: string = "";
// Scan for rules on session start
pi.on("session_start", async (_event, ctx) => {
rulesDir = path.join(ctx.cwd, ".claude", "rules");
ruleFiles = findMarkdownFiles(rulesDir);
if (ruleFiles.length > 0) {
ctx.ui.notify(`Found ${ruleFiles.length} rule(s) in .claude/rules/`, "info");
}
});
// Append available rules to system prompt
pi.on("before_agent_start", async () => {
if (ruleFiles.length === 0) {
return;
}
const rulesList = ruleFiles.map((f) => `- .claude/rules/${f}`).join("\n");
return {
systemPromptAppend: `
## Project Rules
The following project rules are available in .claude/rules/:
${rulesList}
When working on tasks related to these rules, use the read tool to load the relevant rule files for guidance.
`,
};
});
}