@getpaseo/cli 0.4.0 → 0.5.0-beta.1

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.
package/dist/cli.js CHANGED
@@ -3,6 +3,8 @@ import { createAgentCommand } from "./commands/agent/index.js";
3
3
  import { createDaemonCommand } from "./commands/daemon/index.js";
4
4
  import { createPermitCommand } from "./commands/permit/index.js";
5
5
  import { createProviderCommand } from "./commands/provider/index.js";
6
+ import { createPluginCommand } from "./commands/plugin/index.js";
7
+ import { createProjectCommand } from "./commands/project/index.js";
6
8
  import { createScheduleCommand } from "./commands/schedule/index.js";
7
9
  import { createSpeechCommand } from "./commands/speech/index.js";
8
10
  import { createScriptCommand } from "./commands/script/index.js";
@@ -15,6 +17,7 @@ import { createHooksCommand } from "./commands/hooks.js";
15
17
  import { startCommand as daemonStartCommand } from "./commands/daemon/start.js";
16
18
  import { runStatusCommand as runDaemonStatusCommand } from "./commands/daemon/status.js";
17
19
  import { runRestartCommand as runDaemonRestartCommand } from "./commands/daemon/restart.js";
20
+ import { runDaemonReloadCommand } from "./commands/daemon/reload.js";
18
21
  import { addLsOptions, runLsCommand } from "./commands/agent/ls.js";
19
22
  import { addRunOptions, runRunCommand } from "./commands/agent/run.js";
20
23
  import { addLogsOptions, runLogsCommand } from "./commands/agent/logs.js";
@@ -79,6 +82,7 @@ export function createCli() {
79
82
  .description('Show local daemon status (alias for "paseo daemon status")'))
80
83
  .option("--home <path>", "Paseo home directory (default: ~/.paseo)")
81
84
  .action(withOutput(runDaemonStatusCommand));
85
+ addJsonAndDaemonHostOptions(program.command("reload").description('Reload daemon config (alias for "paseo daemon reload")')).action(withOutput(runDaemonReloadCommand));
82
86
  addJsonOption(program
83
87
  .command("restart")
84
88
  .description('Restart local daemon (alias for "paseo daemon restart")'))
