Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 0 additions & 12 deletions src/ast-analysis/shared.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,18 +176,6 @@ export function findFunctionNode(rootNode, startLine, _endLine, rules) {
return best;
}

/**
* Truncate a string to a maximum length, appending an ellipsis if truncated.
*
* @param {string} str - Input string
* @param {number} [max=200] - Maximum length
* @returns {string}
*/
export function truncate(str, max = 200) {
if (!str) return '';
return str.length > max ? `${str.slice(0, max)}…` : str;
}

// ─── Extension / Language Mapping ─────────────────────────────────────────

/**
Expand Down
30 changes: 18 additions & 12 deletions src/db/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,12 @@ export function findRepoRoot(fromDir) {
// matches the realpathSync'd dir in findDbPath.
try {
root = fs.realpathSync(raw);
} catch {
} catch (e) {
debug(`realpathSync failed for git root "${raw}", using resolve: ${e.message}`);
root = path.resolve(raw);
}
} catch {
} catch (e) {
debug(`git rev-parse failed for "${dir}": ${e.message}`);
root = null;
}
if (!fromDir) {
Expand All @@ -60,7 +62,8 @@ function isProcessAlive(pid) {
try {
process.kill(pid, 0);
return true;
} catch {
} catch (e) {
debug(`PID ${pid} not alive: ${e.code || e.message}`);
return false;
}
}
Expand All @@ -75,13 +78,13 @@ function acquireAdvisoryLock(dbPath) {
warn(`Another process (PID ${pid}) may be using this database. Proceeding with caution.`);
}
}
} catch {
/* ignore read errors */
} catch (e) {
debug(`Advisory lock read failed: ${e.message}`);
}
try {
fs.writeFileSync(lockPath, String(process.pid), 'utf-8');
} catch {
/* best-effort */
} catch (e) {
debug(`Advisory lock write failed: ${e.message}`);
}
}

Expand All @@ -91,8 +94,8 @@ function releaseAdvisoryLock(lockPath) {
if (Number(content) === process.pid) {
fs.unlinkSync(lockPath);
}
} catch {
/* ignore */
} catch (e) {
debug(`Advisory lock release failed for ${lockPath}: ${e.message}`);
}
}

