opencode-swarm 7.184.4 → 7.184.6

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.
Files changed (71) hide show
  1. package/.opencode/skills/issue-tracer/SKILL.md +3 -3
  2. package/.opencode/skills/issue-tracer/assets/pr-template.md +7 -0
  3. package/.opencode/skills/issue-tracer/references/acceptance-checks.md +30 -6
  4. package/.opencode/skills/issue-tracer/references/critic-gate.md +5 -0
  5. package/.opencode/skills/issue-tracer/references/evidence-artifacts.md +18 -2
  6. package/.opencode/skills/issue-tracer/scripts/repro-check.sh +604 -77
  7. package/.opencode/skills/issue-tracer/scripts/trace-check.sh +486 -60
  8. package/.opencode/skills/issue-tracer/scripts/trace-init.sh +30 -0
  9. package/binaries/win32-arm64/swarm-sandbox-runner.exe +0 -0
  10. package/binaries/win32-x64/swarm-sandbox-runner.exe +0 -0
  11. package/dist/cli/{coder-settlement-633ec0fb.js → coder-settlement-cxe9fn9d.js} +3 -3
  12. package/dist/cli/{config-doctor-bczcm4sg.js → config-doctor-f2semh9v.js} +2 -2
  13. package/dist/cli/{core-f4tzsgxp.js → core-xf46e82t.js} +1 -1
  14. package/dist/cli/{curator-drift-1x67b2vg.js → curator-drift-8ez4czhj.js} +5 -5
  15. package/dist/cli/{curator-llm-factory-vs1hgm6e.js → curator-llm-factory-smd5741x.js} +22 -22
  16. package/dist/cli/{curator-9mzgd9dd.js → curator-n2gvb7rc.js} +22 -22
  17. package/dist/cli/{evidence-summary-service-fj2qa9eh.js → evidence-summary-service-e2gt8tnz.js} +8 -8
  18. package/dist/cli/{gate-evidence-cvkr2ybm.js → gate-evidence-9zpdk87c.js} +2 -2
  19. package/dist/cli/{guardrail-explain-64q268m0.js → guardrail-explain-e9kjxg97.js} +23 -23
  20. package/dist/cli/{guardrail-log-gp01x3n1.js → guardrail-log-e4ysdxag.js} +3 -3
  21. package/dist/cli/{guardrail-reset-z0t943fx.js → guardrail-reset-1fxdaqwf.js} +22 -22
  22. package/dist/cli/{hive-promoter-vjb7y55n.js → hive-promoter-y81jkaj5.js} +22 -22
  23. package/dist/cli/{index-11z82cjy.js → index-3w3cssas.js} +3 -3
  24. package/dist/cli/{index-a86t7pa9.js → index-47wqppx9.js} +22 -22
  25. package/dist/cli/{index-z3xbmp3x.js → index-4b8997qs.js} +3 -3
  26. package/dist/cli/{index-mybjc24z.js → index-5j6g6kqj.js} +2 -2
  27. package/dist/cli/{index-2xm8hp34.js → index-5p5tpj50.js} +2 -2
  28. package/dist/cli/{index-vww242dg.js → index-7136wpb2.js} +1 -1
  29. package/dist/cli/{index-egd6f37q.js → index-77g30h6x.js} +1 -1
  30. package/dist/cli/{index-hrza3045.js → index-8x0vtc05.js} +1 -1
  31. package/dist/cli/{index-r93jbg90.js → index-9vbsarg2.js} +2 -2
  32. package/dist/cli/{index-90byps3b.js → index-b5xcv0ec.js} +3 -3
  33. package/dist/cli/{index-5qhkd1fc.js → index-d4rhnvtf.js} +1 -1
  34. package/dist/cli/{index-5qdhf317.js → index-dtgj8d9k.js} +1 -1
  35. package/dist/cli/{index-88vt0941.js → index-ejk27p7n.js} +24 -24
  36. package/dist/cli/{index-yjkgrerq.js → index-g1aa3sdm.js} +1 -1
  37. package/dist/cli/{index-ak936pwb.js → index-gekvpzaf.js} +1 -1
  38. package/dist/cli/{index-jqqhq9h6.js → index-h2vpqaf1.js} +1 -1
  39. package/dist/cli/{index-8cd5abna.js → index-jjdrhr7z.js} +1 -1
  40. package/dist/cli/{index-hphprp03.js → index-k1p87csx.js} +1 -1
  41. package/dist/cli/{index-9a2vvwk2.js → index-kkj76rky.js} +1 -1
  42. package/dist/cli/{index-gbshh8v2.js → index-kx3hfpb8.js} +5 -5
  43. package/dist/cli/{index-ye8t9qq3.js → index-m62eysrr.js} +57 -57
  44. package/dist/cli/{index-1d0p1v2q.js → index-p45t3c66.js} +1 -1
  45. package/dist/cli/{index-w06xcw6v.js → index-qksf7d8b.js} +3 -3
  46. package/dist/cli/{index-kaknnfaw.js → index-skw3bgg1.js} +1 -1
  47. package/dist/cli/{index-raqa5vnb.js → index-taw9b4v5.js} +1 -1
  48. package/dist/cli/{index-ae2kzm55.js → index-y1xtkdys.js} +4 -4
  49. package/dist/cli/{index-q7vtghff.js → index-zm08qe68.js} +5 -5
  50. package/dist/cli/index.d.ts +36 -7
  51. package/dist/cli/index.js +336 -76
  52. package/dist/cli/{knowledge-escalator-agfq92ps.js → knowledge-escalator-pw06n575.js} +6 -6
  53. package/dist/cli/{knowledge-events-4krnrjdh.js → knowledge-events-f4wgd8n2.js} +4 -4
  54. package/dist/cli/{knowledge-store-bd0380r4.js → knowledge-store-rttx0zb9.js} +1 -1
  55. package/dist/cli/{knowledge-validator-b9wrs08q.js → knowledge-validator-nakzbx9d.js} +2 -2
  56. package/dist/cli/{mcp-43m5e2bb.js → mcp-fdgfryxa.js} +1 -1
  57. package/dist/cli/{model-preflight-tb6ne377.js → model-preflight-wmm3tard.js} +22 -22
  58. package/dist/cli/{pending-delegations-0qbxpf68.js → pending-delegations-eswsasts.js} +2 -2
  59. package/dist/cli/{pr-subscriptions-dek1sqh0.js → pr-subscriptions-fvwnd217.js} +1 -1
  60. package/dist/cli/{pr-workflow-gate-md8brvw1.js → pr-workflow-gate-v4e8k3gn.js} +22 -22
  61. package/dist/cli/{scan-cursor-w333f57f.js → scan-cursor-krrzh2x5.js} +2 -2
  62. package/dist/cli/{schema-3r41ckef.js → schema-wv7r4x4q.js} +1 -1
  63. package/dist/cli/{scope-persistence-dthn7rxz.js → scope-persistence-d3jv0v2w.js} +5 -5
  64. package/dist/cli/{server-b0teb7vg.js → server-cxf8qmej.js} +22 -22
  65. package/dist/cli/{skill-generator-474vdbar.js → skill-generator-gbs540wv.js} +7 -7
  66. package/dist/cli/{snapshot-coordination-init-wtydyah2.js → snapshot-coordination-init-p98yacqd.js} +22 -22
  67. package/dist/cli/{speckit-checkoff-27mk0e41.js → speckit-checkoff-1zhq6dsh.js} +3 -3
  68. package/dist/cli/{worktree-collision-ownership-2mtmrdkp.js → worktree-collision-ownership-rm7ybyyc.js} +2 -2
  69. package/dist/cli/{worktree-isolation-kgda5awq.js → worktree-isolation-vz6et0s2.js} +22 -22
  70. package/dist/index.js +1 -1
  71. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -14,44 +14,44 @@ import {
14
14
  readCachePackageVersion,
15
15
  resolveCommand,
16
16
  safeRealpathSync
17
- } from "./index-ye8t9qq3.js";
18
- import"./index-hrza3045.js";
17
+ } from "./index-m62eysrr.js";
18
+ import"./index-8x0vtc05.js";
19
19
  import"./index-hamqvzqn.js";
