@wspc/cli 0.1.17 → 0.1.18

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 Command82 } from "commander";
4
+ import { Command as Command86 } from "commander";
5
5
  import { realpathSync } from "fs";
6
6
  import { fileURLToPath } from "url";
7
7
 
@@ -1215,6 +1215,15 @@ var todoTypeList = (options) => (options.client ?? client).get({
1215
1215
  url: "/todo/types",
1216
1216
  ...options
1217
1217
  });
1218
+ var todoTypeCreate = (options) => (options?.client ?? client).post({
1219
+ security: [{ scheme: "bearer", type: "http" }],
1220
+ url: "/todo/types",
1221
+ ...options,
1222
+ headers: {
1223
+ "Content-Type": "application/json",
1224
+ ...options?.headers
1225
+ }
1226
+ });
1218
1227
  var todoCommentDelete = (options) => (options.client ?? client).delete({
1219
1228
  security: [{ scheme: "bearer", type: "http" }],
1220
1229
  url: "/todo/comments/{id}",
@@ -1271,6 +1280,20 @@ var todoUpdate = (options) => (options.client ?? client).patch({
1271
1280
  ...options.headers
1272
1281
  }
1273
1282
  });
1283
+ var todoTypeDelete = (options) => (options.client ?? client).delete({
1284
+ security: [{ scheme: "bearer", type: "http" }],
1285
+ url: "/todo/types/{id}",
1286
+ ...options
1287
+ });
1288
+ var todoTypeUpdate = (options) => (options.client ?? client).patch({
1289
+ security: [{ scheme: "bearer", type: "http" }],
1290
+ url: "/todo/types/{id}",
1291
+ ...options,
1292
+ headers: {
1293
+ "Content-Type": "application/json",
1294
+ ...options.headers
1295
+ }
1296
+ });
1274
1297
  var todoRestore = (options) => (options.client ?? client).post({
1275
1298
  security: [{ scheme: "bearer", type: "http" }],
1276
1299
  url: "/todo/items/{id}/restore",
@@ -1280,6 +1303,11 @@ var todoRestore = (options) => (options.client ?? client).post({
1280
1303
  ...options.headers
1281
1304
  }
1282
1305
  });
1306
+ var todoTypeRestore = (options) => (options.client ?? client).post({
1307
+ security: [{ scheme: "bearer", type: "http" }],
1308
+ url: "/todo/types/{id}/restore",
1309
+ ...options
1310
+ });
1283
1311
 
1284
1312
  // src/handwritten/config/index.ts
1285
1313
  import { promises as fs } from "fs";
@@ -1529,9 +1557,9 @@ function createConsistencyFetch(opts) {
1529
1557
  }
1530
1558
 
1531
1559
  // src/version.ts
1532
- var VERSION = "0.1.17";
1533
- var SPEC_SHA = "74d4defc";
1534
- var SPEC_FETCHED_AT = "2026-07-13T06:40:42.304Z";
1560
+ var VERSION = "0.1.18";
1561
+ var SPEC_SHA = "c11a618e";
1562
+ var SPEC_FETCHED_AT = "2026-07-13T08:36:32.305Z";
1535
1563
  var API_BASE = "https://api.wspc.ai";
1536
1564
 
1537
1565
  // src/index.ts
@@ -2241,7 +2269,7 @@ function formatCell(value, fmt, colorMap, opts) {
2241
2269
  }
2242
2270
 
2243
2271
  // src/generated/cli/invite/accept.ts
2244
- var inviteAcceptCommand = new Command("accept").description("Accept an invite and switch into the inviting organization").argument("<id>", "id").action(async (id, opts) => {
2272
+ var inviteAcceptCommand = new Command("accept").description("Accept an invite and switch into the inviting organization").addHelpText("after", "\nSwitches the caller's org to the invite's org and records the previous org. The caller loses access to data scoped to their previous org.\n\nExamples:\n $ wspc invite accept inv_...\n").argument("<id>", "id").action(async (id, opts) => {
2245
2273
  const client2 = await loadSdkClient();
2246
2274
  const result = await inviteAccept({
2247
2275
  client: client2._rawClient,
@@ -2262,7 +2290,7 @@ var inviteAcceptCommand = new Command("accept").description("Accept an invite an
2262
2290
 
2263
2291
  // src/generated/cli/keys/create.ts
2264
2292
  import { Command as Command2 } from "commander";
2265
- var keyCreateCommand = new Command2("create").description("Create a new API key (full value returned once)").option("--label <value>", "Human-readable label for the new key (1\u201360 chars after trimming). Pick something that identifies where the key will live \u2014 agent name, machine, or environment \u2014 so you can recognise it later in `wspc keys list`.").action(async (opts) => {
2293
+ var keyCreateCommand = new Command2("create").description("Create a new API key (full value returned once)").addHelpText("after", '\n### Overview\nCreates and provisions a new long-lived API key for the authenticated user. The complete plaintext API key value (`api_key`) is returned **only once** in this endpoint\'s response and cannot be retrieved again.\n\n### When to Use\n- Use this endpoint when a user requests a new API key (e.g., `wspc keys create --label "My Agent"`) to isolate access for specific environments, applications, or developers.\n\n### Constraints\n- Requires a valid Bearer token in the `Authorization` header.\n- **Key Limit**: A user is limited to a maximum of 25 active API keys. Requesting a new key beyond this limit will result in a `KEY_LIMIT_EXCEEDED` error.\n- **Label Validation**: The `label` parameter must be between 1 and 60 characters after trimming whitespace. Failing to provide a valid label results in an `INVALID_LABEL` error.\n\n### Troubleshooting\n- **401 Unauthorized**: The Bearer token is missing or invalid.\n- **400 Bad Request**: The `label` parameter is empty, too long, or missing.\n- **400 Bad Request (Limit Exceeded)**: The user has hit the maximum limit of 25 active keys. An existing active key must be revoked before creating a new one.\n').option("--label <value>", "Human-readable label for the new key (1\u201360 chars after trimming). Pick something that identifies where the key will live \u2014 agent name, machine, or environment \u2014 so you can recognise it later in `wspc keys list`.").action(async (opts) => {
2266
2294
  const client2 = await loadSdkClient();
2267
2295
  const result = await keyCreate({
2268
2296
  client: client2._rawClient,
@@ -2283,7 +2311,7 @@ var keyCreateCommand = new Command2("create").description("Create a new API key
2283
2311
 
2284
2312
  // src/generated/cli/keys/ls.ts
2285
2313
  import { Command as Command3 } from "commander";
2286
- var keyListCommand = new Command3("ls").description("List active API keys").action(async (opts) => {
2314
+ var keyListCommand = new Command3("ls").description("List active API keys").addHelpText("after", "\n### Overview\nReturns a list of all active (non-revoked) API keys belonging to the authenticated user. It also includes the `current_key_id` identifying the specific key used to authenticate the current request.\n\n### When to Use\n- Use this endpoint to view active API keys (e.g., when running `wspc keys list` or displaying API key management screens in user profiles).\n- Use the `current_key_id` to identify which key is making the current call, facilitating self-rotation or auditing.\n\n### Constraints\n- Requires a valid Bearer token in the `Authorization` header.\n- Only active keys are returned; keys that have been revoked are filtered out and excluded from the response.\n- The full secret key is never returned; only the last 4 characters (`key_last4`) are provided for identification.\n\n### Troubleshooting\n- **401 Unauthorized**: The provided Bearer token is missing, expired, or invalid. Ensure you are passing a valid, active API key.\n").action(async (opts) => {
2287
2315
  const client2 = await loadSdkClient();
2288
2316
  const result = await keyList({
2289
2317
  client: client2._rawClient
@@ -2301,7 +2329,7 @@ var keyListCommand = new Command3("ls").description("List active API keys").acti
2301
2329
 
2302
2330
  // src/generated/cli/org/invite.ts
2303
2331
  import { Command as Command4 } from "commander";
2304
- var orgInviteCreateCommand = new Command4("invite").description("Invite an email to join the caller's organization").option("--email <value>", "Email address to invite into the caller's organization.").action(async (opts) => {
2332
+ var orgInviteCreateCommand = new Command4("invite").description("Invite an email to join the caller's organization").addHelpText("after", "\nCreates a pending invite for `email` and sends an invite email. Idempotent for an existing pending invite. The invitee accepts after signing in with the invited email.\n\nExamples:\n $ wspc org invite bob@example.com\n").option("--email <value>", "Email address to invite into the caller's organization.").action(async (opts) => {
2305
2333
  const client2 = await loadSdkClient();
2306
2334
  const result = await orgInviteCreate({
2307
2335
  client: client2._rawClient,
@@ -2322,7 +2350,7 @@ var orgInviteCreateCommand = new Command4("invite").description("Invite an email
2322
2350
 
2323
2351
  // src/generated/cli/org/invites.ts
2324
2352
  import { Command as Command5 } from "commander";
2325
- var orgInvitesListCommand = new Command5("invites").description("List invites issued by the caller's organization").action(async (opts) => {
2353
+ var orgInvitesListCommand = new Command5("invites").description("List invites issued by the caller's organization").addHelpText("after", "\nRetrieves a list of all active pending or expired organization invites issued by the caller's organization.\n").action(async (opts) => {
2326
2354
  const client2 = await loadSdkClient();
2327
2355
  const result = await orgInvitesList({
2328
2356
  client: client2._rawClient
@@ -2340,7 +2368,7 @@ var orgInvitesListCommand = new Command5("invites").description("List invites is
2340
2368
 
2341
2369
  // src/generated/cli/org/show.ts
2342
2370
  import { Command as Command6 } from "commander";
2343
- var orgGetCommand = new Command6("show").description("Get the authenticated user's organization").action(async (opts) => {
2371
+ var orgGetCommand = new Command6("show").description("Get the authenticated user's organization").addHelpText("after", "\n### Overview\nReturns the metadata of the organization owned by the authenticated user. In the current version, this represents the user's personal organization space containing all their projects and tokens.\n\n### When to Use\n- Use this endpoint to retrieve the organization ID and name for display or context setup (e.g., when running `wspc org show` or rendering user dashboards).\n- Use this to verify that the API token / credentials are linked to a valid organization.\n\n### Constraints\n- Requires a valid Bearer token (API Key or Session Token) in the `Authorization` header.\n- In the current API version (v1), every user is automatically provisioned a single personal organization. Selecting or switching organizations is not supported.\n\n### Troubleshooting\n- **401 Unauthorized**: The provided Bearer token is missing, expired, or invalid. Verify your `Authorization` header format (`Bearer <token>`).\n- **403 Forbidden**: The token does not have access to read organization metadata.\n- **404 Not Found**: The organization associated with this token could not be found or has been deactivated.\n\nExamples:\n $ wspc org show\n $ wspc org show --json\n").action(async (opts) => {
2344
2372
  const client2 = await loadSdkClient();
2345
2373
  const result = await orgGet({
2346
2374
  client: client2._rawClient
@@ -2358,7 +2386,7 @@ var orgGetCommand = new Command6("show").description("Get the authenticated user
2358
2386
 
2359
2387
  // src/generated/cli/org/rename.ts
2360
2388
  import { Command as Command7 } from "commander";
2361
- var orgUpdateCommand = new Command7("rename").description("Update the authenticated user's organization").option("--name <value>", "The new name for the organization. Cannot be empty or purely whitespace.").action(async (opts) => {
2389
+ var orgUpdateCommand = new Command7("rename").description("Update the authenticated user's organization").addHelpText("after", '\n### Overview\nUpdates the metadata (currently, the name) of the organization associated with the authenticated user.\n\n### Constraints\n- Requires a valid Bearer token.\n- The organization name cannot be empty or purely whitespace.\n- Maximum length is capped by `MAX_ORG_NAME_LEN`.\n\nExamples:\n $ wspc org rename "New Name"\n $ wspc org rename "New Name" --json\n').option("--name <value>", "The new name for the organization. Cannot be empty or purely whitespace.").action(async (opts) => {
2362
2390
  const client2 = await loadSdkClient();
2363
2391
  const result = await orgUpdate({
2364
2392
  client: client2._rawClient,
@@ -2379,7 +2407,7 @@ var orgUpdateCommand = new Command7("rename").description("Update the authentica
2379
2407
 
2380
2408
  // src/generated/cli/invite/show.ts
2381
2409
  import { Command as Command8 } from "commander";
2382
- var inviteGetCommand = new Command8("show").description("Get a single invite addressed to the caller").argument("<id>", "id").action(async (id, opts) => {
2410
+ var inviteGetCommand = new Command8("show").description("Get a single invite addressed to the caller").addHelpText("after", "\nRetrieves the metadata of a specific organization invite addressed to the caller by its ID.\n\nExamples:\n $ wspc invite show inv_...\n").argument("<id>", "id").action(async (id, opts) => {
2383
2411
  const client2 = await loadSdkClient();
2384
2412
  const result = await inviteGet({
2385
2413
  client: client2._rawClient,
@@ -2400,7 +2428,7 @@ var inviteGetCommand = new Command8("show").description("Get a single invite add
2400
2428
 
2401
2429
  // src/generated/cli/auth/me.ts
2402
2430
  import { Command as Command9 } from "commander";
2403
- var authMeCommand = new Command9("me").description("Fetch the user identified by the bearer token").action(async (opts) => {
2431
+ var authMeCommand = new Command9("me").description("Fetch the user identified by the bearer token").addHelpText("after", "\n### Overview\nRetrieves the stable identity profile (user ID, email, and optional display name) of the user associated with the active Bearer token. Works for both long-lived `wspc_*` API keys and OAuth access tokens.\n\n### When to Use\n- Use this endpoint (e.g., in `wspc verify` or `wspc whoami`) to confirm that the active environment's API key or OAuth access token remains valid.\n- Use it in UIs to display the logged-in user's profile details and retrieve the stable `user_id`.\n\n### Constraints\n- Requires a valid Bearer token (either a long-lived `wspc_*` API key or a temporary OAuth access token) in the `Authorization` header.\n- **Response Fields**: The `api_key_id` field is only returned if authenticated via a WSPC API key (prefixed with `wspc_`). OAuth access tokens will omit `api_key_id`. `display_name` is omitted if not configured.\n\n### Troubleshooting\n- **401 Unauthorized**: The Bearer token is missing, malformed, or has been revoked. Ensure the `Authorization` header matches the `Bearer <token>` format.\n\nExamples:\n $ wspc auth me\n $ wspc auth me --json\n").action(async (opts) => {
2404
2432
  const client2 = await loadSdkClient();
2405
2433
  const result = await authMe({
2406
2434
  client: client2._rawClient
@@ -2418,7 +2446,7 @@ var authMeCommand = new Command9("me").description("Fetch the user identified by
2418
2446
 
2419
2447
  // src/generated/cli/invites.ts
2420
2448
  import { Command as Command10 } from "commander";
2421
- var invitesListCommand = new Command10("invites").description("List invites addressed to the authenticated user's email").action(async (opts) => {
2449
+ var invitesListCommand = new Command10("invites").description("List invites addressed to the authenticated user's email").addHelpText("after", "\nRetrieves all pending or expired organization invites addressed to the caller's verified email address.\n").action(async (opts) => {
2422
2450
  const client2 = await loadSdkClient();
2423
2451
  const result = await invitesList({
2424
2452
  client: client2._rawClient
@@ -2436,7 +2464,7 @@ var invitesListCommand = new Command10("invites").description("List invites addr
2436
2464
 
2437
2465
  // src/generated/cli/org/members.ts
2438
2466
  import { Command as Command11 } from "commander";
2439
- var orgMembersListCommand = new Command11("members").description("List members of the authenticated user's organization").option("--cursor <value>", "Opaque pagination cursor. Pass the `next_cursor` returned by the previous page to fetch the next slice. Omit on the first call.").option("--limit <value>", "Maximum members to return. Clamped to [1, 100]. Defaults to 50.").action(async (opts) => {
2467
+ var orgMembersListCommand = new Command11("members").description("List members of the authenticated user's organization").addHelpText("after", "\n### Overview\nRetrieves a paginated list of all members belonging to the authenticated user's organization, including their basic profile information, emails, and roles.\n\n### When to Use\n- Use this endpoint to list members in command-line tools (e.g., `wspc org members ls`) or to display a team directory in a user dashboard.\n- Use this to paginate through large lists of organization members using cursor-based pagination.\n\n### Constraints\n- Requires a valid Bearer token in the `Authorization` header.\n- In the current version (v1), organizations are single-user only, meaning this endpoint will always return exactly one member (the caller).\n- **Pagination**: Supports cursor-based pagination. The `limit` query parameter must be a positive integer, defaulting to 50 and capped at a maximum of 100. Pass `cursor` from the previous response's `next_cursor` to fetch subsequent pages.\n\n### Troubleshooting\n- **401 Unauthorized**: The Bearer token is invalid or has expired.\n- **400 Bad Request**: The query parameters `limit` or `cursor` are malformed. Ensure `limit` is an integer between 1 and 100.\n- **404 Not Found**: The organization associated with this user was not found.\n").option("--cursor <value>", "Opaque pagination cursor. Pass the `next_cursor` returned by the previous page to fetch the next slice. Omit on the first call.").option("--limit <value>", "Maximum members to return. Clamped to [1, 100]. Defaults to 50.").action(async (opts) => {
2440
2468
  const client2 = await loadSdkClient();
2441
2469
  const result = await orgMembersList({
2442
2470
  client: client2._rawClient,
@@ -2458,7 +2486,7 @@ var orgMembersListCommand = new Command11("members").description("List members o
2458
2486
 
2459
2487
  // src/generated/cli/invite/reject.ts
2460
2488
  import { Command as Command12 } from "commander";
2461
- var inviteRejectCommand = new Command12("reject").description("Reject an invite").argument("<id>", "id").action(async (id, opts) => {
2489
+ var inviteRejectCommand = new Command12("reject").description("Reject an invite").addHelpText("after", "\nRejects an organization invite addressed to the caller. The invite will be marked as rejected.\n\nExamples:\n $ wspc invite reject inv_...\n").argument("<id>", "id").action(async (id, opts) => {
2462
2490
  const client2 = await loadSdkClient();
2463
2491
  const result = await inviteReject({
2464
2492
  client: client2._rawClient,
@@ -2479,7 +2507,7 @@ var inviteRejectCommand = new Command12("reject").description("Reject an invite"
2479
2507
 
2480
2508
  // src/generated/cli/keys/rm.ts
2481
2509
  import { Command as Command13 } from "commander";
2482
- var keyRevokeCommand = new Command13("rm").description("Soft-revoke an API key").argument("<id>", "id").action(async (id, opts) => {
2510
+ var keyRevokeCommand = new Command13("rm").description("Soft-revoke an API key").addHelpText("after", "\n### Overview\nPermanently revokes an active API key by its unique ID. Once revoked, the key becomes immediately invalid and will be rejected by all services.\n\n### When to Use\n- Use this endpoint to permanently deactivate an API key (e.g., when running `wspc keys revoke <id>`) due to token rotation, key leakage, or decommissioning of a machine/service.\n\n### Constraints\n- Requires a valid Bearer token in the `Authorization` header.\n- Revocation is permanent and cannot be undone.\n- A user can revoke any key they own, including the one they are currently using to make the call. If they revoke the current key, subsequent requests using that key will return `401 Unauthorized`.\n\n### Troubleshooting\n- **401 Unauthorized**: The active token is missing, expired, or invalid.\n- **404 Not Found**: The specified key ID does not exist, belongs to another user, or has already been revoked.\n- **400 Bad Request**: The `id` path parameter format is invalid. It must be in the format `key_<ULID>`.\n").argument("<id>", "id").action(async (id, opts) => {
2483
2511
  const client2 = await loadSdkClient();
2484
2512
  const result = await keyRevoke({
2485
2513
  client: client2._rawClient,
@@ -2500,7 +2528,7 @@ var keyRevokeCommand = new Command13("rm").description("Soft-revoke an API key")
2500
2528
 
2501
2529
  // src/generated/cli/keys/edit.ts
2502
2530
  import { Command as Command14 } from "commander";
2503
- var keyUpdateCommand = new Command14("edit").description("Update an active API key's label").argument("<id>", "id").option("--label <value>", "Human-readable label for the key (1\u201360 chars after trimming).").action(async (id, opts) => {
2531
+ var keyUpdateCommand = new Command14("edit").description("Update an active API key's label").addHelpText("after", "\n### Overview\nUpdates the human-readable label of an active API key. Only active (non-revoked) keys owned by the authenticated user can be updated.\n\n### Constraints\n- Requires a valid Bearer token in the `Authorization` header.\n- **Label Validation**: The `label` parameter must be between 1 and 60 characters after trimming whitespace. Failing to provide a valid label results in an `INVALID_LABEL` error.\n").argument("<id>", "id").option("--label <value>", "Human-readable label for the key (1\u201360 chars after trimming).").action(async (id, opts) => {
2504
2532
  const client2 = await loadSdkClient();
2505
2533
  const result = await keyUpdate({
2506
2534
  client: client2._rawClient,
@@ -2524,7 +2552,7 @@ var keyUpdateCommand = new Command14("edit").description("Update an active API k
2524
2552
 
2525
2553
  // src/generated/cli/org/invite/revoke.ts
2526
2554
  import { Command as Command15 } from "commander";
2527
- var orgInviteRevokeCommand = new Command15("revoke").description("Revoke a pending invite").argument("<id>", "id").action(async (id, opts) => {
2555
+ var orgInviteRevokeCommand = new Command15("revoke").description("Revoke a pending invite").addHelpText("after", "\nPermanently revokes a pending organization invite. The invitee will no longer be able to accept it.\n\nExamples:\n $ wspc org invite revoke inv_...\n").argument("<id>", "id").action(async (id, opts) => {
2528
2556
  const client2 = await loadSdkClient();
2529
2557
  const result = await orgInviteRevoke({
2530
2558
  client: client2._rawClient,
@@ -2630,7 +2658,7 @@ function parseAttendee(input) {
2630
2658
  }
2631
2659
 
2632
2660
  // src/generated/cli/event/add.ts
2633
- var eventCreateCommand = new Command16("add").description("Schedule a calendar event").argument("<title>", "title").option("--description <value>", "Free-form notes about the event (agenda, dial-in instructions, etc.). Markdown formatted (CommonMark + GFM tables, strikethrough, task lists); stored verbatim. Invitation emails include the raw source \u2014 most email clients display it as plain text.").option("--start <value>", "Accepts ISO 8601 datetime with offset (e.g. `2026-06-01T12:30:00+08:00`) for timed events, or ISO date-only (e.g. `2026-06-01`) for all-day. The `wspc` CLI additionally accepts natural-language phrases (`tomorrow 12:30pm`, `next Monday 9am`) and resolves them to ISO before sending; the server itself only accepts ISO. All-day uses RFC 5545 exclusive end: a one-day event on 6/1 is `start=2026-06-01, end=2026-06-02`; both endpoints must be the same type.").option("--end <value>", "Accepts ISO 8601 datetime with offset (e.g. `2026-06-01T12:30:00+08:00`) for timed events, or ISO date-only (e.g. `2026-06-01`) for all-day. The `wspc` CLI additionally accepts natural-language phrases (`tomorrow 12:30pm`, `next Monday 9am`) and resolves them to ISO before sending; the server itself only accepts ISO. All-day uses RFC 5545 exclusive end: a one-day event on 6/1 is `start=2026-06-01, end=2026-06-02`; both endpoints must be the same type.").option("-l, --location <value>", "Free-text location \u2014 physical address, room, or short note. Separate from `url` (meeting link).").option("-u, --url <value>", "Optional meeting link (Zoom / Meet / etc.). Kept separate from `location` so calendar clients can render it as a join action.").option("--status <value>", "Lifecycle status. `confirmed`: the event will happen (default). `tentative`: organizer has not finalized; still visible in lists. `cancelled`: the event was called off but the record is kept so attendees can be notified and history audited; distinct from soft-delete (DELETE `/calendar/events/{id}`) which hides the event from default list responses.").option("--attendee <value>", "Up to 50 unique attendees (deduped case-insensitively by email). If non-empty, each attendee receives an invitation email with an `.ics` REQUEST attachment as a side effect of creation.", (val, memo) => {
2661
+ var eventCreateCommand = new Command16("add").description("Schedule a calendar event").addHelpText("after", '\n### Overview\nCreate a new calendar event owned by the authenticated user.\n\n### When to Use\nBook a meeting, lunch, all-day trip, or any time-bound item. Optionally provide `attendees` to automatically dispatch invitation emails containing an `.ics` REQUEST attachment to each participant as a side effect.\n\n### Constraints\n- **Format Integrity**: `start` and `end` must be of the exact same type (both ISO 8601 datetimes with offset, or both ISO date-only for all-day).\n- **Chronological Order**: `end` must be strictly after `start`.\n- **All-Day boundary**: All-day events use RFC 5545 exclusive end (e.g., a one-day event on June 1st is specified as `start=2026-06-01` and `end=2026-06-02`).\n- **Attendee Limit**: Up to 50 unique attendees are supported after case-insensitive email address deduplication.\n\n### Troubleshooting\n- Returns 400 `VALIDATION_ERROR` if `start` and `end` format mismatch, or if `end <= start`.\n- Returns 400 `ATTENDEE_LIMIT_EXCEEDED` if more than 50 unique attendees are supplied.\n- Invitation emails are processed and dispatched asynchronously via Cloudflare `waitUntil`; the analytics counter `event_created` is emitted automatically.\n\nExamples:\n $ wspc event add "Lunch with Alice" --start "tomorrow 12:30pm" --end "tomorrow 1:30pm"\n $ wspc event add "Team offsite" --all-day --start 2026-06-01 --end 2026-06-01\n').argument("<title>", "title").option("--description <value>", "Free-form notes about the event (agenda, dial-in instructions, etc.). Markdown formatted (CommonMark + GFM tables, strikethrough, task lists); stored verbatim. Invitation emails include the raw source \u2014 most email clients display it as plain text.").option("--start <value>", "Accepts ISO 8601 datetime with offset (e.g. `2026-06-01T12:30:00+08:00`) for timed events, or ISO date-only (e.g. `2026-06-01`) for all-day. The `wspc` CLI additionally accepts natural-language phrases (`tomorrow 12:30pm`, `next Monday 9am`) and resolves them to ISO before sending; the server itself only accepts ISO. All-day uses RFC 5545 exclusive end: a one-day event on 6/1 is `start=2026-06-01, end=2026-06-02`; both endpoints must be the same type.").option("--end <value>", "Accepts ISO 8601 datetime with offset (e.g. `2026-06-01T12:30:00+08:00`) for timed events, or ISO date-only (e.g. `2026-06-01`) for all-day. The `wspc` CLI additionally accepts natural-language phrases (`tomorrow 12:30pm`, `next Monday 9am`) and resolves them to ISO before sending; the server itself only accepts ISO. All-day uses RFC 5545 exclusive end: a one-day event on 6/1 is `start=2026-06-01, end=2026-06-02`; both endpoints must be the same type.").option("-l, --location <value>", "Free-text location \u2014 physical address, room, or short note. Separate from `url` (meeting link).").option("-u, --url <value>", "Optional meeting link (Zoom / Meet / etc.). Kept separate from `location` so calendar clients can render it as a join action.").option("--status <value>", "Lifecycle status. `confirmed`: the event will happen (default). `tentative`: organizer has not finalized; still visible in lists. `cancelled`: the event was called off but the record is kept so attendees can be notified and history audited; distinct from soft-delete (DELETE `/calendar/events/{id}`) which hides the event from default list responses.").option("--attendee <value>", "Up to 50 unique attendees (deduped case-insensitively by email). If non-empty, each attendee receives an invitation email with an `.ics` REQUEST attachment as a side effect of creation.", (val, memo) => {
2634
2662
  memo.push(val);
2635
2663
  return memo;
2636
2664
  }, []).option("--idempotency-key <value>", "idempotency_key").option("--all-day", "all_day").option("--tz <zone>", "IANA timezone for relative time parsing").action(async (title, opts) => {
@@ -2681,7 +2709,7 @@ var eventCreateCommand = new Command16("add").description("Schedule a calendar e
2681
2709
 
2682
2710
  // src/generated/cli/event/ls.ts
2683
2711
  import { Command as Command17 } from "commander";
2684
- var eventListCommand = new Command17("ls").description("List calendar events").option("--q <value>", "Optional full-text search across title, description, and location (case-insensitive substring).").option("--from <value>", "Inclusive lower bound on the event `start` (ISO datetime with offset, or ISO date-only). When ANY of `start_from`/`start_to`/`end_from`/`end_to` is provided, the implicit past filter is disabled.").option("--to <value>", "Inclusive upper bound on the event `start`.").option("--end-from <value>", "Inclusive lower bound on the event `end`.").option("--end-to <value>", "Inclusive upper bound on the event `end`.").option("--cursor <value>", "Opaque pagination cursor returned in `next_cursor` of a previous response.").option("--limit <value>", "Maximum number of events to return. Clamped to `[1, 200]`. Default is server-defined.").option("--include-deleted", "include_deleted").option("--include-past <value>", "When omitted or `false`, events whose `end` is before now are hidden. Pass `true` to include them. Ignored when any of `start_from`/`start_to`/`end_from`/`end_to` is provided \u2014 explicit time bounds always win.").option("--tz <zone>", "IANA timezone for relative time parsing").action(async (opts) => {
2712
+ var eventListCommand = new Command17("ls").description("List calendar events").addHelpText("after", '\n### Overview\nReturn the authenticated user\'s events, ordered by `start` ascending, with cursor pagination.\n\n### When to Use\nRender calendar list/grid views, search for specific terms using full-text search, query events within a specific time window, or retrieve historically past events.\n\n### Constraints\n- **Default Visibility**: By default, soft-deleted events and past events (events where `end` is before the current time) are automatically hidden.\n- **Time Bounds Override**: Supplying any explicit time bound query parameter (`start_from`, `start_to`, `end_from`, `end_to`) or passing `include_past=true` overrides and disables the implicit past filter.\n- **Search Scope**: `q` performs a case-insensitive substring search across `title`, `description`, and `location`.\n- **Pagination**: The `limit` query parameter is clamped to `[1, 200]`; cursor pagination is enabled via the opaque `cursor` parameter.\n\n### Troubleshooting\n- Returns 400 `VALIDATION_ERROR` if date query bounds are invalid (e.g. `start_from > start_to` or `end_from > end_to`).\n\nExamples:\n $ wspc event ls\n $ wspc event ls --from "today" --to "next week"\n').option("--q <value>", "Optional full-text search across title, description, and location (case-insensitive substring).").option("--from <value>", "Inclusive lower bound on the event `start` (ISO datetime with offset, or ISO date-only). When ANY of `start_from`/`start_to`/`end_from`/`end_to` is provided, the implicit past filter is disabled.").option("--to <value>", "Inclusive upper bound on the event `start`.").option("--end-from <value>", "Inclusive lower bound on the event `end`.").option("--end-to <value>", "Inclusive upper bound on the event `end`.").option("--cursor <value>", "Opaque pagination cursor returned in `next_cursor` of a previous response.").option("--limit <value>", "Maximum number of events to return. Clamped to `[1, 200]`. Default is server-defined.").option("--include-deleted", "include_deleted").option("--include-past <value>", "When omitted or `false`, events whose `end` is before now are hidden. Pass `true` to include them. Ignored when any of `start_from`/`start_to`/`end_from`/`end_to` is provided \u2014 explicit time bounds always win.").option("--tz <zone>", "IANA timezone for relative time parsing").action(async (opts) => {
2685
2713
  const zone = resolveTimezone(opts.tz);
2686
2714
  let fromValue;
2687
2715
  if (opts.from !== void 0) {
@@ -2719,7 +2747,7 @@ var eventListCommand = new Command17("ls").description("List calendar events").o
2719
2747
 
2720
2748
  // src/generated/cli/event/rm.ts
2721
2749
  import { Command as Command18 } from "commander";
2722
- var eventDeleteCommand = new Command18("rm").description("Soft-delete a calendar event").argument("<id>", "id").option("--expected-version <value>", "Optional optimistic lock. Omit to let the server use the current version; pass only to fail with 409 `VERSION_CONFLICT` if someone else has mutated the event since you last read.").action(async (id, opts) => {
2750
+ var eventDeleteCommand = new Command18("rm").description("Soft-delete a calendar event").addHelpText("after", "\n### Overview\nSoft-delete an existing calendar event, hiding it from default listings.\n\n### When to Use\nRemove an event entirely from the user's historical view and calendar client. If the meeting was cancelled but should remain in history (notifying participants of cancellation), use `PATCH /calendar/events/{id}` with `status: cancelled` instead.\n\n### Constraints\n- **Optimistic Locking**: Supports optional optimistic locking via `expected_version` in the request body.\n- **Side Effects**: When soft-deleting an event with attendees, all participants will asynchronously receive a cancellation email containing an `.ics` CANCEL attachment via Cloudflare `waitUntil`.\n- **Recovery**: The record is preserved in the database as a soft-deleted row and can be brought back using `POST /calendar/events/{id}/restore`.\n\n### Troubleshooting\n- Returns 404 `NOT_FOUND` if the event does not exist, or has already been soft-deleted.\n- Returns 409 `VERSION_CONFLICT` if `expected_version` does not match the database value.\n\nExamples:\n $ wspc event rm evt_xxx\n").argument("<id>", "id").option("--expected-version <value>", "Optional optimistic lock. Omit to let the server use the current version; pass only to fail with 409 `VERSION_CONFLICT` if someone else has mutated the event since you last read.").action(async (id, opts) => {
2723
2751
  const client2 = await loadSdkClient();
2724
2752
  const result = await eventDelete({
2725
2753
  client: client2._rawClient,
@@ -2743,7 +2771,7 @@ var eventDeleteCommand = new Command18("rm").description("Soft-delete a calendar
2743
2771
 
2744
2772
  // src/generated/cli/event/show.ts
2745
2773
  import { Command as Command19 } from "commander";
2746
- var eventGetCommand = new Command19("show").description("Get a calendar event by id").argument("<id>", "id").option("--include-deleted <value>", "When `true`, return the row even if soft-deleted. Default `false` (returns 404).").action(async (id, opts) => {
2774
+ var eventGetCommand = new Command19("show").description("Get a calendar event by id").addHelpText("after", "\n### Overview\nFetch a single calendar event by its unique ID, returning the complete record including all attendees.\n\n### When to Use\nDisplay a detailed view of an event, or read the latest database state (capturing `version`) prior to executing an optimistic-locked PATCH update.\n\n### Constraints\n- **Deleted Events**: Soft-deleted events return 404 `NOT_FOUND` by default. You must explicitly pass `include_deleted=true` in the query parameters to retrieve a soft-deleted row.\n- **ICS Format**: To download the RFC 5545 iCalendar text representation of this event, use `GET /calendar/events/{id}.ics` instead.\n\n### Troubleshooting\n- Returns 404 `NOT_FOUND` if the event does not exist, is soft-deleted (without `include_deleted=true`), or is owned by another user.\n\nExamples:\n $ wspc event show evt_xxx\n").argument("<id>", "id").option("--include-deleted <value>", "When `true`, return the row even if soft-deleted. Default `false` (returns 404).").action(async (id, opts) => {
2747
2775
  const client2 = await loadSdkClient();
2748
2776
  const result = await eventGet({
2749
2777
  client: client2._rawClient,
@@ -2767,7 +2795,7 @@ var eventGetCommand = new Command19("show").description("Get a calendar event by
2767
2795
 
2768
2796
  // src/generated/cli/event/set.ts
2769
2797
  import { Command as Command20 } from "commander";
2770
- var eventUpdateCommand = new Command20("set").description("Update a calendar event").argument("<id>", "id").option("--expected-version <value>", "Optional optimistic lock. Omit to let the server use the current version; pass only to fail the call if someone else has mutated the event since you last read. On mismatch the server returns 409 `VERSION_CONFLICT` and includes the current and sent versions in the message.").option("--title <value>", "New event title. Omit to leave unchanged.").option("--description <value>", "New description. Markdown formatted (CommonMark + GFM tables, strikethrough, task lists). Pass an empty string to clear; omit to leave unchanged.").option("--start <value>", "Accepts ISO 8601 datetime with offset (e.g. `2026-06-01T12:30:00+08:00`) for timed events, or ISO date-only (e.g. `2026-06-01`) for all-day. The `wspc` CLI additionally accepts natural-language phrases (`tomorrow 12:30pm`, `next Monday 9am`) and resolves them to ISO before sending; the server itself only accepts ISO. All-day uses RFC 5545 exclusive end: a one-day event on 6/1 is `start=2026-06-01, end=2026-06-02`; both endpoints must be the same type.").option("--end <value>", "Accepts ISO 8601 datetime with offset (e.g. `2026-06-01T12:30:00+08:00`) for timed events, or ISO date-only (e.g. `2026-06-01`) for all-day. The `wspc` CLI additionally accepts natural-language phrases (`tomorrow 12:30pm`, `next Monday 9am`) and resolves them to ISO before sending; the server itself only accepts ISO. All-day uses RFC 5545 exclusive end: a one-day event on 6/1 is `start=2026-06-01, end=2026-06-02`; both endpoints must be the same type.").option("-l, --location <value>", "New location. Pass an empty string to clear; omit to leave unchanged.").option("-u, --url <value>", "New meeting link. Pass an empty string to clear; omit to leave unchanged.").option("--status <value>", "Lifecycle status. `confirmed`: the event will happen (default). `tentative`: organizer has not finalized; still visible in lists. `cancelled`: the event was called off but the record is kept so attendees can be notified and history audited; distinct from soft-delete (DELETE `/calendar/events/{id}`) which hides the event from default list responses.").option("--attendee <value>", "If provided, REPLACES the attendee list (after case-insensitive email dedupe, up to 50). Added attendees receive a fresh invitation, kept attendees receive an update email, removed attendees receive a cancellation.", (val, memo) => {
2798
+ var eventUpdateCommand = new Command20("set").description("Update a calendar event").addHelpText("after", '\n### Overview\nPartially update fields of an existing calendar event. All properties in the request body are optional.\n\n### When to Use\nReschedule an event, update its location or notes, cancel the meeting (retaining the record but notifying participants), or replace/update the attendee list.\n\n### Constraints\n- **Optimistic Locking**: Pass `expected_version` to fail with `VERSION_CONFLICT` if another mutation occurred concurrently since you last read. Omit to let the server force the update.\n- **Field Clearing**: Pass an empty string `""` for `description`, `location`, or `url` to clear those fields in the database.\n- **Attendee replacement**: Providing the `attendees` property fully REPLACES the existing participant list. The server automatically diffs participants and asynchronously sends invitations (for newly added), updates (for kept), or cancellations (for removed) via Cloudflare `waitUntil`.\n- **Validation Rules**: Mismatched start/end formats or chronological order violations will fail the request.\n- **Attendee Limit**: A maximum of 50 unique attendees is allowed.\n\n### Troubleshooting\n- Returns 404 `NOT_FOUND` if the event does not exist or is soft-deleted.\n- Returns 409 `VERSION_CONFLICT` if `expected_version` is provided but stale.\n- Returns 400 `VALIDATION_ERROR` if `start` and `end` kinds do not match, or if `end <= start`.\n- Returns 400 `ATTENDEE_LIMIT_EXCEEDED` if unique attendees exceed 50.\n\nExamples:\n $ wspc event set evt_xxx --start "tomorrow 1pm" --end "tomorrow 2pm"\n $ wspc event set evt_xxx --status cancelled\n').argument("<id>", "id").option("--expected-version <value>", "Optional optimistic lock. Omit to let the server use the current version; pass only to fail the call if someone else has mutated the event since you last read. On mismatch the server returns 409 `VERSION_CONFLICT` and includes the current and sent versions in the message.").option("--title <value>", "New event title. Omit to leave unchanged.").option("--description <value>", "New description. Markdown formatted (CommonMark + GFM tables, strikethrough, task lists). Pass an empty string to clear; omit to leave unchanged.").option("--start <value>", "Accepts ISO 8601 datetime with offset (e.g. `2026-06-01T12:30:00+08:00`) for timed events, or ISO date-only (e.g. `2026-06-01`) for all-day. The `wspc` CLI additionally accepts natural-language phrases (`tomorrow 12:30pm`, `next Monday 9am`) and resolves them to ISO before sending; the server itself only accepts ISO. All-day uses RFC 5545 exclusive end: a one-day event on 6/1 is `start=2026-06-01, end=2026-06-02`; both endpoints must be the same type.").option("--end <value>", "Accepts ISO 8601 datetime with offset (e.g. `2026-06-01T12:30:00+08:00`) for timed events, or ISO date-only (e.g. `2026-06-01`) for all-day. The `wspc` CLI additionally accepts natural-language phrases (`tomorrow 12:30pm`, `next Monday 9am`) and resolves them to ISO before sending; the server itself only accepts ISO. All-day uses RFC 5545 exclusive end: a one-day event on 6/1 is `start=2026-06-01, end=2026-06-02`; both endpoints must be the same type.").option("-l, --location <value>", "New location. Pass an empty string to clear; omit to leave unchanged.").option("-u, --url <value>", "New meeting link. Pass an empty string to clear; omit to leave unchanged.").option("--status <value>", "Lifecycle status. `confirmed`: the event will happen (default). `tentative`: organizer has not finalized; still visible in lists. `cancelled`: the event was called off but the record is kept so attendees can be notified and history audited; distinct from soft-delete (DELETE `/calendar/events/{id}`) which hides the event from default list responses.").option("--attendee <value>", "If provided, REPLACES the attendee list (after case-insensitive email dedupe, up to 50). Added attendees receive a fresh invitation, kept attendees receive an update email, removed attendees receive a cancellation.", (val, memo) => {
2771
2799
  memo.push(val);
2772
2800
  return memo;
2773
2801
  }, []).option("--all-day", "all_day").option("--tz <zone>", "IANA timezone for relative time parsing").action(async (id, opts) => {
@@ -2821,7 +2849,7 @@ var eventUpdateCommand = new Command20("set").description("Update a calendar eve
2821
2849
 
2822
2850
  // src/generated/cli/event/ics.ts
2823
2851
  import { Command as Command21 } from "commander";
2824
- var eventIcsDownloadCommand = new Command21("ics").description("Download event as `.ics`").argument("<id>", "id").action(async (id, opts) => {
2852
+ var eventIcsDownloadCommand = new Command21("ics").description("Download event as `.ics`").addHelpText("after", "\n### Overview\nReturn a single event rendered as an RFC 5545 `.ics` file suitable for import into major calendar clients.\n\n### When to Use\nExpose a 'Save to my calendar' link in email notifications, show a download button in a UI, or programmatically forward raw iCalendar text to third parties.\n\n### Constraints\n- **Router Match**: The path parameter `filename` must be exactly `<event_id>.ics`. The `.ics` suffix is strictly required for the router to match this endpoint ahead of the JSON detail endpoint.\n- **Response Payload**: The response is plain text containing the iCalendar specification, NOT JSON. The `Content-Type` is set to `text/calendar; charset=utf-8` with `Content-Disposition: inline; filename=\"<event_id>.ics\"`.\n- **Authentication**: Standard authentication is required (Bearer API key or OAuth access token), as this endpoint is secure.\n\n### Troubleshooting\n- Returns 404 `NOT_FOUND` if the event does not exist, is soft-deleted, or is owned by another user.\n\nExamples:\n $ wspc event ics evt_xxx > event.ics\n").argument("<id>", "id").action(async (id, opts) => {
2825
2853
  const client2 = await loadSdkClient();
2826
2854
  const result = await eventIcsDownload({
2827
2855
  client: client2._rawClient,
@@ -2842,7 +2870,7 @@ var eventIcsDownloadCommand = new Command21("ics").description("Download event a
2842
2870
 
2843
2871
  // src/generated/cli/event/restore.ts
2844
2872
  import { Command as Command22 } from "commander";
2845
- var eventRestoreCommand = new Command22("restore").description("Restore a soft-deleted event").argument("<id>", "id").option("--expected-version <value>", "Optional optimistic lock. Omit to let the server use the current version; pass only to fail with 409 `VERSION_CONFLICT` if someone else has mutated the event since you last read.").action(async (id, opts) => {
2873
+ var eventRestoreCommand = new Command22("restore").description("Restore a soft-deleted event").addHelpText("after", "\n### Overview\nRestore a previously soft-deleted calendar event, making it active and visible in default list queries.\n\n### When to Use\nRecover an event that was accidentally soft-deleted.\n\n### Constraints\n- **Optimistic Locking**: Supports optional optimistic locking via `expected_version` in the request body.\n- **Side Effects**: When restoring an event with attendees, all participants will asynchronously receive a new invitation email containing an `.ics` REQUEST attachment via Cloudflare `waitUntil`.\n- **State Integrity**: You cannot restore a live (non-deleted) event. Restoring a live event is treated as an error.\n\n### Troubleshooting\n- Returns 404 `NOT_FOUND` if the event does not exist, or is NOT currently soft-deleted.\n- Returns 409 `VERSION_CONFLICT` if `expected_version` does not match the database value.\n").argument("<id>", "id").option("--expected-version <value>", "Optional optimistic lock. Omit to let the server use the current version; pass only to fail with 409 `VERSION_CONFLICT` if someone else has mutated the event since you last read.").action(async (id, opts) => {
2846
2874
  const client2 = await loadSdkClient();
2847
2875
  const result = await eventRestore({
2848
2876
  client: client2._rawClient,
@@ -2866,7 +2894,7 @@ var eventRestoreCommand = new Command22("restore").description("Restore a soft-d
2866
2894
 
2867
2895
  // src/generated/cli/drive/library/add.ts
2868
2896
  import { Command as Command23 } from "commander";
2869
- var driveLibraryCreateCommand = new Command23("add").description("Create a drive library").argument("<name>", "name").action(async (name, opts) => {
2897
+ var driveLibraryCreateCommand = new Command23("add").description("Create a drive library").addHelpText("after", "\nCreate an organization-scoped Drive / Library container.\n").argument("<name>", "name").action(async (name, opts) => {
2870
2898
  const client2 = await loadSdkClient();
2871
2899
  const result = await driveLibraryCreate({
2872
2900
  client: client2._rawClient,
@@ -2887,7 +2915,7 @@ var driveLibraryCreateCommand = new Command23("add").description("Create a drive
2887
2915
 
2888
2916
  // src/generated/cli/drive/library/ls.ts
2889
2917
  import { Command as Command24 } from "commander";
2890
- var driveLibraryListCommand = new Command24("ls").description("List drive libraries").option("--limit <value>", "limit").option("--cursor <value>", "cursor").option("--include-deleted <value>", "include_deleted").action(async (opts) => {
2918
+ var driveLibraryListCommand = new Command24("ls").description("List drive libraries").addHelpText("after", "\nList libraries in the caller organization with cursor pagination.\n").option("--limit <value>", "limit").option("--cursor <value>", "cursor").option("--include-deleted <value>", "include_deleted").action(async (opts) => {
2891
2919
  const client2 = await loadSdkClient();
2892
2920
  const result = await driveLibraryList({
2893
2921
  client: client2._rawClient,
@@ -2910,7 +2938,7 @@ var driveLibraryListCommand = new Command24("ls").description("List drive librar
2910
2938
 
2911
2939
  // src/generated/cli/drive/file/rm.ts
2912
2940
  import { Command as Command25 } from "commander";
2913
- var driveFileDeleteCommand = new Command25("rm").description("Delete a drive file").argument("<id>", "id").argument("<path>", "path").option("--expected-entry-version <value>", "expected_entry_version").action(async (id, path, opts) => {
2941
+ var driveFileDeleteCommand = new Command25("rm").description("Delete a drive file").addHelpText("after", "\nTombstone an active file using optimistic entry version locking.\n").argument("<id>", "id").argument("<path>", "path").option("--expected-entry-version <value>", "expected_entry_version").action(async (id, path, opts) => {
2914
2942
  const client2 = await loadSdkClient();
2915
2943
  const result = await driveFileDelete({
2916
2944
  client: client2._rawClient,
@@ -2935,7 +2963,7 @@ var driveFileDeleteCommand = new Command25("rm").description("Delete a drive fil
2935
2963
 
2936
2964
  // src/generated/cli/drive/library/rm.ts
2937
2965
  import { Command as Command26 } from "commander";
2938
- var driveLibraryDeleteCommand = new Command26("rm").description("Delete a drive library").argument("<id>", "id").option("--expected-version <value>", "expected_version").action(async (id, opts) => {
2966
+ var driveLibraryDeleteCommand = new Command26("rm").description("Delete a drive library").addHelpText("after", "\nSoft-delete an empty library using optimistic version locking.\n").argument("<id>", "id").option("--expected-version <value>", "expected_version").action(async (id, opts) => {
2939
2967
  const client2 = await loadSdkClient();
2940
2968
  const result = await driveLibraryDelete({
2941
2969
  client: client2._rawClient,
@@ -2959,7 +2987,7 @@ var driveLibraryDeleteCommand = new Command26("rm").description("Delete a drive
2959
2987
 
2960
2988
  // src/generated/cli/drive/library/show.ts
2961
2989
  import { Command as Command27 } from "commander";
2962
- var driveLibraryGetCommand = new Command27("show").description("Get a drive library").argument("<id>", "id").action(async (id, opts) => {
2990
+ var driveLibraryGetCommand = new Command27("show").description("Get a drive library").addHelpText("after", "\nFetch one active library by id. Cross-org and soft-deleted rows are hidden.\n").argument("<id>", "id").action(async (id, opts) => {
2963
2991
  const client2 = await loadSdkClient();
2964
2992
  const result = await driveLibraryGet({
2965
2993
  client: client2._rawClient,
@@ -2980,7 +3008,7 @@ var driveLibraryGetCommand = new Command27("show").description("Get a drive libr
2980
3008
 
2981
3009
  // src/generated/cli/drive/library/update.ts
2982
3010
  import { Command as Command28 } from "commander";
2983
- var driveLibraryUpdateCommand = new Command28("update").description("Update a drive library").argument("<id>", "id").option("--name <value>", "name").option("--expected-version <value>", "expected_version").action(async (id, opts) => {
3011
+ var driveLibraryUpdateCommand = new Command28("update").description("Update a drive library").addHelpText("after", "\nRename a library using optimistic version locking.\n").argument("<id>", "id").option("--name <value>", "name").option("--expected-version <value>", "expected_version").action(async (id, opts) => {
2984
3012
  const client2 = await loadSdkClient();
2985
3013
  const result = await driveLibraryUpdate({
2986
3014
  client: client2._rawClient,
@@ -3005,7 +3033,7 @@ var driveLibraryUpdateCommand = new Command28("update").description("Update a dr
3005
3033
 
3006
3034
  // src/generated/cli/drive/file/history.ts
3007
3035
  import { Command as Command29 } from "commander";
3008
- var driveFileHistoryCommand = new Command29("history").description("Get drive file version history").argument("<id>", "id").option("--path <value>", "path").action(async (id, opts) => {
3036
+ var driveFileHistoryCommand = new Command29("history").description("Get drive file version history").addHelpText("after", "\nList stored versions of a file, newest first.\n").argument("<id>", "id").option("--path <value>", "path").action(async (id, opts) => {
3009
3037
  const client2 = await loadSdkClient();
3010
3038
  const result = await driveFileHistory({
3011
3039
  client: client2._rawClient,
@@ -3029,7 +3057,7 @@ var driveFileHistoryCommand = new Command29("history").description("Get drive fi
3029
3057
 
3030
3058
  // src/generated/cli/drive/manifest/get.ts
3031
3059
  import { Command as Command30 } from "commander";
3032
- var driveManifestGetCommand = new Command30("get").description("Get a drive library manifest").argument("<id>", "id").option("--limit <value>", "limit").option("--cursor <value>", "cursor").option("--include-deleted <value>", "include_deleted").option("--path-prefix <value>", "path_prefix").option("--since-cursor <value>", "since_cursor").action(async (id, opts) => {
3060
+ var driveManifestGetCommand = new Command30("get").description("Get a drive library manifest").addHelpText("after", "\nList file entries for sync using path/id cursor pagination.\n").argument("<id>", "id").option("--limit <value>", "limit").option("--cursor <value>", "cursor").option("--include-deleted <value>", "include_deleted").option("--path-prefix <value>", "path_prefix").option("--since-cursor <value>", "since_cursor").action(async (id, opts) => {
3033
3061
  const client2 = await loadSdkClient();
3034
3062
  const result = await driveManifestGet({
3035
3063
  client: client2._rawClient,
@@ -3057,7 +3085,7 @@ var driveManifestGetCommand = new Command30("get").description("Get a drive libr
3057
3085
 
3058
3086
  // src/generated/cli/drive/file/restore.ts
3059
3087
  import { Command as Command31 } from "commander";
3060
- var driveFileRestoreCommand = new Command31("restore").description("Restore a drive file version").argument("<id>", "id").option("--path <value>", "path").option("--version-id <value>", "version_id").action(async (id, opts) => {
3088
+ var driveFileRestoreCommand = new Command31("restore").description("Restore a drive file version").addHelpText("after", "\nPromote a previous file version to be the current content.\n").argument("<id>", "id").option("--path <value>", "path").option("--version-id <value>", "version_id").action(async (id, opts) => {
3061
3089
  const client2 = await loadSdkClient();
3062
3090
  const result = await driveFileRestore({
3063
3091
  client: client2._rawClient,
@@ -3082,7 +3110,7 @@ var driveFileRestoreCommand = new Command31("restore").description("Restore a dr
3082
3110
 
3083
3111
  // src/generated/cli/drive/search.ts
3084
3112
  import { Command as Command32 } from "commander";
3085
- var driveSearchCommand = new Command32("search").description("Search drive library text").argument("<id>", "id").option("--query <value>", "query").option("--limit <value>", "limit").action(async (id, opts) => {
3113
+ var driveSearchCommand = new Command32("search").description("Search drive library text").addHelpText("after", "\nFull-text search over indexed text files in a library (FTS5).\n").argument("<id>", "id").option("--query <value>", "query").option("--limit <value>", "limit").action(async (id, opts) => {
3086
3114
  const client2 = await loadSdkClient();
3087
3115
  const result = await driveSearch({
3088
3116
  client: client2._rawClient,
@@ -3107,7 +3135,7 @@ var driveSearchCommand = new Command32("search").description("Search drive libra
3107
3135
 
3108
3136
  // src/generated/cli/alias/add.ts
3109
3137
  import { Command as Command33 } from "commander";
3110
- var emailAliasCreateCommand = new Command33("add").description("Create a receiving alias").argument("<email>", "email").action(async (email, opts) => {
3138
+ var emailAliasCreateCommand = new Command33("add").description("Create a receiving alias").addHelpText("after", "\n### Overview\nReserves and provisions a new passwordless/disposable receiving email alias address under the configured WSPC domain or a fully verified organization custom domain. All inbound emails received on this alias will be forwarded into the caller's inbox.\n\n### When to Use\n- Use this endpoint to spin up a fresh, dedicated email address (e.g., `alice-shop@wspc.app`) for specific websites, newsletters, or contexts to prevent spam or categorize incoming mail.\n\n### Constraints\n- Requires a valid Bearer token in the `Authorization` header.\n- **Alias Formatting**: The local part must be between 5 and 32 characters, start with an alphanumeric character, and only contain letters, numbers, dots, underscores, and hyphens.\n- **Custom Domains**: If the address uses a non-platform host, that domain must be registered to the caller's organization and have `status = verified`, `sending_status = verified`, and `receiving_status = verified`.\n- **Limit Check**: Each user is allowed a maximum of 10 active email aliases. Soft-deleted aliases do not count against this quota limit.\n\n### Troubleshooting\n- **401 Unauthorized**: Bearer token is missing, invalid, or expired.\n- **400 Bad Request / INVALID_CHARSET / RESERVED**: The alias local part contains invalid characters, is too short/long, or matches a reserved keyword.\n- **400 Bad Request / ALIAS_DOMAIN_NOT_FOUND**: The custom domain is not registered to the caller's organization.\n- **400 Bad Request / UNVERIFIED_DOMAIN**: The custom domain exists but is not verified yet.\n- **400 Bad Request / ALIAS_DOMAIN_NOT_READY**: The custom domain exists but has not completed sending or receiving verification.\n- **409 Conflict / ALIAS_CONFLICT**: An alias with the exact requested email address already exists globally (whether active or soft-deleted by any user).\n- **429 Too Many Requests / ALIAS_LIMIT_EXCEEDED**: The user has reached the active alias cap limit of 10. A previously deleted alias must be cleaned up or wait for quota availability.\n").argument("<email>", "email").action(async (email, opts) => {
3111
3139
  const client2 = await loadSdkClient();
3112
3140
  const result = await emailAliasCreate({
3113
3141
  client: client2._rawClient,
@@ -3128,7 +3156,7 @@ var emailAliasCreateCommand = new Command33("add").description("Create a receivi
3128
3156
 
3129
3157
  // src/generated/cli/alias/ls.ts
3130
3158
  import { Command as Command34 } from "commander";
3131
- var emailAliasListCommand = new Command34("ls").description("List the caller's aliases").option("--include-deleted", "include_deleted").action(async (opts) => {
3159
+ var emailAliasListCommand = new Command34("ls").description("List the caller's aliases").addHelpText("after", "\n### Overview\nRetrieves a list of all email receiving aliases owned by the authenticated user.\n\n### When to Use\n- Use this endpoint to render an alias directory in user profiles or to provide a selection list of verified sender aliases in an email compose interface.\n\n### Constraints\n- Requires a valid Bearer token in the `Authorization` header.\n- By default, only active receiving aliases are returned. Pass `include_deleted=true` in the query to also fetch soft-deleted aliases (which have `deleted_at` timestamps set).\n\n### Troubleshooting\n- **401 Unauthorized**: Active token is missing, expired, or invalid.\n").option("--include-deleted", "include_deleted").action(async (opts) => {
3132
3160
  const client2 = await loadSdkClient();
3133
3161
  const result = await emailAliasList({
3134
3162
  client: client2._rawClient,
@@ -3149,7 +3177,7 @@ var emailAliasListCommand = new Command34("ls").description("List the caller's a
3149
3177
 
3150
3178
  // src/generated/cli/domain/add.ts
3151
3179
  import { Command as Command35 } from "commander";
3152
- var emailDomainCreateCommand = new Command35("add").description("Register a custom email domain").argument("<domain>", "domain").action(async (domain, opts) => {
3180
+ var emailDomainCreateCommand = new Command35("add").description("Register a custom email domain").addHelpText("after", "\n### Overview\nRegisters a new organization-owned custom email domain with the upstream provider and caches the returned DNS verification records in D1.\n\n### When to Use\n- Use this endpoint when onboarding a new custom email domain such as `mail.example.com`.\n- The response contains the DNS records the organization must publish before the domain can be verified.\n- This route registers the domain and returns DNS records. Custom-domain aliases require `status`, `sending_status`, and `receiving_status` to all be `verified`.\n\n### Constraints\n- Requires a valid Bearer token in the `Authorization` header.\n- Domain ownership is globally unique across the platform. Once any organization has reserved a domain, another org cannot register it.\n- This route requires custom domain provider credentials in production because it performs a live provider registration call.\n\n### Troubleshooting\n- **400 Bad Request / DOMAIN_INVALID / DOMAIN_RESERVED**: The hostname is malformed or belongs to the platform (`wspc.app` or its subdomains).\n- **409 Conflict / DOMAIN_CONFLICT**: The domain is already registered by some organization.\n- **502 Bad Gateway / DOMAIN_PROVIDER_ERROR**: The upstream provider request failed, timed out, or returned an unexpected shape.\n").argument("<domain>", "domain").action(async (domain, opts) => {
3153
3181
  const client2 = await loadSdkClient();
3154
3182
  const result = await emailDomainCreate({
3155
3183
  client: client2._rawClient,
@@ -3170,7 +3198,7 @@ var emailDomainCreateCommand = new Command35("add").description("Register a cust
3170
3198
 
3171
3199
  // src/generated/cli/domain/ls.ts
3172
3200
  import { Command as Command36 } from "commander";
3173
- var emailDomainListCommand = new Command36("ls").description("List cached custom domains").action(async (opts) => {
3201
+ var emailDomainListCommand = new Command36("ls").description("List cached custom domains").addHelpText("after", "\n### Overview\nReturns the caller organization's cached custom email domains from D1. This route does not call the upstream provider.\n\n### When to Use\n- Use this to render an admin view of all registered domains and their latest known verification state.\n- Use it to inspect DNS records that were previously fetched during create or verify operations.\n- The cached state includes DNS ownership, sending readiness, and receiving readiness used by custom-domain alias creation.\n\n### Constraints\n- Requires a valid Bearer token in the `Authorization` header.\n- Results are scoped to the caller organization and sorted newest-first by creation time.\n").action(async (opts) => {
3174
3202
  const client2 = await loadSdkClient();
3175
3203
  const result = await emailDomainList({
3176
3204
  client: client2._rawClient
@@ -3188,7 +3216,7 @@ var emailDomainListCommand = new Command36("ls").description("List cached custom
3188
3216
 
3189
3217
  // src/generated/cli/alias/rm.ts
3190
3218
  import { Command as Command37 } from "commander";
3191
- var emailAliasDeleteCommand = new Command37("rm").description("Soft-delete an alias").argument("<email>", "email").action(async (email, opts) => {
3219
+ var emailAliasDeleteCommand = new Command37("rm").description("Soft-delete an alias").addHelpText("after", "\n### Overview\nSoft-deletes a specific active email receiving alias owned by the caller. Once soft-deleted, the alias stops accepting and forwarding any new inbound emails.\n\n### When to Use\n- Use this endpoint when decommissioning a disposable alias address that is no longer needed or is receiving excessive spam.\n\n### Constraints\n- Requires a valid Bearer token in the `Authorization` header.\n- **Data Retention**: Soft-deletion is immediate. Inbound mail forwarding stops, but historical emails previously received on this alias remain fully readable in the inbox.\n- **Restoration**: The alias remains globally reserved and cannot be created fresh by anyone; use `POST /email/aliases/{email}/restore` to reactivate.\n- **Path Parameter**: The `@` character in the `{email}` path parameter must be URL-encoded as `%40`.\n\n### Troubleshooting\n- **401 Unauthorized**: Missing or invalid token.\n- **404 Not Found**: No active alias with this exact address was found for the authenticated user, or the alias is already deleted.\n").argument("<email>", "email").action(async (email, opts) => {
3192
3220
  const client2 = await loadSdkClient();
3193
3221
  const result = await emailAliasDelete({
3194
3222
  client: client2._rawClient,
@@ -3209,7 +3237,7 @@ var emailAliasDeleteCommand = new Command37("rm").description("Soft-delete an al
3209
3237
 
3210
3238
  // src/generated/cli/email/rm.ts
3211
3239
  import { Command as Command38 } from "commander";
3212
- var emailDeleteCommand = new Command38("rm").description("Soft-delete inbound emails").argument("<id...>", "id").action(async (id, opts) => {
3240
+ var emailDeleteCommand = new Command38("rm").description("Soft-delete inbound emails").addHelpText("after", "\n### Overview\nSoft-deletes a batch of inbound emails, moving them to the trash. Soft-deleted emails are immediately excluded from default inbox lists.\n\n### When to Use\n- Use this endpoint to trash one or more email messages from a user's inbox view.\n\n### Constraints\n- Requires a valid Bearer token in the `Authorization` header.\n- Accepts 1 to 100 email IDs per call.\n- Deletion is fully reversible: soft-deleted rows persist in the database and can be undeleted using the restore endpoint.\n- **Data Cleanup**: Out-of-band background processes eventually purge associated raw MIME source payloads and attachment bytes from R2; deletion does not immediately free storage.\n\n### Troubleshooting\n- **401 Unauthorized**: Invalid Bearer token.\n- **400 Bad Request**: The request body is malformed or exceeds the maximum limit of 100 IDs.\n").argument("<id...>", "id").action(async (id, opts) => {
3213
3241
  const idRaw = id;
3214
3242
  const ids = idRaw.length > 0 ? idRaw : void 0;
3215
3243
  const client2 = await loadSdkClient();
@@ -3232,7 +3260,7 @@ var emailDeleteCommand = new Command38("rm").description("Soft-delete inbound em
3232
3260
 
3233
3261
  // src/generated/cli/domain/rm.ts
3234
3262
  import { Command as Command39 } from "commander";
3235
- var emailDomainDeleteCommand = new Command39("rm").description("Delete a custom email domain").argument("<domain>", "domain").action(async (domain, opts) => {
3263
+ var emailDomainDeleteCommand = new Command39("rm").description("Delete a custom email domain").addHelpText("after", "\n### Overview\nDeletes one active custom email domain for the caller organization. The worker first confirms no active aliases use the domain, deletes the upstream provider resource, then soft-deletes the cached D1 row.\n\n### When to Use\n- Use this when an organization no longer wants WSPC to manage a custom email domain.\n- Delete is only allowed when no active aliases use the domain.\n- Deleted domains are hidden from active list/get/verify/delete surfaces and cannot be self-restored in this version.\n\n### Constraints\n- Requires a valid Bearer token in the `Authorization` header.\n- This route requires custom domain provider credentials in production because it performs a live provider delete call.\n- Provider identifiers and provider raw errors are never returned to the client.\n\n### Troubleshooting\n- **400 Bad Request / DOMAIN_INVALID / DOMAIN_RESERVED**: The path hostname is malformed or reserved.\n- **404 Not Found / DOMAIN_NOT_FOUND**: The domain does not exist, belongs to another organization, or was already deleted.\n- **409 Conflict / DOMAIN_IN_USE**: Active aliases still use this domain.\n- **502 Bad Gateway / DOMAIN_PROVIDER_ERROR**: Provider delete failed, timed out, or credentials are missing.\n").argument("<domain>", "domain").action(async (domain, opts) => {
3236
3264
  const client2 = await loadSdkClient();
3237
3265
  const result = await emailDomainDelete({
3238
3266
  client: client2._rawClient,
@@ -3253,7 +3281,7 @@ var emailDomainDeleteCommand = new Command39("rm").description("Delete a custom
3253
3281
 
3254
3282
  // src/generated/cli/domain/show.ts
3255
3283
  import { Command as Command40 } from "commander";
3256
- var emailDomainGetCommand = new Command40("show").description("Get one cached custom domain").argument("<domain>", "domain").action(async (domain, opts) => {
3284
+ var emailDomainGetCommand = new Command40("show").description("Get one cached custom domain").addHelpText("after", "\n### Overview\nReturns the caller organization's cached state for one custom email domain. This is a pure D1 read and never calls the upstream provider.\n\n### When to Use\n- Use this to inspect the latest cached DNS records or verification status for a single domain.\n- This cached view includes ownership, sending readiness, and receiving readiness state for custom-domain alias decisions.\n\n### Constraints\n- Requires a valid Bearer token in the `Authorization` header.\n- The `{domain}` path parameter is normalized and validated server-side before lookup.\n\n### Troubleshooting\n- **400 Bad Request / DOMAIN_INVALID / DOMAIN_RESERVED**: The path hostname is malformed or reserved.\n- **404 Not Found / DOMAIN_NOT_FOUND**: The domain does not exist or belongs to another organization.\n").argument("<domain>", "domain").action(async (domain, opts) => {
3257
3285
  const client2 = await loadSdkClient();
3258
3286
  const result = await emailDomainGet({
3259
3287
  client: client2._rawClient,
@@ -3274,7 +3302,7 @@ var emailDomainGetCommand = new Command40("show").description("Get one cached cu
3274
3302
 
3275
3303
  // src/generated/cli/email/show.ts
3276
3304
  import { Command as Command41 } from "commander";
3277
- var emailGetCommand = new Command41("show").description("Get an inbound email by id").argument("<id>", "id").option("--include-html <value>", "When `true`, fetch the HTML body from R2 and include it as `html_body` in the response. Costs an extra R2 read; omit if you only need text.").option("--include-deleted <value>", "When `true`, allow fetching a soft-deleted email. Defaults to `false` (returns 404 for soft-deleted rows).").action(async (id, opts) => {
3305
+ var emailGetCommand = new Command41("show").description("Get an inbound email by id").addHelpText("after", "\n### Overview\nFetches the metadata and plain-text body of a single inbound email by its unique ID. It also returns metadata for all associated attachments and optionally resolves the rendered HTML content.\n\n### When to Use\n- Use this endpoint to display the complete detail view of an email message.\n- Use it to extract attachment files or read complex HTML layouts.\n\n### Constraints\n- Requires a valid Bearer token in the `Authorization` header.\n- **R2 HTML Read**: The HTML body is stored in Object Storage (R2). To fetch it, explicitly pass `include_html=true` (this incurs an extra R2 read charge; leave unset if only plain text is needed).\n- Returns a 404 error if the email has been soft-deleted, unless `include_deleted=true` is set.\n\n### Troubleshooting\n- **401 Unauthorized**: Missing or expired token.\n- **404 Not Found**: The specified email ID does not exist, belongs to another user, or has been soft-deleted (without `include_deleted=true`).\n").argument("<id>", "id").option("--include-html <value>", "When `true`, fetch the HTML body from R2 and include it as `html_body` in the response. Costs an extra R2 read; omit if you only need text.").option("--include-deleted <value>", "When `true`, allow fetching a soft-deleted email. Defaults to `false` (returns 404 for soft-deleted rows).").action(async (id, opts) => {
3278
3306
  const client2 = await loadSdkClient();
3279
3307
  const result = await emailGet({
3280
3308
  client: client2._rawClient,
@@ -3299,7 +3327,7 @@ var emailGetCommand = new Command41("show").description("Get an inbound email by
3299
3327
 
3300
3328
  // src/generated/cli/email/ls.ts
3301
3329
  import { Command as Command42 } from "commander";
3302
- var emailListCommand = new Command42("ls").description("List inbound emails").option("--limit <value>", "Max items to return (clamped to 1-100). Defaults to 20 server-side.").option("--alias-email <value>", "If set, only return emails received on this full alias email address.").option("--unread-only <value>", "When `true`, only return emails with `is_read=false`.").option("--since <value>", "Unix epoch milliseconds \u2014 only return emails with `received_at >= since`. Useful for incremental sync.").option("--cursor <value>", "Opaque pagination cursor returned in `next_cursor` of a previous response.").option("--include-deleted", "include_deleted").action(async (opts) => {
3330
+ var emailListCommand = new Command42("ls").description("List inbound emails").addHelpText("after", "\n### Overview\nRetrieves a paginated directory list of all inbound emails received by the user's active aliases, sorted in descending order of ingestion time (newest first).\n\n### When to Use\n- Use this endpoint to render mailbox dashboards or inbox streams.\n- Use query parameters to perform incremental syncs (via `since` timestamp) or to filter incoming mail by read state or target alias email.\n\n### Constraints\n- Requires a valid Bearer token in the `Authorization` header.\n- **Pagination**: Supports cursor-based pagination. Pass the returned `next_cursor` value back as the `cursor` query parameter to list subsequent pages. The `limit` is capped between 1 and 100, defaulting to 20.\n- By default, soft-deleted emails are hidden. Pass `include_deleted=true` to retrieve them.\n\n### Troubleshooting\n- **401 Unauthorized**: Missing, invalid, or expired Bearer token.\n- **400 Bad Request**: Malformed pagination cursor or invalid query parameters (e.g., non-integer limit).\n").option("--limit <value>", "Max items to return (clamped to 1-100). Defaults to 20 server-side.").option("--alias-email <value>", "If set, only return emails received on this full alias email address.").option("--unread-only <value>", "When `true`, only return emails with `is_read=false`.").option("--since <value>", "Unix epoch milliseconds \u2014 only return emails with `received_at >= since`. Useful for incremental sync.").option("--cursor <value>", "Opaque pagination cursor returned in `next_cursor` of a previous response.").option("--include-deleted", "include_deleted").action(async (opts) => {
3303
3331
  const client2 = await loadSdkClient();
3304
3332
  const result = await emailList({
3305
3333
  client: client2._rawClient,
@@ -3325,7 +3353,7 @@ var emailListCommand = new Command42("ls").description("List inbound emails").op
3325
3353
 
3326
3354
  // src/generated/cli/email/read.ts
3327
3355
  import { Command as Command43 } from "commander";
3328
- var emailMarkReadCommand = new Command43("read").description("Mark inbound emails as read").argument("<id...>", "id").action(async (id, opts) => {
3356
+ var emailMarkReadCommand = new Command43("read").description("Mark inbound emails as read").addHelpText("after", "\n### Overview\nMarks a batch of inbound emails as read. This batch operation is fully idempotent.\n\n### When to Use\n- Use this endpoint when a user opens an email detail view or performs a bulk mark-read action in an inbox dashboard.\n\n### Constraints\n- Requires a valid Bearer token in the `Authorization` header.\n- Accepts 1 to 100 email IDs in a single call.\n- **Idempotency**: Already-read IDs are silently processed without generating errors but do not count toward the returned `marked` value. Missing, unauthorized, or soft-deleted IDs will be logged in `not_found`.\n\n### Troubleshooting\n- **401 Unauthorized**: Invalid or missing Bearer token.\n- **400 Bad Request**: The request body is malformed or exceeds the maximum limit of 100 IDs.\n").argument("<id...>", "id").action(async (id, opts) => {
3329
3357
  const idRaw = id;
3330
3358
  const ids = idRaw.length > 0 ? idRaw : void 0;
3331
3359
  const client2 = await loadSdkClient();
@@ -3348,7 +3376,7 @@ var emailMarkReadCommand = new Command43("read").description("Mark inbound email
3348
3376
 
3349
3377
  // src/generated/cli/email/unread.ts
3350
3378
  import { Command as Command44 } from "commander";
3351
- var emailMarkUnreadCommand = new Command44("unread").description("Mark inbound emails as unread").argument("<id...>", "id").action(async (id, opts) => {
3379
+ var emailMarkUnreadCommand = new Command44("unread").description("Mark inbound emails as unread").addHelpText("after", "\n### Overview\nResets a batch of inbound emails back to an unread state.\n\n### When to Use\n- Use this endpoint to undo an accidental read marking or to mark messages for later review.\n\n### Constraints\n- Requires a valid Bearer token in the `Authorization` header.\n- Accepts 1 to 100 email IDs per call. Already-unread IDs are silently ignored but do not contribute to `marked`.\n\n### Troubleshooting\n- **401 Unauthorized**: Invalid Bearer token.\n- **400 Bad Request**: Malformed body or ID batch size limit exceeded.\n").argument("<id...>", "id").action(async (id, opts) => {
3352
3380
  const idRaw = id;
3353
3381
  const ids = idRaw.length > 0 ? idRaw : void 0;
3354
3382
  const client2 = await loadSdkClient();
@@ -3371,7 +3399,7 @@ var emailMarkUnreadCommand = new Command44("unread").description("Mark inbound e
3371
3399
 
3372
3400
  // src/generated/cli/alias/restore.ts
3373
3401
  import { Command as Command45 } from "commander";
3374
- var emailAliasRestoreCommand = new Command45("restore").description("Restore a soft-deleted alias").argument("<email>", "email").action(async (email, opts) => {
3402
+ var emailAliasRestoreCommand = new Command45("restore").description("Restore a soft-deleted alias").addHelpText("after", "\n### Overview\nReactivates a previously soft-deleted email receiving alias, immediately resuming mail forwarding to the user's inbox.\n\n### When to Use\n- Use this endpoint to re-enable a temporarily disabled alias or to recover one that was deleted by mistake.\n\n### Constraints\n- Requires a valid Bearer token in the `Authorization` header.\n- **Quota Check**: Reactivating an alias increases the active alias count towards the user's maximum quota of 10 active aliases. If the limit is exceeded, a `429 ALIAS_LIMIT_EXCEEDED` error is returned.\n- **Path Parameter**: The `@` character in the path parameter must be URL-encoded as `%40`.\n\n### Troubleshooting\n- **401 Unauthorized**: Missing or invalid token.\n- **404 Not Found**: No soft-deleted alias with this exact address was found for the authenticated user.\n- **429 Too Many Requests / ALIAS_LIMIT_EXCEEDED**: Reactivating this alias would exceed the per-user limit of 10 active aliases.\n").argument("<email>", "email").action(async (email, opts) => {
3375
3403
  const client2 = await loadSdkClient();
3376
3404
  const result = await emailAliasRestore({
3377
3405
  client: client2._rawClient,
@@ -3392,7 +3420,7 @@ var emailAliasRestoreCommand = new Command45("restore").description("Restore a s
3392
3420
 
3393
3421
  // src/generated/cli/email/restore.ts
3394
3422
  import { Command as Command46 } from "commander";
3395
- var emailRestoreCommand = new Command46("restore").description("Restore soft-deleted inbound emails").argument("<id...>", "id").action(async (id, opts) => {
3423
+ var emailRestoreCommand = new Command46("restore").description("Restore soft-deleted inbound emails").addHelpText("after", "\n### Overview\nRestores a batch of soft-deleted inbound emails from the trash, making them reappear in standard inbox lists.\n\n### When to Use\n- Use this endpoint to recover email messages that were trashed by mistake.\n\n### Constraints\n- Requires a valid Bearer token in the `Authorization` header.\n- Accepts 1 to 100 email IDs. Already-active IDs are silently ignored.\n\n### Troubleshooting\n- **401 Unauthorized**: Invalid token.\n- **400 Bad Request**: Malformed request or batch limit exceeded.\n").argument("<id...>", "id").action(async (id, opts) => {
3396
3424
  const idRaw = id;
3397
3425
  const ids = idRaw.length > 0 ? idRaw : void 0;
3398
3426
  const client2 = await loadSdkClient();
@@ -3415,7 +3443,7 @@ var emailRestoreCommand = new Command46("restore").description("Restore soft-del
3415
3443
 
3416
3444
  // src/generated/cli/domain/verify.ts
3417
3445
  import { Command as Command47 } from "commander";
3418
- var emailDomainVerifyCommand = new Command47("verify").description("Verify a custom domain with the provider").argument("<domain>", "domain").action(async (domain, opts) => {
3446
+ var emailDomainVerifyCommand = new Command47("verify").description("Verify a custom domain with the provider").addHelpText("after", "\n### Overview\nTriggers an upstream provider verification attempt for one custom email domain, refreshes the cached DNS records/status in D1, and returns the updated row.\nThis route refreshes DNS registration and verification state. Custom-domain aliases require `status`, `sending_status`, and `receiving_status` to all be `verified`.\n\n### When to Use\n- Use this after publishing the required DNS records, or whenever you want to refresh cached provider state explicitly.\n- If the provider verify call returns incomplete DNS records, the worker performs a follow-up provider read before responding.\n\n### Constraints\n- Requires a valid Bearer token in the `Authorization` header.\n- This route requires custom domain provider credentials in production because it performs live provider calls.\n- Verification is asynchronous provider work; a successful response may still report `status: pending`.\n- `status: verified` plus `sending_status: verified` enables custom-domain outbound send for active aliases; `receiving_status: verified` is also required before new custom-domain aliases can be created.\n\n### Troubleshooting\n- **400 Bad Request / DOMAIN_INVALID / DOMAIN_RESERVED**: The path hostname is malformed or reserved.\n- **404 Not Found / DOMAIN_NOT_FOUND**: The domain does not exist or belongs to another organization.\n- **502 Bad Gateway / DOMAIN_PROVIDER_ERROR**: Provider verification failed, timed out, or credentials are missing.\n").argument("<domain>", "domain").action(async (domain, opts) => {
3419
3447
  const client2 = await loadSdkClient();
3420
3448
  const result = await emailDomainVerify({
3421
3449
  client: client2._rawClient,
@@ -3436,7 +3464,7 @@ var emailDomainVerifyCommand = new Command47("verify").description("Verify a cus
3436
3464
 
3437
3465
  // src/generated/cli/push/config/rm.ts
3438
3466
  import { Command as Command48 } from "commander";
3439
- var pushConfigDeleteCommand = new Command48("rm").description("Remove a push transport").argument("<transport>", "transport").action(async (transport, opts) => {
3467
+ var pushConfigDeleteCommand = new Command48("rm").description("Remove a push transport").addHelpText("after", "\n### Overview\nDelete the configured push transport row, immediately halting push event dispatching for the caller.\n\n### When to Use\nWhen a user disconnects their notification channel, turns off push preferences, or resets their transport target.\n\n### Constraints\n- **Idempotency**: Deleting a transport that has not been registered (or was already deleted) is handled as a no-op, returning 204 `No Content`.\n- **Side Effects**: Hard-deletes the `(user_id, transport)` configuration record and completely purges all associated test history (`last_test_at` and `last_test_status`).\n- **Transport Support**: The path parameter must be a recognized transport identifier.\n\n### Troubleshooting\n- Returns 400 `UNKNOWN_TRANSPORT` if the transport parameter contains an unrecognized transport identifier.\n").argument("<transport>", "transport").action(async (transport, opts) => {
3440
3468
  const client2 = await loadSdkClient();
3441
3469
  const result = await pushConfigDelete({
3442
3470
  client: client2._rawClient,
@@ -3457,7 +3485,7 @@ var pushConfigDeleteCommand = new Command48("rm").description("Remove a push tra
3457
3485
 
3458
3486
  // src/generated/cli/push/config/set.ts
3459
3487
  import { Command as Command49 } from "commander";
3460
- var pushConfigSetCommand = new Command49("set").description("Register or update a push transport").option("--transport <value>", "Transport discriminator. `telegram` is the only supported value today \u2014 push delivers via a Telegram bot DM. Future transports (web push, iOS/Android, generic webhook) will be added as additional discriminator values.").option("--target-bot-username <value>", "Telegram bot username (with leading `@`, 5\u201332 alphanumeric/underscore characters). This is the bot the user has already started a chat with \u2014 wspc DMs notifications to it via the Telegram Bot API.").action(async (opts) => {
3488
+ var pushConfigSetCommand = new Command49("set").description("Register or update a push transport").addHelpText("after", "\n### Overview\nUpsert a notification transport configuration for the authenticated user. After registration, wspc can dispatch notifications to the user when registered product events fire.\n\n### When to Use\nFirst-time onboarding push configuration setup, or whenever the user updates their transport target details (e.g., pointing notifications to a new Telegram bot username).\n\n### Constraints\n- **Supported Transports**: Currently only `transport: telegram` is supported.\n- **Target Validation**: `target_bot_username` must be a valid Telegram bot name starting with `@` followed by 5\u201332 alphanumeric/underscore characters (`^@[A-Za-z0-9_]{5,32}$`).\n- **Uniqueness**: Up to one registration row is saved per `(user_id, transport)`. Upserting replaces any existing target config, updating `updated_at` while retaining `created_at`.\n- **No Side-effect Messages**: Registering a transport does **not** send a test notification; clients should separately trigger `POST /push/test`.\n\n### Troubleshooting\n- Returns 400 `INVALID_CONFIG` if payload structure is invalid or `target_bot_username` validation fails.\n").option("--transport <value>", "Transport discriminator. `telegram` is the only supported value today \u2014 push delivers via a Telegram bot DM. Future transports (web push, iOS/Android, generic webhook) will be added as additional discriminator values.").option("--target-bot-username <value>", "Telegram bot username (with leading `@`, 5\u201332 alphanumeric/underscore characters). This is the bot the user has already started a chat with \u2014 wspc DMs notifications to it via the Telegram Bot API.").action(async (opts) => {
3461
3489
  const client2 = await loadSdkClient();
3462
3490
  const result = await pushConfigSet({
3463
3491
  client: client2._rawClient,
@@ -3481,7 +3509,7 @@ var pushConfigSetCommand = new Command49("set").description("Register or update
3481
3509
 
3482
3510
  // src/generated/cli/push/config/show.ts
3483
3511
  import { Command as Command50 } from "commander";
3484
- var pushConfigGetCommand = new Command50("show").description("List the caller's push transports").action(async (opts) => {
3512
+ var pushConfigGetCommand = new Command50("show").description("List the caller's push transports").addHelpText("after", "\n### Overview\nRetrieve all active push transport configurations registered for the authenticated user.\n\n### When to Use\nRender settings page, determine if push notifications are enabled before prompting the user, or fetch historical health check results (`last_test_at` and `last_test_status`).\n\n### Constraints\n- **List Limitations**: Currently returns at most one active registration row (`telegram`).\n- **Data Security**: Response payload contains sensitive data (e.g. `target_bot_username`). Callers must handle these values as user secret-equivalent and prevent leakage.\n\n### Troubleshooting\n- Standard 401 Unauthorized or 403 Forbidden checks if authentication credentials are missing or invalid.\n").action(async (opts) => {
3485
3513
  const client2 = await loadSdkClient();
3486
3514
  const result = await pushConfigGet({
3487
3515
  client: client2._rawClient
@@ -3499,7 +3527,7 @@ var pushConfigGetCommand = new Command50("show").description("List the caller's
3499
3527
 
3500
3528
  // src/generated/cli/push/test.ts
3501
3529
  import { Command as Command51 } from "commander";
3502
- var pushTestCommand = new Command51("test").description("Send a test push notification").option("--transport <value>", "Which transport to send the test message through. Must match a transport the caller has already registered via `POST /push/config`; today only `telegram` is supported.").action(async (opts) => {
3530
+ var pushTestCommand = new Command51("test").description("Send a test push notification").addHelpText("after", "\n### Overview\nSynchronously dispatch a static test message via the requested transport target to verify delivery health.\n\n### When to Use\nImmediately after executing `POST /push/config` to verify connection legitimacy, or when troubleshooting missing notification claims.\n\n### Constraints\n- **Target Requirement**: You must have already successfully registered the targeted transport configuration.\n- **Side Effects**: Sends a single probe message to the upstream provider (e.g. Telegram Bot API). Test details are persisted to the configuration row under `last_test_at` and `last_test_status`.\n- **No Audit Footprint**: This operation is treated strictly as an integration probe and will not generate a product audit log footprint.\n\n### Troubleshooting\n- **Upstream Error Handling**: This endpoint returns an HTTP `200 OK` status even if the upstream dispatch fails. Callers must inspect `ok: false` and review `status` and `detail` in the response JSON to verify connection health.\n- Returns 404 `NO_CONFIG` if the user has not registered configuration details for the requested transport.\n").option("--transport <value>", "Which transport to send the test message through. Must match a transport the caller has already registered via `POST /push/config`; today only `telegram` is supported.").action(async (opts) => {
3503
3531
  const client2 = await loadSdkClient();
3504
3532
  const result = await pushTest({
3505
3533
  client: client2._rawClient,
@@ -3523,7 +3551,7 @@ var pushTestCommand = new Command51("test").description("Send a test push notifi
3523
3551
 
3524
3552
  // src/generated/cli/todo/comment/add.ts
3525
3553
  import { Command as Command52 } from "commander";
3526
- var todoCommentCreateCommand = new Command52("add").description("Add a comment to a todo").argument("<id>", "id").argument("<content>", "content").action(async (id, content, opts) => {
3554
+ var todoCommentCreateCommand = new Command52("add").description("Add a comment to a todo").addHelpText("after", '\n### \u{1F3AF} Overview & Purpose\nAttach a free-text comment to a todo. Use this to record progress updates, notes, or remarks as a task moves along.\n\n### \u{1F4A1} Key Features & Constraints\n* **Free text**: Comments are plain text up to 10000 characters; there is no separate "progress" vs "remark" type.\n* **Authorship**: The author is recorded as the calling user (`user_id`).\n\n### \u26A0\uFE0F Common Errors & Troubleshooting\n* **`NOT_FOUND` (HTTP 404)**: Thrown if the target todo does not exist or is soft-deleted.\n* **`VALIDATION_ERROR` (HTTP 400)**: Thrown if content is empty or exceeds 10000 characters.\n\nExamples:\n $ wspc todo comment add tod_01HW3K "Verified on staging"\n').argument("<id>", "id").argument("<content>", "content").action(async (id, content, opts) => {
3527
3555
  const client2 = await loadSdkClient();
3528
3556
  const result = await todoCommentCreate({
3529
3557
  client: client2._rawClient,
@@ -3547,7 +3575,7 @@ var todoCommentCreateCommand = new Command52("add").description("Add a comment t
3547
3575
 
3548
3576
  // src/generated/cli/todo/comment/ls.ts
3549
3577
  import { Command as Command53 } from "commander";
3550
- var todoCommentListCommand = new Command53("ls").description("List comments on a todo").argument("<id>", "id").option("--order <value>", "order").option("--include-deleted <value>", "include_deleted").option("--limit <value>", "Max comments 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 (id, opts) => {
3578
+ var todoCommentListCommand = new Command53("ls").description("List comments on a todo").addHelpText("after", "\n### \u{1F3AF} Overview & Purpose\nList the comments attached to a todo, oldest-first by default.\n\n### \u{1F4A1} Key Features & Constraints\n* **Ordering**: Defaults to chronological (`asc`). Pass `order=desc` for newest-first.\n* **Soft-deleted**: Hidden by default; pass `include_deleted=true` to include them.\n* **Pagination**: Use `limit` (max 200, default 50) and `cursor` (the `next_cursor` from a previous response) to page through results. When `next_cursor` is absent in the response, you are on the last page. Returns `{ comments, next_cursor? }`. Changing `order` invalidates a cursor.\n\n### \u26A0\uFE0F Common Errors & Troubleshooting\n* **`NOT_FOUND` (HTTP 404)**: Thrown if the target todo does not exist or is soft-deleted.\n* **`VALIDATION_ERROR`**: Thrown if a cursor was produced with a different `order` than the current request.\n\nExamples:\n $ wspc todo comment ls tod_01HW3K\n").argument("<id>", "id").option("--order <value>", "order").option("--include-deleted <value>", "include_deleted").option("--limit <value>", "Max comments 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 (id, opts) => {
3551
3579
  const client2 = await loadSdkClient();
3552
3580
  const result = await todoCommentList({
3553
3581
  client: client2._rawClient,
@@ -3574,7 +3602,7 @@ var todoCommentListCommand = new Command53("ls").description("List comments on a
3574
3602
 
3575
3603
  // src/generated/cli/todo/project/add.ts
3576
3604
  import { Command as Command54 } from "commander";
3577
- var projectCreateCommand = new Command54("add").description("Create a project").argument("<name>", "name").option("--default-todo-type-id <value>", "default_todo_type_id").action(async (name, opts) => {
3605
+ var projectCreateCommand = new Command54("add").description("Create a project").addHelpText("after", '\n### \u{1F3AF} Overview & Purpose\nEstablish a new isolated project workspace.\n\n### \u{1F50D} When to Use\n* Use this to set up a new domain, team project, or separate workspace area to isolate tasks, custom types, and recurrence rules.\n\n### \u{1F4A1} Key Features & Constraints\n* **Project Partitioning**: Projects act as strict boundaries. Custom todo types and recurrence rules created under this project are strictly confined to it.\n* **Name Uniqueness**: Project names are free-form and do not have to be unique.\n* **Default Type Inheritance**: Omit `default_todo_type_id` to automatically inherit the Default Project\'s default task type.\n\n### \u26A0\uFE0F Common Errors & Troubleshooting\n* **`VALIDATION_ERROR` (HTTP 400)**: Thrown if required fields are missing, if name is empty, or if name length constraints are violated.\n\nExamples:\n $ wspc todo project add "Personal"\n').argument("<name>", "name").option("--default-todo-type-id <value>", "default_todo_type_id").action(async (name, opts) => {
3578
3606
  const client2 = await loadSdkClient();
3579
3607
  const result = await projectCreate({
3580
3608
  client: client2._rawClient,
@@ -3596,7 +3624,7 @@ var projectCreateCommand = new Command54("add").description("Create a project").
3596
3624
 
3597
3625
  // src/generated/cli/todo/project/ls.ts
3598
3626
  import { Command as Command55 } from "commander";
3599
- var projectListCommand = new Command55("ls").description("List projects").option("--include-deleted <value>", "Set to `true` to include soft-deleted projects in the response.").action(async (opts) => {
3627
+ var projectListCommand = new Command55("ls").description("List projects").addHelpText("after", "\n### \u{1F3AF} Overview & Purpose\nList all project workspaces available to the authenticated organization or user.\n\n### \u{1F50D} When to Use\n* Use this to populate project switcher dropdown menus, load side navigation views, or find valid project IDs before listing other scoped resources.\n\n### \u{1F4A1} Key Features & Constraints\n* **Archived Visibility**: Soft-deleted projects are omitted from default listings. Pass `include_deleted=true` to include them for auditing or recovery dashboards.\n\n### \u26A0\uFE0F Common Errors & Troubleshooting\n* **`AUTH_REQUIRED` (HTTP 401)**: Thrown if the caller is not authenticated.\n\nExamples:\n $ wspc todo project ls\n").option("--include-deleted <value>", "Set to `true` to include soft-deleted projects in the response.").action(async (opts) => {
3600
3628
  const client2 = await loadSdkClient();
3601
3629
  const result = await projectList({
3602
3630
  client: client2._rawClient,
@@ -3617,7 +3645,7 @@ var projectListCommand = new Command55("ls").description("List projects").option
3617
3645
 
3618
3646
  // src/generated/cli/todo/rule/add.ts
3619
3647
  import { Command as Command56 } from "commander";
3620
- var recurrenceRuleCreateCommand = new Command56("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) => {
3648
+ var recurrenceRuleCreateCommand = new Command56("add").description("Create a recurring todo rule").addHelpText("after", '\n### \u{1F3AF} Overview & Purpose\nCreate a recurrence rule that materializes upcoming todo instances on a repeating schedule.\n\n### \u{1F50D} When to Use\n* Use this to set up recurring work like a weekly Standup, monthly reporting, or cyclical maintenance. The server automatically materializes upcoming todo instances on a 14-day rolling horizon.\n\n### \u{1F4A1} Key Features & Constraints\n* **RFC-5545 Conformity**: The `rrule` parameter must be a valid RFC-5545 schedule string (e.g., `FREQ=WEEKLY;BYDAY=MO`) and must **not** include the `DTSTART` or `TZID` directive.\n* **Anchor Date**: `dtstart` specifies the local calendar starting date (`YYYY-MM-DD`) where the schedule rule is anchored.\n* **Nesting Constraints**: Recurrence rules can only be bound to root-level tasks. Child tasks (subtasks) cannot have recurrence rules. Setting a child task as a parent will trigger `PARENT_IS_CHILD`.\n* **Instance Independence**: Once materialized, each todo instance is fully independent with its own `status` and `due_at`.\n\n### \u26A0\uFE0F Common Errors & Troubleshooting\n* **`RRULE_INVALID` (HTTP 400)**: Thrown if the `rrule` schedule string is broken or contains illegal `DTSTART` directives.\n* **`PARENT_IS_CHILD` (HTTP 400)**: Thrown if the specified `parent_id` points to a child task.\n* **`VALIDATION_ERROR` (HTTP 400)**: Thrown if date format is invalid or required fields are missing.\n\nExamples:\n $ wspc todo rule add "Weekly review" --rrule "FREQ=WEEKLY;BYDAY=MO" --dtstart 2026-05-18 --project prj_xxx\n $ wspc todo rule add "Weekly review" --rrule "FREQ=WEEKLY;BYDAY=MO" --dtstart 2026-05-18 --project prj_xxx --type typ_xxx\n').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) => {
3621
3649
  const client2 = await loadSdkClient();
3622
3650
  const result = await recurrenceRuleCreate({
3623
3651
  client: client2._rawClient,
@@ -3644,7 +3672,7 @@ var recurrenceRuleCreateCommand = new Command56("add").description("Create a rec
3644
3672
 
3645
3673
  // src/generated/cli/todo/rule/ls.ts
3646
3674
  import { Command as Command57 } from "commander";
3647
- var recurrenceRuleListCommand = new Command57("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) => {
3675
+ var recurrenceRuleListCommand = new Command57("ls").description("List recurring todo rules").addHelpText("after", "\n### \u{1F3AF} Overview & Purpose\nReturn all active recurrence rules within a specific project owned by the caller.\n\n### \u{1F50D} When to Use\n* Use this to render rule management panels, list scheduled automation templates, or inspect active rules.\n\n### \u{1F4A1} Key Features & Constraints\n* **Project Scope**: The `project_id` query parameter is strictly required.\n* **Exclusion**: Soft-deleted/archived rules are excluded from the response by default.\n\n### \u26A0\uFE0F Common Errors & Troubleshooting\n* **`VALIDATION_ERROR` (HTTP 400)**: Thrown if `project_id` query filter is omitted.\n\nExamples:\n $ wspc todo rule ls\n").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) => {
3648
3676
  const client2 = await loadSdkClient();
3649
3677
  const result = await recurrenceRuleList({
3650
3678
  client: client2._rawClient,
@@ -3680,7 +3708,7 @@ function parseJsonField(raw, flag) {
3680
3708
  }
3681
3709
 
3682
3710
  // src/generated/cli/todo/add.ts
3683
- var todoCreateCommand = new Command58("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("--idempotency-key <value>", "idempotency_key").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) => {
3711
+ var todoCreateCommand = new Command58("add").description("Create a todo").addHelpText("after", '\n### \u{1F3AF} Overview & Purpose\nCreate a new todo item under a specified project. This can either be a standalone root-level todo or a nested subtask attached to an existing root todo.\n\n### \u{1F50D} When to Use\n* Use this to capture a fresh work item, document an ongoing task, or break a larger root todo into subtasks by creating child todos under it.\n\n### \u{1F4A1} Key Features & Constraints\n* **One-Level Nesting Limit**: WSPC supports a maximum of one level of task nesting (Root \u2794 Child). A root-level todo can have children, but a child todo cannot have further subtasks. Setting a child todo as a parent will fail and trigger a `PARENT_IS_CHILD` error.\n* **Description Handling**: Passing a non-empty string stores the description; passing `""` explicitly stores an empty string. Passing `null` is strictly rejected.\n* **Due Date Format**: Accepts an ISO-8601 date-only format (`YYYY-MM-DD`). Pass `""` or omit the field to skip setting a due date.\n* **Project Binding**: Every todo must belong to a valid active project (`project_id`).\n\n### \u{1F4A1} Best Practices & Guidelines\n* **Descriptive Descriptions**: Always provide a detailed description explaining the task\'s context, goal, or definition of done. Avoid leaving it empty.\n* **Task Breakdown**: For complex items, split the task into 2-3 logical subtasks (by passing a parent todo ID in `parent_id`). Remember WSPC supports only one level of nesting (Root -> Child).\n* **Integration with Drive**: If a task requires storing large text blocks, research logs, checklists, or files, do not overload the description. Use the Drive feature to store a markdown file (e.g., `research/notes.md`) and place a clean reference link in the description, such as `[Notes](drive://<library>/<path>)`.\n* **Structured Properties (Custom Fields)**: Avoid putting structured meta-attributes (like priority, severity, story points, tags) directly in the description. Instead, use custom todo types (`todo_type`). Create a custom type schema first, then assign it with `type_id` and pass values via `custom_fields`.\n\n### \u26A0\uFE0F Common Errors & Troubleshooting\n* **`VALIDATION_ERROR` (HTTP 400)**: Thrown if required fields are missing, if `due_at` violates the `YYYY-MM-DD` format, or if `title` exceeds 500 characters.\n* **`PARENT_IS_CHILD` (HTTP 400)**: Thrown if the target `parent_id` refers to a todo that is itself already a child todo.\n* **`WOULD_CREATE_CYCLE` (HTTP 400)**: Thrown if `parent_id` points to the todo\'s own ID.\n* **`NOT_FOUND` (HTTP 404)**: Thrown if the specified `project_id` or `parent_id` does not exist or has been soft-deleted.\n\nExamples:\n $ wspc todo add "Buy milk"\n $ wspc todo add "Buy milk" --project prj_xxx\n $ wspc todo add "Fix styling issue" --type-id typ_xxx --custom-fields \'{"severity":"high","tags":["ui"]}\'\n').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("--idempotency-key <value>", "idempotency_key").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) => {
3684
3712
  const client2 = await loadSdkClient();
3685
3713
  const result = await todoCreate({
3686
3714
  client: client2._rawClient,
@@ -3709,7 +3737,7 @@ var todoCreateCommand = new Command58("add").description("Create a todo").argume
3709
3737
 
3710
3738
  // src/generated/cli/todo/ls.ts
3711
3739
  import { Command as Command59 } from "commander";
3712
- var todoListCommand = new Command59("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", "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) => {
3740
+ var todoListCommand = new Command59("ls").description("List todos with filters").addHelpText("after", "\n### \u{1F3AF} Overview & Purpose\nReturn the caller's active or archived todos, with comprehensive options to filter by project, parent task, status, due-date window, and template visibility.\n\n### \u{1F50D} When to Use\n* Use this to render the main todo board dashboard, query items due in a specific timeframe (using `due_after` and `due_before`), or lazy-load subtasks for an expanded parent todo by passing its ID.\n\n### \u{1F4A1} Key Features & Constraints\n* **Required Parameter**: The `project_id` query parameter is strictly required and must match an active project.\n* **Parent Tasks**: Omitting `parent_id` lists root-level todos by default. Pass a todo id to list direct children of that specific task.\n* **Multi-Status Filters**: Multi-value `status` query is supported by repeating the parameter, e.g., `?status=open&status=in_progress`.\n* **Due-Date Windowing**: The `due_after` filter is inclusive, while `due_before` is exclusive, forming a half-open window `[due_after, due_before)`. Both parameters exclude todos with no due date.\n* **Template & Soft-Delete Visibility**: Soft-deleted todos are hidden unless `include_deleted=true`. Template todos backing recurrence rules are hidden unless `include_templates=true`.\n* **Custom-Field Filters (`cf.<key>=<value>`)**: Repeatable dynamic-prefix query parameters whose name follows the `cf.<key>` pattern (e.g. `?cf.priority=high&cf.team=eng`). Each pair is ANDed; for `string_array` custom fields the match is positive when the array contains the value. Keys must be declared on the project's todo type schema. Because the prefix is dynamic, these parameters cannot be expressed in the JSON Schema below \u2014 clients must construct them from the URL query string directly.\n\n### \u26A0\uFE0F Common Errors & Troubleshooting\n* **`VALIDATION_ERROR` (HTTP 400)**: Thrown if `project_id` is missing, or if query parameters fail schema validation.\n\nExamples:\n $ wspc todo ls\n $ wspc todo ls --status open\n $ wspc todo ls --project prj_xxx\n").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", "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) => {
3713
3741
  const client2 = await loadSdkClient();
3714
3742
  const result = await todoList({
3715
3743
  client: client2._rawClient,
@@ -3741,9 +3769,33 @@ var todoListCommand = new Command59("ls").description("List todos with filters")
3741
3769
  render({ kind: "todo_list", display: { "shape": "list", "columns": ["id", "status", "title", "due_at"], "format": { "id": "id-short", "status": "status-badge", "title": "truncate", "due_at": "relative-time" }, "emptyMessage": "no todos" } }, result.data);
3742
3770
  });
3743
3771
 
3744
- // src/generated/cli/todo/type/ls.ts
3772
+ // src/generated/cli/todo/type/add.ts
3745
3773
  import { Command as Command60 } from "commander";
3746
- var todoTypeListCommand = new Command60("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) => {
3774
+ var todoTypeCreateCommand = new Command60("add").description("Create a todo type").addHelpText("after", '\n### \u{1F3AF} Overview & Purpose\nCreate a new custom todo type. This allows you to define specialized category schemas (e.g. "Bug Report") and configure custom field constraints.\n\n### \u{1F50D} When to Use\n* Use this to set up customized task behaviors (e.g. tracking choices, additional metadata, or enforcing hidden fields) tailored to a project.\n\n### \u{1F4A1} Key Features & Constraints\n* **Automatic Seeding**: The first project initialization will lazily seed a `Default Project` and a `Default` todo type if they do not already exist.\n* **Metadata Schema**: Custom field keys mapped here are evaluated during task creation/update.\n\n### \u26A0\uFE0F Common Errors & Troubleshooting\n* **`VALIDATION_ERROR` (HTTP 400)**: Thrown if required fields are missing or schema constraints are violated.\n\nExamples:\n $ wspc todo type add "Bug Report"\n $ wspc todo type add "Bug Report" --project prj_xxx\n $ wspc todo type add "Sprint Task" --custom-fields \'[{"key":"story_points","label":"Story Points","type":"string"}]\'\n').argument("<label>", "label").option("-p, --project <value>", "Project this type belongs to. Required. It must be an active project in the caller's organization.").option("--hide-core-fields <value>", "hide_core_fields").option("--custom-fields <value>", "custom_fields").action(async (label, opts) => {
3775
+ const client2 = await loadSdkClient();
3776
+ const result = await todoTypeCreate({
3777
+ client: client2._rawClient,
3778
+ body: {
3779
+ label,
3780
+ project_id: opts.project,
3781
+ hide_core_fields: parseJsonField(opts.hideCoreFields, "hide-core-fields"),
3782
+ custom_fields: parseJsonField(opts.customFields, "custom-fields")
3783
+ }
3784
+ });
3785
+ if (result.error || !result.response?.ok) {
3786
+ process.stderr.write(
3787
+ `HTTP ${result.response?.status ?? "?"}: ${JSON.stringify(result.error ?? "unknown error", null, 2)}
3788
+ `
3789
+ );
3790
+ process.exitCode = 1;
3791
+ return;
3792
+ }
3793
+ render({ kind: "todo_type_create", display: void 0 }, result.data);
3794
+ });
3795
+
3796
+ // src/generated/cli/todo/type/ls.ts
3797
+ import { Command as Command61 } from "commander";
3798
+ var todoTypeListCommand = new Command61("ls").description("List todo types").addHelpText("after", "\n### \u{1F3AF} Overview & Purpose\nList custom todo types defined within a project.\n\n### \u{1F50D} When to Use\n* Use this to populate task type selection dropdown elements or load category metadata for dynamic custom forms.\n\n### \u{1F4A1} Key Features & Constraints\n* **Required Parameter**: The `project_id` filter is strictly required and must match an active project.\n* **Exclusion**: Soft-deleted types are excluded by default. Pass `include_deleted=true` to surface archived rows for a recovery UI.\n\n### \u26A0\uFE0F Common Errors & Troubleshooting\n* **`VALIDATION_ERROR` (HTTP 400)**: Thrown if `project_id` query parameter is omitted.\n\nExamples:\n $ wspc todo type ls\n").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) => {
3747
3799
  const client2 = await loadSdkClient();
3748
3800
  const result = await todoTypeList({
3749
3801
  client: client2._rawClient,
@@ -3765,8 +3817,8 @@ var todoTypeListCommand = new Command60("ls").description("List todo types").opt
3765
3817
  });
3766
3818
 
3767
3819
  // src/generated/cli/todo/comment/rm.ts
3768
- import { Command as Command61 } from "commander";
3769
- var todoCommentDeleteCommand = new Command61("rm").description("Soft-delete a comment").argument("<id>", "id").action(async (id, opts) => {
3820
+ import { Command as Command62 } from "commander";
3821
+ var todoCommentDeleteCommand = new Command62("rm").description("Soft-delete a comment").addHelpText("after", "\n### \u{1F3AF} Overview & Purpose\nSoft-delete a comment.\n\n### \u{1F4A1} Key Features & Constraints\n* **Soft delete**: The comment is hidden from default listings but retained; there is no restore endpoint.\n\n### \u26A0\uFE0F Common Errors & Troubleshooting\n* **`COMMENT_NOT_FOUND` (HTTP 404)**: Thrown if the comment id is unknown, already deleted, or not in the caller's organization.\n\nExamples:\n $ wspc todo comment rm tdc_01HW3K\n").argument("<id>", "id").action(async (id, opts) => {
3770
3822
  const client2 = await loadSdkClient();
3771
3823
  const result = await todoCommentDelete({
3772
3824
  client: client2._rawClient,
@@ -3786,8 +3838,8 @@ var todoCommentDeleteCommand = new Command61("rm").description("Soft-delete a co
3786
3838
  });
3787
3839
 
3788
3840
  // src/generated/cli/todo/comment/edit.ts
3789
- import { Command as Command62 } from "commander";
3790
- var todoCommentUpdateCommand = new Command62("edit").description("Edit a comment").argument("<id>", "id").argument("<content>", "content").action(async (id, content, opts) => {
3841
+ import { Command as Command63 } from "commander";
3842
+ var todoCommentUpdateCommand = new Command63("edit").description("Edit a comment").addHelpText("after", '\n### \u{1F3AF} Overview & Purpose\nEdit the body of an existing comment.\n\n### \u{1F4A1} Key Features & Constraints\n* **Last write wins**: There is no optimistic-lock version on comments; the latest edit replaces the content.\n\n### \u26A0\uFE0F Common Errors & Troubleshooting\n* **`COMMENT_NOT_FOUND` (HTTP 404)**: Thrown if the comment id is unknown, soft-deleted, or not in the caller\'s organization.\n* **`VALIDATION_ERROR` (HTTP 400)**: Thrown if content is empty or exceeds 10000 characters.\n\nExamples:\n $ wspc todo comment edit tdc_01HW3K "Edited note"\n').argument("<id>", "id").argument("<content>", "content").action(async (id, content, opts) => {
3791
3843
  const client2 = await loadSdkClient();
3792
3844
  const result = await todoCommentUpdate({
3793
3845
  client: client2._rawClient,
@@ -3810,8 +3862,8 @@ var todoCommentUpdateCommand = new Command62("edit").description("Edit a comment
3810
3862
  });
3811
3863
 
3812
3864
  // src/generated/cli/todo/project/rm.ts
3813
- import { Command as Command63 } from "commander";
3814
- var projectDeleteCommand = new Command63("rm").description("Soft-delete a project").argument("<id>", "id").action(async (id, opts) => {
3865
+ import { Command as Command64 } from "commander";
3866
+ var projectDeleteCommand = new Command64("rm").description("Soft-delete a project").addHelpText("after", "\n### \u{1F3AF} Overview & Purpose\nSoft-delete/archive a project workspace.\n\n### \u{1F50D} When to Use\n* Use this to archive a completed project and hide it from default listings without losing historical metrics.\n\n### \u{1F4A1} Key Features & Constraints\n* **Cascading Effects**: Deleting a project automatically soft-deletes the project record and cascades to soft-delete all todos created under it.\n\n### \u26A0\uFE0F Common Errors & Troubleshooting\n* **`NOT_FOUND` (HTTP 404)**: Thrown if the project ID does not exist or has already been archived.\n\nExamples:\n $ wspc todo project rm prj_01HW3K\n").argument("<id>", "id").action(async (id, opts) => {
3815
3867
  const client2 = await loadSdkClient();
3816
3868
  const result = await projectDelete({
3817
3869
  client: client2._rawClient,
@@ -3831,8 +3883,8 @@ var projectDeleteCommand = new Command63("rm").description("Soft-delete a projec
3831
3883
  });
3832
3884
 
3833
3885
  // src/generated/cli/todo/rule/rm.ts
3834
- import { Command as Command64 } from "commander";
3835
- var recurrenceRuleDeleteCommand = new Command64("rm").description("Delete a recurring todo rule").argument("<id>", "id").option("--expected-version <value>", "expected_version").action(async (id, opts) => {
3886
+ import { Command as Command65 } from "commander";
3887
+ var recurrenceRuleDeleteCommand = new Command65("rm").description("Delete a recurring todo rule").addHelpText("after", "\n### \u{1F3AF} Overview & Purpose\nSoft-delete/delete a recurrence rule to immediately halt future task materialization.\n\n### \u{1F50D} When to Use\n* Use this to permanently end an ongoing cyclical schedule automation (e.g., when a weekly standby rotation is retired).\n\n### \u{1F4A1} Key Features & Constraints\n* **Historic Preservation**: Deleting a rule stops the rolling schedule generations, but **does not** delete or alter todo tasks that have already been materialized. They remain on the user's list.\n* **Optimistic Locking**: Supports optional `expected_version` checks.\n\n### \u26A0\uFE0F Common Errors & Troubleshooting\n* **`VERSION_CONFLICT` (HTTP 409)**: Thrown if `expected_version` mismatches the database.\n* **`NOT_FOUND` (HTTP 404)**: Thrown if the target rule ID does not exist.\n\nExamples:\n $ wspc todo rule rm tdr_xxx\n $ wspc todo rule rm tdr_xxx --expected-version 3\n").argument("<id>", "id").option("--expected-version <value>", "expected_version").action(async (id, opts) => {
3836
3888
  const client2 = await loadSdkClient();
3837
3889
  const result = await recurrenceRuleDelete({
3838
3890
  client: client2._rawClient,
@@ -3855,8 +3907,8 @@ var recurrenceRuleDeleteCommand = new Command64("rm").description("Delete a recu
3855
3907
  });
3856
3908
 
3857
3909
  // src/generated/cli/todo/rule/show.ts
3858
- import { Command as Command65 } from "commander";
3859
- var recurrenceRuleGetCommand = new Command65("show").description("Get a recurring todo rule").argument("<id>", "id").action(async (id, opts) => {
3910
+ import { Command as Command66 } from "commander";
3911
+ var recurrenceRuleGetCommand = new Command66("show").description("Get a recurring todo rule").addHelpText("after", "\n### \u{1F3AF} Overview & Purpose\nFetch a single recurrence rule along with its template todo snapshot and the count of materialized instances.\n\n### \u{1F50D} When to Use\n* Use this to inspect rule details before editing, preview the task template that future occurrences will copy, or check the current materialization metrics.\n\n### \u{1F4A1} Key Features & Constraints\n* **Snapshot Integrity**: The returned template represents a schema template snapshot \u2014 modifying the rule (PATCH) only alters future occurrences; already-materialized tasks are never mutated retroactively.\n\n### \u26A0\uFE0F Common Errors & Troubleshooting\n* **`NOT_FOUND` (HTTP 404)**: Thrown if the specified rule ID does not exist.\n\nExamples:\n $ wspc todo rule show tdr_xxx\n").argument("<id>", "id").action(async (id, opts) => {
3860
3912
  const client2 = await loadSdkClient();
3861
3913
  const result = await recurrenceRuleGet({
3862
3914
  client: client2._rawClient,
@@ -3876,8 +3928,8 @@ var recurrenceRuleGetCommand = new Command65("show").description("Get a recurrin
3876
3928
  });
3877
3929
 
3878
3930
  // src/generated/cli/todo/rm.ts
3879
- import { Command as Command66 } from "commander";
3880
- var todoDeleteCommand = new Command66("rm").description("Soft-delete a todo").argument("<id>", "id").option("--expected-version <value>", "expected_version").option("--cascade <value>", "cascade").action(async (id, opts) => {
3931
+ import { Command as Command67 } from "commander";
3932
+ var todoDeleteCommand = new Command67("rm").description("Soft-delete a todo").addHelpText("after", "\n### \u{1F3AF} Overview & Purpose\nSoft-delete a todo item so that it no longer appears in active list queries. The record remains in the database and can be recovered later.\n\n### \u{1F50D} When to Use\n* Use this to hide an item from your active listings without permanently losing the history or metrics.\n\n### \u{1F4A1} Key Features & Constraints\n* **Cascading Delete (`cascade`)**: If the target todo has active child subtasks:\n - If `cascade: false` (default), the deletion will fail and throw a `HAS_CHILDREN` error to prevent accidental orphaned tasks.\n - If `cascade: true`, the target todo and all its nested child subtasks will be soft-deleted together.\n* **Optimistic Locking**: You may optionally pass `expected_version` to ensure the todo has not been modified since you last read it.\n\n### \u26A0\uFE0F Common Errors & Troubleshooting\n* **`HAS_CHILDREN` (HTTP 400)**: Thrown if you attempt to delete a parent todo that has active subtasks without explicitly setting `cascade: true`.\n* **`VERSION_CONFLICT` (HTTP 409)**: Thrown if `expected_version` is provided and mismatches the database.\n* **`NOT_FOUND` (HTTP 404)**: Thrown if the target todo `id` does not exist or has already been soft-deleted.\n\nExamples:\n $ wspc todo rm tod_xxx\n").argument("<id>", "id").option("--expected-version <value>", "expected_version").option("--cascade <value>", "cascade").action(async (id, opts) => {
3881
3933
  const client2 = await loadSdkClient();
3882
3934
  const result = await todoDelete({
3883
3935
  client: client2._rawClient,
@@ -3901,8 +3953,8 @@ var todoDeleteCommand = new Command66("rm").description("Soft-delete a todo").ar
3901
3953
  });
3902
3954
 
3903
3955
  // src/generated/cli/todo/show.ts
3904
- import { Command as Command67 } from "commander";
3905
- var todoGetCommand = new Command67("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) => {
3956
+ import { Command as Command68 } from "commander";
3957
+ var todoGetCommand = new Command68("show").description("Get a todo by id").addHelpText("after", "\n### \u{1F3AF} Overview & Purpose\nFetch the full details of a single todo item by its unique identifier.\n\n### \u{1F50D} When to Use\n* Use this to confirm the current state of a task, inspect nested field values, or retrieve its current `version` before issuing an optimistic update (PATCH).\n\n### \u{1F4A1} Key Features & Constraints\n* **Soft-Deleted Recovery**: A soft-deleted todo will return an HTTP 404 unless the query parameter `?include_deleted=true` is explicitly supplied.\n\n### \u26A0\uFE0F Common Errors & Troubleshooting\n* **`NOT_FOUND` (HTTP 404)**: Thrown if the specified todo `id` does not exist, or has been soft-deleted and the request did not supply `include_deleted=true`.\n\nExamples:\n $ wspc todo show tod_xxx\n").argument("<id>", "id").option("--include-deleted <value>", "include_deleted").option("--include-orphan-fields <value>", "include_orphan_fields").action(async (id, opts) => {
3906
3958
  const client2 = await loadSdkClient();
3907
3959
  const result = await todoGet({
3908
3960
  client: client2._rawClient,
@@ -3927,8 +3979,8 @@ var todoGetCommand = new Command67("show").description("Get a todo by id").argum
3927
3979
  });
3928
3980
 
3929
3981
  // src/generated/cli/todo/update.ts
3930
- import { Command as Command68 } from "commander";
3931
- var todoUpdateCommand = new Command68("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) => {
3982
+ import { Command as Command69 } from "commander";
3983
+ var todoUpdateCommand = new Command69("update").description("Update a todo").addHelpText("after", '\n### \u{1F3AF} Overview & Purpose\nUpdate one or more fields of an existing todo item, such as its title, status, parent todo, due date, or description.\n\n### \u{1F50D} When to Use\n* Use this to log progress by changing the status (e.g., to `in_progress` or `done`), reschedule due dates, edit title/description, or reassign/move a task by changing its `parent_id`.\n\n### \u{1F4A1} Key Features & Constraints\n* **Optimistic Locking (`expected_version`)**: An optional integer representing the version you expect to update. If provided, the server matches it with the current database version. If they match, the update succeeds and increments the version; if they mismatch, a `VERSION_CONFLICT` error is thrown. Omit this field to skip version checking (Last-Write-Wins behavior).\n* **Parent Re-assignment**: Set `parent_id: null` to move a child todo back to the root level.\n* **Status Transitions**: Transitioning the `status` to `done` automatically emits a `captureTodoCompleted` analytics event.\n* **Clearing Fields**: To clear an existing description or due date, explicitly pass `""`. Passing `null` is rejected.\n\n### \u{1F4A1} Best Practices & Guidelines\n* **Enriched Context**: Keep the description updated with definition of done, relevant progress notes, or resolution summaries.\n* **Keep Descriptions Clean**: Delegate heavy logs/documents to a Markdown file on Drive, and put a markdown link inside the description.\n* **Structured Properties**: Use custom fields (`custom_fields`) matching the todo\'s type to record properties like tags, priority, or severity.\n\n### \u26A0\uFE0F Common Errors & Troubleshooting\n* **`VERSION_CONFLICT` (HTTP 409)**: Thrown if `expected_version` does not match the current database row version.\n* **`PARENT_IS_CHILD` (HTTP 400)**: Thrown if the new `parent_id` refers to a todo that is itself already a child todo.\n* **`WOULD_CREATE_CYCLE` (HTTP 400)**: Thrown if the update attempts to make a parent todo a child of its own descendant.\n* **`NOT_FOUND` (HTTP 404)**: Thrown if the todo `id` or the new `parent_id` does not exist or has been soft-deleted.\n\nExamples:\n $ wspc todo update tod_xxx --status done\n $ wspc todo update tod_xxx --title "New title"\n $ wspc todo update tod_xxx --custom-fields \'{"severity":"critical"}\'\n').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) => {
3932
3984
  const client2 = await loadSdkClient();
3933
3985
  const result = await todoUpdate({
3934
3986
  client: client2._rawClient,
@@ -3958,9 +4010,57 @@ var todoUpdateCommand = new Command68("update").description("Update a todo").arg
3958
4010
  render({ kind: "todo_update", display: { "shape": "object", "format": { "id": "id-short", "user_id": "id-short", "project_id": "id-short", "parent_id": "id-short", "type_id": "id-short", "status": "status-badge", "due_at": "relative-time", "created_at": "relative-time", "updated_at": "relative-time", "deleted_at": "relative-time" } } }, result.data);
3959
4011
  });
3960
4012
 
4013
+ // src/generated/cli/todo/type/rm.ts
4014
+ import { Command as Command70 } from "commander";
4015
+ var todoTypeDeleteCommand = new Command70("rm").description("Soft-delete a todo type").addHelpText("after", "\n### \u{1F3AF} Overview & Purpose\nSoft-delete/archive a custom todo type.\n\n### \u{1F50D} When to Use\n* Use this to retire a custom task category workspace that is no longer needed.\n\n### \u{1F4A1} Key Features & Constraints\n* **Default Type Protection**: The current active default type of a project cannot be deleted. You must assign another type as default first; otherwise the call fails with `CANNOT_DELETE_DEFAULT_TYPE`.\n\n### \u26A0\uFE0F Common Errors & Troubleshooting\n* **`CANNOT_DELETE_DEFAULT_TYPE` (HTTP 409)**: Thrown if the target todo type is currently the project's default type.\n* **`NOT_FOUND` (HTTP 404)**: Thrown if the target ID does not exist.\n\nExamples:\n $ wspc todo type rm typ_xxx\n").argument("<id>", "id").action(async (id, opts) => {
4016
+ const client2 = await loadSdkClient();
4017
+ const result = await todoTypeDelete({
4018
+ client: client2._rawClient,
4019
+ path: {
4020
+ id
4021
+ }
4022
+ });
4023
+ if (result.error || !result.response?.ok) {
4024
+ process.stderr.write(
4025
+ `HTTP ${result.response?.status ?? "?"}: ${JSON.stringify(result.error ?? "unknown error", null, 2)}
4026
+ `
4027
+ );
4028
+ process.exitCode = 1;
4029
+ return;
4030
+ }
4031
+ render({ kind: "todo_type_delete", display: void 0 }, result.data);
4032
+ });
4033
+
4034
+ // src/generated/cli/todo/type/set.ts
4035
+ import { Command as Command71 } from "commander";
4036
+ var todoTypeUpdateCommand = new Command71("set").description("Update a todo type").addHelpText("after", '\n### \u{1F3AF} Overview & Purpose\nUpdate a custom todo type\'s label, core field overrides, or custom field schema definitions.\n\n### \u{1F50D} When to Use\n* Use this to rename a task category category, hide native todo attributes, or adjust custom data schemas.\n\n### \u{1F4A1} Key Features & Constraints\n* **Type Modification Constraints**: Changing the data `type` of an existing custom field key (e.g. converting a string field to a boolean field) is strictly rejected with `CANNOT_CHANGE_FIELD_TYPE`. To migrate, remove the key and re-add it under a brand new name.\n\n### \u26A0\uFE0F Common Errors & Troubleshooting\n* **`CANNOT_CHANGE_FIELD_TYPE` (HTTP 422)**: Thrown if you attempt to modify the declared data type of an existing custom field key.\n* **`NOT_FOUND` (HTTP 404)**: Thrown if the target ID does not exist.\n\nExamples:\n $ wspc todo type set typ_xxx --label "Feature Request"\n $ wspc todo type set typ_xxx --custom-fields \'[{"key":"severity","label":"Severity","type":"string"}]\'\n').argument("<id>", "id").option("--expected-version <value>", "expected_version").option("--label <value>", "label").option("--hide-core-fields <value>", "hide_core_fields").option("--custom-fields <value>", "custom_fields").action(async (id, opts) => {
4037
+ const client2 = await loadSdkClient();
4038
+ const result = await todoTypeUpdate({
4039
+ client: client2._rawClient,
4040
+ path: {
4041
+ id
4042
+ },
4043
+ body: {
4044
+ expected_version: opts.expectedVersion,
4045
+ label: opts.label,
4046
+ hide_core_fields: parseJsonField(opts.hideCoreFields, "hide-core-fields"),
4047
+ custom_fields: parseJsonField(opts.customFields, "custom-fields")
4048
+ }
4049
+ });
4050
+ if (result.error || !result.response?.ok) {
4051
+ process.stderr.write(
4052
+ `HTTP ${result.response?.status ?? "?"}: ${JSON.stringify(result.error ?? "unknown error", null, 2)}
4053
+ `
4054
+ );
4055
+ process.exitCode = 1;
4056
+ return;
4057
+ }
4058
+ render({ kind: "todo_type_update", display: void 0 }, result.data);
4059
+ });
4060
+
3961
4061
  // src/generated/cli/todo/restore.ts
3962
- import { Command as Command69 } from "commander";
3963
- var todoRestoreCommand = new Command69("restore").description("Restore a soft-deleted todo").argument("<id>", "id").option("--expected-version <value>", "expected_version").option("--cascade <value>", "cascade").action(async (id, opts) => {
4062
+ import { Command as Command72 } from "commander";
4063
+ var todoRestoreCommand = new Command72("restore").description("Restore a soft-deleted todo").addHelpText("after", "\n### \u{1F3AF} Overview & Purpose\nReverse a previous soft-delete. The todo (and optionally its descendants) is recovered back to the active list.\n\n### \u{1F50D} When to Use\n* Use this to recover a task deleted by mistake, or pull a task out of the trash to continue active work.\n\n### \u{1F4A1} Key Features & Constraints\n* **Orphan Warning**: If the restored todo's parent is still in the trash, the call succeeds but returns `parent_in_trash_warning: true`, signaling that the restored todo is currently orphaned from a visible ancestor.\n* **Cascading Restore (`cascade`)**: If `cascade: true` is provided, all descendants still in the trash are also restored. Otherwise, descendants are left in the trash, and their count is reported back in `descendants_in_trash_count`.\n\n### \u26A0\uFE0F Common Errors & Troubleshooting\n* **`VERSION_CONFLICT` (HTTP 409)**: Thrown if `expected_version` is supplied and mismatches the database.\n* **`NOT_FOUND` (HTTP 404)**: Thrown if the target todo `id` does not exist or has already been permanently purged.\n\nExamples:\n $ wspc todo restore tod_xxx\n $ wspc todo restore tod_xxx --cascade\n").argument("<id>", "id").option("--expected-version <value>", "expected_version").option("--cascade <value>", "cascade").action(async (id, opts) => {
3964
4064
  const client2 = await loadSdkClient();
3965
4065
  const result = await todoRestore({
3966
4066
  client: client2._rawClient,
@@ -3983,6 +4083,27 @@ var todoRestoreCommand = new Command69("restore").description("Restore a soft-de
3983
4083
  render({ kind: "todo_restore", display: { "shape": "object", "format": { "id": "id-short", "user_id": "id-short", "project_id": "id-short", "parent_id": "id-short", "type_id": "id-short", "status": "status-badge", "due_at": "relative-time", "created_at": "relative-time", "updated_at": "relative-time", "deleted_at": "relative-time" } } }, result.data);
3984
4084
  });
3985
4085
 
4086
+ // src/generated/cli/todo/type/restore.ts
4087
+ import { Command as Command73 } from "commander";
4088
+ var todoTypeRestoreCommand = new Command73("restore").description("Restore a soft-deleted todo type").addHelpText("after", "\n### \u{1F3AF} Overview & Purpose\nRestore a previously archived/soft-deleted custom todo type.\n\n### \u{1F50D} When to Use\n* Use this to bring a retired task category back into active status.\n\n### \u{1F4A1} Key Features & Constraints\n* **Task Re-Attachment**: Restoring a type clears its `deleted_at` timestamp. Todo items previously assigned to this type immediately become active and validated under this recovered category schema.\n\n### \u26A0\uFE0F Common Errors & Troubleshooting\n* **`NOT_FOUND` (HTTP 404)**: Thrown if the target ID does not exist.\n\nExamples:\n $ wspc todo type restore typ_xxx\n").argument("<id>", "id").action(async (id, opts) => {
4089
+ const client2 = await loadSdkClient();
4090
+ const result = await todoTypeRestore({
4091
+ client: client2._rawClient,
4092
+ path: {
4093
+ id
4094
+ }
4095
+ });
4096
+ if (result.error || !result.response?.ok) {
4097
+ process.stderr.write(
4098
+ `HTTP ${result.response?.status ?? "?"}: ${JSON.stringify(result.error ?? "unknown error", null, 2)}
4099
+ `
4100
+ );
4101
+ process.exitCode = 1;
4102
+ return;
4103
+ }
4104
+ render({ kind: "todo_type_restore", display: void 0 }, result.data);
4105
+ });
4106
+
3986
4107
  // src/generated/cli/index.ts
3987
4108
  function registerGeneratedCommands(root) {
3988
4109
  const root_invite = root.command("invite").description("invite commands");
@@ -4069,7 +4190,11 @@ function registerGeneratedCommands(root) {
4069
4190
  root_todo.addCommand(todoCreateCommand);
4070
4191
  root_todo.addCommand(todoListCommand);
4071
4192
  const root_todo_type = root_todo.command("type").description("type commands");
4193
+ root_todo_type.addCommand(todoTypeCreateCommand);
4072
4194
  root_todo_type.addCommand(todoTypeListCommand);
4195
+ root_todo_type.addCommand(todoTypeDeleteCommand);
4196
+ root_todo_type.addCommand(todoTypeUpdateCommand);
4197
+ root_todo_type.addCommand(todoTypeRestoreCommand);
4073
4198
  root_todo.addCommand(todoDeleteCommand);
4074
4199
  root_todo.addCommand(todoGetCommand);
4075
4200
  root_todo.addCommand(todoUpdateCommand);
@@ -4077,7 +4202,7 @@ function registerGeneratedCommands(root) {
4077
4202
  }
4078
4203
 
4079
4204
  // src/handwritten/commands/login.ts
4080
- import { Command as Command70 } from "commander";
4205
+ import { Command as Command74 } from "commander";
4081
4206
 
4082
4207
  // src/handwritten/auth/device-flow.ts
4083
4208
  var DEFAULT_SLEEP = (ms) => new Promise((r) => setTimeout(r, ms));
@@ -4319,7 +4444,7 @@ function resolveLoginTarget(opts, env) {
4319
4444
  function wantsJson(opts, env) {
4320
4445
  return opts.json === true || env.WSPC_OUTPUT === "json";
4321
4446
  }
4322
- var loginCommand = new Command70("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("--api-base <url>", "Target API base URL (default: production)").option("--env <name>", "Config env name to store credentials under").option("--json", "Emit machine-readable events to stdout").action(async (opts) => {
4447
+ var loginCommand = new Command74("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("--api-base <url>", "Target API base URL (default: production)").option("--env <name>", "Config env name to store credentials under").option("--json", "Emit machine-readable events to stdout").action(async (opts) => {
4323
4448
  const store = new ConfigStore();
4324
4449
  const { baseUrl, envName } = resolveLoginTarget(opts, process.env);
4325
4450
  const output = wantsJson(opts, process.env) ? { write: () => {
@@ -4338,7 +4463,7 @@ var loginCommand = new Command70("login").description("Log in via OAuth device f
4338
4463
  });
4339
4464
 
4340
4465
  // src/handwritten/commands/logout.ts
4341
- import { Command as Command71 } from "commander";
4466
+ import { Command as Command75 } from "commander";
4342
4467
 
4343
4468
  // src/handwritten/auth/logout.ts
4344
4469
  async function runLogout(opts) {
@@ -4371,7 +4496,7 @@ async function runLogout(opts) {
4371
4496
  }
4372
4497
 
4373
4498
  // src/handwritten/commands/logout.ts
4374
- var logoutCommand = new Command71("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) => {
4499
+ var logoutCommand = new Command75("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) => {
4375
4500
  const res = await runLogout({ store: new ConfigStore(), email, all: opts.all });
4376
4501
  if (res.removed.length === 0) {
4377
4502
  process.stdout.write("nothing to log out\n");
@@ -4384,7 +4509,7 @@ var logoutCommand = new Command71("logout").description("Log out an account (def
4384
4509
  });
4385
4510
 
4386
4511
  // src/handwritten/commands/whoami.ts
4387
- import { Command as Command72 } from "commander";
4512
+ import { Command as Command76 } from "commander";
4388
4513
  var ENV_DISPLAY = {
4389
4514
  shape: "object",
4390
4515
  fields: ["name", "api_base", "account", "actor", "agent_label"]
@@ -4415,7 +4540,7 @@ async function backfillActiveEmail(store, envName, email, userId) {
4415
4540
  rekeyLegacyAccount(cfg, envName, email, userId);
4416
4541
  });
4417
4542
  }
4418
- var whoamiCommand = new Command72("whoami").description("Show the active env, signed-in account, and organization").action(async () => {
4543
+ var whoamiCommand = new Command76("whoami").description("Show the active env, signed-in account, and organization").action(async () => {
4419
4544
  const store = new ConfigStore();
4420
4545
  const config = await store.read();
4421
4546
  let resolved;
@@ -4468,8 +4593,8 @@ function printLoggedOut() {
4468
4593
  }
4469
4594
 
4470
4595
  // src/handwritten/commands/config.ts
4471
- import { Command as Command73 } from "commander";
4472
- var configCommand = new Command73("config").description("Manage wspc local config");
4596
+ import { Command as Command77 } from "commander";
4597
+ var configCommand = new Command77("config").description("Manage wspc local config");
4473
4598
  registerRenderer("config_show", (data) => {
4474
4599
  const d = data;
4475
4600
  if (d.envs.length === 0) {
@@ -4540,7 +4665,7 @@ configCommand.command("use <env>").description("Switch current_env").action(asyn
4540
4665
  });
4541
4666
 
4542
4667
  // src/handwritten/commands/account.ts
4543
- import { Command as Command74 } from "commander";
4668
+ import { Command as Command78 } from "commander";
4544
4669
  async function listAccounts(store) {
4545
4670
  const c = await store.read();
4546
4671
  const envName = c.current_env;
@@ -4581,7 +4706,7 @@ registerRenderer("account_ls", (data) => {
4581
4706
  ]);
4582
4707
  process.stdout.write(table(headers, body));
4583
4708
  });
4584
- var accountCommand = new Command74("account").description("Manage logged-in accounts");
4709
+ var accountCommand = new Command78("account").description("Manage logged-in accounts");
4585
4710
  accountCommand.command("ls").description("List accounts in the current env (active marked with \u2713)").action(async () => {
4586
4711
  const accounts = await listAccounts(new ConfigStore());
4587
4712
  render({ kind: "account_ls" }, { accounts });
@@ -4593,7 +4718,7 @@ accountCommand.command("switch <email>").description("Set the active account for
4593
4718
  });
4594
4719
 
4595
4720
  // src/handwritten/commands/tour.ts
4596
- import { Command as Command75 } from "commander";
4721
+ import { Command as Command79 } from "commander";
4597
4722
  var GUIDE_URL_DEFAULT = "https://mcp.wspc.ai/guide";
4598
4723
  function resolveGuideUrl(env = process.env) {
4599
4724
  const override = env.WSPC_GUIDE_URL?.trim();
@@ -4605,14 +4730,14 @@ async function fetchGuide(url, fetchImpl = fetch) {
4605
4730
  return await res.text();
4606
4731
  }
4607
4732
  var TOUR_HINT = "This is the wspc guided tour for your AI agent \u2014 ask your agent to run `wspc tour` and follow it.\n";
4608
- var tourCommand = new Command75("tour").description("Print the wspc guided-tour script for your AI agent to read and follow").action(async () => {
4733
+ var tourCommand = new Command79("tour").description("Print the wspc guided-tour script for your AI agent to read and follow").action(async () => {
4609
4734
  const text = await fetchGuide(resolveGuideUrl());
4610
4735
  if (process.stdout.isTTY) process.stderr.write(TOUR_HINT);
4611
4736
  process.stdout.write(text.endsWith("\n") ? text : text + "\n");
4612
4737
  });
4613
4738
 
4614
4739
  // src/handwritten/commands/todo-done.ts
4615
- import { Command as Command76 } from "commander";
4740
+ import { Command as Command80 } from "commander";
4616
4741
  var TODO_UPDATE_DISPLAY = {
4617
4742
  shape: "object",
4618
4743
  format: {
@@ -4630,7 +4755,7 @@ var TODO_UPDATE_DISPLAY = {
4630
4755
  deleted_at: "relative-time"
4631
4756
  }
4632
4757
  };
4633
- var todoDoneCommand = new Command76("done").description("Mark a todo done (sugar for `update <id> --status done`)").argument("<id>", "Todo id").action(async (id) => {
4758
+ var todoDoneCommand = new Command80("done").description("Mark a todo done (sugar for `update <id> --status done`)").argument("<id>", "Todo id").action(async (id) => {
4634
4759
  const client2 = await loadSdkClient();
4635
4760
  const result = await todoUpdate({
4636
4761
  client: client2._rawClient,
@@ -4649,7 +4774,7 @@ var todoDoneCommand = new Command76("done").description("Mark a todo done (sugar
4649
4774
  });
4650
4775
 
4651
4776
  // src/handwritten/commands/email/send.ts
4652
- import { Command as Command77 } from "commander";
4777
+ import { Command as Command81 } from "commander";
4653
4778
  import { randomUUID } from "crypto";
4654
4779
  import { readFile, stat } from "fs/promises";
4655
4780
  import { basename } from "path";
@@ -4708,7 +4833,7 @@ async function resolveAttachment(input) {
4708
4833
  `--attach ${input}: neither a readable file nor a valid <prefix>_<ulid>:<idx> reference.`
4709
4834
  );
4710
4835
  }
4711
- var sendCommand = new Command77("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)", []).option("--idempotency-key <key>", "idempotency key (auto-generated if omitted)").action(async (opts) => {
4836
+ var sendCommand = new Command81("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)", []).option("--idempotency-key <key>", "idempotency key (auto-generated if omitted)").action(async (opts) => {
4712
4837
  const isReply = Boolean(opts.reply);
4713
4838
  const to = opts.to;
4714
4839
  const attachInputs = opts.attach;
@@ -4797,7 +4922,7 @@ var sendCommand = new Command77("send").description("Send an outbound email").re
4797
4922
  });
4798
4923
 
4799
4924
  // src/handwritten/commands/email/attachment.ts
4800
- import { Command as Command78 } from "commander";
4925
+ import { Command as Command82 } from "commander";
4801
4926
  import { createWriteStream } from "fs";
4802
4927
  import { Readable } from "stream";
4803
4928
  import { pipeline } from "stream/promises";
@@ -4815,7 +4940,7 @@ function parseContentDispositionFilename(header) {
4815
4940
  }
4816
4941
 
4817
4942
  // src/handwritten/commands/email/attachment.ts
4818
- var attachmentCommand = new Command78("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) => {
4943
+ var attachmentCommand = new Command82("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) => {
4819
4944
  const idx = Number(idxArg);
4820
4945
  if (!Number.isInteger(idx) || idx < 0) {
4821
4946
  process.stderr.write(`<idx> must be a non-negative integer (got "${idxArg}")
@@ -4847,7 +4972,7 @@ var attachmentCommand = new Command78("attachment").description("Download an inb
4847
4972
  });
4848
4973
 
4849
4974
  // src/handwritten/commands/drive/bind.ts
4850
- import { Command as Command79 } from "commander";
4975
+ import { Command as Command83 } from "commander";
4851
4976
  import { stat as stat3 } from "fs/promises";
4852
4977
  import { resolve } from "path";
4853
4978
 
@@ -5175,7 +5300,7 @@ async function assertExistingDirectory(path) {
5175
5300
  }
5176
5301
  }
5177
5302
  function driveBindCommand() {
5178
- return new Command79("bind").description("Bind a local folder to an existing Drive library").requiredOption("--library <id>", "existing Drive library id").argument("[path]", "local folder path", ".").action(async (path, opts) => {
5303
+ return new Command83("bind").description("Bind a local folder to an existing Drive library").requiredOption("--library <id>", "existing Drive library id").argument("[path]", "local folder path", ".").action(async (path, opts) => {
5179
5304
  const root = resolve(path);
5180
5305
  await assertExistingDirectory(root);
5181
5306
  const api = await createDriveApi();
@@ -5193,7 +5318,7 @@ function driveBindCommand() {
5193
5318
  }
5194
5319
 
5195
5320
  // src/handwritten/commands/drive/sync.ts
5196
- import { Command as Command80 } from "commander";
5321
+ import { Command as Command84 } from "commander";
5197
5322
  import { mkdir as mkdir3, readFile as readFile4, rm as rm3, writeFile as writeFile3 } from "fs/promises";
5198
5323
  import { basename as basename3, dirname as dirname2, join as join6, posix as pathPosix3, resolve as resolve3 } from "path";
5199
5324
  import { createHash as createHash3, randomUUID as randomUUID4 } from "crypto";
@@ -6108,7 +6233,7 @@ function decisionFields(path, action, entry, local, remote) {
6108
6233
  };
6109
6234
  }
6110
6235
  function driveSyncCommand(api) {
6111
- const sync = new Command80("sync").description("Drive sync commands");
6236
+ const sync = new Command84("sync").description("Drive sync commands");
6112
6237
  sync.command("once").description("Run one Drive sync pass").argument("[path]", "local folder path", ".").action(async (path) => {
6113
6238
  const summary = await runDriveSyncOnce(resolve3(path), api);
6114
6239
  render({ kind: "drive_sync_once", display: { shape: "object" } }, summary);
@@ -6667,7 +6792,7 @@ function containsVersionConflict(value) {
6667
6792
  }
6668
6793
 
6669
6794
  // src/handwritten/commands/drive/watch.ts
6670
- import { Command as Command81 } from "commander";
6795
+ import { Command as Command85 } from "commander";
6671
6796
  import chokidar from "chokidar";
6672
6797
  import { watch as fsWatch } from "fs";
6673
6798
  import { basename as basename4, relative as relative2, resolve as resolve4 } from "path";
@@ -7174,7 +7299,7 @@ async function runDriveWatch(root, options = {}) {
7174
7299
  }
7175
7300
  }
7176
7301
  function driveWatchCommand(options = {}) {
7177
- return new Command81("watch").description("Watch a bound Drive folder and sync local changes").argument("[path]", "local folder path", ".").option("--debug", "append debug NDJSON events to <folder>/.wspc-drive/debug.log").action(async (path, flags) => {
7302
+ return new Command85("watch").description("Watch a bound Drive folder and sync local changes").argument("[path]", "local folder path", ".").option("--debug", "append debug NDJSON events to <folder>/.wspc-drive/debug.log").action(async (path, flags) => {
7178
7303
  const debug = options.debug ?? (flags.debug === true || process.env.WSPC_DRIVE_DEBUG === "1");
7179
7304
  await runDriveWatch(resolve4(path), { ...options, debug });
7180
7305
  });
@@ -7260,7 +7385,7 @@ function waitForStopSignal(onRegistered) {
7260
7385
  function mountDriveCommands(program) {
7261
7386
  let drive = program.commands.find((c) => c.name() === "drive");
7262
7387
  if (!drive) {
7263
- drive = new Command82("drive").description("Drive commands");
7388
+ drive = new Command86("drive").description("Drive commands");
7264
7389
  program.addCommand(drive);
7265
7390
  }
7266
7391
  if (!drive.commands.some((c) => c.name() === "bind")) {
@@ -7286,7 +7411,7 @@ function isCliEntrypoint(argv = process.argv, metaUrl = import.meta.url) {
7286
7411
  }
7287
7412
  }
7288
7413
  function buildProgram() {
7289
- const program = new Command82().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) => {
7414
+ const program = new Command86().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) => {
7290
7415
  const globals = actionCommand.optsWithGlobals();
7291
7416
  if (globals.json) process.env.WSPC_OUTPUT = "json";
7292
7417
  if (globals.account) process.env.WSPC_ACCOUNT = String(globals.account);