a21a542afd
- Extracted prompt rendering and formatting utilities from coding-agent to centralized pi-utils package with new API surface (prompt.render, prompt.format, prompt.registerHelper). - Migrated parseFrontmatter utility from coding-agent to pi-utils package; updated 8 files to import from @oh-my-pi/pi-utils. - Removed 170-line prompt-format.ts module and consolidated 192 lines of Handlebars helper registrations into pi-utils prompt module. - Updated 60+ files across coding-agent and typescript-edit-benchmark to use new prompt.render() and prompt.format() API from pi-utils. - Simplified prompt-templates.ts by delegating core functionality to pi-utils while retaining custom helper registrations (jtdToTypeScript, jsonStringify, etc.).
119 lines
3.8 KiB
TypeScript
119 lines
3.8 KiB
TypeScript
/**
|
|
* GitHub Copilot Provider
|
|
*
|
|
* Loads configuration from GitHub Copilot's config directories.
|
|
* Priority: 30 (shared standard provider)
|
|
*
|
|
* Sources:
|
|
* - Project: .github/ (project-only, no user-level discovery)
|
|
*
|
|
* Capabilities:
|
|
* - context-files: copilot-instructions.md in .github/
|
|
* - instructions: *.instructions.md in .github/instructions/ with applyTo frontmatter
|
|
*/
|
|
import * as path from "node:path";
|
|
import { parseFrontmatter } from "@oh-my-pi/pi-utils";
|
|
import { registerProvider } from "../capability";
|
|
import { type ContextFile, contextFileCapability } from "../capability/context-file";
|
|
import { readFile } from "../capability/fs";
|
|
import { type Instruction, instructionCapability } from "../capability/instruction";
|
|
import type { LoadContext, LoadResult, SourceMeta } from "../capability/types";
|
|
|
|
import { calculateDepth, createSourceMeta, getProjectPath, loadFilesFromDir } from "./helpers";
|
|
|
|
const PROVIDER_ID = "github";
|
|
const DISPLAY_NAME = "GitHub Copilot";
|
|
const PRIORITY = 30;
|
|
|
|
// =============================================================================
|
|
// Context Files
|
|
// =============================================================================
|
|
|
|
async function loadContextFiles(ctx: LoadContext): Promise<LoadResult<ContextFile>> {
|
|
const items: ContextFile[] = [];
|
|
const warnings: string[] = [];
|
|
|
|
const copilotInstructionsPath = getProjectPath(ctx, "github", "copilot-instructions.md");
|
|
if (copilotInstructionsPath) {
|
|
const content = await readFile(copilotInstructionsPath);
|
|
if (content) {
|
|
const fileDir = path.dirname(copilotInstructionsPath);
|
|
const depth = calculateDepth(ctx.cwd, fileDir, path.sep);
|
|
|
|
items.push({
|
|
path: copilotInstructionsPath,
|
|
content,
|
|
level: "project",
|
|
depth,
|
|
_source: createSourceMeta(PROVIDER_ID, copilotInstructionsPath, "project"),
|
|
});
|
|
}
|
|
}
|
|
|
|
return { items, warnings };
|
|
}
|
|
|
|
// =============================================================================
|
|
// Instructions
|
|
// =============================================================================
|
|
|
|
async function loadInstructions(ctx: LoadContext): Promise<LoadResult<Instruction>> {
|
|
const items: Instruction[] = [];
|
|
const warnings: string[] = [];
|
|
|
|
const instructionsDir = getProjectPath(ctx, "github", "instructions");
|
|
if (instructionsDir) {
|
|
const result = await loadFilesFromDir<Instruction>(ctx, instructionsDir, PROVIDER_ID, "project", {
|
|
extensions: ["md"],
|
|
transform: transformInstruction,
|
|
});
|
|
items.push(...result.items);
|
|
if (result.warnings) warnings.push(...result.warnings);
|
|
}
|
|
|
|
return { items, warnings };
|
|
}
|
|
|
|
function transformInstruction(name: string, content: string, filePath: string, source: SourceMeta): Instruction | null {
|
|
// Only process .instructions.md files
|
|
if (!name.endsWith(".instructions.md")) {
|
|
return null;
|
|
}
|
|
|
|
const { frontmatter, body } = parseFrontmatter(content, { source: filePath });
|
|
|
|
// Extract applyTo glob pattern from frontmatter
|
|
const applyTo = typeof frontmatter.applyTo === "string" ? frontmatter.applyTo : undefined;
|
|
|
|
// Derive name from filename (strip .instructions.md suffix)
|
|
const instructionName = path.basename(name, ".instructions.md");
|
|
|
|
return {
|
|
name: instructionName,
|
|
path: filePath,
|
|
content: body,
|
|
applyTo,
|
|
_source: source,
|
|
};
|
|
}
|
|
|
|
// =============================================================================
|
|
// Provider Registration
|
|
// =============================================================================
|
|
|
|
registerProvider(contextFileCapability.id, {
|
|
id: PROVIDER_ID,
|
|
displayName: DISPLAY_NAME,
|
|
description: "Load copilot-instructions.md from .github/",
|
|
priority: PRIORITY,
|
|
load: loadContextFiles,
|
|
});
|
|
|
|
registerProvider(instructionCapability.id, {
|
|
id: PROVIDER_ID,
|
|
displayName: DISPLAY_NAME,
|
|
description: "Load *.instructions.md from .github/instructions/ with applyTo frontmatter",
|
|
priority: PRIORITY,
|
|
load: loadInstructions,
|
|
});
|