Skip to content

Commit 61668e5

Browse files
committed
plot agent deploy --spec (alpha)
1 parent 976b763 commit 61668e5

7 files changed

Lines changed: 401 additions & 115 deletions

File tree

.changeset/lucky-snakes-cut.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@plotday/sdk": patch
3+
---
4+
5+
plot create --name argument
6+
plot deploy --spec spec.md (alpha)

sdk/cli/commands/build.ts

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import * as fs from "fs";
2+
import * as path from "path";
3+
4+
import * as out from "../utils/output";
5+
import { bundleAgent } from "../utils/bundle";
6+
7+
interface BuildOptions {
8+
dir: string;
9+
}
10+
11+
/**
12+
* Build command - bundles the agent without deploying.
13+
*
14+
* This command is useful for:
15+
* - Testing that your agent builds successfully
16+
* - Inspecting the bundled output
17+
* - CI/CD pipelines that separate build and deploy steps
18+
*
19+
* @param options - Build configuration
20+
*/
21+
export async function buildCommand(options: BuildOptions) {
22+
const agentPath = path.resolve(process.cwd(), options.dir);
23+
24+
// Verify we're in an agent directory
25+
const packageJsonPath = path.join(agentPath, "package.json");
26+
if (!fs.existsSync(packageJsonPath)) {
27+
out.error(
28+
"package.json not found. Are you in an agent directory?",
29+
"Run this command from your agent's root directory"
30+
);
31+
process.exit(1);
32+
}
33+
34+
// Read package.json for agent name
35+
let agentName = "agent";
36+
try {
37+
const packageJsonContent = fs.readFileSync(packageJsonPath, "utf-8");
38+
const packageJson = JSON.parse(packageJsonContent);
39+
agentName = packageJson.displayName || packageJson.name || "agent";
40+
} catch (error) {
41+
// Continue with default name if parsing fails
42+
}
43+
44+
out.header(`Building ${agentName}`);
45+
46+
try {
47+
// Bundle the agent
48+
out.progress("Bundling...");
49+
const result = await bundleAgent(agentPath, {
50+
minify: false,
51+
sourcemap: true,
52+
});
53+
54+
// Show warnings if any
55+
if (result.warnings.length > 0) {
56+
out.warning(`Build completed with ${result.warnings.length} warning(s)`);
57+
for (const warning of result.warnings.slice(0, 10)) {
58+
console.warn(` ${warning}`);
59+
}
60+
if (result.warnings.length > 10) {
61+
console.warn(` ... and ${result.warnings.length - 10} more warnings`);
62+
}
63+
}
64+
65+
// Get bundle stats
66+
const buildDir = path.join(agentPath, "build");
67+
const bundlePath = path.join(buildDir, "index.js");
68+
const stats = fs.statSync(bundlePath);
69+
const sizeKB = (stats.size / 1024).toFixed(2);
70+
71+
// Show success
72+
out.success(`Built successfully`);
73+
out.plain(` Output: ${path.relative(process.cwd(), bundlePath)}`);
74+
out.plain(` Size: ${sizeKB} KB`);
75+
76+
// Tip for next steps
77+
out.blank();
78+
out.plain("Next steps:");
79+
out.plain(" • Run 'plot agent deploy' to deploy this agent");
80+
out.plain(" • Or inspect the bundled output in build/index.js");
81+
out.blank();
82+
} catch (error) {
83+
out.error("Build failed", String(error));
84+
process.exit(1);
85+
}
86+
}

sdk/cli/commands/create.ts

Lines changed: 56 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import * as out from "../utils/output";
66

77
interface CreateOptions {
88
dir?: string;
9+
name?: string;
10+
displayName?: string;
911
}
1012

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

37-
const response = await prompts([
38-
{
39-
type: "text",
40-
name: "name",
41-
message: "Package name:",
42-
initial: options.dir || undefined,
43-
validate: (value: string) =>
44-
/^[a-z0-9-]+$/.test(value) ||
45-
"Must be kebab-case (lowercase, hyphens only)",
46-
},
47-
{
48-
type: "text",
49-
name: "displayName",
50-
message: "Display name:",
51-
validate: (value: string) => value.length > 0 || "Name is required",
52-
},
53-
]);
39+
let response: { name: string; displayName: string };
40+
41+
// If both name and displayName are provided via CLI, use them directly
42+
if (options.name && options.displayName) {
43+
// Validate name
44+
if (!/^[a-z0-9-]+$/.test(options.name)) {
45+
out.error("Name must be kebab-case (lowercase, hyphens only)");
46+
process.exit(1);
47+
}
48+
49+
// Validate displayName
50+
if (options.displayName.length === 0) {
51+
out.error("Display name is required");
52+
process.exit(1);
53+
}
5454

55-
if (Object.keys(response).length === 0) {
56-
out.plain("\nCancelled.");
57-
process.exit(0);
55+
response = {
56+
name: options.name,
57+
displayName: options.displayName,
58+
};
59+
} else {
60+
// Use interactive prompts
61+
const promptResponse = await prompts([
62+
{
63+
type: "text",
64+
name: "name",
65+
message: "Package name:",
66+
initial: options.dir || options.name || undefined,
67+
validate: (value: string) =>
68+
/^[a-z0-9-]+$/.test(value) ||
69+
"Must be kebab-case (lowercase, hyphens only)",
70+
},
71+
{
72+
type: "text",
73+
name: "displayName",
74+
message: "Display name:",
75+
initial: options.displayName || undefined,
76+
validate: (value: string) => value.length > 0 || "Name is required",
77+
},
78+
]);
79+
80+
if (Object.keys(promptResponse).length === 0) {
81+
out.plain("\nCancelled.");
82+
process.exit(0);
83+
}
84+
85+
response = promptResponse as { name: string; displayName: string };
5886
}
5987

6088
const agentDir = options.dir || response.name;
@@ -122,22 +150,18 @@ export async function createCommand(options: CreateOptions) {
122150
type Activity,
123151
Agent,
124152
type Tools,
125-
createAgent,
126153
} from "@plotday/sdk";
127154
128-
export default createAgent(
129-
class extends Agent {
130-
131-
constructor(tools: Tools) {
132-
super();
133-
}
155+
export default class extends Agent {
156+
constructor(tools: Tools) {
157+
super();
158+
}
134159
135-
async activity(activity: Activity) {
136-
// Implement your agent logic here
137-
console.log("Received activity:", activity);
138-
}
160+
async activity(activity: Activity) {
161+
// Implement your agent logic here
162+
console.log("Received activity:", activity);
139163
}
140-
);
164+
}
141165
`;
142166
fs.writeFileSync(path.join(agentPath, "src", "index.ts"), agentTemplate);
143167

0 commit comments

Comments
 (0)