@wrongstack/tools 0.289.0 → 0.291.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auto-proceed-loop-guard.d.ts +128 -0
- package/dist/auto-proceed-loop-guard.d.ts.map +1 -0
- package/dist/auto-proceed-loop-guard.js +46 -0
- package/dist/auto-proceed-loop-guard.js.map +7 -0
- package/dist/builtin.js +198 -113
- package/dist/builtin.js.map +3 -3
- package/dist/codebase-index/background-indexer.d.ts +1 -1
- package/dist/codebase-index/background-indexer.d.ts.map +1 -1
- package/dist/codebase-index/index.js +181 -104
- package/dist/codebase-index/index.js.map +3 -3
- package/dist/codebase-index/indexer.d.ts.map +1 -1
- package/dist/codebase-index/refs-extractor.d.ts +2 -17
- package/dist/codebase-index/refs-extractor.d.ts.map +1 -1
- package/dist/codebase-index/ts-parser.d.ts.map +1 -1
- package/dist/codebase-index/worker.js +164 -96
- package/dist/codebase-index/worker.js.map +3 -3
- package/dist/codebase-index/writer.d.ts +33 -0
- package/dist/codebase-index/writer.d.ts.map +1 -1
- package/dist/index.js +275 -124
- package/dist/index.js.map +3 -3
- package/dist/kanban.d.ts +10 -0
- package/dist/kanban.d.ts.map +1 -1
- package/dist/kanban.js +34 -17
- package/dist/kanban.js.map +2 -2
- package/dist/pack.js +198 -113
- package/dist/pack.js.map +3 -3
- package/dist/plan.js.map +2 -2
- package/dist/read.d.ts.map +1 -1
- package/dist/read.js.map +2 -2
- package/dist/session-kanban.d.ts.map +1 -1
- package/dist/session-kanban.js +60 -3
- package/dist/session-kanban.js.map +2 -2
- package/dist/task.js.map +2 -2
- package/dist/todo.js.map +2 -2
- package/package.json +9 -5
package/dist/index.js
CHANGED
|
@@ -8099,6 +8099,53 @@ function codebaseIndexDirOverride(ctx) {
|
|
|
8099
8099
|
const v = ctx.meta?.["codebaseIndexDir"];
|
|
8100
8100
|
return typeof v === "string" ? v : void 0;
|
|
8101
8101
|
}
|
|
8102
|
+
var StorePool = class {
|
|
8103
|
+
stores = /* @__PURE__ */ new Map();
|
|
8104
|
+
key(projectRoot, indexDir) {
|
|
8105
|
+
return `${projectRoot}\0${indexDir ?? ""}`;
|
|
8106
|
+
}
|
|
8107
|
+
/** Borrow a store. Creates it on first access for this key. */
|
|
8108
|
+
acquire(projectRoot, opts) {
|
|
8109
|
+
const k = this.key(projectRoot, opts?.indexDir);
|
|
8110
|
+
let store = this.stores.get(k);
|
|
8111
|
+
if (!store) {
|
|
8112
|
+
store = new IndexStore(projectRoot, { indexDir: opts?.indexDir });
|
|
8113
|
+
this.stores.set(k, store);
|
|
8114
|
+
}
|
|
8115
|
+
return store;
|
|
8116
|
+
}
|
|
8117
|
+
/** Return the store to the pool. The connection stays warm for subsequent
|
|
8118
|
+
* operations on the same (projectRoot, indexDir). */
|
|
8119
|
+
release(_store) {
|
|
8120
|
+
}
|
|
8121
|
+
/** Close every pooled connection and drain the pool. Call on shutdown. */
|
|
8122
|
+
closeAll() {
|
|
8123
|
+
for (const store of this.stores.values()) {
|
|
8124
|
+
try {
|
|
8125
|
+
store.close();
|
|
8126
|
+
} catch {
|
|
8127
|
+
}
|
|
8128
|
+
}
|
|
8129
|
+
this.stores.clear();
|
|
8130
|
+
}
|
|
8131
|
+
/** Remove one store from the pool. Used by tests that need isolation. */
|
|
8132
|
+
evict(projectRoot, indexDir) {
|
|
8133
|
+
const k = this.key(projectRoot, indexDir);
|
|
8134
|
+
const store = this.stores.get(k);
|
|
8135
|
+
if (store) {
|
|
8136
|
+
try {
|
|
8137
|
+
store.close();
|
|
8138
|
+
} catch {
|
|
8139
|
+
}
|
|
8140
|
+
this.stores.delete(k);
|
|
8141
|
+
}
|
|
8142
|
+
}
|
|
8143
|
+
/** True when the pool holds a connection for the given key. */
|
|
8144
|
+
has(projectRoot, indexDir) {
|
|
8145
|
+
return this.stores.has(this.key(projectRoot, indexDir));
|
|
8146
|
+
}
|
|
8147
|
+
};
|
|
8148
|
+
var indexStorePool = new StorePool();
|
|
8102
8149
|
var warningSilenced = false;
|
|
8103
8150
|
function silenceSqliteExperimentalWarning() {
|
|
8104
8151
|
if (warningSilenced) return;
|
|
@@ -8316,7 +8363,7 @@ var IndexStore = class _IndexStore {
|
|
|
8316
8363
|
return this.runWithRetry(() => {
|
|
8317
8364
|
this.db.exec("BEGIN IMMEDIATE");
|
|
8318
8365
|
try {
|
|
8319
|
-
const maxRows = this.
|
|
8366
|
+
const maxRows = this.stmt("SELECT MAX(id) AS m FROM symbols").all();
|
|
8320
8367
|
let nextId = (maxRows[0]?.m ?? 0) + 1;
|
|
8321
8368
|
const stmt = this.db.prepare(
|
|
8322
8369
|
`INSERT INTO symbols(id, lang, kind, name, file, line, col, signature, doc_comment, scope, text, file_fk)
|
|
@@ -8588,7 +8635,7 @@ var IndexStore = class _IndexStore {
|
|
|
8588
8635
|
return { results, total: candidates.length };
|
|
8589
8636
|
}
|
|
8590
8637
|
getAllIndexable() {
|
|
8591
|
-
return this.
|
|
8638
|
+
return this.stmt("SELECT id, text FROM symbols").all().map(({ id, text }) => ({ id, text }));
|
|
8592
8639
|
}
|
|
8593
8640
|
/**
|
|
8594
8641
|
* Largest symbol id currently in the table (0 when empty). New ids must be
|
|
@@ -8598,7 +8645,7 @@ var IndexStore = class _IndexStore {
|
|
|
8598
8645
|
* `symbols.id`). Ids may have gaps — that is fine.
|
|
8599
8646
|
*/
|
|
8600
8647
|
getMaxSymbolId() {
|
|
8601
|
-
const rows = this.
|
|
8648
|
+
const rows = this.stmt("SELECT MAX(id) AS m FROM symbols").all();
|
|
8602
8649
|
return rows[0]?.m ?? 0;
|
|
8603
8650
|
}
|
|
8604
8651
|
// ─── Stats ───────────────────────────────────────────────────────────────────
|
|
@@ -8606,14 +8653,14 @@ var IndexStore = class _IndexStore {
|
|
|
8606
8653
|
const sizeBytes = this.sizeBytes();
|
|
8607
8654
|
const lastRows = this.db.prepare("SELECT value FROM metadata WHERE key = 'last_indexed'").all();
|
|
8608
8655
|
const lastIndexed = lastRows.length ? Number(lastRows[0]?.value) : null;
|
|
8609
|
-
const totalRows = this.
|
|
8656
|
+
const totalRows = this.stmt("SELECT COUNT(*) FROM symbols").all();
|
|
8610
8657
|
const totalSymbols = totalRows[0] ? Number(totalRows[0]["COUNT(*)"]) : 0;
|
|
8611
|
-
const fileRows = this.
|
|
8658
|
+
const fileRows = this.stmt("SELECT COUNT(*) FROM files").all();
|
|
8612
8659
|
const totalFiles = fileRows[0] ? Number(fileRows[0]["COUNT(*)"]) : 0;
|
|
8613
|
-
const langRows = this.
|
|
8660
|
+
const langRows = this.stmt("SELECT lang, COUNT(*) FROM symbols GROUP BY lang").all();
|
|
8614
8661
|
const byLang = {};
|
|
8615
8662
|
for (const row of langRows) byLang[row.lang] = Number(row["COUNT(*)"]);
|
|
8616
|
-
const kindRows = this.
|
|
8663
|
+
const kindRows = this.stmt("SELECT kind, COUNT(*) FROM symbols GROUP BY kind").all();
|
|
8617
8664
|
const byKind = {};
|
|
8618
8665
|
for (const row of kindRows) byKind[row.kind] = Number(row["COUNT(*)"]);
|
|
8619
8666
|
return {
|
|
@@ -8645,11 +8692,14 @@ var IndexStore = class _IndexStore {
|
|
|
8645
8692
|
this.runWithRetry(() => {
|
|
8646
8693
|
this.db.exec("BEGIN IMMEDIATE");
|
|
8647
8694
|
try {
|
|
8648
|
-
this.db.exec("
|
|
8649
|
-
this.db.exec("
|
|
8650
|
-
this.db.exec("
|
|
8651
|
-
|
|
8695
|
+
this.db.exec("DROP TABLE IF EXISTS refs");
|
|
8696
|
+
this.db.exec("DROP TABLE IF EXISTS symbols");
|
|
8697
|
+
this.db.exec("DROP TABLE IF EXISTS files");
|
|
8698
|
+
this.db.exec("DROP TABLE IF EXISTS metadata");
|
|
8699
|
+
if (this.ftsAvailable) this.db.exec("DROP TABLE IF EXISTS symbols_fts");
|
|
8652
8700
|
this.db.exec("COMMIT");
|
|
8701
|
+
this.stmtCache.clear();
|
|
8702
|
+
this.initSchema();
|
|
8653
8703
|
} catch (err) {
|
|
8654
8704
|
this.db.exec("ROLLBACK");
|
|
8655
8705
|
throw err;
|
|
@@ -8734,7 +8784,7 @@ var IndexStore = class _IndexStore {
|
|
|
8734
8784
|
).run(...options.deleteForFiles);
|
|
8735
8785
|
this.db.prepare(`DELETE FROM symbols WHERE file IN (${placeholders})`).run(...options.deleteForFiles);
|
|
8736
8786
|
}
|
|
8737
|
-
const maxRows = this.
|
|
8787
|
+
const maxRows = this.stmt("SELECT MAX(id) AS m FROM symbols").all();
|
|
8738
8788
|
let nextId = (maxRows[0]?.m ?? 0) + 1;
|
|
8739
8789
|
const symStmt = this.db.prepare(
|
|
8740
8790
|
`INSERT INTO symbols(id, lang, kind, name, file, line, col, signature, doc_comment, scope, text, file_fk)
|
|
@@ -8918,7 +8968,7 @@ var IndexStore = class _IndexStore {
|
|
|
8918
8968
|
* symbol resolved in package B). Node metadata includes symbol/file counts.
|
|
8919
8969
|
*/
|
|
8920
8970
|
getPackageGraph() {
|
|
8921
|
-
const symbols = this.db.prepare("SELECT file, id
|
|
8971
|
+
const symbols = this.db.prepare("SELECT file, id FROM symbols ORDER BY id").all();
|
|
8922
8972
|
const pkgNodes = /* @__PURE__ */ new Map();
|
|
8923
8973
|
const fileToPkg = /* @__PURE__ */ new Map();
|
|
8924
8974
|
const symbolToPkg = /* @__PURE__ */ new Map();
|
|
@@ -9009,15 +9059,18 @@ var IndexStore = class _IndexStore {
|
|
|
9009
9059
|
* derived from cross-file symbol references within the package.
|
|
9010
9060
|
*/
|
|
9011
9061
|
getFileGraph(packageFilter) {
|
|
9012
|
-
const
|
|
9013
|
-
const
|
|
9014
|
-
|
|
9015
|
-
);
|
|
9016
|
-
|
|
9062
|
+
const allFiles = this.db.prepare("SELECT DISTINCT file FROM symbols").all();
|
|
9063
|
+
const pkgFilePaths = allFiles.filter((f) => (_IndexStore.derivePackage(f.file) ?? "(root)") === packageFilter).map((f) => f.file);
|
|
9064
|
+
const localFiles = new Set(pkgFilePaths);
|
|
9065
|
+
if (localFiles.size === 0) return { nodes: [], edges: [] };
|
|
9066
|
+
const filePlaceholders = [...localFiles].map(() => "?").join(",");
|
|
9067
|
+
const pkgSyms = this.db.prepare(
|
|
9068
|
+
`SELECT file, id, name, kind, lang, line FROM symbols WHERE file IN (${filePlaceholders}) ORDER BY id`
|
|
9069
|
+
).all(...pkgFilePaths);
|
|
9017
9070
|
const fileNodes = /* @__PURE__ */ new Map();
|
|
9018
9071
|
const symToFile = /* @__PURE__ */ new Map();
|
|
9019
9072
|
const fileStats = /* @__PURE__ */ new Map();
|
|
9020
|
-
for (const s of
|
|
9073
|
+
for (const s of pkgSyms) {
|
|
9021
9074
|
symToFile.set(s.id, s.file);
|
|
9022
9075
|
const current = fileStats.get(s.file);
|
|
9023
9076
|
fileStats.set(s.file, {
|
|
@@ -9025,8 +9078,6 @@ var IndexStore = class _IndexStore {
|
|
|
9025
9078
|
lang: current?.lang ?? s.lang
|
|
9026
9079
|
});
|
|
9027
9080
|
}
|
|
9028
|
-
const localFiles = new Set(pkgSyms.map((s) => s.file));
|
|
9029
|
-
const indexedFiles = new Set(allSymbols.map((s) => s.file));
|
|
9030
9081
|
const ensureFileNode = (file) => {
|
|
9031
9082
|
if (fileNodes.has(file)) return;
|
|
9032
9083
|
const stats = fileStats.get(file);
|
|
@@ -9044,11 +9095,32 @@ var IndexStore = class _IndexStore {
|
|
|
9044
9095
|
for (const file of localFiles) {
|
|
9045
9096
|
ensureFileNode(file);
|
|
9046
9097
|
}
|
|
9098
|
+
const indexedFiles = new Set(allFiles.map((f) => f.file));
|
|
9047
9099
|
const refRows = this.db.prepare(
|
|
9048
9100
|
`SELECT r.from_id, r.to_id, r.call_type
|
|
9049
9101
|
FROM refs r
|
|
9050
|
-
WHERE r.
|
|
9051
|
-
|
|
9102
|
+
WHERE (r.from_id IN (SELECT id FROM symbols WHERE file IN (${filePlaceholders}))
|
|
9103
|
+
OR r.to_id IN (SELECT id FROM symbols WHERE file IN (${filePlaceholders})))
|
|
9104
|
+
AND r.to_id IS NOT NULL`
|
|
9105
|
+
).all(...pkgFilePaths, ...pkgFilePaths);
|
|
9106
|
+
const knownSymIds = new Set(pkgSyms.map((s) => s.id));
|
|
9107
|
+
const crossRefIds = /* @__PURE__ */ new Set();
|
|
9108
|
+
for (const r of refRows) {
|
|
9109
|
+
if (!knownSymIds.has(r.from_id)) crossRefIds.add(r.from_id);
|
|
9110
|
+
if (!knownSymIds.has(r.to_id)) crossRefIds.add(r.to_id);
|
|
9111
|
+
}
|
|
9112
|
+
if (crossRefIds.size > 0) {
|
|
9113
|
+
const crossPlaceholders = [...crossRefIds].map(() => "?").join(",");
|
|
9114
|
+
const extras = this.db.prepare(
|
|
9115
|
+
`SELECT id, file FROM symbols WHERE id IN (${crossPlaceholders})`
|
|
9116
|
+
).all(...crossRefIds);
|
|
9117
|
+
for (const x of extras) {
|
|
9118
|
+
symToFile.set(x.id, x.file);
|
|
9119
|
+
if (!fileStats.has(x.file)) {
|
|
9120
|
+
fileStats.set(x.file, { count: 0, lang: "ts" });
|
|
9121
|
+
}
|
|
9122
|
+
}
|
|
9123
|
+
}
|
|
9052
9124
|
const edgeMap = /* @__PURE__ */ new Map();
|
|
9053
9125
|
for (const r of refRows) {
|
|
9054
9126
|
if (r.call_type === "import") continue;
|
|
@@ -9070,8 +9142,9 @@ var IndexStore = class _IndexStore {
|
|
|
9070
9142
|
const importRows = this.db.prepare(
|
|
9071
9143
|
`SELECT r.from_id, r.to_name
|
|
9072
9144
|
FROM refs r
|
|
9073
|
-
WHERE r.call_type = 'import'
|
|
9074
|
-
|
|
9145
|
+
WHERE r.call_type = 'import'
|
|
9146
|
+
AND r.from_id IN (SELECT id FROM symbols WHERE file IN (${filePlaceholders}))`
|
|
9147
|
+
).all(...pkgFilePaths);
|
|
9075
9148
|
for (const r of importRows) {
|
|
9076
9149
|
const fromFile = symToFile.get(r.from_id);
|
|
9077
9150
|
if (!fromFile || !localFiles.has(fromFile)) continue;
|
|
@@ -9113,12 +9186,11 @@ var IndexStore = class _IndexStore {
|
|
|
9113
9186
|
* derived from intra-file and cross-file symbol references (who calls whom).
|
|
9114
9187
|
*/
|
|
9115
9188
|
getSymbolGraph(fileFilter) {
|
|
9116
|
-
const
|
|
9117
|
-
"SELECT id, name, kind, lang, file, line, signature, scope FROM symbols ORDER BY
|
|
9118
|
-
).all();
|
|
9119
|
-
const syms = allSymbols.filter((symbol) => symbol.file === fileFilter);
|
|
9189
|
+
const syms = this.db.prepare(
|
|
9190
|
+
"SELECT id, name, kind, lang, file, line, signature, scope FROM symbols WHERE file = ? ORDER BY line, id"
|
|
9191
|
+
).all(fileFilter);
|
|
9120
9192
|
if (syms.length === 0) return { nodes: [], edges: [] };
|
|
9121
|
-
const symById = new Map(
|
|
9193
|
+
const symById = new Map(syms.map((symbol) => [symbol.id, symbol]));
|
|
9122
9194
|
const relatedIds = new Set(syms.map((symbol) => symbol.id));
|
|
9123
9195
|
const toGraphNode = (s) => ({
|
|
9124
9196
|
id: `sym:${s.id}`,
|
|
@@ -9174,6 +9246,15 @@ var IndexStore = class _IndexStore {
|
|
|
9174
9246
|
refType: bestType
|
|
9175
9247
|
});
|
|
9176
9248
|
}
|
|
9249
|
+
const loadedIds = new Set(syms.map((s) => s.id));
|
|
9250
|
+
const missingIds = [...relatedIds].filter((id) => !loadedIds.has(id));
|
|
9251
|
+
if (missingIds.length > 0) {
|
|
9252
|
+
const placeholders = missingIds.map(() => "?").join(",");
|
|
9253
|
+
const extras = this.db.prepare(
|
|
9254
|
+
`SELECT id, name, kind, lang, file, line, signature, scope FROM symbols WHERE id IN (${placeholders})`
|
|
9255
|
+
).all(...missingIds);
|
|
9256
|
+
for (const s of extras) symById.set(s.id, s);
|
|
9257
|
+
}
|
|
9177
9258
|
const nodes = [...relatedIds].map((id) => symById.get(id)).filter((symbol) => symbol !== void 0).sort((a, b) => {
|
|
9178
9259
|
const aExternal = a.file === fileFilter ? 0 : 1;
|
|
9179
9260
|
const bExternal = b.file === fileFilter ? 0 : 1;
|
|
@@ -9199,6 +9280,7 @@ import { Worker } from "node:worker_threads";
|
|
|
9199
9280
|
import { expectDefined as expectDefined6 } from "@wrongstack/core";
|
|
9200
9281
|
import * as fs14 from "node:fs/promises";
|
|
9201
9282
|
import * as path19 from "node:path";
|
|
9283
|
+
import { availableParallelism } from "node:os";
|
|
9202
9284
|
import { compileGlob as compileGlob2 } from "@wrongstack/core";
|
|
9203
9285
|
|
|
9204
9286
|
// src/codebase-index/ts-parser.ts
|
|
@@ -9255,8 +9337,7 @@ function extToLang(ext) {
|
|
|
9255
9337
|
return null;
|
|
9256
9338
|
}
|
|
9257
9339
|
}
|
|
9258
|
-
function getSignature(node, sourceFile) {
|
|
9259
|
-
const printer = ts.createPrinter({});
|
|
9340
|
+
function getSignature(printer, node, sourceFile) {
|
|
9260
9341
|
const raw = printer.printNode(ts.EmitHint.Unspecified, node, sourceFile);
|
|
9261
9342
|
return raw.replace(/\s+/g, " ").slice(0, 500);
|
|
9262
9343
|
}
|
|
@@ -9275,28 +9356,14 @@ function getJsDoc(node, sourceFile) {
|
|
|
9275
9356
|
}
|
|
9276
9357
|
return "";
|
|
9277
9358
|
}
|
|
9278
|
-
function
|
|
9279
|
-
|
|
9280
|
-
|
|
9281
|
-
|
|
9282
|
-
|
|
9283
|
-
|
|
9284
|
-
return false;
|
|
9285
|
-
}
|
|
9286
|
-
function buildScope(node) {
|
|
9287
|
-
const parts = [];
|
|
9288
|
-
let current = node.parent;
|
|
9289
|
-
while (current) {
|
|
9290
|
-
if (ts.isClassDeclaration(current) || ts.isInterfaceDeclaration(current) || ts.isEnumDeclaration(current) || ts.isTypeAliasDeclaration(current)) {
|
|
9291
|
-
parts.unshift(current.name?.text ?? "Anon");
|
|
9292
|
-
} else if (ts.isMethodDeclaration(current) || ts.isGetAccessor(current) || ts.isSetAccessor(current) || ts.isPropertyDeclaration(current) || ts.isFunctionDeclaration(current)) {
|
|
9293
|
-
if (current.name && ts.isIdentifier(current.name)) {
|
|
9294
|
-
parts.unshift(current.name.text);
|
|
9295
|
-
}
|
|
9359
|
+
function pushScopeName(node, parts) {
|
|
9360
|
+
if (ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node) || ts.isEnumDeclaration(node) || ts.isTypeAliasDeclaration(node)) {
|
|
9361
|
+
parts.push(node.name?.text ?? "Anon");
|
|
9362
|
+
} else if (ts.isMethodDeclaration(node) || ts.isGetAccessor(node) || ts.isSetAccessor(node) || ts.isPropertyDeclaration(node) || ts.isFunctionDeclaration(node)) {
|
|
9363
|
+
if (node.name && ts.isIdentifier(node.name)) {
|
|
9364
|
+
parts.push(node.name.text);
|
|
9296
9365
|
}
|
|
9297
|
-
current = current.parent;
|
|
9298
9366
|
}
|
|
9299
|
-
return parts.join(".");
|
|
9300
9367
|
}
|
|
9301
9368
|
function parseSymbols(opts) {
|
|
9302
9369
|
const { file, content, lang } = opts;
|
|
@@ -9307,45 +9374,39 @@ function parseSymbols(opts) {
|
|
|
9307
9374
|
return { file, lang, symbols: [], mtimeMs: Date.now() };
|
|
9308
9375
|
}
|
|
9309
9376
|
const symbols = [];
|
|
9310
|
-
|
|
9377
|
+
const refs = [];
|
|
9378
|
+
const printer = ts.createPrinter({});
|
|
9379
|
+
function visit(node, funcDepth, scopeParts) {
|
|
9311
9380
|
const kind = kindOf(node);
|
|
9312
9381
|
if (kind) {
|
|
9313
|
-
if ((kind === "const" || kind === "let" || kind === "var" || kind === "parameter") &&
|
|
9314
|
-
|
|
9315
|
-
|
|
9382
|
+
if ((kind === "const" || kind === "let" || kind === "var" || kind === "parameter") && funcDepth > 0) {
|
|
9383
|
+
} else {
|
|
9384
|
+
const nameNode = node.name;
|
|
9385
|
+
if (!nameNode || !ts.isIdentifier(nameNode)) {
|
|
9386
|
+
return;
|
|
9387
|
+
}
|
|
9388
|
+
const name = nameNode.text;
|
|
9389
|
+
const pos2 = nameNode.getStart(sourceFile);
|
|
9390
|
+
const { line: line2, character } = sourceFile.getLineAndCharacterOfPosition(pos2);
|
|
9391
|
+
const scope = scopeParts.join(".");
|
|
9392
|
+
const signature = getSignature(printer, node, sourceFile);
|
|
9393
|
+
const docComment = getJsDoc(node, sourceFile);
|
|
9394
|
+
const text = [name, signature, docComment].filter(Boolean).join(" | ");
|
|
9395
|
+
symbols.push({
|
|
9396
|
+
id: 0,
|
|
9397
|
+
lang,
|
|
9398
|
+
kind,
|
|
9399
|
+
name,
|
|
9400
|
+
file,
|
|
9401
|
+
line: line2 + 1,
|
|
9402
|
+
col: character,
|
|
9403
|
+
signature,
|
|
9404
|
+
docComment,
|
|
9405
|
+
scope,
|
|
9406
|
+
text
|
|
9407
|
+
});
|
|
9316
9408
|
}
|
|
9317
|
-
const nameNode = node.name;
|
|
9318
|
-
if (!nameNode || !ts.isIdentifier(nameNode)) return;
|
|
9319
|
-
const name = nameNode.text;
|
|
9320
|
-
const pos = nameNode.getStart(sourceFile);
|
|
9321
|
-
const { line, character } = sourceFile.getLineAndCharacterOfPosition(pos);
|
|
9322
|
-
const scope = buildScope(node);
|
|
9323
|
-
const signature = getSignature(node, sourceFile);
|
|
9324
|
-
const docComment = getJsDoc(node, sourceFile);
|
|
9325
|
-
const text = [name, signature, docComment].filter(Boolean).join(" | ");
|
|
9326
|
-
symbols.push({
|
|
9327
|
-
id: 0,
|
|
9328
|
-
lang,
|
|
9329
|
-
kind,
|
|
9330
|
-
name,
|
|
9331
|
-
file,
|
|
9332
|
-
line: line + 1,
|
|
9333
|
-
col: character,
|
|
9334
|
-
signature,
|
|
9335
|
-
docComment,
|
|
9336
|
-
scope,
|
|
9337
|
-
text
|
|
9338
|
-
});
|
|
9339
9409
|
}
|
|
9340
|
-
ts.forEachChild(node, visit);
|
|
9341
|
-
}
|
|
9342
|
-
visit(sourceFile);
|
|
9343
|
-
const refs = extractRefs(sourceFile);
|
|
9344
|
-
return { file, lang, symbols, refs, mtimeMs: Date.now() };
|
|
9345
|
-
}
|
|
9346
|
-
function extractRefs(sourceFile) {
|
|
9347
|
-
const refs = [];
|
|
9348
|
-
function visit(node) {
|
|
9349
9410
|
const pos = node.getStart(sourceFile);
|
|
9350
9411
|
const { line } = sourceFile.getLineAndCharacterOfPosition(pos);
|
|
9351
9412
|
const lineNum = line + 1;
|
|
@@ -9370,10 +9431,14 @@ function extractRefs(sourceFile) {
|
|
|
9370
9431
|
const moduleName = getModuleName(node);
|
|
9371
9432
|
if (moduleName) refs.push({ fromId: 0, toName: moduleName, callType: "import", line: lineNum });
|
|
9372
9433
|
}
|
|
9373
|
-
|
|
9434
|
+
const scopeIdx = scopeParts.length;
|
|
9435
|
+
pushScopeName(node, scopeParts);
|
|
9436
|
+
const childFuncDepth = ts.isFunctionLike(node) ? funcDepth + 1 : funcDepth;
|
|
9437
|
+
ts.forEachChild(node, (child) => visit(child, childFuncDepth, scopeParts));
|
|
9438
|
+
scopeParts.length = scopeIdx;
|
|
9374
9439
|
}
|
|
9375
|
-
visit(sourceFile);
|
|
9376
|
-
return deduplicateRefs(refs);
|
|
9440
|
+
visit(sourceFile, 0, []);
|
|
9441
|
+
return { file, lang, symbols, refs: deduplicateRefs(refs), mtimeMs: Date.now() };
|
|
9377
9442
|
}
|
|
9378
9443
|
function getTypeName(name) {
|
|
9379
9444
|
if (ts.isIdentifier(name)) return name.text;
|
|
@@ -10655,7 +10720,7 @@ async function loadGitignoreMatcher(projectRoot) {
|
|
|
10655
10720
|
|
|
10656
10721
|
// src/codebase-index/indexer.ts
|
|
10657
10722
|
var YIELD_EVERY_N = 50;
|
|
10658
|
-
var PARALLEL_BATCH =
|
|
10723
|
+
var PARALLEL_BATCH = Math.min(availableParallelism() * 4, 40);
|
|
10659
10724
|
function yieldEventLoop() {
|
|
10660
10725
|
return new Promise((resolve15) => setImmediate(resolve15));
|
|
10661
10726
|
}
|
|
@@ -10833,11 +10898,14 @@ async function runIndexerWithStore(store, opts) {
|
|
|
10833
10898
|
if (!force) {
|
|
10834
10899
|
for (const meta of store.getAllFileMetas()) existingMeta.set(meta.file, meta);
|
|
10835
10900
|
}
|
|
10901
|
+
let filesSinceLastYield = 0;
|
|
10836
10902
|
for (let batchStart = 0; batchStart < files.length; batchStart += PARALLEL_BATCH) {
|
|
10837
10903
|
const batchEnd = Math.min(batchStart + PARALLEL_BATCH, files.length);
|
|
10838
10904
|
const batchFiles = files.slice(batchStart, batchEnd);
|
|
10839
10905
|
opts.onProgress?.(batchEnd, files.length);
|
|
10840
|
-
|
|
10906
|
+
filesSinceLastYield += batchFiles.length;
|
|
10907
|
+
if (filesSinceLastYield >= YIELD_EVERY_N) {
|
|
10908
|
+
filesSinceLastYield = 0;
|
|
10841
10909
|
await yieldEventLoop();
|
|
10842
10910
|
throwIfAborted(signal);
|
|
10843
10911
|
}
|
|
@@ -11048,7 +11116,7 @@ async function indexService(args, hooks = {}) {
|
|
|
11048
11116
|
});
|
|
11049
11117
|
}
|
|
11050
11118
|
function searchService(args) {
|
|
11051
|
-
const store =
|
|
11119
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
11052
11120
|
try {
|
|
11053
11121
|
return store.searchRanked(
|
|
11054
11122
|
args.query,
|
|
@@ -11061,39 +11129,39 @@ function searchService(args) {
|
|
|
11061
11129
|
args.limit
|
|
11062
11130
|
);
|
|
11063
11131
|
} finally {
|
|
11064
|
-
|
|
11132
|
+
indexStorePool.release(store);
|
|
11065
11133
|
}
|
|
11066
11134
|
}
|
|
11067
11135
|
function statsService(args) {
|
|
11068
|
-
const store =
|
|
11136
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
11069
11137
|
try {
|
|
11070
11138
|
return store.getStats();
|
|
11071
11139
|
} finally {
|
|
11072
|
-
|
|
11140
|
+
indexStorePool.release(store);
|
|
11073
11141
|
}
|
|
11074
11142
|
}
|
|
11075
11143
|
function packageGraphService(args) {
|
|
11076
|
-
const store =
|
|
11144
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
11077
11145
|
try {
|
|
11078
11146
|
return store.getPackageGraph();
|
|
11079
11147
|
} finally {
|
|
11080
|
-
|
|
11148
|
+
indexStorePool.release(store);
|
|
11081
11149
|
}
|
|
11082
11150
|
}
|
|
11083
11151
|
function fileGraphService(args) {
|
|
11084
|
-
const store =
|
|
11152
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
11085
11153
|
try {
|
|
11086
11154
|
return store.getFileGraph(args.packageFilter);
|
|
11087
11155
|
} finally {
|
|
11088
|
-
|
|
11156
|
+
indexStorePool.release(store);
|
|
11089
11157
|
}
|
|
11090
11158
|
}
|
|
11091
11159
|
function symbolGraphService(args) {
|
|
11092
|
-
const store =
|
|
11160
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
11093
11161
|
try {
|
|
11094
11162
|
return store.getSymbolGraph(args.fileFilter);
|
|
11095
11163
|
} finally {
|
|
11096
|
-
|
|
11164
|
+
indexStorePool.release(store);
|
|
11097
11165
|
}
|
|
11098
11166
|
}
|
|
11099
11167
|
|
|
@@ -11210,10 +11278,19 @@ function terminateWorker(reason) {
|
|
|
11210
11278
|
if (w) void w.terminate().catch(() => {
|
|
11211
11279
|
});
|
|
11212
11280
|
}
|
|
11213
|
-
function shutdownCodebaseIndexHost() {
|
|
11281
|
+
async function shutdownCodebaseIndexHost() {
|
|
11214
11282
|
cancelPendingReindexes();
|
|
11215
|
-
|
|
11283
|
+
indexStorePool.closeAll();
|
|
11284
|
+
const w = worker;
|
|
11285
|
+
worker = null;
|
|
11286
|
+
failAllPending(new Error("codebase-index host shut down"));
|
|
11216
11287
|
workerUnavailable = false;
|
|
11288
|
+
if (w) {
|
|
11289
|
+
try {
|
|
11290
|
+
await w.terminate();
|
|
11291
|
+
} catch {
|
|
11292
|
+
}
|
|
11293
|
+
}
|
|
11217
11294
|
}
|
|
11218
11295
|
function callIndexOp(op, args, opts) {
|
|
11219
11296
|
const w = ensureWorker();
|
|
@@ -16252,7 +16329,7 @@ import {
|
|
|
16252
16329
|
duplicateBoard,
|
|
16253
16330
|
exportBoardAsMarkdown,
|
|
16254
16331
|
exportBoardToTaskGraph,
|
|
16255
|
-
|
|
16332
|
+
createBoardFromText,
|
|
16256
16333
|
getBoard as getBoard2,
|
|
16257
16334
|
getKanbanOrchestrationSnapshot,
|
|
16258
16335
|
getKanbanQueueHealth,
|
|
@@ -16317,12 +16394,17 @@ var SESSION_KANBAN_COLUMNS = [
|
|
|
16317
16394
|
];
|
|
16318
16395
|
var boardQueue = /* @__PURE__ */ new Map();
|
|
16319
16396
|
var boardEnsures = /* @__PURE__ */ new Map();
|
|
16397
|
+
var pendingMirrors = /* @__PURE__ */ new Map();
|
|
16398
|
+
var activeMirrors = /* @__PURE__ */ new Set();
|
|
16320
16399
|
var bindings = /* @__PURE__ */ new WeakMap();
|
|
16321
16400
|
var suppressedTodoMirrors = /* @__PURE__ */ new WeakSet();
|
|
16322
16401
|
var activeSessionBoards = /* @__PURE__ */ new Map();
|
|
16323
16402
|
function boardKey(projectRoot, sessionId) {
|
|
16324
16403
|
return `${projectRoot}\0${sessionId}`;
|
|
16325
16404
|
}
|
|
16405
|
+
function mirrorKey(projectRoot, sessionId, sourceSystem) {
|
|
16406
|
+
return `${boardKey(projectRoot, sessionId)}\0${sourceSystem}`;
|
|
16407
|
+
}
|
|
16326
16408
|
function sessionTag(sessionId) {
|
|
16327
16409
|
return `session:${sessionId}`;
|
|
16328
16410
|
}
|
|
@@ -16458,6 +16540,43 @@ async function projectGraph(projectRoot, sessionId, graph, sourceSystem) {
|
|
|
16458
16540
|
return result?.board ?? null;
|
|
16459
16541
|
});
|
|
16460
16542
|
}
|
|
16543
|
+
function queueLatestMirror(projectRoot, sessionId, graph, sourceSystem) {
|
|
16544
|
+
if (!projectRoot || !sessionId || process.env[MIRROR_DISABLED_ENV] === "0") return;
|
|
16545
|
+
const key = mirrorKey(projectRoot, sessionId, sourceSystem);
|
|
16546
|
+
pendingMirrors.set(key, { projectRoot, sessionId, graph, sourceSystem });
|
|
16547
|
+
if (activeMirrors.has(key)) return;
|
|
16548
|
+
activeMirrors.add(key);
|
|
16549
|
+
void (async () => {
|
|
16550
|
+
try {
|
|
16551
|
+
for (; ; ) {
|
|
16552
|
+
const pending2 = pendingMirrors.get(key);
|
|
16553
|
+
if (!pending2) break;
|
|
16554
|
+
pendingMirrors.delete(key);
|
|
16555
|
+
try {
|
|
16556
|
+
await projectGraph(
|
|
16557
|
+
pending2.projectRoot,
|
|
16558
|
+
pending2.sessionId,
|
|
16559
|
+
pending2.graph,
|
|
16560
|
+
pending2.sourceSystem
|
|
16561
|
+
);
|
|
16562
|
+
} catch {
|
|
16563
|
+
}
|
|
16564
|
+
}
|
|
16565
|
+
} finally {
|
|
16566
|
+
activeMirrors.delete(key);
|
|
16567
|
+
const pending2 = pendingMirrors.get(key);
|
|
16568
|
+
if (pending2) {
|
|
16569
|
+
pendingMirrors.delete(key);
|
|
16570
|
+
queueLatestMirror(
|
|
16571
|
+
pending2.projectRoot,
|
|
16572
|
+
pending2.sessionId,
|
|
16573
|
+
pending2.graph,
|
|
16574
|
+
pending2.sourceSystem
|
|
16575
|
+
);
|
|
16576
|
+
}
|
|
16577
|
+
}
|
|
16578
|
+
})();
|
|
16579
|
+
}
|
|
16461
16580
|
function todoListToSerializedGraph(todos, sessionId) {
|
|
16462
16581
|
const nodes = todos.map((todo, index) => ({
|
|
16463
16582
|
id: todo.id,
|
|
@@ -16608,13 +16727,28 @@ Reassess your current plan before continuing; do not rely on the initial todo sn
|
|
|
16608
16727
|
}
|
|
16609
16728
|
}
|
|
16610
16729
|
function mirrorSessionTodosToKanban(projectRoot, todos, sessionId) {
|
|
16611
|
-
|
|
16730
|
+
queueLatestMirror(
|
|
16731
|
+
projectRoot,
|
|
16732
|
+
sessionId,
|
|
16733
|
+
todoListToSerializedGraph(todos, sessionId),
|
|
16734
|
+
"session-todo"
|
|
16735
|
+
);
|
|
16612
16736
|
}
|
|
16613
16737
|
function mirrorSessionTasksToKanban(projectRoot, tasks, sessionId) {
|
|
16614
|
-
|
|
16738
|
+
queueLatestMirror(
|
|
16739
|
+
projectRoot,
|
|
16740
|
+
sessionId,
|
|
16741
|
+
taskFileToSerializedGraph(tasks, sessionId),
|
|
16742
|
+
"session-task"
|
|
16743
|
+
);
|
|
16615
16744
|
}
|
|
16616
16745
|
function mirrorSessionPlanToKanban(projectRoot, items, sessionId) {
|
|
16617
|
-
|
|
16746
|
+
queueLatestMirror(
|
|
16747
|
+
projectRoot,
|
|
16748
|
+
sessionId,
|
|
16749
|
+
planFileToSerializedGraph(items, sessionId),
|
|
16750
|
+
"session-plan"
|
|
16751
|
+
);
|
|
16618
16752
|
}
|
|
16619
16753
|
function attachSessionKanbanMirror(context) {
|
|
16620
16754
|
const existing = bindings.get(context);
|
|
@@ -17158,7 +17292,7 @@ var kanbanTool = {
|
|
|
17158
17292
|
}
|
|
17159
17293
|
case "generate_board": {
|
|
17160
17294
|
if (!input.description) return fail("generate_board requires description.");
|
|
17161
|
-
const boardInput =
|
|
17295
|
+
const boardInput = createBoardFromText({
|
|
17162
17296
|
description: input.description,
|
|
17163
17297
|
...input.title !== void 0 ? { title: input.title } : {},
|
|
17164
17298
|
...input.context !== void 0 ? { context: input.context } : {},
|
|
@@ -17543,20 +17677,31 @@ var kanbanTool = {
|
|
|
17543
17677
|
if (!input.boardId || !input.taskId)
|
|
17544
17678
|
return fail("mark_assignment requires boardId and taskId.");
|
|
17545
17679
|
const assignmentStatus = input.assignmentStatus ?? (input.status === "completed" ? "completed" : input.error ? "failed" : void 0);
|
|
17546
|
-
const board = await updateTaskAssignment(
|
|
17547
|
-
|
|
17548
|
-
|
|
17549
|
-
|
|
17550
|
-
|
|
17551
|
-
|
|
17552
|
-
|
|
17553
|
-
|
|
17554
|
-
|
|
17555
|
-
|
|
17556
|
-
|
|
17557
|
-
|
|
17558
|
-
|
|
17559
|
-
|
|
17680
|
+
const board = await updateTaskAssignment(
|
|
17681
|
+
projectRoot,
|
|
17682
|
+
input.boardId,
|
|
17683
|
+
input.taskId,
|
|
17684
|
+
{
|
|
17685
|
+
...assignmentStatus !== void 0 ? { status: assignmentStatus } : {},
|
|
17686
|
+
...input.subagentId !== void 0 ? { subagentId: input.subagentId } : {},
|
|
17687
|
+
...input.runTaskId !== void 0 ? { runTaskId: input.runTaskId } : {},
|
|
17688
|
+
...input.lastResult !== void 0 ? { lastResult: input.lastResult } : {},
|
|
17689
|
+
...input.error !== void 0 ? { error: input.error } : {},
|
|
17690
|
+
...input.agentId !== void 0 ? { agentId: input.agentId } : {},
|
|
17691
|
+
...input.leaseId !== void 0 ? { leaseId: input.leaseId } : {},
|
|
17692
|
+
...input.claimedAt !== void 0 ? { claimedAt: input.claimedAt } : {},
|
|
17693
|
+
...input.heartbeatAt !== void 0 ? { heartbeatAt: input.heartbeatAt } : {},
|
|
17694
|
+
...input.leaseExpiresAt !== void 0 ? { leaseExpiresAt: input.leaseExpiresAt } : {},
|
|
17695
|
+
...input.attempt !== void 0 ? { attempt: input.attempt } : {},
|
|
17696
|
+
...input.maxAttempts !== void 0 ? { maxAttempts: input.maxAttempts } : {}
|
|
17697
|
+
},
|
|
17698
|
+
// Ownership fence: when expectedLeaseId is supplied, the write is
|
|
17699
|
+
// applied only if the current assignment still holds this lease.
|
|
17700
|
+
// This prevents a recovered+reassigned stale worker's terminal
|
|
17701
|
+
// mark_assignment from overwriting the successor's state. The check
|
|
17702
|
+
// is atomic inside updateTaskAssignment's mutateBoard lock.
|
|
17703
|
+
input.expectedLeaseId !== void 0 ? { expectedLeaseId: input.expectedLeaseId } : {}
|
|
17704
|
+
);
|
|
17560
17705
|
return board ? okBoard(board, "Assignment updated.") : fail("Task not found.");
|
|
17561
17706
|
}
|
|
17562
17707
|
case "heartbeat_assignment": {
|
|
@@ -17565,7 +17710,13 @@ var kanbanTool = {
|
|
|
17565
17710
|
}
|
|
17566
17711
|
const board = await heartbeatTaskAssignment(projectRoot, input.boardId, input.taskId, {
|
|
17567
17712
|
...input.heartbeatAt !== void 0 ? { heartbeatAt: input.heartbeatAt } : {},
|
|
17568
|
-
...input.leaseExpiresAt !== void 0 ? { leaseExpiresAt: input.leaseExpiresAt } : {}
|
|
17713
|
+
...input.leaseExpiresAt !== void 0 ? { leaseExpiresAt: input.leaseExpiresAt } : {},
|
|
17714
|
+
// Ownership fence: when expectedLeaseId is supplied, the renewal
|
|
17715
|
+
// is applied only if the current assignment still holds this lease.
|
|
17716
|
+
// This prevents a recovered+reassigned stale worker's heartbeat
|
|
17717
|
+
// from renewing the successor's lease. The check is atomic inside
|
|
17718
|
+
// heartbeatTaskAssignment's mutateBoard lock.
|
|
17719
|
+
...input.expectedLeaseId !== void 0 ? { expectedLeaseId: input.expectedLeaseId } : {}
|
|
17569
17720
|
});
|
|
17570
17721
|
return board ? okBoard(board, "Assignment heartbeat updated.") : fail("Task assignment not found.");
|
|
17571
17722
|
}
|