forked from farhoodlabs/paperclip
fix(skills): auto-detach agents when pruned skill is in use during repo sync
During a repo sync (scan button), if a skill was removed from the source and is still assigned to agents, the skill is now automatically detached from those agents before deletion, rather than leaving it attached. Manual delete-by-source is unchanged (still blocks if in use). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -294,7 +294,7 @@ describe("company skill mutation permissions", () => {
|
||||
skipped: [],
|
||||
conflicts: [],
|
||||
warnings: [
|
||||
'Skill "ghost-skill" was removed from https://github.com/vercel-labs/agent-browser but is still used by Builder. It will not be automatically removed.',
|
||||
'Skill "ghost-skill" was removed from https://github.com/vercel-labs/agent-browser and detached from Builder.',
|
||||
],
|
||||
});
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { fileURLToPath } from "node:url";
|
||||
import { and, asc, eq } from "drizzle-orm";
|
||||
import type { Db } from "@paperclipai/db";
|
||||
import { companySkills } from "@paperclipai/db";
|
||||
import { readPaperclipSkillSyncPreference } from "@paperclipai/adapter-utils/server-utils";
|
||||
import { readPaperclipSkillSyncPreference, writePaperclipSkillSyncPreference } from "@paperclipai/adapter-utils/server-utils";
|
||||
import type { PaperclipSkillEntry } from "@paperclipai/adapter-utils/server-utils";
|
||||
import type {
|
||||
CompanySkill,
|
||||
@@ -2044,12 +2044,23 @@ export function companySkillService(db: Db) {
|
||||
if (currentSlugs.has(skill.slug)) continue;
|
||||
const usedByAgents = await usage(companyId, skill.key);
|
||||
if (usedByAgents.length > 0) {
|
||||
// Detach the skill from all agents that have it, then delete
|
||||
for (const agent of usedByAgents) {
|
||||
const currentConfig = (agent.adapterConfig ?? {}) as Record<string, unknown>;
|
||||
const preference = readPaperclipSkillSyncPreference(currentConfig);
|
||||
if (preference.desiredSkills.includes(skill.key)) {
|
||||
const updatedConfig = writePaperclipSkillSyncPreference(
|
||||
currentConfig,
|
||||
preference.desiredSkills.filter((k) => k !== skill.key),
|
||||
);
|
||||
await agents.updateAgent(agent.id, { adapterConfig: updatedConfig });
|
||||
}
|
||||
}
|
||||
warnings.push(
|
||||
`Skill "${skill.slug}" was removed from ${sourceLocator} but is still used by ${usedByAgents.map((a) => a.name).join(", ")}. It will not be automatically removed.`,
|
||||
`Skill "${skill.slug}" was removed from ${sourceLocator} and detached from ${usedByAgents.map((a) => a.name).join(", ")}.`,
|
||||
);
|
||||
} else {
|
||||
await deleteSkill(companyId, skill.id);
|
||||
}
|
||||
await deleteSkill(companyId, skill.id);
|
||||
}
|
||||
} catch {
|
||||
// Best-effort: don't fail the whole scan if one source fails
|
||||
|
||||
Reference in New Issue
Block a user