feat: add subagent preset discovery and resolveChildModel change
This commit is contained in:
95
src/presets.ts
Normal file
95
src/presets.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
||||
import { dirname, join } from "node:path";
|
||||
import { getAgentDir, parseFrontmatter } from "@mariozechner/pi-coding-agent";
|
||||
|
||||
export interface SubagentPreset {
|
||||
name: string;
|
||||
description: string;
|
||||
model?: string;
|
||||
tools?: string[];
|
||||
systemPrompt: string;
|
||||
source: "global" | "project";
|
||||
filePath: string;
|
||||
}
|
||||
|
||||
export interface SubagentPresetDiscoveryResult {
|
||||
presets: SubagentPreset[];
|
||||
projectPresetsDir: string | null;
|
||||
}
|
||||
|
||||
function findNearestProjectPresetsDir(cwd: string): string | null {
|
||||
let current = cwd;
|
||||
while (true) {
|
||||
const candidate = join(current, ".pi", "subagents");
|
||||
try {
|
||||
if (statSync(candidate).isDirectory()) return candidate;
|
||||
} catch (_err) {
|
||||
// ignore
|
||||
}
|
||||
const parent = dirname(current);
|
||||
if (parent === current) return null;
|
||||
current = parent;
|
||||
}
|
||||
}
|
||||
|
||||
function loadPresetDir(dir: string, source: "global" | "project"): SubagentPreset[] {
|
||||
if (!existsSync(dir)) return [];
|
||||
const entries = readdirSync(dir, { withFileTypes: true });
|
||||
const presets: SubagentPreset[] = [];
|
||||
|
||||
for (const entry of entries) {
|
||||
if (!entry.name.endsWith(".md")) continue;
|
||||
if (!entry.isFile() && !entry.isSymbolicLink()) continue;
|
||||
const filePath = join(dir, entry.name);
|
||||
let content: string;
|
||||
try {
|
||||
content = readFileSync(filePath, "utf8");
|
||||
} catch (_err) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const parsed = parseFrontmatter<Record<string, unknown>>(content);
|
||||
const frontmatter = parsed.frontmatter ?? {};
|
||||
const body = parsed.body ?? "";
|
||||
|
||||
if (typeof frontmatter.name !== "string") continue;
|
||||
if (typeof frontmatter.description !== "string") continue;
|
||||
|
||||
const tools = typeof frontmatter.tools === "string"
|
||||
? frontmatter.tools.split(",").map((t) => t.trim()).filter(Boolean)
|
||||
: undefined;
|
||||
|
||||
presets.push({
|
||||
name: frontmatter.name,
|
||||
description: frontmatter.description,
|
||||
model: typeof frontmatter.model === "string" ? frontmatter.model : undefined,
|
||||
tools: tools && tools.length > 0 ? tools : undefined,
|
||||
systemPrompt: body.trim(),
|
||||
source,
|
||||
filePath,
|
||||
});
|
||||
}
|
||||
|
||||
return presets;
|
||||
}
|
||||
|
||||
export function discoverSubagentPresets(cwd: string, options: { homeDir?: string } = {}): SubagentPresetDiscoveryResult {
|
||||
const globalAgentDir = options.homeDir ? join(options.homeDir, ".pi", "agent") : getAgentDir();
|
||||
const globalDir = join(globalAgentDir, "subagents");
|
||||
const projectPresetsDir = findNearestProjectPresetsDir(cwd);
|
||||
|
||||
const map = new Map<string, SubagentPreset>();
|
||||
|
||||
for (const preset of loadPresetDir(globalDir, "global")) {
|
||||
map.set(preset.name, preset);
|
||||
}
|
||||
|
||||
if (projectPresetsDir) {
|
||||
for (const preset of loadPresetDir(projectPresetsDir, "project")) {
|
||||
// project overrides global by name
|
||||
map.set(preset.name, preset);
|
||||
}
|
||||
}
|
||||
|
||||
return { presets: Array.from(map.values()), projectPresetsDir };
|
||||
}
|
||||
Reference in New Issue
Block a user