From 4e4de601e12f9861c55fc13b0225d682ad4e3019 Mon Sep 17 00:00:00 2001 From: heimanba <371510756@qq.com> Date: Sat, 18 Jul 2026 19:06:43 +0800 Subject: [PATCH] feat(cli): skip missing sync files Change-Id: Iaebd03ec16a7ef559025240041ec68cd751582c5 --- docs/reference/cli.md | 1 + packages/cli/src/commands/sync.ts | 27 ++++++++++++++++++++--- packages/cli/src/program.ts | 1 + packages/cli/tests/unit/sync-file.test.ts | 16 +++++++++++++- 4 files changed, 41 insertions(+), 4 deletions(-) diff --git a/docs/reference/cli.md b/docs/reference/cli.md index a8e0671..1918eb4 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -73,6 +73,7 @@ Export a provider's remote configuration into a local `agents.yaml`. | `--provider ` | Source provider to sync from. | | `-o, --out ` | Output file (default `agents.synced.yaml`). | | `--force` | Overwrite the output file if it exists. | +| `--skip-missing-files` | Do not prompt for remote files that cannot be downloaded; omit them from the synced output. | ## `agents migrate` diff --git a/packages/cli/src/commands/sync.ts b/packages/cli/src/commands/sync.ts index e99c418..2ae50ff 100644 --- a/packages/cli/src/commands/sync.ts +++ b/packages/cli/src/commands/sync.ts @@ -27,7 +27,13 @@ export function ensureSyncOutputWritable(outPath: string, force?: boolean): void } } -export async function syncCommand(options: { file: string; provider?: string; out?: string; force?: boolean }) { +export async function syncCommand(options: { + file: string; + provider?: string; + out?: string; + force?: boolean; + skipMissingFiles?: boolean; +}) { const outPath = options.out ?? DEFAULT_SYNC_OUTPUT; ensureSyncOutputWritable(outPath, options.force); @@ -38,8 +44,10 @@ export async function syncCommand(options: { file: string; provider?: string; ou const baseDir = dirname(outPath); - // Interactive file path association (before writing yaml — skipped files are removed) - const removedFiles = await promptFileAssociation(result.config, baseDir); + // File path association (before writing yaml — skipped files are removed) + const removedFiles = options.skipMissingFiles + ? removeMissingFileAssociations(result.config, baseDir) + : await promptFileAssociation(result.config, baseDir); if (removedFiles.length > 0) { const files = (result.config.files ?? {}) as Record; for (const key of removedFiles) { @@ -84,6 +92,19 @@ export async function syncCommand(options: { file: string; provider?: string; ou } } +export function removeMissingFileAssociations(config: Record, baseDir: string): string[] { + const files = (config.files ?? {}) as Record>; + const removed = Object.entries(files) + .filter(([, decl]) => !fileExistsSync(join(baseDir, decl.source as string))) + .map(([key]) => key); + + if (removed.length > 0) { + log.info(`${removed.length} file(s) removed (skipped).`); + } + + return removed; +} + async function syncFromConfig( configPath: string, explicitProvider?: string, diff --git a/packages/cli/src/program.ts b/packages/cli/src/program.ts index 4117ea9..211f9ba 100644 --- a/packages/cli/src/program.ts +++ b/packages/cli/src/program.ts @@ -145,6 +145,7 @@ program .addOption(providerOption("Source provider to sync from (defaults from config when -f is set)")) .option("-o, --out ", "Output file path", "agents.synced.yaml") .option("--force", "Overwrite the output file if it already exists") + .option("--skip-missing-files", "Do not prompt for remote files that cannot be downloaded; omit them from output") .action(withResolvedConfigFile(syncCommand)); program diff --git a/packages/cli/tests/unit/sync-file.test.ts b/packages/cli/tests/unit/sync-file.test.ts index 8c1a0ad..d9d995c 100644 --- a/packages/cli/tests/unit/sync-file.test.ts +++ b/packages/cli/tests/unit/sync-file.test.ts @@ -3,7 +3,7 @@ import { mkdtemp, rm } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { UserError } from "@openagentpack/sdk"; -import { syncCommand } from "../../src/commands/sync.ts"; +import { removeMissingFileAssociations, syncCommand } from "../../src/commands/sync.ts"; const tempDirs: string[] = []; @@ -30,3 +30,17 @@ test("syncCommand requires --provider when config file is missing", async () => "--provider when no config file exists", ); }); + +test("removeMissingFileAssociations keeps existing sources and skips missing files", async () => { + const dir = await makeTempDir(); + await Bun.write(join(dir, "present.txt"), "present\n"); + + const config = { + files: { + present: { source: "present.txt" }, + missing: { source: "missing.txt" }, + }, + }; + + expect(removeMissingFileAssociations(config, dir)).toEqual(["missing"]); +});