@codazen/harmonica-mcp 3.8.6 → 3.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +800 -187
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -21545,11 +21545,11 @@ async function validateAuth(client, orgIdentifier, isOrgSlug, userId) {
21545
21545
  async function assertProjectInOrg(client, systemId, orgId) {
21546
21546
  const project = await client.getSystem(systemId);
21547
21547
  if (!project) {
21548
- throw new Error(`Project not found: "${systemId}"`);
21548
+ throw new Error(`System not found: "${systemId}"`);
21549
21549
  }
21550
21550
  if (project.orgId !== orgId) {
21551
21551
  throw new Error(
21552
- `Project "${systemId}" does not belong to the configured organization. Access denied.`
21552
+ `System "${systemId}" does not belong to the configured organization. Access denied.`
21553
21553
  );
21554
21554
  }
21555
21555
  }
@@ -21560,7 +21560,7 @@ async function assertRevisionInProject(client, revisionId, systemId) {
21560
21560
  }
21561
21561
  if (revision.systemId !== systemId) {
21562
21562
  throw new Error(
21563
- `Revision "${revisionId}" does not belong to project "${systemId}". Access denied.`
21563
+ `Revision "${revisionId}" does not belong to system "${systemId}". Access denied.`
21564
21564
  );
21565
21565
  }
21566
21566
  }
