@gonovus-ai/spec-flow-cli 0.2.0 → 0.3.1

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
@@ -5,6 +5,7 @@ import { z } from "zod";
5
5
  import { addAuthenticationCommandTopology } from "./authentication-command-topology.js";
6
6
  import { registerGitHubAccountCommands } from "./github-account-commands.js";
7
7
  import { parseHttpOrigin, unresolvedSpecFlowApiOrigin, } from "./http-origin.js";
8
+ import { registerProductMapCommands } from "./product-map-commands.js";
8
9
  import { specFlowCliUserAgent } from "./user-agent.js";
9
10
  import { registerUserApiKeyCommands } from "./user-api-key-commands.js";
10
11
  import { registerWorkItemAssetCommands } from "./work-item-asset-commands.js";
@@ -56,7 +57,7 @@ const findingReferenceSchema = z
56
57
  const findingTargetSchema = z
57
58
  .object({
58
59
  references: z.array(findingReferenceSchema).min(1).max(50),
59
- specId: z.string().trim().toUpperCase().regex(/^[A-Z]{2,10}-[1-9]\d*$/u),
60
+ specId: z.string().trim().toUpperCase().regex(/^[A-Z]{2,10}-S[1-9]\d*$/u),
60
61
  })
61
62
  .strict();
62
63
  const findingEvidenceSchema = z.discriminatedUnion("kind", [
@@ -169,21 +170,37 @@ export async function runCli(arguments_, dependencies) {
169
170
  workspace
170
171
  .command("list")
171
172
  .action(async () => {
172
- exitCode = await run((client) => client.GET("/v1/workspaces"), { rejectProjectReadToken: true });
173
+ exitCode = await run((client) => client.GET("/v1/workspaces"), { rejectWorkspaceReadToken: true });
173
174
  });
174
175
  workspace
175
176
  .command("create")
176
177
  .requiredOption("--name <name>")
178
+ .requiredOption("--prefix <prefix>", "Immutable Workspace number prefix, 2 to 10 uppercase letters")
179
+ .option("--spec-language <language>", "Workspace keyword language: zh-CN or en", (value) => z.enum(["zh-CN", "en"]).parse(value), "zh-CN")
177
180
  .action(async (options) => {
178
- if (hasProjectReadToken(dependencies.environment)) {
181
+ if (hasWorkspaceReadToken(dependencies.environment)) {
179
182
  exitCode = rejectTokenWrite(dependencies);
180
183
  return;
181
184
  }
182
185
  const idempotencyKey = randomUUID();
183
186
  exitCode = await run((client) => client.POST("/v1/workspaces", {
184
- body: { name: options.name },
187
+ body: { name: options.name, prefix: options.prefix, specLanguage: options.specLanguage },
185
188
  params: { header: { "idempotency-key": idempotencyKey } },
186
- }), { retryTransportOnce: true });
189
+ }), { retryTransportOnce: true, ambiguousOutcomeMessage: "Workspace creation outcome is unknown; run workspace list before trying a new command identity" });
190
+ });
191
+ workspace.command("rename").requiredOption("--workspace-id <workspaceId>").requiredOption("--name <name>").requiredOption("--expected-version <version>", "Observed Workspace settings version", parseOpaqueVersion).requiredOption("--idempotency-key <key>").action(async (options) => {
192
+ exitCode = await run(client => client.PATCH("/v1/workspaces/{workspaceId}/settings", { params: { path: { workspaceId: options.workspaceId }, header: { "idempotency-key": options.idempotencyKey, "if-match": options.expectedVersion } }, body: { name: options.name } }), { retryTransportOnce: true });
193
+ });
194
+ const workspaceSettings = workspace.command("settings");
195
+ workspaceSettings.command("read").requiredOption("--workspace-id <workspaceId>").action(async (options) => {
196
+ exitCode = await run(client => client.GET("/v1/workspaces/{workspaceId}/settings", { params: { path: options } }));
197
+ });
198
+ workspaceSettings.command("update").requiredOption("--workspace-id <workspaceId>").requiredOption("--spec-language <language>", "Workspace keyword language", (value) => z.enum(["zh-CN", "en"]).parse(value)).requiredOption("--expected-version <version>", "Observed Workspace settings version", parseOpaqueVersion).requiredOption("--idempotency-key <key>").action(async (options) => {
199
+ if (hasWorkspaceReadToken(dependencies.environment)) {
200
+ exitCode = rejectTokenWrite(dependencies);
201
+ return;
202
+ }
203
+ exitCode = await run(client => client.PATCH("/v1/workspaces/{workspaceId}/settings", { params: { path: { workspaceId: options.workspaceId }, header: { "idempotency-key": options.idempotencyKey, "if-match": options.expectedVersion } }, body: { specLanguage: options.specLanguage } }), { retryTransportOnce: true });
187
204
  });
188
205
  const workspaceMember = workspace.command("member");
189
206
  workspaceMember
@@ -192,7 +209,7 @@ export async function runCli(arguments_, dependencies) {
192
209
  .action(async (options) => {
193
210
  exitCode = await run((client) => client.GET("/v1/workspaces/{workspaceId}/members", {
194
211
  params: { path: { workspaceId: options.workspaceId } },
195
- }), { rejectProjectReadToken: true });
212
+ }), { rejectWorkspaceReadToken: true });
196
213
  });
197
214
  workspaceMember
198
215
  .command("search")
@@ -205,7 +222,7 @@ export async function runCli(arguments_, dependencies) {
205
222
  path: { workspaceId: options.workspaceId },
206
223
  query: { limit: options.limit, query: options.query },
207
224
  },
208
- }), { rejectProjectReadToken: true });
225
+ }), { rejectWorkspaceReadToken: true });
209
226
  });
210
227
  workspaceMember
211
228
  .command("add")
