ente-cli 0.4.68 → 0.4.70

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.mjs CHANGED
@@ -3710,8 +3710,97 @@ function warn(opts, latest) {
3710
3710
  );
3711
3711
  }
3712
3712
 
3713
+ // core/errors.ts
3714
+ var ApiError = class extends Error {
3715
+ statusCode;
3716
+ error;
3717
+ constructor(statusCode, message, error) {
3718
+ super(`HTTP ${statusCode}: ${message}`);
3719
+ this.name = "ApiError";
3720
+ this.statusCode = statusCode;
3721
+ this.detail = message;
3722
+ this.error = error ?? {};
3723
+ }
3724
+ /** The backend's human message, without the `HTTP <code>:` prefix. */
3725
+ detail;
3726
+ };
3727
+ var ExitWithCode = class extends Error {
3728
+ code;
3729
+ constructor(code2) {
3730
+ super(`exit ${code2}`);
3731
+ this.name = "ExitWithCode";
3732
+ this.code = code2;
3733
+ }
3734
+ };
3735
+
3736
+ // core/output.ts
3737
+ function emit(stdout, data, json, rowsFn, headers) {
3738
+ if (json || !rowsFn) {
3739
+ stdout.write(`${JSON.stringify(data, null, 2)}
3740
+ `);
3741
+ return;
3742
+ }
3743
+ stdout.write(`${renderTable(rowsFn(data), headers)}
3744
+ `);
3745
+ }
3746
+ function errorMessage(err) {
3747
+ if (!(err instanceof Error)) return String(err);
3748
+ const data = err.data;
3749
+ if (typeof data === "string" && data.length > 0) return data;
3750
+ if (data && typeof data === "object") {
3751
+ const { message, code: code2 } = data;
3752
+ if (typeof message === "string" && message.length > 0) {
3753
+ return typeof code2 === "string" ? `${message} (${code2})` : message;
3754
+ }
3755
+ return `${err.message}: ${JSON.stringify(data)}`;
3756
+ }
3757
+ return err.message;
3758
+ }
3759
+ function renderTable(rows, headers) {
3760
+ if (rows.length === 0) return "(none)";
3761
+ const cols = headers ?? Object.keys(rows[0] ?? {});
3762
+ const widths = cols.map((c) => Math.max(c.length, ...rows.map((r) => (r[c] ?? "").length)));
3763
+ const line = (cells) => cells.map((cell, i) => cell.padEnd(widths[i] ?? 0)).join(" ").trimEnd();
3764
+ const out = [line(cols), line(cols.map((_, i) => "\u2500".repeat(widths[i] ?? 0)))];
3765
+ for (const r of rows) out.push(line(cols.map((c) => r[c] ?? "")));
3766
+ return out.join("\n");
3767
+ }
3768
+
3769
+ // core/errorOutput.ts
3770
+ function writeCommandError(stdout, stderr, json, error) {
3771
+ if (!json) {
3772
+ stderr.write(`Error: ${errorMessage(error)}
3773
+ `);
3774
+ return;
3775
+ }
3776
+ const data = error instanceof ApiError ? error.error : error instanceof Error && "data" in error ? error.data : void 0;
3777
+ const details = data && typeof data === "object" && !Array.isArray(data) ? data : {};
3778
+ stdout.write(`${JSON.stringify({ error: {
3779
+ ...details,
3780
+ message: errorMessage(error),
3781
+ ...error instanceof ApiError ? { statusCode: error.statusCode } : {}
3782
+ } }, null, 2)}
3783
+ `);
3784
+ }
3785
+
3786
+ // core/run.ts
3787
+ async function runCli(cli2, argv, context) {
3788
+ try {
3789
+ const command = cli2.process(argv, context);
3790
+ return await cli2.run(command, context);
3791
+ } catch (error) {
3792
+ writeCommandError(
3793
+ context.stdout,
3794
+ context.stderr,
3795
+ argv.includes("--json"),
3796
+ argv.includes("--json") ? new Error("Invalid command or options. Run 'ente <command> --help' for usage.") : error
3797
+ );
3798
+ return 1;
3799
+ }
3800
+ }
3801
+
3713
3802
  // ente/version.ts
3714
- var VERSION = "0.4.68";
3803
+ var VERSION = "0.4.70";
3715
3804
 
3716
3805
  // ente/branding.ts
3717
3806
  var NPM_REGISTRY = "https://registry.npmjs.org";
@@ -3842,62 +3931,6 @@ function deleteJwt(cacheDir, key) {
3842
3931
  return true;
3843
3932
  }
3844
3933
 
