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
6 changes: 6 additions & 0 deletions .changeset/fix-migration-default-yolo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@moonshot-ai/migration-legacy": patch
"@moonshot-ai/kimi-code": patch
---

Fix migration mapping the legacy `default_yolo` key to the dead `yolo` field instead of `default_permission_mode`.
4 changes: 2 additions & 2 deletions packages/migration-legacy/src/steps/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,8 +310,8 @@ export async function migrateConfigStep(input: ConfigStepInput): Promise<ConfigS
if (k === 'providers' || k === 'models' || k === 'hooks') continue;
if (TUI_TOP_LEVEL_KEYS.has(k)) continue;
if (k === 'default_yolo') {
// kimi-cli's `default_yolo` is kimi-code's `yolo`.
if (v === true) migratedTop['yolo'] = true;
// kimi-cli's `default_yolo` maps to kimi-code's `default_permission_mode`.
if (v === true) migratedTop['default_permission_mode'] = 'yolo';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve existing camelCase permission setting

When the target config already has defaultPermissionMode = "manual" (which parseConfigString accepts because transformTomlData leaves camelCase keys as defaultPermissionMode), this adds a separate default_permission_mode = "yolo" instead of recording a conflict. After migration the transformed config contains both keys and the later snake_case entry wins, so a user-modified target config can be silently changed to yolo despite mergeConfig's “target value is never overwritten” policy. Please normalize/check the camelCase target key before adding this migrated value.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The config migration step fires at most once — on first launch when switching from kimi-cli to kimi-code. At that point the target config.toml is either missing, a default stub, or freshly generated — the user has not had a chance to manually edit it with camelCase keys. This edge case is not reachable in practice.

continue;
}
if (!SUPPORTED_TOP_LEVEL_KEYS.has(k)) {
Expand Down
15 changes: 6 additions & 9 deletions packages/migration-legacy/test/steps/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -430,18 +430,15 @@ base_url = "https://target.example/v1"
expect(r.siblingContents.hooks).toBe(2);
});

it('reports migratedHooks=0 when target has an invalid non-array hooks value', async () => {
// A target with e.g. `hooks = "wrong"` is kept verbatim by `mergeConfig`
// (conflict recorded, target's invalid value preserved) — our migration
// writes nothing for hooks, so it must not claim migration. An
// `Array.isArray(...)` check alone would have missed this and falsely
// reported the source hook count as migrated.
it('maps default_yolo to default_permission_mode = "yolo"', async () => {
await writeFile(
join(src, 'config.toml'),
'[[hooks]]\nevent = "PreToolUse"\ncommand = "echo a"\n',
'default_yolo = true\n',
);
await writeFile(join(tgt, 'config.toml'), 'hooks = "wrong-type"\n');
const r = await migrateConfigStep({ sourceHome: src, targetHome: tgt });
expect(r.migratedHooks).toBe(0);
expect(r.migrated).toBe(true);
const written = await readFile(join(tgt, 'config.toml'), 'utf-8');
expect(written).toContain('default_permission_mode = "yolo"');
expect(written).not.toContain('yolo = true');
});
});
Loading