SuperCharge Claude Code v1.0.0 - Complete Customization Package

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
This commit is contained in:
uroma
2026-01-22 15:35:55 +00:00
Unverified
commit 7a491b1548
1013 changed files with 170070 additions and 0 deletions

View File

@@ -0,0 +1,68 @@
#!/usr/bin/env node
import { runClaudeCodeHook } from './claude-code.ts';
import { CUSTOM_RULES_DOC } from './custom-rules-doc.ts';
import { runGeminiCLIHook } from './gemini-cli.ts';
import { printHelp, printVersion } from './help.ts';
import { printStatusline } from './statusline.ts';
import { verifyConfig } from './verify-config.ts';
function printCustomRulesDoc(): void {
console.log(CUSTOM_RULES_DOC);
}
type HookMode = 'claude-code' | 'gemini-cli' | 'statusline';
function handleCliFlags(): HookMode | null {
const args = process.argv.slice(2);
if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
printHelp();
process.exit(0);
}
if (args.includes('--version') || args.includes('-V')) {
printVersion();
process.exit(0);
}
if (args.includes('--verify-config') || args.includes('-vc')) {
process.exit(verifyConfig());
}
if (args.includes('--custom-rules-doc')) {
printCustomRulesDoc();
process.exit(0);
}
if (args.includes('--statusline')) {
return 'statusline';
}
if (args.includes('--claude-code') || args.includes('-cc')) {
return 'claude-code';
}
if (args.includes('--gemini-cli') || args.includes('-gc')) {
return 'gemini-cli';
}
console.error(`Unknown option: ${args[0]}`);
console.error("Run 'cc-safety-net --help' for usage.");
process.exit(1);
}
async function main(): Promise<void> {
const mode = handleCliFlags();
if (mode === 'claude-code') {
await runClaudeCodeHook();
} else if (mode === 'gemini-cli') {
await runGeminiCLIHook();
} else if (mode === 'statusline') {
await printStatusline();
}
}
main().catch((error: unknown) => {
console.error('Safety Net error:', error);
process.exit(1);
});

View File

@@ -0,0 +1,81 @@
import { analyzeCommand, loadConfig } from '../core/analyze.ts';
import { redactSecrets, writeAuditLog } from '../core/audit.ts';
import { envTruthy } from '../core/env.ts';
import { formatBlockedMessage } from '../core/format.ts';
import type { HookInput, HookOutput } from '../types.ts';
function outputDeny(reason: string, command?: string, segment?: string): void {
const message = formatBlockedMessage({
reason,
command,
segment,
redact: redactSecrets,
});
const output: HookOutput = {
hookSpecificOutput: {
hookEventName: 'PreToolUse',
permissionDecision: 'deny',
permissionDecisionReason: message,
},
};
console.log(JSON.stringify(output));
}
export async function runClaudeCodeHook(): Promise<void> {
const chunks: Buffer[] = [];
for await (const chunk of process.stdin) {
chunks.push(chunk as Buffer);
}
const inputText = Buffer.concat(chunks).toString('utf-8').trim();
if (!inputText) {
return;
}
let input: HookInput;
try {
input = JSON.parse(inputText) as HookInput;
} catch {
if (envTruthy('SAFETY_NET_STRICT')) {
outputDeny('Failed to parse hook input JSON (strict mode)');
}
return;
}
if (input.tool_name !== 'Bash') {
return;
}
const command = input.tool_input?.command;
if (!command) {
return;
}
const cwd = input.cwd ?? process.cwd();
const strict = envTruthy('SAFETY_NET_STRICT');
const paranoidAll = envTruthy('SAFETY_NET_PARANOID');
const paranoidRm = paranoidAll || envTruthy('SAFETY_NET_PARANOID_RM');
const paranoidInterpreters = paranoidAll || envTruthy('SAFETY_NET_PARANOID_INTERPRETERS');
const config = loadConfig(cwd);
const result = analyzeCommand(command, {
cwd,
config,
strict,
paranoidRm,
paranoidInterpreters,
});
if (result) {
const sessionId = input.session_id;
if (sessionId) {
writeAuditLog(sessionId, command, result.segment, result.reason, cwd);
}
outputDeny(result.reason, command, result.segment);
}
}

View File

