@getpaseo/cli 0.2.0-beta.2 → 0.2.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,6 +14,7 @@ import { addReloadOptions, runReloadCommand } from "./reload.js";
14
14
  import { addImportOptions, runImportCommand } from "./import.js";
15
15
  import { runUpdateCommand } from "./update.js";
16
16
  import { runDetachCommand } from "./detach.js";
17
+ import { addOpenOptions, runOpenCommand } from "./open.js";
17
18
  import { withOutput } from "../../output/index.js";
18
19
  import { addDaemonHostOption, addJsonAndDaemonHostOptions, collectMultiple, } from "../../utils/command-options.js";
19
20
  export function createAgentCommand() {
@@ -24,6 +25,7 @@ export function createAgentCommand() {
24
25
  addJsonAndDaemonHostOptions(addImportOptions(agent.command("import"))).action(withOutput(runImportCommand));
25
26
  addDaemonHostOption(addAttachOptions(agent.command("attach"))).action(runAttachCommand);
26
27
  addDaemonHostOption(addLogsOptions(agent.command("logs"))).action(runLogsCommand);
28
+ addJsonAndDaemonHostOptions(addOpenOptions(agent.command("open"))).action(withOutput(runOpenCommand));
27
29
  addJsonAndDaemonHostOptions(addStopOptions(agent.command("stop"))).action(withOutput(runStopCommand));
28
30
  addJsonAndDaemonHostOptions(addDeleteOptions(agent.command("delete"))).action(withOutput(runDeleteCommand));
29
31
  addJsonAndDaemonHostOptions(addSendOptions(agent.command("send"))).action(withOutput(runSendCommand));
@@ -0,0 +1,11 @@
1
+ import type { Command } from "commander";
2
+ import type { CommandOptions, SingleResult } from "../../output/index.js";
3
+ interface OpenAgentResult {
4
+ agentId: string;
5
+ serverId: string;
6
+ status: "opened";
7
+ }
8
+ export declare function addOpenOptions(command: Command): Command;
9
+ export declare function runOpenCommand(agentIdArg: string, options: CommandOptions, _command: Command): Promise<SingleResult<OpenAgentResult>>;
10
+ export {};
11
+ //# sourceMappingURL=open.d.ts.map
@@ -0,0 +1,61 @@
1
+ import { buildDaemonConnectionCommandError, connectToDaemon } from "../../utils/client.js";
2
+ import { openDesktopWithAgent } from "../open.js";
3
+ const openAgentSchema = {
4
+ idField: "agentId",
5
+ columns: [
6
+ { header: "AGENT ID", field: "agentId" },
7
+ { header: "SERVER ID", field: "serverId" },
8
+ { header: "STATUS", field: "status" },
9
+ ],
10
+ };
11
+ export function addOpenOptions(command) {
12
+ return command
13
+ .description("Open an existing agent in Paseo Desktop")
14
+ .argument("<agent-id>", "Existing agent ID")
15
+ .option("--server <server-id>", "Server ID (defaults to the local daemon)");
16
+ }
17
+ async function resolveServerId(options) {
18
+ const explicitServerId = typeof options.server === "string" ? options.server.trim() : "";
19
+ if (explicitServerId) {
20
+ return explicitServerId;
21
+ }
22
+ let client;
23
+ try {
24
+ client = await connectToDaemon({ host: options.host });
25
+ }
26
+ catch (error) {
27
+ throw buildDaemonConnectionCommandError({ host: options.host, error });
28
+ }
29
+ try {
30
+ const serverId = client.getLastServerInfoMessage()?.serverId.trim();
31
+ if (!serverId) {
32
+ const error = {
33
+ code: "SERVER_ID_UNAVAILABLE",
34
+ message: "The daemon did not report a server ID.",
35
+ };
36
+ throw error;
37
+ }
38
+ return serverId;
39
+ }
40
+ finally {
41
+ await client.close().catch(() => { });
42
+ }
43
+ }
44
+ export async function runOpenCommand(agentIdArg, options, _command) {
45
+ const agentId = agentIdArg.trim();
46
+ if (!agentId) {
47
+ const error = {
48
+ code: "MISSING_AGENT_ID",
49
+ message: "Agent ID is required.",
50
+ };
51
+ throw error;
52
+ }
53
+ const serverId = await resolveServerId(options);
54
+ await openDesktopWithAgent({ serverId, agentId });
55
+ return {
56
+ type: "single",
57
+ data: { agentId, serverId, status: "opened" },
58
+ schema: openAgentSchema,
59
+ };
60
+ }
61
+ //# sourceMappingURL=open.js.map
@@ -22,9 +22,15 @@ export interface AgentRunOptions extends CommandOptions {
22
22
  model?: string;
23
23
  thinking?: string;
24
24
  mode?: string;
25
- isolation?: string;
25
+ newWorkspace?: string;
26
26
  worktree?: string;
27
+ worktreeMode?: string;
28
+ worktreeSlug?: string;
29
+ newBranch?: string;
27
30
  base?: string;
31
+ branch?: string;
32
+ prNumber?: string;
33
+ forge?: string;
28
34
  workspace?: string;
29
35
  image?: string[];
30
36
  cwd?: string;
@@ -7,6 +7,7 @@ import { lookup } from "mime-types";
7
7
  import { parseDuration } from "../../utils/duration.js";
8
8
  import { collectMultiple } from "../../utils/command-options.js";
9
9
  import { resolveProviderAndModel } from "../../utils/provider-model.js";
10
+ import { buildWorkspaceSource } from "../workspace/create.js";
10
11
  export { resolveProviderAndModel } from "../../utils/provider-model.js";
11
12
  export function addRunOptions(cmd) {
12
13
  return (cmd
@@ -22,10 +23,16 @@ export function addRunOptions(cmd) {
22
23
  .option("--model <model>", "Model to use (e.g., claude-sonnet-4-20250514, claude-3-5-haiku-20241022)")
23
24
  .option("--thinking <id>", "Thinking option ID to use for this run")
24
25
  .option("--mode <mode>", "Provider-specific mode (e.g., plan, default, bypass)")
25
- .option("--isolation <local|worktree>", "Create a new workspace with this isolation")
26
+ .option("--new-workspace <local|worktree>", "Create a separate local or worktree workspace")
26
27
  .addOption(new Option("--worktree <name>", "Legacy workspace isolation alias").hideHelp())
27
- .option("--base <branch>", "Base branch for an isolated workspace")
28
- .option("--workspace <id>", "Run in an existing workspace (default: a new workspace is created per run; falls back to $PASEO_WORKSPACE_ID)")
28
+ .option("--worktree-mode <mode>", "Worktree mode: branch-off, checkout-branch, or checkout-pr")
29
+ .option("--worktree-slug <slug>", "Managed worktree path slug")
30
+ .option("--new-branch <name>", "New branch name for branch-off mode")
31
+ .option("--base <ref>", "Base ref for branch-off mode")
32
+ .option("--branch <name>", "Existing branch for checkout-branch mode")
33
+ .option("--pr-number <n>", "Pull request or change request number for checkout-pr mode")
34
+ .option("--forge <forge>", "Forge for checkout-pr mode")
35
+ .option("--workspace <id>", "Run in an existing workspace (defaults to the caller workspace when agent-scoped)")
29
36
  .option("--image <path>", "Attach image(s) to the initial prompt (can be used multiple times)", collectMultiple, [])
30
37
  .option("--cwd <path>", "Working directory (default: current)")
31
38
  .option("--env <key=value>", "Set environment variable(s) for the agent process (can be used multiple times)", collectMultiple, [])
@@ -44,6 +51,23 @@ export const agentRunSchema = {
44
51
  { header: "TITLE", field: "title", width: 20 },
45
52
  ],
46
53
  };
54
+ function resolveNewWorkspaceKind(options) {
55
+ return options.newWorkspace ?? (options.worktree ? "worktree" : undefined);
56
+ }
57
+ function buildRunWorkspaceSource(options, cwd) {
58
+ const newWorkspace = resolveNewWorkspaceKind(options) ?? "local";
59
+ return buildWorkspaceSource({
60
+ isolation: newWorkspace,
61
+ path: cwd,
62
+ mode: options.worktreeMode,
63
+ worktreeSlug: options.worktreeSlug ?? options.worktree,
64
+ newBranch: options.newBranch,
65
+ base: options.base,
66
+ branch: options.branch,
67
+ prNumber: options.prNumber,
68
+ forge: options.forge,
69
+ });
70
+ }
47
71
  function toRunResult(agent, statusOverride) {
48
72
  return {
49
73
  agentId: agent.id,
@@ -168,34 +192,56 @@ function structuredRunSchema(output) {
168
192
  serialize: () => output,
169
193
  };
170
194
  }
171
- function validateRunOptions(prompt, options, outputSchema) {
172
- if (!prompt || prompt.trim().length === 0) {
195
+ function validateRunWorkspaceOptions(options) {
196
+ const newWorkspace = resolveNewWorkspaceKind(options);
197
+ if (options.newWorkspace &&
198
+ options.newWorkspace !== "local" &&
199
+ options.newWorkspace !== "worktree") {
173
200
  throw {
174
- code: "MISSING_PROMPT",
175
- message: "A prompt is required",
176
- details: "Usage: paseo agent run [options] <prompt>",
201
+ code: "INVALID_OPTIONS",
202
+ message: `Unsupported new workspace kind: ${options.newWorkspace}`,
203
+ details: "Use --new-workspace local or --new-workspace worktree",
177
204
  };
178
205
  }
179
- const createsIsolatedWorkspace = options.isolation === "worktree" || Boolean(options.worktree);
180
- if (options.isolation && options.isolation !== "local" && options.isolation !== "worktree") {
206
+ if (options.newWorkspace && options.worktree) {
181
207
  throw {
182
208
  code: "INVALID_OPTIONS",
183
- message: `Unsupported workspace isolation: ${options.isolation}`,
184
- details: "Use --isolation local or --isolation worktree",
209
+ message: "--new-workspace and --worktree cannot be combined",
210
+ details: "Use --new-workspace worktree and the supported worktree options",
185
211
  };
186
212
  }
187
- if (options.base && !createsIsolatedWorkspace) {
213
+ const hasWorktreeCreationOptions = [
214
+ options.worktreeMode,
215
+ options.worktreeSlug,
216
+ options.newBranch,
217
+ options.base,
218
+ options.branch,
219
+ options.prNumber,
220
+ options.forge,
221
+ ].some((value) => value !== undefined);
222
+ if (hasWorktreeCreationOptions && newWorkspace !== "worktree") {
188
223
  throw {
189
224
  code: "INVALID_OPTIONS",
190
- message: "--base can only be used with --isolation worktree",
191
- details: "Usage: paseo agent run --isolation worktree --base <branch> <prompt>",
225
+ message: "Worktree options require --new-workspace worktree",
226
+ details: "Usage: paseo run --new-workspace worktree [worktree options] <prompt>",
192
227
  };
193
228
  }
194
- if (options.isolation && options.workspace) {
229
+ if (newWorkspace === "worktree") {
230
+ try {
231
+ buildRunWorkspaceSource(options, options.cwd ?? process.cwd());
232
+ }
233
+ catch (error) {
234
+ throw {
235
+ code: "INVALID_OPTIONS",
236
+ message: error instanceof Error ? error.message : String(error),
237
+ };
238
+ }
239
+ }
240
+ if (options.newWorkspace && options.workspace) {
195
241
  throw {
196
242
  code: "INVALID_OPTIONS",
197
- message: "--isolation and --workspace cannot be combined",
198
- details: "Select an existing workspace or create a new one with an isolation choice",
243
+ message: "--new-workspace and --workspace cannot be combined",
244
+ details: "Select an existing workspace or explicitly create a new one",
199
245
  };
200
246
  }
201
247
  // COMPAT(worktreeRunFlag): --worktree implies a new worktree-isolated workspace.
@@ -204,9 +250,19 @@ function validateRunOptions(prompt, options, outputSchema) {
204
250
  throw {
205
251
  code: "INVALID_OPTIONS",
206
252
  message: "--worktree and --workspace cannot be combined",
207
- details: "Use --isolation worktree instead of the legacy --worktree flag",
253
+ details: "Use --new-workspace worktree instead of the legacy --worktree flag",
254
+ };
255
+ }
256
+ }
257
+ function validateRunOptions(prompt, options, outputSchema) {
258
+ if (!prompt || prompt.trim().length === 0) {
259
+ throw {
260
+ code: "MISSING_PROMPT",
261
+ message: "A prompt is required",
262
+ details: "Usage: paseo agent run [options] <prompt>",
208
263
  };
209
264
  }
265
+ validateRunWorkspaceOptions(options);
210
266
  if (outputSchema && runsInBackground(options)) {
211
267
  throw {
212
268
  code: "INVALID_OPTIONS",
@@ -324,37 +380,27 @@ export async function resolveExistingRunWorkspace(client, workspaceId) {
324
380
  // 1. --workspace <id> -> run in that existing workspace
325
381
  // 2. $PASEO_AGENT_ID -> daemon resolves the caller's workspace
326
382
  // 3. $PASEO_WORKSPACE_ID -> exported by workspace terminals
327
- // 4. --isolation <kind> -> mint a new workspace with explicit isolation
383
+ // 4. --new-workspace <kind> -> mint a new workspace explicitly
328
384
  // 5. bare run -> mint a new local-backed workspace for cwd
329
385
  async function resolveRunWorkspace(client, options, cwd) {
330
- const requestedIsolation = options.isolation ?? (options.worktree ? "worktree" : undefined);
331
- const explicit = requestedIsolation ? undefined : options.workspace?.trim();
386
+ const newWorkspace = resolveNewWorkspaceKind(options);
387
+ const explicit = newWorkspace ? undefined : options.workspace?.trim();
332
388
  if (explicit) {
333
389
  console.error(`Using workspace ${explicit}`);
334
390
  return resolveExistingRunWorkspace(client, explicit);
335
391
  }
336
- if (!requestedIsolation && resolveRunCallerAgentId()) {
392
+ if (!newWorkspace && resolveRunCallerAgentId()) {
337
393
  return { cwd };
338
394
  }
339
- const ambientWorkspaceId = requestedIsolation
340
- ? undefined
341
- : process.env.PASEO_WORKSPACE_ID?.trim();
395
+ const ambientWorkspaceId = newWorkspace ? undefined : process.env.PASEO_WORKSPACE_ID?.trim();
342
396
  if (ambientWorkspaceId) {
343
397
  console.error(`Using workspace ${ambientWorkspaceId}`);
344
398
  return resolveExistingRunWorkspace(client, ambientWorkspaceId);
345
399
  }
346
400
  // TODO: thread the run `prompt` as firstAgentContext so workspace-level
347
401
  // title/branch generation picks up the task description (U8/U6 deferred).
348
- const result = requestedIsolation === "worktree"
349
- ? await client.createWorkspace({
350
- source: {
351
- kind: "worktree",
352
- cwd,
353
- worktreeSlug: options.worktree,
354
- baseBranch: options.base,
355
- },
356
- })
357
- : await client.createWorkspace({ source: { kind: "directory", path: cwd } });
402
+ const source = buildRunWorkspaceSource(options, cwd);
403
+ const result = await client.createWorkspace({ source });
358
404
  if (!result.workspace) {
359
405
  throw {
360
406
  code: "WORKSPACE_CREATE_FAILED",
@@ -1,2 +1,4 @@
1
+ import { type AgentDeepLinkTarget } from "@getpaseo/protocol/agent-deep-link";
1
2
  export declare function openDesktopWithProject(projectPath: string): Promise<void>;
3
+ export declare function openDesktopWithAgent(target: AgentDeepLinkTarget): Promise<void>;
2
4
  //# sourceMappingURL=open.d.ts.map
@@ -2,6 +2,7 @@ import { existsSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import path from "node:path";
4
4
  import { spawnProcess } from "@getpaseo/server";
5
+ import { buildAgentDeepLink } from "@getpaseo/protocol/agent-deep-link";
5
6
  function findDesktopApp() {
6
7
  if (process.platform === "darwin") {
7
8
  const candidates = [
@@ -55,25 +56,26 @@ function spawnDetached(command, args) {
55
56
  env: cleanEnvForDesktopLaunch(),
56
57
  }).unref();
57
58
  }
59
+ function launchDesktop(args) {
60
+ if (process.env.PASEO_DESKTOP_CLI === "1") {
61
+ throw new Error("Cannot open Paseo Desktop while running in desktop CLI passthrough mode.");
62
+ }
63
+ const desktopApp = findDesktopApp();
64
+ if (!desktopApp) {
65
+ throw new Error("Paseo desktop app not found. Install it from https://github.com/getpaseo/paseo/releases");
66
+ }
67
+ if (process.platform === "darwin") {
68
+ // -n forces a new instance even if the app is already running. The new
69
+ // instance relays its argv to the existing one through Electron's
70
+ // single-instance lock. -g keeps the terminal in the foreground.
71
+ spawnDetached("open", ["-n", "-g", "-a", desktopApp, "--args", ...args]);
72
+ return;
73
+ }
74
+ spawnDetached(desktopApp, args);
75
+ }
58
76
  export async function openDesktopWithProject(projectPath) {
59
77
  try {
60
- if (process.env.PASEO_DESKTOP_CLI === "1") {
61
- throw new Error("Cannot open a desktop project while running in desktop CLI passthrough mode.");
62
- }
63
- const desktopApp = findDesktopApp();
64
- if (!desktopApp) {
65
- throw new Error("Paseo desktop app not found. Install it from https://github.com/getpaseo/paseo/releases");
66
- }
67
- if (process.platform === "darwin") {
68
- // -n forces a new instance even if the app is already running.
69
- // The new instance hits requestSingleInstanceLock(), fails, and relays
70
- // the argv to the first instance via the second-instance event.
71
- // -g keeps the terminal in the foreground (better CLI UX).
72
- // Without -n, macOS just activates the existing window and drops --args.
73
- spawnDetached("open", ["-n", "-g", "-a", desktopApp, "--args", projectPath]);
74
- return;
75
- }
76
- spawnDetached(desktopApp, [projectPath]);
78
+ launchDesktop([projectPath]);
77
79
  }
78
80
  catch (error) {
79
81
  const message = error instanceof Error ? error.message : String(error);
@@ -81,4 +83,7 @@ export async function openDesktopWithProject(projectPath) {
81
83
  process.exitCode = 1;
82
84
  }
83
85
  }
86
+ export async function openDesktopWithAgent(target) {
87
+ launchDesktop([buildAgentDeepLink(target)]);
88
+ }
84
89
  //# sourceMappingURL=open.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getpaseo/cli",
3
- "version": "0.2.0-beta.2",
3
+ "version": "0.2.0-beta.3",
4
4
  "description": "Paseo CLI - control your AI coding agents from the command line",
5
5
  "bin": {
6
6
  "paseo": "bin/paseo"
@@ -27,9 +27,9 @@
27
27
  },
28
28
  "dependencies": {
29
29
  "@clack/prompts": "^1.0.0",
30
- "@getpaseo/client": "0.2.0-beta.2",
31
- "@getpaseo/protocol": "0.2.0-beta.2",
32
- "@getpaseo/server": "0.2.0-beta.2",
30
+ "@getpaseo/client": "0.2.0-beta.3",
31
+ "@getpaseo/protocol": "0.2.0-beta.3",
32
+ "@getpaseo/server": "0.2.0-beta.3",
33
33
  "chalk": "^5.3.0",
34
34
  "commander": "^12.0.0",
35
35
  "mime-types": "^2.1.35",