feat: rename --from-af to --import and /download to /export (#902)
Co-authored-by: Letta <noreply@letta.com>
This commit is contained in:
@@ -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(", ")}`;
|
||||
}
|
||||
|
||||
@@ -135,12 +135,12 @@ export const commands: Record<string, Command> = {
|
||||
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<string, Command> = {
|
||||
return "Opening agent browser...";
|
||||
},
|
||||
},
|
||||
"/download": {
|
||||
desc: "Export AgentFile (.af)",
|
||||
hidden: true, // Legacy alias for /export
|
||||
handler: () => {
|
||||
return "Exporting agent file...";
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
21
src/index.ts
21
src/index.ts
@@ -78,7 +78,7 @@ OPTIONS
|
||||
--from-agent <id> Inject agent-to-agent system reminder (headless mode)
|
||||
--skills <path> Custom path to skills directory (default: .skills in current directory)
|
||||
--sleeptime Enable sleeptime memory management (only for new agents)
|
||||
--from-af <path> Create agent from an AgentFile (.af) template
|
||||
--import <path> 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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
}
|
||||
}
|
||||
|
||||
// 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<void> {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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",
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user