mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-03-03 15:43:11 +08:00
feat: enhance hook templates management by adding protection category and improving command safety
This commit is contained in:
@@ -274,7 +274,8 @@ function getCategoryName(category: TemplateCategory, formatMessage: ReturnType<t
|
||||
notification: formatMessage({ id: 'cliHooks.templates.categories.notification' }),
|
||||
indexing: formatMessage({ id: 'cliHooks.templates.categories.indexing' }),
|
||||
automation: formatMessage({ id: 'cliHooks.templates.categories.automation' }),
|
||||
utility: formatMessage({ id: 'cliHooks.templates.categories.utility' })
|
||||
utility: formatMessage({ id: 'cliHooks.templates.categories.utility' }),
|
||||
protection: formatMessage({ id: 'cliHooks.templates.categories.protection' }),
|
||||
};
|
||||
return names[category];
|
||||
}
|
||||
@@ -304,7 +305,7 @@ export function HookQuickTemplates({
|
||||
}, []);
|
||||
|
||||
// Define category order
|
||||
const categoryOrder: TemplateCategory[] = ['notification', 'indexing', 'automation'];
|
||||
const categoryOrder: TemplateCategory[] = ['notification', 'indexing', 'automation', 'protection', 'utility'];
|
||||
|
||||
const handleInstall = async (templateId: string) => {
|
||||
await onInstallTemplate(templateId);
|
||||
|
||||
@@ -79,12 +79,6 @@ interface SkillContextConfig {
|
||||
// All templates use `ccw hook template exec <id> --stdin` format
|
||||
// This avoids Windows Git Bash quote handling issues
|
||||
|
||||
interface HookTemplate {
|
||||
event: string;
|
||||
matcher: string;
|
||||
timeout?: number;
|
||||
}
|
||||
|
||||
// Template IDs that map to backend templates
|
||||
const TEMPLATE_IDS = {
|
||||
'memory-update-queue': 'memory-auto-compress',
|
||||
@@ -106,72 +100,6 @@ const DANGER_OPTIONS = [
|
||||
{ id: 'permission-change', templateId: 'danger-permission-change', labelKey: 'cliHooks.wizards.dangerProtection.options.permissionChange', descKey: 'cliHooks.wizards.dangerProtection.options.permissionChangeDesc' },
|
||||
] as const;
|
||||
|
||||
// ========== convertToClaudeCodeFormat (ported from old hook-manager.js) ==========
|
||||
|
||||
function convertToClaudeCodeFormat(hookData: {
|
||||
command: string;
|
||||
args: string[];
|
||||
matcher?: string;
|
||||
timeout?: number;
|
||||
}): Record<string, unknown> {
|
||||
let commandStr = hookData.command || '';
|
||||
|
||||
if (hookData.args && Array.isArray(hookData.args)) {
|
||||
if (commandStr === 'bash' && hookData.args.length >= 2 && hookData.args[0] === '-c') {
|
||||
const script = hookData.args[1];
|
||||
const escapedScript = script.replace(/'/g, "'\\''");
|
||||
commandStr = `bash -c '${escapedScript}'`;
|
||||
if (hookData.args.length > 2) {
|
||||
const additionalArgs = hookData.args.slice(2).map(arg =>
|
||||
arg.includes(' ') && !arg.startsWith('"') && !arg.startsWith("'")
|
||||
? `"${arg.replace(/"/g, '\\"')}"`
|
||||
: arg
|
||||
);
|
||||
commandStr += ' ' + additionalArgs.join(' ');
|
||||
}
|
||||
} else if (commandStr === 'node' && hookData.args.length >= 2 && hookData.args[0] === '-e') {
|
||||
const script = hookData.args[1];
|
||||
const isWindows = typeof navigator !== 'undefined' && navigator.userAgent.includes('Win');
|
||||
if (isWindows) {
|
||||
const escapedScript = script.replace(/"/g, '\\"');
|
||||
commandStr = `node -e "${escapedScript}"`;
|
||||
} else {
|
||||
const escapedScript = script.replace(/'/g, "'\\''");
|
||||
commandStr = `node -e '${escapedScript}'`;
|
||||
}
|
||||
if (hookData.args.length > 2) {
|
||||
const additionalArgs = hookData.args.slice(2).map(arg =>
|
||||
arg.includes(' ') && !arg.startsWith('"') && !arg.startsWith("'")
|
||||
? `"${arg.replace(/"/g, '\\"')}"`
|
||||
: arg
|
||||
);
|
||||
commandStr += ' ' + additionalArgs.join(' ');
|
||||
}
|
||||
} else {
|
||||
const quotedArgs = hookData.args.map(arg =>
|
||||
arg.includes(' ') && !arg.startsWith('"') && !arg.startsWith("'")
|
||||
? `"${arg.replace(/"/g, '\\"')}"`
|
||||
: arg
|
||||
);
|
||||
commandStr = `${commandStr} ${quotedArgs.join(' ')}`.trim();
|
||||
}
|
||||
}
|
||||
|
||||
const converted: Record<string, unknown> = {
|
||||
hooks: [{
|
||||
type: 'command',
|
||||
command: commandStr,
|
||||
...(hookData.timeout ? { timeout: Math.ceil(hookData.timeout / 1000) } : {}),
|
||||
}],
|
||||
};
|
||||
|
||||
if (hookData.matcher) {
|
||||
converted.matcher = hookData.matcher;
|
||||
}
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
// ========== Wizard Definitions ==========
|
||||
|
||||
const WIZARD_METADATA = {
|
||||
|
||||
@@ -28,7 +28,6 @@ import { Card } from '@/components/ui/Card';
|
||||
import { Badge } from '@/components/ui/Badge';
|
||||
import { HookCard, HookFormDialog, HookQuickTemplates, HookWizard, type HookCardData, type HookFormData, type HookTriggerType, HOOK_TEMPLATES, type WizardType } from '@/components/hook';
|
||||
import { useHooks, useToggleHook } from '@/hooks';
|
||||
import { installHookTemplate } from '@/lib/api';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
// ========== Types ==========
|
||||
|
||||
Reference in New Issue
Block a user