fix: handle windows session workdir separators (#486)

* fix: handle windows session workdir separators

* test: cover Windows session workdir normalization

---------

Co-authored-by: qkunio <qkunio@163.com>
Co-authored-by: liruifengv <liruifeng1024@gmail.com>
This commit is contained in:
Qkunio 2026-06-08 15:10:30 +08:00 committed by GitHub
parent 8d0c91faa1
commit 0fe13173f4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 55 additions and 2 deletions

View file

@ -71,6 +71,7 @@
"chalk": "^5.4.1",
"cli-highlight": "^2.1.11",
"commander": "^13.1.0",
"pathe": "^2.0.3",
"semver": "^7.7.4",
"smol-toml": "^1.6.1",
"zod": "^4.3.6"

View file

@ -17,6 +17,7 @@ import {
type SessionStatus,
type TelemetryClient,
} from '@moonshot-ai/kimi-code-sdk';
import { resolve } from 'pathe';
import { CLI_SHUTDOWN_TIMEOUT_MS } from '#/constant/app';
import { experimentalFeatureMap } from '#/utils/experimental-features';
@ -229,7 +230,7 @@ async function resolvePromptSession(
if (target === undefined) {
throw new Error(`Session "${opts.session}" not found.`);
}
if (target.workDir !== workDir) {
if (resolve(target.workDir) !== resolve(workDir)) {
stderr.write(
`${chalk.hex('#E8A838')(
`Session "${opts.session}" was created under a different directory.\n` +

View file

@ -22,6 +22,7 @@ import type {
Session,
} from '@moonshot-ai/kimi-code-sdk';
import chalk from 'chalk';
import { resolve } from 'pathe';
import type { CLIOptions } from '#/cli/options';
import { MigrationScreenComponent, type MigrationScreenResult } from '#/migration/index';
@ -512,7 +513,7 @@ export class KimiTUI {
if (target === undefined) {
throw new Error(`Session "${startup.sessionFlag}" not found.`);
}
if (target.workDir !== workDir) {
if (resolve(target.workDir) !== resolve(workDir)) {
this.state.ui.stop();
process.stderr.write(
`${chalk.hex(this.state.theme.colors.warning)(

View file

@ -440,6 +440,28 @@ describe('runPrompt', () => {
expect(mocks.session.setPermission).toHaveBeenNthCalledWith(2, 'manual');
});
it('allows resuming a concrete session when Windows workdir uses backslashes', async () => {
const cwd = vi.spyOn(process, 'cwd').mockReturnValue(String.raw`C:\Users\kimi\project`);
mocks.harnessListSessions.mockResolvedValueOnce([
{ id: 'ses_existing', workDir: 'C:/Users/kimi/project' },
]);
try {
await runPrompt(opts({ session: 'ses_existing' }), '1.2.3-test', {
stdout: { write: vi.fn(() => true) },
stderr: { write: vi.fn(() => true) },
});
} finally {
cwd.mockRestore();
}
expect(mocks.harnessListSessions).toHaveBeenCalledWith({
sessionId: 'ses_existing',
workDir: String.raw`C:\Users\kimi\project`,
});
expect(mocks.harnessResumeSession).toHaveBeenCalledWith({ id: 'ses_existing' });
});
it('applies the CLI model override to resumed prompt sessions', async () => {
await runPrompt(opts({ session: 'ses_existing', model: 'kimi-code/k2.5' }), '1.2.3-test', {
stdout: { write: vi.fn(() => true) },

View file

@ -897,6 +897,31 @@ describe("KimiTUI startup", () => {
expect(uiContainsFooter(driver)).toBe(true);
});
it("resumes a startup session when Windows workdir uses backslashes", async () => {
const session = makeSession({ id: "ses-target" });
const harness = makeHarness(session, {
listSessions: vi.fn(async () => [
{ id: "ses-target", workDir: "C:/Users/kimi/project" },
]),
});
const driver = makeDriver(
harness,
{
...makeStartupInput({ session: "ses-target" }),
workDir: String.raw`C:\Users\kimi\project`,
},
);
await expect(driver.init()).resolves.toBe(true);
expect(harness.listSessions).toHaveBeenCalledWith({
sessionId: "ses-target",
workDir: String.raw`C:\Users\kimi\project`,
});
expect(harness.resumeSession).toHaveBeenCalledWith({ id: "ses-target" });
expect(driver.state.appState.sessionId).toBe("ses-target");
});
});
function uiContainsFooter(driver: StartupDriver): boolean {

3
pnpm-lock.yaml generated
View file

@ -87,6 +87,9 @@ importers:
commander:
specifier: ^13.1.0
version: 13.1.0
pathe:
specifier: ^2.0.3
version: 2.0.3
semver:
specifier: ^7.7.4
version: 7.7.4