@@ -21578,11 +21578,11 @@ async function assertBeatInOrg(client, beatId, orgId) {
21578
21578
  async function fetchProjectInOrg(client, systemId, orgId) {
21579
21579
  const project = await client.getSystem(systemId);
21580
21580
  if (!project) {
21581
- throw new Error(`Project not found: "${systemId}"`);
21581
+ throw new Error(`System not found: "${systemId}"`);
21582
21582
  }
21583
21583
  if (project.orgId !== orgId) {
21584
21584
  throw new Error(
21585
- `Project "${systemId}" does not belong to the configured organization. Access denied.`
21585
+ `System "${systemId}" does not belong to the configured organization. Access denied.`
21586
21586
  );
21587
21587
  }
21588
21588
  return project;
@@ -22853,7 +22853,7 @@ function formatNoteList(notes) {
22853
22853
  return parts.join("\n");
22854
22854
  }
22855
22855
  function formatDocumentList(docs, allNotes) {
22856
- if (docs.length === 0) return "No documents uploaded for this project.";
22856
+ if (docs.length === 0) return "No documents uploaded for this system.";
22857
22857
  const lines = docs.map((doc) => {
22858
22858
  const meta = doc.documentMeta;
22859
22859
  const status = meta?.documentStatus ?? "unknown";
@@ -23927,7 +23927,7 @@ function registerCheckTools(server, ctx, client) {
23927
23927
  } else {
23928
23928
  const result = await client.listSystemChecks(systemId, checkType, { limit: 200 });
23929
23929
  checks = result.checks;
23930
- scope = `project ${systemId}`;
23930
+ scope = `system ${systemId}`;
23931
23931
  }
23932
23932
  if (checks.length === 0) {
23933
23933
  return { content: [{ type: "text", text: `No checks found for ${scope}.` }] };
@@ -23950,9 +23950,9 @@ function registerCheckTools(server, ctx, client) {
23950
23950
  try {
23951
23951
  const checks = await client.listLatestSystemChecks(systemId);
23952
23952
  if (checks.length === 0) {
23953
- return { content: [{ type: "text", text: `No checks found for project ${systemId}.` }] };
23953
+ return { content: [{ type: "text", text: `No checks found for system ${systemId}.` }] };
23954
23954
  }
23955
- const text = formatCheckList(checks, `latest per target \xB7 project ${systemId}`);
23955
+ const text = formatCheckList(checks, `latest per target \xB7 system ${systemId}`);
23956
23956
  return { content: [{ type: "text", text }] };
23957
23957
  } catch (err) {
23958
23958
  const message = err instanceof Error ? err.message : String(err);
@@ -24690,7 +24690,7 @@ function registerDropTools(server, ctx, client) {
24690
24690
  );
24691
24691
  server.tool(
24692
24692
  "create_drop",
24693
- "Create a Drop (release container) in an Account. Drops group Beat Revisions for coordinated release. The dropCode is auto-generated as D-NNN per Account. Drops are cross-project: a single Drop can include Revisions from any Project in the Account.",
24693
+ "Create a Drop (release container) in an Account. Drops group Beat Revisions for coordinated release. The dropCode is auto-generated as D-NNN per Account. Drops are cross-system: a single Drop can include Revisions from any System in the Account.",
24694
24694
  {
24695
24695
  accountId: external_exports.string().describe("The account ID"),
24696
24696
  name: external_exports.string().min(1).max(200).describe('Drop name (e.g. "MVP", "Phase 2", "Hotfix 2026-Q2")'),
@@ -26336,11 +26336,18 @@ var NoteStatusSchema = external_exports.enum([
26336
26336
  ]);
26337
26337
  var SystemStatusSchema = external_exports.enum(["active", "archived", "draft"]);
26338
26338
  var ChangeImportanceSchema = external_exports.enum(["minor", "moderate", "major"]);
26339
+ var normalizeSystemId = (raw) => {
26340
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return raw;
26341
+ const b = raw;
26342
+ const id = b["systemId"] || b["projectId"];
26343
+ if (!id) return raw;
26344
+ return { ...b, systemId: id, projectId: id };
26345
+ };
26339
26346
  var BeatApiSchema = external_exports.preprocess(
26340
26347
  (raw) => {
26341
26348
  if (typeof raw !== "object" || raw === null) return raw;
26342
26349
  const b = raw;
26343
- const id = b["systemId"] ?? b["projectId"];
26350
+ const id = b["systemId"] || b["projectId"];
26344
26351
  if (!id) return raw;
26345
26352
  return { ...b, systemId: id, projectId: id };
26346
26353
  },
@@ -26373,34 +26380,39 @@ var RevisionScmSchema = external_exports.object({
26373
26380
  state: external_exports.enum(["draft", "open", "merged", "closed"]),
26374
26381
  mergeSha: external_exports.string().optional()
26375
26382
  });
26376
- var RevisionApiSchema = external_exports.object({
26377
- revisionId: external_exports.string(),
26378
- beatId: external_exports.string(),
26379
- projectId: external_exports.string(),
26380
- status: RevisionStatusSchema,
26381
- title: external_exports.string().default(""),
26382
- versionNumber: external_exports.number(),
26383
- changeSummary: external_exports.string().nullish(),
26384
- changeImportance: ChangeImportanceSchema.nullish(),
26385
- primaryUser: external_exports.string(),
26386
- description: external_exports.string().nullish(),
26387
- tags: external_exports.array(external_exports.string()).nullish(),
26388
- priority: external_exports.number().nullish(),
26389
- estimatedEffort: external_exports.string().nullish(),
26390
- humanAssignee: HumanAssigneeSchema.nullish(),
26391
- agentAssignee: AgentAssigneeSchema.nullish(),
26392
- scm: RevisionScmSchema.optional(),
26393
- // The Layer this Revision was created under (immutable). Absent on Revisions
26394
- // predating Layers — never backfilled — so consumers group those as "Unassigned".
26395
- layerId: external_exports.string().nullish(),
26396
- createdAt: external_exports.string(),
26397
- updatedAt: external_exports.string()
26398
- }).passthrough();
26399
- var NoteApiSchema = external_exports.object({
26383
+ var RevisionApiSchema = external_exports.preprocess(
26384
+ normalizeSystemId,
26385
+ external_exports.object({
26386
+ revisionId: external_exports.string(),
26387
+ beatId: external_exports.string(),
26388
+ systemId: external_exports.string(),
26389
+ projectId: external_exports.string(),
26390
+ status: RevisionStatusSchema,
26391
+ title: external_exports.string().default(""),
26392
+ versionNumber: external_exports.number(),
26393
+ changeSummary: external_exports.string().nullish(),
26394
+ changeImportance: ChangeImportanceSchema.nullish(),
26395
+ primaryUser: external_exports.string(),
26396
+ description: external_exports.string().nullish(),
26397
+ tags: external_exports.array(external_exports.string()).nullish(),
26398
+ priority: external_exports.number().nullish(),
26399
+ estimatedEffort: external_exports.string().nullish(),
26400
+ humanAssignee: HumanAssigneeSchema.nullish(),
26401
+ agentAssignee: AgentAssigneeSchema.nullish(),
26402
+ scm: RevisionScmSchema.optional(),
26403
+ // The Layer this Revision was created under (immutable). Absent on Revisions
26404
+ // predating Layers — never backfilled — so consumers group those as "Unassigned".
26405
+ layerId: external_exports.string().nullish(),
26406
+ createdAt: external_exports.string(),
26407
+ updatedAt: external_exports.string()
26408
+ }).passthrough()
26409
+ );
26410
+ var NoteApiSchema = external_exports.preprocess(normalizeSystemId, external_exports.object({
26400
26411
  noteId: external_exports.string(),
26401
26412
  noteType: NoteTypeSchema,
26402
26413
  status: NoteStatusSchema,
26403
26414
  content: external_exports.string(),
26415
+ systemId: external_exports.string().nullish(),
26404
26416
  projectId: external_exports.string().nullish(),
26405
26417
  beatId: external_exports.string().nullish(),
26406
26418
  revisionId: external_exports.string().nullish(),
@@ -26412,18 +26424,22 @@ var NoteApiSchema = external_exports.object({
26412
26424
  agentAssignee: AgentAssigneeSchema.nullish(),
26413
26425
  createdAt: external_exports.string(),
26414
26426
  updatedAt: external_exports.string()
26415
- }).passthrough();
26416
- var SystemApiSchema = external_exports.object({
26417
- projectId: external_exports.string(),
26418
- title: external_exports.string(),
26419
- status: SystemStatusSchema,
26420
- description: external_exports.string().optional(),
26421
- strategy: external_exports.string().optional(),
26422
- orgId: external_exports.string().optional(),
26423
- ownerUserId: external_exports.string().optional(),
26424
- createdAt: external_exports.string(),
26425
- updatedAt: external_exports.string()
26426
- }).passthrough();
26427
+ }).passthrough());
26428
+ var SystemApiSchema = external_exports.preprocess(
26429
+ normalizeSystemId,
26430
+ external_exports.object({
26431
+ systemId: external_exports.string(),
26432
+ projectId: external_exports.string(),
26433
+ title: external_exports.string(),
26434
+ status: SystemStatusSchema,
26435
+ description: external_exports.string().optional(),
26436
+ strategy: external_exports.string().optional(),
26437
+ orgId: external_exports.string().optional(),
26438
+ ownerUserId: external_exports.string().optional(),
26439
+ createdAt: external_exports.string(),
26440
+ updatedAt: external_exports.string()
26441
+ }).passthrough()
26442
+ );
26427
26443
  var BeatsResponseSchema = external_exports.object({
26428
26444
  beats: external_exports.array(BeatApiSchema),
26429
26445
  total: external_exports.number().optional(),
@@ -26608,37 +26624,45 @@ var CHECK_TARGET_TYPES = [
26608
26624
  "organization"
26609
26625
  ];
26610
26626
  var CheckTargetTypeSchema = external_exports.enum(CHECK_TARGET_TYPES);
26611
- var CheckApiSchema = external_exports.object({
26612
- checkId: external_exports.string(),
26613
- checkType: CheckTypeSchema,
26614
- targetType: CheckTargetTypeSchema,
26615
- targetId: external_exports.string(),
26616
- projectId: external_exports.string(),
26617
- overallScore: external_exports.number(),
26618
- dimensions: external_exports.array(CheckDimensionScoreSchema),
26619
- summary: external_exports.string(),
26620
- topSuggestion: external_exports.string().optional(),
26621
- gaps: external_exports.array(CheckGapSchema).optional(),
26622
- taskId: external_exports.string().optional(),
26623
- triggeredBy: external_exports.string().optional(),
26624
- createdAt: external_exports.string(),
26625
- updatedAt: external_exports.string()
26626
- }).passthrough();
26627
- var SystemCheckApiSchema = external_exports.object({
26628
- checkId: external_exports.string(),
26629
- checkType: CheckTypeSchema,
26630
- targetType: CheckTargetTypeSchema,
26631
- targetId: external_exports.string(),
26632
- projectId: external_exports.string(),
26633
- overallScore: external_exports.number(),
26634
- summary: external_exports.string(),
26635
- triggeredBy: external_exports.string().optional(),
26636
- createdAt: external_exports.string(),
26637
- updatedAt: external_exports.string(),
26638
- /** Populated only when the API was called with ?includeDimensions=true.
26639
- * Read-time enrichment from the underlying Check entity. */
26640
- dimensions: external_exports.array(CheckDimensionScoreSchema).optional()
26641
- }).passthrough();
26627
+ var CheckApiSchema = external_exports.preprocess(
26628
+ normalizeSystemId,
26629
+ external_exports.object({
26630
+ checkId: external_exports.string(),
26631
+ checkType: CheckTypeSchema,
26632
+ targetType: CheckTargetTypeSchema,
26633
+ targetId: external_exports.string(),
26634
+ systemId: external_exports.string().optional(),
26635
+ projectId: external_exports.string().optional(),
26636
+ overallScore: external_exports.number(),
26637
+ dimensions: external_exports.array(CheckDimensionScoreSchema),
26638
+ summary: external_exports.string(),
26639
+ topSuggestion: external_exports.string().optional(),
26640
+ gaps: external_exports.array(CheckGapSchema).optional(),
26641
+ taskId: external_exports.string().optional(),
26642
+ triggeredBy: external_exports.string().optional(),
26643
+ createdAt: external_exports.string(),
26644
+ updatedAt: external_exports.string()
26645
+ }).passthrough()
26646
+ );
26647
+ var SystemCheckApiSchema = external_exports.preprocess(
26648
+ normalizeSystemId,
26649
+ external_exports.object({
26650
+ checkId: external_exports.string(),
26651
+ checkType: CheckTypeSchema,
26652
+ targetType: CheckTargetTypeSchema,
26653
+ targetId: external_exports.string(),
26654
+ systemId: external_exports.string().optional(),
26655
+ projectId: external_exports.string().optional(),
26656
+ overallScore: external_exports.number(),
26657
+ summary: external_exports.string(),
26658
+ triggeredBy: external_exports.string().optional(),
26659
+ createdAt: external_exports.string(),
26660
+ updatedAt: external_exports.string(),
26661
+ /** Populated only when the API was called with ?includeDimensions=true.
26662
+ * Read-time enrichment from the underlying Check entity. */
26663
+ dimensions: external_exports.array(CheckDimensionScoreSchema).optional()
26664
+ }).passthrough()
26665
+ );
26642
26666
  var CheckResponseSchema = external_exports.object({ check: CheckApiSchema }).passthrough();
26643
26667
  var ChecksResponseSchema = external_exports.object({ checks: external_exports.array(SystemCheckApiSchema), nextCursor: external_exports.string().optional() }).passthrough();
26644
26668
  var TaskStatusSchema2 = external_exports.enum(["pending", "running", "completed", "failed"]);
@@ -26683,21 +26707,25 @@ var OrgMemberSchema = external_exports.object({
26683
26707
  var OrganizationsResponseSchema = external_exports.array(OrganizationSummarySchema);
26684
26708
  var OrgMembersResponseSchema = external_exports.array(OrgMemberSchema);
26685
26709
  var WrappedOrgMembersResponseSchema = external_exports.object({ members: OrgMembersResponseSchema });
26686
- var ActivitySchema = external_exports.object({
26687
- activityId: external_exports.string(),
26688
- projectId: external_exports.string(),
26689
- action: external_exports.string(),
26690
- importance: external_exports.enum(["minor", "moderate", "major"]),
26691
- reason: external_exports.string(),
26692
- // actorType is typed as string (not a strict enum) because the API accepts values
26693
- // beyond 'user'/'system' (e.g. 'human', 'agent') depending on the call site.
26694
- actorType: external_exports.string().optional(),
26695
- actorId: external_exports.string().optional(),
26696
- affectedBeatIds: external_exports.array(external_exports.string()).optional(),
26697
- affectedBeatTitles: external_exports.array(external_exports.string()).optional(),
26698
- metadata: external_exports.record(external_exports.unknown()).optional(),
26699
- createdAt: external_exports.string()
26700
- }).passthrough();
26710
+ var ActivitySchema = external_exports.preprocess(
26711
+ normalizeSystemId,
26712
+ external_exports.object({
26713
+ activityId: external_exports.string(),
26714
+ systemId: external_exports.string(),
26715
+ projectId: external_exports.string(),
26716
+ action: external_exports.string(),
26717
+ importance: external_exports.enum(["minor", "moderate", "major"]),
26718
+ reason: external_exports.string(),
26719
+ // actorType is typed as string (not a strict enum) because the API accepts values
26720
+ // beyond 'user'/'system' (e.g. 'human', 'agent') depending on the call site.
26721
+ actorType: external_exports.string().optional(),
26722
+ actorId: external_exports.string().optional(),
26723
+ affectedBeatIds: external_exports.array(external_exports.string()).optional(),
26724
+ affectedBeatTitles: external_exports.array(external_exports.string()).optional(),
26725
+ metadata: external_exports.record(external_exports.unknown()).optional(),
26726
+ createdAt: external_exports.string()
26727
+ }).passthrough()
26728
+ );
26701
26729
  var ActivitiesResponseSchema = external_exports.object({
26702
26730
  activities: external_exports.array(ActivitySchema),
26703
26731
  hasMore: external_exports.boolean(),
@@ -26725,26 +26753,30 @@ var NotificationsResponseSchema = external_exports.object({
26725
26753
  nextCursor: external_exports.string().optional()
26726
26754
  }).passthrough();
26727
26755
  var UnreadCountResponseSchema = external_exports.object({ count: external_exports.number() }).passthrough();
26728
- var TriageActionSchema = external_exports.object({
26729
- actionKey: external_exports.string(),
26730
- projectId: external_exports.string(),
26731
- entityId: external_exports.string().optional(),
26732
- entityType: external_exports.enum(["beat", "revision", "note"]).optional(),
26733
- beatId: external_exports.string().optional(),
26734
- category: external_exports.string(),
26735
- priority: external_exports.string(),
26736
- beatStatus: external_exports.string().optional(),
26737
- title: external_exports.string(),
26738
- description: external_exports.string(),
26739
- status: external_exports.enum(["open", "acted", "dismissed", "resolved"]),
26740
- decision: external_exports.enum(["auto-execute", "escalate", "skip"]).optional(),
26741
- taskId: external_exports.string().optional(),
26742
- taskStatus: external_exports.string().optional(),
26743
- firstSeenAt: external_exports.string(),
26744
- lastSeenAt: external_exports.string(),
26745
- dismissedBy: external_exports.string().optional(),
26746
- dismissedAt: external_exports.string().optional()
26747
- }).passthrough();
26756
+ var TriageActionSchema = external_exports.preprocess(
26757
+ normalizeSystemId,
26758
+ external_exports.object({
26759
+ actionKey: external_exports.string(),
26760
+ systemId: external_exports.string(),
26761
+ projectId: external_exports.string(),
26762
+ entityId: external_exports.string().optional(),
26763
+ entityType: external_exports.enum(["beat", "revision", "note"]).optional(),
26764
+ beatId: external_exports.string().optional(),
26765
+ category: external_exports.string(),
26766
+ priority: external_exports.string(),
26767
+ beatStatus: external_exports.string().optional(),
26768
+ title: external_exports.string(),
26769
+ description: external_exports.string(),
26770
+ status: external_exports.enum(["open", "acted", "dismissed", "resolved"]),
26771
+ decision: external_exports.enum(["auto-execute", "escalate", "skip"]).optional(),
26772
+ taskId: external_exports.string().optional(),
26773
+ taskStatus: external_exports.string().optional(),
26774
+ firstSeenAt: external_exports.string(),
26775
+ lastSeenAt: external_exports.string(),
26776
+ dismissedBy: external_exports.string().optional(),
26777
+ dismissedAt: external_exports.string().optional()
26778
+ }).passthrough()
26779
+ );
26748
26780
  var TriageActionsResponseSchema = external_exports.object({
26749
26781
  actions: external_exports.array(TriageActionSchema)
26750
26782
  }).passthrough();
@@ -26761,15 +26793,16 @@ var NextActionSchema = external_exports.object({
26761
26793
  var NextActionsResponseSchema = external_exports.object({
26762
26794
  actions: external_exports.array(NextActionSchema)
26763
26795
  }).passthrough();
26764
- var SearchResultSchema = external_exports.object({
26796
+ var SearchResultSchema = external_exports.preprocess(normalizeSystemId, external_exports.object({
26765
26797
  entityId: external_exports.string(),
26766
- entityType: external_exports.enum(["beat", "note", "revision", "beatVersion", "drop", "project"]),
26798
+ entityType: external_exports.enum(["beat", "note", "revision", "beatVersion", "drop", "project", "system"]),
26767
26799
  score: external_exports.number(),
26768
26800
  snippet: external_exports.string(),
26801
+ systemId: external_exports.string().optional(),
26769
26802
  projectId: external_exports.string().optional(),
26770
26803
  projectName: external_exports.string().optional(),
26771
26804
  teamspaceId: external_exports.string().optional()
26772
- }).passthrough();
26805
+ }).passthrough());
26773
26806
  var SearchScopeSchema = external_exports.object({
26774
26807
  kind: external_exports.enum(["project", "teamspace", "org"]),
26775
26808
  id: external_exports.string()
@@ -26805,34 +26838,50 @@ var LandscapeMetaSchema = external_exports.object({
26805
26838
  cacheHit: external_exports.boolean(),
26806
26839
  projectionHash: external_exports.string()
26807
26840
  }).passthrough();
26808
- var LandscapeResponseSchema = external_exports.object({
26809
- points: external_exports.array(LandscapePointSchema),
26810
- links: external_exports.array(external_exports.object({ from: external_exports.string(), to: external_exports.string() }).passthrough()),
26811
- clusters: external_exports.array(LandscapeClusterSchema),
26812
- scopeBoundary: external_exports.object({
26813
- projectId: external_exports.string(),
26814
- cx: external_exports.number(),
26815
- cy: external_exports.number(),
26816
- radius: external_exports.number(),
26817
- label: external_exports.string()
26818
- }).passthrough().optional(),
26819
- meta: LandscapeMetaSchema
26820
- }).passthrough();
26841
+ var LandscapeResponseSchema = external_exports.preprocess(
26842
+ (raw) => {
26843
+ if (typeof raw !== "object" || raw === null) return raw;
26844
+ const r = raw;
26845
+ const sb = r["scopeBoundary"];
26846
+ if (typeof sb === "object" && sb !== null) {
26847
+ const s = sb;
26848
+ const id = s["systemId"] || s["projectId"];
26849
+ if (id) return { ...r, scopeBoundary: { ...s, systemId: id, projectId: id } };
26850
+ }
26851
+ return r;
26852
+ },
26853
+ external_exports.object({
26854
+ points: external_exports.array(LandscapePointSchema),
26855
+ links: external_exports.array(external_exports.object({ from: external_exports.string(), to: external_exports.string() }).passthrough()),
26856
+ clusters: external_exports.array(LandscapeClusterSchema),
26857
+ scopeBoundary: external_exports.object({
26858
+ systemId: external_exports.string().optional(),
26859
+ projectId: external_exports.string().optional(),
26860
+ cx: external_exports.number(),
26861
+ cy: external_exports.number(),
26862
+ radius: external_exports.number(),
26863
+ label: external_exports.string()
26864
+ }).passthrough().optional(),
26865
+ meta: LandscapeMetaSchema
26866
+ }).passthrough()
26867
+ );
26821
26868
  var ClusterDescriptionSchema = external_exports.object({
26822
26869
  title: external_exports.string(),
26823
26870
  description: external_exports.string()
26824
26871
  }).passthrough();
26825
- var BeatVersionItemSchema = external_exports.object({
26872
+ var BeatVersionItemSchema = external_exports.preprocess(normalizeSystemId, external_exports.object({
26826
26873
  beatVersionId: external_exports.string(),
26827
26874
  beatId: external_exports.string(),
26875
+ systemId: external_exports.string().optional(),
26828
26876
  projectId: external_exports.string().optional(),
26829
26877
  status: external_exports.string()
26830
- }).passthrough();
26878
+ }).passthrough());
26831
26879
  var BeatVersionsResponseSchema = external_exports.object({
26832
26880
  beatVersions: external_exports.array(BeatVersionItemSchema)
26833
26881
  }).passthrough();
26834
26882
  var ConversationSummaryBaseSchema = external_exports.object({
26835
26883
  conversationId: external_exports.string(),
26884
+ systemId: external_exports.string().optional(),
26836
26885
  projectId: external_exports.string().optional(),
26837
26886
  teamspaceId: external_exports.string().optional(),
26838
26887
  userId: external_exports.string(),
@@ -26846,7 +26895,7 @@ var ConversationSummaryBaseSchema = external_exports.object({
26846
26895
  createdAt: external_exports.string(),
26847
26896
  updatedAt: external_exports.string()
26848
26897
  });
26849
- var ConversationSummarySchema = ConversationSummaryBaseSchema.passthrough();
26898
+ var ConversationSummarySchema = external_exports.preprocess(normalizeSystemId, ConversationSummaryBaseSchema.passthrough());
26850
26899
  var StoredMessageSchema = external_exports.object({
26851
26900
  messageId: external_exports.string(),
26852
26901
  conversationId: external_exports.string(),
@@ -26963,6 +27012,93 @@ var WorkItemEstimateHintApiSchema = external_exports.object({
26963
27012
  });
26964
27013
  var WorkItemsResponseSchema = external_exports.object({ workItems: external_exports.array(WorkItemApiSchema) }).passthrough();
26965
27014
  var WorkItemResponseSchema = external_exports.object({ workItem: WorkItemApiSchema, estimateHint: WorkItemEstimateHintApiSchema.optional() }).passthrough();
27015
+ var WORK_INTAKE_STATUSES = [
27016
+ "requested",
27017
+ "needs_decision",
27018
+ "accepted",
27019
+ "done"
27020
+ ];
27021
+ var WorkIntakeStatusApiSchema = external_exports.enum(WORK_INTAKE_STATUSES);
27022
+ var NEEDS_DECISION_REASONS = [
27023
+ "out_of_scope",
27024
+ "exceeds_capacity",
27025
+ "incoherent_with_decision"
27026
+ ];
27027
+ var NeedsDecisionReasonApiSchema = external_exports.enum(NEEDS_DECISION_REASONS);
27028
+ var WORK_ORDER_SIZES = [
27029
+ "XS",
27030
+ "S",
27031
+ "M",
27032
+ "L",
27033
+ "XL"
27034
+ ];
27035
+ var WorkOrderSizeApiSchema = external_exports.enum(WORK_ORDER_SIZES);
27036
+ var WORK_REQUEST_ORIGINS = [
27037
+ "client",
27038
+ "internal"
27039
+ ];
27040
+ var WorkRequestOriginApiSchema = external_exports.enum(WORK_REQUEST_ORIGINS);
27041
+ var WorkRequestApiSchema = external_exports.object({
27042
+ workRequestId: external_exports.string(),
27043
+ movementId: external_exports.string(),
27044
+ teamspaceId: external_exports.string(),
27045
+ orgId: external_exports.string(),
27046
+ title: external_exports.string(),
27047
+ /** The ask as written, in the requester's own language. */
27048
+ body: external_exports.string().optional(),
27049
+ requester: HumanAssigneeApiSchema,
27050
+ /** Where the ask entered — one entity covers both, only the entry point differs. */
27051
+ origin: WorkRequestOriginApiSchema,
27052
+ createdBy: external_exports.string().optional(),
27053
+ version: external_exports.number().optional(),
27054
+ createdAt: external_exports.string(),
27055
+ updatedAt: external_exports.string()
27056
+ }).passthrough();
27057
+ var WorkOrderApiSchema = external_exports.object({
27058
+ workOrderId: external_exports.string(),
27059
+ /** Immutable lineage back to the ask this Order was split from. */
27060
+ workRequestId: external_exports.string(),
27061
+ /** Mutable — an Order moves Tracks when an early split guess turns out wrong. */
27062
+ trackId: external_exports.string(),
27063
+ teamspaceId: external_exports.string(),
27064
+ orgId: external_exports.string(),
27065
+ title: external_exports.string(),
27066
+ description: external_exports.string().optional(),
27067
+ status: WorkIntakeStatusApiSchema,
27068
+ /** Present if and only if status is `needs_decision` — the reason IS the routing. */
27069
+ needsDecisionReason: NeedsDecisionReasonApiSchema.optional(),
27070
+ /** Absent when the Order arrived before it was sized, which the composition counts. */
27071
+ size: WorkOrderSizeApiSchema.optional(),
27072
+ createdBy: external_exports.string().optional(),
27073
+ version: external_exports.number().optional(),
27074
+ createdAt: external_exports.string(),
27075
+ updatedAt: external_exports.string()
27076
+ }).passthrough();
27077
+ var WorkRequestSizeEntryApiSchema = external_exports.object({
27078
+ size: WorkOrderSizeApiSchema,
27079
+ count: external_exports.number()
27080
+ });
27081
+ var WorkRequestSizeCompositionApiSchema = external_exports.object({
27082
+ /** Largest first, so a display reads "a large and a medium". */
27083
+ entries: external_exports.array(WorkRequestSizeEntryApiSchema),
27084
+ /** Orders carrying no usable size — counted, never silently dropped. */
27085
+ unsized: external_exports.number(),
27086
+ /** Sized orders only. */
27087
+ totalValueUnits: external_exports.number()
27088
+ });
27089
+ var WorkRequestOrderApiSchema = WorkOrderApiSchema.extend({
27090
+ /** Absent — not null — when the Track no longer resolves. */
27091
+ trackTitle: external_exports.string().optional()
27092
+ }).passthrough();
27093
+ var WorkRequestsResponseSchema = external_exports.object({ workRequests: external_exports.array(WorkRequestApiSchema) }).passthrough();
27094
+ var WorkOrdersResponseSchema = external_exports.object({ workOrders: external_exports.array(WorkOrderApiSchema) }).passthrough();
27095
+ var WorkRequestResponseSchema = external_exports.object({ workRequest: WorkRequestApiSchema }).passthrough();
27096
+ var WorkOrderResponseSchema = external_exports.object({ workOrder: WorkOrderApiSchema }).passthrough();
27097
+ var WorkRequestDetailResponseSchema = external_exports.object({
27098
+ workRequest: WorkRequestApiSchema,
27099
+ orders: external_exports.array(WorkRequestOrderApiSchema),
27100
+ size: WorkRequestSizeCompositionApiSchema
27101
+ }).passthrough();
26966
27102
  var AccountStatusSchema = external_exports.enum(["active", "inactive", "archived"]);
26967
27103
  var AccountApiSchema = external_exports.object({
26968
27104
  accountId: external_exports.string(),
@@ -26999,7 +27135,7 @@ var LayerApiSchema = external_exports.preprocess(
26999
27135
  (raw) => {
27000
27136
  if (typeof raw !== "object" || raw === null) return raw;
27001
27137
  const l = raw;
27002
- const id = l["systemId"] ?? l["projectId"];
27138
+ const id = l["systemId"] || l["projectId"];
27003
27139
  if (!id) return raw;
27004
27140
  return { ...l, systemId: id, projectId: id };
27005
27141
  },
@@ -27058,10 +27194,11 @@ var DELIVERABLE_GROUP_STATUSES = [
27058
27194
  "cancelled"
27059
27195
  ];
27060
27196
  var DeliverableGroupStatusSchema = external_exports.enum(DELIVERABLE_GROUP_STATUSES);
27061
- var DeliverableGroupApiSchema = external_exports.object({
27197
+ var DeliverableGroupApiSchema = external_exports.preprocess(normalizeSystemId, external_exports.object({
27062
27198
  deliverableGroupId: external_exports.string(),
27063
27199
  groupCode: external_exports.string(),
27064
27200
  teamspaceId: external_exports.string(),
27201
+ systemId: external_exports.string().nullish(),
27065
27202
  projectId: external_exports.string().nullish(),
27066
27203
  movementId: external_exports.string().nullish(),
27067
27204
  orgId: external_exports.string(),
@@ -27079,7 +27216,7 @@ var DeliverableGroupApiSchema = external_exports.object({
27079
27216
  version: external_exports.number().nullish(),
27080
27217
  createdAt: external_exports.string(),
27081
27218
  updatedAt: external_exports.string()
27082
- }).passthrough();
27219
+ }).passthrough());
27083
27220
  var DeliverableGroupsResponseSchema = external_exports.object({
27084
27221
  deliverableGroups: external_exports.array(DeliverableGroupApiSchema)
27085
27222
  }).passthrough();
@@ -27096,27 +27233,34 @@ var MovementDropSummaryApiSchema = external_exports.object({
27096
27233
  var MovementDropsResponseSchema = external_exports.object({
27097
27234
  drops: external_exports.array(MovementDropSummaryApiSchema)
27098
27235
  }).passthrough();
27099
- var WorkPlanBeatVersionApiSchema = external_exports.object({
27100
- beatVersionId: external_exports.string(),
27101
- beatId: external_exports.string(),
27102
- projectId: external_exports.string(),
27103
- versionNumber: external_exports.number(),
27104
- title: external_exports.string(),
27105
- status: BeatVersionStatusSchema,
27106
- // Derived Value Units (VU = Δf × W), read-only. 0 when the BV has no confirmed
27107
- // value-velocity inputs. Computed server-side from the parent Beat's weight.
27108
- // `.default(0)` tolerates a rolling deploy where an older API omits the field —
27109
- // validatedApiFetch would otherwise throw and drop the whole hook into error.
27110
- valueUnits: external_exports.number().default(0),
27111
- createdAt: external_exports.string(),
27112
- updatedAt: external_exports.string()
27113
- }).passthrough();
27236
+ var WorkPlanBeatVersionApiSchema = external_exports.preprocess(
27237
+ normalizeSystemId,
27238
+ external_exports.object({
27239
+ beatVersionId: external_exports.string(),
27240
+ beatId: external_exports.string(),
27241
+ systemId: external_exports.string().optional(),
27242
+ projectId: external_exports.string().optional(),
27243
+ versionNumber: external_exports.number(),
27244
+ title: external_exports.string(),
27245
+ status: BeatVersionStatusSchema,
27246
+ // Derived Value Units (VU = Δf × W), read-only. 0 when the BV has no confirmed
27247
+ // value-velocity inputs. Computed server-side from the parent Beat's weight.
27248
+ // `.default(0)` tolerates a rolling deploy where an older API omits the field —
27249
+ // validatedApiFetch would otherwise throw and drop the whole hook into error.
27250
+ valueUnits: external_exports.number().default(0),
27251
+ createdAt: external_exports.string(),
27252
+ updatedAt: external_exports.string()
27253
+ }).passthrough()
27254
+ );
27114
27255
  var DeliverableGroupBeatVersionsResponseSchema = external_exports.object({
27115
27256
  beatVersions: external_exports.array(WorkPlanBeatVersionApiSchema)
27116
27257
  }).passthrough();
27117
27258
  var RevisionClaimActorSchema = external_exports.union([
27118
27259
  external_exports.object({ kind: external_exports.literal("user"), userId: external_exports.string() }),
27119
- external_exports.object({ kind: external_exports.literal("agent"), agentId: external_exports.string(), projectId: external_exports.string() })
27260
+ external_exports.preprocess(
27261
+ normalizeSystemId,
27262
+ external_exports.object({ kind: external_exports.literal("agent"), agentId: external_exports.string(), systemId: external_exports.string(), projectId: external_exports.string() })
27263
+ )
27120
27264
  ]);
27121
27265
  var RevisionClaimSchema = external_exports.object({
27122
27266
  claimId: external_exports.string(),
@@ -27127,33 +27271,41 @@ var RevisionClaimSchema = external_exports.object({
27127
27271
  var RevisionClaimsResponseSchema = external_exports.object({
27128
27272
  claims: external_exports.array(RevisionClaimSchema)
27129
27273
  }).passthrough();
27130
- var ProposedRevisionSchema = external_exports.object({
27131
- revisionId: external_exports.string(),
27132
- title: external_exports.string(),
27133
- state: external_exports.string(),
27134
- beatId: external_exports.string(),
27135
- beatTitle: external_exports.string().optional(),
27136
- projectId: external_exports.string(),
27137
- projectTitle: external_exports.string().optional()
27138
- }).passthrough();
27274
+ var ProposedRevisionSchema = external_exports.preprocess(
27275
+ normalizeSystemId,
27276
+ external_exports.object({
27277
+ revisionId: external_exports.string(),
27278
+ title: external_exports.string(),
27279
+ state: external_exports.string(),
27280
+ beatId: external_exports.string(),
27281
+ beatTitle: external_exports.string().optional(),
27282
+ systemId: external_exports.string().optional(),
27283
+ projectId: external_exports.string().optional(),
27284
+ projectTitle: external_exports.string().optional()
27285
+ }).passthrough()
27286
+ );
27139
27287
  var ReleaseIntervalSchema = external_exports.object({
27140
27288
  averageIntervalDays: external_exports.number(),
27141
27289
  sampleSize: external_exports.number(),
27142
27290
  isFallback: external_exports.boolean()
27143
27291
  }).passthrough();
27144
- var EligibleBeatVersionForDropSchema = external_exports.object({
27145
- beatVersionId: external_exports.string(),
27146
- title: external_exports.string(),
27147
- state: external_exports.string(),
27148
- beatId: external_exports.string(),
27149
- beatTitle: external_exports.string().optional(),
27150
- projectId: external_exports.string(),
27151
- projectTitle: external_exports.string().optional(),
27152
- createdAt: external_exports.string(),
27153
- updatedAt: external_exports.string(),
27154
- humanAssignee: external_exports.object({ email: external_exports.string(), name: external_exports.string() }).optional(),
27155
- currentDropAssignment: external_exports.object({ dropId: external_exports.string(), dropCode: external_exports.string().optional(), state: external_exports.string() }).optional()
27156
- }).passthrough();
27292
+ var EligibleBeatVersionForDropSchema = external_exports.preprocess(
27293
+ normalizeSystemId,
27294
+ external_exports.object({
27295
+ beatVersionId: external_exports.string(),
27296
+ title: external_exports.string(),
27297
+ state: external_exports.string(),
27298
+ beatId: external_exports.string(),
27299
+ beatTitle: external_exports.string().optional(),
27300
+ systemId: external_exports.string().optional(),
27301
+ projectId: external_exports.string().optional(),
27302
+ projectTitle: external_exports.string().optional(),
27303
+ createdAt: external_exports.string(),
27304
+ updatedAt: external_exports.string(),
27305
+ humanAssignee: external_exports.object({ email: external_exports.string(), name: external_exports.string() }).optional(),
27306
+ currentDropAssignment: external_exports.object({ dropId: external_exports.string(), dropCode: external_exports.string().optional(), state: external_exports.string() }).optional()
27307
+ }).passthrough()
27308
+ );
27157
27309
  var ScoredDropCandidateSchema = external_exports.object({
27158
27310
  bv: EligibleBeatVersionForDropSchema,
27159
27311
  // Score fields are nullable — the ranking engine returns null when there is
@@ -27393,7 +27545,7 @@ ${formatDeliveryPolicy(
27393
27545
  function registerNoteTools(server, ctx, client) {
27394
27546
  server.tool(
27395
27547
  "list_notes",
27396
- "List Notes for a system. Without beatId: returns all notes (system-level + beat-level). With beatId: returns only notes scoped to that beat. Filterable by type, status, revisionId, beatVersionId, and significance. Use offset/limit to paginate beat-scoped results; use cursor for project-wide results.",
27548
+ "List Notes for a system. Without beatId: returns all notes (system-level + beat-level). With beatId: returns only notes scoped to that beat. Filterable by type, status, revisionId, beatVersionId, and significance. Use offset/limit to paginate beat-scoped results; use cursor for system-wide results.",
27397
27549
  {
27398
27550
  systemId: external_exports.string().describe("The system ID"),
27399
27551
  beatId: external_exports.string().optional().describe("Scope to a specific beat (omit for all system notes)"),
@@ -27405,7 +27557,7 @@ function registerNoteTools(server, ctx, client) {
27405
27557
  significance: external_exports.enum(DECISION_SIGNIFICANCE_VALUES2).optional().describe("Filter decision Notes by significance tier (strategic | structural | implementation). Notes without a significance value are treated as implementation."),
27406
27558
  offset: external_exports.coerce.number().int().min(0).default(0).describe("Notes to skip for pagination (applies to beat-scoped and source-document-filtered results only)"),
27407
27559
  limit: external_exports.coerce.number().int().min(1).max(500).default(100).describe("Max notes to return (default 100, max 500)"),
27408
- cursor: external_exports.string().optional().describe("Opaque pagination cursor from a previous response \u2014 use for project-wide note pagination (no beatId)"),
27560
+ cursor: external_exports.string().optional().describe("Opaque pagination cursor from a previous response \u2014 use for system-wide note pagination (no beatId)"),
27409
27561
  orderBy: external_exports.enum(["newest", "oldest"]).optional().describe("Sort order \u2014 newest or oldest first. Applies to beat-scoped queries (beatId provided); omit to use DynamoDB scan order.")
27410
27562
  },
27411
27563
  async ({ systemId, beatId, noteType, status, revisionId, beatVersionId, sourceDocumentNoteId, significance, offset = 0, limit = 100, cursor, orderBy }) => {
@@ -27463,7 +27615,7 @@ function registerNoteTools(server, ctx, client) {
27463
27615
  allNotesResult = await client.listAllSystemNotes(systemId, filters, SOURCE_DOC_FETCH_LIMIT);
27464
27616
  } catch (err) {
27465
27617
  const message = err instanceof Error ? err.message : String(err);
27466
- return { content: [{ type: "text", text: `Failed to list project notes: ${message}` }], isError: true };
27618
+ return { content: [{ type: "text", text: `Failed to list system notes: ${message}` }], isError: true };
27467
27619
  }
27468
27620
  const { notes: allNotes, hasMore: corpusTruncated } = allNotesResult;
27469
27621
  const filtered = allNotes.filter((n) => n.sourceSubmissionId === sourceDocumentNoteId);
@@ -27471,7 +27623,7 @@ function registerNoteTools(server, ctx, client) {
27471
27623
  page = filtered.slice(offset, offset + limit);
27472
27624
  hasMore = offset + limit < total;
27473
27625
  const truncationWarning = corpusTruncated ? `
27474
- > \u26A0\uFE0F Project has >5000 notes; sourceDocumentNoteId filter may be incomplete.
27626
+ > \u26A0\uFE0F System has >5000 notes; sourceDocumentNoteId filter may be incomplete.
27475
27627
  ` : "";
27476
27628
  header = (hasMore ? `Showing ${offset + 1}\u2013${offset + page.length} of ${total} notes. Use offset=${offset + limit} to continue.
27477
27629
 
@@ -27484,7 +27636,7 @@ function registerNoteTools(server, ctx, client) {
27484
27636
  projectNotesResult = await client.listAllSystemNotes(systemId, filters, limit, cursor);
27485
27637
  } catch (err) {
27486
27638
  const message = err instanceof Error ? err.message : String(err);
27487
- return { content: [{ type: "text", text: `Failed to list project notes: ${message}` }], isError: true };
27639
+ return { content: [{ type: "text", text: `Failed to list system notes: ${message}` }], isError: true };
27488
27640
  }
27489
27641
  const { notes: rawNotes, cursor: nextCursor, hasMore: serviceHasMore } = projectNotesResult;
27490
27642
  hasMore = serviceHasMore;
@@ -27570,7 +27722,7 @@ function registerNoteTools(server, ctx, client) {
27570
27722
  const DOCUMENT_FETCH_LIMIT = 5e3;
27571
27723
  const { notes: docs, hasMore: truncated } = await client.listAllSystemNotes(systemId, { noteType: "document" }, DOCUMENT_FETCH_LIMIT);
27572
27724
  const visibleDocs = docs.filter((d) => d.status !== DISMISSED_NOTE_STATUS);
27573
- const truncationNotice = truncated && visibleDocs.length > 0 ? "\n\n> \u26A0\uFE0F This project has more than 5000 documents. Some documents may not be shown.\n" : "";
27725
+ const truncationNotice = truncated && visibleDocs.length > 0 ? "\n\n> \u26A0\uFE0F This system has more than 5000 documents. Some documents may not be shown.\n" : "";
27574
27726
  const text = formatDocumentList(visibleDocs) + truncationNotice;
27575
27727
  return { content: [{ type: "text", text }] };
27576
27728
  }
@@ -27619,8 +27771,8 @@ function registerNoteTools(server, ctx, client) {
27619
27771
  "Create a new Note. Provide exactly one of systemId, teamspaceId, movementId, or none (org-level): systemId scopes the note to a system (optionally with beatId), teamspaceId scopes it to a teamspace (no cascade to child systems), movementId scopes it to a Movement (commercial SOW container \u2014 engagement-level knowledge), and omitting all creates an org-level note that applies across all systems.",
27620
27772
  {
27621
27773
  systemId: external_exports.string().optional().describe("The system ID (omit for teamspace-, movement-, or org-level note)"),
27622
- teamspaceId: external_exports.string().optional().describe("The teamspace ID (omit for project-, movement-, or org-level note)"),
27623
- movementId: external_exports.string().min(1).optional().describe("The Movement ID (omit for project-, teamspace-, or org-level note)"),
27774
+ teamspaceId: external_exports.string().optional().describe("The teamspace ID (omit for system-, movement-, or org-level note)"),
27775
+ movementId: external_exports.string().min(1).optional().describe("The Movement ID (omit for system-, teamspace-, or org-level note)"),
27624
27776
  noteType: external_exports.enum(NOTE_TYPE_VALUES).describe("The type of note"),
27625
27777
  content: external_exports.string().describe("The note content"),
27626
27778
  beatId: external_exports.string().optional().describe("Beat ID if this note is beat-scoped (requires systemId)"),
@@ -27725,7 +27877,7 @@ ${text}` }] };
27725
27877
  content: external_exports.string().optional().describe("Updated content"),
27726
27878
  rationale: external_exports.string().optional().describe("Why this note exists or why this decision was made"),
27727
27879
  response: external_exports.string().optional().describe("User response (for resolving assumptions)"),
27728
- beatId: external_exports.string().nullable().optional().describe("Move this note to a different beat, or null to unscope it back to project level (updates all index items transactionally)"),
27880
+ beatId: external_exports.string().nullable().optional().describe("Move this note to a different beat, or null to unscope it back to system level (updates all index items transactionally)"),
27729
27881
  revisionId: external_exports.string().optional().describe("Revision ID to associate this note with (revision-scoped assumptions, decisions, or guidance)"),
27730
27882
  dependsOnNotes: external_exports.array(external_exports.string()).optional().describe("Note IDs this note depends on (child \u2192 parent links for consolidation)"),
27731
27883
  humanAssignee: humanAssigneeSchema.nullable().optional().describe("Assign or reassign this note to a person (null to clear)"),
@@ -28660,7 +28812,7 @@ function registerOnboardingTools(server, ctx, client) {
28660
28812
  server.tool(
28661
28813
  "create_teamspace_onboarding_batch",
28662
28814
  [
28663
- "Create a Teamspace, Projects, and Notes",
28815
+ "Create a Teamspace, Systems, and Notes",
28664
28816
  "from a PM-approved onboarding payload in a single batch operation.",
28665
28817
  "Call this after the PM approves the pre-flight review card from initiate_teamspace_onboarding.",
28666
28818
  "Returns a creation receipt with IDs and counts for all created entities.",
@@ -28668,8 +28820,8 @@ function registerOnboardingTools(server, ctx, client) {
28668
28820
  ].join(" "),
28669
28821
  {
28670
28822
  teamspaceName: external_exports.string().min(1).max(200).describe("Teamspace display name"),
28671
- projects: external_exports.array(OnboardingProjectInputSchema).min(1).describe("Phase 1 projects to create, each linked to the Teamspace"),
28672
- teamspaceNotes: external_exports.array(OnboardingNoteInputSchema).describe("Constraints, assumptions, and context notes at the Teamspace/project level")
28823
+ projects: external_exports.array(OnboardingProjectInputSchema).min(1).describe("Phase 1 systems to create, each linked to the Teamspace"),
28824
+ teamspaceNotes: external_exports.array(OnboardingNoteInputSchema).describe("Constraints, assumptions, and context notes at the Teamspace/system level")
28673
28825
  },
28674
28826
  async ({
28675
28827
  teamspaceName,
@@ -28710,7 +28862,7 @@ Error: ${msg}`);
28710
28862
  const msg = err instanceof Error ? err.message : String(err);
28711
28863
  lines.push(`
28712
28864
  Projects created: ${createdProjects.length}`);
28713
- lines.unshift(`\u26A0\uFE0F Onboarding batch partially created \u2014 stopped at: project "${p.title}"
28865
+ lines.unshift(`\u26A0\uFE0F Onboarding batch partially created \u2014 stopped at: system "${p.title}"
28714
28866
  Error: ${msg}`);
28715
28867
  return { content: [{ type: "text", text: lines.join("\n") }] };
28716
28868
  }
@@ -31579,6 +31731,337 @@ function registerWorkItemTools(server, _ctx, client) {
31579
31731
  );
31580
31732
  }
31581
31733
 
31734
+ // ../../libs/harmonica-services/src/mcp/tools/work-order-tools.ts
31735
+ var WORK_INTAKE_STATUS_ENUM = [
31736
+ "requested",
31737
+ "needs_decision",
31738
+ "accepted",
31739
+ "done"
31740
+ ];
31741
+ var NEEDS_DECISION_REASON_ENUM = [
31742
+ "out_of_scope",
31743
+ "exceeds_capacity",
31744
+ "incoherent_with_decision"
31745
+ ];
31746
+ var WORK_ORDER_SIZE_ENUM = [
31747
+ "XS",
31748
+ "S",
31749
+ "M",
31750
+ "L",
31751
+ "XL"
31752
+ ];
31753
+ function oneLine(value) {
31754
+ return value.replace(/[\r\n]+/g, " ");
31755
+ }
31756
+ function formatOrderLine(o) {
31757
+ const reason = o.needsDecisionReason ? ` [${o.needsDecisionReason}]` : "";
31758
+ return `[${o.workOrderId}] ${oneLine(o.title)} \u2014 ${o.status}${reason}, ${o.size ?? "unsized"}, on ${o.trackId}`;
31759
+ }
31760
+ function registerWorkOrderTools(server, _ctx, client) {
31761
+ server.tool(
31762
+ "list_track_work_orders",
31763
+ 'List the Work Orders placed on a Track (DeliverableGroup). A Work Order is the internal unit of agreed work. Recognition cues: "what is committed on this track", "list the orders" \u2192 list_track_work_orders.',
31764
+ {
31765
+ trackId: external_exports.string().describe("The Track (DeliverableGroup) ID")
31766
+ },
31767
+ async ({ trackId }) => {
31768
+ const orders = await client.listTrackWorkOrders(trackId);
31769
+ const text = orders.length === 0 ? "No work orders found." : orders.map(formatOrderLine).join("\n");
31770
+ return { content: [{ type: "text", text }] };
31771
+ }
31772
+ );
31773
+ server.tool(
31774
+ "list_work_request_work_orders",
31775
+ "List the Work Orders one Work Request split into \u2014 the lineage view. A four-way split shows four orders here, each possibly on a different Track and in a different state.",
31776
+ {
31777
+ workRequestId: external_exports.string().describe("The Work Request ID")
31778
+ },
31779
+ async ({ workRequestId }) => {
31780
+ const orders = await client.listWorkRequestWorkOrders(workRequestId);
31781
+ const text = orders.length === 0 ? "No work orders found \u2014 this request has not been split yet." : orders.map(formatOrderLine).join("\n");
31782
+ return { content: [{ type: "text", text }] };
31783
+ }
31784
+ );
31785
+ server.tool(
31786
+ "get_work_order",
31787
+ "Get a Work Order by ID. Returns its title, status (with the routing reason when it is awaiting a decision), t-shirt size, the Track it sits on, and the Work Request it was split from.",
31788
+ {
31789
+ workOrderId: external_exports.string().describe("The Work Order ID (e.g., wo-abc123)")
31790
+ },
31791
+ async ({ workOrderId }) => {
31792
+ const wo = await client.getWorkOrder(workOrderId);
31793
+ if (!wo) {
31794
+ return { content: [{ type: "text", text: `Work order not found: ${workOrderId}` }] };
31795
+ }
31796
+ const lines = [
31797
+ `ID: ${wo.workOrderId}`,
31798
+ `Title: ${oneLine(wo.title)}`,
31799
+ `From Work Request: ${wo.workRequestId}`,
31800
+ `Track: ${wo.trackId}`,
31801
+ `Status: ${wo.status}`,
31802
+ wo.needsDecisionReason ? `Routing Reason: ${wo.needsDecisionReason}` : null,
31803
+ wo.size !== void 0 ? `Size: ${wo.size}` : "Size: unsized",
31804
+ wo.description ? `Description: ${oneLine(wo.description)}` : null,
31805
+ wo.createdBy ? `Created By: ${wo.createdBy}` : null,
31806
+ `Created: ${wo.createdAt}`,
31807
+ `Updated: ${wo.updatedAt}`
31808
+ ].filter(Boolean).join("\n");
31809
+ return { content: [{ type: "text", text: lines }] };
31810
+ }
31811
+ );
31812
+ server.tool(
31813
+ "create_work_order",
31814
+ 'Split a Work Request into an agreed Work Order on a Track. Called once per piece: a request covering two separate lines of work becomes two orders. Starts in "requested" status \u2014 moving it past the coherence gate is transition_work_order_status. The Track must belong to the same teamspace and Account as the Request; the service refuses a placement that crosses either boundary.',
31815
+ {
31816
+ trackId: external_exports.string().describe("The Track (DeliverableGroup) this order is placed on"),
31817
+ workRequestId: external_exports.string().min(1).describe("The Work Request this order was split from \u2014 immutable lineage"),
31818
+ title: external_exports.string().min(1).max(300).describe("Work order title"),
31819
+ description: external_exports.string().max(1e4).optional().describe("Optional detail beyond the title"),
31820
+ size: external_exports.enum(WORK_ORDER_SIZE_ENUM).optional().describe("Optional t-shirt size. An order can arrive before it is sized; a request's composition counts unsized orders rather than hiding them."),
31821
+ createdBy: external_exports.string().optional().describe("Optional creator identity for audit")
31822
+ },
31823
+ async ({ trackId, workRequestId, title, description, size, createdBy }) => {
31824
+ const wo = await client.createWorkOrder({
31825
+ trackId,
31826
+ workRequestId,
31827
+ title,
31828
+ ...description !== void 0 && { description },
31829
+ ...size !== void 0 && { size },
31830
+ ...createdBy !== void 0 && { createdBy }
31831
+ });
31832
+ return {
31833
+ content: [{ type: "text", text: `Created work order: ${formatOrderLine(wo)}` }]
31834
+ };
31835
+ }
31836
+ );
31837
+ server.tool(
31838
+ "update_work_order",
31839
+ "Update content fields on a Work Order (title, description, size). To move it between Tracks use move_work_order; to change its status use transition_work_order_status. The Work Request it came from cannot be changed \u2014 that lineage is immutable.",
31840
+ {
31841
+ workOrderId: external_exports.string().describe("The Work Order ID"),
31842
+ title: external_exports.string().min(1).max(300).optional().describe("New title"),
31843
+ description: external_exports.string().max(1e4).nullable().optional().describe("New description. Pass null to clear; omit to leave untouched."),
31844
+ size: external_exports.enum(WORK_ORDER_SIZE_ENUM).nullable().optional().describe("New t-shirt size. Pass null to return it to unsized; omit to leave untouched.")
31845
+ },
31846
+ async ({ workOrderId, title, description, size }) => {
31847
+ if (title === void 0 && description === void 0 && size === void 0) {
31848
+ return {
31849
+ content: [
31850
+ { type: "text", text: "Nothing to update: pass at least one of title, description, or size." }
31851
+ ],
31852
+ isError: true
31853
+ };
31854
+ }
31855
+ const wo = await client.updateWorkOrder(workOrderId, {
31856
+ ...title !== void 0 && { title },
31857
+ ...description !== void 0 && { description },
31858
+ ...size !== void 0 && { size }
31859
+ });
31860
+ if (!wo) {
31861
+ return { content: [{ type: "text", text: `Work order not found: ${workOrderId}` }] };
31862
+ }
31863
+ return { content: [{ type: "text", text: `Updated work order: ${formatOrderLine(wo)}` }] };
31864
+ }
31865
+ );
31866
+ server.tool(
31867
+ "move_work_order",
31868
+ "Move a Work Order to a different Track \u2014 the reversal path for a bad early split guess. The destination must belong to the same teamspace and Account as the order's Work Request; the service refuses a placement that crosses either boundary.",
31869
+ {
31870
+ workOrderId: external_exports.string().describe("The Work Order ID"),
31871
+ newTrackId: external_exports.string().describe("The destination Track (DeliverableGroup) ID")
31872
+ },
31873
+ async ({ workOrderId, newTrackId }) => {
31874
+ const wo = await client.moveWorkOrder(workOrderId, newTrackId);
31875
+ if (!wo) {
31876
+ return { content: [{ type: "text", text: `Work order not found: ${workOrderId}` }] };
31877
+ }
31878
+ return {
31879
+ content: [{ type: "text", text: `Moved work order to Track ${wo.trackId}: ${formatOrderLine(wo)}` }]
31880
+ };
31881
+ }
31882
+ );
31883
+ server.tool(
31884
+ "transition_work_order_status",
31885
+ "Move a Work Order through the intake gate: requested \u2192 accepted is the coherence gate itself, and there is no shortcut past it. Routing an order to needs_decision REQUIRES a reason, because the reason is the routing: out_of_scope goes back to the customer, exceeds_capacity goes to the Orchestrator, incoherent_with_decision goes back for reframing. A routed order returns to requested once resolved.",
31886
+ {
31887
+ workOrderId: external_exports.string().describe("The Work Order ID"),
31888
+ status: external_exports.enum(WORK_INTAKE_STATUS_ENUM).describe("Target status"),
31889
+ reason: external_exports.enum(NEEDS_DECISION_REASON_ENUM).optional().describe("Required when \u2014 and only when \u2014 status is needs_decision. The reason IS the routing.")
31890
+ },
31891
+ async ({ workOrderId, status, reason }) => {
31892
+ if (status === "needs_decision" && reason === void 0) {
31893
+ return {
31894
+ content: [
31895
+ {
31896
+ type: "text",
31897
+ text: "Routing an order to needs_decision requires a reason \u2014 the reason is the routing. Pass one of: out_of_scope, exceeds_capacity, incoherent_with_decision."
31898
+ }
31899
+ ],
31900
+ isError: true
31901
+ };
31902
+ }
31903
+ if (status !== "needs_decision" && reason !== void 0) {
31904
+ return {
31905
+ content: [
31906
+ {
31907
+ type: "text",
31908
+ text: `A routing reason is only meaningful on a transition to needs_decision, not to ${status}.`
31909
+ }
31910
+ ],
31911
+ isError: true
31912
+ };
31913
+ }
31914
+ const wo = await client.transitionWorkOrderStatus(workOrderId, status, reason);
31915
+ if (!wo) {
31916
+ return { content: [{ type: "text", text: `Work order not found: ${workOrderId}` }] };
31917
+ }
31918
+ return {
31919
+ content: [{ type: "text", text: `Transitioned work order: ${formatOrderLine(wo)}` }]
31920
+ };
31921
+ }
31922
+ );
31923
+ }
31924
+
31925
+ // ../../libs/harmonica-services/src/mcp/tools/work-request-tools.ts
31926
+ var WORK_REQUEST_ORIGIN_ENUM = [
31927
+ "client",
31928
+ "internal"
31929
+ ];
31930
+ function oneLine2(value) {
31931
+ return value.replace(/[\r\n]+/g, " ");
31932
+ }
31933
+ function formatSizeComposition(size) {
31934
+ const parts = size.entries.map((e) => `${e.count}${e.size}`);
31935
+ if (size.unsized > 0) parts.push(`${size.unsized} unsized`);
31936
+ if (parts.length === 0) return "no orders yet";
31937
+ return `${parts.join(" + ")} (${size.totalValueUnits} value units)`;
31938
+ }
31939
+ function registerWorkRequestTools(server, _ctx, client) {
31940
+ server.tool(
31941
+ "list_movement_work_requests",
31942
+ `List the Work Requests filed against a Movement. A Work Request is the customer's ask in their own language, before it is sized or agreed. Recognition cues: "what has the client asked for", "list the requests on this engagement" \u2192 list_movement_work_requests.`,
31943
+ {
31944
+ movementId: external_exports.string().describe("The Movement (engagement) ID")
31945
+ },
31946
+ async ({ movementId }) => {
31947
+ const requests = await client.listMovementWorkRequests(movementId);
31948
+ const text = requests.length === 0 ? "No work requests found." : requests.map((wr) => `[${wr.workRequestId}] ${oneLine2(wr.title)} \u2014 from ${oneLine2(wr.requester.name)} (${wr.origin})`).join("\n");
31949
+ return { content: [{ type: "text", text }] };
31950
+ }
31951
+ );
31952
+ server.tool(
31953
+ "get_work_request",
31954
+ "Get a Work Request with the Work Orders it split into and their combined size composition. Note there is no request-level status: each Order carries its own, because a split whose halves sit in different states cannot be reduced to one word.",
31955
+ {
31956
+ workRequestId: external_exports.string().describe("The Work Request ID (e.g., wr-abc123)")
31957
+ },
31958
+ async ({ workRequestId }) => {
31959
+ const detail = await client.getWorkRequestDetail(workRequestId);
31960
+ if (!detail) {
31961
+ return { content: [{ type: "text", text: `Work request not found: ${workRequestId}` }] };
31962
+ }
31963
+ const { workRequest: wr, orders, size } = detail;
31964
+ const orderLines = orders.length === 0 ? ["Orders: none yet \u2014 this request has not been split"] : [
31965
+ "Orders:",
31966
+ ...orders.map((o) => {
31967
+ const track = o.trackTitle ? `${oneLine2(o.trackTitle)} (${o.trackId})` : o.trackId;
31968
+ const reason = o.needsDecisionReason ? ` [${o.needsDecisionReason}]` : "";
31969
+ const sized = o.size ?? "unsized";
31970
+ return ` [${o.workOrderId}] ${oneLine2(o.title)} \u2014 ${o.status}${reason}, ${sized}, on ${track}`;
31971
+ })
31972
+ ];
31973
+ const lines = [
31974
+ `ID: ${wr.workRequestId}`,
31975
+ `Title: ${oneLine2(wr.title)}`,
31976
+ `Movement: ${wr.movementId}`,
31977
+ `Requester: ${oneLine2(wr.requester.name)} <${wr.requester.email}>`,
31978
+ `Origin: ${wr.origin}`,
31979
+ wr.body ? `Ask: ${oneLine2(wr.body)}` : null,
31980
+ `Size: ${formatSizeComposition(size)}`,
31981
+ ...orderLines,
31982
+ wr.createdBy ? `Created By: ${wr.createdBy}` : null,
31983
+ `Created: ${wr.createdAt}`,
31984
+ `Updated: ${wr.updatedAt}`
31985
+ ].filter(Boolean).join("\n");
31986
+ return { content: [{ type: "text", text: lines }] };
31987
+ }
31988
+ );
31989
+ server.tool(
31990
+ "create_work_request",
31991
+ "File a Work Request against a Movement \u2014 how work arrives, in the requester's own language, before it is sized or agreed. Splitting it into Work Orders is a separate step (create_work_order).",
31992
+ {
31993
+ movementId: external_exports.string().describe("The Movement (engagement) this request is filed against"),
31994
+ title: external_exports.string().min(1).max(300).describe("Short title for the ask"),
31995
+ body: external_exports.string().max(1e4).optional().describe("The ask as written, in the requester's own words"),
31996
+ requesterName: external_exports.string().min(1).describe("Who is asking \u2014 display name"),
31997
+ requesterEmail: external_exports.string().email().describe("Who is asking \u2014 email"),
31998
+ origin: external_exports.enum(WORK_REQUEST_ORIGIN_ENUM).describe('Where the ask came from: "client" or "internal". One entity covers both \u2014 only the entry point differs.'),
31999
+ createdBy: external_exports.string().optional().describe("Optional creator identity for audit")
32000
+ },
32001
+ async ({ movementId, title, body, requesterName, requesterEmail, origin, createdBy }) => {
32002
+ const wr = await client.createWorkRequest({
32003
+ movementId,
32004
+ title,
32005
+ ...body !== void 0 && { body },
32006
+ requester: { name: requesterName, email: requesterEmail },
32007
+ origin,
32008
+ ...createdBy !== void 0 && { createdBy }
32009
+ });
32010
+ return {
32011
+ content: [
32012
+ {
32013
+ type: "text",
32014
+ text: `Filed work request: [${wr.workRequestId}] ${wr.title} \u2014 from ${wr.requester.name} (${wr.origin})`
32015
+ }
32016
+ ]
32017
+ };
32018
+ }
32019
+ );
32020
+ server.tool(
32021
+ "update_work_request",
32022
+ "Reword a Work Request (title, body, requester). A Request cannot be moved to another Movement, and has no status to set \u2014 its Orders carry the state.",
32023
+ {
32024
+ workRequestId: external_exports.string().describe("The Work Request ID"),
32025
+ title: external_exports.string().min(1).max(300).optional().describe("New title"),
32026
+ body: external_exports.string().max(1e4).nullable().optional().describe("The ask as written. Pass null to clear; omit to leave untouched."),
32027
+ requesterName: external_exports.string().min(1).optional().describe("New requester display name (requires requesterEmail too)"),
32028
+ requesterEmail: external_exports.string().email().optional().describe("New requester email (requires requesterName too)")
32029
+ },
32030
+ async ({ workRequestId, title, body, requesterName, requesterEmail }) => {
32031
+ if (requesterName === void 0 !== (requesterEmail === void 0)) {
32032
+ return {
32033
+ content: [
32034
+ {
32035
+ type: "text",
32036
+ text: "Changing the requester requires both requesterName and requesterEmail."
32037
+ }
32038
+ ],
32039
+ isError: true
32040
+ };
32041
+ }
32042
+ if (title === void 0 && body === void 0 && requesterName === void 0) {
32043
+ return {
32044
+ content: [
32045
+ { type: "text", text: "Nothing to update: pass at least one of title, body, or requester." }
32046
+ ],
32047
+ isError: true
32048
+ };
32049
+ }
32050
+ const wr = await client.updateWorkRequest(workRequestId, {
32051
+ ...title !== void 0 && { title },
32052
+ ...body !== void 0 && { body },
32053
+ ...requesterName !== void 0 && requesterEmail !== void 0 && { requester: { name: requesterName, email: requesterEmail } }
32054
+ });
32055
+ if (!wr) {
32056
+ return { content: [{ type: "text", text: `Work request not found: ${workRequestId}` }] };
32057
+ }
32058
+ return {
32059
+ content: [{ type: "text", text: `Updated work request: [${wr.workRequestId}] ${wr.title}` }]
32060
+ };
32061
+ }
32062
+ );
32063
+ }
32064
+
31582
32065
  // ../../libs/harmonica-services/src/mcp/tools/workflow-tools.ts
31583
32066
  var TASK_STATUSES = ["pending", "running", "completed", "failed"];
31584
32067
  function registerWorkflowTools(server, ctx, client) {
@@ -32043,6 +32526,8 @@ function registerAllTools(server, ctx, client, profile) {
32043
32526
  registerAccountTools(server, ctx, client);
32044
32527
  registerMovementTools(server, ctx, client);
32045
32528
  registerWorkItemTools(server, ctx, client);
32529
+ registerWorkRequestTools(server, ctx, client);
32530
+ registerWorkOrderTools(server, ctx, client);
32046
32531
  registerBarTools(server, ctx, client);
32047
32532
  registerCadenceScheduleTools(server, ctx, client);
32048
32533
  registerPulseReportTools(server, ctx, client);
@@ -33766,6 +34251,134 @@ function createHttpClient(config2) {
33766
34251
  if (!parsed.success) throw new Error(`Unexpected WorkItem shape from API: ${parsed.error.message}`);
33767
34252
  return parsed.data;
33768
34253
  },
34254
+ // Work intake (B-331) — against the endpoints the API layer shipped in v3.
34255
+ createWorkRequest: async (input) => {
34256
+ const { movementId, title, body: askBody, requester, origin, createdBy } = input;
34257
+ const body = {
34258
+ title,
34259
+ ...askBody !== void 0 && { body: askBody },
34260
+ requester,
34261
+ origin,
34262
+ ...createdBy !== void 0 && { createdBy }
34263
+ };
34264
+ const res = await request(
34265
+ "POST",
34266
+ `/api/movements/${encodeURIComponent(movementId)}/work-requests`,
34267
+ body
34268
+ );
34269
+ if (res === void 0) throw new Error(`Movement ${movementId} not found`);
34270
+ const parsed = WorkRequestApiSchema.safeParse(res.workRequest);
34271
+ if (!parsed.success) throw new Error(`Unexpected WorkRequest shape from API: ${parsed.error.message}`);
34272
+ return parsed.data;
34273
+ },
34274
+ getWorkRequest: async (workRequestId) => {
34275
+ const res = await request(
34276
+ "GET",
34277
+ `/api/work-requests/${encodeURIComponent(workRequestId)}`
34278
+ );
34279
+ if (res === void 0) return void 0;
34280
+ const parsed = WorkRequestApiSchema.safeParse(res.workRequest);
34281
+ if (!parsed.success) throw new Error(`Unexpected WorkRequest shape from API: ${parsed.error.message}`);
34282
+ return parsed.data;
34283
+ },
34284
+ getWorkRequestDetail: async (workRequestId) => {
34285
+ const res = await request("GET", `/api/work-requests/${encodeURIComponent(workRequestId)}`);
34286
+ if (res === void 0) return void 0;
34287
+ const parsed = WorkRequestDetailResponseSchema.safeParse(res);
34288
+ if (!parsed.success) throw new Error(`Unexpected WorkRequest detail shape from API: ${parsed.error.message}`);
34289
+ return parsed.data;
34290
+ },
34291
+ updateWorkRequest: async (workRequestId, updates) => {
34292
+ const res = await request(
34293
+ "PATCH",
34294
+ `/api/work-requests/${encodeURIComponent(workRequestId)}`,
34295
+ updates
34296
+ );
34297
+ if (res === void 0) return void 0;
34298
+ const parsed = WorkRequestApiSchema.safeParse(res.workRequest);
34299
+ if (!parsed.success) throw new Error(`Unexpected WorkRequest shape from API: ${parsed.error.message}`);
34300
+ return parsed.data;
34301
+ },
34302
+ listMovementWorkRequests: async (movementId) => {
34303
+ const res = await request("GET", `/api/movements/${encodeURIComponent(movementId)}/work-requests`);
34304
+ if (res === void 0) throw new Error(`Movement ${movementId} not found`);
34305
+ const parsed = WorkRequestsResponseSchema.safeParse(res);
34306
+ if (!parsed.success) throw new Error(`Unexpected work requests list shape from API: ${parsed.error.message}`);
34307
+ return parsed.data.workRequests;
34308
+ },
34309
+ createWorkOrder: async (input) => {
34310
+ const { trackId, workRequestId, title, description, size, createdBy } = input;
34311
+ const body = {
34312
+ workRequestId,
34313
+ title,
34314
+ ...description !== void 0 && { description },
34315
+ ...size !== void 0 && { size },
34316
+ ...createdBy !== void 0 && { createdBy }
34317
+ };
34318
+ const res = await request(
34319
+ "POST",
34320
+ `/api/deliverable-groups/${encodeURIComponent(trackId)}/work-orders`,
34321
+ body
34322
+ );
34323
+ if (res === void 0) throw new Error(`Track ${trackId} not found`);
34324
+ const parsed = WorkOrderApiSchema.safeParse(res.workOrder);
34325
+ if (!parsed.success) throw new Error(`Unexpected WorkOrder shape from API: ${parsed.error.message}`);
34326
+ return parsed.data;
34327
+ },
34328
+ getWorkOrder: async (workOrderId) => {
34329
+ const res = await request("GET", `/api/work-orders/${encodeURIComponent(workOrderId)}`);
34330
+ if (res === void 0) return void 0;
34331
+ const parsed = WorkOrderApiSchema.safeParse(res.workOrder);
34332
+ if (!parsed.success) throw new Error(`Unexpected WorkOrder shape from API: ${parsed.error.message}`);
34333
+ return parsed.data;
34334
+ },
34335
+ updateWorkOrder: async (workOrderId, updates) => {
34336
+ const res = await request(
34337
+ "PATCH",
34338
+ `/api/work-orders/${encodeURIComponent(workOrderId)}`,
34339
+ updates
34340
+ );
34341
+ if (res === void 0) return void 0;
34342
+ const parsed = WorkOrderApiSchema.safeParse(res.workOrder);
34343
+ if (!parsed.success) throw new Error(`Unexpected WorkOrder shape from API: ${parsed.error.message}`);
34344
+ return parsed.data;
34345
+ },
34346
+ listTrackWorkOrders: async (trackId) => {
34347
+ const res = await request("GET", `/api/deliverable-groups/${encodeURIComponent(trackId)}/work-orders`);
34348
+ if (res === void 0) throw new Error(`Track ${trackId} not found`);
34349
+ const parsed = WorkOrdersResponseSchema.safeParse(res);
34350
+ if (!parsed.success) throw new Error(`Unexpected work orders list shape from API: ${parsed.error.message}`);
34351
+ return parsed.data.workOrders;
34352
+ },
34353
+ listWorkRequestWorkOrders: async (workRequestId) => {
34354
+ const res = await request("GET", `/api/work-requests/${encodeURIComponent(workRequestId)}/work-orders`);
34355
+ if (res === void 0) throw new Error(`Work request ${workRequestId} not found`);
34356
+ const parsed = WorkOrdersResponseSchema.safeParse(res);
34357
+ if (!parsed.success) throw new Error(`Unexpected work orders list shape from API: ${parsed.error.message}`);
34358
+ return parsed.data.workOrders;
34359
+ },
34360
+ moveWorkOrder: async (workOrderId, newTrackId) => {
34361
+ const res = await request(
34362
+ "POST",
34363
+ `/api/work-orders/${encodeURIComponent(workOrderId)}/move`,
34364
+ { newTrackId }
34365
+ );
34366
+ if (res === void 0) return void 0;
34367
+ const parsed = WorkOrderApiSchema.safeParse(res.workOrder);
34368
+ if (!parsed.success) throw new Error(`Unexpected WorkOrder shape from API: ${parsed.error.message}`);
34369
+ return parsed.data;
34370
+ },
34371
+ transitionWorkOrderStatus: async (workOrderId, toStatus, reason) => {
34372
+ const res = await request(
34373
+ "POST",
34374
+ `/api/work-orders/${encodeURIComponent(workOrderId)}/status`,
34375
+ { status: toStatus, ...reason !== void 0 && { reason } }
34376
+ );
34377
+ if (res === void 0) return void 0;
34378
+ const parsed = WorkOrderApiSchema.safeParse(res.workOrder);
34379
+ if (!parsed.success) throw new Error(`Unexpected WorkOrder shape from API: ${parsed.error.message}`);
34380
+ return parsed.data;
34381
+ },
33769
34382
  // Accounts (B-242 / org-scoped paying client entity that owns Movements)
33770
34383
  createAccount: async (input) => {
33771
34384
  const { orgId, name, description, createdBy, parentAccountId, accountType, accountCode } = input;
@@ -34317,7 +34930,7 @@ function loadConfig() {
34317
34930
  };
34318
34931
  }
34319
34932
  async function main() {
34320
- console.error(`[harmonica-mcp] v${"3.8.6"} starting\u2026`);
34933
+ console.error(`[harmonica-mcp] v${"3.9.0"} starting\u2026`);
34321
34934
  const config2 = loadConfig();
34322
34935
  const client = createHttpClient({
34323
34936
  apiBaseUrl: config2.apiBaseUrl,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codazen/harmonica-mcp",
3
- "version": "3.8.6",
3
+ "version": "3.9.0",
4
4
  "description": "MCP server for Harmonica — connect any MCP-compatible AI assistant to Harmonica",
5
5
  "license": "MIT",
6
6
  "bin": {