feat: add .pi/ config for iop-rule-loader

This commit is contained in:
toki 2026-07-04 10:17:40 +09:00
parent de44a80cb7
commit 4cd214f53c
2 changed files with 101 additions and 0 deletions

98
.pi/iop-rule-loader.ts Normal file
View file

@ -0,0 +1,98 @@
import * as fs from "node:fs";
import * as path from "node:path";
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
type RuleSpec = {
relativePath: string;
};
type LoadedRule = {
relativePath: string;
content: string;
};
const entryRules: RuleSpec[] = [
{ relativePath: "agent-ops/rules/common/rules.md" },
];
function findProjectRoot(startDir: string): string {
let current = path.resolve(startDir);
for (;;) {
const marker = path.join(current, "agent-ops", "rules", "common", "rules.md");
if (fs.existsSync(marker)) {
return current;
}
const parent = path.dirname(current);
if (parent === current) {
return path.resolve(startDir);
}
current = parent;
}
}
function loadEntryRules(projectRoot: string): LoadedRule[] {
return entryRules.flatMap((rule) => {
const fullPath = path.join(projectRoot, rule.relativePath);
if (!fs.existsSync(fullPath)) {
return [];
}
return [
{
relativePath: rule.relativePath,
content: fs.readFileSync(fullPath, "utf8").trimEnd(),
},
];
});
}
function buildRulesPrompt(projectRoot: string, rules: LoadedRule[]): string {
const blocks = rules
.map(
(rule) => `<common_rule path="${rule.relativePath}">
${rule.content}
</common_rule>`,
)
.join("\n\n");
return `## Agent-Ops Common Rules
The following common rule entry file was loaded automatically from ${projectRoot}. Treat this common rule as already read and follow it together with AGENTS.md. When the common rule names additional session, domain, roadmap, test, or skill rules, load those files on demand when their trigger conditions apply.
${blocks}`;
}
export default function iopRuleLoader(pi: ExtensionAPI) {
let projectRoot = "";
let loadedRules: LoadedRule[] = [];
pi.on("session_start", async (_event, ctx) => {
projectRoot = findProjectRoot(ctx.cwd);
loadedRules = loadEntryRules(projectRoot);
if (loadedRules.length > 0) {
const names = loadedRules.map((rule) => rule.relativePath).join(", ");
ctx.ui.notify(`Loaded Agent-Ops common rules: ${names}`, "info");
}
});
pi.on("before_agent_start", async (event, ctx) => {
const root = projectRoot || findProjectRoot(ctx.cwd);
const rules = loadEntryRules(root);
if (rules.length === 0) {
return;
}
loadedRules = rules;
projectRoot = root;
return {
systemPrompt: `${event.systemPrompt}
${buildRulesPrompt(projectRoot, loadedRules)}`,
};
});
}

3
.pi/settings.json Normal file
View file

@ -0,0 +1,3 @@
{
"extensions": ["iop-rule-loader.ts"]
}