grepmax 0.23.0 → 0.25.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/.claude-plugin/marketplace.json +1 -1
- package/README.md +25 -6
- package/dist/commands/add.js +38 -31
- package/dist/commands/config.js +16 -15
- package/dist/commands/context.js +38 -13
- package/dist/commands/doctor.js +76 -83
- package/dist/commands/extract.js +12 -1
- package/dist/commands/impact.js +33 -1
- package/dist/commands/index.js +23 -24
- package/dist/commands/list.js +23 -14
- package/dist/commands/mcp.js +494 -162
- package/dist/commands/peek.js +16 -5
- package/dist/commands/repair.js +54 -120
- package/dist/commands/search-output.js +30 -9
- package/dist/commands/search-run.js +75 -47
- package/dist/commands/search-skeletons.js +28 -18
- package/dist/commands/search.js +45 -49
- package/dist/commands/serve.js +415 -373
- package/dist/commands/setup.js +2 -2
- package/dist/commands/similar.js +5 -5
- package/dist/commands/skeleton.js +67 -41
- package/dist/commands/status.js +5 -2
- package/dist/commands/summarize.js +6 -0
- package/dist/commands/surprises.js +150 -0
- package/dist/commands/watch.js +102 -38
- package/dist/config.js +3 -0
- package/dist/eval-surprising-connections.js +191 -0
- package/dist/index.js +2 -0
- package/dist/lib/analysis/surprising-connections.js +600 -0
- package/dist/lib/daemon/daemon.js +1101 -379
- package/dist/lib/daemon/ipc-handler.js +122 -13
- package/dist/lib/daemon/mlx-server-manager.js +268 -125
- package/dist/lib/daemon/process-manager.js +7 -4
- package/dist/lib/daemon/search-handler.js +23 -9
- package/dist/lib/daemon/watcher-manager.js +353 -110
- package/dist/lib/graph/impact-rollup.js +202 -0
- package/dist/lib/graph/impact.js +15 -1
- package/dist/lib/help/agent-cheatsheet.js +1 -0
- package/dist/lib/index/batch-processor.js +231 -67
- package/dist/lib/index/embedding-generation.js +109 -0
- package/dist/lib/index/embedding-status.js +23 -0
- package/dist/lib/index/file-policy.js +273 -0
- package/dist/lib/index/ignore-patterns.js +4 -0
- package/dist/lib/index/index-config.js +18 -4
- package/dist/lib/index/syncer.js +256 -79
- package/dist/lib/index/walker.js +66 -86
- package/dist/lib/index/watcher-batch.js +9 -0
- package/dist/lib/index/watcher.js +129 -3
- package/dist/lib/llm/server.js +6 -0
- package/dist/lib/search/searcher.js +30 -19
- package/dist/lib/skeleton/skeletonizer.js +118 -0
- package/dist/lib/skeleton/summary-formatter.js +8 -1
- package/dist/lib/store/store-lease.js +473 -0
- package/dist/lib/store/vector-db.js +302 -57
- package/dist/lib/utils/blocked-roots.js +63 -0
- package/dist/lib/utils/cross-project.js +7 -3
- package/dist/lib/utils/daemon-client.js +24 -1
- package/dist/lib/utils/daemon-launcher.js +38 -13
- package/dist/lib/utils/doctor-status.js +76 -0
- package/dist/lib/utils/file-utils.js +74 -4
- package/dist/lib/utils/keyed-mutex.js +101 -0
- package/dist/lib/utils/logger.js +57 -1
- package/dist/lib/utils/mlx-hf-cache.js +114 -0
- package/dist/lib/utils/operation-coordinator.js +146 -0
- package/dist/lib/utils/path-containment.js +106 -0
- package/dist/lib/utils/process.js +44 -0
- package/dist/lib/utils/project-registry.js +351 -3
- package/dist/lib/utils/scope-filter.js +3 -9
- package/dist/lib/utils/stale-hint.js +12 -19
- package/dist/lib/utils/watcher-launcher.js +5 -1
- package/dist/lib/utils/watcher-store.js +2 -2
- package/dist/lib/workers/colbert-math.js +15 -12
- package/dist/lib/workers/embeddings/colbert.js +3 -2
- package/dist/lib/workers/embeddings/granite.js +4 -3
- package/dist/lib/workers/embeddings/mlx-client.js +59 -16
- package/dist/lib/workers/orchestrator.js +39 -8
- package/dist/lib/workers/pool.js +150 -83
- package/dist/lib/workers/process-child.js +11 -2
- package/dist/lib/workers/serialized-handler.js +10 -0
- package/dist/lib/workers/worker.js +13 -4
- package/mlx-embed-server/server.py +21 -3
- package/mlx-embed-server/summarizer.py +8 -0
- package/package.json +6 -3
- package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
- package/plugins/grepmax/hooks/start.js +3 -170
package/dist/lib/index/walker.js
CHANGED
|
@@ -32,16 +32,6 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
32
32
|
return result;
|
|
33
33
|
};
|
|
34
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
|
-
var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
|
|
45
35
|
var __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
46
36
|
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
47
37
|
var m = o[Symbol.asyncIterator], i;
|
|
@@ -49,6 +39,7 @@ var __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
|
49
39
|
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
|
50
40
|
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
51
41
|
};
|
|
42
|
+
var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
|
|
52
43
|
var __asyncDelegator = (this && this.__asyncDelegator) || function (o) {
|
|
53
44
|
var i, p;
|
|
54
45
|
return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i;
|
|
@@ -66,103 +57,92 @@ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _ar
|
|
|
66
57
|
function reject(value) { resume("throw", value); }
|
|
67
58
|
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
|
|
68
59
|
};
|
|
69
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
70
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
71
|
-
};
|
|
72
60
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
61
|
+
exports.createWalkState = createWalkState;
|
|
62
|
+
exports.isPathProtectedByWalkState = isPathProtectedByWalkState;
|
|
73
63
|
exports.walk = walk;
|
|
74
64
|
const fs = __importStar(require("node:fs/promises"));
|
|
75
65
|
const path = __importStar(require("node:path"));
|
|
76
|
-
const
|
|
77
|
-
const
|
|
78
|
-
function
|
|
79
|
-
return
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if (!filter)
|
|
86
|
-
filter = (0, ignore_1.default)();
|
|
87
|
-
filter.add(content);
|
|
88
|
-
}
|
|
89
|
-
catch (_err) {
|
|
90
|
-
// Ignore missing files
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
return filter;
|
|
94
|
-
});
|
|
66
|
+
const path_containment_1 = require("../utils/path-containment");
|
|
67
|
+
const file_policy_1 = require("./file-policy");
|
|
68
|
+
function createWalkState() {
|
|
69
|
+
return { rootComplete: true, protectedPaths: new Set(), errors: [] };
|
|
70
|
+
}
|
|
71
|
+
function isPathProtectedByWalkState(candidate, state) {
|
|
72
|
+
if (!state.rootComplete)
|
|
73
|
+
return true;
|
|
74
|
+
return Array.from(state.protectedPaths).some((protectedPath) => (0, path_containment_1.isPathWithin)(protectedPath, candidate));
|
|
95
75
|
}
|
|
96
76
|
function walk(rootDir_1) {
|
|
97
77
|
return __asyncGenerator(this, arguments, function* walk_1(rootDir, options = {}) {
|
|
98
|
-
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
dir: rootDir,
|
|
107
|
-
};
|
|
108
|
-
// Stack of scopes.
|
|
109
|
-
// We check against ALL scopes in the stack.
|
|
110
|
-
// This implements the "additive" ignore behavior (files ignored by parent are ignored by child).
|
|
111
|
-
// Note: This does not strictly support "un-ignoring" a parent rule via a child .gitignore
|
|
112
|
-
// (because we check parent independently), but it is the safest robust implementation for "hiding" nested files.
|
|
113
|
-
const stack = [rootScope];
|
|
114
|
-
// We also try to load root .gitignore immediately to add to the stack
|
|
115
|
-
const rootGitIgnore = yield __await(getIgnoreFilter(rootDir, ignoreFiles));
|
|
116
|
-
if (rootGitIgnore) {
|
|
117
|
-
stack.push({ filter: rootGitIgnore, dir: rootDir });
|
|
118
|
-
}
|
|
119
|
-
yield __await(yield* __asyncDelegator(__asyncValues(_walk(rootDir, rootDir, stack, ignoreFiles))));
|
|
78
|
+
var _a, _b;
|
|
79
|
+
const root = path.resolve(rootDir);
|
|
80
|
+
const policy = (_a = options.policy) !== null && _a !== void 0 ? _a : new file_policy_1.ProjectFilePolicy(root, {
|
|
81
|
+
ignoreFiles: options.ignoreFiles,
|
|
82
|
+
additionalPatterns: options.additionalPatterns,
|
|
83
|
+
});
|
|
84
|
+
const state = (_b = options.state) !== null && _b !== void 0 ? _b : createWalkState();
|
|
85
|
+
yield __await(yield* __asyncDelegator(__asyncValues(walkDirectory(root, root, policy, state, true))));
|
|
120
86
|
});
|
|
121
87
|
}
|
|
122
|
-
function
|
|
123
|
-
return __asyncGenerator(this, arguments, function*
|
|
88
|
+
function walkDirectory(currentDir, rootDir, policy, state, isRoot) {
|
|
89
|
+
return __asyncGenerator(this, arguments, function* walkDirectory_1() {
|
|
90
|
+
const directory = yield __await(policy.classifyDirectory(currentDir));
|
|
91
|
+
if (directory.status === "error") {
|
|
92
|
+
state.protectedPaths.add(directory.protectedPath);
|
|
93
|
+
state.errors.push({
|
|
94
|
+
path: directory.protectedPath,
|
|
95
|
+
error: directory.error,
|
|
96
|
+
});
|
|
97
|
+
if (isRoot)
|
|
98
|
+
state.rootComplete = false;
|
|
99
|
+
return yield __await(void 0);
|
|
100
|
+
}
|
|
101
|
+
if (directory.status !== "traverse") {
|
|
102
|
+
if (isRoot)
|
|
103
|
+
state.rootComplete = false;
|
|
104
|
+
return yield __await(void 0);
|
|
105
|
+
}
|
|
124
106
|
let entries;
|
|
125
107
|
try {
|
|
126
108
|
entries = yield __await(fs.readdir(currentDir, { withFileTypes: true }));
|
|
127
109
|
}
|
|
128
|
-
catch (
|
|
110
|
+
catch (error) {
|
|
111
|
+
state.protectedPaths.add(currentDir);
|
|
112
|
+
state.errors.push({ path: currentDir, error });
|
|
113
|
+
if (isRoot)
|
|
114
|
+
state.rootComplete = false;
|
|
129
115
|
return yield __await(void 0);
|
|
130
116
|
}
|
|
131
117
|
for (const entry of entries) {
|
|
132
118
|
const absPath = path.join(currentDir, entry.name);
|
|
133
|
-
const relPathToRoot = path.relative(rootDir, absPath);
|
|
134
|
-
// 1. Check if ignored by any scope in the stack
|
|
135
|
-
const isDir = entry.isDirectory();
|
|
136
|
-
let isIgnored = false;
|
|
137
|
-
for (const scope of stack) {
|
|
138
|
-
const relToScope = path.relative(scope.dir, absPath);
|
|
139
|
-
if (!relToScope)
|
|
140
|
-
continue;
|
|
141
|
-
// Append trailing slash for directories so patterns like "dist/" match
|
|
142
|
-
const testPath = isDir ? `${relToScope}/` : relToScope;
|
|
143
|
-
if (scope.filter.ignores(testPath)) {
|
|
144
|
-
isIgnored = true;
|
|
145
|
-
break;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
if (isIgnored)
|
|
149
|
-
continue;
|
|
150
119
|
if (entry.isDirectory()) {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
yield __await(
|
|
157
|
-
|
|
120
|
+
yield __await(yield* __asyncDelegator(__asyncValues(walkDirectory(absPath, rootDir, policy, state, false))));
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (!entry.isFile()) {
|
|
124
|
+
try {
|
|
125
|
+
const stat = yield __await(fs.lstat(absPath));
|
|
126
|
+
if (stat.isDirectory()) {
|
|
127
|
+
yield __await(yield* __asyncDelegator(__asyncValues(walkDirectory(absPath, rootDir, policy, state, false))));
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (!stat.isFile())
|
|
131
|
+
continue;
|
|
158
132
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
133
|
+
catch (error) {
|
|
134
|
+
state.protectedPaths.add(absPath);
|
|
135
|
+
state.errors.push({ path: absPath, error });
|
|
136
|
+
continue;
|
|
162
137
|
}
|
|
163
138
|
}
|
|
164
|
-
|
|
165
|
-
|
|
139
|
+
const file = yield __await(policy.classifyFile(absPath));
|
|
140
|
+
if (file.status === "indexable") {
|
|
141
|
+
yield yield __await(path.relative(rootDir, absPath));
|
|
142
|
+
}
|
|
143
|
+
else if (file.status === "error") {
|
|
144
|
+
state.protectedPaths.add(file.protectedPath);
|
|
145
|
+
state.errors.push({ path: file.protectedPath, error: file.error });
|
|
166
146
|
}
|
|
167
147
|
}
|
|
168
148
|
});
|
|
@@ -44,6 +44,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
44
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
45
|
exports.processBatchCore = processBatchCore;
|
|
46
46
|
exports.flushBatchToDb = flushBatchToDb;
|
|
47
|
+
exports.computePathRetry = computePathRetry;
|
|
47
48
|
exports.computeRetryAction = computeRetryAction;
|
|
48
49
|
const fs = __importStar(require("node:fs"));
|
|
49
50
|
const cache_check_1 = require("../utils/cache-check");
|
|
@@ -139,6 +140,14 @@ function flushBatchToDb(result, vectorDb) {
|
|
|
139
140
|
}
|
|
140
141
|
});
|
|
141
142
|
}
|
|
143
|
+
function computePathRetry(previousFailures, maxFailures, baseDelayMs) {
|
|
144
|
+
const failures = previousFailures + 1;
|
|
145
|
+
return {
|
|
146
|
+
retry: failures < maxFailures,
|
|
147
|
+
failures,
|
|
148
|
+
backoffMs: Math.min(baseDelayMs * Math.pow(2, failures), 30000),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
142
151
|
function computeRetryAction(batch, retryCount, maxRetries, isLockError, consecutiveLockFailures, debounceMs) {
|
|
143
152
|
var _a;
|
|
144
153
|
const requeued = new Map();
|
|
@@ -41,11 +41,21 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
41
41
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
42
|
});
|
|
43
43
|
};
|
|
44
|
+
var __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
45
|
+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
46
|
+
var m = o[Symbol.asyncIterator], i;
|
|
47
|
+
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
|
48
|
+
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
|
49
|
+
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
50
|
+
};
|
|
44
51
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
52
|
exports.WATCHER_IGNORE_GLOBS = void 0;
|
|
46
53
|
exports.startWatcher = startWatcher;
|
|
54
|
+
const path = __importStar(require("node:path"));
|
|
47
55
|
const watcher = __importStar(require("@parcel/watcher"));
|
|
48
56
|
const batch_processor_1 = require("./batch-processor");
|
|
57
|
+
const file_policy_1 = require("./file-policy");
|
|
58
|
+
const walker_1 = require("./walker");
|
|
49
59
|
// Ignore patterns for @parcel/watcher (micromatch globs + directory names).
|
|
50
60
|
// Directory names are matched at any depth automatically.
|
|
51
61
|
exports.WATCHER_IGNORE_GLOBS = [
|
|
@@ -70,15 +80,125 @@ exports.WATCHER_IGNORE_GLOBS = [
|
|
|
70
80
|
".m2",
|
|
71
81
|
"vendor",
|
|
72
82
|
"lancedb",
|
|
73
|
-
".*", // dotfiles
|
|
74
83
|
"**/*.tmp.*", // editor atomic save artifacts
|
|
75
84
|
"**/*.sb-*", // Xcode swap files
|
|
76
85
|
];
|
|
77
86
|
function startWatcher(opts) {
|
|
78
87
|
return __awaiter(this, void 0, void 0, function* () {
|
|
88
|
+
var _a, _b;
|
|
79
89
|
const { projectRoot } = opts;
|
|
80
90
|
const wtag = `watch:${projectRoot.split("/").pop()}`;
|
|
81
|
-
const
|
|
91
|
+
const filePolicy = new file_policy_1.ProjectFilePolicy(projectRoot);
|
|
92
|
+
let reconciliation = null;
|
|
93
|
+
let reconcileRequested = false;
|
|
94
|
+
let closing = false;
|
|
95
|
+
let scanHealthy = ((_a = opts.initialScanErrors) !== null && _a !== void 0 ? _a : 0) === 0;
|
|
96
|
+
let ingestionDegraded = ((_b = opts.initialFailedFiles) !== null && _b !== void 0 ? _b : 0) > 0;
|
|
97
|
+
let degradedRepairQueued = false;
|
|
98
|
+
const terminalFailures = new Set();
|
|
99
|
+
let processor;
|
|
100
|
+
const reportHealthyIfSettled = () => {
|
|
101
|
+
var _a;
|
|
102
|
+
if (scanHealthy &&
|
|
103
|
+
!ingestionDegraded &&
|
|
104
|
+
terminalFailures.size === 0 &&
|
|
105
|
+
processor.progress.pendingFiles === 0) {
|
|
106
|
+
(_a = opts.onHealthChange) === null || _a === void 0 ? void 0 : _a.call(opts, true, 0);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
const reconcile = () => {
|
|
110
|
+
if (closing)
|
|
111
|
+
return;
|
|
112
|
+
if (reconciliation) {
|
|
113
|
+
reconcileRequested = true;
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
reconciliation = (() => __awaiter(this, void 0, void 0, function* () {
|
|
117
|
+
var _a, e_1, _b, _c;
|
|
118
|
+
var _d;
|
|
119
|
+
let incompleteRetries = 0;
|
|
120
|
+
do {
|
|
121
|
+
reconcileRequested = false;
|
|
122
|
+
filePolicy.invalidateIgnoreCache();
|
|
123
|
+
const rootPrefix = projectRoot.endsWith("/")
|
|
124
|
+
? projectRoot
|
|
125
|
+
: `${projectRoot}/`;
|
|
126
|
+
const cached = yield opts.metaCache.getKeysWithPrefix(rootPrefix);
|
|
127
|
+
const seen = new Set();
|
|
128
|
+
const state = (0, walker_1.createWalkState)();
|
|
129
|
+
try {
|
|
130
|
+
for (var _e = true, _f = (e_1 = void 0, __asyncValues((0, walker_1.walk)(projectRoot, {
|
|
131
|
+
policy: filePolicy,
|
|
132
|
+
state,
|
|
133
|
+
}))), _g; _g = yield _f.next(), _a = _g.done, !_a; _e = true) {
|
|
134
|
+
_c = _g.value;
|
|
135
|
+
_e = false;
|
|
136
|
+
const relative = _c;
|
|
137
|
+
if (closing)
|
|
138
|
+
return;
|
|
139
|
+
const absolute = path.join(projectRoot, relative);
|
|
140
|
+
seen.add(absolute);
|
|
141
|
+
processor.handleFileEvent("change", absolute);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
145
|
+
finally {
|
|
146
|
+
try {
|
|
147
|
+
if (!_e && !_a && (_b = _f.return)) yield _b.call(_f);
|
|
148
|
+
}
|
|
149
|
+
finally { if (e_1) throw e_1.error; }
|
|
150
|
+
}
|
|
151
|
+
for (const cachedPath of cached) {
|
|
152
|
+
if (closing)
|
|
153
|
+
return;
|
|
154
|
+
if (!seen.has(cachedPath) &&
|
|
155
|
+
!(0, walker_1.isPathProtectedByWalkState)(cachedPath, state)) {
|
|
156
|
+
processor.handleFileEvent("unlink", cachedPath);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (!state.rootComplete || state.errors.length > 0) {
|
|
160
|
+
scanHealthy = false;
|
|
161
|
+
(_d = opts.onHealthChange) === null || _d === void 0 ? void 0 : _d.call(opts, false, state.errors.length);
|
|
162
|
+
console.error(`[${wtag}] Reconciliation incomplete at ${state.errors.length} path(s)`);
|
|
163
|
+
if (incompleteRetries < 3) {
|
|
164
|
+
incompleteRetries++;
|
|
165
|
+
reconcileRequested = true;
|
|
166
|
+
yield new Promise((resolve) => setTimeout(resolve, 1000 * Math.pow(2, (incompleteRetries - 1))));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
scanHealthy = true;
|
|
171
|
+
if (ingestionDegraded && processor.progress.pendingFiles > 0) {
|
|
172
|
+
degradedRepairQueued = true;
|
|
173
|
+
}
|
|
174
|
+
reportHealthyIfSettled();
|
|
175
|
+
incompleteRetries = 0;
|
|
176
|
+
}
|
|
177
|
+
} while (reconcileRequested && !closing);
|
|
178
|
+
}))()
|
|
179
|
+
.catch((err) => console.error(`[${wtag}] Reconciliation failed:`, err))
|
|
180
|
+
.finally(() => {
|
|
181
|
+
reconciliation = null;
|
|
182
|
+
});
|
|
183
|
+
};
|
|
184
|
+
processor = new batch_processor_1.ProjectBatchProcessor(Object.assign(Object.assign({}, opts), { filePolicy, onPolicyChange: reconcile, onReindex: (files, durationMs) => {
|
|
185
|
+
var _a;
|
|
186
|
+
(_a = opts.onReindex) === null || _a === void 0 ? void 0 : _a.call(opts, files, durationMs);
|
|
187
|
+
reportHealthyIfSettled();
|
|
188
|
+
}, onTerminalFailure: (absPath) => {
|
|
189
|
+
var _a;
|
|
190
|
+
terminalFailures.add(absPath);
|
|
191
|
+
(_a = opts.onHealthChange) === null || _a === void 0 ? void 0 : _a.call(opts, false, terminalFailures.size);
|
|
192
|
+
}, onPathSuccess: (absPath) => {
|
|
193
|
+
terminalFailures.delete(absPath);
|
|
194
|
+
if (degradedRepairQueued &&
|
|
195
|
+
terminalFailures.size === 0 &&
|
|
196
|
+
processor.progress.pendingFiles === 0) {
|
|
197
|
+
ingestionDegraded = false;
|
|
198
|
+
degradedRepairQueued = false;
|
|
199
|
+
}
|
|
200
|
+
reportHealthyIfSettled();
|
|
201
|
+
} }));
|
|
82
202
|
const subscription = yield watcher.subscribe(projectRoot, (err, events) => {
|
|
83
203
|
if (err) {
|
|
84
204
|
console.error(`[${wtag}] Watcher error:`, err);
|
|
@@ -88,10 +208,16 @@ function startWatcher(opts) {
|
|
|
88
208
|
processor.handleFileEvent(event.type === "delete" ? "unlink" : "change", event.path);
|
|
89
209
|
}
|
|
90
210
|
}, { ignore: exports.WATCHER_IGNORE_GLOBS });
|
|
211
|
+
reconcile();
|
|
91
212
|
return {
|
|
213
|
+
get progress() {
|
|
214
|
+
return processor.progress;
|
|
215
|
+
},
|
|
92
216
|
close: () => __awaiter(this, void 0, void 0, function* () {
|
|
93
|
-
|
|
217
|
+
closing = true;
|
|
94
218
|
yield subscription.unsubscribe();
|
|
219
|
+
yield reconciliation;
|
|
220
|
+
yield processor.close();
|
|
95
221
|
}),
|
|
96
222
|
};
|
|
97
223
|
});
|
package/dist/lib/llm/server.js
CHANGED
|
@@ -51,6 +51,12 @@ const config_1 = require("../../config");
|
|
|
51
51
|
const index_config_1 = require("../index/index-config");
|
|
52
52
|
const log_rotate_1 = require("../utils/log-rotate");
|
|
53
53
|
const config_2 = require("./config");
|
|
54
|
+
// ⛔ HARD STOP FOR AI AGENTS: This starts llama-server with a multi-GB local
|
|
55
|
+
// LLM (Qwen3.5-35B GGUF ≈21GB by default). DO NOT start it — via start(),
|
|
56
|
+
// llm-start, reviewCommit, investigate, or a benchmark — without the user's
|
|
57
|
+
// explicit, in-session authorization. Loading models this size has crashed the
|
|
58
|
+
// machine. Stop and ask first. See the "HARD STOP" section at the top of
|
|
59
|
+
// CLAUDE.md.
|
|
54
60
|
const HEALTH_TIMEOUT_MS = 2000;
|
|
55
61
|
const POLL_INTERVAL_MS = 500;
|
|
56
62
|
const STOP_GRACE_MS = 5000;
|
|
@@ -101,14 +101,30 @@ function buildWhereClause(pathPrefix, filters, searchIntent) {
|
|
|
101
101
|
if (typeof roleFilter === "string" && roleFilter) {
|
|
102
102
|
parts.push(`role = '${(0, filter_builder_1.escapeSqlString)(roleFilter)}'`);
|
|
103
103
|
}
|
|
104
|
-
const projectRoots = filters === null || filters === void 0 ? void 0 : filters.
|
|
105
|
-
if (
|
|
106
|
-
const roots = projectRoots.
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
104
|
+
const projectRoots = filters === null || filters === void 0 ? void 0 : filters.projectRoots;
|
|
105
|
+
if (Array.isArray(projectRoots)) {
|
|
106
|
+
const roots = projectRoots.filter((root) => typeof root === "string" && root.length > 0);
|
|
107
|
+
if (roots.length === 0) {
|
|
108
|
+
parts.push("1 = 0");
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
const clauses = roots.map((root) => {
|
|
112
|
+
const prefix = root.endsWith("/") ? root : `${root}/`;
|
|
113
|
+
return (0, filter_builder_1.pathStartsWith)(prefix);
|
|
114
|
+
});
|
|
115
|
+
parts.push(`(${clauses.join(" OR ")})`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
const legacyProjectRoots = filters === null || filters === void 0 ? void 0 : filters.project_roots;
|
|
120
|
+
if (typeof legacyProjectRoots === "string" && legacyProjectRoots) {
|
|
121
|
+
const roots = legacyProjectRoots.split(",");
|
|
122
|
+
const clauses = roots.map((r) => {
|
|
123
|
+
const prefix = r.endsWith("/") ? r : `${r}/`;
|
|
124
|
+
return (0, filter_builder_1.pathStartsWith)(prefix);
|
|
125
|
+
});
|
|
126
|
+
parts.push(`(${clauses.join(" OR ")})`);
|
|
127
|
+
}
|
|
112
128
|
}
|
|
113
129
|
const excludeRoots = filters === null || filters === void 0 ? void 0 : filters.exclude_project_roots;
|
|
114
130
|
if (typeof excludeRoots === "string" && excludeRoots) {
|
|
@@ -132,8 +148,9 @@ function buildWhereClause(pathPrefix, filters, searchIntent) {
|
|
|
132
148
|
return parts.length > 0 ? parts.join(" AND ") : undefined;
|
|
133
149
|
}
|
|
134
150
|
class Searcher {
|
|
135
|
-
constructor(db) {
|
|
151
|
+
constructor(db, workerPool = (0, pool_1.getWorkerPool)()) {
|
|
136
152
|
this.db = db;
|
|
153
|
+
this.workerPool = workerPool;
|
|
137
154
|
this.ftsIndexChecked = false;
|
|
138
155
|
this.ftsAvailable = false;
|
|
139
156
|
this.ftsLastCheckedAt = 0;
|
|
@@ -389,7 +406,7 @@ class Searcher {
|
|
|
389
406
|
const searchIntent = intent || (0, intent_1.detectIntent)(query);
|
|
390
407
|
// Bare-identifier queries get symbol-definition promotion (see below).
|
|
391
408
|
const symbolQuery = asSymbolQuery(query);
|
|
392
|
-
const pool =
|
|
409
|
+
const pool = this.workerPool;
|
|
393
410
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
394
411
|
const err = new Error("Aborted");
|
|
395
412
|
err.name = "AbortError";
|
|
@@ -409,13 +426,7 @@ class Searcher {
|
|
|
409
426
|
const PRE_RERANK_K = Number.isFinite(envPreK) && envPreK > 0
|
|
410
427
|
? envPreK
|
|
411
428
|
: Math.max(finalLimit * 5, 500);
|
|
412
|
-
|
|
413
|
-
try {
|
|
414
|
-
table = yield this.db.ensureTable();
|
|
415
|
-
}
|
|
416
|
-
catch (_r) {
|
|
417
|
-
return { data: [] };
|
|
418
|
-
}
|
|
429
|
+
const table = yield this.db.ensureTable();
|
|
419
430
|
// Ensure FTS index exists (lazy init, retry periodically on failure)
|
|
420
431
|
const now = Date.now();
|
|
421
432
|
if (!this.ftsIndexChecked ||
|
|
@@ -496,7 +507,7 @@ class Searcher {
|
|
|
496
507
|
this.ftsAvailable = true;
|
|
497
508
|
console.warn("[Searcher] Rebuilt FTS index with position support — retry search");
|
|
498
509
|
}
|
|
499
|
-
catch (
|
|
510
|
+
catch (_r) { }
|
|
500
511
|
}
|
|
501
512
|
else {
|
|
502
513
|
console.warn(`[Searcher] FTS search failed (will retry later): ${msg}`);
|
|
@@ -806,7 +817,7 @@ class Searcher {
|
|
|
806
817
|
defs = arr.filter((v) => typeof v === "string");
|
|
807
818
|
}
|
|
808
819
|
}
|
|
809
|
-
catch (
|
|
820
|
+
catch (_s) { }
|
|
810
821
|
}
|
|
811
822
|
const norm = (0, pagerank_1.pageRankBoostForSymbols)(defs, prScores, prMax);
|
|
812
823
|
item.score += PR_WEIGHT * norm;
|
|
@@ -69,6 +69,8 @@ const DEFAULT_OPTIONS = {
|
|
|
69
69
|
includeSummary: true,
|
|
70
70
|
maxCallsInSummary: 4,
|
|
71
71
|
};
|
|
72
|
+
const SQL_TEMPLATE_LANGS = new Set(["typescript", "tsx", "javascript"]);
|
|
73
|
+
const SQL_IDENTIFIER_PATTERN = '(?:"[^"]+"|\\[[^\\]]+\\]|`[^`]+`|[A-Za-z_][\\w$]*)(?:\\s*\\.\\s*(?:"[^"]+"|\\[[^\\]]+\\]|`[^`]+`|[A-Za-z_][\\w$]*)){0,2}';
|
|
72
74
|
// WASM locator (same as chunker.ts)
|
|
73
75
|
function resolveTreeSitterWasmLocator() {
|
|
74
76
|
try {
|
|
@@ -238,10 +240,12 @@ class Skeletonizer {
|
|
|
238
240
|
}
|
|
239
241
|
// Extract metadata from the body
|
|
240
242
|
const referencedSymbols = this.extractReferencedSymbols(bodyNode);
|
|
243
|
+
const sqlTemplates = this.extractSqlTemplateSummaries(bodyNode, langId);
|
|
241
244
|
const complexity = this.calculateComplexity(bodyNode);
|
|
242
245
|
const role = this.classifyRole(complexity, referencedSymbols.length);
|
|
243
246
|
const metadata = {
|
|
244
247
|
referencedSymbols,
|
|
248
|
+
sqlTemplates,
|
|
245
249
|
complexity,
|
|
246
250
|
role,
|
|
247
251
|
};
|
|
@@ -450,6 +454,120 @@ class Skeletonizer {
|
|
|
450
454
|
extract(node);
|
|
451
455
|
return refs;
|
|
452
456
|
}
|
|
457
|
+
/**
|
|
458
|
+
* Surface SQL tagged templates hidden inside elided JS/TS function bodies.
|
|
459
|
+
* This is intentionally a lightweight skeleton hint, not a SQL parser.
|
|
460
|
+
*/
|
|
461
|
+
extractSqlTemplateSummaries(node, langId) {
|
|
462
|
+
if (!SQL_TEMPLATE_LANGS.has(langId))
|
|
463
|
+
return [];
|
|
464
|
+
const summaries = [];
|
|
465
|
+
const seen = new Set();
|
|
466
|
+
const extract = (n) => {
|
|
467
|
+
const template = (n.namedChildren || []).find((child) => child.type === "template_string");
|
|
468
|
+
if (template && this.isSqlTaggedTemplate(n, template)) {
|
|
469
|
+
const summary = this.summarizeSqlTemplate(template);
|
|
470
|
+
if (summary && !seen.has(summary)) {
|
|
471
|
+
seen.add(summary);
|
|
472
|
+
summaries.push(summary);
|
|
473
|
+
}
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
for (const child of n.namedChildren || []) {
|
|
477
|
+
extract(child);
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
extract(node);
|
|
481
|
+
return summaries;
|
|
482
|
+
}
|
|
483
|
+
isSqlTaggedTemplate(node, template) {
|
|
484
|
+
const tagText = node.text
|
|
485
|
+
.slice(0, template.startIndex - node.startIndex)
|
|
486
|
+
.trim();
|
|
487
|
+
return /(?:^|[.\s])sql(?:$|[.<(])/.test(tagText);
|
|
488
|
+
}
|
|
489
|
+
summarizeSqlTemplate(template) {
|
|
490
|
+
var _a;
|
|
491
|
+
const sqlText = this.extractTemplateText(template)
|
|
492
|
+
.replace(/--[^\n]*/g, " ")
|
|
493
|
+
.replace(/\/\*[\s\S]*?\*\//g, " ")
|
|
494
|
+
.replace(/\s+/g, " ")
|
|
495
|
+
.trim();
|
|
496
|
+
if (!sqlText)
|
|
497
|
+
return null;
|
|
498
|
+
const op = (_a = sqlText.match(/\b(select|insert|update|delete|merge|create|alter|drop|truncate)\b/i)) === null || _a === void 0 ? void 0 : _a[1];
|
|
499
|
+
if (!op)
|
|
500
|
+
return "SQL";
|
|
501
|
+
const operation = op.toUpperCase();
|
|
502
|
+
const tables = this.extractSqlTables(sqlText, operation);
|
|
503
|
+
if (tables.length === 0)
|
|
504
|
+
return operation;
|
|
505
|
+
const tableList = tables.slice(0, 2);
|
|
506
|
+
if (tables.length > tableList.length)
|
|
507
|
+
tableList.push("...");
|
|
508
|
+
return `${operation} ${tableList.join(", ")}`;
|
|
509
|
+
}
|
|
510
|
+
extractTemplateText(template) {
|
|
511
|
+
const parts = [];
|
|
512
|
+
for (const child of template.namedChildren || []) {
|
|
513
|
+
if (child.type === "string_fragment") {
|
|
514
|
+
parts.push(child.text);
|
|
515
|
+
}
|
|
516
|
+
else if (child.type === "template_substitution") {
|
|
517
|
+
parts.push(" ? ");
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
if (parts.length > 0)
|
|
521
|
+
return parts.join("");
|
|
522
|
+
return template.text.slice(1, -1).replace(/\$\{[^}]*\}/g, " ? ");
|
|
523
|
+
}
|
|
524
|
+
extractSqlTables(sqlText, operation) {
|
|
525
|
+
const tables = [];
|
|
526
|
+
const seen = new Set();
|
|
527
|
+
const addMatches = (pattern) => {
|
|
528
|
+
for (const match of sqlText.matchAll(pattern)) {
|
|
529
|
+
const table = this.normalizeSqlIdentifier(match[1]);
|
|
530
|
+
if (table && !seen.has(table)) {
|
|
531
|
+
seen.add(table);
|
|
532
|
+
tables.push(table);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
};
|
|
536
|
+
switch (operation) {
|
|
537
|
+
case "SELECT":
|
|
538
|
+
addMatches(new RegExp(`\\b(?:from|join)\\s+(${SQL_IDENTIFIER_PATTERN})`, "gi"));
|
|
539
|
+
break;
|
|
540
|
+
case "INSERT":
|
|
541
|
+
addMatches(new RegExp(`\\binsert\\s+into\\s+(${SQL_IDENTIFIER_PATTERN})`, "gi"));
|
|
542
|
+
break;
|
|
543
|
+
case "UPDATE":
|
|
544
|
+
addMatches(new RegExp(`\\bupdate\\s+(${SQL_IDENTIFIER_PATTERN})`, "gi"));
|
|
545
|
+
break;
|
|
546
|
+
case "DELETE":
|
|
547
|
+
addMatches(new RegExp(`\\bdelete\\s+from\\s+(${SQL_IDENTIFIER_PATTERN})`, "gi"));
|
|
548
|
+
break;
|
|
549
|
+
case "MERGE":
|
|
550
|
+
addMatches(new RegExp(`\\bmerge\\s+into\\s+(${SQL_IDENTIFIER_PATTERN})`, "gi"));
|
|
551
|
+
break;
|
|
552
|
+
case "CREATE":
|
|
553
|
+
addMatches(new RegExp(`\\bcreate\\s+(?:or\\s+replace\\s+)?(?:table|view|materialized\\s+view|index)\\s+(?:if\\s+not\\s+exists\\s+)?(${SQL_IDENTIFIER_PATTERN})`, "gi"));
|
|
554
|
+
break;
|
|
555
|
+
case "ALTER":
|
|
556
|
+
case "DROP":
|
|
557
|
+
addMatches(new RegExp(`\\b${operation.toLowerCase()}\\s+(?:table|view|materialized\\s+view|index)\\s+(?:if\\s+(?:exists|not\\s+exists)\\s+)?(${SQL_IDENTIFIER_PATTERN})`, "gi"));
|
|
558
|
+
break;
|
|
559
|
+
case "TRUNCATE":
|
|
560
|
+
addMatches(new RegExp(`\\btruncate\\s+(?:table\\s+)?(${SQL_IDENTIFIER_PATTERN})`, "gi"));
|
|
561
|
+
break;
|
|
562
|
+
}
|
|
563
|
+
return tables;
|
|
564
|
+
}
|
|
565
|
+
normalizeSqlIdentifier(identifier) {
|
|
566
|
+
const normalized = identifier === null || identifier === void 0 ? void 0 : identifier.replace(/\s*\.\s*/g, ".").replace(/["`[\]]/g, "").trim();
|
|
567
|
+
if (!normalized || normalized === "?")
|
|
568
|
+
return null;
|
|
569
|
+
return normalized;
|
|
570
|
+
}
|
|
453
571
|
/**
|
|
454
572
|
* Calculate cyclomatic complexity of a node.
|
|
455
573
|
*/
|
|
@@ -25,7 +25,7 @@ const DEFAULT_OPTIONS = {
|
|
|
25
25
|
* // Output: "// → findByEmail, compare, sign | C:8 | ORCH"
|
|
26
26
|
*/
|
|
27
27
|
function formatSummary(metadata, options = {}) {
|
|
28
|
-
var _a;
|
|
28
|
+
var _a, _b;
|
|
29
29
|
const opts = Object.assign(Object.assign({}, DEFAULT_OPTIONS), options);
|
|
30
30
|
const parts = [];
|
|
31
31
|
// Calls (referenced symbols)
|
|
@@ -36,6 +36,13 @@ function formatSummary(metadata, options = {}) {
|
|
|
36
36
|
}
|
|
37
37
|
parts.push(`→ ${calls.join(", ")}`);
|
|
38
38
|
}
|
|
39
|
+
if ((_b = metadata.sqlTemplates) === null || _b === void 0 ? void 0 : _b.length) {
|
|
40
|
+
const templates = metadata.sqlTemplates.slice(0, 3);
|
|
41
|
+
if (metadata.sqlTemplates.length > templates.length) {
|
|
42
|
+
templates.push("...");
|
|
43
|
+
}
|
|
44
|
+
parts.push(`SQL: ${templates.join("; ")}`);
|
|
45
|
+
}
|
|
39
46
|
// Complexity (only if > 1, trivial functions don't need it)
|
|
40
47
|
if (opts.showComplexity && metadata.complexity && metadata.complexity > 1) {
|
|
41
48
|
parts.push(`C:${metadata.complexity}`);
|