mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-16 19:00:38 +09:00
Add adapter skill sync for codex and claude
This commit is contained in:
parent
271c2b9018
commit
56a34a8f8a
22 changed files with 907 additions and 26 deletions
|
|
@ -12,6 +12,7 @@ import {
|
|||
parseObject,
|
||||
parseJson,
|
||||
buildPaperclipEnv,
|
||||
listPaperclipSkillEntries,
|
||||
joinPromptSections,
|
||||
redactEnvForLogs,
|
||||
ensureAbsoluteDirectory,
|
||||
|
|
@ -27,40 +28,32 @@ import {
|
|||
isClaudeMaxTurnsResult,
|
||||
isClaudeUnknownSessionError,
|
||||
} from "./parse.js";
|
||||
import { resolveClaudeDesiredSkillNames } from "./skills.js";
|
||||
|
||||
const __moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
||||
const PAPERCLIP_SKILLS_CANDIDATES = [
|
||||
path.resolve(__moduleDir, "../../skills"), // published: <pkg>/dist/server/ -> <pkg>/skills/
|
||||
path.resolve(__moduleDir, "../../../../../skills"), // dev: src/server/ -> repo root/skills/
|
||||
];
|
||||
|
||||
async function resolvePaperclipSkillsDir(): Promise<string | null> {
|
||||
for (const candidate of PAPERCLIP_SKILLS_CANDIDATES) {
|
||||
const isDir = await fs.stat(candidate).then((s) => s.isDirectory()).catch(() => false);
|
||||
if (isDir) return candidate;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a tmpdir with `.claude/skills/` containing symlinks to skills from
|
||||
* the repo's `skills/` directory, so `--add-dir` makes Claude Code discover
|
||||
* them as proper registered skills.
|
||||
*/
|
||||
async function buildSkillsDir(): Promise<string> {
|
||||
async function buildSkillsDir(config: Record<string, unknown>): Promise<string> {
|
||||
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-skills-"));
|
||||
const target = path.join(tmp, ".claude", "skills");
|
||||
await fs.mkdir(target, { recursive: true });
|
||||
const skillsDir = await resolvePaperclipSkillsDir();
|
||||
if (!skillsDir) return tmp;
|
||||
const entries = await fs.readdir(skillsDir, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
if (entry.isDirectory()) {
|
||||
await fs.symlink(
|
||||
path.join(skillsDir, entry.name),
|
||||
path.join(target, entry.name),
|
||||
);
|
||||
}
|
||||
const availableEntries = await listPaperclipSkillEntries(__moduleDir);
|
||||
const desiredNames = new Set(
|
||||
resolveClaudeDesiredSkillNames(
|
||||
config,
|
||||
availableEntries.map((entry) => entry.name),
|
||||
),
|
||||
);
|
||||
for (const entry of availableEntries) {
|
||||
if (!desiredNames.has(entry.name)) continue;
|
||||
await fs.symlink(
|
||||
entry.source,
|
||||
path.join(target, entry.name),
|
||||
);
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
|
@ -337,7 +330,7 @@ export async function execute(ctx: AdapterExecutionContext): Promise<AdapterExec
|
|||
extraArgs,
|
||||
} = runtimeConfig;
|
||||
const billingType = resolveClaudeBillingType(env);
|
||||
const skillsDir = await buildSkillsDir();
|
||||
const skillsDir = await buildSkillsDir(config);
|
||||
|
||||
// When instructionsFilePath is configured, create a combined temp file that
|
||||
// includes both the file content and the path directive, so we only need
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue