-
Notifications
You must be signed in to change notification settings - Fork 53
fix: copy user skills and clean up OAuth UX #31
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
bbc8cd4
2f11ab4
b58a772
c4371e0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@moonshot-ai/kimi-code": patch | ||
| --- | ||
|
|
||
| Stop mentioning OAuth credentials in the migration UI — they are never migrated, so the previous "needs /login" notice misread as a failure. OAuth-only installs no longer trigger the migration screen. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| --- | ||
| "@moonshot-ai/migration-legacy": patch | ||
| "@moonshot-ai/kimi-code": patch | ||
| --- | ||
|
|
||
| Migrate user skills from `~/.kimi/skills/` to `~/.kimi-code/skills/` during the first-launch migration; existing target skills are kept. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| import { cp, mkdir, readdir, rename, rm, stat } from 'node:fs/promises'; | ||
| import { existsSync } from 'node:fs'; | ||
| import { join } from 'node:path'; | ||
| import { sourceSkillsDir, targetSkillsDir } from '../paths.js'; | ||
|
|
||
| export interface SkillsStepInput { | ||
| readonly sourceHome: string; | ||
| readonly targetHome: string; | ||
| } | ||
|
|
||
| export interface SkillsStepResult { | ||
| readonly copied: number; | ||
| readonly skippedExisting: number; | ||
| } | ||
|
|
||
| /** | ||
| * Copy the user's legacy skills tree (~/.kimi/skills/) into kimi-code's | ||
| * default user skills root (~/.kimi-code/skills/). Granularity is one | ||
| * top-level entry per "skill unit" — that matches how the new scanner | ||
| * treats a directory containing SKILL.md as a bundle and a flat .md as a | ||
| * skill on its own. We do not filter non-skill entries; the new scanner | ||
| * ignores anything it cannot parse, so passing it through preserves | ||
| * arbitrary user assets without imposing a schema here. | ||
| */ | ||
| export async function migrateSkillsStep(input: SkillsStepInput): Promise<SkillsStepResult> { | ||
| const srcDir = sourceSkillsDir(input.sourceHome); | ||
| const tgtDir = targetSkillsDir(input.targetHome); | ||
|
|
||
| let entries: string[]; | ||
| try { | ||
| entries = await readdir(srcDir); | ||
| } catch { | ||
| return { copied: 0, skippedExisting: 0 }; | ||
| } | ||
|
|
||
| let copied = 0; | ||
| let skippedExisting = 0; | ||
| let targetDirReady = false; | ||
| for (const name of entries) { | ||
| const srcPath = join(srcDir, name); | ||
| const tgtPath = join(tgtDir, name); | ||
|
|
||
| try { | ||
| await stat(srcPath); | ||
| } catch { | ||
| continue; | ||
| } | ||
|
|
||
| if (existsSync(tgtPath)) { | ||
| skippedExisting++; | ||
| continue; | ||
| } | ||
|
|
||
| // Defer creating the target root until we know there is something to put | ||
| // in it — touching it earlier would fail when ~/.kimi-code/skills is | ||
| // blocked by a file or has restrictive permissions, turning an empty | ||
| // source into a hard error. | ||
| if (!targetDirReady) { | ||
| await mkdir(tgtDir, { recursive: true, mode: 0o700 }); | ||
| targetDirReady = true; | ||
| } | ||
|
|
||
| // Copy to a sibling temp path and rename into place so a crash mid-copy | ||
| // never leaves a half-populated skill directory that the next idempotent | ||
| // re-run would then `existsSync` and skip. | ||
| const tmpPath = `${tgtPath}.${process.pid}.tmp`; | ||
| try { | ||
| await cp(srcPath, tmpPath, { recursive: true, errorOnExist: false, force: true }); | ||
| await rename(tmpPath, tgtPath); | ||
| } catch (err) { | ||
| await rm(tmpPath, { recursive: true, force: true }).catch(() => {}); | ||
| throw err; | ||
|
Comment on lines
+68
to
+72
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The step intentionally copies every top-level entry in Useful? React with 👍 / 👎. |
||
| } | ||
| copied++; | ||
| } | ||
|
|
||
| return { copied, skippedExisting }; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This
nothingToMigratecheck now ignores OAuth (intended) but still only considers sessions/config/MCP/history, so a legacy install that contains only~/.kimi/skills/is treated as “nothing to migrate.” In that casedetectPendingMigrationreturnsnull, the first-launch migration UI is skipped, andkimi migrate(migrateOnly) also exits early with “Nothing to migrate,” so the newly added skills migration path never runs for that user segment.Useful? React with 👍 / 👎.