ralph-hero-mcp-server 2.4.34 → 2.4.35

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.
@@ -325,7 +325,7 @@ export function registerRelationshipTools(server, client, fieldCache) {
325
325
  // -------------------------------------------------------------------------
326
326
  // ralph_hero__advance_children
327
327
  // -------------------------------------------------------------------------
328
- server.tool("ralph_hero__advance_children", "Advance all child/sub-issues of a parent to match the parent's new state. Only advances children that are in earlier workflow states. Returns what changed, what was skipped, and any errors.", {
328
+ server.tool("ralph_hero__advance_children", "Advance issues to a target workflow state. Provide either 'number' (parent issue, advances sub-issues) or 'issues' (explicit list of issue numbers). Only advances issues in earlier workflow states. Returns what changed, what was skipped, and any errors.", {
329
329
  owner: z
330
330
  .string()
331
331
  .optional()
@@ -334,12 +334,24 @@ export function registerRelationshipTools(server, client, fieldCache) {
334
334
  .string()
335
335
  .optional()
336
336
  .describe("Repository name. Defaults to GITHUB_REPO env var"),
337
- number: z.coerce.number().describe("Parent issue number"),
337
+ number: z
338
+ .coerce.number()
339
+ .optional()
340
+ .describe("Parent issue number (resolves sub-issues automatically)"),
341
+ issues: z
342
+ .array(z.coerce.number())
343
+ .optional()
344
+ .describe("Explicit list of issue numbers to advance (alternative to parent number)"),
338
345
  targetState: z
339
346
  .string()
340
- .describe("State to advance children to (e.g., 'Research Needed', 'Ready for Plan')"),
347
+ .describe("State to advance issues to (e.g., 'Research Needed', 'Ready for Plan')"),
341
348
  }, async (args) => {
342
349
  try {
350
+ // Validate: at least one of number or issues must be provided
351
+ if (args.number === undefined && (!args.issues || args.issues.length === 0)) {
352
+ return toolError("Either 'number' (parent issue) or 'issues' (explicit list) is required. " +
353
+ "Recovery: provide one of these parameters.");
354
+ }
343
355
  // Validate target state
344
356
  if (!isValidState(args.targetState)) {
345
357
  return toolError(`Unknown target state '${args.targetState}'. ` +
@@ -358,24 +370,32 @@ export function registerRelationshipTools(server, client, fieldCache) {
358
370
  }
359
371
  // Ensure field cache is populated
360
372
  await ensureFieldCache(client, fieldCache, projectOwner, projectNumber);
361
- // Fetch sub-issues
362
- const result = await client.query(`query($owner: String!, $repo: String!, $number: Int!) {
363
- repository(owner: $owner, name: $repo) {
364
- issue(number: $number) {
365
- number
366
- title
367
- subIssues(first: 50) {
368
- nodes { id number title state }
373
+ // Build issue list: from explicit `issues` param or from parent's sub-issues
374
+ let issueNumbers;
375
+ if (args.issues && args.issues.length > 0) {
376
+ // Explicit issue list takes precedence
377
+ issueNumbers = args.issues;
378
+ }
379
+ else {
380
+ // Fetch sub-issues from parent
381
+ const result = await client.query(`query($owner: String!, $repo: String!, $number: Int!) {
382
+ repository(owner: $owner, name: $repo) {
383
+ issue(number: $number) {
384
+ number
385
+ title
386
+ subIssues(first: 50) {
387
+ nodes { id number title state }
388
+ }
369
389
  }
370
390
  }
391
+ }`, { owner, repo, number: args.number });
392
+ const parentIssue = result.repository?.issue;
393
+ if (!parentIssue) {
394
+ return toolError(`Issue #${args.number} not found in ${owner}/${repo}`);
395
+ }
396
+ issueNumbers = parentIssue.subIssues.nodes.map((si) => si.number);
371
397
  }
372
- }`, { owner, repo, number: args.number });
373
- const parentIssue = result.repository?.issue;
374
- if (!parentIssue) {
375
- return toolError(`Issue #${args.number} not found in ${owner}/${repo}`);
376
- }
377
- const subIssues = parentIssue.subIssues.nodes;
378
- if (subIssues.length === 0) {
398
+ if (issueNumbers.length === 0) {
379
399
  return toolSuccess({
380
400
  advanced: [],
381
401
  skipped: [],
@@ -385,22 +405,22 @@ export function registerRelationshipTools(server, client, fieldCache) {
385
405
  const advanced = [];
386
406
  const skipped = [];
387
407
  const errors = [];
388
- for (const child of subIssues) {
408
+ for (const issueNum of issueNumbers) {
389
409
  try {
390
410
  // Get current workflow state
391
- const currentState = await getCurrentFieldValue(client, fieldCache, owner, repo, child.number, "Workflow State");
411
+ const currentState = await getCurrentFieldValue(client, fieldCache, owner, repo, issueNum, "Workflow State");
392
412
  if (!currentState) {
393
413
  skipped.push({
394
- number: child.number,
414
+ number: issueNum,
395
415
  currentState: "unknown",
396
416
  reason: "No workflow state set on issue",
397
417
  });
398
418
  continue;
399
419
  }
400
- // Only advance if child is in an earlier state
420
+ // Only advance if issue is in an earlier state
401
421
  if (!isEarlierState(currentState, args.targetState)) {
402
422
  skipped.push({
403
- number: child.number,
423
+ number: issueNum,
404
424
  currentState,
405
425
  reason: currentState === args.targetState
406
426
  ? "Already at target state"
@@ -408,13 +428,13 @@ export function registerRelationshipTools(server, client, fieldCache) {
408
428
  });
409
429
  continue;
410
430
  }
411
- // Advance the child
412
- const projectItemId = await resolveProjectItemId(client, fieldCache, owner, repo, child.number);
431
+ // Advance the issue
432
+ const projectItemId = await resolveProjectItemId(client, fieldCache, owner, repo, issueNum);
413
433
  await updateProjectItemField(client, fieldCache, projectItemId, "Workflow State", args.targetState);
414
434
  // Sync default Status field (best-effort, one-way)
415
435
  await syncStatusField(client, fieldCache, projectItemId, args.targetState);
416
436
  advanced.push({
417
- number: child.number,
437
+ number: issueNum,
418
438
  fromState: currentState,
419
439
  toState: args.targetState,
420
440
  });
@@ -422,8 +442,8 @@ export function registerRelationshipTools(server, client, fieldCache) {
422
442
  catch (error) {
423
443
  const message = error instanceof Error ? error.message : String(error);
424
444
  errors.push({
425
- number: child.number,
426
- error: `Failed to update: ${message}. Recovery: retry advance_children or update this child manually via update_workflow_state.`,
445
+ number: issueNum,
446
+ error: `Failed to update: ${message}. Recovery: retry advance_children or update this issue manually.`,
427
447
  });
428
448
  }
429
449
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ralph-hero-mcp-server",
3
- "version": "2.4.34",
3
+ "version": "2.4.35",
4
4
  "description": "MCP server for GitHub Projects V2 - Ralph workflow automation",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",