Read skills from the global and workspace .agents/skills directories

- Discover skill directories under ~/.agents/skills and each
  workspace''s .agents/skills; a directory qualifies with a full
  skill.manifest.json or just a SKILL.md, whose manifest is derived
  (frontmatter name or the directory name). Paths resolve per platform
  on Windows, macOS, and Linux.
- Sync at startup for every organization and again when the Skills
  page opens; skills the org already carries are skipped, and one
  broken directory cannot block the rest.
- AGENTCOMPANY_NEW_SKILLS_ROOT points tests at a temporary library
  (overriding USERPROFILE crashes Electron on Windows).
This commit is contained in:
ZacharyZhang-NY committed 2026-07-07 01:21:33 -04:00
1 parent 256e0680c6
commit 2d43950123
14 files changed
+303 -12

No files matched your search

+22
View File
@@ -1,9 +1,12 @@
import { app } from "electron";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import type { DatabaseSync } from "node:sqlite";
import { DatabaseConnection } from "./database/connection";
import { createMainWindow } from "./app/create-window";
import { registerIpcHandlers } from "./app/ipc-handlers";
import { syncSkillsFromDisk } from "./services/skill-discovery";
import { SkillService } from "./services/skill-service";
import { startProviderBackgroundWorker } from "./app/provider-background-worker";
import { recoverInterruptedWork } from "./app/startup-recovery";
import { startTaskQueueWorker } from "./app/task-queue-worker";
@@ -30,6 +33,7 @@ if (!app.requestSingleInstanceLock()) {
mainWindow: win,
artifactRoot: join(profileDir, "artifacts"),
});
syncAllOrganizationSkills(connection.db, join(profileDir, "artifacts"));
const stopProviderBackgroundWorker = startProviderBackgroundWorker({
db: connection.db,
mainWindow: win,
@@ -55,3 +59,21 @@ if (!app.requestSingleInstanceLock()) {
if (process.platform !== "darwin") app.quit();
});
}
/**
* Loads the on-disk skill library for every organization at startup. One
* organization's failure (a broken workspace path, say) must not block the
* rest — or the app.
*/
function syncAllOrganizationSkills(db: DatabaseSync, artifactRoot: string): void {
const skills = new SkillService(db, artifactRoot);
const orgs = db.prepare("select id from organizations").all() as Array<{ id: string }>;
for (const org of orgs) {
try {
syncSkillsFromDisk(db, skills, org.id);
} catch {
// Broken directories are reported per entry inside the sync; anything
// beyond that is a per-org environment fault that must not stop startup.
}
}
}