@codazen/harmonica-mcp 3.7.2 → 3.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +103 -103
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -21972,9 +21972,9 @@ function formatActivity(a) {
21972
21972
  function registerActivityTools(server, ctx, client) {
21973
21973
  server.tool(
21974
21974
  "list_activities",
21975
- "List project activity audit trail \u2014 shows beat changes, revision transitions, note operations, merges, splits, and reassignments. Sorted newest-first.",
21975
+ "List system activity audit trail \u2014 shows beat changes, revision transitions, note operations, merges, splits, and reassignments. Sorted newest-first.",
21976
21976
  {
21977
- systemId: external_exports.string().describe("The project ID"),
21977
+ systemId: external_exports.string().describe("The system 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
  },
@@ -22005,7 +22005,7 @@ 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
- systemId: external_exports.string().describe("The project ID"),
22008
+ systemId: external_exports.string().describe("The system 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")
@@ -22034,9 +22034,9 @@ Analyzed: ${result.totalAnalyzed} | Clusters: ${result.clusters.length} | Noise:
22034
22034
  );
22035
22035
  server.tool(
22036
22036
  "run_clustered_analysis",
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.",
22037
+ "Run batched analysis on clustered system 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
- systemId: external_exports.string().describe("The project ID"),
22039
+ systemId: external_exports.string().describe("The system 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"),
@@ -22071,9 +22071,9 @@ ${result.noise.join(", ")}` : "");
22071
22071
  function registerAuditIntegrityTools(server, ctx, client) {
22072
22072
  server.tool(
22073
22073
  "verify_audit_integrity",
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.",
22074
+ "Verify the cryptographic integrity of audit trail records for a system. Checks SHA-256 hashes on Activity and PromptLog records to detect any post-creation tampering.",
22075
22075
  {
22076
- systemId: external_exports.string().describe("The project ID to verify"),
22076
+ systemId: external_exports.string().describe("The system ID to verify"),
22077
22077
  limit: external_exports.number().optional().describe("Max records to verify (default 100)")
22078
22078
  },
22079
22079
  async ({ systemId, limit }) => {
@@ -22287,7 +22287,7 @@ 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
- systemId: 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 system ID (for access control \u2014 all revisions and target beats must belong to this system)"),
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")
@@ -22328,7 +22328,7 @@ 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
- systemId: external_exports.string().describe("The project ID (for access control)"),
22331
+ systemId: external_exports.string().describe("The system 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")
@@ -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
- systemId: external_exports.string().describe("The project ID"),
22437
+ systemId: external_exports.string().describe("The system 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
  ),
@@ -22536,7 +22536,7 @@ ${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
- systemId: external_exports.string().describe("The project ID"),
22539
+ systemId: external_exports.string().describe("The system 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
22542
  const handler = async ({ beatId, systemId, wait }) => {
@@ -22585,7 +22585,7 @@ 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
- systemId: external_exports.string().describe("The project ID")
22588
+ systemId: external_exports.string().describe("The system ID")
22589
22589
  },
22590
22590
  async ({ beatId, systemId }) => {
22591
22591
  try {
@@ -23095,9 +23095,9 @@ function formatLayerDetail(layer) {
23095
23095
  function registerBeatTools(server, ctx, client) {
23096
23096
  server.tool(
23097
23097
  "list_beats",
23098
- "List beats in a project. Use offset/limit to paginate large result sets.",
23098
+ "List beats in a system. Use offset/limit to paginate large result sets.",
23099
23099
  {
23100
- systemId: external_exports.string().describe("The project ID"),
23100
+ systemId: external_exports.string().describe("The system 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)")
@@ -23139,7 +23139,7 @@ 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
- systemId: external_exports.string().optional().describe("Project ID (auto-resolved from beat if omitted)")
23142
+ systemId: external_exports.string().optional().describe("System ID (auto-resolved from beat if omitted)")
23143
23143
  },
23144
23144
  async ({ beatId, systemId }) => {
23145
23145
  const beat = await fetchBeatInOrg(client, beatId, ctx.orgId);
@@ -23154,9 +23154,9 @@ function registerBeatTools(server, ctx, client) {
23154
23154
  );
23155
23155
  server.tool(
23156
23156
  "create_beat",
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.",
23157
+ "Create a new beat in a system. 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
- systemId: external_exports.string().describe("The project ID"),
23159
+ systemId: external_exports.string().describe("The system 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"),
@@ -23238,7 +23238,7 @@ ${formatBeatDetail(beat)}${similarityWarning}` }] };
23238
23238
  maxConcurrentRevisions: external_exports.coerce.number().min(0).describe("Recommended max active revisions (0 = no cap)"),
23239
23239
  allowOverlap: external_exports.boolean().describe("Whether overlapping work on this Beat is acceptable"),
23240
23240
  scopeOverlapStrategy: external_exports.enum(["block", "warn", "allow"]).describe("Advisory only \u2014 how the UI/MCP should react when limits are exceeded")
23241
- }).nullable().optional().describe("Per-Beat concurrency guidance (advisory, not a gate). Omit to leave unchanged; pass null to clear and use the project default.")
23241
+ }).nullable().optional().describe("Per-Beat concurrency guidance (advisory, not a gate). Omit to leave unchanged; pass null to clear and use the system default.")
23242
23242
  },
