Parent directory

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}