From 0c317652fddb83fe9f971f2be65d8d513bef6c21 Mon Sep 17 00:00:00 2001 From: Sarah Wooders Date: Wed, 11 Feb 2026 15:39:48 -0800 Subject: [PATCH] feat: rename --from-af to --import and /download to /export (#902) Co-authored-by: Letta --- src/cli/App.tsx | 9 ++++---- src/cli/commands/registry.ts | 13 +++++++++--- src/index.ts | 21 +++++++++++-------- .../startup-flow.integration.test.ts | 4 ++-- src/tests/startup-flow.test.ts | 6 +++--- 5 files changed, 32 insertions(+), 21 deletions(-) diff --git a/src/cli/App.tsx b/src/cli/App.tsx index 9724f21..09b0a7c 100644 --- a/src/cli/App.tsx +++ b/src/cli/App.tsx @@ -330,6 +330,7 @@ const NON_STATE_COMMANDS = new Set([ "/search", "/memory", "/feedback", + "/export", "/download", ]); @@ -6752,11 +6753,11 @@ export default function App({ return { submitted: true }; } - // Special handling for /download command - download agent file - if (msg.trim() === "/download") { + // Special handling for /export command (also accepts legacy /download) + if (msg.trim() === "/export" || msg.trim() === "/download") { const cmd = commandRunner.start( msg.trim(), - "Downloading agent file...", + "Exporting agent file...", ); setCommandRunning(true); @@ -6824,7 +6825,7 @@ export default function App({ writeFileSync(fileName, JSON.stringify(fileContent, null, 2)); // Build success message - let summary = `AgentFile downloaded to ${fileName}`; + let summary = `AgentFile exported to ${fileName}`; if (skills.length > 0) { summary += `\n📦 Included ${skills.length} skill(s): ${skills.map((s) => s.name).join(", ")}`; } diff --git a/src/cli/commands/registry.ts b/src/cli/commands/registry.ts index 37851f2..c53794e 100644 --- a/src/cli/commands/registry.ts +++ b/src/cli/commands/registry.ts @@ -135,12 +135,12 @@ export const commands: Record = { return "Updating description..."; }, }, - "/download": { - desc: "Download AgentFile (.af)", + "/export": { + desc: "Export AgentFile (.af)", order: 26, handler: () => { // Handled specially in App.tsx to access agent ID and client - return "Downloading agent file..."; + return "Exporting agent file..."; }, }, "/toolset": { @@ -394,6 +394,13 @@ export const commands: Record = { return "Opening agent browser..."; }, }, + "/download": { + desc: "Export AgentFile (.af)", + hidden: true, // Legacy alias for /export + handler: () => { + return "Exporting agent file..."; + }, + }, }; /** diff --git a/src/index.ts b/src/index.ts index 3f9755e..534c169 100755 --- a/src/index.ts +++ b/src/index.ts @@ -78,7 +78,7 @@ OPTIONS --from-agent Inject agent-to-agent system reminder (headless mode) --skills Custom path to skills directory (default: .skills in current directory) --sleeptime Enable sleeptime memory management (only for new agents) - --from-af Create agent from an AgentFile (.af) template + --import Create agent from an AgentFile (.af) template Use @author/name to import from the agent registry --memfs Enable memory filesystem for this agent --no-memfs Disable memory filesystem for this agent @@ -439,6 +439,7 @@ async function main(): Promise { skills: { type: "string" }, sleeptime: { type: "boolean" }, "from-af": { type: "string" }, + import: { type: "string" }, memfs: { type: "boolean" }, "no-memfs": { type: "boolean" }, @@ -554,7 +555,9 @@ async function main(): Promise { const sleeptimeFlag = (values.sleeptime as boolean | undefined) ?? undefined; const memfsFlag = values.memfs as boolean | undefined; const noMemfsFlag = values["no-memfs"] as boolean | undefined; - const fromAfFile = values["from-af"] as string | undefined; + const fromAfFile = + (values.import as string | undefined) ?? + (values["from-af"] as string | undefined); const isHeadless = values.prompt || values.run || !process.stdin.isTTY; // Fail if an unknown command/argument is passed (and we're not in headless mode where it might be a prompt) @@ -694,7 +697,7 @@ async function main(): Promise { process.exit(1); } if (fromAfFile) { - console.error("Error: --conversation cannot be used with --from-af"); + console.error("Error: --conversation cannot be used with --import"); process.exit(1); } if (shouldResume) { @@ -723,24 +726,24 @@ async function main(): Promise { } } - // Validate --from-af flag + // Validate --import flag (also accepts legacy --from-af) // Detect if it's a registry handle (e.g., @author/name) or a local file path let isRegistryImport = false; if (fromAfFile) { if (specifiedAgentId) { - console.error("Error: --from-af cannot be used with --agent"); + console.error("Error: --import cannot be used with --agent"); process.exit(1); } if (specifiedAgentName) { - console.error("Error: --from-af cannot be used with --name"); + console.error("Error: --import cannot be used with --name"); process.exit(1); } if (shouldResume) { - console.error("Error: --from-af cannot be used with --resume"); + console.error("Error: --import cannot be used with --resume"); process.exit(1); } if (forceNew) { - console.error("Error: --from-af cannot be used with --new"); + console.error("Error: --import cannot be used with --new"); process.exit(1); } @@ -753,7 +756,7 @@ async function main(): Promise { const parts = normalized.split("/"); if (parts.length !== 2 || !parts[0] || !parts[1]) { console.error( - `Error: Invalid registry handle "${fromAfFile}". Use format: @author/agentname`, + `Error: Invalid registry handle "${fromAfFile}". Use format: letta --import @author/agentname`, ); process.exit(1); } diff --git a/src/integration-tests/startup-flow.integration.test.ts b/src/integration-tests/startup-flow.integration.test.ts index 2313c1c..d22195e 100644 --- a/src/integration-tests/startup-flow.integration.test.ts +++ b/src/integration-tests/startup-flow.integration.test.ts @@ -100,9 +100,9 @@ describe("Startup Flow - Invalid Inputs", () => { { timeout: 70000 }, ); - test("--from-af with nonexistent file shows error", async () => { + test("--import with nonexistent file shows error", async () => { const result = await runCli( - ["--from-af", "/nonexistent/path/agent.af", "-p", "test"], + ["--import", "/nonexistent/path/agent.af", "-p", "test"], { expectExit: 1 }, ); expect(result.stderr).toContain("not found"); diff --git a/src/tests/startup-flow.test.ts b/src/tests/startup-flow.test.ts index 5e4696d..61aa840 100644 --- a/src/tests/startup-flow.test.ts +++ b/src/tests/startup-flow.test.ts @@ -104,13 +104,13 @@ describe("Startup Flow - Flag Conflicts", () => { ); }); - test("--conversation conflicts with --from-af", async () => { + test("--conversation conflicts with --import", async () => { const result = await runCli( - ["--conversation", "conv-123", "--from-af", "test.af"], + ["--conversation", "conv-123", "--import", "test.af"], { expectExit: 1 }, ); expect(result.stderr).toContain( - "--conversation cannot be used with --from-af", + "--conversation cannot be used with --import", ); });