@codazen/harmonica-mcp 3.6.1 → 3.7.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.
Files changed (2) hide show
  1. package/dist/index.js +403 -370
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -21542,25 +21542,25 @@ async function validateAuth(client, orgIdentifier, isOrgSlug, userId) {
21542
21542
  }
21543
21543
 
21544
21544
  // ../../libs/harmonica-services/src/mcp/org-guard.ts
21545
- async function assertProjectInOrg(client, projectId, orgId) {
21546
- const project = await client.getSystem(projectId);
21545
+ async function assertProjectInOrg(client, systemId, orgId) {
21546
+ const project = await client.getSystem(systemId);
21547
21547
  if (!project) {
21548
- throw new Error(`Project not found: "${projectId}"`);
21548
+ throw new Error(`Project not found: "${systemId}"`);
21549
21549
  }
21550
21550
  if (project.orgId !== orgId) {
21551
21551
  throw new Error(
21552
- `Project "${projectId}" does not belong to the configured organization. Access denied.`
21552
+ `Project "${systemId}" does not belong to the configured organization. Access denied.`
21553
21553
  );
21554
21554
  }
21555
21555
  }
21556
- async function assertRevisionInProject(client, revisionId, projectId) {
21556
+ async function assertRevisionInProject(client, revisionId, systemId) {
21557
21557
  const revision = await client.getRevision(revisionId);
21558
21558
  if (!revision) {
21559
21559
  throw new Error(`Revision not found: "${revisionId}"`);
21560
21560
  }
21561
- if (revision.projectId !== projectId) {
21561
+ if (revision.systemId !== systemId) {
21562
21562
  throw new Error(
21563
- `Revision "${revisionId}" does not belong to project "${projectId}". Access denied.`
21563
+ `Revision "${revisionId}" does not belong to project "${systemId}". Access denied.`
21564
21564
  );
21565
21565
  }
21566
21566
  }
@@ -21575,14 +21575,14 @@ async function assertBeatInOrg(client, beatId, orgId) {
21575
21575
  );
21576
21576
  }
21577
21577
  }
21578
- async function fetchProjectInOrg(client, projectId, orgId) {
21579
- const project = await client.getSystem(projectId);
21578
+ async function fetchProjectInOrg(client, systemId, orgId) {
21579
+ const project = await client.getSystem(systemId);
21580
21580
  if (!project) {
21581
- throw new Error(`Project not found: "${projectId}"`);
21581
+ throw new Error(`Project not found: "${systemId}"`);
21582
21582
  }
21583
21583
  if (project.orgId !== orgId) {
21584
21584
  throw new Error(
21585
- `Project "${projectId}" does not belong to the configured organization. Access denied.`
21585
+ `Project "${systemId}" does not belong to the configured organization. Access denied.`
21586
21586
  );
21587
21587
  }
21588
21588
  return project;
@@ -21974,13 +21974,13 @@ function registerActivityTools(server, ctx, client) {
21974
21974
  "list_activities",
21975
21975
  "List project activity audit trail \u2014 shows beat changes, revision transitions, note operations, merges, splits, and reassignments. Sorted newest-first.",
21976
21976
  {
21977
- projectId: external_exports.string().describe("The project ID"),
21977
+ systemId: external_exports.string().describe("The project ID"),
21978
21978
  limit: external_exports.coerce.number().min(1).max(100).optional().describe("Max activities to return (default 30)"),
21979
21979
  importance: external_exports.enum(IMPORTANCE_VALUES).optional().describe("Filter by minimum importance level")
21980
21980
  },
21981
- async ({ projectId, limit, importance }) => {
21982
- await assertProjectInOrg(client, projectId, ctx.orgId);
21983
- const { entries, hasMore } = await client.listSystemActivities(projectId, {
21981
+ async ({ systemId, limit, importance }) => {
21982
+ await assertProjectInOrg(client, systemId, ctx.orgId);
21983
+ const { entries, hasMore } = await client.listSystemActivities(systemId, {
21984
21984
  limit: limit ?? 30,
21985
21985
  minImportance: importance
21986
21986
  });
@@ -22005,14 +22005,14 @@ function registerAnalysisTools(server, ctx, client) {
22005
22005
  "cluster_entities",
22006
22006
  "Cluster any embeddable entity type (notes, beats, revisions) by vector similarity using DBSCAN. Returns groups of related entities \u2014 useful for understanding subsystem boundaries, finding duplicates, and planning batched work.",
22007
22007
  {
22008
- projectId: external_exports.string().describe("The project ID"),
22008
+ systemId: external_exports.string().describe("The project ID"),
22009
22009
  entityType: ENTITY_TYPE_SCHEMA.describe("Entity type to cluster. Default: revision"),
22010
22010
  threshold: external_exports.coerce.number().min(0).max(1).optional().default(0.75).describe("DBSCAN similarity threshold (0-1). Higher = tighter clusters. Default 0.75"),
22011
22011
  minClusterSize: external_exports.coerce.number().int().min(2).optional().default(3).describe("Minimum cluster size. Default 3")
22012
22012
  },
22013
- async ({ projectId, entityType, threshold, minClusterSize }) => {
22014
- await assertProjectInOrg(client, projectId, ctx.orgId);
22015
- const result = await client.clusterEntities(projectId, entityType, { threshold, minClusterSize });
22013
+ async ({ systemId, entityType, threshold, minClusterSize }) => {
22014
+ await assertProjectInOrg(client, systemId, ctx.orgId);
22015
+ const result = await client.clusterEntities(systemId, entityType, { threshold, minClusterSize });
22016
22016
  if (result.clusters.length === 0) {
22017
22017
  return {
22018
22018
  content: [{ type: "text", text: `No clusters found (${result.totalAnalyzed} ${entityType}s analyzed, ${result.noise.length} unclustered).` }]
@@ -22036,15 +22036,15 @@ Analyzed: ${result.totalAnalyzed} | Clusters: ${result.clusters.length} | Noise:
22036
22036
  "run_clustered_analysis",
22037
22037
  "Run batched analysis on clustered project entities. Groups entities by vector similarity, then runs a specified analysis type (docs, completeness, tech_debt, security) per cluster. Returns aggregated results.",
22038
22038
  {
22039
- projectId: external_exports.string().describe("The project ID"),
22039
+ systemId: external_exports.string().describe("The project ID"),
22040
22040
  analysisType: ANALYSIS_TYPE_SCHEMA.describe("Type of analysis to run"),
22041
22041
  entityType: ENTITY_TYPE_SCHEMA.describe("Entity type to cluster. Default: revision"),
22042
22042
  threshold: external_exports.coerce.number().min(0).max(1).optional().default(0.7).describe("DBSCAN similarity threshold (0-1). Default 0.7"),
22043
22043
  minClusterSize: external_exports.coerce.number().int().min(2).optional().default(2).describe("Minimum cluster size. Default 2")
22044
22044
  },
22045
- async ({ projectId, analysisType, entityType, threshold, minClusterSize }) => {
22046
- await assertProjectInOrg(client, projectId, ctx.orgId);
22047
- const result = await client.runClusteredAnalysis(projectId, analysisType, {
22045
+ async ({ systemId, analysisType, entityType, threshold, minClusterSize }) => {
22046
+ await assertProjectInOrg(client, systemId, ctx.orgId);
22047
+ const result = await client.runClusteredAnalysis(systemId, analysisType, {
22048
22048
  entityType,
22049
22049
  threshold,
22050
22050
  minClusterSize
@@ -22073,13 +22073,13 @@ function registerAuditIntegrityTools(server, ctx, client) {
22073
22073
  "verify_audit_integrity",
22074
22074
  "Verify the cryptographic integrity of audit trail records for a project. Checks SHA-256 hashes on Activity and PromptLog records to detect any post-creation tampering.",
22075
22075
  {
22076
- projectId: external_exports.string().describe("The project ID to verify"),
22076
+ systemId: external_exports.string().describe("The project ID to verify"),
22077
22077
  limit: external_exports.number().optional().describe("Max records to verify (default 100)")
22078
22078
  },
22079
- async ({ projectId, limit }) => {
22079
+ async ({ systemId, limit }) => {
22080
22080
  try {
22081
- await assertProjectInOrg(client, projectId, ctx.orgId);
22082
- const report = await client.verifyAuditIntegrity(projectId, { limit });
22081
+ await assertProjectInOrg(client, systemId, ctx.orgId);
22082
+ const report = await client.verifyAuditIntegrity(systemId, { limit });
22083
22083
  const lines = [
22084
22084
  `## Audit Integrity Report`,
22085
22085
  ``,
@@ -22287,16 +22287,16 @@ Rationale: ${rationale}`;
22287
22287
  "bulk_reassign_revisions",
22288
22288
  "Reassign multiple revisions to new home Beats in a single call. Each item names a revision and its target Beat (different items can target different Beats \u2014 e.g., redistributing one Beat's revisions across several after decomposition). Each reassignment archives the original revision and creates a copy on the target Beat, identical to reassign_revision. Returns per-item success/failure so partial failures don't abort the batch. Use when migrating many revisions at once after a Beat split.",
22289
22289
  {
22290
- projectId: external_exports.string().describe("The project ID (for access control \u2014 all revisions and target beats must belong to this project)"),
22290
+ systemId: external_exports.string().describe("The project ID (for access control \u2014 all revisions and target beats must belong to this project)"),
22291
22291
  items: external_exports.array(external_exports.object({
22292
22292
  revisionId: external_exports.string().describe("The revision ID to reassign"),
22293
22293
  toBeatId: external_exports.string().describe("The target beat ID for this specific revision")
22294
22294
  })).min(1).max(100).describe("Reassignment items (max 100 per call)"),
22295
22295
  reason: external_exports.string().describe("Why these revisions are being reassigned (applied to all items)")
22296
22296
  },
22297
- async ({ projectId, items, reason }) => {
22297
+ async ({ systemId, items, reason }) => {
22298
22298
  try {
22299
- await assertProjectInOrg(client, projectId, ctx.orgId);
22299
+ await assertProjectInOrg(client, systemId, ctx.orgId);
22300
22300
  const result = await client.bulkReassignRevisions(items, reason, ctx.user.userId);
22301
22301
  const lines = [
22302
22302
  `# Bulk reassignment complete: ${result.reassigned.length} succeeded, ${result.failed.length} failed`,
@@ -22328,14 +22328,14 @@ Rationale: ${rationale}`;
22328
22328
  "reassign_revision",
22329
22329
  "Reassign a misattributed revision from one Beat to another. Creates a copy on the target Beat and archives the original. Logs a revision_reassigned activity for audit trail.",
22330
22330
  {
22331
- projectId: external_exports.string().describe("The project ID (for access control)"),
22331
+ systemId: external_exports.string().describe("The project ID (for access control)"),
22332
22332
  revisionId: external_exports.string().describe("The revision ID to reassign"),
22333
22333
  toBeatId: external_exports.string().describe("The target beat ID"),
22334
22334
  reason: external_exports.string().describe("Why this revision is being reassigned")
22335
22335
  },
22336
- async ({ projectId, revisionId, toBeatId, reason }) => {
22336
+ async ({ systemId, revisionId, toBeatId, reason }) => {
22337
22337
  try {
22338
- await assertProjectInOrg(client, projectId, ctx.orgId);
22338
+ await assertProjectInOrg(client, systemId, ctx.orgId);
22339
22339
  const result = await client.reassignRevision(revisionId, toBeatId, reason, ctx.user.userId);
22340
22340
  const text = [
22341
22341
  `Revision reassigned successfully.`,
@@ -22374,7 +22374,7 @@ function registerBeatConvergenceTools(server, ctx, client) {
22374
22374
  { beatId: external_exports.string().describe("The beat ID (e.g., TF-B-001)") },
22375
22375
  async ({ beatId }) => {
22376
22376
  const beat = await fetchBeatInOrg(client, beatId, ctx.orgId);
22377
- const report = await client.getBeatConvergence(beat.systemId ?? beat.projectId, beatId);
22377
+ const report = await client.getBeatConvergence(beat.systemId, beatId);
22378
22378
  if (!report) {
22379
22379
  return { content: [{ type: "text", text: `No convergence data for beat "${beatId}".` }], isError: true };
22380
22380
  }
@@ -22434,7 +22434,7 @@ function registerBeatPlanningTools(server, ctx, client) {
22434
22434
  "Plan the implementation of a Beat Revision via a conversational turn. Uses the revision description and Beat notes as context. Use this during the planning or building stage to refine scope, explore implementation details, or assess readiness.",
22435
22435
  {
22436
22436
  revisionId: external_exports.string().describe("The revision ID to plan"),
22437
- projectId: external_exports.string().describe("The project ID"),
22437
+ systemId: external_exports.string().describe("The project ID"),
22438
22438
  message: external_exports.string().describe(
22439
22439
  'Your planning message \u2014 e.g. "Review the revision scope", "What should we consider for the auth flow?", or "Does this cover the acceptance criteria?"'
22440
22440
  ),
@@ -22443,7 +22443,7 @@ function registerBeatPlanningTools(server, ctx, client) {
22443
22443
  content: external_exports.string()
22444
22444
  })).optional().describe("Previous conversation messages for context continuity")
22445
22445
  },
22446
- async ({ revisionId, projectId, message, conversationHistory }) => {
22446
+ async ({ revisionId, systemId, message, conversationHistory }) => {
22447
22447
  try {
22448
22448
  const revision = await client.getRevision(revisionId);
22449
22449
  if (!revision) {
@@ -22454,7 +22454,7 @@ function registerBeatPlanningTools(server, ctx, client) {
22454
22454
  ...conversationHistory ?? [],
22455
22455
  { role: "user", content: message }
22456
22456
  ];
22457
- const result = await client.planRevision(revisionId, projectId, messages);
22457
+ const result = await client.planRevision(revisionId, systemId, messages);
22458
22458
  return {
22459
22459
  content: [{
22460
22460
  type: "text",
@@ -22536,13 +22536,13 @@ ${d.rationale}${d.suggestions?.length ? `
22536
22536
  function registerBeatQualityTools(server, ctx, client) {
22537
22537
  const schema = {
22538
22538
  beatId: external_exports.string().describe("The beat ID (e.g., TF-B-001)"),
22539
- projectId: external_exports.string().describe("The project ID"),
22539
+ systemId: external_exports.string().describe("The project ID"),
22540
22540
  wait: external_exports.boolean().optional().describe("If true, block until the check completes and return the scorecard inline. Default: false (returns taskId immediately).")
22541
22541
  };
22542
- const handler = async ({ beatId, projectId, wait }) => {
22542
+ const handler = async ({ beatId, systemId, wait }) => {
22543
22543
  try {
22544
22544
  await assertBeatInOrg(client, beatId, ctx.orgId);
22545
- const { taskId } = await client.checkBeatQuality(projectId, beatId);
22545
+ const { taskId } = await client.checkBeatQuality(systemId, beatId);
22546
22546
  if (!wait) {
22547
22547
  return {
22548
22548
  content: [{
@@ -22585,12 +22585,12 @@ function registerBeatReframeTools(server, ctx, client) {
22585
22585
  "Reframe a Beat's title and description to meet business-outcome quality rules \u2014 outcome-first, plain English, non-technical audience. Returns a proposed rewrite for review.",
22586
22586
  {
22587
22587
  beatId: external_exports.string().describe("The beat ID (e.g., TF-B-001)"),
22588
- projectId: external_exports.string().describe("The project ID")
22588
+ systemId: external_exports.string().describe("The project ID")
22589
22589
  },
22590
- async ({ beatId, projectId }) => {
22590
+ async ({ beatId, systemId }) => {
22591
22591
  try {
22592
22592
  await assertBeatInOrg(client, beatId, ctx.orgId);
22593
- const result = await client.reframeBeat(projectId, beatId);
22593
+ const result = await client.reframeBeat(systemId, beatId);
22594
22594
  const text = formatReframeResult(beatId, result);
22595
22595
  return { content: [{ type: "text", text }] };
22596
22596
  } catch (err) {
@@ -22673,7 +22673,7 @@ function formatProjectSummaryTable(projects) {
22673
22673
  const header = "| Project ID | Title | Status |\n|------------|-------|--------|";
22674
22674
  const rows = projects.map((p) => {
22675
22675
  const status = p.status.replace("_", " ");
22676
- return `| ${p.projectId} | ${p.title} | ${status} |`;
22676
+ return `| ${p.systemId} | ${p.title} | ${status} |`;
22677
22677
  });
22678
22678
  return [header, ...rows].join("\n");
22679
22679
  }
@@ -22702,7 +22702,7 @@ function formatDeliveryPolicy(policy, unresolvedReason) {
22702
22702
  function formatProjectContext(project, notes, orgCoda) {
22703
22703
  const sections = [];
22704
22704
  sections.push(`# System: ${project.title}`);
22705
- sections.push(`**ID:** ${project.projectId}`);
22705
+ sections.push(`**ID:** ${project.systemId}`);
22706
22706
  sections.push(`**Status:** ${project.status.replace("_", " ")}`);
22707
22707
  if (project.repoOwner && project.repoName) {
22708
22708
  const branch = project.repoDefaultBranch ?? "main";
@@ -22989,7 +22989,7 @@ function formatSessionDetail(session) {
22989
22989
  "",
22990
22990
  `**Type:** ${type}`,
22991
22991
  `**Status:** ${status}`,
22992
- `**Project:** ${session.systemId ?? session.projectId}`,
22992
+ `**Project:** ${session.systemId}`,
22993
22993
  `**Actor:** ${session.actor.agentPersona}${session.actor.agentRole ? ` (${session.actor.agentRole})` : ""}`
22994
22994
  ];
22995
22995
  if (session.actor.initiator) {
@@ -23097,14 +23097,14 @@ function registerBeatTools(server, ctx, client) {
23097
23097
  "list_beats",
23098
23098
  "List beats in a project. Use offset/limit to paginate large result sets.",
23099
23099
  {
23100
- projectId: external_exports.string().describe("The project ID"),
23100
+ systemId: external_exports.string().describe("The project ID"),
23101
23101
  offset: external_exports.coerce.number().int().min(0).default(0).describe("Number of beats to skip (for pagination)"),
23102
23102
  limit: external_exports.coerce.number().int().min(1).max(500).default(100).describe("Max beats to return (default 100, max 500)"),
23103
23103
  includeArchived: external_exports.boolean().optional().default(false).describe("Include archived and deprecated beats (excluded by default)")
23104
23104
  },
23105
- async ({ projectId, offset = 0, limit = 100, includeArchived }) => {
23106
- await assertProjectInOrg(client, projectId, ctx.orgId);
23107
- let beats = await client.listSystemBeats(projectId);
23105
+ async ({ systemId, offset = 0, limit = 100, includeArchived }) => {
23106
+ await assertProjectInOrg(client, systemId, ctx.orgId);
23107
+ let beats = await client.listSystemBeats(systemId);
23108
23108
  if (!includeArchived) {
23109
23109
  beats = beats.filter((b) => b.beatStatus !== "archived");
23110
23110
  }
@@ -23139,11 +23139,11 @@ function registerBeatTools(server, ctx, client) {
23139
23139
  "Get the full decision lineage timeline for a beat (revisions, status changes, edits, proposals)",
23140
23140
  {
23141
23141
  beatId: external_exports.string().describe("The beat ID"),
23142
- projectId: external_exports.string().optional().describe("Project ID (auto-resolved from beat if omitted)")
23142
+ systemId: external_exports.string().optional().describe("Project ID (auto-resolved from beat if omitted)")
23143
23143
  },
23144
- async ({ beatId, projectId }) => {
23144
+ async ({ beatId, systemId }) => {
23145
23145
  const beat = await fetchBeatInOrg(client, beatId, ctx.orgId);
23146
- const resolvedProjectId = projectId ?? beat.systemId ?? beat.projectId;
23146
+ const resolvedProjectId = systemId ?? beat.systemId;
23147
23147
  const timeline = await client.getUnifiedBeatTimeline(resolvedProjectId, beatId);
23148
23148
  if (timeline.length === 0) {
23149
23149
  return { content: [{ type: "text", text: `No history for beat "${beatId}".` }] };
@@ -23156,7 +23156,7 @@ function registerBeatTools(server, ctx, client) {
23156
23156
  "create_beat",
23157
23157
  "Create a new beat in a project. Title must be outcome-first (what changes for the user, not what we build). Description is the Beat Coda \u2014 second-person resolved expression of the capability (1-3 sentences). Avoid jargon and user-story format. Read harmonica://guidelines/beat-quality for full rules. Auto-generates beat ID if not provided.",
23158
23158
  {
23159
- projectId: external_exports.string().describe("The project ID"),
23159
+ systemId: external_exports.string().describe("The project ID"),
23160
23160
  title: external_exports.string().describe('Beat title \u2014 outcome-first, e.g. "At-risk shipments screen used daily by Ops"'),
23161
23161
  description: external_exports.string().optional().describe("Beat Coda \u2014 second-person resolved expression of what the capability looks like when complete (1-3 sentences)"),
23162
23162
  tags: external_exports.array(external_exports.string()).optional().describe("Tags for categorization"),
@@ -23167,13 +23167,13 @@ function registerBeatTools(server, ctx, client) {
23167
23167
  agentAssignee: agentAssigneeSchema.optional().describe('Agent assignee (e.g. { agentId: "maya", name: "Maya" })'),
23168
23168
  branch: external_exports.string().optional().describe('Git branch for code assessment (falls back to Project.repoDefaultBranch \u2192 "main")')
23169
23169
  },
23170
- async ({ projectId, title, description, tags, priority, estimatedEffort, beatId, humanAssignee, agentAssignee, branch }) => {
23170
+ async ({ systemId, title, description, tags, priority, estimatedEffort, beatId, humanAssignee, agentAssignee, branch }) => {
23171
23171
  try {
23172
- await fetchProjectInOrg(client, projectId, ctx.orgId);
23173
- const resolvedBeatId = beatId ?? await client.getNextBeatId(projectId);
23172
+ await fetchProjectInOrg(client, systemId, ctx.orgId);
23173
+ const resolvedBeatId = beatId ?? await client.getNextBeatId(systemId);
23174
23174
  const beat = await client.createBeat({
23175
23175
  beatId: resolvedBeatId,
23176
- projectId,
23176
+ systemId,
23177
23177
  orgId: ctx.orgId,
23178
23178
  title,
23179
23179
  description,
@@ -23190,7 +23190,7 @@ function registerBeatTools(server, ctx, client) {
23190
23190
  });
23191
23191
  if (!beat) return { content: [{ type: "text", text: "Failed to create beat: store returned undefined" }], isError: true };
23192
23192
  await logBeatActivity(client, {
23193
- projectId,
23193
+ systemId,
23194
23194
  action: "beat_created",
23195
23195
  beatIds: [resolvedBeatId],
23196
23196
  beatTitles: [title],
@@ -23199,7 +23199,7 @@ function registerBeatTools(server, ctx, client) {
23199
23199
  });
23200
23200
  let similarityWarning = "";
23201
23201
  try {
23202
- const similar = await client.findSimilarBeats(beat.beatId, projectId, { threshold: 0.7, limit: 5 });
23202
+ const similar = await client.findSimilarBeats(beat.beatId, systemId, { threshold: 0.7, limit: 5 });
23203
23203
  if (similar.length > 0) {
23204
23204
  const rows = similar.map((r) => `- **${r.entityId}** (score: ${r.score.toFixed(2)}): ${r.snippet.replace(/[\n\r]/g, " ")}`).join("\n");
23205
23205
  similarityWarning = `
@@ -23257,7 +23257,7 @@ ${formatBeatDetail(beat)}${similarityWarning}` }] };
23257
23257
  return { content: [{ type: "text", text: `Beat not found: "${beatId}"` }], isError: true };
23258
23258
  }
23259
23259
  await logBeatActivity(client, {
23260
- projectId: beat.systemId ?? beat.projectId,
23260
+ systemId: beat.systemId,
23261
23261
  action: "beat_updated",
23262
23262
  beatIds: [beatId],
23263
23263
  beatTitles: [updated.title],
@@ -23279,15 +23279,15 @@ ${formatBeatDetail(beat)}${similarityWarning}` }] };
23279
23279
  "list_revisions",
23280
23280
  "List revisions for a project, optionally filtered by binary status (`active` or `archived`). Archived revisions are excluded by default. Use beatId to scope results to a single beat.",
23281
23281
  {
23282
- projectId: external_exports.string().describe("The project ID"),
23282
+ systemId: external_exports.string().describe("The project ID"),
23283
23283
  beatId: external_exports.string().optional().describe("Scope to a specific beat (omit for all project revisions)"),
23284
23284
  status: external_exports.enum(["active", "archived"]).optional().describe("Filter by binary Revision status (default: all non-archived)"),
23285
23285
  includeArchived: external_exports.boolean().optional().default(false).describe("Include archived revisions (excluded by default)")
23286
23286
  },
23287
- async ({ projectId, beatId, status, includeArchived }) => {
23287
+ async ({ systemId, beatId, status, includeArchived }) => {
23288
23288
  try {
23289
- await assertProjectInOrg(client, projectId, ctx.orgId);
23290
- const revisions = beatId ? await client.listBeatRevisions(projectId, beatId, { status, includeArchived }) : await client.listSystemRevisions(projectId, { status, includeArchived });
23289
+ await assertProjectInOrg(client, systemId, ctx.orgId);
23290
+ const revisions = beatId ? await client.listBeatRevisions(systemId, beatId, { status, includeArchived }) : await client.listSystemRevisions(systemId, { status, includeArchived });
23291
23291
  return { content: [{ type: "text", text: formatRevisionSummaryTable(revisions) }] };
23292
23292
  } catch (err) {
23293
23293
  const message = err instanceof Error ? err.message : String(err);
@@ -23327,7 +23327,7 @@ async function logBeatActivity(client, params) {
23327
23327
  try {
23328
23328
  await client.createActivity({
23329
23329
  activityId: (0, import_crypto2.randomUUID)(),
23330
- projectId: params.projectId,
23330
+ systemId: params.systemId,
23331
23331
  action: params.action,
23332
23332
  importance: params.importance ?? "minor",
23333
23333
  affectedBeatIds: params.beatIds,
@@ -23399,13 +23399,13 @@ function registerBeatVersionTools(server, ctx, client) {
23399
23399
  "list_beat_versions",
23400
23400
  "List Beat Versions for a Beat or Project. Beat Versions are the shippable planning increments that advance a Beat toward its goal. Use beatId to scope to a specific Beat, or omit for all Beat Versions in the project. Status is binary (active | archived); by default excludes archived.",
23401
23401
  {
23402
- projectId: external_exports.string().describe("The project ID"),
23402
+ systemId: external_exports.string().describe("The project ID"),
23403
23403
  beatId: external_exports.string().optional().describe("Filter to a specific Beat (omit for all Beat Versions in project)"),
23404
23404
  status: external_exports.enum(BEAT_VERSION_STATUSES).optional().describe("Filter to a specific status (active | archived)"),
23405
23405
  includeTerminal: external_exports.boolean().optional().describe("Include archived Beat Versions. Default: false.")
23406
23406
  },
23407
- async ({ projectId, beatId, status, includeTerminal }) => {
23408
- const beatVersions = await client.listBeatVersions(projectId, {
23407
+ async ({ systemId, beatId, status, includeTerminal }) => {
23408
+ const beatVersions = await client.listBeatVersions(systemId, {
23409
23409
  beatId,
23410
23410
  status,
23411
23411
  includeTerminal
@@ -23476,7 +23476,7 @@ function registerBeatVersionTools(server, ctx, client) {
23476
23476
  return { content: [{ type: "text", text: `Beat not found: "${beatId}"` }], isError: true };
23477
23477
  }
23478
23478
  await assertBeatInOrg(client, beatId, ctx.orgId);
23479
- const beatVersion = await client.createBeatVersion(beat.systemId ?? beat.projectId, beatId, {
23479
+ const beatVersion = await client.createBeatVersion(beat.systemId, beatId, {
23480
23480
  title,
23481
23481
  description,
23482
23482
  scope,
@@ -23831,13 +23831,13 @@ function registerCheckTools(server, ctx, client) {
23831
23831
  "list_checks",
23832
23832
  "List persisted checks for a project, beat, beat version, or revision. Shows score trends over time. Filter by check type.",
23833
23833
  {
23834
- projectId: external_exports.string().describe("The project ID"),
23834
+ systemId: external_exports.string().describe("The project ID"),
23835
23835
  beatId: external_exports.string().optional().describe("List checks for this specific beat"),
23836
23836
  beatVersionId: external_exports.string().optional().describe("List checks for this specific beat version"),
23837
23837
  revisionId: external_exports.string().optional().describe("List checks for this specific revision"),
23838
23838
  checkType: external_exports.enum(["beat_coda_quality", "beat_quality", "beat_version_quality", "plan_quality", "build_quality", "pii_scan", "portfolio_coherence"]).optional().describe("Filter by check type. beat_quality is the DEPRECATED alias of beat_coda_quality. plan_quality is the DEPRECATED alias of beat_version_quality. pii_scan is RETIRED from the taxonomy (PII detection moved to the Scan family, N-4E09-6809) \u2014 it is still accepted here so historical records stay readable, but no new pii_scan checks are written.")
23839
23839
  },
23840
- async ({ projectId, beatId, beatVersionId, revisionId, checkType }) => {
23840
+ async ({ systemId, beatId, beatVersionId, revisionId, checkType }) => {
23841
23841
  try {
23842
23842
  const filters = [
23843
23843
  revisionId ? "revisionId" : null,
@@ -23916,18 +23916,18 @@ function registerCheckTools(server, ctx, client) {
23916
23916
  let checks;
23917
23917
  let scope;
23918
23918
  if (revisionId) {
23919
- checks = await client.listRevisionChecks(revisionId, projectId, checkType);
23919
+ checks = await client.listRevisionChecks(revisionId, systemId, checkType);
23920
23920
  scope = `revision ${revisionId}`;
23921
23921
  } else if (beatVersionId) {
23922
- checks = await client.listBeatVersionChecks(beatVersionId, projectId, checkType);
23922
+ checks = await client.listBeatVersionChecks(beatVersionId, systemId, checkType);
23923
23923
  scope = `beat_version ${beatVersionId}`;
23924
23924
  } else if (beatId) {
23925
- checks = await client.listBeatChecks(beatId, projectId, checkType);
23925
+ checks = await client.listBeatChecks(beatId, systemId, checkType);
23926
23926
  scope = `beat ${beatId}`;
23927
23927
  } else {
23928
- const result = await client.listSystemChecks(projectId, checkType, { limit: 200 });
23928
+ const result = await client.listSystemChecks(systemId, checkType, { limit: 200 });
23929
23929
  checks = result.checks;
23930
- scope = `project ${projectId}`;
23930
+ scope = `project ${systemId}`;
23931
23931
  }
23932
23932
  if (checks.length === 0) {
23933
23933
  return { content: [{ type: "text", text: `No checks found for ${scope}.` }] };
@@ -23944,15 +23944,15 @@ function registerCheckTools(server, ctx, client) {
23944
23944
  "list_latest_checks",
23945
23945
  "List the latest check per target (Beat, Beat Version, Revision) across a whole project in one call \u2014 the most recent score of each type for each entity. Use this for an at-a-glance quality snapshot of every capability; use list_checks when you need the full history of one target.",
23946
23946
  {
23947
- projectId: external_exports.string().describe("The project ID")
23947
+ systemId: external_exports.string().describe("The project ID")
23948
23948
  },
23949
- async ({ projectId }) => {
23949
+ async ({ systemId }) => {
23950
23950
  try {
23951
- const checks = await client.listLatestSystemChecks(projectId);
23951
+ const checks = await client.listLatestSystemChecks(systemId);
23952
23952
  if (checks.length === 0) {
23953
- return { content: [{ type: "text", text: `No checks found for project ${projectId}.` }] };
23953
+ return { content: [{ type: "text", text: `No checks found for project ${systemId}.` }] };
23954
23954
  }
23955
- const text = formatCheckList(checks, `latest per target \xB7 project ${projectId}`);
23955
+ const text = formatCheckList(checks, `latest per target \xB7 project ${systemId}`);
23956
23956
  return { content: [{ type: "text", text }] };
23957
23957
  } catch (err) {
23958
23958
  const message = err instanceof Error ? err.message : String(err);
@@ -23966,19 +23966,19 @@ function registerCheckTools(server, ctx, client) {
23966
23966
  {
23967
23967
  checkType: external_exports.enum(["beat_coda_quality", "beat_quality", "beat_version_quality", "plan_quality", "build_quality"]).describe("Type of check to run. beat_quality is the deprecated alias of beat_coda_quality. plan_quality is the deprecated alias of beat_version_quality."),
23968
23968
  targetId: external_exports.string().describe("The target entity ID \u2014 a beatId for beat_coda_quality, or a Beat Version ID (bv-*) for beat_version_quality and build_quality"),
23969
- projectId: external_exports.string().describe("The project ID"),
23969
+ systemId: external_exports.string().describe("The project ID"),
23970
23970
  branch: external_exports.string().optional().describe("Override the git branch for code checks (default: project default branch)"),
23971
23971
  localPath: external_exports.string().optional().describe("Absolute path to an already-cloned local copy of the repo \u2014 skips git clone entirely (useful for large repos or offline use)")
23972
23972
  },
23973
- async ({ checkType, targetId, projectId, branch, localPath }) => {
23973
+ async ({ checkType, targetId, systemId, branch, localPath }) => {
23974
23974
  try {
23975
- await assertProjectInOrg(client, projectId, ctx.orgId);
23975
+ await assertProjectInOrg(client, systemId, ctx.orgId);
23976
23976
  const targetError = checkType === "build_quality" ? buildQualityTargetError(targetId) : checkType === "beat_version_quality" || checkType === "plan_quality" ? beatVersionQualityTargetError(targetId) : null;
23977
23977
  if (targetError) {
23978
23978
  return { content: [{ type: "text", text: targetError }], isError: true };
23979
23979
  }
23980
23980
  const opts = branch || localPath ? { branch, localPath } : void 0;
23981
- const task = await client.runCheck(projectId, checkType, targetId, opts);
23981
+ const task = await client.runCheck(systemId, checkType, targetId, opts);
23982
23982
  const text = [
23983
23983
  `Check enqueued.`,
23984
23984
  `**Task ID:** ${task.taskId}`,
@@ -24070,7 +24070,7 @@ function registerClaimTools(server, ctx, client) {
24070
24070
  return { content: [{ type: "text", text: `Revision not found: "${revisionId}"` }], isError: true };
24071
24071
  }
24072
24072
  await assertBeatInOrg(client, revision.beatId, ctx.orgId);
24073
- const result = await client.claimRevision(revision.projectId, revisionId);
24073
+ const result = await client.claimRevision(revision.systemId, revisionId);
24074
24074
  if (!result.success) {
24075
24075
  return { content: [{ type: "text", text: `Failed to claim revision (${result.error.code}): ${result.error.message}` }], isError: true };
24076
24076
  }
@@ -24104,7 +24104,7 @@ function registerClaimTools(server, ctx, client) {
24104
24104
  return { content: [{ type: "text", text: `Revision not found: "${revisionId}"` }], isError: true };
24105
24105
  }
24106
24106
  await assertBeatInOrg(client, revision.beatId, ctx.orgId);
24107
- const result = await client.releaseRevisionClaim(revision.projectId, revisionId, reason);
24107
+ const result = await client.releaseRevisionClaim(revision.systemId, revisionId, reason);
24108
24108
  if (!result.success) {
24109
24109
  return { content: [{ type: "text", text: `Failed to release claim (${result.error.code}): ${result.error.message}` }], isError: true };
24110
24110
  }
@@ -24204,11 +24204,11 @@ ${g.acceptanceCriteria}` : null,
24204
24204
  deliverables: external_exports.string().max(2e4).optional().describe("The Track's deliverables as Markdown bullet points (parallel to acceptanceCriteria)"),
24205
24205
  acceptanceCriteria: external_exports.string().max(2e4).optional().describe("The Track's acceptance criteria (Markdown), parallel to its deliverables"),
24206
24206
  vuRate: external_exports.number().nonnegative().describe("Value-Unit rate (billing rate for this engagement)"),
24207
- projectId: external_exports.string().optional().describe("Optional project to scope this Track to"),
24207
+ systemId: external_exports.string().optional().describe("Optional project to scope this Track to"),
24208
24208
  movementId: external_exports.string().optional().describe("Optional Movement (client SOW) to scope this Track to"),
24209
24209
  parentGroupId: external_exports.string().optional().describe("Optional parent Track ID for nested grouping (Phase 0 scaffolding)")
24210
24210
  };
24211
- const createTrackHandler = async ({ teamspaceId, title, description, deliverables, acceptanceCriteria, vuRate, projectId, movementId, parentGroupId }) => {
24211
+ const createTrackHandler = async ({ teamspaceId, title, description, deliverables, acceptanceCriteria, vuRate, systemId, movementId, parentGroupId }) => {
24212
24212
  await assertTeamspaceInOrg(client, teamspaceId, ctx.orgId);
24213
24213
  if (movementId !== void 0) {
24214
24214
  await assertMovementInOrg(client, movementId, ctx.orgId);
@@ -24224,7 +24224,7 @@ ${g.acceptanceCriteria}` : null,
24224
24224
  ...deliverables !== void 0 && { deliverables },
24225
24225
  ...acceptanceCriteria !== void 0 && { acceptanceCriteria },
24226
24226
  vuRate,
24227
- ...projectId !== void 0 && { projectId },
24227
+ ...systemId !== void 0 && { systemId },
24228
24228
  ...movementId !== void 0 && { movementId },
24229
24229
  ...parentGroupId !== void 0 && { parentGroupId },
24230
24230
  createdBy: ctx.user.userId
@@ -24447,7 +24447,7 @@ function registerDownbeatTools(server, ctx, client) {
24447
24447
  { beatId: external_exports.string().describe("The beat ID (e.g., TF-B-001)") },
24448
24448
  async ({ beatId }) => {
24449
24449
  const beat = await fetchBeatInOrg(client, beatId, ctx.orgId);
24450
- const downbeat = await client.setDownbeat(beat.systemId ?? beat.projectId, beatId);
24450
+ const downbeat = await client.setDownbeat(beat.systemId, beatId);
24451
24451
  if (!downbeat) {
24452
24452
  return { content: [{ type: "text", text: `Could not set a Downbeat for beat "${beatId}".` }], isError: true };
24453
24453
  }
@@ -24460,7 +24460,7 @@ function registerDownbeatTools(server, ctx, client) {
24460
24460
  { beatId: external_exports.string().describe("The beat ID (e.g., TF-B-001)") },
24461
24461
  async ({ beatId }) => {
24462
24462
  const beat = await fetchBeatInOrg(client, beatId, ctx.orgId);
24463
- const result = await client.listIncrementsSinceDownbeat(beat.systemId ?? beat.projectId, beatId);
24463
+ const result = await client.listIncrementsSinceDownbeat(beat.systemId, beatId);
24464
24464
  if (!result) {
24465
24465
  return { content: [{ type: "text", text: `No such beat "${beatId}".` }], isError: true };
24466
24466
  }
@@ -24473,7 +24473,7 @@ function registerDownbeatTools(server, ctx, client) {
24473
24473
  { beatId: external_exports.string().describe("The beat ID (e.g., TF-B-001)") },
24474
24474
  async ({ beatId }) => {
24475
24475
  const beat = await fetchBeatInOrg(client, beatId, ctx.orgId);
24476
- const report = await client.getCodaDrift(beat.systemId ?? beat.projectId, beatId);
24476
+ const report = await client.getCodaDrift(beat.systemId, beatId);
24477
24477
  if (!report) {
24478
24478
  return { content: [{ type: "text", text: `No such beat "${beatId}".` }], isError: true };
24479
24479
  }
@@ -24486,7 +24486,7 @@ function registerDownbeatTools(server, ctx, client) {
24486
24486
  { beatId: external_exports.string().describe("The beat ID (e.g., TF-B-001)") },
24487
24487
  async ({ beatId }) => {
24488
24488
  const beat = await fetchBeatInOrg(client, beatId, ctx.orgId);
24489
- const report = await client.getRelationalDrift(beat.systemId ?? beat.projectId, beatId);
24489
+ const report = await client.getRelationalDrift(beat.systemId, beatId);
24490
24490
  if (!report) {
24491
24491
  return { content: [{ type: "text", text: `No such beat "${beatId}".` }], isError: true };
24492
24492
  }
@@ -24555,17 +24555,17 @@ function registerDropQualityTools(server, ctx, client) {
24555
24555
  "Run a five-dimension coherence check on a Drop (Substantial, Harmonious, Clear, Focused, Strategically Aligned), graded LLM-only on the Drop's Beat Versions and their parent Beats/Projects/Codas. By default returns a taskId immediately \u2014 set wait=true to block and receive the full scorecard inline.",
24556
24556
  {
24557
24557
  dropId: external_exports.string().min(1).describe("The Drop ID (e.g. drop-abc123) to grade"),
24558
- projectId: external_exports.string().min(1).describe("A project within this Drop \u2014 the check record is attributed to it (Drops span projects, so the result is recorded under one member project)"),
24558
+ systemId: external_exports.string().min(1).describe("A project within this Drop \u2014 the check record is attributed to it (Drops span projects, so the result is recorded under one member project)"),
24559
24559
  wait: external_exports.boolean().optional().describe("Block until the check completes and return the scorecard inline. Default: false.")
24560
24560
  },
24561
- async ({ dropId, projectId, wait }) => {
24561
+ async ({ dropId, systemId, wait }) => {
24562
24562
  try {
24563
- await assertProjectInOrg(client, projectId, ctx.orgId);
24563
+ await assertProjectInOrg(client, systemId, ctx.orgId);
24564
24564
  const drop = await client.getDrop(dropId);
24565
24565
  if (!drop) {
24566
24566
  return { content: [{ type: "text", text: `Drop "${dropId}" not found.` }], isError: true };
24567
24567
  }
24568
- const { taskId } = await client.runCheck(projectId, "drop_quality", dropId);
24568
+ const { taskId } = await client.runCheck(systemId, "drop_quality", dropId);
24569
24569
  if (!wait) {
24570
24570
  return {
24571
24571
  content: [{
@@ -24966,9 +24966,9 @@ function registerEmbeddingTools(server, ctx, client) {
24966
24966
  );
24967
24967
  server.tool(
24968
24968
  "search",
24969
- "Free-text semantic search across project, teamspace, or org scope. Provide exactly one of projectId / teamspaceId / orgLevel. Teamspace and org searches fan out across child projects and merge by score. Cross-project results carry the source project name.",
24969
+ "Free-text semantic search across project, teamspace, or org scope. Provide exactly one of systemId / teamspaceId / orgLevel. Teamspace and org searches fan out across child projects and merge by score. Cross-project results carry the source project name.",
24970
24970
  {
24971
- projectId: external_exports.string().optional().describe("Search within a single project"),
24971
+ systemId: external_exports.string().optional().describe("Search within a single project"),
24972
24972
  teamspaceId: external_exports.string().optional().describe("Search across all projects in a teamspace"),
24973
24973
  orgLevel: external_exports.boolean().optional().describe("Search across every project in the configured org"),
24974
24974
  query: external_exports.string().describe("Natural language search query"),
@@ -24979,15 +24979,15 @@ function registerEmbeddingTools(server, ctx, client) {
24979
24979
  noteStatus: external_exports.enum(["active", "unvalidated", "validated", "invalidated", "inProgress", "resolved", "superseded", "dismissed"]).optional().describe("Restrict note results to a specific status (e.g. unvalidated). DynamoDB post-filter \u2014 works without re-embedding."),
24980
24980
  beatId: external_exports.string().optional().describe("Restrict note results to those belonging to a specific beat (DynamoDB post-filter, no re-embedding needed)")
24981
24981
  },
24982
- async ({ projectId, teamspaceId, orgLevel, query, limit, includeArchived, entityType, noteType, noteStatus, beatId }) => {
24983
- const provided = [projectId, teamspaceId, orgLevel].filter(Boolean).length;
24982
+ async ({ systemId, teamspaceId, orgLevel, query, limit, includeArchived, entityType, noteType, noteStatus, beatId }) => {
24983
+ const provided = [systemId, teamspaceId, orgLevel].filter(Boolean).length;
24984
24984
  if (provided !== 1) {
24985
- throw new Error("Specify exactly one of projectId, teamspaceId, or orgLevel.");
24985
+ throw new Error("Specify exactly one of systemId, teamspaceId, or orgLevel.");
24986
24986
  }
24987
24987
  let scope;
24988
- if (projectId) {
24989
- await assertProjectInOrg(client, projectId, ctx.orgId);
24990
- scope = { projectId };
24988
+ if (systemId) {
24989
+ await assertProjectInOrg(client, systemId, ctx.orgId);
24990
+ scope = { systemId };
24991
24991
  } else if (teamspaceId) {
24992
24992
  await assertTeamspaceInOrg(client, teamspaceId, ctx.orgId);
24993
24993
  scope = { teamspaceId };
@@ -25004,22 +25004,22 @@ function registerEmbeddingTools(server, ctx, client) {
25004
25004
  "Find Notes semantically similar to one or more Notes. Accepts a single Note ID or an array of Note IDs for batch duplicate detection. Results are grouped by source Note.",
25005
25005
  {
25006
25006
  noteId: external_exports.union([external_exports.string(), external_exports.array(external_exports.string())]).describe("Note ID or array of Note IDs to find similar notes for"),
25007
- projectId: external_exports.string().describe("The project ID the notes belong to"),
25007
+ systemId: external_exports.string().describe("The project ID the notes belong to"),
25008
25008
  threshold: external_exports.coerce.number().min(0).max(1).optional().default(0.7).describe("Minimum similarity score (0-1). Default 0.7"),
25009
25009
  limit: external_exports.coerce.number().int().min(1).max(50).optional().default(10).describe("Max results per source note. Default 10"),
25010
25010
  includeArchived: external_exports.boolean().optional().default(false).describe("Include archived/deprecated notes in results (excluded by default)"),
25011
25011
  noteStatus: external_exports.enum(["active", "unvalidated", "validated", "invalidated", "inProgress", "resolved", "superseded", "dismissed"]).optional().describe("Restrict results to notes with a specific status (e.g. unvalidated). DynamoDB post-filter."),
25012
25012
  beatId: external_exports.string().optional().describe("Restrict results to notes belonging to a specific beat (DynamoDB post-filter)")
25013
25013
  },
25014
- async ({ noteId, projectId, threshold, limit, includeArchived = false, noteStatus, beatId }) => {
25015
- await assertProjectInOrg(client, projectId, ctx.orgId);
25014
+ async ({ noteId, systemId, threshold, limit, includeArchived = false, noteStatus, beatId }) => {
25015
+ await assertProjectInOrg(client, systemId, ctx.orgId);
25016
25016
  const ids = Array.isArray(noteId) ? noteId : [noteId];
25017
25017
  if (ids.length === 1) {
25018
- const results = await client.findSimilarNotes(ids[0], projectId, { threshold, limit, includeArchived, noteStatus, beatId });
25018
+ const results = await client.findSimilarNotes(ids[0], systemId, { threshold, limit, includeArchived, noteStatus, beatId });
25019
25019
  const text = formatSimilarityResults(results, ids[0], "Similar Notes");
25020
25020
  return { content: [{ type: "text", text }] };
25021
25021
  }
25022
- const batchResults = await client.findSimilarNotesMulti(ids, projectId, { threshold, limit, includeArchived, noteStatus, beatId });
25022
+ const batchResults = await client.findSimilarNotesMulti(ids, systemId, { threshold, limit, includeArchived, noteStatus, beatId });
25023
25023
  const sections = batchResults.map(
25024
25024
  (br) => formatSimilarityResults(br.results, br.sourceNoteId, "Similar Notes")
25025
25025
  );
@@ -25031,14 +25031,14 @@ function registerEmbeddingTools(server, ctx, client) {
25031
25031
  "Find Beats semantically similar to a given Beat. Useful for detecting duplicates, dependencies, or related capabilities.",
25032
25032
  {
25033
25033
  beatId: external_exports.string().describe("The beat ID to find similar beats for"),
25034
- projectId: external_exports.string().describe("The project ID the beat belongs to"),
25034
+ systemId: external_exports.string().describe("The project ID the beat belongs to"),
25035
25035
  threshold: external_exports.coerce.number().min(0).max(1).optional().default(0.7).describe("Minimum similarity score (0-1). Default 0.7"),
25036
25036
  limit: external_exports.coerce.number().int().min(1).max(50).optional().default(10).describe("Max results to return. Default 10"),
25037
25037
  includeArchived: external_exports.boolean().optional().default(false).describe("Include archived/deprecated beats in results (excluded by default)")
25038
25038
  },
25039
- async ({ beatId, projectId, threshold, limit, includeArchived = false }) => {
25039
+ async ({ beatId, systemId, threshold, limit, includeArchived = false }) => {
25040
25040
  await assertBeatInOrg(client, beatId, ctx.orgId);
25041
- const results = await client.findSimilarBeats(beatId, projectId, { threshold, limit, includeArchived });
25041
+ const results = await client.findSimilarBeats(beatId, systemId, { threshold, limit, includeArchived });
25042
25042
  const text = formatSimilarityResults(results, beatId, "Similar Beats");
25043
25043
  return { content: [{ type: "text", text }] };
25044
25044
  }
@@ -25048,13 +25048,13 @@ function registerEmbeddingTools(server, ctx, client) {
25048
25048
  "Find Beats semantically related to a given Note. Surfaces missing affectsBeats links and helps classify unassigned Notes.",
25049
25049
  {
25050
25050
  noteId: external_exports.string().describe("The note ID to find related beats for"),
25051
- projectId: external_exports.string().describe("The project ID the note belongs to"),
25051
+ systemId: external_exports.string().describe("The project ID the note belongs to"),
25052
25052
  threshold: external_exports.coerce.number().min(0).max(1).optional().default(0.7).describe("Minimum similarity score (0-1). Default 0.7"),
25053
25053
  limit: external_exports.coerce.number().int().min(1).max(50).optional().default(10).describe("Max results to return. Default 10")
25054
25054
  },
25055
- async ({ noteId, projectId, threshold, limit }) => {
25056
- await assertProjectInOrg(client, projectId, ctx.orgId);
25057
- const results = await client.findRelatedBeats(noteId, projectId, { threshold, limit });
25055
+ async ({ noteId, systemId, threshold, limit }) => {
25056
+ await assertProjectInOrg(client, systemId, ctx.orgId);
25057
+ const results = await client.findRelatedBeats(noteId, systemId, { threshold, limit });
25058
25058
  const text = formatSimilarityResults(results, noteId, "Related Beats");
25059
25059
  return { content: [{ type: "text", text }] };
25060
25060
  }
@@ -25063,14 +25063,14 @@ function registerEmbeddingTools(server, ctx, client) {
25063
25063
  "cluster_notes",
25064
25064
  "Cluster semantically similar Notes using DBSCAN on vector embeddings. Returns clusters for review with member previews. Use this to discover natural groupings among notes and consolidate them under parent summary notes.",
25065
25065
  {
25066
- projectId: external_exports.string().describe("The project ID"),
25066
+ systemId: external_exports.string().describe("The project ID"),
25067
25067
  threshold: external_exports.coerce.number().min(0).max(1).optional().default(0.75).describe("Cosine similarity threshold for clustering (0-1). Higher = tighter clusters. Default 0.75"),
25068
25068
  minClusterSize: external_exports.coerce.number().int().min(2).optional().default(3).describe("Minimum notes to form a cluster. Default 3"),
25069
25069
  includeLinked: external_exports.coerce.boolean().optional().default(false).describe("Include notes that already have dependsOnNotes links. Default false (excludes them). Set true for thematic grouping pass.")
25070
25070
  },
25071
- async ({ projectId, threshold, minClusterSize, includeLinked }) => {
25072
- await assertProjectInOrg(client, projectId, ctx.orgId);
25073
- const result = await client.clusterNotes(projectId, { threshold, minClusterSize, excludeAlreadyLinked: !includeLinked });
25071
+ async ({ systemId, threshold, minClusterSize, includeLinked }) => {
25072
+ await assertProjectInOrg(client, systemId, ctx.orgId);
25073
+ const result = await client.clusterNotes(systemId, { threshold, minClusterSize, excludeAlreadyLinked: !includeLinked });
25074
25074
  if (result.clusters.length === 0) {
25075
25075
  return { content: [{ type: "text", text: `No clusters found among ${result.totalAnalyzed} analyzed notes. Try lowering the threshold.` }] };
25076
25076
  }
@@ -25094,14 +25094,14 @@ ${members}`;
25094
25094
  "consolidate_notes",
25095
25095
  "Two-pass note consolidation: clusters notes via DBSCAN, then sends each cluster to an LLM for contextual analysis. Returns consolidation proposals (should these be grouped under a parent summary?) without modifying data. Use cluster_notes for a quick vector-only view; use this when ready to act on consolidation.",
25096
25096
  {
25097
- projectId: external_exports.string().describe("The project ID"),
25097
+ systemId: external_exports.string().describe("The project ID"),
25098
25098
  threshold: external_exports.coerce.number().min(0).max(1).optional().default(0.65).describe("Cosine similarity threshold for clustering (0-1). Default 0.65"),
25099
25099
  minClusterSize: external_exports.coerce.number().int().min(2).optional().default(2).describe("Minimum notes to form a cluster. Default 2"),
25100
25100
  includeLinked: external_exports.coerce.boolean().optional().default(false).describe("Include notes that already have dependsOnNotes links. Default false.")
25101
25101
  },
25102
- async ({ projectId, threshold, minClusterSize, includeLinked }) => {
25103
- await assertProjectInOrg(client, projectId, ctx.orgId);
25104
- const result = await client.consolidateNotes(projectId, { threshold, minClusterSize, includeLinked });
25102
+ async ({ systemId, threshold, minClusterSize, includeLinked }) => {
25103
+ await assertProjectInOrg(client, systemId, ctx.orgId);
25104
+ const result = await client.consolidateNotes(systemId, { threshold, minClusterSize, includeLinked });
25105
25105
  if (result.proposals.length === 0) {
25106
25106
  return { content: [{ type: "text", text: `No consolidation candidates found among ${result.totalAnalyzed} analyzed notes.` }] };
25107
25107
  }
@@ -25337,18 +25337,18 @@ function registerLayerTools(server, ctx, client) {
25337
25337
  "list_system_layers",
25338
25338
  "List the architectural Layers declared on a System (e.g. frontend, backend, data). Layers are an evaluation lens for plan/build quality checks, not a work container.",
25339
25339
  {
25340
- projectId: external_exports.string().describe("The System ID")
25340
+ systemId: external_exports.string().describe("The System ID")
25341
25341
  },
25342
- async ({ projectId }) => {
25342
+ async ({ systemId }) => {
25343
25343
  try {
25344
- await assertProjectInOrg(client, projectId, ctx.orgId);
25344
+ await assertProjectInOrg(client, systemId, ctx.orgId);
25345
25345
  } catch (err) {
25346
25346
  const message = err instanceof Error ? err.message : String(err);
25347
25347
  return { content: [{ type: "text", text: `Access denied: ${message}` }], isError: true };
25348
25348
  }
25349
25349
  let layers;
25350
25350
  try {
25351
- layers = await client.listSystemLayers(projectId);
25351
+ layers = await client.listSystemLayers(systemId);
25352
25352
  } catch (err) {
25353
25353
  const message = err instanceof Error ? err.message : String(err);
25354
25354
  return { content: [{ type: "text", text: `Failed to list Layers: ${message}` }], isError: true };
@@ -25361,12 +25361,12 @@ function registerLayerTools(server, ctx, client) {
25361
25361
  "get_layer",
25362
25362
  "Get full details of a single Layer by ID, including its repo mapping and parent Layer (if it was split from one).",
25363
25363
  {
25364
- projectId: external_exports.string().describe("The System ID (for access control)"),
25364
+ systemId: external_exports.string().describe("The System ID (for access control)"),
25365
25365
  layerId: external_exports.string().describe("The Layer ID")
25366
25366
  },
25367
- async ({ projectId, layerId }) => {
25367
+ async ({ systemId, layerId }) => {
25368
25368
  try {
25369
- await assertProjectInOrg(client, projectId, ctx.orgId);
25369
+ await assertProjectInOrg(client, systemId, ctx.orgId);
25370
25370
  } catch (err) {
25371
25371
  const message = err instanceof Error ? err.message : String(err);
25372
25372
  return { content: [{ type: "text", text: `Access denied: ${message}` }], isError: true };
@@ -25381,8 +25381,8 @@ function registerLayerTools(server, ctx, client) {
25381
25381
  if (!layer) {
25382
25382
  return { content: [{ type: "text", text: `Layer not found: "${layerId}"` }], isError: true };
25383
25383
  }
25384
- if (layer.projectId !== projectId) {
25385
- return { content: [{ type: "text", text: `Layer "${layerId}" does not belong to System "${projectId}".` }], isError: true };
25384
+ if (layer.systemId !== systemId) {
25385
+ return { content: [{ type: "text", text: `Layer "${layerId}" does not belong to System "${systemId}".` }], isError: true };
25386
25386
  }
25387
25387
  const text = formatLayerDetail(layer);
25388
25388
  return { content: [{ type: "text", text }] };
@@ -25392,16 +25392,16 @@ function registerLayerTools(server, ctx, client) {
25392
25392
  "create_layer",
25393
25393
  "Declare a new architectural Layer on a System (e.g. frontend, backend, data). Layers are an evaluation lens for plan/build quality checks. layerKey must be a lowercase slug and unique per System.",
25394
25394
  {
25395
- projectId: external_exports.string().describe("The System ID"),
25395
+ systemId: external_exports.string().describe("The System ID"),
25396
25396
  layerKey: external_exports.string().regex(/^[a-z]([a-z0-9-]*[a-z0-9])?$/, "layerKey must be a lowercase slug (letters, digits, hyphens; start with a letter, no trailing hyphen)").describe('Stable lowercase slug for the Layer role, e.g. "frontend" or "agent-workers"'),
25397
25397
  name: external_exports.string().describe('Display name, e.g. "Frontend"'),
25398
25398
  description: external_exports.string().optional().describe("Optional description of the Layer"),
25399
25399
  parentLayerId: external_exports.string().optional().describe("Optional parent Layer to nest under (must be a Layer on the same System)"),
25400
25400
  repoPatterns: external_exports.array(external_exports.string()).optional().describe("Optional repo path patterns owned by this Layer")
25401
25401
  },
25402
- async ({ projectId, layerKey, name, description, parentLayerId, repoPatterns }) => {
25402
+ async ({ systemId, layerKey, name, description, parentLayerId, repoPatterns }) => {
25403
25403
  try {
25404
- await assertProjectInOrg(client, projectId, ctx.orgId);
25404
+ await assertProjectInOrg(client, systemId, ctx.orgId);
25405
25405
  } catch (err) {
25406
25406
  const message = err instanceof Error ? err.message : String(err);
25407
25407
  return { content: [{ type: "text", text: `Access denied: ${message}` }], isError: true };
@@ -25409,7 +25409,7 @@ function registerLayerTools(server, ctx, client) {
25409
25409
  let layer;
25410
25410
  try {
25411
25411
  layer = await client.createSystemLayer({
25412
- projectId,
25412
+ systemId,
25413
25413
  layerKey,
25414
25414
  name,
25415
25415
  ...description !== void 0 && { description },
@@ -25421,7 +25421,7 @@ function registerLayerTools(server, ctx, client) {
25421
25421
  return { content: [{ type: "text", text: `Failed to create Layer: ${message}` }], isError: true };
25422
25422
  }
25423
25423
  if (!layer) {
25424
- return { content: [{ type: "text", text: `System "${projectId}" not found or not writable.` }], isError: true };
25424
+ return { content: [{ type: "text", text: `System "${systemId}" not found or not writable.` }], isError: true };
25425
25425
  }
25426
25426
  return { content: [{ type: "text", text: `Created Layer.
25427
25427
 
@@ -25432,15 +25432,15 @@ ${formatLayerDetail(layer)}` }] };
25432
25432
  "update_layer",
25433
25433
  "Update a Layer's editable fields (name, description, repo mapping). Omitted fields are left unchanged. Archiving/reviving a Layer goes through transition_layer, not this tool. Requires write access to the System.",
25434
25434
  {
25435
- projectId: external_exports.string().describe("The System ID the Layer belongs to (for access control)"),
25435
+ systemId: external_exports.string().describe("The System ID the Layer belongs to (for access control)"),
25436
25436
  layerId: external_exports.string().describe("The Layer ID to update"),
25437
25437
  name: external_exports.string().optional().describe("New display name"),
25438
25438
  description: external_exports.string().optional().describe("New description"),
25439
25439
  repoPatterns: external_exports.array(external_exports.string()).optional().describe("Replacement repo mapping (replaces the existing list)")
25440
25440
  },
25441
- async ({ projectId, layerId, name, description, repoPatterns }) => {
25441
+ async ({ systemId, layerId, name, description, repoPatterns }) => {
25442
25442
  try {
25443
- await assertProjectInOrg(client, projectId, ctx.orgId);
25443
+ await assertProjectInOrg(client, systemId, ctx.orgId);
25444
25444
  } catch (err) {
25445
25445
  const message = err instanceof Error ? err.message : String(err);
25446
25446
  return { content: [{ type: "text", text: `Access denied: ${message}` }], isError: true };
@@ -25453,8 +25453,8 @@ ${formatLayerDetail(layer)}` }] };
25453
25453
  return { content: [{ type: "text", text: `Failed to load Layer: ${message}` }], isError: true };
25454
25454
  }
25455
25455
  if (!existing) return { content: [{ type: "text", text: `Layer not found: "${layerId}"` }], isError: true };
25456
- if (existing.projectId !== projectId) {
25457
- return { content: [{ type: "text", text: `Layer "${layerId}" does not belong to System "${projectId}".` }], isError: true };
25456
+ if (existing.systemId !== systemId) {
25457
+ return { content: [{ type: "text", text: `Layer "${layerId}" does not belong to System "${systemId}".` }], isError: true };
25458
25458
  }
25459
25459
  let layer;
25460
25460
  try {
@@ -25477,14 +25477,14 @@ ${formatLayerDetail(layer)}` }] };
25477
25477
  "transition_layer",
25478
25478
  "Archive or revive a Layer. Archiving requires a reason (audit trail); reviving sets it back to active. Requires write access to the System.",
25479
25479
  {
25480
- projectId: external_exports.string().describe("The System ID the Layer belongs to (for access control)"),
25480
+ systemId: external_exports.string().describe("The System ID the Layer belongs to (for access control)"),
25481
25481
  layerId: external_exports.string().describe("The Layer ID to transition"),
25482
25482
  status: external_exports.enum(["active", "archived"]).describe('Target status: "archived" to retire, "active" to revive'),
25483
25483
  reason: external_exports.string().optional().describe('Why the Layer is being archived (required when status is "archived")')
25484
25484
  },
25485
- async ({ projectId, layerId, status, reason }) => {
25485
+ async ({ systemId, layerId, status, reason }) => {
25486
25486
  try {
25487
- await assertProjectInOrg(client, projectId, ctx.orgId);
25487
+ await assertProjectInOrg(client, systemId, ctx.orgId);
25488
25488
  } catch (err) {
25489
25489
  const message = err instanceof Error ? err.message : String(err);
25490
25490
  return { content: [{ type: "text", text: `Access denied: ${message}` }], isError: true };
@@ -25497,8 +25497,8 @@ ${formatLayerDetail(layer)}` }] };
25497
25497
  return { content: [{ type: "text", text: `Failed to load Layer: ${message}` }], isError: true };
25498
25498
  }
25499
25499
  if (!existing) return { content: [{ type: "text", text: `Layer not found: "${layerId}"` }], isError: true };
25500
- if (existing.projectId !== projectId) {
25501
- return { content: [{ type: "text", text: `Layer "${layerId}" does not belong to System "${projectId}".` }], isError: true };
25500
+ if (existing.systemId !== systemId) {
25501
+ return { content: [{ type: "text", text: `Layer "${layerId}" does not belong to System "${systemId}".` }], isError: true };
25502
25502
  }
25503
25503
  let layer;
25504
25504
  try {
@@ -25823,13 +25823,13 @@ function registerNextActionsTools(server, ctx, client) {
25823
25823
  "get_next_actions",
25824
25824
  "Get prioritized list of recommended next actions for a project. Analyzes Beats, Revisions, and Notes to surface the highest-priority work: P0 (urgent bugs) \u2192 P1 (deploy) \u2192 P2 (build) \u2192 P3 (plan) \u2192 P4 (compose) \u2192 P5 (maintenance).",
25825
25825
  {
25826
- projectId: external_exports.string().describe("The project ID"),
25826
+ systemId: external_exports.string().describe("The project ID"),
25827
25827
  limit: external_exports.coerce.number().optional().default(10).describe("Max actions to return")
25828
25828
  },
25829
- async ({ projectId, limit }) => {
25829
+ async ({ systemId, limit }) => {
25830
25830
  try {
25831
- await assertProjectInOrg(client, projectId, ctx.orgId);
25832
- const actions = await client.getNextActions(projectId, { limit });
25831
+ await assertProjectInOrg(client, systemId, ctx.orgId);
25832
+ const actions = await client.getNextActions(systemId, { limit });
25833
25833
  return { content: [{ type: "text", text: formatNextActions(actions) }] };
25834
25834
  } catch (err) {
25835
25835
  const message = err instanceof Error ? err.message : String(err);
@@ -25841,12 +25841,12 @@ function registerNextActionsTools(server, ctx, client) {
25841
25841
  "list_triage_actions",
25842
25842
  "List active triage actions from the ledger (open + acted). This is the stateful view of what needs attention \u2014 includes status tracking, task progress, and escalation state. Use this instead of get_next_actions when triage has been run.",
25843
25843
  {
25844
- projectId: external_exports.string().describe("The project ID")
25844
+ systemId: external_exports.string().describe("The project ID")
25845
25845
  },
25846
- async ({ projectId }) => {
25846
+ async ({ systemId }) => {
25847
25847
  try {
25848
- await assertProjectInOrg(client, projectId, ctx.orgId);
25849
- const actions = await client.listTriageActions(projectId);
25848
+ await assertProjectInOrg(client, systemId, ctx.orgId);
25849
+ const actions = await client.listTriageActions(systemId);
25850
25850
  return { content: [{ type: "text", text: formatTriageActions(actions) }] };
25851
25851
  } catch (err) {
25852
25852
  const message = err instanceof Error ? err.message : String(err);
@@ -25858,13 +25858,13 @@ function registerNextActionsTools(server, ctx, client) {
25858
25858
  "dismiss_triage_action",
25859
25859
  'Dismiss a triage action (human decides "not now"). The action will not be re-escalated until the underlying state changes.',
25860
25860
  {
25861
- projectId: external_exports.string().describe("The project ID"),
25861
+ systemId: external_exports.string().describe("The project ID"),
25862
25862
  actionKey: external_exports.string().describe("The action key to dismiss (from list_triage_actions)")
25863
25863
  },
25864
- async ({ projectId, actionKey }) => {
25864
+ async ({ systemId, actionKey }) => {
25865
25865
  try {
25866
- await assertProjectInOrg(client, projectId, ctx.orgId);
25867
- await client.dismissTriageAction(projectId, actionKey, ctx.user?.userId ?? "mcp");
25866
+ await assertProjectInOrg(client, systemId, ctx.orgId);
25867
+ await client.dismissTriageAction(systemId, actionKey, ctx.user?.userId ?? "mcp");
25868
25868
  return { content: [{ type: "text", text: `Action ${actionKey} dismissed.` }] };
25869
25869
  } catch (err) {
25870
25870
  const message = err instanceof Error ? err.message : String(err);
@@ -26216,25 +26216,35 @@ var NoteStatusSchema = external_exports.enum([
26216
26216
  ]);
26217
26217
  var SystemStatusSchema = external_exports.enum(["active", "archived", "draft"]);
26218
26218
  var ChangeImportanceSchema = external_exports.enum(["minor", "moderate", "major"]);
26219
- var BeatApiSchema = external_exports.object({
26220
- beatId: external_exports.string(),
26221
- projectId: external_exports.string(),
26222
- /** Source project's title; attached by the flat /api/beats endpoint for
26223
- * multi-project (teamspace/org) responses. */
26224
- projectName: external_exports.string().nullish(),
26225
- title: external_exports.string(),
26226
- version: external_exports.number().nullish(),
26227
- description: external_exports.string().nullish(),
26228
- beatStatus: BeatStatusSchema.nullish(),
26229
- tags: external_exports.array(external_exports.string()).nullish(),
26230
- priority: external_exports.number().nullish(),
26231
- estimatedEffort: external_exports.string().nullish(),
26232
- humanAssignee: HumanAssigneeSchema.nullish(),
26233
- agentAssignee: AgentAssigneeSchema.nullish(),
26234
- revisionCount: external_exports.number().nullish(),
26235
- createdAt: external_exports.string(),
26236
- updatedAt: external_exports.string()
26237
- }).passthrough();
26219
+ var BeatApiSchema = external_exports.preprocess(
26220
+ (raw) => {
26221
+ if (typeof raw !== "object" || raw === null) return raw;
26222
+ const b = raw;
26223
+ const id = b["systemId"] ?? b["projectId"];
26224
+ if (!id) return raw;
26225
+ return { ...b, systemId: id, projectId: id };
26226
+ },
26227
+ external_exports.object({
26228
+ beatId: external_exports.string(),
26229
+ systemId: external_exports.string(),
26230
+ projectId: external_exports.string(),
26231
+ /** Source project's title; attached by the flat /api/beats endpoint for
26232
+ * multi-project (teamspace/org) responses. */
26233
+ projectName: external_exports.string().nullish(),
26234
+ title: external_exports.string(),
26235
+ version: external_exports.number().nullish(),
26236
+ description: external_exports.string().nullish(),
26237
+ beatStatus: BeatStatusSchema.nullish(),
26238
+ tags: external_exports.array(external_exports.string()).nullish(),
26239
+ priority: external_exports.number().nullish(),
26240
+ estimatedEffort: external_exports.string().nullish(),
26241
+ humanAssignee: HumanAssigneeSchema.nullish(),
26242
+ agentAssignee: AgentAssigneeSchema.nullish(),
26243
+ revisionCount: external_exports.number().nullish(),
26244
+ createdAt: external_exports.string(),
26245
+ updatedAt: external_exports.string()
26246
+ }).passthrough()
26247
+ );
26238
26248
  var RevisionScmSchema = external_exports.object({
26239
26249
  provider: external_exports.literal("github"),
26240
26250
  number: external_exports.string(),
@@ -26865,21 +26875,31 @@ var AccountSystemApiSchema = external_exports.object({
26865
26875
  var AccountSystemsResponseSchema = external_exports.object({ systems: external_exports.array(AccountSystemApiSchema) }).passthrough();
26866
26876
  var LAYER_STATUSES = ["active", "archived"];
26867
26877
  var LayerStatusApiSchema = external_exports.enum(LAYER_STATUSES);
26868
- var LayerApiSchema = external_exports.object({
26869
- layerId: external_exports.string(),
26870
- projectId: external_exports.string(),
26871
- layerKey: external_exports.string(),
26872
- name: external_exports.string(),
26873
- description: external_exports.string().nullish(),
26874
- parentLayerId: external_exports.string().nullish(),
26875
- repoPatterns: external_exports.array(external_exports.string()).nullish(),
26876
- status: LayerStatusApiSchema,
26877
- archivedAt: external_exports.string().nullish(),
26878
- createdBy: external_exports.string().nullish(),
26879
- version: external_exports.number().nullish(),
26880
- createdAt: external_exports.string(),
26881
- updatedAt: external_exports.string()
26882
- }).passthrough();
26878
+ var LayerApiSchema = external_exports.preprocess(
26879
+ (raw) => {
26880
+ if (typeof raw !== "object" || raw === null) return raw;
26881
+ const l = raw;
26882
+ const id = l["systemId"] ?? l["projectId"];
26883
+ if (!id) return raw;
26884
+ return { ...l, systemId: id, projectId: id };
26885
+ },
26886
+ external_exports.object({
26887
+ layerId: external_exports.string(),
26888
+ systemId: external_exports.string(),
26889
+ projectId: external_exports.string(),
26890
+ layerKey: external_exports.string(),
26891
+ name: external_exports.string(),
26892
+ description: external_exports.string().nullish(),
26893
+ parentLayerId: external_exports.string().nullish(),
26894
+ repoPatterns: external_exports.array(external_exports.string()).nullish(),
26895
+ status: LayerStatusApiSchema,
26896
+ archivedAt: external_exports.string().nullish(),
26897
+ createdBy: external_exports.string().nullish(),
26898
+ version: external_exports.number().nullish(),
26899
+ createdAt: external_exports.string(),
26900
+ updatedAt: external_exports.string()
26901
+ }).passthrough()
26902
+ );
26883
26903
  var LayersResponseSchema = external_exports.object({ layers: external_exports.array(LayerApiSchema) }).passthrough();
26884
26904
  var LAYER_RUBRIC_SCOPES_LIST = ["account", "teamspace", "movement"];
26885
26905
  var LayerRubricScopeApiSchema = external_exports.enum(LAYER_RUBRIC_SCOPES_LIST);
@@ -27255,7 +27275,7 @@ function registerNoteTools(server, ctx, client) {
27255
27275
  "list_notes",
27256
27276
  "List Notes for a project. Without beatId: returns all notes (project-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.",
27257
27277
  {
27258
- projectId: external_exports.string().describe("The project ID"),
27278
+ systemId: external_exports.string().describe("The project ID"),
27259
27279
  beatId: external_exports.string().optional().describe("Scope to a specific beat (omit for all project notes)"),
27260
27280
  noteType: external_exports.union([external_exports.enum(NOTE_TYPE_VALUES), external_exports.array(external_exports.enum(NOTE_TYPE_VALUES)).min(1)]).optional().describe('Filter by note type \u2014 single value or array for multi-select (e.g. ["constraint", "assumption"])'),
27261
27281
  status: external_exports.enum(NOTE_STATUS_VALUES).optional().describe("Filter by status"),
@@ -27268,7 +27288,7 @@ function registerNoteTools(server, ctx, client) {
27268
27288
  cursor: external_exports.string().optional().describe("Opaque pagination cursor from a previous response \u2014 use for project-wide note pagination (no beatId)"),
27269
27289
  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.")
27270
27290
  },
27271
- async ({ projectId, beatId, noteType, status, revisionId, beatVersionId, sourceDocumentNoteId, significance, offset = 0, limit = 100, cursor, orderBy }) => {
27291
+ async ({ systemId, beatId, noteType, status, revisionId, beatVersionId, sourceDocumentNoteId, significance, offset = 0, limit = 100, cursor, orderBy }) => {
27272
27292
  if (Array.isArray(noteType) && noteType.length === 0) {
27273
27293
  return { content: [{ type: "text", text: "noteType must not be an empty array \u2014 omit it to return all types." }], isError: true };
27274
27294
  }
@@ -27280,7 +27300,7 @@ function registerNoteTools(server, ctx, client) {
27280
27300
  return { content: [{ type: "text", text: "orderBy requires beatId \u2014 it is only supported for beat-scoped queries." }], isError: true };
27281
27301
  }
27282
27302
  try {
27283
- await assertProjectInOrg(client, projectId, ctx.orgId);
27303
+ await assertProjectInOrg(client, systemId, ctx.orgId);
27284
27304
  } catch (err) {
27285
27305
  const message = err instanceof Error ? err.message : String(err);
27286
27306
  return { content: [{ type: "text", text: `Access denied: ${message}` }], isError: true };
@@ -27320,7 +27340,7 @@ function registerNoteTools(server, ctx, client) {
27320
27340
  const SOURCE_DOC_FETCH_LIMIT = 5e3;
27321
27341
  let allNotesResult;
27322
27342
  try {
27323
- allNotesResult = await client.listAllSystemNotes(projectId, filters, SOURCE_DOC_FETCH_LIMIT);
27343
+ allNotesResult = await client.listAllSystemNotes(systemId, filters, SOURCE_DOC_FETCH_LIMIT);
27324
27344
  } catch (err) {
27325
27345
  const message = err instanceof Error ? err.message : String(err);
27326
27346
  return { content: [{ type: "text", text: `Failed to list project notes: ${message}` }], isError: true };
@@ -27341,7 +27361,7 @@ function registerNoteTools(server, ctx, client) {
27341
27361
  } else {
27342
27362
  let projectNotesResult;
27343
27363
  try {
27344
- projectNotesResult = await client.listAllSystemNotes(projectId, filters, limit, cursor);
27364
+ projectNotesResult = await client.listAllSystemNotes(systemId, filters, limit, cursor);
27345
27365
  } catch (err) {
27346
27366
  const message = err instanceof Error ? err.message : String(err);
27347
27367
  return { content: [{ type: "text", text: `Failed to list project notes: ${message}` }], isError: true };
@@ -27423,12 +27443,12 @@ function registerNoteTools(server, ctx, client) {
27423
27443
  "list_documents",
27424
27444
  "List uploaded documents for a project with processing status, filename, child note count, and AI summary. Use get_note with a document note ID for full details.",
27425
27445
  {
27426
- projectId: external_exports.string().describe("The project ID")
27446
+ systemId: external_exports.string().describe("The project ID")
27427
27447
  },
27428
- async ({ projectId }) => {
27429
- await assertProjectInOrg(client, projectId, ctx.orgId);
27448
+ async ({ systemId }) => {
27449
+ await assertProjectInOrg(client, systemId, ctx.orgId);
27430
27450
  const DOCUMENT_FETCH_LIMIT = 5e3;
27431
- const { notes: docs, hasMore: truncated } = await client.listAllSystemNotes(projectId, { noteType: "document" }, DOCUMENT_FETCH_LIMIT);
27451
+ const { notes: docs, hasMore: truncated } = await client.listAllSystemNotes(systemId, { noteType: "document" }, DOCUMENT_FETCH_LIMIT);
27432
27452
  const visibleDocs = docs.filter((d) => d.status !== DISMISSED_NOTE_STATUS);
27433
27453
  const truncationNotice = truncated && visibleDocs.length > 0 ? "\n\n> \u26A0\uFE0F This project has more than 5000 documents. Some documents may not be shown.\n" : "";
27434
27454
  const text = formatDocumentList(visibleDocs) + truncationNotice;
@@ -27439,16 +27459,16 @@ function registerNoteTools(server, ctx, client) {
27439
27459
  "get_note",
27440
27460
  "Get full details of a single Note by ID",
27441
27461
  {
27442
- projectId: external_exports.string().optional().describe("The project ID (for access control). Provide projectId or orgId, not both."),
27443
- orgId: external_exports.string().optional().describe("The org ID (for org-level notes). Provide orgId or projectId, not both."),
27462
+ systemId: external_exports.string().optional().describe("The project ID (for access control). Provide systemId or orgId, not both."),
27463
+ orgId: external_exports.string().optional().describe("The org ID (for org-level notes). Provide orgId or systemId, not both."),
27444
27464
  noteId: external_exports.string().describe("The note ID")
27445
27465
  },
27446
- async ({ projectId, orgId, noteId }) => {
27447
- if (projectId && orgId) {
27448
- return { content: [{ type: "text", text: "Provide projectId or orgId, not both." }], isError: true };
27466
+ async ({ systemId, orgId, noteId }) => {
27467
+ if (systemId && orgId) {
27468
+ return { content: [{ type: "text", text: "Provide systemId or orgId, not both." }], isError: true };
27449
27469
  }
27450
- if (!projectId && !orgId) {
27451
- return { content: [{ type: "text", text: "Provide projectId or orgId (for access control)." }], isError: true };
27470
+ if (!systemId && !orgId) {
27471
+ return { content: [{ type: "text", text: "Provide systemId or orgId (for access control)." }], isError: true };
27452
27472
  }
27453
27473
  try {
27454
27474
  if (orgId) {
@@ -27461,7 +27481,7 @@ function registerNoteTools(server, ctx, client) {
27461
27481
  }
27462
27482
  return { content: [{ type: "text", text: formatNoteDetail(note2) }] };
27463
27483
  }
27464
- await assertProjectInOrg(client, projectId, ctx.orgId);
27484
+ await assertProjectInOrg(client, systemId, ctx.orgId);
27465
27485
  const note = await client.getNote(noteId);
27466
27486
  if (!note) {
27467
27487
  return { content: [{ type: "text", text: `Note not found: "${noteId}"` }], isError: true };
@@ -27476,15 +27496,15 @@ function registerNoteTools(server, ctx, client) {
27476
27496
  );
27477
27497
  server.tool(
27478
27498
  "create_note",
27479
- "Create a new Note. Provide exactly one of projectId, teamspaceId, movementId, or none (org-level): projectId scopes the note to a project (optionally with beatId), teamspaceId scopes it to a teamspace (no cascade to child projects), 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 projects.",
27499
+ "Create a new Note. Provide exactly one of systemId, teamspaceId, movementId, or none (org-level): systemId scopes the note to a project (optionally with beatId), teamspaceId scopes it to a teamspace (no cascade to child projects), 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 projects.",
27480
27500
  {
27481
- projectId: external_exports.string().optional().describe("The project ID (omit for teamspace-, movement-, or org-level note)"),
27501
+ systemId: external_exports.string().optional().describe("The project ID (omit for teamspace-, movement-, or org-level note)"),
27482
27502
  teamspaceId: external_exports.string().optional().describe("The teamspace ID (omit for project-, movement-, or org-level note)"),
27483
27503
  movementId: external_exports.string().min(1).optional().describe("The Movement ID (omit for project-, teamspace-, or org-level note)"),
27484
27504
  noteType: external_exports.enum(NOTE_TYPE_VALUES).describe("The type of note"),
27485
27505
  content: external_exports.string().describe("The note content"),
27486
- beatId: external_exports.string().optional().describe("Beat ID if this note is beat-scoped (requires projectId)"),
27487
- revisionId: external_exports.string().optional().describe("Revision ID if this note is revision-scoped (requires projectId)"),
27506
+ beatId: external_exports.string().optional().describe("Beat ID if this note is beat-scoped (requires systemId)"),
27507
+ revisionId: external_exports.string().optional().describe("Revision ID if this note is revision-scoped (requires systemId)"),
27488
27508
  beatVersionId: external_exports.string().optional().describe("Beat Version ID if this note governs a specific planning increment (requires beatId)"),
27489
27509
  rationale: external_exports.string().optional().describe("Why this note exists"),
27490
27510
  confidence: external_exports.coerce.number().min(0).max(1).optional().describe("Confidence level for assumptions (0-1)"),
@@ -27492,20 +27512,20 @@ function registerNoteTools(server, ctx, client) {
27492
27512
  dependsOnNotes: external_exports.array(external_exports.string()).optional().describe("Note IDs this note depends on (child \u2192 parent links for consolidation)"),
27493
27513
  significance: external_exports.enum(DECISION_SIGNIFICANCE_VALUES2).optional().describe("Significance tier for decision Notes (strategic | structural | implementation). Defaults to implementation when unset. Only meaningful on noteType=decision.")
27494
27514
  },
27495
- async ({ projectId, teamspaceId, movementId, noteType, content, beatId, revisionId, beatVersionId, rationale, confidence, affectsBeats, dependsOnNotes, significance }) => {
27515
+ async ({ systemId, teamspaceId, movementId, noteType, content, beatId, revisionId, beatVersionId, rationale, confidence, affectsBeats, dependsOnNotes, significance }) => {
27496
27516
  try {
27497
- if ([projectId, teamspaceId, movementId].filter(Boolean).length > 1) {
27498
- return { content: [{ type: "text", text: "Specify at most one of projectId, teamspaceId, or movementId." }], isError: true };
27517
+ if ([systemId, teamspaceId, movementId].filter(Boolean).length > 1) {
27518
+ return { content: [{ type: "text", text: "Specify at most one of systemId, teamspaceId, or movementId." }], isError: true };
27499
27519
  }
27500
27520
  const assumptionMeta = noteType === "assumption" && confidence !== void 0 ? { confidence } : void 0;
27501
27521
  let note;
27502
- if (projectId) {
27503
- await assertProjectInOrg(client, projectId, ctx.orgId);
27504
- const projectCode = projectId.substring(0, 4).toUpperCase();
27505
- const noteId = await client.getNextNoteId(projectCode, projectId);
27522
+ if (systemId) {
27523
+ await assertProjectInOrg(client, systemId, ctx.orgId);
27524
+ const projectCode = systemId.substring(0, 4).toUpperCase();
27525
+ const noteId = await client.getNextNoteId(projectCode, systemId);
27506
27526
  note = await client.createNote({
27507
27527
  noteId,
27508
- projectId,
27528
+ projectId: systemId,
27509
27529
  beatId,
27510
27530
  revisionId,
27511
27531
  beatVersionId,
@@ -27577,8 +27597,8 @@ ${text}` }] };
27577
27597
  "update_note",
27578
27598
  "Update a Note (status, content, response, etc.). humanAssignee and agentAssignee are independent fields \u2014 both can be set simultaneously (a note can have a human owner and an agent collaborator). Set either to null to clear it.",
27579
27599
  {
27580
- projectId: external_exports.string().optional().describe("The project ID (for access control). Provide projectId or orgId, not both."),
27581
- orgId: external_exports.string().optional().describe("The org ID (for org-level notes). Provide orgId or projectId, not both."),
27600
+ systemId: external_exports.string().optional().describe("The project ID (for access control). Provide systemId or orgId, not both."),
27601
+ orgId: external_exports.string().optional().describe("The org ID (for org-level notes). Provide orgId or systemId, not both."),
27582
27602
  noteId: external_exports.string().describe("The note ID to update"),
27583
27603
  noteType: external_exports.enum(NOTE_TYPE_VALUES).optional().describe("Reclassify the note type (e.g. assumption \u2192 guidance)"),
27584
27604
  status: external_exports.enum(NOTE_STATUS_VALUES).optional().describe("New status"),
@@ -27592,13 +27612,13 @@ ${text}` }] };
27592
27612
  agentAssignee: agentAssigneeSchema.nullable().optional().describe("Assign or reassign this note to an agent (null to clear)"),
27593
27613
  significance: external_exports.enum(DECISION_SIGNIFICANCE_VALUES2).optional().describe("Significance tier for decision Notes (strategic | structural | implementation). Only meaningful on noteType=decision.")
27594
27614
  },
27595
- async ({ projectId, orgId, noteId, noteType, status, content, rationale, response, beatId, revisionId, dependsOnNotes, humanAssignee, agentAssignee, significance }) => {
27615
+ async ({ systemId, orgId, noteId, noteType, status, content, rationale, response, beatId, revisionId, dependsOnNotes, humanAssignee, agentAssignee, significance }) => {
27596
27616
  try {
27597
- if (projectId && orgId) {
27598
- return { content: [{ type: "text", text: "Provide projectId or orgId, not both." }], isError: true };
27617
+ if (systemId && orgId) {
27618
+ return { content: [{ type: "text", text: "Provide systemId or orgId, not both." }], isError: true };
27599
27619
  }
27600
- if (!projectId && !orgId) {
27601
- return { content: [{ type: "text", text: "Provide projectId or orgId (for access control)." }], isError: true };
27620
+ if (!systemId && !orgId) {
27621
+ return { content: [{ type: "text", text: "Provide systemId or orgId (for access control)." }], isError: true };
27602
27622
  }
27603
27623
  if (orgId) {
27604
27624
  if (orgId !== ctx.orgId) {
@@ -27628,7 +27648,7 @@ ${text}` }] };
27628
27648
 
27629
27649
  ${formatNoteDetail(updated2)}` }] };
27630
27650
  }
27631
- await assertProjectInOrg(client, projectId, ctx.orgId);
27651
+ await assertProjectInOrg(client, systemId, ctx.orgId);
27632
27652
  const updated = await client.updateNote(noteId, {
27633
27653
  ...noteType !== void 0 && { noteType },
27634
27654
  status,
@@ -27659,7 +27679,7 @@ ${text}` }] };
27659
27679
  "bulk_update_notes",
27660
27680
  "Update multiple Notes in one call, applying the same fields to all. More efficient than calling update_note N times. Returns updated notes and any per-note failures.",
27661
27681
  {
27662
- projectId: external_exports.string().describe("The project ID (for access control)"),
27682
+ systemId: external_exports.string().describe("The project ID (for access control)"),
27663
27683
  noteIds: external_exports.array(external_exports.string()).min(1).max(100).describe("Note IDs to update (max 100 per call)"),
27664
27684
  noteType: external_exports.enum(NOTE_TYPE_VALUES).optional().describe("Reclassify all notes to this type"),
27665
27685
  status: external_exports.enum(NOTE_STATUS_VALUES).optional().describe("New status for all notes"),
@@ -27668,9 +27688,9 @@ ${text}` }] };
27668
27688
  humanAssignee: humanAssigneeSchema.nullable().optional().describe("Assign or clear human assignee on all notes (null to clear)"),
27669
27689
  agentAssignee: agentAssigneeSchema.nullable().optional().describe("Assign or clear agent assignee on all notes (null to clear)")
27670
27690
  },
27671
- async ({ projectId, noteIds, noteType, status, content, response, humanAssignee, agentAssignee }) => {
27691
+ async ({ systemId, noteIds, noteType, status, content, response, humanAssignee, agentAssignee }) => {
27672
27692
  try {
27673
- await assertProjectInOrg(client, projectId, ctx.orgId);
27693
+ await assertProjectInOrg(client, systemId, ctx.orgId);
27674
27694
  const result = await client.bulkUpdateNotes(noteIds, {
27675
27695
  ...noteType !== void 0 && { noteType },
27676
27696
  status,
@@ -27701,15 +27721,15 @@ ${text}` }] };
27701
27721
  "reassign_note",
27702
27722
  "Reassign a misattributed note to a different beat (with audit trail)",
27703
27723
  {
27704
- projectId: external_exports.string().describe("The project ID (for access control)"),
27724
+ systemId: external_exports.string().describe("The project ID (for access control)"),
27705
27725
  noteId: external_exports.string().describe("The note ID to reassign"),
27706
27726
  targetBeatId: external_exports.string().describe("The beat ID to move the note to"),
27707
27727
  targetRevisionId: external_exports.string().optional().describe("Optional revision ID to scope the note to"),
27708
27728
  reason: external_exports.string().describe("Why this note is being reassigned")
27709
27729
  },
27710
- async ({ projectId, noteId, targetBeatId, targetRevisionId, reason }) => {
27730
+ async ({ systemId, noteId, targetBeatId, targetRevisionId, reason }) => {
27711
27731
  try {
27712
- await assertProjectInOrg(client, projectId, ctx.orgId);
27732
+ await assertProjectInOrg(client, systemId, ctx.orgId);
27713
27733
  const updated = await client.reassignNote(noteId, targetBeatId, reason, ctx.user.userId, targetRevisionId);
27714
27734
  if (!updated) {
27715
27735
  return { content: [{ type: "text", text: `Note not found: "${noteId}"` }], isError: true };
@@ -27725,15 +27745,15 @@ ${text}` }] };
27725
27745
  "remove_note",
27726
27746
  "Soft-remove a misattributed note by dismissing it (with audit trail)",
27727
27747
  {
27728
- projectId: external_exports.string().min(1).describe("The project ID (for access control)"),
27748
+ systemId: external_exports.string().min(1).describe("The project ID (for access control)"),
27729
27749
  noteId: external_exports.string().min(1).describe("The note ID to remove"),
27730
27750
  reason: external_exports.string().min(1).max(500).optional().describe('Why this note is being removed (defaults to "Removed via remove_note")')
27731
27751
  },
27732
- async ({ projectId, noteId, reason }) => {
27752
+ async ({ systemId, noteId, reason }) => {
27733
27753
  try {
27734
- await assertProjectInOrg(client, projectId, ctx.orgId);
27754
+ await assertProjectInOrg(client, systemId, ctx.orgId);
27735
27755
  const note = await client.getNote(noteId);
27736
- if (!note || note.projectId !== projectId) {
27756
+ if (!note || (note.systemId ?? note.projectId) !== systemId) {
27737
27757
  return { content: [{ type: "text", text: `Note not found: "${noteId}"` }], isError: true };
27738
27758
  }
27739
27759
  const removeReason = reason ?? "Removed via remove_note";
@@ -27752,15 +27772,15 @@ ${text}` }] };
27752
27772
  "dismiss_document",
27753
27773
  "Soft-remove an uploaded document by dismissing it \u2014 it will no longer appear in list_documents output. Only works on document-type notes; use remove_note for non-document notes.",
27754
27774
  {
27755
- projectId: external_exports.string().min(1).describe("The project ID (for access control)"),
27775
+ systemId: external_exports.string().min(1).describe("The project ID (for access control)"),
27756
27776
  noteId: external_exports.string().min(1).describe("The document note ID to dismiss"),
27757
27777
  reason: external_exports.string().min(1).max(500).optional().describe('Why this document is being dismissed (defaults to "Dismissed via dismiss_document")')
27758
27778
  },
27759
- async ({ projectId, noteId, reason }) => {
27779
+ async ({ systemId, noteId, reason }) => {
27760
27780
  try {
27761
- await assertProjectInOrg(client, projectId, ctx.orgId);
27781
+ await assertProjectInOrg(client, systemId, ctx.orgId);
27762
27782
  const note = await client.getNote(noteId);
27763
- if (!note || note.projectId !== projectId) {
27783
+ if (!note || (note.systemId ?? note.projectId) !== systemId) {
27764
27784
  return { content: [{ type: "text", text: `Document not found: "${noteId}"` }], isError: true };
27765
27785
  }
27766
27786
  if (note.noteType !== "document") {
@@ -27785,11 +27805,11 @@ ${text}` }] };
27785
27805
  "list_key_assumptions",
27786
27806
  "Get key assumptions sorted by criticality (highest risk first)",
27787
27807
  {
27788
- projectId: external_exports.string().describe("The project ID")
27808
+ systemId: external_exports.string().describe("The project ID")
27789
27809
  },
27790
- async ({ projectId }) => {
27791
- await assertProjectInOrg(client, projectId, ctx.orgId);
27792
- const assumptions = await client.getKeyAssumptions(projectId);
27810
+ async ({ systemId }) => {
27811
+ await assertProjectInOrg(client, systemId, ctx.orgId);
27812
+ const assumptions = await client.getKeyAssumptions(systemId);
27793
27813
  if (assumptions.length === 0) {
27794
27814
  return { content: [{ type: "text", text: "No unvalidated assumptions found." }] };
27795
27815
  }
@@ -28051,12 +28071,12 @@ ${formatTree(tree.root)}` }] };
28051
28071
  heading: external_exports.string().min(1).max(200).describe('Section label, e.g. "Decision Log"'),
28052
28072
  parentNoteId: external_exports.string().optional().describe("Parent page's Note ID; defaults to the notebook root"),
28053
28073
  noteType: external_exports.enum(["context", "constraint", "guidance", "decision", "document"]).optional().describe("Note type to collect (default: decision)"),
28054
- projectId: external_exports.string().optional().describe("Project scope; if omitted, the Notebook's first bound project scope is used"),
28074
+ systemId: external_exports.string().optional().describe("Project scope; if omitted, the Notebook's first bound project scope is used"),
28055
28075
  limit: external_exports.number().int().min(1).max(500).optional().describe("Max members to show (default: 25)"),
28056
28076
  order: external_exports.enum(["newest", "oldest"]).optional().describe("Sort order (default: newest)"),
28057
28077
  significance: external_exports.enum(["strategic", "structural", "implementation"]).optional().describe("Only for decision notes \u2014 filter to a significance tier (e.g. strategic). Omit to include all.")
28058
28078
  },
28059
- async ({ notebookId, heading, parentNoteId, noteType, projectId, limit, order, significance }) => {
28079
+ async ({ notebookId, heading, parentNoteId, noteType, systemId, limit, order, significance }) => {
28060
28080
  let notebook;
28061
28081
  try {
28062
28082
  notebook = await fetchNotebookInOrg(client, notebookId, ctx.orgId);
@@ -28069,7 +28089,7 @@ ${formatTree(tree.root)}` }] };
28069
28089
  isError: true
28070
28090
  };
28071
28091
  }
28072
- let scopeProjectId = projectId;
28092
+ let scopeProjectId = systemId;
28073
28093
  if (!scopeProjectId) {
28074
28094
  try {
28075
28095
  const bindings = await client.getNotebookScopeBindings(notebookId);
@@ -28078,7 +28098,7 @@ ${formatTree(tree.root)}` }] };
28078
28098
  }
28079
28099
  }
28080
28100
  if (!scopeProjectId) {
28081
- return { content: [{ type: "text", text: "No project scope: pass projectId or bind the notebook to a project first." }], isError: true };
28101
+ return { content: [{ type: "text", text: "No project scope: pass systemId or bind the notebook to a project first." }], isError: true };
28082
28102
  }
28083
28103
  let tree;
28084
28104
  try {
@@ -28139,12 +28159,12 @@ ${formatTree(tree.root)}` }] };
28139
28159
  parentNoteId: external_exports.string().optional().describe("The page Note ID the section sits under; defaults to the notebook root"),
28140
28160
  heading: external_exports.string().min(1).max(200).optional().describe("New section label"),
28141
28161
  noteType: external_exports.enum(["context", "constraint", "guidance", "decision", "document"]).optional().describe("New note type to collect"),
28142
- projectId: external_exports.string().optional().describe("New project scope"),
28162
+ systemId: external_exports.string().optional().describe("New project scope"),
28143
28163
  limit: external_exports.number().int().min(1).max(500).optional().describe("New max members to show"),
28144
28164
  order: external_exports.enum(["newest", "oldest"]).optional().describe("New sort order"),
28145
28165
  significance: external_exports.enum(["strategic", "structural", "implementation"]).optional().describe("New significance-tier filter (decision notes only)")
28146
28166
  },
28147
- async ({ notebookId, elementId, parentNoteId, heading, noteType, projectId, limit, order, significance }) => {
28167
+ async ({ notebookId, elementId, parentNoteId, heading, noteType, systemId, limit, order, significance }) => {
28148
28168
  let notebook;
28149
28169
  try {
28150
28170
  notebook = await fetchNotebookInOrg(client, notebookId, ctx.orgId);
@@ -28183,7 +28203,7 @@ ${formatTree(tree.root)}` }] };
28183
28203
  const base = existing.query;
28184
28204
  const mergedQuery = {
28185
28205
  source: "notes",
28186
- scope: projectId !== void 0 ? `project:${projectId}` : base.scope,
28206
+ scope: systemId !== void 0 ? `project:${systemId}` : base.scope,
28187
28207
  noteType: noteType ?? base.noteType,
28188
28208
  limit: limit ?? base.limit,
28189
28209
  sortBy: order !== void 0 ? order === "oldest" ? "createdAt_asc" : "createdAt_desc" : base.sortBy,
@@ -28485,14 +28505,14 @@ function registerOnboardingTools(server, ctx, client) {
28485
28505
  "The PM reviews and confirms \u2014 use create_beat for each approved Beat, create_note for any Note conversions."
28486
28506
  ].join(" "),
28487
28507
  {
28488
- projectId: external_exports.string().describe("The newly created project ID"),
28508
+ systemId: external_exports.string().describe("The newly created project ID"),
28489
28509
  projectTitle: external_exports.string().describe("The project title"),
28490
28510
  projectDescription: external_exports.string().optional().describe("Discovery context: pain points, goals, and constraints summary to inform the Beat structure"),
28491
28511
  analysisJson: external_exports.string().optional().describe("JSON-serialized DiscoveryAnalysis from initiate_teamspace_onboarding. Pass this to produce Beats grounded in the actual discovery data.")
28492
28512
  },
28493
- async ({ projectId, projectTitle, projectDescription, analysisJson }) => {
28494
- await assertProjectInOrg(client, projectId, ctx.orgId);
28495
- const result = await client.draftOnboardingBeats(projectId, { projectTitle, projectDescription, analysisJson });
28513
+ async ({ systemId, projectTitle, projectDescription, analysisJson }) => {
28514
+ await assertProjectInOrg(client, systemId, ctx.orgId);
28515
+ const result = await client.draftOnboardingBeats(systemId, { projectTitle, projectDescription, analysisJson });
28496
28516
  if (!result.suggestions || result.suggestions.length === 0) {
28497
28517
  return {
28498
28518
  content: [{ type: "text", text: "Failed to generate Beat suggestions. Try providing more project context." }]
@@ -28558,14 +28578,14 @@ Error: ${msg}`);
28558
28578
  for (const p of projects) {
28559
28579
  try {
28560
28580
  const project = await client.createSystem({
28561
- projectId: (0, import_node_crypto.randomUUID)(),
28581
+ systemId: (0, import_node_crypto.randomUUID)(),
28562
28582
  orgId: ctx.orgId,
28563
28583
  teamspaceId: teamspace.teamspaceId,
28564
28584
  ownerUserId: ctx.user?.userId ?? "system",
28565
28585
  title: p.title,
28566
28586
  description: p.description
28567
28587
  });
28568
- createdProjects.push({ projectId: project.projectId, title: project.title });
28588
+ createdProjects.push({ systemId: project.systemId, title: project.title });
28569
28589
  } catch (err) {
28570
28590
  const msg = err instanceof Error ? err.message : String(err);
28571
28591
  lines.push(`
@@ -28578,7 +28598,7 @@ Error: ${msg}`);
28578
28598
  if (createdProjects.length > 0) {
28579
28599
  lines.push(`
28580
28600
  Projects (${createdProjects.length}):`);
28581
- for (const p of createdProjects) lines.push(` [${p.projectId}] ${p.title}`);
28601
+ for (const p of createdProjects) lines.push(` [${p.systemId}] ${p.title}`);
28582
28602
  }
28583
28603
  const createdNotes = [];
28584
28604
  const teamspaceSlug = teamspace.slug ?? teamspaceName.toLowerCase().replace(/\s+/g, "-");
@@ -28604,7 +28624,7 @@ Projects (${createdProjects.length}):`);
28604
28624
  const n = await client.createNote({
28605
28625
  noteId: `N-TEMP-${(0, import_node_crypto.randomUUID)()}`,
28606
28626
  // overridden server-side
28607
- projectId: project.projectId,
28627
+ projectId: project.systemId,
28608
28628
  noteType: note.noteType,
28609
28629
  content: note.content,
28610
28630
  rationale: note.rationale,
@@ -28802,7 +28822,7 @@ function registerOrganizationTools(server, ctx, client) {
28802
28822
  }
28803
28823
  const text = `System transferred.
28804
28824
 
28805
- **System ID:** ${transferred.projectId}
28825
+ **System ID:** ${transferred.systemId}
28806
28826
  **Title:** ${transferred.title}
28807
28827
  **New Org:** ${transferred.orgId}`;
28808
28828
  return { content: [{ type: "text", text }] };
@@ -28926,7 +28946,7 @@ ${d.rationale}${d.suggestions?.length ? `
28926
28946
  function registerPlanQualityTools(server, ctx, client) {
28927
28947
  const schema = {
28928
28948
  beatVersionId: external_exports.string().describe("The Beat Version ID (e.g., bv-abc123). beat_version_quality targets Beat Versions only."),
28929
- projectId: external_exports.string().describe("The project ID"),
28949
+ systemId: external_exports.string().describe("The project ID"),
28930
28950
  // The completeness dimension probes the codebase, so this tool accepts the
28931
28951
  // same repo overrides as run_check — without them the probe could only ever
28932
28952
  // read the default branch from this surface.
@@ -28934,7 +28954,7 @@ function registerPlanQualityTools(server, ctx, client) {
28934
28954
  localPath: external_exports.string().optional().describe("Absolute path to an already-cloned local copy of the repo \u2014 the completeness probe reads it instead of cloning"),
28935
28955
  wait: external_exports.boolean().optional().describe("If true, block until the check completes and return the scorecard inline. Default: false (returns taskId immediately).")
28936
28956
  };
28937
- const handler = async ({ beatVersionId, projectId, branch, localPath, wait }) => {
28957
+ const handler = async ({ beatVersionId, systemId, branch, localPath, wait }) => {
28938
28958
  try {
28939
28959
  const targetError = beatVersionQualityTargetError(beatVersionId ?? "");
28940
28960
  if (targetError) {
@@ -28943,9 +28963,9 @@ function registerPlanQualityTools(server, ctx, client) {
28943
28963
  isError: true
28944
28964
  };
28945
28965
  }
28946
- await assertProjectInOrg(client, projectId, ctx.orgId);
28966
+ await assertProjectInOrg(client, systemId, ctx.orgId);
28947
28967
  const opts = branch || localPath ? { branch, localPath } : void 0;
28948
- const task = await client.runCheck(projectId, "beat_version_quality", beatVersionId, opts);
28968
+ const task = await client.runCheck(systemId, "beat_version_quality", beatVersionId, opts);
28949
28969
  if (!wait) {
28950
28970
  return {
28951
28971
  content: [{
@@ -29085,22 +29105,22 @@ function registerPortfolioCoherenceTools(server, ctx, client) {
29085
29105
  "check_portfolio_coherence",
29086
29106
  "Run a portfolio coherence check on a project. Clusters Beats by semantic similarity to surface fragmentation (merge candidates) and checks what fraction of Beats name an outcome their authoriser would recognise. By default returns a taskId immediately \u2014 set wait=true to block and receive the full report inline.",
29087
29107
  {
29088
- projectId: external_exports.string().min(1).describe("The project ID"),
29108
+ systemId: external_exports.string().min(1).describe("The project ID"),
29089
29109
  wait: external_exports.boolean().optional().describe("Block until the check completes and return the report inline. Default: false.")
29090
29110
  },
29091
- async ({ projectId, wait }) => {
29111
+ async ({ systemId, wait }) => {
29092
29112
  try {
29093
- await assertProjectInOrg(client, projectId, ctx.orgId);
29094
- const { taskId } = await client.runCheck(projectId, "portfolio_coherence", projectId);
29113
+ await assertProjectInOrg(client, systemId, ctx.orgId);
29114
+ const { taskId } = await client.runCheck(systemId, "portfolio_coherence", systemId);
29095
29115
  if (!wait) {
29096
29116
  return {
29097
29117
  content: [{
29098
29118
  type: "text",
29099
29119
  text: [
29100
- `Portfolio coherence check enqueued for ${projectId}.`,
29120
+ `Portfolio coherence check enqueued for ${systemId}.`,
29101
29121
  `Task ID: ${taskId}`,
29102
29122
  "",
29103
- `Use get_job_status with jobId="${taskId}" to poll, or list_checks with projectId="${projectId}" and checkType="portfolio_coherence" to view results.`
29123
+ `Use get_job_status with jobId="${taskId}" to poll, or list_checks with systemId="${systemId}" and checkType="portfolio_coherence" to view results.`
29104
29124
  ].join("\n")
29105
29125
  }]
29106
29126
  };
@@ -29117,17 +29137,17 @@ function registerPortfolioCoherenceTools(server, ctx, client) {
29117
29137
  "consolidate_beats",
29118
29138
  "Consolidate a fragmented Beat cluster \u2014 identified by check_portfolio_coherence \u2014 into a single VP-level capability. By default (confirm omitted or false) returns a dry-run preview: surviving Beat, Beats to archive, Notes to reassign, and any in-flight Beat Versions. Set confirm=true to execute the merge.",
29119
29139
  {
29120
- projectId: external_exports.string().min(1).describe("The project ID \u2014 all beatIds must belong to this project"),
29140
+ systemId: external_exports.string().min(1).describe("The project ID \u2014 all beatIds must belong to this project"),
29121
29141
  beatIds: external_exports.array(external_exports.string().min(1)).min(2).max(50).describe("All Beat IDs in the cluster (including the one that will survive)"),
29122
29142
  primaryBeatId: external_exports.string().min(1).optional().describe("Beat ID that survives \u2014 defaults to beatIds[0] if omitted"),
29123
29143
  rationale: external_exports.string().max(2e3).optional().describe("Why these Beats are being merged \u2014 auto-generated if omitted"),
29124
29144
  confirm: external_exports.boolean().optional().describe("false/omitted = preview; true = execute the merge")
29125
29145
  },
29126
- async ({ projectId, beatIds, primaryBeatId, rationale, confirm }) => {
29146
+ async ({ systemId, beatIds, primaryBeatId, rationale, confirm }) => {
29127
29147
  const archivedBeatIds = [];
29128
29148
  let totalNotesReassigned = 0;
29129
29149
  try {
29130
- await assertProjectInOrg(client, projectId, ctx.orgId);
29150
+ await assertProjectInOrg(client, systemId, ctx.orgId);
29131
29151
  const uniqueBeatIds = [...new Set(beatIds)];
29132
29152
  const resolvedPrimaryId = primaryBeatId ?? uniqueBeatIds[0];
29133
29153
  if (!uniqueBeatIds.includes(resolvedPrimaryId)) {
@@ -29143,19 +29163,19 @@ function registerPortfolioCoherenceTools(server, ctx, client) {
29143
29163
  isError: true
29144
29164
  };
29145
29165
  }
29146
- const allProjectBeats = await client.listSystemBeats(projectId);
29166
+ const allProjectBeats = await client.listSystemBeats(systemId);
29147
29167
  const beatMap = new Map(allProjectBeats.map((b) => [b.beatId, b]));
29148
29168
  const primaryBeat = beatMap.get(resolvedPrimaryId);
29149
29169
  if (!primaryBeat) {
29150
- return { content: [{ type: "text", text: `Primary Beat "${resolvedPrimaryId}" not found in project "${projectId}".` }], isError: true };
29170
+ return { content: [{ type: "text", text: `Primary Beat "${resolvedPrimaryId}" not found in project "${systemId}".` }], isError: true };
29151
29171
  }
29152
29172
  const missingIds = duplicateBeatIds.filter((id) => !beatMap.has(id));
29153
29173
  if (missingIds.length > 0) {
29154
- return { content: [{ type: "text", text: `Beat(s) not found in project "${projectId}": ${missingIds.join(", ")}` }], isError: true };
29174
+ return { content: [{ type: "text", text: `Beat(s) not found in project "${systemId}": ${missingIds.join(", ")}` }], isError: true };
29155
29175
  }
29156
29176
  const dupBvResults = await Promise.allSettled(
29157
29177
  duplicateBeatIds.map(
29158
- (id) => client.listBeatVersions(projectId, { beatId: id, includeTerminal: false })
29178
+ (id) => client.listBeatVersions(systemId, { beatId: id, includeTerminal: false })
29159
29179
  )
29160
29180
  );
29161
29181
  const bvRejected = dupBvResults.filter((r) => r.status === "rejected");
@@ -29322,17 +29342,25 @@ function registerRevisionLifecycleTools(server, ctx, client) {
29322
29342
  if (!beat) {
29323
29343
  return { content: [{ type: "text", text: `Beat not found: "${beatId}"` }], isError: true };
29324
29344
  }
29345
+ const beatSystemId = beat.systemId ?? beat.projectId;
29346
+ if (!beatSystemId) {
29347
+ return { content: [{ type: "text", text: `Beat "${beatId}" has no system identifier \u2014 record may be corrupt.` }], isError: true };
29348
+ }
29325
29349
  let parentBv;
29326
29350
  if (beatVersionId !== void 0) {
29327
29351
  parentBv = await client.getBeatVersion(beatVersionId);
29328
29352
  if (!parentBv) {
29329
29353
  return { content: [{ type: "text", text: `Beat Version not found: "${beatVersionId}"` }], isError: true };
29330
29354
  }
29331
- if (parentBv.beatId !== beatId || (parentBv.systemId ?? parentBv.projectId) !== (beat.systemId ?? beat.projectId)) {
29355
+ const bvSystemId = parentBv.systemId ?? parentBv.projectId;
29356
+ if (!bvSystemId) {
29357
+ return { content: [{ type: "text", text: `Beat Version "${beatVersionId}" has no system identifier \u2014 record may be corrupt.` }], isError: true };
29358
+ }
29359
+ if (parentBv.beatId !== beat.beatId || bvSystemId !== beatSystemId) {
29332
29360
  return { content: [{ type: "text", text: `Beat Version "${beatVersionId}" belongs to a different Beat.` }], isError: true };
29333
29361
  }
29334
29362
  }
29335
- const { revision, warnings } = await client.createRevision(beat.systemId ?? beat.projectId, beatId, {
29363
+ const { revision, warnings } = await client.createRevision(beatSystemId, beat.beatId, {
29336
29364
  title,
29337
29365
  description,
29338
29366
  changeSummary,
@@ -29364,7 +29392,7 @@ function registerRevisionLifecycleTools(server, ctx, client) {
29364
29392
  }
29365
29393
  if (parentBv && !parentBv.targetDropId) {
29366
29394
  try {
29367
- const project = await client.getSystem(parentBv.projectId);
29395
+ const project = await client.getSystem(parentBv.systemId);
29368
29396
  const accountId = project?.accountId;
29369
29397
  if (accountId) {
29370
29398
  const draftDrops = await client.listAccountDrops(accountId, { state: "draft" });
@@ -29420,7 +29448,12 @@ function registerRevisionLifecycleTools(server, ctx, client) {
29420
29448
  if (!bv) {
29421
29449
  return { content: [{ type: "text", text: `Beat Version not found: "${beatVersionId}"` }], isError: true };
29422
29450
  }
29423
- if (bv.beatId !== revision.beatId || (bv.systemId ?? bv.projectId) !== (revision.systemId ?? revision.projectId)) {
29451
+ const bvSystemId = bv.systemId ?? bv.projectId;
29452
+ const revSystemId = revision.systemId ?? revision.projectId;
29453
+ if (!bvSystemId || !revSystemId) {
29454
+ return { content: [{ type: "text", text: `Beat Version "${beatVersionId}" has no system identifier \u2014 record may be corrupt.` }], isError: true };
29455
+ }
29456
+ if (bv.beatId !== revision.beatId || bvSystemId !== revSystemId) {
29424
29457
  return { content: [{ type: "text", text: `Beat Version "${beatVersionId}" belongs to a different Beat.` }], isError: true };
29425
29458
  }
29426
29459
  }
@@ -29589,7 +29622,7 @@ Accepts the full PR metadata (number, url, branch, state, optional mergeSha). Fi
29589
29622
  if (revision.scm) {
29590
29623
  lines2.push("", `\u26A0 This revision already has a linked PR (#${revision.scm.number} \u2014 ${revision.scm.url}). create_pr_for_revision would be rejected.`);
29591
29624
  } else {
29592
- const project = await client.getSystem(revision.projectId);
29625
+ const project = await client.getSystem(revision.systemId);
29593
29626
  if (!project?.repoOwner || !project?.repoName || !project?.repoDefaultBranch) {
29594
29627
  lines2.push("", "\u26A0 The project has no repo configured (repoOwner/repoName/repoDefaultBranch). create_pr_for_revision would be rejected.");
29595
29628
  } else {
@@ -29852,10 +29885,10 @@ function registerRevisionQualityTools(server, ctx, client) {
29852
29885
  "Run a five-dimension PR-readiness check on a Revision (Scoped, Traceable, Testable, Review-ready, Non-duplicating). Advisory only \u2014 no lifecycle transition or creation path gates on this score. By default returns a jobId immediately (fire-and-forget) \u2014 use get_job_status or list_checks to retrieve results. Set wait=true to block until the check completes and receive the scorecard inline. WARNING: wait=true holds the MCP session open for 30\u201360s \u2014 for bulk operations, omit wait and poll get_job_status separately.",
29853
29886
  {
29854
29887
  revisionId: external_exports.string().describe("The Revision ID (e.g., rev-abc123). revision_quality targets Revisions only."),
29855
- projectId: external_exports.string().describe("The project ID"),
29888
+ systemId: external_exports.string().describe("The project ID"),
29856
29889
  wait: external_exports.boolean().optional().describe("If true, block until the check completes and return the scorecard inline. Default: false (returns taskId immediately).")
29857
29890
  },
29858
- async ({ revisionId, projectId, wait }) => {
29891
+ async ({ revisionId, systemId, wait }) => {
29859
29892
  try {
29860
29893
  const targetError = revisionQualityTargetError(revisionId);
29861
29894
  if (targetError) {
@@ -29864,9 +29897,9 @@ function registerRevisionQualityTools(server, ctx, client) {
29864
29897
  isError: true
29865
29898
  };
29866
29899
  }
29867
- await assertProjectInOrg(client, projectId, ctx.orgId);
29868
- await assertRevisionInProject(client, revisionId, projectId);
29869
- const task = await client.runCheck(projectId, "revision_quality", revisionId);
29900
+ await assertProjectInOrg(client, systemId, ctx.orgId);
29901
+ await assertRevisionInProject(client, revisionId, systemId);
29902
+ const task = await client.runCheck(systemId, "revision_quality", revisionId);
29870
29903
  if (!wait) {
29871
29904
  return {
29872
29905
  content: [{
@@ -30330,10 +30363,10 @@ async function resolveSnapshotInput(input) {
30330
30363
  }
30331
30364
  function formatImportSummary(result, targetTeamspaceId) {
30332
30365
  if (!result.counts || result.counts.beats == null) {
30333
- throw new Error(`Import of system ${result.projectId} completed but returned no counts \u2014 check worker logs for a TransactionCanceledException`);
30366
+ throw new Error(`Import of system ${result.systemId} completed but returned no counts \u2014 check worker logs for a TransactionCanceledException`);
30334
30367
  }
30335
30368
  const lines = [
30336
- `Imported system: ${result.projectId}`,
30369
+ `Imported system: ${result.systemId}`,
30337
30370
  ` Beats: ${result.counts.beats}`,
30338
30371
  ` Proposals: ${result.counts.proposals}`,
30339
30372
  ` Revisions: ${result.counts.revisions}`,
@@ -30400,8 +30433,8 @@ function assertSnapshotShape(val, sourceHint) {
30400
30433
  throw new Error(`Snapshot${sourceHint} has an invalid or missing 'version' field \u2014 it may not be a valid project snapshot.`);
30401
30434
  }
30402
30435
  const systemOrProject = obj["system"] ?? obj["project"];
30403
- if (typeof systemOrProject !== "object" || systemOrProject === null || typeof systemOrProject["projectId"] !== "string") {
30404
- throw new Error(`Snapshot${sourceHint} is missing 'system.projectId' \u2014 it may not be a valid project snapshot.`);
30436
+ if (typeof systemOrProject !== "object" || systemOrProject === null || typeof systemOrProject["systemId"] !== "string") {
30437
+ throw new Error(`Snapshot${sourceHint} is missing 'system.systemId' \u2014 it may not be a valid project snapshot.`);
30405
30438
  }
30406
30439
  }
30407
30440
 
@@ -30414,12 +30447,12 @@ function registerSubscriptionTools(server, ctx, client) {
30414
30447
  {
30415
30448
  entityType: external_exports.enum(ENTITY_TYPES).describe("Type of entity to follow"),
30416
30449
  entityId: external_exports.string().describe("The entity ID (project ID, beat ID, or revision ID)"),
30417
- projectId: external_exports.string().describe("The project ID (for access control)")
30450
+ systemId: external_exports.string().describe("The project ID (for access control)")
30418
30451
  },
30419
- async ({ entityType, entityId, projectId }) => {
30452
+ async ({ entityType, entityId, systemId }) => {
30420
30453
  try {
30421
- await assertProjectInOrg(client, projectId, ctx.orgId);
30422
- await client.subscribe(ctx.user.userId, entityType, entityId, projectId, "manual");
30454
+ await assertProjectInOrg(client, systemId, ctx.orgId);
30455
+ await client.subscribe(ctx.user.userId, entityType, entityId, systemId, "manual");
30423
30456
  return {
30424
30457
  content: [{
30425
30458
  type: "text",
@@ -30653,7 +30686,7 @@ function registerProjectTools(server, ctx, client) {
30653
30686
  const lines = [
30654
30687
  `System updated successfully.`,
30655
30688
  "",
30656
- `**ID:** ${updated.projectId}`,
30689
+ `**ID:** ${updated.systemId}`,
30657
30690
  `**Title:** ${updated.title}`,
30658
30691
  accountLine(nonEmpty, updated.accountId),
30659
30692
  updated.strategy ? `**Strategy:** (updated)` : "",
@@ -30690,7 +30723,7 @@ function registerProjectTools(server, ctx, client) {
30690
30723
  text: [
30691
30724
  "System archived successfully.",
30692
30725
  "",
30693
- `**ID:** ${updated.projectId}`,
30726
+ `**ID:** ${updated.systemId}`,
30694
30727
  `**Title:** ${updated.title}`,
30695
30728
  `**Status:** ${updated.status}`
30696
30729
  ].join("\n")
@@ -30725,7 +30758,7 @@ function registerProjectTools(server, ctx, client) {
30725
30758
  const account = normalizeCreateAccountId(accountId);
30726
30759
  try {
30727
30760
  const project = await client.createSystem({
30728
- projectId: (0, import_crypto5.randomUUID)(),
30761
+ systemId: (0, import_crypto5.randomUUID)(),
30729
30762
  orgId: ctx.orgId,
30730
30763
  ownerUserId: ctx.user.userId,
30731
30764
  title,
@@ -30741,7 +30774,7 @@ function registerProjectTools(server, ctx, client) {
30741
30774
  const text = [
30742
30775
  `System created successfully.`,
30743
30776
  "",
30744
- `**ID:** ${project.projectId}`,
30777
+ `**ID:** ${project.systemId}`,
30745
30778
  `**Title:** ${project.title}`,
30746
30779
  `**Status:** ${project.status}`,
30747
30780
  project.accountId ? `**Account:** ${project.accountId}` : "",
@@ -30811,7 +30844,7 @@ function registerTeamspaceTools(server, ctx, client) {
30811
30844
  ];
30812
30845
  if (tsProjects.length > 0) {
30813
30846
  lines.push("", `Projects (${tsProjects.length}):`);
30814
- for (const p of tsProjects) lines.push(` [${p.projectId}] ${p.title} \u2014 ${p.status ?? "unknown"}`);
30847
+ for (const p of tsProjects) lines.push(` [${p.systemId}] ${p.title} \u2014 ${p.status ?? "unknown"}`);
30815
30848
  } else {
30816
30849
  lines.push("Projects: (none)");
30817
30850
  }
@@ -31073,23 +31106,23 @@ function registerValueVelocityTools(server, ctx, client) {
31073
31106
  "propose_enablement_edges",
31074
31107
  'Propose directed enablement edges for a project \u2014 "Beat A accelerates Beat B". The LLM analyzes project Beats and proposes which ones unlock or accelerate others. Only proposed edges are created; a human must confirm before they affect Value Velocity scores. Pass beatId to scope the pass to edges FROM one Beat \u2014 strongly recommended on a large portfolio, where an unscoped pass can exceed the request timeout. If you already know the edge you want, skip proposing and use create_enablement_edge instead.',
31075
31108
  {
31076
- projectId: external_exports.string().describe("The project ID"),
31109
+ systemId: external_exports.string().describe("The project ID"),
31077
31110
  beatId: external_exports.string().min(1).optional().describe("Scope the pass to edges FROM this Beat. Omit for a whole-project pass (slow on large portfolios).")
31078
31111
  },
31079
- async ({ projectId, beatId }) => {
31112
+ async ({ systemId, beatId }) => {
31080
31113
  try {
31081
- await assertProjectInOrg(client, projectId, ctx.orgId);
31114
+ await assertProjectInOrg(client, systemId, ctx.orgId);
31082
31115
  const actorId = resolveActorId(ctx);
31083
31116
  if (!actorId) {
31084
31117
  return { content: [{ type: "text", text: "Actor identity unavailable: user has no email or userId." }], isError: true };
31085
31118
  }
31086
31119
  if (beatId !== void 0) await assertBeatInOrg(client, beatId, ctx.orgId);
31087
- const proposed = await client.proposeEnablementEdges(projectId, actorId, beatId);
31120
+ const proposed = await client.proposeEnablementEdges(systemId, actorId, beatId);
31088
31121
  if (proposed.length === 0) {
31089
31122
  return { content: [{ type: "text", text: "No enablement edges proposed (fewer than 2 Beats or no relationships found)." }] };
31090
31123
  }
31091
31124
  const lines = [
31092
- `Proposed ${proposed.length} enablement edge(s) for project ${projectId}${beatId ? ` scoped to ${beatId}` : ""}:`,
31125
+ `Proposed ${proposed.length} enablement edge(s) for project ${systemId}${beatId ? ` scoped to ${beatId}` : ""}:`,
31093
31126
  "",
31094
31127
  "| Source Beat | Target Beat | Rationale |",
31095
31128
  "|-------------|------------|-----------|"
@@ -31461,13 +31494,13 @@ ${JSON.stringify(task.result, null, 2)}
31461
31494
  "list_tasks",
31462
31495
  "DEPRECATED: use `list_jobs`. List background jobs for a project with optional status filter.",
31463
31496
  {
31464
- projectId: external_exports.string().describe("The project ID"),
31497
+ systemId: external_exports.string().describe("The project ID"),
31465
31498
  status: external_exports.enum(TASK_STATUSES).optional().describe("Filter by status"),
31466
31499
  limit: external_exports.coerce.number().optional().default(20).describe("Max number of jobs to return")
31467
31500
  },
31468
- async ({ projectId, status, limit }) => {
31469
- await assertProjectInOrg(client, projectId, ctx.orgId);
31470
- const tasks = await client.listSystemTasks(projectId, {
31501
+ async ({ systemId, status, limit }) => {
31502
+ await assertProjectInOrg(client, systemId, ctx.orgId);
31503
+ const tasks = await client.listSystemTasks(systemId, {
31471
31504
  status,
31472
31505
  limit
31473
31506
  });
@@ -31519,13 +31552,13 @@ ${JSON.stringify(task.result, null, 2)}
31519
31552
  "list_jobs",
31520
31553
  "List background jobs for a project with optional status filter",
31521
31554
  {
31522
- projectId: external_exports.string().describe("The project ID"),
31555
+ systemId: external_exports.string().describe("The project ID"),
31523
31556
  status: external_exports.enum(TASK_STATUSES).optional().describe("Filter by status"),
31524
31557
  limit: external_exports.coerce.number().optional().default(20).describe("Max number of jobs to return")
31525
31558
  },
31526
- async ({ projectId, status, limit }) => {
31527
- await assertProjectInOrg(client, projectId, ctx.orgId);
31528
- const tasks = await client.listSystemTasks(projectId, {
31559
+ async ({ systemId, status, limit }) => {
31560
+ await assertProjectInOrg(client, systemId, ctx.orgId);
31561
+ const tasks = await client.listSystemTasks(systemId, {
31529
31562
  status,
31530
31563
  limit
31531
31564
  });
@@ -31543,13 +31576,13 @@ ${JSON.stringify(task.result, null, 2)}
31543
31576
  "draft_coda",
31544
31577
  "Generate and apply a Coda (description) for a Beat that has a title but no description. Uses project context, sibling Beats, related Notes, and codebase grep to write a resolved expression of the capability. The Coda is applied directly to the Beat, making it eligible for plan_beat_versions once a beat_quality check passes. An activity is logged so the Beat owner can review and edit. Returns a job ID \u2014 use get_job_status to track progress.",
31545
31578
  {
31546
- projectId: external_exports.string().describe("The project ID"),
31579
+ systemId: external_exports.string().describe("The project ID"),
31547
31580
  beatId: external_exports.string().describe("The beat ID (must have a title but no description)")
31548
31581
  },
31549
- async ({ projectId, beatId }) => {
31582
+ async ({ systemId, beatId }) => {
31550
31583
  try {
31551
- await assertProjectInOrg(client, projectId, ctx.orgId);
31552
- const task = await client.draftCoda(projectId, beatId);
31584
+ await assertProjectInOrg(client, systemId, ctx.orgId);
31585
+ const task = await client.draftCoda(systemId, beatId);
31553
31586
  return {
31554
31587
  content: [{
31555
31588
  type: "text",
@@ -31557,7 +31590,7 @@ ${JSON.stringify(task.result, null, 2)}
31557
31590
  "Coda draft session queued.",
31558
31591
  "",
31559
31592
  `**Beat:** ${beatId}`,
31560
- `**Project:** ${projectId}`,
31593
+ `**Project:** ${systemId}`,
31561
31594
  `**Task ID:** ${task.taskId}`,
31562
31595
  `**Status:** ${task.status}`,
31563
31596
  "",
@@ -31576,20 +31609,20 @@ ${JSON.stringify(task.result, null, 2)}
31576
31609
  "validate_assumptions",
31577
31610
  "Validate unvalidated assumption Notes against the codebase. Checks if assumptions are confirmed (validated), contradicted (invalidated), or have insufficient evidence (unvalidated). Low-criticality assumptions are auto-resolved; high-criticality are flagged for human review. Returns a job ID \u2014 use get_job_status to track progress.",
31578
31611
  {
31579
- projectId: external_exports.string().describe("The project ID"),
31612
+ systemId: external_exports.string().describe("The project ID"),
31580
31613
  noteIds: external_exports.array(external_exports.string()).optional().describe("Specific assumption note IDs to validate. If omitted, validates all unvalidated assumptions.")
31581
31614
  },
31582
- async ({ projectId, noteIds }) => {
31615
+ async ({ systemId, noteIds }) => {
31583
31616
  try {
31584
- await assertProjectInOrg(client, projectId, ctx.orgId);
31585
- const task = await client.validateAssumptions(projectId, noteIds);
31617
+ await assertProjectInOrg(client, systemId, ctx.orgId);
31618
+ const task = await client.validateAssumptions(systemId, noteIds);
31586
31619
  return {
31587
31620
  content: [{
31588
31621
  type: "text",
31589
31622
  text: [
31590
31623
  "Assumption validation queued.",
31591
31624
  "",
31592
- `**Project:** ${projectId}`,
31625
+ `**Project:** ${systemId}`,
31593
31626
  `**Scope:** ${noteIds ? `${noteIds.length} specific assumptions` : "All unvalidated assumptions"}`,
31594
31627
  `**Task ID:** ${task.taskId}`,
31595
31628
  `**Status:** ${task.status}`,
@@ -31610,15 +31643,15 @@ ${JSON.stringify(task.result, null, 2)}
31610
31643
  "start_session",
31611
31644
  'Start an agent session from a free-form message \u2014 like opening a Claude Code conversation pointed at your project. The message is the only instruction: describe what you want ("investigate why login fails", "implement revision rev-abc123", "what does the auth flow do?") and the agent resolves any entity references itself and does whatever the work needs \u2014 investigate and report findings, or change code and open a PR. A findings-only result with no PR is a valid outcome. Returns a job ID \u2014 use get_job_status to track progress. Requires agent sessions enabled on the project.',
31612
31645
  {
31613
- projectId: external_exports.string().min(1).describe("The project ID \u2014 the trusted scope the session is rooted at"),
31646
+ systemId: external_exports.string().min(1).describe("The project ID \u2014 the trusted scope the session is rooted at"),
31614
31647
  message: external_exports.string().min(1).describe("Free-form first message describing what the session should do"),
31615
31648
  revisionId: external_exports.string().min(1).optional().describe("Optional Revision ID (rev-<uuid>). When provided, the Worker routes to the typed implementation session path (prepareRevisionImplementationSession), enabling file hint generation and richer session context. Omit for investigation, planning, or freeform sessions.")
31616
31649
  },
31617
- async ({ projectId, message, revisionId }) => {
31650
+ async ({ systemId, message, revisionId }) => {
31618
31651
  try {
31619
- await assertProjectInOrg(client, projectId, ctx.orgId);
31652
+ await assertProjectInOrg(client, systemId, ctx.orgId);
31620
31653
  const task = await client.startSession(
31621
- projectId,
31654
+ systemId,
31622
31655
  message,
31623
31656
  { name: ctx.user.name, email: ctx.user.email },
31624
31657
  revisionId?.trim() ? { revisionId: revisionId.trim() } : void 0
@@ -31629,7 +31662,7 @@ ${JSON.stringify(task.result, null, 2)}
31629
31662
  text: [
31630
31663
  "Agent session queued.",
31631
31664
  "",
31632
- `**Project:** ${projectId}`,
31665
+ `**Project:** ${systemId}`,
31633
31666
  `**Task ID:** ${task.taskId}`,
31634
31667
  `**Status:** ${task.status}`,
31635
31668
  "",
@@ -31648,20 +31681,20 @@ ${JSON.stringify(task.result, null, 2)}
31648
31681
  "plan_revision_batch",
31649
31682
  "Autonomously generate an implementation plan for planning Revisions. If revisionIds are omitted, discovers all unplanned planning Revisions and plans them all. Uses codebase-aware agent sessions when available, LLM-only fallback otherwise. Returns a job ID \u2014 use get_job_status to track progress.",
31650
31683
  {
31651
- projectId: external_exports.string().describe("The project ID"),
31684
+ systemId: external_exports.string().describe("The project ID"),
31652
31685
  revisionIds: external_exports.array(external_exports.string()).optional().describe("Specific revision IDs to plan. If omitted, auto-discovers all unplanned planning revisions.")
31653
31686
  },
31654
- async ({ projectId, revisionIds }) => {
31687
+ async ({ systemId, revisionIds }) => {
31655
31688
  try {
31656
- await assertProjectInOrg(client, projectId, ctx.orgId);
31657
- const task = await client.planRevisionBatch(projectId, revisionIds);
31689
+ await assertProjectInOrg(client, systemId, ctx.orgId);
31690
+ const task = await client.planRevisionBatch(systemId, revisionIds);
31658
31691
  return {
31659
31692
  content: [{
31660
31693
  type: "text",
31661
31694
  text: [
31662
31695
  "Revision batch planning queued.",
31663
31696
  "",
31664
- `**Project:** ${projectId}`,
31697
+ `**Project:** ${systemId}`,
31665
31698
  `**Scope:** ${revisionIds ? `${revisionIds.length} specific revision(s)` : "All unplanned planning revisions"}`,
31666
31699
  `**Task ID:** ${task.taskId}`,
31667
31700
  `**Status:** ${task.status}`,
@@ -31681,19 +31714,19 @@ ${JSON.stringify(task.result, null, 2)}
31681
31714
  "run_triage",
31682
31715
  "Run the autonomous triage loop for a project. Analyzes next actions, auto-executes safe actions (draft_coda, plan_revision_batch), and escalates items that need human judgment. Requires agentCapabilities.triage enabled on the project. Returns a job ID \u2014 use get_job_status to track progress.",
31683
31716
  {
31684
- projectId: external_exports.string().describe("The project ID")
31717
+ systemId: external_exports.string().describe("The project ID")
31685
31718
  },
31686
- async ({ projectId }) => {
31719
+ async ({ systemId }) => {
31687
31720
  try {
31688
- await assertProjectInOrg(client, projectId, ctx.orgId);
31689
- const task = await client.runTriage(projectId);
31721
+ await assertProjectInOrg(client, systemId, ctx.orgId);
31722
+ const task = await client.runTriage(systemId);
31690
31723
  return {
31691
31724
  content: [{
31692
31725
  type: "text",
31693
31726
  text: [
31694
31727
  "Triage loop queued.",
31695
31728
  "",
31696
- `**Project:** ${projectId}`,
31729
+ `**Project:** ${systemId}`,
31697
31730
  `**Task ID:** ${task.taskId}`,
31698
31731
  `**Status:** ${task.status}`,
31699
31732
  "",
@@ -31724,15 +31757,15 @@ ${JSON.stringify(task.result, null, 2)}
31724
31757
  "Returns a job ID \u2014 use `get_job_status` to track progress."
31725
31758
  ].join("\n"),
31726
31759
  {
31727
- projectId: external_exports.string().describe("The project ID"),
31760
+ systemId: external_exports.string().describe("The project ID"),
31728
31761
  content: external_exports.string().describe('Document content \u2014 plain text (default) or base64-encoded binary (when encoding is "base64")'),
31729
31762
  filename: external_exports.string().optional().default("document.txt").describe('Original filename with extension (e.g., "sow.pdf", "spec.docx"). Used for format detection in binary mode.'),
31730
31763
  encoding: external_exports.enum(["base64"]).optional().describe('Set to "base64" when submitting binary files (DOCX, XLSX, PPTX, PDF). Omit for plain text.')
31731
31764
  },
31732
- async ({ projectId, content, filename, encoding }) => {
31765
+ async ({ systemId, content, filename, encoding }) => {
31733
31766
  try {
31734
- await assertProjectInOrg(client, projectId, ctx.orgId);
31735
- const result = encoding === "base64" ? await client.analyzeBinaryDocument(projectId, content, filename) : await client.analyzeDocument(projectId, content, filename);
31767
+ await assertProjectInOrg(client, systemId, ctx.orgId);
31768
+ const result = encoding === "base64" ? await client.analyzeBinaryDocument(systemId, content, filename) : await client.analyzeDocument(systemId, content, filename);
31736
31769
  return {
31737
31770
  content: [{
31738
31771
  type: "text",
@@ -32176,7 +32209,7 @@ function createHttpClient(config2) {
32176
32209
  );
32177
32210
  const project = result?.project ? {
32178
32211
  ...result.project,
32179
- projectId: result.project.id ?? result.project.projectId,
32212
+ systemId: result.project.id ?? result.project.systemId ?? result.project.projectId,
32180
32213
  title: result.project.name ?? result.project.title,
32181
32214
  orgId: result.project.organizationId ?? result.project.orgId
32182
32215
  } : void 0;
@@ -32196,7 +32229,7 @@ function createHttpClient(config2) {
32196
32229
  if (!raw) return void 0;
32197
32230
  return {
32198
32231
  ...raw,
32199
- projectId: raw.id ?? raw.projectId,
32232
+ systemId: raw.id ?? raw.systemId ?? raw.projectId,
32200
32233
  title: raw.name ?? raw.title,
32201
32234
  orgId: raw.organizationId ?? raw.orgId
32202
32235
  };
@@ -32212,17 +32245,17 @@ function createHttpClient(config2) {
32212
32245
  // is created unlinked — invisible in the Account's Systems list.
32213
32246
  ...input.accountId !== void 0 && { accountId: input.accountId }
32214
32247
  });
32215
- return { ...raw, projectId: raw?.id ?? raw?.projectId, title: raw?.name ?? raw?.title, orgId: raw?.organizationId ?? raw?.orgId };
32248
+ return { ...raw, systemId: raw?.id ?? raw?.systemId ?? raw?.projectId, title: raw?.name ?? raw?.title, orgId: raw?.organizationId ?? raw?.orgId };
32216
32249
  },
32217
32250
  updateSystem: async (projectId, updates) => {
32218
32251
  const raw = await request("PATCH", `/api/systems/${encodeURIComponent(projectId)}`, updates);
32219
32252
  if (!raw) return void 0;
32220
- return { ...raw, projectId: raw.id ?? raw.projectId, title: raw.name ?? raw.title, orgId: raw.organizationId ?? raw.orgId };
32253
+ return { ...raw, systemId: raw.id ?? raw.systemId ?? raw.projectId, title: raw.name ?? raw.title, orgId: raw.organizationId ?? raw.orgId };
32221
32254
  },
32222
32255
  archiveSystem: async (projectId) => {
32223
32256
  const raw = await request("PATCH", `/api/systems/${encodeURIComponent(projectId)}/archive`);
32224
32257
  if (!raw) return void 0;
32225
- return { ...raw, projectId: raw.id ?? raw.projectId, title: raw.name ?? raw.title, orgId: raw.organizationId ?? raw.orgId };
32258
+ return { ...raw, systemId: raw.id ?? raw.systemId ?? raw.projectId, title: raw.name ?? raw.title, orgId: raw.organizationId ?? raw.orgId };
32226
32259
  },
32227
32260
  getSystemFact: async (projectId) => {
32228
32261
  const result = await request("GET", `/api/systems/${encodeURIComponent(projectId)}/facts`);
@@ -32291,7 +32324,7 @@ function createHttpClient(config2) {
32291
32324
  return raw?.report;
32292
32325
  },
32293
32326
  createBeat: async (input) => {
32294
- const raw = await request("POST", `/api/systems/${encodeURIComponent(input.projectId)}/beats`, input);
32327
+ const raw = await request("POST", `/api/systems/${encodeURIComponent(input.systemId)}/beats`, input);
32295
32328
  return { ...raw, beatId: raw.beatId ?? raw.id, status: raw.status ?? "open", beatStatus: coerceBeatStatusLocal(raw.beatStatus) };
32296
32329
  },
32297
32330
  updateBeat: async (beatId, updates) => {
@@ -32375,7 +32408,7 @@ function createHttpClient(config2) {
32375
32408
  return result?.layer;
32376
32409
  },
32377
32410
  createSystemLayer: async (input) => {
32378
- const { projectId, layerKey, name, description, parentLayerId, repoPatterns } = input;
32411
+ const { systemId, layerKey, name, description, parentLayerId, repoPatterns } = input;
32379
32412
  const body = {
32380
32413
  layerKey,
32381
32414
  name,
@@ -32385,7 +32418,7 @@ function createHttpClient(config2) {
32385
32418
  };
32386
32419
  const result = await request(
32387
32420
  "POST",
32388
- `/api/systems/${encodeURIComponent(projectId)}/layers`,
32421
+ `/api/systems/${encodeURIComponent(systemId)}/layers`,
32389
32422
  body
32390
32423
  );
32391
32424
  if (result === void 0) return void 0;
@@ -33000,7 +33033,7 @@ function createHttpClient(config2) {
33000
33033
  return { entries: result.activities ?? [], hasMore: false };
33001
33034
  },
33002
33035
  createActivity: async (input) => {
33003
- const result = await request("POST", `/api/systems/${encodeURIComponent(input.projectId)}/activities`, {
33036
+ const result = await request("POST", `/api/systems/${encodeURIComponent(input.systemId)}/activities`, {
33004
33037
  action: input.action,
33005
33038
  importance: input.importance,
33006
33039
  reason: input.reason,
@@ -33182,10 +33215,10 @@ function createHttpClient(config2) {
33182
33215
  const result = await request(
33183
33216
  "POST",
33184
33217
  `/api/sessions`,
33185
- { projectId, message, ...triggeredBy && { triggeredBy }, ...trimmedRevisionId && { revisionId: trimmedRevisionId } }
33218
+ { systemId: projectId, message, ...triggeredBy && { triggeredBy }, ...trimmedRevisionId && { revisionId: trimmedRevisionId } }
33186
33219
  );
33187
33220
  if (!result?.taskId) throw new Error("Start session enqueue failed: no taskId returned");
33188
- return { taskId: result.taskId, taskType: "generic_session", status: result.status ?? "pending", projectId, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
33221
+ return { taskId: result.taskId, taskType: "generic_session", status: result.status ?? "pending", systemId: projectId, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
33189
33222
  },
33190
33223
  planRevisionBatch: async (projectId, revisionIds) => {
33191
33224
  const result = await request(
@@ -33194,7 +33227,7 @@ function createHttpClient(config2) {
33194
33227
  revisionIds ? { revisionIds } : {}
33195
33228
  );
33196
33229
  if (!result?.taskId) throw new Error("Plan revision batch enqueue failed: no taskId returned");
33197
- return { taskId: result.taskId, taskType: "plan_revision_batch", status: result.status ?? "pending", projectId, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
33230
+ return { taskId: result.taskId, taskType: "plan_revision_batch", status: result.status ?? "pending", systemId: projectId, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
33198
33231
  },
33199
33232
  draftCoda: async (projectId, beatId) => {
33200
33233
  const result = await request(
@@ -33203,7 +33236,7 @@ function createHttpClient(config2) {
33203
33236
  {}
33204
33237
  );
33205
33238
  if (!result?.taskId) throw new Error("Draft coda enqueue failed: no taskId returned");
33206
- return { taskId: result.taskId, taskType: "draft_coda", status: result.status ?? "pending", projectId, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
33239
+ return { taskId: result.taskId, taskType: "draft_coda", status: result.status ?? "pending", systemId: projectId, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
33207
33240
  },
33208
33241
  composeRootCoda: async (orgId) => {
33209
33242
  const result = await request(
@@ -33212,7 +33245,7 @@ function createHttpClient(config2) {
33212
33245
  {}
33213
33246
  );
33214
33247
  if (!result?.taskId) throw new Error("Compose root Coda enqueue failed: no taskId returned");
33215
- return { taskId: result.taskId, taskType: "compose_root_coda", status: result.status ?? "pending", projectId: orgId, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
33248
+ return { taskId: result.taskId, taskType: "compose_root_coda", status: result.status ?? "pending", systemId: orgId, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
33216
33249
  },
33217
33250
  validateAssumptions: async (projectId, noteIds) => {
33218
33251
  const result = await request(
@@ -33221,7 +33254,7 @@ function createHttpClient(config2) {
33221
33254
  noteIds ? { noteIds } : {}
33222
33255
  );
33223
33256
  if (!result?.taskId) throw new Error("Assumption validation enqueue failed: no taskId returned");
33224
- return { taskId: result.taskId, taskType: "validate_assumptions", status: result.status ?? "pending", projectId, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
33257
+ return { taskId: result.taskId, taskType: "validate_assumptions", status: result.status ?? "pending", systemId: projectId, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
33225
33258
  },
33226
33259
  runTriage: async (projectId) => {
33227
33260
  const result = await request(
@@ -33230,7 +33263,7 @@ function createHttpClient(config2) {
33230
33263
  {}
33231
33264
  );
33232
33265
  if (!result?.taskId) throw new Error("Triage enqueue failed: no taskId returned");
33233
- return { taskId: result.taskId, taskType: "triage_loop", status: result.status ?? "pending", projectId, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
33266
+ return { taskId: result.taskId, taskType: "triage_loop", status: result.status ?? "pending", systemId: projectId, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
33234
33267
  },
33235
33268
  baselineProject: async (projectId, options) => {
33236
33269
  const result = await request(
@@ -33239,7 +33272,7 @@ function createHttpClient(config2) {
33239
33272
  { beatIds: options?.beatIds, runChecks: options?.runChecks ?? true, localPath: options?.localPath }
33240
33273
  );
33241
33274
  if (!result?.taskId) throw new Error("Baseline enqueue failed: no taskId returned");
33242
- return { taskId: result.taskId, taskType: "baseline_project", status: result.status ?? "pending", projectId, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
33275
+ return { taskId: result.taskId, taskType: "baseline_project", status: result.status ?? "pending", systemId: projectId, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
33243
33276
  },
33244
33277
  runCheck: async (projectId, checkType, targetId, options) => {
33245
33278
  const result = await request(
@@ -33248,7 +33281,7 @@ function createHttpClient(config2) {
33248
33281
  { checkType, targetId, branch: options?.branch, localPath: options?.localPath }
33249
33282
  );
33250
33283
  if (!result?.taskId) throw new Error("Check enqueue failed: no taskId returned");
33251
- return { taskId: result.taskId, taskType: "run_check", status: result.status ?? "pending", projectId, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
33284
+ return { taskId: result.taskId, taskType: "run_check", status: result.status ?? "pending", systemId: projectId, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
33252
33285
  },
33253
33286
  // Subscriptions
33254
33287
  subscribe: async (userId, entityType, entityId, projectId, source) => {
@@ -33325,7 +33358,7 @@ function createHttpClient(config2) {
33325
33358
  },
33326
33359
  searchByText: async (scope, query, options) => {
33327
33360
  const params = new URLSearchParams({ q: query });
33328
- if ("projectId" in scope) params.set("systemId", scope.projectId);
33361
+ if ("systemId" in scope) params.set("systemId", scope.systemId);
33329
33362
  else if ("teamspaceId" in scope) params.set("teamspaceId", scope.teamspaceId);
33330
33363
  else params.set("orgId", scope.orgId);
33331
33364
  if (options?.limit) params.set("limit", String(options.limit));
@@ -34161,7 +34194,7 @@ function loadConfig() {
34161
34194
  };
34162
34195
  }
34163
34196
  async function main() {
34164
- console.error(`[harmonica-mcp] v${"3.6.1"} starting\u2026`);
34197
+ console.error(`[harmonica-mcp] v${"3.7.1"} starting\u2026`);
34165
34198
  const config2 = loadConfig();
34166
34199
  const client = createHttpClient({
34167
34200
  apiBaseUrl: config2.apiBaseUrl,