Spaces:
Running
Running
File size: 4,064 Bytes
ac68ae4 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | import * as p from "@clack/prompts";
import { askConfirm, askText, isPromptBack, pickOne, PROMPT_BACK } from "../lib/prompts.js";
import type { PluginChoice, PluginSource } from "../types.js";
import { listInstalledPlugins, searchPlugins } from "../lib/plugins.js";
interface AskPluginsOptions {
allowBack?: boolean;
backHint?: string;
initial?: PluginChoice[];
/** When set, skip the interactive plugin search UI. */
prechosen?: PluginChoice[];
skip?: boolean;
}
function pluginLabel(plugin: {
name: string;
version: string;
source: PluginSource;
description: string;
}): string {
const sourceTag = plugin.source === "registry" ? "registry" : "local";
return `${plugin.name} (${sourceTag})`;
}
export async function askPlugins(
opts: AskPluginsOptions = {},
): Promise<PluginChoice[] | typeof PROMPT_BACK> {
if (opts.skip || opts.prechosen !== undefined) {
return opts.prechosen ?? [];
}
const selections = new Map<string, PluginChoice>();
for (const plugin of opts.initial ?? []) {
selections.set(`${plugin.source}:${plugin.name}:${plugin.path ?? ""}`, plugin);
}
while (true) {
const query = await askText({
message: "Search plugin by name (or leave empty to continue)",
placeholder: "chatbot",
});
if (!query.trim()) {
if (opts.allowBack && selections.size === 0) {
const shouldGoBack = await pickOne<"continue" | "back">({
message: "No plugins selected. Continue or go back?",
options: [
{ value: "continue", label: "Continue" },
{ value: "back", label: "Back", hint: opts.backHint },
],
initialValue: "continue",
});
if (shouldGoBack === "back") {
return PROMPT_BACK;
}
}
return [...selections.values()];
}
const results = await searchPlugins(query);
if (results.length === 0) {
p.log.warn(`No plugin found for "${query}".`);
} else {
const choice = await pickOne<string>({
message: "Select a plugin to add",
options: results.map((plugin, index) => ({
value: String(index),
label: pluginLabel(plugin),
hint: plugin.description || `${plugin.version}`,
})),
allowBack: true,
backHint: opts.backHint,
});
if (isPromptBack(choice)) {
if (opts.allowBack) {
return PROMPT_BACK;
}
continue;
}
const selected = results[Number(choice)];
if (!selected) {
p.log.warn("Invalid plugin selection.");
continue;
}
const confirmed = await askConfirm({
message: `Add ${selected.name} from ${selected.source}?`,
initialValue: true,
});
if (confirmed) {
const key = `${selected.source}:${selected.name}:${selected.path ?? ""}`;
selections.set(key, {
name: selected.name,
source: selected.source,
version: selected.version,
path: selected.path,
});
p.log.success(`Added ${selected.name}.`);
}
}
const addMore = await askConfirm({
message: "Add another plugin?",
initialValue: true,
});
if (!addMore) {
return [...selections.values()];
}
}
}
export async function askPluginUninstall(
opts: { allowBack?: boolean; backHint?: string } = {},
): Promise<string | typeof PROMPT_BACK | null> {
const installed = await listInstalledPlugins();
if (installed.length === 0) {
p.log.info("No installed plugins found.");
return null;
}
const options = installed.map((plugin) => ({
value: plugin.name,
label: plugin.name,
hint: plugin.version,
}));
const choice = opts.allowBack
? await pickOne<string>({
message: "Select plugin to uninstall",
options,
allowBack: true,
backHint: opts.backHint,
})
: await pickOne<string>({
message: "Select plugin to uninstall",
options,
});
if (isPromptBack(choice)) {
return PROMPT_BACK;
}
return choice;
}
|