opencode-magi 0.0.0-dev-20260720080129 → 0.0.0-dev-20260720081343

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,6 +1,7 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
3
  import { CONFIG_PATH, DEFAULT_CONFIG } from "@/constant";
4
+ import editPermissions from "@/permissions/editor.json" with { type: "json" };
4
5
  import { filterEmpty, getModels, isArray, isObject, isString, merge, } from "@/utils";
5
6
  export function resolvePermissions(permissions) {
6
7
  if (!permissions)
@@ -83,6 +84,8 @@ export async function getConfig(input) {
83
84
  readConfig(projectPath),
84
85
  ]);
85
86
  const results = filterEmpty(data);
87
+ if (!models.length)
88
+ throw new Error("No OpenCode models found.");
86
89
  if (!results.length)
87
90
  throw new Error(`No Magi config found. Expected ${CONFIG_PATH.GLOBAL} or ${projectPath}.`);
88
91
  const config = results.reduce((prev, current) => merge(prev, current), DEFAULT_CONFIG);
@@ -96,8 +99,14 @@ export async function getConfig(input) {
96
99
  model: resolveModel(models, model),
97
100
  };
98
101
  });
99
- config.merge.editor = mergeAgent(config, config.merge.editor);
100
- config.merge.editor.model = resolveModel(models, config.merge.editor.model);
102
+ if (config.merge.editor) {
103
+ if (config.merge.editor.permissions)
104
+ config.merge.editor.permissions = mergePermissions(editPermissions, config.merge.editor.permissions);
105
+ else
106
+ config.merge.editor.permissions = editPermissions;
107
+ config.merge.editor = mergeAgent(config, config.merge.editor);
108
+ config.merge.editor.model = resolveModel(models, config.merge.editor.model);
109
+ }
101
110
  if (config.triage.voters)
102
111
  config.triage.voters = config.triage.voters.map((agent, index) => {
103
112
  const { id, model, ...rest } = mergeAgent(config, agent);
@@ -107,7 +116,13 @@ export async function getConfig(input) {
107
116
  model: resolveModel(models, model),
108
117
  };
109
118
  });
110
- config.triage.creator = mergeAgent(config, config.triage.creator);
111
- config.triage.creator.model = resolveModel(models, config.triage.creator.model);
119
+ if (config.triage.creator) {
120
+ if (config.triage.creator.permissions)
121
+ config.triage.creator.permissions = mergePermissions(editPermissions, config.triage.creator.permissions);
122
+ else
123
+ config.triage.creator.permissions = editPermissions;
124
+ config.triage.creator = mergeAgent(config, config.triage.creator);
125
+ config.triage.creator.model = resolveModel(models, config.triage.creator.model);
126
+ }
112
127
  return config;
113
128
  }
@@ -12,28 +12,32 @@ function schemaErrors(config) {
12
12
  return (validateSchema.errors ?? []).map((e) => `schema ${e.instancePath || "config"}: ${e.message ?? "invalid value"}`);
13
13
  }