Expand All @@ -107,7 +110,8 @@ function isSameDirectory(a, b) {
const sa = fs.statSync(a);
const sb = fs.statSync(b);
return sa.dev === sb.dev && sa.ino === sb.ino;
} catch {
} catch (e) {
debug(`isSameDirectory stat failed: ${e.message}`);
return false;
}
}
Expand Down Expand Up @@ -139,7 +143,8 @@ export function findDbPath(customPath) {
if (rawCeiling) {
try {
ceiling = fs.realpathSync(rawCeiling);
} catch {
} catch (e) {
debug(`realpathSync failed for ceiling "${rawCeiling}": ${e.message}`);
ceiling = rawCeiling;
}
} else {
Expand All @@ -149,7 +154,8 @@ export function findDbPath(customPath) {
let dir;
try {
dir = fs.realpathSync(process.cwd());
} catch {
} catch (e) {
debug(`realpathSync failed for cwd: ${e.message}`);
dir = process.cwd();
}
while (true) {
Expand Down
105 changes: 41 additions & 64 deletions src/db/migrations.js
Original file line number Diff line number Diff line change
Expand Up @@ -242,11 +242,23 @@ export const MIGRATIONS = [
},
];

function hasColumn(db, table, column) {
const cols = db.pragma(`table_info(${table})`);
return cols.some((c) => c.name === column);
}

function hasTable(db, table) {
const row = db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name=?").get(table);
return !!row;
}

export function getBuildMeta(db, key) {
if (!hasTable(db, 'build_meta')) return null;
try {
const row = db.prepare('SELECT value FROM build_meta WHERE key = ?').get(key);
return row ? row.value : null;
} catch {
} catch (e) {
debug(`getBuildMeta failed for key "${key}": ${e.message}`);
return null;
}
}
Expand Down Expand Up @@ -280,74 +292,39 @@ export function initSchema(db) {
}
}

try {
db.exec('ALTER TABLE nodes ADD COLUMN end_line INTEGER');
} catch {
/* already exists */
}
try {
db.exec('ALTER TABLE edges ADD COLUMN confidence REAL DEFAULT 1.0');
} catch {
/* already exists */
}
try {
db.exec('ALTER TABLE edges ADD COLUMN dynamic INTEGER DEFAULT 0');
} catch {
/* already exists */
}
try {
db.exec('ALTER TABLE nodes ADD COLUMN role TEXT');
} catch {
/* already exists */
}
try {
// Legacy column compat — add columns that may be missing from pre-migration DBs
if (hasTable(db, 'nodes')) {
if (!hasColumn(db, 'nodes', 'end_line')) {
db.exec('ALTER TABLE nodes ADD COLUMN end_line INTEGER');
}
if (!hasColumn(db, 'nodes', 'role')) {
db.exec('ALTER TABLE nodes ADD COLUMN role TEXT');
}
db.exec('CREATE INDEX IF NOT EXISTS idx_nodes_role ON nodes(role)');
} catch {
/* already exists */
}
try {
db.exec('ALTER TABLE nodes ADD COLUMN parent_id INTEGER REFERENCES nodes(id)');
} catch {
/* already exists */
}
try {
if (!hasColumn(db, 'nodes', 'parent_id')) {
db.exec('ALTER TABLE nodes ADD COLUMN parent_id INTEGER REFERENCES nodes(id)');
}
db.exec('CREATE INDEX IF NOT EXISTS idx_nodes_parent ON nodes(parent_id)');
} catch {
/* already exists */
}
try {
db.exec('CREATE INDEX IF NOT EXISTS idx_nodes_kind_parent ON nodes(kind, parent_id)');
} catch {
/* already exists */
}
try {
db.exec('ALTER TABLE nodes ADD COLUMN qualified_name TEXT');
} catch {
/* already exists */
}
try {
db.exec('ALTER TABLE nodes ADD COLUMN scope TEXT');
} catch {
/* already exists */
}
try {
db.exec('ALTER TABLE nodes ADD COLUMN visibility TEXT');
} catch {
/* already exists */
}
try {
if (!hasColumn(db, 'nodes', 'qualified_name')) {
db.exec('ALTER TABLE nodes ADD COLUMN qualified_name TEXT');
}
if (!hasColumn(db, 'nodes', 'scope')) {
db.exec('ALTER TABLE nodes ADD COLUMN scope TEXT');
}
if (!hasColumn(db, 'nodes', 'visibility')) {
db.exec('ALTER TABLE nodes ADD COLUMN visibility TEXT');
}
db.exec('UPDATE nodes SET qualified_name = name WHERE qualified_name IS NULL');
} catch {
/* nodes table may not exist yet */
}
try {
db.exec('CREATE INDEX IF NOT EXISTS idx_nodes_qualified_name ON nodes(qualified_name)');
} catch {
/* already exists */
}
try {
db.exec('CREATE INDEX IF NOT EXISTS idx_nodes_scope ON nodes(scope)');
} catch {
/* already exists */
}
if (hasTable(db, 'edges')) {
if (!hasColumn(db, 'edges', 'confidence')) {
db.exec('ALTER TABLE edges ADD COLUMN confidence REAL DEFAULT 1.0');
}
if (!hasColumn(db, 'edges', 'dynamic')) {
db.exec('ALTER TABLE edges ADD COLUMN dynamic INTEGER DEFAULT 0');
}
}
}
13 changes: 7 additions & 6 deletions src/domain/analysis/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
getComplexityForNode,
openReadonlyOrFail,
} from '../../db/index.js';
import { debug } from '../../infrastructure/logger.js';
import { isTestFile } from '../../infrastructure/test-filter.js';
import {
createFileLinesReader,
Expand Down Expand Up @@ -142,8 +143,8 @@ function explainFunctionImpl(db, target, noTests, getFileLines) {
halsteadVolume: cRow.halstead_volume || 0,
};
}
} catch {
/* table may not exist */
} catch (e) {
debug(`complexity lookup failed for node ${node.id}: ${e.message}`);
}

return {
Expand Down Expand Up @@ -311,8 +312,8 @@ export function contextData(name, customDbPath, opts = {}) {
halsteadVolume: cRow.halstead_volume || 0,
};
}
} catch {
/* table may not exist */
} catch (e) {
debug(`complexity lookup failed for node ${node.id}: ${e.message}`);
}

// Children (parameters, properties, constants)
Expand All @@ -324,8 +325,8 @@ export function contextData(name, customDbPath, opts = {}) {
line: c.line,
endLine: c.end_line || null,
}));
} catch {
/* parent_id column may not exist */
} catch (e) {
debug(`findNodeChildren failed for node ${node.id}: ${e.message}`);
}

