Features: - 30+ Custom Skills (cognitive, development, UI/UX, autonomous agents) - RalphLoop autonomous agent integration - Multi-AI consultation (Qwen) - Agent management system with sync capabilities - Custom hooks for session management - MCP servers integration - Plugin marketplace setup - Comprehensive installation script Components: - Skills: always-use-superpowers, ralph, brainstorming, ui-ux-pro-max, etc. - Agents: 100+ agents across engineering, marketing, product, etc. - Hooks: session-start-superpowers, qwen-consult, ralph-auto-trigger - Commands: /brainstorm, /write-plan, /execute-plan - MCP Servers: zai-mcp-server, web-search-prime, web-reader, zread - Binaries: ralphloop wrapper Installation: ./supercharge.sh
85 lines
2.8 KiB
TypeScript
85 lines
2.8 KiB
TypeScript
import { describe, expect, test } from 'bun:test';
|
|
|
|
async function runGeminiHook(
|
|
input: object,
|
|
): Promise<{ stdout: string; stderr: string; exitCode: number }> {
|
|
const proc = Bun.spawn(['bun', 'src/bin/cc-safety-net.ts', '-gc'], {
|
|
stdin: 'pipe',
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
});
|
|
proc.stdin.write(JSON.stringify(input));
|
|
proc.stdin.end();
|
|
const stdoutPromise = new Response(proc.stdout).text();
|
|
const stderrPromise = new Response(proc.stderr).text();
|
|
const [stdout, stderr] = await Promise.all([stdoutPromise, stderrPromise]);
|
|
const exitCode = await proc.exited;
|
|
return { stdout: stdout.trim(), stderr: stderr.trim(), exitCode };
|
|
}
|
|
|
|
describe('Gemini CLI hook', () => {
|
|
describe('input parsing', () => {
|
|
test('blocks rm -rf via run_shell_command', async () => {
|
|
const input = {
|
|
hook_event_name: 'BeforeTool',
|
|
tool_name: 'run_shell_command',
|
|
tool_input: { command: 'rm -rf /' },
|
|
};
|
|
const { stdout, exitCode } = await runGeminiHook(input);
|
|
expect(exitCode).toBe(0);
|
|
const output = JSON.parse(stdout);
|
|
expect(output.decision).toBe('deny');
|
|
expect(output.reason).toContain('rm -rf');
|
|
});
|
|
|
|
test('allows safe commands (no output)', async () => {
|
|
const input = {
|
|
hook_event_name: 'BeforeTool',
|
|
tool_name: 'run_shell_command',
|
|
tool_input: { command: 'ls -la' },
|
|
};
|
|
const { stdout, exitCode } = await runGeminiHook(input);
|
|
expect(exitCode).toBe(0);
|
|
expect(stdout).toBe(''); // No output means allowed
|
|
});
|
|
|
|
test('ignores non-BeforeTool events', async () => {
|
|
const input = {
|
|
hook_event_name: 'AfterTool',
|
|
tool_name: 'run_shell_command',
|
|
tool_input: { command: 'rm -rf /' },
|
|
};
|
|
const { stdout, exitCode } = await runGeminiHook(input);
|
|
expect(exitCode).toBe(0);
|
|
expect(stdout).toBe(''); // Ignored, not blocked
|
|
});
|
|
|
|
test('ignores non-shell tools', async () => {
|
|
const input = {
|
|
hook_event_name: 'BeforeTool',
|
|
tool_name: 'write_file',
|
|
tool_input: { path: '/etc/passwd' },
|
|
};
|
|
const { stdout, exitCode } = await runGeminiHook(input);
|
|
expect(exitCode).toBe(0);
|
|
expect(stdout).toBe(''); // Ignored, not blocked
|
|
});
|
|
});
|
|
|
|
describe('output format', () => {
|
|
test('outputs Gemini format with decision: deny', async () => {
|
|
const input = {
|
|
hook_event_name: 'BeforeTool',
|
|
tool_name: 'run_shell_command',
|
|
tool_input: { command: 'git reset --hard' },
|
|
};
|
|
const { stdout, exitCode } = await runGeminiHook(input);
|
|
expect(exitCode).toBe(0);
|
|
const output = JSON.parse(stdout);
|
|
expect(output).toHaveProperty('decision', 'deny');
|
|
expect(output).toHaveProperty('reason');
|
|
expect(output.reason).toContain('git reset --hard');
|
|
});
|
|
});
|
|
});
|