Skip to content
Closed
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
5 changes: 5 additions & 0 deletions .changeset/doctor-windows-path-separators.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": patch
---

Fix mixed path separators in `kimi doctor` output on Windows (config.toml showed `C:/...` while tui.toml showed `C:\...`)
9 changes: 7 additions & 2 deletions apps/kimi-code/src/cli/sub/doctor.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { existsSync } from 'node:fs';
import { readFile } from 'node:fs/promises';
import { isAbsolute, resolve } from 'node:path';
import { isAbsolute, normalize, resolve } from 'node:path';

import {
createKimiConfigRpc,
Expand Down Expand Up @@ -259,7 +259,12 @@ function formatFailure(results: readonly CheckResult[], issueCount: number): str
function formatResults(results: readonly CheckResult[]): string[] {
const lines: string[] = [];
for (const result of results) {
lines.push(`${result.status} ${result.label.padEnd(12)} ${result.path}`);
// Normalize the path to the platform's native separators so config.toml
// (resolved via `pathe`, always `/`) and tui.toml (resolved via
// `node:path`, native separator) render consistently. Without this,
// Windows users see both `C:/Users/me/.kimi-code/config.toml` and
// `C:\Users\me\.kimi-code\tui.toml` in the same output.
lines.push(`${result.status} ${result.label.padEnd(12)} ${normalize(result.path)}`);
if (result.message !== undefined) {
for (const line of result.message.split('\n')) {
lines.push(` ${line}`);
Expand Down
18 changes: 15 additions & 3 deletions apps/kimi-code/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* outer update preflight, then delegates to the requested UI runner.
*/

import { normalize } from 'node:path';

import {
createKimiHarness,
flushDiagnosticLogs,
Expand All @@ -21,6 +23,16 @@ import {
withTelemetryContext,
} from '@moonshot-ai/kimi-telemetry';

/**
* The SDK resolves the log path via `pathe` (forward slashes everywhere)
* which produces `C:/Users/.../kimi-code.log` on Windows. Normalize to the
* platform-native separator before showing the path to the user so it
* matches what they'd see in File Explorer / their shell.
*/
function formatLogPathForUser(homeDir: string): string {
return normalize(resolveGlobalLogPath(homeDir));
}

import { createProgram } from './cli/commands';
import type { CLIOptions } from './cli/options';
import { OptionConflictError, validateOptions } from './cli/options';
Expand Down Expand Up @@ -142,15 +154,15 @@ export function main(): void {
operation,
}),
);
process.stderr.write(`See log: ${resolveGlobalLogPath(resolveKimiHome())}\n`);
process.stderr.write(`See log: ${formatLogPathForUser(resolveKimiHome())}\n`);
process.exit(1);
});
},
() => {
void handleMigrateCommand(version).catch(async (error: unknown) => {
await logStartupFailure('run migration', error);
process.stderr.write(formatStartupError(error, { operation: 'run migration' }));
process.stderr.write(`See log: ${resolveGlobalLogPath(resolveKimiHome())}\n`);
process.stderr.write(`See log: ${formatLogPathForUser(resolveKimiHome())}\n`);
process.exit(1);
});
},
Expand All @@ -165,7 +177,7 @@ export function main(): void {
void handleUpgradeCommand(version).catch(async (error: unknown) => {
await logStartupFailure('upgrade', error);
process.stderr.write(formatStartupError(error, { operation: 'upgrade' }));
process.stderr.write(`See log: ${resolveGlobalLogPath(resolveKimiHome())}\n`);
process.stderr.write(`See log: ${formatLogPathForUser(resolveKimiHome())}\n`);
process.exit(1);
});
},
Expand Down
29 changes: 29 additions & 0 deletions apps/kimi-code/test/cli/doctor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,4 +267,33 @@ max_context_size = "large"
expect(err).toContain('Validation issues:');
expect(err).toContain('models.kimi.max_context_size:');
});

it('normalizes path separators in formatted output', async () => {
// Regression: config.toml is resolved via `pathe` (always `/`) while
// tui.toml is resolved via `node:path` (native separators on Windows).
// The formatted output must use one consistent separator — otherwise
// Windows users see a mix of `C:/Users/me/...` and `C:\Users\me\...`
// in the same `kimi doctor` block.
const { deps, stdout } = makeDeps();
const code = await handleDoctor(
{
...deps,
// Force a `/`-only path here to simulate what `pathe` would yield.
defaultConfigPath: () => 'C:/fixture/.kimi-code/config.toml',
defaultTuiConfigPath: () => 'C:\\fixture\\.kimi-code\\tui.toml',
fileExists: () => false,
},
{},
);

expect(code).toBe(0);
const out = stdout.join('');
// On Windows, both lines should use backslashes; on POSIX, both forward
// slashes. Path.sep is what `normalize` aligns to.
const sep = process.platform === 'win32' ? '\\' : '/';
const expectedConfig = `C:${sep}fixture${sep}.kimi-code${sep}config.toml`;
const expectedTui = `C:${sep}fixture${sep}.kimi-code${sep}tui.toml`;
expect(out).toContain(expectedConfig);
expect(out).toContain(expectedTui);
});
});