diff --git a/apps/kimi-code/src/cli/experimental-v2.ts b/apps/kimi-code/src/cli/experimental-v2.ts index 09deacc9c..8ccb56ebd 100644 --- a/apps/kimi-code/src/cli/experimental-v2.ts +++ b/apps/kimi-code/src/cli/experimental-v2.ts @@ -1,7 +1,7 @@ /** * Agent engine routing gates for the CLI surfaces. * - * `kimi -p`, the interactive TUI, and `kimi doctor` use the native + * `kimi -p` and the interactive TUI use the native * agent-core-v2 path by default. A truthy `KIMI_CODE_LEGACY_FLAG` selects the * legacy agent-core-backed path instead. `KIMI_CODE_EXPERIMENTAL_FLAG` remains * the master switch for experimental features within either engine; it does diff --git a/apps/kimi-code/src/cli/sub/doctor.ts b/apps/kimi-code/src/cli/sub/doctor.ts index 8081c6e0e..cbbfa3000 100644 --- a/apps/kimi-code/src/cli/sub/doctor.ts +++ b/apps/kimi-code/src/cli/sub/doctor.ts @@ -2,15 +2,10 @@ import { existsSync } from 'node:fs'; import { readFile } from 'node:fs/promises'; import { isAbsolute, resolve } from 'node:path'; -import { - createKimiConfigRpc, - type KimiConfigRpc, - type KimiConfigValidationIssue, -} from '@moonshot-ai/kimi-code-sdk'; +import { resolveConfigPath, type KimiConfigValidationIssue } from '@moonshot-ai/kimi-code-sdk'; import type { Command } from 'commander'; import { z } from 'zod'; -import { isKimiV2Enabled } from '#/cli/experimental-v2'; import { getTuiConfigPath, parseTuiConfig } from '#/tui/config'; interface WritableLike { @@ -26,7 +21,6 @@ export interface DoctorDeps { readonly stdout: WritableLike; readonly stderr: WritableLike; readonly exit: (code: number) => never; - readonly configRpc?: KimiConfigRpc; readonly fileExists?: (path: string) => boolean; readonly readTextFile?: (path: string) => Promise; readonly validateConfigToml?: (text: string, path: string) => MaybePromise; @@ -115,15 +109,9 @@ async function runDoctorCommand( } function resolveDeps(deps: Partial | DoctorDeps | undefined): ResolvedDoctorDeps { - let configRpc = deps?.configRpc; - const getConfigRpc = (): KimiConfigRpc => { - configRpc ??= createKimiConfigRpc(); - return configRpc; - }; - return { cwd: deps?.cwd ?? (() => process.cwd()), - defaultConfigPath: deps?.defaultConfigPath ?? (() => getConfigRpc().resolveConfigPath()), + defaultConfigPath: deps?.defaultConfigPath ?? (() => resolveConfigPath({})), defaultTuiConfigPath: deps?.defaultTuiConfigPath ?? getTuiConfigPath, stdout: deps?.stdout ?? process.stdout, stderr: deps?.stderr ?? process.stderr, @@ -133,15 +121,8 @@ function resolveDeps(deps: Partial | DoctorDeps | undefined): Resolv validateConfigToml: deps?.validateConfigToml ?? (async (text, filePath) => { - if (isKimiV2Enabled()) { - // Default v2 route (same engine gate as `kimi -p`): validate with - // the agent-core-v2 section registry instead of the legacy schema. - // Loaded lazily so the v2 module graph stays off the legacy path. - const { validateConfigTomlV2 } = await import('../v2/validate-config'); - return validateConfigTomlV2(text, filePath); - } - await getConfigRpc().validateConfigToml({ text, filePath }); - return undefined; + const { validateConfigTomlV2 } = await import('../v2/validate-config'); + return validateConfigTomlV2(text, filePath); }), }; } diff --git a/apps/kimi-code/test/cli/doctor.test.ts b/apps/kimi-code/test/cli/doctor.test.ts index 6422a9acc..71d31d18f 100644 --- a/apps/kimi-code/test/cli/doctor.test.ts +++ b/apps/kimi-code/test/cli/doctor.test.ts @@ -14,7 +14,6 @@ import { let dir: string; beforeEach(async () => { - vi.stubEnv('KIMI_CODE_LEGACY_FLAG', ''); dir = join(tmpdir(), `kimi-doctor-${Date.now()}-${Math.random().toString(36).slice(2)}`); await mkdir(dir, { recursive: true }); }); @@ -103,25 +102,56 @@ describe('kimi doctor', () => { expect(out).toContain('built-in defaults will apply'); }); - it('uses the legacy validator when legacy wins over the experimental flag', async () => { + it('keeps v2 validation when the legacy flag is set', async () => { const configPath = join(dir, 'config.toml'); - const text = '[providers.kimi]\ntype = "kimi"\n'; - await writeFile(configPath, text, 'utf-8'); - vi.stubEnv('KIMI_CODE_LEGACY_FLAG', '1'); - vi.stubEnv('KIMI_CODE_EXPERIMENTAL_FLAG', '1'); - const validateConfigToml = vi.fn(async () => undefined); - const { deps } = makeDeps(); + await writeFile( + configPath, + ` +default_model = "kimi" - const code = await handleDoctor( - { - ...deps, - configRpc: { validateConfigToml } as unknown as NonNullable, - }, - { target: 'config' }, +[providers.kimi] +type = "kimi" +base_url = "https://api.example.com/v1" +api_key = "YOUR_API_KEY" + +[models.kimi] +provider = "kimi" +model = "kimi" +protocol = "openai" +max_context_size = 262144 +`, + 'utf-8', ); + vi.stubEnv('KIMI_CODE_LEGACY_FLAG', '1'); + const { deps, stdout, stderr } = makeDeps(); + + const code = await handleDoctor(deps, { target: 'config' }); expect(code).toBe(0); - expect(validateConfigToml).toHaveBeenCalledWith({ text, filePath: configPath }); + expect(stderr.join('')).toBe(''); + expect(stdout.join('')).toContain(`OK config.toml ${configPath}`); + }); + + it('reports schema-invalid sections with the v2 engine when the legacy flag is set', async () => { + await writeFile( + join(dir, 'config.toml'), + ` +[models.kimi] +provider = "kimi" +model = "kimi" +max_context_size = "large" +`, + 'utf-8', + ); + vi.stubEnv('KIMI_CODE_LEGACY_FLAG', '1'); + const { deps, stderr } = makeDeps(); + + const code = await handleDoctor(deps, { target: 'config' }); + + expect(code).toBe(1); + const err = stderr.join(''); + expect(err).toContain('Validation issues:'); + expect(err).toContain('models.kimi.max_context_size:'); }); it('checks only config.toml when the config target is selected', async () => {