taskin 4.1.4 → 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 +46 -23
  2. package/dist/index.js +1135 -611
  3. package/package.json +7 -7
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";
@@ -3456,6 +4060,51 @@ init_esm_shims();
3456
4060
  // ../task-manager/src/index.ts
3457
4061
  init_esm_shims();
3458
4062
 
4063
+ // ../task-manager/src/filter-tasks/index.ts
4064
+ init_esm_shims();
4065
+
4066
+ // ../task-manager/src/filter-tasks/filter-tasks.ts
4067
+ init_esm_shims();
4068
+ var EM_ABERTO = ["pending", "in-progress", "paused", "in-review", "blocked"];
4069
+ var ENCERRADOS = ["done", "canceled"];
4070
+ var contem = (valor, procurado) => valor?.toLowerCase().includes(procurado) ?? false;
4071
+ function casaResponsavel(task, procurado) {
4072
+ const alvo = procurado.toLowerCase();
4073
+ return contem(task.assignee?.id, alvo) || contem(task.assignee?.name, alvo);
4074
+ }
4075
+ function filterTasks(tasks, criteria) {
4076
+ return tasks.filter((task) => {
4077
+ if (criteria.status !== void 0) {
4078
+ if (task.status !== criteria.status) return false;
4079
+ } else if (criteria.open && !EM_ABERTO.includes(task.status)) {
4080
+ return false;
4081
+ } else if (criteria.closed && !ENCERRADOS.includes(task.status)) {
4082
+ return false;
4083
+ }
4084
+ if (criteria.type !== void 0 && task.type !== criteria.type) return false;
4085
+ if (criteria.assignee !== void 0 && !casaResponsavel(task, criteria.assignee)) return false;
4086
+ if (criteria.text !== void 0) {
4087
+ const procurado = criteria.text.toLowerCase();
4088
+ const casa = task.id.toLowerCase().includes(procurado) || contem(task.title, procurado) || contem(task.status, procurado) || casaResponsavel(task, procurado);
4089
+ if (!casa) return false;
4090
+ }
4091
+ return true;
4092
+ });
4093
+ }
4094
+ function summarizeTask(task) {
4095
+ return {
4096
+ id: task.id,
4097
+ title: task.title,
4098
+ status: task.status,
4099
+ type: task.type,
4100
+ ...task.assignee && { assignee: { id: task.assignee.id, name: task.assignee.name } },
4101
+ ...task.order !== void 0 && { priority: task.order },
4102
+ ...task.groupId && { groupId: task.groupId },
4103
+ ...task.groupName && { groupName: task.groupName },
4104
+ ...task.difficulty !== void 0 && { difficulty: task.difficulty }
4105
+ };
4106
+ }
4107
+
3459
4108
  // ../task-manager/src/metrics.types.ts
3460
4109
  init_esm_shims();
3461
4110
 
@@ -3502,6 +4151,16 @@ var TaskManager = class {
3502
4151
  await this.taskProvider.updateTask(updatedTask);
3503
4152
  return updatedTask;
3504
4153
  }
4154
+ /**
4155
+ * Every task the configured provider knows about.
4156
+ *
4157
+ * Pass-through on purpose: the manager owns the state transitions, not the
4158
+ * storage. Having it here is what lets a consumer holding only the manager —
4159
+ * the MCP server — answer "what work exists?".
4160
+ */
4161
+ async getAllTasks() {
4162
+ return await this.taskProvider.getAllTasks();
4163
+ }
3505
4164
  async finishTask(taskId) {
3506
4165
  const task = await this.taskProvider.findTask(taskId);
3507
4166
  if (!task) {
@@ -4313,7 +4972,7 @@ async function startDashboard(options) {
4313
4972
  }
4314
4973
  });
4315
4974
  await wsServer.start();
4316
- success(`\u2713 WebSocket server running on ws://${host}:${wsPort}`);
4975
+ success(`WebSocket server running on ws://${host}:${wsPort}`);
4317
4976
  info(`Starting dashboard server on http://${host}:${port}...`);
4318
4977
  const isDev = __dirname2.includes("/src/");
4319
4978
  const dashboardDist = isDev ? path8.join(__dirname2, "..", "..", "dashboard-dist") : path8.join(__dirname2, "..", "dashboard-dist");
@@ -4364,7 +5023,7 @@ async function startDashboard(options) {
4364
5023
  if (actualPort !== port) {
4365
5024
  warning(`Port ${port} was in use. Dashboard started on port ${actualPort}.`);
4366
5025
  }
4367
- success(`\u2713 Dashboard available at http://${host}:${actualPort}`);
5026
+ success(`Dashboard available at http://${host}:${actualPort}`);
4368
5027
  const filterParams = new URLSearchParams();
4369
5028
  if (options.open) {
4370
5029
  filterParams.set("filter", "open");
@@ -4396,7 +5055,7 @@ async function startDashboard(options) {
4396
5055
  httpServer.close(() => resolve());
4397
5056
  });
4398
5057
  await wsServer.stop();
4399
- success("\u2713 Servers stopped");
5058
+ success("Servers stopped");
4400
5059
  process.exit(0);
4401
5060
  };
4402
5061
  process.on("SIGINT", cleanup);
@@ -4495,9 +5154,18 @@ init_src3();
4495
5154
  init_src();
4496
5155
  import { execSync as execSync5 } from "child_process";
4497
5156
 
4498
- // src/lib/notification/notify-helper.ts
5157
+ // src/lib/ci-skip-tag/index.ts
4499
5158
  init_esm_shims();
4500
- import { execSync as execSync4 } from "child_process";
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
+
5166
+ // src/lib/notification/notify-helper.ts
5167
+ init_esm_shims();
5168
+ import { execSync as execSync4 } from "child_process";
4501
5169
 
4502
5170
  // src/lib/debug.ts
4503
5171
  init_esm_shims();
