@task-boards/mcp-server 0.20.0 → 0.22.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.
package/build/cli.js CHANGED
@@ -1,4 +1,8 @@
1
1
  #!/usr/bin/env node
2
+ import { fileURLToPath as __tb_fileURLToPath } from "node:url";
3
+ import { dirname as __tb_dirname } from "node:path";
4
+ const __filename = __tb_fileURLToPath(import.meta.url);
5
+ const __dirname = __tb_dirname(__filename);
2
6
  var __create = Object.create;
3
7
  var __defProp = Object.defineProperty;
4
8
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -897,7 +901,8 @@ var require_notifications_enum = __commonJS({
897
901
  AGENT_IDE_ATTENTION: "AGENT_IDE_ATTENTION",
898
902
  AGENT_NEEDS_CLARIFICATION: "AGENT_NEEDS_CLARIFICATION",
899
903
  AGENT_RUN_FAILED: "AGENT_RUN_FAILED",
900
- AGENT_QA_HAS_BUGS: "AGENT_QA_HAS_BUGS"
904
+ AGENT_QA_HAS_BUGS: "AGENT_QA_HAS_BUGS",
905
+ AGENT_BATCH_COMPLETED: "AGENT_BATCH_COMPLETED"
901
906
  };
902
907
  }
903
908
  });
@@ -1504,7 +1509,8 @@ var require_members_api = __commonJS({
1504
1509
  updateMemberRole: (projectId, memberId) => `/projects/${projectId}/members/${memberId}`,
1505
1510
  removeMember: (projectId, memberId) => `/projects/${projectId}/members/${memberId}`,
1506
1511
  listInvitations: (projectId) => `/projects/${projectId}/invitations`,
1507
- revokeInvitation: (projectId, invitationId) => `/projects/${projectId}/invitations/${invitationId}`
1512
+ revokeInvitation: (projectId, invitationId) => `/projects/${projectId}/invitations/${invitationId}`,
1513
+ ensureServiceMember: (projectId) => `/projects/${projectId}/members/ensure-service-member`
1508
1514
  };
1509
1515
  }
1510
1516
  });
