From e454277d108e794bb272064f4736fdfcabef7414 Mon Sep 17 00:00:00 2001 From: Chris Farhood Date: Fri, 10 Apr 2026 22:48:08 -0400 Subject: [PATCH] feat(skills): scan button re-scans existing GitHub/sks_sh sources for new skills When "Scan project workspaces for skills" runs, now also iterates all existing GitHub/sks_sh skills and re-fetches their source repos to detect newly added skills. New skills are upserted automatically. Skips sources that fail, logged as warnings. Co-Authored-By: Claude Opus 4.6 --- server/src/services/company-skills.ts | 28 +++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/server/src/services/company-skills.ts b/server/src/services/company-skills.ts index b6c36db4..89c1979e 100644 --- a/server/src/services/company-skills.ts +++ b/server/src/services/company-skills.ts @@ -2016,6 +2016,34 @@ export function companySkillService(db: Db) { } } + // Re-scan GitHub/sks_sh sources to pick up newly added skills + const existingSkills = await listFull(companyId); + const sourceByLocator = new Map(); + for (const skill of existingSkills) { + if (skill.sourceType !== "github" && skill.sourceType !== "skills_sh") continue; + const locator = skill.sourceLocator ?? ""; + if (!locator) continue; + if (!sourceByLocator.has(locator)) sourceByLocator.set(locator, []); + sourceByLocator.get(locator)!.push(skill); + } + for (const [sourceLocator, skillsAtSource] of sourceByLocator) { + try { + const result = await readUrlSkillImports(companyId, sourceLocator, null); + for (const nextSkill of result.skills) { + const existing = skillsAtSource.find((s) => s.slug === nextSkill.slug); + if (!existing) { + // New skill discovered — derive key and upsert + nextSkill.key = deriveCanonicalSkillKey(companyId, nextSkill); + const persisted = (await upsertImportedSkills(companyId, [nextSkill]))[0]; + if (persisted) imported.push(persisted); + } + } + } catch { + // Best-effort: don't fail the whole scan if one source fails + warnings.push(`Could not re-scan source ${sourceLocator} — skipping.`); + } + } + return { scannedProjects: scannedProjectIds.size, scannedWorkspaces: scanTargets.length,