@proletariat/cli 0.3.22 → 0.3.24

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 (173) hide show
  1. package/LICENSE +190 -21
  2. package/README.md +7 -7
  3. package/dist/commands/action/create.js +1 -1
  4. package/dist/commands/agent/{temp/cleanup.d.ts → cleanup.d.ts} +1 -1
  5. package/dist/commands/agent/{temp/cleanup.js → cleanup.js} +4 -4
  6. package/dist/commands/agent/index.js +8 -8
  7. package/dist/commands/branch/create.js +2 -2
  8. package/dist/commands/epic/create.d.ts +1 -0
  9. package/dist/commands/epic/create.js +39 -2
  10. package/dist/commands/epic/index.js +2 -2
  11. package/dist/commands/{epic/link/remove.d.ts → link/create.d.ts} +6 -7
  12. package/dist/commands/link/create.js +141 -0
  13. package/dist/commands/{epic/link/relates.d.ts → link/index.d.ts} +4 -5
  14. package/dist/commands/link/index.js +87 -0
  15. package/dist/commands/{epic/link/duplicates.d.ts → link/list.d.ts} +7 -4
  16. package/dist/commands/link/list.js +182 -0
  17. package/dist/commands/{spec/link → link}/remove.d.ts +4 -5
  18. package/dist/commands/link/remove.js +120 -0
  19. package/dist/commands/mcp-server.d.ts +22 -0
  20. package/dist/commands/mcp-server.js +98 -0
  21. package/dist/commands/phase/create.js +1 -1
  22. package/dist/commands/project/create.d.ts +1 -0
  23. package/dist/commands/project/create.js +38 -4
  24. package/dist/commands/spec/create.d.ts +1 -0
  25. package/dist/commands/spec/create.js +43 -2
  26. package/dist/commands/spec/index.js +2 -2
  27. package/dist/commands/{agent/staff → staff}/add.js +10 -10
  28. package/dist/commands/{agent/staff → staff}/index.d.ts +1 -1
  29. package/dist/commands/{agent/staff → staff}/index.js +7 -7
  30. package/dist/commands/{agent/staff → staff}/list.js +3 -3
  31. package/dist/commands/{agent/staff → staff}/remove.d.ts +1 -1
  32. package/dist/commands/{agent/staff → staff}/remove.js +8 -8
  33. package/dist/commands/{ticket/template → template}/apply.d.ts +8 -6
  34. package/dist/commands/template/apply.js +262 -0
  35. package/dist/commands/{ticket/template → template}/create.d.ts +5 -6
  36. package/dist/commands/template/create.js +238 -0
  37. package/dist/commands/template/index.js +48 -36
  38. package/dist/commands/{ticket/template → template}/save.d.ts +2 -2
  39. package/dist/commands/template/save.js +104 -0
  40. package/dist/commands/{phase/template → template}/update.d.ts +2 -2
  41. package/dist/commands/template/update.js +99 -0
  42. package/dist/commands/{agent/themes → theme}/add-names.d.ts +1 -1
  43. package/dist/commands/{agent/themes → theme}/add-names.js +6 -6
  44. package/dist/commands/{agent/themes → theme}/create.d.ts +1 -1
  45. package/dist/commands/{agent/themes → theme}/create.js +5 -5
  46. package/dist/commands/{agent/themes → theme}/index.d.ts +1 -1
  47. package/dist/commands/{agent/themes → theme}/index.js +10 -10
  48. package/dist/commands/{agent/themes → theme}/list.d.ts +1 -1
  49. package/dist/commands/{agent/themes → theme}/list.js +5 -5
  50. package/dist/commands/{agent/themes → theme}/set.d.ts +1 -1
  51. package/dist/commands/{agent/themes → theme}/set.js +7 -7
  52. package/dist/commands/ticket/create.d.ts +1 -0
  53. package/dist/commands/ticket/create.js +54 -2
  54. package/dist/commands/ticket/index.js +6 -6
  55. package/dist/commands/work/spawn.js +1 -1
  56. package/dist/lib/mcp/helpers.d.ts +43 -0
  57. package/dist/lib/mcp/helpers.js +57 -0
  58. package/dist/lib/mcp/index.d.ts +6 -0
  59. package/dist/lib/mcp/index.js +6 -0
  60. package/dist/lib/mcp/tools/action.d.ts +6 -0
  61. package/dist/lib/mcp/tools/action.js +88 -0
  62. package/dist/lib/mcp/tools/board.d.ts +6 -0
  63. package/dist/lib/mcp/tools/board.js +139 -0
  64. package/dist/lib/mcp/tools/category.d.ts +6 -0
  65. package/dist/lib/mcp/tools/category.js +84 -0
  66. package/dist/lib/mcp/tools/cli-passthrough.d.ts +15 -0
  67. package/dist/lib/mcp/tools/cli-passthrough.js +333 -0
  68. package/dist/lib/mcp/tools/epic.d.ts +6 -0
  69. package/dist/lib/mcp/tools/epic.js +178 -0
  70. package/dist/lib/mcp/tools/index.d.ts +18 -0
  71. package/dist/lib/mcp/tools/index.js +19 -0
  72. package/dist/lib/mcp/tools/phase.d.ts +6 -0
  73. package/dist/lib/mcp/tools/phase.js +131 -0
  74. package/dist/lib/mcp/tools/project.d.ts +6 -0
  75. package/dist/lib/mcp/tools/project.js +196 -0
  76. package/dist/lib/mcp/tools/roadmap.d.ts +6 -0
  77. package/dist/lib/mcp/tools/roadmap.js +123 -0
  78. package/dist/lib/mcp/tools/spec.d.ts +6 -0
  79. package/dist/lib/mcp/tools/spec.js +196 -0
  80. package/dist/lib/mcp/tools/status.d.ts +6 -0
  81. package/dist/lib/mcp/tools/status.js +109 -0
  82. package/dist/lib/mcp/tools/template.d.ts +6 -0
  83. package/dist/lib/mcp/tools/template.js +107 -0
  84. package/dist/lib/mcp/tools/ticket.d.ts +6 -0
  85. package/dist/lib/mcp/tools/ticket.js +393 -0
  86. package/dist/lib/mcp/tools/view.d.ts +6 -0
  87. package/dist/lib/mcp/tools/view.js +76 -0
  88. package/dist/lib/mcp/tools/work.d.ts +6 -0
  89. package/dist/lib/mcp/tools/work.js +132 -0
  90. package/dist/lib/mcp/tools/workflow.d.ts +6 -0
  91. package/dist/lib/mcp/tools/workflow.js +95 -0
  92. package/dist/lib/mcp/types.d.ts +17 -0
  93. package/dist/lib/mcp/types.js +4 -0
  94. package/dist/lib/prompt-json.d.ts +52 -1
  95. package/dist/lib/prompt-json.js +45 -0
  96. package/oclif.manifest.json +3553 -5457
  97. package/package.json +10 -7
  98. package/dist/commands/agent/temp/index.d.ts +0 -14
  99. package/dist/commands/agent/temp/index.js +0 -85
  100. package/dist/commands/agent/temp/list.d.ts +0 -7
  101. package/dist/commands/agent/temp/list.js +0 -108
  102. package/dist/commands/epic/link/block.d.ts +0 -14
  103. package/dist/commands/epic/link/block.js +0 -81
  104. package/dist/commands/epic/link/duplicates.js +0 -68
  105. package/dist/commands/epic/link/index.d.ts +0 -19
  106. package/dist/commands/epic/link/index.js +0 -272
  107. package/dist/commands/epic/link/relates.js +0 -68
  108. package/dist/commands/epic/link/remove.js +0 -93
  109. package/dist/commands/phase/template/apply.d.ts +0 -17
  110. package/dist/commands/phase/template/apply.js +0 -108
  111. package/dist/commands/phase/template/create.d.ts +0 -17
  112. package/dist/commands/phase/template/create.js +0 -104
  113. package/dist/commands/phase/template/delete.d.ts +0 -17
  114. package/dist/commands/phase/template/delete.js +0 -100
  115. package/dist/commands/phase/template/index.d.ts +0 -15
  116. package/dist/commands/phase/template/index.js +0 -130
  117. package/dist/commands/phase/template/list.d.ts +0 -16
  118. package/dist/commands/phase/template/list.js +0 -97
  119. package/dist/commands/phase/template/update.js +0 -89
  120. package/dist/commands/spec/link/depends.d.ts +0 -14
  121. package/dist/commands/spec/link/depends.js +0 -64
  122. package/dist/commands/spec/link/duplicates.d.ts +0 -14
  123. package/dist/commands/spec/link/duplicates.js +0 -63
  124. package/dist/commands/spec/link/index.d.ts +0 -19
  125. package/dist/commands/spec/link/index.js +0 -207
  126. package/dist/commands/spec/link/relates.d.ts +0 -14
  127. package/dist/commands/spec/link/relates.js +0 -63
  128. package/dist/commands/spec/link/remove.js +0 -96
  129. package/dist/commands/template/phase/apply.d.ts +0 -14
  130. package/dist/commands/template/phase/apply.js +0 -43
  131. package/dist/commands/template/phase/create.d.ts +0 -13
  132. package/dist/commands/template/phase/create.js +0 -38
  133. package/dist/commands/template/phase/delete.d.ts +0 -13
  134. package/dist/commands/template/phase/delete.js +0 -36
  135. package/dist/commands/template/phase/index.d.ts +0 -10
  136. package/dist/commands/template/phase/index.js +0 -63
  137. package/dist/commands/template/phase/list.d.ts +0 -11
  138. package/dist/commands/template/phase/list.js +0 -36
  139. package/dist/commands/template/phase/update.d.ts +0 -14
  140. package/dist/commands/template/phase/update.js +0 -43
  141. package/dist/commands/template/ticket/apply.d.ts +0 -17
  142. package/dist/commands/template/ticket/apply.js +0 -60
  143. package/dist/commands/template/ticket/create.d.ts +0 -20
  144. package/dist/commands/template/ticket/create.js +0 -89
  145. package/dist/commands/template/ticket/delete.d.ts +0 -13
  146. package/dist/commands/template/ticket/delete.js +0 -38
  147. package/dist/commands/template/ticket/index.d.ts +0 -10
  148. package/dist/commands/template/ticket/index.js +0 -63
  149. package/dist/commands/template/ticket/list.d.ts +0 -11
  150. package/dist/commands/template/ticket/list.js +0 -36
  151. package/dist/commands/template/ticket/save.d.ts +0 -15
  152. package/dist/commands/template/ticket/save.js +0 -46
  153. package/dist/commands/ticket/link/block.d.ts +0 -14
  154. package/dist/commands/ticket/link/block.js +0 -96
  155. package/dist/commands/ticket/link/duplicates.d.ts +0 -14
  156. package/dist/commands/ticket/link/duplicates.js +0 -95
  157. package/dist/commands/ticket/link/index.d.ts +0 -19
  158. package/dist/commands/ticket/link/index.js +0 -256
  159. package/dist/commands/ticket/link/relates.d.ts +0 -14
  160. package/dist/commands/ticket/link/relates.js +0 -95
  161. package/dist/commands/ticket/link/remove.d.ts +0 -16
  162. package/dist/commands/ticket/link/remove.js +0 -132
  163. package/dist/commands/ticket/template/apply.js +0 -252
  164. package/dist/commands/ticket/template/create.js +0 -386
  165. package/dist/commands/ticket/template/delete.d.ts +0 -17
  166. package/dist/commands/ticket/template/delete.js +0 -94
  167. package/dist/commands/ticket/template/index.d.ts +0 -15
  168. package/dist/commands/ticket/template/index.js +0 -120
  169. package/dist/commands/ticket/template/list.d.ts +0 -16
  170. package/dist/commands/ticket/template/list.js +0 -112
  171. package/dist/commands/ticket/template/save.js +0 -163
  172. /package/dist/commands/{agent/staff → staff}/add.d.ts +0 -0
  173. /package/dist/commands/{agent/staff → staff}/list.d.ts +0 -0