20
20
  import"./index-6g664sy8.js";
21
- import"./index-9a2vvwk2.js";
22
- import"./index-egd6f37q.js";
23
- import"./index-gbshh8v2.js";
21
+ import"./index-kkj76rky.js";
22
+ import"./index-77g30h6x.js";
23
+ import"./index-kx3hfpb8.js";
24
24
  import"./index-ya32wfkp.js";
25
25
  import"./index-txdagbh7.js";
26
- import"./index-raqa5vnb.js";
26
+ import"./index-taw9b4v5.js";
27
27
  import"./index-6b295cas.js";
28
- import"./index-w06xcw6v.js";
29
- import"./index-q7vtghff.js";
30
- import"./index-hphprp03.js";
28
+ import"./index-qksf7d8b.js";
29
+ import"./index-zm08qe68.js";
30
+ import"./index-k1p87csx.js";
31
31
  import"./index-rdec0y3z.js";
32
32
  import"./index-ar2gsn6b.js";
33
33
  import"./index-cjapb4hy.js";
34
34
  import"./index-zsrtt42s.js";
35
- import"./index-z3xbmp3x.js";
35
+ import"./index-4b8997qs.js";
36
36
  import"./index-vyjped9g.js";
37
37
  import"./index-3n5zsp31.js";
38
38
  import"./index-pt7h91zc.js";
39
39
  import"./index-q7sjgr1y.js";
