Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/lucky-snakes-cut.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@plotday/sdk": patch
---

plot create --name argument
plot deploy --spec spec.md (alpha)
86 changes: 86 additions & 0 deletions sdk/cli/commands/build.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import * as fs from "fs";
import * as path from "path";

import * as out from "../utils/output";
import { bundleAgent } from "../utils/bundle";

interface BuildOptions {
dir: string;
}

/**
* Build command - bundles the agent without deploying.
*
* This command is useful for:
* - Testing that your agent builds successfully
* - Inspecting the bundled output
* - CI/CD pipelines that separate build and deploy steps
*
* @param options - Build configuration
*/
export async function buildCommand(options: BuildOptions) {
const agentPath = path.resolve(process.cwd(), options.dir);

// Verify we're in an agent directory
const packageJsonPath = path.join(agentPath, "package.json");
if (!fs.existsSync(packageJsonPath)) {
out.error(
"package.json not found. Are you in an agent directory?",
"Run this command from your agent's root directory"
);
process.exit(1);
}

// Read package.json for agent name
let agentName = "agent";
try {
const packageJsonContent = fs.readFileSync(packageJsonPath, "utf-8");
const packageJson = JSON.parse(packageJsonContent);
agentName = packageJson.displayName || packageJson.name || "agent";
} catch (error) {
// Continue with default name if parsing fails
}

out.header(`Building ${agentName}`);

try {
// Bundle the agent
out.progress("Bundling...");
const result = await bundleAgent(agentPath, {
minify: false,
sourcemap: true,
});

// Show warnings if any
if (result.warnings.length > 0) {
out.warning(`Build completed with ${result.warnings.length} warning(s)`);
for (const warning of result.warnings.slice(0, 10)) {
console.warn(` ${warning}`);
}
if (result.warnings.length > 10) {
console.warn(` ... and ${result.warnings.length - 10} more warnings`);
}
}

// Get bundle stats
const buildDir = path.join(agentPath, "build");
const bundlePath = path.join(buildDir, "index.js");
const stats = fs.statSync(bundlePath);
const sizeKB = (stats.size / 1024).toFixed(2);

// Show success
out.success(`Built successfully`);
out.plain(` Output: ${path.relative(process.cwd(), bundlePath)}`);
out.plain(` Size: ${sizeKB} KB`);

// Tip for next steps
out.blank();
out.plain("Next steps:");
out.plain(" • Run 'plot agent deploy' to deploy this agent");
out.plain(" • Or inspect the bundled output in build/index.js");
out.blank();
} catch (error) {
out.error("Build failed", String(error));
process.exit(1);
}
}
88 changes: 56 additions & 32 deletions sdk/cli/commands/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import * as out from "../utils/output";

interface CreateOptions {
dir?: string;
name?: string;
displayName?: string;
}

/**
Expand Down Expand Up @@ -34,27 +36,53 @@ function detectPackageManager(): string {
export async function createCommand(options: CreateOptions) {
out.header("Create a new Plot agent");

const response = await prompts([
{
type: "text",
name: "name",
message: "Package name:",
initial: options.dir || undefined,
validate: (value: string) =>
/^[a-z0-9-]+$/.test(value) ||
"Must be kebab-case (lowercase, hyphens only)",
},
{
type: "text",
name: "displayName",
message: "Display name:",
validate: (value: string) => value.length > 0 || "Name is required",
},
]);
let response: { name: string; displayName: string };

// If both name and displayName are provided via CLI, use them directly
if (options.name && options.displayName) {
// Validate name
if (!/^[a-z0-9-]+$/.test(options.name)) {
out.error("Name must be kebab-case (lowercase, hyphens only)");
process.exit(1);
}

// Validate displayName
if (options.displayName.length === 0) {
out.error("Display name is required");
process.exit(1);
}

if (Object.keys(response).length === 0) {
out.plain("\nCancelled.");
process.exit(0);
response = {
name: options.name,
displayName: options.displayName,
};
} else {
// Use interactive prompts
const promptResponse = await prompts([
{
type: "text",
name: "name",
message: "Package name:",
initial: options.dir || options.name || undefined,
validate: (value: string) =>
/^[a-z0-9-]+$/.test(value) ||
"Must be kebab-case (lowercase, hyphens only)",
},
{
type: "text",
name: "displayName",
message: "Display name:",
initial: options.displayName || undefined,
validate: (value: string) => value.length > 0 || "Name is required",
},
]);

if (Object.keys(promptResponse).length === 0) {
out.plain("\nCancelled.");
process.exit(0);
}

response = promptResponse as { name: string; displayName: string };
}

const agentDir = options.dir || response.name;
Expand Down Expand Up @@ -122,22 +150,18 @@ export async function createCommand(options: CreateOptions) {
type Activity,
Agent,
type Tools,
createAgent,
} from "@plotday/sdk";

export default createAgent(
class extends Agent {

constructor(tools: Tools) {
super();
}
export default class extends Agent {
constructor(tools: Tools) {
super();
}

async activity(activity: Activity) {
// Implement your agent logic here
console.log("Received activity:", activity);
}
async activity(activity: Activity) {
// Implement your agent logic here
console.log("Received activity:", activity);
}
);
}
`;
fs.writeFileSync(path.join(agentPath, "src", "index.ts"), agentTemplate);

Expand Down
Loading