grepmax 0.11.2 → 0.12.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/dist/commands/opencode.js +116 -124
- package/dist/commands/plugin.js +259 -0
- package/dist/commands/setup.js +9 -0
- package/dist/index.js +8 -1
- package/package.json +1 -1
- package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
- package/scripts/postinstall.js +12 -0
|
@@ -19,14 +19,14 @@ const node_os_1 = __importDefault(require("node:os"));
|
|
|
19
19
|
const node_path_1 = __importDefault(require("node:path"));
|
|
20
20
|
const commander_1 = require("commander");
|
|
21
21
|
const TOOL_PATH = node_path_1.default.join(node_os_1.default.homedir(), ".config", "opencode", "tool", "gmax.ts");
|
|
22
|
-
const PLUGIN_PATH = node_path_1.default.join(node_os_1.default.homedir(), ".config", "opencode", "
|
|
22
|
+
const PLUGIN_PATH = node_path_1.default.join(node_os_1.default.homedir(), ".config", "opencode", "plugins", "gmax.ts");
|
|
23
|
+
const LEGACY_PLUGIN_PATH = node_path_1.default.join(node_os_1.default.homedir(), ".config", "opencode", "plugin", "gmax.ts");
|
|
23
24
|
const CONFIG_PATH = node_path_1.default.join(node_os_1.default.homedir(), ".config", "opencode", "opencode.json");
|
|
24
25
|
function resolveGmaxBin() {
|
|
25
26
|
try {
|
|
26
27
|
return (0, node_child_process_1.execSync)("which gmax", { encoding: "utf-8" }).trim();
|
|
27
28
|
}
|
|
28
29
|
catch (_a) {
|
|
29
|
-
// Fall back to the path of the current process entry point
|
|
30
30
|
const binDir = node_path_1.default.dirname(process.argv[1]);
|
|
31
31
|
const candidate = node_path_1.default.join(binDir, "gmax");
|
|
32
32
|
if (node_fs_1.default.existsSync(candidate))
|
|
@@ -37,104 +37,44 @@ function resolveGmaxBin() {
|
|
|
37
37
|
function buildShimContent(gmaxBin) {
|
|
38
38
|
return `
|
|
39
39
|
import { tool } from "@opencode-ai/plugin";
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
export async function handleAuth(req: Request) {
|
|
68
|
-
const token = req.headers.get("Authorization");
|
|
69
|
-
const claims = await validateToken(token);
|
|
70
|
-
if (!claims) return unauthorized();
|
|
71
|
-
const allowed = await checkRole(claims.role, req.path);
|
|
72
|
-
...
|
|
73
|
-
|
|
74
|
-
- **ORCHESTRATION** = contains logic, coordinates other code
|
|
75
|
-
- **DEFINITION** = types, interfaces, classes
|
|
76
|
-
- **Score** = relevance (1 = best match)
|
|
77
|
-
- **Calls** = what this code calls (helps you trace flow)
|
|
78
|
-
|
|
79
|
-
## When to Read more
|
|
80
|
-
|
|
81
|
-
The snippet often has enough context. But if you need more:
|
|
82
|
-
|
|
83
|
-
# gmax found src/auth/handler.ts:45-90 as ORCH
|
|
84
|
-
Read src/auth/handler.ts:45-120
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
Read the specific line range, not the whole file.
|
|
88
|
-
|
|
89
|
-
## Other commands
|
|
90
|
-
|
|
91
|
-
# Trace call graph (who calls X, what X calls)
|
|
92
|
-
gmax trace handleAuth
|
|
93
|
-
|
|
94
|
-
# Skeleton of a huge file (to find which ranges to read)
|
|
95
|
-
gmax skeleton src/giant-2000-line-file.ts
|
|
96
|
-
|
|
97
|
-
# Just file paths when you only need locations
|
|
98
|
-
gmax "authentication" --compact
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
## Workflow: architecture questions
|
|
102
|
-
|
|
103
|
-
# 1. Find entry points
|
|
104
|
-
gmax "where do requests enter the server"
|
|
105
|
-
# Review the ORCH results - code is shown
|
|
106
|
-
|
|
107
|
-
# 2. If you need deeper context on a specific function
|
|
108
|
-
Read src/server/handler.ts:45-120
|
|
109
|
-
|
|
110
|
-
# 3. Trace to understand call flow
|
|
111
|
-
gmax trace handleRequest
|
|
112
|
-
|
|
113
|
-
## Tips
|
|
114
|
-
|
|
115
|
-
- More words = better results. "auth" is vague. "where does the server validate JWT tokens" is specific.
|
|
116
|
-
- ORCH results contain the logic - prioritize these
|
|
117
|
-
- Don't read entire files. Use the line ranges gmax gives you.
|
|
118
|
-
- If results seem off, rephrase your query like you'd ask a teammate
|
|
119
|
-
|
|
120
|
-
## If Index is Building
|
|
121
|
-
|
|
122
|
-
If you see "Indexing" or "Syncing": STOP. Tell the user the index is building. Ask if they want to wait or proceed with partial results.
|
|
123
|
-
|
|
124
|
-
\`;
|
|
125
|
-
|
|
126
|
-
const GMAX_BIN = "${gmaxBin}";
|
|
40
|
+
import { existsSync, realpathSync, readFileSync } from "node:fs";
|
|
41
|
+
import { resolve, dirname, join } from "node:path";
|
|
42
|
+
import { execFileSync } from "node:child_process";
|
|
43
|
+
|
|
44
|
+
// Resolve gmax binary and SKILL dynamically from the installed package.
|
|
45
|
+
// This means npm install -g grepmax@latest automatically updates the SKILL
|
|
46
|
+
// without needing to re-run gmax install-opencode.
|
|
47
|
+
const _gmax = (() => {
|
|
48
|
+
// Binary: try hardcoded path, fall back to which
|
|
49
|
+
let bin = "${gmaxBin}";
|
|
50
|
+
if (!existsSync(bin)) {
|
|
51
|
+
try {
|
|
52
|
+
bin = execFileSync("which gmax", { encoding: "utf-8" }).trim();
|
|
53
|
+
} catch {
|
|
54
|
+
return { bin: "gmax", skill: "Semantic code search. Run: gmax 'query' --agent" };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// SKILL: read from package root
|
|
59
|
+
try {
|
|
60
|
+
const root = resolve(dirname(realpathSync(bin)), "..");
|
|
61
|
+
const skillPath = join(root, "plugins", "grepmax", "skills", "grepmax", "SKILL.md");
|
|
62
|
+
return { bin, skill: readFileSync(skillPath, "utf-8") };
|
|
63
|
+
} catch {
|
|
64
|
+
return { bin, skill: "Semantic code search. Run: gmax 'query' --agent" };
|
|
65
|
+
}
|
|
66
|
+
})();
|
|
127
67
|
|
|
128
68
|
export default tool({
|
|
129
|
-
description:
|
|
69
|
+
description: _gmax.skill,
|
|
130
70
|
args: {
|
|
131
71
|
argv: tool.schema.array(tool.schema.string())
|
|
132
|
-
.describe("Arguments for gmax, e.g. ['search', 'user auth']")
|
|
72
|
+
.describe("Arguments for gmax, e.g. ['search', 'user auth', '--agent']")
|
|
133
73
|
},
|
|
134
74
|
async execute({ argv }) {
|
|
135
75
|
try {
|
|
136
76
|
// @ts-ignore
|
|
137
|
-
const out = await Bun.spawn([
|
|
77
|
+
const out = await Bun.spawn([_gmax.bin, ...argv], { stdout: "pipe" }).stdout;
|
|
138
78
|
const text = await new Response(out).text();
|
|
139
79
|
if (text.includes("Indexing") || text.includes("Building") || text.includes("Syncing")) {
|
|
140
80
|
return \`WARN: The index is currently updating.
|
|
@@ -142,7 +82,7 @@ export default tool({
|
|
|
142
82
|
Output so far:
|
|
143
83
|
\${text.trim()}
|
|
144
84
|
|
|
145
|
-
|
|
85
|
+
Please wait for indexing to complete before searching.\`;
|
|
146
86
|
}
|
|
147
87
|
return text.trim();
|
|
148
88
|
} catch (err) {
|
|
@@ -151,43 +91,90 @@ PLEASE READ THE "Indexing" WARNING IN MY SKILL DESCRIPTION.\`;
|
|
|
151
91
|
},
|
|
152
92
|
})`;
|
|
153
93
|
}
|
|
94
|
+
function buildPluginContent(gmaxBin) {
|
|
95
|
+
return `import { existsSync, readFileSync, realpathSync } from "node:fs";
|
|
96
|
+
import { execFileSync } from "node:child_process";
|
|
97
|
+
import { join, resolve, dirname } from "node:path";
|
|
98
|
+
import { homedir } from "node:os";
|
|
99
|
+
|
|
100
|
+
function resolveGmaxBin() {
|
|
101
|
+
const hardcoded = "${gmaxBin}";
|
|
102
|
+
if (existsSync(hardcoded)) return hardcoded;
|
|
103
|
+
try {
|
|
104
|
+
return execFileSync("which gmax", { encoding: "utf-8" }).trim();
|
|
105
|
+
} catch {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function isProjectRegistered() {
|
|
111
|
+
try {
|
|
112
|
+
const projectsPath = join(homedir(), ".gmax", "projects.json");
|
|
113
|
+
const projects = JSON.parse(readFileSync(projectsPath, "utf-8"));
|
|
114
|
+
const cwd = process.cwd();
|
|
115
|
+
return projects.some((p) => cwd.startsWith(p.root));
|
|
116
|
+
} catch {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function startDaemon() {
|
|
122
|
+
const bin = resolveGmaxBin();
|
|
123
|
+
if (!bin || !isProjectRegistered()) return;
|
|
124
|
+
try {
|
|
125
|
+
execFileSync(bin, ["watch", "--daemon", "-b"], {
|
|
126
|
+
timeout: 5000,
|
|
127
|
+
stdio: "ignore",
|
|
128
|
+
});
|
|
129
|
+
} catch {}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export const GmaxPlugin = async () => {
|
|
133
|
+
startDaemon();
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
"session.created": async () => {
|
|
137
|
+
startDaemon();
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
};
|
|
141
|
+
`;
|
|
142
|
+
}
|
|
154
143
|
function install() {
|
|
155
144
|
return __awaiter(this, void 0, void 0, function* () {
|
|
145
|
+
var _a;
|
|
156
146
|
try {
|
|
157
147
|
// 1. Delete legacy plugin
|
|
158
|
-
if (node_fs_1.default.existsSync(
|
|
148
|
+
if (node_fs_1.default.existsSync(LEGACY_PLUGIN_PATH)) {
|
|
159
149
|
try {
|
|
160
|
-
node_fs_1.default.unlinkSync(
|
|
161
|
-
console.log("Deleted legacy plugin at",
|
|
162
|
-
}
|
|
163
|
-
catch (e) {
|
|
164
|
-
console.warn("mnt: Failed to delete legacy plugin:", e);
|
|
150
|
+
node_fs_1.default.unlinkSync(LEGACY_PLUGIN_PATH);
|
|
151
|
+
console.log("Deleted legacy plugin at", LEGACY_PLUGIN_PATH);
|
|
165
152
|
}
|
|
153
|
+
catch (_b) { }
|
|
166
154
|
}
|
|
167
155
|
// 2. Resolve absolute path to gmax binary
|
|
168
156
|
const gmaxBin = resolveGmaxBin();
|
|
169
157
|
console.log(` Resolved gmax binary: ${gmaxBin}`);
|
|
170
|
-
// 3. Create tool shim
|
|
158
|
+
// 3. Create tool shim (reads SKILL dynamically from package root)
|
|
171
159
|
node_fs_1.default.mkdirSync(node_path_1.default.dirname(TOOL_PATH), { recursive: true });
|
|
172
160
|
node_fs_1.default.writeFileSync(TOOL_PATH, buildShimContent(gmaxBin));
|
|
173
161
|
console.log("✅ Created tool shim at", TOOL_PATH);
|
|
174
|
-
// 4.
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
162
|
+
// 4. Create plugin for daemon startup
|
|
163
|
+
node_fs_1.default.mkdirSync(node_path_1.default.dirname(PLUGIN_PATH), { recursive: true });
|
|
164
|
+
node_fs_1.default.writeFileSync(PLUGIN_PATH, buildPluginContent(gmaxBin));
|
|
165
|
+
console.log("✅ Created plugin at", PLUGIN_PATH);
|
|
166
|
+
// 5. Clean up stale MCP registration if present
|
|
167
|
+
if (node_fs_1.default.existsSync(CONFIG_PATH)) {
|
|
168
|
+
try {
|
|
169
|
+
const config = JSON.parse(node_fs_1.default.readFileSync(CONFIG_PATH, "utf-8") || "{}");
|
|
170
|
+
if ((_a = config.mcp) === null || _a === void 0 ? void 0 : _a.gmax) {
|
|
171
|
+
delete config.mcp.gmax;
|
|
172
|
+
node_fs_1.default.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
173
|
+
console.log("✅ Removed stale MCP registration from", CONFIG_PATH);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
catch (_c) { }
|
|
178
177
|
}
|
|
179
|
-
const config = JSON.parse(node_fs_1.default.readFileSync(CONFIG_PATH, "utf-8") || "{}");
|
|
180
|
-
if (!config.$schema)
|
|
181
|
-
config.$schema = "https://opencode.ai/config.json";
|
|
182
|
-
if (!config.mcp)
|
|
183
|
-
config.mcp = {};
|
|
184
|
-
config.mcp.gmax = {
|
|
185
|
-
type: "local",
|
|
186
|
-
command: [gmaxBin, "mcp"],
|
|
187
|
-
enabled: true,
|
|
188
|
-
};
|
|
189
|
-
node_fs_1.default.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
190
|
-
console.log("✅ Registered MCP server in", CONFIG_PATH);
|
|
191
178
|
}
|
|
192
179
|
catch (err) {
|
|
193
180
|
console.error("❌ Installation failed:", err);
|
|
@@ -198,25 +185,30 @@ function uninstall() {
|
|
|
198
185
|
return __awaiter(this, void 0, void 0, function* () {
|
|
199
186
|
var _a;
|
|
200
187
|
try {
|
|
201
|
-
// 1. Remove shim
|
|
188
|
+
// 1. Remove tool shim
|
|
202
189
|
if (node_fs_1.default.existsSync(TOOL_PATH)) {
|
|
203
190
|
node_fs_1.default.unlinkSync(TOOL_PATH);
|
|
204
191
|
console.log("✅ Removed tool shim.");
|
|
205
192
|
}
|
|
206
|
-
// 2.
|
|
193
|
+
// 2. Remove plugin
|
|
194
|
+
if (node_fs_1.default.existsSync(PLUGIN_PATH)) {
|
|
195
|
+
node_fs_1.default.unlinkSync(PLUGIN_PATH);
|
|
196
|
+
console.log("✅ Removed plugin.");
|
|
197
|
+
}
|
|
198
|
+
// 3. Clean up legacy paths
|
|
199
|
+
if (node_fs_1.default.existsSync(LEGACY_PLUGIN_PATH)) {
|
|
200
|
+
node_fs_1.default.unlinkSync(LEGACY_PLUGIN_PATH);
|
|
201
|
+
console.log("✅ Cleaned up legacy plugin.");
|
|
202
|
+
}
|
|
203
|
+
// 4. Clean up MCP registration if present
|
|
207
204
|
if (node_fs_1.default.existsSync(CONFIG_PATH)) {
|
|
208
205
|
const config = JSON.parse(node_fs_1.default.readFileSync(CONFIG_PATH, "utf-8") || "{}");
|
|
209
206
|
if ((_a = config.mcp) === null || _a === void 0 ? void 0 : _a.gmax) {
|
|
210
207
|
delete config.mcp.gmax;
|
|
211
208
|
node_fs_1.default.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
212
|
-
console.log("✅
|
|
209
|
+
console.log("✅ Removed MCP registration.");
|
|
213
210
|
}
|
|
214
211
|
}
|
|
215
|
-
// Cleanup plugin just in case
|
|
216
|
-
if (node_fs_1.default.existsSync(PLUGIN_PATH)) {
|
|
217
|
-
node_fs_1.default.unlinkSync(PLUGIN_PATH);
|
|
218
|
-
console.log("✅ Cleaned up plugin file.");
|
|
219
|
-
}
|
|
220
212
|
}
|
|
221
213
|
catch (err) {
|
|
222
214
|
console.error("❌ Uninstall failed:", err);
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
36
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
37
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
38
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
39
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
40
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
41
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.plugin = void 0;
|
|
46
|
+
const node_child_process_1 = require("node:child_process");
|
|
47
|
+
const fs = __importStar(require("node:fs"));
|
|
48
|
+
const os = __importStar(require("node:os"));
|
|
49
|
+
const path = __importStar(require("node:path"));
|
|
50
|
+
const commander_1 = require("commander");
|
|
51
|
+
const exit_1 = require("../lib/utils/exit");
|
|
52
|
+
function commandExists(cmd) {
|
|
53
|
+
try {
|
|
54
|
+
(0, node_child_process_1.execSync)(`which ${cmd}`, { encoding: "utf-8", stdio: "pipe" });
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
catch (_a) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function getClients() {
|
|
62
|
+
return [
|
|
63
|
+
{
|
|
64
|
+
name: "Claude Code",
|
|
65
|
+
id: "claude",
|
|
66
|
+
detect: () => commandExists("claude"),
|
|
67
|
+
isInstalled: () => fs.existsSync(path.join(os.homedir(), ".claude", "plugins", "cache", "grepmax", "grepmax")),
|
|
68
|
+
install: () => __awaiter(this, void 0, void 0, function* () {
|
|
69
|
+
const { installClaudeCode } = yield Promise.resolve().then(() => __importStar(require("./claude-code")));
|
|
70
|
+
yield installClaudeCode.parseAsync(["node", "gmax"]);
|
|
71
|
+
}),
|
|
72
|
+
uninstall: () => __awaiter(this, void 0, void 0, function* () {
|
|
73
|
+
const cacheBase = path.join(os.homedir(), ".claude", "plugins", "cache", "grepmax");
|
|
74
|
+
if (fs.existsSync(cacheBase)) {
|
|
75
|
+
fs.rmSync(cacheBase, { recursive: true, force: true });
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
const { spawn } = yield Promise.resolve().then(() => __importStar(require("node:child_process")));
|
|
79
|
+
yield new Promise((resolve) => {
|
|
80
|
+
const child = spawn("claude", ["plugin", "marketplace", "remove", "grepmax"], { stdio: "ignore" });
|
|
81
|
+
child.on("exit", () => resolve());
|
|
82
|
+
child.on("error", () => resolve());
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
catch (_a) { }
|
|
86
|
+
console.log("✅ Removed Claude Code plugin.");
|
|
87
|
+
}),
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: "OpenCode",
|
|
91
|
+
id: "opencode",
|
|
92
|
+
detect: () => commandExists("opencode"),
|
|
93
|
+
isInstalled: () => fs.existsSync(path.join(os.homedir(), ".config", "opencode", "tool", "gmax.ts")),
|
|
94
|
+
install: () => __awaiter(this, void 0, void 0, function* () {
|
|
95
|
+
const { installOpencode } = yield Promise.resolve().then(() => __importStar(require("./opencode")));
|
|
96
|
+
yield installOpencode.parseAsync(["node", "gmax"]);
|
|
97
|
+
}),
|
|
98
|
+
uninstall: () => __awaiter(this, void 0, void 0, function* () {
|
|
99
|
+
const { uninstallOpencode } = yield Promise.resolve().then(() => __importStar(require("./opencode")));
|
|
100
|
+
yield uninstallOpencode.parseAsync(["node", "gmax"]);
|
|
101
|
+
}),
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: "Codex",
|
|
105
|
+
id: "codex",
|
|
106
|
+
detect: () => commandExists("codex"),
|
|
107
|
+
isInstalled: () => {
|
|
108
|
+
const p = path.join(os.homedir(), ".codex", "AGENTS.md");
|
|
109
|
+
try {
|
|
110
|
+
return fs.existsSync(p) && fs.readFileSync(p, "utf-8").includes("name: gmax");
|
|
111
|
+
}
|
|
112
|
+
catch (_a) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
install: () => __awaiter(this, void 0, void 0, function* () {
|
|
117
|
+
const { installCodex } = yield Promise.resolve().then(() => __importStar(require("./codex")));
|
|
118
|
+
yield installCodex.parseAsync(["node", "gmax"]);
|
|
119
|
+
}),
|
|
120
|
+
uninstall: () => __awaiter(this, void 0, void 0, function* () {
|
|
121
|
+
const { uninstallCodex } = yield Promise.resolve().then(() => __importStar(require("./codex")));
|
|
122
|
+
yield uninstallCodex.parseAsync(["node", "gmax"]);
|
|
123
|
+
}),
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: "Factory Droid",
|
|
127
|
+
id: "droid",
|
|
128
|
+
detect: () => fs.existsSync(path.join(os.homedir(), ".factory")) &&
|
|
129
|
+
commandExists("droid"),
|
|
130
|
+
isInstalled: () => fs.existsSync(path.join(os.homedir(), ".factory", "skills", "gmax", "SKILL.md")),
|
|
131
|
+
install: () => __awaiter(this, void 0, void 0, function* () {
|
|
132
|
+
const { installDroid } = yield Promise.resolve().then(() => __importStar(require("./droid")));
|
|
133
|
+
yield installDroid.parseAsync(["node", "gmax"]);
|
|
134
|
+
}),
|
|
135
|
+
uninstall: () => __awaiter(this, void 0, void 0, function* () {
|
|
136
|
+
const { uninstallDroid } = yield Promise.resolve().then(() => __importStar(require("./droid")));
|
|
137
|
+
yield uninstallDroid.parseAsync(["node", "gmax"]);
|
|
138
|
+
}),
|
|
139
|
+
},
|
|
140
|
+
];
|
|
141
|
+
}
|
|
142
|
+
// --- Subcommands ---
|
|
143
|
+
const addCmd = new commander_1.Command("add")
|
|
144
|
+
.description("Install or update gmax plugins")
|
|
145
|
+
.argument("[client]", "Client to install (claude, opencode, codex, droid, all)")
|
|
146
|
+
.action((clientArg) => __awaiter(void 0, void 0, void 0, function* () {
|
|
147
|
+
const clients = getClients();
|
|
148
|
+
const onlyId = clientArg && clientArg !== "all" ? clientArg : undefined;
|
|
149
|
+
if (onlyId) {
|
|
150
|
+
const client = clients.find((c) => c.id === onlyId);
|
|
151
|
+
if (!client) {
|
|
152
|
+
console.error(`Unknown client: ${onlyId}`);
|
|
153
|
+
console.error(`Available: ${clients.map((c) => c.id).join(", ")}`);
|
|
154
|
+
yield (0, exit_1.gracefulExit)(1);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (!client.detect()) {
|
|
158
|
+
console.error(`${client.name} not found on this system`);
|
|
159
|
+
yield (0, exit_1.gracefulExit)(1);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
yield client.install();
|
|
163
|
+
yield (0, exit_1.gracefulExit)();
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
// Install all detected
|
|
167
|
+
console.log("gmax plugin add — detecting clients...\n");
|
|
168
|
+
let installed = 0;
|
|
169
|
+
for (const client of clients) {
|
|
170
|
+
if (!client.detect()) {
|
|
171
|
+
console.log(` skip ${client.name} — not found`);
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
try {
|
|
175
|
+
yield client.install();
|
|
176
|
+
installed++;
|
|
177
|
+
}
|
|
178
|
+
catch (err) {
|
|
179
|
+
console.error(` FAIL ${client.name} — ${err instanceof Error ? err.message : String(err)}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (installed === 0) {
|
|
183
|
+
console.log("\nNo supported clients found. Install one of: claude, opencode, codex, droid");
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
console.log(`\n${installed} plugin(s) installed.`);
|
|
187
|
+
}
|
|
188
|
+
yield (0, exit_1.gracefulExit)();
|
|
189
|
+
}));
|
|
190
|
+
const removeCmd = new commander_1.Command("remove")
|
|
191
|
+
.description("Remove gmax plugins")
|
|
192
|
+
.argument("[client]", "Client to remove (claude, opencode, codex, droid, all)")
|
|
193
|
+
.action((clientArg) => __awaiter(void 0, void 0, void 0, function* () {
|
|
194
|
+
const clients = getClients();
|
|
195
|
+
if (clientArg && clientArg !== "all") {
|
|
196
|
+
const client = clients.find((c) => c.id === clientArg);
|
|
197
|
+
if (!client) {
|
|
198
|
+
console.error(`Unknown client: ${clientArg}`);
|
|
199
|
+
console.error(`Available: ${clients.map((c) => c.id).join(", ")}`);
|
|
200
|
+
yield (0, exit_1.gracefulExit)(1);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
try {
|
|
204
|
+
yield client.uninstall();
|
|
205
|
+
}
|
|
206
|
+
catch (err) {
|
|
207
|
+
console.error(`Failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
208
|
+
}
|
|
209
|
+
yield (0, exit_1.gracefulExit)();
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
const installedClients = clients.filter((c) => c.isInstalled());
|
|
213
|
+
if (installedClients.length === 0) {
|
|
214
|
+
console.log("No gmax plugins currently installed.");
|
|
215
|
+
yield (0, exit_1.gracefulExit)();
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
// No arg or "all": remove all installed
|
|
219
|
+
for (const client of installedClients) {
|
|
220
|
+
try {
|
|
221
|
+
yield client.uninstall();
|
|
222
|
+
}
|
|
223
|
+
catch (err) {
|
|
224
|
+
console.error(` FAIL ${client.name} — ${err instanceof Error ? err.message : String(err)}`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
console.log(`\n${installedClients.length} plugin(s) removed.`);
|
|
228
|
+
yield (0, exit_1.gracefulExit)();
|
|
229
|
+
}));
|
|
230
|
+
// --- Status (default action for bare `gmax plugin`) ---
|
|
231
|
+
function statusAction() {
|
|
232
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
233
|
+
const clients = getClients();
|
|
234
|
+
console.log("gmax plugins\n");
|
|
235
|
+
for (const client of clients) {
|
|
236
|
+
const detected = client.detect();
|
|
237
|
+
const installed = client.isInstalled();
|
|
238
|
+
let status;
|
|
239
|
+
if (installed)
|
|
240
|
+
status = "✅ installed";
|
|
241
|
+
else if (detected)
|
|
242
|
+
status = "— not installed";
|
|
243
|
+
else
|
|
244
|
+
status = "— not found";
|
|
245
|
+
console.log(` ${client.name.padEnd(16)} ${status}`);
|
|
246
|
+
}
|
|
247
|
+
console.log("\nCommands:");
|
|
248
|
+
console.log(" gmax plugin add Install all detected clients");
|
|
249
|
+
console.log(" gmax plugin add <client> Install a specific client");
|
|
250
|
+
console.log(" gmax plugin remove Remove all installed plugins");
|
|
251
|
+
console.log(" gmax plugin remove <client> Remove a specific plugin");
|
|
252
|
+
yield (0, exit_1.gracefulExit)();
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
exports.plugin = new commander_1.Command("plugin")
|
|
256
|
+
.description("Manage gmax plugins for AI coding clients")
|
|
257
|
+
.action(statusAction)
|
|
258
|
+
.addCommand(addCmd)
|
|
259
|
+
.addCommand(removeCmd);
|
package/dist/commands/setup.js
CHANGED
|
@@ -148,6 +148,15 @@ exports.setup = new commander_1.Command("setup")
|
|
|
148
148
|
p.log.warn("Embedding mode changed. Run `gmax serve` to apply the new settings.");
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
|
+
// Step 8: Install plugins for detected clients
|
|
152
|
+
const installPlugins = yield p.confirm({
|
|
153
|
+
message: "Install plugins for detected clients?",
|
|
154
|
+
initialValue: true,
|
|
155
|
+
});
|
|
156
|
+
if (!p.isCancel(installPlugins) && installPlugins) {
|
|
157
|
+
const { plugin: pluginCmd } = yield Promise.resolve().then(() => __importStar(require("./plugin")));
|
|
158
|
+
yield pluginCmd.parseAsync(["node", "gmax"]);
|
|
159
|
+
}
|
|
151
160
|
p.outro(`Ready — ${selectedTier.label}, ${embedMode === "gpu" ? "GPU" : "CPU"} mode`);
|
|
152
161
|
yield (0, exit_1.gracefulExit)();
|
|
153
162
|
}));
|
package/dist/index.js
CHANGED
|
@@ -55,6 +55,7 @@ const project_1 = require("./commands/project");
|
|
|
55
55
|
const recent_1 = require("./commands/recent");
|
|
56
56
|
const related_1 = require("./commands/related");
|
|
57
57
|
const opencode_1 = require("./commands/opencode");
|
|
58
|
+
const plugin_1 = require("./commands/plugin");
|
|
58
59
|
const remove_1 = require("./commands/remove");
|
|
59
60
|
const search_1 = require("./commands/search");
|
|
60
61
|
const similar_1 = require("./commands/similar");
|
|
@@ -112,12 +113,18 @@ commander_1.program.addCommand(summarize_1.summarize);
|
|
|
112
113
|
commander_1.program.addCommand(setup_1.setup);
|
|
113
114
|
commander_1.program.addCommand(config_1.config);
|
|
114
115
|
commander_1.program.addCommand(doctor_1.doctor);
|
|
115
|
-
//
|
|
116
|
+
// Plugins
|
|
117
|
+
commander_1.program.addCommand(plugin_1.plugin);
|
|
118
|
+
// Legacy plugin installers (hidden — use `gmax plugin` instead)
|
|
119
|
+
claude_code_1.installClaudeCode._hidden = true;
|
|
116
120
|
commander_1.program.addCommand(claude_code_1.installClaudeCode);
|
|
121
|
+
codex_1.installCodex._hidden = true;
|
|
117
122
|
commander_1.program.addCommand(codex_1.installCodex);
|
|
123
|
+
droid_1.installDroid._hidden = true;
|
|
118
124
|
commander_1.program.addCommand(droid_1.installDroid);
|
|
119
125
|
droid_1.uninstallDroid._hidden = true;
|
|
120
126
|
commander_1.program.addCommand(droid_1.uninstallDroid);
|
|
127
|
+
opencode_1.installOpencode._hidden = true;
|
|
121
128
|
commander_1.program.addCommand(opencode_1.installOpencode);
|
|
122
129
|
opencode_1.uninstallOpencode._hidden = true;
|
|
123
130
|
commander_1.program.addCommand(opencode_1.uninstallOpencode);
|
package/package.json
CHANGED
package/scripts/postinstall.js
CHANGED
|
@@ -64,3 +64,15 @@ for (const ver of versionDirs) {
|
|
|
64
64
|
// Best-effort — don't fail the install
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
|
+
|
|
68
|
+
// Sync OpenCode: re-run installer if tool shim or plugin exists
|
|
69
|
+
const ocToolPath = path.join(os.homedir(), ".config", "opencode", "tool", "gmax.ts");
|
|
70
|
+
const ocPluginPath = path.join(os.homedir(), ".config", "opencode", "plugins", "gmax.ts");
|
|
71
|
+
if (fs.existsSync(ocToolPath) || fs.existsSync(ocPluginPath)) {
|
|
72
|
+
try {
|
|
73
|
+
const { execSync: exec } = require("node:child_process");
|
|
74
|
+
exec("gmax install-opencode", { stdio: "ignore", timeout: 10000 });
|
|
75
|
+
} catch {
|
|
76
|
+
// Best-effort — don't fail the install
|
|
77
|
+
}
|
|
78
|
+
}
|