epismo 0.5.0 → 0.7.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
@@ -9,8 +9,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
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";
12
+ import { createWorkspace, deleteWorkspaceMembers, getWorkspaceCheckout, listWorkspaceMembers, updateWorkspace, upsertWorkspaceMembers } from "./workspaces.js";
13
+ import { addProjectMembers, createProject, deleteProject, deleteProjectMembers, listProjectMembers, listProjects, updateProject } from "./projects.js";
14
14
  import { createToken } from "./token.js";
15
15
  const TRACK_TYPES = ["task", "goal"];
16
16
  const PACK_TYPES = ["workflow", "context"];
@@ -31,15 +31,53 @@ function getExplicitOptionOverride(command, optionName, value) {
31
31
  }
32
32
  return value;
33
33
  }
34
- function buildSharingTargets(command, options) {
35
- const targets = mergeDefined({}, {
36
- projectIds: getExplicitOptionOverride(command, "projectIds", parseStringArrayInput(options.projectIds, "--project-ids")),
37
- userIds: getExplicitOptionOverride(command, "userIds", parseStringArrayInput(options.userIds, "--user-ids")),
38
- emails: getExplicitOptionOverride(command, "emails", parseStringArrayInput(options.emails, "--emails"))
39
- });
40
- return Object.keys(targets).length > 0
41
- ? targets
42
- : undefined;
34
+ function buildScopeOption(command, options) {
35
+ const personalExplicit = getExplicitOptionOverride(command, "personal", options.personal);
36
+ const projectsExplicit = getExplicitOptionOverride(command, "projects", parseStringArrayInput(options.projects, "--projects"));
37
+ if (personalExplicit !== undefined && projectsExplicit !== undefined) {
38
+ throw new CliError({
39
+ code: "INVALID_INPUT",
40
+ message: "--personal and --projects cannot be combined."
41
+ });
42
+ }
43
+ if (personalExplicit === true) {
44
+ return { type: "personal" };
45
+ }
46
+ if (projectsExplicit !== undefined) {
47
+ const ids = projectsExplicit;
48
+ if (ids.length === 0) {
49
+ throw new CliError({
50
+ code: "INVALID_INPUT",
51
+ message: "--projects requires at least one project id."
52
+ });
53
+ }
54
+ return { type: "projects", ids };
55
+ }
56
+ return undefined;
57
+ }
58
+ function buildSharedWithOption(command, options) {
59
+ const values = getExplicitOptionOverride(command, "shareWith", parseStringArrayInput(options.shareWith, "--share-with"));
60
+ if (values === undefined) {
61
+ return undefined;
62
+ }
63
+ const emails = [];
64
+ const userIds = [];
65
+ for (const value of values) {
66
+ if (value.includes("@")) {
67
+ emails.push(value);
68
+ }
69
+ else {
70
+ userIds.push(value);
71
+ }
72
+ }
73
+ const result = {};
74
+ if (userIds.length > 0) {
75
+ result.userIds = userIds;
76
+ }
77
+ if (emails.length > 0) {
78
+ result.emails = emails;
79
+ }
80
+ return result;
43
81
  }
44
82
  function buildSearchTargets(command, options) {
45
83
  const targets = mergeDefined({}, {
@@ -104,9 +142,9 @@ Examples:
104
142
  epismo alias list
105
143
  epismo track search --query "bug"
106
144
  epismo pack search --query "meeting notes"
107
- epismo pack get --id <id> --block-id b001
108
- epismo pack get --alias @myproject
109
- epismo pack get --alias @handle/myproject`);
145
+ epismo pack get <id> --block-id b001
146
+ epismo pack get @myproject
147
+ epismo pack get @handle/myproject`);
110
148
  program
111
149
  .command("login")
112
150
  .description("log in to Epismo; default flow uses email + OTP, or open a browser to sign in")
@@ -181,13 +219,13 @@ Example:
181
219
  workspace
182
220
  .command("use")
183
221
  .description("set the default workspace")
184
- .requiredOption("--workspace-id <workspaceId>", "workspace id to set as default")
185
- .action(async (options) => {
186
- printJson(await useWorkspace({ workspaceId: options.workspaceId }));
222
+ .argument("<workspace-id>", "workspace id to set as default")
223
+ .action(async (workspaceId) => {
224
+ printJson(await useWorkspace({ workspaceId }));
187
225
  });
188
226
  workspace.commands.at(-1)?.addHelpText("after", `
189
227
  Example:
190
- epismo workspace use --workspace-id <workspace-id>`);
228
+ epismo workspace use <workspace-id>`);
191
229
  workspace
192
230
  .command("clear")
193
231
  .description("clear the saved workspace (reverts to personal space)")
@@ -223,25 +261,25 @@ Examples:
223
261
  workspace
224
262
  .command("checkout")
225
263
  .description("get the billing URL for a workspace (to complete or retry payment)")
226
- .requiredOption("--id <id>", "workspace id")
227
- .action(async (options) => {
264
+ .argument("<workspace-id>", "workspace id")
265
+ .action(async (workspaceId) => {
228
266
  const context = await resolveContext();
229
- printJson(await getWorkspaceCheckout(context, options.id));
267
+ printJson(await getWorkspaceCheckout(context, workspaceId));
230
268
  });
231
269
  workspace.commands.at(-1)?.addHelpText("after", `
232
270
  Examples:
233
- epismo workspace checkout --id <workspace-id>`);
271
+ epismo workspace checkout <workspace-id>`);
234
272
  workspace
235
273
  .command("update")
236
274
  .description("update a workspace you own (PATCH — omitted fields are unchanged)")
275
+ .argument("<workspace-id>", "workspace id to update")
237
276
  .option("--input <input>", "JSON object, @file, or - for stdin")
238
- .requiredOption("--id <id>", "workspace id to update")
239
277
  .option("--name <name>", "updated workspace name")
240
278
  .addOption(new Option("--plan <plan>", "basic | pro").choices(["basic", "pro"]))
241
- .action(async (options) => {
279
+ .action(async (workspaceId, options) => {
242
280
  const payload = await resolveInput({ input: options.input }, { name: options.name, plan: options.plan });
243
281
  const context = await resolveContext();
244
- printJson(await updateWorkspace(context, options.id, payload));
282
+ printJson(await updateWorkspace(context, workspaceId, payload));
245
283
  });
246
284
  const workspaceMember = workspace.command("member").description("manage workspace members");
247
285
  workspaceMember
@@ -254,34 +292,35 @@ Examples:
254
292
  workspaceMember
255
293
  .command("upsert")
256
294
  .description("add a workspace member or update the member role")
257
- .option("--input <input>", "JSON object, @file, or - for stdin")
258
- .option("--user-id <userId>", "user id")
259
- .addOption(new Option("--role <role>", "owner | admin | member").choices([
260
- "owner",
261
- "admin",
262
- "member"
263
- ]))
264
- .action(async (options) => {
295
+ .argument("<user-ids>", "user id(s); comma-separated for multiple")
296
+ .addOption(new Option("--role <role>", "owner | admin | member")
297
+ .choices(["owner", "admin", "member"])
298
+ .makeOptionMandatory())
299
+ .action(async (userIdsArg, options) => {
300
+ const userIds = parseStringArrayInput(userIdsArg, "<user-ids>") ?? [];
301
+ if (userIds.length === 0) {
302
+ throw new CliError({
303
+ code: "INVALID_INPUT",
304
+ message: "At least one user id is required."
305
+ });
306
+ }
265
307
  const context = await resolveContext();
266
- const payload = await resolveInput({ input: options.input }, {
267
- workspaceId: context.workspaceId,
268
- userId: options.userId,
269
- role: options.role
270
- });
271
- printJson(await upsertWorkspaceMember(context, payload));
308
+ printJson(await upsertWorkspaceMembers(context, userIds, options.role));
272
309
  });
273
310
  workspaceMember
274
311
  .command("delete")
275
312
  .description("remove a workspace member")
276
- .option("--input <input>", "JSON object, @file, or - for stdin")
277
- .option("--user-id <userId>", "user id")
278
- .action(async (options) => {
313
+ .argument("<user-ids>", "user id(s); comma-separated for multiple")
314
+ .action(async (userIdsArg) => {
315
+ const userIds = parseStringArrayInput(userIdsArg, "<user-ids>") ?? [];
316
+ if (userIds.length === 0) {
317
+ throw new CliError({
318
+ code: "INVALID_INPUT",
319
+ message: "At least one user id is required."
320
+ });
321
+ }
279
322
  const context = await resolveContext();
280
- const payload = await resolveInput({ input: options.input }, {
281
- workspaceId: context.workspaceId,
282
- userId: options.userId
283
- });
284
- printJson(await deleteWorkspaceMember(context, payload));
323
+ printJson(await deleteWorkspaceMembers(context, userIds));
285
324
  });
286
325
  const project = program.command("project").description("manage workspace projects");
287
326
  project
@@ -308,29 +347,25 @@ Examples:
308
347
  project
309
348
  .command("update")
310
349
  .description("update a project you can access (PATCH — omitted fields are unchanged)")
350
+ .argument("<project-id>", "project id to update")
311
351
  .option("--input <input>", "JSON object, @file, or - for stdin")
312
- .requiredOption("--id <id>", "project id to update")
313
352
  .option("--name <name>", "updated project name")
314
353
  .option("--description <description>", "updated project description")
315
- .action(async (options) => {
354
+ .action(async (projectId, options) => {
316
355
  const payload = await resolveInput(options, {
317
356
  name: options.name,
318
357
  description: options.description
319
358
  });
320
359
  const context = await resolveContext();
321
- printJson(await updateProject(context, options.id, payload));
360
+ printJson(await updateProject(context, projectId, payload));
322
361
  });
323
362
  project
324
363
  .command("delete")
325
364
  .description("delete a project you can access in the selected workspace")
326
- .option("--input <input>", "JSON object, @file, or - for stdin")
327
- .option("--id <id>", "project id")
328
- .action(async (options) => {
329
- const payload = await resolveInput(options, {
330
- id: options.id
331
- });
365
+ .argument("<project-id>", "project id")
366
+ .action(async (projectId) => {
332
367
  const context = await resolveContext();
333
- printJson(await deleteProject(context, payload));
368
+ printJson(await deleteProject(context, projectId));
334
369
  });
335
370
  const projectMember = project.command("member").description("manage project members");
336
371
  projectMember
@@ -347,31 +382,39 @@ Examples:
347
382
  });
348
383
  projectMember
349
384
  .command("add")
350
- .description("add a member to a project")
351
- .option("--input <input>", "JSON object, @file, or - for stdin")
352
- .option("--project-id <projectId>", "project id")
353
- .option("--user-id <userId>", "user id")
354
- .action(async (options) => {
355
- const payload = await resolveInput(options, {
356
- projectId: options.projectId,
357
- userId: options.userId
358
- });
385
+ .description("add member(s) to a project")
386
+ .argument("<user-ids>", "user id(s); comma-separated for multiple")
387
+ .requiredOption("--project-id <projectId>", "project id")
388
+ .action(async (userIdsArg, options) => {
389
+ const userIds = parseStringArrayInput(userIdsArg, "<user-ids>") ?? [];
390
+ if (userIds.length === 0) {
391
+ throw new CliError({
392
+ code: "INVALID_INPUT",
393
+ message: "At least one user id is required."
394
+ });
395
+ }
359
396
  const context = await resolveContext();
360
- printJson(await addProjectMember(context, payload));
397
+ printJson({
398
+ results: await addProjectMembers(context, options.projectId, userIds)
399
+ });
361
400
  });
362
401
  projectMember
363
402
  .command("delete")
364
- .description("remove a user from a project")
365
- .option("--input <input>", "JSON object, @file, or - for stdin")
366
- .option("--project-id <projectId>", "project id")
367
- .option("--user-id <userId>", "user id")
368
- .action(async (options) => {
369
- const payload = await resolveInput(options, {
370
- projectId: options.projectId,
371
- userId: options.userId
372
- });
403
+ .description("remove user(s) from a project")
404
+ .argument("<user-ids>", "user id(s); comma-separated for multiple")
405
+ .requiredOption("--project-id <projectId>", "project id")
406
+ .action(async (userIdsArg, options) => {
407
+ const userIds = parseStringArrayInput(userIdsArg, "<user-ids>") ?? [];
408
+ if (userIds.length === 0) {
409
+ throw new CliError({
410
+ code: "INVALID_INPUT",
411
+ message: "At least one user id is required."
412
+ });
413
+ }
373
414
  const context = await resolveContext();
374
- printJson(await deleteProjectMember(context, payload));
415
+ printJson({
416
+ results: await deleteProjectMembers(context, options.projectId, userIds)
417
+ });
375
418
  });
376
419
  const track = program.command("track").description("manage project tracks (tasks and goals)");
377
420
  track
@@ -381,9 +424,9 @@ Examples:
381
424
  .addOption(new Option("--type <type>", "task | goal").choices(TRACK_TYPES))
382
425
  .option("--title <title>", "title")
383
426
  .option("--content <content>", "markdown content")
384
- .option("--project-ids <projectIds>", "JSON array or comma-separated project ids")
385
- .option("--user-ids <userIds>", "JSON array or comma-separated user ids")
386
- .option("--emails <emails>", "JSON array or comma-separated email addresses")
427
+ .option("--personal", "keep private to the current user (mutually exclusive with --projects)")
428
+ .option("--projects <projects>", "JSON array or comma-separated project ids (mutually exclusive with --personal)")
429
+ .option("--share-with <shareWith>", "JSON array or comma-separated list of user ids and/or emails (values containing '@' are emails)")
387
430
  .option("--task <task>", 'task-specific fields as JSON object e.g. \'{"status":"todo","dueDate":"2025-12-31"}\'')
388
431
  .option("--goal <goal>", 'goal-specific fields as JSON object e.g. \'{"status":"on_track","dueDate":"2025-12-31"}\'')
389
432
  .action(async (options, command) => {
@@ -391,7 +434,8 @@ Examples:
391
434
  type: options.type,
392
435
  title: options.title,
393
436
  content: options.content,
394
- targets: buildSharingTargets(command, options),
437
+ scope: buildScopeOption(command, options),
438
+ sharedWith: buildSharedWithOption(command, options),
395
439
  task: parseJsonObjectOption(options.task, "--task"),
396
440
  goal: parseJsonObjectOption(options.goal, "--goal")
397
441
  });
@@ -409,49 +453,47 @@ Examples:
409
453
  track
410
454
  .command("update")
411
455
  .description("update an existing project track (PATCH — omitted fields are unchanged)")
456
+ .argument("<id>", "track id to update")
412
457
  .option("--input <input>", "JSON object, @file, or - for stdin")
413
- .requiredOption("--id <id>", "track id to update")
414
458
  .option("--title <title>", "updated title")
415
459
  .option("--content <content>", "updated markdown content")
416
- .option("--project-ids <projectIds>", "JSON array or comma-separated project ids")
417
- .option("--user-ids <userIds>", "JSON array or comma-separated user ids")
418
- .option("--emails <emails>", "JSON array or comma-separated email addresses")
460
+ .option("--personal", "set scope to personal (mutually exclusive with --projects)")
461
+ .option("--projects <projects>", "JSON array or comma-separated project ids (mutually exclusive with --personal)")
462
+ .option("--share-with <shareWith>", "JSON array or comma-separated list of user ids and/or emails (values containing '@' are emails)")
419
463
  .option("--task <task>", 'task-specific fields as partial JSON object e.g. \'{"status":"done"}\'')
420
464
  .option("--goal <goal>", "goal-specific fields as partial JSON object e.g. '{\"progress\":50}'")
421
- .action(async (options, command) => {
465
+ .action(async (id, options, command) => {
422
466
  const payload = await resolveInput(options, {
423
467
  title: options.title,
424
468
  content: options.content,
425
- targets: buildSharingTargets(command, options),
469
+ scope: buildScopeOption(command, options),
470
+ sharedWith: buildSharedWithOption(command, options),
426
471
  task: parseJsonObjectOption(options.task, "--task"),
427
472
  goal: parseJsonObjectOption(options.goal, "--goal")
428
473
  });
429
474
  const context = await resolveContext();
430
- printJson(await updateTrack(context, options.id, payload));
475
+ printJson(await updateTrack(context, id, payload));
431
476
  });
432
477
  track.commands.at(-1)?.addHelpText("after", `
433
478
  Notes:
434
479
  Omitted fields keep their existing value.
435
480
 
436
481
  Examples:
437
- epismo track update --id <id> --title "Updated title"
438
- epismo track update --id <id> --task '{"status":"done"}'
439
- epismo track update --id <id> --goal '{"progress":75}'`);
482
+ epismo track update <id> --title "Updated title"
483
+ epismo track update <id> --task '{"status":"done"}'
484
+ epismo track update <id> --goal '{"progress":75}'`);
440
485
  track
441
486
  .command("get")
442
487
  .description("fetch one project track")
488
+ .argument("<id>", "track id")
443
489
  .option("--input <input>", "JSON object, @file, or - for stdin")
444
- .option("--id <id>", "item id")
445
- .action(async (options) => {
446
- const payload = await resolveInput(options, {
447
- id: options.id
448
- });
490
+ .action(async (id) => {
449
491
  const context = await resolveContext();
450
- printJson(await getTrack(context, payload));
492
+ printJson(await getTrack(context, id));
451
493
  });
452
494
  track.commands.at(-1)?.addHelpText("after", `
453
495
  Example:
454
- epismo track get --id <id>`);
496
+ epismo track get <id>`);
455
497
  track
456
498
  .command("search")
457
499
  .description("search project tracks")
@@ -482,28 +524,25 @@ Examples:
482
524
  track
483
525
  .command("delete")
484
526
  .description("delete one project track")
485
- .option("--input <input>", "JSON object, @file, or - for stdin")
486
- .option("--id <id>", "item id")
487
- .action(async (options) => {
488
- const payload = await resolveInput(options, {
489
- id: options.id
490
- });
527
+ .argument("<id>", "track id")
528
+ .action(async (id) => {
491
529
  const context = await resolveContext();
492
- printJson(await deleteTrack(context, payload));
530
+ printJson(await deleteTrack(context, id));
493
531
  });
494
532
  track.commands.at(-1)?.addHelpText("after", `
495
533
  Example:
496
- epismo track delete --id <id>`);
534
+ epismo track delete <id>`);
497
535
  track
498
536
  .command("apply")
499
537
  .description("create, update, and delete multiple tracks in a single request")
500
538
  .option("--input <input>", "JSON object, @file, or - for stdin")
501
- .option("--project-ids <projectIds>", "JSON array or comma-separated project ids")
502
- .option("--user-ids <userIds>", "JSON array or comma-separated user ids")
503
- .option("--emails <emails>", "JSON array or comma-separated email addresses")
539
+ .option("--personal", "set scope to personal (mutually exclusive with --projects)")
540
+ .option("--projects <projects>", "JSON array or comma-separated project ids (mutually exclusive with --personal)")
541
+ .option("--share-with <shareWith>", "JSON array or comma-separated list of user ids and/or emails (values containing '@' are emails)")
504
542
  .action(async (options, command) => {
505
543
  const payload = await resolveInput(options, {
506
- targets: buildSharingTargets(command, options)
544
+ scope: buildScopeOption(command, options),
545
+ sharedWith: buildSharedWithOption(command, options)
507
546
  });
508
547
  const context = await resolveContext();
509
548
  printJson(await applyTracks(context, payload));
@@ -527,9 +566,9 @@ Examples:
527
566
  .option("--content <content>", "markdown content")
528
567
  .option("--category <category>", "pack category")
529
568
  .addOption(new Option("--visibility <visibility>", "public | private").choices(PACK_VISIBILITIES))
530
- .option("--project-ids <projectIds>", "JSON array or comma-separated project ids")
531
- .option("--user-ids <userIds>", "JSON array or comma-separated user ids")
532
- .option("--emails <emails>", "JSON array or comma-separated email addresses")
569
+ .option("--personal", "keep private to the current user (mutually exclusive with --projects)")
570
+ .option("--projects <projects>", "JSON array or comma-separated project ids (mutually exclusive with --personal)")
571
+ .option("--share-with <shareWith>", "JSON array or comma-separated list of user ids and/or emails (values containing '@' are emails)")
533
572
  .option("--steps <steps>", "workflow steps as JSON array (use --input @file.json for complex payloads)")
534
573
  .option("--blocks <blocks>", "context blocks as JSON array (for type=context; use --input @file.json for complex payloads)")
535
574
  .action(async (options, command) => {
@@ -539,7 +578,8 @@ Examples:
539
578
  content: options.content,
540
579
  category: options.category,
541
580
  visibility: options.visibility,
542
- targets: buildSharingTargets(command, options),
581
+ scope: buildScopeOption(command, options),
582
+ sharedWith: buildSharedWithOption(command, options),
543
583
  steps: parseJsonArrayOption(options.steps, "--steps"),
544
584
  blocks: parseJsonArrayOption(options.blocks, "--blocks")
545
585
  });
@@ -564,29 +604,30 @@ Examples:
564
604
  epismo pack create --type context --title "My context" --blocks '[{"title":"Block 1","content":"..."}]'`);
565
605
  pack.command("update")
566
606
  .description("update an existing agent pack (PATCH semantics — omitted fields are unchanged)")
567
- .requiredOption("--id <id>", "pack id to update")
607
+ .argument("<reference>", "pack reference: id, alias, share URL, or hub URL")
568
608
  .option("--input <input>", "JSON object, @file, or - for stdin")
569
609
  .option("--title <title>", "updated title")
570
610
  .option("--content <content>", "updated markdown content")
571
611
  .option("--category <category>", "updated pack category")
572
612
  .addOption(new Option("--visibility <visibility>", "public | private").choices(PACK_VISIBILITIES))
573
- .option("--project-ids <projectIds>", "JSON array or comma-separated project ids")
574
- .option("--user-ids <userIds>", "JSON array or comma-separated user ids")
575
- .option("--emails <emails>", "JSON array or comma-separated email addresses")
613
+ .option("--personal", "set scope to personal (mutually exclusive with --projects)")
614
+ .option("--projects <projects>", "JSON array or comma-separated project ids (mutually exclusive with --personal)")
615
+ .option("--share-with <shareWith>", "JSON array or comma-separated list of user ids and/or emails (values containing '@' are emails)")
576
616
  .option("--steps <steps>", 'step operations as JSON array with op field: [{"op":"add","title":"..."},{"op":"update","id":"s001","content":"..."},{"op":"remove","id":"s002"}]')
577
617
  .option("--blocks <blocks>", 'block operations as JSON array with op field: [{"op":"add","title":"..."},{"op":"update","id":"b001","content":"..."},{"op":"remove","id":"b002"}]')
578
- .action(async (options, command) => {
618
+ .action(async (reference, options, command) => {
579
619
  const payload = await resolveInput(options, {
580
620
  title: options.title,
581
621
  content: options.content,
582
622
  category: options.category,
583
623
  visibility: options.visibility,
584
- targets: buildSharingTargets(command, options),
624
+ scope: buildScopeOption(command, options),
625
+ sharedWith: buildSharedWithOption(command, options),
585
626
  steps: parseJsonArrayOption(options.steps, "--steps"),
586
627
  blocks: parseJsonArrayOption(options.blocks, "--blocks")
587
628
  });
588
629
  const context = await resolveContext();
589
- printJson(await updatePack(context, options.id, payload));
630
+ printJson(await updatePack(context, reference, payload));
590
631
  });
591
632
  pack.commands.at(-1)?.addHelpText("after", `
592
633
  Notes:
@@ -596,41 +637,41 @@ Notes:
596
637
  To remove all items, send a remove op for each existing item ID.
597
638
 
598
639
  Examples:
599
- epismo pack update --id <id> --category ""
600
- epismo pack update --id <id> --visibility public --category productivity
601
- epismo pack update --id <id> --blocks '[{"op":"add","title":"New Block","content":"..."}]'
602
- epismo pack update --id <id> --blocks '[{"op":"update","id":"b001","content":"updated..."}]'
603
- epismo pack update --id <id> --blocks '[{"op":"remove","id":"b002"}]'
604
- epismo pack update --id <id> --input @changes.json`);
640
+ epismo pack update <id> --category ""
641
+ epismo pack update @myproject --visibility public
642
+ epismo pack update <id> --visibility public --category productivity
643
+ epismo pack update <id> --blocks '[{"op":"add","title":"New Block","content":"..."}]'
644
+ epismo pack update <id> --blocks '[{"op":"update","id":"b001","content":"updated..."}]'
645
+ epismo pack update <id> --blocks '[{"op":"remove","id":"b002"}]'
646
+ epismo pack update <id> --input @changes.json`);
605
647
  pack.command("get")
606
648
  .description("fetch one agent pack")
649
+ .argument("<reference>", "pack reference: id, alias, share URL, or hub URL")
607
650
  .option("--input <input>", "JSON object, @file, or - for stdin")
608
- .option("--id <id>", "pack id")
609
- .addOption(new Option("--alias <alias>", "pack alias (`@alias` or `@handle/alias`)").conflicts("id"))
610
651
  .option("--full", "include nested item content")
611
652
  .option("--block-id <blockId>", "context block id to extract; comma-separated or repeatable (type=context only)", collectOption)
612
653
  .option("--step-id <stepId>", "workflow step id to extract; comma-separated or repeatable (type=workflow only)", collectOption)
613
- .action(async (options) => {
654
+ .action(async (reference, options) => {
614
655
  const payload = await resolveInput(options, {
615
- id: options.id,
616
- alias: options.alias,
617
656
  full: options.full ? true : undefined
618
657
  });
619
658
  const context = await resolveContext();
620
- printJson(await getPack(context, payload, {
659
+ printJson(await getPack(context, reference, payload, {
621
660
  blockIds: options.blockId,
622
661
  stepIds: options.stepId
623
662
  }));
624
663
  });
625
664
  pack.commands.at(-1)?.addHelpText("after", `
626
665
  Examples:
627
- epismo pack get --id <id>
628
- epismo pack get --alias @myproject
629
- epismo pack get --alias @handle/myproject
630
- epismo pack get --id <id> --full
631
- epismo pack get --id <id> --block-id b001
632
- epismo pack get --id <id> --block-id b001 --block-id b002
633
- epismo pack get --id <id> --step-id s001,s002`);
666
+ epismo pack get <id>
667
+ epismo pack get @myproject
668
+ epismo pack get @handle/myproject
669
+ epismo pack get https://epismo.ai/share/<token>
670
+ epismo pack get https://epismo.ai/hub/workflows/<id>
671
+ epismo pack get <id> --full
672
+ epismo pack get <id> --block-id b001
673
+ epismo pack get <id> --block-id b001 --block-id b002
674
+ epismo pack get <id> --step-id s001,s002`);
634
675
  pack.command("search")
635
676
  .description("search agent packs")
636
677
  .option("--input <input>", "JSON object, @file, or - for stdin")
@@ -659,13 +700,12 @@ Examples:
659
700
  epismo pack search --type context --query "meeting notes"`);
660
701
  pack.command("like")
661
702
  .description("like or unlike an agent pack")
703
+ .argument("<reference>", "pack reference: id, alias, share URL, or hub URL")
662
704
  .option("--input <input>", "JSON object, @file, or - for stdin")
663
- .option("--id <id>", "pack id")
664
705
  .option("--liked", "mark as liked")
665
706
  .option("--no-liked", "remove like")
666
- .action(async (options) => {
707
+ .action(async (reference, options) => {
667
708
  const payload = await resolveInput(options, {
668
- id: options.id,
669
709
  liked: options.liked
670
710
  });
671
711
  if (typeof payload.liked !== "boolean") {
@@ -676,26 +716,25 @@ Examples:
676
716
  });
677
717
  }
678
718
  const context = await resolveContext();
679
- printJson(await likePack(context, payload));
719
+ printJson(await likePack(context, reference, payload));
680
720
  });
681
721
  pack.commands.at(-1)?.addHelpText("after", `
682
722
  Examples:
683
- epismo pack like --id <id> --liked
684
- epismo pack like --id <id> --no-liked`);
723
+ epismo pack like <id> --liked
724
+ epismo pack like @myproject --liked
725
+ epismo pack like <id> --no-liked`);
685
726
  pack.command("delete")
686
727
  .description("delete one agent pack and any of your aliases that point to it")
687
- .option("--input <input>", "JSON object, @file, or - for stdin")
688
- .option("--id <id>", "pack id")
689
- .action(async (options) => {
690
- const payload = await resolveInput(options, {
691
- id: options.id
692
- });
728
+ .argument("<reference>", "pack reference: id, alias, share URL, or hub URL")
729
+ .action(async (reference) => {
693
730
  const context = await resolveContext();
694
- printJson(await deletePack(context, payload));
731
+ printJson(await deletePack(context, reference));
695
732
  });
696
733
  pack.commands.at(-1)?.addHelpText("after", `
697
734
  Examples:
698
- epismo pack delete --id <id> # also removes your aliases for that pack`);
735
+ epismo pack delete <id> # also removes your aliases for that pack
736
+ epismo pack delete @myproject
737
+ epismo pack delete https://epismo.ai/share/<token>`);
699
738
  const agent = program.command("agent").description("manage agent availability");
700
739
  agent
701
740
  .command("list")
@@ -769,39 +808,35 @@ Example:
769
808
  alias
770
809
  .command("upsert")
771
810
  .description("create or update one alias")
811
+ .argument("<alias>", "alias name (`alias` or `@alias`; stored without `@`)")
772
812
  .option("--input <input>", "JSON object, @file, or - for stdin")
773
813
  .addOption(new Option("--type <type>", "workflow | context").choices(PACK_TYPES))
774
814
  .option("--id <id>", "target pack id")
775
- .option("--alias <alias>", "alias name (`alias` or `@alias`; stored without `@`)")
776
- .action(async (options) => {
815
+ .action(async (aliasName, options) => {
777
816
  const payload = await resolveInput(options, {
778
817
  type: options.type,
779
818
  id: options.id,
780
- alias: options.alias
819
+ alias: aliasName
781
820
  });
782
821
  const context = await resolveContext();
783
822
  printJson(await upsertAlias(context, payload));
784
823
  });
785
824
  alias.commands.at(-1)?.addHelpText("after", `
786
825
  Examples:
787
- epismo alias upsert --type workflow --id <id> --alias @myproject
788
- epismo alias upsert --type context --id <id> --alias @mycontext`);
826
+ epismo alias upsert @myproject --type workflow --id <id>
827
+ epismo alias upsert @mycontext --type context --id <id>`);
789
828
  alias
790
829
  .command("get")
791
830
  .description("resolve one alias")
792
- .option("--input <input>", "JSON object, @file, or - for stdin")
793
- .option("--alias <alias>", "alias reference (`@alias` or `@handle/alias`)")
794
- .action(async (options) => {
795
- const payload = await resolveInput(options, {
796
- alias: options.alias
797
- });
831
+ .argument("<alias>", "alias reference (`@alias` or `@handle/alias`)")
832
+ .action(async (aliasRef) => {
798
833
  const context = await resolveContext();
799
- printJson(await getAlias(context, payload));
834
+ printJson(await getAlias(context, { alias: aliasRef }));
800
835
  });
801
836
  alias.commands.at(-1)?.addHelpText("after", `
802
837
  Examples:
803
- epismo alias get --alias @myproject
804
- epismo alias get --alias @handle/myproject`);
838
+ epismo alias get @myproject
839
+ epismo alias get @handle/myproject`);
805
840
  alias
806
841
  .command("list")
807
842
  .description("list your aliases")
@@ -817,18 +852,14 @@ Example:
817
852
  alias
818
853
  .command("delete")
819
854
  .description("delete one alias")
820
- .option("--input <input>", "JSON object, @file, or - for stdin")
821
- .option("--alias <alias>", "alias name (`alias` or `@alias`; stored without `@`)")
822
- .action(async (options) => {
823
- const payload = await resolveInput(options, {
824
- alias: options.alias
825
- });
855
+ .argument("<alias>", "alias name (`alias` or `@alias`; stored without `@`)")
856
+ .action(async (aliasName) => {
826
857
  const context = await resolveContext();
827
- printJson(await deleteAlias(context, payload));
858
+ printJson(await deleteAlias(context, { alias: aliasName }));
828
859
  });
829
860
  alias.commands.at(-1)?.addHelpText("after", `
830
861
  Example:
831
- epismo alias delete --alias @myproject`);
862
+ epismo alias delete @myproject`);
832
863
  const token = program.command("token").description("manage CLI tokens for CI/CD");
833
864
  token
834
865
  .command("create")