@wpro-eng/opencode-config 1.0.0 → 1.1.2

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.
package/dist/index.js CHANGED
@@ -4,25 +4,43 @@ var __getProtoOf = Object.getPrototypeOf;
4
4
  var __defProp = Object.defineProperty;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ function __accessProp(key) {
8
+ return this[key];
9
+ }
10
+ var __toESMCache_node;
11
+ var __toESMCache_esm;
7
12
  var __toESM = (mod, isNodeMode, target) => {
13
+ var canCache = mod != null && typeof mod === "object";
14
+ if (canCache) {
15
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
16
+ var cached = cache.get(mod);
17
+ if (cached)
18
+ return cached;
19
+ }
8
20
  target = mod != null ? __create(__getProtoOf(mod)) : {};
9
21
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
22
  for (let key of __getOwnPropNames(mod))
11
23
  if (!__hasOwnProp.call(to, key))
12
24
  __defProp(to, key, {
13
- get: () => mod[key],
25
+ get: __accessProp.bind(mod, key),
14
26
  enumerable: true
15
27
  });
28
+ if (canCache)
29
+ cache.set(mod, to);
16
30
  return to;
17
31
  };
18
32
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
33
+ var __returnValue = (v) => v;
34
+ function __exportSetter(name, newValue) {
35
+ this[name] = __returnValue.bind(null, newValue);
36
+ }
19
37
  var __export = (target, all) => {
20
38
  for (var name in all)
21
39
  __defProp(target, name, {
22
40
  get: all[name],
23
41
  enumerable: true,
24
42
  configurable: true,
25
- set: (newValue) => all[name] = () => newValue
43
+ set: __exportSetter.bind(all, name)
26
44
  });
27
45
  };
28
46
  var __require = import.meta.require;
@@ -7476,7 +7494,6 @@ var NEVER = INVALID;
7476
7494
  // src/config.ts
7477
7495
  import { existsSync, readFileSync } from "fs";
7478
7496
  import { dirname, join as join2 } from "path";
7479
- import { homedir as homedir2 } from "os";
7480
7497
 
7481
7498
  // node_modules/jsonc-parser/lib/esm/impl/scanner.js
7482
7499
  function createScanner(text, ignoreTrivia = false) {
@@ -8283,7 +8300,6 @@ var ParseErrorCode;
8283
8300
  })(ParseErrorCode || (ParseErrorCode = {}));
8284
8301
 
8285
8302
  // src/constants.ts
8286
- var TEAM_REPO_URL = "git@github.com:wpromote/opencode-config.git";
8287
8303
  var REPO_SHORT_NAME = "opencode-config";
8288
8304
  var CONFIG_FILENAMES = ["wpromote.json", "wpromote.jsonc"];
8289
8305
 