@@ -214,7 +231,7 @@ export async function runCli(arguments_, dependencies) {
214
231
  .requiredOption("--subject <subject>", "Authentik subject from `workspace member search`")
215
232
  .requiredOption("--role <role>", "Workspace Role name")
216
233
  .action(async (options) => {
217
- if (hasProjectReadToken(dependencies.environment)) {
234
+ if (hasWorkspaceReadToken(dependencies.environment)) {
218
235
  exitCode = rejectTokenWrite(dependencies);
219
236
  return;
220
237
  }
@@ -238,7 +255,7 @@ export async function runCli(arguments_, dependencies) {
238
255
  .requiredOption("--role <role>", "Workspace Role name")
239
256
  .requiredOption("--expected-version <version>", "Opaque Member version from `workspace member list`", parseOpaqueVersion)
240
257
  .action(async (options) => {
241
- if (hasProjectReadToken(dependencies.environment)) {
258
+ if (hasWorkspaceReadToken(dependencies.environment)) {
242
259
  exitCode = rejectTokenWrite(dependencies);
243
260
  return;
244
261
  }
@@ -266,7 +283,7 @@ export async function runCli(arguments_, dependencies) {
266
283
  .requiredOption("--user-id <userId>", "Member User ID from `workspace member list`")
267
284
  .requiredOption("--expected-version <version>", "Opaque Member version from `workspace member list`", parseOpaqueVersion)
268
285
  .action(async (options) => {
269
- if (hasProjectReadToken(dependencies.environment)) {
286
+ if (hasWorkspaceReadToken(dependencies.environment)) {
270
287
  exitCode = rejectTokenWrite(dependencies);
271
288
  return;
272
289
  }
@@ -294,7 +311,7 @@ export async function runCli(arguments_, dependencies) {
294
311
  .action(async (options) => {
295
312
  exitCode = await run((client) => client.GET("/v1/workspaces/{workspaceId}/roles", {
296
313
  params: { path: { workspaceId: options.workspaceId } },
297
- }), { rejectProjectReadToken: true });
314
+ }), { rejectWorkspaceReadToken: true });
298
315
  });
299
316
  workspaceRole
300
317
  .command("create")
@@ -302,7 +319,7 @@ export async function runCli(arguments_, dependencies) {
302
319
  .requiredOption("--name <name>", "Custom Workspace Role name")
303
320
  .requiredOption("--statements <statements>", "JSON object mapping permission resources to granted actions", parseRoleStatements)
304
321
  .action(async (options) => {
305
- if (hasProjectReadToken(dependencies.environment)) {
322
+ if (hasWorkspaceReadToken(dependencies.environment)) {
306
323
  exitCode = rejectTokenWrite(dependencies);
307
324
  return;
308
325
  }
@@ -322,7 +339,7 @@ export async function runCli(arguments_, dependencies) {
322
339
  .requiredOption("--statements <statements>", "JSON object mapping permission resources to granted actions", parseRoleStatements)
323
340
  .requiredOption("--expected-version <version>", "Opaque Role version from `workspace role list`", parseOpaqueVersion)
324
341
  .action(async (options) => {
325
- if (hasProjectReadToken(dependencies.environment)) {
342
+ if (hasWorkspaceReadToken(dependencies.environment)) {
326
343
  exitCode = rejectTokenWrite(dependencies);
327
344
  return;
328
345
  }
@@ -350,7 +367,7 @@ export async function runCli(arguments_, dependencies) {
350
367
  .requiredOption("--name <name>", "Custom Workspace Role name")
351
368
  .requiredOption("--expected-version <version>", "Opaque Role version from `workspace role list`", parseOpaqueVersion)
352
369
  .action(async (options) => {
353
- if (hasProjectReadToken(dependencies.environment)) {
370
+ if (hasWorkspaceReadToken(dependencies.environment)) {
354
371
  exitCode = rejectTokenWrite(dependencies);
355
372
  return;
356
373
  }
@@ -388,16 +405,14 @@ export async function runCli(arguments_, dependencies) {
388
405
  : { workspaceId: options.workspaceId }),
389
406
  },
390
407
  },
391
- }), { rejectProjectReadToken: true });
408
+ }), { rejectWorkspaceReadToken: true });
392
409
  });
393
410
  project
394
411
  .command("create")
395
412
  .requiredOption("--name <name>")
396
- .requiredOption("--project-id <projectId>", "Immutable Project ID (2-10 letters)", parseProjectId)
397
413
  .requiredOption("--workspace-id <workspaceId>", "Workspace ID returned by `workspace list` or `workspace create`")
398
- .option("--spec-language <language>", "Spec Language: en or zh-CN (default: en)")
399
414
  .action(async (options) => {
400
- if (hasProjectReadToken(dependencies.environment)) {
415
+ if (hasWorkspaceReadToken(dependencies.environment)) {
401
416
  exitCode = rejectTokenWrite(dependencies);
402
417
  return;
403
418
  }
@@ -405,11 +420,7 @@ export async function runCli(arguments_, dependencies) {
405
420
  exitCode = await run((client) => client.POST("/v1/projects", {
406
421
  body: {
407
422
  name: options.name,
408
- projectId: options.projectId,
409
423
  workspaceId: options.workspaceId,
410
- ...(options.specLanguage === undefined
411
- ? {}
412
- : { specLanguage: options.specLanguage }),
413
424
  },
414
425
  params: { header: { "idempotency-key": idempotencyKey } },
415
426
  }), { retryTransportOnce: true });
@@ -431,7 +442,7 @@ export async function runCli(arguments_, dependencies) {
431
442
  },
432
443
  }), {
433
444
  ambiguousOutcomeMessage: `Project ${operation} outcome is unknown; read Project context before retrying`,
434
- rejectProjectReadToken: true,
445
+ rejectWorkspaceReadToken: true,
435
446
  retryTransportOnce: true,
436
447
  });
437
448
  });
@@ -444,21 +455,13 @@ export async function runCli(arguments_, dependencies) {
444
455
  params: { path: { projectId: options.projectId } },
445
456
  }));
446
457
  });
447
- project
448
- .command("directory")
449
- .requiredOption("--project-id <projectId>")
450
- .action(async (options) => {
451
- exitCode = await run((client) => client.GET("/v1/projects/{projectId}/spec-directory", {
452
- params: { path: { projectId: options.projectId } },
453
- }));
454
- });
455
458
  const sharing = project.command("sharing").description("Manage anonymous task-only Project sharing");
456
459
  sharing.command("read")
457
460
  .requiredOption("--project-id <projectId>")
458
461
  .action(async (options) => {
459
462
  exitCode = await run((client) => client.GET("/v1/projects/{projectId}/sharing", {
460
463
  params: { path: { projectId: options.projectId } },
461
- }), { rejectProjectReadToken: true });
464
+ }), { rejectWorkspaceReadToken: true });
462
465
  });
463
466
  sharing.command("set")
464
467
  .requiredOption("--project-id <projectId>")
@@ -474,42 +477,45 @@ export async function runCli(arguments_, dependencies) {
474
477
  return value;
475
478
  })
476
479
  .action(async (options) => {
477
- if (hasProjectReadToken(dependencies.environment)) {
480
+ if (hasWorkspaceReadToken(dependencies.environment)) {
478
481
  exitCode = rejectTokenWrite(dependencies);
479
482
  return;
480
483
  }
481
484
  exitCode = await run((client) => client.PUT("/v1/projects/{projectId}/sharing", {
482
485
  body: { isPublic: options.public },
483
486
  params: { path: { projectId: options.projectId }, header: { "if-match": options.expectedVersion, "idempotency-key": options.idempotencyKey } },
484
- }), { rejectProjectReadToken: true, retryTransportOnce: true, ambiguousOutcomeMessage: "Sharing outcome is unknown; reread project sharing and retry the same command key and intent" });
487
+ }), { rejectWorkspaceReadToken: true, retryTransportOnce: true, ambiguousOutcomeMessage: "Sharing outcome is unknown; reread project sharing and retry the same command key and intent" });
485
488
  });
486
489
  const spec = program.command("spec");
490
+ spec.command("list").requiredOption("--workspace-id <workspaceId>").option("--project-id <projectId>").option("--query <query>").option("--limit <limit>", "Maximum results", parseSearchLimit, 50).option("--cursor <cursor>").action(async (options) => {
491
+ exitCode = await run(client => client.GET("/v1/workspaces/{workspaceId}/spec-directory", { params: { path: { workspaceId: options.workspaceId }, query: { ...(options.projectId === undefined ? {} : { projectId: options.projectId }), ...(options.query === undefined ? {} : { query: options.query }), limit: options.limit, ...(options.cursor === undefined ? {} : { cursor: options.cursor }) } } }), { rejectWorkspaceReadToken: true });
492
+ });
487
493
  spec
488
494
  .command("read")
489
- .requiredOption("--project-id <projectId>")
495
+ .requiredOption("--workspace-id <workspaceId>")
490
496
  .requiredOption("--spec-id <specId>")
491
497
  .action(async (options) => {
492
- exitCode = await run((client) => client.GET("/v1/projects/{projectId}/specs/{specId}", {
498
+ exitCode = await run((client) => client.GET("/v1/workspaces/{workspaceId}/specs/{specId}", {
493
499
  params: { path: options },
494
- }), { rejectProjectReadToken: true });
500
+ }), { rejectWorkspaceReadToken: true });
495
501
  });
496
502
  spec
497
503
  .command("changelog")
498
- .requiredOption("--project-id <projectId>")
504
+ .requiredOption("--workspace-id <workspaceId>")
499
505
  .requiredOption("--spec-id <specId>")
500
506
  .action(async (options) => {
501
- exitCode = await run((client) => client.GET("/v1/projects/{projectId}/specs/{specId}/changelog", { params: { path: options } }), { rejectProjectReadToken: true });
507
+ exitCode = await run((client) => client.GET("/v1/workspaces/{workspaceId}/specs/{specId}/changelog", { params: { path: options } }), { rejectWorkspaceReadToken: true });
502
508
  });
503
509
  spec
504
510
  .command("search")
505
- .requiredOption("--project-id <projectId>")
511
+ .requiredOption("--workspace-id <workspaceId>")
506
512
  .requiredOption("--query <query>")
507
513
  .option("--limit <limit>", "Maximum results", parseSearchLimit, 20)
508
514
  .option("--cursor <cursor>")
509
515
  .action(async (options) => {
510
- exitCode = await run((client) => client.GET("/v1/projects/{projectId}/spec-search", {
516
+ exitCode = await run((client) => client.GET("/v1/workspaces/{workspaceId}/spec-search", {
511
517
  params: {
512
- path: { projectId: options.projectId },
518
+ path: { workspaceId: options.workspaceId },
513
519
  query: {
514
520
  ...(options.cursor === undefined
515
521
  ? {}
@@ -522,33 +528,33 @@ export async function runCli(arguments_, dependencies) {
522
528
  });
523
529
  spec
524
530
  .command("current")
525
- .requiredOption("--project-id <projectId>")
531
+ .requiredOption("--workspace-id <workspaceId>")
526
532
  .requiredOption("--spec-id <specId>")
527
533
  .action(async (options) => {
528
- exitCode = await run((client) => client.GET("/v1/projects/{projectId}/specs/{specId}/current-revision", { params: { path: options } }));
534
+ exitCode = await run((client) => client.GET(hasWorkspaceReadToken(dependencies.environment) ? "/v1/workspaces/{workspaceId}/published/specs/{specId}/current-revision" : "/v1/workspaces/{workspaceId}/specs/{specId}/current-revision", { params: { path: options } }));
529
535
  });
530
536
  spec
531
537
  .command("revision")
532
- .requiredOption("--project-id <projectId>")
538
+ .requiredOption("--workspace-id <workspaceId>")
533
539
  .requiredOption("--spec-id <specId>")
534
540
  .requiredOption("--revision-id <revisionId>")
535
541
  .action(async (options) => {
536
- exitCode = await run((client) => client.GET("/v1/projects/{projectId}/specs/{specId}/revisions/{revisionId}", { params: { path: options } }));
542
+ exitCode = await run((client) => client.GET(hasWorkspaceReadToken(dependencies.environment) ? "/v1/workspaces/{workspaceId}/published/specs/{specId}/revisions/{revisionId}" : "/v1/workspaces/{workspaceId}/specs/{specId}/revisions/{revisionId}", { params: { path: options } }));
537
543
  });
538
544
  spec
539
545
  .command("edit")
540
- .requiredOption("--project-id <projectId>")
546
+ .requiredOption("--workspace-id <workspaceId>")
541
547
  .requiredOption("--spec-id <specId>")
542
548
  .requiredOption("--content <content>")
543
549
  .option("--agent-revision <agentRevision>")
544
550
  .option("--agent-model <agentModel>")
545
551
  .option("--source-reference-ids <sourceReferenceIds>")
546
552
  .action(async (options) => {
547
- if (hasProjectReadToken(dependencies.environment)) {
553
+ if (hasWorkspaceReadToken(dependencies.environment)) {
548
554
  exitCode = rejectTokenWrite(dependencies);
549
555
  return;
550
556
  }
551
- exitCode = await run((client) => client.PUT("/v1/projects/{projectId}/specs/{specId}/draft", {
557
+ exitCode = await run((client) => client.PUT("/v1/workspaces/{workspaceId}/specs/{specId}/draft", {
552
558
  body: {
553
559
  content: options.content,
554
560
  provenance: toAgentProvenance(options),
@@ -558,17 +564,17 @@ export async function runCli(arguments_, dependencies) {
558
564
  });
559
565
  spec
560
566
  .command("assign-scenario-ids")
561
- .requiredOption("--project-id <projectId>")
567
+ .requiredOption("--workspace-id <workspaceId>")
562
568
  .requiredOption("--spec-id <specId>")
563
569
  .option("--agent-revision <agentRevision>")
564
570
  .option("--agent-model <agentModel>")
565
571
  .action(async (options) => {
566
- if (hasProjectReadToken(dependencies.environment)) {
572
+ if (hasWorkspaceReadToken(dependencies.environment)) {
567
573
  exitCode = rejectTokenWrite(dependencies);
568
574
  return;
569
575
  }
570
576
  const idempotencyKey = randomUUID();
571
- exitCode = await run((client) => client.POST("/v1/projects/{projectId}/specs/{specId}/draft/scenario-ids", {
577
+ exitCode = await run((client) => client.POST("/v1/workspaces/{workspaceId}/specs/{specId}/draft/scenario-ids", {
572
578
  body: { provenance: toAgentProvenance(options) },
573
579
  params: {
574
580
  header: { "idempotency-key": idempotencyKey },
@@ -581,15 +587,15 @@ export async function runCli(arguments_, dependencies) {
581
587
  });
582
588
  spec
583
589
  .command("publish")
584
- .requiredOption("--project-id <projectId>")
590
+ .requiredOption("--workspace-id <workspaceId>")
585
591
  .requiredOption("--spec-id <specId>")
586
592
  .action(async (options) => {
587
- if (hasProjectReadToken(dependencies.environment)) {
593
+ if (hasWorkspaceReadToken(dependencies.environment)) {
588
594
  exitCode = rejectTokenWrite(dependencies);
589
595
  return;
590
596
  }
591
597
  const idempotencyKey = randomUUID();
592
- exitCode = await run((client) => client.POST("/v1/projects/{projectId}/specs/{specId}/publish", {
598
+ exitCode = await run((client) => client.POST("/v1/workspaces/{workspaceId}/specs/{specId}/publish", {
593
599
  params: {
594
600
  header: { "idempotency-key": idempotencyKey },
595
601
  path: options,
@@ -601,18 +607,18 @@ export async function runCli(arguments_, dependencies) {
601
607
  });
602
608
  spec
603
609
  .command("create")
604
- .requiredOption("--project-id <projectId>")
610
+ .requiredOption("--workspace-id <workspaceId>")
605
611
  .option("--capability-id <capabilityId>")
606
612
  .requiredOption("--feature-name <featureName>")
607
613
  .option("--source-references <sourceReferences>")
608
614
  .action(async (options) => {
609
- if (hasProjectReadToken(dependencies.environment)) {
615
+ if (hasWorkspaceReadToken(dependencies.environment)) {
610
616
  exitCode = rejectTokenWrite(dependencies);
611
617
  return;
612
618
  }
613
619
  const idempotencyKey = randomUUID();
614
620
  const sourceReferences = parseSourceReferences(options.sourceReferences ?? "[]");
615
- exitCode = await run((client) => client.POST("/v1/projects/{projectId}/specs", {
621
+ exitCode = await run((client) => client.POST("/v1/workspaces/{workspaceId}/specs", {
616
622
  body: {
617
623
  capabilityId: options.capabilityId ?? null,
618
624
  featureName: options.featureName,
@@ -620,7 +626,7 @@ export async function runCli(arguments_, dependencies) {
620
626
  },
621
627
  params: {
622
628
  header: { "idempotency-key": idempotencyKey },
623
- path: { projectId: options.projectId },
629
+ path: { workspaceId: options.workspaceId },
624
630
  },
625
631
  }), {
626
632
  ambiguousOutcomeMessage: "Spec Creation outcome is unknown; inspect the Project directory before retrying",
@@ -629,13 +635,13 @@ export async function runCli(arguments_, dependencies) {
629
635
  });
630
636
  spec
631
637
  .command("update")
632
- .requiredOption("--project-id <projectId>")
638
+ .requiredOption("--workspace-id <workspaceId>")
633
639
  .requiredOption("--spec-id <specId>")
634
640
  .requiredOption("--expected-version <expectedVersion>", "Opaque version returned by SpecFlow", parseOpaqueVersion)
635
641
  .option("--capability-id <capabilityId>")
636
642
  .option("--source-references <sourceReferences>")
637
643
  .action(async (options) => {
638
- if (hasProjectReadToken(dependencies.environment)) {
644
+ if (hasWorkspaceReadToken(dependencies.environment)) {
639
645
  exitCode = rejectTokenWrite(dependencies);
640
646
  return;
641
647
  }
@@ -646,7 +652,7 @@ export async function runCli(arguments_, dependencies) {
646
652
  const sourceReferences = options.sourceReferences === undefined
647
653
  ? undefined
648
654
  : parseSourceReferences(options.sourceReferences);
649
- exitCode = await run((client) => client.PATCH("/v1/projects/{projectId}/specs/{specId}", {
655
+ exitCode = await run((client) => client.PATCH("/v1/workspaces/{workspaceId}/specs/{specId}", {
650
656
  body: {
651
657
  ...(options.capabilityId === undefined
652
658
  ? {}
@@ -663,16 +669,16 @@ export async function runCli(arguments_, dependencies) {
663
669
  });
664
670
  spec
665
671
  .command("draft")
666
- .requiredOption("--project-id <projectId>")
672
+ .requiredOption("--workspace-id <workspaceId>")
667
673
  .requiredOption("--spec-id <specId>")
668
674
  .requiredOption("--expected-version <expectedVersion>", "Opaque version returned by SpecFlow", parseOpaqueVersion)
669
675
  .action(async (options) => {
670
- if (hasProjectReadToken(dependencies.environment)) {
676
+ if (hasWorkspaceReadToken(dependencies.environment)) {
671
677
  exitCode = rejectTokenWrite(dependencies);
672
678
  return;
673
679
  }
674
680
  const idempotencyKey = randomUUID();
675
- exitCode = await run((client) => client.POST("/v1/projects/{projectId}/specs/{specId}/draft-session", {
681
+ exitCode = await run((client) => client.POST("/v1/workspaces/{workspaceId}/specs/{specId}/draft-session", {
676
682
  params: {
677
683
  header: {
678
684
  "idempotency-key": idempotencyKey,
@@ -687,13 +693,13 @@ export async function runCli(arguments_, dependencies) {
687
693
  });
688
694
  spec
689
695
  .command("state")
690
- .requiredOption("--project-id <projectId>")
696
+ .requiredOption("--workspace-id <workspaceId>")
691
697
  .requiredOption("--spec-id <specId>")
692
698
  .requiredOption("--expected-version <expectedVersion>", "Opaque version returned by SpecFlow", parseOpaqueVersion)
693
699
  .requiredOption("--state <state>", "Ready or Unavailable", parseSpecState)
694
700
  .option("--unavailability-reason <unavailabilityReason>")
695
701
  .action(async (options) => {
696
- if (hasProjectReadToken(dependencies.environment)) {
702
+ if (hasWorkspaceReadToken(dependencies.environment)) {
697
703
  exitCode = rejectTokenWrite(dependencies);
698
704
  return;
699
705
  }
@@ -705,7 +711,7 @@ export async function runCli(arguments_, dependencies) {
705
711
  options.unavailabilityReason !== undefined) {
706
712
  throw invalidArgument("Ready does not accept --unavailability-reason");
707
713
  }
708
- exitCode = await run((client) => client.PATCH("/v1/projects/{projectId}/specs/{specId}/state", {
714
+ exitCode = await run((client) => client.PATCH("/v1/workspaces/{workspaceId}/specs/{specId}/state", {
709
715
  body: options.state === "Ready"
710
716
  ? { state: "Ready" }
711
717
  : {
@@ -720,16 +726,16 @@ export async function runCli(arguments_, dependencies) {
720
726
  });
721
727
  spec
722
728
  .command("rollback")
723
- .requiredOption("--project-id <projectId>")
729
+ .requiredOption("--workspace-id <workspaceId>")
724
730
  .requiredOption("--spec-id <specId>")
725
731
  .requiredOption("--revision-id <revisionId>")
726
732
  .action(async (options) => {
727
- if (hasProjectReadToken(dependencies.environment)) {
733
+ if (hasWorkspaceReadToken(dependencies.environment)) {
728
734
  exitCode = rejectTokenWrite(dependencies);
729
735
  return;
730
736
  }
731
737
  const idempotencyKey = randomUUID();
732
- exitCode = await run((client) => client.POST("/v1/projects/{projectId}/specs/{specId}/rollback", {
738
+ exitCode = await run((client) => client.POST("/v1/workspaces/{workspaceId}/specs/{specId}/rollback", {
733
739
  body: { revisionId: options.revisionId },
734
740
  params: {
735
741
  header: { "idempotency-key": idempotencyKey },
@@ -742,127 +748,131 @@ export async function runCli(arguments_, dependencies) {
742
748
  });
743
749
  spec
744
750
  .command("delete")
745
- .requiredOption("--project-id <projectId>")
751
+ .requiredOption("--workspace-id <workspaceId>")
746
752
  .requiredOption("--spec-id <specId>")
747
753
  .requiredOption("--expected-version <expectedVersion>", "Opaque version returned by SpecFlow", parseOpaqueVersion)
748
754
  .action(async (options) => {
749
- if (hasProjectReadToken(dependencies.environment)) {
755
+ if (hasWorkspaceReadToken(dependencies.environment)) {
750
756
  exitCode = rejectTokenWrite(dependencies);
751
757
  return;
752
758
  }
753
- exitCode = await run(async (client) => withSuccessData(await client.DELETE("/v1/projects/{projectId}/specs/{specId}", {
759
+ exitCode = await run(async (client) => withSuccessData(await client.DELETE("/v1/workspaces/{workspaceId}/specs/{specId}", {
754
760
  params: {
755
761
  header: { "if-match": options.expectedVersion },
756
762
  path: options,
757
763
  },
758
764
  }), { deleted: { specId: options.specId } }));
759
765
  });
760
- const glossary = program.command("glossary");
761
- glossary
766
+ const workspaceGlossary = workspace.command("glossary");
767
+ workspaceGlossary
762
768
  .command("revision")
763
- .requiredOption("--project-id <projectId>")
769
+ .requiredOption("--workspace-id <workspaceId>")
764
770
  .requiredOption("--revision-id <revisionId>")
765
771
  .action(async (options) => {
766
- exitCode = await run((client) => client.GET("/v1/projects/{projectId}/glossary/revisions/{revisionId}", { params: { path: options } }));
772
+ exitCode = await run((client) => client.GET(hasWorkspaceReadToken(dependencies.environment) ? "/v1/workspaces/{workspaceId}/published/glossary/revisions/{revisionId}" : "/v1/workspaces/{workspaceId}/glossary/revisions/{revisionId}", { params: { path: options } }));
767
773
  });
768
- glossary
774
+ workspaceGlossary
769
775
  .command("read")
770
- .requiredOption("--project-id <projectId>")
776
+ .requiredOption("--workspace-id <workspaceId>")
771
777
  .action(async (options) => {
772
- exitCode = await run((client) => client.GET("/v1/projects/{projectId}/glossary", {
773
- params: { path: { projectId: options.projectId } },
774
- }), { rejectProjectReadToken: true });
778
+ exitCode = await run((client) => client.GET("/v1/workspaces/{workspaceId}/glossary", {
779
+ params: { path: { workspaceId: options.workspaceId } },
780
+ }), { rejectWorkspaceReadToken: true });
775
781
  });
776
- glossary
782
+ workspaceGlossary
777
783
  .command("changelog")
778
- .requiredOption("--project-id <projectId>")
784
+ .requiredOption("--workspace-id <workspaceId>")
779
785
  .action(async (options) => {
780
- exitCode = await run((client) => client.GET("/v1/projects/{projectId}/glossary/changelog", {
781
- params: { path: { projectId: options.projectId } },
782
- }), { rejectProjectReadToken: true });
786
+ exitCode = await run((client) => client.GET("/v1/workspaces/{workspaceId}/glossary/changelog", {
787
+ params: { path: { workspaceId: options.workspaceId } },
788
+ }), { rejectWorkspaceReadToken: true });
783
789
  });
784
- glossary
790
+ workspaceGlossary
785
791
  .command("draft")
786
- .requiredOption("--project-id <projectId>")
792
+ .requiredOption("--workspace-id <workspaceId>")
787
793
  .action(async (options) => {
788
- if (hasProjectReadToken(dependencies.environment)) {
794
+ if (hasWorkspaceReadToken(dependencies.environment)) {
789
795
  exitCode = rejectTokenWrite(dependencies);
790
796
  return;
791
797
  }
792
798
  const idempotencyKey = randomUUID();
793
- exitCode = await run((client) => client.POST("/v1/projects/{projectId}/glossary/draft-session", {
799
+ exitCode = await run((client) => client.POST("/v1/workspaces/{workspaceId}/glossary/draft-session", {
794
800
  params: {
795
801
  header: { "idempotency-key": idempotencyKey },
796
- path: { projectId: options.projectId },
802
+ path: { workspaceId: options.workspaceId },
797
803
  },
798
804
  }), {
799
805
  ambiguousOutcomeMessage: "Glossary Draft Session outcome is unknown; read the Glossary before retrying",
800
806
  retryTransportOnce: true,
801
807
  });
802
808
  });
803
- glossary
809
+ workspaceGlossary
804
810
  .command("edit")
805
- .requiredOption("--project-id <projectId>")
811
+ .requiredOption("--workspace-id <workspaceId>")
806
812
  .requiredOption("--entries <entries>")
807
813
  .option("--agent-revision <agentRevision>")
808
814
  .option("--agent-model <agentModel>")
809
815
  .option("--source-reference-ids <sourceReferenceIds>")
810
816
  .action(async (options) => {
811
- if (hasProjectReadToken(dependencies.environment)) {
817
+ if (hasWorkspaceReadToken(dependencies.environment)) {
812
818
  exitCode = rejectTokenWrite(dependencies);
813
819
  return;
814
820
  }
815
821
  const entries = parseGlossaryEntries(options.entries);
816
- exitCode = await run((client) => client.PUT("/v1/projects/{projectId}/glossary/draft", {
822
+ exitCode = await run((client) => client.PUT("/v1/workspaces/{workspaceId}/glossary/draft", {
817
823
  body: { entries, provenance: toAgentProvenance(options) },
818
- params: { path: { projectId: options.projectId } },
824
+ params: { path: { workspaceId: options.workspaceId } },
819
825
  }));
820
826
  });
821
- glossary
827
+ workspaceGlossary
822
828
  .command("publish")
823
- .requiredOption("--project-id <projectId>")
829
+ .requiredOption("--workspace-id <workspaceId>")
830
+ .option("--entry-ids <entryIds...>", "Publish only the selected term changes")
824
831
  .action(async (options) => {
825
- if (hasProjectReadToken(dependencies.environment)) {
832
+ if (hasWorkspaceReadToken(dependencies.environment)) {
826
833
  exitCode = rejectTokenWrite(dependencies);
827
834
  return;
828
835
  }
829
836
  const idempotencyKey = randomUUID();
830
- exitCode = await run((client) => client.POST("/v1/projects/{projectId}/glossary/publish", {
831
- body: {},
837
+ exitCode = await run((client) => client.POST("/v1/workspaces/{workspaceId}/glossary/publish", {
838
+ body: options.entryIds === undefined ? {} : { entryIds: options.entryIds },
832
839
  params: {
833
840
  header: { "idempotency-key": idempotencyKey },
834
- path: { projectId: options.projectId },
841
+ path: { workspaceId: options.workspaceId },
835
842
  },
836
843
  }), {
837
844
  ambiguousOutcomeMessage: "Glossary Publish outcome is unknown; inspect the Glossary Changelog before retrying",
838
845
  retryTransportOnce: true,
839
846
  });
840
847
  });
841
- glossary
848
+ workspaceGlossary
842
849
  .command("rollback")
843
- .requiredOption("--project-id <projectId>")
850
+ .requiredOption("--workspace-id <workspaceId>")
844
851
  .requiredOption("--revision-id <revisionId>")
845
852
  .action(async (options) => {
846
- if (hasProjectReadToken(dependencies.environment)) {
853
+ if (hasWorkspaceReadToken(dependencies.environment)) {
847
854
  exitCode = rejectTokenWrite(dependencies);
848
855
  return;
849
856
  }
850
857
  const idempotencyKey = randomUUID();
851
- exitCode = await run((client) => client.POST("/v1/projects/{projectId}/glossary/rollback", {
858
+ exitCode = await run((client) => client.POST("/v1/workspaces/{workspaceId}/glossary/rollback", {
852
859
  body: { revisionId: options.revisionId },
853
860
  params: {
854
861
  header: { "idempotency-key": idempotencyKey },
855
- path: { projectId: options.projectId },
862
+ path: { workspaceId: options.workspaceId },
856
863
  },
857
864
  }), {
858
865
  ambiguousOutcomeMessage: "Glossary Rollback outcome is unknown; inspect the Glossary Changelog before retrying",
859
866
  retryTransportOnce: true,
860
867
  });
861
868
  });
869
+ workspace.command("context").requiredOption("--workspace-id <workspaceId>").action(async (options) => {
870
+ exitCode = await run((client) => client.GET("/v1/workspaces/{workspaceId}/knowledge-context", { params: { path: { workspaceId: options.workspaceId } } }), { rejectWorkspaceReadToken: true });
871
+ });
862
872
  const proposal = program.command("proposal");
863
873
  proposal
864
874
  .command("create-spec")
865
- .requiredOption("--project-id <projectId>")
875
+ .requiredOption("--workspace-id <workspaceId>")
866
876
  .requiredOption("--spec-id <specId>")
867
877
  .requiredOption("--baseline-content <baselineContent>")
868
878
  .requiredOption("--proposed-content <proposedContent>")
@@ -870,12 +880,12 @@ export async function runCli(arguments_, dependencies) {
870
880
  .option("--agent-model <agentModel>")
871
881
  .option("--source-reference-ids <sourceReferenceIds>")
872
882
  .action(async (options) => {
873
- if (hasProjectReadToken(dependencies.environment)) {
883
+ if (hasWorkspaceReadToken(dependencies.environment)) {
874
884
  exitCode = rejectTokenWrite(dependencies);
875
885
  return;
876
886
  }
877
887
  const idempotencyKey = randomUUID();
878
- exitCode = await run((client) => client.POST("/v1/projects/{projectId}/draft-proposals", {
888
+ exitCode = await run((client) => client.POST("/v1/workspaces/{workspaceId}/draft-proposals", {
879
889
  body: {
880
890
  baselineContent: options.baselineContent,
881
891
  kind: "Spec",
@@ -885,7 +895,7 @@ export async function runCli(arguments_, dependencies) {
885
895
  },
886
896
  params: {
887
897
  header: { "idempotency-key": idempotencyKey },
888
- path: { projectId: options.projectId },
898
+ path: { workspaceId: options.workspaceId },
889
899
  },
890
900
  }), {
891
901
  ambiguousOutcomeMessage: "Draft Proposal Creation outcome is unknown; list open Proposals before retrying",
@@ -894,19 +904,19 @@ export async function runCli(arguments_, dependencies) {
894
904
  });
895
905
  proposal
896
906
  .command("create-glossary")
897
- .requiredOption("--project-id <projectId>")
907
+ .requiredOption("--workspace-id <workspaceId>")
898
908
  .requiredOption("--baseline-entries <baselineEntries>")
899
909
  .requiredOption("--proposed-entries <proposedEntries>")
900
910
  .option("--agent-revision <agentRevision>")
901
911
  .option("--agent-model <agentModel>")
902
912
  .option("--source-reference-ids <sourceReferenceIds>")
903
913
  .action(async (options) => {
904
- if (hasProjectReadToken(dependencies.environment)) {
914
+ if (hasWorkspaceReadToken(dependencies.environment)) {
905
915
  exitCode = rejectTokenWrite(dependencies);
906
916
  return;
907
917
  }
908
918
  const idempotencyKey = randomUUID();
909
- exitCode = await run((client) => client.POST("/v1/projects/{projectId}/draft-proposals", {
919
+ exitCode = await run((client) => client.POST("/v1/workspaces/{workspaceId}/draft-proposals", {
910
920
  body: {
911
921
  baselineEntries: parseGlossaryEntries(options.baselineEntries),
912
922
  kind: "Glossary",
@@ -915,7 +925,7 @@ export async function runCli(arguments_, dependencies) {
915
925
  },
916
926
  params: {
917
927
  header: { "idempotency-key": idempotencyKey },
918
- path: { projectId: options.projectId },
928
+ path: { workspaceId: options.workspaceId },
919
929
  },
920
930
  }), {
921
931
  ambiguousOutcomeMessage: "Draft Proposal Creation outcome is unknown; list open Proposals before retrying",
@@ -924,7 +934,7 @@ export async function runCli(arguments_, dependencies) {
924
934
  });
925
935
  proposal
926
936
  .command("list")
927
- .requiredOption("--project-id <projectId>")
937
+ .requiredOption("--workspace-id <workspaceId>")
928
938
  .option("--target-kind <targetKind>", "Spec or Glossary", parseProposalTargetKind)
929
939
  .option("--spec-id <specId>")
930
940
  .option("--status <status>", "Open, Applied, or Discarded", parseProposalStatus)
@@ -933,9 +943,9 @@ export async function runCli(arguments_, dependencies) {
933
943
  (options.targetKind !== "Spec" && options.specId !== undefined)) {
934
944
  throw invalidArgument("--spec-id requires --target-kind Spec");
935
945
  }
936
- exitCode = await run((client) => client.GET("/v1/projects/{projectId}/draft-proposals", {
946
+ exitCode = await run((client) => client.GET("/v1/workspaces/{workspaceId}/draft-proposals", {
937
947
  params: {
938
- path: { projectId: options.projectId },
948
+ path: { workspaceId: options.workspaceId },
939
949
  query: {
940
950
  ...(options.specId === undefined ? {} : { specId: options.specId }),
941
951
  ...(options.status === undefined ? {} : { status: options.status }),
@@ -948,16 +958,16 @@ export async function runCli(arguments_, dependencies) {
948
958
  });
949
959
  proposal
950
960
  .command("apply")
951
- .requiredOption("--project-id <projectId>")
961
+ .requiredOption("--workspace-id <workspaceId>")
952
962
  .requiredOption("--proposal-id <proposalId>")
953
963
  .option("--change-ids <changeIds>")
954
964
  .action(async (options) => {
955
- if (hasProjectReadToken(dependencies.environment)) {
965
+ if (hasWorkspaceReadToken(dependencies.environment)) {
956
966
  exitCode = rejectTokenWrite(dependencies);
957
967
  return;
958
968
  }
959
969
  const idempotencyKey = randomUUID();
960
- exitCode = await run((client) => client.POST("/v1/projects/{projectId}/draft-proposals/{proposalId}/apply", {
970
+ exitCode = await run((client) => client.POST("/v1/workspaces/{workspaceId}/draft-proposals/{proposalId}/apply", {
961
971
  body: {
962
972
  changeIds: options.changeIds === undefined
963
973
  ? null
@@ -966,7 +976,7 @@ export async function runCli(arguments_, dependencies) {
966
976
  params: {
967
977
  header: { "idempotency-key": idempotencyKey },
968
978
  path: {
969
- projectId: options.projectId,
979
+ workspaceId: options.workspaceId,
970
980
  proposalId: options.proposalId,
971
981
  },
972
982
  },
@@ -977,19 +987,19 @@ export async function runCli(arguments_, dependencies) {
977
987
  });
978
988
  proposal
979
989
  .command("discard")
980
- .requiredOption("--project-id <projectId>")
990
+ .requiredOption("--workspace-id <workspaceId>")
981
991
  .requiredOption("--proposal-id <proposalId>")
982
992
  .action(async (options) => {
983
- if (hasProjectReadToken(dependencies.environment)) {
993
+ if (hasWorkspaceReadToken(dependencies.environment)) {
984
994
  exitCode = rejectTokenWrite(dependencies);
985
995
  return;
986
996
  }
987
997
  const idempotencyKey = randomUUID();
988
- exitCode = await run((client) => client.POST("/v1/projects/{projectId}/draft-proposals/{proposalId}/discard", {
998
+ exitCode = await run((client) => client.POST("/v1/workspaces/{workspaceId}/draft-proposals/{proposalId}/discard", {
989
999
  params: {
990
1000
  header: { "idempotency-key": idempotencyKey },
991
1001
  path: {
992
- projectId: options.projectId,
1002
+ workspaceId: options.workspaceId,
993
1003
  proposalId: options.proposalId,
994
1004
  },
995
1005
  },
@@ -1000,32 +1010,30 @@ export async function runCli(arguments_, dependencies) {
1000
1010
  });
1001
1011
  program
1002
1012
  .command("authoring-history")
1003
- .requiredOption("--project-id <projectId>")
1013
+ .requiredOption("--workspace-id <workspaceId>")
1004
1014
  .action(async (options) => {
1005
- exitCode = await run((client) => client.GET("/v1/projects/{projectId}/authoring-history", {
1006
- params: { path: { projectId: options.projectId } },
1007
- }));
1015
+ exitCode = await run((client) => client.GET("/v1/workspaces/{workspaceId}/authoring-history", { params: { path: options } }));
1008
1016
  });
1009
1017
  const capability = program.command("capability");
1010
1018
  capability
1011
1019
  .command("create")
1012
- .requiredOption("--project-id <projectId>")
1020
+ .requiredOption("--workspace-id <workspaceId>")
1013
1021
  .requiredOption("--name <name>")
1014
1022
  .option("--parent-id <parentId>")
1015
1023
  .action(async (options) => {
1016
- if (hasProjectReadToken(dependencies.environment)) {
1024
+ if (hasWorkspaceReadToken(dependencies.environment)) {
1017
1025
  exitCode = rejectTokenWrite(dependencies);
1018
1026
  return;
1019
1027
  }
1020
1028
  const idempotencyKey = randomUUID();
1021
- exitCode = await run((client) => client.POST("/v1/projects/{projectId}/capabilities", {
1029
+ exitCode = await run((client) => client.POST("/v1/workspaces/{workspaceId}/capabilities", {
1022
1030
  body: {
1023
1031
  name: options.name,
1024
1032
  parentId: options.parentId ?? null,
1025
1033
  },
1026
1034
  params: {
1027
1035
  header: { "idempotency-key": idempotencyKey },
1028
- path: { projectId: options.projectId },
1036
+ path: { workspaceId: options.workspaceId },
1029
1037
  },
1030
1038
  }), {
1031
1039
  ambiguousOutcomeMessage: "Capability Creation outcome is unknown; inspect the Project directory before retrying",
@@ -1034,14 +1042,14 @@ export async function runCli(arguments_, dependencies) {
1034
1042
  });
1035
1043
  capability
1036
1044
  .command("update")
1037
- .requiredOption("--project-id <projectId>")
1045
+ .requiredOption("--workspace-id <workspaceId>")
1038
1046
  .requiredOption("--capability-id <capabilityId>")
1039
1047
  .requiredOption("--expected-version <expectedVersion>", "Opaque version returned by SpecFlow", parseOpaqueVersion)
1040
1048
  .option("--name <name>")
1041
1049
  .option("--parent-id <parentId>")
1042
1050
  .option("--move-to-root")
1043
1051
  .action(async (options) => {
1044
- if (hasProjectReadToken(dependencies.environment)) {
1052
+ if (hasWorkspaceReadToken(dependencies.environment)) {
1045
1053
  exitCode = rejectTokenWrite(dependencies);
1046
1054
  return;
1047
1055
  }
@@ -1053,7 +1061,7 @@ export async function runCli(arguments_, dependencies) {
1053
1061
  options.moveToRoot !== true) {
1054
1062
  throw invalidArgument("Capability update requires a change");
1055
1063
  }
1056
- exitCode = await run((client) => client.PATCH("/v1/projects/{projectId}/capabilities/{capabilityId}", {
1064
+ exitCode = await run((client) => client.PATCH("/v1/workspaces/{workspaceId}/capabilities/{capabilityId}", {
1057
1065
  body: {
1058
1066
  ...(options.name === undefined ? {} : { name: options.name }),
1059
1067
  ...(options.moveToRoot === true
@@ -1071,71 +1079,60 @@ export async function runCli(arguments_, dependencies) {
1071
1079
  capability
1072
1080
  .command("delete")
1073
1081
  .option("--idempotency-key <key>", "Reuse this key when retrying the same deletion")
1074
- .requiredOption("--project-id <projectId>")
1082
+ .requiredOption("--workspace-id <workspaceId>")
1075
1083
  .requiredOption("--capability-id <capabilityId>")
1076
1084
  .requiredOption("--expected-version <expectedVersion>", "Opaque version returned by SpecFlow", parseOpaqueVersion)
1077
1085
  .action(async (options) => {
1078
- if (hasProjectReadToken(dependencies.environment)) {
1086
+ if (hasWorkspaceReadToken(dependencies.environment)) {
1079
1087
  exitCode = rejectTokenWrite(dependencies);
1080
1088
  return;
1081
1089
  }
1082
1090
  const idempotencyKey = options.idempotencyKey ?? randomUUID();
1083
- exitCode = await run(async (client) => withSuccessData(await client.DELETE("/v1/projects/{projectId}/capabilities/{capabilityId}", {
1091
+ exitCode = await run(async (client) => withSuccessData(await client.DELETE("/v1/workspaces/{workspaceId}/capabilities/{capabilityId}", {
1084
1092
  params: {
1085
1093
  header: { "if-match": options.expectedVersion, "idempotency-key": idempotencyKey },
1086
1094
  path: options,
1087
1095
  },
1088
1096
  }), { deleted: { capabilityId: options.capabilityId } }));
1089
1097
  });
1090
- const finding = program.command("finding");
1091
- finding
1098
+ const workspaceFinding = workspace.command("finding");
1099
+ workspaceFinding
1092
1100
  .command("list")
1093
- .requiredOption("--project-id <projectId>")
1094
- .requiredOption("--spec-id <specId>")
1101
+ .requiredOption("--workspace-id <workspaceId>")
1102
+ .option("--spec-id <specId>")
1095
1103
  .option("--status <status>", "Finding status", parseFindingStatus)
1096
1104
  .option("--limit <limit>", "Maximum results", parseSearchLimit, 20)
1097
1105
  .option("--cursor <cursor>")
1098
1106
  .action(async (options) => {
1099
- exitCode = await run((client) => client.GET("/v1/projects/{projectId}/specs/{specId}/findings", {
1100
- params: {
1101
- path: { projectId: options.projectId, specId: options.specId },
1102
- query: {
1103
- ...(options.cursor === undefined
1104
- ? {}
1105
- : { cursor: options.cursor }),
1106
- limit: options.limit,
1107
- ...(options.status === undefined
1108
- ? {}
1109
- : { status: options.status }),
1110
- },
1111
- },
1112
- }), { rejectProjectReadToken: true });
1107
+ exitCode = await run(client => options.specId === undefined
1108
+ ? client.GET("/v1/workspaces/{workspaceId}/findings", { params: { path: { workspaceId: options.workspaceId }, query: { limit: options.limit, ...(options.cursor === undefined ? {} : { cursor: options.cursor }), ...(options.status === undefined ? {} : { status: options.status }) } } })
1109
+ : client.GET("/v1/workspaces/{workspaceId}/specs/{specId}/findings", { params: { path: { workspaceId: options.workspaceId, specId: options.specId }, query: { limit: options.limit, ...(options.cursor === undefined ? {} : { cursor: options.cursor }), ...(options.status === undefined ? {} : { status: options.status }) } } }), { rejectWorkspaceReadToken: true });
1113
1110
  });
1114
- finding
1111
+ workspaceFinding
1115
1112
  .command("read")
1116
- .requiredOption("--project-id <projectId>")
1113
+ .requiredOption("--workspace-id <workspaceId>")
1117
1114
  .requiredOption("--finding-id <findingId>")
1118
1115
  .action(async (options) => {
1119
- exitCode = await run((client) => client.GET("/v1/projects/{projectId}/findings/{findingId}", {
1116
+ exitCode = await run((client) => client.GET("/v1/workspaces/{workspaceId}/findings/{findingId}", {
1120
1117
  params: { path: options },
1121
- }), { rejectProjectReadToken: true });
1118
+ }), { rejectWorkspaceReadToken: true });
1122
1119
  });
1123
- finding
1120
+ workspaceFinding
1124
1121
  .command("create")
1125
- .requiredOption("--project-id <projectId>")
1122
+ .requiredOption("--workspace-id <workspaceId>")
1126
1123
  .requiredOption("--input <input>")
1127
1124
  .action(async (options) => {
1128
- if (hasProjectReadToken(dependencies.environment)) {
1125
+ if (hasWorkspaceReadToken(dependencies.environment)) {
1129
1126
  exitCode = rejectTokenWrite(dependencies);
1130
1127
  return;
1131
1128
  }
1132
1129
  const idempotencyKey = randomUUID();
1133
1130
  const input = parseFindingInput(options.input);
1134
- exitCode = await run((client) => client.POST("/v1/projects/{projectId}/findings", {
1131
+ exitCode = await run((client) => client.POST("/v1/workspaces/{workspaceId}/findings", {
1135
1132
  body: input,
1136
1133
  params: {
1137
1134
  header: { "idempotency-key": idempotencyKey },
1138
- path: { projectId: options.projectId },
1135
+ path: { workspaceId: options.workspaceId },
1139
1136
  },
1140
1137
  }), {
1141
1138
  ambiguousOutcomeMessage: "Finding Creation outcome is unknown; inspect the Finding list before retrying",
@@ -1147,18 +1144,18 @@ export async function runCli(arguments_, dependencies) {
1147
1144
  ["dismiss", "Dismissed"],
1148
1145
  ["resolve", "Resolved"],
1149
1146
  ]) {
1150
- finding
1147
+ workspaceFinding
1151
1148
  .command(commandName)
1152
- .requiredOption("--project-id <projectId>")
1149
+ .requiredOption("--workspace-id <workspaceId>")
1153
1150
  .requiredOption("--finding-id <findingId>")
1154
1151
  .requiredOption("--expected-version <expectedVersion>", "Opaque version returned by SpecFlow", parseOpaqueVersion)
1155
1152
  .action(async (options) => {
1156
- if (hasProjectReadToken(dependencies.environment)) {
1153
+ if (hasWorkspaceReadToken(dependencies.environment)) {
1157
1154
  exitCode = rejectTokenWrite(dependencies);
1158
1155
  return;
1159
1156
  }
1160
1157
  const idempotencyKey = randomUUID();
1161
- exitCode = await run((client) => client.PATCH("/v1/projects/{projectId}/findings/{findingId}/status", {
1158
+ exitCode = await run((client) => client.PATCH("/v1/workspaces/{workspaceId}/findings/{findingId}/status", {
1162
1159
  body: { status },
1163
1160
  params: {
1164
1161
  header: {
@@ -1173,10 +1170,39 @@ export async function runCli(arguments_, dependencies) {
1173
1170
  });
1174
1171
  });
1175
1172
  }
1173
+ workspaceFinding.command("handler").requiredOption("--workspace-id <workspaceId>").requiredOption("--finding-id <findingId>").option("--user-id <userId>", "Workspace member; omit to clear the handler").requiredOption("--expected-version <expectedVersion>", "Opaque version", parseOpaqueVersion).requiredOption("--idempotency-key <key>").action(async (options) => {
1174
+ exitCode = await run(client => client.PATCH("/v1/workspaces/{workspaceId}/findings/{findingId}/handler", { body: { handlerUserId: options.userId ?? null }, params: { path: options, header: { "idempotency-key": options.idempotencyKey, "if-match": options.expectedVersion } } }), { rejectWorkspaceReadToken: true });
1175
+ });
1176
+ workspaceFinding.command("follow-up").requiredOption("--workspace-id <workspaceId>").requiredOption("--finding-id <findingId>").requiredOption("--project-id <projectId>").requiredOption("--idempotency-key <key>").action(async (options) => {
1177
+ exitCode = await run(client => client.POST("/v1/workspaces/{workspaceId}/findings/{findingId}/work-item", { body: { projectId: options.projectId }, params: { path: { workspaceId: options.workspaceId, findingId: options.findingId }, header: { "idempotency-key": options.idempotencyKey } } }), { rejectWorkspaceReadToken: true });
1178
+ });
1179
+ workspaceFinding.command("discussion").requiredOption("--workspace-id <workspaceId>").requiredOption("--finding-id <findingId>").action(async (options) => {
1180
+ exitCode = await run(client => client.GET("/v1/workspaces/{workspaceId}/findings/{findingId}/discussion", { params: { path: options } }), { rejectWorkspaceReadToken: true });
1181
+ });
1182
+ workspaceFinding.command("comment").requiredOption("--workspace-id <workspaceId>").requiredOption("--finding-id <findingId>").requiredOption("--body <body>").option("--parent-id <parentId>").requiredOption("--idempotency-key <key>").action(async (options) => {
1183
+ exitCode = await run(client => client.POST("/v1/workspaces/{workspaceId}/findings/{findingId}/discussion", { body: { body: options.body, parentId: options.parentId ?? null }, params: { path: options, header: { "idempotency-key": options.idempotencyKey } } }), { rejectWorkspaceReadToken: true });
1184
+ });
1185
+ const workspaceReview = workspace.command("review");
1186
+ workspaceReview.command("request").requiredOption("--workspace-id <workspaceId>").requiredOption("--spec-id <specId>").option("--idempotency-key <key>").action(async (options) => {
1187
+ exitCode = await run(client => client.POST("/v1/workspaces/{workspaceId}/specs/{specId}/reviews", { params: { path: options, header: { "idempotency-key": options.idempotencyKey ?? randomUUID() } } }), { rejectWorkspaceReadToken: true });
1188
+ });
1189
+ workspaceReview.command("history").requiredOption("--workspace-id <workspaceId>").action(async (options) => {
1190
+ exitCode = await run(client => client.GET("/v1/workspaces/{workspaceId}/reviews", { params: { path: options } }), { rejectWorkspaceReadToken: true });
1191
+ });
1192
+ workspaceReview.command("read").requiredOption("--workspace-id <workspaceId>").requiredOption("--review-id <reviewId>").action(async (options) => {
1193
+ exitCode = await run(client => client.GET("/v1/workspaces/{workspaceId}/reviews/{reviewId}", { params: { path: options } }), { rejectWorkspaceReadToken: true });
1194
+ });
1195
+ workspaceReview.command("plan").requiredOption("--workspace-id <workspaceId>").action(async (options) => {
1196
+ exitCode = await run(client => client.GET("/v1/workspaces/{workspaceId}/review-schedule", { params: { path: options } }), { rejectWorkspaceReadToken: true });
1197
+ });
1198
+ workspaceReview.command("configure").requiredOption("--workspace-id <workspaceId>").requiredOption("--expected-version <expectedVersion>", "Opaque version", parseOpaqueVersion).requiredOption("--input <input>", "Review plan JSON").action(async (options) => {
1199
+ const body = reviewPlanInputSchema.parse(JSON.parse(options.input));
1200
+ exitCode = await run(client => client.PUT("/v1/workspaces/{workspaceId}/review-schedule", { body, params: { path: options, header: { "if-match": options.expectedVersion } } }), { rejectWorkspaceReadToken: true });
1201
+ });
1176
1202
  const notification = program.command("notification").description("Read and manage your personal task notifications");
1177
1203
  notification.command("list").action(async () => {
1178
- if (hasProjectReadToken(dependencies.environment)) {
1179
- exitCode = rejectProjectReadTokenOperation(dependencies);
1204
+ if (hasWorkspaceReadToken(dependencies.environment)) {
1205
+ exitCode = rejectWorkspaceReadTokenOperation(dependencies);
1180
1206
  return;
1181
1207
  }
1182
1208
  exitCode = await run((client) => client.GET("/v1/notifications"));
@@ -1191,8 +1217,8 @@ export async function runCli(arguments_, dependencies) {
1191
1217
  throw invalidArgument("Notification target must be task or workspace"); return value; })
1192
1218
  .requiredOption("--idempotency-key <key>", "Reuse the same command key and inputs when retrying", parseNotificationUuid)
1193
1219
  .action(async (options) => {
1194
- if (hasProjectReadToken(dependencies.environment)) {
1195
- exitCode = rejectProjectReadTokenOperation(dependencies);
1220
+ if (hasWorkspaceReadToken(dependencies.environment)) {
1221
+ exitCode = rejectWorkspaceReadTokenOperation(dependencies);
1196
1222
  return;
1197
1223
  }
1198
1224
  if ((options.relatedEntityId === undefined) !== (options.relatedEntityType === undefined))
@@ -1203,8 +1229,8 @@ export async function runCli(arguments_, dependencies) {
1203
1229
  .requiredOption("--notification-id <id>", "Notification UUID returned by the list", parseNotificationUuid)
1204
1230
  .requiredOption("--idempotency-key <key>", "Reuse the same key when retrying", parseNotificationUuid)
1205
1231
  .action(async (options) => {
1206
- if (hasProjectReadToken(dependencies.environment)) {
1207
- exitCode = rejectProjectReadTokenOperation(dependencies);
1232
+ if (hasWorkspaceReadToken(dependencies.environment)) {
1233
+ exitCode = rejectWorkspaceReadTokenOperation(dependencies);
1208
1234
  return;
1209
1235
  }
1210
1236
  exitCode = await run((client) => client.PATCH("/v1/notifications/{notificationId}/read", { params: { path: { notificationId: options.notificationId }, header: { "idempotency-key": options.idempotencyKey } } }), { retryTransportOnce: true, ambiguousOutcomeMessage: "Notification read outcome is unknown; retry with the same notification and command key" });
@@ -1212,8 +1238,8 @@ export async function runCli(arguments_, dependencies) {
1212
1238
  for (const action of ["read-all", "clear"]) {
1213
1239
  notification.command(action).requiredOption("--idempotency-key <key>", "Reuse the same key to avoid changing notifications that arrived after the first command", parseNotificationUuid)
1214
1240
  .action(async (options) => {
1215
- if (hasProjectReadToken(dependencies.environment)) {
1216
- exitCode = rejectProjectReadTokenOperation(dependencies);
1241
+ if (hasWorkspaceReadToken(dependencies.environment)) {
1242
+ exitCode = rejectWorkspaceReadTokenOperation(dependencies);
1217
1243
  return;
1218
1244
  }
1219
1245
  const params = { header: { "idempotency-key": options.idempotencyKey } };
@@ -1222,8 +1248,8 @@ export async function runCli(arguments_, dependencies) {
1222
1248
  }
1223
1249
  const notificationPreferences = notification.command("preferences");
1224
1250
  notificationPreferences.command("get").action(async () => {
1225
- if (hasProjectReadToken(dependencies.environment)) {
1226
- exitCode = rejectProjectReadTokenOperation(dependencies);
1251
+ if (hasWorkspaceReadToken(dependencies.environment)) {
1252
+ exitCode = rejectWorkspaceReadTokenOperation(dependencies);
1227
1253
  return;
1228
1254
  }
1229
1255
  exitCode = await run((client) => client.GET("/v1/notification-preferences"));
@@ -1233,15 +1259,16 @@ export async function runCli(arguments_, dependencies) {
1233
1259
  .requiredOption("--preferences <json>", "JSON category settings; omitted settings remain unchanged", parseNotificationPreferences)
1234
1260
  .requiredOption("--idempotency-key <key>", "Reuse the same key and settings when retrying", parseNotificationUuid)
1235
1261
  .action(async (options) => {
1236
- if (hasProjectReadToken(dependencies.environment)) {
1237
- exitCode = rejectProjectReadTokenOperation(dependencies);
1262
+ if (hasWorkspaceReadToken(dependencies.environment)) {
1263
+ exitCode = rejectWorkspaceReadTokenOperation(dependencies);
1238
1264
  return;
1239
1265
  }
1240
1266
  exitCode = await run((client) => client.PATCH("/v1/notification-preferences", { body: options.preferences, params: { header: { "idempotency-key": options.idempotencyKey, "if-match": options.expectedVersion } } }), { retryTransportOnce: true, ambiguousOutcomeMessage: "Notification preference outcome is unknown; retry with the same settings and command key" });
1241
1267
  });
1242
1268
  const workItem = program.command("work-item");
1243
1269
  registerWorkItemAssetCommands(workItem, { dependencies, run, setExitCode: (value) => { exitCode = value; }, rejectWrite: () => rejectTokenWrite(dependencies) });
1244
- registerWorkItemResourceLinkCommands(workItem, spec, { run, setExitCode: (value) => { exitCode = value; }, canWrite: () => !hasProjectReadToken(dependencies.environment), rejectWrite: () => rejectTokenWrite(dependencies), parseOpaqueVersion, parseLimit: parseSearchLimit });
1270
+ registerProductMapCommands(program, { run, setExitCode: value => { exitCode = value; }, canWrite: () => !hasWorkspaceReadToken(dependencies.environment), rejectWrite: () => rejectTokenWrite(dependencies), parseOpaqueVersion });
1271
+ registerWorkItemResourceLinkCommands(workItem, spec, { run, setExitCode: (value) => { exitCode = value; }, canWrite: () => !hasWorkspaceReadToken(dependencies.environment), rejectWrite: () => rejectTokenWrite(dependencies), parseOpaqueVersion, parseLimit: parseSearchLimit });
1245
1272
  const timeEntry = workItem.command("time-entry").description("Record and read task time entries");
1246
1273
  timeEntry.command("list")
1247
1274
  .requiredOption("--project-id <projectId>")
@@ -1299,7 +1326,7 @@ export async function runCli(arguments_, dependencies) {
1299
1326
  throw invalidArgument("Time Entry ID must be a UUID");
1300
1327
  } }).requiredOption("--expected-version <version>", "Opaque version returned by SpecFlow", parseOpaqueVersion);
1301
1328
  command.action(async (options) => {
1302
- if (hasProjectReadToken(dependencies.environment)) {
1329
+ if (hasWorkspaceReadToken(dependencies.environment)) {
1303
1330
  exitCode = rejectTokenWrite(dependencies);
1304
1331
  return;
1305
1332
  }
@@ -1344,7 +1371,7 @@ export async function runCli(arguments_, dependencies) {
1344
1371
  .requiredOption("--content <markdown>", "Markdown comment body, up to 20000 characters", parseCommentContent)
1345
1372
  .requiredOption("--idempotency-key <key>", "Reuse the same key and inputs when retrying", parseCommentCommandKey)
1346
1373
  .action(async (options) => {
1347
- if (hasProjectReadToken(dependencies.environment)) {
1374
+ if (hasWorkspaceReadToken(dependencies.environment)) {
1348
1375
  exitCode = rejectTokenWrite(dependencies);
1349
1376
  return;
1350
1377
  }
@@ -1361,7 +1388,7 @@ export async function runCli(arguments_, dependencies) {
1361
1388
  .requiredOption("--expected-version <version>", "Opaque comment version returned by SpecFlow", parseOpaqueVersion)
1362
1389
  .requiredOption("--idempotency-key <key>", "Reuse the same key and inputs when retrying", parseCommentCommandKey)
1363
1390
  .action(async (options) => {
1364
- if (hasProjectReadToken(dependencies.environment)) {
1391
+ if (hasWorkspaceReadToken(dependencies.environment)) {
1365
1392
  exitCode = rejectTokenWrite(dependencies);
1366
1393
  return;
1367
1394
  }
@@ -1378,7 +1405,7 @@ export async function runCli(arguments_, dependencies) {
1378
1405
  .requiredOption("--expected-version <version>", "Opaque comment version returned by SpecFlow", parseOpaqueVersion)
1379
1406
  .requiredOption("--idempotency-key <key>", "Reuse the same key and inputs when retrying", parseCommentCommandKey)
1380
1407
  .action(async (options) => {
1381
- if (hasProjectReadToken(dependencies.environment)) {
1408
+ if (hasWorkspaceReadToken(dependencies.environment)) {
1382
1409
  exitCode = rejectTokenWrite(dependencies);
1383
1410
  return;
1384
1411
  }
@@ -1407,7 +1434,7 @@ export async function runCli(arguments_, dependencies) {
1407
1434
  })
1408
1435
  .requiredOption("--idempotency-key <key>", "Reuse the same key and inputs when retrying", parseRelationCommandKey)
1409
1436
  .action(async (options) => {
1410
- if (hasProjectReadToken(dependencies.environment)) {
1437
+ if (hasWorkspaceReadToken(dependencies.environment)) {
1411
1438
  exitCode = rejectTokenWrite(dependencies);
1412
1439
  return;
1413
1440
  }
@@ -1428,7 +1455,7 @@ export async function runCli(arguments_, dependencies) {
1428
1455
  .requiredOption("--expected-version <expectedVersion>", "Opaque relation version returned by SpecFlow", parseOpaqueVersion)
1429
1456
  .requiredOption("--idempotency-key <key>", "Reuse the same key and inputs when retrying", parseRelationCommandKey)
1430
1457
  .action(async (options) => {
1431
- if (hasProjectReadToken(dependencies.environment)) {
1458
+ if (hasWorkspaceReadToken(dependencies.environment)) {
1432
1459
  exitCode = rejectTokenWrite(dependencies);
1433
1460
  return;
1434
1461
  }
@@ -1494,7 +1521,7 @@ export async function runCli(arguments_, dependencies) {
1494
1521
  .option("--due-date <dueDate>", "ISO 8601 date", parseWorkItemDate)
1495
1522
  .option("--status <status>", "Workflow Column slug or virtual status to create the Work Item in")
1496
1523
  .action(async (options) => {
1497
- if (hasProjectReadToken(dependencies.environment)) {
1524
+ if (hasWorkspaceReadToken(dependencies.environment)) {
1498
1525
  exitCode = rejectTokenWrite(dependencies);
1499
1526
  return;
1500
1527
  }
@@ -1550,7 +1577,7 @@ export async function runCli(arguments_, dependencies) {
1550
1577
  .option("--clear-start-date", "Remove the Work Item start date")
1551
1578
  .option("--clear-due-date", "Remove the Work Item due date")
1552
1579
  .action(async (options) => {
1553
- if (hasProjectReadToken(dependencies.environment)) {
1580
+ if (hasWorkspaceReadToken(dependencies.environment)) {
1554
1581
  exitCode = rejectTokenWrite(dependencies);
1555
1582
  return;
1556
1583
  }
@@ -1622,7 +1649,7 @@ export async function runCli(arguments_, dependencies) {
1622
1649
  .option("--assignee <assignee>", "Workspace member User ID")
1623
1650
  .option("--unassign", "Remove the Work Item assignee")
1624
1651
  .action(async (options) => {
1625
- if (hasProjectReadToken(dependencies.environment)) {
1652
+ if (hasWorkspaceReadToken(dependencies.environment)) {
1626
1653
  exitCode = rejectTokenWrite(dependencies);
1627
1654
  return;
1628
1655
  }
@@ -1658,7 +1685,7 @@ export async function runCli(arguments_, dependencies) {
1658
1685
  .requiredOption("--work-item-id <workItemId>")
1659
1686
  .requiredOption("--expected-version <expectedVersion>", "Opaque version returned by SpecFlow", parseOpaqueVersion)
1660
1687
  .action(async (options) => {
1661
- if (hasProjectReadToken(dependencies.environment)) {
1688
+ if (hasWorkspaceReadToken(dependencies.environment)) {
1662
1689
  exitCode = rejectTokenWrite(dependencies);
1663
1690
  return;
1664
1691
  }
@@ -1693,7 +1720,7 @@ export async function runCli(arguments_, dependencies) {
1693
1720
  .command("bulk")
1694
1721
  .requiredOption("--project-id <projectId>")
1695
1722
  .requiredOption("--action <action>", `Bulk action: ${workItemBulkActions.join(", ")}`, parseWorkItemBulkAction)
1696
- .requiredOption("--target <id@version>", "Target Work Item ID with its observed version, e.g. PAY-1@3; repeat for several Projects in the same Workspace", parseWorkItemBulkTarget, [])
1723
+ .requiredOption("--target <[projectId/]id@version>", "Target with its observed version, e.g. PAY-1@3 or project-id/PAY-2@4; repeat within the same Workspace", parseWorkItemBulkTarget, [])
1697
1724
  .option("--status <status>", "Destination Workflow Column slug or virtual status (updateStatus)")
1698
1725
  .option("--priority <priority>", "Priority (updatePriority)", parseWorkItemPriority)
1699
1726
  .option("--assignee <assignee>", "Assignee User ID (updateAssignee)")
@@ -1703,7 +1730,7 @@ export async function runCli(arguments_, dependencies) {
1703
1730
  .option("--clear-due-date", "Clear the due date (updateDueDate)")
1704
1731
  .description("Apply one action to several Work Items, which may span Projects inside the same Workspace")
1705
1732
  .action(async (options) => {
1706
- if (hasProjectReadToken(dependencies.environment)) {
1733
+ if (hasWorkspaceReadToken(dependencies.environment)) {
1707
1734
  exitCode = rejectTokenWrite(dependencies);
1708
1735
  return;
1709
1736
  }
@@ -1758,6 +1785,7 @@ export async function runCli(arguments_, dependencies) {
1758
1785
  : {}),
1759
1786
  workItems: options.target.map((target) => ({
1760
1787
  id: target.id,
1788
+ ...(target.projectId === undefined ? {} : { projectId: target.projectId }),
1761
1789
  version: target.expectedVersion,
1762
1790
  })),
1763
1791
  },
@@ -1777,7 +1805,7 @@ export async function runCli(arguments_, dependencies) {
1777
1805
  .requiredOption("--file <path>", "JSON task file, at most 1 MiB and 1000 tasks")
1778
1806
  .requiredOption("--idempotency-key <key>", "Reuse this key with the same file when retrying")
1779
1807
  .action(async (options) => {
1780
- if (hasProjectReadToken(dependencies.environment)) {
1808
+ if (hasWorkspaceReadToken(dependencies.environment)) {
1781
1809
  exitCode = rejectTokenWrite(dependencies);
1782
1810
  return;
1783
1811
  }
@@ -1832,7 +1860,7 @@ export async function runCli(arguments_, dependencies) {
1832
1860
  .requiredOption("--expected-version <expectedVersion>", "Opaque version returned by SpecFlow", parseOpaqueVersion)
1833
1861
  .option("--destination-status <status>", "Destination Workflow Column slug; defaults to the matching or first column")
1834
1862
  .action(async (options) => {
1835
- if (hasProjectReadToken(dependencies.environment)) {
1863
+ if (hasWorkspaceReadToken(dependencies.environment)) {
1836
1864
  exitCode = rejectTokenWrite(dependencies);
1837
1865
  return;
1838
1866
  }
@@ -1859,7 +1887,7 @@ export async function runCli(arguments_, dependencies) {
1859
1887
  .requiredOption("--status <status>", "Destination Workflow Column slug or virtual status")
1860
1888
  .option("--position <position>", "Board index inside the destination column", parseBoardPosition)
1861
1889
  .action(async (options) => {
1862
- if (hasProjectReadToken(dependencies.environment)) {
1890
+ if (hasWorkspaceReadToken(dependencies.environment)) {
1863
1891
  exitCode = rejectTokenWrite(dependencies);
1864
1892
  return;
1865
1893
  }
@@ -1893,7 +1921,7 @@ export async function runCli(arguments_, dependencies) {
1893
1921
  .requiredOption("--expected-version <expectedVersion>", "Opaque version returned by SpecFlow", parseOpaqueVersion)
1894
1922
  .description("Move a Work Item into the Project's first final Workflow Column")
1895
1923
  .action(async (options) => {
1896
- if (hasProjectReadToken(dependencies.environment)) {
1924
+ if (hasWorkspaceReadToken(dependencies.environment)) {
1897
1925
  exitCode = rejectTokenWrite(dependencies);
1898
1926
  return;
1899
1927
  }
@@ -1964,7 +1992,7 @@ export async function runCli(arguments_, dependencies) {
1964
1992
  .requiredOption("--color <color>")
1965
1993
  .option("--work-item-id <workItemId>", "Create the label directly on the named Work Item instead of the Workspace definition")
1966
1994
  .action(async (options) => {
1967
- if (hasProjectReadToken(dependencies.environment)) {
1995
+ if (hasWorkspaceReadToken(dependencies.environment)) {
1968
1996
  exitCode = rejectTokenWrite(dependencies);
1969
1997
  return;
1970
1998
  }
@@ -1994,7 +2022,7 @@ export async function runCli(arguments_, dependencies) {
1994
2022
  .option("--name <name>")
1995
2023
  .option("--color <color>")
1996
2024
  .action(async (options) => {
1997
- if (hasProjectReadToken(dependencies.environment)) {
2025
+ if (hasWorkspaceReadToken(dependencies.environment)) {
1998
2026
  exitCode = rejectTokenWrite(dependencies);
1999
2027
  return;
2000
2028
  }
@@ -2031,7 +2059,7 @@ export async function runCli(arguments_, dependencies) {
2031
2059
  .requiredOption("--expected-version <expectedVersion>", "Opaque version returned by SpecFlow", parseOpaqueVersion)
2032
2060
  .description("Delete a Work Item Label; a Workspace definition's task-copy cascade resumes automatically across bounded batches")
2033
2061
  .action(async (options) => {
2034
- if (hasProjectReadToken(dependencies.environment)) {
2062
+ if (hasWorkspaceReadToken(dependencies.environment)) {
2035
2063
  exitCode = rejectTokenWrite(dependencies);
2036
2064
  return;
2037
2065
  }
@@ -2081,7 +2109,7 @@ export async function runCli(arguments_, dependencies) {
2081
2109
  .option("--expected-version <expectedVersion>", "Opaque version of the task copy being moved; required when the Label is already attached elsewhere", parseOpaqueVersion)
2082
2110
  .description("Attach a Work Item Label to a Work Item as a task-level copy")
2083
2111
  .action(async (options) => {
2084
- if (hasProjectReadToken(dependencies.environment)) {
2112
+ if (hasWorkspaceReadToken(dependencies.environment)) {
2085
2113
  exitCode = rejectTokenWrite(dependencies);
2086
2114
  return;
2087
2115
  }
@@ -2113,7 +2141,7 @@ export async function runCli(arguments_, dependencies) {
2113
2141
  .requiredOption("--expected-version <expectedVersion>", "Opaque version returned by SpecFlow for the attached copy", parseOpaqueVersion)
2114
2142
  .description("Detach a Work Item Label copy from a Work Item")
2115
2143
  .action(async (options) => {
2116
- if (hasProjectReadToken(dependencies.environment)) {
2144
+ if (hasWorkspaceReadToken(dependencies.environment)) {
2117
2145
  exitCode = rejectTokenWrite(dependencies);
2118
2146
  return;
2119
2147
  }
@@ -2153,7 +2181,7 @@ export async function runCli(arguments_, dependencies) {
2153
2181
  .option("--default-value <defaultValue>")
2154
2182
  .option("--required", "Reject Work Items that leave the field blank")
2155
2183
  .action(async (options) => {
2156
- if (hasProjectReadToken(dependencies.environment)) {
2184
+ if (hasWorkspaceReadToken(dependencies.environment)) {
2157
2185
  exitCode = rejectTokenWrite(dependencies);
2158
2186
  return;
2159
2187
  }
@@ -2188,7 +2216,7 @@ export async function runCli(arguments_, dependencies) {
2188
2216
  .requiredOption("--expected-version <expectedVersion>", "Opaque version returned by SpecFlow", parseOpaqueVersion)
2189
2217
  .description("Delete a Work Item Custom Field; its stored values cascade with the definition")
2190
2218
  .action(async (options) => {
2191
- if (hasProjectReadToken(dependencies.environment)) {
2219
+ if (hasWorkspaceReadToken(dependencies.environment)) {
2192
2220
  exitCode = rejectTokenWrite(dependencies);
2193
2221
  return;
2194
2222
  }
@@ -2214,7 +2242,7 @@ export async function runCli(arguments_, dependencies) {
2214
2242
  .requiredOption("--project-id <projectId>")
2215
2243
  .requiredOption("--order <order>", "Comma-separated id@version pairs in display order — version is the integer inside the field's opaque \"N\" version")
2216
2244
  .action(async (options) => {
2217
- if (hasProjectReadToken(dependencies.environment)) {
2245
+ if (hasWorkspaceReadToken(dependencies.environment)) {
2218
2246
  exitCode = rejectTokenWrite(dependencies);
2219
2247
  return;
2220
2248
  }
@@ -2263,7 +2291,7 @@ export async function runCli(arguments_, dependencies) {
2263
2291
  .requiredOption("--expected-version <version>", "The opaque Work Item version returned by SpecFlow")
2264
2292
  .requiredOption("--value <value>", "Stored value; an empty string clears a non-required field")
2265
2293
  .action(async (options) => {
2266
- if (hasProjectReadToken(dependencies.environment)) {
2294
+ if (hasWorkspaceReadToken(dependencies.environment)) {
2267
2295
  exitCode = rejectTokenWrite(dependencies);
2268
2296
  return;
2269
2297
  }
@@ -2304,7 +2332,7 @@ export async function runCli(arguments_, dependencies) {
2304
2332
  .option("--color <color>", "Column color")
2305
2333
  .option("--final", "Mark the column as an explicit completion lane")
2306
2334
  .action(async (options) => {
2307
- if (hasProjectReadToken(dependencies.environment)) {
2335
+ if (hasWorkspaceReadToken(dependencies.environment)) {
2308
2336
  exitCode = rejectTokenWrite(dependencies);
2309
2337
  return;
2310
2338
  }
@@ -2342,7 +2370,7 @@ export async function runCli(arguments_, dependencies) {
2342
2370
  .option("--final", "Mark the column as an explicit completion lane")
2343
2371
  .option("--not-final", "Remove the column's completion marker")
2344
2372
  .action(async (options) => {
2345
- if (hasProjectReadToken(dependencies.environment)) {
2373
+ if (hasWorkspaceReadToken(dependencies.environment)) {
2346
2374
  exitCode = rejectTokenWrite(dependencies);
2347
2375
  return;
2348
2376
  }
@@ -2407,7 +2435,7 @@ export async function runCli(arguments_, dependencies) {
2407
2435
  .requiredOption("--column-slug <columnSlug>")
2408
2436
  .requiredOption("--expected-version <expectedVersion>", "Opaque version returned by SpecFlow", parseOpaqueVersion)
2409
2437
  .action(async (options) => {
2410
- if (hasProjectReadToken(dependencies.environment)) {
2438
+ if (hasWorkspaceReadToken(dependencies.environment)) {
2411
2439
  exitCode = rejectTokenWrite(dependencies);
2412
2440
  return;
2413
2441
  }
@@ -2433,7 +2461,7 @@ export async function runCli(arguments_, dependencies) {
2433
2461
  .requiredOption("--project-id <projectId>")
2434
2462
  .requiredOption("--order <order>", "Comma-separated slug@version pairs in board order — version is the integer inside the column's opaque \"N\" version")
2435
2463
  .action(async (options) => {
2436
- if (hasProjectReadToken(dependencies.environment)) {
2464
+ if (hasWorkspaceReadToken(dependencies.environment)) {
2437
2465
  exitCode = rejectTokenWrite(dependencies);
2438
2466
  return;
2439
2467
  }
@@ -2453,82 +2481,52 @@ export async function runCli(arguments_, dependencies) {
2453
2481
  const token = program.command("token");
2454
2482
  token
2455
2483
  .command("issue")
2456
- .requiredOption("--project-id <projectId>")
2484
+ .requiredOption("--workspace-id <workspaceId>")
2457
2485
  .requiredOption("--name <name>")
2458
2486
  .option("--expires-at <expiresAt>", "ISO 8601 expiry date-time", parseIsoDateTime)
2459
2487
  .action(async (options) => {
2460
- if (hasProjectReadToken(dependencies.environment)) {
2488
+ if (hasWorkspaceReadToken(dependencies.environment)) {
2461
2489
  exitCode = rejectTokenWrite(dependencies);
2462
2490
  return;
2463
2491
  }
2464
2492
  const idempotencyKey = randomUUID();
2465
- exitCode = await run((client) => client.POST("/v1/projects/{projectId}/read-tokens", {
2493
+ exitCode = await run((client) => client.POST("/v1/workspaces/{workspaceId}/read-tokens", {
2466
2494
  body: {
2467
2495
  expiresAt: options.expiresAt ?? null,
2468
2496
  name: options.name,
2469
2497
  },
2470
2498
  params: {
2471
2499
  header: { "idempotency-key": idempotencyKey },
2472
- path: { projectId: options.projectId },
2500
+ path: { workspaceId: options.workspaceId },
2473
2501
  },
2474
2502
  }), {
2475
- ambiguousOutcomeMessage: "Project Read Token issuance outcome is unknown; list tokens before retrying",
2503
+ ambiguousOutcomeMessage: "Workspace read-only Token issuance outcome is unknown; list tokens before retrying",
2476
2504
  retryTransportOnce: true,
2477
- validateSuccess: validateIssuedProjectReadToken,
2505
+ validateSuccess: validateIssuedWorkspaceReadToken,
2478
2506
  });
2479
2507
  });
2480
2508
  token
2481
2509
  .command("list")
2482
- .requiredOption("--project-id <projectId>")
2510
+ .requiredOption("--workspace-id <workspaceId>")
2483
2511
  .action(async (options) => {
2484
- exitCode = await run((client) => client.GET("/v1/projects/{projectId}/read-tokens", {
2485
- params: { path: { projectId: options.projectId } },
2486
- }), { rejectProjectReadToken: true });
2512
+ exitCode = await run((client) => client.GET("/v1/workspaces/{workspaceId}/read-tokens", {
2513
+ params: { path: { workspaceId: options.workspaceId } },
2514
+ }), { rejectWorkspaceReadToken: true });
2487
2515
  });
2488
2516
  token
2489
2517
  .command("revoke")
2490
- .requiredOption("--project-id <projectId>")
2518
+ .requiredOption("--workspace-id <workspaceId>")
2491
2519
  .requiredOption("--token-id <tokenId>")
2492
2520
  .action(async (options) => {
2493
- if (hasProjectReadToken(dependencies.environment)) {
2521
+ if (hasWorkspaceReadToken(dependencies.environment)) {
2494
2522
  exitCode = rejectTokenWrite(dependencies);
2495
2523
  return;
2496
2524
  }
2497
- exitCode = await run(async (client) => withSuccessData(await client.DELETE("/v1/projects/{projectId}/read-tokens/{tokenId}", { params: { path: options } }), { revoked: { tokenId: options.tokenId } }), {
2498
- ambiguousOutcomeMessage: "Project Read Token revocation outcome is unknown; list tokens before retrying",
2525
+ exitCode = await run(async (client) => withSuccessData(await client.DELETE("/v1/workspaces/{workspaceId}/read-tokens/{tokenId}", { params: { path: options } }), { revoked: { tokenId: options.tokenId } }), {
2526
+ ambiguousOutcomeMessage: "Workspace read-only Token revocation outcome is unknown; list tokens before retrying",
2499
2527
  retryTransportOnce: true,
2500
2528
  });
2501
2529
  });
2502
- const review = program.command("review");
2503
- review
2504
- .command("request")
2505
- .requiredOption("--project-id <projectId>")
2506
- .requiredOption("--spec-id <specId>")
2507
- .action(async (options) => {
2508
- if (hasProjectReadToken(dependencies.environment)) {
2509
- exitCode = rejectTokenWrite(dependencies);
2510
- return;
2511
- }
2512
- const idempotencyKey = randomUUID();
2513
- exitCode = await run((client) => client.POST("/v1/projects/{projectId}/specs/{specId}/reviews", {
2514
- params: {
2515
- header: { "idempotency-key": idempotencyKey },
2516
- path: options,
2517
- },
2518
- }), {
2519
- ambiguousOutcomeMessage: "Manual Review request outcome is unknown; inspect Review Records before retrying",
2520
- retryTransportOnce: true,
2521
- });
2522
- });
2523
- review
2524
- .command("read")
2525
- .requiredOption("--project-id <projectId>")
2526
- .requiredOption("--review-id <reviewId>")
2527
- .action(async (options) => {
2528
- exitCode = await run((client) => client.GET("/v1/projects/{projectId}/reviews/{reviewId}", {
2529
- params: { path: options },
2530
- }), { rejectProjectReadToken: true });
2531
- });
2532
2530
  try {
2533
2531
  await program.parseAsync([...arguments_], { from: "user" });
2534
2532
  return exitCode;
@@ -2539,7 +2537,8 @@ export async function runCli(arguments_, dependencies) {
2539
2537
  return cause.exitCode;
2540
2538
  }
2541
2539
  writeSkillError(dependencies, {
2542
- message: "The command does not match the supported CLI contract",
2540
+ message: cause.message.replace(/'[^']*'/gu, (quoted) => /^'--?[a-z][a-z-]*(?: [<[][^>\]]+[>\]])?'$/u.test(quoted) ? quoted : "'<redacted>'").replace(/[\r\n]/gu, " ").slice(0, 500),
2541
+ code: "INVALID_CLI_INPUT",
2543
2542
  reason: "Invalid Operation",
2544
2543
  retryable: false,
2545
2544
  });
@@ -2549,15 +2548,19 @@ export async function runCli(arguments_, dependencies) {
2549
2548
  }
2550
2549
  }
2551
2550
  async function executeRequest(dependencies, request, options = {}, serverFlag, apiKeyIdFlag) {
2552
- const environmentCredential = dependencies.environment.SPEC_FLOW_PROJECT_READ_TOKEN;
2551
+ if (dependencies.environment.SPEC_FLOW_PROJECT_READ_TOKEN !== undefined) {
2552
+ writeSkillError(dependencies, { message: "Project Read Tokens are retired; use a Workspace read-only Token", reason: "Authentication Failed", retryable: false });
2553
+ return 2;
2554
+ }
2555
+ const environmentCredential = dependencies.environment.SPEC_FLOW_WORKSPACE_READ_TOKEN;
2553
2556
  const apiKeyEnvironment = dependencies.environment.SPEC_FLOW_API_KEY;
2554
2557
  const usesApiKey = apiKeyEnvironment !== undefined || apiKeyIdFlag !== undefined;
2555
- if (options.requireUserSession === true && (usesApiKey || hasProjectReadToken(dependencies.environment))) {
2558
+ if (options.requireUserSession === true && (usesApiKey || hasWorkspaceReadToken(dependencies.environment))) {
2556
2559
  writeSkillError(dependencies, { message: "A User Session is required for account management", reason: "Permission Denied", retryable: false });
2557
2560
  return 3;
2558
2561
  }
2559
- if ((apiKeyEnvironment !== undefined && apiKeyIdFlag !== undefined) || (usesApiKey && hasProjectReadToken(dependencies.environment))) {
2560
- writeSkillError(dependencies, { message: "Select exactly one API Key or Project Read Token credential", reason: "Invalid Operation", retryable: false });
2562
+ if ((apiKeyEnvironment !== undefined && apiKeyIdFlag !== undefined) || (usesApiKey && hasWorkspaceReadToken(dependencies.environment))) {
2563
+ writeSkillError(dependencies, { message: "Select exactly one API Key or Workspace read-only Token credential", reason: "Invalid Operation", retryable: false });
2561
2564
  return 2;
2562
2565
  }
2563
2566
  if (apiKeyIdFlag !== undefined && !z.uuid().safeParse(apiKeyIdFlag).success) {
@@ -2568,9 +2571,9 @@ async function executeRequest(dependencies, request, options = {}, serverFlag, a
2568
2571
  writeSkillError(dependencies, { message: "SPEC_FLOW_API_KEY does not contain a User API Key", reason: "Authentication Failed", retryable: false });
2569
2572
  return 2;
2570
2573
  }
2571
- if (options.rejectProjectReadToken === true &&
2572
- hasProjectReadToken(dependencies.environment)) {
2573
- return rejectProjectReadTokenOperation(dependencies);
2574
+ if (options.rejectWorkspaceReadToken === true &&
2575
+ hasWorkspaceReadToken(dependencies.environment)) {
2576
+ return rejectWorkspaceReadTokenOperation(dependencies);
2574
2577
  }
2575
2578
  const baseUrl = resolveApiOrigin(dependencies, serverFlag);
2576
2579
  if (baseUrl === null) {
@@ -2586,8 +2589,7 @@ async function executeRequest(dependencies, request, options = {}, serverFlag, a
2586
2589
  credential = await dependencies.credentialStore.readApiKey(apiKeyIdFlag.toLowerCase());
2587
2590
  }
2588
2591
  else {
2589
- credential = environmentCredential === undefined || environmentCredential.trim().length === 0
2590
- ? await dependencies.credentialStore.read() : environmentCredential;
2592
+ credential = environmentCredential ?? await dependencies.credentialStore.read();
2591
2593
  }
2592
2594
  }
2593
2595
  catch {
@@ -2600,7 +2602,7 @@ async function executeRequest(dependencies, request, options = {}, serverFlag, a
2600
2602
  }
2601
2603
  if (credential === null || credential.trim().length === 0) {
2602
2604
  writeSkillError(dependencies, {
2603
- message: "An authenticated CLI Session, User API Key or Project Read Token is required",
2605
+ message: "An authenticated CLI Session, User API Key or Workspace read-only Token is required",
2604
2606
  reason: "Authentication Failed",
2605
2607
  retryable: false,
2606
2608
  });
@@ -2663,18 +2665,19 @@ function retryTransportOnce(fetch) {
2663
2665
  function ambiguousMutationError(message) {
2664
2666
  return {
2665
2667
  message: message ??
2666
- "Project creation outcome is unknown; run project list before trying a new create",
2668
+ "Write outcome is unknown; read the target resource before trying a new command identity",
2669
+ code: "EXECUTION_OUTCOME_UNKNOWN",
2667
2670
  reason: "Temporary Failure",
2668
2671
  retryable: false,
2669
2672
  };
2670
2673
  }
2671
- function validateIssuedProjectReadToken(data) {
2674
+ function validateIssuedWorkspaceReadToken(data) {
2672
2675
  if (typeof data === "object" &&
2673
2676
  data !== null &&
2674
2677
  "credential" in data &&
2675
2678
  data.credential === null) {
2676
2679
  return {
2677
- message: "Project Read Token issuance completed, but its credential cannot be recovered; list or revoke the token before deliberately issuing another",
2680
+ message: "Workspace read-only Token issuance completed, but its credential cannot be recovered; list or revoke the token before deliberately issuing another",
2678
2681
  reason: "Temporary Failure",
2679
2682
  retryable: false,
2680
2683
  };
@@ -2726,18 +2729,17 @@ function parseProjectStatus(value) {
2726
2729
  throw new CommanderError(1, "commander.invalidArgument", "Project status must be Active, Archived, or Trashed");
2727
2730
  }
2728
2731
  function parseProjectId(value) {
2729
- const normalized = value.trim().toUpperCase();
2730
- if (/^[A-Z]{2,10}$/u.test(normalized)) {
2732
+ const normalized = value.trim();
2733
+ if (/^[A-Za-z0-9_-]{1,200}$/u.test(normalized))
2731
2734
  return normalized;
2732
- }
2733
- throw invalidArgument("Project ID must contain 2-10 letters");
2735
+ throw invalidArgument("Project ID must be the system identity returned by Project discovery");
2734
2736
  }
2735
2737
  function parseSpecId(value) {
2736
2738
  const normalized = value.trim().toUpperCase();
2737
- if (/^[A-Z]{2,10}-[1-9]\d*$/u.test(normalized)) {
2739
+ if (/^[A-Z]{2,10}-S[1-9]\d*$/u.test(normalized)) {
2738
2740
  return normalized;
2739
2741
  }
2740
- throw invalidArgument("Spec ID must be a Project ID followed by a positive number");
2742
+ throw invalidArgument("Spec ID must have the Workspace prefix, S marker and positive number");
2741
2743
  }
2742
2744
  function parseWorkItemId(value) {
2743
2745
  const normalized = value.trim().toUpperCase();
@@ -2826,6 +2828,7 @@ function workItemSelectionQuery(options) {
2826
2828
  ...(options.priority.length === 0
2827
2829
  ? {}
2828
2830
  : { priority: options.priority.join(",") }),
2831
+ ...(options.repositoryConnectionId === undefined ? {} : { repositoryConnectionId: options.repositoryConnectionId }),
2829
2832
  ...(options.search === undefined ? {} : { search: options.search }),
2830
2833
  ...(options.sort === undefined ? {} : { sortBy: options.sort }),
2831
2834
  ...(options.order === undefined ? {} : { sortOrder: options.order }),
@@ -2859,6 +2862,7 @@ function workItemSelectionFlags(command) {
2859
2862
  ...previous,
2860
2863
  parseWorkItemPriority(value),
2861
2864
  ], [])
2865
+ .option("--repository-connection-id <id>", "Filter by an explicit GitHub repository connection")
2862
2866
  .option("--search <search>", "Filter by title search text")
2863
2867
  .option("--sort <field>", "Sort by field", parseWorkItemSortField)
2864
2868
  .option("--order <order>", "Sort order", parseWorkItemSortOrder)
@@ -2873,12 +2877,16 @@ function parseWorkItemBulkAction(value) {
2873
2877
  throw invalidArgument(`Bulk action must be ${workItemBulkActions.join(", ")}`);
2874
2878
  }
2875
2879
  /**
2876
- * A bulk target is `WORKITEM@version` — the public id and its observed
2877
- * version, echoed back as the `"N"` precondition each item carries.
2880
+ * Targets default to the route Project; an explicit `projectId/` selects
2881
+ * another owner without inferring Project identity from Workspace numbering.
2878
2882
  */
2879
2883
  function parseWorkItemBulkTarget(value, previous) {
2880
- const [id, version, extra] = value.split("@");
2881
- if (id === undefined ||
2884
+ const [identity, version, extra] = value.split("@");
2885
+ const segments = identity?.split("/") ?? [];
2886
+ const id = segments.length === 2 ? segments[1] : segments[0];
2887
+ const projectId = segments.length === 2 ? parseProjectId(segments[0] ?? "") : undefined;
2888
+ if (segments.length > 2 ||
2889
+ id === undefined ||
2882
2890
  version === undefined ||
2883
2891
  extra !== undefined ||
2884
2892
  !/^[A-Z]{2,10}-[1-9]\d*$/u.test(id.trim().toUpperCase()) ||
@@ -2892,7 +2900,7 @@ function parseWorkItemBulkTarget(value, previous) {
2892
2900
  }
2893
2901
  return [
2894
2902
  ...previous,
2895
- { expectedVersion: `"${version}"`, id: id.trim().toUpperCase() },
2903
+ { expectedVersion: `"${version}"`, id: id.trim().toUpperCase(), ...(projectId === undefined ? {} : { projectId }) },
2896
2904
  ];
2897
2905
  }
2898
2906
  function parseBoardPosition(value) {
@@ -3140,9 +3148,9 @@ function isWorkItemLabelDeletionPending(data) {
3140
3148
  undefined);
3141
3149
  }
3142
3150
  function rejectTokenWrite(dependencies) {
3143
- if (hasProjectReadToken(dependencies.environment)) {
3151
+ if (hasWorkspaceReadToken(dependencies.environment)) {
3144
3152
  writeSkillError(dependencies, {
3145
- message: "Project Read Token does not permit write operations",
3153
+ message: "Workspace read-only Token does not permit write operations",
3146
3154
  reason: "Permission Denied",
3147
3155
  retryable: false,
3148
3156
  });
@@ -3155,32 +3163,39 @@ function rejectTokenWrite(dependencies) {
3155
3163
  });
3156
3164
  return 2;
3157
3165
  }
3158
- function rejectProjectReadTokenOperation(dependencies) {
3166
+ function rejectWorkspaceReadTokenOperation(dependencies) {
3159
3167
  writeSkillError(dependencies, {
3160
- message: "Project Read Token does not permit this operation",
3168
+ message: "Workspace read-only Token does not permit this operation",
3161
3169
  reason: "Permission Denied",
3162
3170
  retryable: false,
3163
3171
  });
3164
3172
  return 3;
3165
3173
  }
3166
- function hasProjectReadToken(environment) {
3167
- const credential = environment.SPEC_FLOW_PROJECT_READ_TOKEN;
3174
+ function hasWorkspaceReadToken(environment) {
3175
+ const credential = environment.SPEC_FLOW_WORKSPACE_READ_TOKEN;
3168
3176
  return credential !== undefined && credential.trim().length > 0;
3169
3177
  }
3170
3178
  function mapHttpFailure(response, payload) {
3179
+ const failure = classifyHttpFailure(response, payload);
3180
+ const issues = z.array(z.object({ location: z.enum(["body", "query", "path", "headers", "tool"]), path: z.array(z.union([z.string().max(100), z.number()])), rule: z.string().max(100), message: z.string().max(500) })).max(20).safeParse(z.object({ issues: z.unknown() }).safeParse(payload).data?.issues);
3181
+ const code = readServerCode(payload);
3182
+ const requestId = response.headers.get("x-request-id");
3183
+ return { ...failure, ...(requestId !== null && /^[A-Za-z0-9._:-]{1,100}$/u.test(requestId) ? { requestId } : {}), status: response.status, ...(code !== null && /^[A-Z][A-Z0-9_]{0,79}$/u.test(code) ? { code } : {}), ...(issues.success ? { issues: issues.data } : {}) };
3184
+ }
3185
+ function classifyHttpFailure(response, payload) {
3171
3186
  const { status } = response;
3172
3187
  const serverMessage = readServerMessage(payload);
3173
3188
  const serverCode = readServerCode(payload);
3174
3189
  if (status === 401) {
3175
3190
  return {
3176
- message: serverMessage ?? "Project Read Token is invalid, expired, or revoked",
3191
+ message: serverMessage ?? "Workspace read-only Token is invalid, expired, or revoked",
3177
3192
  reason: "Authentication Failed",
3178
3193
  retryable: false,
3179
3194
  };
3180
3195
  }
3181
3196
  if (status === 403) {
3182
3197
  return {
3183
- message: serverMessage ?? "Project Read Token does not permit this operation",
3198
+ message: serverMessage ?? "Workspace read-only Token does not permit this operation",
3184
3199
  reason: "Permission Denied",
3185
3200
  retryable: false,
3186
3201
  };
@@ -3206,7 +3221,7 @@ function mapHttpFailure(response, payload) {
3206
3221
  retryable: false,
3207
3222
  };
3208
3223
  }
3209
- if (status === 400 || status === 409 || status === 422) {
3224
+ if (status === 400 || status === 409 || status === 413 || status === 422 || status === 428) {
3210
3225
  const diagnostics = readServerDiagnostics(payload);
3211
3226
  return {
3212
3227
  ...(diagnostics === undefined ? {} : { diagnostics }),
@@ -3336,4 +3351,5 @@ function parseNotificationPreferences(value) {
3336
3351
  const data = result.data;
3337
3352
  return { ...(data.taskAssignmentEnabled === undefined ? {} : { taskAssignmentEnabled: data.taskAssignmentEnabled }), ...(data.taskCommentEnabled === undefined ? {} : { taskCommentEnabled: data.taskCommentEnabled }), ...(data.taskStatusChangeEnabled === undefined ? {} : { taskStatusChangeEnabled: data.taskStatusChangeEnabled }), ...(data.dueDateReminderEnabled === undefined ? {} : { dueDateReminderEnabled: data.dueDateReminderEnabled }), ...(data.dueDateReminderMinutes === undefined ? {} : { dueDateReminderMinutes: data.dueDateReminderMinutes }) };
3338
3353
  }
3354
+ const reviewPlanInputSchema = z.discriminatedUnion("cadence", [z.object({ cadence: z.literal("Disabled") }).strict(), z.object({ cadence: z.literal("Daily"), timeOfDay: z.string(), timeZone: z.string() }).strict(), z.object({ cadence: z.literal("Weekly"), dayOfWeek: z.number().int().min(1).max(7), timeOfDay: z.string(), timeZone: z.string() }).strict()]);
3339
3355
  //# sourceMappingURL=cli.js.map