23243
23243
  async ({ beatId, changeSummary, ...changes }) => {
23244
23244
  try {
@@ -23277,10 +23277,10 @@ ${formatBeatDetail(beat)}${similarityWarning}` }] };
23277
23277
  );
23278
23278
  server.tool(
23279
23279
  "list_revisions",
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.",
23280
+ "List revisions for a system, 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
- systemId: external_exports.string().describe("The project ID"),
23283
- beatId: external_exports.string().optional().describe("Scope to a specific beat (omit for all project revisions)"),
23282
+ systemId: external_exports.string().describe("The system ID"),
23283
+ beatId: external_exports.string().optional().describe("Scope to a specific beat (omit for all system 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
  },
@@ -23397,10 +23397,10 @@ var PlanVersionsIdempotencyError = class extends Error {
23397
23397
  function registerBeatVersionTools(server, ctx, client) {
23398
23398
  server.tool(
23399
23399
  "list_beat_versions",
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.",
23400
+ "List Beat Versions for a Beat or System. 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 system. Status is binary (active | archived); by default excludes archived.",
23401
23401
  {
23402
- systemId: external_exports.string().describe("The project ID"),
23403
- beatId: external_exports.string().optional().describe("Filter to a specific Beat (omit for all Beat Versions in project)"),
23402
+ systemId: external_exports.string().describe("The system ID"),
23403
+ beatId: external_exports.string().optional().describe("Filter to a specific Beat (omit for all Beat Versions in system)"),
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
  },
@@ -23672,10 +23672,10 @@ function registerBeatVersionTools(server, ctx, client) {
23672
23672
  );
23673
23673
  server.tool(
23674
23674
  "relocate_beat_version",
23675
- "Move a Beat Version from one parent Beat to another within the same project, re-parenting all child Revisions and keeping every DynamoDB index consistent. Use when a Beat Version was planned under the wrong Beat or the scope of a Beat has been reorganized. Dry-run by default (confirm=false): shows what would change without writing. Pass confirm=true to execute. Blocked on released Drops by default \u2014 pass overrideReleasedDropLock=true to proceed (preserves Drop membership for audit trail).",
23675
+ "Move a Beat Version from one parent Beat to another within the same system, re-parenting all child Revisions and keeping every DynamoDB index consistent. Use when a Beat Version was planned under the wrong Beat or the scope of a Beat has been reorganized. Dry-run by default (confirm=false): shows what would change without writing. Pass confirm=true to execute. Blocked on released Drops by default \u2014 pass overrideReleasedDropLock=true to proceed (preserves Drop membership for audit trail).",
23676
23676
  {
23677
23677
  beatVersionId: external_exports.string().describe("The Beat Version ID to relocate (e.g., bv-abc123)"),
23678
- targetBeatId: external_exports.string().describe("The Beat ID to relocate the Beat Version to (must be in the same project)"),
23678
+ targetBeatId: external_exports.string().describe("The Beat ID to relocate the Beat Version to (must be in the same system)"),
23679
23679
  reason: external_exports.string().min(1).describe("Why this relocation is being made (recorded in audit trail)"),
23680
23680
  confirm: external_exports.boolean().optional().describe("Set true to execute the relocation. Default false (dry-run describes the move)."),
23681
23681
  overrideReleasedDropLock: external_exports.boolean().optional().describe("Allow relocation even when the Beat Version belongs to a released Drop (preserves Drop membership). Default false.")
@@ -23829,9 +23829,9 @@ function registerCheckTools(server, ctx, client) {
23829
23829
  );
23830
23830
  server.tool(
23831
23831
  "list_checks",
23832
- "List persisted checks for a project, beat, beat version, or revision. Shows score trends over time. Filter by check type.",
23832
+ "List persisted checks for a system, beat, beat version, or revision. Shows score trends over time. Filter by check type.",
23833
23833
  {
23834
- systemId: external_exports.string().describe("The project ID"),
23834
+ systemId: external_exports.string().describe("The system 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"),
@@ -23942,9 +23942,9 @@ function registerCheckTools(server, ctx, client) {
23942
23942
  );
23943
23943
  server.tool(
23944
23944
  "list_latest_checks",
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.",
23945
+ "List the latest check per target (Beat, Beat Version, Revision) across a whole system 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
- systemId: external_exports.string().describe("The project ID")
23947
+ systemId: external_exports.string().describe("The system ID")
23948
23948
  },
23949
23949
  async ({ systemId }) => {
23950
23950
  try {
@@ -23966,8 +23966,8 @@ 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
- systemId: external_exports.string().describe("The project ID"),
23970
- branch: external_exports.string().optional().describe("Override the git branch for code checks (default: project default branch)"),
23969
+ systemId: external_exports.string().describe("The system ID"),
23970
+ branch: external_exports.string().optional().describe("Override the git branch for code checks (default: system 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
23973
  async ({ checkType, targetId, systemId, branch, localPath }) => {
@@ -24555,7 +24555,7 @@ 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
- 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)"),
24558
+ systemId: external_exports.string().min(1).describe("A system within this Drop \u2014 the check record is attributed to it (Drops span systems, so the result is recorded under one member system)"),
24559
24559
  wait: external_exports.boolean().optional().describe("Block until the check completes and return the scorecard inline. Default: false.")
24560
24560
  },
24561
24561
  async ({ dropId, systemId, wait }) => {
@@ -25004,7 +25004,7 @@ 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
- systemId: external_exports.string().describe("The project ID the notes belong to"),
25007
+ systemId: external_exports.string().describe("The system 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)"),
@@ -25031,7 +25031,7 @@ 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
- systemId: external_exports.string().describe("The project ID the beat belongs to"),
25034
+ systemId: external_exports.string().describe("The system 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)")
@@ -25048,7 +25048,7 @@ 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
- systemId: external_exports.string().describe("The project ID the note belongs to"),
25051
+ systemId: external_exports.string().describe("The system 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
  },
@@ -25063,7 +25063,7 @@ 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
- systemId: external_exports.string().describe("The project ID"),
25066
+ systemId: external_exports.string().describe("The system 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.")
@@ -25094,7 +25094,7 @@ ${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
- systemId: external_exports.string().describe("The project ID"),
25097
+ systemId: external_exports.string().describe("The system 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.")
@@ -25821,9 +25821,9 @@ function formatNextActions(actions) {
25821
25821
  function registerNextActionsTools(server, ctx, client) {
25822
25822
  server.tool(
25823
25823
  "get_next_actions",
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).",
25824
+ "Get prioritized list of recommended next actions for a system. 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
- systemId: external_exports.string().describe("The project ID"),
25826
+ systemId: external_exports.string().describe("The system ID"),
25827
25827
  limit: external_exports.coerce.number().optional().default(10).describe("Max actions to return")
25828
25828
  },
25829
25829
  async ({ systemId, limit }) => {
@@ -25841,7 +25841,7 @@ 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
- systemId: external_exports.string().describe("The project ID")
25844
+ systemId: external_exports.string().describe("The system ID")
25845
25845
  },
25846
25846
  async ({ systemId }) => {
25847
25847
  try {
@@ -25858,7 +25858,7 @@ 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
- systemId: external_exports.string().describe("The project ID"),
25861
+ systemId: external_exports.string().describe("The system ID"),
25862
25862
  actionKey: external_exports.string().describe("The action key to dismiss (from list_triage_actions)")
25863
25863
  },
25864
25864
  async ({ systemId, actionKey }) => {
@@ -26624,7 +26624,7 @@ var CheckApiSchema = external_exports.object({
26624
26624
  createdAt: external_exports.string(),
26625
26625
  updatedAt: external_exports.string()
26626
26626
  }).passthrough();
26627
- var ProjectCheckApiSchema = external_exports.object({
26627
+ var SystemCheckApiSchema = external_exports.object({
26628
26628
  checkId: external_exports.string(),
26629
26629
  checkType: CheckTypeSchema,
26630
26630
  targetType: CheckTargetTypeSchema,
@@ -26640,7 +26640,7 @@ var ProjectCheckApiSchema = external_exports.object({
26640
26640
  dimensions: external_exports.array(CheckDimensionScoreSchema).optional()
26641
26641
  }).passthrough();
26642
26642
  var CheckResponseSchema = external_exports.object({ check: CheckApiSchema }).passthrough();
26643
- var ChecksResponseSchema = external_exports.object({ checks: external_exports.array(ProjectCheckApiSchema), nextCursor: external_exports.string().optional() }).passthrough();
26643
+ var ChecksResponseSchema = external_exports.object({ checks: external_exports.array(SystemCheckApiSchema), nextCursor: external_exports.string().optional() }).passthrough();
26644
26644
  var TaskStatusSchema2 = external_exports.enum(["pending", "running", "completed", "failed"]);
26645
26645
  var TaskProgressSchema = external_exports.object({
26646
26646
  current: external_exports.number(),
@@ -27393,10 +27393,10 @@ ${formatDeliveryPolicy(
27393
27393
  function registerNoteTools(server, ctx, client) {
27394
27394
  server.tool(
27395
27395
  "list_notes",
27396
- "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.",
27396
+ "List Notes for a system. Without beatId: returns all notes (system-level + beat-level). With beatId: returns only notes scoped to that beat. Filterable by type, status, revisionId, beatVersionId, and significance. Use offset/limit to paginate beat-scoped results; use cursor for project-wide results.",
27397
27397
  {
27398
- systemId: external_exports.string().describe("The project ID"),
27399
- beatId: external_exports.string().optional().describe("Scope to a specific beat (omit for all project notes)"),
27398
+ systemId: external_exports.string().describe("The system ID"),
27399
+ beatId: external_exports.string().optional().describe("Scope to a specific beat (omit for all system notes)"),
27400
27400
  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"])'),
27401
27401
  status: external_exports.enum(NOTE_STATUS_VALUES).optional().describe("Filter by status"),
27402
27402
  revisionId: external_exports.string().optional().describe("Filter to notes scoped to a specific revision"),
@@ -27561,9 +27561,9 @@ function registerNoteTools(server, ctx, client) {
27561
27561
  );
27562
27562
  server.tool(
27563
27563
  "list_documents",
27564
- "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.",
27564
+ "List uploaded documents for a system with processing status, filename, child note count, and AI summary. Use get_note with a document note ID for full details.",
27565
27565
  {
27566
- systemId: external_exports.string().describe("The project ID")
27566
+ systemId: external_exports.string().describe("The system ID")
27567
27567
  },
27568
27568
  async ({ systemId }) => {
27569
27569
  await assertProjectInOrg(client, systemId, ctx.orgId);
@@ -27579,7 +27579,7 @@ function registerNoteTools(server, ctx, client) {
27579
27579
  "get_note",
27580
27580
  "Get full details of a single Note by ID",
27581
27581
  {
27582
- systemId: external_exports.string().optional().describe("The project ID (for access control). Provide systemId or orgId, not both."),
27582
+ systemId: external_exports.string().optional().describe("The system ID (for access control). Provide systemId or orgId, not both."),
27583
27583
  orgId: external_exports.string().optional().describe("The org ID (for org-level notes). Provide orgId or systemId, not both."),
27584
27584
  noteId: external_exports.string().describe("The note ID")
27585
27585
  },
@@ -27616,9 +27616,9 @@ function registerNoteTools(server, ctx, client) {
27616
27616
  );
27617
27617
  server.tool(
27618
27618
  "create_note",
27619
- "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.",
27619
+ "Create a new Note. Provide exactly one of systemId, teamspaceId, movementId, or none (org-level): systemId scopes the note to a system (optionally with beatId), teamspaceId scopes it to a teamspace (no cascade to child systems), movementId scopes it to a Movement (commercial SOW container \u2014 engagement-level knowledge), and omitting all creates an org-level note that applies across all systems.",
27620
27620
  {
27621
- systemId: external_exports.string().optional().describe("The project ID (omit for teamspace-, movement-, or org-level note)"),
27621
+ systemId: external_exports.string().optional().describe("The system ID (omit for teamspace-, movement-, or org-level note)"),
27622
27622
  teamspaceId: external_exports.string().optional().describe("The teamspace ID (omit for project-, movement-, or org-level note)"),
27623
27623
  movementId: external_exports.string().min(1).optional().describe("The Movement ID (omit for project-, teamspace-, or org-level note)"),
27624
27624
  noteType: external_exports.enum(NOTE_TYPE_VALUES).describe("The type of note"),
@@ -27717,7 +27717,7 @@ ${text}` }] };
27717
27717
  "update_note",