@@ -0,0 +1,109 @@
1
+ /**
2
+ * MCP Status Tools
3
+ */
4
+ import { z } from 'zod';
5
+ import { errorResponse } from '../helpers.js';
6
+ export function registerStatusTools(server, ctx) {
7
+ server.tool('status_list', 'List statuses in a workflow', { workflow_id: z.string().describe('Workflow ID') }, async (params) => {
8
+ try {
9
+ const statuses = await ctx.storage.listStatuses(params.workflow_id);
10
+ return {
11
+ content: [{
12
+ type: 'text',
13
+ text: JSON.stringify({
14
+ success: true,
15
+ statuses: statuses.map((s) => ({
16
+ id: s.id,
17
+ name: s.name,
18
+ category: s.category,
19
+ position: s.position,
20
+ isDefault: s.isDefault,
21
+ })),
22
+ }, null, 2),
23
+ }],
24
+ };
25
+ }
26
+ catch (error) {
27
+ return errorResponse(error);
28
+ }
29
+ });
30
+ server.tool('status_create', 'Create a status in a workflow', {
31
+ workflow_id: z.string().describe('Workflow ID'),
32
+ name: z.string().describe('Status name'),
33
+ category: z.enum(['triage', 'backlog', 'unstarted', 'started', 'completed', 'canceled']).describe('Category'),
34
+ position: z.number().optional(),
35
+ is_default: z.boolean().optional(),
36
+ }, async (params) => {
37
+ try {
38
+ const status = await ctx.storage.createStatus(params.workflow_id, {
39
+ name: params.name,
40
+ category: params.category,
41
+ position: params.position,
42
+ isDefault: params.is_default,
43
+ });
44
+ return {
45
+ content: [{
46
+ type: 'text',
47
+ text: JSON.stringify({ success: true, status: { id: status.id, name: status.name, category: status.category } }, null, 2),
48
+ }],
49
+ };
50
+ }
51
+ catch (error) {
52
+ return errorResponse(error);
53
+ }
54
+ });
55
+ server.tool('status_update', 'Update a status', {
56
+ id: z.string().describe('Status ID'),
57
+ name: z.string().optional(),
58
+ category: z.enum(['triage', 'backlog', 'unstarted', 'started', 'completed', 'canceled']).optional(),
59
+ }, async (params) => {
60
+ try {
61
+ const changes = {};
62
+ if (params.name)
63
+ changes.name = params.name;
64
+ if (params.category)
65
+ changes.category = params.category;
66
+ const status = await ctx.storage.updateStatus(params.id, changes);
67
+ return {
68
+ content: [{
69
+ type: 'text',
70
+ text: JSON.stringify({ success: true, status: { id: status.id, name: status.name } }, null, 2),
71
+ }],
72
+ };
73
+ }
74
+ catch (error) {
75
+ return errorResponse(error);
76
+ }
77
+ });
78
+ server.tool('status_reorder', 'Reorder a status', {
79
+ id: z.string().describe('Status ID'),
80
+ position: z.number().describe('New position'),
81
+ }, async (params) => {
82
+ try {
83
+ const status = await ctx.storage.reorderStatus(params.id, params.position);
84
+ return {
85
+ content: [{
86
+ type: 'text',
87
+ text: JSON.stringify({ success: true, status: { id: status.id, position: status.position } }, null, 2),
88
+ }],
89
+ };
90
+ }
91
+ catch (error) {
92
+ return errorResponse(error);
93
+ }
94
+ });
95
+ server.tool('status_delete', 'Delete a status', { id: z.string().describe('Status ID') }, async (params) => {
96
+ try {
97
+ await ctx.storage.deleteStatus(params.id);
98
+ return {
99
+ content: [{
100
+ type: 'text',
101
+ text: JSON.stringify({ success: true, message: 'Status deleted' }, null, 2),
102
+ }],
103
+ };
104
+ }
105
+ catch (error) {
106
+ return errorResponse(error);
107
+ }
108
+ });
109
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * MCP Template Tools
3
+ */
4
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
5
+ import type { McpToolContext } from '../types.js';
6
+ export declare function registerTemplateTools(server: McpServer, ctx: McpToolContext): void;
@@ -0,0 +1,107 @@
1
+ /**
2
+ * MCP Template Tools
3
+ */
4
+ import { z } from 'zod';
5
+ import { errorResponse } from '../helpers.js';
6
+ export function registerTemplateTools(server, ctx) {
7
+ server.tool('ticket_template_list', 'List ticket templates', { include_builtin: z.boolean().optional() }, async (params) => {
8
+ try {
9
+ const templates = await ctx.storage.listTicketTemplates({
10
+ isBuiltin: params.include_builtin ? undefined : false,
11
+ });
12
+ return {
13
+ content: [{
14
+ type: 'text',
15
+ text: JSON.stringify({
16
+ success: true,
17
+ templates: templates.map((t) => ({
18
+ id: t.id,
19
+ name: t.name,
20
+ description: t.description,
21
+ isBuiltin: t.isBuiltin,
22
+ })),
23
+ }, null, 2),
24
+ }],
25
+ };
26
+ }
27
+ catch (error) {
28
+ return errorResponse(error);
29
+ }
30
+ });
31
+ server.tool('ticket_template_show', 'Get ticket template details', { id: z.string().describe('Template ID') }, async (params) => {
32
+ try {
33
+ const template = await ctx.storage.getTicketTemplate(params.id);
34
+ if (!template)
35
+ throw new Error(`Template not found: ${params.id}`);
36
+ return {
37
+ content: [{
38
+ type: 'text',
39
+ text: JSON.stringify({ success: true, template }, null, 2),
40
+ }],
41
+ };
42
+ }
43
+ catch (error) {
44
+ return errorResponse(error);
45
+ }
46
+ });
47
+ server.tool('ticket_template_create', 'Create a ticket template', {
48
+ name: z.string().describe('Template name'),
49
+ description: z.string().optional(),
50
+ title_pattern: z.string().optional(),
51
+ description_template: z.string().optional(),
52
+ default_priority: z.enum(['P0', 'P1', 'P2', 'P3']).optional(),
53
+ default_category: z.string().optional(),
54
+ }, async (params) => {
55
+ try {
56
+ const template = await ctx.storage.createTicketTemplate({
57
+ name: params.name,
58
+ description: params.description,
59
+ titlePattern: params.title_pattern,
60
+ descriptionTemplate: params.description_template,
61
+ defaultPriority: params.default_priority,
62
+ defaultCategory: params.default_category,
63
+ });
64
+ return {
65
+ content: [{
66
+ type: 'text',
67
+ text: JSON.stringify({ success: true, template: { id: template.id, name: template.name } }, null, 2),
68
+ }],
69
+ };
70
+ }
71
+ catch (error) {
72
+ return errorResponse(error);
73
+ }
74
+ });
75
+ server.tool('ticket_template_create_from_ticket', 'Create template from existing ticket', {
76
+ ticket_id: z.string().describe('Ticket ID'),
77
+ name: z.string().describe('Template name'),
78
+ description: z.string().optional(),
79
+ }, async (params) => {
80
+ try {
81
+ const template = await ctx.storage.createTicketTemplateFromTicket(params.ticket_id, params.name, params.description);
82
+ return {
83
+ content: [{
84
+ type: 'text',
85
+ text: JSON.stringify({ success: true, template: { id: template.id, name: template.name } }, null, 2),
86
+ }],
87
+ };
88
+ }
89
+ catch (error) {
90
+ return errorResponse(error);
91
+ }
92
+ });
93
+ server.tool('ticket_template_delete', 'Delete a ticket template', { id: z.string().describe('Template ID') }, async (params) => {
94
+ try {
95
+ await ctx.storage.deleteTicketTemplate(params.id);
96
+ return {
97
+ content: [{
98
+ type: 'text',
99
+ text: JSON.stringify({ success: true, message: 'Template deleted' }, null, 2),
100
+ }],
101
+ };
102
+ }
103
+ catch (error) {
104
+ return errorResponse(error);
105
+ }
106
+ });
107
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * MCP Ticket Tools
3
+ */
4
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
5
+ import type { McpToolContext } from '../types.js';
6
+ export declare function registerTicketTools(server: McpServer, ctx: McpToolContext): void;
@@ -0,0 +1,393 @@
1
+ /**
2
+ * MCP Ticket Tools
3
+ */
4
+ import { z } from 'zod';
5
+ import { formatTicket, formatTicketFull, errorResponse } from '../helpers.js';
6
+ export function registerTicketTools(server, ctx) {
7
+ server.tool('ticket_list', 'List tickets with optional filters', {
8
+ project: z.string().optional().describe('Project ID'),
9
+ column: z.string().optional().describe('Filter by column/status'),
10
+ priority: z.enum(['P0', 'P1', 'P2', 'P3']).optional().describe('Filter by priority'),
11
+ category: z.string().optional().describe('Filter by category'),
12
+ assignee: z.string().optional().describe('Filter by assignee'),
13
+ owner: z.string().optional().describe('Filter by owner'),
14
+ search: z.string().optional().describe('Search in title/description'),
15
+ epic: z.string().optional().describe('Filter by epic ID'),
16
+ all_projects: z.boolean().optional().describe('List from all projects'),
17
+ }, async (params) => {
18
+ try {
19
+ const tickets = await ctx.storage.listTickets(params.all_projects ? undefined : params.project, {
20
+ column: params.column,
21
+ priority: params.priority,
22
+ category: params.category,
23
+ assignee: params.assignee,
24
+ owner: params.owner,
25
+ search: params.search,
26
+ epic: params.epic,
27
+ allProjects: params.all_projects,
28
+ });
29
+ return {
30
+ content: [{
31
+ type: 'text',
32
+ text: JSON.stringify({
33
+ success: true,
34
+ count: tickets.length,
35
+ tickets: tickets.map((t) => ({
36
+ id: t.id,
37
+ title: t.title,
38
+ description: t.description,
39
+ priority: t.priority,
40
+ category: t.category,
41
+ statusName: t.statusName,
42
+ statusCategory: t.statusCategory,
43
+ projectId: t.projectId,
44
+ assignee: t.assignee,
45
+ owner: t.owner,
46
+ epicId: t.epicId,
47
+ branch: t.branch,
48
+ createdAt: t.createdAt.toISOString(),
49
+ updatedAt: t.updatedAt.toISOString(),
50
+ })),
51
+ }, null, 2),
52
+ }],
53
+ };
54
+ }
55
+ catch (error) {
56
+ return errorResponse(error);
57
+ }
58
+ });
59
+ server.tool('ticket_create', 'Create a new ticket', {
60
+ title: z.string().describe('Ticket title (required)'),
61
+ project: z.string().optional().describe('Project ID'),
62
+ description: z.string().optional().describe('Ticket description'),
63
+ priority: z.enum(['P0', 'P1', 'P2', 'P3']).optional().describe('Priority'),
64
+ category: z.string().optional().describe('Category (feature, bug, etc.)'),
65
+ column: z.string().optional().describe('Column/status name'),
66
+ assignee: z.string().optional().describe('Assignee'),
67
+ owner: z.string().optional().describe('Owner'),
68
+ epic_id: z.string().optional().describe('Epic ID to link'),
69
+ labels: z.array(z.string()).optional().describe('Labels to add'),
70
+ subtasks: z.array(z.string()).optional().describe('Subtasks to add'),
71
+ }, async (params) => {
72
+ try {
73
+ let projectId = params.project;
74
+ if (!projectId) {
75
+ const projects = await ctx.storage.listProjects();
76
+ if (projects.length === 0)
77
+ throw new Error('No projects found');
78
+ projectId = projects[0].id;
79
+ }
80
+ const ticket = await ctx.storage.createTicket(projectId, {
81
+ title: params.title,
82
+ description: params.description,
83
+ priority: params.priority,
84
+ category: params.category,
85
+ statusName: params.column,
86
+ assignee: params.assignee,
87
+ owner: params.owner,
88
+ epicId: params.epic_id,
89
+ labels: params.labels,
90
+ subtasks: params.subtasks?.map((title) => ({ id: '', title, done: false })),
91
+ });
92
+ return {
93
+ content: [{
94
+ type: 'text',
95
+ text: JSON.stringify({ success: true, ticket: formatTicket(ticket) }, null, 2),
96
+ }],
97
+ };
98
+ }
99
+ catch (error) {
100
+ return errorResponse(error);
101
+ }
102
+ });
103
+ server.tool('ticket_show', 'Get detailed ticket information', { id: z.string().describe('Ticket ID') }, async (params) => {
104
+ try {
105
+ const ticket = await ctx.storage.getTicket(params.id);
106
+ if (!ticket)
107
+ throw new Error(`Ticket not found: ${params.id}`);
108
+ return {
109
+ content: [{
110
+ type: 'text',
111
+ text: JSON.stringify({ success: true, ticket: formatTicketFull(ticket) }, null, 2),
112
+ }],
113
+ };
114
+ }
115
+ catch (error) {
116
+ return errorResponse(error);
117
+ }
118
+ });
119
+ server.tool('ticket_update', 'Update a ticket', {
120
+ id: z.string().describe('Ticket ID'),
121
+ title: z.string().optional().describe('New title'),
122
+ description: z.string().optional().describe('New description'),
123
+ priority: z.enum(['P0', 'P1', 'P2', 'P3']).optional().describe('New priority'),
124
+ category: z.string().optional().describe('New category'),
125
+ assignee: z.string().optional().describe('New assignee'),
126
+ owner: z.string().optional().describe('New owner'),
127
+ branch: z.string().optional().describe('Git branch'),
128
+ }, async (params) => {
129
+ try {
130
+ const changes = {};
131
+ if (params.title !== undefined)
132
+ changes.title = params.title;
133
+ if (params.description !== undefined)
134
+ changes.description = params.description;
135
+ if (params.priority !== undefined)
136
+ changes.priority = params.priority;
137
+ if (params.category !== undefined)
138
+ changes.category = params.category;
139
+ if (params.assignee !== undefined)
140
+ changes.assignee = params.assignee;
141
+ if (params.owner !== undefined)
142
+ changes.owner = params.owner;
143
+ if (params.branch !== undefined)
144
+ changes.branch = params.branch;
145
+ const ticket = await ctx.storage.updateTicket(params.id, changes);
146
+ return {
147
+ content: [{
148
+ type: 'text',
149
+ text: JSON.stringify({ success: true, ticket: formatTicket(ticket) }, null, 2),
150
+ }],
151
+ };
152
+ }
153
+ catch (error) {
154
+ return errorResponse(error);
155
+ }
156
+ });
157
+ server.tool('ticket_move', 'Move ticket to a different column/status', {
158
+ id: z.string().describe('Ticket ID'),
159
+ column: z.string().describe('Target column/status'),
160
+ position: z.number().optional().describe('Position in column'),
161
+ }, async (params) => {
162
+ try {
163
+ const ticket = await ctx.storage.getTicket(params.id);
164
+ if (!ticket)
165
+ throw new Error(`Ticket not found: ${params.id}`);
166
+ const moved = await ctx.storage.moveTicket(ticket.projectId, params.id, params.column, params.position);
167
+ return {
168
+ content: [{
169
+ type: 'text',
170
+ text: JSON.stringify({ success: true, ticket: formatTicket(moved) }, null, 2),
171
+ }],
172
+ };
173
+ }
174
+ catch (error) {
175
+ return errorResponse(error);
176
+ }
177
+ });
178
+ server.tool('ticket_delete', 'Delete a ticket', { id: z.string().describe('Ticket ID') }, async (params) => {
179
+ try {
180
+ await ctx.storage.deleteTicket(params.id);
181
+ return {
182
+ content: [{
183
+ type: 'text',
184
+ text: JSON.stringify({ success: true, message: `Deleted ${params.id}` }, null, 2),
185
+ }],
186
+ };
187
+ }
188
+ catch (error) {
189
+ return errorResponse(error);
190
+ }
191
+ });
192
+ server.tool('ticket_move_to_project', 'Move ticket to a different project', {
193
+ id: z.string().describe('Ticket ID'),
194
+ project: z.string().describe('Target project ID'),
195
+ }, async (params) => {
196
+ try {
197
+ const ticket = await ctx.storage.moveTicketToProject(params.id, params.project);
198
+ return {
199
+ content: [{
200
+ type: 'text',
201
+ text: JSON.stringify({ success: true, ticket: formatTicket(ticket) }, null, 2),
202
+ }],
203
+ };
204
+ }
205
+ catch (error) {
206
+ return errorResponse(error);
207
+ }
208
+ });
209
+ server.tool('ticket_add_subtask', 'Add a subtask to a ticket', {
210
+ ticket_id: z.string().describe('Ticket ID'),
211
+ title: z.string().describe('Subtask title'),
212
+ }, async (params) => {
213
+ try {
214
+ const subtask = await ctx.storage.addSubtask(params.ticket_id, params.title);
215
+ return {
216
+ content: [{
217
+ type: 'text',
218
+ text: JSON.stringify({ success: true, subtask }, null, 2),
219
+ }],
220
+ };
221
+ }
222
+ catch (error) {
223
+ return errorResponse(error);
224
+ }
225
+ });
226
+ server.tool('ticket_toggle_subtask', 'Toggle subtask completion', {
227
+ ticket_id: z.string().describe('Ticket ID'),
228
+ subtask_id: z.string().describe('Subtask ID'),
229
+ }, async (params) => {
230
+ try {
231
+ const subtask = await ctx.storage.toggleSubtask(params.ticket_id, params.subtask_id);
232
+ return {
233
+ content: [{
234
+ type: 'text',
235
+ text: JSON.stringify({ success: true, subtask }, null, 2),
236
+ }],
237
+ };
238
+ }
239
+ catch (error) {
240
+ return errorResponse(error);
241
+ }
242
+ });
243
+ server.tool('ticket_remove_subtask', 'Remove a subtask', {
244
+ ticket_id: z.string().describe('Ticket ID'),
245
+ subtask_id: z.string().describe('Subtask ID'),
246
+ }, async (params) => {
247
+ try {
248
+ await ctx.storage.removeSubtask(params.ticket_id, params.subtask_id);
249
+ return {
250
+ content: [{
251
+ type: 'text',
252
+ text: JSON.stringify({ success: true, message: 'Subtask removed' }, null, 2),
253
+ }],
254
+ };
255
+ }
256
+ catch (error) {
257
+ return errorResponse(error);
258
+ }
259
+ });
260
+ server.tool('ticket_add_acceptance_criterion', 'Add acceptance criterion to a ticket', {
261
+ ticket_id: z.string().describe('Ticket ID'),
262
+ criterion: z.string().describe('Acceptance criterion text'),
263
+ }, async (params) => {
264
+ try {
265
+ const ac = await ctx.storage.addAcceptanceCriterion(params.ticket_id, params.criterion);
266
+ return {
267
+ content: [{
268
+ type: 'text',
269
+ text: JSON.stringify({ success: true, acceptanceCriterion: ac }, null, 2),
270
+ }],
271
+ };
272
+ }
273
+ catch (error) {
274
+ return errorResponse(error);
275
+ }
276
+ });
277
+ server.tool('ticket_remove_acceptance_criterion', 'Remove acceptance criterion', {
278
+ ticket_id: z.string().describe('Ticket ID'),
279
+ criterion_id: z.string().describe('Criterion ID'),
280
+ }, async (params) => {
281
+ try {
282
+ await ctx.storage.removeAcceptanceCriterion(params.ticket_id, params.criterion_id);
283
+ return {
284
+ content: [{
285
+ type: 'text',
286
+ text: JSON.stringify({ success: true, message: 'Criterion removed' }, null, 2),
287
+ }],
288
+ };
289
+ }
290
+ catch (error) {
291
+ return errorResponse(error);
292
+ }
293
+ });
294
+ server.tool('ticket_link_to_epic', 'Link ticket to an epic', {
295
+ ticket_id: z.string().describe('Ticket ID'),
296
+ epic_id: z.string().describe('Epic ID'),
297
+ }, async (params) => {
298
+ try {
299
+ await ctx.storage.linkTicketToEpic(params.ticket_id, params.epic_id);
300
+ return {
301
+ content: [{
302
+ type: 'text',
303
+ text: JSON.stringify({ success: true, message: 'Ticket linked to epic' }, null, 2),
304
+ }],
305
+ };
306
+ }
307
+ catch (error) {
308
+ return errorResponse(error);
309
+ }
310
+ });
311
+ server.tool('ticket_unlink_from_epic', 'Unlink ticket from its epic', { ticket_id: z.string().describe('Ticket ID') }, async (params) => {
312
+ try {
313
+ await ctx.storage.unlinkTicketFromEpic(params.ticket_id);
314
+ return {
315
+ content: [{
316
+ type: 'text',
317
+ text: JSON.stringify({ success: true, message: 'Ticket unlinked from epic' }, null, 2),
318
+ }],
319
+ };
320
+ }
321
+ catch (error) {
322
+ return errorResponse(error);
323
+ }
324
+ });
325
+ server.tool('ticket_link_to_spec', 'Link ticket to a spec', {
326
+ ticket_id: z.string().describe('Ticket ID'),
327
+ spec_id: z.string().describe('Spec ID'),
328
+ }, async (params) => {
329
+ try {
330
+ await ctx.storage.linkTicketToSpec(params.ticket_id, params.spec_id);
331
+ return {
332
+ content: [{
333
+ type: 'text',
334
+ text: JSON.stringify({ success: true, message: 'Ticket linked to spec' }, null, 2),
335
+ }],
336
+ };
337
+ }
338
+ catch (error) {
339
+ return errorResponse(error);
340
+ }
341
+ });
342
+ server.tool('ticket_add_blocker', 'Add a blocking dependency', {
343
+ ticket_id: z.string().describe('Ticket that will be blocked'),
344
+ blocker_id: z.string().describe('Ticket that blocks'),
345
+ }, async (params) => {
346
+ try {
347
+ await ctx.storage.createTicketDependency(params.ticket_id, params.blocker_id, 'blocks');
348
+ return {
349
+ content: [{
350
+ type: 'text',
351
+ text: JSON.stringify({ success: true, message: `${params.ticket_id} is now blocked by ${params.blocker_id}` }, null, 2),
352
+ }],
353
+ };
354
+ }
355
+ catch (error) {
356
+ return errorResponse(error);
357
+ }
358
+ });
359
+ server.tool('ticket_remove_blocker', 'Remove a blocking dependency', {
360
+ ticket_id: z.string().describe('Blocked ticket'),
361
+ blocker_id: z.string().describe('Blocking ticket'),
362
+ }, async (params) => {
363
+ try {
364
+ await ctx.storage.deleteTicketDependency(params.ticket_id, params.blocker_id, 'blocks');
365
+ return {
366
+ content: [{
367
+ type: 'text',
368
+ text: JSON.stringify({ success: true, message: 'Blocker removed' }, null, 2),
369
+ }],
370
+ };
371
+ }
372
+ catch (error) {
373
+ return errorResponse(error);
374
+ }
375
+ });
376
+ server.tool('ticket_get_blockers', 'Get tickets blocking this ticket', { ticket_id: z.string().describe('Ticket ID') }, async (params) => {
377
+ try {
378
+ const blockers = await ctx.storage.getTicketBlockers(params.ticket_id);
379
+ return {
380
+ content: [{
381
+ type: 'text',
382
+ text: JSON.stringify({
383
+ success: true,
384
+ blockers: blockers.map((t) => ({ id: t.id, title: t.title, statusName: t.statusName })),
385
+ }, null, 2),
386
+ }],
387
+ };
388
+ }
389
+ catch (error) {
390
+ return errorResponse(error);
391
+ }
392
+ });
393
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * MCP View Tools
3
+ */
4
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
5
+ import type { McpToolContext } from '../types.js';
6
+ export declare function registerViewTools(server: McpServer, ctx: McpToolContext): void;