diff --git a/packages/adapter-claude-local/src/server/execute.ts b/packages/adapter-claude-local/src/server/execute.ts index 5f89fb4b..1e5fe2db 100644 --- a/packages/adapter-claude-local/src/server/execute.ts +++ b/packages/adapter-claude-local/src/server/execute.ts @@ -4,6 +4,7 @@ import path from "node:path"; import { fileURLToPath } from "node:url"; import type { AdapterExecutionContext, AdapterExecutionResult } from "@agentcompany/adapter-utils"; import type { RunProcessResult } from "@agentcompany/adapter-utils/server-utils"; +import type { ManagedSkillRef } from "@agentcompany/adapter-utils"; import { asString, asNumber, @@ -19,6 +20,7 @@ import { filterConflictingArgs, renderTemplate, runChildProcess, + createManagedSkillSymlinks, } from "@agentcompany/adapter-utils/server-utils"; import { parseClaudeStreamJson, @@ -44,13 +46,24 @@ async function resolveAgentCompanySkillsDir(): Promise { /** * Create a tmpdir with `.claude/skills/` containing symlinks to skills from - * the repo's `skills/` directory, so `--add-dir` makes Claude Code discover + * managed skill entries (preferred) or the repo's static `skills/` directory + * (backward compatibility fallback), so `--add-dir` makes Claude Code discover * them as proper registered skills. */ -async function buildSkillsDir(): Promise { +async function buildSkillsDir( + managedSkills: ManagedSkillRef[], + onLog: (stream: "stdout" | "stderr", chunk: string) => Promise, +): Promise { const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "agent-company-skills-")); const target = path.join(tmp, ".claude", "skills"); await fs.mkdir(target, { recursive: true }); + + // Use managed skills when available; fall back to static repo scan + if (managedSkills.length > 0) { + await createManagedSkillSymlinks(managedSkills, target, onLog); + return tmp; + } + const skillsDir = await resolveAgentCompanySkillsDir(); if (!skillsDir) return tmp; const entries = await fs.readdir(skillsDir, { withFileTypes: true }); @@ -337,7 +350,12 @@ export async function execute(ctx: AdapterExecutionContext): Promise typeof s.runtimeName === "string" && typeof s.sourcePath === "string", + ) + : []; + const skillsDir = await buildSkillsDir(managedSkills, onLog); // When instructionsFilePath is configured, create a combined temp file that // includes both the file content and the path directive, so we only need diff --git a/packages/adapter-codex-local/src/server/execute.ts b/packages/adapter-codex-local/src/server/execute.ts index 36e535a3..2c99cef8 100644 --- a/packages/adapter-codex-local/src/server/execute.ts +++ b/packages/adapter-codex-local/src/server/execute.ts @@ -2,7 +2,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { fileURLToPath } from "node:url"; -import type { AdapterExecutionContext, AdapterExecutionResult } from "@agentcompany/adapter-utils"; +import type { AdapterExecutionContext, AdapterExecutionResult, ManagedSkillRef } from "@agentcompany/adapter-utils"; import { asString, asNumber, @@ -17,6 +17,7 @@ import { filterConflictingArgs, renderTemplate, runChildProcess, + createManagedSkillSymlinks, } from "@agentcompany/adapter-utils/server-utils"; import { parseCodexJsonl, isCodexUnknownSessionError } from "./parse.js"; @@ -76,11 +77,21 @@ async function resolveAgentCompanySkillsDir(): Promise { return null; } -async function ensureCodexSkillsInjected(onLog: AdapterExecutionContext["onLog"]) { +async function ensureCodexSkillsInjected( + managedSkills: ManagedSkillRef[], + onLog: AdapterExecutionContext["onLog"], +) { + const skillsHome = path.join(codexHomeDir(), "skills"); + + // Use managed skills when available; fall back to static repo scan + if (managedSkills.length > 0) { + await createManagedSkillSymlinks(managedSkills, skillsHome, onLog); + return; + } + const skillsDir = await resolveAgentCompanySkillsDir(); if (!skillsDir) return; - const skillsHome = path.join(codexHomeDir(), "skills"); await fs.mkdir(skillsHome, { recursive: true }); const entries = await fs.readdir(skillsDir, { withFileTypes: true }); for (const entry of entries) { @@ -154,7 +165,12 @@ export async function execute(ctx: AdapterExecutionContext): Promise typeof s.runtimeName === "string" && typeof s.sourcePath === "string", + ) + : []; + await ensureCodexSkillsInjected(managedSkills, onLog); const envConfig = parseObject(config.env); const hasExplicitApiKey = typeof envConfig.AGENT_COMPANY_API_KEY === "string" && envConfig.AGENT_COMPANY_API_KEY.trim().length > 0; diff --git a/packages/adapter-gemini-local/src/server/execute.ts b/packages/adapter-gemini-local/src/server/execute.ts index a30add9c..381e0241 100644 --- a/packages/adapter-gemini-local/src/server/execute.ts +++ b/packages/adapter-gemini-local/src/server/execute.ts @@ -3,7 +3,7 @@ import type { Dirent } from "node:fs"; import os from "node:os"; import path from "node:path"; import { fileURLToPath } from "node:url"; -import type { AdapterExecutionContext, AdapterExecutionResult } from "@agentcompany/adapter-utils"; +import type { AdapterExecutionContext, AdapterExecutionResult, ManagedSkillRef } from "@agentcompany/adapter-utils"; import { asBoolean, asNumber, @@ -18,6 +18,7 @@ import { redactEnvForLogs, renderTemplate, runChildProcess, + createManagedSkillSymlinks, } from "@agentcompany/adapter-utils/server-utils"; import { DEFAULT_GEMINI_LOCAL_MODEL } from "../index.js"; import { @@ -88,16 +89,23 @@ function geminiSkillsHome(): string { /** * Inject AgentCompany skills directly into `~/.gemini/skills/` via symlinks. - * This avoids needing GEMINI_CLI_HOME overrides, so the CLI naturally finds - * both its auth credentials and the injected skills in the real home directory. + * Uses managed skills when available; falls back to static repo scan. */ async function ensureGeminiSkillsInjected( + managedSkills: ManagedSkillRef[], onLog: AdapterExecutionContext["onLog"], ): Promise { + const skillsHome = geminiSkillsHome(); + + // Use managed skills when available; fall back to static repo scan + if (managedSkills.length > 0) { + await createManagedSkillSymlinks(managedSkills, skillsHome, onLog); + return; + } + const skillsDir = await resolveAgentCompanySkillsDir(); if (!skillsDir) return; - const skillsHome = geminiSkillsHome(); try { await fs.mkdir(skillsHome, { recursive: true }); } catch (err) { @@ -165,7 +173,12 @@ export async function execute(ctx: AdapterExecutionContext): Promise typeof s.runtimeName === "string" && typeof s.sourcePath === "string", + ) + : []; + await ensureGeminiSkillsInjected(managedSkills, onLog); const envConfig = parseObject(config.env); const hasExplicitApiKey = diff --git a/packages/adapter-kimi-local/src/server/execute.ts b/packages/adapter-kimi-local/src/server/execute.ts index 89cf71c8..e665ed6d 100644 --- a/packages/adapter-kimi-local/src/server/execute.ts +++ b/packages/adapter-kimi-local/src/server/execute.ts @@ -1,4 +1,6 @@ -import type { AdapterExecutionContext, AdapterExecutionResult } from "@agentcompany/adapter-utils"; +import os from "node:os"; +import path from "node:path"; +import type { AdapterExecutionContext, AdapterExecutionResult, ManagedSkillRef } from "@agentcompany/adapter-utils"; import { asNumber, asString, @@ -7,9 +9,22 @@ import { ensureCommandResolvable, ensurePathInEnv, runChildProcess, + createManagedSkillSymlinks, } from "@agentcompany/adapter-utils/server-utils"; import { DEFAULT_KIMI_LOCAL_MODEL } from "../index.js"; +function kimiSkillsHome(): string { + return path.join(os.homedir(), ".kimi", "skills"); +} + +async function ensureKimiSkillsInjected( + managedSkills: ManagedSkillRef[], + onLog: AdapterExecutionContext["onLog"], +): Promise { + if (managedSkills.length === 0) return; + await createManagedSkillSymlinks(managedSkills, kimiSkillsHome(), onLog); +} + function normalizeEnv(input: unknown): Record { if (typeof input !== "object" || input === null || Array.isArray(input)) return {}; const env: Record = {}; @@ -29,6 +44,12 @@ export async function execute(ctx: AdapterExecutionContext): Promise typeof s.runtimeName === "string" && typeof s.sourcePath === "string", + ) + : []; + await ensureKimiSkillsInjected(managedSkills, onLog); const prompt = typeof context.taskTitle === "string" && context.taskTitle.length > 0 ? [ diff --git a/packages/adapter-opencode-local/src/server/execute.ts b/packages/adapter-opencode-local/src/server/execute.ts index 1e19718a..68823dd4 100644 --- a/packages/adapter-opencode-local/src/server/execute.ts +++ b/packages/adapter-opencode-local/src/server/execute.ts @@ -2,7 +2,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { fileURLToPath } from "node:url"; -import type { AdapterExecutionContext, AdapterExecutionResult } from "@agentcompany/adapter-utils"; +import type { AdapterExecutionContext, AdapterExecutionResult, ManagedSkillRef } from "@agentcompany/adapter-utils"; import { asString, asNumber, @@ -15,6 +15,7 @@ import { ensurePathInEnv, renderTemplate, runChildProcess, + createManagedSkillSymlinks, } from "@agentcompany/adapter-utils/server-utils"; import { isOpenCodeUnknownSessionError, parseOpenCodeJsonl } from "./parse.js"; import { ensureOpenCodeModelConfiguredAndAvailable } from "./models.js"; @@ -53,11 +54,21 @@ async function resolveAgentCompanySkillsDir(): Promise { return null; } -async function ensureOpenCodeSkillsInjected(onLog: AdapterExecutionContext["onLog"]) { +async function ensureOpenCodeSkillsInjected( + managedSkills: ManagedSkillRef[], + onLog: AdapterExecutionContext["onLog"], +) { + const skillsHome = openCodeSkillsHome(); + + // Use managed skills when available; fall back to static repo scan + if (managedSkills.length > 0) { + await createManagedSkillSymlinks(managedSkills, skillsHome, onLog); + return; + } + const skillsDir = await resolveAgentCompanySkillsDir(); if (!skillsDir) return; - const skillsHome = openCodeSkillsHome(); await fs.mkdir(skillsHome, { recursive: true }); const entries = await fs.readdir(skillsDir, { withFileTypes: true }); for (const entry of entries) { @@ -109,7 +120,12 @@ export async function execute(ctx: AdapterExecutionContext): Promise typeof s.runtimeName === "string" && typeof s.sourcePath === "string", + ) + : []; + await ensureOpenCodeSkillsInjected(managedSkills, onLog); const envConfig = parseObject(config.env); const hasExplicitApiKey =