@robbeverhelst/do 1.340.0 → 1.341.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.
Files changed (2) hide show
  1. package/dist/do.js +2268 -270
  2. package/package.json +1 -1
package/dist/do.js CHANGED
@@ -14418,7 +14418,7 @@ var configError = (message) => new CliError("config", message, EXIT.CONFIG);
14418
14418
 
14419
14419
  // src/meta.ts
14420
14420
  var CLI_NAME = "do";
14421
- var VERSION = "1.340.0";
14421
+ var VERSION = "1.341.0";
14422
14422
  var API_PREFIX = "/api/v1";
14423
14423
  var DEFAULT_API_URL = "http://localhost:3001";
14424
14424
 
@@ -14852,8 +14852,8 @@ function httpMessage(status, body) {
14852
14852
  if (typeof e === "string")
14853
14853
  return e;
14854
14854
  const inner = e?.message;
14855
- if (typeof inner === "string" && status === 413)
14856
- return `${inner} (HTTP 413)`;
14855
+ if (typeof inner === "string")
14856
+ return status === 413 ? `${inner} (HTTP 413)` : inner;
14857
14857
  }
14858
14858
  if (typeof body === "string" && body.length > 0)
14859
14859
  return body;
@@ -17642,6 +17642,7 @@ function uniqueIndex(name) {
17642
17642
 
17643
17643
  // ../../packages/schema/src/db/tenancy.ts
17644
17644
  var baseRole = pgEnum("base_role", ["owner", "admin", "member", "guest"]);
17645
+ var workPriority = pgEnum("work_priority", ["none", "low", "medium", "high"]);
17645
17646
  var tenants = pgTable("tenants", {
17646
17647
  id: text("id").primaryKey(),
17647
17648
  name: text("name").notNull(),
@@ -17649,6 +17650,8 @@ var tenants = pgTable("tenants", {
17649
17650
  gaardHouseUrl: text("gaard_house_url"),
17650
17651
  awayFrom: date5("away_from", { mode: "string" }),
17651
17652
  awayUntil: date5("away_until", { mode: "string" }),
17653
+ lastTaskNumber: integer2("last_task_number").notNull().default(0),
17654
+ lastProjectNumber: integer2("last_project_number").notNull().default(0),
17652
17655
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
17653
17656
  updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
17654
17657
  deletedAt: timestamp("deleted_at", { withTimezone: true })
@@ -17883,176 +17886,6 @@ var automationRuns = pgTable("automation_runs", {
17883
17886
  index("automation_runs_tenant_automation_created_idx").on(t.tenantId, t.automationId, t.createdAt)
17884
17887
  ]);
17885
17888
 
17886
- // ../../packages/schema/src/db/challenges.ts
17887
- var challengeSource = pgEnum("challenge_source", ["self", "system"]);
17888
- var challengeSubjectType = pgEnum("challenge_subject_type", ["habit", "area", "tag"]);
17889
- var challenges = pgTable("challenges", {
17890
- id: text("id").primaryKey(),
17891
- tenantId: text("tenant_id").notNull().references(() => tenants.id, { onDelete: "cascade" }),
17892
- title: text("title").notNull(),
17893
- description: text("description"),
17894
- source: challengeSource("source").notNull().default("self"),
17895
- metric: text("metric").notNull(),
17896
- subjectType: challengeSubjectType("subject_type"),
17897
- subjectId: text("subject_id"),
17898
- target: integer2("target").notNull(),
17899
- startAt: timestamp("start_at", { withTimezone: true }).notNull(),
17900
- endAt: timestamp("end_at", { withTimezone: true }).notNull(),
17901
- createdBy: text("created_by").references(() => users.id, { onDelete: "set null" }),
17902
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
17903
- updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
17904
- }, (t) => [
17905
- index("challenges_tenant_idx").on(t.tenantId),
17906
- index("challenges_tenant_window_idx").on(t.tenantId, t.startAt, t.endAt),
17907
- index("challenges_tenant_subject_idx").on(t.tenantId, t.subjectType, t.subjectId),
17908
- check2("challenges_subject_pair_chk", sql`(${t.subjectType} IS NULL) = (${t.subjectId} IS NULL)`)
17909
- ]);
17910
- var challengeParticipants = pgTable("challenge_participants", {
17911
- id: text("id").primaryKey(),
17912
- tenantId: text("tenant_id").notNull().references(() => tenants.id, { onDelete: "cascade" }),
17913
- challengeId: text("challenge_id").notNull().references(() => challenges.id, { onDelete: "cascade" }),
17914
- userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
17915
- joinedAt: timestamp("joined_at", { withTimezone: true }).notNull().defaultNow()
17916
- }, (t) => [
17917
- unique("challenge_participants_challenge_user_uniq").on(t.challengeId, t.userId),
17918
- index("challenge_participants_tenant_idx").on(t.tenantId),
17919
- index("challenge_participants_user_idx").on(t.tenantId, t.userId)
17920
- ]);
17921
-
17922
- // ../../packages/schema/src/db/collaboration.ts
17923
- var capability = pgEnum("collab_capability", ["view", "comment", "edit", "admin"]);
17924
- var grantSubjectType = pgEnum("grant_subject_type", ["member", "role"]);
17925
- var inviteType = pgEnum("invite_type", ["email", "link", "join_request", "guest"]);
17926
- var inviteStatus = pgEnum("invite_status", ["pending", "accepted", "revoked", "expired"]);
17927
- var permissionSets = pgTable("permission_sets", {
17928
- id: text("id").primaryKey(),
17929
- tenantId: text("tenant_id").notNull().references(() => tenants.id),
17930
- name: text("name").notNull(),
17931
- canView: boolean4("can_view").notNull().default(true),
17932
- canComment: boolean4("can_comment").notNull().default(false),
17933
- canEdit: boolean4("can_edit").notNull().default(false),
17934
- canShare: boolean4("can_share").notNull().default(false),
17935
- canManageMembers: boolean4("can_manage_members").notNull().default(false),
17936
- canManageRoles: boolean4("can_manage_roles").notNull().default(false),
17937
- canManageSpace: boolean4("can_manage_space").notNull().default(false),
17938
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
17939
- updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
17940
- }, (t) => [unique("permission_sets_tenant_name_uniq").on(t.tenantId, t.name)]);
17941
- var roles = pgTable("roles", {
17942
- id: text("id").primaryKey(),
17943
- tenantId: text("tenant_id").notNull().references(() => tenants.id),
17944
- name: text("name").notNull(),
17945
- description: text("description"),
17946
- permissionSetId: text("permission_set_id").notNull().references(() => permissionSets.id),
17947
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
17948
- updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
17949
- }, (t) => [unique("roles_tenant_name_uniq").on(t.tenantId, t.name)]);
17950
- var invites = pgTable("invites", {
17951
- id: text("id").primaryKey(),
17952
- tenantId: text("tenant_id").notNull().references(() => tenants.id),
17953
- type: inviteType("type").notNull(),
17954
- email: text("email"),
17955
- token: text("token").unique(),
17956
- tokenHash: text("token_hash").unique(),
17957
- tokenSealed: text("token_sealed"),
17958
- role: baseRole("role").notNull().default("member"),
17959
- customRoleId: text("custom_role_id").references(() => roles.id),
17960
- invitedByUserId: text("invited_by_user_id").references(() => users.id),
17961
- status: inviteStatus("status").notNull().default("pending"),
17962
- accessUntil: timestamp("access_until", { withTimezone: true }),
17963
- expiresAt: timestamp("expires_at", { withTimezone: true }),
17964
- acceptedAt: timestamp("accepted_at", { withTimezone: true }),
17965
- acceptedByUserId: text("accepted_by_user_id").references(() => users.id),
17966
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
17967
- updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
17968
- }, (t) => [index("invites_tenant_status_idx").on(t.tenantId, t.status)]);
17969
- var nodeGrants = pgTable("node_grants", {
17970
- id: text("id").primaryKey(),
17971
- tenantId: text("tenant_id").notNull().references(() => tenants.id),
17972
- nodeId: text("node_id").notNull().references(() => nodes.id),
17973
- subjectType: grantSubjectType("subject_type").notNull(),
17974
- subjectId: text("subject_id").notNull(),
17975
- capability: capability("capability").notNull(),
17976
- assigned: boolean4("assigned").notNull().default(false),
17977
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
17978
- updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
17979
- }, (t) => [
17980
- unique("node_grants_node_subject_uniq").on(t.tenantId, t.nodeId, t.subjectType, t.subjectId),
17981
- index("node_grants_tenant_node_idx").on(t.tenantId, t.nodeId),
17982
- index("node_grants_node_idx").on(t.nodeId)
17983
- ]);
17984
- var shareLinks = pgTable("share_links", {
17985
- id: text("id").primaryKey(),
17986
- tenantId: text("tenant_id").notNull().references(() => tenants.id),
17987
- nodeId: text("node_id").notNull().references(() => nodes.id),
17988
- tokenHash: text("token_hash").notNull().unique(),
17989
- capability: capability("capability").notNull().default("view"),
17990
- createdByUserId: text("created_by_user_id").references(() => users.id),
17991
- expiresAt: timestamp("expires_at", { withTimezone: true }),
17992
- revokedAt: timestamp("revoked_at", { withTimezone: true }),
17993
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
17994
- updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
17995
- }, (t) => [
17996
- index("share_links_tenant_node_idx").on(t.tenantId, t.nodeId),
17997
- index("share_links_node_idx").on(t.nodeId)
17998
- ]);
17999
- var shareLinkGuests = pgTable("share_link_guests", {
18000
- id: text("id").primaryKey(),
18001
- tenantId: text("tenant_id").notNull().references(() => tenants.id),
18002
- shareLinkId: text("share_link_id").notNull().references(() => shareLinks.id, { onDelete: "cascade" }),
18003
- name: text("name").notNull(),
18004
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
18005
- }, (t) => [
18006
- uniqueIndex("share_link_guests_link_name_uniq").on(t.shareLinkId, sql`lower(${t.name})`),
18007
- index("share_link_guests_tenant_idx").on(t.tenantId)
18008
- ]);
18009
- var comments = pgTable("comments", {
18010
- id: text("id").primaryKey(),
18011
- tenantId: text("tenant_id").notNull().references(() => tenants.id),
18012
- nodeId: text("node_id").notNull().references(() => nodes.id),
18013
- parentId: text("parent_id").references(() => comments.id),
18014
- authorUserId: text("author_user_id").notNull().references(() => users.id),
18015
- body: text("body").notNull(),
18016
- editedAt: timestamp("edited_at", { withTimezone: true }),
18017
- deletedAt: timestamp("deleted_at", { withTimezone: true }),
18018
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
18019
- updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
18020
- }, (t) => [
18021
- index("comments_tenant_node_created_idx").on(t.tenantId, t.nodeId, t.createdAt),
18022
- index("comments_parent_idx").on(t.parentId),
18023
- index("comments_node_idx").on(t.nodeId)
18024
- ]);
18025
- var commentMentions = pgTable("comment_mentions", {
18026
- id: text("id").primaryKey(),
18027
- tenantId: text("tenant_id").notNull().references(() => tenants.id),
18028
- commentId: text("comment_id").notNull().references(() => comments.id),
18029
- mentionedUserId: text("mentioned_user_id").notNull().references(() => users.id),
18030
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
18031
- }, (t) => [unique("comment_mentions_comment_user_uniq").on(t.commentId, t.mentionedUserId)]);
18032
- var commentReactions = pgTable("comment_reactions", {
18033
- id: text("id").primaryKey(),
18034
- tenantId: text("tenant_id").notNull().references(() => tenants.id),
18035
- commentId: text("comment_id").notNull().references(() => comments.id),
18036
- userId: text("user_id").notNull().references(() => users.id),
18037
- emoji: text("emoji").notNull(),
18038
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
18039
- }, (t) => [unique("comment_reactions_comment_user_emoji_uniq").on(t.commentId, t.userId, t.emoji)]);
18040
- var activityEvents = pgTable("activity_events", {
18041
- id: text("id").primaryKey(),
18042
- tenantId: text("tenant_id").notNull().references(() => tenants.id, { onDelete: "cascade" }),
18043
- actorUserId: text("actor_user_id").references(() => users.id, { onDelete: "set null" }),
18044
- actorAgentId: text("actor_agent_id"),
18045
- action: text("action").notNull(),
18046
- nodeId: text("node_id"),
18047
- subjectType: text("subject_type"),
18048
- subjectId: text("subject_id"),
18049
- metadata: text("metadata"),
18050
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
18051
- }, (t) => [
18052
- index("activity_events_tenant_created_idx").on(t.tenantId, t.createdAt),
18053
- index("activity_events_tenant_node_idx").on(t.tenantId, t.nodeId)
18054
- ]);
18055
-
18056
17889
  // ../../packages/schema/src/db/places.ts
18057
17890
  var placeKindValues = ["room", "building", "outdoor", "storage", "elsewhere"];
18058
17891
  var places = pgTable("places", {
@@ -18069,7 +17902,14 @@ var places = pgTable("places", {
18069
17902
  }, (t) => [index("places_tenant_idx").on(t.tenantId, t.levelOrder)]);
18070
17903
 
18071
17904
  // ../../packages/schema/src/db/domain.ts
18072
- var taskStatus = pgEnum("task_status", ["todo", "in_progress", "done", "cancelled"]);
17905
+ var taskStatus = pgEnum("task_status", [
17906
+ "backlog",
17907
+ "todo",
17908
+ "in_progress",
17909
+ "waiting",
17910
+ "done",
17911
+ "cancelled"
17912
+ ]);
18073
17913
  var habitMeasurementType = pgEnum("habit_measurement_type", [
18074
17914
  "binary",
18075
17915
  "quantitative",
@@ -18098,6 +17938,9 @@ var occurrenceStatus = pgEnum("occurrence_status", [
18098
17938
  var tasks = pgTable("tasks", {
18099
17939
  ...leafColumns(),
18100
17940
  status: taskStatus("status").notNull().default("todo"),
17941
+ priority: workPriority("priority").notNull().default("none"),
17942
+ number: integer2("number").notNull().default(0),
17943
+ deliverable: text("deliverable"),
18101
17944
  starred: boolean4("starred").notNull().default(false),
18102
17945
  allDay: boolean4("all_day").notNull().default(true),
18103
17946
  scheduledAt: timestamp("scheduled_at", { withTimezone: true }),
@@ -18142,6 +17985,7 @@ var tasks = pgTable("tasks", {
18142
17985
  movedFrom: timestamp("moved_from", { withTimezone: true })
18143
17986
  }, (t) => [
18144
17987
  index("tasks_tenant_project_idx").on(t.tenantId, t.projectId),
17988
+ unique("tasks_tenant_number_uniq").on(t.tenantId, t.number),
18145
17989
  index("tasks_tenant_place_idx").on(t.tenantId, t.placeId),
18146
17990
  index("tasks_milestone_idx").on(t.milestoneId),
18147
17991
  index("tasks_tenant_owner_agent_idx").on(t.tenantId, t.ownerAgentId, t.ownerDesk),
@@ -18301,10 +18145,12 @@ var routineRunSteps = pgTable("routine_run_steps", {
18301
18145
  id: text("id").primaryKey(),
18302
18146
  tenantId: text("tenant_id").notNull().references(() => tenants.id),
18303
18147
  runId: text("run_id").notNull().references(() => routineRuns.id),
18304
- stepId: text("step_id").notNull().references(() => routineSteps.id),
18148
+ stepId: text("step_id").references(() => routineSteps.id),
18305
18149
  title: text("title").notNull(),
18306
18150
  refKind: routineStepRef("ref_kind").notNull().default("free"),
18307
18151
  habitId: text("habit_id").references(() => habits.id),
18152
+ taskId: text("task_id").references(() => tasks.id),
18153
+ reason: text("reason"),
18308
18154
  habitEntryId: text("habit_entry_id").references(() => habitEntries.id),
18309
18155
  sortOrder: integer2("sort_order").notNull().default(0),
18310
18156
  durationMinutes: integer2("duration_minutes"),
@@ -18319,6 +18165,8 @@ var routineRunSteps = pgTable("routine_run_steps", {
18319
18165
  var projects = pgTable("projects", {
18320
18166
  ...leafColumns(),
18321
18167
  status: projectStatus("status").notNull().default("planning"),
18168
+ priority: workPriority("priority").notNull().default("none"),
18169
+ number: integer2("number").notNull().default(0),
18322
18170
  icon: text("icon"),
18323
18171
  progress: integer2("progress").notNull().default(0),
18324
18172
  startAt: timestamp("start_at", { withTimezone: true }),
@@ -18329,6 +18177,7 @@ var projects = pgTable("projects", {
18329
18177
  finishNotYetAt: timestamp("finish_not_yet_at", { withTimezone: true })
18330
18178
  }, (t) => [
18331
18179
  index("projects_tenant_idx").on(t.tenantId),
18180
+ unique("projects_tenant_number_uniq").on(t.tenantId, t.number),
18332
18181
  index("projects_tenant_area_idx").on(t.tenantId, t.areaId)
18333
18182
  ]);
18334
18183
  var projectMilestones = pgTable("project_milestones", {
@@ -18370,6 +18219,191 @@ var userSettings = pgTable("user_settings", {
18370
18219
  updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
18371
18220
  }, (t) => [unique("user_settings_tenant_user_uniq").on(t.tenantId, t.userId)]);
18372
18221
 
18222
+ // ../../packages/schema/src/db/challenges.ts
18223
+ var challengeSource = pgEnum("challenge_source", ["self", "system"]);
18224
+ var challengeSubjectType = pgEnum("challenge_subject_type", ["habit", "area", "tag"]);
18225
+ var challengePartKind = pgEnum("challenge_part_kind", ["habit", "task_series"]);
18226
+ var challenges = pgTable("challenges", {
18227
+ id: text("id").primaryKey(),
18228
+ tenantId: text("tenant_id").notNull().references(() => tenants.id, { onDelete: "cascade" }),
18229
+ title: text("title").notNull(),
18230
+ description: text("description"),
18231
+ source: challengeSource("source").notNull().default("self"),
18232
+ metric: text("metric").notNull(),
18233
+ subjectType: challengeSubjectType("subject_type"),
18234
+ subjectId: text("subject_id"),
18235
+ target: integer2("target").notNull(),
18236
+ startAt: timestamp("start_at", { withTimezone: true }).notNull(),
18237
+ endAt: timestamp("end_at", { withTimezone: true }).notNull(),
18238
+ projectId: text("project_id").references(() => projects.id, { onDelete: "set null" }),
18239
+ createdBy: text("created_by").references(() => users.id, { onDelete: "set null" }),
18240
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
18241
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
18242
+ }, (t) => [
18243
+ index("challenges_tenant_idx").on(t.tenantId),
18244
+ index("challenges_tenant_window_idx").on(t.tenantId, t.startAt, t.endAt),
18245
+ index("challenges_tenant_subject_idx").on(t.tenantId, t.subjectType, t.subjectId),
18246
+ index("challenges_tenant_project_idx").on(t.tenantId, t.projectId),
18247
+ check2("challenges_subject_pair_chk", sql`(${t.subjectType} IS NULL) = (${t.subjectId} IS NULL)`)
18248
+ ]);
18249
+ var challengeParticipants = pgTable("challenge_participants", {
18250
+ id: text("id").primaryKey(),
18251
+ tenantId: text("tenant_id").notNull().references(() => tenants.id, { onDelete: "cascade" }),
18252
+ challengeId: text("challenge_id").notNull().references(() => challenges.id, { onDelete: "cascade" }),
18253
+ userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
18254
+ joinedAt: timestamp("joined_at", { withTimezone: true }).notNull().defaultNow()
18255
+ }, (t) => [
18256
+ unique("challenge_participants_challenge_user_uniq").on(t.challengeId, t.userId),
18257
+ index("challenge_participants_tenant_idx").on(t.tenantId),
18258
+ index("challenge_participants_user_idx").on(t.tenantId, t.userId)
18259
+ ]);
18260
+ var challengeParts = pgTable("challenge_parts", {
18261
+ id: text("id").primaryKey(),
18262
+ tenantId: text("tenant_id").notNull().references(() => tenants.id, { onDelete: "cascade" }),
18263
+ challengeId: text("challenge_id").notNull().references(() => challenges.id, { onDelete: "cascade" }),
18264
+ kind: challengePartKind("kind").notNull(),
18265
+ subjectId: text("subject_id").notNull(),
18266
+ position: integer2("position").notNull()
18267
+ }, (t) => [
18268
+ index("challenge_parts_tenant_idx").on(t.tenantId),
18269
+ index("challenge_parts_challenge_idx").on(t.challengeId, t.position),
18270
+ index("challenge_parts_tenant_subject_idx").on(t.tenantId, t.kind, t.subjectId)
18271
+ ]);
18272
+
18273
+ // ../../packages/schema/src/db/collaboration.ts
18274
+ var capability = pgEnum("collab_capability", ["view", "comment", "edit", "admin"]);
18275
+ var grantSubjectType = pgEnum("grant_subject_type", ["member", "role"]);
18276
+ var inviteType = pgEnum("invite_type", ["email", "link", "join_request", "guest"]);
18277
+ var inviteStatus = pgEnum("invite_status", ["pending", "accepted", "revoked", "expired"]);
18278
+ var permissionSets = pgTable("permission_sets", {
18279
+ id: text("id").primaryKey(),
18280
+ tenantId: text("tenant_id").notNull().references(() => tenants.id),
18281
+ name: text("name").notNull(),
18282
+ canView: boolean4("can_view").notNull().default(true),
18283
+ canComment: boolean4("can_comment").notNull().default(false),
18284
+ canEdit: boolean4("can_edit").notNull().default(false),
18285
+ canShare: boolean4("can_share").notNull().default(false),
18286
+ canManageMembers: boolean4("can_manage_members").notNull().default(false),
18287
+ canManageRoles: boolean4("can_manage_roles").notNull().default(false),
18288
+ canManageSpace: boolean4("can_manage_space").notNull().default(false),
18289
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
18290
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
18291
+ }, (t) => [unique("permission_sets_tenant_name_uniq").on(t.tenantId, t.name)]);
18292
+ var roles = pgTable("roles", {
18293
+ id: text("id").primaryKey(),
18294
+ tenantId: text("tenant_id").notNull().references(() => tenants.id),
18295
+ name: text("name").notNull(),
18296
+ description: text("description"),
18297
+ permissionSetId: text("permission_set_id").notNull().references(() => permissionSets.id),
18298
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
18299
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
18300
+ }, (t) => [unique("roles_tenant_name_uniq").on(t.tenantId, t.name)]);
18301
+ var invites = pgTable("invites", {
18302
+ id: text("id").primaryKey(),
18303
+ tenantId: text("tenant_id").notNull().references(() => tenants.id),
18304
+ type: inviteType("type").notNull(),
18305
+ email: text("email"),
18306
+ token: text("token").unique(),
18307
+ tokenHash: text("token_hash").unique(),
18308
+ tokenSealed: text("token_sealed"),
18309
+ role: baseRole("role").notNull().default("member"),
18310
+ customRoleId: text("custom_role_id").references(() => roles.id),
18311
+ invitedByUserId: text("invited_by_user_id").references(() => users.id),
18312
+ status: inviteStatus("status").notNull().default("pending"),
18313
+ accessUntil: timestamp("access_until", { withTimezone: true }),
18314
+ expiresAt: timestamp("expires_at", { withTimezone: true }),
18315
+ acceptedAt: timestamp("accepted_at", { withTimezone: true }),
18316
+ acceptedByUserId: text("accepted_by_user_id").references(() => users.id),
18317
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
18318
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
18319
+ }, (t) => [index("invites_tenant_status_idx").on(t.tenantId, t.status)]);
18320
+ var nodeGrants = pgTable("node_grants", {
18321
+ id: text("id").primaryKey(),
18322
+ tenantId: text("tenant_id").notNull().references(() => tenants.id),
18323
+ nodeId: text("node_id").notNull().references(() => nodes.id),
18324
+ subjectType: grantSubjectType("subject_type").notNull(),
18325
+ subjectId: text("subject_id").notNull(),
18326
+ capability: capability("capability").notNull(),
18327
+ assigned: boolean4("assigned").notNull().default(false),
18328
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
18329
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
18330
+ }, (t) => [
18331
+ unique("node_grants_node_subject_uniq").on(t.tenantId, t.nodeId, t.subjectType, t.subjectId),
18332
+ index("node_grants_tenant_node_idx").on(t.tenantId, t.nodeId),
18333
+ index("node_grants_node_idx").on(t.nodeId)
18334
+ ]);
18335
+ var shareLinks = pgTable("share_links", {
18336
+ id: text("id").primaryKey(),
18337
+ tenantId: text("tenant_id").notNull().references(() => tenants.id),
18338
+ nodeId: text("node_id").notNull().references(() => nodes.id),
18339
+ tokenHash: text("token_hash").notNull().unique(),
18340
+ capability: capability("capability").notNull().default("view"),
18341
+ createdByUserId: text("created_by_user_id").references(() => users.id),
18342
+ expiresAt: timestamp("expires_at", { withTimezone: true }),
18343
+ revokedAt: timestamp("revoked_at", { withTimezone: true }),
18344
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
18345
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
18346
+ }, (t) => [
18347
+ index("share_links_tenant_node_idx").on(t.tenantId, t.nodeId),
18348
+ index("share_links_node_idx").on(t.nodeId)
18349
+ ]);
18350
+ var shareLinkGuests = pgTable("share_link_guests", {
18351
+ id: text("id").primaryKey(),
18352
+ tenantId: text("tenant_id").notNull().references(() => tenants.id),
18353
+ shareLinkId: text("share_link_id").notNull().references(() => shareLinks.id, { onDelete: "cascade" }),
18354
+ name: text("name").notNull(),
18355
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
18356
+ }, (t) => [
18357
+ uniqueIndex("share_link_guests_link_name_uniq").on(t.shareLinkId, sql`lower(${t.name})`),
18358
+ index("share_link_guests_tenant_idx").on(t.tenantId)
18359
+ ]);
18360
+ var comments = pgTable("comments", {
18361
+ id: text("id").primaryKey(),
18362
+ tenantId: text("tenant_id").notNull().references(() => tenants.id),
18363
+ nodeId: text("node_id").notNull().references(() => nodes.id),
18364
+ parentId: text("parent_id").references(() => comments.id),
18365
+ authorUserId: text("author_user_id").notNull().references(() => users.id),
18366
+ body: text("body").notNull(),
18367
+ editedAt: timestamp("edited_at", { withTimezone: true }),
18368
+ deletedAt: timestamp("deleted_at", { withTimezone: true }),
18369
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
18370
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
18371
+ }, (t) => [
18372
+ index("comments_tenant_node_created_idx").on(t.tenantId, t.nodeId, t.createdAt),
18373
+ index("comments_parent_idx").on(t.parentId),
18374
+ index("comments_node_idx").on(t.nodeId)
18375
+ ]);
18376
+ var commentMentions = pgTable("comment_mentions", {
18377
+ id: text("id").primaryKey(),
18378
+ tenantId: text("tenant_id").notNull().references(() => tenants.id),
18379
+ commentId: text("comment_id").notNull().references(() => comments.id),
18380
+ mentionedUserId: text("mentioned_user_id").notNull().references(() => users.id),
18381
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
18382
+ }, (t) => [unique("comment_mentions_comment_user_uniq").on(t.commentId, t.mentionedUserId)]);
18383
+ var commentReactions = pgTable("comment_reactions", {
18384
+ id: text("id").primaryKey(),
18385
+ tenantId: text("tenant_id").notNull().references(() => tenants.id),
18386
+ commentId: text("comment_id").notNull().references(() => comments.id),
18387
+ userId: text("user_id").notNull().references(() => users.id),
18388
+ emoji: text("emoji").notNull(),
18389
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
18390
+ }, (t) => [unique("comment_reactions_comment_user_emoji_uniq").on(t.commentId, t.userId, t.emoji)]);
18391
+ var activityEvents = pgTable("activity_events", {
18392
+ id: text("id").primaryKey(),
18393
+ tenantId: text("tenant_id").notNull().references(() => tenants.id, { onDelete: "cascade" }),
18394
+ actorUserId: text("actor_user_id").references(() => users.id, { onDelete: "set null" }),
18395
+ actorAgentId: text("actor_agent_id"),
18396
+ action: text("action").notNull(),
18397
+ nodeId: text("node_id"),
18398
+ subjectType: text("subject_type"),
18399
+ subjectId: text("subject_id"),
18400
+ metadata: text("metadata"),
18401
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
18402
+ }, (t) => [
18403
+ index("activity_events_tenant_created_idx").on(t.tenantId, t.createdAt),
18404
+ index("activity_events_tenant_node_idx").on(t.tenantId, t.nodeId)
18405
+ ]);
18406
+
18373
18407
  // ../../packages/schema/src/db/contacts.ts
18374
18408
  var contactKind = pgEnum("contact_kind", ["person", "organization"]);
18375
18409
  var contacts = pgTable("contacts", {
@@ -18995,6 +19029,9 @@ var areaColorSchema = _enum2(areaColor.enumValues);
18995
19029
  var reviewCadenceSchema = _enum2(reviewCadence.enumValues);
18996
19030
  var projectStatusSchema = _enum2(projectStatus.enumValues);
18997
19031
  var taskStatusSchema = _enum2(taskStatus.enumValues);
19032
+ var workPrioritySchema = _enum2(workPriority.enumValues);
19033
+ var OPEN_TASK_STATUSES = ["backlog", "todo", "in_progress", "waiting"];
19034
+ var BOARD_TASK_STATUSES = [...OPEN_TASK_STATUSES, "done"];
18998
19035
  var routineKindSchema = _enum2(routineKind.enumValues);
18999
19036
  var routineStepRefSchema = _enum2(routineStepRef.enumValues);
19000
19037
  var routineRunStatusSchema = _enum2(routineRunStatus.enumValues);
@@ -19387,7 +19424,13 @@ var spaceSchema = createSelectSchema(tenants, {
19387
19424
  createdAt: exports_coerce.date(),
19388
19425
  updatedAt: exports_coerce.date(),
19389
19426
  deletedAt: exports_coerce.date().nullable()
19390
- }).omit({ gaardHouseUrl: true, awayFrom: true, awayUntil: true });
19427
+ }).omit({
19428
+ gaardHouseUrl: true,
19429
+ awayFrom: true,
19430
+ awayUntil: true,
19431
+ lastTaskNumber: true,
19432
+ lastProjectNumber: true
19433
+ });
19391
19434
  var backdropIdSchema = string2().trim().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "a backdrop id is lowercase words joined by dashes").max(40);
19392
19435
  var updateSpaceInputSchema = object({
19393
19436
  name: string2().trim().min(1).max(120).optional(),
@@ -21598,6 +21641,17 @@ var noteMadeTaskSchema = object({
21598
21641
  createdAt: exports_coerce.date(),
21599
21642
  by: noteActorSchema
21600
21643
  }).strict();
21644
+ var noteChecklistItemSchema = object({
21645
+ line: number2().int().nonnegative(),
21646
+ taskId: ulidSchema.nullable(),
21647
+ title: string2(),
21648
+ status: string2().nullable(),
21649
+ done: boolean2(),
21650
+ scheduledOn: exports_coerce.date().nullable(),
21651
+ completedAt: exports_coerce.date().nullable(),
21652
+ projectId: ulidSchema.nullable(),
21653
+ projectTitle: string2().nullable()
21654
+ }).strict();
21601
21655
  var noteDetailsSchema = object({
21602
21656
  noteId: ulidSchema,
21603
21657
  links: array(noteLinkedNodeSchema),
@@ -21606,9 +21660,66 @@ var noteDetailsSchema = object({
21606
21660
  createdBy: noteActorSchema,
21607
21661
  editedBy: noteActorSchema,
21608
21662
  seenBy: array(string2()),
21609
- madeTasks: array(noteMadeTaskSchema)
21663
+ madeTasks: array(noteMadeTaskSchema),
21664
+ checklist: array(noteChecklistItemSchema).default([])
21610
21665
  }).strict();
21611
21666
 
21667
+ // ../../packages/schema/src/db/project-updates.ts
21668
+ var projectUpdateStatus = pgEnum("project_update_status", [
21669
+ "on_track",
21670
+ "at_risk",
21671
+ "off_track",
21672
+ "milestone_done",
21673
+ "done"
21674
+ ]);
21675
+ var PROJECT_UPDATE_BODY_MAX = 2000;
21676
+ var projectUpdates = pgTable("project_updates", {
21677
+ id: text("id").primaryKey(),
21678
+ tenantId: text("tenant_id").notNull().references(() => tenants.id),
21679
+ projectId: text("project_id").notNull().references(() => projects.id, { onDelete: "cascade" }),
21680
+ status: projectUpdateStatus("status").notNull(),
21681
+ body: text("body").notNull().default(""),
21682
+ authorUserId: text("author_user_id").references(() => users.id),
21683
+ authorAgentId: text("author_agent_id").references(() => agents.id),
21684
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
21685
+ }, (t) => [
21686
+ index("project_updates_project_created_idx").on(t.tenantId, t.projectId, t.createdAt),
21687
+ check2("project_updates_one_author_chk", sql`(${t.authorUserId} IS NULL) <> (${t.authorAgentId} IS NULL)`),
21688
+ check2("project_updates_body_len_chk", sql`char_length(${t.body}) <= 2000`)
21689
+ ]);
21690
+ var projectLeads = pgTable("project_leads", {
21691
+ id: text("id").primaryKey(),
21692
+ tenantId: text("tenant_id").notNull().references(() => tenants.id),
21693
+ projectId: text("project_id").notNull().references(() => projects.id, { onDelete: "cascade" }),
21694
+ userId: text("user_id").notNull().references(() => users.id),
21695
+ position: integer2("position").notNull().default(0)
21696
+ }, (t) => [
21697
+ index("project_leads_project_idx").on(t.tenantId, t.projectId, t.position),
21698
+ unique("project_leads_project_user_uniq").on(t.projectId, t.userId),
21699
+ index("project_leads_user_idx").on(t.userId)
21700
+ ]);
21701
+
21702
+ // ../../packages/schema/src/contracts/work-refs.ts
21703
+ var WORK_REF_PREFIX = "DO";
21704
+ var REF_RE = new RegExp(`^${WORK_REF_PREFIX}-(P)?([1-9]\\d{0,8})$`, "i");
21705
+ function parseWorkRef(raw) {
21706
+ const m = REF_RE.exec(raw.trim());
21707
+ if (!m)
21708
+ return null;
21709
+ return { kind: m[1] ? "project" : "task", number: Number(m[2]) };
21710
+ }
21711
+
21712
+ // ../../packages/schema/src/contracts/task-blocks.ts
21713
+ var taskIdOrRefSchema = string2().trim().refine((raw) => ulidSchema.safeParse(raw).success || parseWorkRef(raw)?.kind === "task", "a task's ULID or its ref, like DO-147");
21714
+ var addTaskBlockerInputSchema = object({ taskId: taskIdOrRefSchema }).strict();
21715
+ var taskBlockerSchema = object({
21716
+ id: ulidSchema,
21717
+ ref: string2().nullable(),
21718
+ title: string2(),
21719
+ status: taskStatusSchema,
21720
+ dueAt: exports_coerce.date().nullable()
21721
+ });
21722
+
21612
21723
  // ../../packages/schema/src/contracts/task-source.ts
21613
21724
  var taskSourceSchema = object({
21614
21725
  kind: string2().trim().min(1).max(40),
@@ -21763,6 +21874,9 @@ var worktreeLinkListQuerySchema = object({
21763
21874
 
21764
21875
  // ../../packages/schema/src/contracts/tasks.ts
21765
21876
  var taskSchema = createSelectSchema(tasks, {
21877
+ priority: workPrioritySchema.default("none"),
21878
+ number: number2().int().nonnegative().default(0),
21879
+ deliverable: string2().nullable().default(null),
21766
21880
  scheduledAt: exports_coerce.date().nullable(),
21767
21881
  startAt: exports_coerce.date().nullable(),
21768
21882
  dueAt: exports_coerce.date().nullable(),
@@ -21832,6 +21946,7 @@ var taskFilingSuggestionSchema = object({
21832
21946
  });
21833
21947
  var completedAtInputSchema = exports_coerce.date().refine((d) => d.getTime() <= Date.now() + 60000, "completedAt can't be in the future").nullish();
21834
21948
  var taskNextStepSchema = string2().trim().min(1).max(500);
21949
+ var taskDeliverableSchema = string2().trim().min(1).max(500);
21835
21950
  var taskViewSchema = taskSchema.omit({
21836
21951
  ownerUserId: true,
21837
21952
  ownerAgentId: true,
@@ -21847,6 +21962,7 @@ var taskViewSchema = taskSchema.omit({
21847
21962
  movedFrom: true
21848
21963
  }).extend({
21849
21964
  title: string2(),
21965
+ ref: string2().nullable().default(null),
21850
21966
  parentId: ulidSchema.nullable(),
21851
21967
  sortOrder: number2().int().nonnegative().default(0),
21852
21968
  createdAt: exports_coerce.date(),
@@ -21862,7 +21978,10 @@ var taskViewSchema = taskSchema.omit({
21862
21978
  nextStep: string2().nullable().default(null),
21863
21979
  completedBy: actorRefSchema.nullable().default(null),
21864
21980
  worktreeLink: worktreeLinkSchema.nullable().default(null),
21865
- filingSuggestion: taskFilingSuggestionSchema.nullable().default(null)
21981
+ filingSuggestion: taskFilingSuggestionSchema.nullable().default(null),
21982
+ blockedBy: array(taskBlockerSchema).default([]),
21983
+ blocks: number2().int().nonnegative().default(0),
21984
+ isBlocked: boolean2().default(false)
21866
21985
  });
21867
21986
  var taskProjectIdSchema = ulidSchema.nullish();
21868
21987
  var taskAreaIdSchema = ulidSchema.nullish();
@@ -21878,9 +21997,11 @@ var createTaskInputSchema = createInsertSchema(tasks, {
21878
21997
  areaId: taskAreaIdSchema,
21879
21998
  placeId: ulidSchema.nullish(),
21880
21999
  milestoneId: ulidSchema.nullish(),
21881
- somedayAt: exports_coerce.date().nullish()
22000
+ somedayAt: exports_coerce.date().nullish(),
22001
+ deliverable: taskDeliverableSchema.nullish()
21882
22002
  }).omit({
21883
22003
  tenantId: true,
22004
+ number: true,
21884
22005
  recurrenceRuleId: true,
21885
22006
  startAt: true,
21886
22007
  ownerUserId: true,
@@ -21928,10 +22049,12 @@ var updateTaskInputSchema = createUpdateSchema(tasks, {
21928
22049
  areaId: taskAreaIdSchema,
21929
22050
  placeId: ulidSchema.nullish(),
21930
22051
  milestoneId: ulidSchema.nullish(),
21931
- somedayAt: exports_coerce.date().nullish()
22052
+ somedayAt: exports_coerce.date().nullish(),
22053
+ deliverable: taskDeliverableSchema.nullish()
21932
22054
  }).omit({
21933
22055
  id: true,
21934
22056
  tenantId: true,
22057
+ number: true,
21935
22058
  recurrenceRuleId: true,
21936
22059
  startAt: true,
21937
22060
  ownerUserId: true,
@@ -21971,6 +22094,8 @@ var taskListFilterSchema = _enum2(["all", "unsorted", "starred"]);
21971
22094
 
21972
22095
  // ../../packages/schema/src/contracts/projects.ts
21973
22096
  var projectSchema = createSelectSchema(projects, {
22097
+ priority: workPrioritySchema.default("none"),
22098
+ number: number2().int().nonnegative().default(0),
21974
22099
  icon: string2().nullable().default(null),
21975
22100
  startAt: exports_coerce.date().nullable(),
21976
22101
  dueAt: exports_coerce.date().nullable(),
@@ -21998,7 +22123,13 @@ var createProjectInputSchema = createInsertSchema(projects, {
21998
22123
  dueAt: exports_coerce.date().nullish(),
21999
22124
  areaId: ulidSchema.nullish(),
22000
22125
  icon: projectIconSchema.nullish()
22001
- }).omit({ tenantId: true, completedAt: true, finishProposedAt: true, finishNotYetAt: true }).extend({
22126
+ }).omit({
22127
+ tenantId: true,
22128
+ number: true,
22129
+ completedAt: true,
22130
+ finishProposedAt: true,
22131
+ finishNotYetAt: true
22132
+ }).extend({
22002
22133
  title: string2().min(1),
22003
22134
  parentId: ulidSchema.nullish(),
22004
22135
  milestones: array(projectMilestoneInputSchema).optional(),
@@ -22010,15 +22141,39 @@ var updateProjectInputSchema = createUpdateSchema(projects, {
22010
22141
  dueAt: exports_coerce.date().nullish(),
22011
22142
  areaId: ulidSchema.nullish(),
22012
22143
  icon: projectIconSchema.nullish()
22013
- }).omit({ id: true, tenantId: true, completedAt: true, finishProposedAt: true }).extend({
22144
+ }).omit({ id: true, tenantId: true, number: true, completedAt: true, finishProposedAt: true }).extend({
22014
22145
  title: string2().min(1).optional(),
22015
22146
  finishNotYetAt: exports_coerce.date().nullish(),
22016
22147
  parentId: ulidSchema.nullish(),
22017
22148
  milestones: array(projectMilestoneInputSchema).optional(),
22018
- notes: string2().nullish()
22149
+ notes: string2().nullish(),
22150
+ leadUserIds: array(ulidSchema).max(20).refine((ids) => new Set(ids).size === ids.length, "each lead once").optional()
22151
+ }).strict();
22152
+ var projectUpdateStatusSchema = _enum2(projectUpdateStatus.enumValues);
22153
+ var projectUpdateViewSchema = object({
22154
+ id: ulidSchema,
22155
+ projectId: ulidSchema,
22156
+ status: projectUpdateStatusSchema,
22157
+ body: string2(),
22158
+ authorUserId: string2().nullable(),
22159
+ authorAgentId: string2().nullable(),
22160
+ authorName: string2(),
22161
+ createdAt: exports_coerce.date()
22162
+ });
22163
+ var createProjectUpdateInputSchema = object({
22164
+ id: ulidSchema.optional(),
22165
+ status: projectUpdateStatusSchema,
22166
+ body: string2().trim().max(PROJECT_UPDATE_BODY_MAX).default("")
22019
22167
  }).strict();
22168
+ var projectLatestUpdateSchema = object({
22169
+ id: ulidSchema,
22170
+ status: projectUpdateStatusSchema,
22171
+ createdAt: exports_coerce.date()
22172
+ });
22173
+ var projectLeadSchema = object({ userId: ulidSchema, name: string2() });
22020
22174
  var projectViewSchema = projectSchema.extend({
22021
22175
  title: string2(),
22176
+ ref: string2().nullable().default(null),
22022
22177
  parentId: ulidSchema.nullable(),
22023
22178
  createdAt: exports_coerce.date(),
22024
22179
  updatedAt: exports_coerce.date(),
@@ -22029,7 +22184,9 @@ var projectViewSchema = projectSchema.extend({
22029
22184
  taskCount: number2().int().min(0),
22030
22185
  completedTaskCount: number2().int().min(0),
22031
22186
  worktreeLink: worktreeLinkSchema.nullable().default(null),
22032
- milestones: array(projectMilestoneSchema).default([])
22187
+ milestones: array(projectMilestoneSchema).default([]),
22188
+ leads: array(projectLeadSchema).default([]),
22189
+ latestUpdate: projectLatestUpdateSchema.nullable().default(null)
22033
22190
  });
22034
22191
  var projectDetailSchema = projectViewSchema.extend({
22035
22192
  milestones: array(projectMilestoneSchema),
@@ -22205,10 +22362,18 @@ var routineRunSchema = createSelectSchema(routineRuns, {
22205
22362
  completedAt: exports_coerce.date().nullable()
22206
22363
  });
22207
22364
  var routineRunStepSchema = createSelectSchema(routineRunSteps, {
22208
- completedAt: exports_coerce.date().nullable()
22365
+ completedAt: exports_coerce.date().nullable(),
22366
+ taskId: string2().nullable().default(null),
22367
+ reason: string2().nullable().default(null)
22209
22368
  });
22210
22369
  var routineRunStepViewSchema = routineRunStepSchema.extend({
22211
- habit: habitSummarySchema.nullable().default(null)
22370
+ habit: habitSummarySchema.nullable().default(null),
22371
+ task: object({
22372
+ id: string2(),
22373
+ title: string2(),
22374
+ status: string2(),
22375
+ dueAt: exports_coerce.date().nullable()
22376
+ }).nullable().default(null)
22212
22377
  });
22213
22378
  var routineRunViewSchema = routineRunSchema.extend({
22214
22379
  routineTitle: string2(),
@@ -22282,6 +22447,44 @@ var routineRhythmResultSchema = object({
22282
22447
  routines: array(routineRhythmSchema),
22283
22448
  notices: array(routineNoticeSchema)
22284
22449
  });
22450
+ var addRoutineTaskStepInputSchema = object({
22451
+ taskId: ulidSchema,
22452
+ reason: string2().min(1).max(280).optional()
22453
+ }).strict();
22454
+ var reorderRoutineStepsInputSchema = object({
22455
+ stepIds: array(ulidSchema).min(1).max(200).refine((ids) => new Set(ids).size === ids.length, "stepIds must not repeat")
22456
+ }).strict();
22457
+ var routineTodayStepKindSchema = _enum2(["step", "habit", "task"]);
22458
+ var routineTodayStepSchema = object({
22459
+ n: number2().int().positive(),
22460
+ title: string2(),
22461
+ kind: routineTodayStepKindSchema,
22462
+ meta: string2().nullable(),
22463
+ minutes: number2().int().nullable(),
22464
+ done: boolean2()
22465
+ });
22466
+ var routineTodayStateSchema = _enum2(["done", "running", "upcoming", "resting"]);
22467
+ var routineTodaySchema = object({
22468
+ id: string2(),
22469
+ title: string2(),
22470
+ icon: string2().nullable(),
22471
+ start: string2().nullable(),
22472
+ durationMinutes: number2().int().nonnegative(),
22473
+ state: routineTodayStateSchema,
22474
+ run: object({
22475
+ id: string2(),
22476
+ startedAt: exports_coerce.date(),
22477
+ completedAt: exports_coerce.date().nullable(),
22478
+ done: number2().int().nonnegative(),
22479
+ total: number2().int().nonnegative()
22480
+ }).nullable(),
22481
+ steps: array(routineTodayStepSchema),
22482
+ rhythm: array(routineDayStateSchema).length(14)
22483
+ });
22484
+ var routinesTodayResultSchema = object({
22485
+ today: ymdSchema,
22486
+ routines: array(routineTodaySchema)
22487
+ });
22285
22488
 
22286
22489
  // ../../packages/schema/src/contracts/areas.ts
22287
22490
  var areaPurposeSchema = string2().trim().max(140).transform((v) => v.length === 0 ? null : v);
@@ -22505,7 +22708,8 @@ var collectionTypeValues = [
22505
22708
  "place",
22506
22709
  "wine",
22507
22710
  "wardrobe",
22508
- "asset"
22711
+ "asset",
22712
+ "hobby"
22509
22713
  ];
22510
22714
  var collectionTypeSchema = _enum2(collectionTypeValues);
22511
22715
  var collectionViewValues = ["grid", "shelf", "map"];
@@ -22719,6 +22923,8 @@ var taskListQuerySchema = object({
22719
22923
  areaId: ulidSchema.optional(),
22720
22924
  projectId: ulidSchema.optional(),
22721
22925
  status: csvList(taskStatusSchema).optional(),
22926
+ priority: csvList(workPrioritySchema).optional(),
22927
+ view: ulidSchema.optional(),
22722
22928
  tag: tagListSchema.optional(),
22723
22929
  updatedSince: instantQuerySchema.optional(),
22724
22930
  dueBefore: instantQuerySchema.optional(),
@@ -24082,7 +24288,7 @@ var calendarOperations = defineOperations({
24082
24288
  summary: "Push to the calendar now",
24083
24289
  description: "Reconcile do's placements (timed tasks, deadlines, Time blocks, timed routine occurrences) onto do's own calendar in each of the member's usable connections, now rather than at the next sweep. Idempotent: a settled calendar is all `skipped`. A connection that needs reconnecting is left out. Needs a key that acts for a member.",
24084
24290
  status: 200,
24085
- response: { description: "One push result per connection.", list: true },
24291
+ response: { description: "`{ pushed }`: one push result per connection." },
24086
24292
  errors: { 400: true }
24087
24293
  },
24088
24294
  refresh: {
@@ -29102,6 +29308,32 @@ var guestListOverviewSchema = object({
29102
29308
  invites: number2().int()
29103
29309
  })
29104
29310
  });
29311
+ var guestListGuestSchema = guestViewSchema.omit({ diet: true });
29312
+ var guestListHouseholdSchema = object({
29313
+ householdId: ulidSchema.nullable(),
29314
+ title: string2(),
29315
+ address: householdAddressSchema.nullable(),
29316
+ addressConfirmedAt: exports_coerce.date().nullable(),
29317
+ language: rsvpLanguageSchema,
29318
+ group: boolean2(),
29319
+ catererNotes: number2().int().min(0),
29320
+ guests: array(guestListGuestSchema)
29321
+ });
29322
+ var guestListSummarySchema = object({
29323
+ invited: number2().int(),
29324
+ households: number2().int(),
29325
+ coming: number2().int(),
29326
+ waiting: number2().int(),
29327
+ notComing: number2().int(),
29328
+ tables: number2().int(),
29329
+ notSeated: number2().int(),
29330
+ noAddress: number2().int()
29331
+ });
29332
+ var guestListHouseholdsViewSchema = object({
29333
+ guestList: guestListViewSchema,
29334
+ summary: guestListSummarySchema,
29335
+ households: array(guestListHouseholdSchema)
29336
+ });
29105
29337
  var rsvpDigestQuerySchema = object({ since: exports_coerce.date().optional() }).strict();
29106
29338
  var rsvpDigestSchema = object({
29107
29339
  guestListId: string2(),
@@ -30733,6 +30965,21 @@ var housePlaceTileSchema = object({
30733
30965
  gardenFeatureId: string2().nullable(),
30734
30966
  thingCount: number2().int().min(0)
30735
30967
  });
30968
+ var houseLowSchema = object({
30969
+ id: string2(),
30970
+ title: string2(),
30971
+ placeTitle: string2().nullable(),
30972
+ amountWords: string2(),
30973
+ state: _enum2(["low", "fine"])
30974
+ });
30975
+ var houseLoanRowSchema = object({
30976
+ itemId: string2(),
30977
+ title: string2(),
30978
+ direction: _enum2(["lent", "borrowed"]),
30979
+ who: string2(),
30980
+ since: day,
30981
+ dueBack: day.nullable()
30982
+ });
30736
30983
  var houseOverviewSchema = object({
30737
30984
  broken: array(houseBrokenSchema),
30738
30985
  upkeepSoon: array(houseUpkeepSoonSchema),
@@ -30742,7 +30989,14 @@ var houseOverviewSchema = object({
30742
30989
  toolCount: number2().int().min(0),
30743
30990
  animalCount: number2().int().min(0),
30744
30991
  toConfirm: number2().int().min(0).default(0),
30745
- toConfirmTitles: array(string2()).max(3).default([])
30992
+ toConfirmTitles: array(string2()).max(3).default([]),
30993
+ low: array(houseLowSchema).max(5).default([]),
30994
+ loans: array(houseLoanRowSchema).max(5).default([]),
30995
+ lowCount: number2().int().min(0).default(0),
30996
+ lentCount: number2().int().min(0).default(0),
30997
+ borrowedCount: number2().int().min(0).default(0),
30998
+ upkeepThisMonth: number2().int().min(0).default(0),
30999
+ brokenWaitingCount: number2().int().min(0).default(0)
30746
31000
  });
30747
31001
  var upkeepScheduleSchema = object({
30748
31002
  every: number2().int().min(1).max(365),
@@ -31066,6 +31320,166 @@ var householdOperations = defineOperations({
31066
31320
  })
31067
31321
  });
31068
31322
 
31323
+ // ../../packages/schema/src/contracts/inbox.ts
31324
+ var INBOX_KINDS = ["capture", "proposal", "answer"];
31325
+ var inboxKindSchema = _enum2(INBOX_KINDS);
31326
+ var inboxFilterSchema = _enum2(["all", ...INBOX_KINDS]);
31327
+ var inboxQuerySchema = object({
31328
+ kind: inboxFilterSchema.default("all")
31329
+ });
31330
+ var inboxIconSchema = _enum2(["mic", "edit", "camera", "echo", "agents", "bell"]);
31331
+ var inboxHueSchema = _enum2([
31332
+ "sage",
31333
+ "sky",
31334
+ "peach",
31335
+ "plum",
31336
+ "sun",
31337
+ "sea",
31338
+ "clay",
31339
+ "mist"
31340
+ ]);
31341
+ var inboxActionSchema = object({
31342
+ id: string2(),
31343
+ label: string2(),
31344
+ key: string2().length(1),
31345
+ primary: boolean2(),
31346
+ route: string2().nullable().default(null)
31347
+ });
31348
+ var inboxItemSchema = object({
31349
+ id: ulidSchema,
31350
+ kind: inboxKindSchema,
31351
+ title: string2(),
31352
+ source: string2(),
31353
+ meta: string2(),
31354
+ icon: inboxIconSchema,
31355
+ hue: inboxHueSchema,
31356
+ createdAt: exports_coerce.date(),
31357
+ actions: array(inboxActionSchema)
31358
+ });
31359
+ var inboxCountsSchema = object({
31360
+ all: number2().int().nonnegative(),
31361
+ capture: number2().int().nonnegative(),
31362
+ proposal: number2().int().nonnegative(),
31363
+ answer: number2().int().nonnegative()
31364
+ });
31365
+ var inboxResponseSchema = object({
31366
+ counts: inboxCountsSchema,
31367
+ items: array(inboxItemSchema)
31368
+ });
31369
+ var INBOX_SORT_AS = ["task", "note", "habit", "log"];
31370
+ var inboxSortAsSchema = _enum2(INBOX_SORT_AS);
31371
+ var inboxFactSchema = object({ key: string2(), value: string2() });
31372
+ var inboxSortingSchema = object({
31373
+ id: ulidSchema,
31374
+ original: object({
31375
+ how: string2(),
31376
+ at: exports_coerce.date(),
31377
+ text: string2()
31378
+ }),
31379
+ becomes: inboxSortAsSchema,
31380
+ facts: array(inboxFactSchema),
31381
+ why: string2().nullable()
31382
+ });
31383
+ var inboxTaskFieldsSchema = updateTaskInputSchema.pick({
31384
+ title: true,
31385
+ notes: true,
31386
+ areaId: true,
31387
+ projectId: true,
31388
+ scheduledAt: true,
31389
+ allDay: true,
31390
+ dueAt: true,
31391
+ somedayAt: true,
31392
+ tags: true,
31393
+ owner: true
31394
+ });
31395
+ var inboxNoteFieldsSchema = createNoteInputSchema.pick({ areaId: true, body: true, tags: true }).extend({ title: string2().min(1).optional() }).strict();
31396
+ var inboxHabitFieldsSchema = createHabitInputSchema.pick({ areaId: true, period: true, frequencyCount: true, timeOfDay: true }).partial().extend({ title: string2().min(1).optional() }).strict();
31397
+ var inboxSortInputSchema = discriminatedUnion("as", [
31398
+ object({ as: literal("task"), fields: inboxTaskFieldsSchema.optional() }).strict(),
31399
+ object({ as: literal("note"), fields: inboxNoteFieldsSchema.optional() }).strict(),
31400
+ object({
31401
+ as: literal("habit"),
31402
+ fields: inboxHabitFieldsSchema.optional()
31403
+ }).strict(),
31404
+ object({
31405
+ as: literal("log"),
31406
+ fields: record(string2(), unknown()).optional()
31407
+ }).strict()
31408
+ ]);
31409
+ var inboxSortResultSchema = object({
31410
+ id: ulidSchema,
31411
+ as: inboxSortAsSchema,
31412
+ homeId: ulidSchema
31413
+ });
31414
+ var inboxLetGoResultSchema = object({
31415
+ id: ulidSchema,
31416
+ status: literal("cancelled")
31417
+ });
31418
+
31419
+ // ../../packages/schema/src/operations/inbox.ts
31420
+ var ID = "The capture's id (an Unsorted task).";
31421
+ var inboxOperations = defineOperations({
31422
+ list: {
31423
+ method: "get",
31424
+ path: "/inbox",
31425
+ scopes: ["tasks:read"],
31426
+ tag: "Tasks",
31427
+ summary: "The Inbox",
31428
+ description: "Everything that waits on you, in one list, oldest first: Unsorted captures (open, top-level tasks with no place yet), the agent's proposals (proposed tasks not set aside with Not now) and the key's member's notifications that still need an answer (unanswered, not expired, carrying buttons). Each item has exactly one primary action with its key, plus a secondary where its kind has one: a capture `File (e)`, a proposal `Approve (a)` + `Decline (d)`, an answer its own button + `Remind me (z)` when it carries one. `counts` is always the whole Inbox, whatever `kind` filters. A machine-only key has no notifications, so no answers.",
31429
+ query: [
31430
+ {
31431
+ name: "kind",
31432
+ description: "`all` (default), `capture`, `proposal` or `answer`.",
31433
+ enum: ["all", "capture", "proposal", "answer"]
31434
+ }
31435
+ ],
31436
+ querySchema: inboxQuerySchema,
31437
+ status: 200,
31438
+ response: { description: "The Inbox.", view: "InboxResponse" },
31439
+ errors: { 400: true }
31440
+ },
31441
+ sorting: {
31442
+ method: "get",
31443
+ path: "/inbox/{id}/sorting",
31444
+ scopes: ["tasks:read"],
31445
+ tag: "Tasks",
31446
+ summary: "Sort a capture: the suggestion",
31447
+ description: "The sorting pane for one capture: how and when it came in and what was said, what it becomes (a Task unless it reads as an idea, then a Note), and the facts the agent filled in from its filing suggestion (Goes to, Status, Scheduled, Labels, Owner), with `why` in a line. 404 when the id is no capture in the Inbox.",
31448
+ pathParams: { id: ID },
31449
+ status: 200,
31450
+ response: { description: "The sorting suggestion.", view: "InboxSorting" },
31451
+ errors: { 404: true }
31452
+ },
31453
+ sort: {
31454
+ method: "post",
31455
+ path: "/inbox/{id}/sort",
31456
+ scopes: ["tasks:write"],
31457
+ tag: "Tasks",
31458
+ summary: "Sort a capture",
31459
+ description: "File the capture into its home and out of the Inbox. `task`: it stays the same task, filed with `fields` (absent ones from the agent's suggestion: Area or Project, Owner, the planned day), and needs a place (an Area, Project, day, Deadline or Someday); `note`: a Note with the capture's title and notes (needs `notes:write` too); `habit`: a daily yes/no Habit by the capture's title (needs `habits:write` too). A capture that became a Note or Habit is removed from Tasks, so it never counts as Landed. `log` is 400 until the Log something writer lands.",
31460
+ pathParams: { id: ID },
31461
+ body: inboxSortInputSchema,
31462
+ status: 201,
31463
+ response: { description: "Where it went.", view: "InboxSortResult" },
31464
+ errors: { 400: true, 403: true, 404: true }
31465
+ },
31466
+ letGo: {
31467
+ method: "post",
31468
+ path: "/inbox/{id}/let-go",
31469
+ scopes: ["tasks:write"],
31470
+ tag: "Tasks",
31471
+ summary: "Let a capture go",
31472
+ description: 'Drop the capture: it is cancelled (an ending, not a delete; undo with `PATCH /tasks/{id}` `{ status: "todo" }`), leaves the Inbox and never counts as Landed. 404 when the id is no capture in the Inbox.',
31473
+ pathParams: { id: ID },
31474
+ status: 201,
31475
+ response: {
31476
+ description: "The capture, cancelled.",
31477
+ view: "InboxLetGoResult"
31478
+ },
31479
+ errors: { 404: true }
31480
+ }
31481
+ });
31482
+
31069
31483
  // ../../packages/schema/src/db/incidents.ts
31070
31484
  var incidentKind = pgEnum("incident_kind", [
31071
31485
  "water",
@@ -31562,6 +31976,13 @@ var journalLinkSchema = object({
31562
31976
  title: string2(),
31563
31977
  type: string2()
31564
31978
  });
31979
+ var JOURNAL_NOTES_WRITTEN_MAX = 5;
31980
+ var journalNoteWrittenSchema = object({
31981
+ id: string2(),
31982
+ title: string2(),
31983
+ tags: array(string2()),
31984
+ updatedAt: exports_coerce.date()
31985
+ });
31565
31986
  var journalSnapshotSchema = object({
31566
31987
  date: journalDateSchema,
31567
31988
  timezone: string2().min(1),
@@ -31571,7 +31992,9 @@ var journalSnapshotSchema = object({
31571
31992
  habits: array(journalHabitSchema),
31572
31993
  events: array(journalEventSchema),
31573
31994
  links: array(journalLinkSchema),
31574
- files: array(fileViewSchema)
31995
+ files: array(fileViewSchema),
31996
+ tasksLeft: number2().int().nonnegative().default(0),
31997
+ notesWritten: array(journalNoteWrittenSchema).max(JOURNAL_NOTES_WRITTEN_MAX).default([])
31575
31998
  });
31576
31999
  var JOURNAL_RANGE_MAX_DAYS = 366;
31577
32000
  var journalRangeQuerySchema = object({ from: journalDateSchema, to: journalDateSchema }).refine((q) => q.from <= q.to, { message: "from must be on or before to", path: ["to"] }).refine((q) => (Date.parse(`${q.to}T00:00:00Z`) - Date.parse(`${q.from}T00:00:00Z`)) / 86400000 < JOURNAL_RANGE_MAX_DAYS, { message: `a range covers at most ${JOURNAL_RANGE_MAX_DAYS} days`, path: ["to"] });
@@ -31589,7 +32012,8 @@ var journalDayListItemSchema = object({
31589
32012
  links: array(journalLinkSchema).max(3),
31590
32013
  source: journalDaySourceSchema.nullable().default(null),
31591
32014
  landed: number2().int().nonnegative().default(0),
31592
- habitsDone: number2().int().nonnegative().default(0)
32015
+ habitsDone: number2().int().nonnegative().default(0),
32016
+ words: number2().int().nonnegative().default(0)
31593
32017
  });
31594
32018
  var journalDayListSchema = object({
31595
32019
  from: journalDateSchema,
@@ -32852,6 +33276,145 @@ var krakenLinkOperations = defineOperations({
32852
33276
  }
32853
33277
  });
32854
33278
 
33279
+ // ../../packages/schema/src/db/log-kinds.ts
33280
+ var LOG_DESTINATIONS = ["habits", "animals", "health", "journal"];
33281
+ var logKinds = pgTable("log_kinds", {
33282
+ id: text("id").primaryKey(),
33283
+ tenantId: text("tenant_id").notNull().references(() => tenants.id),
33284
+ word: text("word").notNull(),
33285
+ destination: text("destination", { enum: LOG_DESTINATIONS }).notNull(),
33286
+ likeKind: text("like_kind"),
33287
+ targetId: text("target_id"),
33288
+ createdBy: text("created_by").references(() => users.id, { onDelete: "set null" }),
33289
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
33290
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
33291
+ }, (t) => [unique("log_kinds_tenant_word_uniq").on(t.tenantId, t.word)]);
33292
+ var logEntries = pgTable("log_entries", {
33293
+ id: text("id").primaryKey(),
33294
+ tenantId: text("tenant_id").notNull().references(() => tenants.id),
33295
+ userId: text("user_id").references(() => users.id, { onDelete: "set null" }),
33296
+ kind: text("kind").notNull(),
33297
+ text: text("text").notNull(),
33298
+ amount: doublePrecision("amount"),
33299
+ unit: text("unit"),
33300
+ destination: text("destination", { enum: LOG_DESTINATIONS }).notNull(),
33301
+ targetId: text("target_id"),
33302
+ refId: text("ref_id"),
33303
+ what: text("what").notNull(),
33304
+ whereLabel: text("where_label").notNull(),
33305
+ occurredAt: timestamp("occurred_at", { withTimezone: true }).notNull(),
33306
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
33307
+ }, (t) => [index("log_entries_tenant_occurred_idx").on(t.tenantId, t.occurredAt)]);
33308
+
33309
+ // ../../packages/schema/src/contracts/log-entry.ts
33310
+ var logDestinationSchema = _enum2(LOG_DESTINATIONS);
33311
+ var logWordSchema = string2().trim().toLowerCase().min(2).max(40).regex(/^\p{L}[\p{L}\p{N}'-]*$/u, "one word");
33312
+ var lineSchema = string2().trim().min(1).max(500);
33313
+ var logPreviewInputSchema = object({ text: lineSchema }).strict();
33314
+ var logWriteInputSchema = object({
33315
+ text: lineSchema,
33316
+ kind: string2().trim().toLowerCase().min(1).max(40).optional(),
33317
+ destination: logDestinationSchema.optional()
33318
+ }).strict();
33319
+ var logDayQuerySchema = object({ day: exports_iso.date().optional() });
33320
+ var logPreviewSchema = object({
33321
+ kind: string2().nullable(),
33322
+ word: string2().nullable(),
33323
+ amount: number2().nullable(),
33324
+ unit: string2().nullable(),
33325
+ label: string2(),
33326
+ destination: logDestinationSchema.nullable(),
33327
+ goesTo: string2().nullable(),
33328
+ effect: string2().nullable(),
33329
+ icon: string2(),
33330
+ needsKind: boolean2()
33331
+ });
33332
+ var logEntryViewSchema = object({
33333
+ id: string2(),
33334
+ at: string2(),
33335
+ kind: string2(),
33336
+ what: string2(),
33337
+ where: string2(),
33338
+ destination: logDestinationSchema,
33339
+ text: string2(),
33340
+ amount: number2().nullable(),
33341
+ occurredAt: exports_coerce.date()
33342
+ });
33343
+ var logWriteResultSchema = object({
33344
+ entry: logEntryViewSchema,
33345
+ preview: logPreviewSchema,
33346
+ remembered: string2().nullable()
33347
+ });
33348
+ var logKindViewSchema = object({
33349
+ key: string2(),
33350
+ label: string2(),
33351
+ words: array(string2()),
33352
+ destination: logDestinationSchema,
33353
+ goesTo: string2(),
33354
+ icon: string2(),
33355
+ builtIn: boolean2(),
33356
+ likeKind: string2().nullable()
33357
+ });
33358
+
33359
+ // ../../packages/schema/src/operations/log.ts
33360
+ var SECTION_SCOPES = "A line kept in Animals also needs `animals:write`; in Health or the Journal, `health:write` or `journal:write` held by name, on a key that acts for a member.";
33361
+ var logOperations = defineOperations({
33362
+ preview: {
33363
+ method: "post",
33364
+ path: "/log/preview",
33365
+ scopes: ["habits:read"],
33366
+ tag: "Log",
33367
+ summary: "Preview a logged line",
33368
+ description: 'Parse one line ("pages 34", "eggs 4") without writing anything: its `kind` and `amount`, a `label` ("34 pages, today"), where it\'s kept (`goesTo`: "Habits, Read 20 pages") and what it does (`effect`: "Ticks it, streak 32 days", or null). `needsKind` is true when the agent doesn\'t know the word yet: ask once (the kinds from `GET /api/v1/log/kinds`) and send `kind` on the write.',
33369
+ body: logPreviewInputSchema,
33370
+ status: 200,
33371
+ response: { description: "What the line would do." },
33372
+ errors: { 400: true },
33373
+ operationId: "log_preview"
33374
+ },
33375
+ write: {
33376
+ method: "post",
33377
+ path: "/log",
33378
+ scopes: ["habits:write"],
33379
+ tag: "Log",
33380
+ summary: "Log something",
33381
+ description: `Write one line into its section: a habit check-in with the amount, the animals log (eggs on the flock), the Health log or a Journal note. For an unknown word send \`kind\` (a built-in's key or a remembered word) or \`destination\`: the word is remembered for the Space, so the next line with it previews without asking. Returns the entry, its preview and the word remembered (or null). ${SECTION_SCOPES}`,
33382
+ body: logWriteInputSchema,
33383
+ status: 201,
33384
+ response: { description: "The entry, what it did, and the word remembered." },
33385
+ errors: {
33386
+ 400: "The line is blank, its kind is unknown and none was given, or it needs an amount.",
33387
+ 403: true
33388
+ },
33389
+ operationId: "log_write"
33390
+ },
33391
+ list: {
33392
+ method: "get",
33393
+ path: "/log",
33394
+ scopes: ["habits:read"],
33395
+ tag: "Log",
33396
+ summary: "List what was logged",
33397
+ description: 'The lines written through Log something on `day` (the member\'s day; today by default), oldest first: `{ at: "08:10", what: "Eggs, 4", where: "Animals, Coop" }`. A Health or Journal line shows only to the member who logged it, on a key holding that section\'s read scope by name; an Animals line needs `animals:read`.',
33398
+ query: [{ name: "day", description: "The day, YYYY-MM-DD; today by default.", format: "date" }],
33399
+ querySchema: logDayQuerySchema,
33400
+ status: 200,
33401
+ response: { description: "The day's logged lines.", list: true },
33402
+ errors: { 400: true },
33403
+ operationId: "log_list"
33404
+ },
33405
+ kinds: {
33406
+ method: "get",
33407
+ path: "/log/kinds",
33408
+ scopes: ["habits:read"],
33409
+ tag: "Log",
33410
+ summary: "List the kinds you log",
33411
+ description: "The Space's kinds: the built-ins (Pages read \u2192 Habits, Eggs \u2192 Animals, A health entry \u2192 Health, A note \u2192 Journal) and every word it taught the agent, each with its words and where it's kept (`goesTo`).",
33412
+ status: 200,
33413
+ response: { description: "The kinds.", list: true },
33414
+ operationId: "log_kinds"
33415
+ }
33416
+ });
33417
+
32855
33418
  // ../../packages/schema/src/db/mail.ts
32856
33419
  var mailCampaignStatus = pgEnum("mail_campaign_status", [
32857
33420
  "draft",
@@ -32951,11 +33514,11 @@ var mailLookShape = {
32951
33514
  couple: string2().trim().min(1).max(120).optional(),
32952
33515
  imagesUrl: url({ protocol: /^https$/ }).max(500).optional()
32953
33516
  };
32954
- var lineSchema = string2().trim().min(1).max(2000);
33517
+ var lineSchema2 = string2().trim().min(1).max(2000);
32955
33518
  var mailMessageCopySchema = object({
32956
33519
  subject: string2().trim().min(1).max(200),
32957
33520
  heading: string2().trim().min(1).max(200),
32958
- paragraphs: array(lineSchema).max(20).default([]),
33521
+ paragraphs: array(lineSchema2).max(20).default([]),
32959
33522
  button: object({ label: string2().trim().min(1).max(80), url: url() }).strict().nullish()
32960
33523
  }).strict();
32961
33524
  var mailMessageDataSchema = object({
@@ -32975,7 +33538,7 @@ var mailDaySchema = object({
32975
33538
  var mailSaveTheDateCopySchema = object({
32976
33539
  subject: string2().trim().min(1).max(200),
32977
33540
  heading: string2().trim().min(1).max(200),
32978
- paragraphs: array(lineSchema).max(20).default([]),
33541
+ paragraphs: array(lineSchema2).max(20).default([]),
32979
33542
  button: string2().trim().min(1).max(80)
32980
33543
  }).strict();
32981
33544
  var mailSaveTheDateDataSchema = object({
@@ -36188,6 +36751,67 @@ var progressDayDetailSchema = object({
36188
36751
  slipped: array(progressSlippedItemSchema)
36189
36752
  });
36190
36753
 
36754
+ // ../../packages/schema/src/contracts/progress-review.ts
36755
+ var REVIEW_SPANS = ["week", "month"];
36756
+ var reviewSpanSchema = _enum2(REVIEW_SPANS);
36757
+ var reviewSummaryQuerySchema = object({
36758
+ span: reviewSpanSchema.default("week"),
36759
+ date: progressDateSchema.optional()
36760
+ });
36761
+ var countSchema2 = number2().int().nonnegative();
36762
+ var REVIEW_MARK_STATES = ["done", "missed", "open", "later", "not_due"];
36763
+ var reviewMarkStateSchema = _enum2(REVIEW_MARK_STATES);
36764
+ var reviewMarkSchema = object({
36765
+ date: progressDateSchema,
36766
+ state: reviewMarkStateSchema
36767
+ });
36768
+ var reviewNumbersSchema = object({
36769
+ tasksDone: countSchema2,
36770
+ tasksDoneDelta: number2().int(),
36771
+ habitTicks: countSchema2,
36772
+ habitDue: countSchema2,
36773
+ projectsMoved: countSchema2,
36774
+ challengesRunning: countSchema2
36775
+ });
36776
+ var reviewSeriesEntrySchema = object({
36777
+ key: string2().min(1),
36778
+ label: string2().min(1),
36779
+ tasksDone: countSchema2,
36780
+ habitTicks: countSchema2,
36781
+ current: boolean2()
36782
+ });
36783
+ var reviewStreakSchema = object({
36784
+ habitId: string2().min(1),
36785
+ title: string2(),
36786
+ scheduleLabel: string2().min(1),
36787
+ current: countSchema2,
36788
+ best: countSchema2,
36789
+ unitLabel: string2().min(1),
36790
+ marks: array(reviewMarkSchema)
36791
+ });
36792
+ var reviewProjectSchema = object({
36793
+ projectId: string2().min(1),
36794
+ title: string2(),
36795
+ done: countSchema2,
36796
+ total: countSchema2,
36797
+ targetDate: progressDateSchema.nullable()
36798
+ });
36799
+ var reviewSummarySchema = object({
36800
+ span: reviewSpanSchema,
36801
+ label: string2().min(1),
36802
+ from: progressDateSchema,
36803
+ to: progressDateSchema,
36804
+ rangeLabel: string2().min(1),
36805
+ timezone: string2().min(1),
36806
+ numbers: reviewNumbersSchema,
36807
+ series: array(reviewSeriesEntrySchema).length(8),
36808
+ streaks: array(reviewStreakSchema),
36809
+ projects: array(reviewProjectSchema)
36810
+ }).refine((s) => s.series.at(-1)?.current === true, {
36811
+ message: "the last series entry is the asked period",
36812
+ path: ["series"]
36813
+ }).refine((s) => s.from <= s.to, { message: "from is on or before to", path: ["from"] });
36814
+
36191
36815
  // ../../packages/schema/src/operations/progress.ts
36192
36816
  var NO_MEMBER3 = "The key acts for no member.";
36193
36817
  var question = {
@@ -36229,6 +36853,33 @@ var progressOperations = defineOperations({
36229
36853
  errors: { 400: true },
36230
36854
  operationId: "get_progress_day"
36231
36855
  },
36856
+ review: {
36857
+ method: "get",
36858
+ path: "/progress/review",
36859
+ scopes: ["tasks:read", "habits:read"],
36860
+ tag: "Reads",
36861
+ summary: "Review summary",
36862
+ description: "Plan > Review in one read, for the ISO week (Monday to Sunday) or calendar month holding `date` (default: the one holding today, in the key's member's saved timezone): the headline numbers (tasks done, mine and the agents', against the period before; habit days met and due; projects with a task done; challenges running), tasks done and habit days met for the last 8 periods (oldest first, the last one the asked period), one streak row per live habit (current and best run, `unitLabel` `N days` for a daily habit and `N in a row` otherwise, and a mark per day of the week or per week of the month: done, missed, open, later or not_due, a day it isn't due that never breaks the run), and each open project's tasks done of its total, the most recently active first.",
36863
+ query: [
36864
+ {
36865
+ name: "span",
36866
+ description: "`week` (default) or `month`.",
36867
+ type: "string",
36868
+ enum: [...REVIEW_SPANS]
36869
+ },
36870
+ {
36871
+ name: "date",
36872
+ description: "A day (`YYYY-MM-DD`) in the period; default today.",
36873
+ type: "string",
36874
+ format: "date"
36875
+ }
36876
+ ],
36877
+ querySchema: reviewSummaryQuerySchema,
36878
+ status: 200,
36879
+ response: { description: "The Review summary (`ReviewSummary`)." },
36880
+ errors: { 400: true },
36881
+ operationId: "get_progress_review"
36882
+ },
36232
36883
  habits: {
36233
36884
  method: "get",
36234
36885
  path: "/progress/habits",
@@ -36364,6 +37015,46 @@ var projectOperations = defineOperations({
36364
37015
  response: { description: "Detached." },
36365
37016
  errors: { 404: true },
36366
37017
  webhook: "project.task.detached"
37018
+ },
37019
+ listUpdates: {
37020
+ method: "get",
37021
+ path: "/projects/{id}/updates",
37022
+ pathParams: { id: "The project's ULID or its ref (`DO-P7`)." },
37023
+ scopes: ["projects:read"],
37024
+ tag: "Projects",
37025
+ summary: "List a project's updates",
37026
+ description: "The project's updates, newest first: each a status, a note, its author and date.",
37027
+ status: 200,
37028
+ response: { description: "The project's updates, newest first." },
37029
+ errors: { 404: true }
37030
+ },
37031
+ createUpdate: {
37032
+ method: "post",
37033
+ path: "/projects/{id}/updates",
37034
+ pathParams: { id: "The project's ULID or its ref (`DO-P7`)." },
37035
+ scopes: ["projects:write"],
37036
+ tag: "Projects",
37037
+ summary: "Post a project update",
37038
+ description: "Write a dated status note on the project (`on_track`, `at_risk`, `off_track`, `milestone_done` or `done`). It is attributed to the key's Agent, or else to the key's member; a machine key with neither is a 403.",
37039
+ body: createProjectUpdateInputSchema,
37040
+ status: 201,
37041
+ response: { description: "The posted update." },
37042
+ errors: { 400: true, 403: true, 404: true }
37043
+ },
37044
+ removeUpdate: {
37045
+ method: "delete",
37046
+ path: "/projects/{id}/updates/{updateId}",
37047
+ pathParams: {
37048
+ id: "The project's ULID or its ref (`DO-P7`).",
37049
+ updateId: "The update's ULID."
37050
+ },
37051
+ scopes: ["projects:write"],
37052
+ tag: "Projects",
37053
+ summary: "Delete a project update",
37054
+ description: "Delete an update; only its author (the member or the Agent) may.",
37055
+ status: 204,
37056
+ response: { description: "Deleted." },
37057
+ errors: { 403: true, 404: true }
36367
37058
  }
36368
37059
  });
36369
37060
 
@@ -36432,12 +37123,47 @@ var landedByActorSchema = object({
36432
37123
  actor: actorRefSchema.nullable(),
36433
37124
  count: number2().int().nonnegative()
36434
37125
  });
37126
+ var landedQuerySchema = readWindowQuerySchema.extend({
37127
+ tz: string2().trim().min(1).max(64).optional()
37128
+ });
37129
+ var landedSectionSchema = _enum2(["work", "habits", "agents", "home", "food", "journal"]);
37130
+ var landedActorSchema = object({
37131
+ kind: _enum2(["you", "member", "echo", "agent"]),
37132
+ id: string2().nullable(),
37133
+ name: string2().nullable(),
37134
+ machine: string2().nullable().optional()
37135
+ });
37136
+ var landedItemSchema = object({
37137
+ at: exports_coerce.date(),
37138
+ title: string2(),
37139
+ where: string2(),
37140
+ section: landedSectionSchema,
37141
+ actor: landedActorSchema,
37142
+ ref: object({
37143
+ type: _enum2(["task", "habit_entry", "document", "shopping_item"]),
37144
+ id: string2()
37145
+ })
37146
+ });
37147
+ var landedDaySchema = object({
37148
+ date: string2().regex(/^\d{4}-\d{2}-\d{2}$/),
37149
+ you: number2().int().nonnegative(),
37150
+ agents: number2().int().nonnegative()
37151
+ });
36435
37152
  var landedResponseSchema = object({
36436
37153
  from: exports_coerce.date(),
36437
37154
  to: exports_coerce.date(),
36438
37155
  total: number2().int().nonnegative(),
36439
37156
  byActor: array(landedByActorSchema),
36440
- tasks: array(taskViewSchema)
37157
+ tasks: array(taskViewSchema),
37158
+ itemsTotal: number2().int().nonnegative().default(0),
37159
+ today: number2().int().nonnegative().default(0),
37160
+ previousWeekTotal: number2().int().nonnegative().default(0),
37161
+ byDay: array(landedDaySchema).default([]),
37162
+ bySection: array(object({
37163
+ section: landedSectionSchema,
37164
+ count: number2().int().nonnegative()
37165
+ })).default([]),
37166
+ items: array(landedItemSchema).default([])
36441
37167
  });
36442
37168
  var searchNodeTypeSchema = _enum2([
36443
37169
  "task",
@@ -36503,9 +37229,15 @@ var readOperations = defineOperations({
36503
37229
  scopes: ["tasks:read"],
36504
37230
  tag: "Reads",
36505
37231
  summary: "What landed",
36506
- description: "What got done in the window by `completedAt` (cancelled tasks never count): the total, a per-Actor split (who finished each \u2014 a member or an Agent), and the tasks, most recently finished first.",
36507
- query: windowParams,
36508
- querySchema: readWindowQuerySchema,
37232
+ description: "What got done in the window by `completedAt` (cancelled tasks never count): the total, a per-Actor split (who finished each \u2014 a member or an Agent), and the tasks, most recently finished first. Across sections too: `items` (done tasks, habit check-ins, agent sessions' finished tasks, papers filed, shopping lines ticked by anyone; newest last, each with who did it and where it lives), `byDay` (each day of the window in `tz`, by you vs by agents), `bySection` (most first), `itemsTotal`, `today` and `previousWeekTotal` (the same-length window just before). The Agent's and agents' work counts in every total and is marked by `actor.kind` (`echo` / `agent`).",
37233
+ query: [
37234
+ ...windowParams,
37235
+ {
37236
+ name: "tz",
37237
+ description: "The IANA zone the window's days are cut in (`Europe/Brussels`). Default UTC."
37238
+ }
37239
+ ],
37240
+ querySchema: landedQuerySchema,
36509
37241
  status: 200,
36510
37242
  response: { description: "The Landed read.", view: "LandedResponse" },
36511
37243
  operationId: "get_landed"
@@ -36765,6 +37497,17 @@ var roleOperations = defineOperations({
36765
37497
 
36766
37498
  // ../../packages/schema/src/operations/routines.ts
36767
37499
  var routineOperations = defineOperations({
37500
+ today: {
37501
+ method: "get",
37502
+ path: "/routines/today",
37503
+ scopes: ["routines:read"],
37504
+ tag: "Routines",
37505
+ summary: "Today's routines",
37506
+ description: "The routines on the key's member's today (due by their rule, with a slot today, or run today), by start: each with its start (\"HH:MM\", null without a clock time), length, state (`done`, `running`, `upcoming`, `resting`), today's run (`startedAt`, `completedAt`, `done`, `total`) or null, its numbered steps with their kind (`step`, `habit`, `task`), a meta line and whether each is done, and `rhythm`, its last 14 days (`all`, `some`, `none`, `off`), oldest first.",
37507
+ status: 200,
37508
+ response: { description: "`{ today, routines }`." },
37509
+ operationId: "routines_today"
37510
+ },
36768
37511
  getRun: {
36769
37512
  method: "get",
36770
37513
  path: "/routines/runs/{runId}",
@@ -36920,6 +37663,34 @@ var routineOperations = defineOperations({
36920
37663
  webhook: "routine.run.updated",
36921
37664
  operationId: "routine_all_taken"
36922
37665
  },
37666
+ reorderSteps: {
37667
+ method: "patch",
37668
+ path: "/routines/{id}/steps/order",
37669
+ scopes: ["routines:write"],
37670
+ tag: "Routines",
37671
+ summary: "Reorder a routine's steps",
37672
+ description: "Put the routine's template steps in a new order: `stepIds` names each step once, and each takes its index as its position. A list that isn't exactly the routine's steps is a 400. Runs already started keep their order. Fires `routine.updated`.",
37673
+ body: reorderRoutineStepsInputSchema,
37674
+ status: 200,
37675
+ response: { description: "The routine." },
37676
+ errors: { 400: true, 404: true },
37677
+ webhook: "routine.updated",
37678
+ operationId: "reorder_routine_steps"
37679
+ },
37680
+ addTaskStep: {
37681
+ method: "post",
37682
+ path: "/routines/{id}/runs/today/steps",
37683
+ scopes: ["routines:write"],
37684
+ tag: "Routines",
37685
+ summary: "Add a Work task to today's run",
37686
+ description: "Pull a Work task due today into today's run of the routine (the key's member's day) as a step at the end, starting today's run when there's none. The routine's own steps are unchanged. `reason` is the line shown with it (\"Call the plumber is due today in Work, so I added it to the end of this routine.\"). A task not due today is a 422; one already in today's run changes nothing. Ticking the step completes the task in Work; undo reopens it. Fires `routine.run.updated`.",
37687
+ body: addRoutineTaskStepInputSchema,
37688
+ status: 201,
37689
+ response: { description: "Today's run." },
37690
+ errors: { 400: true, 404: true, 422: true },
37691
+ webhook: "routine.run.updated",
37692
+ operationId: "add_routine_task_step"
37693
+ },
36923
37694
  startRun: {
36924
37695
  method: "post",
36925
37696
  path: "/routines/{id}/runs",
@@ -38549,6 +39320,40 @@ var tapTagOperations = defineOperations({
38549
39320
  }
38550
39321
  });
38551
39322
 
39323
+ // ../../packages/schema/src/operations/task-blocks.ts
39324
+ var pathParams5 = { id: "The blocked task's ULID or ref (`DO-148`)." };
39325
+ var taskBlockOperations = defineOperations({
39326
+ addBlocker: {
39327
+ method: "post",
39328
+ path: "/tasks/{id}/blocked-by",
39329
+ pathParams: pathParams5,
39330
+ scopes: ["tasks:write"],
39331
+ tag: "Tasks",
39332
+ summary: "Block a task by another",
39333
+ description: "`taskId` (a ULID or ref) blocks this task until it's Done. Both must be live in the Space; a task can't block itself, and a link that would make tasks wait on each other is a 422. Already linked is a no-op.",
39334
+ body: addTaskBlockerInputSchema,
39335
+ status: 200,
39336
+ response: { description: "The blocked task.", view: "Task" },
39337
+ errors: {
39338
+ 400: true,
39339
+ 404: true,
39340
+ 422: "A task can't wait on itself, or the link would make a loop."
39341
+ }
39342
+ },
39343
+ removeBlocker: {
39344
+ method: "delete",
39345
+ path: "/tasks/{id}/blocked-by/{blockerId}",
39346
+ pathParams: { ...pathParams5, blockerId: "The blocker's ULID or ref (`DO-147`)." },
39347
+ scopes: ["tasks:write"],
39348
+ tag: "Tasks",
39349
+ summary: "Unblock a task",
39350
+ description: "The blocker no longer blocks this task. A link that isn't there is a no-op.",
39351
+ status: 200,
39352
+ response: { description: "The task.", view: "Task" },
39353
+ errors: { 404: true }
39354
+ }
39355
+ });
39356
+
38552
39357
  // ../../packages/schema/src/operations/tasks.ts
38553
39358
  var taskListQuery = taskListQuerySchema.extend(paginationQuerySchema.shape).extend({ filter: taskListFilterSchema.optional().default("all") });
38554
39359
  var taskOperations = defineOperations({
@@ -38572,7 +39377,15 @@ var taskOperations = defineOperations({
38572
39377
  { name: "projectId", description: "Only tasks attached to this project (ULID)." },
38573
39378
  {
38574
39379
  name: "status",
38575
- description: "Only rows in any of these statuses (comma list or repeated key): `todo`, `in_progress`, `done`, `cancelled`."
39380
+ description: "Only rows in any of these statuses (comma list or repeated key): `backlog`, `todo`, `in_progress`, `waiting`, `done`, `cancelled`."
39381
+ },
39382
+ {
39383
+ name: "priority",
39384
+ description: "Only tasks with any of these priorities (comma list or repeated key): `none`, `low`, `medium`, `high`."
39385
+ },
39386
+ {
39387
+ name: "view",
39388
+ description: "Only the tasks a Work saved view matches (its ULID): exactly the ones its `count` counts."
38576
39389
  },
38577
39390
  {
38578
39391
  name: "tag",
@@ -38644,7 +39457,7 @@ var taskOperations = defineOperations({
38644
39457
  scopes: ["tasks:read"],
38645
39458
  tag: "Tasks",
38646
39459
  summary: "Get a task",
38647
- description: "Fetch one task by id.",
39460
+ description: "Fetch one task by id (ULID) or by its ref (`DO-142`, case-insensitive).",
38648
39461
  status: 200,
38649
39462
  response: { description: "The task." },
38650
39463
  errors: { 404: true }
@@ -39168,8 +39981,146 @@ var withingsOperations = defineOperations({
39168
39981
  }
39169
39982
  });
39170
39983
 
39984
+ // ../../packages/schema/src/db/work-views.ts
39985
+ var workSavedViews = pgTable("work_saved_views", {
39986
+ id: text("id").primaryKey(),
39987
+ tenantId: text("tenant_id").notNull().references(() => tenants.id),
39988
+ name: text("name").notNull(),
39989
+ filters: jsonb("filters").notNull().default({}),
39990
+ groupBy: text("group_by").notNull().default("none"),
39991
+ view: text("view").notNull().default("list"),
39992
+ sortOrder: integer2("sort_order").notNull().default(0),
39993
+ pinned: boolean4("pinned").notNull().default(false),
39994
+ defaultKey: text("default_key"),
39995
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
39996
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
39997
+ deletedAt: timestamp("deleted_at", { withTimezone: true })
39998
+ }, (t) => [
39999
+ index("work_saved_views_tenant_idx").on(t.tenantId, t.sortOrder)
40000
+ ]);
40001
+
40002
+ // ../../packages/schema/src/contracts/work-views.ts
40003
+ var workViewGroupBySchema = _enum2([
40004
+ "area",
40005
+ "status",
40006
+ "project",
40007
+ "priority",
40008
+ "assignee",
40009
+ "none"
40010
+ ]);
40011
+ var workViewKindSchema = _enum2(["list", "board", "timeline"]);
40012
+ var workViewWindowSchema = _enum2(["overdue", "today", "this_week", "next_week", "none"]);
40013
+ var workViewAssigneeSchema = union([
40014
+ _enum2(["me", "none"]),
40015
+ string2().regex(/^member:[0-9A-HJKMNP-TV-Z]{26}$/, "member:<ULID>"),
40016
+ string2().regex(/^agent:[0-9A-HJKMNP-TV-Z]{26}$/, "agent:<ULID>")
40017
+ ]);
40018
+ var workViewFilterSchema = object({
40019
+ status: array(taskStatusSchema).min(1).optional(),
40020
+ notStatus: array(taskStatusSchema).min(1).optional(),
40021
+ areaIds: array(ulidSchema).min(1).optional(),
40022
+ projectIds: array(ulidSchema).min(1).optional(),
40023
+ labels: array(string2().trim().min(1).max(64)).min(1).optional(),
40024
+ priority: array(workPrioritySchema).min(1).optional(),
40025
+ assignee: workViewAssigneeSchema.optional(),
40026
+ waiting: boolean2().optional(),
40027
+ due: workViewWindowSchema.optional(),
40028
+ scheduled: workViewWindowSchema.optional(),
40029
+ when: workViewWindowSchema.optional()
40030
+ }).strict();
40031
+ var workViewDefaultKeySchema = _enum2(["all_open", "this_week", "waiting"]);
40032
+ var workViewNameSchema = string2().trim().min(1).max(80);
40033
+ var workSavedViewSchema = createSelectSchema(workSavedViews, {
40034
+ filters: workViewFilterSchema,
40035
+ groupBy: workViewGroupBySchema,
40036
+ view: workViewKindSchema,
40037
+ defaultKey: workViewDefaultKeySchema.nullable(),
40038
+ createdAt: exports_coerce.date(),
40039
+ updatedAt: exports_coerce.date(),
40040
+ deletedAt: exports_coerce.date().nullable()
40041
+ });
40042
+ var workViewSchema = workSavedViewSchema.omit({ tenantId: true, deletedAt: true }).extend({ count: number2().int().nonnegative() });
40043
+ var createWorkViewInputSchema = object({
40044
+ id: ulidSchema.optional(),
40045
+ name: workViewNameSchema,
40046
+ filters: workViewFilterSchema.default({}),
40047
+ groupBy: workViewGroupBySchema.default("none"),
40048
+ view: workViewKindSchema.default("list"),
40049
+ pinned: boolean2().optional(),
40050
+ sortOrder: number2().int().nonnegative().optional()
40051
+ }).strict();
40052
+ var updateWorkViewInputSchema = object({
40053
+ name: workViewNameSchema.optional(),
40054
+ filters: workViewFilterSchema.optional(),
40055
+ groupBy: workViewGroupBySchema.optional(),
40056
+ view: workViewKindSchema.optional(),
40057
+ pinned: boolean2().optional()
40058
+ }).strict();
40059
+ var reorderWorkViewsInputSchema = object({ ids: array(ulidSchema).min(1).max(200) }).strict();
40060
+
40061
+ // ../../packages/schema/src/operations/work-views.ts
40062
+ var workViewOperations = defineOperations({
40063
+ list: {
40064
+ method: "get",
40065
+ path: "/work-views",
40066
+ scopes: ["tasks:read"],
40067
+ tag: "Tasks",
40068
+ summary: "List Work saved views",
40069
+ description: "The Space's saved Work views in order, each with its filters, grouping, view kind and live `count` of the tasks the key may view that it matches. A Space's first read seeds All open, This week and Waiting on someone.",
40070
+ status: 200,
40071
+ response: { description: "The views, in order." }
40072
+ },
40073
+ create: {
40074
+ method: "post",
40075
+ path: "/work-views",
40076
+ scopes: ["tasks:write"],
40077
+ tag: "Tasks",
40078
+ summary: "Save a Work view",
40079
+ description: "A named filter (strict: an unknown key is a 400), with `groupBy` and `view`; it goes last unless `sortOrder` says where.",
40080
+ body: createWorkViewInputSchema,
40081
+ status: 201,
40082
+ response: { description: "The saved view, with its count." },
40083
+ errors: { 400: true }
40084
+ },
40085
+ reorder: {
40086
+ method: "post",
40087
+ path: "/work-views/reorder",
40088
+ scopes: ["tasks:write"],
40089
+ tag: "Tasks",
40090
+ summary: "Reorder Work views",
40091
+ description: "The listed views first, in the order given; any not listed keep their order after them. An unknown id is a 404.",
40092
+ body: reorderWorkViewsInputSchema,
40093
+ status: 200,
40094
+ response: { description: "The views, in their new order." },
40095
+ errors: { 400: true, 404: true }
40096
+ },
40097
+ update: {
40098
+ method: "patch",
40099
+ path: "/work-views/{id}",
40100
+ scopes: ["tasks:write"],
40101
+ tag: "Tasks",
40102
+ summary: "Update a Work view",
40103
+ description: "Rename it, or change its filters (replaced whole), grouping, view kind or pin.",
40104
+ body: updateWorkViewInputSchema,
40105
+ status: 200,
40106
+ response: { description: "The view, with its count." },
40107
+ errors: { 400: true, 404: true }
40108
+ },
40109
+ remove: {
40110
+ method: "delete",
40111
+ path: "/work-views/{id}",
40112
+ scopes: ["tasks:write"],
40113
+ tag: "Tasks",
40114
+ summary: "Delete a Work view",
40115
+ description: "Delete a view, a default one too; it isn't seeded again.",
40116
+ status: 204,
40117
+ response: { description: "Deleted." },
40118
+ errors: { 404: true }
40119
+ }
40120
+ });
40121
+
39171
40122
  // ../../packages/schema/src/operations/worktree-links.ts
39172
- var pathParams5 = { nodeId: "The linked Task's or Project's ULID." };
40123
+ var pathParams6 = { nodeId: "The linked Task's or Project's ULID." };
39173
40124
  var notLinkable = "No live Task or Project with that id in this Space (or, for GET, no link on it).";
39174
40125
  var worktreeLinkOperations = defineOperations({
39175
40126
  list: {
@@ -39195,7 +40146,7 @@ var worktreeLinkOperations = defineOperations({
39195
40146
  get: {
39196
40147
  method: "get",
39197
40148
  path: "/worktree-links/{nodeId}",
39198
- pathParams: pathParams5,
40149
+ pathParams: pathParams6,
39199
40150
  scopes: ["orca:read"],
39200
40151
  tag: "Orca",
39201
40152
  summary: "Get a worktree link",
@@ -39207,7 +40158,7 @@ var worktreeLinkOperations = defineOperations({
39207
40158
  set: {
39208
40159
  method: "put",
39209
40160
  path: "/worktree-links/{nodeId}",
39210
- pathParams: pathParams5,
40161
+ pathParams: pathParams6,
39211
40162
  scopes: ["orca:write"],
39212
40163
  tag: "Orca",
39213
40164
  summary: "Set a worktree link",
@@ -39221,7 +40172,7 @@ var worktreeLinkOperations = defineOperations({
39221
40172
  clear: {
39222
40173
  method: "delete",
39223
40174
  path: "/worktree-links/{nodeId}",
39224
- pathParams: pathParams5,
40175
+ pathParams: pathParams6,
39225
40176
  scopes: ["orca:write"],
39226
40177
  tag: "Orca",
39227
40178
  summary: "Clear a worktree link",
@@ -39750,7 +40701,8 @@ var filmFacetSchema = object({
39750
40701
  coverUrl,
39751
40702
  episodeCount: number2().int().min(1).max(1e4).nullish(),
39752
40703
  currentEpisode: number2().int().min(0).max(1e4).nullish(),
39753
- format: string2().min(1).max(60).nullish()
40704
+ format: string2().min(1).max(60).nullish(),
40705
+ runtimeMinutes: number2().int().min(1).max(2000).nullish()
39754
40706
  }).strict();
39755
40707
  var gameFacetSchema = object({
39756
40708
  platform: string2().min(1).max(120).nullish(),
@@ -39764,11 +40716,30 @@ var gameFacetSchema = object({
39764
40716
  year: number2().int().min(1950).max(2100).nullish(),
39765
40717
  playedWith: string2().min(1).max(120).nullish()
39766
40718
  }).strict();
40719
+ var musicTrackSchema = object({
40720
+ pos: string2().min(1).max(8),
40721
+ title: string2().min(1).max(200),
40722
+ seconds: number2().int().min(0).max(36000).nullish()
40723
+ }).strict();
39767
40724
  var musicFacetSchema = object({
39768
40725
  artist: string2().min(1).max(200).nullish(),
39769
40726
  year: number2().int().min(1860).max(2100).nullish(),
39770
40727
  coverUrl,
39771
- format: string2().min(1).max(60).nullish()
40728
+ format: string2().min(1).max(60).nullish(),
40729
+ edition: string2().min(1).max(60).nullish(),
40730
+ tracks: array(musicTrackSchema).max(60).nullish()
40731
+ }).strict();
40732
+ var hobbyFacetSchema = object({
40733
+ kind: string2().min(1).max(40).nullish(),
40734
+ progress: object({
40735
+ done: number2().int().min(0).max(1e5),
40736
+ total: number2().int().min(1).max(1e5),
40737
+ unit: string2().min(1).max(40)
40738
+ }).strict().nullish(),
40739
+ shelf: _enum2(["display", "boxed"]).default("display"),
40740
+ needsFix: boolean2().nullish(),
40741
+ fixNote: string2().min(1).max(200).nullish(),
40742
+ coverUrl
39772
40743
  }).strict();
39773
40744
  var placeFacetSchema = object({
39774
40745
  address: string2().min(1).max(400).nullish(),
@@ -39996,6 +40967,16 @@ var COLLECTION_TYPES = {
39996
40967
  facetSchema: assetFacetSchema,
39997
40968
  view: "grid",
39998
40969
  subtitleKey: "brand"
40970
+ },
40971
+ hobby: {
40972
+ key: "hobby",
40973
+ label: "Things",
40974
+ itemNoun: "thing",
40975
+ statuses: ["want", "doing", "have"],
40976
+ statusLabels: { want: "Want", doing: "Building", have: "Built" },
40977
+ facetSchema: hobbyFacetSchema,
40978
+ view: "grid",
40979
+ subtitleKey: "kind"
39999
40980
  }
40000
40981
  };
40001
40982
  var COLLECTION_TYPE_LIST = collectionTypeValues.map((k) => COLLECTION_TYPES[k]);
@@ -40074,8 +41055,11 @@ var STATUS_MARK = {
40074
41055
  done: "\u2713",
40075
41056
  cancelled: "\u2717",
40076
41057
  in_progress: "\u25D0",
40077
- todo: "\u25CB"
41058
+ todo: "\u25CB",
41059
+ backlog: "\u25CC",
41060
+ waiting: "\u29D7"
40078
41061
  };
41062
+ var PRIORITY_WORD = { low: "low", medium: "medium", high: "high" };
40079
41063
  function suggestionText(s) {
40080
41064
  const parts = [
40081
41065
  [s.area, s.project].filter(Boolean).join(" \u203A ") || null,
@@ -40100,18 +41084,24 @@ function renderTask(data, mode) {
40100
41084
  const date = when ? paint(mode, "dim", ` (${shortDate(when)})`) : "";
40101
41085
  const by = t.createdBy?.kind === "agent" ? paint(mode, "dim", ` \xB7 by ${t.createdBy.name ?? "an agent"}`) : "";
40102
41086
  const meta = [
41087
+ t.priority && PRIORITY_WORD[t.priority] ? `${PRIORITY_WORD[t.priority]} priority` : null,
40103
41088
  t.owner ? `owner ${ownerText(t.owner)}` : null,
40104
- t.nextStep ? `next: ${t.nextStep}` : null
41089
+ t.nextStep ? `next: ${t.nextStep}` : null,
41090
+ t.deliverable ? `delivers: ${t.deliverable}` : null
40105
41091
  ].filter(Boolean);
40106
41092
  const waiting = t.waitingOn ? `
40107
41093
  ${paint(mode, "yellow", `\u29D7 waiting on ${t.waitingOn.name ?? "someone"}`)}` : "";
40108
41094
  const metaLine = meta.length > 0 ? `
40109
41095
  ${paint(mode, "dim", meta.join(" \xB7 "))}` : "";
41096
+ const holding = (t.blockedBy ?? []).filter((b) => b.status !== "done");
41097
+ const blocked = t.isBlocked && holding.length > 0 ? `
41098
+ ${paint(mode, "yellow", `\u2298 blocked by ${holding.map((b) => `${b.ref ?? ""} ${b.title ?? ""}`.trim()).join(", ")}`)}` : "";
40110
41099
  const worktree = t.worktreeLink ? `
40111
41100
  ${paint(mode, "dim", `\u2387 ${worktreeText(t.worktreeLink)}`)}` : "";
40112
41101
  const suggestion = t.filingSuggestion ? `
40113
41102
  ${paint(mode, "dim", `\u2726 ${suggestionText(t.filingSuggestion)}`)}` : "";
40114
- return `${mark} ${star}${t.title ?? "(untitled)"}${date}${by}${waiting}${metaLine}${worktree}${suggestion}
41103
+ const ref = t.ref ? `${t.ref} ` : "";
41104
+ return `${mark} ${ref}${star}${t.title ?? "(untitled)"}${date}${by}${waiting}${blocked}${metaLine}${worktree}${suggestion}
40115
41105
  ${paint(mode, "dim", t.id)}`;
40116
41106
  }
40117
41107
  function renderToday(data, mode) {
@@ -40437,7 +41427,8 @@ function renderList(data, mode) {
40437
41427
  const body = rows.length === 0 ? paint(mode, "dim", "(no records)") : [
40438
41428
  `${rows.length} record(s):`,
40439
41429
  ...rows.map((row) => {
40440
- const label = labelOf(row);
41430
+ const ref = refOf(row);
41431
+ const label = ref ? `${ref} ${labelOf(row)}` : labelOf(row);
40441
41432
  const id = idOf(row);
40442
41433
  return id ? ` ${label} ${paint(mode, "dim", id)}` : ` ${label}`;
40443
41434
  })
@@ -40448,6 +41439,12 @@ function renderList(data, mode) {
40448
41439
  return `${body}
40449
41440
  ${paint(mode, "dim", `\u2026 more \u2014 rerun with --cursor ${nextCursor}`)}`;
40450
41441
  }
41442
+ function refOf(row) {
41443
+ if (row === null || typeof row !== "object")
41444
+ return null;
41445
+ const ref = row.ref;
41446
+ return typeof ref === "string" && ref.length > 0 ? ref : null;
41447
+ }
40451
41448
  function asPage(data) {
40452
41449
  if (Array.isArray(data))
40453
41450
  return { rows: data, nextCursor: null };
@@ -41325,6 +42322,9 @@ function mergePreview(before, changes) {
41325
42322
  }
41326
42323
 
41327
42324
  // src/entities.ts
42325
+ function taskStatusWire(value) {
42326
+ return value.trim().toLowerCase().replace(/-/g, "_");
42327
+ }
41328
42328
  var AREA_FLAG = {
41329
42329
  name: "area",
41330
42330
  description: "only rows in this area id",
@@ -41386,7 +42386,12 @@ var ENTITIES = [
41386
42386
  { name: "project", description: "only tasks in this project id", query: "projectId" },
41387
42387
  {
41388
42388
  name: "status",
41389
- description: "only these statuses, comma-separated (todo,in_progress,done,cancelled)"
42389
+ description: "only these statuses, comma-separated (backlog,todo,in-progress,waiting,done,cancelled)",
42390
+ normalize: taskStatusWire
42391
+ },
42392
+ {
42393
+ name: "priority",
42394
+ description: "only these priorities, comma-separated (none,low,medium,high)"
41390
42395
  },
41391
42396
  { name: "tag", description: "only tasks carrying every listed tag, comma-separated" },
41392
42397
  UPDATED_SINCE_FLAG,
@@ -42888,7 +43893,7 @@ var commands5 = [
42888
43893
  notes: `${routeOf(calendarOperations.status)}. retrying = a passing failure the sweep retries; reconnect = the member reconnects in Settings. Needs a key that acts for a member.`
42889
43894
  },
42890
43895
  run: async ({ requireClient }) => ({
42891
- data: await requireClient().get(pathOf(calendarOperations.status)),
43896
+ data: (await requireClient().getPage(pathOf(calendarOperations.status))).data,
42892
43897
  render: renderCalendarStatus
42893
43898
  })
42894
43899
  },
@@ -42899,11 +43904,11 @@ var commands5 = [
42899
43904
  args: [],
42900
43905
  flags: [],
42901
43906
  scopes: [...calendarOperations.push.scopes],
42902
- returns: "CalendarPushResult[]",
43907
+ returns: "{ pushed: CalendarPushResult[] }",
42903
43908
  notes: `${routeOf(calendarOperations.push)}. A connection that needs reconnecting is left out. Needs a key that acts for a member.`
42904
43909
  },
42905
43910
  run: async ({ requireClient }) => ({
42906
- data: await requireClient().post(pathOf(calendarOperations.push), {}),
43911
+ data: (await requireClient().post(pathOf(calendarOperations.push), {})).pushed,
42907
43912
  render: renderCalendarPush
42908
43913
  })
42909
43914
  },
@@ -43166,7 +44171,11 @@ var commands8 = [
43166
44171
  summary: "List Collection Items, optionally sliced by Type / Status / tag.",
43167
44172
  args: [],
43168
44173
  flags: [
43169
- { name: "type", type: "string", description: "Filter to one Type (book|film|game|\u2026)." },
44174
+ {
44175
+ name: "type",
44176
+ type: "string",
44177
+ description: "Filter to one Type (book|film|game|\u2026|hobby)."
44178
+ },
43170
44179
  {
43171
44180
  name: "status",
43172
44181
  type: "string",
@@ -43209,7 +44218,11 @@ var commands8 = [
43209
44218
  summary: "Add a Collection Item (the title may embed #tag).",
43210
44219
  args: [{ name: "title", required: true, description: "Item title; may embed #tag." }],
43211
44220
  flags: [
43212
- { name: "type", type: "string", description: "Type (book|film|game|music|place|wine)." },
44221
+ {
44222
+ name: "type",
44223
+ type: "string",
44224
+ description: "Type (book|film|game|music|place|wine|wardrobe|hobby)."
44225
+ },
43213
44226
  {
43214
44227
  name: "status",
43215
44228
  type: "string",
@@ -43221,15 +44234,16 @@ var commands8 = [
43221
44234
  grammar: {
43222
44235
  "#tag": "Adds a tag. Repeatable: '#scifi #2024'."
43223
44236
  },
43224
- notes: "--type is required; the per-Type facet rides --data (validated server-side against the Type)."
44237
+ notes: '--type is required; the per-Type facet rides --data (validated server-side against the Type), e.g. `do collection add "Castle" --type hobby --status doing --data \'{"facet":{"kind":"LEGO","progress":{"done":7,"total":12,"unit":"bag"}}}\'`.'
43225
44238
  },
43226
44239
  run: async ({ args, flags, requireClient }) => {
43227
44240
  const { title, tags } = parseTitleTags(args);
43228
44241
  if (title.length === 0)
43229
44242
  throw usageError('collection add needs a title, e.g. `do collection add "Dune" --type book`');
43230
- const type = requireFlag(flags, "type", "collection add needs --type <book|film|game|music|place|wine>");
44243
+ const type = requireFlag(flags, "type", "collection add needs --type <book|film|game|music|place|wine|wardrobe|hobby>");
43231
44244
  const body = parseInput(createCollectionItemInputSchema, {
43232
44245
  id: ulid3(),
44246
+ ...readDataFlag(flags),
43233
44247
  type,
43234
44248
  title,
43235
44249
  ...tags.length > 0 ? { tags } : {},
@@ -47504,8 +48518,126 @@ var commands22 = [
47504
48518
  }
47505
48519
  ];
47506
48520
 
47507
- // src/commands/incidents.ts
48521
+ // src/commands/inbox.ts
48522
+ function renderInbox(data, _mode) {
48523
+ const d = data;
48524
+ const items = d.items ?? [];
48525
+ const c = d.counts ?? {};
48526
+ const head = `${c.all ?? items.length} waiting \xB7 ${c.capture ?? 0} captures \xB7 ${c.proposal ?? 0} proposals \xB7 ${c.answer ?? 0} need an answer`;
48527
+ if (items.length === 0)
48528
+ return `${head}
48529
+ Nothing waits here.`;
48530
+ const lines = items.map((item) => {
48531
+ const primary = item.actions.find((a) => a.primary);
48532
+ const action = primary ? ` [${primary.label} (${primary.key})]` : "";
48533
+ return `${item.id} ${item.kind.padEnd(8)} ${item.title} \u2014 ${item.source} \xB7 ${item.meta}${action}`;
48534
+ });
48535
+ return [head, ...lines].join(`
48536
+ `);
48537
+ }
48538
+ function renderSorting(data, _mode) {
48539
+ const d = data;
48540
+ return [
48541
+ `Captured by ${d.original.how}: \u201C${d.original.text}\u201D`,
48542
+ `Becomes a ${d.becomes}`,
48543
+ ...d.facts.map((f) => ` ${f.key}: ${f.value}`),
48544
+ ...d.why ? [d.why] : []
48545
+ ].join(`
48546
+ `);
48547
+ }
47508
48548
  var commands23 = [
48549
+ defineCommand({
48550
+ operation: inboxOperations.list,
48551
+ path: ["inbox"],
48552
+ summary: "The Inbox: captures, the agent's proposals and notifications that need an answer, oldest first, with per-kind counts.",
48553
+ flags: [
48554
+ {
48555
+ name: "kind",
48556
+ type: "string",
48557
+ description: "all (default), capture, proposal or answer."
48558
+ }
48559
+ ],
48560
+ notes: ". Each row names its one primary action and key (File e \xB7 Approve a \xB7 Decline d); approve and decline a proposal with `do tasks accept <id>` / `do task cancel <id>`, sort a capture with `do inbox sort`.",
48561
+ run: async ({ flags, requireClient }) => {
48562
+ const kind = flagString(flags, "kind");
48563
+ if (kind !== undefined && !inboxFilterSchema.safeParse(kind).success) {
48564
+ throw usageError(`--kind must be one of ${inboxFilterSchema.options.join(", ")}`);
48565
+ }
48566
+ return {
48567
+ data: await requireClient().get(pathOf(inboxOperations.list), {
48568
+ query: { kind }
48569
+ }),
48570
+ render: renderInbox
48571
+ };
48572
+ }
48573
+ }),
48574
+ defineCommand({
48575
+ operation: inboxOperations.sorting,
48576
+ path: ["inbox", "sorting"],
48577
+ summary: "The sorting pane for a capture: what it becomes and what the agent filled in.",
48578
+ args: [{ name: "id", required: true, description: "The capture's id." }],
48579
+ run: async ({ args, requireClient }) => ({
48580
+ data: await requireClient().get(pathOf(inboxOperations.sorting, { id: requireArg(args, 0, "id") })),
48581
+ render: renderSorting
48582
+ })
48583
+ }),
48584
+ defineCommand({
48585
+ operation: inboxOperations.sort,
48586
+ path: ["inbox", "sort"],
48587
+ summary: "Sort a capture into its home: a task, note, habit or log entry.",
48588
+ args: [{ name: "id", required: true, description: "The capture's id." }],
48589
+ flags: [
48590
+ {
48591
+ name: "as",
48592
+ type: "string",
48593
+ description: "task, note, habit or log (required)."
48594
+ },
48595
+ {
48596
+ name: "area",
48597
+ type: "string",
48598
+ description: "File it in this Area (id); a task without one takes the agent's suggestion."
48599
+ },
48600
+ {
48601
+ name: "title",
48602
+ type: "string",
48603
+ description: "A new title for what it becomes."
48604
+ }
48605
+ ],
48606
+ notes: ". A task keeps its id and is filed (the agent's suggestion unless --area); a note or habit is made from it (needs notes:write or habits:write) and the capture removed. `log` waits for the Log something writer.",
48607
+ run: async ({ args, flags, requireClient }) => {
48608
+ const id = requireArg(args, 0, "id");
48609
+ const as = requireFlag(flags, "as", `inbox sort needs --as ${INBOX_SORT_AS.join("|")}`);
48610
+ if (!INBOX_SORT_AS.includes(as)) {
48611
+ throw usageError(`--as must be one of ${INBOX_SORT_AS.join(", ")} (got \`${as}\`)`);
48612
+ }
48613
+ const area = flagString(flags, "area");
48614
+ const title = flagString(flags, "title");
48615
+ const fields = {
48616
+ ...area ? { areaId: area } : {},
48617
+ ...title ? { title } : {}
48618
+ };
48619
+ const body = Object.keys(fields).length > 0 ? { as, fields } : { as };
48620
+ return {
48621
+ data: await requireClient().post(pathOf(inboxOperations.sort, { id }), body),
48622
+ render: renderRecord
48623
+ };
48624
+ }
48625
+ }),
48626
+ defineCommand({
48627
+ operation: inboxOperations.letGo,
48628
+ path: ["inbox", "let-go"],
48629
+ summary: "Let a capture go: cancelled, out of the Inbox, never counted as Landed.",
48630
+ args: [{ name: "id", required: true, description: "The capture's id." }],
48631
+ notes: ". Changed your mind? `do task reopen <id>`.",
48632
+ run: async ({ args, requireClient }) => ({
48633
+ data: await requireClient().post(pathOf(inboxOperations.letGo, { id: requireArg(args, 0, "id") }), {}),
48634
+ render: renderRecord
48635
+ })
48636
+ })
48637
+ ];
48638
+
48639
+ // src/commands/incidents.ts
48640
+ var commands24 = [
47509
48641
  {
47510
48642
  spec: {
47511
48643
  path: ["incidents", "open"],
@@ -47734,7 +48866,7 @@ function journalDateArg(args, verb) {
47734
48866
  }
47735
48867
  return date;
47736
48868
  }
47737
- var commands24 = [
48869
+ var commands25 = [
47738
48870
  defineCommand({
47739
48871
  operation: journalOperations.day,
47740
48872
  path: ["journal", "day"],
@@ -48286,7 +49418,7 @@ var commands24 = [
48286
49418
  ];
48287
49419
 
48288
49420
  // src/commands/keys.ts
48289
- var commands25 = [
49421
+ var commands26 = [
48290
49422
  {
48291
49423
  spec: {
48292
49424
  path: ["keys", "list"],
@@ -48345,8 +49477,108 @@ var commands25 = [
48345
49477
  }
48346
49478
  ];
48347
49479
 
49480
+ // src/commands/log.ts
49481
+ function previewLine(preview, mode) {
49482
+ const effect = preview.effect ? paint(mode, "dim", ` \xB7 ${preview.effect}`) : "";
49483
+ return `${preview.label} \u2192 ${preview.goesTo ?? "?"}${effect}`;
49484
+ }
49485
+ function renderPreview(data, mode) {
49486
+ return `Would log: ${previewLine(data, mode)}`;
49487
+ }
49488
+ function renderWritten(data, mode) {
49489
+ const { preview, remembered } = data;
49490
+ const learnt = remembered ? `
49491
+ ${paint(mode, "dim", `Remembered "${remembered}": next time it goes there without asking.`)}` : "";
49492
+ return `Logged: ${previewLine(preview, mode)}${learnt}`;
49493
+ }
49494
+ function renderLogged(data, mode) {
49495
+ const rows = (data.data ?? data) || [];
49496
+ if (rows.length === 0)
49497
+ return paint(mode, "dim", "(nothing logged)");
49498
+ return rows.map((e) => `${e.at} ${e.what} ${paint(mode, "dim", e.where)}`).join(`
49499
+ `);
49500
+ }
49501
+ var KIND_FLAG = {
49502
+ name: "kind",
49503
+ type: "string",
49504
+ description: "The kind of an unknown word (pages, eggs, health, note, or a word taught before); remembered for the Space."
49505
+ };
49506
+ var commands27 = [
49507
+ {
49508
+ spec: {
49509
+ path: ["log"],
49510
+ summary: 'Log one line ("pages 34", "eggs 4"): preview where it goes and what it does, then write it into its section.',
49511
+ args: [{ name: "line", required: true, description: 'The line, e.g. "pages 34".' }],
49512
+ flags: [
49513
+ { name: "dry-run", type: "boolean", description: "Preview only; write nothing." },
49514
+ KIND_FLAG,
49515
+ {
49516
+ name: "destination",
49517
+ type: "string",
49518
+ description: "The section for an unknown word instead of a kind: habits, animals, health, journal."
49519
+ }
49520
+ ],
49521
+ scopes: [...logOperations.write.scopes],
49522
+ returns: "LogWriteResult",
49523
+ notes: `${routeOf(logOperations.preview)}, then ${routeOf(logOperations.write)}. An unknown word stops at the preview (needsKind): rerun with --kind. A line kept in Animals, Health or the Journal needs that section's scope too.`
49524
+ },
49525
+ run: async ({ args, flags, requireClient }) => {
49526
+ const text = args.join(" ").trim();
49527
+ if (!text)
49528
+ throw usageError('log needs a line, e.g. `do log "pages 34"`');
49529
+ const kind = flagString(flags, "kind");
49530
+ const destination = flagString(flags, "destination");
49531
+ const client = requireClient();
49532
+ const preview = await client.post(pathOf(logOperations.preview), { text });
49533
+ if (flagBool(flags, "dry-run"))
49534
+ return { data: preview, render: renderPreview };
49535
+ if (preview.needsKind && !kind && !destination) {
49536
+ throw usageError(`"${preview.word ?? text}" is a kind this Space doesn't know yet: rerun with --kind <pages|eggs|health|note> (see \`do log kinds\`).`, preview);
49537
+ }
49538
+ const written = await client.post(pathOf(logOperations.write), {
49539
+ text,
49540
+ ...kind ? { kind } : {},
49541
+ ...destination ? { destination } : {}
49542
+ });
49543
+ return { data: written, render: renderWritten };
49544
+ }
49545
+ },
49546
+ {
49547
+ spec: {
49548
+ path: ["log", "list"],
49549
+ summary: "What was logged through Log something on a day (today by default).",
49550
+ args: [],
49551
+ flags: [{ name: "day", type: "string", description: "The day, YYYY-MM-DD." }],
49552
+ scopes: [...logOperations.list.scopes],
49553
+ returns: "LogEntryView[]",
49554
+ notes: `${routeOf(logOperations.list)}. Health and Journal lines show only to their member.`
49555
+ },
49556
+ run: async ({ flags, requireClient }) => ({
49557
+ data: await requireClient().get(pathOf(logOperations.list), {
49558
+ query: { day: flagString(flags, "day") }
49559
+ }),
49560
+ render: renderLogged
49561
+ })
49562
+ },
49563
+ {
49564
+ spec: {
49565
+ path: ["log", "kinds"],
49566
+ summary: "The kinds this Space logs, and where each is kept.",
49567
+ args: [],
49568
+ flags: [],
49569
+ scopes: [...logOperations.kinds.scopes],
49570
+ returns: "LogKindView[]",
49571
+ notes: routeOf(logOperations.kinds)
49572
+ },
49573
+ run: async ({ requireClient }) => ({
49574
+ data: await requireClient().get(pathOf(logOperations.kinds)),
49575
+ render: renderRecord
49576
+ })
49577
+ }
49578
+ ];
49579
+
48348
49580
  // src/commands/mail-campaigns.ts
48349
- var commands26 = [
49581
+ var commands28 = [
48350
49582
  {
48351
49583
  spec: {
48352
49584
  path: ["mail-campaigns", "list"],
@@ -48661,7 +49893,7 @@ async function serveMcp(io, target) {
48661
49893
  }
48662
49894
 
48663
49895
  // src/commands/mcp.ts
48664
- var commands27 = [
49896
+ var commands29 = [
48665
49897
  {
48666
49898
  spec: {
48667
49899
  path: ["mcp"],
@@ -48706,7 +49938,7 @@ var commands27 = [
48706
49938
  ];
48707
49939
 
48708
49940
  // src/commands/members.ts
48709
- var commands28 = [
49941
+ var commands30 = [
48710
49942
  {
48711
49943
  spec: {
48712
49944
  path: ["invite", "create"],
@@ -48831,7 +50063,7 @@ var commands28 = [
48831
50063
  ];
48832
50064
 
48833
50065
  // src/commands/metrics.ts
48834
- var commands29 = [
50066
+ var commands31 = [
48835
50067
  {
48836
50068
  spec: {
48837
50069
  path: ["metrics", "list"],
@@ -48959,7 +50191,7 @@ async function setModuleVisibility(args, requireClient, on) {
48959
50191
  render: renderRecord
48960
50192
  };
48961
50193
  }
48962
- var commands30 = [
50194
+ var commands32 = [
48963
50195
  {
48964
50196
  spec: {
48965
50197
  path: ["modules", "list"],
@@ -49183,7 +50415,7 @@ function shareFlag(flags) {
49183
50415
  function localMonth(now) {
49184
50416
  return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}`;
49185
50417
  }
49186
- var commands31 = [
50418
+ var commands33 = [
49187
50419
  defineCommand({
49188
50420
  operation: moneyOperations.listAccounts,
49189
50421
  path: ["money", "accounts"],
@@ -50325,7 +51557,7 @@ var commands31 = [
50325
51557
  ];
50326
51558
 
50327
51559
  // src/commands/notes.ts
50328
- var commands32 = [
51560
+ var commands34 = [
50329
51561
  {
50330
51562
  spec: {
50331
51563
  path: ["notes", "backlinks"],
@@ -50447,7 +51679,7 @@ var commands32 = [
50447
51679
  ];
50448
51680
 
50449
51681
  // src/commands/orca.ts
50450
- var commands33 = [
51682
+ var commands35 = [
50451
51683
  {
50452
51684
  spec: {
50453
51685
  path: ["orca", "list"],
@@ -50777,7 +52009,7 @@ function linkBody(args) {
50777
52009
  return parseInput(familyTreeLinkInputSchema, { kind, a: ref, b: other });
50778
52010
  throw usageError("the kind is parent, child or partner");
50779
52011
  }
50780
- var commands34 = [
52012
+ var commands36 = [
50781
52013
  {
50782
52014
  spec: {
50783
52015
  path: ["people", "propose"],
@@ -51466,7 +52698,7 @@ var commands34 = [
51466
52698
 
51467
52699
  // src/commands/places.ts
51468
52700
  import { readFileSync as readFileSync10 } from "fs";
51469
- var commands35 = [
52701
+ var commands37 = [
51470
52702
  {
51471
52703
  spec: {
51472
52704
  path: ["places", "import"],
@@ -51523,7 +52755,7 @@ async function planDecision({ args, requireClient }, verb) {
51523
52755
  render: renderPlan
51524
52756
  };
51525
52757
  }
51526
- var commands36 = [
52758
+ var commands38 = [
51527
52759
  {
51528
52760
  spec: {
51529
52761
  path: ["plans", "list"],
@@ -51750,7 +52982,7 @@ var commands36 = [
51750
52982
  ];
51751
52983
 
51752
52984
  // src/commands/presets.ts
51753
- var commands37 = [
52985
+ var commands39 = [
51754
52986
  {
51755
52987
  spec: {
51756
52988
  path: ["presets", "list"],
@@ -51804,7 +53036,50 @@ var commands37 = [
51804
53036
  ];
51805
53037
 
51806
53038
  // src/commands/progress.ts
51807
- var commands38 = [
53039
+ var MARK = {
53040
+ done: "\u25CF",
53041
+ missed: "\u2715",
53042
+ open: "\u25CB",
53043
+ later: "\xB7",
53044
+ not_due: " "
53045
+ };
53046
+ var signed = (n) => n > 0 ? `+${n}` : String(n);
53047
+ function renderReviewSummary(data, mode) {
53048
+ const r = data;
53049
+ const n = r.numbers;
53050
+ const out = [
53051
+ paint(mode, "bold", `${r.label} \xB7 ${r.rangeLabel}`),
53052
+ ` Tasks done ${n.tasksDone} (${signed(n.tasksDoneDelta)} on the ${r.span} before)`,
53053
+ ` Habits ${n.habitTicks} of ${n.habitDue} due`,
53054
+ ` Projects ${n.projectsMoved} moved \xB7 Challenges ${n.challengesRunning} running`,
53055
+ "",
53056
+ paint(mode, "bold", "Last 8")
53057
+ ];
53058
+ const keyWidth = Math.max(0, ...r.series.map((p) => p.key.length));
53059
+ for (const p of r.series) {
53060
+ const row = ` ${p.key.padEnd(keyWidth)} ${String(p.tasksDone).padStart(3)} tasks ${String(p.habitTicks).padStart(3)} habits`;
53061
+ out.push(p.current ? paint(mode, "cyan", row) : row);
53062
+ }
53063
+ out.push("", paint(mode, "bold", "Streaks"));
53064
+ if (r.streaks.length === 0)
53065
+ out.push(paint(mode, "dim", " (no habits yet)"));
53066
+ const titleWidth = Math.max(0, ...r.streaks.map((s) => s.title.length));
53067
+ for (const s of r.streaks) {
53068
+ const marks = s.marks.map((m) => MARK[m.state]).join(" ");
53069
+ out.push(` ${s.title.padEnd(titleWidth)} ${s.unitLabel} \xB7 best ${s.best} ${marks} ${paint(mode, "dim", s.scheduleLabel)}`);
53070
+ }
53071
+ out.push("", paint(mode, "bold", "Projects"));
53072
+ if (r.projects.length === 0)
53073
+ out.push(paint(mode, "dim", " (no open projects)"));
53074
+ const projectWidth = Math.max(0, ...r.projects.map((p) => p.title.length));
53075
+ for (const p of r.projects) {
53076
+ const target = p.targetDate ? paint(mode, "dim", ` \xB7 by ${p.targetDate}`) : "";
53077
+ out.push(` ${p.title.padEnd(projectWidth)} ${p.done} of ${p.total} tasks${target}`);
53078
+ }
53079
+ return out.join(`
53080
+ `);
53081
+ }
53082
+ var commands40 = [
51808
53083
  {
51809
53084
  spec: {
51810
53085
  path: ["progress"],
@@ -51824,6 +53099,38 @@ var commands38 = [
51824
53099
  render: renderProgress
51825
53100
  })
51826
53101
  },
53102
+ {
53103
+ spec: {
53104
+ path: ["review", "summary"],
53105
+ summary: "Plan > Review: the week's or month's numbers, the last 8 periods, habit streaks and open projects.",
53106
+ args: [],
53107
+ flags: [
53108
+ { name: "span", type: "string", description: "week (default) or month." },
53109
+ {
53110
+ name: "date",
53111
+ type: "string",
53112
+ description: "A day in the period, YYYY-MM-DD; default today."
53113
+ }
53114
+ ],
53115
+ scopes: [...progressOperations.review.scopes],
53116
+ returns: "ReviewSummary",
53117
+ notes: `${routeOf(progressOperations.review)} \u2014 the ISO week (Monday to Sunday) or calendar month holding the day, in your saved timezone. Tasks done count yours and your agents' as Progress does; a streak reads \`N days\` for a daily habit and \`N in a row\` otherwise, and a day a habit isn't due never breaks it.`
53118
+ },
53119
+ run: async ({ flags, requireClient }) => {
53120
+ const span = flagString(flags, "span");
53121
+ const date = flagString(flags, "date");
53122
+ const query = parseInput(reviewSummaryQuerySchema, {
53123
+ ...span ? { span } : {},
53124
+ ...date ? { date } : {}
53125
+ });
53126
+ return {
53127
+ data: await requireClient().get(pathOf(progressOperations.review), {
53128
+ query: { span: query.span, ...query.date ? { date: query.date } : {} }
53129
+ }),
53130
+ render: renderReviewSummary
53131
+ };
53132
+ }
53133
+ },
51827
53134
  {
51828
53135
  spec: {
51829
53136
  path: ["progress", "day"],
@@ -51957,8 +53264,281 @@ var commands38 = [
51957
53264
  }
51958
53265
  ];
51959
53266
 
53267
+ // src/commands/projects.ts
53268
+ var PROJECT_ARG = {
53269
+ name: "id",
53270
+ required: true,
53271
+ description: "Project ref (DO-P7, any case) or ULID."
53272
+ };
53273
+ var UPDATE_STATUSES = projectUpdateStatusSchema.options.map((s) => s.replace(/_/g, "-"));
53274
+ function updateStatus(raw) {
53275
+ return raw.trim().toLowerCase().replace(/[-\s]+/g, "_");
53276
+ }
53277
+ function updateLine(u, mode) {
53278
+ const date = (u.createdAt ?? "").slice(0, 10);
53279
+ const status = (u.status ?? "").replace(/_/g, "-");
53280
+ const body = u.body ? ` ${u.body}` : "";
53281
+ const by = u.authorName ? paint(mode, "dim", ` \u2014 ${u.authorName}`) : "";
53282
+ return `${date} ${paint(mode, "bold", status)}${body}${by}`;
53283
+ }
53284
+ function renderUpdates(data, mode) {
53285
+ const updates = data ?? [];
53286
+ if (updates.length === 0)
53287
+ return paint(mode, "dim", "No updates yet.");
53288
+ return updates.map((u) => updateLine(u, mode)).join(`
53289
+ `);
53290
+ }
53291
+ var commands41 = [
53292
+ defineCommand({
53293
+ operation: projectOperations.update,
53294
+ path: ["projects", "set"],
53295
+ summary: "Set a project's priority.",
53296
+ returns: "ProjectView",
53297
+ args: [PROJECT_ARG],
53298
+ flags: [{ name: "priority", type: "string", description: "none, low, medium or high." }],
53299
+ run: async ({ args, flags, requireClient }) => {
53300
+ const id = requireArg(args, 0, "project id");
53301
+ const priority = flagString(flags, "priority");
53302
+ if (priority === undefined)
53303
+ throw usageError("projects set needs --priority");
53304
+ const body = parseInput(updateProjectInputSchema, {
53305
+ priority: priority.trim().toLowerCase()
53306
+ });
53307
+ return {
53308
+ data: await requireClient().patch(pathOf(projectOperations.update, { id }), body),
53309
+ render: renderRecord
53310
+ };
53311
+ }
53312
+ }),
53313
+ defineCommand({
53314
+ operation: projectOperations.createUpdate,
53315
+ path: ["projects", "update"],
53316
+ summary: "Post a project update (--status and a note), or change the project (--data, --leads).",
53317
+ returns: "ProjectUpdateView",
53318
+ args: [
53319
+ PROJECT_ARG,
53320
+ { name: "note", required: false, description: "The update's note (up to 2000 characters)." }
53321
+ ],
53322
+ flags: [
53323
+ {
53324
+ name: "status",
53325
+ type: "string",
53326
+ description: `Post an update with this status: ${UPDATE_STATUSES.join(", ")}.`
53327
+ },
53328
+ {
53329
+ name: "data",
53330
+ type: "string",
53331
+ description: "UpdateProjectInput as JSON, or @path to read it from a file."
53332
+ },
53333
+ {
53334
+ name: "leads",
53335
+ type: "string",
53336
+ description: "The project's leads, as member user ids, comma-separated; '' clears them."
53337
+ },
53338
+ { name: "dry-run", type: "boolean", description: "Show the change, send nothing." }
53339
+ ],
53340
+ notes: ". Shadows the generic `projects update`: with --status it posts an update, attributed to the key's Agent or member; without, it PATCHes /api/v1/projects/{id} with --data and --leads. Read updates with `do projects updates <id>`.",
53341
+ run: async ({ args, flags, requireClient }) => {
53342
+ const id = requireArg(args, 0, "project id");
53343
+ const note = args.slice(1).join(" ");
53344
+ const status = flagString(flags, "status");
53345
+ const leads = flagString(flags, "leads");
53346
+ const dryRun = flagBool(flags, "dry-run");
53347
+ if (status !== undefined) {
53348
+ if (flags.data !== undefined || leads !== undefined) {
53349
+ throw usageError("--status posts an update; change the project in a separate call");
53350
+ }
53351
+ const body = parseInput(createProjectUpdateInputSchema, {
53352
+ status: updateStatus(status),
53353
+ body: note
53354
+ });
53355
+ if (dryRun) {
53356
+ return {
53357
+ data: { dryRun: true, op: "create", entity: "project update", id, after: body },
53358
+ render: renderRecord
53359
+ };
53360
+ }
53361
+ return {
53362
+ data: await requireClient().post(pathOf(projectOperations.createUpdate, { id }), body),
53363
+ render: (data, mode) => updateLine(data, mode)
53364
+ };
53365
+ }
53366
+ if (note)
53367
+ throw usageError("a note needs --status (on-track, at-risk, \u2026)");
53368
+ const fields = readDataFlag(flags);
53369
+ if (leads !== undefined) {
53370
+ fields.leadUserIds = leads.split(",").map((s) => s.trim()).filter(Boolean);
53371
+ }
53372
+ const body = parseInput(updateProjectInputSchema, fields);
53373
+ if (Object.keys(body).length === 0) {
53374
+ throw usageError("nothing to change \u2014 pass --status, --data or --leads");
53375
+ }
53376
+ const client = requireClient();
53377
+ if (dryRun) {
53378
+ const before = await client.get(pathOf(projectOperations.get, { id }));
53379
+ return {
53380
+ data: {
53381
+ dryRun: true,
53382
+ op: "update",
53383
+ entity: "projects",
53384
+ id,
53385
+ before,
53386
+ after: mergePreview(before, body),
53387
+ changes: body
53388
+ },
53389
+ render: renderRecord
53390
+ };
53391
+ }
53392
+ return {
53393
+ data: await client.patch(pathOf(projectOperations.update, { id }), body),
53394
+ render: renderRecord
53395
+ };
53396
+ }
53397
+ }),
53398
+ defineCommand({
53399
+ operation: projectOperations.listUpdates,
53400
+ path: ["projects", "updates"],
53401
+ summary: "A project's updates, newest first.",
53402
+ returns: "ProjectUpdateView[]",
53403
+ args: [PROJECT_ARG],
53404
+ run: async ({ args, requireClient }) => {
53405
+ const id = requireArg(args, 0, "project id");
53406
+ return {
53407
+ data: await requireClient().get(pathOf(projectOperations.listUpdates, { id })),
53408
+ render: renderUpdates
53409
+ };
53410
+ }
53411
+ }),
53412
+ defineCommand({
53413
+ operation: projectOperations.removeUpdate,
53414
+ path: ["projects", "delete-update"],
53415
+ summary: "Delete one of your own updates on a project.",
53416
+ returns: "void",
53417
+ args: [PROJECT_ARG, { name: "updateId", required: true, description: "The update's ULID." }],
53418
+ notes: ". Needs --allow-destructive; only the update's author (the member or the Agent) may.",
53419
+ run: async ({ args, flags, requireClient }) => {
53420
+ const id = requireArg(args, 0, "project id");
53421
+ const updateId = requireArg(args, 1, "update id");
53422
+ assertDestructiveAllowed(flags, `delete update ${updateId} on ${id}`);
53423
+ await requireClient().delete(pathOf(projectOperations.removeUpdate, { id, updateId }));
53424
+ return { data: { ok: true, deleted: updateId }, render: renderRecord };
53425
+ }
53426
+ })
53427
+ ];
53428
+
53429
+ // src/commands/routines.ts
53430
+ var STATE_WORDS = {
53431
+ done: "done",
53432
+ running: "under way",
53433
+ upcoming: "to come",
53434
+ resting: "resting"
53435
+ };
53436
+ function renderRoutinesToday(data, mode) {
53437
+ const d = data;
53438
+ const list = d.routines ?? [];
53439
+ if (list.length === 0)
53440
+ return "No routines today.";
53441
+ return list.map((r) => {
53442
+ const progress = r.run ? ` \xB7 ${r.run.done}/${r.run.total}` : "";
53443
+ const head = `${r.start ?? "--:--"} ${r.title} ${paint(mode, "dim", `${STATE_WORDS[r.state] ?? r.state}${progress} ${r.id}`)}`;
53444
+ const steps = r.steps.map((s) => {
53445
+ const meta = s.meta ? paint(mode, "dim", ` \xB7 ${s.meta}`) : "";
53446
+ const kind = s.kind === "step" ? "" : ` [${s.kind}]`;
53447
+ return ` ${s.done ? "\u2713" : "\u25CB"} ${s.n}. ${s.title}${kind}${meta}`;
53448
+ });
53449
+ return [head, ...steps].join(`
53450
+ `);
53451
+ }).join(`
53452
+ `);
53453
+ }
53454
+ function renderRoutineTaskAdded(data) {
53455
+ const run = data;
53456
+ const last = run.steps?.at(-1)?.title ?? "the task";
53457
+ return `${run.routineTitle ?? "(routine)"}: ${last} is the last step of today's run.`;
53458
+ }
53459
+ var commands42 = [
53460
+ {
53461
+ spec: {
53462
+ path: ["routines", "today"],
53463
+ summary: "Today's routines by start: their state, steps (step, habit, task) and 14 days.",
53464
+ args: [],
53465
+ flags: [],
53466
+ scopes: [...routineOperations.today.scopes],
53467
+ returns: "RoutinesTodayResult",
53468
+ notes: `${routeOf(routineOperations.today)}. Today is your saved time zone's day. A routine is on it when its rule has it due, it has a slot today, or it ran today. Each step's \`kind\` is step, habit or task (a Work task); \`rhythm\` is its last 14 days, oldest first (all, some, none, off).`
53469
+ },
53470
+ run: async ({ requireClient }) => ({
53471
+ data: await requireClient().get(pathOf(routineOperations.today)),
53472
+ render: renderRoutinesToday
53473
+ })
53474
+ },
53475
+ {
53476
+ spec: {
53477
+ path: ["routines", "add-task"],
53478
+ summary: "Add a Work task due today to the end of today's run of a routine.",
53479
+ args: [
53480
+ { name: "id", required: true, description: "Routine ULID (`do routines today`)." },
53481
+ { name: "task", required: true, description: "Task ULID; it must be due today." }
53482
+ ],
53483
+ flags: [
53484
+ {
53485
+ name: "reason",
53486
+ type: "string",
53487
+ description: 'The line shown with it ("Call the plumber is due today in Work, so I added it to the end of this routine.").'
53488
+ }
53489
+ ],
53490
+ scopes: [...routineOperations.addTaskStep.scopes],
53491
+ returns: "RoutineRunView",
53492
+ notes: `${routeOf(routineOperations.addTaskStep)}. Starts today's run when there's none; the routine's own steps are unchanged. A task not due today is a 422; adding it again changes nothing. Ticking the step completes the task in Work, undo reopens it.`
53493
+ },
53494
+ run: async ({ args, flags, requireClient }) => {
53495
+ const id = requireArg(args, 0, "routine id");
53496
+ const taskId = requireArg(args, 1, "task id");
53497
+ const reason = flagString(flags, "reason");
53498
+ const input = parseInput(addRoutineTaskStepInputSchema, {
53499
+ taskId,
53500
+ ...reason !== undefined ? { reason } : {}
53501
+ });
53502
+ return {
53503
+ data: await requireClient().post(pathOf(routineOperations.addTaskStep, { id }), input),
53504
+ render: renderRoutineTaskAdded
53505
+ };
53506
+ }
53507
+ },
53508
+ {
53509
+ spec: {
53510
+ path: ["routines", "reorder"],
53511
+ summary: "Put a routine's steps in a new order.",
53512
+ args: [
53513
+ { name: "id", required: true, description: "Routine ULID." },
53514
+ {
53515
+ name: "steps",
53516
+ required: true,
53517
+ description: "Every step ULID of the routine, in the new order, space- or comma-separated."
53518
+ }
53519
+ ],
53520
+ flags: [],
53521
+ scopes: [...routineOperations.reorderSteps.scopes],
53522
+ returns: "RoutineView",
53523
+ notes: `${routeOf(routineOperations.reorderSteps)}. Name each step once; a list that isn't exactly the routine's steps is a 400. Runs already started keep their order.`
53524
+ },
53525
+ run: async ({ args, requireClient }) => {
53526
+ const id = requireArg(args, 0, "routine id");
53527
+ const stepIds = args.slice(1).flatMap((a) => a.split(",")).map((s) => s.trim()).filter(Boolean);
53528
+ if (stepIds.length === 0) {
53529
+ throw usageError("routines reorder needs the step ids in their new order, e.g. `do routines reorder <routine> <step> <step>`");
53530
+ }
53531
+ const input = parseInput(reorderRoutineStepsInputSchema, { stepIds });
53532
+ return {
53533
+ data: await requireClient().patch(pathOf(routineOperations.reorderSteps, { id }), input),
53534
+ render: (data) => `${data.title ?? "(routine)"}: steps reordered.`
53535
+ };
53536
+ }
53537
+ }
53538
+ ];
53539
+
51960
53540
  // src/commands/rsvp-answers.ts
51961
- var commands39 = [
53541
+ var commands43 = [
51962
53542
  {
51963
53543
  spec: {
51964
53544
  path: ["rsvp-answers", "list"],
@@ -52012,7 +53592,7 @@ var commands39 = [
52012
53592
  ];
52013
53593
 
52014
53594
  // src/commands/rsvp-links.ts
52015
- var commands40 = [
53595
+ var commands44 = [
52016
53596
  {
52017
53597
  spec: {
52018
53598
  path: ["rsvp-links", "list"],
@@ -52108,7 +53688,7 @@ var commands40 = [
52108
53688
  ];
52109
53689
 
52110
53690
  // src/commands/rsvp-questions.ts
52111
- var commands41 = [
53691
+ var commands45 = [
52112
53692
  {
52113
53693
  spec: {
52114
53694
  path: ["rsvp-questions", "presets"],
@@ -52680,7 +54260,7 @@ function sessionFlags(flags) {
52680
54260
  body.occurredOn = on;
52681
54261
  return body;
52682
54262
  }
52683
- var commands42 = [
54263
+ var commands46 = [
52684
54264
  {
52685
54265
  spec: {
52686
54266
  path: ["runs", "change"],
@@ -52997,7 +54577,7 @@ var commands42 = [
52997
54577
  ];
52998
54578
 
52999
54579
  // src/commands/search.ts
53000
- var commands43 = [
54580
+ var commands47 = [
53001
54581
  {
53002
54582
  spec: {
53003
54583
  path: ["search"],
@@ -53030,7 +54610,7 @@ var commands43 = [
53030
54610
  ];
53031
54611
 
53032
54612
  // src/commands/seating-proposals.ts
53033
- var commands44 = [
54613
+ var commands48 = [
53034
54614
  {
53035
54615
  spec: {
53036
54616
  path: ["seating-proposals", "list"],
@@ -53117,7 +54697,7 @@ var commands44 = [
53117
54697
  ];
53118
54698
 
53119
54699
  // src/commands/sensors.ts
53120
- var commands45 = [
54700
+ var commands49 = [
53121
54701
  {
53122
54702
  spec: {
53123
54703
  path: ["sensors", "status"],
@@ -53277,7 +54857,7 @@ var commands45 = [
53277
54857
  ];
53278
54858
 
53279
54859
  // src/commands/settings.ts
53280
- var commands46 = [
54860
+ var commands50 = [
53281
54861
  {
53282
54862
  spec: {
53283
54863
  path: ["settings", "get"],
@@ -53310,7 +54890,7 @@ var commands46 = [
53310
54890
  ];
53311
54891
 
53312
54892
  // src/commands/sharing.ts
53313
- var commands47 = [
54893
+ var commands51 = [
53314
54894
  {
53315
54895
  spec: {
53316
54896
  path: ["share"],
@@ -53443,7 +55023,7 @@ var commands47 = [
53443
55023
  ];
53444
55024
 
53445
55025
  // src/commands/tap-tags.ts
53446
- var commands48 = [
55026
+ var commands52 = [
53447
55027
  {
53448
55028
  spec: {
53449
55029
  path: ["tap-tags", "tap"],
@@ -53464,6 +55044,48 @@ var commands48 = [
53464
55044
  }
53465
55045
  ];
53466
55046
 
55047
+ // src/commands/task-blocks.ts
55048
+ var ID2 = { name: "id", required: true, description: "The blocked task's ref (DO-148) or ULID." };
55049
+ var BY = {
55050
+ name: "by",
55051
+ type: "string",
55052
+ description: "The blocking task's ref (DO-147) or ULID."
55053
+ };
55054
+ var commands53 = [
55055
+ defineCommand({
55056
+ operation: taskBlockOperations.addBlocker,
55057
+ path: ["tasks", "block"],
55058
+ summary: "Block a task by another, until that one is Done.",
55059
+ args: [ID2],
55060
+ flags: [BY],
55061
+ notes: " \u2014 a self-link or a loop (two tasks waiting on each other) is refused.",
55062
+ run: async ({ args, flags, requireClient }) => {
55063
+ const id = requireArg(args, 0, "task id");
55064
+ const by = requireFlag(flags, "by", "tasks block needs --by <task>");
55065
+ const body = parseInput(addTaskBlockerInputSchema, { taskId: by });
55066
+ return {
55067
+ data: await requireClient().post(pathOf(taskBlockOperations.addBlocker, { id }), body),
55068
+ render: renderTask
55069
+ };
55070
+ }
55071
+ }),
55072
+ defineCommand({
55073
+ operation: taskBlockOperations.removeBlocker,
55074
+ path: ["tasks", "unblock"],
55075
+ summary: "Lift a block: the other task no longer blocks this one.",
55076
+ args: [ID2],
55077
+ flags: [BY],
55078
+ run: async ({ args, flags, requireClient }) => {
55079
+ const id = requireArg(args, 0, "task id");
55080
+ const blockerId = requireFlag(flags, "by", "tasks unblock needs --by <task>");
55081
+ return {
55082
+ data: await requireClient().delete(pathOf(taskBlockOperations.removeBlocker, { id, blockerId })),
55083
+ render: renderTask
55084
+ };
55085
+ }
55086
+ })
55087
+ ];
55088
+
53467
55089
  // src/capture.ts
53468
55090
  function parseCapture2(input, now) {
53469
55091
  const parsed = parseCapture(input, now);
@@ -53474,7 +55096,7 @@ function parseCapture2(input, now) {
53474
55096
  }
53475
55097
 
53476
55098
  // src/commands/tasks.ts
53477
- var commands49 = [
55099
+ var commands54 = [
53478
55100
  {
53479
55101
  spec: {
53480
55102
  path: ["capture"],
@@ -53553,7 +55175,7 @@ var commands49 = [
53553
55175
  spec: {
53554
55176
  path: ["task", "done"],
53555
55177
  summary: "Mark a task done (idempotent \u2014 a no-op success if already done).",
53556
- args: [{ name: "id", required: true, description: "Task ULID." }],
55178
+ args: [{ name: "id", required: true, description: "Task ULID or ref (DO-142)." }],
53557
55179
  flags: [
53558
55180
  {
53559
55181
  name: "at",
@@ -53586,7 +55208,7 @@ var commands49 = [
53586
55208
  spec: {
53587
55209
  path: ["task", "cancel"],
53588
55210
  summary: "Drop a task: status cancelled, never counted as Landed.",
53589
- args: [{ name: "id", required: true, description: "Task ULID." }],
55211
+ args: [{ name: "id", required: true, description: "Task ULID or ref (DO-142)." }],
53590
55212
  flags: [],
53591
55213
  scopes: [...taskOperations.cancel.scopes],
53592
55214
  returns: "Task",
@@ -53604,7 +55226,7 @@ var commands49 = [
53604
55226
  spec: {
53605
55227
  path: ["tasks", "accept"],
53606
55228
  summary: "Accept a task Proposed by the agent: it joins the plan as suggested (day, Deadline, Area kept).",
53607
- args: [{ name: "id", required: true, description: "Task ULID." }],
55229
+ args: [{ name: "id", required: true, description: "Task ULID or ref (DO-142)." }],
53608
55230
  flags: [],
53609
55231
  scopes: [...taskOperations.accept.scopes],
53610
55232
  returns: "Task",
@@ -53622,7 +55244,7 @@ var commands49 = [
53622
55244
  spec: {
53623
55245
  path: ["tasks", "dismiss"],
53624
55246
  summary: "Not now on a task Proposed by the agent: it leaves the Inbox for a week, still proposed.",
53625
- args: [{ name: "id", required: true, description: "Task ULID." }],
55247
+ args: [{ name: "id", required: true, description: "Task ULID or ref (DO-142)." }],
53626
55248
  flags: [],
53627
55249
  scopes: [...taskOperations.notNow.scopes],
53628
55250
  returns: "Task",
@@ -53640,7 +55262,7 @@ var commands49 = [
53640
55262
  spec: {
53641
55263
  path: ["tasks", "ready"],
53642
55264
  summary: "Mark a task Ready for you: its Owner finished something the member should look at (a draft, a link, a worktree).",
53643
- args: [{ name: "id", required: true, description: "Task ULID." }],
55265
+ args: [{ name: "id", required: true, description: "Task ULID or ref (DO-142)." }],
53644
55266
  flags: [
53645
55267
  {
53646
55268
  name: "note",
@@ -53666,7 +55288,7 @@ var commands49 = [
53666
55288
  spec: {
53667
55289
  path: ["task", "reopen"],
53668
55290
  summary: "Reopen a done or cancelled task (status todo; clears completedAt).",
53669
- args: [{ name: "id", required: true, description: "Task ULID." }],
55291
+ args: [{ name: "id", required: true, description: "Task ULID or ref (DO-142)." }],
53670
55292
  flags: [],
53671
55293
  scopes: [...taskOperations.update.scopes],
53672
55294
  returns: "Task"
@@ -53684,7 +55306,7 @@ var commands49 = [
53684
55306
  spec: {
53685
55307
  path: ["task", "snooze"],
53686
55308
  summary: "Push a task's scheduled date forward.",
53687
- args: [{ name: "id", required: true, description: "Task ULID." }],
55309
+ args: [{ name: "id", required: true, description: "Task ULID or ref (DO-142)." }],
53688
55310
  flags: [
53689
55311
  { name: "days", type: "string", description: "Days from now to reschedule.", default: "1" },
53690
55312
  {
@@ -53721,7 +55343,7 @@ var commands49 = [
53721
55343
  spec: {
53722
55344
  path: ["task", "schedule"],
53723
55345
  summary: "Plan a task at a day or a time, so a timed one shows on the calendar.",
53724
- args: [{ name: "id", required: true, description: "Task ULID." }],
55346
+ args: [{ name: "id", required: true, description: "Task ULID or ref (DO-142)." }],
53725
55347
  flags: [
53726
55348
  {
53727
55349
  name: "at",
@@ -53779,11 +55401,65 @@ var commands49 = [
53779
55401
  render: renderRecord
53780
55402
  };
53781
55403
  }
53782
- }
55404
+ },
55405
+ defineCommand({
55406
+ operation: taskOperations.get,
55407
+ path: ["tasks", "show"],
55408
+ summary: "Show one task by its ref (DO-142) or ULID.",
55409
+ returns: "Task",
55410
+ args: [{ name: "id", required: true, description: "Task ref (DO-142, any case) or ULID." }],
55411
+ flags: [],
55412
+ run: async ({ args, requireClient }) => {
55413
+ const id = requireArg(args, 0, "task id");
55414
+ return {
55415
+ data: await requireClient().get(pathOf(taskOperations.get, { id })),
55416
+ render: renderTask
55417
+ };
55418
+ }
55419
+ }),
55420
+ defineCommand({
55421
+ operation: taskOperations.update,
55422
+ path: ["tasks", "set"],
55423
+ summary: "Set a task's status, priority or deliverable.",
55424
+ returns: "Task",
55425
+ args: [{ name: "id", required: true, description: "Task ref (DO-142, any case) or ULID." }],
55426
+ flags: [
55427
+ {
55428
+ name: "status",
55429
+ type: "string",
55430
+ description: "backlog, todo, in-progress, waiting or done (cancel drops it: `do task cancel`)."
55431
+ },
55432
+ { name: "priority", type: "string", description: "none, low, medium or high." },
55433
+ {
55434
+ name: "deliverable",
55435
+ type: "string",
55436
+ description: "What finishing it hands over, in a line; an empty string clears it."
55437
+ }
55438
+ ],
55439
+ notes: " \u2014 setting a waiting-on party moves an open task to waiting, clearing it moves it back to todo, unless --status says otherwise.",
55440
+ run: async ({ args, flags, requireClient }) => {
55441
+ const id = requireArg(args, 0, "task id");
55442
+ const status = flagString(flags, "status");
55443
+ const priority = flagString(flags, "priority");
55444
+ const deliverable = flagString(flags, "deliverable");
55445
+ if (status === undefined && priority === undefined && deliverable === undefined) {
55446
+ throw usageError("tasks set needs --status, --priority or --deliverable");
55447
+ }
55448
+ const body = parseInput(updateTaskInputSchema, {
55449
+ ...status !== undefined ? { status: taskStatusWire(status) } : {},
55450
+ ...priority !== undefined ? { priority: priority.trim().toLowerCase() } : {},
55451
+ ...deliverable !== undefined ? { deliverable: deliverable.trim() || null } : {}
55452
+ });
55453
+ return {
55454
+ data: await requireClient().patch(pathOf(taskOperations.update, { id }), body),
55455
+ render: renderTask
55456
+ };
55457
+ }
55458
+ })
53783
55459
  ];
53784
55460
 
53785
55461
  // src/commands/time-blocks.ts
53786
- var commands50 = [
55462
+ var commands55 = [
53787
55463
  {
53788
55464
  spec: {
53789
55465
  path: ["time-blocks", "record"],
@@ -53837,7 +55513,7 @@ var commands50 = [
53837
55513
  ];
53838
55514
 
53839
55515
  // src/today.ts
53840
- var OPEN_STATUSES = new Set(["todo", "in_progress"]);
55516
+ var OPEN_STATUSES = new Set(OPEN_TASK_STATUSES);
53841
55517
  function isTaskLike(value) {
53842
55518
  return typeof value === "object" && value !== null && typeof value.id === "string";
53843
55519
  }
@@ -53923,7 +55599,48 @@ var FROM_EARLIER_ANSWERS = ["today", "later", "someday", "let-go"];
53923
55599
  function localDayStart(now) {
53924
55600
  return new Date(now.getFullYear(), now.getMonth(), now.getDate());
53925
55601
  }
53926
- var commands51 = [
55602
+ function localWeek(now) {
55603
+ const back = (now.getDay() + 6) % 7;
55604
+ const from = new Date(now.getFullYear(), now.getMonth(), now.getDate() - back);
55605
+ return {
55606
+ from,
55607
+ to: new Date(from.getFullYear(), from.getMonth(), from.getDate() + 7)
55608
+ };
55609
+ }
55610
+ function landedWho(actor) {
55611
+ if (!actor || actor.kind === "you")
55612
+ return "you";
55613
+ if (actor.kind === "agent") {
55614
+ const name = actor.name ?? "agent";
55615
+ return actor.machine ? `${name} on ${actor.machine}` : name;
55616
+ }
55617
+ return actor.name ?? (actor.kind === "echo" ? "your agent" : "a member");
55618
+ }
55619
+ function renderLandedCounts(data, mode) {
55620
+ const base = renderLanded(data, mode);
55621
+ const c = data;
55622
+ if (c.itemsTotal === undefined)
55623
+ return base;
55624
+ const out = [
55625
+ base,
55626
+ "",
55627
+ paint(mode, "bold", `Across sections \xB7 ${c.itemsTotal} (today ${c.today ?? 0}, before ${c.previousWeekTotal ?? 0})`)
55628
+ ];
55629
+ for (const d of c.byDay ?? [])
55630
+ out.push(` ${d.date} ${d.you} by you \xB7 ${d.agents} by agents`);
55631
+ const sections = (c.bySection ?? []).map((s) => `${s.section} ${s.count}`).join(" \xB7 ");
55632
+ if (sections)
55633
+ out.push(` ${sections}`);
55634
+ const items = c.items ?? [];
55635
+ if (items.length > 0)
55636
+ out.push("");
55637
+ for (const i of items) {
55638
+ out.push(` ${i.title} ${paint(mode, "dim", `\xB7 ${i.where} \xB7 ${landedWho(i.actor)}`)}`);
55639
+ }
55640
+ return out.join(`
55641
+ `);
55642
+ }
55643
+ var commands56 = [
53927
55644
  {
53928
55645
  spec: {
53929
55646
  path: ["today"],
@@ -54059,22 +55776,47 @@ var commands51 = [
54059
55776
  {
54060
55777
  spec: {
54061
55778
  path: ["landed"],
54062
- summary: "What got done in a window (by completedAt), split by who finished it.",
55779
+ summary: "What got done in a window (by completedAt), split by who finished it, and counted by day and section.",
54063
55780
  args: [],
54064
55781
  flags: [
54065
- { name: "from", type: "string", description: "Window start (ISO); default today, UTC." },
54066
- { name: "to", type: "string", description: "Window end (ISO); default a day after from." }
55782
+ {
55783
+ name: "from",
55784
+ type: "string",
55785
+ description: "Window start (ISO); default today, UTC."
55786
+ },
55787
+ {
55788
+ name: "to",
55789
+ type: "string",
55790
+ description: "Window end (ISO); default a day after from."
55791
+ },
55792
+ {
55793
+ name: "week",
55794
+ type: "boolean",
55795
+ description: "This local week, Monday to Monday (instead of --from/--to)."
55796
+ },
55797
+ {
55798
+ name: "tz",
55799
+ type: "string",
55800
+ description: "IANA zone the days are cut in; default this machine's."
55801
+ }
54067
55802
  ],
54068
55803
  scopes: [...readOperations.landed.scopes],
54069
55804
  returns: "LandedResponse",
54070
- notes: "Cancelled tasks never count. For a week: --from <monday> --to <next monday>."
55805
+ notes: "Cancelled tasks never count. Across sections: tasks, habit check-ins, agent sessions, papers filed and shopping ticks; your agent's and agents' work counts and is marked. For a week: --week, or --from <monday> --to <next monday>."
54071
55806
  },
54072
- run: async ({ flags, requireClient }) => ({
54073
- data: await requireClient().get(pathOf(readOperations.landed), {
54074
- query: { from: flagString(flags, "from"), to: flagString(flags, "to") }
54075
- }),
54076
- render: renderLanded
54077
- })
55807
+ run: async ({ flags, requireClient }) => {
55808
+ const week = flagBool(flags, "week") ? localWeek(new Date) : null;
55809
+ return {
55810
+ data: await requireClient().get(pathOf(readOperations.landed), {
55811
+ query: {
55812
+ from: flagString(flags, "from") ?? week?.from.toISOString(),
55813
+ to: flagString(flags, "to") ?? week?.to.toISOString(),
55814
+ tz: flagString(flags, "tz") ?? Intl.DateTimeFormat().resolvedOptions().timeZone
55815
+ }
55816
+ }),
55817
+ render: renderLandedCounts
55818
+ };
55819
+ }
54078
55820
  },
54079
55821
  {
54080
55822
  spec: {
@@ -54139,8 +55881,257 @@ var commands51 = [
54139
55881
  }
54140
55882
  ];
54141
55883
 
55884
+ // src/commands/work-views.ts
55885
+ var FILTER_FLAGS = [
55886
+ {
55887
+ name: "status",
55888
+ type: "string",
55889
+ description: "Any of these statuses (backlog,todo,in-progress,waiting,done)."
55890
+ },
55891
+ {
55892
+ name: "not-status",
55893
+ type: "string",
55894
+ description: "None of these statuses, e.g. done."
55895
+ },
55896
+ { name: "area", type: "string", description: "Any of these Area ids, comma-separated." },
55897
+ {
55898
+ name: "project",
55899
+ type: "string",
55900
+ description: "Any of these Project ids, comma-separated."
55901
+ },
55902
+ {
55903
+ name: "label",
55904
+ type: "string",
55905
+ description: "Any of these labels (tags), comma-separated."
55906
+ },
55907
+ {
55908
+ name: "priority",
55909
+ type: "string",
55910
+ description: "Any of these priorities (none,low,medium,high)."
55911
+ },
55912
+ {
55913
+ name: "assignee",
55914
+ type: "string",
55915
+ description: "me, none, member:<id> or agent:<id>."
55916
+ },
55917
+ {
55918
+ name: "waiting",
55919
+ type: "string",
55920
+ description: "true = only Waiting, false = none of them."
55921
+ },
55922
+ {
55923
+ name: "due",
55924
+ type: "string",
55925
+ description: "overdue, today, this_week, next_week or none."
55926
+ },
55927
+ {
55928
+ name: "scheduled",
55929
+ type: "string",
55930
+ description: "The planned day: overdue, today, this_week, next_week or none."
55931
+ },
55932
+ {
55933
+ name: "when",
55934
+ type: "string",
55935
+ description: "Due or planned in: overdue, today, this_week, next_week or none."
55936
+ }
55937
+ ];
55938
+ var SHAPE_FLAGS = [
55939
+ {
55940
+ name: "group",
55941
+ type: "string",
55942
+ description: "area, status, project, priority, assignee or none."
55943
+ },
55944
+ { name: "view", type: "string", description: "list, board or timeline." },
55945
+ {
55946
+ name: "pin",
55947
+ type: "string",
55948
+ description: "true pins it to Work's top row, false unpins."
55949
+ }
55950
+ ];
55951
+ var list3 = (raw) => raw.split(",").map((s) => s.trim()).filter(Boolean);
55952
+ function filtersOf(flags) {
55953
+ const out = {};
55954
+ const lists = [
55955
+ ["status", "status", taskStatusWire],
55956
+ ["not-status", "notStatus", taskStatusWire],
55957
+ ["area", "areaIds", (v) => v],
55958
+ ["project", "projectIds", (v) => v],
55959
+ ["label", "labels", (v) => v],
55960
+ ["priority", "priority", (v) => v.toLowerCase()]
55961
+ ];
55962
+ for (const [flag, key, norm] of lists) {
55963
+ const values = flagStrings(flags, flag).flatMap(list3);
55964
+ if (values.length > 0)
55965
+ out[key] = values.map(norm);
55966
+ }
55967
+ const assignee = flagString(flags, "assignee");
55968
+ if (assignee)
55969
+ out.assignee = assignee.trim();
55970
+ const waiting = flagString(flags, "waiting");
55971
+ if (waiting !== undefined)
55972
+ out.waiting = waiting !== "false";
55973
+ for (const key of ["due", "scheduled", "when"]) {
55974
+ const v = flagString(flags, key);
55975
+ if (v)
55976
+ out[key] = v.trim().toLowerCase().replace(/-/g, "_");
55977
+ }
55978
+ return Object.keys(out).length > 0 ? out : null;
55979
+ }
55980
+ function shapeOf(flags) {
55981
+ const out = {};
55982
+ const group = flagString(flags, "group");
55983
+ if (group)
55984
+ out.groupBy = group.trim().toLowerCase();
55985
+ const view = flagString(flags, "view");
55986
+ if (view)
55987
+ out.view = view.trim().toLowerCase();
55988
+ const pin = flagString(flags, "pin");
55989
+ if (pin !== undefined)
55990
+ out.pinned = pin !== "false";
55991
+ return out;
55992
+ }
55993
+ async function findView(client, nameOrId) {
55994
+ const page = await client.get(pathOf(workViewOperations.list));
55995
+ const views = Array.isArray(page) ? page : page.data;
55996
+ const key = nameOrId.trim().toLowerCase();
55997
+ const view = views.find((v) => v.id === nameOrId) ?? views.find((v) => v.name.toLowerCase() === key);
55998
+ if (!view) {
55999
+ throw new CliError("not_found", `no Work view named \`${nameOrId}\` \u2014 run \`do views list\``, EXIT.NOT_FOUND);
56000
+ }
56001
+ return view;
56002
+ }
56003
+ function renderViews(data, mode) {
56004
+ const page = data;
56005
+ const views = Array.isArray(page) ? page : page.data;
56006
+ if (views.length === 0)
56007
+ return paint(mode, "dim", "(no views)");
56008
+ const width = Math.max(...views.map((v) => v.name.length));
56009
+ return views.map((v) => ` ${v.pinned ? "\u2605 " : " "}${v.name.padEnd(width)} ${String(v.count).padStart(4)} ${paint(mode, "dim", v.id)}`).join(`
56010
+ `);
56011
+ }
56012
+ function renderView(data, mode) {
56013
+ return renderViews([data], mode);
56014
+ }
56015
+ var commands57 = [
56016
+ defineCommand({
56017
+ operation: workViewOperations.list,
56018
+ path: ["views", "list"],
56019
+ summary: "List the Work saved views, each with its live count.",
56020
+ returns: "WorkView[]",
56021
+ run: async ({ requireClient }) => ({
56022
+ data: await requireClient().get(pathOf(workViewOperations.list)),
56023
+ render: renderViews
56024
+ })
56025
+ }),
56026
+ defineCommand({
56027
+ operation: taskOperations.list,
56028
+ path: ["views", "show"],
56029
+ summary: "The tasks a Work saved view matches (exactly its count).",
56030
+ args: [{ name: "view", required: true, description: "The view's name (any case) or id." }],
56031
+ scopes: [...new Set([...workViewOperations.list.scopes, ...taskOperations.list.scopes])],
56032
+ returns: "Task[]",
56033
+ notes: " with ?view=<id>, after the view is found by name in the views list.",
56034
+ run: async ({ args, requireClient }) => {
56035
+ const client = requireClient();
56036
+ const view = await findView(client, requireArg(args, 0, "view"));
56037
+ return {
56038
+ data: await client.getPage(pathOf(taskOperations.list), { query: { view: view.id } }),
56039
+ render: renderList
56040
+ };
56041
+ }
56042
+ }),
56043
+ defineCommand({
56044
+ operation: workViewOperations.create,
56045
+ path: ["views", "save"],
56046
+ summary: "Save a Work view: a name, its filters, and how Work groups and draws it.",
56047
+ args: [{ name: "name", required: true, description: "The view's name." }],
56048
+ flags: [...FILTER_FLAGS, ...SHAPE_FLAGS],
56049
+ returns: "WorkView",
56050
+ run: async ({ args, flags, requireClient }) => {
56051
+ const name = args.join(" ").trim();
56052
+ if (!name)
56053
+ throw usageError('views save needs a name, e.g. `do views save "Deep work"`');
56054
+ const body = parseInput(createWorkViewInputSchema, {
56055
+ name,
56056
+ filters: filtersOf(flags) ?? {},
56057
+ ...shapeOf(flags)
56058
+ });
56059
+ return {
56060
+ data: await requireClient().post(pathOf(workViewOperations.create), body),
56061
+ render: renderView
56062
+ };
56063
+ }
56064
+ }),
56065
+ defineCommand({
56066
+ operation: workViewOperations.update,
56067
+ path: ["views", "update"],
56068
+ summary: "Rename a Work view, or change its filters (replaced whole), grouping or kind.",
56069
+ args: [{ name: "view", required: true, description: "The view's name (any case) or id." }],
56070
+ flags: [
56071
+ { name: "name", type: "string", description: "Its new name." },
56072
+ ...FILTER_FLAGS,
56073
+ ...SHAPE_FLAGS
56074
+ ],
56075
+ returns: "WorkView",
56076
+ run: async ({ args, flags, requireClient }) => {
56077
+ const client = requireClient();
56078
+ const view = await findView(client, requireArg(args, 0, "view"));
56079
+ const filters = filtersOf(flags);
56080
+ const rename = flagString(flags, "name");
56081
+ const body = parseInput(updateWorkViewInputSchema, {
56082
+ ...rename !== undefined ? { name: rename } : {},
56083
+ ...filters ? { filters } : {},
56084
+ ...shapeOf(flags)
56085
+ });
56086
+ if (Object.keys(body).length === 0) {
56087
+ throw usageError("views update needs --name, a filter flag, --group, --view or --pin");
56088
+ }
56089
+ return {
56090
+ data: await client.patch(pathOf(workViewOperations.update, { id: view.id }), body),
56091
+ render: renderView
56092
+ };
56093
+ }
56094
+ }),
56095
+ defineCommand({
56096
+ operation: workViewOperations.reorder,
56097
+ path: ["views", "reorder"],
56098
+ summary: "Put the Work views in this order (names or ids); the rest keep theirs after.",
56099
+ args: [{ name: "views", required: true, description: "Views by name or id, in order." }],
56100
+ returns: "WorkView[]",
56101
+ run: async ({ args, requireClient }) => {
56102
+ if (args.length === 0)
56103
+ throw usageError("views reorder needs the views in order");
56104
+ const client = requireClient();
56105
+ const ids = [];
56106
+ for (const arg of args)
56107
+ ids.push((await findView(client, arg)).id);
56108
+ const body = parseInput(reorderWorkViewsInputSchema, { ids });
56109
+ return {
56110
+ data: await client.post(pathOf(workViewOperations.reorder), body),
56111
+ render: renderViews
56112
+ };
56113
+ }
56114
+ }),
56115
+ defineCommand({
56116
+ operation: workViewOperations.remove,
56117
+ path: ["views", "delete"],
56118
+ summary: "Delete a Work view (a default one too; it isn't seeded again).",
56119
+ args: [{ name: "view", required: true, description: "The view's name (any case) or id." }],
56120
+ returns: "{ ok, deleted }",
56121
+ run: async ({ args, requireClient }) => {
56122
+ const client = requireClient();
56123
+ const view = await findView(client, requireArg(args, 0, "view"));
56124
+ await client.delete(pathOf(workViewOperations.remove, { id: view.id }));
56125
+ return {
56126
+ data: { ok: true, deleted: view.id, name: view.name },
56127
+ render: () => `Deleted the view ${view.name}.`
56128
+ };
56129
+ }
56130
+ })
56131
+ ];
56132
+
54142
56133
  // src/commands/worktree-links.ts
54143
- var commands52 = [
56134
+ var commands58 = [
54144
56135
  {
54145
56136
  spec: {
54146
56137
  path: ["worktree-links", "list"],
@@ -54298,7 +56289,13 @@ var FEATURE_COMMANDS = [
54298
56289
  ...commands49,
54299
56290
  ...commands50,
54300
56291
  ...commands51,
54301
- ...commands52
56292
+ ...commands52,
56293
+ ...commands53,
56294
+ ...commands54,
56295
+ ...commands55,
56296
+ ...commands56,
56297
+ ...commands57,
56298
+ ...commands58
54302
56299
  ];
54303
56300
 
54304
56301
  // src/registry.ts
@@ -54424,7 +56421,8 @@ async function dispatchEntity(entity, rest, flags, requireClient) {
54424
56421
  query[key] = f.sends;
54425
56422
  continue;
54426
56423
  }
54427
- const value = flags[f.name] === true ? "true" : flagString(flags, f.name);
56424
+ const raw = flags[f.name] === true ? "true" : flagString(flags, f.name);
56425
+ const value = raw !== undefined && f.normalize ? f.normalize(raw) : raw;
54428
56426
  if (value !== undefined)
54429
56427
  query[key] = value;
54430
56428
  else if (f.required)