Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@verboo/code",
"version": "0.15.24",
"version": "0.15.25",
"description": "Verboo Code — coding agent for the Verboo platform",
"type": "module",
"bin": {
Expand Down
2 changes: 1 addition & 1 deletion src/components/LanguagePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export function LanguagePicker(t0) {
}
let t7;
if ($[10] === Symbol.for("react.memo_cache_sentinel")) {
t7 = <Text dimColor={true}>Leave empty for default (English)</Text>;
t7 = <Text dimColor={true}>Leave empty to respond in the user's language</Text>;
$[10] = t7;
} else {
t7 = $[10];
Expand Down
123 changes: 123 additions & 0 deletions src/constants/languageAnchor.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { afterEach, beforeEach, expect, test } from 'bun:test'
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'

// MACRO is replaced at build time by Bun.define but not in test mode.
// Define it globally so tests that import modules using MACRO don't crash.
;(globalThis as Record<string, unknown>).MACRO = {
VERSION: '99.0.0',
DISPLAY_VERSION: '0.0.0-test',
BUILD_TIME: new Date().toISOString(),
ISSUES_EXPLAINER: 'report the issue at https://github.com/verbeux-ai/code/issues',
PACKAGE_URL: '@verboo/code',
NATIVE_PACKAGE_URL: undefined,
}

import { setClaudeConfigHomeDirForTesting } from '../utils/envUtils.js'
import { resetSettingsCache } from '../utils/settings/settingsCache.js'
import { clearSystemPromptSections } from './systemPromptSections.js'
import { getSystemPrompt } from './prompts.js'

const MIRROR_SNIPPET =
"Always respond in the same language as the user's most recent message"
const SECTION_HEADER = '# Language'

let dir: string

beforeEach(() => {
dir = mkdtempSync(join(tmpdir(), 'lang-anchor-'))
setClaudeConfigHomeDirForTesting(dir)
resetSettingsCache()
clearSystemPromptSections()
})

afterEach(() => {
setClaudeConfigHomeDirForTesting(undefined)
resetSettingsCache()
clearSystemPromptSections()
rmSync(dir, { recursive: true, force: true })
delete process.env.CLAUDE_CODE_SIMPLE
})

test('default (unset language) injects the mirror directive', async () => {
const text = (await getSystemPrompt([], 'test-model')).join('\n')

expect(text).toContain(SECTION_HEADER)
expect(text).toContain(MIRROR_SNIPPET)
})

test('default (unset language) never leaks an undefined preference', async () => {
const text = (await getSystemPrompt([], 'test-model')).join('\n')

expect(text).not.toContain('Always respond in undefined')
expect(text).not.toContain('Use undefined')
})

test('the default directive is locale-agnostic — pins no language', async () => {
const text = (await getSystemPrompt([], 'test-model')).join('\n')

expect(text).not.toContain('Always respond in English')
expect(text).not.toContain('Always respond in Portuguese')
expect(text).not.toContain('Always respond in Spanish')
})

test('explicit language override replaces the mirror directive', async () => {
writeFileSync(
join(dir, 'settings.json'),
JSON.stringify({ language: 'Brazilian Portuguese' }),
)
resetSettingsCache()
clearSystemPromptSections()

const text = (await getSystemPrompt([], 'test-model')).join('\n')

expect(text).toContain('Always respond in Brazilian Portuguese.')
expect(text).not.toContain(MIRROR_SNIPPET)
})

test('any configured language value passes through verbatim', async () => {
writeFileSync(
join(dir, 'settings.json'),
JSON.stringify({ language: 'japanese' }),
)
resetSettingsCache()
clearSystemPromptSections()

const text = (await getSystemPrompt([], 'test-model')).join('\n')

expect(text).toContain('Always respond in japanese.')
expect(text).not.toContain(MIRROR_SNIPPET)
})

test('simple mode system prompt also includes the language anchor', async () => {
process.env.CLAUDE_CODE_SIMPLE = '1'
clearSystemPromptSections()

const text = (await getSystemPrompt([], 'test-model')).join('\n')

expect(text).toContain(MIRROR_SNIPPET)
})

test('simple mode respects an explicit language override', async () => {
writeFileSync(
join(dir, 'settings.json'),
JSON.stringify({ language: 'english' }),
)
resetSettingsCache()
process.env.CLAUDE_CODE_SIMPLE = '1'
clearSystemPromptSections()

const text = (await getSystemPrompt([], 'test-model')).join('\n')

expect(text).toContain('Always respond in english.')
expect(text).not.toContain(MIRROR_SNIPPET)
})

test('mirror section keeps technical terms exempt (no forced translation of code)', async () => {
const text = (await getSystemPrompt([], 'test-model')).join('\n')

expect(text).toContain(
'Technical terms and code identifiers should remain in their original form',
)
})
12 changes: 10 additions & 2 deletions src/constants/prompts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,10 +139,17 @@ function getAntModelOverrideSection(): string | null {
return getAntModelOverrideConfig()?.defaultSystemPromptSuffix || null
}

// Locale-agnostic default: mirror the user's language instead of pinning a
// fixed locale — Verboo Code is an international product.
const MIRROR_USER_LANGUAGE_SECTION = `Always respond in the same language as the user's most recent message. Use that language for all explanations, comments, and communications with the user. Technical terms and code identifiers should remain in their original form.`

function getLanguageSection(
languagePreference: string | undefined,
): string | null {
if (!languagePreference) return null
if (!languagePreference) {
return `# Language
${MIRROR_USER_LANGUAGE_SECTION}`
}

return `# Language
Always respond in ${languagePreference}. Use ${languagePreference} for all explanations, comments, and communications with the user. Technical terms and code identifiers should remain in their original form.`
Expand Down Expand Up @@ -450,7 +457,8 @@ export async function getSystemPrompt(
if (isEnvTruthy(process.env.CLAUDE_CODE_SIMPLE)) {
return [
`You are Verboo Code, an open-source coding agent and CLI.\n\nCWD: ${getCwd()}\nDate: ${getSessionStartDate()}`,
]
getLanguageSection(getInitialSettings().language),
].filter(s => s !== null)
}

const cwd = getCwd()
Expand Down
Loading