taskin 4.2.0 → 4.3.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 (3) hide show
  1. package/README.md +45 -23
  2. package/dist/index.js +967 -570
  3. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -278,8 +278,8 @@ ${body}` : subject;
278
278
  }
279
279
  const lines = output.split("\n").filter((l) => l.trim());
280
280
  for (const line of lines) {
281
- const [added, removed, path12] = line.split(" ");
282
- if (added === void 0 || removed === void 0 || path12 === void 0) continue;
281
+ const [added, removed, path16] = line.split(" ");
282
+ if (added === void 0 || removed === void 0 || path16 === void 0) continue;
283
283
  if (added === "-" || removed === "-") {
284
284
  continue;
285
285
  }
@@ -288,7 +288,7 @@ ${body}` : subject;
288
288
  totalLinesAdded += linesAdded;
289
289
  totalLinesRemoved += linesRemoved;
290
290
  files.push({
291
- path: path12,
291
+ path: path16,
292
292
  linesAdded,
293
293
  linesRemoved,
294
294
  changeType: "modified"
@@ -314,15 +314,15 @@ ${body}` : subject;
314
314
  if (!output) {
315
315
  return null;
316
316
  }
317
- const [added, removed, path12] = output.split(" ");
318
- if (added === void 0 || removed === void 0 || path12 === void 0) {
317
+ const [added, removed, path16] = output.split(" ");
318
+ if (added === void 0 || removed === void 0 || path16 === void 0) {
319
319
  return null;
320
320
  }
321
321
  if (added === "-" || removed === "-") {
322
322
  return null;
323
323
  }
324
324
  return {
325
- path: path12,
325
+ path: path16,
326
326
  linesAdded: parseInt(added, 10),
327
327
  linesRemoved: parseInt(removed, 10),
328
328
  changeType: "modified"
@@ -493,8 +493,8 @@ var init_git_service = __esm({
493
493
  }).trim();
494
494
  if (files) {
495
495
  taskFilePath = files.split("\n")[0] ?? null;
496
- const { readFileSync: readFileSync4 } = await import("fs");
497
- taskFileContent = readFileSync4(`${this.cwd}/${taskFilePath}`, "utf-8");
496
+ const { readFileSync: readFileSync7 } = await import("fs");
497
+ taskFileContent = readFileSync7(`${this.cwd}/${taskFilePath}`, "utf-8");
498
498
  }
499
499
  } catch {
500
500
  }
@@ -514,8 +514,8 @@ var init_git_service = __esm({
514
514
  });
515
515
  let committed = false;
516
516
  if (taskFileContent && taskFilePath) {
517
- const { writeFileSync: writeFileSync3 } = await import("fs");
518
- writeFileSync3(`${this.cwd}/${taskFilePath}`, taskFileContent, "utf-8");
517
+ const { writeFileSync: writeFileSync4 } = await import("fs");
518
+ writeFileSync4(`${this.cwd}/${taskFilePath}`, taskFileContent, "utf-8");
519
519
  const message = buildTaskStatusCommitMessage({ taskId, status, ciSkipTag: this.ciSkipTag });
520
520
  committed = await this.addAndCommit(taskPattern, message);
521
521
  } else {
@@ -2737,6 +2737,610 @@ var init_src3 = __esm({
2737
2737
  }
2738
2738
  });
2739
2739
 
2740
+ // node_modules/@opentask/taskin-task-server-mcp/node_modules/@opentask/taskin-task-manager/dist/filter-tasks/filter-tasks.js
2741
+ function casaResponsavel2(task, procurado) {
2742
+ const alvo = procurado.toLowerCase();
2743
+ return contem2(task.assignee?.id, alvo) || contem2(task.assignee?.name, alvo);
2744
+ }
2745
+ function filterTasks2(tasks, criteria) {
2746
+ return tasks.filter((task) => {
2747
+ if (criteria.status !== void 0) {
2748
+ if (task.status !== criteria.status)
2749
+ return false;
2750
+ } else if (criteria.open && !EM_ABERTO2.includes(task.status)) {
2751
+ return false;
2752
+ } else if (criteria.closed && !ENCERRADOS2.includes(task.status)) {
2753
+ return false;
2754
+ }
2755
+ if (criteria.type !== void 0 && task.type !== criteria.type)
2756
+ return false;
2757
+ if (criteria.assignee !== void 0 && !casaResponsavel2(task, criteria.assignee))
2758
+ return false;
2759
+ if (criteria.text !== void 0) {
2760
+ const procurado = criteria.text.toLowerCase();
2761
+ const casa = task.id.toLowerCase().includes(procurado) || contem2(task.title, procurado) || contem2(task.status, procurado) || casaResponsavel2(task, procurado);
2762
+ if (!casa)
2763
+ return false;
2764
+ }
2765
+ return true;
2766
+ });
2767
+ }
2768
+ function summarizeTask2(task) {
2769
+ return {
2770
+ id: task.id,
2771
+ title: task.title,
2772
+ status: task.status,
2773
+ type: task.type,
2774
+ ...task.assignee && { assignee: { id: task.assignee.id, name: task.assignee.name } },
2775
+ ...task.order !== void 0 && { priority: task.order },
2776
+ ...task.groupId && { groupId: task.groupId },
2777
+ ...task.groupName && { groupName: task.groupName },
2778
+ ...task.difficulty !== void 0 && { difficulty: task.difficulty }
2779
+ };
2780
+ }
2781
+ var EM_ABERTO2, ENCERRADOS2, contem2;
2782
+ var init_filter_tasks = __esm({
2783
+ "node_modules/@opentask/taskin-task-server-mcp/node_modules/@opentask/taskin-task-manager/dist/filter-tasks/filter-tasks.js"() {
2784
+ "use strict";
2785
+ init_esm_shims();
2786
+ EM_ABERTO2 = ["pending", "in-progress", "paused", "in-review", "blocked"];
2787
+ ENCERRADOS2 = ["done", "canceled"];
2788
+ contem2 = (valor, procurado) => valor?.toLowerCase().includes(procurado) ?? false;
2789
+ }
2790
+ });
2791
+
2792
+ // node_modules/@opentask/taskin-task-server-mcp/node_modules/@opentask/taskin-task-manager/dist/filter-tasks/index.js
2793
+ var init_filter_tasks2 = __esm({
2794
+ "node_modules/@opentask/taskin-task-server-mcp/node_modules/@opentask/taskin-task-manager/dist/filter-tasks/index.js"() {
2795
+ "use strict";
2796
+ init_esm_shims();
2797
+ init_filter_tasks();
2798
+ }
2799
+ });
2800
+
2801
+ // node_modules/@opentask/taskin-task-server-mcp/node_modules/@opentask/taskin-task-manager/dist/metrics.types.js
2802
+ var init_metrics_types = __esm({
2803
+ "node_modules/@opentask/taskin-task-server-mcp/node_modules/@opentask/taskin-task-manager/dist/metrics.types.js"() {
2804
+ "use strict";
2805
+ init_esm_shims();
2806
+ }
2807
+ });
2808
+
2809
+ // node_modules/@opentask/taskin-task-server-mcp/node_modules/@opentask/taskin-task-manager/dist/task-manager.js
2810
+ var init_task_manager = __esm({
2811
+ "node_modules/@opentask/taskin-task-server-mcp/node_modules/@opentask/taskin-task-manager/dist/task-manager.js"() {
2812
+ "use strict";
2813
+ init_esm_shims();
2814
+ }
2815
+ });
2816
+
2817
+ // node_modules/@opentask/taskin-task-server-mcp/node_modules/@opentask/taskin-task-manager/dist/task-manager.types.js
2818
+ var init_task_manager_types = __esm({
2819
+ "node_modules/@opentask/taskin-task-server-mcp/node_modules/@opentask/taskin-task-manager/dist/task-manager.types.js"() {
2820
+ "use strict";
2821
+ init_esm_shims();
2822
+ }
2823
+ });
2824
+
2825
+ // node_modules/@opentask/taskin-task-server-mcp/node_modules/@opentask/taskin-task-manager/dist/user-registry.types.js
2826
+ var init_user_registry_types = __esm({
2827
+ "node_modules/@opentask/taskin-task-server-mcp/node_modules/@opentask/taskin-task-manager/dist/user-registry.types.js"() {
2828
+ "use strict";
2829
+ init_esm_shims();
2830
+ }
2831
+ });
2832
+
2833
+ // node_modules/@opentask/taskin-task-server-mcp/node_modules/@opentask/taskin-task-manager/dist/index.js
2834
+ var init_dist = __esm({
2835
+ "node_modules/@opentask/taskin-task-server-mcp/node_modules/@opentask/taskin-task-manager/dist/index.js"() {
2836
+ "use strict";
2837
+ init_esm_shims();
2838
+ init_filter_tasks2();
2839
+ init_metrics_types();
2840
+ init_task_manager();
2841
+ init_task_manager_types();
2842
+ init_user_registry_types();
2843
+ }
2844
+ });
2845
+
2846
+ // node_modules/@opentask/taskin-task-server-mcp/dist/task-server-mcp.js
2847
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2848
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
2849
+ import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema } from "@modelcontextprotocol/sdk/types.js";
2850
+ import { TaskIdSchema as TaskIdSchema4 } from "@opentask/taskin-types";
2851
+ function readTaskId(raw) {
2852
+ if (typeof raw !== "string")
2853
+ return void 0;
2854
+ const parsed = TaskIdSchema4.safeParse(raw);
2855
+ return parsed.success ? parsed.data : void 0;
2856
+ }
2857
+ function invalidTaskId(raw) {
2858
+ return {
2859
+ content: [
2860
+ {
2861
+ type: "text",
2862
+ text: `Invalid task id: ${JSON.stringify(raw)}. Expected the numeric id of a task, e.g. "020".`
2863
+ }
2864
+ ],
2865
+ isError: true
2866
+ };
2867
+ }
2868
+ async function createTaskMCPServer(taskManager, options) {
2869
+ const server = new TaskMCPServer({
2870
+ taskManager,
2871
+ ...options
2872
+ });
2873
+ await server.connect({ transport: "stdio" });
2874
+ return server;
2875
+ }
2876
+ var TaskMCPServer;
2877
+ var init_task_server_mcp = __esm({
2878
+ "node_modules/@opentask/taskin-task-server-mcp/dist/task-server-mcp.js"() {
2879
+ "use strict";
2880
+ init_esm_shims();
2881
+ init_dist();
2882
+ TaskMCPServer = class _TaskMCPServer {
2883
+ server;
2884
+ taskManager;
2885
+ config;
2886
+ constructor(config) {
2887
+ this.taskManager = config.taskManager;
2888
+ this.config = {
2889
+ name: "taskin-mcp-server",
2890
+ version: "1.0.0",
2891
+ debug: false,
2892
+ ...config,
2893
+ taskManager: config.taskManager
2894
+ };
2895
+ this.server = new Server({
2896
+ name: this.config.name,
2897
+ version: this.config.version
2898
+ }, {
2899
+ capabilities: {
2900
+ tools: {},
2901
+ prompts: {},
2902
+ resources: {}
2903
+ }
2904
+ });
2905
+ this.setupHandlers();
2906
+ }
2907
+ /**
2908
+ * Setup MCP request handlers
2909
+ */
2910
+ setupHandlers() {
2911
+ this.server.setRequestHandler(ListToolsRequestSchema, async () => {
2912
+ const { tools } = this.listTools();
2913
+ return { tools };
2914
+ });
2915
+ this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
2916
+ const result = await this.callTool({
2917
+ name: request.params.name,
2918
+ arguments: request.params.arguments
2919
+ });
2920
+ return {
2921
+ content: result.content,
2922
+ isError: result.isError
2923
+ };
2924
+ });
2925
+ this.server.setRequestHandler(ListPromptsRequestSchema, async () => {
2926
+ const { prompts } = this.listPrompts();
2927
+ return { prompts };
2928
+ });
2929
+ this.server.setRequestHandler(GetPromptRequestSchema, async (request) => {
2930
+ const result = await this.getPrompt({
2931
+ name: request.params.name,
2932
+ arguments: request.params.arguments
2933
+ });
2934
+ return {
2935
+ messages: result.messages.map((msg) => ({
2936
+ role: msg.role,
2937
+ content: {
2938
+ type: "text",
2939
+ text: msg.content
2940
+ }
2941
+ }))
2942
+ };
2943
+ });
2944
+ this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
2945
+ const result = await this.listResources();
2946
+ return { resources: result.resources };
2947
+ });
2948
+ this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
2949
+ const result = await this.readResource({
2950
+ uri: request.params.uri
2951
+ });
2952
+ return result;
2953
+ });
2954
+ }
2955
+ /**
2956
+ * Connect to MCP transport
2957
+ *
2958
+ * `MCPTransportType` tem um valor so, entao nao ha o que despachar. O ramo
2959
+ * que existia aqui aceitava `'sse'` pelo tipo e recusava em tempo de
2960
+ * execucao, ja com o provider inicializado.
2961
+ */
2962
+ async connect(_options) {
2963
+ await this.server.connect(new StdioServerTransport());
2964
+ this.log("MCP Server connected via stdio");
2965
+ }
2966
+ /**
2967
+ * Get server information
2968
+ */
2969
+ getServerInfo() {
2970
+ return {
2971
+ name: this.config.name,
2972
+ version: this.config.version,
2973
+ protocolVersion: "2024-11-05",
2974
+ capabilities: {
2975
+ tools: {},
2976
+ prompts: {},
2977
+ resources: {}
2978
+ }
2979
+ };
2980
+ }
2981
+ /**
2982
+ * List available tools
2983
+ */
2984
+ listTools() {
2985
+ const tools = [
2986
+ {
2987
+ name: "list_tasks",
2988
+ description: "List the tasks in the project. Returns a JSON array with what identifies each task \u2014 id, title, status, type, assignee \u2014 without the markdown body. Fetch a task body by id after choosing one.",
2989
+ inputSchema: {
2990
+ type: "object",
2991
+ properties: {
2992
+ status: { type: "string", description: "Exact status (pending, in-progress, done, ...)" },
2993
+ type: { type: "string", description: "Exact type (feat, fix, chore, ...)" },
2994
+ assignee: { type: "string", description: "Assignee id or name, whole or in part" },
2995
+ open: { type: "boolean", description: "Only tasks still open" },
2996
+ closed: { type: "boolean", description: "Only tasks already closed" },
2997
+ text: { type: "string", description: "Free text over id, title, status and assignee" }
2998
+ },
2999
+ required: []
3000
+ }
3001
+ },
3002
+ {
3003
+ name: "start_task",
3004
+ description: "Start working on a task by changing its status to in-progress",
3005
+ inputSchema: {
3006
+ type: "object",
3007
+ properties: {
3008
+ taskId: {
3009
+ type: "string",
3010
+ description: "The unique identifier of the task (UUID)"
3011
+ }
3012
+ },
3013
+ required: ["taskId"]
3014
+ }
3015
+ },
3016
+ {
3017
+ name: "finish_task",
3018
+ description: "Mark a task as completed by changing its status to done",
3019
+ inputSchema: {
3020
+ type: "object",
3021
+ properties: {
3022
+ taskId: {
3023
+ type: "string",
3024
+ description: "The unique identifier of the task (UUID)"
3025
+ }
3026
+ },
3027
+ required: ["taskId"]
3028
+ }
3029
+ }
3030
+ ];
3031
+ return { tools };
3032
+ }
3033
+ /**
3034
+ * Call a tool
3035
+ */
3036
+ async callTool(params) {
3037
+ try {
3038
+ this.log(`Calling tool: ${params.name}`, params.arguments);
3039
+ switch (params.name) {
3040
+ case "list_tasks":
3041
+ return await this.handleListTasks(params.arguments ?? {});
3042
+ case "start_task": {
3043
+ const taskId = readTaskId(params.arguments?.taskId);
3044
+ return taskId ? await this.handleStartTask(taskId) : invalidTaskId(params.arguments?.taskId);
3045
+ }
3046
+ case "finish_task": {
3047
+ const taskId = readTaskId(params.arguments?.taskId);
3048
+ return taskId ? await this.handleFinishTask(taskId) : invalidTaskId(params.arguments?.taskId);
3049
+ }
3050
+ default:
3051
+ return {
3052
+ content: [
3053
+ {
3054
+ type: "text",
3055
+ text: `Unknown tool: ${params.name}`
3056
+ }
3057
+ ],
3058
+ isError: true
3059
+ };
3060
+ }
3061
+ } catch (error2) {
3062
+ this.log("Tool call error:", error2);
3063
+ return {
3064
+ content: [
3065
+ {
3066
+ type: "text",
3067
+ text: error2 instanceof Error ? error2.message : String(error2)
3068
+ }
3069
+ ],
3070
+ isError: true
3071
+ };
3072
+ }
3073
+ }
3074
+ /**
3075
+ * Handle start_task tool
3076
+ */
3077
+ async handleStartTask(taskId) {
3078
+ const task = await this.taskManager.startTask(taskId);
3079
+ return {
3080
+ content: [
3081
+ {
3082
+ type: "text",
3083
+ text: JSON.stringify({
3084
+ success: true,
3085
+ message: `Task ${taskId} started successfully`,
3086
+ task: {
3087
+ id: task.id,
3088
+ title: task.title,
3089
+ status: task.status,
3090
+ type: task.type
3091
+ }
3092
+ }, null, 2)
3093
+ }
3094
+ ]
3095
+ };
3096
+ }
3097
+ /**
3098
+ * Handle finish_task tool
3099
+ */
3100
+ async handleFinishTask(taskId) {
3101
+ const task = await this.taskManager.finishTask(taskId);
3102
+ return {
3103
+ content: [
3104
+ {
3105
+ type: "text",
3106
+ text: JSON.stringify({
3107
+ success: true,
3108
+ message: `Task ${taskId} finished successfully`,
3109
+ task: {
3110
+ id: task.id,
3111
+ title: task.title,
3112
+ status: task.status,
3113
+ type: task.type
3114
+ }
3115
+ }, null, 2)
3116
+ }
3117
+ ]
3118
+ };
3119
+ }
3120
+ /**
3121
+ * List available prompts
3122
+ */
3123
+ listPrompts() {
3124
+ const prompts = [
3125
+ {
3126
+ name: "start-task-workflow",
3127
+ description: "Guide the user through starting work on a task with git branch creation",
3128
+ arguments: [
3129
+ {
3130
+ name: "taskId",
3131
+ description: "The task ID to start",
3132
+ required: true
3133
+ }
3134
+ ]
3135
+ },
3136
+ {
3137
+ name: "finish-task-workflow",
3138
+ description: "Guide the user through completing a task with commit and PR creation",
3139
+ arguments: [
3140
+ {
3141
+ name: "taskId",
3142
+ description: "The task ID to finish",
3143
+ required: true
3144
+ }
3145
+ ]
3146
+ },
3147
+ {
3148
+ name: "task-summary",
3149
+ description: "Generate a summary of a task for documentation or reports",
3150
+ arguments: [
3151
+ {
3152
+ name: "taskId",
3153
+ description: "The task ID to summarize",
3154
+ required: true
3155
+ }
3156
+ ]
3157
+ }
3158
+ ];
3159
+ return { prompts };
3160
+ }
3161
+ /**
3162
+ * Get a prompt
3163
+ */
3164
+ async getPrompt(params) {
3165
+ this.log(`Getting prompt: ${params.name}`, params.arguments);
3166
+ switch (params.name) {
3167
+ case "start-task-workflow":
3168
+ return {
3169
+ description: "Workflow for starting a task",
3170
+ messages: [
3171
+ {
3172
+ role: "user",
3173
+ content: {
3174
+ type: "text",
3175
+ text: `I want to start working on task ${params.arguments?.taskId}. Can you help me set everything up?`
3176
+ }
3177
+ },
3178
+ {
3179
+ role: "assistant",
3180
+ content: {
3181
+ type: "text",
3182
+ text: `I'll help you start working on task ${params.arguments?.taskId}. Here's what I'll do:
3183
+
3184
+ 1. Mark the task as "in-progress"
3185
+ 2. Create a git branch based on the task type and ID
3186
+ 3. Ensure your working directory is clean
3187
+ 4. Switch to the new branch
3188
+
3189
+ Let me start by marking the task as in-progress using the start_task tool.`
3190
+ }
3191
+ }
3192
+ ]
3193
+ };
3194
+ case "finish-task-workflow":
3195
+ return {
3196
+ description: "Workflow for finishing a task",
3197
+ messages: [
3198
+ {
3199
+ role: "user",
3200
+ content: {
3201
+ type: "text",
3202
+ text: `I've completed task ${params.arguments?.taskId}. What should I do next?`
3203
+ }
3204
+ },
3205
+ {
3206
+ role: "assistant",
3207
+ content: {
3208
+ type: "text",
3209
+ text: `Great! Let me help you finish task ${params.arguments?.taskId}. Here's the workflow:
3210
+
3211
+ 1. Mark the task as "done"
3212
+ 2. Ensure all changes are committed
3213
+ 3. Push your branch to remote
3214
+ 4. Create a pull request
3215
+
3216
+ Let me start by marking the task as done using the finish_task tool.`
3217
+ }
3218
+ }
3219
+ ]
3220
+ };
3221
+ case "task-summary":
3222
+ return {
3223
+ description: "Generate task summary",
3224
+ messages: [
3225
+ {
3226
+ role: "user",
3227
+ content: {
3228
+ type: "text",
3229
+ text: `Can you provide a summary of task ${params.arguments?.taskId}?`
3230
+ }
3231
+ },
3232
+ {
3233
+ role: "assistant",
3234
+ content: {
3235
+ type: "text",
3236
+ text: `I'll generate a comprehensive summary of task ${params.arguments?.taskId} including its current status, description, and any relevant metadata.`
3237
+ }
3238
+ }
3239
+ ]
3240
+ };
3241
+ default:
3242
+ throw new Error(`Unknown prompt: ${params.name}`);
3243
+ }
3244
+ }
3245
+ /**
3246
+ * Le e seleciona as tarefas, pela mesma seam que o CLI usa.
3247
+ *
3248
+ * `filterTasks` e `summarizeTask` vivem no pacote agnostico justamente para
3249
+ * que a resposta aqui e a de `taskin list --json` nao possam divergir — ja
3250
+ * houve duas filtragens discordando no repositorio.
3251
+ */
3252
+ async selecionarTarefas(criteria) {
3253
+ const tasks = await this.taskManager.getAllTasks();
3254
+ return filterTasks2(tasks, criteria).map(summarizeTask2);
3255
+ }
3256
+ /** Converte os argumentos crus da chamada MCP no criterio tipado. */
3257
+ static criterioDe(args) {
3258
+ return {
3259
+ ...typeof args.status === "string" && { status: args.status },
3260
+ ...typeof args.type === "string" && { type: args.type },
3261
+ ...typeof args.assignee === "string" && { assignee: args.assignee },
3262
+ ...args.open === true && { open: true },
3263
+ ...args.closed === true && { closed: true },
3264
+ ...typeof args.text === "string" && { text: args.text }
3265
+ };
3266
+ }
3267
+ async handleListTasks(args) {
3268
+ const tarefas = await this.selecionarTarefas(_TaskMCPServer.criterioDe(args));
3269
+ return {
3270
+ content: [{ type: "text", text: JSON.stringify(tarefas, null, 2) }],
3271
+ isError: false
3272
+ };
3273
+ }
3274
+ /**
3275
+ * List available resources
3276
+ */
3277
+ async listResources() {
3278
+ return {
3279
+ resources: [
3280
+ {
3281
+ uri: "taskin://tasks",
3282
+ name: "All Tasks",
3283
+ description: "Access to all tasks in the system",
3284
+ mimeType: "application/json"
3285
+ }
3286
+ ]
3287
+ };
3288
+ }
3289
+ /**
3290
+ * Read a resource
3291
+ */
3292
+ async readResource(params) {
3293
+ this.log(`Reading resource: ${params.uri}`);
3294
+ const uri = params.uri;
3295
+ if (uri === "taskin://tasks") {
3296
+ const tarefas = await this.selecionarTarefas({});
3297
+ return {
3298
+ contents: [
3299
+ {
3300
+ uri,
3301
+ mimeType: "application/json",
3302
+ text: JSON.stringify(tarefas, null, 2)
3303
+ }
3304
+ ]
3305
+ };
3306
+ }
3307
+ throw new Error(`Unknown resource: ${uri}`);
3308
+ }
3309
+ /**
3310
+ * Debug logging
3311
+ */
3312
+ log(...args) {
3313
+ if (this.config.debug) {
3314
+ console.error("[TaskMCPServer]", ...args);
3315
+ }
3316
+ }
3317
+ };
3318
+ }
3319
+ });
3320
+
3321
+ // node_modules/@opentask/taskin-task-server-mcp/dist/task-server-mcp.types.js
3322
+ var init_task_server_mcp_types = __esm({
3323
+ "node_modules/@opentask/taskin-task-server-mcp/dist/task-server-mcp.types.js"() {
3324
+ "use strict";
3325
+ init_esm_shims();
3326
+ }
3327
+ });
3328
+
3329
+ // node_modules/@opentask/taskin-task-server-mcp/dist/index.js
3330
+ var dist_exports = {};
3331
+ __export(dist_exports, {
3332
+ TaskMCPServer: () => TaskMCPServer,
3333
+ createTaskMCPServer: () => createTaskMCPServer
3334
+ });
3335
+ var init_dist2 = __esm({
3336
+ "node_modules/@opentask/taskin-task-server-mcp/dist/index.js"() {
3337
+ "use strict";
3338
+ init_esm_shims();
3339
+ init_task_server_mcp();
3340
+ init_task_server_mcp_types();
3341
+ }
3342
+ });
3343
+
2740
3344
  // src/index.ts