@@ -1522,7 +1528,7 @@ var require_invitations_enums = __commonJS({
1522
1528
  "../shared/build/internal/members/invitations.enums.js"(exports) {
1523
1529
  "use strict";
1524
1530
  Object.defineProperty(exports, "__esModule", { value: true });
1525
- exports.INVITE_OUTCOME = exports.INVITATION_STATUS = void 0;
1531
+ exports.ENSURE_SERVICE_MEMBER_OUTCOME = exports.INVITE_OUTCOME = exports.INVITATION_STATUS = void 0;
1526
1532
  exports.INVITATION_STATUS = {
1527
1533
  PENDING: "PENDING",
1528
1534
  ACCEPTED: "ACCEPTED",
@@ -1533,6 +1539,10 @@ var require_invitations_enums = __commonJS({
1533
1539
  MEMBER_ADDED: "MEMBER_ADDED",
1534
1540
  INVITATION_SENT: "INVITATION_SENT"
1535
1541
  };
1542
+ exports.ENSURE_SERVICE_MEMBER_OUTCOME = {
1543
+ ADDED: "added",
1544
+ ALREADY_PRESENT: "already_present"
1545
+ };
1536
1546
  }
1537
1547
  });
1538
1548
 
@@ -1631,6 +1641,7 @@ var require_exception_codes = __commonJS({
1631
1641
  CANNOT_DEMOTE_LAST_OWNER: "CANNOT_DEMOTE_LAST_OWNER",
1632
1642
  CANNOT_MODIFY_SERVICE_USER: "CANNOT_MODIFY_SERVICE_USER",
1633
1643
  CANNOT_REMOVE_SERVICE_USER: "CANNOT_REMOVE_SERVICE_USER",
1644
+ SERVICE_MEMBER_ENSURE_NOT_APPLICABLE: "SERVICE_MEMBER_ENSURE_NOT_APPLICABLE",
1634
1645
  AGENT_RUN_NOT_COMPLETABLE: "AGENT_RUN_NOT_COMPLETABLE",
1635
1646
  AGENT_RUN_INFLIGHT: "AGENT_RUN_INFLIGHT",
1636
1647
  AGENT_RUN_NOT_RETRYABLE: "AGENT_RUN_NOT_RETRYABLE",
@@ -1703,7 +1714,8 @@ var require_exception_codes = __commonJS({
1703
1714
  PAYMENTS_DISABLED: "PAYMENTS_DISABLED",
1704
1715
  PAYMENTS_NOT_CONFIGURED: "PAYMENTS_NOT_CONFIGURED",
1705
1716
  PAYMENTS_PROBE_FAILED: "PAYMENTS_PROBE_FAILED",
1706
- SYSTEM_READ_ONLY: "SYSTEM_READ_ONLY"
1717
+ SYSTEM_READ_ONLY: "SYSTEM_READ_ONLY",
1718
+ ESTIMATION_REQUIRED_FOR_MOVE: "ESTIMATION_REQUIRED_FOR_MOVE"
1707
1719
  };
1708
1720
  }
1709
1721
  });
@@ -2502,6 +2514,8 @@ var require_agentic_sdlc_phase_chain = __commonJS({
2502
2514
  exports.advanceAnalysisPhaseSkippingUnbound = advanceAnalysisPhaseSkippingUnbound;
2503
2515
  exports.initPendingDevRoles = initPendingDevRoles;
2504
2516
  exports.removeDevRoleFromPending = removeDevRoleFromPending;
2517
+ exports.isArchitectCompletionHandoff = isArchitectCompletionHandoff;
2518
+ exports.resolvePostArchitectColumnWhenNoDevRoles = resolvePostArchitectColumnWhenNoDevRoles;
2505
2519
  var subagent_role_enum_1 = require_subagent_role_enum();
2506
2520
  var workflow_phase_enum_1 = require_workflow_phase_enum();
2507
2521
  var subagent_role_util_1 = require_subagent_role_util();
@@ -2553,6 +2567,15 @@ var require_agentic_sdlc_phase_chain = __commonJS({
2553
2567
  function removeDevRoleFromPending(pendingDevRoles, completedRole) {
2554
2568
  return pendingDevRoles.filter((role) => role !== completedRole);
2555
2569
  }
2570
+ function isArchitectCompletionHandoff(completedAgentRole, transition) {
2571
+ return completedAgentRole === subagent_role_enum_1.SUBAGENT_ROLE.ARCHITECT && transition.nextColumnSlug === "in-development" && transition.nextWorkflowPhase === null && (transition.kind === "MOVE" || transition.kind === "HANDOFF");
2572
+ }
2573
+ function resolvePostArchitectColumnWhenNoDevRoles(bindings) {
2574
+ if (isRoleBound(bindings, subagent_role_enum_1.SUBAGENT_ROLE.QA)) {
2575
+ return "in-qa";
2576
+ }
2577
+ return "done";
2578
+ }
2556
2579
  }
2557
2580
  });
2558
2581
 
@@ -2561,7 +2584,7 @@ var require_agentic_sdlc_workflow = __commonJS({
2561
2584
  "../shared/build/workflows/agentic-sdlc.workflow.js"(exports) {
2562
2585
  "use strict";
2563
2586
  Object.defineProperty(exports, "__esModule", { value: true });
2564
- exports.removeDevRoleFromPending = exports.isRoleBound = exports.initPendingDevRoles = exports.getNextAnalysisPhase = exports.getFirstConfiguredAnalysisPhase = exports.advanceAnalysisPhaseSkippingUnbound = exports.IN_ANALYSIS_PHASE_CHAIN = exports.DEVELOPMENT_DEV_ROLES = exports.AGENTIC_SDLC_COLUMN_CLAIM_PRIORITY = exports.workflowPhaseToSubagentRole = exports.workflowPhaseToAgentRole = exports.subagentRoleToWorkflowPhase = exports.agentRoleToWorkflowPhase = exports.WORKFLOW_TRANSITION_KIND = exports.AGENTIC_SDLC_WORKFLOW = exports.AGENTIC_SDLC_AGENT_COLUMN_SLUGS = exports.IN_AWAITING_COLUMN_SLUG = void 0;
2587
+ exports.resolvePostArchitectColumnWhenNoDevRoles = exports.removeDevRoleFromPending = exports.isRoleBound = exports.isArchitectCompletionHandoff = exports.initPendingDevRoles = exports.getNextAnalysisPhase = exports.getFirstConfiguredAnalysisPhase = exports.advanceAnalysisPhaseSkippingUnbound = exports.IN_ANALYSIS_PHASE_CHAIN = exports.DEVELOPMENT_DEV_ROLES = exports.AGENTIC_SDLC_COLUMN_CLAIM_PRIORITY = exports.workflowPhaseToSubagentRole = exports.workflowPhaseToAgentRole = exports.subagentRoleToWorkflowPhase = exports.agentRoleToWorkflowPhase = exports.WORKFLOW_TRANSITION_KIND = exports.AGENTIC_SDLC_WORKFLOW = exports.AGENTIC_SDLC_AGENT_COLUMN_SLUGS = exports.IN_AWAITING_COLUMN_SLUG = void 0;
2565
2588
  exports.isAgenticSdlcAgentColumnSlug = isAgenticSdlcAgentColumnSlug;
2566
2589
  exports.isInAnalysisPhaseColumn = isInAnalysisPhaseColumn;
2567
2590
  exports.resolveAgenticSdlcTransition = resolveAgenticSdlcTransition;
@@ -2798,12 +2821,18 @@ var require_agentic_sdlc_workflow = __commonJS({
2798
2821
  Object.defineProperty(exports, "initPendingDevRoles", { enumerable: true, get: function() {
2799
2822
  return agentic_sdlc_phase_chain_2.initPendingDevRoles;
2800
2823
  } });
2824
+ Object.defineProperty(exports, "isArchitectCompletionHandoff", { enumerable: true, get: function() {
2825
+ return agentic_sdlc_phase_chain_2.isArchitectCompletionHandoff;
2826
+ } });
2801
2827
  Object.defineProperty(exports, "isRoleBound", { enumerable: true, get: function() {
2802
2828
  return agentic_sdlc_phase_chain_2.isRoleBound;
2803
2829
  } });
2804
2830
  Object.defineProperty(exports, "removeDevRoleFromPending", { enumerable: true, get: function() {
2805
2831
  return agentic_sdlc_phase_chain_2.removeDevRoleFromPending;
2806
2832
  } });
2833
+ Object.defineProperty(exports, "resolvePostArchitectColumnWhenNoDevRoles", { enumerable: true, get: function() {
2834
+ return agentic_sdlc_phase_chain_2.resolvePostArchitectColumnWhenNoDevRoles;
2835
+ } });
2807
2836
  }
2808
2837
  });
2809
2838
 
@@ -2926,7 +2955,7 @@ var require_workflows = __commonJS({
2926
2955
  "../shared/build/workflows/index.js"(exports) {
2927
2956
  "use strict";
2928
2957
  Object.defineProperty(exports, "__esModule", { value: true });
2929
- exports.workflowPhaseToSubagentRole = exports.workflowPhaseToAgentRole = exports.subagentRoleToWorkflowPhase = exports.resolveAgenticSdlcTransition = exports.resolveAgenticSdlcNextColumnSlug = exports.removeDevRoleFromPending = exports.isRoleBound = exports.isInAnalysisPhaseColumn = exports.isAgenticSdlcAgentColumnSlug = exports.initPendingDevRoles = exports.getNextAnalysisPhase = exports.getFirstConfiguredAnalysisPhase = exports.agentRoleToWorkflowPhase = exports.advanceAnalysisPhaseSkippingUnbound = exports.WORKFLOW_TRANSITION_KIND = exports.IN_AWAITING_COLUMN_SLUG = exports.IN_ANALYSIS_PHASE_CHAIN = exports.DEVELOPMENT_DEV_ROLES = exports.AGENTIC_SDLC_WORKFLOW = exports.AGENTIC_SDLC_COLUMN_CLAIM_PRIORITY = exports.AGENTIC_SDLC_AGENT_COLUMN_SLUGS = exports.toPhysicalColumnSlug = exports.toLogicalColumnSlug = exports.shouldSyntheticEnqueue = exports.resolveLogicalColumnSlugForWorkItem = exports.isLogicalAgentColumnSlug = void 0;
2958
+ exports.workflowPhaseToSubagentRole = exports.workflowPhaseToAgentRole = exports.subagentRoleToWorkflowPhase = exports.resolvePostArchitectColumnWhenNoDevRoles = exports.resolveAgenticSdlcTransition = exports.resolveAgenticSdlcNextColumnSlug = exports.removeDevRoleFromPending = exports.isRoleBound = exports.isInAnalysisPhaseColumn = exports.isAgenticSdlcAgentColumnSlug = exports.isArchitectCompletionHandoff = exports.initPendingDevRoles = exports.getNextAnalysisPhase = exports.getFirstConfiguredAnalysisPhase = exports.agentRoleToWorkflowPhase = exports.advanceAnalysisPhaseSkippingUnbound = exports.WORKFLOW_TRANSITION_KIND = exports.IN_AWAITING_COLUMN_SLUG = exports.IN_ANALYSIS_PHASE_CHAIN = exports.DEVELOPMENT_DEV_ROLES = exports.AGENTIC_SDLC_WORKFLOW = exports.AGENTIC_SDLC_COLUMN_CLAIM_PRIORITY = exports.AGENTIC_SDLC_AGENT_COLUMN_SLUGS = exports.toPhysicalColumnSlug = exports.toLogicalColumnSlug = exports.shouldSyntheticEnqueue = exports.resolveLogicalColumnSlugForWorkItem = exports.isLogicalAgentColumnSlug = void 0;
2930
2959
  var column_slug_mapping_1 = require_column_slug_mapping();
2931
2960
  Object.defineProperty(exports, "isLogicalAgentColumnSlug", { enumerable: true, get: function() {
2932
2961
  return column_slug_mapping_1.isLogicalAgentColumnSlug;
@@ -2980,6 +3009,9 @@ var require_workflows = __commonJS({
2980
3009
  Object.defineProperty(exports, "initPendingDevRoles", { enumerable: true, get: function() {
2981
3010
  return agentic_sdlc_workflow_1.initPendingDevRoles;
2982
3011
  } });
3012
+ Object.defineProperty(exports, "isArchitectCompletionHandoff", { enumerable: true, get: function() {
3013
+ return agentic_sdlc_workflow_1.isArchitectCompletionHandoff;
3014
+ } });
2983
3015
  Object.defineProperty(exports, "isAgenticSdlcAgentColumnSlug", { enumerable: true, get: function() {
2984
3016
  return agentic_sdlc_workflow_1.isAgenticSdlcAgentColumnSlug;
2985
3017
  } });
@@ -2998,6 +3030,9 @@ var require_workflows = __commonJS({
2998
3030
  Object.defineProperty(exports, "resolveAgenticSdlcTransition", { enumerable: true, get: function() {
2999
3031
  return agentic_sdlc_workflow_1.resolveAgenticSdlcTransition;
3000
3032
  } });
3033
+ Object.defineProperty(exports, "resolvePostArchitectColumnWhenNoDevRoles", { enumerable: true, get: function() {
3034
+ return agentic_sdlc_workflow_1.resolvePostArchitectColumnWhenNoDevRoles;
3035
+ } });
3001
3036
  Object.defineProperty(exports, "subagentRoleToWorkflowPhase", { enumerable: true, get: function() {
3002
3037
  return agentic_sdlc_workflow_1.subagentRoleToWorkflowPhase;
3003
3038
  } });
@@ -5029,6 +5064,10 @@ function isAttentionCliInvocation(argv) {
5029
5064
  return topLevel === "attention" || topLevel === "orchestrator";
5030
5065
  }
5031
5066
 
5067
+ // src/cli-help.ts
5068
+ import { readFileSync as readFileSync4 } from "node:fs";
5069
+ import { join as join5 } from "node:path";
5070
+
5032
5071
  // src/config.ts
5033
5072
  var import_api = __toESM(require_build(), 1);
5034
5073
  function resolveSanitizeResponses() {
@@ -5047,15 +5086,22 @@ function resolveBlockSensitiveAttachments() {
5047
5086
  const normalized = raw.trim().toLowerCase();
5048
5087
  return normalized !== "false" && normalized !== "0" && normalized !== "no";
5049
5088
  }
5089
+ function resolveApiUrlFromEnv(env = process.env) {
5090
+ const rawBaseUrl = (env.TASK_BOARDS_API_URL ?? "http://localhost:3000").replace(/\/$/, "");
5091
+ return {
5092
+ rawBaseUrl,
5093
+ effectiveApiUrl: `${rawBaseUrl}${import_api.API_V1_PREFIX}`
5094
+ };
5095
+ }
5050
5096
  function loadConfig() {
5051
- const apiUrl = process.env.TASK_BOARDS_API_URL ?? "http://localhost:3000";
5097
+ const { effectiveApiUrl } = resolveApiUrlFromEnv();
5052
5098
  const apiToken = process.env.TASK_BOARDS_API_TOKEN;
5053
5099
  const workspaceRoot = process.env.WORKSPACE_ROOT;
5054
5100
  if (process.env.NODE_ENV === "production" && (apiToken === void 0 || apiToken.trim() === "")) {
5055
5101
  console.error("WARNING: TASK_BOARDS_API_TOKEN is not set. MCP server cannot authenticate to the Task Boards API.");
5056
5102
  }
5057
5103
  return {
5058
- apiUrl: `${apiUrl.replace(/\/$/, "")}${import_api.API_V1_PREFIX}`,
5104
+ apiUrl: effectiveApiUrl,
5059
5105
  apiToken,
5060
5106
  workspaceRoot: workspaceRoot !== void 0 && workspaceRoot.trim() !== "" ? workspaceRoot : void 0,
5061
5107
  sanitizeResponses: resolveSanitizeResponses(),
@@ -5063,6 +5109,405 @@ function loadConfig() {
5063
5109
  };
5064
5110
  }
5065
5111
 
5112
+ // src/cli-help.ts
5113
+ var NPM_README_URL = "https://www.npmjs.com/package/@task-boards/mcp-server#readme";
5114
+ var CLI_ENV_VARS = [
5115
+ {
5116
+ name: "TASK_BOARDS_API_URL",
5117
+ required: "No",
5118
+ defaultDisplay: "http://localhost:3000",
5119
+ description: "Task Boards backend base URL (without /api/v1 suffix)"
5120
+ },
5121
+ {
5122
+ name: "TASK_BOARDS_API_TOKEN",
5123
+ required: "Yes in production",
5124
+ defaultDisplay: "(unset \u2014 value never printed)",
5125
+ description: "API token with read / write scopes"
5126
+ },
5127
+ {
5128
+ name: "WORKSPACE_ROOT",
5129
+ required: "No",
5130
+ defaultDisplay: "(unset)",
5131
+ description: "Git repo root for attachments, sync tools, and orchestrator bridge state"
5132
+ },
5133
+ {
5134
+ name: "TASK_BOARDS_MCP_SANITIZE",
5135
+ required: "No",
5136
+ defaultDisplay: "true (enabled)",
5137
+ description: "Sanitize MCP tool responses; set false, 0, or no to disable"
5138
+ },
5139
+ {
5140
+ name: "TASK_BOARDS_MCP_BLOCK_SENSITIVE_ATTACHMENTS",
5141
+ required: "No",
5142
+ defaultDisplay: "true (enabled)",
5143
+ description: "Block sensitive attachment paths; set false, 0, or no to disable"
5144
+ }
5145
+ ];
5146
+ var CLI_TOOL_CATALOG = [
5147
+ {
5148
+ name: "list_projects",
5149
+ description: "List task-boards projects (non-archived by default)."
5150
+ },
5151
+ {
5152
+ name: "get_project",
5153
+ description: "Get a project by id."
5154
+ },
5155
+ {
5156
+ name: "create_project",
5157
+ description: "Create a project and its board from the selected preset."
5158
+ },
5159
+ {
5160
+ name: "update_project",
5161
+ description: "Update project settings (name, description, estimation mode/required, task card estimation display, subagent role bindings). Requires write scope."
5162
+ },
5163
+ {
5164
+ name: "resolve_project",
5165
+ description: "Resolve the task-boards project for the current workspace (.task-boards.yaml, .cursor/rules, or folder name auto-match)."
5166
+ },
5167
+ {
5168
+ name: "list_work_items",
5169
+ description: "List work items for a project."
5170
+ },
5171
+ {
5172
+ name: "search_work_items",
5173
+ description: "Search work items by query string (title, description, acceptance criteria, display key)."
5174
+ },
5175
+ {
5176
+ name: "get_work_item",
5177
+ description: "Get a work item by id."
5178
+ },
5179
+ {
5180
+ name: "create_work_item",
5181
+ description: "Create a work item under a project. MCP/automation attributes creator and default assignee to the \xAB\u0410\u0433\u0435\u043D\u0442 \u0418\u0418\xBB service user (UserKind.SERVICE) unless assigneeUserId is set. The AI orchestrator processes STORY items only when assignee is that service user. Priority defaults to LOW when omitted. storyPoints and estimate are optional on create; when project.estimationRequired is true they are required before moving a STORY to any column except backlog (slug=backlog)."
5182
+ },
5183
+ {
5184
+ name: "update_work_item",
5185
+ description: "Update work item fields (fetches current version first)."
5186
+ },
5187
+ {
5188
+ name: "move_work_item",
5189
+ description: "Move a work item to another board column (fetches current version first). When project.estimationRequired is true, returns ESTIMATION_REQUIRED_FOR_MOVE (422) if the STORY lacks active storyPoints or estimate (per estimationMode) and the target column slug is not backlog."
5190
+ },
5191
+ {
5192
+ name: "get_work_item_delete_preview",
5193
+ description: "Preview hard-delete impact for a work item (cascade counts and eligibility). MCP may delete SUBTASK items only."
5194
+ },
5195
+ {
5196
+ name: "delete_work_item",
5197
+ description: "Hard-delete a SUBTASK work item (leaf delete with confirmCascade false). EPIC/STORY deletes are human-only."
5198
+ },
5199
+ {
5200
+ name: "list_comments",
5201
+ description: "List comments for a work item (newest last)."
5202
+ },
5203
+ {
5204
+ name: "create_comment",
5205
+ description: "Create a comment on a work item. Use for orchestrator NEEDS_CLARIFICATION questions before complete_agent_run."
5206
+ },
5207
+ {
5208
+ name: "list_labels",
5209
+ description: "List labels for a project."
5210
+ },
5211
+ {
5212
+ name: "create_label",
5213
+ description: "Create a project label."
5214
+ },
5215
+ {
5216
+ name: "update_label",
5217
+ description: "Update a project label."
5218
+ },
5219
+ {
5220
+ name: "delete_label",
5221
+ description: "Delete a project label."
5222
+ },
5223
+ {
5224
+ name: "list_work_item_attachments",
5225
+ description: "List file attachments for a work item."
5226
+ },
5227
+ {
5228
+ name: "download_work_item_attachments",
5229
+ description: "Download work item attachments into the local workspace staging directory (.task-boards/attachments)."
5230
+ },
5231
+ {
5232
+ name: "upload_work_item_attachment",
5233
+ description: "Upload a local workspace file as a work item attachment (multipart field file)."
5234
+ },
5235
+ {
5236
+ name: "get_board",
5237
+ description: "Get board projection for a project (columns and on-board work items)."
5238
+ },
5239
+ {
5240
+ name: "list_agent_runs",
5241
+ description: "List agent runs for a project (default status PENDING). Prefer wait_for_agent_runs for orchestrator long-polling."
5242
+ },
5243
+ {
5244
+ name: "wait_for_agent_runs",
5245
+ description: "Long-poll agent runs for a project: atomically claims PENDING runs (POST /agent-runs/claim). On empty claim, immediately lists inflight DISPATCHED/ACKNOWLEDGED runs (GET activeOnly) for orphan recovery before sleeping. Returns items (newly claimed), inflightRuns (resume candidates), and orchestrator hints. First claim is immediate; subsequent polls sleep pollInterval seconds only when both claim and inflight are empty."
5246
+ },
5247
+ {
5248
+ name: "ack_agent_run",
5249
+ description: "Acknowledge agent run delivery after Task subagent has been dispatched. Requires status DISPATCHED (DISPATCHED \u2192 ACKNOWLEDGED)."
5250
+ },
5251
+ {
5252
+ name: "report_agent_attention",
5253
+ description: "Signal that the in-flight subagent needs human attention while the run is ACKNOWLEDGED. Sets requiresAttention on the agent run for orchestrator/UI visibility."
5254
+ },
5255
+ {
5256
+ name: "clear_agent_attention",
5257
+ description: "Clear the human-attention flag after the blocker is resolved. Requires the run to still be ACKNOWLEDGED."
5258
+ },
5259
+ {
5260
+ name: "complete_agent_run",
5261
+ description: "Complete agent run: apply AGENTIC_SDLC workflow transition and optional work item patch. AGENTIC_SDLC has 7 columns: backlog, in-analysis (PRODUCT\u2192ANALYST\u2192ARCHITECT handoffs), in-development (DESIGNER optional, then parallel FRONTEND_DEVELOPER+DEVELOPER), in-qa, in-awaiting, done, released. Outcomes: Prerequisite: STORY assignee must be \u0410\u0433\u0435\u043D\u0442 \u0418\u0418 (SERVICE user) \u2014 otherwise no agent_run is enqueued or claimed. in-analysis: PRODUCT/ANALYST DEFAULT \u2192 HANDOFF next phase (skip unbound roles); ANALYST SKIP_DESIGN \u2192 in-development; ANALYST SKIP_DEV \u2192 released when codeChangesRequired=false (no git commit); ARCHITECT DEFAULT \u2192 in-development (set workItemPatch.designerRequired). in-development: DESIGNER DEFAULT \u2192 DEVELOPMENT; dev role DEFAULT removes role from pendingDevRoles, moves to in-qa when empty; SKIP_DESIGN skips DESIGNER. in-qa: DEFAULT \u2192 done; HAS_BUGS \u2192 in-development (re-init pendingDevRoles). NEEDS_CLARIFICATION \u2192 in-awaiting. FAILED \u2192 no move. Claim priority: in-development > in-qa > in-analysis (SERVICE-assigned stories only block analysis)."
5262
+ },
5263
+ {
5264
+ name: "run_orchestrator_once",
5265
+ description: "Resolve project (if needed), long-poll agent runs with atomic claim, and return orchestrator hints. Claims only STORY items assigned to \u0410\u0433\u0435\u043D\u0442 \u0418\u0418 (SERVICE user). Does not call sync_git_releases \u2014 the orchestrator agent decides when to sync."
5266
+ },
5267
+ {
5268
+ name: "sync_git_releases",
5269
+ description: "Fetch origin and sync commits tagged with work-item:{uuid} from the default branch (master/main) and, when cwd is on a feature branch, the current branch too. Use after git push on feature branches; on master/main local commit only. Requires MCP API token with write scope."
5270
+ },
5271
+ {
5272
+ name: "sync_project_subagents",
5273
+ description: "Sync project custom subagent definitions from the API to {workspaceRoot}/.cursor/agents/{slug}.md so the IDE Task tool can resolve custom slugs. Idempotent overwrite. Requires MCP API token with write scope."
5274
+ }
5275
+ ];
5276
+ var CLI_AUX_MODES = [
5277
+ {
5278
+ invocation: "task-boards-mcp",
5279
+ description: "Default: MCP stdio server for IDE clients"
5280
+ },
5281
+ {
5282
+ invocation: "task-boards-mcp attention report",
5283
+ description: "Report IDE attention for an in-flight agent run (orchestrator hooks)"
5284
+ },
5285
+ {
5286
+ invocation: "task-boards-mcp attention report-from-hook",
5287
+ description: "Report IDE attention from Cursor hook stdin payload"
5288
+ },
5289
+ {
5290
+ invocation: "task-boards-mcp attention clear",
5291
+ description: "Clear local attention pending state"
5292
+ },
5293
+ {
5294
+ invocation: "task-boards-mcp orchestrator write-active-run",
5295
+ description: "Persist active agent run state to workspace bridge files"
5296
+ },
5297
+ {
5298
+ invocation: "task-boards-mcp orchestrator clear-active-run",
5299
+ description: "Clear orchestrator bridge state from workspace"
5300
+ },
5301
+ {
5302
+ invocation: "task-boards-mcp skills install [--global]",
5303
+ description: "Install bundled Task Boards Cursor skills to .cursor/skills/ (project) or ~/.cursor/skills/ (--global)"
5304
+ }
5305
+ ];
5306
+ function isHelpInvocation(argv) {
5307
+ const topLevel = argv[0];
5308
+ return topLevel === "-h" || topLevel === "--help";
5309
+ }
5310
+ function readPackageMeta() {
5311
+ const pkgPath = join5(__dirname, "../package.json");
5312
+ const pkg = JSON.parse(readFileSync4(pkgPath, "utf8"));
5313
+ return {
5314
+ name: pkg.name,
5315
+ version: pkg.version
5316
+ };
5317
+ }
5318
+ function formatEnvSection(env) {
5319
+ const lines = ["ENVIRONMENT"];
5320
+ for (const variable of CLI_ENV_VARS) {
5321
+ lines.push(` ${variable.name}`);
5322
+ lines.push(` Required: ${variable.required}`);
5323
+ lines.push(` Default: ${variable.defaultDisplay}`);
5324
+ lines.push(` ${variable.description}`);
5325
+ lines.push("");
5326
+ }
5327
+ const { effectiveApiUrl } = resolveApiUrlFromEnv(env);
5328
+ lines.push(" Effective API base: {TASK_BOARDS_API_URL without trailing slash}/api/v1");
5329
+ lines.push(` Example default: ${effectiveApiUrl}`);
5330
+ return lines.join("\n");
5331
+ }
5332
+ function formatToolsSection() {
5333
+ const lines = [`MCP TOOLS (${CLI_TOOL_CATALOG.length})`];
5334
+ for (const tool of CLI_TOOL_CATALOG) {
5335
+ lines.push(` ${tool.name} \u2014 ${tool.description}`);
5336
+ }
5337
+ return lines.join("\n");
5338
+ }
5339
+ function formatAuxModesSection() {
5340
+ const lines = ["AUXILIARY CLI MODES"];
5341
+ for (const mode of CLI_AUX_MODES) {
5342
+ lines.push(` ${mode.invocation}`);
5343
+ lines.push(` ${mode.description}`);
5344
+ }
5345
+ lines.push("");
5346
+ lines.push(" attention report|report-from-hook|clear");
5347
+ lines.push(" orchestrator write-active-run|clear-active-run");
5348
+ lines.push(" skills install [--global]");
5349
+ return lines.join("\n");
5350
+ }
5351
+ function formatCliHelp(options = {}) {
5352
+ const packageMeta = options.packageMeta ?? readPackageMeta();
5353
+ const env = options.env ?? process.env;
5354
+ const sections = [
5355
+ `${packageMeta.name} ${packageMeta.version}`,
5356
+ "",
5357
+ "USAGE",
5358
+ " task-boards-mcp [--help|-h]",
5359
+ " task-boards-mcp [attention|orchestrator ...] # auxiliary bridge modes",
5360
+ " task-boards-mcp skills install [--global] # install bundled Cursor skills",
5361
+ " task-boards-mcp # default: MCP stdio server",
5362
+ "",
5363
+ formatEnvSection(env),
5364
+ "",
5365
+ formatToolsSection(),
5366
+ "",
5367
+ formatAuxModesSection(),
5368
+ "",
5369
+ "DOCUMENTATION",
5370
+ ` Orchestrator loop, IDE MCP config: ${NPM_README_URL}`,
5371
+ " Local development: packages/mcp-server/README.md",
5372
+ ""
5373
+ ];
5374
+ return sections.join("\n");
5375
+ }
5376
+
5377
+ // src/skills/install-bundled-skills.ts
5378
+ import { copyFileSync, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "node:fs";
5379
+ import { join as join6 } from "node:path";
5380
+
5381
+ // src/skills/bundled-skills-catalog.ts
5382
+ var BUNDLED_SKILL_NAMES = [
5383
+ "task-boards-setup",
5384
+ "task-boards-orchestrator",
5385
+ "task-boards-work-items",
5386
+ "task-boards-agent-runs",
5387
+ "task-boards-attention",
5388
+ "task-boards-git-sync"
5389
+ ];
5390
+
5391
+ // src/skills/install-bundled-skills.ts
5392
+ var BundledSkillsError = class extends Error {
5393
+ constructor(message) {
5394
+ super(message);
5395
+ this.name = "BundledSkillsError";
5396
+ }
5397
+ };
5398
+ function installBundledSkills(params) {
5399
+ const { bundledSkillsRoot, targetSkillsRoot } = params;
5400
+ const packageMeta = readPackageMeta();
5401
+ mkdirSync3(targetSkillsRoot, { recursive: true });
5402
+ const skills = [];
5403
+ for (const skillName of BUNDLED_SKILL_NAMES) {
5404
+ const srcFile = join6(bundledSkillsRoot, skillName, "SKILL.md");
5405
+ if (!existsSync6(srcFile)) {
5406
+ throw new BundledSkillsError(`Missing bundled skill file: ${srcFile}`);
5407
+ }
5408
+ const destDir = join6(targetSkillsRoot, skillName);
5409
+ const destFile = join6(destDir, "SKILL.md");
5410
+ const action = existsSync6(destFile) ? "overwritten" : "installed";
5411
+ mkdirSync3(destDir, { recursive: true });
5412
+ copyFileSync(srcFile, destFile);
5413
+ skills.push({
5414
+ skillName,
5415
+ targetPath: destFile,
5416
+ action
5417
+ });
5418
+ }
5419
+ return {
5420
+ targetDirectory: targetSkillsRoot,
5421
+ packageName: packageMeta.name,
5422
+ packageVersion: packageMeta.version,
5423
+ skills
5424
+ };
5425
+ }
5426
+
5427
+ // src/skills/resolve-bundled-skills-root.ts
5428
+ import { homedir } from "node:os";
5429
+ import { join as join7 } from "node:path";
5430
+ function resolveBundledSkillsRoot() {
5431
+ return join7(__dirname, "../skills");
5432
+ }
5433
+ function resolveProjectSkillsTarget(workspaceRoot) {
5434
+ return join7(workspaceRoot, ".cursor", "skills");
5435
+ }
5436
+ function resolveGlobalSkillsTarget(homeDirectory = homedir()) {
5437
+ return join7(homeDirectory, ".cursor", "skills");
5438
+ }
5439
+
5440
+ // src/skills/skills-cli.ts
5441
+ function defaultLog(message) {
5442
+ process.stdout.write(`${message}
5443
+ `);
5444
+ }
5445
+ function defaultLogError(message) {
5446
+ process.stderr.write(`${message}
5447
+ `);
5448
+ }
5449
+ function isSkillsCliInvocation(argv) {
5450
+ return argv[0] === "skills";
5451
+ }
5452
+ function hasGlobalFlag(argv) {
5453
+ return argv.includes("--global");
5454
+ }
5455
+ function formatSkillsInstallSummary(result) {
5456
+ const lines = [
5457
+ `Installed Task Boards skills (${result.packageName} ${result.packageVersion})`,
5458
+ `Target: ${result.targetDirectory}`,
5459
+ ""
5460
+ ];
5461
+ for (const skill of result.skills) {
5462
+ lines.push(` ${skill.skillName}: ${skill.action}`);
5463
+ }
5464
+ return lines.join("\n");
5465
+ }
5466
+ function runSkillsCli(argv, deps = {}) {
5467
+ const log = deps.log ?? defaultLog;
5468
+ const logError = deps.logError ?? defaultLogError;
5469
+ const installFn = deps.installFn ?? installBundledSkills;
5470
+ const resolveBundledRoot = deps.resolveBundledRoot ?? resolveBundledSkillsRoot;
5471
+ const resolveWorkspace = deps.resolveWorkspace ?? resolveWorkspaceRoot;
5472
+ const resolveGlobalTarget = deps.resolveGlobalTarget ?? resolveGlobalSkillsTarget;
5473
+ const subcommand = argv[1];
5474
+ if (subcommand !== "install") {
5475
+ logError("Usage: task-boards-mcp skills install [--global]");
5476
+ return 1;
5477
+ }
5478
+ const isGlobal = hasGlobalFlag(argv);
5479
+ let targetSkillsRoot;
5480
+ try {
5481
+ if (isGlobal) {
5482
+ targetSkillsRoot = resolveGlobalTarget();
5483
+ } else {
5484
+ const config = loadConfig();
5485
+ const workspaceRoot = resolveWorkspace(void 0, config.workspaceRoot);
5486
+ targetSkillsRoot = resolveProjectSkillsTarget(workspaceRoot);
5487
+ }
5488
+ } catch (error) {
5489
+ const message = error instanceof Error ? error.message : "Failed to resolve skills install target";
5490
+ logError(message);
5491
+ return 1;
5492
+ }
5493
+ try {
5494
+ const result = installFn({
5495
+ bundledSkillsRoot: resolveBundledRoot(),
5496
+ targetSkillsRoot
5497
+ });
5498
+ log(formatSkillsInstallSummary(result));
5499
+ return 0;
5500
+ } catch (error) {
5501
+ if (error instanceof BundledSkillsError) {
5502
+ logError(error.message);
5503
+ return 1;
5504
+ }
5505
+ const message = error instanceof Error ? error.message : "Failed to install bundled skills";
5506
+ logError(message);
5507
+ return 1;
5508
+ }
5509
+ }
5510
+
5066
5511
  // src/index.ts
5067
5512
  import { createRequire } from "node:module";
5068
5513
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -5509,8 +5954,8 @@ function autoMatchProject(folderToken, projects) {
5509
5954
  }
5510
5955
 
5511
5956
  // src/workspace/parse-ide-rules.ts
5512
- import { existsSync as existsSync6, readdirSync, readFileSync as readFileSync4 } from "node:fs";
5513
- import { join as join5 } from "node:path";
5957
+ import { existsSync as existsSync7, readdirSync, readFileSync as readFileSync5 } from "node:fs";
5958
+ import { join as join8 } from "node:path";
5514
5959
  var PROJECT_ID_LINE_PATTERN = /projectId\s*[:=]\s*["']?([0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})/gi;
5515
5960
  var TASK_BOARDS_UUID_PATTERN = /task-boards[^\n\r]*?([0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})/gi;
5516
5961
  function extractUuids(content) {
@@ -5529,8 +5974,8 @@ function extractUuids(content) {
5529
5974
  return ids;
5530
5975
  }
5531
5976
  function parseIdeRules(workspaceRoot) {
5532
- const rulesDir = join5(workspaceRoot, ".cursor", "rules");
5533
- if (!existsSync6(rulesDir)) {
5977
+ const rulesDir = join8(workspaceRoot, ".cursor", "rules");
5978
+ if (!existsSync7(rulesDir)) {
5534
5979
  return { status: "missing" };
5535
5980
  }
5536
5981
  const entries = readdirSync(rulesDir, { withFileTypes: true });
@@ -5542,7 +5987,7 @@ function parseIdeRules(workspaceRoot) {
5542
5987
  if (!entry.name.endsWith(".mdc") && !entry.name.endsWith(".md")) {
5543
5988
  continue;
5544
5989
  }
5545
- const content = readFileSync4(join5(rulesDir, entry.name), "utf8");
5990
+ const content = readFileSync5(join8(rulesDir, entry.name), "utf8");
5546
5991
  for (const id of extractUuids(content)) {
5547
5992
  allIds.add(id);
5548
5993
  }
@@ -6184,7 +6629,7 @@ function registerWorkItemTools(server, client) {
6184
6629
  server.registerTool(
6185
6630
  "create_work_item",
6186
6631
  {
6187
- description: "Create a work item under a project. MCP/automation attributes creator and default assignee to the \xAB\u0410\u0433\u0435\u043D\u0442 \u0418\u0418\xBB service user (UserKind.SERVICE) unless assigneeUserId is set. The AI orchestrator processes STORY items only when assignee is that service user. Priority defaults to LOW when omitted.",
6632
+ description: "Create a work item under a project. MCP/automation attributes creator and default assignee to the \xAB\u0410\u0433\u0435\u043D\u0442 \u0418\u0418\xBB service user (UserKind.SERVICE) unless assigneeUserId is set. The AI orchestrator processes STORY items only when assignee is that service user. Priority defaults to LOW when omitted. storyPoints and estimate are optional on create; when project.estimationRequired is true they are required before moving a STORY to any column except backlog (slug=backlog).",
6188
6633
  inputSchema: {
6189
6634
  projectId: z11.string().uuid().describe("Project UUID"),
6190
6635
  type: z11.enum(workItemTypeValues).describe("Work item type"),
@@ -6193,9 +6638,11 @@ function registerWorkItemTools(server, client) {
6193
6638
  description: z11.string().nullable().optional().describe("Work item description"),
6194
6639
  acceptanceCriteria: z11.string().nullable().optional().describe("Acceptance criteria"),
6195
6640
  priority: z11.enum(priorityValues).nullable().optional().describe("Work item priority; defaults to LOW when omitted or null"),
6196
- storyPoints: z11.number().int().min(0).max(999).nullable().optional().describe("Story points (EPIC/STORY only). Required when project estimationRequired and mode is storyPoints"),
6641
+ storyPoints: z11.number().int().min(0).max(999).nullable().optional().describe(
6642
+ "Story points (EPIC/STORY only). Optional on create; required before move to non-backlog columns when project estimationRequired and mode is storyPoints"
6643
+ ),
6197
6644
  estimate: z11.string().nullable().optional().describe(
6198
- "Estimate in hours as decimal string (EPIC/STORY only). Required when estimationRequired and mode is estimate"
6645
+ "Estimate in hours as decimal string (EPIC/STORY only). Optional on create; required before move to non-backlog columns when estimationRequired and mode is estimate"
6199
6646
  ),
6200
6647
  assigneeUserId: z11.string().uuid().nullable().optional().describe(
6201
6648
  "Project member user id; defaults to creator (human) or \xAB\u0410\u0433\u0435\u043D\u0442 \u0418\u0418\xBB service user (MCP) when omitted"
@@ -6290,7 +6737,7 @@ function registerWorkItemTools(server, client) {
6290
6737
  server.registerTool(
6291
6738
  "move_work_item",
6292
6739
  {
6293
- description: "Move a work item to another board column (fetches current version first).",
6740
+ description: "Move a work item to another board column (fetches current version first). When project.estimationRequired is true, returns ESTIMATION_REQUIRED_FOR_MOVE (422) if the STORY lacks active storyPoints or estimate (per estimationMode) and the target column slug is not backlog.",
6294
6741
  inputSchema: {
6295
6742
  workItemId: z11.string().uuid().describe("Work item UUID"),
6296
6743
  columnId: z11.string().uuid().describe("Target column UUID")
@@ -6364,12 +6811,20 @@ async function main() {
6364
6811
  // src/cli.ts
6365
6812
  async function main2() {
6366
6813
  const argv = process.argv.slice(2);
6814
+ if (isHelpInvocation(argv)) {
6815
+ process.stdout.write(formatCliHelp({ packageMeta: readPackageMeta() }));
6816
+ process.exit(0);
6817
+ }
6367
6818
  if (isAttentionCliInvocation(argv)) {
6368
6819
  const config = loadConfig();
6369
6820
  const client = new RestClient(config.apiUrl, config.apiToken);
6370
6821
  const exitCode = await runAttentionCli(argv, { config, client });
6371
6822
  process.exit(exitCode);
6372
6823
  }
6824
+ if (isSkillsCliInvocation(argv)) {
6825
+ const exitCode = runSkillsCli(argv);
6826
+ process.exit(exitCode);
6827
+ }
6373
6828
  await main();
6374
6829
  }
6375
6830
  main2().catch((error) => {