@wpro-eng/opencode-config 1.2.0 → 1.4.0
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/command/wpromote-cleanup.md +72 -0
- package/dist/index.js +195 -3
- package/package.json +1 -1
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Remove stale cache files, broken symlinks, and unused plugins from OpenCode's plugin cache
|
|
3
|
+
---
|
|
4
|
+
Clean up stale artifacts left behind by @wpro-eng/opencode-config and OpenCode's plugin cache.
|
|
5
|
+
|
|
6
|
+
OpenCode's plugin cache (`~/.cache/opencode/package.json`) is append-only: it never removes plugins you've uninstalled from `opencode.json`. This command fixes that, along with other accumulated cruft.
|
|
7
|
+
|
|
8
|
+
## Instructions
|
|
9
|
+
|
|
10
|
+
Run each cleanup step below in order. Report what was found and what was removed. Use the Bash tool for filesystem operations.
|
|
11
|
+
|
|
12
|
+
### 1. Remove old git-clone cache (legacy architecture)
|
|
13
|
+
|
|
14
|
+
The plugin used to clone the team config repo via git. That was replaced by bundled npm assets. The entire `repos/` directory is inert.
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
rm -rf ~/.cache/opencode/wpromote-config/repos/
|
|
18
|
+
rm -f ~/.cache/opencode/wpromote-config/opencode-config.sync-status.json
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Report the size freed if the directory existed.
|
|
22
|
+
|
|
23
|
+
### 2. Prune unused plugins from OpenCode's npm cache
|
|
24
|
+
|
|
25
|
+
Read `~/.config/opencode/opencode.json` to get the active `plugin` list. Then read `~/.cache/opencode/package.json` to get cached dependencies.
|
|
26
|
+
|
|
27
|
+
Any dependency in the cache that is NOT in the active plugin list AND is NOT `opencode-anthropic-auth` (OpenCode's built-in default plugin) should be removed:
|
|
28
|
+
- Delete the entry from `~/.cache/opencode/package.json`
|
|
29
|
+
- Delete the package directory from `~/.cache/opencode/node_modules/<pkg>/`
|
|
30
|
+
- Delete `~/.cache/opencode/bun.lock` so Bun re-resolves cleanly on next startup
|
|
31
|
+
|
|
32
|
+
List each removed package by name and version.
|
|
33
|
+
|
|
34
|
+
**Important**: Also check for project-level `.opencode/opencode.json` plugin entries (in the current working directory) so you don't accidentally prune a plugin that's declared at the project level rather than the user level.
|
|
35
|
+
|
|
36
|
+
### 3. Fix broken symlinks
|
|
37
|
+
|
|
38
|
+
Check these locations for symlinks whose targets no longer exist:
|
|
39
|
+
- `~/.config/opencode/skill/_plugins/opencode-config/*/`
|
|
40
|
+
- `~/.config/opencode/plugin/_remote_opencode-config_*`
|
|
41
|
+
- `~/.config/opencode/plugins/_remote_opencode-config_*`
|
|
42
|
+
- `~/.config/opencode/mcp/_plugins/opencode-config/*/`
|
|
43
|
+
|
|
44
|
+
Remove any broken symlinks. Report each one removed.
|
|
45
|
+
|
|
46
|
+
### 4. Clean stale orchestration task files
|
|
47
|
+
|
|
48
|
+
Check `~/.cache/opencode/wpromote-config/orchestration-tasks/` for session files older than 7 days:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
find ~/.cache/opencode/wpromote-config/orchestration-tasks/ -name "ses_*.json" -mtime +7
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Remove them and report the count.
|
|
55
|
+
|
|
56
|
+
### 5. Rotate plugin log
|
|
57
|
+
|
|
58
|
+
If `~/.cache/opencode/wpromote-config/plugin.log` is larger than 1MB, truncate it to the last 200 lines.
|
|
59
|
+
|
|
60
|
+
## Output Format
|
|
61
|
+
|
|
62
|
+
Present a summary table:
|
|
63
|
+
|
|
64
|
+
| Category | Found | Cleaned | Details |
|
|
65
|
+
|----------|-------|---------|---------|
|
|
66
|
+
| Git clone cache | ... | ... | ... |
|
|
67
|
+
| Unused cached plugins | ... | ... | ... |
|
|
68
|
+
| Broken symlinks | ... | ... | ... |
|
|
69
|
+
| Stale task files | ... | ... | ... |
|
|
70
|
+
| Plugin log | ... | ... | ... |
|
|
71
|
+
|
|
72
|
+
End with either "All clean, no restart needed." or "Restart OpenCode to pick up changes." if any cached plugins were removed or symlinks were fixed.
|
package/dist/index.js
CHANGED
|
@@ -9649,7 +9649,7 @@ function createDisableMatcher(disable) {
|
|
|
9649
9649
|
}
|
|
9650
9650
|
|
|
9651
9651
|
// src/index.ts
|
|
9652
|
-
import * as
|
|
9652
|
+
import * as path9 from "path";
|
|
9653
9653
|
|
|
9654
9654
|
// src/mcp-schema.ts
|
|
9655
9655
|
import * as fs6 from "fs";
|
|
@@ -9880,8 +9880,191 @@ function updatePluginStatus(data) {
|
|
|
9880
9880
|
writePluginStatus(status);
|
|
9881
9881
|
}
|
|
9882
9882
|
|
|
9883
|
+
// src/auto-update.ts
|
|
9884
|
+
import * as fs8 from "fs";
|
|
9885
|
+
import * as path8 from "path";
|
|
9886
|
+
var PACKAGE_NAME = "@wpro-eng/opencode-config";
|
|
9887
|
+
var NPM_DIST_TAGS_URL = `https://registry.npmjs.org/-/package/${encodeURIComponent(PACKAGE_NAME)}/dist-tags`;
|
|
9888
|
+
var REGISTRY_FETCH_TIMEOUT = 5000;
|
|
9889
|
+
var BUN_INSTALL_TIMEOUT = 60000;
|
|
9890
|
+
function getOpenCodeCacheDir() {
|
|
9891
|
+
const xdgCache = process.env.XDG_CACHE_HOME || path8.join(process.env.HOME || "~", ".cache");
|
|
9892
|
+
return path8.join(xdgCache, "opencode");
|
|
9893
|
+
}
|
|
9894
|
+
function getOpenCodeConfigDir() {
|
|
9895
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME || path8.join(process.env.HOME || "~", ".config");
|
|
9896
|
+
return path8.join(xdgConfig, "opencode");
|
|
9897
|
+
}
|
|
9898
|
+
function getCachedVersion() {
|
|
9899
|
+
const pkgJsonPath = path8.join(getOpenCodeCacheDir(), "node_modules", PACKAGE_NAME, "package.json");
|
|
9900
|
+
try {
|
|
9901
|
+
if (!fs8.existsSync(pkgJsonPath))
|
|
9902
|
+
return null;
|
|
9903
|
+
const content = fs8.readFileSync(pkgJsonPath, "utf-8");
|
|
9904
|
+
const pkg = JSON.parse(content);
|
|
9905
|
+
return pkg.version ?? null;
|
|
9906
|
+
} catch {
|
|
9907
|
+
return null;
|
|
9908
|
+
}
|
|
9909
|
+
}
|
|
9910
|
+
async function getLatestVersion() {
|
|
9911
|
+
const controller = new AbortController;
|
|
9912
|
+
const timeoutId = setTimeout(() => controller.abort(), REGISTRY_FETCH_TIMEOUT);
|
|
9913
|
+
try {
|
|
9914
|
+
const response = await fetch(NPM_DIST_TAGS_URL, {
|
|
9915
|
+
signal: controller.signal,
|
|
9916
|
+
headers: { Accept: "application/json" }
|
|
9917
|
+
});
|
|
9918
|
+
if (!response.ok)
|
|
9919
|
+
return null;
|
|
9920
|
+
const data = await response.json();
|
|
9921
|
+
return data.latest ?? null;
|
|
9922
|
+
} catch {
|
|
9923
|
+
return null;
|
|
9924
|
+
} finally {
|
|
9925
|
+
clearTimeout(timeoutId);
|
|
9926
|
+
}
|
|
9927
|
+
}
|
|
9928
|
+
function stripTrailingCommas(json) {
|
|
9929
|
+
return json.replace(/,(\s*[}\]])/g, "$1");
|
|
9930
|
+
}
|
|
9931
|
+
function removeFromBunLock(cacheDir) {
|
|
9932
|
+
const textLockPath = path8.join(cacheDir, "bun.lock");
|
|
9933
|
+
const binaryLockPath = path8.join(cacheDir, "bun.lockb");
|
|
9934
|
+
if (fs8.existsSync(textLockPath)) {
|
|
9935
|
+
try {
|
|
9936
|
+
const content = fs8.readFileSync(textLockPath, "utf-8");
|
|
9937
|
+
const lock = JSON.parse(stripTrailingCommas(content));
|
|
9938
|
+
if (lock.packages?.[PACKAGE_NAME]) {
|
|
9939
|
+
delete lock.packages[PACKAGE_NAME];
|
|
9940
|
+
fs8.writeFileSync(textLockPath, JSON.stringify(lock, null, 2));
|
|
9941
|
+
logDebug(`Removed ${PACKAGE_NAME} from bun.lock`);
|
|
9942
|
+
return true;
|
|
9943
|
+
}
|
|
9944
|
+
return false;
|
|
9945
|
+
} catch {
|
|
9946
|
+
try {
|
|
9947
|
+
fs8.unlinkSync(textLockPath);
|
|
9948
|
+
logDebug("Deleted unparseable bun.lock to force re-resolution");
|
|
9949
|
+
return true;
|
|
9950
|
+
} catch {
|
|
9951
|
+
return false;
|
|
9952
|
+
}
|
|
9953
|
+
}
|
|
9954
|
+
}
|
|
9955
|
+
if (fs8.existsSync(binaryLockPath)) {
|
|
9956
|
+
try {
|
|
9957
|
+
fs8.unlinkSync(binaryLockPath);
|
|
9958
|
+
logDebug("Deleted bun.lockb to force re-resolution");
|
|
9959
|
+
return true;
|
|
9960
|
+
} catch {
|
|
9961
|
+
return false;
|
|
9962
|
+
}
|
|
9963
|
+
}
|
|
9964
|
+
return false;
|
|
9965
|
+
}
|
|
9966
|
+
function invalidatePackage() {
|
|
9967
|
+
const cacheDir = getOpenCodeCacheDir();
|
|
9968
|
+
const configDir = getOpenCodeConfigDir();
|
|
9969
|
+
const pkgDirs = [
|
|
9970
|
+
path8.join(cacheDir, "node_modules", PACKAGE_NAME),
|
|
9971
|
+
path8.join(configDir, "node_modules", PACKAGE_NAME)
|
|
9972
|
+
];
|
|
9973
|
+
let packageRemoved = false;
|
|
9974
|
+
let lockRemoved = false;
|
|
9975
|
+
for (const pkgDir of pkgDirs) {
|
|
9976
|
+
if (fs8.existsSync(pkgDir)) {
|
|
9977
|
+
fs8.rmSync(pkgDir, { recursive: true, force: true });
|
|
9978
|
+
logDebug(`Removed cached package: ${pkgDir}`);
|
|
9979
|
+
packageRemoved = true;
|
|
9980
|
+
}
|
|
9981
|
+
}
|
|
9982
|
+
lockRemoved = removeFromBunLock(cacheDir);
|
|
9983
|
+
return packageRemoved || lockRemoved;
|
|
9984
|
+
}
|
|
9985
|
+
async function runBunInstall() {
|
|
9986
|
+
const cacheDir = getOpenCodeCacheDir();
|
|
9987
|
+
const packageJsonPath = path8.join(cacheDir, "package.json");
|
|
9988
|
+
if (!fs8.existsSync(packageJsonPath)) {
|
|
9989
|
+
logDebug("No package.json in OpenCode cache, skipping bun install");
|
|
9990
|
+
return false;
|
|
9991
|
+
}
|
|
9992
|
+
try {
|
|
9993
|
+
const proc = Bun.spawn(["bun", "install"], {
|
|
9994
|
+
cwd: cacheDir,
|
|
9995
|
+
stdout: "ignore",
|
|
9996
|
+
stderr: "ignore"
|
|
9997
|
+
});
|
|
9998
|
+
let timeoutId;
|
|
9999
|
+
const timeoutPromise = new Promise((resolve5) => {
|
|
10000
|
+
timeoutId = setTimeout(() => resolve5("timeout"), BUN_INSTALL_TIMEOUT);
|
|
10001
|
+
});
|
|
10002
|
+
const exitPromise = proc.exited.then(() => "completed");
|
|
10003
|
+
const result = await Promise.race([exitPromise, timeoutPromise]);
|
|
10004
|
+
clearTimeout(timeoutId);
|
|
10005
|
+
if (result === "timeout") {
|
|
10006
|
+
try {
|
|
10007
|
+
proc.kill();
|
|
10008
|
+
} catch {}
|
|
10009
|
+
logError("bun install timed out after 60s");
|
|
10010
|
+
return false;
|
|
10011
|
+
}
|
|
10012
|
+
if (proc.exitCode !== 0) {
|
|
10013
|
+
logError(`bun install failed with exit code ${proc.exitCode}`);
|
|
10014
|
+
return false;
|
|
10015
|
+
}
|
|
10016
|
+
return true;
|
|
10017
|
+
} catch (err) {
|
|
10018
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
10019
|
+
logError(`bun install failed: ${message}`);
|
|
10020
|
+
return false;
|
|
10021
|
+
}
|
|
10022
|
+
}
|
|
10023
|
+
async function checkAndUpdate() {
|
|
10024
|
+
const cachedVersion = getCachedVersion();
|
|
10025
|
+
const latestVersion = await getLatestVersion();
|
|
10026
|
+
if (!latestVersion) {
|
|
10027
|
+
logDebug("Could not fetch latest version from npm registry, skipping update check");
|
|
10028
|
+
return {
|
|
10029
|
+
updateAvailable: false,
|
|
10030
|
+
previousVersion: cachedVersion,
|
|
10031
|
+
latestVersion: null,
|
|
10032
|
+
installed: false
|
|
10033
|
+
};
|
|
10034
|
+
}
|
|
10035
|
+
if (cachedVersion === latestVersion) {
|
|
10036
|
+
logDebug(`Already on latest version: ${cachedVersion}`);
|
|
10037
|
+
return {
|
|
10038
|
+
updateAvailable: false,
|
|
10039
|
+
previousVersion: cachedVersion,
|
|
10040
|
+
latestVersion,
|
|
10041
|
+
installed: false
|
|
10042
|
+
};
|
|
10043
|
+
}
|
|
10044
|
+
log(`Update available: ${cachedVersion ?? "unknown"} \u2192 ${latestVersion}`);
|
|
10045
|
+
invalidatePackage();
|
|
10046
|
+
const installSuccess = await runBunInstall();
|
|
10047
|
+
if (installSuccess) {
|
|
10048
|
+
log(`Updated to ${latestVersion}. Restart OpenCode to apply.`);
|
|
10049
|
+
return {
|
|
10050
|
+
updateAvailable: true,
|
|
10051
|
+
previousVersion: cachedVersion,
|
|
10052
|
+
latestVersion,
|
|
10053
|
+
installed: true
|
|
10054
|
+
};
|
|
10055
|
+
}
|
|
10056
|
+
logError(`Failed to install update to ${latestVersion}`);
|
|
10057
|
+
return {
|
|
10058
|
+
updateAvailable: true,
|
|
10059
|
+
previousVersion: cachedVersion,
|
|
10060
|
+
latestVersion,
|
|
10061
|
+
installed: false,
|
|
10062
|
+
error: "bun install failed"
|
|
10063
|
+
};
|
|
10064
|
+
}
|
|
10065
|
+
|
|
9883
10066
|
// src/index.ts
|
|
9884
|
-
var PACKAGE_ROOT =
|
|
10067
|
+
var PACKAGE_ROOT = path9.resolve(import.meta.dir, "..");
|
|
9885
10068
|
var initialized = false;
|
|
9886
10069
|
var REQUIRED_RUNTIME_PLUGINS = ["@tarquinen/opencode-dcp@latest"];
|
|
9887
10070
|
var DEFAULT_OPENCODE_AGENTS_TO_DISABLE = ["build", "plan"];
|
|
@@ -10358,7 +10541,16 @@ var WpromoteConfigPlugin = async (ctx) => {
|
|
|
10358
10541
|
}
|
|
10359
10542
|
}
|
|
10360
10543
|
},
|
|
10361
|
-
event: async ({ event }) => {
|
|
10544
|
+
event: async ({ event }) => {
|
|
10545
|
+
if (event.type !== "session.created")
|
|
10546
|
+
return;
|
|
10547
|
+
const props = event.properties;
|
|
10548
|
+
if (props?.info?.parentID)
|
|
10549
|
+
return;
|
|
10550
|
+
checkAndUpdate().catch((err) => {
|
|
10551
|
+
logError(`Auto-update check failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
10552
|
+
});
|
|
10553
|
+
}
|
|
10362
10554
|
};
|
|
10363
10555
|
};
|
|
10364
10556
|
var RemoteSkillsPlugin = WpromoteConfigPlugin;
|