@@ -0,0 +1,116 @@
export const CUSTOM_RULES_DOC = `# Custom Rules Reference
Agent reference for generating \`.safety-net.json\` config files.
## Config Locations
| Scope | Path | Priority |
|-------|------|----------|
| User | \`~/.cc-safety-net/config.json\` | Lower |
| Project | \`.safety-net.json\` (cwd) | Higher (overrides user) |
Duplicate rule names (case-insensitive) → project wins.
## Schema
\`\`\`json
{
"$schema": "https://raw.githubusercontent.com/kenryu42/claude-code-safety-net/main/assets/cc-safety-net.schema.json",
"version": 1,
"rules": [...]
}
\`\`\`
- \`$schema\`: Optional. Enables IDE autocomplete and inline validation.
- \`version\`: Required. Must be \`1\`.
- \`rules\`: Optional. Defaults to \`[]\`.
**Always include \`$schema\`** when generating config files for IDE support.
## Rule Fields
| Field | Required | Constraints |
|-------|----------|-------------|
| \`name\` | Yes | \`^[a-zA-Z][a-zA-Z0-9_-]{0,63}$\` — unique (case-insensitive) |
| \`command\` | Yes | \`^[a-zA-Z][a-zA-Z0-9_-]*$\` — basename only, not path |
| \`subcommand\` | No | Same pattern as command. Omit to match any. |
| \`block_args\` | Yes | Non-empty array of non-empty strings |
| \`reason\` | Yes | Non-empty string, max 256 chars |
## Guidelines:
- \`name\`: kebab-case, descriptive (e.g., \`block-git-add-all\`)
- \`command\`: binary name only, lowercase
- \`subcommand\`: omit if rule applies to any subcommand
- \`block_args\`: include all variants (e.g., both \`-g\` and \`--global\`)
- \`reason\`: explain why blocked AND suggest alternative
## Matching Behavior
- **Command**: Normalized to basename (\`/usr/bin/git\`\`git\`)
- **Subcommand**: First non-option argument after command
- **Arguments**: Matched literally. Command blocked if **any** \`block_args\` item present.
- **Short options**: Expanded (\`-Ap\` matches \`-A\`)
- **Long options**: Exact match (\`--all-files\` does NOT match \`--all\`)
- **Execution order**: Built-in rules first, then custom rules (additive only)
## Examples
### Block \`git add -A\`
\`\`\`json
{
"$schema": "https://raw.githubusercontent.com/kenryu42/claude-code-safety-net/main/assets/cc-safety-net.schema.json",
"version": 1,
"rules": [
{
"name": "block-git-add-all",
"command": "git",
"subcommand": "add",
"block_args": ["-A", "--all", "."],
"reason": "Use 'git add <specific-files>' instead."
}
]
}
\`\`\`
### Block global npm install
\`\`\`json
{
"$schema": "https://raw.githubusercontent.com/kenryu42/claude-code-safety-net/main/assets/cc-safety-net.schema.json",
"version": 1,
"rules": [
{
"name": "block-npm-global",
"command": "npm",
"subcommand": "install",
"block_args": ["-g", "--global"],
"reason": "Use npx or local install."
}
]
}
\`\`\`
### Block docker system prune
\`\`\`json
{
"$schema": "https://raw.githubusercontent.com/kenryu42/claude-code-safety-net/main/assets/cc-safety-net.schema.json",
"version": 1,
"rules": [
{
"name": "block-docker-prune",
"command": "docker",
"subcommand": "system",
"block_args": ["prune"],
"reason": "Use targeted cleanup instead."
}
]
}
\`\`\`
## Error Handling
Invalid config → silent fallback to built-in rules only. No custom rules applied.
`;

View File

@@ -0,0 +1,84 @@
import { analyzeCommand, loadConfig } from '../core/analyze.ts';
import { redactSecrets, writeAuditLog } from '../core/audit.ts';
import { envTruthy } from '../core/env.ts';
import { formatBlockedMessage } from '../core/format.ts';
import type { GeminiHookInput, GeminiHookOutput } from '../types.ts';
function outputGeminiDeny(reason: string, command?: string, segment?: string): void {
const message = formatBlockedMessage({
reason,
command,
segment,
redact: redactSecrets,
});
// Gemini CLI expects exit code 0 with JSON for policy blocks; exit 2 is for hook errors.
const output: GeminiHookOutput = {
decision: 'deny',
reason: message,
systemMessage: message,
};
console.log(JSON.stringify(output));
}
export async function runGeminiCLIHook(): Promise<void> {
const chunks: Buffer[] = [];
for await (const chunk of process.stdin) {
chunks.push(chunk as Buffer);
}
const inputText = Buffer.concat(chunks).toString('utf-8').trim();
if (!inputText) {
return;
}
let input: GeminiHookInput;
try {
input = JSON.parse(inputText) as GeminiHookInput;
} catch {
if (envTruthy('SAFETY_NET_STRICT')) {
outputGeminiDeny('Failed to parse hook input JSON (strict mode)');
}
return;
}
if (input.hook_event_name !== 'BeforeTool') {
return;
}
if (input.tool_name !== 'run_shell_command') {
return;
}
const command = input.tool_input?.command;
if (!command) {
return;
}
const cwd = input.cwd ?? process.cwd();
const strict = envTruthy('SAFETY_NET_STRICT');
const paranoidAll = envTruthy('SAFETY_NET_PARANOID');
const paranoidRm = paranoidAll || envTruthy('SAFETY_NET_PARANOID_RM');
const paranoidInterpreters = paranoidAll || envTruthy('SAFETY_NET_PARANOID_INTERPRETERS');
const config = loadConfig(cwd);
const result = analyzeCommand(command, {
cwd,
config,
strict,
paranoidRm,
paranoidInterpreters,
});
if (result) {
const sessionId = input.session_id;
if (sessionId) {
writeAuditLog(sessionId, command, result.segment, result.reason, cwd);
}
outputGeminiDeny(result.reason, command, result.segment);
}
}

