@wrongstack/tools 0.306.0 → 0.306.3
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/builtin.js +256 -137
- package/dist/codebase-index/index.js +241 -137
- package/dist/codebase-index/project-server.js +233 -124
- package/dist/codebase-index/worker.js +210 -107
- package/dist/codebase-index/writer.d.ts +18 -0
- package/dist/index.js +258 -138
- package/dist/pack.js +256 -137
- package/dist/plan.js +15 -0
- package/dist/read.js +210 -110
- package/dist/session-kanban.js +3 -1
- package/dist/task.js +15 -0
- package/dist/todo.js +15 -0
- package/dist/tool-tier.js +256 -137
- package/package.json +4 -4
package/dist/builtin.js
CHANGED
|
@@ -11133,6 +11133,85 @@ function runSqliteWithRetry(fn) {
|
|
|
11133
11133
|
throw lastError;
|
|
11134
11134
|
}
|
|
11135
11135
|
|
|
11136
|
+
// src/codebase-index/vector-search.ts
|
|
11137
|
+
var RRF_K = 60;
|
|
11138
|
+
var VECTOR_DIMENSIONS = 384;
|
|
11139
|
+
var NGRAM_SIZE = 3;
|
|
11140
|
+
function embedText(text) {
|
|
11141
|
+
const vec = new Float32Array(VECTOR_DIMENSIONS);
|
|
11142
|
+
const normalized = text.toLowerCase().trim();
|
|
11143
|
+
if (normalized.length < NGRAM_SIZE) {
|
|
11144
|
+
const padded = ` ${normalized} `.slice(0, Math.max(NGRAM_SIZE, normalized.length + 2));
|
|
11145
|
+
for (let i = 0; i <= padded.length - NGRAM_SIZE; i++) {
|
|
11146
|
+
const ngram = padded.slice(i, i + NGRAM_SIZE);
|
|
11147
|
+
const bucket = hashNgram(ngram) % VECTOR_DIMENSIONS;
|
|
11148
|
+
vec[bucket] += 1;
|
|
11149
|
+
}
|
|
11150
|
+
} else {
|
|
11151
|
+
for (let i = 0; i <= normalized.length - NGRAM_SIZE; i++) {
|
|
11152
|
+
const ngram = normalized.slice(i, i + NGRAM_SIZE);
|
|
11153
|
+
const bucket = hashNgram(ngram) % VECTOR_DIMENSIONS;
|
|
11154
|
+
vec[bucket] += 1;
|
|
11155
|
+
}
|
|
11156
|
+
}
|
|
11157
|
+
let norm = 0;
|
|
11158
|
+
for (let i = 0; i < VECTOR_DIMENSIONS; i++) {
|
|
11159
|
+
norm += vec[i] * vec[i];
|
|
11160
|
+
}
|
|
11161
|
+
norm = Math.sqrt(norm);
|
|
11162
|
+
if (norm > 0) {
|
|
11163
|
+
for (let i = 0; i < VECTOR_DIMENSIONS; i++) {
|
|
11164
|
+
vec[i] /= norm;
|
|
11165
|
+
}
|
|
11166
|
+
}
|
|
11167
|
+
return vec;
|
|
11168
|
+
}
|
|
11169
|
+
function hashNgram(str) {
|
|
11170
|
+
let hash = 2166136261;
|
|
11171
|
+
for (let i = 0; i < str.length; i++) {
|
|
11172
|
+
hash ^= str.charCodeAt(i);
|
|
11173
|
+
hash = Math.imul(hash, 16777619);
|
|
11174
|
+
}
|
|
11175
|
+
return hash >>> 0;
|
|
11176
|
+
}
|
|
11177
|
+
function cosineSimilarity(a, b) {
|
|
11178
|
+
let dot = 0;
|
|
11179
|
+
const len = Math.min(a.length, b.length);
|
|
11180
|
+
for (let i = 0; i < len; i++) {
|
|
11181
|
+
dot += a[i] * b[i];
|
|
11182
|
+
}
|
|
11183
|
+
return dot;
|
|
11184
|
+
}
|
|
11185
|
+
function encodeVector(vec) {
|
|
11186
|
+
return Buffer.from(vec.buffer, vec.byteOffset, vec.byteLength);
|
|
11187
|
+
}
|
|
11188
|
+
function decodeVector(buf) {
|
|
11189
|
+
const view = new DataView(
|
|
11190
|
+
buf.buffer,
|
|
11191
|
+
buf.byteOffset,
|
|
11192
|
+
buf.byteLength
|
|
11193
|
+
);
|
|
11194
|
+
const copy = new Float32Array(buf.byteLength / 4);
|
|
11195
|
+
for (let i = 0; i < copy.length; i++) {
|
|
11196
|
+
copy[i] = view.getFloat32(i * 4, true);
|
|
11197
|
+
}
|
|
11198
|
+
return copy;
|
|
11199
|
+
}
|
|
11200
|
+
function reciprocalRankFusion(bm25Ranks, vectorRanks, k = RRF_K) {
|
|
11201
|
+
const allIds = /* @__PURE__ */ new Set([...bm25Ranks.keys(), ...vectorRanks.keys()]);
|
|
11202
|
+
const scored = [];
|
|
11203
|
+
for (const id of allIds) {
|
|
11204
|
+
const bm25Rank = bm25Ranks.get(id);
|
|
11205
|
+
const vecRank = vectorRanks.get(id);
|
|
11206
|
+
let score = 0;
|
|
11207
|
+
if (bm25Rank !== void 0) score += 1 / (k + bm25Rank);
|
|
11208
|
+
if (vecRank !== void 0) score += 1 / (k + vecRank);
|
|
11209
|
+
scored.push([id, score]);
|
|
11210
|
+
}
|
|
11211
|
+
scored.sort((a, b) => b[1] - a[1]);
|
|
11212
|
+
return scored;
|
|
11213
|
+
}
|
|
11214
|
+
|
|
11136
11215
|
// src/codebase-index/writer-admin.ts
|
|
11137
11216
|
import * as fs9 from "node:fs";
|
|
11138
11217
|
import * as path14 from "node:path";
|
|
@@ -12494,90 +12573,19 @@ var StorePool = class {
|
|
|
12494
12573
|
}
|
|
12495
12574
|
};
|
|
12496
12575
|
|
|
12497
|
-
// src/codebase-index/vector-search.ts
|
|
12498
|
-
var RRF_K = 60;
|
|
12499
|
-
var VECTOR_DIMENSIONS = 384;
|
|
12500
|
-
var NGRAM_SIZE = 3;
|
|
12501
|
-
function embedText(text) {
|
|
12502
|
-
const vec = new Float32Array(VECTOR_DIMENSIONS);
|
|
12503
|
-
const normalized = text.toLowerCase().trim();
|
|
12504
|
-
if (normalized.length < NGRAM_SIZE) {
|
|
12505
|
-
const padded = ` ${normalized} `.slice(0, Math.max(NGRAM_SIZE, normalized.length + 2));
|
|
12506
|
-
for (let i = 0; i <= padded.length - NGRAM_SIZE; i++) {
|
|
12507
|
-
const ngram = padded.slice(i, i + NGRAM_SIZE);
|
|
12508
|
-
const bucket = hashNgram(ngram) % VECTOR_DIMENSIONS;
|
|
12509
|
-
vec[bucket] += 1;
|
|
12510
|
-
}
|
|
12511
|
-
} else {
|
|
12512
|
-
for (let i = 0; i <= normalized.length - NGRAM_SIZE; i++) {
|
|
12513
|
-
const ngram = normalized.slice(i, i + NGRAM_SIZE);
|
|
12514
|
-
const bucket = hashNgram(ngram) % VECTOR_DIMENSIONS;
|
|
12515
|
-
vec[bucket] += 1;
|
|
12516
|
-
}
|
|
12517
|
-
}
|
|
12518
|
-
let norm = 0;
|
|
12519
|
-
for (let i = 0; i < VECTOR_DIMENSIONS; i++) {
|
|
12520
|
-
norm += vec[i] * vec[i];
|
|
12521
|
-
}
|
|
12522
|
-
norm = Math.sqrt(norm);
|
|
12523
|
-
if (norm > 0) {
|
|
12524
|
-
for (let i = 0; i < VECTOR_DIMENSIONS; i++) {
|
|
12525
|
-
vec[i] /= norm;
|
|
12526
|
-
}
|
|
12527
|
-
}
|
|
12528
|
-
return vec;
|
|
12529
|
-
}
|
|
12530
|
-
function hashNgram(str) {
|
|
12531
|
-
let hash = 2166136261;
|
|
12532
|
-
for (let i = 0; i < str.length; i++) {
|
|
12533
|
-
hash ^= str.charCodeAt(i);
|
|
12534
|
-
hash = Math.imul(hash, 16777619);
|
|
12535
|
-
}
|
|
12536
|
-
return hash >>> 0;
|
|
12537
|
-
}
|
|
12538
|
-
function cosineSimilarity(a, b) {
|
|
12539
|
-
let dot = 0;
|
|
12540
|
-
const len = Math.min(a.length, b.length);
|
|
12541
|
-
for (let i = 0; i < len; i++) {
|
|
12542
|
-
dot += a[i] * b[i];
|
|
12543
|
-
}
|
|
12544
|
-
return dot;
|
|
12545
|
-
}
|
|
12546
|
-
function encodeVector(vec) {
|
|
12547
|
-
return Buffer.from(vec.buffer, vec.byteOffset, vec.byteLength);
|
|
12548
|
-
}
|
|
12549
|
-
function decodeVector(buf) {
|
|
12550
|
-
const view = new DataView(
|
|
12551
|
-
buf.buffer,
|
|
12552
|
-
buf.byteOffset,
|
|
12553
|
-
buf.byteLength
|
|
12554
|
-
);
|
|
12555
|
-
const copy = new Float32Array(buf.byteLength / 4);
|
|
12556
|
-
for (let i = 0; i < copy.length; i++) {
|
|
12557
|
-
copy[i] = view.getFloat32(i * 4, true);
|
|
12558
|
-
}
|
|
12559
|
-
return copy;
|
|
12560
|
-
}
|
|
12561
|
-
function reciprocalRankFusion(bm25Ranks, vectorRanks, k = RRF_K) {
|
|
12562
|
-
const allIds = /* @__PURE__ */ new Set([...bm25Ranks.keys(), ...vectorRanks.keys()]);
|
|
12563
|
-
const scored = [];
|
|
12564
|
-
for (const id of allIds) {
|
|
12565
|
-
const bm25Rank = bm25Ranks.get(id);
|
|
12566
|
-
const vecRank = vectorRanks.get(id);
|
|
12567
|
-
let score = 0;
|
|
12568
|
-
if (bm25Rank !== void 0) score += 1 / (k + bm25Rank);
|
|
12569
|
-
if (vecRank !== void 0) score += 1 / (k + vecRank);
|
|
12570
|
-
scored.push([id, score]);
|
|
12571
|
-
}
|
|
12572
|
-
scored.sort((a, b) => b[1] - a[1]);
|
|
12573
|
-
return scored;
|
|
12574
|
-
}
|
|
12575
|
-
|
|
12576
12576
|
// src/codebase-index/writer.ts
|
|
12577
12577
|
var DB_FILE2 = "index.db";
|
|
12578
12578
|
var MAX_STATEMENT_CACHE = 128;
|
|
12579
12579
|
var IndexStore = class _IndexStore {
|
|
12580
12580
|
db;
|
|
12581
|
+
/**
|
|
12582
|
+
* True while an index run owns one outer SQLite transaction. Individual
|
|
12583
|
+
* writer methods normally protect themselves with BEGIN/COMMIT, but during
|
|
12584
|
+
* a refresh they join this transaction so readers observe either the last
|
|
12585
|
+
* completed index or the next completed index, never an in-between batch.
|
|
12586
|
+
*/
|
|
12587
|
+
atomicIndexUpdateActive = false;
|
|
12588
|
+
writeSavepointSequence = 0;
|
|
12581
12589
|
/** Absolute path to this project's index directory. */
|
|
12582
12590
|
indexDir;
|
|
12583
12591
|
/**
|
|
@@ -12658,6 +12666,51 @@ var IndexStore = class _IndexStore {
|
|
|
12658
12666
|
runWithRetry(fn) {
|
|
12659
12667
|
return runSqliteWithRetry(fn);
|
|
12660
12668
|
}
|
|
12669
|
+
/** Run a complete index mutation as one WAL-visible publication. */
|
|
12670
|
+
async runAtomicIndexUpdate(job) {
|
|
12671
|
+
if (this.atomicIndexUpdateActive) return job();
|
|
12672
|
+
this.runWithRetry(() => this.db.exec("BEGIN IMMEDIATE"));
|
|
12673
|
+
this.atomicIndexUpdateActive = true;
|
|
12674
|
+
try {
|
|
12675
|
+
const result = await job();
|
|
12676
|
+
this.db.exec("COMMIT");
|
|
12677
|
+
return result;
|
|
12678
|
+
} catch (error) {
|
|
12679
|
+
try {
|
|
12680
|
+
this.db.exec("ROLLBACK");
|
|
12681
|
+
} catch {
|
|
12682
|
+
}
|
|
12683
|
+
throw error;
|
|
12684
|
+
} finally {
|
|
12685
|
+
this.atomicIndexUpdateActive = false;
|
|
12686
|
+
}
|
|
12687
|
+
}
|
|
12688
|
+
/**
|
|
12689
|
+
* Begin a method-local transaction. Inside an atomic index publication a
|
|
12690
|
+
* SAVEPOINT preserves the old per-batch rollback boundary, which is needed
|
|
12691
|
+
* when commitBatch falls back to per-file writes after one batch fails.
|
|
12692
|
+
*/
|
|
12693
|
+
beginWriteTransaction() {
|
|
12694
|
+
if (this.atomicIndexUpdateActive) {
|
|
12695
|
+
const savepoint = `index_write_${++this.writeSavepointSequence}`;
|
|
12696
|
+
this.db.exec(`SAVEPOINT ${savepoint}`);
|
|
12697
|
+
return savepoint;
|
|
12698
|
+
}
|
|
12699
|
+
this.db.exec("BEGIN IMMEDIATE");
|
|
12700
|
+
return null;
|
|
12701
|
+
}
|
|
12702
|
+
commitWriteTransaction(savepoint) {
|
|
12703
|
+
if (savepoint) this.db.exec(`RELEASE SAVEPOINT ${savepoint}`);
|
|
12704
|
+
else this.db.exec("COMMIT");
|
|
12705
|
+
}
|
|
12706
|
+
rollbackWriteTransaction(savepoint) {
|
|
12707
|
+
if (savepoint) {
|
|
12708
|
+
this.db.exec(`ROLLBACK TO SAVEPOINT ${savepoint}`);
|
|
12709
|
+
this.db.exec(`RELEASE SAVEPOINT ${savepoint}`);
|
|
12710
|
+
} else {
|
|
12711
|
+
this.db.exec("ROLLBACK");
|
|
12712
|
+
}
|
|
12713
|
+
}
|
|
12661
12714
|
/**
|
|
12662
12715
|
* Mirror the in-process language→family map into SQLite.
|
|
12663
12716
|
*
|
|
@@ -12892,7 +12945,7 @@ var IndexStore = class _IndexStore {
|
|
|
12892
12945
|
insertSymbols(symbols) {
|
|
12893
12946
|
this.invalidateBm25();
|
|
12894
12947
|
return this.runWithRetry(() => {
|
|
12895
|
-
this.
|
|
12948
|
+
const ownsTransaction = this.beginWriteTransaction();
|
|
12896
12949
|
try {
|
|
12897
12950
|
let nextId = this.allocateSymbolIds(symbols.length);
|
|
12898
12951
|
const result = [];
|
|
@@ -12919,7 +12972,9 @@ var IndexStore = class _IndexStore {
|
|
|
12919
12972
|
}
|
|
12920
12973
|
vectorRows.push({
|
|
12921
12974
|
id,
|
|
12922
|
-
vector: encodeVector(
|
|
12975
|
+
vector: encodeVector(
|
|
12976
|
+
embedText(s.text || buildIndexableText(s.name, s.signature, s.docComment))
|
|
12977
|
+
)
|
|
12923
12978
|
});
|
|
12924
12979
|
result.push({ ...s, id });
|
|
12925
12980
|
}
|
|
@@ -12937,10 +12992,10 @@ var IndexStore = class _IndexStore {
|
|
|
12937
12992
|
vectorRows
|
|
12938
12993
|
);
|
|
12939
12994
|
}
|
|
12940
|
-
this.
|
|
12995
|
+
this.commitWriteTransaction(ownsTransaction);
|
|
12941
12996
|
return result;
|
|
12942
12997
|
} catch (err) {
|
|
12943
|
-
this.
|
|
12998
|
+
this.rollbackWriteTransaction(ownsTransaction);
|
|
12944
12999
|
throw err;
|
|
12945
13000
|
}
|
|
12946
13001
|
});
|
|
@@ -12948,7 +13003,7 @@ var IndexStore = class _IndexStore {
|
|
|
12948
13003
|
deleteSymbolsForFile(file) {
|
|
12949
13004
|
this.invalidateBm25();
|
|
12950
13005
|
this.runWithRetry(() => {
|
|
12951
|
-
this.
|
|
13006
|
+
const ownsTransaction = this.beginWriteTransaction();
|
|
12952
13007
|
try {
|
|
12953
13008
|
const affectedNames = this.invalidateIncomingRefsForFiles([file]);
|
|
12954
13009
|
if (this.ftsAvailable) {
|
|
@@ -12963,9 +13018,9 @@ var IndexStore = class _IndexStore {
|
|
|
12963
13018
|
}
|
|
12964
13019
|
this.stmt("DELETE FROM symbols WHERE file_fk = ?").run(file);
|
|
12965
13020
|
this.resolveRefsForNamesUnsafe(affectedNames);
|
|
12966
|
-
this.
|
|
13021
|
+
this.commitWriteTransaction(ownsTransaction);
|
|
12967
13022
|
} catch (error) {
|
|
12968
|
-
this.
|
|
13023
|
+
this.rollbackWriteTransaction(ownsTransaction);
|
|
12969
13024
|
throw error;
|
|
12970
13025
|
}
|
|
12971
13026
|
});
|
|
@@ -12978,7 +13033,7 @@ var IndexStore = class _IndexStore {
|
|
|
12978
13033
|
deleteFile(file) {
|
|
12979
13034
|
this.invalidateBm25();
|
|
12980
13035
|
this.runWithRetry(() => {
|
|
12981
|
-
this.
|
|
13036
|
+
const ownsTransaction = this.beginWriteTransaction();
|
|
12982
13037
|
try {
|
|
12983
13038
|
const affectedNames = this.invalidateIncomingRefsForFiles([file]);
|
|
12984
13039
|
if (this.ftsAvailable) {
|
|
@@ -12997,9 +13052,9 @@ var IndexStore = class _IndexStore {
|
|
|
12997
13052
|
this.stmt("DELETE FROM symbols WHERE file_fk = ?").run(file);
|
|
12998
13053
|
this.stmt("DELETE FROM files WHERE file = ?").run(file);
|
|
12999
13054
|
this.resolveRefsForNamesUnsafe(affectedNames);
|
|
13000
|
-
this.
|
|
13055
|
+
this.commitWriteTransaction(ownsTransaction);
|
|
13001
13056
|
} catch (err) {
|
|
13002
|
-
this.
|
|
13057
|
+
this.rollbackWriteTransaction(ownsTransaction);
|
|
13003
13058
|
throw err;
|
|
13004
13059
|
}
|
|
13005
13060
|
});
|
|
@@ -13386,7 +13441,7 @@ var IndexStore = class _IndexStore {
|
|
|
13386
13441
|
clearAll() {
|
|
13387
13442
|
this.invalidateBm25();
|
|
13388
13443
|
this.runWithRetry(() => {
|
|
13389
|
-
this.
|
|
13444
|
+
const ownsTransaction = this.beginWriteTransaction();
|
|
13390
13445
|
try {
|
|
13391
13446
|
this.db.exec("DROP TABLE IF EXISTS refs");
|
|
13392
13447
|
this.db.exec("DROP TABLE IF EXISTS symbols");
|
|
@@ -13394,15 +13449,15 @@ var IndexStore = class _IndexStore {
|
|
|
13394
13449
|
this.db.exec("DROP TABLE IF EXISTS metadata");
|
|
13395
13450
|
if (this.ftsAvailable) this.db.exec("DROP TABLE IF EXISTS symbols_fts");
|
|
13396
13451
|
this.db.exec("DROP TABLE IF EXISTS symbol_vectors");
|
|
13397
|
-
this.db.exec("COMMIT");
|
|
13398
13452
|
this.stmtCache.clear();
|
|
13399
13453
|
this.initSchema();
|
|
13400
13454
|
this.stmt("INSERT OR REPLACE INTO metadata(key, value) VALUES (?, ?)").run(
|
|
13401
13455
|
_IndexStore.NEXT_SYMBOL_ID_KEY,
|
|
13402
13456
|
"1"
|
|
13403
13457
|
);
|
|
13458
|
+
this.commitWriteTransaction(ownsTransaction);
|
|
13404
13459
|
} catch (err) {
|
|
13405
|
-
this.
|
|
13460
|
+
this.rollbackWriteTransaction(ownsTransaction);
|
|
13406
13461
|
throw err;
|
|
13407
13462
|
}
|
|
13408
13463
|
});
|
|
@@ -13464,7 +13519,7 @@ var IndexStore = class _IndexStore {
|
|
|
13464
13519
|
}
|
|
13465
13520
|
this.invalidateBm25();
|
|
13466
13521
|
return this.runWithRetry(() => {
|
|
13467
|
-
this.
|
|
13522
|
+
const ownsTransaction = this.beginWriteTransaction();
|
|
13468
13523
|
try {
|
|
13469
13524
|
const affectedNames = /* @__PURE__ */ new Set();
|
|
13470
13525
|
for (const entry of entries) {
|
|
@@ -13525,7 +13580,9 @@ var IndexStore = class _IndexStore {
|
|
|
13525
13580
|
}
|
|
13526
13581
|
vectorRows.push({
|
|
13527
13582
|
id,
|
|
13528
|
-
vector: encodeVector(
|
|
13583
|
+
vector: encodeVector(
|
|
13584
|
+
embedText(s.text || buildIndexableText(s.name, s.signature, s.docComment))
|
|
13585
|
+
)
|
|
13529
13586
|
});
|
|
13530
13587
|
const inserted = { ...s, id };
|
|
13531
13588
|
allInserted.push(inserted);
|
|
@@ -13570,10 +13627,10 @@ var IndexStore = class _IndexStore {
|
|
|
13570
13627
|
);
|
|
13571
13628
|
}
|
|
13572
13629
|
this.resolveRefsForNamesUnsafe(affectedNames);
|
|
13573
|
-
this.
|
|
13630
|
+
this.commitWriteTransaction(ownsTransaction);
|
|
13574
13631
|
return allInserted;
|
|
13575
13632
|
} catch (err) {
|
|
13576
|
-
this.
|
|
13633
|
+
this.rollbackWriteTransaction(ownsTransaction);
|
|
13577
13634
|
throw err;
|
|
13578
13635
|
}
|
|
13579
13636
|
});
|
|
@@ -13650,7 +13707,7 @@ var IndexStore = class _IndexStore {
|
|
|
13650
13707
|
replaceEmptyFile(meta) {
|
|
13651
13708
|
this.invalidateBm25();
|
|
13652
13709
|
this.runWithRetry(() => {
|
|
13653
|
-
this.
|
|
13710
|
+
const ownsTransaction = this.beginWriteTransaction();
|
|
13654
13711
|
try {
|
|
13655
13712
|
const affectedNames = this.invalidateIncomingRefsForFiles([meta.file]);
|
|
13656
13713
|
if (this.ftsAvailable) {
|
|
@@ -13685,9 +13742,9 @@ var IndexStore = class _IndexStore {
|
|
|
13685
13742
|
meta.lastIndexed
|
|
13686
13743
|
);
|
|
13687
13744
|
this.resolveRefsForNamesUnsafe(affectedNames);
|
|
13688
|
-
this.
|
|
13745
|
+
this.commitWriteTransaction(ownsTransaction);
|
|
13689
13746
|
} catch (err) {
|
|
13690
|
-
this.
|
|
13747
|
+
this.rollbackWriteTransaction(ownsTransaction);
|
|
13691
13748
|
throw err;
|
|
13692
13749
|
}
|
|
13693
13750
|
});
|
|
@@ -14611,6 +14668,7 @@ import { Worker as Worker2 } from "node:worker_threads";
|
|
|
14611
14668
|
// src/codebase-index/indexer.ts
|
|
14612
14669
|
import { expectDefined as expectDefined6 } from "@wrongstack/core/utils";
|
|
14613
14670
|
import { execFile } from "node:child_process";
|
|
14671
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
14614
14672
|
import * as fs18 from "node:fs/promises";
|
|
14615
14673
|
import { availableParallelism } from "node:os";
|
|
14616
14674
|
import * as path24 from "node:path";
|
|
@@ -15451,6 +15509,10 @@ var DEFAULT_IGNORE_FILES = /* @__PURE__ */ new Set(["package-lock.json", "pnpm-l
|
|
|
15451
15509
|
var INDEXABLE_EXTENSION_SET = new Set(INDEXABLE_EXTENSIONS);
|
|
15452
15510
|
var MAX_INDEX_FILE_BYTES = 5 * 1024 * 1024;
|
|
15453
15511
|
var MAX_GIT_FILE_LIST_BYTES = 64 * 1024 * 1024;
|
|
15512
|
+
var GIT_SNAPSHOT_METADATA_KEY = "git_discovery_snapshot";
|
|
15513
|
+
var IndexSourceChangedError = class extends Error {
|
|
15514
|
+
name = "IndexSourceChangedError";
|
|
15515
|
+
};
|
|
15454
15516
|
function isWithinProject(projectRoot, file) {
|
|
15455
15517
|
const rel = path24.relative(projectRoot, file);
|
|
15456
15518
|
return rel !== "" && !rel.startsWith(`..${path24.sep}`) && rel !== ".." && !path24.isAbsolute(rel);
|
|
@@ -15487,7 +15549,7 @@ async function findGitSourceFiles(projectRoot, ignore, signal) {
|
|
|
15487
15549
|
if (normalizeComparablePath(topLevel) !== normalizeComparablePath(projectRoot)) return null;
|
|
15488
15550
|
throwIfAborted(signal);
|
|
15489
15551
|
const ignoreSet = /* @__PURE__ */ new Set([...DEFAULT_IGNORE, ...ignore]);
|
|
15490
|
-
const [output, statusOutput] = await Promise.all([
|
|
15552
|
+
const [output, statusOutput, stagedOutput] = await Promise.all([
|
|
15491
15553
|
gitOutput(projectRoot, ["ls-files", "--cached", "--others", "--exclude-standard", "-z"]),
|
|
15492
15554
|
gitOutput(projectRoot, [
|
|
15493
15555
|
"status",
|
|
@@ -15495,7 +15557,8 @@ async function findGitSourceFiles(projectRoot, ignore, signal) {
|
|
|
15495
15557
|
"-z",
|
|
15496
15558
|
"--untracked-files=all",
|
|
15497
15559
|
"--ignored=no"
|
|
15498
|
-
])
|
|
15560
|
+
]),
|
|
15561
|
+
gitOutput(projectRoot, ["ls-files", "--stage", "-z"])
|
|
15499
15562
|
]);
|
|
15500
15563
|
throwIfAborted(signal);
|
|
15501
15564
|
const dirty = /* @__PURE__ */ new Set();
|
|
@@ -15525,9 +15588,17 @@ async function findGitSourceFiles(projectRoot, ignore, signal) {
|
|
|
15525
15588
|
const ext = path24.extname(relative12).toLowerCase();
|
|
15526
15589
|
if (INDEXABLE_EXTENSION_SET.has(ext) || detectLang(full) !== null) files.push(full);
|
|
15527
15590
|
}
|
|
15591
|
+
const snapshot = createHash5("sha256").update(stagedOutput).update("\0").update(statusOutput);
|
|
15592
|
+
const indexedFiles = new Set(files);
|
|
15593
|
+
for (const dirtyFile of [...dirty].sort()) {
|
|
15594
|
+
if (!indexedFiles.has(dirtyFile) || deleted.has(dirtyFile)) continue;
|
|
15595
|
+
snapshot.update("\0").update(dirtyFile).update("\0");
|
|
15596
|
+
snapshot.update(xxhash64String(await fs18.readFile(dirtyFile, "utf8")));
|
|
15597
|
+
}
|
|
15528
15598
|
return {
|
|
15529
15599
|
files,
|
|
15530
|
-
trustedUnchanged: new Set(files.filter((file) => !dirty.has(file)))
|
|
15600
|
+
trustedUnchanged: new Set(files.filter((file) => !dirty.has(file))),
|
|
15601
|
+
snapshotKey: snapshot.digest("hex")
|
|
15531
15602
|
};
|
|
15532
15603
|
} catch {
|
|
15533
15604
|
return null;
|
|
@@ -15540,7 +15611,8 @@ async function findSourceFiles(projectRoot, ignore, isGitIgnored, signal) {
|
|
|
15540
15611
|
files: gitFiles.files,
|
|
15541
15612
|
complete: true,
|
|
15542
15613
|
errors: [],
|
|
15543
|
-
trustedUnchanged: gitFiles.trustedUnchanged
|
|
15614
|
+
trustedUnchanged: gitFiles.trustedUnchanged,
|
|
15615
|
+
snapshotKey: gitFiles.snapshotKey
|
|
15544
15616
|
};
|
|
15545
15617
|
}
|
|
15546
15618
|
const results = [];
|
|
@@ -15627,6 +15699,17 @@ async function resolveProjectRelations(store, projectRoot, opts) {
|
|
|
15627
15699
|
}
|
|
15628
15700
|
}
|
|
15629
15701
|
async function runIndexerWithStore(store, opts) {
|
|
15702
|
+
let result;
|
|
15703
|
+
try {
|
|
15704
|
+
result = await store.runAtomicIndexUpdate(() => runIndexerAtomic(store, opts));
|
|
15705
|
+
} catch (error) {
|
|
15706
|
+
if (!(error instanceof IndexSourceChangedError)) throw error;
|
|
15707
|
+
result = await store.runAtomicIndexUpdate(() => runIndexerAtomic(store, opts));
|
|
15708
|
+
}
|
|
15709
|
+
if (!opts.files) store.compactIfNeeded();
|
|
15710
|
+
return result;
|
|
15711
|
+
}
|
|
15712
|
+
async function runIndexerAtomic(store, opts) {
|
|
15630
15713
|
const { projectRoot, langs, ignore = [], signal } = opts;
|
|
15631
15714
|
const relationGraphVersion = "2";
|
|
15632
15715
|
const refResolutionVersion = "2";
|
|
@@ -15646,6 +15729,7 @@ async function runIndexerWithStore(store, opts) {
|
|
|
15646
15729
|
let discoveredFiles = null;
|
|
15647
15730
|
let discoveryComplete = true;
|
|
15648
15731
|
let trustedUnchanged;
|
|
15732
|
+
let discoverySnapshotKey;
|
|
15649
15733
|
if (opts.files && opts.files.length > 0) {
|
|
15650
15734
|
files = opts.files.map((f) => path24.resolve(projectRoot, f)).filter((f) => {
|
|
15651
15735
|
if (!isWithinProject(projectRoot, f)) return false;
|
|
@@ -15659,6 +15743,7 @@ async function runIndexerWithStore(store, opts) {
|
|
|
15659
15743
|
discoveryComplete = discovery.complete;
|
|
15660
15744
|
discoveredFiles = new Set(files);
|
|
15661
15745
|
trustedUnchanged = discovery.trustedUnchanged;
|
|
15746
|
+
discoverySnapshotKey = discovery.snapshotKey;
|
|
15662
15747
|
}
|
|
15663
15748
|
if (langs && langs.length > 0) {
|
|
15664
15749
|
const langSet = new Set(langs);
|
|
@@ -15672,6 +15757,8 @@ async function runIndexerWithStore(store, opts) {
|
|
|
15672
15757
|
if (!force) {
|
|
15673
15758
|
for (const meta of store.getAllFileMetas()) existingMeta.set(meta.file, meta);
|
|
15674
15759
|
}
|
|
15760
|
+
const snapshotTrusted = !force && discoverySnapshotKey !== void 0 && store.getMetadata(GIT_SNAPSHOT_METADATA_KEY) === discoverySnapshotKey;
|
|
15761
|
+
if (!snapshotTrusted) trustedUnchanged = void 0;
|
|
15675
15762
|
const totalFilesForProgress = files.length;
|
|
15676
15763
|
let filesPreSkipped = 0;
|
|
15677
15764
|
if (!force && trustedUnchanged) {
|
|
@@ -15734,9 +15821,6 @@ async function runIndexerWithStore(store, opts) {
|
|
|
15734
15821
|
};
|
|
15735
15822
|
}
|
|
15736
15823
|
const meta = existingMeta.get(file);
|
|
15737
|
-
if (!force && meta && meta.mtimeMs === Math.floor(stat19.mtimeMs)) {
|
|
15738
|
-
return { file, stat: stat19, lang, parsed: null, skippedMeta: meta };
|
|
15739
|
-
}
|
|
15740
15824
|
let content;
|
|
15741
15825
|
try {
|
|
15742
15826
|
content = await fs18.readFile(file, { encoding: "utf8", signal });
|
|
@@ -15965,9 +16049,18 @@ async function runIndexerWithStore(store, opts) {
|
|
|
15965
16049
|
});
|
|
15966
16050
|
store.setMetadata("ref_resolution_version", refResolutionVersion);
|
|
15967
16051
|
store.setMetadata("relation_graph_version", relationGraphVersion);
|
|
16052
|
+
const completeProjectScope = !opts.files && (!langs || langs.length === 0) && (!opts.ignore || opts.ignore.length === 0);
|
|
16053
|
+
if (completeProjectScope && discoverySnapshotKey !== void 0) {
|
|
16054
|
+
const finalSnapshot = await findGitSourceFiles(projectRoot, ignore, signal);
|
|
16055
|
+
if (!finalSnapshot || finalSnapshot.snapshotKey !== discoverySnapshotKey) {
|
|
16056
|
+
throw new IndexSourceChangedError(
|
|
16057
|
+
"Project files changed during indexing; retrying before publishing the generation."
|
|
16058
|
+
);
|
|
16059
|
+
}
|
|
16060
|
+
store.setMetadata(GIT_SNAPSHOT_METADATA_KEY, errors.length === 0 ? discoverySnapshotKey : "");
|
|
16061
|
+
}
|
|
15968
16062
|
if (!opts.files || filesIndexed >= 50) store.optimize();
|
|
15969
16063
|
store.setLastIndexed(Date.now());
|
|
15970
|
-
if (!opts.files) store.compactIfNeeded();
|
|
15971
16064
|
const durationMs = Date.now() - startMs;
|
|
15972
16065
|
return {
|
|
15973
16066
|
filesIndexed,
|
|
@@ -16252,6 +16345,13 @@ function callIndexOp(op, args, opts) {
|
|
|
16252
16345
|
});
|
|
16253
16346
|
}
|
|
16254
16347
|
async function callInline(op, args, opts) {
|
|
16348
|
+
if (op !== "index" && _indexing) {
|
|
16349
|
+
const error = new Error(
|
|
16350
|
+
"Codebase index refresh in progress; retry after the completed generation is published."
|
|
16351
|
+
);
|
|
16352
|
+
error.name = "IndexRefreshInProgressError";
|
|
16353
|
+
throw error;
|
|
16354
|
+
}
|
|
16255
16355
|
const ac = new AbortController();
|
|
16256
16356
|
const onOuterAbort = () => ac.abort(opts.signal?.reason ?? new Error("Indexing cancelled"));
|
|
16257
16357
|
if (opts.signal?.aborted) onOuterAbort();
|
|
@@ -16479,12 +16579,12 @@ var codebaseSearchTool = {
|
|
|
16479
16579
|
},
|
|
16480
16580
|
async execute(input, ctx, execOpts) {
|
|
16481
16581
|
const state = getIndexState();
|
|
16482
|
-
if (state.indexing
|
|
16582
|
+
if (state.indexing) {
|
|
16483
16583
|
return {
|
|
16484
16584
|
results: [],
|
|
16485
16585
|
total: 0,
|
|
16486
16586
|
query: input.query,
|
|
16487
|
-
indexStatus: `
|
|
16587
|
+
indexStatus: `Index refresh in progress (${state.currentFile}/${state.totalFiles} files) \u2014 retry after the completed generation is published.`
|
|
16488
16588
|
};
|
|
16489
16589
|
}
|
|
16490
16590
|
if (state.lastError) {
|
|
@@ -16665,12 +16765,12 @@ var codebaseIncomingCallsTool = {
|
|
|
16665
16765
|
},
|
|
16666
16766
|
async execute(input, ctx) {
|
|
16667
16767
|
const state = getIndexState();
|
|
16668
|
-
if (state.indexing
|
|
16768
|
+
if (state.indexing) {
|
|
16669
16769
|
return {
|
|
16670
16770
|
symbol: input.symbol,
|
|
16671
16771
|
calls: [],
|
|
16672
16772
|
total: 0,
|
|
16673
|
-
indexStatus: `
|
|
16773
|
+
indexStatus: `Index refresh in progress (${state.currentFile}/${state.totalFiles} files) \u2014 retry after the completed generation is published.`
|
|
16674
16774
|
};
|
|
16675
16775
|
}
|
|
16676
16776
|
if (state.lastError) {
|
|
@@ -16687,16 +16787,14 @@ var codebaseIncomingCallsTool = {
|
|
|
16687
16787
|
const transitive = input.transitive === true;
|
|
16688
16788
|
let serviced;
|
|
16689
16789
|
try {
|
|
16690
|
-
serviced = await incomingCallsService2(
|
|
16691
|
-
|
|
16692
|
-
|
|
16693
|
-
|
|
16694
|
-
|
|
16695
|
-
|
|
16696
|
-
|
|
16697
|
-
|
|
16698
|
-
}
|
|
16699
|
-
);
|
|
16790
|
+
serviced = await incomingCallsService2({
|
|
16791
|
+
projectRoot: ctx.projectRoot,
|
|
16792
|
+
indexDir: codebaseIndexDirOverride(ctx),
|
|
16793
|
+
symbol: input.symbol,
|
|
16794
|
+
file: input.file,
|
|
16795
|
+
limit,
|
|
16796
|
+
transitive
|
|
16797
|
+
});
|
|
16700
16798
|
} catch (err) {
|
|
16701
16799
|
return {
|
|
16702
16800
|
symbol: input.symbol,
|
|
@@ -16735,10 +16833,14 @@ var codebaseIncomingCallsTool = {
|
|
|
16735
16833
|
}
|
|
16736
16834
|
const notes = [];
|
|
16737
16835
|
if (totalMatches > limit) {
|
|
16738
|
-
notes.push(
|
|
16836
|
+
notes.push(
|
|
16837
|
+
`Results capped at ${limit} of ${totalMatches} call sites. Increase \`limit\` or use \`file\` to narrow.`
|
|
16838
|
+
);
|
|
16739
16839
|
}
|
|
16740
16840
|
if (ambiguous) {
|
|
16741
|
-
notes.push(
|
|
16841
|
+
notes.push(
|
|
16842
|
+
`Symbol "${input.symbol}" exists in multiple files. Results include callers of all same-named symbols. Use codebase-search to find the exact file and pass it as \`file\`.`
|
|
16843
|
+
);
|
|
16742
16844
|
}
|
|
16743
16845
|
return {
|
|
16744
16846
|
symbol: input.symbol,
|
|
@@ -16788,12 +16890,12 @@ var codebaseOutgoingCallsTool = {
|
|
|
16788
16890
|
},
|
|
16789
16891
|
async execute(input, ctx) {
|
|
16790
16892
|
const state = getIndexState();
|
|
16791
|
-
if (state.indexing
|
|
16893
|
+
if (state.indexing) {
|
|
16792
16894
|
return {
|
|
16793
16895
|
symbol: input.symbol,
|
|
16794
16896
|
calls: [],
|
|
16795
16897
|
total: 0,
|
|
16796
|
-
indexStatus: `
|
|
16898
|
+
indexStatus: `Index refresh in progress (${state.currentFile}/${state.totalFiles} files) \u2014 retry after the completed generation is published.`
|
|
16797
16899
|
};
|
|
16798
16900
|
}
|
|
16799
16901
|
if (state.lastError) {
|
|
@@ -16810,16 +16912,14 @@ var codebaseOutgoingCallsTool = {
|
|
|
16810
16912
|
const transitive = input.transitive === true;
|
|
16811
16913
|
let serviced;
|
|
16812
16914
|
try {
|
|
16813
|
-
serviced = await outgoingCallsService2(
|
|
16814
|
-
|
|
16815
|
-
|
|
16816
|
-
|
|
16817
|
-
|
|
16818
|
-
|
|
16819
|
-
|
|
16820
|
-
|
|
16821
|
-
}
|
|
16822
|
-
);
|
|
16915
|
+
serviced = await outgoingCallsService2({
|
|
16916
|
+
projectRoot: ctx.projectRoot,
|
|
16917
|
+
indexDir: codebaseIndexDirOverride(ctx),
|
|
16918
|
+
symbol: input.symbol,
|
|
16919
|
+
file: input.file,
|
|
16920
|
+
limit,
|
|
16921
|
+
transitive
|
|
16922
|
+
});
|
|
16823
16923
|
} catch (err) {
|
|
16824
16924
|
return {
|
|
16825
16925
|
symbol: input.symbol,
|
|
@@ -16858,10 +16958,14 @@ var codebaseOutgoingCallsTool = {
|
|
|
16858
16958
|
}
|
|
16859
16959
|
const notes = [];
|
|
16860
16960
|
if (totalMatches > limit) {
|
|
16861
|
-
notes.push(
|
|
16961
|
+
notes.push(
|
|
16962
|
+
`Results capped at ${limit} of ${totalMatches} call sites. Increase \`limit\` or use \`file\` to narrow.`
|
|
16963
|
+
);
|
|
16862
16964
|
}
|
|
16863
16965
|
if (unresolvedCount > 0) {
|
|
16864
|
-
notes.push(
|
|
16966
|
+
notes.push(
|
|
16967
|
+
`${unresolvedCount} unresolved reference(s) not shown \u2014 their targets could not be resolved during indexing.`
|
|
16968
|
+
);
|
|
16865
16969
|
}
|
|
16866
16970
|
return {
|
|
16867
16971
|
symbol: input.symbol,
|
|
@@ -24447,6 +24551,7 @@ function sourceStatus(task) {
|
|
|
24447
24551
|
function todoStatus(task) {
|
|
24448
24552
|
const status = sourceStatus(task);
|
|
24449
24553
|
if (status === "completed") return "completed";
|
|
24554
|
+
if (status === "review" && task.assignment?.status === "completed") return "completed";
|
|
24450
24555
|
if (status === "in_progress" || status === "review") return "in_progress";
|
|
24451
24556
|
return "pending";
|
|
24452
24557
|
}
|
|
@@ -26398,6 +26503,20 @@ async function synchronizeManagedKanban(items, board, ctx, signal) {
|
|
|
26398
26503
|
}
|
|
26399
26504
|
const task = board.tasks.find((candidate) => candidate.id === item.kanbanTaskId);
|
|
26400
26505
|
if (!task || task.status === "completed") continue;
|
|
26506
|
+
const stage = task.lifecycle?.currentStage;
|
|
26507
|
+
if (stage === "backlog" || stage === "todo") {
|
|
26508
|
+
const started = await execute({
|
|
26509
|
+
action: "start_task",
|
|
26510
|
+
boardId: board.id,
|
|
26511
|
+
taskId: task.id,
|
|
26512
|
+
author: actor,
|
|
26513
|
+
agentId: actor,
|
|
26514
|
+
transitionComment: `Auto-started for completion: ${item.content}`
|
|
26515
|
+
});
|
|
26516
|
+
if (!started.ok) {
|
|
26517
|
+
continue;
|
|
26518
|
+
}
|
|
26519
|
+
}
|
|
26401
26520
|
await execute({
|
|
26402
26521
|
action: "mark_assignment",
|
|
26403
26522
|
boardId: board.id,
|