agenthub-multiagent-mcp 1.64.0 → 1.65.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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,OAAO,EAAqD,MAAM,cAAc,CAAC;AAOrG,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAkG/D,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,iBAAiB,EAAE,MAAM,MAAM,CAAC;IAChC,iBAAiB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,aAAa,EAAE,MAAM,MAAM,CAAC;IAC5B,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC;;;OAGG;IACH,cAAc,EAAE,MAAM,WAAW,CAAC;IAClC,gDAAgD;IAChD,eAAe,CAAC,EAAE,MAAM,WAAW,CAAC;IACpC,oEAAoE;IACpE,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE;QAC7B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;KACvB,KAAK,IAAI,CAAC;CACZ;AAED,UAAU,WAAW;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAyLD,wBAAgB,aAAa,IAAI,IAAI,EAAE,CAk2FtC;AA2BD,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,OAAO,CAAC,CAyuFlB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,OAAO,EAAqD,MAAM,cAAc,CAAC;AAOrG,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAmG/D,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,iBAAiB,EAAE,MAAM,MAAM,CAAC;IAChC,iBAAiB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,aAAa,EAAE,MAAM,MAAM,CAAC;IAC5B,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC;;;OAGG;IACH,cAAc,EAAE,MAAM,WAAW,CAAC;IAClC,gDAAgD;IAChD,eAAe,CAAC,EAAE,MAAM,WAAW,CAAC;IACpC,oEAAoE;IACpE,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE;QAC7B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;KACvB,KAAK,IAAI,CAAC;CACZ;AAED,UAAU,WAAW;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAyLD,wBAAgB,aAAa,IAAI,IAAI,EAAE,CAk2FtC;AA2BD,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,OAAO,CAAC,CA+qFlB"}
@@ -10,6 +10,7 @@ import * as brainCapture from "../hooks/brainCapture.js";
10
10
  import { getBrain } from "../brain/backend.js";
11
11
  import open from "open";
12
12
  import { registerOpenSpecVizTools, handleOpenSpecVizToolCall } from "./openspec-viz/index.js";
13
+ import { syncTaskGroups, describeTaskSync } from "./openspec-viz/taskSync.js";
13
14
  import { registerIntakeRequestTools, handleIntakeRequestToolCall, isIntakeRequestTool, } from "./intakeRequests.js";
14
15
  import { registerSessionControlTools, handleSessionControlToolCall, isSessionControlTool, } from "./sessionControl.js";
15
16
  import { registerGoshtiReadTools, handleGoshtiReadToolCall, isGoshtiReadTool, } from "./goshtiRead.js";
