Manage skills as sources over ACP (#8675)

Co-authored-by: Lifei Zhou <lifei@squareup.com>
This commit is contained in:
Jack Amadeo
2026-04-20 23:09:21 -04:00
committed by GitHub
parent 93299b513c
commit b1235e7c01
18 changed files with 1622 additions and 363 deletions
+64 -9
View File
@@ -1,4 +1,4 @@
import { invoke } from "@tauri-apps/api/core";
import { getClient } from "@/shared/api/acpConnection";
export interface SkillInfo {
name: string;
@@ -7,20 +7,54 @@ export interface SkillInfo {
path: string;
}
// Shape returned by _goose/sources/*. Narrowed to skill-type sources here.
interface SourceEntry {
type: "skill";
name: string;
description: string;
content: string;
directory: string;
global: boolean;
}
function toSkillInfo(source: SourceEntry): SkillInfo {
return {
name: source.name,
description: source.description,
instructions: source.content,
path: source.directory,
};
}
export async function createSkill(
name: string,
description: string,
instructions: string,
): Promise<void> {
return invoke("create_skill", { name, description, instructions });
const client = await getClient();
await client.extMethod("_goose/sources/create", {
type: "skill",
name,
description,
content: instructions,
global: true,
});
}
export async function listSkills(): Promise<SkillInfo[]> {
return invoke("list_skills");
const client = await getClient();
const raw = await client.extMethod("_goose/sources/list", { type: "skill" });
const sources = (raw.sources ?? []) as SourceEntry[];
return sources.map(toSkillInfo);
}
export async function deleteSkill(name: string): Promise<void> {
return invoke("delete_skill", { name });
const client = await getClient();
await client.extMethod("_goose/sources/delete", {
type: "skill",
name,
global: true,
});
}
export async function updateSkill(
@@ -28,21 +62,42 @@ export async function updateSkill(
description: string,
instructions: string,
): Promise<SkillInfo> {
return invoke("update_skill", { name, description, instructions });
const client = await getClient();
const raw = await client.extMethod("_goose/sources/update", {
type: "skill",
name,
description,
content: instructions,
global: true,
});
return toSkillInfo(raw.source as SourceEntry);
}
export async function exportSkill(
name: string,
): Promise<{ json: string; filename: string }> {
return invoke("export_skill", { name });
const client = await getClient();
const raw = await client.extMethod("_goose/sources/export", {
type: "skill",
name,
global: true,
});
return { json: raw.json as string, filename: raw.filename as string };
}
export async function importSkills(
fileBytes: number[],
fileName: string,
): Promise<SkillInfo[]> {
return invoke("import_skills", {
fileBytes: Array.from(fileBytes),
fileName,
if (!fileName.endsWith(".skill.json") && !fileName.endsWith(".json")) {
throw new Error("File must have a .skill.json or .json extension");
}
const data = new TextDecoder().decode(new Uint8Array(fileBytes));
const client = await getClient();
const raw = await client.extMethod("_goose/sources/import", {
data,
global: true,
});
const sources = (raw.sources ?? []) as SourceEntry[];
return sources.map(toSkillInfo);
}