command.ts
2537 bytes
1import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
2import type { SkillInventory } from "./inventory/loader.ts";
3import type { SkillTogglePlanner } from "./apply/planner.ts";
4import type { SkillChangeWriter } from "./apply/writer.ts";
5import { showSkillToggleUi } from "./ui/overlay.ts";
6import type { ApplyResult } from "./types.ts";
7
8export interface ToggleSkillsCommandDeps {
9 inventory: SkillInventory;
10 planner: SkillTogglePlanner;
11 writer: SkillChangeWriter;
12}
13
14export async function runToggleSkillsCommand(ctx: ExtensionCommandContext, deps: ToggleSkillsCommandDeps): Promise<void> {
15 if (!ctx.hasUI) {
16 ctx.ui.notify("/toggle-skills requires interactive mode", "error");
17 return;
18 }
19
20 let skills;
21 try {
22 skills = await deps.inventory.load(ctx.cwd);
23 } catch (error) {
24 ctx.ui.notify(`Pi Skill Toggle failed to scan skills: ${error instanceof Error ? error.message : String(error)}`, "error");
25 return;
26 }
27
28 if (skills.length === 0) {
29 ctx.ui.notify("Pi Skill Toggle: no skills found in global, user, or project skill directories", "info");
30 return;
31 }
32
33 const result = await showSkillToggleUi(ctx, skills);
34 if (result.action !== "apply") return;
35
36 let changes;
37 try {
38 changes = await deps.planner.plan(skills, result.drafts);
39 } catch (error) {
40 ctx.ui.notify(`Pi Skill Toggle failed to plan changes: ${error instanceof Error ? error.message : String(error)}`, "error");
41 return;
42 }
43
44 if (changes.length === 0) {
45 ctx.ui.notify("Pi Skill Toggle: no changes to apply", "info");
46 return;
47 }
48
49 const applied = await deps.writer.apply(changes);
50 ctx.ui.notify(formatApplyResult(applied), applied.errors.length > 0 ? "warning" : "info");
51
52 if (applied.applied.length > 0) {
53 await ctx.reload();
54 }
55}
56
57function formatApplyResult(result: ApplyResult): string {
58 const lines = [`Pi Skill Toggle applied ${result.applied.length} change${result.applied.length === 1 ? "" : "s"}.`];
59 for (const change of result.applied.slice(0, 6)) {
60 lines.push(`- ${change.skill.name}: ${change.from} → ${change.to}`);
61 }
62 if (result.applied.length > 6) {
63 lines.push(`- … ${result.applied.length - 6} more`);
64 }
65 if (result.errors.length > 0) {
66 lines.push(`Errors/skipped: ${result.errors.length}`);
67 for (const error of result.errors.slice(0, 4)) {
68 lines.push(`- ${error.message}`);
69 }
70 }
71 if (result.applied.length > 0) {
72 lines.push("Reloaded skills, prompts, extensions, and themes.");
73 }
74 return lines.join("\n");
75}