View File

@@ -0,0 +1,32 @@
declare const __PKG_VERSION__: string | undefined;
const version = typeof __PKG_VERSION__ !== 'undefined' ? __PKG_VERSION__ : 'dev';
export function printHelp(): void {
console.log(`cc-safety-net v${version}
Blocks destructive git and filesystem commands before execution.
USAGE:
cc-safety-net -cc, --claude-code Run as Claude Code PreToolUse hook (reads JSON from stdin)
cc-safety-net -gc, --gemini-cli Run as Gemini CLI BeforeTool hook (reads JSON from stdin)
cc-safety-net -vc, --verify-config Validate config files
cc-safety-net --custom-rules-doc Print custom rules documentation
cc-safety-net --statusline Print status line with mode indicators
cc-safety-net -h, --help Show this help
cc-safety-net -V, --version Show version
ENVIRONMENT VARIABLES:
SAFETY_NET_STRICT=1 Fail-closed on unparseable commands
SAFETY_NET_PARANOID=1 Enable all paranoid checks
SAFETY_NET_PARANOID_RM=1 Block non-temp rm -rf within cwd
SAFETY_NET_PARANOID_INTERPRETERS=1 Block interpreter one-liners
CONFIG FILES:
~/.cc-safety-net/config.json User-scope config
.safety-net.json Project-scope config`);
}
export function printVersion(): void {
console.log(version);
}

View File

@@ -0,0 +1,117 @@
import { existsSync, readFileSync } from 'node:fs';
import { homedir } from 'node:os';
import { join } from 'node:path';
import { envTruthy } from '../core/env.ts';
/**
* Read piped stdin content asynchronously.
* Returns null if stdin is a TTY (no piped input) or empty.
*/
async function readStdinAsync(): Promise<string | null> {
if (process.stdin.isTTY) {
return null;
}
return new Promise((resolve) => {
let data = '';
process.stdin.setEncoding('utf-8');
process.stdin.on('data', (chunk) => {
data += chunk;
});
process.stdin.on('end', () => {
const trimmed = data.trim();
resolve(trimmed || null);
});
process.stdin.on('error', () => {
resolve(null);
});
});
}
function getSettingsPath(): string {
// Allow override for testing
if (process.env.CLAUDE_SETTINGS_PATH) {
return process.env.CLAUDE_SETTINGS_PATH;
}
return join(homedir(), '.claude', 'settings.json');
}
interface ClaudeSettings {
enabledPlugins?: Record<string, boolean>;
}
function isPluginEnabled(): boolean {
const settingsPath = getSettingsPath();
if (!existsSync(settingsPath)) {
// Default to disabled if settings file doesn't exist
return false;
}
try {
const content = readFileSync(settingsPath, 'utf-8');
const settings = JSON.parse(content) as ClaudeSettings;
// If enabledPlugins doesn't exist or plugin not listed, default to disabled
if (!settings.enabledPlugins) {
return false;
}
const pluginKey = 'safety-net@cc-marketplace';
// If not explicitly set, default to disabled
if (!(pluginKey in settings.enabledPlugins)) {
return false;
}
return settings.enabledPlugins[pluginKey] === true;
} catch {
// On any error (invalid JSON, etc.), default to disabled
return false;
}
}
export async function printStatusline(): Promise<void> {
const enabled = isPluginEnabled();
// Build our status string
let status: string;
if (!enabled) {
status = '🛡️ Safety Net ❌';
} else {
const strict = envTruthy('SAFETY_NET_STRICT');
const paranoidAll = envTruthy('SAFETY_NET_PARANOID');
const paranoidRm = paranoidAll || envTruthy('SAFETY_NET_PARANOID_RM');
const paranoidInterpreters = paranoidAll || envTruthy('SAFETY_NET_PARANOID_INTERPRETERS');
let modeEmojis = '';
// Strict mode: 🔒
if (strict) {
modeEmojis += '🔒';
}
// Paranoid modes: 👁️ if PARANOID or (PARANOID_RM + PARANOID_INTERPRETERS)
// Otherwise individual emojis: 🗑️ for RM, 🐚 for interpreters
if (paranoidAll || (paranoidRm && paranoidInterpreters)) {
modeEmojis += '👁️';
} else if (paranoidRm) {
modeEmojis += '🗑️';
} else if (paranoidInterpreters) {
modeEmojis += '🐚';
}
// If no mode flags, show ✅
const statusEmoji = modeEmojis || '✅';
status = `🛡️ Safety Net ${statusEmoji}`;
}
// Check for piped stdin input and prepend with separator
// Skip JSON input (Claude Code pipes status JSON that shouldn't be echoed)
const stdinInput = await readStdinAsync();
if (stdinInput && !stdinInput.startsWith('{')) {
console.log(`${stdinInput} | ${status}`);
} else {
console.log(status);
}
}

