mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-31 14:06:50 +01:00
C7: keep the required-mode hook's exit-2 deny
Copilot CLI treats a preToolUse exit 2 as deny (its hooks reference), and #2413 established exit 2 plus hookSpecificOutput as the shape Claude Code enforces, so the deny output stays as on main; only the matcher and the in-place upgrade of older 'Skill' entries change.
This commit is contained in:
1 parent
ac5cf21587
commit
8ae92b2bd0
2 files changed
+5
-23
No files matched your search
@@ -151,10 +151,10 @@ Install it:
|
||||
|
||||
Then restart your AI coding tool.
|
||||
MSG
|
||||
# Claude Code reads hookSpecificOutput; Copilot CLI reads the top-level
|
||||
# fields (#2229). Exit 0 so both parse the decision instead of an error.
|
||||
echo '{"permissionDecision":"deny","permissionDecisionReason":"gstack is required but not installed. See stderr for install instructions.","hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"gstack is required but not installed. See stderr for install instructions."}}'
|
||||
exit 0
|
||||
# Exit 2 denies on Claude Code (#2413) and on Copilot CLI, which treats a
|
||||
# preToolUse exit 2 as deny (#2229).
|
||||
echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"gstack is required but not installed. See stderr for install instructions."}}'
|
||||
exit 2
|
||||
fi
|
||||
|
||||
echo '{}'
|
||||
|
||||
+1
-19
@@ -2,7 +2,7 @@ import { describe, test, expect, beforeEach, afterEach } from 'bun:test';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import { execSync, spawnSync } from 'child_process';
|
||||
import { execSync } from 'child_process';
|
||||
import { createHash } from 'crypto';
|
||||
import { runBashScript } from './helpers/bash-script';
|
||||
|
||||
@@ -267,24 +267,6 @@ describe('gstack-team-init', () => {
|
||||
expect(pre.map((e: { matcher: string }) => e.matcher)).toEqual(['Bash', 'Skill|skill']);
|
||||
});
|
||||
|
||||
test('required: the hook denies in both Claude Code and Copilot CLI decision formats (C7)', () => {
|
||||
run(`${TEAM_INIT} required`, { cwd: tmpDir });
|
||||
const home = mkTmpDir();
|
||||
try {
|
||||
const r = spawnSync('bash', [path.join(tmpDir, '.claude', 'hooks', 'check-gstack.sh')], {
|
||||
encoding: 'utf-8', timeout: 30_000, env: { PATH: process.env.PATH ?? '/usr/bin:/bin', HOME: home },
|
||||
});
|
||||
expect(r.status).toBe(0);
|
||||
const out = JSON.parse(r.stdout);
|
||||
expect(out.permissionDecision).toBe('deny');
|
||||
expect(out.permissionDecisionReason).toContain('gstack is required');
|
||||
expect(out.hookSpecificOutput).toMatchObject({ hookEventName: 'PreToolUse', permissionDecision: 'deny' });
|
||||
expect(r.stderr).toContain('BLOCKED: gstack is not installed');
|
||||
} finally {
|
||||
fs.rmSync(home, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('idempotent: running twice does not duplicate CLAUDE.md section', () => {
|
||||
run(`${TEAM_INIT} optional`, { cwd: tmpDir });
|
||||
run(`${TEAM_INIT} optional`, { cwd: tmpDir });
|
||||
|
||||
Reference in new issue
Block a user