@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/plan.js
CHANGED
|
@@ -426,6 +426,7 @@ function sourceStatus(task) {
|
|
|
426
426
|
function todoStatus(task) {
|
|
427
427
|
const status = sourceStatus(task);
|
|
428
428
|
if (status === "completed") return "completed";
|
|
429
|
+
if (status === "review" && task.assignment?.status === "completed") return "completed";
|
|
429
430
|
if (status === "in_progress" || status === "review") return "in_progress";
|
|
430
431
|
return "pending";
|
|
431
432
|
}
|
|
@@ -2599,6 +2600,20 @@ async function synchronizeManagedKanban(items, board, ctx, signal) {
|
|
|
2599
2600
|
}
|
|
2600
2601
|
const task = board.tasks.find((candidate) => candidate.id === item.kanbanTaskId);
|
|
2601
2602
|
if (!task || task.status === "completed") continue;
|
|
2603
|
+
const stage = task.lifecycle?.currentStage;
|
|
2604
|
+
if (stage === "backlog" || stage === "todo") {
|
|
2605
|
+
const started = await execute({
|
|
2606
|
+
action: "start_task",
|
|
2607
|
+
boardId: board.id,
|
|
2608
|
+
taskId: task.id,
|
|
2609
|
+
author: actor,
|
|
2610
|
+
agentId: actor,
|
|
2611
|
+
transitionComment: `Auto-started for completion: ${item.content}`
|
|
2612
|
+
});
|
|
2613
|
+
if (!started.ok) {
|
|
2614
|
+
continue;
|
|
2615
|
+
}
|
|
2616
|
+
}
|
|
2602
2617
|
await execute({
|
|
2603
2618
|
action: "mark_assignment",
|
|
2604
2619
|
boardId: board.id,
|
package/dist/read.js
CHANGED
|
@@ -2858,6 +2858,7 @@ var indexCircuitBreaker = new IndexCircuitBreaker();
|
|
|
2858
2858
|
// src/codebase-index/indexer.ts
|
|
2859
2859
|
import { expectDefined as expectDefined5 } from "@wrongstack/core/utils";
|
|
2860
2860
|
import { execFile } from "node:child_process";
|
|
2861
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
2861
2862
|
import * as fs9 from "node:fs/promises";
|
|
2862
2863
|
import { availableParallelism } from "node:os";
|
|
2863
2864
|
import * as path13 from "node:path";
|
|
@@ -4178,6 +4179,85 @@ function runSqliteWithRetry(fn) {
|
|
|
4178
4179
|
throw lastError;
|
|
4179
4180
|
}
|
|
4180
4181
|
|
|
4182
|
+
// src/codebase-index/vector-search.ts
|
|
4183
|
+
var RRF_K = 60;
|
|
4184
|
+
var VECTOR_DIMENSIONS = 384;
|
|
4185
|
+
var NGRAM_SIZE = 3;
|
|
4186
|
+
function embedText(text) {
|
|
4187
|
+
const vec = new Float32Array(VECTOR_DIMENSIONS);
|
|
4188
|
+
const normalized = text.toLowerCase().trim();
|
|
4189
|
+
if (normalized.length < NGRAM_SIZE) {
|
|
4190
|
+
const padded = ` ${normalized} `.slice(0, Math.max(NGRAM_SIZE, normalized.length + 2));
|
|
4191
|
+
for (let i = 0; i <= padded.length - NGRAM_SIZE; i++) {
|
|
4192
|
+
const ngram = padded.slice(i, i + NGRAM_SIZE);
|
|
4193
|
+
const bucket = hashNgram(ngram) % VECTOR_DIMENSIONS;
|
|
4194
|
+
vec[bucket] += 1;
|
|
4195
|
+
}
|
|
4196
|
+
} else {
|
|
4197
|
+
for (let i = 0; i <= normalized.length - NGRAM_SIZE; i++) {
|
|
4198
|
+
const ngram = normalized.slice(i, i + NGRAM_SIZE);
|
|
4199
|
+
const bucket = hashNgram(ngram) % VECTOR_DIMENSIONS;
|
|
4200
|
+
vec[bucket] += 1;
|
|
4201
|
+
}
|
|
4202
|
+
}
|
|
4203
|
+
let norm = 0;
|
|
4204
|
+
for (let i = 0; i < VECTOR_DIMENSIONS; i++) {
|
|
4205
|
+
norm += vec[i] * vec[i];
|
|
4206
|
+
}
|
|
4207
|
+
norm = Math.sqrt(norm);
|
|
4208
|
+
if (norm > 0) {
|
|
4209
|
+
for (let i = 0; i < VECTOR_DIMENSIONS; i++) {
|
|
4210
|
+
vec[i] /= norm;
|
|
4211
|
+
}
|
|
4212
|
+
}
|
|
4213
|
+
return vec;
|
|
4214
|
+
}
|
|
4215
|
+
function hashNgram(str) {
|
|
4216
|
+
let hash = 2166136261;
|
|
4217
|
+
for (let i = 0; i < str.length; i++) {
|
|
4218
|
+
hash ^= str.charCodeAt(i);
|
|
4219
|
+
hash = Math.imul(hash, 16777619);
|
|
4220
|
+
}
|
|
4221
|
+
return hash >>> 0;
|
|
4222
|
+
}
|
|
4223
|
+
function cosineSimilarity(a, b) {
|
|
4224
|
+
let dot = 0;
|
|
4225
|
+
const len = Math.min(a.length, b.length);
|
|
4226
|
+
for (let i = 0; i < len; i++) {
|
|
4227
|
+
dot += a[i] * b[i];
|
|
4228
|
+
}
|
|
4229
|
+
return dot;
|
|
4230
|
+
}
|
|
4231
|
+
function encodeVector(vec) {
|
|
4232
|
+
return Buffer.from(vec.buffer, vec.byteOffset, vec.byteLength);
|
|
4233
|
+
}
|
|
4234
|
+
function decodeVector(buf) {
|
|
4235
|
+
const view = new DataView(
|
|
4236
|
+
buf.buffer,
|
|
4237
|
+
buf.byteOffset,
|
|
4238
|
+
buf.byteLength
|
|
4239
|
+
);
|
|
4240
|
+
const copy = new Float32Array(buf.byteLength / 4);
|
|
4241
|
+
for (let i = 0; i < copy.length; i++) {
|
|
4242
|
+
copy[i] = view.getFloat32(i * 4, true);
|
|
4243
|
+
}
|
|
4244
|
+
return copy;
|
|
4245
|
+
}
|
|
4246
|
+
function reciprocalRankFusion(bm25Ranks, vectorRanks, k = RRF_K) {
|
|
4247
|
+
const allIds = /* @__PURE__ */ new Set([...bm25Ranks.keys(), ...vectorRanks.keys()]);
|
|
4248
|
+
const scored = [];
|
|
4249
|
+
for (const id of allIds) {
|
|
4250
|
+
const bm25Rank = bm25Ranks.get(id);
|
|
4251
|
+
const vecRank = vectorRanks.get(id);
|
|
4252
|
+
let score = 0;
|
|
4253
|
+
if (bm25Rank !== void 0) score += 1 / (k + bm25Rank);
|
|
4254
|
+
if (vecRank !== void 0) score += 1 / (k + vecRank);
|
|
4255
|
+
scored.push([id, score]);
|
|
4256
|
+
}
|
|
4257
|
+
scored.sort((a, b) => b[1] - a[1]);
|
|
4258
|
+
return scored;
|
|
4259
|
+
}
|
|
4260
|
+
|
|
4181
4261
|
// src/codebase-index/writer-admin.ts
|
|
4182
4262
|
import * as fs7 from "node:fs";
|
|
4183
4263
|
import * as path11 from "node:path";
|
|
@@ -5237,90 +5317,19 @@ var StorePool = class {
|
|
|
5237
5317
|
}
|
|
5238
5318
|
};
|
|
5239
5319
|
|
|
5240
|
-
// src/codebase-index/vector-search.ts
|
|
5241
|
-
var RRF_K = 60;
|
|
5242
|
-
var VECTOR_DIMENSIONS = 384;
|
|
5243
|
-
var NGRAM_SIZE = 3;
|
|
5244
|
-
function embedText(text) {
|
|
5245
|
-
const vec = new Float32Array(VECTOR_DIMENSIONS);
|
|
5246
|
-
const normalized = text.toLowerCase().trim();
|
|
5247
|
-
if (normalized.length < NGRAM_SIZE) {
|
|
5248
|
-
const padded = ` ${normalized} `.slice(0, Math.max(NGRAM_SIZE, normalized.length + 2));
|
|
5249
|
-
for (let i = 0; i <= padded.length - NGRAM_SIZE; i++) {
|
|
5250
|
-
const ngram = padded.slice(i, i + NGRAM_SIZE);
|
|
5251
|
-
const bucket = hashNgram(ngram) % VECTOR_DIMENSIONS;
|
|
5252
|
-
vec[bucket] += 1;
|
|
5253
|
-
}
|
|
5254
|
-
} else {
|
|
5255
|
-
for (let i = 0; i <= normalized.length - NGRAM_SIZE; i++) {
|
|
5256
|
-
const ngram = normalized.slice(i, i + NGRAM_SIZE);
|
|
5257
|
-
const bucket = hashNgram(ngram) % VECTOR_DIMENSIONS;
|
|
5258
|
-
vec[bucket] += 1;
|
|
5259
|
-
}
|
|
5260
|
-
}
|
|
5261
|
-
let norm = 0;
|
|
5262
|
-
for (let i = 0; i < VECTOR_DIMENSIONS; i++) {
|
|
5263
|
-
norm += vec[i] * vec[i];
|
|
5264
|
-
}
|
|
5265
|
-
norm = Math.sqrt(norm);
|
|
5266
|
-
if (norm > 0) {
|
|
5267
|
-
for (let i = 0; i < VECTOR_DIMENSIONS; i++) {
|
|
5268
|
-
vec[i] /= norm;
|
|
5269
|
-
}
|
|
5270
|
-
}
|
|
5271
|
-
return vec;
|
|
5272
|
-
}
|
|
5273
|
-
function hashNgram(str) {
|
|
5274
|
-
let hash = 2166136261;
|
|
5275
|
-
for (let i = 0; i < str.length; i++) {
|
|
5276
|
-
hash ^= str.charCodeAt(i);
|
|
5277
|
-
hash = Math.imul(hash, 16777619);
|
|
5278
|
-
}
|
|
5279
|
-
return hash >>> 0;
|
|
5280
|
-
}
|
|
5281
|
-
function cosineSimilarity(a, b) {
|
|
5282
|
-
let dot = 0;
|
|
5283
|
-
const len = Math.min(a.length, b.length);
|
|
5284
|
-
for (let i = 0; i < len; i++) {
|
|
5285
|
-
dot += a[i] * b[i];
|
|
5286
|
-
}
|
|
5287
|
-
return dot;
|
|
5288
|
-
}
|
|
5289
|
-
function encodeVector(vec) {
|
|
5290
|
-
return Buffer.from(vec.buffer, vec.byteOffset, vec.byteLength);
|
|
5291
|
-
}
|
|
5292
|
-
function decodeVector(buf) {
|
|
5293
|
-
const view = new DataView(
|
|
5294
|
-
buf.buffer,
|
|
5295
|
-
buf.byteOffset,
|
|
5296
|
-
buf.byteLength
|
|
5297
|
-
);
|
|
5298
|
-
const copy = new Float32Array(buf.byteLength / 4);
|
|
5299
|
-
for (let i = 0; i < copy.length; i++) {
|
|
5300
|
-
copy[i] = view.getFloat32(i * 4, true);
|
|
5301
|
-
}
|
|
5302
|
-
return copy;
|
|
5303
|
-
}
|
|
5304
|
-
function reciprocalRankFusion(bm25Ranks, vectorRanks, k = RRF_K) {
|
|
5305
|
-
const allIds = /* @__PURE__ */ new Set([...bm25Ranks.keys(), ...vectorRanks.keys()]);
|
|
5306
|
-
const scored = [];
|
|
5307
|
-
for (const id of allIds) {
|
|
5308
|
-
const bm25Rank = bm25Ranks.get(id);
|
|
5309
|
-
const vecRank = vectorRanks.get(id);
|
|
5310
|
-
let score = 0;
|
|
5311
|
-
if (bm25Rank !== void 0) score += 1 / (k + bm25Rank);
|
|
5312
|
-
if (vecRank !== void 0) score += 1 / (k + vecRank);
|
|
5313
|
-
scored.push([id, score]);
|
|
5314
|
-
}
|
|
5315
|
-
scored.sort((a, b) => b[1] - a[1]);
|
|
5316
|
-
return scored;
|
|
5317
|
-
}
|
|
5318
|
-
|
|
5319
5320
|
// src/codebase-index/writer.ts
|
|
5320
5321
|
var DB_FILE2 = "index.db";
|
|
5321
5322
|
var MAX_STATEMENT_CACHE = 128;
|
|
5322
5323
|
var IndexStore = class _IndexStore {
|
|
5323
5324
|
db;
|
|
5325
|
+
/**
|
|
5326
|
+
* True while an index run owns one outer SQLite transaction. Individual
|
|
5327
|
+
* writer methods normally protect themselves with BEGIN/COMMIT, but during
|
|
5328
|
+
* a refresh they join this transaction so readers observe either the last
|
|
5329
|
+
* completed index or the next completed index, never an in-between batch.
|
|
5330
|
+
*/
|
|
5331
|
+
atomicIndexUpdateActive = false;
|
|
5332
|
+
writeSavepointSequence = 0;
|
|
5324
5333
|
/** Absolute path to this project's index directory. */
|
|
5325
5334
|
indexDir;
|
|
5326
5335
|
/**
|
|
@@ -5401,6 +5410,51 @@ var IndexStore = class _IndexStore {
|
|
|
5401
5410
|
runWithRetry(fn) {
|
|
5402
5411
|
return runSqliteWithRetry(fn);
|
|
5403
5412
|
}
|
|
5413
|
+
/** Run a complete index mutation as one WAL-visible publication. */
|
|
5414
|
+
async runAtomicIndexUpdate(job) {
|
|
5415
|
+
if (this.atomicIndexUpdateActive) return job();
|
|
5416
|
+
this.runWithRetry(() => this.db.exec("BEGIN IMMEDIATE"));
|
|
5417
|
+
this.atomicIndexUpdateActive = true;
|
|
5418
|
+
try {
|
|
5419
|
+
const result = await job();
|
|
5420
|
+
this.db.exec("COMMIT");
|
|
5421
|
+
return result;
|
|
5422
|
+
} catch (error) {
|
|
5423
|
+
try {
|
|
5424
|
+
this.db.exec("ROLLBACK");
|
|
5425
|
+
} catch {
|
|
5426
|
+
}
|
|
5427
|
+
throw error;
|
|
5428
|
+
} finally {
|
|
5429
|
+
this.atomicIndexUpdateActive = false;
|
|
5430
|
+
}
|
|
5431
|
+
}
|
|
5432
|
+
/**
|
|
5433
|
+
* Begin a method-local transaction. Inside an atomic index publication a
|
|
5434
|
+
* SAVEPOINT preserves the old per-batch rollback boundary, which is needed
|
|
5435
|
+
* when commitBatch falls back to per-file writes after one batch fails.
|
|
5436
|
+
*/
|
|
5437
|
+
beginWriteTransaction() {
|
|
5438
|
+
if (this.atomicIndexUpdateActive) {
|
|
5439
|
+
const savepoint = `index_write_${++this.writeSavepointSequence}`;
|
|
5440
|
+
this.db.exec(`SAVEPOINT ${savepoint}`);
|
|
5441
|
+
return savepoint;
|
|
5442
|
+
}
|
|
5443
|
+
this.db.exec("BEGIN IMMEDIATE");
|
|
5444
|
+
return null;
|
|
5445
|
+
}
|
|
5446
|
+
commitWriteTransaction(savepoint) {
|
|
5447
|
+
if (savepoint) this.db.exec(`RELEASE SAVEPOINT ${savepoint}`);
|
|
5448
|
+
else this.db.exec("COMMIT");
|
|
5449
|
+
}
|
|
5450
|
+
rollbackWriteTransaction(savepoint) {
|
|
5451
|
+
if (savepoint) {
|
|
5452
|
+
this.db.exec(`ROLLBACK TO SAVEPOINT ${savepoint}`);
|
|
5453
|
+
this.db.exec(`RELEASE SAVEPOINT ${savepoint}`);
|
|
5454
|
+
} else {
|
|
5455
|
+
this.db.exec("ROLLBACK");
|
|
5456
|
+
}
|
|
5457
|
+
}
|
|
5404
5458
|
/**
|
|
5405
5459
|
* Mirror the in-process language→family map into SQLite.
|
|
5406
5460
|
*
|
|
@@ -5635,7 +5689,7 @@ var IndexStore = class _IndexStore {
|
|
|
5635
5689
|
insertSymbols(symbols) {
|
|
5636
5690
|
this.invalidateBm25();
|
|
5637
5691
|
return this.runWithRetry(() => {
|
|
5638
|
-
this.
|
|
5692
|
+
const ownsTransaction = this.beginWriteTransaction();
|
|
5639
5693
|
try {
|
|
5640
5694
|
let nextId = this.allocateSymbolIds(symbols.length);
|
|
5641
5695
|
const result = [];
|
|
@@ -5662,7 +5716,9 @@ var IndexStore = class _IndexStore {
|
|
|
5662
5716
|
}
|
|
5663
5717
|
vectorRows.push({
|
|
5664
5718
|
id,
|
|
5665
|
-
vector: encodeVector(
|
|
5719
|
+
vector: encodeVector(
|
|
5720
|
+
embedText(s.text || buildIndexableText(s.name, s.signature, s.docComment))
|
|
5721
|
+
)
|
|
5666
5722
|
});
|
|
5667
5723
|
result.push({ ...s, id });
|
|
5668
5724
|
}
|
|
@@ -5680,10 +5736,10 @@ var IndexStore = class _IndexStore {
|
|
|
5680
5736
|
vectorRows
|
|
5681
5737
|
);
|
|
5682
5738
|
}
|
|
5683
|
-
this.
|
|
5739
|
+
this.commitWriteTransaction(ownsTransaction);
|
|
5684
5740
|
return result;
|
|
5685
5741
|
} catch (err) {
|
|
5686
|
-
this.
|
|
5742
|
+
this.rollbackWriteTransaction(ownsTransaction);
|
|
5687
5743
|
throw err;
|
|
5688
5744
|
}
|
|
5689
5745
|
});
|
|
@@ -5691,7 +5747,7 @@ var IndexStore = class _IndexStore {
|
|
|
5691
5747
|
deleteSymbolsForFile(file) {
|
|
5692
5748
|
this.invalidateBm25();
|
|
5693
5749
|
this.runWithRetry(() => {
|
|
5694
|
-
this.
|
|
5750
|
+
const ownsTransaction = this.beginWriteTransaction();
|
|
5695
5751
|
try {
|
|
5696
5752
|
const affectedNames = this.invalidateIncomingRefsForFiles([file]);
|
|
5697
5753
|
if (this.ftsAvailable) {
|
|
@@ -5706,9 +5762,9 @@ var IndexStore = class _IndexStore {
|
|
|
5706
5762
|
}
|
|
5707
5763
|
this.stmt("DELETE FROM symbols WHERE file_fk = ?").run(file);
|
|
5708
5764
|
this.resolveRefsForNamesUnsafe(affectedNames);
|
|
5709
|
-
this.
|
|
5765
|
+
this.commitWriteTransaction(ownsTransaction);
|
|
5710
5766
|
} catch (error) {
|
|
5711
|
-
this.
|
|
5767
|
+
this.rollbackWriteTransaction(ownsTransaction);
|
|
5712
5768
|
throw error;
|
|
5713
5769
|
}
|
|
5714
5770
|
});
|
|
@@ -5721,7 +5777,7 @@ var IndexStore = class _IndexStore {
|
|
|
5721
5777
|
deleteFile(file) {
|
|
5722
5778
|
this.invalidateBm25();
|
|
5723
5779
|
this.runWithRetry(() => {
|
|
5724
|
-
this.
|
|
5780
|
+
const ownsTransaction = this.beginWriteTransaction();
|
|
5725
5781
|
try {
|
|
5726
5782
|
const affectedNames = this.invalidateIncomingRefsForFiles([file]);
|
|
5727
5783
|
if (this.ftsAvailable) {
|
|
@@ -5740,9 +5796,9 @@ var IndexStore = class _IndexStore {
|
|
|
5740
5796
|
this.stmt("DELETE FROM symbols WHERE file_fk = ?").run(file);
|
|
5741
5797
|
this.stmt("DELETE FROM files WHERE file = ?").run(file);
|
|
5742
5798
|
this.resolveRefsForNamesUnsafe(affectedNames);
|
|
5743
|
-
this.
|
|
5799
|
+
this.commitWriteTransaction(ownsTransaction);
|
|
5744
5800
|
} catch (err) {
|
|
5745
|
-
this.
|
|
5801
|
+
this.rollbackWriteTransaction(ownsTransaction);
|
|
5746
5802
|
throw err;
|
|
5747
5803
|
}
|
|
5748
5804
|
});
|
|
@@ -6129,7 +6185,7 @@ var IndexStore = class _IndexStore {
|
|
|
6129
6185
|
clearAll() {
|
|
6130
6186
|
this.invalidateBm25();
|
|
6131
6187
|
this.runWithRetry(() => {
|
|
6132
|
-
this.
|
|
6188
|
+
const ownsTransaction = this.beginWriteTransaction();
|
|
6133
6189
|
try {
|
|
6134
6190
|
this.db.exec("DROP TABLE IF EXISTS refs");
|
|
6135
6191
|
this.db.exec("DROP TABLE IF EXISTS symbols");
|
|
@@ -6137,15 +6193,15 @@ var IndexStore = class _IndexStore {
|
|
|
6137
6193
|
this.db.exec("DROP TABLE IF EXISTS metadata");
|
|
6138
6194
|
if (this.ftsAvailable) this.db.exec("DROP TABLE IF EXISTS symbols_fts");
|
|
6139
6195
|
this.db.exec("DROP TABLE IF EXISTS symbol_vectors");
|
|
6140
|
-
this.db.exec("COMMIT");
|
|
6141
6196
|
this.stmtCache.clear();
|
|
6142
6197
|
this.initSchema();
|
|
6143
6198
|
this.stmt("INSERT OR REPLACE INTO metadata(key, value) VALUES (?, ?)").run(
|
|
6144
6199
|
_IndexStore.NEXT_SYMBOL_ID_KEY,
|
|
6145
6200
|
"1"
|
|
6146
6201
|
);
|
|
6202
|
+
this.commitWriteTransaction(ownsTransaction);
|
|
6147
6203
|
} catch (err) {
|
|
6148
|
-
this.
|
|
6204
|
+
this.rollbackWriteTransaction(ownsTransaction);
|
|
6149
6205
|
throw err;
|
|
6150
6206
|
}
|
|
6151
6207
|
});
|
|
@@ -6207,7 +6263,7 @@ var IndexStore = class _IndexStore {
|
|
|
6207
6263
|
}
|
|
6208
6264
|
this.invalidateBm25();
|
|
6209
6265
|
return this.runWithRetry(() => {
|
|
6210
|
-
this.
|
|
6266
|
+
const ownsTransaction = this.beginWriteTransaction();
|
|
6211
6267
|
try {
|
|
6212
6268
|
const affectedNames = /* @__PURE__ */ new Set();
|
|
6213
6269
|
for (const entry of entries) {
|
|
@@ -6268,7 +6324,9 @@ var IndexStore = class _IndexStore {
|
|
|
6268
6324
|
}
|
|
6269
6325
|
vectorRows.push({
|
|
6270
6326
|
id,
|
|
6271
|
-
vector: encodeVector(
|
|
6327
|
+
vector: encodeVector(
|
|
6328
|
+
embedText(s.text || buildIndexableText(s.name, s.signature, s.docComment))
|
|
6329
|
+
)
|
|
6272
6330
|
});
|
|
6273
6331
|
const inserted = { ...s, id };
|
|
6274
6332
|
allInserted.push(inserted);
|
|
@@ -6313,10 +6371,10 @@ var IndexStore = class _IndexStore {
|
|
|
6313
6371
|
);
|
|
6314
6372
|
}
|
|
6315
6373
|
this.resolveRefsForNamesUnsafe(affectedNames);
|
|
6316
|
-
this.
|
|
6374
|
+
this.commitWriteTransaction(ownsTransaction);
|
|
6317
6375
|
return allInserted;
|
|
6318
6376
|
} catch (err) {
|
|
6319
|
-
this.
|
|
6377
|
+
this.rollbackWriteTransaction(ownsTransaction);
|
|
6320
6378
|
throw err;
|
|
6321
6379
|
}
|
|
6322
6380
|
});
|
|
@@ -6393,7 +6451,7 @@ var IndexStore = class _IndexStore {
|
|
|
6393
6451
|
replaceEmptyFile(meta) {
|
|
6394
6452
|
this.invalidateBm25();
|
|
6395
6453
|
this.runWithRetry(() => {
|
|
6396
|
-
this.
|
|
6454
|
+
const ownsTransaction = this.beginWriteTransaction();
|
|
6397
6455
|
try {
|
|
6398
6456
|
const affectedNames = this.invalidateIncomingRefsForFiles([meta.file]);
|
|
6399
6457
|
if (this.ftsAvailable) {
|
|
@@ -6428,9 +6486,9 @@ var IndexStore = class _IndexStore {
|
|
|
6428
6486
|
meta.lastIndexed
|
|
6429
6487
|
);
|
|
6430
6488
|
this.resolveRefsForNamesUnsafe(affectedNames);
|
|
6431
|
-
this.
|
|
6489
|
+
this.commitWriteTransaction(ownsTransaction);
|
|
6432
6490
|
} catch (err) {
|
|
6433
|
-
this.
|
|
6491
|
+
this.rollbackWriteTransaction(ownsTransaction);
|
|
6434
6492
|
throw err;
|
|
6435
6493
|
}
|
|
6436
6494
|
});
|
|
@@ -6623,6 +6681,10 @@ var DEFAULT_IGNORE_FILES = /* @__PURE__ */ new Set(["package-lock.json", "pnpm-l
|
|
|
6623
6681
|
var INDEXABLE_EXTENSION_SET = new Set(INDEXABLE_EXTENSIONS);
|
|
6624
6682
|
var MAX_INDEX_FILE_BYTES = 5 * 1024 * 1024;
|
|
6625
6683
|
var MAX_GIT_FILE_LIST_BYTES = 64 * 1024 * 1024;
|
|
6684
|
+
var GIT_SNAPSHOT_METADATA_KEY = "git_discovery_snapshot";
|
|
6685
|
+
var IndexSourceChangedError = class extends Error {
|
|
6686
|
+
name = "IndexSourceChangedError";
|
|
6687
|
+
};
|
|
6626
6688
|
function isWithinProject(projectRoot, file) {
|
|
6627
6689
|
const rel = path13.relative(projectRoot, file);
|
|
6628
6690
|
return rel !== "" && !rel.startsWith(`..${path13.sep}`) && rel !== ".." && !path13.isAbsolute(rel);
|
|
@@ -6659,7 +6721,7 @@ async function findGitSourceFiles(projectRoot, ignore, signal) {
|
|
|
6659
6721
|
if (normalizeComparablePath(topLevel) !== normalizeComparablePath(projectRoot)) return null;
|
|
6660
6722
|
throwIfAborted(signal);
|
|
6661
6723
|
const ignoreSet = /* @__PURE__ */ new Set([...DEFAULT_IGNORE, ...ignore]);
|
|
6662
|
-
const [output, statusOutput] = await Promise.all([
|
|
6724
|
+
const [output, statusOutput, stagedOutput] = await Promise.all([
|
|
6663
6725
|
gitOutput(projectRoot, ["ls-files", "--cached", "--others", "--exclude-standard", "-z"]),
|
|
6664
6726
|
gitOutput(projectRoot, [
|
|
6665
6727
|
"status",
|
|
@@ -6667,7 +6729,8 @@ async function findGitSourceFiles(projectRoot, ignore, signal) {
|
|
|
6667
6729
|
"-z",
|
|
6668
6730
|
"--untracked-files=all",
|
|
6669
6731
|
"--ignored=no"
|
|
6670
|
-
])
|
|
6732
|
+
]),
|
|
6733
|
+
gitOutput(projectRoot, ["ls-files", "--stage", "-z"])
|
|
6671
6734
|
]);
|
|
6672
6735
|
throwIfAborted(signal);
|
|
6673
6736
|
const dirty = /* @__PURE__ */ new Set();
|
|
@@ -6697,9 +6760,17 @@ async function findGitSourceFiles(projectRoot, ignore, signal) {
|
|
|
6697
6760
|
const ext = path13.extname(relative3).toLowerCase();
|
|
6698
6761
|
if (INDEXABLE_EXTENSION_SET.has(ext) || detectLang(full) !== null) files.push(full);
|
|
6699
6762
|
}
|
|
6763
|
+
const snapshot = createHash2("sha256").update(stagedOutput).update("\0").update(statusOutput);
|
|
6764
|
+
const indexedFiles = new Set(files);
|
|
6765
|
+
for (const dirtyFile of [...dirty].sort()) {
|
|
6766
|
+
if (!indexedFiles.has(dirtyFile) || deleted.has(dirtyFile)) continue;
|
|
6767
|
+
snapshot.update("\0").update(dirtyFile).update("\0");
|
|
6768
|
+
snapshot.update(xxhash64String(await fs9.readFile(dirtyFile, "utf8")));
|
|
6769
|
+
}
|
|
6700
6770
|
return {
|
|
6701
6771
|
files,
|
|
6702
|
-
trustedUnchanged: new Set(files.filter((file) => !dirty.has(file)))
|
|
6772
|
+
trustedUnchanged: new Set(files.filter((file) => !dirty.has(file))),
|
|
6773
|
+
snapshotKey: snapshot.digest("hex")
|
|
6703
6774
|
};
|
|
6704
6775
|
} catch {
|
|
6705
6776
|
return null;
|
|
@@ -6712,7 +6783,8 @@ async function findSourceFiles(projectRoot, ignore, isGitIgnored, signal) {
|
|
|
6712
6783
|
files: gitFiles.files,
|
|
6713
6784
|
complete: true,
|
|
6714
6785
|
errors: [],
|
|
6715
|
-
trustedUnchanged: gitFiles.trustedUnchanged
|
|
6786
|
+
trustedUnchanged: gitFiles.trustedUnchanged,
|
|
6787
|
+
snapshotKey: gitFiles.snapshotKey
|
|
6716
6788
|
};
|
|
6717
6789
|
}
|
|
6718
6790
|
const results = [];
|
|
@@ -6799,6 +6871,17 @@ async function resolveProjectRelations(store, projectRoot, opts) {
|
|
|
6799
6871
|
}
|
|
6800
6872
|
}
|
|
6801
6873
|
async function runIndexerWithStore(store, opts) {
|
|
6874
|
+
let result;
|
|
6875
|
+
try {
|
|
6876
|
+
result = await store.runAtomicIndexUpdate(() => runIndexerAtomic(store, opts));
|
|
6877
|
+
} catch (error) {
|
|
6878
|
+
if (!(error instanceof IndexSourceChangedError)) throw error;
|
|
6879
|
+
result = await store.runAtomicIndexUpdate(() => runIndexerAtomic(store, opts));
|
|
6880
|
+
}
|
|
6881
|
+
if (!opts.files) store.compactIfNeeded();
|
|
6882
|
+
return result;
|
|
6883
|
+
}
|
|
6884
|
+
async function runIndexerAtomic(store, opts) {
|
|
6802
6885
|
const { projectRoot, langs, ignore = [], signal } = opts;
|
|
6803
6886
|
const relationGraphVersion = "2";
|
|
6804
6887
|
const refResolutionVersion = "2";
|
|
@@ -6818,6 +6901,7 @@ async function runIndexerWithStore(store, opts) {
|
|
|
6818
6901
|
let discoveredFiles = null;
|
|
6819
6902
|
let discoveryComplete = true;
|
|
6820
6903
|
let trustedUnchanged;
|
|
6904
|
+
let discoverySnapshotKey;
|
|
6821
6905
|
if (opts.files && opts.files.length > 0) {
|
|
6822
6906
|
files = opts.files.map((f) => path13.resolve(projectRoot, f)).filter((f) => {
|
|
6823
6907
|
if (!isWithinProject(projectRoot, f)) return false;
|
|
@@ -6831,6 +6915,7 @@ async function runIndexerWithStore(store, opts) {
|
|
|
6831
6915
|
discoveryComplete = discovery.complete;
|
|
6832
6916
|
discoveredFiles = new Set(files);
|
|
6833
6917
|
trustedUnchanged = discovery.trustedUnchanged;
|
|
6918
|
+
discoverySnapshotKey = discovery.snapshotKey;
|
|
6834
6919
|
}
|
|
6835
6920
|
if (langs && langs.length > 0) {
|
|
6836
6921
|
const langSet = new Set(langs);
|
|
@@ -6844,6 +6929,8 @@ async function runIndexerWithStore(store, opts) {
|
|
|
6844
6929
|
if (!force) {
|
|
6845
6930
|
for (const meta of store.getAllFileMetas()) existingMeta.set(meta.file, meta);
|
|
6846
6931
|
}
|
|
6932
|
+
const snapshotTrusted = !force && discoverySnapshotKey !== void 0 && store.getMetadata(GIT_SNAPSHOT_METADATA_KEY) === discoverySnapshotKey;
|
|
6933
|
+
if (!snapshotTrusted) trustedUnchanged = void 0;
|
|
6847
6934
|
const totalFilesForProgress = files.length;
|
|
6848
6935
|
let filesPreSkipped = 0;
|
|
6849
6936
|
if (!force && trustedUnchanged) {
|
|
@@ -6906,9 +6993,6 @@ async function runIndexerWithStore(store, opts) {
|
|
|
6906
6993
|
};
|
|
6907
6994
|
}
|
|
6908
6995
|
const meta = existingMeta.get(file);
|
|
6909
|
-
if (!force && meta && meta.mtimeMs === Math.floor(stat3.mtimeMs)) {
|
|
6910
|
-
return { file, stat: stat3, lang, parsed: null, skippedMeta: meta };
|
|
6911
|
-
}
|
|
6912
6996
|
let content;
|
|
6913
6997
|
try {
|
|
6914
6998
|
content = await fs9.readFile(file, { encoding: "utf8", signal });
|
|
@@ -7137,9 +7221,18 @@ async function runIndexerWithStore(store, opts) {
|
|
|
7137
7221
|
});
|
|
7138
7222
|
store.setMetadata("ref_resolution_version", refResolutionVersion);
|
|
7139
7223
|
store.setMetadata("relation_graph_version", relationGraphVersion);
|
|
7224
|
+
const completeProjectScope = !opts.files && (!langs || langs.length === 0) && (!opts.ignore || opts.ignore.length === 0);
|
|
7225
|
+
if (completeProjectScope && discoverySnapshotKey !== void 0) {
|
|
7226
|
+
const finalSnapshot = await findGitSourceFiles(projectRoot, ignore, signal);
|
|
7227
|
+
if (!finalSnapshot || finalSnapshot.snapshotKey !== discoverySnapshotKey) {
|
|
7228
|
+
throw new IndexSourceChangedError(
|
|
7229
|
+
"Project files changed during indexing; retrying before publishing the generation."
|
|
7230
|
+
);
|
|
7231
|
+
}
|
|
7232
|
+
store.setMetadata(GIT_SNAPSHOT_METADATA_KEY, errors.length === 0 ? discoverySnapshotKey : "");
|
|
7233
|
+
}
|
|
7140
7234
|
if (!opts.files || filesIndexed >= 50) store.optimize();
|
|
7141
7235
|
store.setLastIndexed(Date.now());
|
|
7142
|
-
if (!opts.files) store.compactIfNeeded();
|
|
7143
7236
|
const durationMs = Date.now() - startMs;
|
|
7144
7237
|
return {
|
|
7145
7238
|
filesIndexed,
|
|
@@ -7272,7 +7365,7 @@ function decodeBinaryFrame(payload) {
|
|
|
7272
7365
|
}
|
|
7273
7366
|
|
|
7274
7367
|
// src/codebase-index/project-server-endpoint.ts
|
|
7275
|
-
import { createHash as
|
|
7368
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
7276
7369
|
import * as fs10 from "node:fs";
|
|
7277
7370
|
import * as os3 from "node:os";
|
|
7278
7371
|
import * as path14 from "node:path";
|
|
@@ -7290,7 +7383,7 @@ function projectIndexServerBuildId(entrypoint) {
|
|
|
7290
7383
|
if (buildIdCache?.file === file && buildIdCache.mtimeMs === stat3.mtimeMs && buildIdCache.size === stat3.size) {
|
|
7291
7384
|
return buildIdCache.buildId;
|
|
7292
7385
|
}
|
|
7293
|
-
const buildId =
|
|
7386
|
+
const buildId = createHash3("sha256").update(fs10.readFileSync(file)).digest("hex").slice(0, 24);
|
|
7294
7387
|
buildIdCache = { file, mtimeMs: stat3.mtimeMs, size: stat3.size, buildId };
|
|
7295
7388
|
return buildId;
|
|
7296
7389
|
} catch {
|
|
@@ -7303,7 +7396,7 @@ function normalizeLocalPath(value) {
|
|
|
7303
7396
|
}
|
|
7304
7397
|
function projectIndexServerKey(projectRoot, indexDir) {
|
|
7305
7398
|
const resolvedIndexDir = normalizeLocalPath(resolveIndexDir(projectRoot, indexDir));
|
|
7306
|
-
return
|
|
7399
|
+
return createHash3("sha256").update(resolvedIndexDir).digest("hex").slice(0, 24);
|
|
7307
7400
|
}
|
|
7308
7401
|
function projectIndexServerEndpoint(projectRoot, indexDir) {
|
|
7309
7402
|
const key = projectIndexServerKey(projectRoot, indexDir);
|
|
@@ -8192,6 +8285,13 @@ function callIndexOp(op, args, opts) {
|
|
|
8192
8285
|
});
|
|
8193
8286
|
}
|
|
8194
8287
|
async function callInline(op, args, opts) {
|
|
8288
|
+
if (op !== "index" && _indexing) {
|
|
8289
|
+
const error = new Error(
|
|
8290
|
+
"Codebase index refresh in progress; retry after the completed generation is published."
|
|
8291
|
+
);
|
|
8292
|
+
error.name = "IndexRefreshInProgressError";
|
|
8293
|
+
throw error;
|
|
8294
|
+
}
|
|
8195
8295
|
const ac = new AbortController();
|
|
8196
8296
|
const onOuterAbort = () => ac.abort(opts.signal?.reason ?? new Error("Indexing cancelled"));
|
|
8197
8297
|
if (opts.signal?.aborted) onOuterAbort();
|
package/dist/session-kanban.js
CHANGED
|
@@ -770,6 +770,7 @@ function sourceStatus(task) {
|
|
|
770
770
|
function todoStatus(task) {
|
|
771
771
|
const status = sourceStatus(task);
|
|
772
772
|
if (status === "completed") return "completed";
|
|
773
|
+
if (status === "review" && task.assignment?.status === "completed") return "completed";
|
|
773
774
|
if (status === "in_progress" || status === "review") return "in_progress";
|
|
774
775
|
return "pending";
|
|
775
776
|
}
|
|
@@ -891,11 +892,12 @@ async function applySessionKanbanTaskToSource(context, task, options = {}) {
|
|
|
891
892
|
const graphId = task.origin?.graphId ?? "";
|
|
892
893
|
if (!originId) return { source: null };
|
|
893
894
|
if (task.origin?.system === "session-todo" || graphId.startsWith("todo:")) {
|
|
895
|
+
const mappedStatus = todoStatus(task);
|
|
894
896
|
const next = options.remove ? context.todos.filter((todo) => todo.id !== originId) : context.todos.map(
|
|
895
897
|
(todo) => todo.id === originId ? {
|
|
896
898
|
...todo,
|
|
897
899
|
content: task.title,
|
|
898
|
-
status:
|
|
900
|
+
status: mappedStatus
|
|
899
901
|
} : todo
|
|
900
902
|
);
|
|
901
903
|
suppressedTodoMirrors.add(context);
|
package/dist/task.js
CHANGED
|
@@ -386,6 +386,7 @@ function sourceStatus(task) {
|
|
|
386
386
|
function todoStatus(task) {
|
|
387
387
|
const status = sourceStatus(task);
|
|
388
388
|
if (status === "completed") return "completed";
|
|
389
|
+
if (status === "review" && task.assignment?.status === "completed") return "completed";
|
|
389
390
|
if (status === "in_progress" || status === "review") return "in_progress";
|
|
390
391
|
return "pending";
|
|
391
392
|
}
|
|
@@ -2559,6 +2560,20 @@ async function synchronizeManagedKanban(items, board, ctx, signal) {
|
|
|
2559
2560
|
}
|
|
2560
2561
|
const task = board.tasks.find((candidate) => candidate.id === item.kanbanTaskId);
|
|
2561
2562
|
if (!task || task.status === "completed") continue;
|
|
2563
|
+
const stage = task.lifecycle?.currentStage;
|
|
2564
|
+
if (stage === "backlog" || stage === "todo") {
|
|
2565
|
+
const started = await execute({
|
|
2566
|
+
action: "start_task",
|
|
2567
|
+
boardId: board.id,
|
|
2568
|
+
taskId: task.id,
|
|
2569
|
+
author: actor,
|
|
2570
|
+
agentId: actor,
|
|
2571
|
+
transitionComment: `Auto-started for completion: ${item.content}`
|
|
2572
|
+
});
|
|
2573
|
+
if (!started.ok) {
|
|
2574
|
+
continue;
|
|
2575
|
+
}
|
|
2576
|
+
}
|
|
2562
2577
|
await execute({
|
|
2563
2578
|
action: "mark_assignment",
|
|
2564
2579
|
boardId: board.id,
|
package/dist/todo.js
CHANGED
|
@@ -1437,6 +1437,7 @@ function sourceStatus(task) {
|
|
|
1437
1437
|
function todoStatus(task) {
|
|
1438
1438
|
const status = sourceStatus(task);
|
|
1439
1439
|
if (status === "completed") return "completed";
|
|
1440
|
+
if (status === "review" && task.assignment?.status === "completed") return "completed";
|
|
1440
1441
|
if (status === "in_progress" || status === "review") return "in_progress";
|
|
1441
1442
|
return "pending";
|
|
1442
1443
|
}
|
|
@@ -2546,6 +2547,20 @@ async function synchronizeManagedKanban(items, board, ctx, signal) {
|
|
|
2546
2547
|
}
|
|
2547
2548
|
const task = board.tasks.find((candidate) => candidate.id === item.kanbanTaskId);
|
|
2548
2549
|
if (!task || task.status === "completed") continue;
|
|
2550
|
+
const stage = task.lifecycle?.currentStage;
|
|
2551
|
+
if (stage === "backlog" || stage === "todo") {
|
|
2552
|
+
const started = await execute({
|
|
2553
|
+
action: "start_task",
|
|
2554
|
+
boardId: board.id,
|
|
2555
|
+
taskId: task.id,
|
|
2556
|
+
author: actor,
|
|
2557
|
+
agentId: actor,
|
|
2558
|
+
transitionComment: `Auto-started for completion: ${item.content}`
|
|
2559
|
+
});
|
|
2560
|
+
if (!started.ok) {
|
|
2561
|
+
continue;
|
|
2562
|
+
}
|
|
2563
|
+
}
|
|
2549
2564
|
await execute({
|
|
2550
2565
|
action: "mark_assignment",
|
|
2551
2566
|
boardId: board.id,
|