@hmanlab/hl-plugins 0.1.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.
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env node
2
+ // Shim for the `hl-plugins` binary.
3
+ // Prefers the compiled dist/ entry (published + after `npm run build`).
4
+ // Falls back to running src/index.ts through tsx (dev, no build required).
5
+
6
+ import { spawn } from "node:child_process"
7
+ import { fileURLToPath } from "node:url"
8
+ import { dirname, join, resolve } from "node:path"
9
+ import { existsSync } from "node:fs"
10
+
11
+ const here = dirname(fileURLToPath(import.meta.url))
12
+ const pkgDir = resolve(here, "..")
13
+ const args = process.argv.slice(2)
14
+
15
+ // Locate the entry. dist/ wins; src/ is the dev fallback.
16
+ const distEntry = join(pkgDir, "dist", "index.js")
17
+ const srcEntry = join(pkgDir, "src", "index.ts")
18
+
19
+ if (existsSync(distEntry)) {
20
+ const child = spawn(process.execPath, [distEntry, ...args], { stdio: "inherit" })
21
+ child.on("exit", (code, signal) => {
22
+ if (signal) process.kill(process.pid, signal)
23
+ process.exit(code ?? 0)
24
+ })
25
+ } else {
26
+ // Dev path: find tsx in any ancestor node_modules.
27
+ const candidates = [
28
+ join(pkgDir, "node_modules", ".bin", "tsx"),
29
+ join(pkgDir, "..", "..", "node_modules", ".bin", "tsx"),
30
+ ]
31
+ const tsxBin = candidates.find((p) => existsSync(p))
32
+ if (!tsxBin) {
33
+ console.error(
34
+ "hl-plugins: tsx not found and dist/ is missing.\n" +
35
+ "Run `npm install` at the monorepo root, or `npm run build` to compile.",
36
+ )
37
+ process.exit(1)
38
+ }
39
+ const child = spawn(resolve(tsxBin), [srcEntry, ...args], { stdio: "inherit" })
40
+ child.on("exit", (code, signal) => {
41
+ if (signal) process.kill(process.pid, signal)
42
+ process.exit(code ?? 0)
43
+ })
44
+ }
@@ -0,0 +1,30 @@
1
+ // Show all commands and a short description of each.
2
+ const rows = [
3
+ ["hl-plugins install [plugin]", "Install one or more plugins"],
4
+ ["hl-plugins uninstall [plugin]", "Remove one or more plugins"],
5
+ ["hl-plugins list", "Show known plugins and their install state"],
6
+ ["hl-plugins status [plugin]", "Per-plugin diagnostic report"],
7
+ ["hl-plugins update [plugin]", "Re-copy files and bump dependencies"],
8
+ ["hl-plugins help", "Show this help text"],
9
+ ];
10
+ import { ui } from "../lib/ui.js";
11
+ import { opencodeConfigDir, tilde } from "../lib/paths.js";
12
+ export async function help(_args) {
13
+ const out = [];
14
+ out.push(ui.header("hl-plugins — curated OpenCode plugins"));
15
+ out.push(ui.bold("Usage:"), " hl-plugins <command> [args]", "", ui.bold("Commands:"));
16
+ for (const [name, desc] of rows) {
17
+ out.push(` ${ui.cyan(name.padEnd(38))} ${desc}`);
18
+ }
19
+ out.push("");
20
+ out.push(ui.bold("Install target:"));
21
+ out.push(` ${tilde(opencodeConfigDir())}/`);
22
+ out.push("");
23
+ out.push(ui.bold("More info:"));
24
+ out.push(" docs/commands.md -- full CLI reference");
25
+ out.push(" docs/architecture.md -- install flow + plugin contract");
26
+ out.push(" docs/adding-a-plugin.md -- how to add a new plugin");
27
+ ui.info(out.join("\n"));
28
+ return 0;
29
+ }
30
+ //# sourceMappingURL=help.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"help.js","sourceRoot":"","sources":["../../src/commands/help.ts"],"names":[],"mappings":"AAAA,qDAAqD;AAErD,MAAM,IAAI,GAA4B;IACpC,CAAC,6BAA6B,EAAE,6BAA6B,CAAC;IAC9D,CAAC,+BAA+B,EAAE,4BAA4B,CAAC;IAC/D,CAAC,iBAAiB,EAAE,4CAA4C,CAAC;IACjE,CAAC,4BAA4B,EAAE,8BAA8B,CAAC;IAC9D,CAAC,4BAA4B,EAAE,qCAAqC,CAAC;IACrE,CAAC,iBAAiB,EAAE,qBAAqB,CAAC;CAC3C,CAAA;AAED,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AACjC,OAAO,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAA;AAE1D,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,KAAe;IACxC,MAAM,GAAG,GAAa,EAAE,CAAA;IACxB,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,uCAAuC,CAAC,CAAC,CAAA;IAC5D,GAAG,CAAC,IAAI,CACN,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,EACjB,+BAA+B,EAC/B,EAAE,EACF,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,CACrB,CAAA;IACD,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC;QAChC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAA;IACnD,CAAC;IACD,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACZ,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAA;IACpC,GAAG,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,iBAAiB,EAAE,CAAC,GAAG,CAAC,CAAA;IAC5C,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACZ,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAA;IAC/B,GAAG,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAA;IACrD,GAAG,CAAC,IAAI,CAAC,0DAA0D,CAAC,CAAA;IACpE,GAAG,CAAC,IAAI,CAAC,sDAAsD,CAAC,CAAA;IAChE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;IACvB,OAAO,CAAC,CAAA;AACV,CAAC"}
@@ -0,0 +1,247 @@
1
+ // install [plugin] -- full install flow.
2
+ //
3
+ // Steps (mirrors docs/architecture.md):
4
+ // 1. Resolve plugin
5
+ // 2. Pre-flight (Node version, opencode config dir)
6
+ // 3. Requirements (auto-install missing binaries)
7
+ // 4. Authenticate (hidden API key prompt, region auto-retry on 401)
8
+ // 5. Copy files (plugin + skill) into ~/.opencode/
9
+ // 6. Merge config (additive, idempotent)
10
+ // 7. Verify (run postInstall commands)
11
+ import { copyFileSync, existsSync, mkdirSync, statSync } from "node:fs";
12
+ import { basename, dirname, join } from "node:path";
13
+ import { defaultInstallPlugins, getPlugin } from "../lib/registry.js";
14
+ import { run, tryRun, ShellError, fillTemplate } from "../lib/shell.js";
15
+ import { ui } from "../lib/ui.js";
16
+ import { opencodeConfigDir, opencodePluginDir, opencodeSkillDir, tilde, } from "../lib/paths.js";
17
+ import { addBashPermission, addPluginToConfig } from "../lib/config.js";
18
+ // ── Per-step actions ──────────────────────────────────────────────────────
19
+ async function checkPreflight() {
20
+ const major = parseInt(process.versions.node.split(".")[0] ?? "0", 10);
21
+ if (major < 18) {
22
+ throw new Error(`Node.js >= 18 required (you have ${process.versions.node}).`);
23
+ }
24
+ if (!existsSync(opencodeConfigDir())) {
25
+ mkdirSync(opencodeConfigDir(), { recursive: true });
26
+ }
27
+ }
28
+ async function ensureRequirement(req) {
29
+ const probe = await tryRun(req.check);
30
+ if (probe && probe.code === 0) {
31
+ // Try to surface a version string from the probe output if present
32
+ const line = (probe.stdout.trim().split("\n")[0] ?? "").trim();
33
+ return line ? `present (${line})` : "present";
34
+ }
35
+ // Not present -- auto-install
36
+ ui.info(` ${ui.cyan("→")} installing ${req.name}: ${req.install}`);
37
+ const install = await run(req.install, { throwOnError: false });
38
+ if (install.code !== 0) {
39
+ throw new Error(`Failed to install ${req.name}.\n` +
40
+ ` command: ${req.install}\n` +
41
+ ` ${install.stderr.trim() || install.stdout.trim() || "(no output)"}`);
42
+ }
43
+ // Re-check
44
+ const verify = await tryRun(req.check);
45
+ if (!verify || verify.code !== 0) {
46
+ throw new Error(`Installed ${req.name} but "${req.check}" still fails. ` +
47
+ `Make sure it's on your PATH.`);
48
+ }
49
+ return "installed";
50
+ }
51
+ async function authenticate(plugin, opts) {
52
+ const auth = plugin.contract.auth;
53
+ if (!auth)
54
+ return "no auth required";
55
+ if (opts.skipAuth)
56
+ return "skipped (--no-auth)";
57
+ // Already logged in?
58
+ const status = await tryRun(auth.check);
59
+ if (status && status.code === 0)
60
+ return "already authenticated";
61
+ // Find a key: flag → env var → prompt
62
+ let key = opts.key;
63
+ if (!key && auth.envVar)
64
+ key = process.env[auth.envVar];
65
+ if (!key) {
66
+ key = await ui.promptHidden(`Paste your ${auth.keyLabel} (input hidden): `);
67
+ }
68
+ if (!key)
69
+ throw new Error(`No ${auth.keyLabel} provided.`);
70
+ const loginCmd = fillTemplate(auth.login, { key });
71
+ const login = await run(loginCmd, { throwOnError: false });
72
+ if (login.code !== 0) {
73
+ throw new Error(`Login failed (exit ${login.code}).\n` +
74
+ ` ${(login.stderr || login.stdout).trim() || "(no output)"}`);
75
+ }
76
+ // Smoke test + region auto-retry
77
+ const regions = ["global", "cn"];
78
+ for (const attempt of [auth.verify, ...regions.map((r) => `mmx config set --key region --value ${r} && ${auth.verify}`)]) {
79
+ const res = await run(attempt, { throwOnError: false });
80
+ if (res.code === 0) {
81
+ return attempt === auth.verify ? "authenticated" : "authenticated (region auto-retry succeeded)";
82
+ }
83
+ }
84
+ throw new Error(`Authentication smoke test ("${auth.verify}") failed. ` +
85
+ `Tried region=global and region=cn. ` +
86
+ `Check your key and network connection.`);
87
+ }
88
+ async function copyPluginFiles(plugin) {
89
+ const copied = [];
90
+ if (plugin.contract.opencodePlugin) {
91
+ const src = join(plugin.packageDir, plugin.contract.opencodePlugin);
92
+ const dest = join(opencodePluginDir(), basename(plugin.contract.opencodePlugin));
93
+ if (!existsSync(src)) {
94
+ throw new Error(`Plugin source missing: ${src}`);
95
+ }
96
+ mkdirSync(dirname(dest), { recursive: true });
97
+ copyFileSync(src, dest);
98
+ copied.push(tilde(dest));
99
+ }
100
+ if (plugin.contract.opencodeSkill) {
101
+ const src = join(plugin.packageDir, plugin.contract.opencodeSkill);
102
+ if (!existsSync(src)) {
103
+ throw new Error(`Skill source missing: ${src}`);
104
+ }
105
+ // Preserve the relative path under skill/ (e.g. "mmx/SKILL.md" or "mmx/index.md")
106
+ const parts = plugin.contract.opencodeSkill.split("/");
107
+ const skillIdx = parts.lastIndexOf("skill");
108
+ const rel = skillIdx >= 0 ? parts.slice(skillIdx + 1).join("/") : basename(src);
109
+ const dest = join(opencodeSkillDir(), rel);
110
+ if (statSync(src).isDirectory()) {
111
+ // skill is a directory → copy its contents recursively
112
+ await copyDir(src, dirname(dest));
113
+ }
114
+ else {
115
+ mkdirSync(dirname(dest), { recursive: true });
116
+ copyFileSync(src, dest);
117
+ }
118
+ copied.push(tilde(dest));
119
+ }
120
+ return copied;
121
+ }
122
+ async function copyDir(src, dest) {
123
+ mkdirSync(dest, { recursive: true });
124
+ // Use the shell's `cp -R` -- works on macOS + Linux, no extra dep.
125
+ // The `/.` trick copies contents without nesting a subdirectory.
126
+ await run(`cp -R ${JSON.stringify(src + "/.")} ${JSON.stringify(dest)}`);
127
+ }
128
+ async function mergeConfig(plugin) {
129
+ const changes = [];
130
+ if (plugin.contract.opencodePlugin) {
131
+ const filename = basename(plugin.contract.opencodePlugin);
132
+ const entry = `./plugin/${filename}`;
133
+ if (addPluginToConfig(entry)) {
134
+ changes.push(`added ${entry} to plugin[]`);
135
+ }
136
+ else {
137
+ changes.push(`plugin[] already has ${entry}`);
138
+ }
139
+ }
140
+ if (plugin.contract.permission) {
141
+ if (addBashPermission(plugin.contract.permission, "allow")) {
142
+ changes.push(`added bash.${plugin.contract.permission} = "allow"`);
143
+ }
144
+ else {
145
+ changes.push(`bash.${plugin.contract.permission} = "allow" already set`);
146
+ }
147
+ }
148
+ return changes;
149
+ }
150
+ async function verify(plugin) {
151
+ const out = [];
152
+ for (const cmd of plugin.contract.postInstall ?? []) {
153
+ const res = await run(cmd, { throwOnError: false });
154
+ const text = (res.stdout || res.stderr).trim();
155
+ if (res.code !== 0) {
156
+ throw new Error(`Verification failed: ${cmd}\n` +
157
+ ` ${text || "(no output)"}`);
158
+ }
159
+ out.push(text.split("\n")[0] || cmd);
160
+ }
161
+ return out;
162
+ }
163
+ async function installOne(plugin, opts, step, total) {
164
+ ui.info(ui.bold(`\n[${step}/${total}] ${plugin.name} -- ${plugin.description}`));
165
+ await ui.spinner("Pre-flight checks", async () => {
166
+ await checkPreflight();
167
+ });
168
+ for (const req of plugin.contract.requires ?? []) {
169
+ const note = await ui.spinner(`Requirement: ${req.name}`, () => ensureRequirement(req));
170
+ if (note)
171
+ ui.info(` ${ui.dim(note)}`);
172
+ }
173
+ const auth = plugin.contract.auth;
174
+ if (auth) {
175
+ const note = await ui.spinner(`Authenticate: ${auth.keyLabel}`, () => authenticate(plugin, opts));
176
+ if (note)
177
+ ui.info(` ${ui.dim(note)}`);
178
+ }
179
+ const copied = await ui.spinner("Copy files", () => copyPluginFiles(plugin));
180
+ for (const p of copied)
181
+ ui.info(` ${ui.dim("→")} ${p}`);
182
+ const configChanges = await ui.spinner("Merge opencode config", () => mergeConfig(plugin));
183
+ for (const c of configChanges)
184
+ ui.info(` ${ui.dim("•")} ${c}`);
185
+ if (plugin.contract.postInstall?.length) {
186
+ const verified = await ui.spinner("Verify", () => verify(plugin));
187
+ for (const v of verified)
188
+ ui.info(` ${ui.dim("•")} ${v}`);
189
+ }
190
+ }
191
+ function parseArgs(args) {
192
+ const names = [];
193
+ let key;
194
+ let skipAuth = false;
195
+ for (let i = 0; i < args.length; i++) {
196
+ const a = args[i];
197
+ if (a === "--no-auth")
198
+ skipAuth = true;
199
+ else if (a === "--key" || a === "-k") {
200
+ key = args[++i];
201
+ if (!key)
202
+ throw new Error("--key requires a value");
203
+ }
204
+ else if (a === "--verbose" || a === "-v") {
205
+ // handled below
206
+ }
207
+ else if (a.startsWith("--")) {
208
+ // unknown flag -- ignore for now, but warn
209
+ ui.warn(`unknown flag: ${a}`);
210
+ }
211
+ else {
212
+ names.push(a);
213
+ }
214
+ }
215
+ return { names, skipAuth, key, verbose: args.includes("--verbose") || args.includes("-v") || process.env["HL_PLUGINS_DEBUG"] === "1" };
216
+ }
217
+ export async function install(args) {
218
+ const opts = parseArgs(args);
219
+ const targets = opts.names.length > 0 ? opts.names.map((n) => getPlugin(n)) : defaultInstallPlugins();
220
+ if (targets.length === 0) {
221
+ ui.warn("No plugins to install (none marked defaultInstall in this monorepo).");
222
+ return 0;
223
+ }
224
+ let succeeded = 0;
225
+ for (let i = 0; i < targets.length; i++) {
226
+ const plugin = targets[i];
227
+ try {
228
+ await installOne(plugin, opts, i + 1, targets.length);
229
+ succeeded++;
230
+ }
231
+ catch (err) {
232
+ ui.info("");
233
+ ui.error(`${plugin.name}: ${err.message}`);
234
+ if (err instanceof ShellError) {
235
+ ui.info(ui.dim(` command: ${err.cmd}`));
236
+ }
237
+ // Don't roll back: the install is idempotent, so re-running picks
238
+ // up where this attempt left off.
239
+ return 1;
240
+ }
241
+ }
242
+ if (succeeded > 0) {
243
+ ui.info(ui.green(`\n✓ Done. Installed ${succeeded} plugin${succeeded === 1 ? "" : "s"}. Restart opencode to use the new tools.`));
244
+ }
245
+ return 0;
246
+ }
247
+ //# sourceMappingURL=install.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ // list -- show all known plugins and whether they're installed.
2
+ import { existsSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { discoverPlugins } from "../lib/registry.js";
5
+ import { ui } from "../lib/ui.js";
6
+ import { opencodePluginDir, tilde } from "../lib/paths.js";
7
+ export async function list(_args) {
8
+ const plugins = discoverPlugins();
9
+ if (plugins.length === 0) {
10
+ ui.info("No plugins discovered in this monorepo.");
11
+ return 0;
12
+ }
13
+ const nameW = Math.max(5, ...plugins.map((p) => p.name.length));
14
+ const verW = Math.max(7, ...plugins.map((p) => p.version.length));
15
+ const stateW = 9;
16
+ const header = [
17
+ "PLUGIN".padEnd(nameW),
18
+ "INSTALLED".padEnd(stateW),
19
+ "VERSION".padEnd(verW),
20
+ "DESCRIPTION",
21
+ ].join(" ");
22
+ ui.info(ui.header("hl-plugins -- known plugins"));
23
+ ui.info(ui.bold(header));
24
+ for (const p of plugins) {
25
+ const pluginTarget = p.contract.opencodePlugin
26
+ ? join(opencodePluginDir(), p.contract.opencodePlugin.split("/").pop() ?? "")
27
+ : null;
28
+ const installed = pluginTarget && existsSync(pluginTarget) ? "✓" : "·";
29
+ ui.info([
30
+ ui.cyan(p.name.padEnd(nameW)),
31
+ (installed === "✓" ? ui.green : ui.gray)(installed.padEnd(stateW)),
32
+ ui.gray(p.version.padEnd(verW)),
33
+ p.description || ui.gray("(no description)"),
34
+ ].join(" "));
35
+ }
36
+ ui.info(ui.dim(`\ninstall target: ${tilde(opencodePluginDir())}/`));
37
+ return 0;
38
+ }
39
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAEhE,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACpD,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AACjC,OAAO,EAAE,iBAAiB,EAAoB,KAAK,EAAE,MAAM,iBAAiB,CAAA;AAE5E,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,KAAe;IACxC,MAAM,OAAO,GAAG,eAAe,EAAE,CAAA;IACjC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,EAAE,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAA;QAClD,OAAO,CAAC,CAAA;IACV,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;IAC/D,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAA;IACjE,MAAM,MAAM,GAAG,CAAC,CAAA;IAEhB,MAAM,MAAM,GAAG;QACb,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC;QACtB,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC;QAC1B,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC;QACtB,aAAa;KACd,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAEZ,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,6BAA6B,CAAC,CAAC,CAAA;IACjD,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;IACxB,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,YAAY,GAAG,CAAC,CAAC,QAAQ,CAAC,cAAc;YAC5C,CAAC,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;YAC7E,CAAC,CAAC,IAAI,CAAA;QACR,MAAM,SAAS,GAAG,YAAY,IAAI,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAA;QACvE,EAAE,CAAC,IAAI,CACL;YACE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC7B,CAAC,SAAS,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAClE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC/B,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC,IAAI,CAAC,kBAAkB,CAAC;SAC7C,CAAC,IAAI,CAAC,IAAI,CAAC,CACb,CAAA;IACH,CAAC;IACD,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,qBAAqB,KAAK,CAAC,iBAAiB,EAAE,CAAC,GAAG,CAAC,CAAC,CAAA;IACnE,OAAO,CAAC,CAAA;AACV,CAAC"}
@@ -0,0 +1,120 @@
1
+ // status [plugin] -- per-plugin diagnostic report.
2
+ // Shows: file presence, config merge state, required binaries, auth, smoke test.
3
+ import { existsSync } from "node:fs";
4
+ import { basename, join } from "node:path";
5
+ import { discoverPlugins, getPlugin } from "../lib/registry.js";
6
+ import { run, tryRun } from "../lib/shell.js";
7
+ import { ui } from "../lib/ui.js";
8
+ import { opencodePluginDir, opencodeSkillDir, tilde } from "../lib/paths.js";
9
+ import { readOpencodeConfig } from "../lib/config.js";
10
+ const LABEL_W = 16;
11
+ function row(label, value, ok) {
12
+ const pad = label.padEnd(LABEL_W);
13
+ const mark = ok === "info" ? ui.dim("·") : ok ? ui.green("✓") : ui.red("✗");
14
+ return ` ${ui.dim(pad)} ${value} ${mark}`;
15
+ }
16
+ function firstNonEmptyLine(s) {
17
+ for (const line of s.split("\n")) {
18
+ const t = line.trim();
19
+ if (t)
20
+ return t;
21
+ }
22
+ return "";
23
+ }
24
+ async function checkBinary(req) {
25
+ const res = await tryRun(req.check);
26
+ if (res && res.code === 0) {
27
+ return { ok: true, version: firstNonEmptyLine(res.stdout) || "present" };
28
+ }
29
+ return { ok: false, version: "not found" };
30
+ }
31
+ function skillDest(contractPath) {
32
+ const parts = contractPath.split("/");
33
+ const skillIdx = parts.lastIndexOf("skill");
34
+ if (skillIdx === -1)
35
+ return null;
36
+ const rel = parts.slice(skillIdx + 1).join("/");
37
+ return join(opencodeSkillDir(), rel);
38
+ }
39
+ async function reportOne(plugin) {
40
+ ui.info(ui.bold(`\n${plugin.name} -- ${plugin.description || "(no description)"}`));
41
+ // Plugin file
42
+ if (plugin.contract.opencodePlugin) {
43
+ const dest = join(opencodePluginDir(), basename(plugin.contract.opencodePlugin));
44
+ ui.info(row("Plugin file:", tilde(dest), existsSync(dest)));
45
+ }
46
+ // Skill file
47
+ if (plugin.contract.opencodeSkill) {
48
+ const dest = skillDest(plugin.contract.opencodeSkill);
49
+ if (dest) {
50
+ ui.info(row("Skill file:", tilde(dest), existsSync(dest)));
51
+ }
52
+ }
53
+ // Config merged: plugin[] entry + permission pattern
54
+ const cfg = readOpencodeConfig();
55
+ const pluginEntry = plugin.contract.opencodePlugin
56
+ ? `./plugin/${basename(plugin.contract.opencodePlugin)}`
57
+ : null;
58
+ const pluginInCfg = pluginEntry ? (cfg.plugin ?? []).includes(pluginEntry) : true;
59
+ const permInCfg = plugin.contract.permission
60
+ ? cfg.permission?.bash?.[plugin.contract.permission] === "allow"
61
+ : true;
62
+ const configOk = pluginInCfg && permInCfg;
63
+ const configDetail = configOk
64
+ ? `plugin[] + ${plugin.contract.permission ?? "(no perm)"}`
65
+ : [pluginInCfg ? null : "plugin[]", permInCfg ? null : "permission"].filter(Boolean).join(" + ") + " missing";
66
+ ui.info(row("Config merged:", configDetail, configOk));
67
+ // Requirements
68
+ for (const req of plugin.contract.requires ?? []) {
69
+ const { ok, version } = await checkBinary(req);
70
+ ui.info(row(`Required: ${req.name}`, version, ok));
71
+ }
72
+ // Auth: just show logged in / not logged in based on exit code
73
+ if (plugin.contract.auth) {
74
+ const status = await tryRun(plugin.contract.auth.check);
75
+ ui.info(row("Auth:", "logged in", status?.code === 0));
76
+ }
77
+ // Smoke test (postInstall)
78
+ for (const cmd of plugin.contract.postInstall ?? []) {
79
+ const res = await run(cmd, { throwOnError: false });
80
+ if (res.code === 0) {
81
+ const detail = summarizeOutput(res.stdout);
82
+ ui.info(row(`${cmd.split(" ")[0]}:`, detail, true));
83
+ }
84
+ else {
85
+ const errText = firstNonEmptyLine(res.stderr || res.stdout) || `(exit ${res.code})`;
86
+ ui.info(row(`${cmd.split(" ")[0]}:`, ui.red(errText), false));
87
+ }
88
+ }
89
+ }
90
+ /** Collapse multi-line output to a single readable line. */
91
+ function summarizeOutput(s) {
92
+ const collapsed = s.replace(/\s+/g, " ").trim();
93
+ if (collapsed.length <= 120)
94
+ return collapsed || "ok";
95
+ return collapsed.slice(0, 117) + "...";
96
+ }
97
+ function parseArgs(args) {
98
+ const names = [];
99
+ for (const a of args) {
100
+ if (a.startsWith("-"))
101
+ ui.warn(`unknown flag: ${a}`);
102
+ else
103
+ names.push(a);
104
+ }
105
+ return { names };
106
+ }
107
+ export async function status(args) {
108
+ const { names } = parseArgs(args);
109
+ const targets = names.length > 0 ? names.map((n) => getPlugin(n)) : discoverPlugins();
110
+ if (targets.length === 0) {
111
+ ui.info("No plugins discovered.");
112
+ return 0;
113
+ }
114
+ let allOk = true;
115
+ for (const plugin of targets) {
116
+ await reportOne(plugin);
117
+ }
118
+ return 0;
119
+ }
120
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,121 @@
1
+ // uninstall [plugin] -- remove plugin files and config entries.
2
+ // Symmetric with install: same manifest, same idempotency rules.
3
+ // Does NOT remove the plugin's dependencies (e.g. mmx-cli) or auth.
4
+ import { existsSync, readdirSync, rmSync } from "node:fs";
5
+ import { basename, dirname, join } from "node:path";
6
+ import { discoverPlugins, getPlugin } from "../lib/registry.js";
7
+ import { ui } from "../lib/ui.js";
8
+ import { opencodePluginDir, opencodeSkillDir, tilde } from "../lib/paths.js";
9
+ import { removeBashPermission, removePluginFromConfig } from "../lib/config.js";
10
+ function parseArgs(args) {
11
+ const names = [];
12
+ let force = false;
13
+ for (const a of args) {
14
+ if (a === "-y" || a === "--yes" || a === "--force")
15
+ force = true;
16
+ else if (a.startsWith("-"))
17
+ ui.warn(`unknown flag: ${a}`);
18
+ else
19
+ names.push(a);
20
+ }
21
+ return { force, names };
22
+ }
23
+ /** Plugins whose plugin file currently exists in the install target. */
24
+ function installedPluginNames() {
25
+ return discoverPlugins()
26
+ .filter((p) => {
27
+ if (!p.contract.opencodePlugin)
28
+ return false;
29
+ const target = join(opencodePluginDir(), basename(p.contract.opencodePlugin));
30
+ return existsSync(target);
31
+ })
32
+ .map((p) => p.name);
33
+ }
34
+ function uninstallOne(plugin) {
35
+ ui.info(ui.bold(`\n${plugin.name}`));
36
+ // 1. Remove plugin file
37
+ if (plugin.contract.opencodePlugin) {
38
+ const dest = join(opencodePluginDir(), basename(plugin.contract.opencodePlugin));
39
+ if (existsSync(dest)) {
40
+ rmSync(dest);
41
+ ui.info(` ${ui.ok("removed " + tilde(dest))}`);
42
+ }
43
+ else {
44
+ ui.info(` ${ui.dim("· plugin file not present: " + tilde(dest))}`);
45
+ }
46
+ }
47
+ // 2. Remove skill file (and its parent dir if empty)
48
+ if (plugin.contract.opencodeSkill) {
49
+ const parts = plugin.contract.opencodeSkill.split("/");
50
+ const skillIdx = parts.lastIndexOf("skill");
51
+ const rel = skillIdx >= 0 ? parts.slice(skillIdx + 1).join("/") : basename(plugin.contract.opencodeSkill);
52
+ const dest = join(opencodeSkillDir(), rel);
53
+ if (existsSync(dest)) {
54
+ rmSync(dest);
55
+ ui.info(` ${ui.ok("removed " + tilde(dest))}`);
56
+ }
57
+ else {
58
+ ui.info(` ${ui.dim("· skill file not present: " + tilde(dest))}`);
59
+ }
60
+ // Remove the skill directory if it's now empty (be conservative).
61
+ const parent = dirname(dest);
62
+ try {
63
+ if (existsSync(parent) && readdirSync(parent).length === 0) {
64
+ rmSync(parent, { recursive: true });
65
+ ui.info(` ${ui.dim("· removed empty dir " + tilde(parent))}`);
66
+ }
67
+ }
68
+ catch {
69
+ /* ignore */
70
+ }
71
+ }
72
+ // 3. Remove plugin[] entry
73
+ if (plugin.contract.opencodePlugin) {
74
+ const entry = `./plugin/${basename(plugin.contract.opencodePlugin)}`;
75
+ if (removePluginFromConfig(entry)) {
76
+ ui.info(` ${ui.ok(`removed ${entry} from plugin[]`)}`);
77
+ }
78
+ else {
79
+ ui.info(` ${ui.dim(`· ${entry} not in plugin[]`)}`);
80
+ }
81
+ }
82
+ // 4. Remove permission.bash pattern (only the plugin's own, never the catch-all)
83
+ if (plugin.contract.permission) {
84
+ if (removeBashPermission(plugin.contract.permission)) {
85
+ ui.info(` ${ui.ok(`removed bash.${plugin.contract.permission}`)}`);
86
+ }
87
+ else {
88
+ ui.info(` ${ui.dim(`· bash.${plugin.contract.permission} not set`)}`);
89
+ }
90
+ }
91
+ }
92
+ export async function uninstall(args) {
93
+ const opts = parseArgs(args);
94
+ const names = opts.names.length > 0 ? opts.names : installedPluginNames();
95
+ if (names.length === 0) {
96
+ ui.info("Nothing to uninstall (no installed plugins detected).");
97
+ return 0;
98
+ }
99
+ if (!opts.force) {
100
+ ui.info(`About to uninstall: ${names.join(", ")}`);
101
+ ui.info(ui.dim("This removes plugin files and config entries. " +
102
+ "Plugin dependencies (e.g. mmx-cli) and credentials are left untouched."));
103
+ const answer = await ui.promptVisible("Proceed? [y/N] ");
104
+ if (answer.toLowerCase() !== "y" && answer.toLowerCase() !== "yes") {
105
+ ui.info("Cancelled.");
106
+ return 0;
107
+ }
108
+ }
109
+ for (const name of names) {
110
+ try {
111
+ uninstallOne(getPlugin(name));
112
+ }
113
+ catch (err) {
114
+ ui.error(`${name}: ${err.message}`);
115
+ return 1;
116
+ }
117
+ }
118
+ ui.info(ui.green(`\n✓ Uninstalled ${names.length} plugin${names.length === 1 ? "" : "s"}.`));
119
+ return 0;
120
+ }
121
+ //# sourceMappingURL=uninstall.js.map