Ship skills built-in: bundle the catalogue, assign per hire, force humanizer
Skills no longer come from the user's ~/.agents library - most users have none, and the CEO branch that compiled the entire catalogue into its prompt overflowed the model context (1.1MB) and failed every CEO run. - Bundle "built-in skills/" with the app (extraResources) and stage it at startup into <profile>/builtin-skills as one dir-per-skill copy - both bundle shapes (SKILL.md dirs and flat category .md files) normalize into the same importable layout - Import the staged catalogue per org at startup and inside onboarding, so the CEO's first run already carries it - Forced house style: resolveRuntimeSnapshot unions the humanizer skill into every agent's runtime (CEO included) as a resolution rule - no binding row to forget, delete, or bypass; revoke_skill refuses it - CEO staffs hires with skills: hire_agent accepts a skills array, grant_skill/revoke_skill adjust later, list_skills now shows descriptions from skill frontmatter - Kill the c_suite compile-all branch; everyone resolves bindings-only, capped at 200k chars with omissions named in the prompt - Remove the Skills settings page and its import/sync/test IPC surface; drop ~/.agents discovery and the AGENTCOMPANY_NEW_SKILLS_ROOT seam in favor of AGENTCOMPANY_NEW_BUILTIN_SKILLS_DIR - Tests: staging/import/forced-resolution/cap units, real-bundle CI check, e2e asserts the tab is gone and the catalogue self-imports
This commit is contained in:
1 parent
55c043f8b4
commit
86aa6fdb29
318 files changed
+77467
-520
No files matched your search
+34
-14
@@ -5,7 +5,7 @@ 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 { builtinSkillsSourceOverride, importStagedSkills, stageBuiltinSkillsLibrary } from "./services/builtin-skills";
|
||||
import { SkillService } from "./services/skill-service";
|
||||
import { startProviderBackgroundWorker } from "./app/provider-background-worker";
|
||||
import { recoverInterruptedWork } from "./app/startup-recovery";
|
||||
@@ -35,12 +35,16 @@ if (!app.requestSingleInstanceLock()) {
|
||||
language,
|
||||
});
|
||||
|
||||
const builtinSkillsSource = resolveBuiltinSkillsSource();
|
||||
const builtinSkillsLibrary = join(profileDir, "builtin-skills");
|
||||
registerIpcHandlers({
|
||||
connection,
|
||||
mainWindow: win,
|
||||
artifactRoot: join(profileDir, "artifacts"),
|
||||
builtinSkillsSource,
|
||||
builtinSkillsLibrary,
|
||||
});
|
||||
syncAllOrganizationSkills(connection.db, join(profileDir, "artifacts"));
|
||||
syncAllOrganizationSkills(connection, join(profileDir, "artifacts"), builtinSkillsSource, builtinSkillsLibrary);
|
||||
const stopProviderBackgroundWorker = startProviderBackgroundWorker({
|
||||
db: connection.db,
|
||||
mainWindow: win,
|
||||
@@ -73,19 +77,35 @@ function storedLanguage(db: DatabaseSync): string {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* The bundled skill catalogue: the repo folder during development, the
|
||||
* extraResources copy in a packaged build, or an explicit override for
|
||||
* hermetic tests (same seam pattern as AGENTCOMPANY_NEW_PROFILE_DIR).
|
||||
*/
|
||||
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.
|
||||
function resolveBuiltinSkillsSource(): string {
|
||||
const override = builtinSkillsSourceOverride();
|
||||
if (override) return override;
|
||||
if (app.isPackaged) return join(process.resourcesPath, "built-in-skills");
|
||||
return join(app.getAppPath(), "built-in skills");
|
||||
}
|
||||
|
||||
/**
|
||||
* Mirrors the bundled skill catalogue into the profile library and imports it
|
||||
* for every organization at startup. Per-skill failures are reported inside
|
||||
* the sync; a broken bundle is loud in the log but must not stop the app.
|
||||
*/
|
||||
function syncAllOrganizationSkills(connection: DatabaseConnection, artifactRoot: string, sourceRoot: string, libraryRoot: string): void {
|
||||
try {
|
||||
const staged = stageBuiltinSkillsLibrary(sourceRoot, libraryRoot);
|
||||
const skills = new SkillService(connection.db, artifactRoot);
|
||||
const orgs = connection.db.prepare("select id from organizations").all() as Array<{ id: string }>;
|
||||
for (const org of orgs) {
|
||||
const report = connection.transaction(() => importStagedSkills(skills, org.id, staged));
|
||||
if (report.imported > 0 || report.failures.length > 0) {
|
||||
console.info(`[skills] org ${org.id}: imported ${report.imported}, skipped ${report.skipped}, failures ${report.failures.length}`);
|
||||
for (const failure of report.failures.slice(0, 5)) console.warn(`[skills] ${failure}`);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`[skills] built-in skill sync failed: ${error instanceof Error ? error.message : String(error)}`);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user