feat: plugin system + voice-call

This commit is contained in:
Peter Steinberger
2026-01-12 01:16:39 +00:00
parent a6ea74f8e6
commit 2f4a248314
16 changed files with 614 additions and 58 deletions

View File

@@ -13,6 +13,19 @@ import { CONFIG_DIR, resolveUserPath } from "../utils.js";
const fsp = fs.promises;
const SKILLS_SYNC_QUEUE = new Map<string, Promise<unknown>>();
async function serializeByKey<T>(key: string, task: () => Promise<T>) {
const prev = SKILLS_SYNC_QUEUE.get(key) ?? Promise.resolve();
const next = prev.then(task, task);
SKILLS_SYNC_QUEUE.set(key, next);
try {
return await next;
} finally {
if (SKILLS_SYNC_QUEUE.get(key) === next) SKILLS_SYNC_QUEUE.delete(key);
}
}
export type SkillInstallSpec = {
id?: string;
kind: "brew" | "node" | "go" | "uv";
@@ -649,29 +662,35 @@ export async function syncSkillsToWorkspace(params: {
const sourceDir = resolveUserPath(params.sourceWorkspaceDir);
const targetDir = resolveUserPath(params.targetWorkspaceDir);
if (sourceDir === targetDir) return;
const targetSkillsDir = path.join(targetDir, "skills");
const entries = loadSkillEntries(sourceDir, {
config: params.config,
managedSkillsDir: params.managedSkillsDir,
bundledSkillsDir: params.bundledSkillsDir,
});
await serializeByKey(`syncSkills:${targetDir}`, async () => {
const targetSkillsDir = path.join(targetDir, "skills");
await fsp.rm(targetSkillsDir, { recursive: true, force: true });
await fsp.mkdir(targetSkillsDir, { recursive: true });
const entries = loadSkillEntries(sourceDir, {
config: params.config,
managedSkillsDir: params.managedSkillsDir,
bundledSkillsDir: params.bundledSkillsDir,
});
for (const entry of entries) {
const dest = path.join(targetSkillsDir, entry.skill.name);
try {
await fsp.cp(entry.skill.baseDir, dest, { recursive: true, force: true });
} catch (error) {
const message =
error instanceof Error ? error.message : JSON.stringify(error);
console.warn(
`[skills] Failed to copy ${entry.skill.name} to sandbox: ${message}`,
);
await fsp.rm(targetSkillsDir, { recursive: true, force: true });
await fsp.mkdir(targetSkillsDir, { recursive: true });
for (const entry of entries) {
const dest = path.join(targetSkillsDir, entry.skill.name);
try {
await fsp.cp(entry.skill.baseDir, dest, {
recursive: true,
force: true,
});
} catch (error) {
const message =
error instanceof Error ? error.message : JSON.stringify(error);
console.warn(
`[skills] Failed to copy ${entry.skill.name} to sandbox: ${message}`,
);
}
}
}
});
}
export function filterWorkspaceSkillEntries(