@@ -4750,89 +4751,29 @@ export async function handleToolCall(name, args, client, context) {
4750
4751
  catch {
4751
4752
  // Non-fatal if cleanup fails
4752
4753
  }
4753
- // 5. Sync tasks to ticket system (create Stories + Tasks from spec task groups)
4754
- let taskSyncResult;
4754
+ // 5. Sync tasks to ticket system (create Stories + Tasks from spec task groups).
4755
+ // The sync reports its own outcome. It used to be wrapped in a bare
4756
+ // `catch` here, which made a crashed sync byte-identical to one that
4757
+ // was never requested — the hierarchy was then discovered to be
4758
+ // missing only by independently listing the epic's children
4759
+ // (AGHUB-1469).
4755
4760
  const publishAgentState = workingDir ? state.loadState(workingDir) : undefined;
4756
- if (publishResult.sync_tasks && publishResult.taskGroups.length > 0 && publishAgentState?.project_id) {
4757
- taskSyncResult = { storiesCreated: 0, tasksCreated: 0, storiesSkipped: 0, tasksSkipped: 0 };
4761
+ const taskSyncOutcome = await syncTaskGroups({
4762
+ client,
4763
+ projectId: publishAgentState?.project_id ?? "",
4764
+ taskGroups: publishResult.taskGroups,
4765
+ ticketType: publishResult.ticket_type,
4766
+ ticketId: publishResult.ticket_id,
4758
4767
  // The change id is the input arg `id` — in change/diff modes that's
4759
- // the openspec change name. Stamp newly-created stories and tasks
4760
- // with it so the hierarchy resolver can find them.
4761
- const changeID = typeof args.id === "string" ? args.id : undefined;
4762
- try {
4763
- // Get existing stories to detect duplicates
4764
- const existingStories = await client.listProjectStories(publishAgentState.project_id);
4765
- const storyTitleMap = new Map(); // title → id
4766
- for (const s of existingStories.stories || []) {
4767
- storyTitleMap.set(s.title.toLowerCase().trim(), s.id);
4768
- }
4769
- for (const group of publishResult.taskGroups) {
4770
- const groupTitle = group.title.trim();
4771
- if (!groupTitle)
4772
- continue;
4773
- // Create or find story for this task group
4774
- let storyId;
4775
- const existingStoryId = storyTitleMap.get(groupTitle.toLowerCase());
4776
- if (existingStoryId) {
4777
- storyId = existingStoryId;
4778
- taskSyncResult.storiesSkipped++;
4779
- }
4780
- else {
4781
- const storyResult = await client.createStory({
4782
- project_id: publishAgentState.project_id,
4783
- epic_id: publishResult.ticket_type === "epic" ? publishResult.ticket_id : undefined,
4784
- title: groupTitle,
4785
- labels: ["openspec-sync"],
4786
- openspec_change_id: changeID,
4787
- });
4788
- storyId = storyResult.id;
4789
- storyTitleMap.set(groupTitle.toLowerCase(), storyId);
4790
- taskSyncResult.storiesCreated++;
4791
- }
4792
- // Get existing tasks for this story — map titles to ticket info for dedup + mapping
4793
- const existingTasks = await client.listProjectTasks(publishAgentState.project_id, storyId);
4794
- const taskInfoMap = new Map();
4795
- for (const t of existingTasks.tasks || []) {
4796
- taskInfoMap.set(t.title.toLowerCase().trim(), { id: t.id, key: t.key });
4797
- }
4798
- // Create tasks for each item in the group
4799
- for (const task of group.tasks) {
4800
- const taskTitle = task.text.trim();
4801
- if (!taskTitle)
4802
- continue;
4803
- const existingTask = taskInfoMap.get(taskTitle.toLowerCase());
4804
- if (existingTask) {
4805
- // Store the ticket mapping even for skipped (existing) tasks
4806
- task.ticket_id = existingTask.id;
4807
- task.ticket_key = existingTask.key;
4808
- taskSyncResult.tasksSkipped++;
4809
- continue;
4810
- }
4811
- const createdTask = await client.createTask({
4812
- project_id: publishAgentState.project_id,
4813
- story_id: storyId,
4814
- title: taskTitle,
4815
- labels: ["openspec-sync"],
4816
- });
4817
- // Store the ticket mapping for the newly created task. Tasks
4818
- // created here go through story_id, so the discriminated
4819
- // result is always ok:true; ignore the !ok branch silently
4820
- // (the outer try/catch will swallow truly unexpected errors).
4821
- if (createdTask.ok) {
4822
- task.ticket_id = createdTask.id;
4823
- task.ticket_key = createdTask.key;
4824
- taskSyncResult.tasksCreated++;
4825
- }
4826
- }
4827
- }
4828
- }
4829
- catch {
4830
- // Task sync failure is non-fatal
4831
- taskSyncResult = undefined;
4832
- }
4833
- }
4834
- // 6. Re-upload JSON with ticket ID mappings if task sync produced mappings
4835
- if (taskSyncResult) {
4768
+ // the openspec change name. Stamp newly-created stories with it so the
4769
+ // hierarchy resolver can find them.
4770
+ changeId: typeof args.id === "string" ? args.id : undefined,
4771
+ enabled: publishResult.sync_tasks,
4772
+ });
4773
+ // 6. Re-upload JSON with ticket ID mappings if task sync produced mappings.
4774
+ // A failed sync may still have mapped part of the hierarchy, so the
4775
+ // mappings it did write are preserved rather than discarded.
4776
+ if (taskSyncOutcome.status !== "skipped") {
4836
4777
  try {
4837
4778
  const fs = await import("fs");
4838
4779
  // Update the parsed JSON object's tasks with ticket mappings
@@ -4868,9 +4809,11 @@ export async function handleToolCall(name, args, client, context) {
4868
4809
  sections: publishResult.sections,
4869
4810
  requirementCount: publishResult.requirementCount,
4870
4811
  gapCount: publishResult.gapCount,
4871
- taskSync: taskSyncResult,
4872
- message: `Published OpenSpec to ${publishResult.ticket_key} (HTML + JSON, ${publishResult.requirementCount} requirements)` +
4873
- (taskSyncResult ? `. Task sync: ${taskSyncResult.storiesCreated} stories + ${taskSyncResult.tasksCreated} tasks created (${taskSyncResult.storiesSkipped + taskSyncResult.tasksSkipped} skipped as duplicates)` : ""),
4812
+ taskSync: taskSyncOutcome,
4813
+ // Always stated, so a caller reading only the message can tell a
4814
+ // successful sync from a failed or skipped one.
4815
+ message: `Published OpenSpec to ${publishResult.ticket_key} (HTML + JSON, ${publishResult.requirementCount} requirements). ` +
4816
+ describeTaskSync(taskSyncOutcome),
4874
4817
  }, context);
4875
4818
  }
4876
4819
  // Plan Review tools