refactor(subagent): split extension into modules

This commit is contained in:
2026-05-12 14:15:11 -04:00
parent 4d5584aa8c
commit 4f80b590da
9 changed files with 669 additions and 604 deletions

73
src/prompts.ts Normal file
View File

@@ -0,0 +1,73 @@
import * as fs from "node:fs";
import * as path from "node:path";
import { parseFrontmatter } from "@mariozechner/pi-coding-agent";
import { FINALIZE_TOOL_NAME, PROMPTS_DIR } from "./constants.ts";
import type { PromptConfig } from "./types.ts";
import { FinalizeStatus } from "./types.ts";
// Parse Tool List - Frontmatter may use YAML arrays or comma-delimited strings.
export function parseToolList(value: unknown): string[] {
if (!value) return [];
if (Array.isArray(value)) {
return value
.map(String)
.map((tool) => tool.trim())
.filter(Boolean);
}
if (typeof value === "string") {
return value
.split(",")
.map((tool) => tool.trim())
.filter(Boolean);
}
return [];
}
// Discover Prompts - Load prompt markdown files from this extension's prompts dir.
export function discoverPrompts(): PromptConfig[] {
if (!fs.existsSync(PROMPTS_DIR)) return [];
const prompts: PromptConfig[] = [];
const entries = fs.readdirSync(PROMPTS_DIR, { withFileTypes: true });
for (const entry of entries) {
if (!entry.name.endsWith(".md")) continue;
if (!entry.isFile() && !entry.isSymbolicLink()) continue;
const filePath = path.join(PROMPTS_DIR, entry.name);
const content = fs.readFileSync(filePath, "utf-8");
const { frontmatter, body } =
parseFrontmatter<Record<string, unknown>>(content);
if (
typeof frontmatter.name !== "string" ||
typeof frontmatter.description !== "string"
) {
continue;
}
prompts.push({
name: frontmatter.name,
description: frontmatter.description,
approvedTools: parseToolList(
frontmatter.approved_tools ?? frontmatter.allowed_tools,
),
deniedTools: parseToolList(frontmatter.denied_tools),
systemPrompt: body.trim(),
filePath,
});
}
return prompts.sort((a, b) => a.name.localeCompare(b.name));
}
// Build Finalize Prompt - Child agents must terminate by calling this tool.
export function buildSubagentPrompt(agent: PromptConfig): string {
const finalizePrompt = [
"You are running as a subagent.",
`When the task is complete, call ${FINALIZE_TOOL_NAME} as your final action.`,
"Do not provide the final answer as normal assistant text.",
`${FINALIZE_TOOL_NAME} requires status ${FinalizeStatus.SUCCESS} with result, or status ${FinalizeStatus.ERROR} with error and optional result.`,
].join("\n");
return [agent.systemPrompt, finalizePrompt].filter(Boolean).join("\n\n");
}