mirror of https://github.com/garrytan/gstack.git
Merge 8234ba1263 into a3259400a3
This commit is contained in:
commit
32965df418
|
|
@ -14,7 +14,7 @@ hooks:
|
|||
- matcher: "Bash"
|
||||
hooks:
|
||||
- type: command
|
||||
command: "bash $HOME/.claude/skills/gstack/careful/bin/check-careful.sh"
|
||||
command: 'bash -c ''D="$PWD"; while :; do S="$D/.claude/skills/gstack/careful/bin/check-careful.sh"; [ -x "$S" ] && exec bash "$S"; [ "$D" = "/" ] && break; D="$(dirname "$D")"; done; S="$HOME/.claude/skills/gstack/careful/bin/check-careful.sh"; [ -x "$S" ] && exec bash "$S"; exit 0'''
|
||||
statusMessage: "Checking for destructive commands..."
|
||||
---
|
||||
<!-- AUTO-GENERATED from SKILL.md.tmpl — do not edit directly -->
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ hooks:
|
|||
- matcher: "Bash"
|
||||
hooks:
|
||||
- type: command
|
||||
command: "bash $HOME/.claude/skills/gstack/careful/bin/check-careful.sh"
|
||||
command: 'bash -c ''D="$PWD"; while :; do S="$D/.claude/skills/gstack/careful/bin/check-careful.sh"; [ -x "$S" ] && exec bash "$S"; [ "$D" = "/" ] && break; D="$(dirname "$D")"; done; S="$HOME/.claude/skills/gstack/careful/bin/check-careful.sh"; [ -x "$S" ] && exec bash "$S"; exit 0'''
|
||||
statusMessage: "Checking for destructive commands..."
|
||||
sensitive: true
|
||||
---
|
||||
|
|
|
|||
|
|
@ -15,12 +15,12 @@ hooks:
|
|||
- matcher: "Edit"
|
||||
hooks:
|
||||
- type: command
|
||||
command: "bash $HOME/.claude/skills/gstack/freeze/bin/check-freeze.sh"
|
||||
command: 'bash -c ''D="$PWD"; while :; do S="$D/.claude/skills/gstack/freeze/bin/check-freeze.sh"; [ -x "$S" ] && exec bash "$S"; [ "$D" = "/" ] && break; D="$(dirname "$D")"; done; S="$HOME/.claude/skills/gstack/freeze/bin/check-freeze.sh"; [ -x "$S" ] && exec bash "$S"; exit 0'''
|
||||
statusMessage: "Checking freeze boundary..."
|
||||
- matcher: "Write"
|
||||
hooks:
|
||||
- type: command
|
||||
command: "bash $HOME/.claude/skills/gstack/freeze/bin/check-freeze.sh"
|
||||
command: 'bash -c ''D="$PWD"; while :; do S="$D/.claude/skills/gstack/freeze/bin/check-freeze.sh"; [ -x "$S" ] && exec bash "$S"; [ "$D" = "/" ] && break; D="$(dirname "$D")"; done; S="$HOME/.claude/skills/gstack/freeze/bin/check-freeze.sh"; [ -x "$S" ] && exec bash "$S"; exit 0'''
|
||||
statusMessage: "Checking freeze boundary..."
|
||||
---
|
||||
<!-- AUTO-GENERATED from SKILL.md.tmpl — do not edit directly -->
|
||||
|
|
|
|||
|
|
@ -20,12 +20,12 @@ hooks:
|
|||
- matcher: "Edit"
|
||||
hooks:
|
||||
- type: command
|
||||
command: "bash $HOME/.claude/skills/gstack/freeze/bin/check-freeze.sh"
|
||||
command: 'bash -c ''D="$PWD"; while :; do S="$D/.claude/skills/gstack/freeze/bin/check-freeze.sh"; [ -x "$S" ] && exec bash "$S"; [ "$D" = "/" ] && break; D="$(dirname "$D")"; done; S="$HOME/.claude/skills/gstack/freeze/bin/check-freeze.sh"; [ -x "$S" ] && exec bash "$S"; exit 0'''
|
||||
statusMessage: "Checking freeze boundary..."
|
||||
- matcher: "Write"
|
||||
hooks:
|
||||
- type: command
|
||||
command: "bash $HOME/.claude/skills/gstack/freeze/bin/check-freeze.sh"
|
||||
command: 'bash -c ''D="$PWD"; while :; do S="$D/.claude/skills/gstack/freeze/bin/check-freeze.sh"; [ -x "$S" ] && exec bash "$S"; [ "$D" = "/" ] && break; D="$(dirname "$D")"; done; S="$HOME/.claude/skills/gstack/freeze/bin/check-freeze.sh"; [ -x "$S" ] && exec bash "$S"; exit 0'''
|
||||
statusMessage: "Checking freeze boundary..."
|
||||
sensitive: true
|
||||
---
|
||||
|
|
|
|||
|
|
@ -15,17 +15,17 @@ hooks:
|
|||
- matcher: "Bash"
|
||||
hooks:
|
||||
- type: command
|
||||
command: "bash $HOME/.claude/skills/gstack/careful/bin/check-careful.sh"
|
||||
command: 'bash -c ''D="$PWD"; while :; do S="$D/.claude/skills/gstack/careful/bin/check-careful.sh"; [ -x "$S" ] && exec bash "$S"; [ "$D" = "/" ] && break; D="$(dirname "$D")"; done; S="$HOME/.claude/skills/gstack/careful/bin/check-careful.sh"; [ -x "$S" ] && exec bash "$S"; exit 0'''
|
||||
statusMessage: "Checking for destructive commands..."
|
||||
- matcher: "Edit"
|
||||
hooks:
|
||||
- type: command
|
||||
command: "bash $HOME/.claude/skills/gstack/freeze/bin/check-freeze.sh"
|
||||
command: 'bash -c ''D="$PWD"; while :; do S="$D/.claude/skills/gstack/freeze/bin/check-freeze.sh"; [ -x "$S" ] && exec bash "$S"; [ "$D" = "/" ] && break; D="$(dirname "$D")"; done; S="$HOME/.claude/skills/gstack/freeze/bin/check-freeze.sh"; [ -x "$S" ] && exec bash "$S"; exit 0'''
|
||||
statusMessage: "Checking freeze boundary..."
|
||||
- matcher: "Write"
|
||||
hooks:
|
||||
- type: command
|
||||
command: "bash $HOME/.claude/skills/gstack/freeze/bin/check-freeze.sh"
|
||||
command: 'bash -c ''D="$PWD"; while :; do S="$D/.claude/skills/gstack/freeze/bin/check-freeze.sh"; [ -x "$S" ] && exec bash "$S"; [ "$D" = "/" ] && break; D="$(dirname "$D")"; done; S="$HOME/.claude/skills/gstack/freeze/bin/check-freeze.sh"; [ -x "$S" ] && exec bash "$S"; exit 0'''
|
||||
statusMessage: "Checking freeze boundary..."
|
||||
---
|
||||
<!-- AUTO-GENERATED from SKILL.md.tmpl — do not edit directly -->
|
||||
|
|
|
|||
|
|
@ -20,17 +20,17 @@ hooks:
|
|||
- matcher: "Bash"
|
||||
hooks:
|
||||
- type: command
|
||||
command: "bash $HOME/.claude/skills/gstack/careful/bin/check-careful.sh"
|
||||
command: 'bash -c ''D="$PWD"; while :; do S="$D/.claude/skills/gstack/careful/bin/check-careful.sh"; [ -x "$S" ] && exec bash "$S"; [ "$D" = "/" ] && break; D="$(dirname "$D")"; done; S="$HOME/.claude/skills/gstack/careful/bin/check-careful.sh"; [ -x "$S" ] && exec bash "$S"; exit 0'''
|
||||
statusMessage: "Checking for destructive commands..."
|
||||
- matcher: "Edit"
|
||||
hooks:
|
||||
- type: command
|
||||
command: "bash $HOME/.claude/skills/gstack/freeze/bin/check-freeze.sh"
|
||||
command: 'bash -c ''D="$PWD"; while :; do S="$D/.claude/skills/gstack/freeze/bin/check-freeze.sh"; [ -x "$S" ] && exec bash "$S"; [ "$D" = "/" ] && break; D="$(dirname "$D")"; done; S="$HOME/.claude/skills/gstack/freeze/bin/check-freeze.sh"; [ -x "$S" ] && exec bash "$S"; exit 0'''
|
||||
statusMessage: "Checking freeze boundary..."
|
||||
- matcher: "Write"
|
||||
hooks:
|
||||
- type: command
|
||||
command: "bash $HOME/.claude/skills/gstack/freeze/bin/check-freeze.sh"
|
||||
command: 'bash -c ''D="$PWD"; while :; do S="$D/.claude/skills/gstack/freeze/bin/check-freeze.sh"; [ -x "$S" ] && exec bash "$S"; [ "$D" = "/" ] && break; D="$(dirname "$D")"; done; S="$HOME/.claude/skills/gstack/freeze/bin/check-freeze.sh"; [ -x "$S" ] && exec bash "$S"; exit 0'''
|
||||
statusMessage: "Checking freeze boundary..."
|
||||
sensitive: true
|
||||
---
|
||||
|
|
|
|||
|
|
@ -23,12 +23,12 @@ hooks:
|
|||
- matcher: "Edit"
|
||||
hooks:
|
||||
- type: command
|
||||
command: 'bash -c ''S="${CLAUDE_SKILL_DIR}/../freeze/bin/check-freeze.sh"; [ -x "$S" ] || S="${CLAUDE_SKILL_DIR}/../gstack-freeze/bin/check-freeze.sh"; [ -x "$S" ] && bash "$S" || exit 0'''
|
||||
command: 'bash -c ''D="$PWD"; while :; do S="$D/.claude/skills/gstack/freeze/bin/check-freeze.sh"; [ -x "$S" ] && exec bash "$S"; [ "$D" = "/" ] && break; D="$(dirname "$D")"; done; S="$HOME/.claude/skills/gstack/freeze/bin/check-freeze.sh"; [ -x "$S" ] && exec bash "$S"; exit 0'''
|
||||
statusMessage: "Checking debug scope boundary..."
|
||||
- matcher: "Write"
|
||||
hooks:
|
||||
- type: command
|
||||
command: 'bash -c ''S="${CLAUDE_SKILL_DIR}/../freeze/bin/check-freeze.sh"; [ -x "$S" ] || S="${CLAUDE_SKILL_DIR}/../gstack-freeze/bin/check-freeze.sh"; [ -x "$S" ] && bash "$S" || exit 0'''
|
||||
command: 'bash -c ''D="$PWD"; while :; do S="$D/.claude/skills/gstack/freeze/bin/check-freeze.sh"; [ -x "$S" ] && exec bash "$S"; [ "$D" = "/" ] && break; D="$(dirname "$D")"; done; S="$HOME/.claude/skills/gstack/freeze/bin/check-freeze.sh"; [ -x "$S" ] && exec bash "$S"; exit 0'''
|
||||
statusMessage: "Checking debug scope boundary..."
|
||||
gbrain:
|
||||
schema: 1
|
||||
|
|
|
|||
|
|
@ -30,12 +30,12 @@ hooks:
|
|||
- matcher: "Edit"
|
||||
hooks:
|
||||
- type: command
|
||||
command: 'bash -c ''S="${CLAUDE_SKILL_DIR}/../freeze/bin/check-freeze.sh"; [ -x "$S" ] || S="${CLAUDE_SKILL_DIR}/../gstack-freeze/bin/check-freeze.sh"; [ -x "$S" ] && bash "$S" || exit 0'''
|
||||
command: 'bash -c ''D="$PWD"; while :; do S="$D/.claude/skills/gstack/freeze/bin/check-freeze.sh"; [ -x "$S" ] && exec bash "$S"; [ "$D" = "/" ] && break; D="$(dirname "$D")"; done; S="$HOME/.claude/skills/gstack/freeze/bin/check-freeze.sh"; [ -x "$S" ] && exec bash "$S"; exit 0'''
|
||||
statusMessage: "Checking debug scope boundary..."
|
||||
- matcher: "Write"
|
||||
hooks:
|
||||
- type: command
|
||||
command: 'bash -c ''S="${CLAUDE_SKILL_DIR}/../freeze/bin/check-freeze.sh"; [ -x "$S" ] || S="${CLAUDE_SKILL_DIR}/../gstack-freeze/bin/check-freeze.sh"; [ -x "$S" ] && bash "$S" || exit 0'''
|
||||
command: 'bash -c ''D="$PWD"; while :; do S="$D/.claude/skills/gstack/freeze/bin/check-freeze.sh"; [ -x "$S" ] && exec bash "$S"; [ "$D" = "/" ] && break; D="$(dirname "$D")"; done; S="$HOME/.claude/skills/gstack/freeze/bin/check-freeze.sh"; [ -x "$S" ] && exec bash "$S"; exit 0'''
|
||||
statusMessage: "Checking debug scope boundary..."
|
||||
gbrain:
|
||||
schema: 1
|
||||
|
|
|
|||
|
|
@ -1841,7 +1841,7 @@ describe('Codex generation (--host codex)', () => {
|
|||
expect(frontmatter).toContain('YC Office Hours');
|
||||
});
|
||||
|
||||
test('hook skills have safety prose and no hooks: in frontmatter', () => {
|
||||
test('Codex hook skills have safety prose and no hooks: in frontmatter', () => {
|
||||
const HOOK_SKILLS = ['gstack-careful', 'gstack-freeze', 'gstack-guard'];
|
||||
for (const skillName of HOOK_SKILLS) {
|
||||
const content = fs.readFileSync(path.join(AGENTS_DIR, skillName, 'SKILL.md'), 'utf-8');
|
||||
|
|
@ -1854,6 +1854,59 @@ describe('Codex generation (--host codex)', () => {
|
|||
}
|
||||
});
|
||||
|
||||
test('Claude hook commands do not depend on CLAUDE_SKILL_DIR', () => {
|
||||
// #1871: Claude Code 2.1.162 does not populate CLAUDE_SKILL_DIR for
|
||||
// skill-frontmatter PreToolUse hooks. If these commands use that variable,
|
||||
// they expand to paths like /../freeze/bin/check-freeze.sh and fail before
|
||||
// the safety hook can do its job.
|
||||
const HOOK_SKILLS = ['careful', 'freeze', 'guard', 'investigate'];
|
||||
for (const skillName of HOOK_SKILLS) {
|
||||
const content = fs.readFileSync(path.join(ROOT, skillName, 'SKILL.md'), 'utf-8');
|
||||
const fmEnd = content.indexOf('\n---', 4);
|
||||
const frontmatter = content.slice(4, fmEnd);
|
||||
expect(frontmatter).toContain('hooks:');
|
||||
expect(frontmatter).not.toContain('CLAUDE_SKILL_DIR');
|
||||
expect(frontmatter).not.toContain('CLAUDE_PLUGIN_ROOT');
|
||||
expect(frontmatter).not.toContain('git rev-parse --show-toplevel');
|
||||
expect(frontmatter).toContain('while :; do');
|
||||
expect(frontmatter).toContain('$D/.claude/skills/gstack/');
|
||||
expect(frontmatter).toContain('$HOME/.claude/skills/gstack/');
|
||||
}
|
||||
});
|
||||
|
||||
test('Claude hook commands resolve project-local install from nested non-git cwd', () => {
|
||||
const { execFileSync } = require('child_process');
|
||||
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gstack-hook-local-'));
|
||||
const projectRoot = path.join(tmpRoot, 'project');
|
||||
const nestedCwd = path.join(projectRoot, 'nested', 'plain-dir');
|
||||
const scriptPath = path.join(projectRoot, '.claude', 'skills', 'gstack', 'careful', 'bin', 'check-careful.sh');
|
||||
|
||||
fs.mkdirSync(path.dirname(scriptPath), { recursive: true });
|
||||
fs.mkdirSync(nestedCwd, { recursive: true });
|
||||
fs.writeFileSync(scriptPath, '#!/usr/bin/env bash\nprintf local-careful');
|
||||
fs.chmodSync(scriptPath, 0o755);
|
||||
|
||||
try {
|
||||
const content = fs.readFileSync(path.join(ROOT, 'careful', 'SKILL.md'), 'utf-8');
|
||||
const fmEnd = content.indexOf('\n---', 4);
|
||||
const frontmatter = content.slice(4, fmEnd);
|
||||
const commandLine = frontmatter.split('\n').find(line => line.trim().startsWith('command: '));
|
||||
expect(commandLine).toBeTruthy();
|
||||
let command = commandLine!.trim().slice('command: '.length);
|
||||
expect(command.startsWith("'")).toBe(true);
|
||||
command = command.slice(1, -1).replaceAll("''", "'");
|
||||
|
||||
const output = execFileSync('bash', ['-c', command], {
|
||||
cwd: nestedCwd,
|
||||
input: '{}',
|
||||
encoding: 'utf8',
|
||||
});
|
||||
expect(output).toBe('local-careful');
|
||||
} finally {
|
||||
fs.rmSync(tmpRoot, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('all Codex SKILL.md files have auto-generated header', () => {
|
||||
for (const skill of CODEX_SKILLS) {
|
||||
const content = fs.readFileSync(path.join(AGENTS_DIR, skill.codexName, 'SKILL.md'), 'utf-8');
|
||||
|
|
|
|||
Loading…
Reference in New Issue