mirror of
https://github.com/jarmuine/claude-code.git
synced 2026-04-26 14:51:24 +03:00
asdf
Squash the current repository state back into one baseline commit while preserving the README reframing and repository contents. Constraint: User explicitly requested a single squashed commit with subject "asdf" Confidence: high Scope-risk: broad Reversibility: clean Directive: This commit intentionally rewrites published history; coordinate before future force-pushes Tested: git status clean; local history rewritten to one commit; force-pushed main to origin and instructkr Not-tested: Fresh clone verification after push
This commit is contained in:
170
src/cli/handlers/autoMode.ts
Normal file
170
src/cli/handlers/autoMode.ts
Normal file
@@ -0,0 +1,170 @@
|
||||
/**
|
||||
* 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'
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user