27718
27718
  "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.",
27719
27719
  {
27720
- systemId: external_exports.string().optional().describe("The project ID (for access control). Provide systemId or orgId, not both."),
27720
+ systemId: external_exports.string().optional().describe("The system ID (for access control). Provide systemId or orgId, not both."),
27721
27721
  orgId: external_exports.string().optional().describe("The org ID (for org-level notes). Provide orgId or systemId, not both."),
27722
27722
  noteId: external_exports.string().describe("The note ID to update"),
27723
27723
  noteType: external_exports.enum(NOTE_TYPE_VALUES).optional().describe("Reclassify the note type (e.g. assumption \u2192 guidance)"),
@@ -27799,7 +27799,7 @@ ${text}` }] };
27799
27799
  "bulk_update_notes",
27800
27800
  "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.",
27801
27801
  {
27802
- systemId: external_exports.string().describe("The project ID (for access control)"),
27802
+ systemId: external_exports.string().describe("The system ID (for access control)"),
27803
27803
  noteIds: external_exports.array(external_exports.string()).min(1).max(100).describe("Note IDs to update (max 100 per call)"),
27804
27804
  noteType: external_exports.enum(NOTE_TYPE_VALUES).optional().describe("Reclassify all notes to this type"),
27805
27805
  status: external_exports.enum(NOTE_STATUS_VALUES).optional().describe("New status for all notes"),
@@ -27841,7 +27841,7 @@ ${text}` }] };
27841
27841
  "reassign_note",
