@wrongstack/tools 0.298.2 → 0.299.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/bash.js +14 -0
- package/dist/builtin.js +396 -0
- package/dist/codebase-index/background-indexer.d.ts +6 -1
- package/dist/codebase-index/codebase-incoming-calls-tool.d.ts +34 -0
- package/dist/codebase-index/codebase-outgoing-calls-tool.d.ts +34 -0
- package/dist/codebase-index/index-service.d.ts +24 -2
- package/dist/codebase-index/index.d.ts +4 -2
- package/dist/codebase-index/index.js +382 -0
- package/dist/codebase-index/project-server.js +177 -1
- package/dist/codebase-index/schema.d.ts +23 -0
- package/dist/codebase-index/worker-protocol.d.ts +14 -0
- package/dist/codebase-index/worker.js +164 -0
- package/dist/codebase-index/writer-graph-reader.d.ts +24 -1
- package/dist/codebase-index/writer.d.ts +21 -1
- package/dist/exec.js +4 -0
- package/dist/index.js +396 -0
- package/dist/pack.js +394 -0
- package/dist/ps-slash.js +4 -0
- package/dist/read.js +164 -0
- package/dist/tool-tier.js +396 -0
- package/package.json +3 -3
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Errors cross the boundary as strings and are re-wrapped by the host.
|
|
6
6
|
*/
|
|
7
7
|
import type { CodeMapGraph, IndexResult, IndexStats, SearchResult } from './schema.js';
|
|
8
|
+
import type { IncomingCallsResult, OutgoingCallsResult } from './index-service.js';
|
|
8
9
|
export interface IndexOpArgs {
|
|
9
10
|
projectRoot: string;
|
|
10
11
|
indexDir?: string | undefined;
|
|
@@ -33,6 +34,11 @@ export interface FileGraphOpArgs extends StatsOpArgs {
|
|
|
33
34
|
export interface SymbolGraphOpArgs extends StatsOpArgs {
|
|
34
35
|
fileFilter: string;
|
|
35
36
|
}
|
|
37
|
+
export interface CallRefsOpArgs extends StatsOpArgs {
|
|
38
|
+
symbol: string;
|
|
39
|
+
file?: string | undefined;
|
|
40
|
+
limit?: number | undefined;
|
|
41
|
+
}
|
|
36
42
|
export interface SearchOpResult {
|
|
37
43
|
results: SearchResult[];
|
|
38
44
|
total: number;
|
|
@@ -63,6 +69,14 @@ export interface OpShapes {
|
|
|
63
69
|
args: SymbolGraphOpArgs;
|
|
64
70
|
result: CodeMapGraph;
|
|
65
71
|
};
|
|
72
|
+
incomingCalls: {
|
|
73
|
+
args: CallRefsOpArgs;
|
|
74
|
+
result: IncomingCallsResult;
|
|
75
|
+
};
|
|
76
|
+
outgoingCalls: {
|
|
77
|
+
args: CallRefsOpArgs;
|
|
78
|
+
result: OutgoingCallsResult;
|
|
79
|
+
};
|
|
66
80
|
}
|
|
67
81
|
export type OpName = keyof OpShapes;
|
|
68
82
|
export type HostToWorker = {
|
|
@@ -2658,6 +2658,136 @@ function mapWriterRefRow(row) {
|
|
|
2658
2658
|
}
|
|
2659
2659
|
|
|
2660
2660
|
// src/codebase-index/writer-graph-reader.ts
|
|
2661
|
+
var MAX_SQL_VARS = 900;
|
|
2662
|
+
function chunkedIdQuery(stmt, ids, buildSql, extraArgs = []) {
|
|
2663
|
+
const results = [];
|
|
2664
|
+
for (let start = 0; start < ids.length; start += MAX_SQL_VARS) {
|
|
2665
|
+
const chunk = ids.slice(start, start + MAX_SQL_VARS);
|
|
2666
|
+
const placeholders = chunk.map(() => "?").join(",");
|
|
2667
|
+
const sql = buildSql(placeholders);
|
|
2668
|
+
results.push(...stmt(sql).all(...chunk, ...extraArgs));
|
|
2669
|
+
}
|
|
2670
|
+
return results;
|
|
2671
|
+
}
|
|
2672
|
+
function chunkedIdScalar(stmt, ids, buildSql, extraArgs = []) {
|
|
2673
|
+
let total = 0;
|
|
2674
|
+
for (let start = 0; start < ids.length; start += MAX_SQL_VARS) {
|
|
2675
|
+
const chunk = ids.slice(start, start + MAX_SQL_VARS);
|
|
2676
|
+
const placeholders = chunk.map(() => "?").join(",");
|
|
2677
|
+
const sql = buildSql(placeholders);
|
|
2678
|
+
const rows = stmt(sql).all(...chunk, ...extraArgs);
|
|
2679
|
+
total += rows[0]?.n ?? 0;
|
|
2680
|
+
}
|
|
2681
|
+
return total;
|
|
2682
|
+
}
|
|
2683
|
+
function mapCallSiteRow(row) {
|
|
2684
|
+
return {
|
|
2685
|
+
symbol: {
|
|
2686
|
+
id: row.sym_id,
|
|
2687
|
+
name: row.sym_name,
|
|
2688
|
+
kind: row.sym_kind,
|
|
2689
|
+
lang: row.sym_lang,
|
|
2690
|
+
file: row.sym_file,
|
|
2691
|
+
line: row.sym_line,
|
|
2692
|
+
signature: row.sym_signature
|
|
2693
|
+
},
|
|
2694
|
+
callType: row.call_type,
|
|
2695
|
+
line: row.ref_line
|
|
2696
|
+
};
|
|
2697
|
+
}
|
|
2698
|
+
function resolveSymbolIds(stmt, symbolName, file) {
|
|
2699
|
+
const baseSql = file ? `SELECT id FROM symbols WHERE name = ? AND file = ? ORDER BY id` : `SELECT id FROM symbols WHERE name = ? ORDER BY id`;
|
|
2700
|
+
const args = file ? [symbolName, file] : [symbolName];
|
|
2701
|
+
const rows = stmt(baseSql).all(...args);
|
|
2702
|
+
return rows.map((r) => r.id);
|
|
2703
|
+
}
|
|
2704
|
+
function findIncomingCallsByName(stmt, symbolName, file, limit) {
|
|
2705
|
+
const targetIds = resolveSymbolIds(stmt, symbolName, file);
|
|
2706
|
+
if (targetIds.length === 0) return { calls: [], symbolFound: false, ambiguous: false, totalMatches: 0 };
|
|
2707
|
+
let matchIds = targetIds;
|
|
2708
|
+
let ambiguous = false;
|
|
2709
|
+
if (file !== void 0) {
|
|
2710
|
+
const allNamedIds = resolveSymbolIds(stmt, symbolName, void 0);
|
|
2711
|
+
if (allNamedIds.length > targetIds.length) {
|
|
2712
|
+
matchIds = allNamedIds;
|
|
2713
|
+
ambiguous = true;
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
const useFallback = !file;
|
|
2717
|
+
const rows = chunkedIdQuery(
|
|
2718
|
+
stmt,
|
|
2719
|
+
matchIds,
|
|
2720
|
+
(ph) => `SELECT
|
|
2721
|
+
s.id AS sym_id,
|
|
2722
|
+
s.name AS sym_name,
|
|
2723
|
+
s.kind AS sym_kind,
|
|
2724
|
+
s.lang AS sym_lang,
|
|
2725
|
+
s.file AS sym_file,
|
|
2726
|
+
s.line AS sym_line,
|
|
2727
|
+
s.signature AS sym_signature,
|
|
2728
|
+
r.call_type,
|
|
2729
|
+
r.line AS ref_line
|
|
2730
|
+
FROM refs r
|
|
2731
|
+
JOIN symbols s ON s.id = r.from_id
|
|
2732
|
+
WHERE r.to_id IN (${ph})
|
|
2733
|
+
ORDER BY r.line, r.id`,
|
|
2734
|
+
[]
|
|
2735
|
+
);
|
|
2736
|
+
if (useFallback) {
|
|
2737
|
+
const fallbackRows = stmt(
|
|
2738
|
+
`SELECT
|
|
2739
|
+
s.id AS sym_id,
|
|
2740
|
+
s.name AS sym_name,
|
|
2741
|
+
s.kind AS sym_kind,
|
|
2742
|
+
s.lang AS sym_lang,
|
|
2743
|
+
s.file AS sym_file,
|
|
2744
|
+
s.line AS sym_line,
|
|
2745
|
+
s.signature AS sym_signature,
|
|
2746
|
+
r.call_type,
|
|
2747
|
+
r.line AS ref_line
|
|
2748
|
+
FROM refs r
|
|
2749
|
+
JOIN symbols s ON s.id = r.from_id
|
|
2750
|
+
WHERE r.to_id IS NULL AND r.to_name = ?
|
|
2751
|
+
ORDER BY r.line, r.id`
|
|
2752
|
+
).all(symbolName);
|
|
2753
|
+
rows.push(...fallbackRows);
|
|
2754
|
+
}
|
|
2755
|
+
rows.sort((a, b) => a.ref_line - b.ref_line || a.sym_id - b.sym_id);
|
|
2756
|
+
const allCalls = rows.map(mapCallSiteRow);
|
|
2757
|
+
return { calls: allCalls.slice(0, limit), symbolFound: true, ambiguous, totalMatches: allCalls.length };
|
|
2758
|
+
}
|
|
2759
|
+
function findOutgoingCallsByName(stmt, symbolName, file, limit) {
|
|
2760
|
+
const sourceIds = resolveSymbolIds(stmt, symbolName, file);
|
|
2761
|
+
if (sourceIds.length === 0) return { calls: [], symbolFound: false, unresolvedCount: 0, totalMatches: 0 };
|
|
2762
|
+
const unresolvedCount = chunkedIdScalar(
|
|
2763
|
+
stmt,
|
|
2764
|
+
sourceIds,
|
|
2765
|
+
(ph) => `SELECT COUNT(*) AS n FROM refs WHERE from_id IN (${ph}) AND to_id IS NULL`
|
|
2766
|
+
);
|
|
2767
|
+
const rows = chunkedIdQuery(
|
|
2768
|
+
stmt,
|
|
2769
|
+
sourceIds,
|
|
2770
|
+
(ph) => `SELECT
|
|
2771
|
+
s.id AS sym_id,
|
|
2772
|
+
s.name AS sym_name,
|
|
2773
|
+
s.kind AS sym_kind,
|
|
2774
|
+
s.lang AS sym_lang,
|
|
2775
|
+
s.file AS sym_file,
|
|
2776
|
+
s.line AS sym_line,
|
|
2777
|
+
s.signature AS sym_signature,
|
|
2778
|
+
r.call_type,
|
|
2779
|
+
r.line AS ref_line
|
|
2780
|
+
FROM refs r
|
|
2781
|
+
JOIN symbols s ON s.id = r.to_id
|
|
2782
|
+
WHERE r.from_id IN (${ph})
|
|
2783
|
+
AND r.to_id IS NOT NULL -- INNER JOIN already excludes NULL to_id; this is defensive belt-and-suspenders
|
|
2784
|
+
ORDER BY r.line, r.id`,
|
|
2785
|
+
[]
|
|
2786
|
+
);
|
|
2787
|
+
rows.sort((a, b) => a.ref_line - b.ref_line || a.sym_id - b.sym_id);
|
|
2788
|
+
const calls = rows.map(mapCallSiteRow).slice(0, limit);
|
|
2789
|
+
return { calls, symbolFound: true, unresolvedCount, totalMatches: rows.length };
|
|
2790
|
+
}
|
|
2661
2791
|
function findRefsToWithStatement(stmt, symbolId) {
|
|
2662
2792
|
return stmt(
|
|
2663
2793
|
"SELECT id, from_id, to_name, to_id, call_type, line FROM refs WHERE to_id = ? OR to_name = (SELECT name FROM symbols WHERE id = ?)"
|
|
@@ -3889,6 +4019,20 @@ var IndexStore = class _IndexStore {
|
|
|
3889
4019
|
return false;
|
|
3890
4020
|
}
|
|
3891
4021
|
}
|
|
4022
|
+
/**
|
|
4023
|
+
* Find all symbols that reference the named target symbol (incoming callers).
|
|
4024
|
+
* Accepts a name instead of an id so the agent doesn't need a prior lookup.
|
|
4025
|
+
*/
|
|
4026
|
+
findIncomingCallsByName(symbolName, file, limit = 100) {
|
|
4027
|
+
return findIncomingCallsByName((sql) => this.stmt(sql), symbolName, file, limit);
|
|
4028
|
+
}
|
|
4029
|
+
/**
|
|
4030
|
+
* Find all symbols that the named source symbol references (outgoing callees).
|
|
4031
|
+
* Accepts a name instead of an id so the agent doesn't need a prior lookup.
|
|
4032
|
+
*/
|
|
4033
|
+
findOutgoingCallsByName(symbolName, file, limit = 100) {
|
|
4034
|
+
return findOutgoingCallsByName((sql) => this.stmt(sql), symbolName, file, limit);
|
|
4035
|
+
}
|
|
3892
4036
|
/**
|
|
3893
4037
|
* Find all references TO a given symbol (who calls / uses this symbol?).
|
|
3894
4038
|
*/
|
|
@@ -4473,6 +4617,22 @@ function symbolGraphService(args) {
|
|
|
4473
4617
|
indexStorePool.release(store);
|
|
4474
4618
|
}
|
|
4475
4619
|
}
|
|
4620
|
+
function incomingCallsService(args) {
|
|
4621
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
4622
|
+
try {
|
|
4623
|
+
return store.findIncomingCallsByName(args.symbol, args.file, args.limit ?? 100);
|
|
4624
|
+
} finally {
|
|
4625
|
+
indexStorePool.release(store);
|
|
4626
|
+
}
|
|
4627
|
+
}
|
|
4628
|
+
function outgoingCallsService(args) {
|
|
4629
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
4630
|
+
try {
|
|
4631
|
+
return store.findOutgoingCallsByName(args.symbol, args.file, args.limit ?? 100);
|
|
4632
|
+
} finally {
|
|
4633
|
+
indexStorePool.release(store);
|
|
4634
|
+
}
|
|
4635
|
+
}
|
|
4476
4636
|
|
|
4477
4637
|
// src/codebase-index/worker.ts
|
|
4478
4638
|
if (!parentPort) throw new Error("codebase-index worker must be started as a worker thread");
|
|
@@ -4505,6 +4665,10 @@ async function dispatch(msg) {
|
|
|
4505
4665
|
return fileGraphService(msg.args);
|
|
4506
4666
|
case "symbolGraph":
|
|
4507
4667
|
return symbolGraphService(msg.args);
|
|
4668
|
+
case "incomingCalls":
|
|
4669
|
+
return incomingCallsService(msg.args);
|
|
4670
|
+
case "outgoingCalls":
|
|
4671
|
+
return outgoingCallsService(msg.args);
|
|
4508
4672
|
default:
|
|
4509
4673
|
throw new Error(`unknown index op: ${msg.op}`);
|
|
4510
4674
|
}
|
|
@@ -1,7 +1,30 @@
|
|
|
1
1
|
import type { DatabaseSync } from 'node:sqlite';
|
|
2
|
-
import type { CodeMapGraph, Ref } from './schema.js';
|
|
2
|
+
import type { CallSite, CodeMapGraph, Ref } from './schema.js';
|
|
3
3
|
type Statement = ReturnType<DatabaseSync['prepare']>;
|
|
4
4
|
type PrepareStatement = (sql: string) => Statement;
|
|
5
|
+
/**
|
|
6
|
+
* Find all symbols that CALL/USE the named target symbol (incoming callers).
|
|
7
|
+
*
|
|
8
|
+
* Returns one `CallSite` per ref edge, with the caller's full metadata so the
|
|
9
|
+
* agent sees file, line, kind, and signature without a second lookup.
|
|
10
|
+
*/
|
|
11
|
+
export declare function findIncomingCallsByName(stmt: PrepareStatement, symbolName: string, file: string | undefined, limit: number): {
|
|
12
|
+
calls: CallSite[];
|
|
13
|
+
symbolFound: boolean;
|
|
14
|
+
ambiguous: boolean;
|
|
15
|
+
totalMatches: number;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Find all symbols that the named source symbol CALLS/USES (outgoing callees).
|
|
19
|
+
*
|
|
20
|
+
* Returns one `CallSite` per ref edge, with the callee's full metadata.
|
|
21
|
+
*/
|
|
22
|
+
export declare function findOutgoingCallsByName(stmt: PrepareStatement, symbolName: string, file: string | undefined, limit: number): {
|
|
23
|
+
calls: CallSite[];
|
|
24
|
+
symbolFound: boolean;
|
|
25
|
+
unresolvedCount: number;
|
|
26
|
+
totalMatches: number;
|
|
27
|
+
};
|
|
5
28
|
export declare function findRefsToWithStatement(stmt: PrepareStatement, symbolId: number): Ref[];
|
|
6
29
|
export declare function findRefsFromWithStatement(stmt: PrepareStatement, symbolId: number): Ref[];
|
|
7
30
|
export declare function getPackageGraphWithStatement(stmt: PrepareStatement): CodeMapGraph;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CodeMapGraph, FileMeta, IndexStats, Symbol as IndexSymbol, Ref, SearchResult, SymbolKind, SymbolLang } from './schema.js';
|
|
1
|
+
import type { CallSite, CodeMapGraph, FileMeta, IndexStats, Symbol as IndexSymbol, Ref, SearchResult, SymbolKind, SymbolLang } from './schema.js';
|
|
2
2
|
import { type WriterSearchFilter } from './writer-search-helpers.js';
|
|
3
3
|
import { StorePool } from './writer-store-pool.js';
|
|
4
4
|
export { codebaseIndexDirOverride, resolveIndexDir } from './writer-helpers.js';
|
|
@@ -252,6 +252,26 @@ export declare class IndexStore {
|
|
|
252
252
|
minBytes?: number;
|
|
253
253
|
minFreeRatio?: number;
|
|
254
254
|
}): boolean;
|
|
255
|
+
/**
|
|
256
|
+
* Find all symbols that reference the named target symbol (incoming callers).
|
|
257
|
+
* Accepts a name instead of an id so the agent doesn't need a prior lookup.
|
|
258
|
+
*/
|
|
259
|
+
findIncomingCallsByName(symbolName: string, file?: string, limit?: number): {
|
|
260
|
+
calls: CallSite[];
|
|
261
|
+
symbolFound: boolean;
|
|
262
|
+
ambiguous: boolean;
|
|
263
|
+
totalMatches: number;
|
|
264
|
+
};
|
|
265
|
+
/**
|
|
266
|
+
* Find all symbols that the named source symbol references (outgoing callees).
|
|
267
|
+
* Accepts a name instead of an id so the agent doesn't need a prior lookup.
|
|
268
|
+
*/
|
|
269
|
+
findOutgoingCallsByName(symbolName: string, file?: string, limit?: number): {
|
|
270
|
+
calls: CallSite[];
|
|
271
|
+
symbolFound: boolean;
|
|
272
|
+
unresolvedCount: number;
|
|
273
|
+
totalMatches: number;
|
|
274
|
+
};
|
|
255
275
|
/**
|
|
256
276
|
* Find all references TO a given symbol (who calls / uses this symbol?).
|
|
257
277
|
*/
|
package/dist/exec.js
CHANGED
|
@@ -1284,6 +1284,10 @@ async function acquireLock(lockfilePath, timeoutMs = 5e3) {
|
|
|
1284
1284
|
const start = Date.now();
|
|
1285
1285
|
const pidStr = String(process.pid);
|
|
1286
1286
|
const hostStr = os2.hostname();
|
|
1287
|
+
try {
|
|
1288
|
+
await fs2.mkdir(path4.dirname(lockfilePath), { recursive: true });
|
|
1289
|
+
} catch {
|
|
1290
|
+
}
|
|
1287
1291
|
while (Date.now() - start < timeoutMs) {
|
|
1288
1292
|
try {
|
|
1289
1293
|
await fs2.writeFile(lockfilePath, `${pidStr}:${hostStr}:${Date.now()}`, { flag: "wx" });
|