feat(baoyu-imagine): auto-migrate legacy baoyu-image-gen EXTEND.md config path

This commit is contained in:
Jim Liu 宝玉 2026-03-25 17:36:46 -05:00
parent 7a0ffd9533
commit b6bf8ecd06
3 changed files with 100 additions and 7 deletions

View File

@ -55,6 +55,8 @@ if (Test-Path "$HOME/.baoyu-skills/baoyu-imagine/EXTEND.md") { "user" }
| `.baoyu-skills/baoyu-imagine/EXTEND.md` | Project directory | | `.baoyu-skills/baoyu-imagine/EXTEND.md` | Project directory |
| `$HOME/.baoyu-skills/baoyu-imagine/EXTEND.md` | User home | | `$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 **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` Schema: `references/config/preferences-schema.md`

View File

@ -13,6 +13,7 @@ import {
getWorkerCount, getWorkerCount,
isRetryableGenerationError, isRetryableGenerationError,
loadBatchTasks, loadBatchTasks,
loadExtendConfig,
mergeConfig, mergeConfig,
normalizeOutputImagePath, normalizeOutputImagePath,
parseArgs, 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", () => { test("mergeConfig only fills values missing from CLI args", () => {
const merged = mergeConfig( const merged = mergeConfig(
makeArgs({ makeArgs({

View File

@ -2,7 +2,7 @@ import path from "node:path";
import process from "node:process"; import process from "node:process";
import { homedir } from "node:os"; import { homedir } from "node:os";
import { fileURLToPath } from "node:url"; 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 { import type {
BatchFile, BatchFile,
BatchTaskInput, BatchTaskInput,
@ -471,14 +471,49 @@ export function parseSimpleYaml(yaml: string): Partial<ExtendConfig> {
return config; return config;
} }
async function loadExtendConfig(): Promise<Partial<ExtendConfig>> { type ExtendConfigPathPair = {
const home = homedir(); current: string;
const cwd = process.cwd(); legacy: string;
};
const paths = [ function getExtendConfigPathPairs(cwd: string, home: string): ExtendConfigPathPair[] {
path.join(cwd, ".baoyu-skills", "baoyu-imagine", "EXTEND.md"), return [
path.join(home, ".baoyu-skills", "baoyu-imagine", "EXTEND.md"), {
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<boolean> {
try {
await access(filePath);
return true;
} catch {
return false;
}
}
async function migrateLegacyExtendConfig(cwd: string, home: string): Promise<void> {
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<Partial<ExtendConfig>> {
await migrateLegacyExtendConfig(cwd, home);
const paths = getExtendConfigPathPairs(cwd, home).map(({ current }) => current);
for (const p of paths) { for (const p of paths) {
try { try {