@freelancercom/phabricator-mcp 2.0.16 → 2.0.18
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/dist/tools/maniphest.js +110 -12
- package/package.json +1 -1
package/dist/tools/maniphest.js
CHANGED
|
@@ -1,5 +1,63 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { jsonCoerce } from './coerce.js';
|
|
3
|
+
/**
|
|
4
|
+
* Resolve revision identifiers (e.g. "D223125", "PHID-DREV-xxx") to PHIDs.
|
|
5
|
+
* Accepts a mix of D-prefixed IDs and raw PHIDs.
|
|
6
|
+
*/
|
|
7
|
+
async function resolveRevisionPHIDs(client, ids) {
|
|
8
|
+
const phids = [];
|
|
9
|
+
const namesToLookup = [];
|
|
10
|
+
for (const id of ids) {
|
|
11
|
+
if (id.startsWith('PHID-')) {
|
|
12
|
+
phids.push(id);
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
// Normalize to D-prefixed format if just a number
|
|
16
|
+
const name = /^\d+$/.test(id) ? `D${id}` : id;
|
|
17
|
+
namesToLookup.push(name);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
if (namesToLookup.length > 0) {
|
|
21
|
+
const lookupResult = await client.call('phid.lookup', { names: namesToLookup });
|
|
22
|
+
for (const name of namesToLookup) {
|
|
23
|
+
const entry = lookupResult[name];
|
|
24
|
+
if (entry) {
|
|
25
|
+
phids.push(entry.phid);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
throw new Error(`Could not resolve revision identifier: ${name}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return phids;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Link or unlink revisions to/from a task by calling differential.revision.edit
|
|
36
|
+
* with tasks.add / tasks.remove transactions.
|
|
37
|
+
*/
|
|
38
|
+
async function linkRevisionsToTask(client, taskIdentifier, revisionPHIDs, action) {
|
|
39
|
+
// Resolve the task identifier to a PHID if it's a T-prefixed ID
|
|
40
|
+
let taskPHID = taskIdentifier;
|
|
41
|
+
if (!taskIdentifier.startsWith('PHID-')) {
|
|
42
|
+
const lookupResult = await client.call('phid.lookup', { names: [taskIdentifier] });
|
|
43
|
+
const entry = lookupResult[taskIdentifier];
|
|
44
|
+
if (entry) {
|
|
45
|
+
taskPHID = entry.phid;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
throw new Error(`Could not resolve task identifier: ${taskIdentifier}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const results = [];
|
|
52
|
+
for (const revPHID of revisionPHIDs) {
|
|
53
|
+
const result = await client.call('differential.revision.edit', {
|
|
54
|
+
objectIdentifier: revPHID,
|
|
55
|
+
transactions: [{ type: `tasks.${action}`, value: [taskPHID] }],
|
|
56
|
+
});
|
|
57
|
+
results.push(result);
|
|
58
|
+
}
|
|
59
|
+
return results;
|
|
60
|
+
}
|
|
3
61
|
export function registerManiphestTools(server, client) {
|
|
4
62
|
// Search tasks
|
|
5
63
|
server.tool('phabricator_task_search', 'Search Maniphest tasks with optional filters', {
|
|
@@ -54,7 +112,8 @@ export function registerManiphestTools(server, client) {
|
|
|
54
112
|
subtype: z.string().optional().describe('Task subtype (e.g. "default", "incident")'),
|
|
55
113
|
parentPHIDs: z.array(z.string()).optional().describe('Parent task PHIDs'),
|
|
56
114
|
subtaskPHIDs: z.array(z.string()).optional().describe('Subtask PHIDs'),
|
|
57
|
-
commitPHIDs: z.array(z.string()).optional().describe('Commit PHIDs to associate'),
|
|
115
|
+
commitPHIDs: z.array(z.string()).optional().describe('Commit PHIDs to associate (for actual commits only, not revisions)'),
|
|
116
|
+
revisionIDs: z.array(z.string()).optional().describe('Differential revision IDs to link (e.g. ["D223125"] or ["PHID-DREV-xxx"]). Creates a bidirectional link between the revision and the task.'),
|
|
58
117
|
points: z.coerce.number().nullable().optional().describe('Story points value (if points are enabled on this instance)'),
|
|
59
118
|
space: z.string().optional().describe('Space PHID to place the task in (for multi-space installations)'),
|
|
60
119
|
comment: z.string().optional().describe('Initial comment on the task (supports Remarkup)'),
|
|
@@ -102,13 +161,32 @@ export function registerManiphestTools(server, client) {
|
|
|
102
161
|
if (params.comment !== undefined) {
|
|
103
162
|
transactions.push({ type: 'comment', value: params.comment });
|
|
104
163
|
}
|
|
164
|
+
const result = await client.call('maniphest.edit', { transactions });
|
|
165
|
+
const extras = {};
|
|
166
|
+
// Custom fields are applied in a second call because Phabricator validates
|
|
167
|
+
// transaction types against the default subtype during creation. Subtype-specific
|
|
168
|
+
// custom fields (e.g. incident fields) are only available after the task exists
|
|
169
|
+
// with the correct subtype.
|
|
105
170
|
if (params.customFields !== undefined) {
|
|
171
|
+
const customTransactions = [];
|
|
106
172
|
for (const [key, value] of Object.entries(params.customFields)) {
|
|
107
|
-
|
|
173
|
+
customTransactions.push({ type: key, value });
|
|
174
|
+
}
|
|
175
|
+
if (customTransactions.length > 0) {
|
|
176
|
+
extras.customFields = await client.call('maniphest.edit', {
|
|
177
|
+
objectIdentifier: result.object.phid,
|
|
178
|
+
transactions: customTransactions,
|
|
179
|
+
});
|
|
108
180
|
}
|
|
109
181
|
}
|
|
110
|
-
|
|
111
|
-
|
|
182
|
+
// Link revisions to the newly created task via differential.revision.edit
|
|
183
|
+
if (params.revisionIDs !== undefined && params.revisionIDs.length > 0) {
|
|
184
|
+
const revPHIDs = await resolveRevisionPHIDs(client, params.revisionIDs);
|
|
185
|
+
const taskId = `T${result.object.id}`;
|
|
186
|
+
extras.linkedRevisions = await linkRevisionsToTask(client, taskId, revPHIDs, 'add');
|
|
187
|
+
}
|
|
188
|
+
const output = Object.keys(extras).length > 0 ? { ...result, ...extras } : result;
|
|
189
|
+
return { content: [{ type: 'text', text: JSON.stringify(output, null, 2) }] };
|
|
112
190
|
});
|
|
113
191
|
// Edit task
|
|
114
192
|
server.tool('phabricator_task_edit', 'Edit an existing Maniphest task', {
|
|
@@ -127,8 +205,10 @@ export function registerManiphestTools(server, client) {
|
|
|
127
205
|
removeParentPHIDs: z.array(z.string()).optional().describe('Parent task PHIDs to remove'),
|
|
128
206
|
addSubtaskPHIDs: z.array(z.string()).optional().describe('Subtask PHIDs to add'),
|
|
129
207
|
removeSubtaskPHIDs: z.array(z.string()).optional().describe('Subtask PHIDs to remove'),
|
|
130
|
-
addCommitPHIDs: z.array(z.string()).optional().describe('Commit PHIDs to associate'),
|
|
131
|
-
removeCommitPHIDs: z.array(z.string()).optional().describe('Commit PHIDs to disassociate'),
|
|
208
|
+
addCommitPHIDs: z.array(z.string()).optional().describe('Commit PHIDs to associate (for actual commits only, not revisions)'),
|
|
209
|
+
removeCommitPHIDs: z.array(z.string()).optional().describe('Commit PHIDs to disassociate (for actual commits only, not revisions)'),
|
|
210
|
+
addRevisionIDs: z.array(z.string()).optional().describe('Differential revision IDs to link (e.g. ["D223125"] or ["PHID-DREV-xxx"]). Creates a bidirectional link between the revision and the task.'),
|
|
211
|
+
removeRevisionIDs: z.array(z.string()).optional().describe('Differential revision IDs to unlink (e.g. ["D223125"] or ["PHID-DREV-xxx"]).'),
|
|
132
212
|
points: z.coerce.number().nullable().optional().describe('Story points value (null to clear)'),
|
|
133
213
|
columnPHID: z.string().optional().describe('Move to workboard column'),
|
|
134
214
|
space: z.string().optional().describe('Space PHID to move the task to (for multi-space installations)'),
|
|
@@ -201,14 +281,32 @@ export function registerManiphestTools(server, client) {
|
|
|
201
281
|
transactions.push({ type: key, value });
|
|
202
282
|
}
|
|
203
283
|
}
|
|
204
|
-
|
|
284
|
+
const hasRevisionChanges = (params.addRevisionIDs !== undefined && params.addRevisionIDs.length > 0) ||
|
|
285
|
+
(params.removeRevisionIDs !== undefined && params.removeRevisionIDs.length > 0);
|
|
286
|
+
if (transactions.length === 0 && !hasRevisionChanges) {
|
|
205
287
|
return { content: [{ type: 'text', text: 'No changes specified' }] };
|
|
206
288
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
289
|
+
let result = undefined;
|
|
290
|
+
if (transactions.length > 0) {
|
|
291
|
+
result = await client.call('maniphest.edit', {
|
|
292
|
+
objectIdentifier: params.objectIdentifier,
|
|
293
|
+
transactions,
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
// Link/unlink revisions via differential.revision.edit
|
|
297
|
+
const revisionResults = {};
|
|
298
|
+
if (params.addRevisionIDs !== undefined && params.addRevisionIDs.length > 0) {
|
|
299
|
+
const revPHIDs = await resolveRevisionPHIDs(client, params.addRevisionIDs);
|
|
300
|
+
revisionResults.added = await linkRevisionsToTask(client, params.objectIdentifier, revPHIDs, 'add');
|
|
301
|
+
}
|
|
302
|
+
if (params.removeRevisionIDs !== undefined && params.removeRevisionIDs.length > 0) {
|
|
303
|
+
const revPHIDs = await resolveRevisionPHIDs(client, params.removeRevisionIDs);
|
|
304
|
+
revisionResults.removed = await linkRevisionsToTask(client, params.objectIdentifier, revPHIDs, 'remove');
|
|
305
|
+
}
|
|
306
|
+
const output = hasRevisionChanges
|
|
307
|
+
? { ...(result !== undefined ? { task: result } : {}), linkedRevisions: revisionResults }
|
|
308
|
+
: result;
|
|
309
|
+
return { content: [{ type: 'text', text: JSON.stringify(output, null, 2) }] };
|
|
212
310
|
});
|
|
213
311
|
// Add comment to task
|
|
214
312
|
server.tool('phabricator_task_add_comment', 'Add a comment to a Maniphest task', {
|
package/package.json
CHANGED