/**
* @pi-unipi/milestone — Command registration
*
* Registers milestone-onboard and milestone-update commands.
* Follows the same pattern as workflow/commands.ts:
* loads SKILL.md content and sends it as a user message.
*/
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
import { UNIPI_PREFIX, MILESTONE_COMMANDS, MILESTONE_DIRS } from "@pi-unipi/core";
import { parseMilestones } from "./milestone.js";
import { readFileSync } from "node:fs";
/** Resolve the skills directory relative to this file */
const SKILLS_DIR = join(dirname(fileURLToPath(import.meta.url)), "skills");
/**
* Load SKILL.md content for a given skill name.
*/
function loadSkill(skillName: string): string {
try {
return readFileSync(join(SKILLS_DIR, skillName, "SKILL.md"), "utf-8");
} catch {
return "";
}
}
/**
* Register milestone commands with the extension API.
*/
export function registerCommands(pi: ExtensionAPI): void {
// milestone-onboard — create milestones from existing work
pi.registerCommand(`${UNIPI_PREFIX}${MILESTONE_COMMANDS.ONBOARD}`, {
description: "Create MILESTONES.md from existing workflow docs — scan, propose, refine, write",
handler: async (args: string, ctx: ExtensionCommandContext) => {
const skillContent = loadSkill("milestone-onboard");
let message = "Execute the milestone-onboard workflow.";
if (args?.trim()) {
message += `\n\nArguments: ${args.trim()}`;
}
if (skillContent) {
message += `\n\n\n${skillContent}\n`;
}
pi.sendUserMessage(message, { deliverAs: "followUp" });
if (ctx.hasUI) {
ctx.ui.notify("Running /unipi:milestone-onboard", "info");
}
},
});
// milestone-update — sync milestones with completed work
pi.registerCommand(`${UNIPI_PREFIX}${MILESTONE_COMMANDS.UPDATE}`, {
description: "Sync MILESTONES.md with completed work — scan docs, diff checkboxes, auto-update",
handler: async (args: string, ctx: ExtensionCommandContext) => {
const skillContent = loadSkill("milestone-update");
let message = "Execute the milestone-update workflow.";
if (args?.trim()) {
message += `\n\nArguments: ${args.trim()}`;
}
if (skillContent) {
message += `\n\n\n${skillContent}\n`;
}
pi.sendUserMessage(message, { deliverAs: "followUp" });
if (ctx.hasUI) {
ctx.ui.notify("Running /unipi:milestone-update", "info");
}
},
getArgumentCompletions: () => {
// Suggest phase names from existing MILESTONES.md
const cwd = process.cwd();
const milestonesPath = join(cwd, MILESTONE_DIRS.MILESTONES);
const doc = parseMilestones(milestonesPath);
const suggestions = doc.phases.map((phase) => ({
value: phase.name,
label: phase.name,
description: `${phase.items.filter((i) => i.checked).length}/${phase.items.length} done`,
}));
// Add "all" option
suggestions.unshift({
value: "all",
label: "all",
description: "Update all phases",
});
return suggestions;
},
});
}