|
|
@@ -2,8 +2,9 @@
|
|
|
// agent-sync — installs the bundled skills and agent prompts into the canonical
|
|
|
// user-level directory (~/.agents) and links the skills into every detected AI
|
|
|
// agent's own directory (same architecture as the vercel-labs/skills CLI).
|
|
|
-// Agent prompts are managed only inside ~/.agents/agents via plain copies
|
|
|
-// (file links on windows require privileges).
|
|
|
+// Agent prompts are copied to ~/.agents/agents (file links on windows require
|
|
|
+// privileges) and, for claude, inlined into the existing wrappers below their
|
|
|
+// frontmatter.
|
|
|
// Selection is explicit (--all, --skills, --agents, --targets): without a
|
|
|
// selection flag the tool prints its help. See HELP for the full surface.
|
|
|
|
|
|
@@ -52,6 +53,9 @@ global system prompt of every detected agent, renamed to each target's
|
|
|
standard filename (CLAUDE.md, APPEND_SYSTEM.md, AGENTS.md, global_rules.md).
|
|
|
It is always copied, never linked, and never stored in ~/.agents.
|
|
|
|
|
|
+The --agents dimension also inlines each prompt body into the existing Claude
|
|
|
+Code wrappers (~/.claude/agents/<name>.md), keeping their frontmatter.
|
|
|
+
|
|
|
--check dry-run, reports drift without writing anything (exit 1 on drift)
|
|
|
|
|
|
Exit codes: ${EXIT_OK} ok or help, ${EXIT_FAILURE} drift (dry-run) or failure, ${EXIT_USAGE} usage error.`;
|
|
|
@@ -71,13 +75,15 @@ function homeDir(dir) {
|
|
|
// are linked only where the convention is verified (claude); pi gets a minimal
|
|
|
// manifest instead (see writePiManifests) because its manifests carry
|
|
|
// per-machine config such as model and thinking.
|
|
|
+// mergeBody: existing wrappers under agentsSub keep their frontmatter and get
|
|
|
+// the bundled prompt body inlined.
|
|
|
// sys is the --sys destination relative to base(): the bundled SYSTEM_PROMPT.md
|
|
|
// is copied there (renamed, plain copy, never linked). null = no file-based
|
|
|
// global prompt (cursor keeps its user rules only in the app settings).
|
|
|
// opencode and windsurf read their config outside the install dir, hence the
|
|
|
// ~/.config/opencode and ~/.codeium/windsurf bases.
|
|
|
const AGENT_REGISTRY = [
|
|
|
- { name: 'claude', base: claudeBase, skillsSub: 'skills', agentsSub: 'agents', sys: { file: 'CLAUDE.md' } },
|
|
|
+ { name: 'claude', base: claudeBase, skillsSub: 'skills', agentsSub: 'agents', mergeBody: true, sys: { file: 'CLAUDE.md' } },
|
|
|
{ name: 'pi', base: homeDir(join('.pi', 'agent')), skillsSub: 'skills', agentsSub: 'agents', sys: { file: 'APPEND_SYSTEM.md' } },
|
|
|
{ name: 'codex', base: homeDir('.codex'), skillsSub: 'skills', sys: { file: 'AGENTS.md' } },
|
|
|
{ name: 'opencode', base: homeDir(join('.config', 'opencode')), skillsSub: 'skills', sys: { file: 'AGENTS.md' } },
|
|
|
@@ -310,7 +316,8 @@ function ensureLink(linkPath, targetPath) {
|
|
|
// Links the selected skills (source package names only, so third-party
|
|
|
// content in the canonical directory is never propagated) into one detected
|
|
|
// agent's directories. Agent prompts are deliberately not linked here: they
|
|
|
-// are managed only inside the canonical ~/.agents/agents directory.
|
|
|
+// are copied to the canonical ~/.agents/agents directory and, for claude,
|
|
|
+// inlined into the existing wrappers below their frontmatter (see mergeAgentBodies).
|
|
|
function linkForAgent(agent, skills) {
|
|
|
const actions = [];
|
|
|
for (const name of skills) {
|
|
|
@@ -322,6 +329,32 @@ function linkForAgent(agent, skills) {
|
|
|
return actions;
|
|
|
}
|
|
|
|
|
|
+// Inlines each prompt body into the existing wrapper below its frontmatter (mergeBody agents only).
|
|
|
+function mergeAgentBodies(agent, agentFiles) {
|
|
|
+ if (!agent.mergeBody) return [];
|
|
|
+ const actions = [];
|
|
|
+ for (const file of agentFiles) {
|
|
|
+ const dest = join(agent.baseDir, agent.agentsSub, file);
|
|
|
+ const label = `${agent.name}:agents/${file}`;
|
|
|
+ // Wrappers carry per-machine config (model, hooks), so they are never created.
|
|
|
+ if (!existsSync(dest)) {
|
|
|
+ actions.push({ label, status: 'skipped' });
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ const current = readFileSync(dest, 'utf8');
|
|
|
+ const match = current.match(/^---\r?\n[\s\S]*?\r?\n---[ \t]*(?:\r?\n|$)/);
|
|
|
+ if (!match) {
|
|
|
+ actions.push({ label, status: 'skipped' });
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ const merged = `${match[0].trimEnd()}\n\n${readFileSync(join(SOURCE_AGENTS_DIR, file), 'utf8').trim()}\n`;
|
|
|
+ const status = merged === current ? 'up-to-date' : 'merged';
|
|
|
+ if (status === 'merged' && !DRY_RUN) writeFileSync(dest, merged);
|
|
|
+ actions.push({ label, status });
|
|
|
+ }
|
|
|
+ return actions;
|
|
|
+}
|
|
|
+
|
|
|
// Copies the bundled SYSTEM_PROMPT.md into every detected agent that supports
|
|
|
// a global system prompt (sys mapping), renamed to its standard filename and
|
|
|
// always as a plain copy (never linked, never through ~/.agents).
|
|
|
@@ -383,6 +416,7 @@ function main() {
|
|
|
}
|
|
|
for (const agent of detected) {
|
|
|
report.push(...linkForAgent(agent, skills));
|
|
|
+ report.push(...mergeAgentBodies(agent, agentFiles));
|
|
|
}
|
|
|
|
|
|
for (const { label, status } of report) {
|