mirror of
https://github.com/affaan-m/ECC.git
synced 2026-10-26 18:35:05 +00:00
fix(ck): escape the skill dir for bash when SessionStart fills it in
Signed-off-by: Wen <jwcastillo@gmail.com>
(cherry picked from commit 00a03ab84f)
This commit is contained in:
2 files changed
+29
-2
No files matched your search
@@ -89,8 +89,11 @@ function main() {
|
||||
|
||||
// Load skill (always inject — now only ~50 lines)
|
||||
// The host does not expand ${CLAUDE_SKILL_DIR} in hook output, so fill it in here.
|
||||
// SKILL.md uses it inside double-quoted bash, so escape the characters bash
|
||||
// still interprets there; the callback keeps `$&` etc. in the path literal.
|
||||
const shellSkillDir = SKILL_DIR.replace(/[\\$`"]/g, '\\$&');
|
||||
const skill = existsSync(SKILL_FILE)
|
||||
? readFileSync(SKILL_FILE, 'utf8').replaceAll('${CLAUDE_SKILL_DIR}', SKILL_DIR)
|
||||
? readFileSync(SKILL_FILE, 'utf8').replaceAll('${CLAUDE_SKILL_DIR}', () => shellSkillDir)
|
||||
: '';
|
||||
|
||||
const projects = readJson(PROJECTS_FILE) || {};
|
||||
|
||||
@@ -78,7 +78,31 @@ async function runTests() {
|
||||
const { additionalContext } = JSON.parse(result.stdout);
|
||||
assert.ok(additionalContext.includes('# ck — Context Keeper'), 'SKILL.md was not injected');
|
||||
assert.ok(!additionalContext.includes('${CLAUDE_SKILL_DIR}'), 'skill dir placeholder left unexpanded');
|
||||
assert.ok(additionalContext.includes(path.join(SKILL_DIR, 'commands')), 'skill dir not substituted');
|
||||
// Escaped for double-quoted bash (Windows backslashes are doubled).
|
||||
const shellCommandsDir = path.join(SKILL_DIR, 'commands').replace(/[\\$`"]/g, '\\$&');
|
||||
assert.ok(additionalContext.includes(shellCommandsDir), 'skill dir not substituted');
|
||||
} finally {
|
||||
fs.rmSync(ctx.root, { recursive: true, force: true });
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
console.log(' - skipped shell-special skill dir test on Windows');
|
||||
} else if (await test('hook-injected commands run from a skill dir with shell-special characters', async () => {
|
||||
const ctx = tempEnv();
|
||||
try {
|
||||
const skillDir = path.join(ctx.root, 'skills $USER $& `x` "q"', 'ck');
|
||||
fs.cpSync(SKILL_DIR, skillDir, { recursive: true });
|
||||
const result = spawnSync(process.execPath, [path.join(skillDir, 'hooks', 'session-start.mjs')], {
|
||||
cwd: ctx.project, env: ctx.env, input: '{}', encoding: 'utf8',
|
||||
});
|
||||
assert.strictEqual(result.status, 0, result.stderr);
|
||||
const { additionalContext } = JSON.parse(result.stdout);
|
||||
const listCommand = additionalContext.split('\n').find(line => line.includes('commands/list.mjs'));
|
||||
assert.ok(listCommand, 'no list.mjs command injected');
|
||||
const list = spawnSync('bash', ['-c', listCommand], { cwd: ctx.project, env: ctx.env, encoding: 'utf8' });
|
||||
// No projects are registered, so list.mjs reaching its own message proves the path resolved.
|
||||
assert.ok(list.stdout.includes('No projects registered'), `${listCommand}\n${list.stdout}${list.stderr}`);
|
||||
} finally {
|
||||
fs.rmSync(ctx.root, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user