Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
d6c3ce3
feat: type domain/analysis modules, parser WASM maps, and add canStri…
carlos-alm Mar 21, 2026
cc65d63
chore: remove original .js files replaced by TypeScript conversions
carlos-alm Mar 21, 2026
2d5b3d2
Revert "chore: remove original .js files replaced by TypeScript conve…
carlos-alm Mar 21, 2026
292c317
fix: merge main after upstream PR merges
carlos-alm Mar 21, 2026
1e77de0
fix: drop misleading underscore prefix on used variables in node-vers…
carlos-alm Mar 21, 2026
1574c1e
fix: add Statement.raw() to vendor types and remove unnecessary as-an…
carlos-alm Mar 21, 2026
41c6f4d
fix: move inline SQL queries in context.ts behind db repository layer
carlos-alm Mar 21, 2026
aa2a7d0
fix: align customDbPath type to string in implementations.ts
carlos-alm Mar 21, 2026
208e30f
fix: eliminate N+1 re-query in buildTransitiveCallers by passing call…
carlos-alm Mar 21, 2026
6db5ae7
Merge branch 'main' into feat/ts-deferred-followups
carlos-alm Mar 21, 2026
81f7efe
Merge branch 'main' into feat/ts-deferred-followups
carlos-alm Mar 21, 2026
3e3cb50
fix: hoist db.prepare() out of BFS loops and include n.id in SELECT t…
carlos-alm Mar 21, 2026
b3fc6d5
fix: promote TS_BACKFILL_EXTS to module-level constant, remove 3 dupl…
carlos-alm Mar 21, 2026
7a86aa1
chore: update package-lock.json
carlos-alm Mar 21, 2026
87b7ab7
Merge branch 'feat/ts-deferred-followups' of https://github.com/optav…
carlos-alm Mar 21, 2026
59cc1e1
fix: eliminate duplicate findCallers() call and hoist db.prepare() ou…
carlos-alm Mar 22, 2026
204c3a6
fix: auto-format exports.ts and parser.ts
carlos-alm Mar 22, 2026
b4f3979
fix: hoist all db.prepare() calls out of fileNodes.map() loop in expo…
carlos-alm Mar 22, 2026
a4597aa
fix: resolve merge conflicts with main
carlos-alm Mar 22, 2026
c85f05d
fix: hoist db.prepare() calls and cache schema probes per Greptile P2…
carlos-alm Mar 22, 2026
2d30003
fix: resolve TypeScript type mismatches from merge conflict resolution
carlos-alm Mar 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/db/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ export {
getFileNodesAll,
getFunctionNodeId,
getImportEdges,
getLineCountForNode,
getMaxEndLineForFile,
getNodeId,
hasCfgTables,
hasCoChanges,
Expand Down
2 changes: 2 additions & 0 deletions src/db/repository/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ export {
findNodesForTriage,
findNodesWithFanIn,
getFunctionNodeId,
getLineCountForNode,
getMaxEndLineForFile,
getNodeId,
iterateFunctionNodes,
listFunctionNodes,
Expand Down
34 changes: 34 additions & 0 deletions src/db/repository/nodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,3 +275,37 @@ export function findNodeByQualifiedName(
'SELECT * FROM nodes WHERE qualified_name = ? ORDER BY file, line',
).all(qualifiedName);
}

// ─── Metric helpers ──────────────────────────────────────────────────────

const _getLineCountForNodeStmt = new WeakMap();

/**
* Get line_count from node_metrics for a given node.
* @param {object} db
* @param {number} nodeId
* @returns {{ line_count: number } | undefined}
*/
export function getLineCountForNode(db: BetterSqlite3Database, nodeId: number) {
return cachedStmt(
_getLineCountForNodeStmt,
db,
'SELECT line_count FROM node_metrics WHERE node_id = ?',
).get(nodeId);
}

const _getMaxEndLineForFileStmt = new WeakMap();

/**
* Get the maximum end_line across all nodes in a file.
* @param {object} db
* @param {string} file
* @returns {{ max_end: number | null } | undefined}
*/
export function getMaxEndLineForFile(db: BetterSqlite3Database, file: string) {
return cachedStmt(
_getMaxEndLineForFileStmt,
db,
'SELECT MAX(end_line) as max_end FROM nodes WHERE file = ?',
).get(file);
}
32 changes: 19 additions & 13 deletions src/domain/analysis/brief.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
} from '../../db/index.js';
import { loadConfig } from '../../infrastructure/config.js';
import { isTestFile } from '../../infrastructure/test-filter.js';
import type { BetterSqlite3Database, ImportEdgeRow, NodeRow, RelatedNodeRow } from '../../types.js';