27842
27842
  "Reassign a misattributed note to a different beat (with audit trail)",
27843
27843
  {
27844
- systemId: external_exports.string().describe("The project ID (for access control)"),
27844
+ systemId: external_exports.string().describe("The system ID (for access control)"),
27845
27845
  noteId: external_exports.string().describe("The note ID to reassign"),
27846
27846
  targetBeatId: external_exports.string().describe("The beat ID to move the note to"),
27847
27847
  targetRevisionId: external_exports.string().optional().describe("Optional revision ID to scope the note to"),
@@ -27865,7 +27865,7 @@ ${text}` }] };
27865
27865
  "remove_note",
27866
27866
  "Soft-remove a misattributed note by dismissing it (with audit trail)",
27867
27867
  {
27868
- systemId: external_exports.string().min(1).describe("The project ID (for access control)"),
27868
+ systemId: external_exports.string().min(1).describe("The system ID (for access control)"),
27869
27869
  noteId: external_exports.string().min(1).describe("The note ID to remove"),
27870
27870
  reason: external_exports.string().min(1).max(500).optional().describe('Why this note is being removed (defaults to "Removed via remove_note")')
27871
27871
  },
@@ -27892,7 +27892,7 @@ ${text}` }] };
27892
27892
  "dismiss_document",
27893
27893
  "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.",
27894
27894
  {
27895
- systemId: external_exports.string().min(1).describe("The project ID (for access control)"),
27895
+ systemId: external_exports.string().min(1).describe("The system ID (for access control)"),
27896
27896
  noteId: external_exports.string().min(1).describe("The document note ID to dismiss"),
27897
27897
  reason: external_exports.string().min(1).max(500).optional().describe('Why this document is being dismissed (defaults to "Dismissed via dismiss_document")')
27898
27898
  },
@@ -27925,7 +27925,7 @@ ${text}` }] };
27925
27925
  "list_key_assumptions",
27926
27926
  "Get key assumptions sorted by criticality (highest risk first)",
27927
27927
  {
27928
- systemId: external_exports.string().describe("The project ID")
27928
+ systemId: external_exports.string().describe("The system ID")
27929
27929
  },
27930
27930
  async ({ systemId }) => {
27931
27931
  await assertProjectInOrg(client, systemId, ctx.orgId);
@@ -28008,7 +28008,7 @@ function formatTree(node, depth = 0) {
28008
28008
  function registerNotebookTools(server, ctx, client) {
28009
28009
  server.tool(
28010
28010
  "create_notebook",
28011
- "Create a new Notebook in the configured organization. Optionally bind it to one or more scopes (org, teamspace, or project) at creation.",
28011
+ "Create a new Notebook in the configured organization. Optionally bind it to one or more scopes (org, teamspace, or system) at creation.",
28012
28012
  {
28013
28013
  title: external_exports.string().min(1).max(300).describe("Notebook title (1\u2013300 characters)"),
28014
28014
  description: external_exports.string().max(2e4).optional().describe("Optional description (max 20 000 characters)"),
@@ -28185,7 +28185,7 @@ ${formatTree(tree.root)}` }] };
28185
28185
  );
