From b6bf8ecd06e240831d20c90d510a09ce071576a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jim=20Liu=20=E5=AE=9D=E7=8E=89?= Date: Wed, 25 Mar 2026 17:36:46 -0500 Subject: [PATCH] feat(baoyu-imagine): auto-migrate legacy baoyu-image-gen EXTEND.md config path --- skills/baoyu-imagine/SKILL.md | 2 + skills/baoyu-imagine/scripts/main.test.ts | 56 +++++++++++++++++++++++ skills/baoyu-imagine/scripts/main.ts | 49 +++++++++++++++++--- 3 files changed, 100 insertions(+), 7 deletions(-) diff --git a/skills/baoyu-imagine/SKILL.md b/skills/baoyu-imagine/SKILL.md index b5b42f8..4a9f960 100644 --- a/skills/baoyu-imagine/SKILL.md +++ b/skills/baoyu-imagine/SKILL.md @@ -55,6 +55,8 @@ if (Test-Path "$HOME/.baoyu-skills/baoyu-imagine/EXTEND.md") { "user" } | `.baoyu-skills/baoyu-imagine/EXTEND.md` | Project directory | | `$HOME/.baoyu-skills/baoyu-imagine/EXTEND.md` | User home | +Legacy compatibility: if `.baoyu-skills/baoyu-image-gen/EXTEND.md` exists and the new path does not, runtime renames it to `baoyu-imagine`. If both files exist, runtime leaves them unchanged and uses the new path. + **EXTEND.md Supports**: Default provider | Default quality | Default aspect ratio | Default image size | Default models | Batch worker cap | Provider-specific batch limits Schema: `references/config/preferences-schema.md` diff --git a/skills/baoyu-imagine/scripts/main.test.ts b/skills/baoyu-imagine/scripts/main.test.ts index fdd4262..29928f6 100644 --- a/skills/baoyu-imagine/scripts/main.test.ts +++ b/skills/baoyu-imagine/scripts/main.test.ts @@ -13,6 +13,7 @@ import { getWorkerCount, isRetryableGenerationError, loadBatchTasks, + loadExtendConfig, mergeConfig, normalizeOutputImagePath, parseArgs, @@ -170,6 +171,61 @@ batch: }); }); +test("loadExtendConfig renames legacy EXTEND.md when the new path is missing", async () => { + const root = await makeTempDir("baoyu-imagine-extend-"); + const cwd = path.join(root, "project"); + const home = path.join(root, "home"); + const legacyPath = path.join(cwd, ".baoyu-skills", "baoyu-image-gen", "EXTEND.md"); + const currentPath = path.join(cwd, ".baoyu-skills", "baoyu-imagine", "EXTEND.md"); + + await fs.mkdir(path.dirname(legacyPath), { recursive: true }); + await fs.mkdir(home, { recursive: true }); + await fs.writeFile(legacyPath, `--- +default_provider: google +default_quality: 2k +--- +`); + + const config = await loadExtendConfig(cwd, home); + + assert.equal(config.default_provider, "google"); + assert.equal(config.default_quality, "2k"); + await fs.access(currentPath); + await assert.rejects(() => fs.access(legacyPath)); +}); + +test("loadExtendConfig leaves legacy EXTEND.md untouched when both paths exist", async () => { + const root = await makeTempDir("baoyu-imagine-extend-dual-"); + const cwd = path.join(root, "project"); + const home = path.join(root, "home"); + const legacyPath = path.join(cwd, ".baoyu-skills", "baoyu-image-gen", "EXTEND.md"); + const currentPath = path.join(cwd, ".baoyu-skills", "baoyu-imagine", "EXTEND.md"); + + await fs.mkdir(path.dirname(legacyPath), { recursive: true }); + await fs.mkdir(path.dirname(currentPath), { recursive: true }); + await fs.mkdir(home, { recursive: true }); + await fs.writeFile(legacyPath, `--- +default_provider: google +--- +`); + await fs.writeFile(currentPath, `--- +default_provider: openai +--- +`); + + const config = await loadExtendConfig(cwd, home); + + assert.equal(config.default_provider, "openai"); + assert.equal(await fs.readFile(legacyPath, "utf8"), `--- +default_provider: google +--- +`); + assert.equal(await fs.readFile(currentPath, "utf8"), `--- +default_provider: openai +--- +`); +}); + test("mergeConfig only fills values missing from CLI args", () => { const merged = mergeConfig( makeArgs({ diff --git a/skills/baoyu-imagine/scripts/main.ts b/skills/baoyu-imagine/scripts/main.ts index 2a9cbc3..c61dfc8 100644 --- a/skills/baoyu-imagine/scripts/main.ts +++ b/skills/baoyu-imagine/scripts/main.ts @@ -2,7 +2,7 @@ import path from "node:path"; import process from "node:process"; import { homedir } from "node:os"; import { fileURLToPath } from "node:url"; -import { access, mkdir, readFile, writeFile } from "node:fs/promises"; +import { access, mkdir, readFile, rename, writeFile } from "node:fs/promises"; import type { BatchFile, BatchTaskInput, @@ -471,14 +471,49 @@ export function parseSimpleYaml(yaml: string): Partial { return config; } -async function loadExtendConfig(): Promise> { - const home = homedir(); - const cwd = process.cwd(); +type ExtendConfigPathPair = { + current: string; + legacy: string; +}; - const paths = [ - path.join(cwd, ".baoyu-skills", "baoyu-imagine", "EXTEND.md"), - path.join(home, ".baoyu-skills", "baoyu-imagine", "EXTEND.md"), +function getExtendConfigPathPairs(cwd: string, home: string): ExtendConfigPathPair[] { + return [ + { + current: path.join(cwd, ".baoyu-skills", "baoyu-imagine", "EXTEND.md"), + legacy: path.join(cwd, ".baoyu-skills", "baoyu-image-gen", "EXTEND.md"), + }, + { + current: path.join(home, ".baoyu-skills", "baoyu-imagine", "EXTEND.md"), + legacy: path.join(home, ".baoyu-skills", "baoyu-image-gen", "EXTEND.md"), + }, ]; +} + +async function exists(filePath: string): Promise { + try { + await access(filePath); + return true; + } catch { + return false; + } +} + +async function migrateLegacyExtendConfig(cwd: string, home: string): Promise { + for (const { current, legacy } of getExtendConfigPathPairs(cwd, home)) { + const [hasCurrent, hasLegacy] = await Promise.all([exists(current), exists(legacy)]); + if (hasCurrent || !hasLegacy) continue; + await mkdir(path.dirname(current), { recursive: true }); + await rename(legacy, current); + } +} + +export async function loadExtendConfig( + cwd = process.cwd(), + home = homedir(), +): Promise> { + await migrateLegacyExtendConfig(cwd, home); + + const paths = getExtendConfigPathPairs(cwd, home).map(({ current }) => current); for (const p of paths) { try {