40
- import"./index-ak936pwb.js";
41
- import"./index-vww242dg.js";
42
- import"./index-kaknnfaw.js";
43
- import"./index-jqqhq9h6.js";
44
- import"./index-11z82cjy.js";
40
+ import"./index-gekvpzaf.js";
41
+ import"./index-7136wpb2.js";
42
+ import"./index-skw3bgg1.js";
43
+ import"./index-h2vpqaf1.js";
44
+ import"./index-3w3cssas.js";
45
45
  import"./index-wy3zpwjy.js";
46
46
  import"./index-2s7dvzff.js";
47
- import"./index-5qdhf317.js";
47
+ import"./index-dtgj8d9k.js";
48
48
  import"./index-earq3mda.js";
49
49
  import"./index-rtry5xyf.js";
50
- import"./index-mybjc24z.js";
50
+ import"./index-5j6g6kqj.js";
51
51
  import"./index-frcemc0b.js";
52
52
  import"./index-0725jpp7.js";
53
- import"./index-8cd5abna.js";
54
- import"./index-r93jbg90.js";
53
+ import"./index-jjdrhr7z.js";
54
+ import"./index-9vbsarg2.js";
55
55
  import"./index-jvk95e6y.js";
56
56
  import"./index-456awy1p.js";
57
57
  import"./index-f0nj3d8z.js";
@@ -59,7 +59,7 @@ import"./index-w9hxzcz4.js";
59
59
  import"./index-rdm3px0g.js";
60
60
  import"./index-ry16d01s.js";
61
61
  import"./index-zzvxj02e.js";
62
- import"./index-ae2kzm55.js";
62
+ import"./index-y1xtkdys.js";
63
63
  import"./index-vzm6zre7.js";
64
64
  import"./index-ae75rja9.js";
65
65
  import"./index-475bkab8.js";
@@ -72,12 +72,12 @@ import"./index-ejhxgyvm.js";
72
72
  import"./index-7g4c7s5r.js";
73
73
  import"./index-k53a69vb.js";
74
74
  import"./index-gx2vp8gx.js";
75
- import"./index-5qhkd1fc.js";
75
+ import"./index-d4rhnvtf.js";
76
76
  import"./index-zxdawwc7.js";