28186
28186
  server.tool(
28187
28187
  "add_notebook_collection",
28188
- "Add a live-collection section to a Notebook page. Unlike a normal section, its members are resolved from a live query at read time (never hand-picked), so it stays current on its own as notes are created, edited, or retired. v1 supports the project Decision Log: every decision note in a project (including beat-scoped), newest first. Attach under a page (defaults to the notebook root).",
28188
+ "Add a live-collection section to a Notebook page. Unlike a normal section, its members are resolved from a live query at read time (never hand-picked), so it stays current on its own as notes are created, edited, or retired. v1 supports the system Decision Log: every decision note in a system (including beat-scoped), newest first. Attach under a page (defaults to the notebook root).",
28189
28189
  {
28190
28190
  notebookId: external_exports.string().describe("The Notebook ID"),
28191
28191
  heading: external_exports.string().min(1).max(200).describe('Section label, e.g. "Decision Log"'),
@@ -28408,7 +28408,7 @@ ${scopeText}` }] };
28408
28408
  );
28409
28409
  server.tool(
28410
28410
  "list_notebooks",
28411
- "List Notebooks reachable from a scope (org, teamspace, or project). Returns all notebooks bound to that scope.",
28411
+ "List Notebooks reachable from a scope (org, teamspace, or system). Returns all notebooks bound to that scope.",
28412
28412
  {
28413
28413
  scopeType: external_exports.enum(SCOPE_TYPE_VALUES).describe("Scope type \u2014 org, teamspace, or project"),
28414
28414
  scopeId: external_exports.string().min(1).describe("ID of the scope entity"),
@@ -28619,27 +28619,27 @@ function registerOnboardingTools(server, ctx, client) {
28619
28619
  server.tool(
28620
28620
  "draft_onboarding_beats",
28621
28621
  [
28622
- "Generate an initial Beat structure for a newly created project.",
28623
- "Call this after create_teamspace_onboarding_batch to draft 3\u20135 Beats based on the project context.",
28622
+ "Generate an initial Beat structure for a newly created system.",
28623
+ "Call this after create_teamspace_onboarding_batch to draft 3\u20135 Beats based on the system context.",
28624
28624
  "Returns Beat suggestions with titles, descriptions, and any Maya recommendations to convert a suggestion to a Note instead.",
28625
28625
  "The PM reviews and confirms \u2014 use create_beat for each approved Beat, create_note for any Note conversions."
28626
28626
  ].join(" "),
28627
28627
  {
28628
- systemId: external_exports.string().describe("The newly created project ID"),
28629
- projectTitle: external_exports.string().describe("The project title"),
28628
+ systemId: external_exports.string().describe("The newly created system ID"),
28629
+ systemTitle: external_exports.string().describe("The system title"),
28630
28630
  projectDescription: external_exports.string().optional().describe("Discovery context: pain points, goals, and constraints summary to inform the Beat structure"),
28631
28631
  analysisJson: external_exports.string().optional().describe("JSON-serialized DiscoveryAnalysis from initiate_teamspace_onboarding. Pass this to produce Beats grounded in the actual discovery data.")
28632
28632
  },
28633
- async ({ systemId, projectTitle, projectDescription, analysisJson }) => {
28633
+ async ({ systemId, systemTitle, projectDescription, analysisJson }) => {
28634
28634
  await assertProjectInOrg(client, systemId, ctx.orgId);
28635
- const result = await client.draftOnboardingBeats(systemId, { projectTitle, projectDescription, analysisJson });
28635
+ const result = await client.draftOnboardingBeats(systemId, { systemTitle, projectDescription, analysisJson });
28636
28636
  if (!result.suggestions || result.suggestions.length === 0) {
28637
28637
  return {
28638
28638
  content: [{ type: "text", text: "Failed to generate Beat suggestions. Try providing more project context." }]
28639
28639
  };
28640
28640
  }
28641
28641
  const lines = [
28642
- `## Beat Draft for "${projectTitle}" (${result.suggestions.length} suggestions)`,
28642
+ `## Beat Draft for "${systemTitle}" (${result.suggestions.length} suggestions)`,
28643
28643
  "",
28644
28644
  ...result.suggestions.flatMap((s, i) => {
28645
28645
  const sug = s;
@@ -28936,7 +28936,7 @@ function registerOrganizationTools(server, ctx, client) {
28936
28936
  if (result.error) {
28937
28937
  return { content: [{ type: "text", text: result.error }], isError: true };
28938
28938
  }
28939
- const transferred = result.project;
28939
+ const transferred = result.system;
28940
28940
  if (!transferred) {
28941
28941
  return { content: [{ type: "text", text: "Transfer succeeded but system data was not returned." }], isError: true };
28942
28942
  }
@@ -29066,11 +29066,11 @@ ${d.rationale}${d.suggestions?.length ? `
29066
29066
  function registerPlanQualityTools(server, ctx, client) {
29067
29067
  const schema = {
29068
29068
  beatVersionId: external_exports.string().describe("The Beat Version ID (e.g., bv-abc123). beat_version_quality targets Beat Versions only."),
29069
- systemId: external_exports.string().describe("The project ID"),
29069
+ systemId: external_exports.string().describe("The system ID"),
29070
29070
  // The completeness dimension probes the codebase, so this tool accepts the
29071
29071
  // same repo overrides as run_check — without them the probe could only ever
29072
29072
  // read the default branch from this surface.
29073
- branch: external_exports.string().optional().describe("Override the git branch the completeness dimension probes (default: the branch of an open child Revision, else the project default branch)"),
29073
+ branch: external_exports.string().optional().describe("Override the git branch the completeness dimension probes (default: the branch of an open child Revision, else the system default branch)"),
29074
29074
  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"),
29075
29075
  wait: external_exports.boolean().optional().describe("If true, block until the check completes and return the scorecard inline. Default: false (returns taskId immediately).")
29076
29076
  };
@@ -29223,9 +29223,9 @@ function formatConsolidationResult(primaryId, primary, archivedBeatIds, notesRea
29223
29223
  function registerPortfolioCoherenceTools(server, ctx, client) {
29224
29224
  server.tool(
29225
29225
  "check_portfolio_coherence",
29226
- "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.",
29226
+ "Run a portfolio coherence check on a system. 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.",
29227
29227
  {
29228
- systemId: external_exports.string().min(1).describe("The project ID"),
29228
+ systemId: external_exports.string().min(1).describe("The system ID"),
29229
29229
  wait: external_exports.boolean().optional().describe("Block until the check completes and return the report inline. Default: false.")
29230
29230
  },
29231
29231
  async ({ systemId, wait }) => {
@@ -29257,7 +29257,7 @@ function registerPortfolioCoherenceTools(server, ctx, client) {
29257
29257
  "consolidate_beats",
29258
29258
  "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.",
29259
29259
  {
29260
- systemId: external_exports.string().min(1).describe("The project ID \u2014 all beatIds must belong to this project"),
29260
+ systemId: external_exports.string().min(1).describe("The system ID \u2014 all beatIds must belong to this system"),
29261
29261
  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)"),
29262
29262
  primaryBeatId: external_exports.string().min(1).optional().describe("Beat ID that survives \u2014 defaults to beatIds[0] if omitted"),
29263
29263
  rationale: external_exports.string().max(2e3).optional().describe("Why these Beats are being merged \u2014 auto-generated if omitted"),
@@ -29441,14 +29441,14 @@ This is a deliberate choice by whoever configured the System, and \`confirm: tru
29441
29441
  function registerRevisionLifecycleTools(server, ctx, client) {
29442
29442
  server.tool(
29443
29443
  "create_revision",
29444
- "Create a new revision on a Beat. A Revision's canonical status starts `active`; PR progression is tracked separately on `scm.state` (`draft` \u2192 `open` \u2192 `merged` / `closed`). Environment progression (`in_staging`, `ready_for_production`, `live`) is tracked on the parent Beat Version, not the Revision. `beatVersionId` is optional \u2014 omit it for hotfixes, dep bumps, or other operational fixes that don't belong to a planning increment. Orphan Revisions (no BV) must carry their own `description` since there's no BV context to inherit. `baseRevisionId` stacks this Revision's PR/session checkout on another Revision's branch instead of the project default \u2014 for GitHub stacked PRs, must reference a Revision in the same project (validated server-side). Use this to add revisions alongside existing ones \u2014 no idempotency guard. AFTER creating a revision you intend to work on, call `create_claim` on it before writing any code \u2014 the claim signals to other agents/humans that the Revision is actively being worked, preventing duplicate work \u2014 and `release_claim` when you finish or hand off.",
29444
+ "Create a new revision on a Beat. A Revision's canonical status starts `active`; PR progression is tracked separately on `scm.state` (`draft` \u2192 `open` \u2192 `merged` / `closed`). Environment progression (`in_staging`, `ready_for_production`, `live`) is tracked on the parent Beat Version, not the Revision. `beatVersionId` is optional \u2014 omit it for hotfixes, dep bumps, or other operational fixes that don't belong to a planning increment. Orphan Revisions (no BV) must carry their own `description` since there's no BV context to inherit. `baseRevisionId` stacks this Revision's PR/session checkout on another Revision's branch instead of the system default \u2014 for GitHub stacked PRs, must reference a Revision in the same system (validated server-side). Use this to add revisions alongside existing ones \u2014 no idempotency guard. AFTER creating a revision you intend to work on, call `create_claim` on it before writing any code \u2014 the claim signals to other agents/humans that the Revision is actively being worked, preventing duplicate work \u2014 and `release_claim` when you finish or hand off.",
29445
29445
  {
29446
29446
  beatId: external_exports.string().describe("The beat ID to create the revision on"),
29447
29447
  title: external_exports.string().describe("Title for this revision"),
29448
29448
  description: external_exports.string().describe("What this revision delivers. Required when beatVersionId is omitted (the description is the audit-trail floor for orphan Revisions)."),
29449
29449
  changeSummary: external_exports.string().describe("Brief summary of the change"),
29450
29450
  beatVersionId: external_exports.string().min(1).optional().describe("Beat Version ID to associate this revision with. Omit for hotfixes / orphan changes that don't fit a planning increment."),
29451
- baseRevisionId: external_exports.string().min(1).optional().describe("Existing Revision (same project) to stack this one on top of \u2014 its PR and agent session will target that Revision's branch instead of the project default (GitHub stacked PRs)."),
29451
+ baseRevisionId: external_exports.string().min(1).optional().describe("Existing Revision (same system) to stack this one on top of \u2014 its PR and agent session will target that Revision's branch instead of the system default (GitHub stacked PRs)."),
29452
29452
  tags: external_exports.array(external_exports.string()).optional().describe("Tags for categorization"),
29453
29453
  priority: external_exports.coerce.number().optional().describe("Priority (lower = higher priority)"),
29454
29454
  estimatedEffort: external_exports.string().optional().describe("Estimated effort (e.g., S, M, L, XL)"),
@@ -29718,7 +29718,7 @@ Accepts the full PR metadata (number, url, branch, state, optional mergeSha). Fi
29718
29718
  );
29719
29719
  server.tool(
29720
29720
  "create_pr_for_revision",
29721
- "Open a Harmonica-initiated DRAFT pull request for a revision and link it back via revision.scm. Cuts a branch (agent/rev-<short>-<slug>), bootstraps it with an empty commit, opens a draft PR whose body starts with the `Harmonica: rev-<uuid>` marker, then records the PR on the revision. Rejects if the revision already has a linked PR, or if the project has no repo configured. Dry-run by default \u2014 call with `confirm: true` to actually create the branch and PR. Leaves the revision in `draft`; transition it to `open` when the PR is marked ready for review.",
29721
+ "Open a Harmonica-initiated DRAFT pull request for a revision and link it back via revision.scm. Cuts a branch (agent/rev-<short>-<slug>), bootstraps it with an empty commit, opens a draft PR whose body starts with the `Harmonica: rev-<uuid>` marker, then records the PR on the revision. Rejects if the revision already has a linked PR, or if the system has no repo configured. Dry-run by default \u2014 call with `confirm: true` to actually create the branch and PR. Leaves the revision in `draft`; transition it to `open` when the PR is marked ready for review.",
29722
29722
  {
29723
29723
  revisionId: external_exports.string().describe("The revision ID (e.g., rev-abc123)"),
29724
29724
  confirm: external_exports.boolean().optional().describe("Set true to create the branch + draft PR. Omitted/false performs a dry-run that only describes what would happen \u2014 no GitHub or DB writes.")
@@ -30008,7 +30008,7 @@ function registerRevisionQualityTools(server, ctx, client) {
30008
30008
  "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.",
30009
30009
  {
30010
30010
  revisionId: external_exports.string().describe("The Revision ID (e.g., rev-abc123). revision_quality targets Revisions only."),
30011
- systemId: external_exports.string().describe("The project ID"),
30011
+ systemId: external_exports.string().describe("The system ID"),
30012
30012
  wait: external_exports.boolean().optional().describe("If true, block until the check completes and return the scorecard inline. Default: false (returns taskId immediately).")
30013
30013
  },
30014
30014
  async ({ revisionId, systemId, wait }) => {
@@ -30569,8 +30569,8 @@ function registerSubscriptionTools(server, ctx, client) {
30569
30569
  "Follow a Project, Beat, or Revision to receive notifications when it changes. You are auto-subscribed to things you own \u2014 use this to follow additional entities.",
30570
30570
  {
30571
30571
  entityType: external_exports.enum(ENTITY_TYPES).describe("Type of entity to follow"),
30572
- entityId: external_exports.string().describe("The entity ID (project ID, beat ID, or revision ID)"),
30573
- systemId: external_exports.string().describe("The project ID (for access control)")
30572
+ entityId: external_exports.string().describe("The entity ID (system ID, beat ID, or revision ID)"),
30573
+ systemId: external_exports.string().describe("The system ID (for access control)")
30574
30574
  },
30575
30575
  async ({ entityType, entityId, systemId }) => {
30576
30576
  try {
@@ -30614,7 +30614,7 @@ function registerSubscriptionTools(server, ctx, client) {
30614
30614
  );
30615
30615
  server.tool(
30616
30616
  "list_subscriptions",
30617
- "List entities you are currently following. Optionally filter by entity type (project, beat, revision).",
30617
+ "List entities you are currently following. Optionally filter by entity type (system, beat, revision).",
30618
30618
  {
30619
30619
  entityType: external_exports.enum(ENTITY_TYPES).optional().describe("Filter by entity type")
30620
30620
  },
@@ -30726,7 +30726,7 @@ function registerProjectTools(server, ctx, client) {
30726
30726
  };
30727
30727
  server.tool(
30728
30728
  "list_systems",
30729
- "List all systems (projects) in the configured organization",
30729
+ "List all systems in the configured organization",
30730
30730
  {
30731
30731
  teamspaceId: external_exports.string().optional().describe("Filter to systems belonging to a specific teamspace. Empty or whitespace-only treated as no filter.")
30732
30732
  },
@@ -31227,9 +31227,9 @@ function registerValueVelocityTools(server, ctx, client) {
31227
31227
  );
31228
31228
  server.tool(
31229
31229
  "propose_enablement_edges",
31230
- '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.',
31230
+ 'Propose directed enablement edges for a system \u2014 "Beat A accelerates Beat B". The LLM analyzes system 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.',
31231
31231
  {
31232
- systemId: external_exports.string().describe("The project ID"),
31232
+ systemId: external_exports.string().describe("The system ID"),
31233
31233
  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).")
31234
31234
  },
31235
31235
  async ({ systemId, beatId }) => {
@@ -31615,9 +31615,9 @@ ${JSON.stringify(task.result, null, 2)}
31615
31615
  );
31616
31616
  server.tool(
31617
31617
  "list_tasks",
31618
- "DEPRECATED: use `list_jobs`. List background jobs for a project with optional status filter.",
31618
+ "DEPRECATED: use `list_jobs`. List background jobs for a system with optional status filter.",
31619
31619
  {
31620
- systemId: external_exports.string().describe("The project ID"),
31620
+ systemId: external_exports.string().describe("The system ID"),
31621
31621
  status: external_exports.enum(TASK_STATUSES).optional().describe("Filter by status"),
31622
31622
  limit: external_exports.coerce.number().optional().default(20).describe("Max number of jobs to return")
31623
31623
  },
@@ -31673,9 +31673,9 @@ ${JSON.stringify(task.result, null, 2)}
31673
31673
  );
31674
31674
  server.tool(
31675
31675
  "list_jobs",
31676
- "List background jobs for a project with optional status filter",
31676
+ "List background jobs for a system with optional status filter",
31677
31677
  {
31678
- systemId: external_exports.string().describe("The project ID"),
31678
+ systemId: external_exports.string().describe("The system ID"),
31679
31679
  status: external_exports.enum(TASK_STATUSES).optional().describe("Filter by status"),
31680
31680
  limit: external_exports.coerce.number().optional().default(20).describe("Max number of jobs to return")
31681
31681
  },
@@ -31699,7 +31699,7 @@ ${JSON.stringify(task.result, null, 2)}
31699
31699
  "draft_coda",
31700
31700
  "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.",
31701
31701
  {
31702
- systemId: external_exports.string().describe("The project ID"),
31702
+ systemId: external_exports.string().describe("The system ID"),
31703
31703
  beatId: external_exports.string().describe("The beat ID (must have a title but no description)")
31704
31704
  },
31705
31705
  async ({ systemId, beatId }) => {
@@ -31732,7 +31732,7 @@ ${JSON.stringify(task.result, null, 2)}
31732
31732
  "validate_assumptions",
31733
31733
  "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.",
31734
31734
  {
31735
- systemId: external_exports.string().describe("The project ID"),
31735
+ systemId: external_exports.string().describe("The system ID"),
31736
31736
  noteIds: external_exports.array(external_exports.string()).optional().describe("Specific assumption note IDs to validate. If omitted, validates all unvalidated assumptions.")
31737
31737
  },
31738
31738
  async ({ systemId, noteIds }) => {
@@ -31764,9 +31764,9 @@ ${JSON.stringify(task.result, null, 2)}
31764
31764
  );
31765
31765
  server.tool(
31766
31766
  "start_session",
31767
- '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.',
31767
+ 'Start an agent session from a free-form message \u2014 like opening a Claude Code conversation pointed at your system. 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 system.',
31768
31768
  {
31769
- systemId: external_exports.string().min(1).describe("The project ID \u2014 the trusted scope the session is rooted at"),
31769
+ systemId: external_exports.string().min(1).describe("The system ID \u2014 the trusted scope the session is rooted at"),
31770
31770
  message: external_exports.string().min(1).describe("Free-form first message describing what the session should do"),
31771
31771
  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.")
31772
31772
  },
@@ -31804,7 +31804,7 @@ ${JSON.stringify(task.result, null, 2)}
31804
31804
  "plan_revision_batch",
31805
31805
  "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.",
31806
31806
  {
31807
- systemId: external_exports.string().describe("The project ID"),
31807
+ systemId: external_exports.string().describe("The system ID"),
31808
31808
  revisionIds: external_exports.array(external_exports.string()).optional().describe("Specific revision IDs to plan. If omitted, auto-discovers all unplanned planning revisions.")
31809
31809
  },
31810
31810
  async ({ systemId, revisionIds }) => {
@@ -31835,9 +31835,9 @@ ${JSON.stringify(task.result, null, 2)}
31835
31835
  );
31836
31836
  server.tool(
31837
31837
  "run_triage",
31838
- "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.",
31838
+ "Run the autonomous triage loop for a system. 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 system. Returns a job ID \u2014 use get_job_status to track progress.",
31839
31839
  {
31840
- systemId: external_exports.string().describe("The project ID")
31840
+ systemId: external_exports.string().describe("The system ID")
31841
31841
  },
31842
31842
  async ({ systemId }) => {
31843
31843
  try {
@@ -31880,7 +31880,7 @@ ${JSON.stringify(task.result, null, 2)}
31880
31880
  "Returns a job ID \u2014 use `get_job_status` to track progress."
31881
31881
  ].join("\n"),
31882
31882
  {
31883
- systemId: external_exports.string().describe("The project ID"),
31883
+ systemId: external_exports.string().describe("The system ID"),
31884
31884
  content: external_exports.string().describe('Document content \u2014 plain text (default) or base64-encoded binary (when encoding is "base64")'),
31885
31885
  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.'),
31886
31886
  encoding: external_exports.enum(["base64"]).optional().describe('Set to "base64" when submitting binary files (DOCX, XLSX, PPTX, PDF). Omit for plain text.')
@@ -32330,13 +32330,13 @@ function createHttpClient(config2) {
32330
32330
  `/api/organizations/${encodeURIComponent(sourceOrgId)}/transfer-project`,
32331
32331
  { systemId: projectId, targetOrgId }
32332
32332
  );
32333
- const project = result?.project ? {
32334
- ...result.project,
32335
- systemId: result.project.id ?? result.project.systemId ?? result.project.projectId,
32336
- title: result.project.name ?? result.project.title,
32337
- orgId: result.project.organizationId ?? result.project.orgId
32333
+ const system = result?.system ? {
32334
+ ...result.system,
32335
+ systemId: result.system.id ?? result.system.systemId ?? result.system.projectId,
32336
+ title: result.system.name ?? result.system.title,
32337
+ orgId: result.system.organizationId ?? result.system.orgId
32338
32338
  } : void 0;
32339
- return { success: result?.success ?? false, project };
32339
+ return { success: result?.success ?? false, system };
32340
32340
  } catch (err) {
32341
32341
  if (err instanceof Error) return { success: false, error: err.message };
32342
32342
  return { success: false, error: String(err) };
@@ -34317,7 +34317,7 @@ function loadConfig() {
34317
34317
  };
34318
34318
  }
34319
34319
  async function main() {
34320
- console.error(`[harmonica-mcp] v${"3.7.2"} starting\u2026`);
34320
+ console.error(`[harmonica-mcp] v${"3.8.0"} starting\u2026`);
34321
34321
  const config2 = loadConfig();
34322
34322
  const client = createHttpClient({
34323
34323
  apiBaseUrl: config2.apiBaseUrl,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codazen/harmonica-mcp",
3
- "version": "3.7.2",
3
+ "version": "3.8.0",
4
4
  "description": "MCP server for Harmonica — connect any MCP-compatible AI assistant to Harmonica",
5
5
  "license": "MIT",
6
6
  "bin": {