@@ -4891,6 +5559,10 @@ var finishCommand = defineCommand({
4891
5559
  {
4892
5560
  flags: "--dry-run",
4893
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"
4894
5566
  }
4895
5567
  ],
4896
5568
  handler: async (taskId, options) => {
@@ -4916,11 +5588,12 @@ async function finishTask(taskId, options, gitService) {
4916
5588
  info(`Current status: ${task.status}`);
4917
5589
  const configManager = new ConfigManager(monorepoRoot);
4918
5590
  const behavior = configManager.getAutomationBehavior();
5591
+ const ciSkipTag = resolveCiSkipTag(behavior.ciSkipTag, options.skipCi);
4919
5592
  const autoSyncActive = behavior.autoSync && !!behavior.defaultBranch;
4920
5593
  const statusCommitMessage = buildTaskStatusCommitMessage({
4921
5594
  taskId: normalizedId,
4922
5595
  status: "done",
4923
- ciSkipTag: behavior.ciSkipTag
5596
+ ciSkipTag
4924
5597
  });
4925
5598
  if (options.dryRun) {
4926
5599
  console.log();
@@ -4943,7 +5616,7 @@ async function finishTask(taskId, options, gitService) {
4943
5616
  );
4944
5617
  console.log(colors.secondary(` - Push: git push`));
4945
5618
  console.log();
4946
- info("\u2713 Dry run complete");
5619
+ info("Dry run complete");
4947
5620
  return;
4948
5621
  }
4949
5622
  if (task.status === "done") {
@@ -4953,7 +5626,7 @@ async function finishTask(taskId, options, gitService) {
4953
5626
  if (behavior.autoSync && !behavior.defaultBranch) {
4954
5627
  warning("autoSync is enabled but no defaultBranch is configured. Nothing will be synced.");
4955
5628
  }
4956
- const git = gitService ?? new GitService(process.cwd(), { ciSkipTag: behavior.ciSkipTag });
5629
+ const git = gitService ?? new GitService(process.cwd(), { ciSkipTag });
4957
5630
  if (!options.skipUpdate) {
4958
5631
  info("Marking task as done...");
4959
5632
  const updatedTask = await taskManager.finishTask(task.id);
@@ -4962,7 +5635,7 @@ async function finishTask(taskId, options, gitService) {
4962
5635
  if (behavior.autoCommitStatusChange) {
4963
5636
  const committed = await git.commitTaskStatusChangeOnBranch(normalizedId, "done", behavior.defaultBranch);
4964
5637
  if (committed) {
4965
- success("\u2713 Auto-committed status change");
5638
+ success("Auto-committed status change");
4966
5639
  }
4967
5640
  }
4968
5641
  if (behavior.autoSync && behavior.originBranch && behavior.defaultBranch) {
@@ -4971,10 +5644,10 @@ async function finishTask(taskId, options, gitService) {
4971
5644
  taskId: normalizedId,
4972
5645
  defaultBranch: behavior.defaultBranch,
4973
5646
  originBranch: behavior.originBranch,
4974
- ciSkipTag: behavior.ciSkipTag
5647
+ ciSkipTag
4975
5648
  });
4976
5649
  if (squashed) {
4977
- success(`\u2713 Squash commit pushed to ${behavior.originBranch}`);
5650
+ success(`Squash commit pushed to ${behavior.originBranch}`);
4978
5651
  }
4979
5652
  } catch (squashError) {
4980
5653
  error(
@@ -4990,7 +5663,7 @@ async function finishTask(taskId, options, gitService) {
4990
5663
  cwd: process.cwd(),
4991
5664
  stdio: "ignore"
4992
5665
  });
4993
- success("\u2713 Auto-committed completed work");
5666
+ success("Auto-committed completed work");
4994
5667
  } catch {
4995
5668
  }
4996
5669
  }
@@ -5212,7 +5885,7 @@ async function initializeTaskin(options) {
5212
5885
  await ensureProviderInstalled(selectedProvider);
5213
5886
  }
5214
5887
  const providerConfig = await setupProviderConfig(selectedProvider, cwd);
5215
- const ciSkipTag = await resolveCiSkipTag(options.ciSkipTag);
5888
+ const ciSkipTag = await resolveCiSkipTag2(options.ciSkipTag);
5216
5889
  const config = {
5217
5890
  version: "1.0.3",
5218
5891
  automation: {
@@ -5227,7 +5900,7 @@ async function initializeTaskin(options) {
5227
5900
  };
5228
5901
  info("Creating configuration file...");
5229
5902
  writeFileSync2(configFile, JSON.stringify(config, null, 2), "utf-8");
5230
- success(`\u2713 Created ${colors.highlight(".taskin.json")}`);
5903
+ success(`Created ${colors.highlight(".taskin.json")}`);
5231
5904
  if (process.env.CI !== "true" && selectedProvider.id === "fs") {
5232
5905
  await promptCreateFirstUser(cwd);
5233
5906
  }
@@ -5246,7 +5919,7 @@ async function initializeTaskin(options) {
5246
5919
  info("For more information, run: taskin --help");
5247
5920
  console.log();
5248
5921
  }
5249
- async function resolveCiSkipTag(fromFlag) {
5922
+ async function resolveCiSkipTag2(fromFlag) {
5250
5923
  if (fromFlag !== void 0) {
5251
5924
  return normalizeCiSkipTag(fromFlag);
5252
5925
  }
@@ -5310,7 +5983,7 @@ async function setupProviderConfig(provider, cwd) {
5310
5983
  }));
5311
5984
  const answers = await inquirer2.prompt(questions);
5312
5985
  console.log();
5313
- success(`\u2713 ${provider.name} configuration saved`);
5986
+ success(`${provider.name} configuration saved`);
5314
5987
  return answers;
5315
5988
  }
5316
5989
  async function setupFileSystemProvider(cwd) {
@@ -5352,7 +6025,7 @@ This is a sample task created during Taskin initialization.
5352
6025
  You can edit or delete this file. Use \`taskin list\` to see all tasks.
5353
6026
  `;
5354
6027
  writeFileSync2(sampleTaskFile, sampleTask, "utf-8");
5355
- success(`\u2713 Created sample task ${colors.highlight("task-001-setup-project.md")}`);
6028
+ success(`Created sample task ${colors.highlight("task-001-setup-project.md")}`);
5356
6029
  }
5357
6030
  return {
5358
6031
  tasksDir: "TASKS",
@@ -5397,7 +6070,7 @@ async function promptCreateFirstUser(cwd) {
5397
6070
  email: answers.email
5398
6071
  };
5399
6072
  await userRegistry.saveUser(user);
5400
- success(`\u2713 User "${user.name}" (${user.email}) created successfully!`);
6073
+ success(`User "${user.name}" (${user.email}) created successfully!`);
5401
6074
  }
5402
6075
 
5403
6076
  // src/commands/lint.ts
@@ -5518,6 +6191,10 @@ var listCommand = defineCommand({
5518
6191
  {
5519
6192
  flags: "--closed",
5520
6193
  description: "Show only closed tasks (done, canceled)"
6194
+ },
6195
+ {
6196
+ flags: "--json",
6197
+ description: "Print the tasks as JSON, for other tools to consume"
5521
6198
  }
5522
6199
  ],
5523
6200
  handler: async (filter, options) => {
@@ -5526,36 +6203,28 @@ var listCommand = defineCommand({
5526
6203
  });
5527
6204
  async function listTasks(filter, options) {
5528
6205
  requireTaskinProject();
5529
- printHeader("Task List", "\u{1F4CA}");
6206
+ const comoJson = options.json === true;
6207
+ if (!comoJson) {
6208
+ printHeader("Task List", "\u{1F4CA}");
6209
+ }
5530
6210
  const { provider: taskProvider } = await resolveTaskProvider();
5531
6211
  const tasks = await taskProvider.getAllTasks();
5532
- if (tasks.length === 0) {
6212
+ if (tasks.length === 0 && !comoJson) {
5533
6213
  console.log(colors.warning("No tasks found in TASKS/ directory"));
5534
6214
  return;
5535
6215
  }
5536
- const openStatuses = ["pending", "in-progress", "paused", "in-review", "blocked"];
5537
- const closedStatuses = ["done", "canceled"];
5538
- let filteredTasks = tasks;
5539
- if (options.status) {
5540
- filteredTasks = filteredTasks.filter((t) => t.status === options.status);
5541
- } else if (options.open) {
5542
- filteredTasks = filteredTasks.filter((t) => t.status && openStatuses.includes(t.status));
5543
- } else if (options.closed) {
5544
- filteredTasks = filteredTasks.filter((t) => t.status && closedStatuses.includes(t.status));
5545
- }
5546
- if (options.type) {
5547
- filteredTasks = filteredTasks.filter((t) => t.type === options.type);
5548
- }
5549
- if (options.assignee) {
5550
- filteredTasks = filteredTasks.filter(
5551
- (t) => t.assignee?.name.toLowerCase().includes(options.assignee.toLowerCase()) || t.assignee?.id.toLowerCase().includes(options.assignee.toLowerCase())
5552
- );
5553
- }
5554
- if (filter) {
5555
- const lowerFilter = filter.toLowerCase();
5556
- filteredTasks = filteredTasks.filter(
5557
- (t) => t.id.includes(lowerFilter) || t.title.toLowerCase().includes(lowerFilter) || t.status.toLowerCase().includes(lowerFilter) || t.assignee?.name.toLowerCase().includes(lowerFilter) || t.assignee?.id.toLowerCase().includes(lowerFilter)
5558
- );
6216
+ const criteria = {
6217
+ ...options.status && { status: options.status },
6218
+ ...options.type && { type: options.type },
6219
+ ...options.assignee && { assignee: options.assignee },
6220
+ ...options.open && { open: true },
6221
+ ...options.closed && { closed: true },
6222
+ ...filter && { text: filter }
6223
+ };
6224
+ const filteredTasks = filterTasks(tasks, criteria);
6225
+ if (comoJson) {
6226
+ console.log(JSON.stringify(filteredTasks.map(summarizeTask), null, 2));
6227
+ return;
5559
6228
  }
5560
6229
  if (filteredTasks.length === 0) {
5561
6230
  console.log(colors.warning("No tasks match the filters"));
@@ -5623,448 +6292,324 @@ function getTypeColor(type) {
5623
6292
  }
5624
6293
  }
5625
6294
 
5626
- // src/commands/mcp-server.ts
6295
+ // src/commands/mcp-install.ts
5627
6296
  init_esm_shims();
6297
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
6298
+ import path14 from "path";
5628
6299
 
5629
- // node_modules/@opentask/taskin-task-server-mcp/dist/index.js
6300
+ // src/lib/mcp-install/index.ts
5630
6301
  init_esm_shims();
5631
6302
 
5632
- // node_modules/@opentask/taskin-task-server-mcp/dist/task-server-mcp.js
6303
+ // src/lib/mcp-install/merge-mcp-config.ts
5633
6304
  init_esm_shims();
5634
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
5635
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5636
- import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema } from "@modelcontextprotocol/sdk/types.js";
5637
- import { TaskIdSchema as TaskIdSchema4 } from "@opentask/taskin-types";
5638
- function readTaskId(raw) {
5639
- if (typeof raw !== "string")
5640
- return void 0;
5641
- const parsed = TaskIdSchema4.safeParse(raw);
5642
- return parsed.success ? parsed.data : void 0;
5643
- }
5644
- function invalidTaskId(raw) {
5645
- return {
5646
- content: [
5647
- {
5648
- type: "text",
5649
- text: `Invalid task id: ${JSON.stringify(raw)}. Expected the numeric id of a task, e.g. "020".`
5650
- }
5651
- ],
5652
- isError: true
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" };
6330
+ }
6331
+ if (!options.overwrite) {
6332
+ return { outcome: "conflict", current: atual };
6333
+ }
6334
+ }
6335
+ const config = {
6336
+ ...lido,
6337
+ mcpServers: { ...atuais, [NOME_DO_SERVIDOR]: entrada }
5653
6338
  };
6339
+ return atual === void 0 ? { outcome: "added", config } : { outcome: "replaced", config };
5654
6340
  }
5655
- var TaskMCPServer = class {
5656
- server;
5657
- taskManager;
5658
- config;
5659
- constructor(config) {
5660
- this.taskManager = config.taskManager;
5661
- this.config = {
5662
- name: "taskin-mcp-server",
5663
- version: "1.0.0",
5664
- debug: false,
5665
- ...config,
5666
- taskManager: config.taskManager
5667
- };
5668
- this.server = new Server({
5669
- name: this.config.name,
5670
- version: this.config.version
5671
- }, {
5672
- capabilities: {
5673
- tools: {},
5674
- prompts: {},
5675
- resources: {}
5676
- }
5677
- });
5678
- this.setupHandlers();
6341
+
6342
+ // src/lib/mcp-install/package-manager.ts
6343
+ init_esm_shims();
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;
5679
6360
  }
5680
- /**
5681
- * Setup MCP request handlers
5682
- */
5683
- setupHandlers() {
5684
- this.server.setRequestHandler(ListToolsRequestSchema, async () => {
5685
- const { tools } = this.listTools();
5686
- return { tools };
5687
- });
5688
- this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
5689
- const result = await this.callTool({
5690
- name: request.params.name,
5691
- arguments: request.params.arguments
5692
- });
5693
- return {
5694
- content: [
5695
- {
5696
- type: "text",
5697
- text: result.content
5698
- }
5699
- ],
5700
- isError: result.isError
5701
- };
5702
- });
5703
- this.server.setRequestHandler(ListPromptsRequestSchema, async () => {
5704
- const { prompts } = this.listPrompts();
5705
- return { prompts };
5706
- });
5707
- this.server.setRequestHandler(GetPromptRequestSchema, async (request) => {
5708
- const result = await this.getPrompt({
5709
- name: request.params.name,
5710
- arguments: request.params.arguments
5711
- });
5712
- return {
5713
- messages: result.messages.map((msg) => ({
5714
- role: msg.role,
5715
- content: {
5716
- type: "text",
5717
- text: msg.content
5718
- }
5719
- }))
5720
- };
5721
- });
5722
- this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
5723
- const result = await this.listResources();
5724
- return { resources: result.resources };
5725
- });
5726
- this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
5727
- const result = await this.readResource({
5728
- uri: request.params.uri
5729
- });
5730
- return result;
5731
- });
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;
5732
6367
  }
5733
- /**
5734
- * Connect to MCP transport
5735
- */
5736
- async connect(options) {
5737
- if (options.transport === "stdio") {
5738
- const transport = new StdioServerTransport();
5739
- await this.server.connect(transport);
5740
- this.log("MCP Server connected via stdio");
5741
- } else {
5742
- throw new Error(`Transport ${options.transport} not yet implemented`);
5743
- }
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"] };
5744
6380
  }
5745
- /**
5746
- * Get server information
5747
- */
5748
- getServerInfo() {
5749
- return {
5750
- name: this.config.name,
5751
- version: this.config.version,
5752
- protocolVersion: "2024-11-05",
5753
- capabilities: {
5754
- tools: {},
5755
- prompts: {},
5756
- resources: {}
5757
- }
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);
5758
6405
  };
5759
- }
5760
- /**
5761
- * List available tools
5762
- */
5763
- listTools() {
5764
- const tools = [
5765
- {
5766
- name: "start_task",
5767
- description: "Start working on a task by changing its status to in-progress",
5768
- inputSchema: {
5769
- type: "object",
5770
- properties: {
5771
- taskId: {
5772
- type: "string",
5773
- description: "The unique identifier of the task (UUID)"
5774
- }
5775
- },
5776
- required: ["taskId"]
5777
- }
5778
- },
5779
- {
5780
- name: "finish_task",
5781
- description: "Mark a task as completed by changing its status to done",
5782
- inputSchema: {
5783
- type: "object",
5784
- properties: {
5785
- taskId: {
5786
- type: "string",
5787
- description: "The unique identifier of the task (UUID)"
5788
- }
5789
- },
5790
- required: ["taskId"]
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;
5791
6423
  }
5792
- }
5793
- ];
5794
- return { tools };
5795
- }
5796
- /**
5797
- * Call a tool
5798
- */
5799
- async callTool(params) {
5800
- try {
5801
- this.log(`Calling tool: ${params.name}`, params.arguments);
5802
- switch (params.name) {
5803
- case "start_task": {
5804
- const taskId = readTaskId(params.arguments?.taskId);
5805
- 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;
5806
6428
  }
5807
- case "finish_task": {
5808
- const taskId = readTaskId(params.arguments?.taskId);
5809
- 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;
5810
6433
  }
5811
- default:
5812
- return {
5813
- content: [
5814
- {
5815
- type: "text",
5816
- text: `Unknown tool: ${params.name}`
5817
- }
5818
- ],
5819
- isError: true
5820
- };
5821
- }
5822
- } catch (error2) {
5823
- this.log("Tool call error:", error2);
5824
- return {
5825
- content: [
5826
- {
5827
- type: "text",
5828
- 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()
5829
6440
  }
5830
- ],
5831
- isError: true
5832
- };
5833
- }
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"] };
5834
6467
  }
5835
- /**
5836
- * Handle start_task tool
5837
- */
5838
- async handleStartTask(taskId) {
5839
- const task = await this.taskManager.startTask(taskId);
5840
- return {
5841
- content: [
5842
- {
5843
- type: "text",
5844
- text: JSON.stringify({
5845
- success: true,
5846
- message: `Task ${taskId} started successfully`,
5847
- task: {
5848
- id: task.id,
5849
- title: task.title,
5850
- status: task.status,
5851
- type: task.type
5852
- }
5853
- }, null, 2)
5854
- }
5855
- ]
5856
- };
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;
5857
6476
  }
5858
- /**
5859
- * Handle finish_task tool
5860
- */
5861
- async handleFinishTask(taskId) {
5862
- const task = await this.taskManager.finishTask(taskId);
5863
- return {
5864
- content: [
5865
- {
5866
- type: "text",
5867
- text: JSON.stringify({
5868
- success: true,
5869
- message: `Task ${taskId} finished successfully`,
5870
- task: {
5871
- id: task.id,
5872
- title: task.title,
5873
- status: task.status,
5874
- type: task.type
5875
- }
5876
- }, null, 2)
5877
- }
5878
- ]
5879
- };
6477
+ }
6478
+ function resolveInvocation(raiz, manager) {
6479
+ if (existsSync7(path12.join(raiz, "node_modules", ".bin", "taskin"))) {
6480
+ return mcpCommandFor(manager);
5880
6481
  }
5881
- /**
5882
- * List available prompts
5883
- */
5884
- listPrompts() {
5885
- const prompts = [
5886
- {
5887
- name: "start-task-workflow",
5888
- description: "Guide the user through starting work on a task with git branch creation",
5889
- arguments: [
5890
- {
5891
- name: "taskId",
5892
- description: "The task ID to start",
5893
- required: true
5894
- }
5895
- ]
5896
- },
5897
- {
5898
- name: "finish-task-workflow",
5899
- description: "Guide the user through completing a task with commit and PR creation",
5900
- arguments: [
5901
- {
5902
- name: "taskId",
5903
- description: "The task ID to finish",
5904
- required: true
5905
- }
5906
- ]
5907
- },
5908
- {
5909
- name: "task-summary",
5910
- description: "Generate a summary of a task for documentation or reports",
5911
- arguments: [
5912
- {
5913
- name: "taskId",
5914
- description: "The task ID to summarize",
5915
- required: true
5916
- }
5917
- ]
5918
- }
5919
- ];
5920
- return { prompts };
6482
+ if (temScriptTaskin(raiz)) {
6483
+ return viaScript(manager);
5921
6484
  }
5922
- /**
5923
- * Get a prompt
5924
- */
5925
- async getPrompt(params) {
5926
- this.log(`Getting prompt: ${params.name}`, params.arguments);
5927
- switch (params.name) {
5928
- case "start-task-workflow":
5929
- return {
5930
- description: "Workflow for starting a task",
5931
- messages: [
5932
- {
5933
- role: "user",
5934
- content: {
5935
- type: "text",
5936
- text: `I want to start working on task ${params.arguments?.taskId}. Can you help me set everything up?`
5937
- }
5938
- },
5939
- {
5940
- role: "assistant",
5941
- content: {
5942
- type: "text",
5943
- text: `I'll help you start working on task ${params.arguments?.taskId}. Here's what I'll do:
5944
-
5945
- 1. Mark the task as "in-progress"
5946
- 2. Create a git branch based on the task type and ID
5947
- 3. Ensure your working directory is clean
5948
- 4. Switch to the new branch
6485
+ return mcpCommandFor(manager);
6486
+ }
5949
6487
 
5950
- Let me start by marking the task as in-progress using the start_task tool.`
5951
- }
5952
- }
5953
- ]
5954
- };
5955
- case "finish-task-workflow":
5956
- return {
5957
- description: "Workflow for finishing a task",
5958
- messages: [
5959
- {
5960
- role: "user",
5961
- content: {
5962
- type: "text",
5963
- text: `I've completed task ${params.arguments?.taskId}. What should I do next?`
5964
- }
5965
- },
5966
- {
5967
- role: "assistant",
5968
- content: {
5969
- type: "text",
5970
- text: `Great! Let me help you finish task ${params.arguments?.taskId}. Here's the workflow:
6488
+ // src/lib/project-root/index.ts
6489
+ init_esm_shims();
5971
6490
 
5972
- 1. Mark the task as "done"
5973
- 2. Ensure all changes are committed
5974
- 3. Push your branch to remote
5975
- 4. Create a pull request
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;
6501
+ }
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;
6510
+ }
6511
+ }
5976
6512
 
5977
- Let me start by marking the task as done using the finish_task tool.`
5978
- }
5979
- }
5980
- ]
5981
- };
5982
- case "task-summary":
5983
- return {
5984
- description: "Generate task summary",
5985
- messages: [
5986
- {
5987
- role: "user",
5988
- content: {
5989
- type: "text",
5990
- text: `Can you provide a summary of task ${params.arguments?.taskId}?`
5991
- }
5992
- },
5993
- {
5994
- role: "assistant",
5995
- content: {
5996
- type: "text",
5997
- text: `I'll generate a comprehensive summary of task ${params.arguments?.taskId} including its current status, description, and any relevant metadata.`
5998
- }
5999
- }
6000
- ]
6001
- };
6002
- default:
6003
- throw new Error(`Unknown prompt: ${params.name}`);
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;
6520
+ }
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"
6004
6533
  }
6534
+ ],
6535
+ handler: async (options) => {
6536
+ await instalar(options);
6005
6537
  }
6006
- /**
6007
- * List available resources
6008
- */
6009
- async listResources() {
6010
- return {
6011
- resources: [
6012
- {
6013
- uri: "taskin://tasks",
6014
- name: "All Tasks",
6015
- description: "Access to all tasks in the system",
6016
- mimeType: "application/json"
6017
- }
6018
- ]
6019
- };
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);
6020
6544
  }
6021
- /**
6022
- * Read a resource
6023
- */
6024
- async readResource(params) {
6025
- this.log(`Reading resource: ${params.uri}`);
6026
- const uri = params.uri;
6027
- if (uri === "taskin://tasks") {
6028
- return {
6029
- contents: [
6030
- {
6031
- uri,
6032
- mimeType: "application/json",
6033
- text: JSON.stringify({
6034
- message: "Task list would be here",
6035
- note: "Requires ITaskProvider integration"
6036
- })
6037
- }
6038
- ]
6039
- };
6040
- }
6041
- 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
+ );
6042
6572
  }
6043
- /**
6044
- * Debug logging
6045
- */
6046
- log(...args) {
6047
- if (this.config.debug) {
6048
- 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);
6049
6585
  }
6586
+ process.exit(1);
6587
+ return;
6050
6588
  }
6051
- };
6052
-
6053
- // node_modules/@opentask/taskin-task-server-mcp/dist/task-server-mcp.types.js
6054
- init_esm_shims();
6589
+ success(`The server answered with ${sonda.tools.length} tool(s): ${sonda.tools.join(", ")}`);
6590
+ }
6055
6591
 