3845
- // core/errors.ts
3846
- var ApiError = class extends Error {
3847
- statusCode;
3848
- error;
3849
- constructor(statusCode, message, error) {
3850
- super(`HTTP ${statusCode}: ${message}`);
3851
- this.name = "ApiError";
3852
- this.statusCode = statusCode;
3853
- this.detail = message;
3854
- this.error = error ?? {};
3855
- }
3856
- /** The backend's human message, without the `HTTP <code>:` prefix. */
3857
- detail;
3858
- };
3859
- var ExitWithCode = class extends Error {
3860
- code;
3861
- constructor(code2) {
3862
- super(`exit ${code2}`);
3863
- this.name = "ExitWithCode";
3864
- this.code = code2;
3865
- }
3866
- };
3867
-
3868
- // core/output.ts
3869
- function emit(stdout, data, json, rowsFn, headers) {
3870
- if (json || !rowsFn) {
3871
- stdout.write(`${JSON.stringify(data, null, 2)}
3872
- `);
3873
- return;
3874
- }
3875
- stdout.write(`${renderTable(rowsFn(data), headers)}
3876
- `);
3877
- }
3878
- function errorMessage(err) {
3879
- if (!(err instanceof Error)) return String(err);
3880
- const data = err.data;
3881
- if (typeof data === "string" && data.length > 0) return data;
3882
- if (data && typeof data === "object") {
3883
- const { message, code: code2 } = data;
3884
- if (typeof message === "string" && message.length > 0) {
3885
- return typeof code2 === "string" ? `${message} (${code2})` : message;
3886
- }
3887
- return `${err.message}: ${JSON.stringify(data)}`;
3888
- }
3889
- return err.message;
3890
- }
3891
- function renderTable(rows, headers) {
3892
- if (rows.length === 0) return "(none)";
3893
- const cols = headers ?? Object.keys(rows[0] ?? {});
3894
- const widths = cols.map((c) => Math.max(c.length, ...rows.map((r) => (r[c] ?? "").length)));
3895
- const line = (cells) => cells.map((cell, i) => cell.padEnd(widths[i] ?? 0)).join(" ").trimEnd();
3896
- const out = [line(cols), line(cols.map((_, i) => "\u2500".repeat(widths[i] ?? 0)))];
3897
- for (const r of rows) out.push(line(cols.map((c) => r[c] ?? "")));
3898
- return out.join("\n");
3899
- }
3900
-
3901
3934
  // core/command.ts
