Skip to content

Commit 974e447

Browse files
authored
feat(cli): skip missing sync files (#30)
Change-Id: Iaebd03ec16a7ef559025240041ec68cd751582c5
1 parent f831d3d commit 974e447

4 files changed

Lines changed: 41 additions & 4 deletions

File tree

docs/reference/cli.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ Export a provider's remote configuration into a local `agents.yaml`.
7373
| `--provider <name>` | Source provider to sync from. |
7474
| `-o, --out <path>` | Output file (default `agents.synced.yaml`). |
7575
| `--force` | Overwrite the output file if it exists. |
76+
| `--skip-missing-files` | Do not prompt for remote files that cannot be downloaded; omit them from the synced output. |
7677

7778
## `agents migrate`
7879

packages/cli/src/commands/sync.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,13 @@ export function ensureSyncOutputWritable(outPath: string, force?: boolean): void
2727
}
2828
}
2929

30-
export async function syncCommand(options: { file: string; provider?: string; out?: string; force?: boolean }) {
30+
export async function syncCommand(options: {
31+
file: string;
32+
provider?: string;
33+
out?: string;
34+
force?: boolean;
35+
skipMissingFiles?: boolean;
36+
}) {
3137
const outPath = options.out ?? DEFAULT_SYNC_OUTPUT;
3238
ensureSyncOutputWritable(outPath, options.force);
3339

@@ -38,8 +44,10 @@ export async function syncCommand(options: { file: string; provider?: string; ou
3844

3945
const baseDir = dirname(outPath);
4046

41-
// Interactive file path association (before writing yaml — skipped files are removed)
42-
const removedFiles = await promptFileAssociation(result.config, baseDir);
47+
// File path association (before writing yaml — skipped files are removed)
48+
const removedFiles = options.skipMissingFiles
49+
? removeMissingFileAssociations(result.config, baseDir)
50+
: await promptFileAssociation(result.config, baseDir);
4351
if (removedFiles.length > 0) {
4452
const files = (result.config.files ?? {}) as Record<string, unknown>;
4553
for (const key of removedFiles) {
@@ -84,6 +92,19 @@ export async function syncCommand(options: { file: string; provider?: string; ou
8492
}
8593
}
8694

95+
export function removeMissingFileAssociations(config: Record<string, unknown>, baseDir: string): string[] {
96+
const files = (config.files ?? {}) as Record<string, Record<string, unknown>>;
97+
const removed = Object.entries(files)
98+
.filter(([, decl]) => !fileExistsSync(join(baseDir, decl.source as string)))
99+
.map(([key]) => key);
100+
101+
if (removed.length > 0) {
102+
log.info(`${removed.length} file(s) removed (skipped).`);
103+
}
104+
105+
return removed;
106+
}
107+
87108
async function syncFromConfig(
88109
configPath: string,
89110
explicitProvider?: string,

packages/cli/src/program.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ program
145145
.addOption(providerOption("Source provider to sync from (defaults from config when -f is set)"))
146146
.option("-o, --out <path>", "Output file path", "agents.synced.yaml")
147147
.option("--force", "Overwrite the output file if it already exists")
148+
.option("--skip-missing-files", "Do not prompt for remote files that cannot be downloaded; omit them from output")
148149
.action(withResolvedConfigFile(syncCommand));
149150

150151
program

packages/cli/tests/unit/sync-file.test.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { mkdtemp, rm } from "node:fs/promises";
33
import { tmpdir } from "node:os";
44
import { join } from "node:path";
55
import { UserError } from "@openagentpack/sdk";
6-
import { syncCommand } from "../../src/commands/sync.ts";
6+
import { removeMissingFileAssociations, syncCommand } from "../../src/commands/sync.ts";
77

88
const tempDirs: string[] = [];
99

@@ -30,3 +30,17 @@ test("syncCommand requires --provider when config file is missing", async () =>
3030
"--provider when no config file exists",
3131
);
3232
});
33+
34+
test("removeMissingFileAssociations keeps existing sources and skips missing files", async () => {
35+
const dir = await makeTempDir();
36+
await Bun.write(join(dir, "present.txt"), "present\n");
37+
38+
const config = {
39+
files: {
40+
present: { source: "present.txt" },
41+
missing: { source: "missing.txt" },
42+
},
43+
};
44+
45+
expect(removeMissingFileAssociations(config, dir)).toEqual(["missing"]);
46+
});

0 commit comments

Comments
 (0)