77
77
  import {
78
78
  init_package,
79
79
  package_default
80
- } from "./index-yjkgrerq.js";
80
+ } from "./index-g1aa3sdm.js";
81
81
  import {
82
82
  DEFAULT_AGENT_CONFIGS,
83
83
  init_constants
@@ -118,6 +118,24 @@ function segmentDepthBelowRoot(resolved) {
118
118
  const { root } = path.parse(resolved);
119
119
  return resolved.slice(root.length).split(path.sep).filter((s) => s.length > 0).length;
120
120
  }
121
+ function normalizePathForComparison(value) {
122
+ const normalized = path.normalize(path.resolve(value));
123
+ return process.platform === "win32" ? normalized.toLowerCase() : normalized;
124
+ }
125
+ function isExactConfiguredConfigChild(canonicalTarget, configuredDir) {
126
+ const canonicalConfigDir = safeRealpathSync(configuredDir, configuredDir);
127
+ const canonicalParent = safeRealpathSync(path.dirname(canonicalTarget), path.dirname(canonicalTarget));
128
+ if (canonicalConfigDir === null || canonicalParent === null) {
129
+ return false;
130
+ }
131
+ return normalizePathForComparison(canonicalParent) === normalizePathForComparison(canonicalConfigDir);
132
+ }
133
+ function hasSafeConfigArtifactParent(resolved, configuredDir) {
134
+ if (configuredDir !== undefined) {
135
+ return isExactConfiguredConfigChild(resolved, configuredDir);
136
+ }
137
+ return path.basename(path.dirname(resolved)) === path.basename(CONFIG_DIR);
138
+ }
121
139
  function isSafeCachePath(p) {
122
140
  const resolved = path.resolve(p);
123
141
  const home = path.resolve(os.homedir());
@@ -164,7 +182,7 @@ function isSafeLockFilePath(p) {
164
182
  }
165
183
  return true;
166
184
  }
167
- function isSafePromptsDir(p) {
185
+ function isSafePromptsDir(p, configuredDir) {
168
186
  const canonical = safeRealpathSync(p, p);
169
187
  if (canonical === null) {
170
188
  return false;
@@ -180,12 +198,12 @@ function isSafePromptsDir(p) {
180
198
  if (path.basename(resolved) !== "opencode-swarm") {
181
199
  return false;
182
200
  }
183
- if (path.basename(path.dirname(resolved)) !== path.basename(CONFIG_DIR)) {
201
+ if (!hasSafeConfigArtifactParent(resolved, configuredDir)) {
184
202
  return false;
185
203
  }
186
204
  return true;
187
205
  }
188
- function isSafePluginConfigPath(p) {
206
+ function isSafePluginConfigPath(p, configuredDir) {
189
207
  const canonical = safeRealpathSync(p, p);
190
208
  if (canonical === null) {
191
209
  return false;
@@ -201,25 +219,235 @@ function isSafePluginConfigPath(p) {
201
219
  if (path.basename(resolved) !== "opencode-swarm.json") {
202
220
  return false;
203
221
  }
204
- if (path.basename(path.dirname(resolved)) !== path.basename(CONFIG_DIR)) {
222
+ if (!hasSafeConfigArtifactParent(resolved, configuredDir)) {
205
223
  return false;
206
224
  }
207
225
  return true;
208
226
  }
227
+ function isSafeInstallBackupPath(p, configuredDir) {
228
+ const canonical = safeRealpathSync(p, p);
229
+ if (canonical === null) {
230
+ return false;
231
+ }
232
+ const resolved = path.resolve(canonical);
233
+ const home = path.resolve(os.homedir());
234
+ if (resolved === "/" || resolved === home || resolved.length <= home.length) {
235
+ return false;
236
+ }
237
+ if (segmentDepthBelowRoot(resolved) < 3) {
238
+ return false;
239
+ }
240
+ if (path.basename(resolved) !== "opencode.swarm-install-backup.json") {
241
+ return false;
242
+ }
243
+ return hasSafeConfigArtifactParent(resolved, configuredDir);
244
+ }
245
+ var cleanupFs = {
246
+ lstatSync: (target) => fs.lstatSync(target),
247
+ openSync: (target, flags) => fs.openSync(target, flags),
248
+ fstatSync: (descriptor) => fs.fstatSync(descriptor),
249
+ closeSync: (descriptor) => fs.closeSync(descriptor),
250
+ unlinkSync: (target) => fs.unlinkSync(target),
251
+ rmSync: (target) => fs.rmSync(target, { recursive: true }),
252
+ existsSync: (target) => fs.existsSync(target)
253
+ };
254
+ function sameFileIdentity(left, right) {
255
+ return left.dev === right.dev && left.ino === right.ino;
256
+ }
257
+ function snapshotCleanupTarget(canonicalTarget, configuredDir, kind) {
258
+ const canonicalConfigDir = safeRealpathSync(configuredDir, configuredDir);
259
+ const canonicalParent = safeRealpathSync(path.dirname(canonicalTarget), path.dirname(canonicalTarget));
260
+ if (canonicalConfigDir === null || canonicalParent === null || normalizePathForComparison(canonicalConfigDir) !== normalizePathForComparison(canonicalParent)) {
261
+ return null;
262
+ }
263
+ try {
264
+ const parentStat = cleanupFs.lstatSync(path.dirname(canonicalTarget));
265
+ if (parentStat.isSymbolicLink() || !parentStat.isDirectory())
266
+ return null;
267
+ const stat = cleanupFs.lstatSync(canonicalTarget);
268
+ if (stat.isSymbolicLink())
269
+ return null;
270
+ if (kind === "file" ? !stat.isFile() : !stat.isDirectory())
271
+ return null;
272
+ return { parent: parentStat, target: stat };
273
+ } catch {
274
+ return null;
275
+ }
276
+ }
277
+ function removeBoundCleanupTarget(canonicalTarget, configuredDir, kind) {
278
+ const before = snapshotCleanupTarget(canonicalTarget, configuredDir, kind);
279
+ if (before === null) {
280
+ if (kind === "file") {
281
+ try {
282
+ const target = cleanupFs.lstatSync(canonicalTarget);
283
+ if (!target.isSymbolicLink() && target.isDirectory()) {
284
+ return { ok: false, error: "path is a directory, not a file" };
285
+ }
286
+ } catch {}
287
+ }
288
+ return { ok: false, error: "target or parent changed during validation" };
289
+ }
290
+ let descriptor;
291
+ try {
292
+ if (kind === "file") {
293
+ descriptor = cleanupFs.openSync(canonicalTarget, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0));
294
+ const opened = cleanupFs.fstatSync(descriptor);
295
+ if (!sameFileIdentity(before.target, opened)) {
296
+ return { ok: false, error: "target changed while being opened" };
297
+ }
298
+ }
299
+ const immediatelyBefore = snapshotCleanupTarget(canonicalTarget, configuredDir, kind);
300
+ if (immediatelyBefore === null || !sameFileIdentity(before.parent, immediatelyBefore.parent) || !sameFileIdentity(before.target, immediatelyBefore.target)) {
301
+ if (immediatelyBefore === null && kind === "file") {
302
+ try {
303
+ const target = cleanupFs.lstatSync(canonicalTarget);
304
+ if (!target.isSymbolicLink() && target.isDirectory()) {
305
+ return { ok: false, error: "path is a directory, not a file" };
306
+ }
307
+ } catch {}
308
+ }
309
+ return { ok: false, error: "target or parent changed before deletion" };
310
+ }
311
+ if (kind === "file") {
312
+ cleanupFs.unlinkSync(canonicalTarget);
313
+ } else {
314
+ cleanupFs.rmSync(canonicalTarget);
315
+ }
316
+ if (cleanupFs.existsSync(canonicalTarget)) {
317
+ return { ok: false, error: "delete returned but target still exists" };
318
+ }
319
+ return { ok: true };
320
+ } catch (error) {
321
+ return {
322
+ ok: false,
323
+ error: error instanceof Error ? error.message : String(error)
324
+ };
325
+ } finally {
326
+ if (descriptor !== undefined) {
327
+ try {
328
+ cleanupFs.closeSync(descriptor);
329
+ } catch {}
330
+ }
331
+ }
332
+ }
333
+ var _test_exports = { cleanupFs, removeBoundCleanupTarget };
209
334
  function ensureDir(dir) {
210
335
  if (!fs.existsSync(dir)) {
211
336
  fs.mkdirSync(dir, { recursive: true });
212
337
  }
213
338
  }
339
+ function normalizeJsonc(content) {
340
+ let normalized = "";
341
+ let inString = false;
342
+ let escaped = false;
343
+ const nextSignificantToken = (from) => {
344
+ let index = from;
345
+ while (index < content.length) {
346
+ const current = content[index];
347
+ if (/\s/.test(current)) {
348
+ index += 1;
349
+ continue;
350
+ }
351
+ if (current === "/" && content[index + 1] === "/") {
352
+ index += 2;
353
+ while (index < content.length && content[index] !== `
354
+ ` && content[index] !== "\r") {
355
+ index += 1;
356
+ }
357
+ continue;
358
+ }
359
+ if (current === "/" && content[index + 1] === "*") {
360
+ const close = content.indexOf("*/", index + 2);
361
+ if (close < 0) {
362
+ return null;
363
+ }
364
+ index = close + 2;
365
+ continue;
366
+ }
367
+ return index;
368
+ }
369
+ return content.length;
370
+ };
371
+ for (let index = 0;index < content.length; index += 1) {
372
+ const current = content[index];
373
+ if (inString) {
374
+ normalized += current;
375
+ if (escaped) {
376
+ escaped = false;
377
+ } else if (current === "\\") {
378
+ escaped = true;
379
+ } else if (current === '"') {
380
+ inString = false;
381
+ }
382
+ continue;
383
+ }
384
+ if (current === '"') {
385
+ inString = true;
386
+ normalized += current;
387
+ continue;
388
+ }
389
+ if (current === "/" && content[index + 1] === "/") {
390
+ index += 2;
391
+ while (index < content.length && content[index] !== `
392
+ ` && content[index] !== "\r") {
393
+ index += 1;
394
+ }
395
+ if (index < content.length) {
396
+ normalized += content[index];
397
+ }
398
+ continue;
399
+ }
400
+ if (current === "/" && content[index + 1] === "*") {
401
+ const close = content.indexOf("*/", index + 2);
402
+ if (close < 0) {
403
+ return null;
404
+ }
405
+ index = close + 1;
406
+ normalized += " ";
407
+ continue;
408
+ }
409
+ if (current === ",") {
410
+ const next = nextSignificantToken(index + 1);
411
+ if (next === null) {
412
+ return null;
413
+ }
414
+ if (content[next] === "}" || content[next] === "]") {
415
+ continue;
416
+ }
417
+ }
418
+ normalized += current;
419
+ }
420
+ return normalized;
421
+ }
214
422
  function loadJson(filepath) {
215
423
  try {
216
424
  const content = fs.readFileSync(filepath, "utf-8");
217
- const stripped = content.replace(/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g, (match, comment) => comment ? "" : match).replace(/,(\s*[}\]])/g, "$1");
218
- return JSON.parse(stripped);
425
+ const normalized = normalizeJsonc(content);
426
+ if (normalized === null) {
427
+ return null;
428
+ }
429
+ return JSON.parse(normalized);
219
430
  } catch {
220
431
  return null;
221
432
  }
222
433
  }
434
+ function isOpenCodeConfig(value) {
435
+ return typeof value === "object" && value !== null && !Array.isArray(value);
436
+ }
437
+ function loadConfigObject(filepath) {
438
+ if (!fs.existsSync(filepath))
439
+ return { kind: "missing" };
440
+ try {
441
+ const content = fs.readFileSync(filepath, "utf-8");
442
+ const normalized = normalizeJsonc(content);
443
+ if (normalized === null)
444
+ return { kind: "invalid" };
445
+ const parsed = JSON.parse(normalized);
446
+ return isOpenCodeConfig(parsed) ? { kind: "object", value: parsed } : { kind: "non-object" };
447
+ } catch {
448
+ return { kind: "invalid" };
449
+ }
450
+ }
223
451
  function saveJson(filepath, data) {
224
452
  fs.writeFileSync(filepath, `${JSON.stringify(data, null, 2)}
225
453
  `, "utf-8");
@@ -249,12 +477,22 @@ async function install() {
249
477
  const LEGACY_CONFIG_PATH = path.join(CONFIG_DIR, "config.json");
250
478
  const configExisted = fs.existsSync(OPENCODE_CONFIG_PATH);
251
479
  const originalConfigRaw = configExisted ? fs.readFileSync(OPENCODE_CONFIG_PATH, "utf-8") : null;
252
- const originalConfig = loadJson(OPENCODE_CONFIG_PATH);
480
+ const originalConfigLoad = loadConfigObject(OPENCODE_CONFIG_PATH);
481
+ if (originalConfigLoad.kind === "non-object") {
482
+ console.error("\u2717 opencode.json must contain a JSON object at the root; refusing to overwrite it.");
483
+ return 1;
484
+ }
485
+ const originalConfig = originalConfigLoad.kind === "object" ? originalConfigLoad.value : null;
253
486
  let opencodeConfig;
254
487
  if (originalConfig) {
255
488
  opencodeConfig = structuredClone(originalConfig);
256
489
  } else {
257
- const legacyConfig = loadJson(LEGACY_CONFIG_PATH);
490
+ const legacyConfigLoad = loadConfigObject(LEGACY_CONFIG_PATH);
491
+ if (legacyConfigLoad.kind === "non-object") {
492
+ console.error("\u2717 config.json must contain a JSON object at the root; refusing to overwrite it.");
493
+ return 1;
494
+ }
495
+ const legacyConfig = legacyConfigLoad.kind === "object" ? legacyConfigLoad.value : null;
258
496
  if (legacyConfig) {
259
497
  console.log("\u26A0 Migrating existing config from config.json to opencode.json...");
260
498
  opencodeConfig = structuredClone(legacyConfig);
@@ -421,9 +659,9 @@ function evictPluginCaches(additionalPaths = []) {
421
659
  }
422
660
  const versionBeforeDelete = readCachePackageVersion(canonical);
423
661
  try {
424
- fs.rmSync(canonical, { recursive: true, force: true });
425
- if (fs.existsSync(canonical)) {
426
- failed.push(`${canonical} (rmSync returned without error, but the path still exists)`);
662
+ const removal = removeBoundCleanupTarget(canonical, path.dirname(canonical), "directory");
663
+ if (!removal.ok) {
664
+ failed.push(`${canonical} (${removal.error ?? "target changed before deletion"})`);
427
665
  continue;
428
666
  }
429
667
  cleared.push(canonical);
@@ -448,9 +686,9 @@ function evictLockFiles() {
448
686
  continue;
449
687
  }
450
688
  try {
451
- fs.unlinkSync(canonical);
452
- if (fs.existsSync(canonical)) {
453
- failed.push(`${canonical} (unlinkSync returned without error, but the path still exists)`);
689
+ const removal = removeBoundCleanupTarget(canonical, path.dirname(canonical), "file");
690
+ if (!removal.ok) {
691
+ failed.push(`${canonical} (${removal.error ?? "target changed before deletion"})`);
454
692
  continue;
455
693
  }
456
694
  cleared.push(canonical);
@@ -465,10 +703,65 @@ function evictLockFiles() {
465
703
  }
466
704
  return { cleared, failed };
467
705
  }
706
+ function cleanupPluginOwnedFiles() {
707
+ let cleaned = false;
708
+ let hadTargets = false;
709
+ if (fs.existsSync(PLUGIN_CONFIG_PATH)) {
710
+ hadTargets = true;
711
+ const canonical = safeRealpathSync(PLUGIN_CONFIG_PATH, PLUGIN_CONFIG_PATH);
712
+ if (canonical === null || !isSafePluginConfigPath(canonical, CONFIG_DIR)) {
713
+ console.log(`\u2717 Refused to remove plugin config (failed safety check): ${canonical ?? PLUGIN_CONFIG_PATH}`);
714
+ } else {
715
+ const removal = removeBoundCleanupTarget(canonical, CONFIG_DIR, "file");
716
+ if (removal.ok) {
717
+ console.log(`\u2713 Removed plugin config: ${canonical}`);
718
+ cleaned = true;
719
+ } else {
720
+ console.log(`\u2717 Refused to remove plugin config (target changed): ${canonical} (${removal.error})`);
721
+ }
722
+ }
723
+ }
724
+ if (fs.existsSync(PROMPTS_DIR)) {
725
+ hadTargets = true;
726
+ const canonical = safeRealpathSync(PROMPTS_DIR, PROMPTS_DIR);
727
+ if (canonical === null || !isSafePromptsDir(canonical, CONFIG_DIR)) {
728
+ console.log(`\u2717 Refused to remove custom prompts (failed safety check): ${canonical ?? PROMPTS_DIR}`);
729
+ } else {
730
+ const removal = removeBoundCleanupTarget(canonical, CONFIG_DIR, "directory");
731
+ if (removal.ok) {
732
+ console.log(`\u2713 Removed custom prompts: ${canonical}`);
733
+ cleaned = true;
734
+ } else {
735
+ console.log(`\u2717 Refused to remove custom prompts (target changed): ${canonical} (${removal.error})`);
736
+ }
737
+ }
738
+ }
739
+ const backupPath = path.join(CONFIG_DIR, "opencode.swarm-install-backup.json");
740
+ if (fs.existsSync(backupPath)) {
741
+ hadTargets = true;
742
+ const canonical = safeRealpathSync(backupPath, backupPath);
743
+ if (canonical === null || !isSafeInstallBackupPath(canonical, CONFIG_DIR)) {
744
+ console.log(`\u2717 Refused to remove install backup (failed safety check): ${canonical ?? backupPath}`);
745
+ } else {
746
+ const removal = removeBoundCleanupTarget(canonical, CONFIG_DIR, "file");
747
+ if (removal.ok) {
748
+ console.log(`\u2713 Removed install backup: ${canonical}`);
749
+ cleaned = true;
750
+ } else {
751
+ console.log(`\u2717 Refused to remove install backup (target changed): ${canonical} (${removal.error})`);
752
+ }
753
+ }
754
+ }
755
+ if (!cleaned && !hadTargets) {
756
+ console.log("\u2713 No config files to clean up");
757
+ }
758
+ return { cleaned, hadTargets };
759
+ }
468
760
  async function uninstall() {
469
761
  try {
470
762
  console.log(`\uD83D\uDC1D Uninstalling OpenCode Swarm...
471
763
  `);
764
+ const cleanupResult = process.argv.includes("--clean") ? cleanupPluginOwnedFiles() : { cleaned: false, hadTargets: false };
472
765
  const opencodeConfig = loadJson(OPENCODE_CONFIG_PATH);
473
766
  if (!opencodeConfig) {
474
767
  if (fs.existsSync(OPENCODE_CONFIG_PATH)) {
@@ -476,7 +769,9 @@ async function uninstall() {
476
769
  return 1;
477
770
  } else {
478
771
  console.log(`\u26A0 No opencode config found at: ${OPENCODE_CONFIG_PATH}`);
479
- console.log("Nothing to uninstall.");
772
+ if (!cleanupResult.cleaned && !cleanupResult.hadTargets) {
773
+ console.log("Nothing to uninstall.");
774
+ }
480
775
  return 0;
481
776
  }
482
777
  }
@@ -508,43 +803,6 @@ async function uninstall() {
508
803
  saveJson(OPENCODE_CONFIG_PATH, opencodeConfig);
509
804
  console.log("\u2713 Removed opencode-swarm from OpenCode plugins");
510
805
  console.log("\u2713 Re-enabled default OpenCode agents (explore, general)");
511
- if (process.argv.includes("--clean")) {
512
- let cleaned = false;
513
- if (fs.existsSync(PLUGIN_CONFIG_PATH)) {
514
- const canonical = safeRealpathSync(PLUGIN_CONFIG_PATH, PLUGIN_CONFIG_PATH);
515
- if (canonical === null || !isSafePluginConfigPath(canonical)) {
516
- console.log(`\u2717 Refused to remove plugin config (failed safety check): ${canonical ?? PLUGIN_CONFIG_PATH}`);
517
- } else {
518
- fs.unlinkSync(canonical);
519
- console.log(`\u2713 Removed plugin config: ${canonical}`);
520
- cleaned = true;
521
- }
522
- }
523
- if (fs.existsSync(PROMPTS_DIR)) {
524
- const canonical = safeRealpathSync(PROMPTS_DIR, PROMPTS_DIR);
525
- if (canonical === null || !isSafePromptsDir(canonical)) {
526
- console.log(`\u2717 Refused to remove custom prompts (failed safety check): ${canonical ?? PROMPTS_DIR}`);
527
- } else {
528
- fs.rmSync(canonical, { recursive: true });
529
- console.log(`\u2713 Removed custom prompts: ${canonical}`);
530
- cleaned = true;
531
- }
532
- }
533
- const backupPath = path.join(CONFIG_DIR, "opencode.swarm-install-backup.json");
534
- if (fs.existsSync(backupPath)) {
535
- const canonical = safeRealpathSync(backupPath, backupPath);
536
- if (canonical === null || path.basename(canonical) !== "opencode.swarm-install-backup.json" || path.basename(path.dirname(canonical)) !== path.basename(CONFIG_DIR)) {
537
- console.log(`\u2717 Refused to remove install backup (failed safety check): ${canonical ?? backupPath}`);
538
- } else {
539
- fs.unlinkSync(canonical);
540
- console.log(`\u2713 Removed install backup: ${canonical}`);
541
- cleaned = true;
542
- }
543
- }
544
- if (!cleaned) {
545
- console.log("\u2713 No config files to clean up");
546
- }
547
- }
548
806
  console.log(`
549
807
  \u2705 Uninstall complete!`);
550
808
  return 0;
@@ -645,7 +903,7 @@ async function main() {
645
903
  const exitCode = await run(["ci", ...args.slice(1)]);
646
904
  process.exit(exitCode);
647
905
  } else if (command === "mcp") {
648
- const { handleMcpCommand } = await import("./mcp-43m5e2bb.js");
906
+ const { handleMcpCommand } = await import("./mcp-fdgfryxa.js");
649
907
  const exitCode = await handleMcpCommand(args.slice(1));
650
908
  process.exit(exitCode);
651
909
  } else {
@@ -713,7 +971,9 @@ export {
713
971
  isSafePromptsDir,
714
972
  isSafePluginConfigPath,
715
973
  isSafeLockFilePath,
974
+ isSafeInstallBackupPath,
716
975
  isSafeCachePath,
717
976
  evictPluginCaches,
718
- evictLockFiles
977
+ evictLockFiles,
978
+ _test_exports
719
979
  };
@@ -9,15 +9,15 @@ import {
9
9
  maybeEscalateOnViolation,
10
10
  maybeQuarantineOnContradiction,
11
11
  readRecentEscalations
12
- } from "./index-q7vtghff.js";
13
- import"./index-11z82cjy.js";
12
+ } from "./index-zm08qe68.js";
13
+ import"./index-3w3cssas.js";
14
14
  import"./index-wy3zpwjy.js";
15
15
  import"./index-2s7dvzff.js";
16
- import"./index-mybjc24z.js";
16
+ import"./index-5j6g6kqj.js";
17
17
  import"./index-frcemc0b.js";
18
18
  import"./index-0725jpp7.js";
19
19
  import"./index-zzvxj02e.js";
20
- import"./index-ae2kzm55.js";
20
+ import"./index-y1xtkdys.js";
21
21
  import"./index-vzm6zre7.js";
22
22
  import"./index-ae75rja9.js";
23
23
  import"./index-475bkab8.js";
@@ -30,9 +30,9 @@ import"./index-ejhxgyvm.js";
30
30
  import"./index-7g4c7s5r.js";
31
31
  import"./index-k53a69vb.js";
32
32
  import"./index-gx2vp8gx.js";
33
- import"./index-5qhkd1fc.js";
33
+ import"./index-d4rhnvtf.js";
34
34
  import"./index-zxdawwc7.js";
35
- import"./index-yjkgrerq.js";
35
+ import"./index-g1aa3sdm.js";
36
36
  import"./index-wzec3c4g.js";
37
37
  import"./index-bk5tah7q.js";
38
38
  import"./index-gknz3bv1.js";
@@ -29,10 +29,10 @@ import {
29
29
  resolveKnowledgeCounterBaselinePath,
30
30
  resolveKnowledgeEventsPath,
31
31
  resolveLegacyApplicationLogPath
32
- } from "./index-11z82cjy.js";
32
+ } from "./index-3w3cssas.js";
33
33
  import"./index-wy3zpwjy.js";
34
34
  import"./index-2s7dvzff.js";
35
- import"./index-mybjc24z.js";
35
+ import"./index-5j6g6kqj.js";
36
36
  import"./index-frcemc0b.js";
37
37
  import"./index-0725jpp7.js";
38
38
  import"./index-zzvxj02e.js";
@@ -47,9 +47,9 @@ import"./index-ejhxgyvm.js";
47
47
  import"./index-7g4c7s5r.js";
48
48
  import"./index-k53a69vb.js";
49
49
  import"./index-gx2vp8gx.js";
50
- import"./index-5qhkd1fc.js";
50
+ import"./index-d4rhnvtf.js";
51
51
  import"./index-zxdawwc7.js";
52
- import"./index-yjkgrerq.js";
52
+ import"./index-g1aa3sdm.js";
53
53
  import"./index-wzec3c4g.js";
54
54
  import"./index-bk5tah7q.js";
55
55
  import"./index-gknz3bv1.js";
@@ -43,7 +43,7 @@ import {
43
43
  transactKnowledge,
44
44
  transactKnowledgeWithCas,
45
45
  wordBigrams
46
- } from "./index-ae2kzm55.js";
46
+ } from "./index-y1xtkdys.js";
47
47
  import"./index-vzm6zre7.js";
48
48
  import {
49
49
  resolveHiveDataDir
@@ -23,10 +23,10 @@ import {
23
23
  validateLesson,
24
24
  validateSkillCandidatePath,
25
25
  validateSkillPath
26
- } from "./index-5qdhf317.js";
26
+ } from "./index-dtgj8d9k.js";
27
27
  import"./index-earq3mda.js";
28
28
  import"./index-rtry5xyf.js";
29
- import"./index-ae2kzm55.js";
29
+ import"./index-y1xtkdys.js";
30
30
  import"./index-vzm6zre7.js";
31
31
  import"./index-ae75rja9.js";
32
32
  import"./index-475bkab8.js";
@@ -95,7 +95,7 @@ function resolveMcpRoot(input) {
95
95
  }
96
96
  var _internals = {
97
97
  runMcpServer: async (options) => {
98
- const { runMcpServer } = await import("./server-b0teb7vg.js");
98
+ const { runMcpServer } = await import("./server-cxf8qmej.js");
99
99
  return runMcpServer(options);
100
100
  }
101
101
  };