Give plugin agent files the plugin-root variable, not the skill token

A spawned agent never loads SKILL.md, so the <skill-base-dir> token
Setup defines is undefined in the one context that must act on it
(review finding). Claude Code substitutes ${CLAUDE_PLUGIN_ROOT} inline
anywhere in plugin agent content, so the agents directory now gets its
own rewrite emitting the quoted variable form; the skill and reference
files keep the token, which the main thread's base-directory report
resolves.

Drafted with AI assistance, reviewed by a maintainer.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Abdul Wahab
2026-08-28 01:08:16 +05:00
co-authored by Cursor
parent abf7773c3d
commit d28a9dc31c
4 changed files with 74 additions and 7 deletions
+36
View File
@@ -17,6 +17,7 @@ import os from 'os';
import path from 'path';
import {
rewritePluginMarkdown,
rewritePluginAgentMarkdown,
rewritePluginMarkdownTree,
verifyPluginSkillRewrite,
CLAUDE_PROJECT_SCRIPTS_PATH,
@@ -92,6 +93,26 @@ describe('rewritePluginMarkdown', () => {
});
});
describe('rewritePluginAgentMarkdown', () => {
test('rewrites agent instructions to the quoted plugin-root variable form', () => {
// A spawned agent never loads SKILL.md, so the <skill-base-dir> token
// Setup defines is unresolvable in its prompt. Claude Code substitutes
// ${CLAUDE_PLUGIN_ROOT} inline in plugin agent content.
const input =
'run `node .claude/skills/impeccable/scripts/embed-prompt.mjs <asset> --prompt "<the prompt used>"`';
expect(rewritePluginAgentMarkdown(input)).toBe(
'run `node "${CLAUDE_PLUGIN_ROOT}/skills/impeccable/scripts/embed-prompt.mjs" <asset> --prompt "<the prompt used>"`',
);
});
test('never emits the skill-base-dir token into an agent file', () => {
const input = 'node .claude/skills/impeccable/scripts/embed-prompt.mjs asset.png';
const output = rewritePluginAgentMarkdown(input);
expect(output).not.toContain('<skill-base-dir>');
expect(output).not.toContain(CLAUDE_PROJECT_SCRIPTS_PATH);
});
});
describe('rewritePluginMarkdownTree', () => {
let root;
@@ -130,6 +151,21 @@ describe('rewritePluginMarkdownTree', () => {
);
});
test('applies the agent rewrite when passed for an agents tree', () => {
const agentsDir = path.join(root, 'agents');
fs.mkdirSync(agentsDir, { recursive: true });
fs.writeFileSync(
path.join(agentsDir, 'impeccable-asset-producer.md'),
'run `node .claude/skills/impeccable/scripts/embed-prompt.mjs <asset>`',
);
rewritePluginMarkdownTree(agentsDir, rewritePluginAgentMarkdown);
expect(fs.readFileSync(path.join(agentsDir, 'impeccable-asset-producer.md'), 'utf-8')).toBe(
'run `node "${CLAUDE_PLUGIN_ROOT}/skills/impeccable/scripts/embed-prompt.mjs" <asset>`',
);
});
test('is a no-op on a missing directory', () => {
expect(() => rewritePluginMarkdownTree(path.join(root, 'does-not-exist'))).not.toThrow();
});