14
14
  function requiredErrors(config, { creator = false, editor = false, github = true, reviewers = false, voters = false, } = {}) {
15
+ creator ||= !!config.triage.creator;
16
+ editor ||= !!config.merge.editor;
17
+ reviewers ||= !!config.review.reviewers;
18
+ voters ||= !!config.triage.voters;
15
19
  const errors = [
16
20
  required(github, config.github.owner, "github.owner"),
17
21
  required(github, config.github.repo, "github.repo"),
18
22
  required(reviewers, config.review.reviewers, "review.reviewers"),
19
- ...(config.review.reviewers ?? []).map((reviewer, index) => required(reviewers, reviewer.model, `review.reviewers[${index}].model`)),
23
+ ...(config.review.reviewers ?? []).map((reviewer, index) => required(reviewers || !!reviewer, reviewer.model, `review.reviewers[${index}].model`)),
20
24
  required(editor, config.merge.editor, "merge.editor"),
21
- required(editor, config.merge.editor.model, "merge.editor.model"),
22
- required(editor, config.merge.editor.author, "merge.editor.author"),
25
+ required(editor, config.merge.editor?.model, "merge.editor.model"),
26
+ required(editor, config.merge.editor?.author, "merge.editor.author"),
23
27
  required(voters, config.triage.voters, "triage.voters"),
24
- ...(config.triage.voters ?? []).map((voter, index) => required(voters, voter.model, `triage.voters[${index}].model`)),
28
+ ...(config.triage.voters ?? []).map((voter, index) => required(voters || !!voter, voter.model, `triage.voters[${index}].model`)),
25
29
  required(creator, config.triage.creator, "triage.creator"),
26
- required(creator, config.triage.creator.model, "triage.creator.model"),
27
- required(creator, config.triage.creator.author, "triage.creator.author"),
30
+ required(creator, config.triage.creator?.model, "triage.creator.model"),
31
+ required(creator, config.triage.creator?.author, "triage.creator.author"),
28
32
  ];
29
33
  if (config.mode === "single") {
30
34
  errors.push(required(true, config.account, "account"));
31
35
  }
32
36
  else {
33
37
  errors.push(...(config.review.reviewers ?? []).map((reviewer, index) => required(reviewers, reviewer.account, `review.reviewers[${index}].account`)));
34
- errors.push(required(editor, config.merge.editor.account, "merge.editor.account"));
38
+ errors.push(required(editor, config.merge.editor?.account, "merge.editor.account"));
35
39
  errors.push(...(config.triage.voters ?? []).map((voter, index) => required(voters, voter.account, `triage.voters[${index}].account`)));
36
- errors.push(required(creator, config.triage.creator.account, "triage.creator.account"));
40
+ errors.push(required(creator, config.triage.creator?.account, "triage.creator.account"));
37
41
  }
38
42
  return filterEmpty(errors);
39
43
  }
@@ -85,17 +89,19 @@ async function authErrors(config, exec) {
85
89
  }
86
90
  else {
87
91
  const accounts = {
88
- ...Object.fromEntries(config.review.reviewers.map(({ account }, index) => [
92
+ ...Object.fromEntries(config.review.reviewers?.map(({ account }, index) => [
89
93
  account,
90
94
  `review.reviewers[${index}]`,
91
- ])),
92
- [config.merge.editor.account]: "merge.editor",
93
- ...Object.fromEntries(config.triage.voters.map(({ account }, index) => [
95
+ ]) ?? []),
96
+ ...Object.fromEntries(config.triage.voters?.map(({ account }, index) => [
94
97
  account,
95
98
  `triage.voters[${index}]`,
96
- ])),
97
- [config.triage.creator.account]: "triage.creator",
99
+ ]) ?? []),
98
100
  };
101
+ if (config.merge.editor)
102
+ accounts[config.merge.editor.account] = "merge.editor";
103
+ if (config.triage.creator)
104
+ accounts[config.triage.creator.account] = "triage.creator";
99
105
  return filterEmpty([
100
106
  ...duplicateErrors(Object.keys(accounts), (value) => `${accounts[value]} has duplicate account: ${value}`),
101
107
  ...(await Promise.all(Object.keys(accounts).map((account) => authError(config, exec, account)))),
package/dist/constant.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import { homedir } from "node:os";
2
2
  import { join } from "node:path";
3
3
  import commonPermissions from "./permissions/common.json" with { type: "json" };
4
- import editPermissions from "./permissions/editor.json" with { type: "json" };
5
4
  export const CONFIG_PATH = {
6
5
  GLOBAL: join(homedir(), ".config", "opencode", "magi.json"),
7
6
  PROJECT: join(".opencode", "magi.json"),
@@ -33,9 +32,6 @@ export const DEFAULT_CONFIG = {
33
32
  checks: {
34
33
  wait: true,
35
34
  },
36
- editor: {
37
- permissions: editPermissions,
38
- },
39
35
  maxThreadResolutionCycles: 5,
40
36
  },
41
37
  mode: "single",
@@ -126,9 +122,6 @@ export const DEFAULT_CONFIG = {
126
122
  concurrency: {
127
123
  runs: 3,
128
124
  },
129
- creator: {
130
- permissions: editPermissions,
131
- },
132
125
  output: ".magi/runs/issue",
133
126
  safety: {
134
127
  allowAuthors: [],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-magi",
3
- "version": "0.0.0-dev-20260720080129",
3
+ "version": "0.0.0-dev-20260720081343",
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>",