@@ -116,9 +120,11 @@ export function createCli() {
116
120
  program.addCommand(createPermitCommand());
117
121
  // Provider commands
118
122
  program.addCommand(createProviderCommand());
123
+ program.addCommand(createPluginCommand());
119
124
  // Speech model commands
120
125
  program.addCommand(createSpeechCommand());
121
126
  // Workspace commands
127
+ program.addCommand(createProjectCommand());
122
128
  program.addCommand(createWorkspaceCommand());
123
129
  // COMPAT(worktreeCli): legacy command alias added before workspace was the product unit.
124
130
  // Added in v0.2.0; remove after 2027-01-17.
@@ -5,8 +5,9 @@ import { runStopCommand } from "./stop.js";
5
5
  import { runRestartCommand } from "./restart.js";
6
6
  import { runSetPasswordCommand } from "./set-password.js";
7
7
  import { pairCommand } from "./pair.js";
8
+ import { runDaemonReloadCommand } from "./reload.js";
8
9
  import { withOutput } from "../../output/index.js";
9
- import { addJsonOption } from "../../utils/command-options.js";
10
+ import { addJsonAndDaemonHostOptions, addJsonOption } from "../../utils/command-options.js";
10
11
  function resolveHostnamesOption(hostnames, allowedHosts) {
11
12
  if (typeof hostnames === "string")
12
13
  return hostnames;
@@ -18,6 +19,7 @@ export function createDaemonCommand() {
18
19
  const daemon = new Command("daemon").description("Manage the Paseo daemon");
19
20
  daemon.addCommand(startCommand());
20
21
  daemon.addCommand(pairCommand());
22
+ addJsonAndDaemonHostOptions(daemon.command("reload").description("Reload config.json without restarting the daemon")).action(withOutput(runDaemonReloadCommand));
21
23
  addJsonOption(daemon.command("status").description("Show local daemon status"))
22
24
  .option("--home <path>", "Paseo home directory (default: ~/.paseo)")
23
25
  .action(withOutput(runStatusCommand));
@@ -0,0 +1,10 @@
1
+ import type { Command } from "commander";
2
+ import type { CommandOptions, OutputSchema, SingleResult } from "../../output/index.js";
3
+ export interface DaemonReloadResult {
4
+ appliedPaths: string[];
5
+ restartRequiredPaths: string[];
6
+ overrideControlledPaths: string[];
7
+ }
8
+ export declare const daemonReloadSchema: OutputSchema<DaemonReloadResult>;
9
+ export declare function runDaemonReloadCommand(options: CommandOptions, _command: Command): Promise<SingleResult<DaemonReloadResult>>;
10
+ //# sourceMappingURL=reload.d.ts.map
@@ -0,0 +1,36 @@
1
+ import { connectToDaemon } from "../../utils/client.js";
2
+ export const daemonReloadSchema = {
3
+ idField: () => "daemon-config",
4
+ columns: [],
5
+ renderHuman(result) {
6
+ if (result.type !== "single")
7
+ return "";
8
+ const lines = ["Configuration reloaded."];
9
+ if (result.data.restartRequiredPaths.length > 0) {
10
+ lines.push("", "Warning: These changes require a daemon restart:", ...result.data.restartRequiredPaths.map((path) => ` ${path}`), "", "Run: paseo daemon restart");
11
+ }
12
+ if (result.data.overrideControlledPaths.length > 0) {
13
+ lines.push("", "Warning: These settings are controlled by daemon launch overrides:", ...result.data.overrideControlledPaths.map((path) => ` ${path}`));
14
+ }
15
+ return lines.join("\n");
16
+ },
17
+ };
18
+ export async function runDaemonReloadCommand(options, _command) {
19
+ const client = await connectToDaemon({ host: options.host });
20
+ try {
21
+ const payload = await client.reloadDaemonConfig();
22
+ return {
23
+ type: "single",
24
+ data: {
25
+ appliedPaths: payload.appliedPaths,
26
+ restartRequiredPaths: payload.restartRequiredPaths,
27
+ overrideControlledPaths: payload.overrideControlledPaths,
28
+ },
29
+ schema: daemonReloadSchema,
30
+ };
31
+ }
32
+ finally {
33
+ await client.close();
34
+ }
35
+ }
36
+ //# sourceMappingURL=reload.js.map
@@ -0,0 +1,14 @@
1
+ import { Command } from "commander";
2
+ import type { PluginListItem, PluginLogEntry } from "@getpaseo/protocol/messages";
3
+ import type { CommandOptions, ListResult, SingleResult } from "../../output/index.js";
4
+ import { type PluginScaffold } from "./scaffold.js";
5
+ interface PluginOptions extends CommandOptions {
6
+ host?: string;
7
+ id?: string;
8
+ }
9
+ export declare function runPluginInitCommand(directory: string, options: PluginOptions, _command: Command): Promise<SingleResult<PluginScaffold>>;
10
+ export declare function runPluginListCommand(options: PluginOptions, _command: Command): Promise<ListResult<PluginListItem>>;
11
+ export declare function runPluginLogsCommand(pluginId: string, options: PluginOptions, _command: Command): Promise<ListResult<PluginLogEntry>>;
12
+ export declare function createPluginCommand(): Command;
13
+ export {};
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,84 @@
1
+ import { Command } from "commander";
2
+ import { withOutput } from "../../output/index.js";
3
+ import { addJsonAndDaemonHostOptions, addJsonOption } from "../../utils/command-options.js";
4
+ import { scaffoldPluginDirectory } from "./scaffold.js";
5
+ import { withPluginLogsClient, withPluginManagementClient } from "./shared.js";
6
+ const pluginSchema = {
7
+ idField: "id",
8
+ columns: [
9
+ { header: "PLUGIN", field: "id", width: 20 },
10
+ { header: "STATUS", field: "status", width: 10 },
11
+ { header: "ENABLED", field: (plugin) => (plugin.enabled ? "yes" : "no"), width: 8 },
12
+ { header: "DIRECTORY", field: "path", width: 40 },
13
+ { header: "ERROR", field: (plugin) => plugin.error ?? "", width: 40 },
14
+ ],
15
+ };
16
+ const scaffoldSchema = {
17
+ idField: "id",
18
+ columns: [
19
+ { header: "PLUGIN", field: "id", width: 20 },
20
+ { header: "DIRECTORY", field: "directory", width: 60 },
21
+ ],
22
+ };
23
+ const pluginLogsSchema = {
24
+ idField: (entry) => String(entry.sequence),
25
+ columns: [
26
+ { header: "TIME", field: "timestamp", width: 24 },
27
+ { header: "STREAM", field: "stream", width: 8 },
28
+ { header: "MESSAGE", field: "message", width: 80 },
29
+ ],
30
+ };
31
+ export async function runPluginInitCommand(directory, options, _command) {
32
+ return {
33
+ type: "single",
34
+ data: await scaffoldPluginDirectory(directory, options.id),
35
+ schema: scaffoldSchema,
36
+ };
37
+ }
38
+ export async function runPluginListCommand(options, _command) {
39
+ const data = await withPluginManagementClient(options.host, (client) => client.listPlugins());
40
+ return { type: "list", data, schema: pluginSchema };
41
+ }
42
+ export async function runPluginLogsCommand(pluginId, options, _command) {
43
+ const data = await withPluginLogsClient(options.host, (client) => client.getPluginLogs(pluginId));
44
+ return { type: "list", data, schema: pluginLogsSchema };
45
+ }
46
+ async function install(directory, options, _command) {
47
+ const data = await withPluginManagementClient(options.host, (client) => client.installDirectoryPlugin(directory, options.id));
48
+ return { type: "single", data, schema: pluginSchema };
49
+ }
50
+ async function act(action, pluginId, options) {
51
+ const data = await withPluginManagementClient(options.host, (client) => client[`${action}Plugin`](pluginId));
52
+ return { type: "single", data, schema: pluginSchema };
53
+ }
54
+ async function remove(pluginId, options, _command) {
55
+ const data = await withPluginManagementClient(options.host, async (client) => {
56
+ const current = (await client.listPlugins()).find((plugin) => plugin.id === pluginId);
57
+ if (!current)
58
+ throw new Error(`Plugin is not configured: ${pluginId}`);
59
+ await client.removePlugin(pluginId);
60
+ return { ...current, enabled: false, status: "disabled" };
61
+ });
62
+ return { type: "single", data, schema: pluginSchema };
63
+ }
64
+ export function createPluginCommand() {
65
+ const plugin = new Command("plugin").description("Manage trusted local plugins");
66
+ addJsonOption(plugin
67
+ .command("init")
68
+ .description("Create a typecheckable local plugin")
69
+ .argument("<directory>")
70
+ .option("--id <id>", "Manifest plugin ID (defaults to the directory name)")).action(withOutput(runPluginInitCommand));
71
+ addJsonAndDaemonHostOptions(plugin.command("ls").description("List configured plugins")).action(withOutput(runPluginListCommand));
72
+ addJsonAndDaemonHostOptions(plugin.command("logs").description("Show recent plugin output").argument("<id>")).action(withOutput(runPluginLogsCommand));
73
+ addJsonAndDaemonHostOptions(plugin
74
+ .command("install")
75
+ .description("Install a local plugin directory")
76
+ .argument("<directory>", "Host filesystem directory")
77
+ .option("--id <id>", "Runtime plugin ID (defaults to paseo-plugin.json id)")).action(withOutput(install));
78
+ for (const action of ["reload", "enable", "disable"]) {
79
+ addJsonAndDaemonHostOptions(plugin.command(action).description(`${action} a local plugin`).argument("<id>")).action(withOutput((id, options, _command) => act(action, id, options)));
80
+ }
81
+ addJsonAndDaemonHostOptions(plugin.command("remove").description("Remove plugin configuration").argument("<id>")).action(withOutput(remove));
82
+ return plugin;
83
+ }
84
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,6 @@
1
+ export interface PluginScaffold {
2
+ id: string;
3
+ directory: string;
4
+ }
5
+ export declare function scaffoldPluginDirectory(targetDirectory: string, requestedId?: string): Promise<PluginScaffold>;
6
+ //# sourceMappingURL=scaffold.d.ts.map
@@ -0,0 +1,307 @@
1
+ import { mkdir, readdir, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { PluginIdSchema } from "@getpaseo/protocol/messages";
4
+ const SDK_DECLARATIONS = `declare module "@paseo/plugin/server" {
5
+ import type { PaseoApi } from "@getpaseo/client";
6
+ import type { ZodType, input as ZodInput, output as ZodOutput } from "zod";
7
+
8
+ export interface PluginRpcContract<
9
+ InputSchema extends ZodType = ZodType,
10
+ OutputSchema extends ZodType = ZodType,
11
+ > {
12
+ name: string;
13
+ input: InputSchema;
14
+ output: OutputSchema;
15
+ }
16
+
17
+ export interface PluginAttachmentItem {
18
+ id: string;
19
+ identifier: string;
20
+ title: string;
21
+ subtitle?: string;
22
+ url: string;
23
+ text: string;
24
+ resourceType: string;
25
+ }
26
+
27
+ export interface PluginAttachmentSearchPayload {
28
+ items: PluginAttachmentItem[];
29
+ }
30
+
31
+ export interface PluginAttachmentSourceContribution {
32
+ id: string;
33
+ title: string;
34
+ icon: string;
35
+ pickerTitle: string;
36
+ searchPlaceholder: string;
37
+ search: PluginRpcContract;
38
+ }
39
+
40
+ export interface PluginHandlerContext {
41
+ paseo: PaseoApi;
42
+ }
43
+
44
+ export function defineRpc<InputSchema extends ZodType, OutputSchema extends ZodType>(definition: {
45
+ name: string;
46
+ input: InputSchema;
47
+ output: OutputSchema;
48
+ }): PluginRpcContract<InputSchema, OutputSchema>;
49
+
50
+ export function defineAttachmentSource<Definition extends PluginAttachmentSourceContribution>(
51
+ definition: Definition,
52
+ ): Definition;
53
+
54
+ export const PluginAttachmentItemSchema: import("zod").ZodType<PluginAttachmentItem>;
55
+ export const PluginAttachmentSearchPayloadSchema: import("zod").ZodType<PluginAttachmentSearchPayload>;
56
+ }
57
+
58
+ declare module "@paseo/plugin" {
59
+ import type { ComponentType } from "react";
60
+ import type { PaseoApi } from "@getpaseo/client";
61
+ import type { ZodType, input as ZodInput, output as ZodOutput } from "zod";
62
+ import type {
63
+ PluginAttachmentSourceContribution,
64
+ PluginHandlerContext,
65
+ PluginRpcContract,
66
+ } from "@paseo/plugin/server";
67
+
68
+ export {
69
+ PluginAttachmentItemSchema,
70
+ PluginAttachmentSearchPayloadSchema,
71
+ defineAttachmentSource,
72
+ defineRpc,
73
+ type PluginAttachmentItem,
74
+ type PluginAttachmentSearchPayload,
75
+ type PluginAttachmentSourceContribution,
76
+ type PluginHandlerContext,
77
+ type PluginRpcContract,
78
+ } from "@paseo/plugin/server";
79
+
80
+ export interface PluginTheme {
81
+ readonly colors: {
82
+ readonly surface0: string;
83
+ readonly foreground: string;
84
+ readonly foregroundMuted: string;
85
+ readonly accent: string;
86
+ readonly accentForeground: string;
87
+ readonly statusDanger: string;
88
+ };
89
+ }
90
+
91
+ export interface PluginHostProps {
92
+ theme: PluginTheme;
93
+ host: { id: string; label: string };
94
+ layout: { compact: boolean; platform: "ios" | "android" | "web" };
95
+ }
96
+
97
+ export interface PluginSurfaceProps extends PluginHostProps {}
98
+
99
+ export interface PluginWorkspaceSnapshot {
100
+ readonly id: string;
101
+ readonly projectId: string;
102
+ readonly projectDisplayName: string;
103
+ readonly projectRootPath: string;
104
+ readonly directory: string;
105
+ readonly projectKind: "git" | "non_git" | "directory";
106
+ readonly kind: "directory" | "local_checkout" | "checkout" | "worktree";
107
+ readonly name: string;
108
+ readonly title: string | null;
109
+ readonly status: "needs_input" | "failed" | "running" | "attention" | "done";
110
+ readonly statusEnteredAt: string | null;
111
+ readonly archivingAt: string | null;
112
+ readonly diffStat: { readonly additions: number; readonly deletions: number } | null;
113
+ }
114
+
115
+ export interface PluginAgentSnapshot {
116
+ readonly id: string;
117
+ readonly workspaceId: string;
118
+ readonly provider: string;
119
+ readonly status: "initializing" | "idle" | "running" | "error" | "closed";
120
+ readonly createdAt: string;
121
+ readonly updatedAt: string;
122
+ readonly lastActivityAt: string;
123
+ readonly title: string | null;
124
+ readonly cwd: string;
125
+ readonly model: string | null;
126
+ readonly currentModeId: string | null;
127
+ readonly thinkingOptionId: string | null;
128
+ readonly requiresAttention: boolean;
129
+ readonly attentionReason: "finished" | "error" | "permission" | null;
130
+ readonly parentAgentId: string | null;
131
+ readonly labels: Readonly<Record<string, string>>;
132
+ }
133
+
134
+ export interface PluginWorkspacePanelProps extends PluginHostProps {
135
+ context: "workspace";
136
+ workspaceId: string;
137
+ }
138
+
139
+ export interface PluginAgentPanelProps extends PluginHostProps {
140
+ context: "agent";
141
+ workspaceId: string;
142
+ agentId: string;
143
+ }
144
+
145
+ export type PluginWorkspacePanelContribution =
146
+ | { id: string; title: string; icon: string; context: "workspace"; Component: ComponentType<PluginWorkspacePanelProps> }
147
+ | { id: string; title: string; icon: string; context: "agent"; Component: ComponentType<PluginAgentPanelProps> };
148
+
149
+ export interface PluginSidebarContribution {
150
+ id: string;
151
+ title: string;
152
+ icon: string;
153
+ surface: string;
154
+ }
155
+
156
+ export interface PluginSurfaceContribution {
157
+ id: string;
158
+ Component: ComponentType<PluginSurfaceProps>;
159
+ }
160
+
161
+ export interface PluginCommandCapabilities {
162
+ paseo: PaseoApi;
163
+ rpc<InputSchema extends ZodType, OutputSchema extends ZodType>(
164
+ contract: PluginRpcContract<InputSchema, OutputSchema>,
165
+ input: ZodInput<InputSchema>,
166
+ ): Promise<ZodOutput<OutputSchema>>;
167
+ openSurface(id: string): void;
168
+ }
169
+
170
+ export interface PluginGlobalCommandContext extends PluginCommandCapabilities {
171
+ context: "global";
172
+ }
173
+
174
+ export interface PluginWorkspaceCommandContext extends PluginCommandCapabilities {
175
+ context: "workspace";
176
+ workspace: PluginWorkspaceSnapshot;
177
+ openPanel(id: string): void;
178
+ }
179
+
180
+ export interface PluginAgentCommandContext extends PluginCommandCapabilities {
181
+ context: "agent";
182
+ workspace: PluginWorkspaceSnapshot;
183
+ agent: PluginAgentSnapshot;
184
+ openPanel(id: string): void;
185
+ }
186
+
187
+ export type PluginCommandCenterItemContribution =
188
+ | { id: string; title: string; icon: string; keywords?: readonly string[]; context: "global"; onSelect(context: PluginGlobalCommandContext): void | Promise<void> }
189
+ | { id: string; title: string; icon: string; keywords?: readonly string[]; context: "workspace"; onSelect(context: PluginWorkspaceCommandContext): void | Promise<void> }
190
+ | { id: string; title: string; icon: string; keywords?: readonly string[]; context: "agent"; onSelect(context: PluginAgentCommandContext): void | Promise<void> };
191
+
192
+ export interface PluginContext {
193
+ handle<InputSchema extends ZodType, OutputSchema extends ZodType>(
194
+ contract: PluginRpcContract<InputSchema, OutputSchema>,
195
+ handler: (
196
+ input: ZodOutput<InputSchema>,
197
+ context: PluginHandlerContext,
198
+ ) => ZodInput<OutputSchema> | Promise<ZodInput<OutputSchema>>,
199
+ ): void;
200
+ addSurface(id: string, Component: ComponentType<PluginSurfaceProps>): void;
201
+ addSidebarItem(contribution: PluginSidebarContribution): void;
202
+ addWorkspacePanel(contribution: PluginWorkspacePanelContribution): void;
203
+ addCommandCenterItem(contribution: PluginCommandCenterItemContribution): void;
204
+ addAttachmentSource(contribution: PluginAttachmentSourceContribution): void;
205
+ }
206
+
207
+ export type PluginCleanup = () => void | Promise<void>;
208
+ export type PluginContribution = (plugin: PluginContext) => PluginCleanup;
209
+
210
+ export function useRpc<InputSchema extends ZodType, OutputSchema extends ZodType>(
211
+ contract: PluginRpcContract<InputSchema, OutputSchema>,
212
+ ): (input: ZodInput<InputSchema>) => Promise<ZodOutput<OutputSchema>>;
213
+
214
+ export function usePaseo(): PaseoApi;
215
+
216
+ export function useWorkspace<Selection>(
217
+ workspaceId: string,
218
+ selector: (workspace: PluginWorkspaceSnapshot) => Selection,
219
+ ): Selection | null;
220
+
221
+ export function useAgent<Selection>(
222
+ agentId: string,
223
+ selector: (agent: PluginAgentSnapshot) => Selection,
224
+ ): Selection | null;
225
+ }
226
+ `;
227
+ const TSCONFIG = {
228
+ compilerOptions: {
229
+ target: "ES2020",
230
+ module: "ESNext",
231
+ moduleResolution: "Bundler",
232
+ lib: ["ES2023", "DOM"],
233
+ jsx: "react-jsx",
234
+ strict: true,
235
+ skipLibCheck: true,
236
+ noEmit: true,
237
+ esModuleInterop: true,
238
+ allowSyntheticDefaultImports: true,
239
+ },
240
+ include: ["**/*.ts", "**/*.tsx"],
241
+ };
242
+ const ENTRY = `import type { PluginContext } from "@paseo/plugin";
243
+ import { MainSurface } from "./main.client";
244
+
245
+ export default function contribute(plugin: PluginContext) {
246
+ plugin.addSurface("main", MainSurface);
247
+ return () => {};
248
+ }
249
+ `;
250
+ const CLIENT_SURFACE = `import type { PluginSurfaceProps } from "@paseo/plugin";
251
+ import React, { useMemo } from "react";
252
+ import { Text, View } from "react-native";
253
+
254
+ export function MainSurface({ theme, layout }: PluginSurfaceProps) {
255
+ const styles = useMemo(
256
+ () => ({
257
+ screen: {
258
+ flex: 1,
259
+ padding: layout.compact ? 16 : 24,
260
+ backgroundColor: theme.colors.surface0,
261
+ },
262
+ text: { color: theme.colors.foreground },
263
+ }),
264
+ [theme, layout.compact],
265
+ );
266
+ return (
267
+ <View style={styles.screen}>
268
+ <Text style={styles.text}>Hello from my plugin</Text>
269
+ </View>
270
+ );
271
+ }
272
+ `;
273
+ export async function scaffoldPluginDirectory(targetDirectory, requestedId) {
274
+ const directory = path.resolve(targetDirectory);
275
+ const id = PluginIdSchema.parse(requestedId ?? path.basename(directory));
276
+ await mkdir(directory, { recursive: true });
277
+ const existing = await readdir(directory);
278
+ if (existing.length > 0) {
279
+ throw new Error(`Plugin directory must be empty: ${directory}`);
280
+ }
281
+ const packageJson = {
282
+ name: id,
283
+ private: true,
284
+ version: "0.0.0",
285
+ scripts: { typecheck: "tsc --noEmit" },
286
+ devDependencies: {
287
+ "@getpaseo/client": "^0.4.0",
288
+ "@tanstack/react-query": "^5.90.11",
289
+ "@types/react": "~19.2.0",
290
+ react: "19.1.0",
291
+ "react-native": "0.81.5",
292
+ typescript: "^5.9.3",
293
+ zod: "^4.4.3",
294
+ },
295
+ };
296
+ const files = new Map([
297
+ ["paseo-plugin.json", `${JSON.stringify({ id }, null, 2)}\n`],
298
+ ["package.json", `${JSON.stringify(packageJson, null, 2)}\n`],
299
+ ["tsconfig.json", `${JSON.stringify(TSCONFIG, null, 2)}\n`],
300
+ ["paseo-plugin.d.ts", SDK_DECLARATIONS],
301
+ ["index.ts", ENTRY],
302
+ ["main.client.tsx", CLIENT_SURFACE],
303
+ ]);
304
+ await Promise.all([...files].map(([filename, contents]) => writeFile(path.join(directory, filename), contents, { flag: "wx" })));
305
+ return { id, directory };
306
+ }
307
+ //# sourceMappingURL=scaffold.js.map
@@ -0,0 +1,4 @@
1
+ import type { DaemonClient } from "@getpaseo/client/internal/daemon-client";
2
+ export declare function withPluginManagementClient<T>(host: string | undefined, run: (client: DaemonClient) => Promise<T>): Promise<T>;
3
+ export declare function withPluginLogsClient<T>(host: string | undefined, run: (client: DaemonClient) => Promise<T>): Promise<T>;
4
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1,26 @@
1
+ import { connectToDaemon } from "../../utils/client.js";
2
+ async function withPluginClient(host, feature, updateMessage, run) {
3
+ const client = await connectToDaemon({ host });
4
+ if (client.getLastServerInfoMessage()?.features?.[feature] !== true) {
5
+ await client.close().catch(() => undefined);
6
+ throw {
7
+ code: "DAEMON_UPDATE_REQUIRED",
8
+ message: updateMessage,
9
+ };
10
+ }
11
+ try {
12
+ return await run(client);
13
+ }
14
+ finally {
15
+ await client.close().catch(() => undefined);
16
+ }
17
+ }
18
+ export async function withPluginManagementClient(host, run) {
19
+ // COMPAT(pluginManagement): added in v0.3.1, remove gate after 2027-08-14.
20
+ return withPluginClient(host, "pluginManagement", "Update the host to use plugin management.", run);
21
+ }
22
+ export async function withPluginLogsClient(host, run) {
23
+ // COMPAT(pluginLogs): added in v0.4.0, remove gate after 2027-08-16.
24
+ return withPluginClient(host, "pluginLogs", "Update the host to view plugin logs.", run);
25
+ }
26
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1,10 @@
1
+ import type { Command } from "commander";
2
+ import type { CommandOptions, SingleResult } from "../../output/index.js";
3
+ import { type ProjectRow } from "./shared.js";
4
+ export declare function resolveProjectPath(input: {
5
+ pathArg?: string;
6
+ cwd: string;
7
+ daemonTarget?: string;
8
+ }): string;
9
+ export declare function runCreateCommand(pathArg: string | undefined, options: CommandOptions, _command: Command): Promise<SingleResult<ProjectRow>>;
10
+ //# sourceMappingURL=create.d.ts.map
@@ -0,0 +1,41 @@
1
+ import path from "node:path";
2
+ import { buildDaemonConnectionCommandError, connectToDaemon } from "../../utils/client.js";
3
+ import { projectSchema, toProjectRow } from "./shared.js";
4
+ export function resolveProjectPath(input) {
5
+ if (input.daemonTarget) {
6
+ if (input.pathArg === undefined) {
7
+ throw {
8
+ code: "MISSING_PATH",
9
+ message: "Project path is required when targeting a daemon explicitly",
10
+ details: "Usage: paseo project create <path> --host <host>",
11
+ };
12
+ }
13
+ return input.pathArg;
14
+ }
15
+ return path.resolve(input.cwd, input.pathArg ?? ".");
16
+ }
17
+ export async function runCreateCommand(pathArg, options, _command) {
18
+ const projectPath = resolveProjectPath({
19
+ pathArg,
20
+ cwd: process.cwd(),
21
+ daemonTarget: options.host ?? process.env.PASEO_HOST,
22
+ });
23
+ const client = await connectToDaemon({ host: options.host }).catch((error) => {
24
+ throw buildDaemonConnectionCommandError({ host: options.host, error });
25
+ });
26
+ try {
27
+ const payload = await client.addProject(projectPath);
28
+ if (!payload.project) {
29
+ throw new Error(payload.error ?? "Project creation failed");
30
+ }
31
+ return { type: "single", data: toProjectRow(payload.project), schema: projectSchema };
32
+ }
33
+ catch (error) {
34
+ const message = error instanceof Error ? error.message : String(error);
35
+ throw { code: "PROJECT_CREATE_FAILED", message };
36
+ }
37
+ finally {
38
+ await client.close().catch(() => undefined);
39
+ }
40
+ }
41
+ //# sourceMappingURL=create.js.map
@@ -0,0 +1,9 @@
1
+ import type { Command } from "commander";
2
+ import type { CommandOptions, SingleResult } from "../../output/index.js";
3
+ interface ProjectDeleteResult {
4
+ projectId: string;
5
+ removedWorkspaceIds: string[];
6
+ }
7
+ export declare function runDeleteCommand(projectId: string, options: CommandOptions, _command: Command): Promise<SingleResult<ProjectDeleteResult>>;
8
+ export {};
9
+ //# sourceMappingURL=delete.d.ts.map
@@ -0,0 +1,33 @@
1
+ import { buildDaemonConnectionCommandError, connectToDaemon } from "../../utils/client.js";
2
+ const projectDeleteSchema = {
3
+ idField: "projectId",
4
+ columns: [
5
+ { header: "PROJECT ID", field: "projectId", width: 20 },
6
+ {
7
+ header: "REMOVED WORKSPACES",
8
+ field: (result) => result.removedWorkspaceIds.length,
9
+ width: 18,
10
+ },
11
+ ],
12
+ };
13
+ export async function runDeleteCommand(projectId, options, _command) {
14
+ const client = await connectToDaemon({ host: options.host }).catch((error) => {
15
+ throw buildDaemonConnectionCommandError({ host: options.host, error });
16
+ });
17
+ try {
18
+ const result = await client.removeProject(projectId);
19
+ return {
20
+ type: "single",
21
+ data: { projectId, removedWorkspaceIds: result.removedWorkspaceIds },
22
+ schema: projectDeleteSchema,
23
+ };
24
+ }
25
+ catch (error) {
26
+ const message = error instanceof Error ? error.message : String(error);
27
+ throw { code: "PROJECT_DELETE_FAILED", message };
28
+ }
29
+ finally {
30
+ await client.close().catch(() => undefined);
31
+ }
32
+ }
33
+ //# sourceMappingURL=delete.js.map
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare function createProjectCommand(): Command;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,28 @@
1
+ import { Command } from "commander";
2
+ import { withOutput } from "../../output/index.js";
3
+ import { addJsonAndDaemonHostOptions } from "../../utils/command-options.js";
4
+ import { runCreateCommand } from "./create.js";
5
+ import { runDeleteCommand } from "./delete.js";
6
+ import { runLsCommand } from "./ls.js";
7
+ import { runRenameCommand } from "./rename.js";
8
+ export function createProjectCommand() {
9
+ const project = new Command("project").description("Manage projects");
10
+ addJsonAndDaemonHostOptions(project
11
+ .command("create")
12
+ .description("Register a project directory")
13
+ .argument("[path]", "Project directory (default: current directory)")).action(withOutput(runCreateCommand));
14
+ addJsonAndDaemonHostOptions(project.command("ls").description("List projects")).action(withOutput(runLsCommand));
15
+ addJsonAndDaemonHostOptions(project
16
+ .command("rename")
17
+ .description("Set a project's user-visible name")
18
+ .argument("<project-id>", "Project id")
19
+ .argument("[name]", "New project name")
20
+ .option("--reset", "Clear the custom name and use the directory name")
21
+ .allowExcessArguments(false)).action(withOutput(runRenameCommand));
22
+ addJsonAndDaemonHostOptions(project
23
+ .command("delete")
24
+ .description("Delete a project and its workspaces")
25
+ .argument("<project-id>", "Project id")).action(withOutput(runDeleteCommand));
26
+ return project;
27
+ }
28
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,5 @@
1
+ import type { Command } from "commander";
2
+ import type { CommandOptions, ListResult } from "../../output/index.js";
3
+ import { type ProjectRow } from "./shared.js";
4
+ export declare function runLsCommand(options: CommandOptions, _command: Command): Promise<ListResult<ProjectRow>>;
5
+ //# sourceMappingURL=ls.d.ts.map
@@ -0,0 +1,19 @@
1
+ import { buildDaemonConnectionCommandError, connectToDaemon } from "../../utils/client.js";
2
+ import { projectSchema, toProjectRow } from "./shared.js";
3
+ export async function runLsCommand(options, _command) {
4
+ const client = await connectToDaemon({ host: options.host }).catch((error) => {
5
+ throw buildDaemonConnectionCommandError({ host: options.host, error });
6
+ });
7
+ try {
8
+ const payload = await client.listProjects();
9
+ return {
10
+ type: "list",
11
+ data: payload.projects.map(toProjectRow),
12
+ schema: projectSchema,
13
+ };
14
+ }
15
+ finally {
16
+ await client.close().catch(() => undefined);
17
+ }
18
+ }
19
+ //# sourceMappingURL=ls.js.map
@@ -0,0 +1,15 @@
1
+ import type { Command } from "commander";
2
+ import type { CommandOptions, SingleResult } from "../../output/index.js";
3
+ interface ProjectRenameResult {
4
+ projectId: string;
5
+ name: string | null;
6
+ }
7
+ export declare function resolveProjectName(input: {
8
+ name?: string;
9
+ reset?: boolean;
10
+ }): string | null;
11
+ export declare function runRenameCommand(projectId: string, nameArg: string | undefined, options: CommandOptions & {
12
+ reset?: boolean;
13
+ }, _command: Command): Promise<SingleResult<ProjectRenameResult>>;
14
+ export {};
15
+ //# sourceMappingURL=rename.d.ts.map
@@ -0,0 +1,50 @@
1
+ import { buildDaemonConnectionCommandError, connectToDaemon } from "../../utils/client.js";
2
+ const projectRenameSchema = {
3
+ idField: "projectId",
4
+ columns: [
5
+ { header: "PROJECT ID", field: "projectId", width: 20 },
6
+ { header: "NAME", field: "name", width: 30 },
7
+ ],
8
+ };
9
+ export function resolveProjectName(input) {
10
+ if (input.reset) {
11
+ if (input.name !== undefined) {
12
+ throw {
13
+ code: "INVALID_OPTIONS",
14
+ message: "--reset cannot be combined with a name",
15
+ };
16
+ }
17
+ return null;
18
+ }
19
+ const name = input.name?.trim() ?? "";
20
+ if (name.length === 0) {
21
+ throw {
22
+ code: "MISSING_NAME",
23
+ message: "Project name cannot be empty",
24
+ details: "Usage: paseo project rename <project-id> <name> | --reset",
25
+ };
26
+ }
27
+ return name;
28
+ }
29
+ export async function runRenameCommand(projectId, nameArg, options, _command) {
30
+ const name = resolveProjectName({ name: nameArg, reset: options.reset });
31
+ const client = await connectToDaemon({ host: options.host }).catch((error) => {
32
+ throw buildDaemonConnectionCommandError({ host: options.host, error });
33
+ });
34
+ try {
35
+ const applied = await client.renameProject(projectId, name);
36
+ return {
37
+ type: "single",
38
+ data: { projectId, name: applied.customName },
39
+ schema: projectRenameSchema,
40
+ };
41
+ }
42
+ catch (error) {
43
+ const message = error instanceof Error ? error.message : String(error);
44
+ throw { code: "PROJECT_RENAME_FAILED", message };
45
+ }
46
+ finally {
47
+ await client.close().catch(() => undefined);
48
+ }
49
+ }
50
+ //# sourceMappingURL=rename.js.map
@@ -0,0 +1,11 @@
1
+ import type { WorkspaceProjectDescriptorPayload } from "@getpaseo/protocol/messages";
2
+ import type { OutputSchema } from "../../output/index.js";
3
+ export interface ProjectRow {
4
+ projectId: string;
5
+ name: string;
6
+ kind: "git" | "non_git" | "directory";
7
+ path: string;
8
+ }
9
+ export declare const projectSchema: OutputSchema<ProjectRow>;
10
+ export declare function toProjectRow(project: WorkspaceProjectDescriptorPayload): ProjectRow;
11
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1,18 @@
1
+ export const projectSchema = {
2
+ idField: "projectId",
3
+ columns: [
4
+ { header: "PROJECT ID", field: "projectId", width: 20 },
5
+ { header: "NAME", field: "name", width: 24 },
6
+ { header: "KIND", field: "kind", width: 10 },
7
+ { header: "PATH", field: "path", width: 42 },
8
+ ],
9
+ };
10
+ export function toProjectRow(project) {
11
+ return {
12
+ projectId: project.projectId,
13
+ name: project.projectDisplayName,
14
+ kind: project.projectKind,
15
+ path: project.projectRootPath,
16
+ };
17
+ }
18
+ //# sourceMappingURL=shared.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getpaseo/cli",
3
- "version": "0.4.0",
3
+ "version": "0.5.0-beta.1",
4
4
  "description": "Paseo CLI - control your AI coding agents from the command line",
5
5
  "bin": {
6
6
  "paseo": "bin/paseo"
@@ -28,9 +28,9 @@
28
28
  },
29
29
  "dependencies": {
30
30
  "@clack/prompts": "^1.0.0",
31
- "@getpaseo/client": "0.4.0",
32
- "@getpaseo/protocol": "0.4.0",
33
- "@getpaseo/server": "0.4.0",
31
+ "@getpaseo/client": "0.5.0-beta.1",
32
+ "@getpaseo/protocol": "0.5.0-beta.1",
33
+ "@getpaseo/server": "0.5.0-beta.1",
34
34
  "chalk": "^5.3.0",
35
35
  "commander": "^12.0.0",
36
36
  "mime-types": "^2.1.35",