epismo 0.1.7 → 0.3.0

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/program.js CHANGED
@@ -3,18 +3,19 @@ import { clearWorkspace, getCurrentWorkspace, listAvailableWorkspaces, login, lo
3
3
  import { resolveExecutionContext } from "./context.js";
4
4
  import { mergeDefined, parseJsonArrayOption, parseJsonObjectOption, parseOptionalPositiveInteger, parseStringArrayInput, readJsonObjectInput } from "./input.js";
5
5
  import { printJson, printWarning } from "./output.js";
6
- import { deleteTrack, getTrack, searchTracks, upsertTrack } from "./tracks.js";
7
- import { deletePack, getPack, likePack, searchPacks, upsertPack } from "./packs.js";
6
+ import { applyTracks, createTrack, deleteTrack, getTrack, searchTracks, updateTrack } from "./tracks.js";
7
+ import { createPack, deletePack, getPack, likePack, searchPacks, updatePack } from "./packs.js";
8
8
  import { deleteAlias, getAlias, listAliases, upsertAlias } from "./aliases.js";
9
9
  import { creditBalance, creditCheckout } from "./credits.js";
10
10
  import { CliError } from "./errors.js";
11
+ import { addAgents, listAgents, removeAgents } from "./agents.js";
12
+ import { createWorkspace, deleteWorkspaceMember, getWorkspaceCheckout, listWorkspaceMembers, updateWorkspace, upsertWorkspaceMember } from "./workspaces.js";
13
+ import { addProjectMember, createProject, deleteProject, deleteProjectMember, listProjectMembers, listProjects, updateProject } from "./projects.js";
14
+ import { createToken } from "./token.js";
11
15
  const TRACK_TYPES = ["task", "goal"];
12
16
  const PACK_TYPES = ["workflow", "context"];
13
17
  const PACK_VISIBILITIES = ["public", "private"];
14
- // #1: centralize --workspace-id option so description is defined once
15
- const WORKSPACE_ID_OPT = "--workspace-id <workspaceId>";
16
- const WORKSPACE_ID_DESC = "workspace to use; resolved in order: CLI arg → saved default → personal space";
17
- // #2: centralize warning codes so wording is consistent everywhere
18
+ // centralize warning codes so wording is consistent everywhere
18
19
  const WARNING_ENV_TOKEN_WORKSPACE_IGNORED = "EPISMO_TOKEN_WORKSPACE_IGNORED";
19
20
  async function resolveInput(options, overrides) {
20
21
  const base = await readJsonObjectInput(options.input);
@@ -27,8 +28,8 @@ function getExplicitOptionOverride(command, optionName, value) {
27
28
  }
28
29
  return value;
29
30
  }
30
- async function resolveContext(options) {
31
- const context = await resolveExecutionContext(options);
31
+ async function resolveContext() {
32
+ const context = await resolveExecutionContext();
32
33
  if (context.warning) {
33
34
  printWarning({
34
35
  warning: { code: WARNING_ENV_TOKEN_WORKSPACE_IGNORED, message: context.warning }
@@ -48,11 +49,12 @@ export function buildProgram(version) {
48
49
  .exitOverride();
49
50
  program.addHelpText("after", `
50
51
  Workspace resolution:
51
- Commands that accept --workspace-id resolve the workspace in this order:
52
- 1. --workspace-id flag
53
- 2. Saved default (epismo workspace use)
54
- 3. Personal space (fallback)
55
- When EPISMO_TOKEN is set, the saved default is skipped.
52
+ All commands resolve workspace in this order:
53
+ 1. Saved default (epismo workspace use)
54
+ 2. Personal space (fallback)
55
+ When EPISMO_TOKEN is set, the saved default is skipped — the workspace is
56
+ resolved from the token itself. Use epismo token create --workspace-id to
57
+ issue a workspace-scoped token for CI/CD.
56
58
 
57
59
  Examples:
58
60
  epismo login
@@ -105,17 +107,17 @@ Examples:
105
107
  program
106
108
  .command("whoami")
107
109
  .description("show the currently authenticated user")
108
- .option(WORKSPACE_ID_OPT, WORKSPACE_ID_DESC)
109
- .action(async (options) => {
110
- const context = await resolveContext({ workspaceId: options.workspaceId });
110
+ .action(async () => {
111
+ const context = await resolveContext();
111
112
  printJson(await whoami({ workspaceId: context.workspaceId, auth: context.auth }));
112
113
  });
113
114
  program.commands.at(-1)?.addHelpText("after", `
114
115
  Examples:
115
116
  epismo whoami
116
- EPISMO_TOKEN=<access-token> epismo whoami
117
- epismo whoami --workspace-id <workspace-id>`);
118
- const workspace = program.command("workspace").description("manage the default workspace");
117
+ EPISMO_TOKEN=<access-token> epismo whoami`);
118
+ const workspace = program
119
+ .command("workspace")
120
+ .description("manage workspaces and the saved default workspace");
119
121
  workspace
120
122
  .command("list")
121
123
  .description("list accessible workspaces")
@@ -153,53 +155,252 @@ Example:
153
155
  workspace.commands.at(-1)?.addHelpText("after", `
154
156
  Example:
155
157
  epismo workspace clear`);
156
- const track = program
157
- .command("track")
158
- .description("manage project tracks (tasks and goals)");
159
- track
158
+ workspace
159
+ .command("create")
160
+ .description("create a new workspace")
161
+ .option("--input <input>", "JSON object, @file, or - for stdin")
162
+ .option("--name <name>", "workspace name")
163
+ .addOption(new Option("--plan <plan>", "basic | pro").choices(["basic", "pro"]))
164
+ .action(async (options) => {
165
+ const payload = await resolveInput({ input: options.input }, { name: options.name, plan: options.plan });
166
+ const context = await resolveContext();
167
+ const created = await createWorkspace(context, payload);
168
+ const workspaceId = created.workspace?.id;
169
+ if (workspaceId) {
170
+ const checkout = await getWorkspaceCheckout(context, workspaceId);
171
+ printJson({ ...created, ...checkout });
172
+ }
173
+ else {
174
+ printJson(created);
175
+ }
176
+ });
177
+ workspace.commands.at(-1)?.addHelpText("after", `
178
+ Examples:
179
+ epismo workspace create --name my-team --plan basic
180
+ epismo workspace create --input @workspace.json`);
181
+ workspace
182
+ .command("checkout")
183
+ .description("get the billing URL for a workspace (to complete or retry payment)")
184
+ .requiredOption("--id <id>", "workspace id")
185
+ .action(async (options) => {
186
+ const context = await resolveContext();
187
+ printJson(await getWorkspaceCheckout(context, options.id));
188
+ });
189
+ workspace.commands.at(-1)?.addHelpText("after", `
190
+ Examples:
191
+ epismo workspace checkout --id <workspace-id>`);
192
+ workspace
193
+ .command("update")
194
+ .description("update a workspace you own (PATCH — omitted fields are unchanged)")
195
+ .option("--input <input>", "JSON object, @file, or - for stdin")
196
+ .requiredOption("--id <id>", "workspace id to update")
197
+ .option("--name <name>", "updated workspace name")
198
+ .addOption(new Option("--plan <plan>", "basic | pro").choices(["basic", "pro"]))
199
+ .action(async (options) => {
200
+ const payload = await resolveInput({ input: options.input }, { name: options.name, plan: options.plan });
201
+ const context = await resolveContext();
202
+ printJson(await updateWorkspace(context, options.id, payload));
203
+ });
204
+ const workspaceMember = workspace.command("member").description("manage workspace members");
205
+ workspaceMember
206
+ .command("list")
207
+ .description("list members in a workspace")
208
+ .action(async () => {
209
+ const context = await resolveContext();
210
+ printJson(await listWorkspaceMembers(context, { workspaceId: context.workspaceId }));
211
+ });
212
+ workspaceMember
160
213
  .command("upsert")
161
- .description("create or update a project track (task or goal)")
214
+ .description("add a workspace member or update the member role")
215
+ .option("--input <input>", "JSON object, @file, or - for stdin")
216
+ .option("--user-id <userId>", "user id")
217
+ .addOption(new Option("--role <role>", "owner | admin | member").choices([
218
+ "owner",
219
+ "admin",
220
+ "member"
221
+ ]))
222
+ .action(async (options) => {
223
+ const context = await resolveContext();
224
+ const payload = await resolveInput({ input: options.input }, {
225
+ workspaceId: context.workspaceId,
226
+ userId: options.userId,
227
+ role: options.role
228
+ });
229
+ printJson(await upsertWorkspaceMember(context, payload));
230
+ });
231
+ workspaceMember
232
+ .command("delete")
233
+ .description("remove a workspace member")
234
+ .option("--input <input>", "JSON object, @file, or - for stdin")
235
+ .option("--user-id <userId>", "user id")
236
+ .action(async (options) => {
237
+ const context = await resolveContext();
238
+ const payload = await resolveInput({ input: options.input }, {
239
+ workspaceId: context.workspaceId,
240
+ userId: options.userId
241
+ });
242
+ printJson(await deleteWorkspaceMember(context, payload));
243
+ });
244
+ const project = program.command("project").description("manage workspace projects");
245
+ project
246
+ .command("list")
247
+ .description("list projects visible in the selected workspace")
248
+ .action(async () => {
249
+ const context = await resolveContext();
250
+ printJson(await listProjects(context));
251
+ });
252
+ project
253
+ .command("create")
254
+ .description("create a project in the selected workspace")
255
+ .option("--input <input>", "JSON object, @file, or - for stdin")
256
+ .option("--name <name>", "project name")
257
+ .option("--description <description>", "project description")
258
+ .action(async (options) => {
259
+ const payload = await resolveInput(options, {
260
+ name: options.name,
261
+ description: options.description
262
+ });
263
+ const context = await resolveContext();
264
+ printJson(await createProject(context, payload));
265
+ });
266
+ project
267
+ .command("update")
268
+ .description("update a project you can access (PATCH — omitted fields are unchanged)")
269
+ .option("--input <input>", "JSON object, @file, or - for stdin")
270
+ .requiredOption("--id <id>", "project id to update")
271
+ .option("--name <name>", "updated project name")
272
+ .option("--description <description>", "updated project description")
273
+ .action(async (options) => {
274
+ const payload = await resolveInput(options, {
275
+ name: options.name,
276
+ description: options.description
277
+ });
278
+ const context = await resolveContext();
279
+ printJson(await updateProject(context, options.id, payload));
280
+ });
281
+ project
282
+ .command("delete")
283
+ .description("delete a project you can access in the selected workspace")
284
+ .option("--input <input>", "JSON object, @file, or - for stdin")
285
+ .option("--id <id>", "project id")
286
+ .action(async (options) => {
287
+ const payload = await resolveInput(options, {
288
+ id: options.id
289
+ });
290
+ const context = await resolveContext();
291
+ printJson(await deleteProject(context, payload));
292
+ });
293
+ const projectMember = project.command("member").description("manage project members");
294
+ projectMember
295
+ .command("list")
296
+ .description("list members across one or more projects")
297
+ .option("--input <input>", "JSON object, @file, or - for stdin")
298
+ .option("--project-ids <projectIds>", "JSON array or comma-separated project ids")
299
+ .action(async (options) => {
300
+ const payload = await resolveInput(options, {
301
+ projectIds: parseStringArrayInput(options.projectIds, "--project-ids")
302
+ });
303
+ const context = await resolveContext();
304
+ printJson(await listProjectMembers(context, payload));
305
+ });
306
+ projectMember
307
+ .command("add")
308
+ .description("add a member to a project")
309
+ .option("--input <input>", "JSON object, @file, or - for stdin")
310
+ .option("--project-id <projectId>", "project id")
311
+ .option("--user-id <userId>", "user id")
312
+ .action(async (options) => {
313
+ const payload = await resolveInput(options, {
314
+ projectId: options.projectId,
315
+ userId: options.userId
316
+ });
317
+ const context = await resolveContext();
318
+ printJson(await addProjectMember(context, payload));
319
+ });
320
+ projectMember
321
+ .command("delete")
322
+ .description("remove a user from a project")
323
+ .option("--input <input>", "JSON object, @file, or - for stdin")
324
+ .option("--project-id <projectId>", "project id")
325
+ .option("--user-id <userId>", "user id")
326
+ .action(async (options) => {
327
+ const payload = await resolveInput(options, {
328
+ projectId: options.projectId,
329
+ userId: options.userId
330
+ });
331
+ const context = await resolveContext();
332
+ printJson(await deleteProjectMember(context, payload));
333
+ });
334
+ const track = program.command("track").description("manage project tracks (tasks and goals)");
335
+ track
336
+ .command("create")
337
+ .description("create a new project track (task or goal)")
162
338
  .option("--input <input>", "JSON object, @file, or - for stdin")
163
339
  .addOption(new Option("--type <type>", "task | goal").choices(TRACK_TYPES))
164
- .option("--id <id>", "existing item id")
165
340
  .option("--title <title>", "title")
166
341
  .option("--content <content>", "markdown content")
167
342
  .option("--projects <projects>", "JSON array or comma-separated project ids")
168
343
  .option("--task <task>", 'task-specific fields as JSON object e.g. \'{"status":"todo","dueDate":"2025-12-31"}\'')
169
344
  .option("--goal <goal>", 'goal-specific fields as JSON object e.g. \'{"status":"on_track","dueDate":"2025-12-31"}\'')
170
- .option(WORKSPACE_ID_OPT, WORKSPACE_ID_DESC)
171
345
  .action(async (options) => {
172
346
  const payload = await resolveInput(options, {
173
347
  type: options.type,
174
- id: options.id,
175
348
  title: options.title,
176
349
  content: options.content,
177
350
  projects: parseStringArrayInput(options.projects, "--projects"),
178
351
  task: parseJsonObjectOption(options.task, "--task"),
179
352
  goal: parseJsonObjectOption(options.goal, "--goal")
180
353
  });
181
- const context = await resolveContext({ workspaceId: options.workspaceId });
182
- printJson(await upsertTrack(context, payload));
354
+ const context = await resolveContext();
355
+ printJson(await createTrack(context, payload));
183
356
  });
184
357
  track.commands.at(-1)?.addHelpText("after", `
185
358
  Notes:
186
359
  Use --task or --goal to pass type-specific fields.
187
360
 
188
361
  Examples:
189
- epismo track upsert --type task --title "Fix bug" --content "Details..."
190
- epismo track upsert --input @task.json
191
- epismo track upsert --type goal --title "Launch" --goal '{"status":"on_track"}'`);
362
+ epismo track create --type task --title "Fix bug" --content "Details..."
363
+ epismo track create --input @task.json
364
+ epismo track create --type goal --title "Launch" --goal '{"status":"on_track"}'`);
365
+ track
366
+ .command("update")
367
+ .description("update an existing project track (PATCH — omitted fields are unchanged)")
368
+ .option("--input <input>", "JSON object, @file, or - for stdin")
369
+ .requiredOption("--id <id>", "track id to update")
370
+ .option("--title <title>", "updated title")
371
+ .option("--content <content>", "updated markdown content")
372
+ .option("--projects <projects>", "JSON array or comma-separated project ids")
373
+ .option("--task <task>", 'task-specific fields as partial JSON object e.g. \'{"status":"done"}\'')
374
+ .option("--goal <goal>", 'goal-specific fields as partial JSON object e.g. \'{"progress":50}\'')
375
+ .action(async (options) => {
376
+ const payload = await resolveInput(options, {
377
+ title: options.title,
378
+ content: options.content,
379
+ projects: parseStringArrayInput(options.projects, "--projects"),
380
+ task: parseJsonObjectOption(options.task, "--task"),
381
+ goal: parseJsonObjectOption(options.goal, "--goal")
382
+ });
383
+ const context = await resolveContext();
384
+ printJson(await updateTrack(context, options.id, payload));
385
+ });
386
+ track.commands.at(-1)?.addHelpText("after", `
387
+ Notes:
388
+ Omitted fields keep their existing value.
389
+
390
+ Examples:
391
+ epismo track update --id <id> --title "Updated title"
392
+ epismo track update --id <id> --task '{"status":"done"}'
393
+ epismo track update --id <id> --goal '{"progress":75}'`);
192
394
  track
193
395
  .command("get")
194
396
  .description("fetch one project track")
195
397
  .option("--input <input>", "JSON object, @file, or - for stdin")
196
398
  .option("--id <id>", "item id")
197
- .option(WORKSPACE_ID_OPT, WORKSPACE_ID_DESC)
198
399
  .action(async (options) => {
199
400
  const payload = await resolveInput(options, {
200
401
  id: options.id
201
402
  });
202
- const context = await resolveContext({ workspaceId: options.workspaceId });
403
+ const context = await resolveContext();
203
404
  printJson(await getTrack(context, payload));
204
405
  });
205
406
  track.commands.at(-1)?.addHelpText("after", `
@@ -214,7 +415,6 @@ Example:
214
415
  .option("--page <page>", "1-based page number")
215
416
  .option("--projects <projects>", "JSON array or comma-separated project ids")
216
417
  .option("--filter <filter>", 'filter as JSON object; each value is an array of accepted values e.g. \'{"status":["todo","in_progress"]}\' (use --input @file.json for complex filters)')
217
- .option(WORKSPACE_ID_OPT, WORKSPACE_ID_DESC)
218
418
  .action(async (options, command) => {
219
419
  const payload = await resolveInput(options, {
220
420
  type: getExplicitOptionOverride(command, "type", options.type),
@@ -223,7 +423,7 @@ Example:
223
423
  projects: getExplicitOptionOverride(command, "projects", parseStringArrayInput(options.projects, "--projects")),
224
424
  filter: getExplicitOptionOverride(command, "filter", parseJsonObjectOption(options.filter, "--filter"))
225
425
  });
226
- const context = await resolveContext({ workspaceId: options.workspaceId });
426
+ const context = await resolveContext();
227
427
  printJson(await searchTracks(context, payload));
228
428
  });
229
429
  track.commands.at(-1)?.addHelpText("after", `
@@ -236,27 +436,43 @@ Examples:
236
436
  .description("delete one project track")
237
437
  .option("--input <input>", "JSON object, @file, or - for stdin")
238
438
  .option("--id <id>", "item id")
239
- .option(WORKSPACE_ID_OPT, WORKSPACE_ID_DESC)
240
439
  .action(async (options) => {
241
440
  const payload = await resolveInput(options, {
242
441
  id: options.id
243
442
  });
244
- const context = await resolveContext({ workspaceId: options.workspaceId });
443
+ const context = await resolveContext();
245
444
  printJson(await deleteTrack(context, payload));
246
445
  });
247
446
  track.commands.at(-1)?.addHelpText("after", `
248
447
  Example:
249
448
  epismo track delete --id <id>`);
250
- const pack = program
251
- .command("pack")
252
- .alias("asset")
253
- .description("manage agent packs (workflows and contexts)");
254
- pack
255
- .command("upsert")
256
- .description("create or update an agent pack")
449
+ track
450
+ .command("apply")
451
+ .description("create, update, and delete multiple tracks in a single request")
452
+ .option("--input <input>", "JSON object, @file, or - for stdin")
453
+ .option("--projects <projects>", "JSON array or comma-separated project ids")
454
+ .action(async (options) => {
455
+ const payload = await resolveInput(options, {
456
+ projects: parseStringArrayInput(options.projects, "--projects")
457
+ });
458
+ const context = await resolveContext();
459
+ printJson(await applyTracks(context, payload));
460
+ });
461
+ track.commands.at(-1)?.addHelpText("after", `
462
+ Notes:
463
+ updateDrafts: use a non-UUID string as id (e.g. "t001") to create a new track, or a UUID to update an existing one.
464
+ Labels in task.parentId, task.dependsOn, and task.goalId are resolved by the server, so you can wire
465
+ up dependencies between new entries in the same request.
466
+ deleteDrafts: provide UUIDs of tracks to delete.
467
+
468
+ Examples:
469
+ epismo track apply --input @batch.json
470
+ epismo track apply --input '{"updateDrafts":[{"id":"t001","title":"Task A","task":{"status":"todo"}},{"id":"t002","title":"Task B","task":{"status":"todo","dependsOn":["t001"]}}]}'`);
471
+ const pack = program.command("pack").description("manage agent packs (workflows and contexts)");
472
+ pack.command("create")
473
+ .description("create a new agent pack")
257
474
  .option("--input <input>", "JSON object, @file, or - for stdin")
258
475
  .addOption(new Option("--type <type>", "workflow | context").choices(PACK_TYPES))
259
- .option("--id <id>", "existing pack id")
260
476
  .option("--title <title>", "title")
261
477
  .option("--content <content>", "markdown content")
262
478
  .option("--category <category>", "pack category")
@@ -264,11 +480,9 @@ Example:
264
480
  .option("--projects <projects>", "JSON array or comma-separated project ids")
265
481
  .option("--steps <steps>", "workflow steps as JSON array (use --input @file.json for complex payloads)")
266
482
  .option("--blocks <blocks>", "context blocks as JSON array (for type=context; use --input @file.json for complex payloads)")
267
- .option(WORKSPACE_ID_OPT, WORKSPACE_ID_DESC)
268
483
  .action(async (options) => {
269
484
  const payload = await resolveInput(options, {
270
485
  type: options.type,
271
- id: options.id,
272
486
  title: options.title,
273
487
  content: options.content,
274
488
  category: options.category,
@@ -277,17 +491,54 @@ Example:
277
491
  steps: parseJsonArrayOption(options.steps, "--steps"),
278
492
  blocks: parseJsonArrayOption(options.blocks, "--blocks")
279
493
  });
280
- const context = await resolveContext({ workspaceId: options.workspaceId });
281
- printJson(await upsertPack(context, payload));
494
+ const context = await resolveContext();
495
+ printJson(await createPack(context, payload));
282
496
  });
283
497
  pack.commands.at(-1)?.addHelpText("after", `
284
498
  Examples:
285
- epismo pack upsert --type workflow --title "My workflow" --input @workflow.json
286
- epismo pack upsert --type workflow --title "My workflow" --steps '[{"title":"Step 1","content":"..."}]'
287
- epismo pack upsert --type context --title "My context" --input @context.json
288
- epismo pack upsert --type context --title "My context" --blocks '[{"title":"Block 1","content":"..."}]'`);
289
- pack
290
- .command("get")
499
+ epismo pack create --type workflow --title "My workflow" --input @workflow.json
500
+ epismo pack create --type workflow --title "My workflow" --steps '[{"title":"Step 1","content":"..."}]'
501
+ epismo pack create --type context --title "My context" --input @context.json
502
+ epismo pack create --type context --title "My context" --blocks '[{"title":"Block 1","content":"..."}]'`);
503
+ pack.command("update")
504
+ .description("update an existing agent pack (PATCH semantics — omitted fields are unchanged)")
505
+ .requiredOption("--id <id>", "pack id to update")
506
+ .option("--input <input>", "JSON object, @file, or - for stdin")
507
+ .option("--title <title>", "updated title")
508
+ .option("--content <content>", "updated markdown content")
509
+ .option("--category <category>", "updated pack category")
510
+ .addOption(new Option("--visibility <visibility>", "public | private").choices(PACK_VISIBILITIES))
511
+ .option("--projects <projects>", "JSON array or comma-separated project ids")
512
+ .option("--steps <steps>", 'step operations as JSON array with op field: [{"op":"add","title":"..."},{"op":"update","id":"s001","content":"..."},{"op":"remove","id":"s002"}]')
513
+ .option("--blocks <blocks>", 'block operations as JSON array with op field: [{"op":"add","title":"..."},{"op":"update","id":"b001","content":"..."},{"op":"remove","id":"b002"}]')
514
+ .action(async (options) => {
515
+ const payload = await resolveInput(options, {
516
+ title: options.title,
517
+ content: options.content,
518
+ category: options.category,
519
+ visibility: options.visibility,
520
+ projects: parseStringArrayInput(options.projects, "--projects"),
521
+ steps: parseJsonArrayOption(options.steps, "--steps"),
522
+ blocks: parseJsonArrayOption(options.blocks, "--blocks")
523
+ });
524
+ const context = await resolveContext();
525
+ printJson(await updatePack(context, options.id, payload));
526
+ });
527
+ pack.commands.at(-1)?.addHelpText("after", `
528
+ Notes:
529
+ Omitted fields keep their existing value.
530
+ steps/blocks use operation objects with an "op" field (add | update | remove).
531
+ Omitting steps/blocks keeps them unchanged. Passing an empty array [] is a no-op.
532
+ To remove all items, send a remove op for each existing item ID.
533
+
534
+ Examples:
535
+ epismo pack update --id <id> --category ""
536
+ epismo pack update --id <id> --visibility public --category productivity
537
+ epismo pack update --id <id> --blocks '[{"op":"add","title":"New Block","content":"..."}]'
538
+ epismo pack update --id <id> --blocks '[{"op":"update","id":"b001","content":"updated..."}]'
539
+ epismo pack update --id <id> --blocks '[{"op":"remove","id":"b002"}]'
540
+ epismo pack update --id <id> --input @changes.json`);
541
+ pack.command("get")
291
542
  .description("fetch one agent pack")
292
543
  .option("--input <input>", "JSON object, @file, or - for stdin")
293
544
  .option("--id <id>", "pack id")
@@ -295,14 +546,13 @@ Examples:
295
546
  .option("--full", "include nested item content")
296
547
  .option("--block-id <blockId>", "context block id to extract (type=context only)")
297
548
  .option("--step-id <stepId>", "workflow step id to extract (type=workflow only)")
298
- .option(WORKSPACE_ID_OPT, WORKSPACE_ID_DESC)
299
549
  .action(async (options) => {
300
550
  const payload = await resolveInput(options, {
301
551
  id: options.id,
302
552
  alias: options.alias,
303
553
  full: options.full ? true : undefined
304
554
  });
305
- const context = await resolveContext({ workspaceId: options.workspaceId });
555
+ const context = await resolveContext();
306
556
  printJson(await getPack(context, payload, {
307
557
  blockId: options.blockId,
308
558
  stepId: options.stepId
@@ -316,8 +566,7 @@ Examples:
316
566
  epismo pack get --id <id> --full
317
567
  epismo pack get --id <id> --block-id b001
318
568
  epismo pack get --id <id> --step-id s001`);
319
- pack
320
- .command("search")
569
+ pack.command("search")
321
570
  .description("search agent packs")
322
571
  .option("--input <input>", "JSON object, @file, or - for stdin")
323
572
  .addOption(new Option("--type <type>", "workflow | context").choices(PACK_TYPES))
@@ -325,7 +574,6 @@ Examples:
325
574
  .option("--page <page>", "1-based page number")
326
575
  .option("--projects <projects>", "JSON array or comma-separated project ids")
327
576
  .option("--filter <filter>", 'filter as JSON object; each value is an array of accepted values e.g. \'{"visibility":["public"]}\' (use --input @file.json for complex filters)')
328
- .option(WORKSPACE_ID_OPT, WORKSPACE_ID_DESC)
329
577
  .action(async (options, command) => {
330
578
  const payload = await resolveInput(options, {
331
579
  type: getExplicitOptionOverride(command, "type", options.type),
@@ -334,7 +582,7 @@ Examples:
334
582
  projects: getExplicitOptionOverride(command, "projects", parseStringArrayInput(options.projects, "--projects")),
335
583
  filter: getExplicitOptionOverride(command, "filter", parseJsonObjectOption(options.filter, "--filter"))
336
584
  });
337
- const context = await resolveContext({ workspaceId: options.workspaceId });
585
+ const context = await resolveContext();
338
586
  printJson(await searchPacks(context, payload));
339
587
  });
340
588
  pack.commands.at(-1)?.addHelpText("after", `
@@ -342,14 +590,12 @@ Examples:
342
590
  epismo pack search --type workflow --query "onboarding"
343
591
  epismo pack search --type workflow --filter '{"visibility":["public"]}'
344
592
  epismo pack search --type context --query "meeting notes"`);
345
- pack
346
- .command("like")
593
+ pack.command("like")
347
594
  .description("like or unlike an agent pack")
348
595
  .option("--input <input>", "JSON object, @file, or - for stdin")
349
596
  .option("--id <id>", "pack id")
350
597
  .option("--liked", "mark as liked")
351
598
  .option("--no-liked", "remove like")
352
- .option(WORKSPACE_ID_OPT, WORKSPACE_ID_DESC)
353
599
  .action(async (options) => {
354
600
  const payload = await resolveInput(options, {
355
601
  id: options.id,
@@ -362,53 +608,91 @@ Examples:
362
608
  hint: "Use --liked to add a like, or --no-liked to remove one."
363
609
  });
364
610
  }
365
- const context = await resolveContext({ workspaceId: options.workspaceId });
611
+ const context = await resolveContext();
366
612
  printJson(await likePack(context, payload));
367
613
  });
368
614
  pack.commands.at(-1)?.addHelpText("after", `
369
615
  Examples:
370
616
  epismo pack like --id <id> --liked
371
617
  epismo pack like --id <id> --no-liked`);
372
- pack
373
- .command("delete")
618
+ pack.command("delete")
374
619
  .description("delete one agent pack and any of your aliases that point to it")
375
620
  .option("--input <input>", "JSON object, @file, or - for stdin")
376
621
  .option("--id <id>", "pack id")
377
- .option(WORKSPACE_ID_OPT, WORKSPACE_ID_DESC)
378
622
  .action(async (options) => {
379
623
  const payload = await resolveInput(options, {
380
624
  id: options.id
381
625
  });
382
- const context = await resolveContext({ workspaceId: options.workspaceId });
626
+ const context = await resolveContext();
383
627
  printJson(await deletePack(context, payload));
384
628
  });
385
629
  pack.commands.at(-1)?.addHelpText("after", `
386
630
  Examples:
387
631
  epismo pack delete --id <id> # also removes your aliases for that pack`);
632
+ const agent = program.command("agent").description("manage agent availability");
633
+ agent
634
+ .command("list")
635
+ .description("list AI teammates and whether they appear in the assignee roster")
636
+ .action(async () => {
637
+ const context = await resolveContext();
638
+ printJson(await listAgents(context));
639
+ });
640
+ agent.commands.at(-1)?.addHelpText("after", `
641
+ Examples:
642
+ epismo agent list`);
643
+ agent
644
+ .command("add")
645
+ .description("add AI teammates to the assignee roster")
646
+ .option("--input <input>", "JSON object, @file, or - for stdin")
647
+ .option("--agent-ids <agentIds>", "JSON array or comma-separated agent ids")
648
+ .action(async (options) => {
649
+ const payload = await resolveInput(options, {
650
+ agentIds: parseStringArrayInput(options.agentIds, "--agent-ids")
651
+ });
652
+ const context = await resolveContext();
653
+ printJson(await addAgents(context, payload));
654
+ });
655
+ agent.commands.at(-1)?.addHelpText("after", `
656
+ Examples:
657
+ epismo agent add --agent-ids agent_a
658
+ epismo agent add --agent-ids '["agent_a","agent_b"]'`);
659
+ agent
660
+ .command("remove")
661
+ .description("remove AI teammates from the assignee roster")
662
+ .option("--input <input>", "JSON object, @file, or - for stdin")
663
+ .option("--agent-ids <agentIds>", "JSON array or comma-separated agent ids")
664
+ .action(async (options) => {
665
+ const payload = await resolveInput(options, {
666
+ agentIds: parseStringArrayInput(options.agentIds, "--agent-ids")
667
+ });
668
+ const context = await resolveContext();
669
+ printJson(await removeAgents(context, payload));
670
+ });
671
+ agent.commands.at(-1)?.addHelpText("after", `
672
+ Examples:
673
+ epismo agent remove --agent-ids agent_a
674
+ epismo agent remove --agent-ids '["agent_a","agent_b"]'`);
388
675
  const credit = program.command("credit").description("view balance and purchase credits");
389
676
  credit
390
677
  .command("balance")
391
678
  .description("show current credit balance")
392
- .option(WORKSPACE_ID_OPT, WORKSPACE_ID_DESC)
393
- .action(async (options) => {
394
- const context = await resolveContext({ workspaceId: options.workspaceId });
679
+ .action(async () => {
680
+ const context = await resolveContext();
395
681
  printJson(await creditBalance(context));
396
682
  });
397
683
  credit.commands.at(-1)?.addHelpText("after", `
398
684
  Examples:
399
- epismo credit balance
400
- epismo credit balance --workspace-id <workspace-id>`);
685
+ epismo credit balance`);
401
686
  credit
402
687
  .command("checkout")
403
688
  .description("start a credit purchase and get a checkout URL")
404
689
  .option("--input <input>", "JSON object, @file, or - for stdin")
405
690
  .option("--allocations <allocations>", 'JSON array of { userId, quantity } e.g. \'[{"userId":"u_1","quantity":10}]\'')
406
- .option(WORKSPACE_ID_OPT, WORKSPACE_ID_DESC)
407
691
  .action(async (options) => {
408
692
  const payload = await resolveInput(options, {
409
693
  allocations: parseJsonArrayOption(options.allocations, "--allocations")
410
694
  });
411
- const context = await resolveContext({ workspaceId: options.workspaceId });
695
+ const context = await resolveContext();
412
696
  printJson(await creditCheckout(context, payload));
413
697
  });
414
698
  credit.commands.at(-1)?.addHelpText("after", `
@@ -422,14 +706,13 @@ Example:
422
706
  .addOption(new Option("--type <type>", "workflow | context").choices(PACK_TYPES))
423
707
  .option("--id <id>", "target pack id")
424
708
  .option("--alias <alias>", "alias name")
425
- .option(WORKSPACE_ID_OPT, WORKSPACE_ID_DESC)
426
709
  .action(async (options) => {
427
710
  const payload = await resolveInput(options, {
428
711
  type: options.type,
429
712
  id: options.id,
430
713
  alias: options.alias
431
714
  });
432
- const context = await resolveContext({ workspaceId: options.workspaceId });
715
+ const context = await resolveContext();
433
716
  printJson(await upsertAlias(context, payload));
434
717
  });
435
718
  alias.commands.at(-1)?.addHelpText("after", `
@@ -441,12 +724,11 @@ Examples:
441
724
  .description("resolve one alias")
442
725
  .option("--input <input>", "JSON object, @file, or - for stdin")
443
726
  .option("--alias <alias>", "alias reference (`alias` or `@handle/alias`)")
444
- .option(WORKSPACE_ID_OPT, WORKSPACE_ID_DESC)
445
727
  .action(async (options) => {
446
728
  const payload = await resolveInput(options, {
447
729
  alias: options.alias
448
730
  });
449
- const context = await resolveContext({ workspaceId: options.workspaceId });
731
+ const context = await resolveContext();
450
732
  printJson(await getAlias(context, payload));
451
733
  });
452
734
  alias.commands.at(-1)?.addHelpText("after", `
@@ -457,9 +739,8 @@ Examples:
457
739
  .command("list")
458
740
  .description("list your aliases")
459
741
  .addOption(new Option("--type <type>", "workflow | context").choices(PACK_TYPES))
460
- .option(WORKSPACE_ID_OPT, WORKSPACE_ID_DESC)
461
742
  .action(async (options) => {
462
- const context = await resolveContext({ workspaceId: options.workspaceId });
743
+ const context = await resolveContext();
463
744
  printJson(await listAliases(context, { type: options.type }));
464
745
  });
465
746
  alias.commands.at(-1)?.addHelpText("after", `
@@ -471,17 +752,36 @@ Example:
471
752
  .description("delete one alias")
472
753
  .option("--input <input>", "JSON object, @file, or - for stdin")
473
754
  .option("--alias <alias>", "alias name")
474
- .option(WORKSPACE_ID_OPT, WORKSPACE_ID_DESC)
475
755
  .action(async (options) => {
476
756
  const payload = await resolveInput(options, {
477
757
  alias: options.alias
478
758
  });
479
- const context = await resolveContext({ workspaceId: options.workspaceId });
759
+ const context = await resolveContext();
480
760
  printJson(await deleteAlias(context, payload));
481
761
  });
482
762
  alias.commands.at(-1)?.addHelpText("after", `
483
763
  Example:
484
764
  epismo alias delete --alias myproject`);
765
+ const token = program.command("token").description("manage CLI tokens for CI/CD");
766
+ token
767
+ .command("create")
768
+ .description("issue a workspace-scoped CLI token for use in CI/CD pipelines")
769
+ .option("--workspace-id <workspaceId>", "workspace to scope the token to (omit for personal space)")
770
+ .action(async (options) => {
771
+ const context = await resolveContext();
772
+ printJson(await createToken(context, options.workspaceId ?? context.workspaceId));
773
+ });
774
+ token.commands.at(-1)?.addHelpText("after", `
775
+ Notes:
776
+ The issued token has the same scopes as a normal CLI token (read, write, offline_access)
777
+ but carries the workspace ID embedded. Use it via the EPISMO_TOKEN environment variable
778
+ in CI/CD so no --workspace-id flag is needed at runtime.
779
+ If --workspace-id is omitted, the saved default workspace is used (epismo workspace use),
780
+ falling back to personal space when no default is set.
781
+
782
+ Examples:
783
+ epismo token create --workspace-id <workspace-id>
784
+ epismo token create # uses saved default workspace, or personal space if none`);
485
785
  return program;
486
786
  }
487
787
  //# sourceMappingURL=program.js.map