@wspc/cli 0.0.12 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import { Command as Command59 } from "commander";
4
+ import { Command as Command62 } from "commander";
5
5
 
6
6
  // src/generated/cli/invite/accept.ts
7
7
  import { Command } from "commander";
@@ -1078,6 +1078,15 @@ var recurrenceRuleList = (options) => (options.client ?? client).get({
1078
1078
  url: "/todo/recurrence-rules",
1079
1079
  ...options
1080
1080
  });
1081
+ var recurrenceRuleCreate = (options) => (options?.client ?? client).post({
1082
+ security: [{ scheme: "bearer", type: "http" }],
1083
+ url: "/todo/recurrence-rules",
1084
+ ...options,
1085
+ headers: {
1086
+ "Content-Type": "application/json",
1087
+ ...options?.headers
1088
+ }
1089
+ });
1081
1090
  var todoList = (options) => (options.client ?? client).get({
1082
1091
  security: [{ scheme: "bearer", type: "http" }],
1083
1092
  url: "/todo/items",
@@ -1111,6 +1120,20 @@ var todoCommentUpdate = (options) => (options.client ?? client).patch({
1111
1120
  ...options.headers
1112
1121
  }
1113
1122
  });
1123
+ var recurrenceRuleDelete = (options) => (options.client ?? client).delete({
1124
+ security: [{ scheme: "bearer", type: "http" }],
1125
+ url: "/todo/recurrence-rules/{id}",
1126
+ ...options,
1127
+ headers: {
1128
+ "Content-Type": "application/json",
1129
+ ...options.headers
1130
+ }
1131
+ });
1132
+ var recurrenceRuleGet = (options) => (options.client ?? client).get({
1133
+ security: [{ scheme: "bearer", type: "http" }],
1134
+ url: "/todo/recurrence-rules/{id}",
1135
+ ...options
1136
+ });
1114
1137
  var todoDelete = (options) => (options.client ?? client).delete({
1115
1138
  security: [{ scheme: "bearer", type: "http" }],
1116
1139
  url: "/todo/items/{id}",
@@ -1230,9 +1253,9 @@ var ConfigStore = class {
1230
1253
  };
1231
1254
 
1232
1255
  // src/version.ts
1233
- var VERSION = "0.0.12";
1234
- var SPEC_SHA = "869ea6a3";
1235
- var SPEC_FETCHED_AT = "2026-06-09T06:41:08.172Z";
1256
+ var VERSION = "0.0.13";
1257
+ var SPEC_SHA = "7842b7a9";
1258
+ var SPEC_FETCHED_AT = "2026-06-09T08:10:52.164Z";
1236
1259
  var API_BASE = "https://api.wspc.ai";
1237
1260
 
1238
1261
  // src/index.ts
@@ -2911,9 +2934,36 @@ var projectListCommand = new Command41("ls").description("List projects").option
2911
2934
  render({ kind: "project_list", display: { "shape": "list", "columns": ["id", "name", "default_todo_type_id"], "format": { "id": "id-short", "name": "truncate", "default_todo_type_id": "id-short" }, "emptyMessage": "no projects" } }, result.data);
2912
2935
  });
2913
2936
 
2914
- // src/generated/cli/todo/rule/ls.ts
2937
+ // src/generated/cli/todo/rule/add.ts
2915
2938
  import { Command as Command42 } from "commander";
2916
- var recurrenceRuleListCommand = new Command42("ls").description("List recurring todo rules").option("--project-id <value>", "Project id filter. Required. Unknown, cross-organization, or soft-deleted project ids return NOT_FOUND.").option("--user-id <value>", "user_id").action(async (opts) => {
2939
+ var recurrenceRuleCreateCommand = new Command42("add").description("Create a recurring todo rule").argument("<title>", "title").option("--rrule <value>", "rrule").option("--dtstart <value>", "dtstart").option("--description <value>", "description").option("--parent-id <value>", "parent_id").option("-p, --project <value>", "Project for the recurrence rule, its template todo, and all materialized instances. Must be an active project in the caller's organization.").option("-t, --type <value>", "type_id").action(async (title, opts) => {
2940
+ const client2 = await loadSdkClient();
2941
+ const result = await recurrenceRuleCreate({
2942
+ client: client2._rawClient,
2943
+ body: {
2944
+ title,
2945
+ rrule: opts.rrule,
2946
+ dtstart: opts.dtstart,
2947
+ description: opts.description,
2948
+ parent_id: opts.parentId,
2949
+ project_id: opts.project,
2950
+ type_id: opts.type
2951
+ }
2952
+ });
2953
+ if (result.error || !result.response?.ok) {
2954
+ process.stderr.write(
2955
+ `HTTP ${result.response?.status ?? "?"}: ${JSON.stringify(result.error ?? "unknown error", null, 2)}
2956
+ `
2957
+ );
2958
+ process.exitCode = 1;
2959
+ return;
2960
+ }
2961
+ render({ kind: "recurrence_rule_create", display: void 0 }, result.data);
2962
+ });
2963
+
2964
+ // src/generated/cli/todo/rule/ls.ts
2965
+ import { Command as Command43 } from "commander";
2966
+ var recurrenceRuleListCommand = new Command43("ls").description("List recurring todo rules").option("--project-id <value>", "Project id filter. Required. Unknown, cross-organization, or soft-deleted project ids return NOT_FOUND.").option("--user-id <value>", "user_id").action(async (opts) => {
2917
2967
  const client2 = await loadSdkClient();
2918
2968
  const result = await recurrenceRuleList({
2919
2969
  client: client2._rawClient,
@@ -2930,12 +2980,26 @@ var recurrenceRuleListCommand = new Command42("ls").description("List recurring
2930
2980
  process.exitCode = 1;
2931
2981
  return;
2932
2982
  }
2933
- render({ kind: "recurrence_rule_list", display: { "shape": "list", "columns": ["id", "rrule", "dtstart"], "format": { "id": "id-short", "rrule": "truncate" }, "emptyMessage": "no recurrence rules" } }, result.data);
2983
+ render({ kind: "recurrence_rule_list", display: { "shape": "list", "columns": ["id", "rrule", "dtstart", "type_id"], "format": { "id": "id-short", "rrule": "truncate", "type_id": "id-short" }, "emptyMessage": "no recurrence rules" } }, result.data);
2934
2984
  });
2935
2985
 
2936
2986
  // src/generated/cli/todo/add.ts
2937
- import { Command as Command43 } from "commander";
2938
- var todoCreateCommand = new Command43("add").description("Create a todo").argument("<title>", "title").option("-p, --project <value>", "Project id to assign this todo to. It must be an active project in the caller's organization.").option("--description <value>", "Free-form details about the todo. Fully supports GFM Markdown (tables, strikethrough, task lists). Stored verbatim; client applications are responsible for rendering. Optional. Passing `null` is strictly rejected.").option("--parent-id <value>", "Parent todo ID (`tod_<ULID>`) to attach this todo as a child under another todo. Omit or pass `null` to create a root-level todo. Nesting is limited to one level; attempting to set a child todo as a parent will trigger `PARENT_IS_CHILD`.").option("--status <value>", "Initial status of the todo. Omit to default to `open`. Allowed values: `open`, `in_progress`, `done`, `cancelled`.").option("--due-at <value>", 'Optional calendar due date in ISO date-only format (`YYYY-MM-DD`). Stored without timezone offsets to represent the same local calendar day globally. Pass `""` or omit the field to skip setting a due date. Passing `null` is strictly rejected.').option("--type-id <value>", "Type id this todo belongs to. Omit to use the project's default type. When project_id is also supplied, the type must belong to the same project. New server-generated type ids use typ_<ULID>; legacy ids remain accepted.").option("--custom-fields <value>", "Custom field values keyed by the field's immutable `key` (not the human `label`). Each value must match the declared field type: string fields require string values, and string_array fields require string arrays. Providing a key that is not declared on the resolved todo type is strictly rejected with `UNDECLARED_FIELD`. Missing required fields that lack a default value are rejected with `FIELD_REQUIRED`. Defaults declared on the type are auto-applied at create time.").action(async (title, opts) => {
2987
+ import { Command as Command44 } from "commander";
2988
+
2989
+ // src/handwritten/utils/parse-json-field.ts
2990
+ function parseJsonField(raw, flag) {
2991
+ if (raw === void 0) return void 0;
2992
+ try {
2993
+ return JSON.parse(raw);
2994
+ } catch {
2995
+ process.stderr.write(`Invalid JSON for --${flag}: ${raw}
2996
+ `);
2997
+ process.exit(1);
2998
+ }
2999
+ }
3000
+
3001
+ // src/generated/cli/todo/add.ts
3002
+ var todoCreateCommand = new Command44("add").description("Create a todo").argument("<title>", "title").option("-p, --project <value>", "Project id to assign this todo to. It must be an active project in the caller's organization.").option("--description <value>", "Free-form details about the todo. Fully supports GFM Markdown (tables, strikethrough, task lists). Stored verbatim; client applications are responsible for rendering. Optional. Passing `null` is strictly rejected.").option("--parent-id <value>", "Parent todo ID (`tod_<ULID>`) to attach this todo as a child under another todo. Omit or pass `null` to create a root-level todo. Nesting is limited to one level; attempting to set a child todo as a parent will trigger `PARENT_IS_CHILD`. To make a subtask appear on every occurrence of a recurring rule, set this to that rule's template todo id (the template id returned when the rule is created); the server re-materializes future occurrences so each carries the subtask.").option("--status <value>", "Initial status of the todo. Omit to default to `open`. Allowed values: `open`, `in_progress`, `done`, `cancelled`.").option("--due-at <value>", 'Optional calendar due date in ISO date-only format (`YYYY-MM-DD`). Stored without timezone offsets to represent the same local calendar day globally. Pass `""` or omit the field to skip setting a due date. Passing `null` is strictly rejected.').option("--type-id <value>", "Type id this todo belongs to. Omit to use the project's default type. When project_id is also supplied, the type must belong to the same project. New server-generated type ids use typ_<ULID>; legacy ids remain accepted.").option("--custom-fields <value>", "Custom field values keyed by the field's immutable `key` (not the human `label`). Each value must match the declared field type: string fields require string values, and string_array fields require string arrays. Providing a key that is not declared on the resolved todo type is strictly rejected with `UNDECLARED_FIELD`. Missing required fields that lack a default value are rejected with `FIELD_REQUIRED`. Defaults declared on the type are auto-applied at create time.").action(async (title, opts) => {
2939
3003
  const client2 = await loadSdkClient();
2940
3004
  const result = await todoCreate({
2941
3005
  client: client2._rawClient,
@@ -2947,7 +3011,7 @@ var todoCreateCommand = new Command43("add").description("Create a todo").argume
2947
3011
  status: opts.status,
2948
3012
  due_at: opts.dueAt,
2949
3013
  type_id: opts.typeId,
2950
- custom_fields: opts.customFields
3014
+ custom_fields: parseJsonField(opts.customFields, "custom-fields")
2951
3015
  }
2952
3016
  });
2953
3017
  if (result.error || !result.response?.ok) {
@@ -2962,8 +3026,8 @@ var todoCreateCommand = new Command43("add").description("Create a todo").argume
2962
3026
  });
2963
3027
 
2964
3028
  // src/generated/cli/todo/ls.ts
2965
- import { Command as Command44 } from "commander";
2966
- var todoListCommand = new Command44("ls").description("List todos with filters").option("-p, --project <value>", "Filter by project. Required. Unknown, cross-organization, or soft-deleted project ids return NOT_FOUND.").option("--user-id <value>", "user_id").option("--parent-id <value>", "parent_id").option("-s, --status <value>", "status").option("--include-deleted <value>", "include_deleted").option("--include-templates <value>", "include_templates").option("--due-after <value>", "due_after").option("--due-before <value>", "due_before").option("--type-id <value>", "type_id").option("--sort-by <value>", "sort_by").option("--order <value>", "order").option("--include-orphan-fields <value>", "include_orphan_fields").option("--limit <value>", "Max todos to return. Clamped to [1, 200]. Default 50 server-side.").option("--cursor <value>", "Opaque pagination cursor returned in `next_cursor` of a previous response.").action(async (opts) => {
3029
+ import { Command as Command45 } from "commander";
3030
+ var todoListCommand = new Command45("ls").description("List todos with filters").option("-p, --project <value>", "Filter by project. Required. Unknown, cross-organization, or soft-deleted project ids return NOT_FOUND.").option("--user-id <value>", "user_id").option("--parent-id <value>", "parent_id").option("-s, --status <value>", "status").option("--include-deleted <value>", "include_deleted").option("--include-templates <value>", "include_templates").option("--due-after <value>", "due_after").option("--due-before <value>", "due_before").option("--type-id <value>", "type_id").option("--sort-by <value>", "sort_by").option("--order <value>", "order").option("--include-orphan-fields <value>", "include_orphan_fields").option("--limit <value>", "Max todos to return. Clamped to [1, 200]. Default 50 server-side.").option("--cursor <value>", "Opaque pagination cursor returned in `next_cursor` of a previous response.").action(async (opts) => {
2967
3031
  const client2 = await loadSdkClient();
2968
3032
  const result = await todoList({
2969
3033
  client: client2._rawClient,
@@ -2996,8 +3060,8 @@ var todoListCommand = new Command44("ls").description("List todos with filters")
2996
3060
  });
2997
3061
 
2998
3062
  // src/generated/cli/todo/type/ls.ts
2999
- import { Command as Command45 } from "commander";
3000
- var todoTypeListCommand = new Command45("ls").description("List todo types").option("--project-id <value>", "Project id filter. Required. Unknown, cross-organization, or soft-deleted project ids return NOT_FOUND.").option("--user-id <value>", "user_id").option("--include-deleted <value>", "include_deleted").action(async (opts) => {
3063
+ import { Command as Command46 } from "commander";
3064
+ var todoTypeListCommand = new Command46("ls").description("List todo types").option("--project-id <value>", "Project id filter. Required. Unknown, cross-organization, or soft-deleted project ids return NOT_FOUND.").option("--user-id <value>", "user_id").option("--include-deleted <value>", "include_deleted").action(async (opts) => {
3001
3065
  const client2 = await loadSdkClient();
3002
3066
  const result = await todoTypeList({
3003
3067
  client: client2._rawClient,
@@ -3019,8 +3083,8 @@ var todoTypeListCommand = new Command45("ls").description("List todo types").opt
3019
3083
  });
3020
3084
 
3021
3085
  // src/generated/cli/todo/comment/rm.ts
3022
- import { Command as Command46 } from "commander";
3023
- var todoCommentDeleteCommand = new Command46("rm").description("Soft-delete a comment").argument("<id>", "id").action(async (id, opts) => {
3086
+ import { Command as Command47 } from "commander";
3087
+ var todoCommentDeleteCommand = new Command47("rm").description("Soft-delete a comment").argument("<id>", "id").action(async (id, opts) => {
3024
3088
  const client2 = await loadSdkClient();
3025
3089
  const result = await todoCommentDelete({
3026
3090
  client: client2._rawClient,
@@ -3040,8 +3104,8 @@ var todoCommentDeleteCommand = new Command46("rm").description("Soft-delete a co
3040
3104
  });
3041
3105
 
3042
3106
  // src/generated/cli/todo/comment/edit.ts
3043
- import { Command as Command47 } from "commander";
3044
- var todoCommentUpdateCommand = new Command47("edit").description("Edit a comment").argument("<id>", "id").argument("<content>", "content").action(async (id, content, opts) => {
3107
+ import { Command as Command48 } from "commander";
3108
+ var todoCommentUpdateCommand = new Command48("edit").description("Edit a comment").argument("<id>", "id").argument("<content>", "content").action(async (id, content, opts) => {
3045
3109
  const client2 = await loadSdkClient();
3046
3110
  const result = await todoCommentUpdate({
3047
3111
  client: client2._rawClient,
@@ -3063,9 +3127,54 @@ var todoCommentUpdateCommand = new Command47("edit").description("Edit a comment
3063
3127
  render({ kind: "todo_comment_update", display: { "shape": "object", "format": { "id": "id-short", "todo_id": "id-short", "user_id": "id-short", "created_at": "relative-time", "updated_at": "relative-time", "deleted_at": "relative-time" } } }, result.data);
3064
3128
  });
3065
3129
 
3130
+ // src/generated/cli/todo/rule/rm.ts
3131
+ import { Command as Command49 } from "commander";
3132
+ var recurrenceRuleDeleteCommand = new Command49("rm").description("Delete a recurring todo rule").argument("<id>", "id").option("--expected-version <value>", "expected_version").action(async (id, opts) => {
3133
+ const client2 = await loadSdkClient();
3134
+ const result = await recurrenceRuleDelete({
3135
+ client: client2._rawClient,
3136
+ path: {
3137
+ id
3138
+ },
3139
+ body: {
3140
+ expected_version: opts.expectedVersion
3141
+ }
3142
+ });
3143
+ if (result.error || !result.response?.ok) {
3144
+ process.stderr.write(
3145
+ `HTTP ${result.response?.status ?? "?"}: ${JSON.stringify(result.error ?? "unknown error", null, 2)}
3146
+ `
3147
+ );
3148
+ process.exitCode = 1;
3149
+ return;
3150
+ }
3151
+ render({ kind: "recurrence_rule_delete", display: void 0 }, result.data);
3152
+ });
3153
+
3154
+ // src/generated/cli/todo/rule/show.ts
3155
+ import { Command as Command50 } from "commander";
3156
+ var recurrenceRuleGetCommand = new Command50("show").description("Get a recurring todo rule").argument("<id>", "id").action(async (id, opts) => {
3157
+ const client2 = await loadSdkClient();
3158
+ const result = await recurrenceRuleGet({
3159
+ client: client2._rawClient,
3160
+ path: {
3161
+ id
3162
+ }
3163
+ });
3164
+ if (result.error || !result.response?.ok) {
3165
+ process.stderr.write(
3166
+ `HTTP ${result.response?.status ?? "?"}: ${JSON.stringify(result.error ?? "unknown error", null, 2)}
3167
+ `
3168
+ );
3169
+ process.exitCode = 1;
3170
+ return;
3171
+ }
3172
+ render({ kind: "recurrence_rule_get", display: { "shape": "object", "format": { "id": "id-short", "type_id": "id-short" } } }, result.data);
3173
+ });
3174
+
3066
3175
  // src/generated/cli/todo/rm.ts
3067
- import { Command as Command48 } from "commander";
3068
- var todoDeleteCommand = new Command48("rm").description("Soft-delete a todo").argument("<id>", "id").option("--expected-version <value>", "expected_version").option("--cascade <value>", "cascade").action(async (id, opts) => {
3176
+ import { Command as Command51 } from "commander";
3177
+ var todoDeleteCommand = new Command51("rm").description("Soft-delete a todo").argument("<id>", "id").option("--expected-version <value>", "expected_version").option("--cascade <value>", "cascade").action(async (id, opts) => {
3069
3178
  const client2 = await loadSdkClient();
3070
3179
  const result = await todoDelete({
3071
3180
  client: client2._rawClient,
@@ -3089,8 +3198,8 @@ var todoDeleteCommand = new Command48("rm").description("Soft-delete a todo").ar
3089
3198
  });
3090
3199
 
3091
3200
  // src/generated/cli/todo/show.ts
3092
- import { Command as Command49 } from "commander";
3093
- var todoGetCommand = new Command49("show").description("Get a todo by id").argument("<id>", "id").option("--include-deleted <value>", "include_deleted").option("--include-orphan-fields <value>", "include_orphan_fields").action(async (id, opts) => {
3201
+ import { Command as Command52 } from "commander";
3202
+ var todoGetCommand = new Command52("show").description("Get a todo by id").argument("<id>", "id").option("--include-deleted <value>", "include_deleted").option("--include-orphan-fields <value>", "include_orphan_fields").action(async (id, opts) => {
3094
3203
  const client2 = await loadSdkClient();
3095
3204
  const result = await todoGet({
3096
3205
  client: client2._rawClient,
@@ -3115,8 +3224,8 @@ var todoGetCommand = new Command49("show").description("Get a todo by id").argum
3115
3224
  });
3116
3225
 
3117
3226
  // src/generated/cli/todo/update.ts
3118
- import { Command as Command50 } from "commander";
3119
- var todoUpdateCommand = new Command50("update").description("Update a todo").argument("<id>", "id").option("--expected-version <value>", "expected_version").option("--title <value>", "New title. Omit to leave the existing title unchanged. Must be non-empty when supplied.").option("--description <value>", 'New description. Markdown formatted (CommonMark + GFM tables, strikethrough, task lists). Pass empty string `""` explicitly to clear an existing description, or omit to leave unchanged. Passing `null` is strictly rejected.').option("--parent-id <value>", "Re-parent the todo. Pass a valid parent ID to attach under another todo, pass `null` to move it back to the root level, or omit to leave unchanged. Nesting is limited to one level; attempting to set a child todo as a parent will trigger `PARENT_IS_CHILD`.").option("--status <value>", "New status of the todo. Allowed transitions: `open` \u2794 `in_progress` \u2794 `done`. `cancelled` represents a terminal state. Transitioning to `done` automatically emits a `captureTodoCompleted` analytics event. Omit to leave the existing status unchanged.").option("--due-at <value>", 'Update calendar due date in ISO date-only format (`YYYY-MM-DD`). Pass `""` explicitly to clear an existing due date, or omit to leave it unchanged. Passing `null` is strictly rejected.').option("--type-id <value>", "Re-assign this todo to a different active type. The new type must belong to the todo's same project; otherwise the request fails with TYPE_PROJECT_MISMATCH. New server-generated type ids use typ_<ULID>; legacy ids remain accepted.").option("--custom-fields <value>", "PATCH semantics: only the keys present in this map change. Pass `null` for a key (e.g. `custom_fields: { priority: null }`) to explicitly delete that custom field value. Array values are replaced wholesale with no element-level diff. Providing a key that is not declared on the effective todo type is rejected with `UNDECLARED_FIELD`.").option("--user-id <value>", "Reassign the owner (assignee) user ID of this todo. Target user must belong to the same organization.").action(async (id, opts) => {
3227
+ import { Command as Command53 } from "commander";
3228
+ var todoUpdateCommand = new Command53("update").description("Update a todo").argument("<id>", "id").option("--expected-version <value>", "expected_version").option("--title <value>", "New title. Omit to leave the existing title unchanged. Must be non-empty when supplied.").option("--description <value>", 'New description. Markdown formatted (CommonMark + GFM tables, strikethrough, task lists). Pass empty string `""` explicitly to clear an existing description, or omit to leave unchanged. Passing `null` is strictly rejected.').option("--parent-id <value>", "Re-parent the todo. Pass a valid parent ID to attach under another todo, pass `null` to move it back to the root level, or omit to leave unchanged. Nesting is limited to one level; attempting to set a child todo as a parent will trigger `PARENT_IS_CHILD`.").option("--status <value>", "New status of the todo. Allowed transitions: `open` \u2794 `in_progress` \u2794 `done`. `cancelled` represents a terminal state. Transitioning to `done` automatically emits a `captureTodoCompleted` analytics event. Omit to leave the existing status unchanged.").option("--due-at <value>", 'Update calendar due date in ISO date-only format (`YYYY-MM-DD`). Pass `""` explicitly to clear an existing due date, or omit to leave it unchanged. Passing `null` is strictly rejected.').option("--type-id <value>", "Re-assign this todo to a different active type. The new type must belong to the todo's same project; otherwise the request fails with TYPE_PROJECT_MISMATCH. New server-generated type ids use typ_<ULID>; legacy ids remain accepted.").option("--custom-fields <value>", "PATCH semantics: only the keys present in this map change. Pass `null` for a key (e.g. `custom_fields: { priority: null }`) to explicitly delete that custom field value. Array values are replaced wholesale with no element-level diff. Providing a key that is not declared on the effective todo type is rejected with `UNDECLARED_FIELD`.").option("--user-id <value>", "Reassign the owner (assignee) user ID of this todo. Target user must belong to the same organization.").action(async (id, opts) => {
3120
3229
  const client2 = await loadSdkClient();
3121
3230
  const result = await todoUpdate({
3122
3231
  client: client2._rawClient,
@@ -3131,7 +3240,7 @@ var todoUpdateCommand = new Command50("update").description("Update a todo").arg
3131
3240
  status: opts.status,
3132
3241
  due_at: opts.dueAt,
3133
3242
  type_id: opts.typeId,
3134
- custom_fields: opts.customFields,
3243
+ custom_fields: parseJsonField(opts.customFields, "custom-fields"),
3135
3244
  user_id: opts.userId
3136
3245
  }
3137
3246
  });
@@ -3206,7 +3315,10 @@ function registerGeneratedCommands(root) {
3206
3315
  root_todo_project.addCommand(projectCreateCommand);
3207
3316
  root_todo_project.addCommand(projectListCommand);
3208
3317
  const root_todo_rule = root_todo.command("rule").description("rule commands");
3318
+ root_todo_rule.addCommand(recurrenceRuleCreateCommand);
3209
3319
  root_todo_rule.addCommand(recurrenceRuleListCommand);
3320
+ root_todo_rule.addCommand(recurrenceRuleDeleteCommand);
3321
+ root_todo_rule.addCommand(recurrenceRuleGetCommand);
3210
3322
  root_todo.addCommand(todoCreateCommand);
3211
3323
  root_todo.addCommand(todoListCommand);
3212
3324
  const root_todo_type = root_todo.command("type").description("type commands");
@@ -3217,7 +3329,7 @@ function registerGeneratedCommands(root) {
3217
3329
  }
3218
3330
 
3219
3331
  // src/handwritten/commands/login.ts
3220
- import { Command as Command51 } from "commander";
3332
+ import { Command as Command54 } from "commander";
3221
3333
 
3222
3334
  // src/handwritten/auth/device-flow.ts
3223
3335
  var DEFAULT_SLEEP = (ms) => new Promise((r) => setTimeout(r, ms));
@@ -3396,7 +3508,7 @@ async function runLogin(opts) {
3396
3508
  }
3397
3509
 
3398
3510
  // src/handwritten/commands/login.ts
3399
- var loginCommand = new Command51("login").description("Log in via OAuth device flow (default) or API key").option("--api-key <key>", "Log in with a wspc API key (escape hatch)").option("--json", "Emit machine-readable events to stdout").action(async (opts) => {
3511
+ var loginCommand = new Command54("login").description("Log in via OAuth device flow (default) or API key").option("--api-key <key>", "Log in with a wspc API key (escape hatch)").option("--json", "Emit machine-readable events to stdout").action(async (opts) => {
3400
3512
  const store = new ConfigStore();
3401
3513
  const output = opts.json ? { write: () => {
3402
3514
  }, writeJson: (e) => process.stdout.write(JSON.stringify(e) + "\n") } : {
@@ -3413,7 +3525,7 @@ var loginCommand = new Command51("login").description("Log in via OAuth device f
3413
3525
  });
3414
3526
 
3415
3527
  // src/handwritten/commands/logout.ts
3416
- import { Command as Command52 } from "commander";
3528
+ import { Command as Command55 } from "commander";
3417
3529
 
3418
3530
  // src/handwritten/auth/logout.ts
3419
3531
  async function runLogout(opts) {
@@ -3441,7 +3553,7 @@ async function runLogout(opts) {
3441
3553
  }
3442
3554
 
3443
3555
  // src/handwritten/commands/logout.ts
3444
- var logoutCommand = new Command52("logout").description("Log out an account (default: the active account in the current env)").argument("[email]", "Email of the account to log out").option("--all", "Log out every account in the current env").action(async (email, opts) => {
3556
+ var logoutCommand = new Command55("logout").description("Log out an account (default: the active account in the current env)").argument("[email]", "Email of the account to log out").option("--all", "Log out every account in the current env").action(async (email, opts) => {
3445
3557
  const res = await runLogout({ store: new ConfigStore(), email, all: opts.all });
3446
3558
  if (res.removed.length === 0) {
3447
3559
  process.stdout.write("nothing to log out\n");
@@ -3454,7 +3566,7 @@ var logoutCommand = new Command52("logout").description("Log out an account (def
3454
3566
  });
3455
3567
 
3456
3568
  // src/handwritten/commands/whoami.ts
3457
- import { Command as Command53 } from "commander";
3569
+ import { Command as Command56 } from "commander";
3458
3570
  var ENV_DISPLAY = {
3459
3571
  shape: "object",
3460
3572
  fields: ["name", "api_base", "account", "actor", "agent_label"]
@@ -3486,7 +3598,7 @@ async function backfillActiveEmail(store, envName, email, userId) {
3486
3598
  await store.write(cfg);
3487
3599
  }
3488
3600
  }
3489
- var whoamiCommand = new Command53("whoami").description("Show the active env, signed-in account, and organization").action(async () => {
3601
+ var whoamiCommand = new Command56("whoami").description("Show the active env, signed-in account, and organization").action(async () => {
3490
3602
  const store = new ConfigStore();
3491
3603
  const config = await store.read();
3492
3604
  let resolved;
@@ -3539,8 +3651,8 @@ function printLoggedOut() {
3539
3651
  }
3540
3652
 
3541
3653
  // src/handwritten/commands/config.ts
3542
- import { Command as Command54 } from "commander";
3543
- var configCommand = new Command54("config").description("Manage wspc local config");
3654
+ import { Command as Command57 } from "commander";
3655
+ var configCommand = new Command57("config").description("Manage wspc local config");
3544
3656
  registerRenderer("config_show", (data) => {
3545
3657
  const d = data;
3546
3658
  if (d.envs.length === 0) {
@@ -3611,7 +3723,7 @@ configCommand.command("use <env>").description("Switch current_env").action(asyn
3611
3723
  });
3612
3724
 
3613
3725
  // src/handwritten/commands/account.ts
3614
- import { Command as Command55 } from "commander";
3726
+ import { Command as Command58 } from "commander";
3615
3727
  async function listAccounts(store) {
3616
3728
  const c = await store.read();
3617
3729
  const envName = c.current_env;
@@ -3652,7 +3764,7 @@ registerRenderer("account_ls", (data) => {
3652
3764
  ]);
3653
3765
  process.stdout.write(table(headers, body));
3654
3766
  });
3655
- var accountCommand = new Command55("account").description("Manage logged-in accounts");
3767
+ var accountCommand = new Command58("account").description("Manage logged-in accounts");
3656
3768
  accountCommand.command("ls").description("List accounts in the current env (active marked with \u2713)").action(async () => {
3657
3769
  const accounts = await listAccounts(new ConfigStore());
3658
3770
  render({ kind: "account_ls" }, { accounts });
@@ -3664,7 +3776,7 @@ accountCommand.command("switch <email>").description("Set the active account for
3664
3776
  });
3665
3777
 
3666
3778
  // src/handwritten/commands/todo-done.ts
3667
- import { Command as Command56 } from "commander";
3779
+ import { Command as Command59 } from "commander";
3668
3780
  var TODO_UPDATE_DISPLAY = {
3669
3781
  shape: "object",
3670
3782
  format: {
@@ -3682,7 +3794,7 @@ var TODO_UPDATE_DISPLAY = {
3682
3794
  deleted_at: "relative-time"
3683
3795
  }
3684
3796
  };
3685
- var todoDoneCommand = new Command56("done").description("Mark a todo done (sugar for `update <id> --status done`)").argument("<id>", "Todo id").action(async (id) => {
3797
+ var todoDoneCommand = new Command59("done").description("Mark a todo done (sugar for `update <id> --status done`)").argument("<id>", "Todo id").action(async (id) => {
3686
3798
  const client2 = await loadSdkClient();
3687
3799
  const result = await todoUpdate({
3688
3800
  client: client2._rawClient,
@@ -3701,7 +3813,7 @@ var todoDoneCommand = new Command56("done").description("Mark a todo done (sugar
3701
3813
  });
3702
3814
 
3703
3815
  // src/handwritten/commands/email/send.ts
3704
- import { Command as Command57 } from "commander";
3816
+ import { Command as Command60 } from "commander";
3705
3817
  import { readFile, stat } from "fs/promises";
3706
3818
  import { basename } from "path";
3707
3819
 
@@ -3759,7 +3871,7 @@ async function resolveAttachment(input) {
3759
3871
  `--attach ${input}: neither a readable file nor a valid <prefix>_<ulid>:<idx> reference.`
3760
3872
  );
3761
3873
  }
3762
- var sendCommand = new Command57("send").description("Send an outbound email").requiredOption("--from <alias-email>", "alias email to send from").option("--to <addr...>", "recipient address (repeatable)", []).option("--subject <text>", "subject").option("--text <body>", "plain-text body").option("--text-file <path>", "read text body from file").option("--reply <id>", "inbound email id to reply to").option("--attach <path-or-ref...>", "attachment (file path or eml_xxx:idx)", []).requiredOption("--idempotency-key <key>", "idempotency key").action(async (opts) => {
3874
+ var sendCommand = new Command60("send").description("Send an outbound email").requiredOption("--from <alias-email>", "alias email to send from").option("--to <addr...>", "recipient address (repeatable)", []).option("--subject <text>", "subject").option("--text <body>", "plain-text body").option("--text-file <path>", "read text body from file").option("--reply <id>", "inbound email id to reply to").option("--attach <path-or-ref...>", "attachment (file path or eml_xxx:idx)", []).requiredOption("--idempotency-key <key>", "idempotency key").action(async (opts) => {
3763
3875
  const isReply = Boolean(opts.reply);
3764
3876
  const to = opts.to;
3765
3877
  const attachInputs = opts.attach;
@@ -3846,7 +3958,7 @@ var sendCommand = new Command57("send").description("Send an outbound email").re
3846
3958
  });
3847
3959
 
3848
3960
  // src/handwritten/commands/email/attachment.ts
3849
- import { Command as Command58 } from "commander";
3961
+ import { Command as Command61 } from "commander";
3850
3962
  import { createWriteStream } from "fs";
3851
3963
  import { Readable } from "stream";
3852
3964
  import { pipeline } from "stream/promises";
@@ -3863,7 +3975,7 @@ function parseContentDispositionFilename(header) {
3863
3975
  }
3864
3976
 
3865
3977
  // src/handwritten/commands/email/attachment.ts
3866
- var attachmentCommand = new Command58("attachment").description("Download an inbound email attachment by index").argument("<email-id>").argument("<idx>").option("--output <path>", "output file path").option("--include-deleted", "allow downloads from soft-deleted parent emails").action(async (emailId, idxArg, opts) => {
3978
+ var attachmentCommand = new Command61("attachment").description("Download an inbound email attachment by index").argument("<email-id>").argument("<idx>").option("--output <path>", "output file path").option("--include-deleted", "allow downloads from soft-deleted parent emails").action(async (emailId, idxArg, opts) => {
3867
3979
  const idx = Number(idxArg);
3868
3980
  if (!Number.isInteger(idx) || idx < 0) {
3869
3981
  process.stderr.write(`<idx> must be a non-negative integer (got "${idxArg}")
@@ -3896,7 +4008,7 @@ var attachmentCommand = new Command58("attachment").description("Download an inb
3896
4008
 
3897
4009
  // src/cli.ts
3898
4010
  function buildProgram() {
3899
- const program = new Command59().name("wspc").description("Official CLI for wspc.ai").version(`wspc ${VERSION} (spec ${SPEC_SHA}, fetched ${SPEC_FETCHED_AT})`).option("--json", "Output raw JSON (machine-readable)").option("--account <email>", "Run as a specific account (overrides the active account)").hook("preAction", (_thisCommand, actionCommand) => {
4011
+ const program = new Command62().name("wspc").description("Official CLI for wspc.ai").version(`wspc ${VERSION} (spec ${SPEC_SHA}, fetched ${SPEC_FETCHED_AT})`).option("--json", "Output raw JSON (machine-readable)").option("--account <email>", "Run as a specific account (overrides the active account)").hook("preAction", (_thisCommand, actionCommand) => {
3900
4012
  const globals = actionCommand.optsWithGlobals();
3901
4013
  if (globals.json) process.env.WSPC_OUTPUT = "json";
3902
4014
  if (globals.account) process.env.WSPC_ACCOUNT = String(globals.account);