ralph-hero-mcp-server 2.4.64 → 2.4.65
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.
|
@@ -23,23 +23,33 @@ export function registerProjectManagementTools(server, client, fieldCache) {
|
|
|
23
23
|
// -------------------------------------------------------------------------
|
|
24
24
|
// ralph_hero__archive_item
|
|
25
25
|
// -------------------------------------------------------------------------
|
|
26
|
-
server.tool("ralph_hero__archive_item", "Archive or unarchive a project item. Archived items are hidden from default views but not deleted. Returns: number, archived, projectItemId.", {
|
|
26
|
+
server.tool("ralph_hero__archive_item", "Archive or unarchive a project item. Archived items are hidden from default views but not deleted. Accepts either number (for issues) or projectItemId (for draft items). Returns: number, archived, projectItemId.", {
|
|
27
27
|
owner: z.string().optional().describe("GitHub owner. Defaults to env var"),
|
|
28
28
|
repo: z.string().optional().describe("Repository name. Defaults to env var"),
|
|
29
29
|
projectNumber: z.coerce.number().optional()
|
|
30
30
|
.describe("Project number override (defaults to configured project)"),
|
|
31
|
-
number: z.coerce.number().describe("Issue number"),
|
|
31
|
+
number: z.coerce.number().optional().describe("Issue number (provide this or projectItemId)"),
|
|
32
|
+
projectItemId: z.string().optional()
|
|
33
|
+
.describe("Project item node ID (PVTI_...) — use instead of number for draft items"),
|
|
32
34
|
unarchive: z.boolean().optional().default(false)
|
|
33
35
|
.describe("If true, unarchive instead of archive (default: false)"),
|
|
34
36
|
}, async (args) => {
|
|
35
37
|
try {
|
|
38
|
+
if (!args.number && !args.projectItemId) {
|
|
39
|
+
return toolError("Either number or projectItemId must be provided");
|
|
40
|
+
}
|
|
41
|
+
if (args.number && args.projectItemId) {
|
|
42
|
+
return toolError("Provide either number or projectItemId, not both");
|
|
43
|
+
}
|
|
36
44
|
const { owner, repo, projectNumber, projectOwner } = resolveFullConfig(client, args);
|
|
37
45
|
await ensureFieldCache(client, fieldCache, projectOwner, projectNumber);
|
|
38
46
|
const projectId = fieldCache.getProjectId(projectNumber);
|
|
39
47
|
if (!projectId) {
|
|
40
48
|
return toolError("Could not resolve project ID");
|
|
41
49
|
}
|
|
42
|
-
const
|
|
50
|
+
const itemId = args.projectItemId
|
|
51
|
+
? args.projectItemId
|
|
52
|
+
: await resolveProjectItemId(client, fieldCache, owner, repo, args.number, projectNumber);
|
|
43
53
|
if (args.unarchive) {
|
|
44
54
|
await client.projectMutate(`mutation($projectId: ID!, $itemId: ID!) {
|
|
45
55
|
unarchiveProjectV2Item(input: {
|
|
@@ -48,7 +58,7 @@ export function registerProjectManagementTools(server, client, fieldCache) {
|
|
|
48
58
|
}) {
|
|
49
59
|
item { id }
|
|
50
60
|
}
|
|
51
|
-
}`, { projectId, itemId
|
|
61
|
+
}`, { projectId, itemId });
|
|
52
62
|
}
|
|
53
63
|
else {
|
|
54
64
|
await client.projectMutate(`mutation($projectId: ID!, $itemId: ID!) {
|
|
@@ -58,12 +68,12 @@ export function registerProjectManagementTools(server, client, fieldCache) {
|
|
|
58
68
|
}) {
|
|
59
69
|
item { id }
|
|
60
70
|
}
|
|
61
|
-
}`, { projectId, itemId
|
|
71
|
+
}`, { projectId, itemId });
|
|
62
72
|
}
|
|
63
73
|
return toolSuccess({
|
|
64
|
-
number: args.number,
|
|
74
|
+
number: args.number ?? null,
|
|
65
75
|
archived: !args.unarchive,
|
|
66
|
-
projectItemId,
|
|
76
|
+
projectItemId: itemId,
|
|
67
77
|
});
|
|
68
78
|
}
|
|
69
79
|
catch (error) {
|
|
@@ -74,21 +84,31 @@ export function registerProjectManagementTools(server, client, fieldCache) {
|
|
|
74
84
|
// -------------------------------------------------------------------------
|
|
75
85
|
// ralph_hero__remove_from_project
|
|
76
86
|
// -------------------------------------------------------------------------
|
|
77
|
-
server.tool("ralph_hero__remove_from_project", "Remove an
|
|
87
|
+
server.tool("ralph_hero__remove_from_project", "Remove an item from the project. This deletes the project item (not the issue itself). Accepts either number (for issues) or projectItemId (for draft items). WARNING: For draft issues, this permanently destroys the draft content — there is no undo. Returns: number, removed, projectItemId.", {
|
|
78
88
|
owner: z.string().optional().describe("GitHub owner. Defaults to env var"),
|
|
79
89
|
repo: z.string().optional().describe("Repository name. Defaults to env var"),
|
|
80
90
|
projectNumber: z.coerce.number().optional()
|
|
81
91
|
.describe("Project number override (defaults to configured project)"),
|
|
82
|
-
number: z.coerce.number().describe("Issue number"),
|
|
92
|
+
number: z.coerce.number().optional().describe("Issue number (provide this or projectItemId)"),
|
|
93
|
+
projectItemId: z.string().optional()
|
|
94
|
+
.describe("Project item node ID (PVTI_...) — use instead of number for draft items"),
|
|
83
95
|
}, async (args) => {
|
|
84
96
|
try {
|
|
97
|
+
if (!args.number && !args.projectItemId) {
|
|
98
|
+
return toolError("Either number or projectItemId must be provided");
|
|
99
|
+
}
|
|
100
|
+
if (args.number && args.projectItemId) {
|
|
101
|
+
return toolError("Provide either number or projectItemId, not both");
|
|
102
|
+
}
|
|
85
103
|
const { owner, repo, projectNumber, projectOwner } = resolveFullConfig(client, args);
|
|
86
104
|
await ensureFieldCache(client, fieldCache, projectOwner, projectNumber);
|
|
87
105
|
const projectId = fieldCache.getProjectId(projectNumber);
|
|
88
106
|
if (!projectId) {
|
|
89
107
|
return toolError("Could not resolve project ID");
|
|
90
108
|
}
|
|
91
|
-
const
|
|
109
|
+
const itemId = args.projectItemId
|
|
110
|
+
? args.projectItemId
|
|
111
|
+
: await resolveProjectItemId(client, fieldCache, owner, repo, args.number, projectNumber);
|
|
92
112
|
await client.projectMutate(`mutation($projectId: ID!, $itemId: ID!) {
|
|
93
113
|
deleteProjectV2Item(input: {
|
|
94
114
|
projectId: $projectId,
|
|
@@ -96,12 +116,15 @@ export function registerProjectManagementTools(server, client, fieldCache) {
|
|
|
96
116
|
}) {
|
|
97
117
|
deletedItemId
|
|
98
118
|
}
|
|
99
|
-
}`, { projectId, itemId
|
|
119
|
+
}`, { projectId, itemId });
|
|
100
120
|
// Invalidate cached project item ID since it no longer exists
|
|
101
|
-
|
|
121
|
+
if (args.number) {
|
|
122
|
+
client.getCache().invalidate(`project-item-id:${owner}/${repo}#${args.number}`);
|
|
123
|
+
}
|
|
102
124
|
return toolSuccess({
|
|
103
|
-
number: args.number,
|
|
125
|
+
number: args.number ?? null,
|
|
104
126
|
removed: true,
|
|
127
|
+
projectItemId: itemId,
|
|
105
128
|
});
|
|
106
129
|
}
|
|
107
130
|
catch (error) {
|
|
@@ -307,8 +330,26 @@ export function registerProjectManagementTools(server, client, fieldCache) {
|
|
|
307
330
|
await updateProjectItemField(client, fieldCache, projectItemId, "Estimate", args.estimate, projectNumber);
|
|
308
331
|
fieldsSet.push("Estimate");
|
|
309
332
|
}
|
|
333
|
+
// Fetch the DI_ content node ID so callers can use update_draft_issue
|
|
334
|
+
let draftIssueId = null;
|
|
335
|
+
try {
|
|
336
|
+
const itemResult = await client.projectQuery(`query($itemId: ID!) {
|
|
337
|
+
node(id: $itemId) {
|
|
338
|
+
... on ProjectV2Item {
|
|
339
|
+
content {
|
|
340
|
+
... on DraftIssue { id }
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}`, { itemId: projectItemId });
|
|
345
|
+
draftIssueId = itemResult.node?.content?.id ?? null;
|
|
346
|
+
}
|
|
347
|
+
catch {
|
|
348
|
+
// Best-effort: if the query fails, return without draftIssueId
|
|
349
|
+
}
|
|
310
350
|
return toolSuccess({
|
|
311
351
|
projectItemId,
|
|
352
|
+
draftIssueId,
|
|
312
353
|
title: args.title,
|
|
313
354
|
fieldsSet,
|
|
314
355
|
});
|
|
@@ -361,6 +402,50 @@ export function registerProjectManagementTools(server, client, fieldCache) {
|
|
|
361
402
|
}
|
|
362
403
|
});
|
|
363
404
|
// -------------------------------------------------------------------------
|
|
405
|
+
// ralph_hero__convert_draft_issue
|
|
406
|
+
// -------------------------------------------------------------------------
|
|
407
|
+
server.tool("ralph_hero__convert_draft_issue", "Convert a draft issue to a real repository issue. Requires the project item ID (PVTI_...) returned by create_draft_issue. CAVEAT: This mutation fails with fine-grained PATs (known GitHub bug, unresolved as of early 2026). Use a classic PAT with repo+project scopes. Returns: projectItemId, converted.", {
|
|
408
|
+
owner: z.string().optional().describe("GitHub owner. Defaults to env var"),
|
|
409
|
+
repo: z.string().optional().describe("Repository name. Defaults to env var"),
|
|
410
|
+
projectNumber: z.coerce.number().optional()
|
|
411
|
+
.describe("Project number override (defaults to configured project)"),
|
|
412
|
+
projectItemId: z.string().describe("Project item node ID (PVTI_...) of the draft issue"),
|
|
413
|
+
repositoryId: z.string().optional()
|
|
414
|
+
.describe("Repository node ID (R_...). Auto-fetched from configured repo if omitted"),
|
|
415
|
+
}, async (args) => {
|
|
416
|
+
try {
|
|
417
|
+
const { owner, repo, projectNumber, projectOwner } = resolveFullConfig(client, args);
|
|
418
|
+
await ensureFieldCache(client, fieldCache, projectOwner, projectNumber);
|
|
419
|
+
// Resolve repository node ID if not provided
|
|
420
|
+
let repoId = args.repositoryId;
|
|
421
|
+
if (!repoId) {
|
|
422
|
+
const repoResult = await client.query(`query($repoOwner: String!, $repoName: String!) {
|
|
423
|
+
repository(owner: $repoOwner, name: $repoName) { id }
|
|
424
|
+
}`, { repoOwner: owner, repoName: repo }, { cache: true, cacheTtlMs: 60 * 60 * 1000 });
|
|
425
|
+
repoId = repoResult.repository?.id;
|
|
426
|
+
if (!repoId) {
|
|
427
|
+
return toolError(`Repository ${owner}/${repo} not found`);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
await client.projectMutate(`mutation($itemId: ID!, $repositoryId: ID!) {
|
|
431
|
+
convertProjectV2DraftIssueItemToIssue(input: {
|
|
432
|
+
itemId: $itemId,
|
|
433
|
+
repositoryId: $repositoryId
|
|
434
|
+
}) {
|
|
435
|
+
item { id }
|
|
436
|
+
}
|
|
437
|
+
}`, { itemId: args.projectItemId, repositoryId: repoId });
|
|
438
|
+
return toolSuccess({
|
|
439
|
+
projectItemId: args.projectItemId,
|
|
440
|
+
converted: true,
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
catch (error) {
|
|
444
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
445
|
+
return toolError(`Failed to convert draft issue: ${message}`);
|
|
446
|
+
}
|
|
447
|
+
});
|
|
448
|
+
// -------------------------------------------------------------------------
|
|
364
449
|
// ralph_hero__reorder_item
|
|
365
450
|
// -------------------------------------------------------------------------
|
|
366
451
|
server.tool("ralph_hero__reorder_item", "Set item position within project views. Moves an issue before or after another item. Omit afterNumber to move to the top. Returns: number, position.", {
|