return {
Expand Down
5 changes: 3 additions & 2 deletions src/domain/analysis/exports.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
findNodesByFile,
openReadonlyOrFail,
} from '../../db/index.js';
import { debug } from '../../infrastructure/logger.js';
import { isTestFile } from '../../infrastructure/test-filter.js';
import {
createFileLinesReader,
Expand Down Expand Up @@ -60,8 +61,8 @@ function exportsFileImpl(db, target, noTests, getFileLines, unused) {
try {
db.prepare('SELECT exported FROM nodes LIMIT 0').raw();
hasExportedCol = true;
} catch {
/* old DB without exported column */
} catch (e) {
debug(`exported column not available, using fallback: ${e.message}`);
}

return fileNodes.map((fn) => {
Expand Down
13 changes: 7 additions & 6 deletions src/domain/analysis/impact.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { evaluateBoundaries } from '../../features/boundaries.js';
import { coChangeForFiles } from '../../features/cochange.js';
import { ownersForFiles } from '../../features/owners.js';
import { loadConfig } from '../../infrastructure/config.js';
import { debug } from '../../infrastructure/logger.js';
import { isTestFile } from '../../infrastructure/test-filter.js';
import { normalizeSymbol } from '../../shared/normalize.js';
import { paginateResult } from '../../shared/paginate.js';
Expand Down Expand Up @@ -289,8 +290,8 @@ export function diffImpactData(customDbPath, opts = {}) {
});
// Exclude files already found via static analysis
historicallyCoupled = coResults.filter((r) => !affectedFiles.has(r.file));
} catch {
/* co_changes table doesn't exist — skip silently */
} catch (e) {
debug(`co_changes lookup skipped: ${e.message}`);
}

// Look up CODEOWNERS for changed + affected files
Expand All @@ -305,8 +306,8 @@ export function diffImpactData(customDbPath, opts = {}) {
suggestedReviewers: ownerResult.suggestedReviewers,
};
}
} catch {
/* CODEOWNERS missing or unreadable — skip silently */
} catch (e) {
debug(`CODEOWNERS lookup skipped: ${e.message}`);
}

// Check boundary violations scoped to changed files
Expand All @@ -323,8 +324,8 @@ export function diffImpactData(customDbPath, opts = {}) {
boundaryViolations = result.violations;
boundaryViolationCount = result.violationCount;
}
} catch {
/* boundary check failed — skip silently */
} catch (e) {
debug(`boundary check skipped: ${e.message}`);
}

const base = {
Expand Down
9 changes: 5 additions & 4 deletions src/domain/analysis/module-map.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import path from 'node:path';
import { openReadonlyOrFail, testFilterSQL } from '../../db/index.js';
import { debug } from '../../infrastructure/logger.js';
import { isTestFile } from '../../infrastructure/test-filter.js';
import { findCycles } from '../graph/cycles.js';
import { LANGUAGE_REGISTRY } from '../parser.js';
Expand Down Expand Up @@ -193,8 +194,8 @@ export function statsData(customDbPath, opts = {}) {
builtAt: meta.built_at || null,
};
}
} catch {
/* embeddings table may not exist */
} catch (e) {
debug(`embeddings lookup skipped: ${e.message}`);
}

// Graph quality metrics
Expand Down Expand Up @@ -301,8 +302,8 @@ export function statsData(customDbPath, opts = {}) {
minMI: +Math.min(...miValues).toFixed(1),
};
}
} catch {
/* table may not exist in older DBs */
} catch (e) {
debug(`complexity summary skipped: ${e.message}`);
}

return {
Expand Down
4 changes: 3 additions & 1 deletion src/domain/analysis/symbol-lookup.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
openReadonlyOrFail,
Repository,
} from '../../db/index.js';
import { debug } from '../../infrastructure/logger.js';
import { isTestFile } from '../../infrastructure/test-filter.js';
import { ALL_SYMBOL_KINDS } from '../../shared/kinds.js';
import { getFileHash, normalizeSymbol } from '../../shared/normalize.js';
Expand Down Expand Up @@ -206,7 +207,8 @@ export function childrenData(name, customDbPath, opts = {}) {
let children;
try {
children = findNodeChildren(db, node.id);
} catch {
} catch (e) {
debug(`findNodeChildren failed for node ${node.id}: ${e.message}`);
children = [];
}
if (noTests) children = children.filter((c) => !isTestFile(c.file || node.file));
Expand Down
2 changes: 1 addition & 1 deletion src/domain/graph/builder/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ export function purgeFilesFromGraph(db, files, options = {}) {
}

/** Batch INSERT chunk size for multi-value INSERTs. */
export const BATCH_CHUNK = 200;
const BATCH_CHUNK = 200;

/**
* Batch-insert node rows via multi-value INSERT statements.
Expand Down
Loading
Loading