/** * Auto mode subcommand handlers — dump default/merged classifier rules and * critique user-written rules. Dynamically imported when `claude auto-mode ...` runs. */ import { errorMessage } from '../../utils/errors.js' import { getMainLoopModel, parseUserSpecifiedModel, } from '../../utils/model/model.js' import { type AutoModeRules, buildDefaultExternalSystemPrompt, getDefaultExternalAutoModeRules, } from '../../utils/permissions/yoloClassifier.js' import { getAutoModeConfig } from '../../utils/settings/settings.js' import { sideQuery } from '../../utils/sideQuery.js' import { jsonStringify } from '../../utils/slowOperations.js' function writeRules(rules: AutoModeRules): void { process.stdout.write(jsonStringify(rules, null, 2) + '\n') } export function autoModeDefaultsHandler(): void { writeRules(getDefaultExternalAutoModeRules()) } /** * Dump the effective auto mode config: user settings where provided, external * defaults otherwise. Per-section REPLACE semantics — matches how * buildYoloSystemPrompt resolves the external template (a non-empty user * section replaces that section's defaults entirely; an empty/absent section * falls through to defaults). */ export function autoModeConfigHandler(): void { const config = getAutoModeConfig() const defaults = getDefaultExternalAutoModeRules() writeRules({ allow: config?.allow?.length ? config.allow : defaults.allow, soft_deny: config?.soft_deny?.length ? config.soft_deny : defaults.soft_deny, environment: config?.environment?.length ? config.environment : defaults.environment, }) } const CRITIQUE_SYSTEM_PROMPT = 'You are an expert reviewer of auto mode classifier rules for Claude Code.\n' + '\n' + 'Claude Code has an "auto mode" that uses an AI classifier to decide whether ' + 'tool calls should be auto-approved or require user confirmation. Users can ' + 'write custom rules in three categories:\n' + '\n' + '- **allow**: Actions the classifier should auto-approve\n' + '- **soft_deny**: Actions the classifier should block (require user confirmation)\n' + "- **environment**: Context about the user's setup that helps the classifier make decisions\n" + '\n' + "Your job is to critique the user's custom rules for clarity, completeness, " + 'and potential issues. The classifier is an LLM that reads these rules as ' + 'part of its system prompt.\n' + '\n' + 'For each rule, evaluate:\n' + '1. **Clarity**: Is the rule unambiguous? Could the classifier misinterpret it?\n' + "2. **Completeness**: Are there gaps or edge cases the rule doesn't cover?\n" + '3. **Conflicts**: Do any of the rules conflict with each other?\n' + '4. **Actionability**: Is the rule specific enough for the classifier to act on?\n' + '\n' + 'Be concise and constructive. Only comment on rules that could be improved. ' + 'If all rules look good, say so.' export async function autoModeCritiqueHandler(options: { model?: string }): Promise { const config = getAutoModeConfig() const hasCustomRules = (config?.allow?.length ?? 0) > 0 || (config?.soft_deny?.length ?? 0) > 0 || (config?.environment?.length ?? 0) > 0 if (!hasCustomRules) { process.stdout.write( 'No custom auto mode rules found.\n\n' + 'Add rules to your settings file under autoMode.{allow, soft_deny, environment}.\n' + 'Run `claude auto-mode defaults` to see the default rules for reference.\n', ) return } const model = options.model ? parseUserSpecifiedModel(options.model) : getMainLoopModel() const defaults = getDefaultExternalAutoModeRules() const classifierPrompt = buildDefaultExternalSystemPrompt() const userRulesSummary = formatRulesForCritique('allow', config?.allow ?? [], defaults.allow) + formatRulesForCritique( 'soft_deny', config?.soft_deny ?? [], defaults.soft_deny, ) + formatRulesForCritique( 'environment', config?.environment ?? [], defaults.environment, ) process.stdout.write('Analyzing your auto mode rules…\n\n') let response try { response = await sideQuery({ querySource: 'auto_mode_critique', model, system: CRITIQUE_SYSTEM_PROMPT, skipSystemPromptPrefix: true, max_tokens: 4096, messages: [ { role: 'user', content: 'Here is the full classifier system prompt that the auto mode classifier receives:\n\n' + '\n' + classifierPrompt + '\n\n\n' + "Here are the user's custom rules that REPLACE the corresponding default sections:\n\n" + userRulesSummary + '\nPlease critique these custom rules.', }, ], }) } catch (error) { process.stderr.write( 'Failed to analyze rules: ' + errorMessage(error) + '\n', ) process.exitCode = 1 return } const textBlock = response.content.find(block => block.type === 'text') if (textBlock?.type === 'text') { process.stdout.write(textBlock.text + '\n') } else { process.stdout.write('No critique was generated. Please try again.\n') } } function formatRulesForCritique( section: string, userRules: string[], defaultRules: string[], ): string { if (userRules.length === 0) return '' const customLines = userRules.map(r => '- ' + r).join('\n') const defaultLines = defaultRules.map(r => '- ' + r).join('\n') return ( '## ' + section + ' (custom rules replacing defaults)\n' + 'Custom:\n' + customLines + '\n\n' + 'Defaults being replaced:\n' + defaultLines + '\n\n' ) }