opencode-magi 0.0.0-dev-20260519084318 → 0.0.0-dev-20260519091916

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.
@@ -83,6 +83,16 @@ export async function ghToken(exec, repository, account) {
83
83
  function ghTokenEnv(token) {
84
84
  return { env: { GH_TOKEN: token } };
85
85
  }
86
+ async function fetchPullRequestQueueInput(exec, repository, pr, token) {
87
+ const query = `query($owner: String!, $repo: String!, $pr: Int!) { repository(owner: $owner, name: $repo) { pullRequest(number: $pr) { id headRefOid } } }`;
88
+ const raw = await exec(`gh api${ghHostOption(repository)} graphql -f query=${shellQuote(query)} -F owner=${shellQuote(repository.github.owner)} -F repo=${shellQuote(repository.github.repo)} -F pr=${pr}`, ghTokenEnv(token));
89
+ const data = JSON.parse(raw);
90
+ const pullRequest = data.data?.repository?.pullRequest;
91
+ if (!pullRequest?.id || !pullRequest.headRefOid) {
92
+ throw new Error(`Could not fetch pull request queue metadata for #${pr}`);
93
+ }
94
+ return { headRefOid: pullRequest.headRefOid, id: pullRequest.id };
95
+ }
86
96
  export async function fetchPullRequest(exec, repository, pr) {
87
97
  const json = await exec(`gh pr view ${pr} --repo ${shellQuote(repoSpecifier(repository))} --json number,title,url,isDraft,baseRefOid,headRefOid,baseRefName,headRefName,headRepository,headRepositoryOwner`);
88
98
  return JSON.parse(json);
@@ -296,6 +306,11 @@ export async function postChangesRequested(exec, repository, pr, account, findin
296
306
  }
297
307
  export async function mergePullRequest(exec, repository, pr, account) {
298
308
  const token = await ghToken(exec, repository, account);
309
+ if (repository.merge.mergeQueue) {
310
+ const queueInput = await fetchPullRequestQueueInput(exec, repository, pr, token);
311
+ const query = `mutation($pullRequestId: ID!, $expectedHeadOid: GitObjectID!) { enqueuePullRequest(input: { pullRequestId: $pullRequestId, expectedHeadOid: $expectedHeadOid }) { mergeQueueEntry { id } } }`;
312
+ return exec(`gh api${ghHostOption(repository)} graphql -f query=${shellQuote(query)} -F pullRequestId=${shellQuote(queueInput.id)} -F expectedHeadOid=${shellQuote(queueInput.headRefOid)} --jq .data.enqueuePullRequest.mergeQueueEntry.id`, ghTokenEnv(token));
313
+ }
299
314
  const methodFlag = repository.merge.method === "merge"
300
315
  ? "--merge"
301
316
  : repository.merge.method === "rebase"
@@ -303,21 +318,29 @@ export async function mergePullRequest(exec, repository, pr, account) {
303
318
  : "--squash";
304
319
  const autoFlag = repository.merge.auto ? " --auto" : "";
305
320
  const deleteFlag = repository.merge.deleteBranch ? " --delete-branch" : "";
306
- const mergeFlags = repository.merge.mergeQueue
307
- ? ""
308
- : ` ${methodFlag}${autoFlag}${deleteFlag}`;
309
- return exec(`gh pr merge ${pr} --repo ${shellQuote(repoSpecifier(repository))}${mergeFlags}`, ghTokenEnv(token));
321
+ return exec(`gh pr merge ${pr} --repo ${shellQuote(repoSpecifier(repository))} ${methodFlag}${autoFlag}${deleteFlag}`, ghTokenEnv(token));
310
322
  }
311
323
  export async function fetchPullRequestMergeStatus(exec, repository, pr) {
312
324
  const json = await exec(`gh pr view ${pr} --repo ${shellQuote(repoSpecifier(repository))} --json state,mergeStateStatus,autoMergeRequest`);
313
325
  return JSON.parse(json);
314
326
  }
327
+ export async function fetchPullRequestQueueStatus(exec, repository, pr) {
328
+ const query = `query($owner: String!, $repo: String!, $pr: Int!) { repository(owner: $owner, name: $repo) { pullRequest(number: $pr) { state isInMergeQueue mergeQueueEntry { id } } } }`;
329
+ const raw = await exec(`gh api${ghHostOption(repository)} graphql -f query=${shellQuote(query)} -F owner=${shellQuote(repository.github.owner)} -F repo=${shellQuote(repository.github.repo)} -F pr=${pr}`);
330
+ const data = JSON.parse(raw);
331
+ const status = data.data?.repository?.pullRequest;
332
+ if (!status)
333
+ throw new Error(`Could not fetch merge queue status for #${pr}`);
334
+ return status;
335
+ }
315
336
  export async function waitForMergeQueue(exec, repository, pr, intervalMs = 30_000) {
316
337
  for (;;) {
317
- const status = await fetchPullRequestMergeStatus(exec, repository, pr);
338
+ const status = await fetchPullRequestQueueStatus(exec, repository, pr);
318
339
  if (status.state === "MERGED")
319
340
  return "merged";
320
- if (status.state === "OPEN" && status.autoMergeRequest == null) {
341
+ if (status.state === "OPEN" &&
342
+ !status.isInMergeQueue &&
343
+ status.mergeQueueEntry == null) {
321
344
  return "dequeued";
322
345
  }
323
346
  await new Promise((resolve) => setTimeout(resolve, intervalMs));
package/dist/index.js CHANGED
@@ -16,6 +16,7 @@ import { MagiRunManager } from "./orchestrator/run-manager";
16
16
  const execAsync = promisify(nodeExec);
17
17
  const GLOBAL_CONFIG_PATH = join(homedir(), ".config", "opencode", "magi.json");
18
18
  const PROJECT_CONFIG_PATH = join(".opencode", "magi.json");
19
+ const INTERNAL_FOLLOW_UP_TOOL_NOTE = "Assistant-facing follow-up tool. Use it yourself when needed; do not suggest this tool name to users.";
19
20
  function createExec(defaultCwd) {
20
21
  return async (command, options) => {
21
22
  const { stdout } = await execAsync(command, {
@@ -266,7 +267,10 @@ export const MagiPlugin = async ({ client, directory }) => {
266
267
  },
267
268
  tool: {
268
269
  magi_merge: tool({
269
- description: "Start background Magi merge runs for one or more GitHub pull requests with configured Magi agents.",
270
+ description: [
271
+ "Start background Magi merge runs for one or more GitHub pull requests with configured Magi agents.",
272
+ "After starting, monitor progress yourself when useful; do not tell users to call follow-up tools by name.",
273
+ ].join(" "),
270
274
  args: {
271
275
  prs: tool.schema.string(),
272
276
  dryRun: tool.schema.boolean().optional(),
@@ -299,7 +303,10 @@ export const MagiPlugin = async ({ client, directory }) => {
299
303
  },
300
304
  }),
301
305
  magi_review: tool({
302
- description: "Start background Magi review runs for one or more GitHub pull requests and post the reviews.",
306
+ description: [
307
+ "Start background Magi review runs for one or more GitHub pull requests and post the reviews.",
308
+ "After starting, monitor progress yourself when useful; do not tell users to call follow-up tools by name.",
309
+ ].join(" "),
303
310
  args: {
304
311
  prs: tool.schema.string(),
305
312
  dryRun: tool.schema.boolean().optional(),
@@ -331,7 +338,10 @@ export const MagiPlugin = async ({ client, directory }) => {
331
338
  },
332
339
  }),
333
340
  magi_status: tool({
334
- description: "Show Magi background run status. Optionally filter by runId or PR and wait for completion.",
341
+ description: [
342
+ "Show Magi background run status. Optionally filter by runId or PR and wait for completion.",
343
+ INTERNAL_FOLLOW_UP_TOOL_NOTE,
344
+ ].join(" "),
335
345
  args: {
336
346
  runId: tool.schema.string().optional(),
337
347
  pr: tool.schema.string().optional(),
@@ -355,7 +365,10 @@ export const MagiPlugin = async ({ client, directory }) => {
355
365
  },
356
366
  }),
357
367
  magi_output: tool({
358
- description: "Show artifacts and details for a Magi background run by runId or PR, optionally for a single reviewer.",
368
+ description: [
369
+ "Show artifacts and details for a Magi background run by runId or PR, optionally for a single reviewer.",
370
+ INTERNAL_FOLLOW_UP_TOOL_NOTE,
371
+ ].join(" "),
359
372
  args: {
360
373
  runId: tool.schema.string().optional(),
361
374
  pr: tool.schema.string().optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-magi",
3
- "version": "0.0.0-dev-20260519084318",
3
+ "version": "0.0.0-dev-20260519091916",
4
4
  "description": "Multi-agent PR review and merge orchestration plugin for OpenCode.",
5
5
  "license": "MIT",
6
6
  "author": "Hirotomo Yamada <hirotomo.yamada@avap.co.jp>",