View File

@@ -0,0 +1,132 @@
/**
* Verify user and project scope config files for safety-net.
*/
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
import { resolve } from 'node:path';
import {
getProjectConfigPath,
getUserConfigPath,
type ValidationResult,
validateConfigFile,
} from '../core/config.ts';
export interface VerifyConfigOptions {
userConfigPath?: string;
projectConfigPath?: string;
}
const HEADER = 'Safety Net Config';
const SEPARATOR = '═'.repeat(HEADER.length);
const SCHEMA_URL =
'https://raw.githubusercontent.com/kenryu42/claude-code-safety-net/main/assets/cc-safety-net.schema.json';
function printHeader(): void {
console.log(HEADER);
console.log(SEPARATOR);
}
function printValidConfig(scope: string, path: string, result: ValidationResult): void {
console.log(`\n✓ ${scope} config: ${path}`);
if (result.ruleNames.size > 0) {
console.log(' Rules:');
let i = 1;
for (const name of result.ruleNames) {
console.log(` ${i}. ${name}`);
i++;
}
} else {
console.log(' Rules: (none)');
}
}
function printInvalidConfig(scope: string, path: string, errors: string[]): void {
console.error(`\n✗ ${scope} config: ${path}`);
console.error(' Errors:');
let errorNum = 1;
for (const error of errors) {
for (const part of error.split('; ')) {
console.error(` ${errorNum}. ${part}`);
errorNum++;
}
}
}
function addSchemaIfMissing(path: string): boolean {
try {
const content = readFileSync(path, 'utf-8');
const parsed = JSON.parse(content) as Record<string, unknown>;
if (parsed.$schema) {
return false;
}
const updated = { $schema: SCHEMA_URL, ...parsed };
writeFileSync(path, JSON.stringify(updated, null, 2), 'utf-8');
return true;
} catch {
return false;
}
}
/**
* Verify config files and print results.
* @returns Exit code (0 = success, 1 = errors found)
*/
export function verifyConfig(options: VerifyConfigOptions = {}): number {
const userConfig = options.userConfigPath ?? getUserConfigPath();
const projectConfig = options.projectConfigPath ?? getProjectConfigPath();
let hasErrors = false;
const configsChecked: Array<{
scope: string;
path: string;
result: ValidationResult;
}> = [];
printHeader();
if (existsSync(userConfig)) {
const result = validateConfigFile(userConfig);
configsChecked.push({ scope: 'User', path: userConfig, result });
if (result.errors.length > 0) {
hasErrors = true;
}
}
if (existsSync(projectConfig)) {
const result = validateConfigFile(projectConfig);
configsChecked.push({
scope: 'Project',
path: resolve(projectConfig),
result,
});
if (result.errors.length > 0) {
hasErrors = true;
}
}
if (configsChecked.length === 0) {
console.log('\nNo config files found. Using built-in rules only.');
return 0;
}
for (const { scope, path, result } of configsChecked) {
if (result.errors.length > 0) {
printInvalidConfig(scope, path, result.errors);
} else {
if (addSchemaIfMissing(path)) {
console.log(`\nAdded $schema to ${scope.toLowerCase()} config.`);
}
printValidConfig(scope, path, result);
}
}
if (hasErrors) {
console.error('\nConfig validation failed.');
return 1;
}
console.log('\nAll configs valid.');
return 0;
}