opencode-rules-md 0.8.3 → 0.8.5
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/cli.mjs +915 -0
- package/dist/src/cli/config.d.ts +93 -0
- package/dist/src/cli/config.d.ts.map +1 -0
- package/dist/src/cli/config.js +382 -0
- package/dist/src/cli/config.js.map +1 -0
- package/dist/src/cli/install.d.ts +35 -0
- package/dist/src/cli/install.d.ts.map +1 -0
- package/dist/src/cli/install.js +148 -0
- package/dist/src/cli/install.js.map +1 -0
- package/dist/src/cli/main.d.ts +14 -0
- package/dist/src/cli/main.d.ts.map +1 -0
- package/dist/src/cli/main.js +244 -0
- package/dist/src/cli/main.js.map +1 -0
- package/dist/src/cli/real-fs.d.ts +3 -0
- package/dist/src/cli/real-fs.d.ts.map +1 -0
- package/dist/src/cli/real-fs.js +32 -0
- package/dist/src/cli/real-fs.js.map +1 -0
- package/dist/src/cli/registry.d.ts +12 -0
- package/dist/src/cli/registry.d.ts.map +1 -0
- package/dist/src/cli/registry.js +34 -0
- package/dist/src/cli/registry.js.map +1 -0
- package/dist/src/cli/status.d.ts +44 -0
- package/dist/src/cli/status.d.ts.map +1 -0
- package/dist/src/cli/status.js +182 -0
- package/dist/src/cli/status.js.map +1 -0
- package/dist/src/cli/uninstall.d.ts +27 -0
- package/dist/src/cli/uninstall.d.ts.map +1 -0
- package/dist/src/cli/uninstall.js +101 -0
- package/dist/src/cli/uninstall.js.map +1 -0
- package/dist/src/cli/update.d.ts +31 -0
- package/dist/src/cli/update.d.ts.map +1 -0
- package/dist/src/cli/update.js +173 -0
- package/dist/src/cli/update.js.map +1 -0
- package/package.json +11 -15
- package/src/cli/config.ts +461 -0
- package/src/cli/install.ts +211 -0
- package/src/cli/main.ts +299 -0
- package/src/cli/real-fs.ts +44 -0
- package/src/cli/registry.ts +36 -0
- package/src/cli/status.ts +247 -0
- package/src/cli/uninstall.ts +146 -0
- package/src/cli/update.ts +220 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// src/cli/status.ts — `omd status` and `omd doctor` command implementations.
|
|
3
|
+
//
|
|
4
|
+
// runStatus: read-only probe for both configs. Reports path, format,
|
|
5
|
+
// installed specifier, other plugins, and installed-vs-latest version.
|
|
6
|
+
// runDoctor: health checks grouped into issues (exit 1), warnings, and info.
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
import { join, dirname } from 'path';
|
|
9
|
+
import { homedir } from 'os';
|
|
10
|
+
import { extname } from 'path';
|
|
11
|
+
import { loadGlobalConfig, matchesPlugin, normalizePlugin, } from './config.js';
|
|
12
|
+
import { fetchLatestVersion } from './registry.js';
|
|
13
|
+
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
14
|
+
const RULE_DIR_NAME = 'opencode-rules-md';
|
|
15
|
+
const MIN_NODE_VERSION = 20;
|
|
16
|
+
const CONFIG_BASENAMES = ['opencode', 'tui'];
|
|
17
|
+
// ─── runStatus ────────────────────────────────────────────────────────────────
|
|
18
|
+
/**
|
|
19
|
+
* Run the status command: read-only probe for both opencode.json and tui.json.
|
|
20
|
+
* Reports path, format, installed specifier, other plugins, and freshness.
|
|
21
|
+
*/
|
|
22
|
+
export const runStatus = async (fs, env, log) => {
|
|
23
|
+
const configs = [];
|
|
24
|
+
const latest = await fetchLatestVersion();
|
|
25
|
+
for (const basename of CONFIG_BASENAMES) {
|
|
26
|
+
const loaded = loadGlobalConfig(fs, env, basename);
|
|
27
|
+
const format = extname(loaded.path); // '.json' or '.jsonc'
|
|
28
|
+
const plugins = normalizePlugin(loaded.data['plugins']);
|
|
29
|
+
const match = plugins.find(p => matchesPlugin(p));
|
|
30
|
+
const entry = {
|
|
31
|
+
basename,
|
|
32
|
+
path: loaded.path,
|
|
33
|
+
format,
|
|
34
|
+
installed: match ?? null,
|
|
35
|
+
notInstalled: !loaded.exists ? true : match === undefined || match === null,
|
|
36
|
+
otherPlugins: plugins.filter(p => !matchesPlugin(p)),
|
|
37
|
+
latest,
|
|
38
|
+
isLatest: match !== undefined && latest !== null ? match === `opencode-rules-md@${latest}` : null,
|
|
39
|
+
};
|
|
40
|
+
configs.push(entry);
|
|
41
|
+
// ── Print status lines ──────────────────────────────────────────────────
|
|
42
|
+
if (!loaded.exists) {
|
|
43
|
+
log(`omd: ${basename}.json — config not found at ${loaded.path}`);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
log(`omd: ${basename}${format} — ${loaded.path}`);
|
|
47
|
+
if (match) {
|
|
48
|
+
const isLatestLabel = entry.isLatest === true
|
|
49
|
+
? ' (latest)'
|
|
50
|
+
: entry.isLatest === false
|
|
51
|
+
? ` (behind: latest is ${latest ?? 'unknown'})`
|
|
52
|
+
: '';
|
|
53
|
+
log(` opencode-rules-md: ${match}${isLatestLabel}`);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
log(` opencode-rules-md: not installed`);
|
|
57
|
+
}
|
|
58
|
+
if (entry.otherPlugins.length > 0) {
|
|
59
|
+
log(` other plugins: ${entry.otherPlugins.join(', ')}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return { configs };
|
|
63
|
+
};
|
|
64
|
+
// ─── runDoctor ────────────────────────────────────────────────────────────────
|
|
65
|
+
/**
|
|
66
|
+
* Run the doctor command: health checks for the plugin environment.
|
|
67
|
+
* - Node >= 20: issue if below
|
|
68
|
+
* - Bun on PATH: issue if absent
|
|
69
|
+
* - Configs readable: issue if absent or malformed
|
|
70
|
+
* - Plugin shape valid: issue if malformed
|
|
71
|
+
* - Rule dir existence: warning if absent
|
|
72
|
+
* - Config dir writable: issue if not writable
|
|
73
|
+
* - Package freshness: info line about update availability
|
|
74
|
+
*
|
|
75
|
+
* Exit 1 if any issues found.
|
|
76
|
+
*/
|
|
77
|
+
export const runDoctor = async (fs, env, log, error, opts = {}) => {
|
|
78
|
+
const issues = [];
|
|
79
|
+
const warnings = [];
|
|
80
|
+
const info = [];
|
|
81
|
+
const nodeVersion = opts.nodeVersion ?? process.version.slice(1); // strip 'v'
|
|
82
|
+
const ruleDirExists = opts.ruleDirExists ?? true;
|
|
83
|
+
// Check if Bun is available: use override if provided, otherwise scan PATH
|
|
84
|
+
let hasBun = opts.hasBun;
|
|
85
|
+
if (hasBun === undefined) {
|
|
86
|
+
const pathEnv = (env.PATH ?? '').split(':');
|
|
87
|
+
hasBun = pathEnv.some(p => {
|
|
88
|
+
try {
|
|
89
|
+
const { existsSync } = require('node:fs');
|
|
90
|
+
return existsSync(p + '/bun');
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
// ── Check: Node version ───────────────────────────────────────────────────
|
|
98
|
+
log('Checking Node version...');
|
|
99
|
+
const nodeMajor = parseInt(nodeVersion.split('.')[0] ?? '0', 10);
|
|
100
|
+
if (nodeMajor < MIN_NODE_VERSION) {
|
|
101
|
+
issues.push(`Node.js ${nodeVersion} is too old; requires Node >= ${MIN_NODE_VERSION}`);
|
|
102
|
+
error(`[ISSUE] Node.js ${nodeVersion} — requires >= ${MIN_NODE_VERSION}. Download latest from https://nodejs.org`);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
log(` Node.js ${nodeVersion} — OK`);
|
|
106
|
+
}
|
|
107
|
+
// ── Check: Bun on PATH ─────────────────────────────────────────────────────
|
|
108
|
+
log('Checking for Bun...');
|
|
109
|
+
if (!hasBun) {
|
|
110
|
+
issues.push('Bun is not on PATH — install from https://bun.sh');
|
|
111
|
+
error('[ISSUE] Bun not found on PATH — install Bun for best performance');
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
log(' Bun — found on PATH');
|
|
115
|
+
}
|
|
116
|
+
// ── Check: both configs readable and valid ─────────────────────────────────
|
|
117
|
+
for (const basename of CONFIG_BASENAMES) {
|
|
118
|
+
log(`Checking ${basename} config...`);
|
|
119
|
+
const loaded = loadGlobalConfig(fs, env, basename);
|
|
120
|
+
if (!loaded.exists) {
|
|
121
|
+
warnings.push(`${basename} config not found at ${loaded.path}`);
|
|
122
|
+
log(` ${basename}: not found (will be created on first install)`);
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
log(` ${basename}${extname(loaded.path)}: ${loaded.path}`);
|
|
126
|
+
const plugins = normalizePlugin(loaded.data['plugins']);
|
|
127
|
+
const match = plugins.find(p => matchesPlugin(p));
|
|
128
|
+
if (match) {
|
|
129
|
+
info.push(`${basename}: opencode-rules-md@${match ?? 'unknown'} installed`);
|
|
130
|
+
log(` opencode-rules-md: ${match}`);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
warnings.push(`${basename}: plugin not installed`);
|
|
134
|
+
log(` opencode-rules-md: not installed`);
|
|
135
|
+
}
|
|
136
|
+
// ── Check plugin shape ────────────────────────────────────────────────────
|
|
137
|
+
if (loaded.data['plugins'] !== undefined && !Array.isArray(loaded.data['plugins']) && typeof loaded.data['plugins'] !== 'object') {
|
|
138
|
+
issues.push(`${basename}: plugins field has invalid type — expected array or object`);
|
|
139
|
+
error(`[ISSUE] ${basename}: invalid plugin shape`);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
log(` plugins field: valid`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// ── Check: rule dir existence (warning, not issue) ─────────────────────────
|
|
146
|
+
log('Checking rule directory...');
|
|
147
|
+
const ruleBase = join(homedir(), '.local', 'share', RULE_DIR_NAME);
|
|
148
|
+
if (!ruleDirExists) {
|
|
149
|
+
warnings.push(`Rule directory not found at ${ruleBase}`);
|
|
150
|
+
log(` ${ruleBase}: not found (plugin rules not installed — this is optional)`);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
log(` ${ruleBase}: exists`);
|
|
154
|
+
}
|
|
155
|
+
// ── Check: config dir writable ─────────────────────────────────────────────
|
|
156
|
+
const configDir = env.OPENCODE_CONFIG_DIR ?? join(homedir(), '.config', 'opencode');
|
|
157
|
+
log('Checking config directory write access...');
|
|
158
|
+
const parentDir = dirname(configDir);
|
|
159
|
+
if (!fs.existsSync(parentDir)) {
|
|
160
|
+
issues.push(`Config parent directory does not exist: ${parentDir}`);
|
|
161
|
+
error(`[ISSUE] Config dir parent missing: ${parentDir}`);
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
log(` ${parentDir}: writable`);
|
|
165
|
+
}
|
|
166
|
+
// ── Summary ────────────────────────────────────────────────────────────────
|
|
167
|
+
log('');
|
|
168
|
+
if (issues.length === 0) {
|
|
169
|
+
log('omd doctor: all checks passed ✓');
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
error(`omd doctor: ${issues.length} issue(s) found — fix before using the plugin`);
|
|
173
|
+
error(`Run 'omd --help' for usage information`);
|
|
174
|
+
}
|
|
175
|
+
return {
|
|
176
|
+
ok: issues.length === 0,
|
|
177
|
+
issues,
|
|
178
|
+
warnings,
|
|
179
|
+
info,
|
|
180
|
+
};
|
|
181
|
+
};
|
|
182
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/cli/status.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,6EAA6E;AAC7E,EAAE;AACF,qEAAqE;AACrE,uEAAuE;AACvE,6EAA6E;AAC7E,8EAA8E;AAE9E,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAC/B,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,eAAe,GAEhB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAkCnD,gFAAgF;AAEhF,MAAM,aAAa,GAAG,mBAAmB,CAAC;AAC1C,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAE5B,MAAM,gBAAgB,GAAG,CAAC,UAAU,EAAE,KAAK,CAAU,CAAC;AAEtD,iFAAiF;AAEjF;;;GAGG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,EAC5B,EAAS,EACT,GAAsB,EACtB,GAAwB,EACD,EAAE;IACzB,MAAM,OAAO,GAAkB,EAAE,CAAC;IAClC,MAAM,MAAM,GAAG,MAAM,kBAAkB,EAAE,CAAC;IAE1C,KAAK,MAAM,QAAQ,IAAI,gBAAgB,EAAE,CAAC;QACxC,MAAM,MAAM,GAAG,gBAAgB,CAAC,EAAE,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;QACnD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAW,CAAC,CAAC,sBAAsB;QACrE,MAAM,OAAO,GAAG,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;QACxD,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;QAElD,MAAM,KAAK,GAAgB;YACzB,QAAQ;YACR,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,MAAM;YACN,SAAS,EAAE,KAAK,IAAI,IAAI;YACxB,YAAY,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;YAC3E,YAAY,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;YACpD,MAAM;YACN,QAAQ,EAAE,KAAK,KAAK,SAAS,IAAI,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,KAAK,qBAAqB,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI;SAClG,CAAC;QAEF,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAEpB,2EAA2E;QAC3E,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACnB,GAAG,CAAC,QAAQ,QAAQ,+BAA+B,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;YAClE,SAAS;QACX,CAAC;QAED,GAAG,CAAC,QAAQ,QAAQ,GAAG,MAAM,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QAClD,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,aAAa,GAAG,KAAK,CAAC,QAAQ,KAAK,IAAI;gBAC3C,CAAC,CAAC,WAAW;gBACb,CAAC,CAAC,KAAK,CAAC,QAAQ,KAAK,KAAK;oBACxB,CAAC,CAAC,uBAAuB,MAAM,IAAI,SAAS,GAAG;oBAC/C,CAAC,CAAC,EAAE,CAAC;YACT,GAAG,CAAC,wBAAwB,KAAK,GAAG,aAAa,EAAE,CAAC,CAAC;QACvD,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,oCAAoC,CAAC,CAAC;QAC5C,CAAC;QAED,IAAI,KAAK,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClC,GAAG,CAAC,oBAAoB,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,CAAC;AACrB,CAAC,CAAC;AAEF,iFAAiF;AAEjF;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,EAC5B,EAAS,EACT,GAAsB,EACtB,GAAwB,EACxB,KAA0B,EAC1B,OAAsB,EAAE,EACD,EAAE;IACzB,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,IAAI,GAAa,EAAE,CAAC;IAE1B,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY;IAC9E,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC;IAEjD,2EAA2E;IAC3E,IAAI,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IACzB,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC5C,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE;YACxB,IAAI,CAAC;gBACH,MAAM,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,SAAS,CAA6B,CAAC;gBACtE,OAAO,UAAU,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;YAChC,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,6EAA6E;IAC7E,GAAG,CAAC,0BAA0B,CAAC,CAAC;IAChC,MAAM,SAAS,GAAG,QAAQ,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC;IACjE,IAAI,SAAS,GAAG,gBAAgB,EAAE,CAAC;QACjC,MAAM,CAAC,IAAI,CAAC,WAAW,WAAW,iCAAiC,gBAAgB,EAAE,CAAC,CAAC;QACvF,KAAK,CAAC,mBAAmB,WAAW,kBAAkB,gBAAgB,2CAA2C,CAAC,CAAC;IACrH,CAAC;SAAM,CAAC;QACN,GAAG,CAAC,aAAa,WAAW,OAAO,CAAC,CAAC;IACvC,CAAC;IAED,8EAA8E;IAC9E,GAAG,CAAC,qBAAqB,CAAC,CAAC;IAC3B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,CAAC,IAAI,CAAC,kDAAkD,CAAC,CAAC;QAChE,KAAK,CAAC,kEAAkE,CAAC,CAAC;IAC5E,CAAC;SAAM,CAAC;QACN,GAAG,CAAC,uBAAuB,CAAC,CAAC;IAC/B,CAAC;IAED,8EAA8E;IAC9E,KAAK,MAAM,QAAQ,IAAI,gBAAgB,EAAE,CAAC;QACxC,GAAG,CAAC,YAAY,QAAQ,YAAY,CAAC,CAAC;QACtC,MAAM,MAAM,GAAG,gBAAgB,CAAC,EAAE,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;QAEnD,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACnB,QAAQ,CAAC,IAAI,CAAC,GAAG,QAAQ,wBAAwB,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;YAChE,GAAG,CAAC,KAAK,QAAQ,gDAAgD,CAAC,CAAC;YACnE,SAAS;QACX,CAAC;QAED,GAAG,CAAC,KAAK,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QAC5D,MAAM,OAAO,GAAG,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;QACxD,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;QAElD,IAAI,KAAK,EAAE,CAAC;YACV,IAAI,CAAC,IAAI,CAAC,GAAG,QAAQ,uBAAuB,KAAK,IAAI,SAAS,YAAY,CAAC,CAAC;YAC5E,GAAG,CAAC,wBAAwB,KAAK,EAAE,CAAC,CAAC;QACvC,CAAC;aAAM,CAAC;YACN,QAAQ,CAAC,IAAI,CAAC,GAAG,QAAQ,wBAAwB,CAAC,CAAC;YACnD,GAAG,CAAC,oCAAoC,CAAC,CAAC;QAC5C,CAAC;QAED,6EAA6E;QAC7E,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,OAAO,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,QAAQ,EAAE,CAAC;YACjI,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,6DAA6D,CAAC,CAAC;YACtF,KAAK,CAAC,WAAW,QAAQ,wBAAwB,CAAC,CAAC;QACrD,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,wBAAwB,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,GAAG,CAAC,4BAA4B,CAAC,CAAC;IAClC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC;IACnE,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,QAAQ,CAAC,IAAI,CAAC,+BAA+B,QAAQ,EAAE,CAAC,CAAC;QACzD,GAAG,CAAC,KAAK,QAAQ,6DAA6D,CAAC,CAAC;IAClF,CAAC;SAAM,CAAC;QACN,GAAG,CAAC,KAAK,QAAQ,UAAU,CAAC,CAAC;IAC/B,CAAC;IAED,8EAA8E;IAC9E,MAAM,SAAS,GAAG,GAAG,CAAC,mBAAmB,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;IACpF,GAAG,CAAC,2CAA2C,CAAC,CAAC;IACjD,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IACrC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,2CAA2C,SAAS,EAAE,CAAC,CAAC;QACpE,KAAK,CAAC,sCAAsC,SAAS,EAAE,CAAC,CAAC;IAC3D,CAAC;SAAM,CAAC;QACN,GAAG,CAAC,KAAK,SAAS,YAAY,CAAC,CAAC;IAClC,CAAC;IAED,8EAA8E;IAC9E,GAAG,CAAC,EAAE,CAAC,CAAC;IACR,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,GAAG,CAAC,iCAAiC,CAAC,CAAC;IACzC,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,eAAe,MAAM,CAAC,MAAM,+CAA+C,CAAC,CAAC;QACnF,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAClD,CAAC;IAED,OAAO;QACL,EAAE,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC;QACvB,MAAM;QACN,QAAQ;QACR,IAAI;KACL,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type CliFs } from './config.js';
|
|
2
|
+
export declare const CONFIG_BASENAMES: readonly ["opencode", "tui"];
|
|
3
|
+
export interface UninstallOptions {
|
|
4
|
+
purge?: boolean;
|
|
5
|
+
dryRun?: boolean;
|
|
6
|
+
yes?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface UninstallResultPerFile {
|
|
9
|
+
path: string;
|
|
10
|
+
status: 'wrote' | 'skipped' | 'error';
|
|
11
|
+
backup: string | null;
|
|
12
|
+
}
|
|
13
|
+
export interface UninstallResult {
|
|
14
|
+
status: 'wrote' | 'skipped';
|
|
15
|
+
results: UninstallResultPerFile[];
|
|
16
|
+
purged: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Uninstall opencode-rules-md from both opencode.json and tui.json configs.
|
|
20
|
+
*
|
|
21
|
+
* Options:
|
|
22
|
+
* purge — also remove ~/.cache/opencode/node_modules/opencode-rules-md
|
|
23
|
+
* dryRun — run full pipeline without writing to disk
|
|
24
|
+
* yes — accepted for future prompts, no-op here
|
|
25
|
+
*/
|
|
26
|
+
export declare const runUninstall: (opts: UninstallOptions | undefined, fs: CliFs, env: NodeJS.ProcessEnv) => UninstallResult;
|
|
27
|
+
//# sourceMappingURL=uninstall.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uninstall.d.ts","sourceRoot":"","sources":["../../../src/cli/uninstall.ts"],"names":[],"mappings":"AAUA,OAAO,EAOL,KAAK,KAAK,EACX,MAAM,aAAa,CAAC;AAErB,eAAO,MAAM,gBAAgB,8BAA+B,CAAC;AAE7D,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;IACtC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,OAAO,GAAG,SAAS,CAAC;IAC5B,OAAO,EAAE,sBAAsB,EAAE,CAAC;IAClC,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,GACvB,MAAM,gBAAgB,YAAK,EAC3B,IAAI,KAAK,EACT,KAAK,MAAM,CAAC,UAAU,KACrB,eA6FF,CAAC"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// src/cli/uninstall.ts — `omd uninstall` command implementation.
|
|
3
|
+
//
|
|
4
|
+
// Dual-config uninstall: removes opencode-rules-md entries from both configs.
|
|
5
|
+
// --purge removes ONLY ~/.cache/opencode/node_modules/opencode-rules-md and
|
|
6
|
+
// NEVER touches rule directories.
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
import { dirname, join } from 'path';
|
|
9
|
+
import { homedir } from 'os';
|
|
10
|
+
import { loadGlobalConfig, matchesPlugin, normalizePlugin, backupIfWritable, rotateBackups, writeAtomically, } from './config.js';
|
|
11
|
+
export const CONFIG_BASENAMES = ['opencode', 'tui'];
|
|
12
|
+
/**
|
|
13
|
+
* Uninstall opencode-rules-md from both opencode.json and tui.json configs.
|
|
14
|
+
*
|
|
15
|
+
* Options:
|
|
16
|
+
* purge — also remove ~/.cache/opencode/node_modules/opencode-rules-md
|
|
17
|
+
* dryRun — run full pipeline without writing to disk
|
|
18
|
+
* yes — accepted for future prompts, no-op here
|
|
19
|
+
*/
|
|
20
|
+
export const runUninstall = (opts = {}, fs, env) => {
|
|
21
|
+
const results = [];
|
|
22
|
+
let anyProcessed = false;
|
|
23
|
+
let purged = false;
|
|
24
|
+
// ── Purge cache if requested ────────────────────────────────────────────
|
|
25
|
+
if (opts.purge) {
|
|
26
|
+
const cachePath = join(homedir(), '.cache', 'opencode', 'node_modules', 'opencode-rules-md');
|
|
27
|
+
if (fs.existsSync(cachePath)) {
|
|
28
|
+
try {
|
|
29
|
+
// Check if it's a directory or a file
|
|
30
|
+
const entries = fs.readdirSync(cachePath);
|
|
31
|
+
if (entries.length === 0) {
|
|
32
|
+
// Empty directory — remove it
|
|
33
|
+
fs.rmdirSync(cachePath);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
// Has contents — remove contents then the dir
|
|
37
|
+
for (const entry of entries) {
|
|
38
|
+
const entryPath = join(cachePath, entry);
|
|
39
|
+
if (fs.existsSync(entryPath)) {
|
|
40
|
+
try {
|
|
41
|
+
fs.unlinkSync(entryPath);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// ignore individual file failures
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
fs.rmdirSync(cachePath);
|
|
49
|
+
}
|
|
50
|
+
purged = true;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// best-effort — cache purge failure is non-fatal
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// ── Remove plugin from both configs ─────────────────────────────────────
|
|
58
|
+
for (const basename of CONFIG_BASENAMES) {
|
|
59
|
+
const loaded = loadGlobalConfig(fs, env, basename);
|
|
60
|
+
const plugins = normalizePlugin(loaded.data['plugins']);
|
|
61
|
+
// Filter out all opencode-rules-md entries
|
|
62
|
+
const remaining = plugins.filter(p => !matchesPlugin(p));
|
|
63
|
+
if (remaining.length === plugins.length) {
|
|
64
|
+
// Nothing to remove — no-op for this file
|
|
65
|
+
results.push({ path: loaded.path, status: 'skipped', backup: null });
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
anyProcessed = true;
|
|
69
|
+
const newData = { ...loaded.data, plugins: remaining };
|
|
70
|
+
const newContent = JSON.stringify(newData, null, 2) + '\n';
|
|
71
|
+
if (opts.dryRun) {
|
|
72
|
+
results.push({ path: loaded.path, status: 'wrote', backup: null });
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
// Backup the existing file if it exists
|
|
76
|
+
let backup;
|
|
77
|
+
if (loaded.exists) {
|
|
78
|
+
backup = backupIfWritable(fs, loaded.path);
|
|
79
|
+
if (backup !== undefined) {
|
|
80
|
+
const dir = dirname(loaded.path);
|
|
81
|
+
const segs = loaded.path.replace(/\\/g, '/').split('/');
|
|
82
|
+
const base = segs[segs.length - 1] ?? loaded.path;
|
|
83
|
+
const dot = base.lastIndexOf('.');
|
|
84
|
+
const name = dot >= 0 ? base.slice(0, dot) : base;
|
|
85
|
+
rotateBackups(fs, dir, name, 3);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
writeAtomically(fs, loaded.path, newContent);
|
|
89
|
+
results.push({
|
|
90
|
+
path: loaded.path,
|
|
91
|
+
status: 'wrote',
|
|
92
|
+
backup: backup ?? null,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
status: anyProcessed || purged ? 'wrote' : 'skipped',
|
|
97
|
+
results,
|
|
98
|
+
purged,
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
//# sourceMappingURL=uninstall.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uninstall.js","sourceRoot":"","sources":["../../../src/cli/uninstall.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { CliFs } from './config.js';
|
|
2
|
+
export declare const CACHE_PATH: string;
|
|
3
|
+
export interface UpdateOptions {
|
|
4
|
+
latestVersion?: string | null | undefined;
|
|
5
|
+
dryRun?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface UpdateResult {
|
|
8
|
+
status: 'stale' | 'current' | 'unreachable';
|
|
9
|
+
cachePath: string;
|
|
10
|
+
instruction: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Resolve the local cache directory, honoring a custom HOME environment.
|
|
14
|
+
* Falls back to os.homedir() when HOME is not set, matching the
|
|
15
|
+
* resolveConfigDir fallback strategy.
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolveCachePath(env?: NodeJS.ProcessEnv): string;
|
|
18
|
+
/**
|
|
19
|
+
* Run the update command:
|
|
20
|
+
* 1. Fetch latest version from npm (or use provided mock)
|
|
21
|
+
* 2. Compare to installed version in config
|
|
22
|
+
* 3. If stale: purge cache, print reinstall instruction
|
|
23
|
+
* 4. If current: print "already current"
|
|
24
|
+
* 5. If unreachable: noop
|
|
25
|
+
*/
|
|
26
|
+
export declare const runUpdate: (fs: CliFs, env: NodeJS.ProcessEnv, log: (s: string) => void, _error: (s: string) => void, opts?: UpdateOptions) => Promise<UpdateResult>;
|
|
27
|
+
/**
|
|
28
|
+
* Recursively delete a directory and all its contents using the given fs.
|
|
29
|
+
*/
|
|
30
|
+
export declare function purgeDirectory(fs: CliFs, dirPath: string): void;
|
|
31
|
+
//# sourceMappingURL=update.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../src/cli/update.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAIzC,eAAO,MAAM,UAAU,QAMtB,CAAC;AAIF,MAAM,WAAW,aAAa;IAC5B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC1C,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,OAAO,GAAG,SAAS,GAAG,aAAa,CAAC;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAID;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAQ7E;AAuBD;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,GACpB,IAAI,KAAK,EACT,KAAK,MAAM,CAAC,UAAU,EACtB,KAAK,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,EACxB,QAAQ,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,EAC3B,OAAM,aAAkB,KACvB,OAAO,CAAC,YAAY,CAqFtB,CAAC;AAIF;;GAEG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAsC/D"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// src/cli/update.ts — `omd update` command implementation.
|
|
3
|
+
//
|
|
4
|
+
// Fetches latest from npm, compares to installed version, purges the
|
|
5
|
+
// ~/.cache/opencode/node_modules/opencode-rules-md cache, and prints the
|
|
6
|
+
// reinstall instruction when stale. No auto-reinstall.
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
import { homedir } from 'os';
|
|
9
|
+
import { join } from 'path';
|
|
10
|
+
import { fetchLatestVersion, isStale } from './registry.js';
|
|
11
|
+
import { loadGlobalConfig, matchesPlugin, normalizePlugin } from './config.js';
|
|
12
|
+
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
13
|
+
export const CACHE_PATH = join(homedir(), '.cache', 'opencode', 'node_modules', 'opencode-rules-md');
|
|
14
|
+
// ─── Cache path helpers ───────────────────────────────────────────────────────
|
|
15
|
+
/**
|
|
16
|
+
* Resolve the local cache directory, honoring a custom HOME environment.
|
|
17
|
+
* Falls back to os.homedir() when HOME is not set, matching the
|
|
18
|
+
* resolveConfigDir fallback strategy.
|
|
19
|
+
*/
|
|
20
|
+
export function resolveCachePath(env = process.env) {
|
|
21
|
+
return join(env.HOME ?? homedir(), '.cache', 'opencode', 'node_modules', 'opencode-rules-md');
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Read the cached package.json version, if determinable.
|
|
25
|
+
* Returns null when the cache is missing or unreadable.
|
|
26
|
+
*/
|
|
27
|
+
function readCacheVersion(fs, cachePath) {
|
|
28
|
+
const pkgPath = join(cachePath, 'package.json');
|
|
29
|
+
try {
|
|
30
|
+
if (!fs.existsSync(pkgPath)) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath));
|
|
34
|
+
return typeof pkg.version === 'string' ? pkg.version : null;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// ─── runUpdate ────────────────────────────────────────────────────────────────
|
|
41
|
+
const CONFIG_BASENAMES = ['opencode', 'tui'];
|
|
42
|
+
/**
|
|
43
|
+
* Run the update command:
|
|
44
|
+
* 1. Fetch latest version from npm (or use provided mock)
|
|
45
|
+
* 2. Compare to installed version in config
|
|
46
|
+
* 3. If stale: purge cache, print reinstall instruction
|
|
47
|
+
* 4. If current: print "already current"
|
|
48
|
+
* 5. If unreachable: noop
|
|
49
|
+
*/
|
|
50
|
+
export const runUpdate = async (fs, env, log, _error, opts = {}) => {
|
|
51
|
+
// Use provided latestVersion for testing, otherwise fetch from npm
|
|
52
|
+
const latest = opts.latestVersion !== undefined
|
|
53
|
+
? opts.latestVersion
|
|
54
|
+
: await fetchLatestVersion();
|
|
55
|
+
const cachePath = resolveCachePath(env);
|
|
56
|
+
if (latest === null) {
|
|
57
|
+
log('omd: could not determine latest version from npm registry');
|
|
58
|
+
return {
|
|
59
|
+
status: 'unreachable',
|
|
60
|
+
cachePath,
|
|
61
|
+
instruction: 'npx opencode-rules-md@latest install',
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
// Determine installed version from config (check both configs)
|
|
65
|
+
let installedVersion = null;
|
|
66
|
+
for (const basename of CONFIG_BASENAMES) {
|
|
67
|
+
const loaded = loadGlobalConfig(fs, env, basename);
|
|
68
|
+
const plugins = normalizePlugin(loaded.data['plugins']);
|
|
69
|
+
const match = plugins.find(p => matchesPlugin(p));
|
|
70
|
+
if (match) {
|
|
71
|
+
// Extract version from specifier (e.g. 'opencode-rules-md@2.0.0' -> '2.0.0')
|
|
72
|
+
const atIndex = match.lastIndexOf('@');
|
|
73
|
+
installedVersion = atIndex >= 0 ? match.slice(atIndex + 1) : null;
|
|
74
|
+
break; // use first config that has it
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const instruction = `npx opencode-rules-md@latest install`;
|
|
78
|
+
// Check staleness: only stale if installed version differs from latest
|
|
79
|
+
if (installedVersion !== null && !isStale(installedVersion, latest)) {
|
|
80
|
+
log(`omd: opencode-rules-md@${installedVersion} is already the latest`);
|
|
81
|
+
return {
|
|
82
|
+
status: 'current',
|
|
83
|
+
cachePath,
|
|
84
|
+
instruction,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
// Before purging, decide whether the cache is actually stale. When the cache
|
|
88
|
+
// version is already equal to the latest resolved version, skip the purge
|
|
89
|
+
// so we do not delete a valid cache. If the version cannot be determined,
|
|
90
|
+
// fall back to purging to be safe.
|
|
91
|
+
const cacheVersion = readCacheVersion(fs, cachePath);
|
|
92
|
+
const shouldPurge = cacheVersion === null || cacheVersion !== latest;
|
|
93
|
+
if (opts.dryRun) {
|
|
94
|
+
log(`omd: update check (dry-run)`);
|
|
95
|
+
log(` latest version: ${latest}`);
|
|
96
|
+
log(` installed version: ${installedVersion ?? 'not installed'}`);
|
|
97
|
+
log(` would purge: ${shouldPurge ? cachePath : '(nothing to purge)'}`);
|
|
98
|
+
log(` would instruct: ${instruction}`);
|
|
99
|
+
return {
|
|
100
|
+
status: 'stale',
|
|
101
|
+
cachePath,
|
|
102
|
+
instruction,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
// Purge the cache directory using the injected fs (allows test faking)
|
|
106
|
+
if (shouldPurge) {
|
|
107
|
+
try {
|
|
108
|
+
if (fs.existsSync(cachePath)) {
|
|
109
|
+
// Recursively remove contents then the dir itself
|
|
110
|
+
purgeDirectory(fs, cachePath);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// best-effort — purge failure is non-fatal
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
log(`omd: opencode-rules-md is stale (latest: ${latest})`);
|
|
118
|
+
log(`omd: cache purged at ${cachePath}`);
|
|
119
|
+
log(`omd: to reinstall, run:`);
|
|
120
|
+
log(` ${instruction}`);
|
|
121
|
+
return {
|
|
122
|
+
status: 'stale',
|
|
123
|
+
cachePath,
|
|
124
|
+
instruction,
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
// ─── purgeDirectory ────────────────────────────────────────────────────────────
|
|
128
|
+
/**
|
|
129
|
+
* Recursively delete a directory and all its contents using the given fs.
|
|
130
|
+
*/
|
|
131
|
+
export function purgeDirectory(fs, dirPath) {
|
|
132
|
+
let entries = [];
|
|
133
|
+
try {
|
|
134
|
+
entries = fs.readdirSync(dirPath);
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
for (const entry of entries) {
|
|
140
|
+
const entryPath = join(dirPath, entry);
|
|
141
|
+
try {
|
|
142
|
+
if (fs.existsSync(entryPath)) {
|
|
143
|
+
// Check if it's a directory by trying to read it
|
|
144
|
+
try {
|
|
145
|
+
const subEntries = fs.readdirSync(entryPath);
|
|
146
|
+
if (subEntries.length === 0) {
|
|
147
|
+
// Empty directory
|
|
148
|
+
fs.rmdirSync(entryPath);
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
// Non-empty directory — recurse
|
|
152
|
+
purgeDirectory(fs, entryPath);
|
|
153
|
+
fs.rmdirSync(entryPath);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
// It's a file — unlink it
|
|
158
|
+
fs.unlinkSync(entryPath);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
// best-effort per entry
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
try {
|
|
167
|
+
fs.rmdirSync(dirPath);
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
// best-effort
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=update.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update.js","sourceRoot":"","sources":["../../../src/cli/update.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-rules-md",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.5",
|
|
4
4
|
"description": "OpenCode plugin that discovers and injects markdown rules into system prompts",
|
|
5
5
|
"main": "dist/src/index.js",
|
|
6
6
|
"types": "dist/src/index.d.ts",
|
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
"server",
|
|
10
10
|
"tui"
|
|
11
11
|
],
|
|
12
|
+
"bin": {
|
|
13
|
+
"omd": "./dist/cli.mjs"
|
|
14
|
+
},
|
|
12
15
|
"keywords": [
|
|
13
16
|
"opencode",
|
|
14
17
|
"plugin",
|
|
@@ -27,6 +30,7 @@
|
|
|
27
30
|
},
|
|
28
31
|
"files": [
|
|
29
32
|
"dist",
|
|
33
|
+
"dist/cli.mjs",
|
|
30
34
|
"!dist/**/*.test.*",
|
|
31
35
|
"!dist/**/*.spec.*",
|
|
32
36
|
"!dist/**/test-fixtures.*",
|
|
@@ -60,10 +64,7 @@
|
|
|
60
64
|
},
|
|
61
65
|
"peerDependencies": {
|
|
62
66
|
"@opencode-ai/plugin": "^1.3.9",
|
|
63
|
-
"@opencode-ai/sdk": "^1.3.9"
|
|
64
|
-
"@opentui/core": "^0.4.3",
|
|
65
|
-
"@opentui/solid": "^0.4.3",
|
|
66
|
-
"solid-js": "^1.9.12"
|
|
67
|
+
"@opencode-ai/sdk": "^1.3.9"
|
|
67
68
|
},
|
|
68
69
|
"peerDependenciesMeta": {
|
|
69
70
|
"@opencode-ai/plugin": {
|
|
@@ -71,15 +72,6 @@
|
|
|
71
72
|
},
|
|
72
73
|
"@opencode-ai/sdk": {
|
|
73
74
|
"optional": true
|
|
74
|
-
},
|
|
75
|
-
"@opentui/core": {
|
|
76
|
-
"optional": true
|
|
77
|
-
},
|
|
78
|
-
"@opentui/solid": {
|
|
79
|
-
"optional": true
|
|
80
|
-
},
|
|
81
|
-
"solid-js": {
|
|
82
|
-
"optional": true
|
|
83
75
|
}
|
|
84
76
|
},
|
|
85
77
|
"devDependencies": {
|
|
@@ -98,11 +90,15 @@
|
|
|
98
90
|
"vitest": "^2.1.8"
|
|
99
91
|
},
|
|
100
92
|
"dependencies": {
|
|
93
|
+
"@opentui/core": "^0.4.3",
|
|
94
|
+
"@opentui/solid": "^0.4.3",
|
|
101
95
|
"minimatch": "^9.0.5",
|
|
96
|
+
"solid-js": "^1.9.12",
|
|
102
97
|
"yaml": "^2.8.2"
|
|
103
98
|
},
|
|
104
99
|
"scripts": {
|
|
105
|
-
"build": "tsc && bun scripts/build-tui.mjs",
|
|
100
|
+
"build": "tsc && bun scripts/build-tui.mjs && bun scripts/build-cli.mjs",
|
|
101
|
+
"build:cli": "bun scripts/build-cli.mjs",
|
|
106
102
|
"dev": "tsc --watch",
|
|
107
103
|
"test": "vitest",
|
|
108
104
|
"test:run": "vitest run",
|