@@ -8329,7 +8345,6 @@ function logWarn(message) {
8329
8345
  // src/config.ts
8330
8346
  var UserConfigSchema = exports_external.object({
8331
8347
  disable: exports_external.array(exports_external.string()).default([]),
8332
- ref: exports_external.string().optional(),
8333
8348
  installMethod: exports_external.enum(["link", "copy"]).default("link"),
8334
8349
  dryRun: exports_external.boolean().default(false),
8335
8350
  orchestration: exports_external.object({
@@ -8390,7 +8405,6 @@ var UserConfigSchema = exports_external.object({
8390
8405
  }).strict();
8391
8406
  var DEFAULT_CONFIG = {
8392
8407
  disable: [],
8393
- ref: undefined,
8394
8408
  installMethod: "link",
8395
8409
  dryRun: false,
8396
8410
  orchestration: {
@@ -8445,7 +8459,8 @@ var DEFAULT_CONFIG = {
8445
8459
  };
8446
8460
  function getConfigPaths() {
8447
8461
  const projectDir = join2(process.cwd(), ".opencode");
8448
- const userDir = join2(homedir2(), ".config", "opencode");
8462
+ const home = process.env.HOME || process.env.USERPROFILE || "~";
8463
+ const userDir = join2(home, ".config", "opencode");
8449
8464
  return [
8450
8465
  ...CONFIG_FILENAMES.map((filename) => join2(projectDir, filename)),
8451
8466
  ...CONFIG_FILENAMES.map((filename) => join2(userDir, filename))
@@ -8485,9 +8500,8 @@ function loadConfigWithLocation() {
8485
8500
  return { config: DEFAULT_CONFIG, configDir: null };
8486
8501
  }
8487
8502
 
8488
- // src/git.ts
8503
+ // src/assets.ts
8489
8504
  var import_gray_matter = __toESM(require_gray_matter(), 1);
8490
- var {$ } = globalThis.Bun;
8491
8505
  import * as path from "path";
8492
8506
  import * as fs from "fs";
8493
8507
 
@@ -8610,201 +8624,27 @@ function discoverInstructions(repoPath) {
8610
8624
  return instructions;
8611
8625
  }
8612
8626
 
8613
- // src/repo.ts
8614
- function getRepoId(url) {
8615
- const sshMatch = url.match(/^git@([^:]+):(.+?)(\.git)?$/);
8616
- if (sshMatch) {
8617
- const host = sshMatch[1];
8618
- const p = sshMatch[2].replace(/\//g, "-");
8619
- return `${host}-${p}`;
8620
- }
8621
- const httpsMatch = url.match(/^https?:\/\/([^/]+)\/(.+?)(\.git)?$/);
8622
- if (httpsMatch) {
8623
- const host = httpsMatch[1];
8624
- const p = httpsMatch[2].replace(/\//g, "-");
8625
- return `${host}-${p}`;
8626
- }
8627
- return url.replace(/[^a-zA-Z0-9-]/g, "-").replace(/-+/g, "-").slice(0, 100);
8628
- }
8629
-
8630
- // src/git.ts
8631
- var DEFAULT_RETRY_CONFIG = {
8632
- maxRetries: 3,
8633
- initialDelayMs: 1000,
8634
- maxDelayMs: 30000,
8635
- backoffMultiplier: 2
8636
- };
8637
- function sleep(ms) {
8638
- return new Promise((resolve2) => setTimeout(resolve2, ms));
8639
- }
8640
- async function withRetry(operation, operationName, config = DEFAULT_RETRY_CONFIG) {
8641
- let lastError;
8642
- let delay = config.initialDelayMs;
8643
- for (let attempt = 0;attempt <= config.maxRetries; attempt++) {
8644
- try {
8645
- return await operation();
8646
- } catch (error) {
8647
- lastError = error;
8648
- const isNetworkError = isNetworkRelatedError(lastError);
8649
- if (!isNetworkError) {
8650
- throw lastError;
8651
- }
8652
- if (attempt >= config.maxRetries) {
8653
- logError(`${operationName} failed after ${config.maxRetries + 1} attempts: ${lastError.message}`);
8654
- throw lastError;
8655
- }
8656
- logWarn(`${operationName} failed (attempt ${attempt + 1}/${config.maxRetries + 1}), retrying in ${delay}ms: ${lastError.message}`);
8657
- await sleep(delay);
8658
- delay = Math.min(delay * config.backoffMultiplier, config.maxDelayMs);
8659
- }
8660
- }
8661
- throw lastError || new Error(`${operationName} failed`);
8662
- }
8663
- function isNetworkRelatedError(error) {
8664
- const message = error.message.toLowerCase();
8665
- const networkPatterns = [
8666
- "could not resolve host",
8667
- "connection refused",
8668
- "connection timed out",
8669
- "connection reset",
8670
- "network is unreachable",
8671
- "no route to host",
8672
- "temporary failure in name resolution",
8673
- "ssl",
8674
- "tls",
8675
- "certificate",
8676
- "handshake",
8677
- "unable to access",
8678
- "couldn't connect",
8679
- "failed to connect",
8680
- "ssh_exchange_identification",
8681
- "kex_exchange_identification",
8682
- "connection closed by remote host",
8683
- "early eof",
8684
- "unexpected disconnect",
8685
- "remote end hung up",
8686
- "the remote end hung up unexpectedly"
8687
- ];
8688
- return networkPatterns.some((pattern) => message.includes(pattern));
8689
- }
8690
- var CACHE_BASE = path.join(process.env.HOME || "~", ".cache", "opencode", "wpromote-config", "repos");
8627
+ // src/assets.ts
8691
8628
  var DISCOVERY_LIMITS = {
8692
8629
  maxFiles: 100,
8693
8630
  maxFileSize: 256 * 1024,
8694
8631
  maxDepth: 10
8695
8632
  };
8696
- function getRepoPath(url) {
8697
- return path.join(CACHE_BASE, getRepoId(url));
8698
- }
8699
- function isCloned(repoPath) {
8700
- return fs.existsSync(path.join(repoPath, ".git"));
8701
- }
8702
- async function cloneRepo(url, repoPath, retryConfig = DEFAULT_RETRY_CONFIG) {
8703
- fs.mkdirSync(path.dirname(repoPath), { recursive: true });
8704
- await withRetry(async () => {
8705
- const result = await $`git clone ${url} ${repoPath}`.quiet().nothrow();
8706
- if (result.exitCode !== 0) {
8707
- const stderr = result.stderr.toString().trim();
8708
- const stdout = result.stdout.toString().trim();
8709
- const output = [stderr, stdout].filter(Boolean).join(`
8710
- `) || `exit code ${result.exitCode}`;
8711
- throw new Error(`git clone failed: ${output}`);
8712
- }
8713
- }, `git clone ${url}`, retryConfig);
8714
- }
8715
- async function fetchAndCheckout(repoPath, ref, retryConfig = DEFAULT_RETRY_CONFIG) {
8716
- const beforeCommit = await $`git -C ${repoPath} rev-parse HEAD`.quiet();
8717
- const beforeHash = beforeCommit.stdout.toString().trim();
8718
- await withRetry(async () => {
8719
- const fetchResult = await $`git -C ${repoPath} fetch --all --prune`.quiet();
8720
- if (fetchResult.exitCode !== 0) {
8721
- const stderr = fetchResult.stderr.toString().trim();
8722
- const stdout = fetchResult.stdout.toString().trim();
8723
- const output = [stderr, stdout].filter(Boolean).join(`
8724
- `) || `exit code ${fetchResult.exitCode}`;
8725
- throw new Error(`git fetch failed: ${output}`);
8726
- }
8727
- }, `git fetch (${repoPath})`, retryConfig);
8728
- const checkoutResult = await $`git -C ${repoPath} checkout ${ref}`.quiet().nothrow();
8729
- if (checkoutResult.exitCode !== 0) {
8730
- const stderr = checkoutResult.stderr.toString().trim();
8731
- const stdout = checkoutResult.stdout.toString().trim();
8732
- const output = [stderr, stdout].filter(Boolean).join(`
8733
- `) || `exit code ${checkoutResult.exitCode}`;
8734
- throw new Error(`git checkout ${ref} failed: ${output}`);
8735
- }
8736
- const isBranch = await $`git -C ${repoPath} symbolic-ref -q HEAD`.quiet().nothrow();
8737
- if (isBranch.exitCode === 0) {
8738
- await withRetry(async () => {
8739
- const pullResult = await $`git -C ${repoPath} pull --ff-only`.quiet();
8740
- if (pullResult.exitCode !== 0) {
8741
- const stderr = pullResult.stderr.toString().trim();
8742
- const stdout = pullResult.stdout.toString().trim();
8743
- const output = [stderr, stdout].filter(Boolean).join(`
8744
- `) || `exit code ${pullResult.exitCode}`;
8745
- throw new Error(`git pull failed: ${output}`);
8746
- }
8747
- }, `git pull (${repoPath})`, retryConfig);
8748
- }
8749
- const afterCommit = await $`git -C ${repoPath} rev-parse HEAD`.quiet();
8750
- const afterHash = afterCommit.stdout.toString().trim();
8751
- return beforeHash !== afterHash;
8752
- }
8753
- async function getCurrentRef(repoPath) {
8754
- const branchResult = await $`git -C ${repoPath} symbolic-ref --short HEAD`.quiet().nothrow();
8755
- if (branchResult.exitCode === 0)
8756
- return branchResult.stdout.toString().trim();
8757
- const commitResult = await $`git -C ${repoPath} rev-parse --short HEAD`.quiet();
8758
- return commitResult.stdout.toString().trim();
8759
- }
8760
- async function syncTeamRepository(ref) {
8761
- const effectiveRef = ref ?? "main";
8762
- const repoId = getRepoId(TEAM_REPO_URL);
8763
- const repoPath = getRepoPath(TEAM_REPO_URL);
8764
- const shortName = REPO_SHORT_NAME;
8765
- let updated = false;
8766
- let error;
8767
- try {
8768
- if (!isCloned(repoPath)) {
8769
- if (fs.existsSync(repoPath)) {
8770
- fs.rmSync(repoPath, { recursive: true, force: true });
8771
- }
8772
- await cloneRepo(TEAM_REPO_URL, repoPath);
8773
- updated = true;
8774
- }
8775
- updated = await fetchAndCheckout(repoPath, effectiveRef) || updated;
8776
- } catch (err) {
8777
- error = err instanceof Error ? err.message : String(err);
8778
- }
8779
- let skills = [];
8780
- let agents = [];
8781
- let commands = [];
8782
- let plugins = [];
8783
- let instructions = [];
8784
- let mcps = [];
8785
- let currentRef = effectiveRef;
8786
- if (isCloned(repoPath)) {
8787
- currentRef = await getCurrentRef(repoPath);
8788
- skills = await discoverSkills(repoPath);
8789
- agents = await discoverAgents(repoPath);
8790
- commands = await discoverCommands(repoPath);
8791
- plugins = await discoverPlugins(repoPath, shortName);
8792
- instructions = discoverInstructions(repoPath);
8793
- mcps = await discoverMcps(repoPath);
8794
- }
8633
+ async function discoverAssets(packageRoot) {
8634
+ const skills = await discoverSkills(packageRoot);
8635
+ const agents = await discoverAgents(packageRoot);
8636
+ const commands = await discoverCommands(packageRoot);
8637
+ const plugins = await discoverPlugins(packageRoot, REPO_SHORT_NAME);
8638
+ const instructions = discoverInstructions(packageRoot);
8639
+ const mcps = await discoverMcps(packageRoot);
8795
8640
  return {
8796
- repoId,
8797
- repoPath,
8798
- shortName,
8799
- ref: currentRef,
8641
+ shortName: REPO_SHORT_NAME,
8800
8642
  skills,
8801
8643
  agents,
8802
8644
  commands,
8803
8645
  plugins,
8804
8646
  instructions,
8805
- mcps,
8806
- updated,
8807
- error
8647
+ mcps
8808
8648
  };
8809
8649
  }
8810
8650
  async function discoverSkills(repoPath) {
@@ -9130,10 +8970,10 @@ async function discoverMcps(repoPath) {
9130
8970
  // src/install.ts
9131
8971
  import * as path3 from "path";
9132
8972
  import * as fs3 from "fs";
9133
- var {$: $3 } = globalThis.Bun;
8973
+ var {$: $2 } = globalThis.Bun;
9134
8974
 
9135
8975
  // src/copy.ts
9136
- var {$: $2 } = globalThis.Bun;
8976
+ var {$ } = globalThis.Bun;
9137
8977
  import * as fs2 from "fs";
9138
8978
  import * as path2 from "path";
9139
8979
  var rsyncAvailable = null;
@@ -9142,7 +8982,7 @@ async function detectRsync() {
9142
8982
  return rsyncAvailable;
9143
8983
  }
9144
8984
  try {
9145
- const result = await $2`which rsync`.quiet();
8985
+ const result = await $`which rsync`.quiet();
9146
8986
  rsyncAvailable = result.exitCode === 0;
9147
8987
  } catch {
9148
8988
  rsyncAvailable = false;
@@ -9157,7 +8997,7 @@ async function copyWithRsync(source, target) {
9157
8997
  } else if (stat.isDirectory()) {}
9158
8998
  }
9159
8999
  fs2.mkdirSync(path2.dirname(target), { recursive: true });
9160
- const result = await $2`rsync -a --delete ${source}/ ${target}/`.quiet();
9000
+ const result = await $`rsync -a --delete ${source}/ ${target}/`.quiet();
9161
9001
  if (result.exitCode !== 0) {
9162
9002
  throw new Error(`rsync failed: ${result.stderr.toString()}`);
9163
9003
  }
@@ -9384,7 +9224,7 @@ function hasLocalConflict(skillName) {
9384
9224
  }
9385
9225
  async function findGitRoot(startPath) {
9386
9226
  try {
9387
- const result = await $3`git -C ${startPath} rev-parse --show-toplevel`.quiet();
9227
+ const result = await $2`git -C ${startPath} rev-parse --show-toplevel`.quiet();
9388
9228
  if (result.exitCode === 0) {
9389
9229
  return result.stdout.toString().trim();
9390
9230
  }
@@ -9432,9 +9272,9 @@ ${gitignoreEntry}
9432
9272
  // src/plugin-install.ts
9433
9273
  import * as fs4 from "fs";
9434
9274
  import * as path4 from "path";
9435
- import { homedir as homedir3 } from "os";
9436
- var PLUGIN_DIR_PLURAL = path4.join(homedir3(), ".config", "opencode", "plugins");
9437
- var PLUGIN_DIR_SINGULAR = path4.join(homedir3(), ".config", "opencode", "plugin");
9275
+ import { homedir as homedir2 } from "os";
9276
+ var PLUGIN_DIR_PLURAL = path4.join(homedir2(), ".config", "opencode", "plugins");
9277
+ var PLUGIN_DIR_SINGULAR = path4.join(homedir2(), ".config", "opencode", "plugin");
9438
9278
  var REMOTE_PREFIX = "_remote_";
9439
9279
  function removePathIfExists(targetPath) {
9440
9280
  try {
@@ -9607,8 +9447,8 @@ function findBrokenUnmanagedPluginLinks(pluginDir) {
9607
9447
  // src/mcp-install.ts
9608
9448
  import * as fs5 from "fs";
9609
9449
  import * as path5 from "path";
9610
- import { homedir as homedir4 } from "os";
9611
- var DEFAULT_MCP_DIR = path5.join(homedir4(), ".config", "opencode", "mcp");
9450
+ import { homedir as homedir3 } from "os";
9451
+ var DEFAULT_MCP_DIR = path5.join(homedir3(), ".config", "opencode", "mcp");
9612
9452
  function getMcpPluginsSubdirForRepo(repoShortName) {
9613
9453
  return path5.join("_plugins", repoShortName);
9614
9454
  }
@@ -9808,131 +9648,12 @@ function createDisableMatcher(disable) {
9808
9648
  };
9809
9649
  }
9810
9650
 
9811
- // src/lock.ts
9812
- import * as fs6 from "fs";
9813
- import * as path6 from "path";
9814
- import * as os from "os";
9815
- var DEFAULT_TIMEOUT_MS = 30000;
9816
- var POLL_INTERVAL_MS = 500;
9817
- var STALE_THRESHOLD_MS = 5 * 60 * 1000;
9818
- function getLockPath() {
9819
- return path6.join(getRepoPath(TEAM_REPO_URL), ".sync.lock");
9820
- }
9821
- function isProcessRunning(pid) {
9822
- try {
9823
- process.kill(pid, 0);
9824
- return true;
9825
- } catch (err) {
9826
- if (err.code === "EPERM") {
9827
- return true;
9828
- }
9829
- return false;
9830
- }
9831
- }
9832
- function isLockStale(lockData) {
9833
- if (lockData.hostname === os.hostname()) {
9834
- if (!isProcessRunning(lockData.pid)) {
9835
- logDebug(`Lock held by dead process ${lockData.pid}, considering stale`);
9836
- return true;
9837
- }
9838
- }
9839
- const age = Date.now() - lockData.timestamp;
9840
- if (age > STALE_THRESHOLD_MS) {
9841
- if (lockData.hostname !== os.hostname()) {
9842
- logDebug(`Lock from remote host ${lockData.hostname} is ${Math.round(age / 1000)}s old, considering stale`);
9843
- return true;
9844
- }
9845
- }
9846
- return false;
9847
- }
9848
- function readLockData() {
9849
- const lockPath = getLockPath();
9850
- try {
9851
- const content = fs6.readFileSync(lockPath, "utf8");
9852
- const data = JSON.parse(content);
9853
- if (typeof data.pid !== "number" || typeof data.hostname !== "string" || typeof data.timestamp !== "number") {
9854
- logDebug("Invalid lockfile structure, ignoring");
9855
- return;
9856
- }
9857
- return data;
9858
- } catch {
9859
- return;
9860
- }
9861
- }
9862
- function removeLockFile() {
9863
- const lockPath = getLockPath();
9864
- try {
9865
- fs6.unlinkSync(lockPath);
9866
- } catch {}
9867
- }
9868
- function tryAcquireLock() {
9869
- const lockPath = getLockPath();
9870
- fs6.mkdirSync(path6.dirname(lockPath), { recursive: true });
9871
- const existingLock = readLockData();
9872
- if (existingLock) {
9873
- if (isLockStale(existingLock)) {
9874
- logDebug("Removing stale lock");
9875
- removeLockFile();
9876
- } else {
9877
- return false;
9878
- }
9879
- }
9880
- try {
9881
- const data = {
9882
- pid: process.pid,
9883
- hostname: os.hostname(),
9884
- timestamp: Date.now()
9885
- };
9886
- fs6.writeFileSync(lockPath, JSON.stringify(data, null, 2), { flag: "wx" });
9887
- return true;
9888
- } catch (err) {
9889
- if (err.code === "EEXIST") {
9890
- return false;
9891
- }
9892
- throw err;
9893
- }
9894
- }
9895
- async function acquireLock(timeoutMs = DEFAULT_TIMEOUT_MS) {
9896
- const startTime = Date.now();
9897
- while (true) {
9898
- if (tryAcquireLock()) {
9899
- logDebug("Acquired sync lock");
9900
- return { acquired: true };
9901
- }
9902
- const elapsed = Date.now() - startTime;
9903
- if (elapsed >= timeoutMs) {
9904
- const lockData = readLockData();
9905
- const holder = lockData ? `pid ${lockData.pid} on ${lockData.hostname}` : "unknown process";
9906
- return {
9907
- acquired: false,
9908
- error: `Failed to acquire sync lock after ${Math.round(elapsed / 1000)}s (held by ${holder})`
9909
- };
9910
- }
9911
- await new Promise((resolve4) => setTimeout(resolve4, POLL_INTERVAL_MS));
9912
- }
9913
- }
9914
- function releaseLock() {
9915
- const lockData = readLockData();
9916
- if (lockData && lockData.pid === process.pid && lockData.hostname === os.hostname()) {
9917
- removeLockFile();
9918
- logDebug("Released sync lock");
9919
- }
9920
- }
9921
- async function withLock(fn, timeoutMs = DEFAULT_TIMEOUT_MS) {
9922
- const lockResult = await acquireLock(timeoutMs);
9923
- if (!lockResult.acquired) {
9924
- throw new Error(lockResult.error);
9925
- }
9926
- try {
9927
- return await fn();
9928
- } finally {
9929
- releaseLock();
9930
- }
9931
- }
9651
+ // src/index.ts
9652
+ import * as path8 from "path";
9932
9653
 
9933
9654
  // src/mcp-schema.ts
9934
- import * as fs7 from "fs";
9935
- import * as path7 from "path";
9655
+ import * as fs6 from "fs";
9656
+ import * as path6 from "path";
9936
9657
  function isNonEmptyString(value) {
9937
9658
  return typeof value === "string" && value.trim().length > 0;
9938
9659
  }
@@ -10080,7 +9801,7 @@ function validateMcpConfig(obj) {
10080
9801
  }
10081
9802
  function validateMcpFile(mcpJsonPath) {
10082
9803
  try {
10083
- const content = fs7.readFileSync(mcpJsonPath, "utf-8");
9804
+ const content = fs6.readFileSync(mcpJsonPath, "utf-8");
10084
9805
  const parsed = JSON.parse(content);
10085
9806
  return validateMcpConfig(parsed);
10086
9807
  } catch (err) {
@@ -10099,7 +9820,7 @@ function validateMcpFile(mcpJsonPath) {
10099
9820
  }
10100
9821
  }
10101
9822
  function validateMcpInfo(mcp) {
10102
- const mcpJsonPath = mcp.path.endsWith(".json") ? mcp.path : path7.join(mcp.path, "mcp.json");
9823
+ const mcpJsonPath = mcp.path.endsWith(".json") ? mcp.path : path6.join(mcp.path, "mcp.json");
10103
9824
  const result = validateMcpFile(mcpJsonPath);
10104
9825
  for (const warning of result.warnings) {
10105
9826
  logWarn(`MCP '${mcp.name}': ${warning}`);
@@ -10117,32 +9838,22 @@ function filterValidMcps(mcps) {
10117
9838
  }
10118
9839
 
10119
9840
  // src/status.ts
10120
- import * as fs8 from "fs";
10121
- import * as path8 from "path";
10122
- var STATUS_BASE = path8.join(process.env.HOME || "~", ".cache", "opencode", "wpromote-config");
9841
+ import * as fs7 from "fs";
9842
+ import * as path7 from "path";
9843
+ var STATUS_BASE = path7.join(process.env.HOME || "~", ".cache", "opencode", "wpromote-config");
10123
9844
  function getStatusFilePath() {
10124
- return path8.join(STATUS_BASE, `${REPO_SHORT_NAME}.sync-status.json`);
10125
- }
10126
- function readSyncStatus() {
10127
- const statusPath = getStatusFilePath();
10128
- try {
10129
- const content = fs8.readFileSync(statusPath, "utf-8");
10130
- return JSON.parse(content);
10131
- } catch {
10132
- return null;
10133
- }
9845
+ return path7.join(STATUS_BASE, `${REPO_SHORT_NAME}.sync-status.json`);
10134
9846
  }
10135
9847
  function writeSyncStatus(status) {
10136
9848
  const statusPath = getStatusFilePath();
10137
- const dir = path8.dirname(statusPath);
10138
- fs8.mkdirSync(dir, { recursive: true });
10139
- fs8.writeFileSync(statusPath, JSON.stringify(status, null, 2));
9849
+ const dir = path7.dirname(statusPath);
9850
+ fs7.mkdirSync(dir, { recursive: true });
9851
+ fs7.writeFileSync(statusPath, JSON.stringify(status, null, 2));
10140
9852
  logDebug(`Wrote sync status to ${statusPath}`);
10141
9853
  }
10142
9854
  function updateSyncStatus(data) {
10143
9855
  const status = {
10144
9856
  lastSyncTime: new Date().toISOString(),
10145
- currentRef: data.currentRef,
10146
9857
  installedSkills: data.installedSkills,
10147
9858
  installedPlugins: data.installedPlugins,
10148
9859
  installedMcps: data.installedMcps,
@@ -10156,43 +9867,16 @@ function updateSyncStatus(data) {
10156
9867
  };
10157
9868
  writeSyncStatus(status);
10158
9869
  }
10159
- function recordSyncError(error) {
10160
- const existing = readSyncStatus();
10161
- const status = existing ?? {
10162
- lastSyncTime: null,
10163
- currentRef: null,
10164
- installedSkills: 0,
10165
- installedPlugins: 0,
10166
- installedMcps: 0,
10167
- remoteAgents: 0,
10168
- remoteCommands: 0,
10169
- remoteInstructions: 0,
10170
- skippedConflicts: 0,
10171
- disabledAssets: 0,
10172
- lastError: null,
10173
- pluginsChanged: false
10174
- };
10175
- status.lastError = error;
10176
- writeSyncStatus(status);
10177
- }
10178
9870
 
10179
9871
  // src/index.ts
9872
+ var PACKAGE_ROOT = path8.resolve(import.meta.dir, "..");
10180
9873
  var initialized = false;
10181
9874
  var REQUIRED_RUNTIME_PLUGINS = ["@tarquinen/opencode-dcp@latest"];
10182
9875
  var DEFAULT_OPENCODE_AGENTS_TO_DISABLE = ["build", "plan"];
10183
9876
  async function performSync(config) {
10184
9877
  const disableMatcher = createDisableMatcher(config.disable);
10185
9878
  log(config.dryRun ? "[DRY-RUN] Analyzing team configuration..." : "Syncing team configuration...");
10186
- const result = await syncTeamRepository(config.ref);
10187
- const degradedSyncMode = Boolean(result.error);
10188
- if (degradedSyncMode) {
10189
- const syncError = result.error ?? "Unknown sync error";
10190
- logError(`\u2717 Failed to sync team repository: ${syncError}`);
10191
- if (!config.dryRun) {
10192
- recordSyncError(syncError);
10193
- }
10194
- log("Continuing with cached assets in degraded mode");
10195
- }
9879
+ const result = await discoverAssets(PACKAGE_ROOT);
10196
9880
  if (!config.dryRun) {
10197
9881
  const brokenUnmanagedLinks = findBrokenUnmanagedPluginLinks();
10198
9882
  if (brokenUnmanagedLinks.length > 0) {
@@ -10366,20 +10050,6 @@ async function performSync(config) {
10366
10050
  dryRun: dryRunReport
10367
10051
  };
10368
10052
  }
10369
- if (degradedSyncMode) {
10370
- return {
10371
- result,
10372
- skippedConflicts,
10373
- totalSkills: 0,
10374
- remoteAgents,
10375
- remoteCommands,
10376
- remoteMcps,
10377
- remoteInstructions,
10378
- pluginsChanged: false,
10379
- totalPlugins: 0,
10380
- totalMcps: 0
10381
- };
10382
- }
10383
10053
  const filteredResult = { ...result, skills: skillsToInstall };
10384
10054
  const installResults = await createInstallsForRepo(filteredResult, config.installMethod);
10385
10055
  for (const sr of installResults) {
@@ -10390,7 +10060,7 @@ async function performSync(config) {
10390
10060
  logError(`\u2717 Failed to install skill ${sr.skillName}: ${sr.error}`);
10391
10061
  }
10392
10062
  }
10393
- log(`\u2713 ${result.shortName} (${result.ref}) - ${totalSkills} skills`);
10063
+ log(`\u2713 ${result.shortName} - ${totalSkills} skills`);
10394
10064
  const cleanup = cleanupStaleInstalls(currentSkills);
10395
10065
  if (cleanup.removed.length > 0) {
10396
10066
  log(`Cleaned up ${cleanup.removed.length} stale skill installs`);
@@ -10450,7 +10120,6 @@ async function performSync(config) {
10450
10120
  }
10451
10121
  const totalMcps = newMcpInstalls.size;
10452
10122
  updateSyncStatus({
10453
- currentRef: result.ref,
10454
10123
  installedSkills: totalSkills,
10455
10124
  installedPlugins: totalPlugins,
10456
10125
  installedMcps: totalMcps,
@@ -10536,7 +10205,7 @@ var WpromoteConfigPlugin = async (ctx) => {
10536
10205
  pluginsChanged,
10537
10206
  totalPlugins,
10538
10207
  totalMcps
10539
- } = await withLock(async () => performSync(userConfig));
10208
+ } = await performSync(userConfig);
10540
10209
  if (pluginsChanged) {
10541
10210
  log("\u26A0 Plugin changes detected. Restart OpenCode to apply.");
10542
10211
  }
@@ -0,0 +1,24 @@
1
+ # Getting Started with Wpromote OpenCode
2
+
3
+ Welcome to the Wpromote OpenCode configuration.
4
+
5
+ ## Quick Start
6
+
7
+ 1. Install the plugin
8
+ 2. Restart OpenCode
9
+ 3. All team configs will be synced automatically
10
+
11
+ ## Configuration
12
+
13
+ Create `~/.config/opencode/wpromote.json` (or `~/.config/opencode/wpromote.jsonc`) to customize:
14
+
15
+ ```json
16
+ {
17
+ "disable": ["skill:example"],
18
+ "installMethod": "link"
19
+ }
20
+ ```
21
+
22
+ ## Overriding Team Configs
23
+
24
+ To override a team skill/agent/command, create a local file with the same name in your personal config directory.