grepmax 0.12.2 → 0.12.4
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/codex.js +56 -41
- package/dist/commands/droid.js +73 -70
- package/dist/commands/plugin.js +1 -1
- package/dist/commands/status.js +26 -5
- package/dist/lib/search/searcher.js +12 -1
- package/dist/lib/store/vector-db.js +23 -4
- package/package.json +1 -1
- package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
- package/scripts/postinstall.js +99 -56
package/dist/commands/codex.js
CHANGED
|
@@ -21,46 +21,62 @@ const node_util_1 = require("node:util");
|
|
|
21
21
|
const commander_1 = require("commander");
|
|
22
22
|
const shell = process.env.SHELL || (process.platform === "win32" ? "cmd.exe" : "/bin/sh");
|
|
23
23
|
const execAsync = (0, node_util_1.promisify)(node_child_process_1.exec);
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
24
|
+
const AGENTS_PATH = node_path_1.default.join(node_os_1.default.homedir(), ".codex", "AGENTS.md");
|
|
25
|
+
const SKILL_START = "<!-- gmax:start -->";
|
|
26
|
+
const SKILL_END = "<!-- gmax:end -->";
|
|
27
|
+
function getPackageRoot() {
|
|
28
|
+
return node_path_1.default.resolve(__dirname, "../..");
|
|
29
|
+
}
|
|
30
|
+
function loadSkill() {
|
|
31
|
+
const skillPath = node_path_1.default.join(getPackageRoot(), "plugins", "grepmax", "skills", "grepmax", "SKILL.md");
|
|
32
|
+
try {
|
|
33
|
+
return node_fs_1.default.readFileSync(skillPath, "utf-8");
|
|
34
|
+
}
|
|
35
|
+
catch (_a) {
|
|
36
|
+
return [
|
|
37
|
+
"---",
|
|
38
|
+
"name: gmax",
|
|
39
|
+
"description: Semantic code search. Use alongside grep - grep for exact strings, gmax for concepts.",
|
|
40
|
+
"---",
|
|
41
|
+
"",
|
|
42
|
+
'Use `gmax "query" --agent` for semantic search.',
|
|
43
|
+
].join("\n");
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function writeSkillToAgents(skill) {
|
|
47
|
+
node_fs_1.default.mkdirSync(node_path_1.default.dirname(AGENTS_PATH), { recursive: true });
|
|
48
|
+
const block = `${SKILL_START}\n${skill.trim()}\n${SKILL_END}`;
|
|
49
|
+
if (!node_fs_1.default.existsSync(AGENTS_PATH)) {
|
|
50
|
+
node_fs_1.default.writeFileSync(AGENTS_PATH, block);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const content = node_fs_1.default.readFileSync(AGENTS_PATH, "utf-8");
|
|
54
|
+
// Check if file has any gmax content (markers or legacy)
|
|
55
|
+
if (content.includes("gmax")) {
|
|
56
|
+
// Remove all gmax content and rewrite with just our block
|
|
57
|
+
const markerRe = new RegExp(`\n?${SKILL_START}[\\s\\S]*?${SKILL_END}\n?`, "g");
|
|
58
|
+
const cleaned = content.replace(markerRe, "");
|
|
59
|
+
// Remove legacy content (everything between --- blocks mentioning gmax)
|
|
60
|
+
const withoutLegacy = cleaned.replace(/---[\s\S]*?(?:gmax|--compact)[\s\S]*?(?=\n<!-- |$)/, "").trim();
|
|
61
|
+
node_fs_1.default.writeFileSync(AGENTS_PATH, withoutLegacy ? `${withoutLegacy}\n\n${block}` : block);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
node_fs_1.default.writeFileSync(AGENTS_PATH, `${content.trim()}\n\n${block}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
40
67
|
function installPlugin() {
|
|
41
68
|
return __awaiter(this, void 0, void 0, function* () {
|
|
42
69
|
try {
|
|
43
|
-
// 1. Register
|
|
44
|
-
// 'gmax mcp' acts as the server.
|
|
70
|
+
// 1. Register MCP tool
|
|
45
71
|
yield execAsync("codex mcp add gmax gmax mcp", {
|
|
46
72
|
shell,
|
|
47
73
|
env: process.env,
|
|
48
74
|
});
|
|
49
75
|
console.log("✅ gmax MCP tool registered with Codex");
|
|
50
|
-
// 2.
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
? node_fs_1.default.readFileSync(destPath, "utf-8")
|
|
55
|
-
: "";
|
|
56
|
-
// Only append if not present
|
|
57
|
-
if (!content.includes("name: gmax")) {
|
|
58
|
-
node_fs_1.default.appendFileSync(destPath, `\n${SKILL}`);
|
|
59
|
-
console.log("✅ gmax skill instructions added to Codex");
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
console.log("ℹ️ gmax skill instructions already present");
|
|
63
|
-
}
|
|
76
|
+
// 2. Write SKILL to AGENTS.md (idempotent)
|
|
77
|
+
const skill = loadSkill();
|
|
78
|
+
writeSkillToAgents(skill);
|
|
79
|
+
console.log("✅ gmax skill instructions written to", AGENTS_PATH);
|
|
64
80
|
}
|
|
65
81
|
catch (error) {
|
|
66
82
|
console.error(`❌ Error installing Codex plugin: ${error}`);
|
|
@@ -74,17 +90,16 @@ function uninstallPlugin() {
|
|
|
74
90
|
yield execAsync("codex mcp remove gmax", { shell, env: process.env });
|
|
75
91
|
console.log("✅ gmax MCP tool removed");
|
|
76
92
|
}
|
|
77
|
-
catch (
|
|
93
|
+
catch (_a) {
|
|
78
94
|
/* ignore if not found */
|
|
79
95
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
node_fs_1.default.writeFileSync(destPath, content);
|
|
96
|
+
if (node_fs_1.default.existsSync(AGENTS_PATH)) {
|
|
97
|
+
let content = node_fs_1.default.readFileSync(AGENTS_PATH, "utf-8");
|
|
98
|
+
// Remove marked block
|
|
99
|
+
const markerRe = new RegExp(`\n?${SKILL_START}[\\s\\S]*?${SKILL_END}\n?`, "g");
|
|
100
|
+
if (markerRe.test(content)) {
|
|
101
|
+
content = content.replace(markerRe, "").trim();
|
|
102
|
+
node_fs_1.default.writeFileSync(AGENTS_PATH, content || "");
|
|
88
103
|
console.log("✅ gmax instructions removed from AGENTS.md");
|
|
89
104
|
}
|
|
90
105
|
}
|
package/dist/commands/droid.js
CHANGED
|
@@ -13,57 +13,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.uninstallDroid = exports.installDroid = void 0;
|
|
16
|
+
const node_child_process_1 = require("node:child_process");
|
|
16
17
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
17
18
|
const node_os_1 = __importDefault(require("node:os"));
|
|
18
19
|
const node_path_1 = __importDefault(require("node:path"));
|
|
19
20
|
const commander_1 = require("commander");
|
|
20
|
-
const SKILL = `
|
|
21
|
-
---
|
|
22
|
-
name: gmax
|
|
23
|
-
description: Semantic code search and call-graph tracing for AI agents. Finds code by concept, surfaces roles (ORCHESTRATION vs DEFINITION), and traces dependencies.
|
|
24
|
-
allowed-tools: "Bash(gmax:*), Read"
|
|
25
|
-
license: Apache-2.0
|
|
26
|
-
---
|
|
27
|
-
|
|
28
|
-
## ⚠️ CRITICAL: Handling "Indexing" State
|
|
29
|
-
If any \`gmax\` command returns a status indicating **"Indexing"**, **"Building"**, or **"Syncing"**:
|
|
30
|
-
1. **STOP** your current train of thought.
|
|
31
|
-
2. **INFORM** the user: "The semantic index is currently building. Search results will be incomplete."
|
|
32
|
-
3. **ASK**: "Do you want me to proceed with partial results, or wait for indexing to finish?"
|
|
33
|
-
*(Do not assume you should proceed without confirmation).*
|
|
34
|
-
|
|
35
|
-
## Core Commands
|
|
36
|
-
- Search: \`gmax "how does auth work" --compact\`
|
|
37
|
-
- Trace: \`gmax trace "AuthService"\`
|
|
38
|
-
- Symbols: \`gmax symbols "Auth"\`
|
|
39
|
-
|
|
40
|
-
## Output (Default = Compact TSV)
|
|
41
|
-
- One line per hit: \`path\\tlines\\tscore\\trole\\tconf\\tdefined\\tpreview\`
|
|
42
|
-
- Roles: \`ORCH\` (Orchestration), \`DEF\` (Definition), \`IMPL\` (Implementation).
|
|
43
|
-
- **Note:** If output is empty but valid, say "No semantic matches found."
|
|
44
|
-
|
|
45
|
-
## Typical Workflow
|
|
46
|
-
|
|
47
|
-
1. **Discover**
|
|
48
|
-
\`\`\`bash
|
|
49
|
-
gmax "worker pool lifecycle" --compact
|
|
50
|
-
\`\`\`
|
|
51
|
-
|
|
52
|
-
2. **Explore**
|
|
53
|
-
\`\`\`bash
|
|
54
|
-
gmax symbols Worker
|
|
55
|
-
\`\`\`
|
|
56
|
-
|
|
57
|
-
3. **Trace**
|
|
58
|
-
\`\`\`bash
|
|
59
|
-
gmax trace WorkerPool
|
|
60
|
-
\`\`\`
|
|
61
|
-
|
|
62
|
-
4. **Read**
|
|
63
|
-
\`\`\`bash
|
|
64
|
-
Read src/lib/workers/pool.ts:112-186
|
|
65
|
-
\`\`\`
|
|
66
|
-
`;
|
|
67
21
|
function resolveDroidRoot() {
|
|
68
22
|
const root = node_path_1.default.join(node_os_1.default.homedir(), ".factory");
|
|
69
23
|
if (!node_fs_1.default.existsSync(root)) {
|
|
@@ -71,6 +25,37 @@ function resolveDroidRoot() {
|
|
|
71
25
|
}
|
|
72
26
|
return root;
|
|
73
27
|
}
|
|
28
|
+
function resolveGmaxBin() {
|
|
29
|
+
try {
|
|
30
|
+
return (0, node_child_process_1.execSync)("which gmax", { encoding: "utf-8", stdio: "pipe" }).trim();
|
|
31
|
+
}
|
|
32
|
+
catch (_a) {
|
|
33
|
+
const binDir = node_path_1.default.dirname(process.argv[1]);
|
|
34
|
+
const candidate = node_path_1.default.join(binDir, "gmax");
|
|
35
|
+
if (node_fs_1.default.existsSync(candidate))
|
|
36
|
+
return candidate;
|
|
37
|
+
return "gmax";
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function getPackageRoot() {
|
|
41
|
+
return node_path_1.default.resolve(__dirname, "../..");
|
|
42
|
+
}
|
|
43
|
+
function loadSkill() {
|
|
44
|
+
const skillPath = node_path_1.default.join(getPackageRoot(), "plugins", "grepmax", "skills", "grepmax", "SKILL.md");
|
|
45
|
+
try {
|
|
46
|
+
return node_fs_1.default.readFileSync(skillPath, "utf-8");
|
|
47
|
+
}
|
|
48
|
+
catch (_a) {
|
|
49
|
+
return [
|
|
50
|
+
"---",
|
|
51
|
+
"name: gmax",
|
|
52
|
+
"description: Semantic code search.",
|
|
53
|
+
"---",
|
|
54
|
+
"",
|
|
55
|
+
'Use `gmax "query" --agent` for semantic search.',
|
|
56
|
+
].join("\n");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
74
59
|
function writeFileIfChanged(filePath, content) {
|
|
75
60
|
node_fs_1.default.mkdirSync(node_path_1.default.dirname(filePath), { recursive: true });
|
|
76
61
|
const already = node_fs_1.default.existsSync(filePath)
|
|
@@ -105,7 +90,6 @@ function mergeHooks(existing, incoming) {
|
|
|
105
90
|
for (const [event, entries] of Object.entries(incoming)) {
|
|
106
91
|
const current = merged[event] || [];
|
|
107
92
|
for (const entry of entries) {
|
|
108
|
-
// Simple duplicate check based on command string
|
|
109
93
|
const cmd = entry.hooks[0].command;
|
|
110
94
|
if (!current.some((c) => c.hooks[0].command === cmd)) {
|
|
111
95
|
current.push(entry);
|
|
@@ -115,37 +99,56 @@ function mergeHooks(existing, incoming) {
|
|
|
115
99
|
}
|
|
116
100
|
return merged;
|
|
117
101
|
}
|
|
118
|
-
// ---
|
|
102
|
+
// --- Installer ---
|
|
119
103
|
function installPlugin() {
|
|
120
104
|
return __awaiter(this, void 0, void 0, function* () {
|
|
121
105
|
const root = resolveDroidRoot();
|
|
106
|
+
const gmaxBin = resolveGmaxBin();
|
|
122
107
|
const hooksDir = node_path_1.default.join(root, "hooks", "gmax");
|
|
123
108
|
const skillsDir = node_path_1.default.join(root, "skills", "gmax");
|
|
124
109
|
const settingsPath = node_path_1.default.join(root, "settings.json");
|
|
125
|
-
// 1. Install
|
|
126
|
-
// We expect these files to exist in your dist/hooks folder
|
|
127
|
-
const startJsPath = node_path_1.default.join(hooksDir, "gmax_start.js");
|
|
128
|
-
const stopJsPath = node_path_1.default.join(hooksDir, "gmax_stop.js");
|
|
129
|
-
// Create these scripts dynamically if we don't want to read from dist
|
|
110
|
+
// 1. Install hook scripts (start/stop daemon)
|
|
130
111
|
const startScript = `
|
|
131
|
-
const {
|
|
112
|
+
const { execFileSync } = require("child_process");
|
|
132
113
|
const fs = require("fs");
|
|
133
114
|
const path = require("path");
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
115
|
+
const os = require("os");
|
|
116
|
+
|
|
117
|
+
const BIN = "${gmaxBin}";
|
|
118
|
+
|
|
119
|
+
function resolveGmax() {
|
|
120
|
+
if (fs.existsSync(BIN)) return BIN;
|
|
121
|
+
try {
|
|
122
|
+
return require("child_process").execSync("which gmax", { encoding: "utf-8" }).trim();
|
|
123
|
+
} catch { return null; }
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function isProjectRegistered() {
|
|
127
|
+
try {
|
|
128
|
+
const projectsPath = path.join(os.homedir(), ".gmax", "projects.json");
|
|
129
|
+
const projects = JSON.parse(fs.readFileSync(projectsPath, "utf-8"));
|
|
130
|
+
const cwd = process.cwd();
|
|
131
|
+
return projects.some((p) => cwd.startsWith(p.root));
|
|
132
|
+
} catch { return false; }
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const bin = resolveGmax();
|
|
136
|
+
if (bin && isProjectRegistered()) {
|
|
137
|
+
try { execFileSync(bin, ["watch", "--daemon", "-b"], { timeout: 5000, stdio: "ignore" }); } catch {}
|
|
138
|
+
}
|
|
139
|
+
`.trim();
|
|
140
140
|
const stopScript = `
|
|
141
|
-
const {
|
|
142
|
-
try {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
writeFileIfChanged(
|
|
148
|
-
//
|
|
141
|
+
const { execFileSync } = require("child_process");
|
|
142
|
+
try { execFileSync("gmax", ["watch", "stop", "--all"], { stdio: "ignore", timeout: 5000 }); } catch {}
|
|
143
|
+
`.trim();
|
|
144
|
+
const startJsPath = node_path_1.default.join(hooksDir, "gmax_start.js");
|
|
145
|
+
const stopJsPath = node_path_1.default.join(hooksDir, "gmax_stop.js");
|
|
146
|
+
writeFileIfChanged(startJsPath, startScript);
|
|
147
|
+
writeFileIfChanged(stopJsPath, stopScript);
|
|
148
|
+
// 2. Install SKILL (read from package root)
|
|
149
|
+
const skill = loadSkill();
|
|
150
|
+
writeFileIfChanged(node_path_1.default.join(skillsDir, "SKILL.md"), skill.trim());
|
|
151
|
+
// 3. Configure settings
|
|
149
152
|
const hookConfig = {
|
|
150
153
|
SessionStart: [
|
|
151
154
|
{
|
|
@@ -168,7 +171,7 @@ try { execSync("gmax serve stop", { stdio: "ignore" }); } catch {}
|
|
|
168
171
|
settings.allowBackgroundProcesses = true;
|
|
169
172
|
settings.hooks = mergeHooks(settings.hooks, hookConfig);
|
|
170
173
|
saveSettings(settingsPath, settings);
|
|
171
|
-
console.log(
|
|
174
|
+
console.log("✅ gmax installed for Factory Droid (Hooks + Skill)");
|
|
172
175
|
});
|
|
173
176
|
}
|
|
174
177
|
function uninstallPlugin() {
|
package/dist/commands/plugin.js
CHANGED
|
@@ -107,7 +107,7 @@ function getClients() {
|
|
|
107
107
|
isInstalled: () => {
|
|
108
108
|
const p = path.join(os.homedir(), ".codex", "AGENTS.md");
|
|
109
109
|
try {
|
|
110
|
-
return fs.existsSync(p) && fs.readFileSync(p, "utf-8").includes("
|
|
110
|
+
return fs.existsSync(p) && fs.readFileSync(p, "utf-8").includes("gmax");
|
|
111
111
|
}
|
|
112
112
|
catch (_a) {
|
|
113
113
|
return false;
|
package/dist/commands/status.js
CHANGED
|
@@ -45,13 +45,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
45
45
|
exports.status = void 0;
|
|
46
46
|
const os = __importStar(require("node:os"));
|
|
47
47
|
const commander_1 = require("commander");
|
|
48
|
+
const config_1 = require("../config");
|
|
48
49
|
const index_config_1 = require("../lib/index/index-config");
|
|
50
|
+
const filter_builder_1 = require("../lib/utils/filter-builder");
|
|
49
51
|
const exit_1 = require("../lib/utils/exit");
|
|
50
52
|
const lock_1 = require("../lib/utils/lock");
|
|
51
53
|
const project_registry_1 = require("../lib/utils/project-registry");
|
|
52
54
|
const project_root_1 = require("../lib/utils/project-root");
|
|
53
55
|
const watcher_store_1 = require("../lib/utils/watcher-store");
|
|
54
|
-
const config_1 = require("../config");
|
|
55
56
|
const style = {
|
|
56
57
|
bold: (s) => `\x1b[1m${s}\x1b[22m`,
|
|
57
58
|
dim: (s) => `\x1b[2m${s}\x1b[22m`,
|
|
@@ -97,7 +98,7 @@ Examples:
|
|
|
97
98
|
gmax status Show status of all indexed projects
|
|
98
99
|
`)
|
|
99
100
|
.action((opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
100
|
-
var _a, _b;
|
|
101
|
+
var _a, _b, _c, _d;
|
|
101
102
|
const globalConfig = (0, index_config_1.readGlobalConfig)();
|
|
102
103
|
const projects = (0, project_registry_1.listProjects)();
|
|
103
104
|
(0, watcher_store_1.listWatchers)(); // cleans stale entries as side effect
|
|
@@ -107,6 +108,24 @@ Examples:
|
|
|
107
108
|
// Header
|
|
108
109
|
console.log(`\n${style.bold("gmax")} · ${globalConfig.modelTier} (${globalConfig.vectorDim}d, ${globalConfig.embedMode})${indexing ? style.yellow(" · indexing...") : ""}`);
|
|
109
110
|
}
|
|
111
|
+
// Query live chunk counts from LanceDB
|
|
112
|
+
const chunkCounts = new Map();
|
|
113
|
+
try {
|
|
114
|
+
const { VectorDB } = yield Promise.resolve().then(() => __importStar(require("../lib/store/vector-db")));
|
|
115
|
+
const db = new VectorDB(config_1.PATHS.lancedbDir);
|
|
116
|
+
const table = yield db.ensureTable();
|
|
117
|
+
for (const project of projects) {
|
|
118
|
+
const prefix = project.root.endsWith("/") ? project.root : `${project.root}/`;
|
|
119
|
+
const rows = yield table
|
|
120
|
+
.query()
|
|
121
|
+
.select(["id"])
|
|
122
|
+
.where(`path LIKE '${(0, filter_builder_1.escapeSqlString)(prefix)}%'`)
|
|
123
|
+
.toArray();
|
|
124
|
+
chunkCounts.set(project.root, rows.length);
|
|
125
|
+
}
|
|
126
|
+
yield db.close();
|
|
127
|
+
}
|
|
128
|
+
catch (_e) { }
|
|
110
129
|
if (projects.length === 0) {
|
|
111
130
|
if (opts.agent) {
|
|
112
131
|
console.log("(none)");
|
|
@@ -133,7 +152,8 @@ Examples:
|
|
|
133
152
|
else
|
|
134
153
|
st = "idle";
|
|
135
154
|
const isCurrent = project.root === currentRoot;
|
|
136
|
-
|
|
155
|
+
const count = (_b = chunkCounts.get(project.root)) !== null && _b !== void 0 ? _b : project.chunkCount;
|
|
156
|
+
console.log(`${project.name}\t${formatChunks(count)}\t${formatAge(project.lastIndexed)}\t${st}${isCurrent ? "\tcurrent" : ""}`);
|
|
137
157
|
}
|
|
138
158
|
yield (0, exit_1.gracefulExit)();
|
|
139
159
|
return;
|
|
@@ -146,7 +166,7 @@ Examples:
|
|
|
146
166
|
const watcher = (0, watcher_store_1.getWatcherForProject)(project.root);
|
|
147
167
|
// Status column
|
|
148
168
|
let statusStr;
|
|
149
|
-
const projectStatus = (
|
|
169
|
+
const projectStatus = (_c = project.status) !== null && _c !== void 0 ? _c : "indexed";
|
|
150
170
|
if (projectStatus === "pending") {
|
|
151
171
|
statusStr = style.yellow("pending");
|
|
152
172
|
}
|
|
@@ -163,7 +183,8 @@ Examples:
|
|
|
163
183
|
statusStr = style.dim("idle");
|
|
164
184
|
}
|
|
165
185
|
// Chunks column
|
|
166
|
-
const
|
|
186
|
+
const count = (_d = chunkCounts.get(project.root)) !== null && _d !== void 0 ? _d : project.chunkCount;
|
|
187
|
+
const chunks = `${formatChunks(count)} chunks`;
|
|
167
188
|
// Age column
|
|
168
189
|
const age = formatAge(project.lastIndexed);
|
|
169
190
|
// Current marker
|
|
@@ -394,7 +394,18 @@ class Searcher {
|
|
|
394
394
|
ftsSearchFailed = true;
|
|
395
395
|
this.ftsAvailable = false;
|
|
396
396
|
const msg = e instanceof Error ? e.message : String(e);
|
|
397
|
-
|
|
397
|
+
if (msg.includes("position")) {
|
|
398
|
+
// FTS index lacks positional data — rebuild it
|
|
399
|
+
try {
|
|
400
|
+
yield this.db.createFTSIndex(true);
|
|
401
|
+
this.ftsAvailable = true;
|
|
402
|
+
console.warn("[Searcher] Rebuilt FTS index with position support — retry search");
|
|
403
|
+
}
|
|
404
|
+
catch (_k) { }
|
|
405
|
+
}
|
|
406
|
+
else {
|
|
407
|
+
console.warn(`[Searcher] FTS search failed (will retry later): ${msg}`);
|
|
408
|
+
}
|
|
398
409
|
}
|
|
399
410
|
}
|
|
400
411
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
@@ -260,18 +260,37 @@ class VectorDB {
|
|
|
260
260
|
});
|
|
261
261
|
}
|
|
262
262
|
createFTSIndex() {
|
|
263
|
-
return __awaiter(this,
|
|
263
|
+
return __awaiter(this, arguments, void 0, function* (rebuild = false) {
|
|
264
264
|
const table = yield this.ensureTable();
|
|
265
|
+
if (rebuild) {
|
|
266
|
+
try {
|
|
267
|
+
yield table.dropIndex("content_idx");
|
|
268
|
+
}
|
|
269
|
+
catch (_a) { }
|
|
270
|
+
}
|
|
265
271
|
try {
|
|
266
272
|
yield table.createIndex("content", {
|
|
267
|
-
config: lancedb.Index.fts(),
|
|
273
|
+
config: lancedb.Index.fts({ withPosition: true }),
|
|
268
274
|
});
|
|
269
275
|
}
|
|
270
276
|
catch (e) {
|
|
271
277
|
const msg = e instanceof Error ? e.message : String(e);
|
|
272
|
-
if (
|
|
273
|
-
|
|
278
|
+
if (msg.includes("already exists")) {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
// If position error, try dropping and recreating
|
|
282
|
+
if (msg.includes("position")) {
|
|
283
|
+
try {
|
|
284
|
+
yield table.dropIndex("content_idx");
|
|
285
|
+
yield table.createIndex("content", {
|
|
286
|
+
config: lancedb.Index.fts({ withPosition: true }),
|
|
287
|
+
});
|
|
288
|
+
(0, logger_1.log)("vectordb", "Rebuilt FTS index with position support");
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
catch (_b) { }
|
|
274
292
|
}
|
|
293
|
+
console.warn("Failed to create FTS index:", e);
|
|
275
294
|
}
|
|
276
295
|
});
|
|
277
296
|
}
|
package/package.json
CHANGED
package/scripts/postinstall.js
CHANGED
|
@@ -1,78 +1,121 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* Postinstall: sync plugin files
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* Postinstall: sync plugin files to all installed integrations.
|
|
4
|
+
* Runs after `npm install -g grepmax@latest` to automatically update
|
|
5
|
+
* skills, hooks, and configs without manual re-installation.
|
|
6
|
+
*
|
|
7
|
+
* Supported integrations:
|
|
8
|
+
* - Claude Code: sync skills/hooks to plugin cache
|
|
9
|
+
* - OpenCode: re-run installer (regenerates tool shim + plugin)
|
|
10
|
+
* - Codex: re-run installer (updates AGENTS.md + MCP registration)
|
|
11
|
+
* - Factory Droid: re-run installer (updates skills + hooks)
|
|
6
12
|
*/
|
|
7
13
|
const fs = require("node:fs");
|
|
8
14
|
const path = require("node:path");
|
|
9
15
|
const os = require("node:os");
|
|
16
|
+
const { execSync } = require("node:child_process");
|
|
10
17
|
|
|
11
|
-
const pluginCacheBase = path.join(os.homedir(), ".claude", "plugins", "cache", "grepmax", "grepmax");
|
|
12
18
|
const sourcePlugin = path.join(__dirname, "..", "plugins", "grepmax");
|
|
13
19
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
20
|
+
// --- Claude Code: sync files to plugin cache ---
|
|
21
|
+
const pluginCacheBase = path.join(
|
|
22
|
+
os.homedir(),
|
|
23
|
+
".claude",
|
|
24
|
+
"plugins",
|
|
25
|
+
"cache",
|
|
26
|
+
"grepmax",
|
|
27
|
+
"grepmax",
|
|
28
|
+
);
|
|
18
29
|
|
|
19
|
-
|
|
20
|
-
let entries;
|
|
21
|
-
try {
|
|
22
|
-
|
|
23
|
-
} catch {
|
|
24
|
-
|
|
25
|
-
}
|
|
30
|
+
if (fs.existsSync(pluginCacheBase) && fs.existsSync(sourcePlugin)) {
|
|
31
|
+
let entries;
|
|
32
|
+
try {
|
|
33
|
+
entries = fs.readdirSync(pluginCacheBase, { withFileTypes: true });
|
|
34
|
+
} catch {
|
|
35
|
+
entries = [];
|
|
36
|
+
}
|
|
26
37
|
|
|
27
|
-
const versionDirs = entries
|
|
28
|
-
|
|
38
|
+
const versionDirs = entries
|
|
39
|
+
.filter((e) => e.isDirectory())
|
|
40
|
+
.map((e) => e.name);
|
|
29
41
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
42
|
+
function copyRecursive(src, dest) {
|
|
43
|
+
if (!fs.existsSync(src)) return;
|
|
44
|
+
const stat = fs.statSync(src);
|
|
45
|
+
if (stat.isDirectory()) {
|
|
46
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
47
|
+
for (const entry of fs.readdirSync(src)) {
|
|
48
|
+
copyRecursive(path.join(src, entry), path.join(dest, entry));
|
|
49
|
+
}
|
|
50
|
+
} else {
|
|
51
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
52
|
+
fs.copyFileSync(src, dest);
|
|
38
53
|
}
|
|
39
|
-
} else {
|
|
40
|
-
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
41
|
-
fs.copyFileSync(src, dest);
|
|
42
54
|
}
|
|
43
|
-
}
|
|
44
55
|
|
|
45
|
-
for (const ver of versionDirs) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
path.join(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
for (const ver of versionDirs) {
|
|
57
|
+
const destDir = path.join(pluginCacheBase, ver);
|
|
58
|
+
try {
|
|
59
|
+
copyRecursive(
|
|
60
|
+
path.join(sourcePlugin, "skills"),
|
|
61
|
+
path.join(destDir, "skills"),
|
|
62
|
+
);
|
|
63
|
+
copyRecursive(
|
|
64
|
+
path.join(sourcePlugin, "hooks"),
|
|
65
|
+
path.join(destDir, "hooks"),
|
|
66
|
+
);
|
|
67
|
+
const hooksJson = path.join(sourcePlugin, "hooks.json");
|
|
68
|
+
if (fs.existsSync(hooksJson)) {
|
|
69
|
+
fs.copyFileSync(hooksJson, path.join(destDir, "hooks.json"));
|
|
70
|
+
}
|
|
71
|
+
} catch {
|
|
72
|
+
// Best-effort
|
|
62
73
|
}
|
|
63
|
-
} catch {
|
|
64
|
-
// Best-effort — don't fail the install
|
|
65
74
|
}
|
|
66
75
|
}
|
|
67
76
|
|
|
68
|
-
//
|
|
69
|
-
const ocToolPath = path.join(
|
|
70
|
-
|
|
77
|
+
// --- OpenCode: re-run installer if tool shim or plugin exists ---
|
|
78
|
+
const ocToolPath = path.join(
|
|
79
|
+
os.homedir(),
|
|
80
|
+
".config",
|
|
81
|
+
"opencode",
|
|
82
|
+
"tool",
|
|
83
|
+
"gmax.ts",
|
|
84
|
+
);
|
|
85
|
+
const ocPluginPath = path.join(
|
|
86
|
+
os.homedir(),
|
|
87
|
+
".config",
|
|
88
|
+
"opencode",
|
|
89
|
+
"plugins",
|
|
90
|
+
"gmax.ts",
|
|
91
|
+
);
|
|
71
92
|
if (fs.existsSync(ocToolPath) || fs.existsSync(ocPluginPath)) {
|
|
72
93
|
try {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
94
|
+
execSync("gmax install-opencode", { stdio: "ignore", timeout: 10000 });
|
|
95
|
+
} catch {}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// --- Codex: re-run installer if AGENTS.md has gmax skill ---
|
|
99
|
+
const codexAgentsPath = path.join(os.homedir(), ".codex", "AGENTS.md");
|
|
100
|
+
if (fs.existsSync(codexAgentsPath)) {
|
|
101
|
+
try {
|
|
102
|
+
const content = fs.readFileSync(codexAgentsPath, "utf-8");
|
|
103
|
+
if (content.includes("gmax")) {
|
|
104
|
+
execSync("gmax install-codex", { stdio: "ignore", timeout: 10000 });
|
|
105
|
+
}
|
|
106
|
+
} catch {}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// --- Factory Droid: re-run installer if skill exists ---
|
|
110
|
+
const droidSkillPath = path.join(
|
|
111
|
+
os.homedir(),
|
|
112
|
+
".factory",
|
|
113
|
+
"skills",
|
|
114
|
+
"gmax",
|
|
115
|
+
"SKILL.md",
|
|
116
|
+
);
|
|
117
|
+
if (fs.existsSync(droidSkillPath)) {
|
|
118
|
+
try {
|
|
119
|
+
execSync("gmax install-droid", { stdio: "ignore", timeout: 10000 });
|
|
120
|
+
} catch {}
|
|
78
121
|
}
|