6056
6592
  // src/commands/mcp-server.ts
6593
+ init_esm_shims();
6594
+ init_dist2();
6057
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
+ }
6058
6608
  var mcpServerCommand = defineCommand({
6059
6609
  name: "mcp-server",
6060
6610
  description: "\u{1F916} Start Model Context Protocol server for LLM integration",
6061
6611
  alias: "mcp",
6062
6612
  options: [
6063
- {
6064
- flags: "-t, --transport <type>",
6065
- description: "Transport type (stdio or sse)",
6066
- defaultValue: "stdio"
6067
- },
6068
6613
  {
6069
6614
  flags: "-d, --debug",
6070
6615
  description: "Enable debug logging"
@@ -6076,45 +6621,40 @@ var mcpServerCommand = defineCommand({
6076
6621
  });
6077
6622
  async function startMCPServer(options) {
6078
6623
  requireTaskinProject();
6079
- const transport = options.transport || "stdio";
6080
6624
  const debug = options.debug || false;
6081
- printHeader("Starting MCP Server", "\u{1F916}");
6625
+ cabecalho("Starting MCP Server", "\u{1F916}");
6082
6626
  try {
6083
- info("Initializing task manager...");
6627
+ aviso("Initializing task manager...");
6084
6628
  const { provider } = await resolveTaskProvider();
6085
6629
  const manager = new TaskManager(provider);
6086
- info(`Creating MCP server with ${transport} transport...`);
6087
6630
  const mcpServer = new TaskMCPServer({
6088
6631
  taskManager: manager,
6089
6632
  name: "taskin-mcp-server",
6090
6633
  version: "1.0.0",
6091
6634
  debug
6092
6635
  });
6093
- info("Starting MCP server...");
6094
- await mcpServer.connect({ transport });
6095
- success("\u2713 MCP server started successfully");
6096
- info("");
6097
- info(chalk6.bold("Server Information:"));
6098
- info(` \u2022 Transport: ${chalk6.cyan(transport)}`);
6099
- info(` \u2022 Debug: ${chalk6.cyan(debug ? "enabled" : "disabled")}`);
6100
- info("");
6101
- info(chalk6.bold("Available Tools:"));
6102
- info(` \u2022 ${chalk6.green("start_task")} - Start working on a task`);
6103
- info(` \u2022 ${chalk6.green("finish_task")} - Mark a task as finished`);
6104
- info("");
6105
- info(chalk6.bold("Available Prompts:"));
6106
- info(` \u2022 ${chalk6.green("start-task-workflow")} - Guide for starting tasks`);
6107
- info(` \u2022 ${chalk6.green("finish-task-workflow")} - Guide for finishing tasks`);
6108
- info(` \u2022 ${chalk6.green("task-summary")} - Get task summary and insights`);
6109
- info("");
6110
- info(chalk6.bold("Available Resources:"));
6111
- info(` \u2022 ${chalk6.green("taskin://tasks")} - Access all tasks`);
6112
- info("");
6113
- info(`Press ${chalk6.bold("Ctrl+C")} to stop the server`);
6114
- info("");
6115
- const cleanup = async () => {
6116
- info("\nShutting down MCP server...");
6117
- success("\u2713 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");
6118
6658
  process.exit(0);
6119
6659
  };
6120
6660
  process.on("SIGINT", cleanup);
@@ -6122,9 +6662,9 @@ async function startMCPServer(options) {
6122
6662
  await new Promise(() => {
6123
6663
  });
6124
6664
  } catch (err) {
6125
- error("Failed to start MCP server");
6665
+ falha("Failed to start MCP server");
6126
6666
  if (err instanceof Error) {
6127
- error(err.message);
6667
+ falha(err.message);
6128
6668
  if (debug) {
6129
6669
  console.error(err.stack);
6130
6670
  }
@@ -6159,6 +6699,10 @@ var createCommand = defineCommand({
6159
6699
  {
6160
6700
  flags: "-u, --user <user>",
6161
6701
  description: "Assignee user"
6702
+ },
6703
+ {
6704
+ flags: "--no-skip-ci",
6705
+ description: "Write the status commit without the CI-skip tag"
6162
6706
  }
6163
6707
  ],
6164
6708
  handler: async (options) => {
@@ -6234,11 +6778,12 @@ async function createTask(options, gitService) {
6234
6778
  await userRegistry.ensureCurrentUser();
6235
6779
  const configManager = new ConfigManager(monorepoRoot);
6236
6780
  const behavior = configManager.getAutomationBehavior();
6781
+ const ciSkipTag = resolveCiSkipTag(behavior.ciSkipTag, options.skipCi);
6237
6782
  const autoSyncActive = behavior.autoSync && !!behavior.defaultBranch;
6238
6783
  if (behavior.autoSync && !behavior.defaultBranch) {
6239
6784
  warning("autoSync is enabled but no defaultBranch is configured. Nothing will be synced.");
6240
6785
  }
6241
- const git = gitService ?? new GitService(process.cwd(), { ciSkipTag: behavior.ciSkipTag });
6786
+ const git = gitService ?? new GitService(process.cwd(), { ciSkipTag });
6242
6787
  if (autoSyncActive) {
6243
6788
  try {
6244
6789
  await syncBeforeCreate(git, {
@@ -6271,7 +6816,7 @@ async function createTask(options, gitService) {
6271
6816
  taskId,
6272
6817
  title: options.title,
6273
6818
  defaultBranch: behavior.defaultBranch,
6274
- ciSkipTag: behavior.ciSkipTag
6819
+ ciSkipTag
6275
6820
  });
6276
6821
  } catch (pushError) {
6277
6822
  error(
@@ -6287,7 +6832,7 @@ async function createTask(options, gitService) {
6287
6832
  console.log(colors.secondary(`\u{1F4C1} Path: ${createdPath}`));
6288
6833
  }
6289
6834
  if (autoSyncActive) {
6290
- success("\u2713 Task committed and pushed to remote");
6835
+ success("Task committed and pushed to remote");
6291
6836
  }
6292
6837
  console.log();
6293
6838
  console.log(colors.info("Next steps:"));
@@ -6442,7 +6987,7 @@ async function pauseTask(taskId, options) {
6442
6987
  console.log(colors.secondary(` - Add all changes: git add -A`));
6443
6988
  console.log(colors.secondary(` - Commit: git commit -m "${commitMessage2}"`));
6444
6989
  console.log();
6445
- info("\u2713 Dry run complete");
6990
+ info("Dry run complete");
6446
6991
  return;
6447
6992
  }
6448
6993
  if (task.status !== "in-progress") {
@@ -6476,7 +7021,7 @@ async function pauseTask(taskId, options) {
6476
7021
  }
6477
7022
  await taskManager.pauseTask(normalizedId);
6478
7023
  success("Task paused successfully!");
6479
- success("\u2713 Auto-committed work in progress");
7024
+ success("Auto-committed work in progress");
6480
7025
  info("Status updated to paused");
6481
7026
  console.log();
6482
7027
  info("Next steps:");
@@ -6593,6 +7138,10 @@ var reviewCommand = defineCommand({
6593
7138
  {
6594
7139
  flags: "--no-sound",
6595
7140
  description: "Disable review sound"
7141
+ },
7142
+ {
7143
+ flags: "--no-skip-ci",
7144
+ description: "Write the status commit without the CI-skip tag"
6596
7145
  }
6597
7146
  ],
6598
7147
  handler: async (taskId, options) => {
@@ -6624,6 +7173,7 @@ async function reviewTask(taskId, options) {
6624
7173
  const hookSettings = configManager.getHookSettings();
6625
7174
  const reviewHooks = configManager.getCommandHooks("review");
6626
7175
  const behavior = configManager.getAutomationBehavior();
7176
+ const ciSkipTag = resolveCiSkipTag(behavior.ciSkipTag, options.skipCi);
6627
7177
  const hookRunner = new HookRunner();
6628
7178
  const hookContext = {
6629
7179
  taskId: normalizedId,
@@ -6663,7 +7213,7 @@ async function reviewTask(taskId, options) {
6663
7213
  });
6664
7214
  console.log();
6665
7215
  }
6666
- info("\u2713 Dry run complete");
7216
+ info("Dry run complete");
6667
7217
  return;
6668
7218
  }
6669
7219
  if (reviewHooks.pre && reviewHooks.pre.length > 0 && !options.skipMerge) {
@@ -6682,7 +7232,7 @@ async function reviewTask(taskId, options) {
6682
7232
  const failedPre = preResults.find((r) => !r.success);
6683
7233
  if (failedPre && !hookSettings.continueOnError) {
6684
7234
  console.log();
6685
- error("\u2717 Pre-review hooks failed!");
7235
+ error("Pre-review hooks failed!");
6686
7236
  error("Fix the errors above and try again.");
6687
7237
  process.exit(1);
6688
7238
  }
@@ -6704,7 +7254,7 @@ async function reviewTask(taskId, options) {
6704
7254
  const failedCheck = duringResults.find((r) => !r.success);
6705
7255
  if (failedCheck && !hookSettings.continueOnError) {
6706
7256
  console.log();
6707
- error("\u2717 Review checks failed!");
7257
+ error("Review checks failed!");
6708
7258
  error("Fix the errors above and try again.");
6709
7259
  console.log();
6710
7260
  info("Tip: Run individual checks to see full error details");
@@ -6714,18 +7264,15 @@ async function reviewTask(taskId, options) {
6714
7264
  }
6715
7265
  info("Marking task as ready for review...");
6716
7266
  const updatedTask = await taskManager.reviewTask(task.id);
6717
- success(`\u2713 Task ${updatedTask.id} status changed to: ${updatedTask.status}`);
7267
+ success(`Task ${updatedTask.id} status changed to: ${updatedTask.status}`);
6718
7268
  if (behavior.autoCommitStatusChange) {
6719
7269
  try {
6720
- const message = appendCiSkipTag(
6721
- `docs(TASKS): task-${normalizedId} - mark as ready for review`,
6722
- behavior.ciSkipTag
6723
- );
7270
+ const message = appendCiSkipTag(`docs(TASKS): task-${normalizedId} - mark as ready for review`, ciSkipTag);
6724
7271
  execSync9(`git add TASKS/task-${normalizedId}-*.md && git commit -m "${message}"`, {
6725
7272
  cwd: monorepoRoot,
6726
7273
  stdio: "ignore"
6727
7274
  });
6728
- success("\u2713 Auto-committed status change");
7275
+ success("Auto-committed status change");
6729
7276
  } catch {
6730
7277
  }
6731
7278
  }
@@ -6746,7 +7293,7 @@ async function reviewTask(taskId, options) {
6746
7293
  console.log();
6747
7294
  }
6748
7295
  const totalDuration = [...reviewHooks.pre ?? [], ...reviewHooks.during ?? [], ...reviewHooks.post ?? []].length > 0 ? "with hooks" : "";
6749
- success(`\u2713 Task ready for review! ${totalDuration}`);
7296
+ success(`Task ready for review! ${totalDuration}`);
6750
7297
  await sendTaskNotification(configManager, "task:review", normalizedId, task.title);
6751
7298
  if (options.sound !== false) {
6752
7299
  await playSound("review");
@@ -6793,13 +7340,17 @@ var startCommand = defineCommand({
6793
7340
  {
6794
7341
  flags: "--dry-run",
6795
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"
6796
7347
  }
6797
7348
  ],
6798
7349
  handler: async (taskId, options) => {
6799
7350
  await startTask(taskId, options);
6800
7351
  }
6801
7352
  });
6802
- async function startTask(taskId, _options, gitService) {
7353
+ async function startTask(taskId, options, gitService) {
6803
7354
  requireTaskinProject();
6804
7355
  printHeader(`Starting Task ${taskId}`, "\u{1F680}");
6805
7356
  const normalizedId = normalizeTaskId(taskId);
@@ -6819,12 +7370,13 @@ async function startTask(taskId, _options, gitService) {
6819
7370
  info(`Current status: ${task.status}`);
6820
7371
  const configManager = new ConfigManager(monorepoRoot);
6821
7372
  const behavior = configManager.getAutomationBehavior();
7373
+ const ciSkipTag = resolveCiSkipTag(behavior.ciSkipTag, options.skipCi);
6822
7374
  const statusCommitMessage = buildTaskStatusCommitMessage({
6823
7375
  taskId: normalizedId,
6824
7376
  status: "in-progress",
6825
- ciSkipTag: behavior.ciSkipTag
7377
+ ciSkipTag
6826
7378
  });
6827
- if (_options.dryRun) {
7379
+ if (options.dryRun) {
6828
7380
  console.log();
6829
7381
  info("\u{1F50D} Dry run mode - showing what would be executed:");
6830
7382
  console.log();
@@ -6839,7 +7391,7 @@ async function startTask(taskId, _options, gitService) {
6839
7391
  )
6840
7392
  );
6841
7393
  console.log();
6842
- info("\u2713 Dry run complete");
7394
+ info("Dry run complete");
6843
7395
  return;
6844
7396
  }
6845
7397
  if (task.status === "in-progress") {
@@ -6854,11 +7406,11 @@ async function startTask(taskId, _options, gitService) {
6854
7406
  const updatedTask = await taskManager.startTask(task.id);
6855
7407
  success(`Task ${updatedTask.id} started successfully!`);
6856
7408
  success(`Status changed to: ${updatedTask.status}`);
6857
- const git = gitService ?? new GitService(process.cwd(), { ciSkipTag: behavior.ciSkipTag });
7409
+ const git = gitService ?? new GitService(process.cwd(), { ciSkipTag });
6858
7410
  if (behavior.autoCommitStatusChange) {
6859
7411
  const committed = await git.commitTaskStatusChangeOnBranch(normalizedId, "in-progress", behavior.defaultBranch);
6860
7412
  if (committed) {
6861
- success("\u2713 Auto-committed status change");
7413
+ success("Auto-committed status change");
6862
7414
  }
6863
7415
  }
6864
7416
  console.log();
@@ -6880,7 +7432,7 @@ async function startTask(taskId, _options, gitService) {
6880
7432
  }
6881
7433
  console.log();
6882
7434
  await sendTaskNotification(configManager, "task:start", normalizedId, task.title);
6883
- if (_options.sound !== false) {
7435
+ if (options.sound !== false) {
6884
7436
  playSound("start");
6885
7437
  }
6886
7438
  }
@@ -6890,7 +7442,7 @@ init_esm_shims();
6890
7442
  init_src3();
6891
7443
  init_src();
6892
7444
  import chalk7 from "chalk";
6893
- import path11 from "path";
7445
+ import path15 from "path";
6894
7446
  var statsCommand = defineCommand({
6895
7447
  name: "stats",
6896
7448
  description: "\u{1F4CA} Show user/team metrics and statistics",
@@ -6922,7 +7474,7 @@ var statsCommand = defineCommand({
6922
7474
  });
6923
7475
  async function showStats(options) {
6924
7476
  requireTaskinProject();
6925
- const tasksDir = path11.join(process.cwd(), "TASKS");
7477
+ const tasksDir = path15.join(process.cwd(), "TASKS");
6926
7478
  const { userRegistry } = await resolveTaskProvider();
6927
7479
  const gitAnalyzer = new GitAnalyzer(process.cwd());
6928
7480
  const metricsAdapter = new FileSystemMetricsAdapter(tasksDir, userRegistry, gitAnalyzer);
@@ -7077,102 +7629,73 @@ function getStatusEmoji(status) {
7077
7629
 
7078
7630
  // src/lib/help.ts
7079
7631
  init_esm_shims();
7080
- function showCustomHelp() {
7632
+ var EXEMPLOS = {
7633
+ init: ["taskin init", "taskin setup"],
7634
+ list: ["taskin list", "taskin list pending", "taskin list --status in-progress", "taskin list --type feat"],
7635
+ new: [
7636
+ 'taskin new -t feat -T "Add login" -d "Implement user authentication"',
7637
+ 'taskin new --type fix --title "Fix bug" --user "John"',
7638
+ 'taskin create -t docs -T "Update README"'
7639
+ ],
7640
+ start: ["taskin start 001", "taskin start task-001", "taskin start 001 --force"],
7641
+ pause: ["taskin pause 001", 'taskin pause 001 -m "saving progress"'],
7642
+ review: ["taskin review 001", "taskin review 001 --dry-run"],
7643
+ finish: ["taskin finish 001", "taskin done task-001"],
7644
+ stats: ["taskin stats", "taskin stats --team", "taskin stats --period month"],
7645
+ config: ["taskin config", "taskin config --level assisted", "taskin config --show"],
7646
+ export: ["taskin export", "taskin export --format json"],
7647
+ lint: ["taskin lint", "taskin lint --fix", "taskin lint --fix --metadata-style=list"],
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"],
7650
+ "mcp-server": ["taskin mcp-server", "taskin mcp"],
7651
+ notify: ["taskin notify --event task:done --task 001"]
7652
+ };
7653
+ var ICONE_PADRAO = "\u2022";
7654
+ function separarIcone(descricao) {
7655
+ const match = descricao.match(new RegExp("^(\\p{Extended_Pictographic}\\uFE0F?)\\s+(.*)$", "su"));
7656
+ return match?.[1] && match[2] !== void 0 ? { icone: match[1], texto: match[2] } : { icone: ICONE_PADRAO, texto: descricao };
7657
+ }
7658
+ var OCULTOS = ["help"];
7659
+ function assinatura(cmd) {
7660
+ const argumentos = cmd.usage().replace("[options]", "").trim();
7661
+ const nome = colors.highlight(`taskin ${cmd.name()}`);
7662
+ return argumentos ? nome + colors.normal(` ${argumentos}`) : nome;
7663
+ }
7664
+ function detalhes(cmd) {
7665
+ const partes = [];
7666
+ const aliases = cmd.aliases();
7667
+ if (aliases.length > 0) {
7668
+ partes.push(`Alias: ${aliases.join(", ")}`);
7669
+ }
7670
+ const flags = cmd.options.map((opcao) => opcao.flags.split(",")[0]?.trim()).filter(Boolean);
7671
+ if (flags.length > 0) {
7672
+ partes.push(`Options: ${flags.join(", ")}`);
7673
+ }
7674
+ return partes.length > 0 ? partes.join(" \xB7 ") : void 0;
7675
+ }
7676
+ function showCustomHelp(program2) {
7081
7677
  printHeader("Taskin - Task Management System", icons.rocket);
7082
7678
  console.log(colors.info("\u{1F4CB} AVAILABLE COMMANDS"));
7083
7679
  console.log(colors.highlight("\u2550".repeat(60)));
7084
7680
  console.log();
7085
- const commands = [
7086
- {
7087
- name: colors.highlight("taskin init"),
7088
- alias: colors.secondary("Alias: setup"),
7089
- description: "Initialize Taskin in your project",
7090
- examples: ["taskin init", "taskin setup"],
7091
- icon: "\u{1F3AF}"
7092
- },
7093
- {
7094
- name: colors.highlight("taskin list") + colors.normal(" [filter]"),
7095
- alias: colors.secondary("Alias: ls"),
7096
- description: "List all tasks in the project",
7097
- examples: ["taskin list", "taskin list pending", "taskin list --status in-progress", "taskin list --type feat"],
7098
- icon: "\u{1F4CA}"
7099
- },
7100
- {
7101
- name: colors.highlight("taskin new"),
7102
- alias: colors.secondary("Alias: create"),
7103
- description: "Create a new task",
7104
- examples: [
7105
- 'taskin new -t feat -T "Add login" -d "Implement user authentication"',
7106
- 'taskin new --type fix --title "Fix bug" --user "John"',
7107
- 'taskin create -t docs -T "Update README"'
7108
- ],
7109
- icon: "\u{1F4DD}"
7110
- },
7111
- {
7112
- name: colors.highlight("taskin start") + colors.normal(" <task-id>"),
7113
- alias: colors.secondary("Alias: begin"),
7114
- description: "Start working on a task (suggests commits)",
7115
- examples: ["taskin start 001", "taskin start task-001", "taskin start 001 --force"],
7116
- icon: "\u{1F680}"
7117
- },
7118
- {
7119
- name: colors.highlight("taskin pause") + colors.normal(" <task-id>"),
7120
- alias: colors.secondary("Alias: stop"),
7121
- description: "Pause a task (auto-commits work in progress)",
7122
- examples: ["taskin pause 001", 'taskin pause 001 -m "saving progress"'],
7123
- icon: "\u23F8\uFE0F"
7124
- },
7125
- {
7126
- name: colors.highlight("taskin finish") + colors.normal(" <task-id>"),
7127
- alias: colors.secondary("Alias: done"),
7128
- description: "Finish a task (suggests commits)",
7129
- examples: ["taskin finish 001", "taskin done task-001"],
7130
- icon: "\u2705"
7131
- },
7132
- {
7133
- name: colors.highlight("taskin config") + colors.normal(" [options]"),
7134
- alias: colors.secondary("Options: --level <manual|assisted|autopilot>"),
7135
- description: "Configure automation level",
7136
- examples: ["taskin config", "taskin config --level assisted", "taskin config --level autopilot"],
7137
- icon: "\u2699\uFE0F"
7138
- },
7139
- {
7140
- name: colors.highlight("taskin lint") + colors.normal(" [options]"),
7141
- alias: colors.secondary("Options: -p, --path <directory>"),
7142
- description: "Validate task markdown files",
7143
- examples: ["taskin lint", "taskin lint --path ./TASKS", "taskin lint -p /path/to/tasks"],
7144
- icon: "\u{1F50D}"
7145
- },
7146
- {
7147
- name: colors.highlight("taskin dashboard") + colors.normal(" [options]"),
7148
- alias: colors.secondary("Options: --host, --port, --filter-open, --filter-closed"),
7149
- description: "Start the web dashboard",
7150
- examples: [
7151
- "taskin dashboard",
7152
- "taskin dashboard --port 3000",
7153
- "taskin dashboard --filter-open",
7154
- "taskin dashboard --filter-closed -o"
7155
- ],
7156
- icon: "\u{1F4CA}"
7157
- },
7158
- {
7159
- name: colors.highlight("taskin mcp-server"),
7160
- alias: colors.secondary("Alias: mcp"),
7161
- description: "Start MCP server for Claude Desktop integration",
7162
- examples: ["taskin mcp-server", "taskin mcp"],
7163
- icon: "\u{1F916}"
7681
+ const comandos = program2.commands.filter((cmd) => !OCULTOS.includes(cmd.name()));
7682
+ comandos.forEach((cmd, index) => {
7683
+ const exemplos = EXEMPLOS[cmd.name()] ?? [];
7684
+ const { icone, texto } = separarIcone(cmd.description());
7685
+ console.log(colors.warning(`${icone} ${assinatura(cmd)}`));
7686
+ const linhaDeDetalhes = detalhes(cmd);
7687
+ if (linhaDeDetalhes) {
7688
+ console.log(colors.normal(` ${colors.secondary(linhaDeDetalhes)}`));
7689
+ }
7690
+ console.log(colors.info(` ${texto}`));
7691
+ if (exemplos.length > 0) {
7692
+ console.log();
7693
+ console.log(colors.normal(` ${colors.info("\u{1F4DD} Examples:")}`));
7694
+ for (const exemplo of exemplos) {
7695
+ console.log(colors.secondary(` ${exemplo}`));
7696
+ }
7164
7697
  }
7165
- ];
7166
- commands.forEach((cmd, index) => {
7167
- console.log(colors.warning(`${cmd.icon} ${cmd.name}`));
7168
- console.log(colors.normal(` ${cmd.alias}`));
7169
- console.log(colors.info(` ${cmd.description}`));
7170
- console.log();
7171
- console.log(colors.normal(` ${colors.info("\u{1F4DD} Examples:")}`));
7172
- cmd.examples.forEach((example) => {
7173
- console.log(colors.secondary(` ${example}`));
7174
- });
7175
- if (index < commands.length - 1) {
7698
+ if (index < comandos.length - 1) {
7176
7699
  console.log();
7177
7700
  console.log(colors.normal(` ${colors.secondary("\u2500".repeat(50))}`));
7178
7701
  console.log();
@@ -7207,7 +7730,7 @@ function showCustomHelp() {
7207
7730
 
7208
7731
  // src/version.ts
7209
7732
  init_esm_shims();
7210
- import { readFileSync as readFileSync3 } from "fs";
7733
+ import { readFileSync as readFileSync6 } from "fs";
7211
7734
  import { dirname, join as join4 } from "path";
7212
7735
  import { fileURLToPath as fileURLToPath3 } from "url";
7213
7736
  var __filename3 = fileURLToPath3(import.meta.url);
@@ -7215,7 +7738,7 @@ var __dirname3 = dirname(__filename3);
7215
7738
  function getVersion() {
7216
7739
  try {
7217
7740
  const packageJsonPath = join4(__dirname3, "../package.json");
7218
- const packageJson = JSON.parse(readFileSync3(packageJsonPath, "utf-8"));
7741
+ const packageJson = JSON.parse(readFileSync6(packageJsonPath, "utf-8"));
7219
7742
  return packageJson.version;
7220
7743
  } catch {
7221
7744
  return "0.0.0";
@@ -7574,7 +8097,7 @@ var program = new Command();
7574
8097
  program.name("taskin").description("\u{1F680} Task Management System").version(getVersion());
7575
8098
  program.helpOption("-h, --help", "Display help information");
7576
8099
  program.command("help").description("Show help information").action(() => {
7577
- showCustomHelp();
8100
+ showCustomHelp(program);
7578
8101
  });
7579
8102
  initCommand(program);
7580
8103
  listCommand(program);
@@ -7588,18 +8111,19 @@ configCommand(program);
7588
8111
  registerExportCommand(program);
7589
8112
  lintCommand(program);
7590
8113
  dashboardCommand(program);
8114
+ mcpInstallCommand(program);
7591
8115
  mcpServerCommand(program);
7592
8116
  notifyCommand(program);
7593
8117
  program.on("option:help", () => {
7594
- showCustomHelp();
8118
+ showCustomHelp(program);
7595
8119
  process.exit(0);
7596
8120
  });
7597
8121
  if (process.argv.length <= 2) {
7598
- showCustomHelp();
8122
+ showCustomHelp(program);
7599
8123
  process.exit(0);
7600
8124
  }
7601
8125
  if (process.argv.length === 3 && (process.argv[2] === "--help" || process.argv[2] === "-h")) {
7602
- showCustomHelp();
8126
+ showCustomHelp(program);
7603
8127
  process.exit(0);
7604
8128
  }
7605
8129
  program.parse();