|
@@ -30,11 +30,6 @@ const SOURCE_SYSTEM_PROMPT = join(PACKAGE_ROOT, 'SYSTEM_PROMPT.md');
|
|
|
const CANONICAL_SKILLS_DIR = join(homedir(), '.agents', 'skills');
|
|
const CANONICAL_SKILLS_DIR = join(homedir(), '.agents', 'skills');
|
|
|
const CANONICAL_AGENTS_DIR = join(homedir(), '.agents', 'agents');
|
|
const CANONICAL_AGENTS_DIR = join(homedir(), '.agents', 'agents');
|
|
|
|
|
|
|
|
-// Frontmatter prepended to SYSTEM_PROMPT.md for rule-based agents that require
|
|
|
|
|
-// an always-on wrapper (cursor .mdc, windsurf rules).
|
|
|
|
|
-const SYS_CURSOR_FRONTMATTER = '---\ndescription: oh-my-skills system prompt\nalwaysApply: true\n---\n\n';
|
|
|
|
|
-const SYS_WINDSURF_FRONTMATTER = '---\ntrigger: always_on\n---\n\n';
|
|
|
|
|
-
|
|
|
|
|
const HELP = `agent-sync — installs bundled skills and agent prompts into the
|
|
const HELP = `agent-sync — installs bundled skills and agent prompts into the
|
|
|
canonical ~/.agents directory and links them into the AI agents detected
|
|
canonical ~/.agents directory and links them into the AI agents detected
|
|
|
on the machine.
|
|
on the machine.
|
|
@@ -54,7 +49,7 @@ selection flag agent-sync prints this help.
|
|
|
|
|
|
|
|
The --sys dimension installs the repository-level SYSTEM_PROMPT.md as the
|
|
The --sys dimension installs the repository-level SYSTEM_PROMPT.md as the
|
|
|
global system prompt of every detected agent, renamed to each target's
|
|
global system prompt of every detected agent, renamed to each target's
|
|
|
-standard filename (CLAUDE.md, APPEND_SYSTEM.md, AGENTS.md, *.mdc, ...).
|
|
|
|
|
|
|
+standard filename (CLAUDE.md, APPEND_SYSTEM.md, AGENTS.md, global_rules.md).
|
|
|
It is always copied, never linked, and never stored in ~/.agents.
|
|
It is always copied, never linked, and never stored in ~/.agents.
|
|
|
|
|
|
|
|
--check dry-run, reports drift without writing anything (exit 1 on drift)
|
|
--check dry-run, reports drift without writing anything (exit 1 on drift)
|
|
@@ -77,17 +72,19 @@ function homeDir(dir) {
|
|
|
// manifest instead (see writePiManifests) because its manifests carry
|
|
// manifest instead (see writePiManifests) because its manifests carry
|
|
|
// per-machine config such as model and thinking.
|
|
// per-machine config such as model and thinking.
|
|
|
// sys is the --sys destination relative to base(): the bundled SYSTEM_PROMPT.md
|
|
// sys is the --sys destination relative to base(): the bundled SYSTEM_PROMPT.md
|
|
|
-// is copied there (renamed, plain copy, never linked) or wrapped with a
|
|
|
|
|
-// frontmatter when the target expects one. null = no system prompt support.
|
|
|
|
|
|
|
+// 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 = [
|
|
const AGENT_REGISTRY = [
|
|
|
- { name: 'claude', base: claudeBase, skillsSub: 'skills', agentsSub: 'agents', 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('.opencode'), skillsSub: 'skills', sys: { file: 'AGENTS.md' } },
|
|
|
|
|
- { name: 'cursor', base: homeDir('.cursor'), skillsSub: 'skills', sys: { file: join('rules', 'oh-my-skills.mdc'), frontmatter: SYS_CURSOR_FRONTMATTER } },
|
|
|
|
|
- { name: 'trae', base: homeDir('.trae'), skillsSub: 'skills', sys: null },
|
|
|
|
|
- { name: 'windsurf', base: homeDir('.windsurf'), skillsSub: 'skills', sys: { file: join('rules', 'oh-my-skills.md'), frontmatter: SYS_WINDSURF_FRONTMATTER } },
|
|
|
|
|
- { name: 'aider-desk', base: homeDir('.aider-desk'), skillsSub: 'skills', sys: null },
|
|
|
|
|
|
|
+ { name: 'claude', base: claudeBase, skillsSub: 'skills', agentsSub: 'agents', 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' } },
|
|
|
|
|
+ { name: 'cursor', base: homeDir('.cursor'), skillsSub: 'skills', sys: null },
|
|
|
|
|
+ { name: 'trae', base: homeDir('.trae'), skillsSub: 'skills', sys: null },
|
|
|
|
|
+ { name: 'windsurf', base: homeDir(join('.codeium', 'windsurf')), skillsSub: 'skills', sys: { file: join('memories', 'global_rules.md') } },
|
|
|
|
|
+ { name: 'aider-desk', base: homeDir('.aider-desk'), skillsSub: 'skills', sys: null },
|
|
|
];
|
|
];
|
|
|
|
|
|
|
|
// Prints a usage error and exits with the usage exit code.
|
|
// Prints a usage error and exits with the usage exit code.
|
|
@@ -327,19 +324,17 @@ function linkForAgent(agent, skills) {
|
|
|
|
|
|
|
|
// Copies the bundled SYSTEM_PROMPT.md into every detected agent that supports
|
|
// Copies the bundled SYSTEM_PROMPT.md into every detected agent that supports
|
|
|
// a global system prompt (sys mapping), renamed to its standard filename and
|
|
// a global system prompt (sys mapping), renamed to its standard filename and
|
|
|
-// always as a plain copy (never linked, never through ~/.agents). Cursor and
|
|
|
|
|
-// windsurf receive the content wrapped in an always-on rule frontmatter.
|
|
|
|
|
|
|
+// always as a plain copy (never linked, never through ~/.agents).
|
|
|
function syncSystemPrompts(detected, systemPrompt) {
|
|
function syncSystemPrompts(detected, systemPrompt) {
|
|
|
const actions = [];
|
|
const actions = [];
|
|
|
|
|
+ const expectedHash = createHash('sha256').update(systemPrompt).digest('hex');
|
|
|
for (const agent of detected) {
|
|
for (const agent of detected) {
|
|
|
if (!agent.sys) continue;
|
|
if (!agent.sys) continue;
|
|
|
- const expected = (agent.sys.frontmatter ?? '') + systemPrompt;
|
|
|
|
|
const dest = join(agent.base(), agent.sys.file);
|
|
const dest = join(agent.base(), agent.sys.file);
|
|
|
- const expectedHash = createHash('sha256').update(expected).digest('hex');
|
|
|
|
|
const status = expectedHash === fileHash(dest) ? 'up-to-date' : existsSync(dest) ? 'updated' : 'added';
|
|
const status = expectedHash === fileHash(dest) ? 'up-to-date' : existsSync(dest) ? 'updated' : 'added';
|
|
|
if (status !== 'up-to-date' && !DRY_RUN) {
|
|
if (status !== 'up-to-date' && !DRY_RUN) {
|
|
|
mkdirSync(dirname(dest), { recursive: true });
|
|
mkdirSync(dirname(dest), { recursive: true });
|
|
|
- writeFileSync(dest, expected);
|
|
|
|
|
|
|
+ writeFileSync(dest, systemPrompt);
|
|
|
}
|
|
}
|
|
|
actions.push({ label: `${agent.name}:sys/${basename(agent.sys.file)}`, status });
|
|
actions.push({ label: `${agent.name}:sys/${basename(agent.sys.file)}`, status });
|
|
|
}
|
|
}
|