74 lines
2.4 KiB
TypeScript
74 lines
2.4 KiB
TypeScript
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");
|
|
}
|