3902
3935
  var BaseCommand = class extends Command {
3903
3936
  json = options_exports.Boolean("--json", false, {
@@ -3935,8 +3968,7 @@ var BaseCommand = class extends Command {
3935
3968
  return typeof code2 === "number" ? code2 : 0;
3936
3969
  } catch (err) {
3937
3970
  if (err instanceof ExitWithCode) return err.code;
3938
- this.context.stderr.write(`Error: ${errorMessage(err)}
3939
- `);
3971
+ writeCommandError(this.context.stdout, this.context.stderr, this.json, err);
3940
3972
  return 1;
3941
3973
  }
3942
3974
  }
@@ -4750,6 +4782,12 @@ ${footer}` : body;
4750
4782
  }
4751
4783
  // --- shared: activity list (used by `activity list` and `tasks`) --------
4752
4784
  async runActivityList(opts) {
4785
+ if ((opts.compact || opts.cursor !== void 0) && (!opts.entity || opts.typeFilter || opts.onlyOpen || opts.attrFilter?.length || opts.sort && opts.sort !== "created")) {
4786
+ throw new Error("--compact/--cursor require --entity and cannot combine with --type, --only-open, --attr or --sort deadline. Use an unfiltered page, then inspect full details.");
4787
+ }
4788
+ if (opts.minCount !== void 0 && (!Number.isSafeInteger(opts.minCount) || opts.minCount < 0 || !opts.entity || opts.typeFilter || opts.onlyOpen || opts.attrFilter?.length)) {
4789
+ throw new Error("--min-count requires an unfiltered entity-scoped page and a nonnegative integer. Use 0 for a strict time window.");
4790
+ }
4753
4791
  if (opts.sort && !VALID_SORT.has(opts.sort)) {
4754
4792
  throw new Error(`Invalid --sort value '${opts.sort}'. Allowed: deadline, created`);
4755
4793
  }
@@ -4795,6 +4833,8 @@ ${footer}` : body;
4795
4833
  const hasClientFilters = !!(opts.typeFilter || opts.onlyOpen || opts.attrFilter && opts.attrFilter.length);
4796
4834
  let activities;
4797
4835
  let serverHasMore;
4836
+ let nextCursor;
4837
+ let projection;
4798
4838
  if (hasClientFilters) {
4799
4839
  const hoursCutoff = opts.hours ? Date.now() - opts.hours * 36e5 : void 0;
4800
4840
  activities = [];
@@ -4840,9 +4880,14 @@ ${footer}` : body;
4840
4880
  if (opts.limit) params.limit = String(opts.limit);
4841
4881
  if (opts.before !== void 0) params.before = String(opts.before);
4842
4882
  if (opts.after !== void 0) params.after = String(opts.after);
4883
+ if (opts.cursor !== void 0) params.cursor = opts.cursor;
4884
+ if (opts.compact) params.compact = "true";
4885
+ if (opts.minCount !== void 0) params.minCount = String(opts.minCount);
4843
4886
  const res = await client.listActivities(params);
4844
4887
  activities = res.data ?? [];
4845
4888
  serverHasMore = res.hasMore;
4889
+ nextCursor = res.nextCursor;
4890
+ projection = res.projection;
4846
4891
  }
4847
4892
  if (opts.typeFilter) activities = activities.filter((a) => a.type === opts.typeFilter);
4848
4893
  if (opts.onlyOpen) activities = activities.filter((a) => !a.completed);
@@ -4863,14 +4908,20 @@ ${footer}` : body;
4863
4908
  const hasMore = hasClientFilters ? void 0 : serverHasMore;
4864
4909
  const jsonData = { data: activities };
4865
4910
  if (hasMore !== void 0) jsonData.hasMore = hasMore;
4911
+ if (nextCursor !== void 0) jsonData.nextCursor = nextCursor;
4912
+ if (projection !== void 0) jsonData.projection = projection;
4866
4913
  this.emitText(jsonWithSpaces(jsonData, spaces), () => {
4867
4914
  let body = formatActivityList(activities);
4868
- if (hasMore && activities.length) {
4869
- const oldest = activities[activities.length - 1];
4915
+ if (hasMore && nextCursor) {
4870
4916
  body += `
4871
4917
 
4872
- (more results available \u2014 use --before ${oldest.createdAt})`;
4918
+ (more results available \u2014 use --cursor '${nextCursor.replaceAll("'", "'\\''")}' with the same --entity and filters)`;
4919
+ } else if (hasMore && activities.length) {
4920
+ body += `
4921
+
4922
+ (more results available \u2014 use --before ${activities[activities.length - 1].createdAt})`;
4873
4923
  }
4924
+ if (projection) body += "\n\nCompact excerpts. Full detail: ente activity get <activityId> --json";
4874
4925
  return this.withFooter(body, spaces);
4875
4926
  });
4876
4927
  }
@@ -11115,14 +11166,12 @@ var AuthLogin = class extends BaseCommand {
11115
11166
  try {
11116
11167
  resp = await validateExternalCredential(cleanUrl, key, controller.signal);
11117
11168
  } catch {
11118
- this.err(`Could not reach ${cleanUrl}`);
11119
- return 1;
11169
+ throw new Error(`Could not reach ${cleanUrl}`);
11120
11170
  } finally {
11121
11171
  clearTimeout(timer);
11122
11172
  }
11123
11173
  if (resp.status >= 400) {
11124
- this.err(`Authentication failed: ${resp.status} ${resp.statusText}`);
11125
- return 1;
11174
+ throw new Error(`Authentication failed: ${resp.status} ${resp.statusText}`);
11126
11175
  }
11127
11176
  const previous = readCredentialsForCleanup(this.profile);
11128
11177
  if (previous) {
@@ -11818,6 +11867,7 @@ var EntityCreate = class extends ProfileCommand {
11818
11867
  const spaces = await spacesFooterFromMemberships(caller);
11819
11868
  const jsonOut = { data: { entityId } };
11820
11869
  if (pinFailures.length) {
11870
+ jsonOut.error = { message: "Entity created, but some parent relationships failed.", code: "PARTIAL_SUCCESS" };
11821
11871
  jsonOut.data = {
11822
11872
  entityId,
11823
11873
  pinFailures: pinFailures.map(([target, error]) => ({ target, error })),
@@ -11843,6 +11893,13 @@ Pinned to: ${pinSucceeded.join(", ")}`;
11843
11893
  }
11844
11894
  }
11845
11895
  };
11896
+ var EntityShowCorrection = class extends ProfileCommand {
11897
+ static paths = [["entity", "show"]];
11898
+ nameOrId = options_exports.String();
11899
+ async run() {
11900
+ throw new Error("Use 'ente entity get <name-or-id>' to read entity details.");
11901
+ }
11902
+ };
11846
11903
  var EntityGet = class extends ProfileCommand {
11847
11904
  static paths = [["entity", "get"]];
11848
11905
  static usage = Command.Usage({ category: "Entity", description: "Get entity details" });
@@ -11926,10 +11983,9 @@ var EntityUpdate = class extends ProfileCommand {
11926
11983
  didSomething = true;
11927
11984
  }
11928
11985
  if (!didSomething) {
11929
- this.err(
11986
+ throw new Error(
11930
11987
  "Nothing to update. Specify --name, --alias-add, --alias-remove, --attr, --tag, or --description."
11931
11988
  );
11932
- return 1;
11933
11989
  }
11934
11990
  const spaces = await spacesFooterFromMemberships(caller);
11935
11991
  this.emitText(
@@ -12215,6 +12271,7 @@ var EntityHtmlUpdate = class extends ProfileCommand {
12215
12271
  }
12216
12272
  };
12217
12273
  var ENTITY_COMMANDS = [
12274
+ EntityShowCorrection,
12218
12275
  EntityList,
12219
12276
  EntityCreate,
12220
12277
  EntityGet,
@@ -12247,6 +12304,96 @@ function readContentOption(content, contentFile) {
12247
12304
  return void 0;
12248
12305
  }
12249
12306
 
12307
+ // ente/deadline.ts
12308
+ var DEFAULT_HOUR = 22;
12309
+ var DEFAULT_MINUTE = 0;
12310
+ function addDaysToBerlinToday(days) {
12311
+ const t = berlinToday();
12312
+ const dt = new Date(Date.UTC(t.year, t.month - 1, t.day) + days * 864e5);
12313
+ return { year: dt.getUTCFullYear(), month: dt.getUTCMonth() + 1, day: dt.getUTCDate() };
12314
+ }
12315
+ function parseTimeOrDefault(s) {
12316
+ if (!s) return [DEFAULT_HOUR, DEFAULT_MINUTE];
12317
+ const m = /^(\d{1,2}):(\d{2})$/.exec(s);
12318
+ if (!m) throw new Error(`Invalid time format: '${s}'. Use HH:MM (e.g. 14:00).`);
12319
+ const hour = Number(m[1]);
12320
+ const minute = Number(m[2]);
12321
+ if (hour > 23 || minute > 59) throw new Error(`Invalid time format: '${s}'.`);
12322
+ return [hour, minute];
12323
+ }
12324
+ function validateDate(year, month, day, original) {
12325
+ if (month < 1 || month > 12 || day < 1 || day > 31) {
12326
+ throw new Error(`Invalid date: '${original}'.`);
12327
+ }
12328
+ const d = new Date(Date.UTC(year, month - 1, day));
12329
+ if (d.getUTCFullYear() !== year || d.getUTCMonth() + 1 !== month || d.getUTCDate() !== day) {
12330
+ throw new Error(`Invalid date: '${original}' \u2014 day ${day} does not exist in month ${month}.`);
12331
+ }
12332
+ }
12333
+ function parseDeadline(input) {
12334
+ const trimmed = input.trim().toLowerCase();
12335
+ if (trimmed.startsWith("tomorrow")) {
12336
+ const [hour, minute] = parseTimeOrDefault(trimmed.slice("tomorrow".length).trim());
12337
+ const t = addDaysToBerlinToday(1);
12338
+ return berlinToEpochMs(t.year, t.month, t.day, hour, minute);
12339
+ }
12340
+ if (trimmed.startsWith("today")) {
12341
+ const [hour, minute] = parseTimeOrDefault(trimmed.slice("today".length).trim());
12342
+ const t = addDaysToBerlinToday(0);
12343
+ return berlinToEpochMs(t.year, t.month, t.day, hour, minute);
12344
+ }
12345
+ const rel = /^\+(\d+)\s*(days?|weeks?)$/.exec(trimmed);
12346
+ if (rel) {
12347
+ const n = Number(rel[1]);
12348
+ const days = rel[2].startsWith("week") ? n * 7 : n;
12349
+ const t = addDaysToBerlinToday(days);
12350
+ return berlinToEpochMs(t.year, t.month, t.day, DEFAULT_HOUR, DEFAULT_MINUTE);
12351
+ }
12352
+ const iso = /^(\d{4})-(\d{2})-(\d{2})t(\d{2}):(\d{2})(?::(\d{2}))?(?:z|([+-])(\d{2}):(\d{2}))$/.exec(
12353
+ trimmed
12354
+ );
12355
+ if (iso) {
12356
+ const year = Number(iso[1]);
12357
+ const month = Number(iso[2]);
12358
+ const day = Number(iso[3]);
12359
+ const hour = Number(iso[4]);
12360
+ const minute = Number(iso[5]);
12361
+ const second = iso[6] ? Number(iso[6]) : 0;
12362
+ validateDate(year, month, day, input);
12363
+ if (hour > 23 || minute > 59 || second > 59) throw new Error(`Invalid time in deadline: '${input}'.`);
12364
+ let offsetMs = 0;
12365
+ if (iso[7]) {
12366
+ const sign = iso[7] === "-" ? -1 : 1;
12367
+ offsetMs = sign * (Number(iso[8]) * 60 + Number(iso[9])) * 6e4;
12368
+ }
12369
+ return Date.UTC(year, month - 1, day, hour, minute, second) - offsetMs;
12370
+ }
12371
+ const date = /^(\d{4})-(\d{2})-(\d{2})(?:\s+(\d{1,2}):(\d{2}))?$/.exec(trimmed);
12372
+ if (date) {
12373
+ const year = Number(date[1]);
12374
+ const month = Number(date[2]);
12375
+ const day = Number(date[3]);
12376
+ validateDate(year, month, day, input);
12377
+ const hour = date[4] ? Number(date[4]) : DEFAULT_HOUR;
12378
+ const minute = date[5] ? Number(date[5]) : DEFAULT_MINUTE;
12379
+ if (hour > 23 || minute > 59) throw new Error(`Invalid time in deadline: '${input}'.`);
12380
+ return berlinToEpochMs(year, month, day, hour, minute);
12381
+ }
12382
+ throw new Error(
12383
+ `Cannot parse deadline '${input}'. Accepted: "tomorrow", "today", "+3 days", "+1 week", "2026-03-15", "2026-03-15 14:00", "2026-03-15T14:00:00+01:00"`
12384
+ );
12385
+ }
12386
+ function defaultDeadline() {
12387
+ const t = addDaysToBerlinToday(1);
12388
+ return berlinToEpochMs(t.year, t.month, t.day, DEFAULT_HOUR, DEFAULT_MINUTE);
12389
+ }
12390
+ function deadlineChange(deadline, clear) {
12391
+ if (clear && deadline !== void 0) throw new Error("Use either --deadline or --clear-deadline, not both.");
12392
+ if (clear) return { deadline: void 0 };
12393
+ if (deadline !== void 0) return { deadline: parseDeadline(deadline) };
12394
+ return null;
12395
+ }
12396
+
12250
12397
  // ente/fileAttachments.ts
12251
12398
  import { readFileSync as readFileSync8 } from "node:fs";
12252
12399
  import { basename, extname } from "node:path";
@@ -12413,6 +12560,7 @@ var activitiesCreate = makeFunctionReference("activities:create");
12413
12560
  var activitiesGetCompleteByIdRich = makeFunctionReference("activities:getCompleteByIdRich");
12414
12561
  var activitiesGetByIdRich = makeFunctionReference("activities:getByIdRich");
12415
12562
  var activitiesSearchRich = makeFunctionReference("activities:searchRich");
12563
+ var activitiesUpdateDeadline = makeFunctionReference("activities:updateDeadline");
12416
12564
  var activitiesUpdateTitle = makeFunctionReference("activities:updateTitle");
12417
12565
  var activitiesSetContentFromMarkdown = makeFunctionReference("activities:setContentFromMarkdown");
12418
12566
  var activitiesReadContent = makeFunctionReference("activities:readContentDurable");
@@ -12447,6 +12595,9 @@ var ActivityList = class extends ClientCommand {
12447
12595
  static paths = [["activity", "list"]];
12448
12596
  static usage = Command.Usage({ category: "Activity", description: "List activities" });
12449
12597
  entity = options_exports.String("--entity", { description: "Filter by entity (name or ID)" });
12598
+ compact = options_exports.Boolean("--compact", false, { description: "Entity-scoped compact page; preserves spaces and continuation. Read full detail with activity get." });
12599
+ cursor = options_exports.String("--cursor", { description: "Opaque nextCursor from the previous entity-scoped page; keep the same entity and filters" });
12600
+ minCount = options_exports.String("--min-count", { description: "Minimum entity history entries, even outside the time window (default 5). Use 0 for a strict window." });
12450
12601
  type = options_exports.String("--type", { description: "Filter by type (task, call, note, message, conversation, meeting)" });
12451
12602
  attr = options_exports.Array("--attr", { description: "Filter by attribute key=value" });
12452
12603
  onlyOpen = options_exports.Boolean("--only-open", false, { description: "Only show non-completed activities" });
@@ -12460,6 +12611,7 @@ var ActivityList = class extends ClientCommand {
12460
12611
  });
12461
12612
  async run() {
12462
12613
  if (this.archived) {
12614
+ if (this.compact || this.cursor !== void 0 || this.minCount !== void 0) throw new Error("--compact/--cursor/--min-count require an active entity-scoped activity list.");
12463
12615
  const client = this.client();
12464
12616
  const params = { archived: "true" };
12465
12617
  if (this.limit) params.limit = this.limit;
@@ -12472,6 +12624,9 @@ var ActivityList = class extends ClientCommand {
12472
12624
  return;
12473
12625
  }
12474
12626
  await this.runActivityList({
12627
+ compact: this.compact,
12628
+ cursor: this.cursor,
12629
+ minCount: this.minCount !== void 0 ? Number(this.minCount) : void 0,
12475
12630
  entity: this.entity,
12476
12631
  typeFilter: this.type,
12477
12632
  attrFilter: this.attr,
@@ -12676,14 +12831,16 @@ var ActivityUpdate = class extends ProfileCommand {
12676
12831
  content = options_exports.String("--content", { description: "Update content" });
12677
12832
  contentFile = options_exports.String("--content-file", { description: "Read updated content from a markdown/text file" });
12678
12833
  title = options_exports.String("--title", { description: "Update title" });
12834
+ deadline = options_exports.String("--deadline", { description: "Update deadline (human-friendly)" });
12835
+ clearDeadline = options_exports.Boolean("--clear-deadline", false, { description: "Remove the deadline" });
12679
12836
  entityAdd = options_exports.Array("--entity-add", { description: "Link additional entities" });
12680
12837
  entityRemove = options_exports.Array("--entity-remove", { description: "Unlink entities" });
12681
12838
  async run() {
12682
12839
  const content = readContentOption(this.content, this.contentFile);
12840
+ const deadline = deadlineChange(this.deadline, this.clearDeadline);
12683
12841
  const hasEntityChange = !!(this.entityAdd && this.entityAdd.length || this.entityRemove && this.entityRemove.length);
12684
- if (this.content === void 0 && this.contentFile === void 0 && this.title === void 0 && !hasEntityChange && !this.agentAssignee?.length && !this.clearAgentAssignees) {
12685
- this.err("Nothing to update. Specify content, title, entity links or agent assignees.");
12686
- return 1;
12842
+ if (this.content === void 0 && this.contentFile === void 0 && this.title === void 0 && deadline === null && !hasEntityChange && !this.agentAssignee?.length && !this.clearAgentAssignees) {
12843
+ throw new Error("Nothing to update. Specify content, title, deadline, entity links or agent assignees.");
12687
12844
  }
12688
12845
  if (this.clearAgentAssignees && this.agentAssignee?.length) throw new Error("Choose --agent-assignee or --clear-agent-assignees");
12689
12846
  const caller = await resolveCaller();
@@ -12692,6 +12849,7 @@ var ActivityUpdate = class extends ProfileCommand {
12692
12849
  { activityId: this.id, agentAssigneeIds: this.agentAssignee ?? [] }
12693
12850
  );
12694
12851
  if (this.title !== void 0) await caller.mutation(activitiesUpdateTitle, { activityId: this.id, title: this.title });
12852
+ if (deadline) await caller.mutation(activitiesUpdateDeadline, { activityId: this.id, ...deadline });
12695
12853
  if (content !== void 0) await caller.mutation(activitiesSetContentFromMarkdown, { activityId: this.id, markdown: content });
12696
12854
  if (hasEntityChange) {
12697
12855
  const rich = await caller.query(activitiesGetByIdRich, { activityId: this.id });
@@ -12703,8 +12861,7 @@ var ActivityUpdate = class extends ProfileCommand {
12703
12861
  const newIds = currentIds.filter((eid) => !removeSet.has(eid));
12704
12862
  for (const eid of addIds) if (!currentIds.includes(eid)) newIds.push(eid);
12705
12863
  if (newIds.length === 0) {
12706
- this.err("Cannot remove all entities \u2014 an activity must have at least one.");
12707
- return 1;
12864
+ throw new Error("Cannot remove all entities \u2014 an activity must have at least one.");
12708
12865
  }
12709
12866
  await caller.mutation(activitiesUpdateEntities, { activityId: this.id, entities: newIds });
12710
12867
  }
@@ -12879,8 +13036,7 @@ var ActivityMessageAdd = class extends ProfileCommand {
12879
13036
  async run() {
12880
13037
  const markdown = readContentOption(this.content, this.contentFile);
12881
13038
  if (!markdown?.trim()) {
12882
- this.err("Message content is required. Specify --content or --content-file.");
12883
- return 1;
13039
+ throw new Error("Message content is required. Specify --content or --content-file.");
12884
13040
  }
12885
13041
  const caller = await resolveCaller();
12886
13042
  const messageId = await caller.mutation(messagesCreateFromMarkdown, {
@@ -13174,90 +13330,6 @@ var ActivityTranscript = class extends ProfileCommand {
13174
13330
  };
13175
13331
  var ACTIVITY_TRANSCRIPT_COMMANDS = [ActivityTranscript];
13176
13332
 
13177
- // ente/deadline.ts
13178
- var DEFAULT_HOUR = 22;
13179
- var DEFAULT_MINUTE = 0;
13180
- function addDaysToBerlinToday(days) {
13181
- const t = berlinToday();
13182
- const dt = new Date(Date.UTC(t.year, t.month - 1, t.day) + days * 864e5);
13183
- return { year: dt.getUTCFullYear(), month: dt.getUTCMonth() + 1, day: dt.getUTCDate() };
13184
- }
13185
- function parseTimeOrDefault(s) {
13186
- if (!s) return [DEFAULT_HOUR, DEFAULT_MINUTE];
13187
- const m = /^(\d{1,2}):(\d{2})$/.exec(s);
13188
- if (!m) throw new Error(`Invalid time format: '${s}'. Use HH:MM (e.g. 14:00).`);
13189
- const hour = Number(m[1]);
13190
- const minute = Number(m[2]);
13191
- if (hour > 23 || minute > 59) throw new Error(`Invalid time format: '${s}'.`);
13192
- return [hour, minute];
13193
- }
13194
- function validateDate(year, month, day, original) {
13195
- if (month < 1 || month > 12 || day < 1 || day > 31) {
13196
- throw new Error(`Invalid date: '${original}'.`);
13197
- }
13198
- const d = new Date(Date.UTC(year, month - 1, day));
13199
- if (d.getUTCFullYear() !== year || d.getUTCMonth() + 1 !== month || d.getUTCDate() !== day) {
13200
- throw new Error(`Invalid date: '${original}' \u2014 day ${day} does not exist in month ${month}.`);
13201
- }
13202
- }
13203
- function parseDeadline(input) {
13204
- const trimmed = input.trim().toLowerCase();
13205
- if (trimmed.startsWith("tomorrow")) {
13206
- const [hour, minute] = parseTimeOrDefault(trimmed.slice("tomorrow".length).trim());
13207
- const t = addDaysToBerlinToday(1);
13208
- return berlinToEpochMs(t.year, t.month, t.day, hour, minute);
13209
- }
13210
- if (trimmed.startsWith("today")) {
13211
- const [hour, minute] = parseTimeOrDefault(trimmed.slice("today".length).trim());
13212
- const t = addDaysToBerlinToday(0);
13213
- return berlinToEpochMs(t.year, t.month, t.day, hour, minute);
13214
- }
13215
- const rel = /^\+(\d+)\s*(days?|weeks?)$/.exec(trimmed);
13216
- if (rel) {
13217
- const n = Number(rel[1]);
13218
- const days = rel[2].startsWith("week") ? n * 7 : n;
13219
- const t = addDaysToBerlinToday(days);
13220
- return berlinToEpochMs(t.year, t.month, t.day, DEFAULT_HOUR, DEFAULT_MINUTE);
13221
- }
13222
- const iso = /^(\d{4})-(\d{2})-(\d{2})t(\d{2}):(\d{2})(?::(\d{2}))?(?:z|([+-])(\d{2}):(\d{2}))$/.exec(
13223
- trimmed
13224
- );
13225
- if (iso) {
13226
- const year = Number(iso[1]);
13227
- const month = Number(iso[2]);
13228
- const day = Number(iso[3]);
13229
- const hour = Number(iso[4]);
13230
- const minute = Number(iso[5]);
13231
- const second = iso[6] ? Number(iso[6]) : 0;
13232
- validateDate(year, month, day, input);
13233
- if (hour > 23 || minute > 59 || second > 59) throw new Error(`Invalid time in deadline: '${input}'.`);
13234
- let offsetMs = 0;
13235
- if (iso[7]) {
13236
- const sign = iso[7] === "-" ? -1 : 1;
13237
- offsetMs = sign * (Number(iso[8]) * 60 + Number(iso[9])) * 6e4;
13238
- }
13239
- return Date.UTC(year, month - 1, day, hour, minute, second) - offsetMs;
13240
- }
13241
- const date = /^(\d{4})-(\d{2})-(\d{2})(?:\s+(\d{1,2}):(\d{2}))?$/.exec(trimmed);
13242
- if (date) {
13243
- const year = Number(date[1]);
13244
- const month = Number(date[2]);
13245
- const day = Number(date[3]);
13246
- validateDate(year, month, day, input);
13247
- const hour = date[4] ? Number(date[4]) : DEFAULT_HOUR;
13248
- const minute = date[5] ? Number(date[5]) : DEFAULT_MINUTE;
13249
- if (hour > 23 || minute > 59) throw new Error(`Invalid time in deadline: '${input}'.`);
13250
- return berlinToEpochMs(year, month, day, hour, minute);
13251
- }
13252
- throw new Error(
13253
- `Cannot parse deadline '${input}'. Accepted: "tomorrow", "today", "+3 days", "+1 week", "2026-03-15", "2026-03-15 14:00", "2026-03-15T14:00:00+01:00"`
13254
- );
13255
- }
13256
- function defaultDeadline() {
13257
- const t = addDaysToBerlinToday(1);
13258
- return berlinToEpochMs(t.year, t.month, t.day, DEFAULT_HOUR, DEFAULT_MINUTE);
13259
- }
13260
-
13261
13333
  // ente/commands/task.ts
13262
13334
  function toTaskItem(rich) {
13263
13335
  return {
@@ -13296,7 +13368,7 @@ var activitiesSetAttribute = makeFunctionReference("activities:setAttribute");
13296
13368
  var activitiesUpdateCreatedAt2 = makeFunctionReference("activities:updateCreatedAt");
13297
13369
  var activitiesUpdateTitle2 = makeFunctionReference("activities:updateTitle");
13298
13370
  var activitiesSetContentFromMarkdown2 = makeFunctionReference("activities:setContentFromMarkdown");
13299
- var activitiesUpdateDeadline = makeFunctionReference("activities:updateDeadline");
13371
+ var activitiesUpdateDeadline2 = makeFunctionReference("activities:updateDeadline");
13300
13372
  var activitiesSetCompleted = makeFunctionReference("activities:setCompleted");
13301
13373
  var activitiesUpdateAssignees = makeFunctionReference("activities:updateAssignees");
13302
13374
  function parseShortId(idStr) {
@@ -13333,8 +13405,7 @@ var TaskListCmd = class extends ClientCommand {
13333
13405
  else if (this.due === "today") params.dueBy = String(endOfTodayBerlin());
13334
13406
  else if (this.due === "overdue") params.dueBy = String(Date.now());
13335
13407
  else if (this.due) {
13336
- this.err(`Unknown --due value: '${this.due}'. Use: today, overdue, or all.`);
13337
- return 1;
13408
+ throw new Error(`Unknown --due value: '${this.due}'. Use: today, overdue, or all.`);
13338
13409
  }
13339
13410
  const res = await client.listTasks(params);
13340
13411
  const tasks = (res.data ?? []).slice();
@@ -13462,17 +13533,18 @@ var TaskUpdate = class extends ProfileCommand {
13462
13533
  content = options_exports.String("--content", { description: "Update content" });
13463
13534
  attr = options_exports.Array("--attr", { description: "Set attributes as key=value pairs" });
13464
13535
  deadline = options_exports.String("--deadline", { description: "Update deadline (human-friendly)" });
13536
+ clearDeadline = options_exports.Boolean("--clear-deadline", false, { description: "Remove the deadline" });
13465
13537
  async run() {
13466
13538
  const attributes = parseAttrs(this.attr);
13467
- if (this.title === void 0 && this.content === void 0 && this.deadline === void 0 && Object.keys(attributes).length === 0) {
13468
- this.err("Nothing to update. Specify --title, --content, --attr, or --deadline.");
13469
- return 1;
13539
+ const deadline = deadlineChange(this.deadline, this.clearDeadline);
13540
+ if (this.title === void 0 && this.content === void 0 && deadline === null && Object.keys(attributes).length === 0) {
13541
+ throw new Error("Nothing to update. Specify --title, --content, --attr, --deadline, or --clear-deadline.");
13470
13542
  }
13471
13543
  const caller = await resolveCaller();
13472
13544
  const activityId = await resolveTaskIdC(caller, this.id);
13473
13545
  if (this.title !== void 0) await caller.mutation(activitiesUpdateTitle2, { activityId, title: this.title });
13474
13546
  if (this.content !== void 0) await caller.mutation(activitiesSetContentFromMarkdown2, { activityId, markdown: this.content });
13475
- if (this.deadline) await caller.mutation(activitiesUpdateDeadline, { activityId, deadline: parseDeadline(this.deadline) });
13547
+ if (deadline) await caller.mutation(activitiesUpdateDeadline2, { activityId, ...deadline });
13476
13548
  for (const [key, value] of Object.entries(attributes)) {
13477
13549
  await caller.mutation(activitiesSetAttribute, { activityId, key, value });
13478
13550
  }
@@ -14984,7 +15056,7 @@ var RecordingRerunCommand = class extends ProfileCommand {
14984
15056
  audioPreserved
14985
15057
  };
14986
15058
  if (after.processingStatus === "error") {
14987
- this.emitText(data, () => `Pipeline failed for ${after.id}: ${after.processingError}`);
15059
+ this.emitText({ ...data, error: { message: `Pipeline failed for ${after.id}: ${after.processingError}` } }, () => `Pipeline failed for ${after.id}: ${after.processingError}`);
14988
15060
  return 1;
14989
15061
  }
14990
15062
  this.emitText(
@@ -15927,7 +15999,7 @@ Commands:
15927
15999
  ente guide [topic] This help
15928
16000
 
15929
16001
  Run \`ente <command> --help\` for detailed usage on any command.
15930
- Run \`ente guide <topic>\` for deep dives: entities, tasks, activities, search, patterns, limits`
16002
+ Run \`ente guide <topic>\` for deep dives: entities, tasks, activities, search, retrieval, patterns, limits`
15931
16003
  },
15932
16004
  entities: {
15933
16005
  title: "Entities",
@@ -15986,7 +16058,8 @@ Creating tasks:
15986
16058
  ente task create "Reply to Ashley" --entity "Ashley" --space "Personal" --deadline "tomorrow" --assignee ashley@example.com
15987
16059
  ente task create "Fix bug" --entity "TiVA" --space "TiVA" --deadline "+3 days" --attr github-issue=https://...
15988
16060
 
15989
- Every task needs a deadline. If not specified, defaults to tomorrow 22:00 Berlin.
16061
+ Without --deadline a task is due tomorrow 22:00 Berlin; --no-deadline creates it without one.
16062
+ Remove a deadline later with: ente task update 5 --clear-deadline
15990
16063
 
15991
16064
  Deadlines (human-friendly):
15992
16065
  "tomorrow" \u2192 tomorrow 22:00 Berlin
@@ -16053,6 +16126,26 @@ Archiving (trash):
16053
16126
  ente activity delete <activity-id> # move to trash (soft delete)
16054
16127
  ente activity list --archived # list trashed activities
16055
16128
  ente activity restore <activity-id> # restore from trash`
16129
+ },
16130
+ retrieval: {
16131
+ title: "Bounded retrieval",
16132
+ content: `Resolve entities globally first: ente search "Apollo" --json
16133
+ Use an explicit space when the task specifies one:
16134
+ ente query "launch decisions" --space "Work" --entity "Apollo" --since 1791763200000 --until 1792368000000 --limit 8 --json
16135
+ Time filters apply to activity evidence; query uses no model.
16136
+
16137
+ Read one compact entity-history page:
16138
+ ente activity list --entity <id> --after 1791763200000 --min-count 0 --limit 10 --compact --json
16139
+ Continue with --cursor <nextCursor>, keeping the entity and time filters.
16140
+ Compact pages retain spaces/member context, IDs, hasMore and nextCursor.
16141
+ Bodies are excerpts; projection.fullDetail names the complete read.
16142
+ ente activity get <activityId> --json
16143
+
16144
+ Keep spaces, spaceId/spaceIds, scope, warnings and continuation when selecting
16145
+ fields. Access does not authorize copying records into another space.
16146
+ Check command exits before parsing JSON. JSON failures use an error envelope
16147
+ on stdout and a nonzero exit. Use entity get, not entity show.
16148
+ For examples: playbooks/retrieve-bounded-context.md`
16056
16149
  },
16057
16150
  search: {
16058
16151
  title: "Search \u2014 Fuzzy, Multi-Strategy",
@@ -16145,8 +16238,7 @@ var GuideCmd = class extends BaseCommand {
16145
16238
  }
16146
16239
  const entry = TOPICS[this.topic.toLowerCase()];
16147
16240
  if (!entry) {
16148
- this.err(`Unknown topic: '${this.topic}'. Available: ${TOPIC_NAMES.join(", ")}`);
16149
- return 1;
16241
+ throw new Error(`Unknown topic: '${this.topic}'. Available: ${TOPIC_NAMES.join(", ")}`);
16150
16242
  }
16151
16243
  this.out(`
16152
16244
  ${entry.title}
@@ -17197,7 +17289,7 @@ async function main() {
17197
17289
  upgradeCommand: ENTE_BRANDING.upgradeCommand
17198
17290
  });
17199
17291
  }
17200
- await cli.runExit(argv);
17292
+ process.exitCode = await runCli(cli, argv, { stdout: process.stdout, stderr: process.stderr });
17201
17293
  }
17202
17294
  void main();
17203
17295
  /*! Bundled license information: