feat(02-04): update all 5 adapters to use managed skills instead of static directories
- Claude: buildSkillsDir() accepts managedSkills, uses createManagedSkillSymlinks - Codex: ensureCodexSkillsInjected() prefers managed skills over static scan - Gemini: ensureGeminiSkillsInjected() prefers managed skills over static scan - OpenCode: ensureOpenCodeSkillsInjected() prefers managed skills over static scan - Kimi: new skill injection via ensureKimiSkillsInjected() (first-time support) - All adapters fall back to static SKILLS_CANDIDATES when no managed skills provided
This commit is contained in:
1 parent
3fb9ab86d9
commit
14a7f4468e
5 files changed
+101
-17
No files matched your search
@@ -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<string | null> {
|
||||
|
||||
/**
|
||||
* 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<string> {
|
||||
async function buildSkillsDir(
|
||||
managedSkills: ManagedSkillRef[],
|
||||
onLog: (stream: "stdout" | "stderr", chunk: string) => Promise<void>,
|
||||
): Promise<string> {
|
||||
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<AdapterExec
|
||||
extraArgs,
|
||||
} = runtimeConfig;
|
||||
const billingType = resolveClaudeBillingType(env);
|
||||
const skillsDir = await buildSkillsDir();
|
||||
const managedSkills: ManagedSkillRef[] = Array.isArray(config.managedSkills)
|
||||
? (config.managedSkills as ManagedSkillRef[]).filter(
|
||||
(s) => 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
|
||||
|
||||
@@ -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<string | null> {
|
||||
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<AdapterExec
|
||||
const effectiveWorkspaceCwd = useConfiguredInsteadOfAgentHome ? "" : workspaceCwd;
|
||||
const cwd = effectiveWorkspaceCwd || configuredCwd || process.cwd();
|
||||
await ensureAbsoluteDirectory(cwd, { createIfMissing: true });
|
||||
await ensureCodexSkillsInjected(onLog);
|
||||
const managedSkills: ManagedSkillRef[] = Array.isArray(config.managedSkills)
|
||||
? (config.managedSkills as ManagedSkillRef[]).filter(
|
||||
(s) => 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;
|
||||
|
||||
@@ -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<void> {
|
||||
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<AdapterExec
|
||||
const effectiveWorkspaceCwd = useConfiguredInsteadOfAgentHome ? "" : workspaceCwd;
|
||||
const cwd = effectiveWorkspaceCwd || configuredCwd || process.cwd();
|
||||
await ensureAbsoluteDirectory(cwd, { createIfMissing: true });
|
||||
await ensureGeminiSkillsInjected(onLog);
|
||||
const managedSkills: ManagedSkillRef[] = Array.isArray(config.managedSkills)
|
||||
? (config.managedSkills as ManagedSkillRef[]).filter(
|
||||
(s) => typeof s.runtimeName === "string" && typeof s.sourcePath === "string",
|
||||
)
|
||||
: [];
|
||||
await ensureGeminiSkillsInjected(managedSkills, onLog);
|
||||
|
||||
const envConfig = parseObject(config.env);
|
||||
const hasExplicitApiKey =
|
||||
|
||||
@@ -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<void> {
|
||||
if (managedSkills.length === 0) return;
|
||||
await createManagedSkillSymlinks(managedSkills, kimiSkillsHome(), onLog);
|
||||
}
|
||||
|
||||
function normalizeEnv(input: unknown): Record<string, string> {
|
||||
if (typeof input !== "object" || input === null || Array.isArray(input)) return {};
|
||||
const env: Record<string, string> = {};
|
||||
@@ -29,6 +44,12 @@ export async function execute(ctx: AdapterExecutionContext): Promise<AdapterExec
|
||||
const extraArgs = asStringArray(config.extraArgs);
|
||||
|
||||
await ensureAbsoluteDirectory(cwd, { createIfMissing: true });
|
||||
const managedSkills: ManagedSkillRef[] = Array.isArray(config.managedSkills)
|
||||
? (config.managedSkills as ManagedSkillRef[]).filter(
|
||||
(s) => typeof s.runtimeName === "string" && typeof s.sourcePath === "string",
|
||||
)
|
||||
: [];
|
||||
await ensureKimiSkillsInjected(managedSkills, onLog);
|
||||
|
||||
const prompt = typeof context.taskTitle === "string" && context.taskTitle.length > 0
|
||||
? [
|
||||
|
||||
@@ -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<string | null> {
|
||||
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<AdapterExec
|
||||
const effectiveWorkspaceCwd = useConfiguredInsteadOfAgentHome ? "" : workspaceCwd;
|
||||
const cwd = effectiveWorkspaceCwd || configuredCwd || process.cwd();
|
||||
await ensureAbsoluteDirectory(cwd, { createIfMissing: true });
|
||||
await ensureOpenCodeSkillsInjected(onLog);
|
||||
const managedSkills: ManagedSkillRef[] = Array.isArray(config.managedSkills)
|
||||
? (config.managedSkills as ManagedSkillRef[]).filter(
|
||||
(s) => typeof s.runtimeName === "string" && typeof s.sourcePath === "string",
|
||||
)
|
||||
: [];
|
||||
await ensureOpenCodeSkillsInjected(managedSkills, onLog);
|
||||
|
||||
const envConfig = parseObject(config.env);
|
||||
const hasExplicitApiKey =
|
||||
|
||||
Reference in new issue
Block a user