@massa-ai/opencode-plugin 1.60.0 → 1.60.1

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 (2) hide show
  1. package/dist/config-cli.js +290 -101
  2. package/package.json +3 -3
@@ -2343,9 +2343,9 @@ function acquireLock(stateFilePath, options = {}) {
2343
2343
  }
2344
2344
  }
2345
2345
  // ../../packages/shared/dist/profile-switch/engine.js
2346
- import fs6 from "fs";
2347
- import path10 from "path";
2348
- import os6 from "os";
2346
+ import fs7 from "fs";
2347
+ import path11 from "path";
2348
+ import os7 from "os";
2349
2349
  import crypto3 from "crypto";
2350
2350
  import { execFileSync as execFileSync2 } from "child_process";
2351
2351
 
@@ -2425,12 +2425,13 @@ function selectRecord(records) {
2425
2425
  }
2426
2426
  return best ?? pool[pool.length - 1];
2427
2427
  }
2428
- function resolveClaudeMarketplaceRoot(opts = {}) {
2428
+ function resolveClaudeMarketplaceInstall(opts = {}) {
2429
2429
  const targetHome = opts.targetHome ?? os5.homedir();
2430
2430
  const pluginKey = opts.pluginKey ?? DEFAULT_PLUGIN_KEY;
2431
2431
  const directoryResult = resolveDirectorySourceRoot(targetHome, pluginKey);
2432
- if (directoryResult !== undefined)
2433
- return directoryResult;
2432
+ if (directoryResult !== undefined) {
2433
+ return directoryResult === null ? null : { root: directoryResult, route: "directory-source" };
2434
+ }
2434
2435
  const registryPath = path9.join(targetHome, ".claude", "plugins", "installed_plugins.json");
2435
2436
  let records;
2436
2437
  try {
@@ -2452,7 +2453,186 @@ function resolveClaudeMarketplaceRoot(opts = {}) {
2452
2453
  } catch {
2453
2454
  return null;
2454
2455
  }
2455
- return installPath;
2456
+ return { root: installPath, route: "registry-cache" };
2457
+ }
2458
+ function resolveClaudeMarketplaceRoot(opts = {}) {
2459
+ return resolveClaudeMarketplaceInstall(opts)?.root ?? null;
2460
+ }
2461
+ function readInstalledPluginVersion(opts = {}) {
2462
+ const targetHome = opts.targetHome ?? os5.homedir();
2463
+ const pluginKey = opts.pluginKey ?? DEFAULT_PLUGIN_KEY;
2464
+ const registryPath = path9.join(targetHome, ".claude", "plugins", "installed_plugins.json");
2465
+ let records;
2466
+ try {
2467
+ const parsed = JSON.parse(fs5.readFileSync(registryPath, "utf8"));
2468
+ records = parsed?.plugins?.[pluginKey];
2469
+ } catch {
2470
+ return null;
2471
+ }
2472
+ if (!Array.isArray(records) || records.length === 0)
2473
+ return null;
2474
+ return selectRecord(records)?.version ?? null;
2475
+ }
2476
+
2477
+ // ../../packages/shared/dist/profile-switch/doctor.js
2478
+ import fs6 from "fs";
2479
+ import os6 from "os";
2480
+ import path10 from "path";
2481
+
2482
+ // ../../packages/shared/dist/profile-switch/frontmatter.js
2483
+ function parseFrontmatter(raw) {
2484
+ const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/.exec(raw);
2485
+ if (!match) {
2486
+ throw new Error("charter missing YAML frontmatter (--- ... ---) block");
2487
+ }
2488
+ const yamlText = match[1] ?? "";
2489
+ const body = (match[2] ?? "").replace(/^\r?\n/, "");
2490
+ const frontmatter = parseSimpleYaml(yamlText);
2491
+ return { frontmatter, body };
2492
+ }
2493
+ function parseSimpleYaml(text) {
2494
+ const result = {};
2495
+ const lines = text.split(/\r?\n/);
2496
+ let i = 0;
2497
+ while (i < lines.length) {
2498
+ const line = lines[i] ?? "";
2499
+ if (line.trim() === "" || line.trim().startsWith("#")) {
2500
+ i++;
2501
+ continue;
2502
+ }
2503
+ const m = /^([A-Za-z_][A-Za-z0-9_]*):\s*(.*)$/.exec(line);
2504
+ if (!m) {
2505
+ i++;
2506
+ continue;
2507
+ }
2508
+ const key = m[1];
2509
+ const rest = (m[2] ?? "").trim();
2510
+ if (rest !== "") {
2511
+ result[key] = unquoteScalar(rest);
2512
+ i++;
2513
+ continue;
2514
+ }
2515
+ const nested = {};
2516
+ i++;
2517
+ while (i < lines.length) {
2518
+ const nestedLine = lines[i] ?? "";
2519
+ if (/^\s{2,}\S/.test(nestedLine) === false)
2520
+ break;
2521
+ const nm = /^\s{2,}([A-Za-z_][A-Za-z0-9_]*):\s*(.*)$/.exec(nestedLine);
2522
+ if (!nm)
2523
+ break;
2524
+ nested[nm[1]] = unquoteScalar((nm[2] ?? "").trim());
2525
+ i++;
2526
+ }
2527
+ result[key] = nested;
2528
+ }
2529
+ return result;
2530
+ }
2531
+ function unquoteScalar(s) {
2532
+ if (s.startsWith('"') && s.endsWith('"') || s.startsWith("'") && s.endsWith("'")) {
2533
+ return s.slice(1, -1);
2534
+ }
2535
+ return s;
2536
+ }
2537
+
2538
+ // ../../packages/shared/dist/profile-switch/doctor.js
2539
+ var ENV_OVERRIDE_VARS = ["CLAUDE_CODE_SUBAGENT_MODEL"];
2540
+ function readTextFile(filePath) {
2541
+ try {
2542
+ return fs6.readFileSync(filePath, "utf8");
2543
+ } catch {
2544
+ return null;
2545
+ }
2546
+ }
2547
+ function readJsonFile(filePath) {
2548
+ const raw = readTextFile(filePath);
2549
+ if (raw === null)
2550
+ return null;
2551
+ try {
2552
+ return JSON.parse(raw);
2553
+ } catch {
2554
+ return null;
2555
+ }
2556
+ }
2557
+ function readPluginVersion(pluginRoot) {
2558
+ const manifest = readJsonFile(path10.join(pluginRoot, ".claude-plugin", "plugin.json"));
2559
+ return typeof manifest?.version === "string" ? manifest.version : null;
2560
+ }
2561
+ function detectEnvOverride(env) {
2562
+ for (const name of ENV_OVERRIDE_VARS) {
2563
+ const value = env[name];
2564
+ if (typeof value === "string" && value.trim()) {
2565
+ return { name, value: value.trim() };
2566
+ }
2567
+ }
2568
+ return null;
2569
+ }
2570
+ function readRoles(liveRoot, activeProfile) {
2571
+ const agentsDir = path10.join(liveRoot, "agents");
2572
+ let entries;
2573
+ try {
2574
+ entries = fs6.readdirSync(agentsDir, { withFileTypes: true });
2575
+ } catch {
2576
+ return [];
2577
+ }
2578
+ const roles = [];
2579
+ for (const entry of entries) {
2580
+ if (!entry.isFile() || !entry.name.startsWith("massa-ai-") || !entry.name.endsWith(".md")) {
2581
+ continue;
2582
+ }
2583
+ const activeRaw = readTextFile(path10.join(agentsDir, entry.name));
2584
+ let model = null;
2585
+ let effort = null;
2586
+ if (activeRaw !== null) {
2587
+ try {
2588
+ const { frontmatter } = parseFrontmatter(activeRaw);
2589
+ model = typeof frontmatter.model === "string" ? frontmatter.model : null;
2590
+ effort = typeof frontmatter.effort === "string" ? frontmatter.effort : null;
2591
+ } catch {}
2592
+ }
2593
+ let staleVariant = false;
2594
+ if (activeProfile && activeRaw !== null) {
2595
+ const variantRaw = readTextFile(path10.join(liveRoot, "agent-profiles", activeProfile, entry.name));
2596
+ if (variantRaw !== null) {
2597
+ staleVariant = variantRaw !== activeRaw;
2598
+ }
2599
+ }
2600
+ roles.push({ name: entry.name, model, effort, staleVariant });
2601
+ }
2602
+ return roles.sort((a, b) => a.name.localeCompare(b.name));
2603
+ }
2604
+ function runtimeDriftReport(opts = {}) {
2605
+ const targetHome = opts.targetHome ?? os6.homedir();
2606
+ const stateFilePath = opts.stateFilePath ?? path10.join(targetHome, ".config", "massa-ai", "install-state.json");
2607
+ let state = opts.state ?? null;
2608
+ if (state === null) {
2609
+ try {
2610
+ state = readInstallState(stateFilePath);
2611
+ } catch {
2612
+ state = null;
2613
+ }
2614
+ }
2615
+ const platform = state?.platforms?.claude;
2616
+ const stateVersion = typeof platform?.plugin?.version === "string" ? platform.plugin.version : null;
2617
+ const activeProfile = platform?.modelProfile?.profile ?? null;
2618
+ const install = resolveClaudeMarketplaceInstall({ targetHome, pluginKey: opts.pluginKey });
2619
+ const liveRoot = install?.root ?? null;
2620
+ const sourceVersion = liveRoot === null ? null : readPluginVersion(liveRoot);
2621
+ const pinnedVersion = readInstalledPluginVersion({ targetHome, pluginKey: opts.pluginKey });
2622
+ const roles = liveRoot === null ? [] : readRoles(liveRoot, activeProfile);
2623
+ return {
2624
+ host: "claude",
2625
+ route: install?.route ?? "unresolved",
2626
+ liveRoot,
2627
+ sourceVersion,
2628
+ stateVersion,
2629
+ pinnedVersion,
2630
+ activeProfile,
2631
+ roles,
2632
+ envOverride: detectEnvOverride(opts.env ?? process.env),
2633
+ versionDrift: sourceVersion !== null && stateVersion !== null && sourceVersion !== stateVersion,
2634
+ profileMaterialized: roles.some((role) => role.staleVariant)
2635
+ };
2456
2636
  }
2457
2637
 
2458
2638
  // ../../packages/shared/dist/profile-switch/engine.js
@@ -2470,10 +2650,10 @@ function namedError3(name, message) {
2470
2650
  var UnknownProfileError = (profile, known) => namedError3("UnknownProfileError", `unknown profile "${profile}" \u2014 installed: ${known.length > 0 ? known.join(", ") : "none"}`);
2471
2651
  var NoHostsDetectedError = () => namedError3("NoHostsDetectedError", "no installed hosts found");
2472
2652
  function defaultStatePath(targetHome) {
2473
- return path10.join(targetHome, ".config", "massa-ai", "install-state.json");
2653
+ return path11.join(targetHome, ".config", "massa-ai", "install-state.json");
2474
2654
  }
2475
2655
  function resolveCommon(opts) {
2476
- const targetHome = opts.targetHome ?? os6.homedir();
2656
+ const targetHome = opts.targetHome ?? os7.homedir();
2477
2657
  const stateFilePath = opts.stateFilePath ?? defaultStatePath(targetHome);
2478
2658
  return { targetHome, stateFilePath };
2479
2659
  }
@@ -2481,7 +2661,7 @@ function marketplaceRoots(targetHome, state) {
2481
2661
  return state.platforms.claude?.installRoute === "marketplace" ? { claude: resolveClaudeMarketplaceRoot({ targetHome }) ?? undefined } : {};
2482
2662
  }
2483
2663
  function claudeMarketplaceUnresolvedReason(targetHome) {
2484
- const registryPath = path10.join(targetHome, ".claude", "plugins", "installed_plugins.json");
2664
+ const registryPath = path11.join(targetHome, ".claude", "plugins", "installed_plugins.json");
2485
2665
  return `claude installRoute is "marketplace" but no install root could be resolved from ${registryPath} ` + "\u2014 re-run the Claude plugin installer, or verify the plugin registry file";
2486
2666
  }
2487
2667
  function listProfiles(opts = {}) {
@@ -2489,6 +2669,12 @@ function listProfiles(opts = {}) {
2489
2669
  const state = readInstallState(stateFilePath);
2490
2670
  const roots = marketplaceRoots(targetHome, state);
2491
2671
  const universe = opts.hosts ?? HOSTS;
2672
+ const claudeDrift = universe.includes("claude") ? runtimeDriftReport({ targetHome, stateFilePath, state, env: opts.env }) : null;
2673
+ const claudeDriftFields = (host) => host === "claude" && claudeDrift !== null ? {
2674
+ liveRoot: claudeDrift.liveRoot,
2675
+ sourceVersion: claudeDrift.sourceVersion,
2676
+ envOverride: claudeDrift.envOverride ? `${claudeDrift.envOverride.name}=${claudeDrift.envOverride.value}` : null
2677
+ } : { liveRoot: null, sourceVersion: null, envOverride: null };
2492
2678
  const hosts = universe.map((host) => {
2493
2679
  if (host === "claude" && state.platforms.claude?.installRoute === "marketplace" && roots.claude === undefined) {
2494
2680
  const platform2 = state.platforms.claude;
@@ -2499,7 +2685,8 @@ function listProfiles(opts = {}) {
2499
2685
  skipReason: null,
2500
2686
  activeProfile: platform2.modelProfile?.profile ?? opts.hostDefaults?.[host] ?? "balanced",
2501
2687
  bundleVersion: platform2.plugin?.version ?? null,
2502
- availableProfiles: []
2688
+ availableProfiles: [],
2689
+ ...claudeDriftFields(host)
2503
2690
  };
2504
2691
  }
2505
2692
  const layout = resolveHostLayout(host, { targetHome, projectRoot: opts.projectRoot, marketplaceRoot: roots });
@@ -2511,10 +2698,11 @@ function listProfiles(opts = {}) {
2511
2698
  skipReason: layout.reason,
2512
2699
  activeProfile: null,
2513
2700
  bundleVersion: null,
2514
- availableProfiles: []
2701
+ availableProfiles: [],
2702
+ ...claudeDriftFields(host)
2515
2703
  };
2516
2704
  }
2517
- const installed = fs6.existsSync(layout.activeDir);
2705
+ const installed = fs7.existsSync(layout.activeDir);
2518
2706
  const availableProfiles = listVariantProfiles(layout);
2519
2707
  const platform = state.platforms[host];
2520
2708
  return {
@@ -2524,15 +2712,16 @@ function listProfiles(opts = {}) {
2524
2712
  skipReason: null,
2525
2713
  activeProfile: platform?.modelProfile?.profile ?? opts.hostDefaults?.[host] ?? "balanced",
2526
2714
  bundleVersion: platform?.plugin?.version ?? null,
2527
- availableProfiles
2715
+ availableProfiles,
2716
+ ...claudeDriftFields(host)
2528
2717
  };
2529
2718
  });
2530
2719
  return { hosts };
2531
2720
  }
2532
2721
  function listVariantProfiles(layout) {
2533
- if (!fs6.existsSync(layout.variantsRoot))
2722
+ if (!fs7.existsSync(layout.variantsRoot))
2534
2723
  return [];
2535
- return fs6.readdirSync(layout.variantsRoot, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
2724
+ return fs7.readdirSync(layout.variantsRoot, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
2536
2725
  }
2537
2726
  function matchesGlob(filename, glob) {
2538
2727
  const starIdx = glob.indexOf("*");
@@ -2543,7 +2732,7 @@ function matchesGlob(filename, glob) {
2543
2732
  return filename.length >= prefix.length + suffix.length && filename.startsWith(prefix) && filename.endsWith(suffix);
2544
2733
  }
2545
2734
  function matchingFileNames(dir, glob) {
2546
- return fs6.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && matchesGlob(e.name, glob)).map((e) => e.name);
2735
+ return fs7.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && matchesGlob(e.name, glob)).map((e) => e.name);
2547
2736
  }
2548
2737
  function detectGitAvailability(dir) {
2549
2738
  try {
@@ -2571,7 +2760,7 @@ function gitTrackedFileNames(dir, filenames) {
2571
2760
  var GUARD_PASS = { blocked: false, unchecked: false };
2572
2761
  var GUARD_UNCHECKED = { blocked: false, unchecked: true };
2573
2762
  function checkTrackedPathGuard(activeDir, filenames) {
2574
- if (filenames.length === 0 || !fs6.existsSync(activeDir))
2763
+ if (filenames.length === 0 || !fs7.existsSync(activeDir))
2575
2764
  return GUARD_PASS;
2576
2765
  const availability = detectGitAvailability(activeDir);
2577
2766
  if (availability === "no-git")
@@ -2582,53 +2771,53 @@ function checkTrackedPathGuard(activeDir, filenames) {
2582
2771
  if (tracked.size === 0)
2583
2772
  return GUARD_PASS;
2584
2773
  const offending = filenames.find((name) => tracked.has(name));
2585
- return { blocked: true, path: path10.join(activeDir, offending), unchecked: false };
2774
+ return { blocked: true, path: path11.join(activeDir, offending), unchecked: false };
2586
2775
  }
2587
2776
  function assertStateWritable(stateFilePath) {
2588
- const dir = path10.dirname(stateFilePath);
2777
+ const dir = path11.dirname(stateFilePath);
2589
2778
  try {
2590
- fs6.mkdirSync(dir, { recursive: true });
2779
+ fs7.mkdirSync(dir, { recursive: true });
2591
2780
  } catch (err) {
2592
2781
  throw UnwritableInstallStateError(stateFilePath, err.message);
2593
2782
  }
2594
- const checkPath = fs6.existsSync(stateFilePath) ? stateFilePath : dir;
2783
+ const checkPath = fs7.existsSync(stateFilePath) ? stateFilePath : dir;
2595
2784
  try {
2596
- fs6.accessSync(checkPath, fs6.constants.W_OK);
2785
+ fs7.accessSync(checkPath, fs7.constants.W_OK);
2597
2786
  } catch (err) {
2598
2787
  throw UnwritableInstallStateError(stateFilePath, err.message);
2599
2788
  }
2600
2789
  }
2601
2790
  function copyFileRouteVariant(layout, variantDir) {
2602
- fs6.mkdirSync(layout.activeDir, { recursive: true });
2791
+ fs7.mkdirSync(layout.activeDir, { recursive: true });
2603
2792
  let changed = 0;
2604
- for (const entry of fs6.readdirSync(variantDir, { withFileTypes: true })) {
2793
+ for (const entry of fs7.readdirSync(variantDir, { withFileTypes: true })) {
2605
2794
  if (!entry.isFile() || !matchesGlob(entry.name, layout.activeGlob))
2606
2795
  continue;
2607
- fs6.copyFileSync(path10.join(variantDir, entry.name), path10.join(layout.activeDir, entry.name));
2796
+ fs7.copyFileSync(path11.join(variantDir, entry.name), path11.join(layout.activeDir, entry.name));
2608
2797
  changed++;
2609
2798
  }
2610
2799
  return changed;
2611
2800
  }
2612
2801
  function repointOpencodeVariant(layout, variantDir) {
2613
- fs6.mkdirSync(layout.activeDir, { recursive: true });
2802
+ fs7.mkdirSync(layout.activeDir, { recursive: true });
2614
2803
  let changed = 0;
2615
- for (const entry of fs6.readdirSync(variantDir, { withFileTypes: true })) {
2804
+ for (const entry of fs7.readdirSync(variantDir, { withFileTypes: true })) {
2616
2805
  if (!entry.isFile() || !matchesGlob(entry.name, layout.activeGlob))
2617
2806
  continue;
2618
- const dest = path10.join(layout.activeDir, entry.name);
2619
- const target = path10.resolve(path10.join(variantDir, entry.name));
2807
+ const dest = path11.join(layout.activeDir, entry.name);
2808
+ const target = path11.resolve(path11.join(variantDir, entry.name));
2620
2809
  let destExists = true;
2621
2810
  let destIsSymlink = false;
2622
2811
  try {
2623
- destIsSymlink = fs6.lstatSync(dest).isSymbolicLink();
2812
+ destIsSymlink = fs7.lstatSync(dest).isSymbolicLink();
2624
2813
  } catch {
2625
2814
  destExists = false;
2626
2815
  }
2627
2816
  if (destExists && !destIsSymlink)
2628
2817
  continue;
2629
2818
  const tmp = `${dest}.massa-ai-switch.${crypto3.randomUUID()}`;
2630
- fs6.symlinkSync(target, tmp);
2631
- fs6.renameSync(tmp, dest);
2819
+ fs7.symlinkSync(target, tmp);
2820
+ fs7.renameSync(tmp, dest);
2632
2821
  changed++;
2633
2822
  }
2634
2823
  return changed;
@@ -2668,13 +2857,13 @@ function switchProfile(opts) {
2668
2857
  if (fileHosts.length === 0) {
2669
2858
  return { profile: opts.profile, dryRun, hosts: orderRows(universe, skipRows), restartRequired: false };
2670
2859
  }
2671
- const installedFileHosts = fileHosts.filter((h) => fs6.existsSync(h.layout.activeDir));
2860
+ const installedFileHosts = fileHosts.filter((h) => fs7.existsSync(h.layout.activeDir));
2672
2861
  if (installedFileHosts.length === 0)
2673
2862
  throw NoHostsDetectedError();
2674
2863
  const withAvailability = fileHosts.map((h) => {
2675
- const variantsRootExists = fs6.existsSync(h.layout.variantsRoot);
2864
+ const variantsRootExists = fs7.existsSync(h.layout.variantsRoot);
2676
2865
  const variantDir = h.layout.variantDir(opts.profile);
2677
- const available = variantsRootExists && fs6.existsSync(variantDir) && fs6.statSync(variantDir).isDirectory();
2866
+ const available = variantsRootExists && fs7.existsSync(variantDir) && fs7.statSync(variantDir).isDirectory();
2678
2867
  return { ...h, variantsRootExists, variantDir, available };
2679
2868
  });
2680
2869
  if (!withAvailability.some((h) => h.available)) {
@@ -2710,7 +2899,7 @@ function switchProfile(opts) {
2710
2899
  continue;
2711
2900
  }
2712
2901
  if (dryRun) {
2713
- rows.push({ host: h.host, status: "switched" });
2902
+ rows.push({ host: h.host, status: "would-switch" });
2714
2903
  continue;
2715
2904
  }
2716
2905
  const candidateNames = matchingFileNames(h.variantDir, h.layout.activeGlob);
@@ -2751,15 +2940,15 @@ function orderRows(universe, rows) {
2751
2940
  }
2752
2941
  // ../../packages/shared/dist/profile-switch/report.js
2753
2942
  function reportSucceeded(report) {
2754
- return report.hosts.every((h) => h.status === "switched" || h.status === "skipped");
2943
+ return report.hosts.every((h) => h.status === "switched" || h.status === "would-switch" || h.status === "skipped");
2755
2944
  }
2756
2945
  // ../../packages/shared/dist/profile-switch/variant-sync.js
2757
- import fs7 from "fs";
2758
- import path11 from "path";
2759
- import os7 from "os";
2946
+ import fs8 from "fs";
2947
+ import path12 from "path";
2948
+ import os8 from "os";
2760
2949
  import crypto4 from "crypto";
2761
2950
  function defaultStatePath2(targetHome) {
2762
- return path11.join(targetHome, ".config", "massa-ai", "install-state.json");
2951
+ return path12.join(targetHome, ".config", "massa-ai", "install-state.json");
2763
2952
  }
2764
2953
  function marketplaceRoots2(targetHome, state) {
2765
2954
  return state.platforms.claude?.installRoute === "marketplace" ? { claude: resolveClaudeMarketplaceRoot({ targetHome }) ?? undefined } : {};
@@ -2767,13 +2956,13 @@ function marketplaceRoots2(targetHome, state) {
2767
2956
  var tempFileCounter2 = 0;
2768
2957
  function writeFileIntoDirAtomically(destDir, destName, content) {
2769
2958
  const unique = `${process.pid}.${++tempFileCounter2}.${crypto4.randomBytes(6).toString("hex")}`;
2770
- const tempFile = path11.join(destDir, `.${destName}.${unique}.tmp`);
2959
+ const tempFile = path12.join(destDir, `.${destName}.${unique}.tmp`);
2771
2960
  try {
2772
- fs7.writeFileSync(tempFile, content);
2773
- fs7.renameSync(tempFile, path11.join(destDir, destName));
2961
+ fs8.writeFileSync(tempFile, content);
2962
+ fs8.renameSync(tempFile, path12.join(destDir, destName));
2774
2963
  } catch (error) {
2775
2964
  try {
2776
- fs7.unlinkSync(tempFile);
2965
+ fs8.unlinkSync(tempFile);
2777
2966
  } catch {}
2778
2967
  throw error;
2779
2968
  }
@@ -2781,20 +2970,20 @@ function writeFileIntoDirAtomically(destDir, destName, content) {
2781
2970
  function isSafeDirName(name) {
2782
2971
  if (name === "." || name === "..")
2783
2972
  return false;
2784
- if (name.includes("/") || name.includes("\\") || name.includes(path11.sep))
2973
+ if (name.includes("/") || name.includes("\\") || name.includes(path12.sep))
2785
2974
  return false;
2786
- return path11.basename(name) === name;
2975
+ return path12.basename(name) === name;
2787
2976
  }
2788
2977
  function syncHost(host, sourceRoot, targetHome, marketplaceRoot) {
2789
2978
  const layout = resolveHostLayout(host, { targetHome, marketplaceRoot });
2790
2979
  if (layout.route === "skip") {
2791
2980
  return { host, status: "skipped", profiles: [], retained: [], files: 0, reason: layout.reason };
2792
2981
  }
2793
- const srcDir = path11.join(sourceRoot, "apps", `${host}-plugin`, "agent-profiles");
2794
- if (!fs7.existsSync(srcDir) || !fs7.statSync(srcDir).isDirectory()) {
2982
+ const srcDir = path12.join(sourceRoot, "apps", `${host}-plugin`, "agent-profiles");
2983
+ if (!fs8.existsSync(srcDir) || !fs8.statSync(srcDir).isDirectory()) {
2795
2984
  return { host, status: "skipped", profiles: [], retained: [], files: 0, reason: "no generated variants for this host" };
2796
2985
  }
2797
- if (!fs7.existsSync(layout.variantsRoot)) {
2986
+ if (!fs8.existsSync(layout.variantsRoot)) {
2798
2987
  return {
2799
2988
  host,
2800
2989
  status: "skipped",
@@ -2806,24 +2995,24 @@ function syncHost(host, sourceRoot, targetHome, marketplaceRoot) {
2806
2995
  }
2807
2996
  const profiles = [];
2808
2997
  let files = 0;
2809
- for (const entry of fs7.readdirSync(srcDir, { withFileTypes: true })) {
2998
+ for (const entry of fs8.readdirSync(srcDir, { withFileTypes: true })) {
2810
2999
  if (!entry.isDirectory())
2811
3000
  continue;
2812
3001
  if (!isSafeDirName(entry.name))
2813
3002
  continue;
2814
- const srcProfileDir = path11.join(srcDir, entry.name);
2815
- const destProfileDir = path11.join(layout.variantsRoot, entry.name);
2816
- fs7.mkdirSync(destProfileDir, { recursive: true });
2817
- for (const fileEntry of fs7.readdirSync(srcProfileDir, { withFileTypes: true })) {
3003
+ const srcProfileDir = path12.join(srcDir, entry.name);
3004
+ const destProfileDir = path12.join(layout.variantsRoot, entry.name);
3005
+ fs8.mkdirSync(destProfileDir, { recursive: true });
3006
+ for (const fileEntry of fs8.readdirSync(srcProfileDir, { withFileTypes: true })) {
2818
3007
  if (!fileEntry.isFile())
2819
3008
  continue;
2820
- const content = fs7.readFileSync(path11.join(srcProfileDir, fileEntry.name));
3009
+ const content = fs8.readFileSync(path12.join(srcProfileDir, fileEntry.name));
2821
3010
  writeFileIntoDirAtomically(destProfileDir, fileEntry.name, content);
2822
3011
  files++;
2823
3012
  }
2824
3013
  profiles.push(entry.name);
2825
3014
  }
2826
- const retained = fs7.readdirSync(layout.variantsRoot, { withFileTypes: true }).filter((e) => e.isDirectory() && !profiles.includes(e.name)).map((e) => e.name).sort();
3015
+ const retained = fs8.readdirSync(layout.variantsRoot, { withFileTypes: true }).filter((e) => e.isDirectory() && !profiles.includes(e.name)).map((e) => e.name).sort();
2827
3016
  return { host, status: "synced", profiles: profiles.sort(), retained, files };
2828
3017
  }
2829
3018
  function syncGeneratedVariants(opts) {
@@ -2839,7 +3028,7 @@ function syncGeneratedVariants(opts) {
2839
3028
  }));
2840
3029
  }
2841
3030
  const sourceRoot = opts.sourceRoot;
2842
- const targetHome = opts.targetHome ?? os7.homedir();
3031
+ const targetHome = opts.targetHome ?? os8.homedir();
2843
3032
  const state = readInstallState(defaultStatePath2(targetHome));
2844
3033
  const roots = marketplaceRoots2(targetHome, state);
2845
3034
  return hosts.map((host) => {
@@ -2851,14 +3040,14 @@ function syncGeneratedVariants(opts) {
2851
3040
  });
2852
3041
  }
2853
3042
  // ../../packages/shared/dist/profile-switch/repo-root.js
2854
- import fs8 from "fs";
2855
- import path12 from "path";
3043
+ import fs9 from "fs";
3044
+ import path13 from "path";
2856
3045
  function findRepoRootWithMarker(startDir, marker, maxLevels) {
2857
3046
  let dir = startDir;
2858
3047
  for (let i = 0;i <= maxLevels; i++) {
2859
- if (fs8.existsSync(path12.join(dir, marker)))
3048
+ if (fs9.existsSync(path13.join(dir, marker)))
2860
3049
  return dir;
2861
- const parent = path12.dirname(dir);
3050
+ const parent = path13.dirname(dir);
2862
3051
  if (parent === dir)
2863
3052
  break;
2864
3053
  dir = parent;
@@ -2953,7 +3142,7 @@ function assertKnownRuleId(id) {
2953
3142
  }
2954
3143
  // ../../packages/shared/dist/bootstrap/state.js
2955
3144
  init_config_loader();
2956
- import fs9 from "fs";
3145
+ import fs10 from "fs";
2957
3146
  var BOOTSTRAP_STATE_KEY = "bootstrap";
2958
3147
  var BOOTSTRAP_RULES_KEY = "rules";
2959
3148
  var BOOTSTRAP_STATE_PATH = `${BOOTSTRAP_STATE_KEY}.${BOOTSTRAP_RULES_KEY}`;
@@ -2987,7 +3176,7 @@ function resolveBootstrapState(doc) {
2987
3176
  }
2988
3177
  function readConfigBytes() {
2989
3178
  try {
2990
- return fs9.readFileSync(getConfigPath(), "utf-8");
3179
+ return fs10.readFileSync(getConfigPath(), "utf-8");
2991
3180
  } catch (error) {
2992
3181
  if (error?.code === "ENOENT")
2993
3182
  return "";
@@ -3035,7 +3224,7 @@ function setBootstrapRuleEnabled(id, enabled) {
3035
3224
  };
3036
3225
  }
3037
3226
  // ../../packages/shared/dist/bootstrap/render.js
3038
- import path13 from "path";
3227
+ import path14 from "path";
3039
3228
  var BOOTSTRAP_BLOCK_START = "<!-- massa-ai:bootstrap:start -->";
3040
3229
  var BOOTSTRAP_BLOCK_END = "<!-- massa-ai:bootstrap:end -->";
3041
3230
  var CONTRACT_FILENAME = "MASSA-AI.md";
@@ -3067,19 +3256,19 @@ var HOST_CONFIG_DIR = {
3067
3256
  function resolveHostRoot(host, targetHome, hostRoot) {
3068
3257
  requireAbsoluteTargetHome(targetHome);
3069
3258
  if (hostRoot === undefined)
3070
- return path13.join(targetHome, ...HOST_CONFIG_DIR[host]);
3071
- const relative = path13.relative(targetHome, hostRoot);
3072
- if (!path13.isAbsolute(hostRoot) || relative === "" || relative.startsWith("..") || path13.isAbsolute(relative)) {
3259
+ return path14.join(targetHome, ...HOST_CONFIG_DIR[host]);
3260
+ const relative = path14.relative(targetHome, hostRoot);
3261
+ if (!path14.isAbsolute(hostRoot) || relative === "" || relative.startsWith("..") || path14.isAbsolute(relative)) {
3073
3262
  throw new BootstrapRenderError("HostRootOutsideTargetHomeError", `hostRoot must be an absolute directory inside targetHome, got "${hostRoot}" for targetHome "${targetHome}"`, [hostRoot, targetHome]);
3074
3263
  }
3075
3264
  return hostRoot;
3076
3265
  }
3077
3266
  function bootstrapContractPath(host, targetHome, hostRoot) {
3078
- return path13.join(resolveHostRoot(host, targetHome, hostRoot), CONTRACT_FILENAME);
3267
+ return path14.join(resolveHostRoot(host, targetHome, hostRoot), CONTRACT_FILENAME);
3079
3268
  }
3080
3269
  function bootstrapStateFilePath(targetHome) {
3081
3270
  requireAbsoluteTargetHome(targetHome);
3082
- return path13.join(targetHome, ".config", "massa-ai", "config.json");
3271
+ return path14.join(targetHome, ".config", "massa-ai", "config.json");
3083
3272
  }
3084
3273
  function renderBootstrap(options) {
3085
3274
  const { source, state, host, targetHome, hostRoot } = options;
@@ -3102,7 +3291,7 @@ ${body}`;
3102
3291
  return { contract, pointer };
3103
3292
  }
3104
3293
  function requireAbsoluteTargetHome(targetHome) {
3105
- if (!path13.isAbsolute(targetHome)) {
3294
+ if (!path14.isAbsolute(targetHome)) {
3106
3295
  throw new BootstrapRenderError("TargetHomeNotAbsoluteError", `targetHome must be an absolute path, got "${targetHome}"`, [targetHome]);
3107
3296
  }
3108
3297
  }
@@ -3255,8 +3444,8 @@ function buildBootstrapReport(input) {
3255
3444
  }
3256
3445
  // ../../packages/shared/dist/bootstrap/engine.js
3257
3446
  init_config_loader();
3258
- import fs10 from "fs";
3259
- import path14 from "path";
3447
+ import fs11 from "fs";
3448
+ import path15 from "path";
3260
3449
  var INSTALL_STATE_FILENAME = "install-state.json";
3261
3450
  var WIRING_REMEDY = "scripts/install-skills.sh --apply";
3262
3451
 
@@ -3273,7 +3462,7 @@ function applyBootstrapState(options) {
3273
3462
  const dryRun = options.dryRun ?? false;
3274
3463
  const warn = options.onWarning ?? ((message) => console.warn(message));
3275
3464
  const configPath = bootstrapStateFilePath(targetHome);
3276
- const installStatePath = path14.join(path14.dirname(configPath), INSTALL_STATE_FILENAME);
3465
+ const installStatePath = path15.join(path15.dirname(configPath), INSTALL_STATE_FILENAME);
3277
3466
  const { platforms } = readInstallState(installStatePath);
3278
3467
  const installed = HOSTS.filter((host) => platforms[host] !== undefined);
3279
3468
  if (installed.length === 0) {
@@ -3366,22 +3555,22 @@ function applyHost(input) {
3366
3555
  }
3367
3556
  function wiringArtifact(host, targetHome, hostRoot) {
3368
3557
  const root = resolveHostRoot(host, targetHome, hostRoot);
3369
- const contractPath = path14.join(root, CONTRACT_FILENAME);
3558
+ const contractPath = path15.join(root, CONTRACT_FILENAME);
3370
3559
  switch (host) {
3371
3560
  case "claude":
3372
- return { file: path14.join(root, "CLAUDE.md"), token: `@${CONTRACT_FILENAME}` };
3561
+ return { file: path15.join(root, "CLAUDE.md"), token: `@${CONTRACT_FILENAME}` };
3373
3562
  case "codex":
3374
3563
  case "cursor":
3375
- return { file: path14.join(root, "AGENTS.md"), token: contractPath };
3564
+ return { file: path15.join(root, "AGENTS.md"), token: contractPath };
3376
3565
  case "opencode":
3377
3566
  return { file: openCodeConfigPath(root), token: `"${contractPath}"` };
3378
3567
  }
3379
3568
  }
3380
3569
  function openCodeConfigPath(root) {
3381
- const json = path14.join(root, "opencode.json");
3382
- if (fs10.existsSync(json))
3570
+ const json = path15.join(root, "opencode.json");
3571
+ if (fs11.existsSync(json))
3383
3572
  return json;
3384
- return path14.join(root, "opencode.jsonc");
3573
+ return path15.join(root, "opencode.jsonc");
3385
3574
  }
3386
3575
  function isWired(host, targetHome, hostRoot) {
3387
3576
  const artifact = wiringArtifact(host, targetHome, hostRoot);
@@ -3394,7 +3583,7 @@ function notWiredReason(host, targetHome, hostRoot) {
3394
3583
  }
3395
3584
  function readFileOrNull(filePath) {
3396
3585
  try {
3397
- return fs10.readFileSync(filePath, "utf-8");
3586
+ return fs11.readFileSync(filePath, "utf-8");
3398
3587
  } catch {
3399
3588
  return null;
3400
3589
  }
@@ -3440,11 +3629,11 @@ function formatBootstrapReport(report) {
3440
3629
  init_config_loader();
3441
3630
  // src/config-cli.ts
3442
3631
  init_inference_providers();
3443
- import { promises as fs11 } from "fs";
3444
- import path15 from "path";
3445
- import os8 from "os";
3632
+ import { promises as fs12 } from "fs";
3633
+ import path16 from "path";
3634
+ import os9 from "os";
3446
3635
  import { fileURLToPath } from "url";
3447
- var __dirname2 = path15.dirname(fileURLToPath(import.meta.url));
3636
+ var __dirname2 = path16.dirname(fileURLToPath(import.meta.url));
3448
3637
  var WRITABLE_PROVIDERS = ["ollama", "lmstudio", "mistral", "openai", "google", "cohere"];
3449
3638
  var GENERATOR_MARKER = "scripts/generate-subagent-artifacts.ts";
3450
3639
  var GENERATOR_MARKER_MAX_LEVELS = 6;
@@ -3722,18 +3911,18 @@ Using defaults:`);
3722
3911
  return 1;
3723
3912
  }
3724
3913
  const scope = typeof options.project === "boolean" ? "project" : "user";
3725
- const agentsDir = scope === "project" ? path15.join(process.cwd(), ".opencode/agents") : path15.join(process.env.XDG_CONFIG_HOME && process.env.XDG_CONFIG_HOME.trim() || path15.join(os8.homedir(), ".config"), "opencode", "agents");
3726
- const sourceAgentsDir = path15.resolve(__dirname2, "..", "agents");
3914
+ const agentsDir = scope === "project" ? path16.join(process.cwd(), ".opencode/agents") : path16.join(process.env.XDG_CONFIG_HOME && process.env.XDG_CONFIG_HOME.trim() || path16.join(os9.homedir(), ".config"), "opencode", "agents");
3915
+ const sourceAgentsDir = path16.resolve(__dirname2, "..", "agents");
3727
3916
  if (subcommand === "install") {
3728
- await fs11.mkdir(agentsDir, { recursive: true });
3917
+ await fs12.mkdir(agentsDir, { recursive: true });
3729
3918
  let count = 0;
3730
- const entries = await fs11.readdir(sourceAgentsDir);
3919
+ const entries = await fs12.readdir(sourceAgentsDir);
3731
3920
  for (const entry of entries) {
3732
3921
  if (!entry.startsWith("massa-ai-") || !entry.endsWith(".md"))
3733
3922
  continue;
3734
- const src = path15.join(sourceAgentsDir, entry);
3735
- const dest = path15.join(agentsDir, entry);
3736
- await fs11.copyFile(src, dest);
3923
+ const src = path16.join(sourceAgentsDir, entry);
3924
+ const dest = path16.join(agentsDir, entry);
3925
+ await fs12.copyFile(src, dest);
3737
3926
  count++;
3738
3927
  }
3739
3928
  console.log(`+ ${count} subagent specialists (generated from skills/agents/*/SKILL.md)`);
@@ -3741,14 +3930,14 @@ Using defaults:`);
3741
3930
  } else {
3742
3931
  let removed = 0;
3743
3932
  try {
3744
- const entries = await fs11.readdir(agentsDir);
3933
+ const entries = await fs12.readdir(agentsDir);
3745
3934
  for (const entry of entries) {
3746
3935
  if (!entry.startsWith("massa-ai-") || !entry.endsWith(".md"))
3747
3936
  continue;
3748
- const filePath = path15.join(agentsDir, entry);
3749
- const content = await fs11.readFile(filePath, "utf8");
3937
+ const filePath = path16.join(agentsDir, entry);
3938
+ const content = await fs12.readFile(filePath, "utf8");
3750
3939
  if (content.includes("massa-ai-owned: true")) {
3751
- await fs11.unlink(filePath);
3940
+ await fs12.unlink(filePath);
3752
3941
  removed++;
3753
3942
  }
3754
3943
  }
@@ -3825,9 +4014,9 @@ Using defaults:`);
3825
4014
  return 1;
3826
4015
  }
3827
4016
  const targetOpt = typeof options.target === "string" ? options.target : undefined;
3828
- const targetHome = targetOpt === undefined ? os8.homedir() : path15.resolve(targetOpt);
3829
- if (targetHome !== os8.homedir() && options.yes !== true) {
3830
- console.error(`Error: --target ${targetHome} is not your home (${os8.homedir()}) \u2014 pass --yes to confirm writing there`);
4017
+ const targetHome = targetOpt === undefined ? os9.homedir() : path16.resolve(targetOpt);
4018
+ if (targetHome !== os9.homedir() && options.yes !== true) {
4019
+ console.error(`Error: --target ${targetHome} is not your home (${os9.homedir()}) \u2014 pass --yes to confirm writing there`);
3831
4020
  return 1;
3832
4021
  }
3833
4022
  const dryRun = options["dry-run"] === true;
@@ -3845,7 +4034,7 @@ Using defaults:`);
3845
4034
  const report = applyBootstrapState({
3846
4035
  targetHome,
3847
4036
  dryRun,
3848
- sourcePath: repoRoot === null ? undefined : path15.join(repoRoot, "skills", "AGENTS.md")
4037
+ sourcePath: repoRoot === null ? undefined : path16.join(repoRoot, "skills", "AGENTS.md")
3849
4038
  });
3850
4039
  console.log(formatBootstrapReport(report));
3851
4040
  return bootstrapReportSucceeded(report) ? 0 : 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@massa-ai/opencode-plugin",
3
- "version": "1.60.0",
3
+ "version": "1.60.1",
4
4
  "description": "massa-ai plugin for OpenCode - Semantic code search, memory, and context compression",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -24,8 +24,8 @@
24
24
  "dependencies": {
25
25
  "@opencode-ai/plugin": "^1.2.15",
26
26
  "@opencode-ai/sdk": "^1.2.15",
27
- "@massa-ai/core": "^1.60.0",
28
- "@massa-ai/shared": "^1.60.0"
27
+ "@massa-ai/core": "^1.60.1",
28
+ "@massa-ai/shared": "^1.60.1"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/node": "^22.10.5",