2741
3345
  init_esm_shims();
2742
3346
  import { Command } from "commander";
@@ -3463,7 +4067,7 @@ init_esm_shims();
3463
4067
  init_esm_shims();
3464
4068
  var EM_ABERTO = ["pending", "in-progress", "paused", "in-review", "blocked"];
3465
4069
  var ENCERRADOS = ["done", "canceled"];
3466
- var contem = (valor, procurado) => valor !== void 0 && valor.toLowerCase().includes(procurado);
4070
+ var contem = (valor, procurado) => valor?.toLowerCase().includes(procurado) ?? false;
3467
4071
  function casaResponsavel(task, procurado) {
3468
4072
  const alvo = procurado.toLowerCase();
3469
4073
  return contem(task.assignee?.id, alvo) || contem(task.assignee?.name, alvo);
@@ -4550,6 +5154,15 @@ init_src3();
4550
5154
  init_src();
4551
5155
  import { execSync as execSync5 } from "child_process";
4552
5156
 
5157
+ // src/lib/ci-skip-tag/index.ts
5158
+ init_esm_shims();
5159
+
5160
+ // src/lib/ci-skip-tag/resolve-ci-skip-tag.ts
5161
+ init_esm_shims();
5162
+ function resolveCiSkipTag(configurada, skipCi) {
5163
+ return skipCi === false ? "" : configurada;
5164
+ }
5165
+
4553
5166
  // src/lib/notification/notify-helper.ts
4554
5167
  init_esm_shims();
4555
5168
  import { execSync as execSync4 } from "child_process";
@@ -4946,6 +5559,10 @@ var finishCommand = defineCommand({
4946
5559
  {
4947
5560
  flags: "--dry-run",
4948
5561
  description: "Show what would be executed without running"
5562
+ },
5563
+ {
5564
+ flags: "--no-skip-ci",
5565
+ description: "Write the status commit without the CI-skip tag"
4949
5566
  }
4950
5567
  ],
4951
5568
  handler: async (taskId, options) => {
@@ -4971,11 +5588,12 @@ async function finishTask(taskId, options, gitService) {
4971
5588
  info(`Current status: ${task.status}`);
4972
5589
  const configManager = new ConfigManager(monorepoRoot);
4973
5590
  const behavior = configManager.getAutomationBehavior();
5591
+ const ciSkipTag = resolveCiSkipTag(behavior.ciSkipTag, options.skipCi);
4974
5592
  const autoSyncActive = behavior.autoSync && !!behavior.defaultBranch;
4975
5593
  const statusCommitMessage = buildTaskStatusCommitMessage({
4976
5594
  taskId: normalizedId,
4977
5595
  status: "done",
4978
- ciSkipTag: behavior.ciSkipTag
5596
+ ciSkipTag
4979
5597
  });
4980
5598
  if (options.dryRun) {
4981
5599
  console.log();
@@ -5008,7 +5626,7 @@ async function finishTask(taskId, options, gitService) {
5008
5626
  if (behavior.autoSync && !behavior.defaultBranch) {
5009
5627
  warning("autoSync is enabled but no defaultBranch is configured. Nothing will be synced.");
5010
5628
  }
5011
- const git = gitService ?? new GitService(process.cwd(), { ciSkipTag: behavior.ciSkipTag });
5629
+ const git = gitService ?? new GitService(process.cwd(), { ciSkipTag });
5012
5630
  if (!options.skipUpdate) {
5013
5631
  info("Marking task as done...");
5014
5632
  const updatedTask = await taskManager.finishTask(task.id);
@@ -5026,7 +5644,7 @@ async function finishTask(taskId, options, gitService) {
5026
5644
  taskId: normalizedId,
5027
5645
  defaultBranch: behavior.defaultBranch,
5028
5646
  originBranch: behavior.originBranch,
5029
- ciSkipTag: behavior.ciSkipTag
5647
+ ciSkipTag
5030
5648
  });
5031
5649
  if (squashed) {
5032
5650
  success(`Squash commit pushed to ${behavior.originBranch}`);
@@ -5267,7 +5885,7 @@ async function initializeTaskin(options) {
5267
5885
  await ensureProviderInstalled(selectedProvider);
5268
5886
  }
5269
5887
  const providerConfig = await setupProviderConfig(selectedProvider, cwd);
5270
- const ciSkipTag = await resolveCiSkipTag(options.ciSkipTag);
5888
+ const ciSkipTag = await resolveCiSkipTag2(options.ciSkipTag);
5271
5889
  const config = {
5272
5890
  version: "1.0.3",
5273
5891
  automation: {
@@ -5301,7 +5919,7 @@ async function initializeTaskin(options) {
5301
5919
  info("For more information, run: taskin --help");
5302
5920
  console.log();
5303
5921
  }
5304
- async function resolveCiSkipTag(fromFlag) {
5922
+ async function resolveCiSkipTag2(fromFlag) {
5305
5923
  if (fromFlag !== void 0) {
5306
5924
  return normalizeCiSkipTag(fromFlag);
5307
5925
  }
@@ -5674,554 +6292,324 @@ function getTypeColor(type) {
5674
6292
  }
5675
6293
  }
5676
6294
 
5677
- // src/commands/mcp-server.ts
5678
- init_esm_shims();
5679
-
5680
- // node_modules/@opentask/taskin-task-server-mcp/dist/index.js
5681
- init_esm_shims();
5682
-
5683
- // node_modules/@opentask/taskin-task-server-mcp/dist/task-server-mcp.js
5684
- init_esm_shims();
5685
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
5686
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5687
- import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema } from "@modelcontextprotocol/sdk/types.js";
5688
-
5689
- // node_modules/@opentask/taskin-task-server-mcp/node_modules/@opentask/taskin-task-manager/dist/index.js
6295
+ // src/commands/mcp-install.ts
5690
6296
  init_esm_shims();
6297
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
6298
+ import path14 from "path";
5691
6299
 
5692
- // node_modules/@opentask/taskin-task-server-mcp/node_modules/@opentask/taskin-task-manager/dist/filter-tasks/index.js
6300
+ // src/lib/mcp-install/index.ts
5693
6301
  init_esm_shims();
5694
6302
 
5695
- // node_modules/@opentask/taskin-task-server-mcp/node_modules/@opentask/taskin-task-manager/dist/filter-tasks/filter-tasks.js
6303
+ // src/lib/mcp-install/merge-mcp-config.ts
5696
6304
  init_esm_shims();
5697
- var EM_ABERTO2 = ["pending", "in-progress", "paused", "in-review", "blocked"];
5698
- var ENCERRADOS2 = ["done", "canceled"];
5699
- var contem2 = (valor, procurado) => valor !== void 0 && valor.toLowerCase().includes(procurado);
5700
- function casaResponsavel2(task, procurado) {
5701
- const alvo = procurado.toLowerCase();
5702
- return contem2(task.assignee?.id, alvo) || contem2(task.assignee?.name, alvo);
5703
- }
5704
- function filterTasks2(tasks, criteria) {
5705
- return tasks.filter((task) => {
5706
- if (criteria.status !== void 0) {
5707
- if (task.status !== criteria.status)
5708
- return false;
5709
- } else if (criteria.open && !EM_ABERTO2.includes(task.status)) {
5710
- return false;
5711
- } else if (criteria.closed && !ENCERRADOS2.includes(task.status)) {
5712
- return false;
6305
+ var NOME_DO_SERVIDOR = "taskin";
6306
+ var ehObjeto = (valor) => typeof valor === "object" && valor !== null && !Array.isArray(valor);
6307
+ var mesmaEntrada = (a, b) => JSON.stringify(a) === JSON.stringify(b);
6308
+ function mergeMcpConfig(existente, entrada, options = {}) {
6309
+ if (existente === void 0) {
6310
+ return { outcome: "created", config: { mcpServers: { [NOME_DO_SERVIDOR]: entrada } } };
6311
+ }
6312
+ let lido;
6313
+ try {
6314
+ lido = JSON.parse(existente);
6315
+ } catch {
6316
+ return { outcome: "unreadable", reason: "o arquivo n\xE3o \xE9 JSON v\xE1lido" };
6317
+ }
6318
+ if (!ehObjeto(lido)) {
6319
+ return { outcome: "unreadable", reason: "o conte\xFAdo n\xE3o \xE9 um objeto" };
6320
+ }
6321
+ const servidores = lido.mcpServers;
6322
+ if (servidores !== void 0 && !ehObjeto(servidores)) {
6323
+ return { outcome: "unreadable", reason: "`mcpServers` n\xE3o \xE9 um objeto" };
6324
+ }
6325
+ const atuais = ehObjeto(servidores) ? servidores : {};
6326
+ const atual = atuais[NOME_DO_SERVIDOR];
6327
+ if (atual !== void 0) {
6328
+ if (mesmaEntrada(atual, entrada)) {
6329
+ return { outcome: "unchanged" };
5713
6330
  }
5714
- if (criteria.type !== void 0 && task.type !== criteria.type)
5715
- return false;
5716
- if (criteria.assignee !== void 0 && !casaResponsavel2(task, criteria.assignee))
5717
- return false;
5718
- if (criteria.text !== void 0) {
5719
- const procurado = criteria.text.toLowerCase();
5720
- const casa = task.id.toLowerCase().includes(procurado) || contem2(task.title, procurado) || contem2(task.status, procurado) || casaResponsavel2(task, procurado);
5721
- if (!casa)
5722
- return false;
6331
+ if (!options.overwrite) {
6332
+ return { outcome: "conflict", current: atual };
5723
6333
  }
5724
- return true;
5725
- });
5726
- }
5727
- function summarizeTask2(task) {
5728
- return {
5729
- id: task.id,
5730
- title: task.title,
5731
- status: task.status,
5732
- type: task.type,
5733
- ...task.assignee && { assignee: { id: task.assignee.id, name: task.assignee.name } },
5734
- ...task.order !== void 0 && { priority: task.order },
5735
- ...task.groupId && { groupId: task.groupId },
5736
- ...task.groupName && { groupName: task.groupName },
5737
- ...task.difficulty !== void 0 && { difficulty: task.difficulty }
6334
+ }
6335
+ const config = {
6336
+ ...lido,
6337
+ mcpServers: { ...atuais, [NOME_DO_SERVIDOR]: entrada }
5738
6338
  };
6339
+ return atual === void 0 ? { outcome: "added", config } : { outcome: "replaced", config };
5739
6340
  }
5740
6341
 
5741
- // node_modules/@opentask/taskin-task-server-mcp/node_modules/@opentask/taskin-task-manager/dist/metrics.types.js
5742
- init_esm_shims();
5743
-
5744
- // node_modules/@opentask/taskin-task-server-mcp/node_modules/@opentask/taskin-task-manager/dist/task-manager.js
5745
- init_esm_shims();
5746
-
5747
- // node_modules/@opentask/taskin-task-server-mcp/node_modules/@opentask/taskin-task-manager/dist/task-manager.types.js
5748
- init_esm_shims();
5749
-
5750
- // node_modules/@opentask/taskin-task-server-mcp/node_modules/@opentask/taskin-task-manager/dist/user-registry.types.js
6342
+ // src/lib/mcp-install/package-manager.ts
5751
6343
  init_esm_shims();
5752
-
5753
- // node_modules/@opentask/taskin-task-server-mcp/dist/task-server-mcp.js
5754
- import { TaskIdSchema as TaskIdSchema4 } from "@opentask/taskin-types";
5755
- function readTaskId(raw) {
5756
- if (typeof raw !== "string")
5757
- return void 0;
5758
- const parsed = TaskIdSchema4.safeParse(raw);
5759
- return parsed.success ? parsed.data : void 0;
5760
- }
5761
- function invalidTaskId(raw) {
5762
- return {
5763
- content: [
5764
- {
5765
- type: "text",
5766
- text: `Invalid task id: ${JSON.stringify(raw)}. Expected the numeric id of a task, e.g. "020".`
5767
- }
5768
- ],
5769
- isError: true
5770
- };
5771
- }
5772
- var TaskMCPServer = class _TaskMCPServer {
5773
- server;
5774
- taskManager;
5775
- config;
5776
- constructor(config) {
5777
- this.taskManager = config.taskManager;
5778
- this.config = {
5779
- name: "taskin-mcp-server",
5780
- version: "1.0.0",
5781
- debug: false,
5782
- ...config,
5783
- taskManager: config.taskManager
5784
- };
5785
- this.server = new Server({
5786
- name: this.config.name,
5787
- version: this.config.version
5788
- }, {
5789
- capabilities: {
5790
- tools: {},
5791
- prompts: {},
5792
- resources: {}
5793
- }
5794
- });
5795
- this.setupHandlers();
5796
- }
5797
- /**
5798
- * Setup MCP request handlers
5799
- */
5800
- setupHandlers() {
5801
- this.server.setRequestHandler(ListToolsRequestSchema, async () => {
5802
- const { tools } = this.listTools();
5803
- return { tools };
5804
- });
5805
- this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
5806
- const result = await this.callTool({
5807
- name: request.params.name,
5808
- arguments: request.params.arguments
5809
- });
5810
- return {
5811
- content: result.content,
5812
- isError: result.isError
5813
- };
5814
- });
5815
- this.server.setRequestHandler(ListPromptsRequestSchema, async () => {
5816
- const { prompts } = this.listPrompts();
5817
- return { prompts };
5818
- });
5819
- this.server.setRequestHandler(GetPromptRequestSchema, async (request) => {
5820
- const result = await this.getPrompt({
5821
- name: request.params.name,
5822
- arguments: request.params.arguments
5823
- });
5824
- return {
5825
- messages: result.messages.map((msg) => ({
5826
- role: msg.role,
5827
- content: {
5828
- type: "text",
5829
- text: msg.content
5830
- }
5831
- }))
5832
- };
5833
- });
5834
- this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
5835
- const result = await this.listResources();
5836
- return { resources: result.resources };
5837
- });
5838
- this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
5839
- const result = await this.readResource({
5840
- uri: request.params.uri
5841
- });
5842
- return result;
5843
- });
6344
+ import { existsSync as existsSync6, readFileSync as readFileSync3 } from "fs";
6345
+ import path11 from "path";
6346
+ var POR_LOCKFILE = [
6347
+ ["pnpm-lock.yaml", "pnpm"],
6348
+ ["yarn.lock", "yarn"],
6349
+ ["bun.lockb", "bun"],
6350
+ ["package-lock.json", "npm"]
6351
+ ];
6352
+ function declarado(raiz) {
6353
+ try {
6354
+ const { packageManager } = JSON.parse(readFileSync3(path11.join(raiz, "package.json"), "utf-8"));
6355
+ if (typeof packageManager !== "string") return void 0;
6356
+ const nome = packageManager.split("@")[0];
6357
+ return POR_LOCKFILE.some(([, gerenciador]) => gerenciador === nome) ? nome : void 0;
6358
+ } catch {
6359
+ return void 0;
5844
6360
  }
5845
- /**
5846
- * Connect to MCP transport
5847
- */
5848
- async connect(options) {
5849
- if (options.transport === "stdio") {
5850
- const transport = new StdioServerTransport();
5851
- await this.server.connect(transport);
5852
- this.log("MCP Server connected via stdio");
5853
- } else {
5854
- throw new Error(`Transport ${options.transport} not yet implemented`);
5855
- }
6361
+ }
6362
+ function detectPackageManager2(raiz) {
6363
+ const explicito = declarado(raiz);
6364
+ if (explicito) return explicito;
6365
+ for (const [lockfile, gerenciador] of POR_LOCKFILE) {
6366
+ if (existsSync6(path11.join(raiz, lockfile))) return gerenciador;
5856
6367
  }
5857
- /**
5858
- * Get server information
5859
- */
5860
- getServerInfo() {
5861
- return {
5862
- name: this.config.name,
5863
- version: this.config.version,
5864
- protocolVersion: "2024-11-05",
5865
- capabilities: {
5866
- tools: {},
5867
- prompts: {},
5868
- resources: {}
5869
- }
5870
- };
6368
+ return "npm";
6369
+ }
6370
+ function mcpCommandFor(manager) {
6371
+ switch (manager) {
6372
+ case "pnpm":
6373
+ return { command: "pnpm", args: ["exec", "taskin", "mcp-server"] };
6374
+ case "yarn":
6375
+ return { command: "yarn", args: ["taskin", "mcp-server"] };
6376
+ case "bun":
6377
+ return { command: "bunx", args: ["taskin", "mcp-server"] };
6378
+ case "npm":
6379
+ return { command: "npx", args: ["taskin", "mcp-server"] };
5871
6380
  }
5872
- /**
5873
- * List available tools
5874
- */
5875
- listTools() {
5876
- const tools = [
5877
- {
5878
- name: "list_tasks",
5879
- description: "List the tasks in the project. Returns a JSON array with what identifies each task \u2014 id, title, status, type, assignee \u2014 without the markdown body. Fetch a task body by id after choosing one.",
5880
- inputSchema: {
5881
- type: "object",
5882
- properties: {
5883
- status: { type: "string", description: "Exact status (pending, in-progress, done, ...)" },
5884
- type: { type: "string", description: "Exact type (feat, fix, chore, ...)" },
5885
- assignee: { type: "string", description: "Assignee id or name, whole or in part" },
5886
- open: { type: "boolean", description: "Only tasks still open" },
5887
- closed: { type: "boolean", description: "Only tasks already closed" },
5888
- text: { type: "string", description: "Free text over id, title, status and assignee" }
5889
- },
5890
- required: []
5891
- }
5892
- },
5893
- {
5894
- name: "start_task",
5895
- description: "Start working on a task by changing its status to in-progress",
5896
- inputSchema: {
5897
- type: "object",
5898
- properties: {
5899
- taskId: {
5900
- type: "string",
5901
- description: "The unique identifier of the task (UUID)"
5902
- }
5903
- },
5904
- required: ["taskId"]
5905
- }
5906
- },
5907
- {
5908
- name: "finish_task",
5909
- description: "Mark a task as completed by changing its status to done",
5910
- inputSchema: {
5911
- type: "object",
5912
- properties: {
5913
- taskId: {
5914
- type: "string",
5915
- description: "The unique identifier of the task (UUID)"
5916
- }
5917
- },
5918
- required: ["taskId"]
6381
+ }
6382
+
6383
+ // src/lib/mcp-install/probe-server.ts
6384
+ init_esm_shims();
6385
+ import { spawn } from "child_process";
6386
+ var LIMITE_MS = 3e4;
6387
+ var requisicao = (id, method, params) => `${JSON.stringify({ jsonrpc: "2.0", id, method, ...params !== void 0 && { params } })}
6388
+ `;
6389
+ function probeMcpServer(entrada, cwd, esperadas = []) {
6390
+ return new Promise((resolve) => {
6391
+ const filho = spawn(entrada.command, [...entrada.args], {
6392
+ cwd,
6393
+ stdio: ["pipe", "pipe", "pipe"],
6394
+ env: { ...process.env }
6395
+ });
6396
+ let saida = "";
6397
+ let erro = "";
6398
+ let respondido = false;
6399
+ const encerrar = (resultado) => {
6400
+ if (respondido) return;
6401
+ respondido = true;
6402
+ clearTimeout(cronometro);
6403
+ filho.kill();
6404
+ resolve(resultado);
6405
+ };
6406
+ const cronometro = setTimeout(
6407
+ () => encerrar({ ok: false, reason: `o servidor n\xE3o respondeu em ${LIMITE_MS / 1e3}s`, stderr: erro.trim() }),
6408
+ LIMITE_MS
6409
+ );
6410
+ filho.on("error", (e) => encerrar({ ok: false, reason: e.message, stderr: erro.trim() }));
6411
+ filho.stderr.on("data", (pedaco) => {
6412
+ erro += pedaco.toString();
6413
+ });
6414
+ filho.stdout.on("data", (pedaco) => {
6415
+ saida += pedaco.toString();
6416
+ for (const linha2 of saida.split("\n")) {
6417
+ if (!linha2.trim()) continue;
6418
+ let mensagem;
6419
+ try {
6420
+ mensagem = JSON.parse(linha2);
6421
+ } catch {
6422
+ continue;
5919
6423
  }
5920
- }
5921
- ];
5922
- return { tools };
5923
- }
5924
- /**
5925
- * Call a tool
5926
- */
5927
- async callTool(params) {
5928
- try {
5929
- this.log(`Calling tool: ${params.name}`, params.arguments);
5930
- switch (params.name) {
5931
- case "list_tasks":
5932
- return await this.handleListTasks(params.arguments ?? {});
5933
- case "start_task": {
5934
- const taskId = readTaskId(params.arguments?.taskId);
5935
- return taskId ? await this.handleStartTask(taskId) : invalidTaskId(params.arguments?.taskId);
6424
+ if (mensagem.id !== 2) continue;
6425
+ if (mensagem.error) {
6426
+ encerrar({ ok: false, reason: `recusou tools/list: ${mensagem.error.message}`, stderr: erro.trim() });
6427
+ return;
5936
6428
  }
5937
- case "finish_task": {
5938
- const taskId = readTaskId(params.arguments?.taskId);
5939
- return taskId ? await this.handleFinishTask(taskId) : invalidTaskId(params.arguments?.taskId);
6429
+ const ferramentas = (mensagem.result?.tools ?? []).map((t) => t.name);
6430
+ if (ferramentas.length === 0) {
6431
+ encerrar({ ok: false, reason: "subiu, mas n\xE3o anuncia ferramenta nenhuma", stderr: erro.trim() });
6432
+ return;
5940
6433
  }
5941
- default:
5942
- return {
5943
- content: [
5944
- {
5945
- type: "text",
5946
- text: `Unknown tool: ${params.name}`
5947
- }
5948
- ],
5949
- isError: true
5950
- };
5951
- }
5952
- } catch (error2) {
5953
- this.log("Tool call error:", error2);
5954
- return {
5955
- content: [
5956
- {
5957
- type: "text",
5958
- text: error2 instanceof Error ? error2.message : String(error2)
6434
+ const faltando = esperadas.filter((nome) => !ferramentas.includes(nome));
6435
+ encerrar(
6436
+ faltando.length === 0 ? { ok: true, tools: ferramentas } : {
6437
+ ok: false,
6438
+ reason: `respondeu, mas sem ${faltando.join(", ")} \u2014 o comando provavelmente alcan\xE7a outra instala\xE7\xE3o do taskin (anunciou: ${ferramentas.join(", ")})`,
6439
+ stderr: erro.trim()
5959
6440
  }
5960
- ],
5961
- isError: true
5962
- };
5963
- }
6441
+ );
6442
+ return;
6443
+ }
6444
+ });
6445
+ filho.on(
6446
+ "close",
6447
+ (code) => encerrar({ ok: false, reason: `encerrou com c\xF3digo ${code} sem responder`, stderr: erro.trim() })
6448
+ );
6449
+ filho.stdin.write(
6450
+ requisicao(1, "initialize", {
6451
+ protocolVersion: "2024-11-05",
6452
+ capabilities: {},
6453
+ clientInfo: { name: "taskin-install", version: "1" }
6454
+ })
6455
+ );
6456
+ filho.stdin.write(requisicao(2, "tools/list"));
6457
+ });
6458
+ }
6459
+
6460
+ // src/lib/mcp-install/resolve-invocation.ts
6461
+ init_esm_shims();
6462
+ import { existsSync as existsSync7, readFileSync as readFileSync4 } from "fs";
6463
+ import path12 from "path";
6464
+ function viaScript(manager) {
6465
+ if (manager === "npm") {
6466
+ return { command: "npm", args: ["run", "taskin", "--", "mcp-server"] };
5964
6467
  }
5965
- /**
5966
- * Handle start_task tool
5967
- */
5968
- async handleStartTask(taskId) {
5969
- const task = await this.taskManager.startTask(taskId);
5970
- return {
5971
- content: [
5972
- {
5973
- type: "text",
5974
- text: JSON.stringify({
5975
- success: true,
5976
- message: `Task ${taskId} started successfully`,
5977
- task: {
5978
- id: task.id,
5979
- title: task.title,
5980
- status: task.status,
5981
- type: task.type
5982
- }
5983
- }, null, 2)
5984
- }
5985
- ]
5986
- };
6468
+ return { command: manager, args: ["taskin", "mcp-server"] };
6469
+ }
6470
+ function temScriptTaskin(raiz) {
6471
+ try {
6472
+ const { scripts } = JSON.parse(readFileSync4(path12.join(raiz, "package.json"), "utf-8"));
6473
+ return typeof scripts?.taskin === "string";
6474
+ } catch {
6475
+ return false;
5987
6476
  }
5988
- /**
5989
- * Handle finish_task tool
5990
- */
5991
- async handleFinishTask(taskId) {
5992
- const task = await this.taskManager.finishTask(taskId);
5993
- return {
5994
- content: [
5995
- {
5996
- type: "text",
5997
- text: JSON.stringify({
5998
- success: true,
5999
- message: `Task ${taskId} finished successfully`,
6000
- task: {
6001
- id: task.id,
6002
- title: task.title,
6003
- status: task.status,
6004
- type: task.type
6005
- }
6006
- }, null, 2)
6007
- }
6008
- ]
6009
- };
6477
+ }
6478
+ function resolveInvocation(raiz, manager) {
6479
+ if (existsSync7(path12.join(raiz, "node_modules", ".bin", "taskin"))) {
6480
+ return mcpCommandFor(manager);
6010
6481
  }
6011
- /**
6012
- * List available prompts
6013
- */
6014
- listPrompts() {
6015
- const prompts = [
6016
- {
6017
- name: "start-task-workflow",
6018
- description: "Guide the user through starting work on a task with git branch creation",
6019
- arguments: [
6020
- {
6021
- name: "taskId",
6022
- description: "The task ID to start",
6023
- required: true
6024
- }
6025
- ]
6026
- },
6027
- {
6028
- name: "finish-task-workflow",
6029
- description: "Guide the user through completing a task with commit and PR creation",
6030
- arguments: [
6031
- {
6032
- name: "taskId",
6033
- description: "The task ID to finish",
6034
- required: true
6035
- }
6036
- ]
6037
- },
6038
- {
6039
- name: "task-summary",
6040
- description: "Generate a summary of a task for documentation or reports",
6041
- arguments: [
6042
- {
6043
- name: "taskId",
6044
- description: "The task ID to summarize",
6045
- required: true
6046
- }
6047
- ]
6048
- }
6049
- ];
6050
- return { prompts };
6482
+ if (temScriptTaskin(raiz)) {
6483
+ return viaScript(manager);
6051
6484
  }
6052
- /**
6053
- * Get a prompt
6054
- */
6055
- async getPrompt(params) {
6056
- this.log(`Getting prompt: ${params.name}`, params.arguments);
6057
- switch (params.name) {
6058
- case "start-task-workflow":
6059
- return {
6060
- description: "Workflow for starting a task",
6061
- messages: [
6062
- {
6063
- role: "user",
6064
- content: {
6065
- type: "text",
6066
- text: `I want to start working on task ${params.arguments?.taskId}. Can you help me set everything up?`
6067
- }
6068
- },
6069
- {
6070
- role: "assistant",
6071
- content: {
6072
- type: "text",
6073
- text: `I'll help you start working on task ${params.arguments?.taskId}. Here's what I'll do:
6074
-
6075
- 1. Mark the task as "in-progress"
6076
- 2. Create a git branch based on the task type and ID
6077
- 3. Ensure your working directory is clean
6078
- 4. Switch to the new branch
6079
-
6080
- Let me start by marking the task as in-progress using the start_task tool.`
6081
- }
6082
- }
6083
- ]
6084
- };
6085
- case "finish-task-workflow":
6086
- return {
6087
- description: "Workflow for finishing a task",
6088
- messages: [
6089
- {
6090
- role: "user",
6091
- content: {
6092
- type: "text",
6093
- text: `I've completed task ${params.arguments?.taskId}. What should I do next?`
6094
- }
6095
- },
6096
- {
6097
- role: "assistant",
6098
- content: {
6099
- type: "text",
6100
- text: `Great! Let me help you finish task ${params.arguments?.taskId}. Here's the workflow:
6485
+ return mcpCommandFor(manager);
6486
+ }
6101
6487
 
6102
- 1. Mark the task as "done"
6103
- 2. Ensure all changes are committed
6104
- 3. Push your branch to remote
6105
- 4. Create a pull request
6488
+ // src/lib/project-root/index.ts
6489
+ init_esm_shims();
6106
6490
 
6107
- Let me start by marking the task as done using the finish_task tool.`
6108
- }
6109
- }
6110
- ]
6111
- };
6112
- case "task-summary":
6113
- return {
6114
- description: "Generate task summary",
6115
- messages: [
6116
- {
6117
- role: "user",
6118
- content: {
6119
- type: "text",
6120
- text: `Can you provide a summary of task ${params.arguments?.taskId}?`
6121
- }
6122
- },
6123
- {
6124
- role: "assistant",
6125
- content: {
6126
- type: "text",
6127
- text: `I'll generate a comprehensive summary of task ${params.arguments?.taskId} including its current status, description, and any relevant metadata.`
6128
- }
6129
- }
6130
- ]
6131
- };
6132
- default:
6133
- throw new Error(`Unknown prompt: ${params.name}`);
6134
- }
6491
+ // src/lib/project-root/project-root.ts
6492
+ init_esm_shims();
6493
+ import { statSync } from "fs";
6494
+ import path13 from "path";
6495
+ var MARCADOR = ".taskin.json";
6496
+ function temMarcador(diretorio) {
6497
+ try {
6498
+ return statSync(path13.join(diretorio, MARCADOR)).isFile();
6499
+ } catch {
6500
+ return false;
6135
6501
  }
6136
- /**
6137
- * Le e seleciona as tarefas, pela mesma seam que o CLI usa.
6138
- *
6139
- * `filterTasks` e `summarizeTask` vivem no pacote agnostico justamente para
6140
- * que a resposta aqui e a de `taskin list --json` nao possam divergir — ja
6141
- * houve duas filtragens discordando no repositorio.
6142
- */
6143
- async selecionarTarefas(criteria) {
6144
- const tasks = await this.taskManager.getAllTasks();
6145
- return filterTasks2(tasks, criteria).map(summarizeTask2);
6502
+ }
6503
+ function findProjectRoot(startDir = process.cwd()) {
6504
+ let atual = path13.resolve(startDir);
6505
+ while (true) {
6506
+ if (temMarcador(atual)) return atual;
6507
+ const acima = path13.dirname(atual);
6508
+ if (acima === atual) return void 0;
6509
+ atual = acima;
6146
6510
  }
6147
- /** Converte os argumentos crus da chamada MCP no criterio tipado. */
6148
- static criterioDe(args) {
6149
- return {
6150
- ...typeof args.status === "string" && { status: args.status },
6151
- ...typeof args.type === "string" && { type: args.type },
6152
- ...typeof args.assignee === "string" && { assignee: args.assignee },
6153
- ...args.open === true && { open: true },
6154
- ...args.closed === true && { closed: true },
6155
- ...typeof args.text === "string" && { text: args.text }
6156
- };
6511
+ }
6512
+
6513
+ // src/commands/mcp-install.ts
6514
+ var ARQUIVO = ".mcp.json";
6515
+ function lerSeExistir(caminho) {
6516
+ try {
6517
+ return readFileSync5(caminho, "utf-8");
6518
+ } catch {
6519
+ return void 0;
6157
6520
  }
6158
- async handleListTasks(args) {
6159
- const tarefas = await this.selecionarTarefas(_TaskMCPServer.criterioDe(args));
6160
- return {
6161
- content: [{ type: "text", text: JSON.stringify(tarefas, null, 2) }],
6162
- isError: false
6163
- };
6521
+ }
6522
+ var mcpInstallCommand = defineCommand({
6523
+ name: "mcp-install",
6524
+ description: "\u{1F50C} Register the Taskin MCP server in this project's .mcp.json",
6525
+ options: [
6526
+ {
6527
+ flags: "-f, --force",
6528
+ description: "Replace an existing taskin entry that differs"
6529
+ },
6530
+ {
6531
+ flags: "--no-probe",
6532
+ description: "Skip starting the server to verify the entry works"
6533
+ }
6534
+ ],
6535
+ handler: async (options) => {
6536
+ await instalar(options);
6164
6537
  }
6165
- /**
6166
- * List available resources
6167
- */
6168
- async listResources() {
6169
- return {
6170
- resources: [
6171
- {
6172
- uri: "taskin://tasks",
6173
- name: "All Tasks",
6174
- description: "Access to all tasks in the system",
6175
- mimeType: "application/json"
6176
- }
6177
- ]
6178
- };
6538
+ });
6539
+ async function instalar(options) {
6540
+ const raiz = findProjectRoot();
6541
+ if (!raiz) {
6542
+ error('No .taskin.json found here or in any parent directory. Run "taskin init" first.');
6543
+ process.exit(1);
6179
6544
  }
6180
- /**
6181
- * Read a resource
6182
- */
6183
- async readResource(params) {
6184
- this.log(`Reading resource: ${params.uri}`);
6185
- const uri = params.uri;
6186
- if (uri === "taskin://tasks") {
6187
- const tarefas = await this.selecionarTarefas({});
6188
- return {
6189
- contents: [
6190
- {
6191
- uri,
6192
- mimeType: "application/json",
6193
- text: JSON.stringify(tarefas, null, 2)
6194
- }
6195
- ]
6196
- };
6197
- }
6198
- throw new Error(`Unknown resource: ${uri}`);
6545
+ const caminho = path14.join(raiz, ARQUIVO);
6546
+ const manager = detectPackageManager2(raiz);
6547
+ const entrada = resolveInvocation(raiz, manager);
6548
+ info(`Project root: ${raiz}`);
6549
+ info(`Package manager: ${manager} \u2014 the entry will run \`${[entrada.command, ...entrada.args].join(" ")}\``);
6550
+ const resultado = mergeMcpConfig(lerSeExistir(caminho), entrada, { overwrite: options.force === true });
6551
+ switch (resultado.outcome) {
6552
+ case "unreadable":
6553
+ error(`${ARQUIVO} exists but ${resultado.reason}. Nothing was written \u2014 fix or remove it first.`);
6554
+ process.exit(1);
6555
+ return;
6556
+ case "conflict":
6557
+ warning(`${ARQUIVO} already has a "${NOME_DO_SERVIDOR}" entry that differs:`);
6558
+ console.log(` current: ${JSON.stringify(resultado.current)}`);
6559
+ console.log(` new: ${JSON.stringify(entrada)}`);
6560
+ info("Run again with --force to replace it.");
6561
+ process.exit(1);
6562
+ return;
6563
+ case "unchanged":
6564
+ success(`${ARQUIVO} already registers the Taskin MCP server. Nothing to do.`);
6565
+ break;
6566
+ default:
6567
+ writeFileSync3(caminho, `${JSON.stringify(resultado.config, null, 2)}
6568
+ `, "utf-8");
6569
+ success(
6570
+ resultado.outcome === "created" ? `Created ${ARQUIVO}` : `${resultado.outcome === "added" ? "Added" : "Replaced"} the "${NOME_DO_SERVIDOR}" entry in ${ARQUIVO}`
6571
+ );
6199
6572
  }
6200
- /**
6201
- * Debug logging
6202
- */
6203
- log(...args) {
6204
- if (this.config.debug) {
6205
- console.error("[TaskMCPServer]", ...args);
6573
+ if (options.probe === false) {
6574
+ info("Skipped the check. Run without --no-probe to verify the server actually starts.");
6575
+ return;
6576
+ }
6577
+ info("Starting the server to check the entry actually works...");
6578
+ const { TaskMCPServer: TaskMCPServer2 } = await Promise.resolve().then(() => (init_dist2(), dist_exports));
6579
+ const esperadas = new TaskMCPServer2({ taskManager: {} }).listTools().tools.map((t) => t.name);
6580
+ const sonda = await probeMcpServer(entrada, raiz, esperadas);
6581
+ if (!sonda.ok) {
6582
+ error(`The entry was written, but the server did not answer: ${sonda.reason}`);
6583
+ if (sonda.stderr) {
6584
+ console.log(sonda.stderr);
6206
6585
  }
6586
+ process.exit(1);
6587
+ return;
6207
6588
  }
6208
- };
6209
-
6210
- // node_modules/@opentask/taskin-task-server-mcp/dist/task-server-mcp.types.js
6211
- init_esm_shims();
6589
+ success(`The server answered with ${sonda.tools.length} tool(s): ${sonda.tools.join(", ")}`);
6590
+ }
6212
6591
 
6213
6592
  // src/commands/mcp-server.ts
6593
+ init_esm_shims();
6594
+ init_dist2();
6214
6595
  import chalk6 from "chalk";
6596
+ var linha = (texto = "") => process.stderr.write(`${texto}
6597
+ `);
6598
+ var aviso = (mensagem) => linha(colors.info(`\u2139 ${mensagem}`));
6599
+ var feito = (mensagem) => linha(colors.success(`\u2713 ${mensagem}`));
6600
+ var falha = (mensagem) => linha(colors.error(`\u2717 ${mensagem}`));
6601
+ function cabecalho(titulo, icone) {
6602
+ linha();
6603
+ linha(colors.highlight("=".repeat(60)));
6604
+ linha(colors.highlight(`${icone} ${titulo}`));
6605
+ linha(colors.highlight("=".repeat(60)));
6606
+ linha();
6607
+ }
6215
6608
  var mcpServerCommand = defineCommand({
6216
6609
  name: "mcp-server",
6217
6610
  description: "\u{1F916} Start Model Context Protocol server for LLM integration",
6218
6611
  alias: "mcp",
6219
6612
  options: [
6220
- {
6221
- flags: "-t, --transport <type>",
6222
- description: "Transport type (stdio or sse)",
6223
- defaultValue: "stdio"
6224
- },
6225
6613
  {
6226
6614
  flags: "-d, --debug",
6227
6615
  description: "Enable debug logging"
@@ -6233,45 +6621,40 @@ var mcpServerCommand = defineCommand({
6233
6621
  });
6234
6622
  async function startMCPServer(options) {
6235
6623
  requireTaskinProject();
6236
- const transport = options.transport || "stdio";
6237
6624
  const debug = options.debug || false;
6238
- printHeader("Starting MCP Server", "\u{1F916}");
6625
+ cabecalho("Starting MCP Server", "\u{1F916}");
6239
6626
  try {
6240
- info("Initializing task manager...");
6627
+ aviso("Initializing task manager...");
6241
6628
  const { provider } = await resolveTaskProvider();
6242
6629
  const manager = new TaskManager(provider);
6243
- info(`Creating MCP server with ${transport} transport...`);
6244
6630
  const mcpServer = new TaskMCPServer({
6245
6631
  taskManager: manager,
6246
6632
  name: "taskin-mcp-server",
6247
6633
  version: "1.0.0",
6248
6634
  debug
6249
6635
  });
6250
- info("Starting MCP server...");
6251
- await mcpServer.connect({ transport });
6252
- success("MCP server started successfully");
6253
- info("");
6254
- info(chalk6.bold("Server Information:"));
6255
- info(` \u2022 Transport: ${chalk6.cyan(transport)}`);
6256
- info(` \u2022 Debug: ${chalk6.cyan(debug ? "enabled" : "disabled")}`);
6257
- info("");
6258
- info(chalk6.bold("Available Tools:"));
6259
- info(` \u2022 ${chalk6.green("start_task")} - Start working on a task`);
6260
- info(` \u2022 ${chalk6.green("finish_task")} - Mark a task as finished`);
6261
- info("");
6262
- info(chalk6.bold("Available Prompts:"));
6263
- info(` \u2022 ${chalk6.green("start-task-workflow")} - Guide for starting tasks`);
6264
- info(` \u2022 ${chalk6.green("finish-task-workflow")} - Guide for finishing tasks`);
6265
- info(` \u2022 ${chalk6.green("task-summary")} - Get task summary and insights`);
6266
- info("");
6267
- info(chalk6.bold("Available Resources:"));
6268
- info(` \u2022 ${chalk6.green("taskin://tasks")} - Access all tasks`);
6269
- info("");
6270
- info(`Press ${chalk6.bold("Ctrl+C")} to stop the server`);
6271
- info("");
6272
- const cleanup = async () => {
6273
- info("\nShutting down MCP server...");
6274
- success("Server stopped");
6636
+ aviso("Starting MCP server over stdio...");
6637
+ await mcpServer.connect({ transport: "stdio" });
6638
+ feito("MCP server started successfully");
6639
+ linha();
6640
+ aviso(` ${chalk6.bold("Debug")}: ${chalk6.cyan(debug ? "enabled" : "disabled")}`);
6641
+ linha();
6642
+ aviso(chalk6.bold("Available Tools:"));
6643
+ for (const ferramenta of mcpServer.listTools().tools) {
6644
+ aviso(` \u2022 ${chalk6.green(ferramenta.name)} - ${ferramenta.description}`);
6645
+ }
6646
+ linha();
6647
+ aviso(chalk6.bold("Available Prompts:"));
6648
+ for (const prompt of mcpServer.listPrompts().prompts) {
6649
+ aviso(` \u2022 ${chalk6.green(prompt.name)} - ${prompt.description}`);
6650
+ }
6651
+ linha();
6652
+ aviso(`Press ${chalk6.bold("Ctrl+C")} to stop the server`);
6653
+ linha();
6654
+ const cleanup = () => {
6655
+ linha();
6656
+ aviso("Shutting down MCP server...");
6657
+ feito("Server stopped");
6275
6658
  process.exit(0);
6276
6659
  };
6277
6660
  process.on("SIGINT", cleanup);
@@ -6279,9 +6662,9 @@ async function startMCPServer(options) {
6279
6662
  await new Promise(() => {
6280
6663
  });
6281
6664
  } catch (err) {
6282
- error("Failed to start MCP server");
6665
+ falha("Failed to start MCP server");
6283
6666
  if (err instanceof Error) {
6284
- error(err.message);
6667
+ falha(err.message);
6285
6668
  if (debug) {
6286
6669
  console.error(err.stack);
6287
6670
  }
@@ -6316,6 +6699,10 @@ var createCommand = defineCommand({
6316
6699
  {
6317
6700
  flags: "-u, --user <user>",
6318
6701
  description: "Assignee user"
6702
+ },
6703
+ {
6704
+ flags: "--no-skip-ci",
6705
+ description: "Write the status commit without the CI-skip tag"
6319
6706
  }
6320
6707
  ],
6321
6708
  handler: async (options) => {
@@ -6391,11 +6778,12 @@ async function createTask(options, gitService) {
6391
6778
  await userRegistry.ensureCurrentUser();
6392
6779
  const configManager = new ConfigManager(monorepoRoot);
6393
6780
  const behavior = configManager.getAutomationBehavior();
6781
+ const ciSkipTag = resolveCiSkipTag(behavior.ciSkipTag, options.skipCi);
6394
6782
  const autoSyncActive = behavior.autoSync && !!behavior.defaultBranch;
6395
6783
  if (behavior.autoSync && !behavior.defaultBranch) {
6396
6784
  warning("autoSync is enabled but no defaultBranch is configured. Nothing will be synced.");
6397
6785
  }
6398
- const git = gitService ?? new GitService(process.cwd(), { ciSkipTag: behavior.ciSkipTag });
6786
+ const git = gitService ?? new GitService(process.cwd(), { ciSkipTag });
6399
6787
  if (autoSyncActive) {
6400
6788
  try {
6401
6789
  await syncBeforeCreate(git, {
@@ -6428,7 +6816,7 @@ async function createTask(options, gitService) {
6428
6816
  taskId,
6429
6817
  title: options.title,
6430
6818
  defaultBranch: behavior.defaultBranch,
6431
- ciSkipTag: behavior.ciSkipTag
6819
+ ciSkipTag
6432
6820
  });
6433
6821
  } catch (pushError) {
6434
6822
  error(
@@ -6750,6 +7138,10 @@ var reviewCommand = defineCommand({
6750
7138
  {
6751
7139
  flags: "--no-sound",
6752
7140
  description: "Disable review sound"
7141
+ },
7142
+ {
7143
+ flags: "--no-skip-ci",
7144
+ description: "Write the status commit without the CI-skip tag"
6753
7145
  }
6754
7146
  ],
6755
7147
  handler: async (taskId, options) => {
@@ -6781,6 +7173,7 @@ async function reviewTask(taskId, options) {
6781
7173
  const hookSettings = configManager.getHookSettings();
6782
7174
  const reviewHooks = configManager.getCommandHooks("review");
6783
7175
  const behavior = configManager.getAutomationBehavior();
7176
+ const ciSkipTag = resolveCiSkipTag(behavior.ciSkipTag, options.skipCi);
6784
7177
  const hookRunner = new HookRunner();
6785
7178
  const hookContext = {
6786
7179
  taskId: normalizedId,
@@ -6874,10 +7267,7 @@ async function reviewTask(taskId, options) {
6874
7267
  success(`Task ${updatedTask.id} status changed to: ${updatedTask.status}`);
6875
7268
  if (behavior.autoCommitStatusChange) {
6876
7269
  try {
6877
- const message = appendCiSkipTag(
6878
- `docs(TASKS): task-${normalizedId} - mark as ready for review`,
6879
- behavior.ciSkipTag
6880
- );
7270
+ const message = appendCiSkipTag(`docs(TASKS): task-${normalizedId} - mark as ready for review`, ciSkipTag);
6881
7271
  execSync9(`git add TASKS/task-${normalizedId}-*.md && git commit -m "${message}"`, {
6882
7272
  cwd: monorepoRoot,
6883
7273
  stdio: "ignore"
@@ -6950,13 +7340,17 @@ var startCommand = defineCommand({
6950
7340
  {
6951
7341
  flags: "--dry-run",
6952
7342
  description: "Show what would be executed without running"
7343
+ },
7344
+ {
7345
+ flags: "--no-skip-ci",
7346
+ description: "Write the status commit without the CI-skip tag"
6953
7347
  }
6954
7348
  ],
6955
7349
  handler: async (taskId, options) => {
6956
7350
  await startTask(taskId, options);
6957
7351
  }
6958
7352
  });
6959
- async function startTask(taskId, _options, gitService) {
7353
+ async function startTask(taskId, options, gitService) {
6960
7354
  requireTaskinProject();
6961
7355
  printHeader(`Starting Task ${taskId}`, "\u{1F680}");
6962
7356
  const normalizedId = normalizeTaskId(taskId);
@@ -6976,12 +7370,13 @@ async function startTask(taskId, _options, gitService) {
6976
7370
  info(`Current status: ${task.status}`);
6977
7371
  const configManager = new ConfigManager(monorepoRoot);
6978
7372
  const behavior = configManager.getAutomationBehavior();
7373
+ const ciSkipTag = resolveCiSkipTag(behavior.ciSkipTag, options.skipCi);
6979
7374
  const statusCommitMessage = buildTaskStatusCommitMessage({
6980
7375
  taskId: normalizedId,
6981
7376
  status: "in-progress",
6982
- ciSkipTag: behavior.ciSkipTag
7377
+ ciSkipTag
6983
7378
  });
6984
- if (_options.dryRun) {
7379
+ if (options.dryRun) {
6985
7380
  console.log();
6986
7381
  info("\u{1F50D} Dry run mode - showing what would be executed:");
6987
7382
  console.log();
@@ -7011,7 +7406,7 @@ async function startTask(taskId, _options, gitService) {
7011
7406
  const updatedTask = await taskManager.startTask(task.id);
7012
7407
  success(`Task ${updatedTask.id} started successfully!`);
7013
7408
  success(`Status changed to: ${updatedTask.status}`);
7014
- const git = gitService ?? new GitService(process.cwd(), { ciSkipTag: behavior.ciSkipTag });
7409
+ const git = gitService ?? new GitService(process.cwd(), { ciSkipTag });
7015
7410
  if (behavior.autoCommitStatusChange) {
7016
7411
  const committed = await git.commitTaskStatusChangeOnBranch(normalizedId, "in-progress", behavior.defaultBranch);
7017
7412
  if (committed) {
@@ -7037,7 +7432,7 @@ async function startTask(taskId, _options, gitService) {
7037
7432
  }
7038
7433
  console.log();
7039
7434
  await sendTaskNotification(configManager, "task:start", normalizedId, task.title);
7040
- if (_options.sound !== false) {
7435
+ if (options.sound !== false) {
7041
7436
  playSound("start");
7042
7437
  }
7043
7438
  }
@@ -7047,7 +7442,7 @@ init_esm_shims();
7047
7442
  init_src3();
7048
7443
  init_src();
7049
7444
  import chalk7 from "chalk";
7050
- import path11 from "path";
7445
+ import path15 from "path";
7051
7446
  var statsCommand = defineCommand({
7052
7447
  name: "stats",
7053
7448
  description: "\u{1F4CA} Show user/team metrics and statistics",
@@ -7079,7 +7474,7 @@ var statsCommand = defineCommand({
7079
7474
  });
7080
7475
  async function showStats(options) {
7081
7476
  requireTaskinProject();
7082
- const tasksDir = path11.join(process.cwd(), "TASKS");
7477
+ const tasksDir = path15.join(process.cwd(), "TASKS");
7083
7478
  const { userRegistry } = await resolveTaskProvider();
7084
7479
  const gitAnalyzer = new GitAnalyzer(process.cwd());
7085
7480
  const metricsAdapter = new FileSystemMetricsAdapter(tasksDir, userRegistry, gitAnalyzer);
@@ -7251,6 +7646,7 @@ var EXEMPLOS = {
7251
7646
  export: ["taskin export", "taskin export --format json"],
7252
7647
  lint: ["taskin lint", "taskin lint --fix", "taskin lint --fix --metadata-style=list"],
7253
7648
  dashboard: ["taskin dashboard", "taskin dashboard --port 3000", "taskin dashboard --filter-open"],
7649
+ "mcp-install": ["taskin mcp-install", "taskin mcp-install --force", "taskin mcp-install --no-probe"],
7254
7650
  "mcp-server": ["taskin mcp-server", "taskin mcp"],
7255
7651
  notify: ["taskin notify --event task:done --task 001"]
7256
7652
  };
@@ -7334,7 +7730,7 @@ function showCustomHelp(program2) {
7334
7730
 
7335
7731
  // src/version.ts
7336
7732
  init_esm_shims();
7337
- import { readFileSync as readFileSync3 } from "fs";
7733
+ import { readFileSync as readFileSync6 } from "fs";
7338
7734
  import { dirname, join as join4 } from "path";
7339
7735
  import { fileURLToPath as fileURLToPath3 } from "url";
7340
7736
  var __filename3 = fileURLToPath3(import.meta.url);
@@ -7342,7 +7738,7 @@ var __dirname3 = dirname(__filename3);
7342
7738
  function getVersion() {
7343
7739
  try {
7344
7740
  const packageJsonPath = join4(__dirname3, "../package.json");
7345
- const packageJson = JSON.parse(readFileSync3(packageJsonPath, "utf-8"));
7741
+ const packageJson = JSON.parse(readFileSync6(packageJsonPath, "utf-8"));
7346
7742
  return packageJson.version;
7347
7743
  } catch {
7348
7744
  return "0.0.0";
@@ -7715,6 +8111,7 @@ configCommand(program);
7715
8111
  registerExportCommand(program);
7716
8112
  lintCommand(program);
7717
8113
  dashboardCommand(program);
8114
+ mcpInstallCommand(program);
7718
8115
  mcpServerCommand(program);
7719
8116
  notifyCommand(program);
7720
8117
  program.on("option:help", () => {