autoMode.ts
cli/handlers/autoMode.ts
171
Lines
5742
Bytes
3
Exports
6
Imports
10
Keywords
What this is
This page documents one file from the repository and includes its full source so you can read it without leaving the docs site.
Beginner explanation
This file is one piece of the larger system. Its name, directory, imports, and exports show where it fits. Start by reading the exports and related files first.
How it is used
Start from the exports list and related files. Those are the easiest clues for where this file fits into the system.
Expert explanation
Architecturally, this file intersects with modes. It contains 171 lines, 6 detected imports, and 3 detected exports.
Important relationships
Detected exports
autoModeDefaultsHandlerautoModeConfigHandlerautoModeCritiqueHandler
Keywords
rulesconfigdefaultsclassifierallowsoft_denyenvironmentuserautomode
Detected imports
../../utils/errors.js../../utils/model/model.js../../utils/permissions/yoloClassifier.js../../utils/settings/settings.js../../utils/sideQuery.js../../utils/slowOperations.js
Source notes
This page embeds the full file contents. Small or leaf files are still indexed honestly instead of being over-explained.
Full source
/**
* 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<void> {
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' +
'<classifier_system_prompt>\n' +
classifierPrompt +
'\n</classifier_system_prompt>\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'
)
}