/** Symbol kinds meaningful for a file brief — excludes parameters, properties, constants. */
const BRIEF_KINDS = new Set([
Expand Down Expand Up @@ -47,15 +48,19 @@ function computeRiskTier(
* BFS to count transitive callers for a single node.
* Lightweight variant — only counts, does not collect details.
*/
// biome-ignore lint/suspicious/noExplicitAny: db handle from better-sqlite3
function countTransitiveCallers(db: any, startId: number, noTests: boolean, maxDepth = 5): number {
function countTransitiveCallers(
db: BetterSqlite3Database,
startId: number,
noTests: boolean,
maxDepth = 5,
): number {
const visited = new Set([startId]);
let frontier = [startId];

for (let d = 1; d <= maxDepth; d++) {
const nextFrontier: number[] = [];
for (const fid of frontier) {
const callers = findDistinctCallers(db, fid);
const callers = findDistinctCallers(db, fid) as RelatedNodeRow[];
for (const c of callers) {
if (!visited.has(c.id) && (!noTests || !isTestFile(c.file))) {
visited.add(c.id);
Expand All @@ -75,8 +80,7 @@ function countTransitiveCallers(db: any, startId: number, noTests: boolean, maxD
* Depth-bounded to match countTransitiveCallers and keep hook latency predictable.
*/
function countTransitiveImporters(
// biome-ignore lint/suspicious/noExplicitAny: db handle from better-sqlite3
db: any,
db: BetterSqlite3Database,
fileNodeIds: number[],
noTests: boolean,
maxDepth = 5,
Expand All @@ -87,7 +91,7 @@ function countTransitiveImporters(
for (let d = 1; d <= maxDepth; d++) {
const nextFrontier: number[] = [];
for (const current of frontier) {
const dependents = findImportDependents(db, current);
const dependents = findImportDependents(db, current) as RelatedNodeRow[];
for (const dep of dependents) {
if (!visited.has(dep.id) && (!noTests || !isTestFile(dep.file))) {
visited.add(dep.id);
Expand All @@ -108,10 +112,10 @@ function countTransitiveImporters(
*/
export function briefData(
file: string,
customDbPath: string | undefined,
// biome-ignore lint/suspicious/noExplicitAny: config shape not yet typed
customDbPath: string,
// biome-ignore lint/suspicious/noExplicitAny: config shape is dynamic
opts: { noTests?: boolean; config?: any } = {},
): object {
) {
const db = openReadonlyOrFail(customDbPath);
try {
const noTests = opts.noTests || false;
Expand All @@ -120,26 +124,28 @@ export function briefData(
const importerDepth = config.analysis?.briefImporterDepth ?? 5;
const highRiskCallers = config.analysis?.briefHighRiskCallers ?? 10;
const mediumRiskCallers = config.analysis?.briefMediumRiskCallers ?? 3;
const fileNodes = findFileNodes(db, `%${file}%`);
const fileNodes = findFileNodes(db, `%${file}%`) as NodeRow[];
if (fileNodes.length === 0) {
return { file, results: [] };
}

const results = fileNodes.map((fn) => {
// Direct importers
let importedBy = findImportSources(db, fn.id);
let importedBy = findImportSources(db, fn.id) as ImportEdgeRow[];
if (noTests) importedBy = importedBy.filter((i) => !isTestFile(i.file));
const directImporters = [...new Set(importedBy.map((i) => i.file))];

// Transitive importer count
const totalImporterCount = countTransitiveImporters(db, [fn.id], noTests, importerDepth);

// Direct imports
let importsTo = findImportTargets(db, fn.id);
let importsTo = findImportTargets(db, fn.id) as ImportEdgeRow[];
if (noTests) importsTo = importsTo.filter((i) => !isTestFile(i.file));

// Symbol definitions with roles and caller counts
const defs = findNodesByFile(db, fn.file).filter((d) => BRIEF_KINDS.has(d.kind));
const defs = (findNodesByFile(db, fn.file) as NodeRow[]).filter((d) =>
BRIEF_KINDS.has(d.kind),
);
const symbols = defs.map((d) => {
const callerCount = countTransitiveCallers(db, d.id, noTests, callerDepth);
return {
Expand Down
Loading
Loading