@chorus-aidlc/openclaw-plugin 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +287 -0
- package/images/slug.png +0 -0
- package/openclaw.plugin.json +29 -0
- package/package.json +37 -0
- package/src/commands.ts +141 -0
- package/src/config.ts +24 -0
- package/src/event-router.ts +228 -0
- package/src/index.ts +132 -0
- package/src/mcp-client.ts +141 -0
- package/src/sse-listener.ts +184 -0
- package/src/tools/common-tools.ts +418 -0
- package/src/tools/dev-tools.ts +85 -0
- package/src/tools/pm-tools.ts +356 -0
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import type { ChorusMcpClient } from "../mcp-client.js";
|
|
2
|
+
|
|
3
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4
|
+
export function registerPmTools(api: any, mcpClient: ChorusMcpClient) {
|
|
5
|
+
// 1. chorus_claim_idea
|
|
6
|
+
api.registerTool({
|
|
7
|
+
name: "chorus_claim_idea",
|
|
8
|
+
description: "Claim an open Idea for elaboration (open -> elaborating). After claiming, start elaboration or create a proposal directly.",
|
|
9
|
+
parameters: {
|
|
10
|
+
type: "object",
|
|
11
|
+
properties: {
|
|
12
|
+
ideaUuid: { type: "string", description: "UUID of the idea to claim" },
|
|
13
|
+
},
|
|
14
|
+
required: ["ideaUuid"],
|
|
15
|
+
additionalProperties: false,
|
|
16
|
+
},
|
|
17
|
+
async execute(_id: string, { ideaUuid }: { ideaUuid: string }) {
|
|
18
|
+
const result = await mcpClient.callTool("chorus_claim_idea", { ideaUuid });
|
|
19
|
+
return JSON.stringify(result, null, 2);
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// 2. chorus_start_elaboration
|
|
24
|
+
api.registerTool({
|
|
25
|
+
name: "chorus_start_elaboration",
|
|
26
|
+
description: "Start an elaboration round for an Idea. Creates structured questions for the stakeholder to answer before proposal creation.",
|
|
27
|
+
parameters: {
|
|
28
|
+
type: "object",
|
|
29
|
+
properties: {
|
|
30
|
+
ideaUuid: { type: "string", description: "UUID of the idea" },
|
|
31
|
+
depth: { type: "string", description: 'Elaboration depth: "minimal", "standard", or "comprehensive"' },
|
|
32
|
+
questions: {
|
|
33
|
+
type: "array",
|
|
34
|
+
description: "Array of questions. Each: { id, text, category, options: [{ id, label, description? }] }",
|
|
35
|
+
items: { type: "object" },
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
required: ["ideaUuid", "depth", "questions"],
|
|
39
|
+
additionalProperties: false,
|
|
40
|
+
},
|
|
41
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
42
|
+
async execute(_id: string, { ideaUuid, depth, questions }: { ideaUuid: string; depth: string; questions: any[] }) {
|
|
43
|
+
const result = await mcpClient.callTool("chorus_pm_start_elaboration", { ideaUuid, depth, questions });
|
|
44
|
+
return JSON.stringify(result, null, 2);
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// 3. chorus_answer_elaboration
|
|
49
|
+
api.registerTool({
|
|
50
|
+
name: "chorus_answer_elaboration",
|
|
51
|
+
description: "Answer elaboration questions for an Idea. Submits answers for a specific elaboration round.",
|
|
52
|
+
parameters: {
|
|
53
|
+
type: "object",
|
|
54
|
+
properties: {
|
|
55
|
+
ideaUuid: { type: "string", description: "UUID of the idea" },
|
|
56
|
+
roundUuid: { type: "string", description: "UUID of the elaboration round" },
|
|
57
|
+
answers: {
|
|
58
|
+
type: "array",
|
|
59
|
+
description: "Array of answers. Each: { questionId, selectedOptionId, customText }",
|
|
60
|
+
items: { type: "object" },
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
required: ["ideaUuid", "roundUuid", "answers"],
|
|
64
|
+
additionalProperties: false,
|
|
65
|
+
},
|
|
66
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
67
|
+
async execute(_id: string, { ideaUuid, roundUuid, answers }: { ideaUuid: string; roundUuid: string; answers: any[] }) {
|
|
68
|
+
const result = await mcpClient.callTool("chorus_answer_elaboration", { ideaUuid, roundUuid, answers });
|
|
69
|
+
return JSON.stringify(result, null, 2);
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// 4. chorus_validate_elaboration
|
|
74
|
+
api.registerTool({
|
|
75
|
+
name: "chorus_validate_elaboration",
|
|
76
|
+
description: "Validate answers from an elaboration round. Empty issues array = all valid, marks elaboration as resolved.",
|
|
77
|
+
parameters: {
|
|
78
|
+
type: "object",
|
|
79
|
+
properties: {
|
|
80
|
+
ideaUuid: { type: "string", description: "UUID of the idea" },
|
|
81
|
+
roundUuid: { type: "string", description: "UUID of the elaboration round" },
|
|
82
|
+
issues: {
|
|
83
|
+
type: "array",
|
|
84
|
+
description: 'Array of issues. Each: { questionId, type: "contradiction"|"ambiguity"|"incomplete", description }. Empty = valid.',
|
|
85
|
+
items: { type: "object" },
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
required: ["ideaUuid", "roundUuid", "issues"],
|
|
89
|
+
additionalProperties: false,
|
|
90
|
+
},
|
|
91
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
92
|
+
async execute(_id: string, { ideaUuid, roundUuid, issues }: { ideaUuid: string; roundUuid: string; issues: any[] }) {
|
|
93
|
+
const result = await mcpClient.callTool("chorus_pm_validate_elaboration", { ideaUuid, roundUuid, issues });
|
|
94
|
+
return JSON.stringify(result, null, 2);
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// 5. chorus_create_proposal
|
|
99
|
+
api.registerTool({
|
|
100
|
+
name: "chorus_create_proposal",
|
|
101
|
+
description: "Create a Proposal container with optional document drafts and task drafts.",
|
|
102
|
+
parameters: {
|
|
103
|
+
type: "object",
|
|
104
|
+
properties: {
|
|
105
|
+
projectUuid: { type: "string", description: "Project UUID" },
|
|
106
|
+
title: { type: "string", description: "Proposal title" },
|
|
107
|
+
inputType: { type: "string", description: 'Input source type: "idea" or "document"' },
|
|
108
|
+
inputUuids: { type: "array", description: "Array of input UUIDs", items: { type: "string" } },
|
|
109
|
+
description: { type: "string", description: "Proposal description" },
|
|
110
|
+
documentDrafts: { type: "array", description: "Array of { type, title, content }", items: { type: "object" } },
|
|
111
|
+
taskDrafts: { type: "array", description: "Array of { title, description?, priority?, storyPoints?, acceptanceCriteria?, dependsOnDraftUuids? }", items: { type: "object" } },
|
|
112
|
+
},
|
|
113
|
+
required: ["projectUuid", "title", "inputType", "inputUuids"],
|
|
114
|
+
additionalProperties: false,
|
|
115
|
+
},
|
|
116
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
117
|
+
async execute(_id: string, { projectUuid, title, inputType, inputUuids, description, documentDrafts, taskDrafts }: any) {
|
|
118
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
119
|
+
const args: Record<string, any> = { projectUuid, title, inputType, inputUuids };
|
|
120
|
+
if (description !== undefined) args.description = description;
|
|
121
|
+
if (documentDrafts !== undefined) args.documentDrafts = documentDrafts;
|
|
122
|
+
if (taskDrafts !== undefined) args.taskDrafts = taskDrafts;
|
|
123
|
+
const result = await mcpClient.callTool("chorus_pm_create_proposal", args);
|
|
124
|
+
return JSON.stringify(result, null, 2);
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// 6. chorus_add_document_draft
|
|
129
|
+
api.registerTool({
|
|
130
|
+
name: "chorus_add_document_draft",
|
|
131
|
+
description: "Add a document draft to a pending Proposal container.",
|
|
132
|
+
parameters: {
|
|
133
|
+
type: "object",
|
|
134
|
+
properties: {
|
|
135
|
+
proposalUuid: { type: "string", description: "Proposal UUID" },
|
|
136
|
+
type: { type: "string", description: "Document type (prd, tech_design, adr, spec, guide)" },
|
|
137
|
+
title: { type: "string", description: "Document title" },
|
|
138
|
+
content: { type: "string", description: "Document content (Markdown)" },
|
|
139
|
+
},
|
|
140
|
+
required: ["proposalUuid", "type", "title", "content"],
|
|
141
|
+
additionalProperties: false,
|
|
142
|
+
},
|
|
143
|
+
async execute(_id: string, { proposalUuid, type, title, content }: { proposalUuid: string; type: string; title: string; content: string }) {
|
|
144
|
+
const result = await mcpClient.callTool("chorus_pm_add_document_draft", { proposalUuid, type, title, content });
|
|
145
|
+
return JSON.stringify(result, null, 2);
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// 7. chorus_add_task_draft
|
|
150
|
+
api.registerTool({
|
|
151
|
+
name: "chorus_add_task_draft",
|
|
152
|
+
description: "Add a task draft to a pending Proposal container.",
|
|
153
|
+
parameters: {
|
|
154
|
+
type: "object",
|
|
155
|
+
properties: {
|
|
156
|
+
proposalUuid: { type: "string", description: "Proposal UUID" },
|
|
157
|
+
title: { type: "string", description: "Task title" },
|
|
158
|
+
description: { type: "string", description: "Task description" },
|
|
159
|
+
priority: { type: "string", description: 'Priority: "low", "medium", or "high"' },
|
|
160
|
+
storyPoints: { type: "number", description: "Effort estimate in agent hours" },
|
|
161
|
+
acceptanceCriteria: { type: "string", description: "Acceptance criteria in Markdown" },
|
|
162
|
+
dependsOnDraftUuids: { type: "array", description: "Dependent task draft UUIDs", items: { type: "string" } },
|
|
163
|
+
},
|
|
164
|
+
required: ["proposalUuid", "title"],
|
|
165
|
+
additionalProperties: false,
|
|
166
|
+
},
|
|
167
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
168
|
+
async execute(_id: string, { proposalUuid, title, description, priority, storyPoints, acceptanceCriteria, dependsOnDraftUuids }: any) {
|
|
169
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
170
|
+
const args: Record<string, any> = { proposalUuid, title };
|
|
171
|
+
if (description !== undefined) args.description = description;
|
|
172
|
+
if (priority !== undefined) args.priority = priority;
|
|
173
|
+
if (storyPoints !== undefined) args.storyPoints = storyPoints;
|
|
174
|
+
if (acceptanceCriteria !== undefined) args.acceptanceCriteria = acceptanceCriteria;
|
|
175
|
+
if (dependsOnDraftUuids !== undefined) args.dependsOnDraftUuids = dependsOnDraftUuids;
|
|
176
|
+
const result = await mcpClient.callTool("chorus_pm_add_task_draft", args);
|
|
177
|
+
return JSON.stringify(result, null, 2);
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// 8. chorus_get_proposal — View full proposal with all drafts
|
|
182
|
+
api.registerTool({
|
|
183
|
+
name: "chorus_get_proposal",
|
|
184
|
+
description: "Get detailed information for a Proposal, including all document drafts and task drafts with their UUIDs. Use this to inspect proposal contents before modifying or submitting.",
|
|
185
|
+
parameters: {
|
|
186
|
+
type: "object",
|
|
187
|
+
properties: {
|
|
188
|
+
proposalUuid: { type: "string", description: "Proposal UUID" },
|
|
189
|
+
},
|
|
190
|
+
required: ["proposalUuid"],
|
|
191
|
+
additionalProperties: false,
|
|
192
|
+
},
|
|
193
|
+
async execute(_id: string, { proposalUuid }: { proposalUuid: string }) {
|
|
194
|
+
const result = await mcpClient.callTool("chorus_get_proposal", { proposalUuid });
|
|
195
|
+
return JSON.stringify(result, null, 2);
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// 9. chorus_update_document_draft — Modify an existing document draft
|
|
200
|
+
api.registerTool({
|
|
201
|
+
name: "chorus_update_document_draft",
|
|
202
|
+
description: "Update a document draft in a Proposal. Can change title, type, or content.",
|
|
203
|
+
parameters: {
|
|
204
|
+
type: "object",
|
|
205
|
+
properties: {
|
|
206
|
+
proposalUuid: { type: "string", description: "Proposal UUID" },
|
|
207
|
+
draftUuid: { type: "string", description: "Document draft UUID to update" },
|
|
208
|
+
title: { type: "string", description: "New document title" },
|
|
209
|
+
type: { type: "string", description: "New document type (prd, tech_design, adr, spec, guide)" },
|
|
210
|
+
content: { type: "string", description: "New document content (Markdown)" },
|
|
211
|
+
},
|
|
212
|
+
required: ["proposalUuid", "draftUuid"],
|
|
213
|
+
additionalProperties: false,
|
|
214
|
+
},
|
|
215
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
216
|
+
async execute(_id: string, { proposalUuid, draftUuid, title, type, content }: any) {
|
|
217
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
218
|
+
const args: Record<string, any> = { proposalUuid, draftUuid };
|
|
219
|
+
if (title !== undefined) args.title = title;
|
|
220
|
+
if (type !== undefined) args.type = type;
|
|
221
|
+
if (content !== undefined) args.content = content;
|
|
222
|
+
const result = await mcpClient.callTool("chorus_pm_update_document_draft", args);
|
|
223
|
+
return JSON.stringify(result, null, 2);
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// 10. chorus_update_task_draft — Modify an existing task draft (including dependencies)
|
|
228
|
+
api.registerTool({
|
|
229
|
+
name: "chorus_update_task_draft",
|
|
230
|
+
description: "Update a task draft in a Proposal. Use this to fix validation issues, add dependencies (dependsOnDraftUuids), change priority, etc.",
|
|
231
|
+
parameters: {
|
|
232
|
+
type: "object",
|
|
233
|
+
properties: {
|
|
234
|
+
proposalUuid: { type: "string", description: "Proposal UUID" },
|
|
235
|
+
draftUuid: { type: "string", description: "Task draft UUID to update" },
|
|
236
|
+
title: { type: "string", description: "New task title" },
|
|
237
|
+
description: { type: "string", description: "New task description" },
|
|
238
|
+
priority: { type: "string", description: 'Priority: "low", "medium", or "high"' },
|
|
239
|
+
storyPoints: { type: "number", description: "Effort estimate in agent hours" },
|
|
240
|
+
acceptanceCriteria: { type: "string", description: "Acceptance criteria in Markdown" },
|
|
241
|
+
dependsOnDraftUuids: { type: "array", description: "Task draft UUIDs this task depends on (sets execution order)", items: { type: "string" } },
|
|
242
|
+
},
|
|
243
|
+
required: ["proposalUuid", "draftUuid"],
|
|
244
|
+
additionalProperties: false,
|
|
245
|
+
},
|
|
246
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
247
|
+
async execute(_id: string, { proposalUuid, draftUuid, title, description, priority, storyPoints, acceptanceCriteria, dependsOnDraftUuids }: any) {
|
|
248
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
249
|
+
const args: Record<string, any> = { proposalUuid, draftUuid };
|
|
250
|
+
if (title !== undefined) args.title = title;
|
|
251
|
+
if (description !== undefined) args.description = description;
|
|
252
|
+
if (priority !== undefined) args.priority = priority;
|
|
253
|
+
if (storyPoints !== undefined) args.storyPoints = storyPoints;
|
|
254
|
+
if (acceptanceCriteria !== undefined) args.acceptanceCriteria = acceptanceCriteria;
|
|
255
|
+
if (dependsOnDraftUuids !== undefined) args.dependsOnDraftUuids = dependsOnDraftUuids;
|
|
256
|
+
const result = await mcpClient.callTool("chorus_pm_update_task_draft", args);
|
|
257
|
+
return JSON.stringify(result, null, 2);
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// 11. chorus_remove_document_draft
|
|
262
|
+
api.registerTool({
|
|
263
|
+
name: "chorus_remove_document_draft",
|
|
264
|
+
description: "Remove a document draft from a Proposal.",
|
|
265
|
+
parameters: {
|
|
266
|
+
type: "object",
|
|
267
|
+
properties: {
|
|
268
|
+
proposalUuid: { type: "string", description: "Proposal UUID" },
|
|
269
|
+
draftUuid: { type: "string", description: "Document draft UUID to remove" },
|
|
270
|
+
},
|
|
271
|
+
required: ["proposalUuid", "draftUuid"],
|
|
272
|
+
additionalProperties: false,
|
|
273
|
+
},
|
|
274
|
+
async execute(_id: string, { proposalUuid, draftUuid }: { proposalUuid: string; draftUuid: string }) {
|
|
275
|
+
const result = await mcpClient.callTool("chorus_pm_remove_document_draft", { proposalUuid, draftUuid });
|
|
276
|
+
return JSON.stringify(result, null, 2);
|
|
277
|
+
},
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
// 12. chorus_remove_task_draft
|
|
281
|
+
api.registerTool({
|
|
282
|
+
name: "chorus_remove_task_draft",
|
|
283
|
+
description: "Remove a task draft from a Proposal.",
|
|
284
|
+
parameters: {
|
|
285
|
+
type: "object",
|
|
286
|
+
properties: {
|
|
287
|
+
proposalUuid: { type: "string", description: "Proposal UUID" },
|
|
288
|
+
draftUuid: { type: "string", description: "Task draft UUID to remove" },
|
|
289
|
+
},
|
|
290
|
+
required: ["proposalUuid", "draftUuid"],
|
|
291
|
+
additionalProperties: false,
|
|
292
|
+
},
|
|
293
|
+
async execute(_id: string, { proposalUuid, draftUuid }: { proposalUuid: string; draftUuid: string }) {
|
|
294
|
+
const result = await mcpClient.callTool("chorus_pm_remove_task_draft", { proposalUuid, draftUuid });
|
|
295
|
+
return JSON.stringify(result, null, 2);
|
|
296
|
+
},
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
// 13. chorus_validate_proposal
|
|
300
|
+
api.registerTool({
|
|
301
|
+
name: "chorus_validate_proposal",
|
|
302
|
+
description: "Validate a Proposal's completeness before submission. Returns errors (block submit), warnings, and info. ALWAYS call this before chorus_submit_proposal. If errors exist, use chorus_update_task_draft / chorus_update_document_draft to fix them, then validate again.",
|
|
303
|
+
parameters: {
|
|
304
|
+
type: "object",
|
|
305
|
+
properties: {
|
|
306
|
+
proposalUuid: { type: "string", description: "Proposal UUID to validate" },
|
|
307
|
+
},
|
|
308
|
+
required: ["proposalUuid"],
|
|
309
|
+
additionalProperties: false,
|
|
310
|
+
},
|
|
311
|
+
async execute(_id: string, { proposalUuid }: { proposalUuid: string }) {
|
|
312
|
+
const result = await mcpClient.callTool("chorus_pm_validate_proposal", { proposalUuid });
|
|
313
|
+
return JSON.stringify(result, null, 2);
|
|
314
|
+
},
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
// 9. chorus_submit_proposal
|
|
318
|
+
api.registerTool({
|
|
319
|
+
name: "chorus_submit_proposal",
|
|
320
|
+
description: "Submit a Proposal for approval (draft -> pending). Requires all input Ideas to have elaboration resolved.",
|
|
321
|
+
parameters: {
|
|
322
|
+
type: "object",
|
|
323
|
+
properties: {
|
|
324
|
+
proposalUuid: { type: "string", description: "Proposal UUID to submit" },
|
|
325
|
+
},
|
|
326
|
+
required: ["proposalUuid"],
|
|
327
|
+
additionalProperties: false,
|
|
328
|
+
},
|
|
329
|
+
async execute(_id: string, { proposalUuid }: { proposalUuid: string }) {
|
|
330
|
+
const result = await mcpClient.callTool("chorus_pm_submit_proposal", { proposalUuid });
|
|
331
|
+
return JSON.stringify(result, null, 2);
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
// 15. chorus_pm_create_idea
|
|
336
|
+
api.registerTool({
|
|
337
|
+
name: "chorus_pm_create_idea",
|
|
338
|
+
description: "Create a new Idea in a project. Use this when you discover a requirement, want to propose work, or record a user request.",
|
|
339
|
+
parameters: {
|
|
340
|
+
type: "object",
|
|
341
|
+
properties: {
|
|
342
|
+
projectUuid: { type: "string", description: "Project UUID" },
|
|
343
|
+
title: { type: "string", description: "Idea title" },
|
|
344
|
+
content: { type: "string", description: "Idea detailed description" },
|
|
345
|
+
},
|
|
346
|
+
required: ["projectUuid", "title"],
|
|
347
|
+
additionalProperties: false,
|
|
348
|
+
},
|
|
349
|
+
async execute(_id: string, { projectUuid, title, content }: { projectUuid: string; title: string; content?: string }) {
|
|
350
|
+
const args: Record<string, unknown> = { projectUuid, title };
|
|
351
|
+
if (content) args.content = content;
|
|
352
|
+
const result = await mcpClient.callTool("chorus_pm_create_idea", args);
|
|
353
|
+
return JSON.stringify(result, null, 2);
|
|
354
|
+
},
|
|
355
|
+
});
|
|
356
|
+
}
|