Skip to content

Commit 885f674

Browse files
committed
tool logic fixes and improvements
1 parent f760f26 commit 885f674

File tree

18 files changed

+272
-69
lines changed

18 files changed

+272
-69
lines changed

src/cli/commands/init.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ const DEFAULT_IGNORE_PATTERNS = [
6868
"**/build/**",
6969
"**/.git/**",
7070
"**/coverage/**",
71+
"**/target/**",
72+
"**/vendor/**",
73+
"**/*.min.js",
74+
"**/*.min.css",
7175
];
7276

7377
const LANGUAGE_BY_EXTENSION: Record<string, LanguageType> = {

src/code/hotpath.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ export async function extractHotPath(
243243
});
244244
return null;
245245
}
246-
content = await readFile(filePath, "utf-8");
246+
content = (await readFile(filePath, "utf-8")).replace(/\r\n/g, "\n");
247247
} catch (error) {
248248
logger.warn("Failed to read file for hot path extraction", {
249249
filePath: file.relPath,

src/code/skeleton.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -701,7 +701,7 @@ export async function generateSymbolSkeleton(
701701
return null;
702702
}
703703

704-
const content = await readFile(filePath, "utf-8");
704+
const content = (await readFile(filePath, "utf-8")).replace(/\r\n/g, "\n");
705705
const tree = parseFile(content, `.${extension}`);
706706

707707
if (!tree) {
@@ -799,7 +799,7 @@ export async function generateFileSkeleton(
799799
return null;
800800
}
801801

802-
const content = await readFile(absPath, "utf-8");
802+
const content = (await readFile(absPath, "utf-8")).replace(/\r\n/g, "\n");
803803
const tree = parseFile(content, `.${extension}`);
804804

805805
if (!tree) {
@@ -1138,7 +1138,7 @@ export async function generateSkeletonIR(
11381138
return null;
11391139
}
11401140

1141-
const content = await readFile(filePath, "utf-8");
1141+
const content = (await readFile(filePath, "utf-8")).replace(/\r\n/g, "\n");
11421142
const tree = parseFile(content, `.${extension}`);
11431143

11441144
if (!tree) {

src/code/windows.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export async function extractCodeWindow(
5252
return null;
5353
}
5454

55-
const lines = fileContent.split("\n");
55+
const lines = fileContent.replace(/\r\n/g, "\n").split("\n");
5656

5757
const startLine = symbol.rangeStartLine;
5858
const endLine = symbol.rangeEndLine;

src/db/ladybug-core.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,20 @@ async function execute(
121121
statement: string,
122122
params: Record<string, unknown> = {},
123123
): Promise<QueryResult> {
124-
const prepared = await getPreparedStatement(conn, statement);
125-
// Ladybug accepts string | number | boolean | null | bigint — callers pass
126-
// Record<string, unknown> for convenience; the cast is safe.
127-
const result = await conn.execute(
128-
prepared,
129-
params as Parameters<Connection["execute"]>[1],
130-
);
131-
return result;
124+
try {
125+
const prepared = await getPreparedStatement(conn, statement);
126+
// Ladybug accepts string | number | boolean | null | bigint — callers pass
127+
// Record<string, unknown> for convenience; the cast is safe.
128+
const result = await conn.execute(
129+
prepared,
130+
params as Parameters<Connection["execute"]>[1],
131+
);
132+
return result;
133+
} catch (err) {
134+
throw new DatabaseError(
135+
`Query execution failed: ${err instanceof Error ? err.message : String(err)}`,
136+
);
137+
}
132138
}
133139

134140
export async function queryAll<T>(

src/db/ladybug-symbols.ts

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -826,11 +826,19 @@ async function searchSymbolsSingleTerm(
826826
WITH s, f,
827827
CASE WHEN s.name = $query THEN 0 ELSE 1 END AS exactNameRank,
828828
CASE WHEN lower(s.name) = lower($query) THEN 0 ELSE 1 END AS ciExactNameRank,
829+
CASE
830+
WHEN lower(coalesce(s.searchText, '')) CONTAINS (' ' || lower($query) || ' ')
831+
OR lower(coalesce(s.searchText, '')) STARTS WITH (lower($query) || ' ')
832+
OR lower(coalesce(s.searchText, '')) ENDS WITH (' ' || lower($query))
833+
THEN 0 ELSE 1
834+
END AS wordBoundaryRank,
829835
CASE
830836
WHEN f.relPath CONTAINS '/adapter/' THEN 2
831837
WHEN f.relPath CONTAINS '/tests/' OR f.relPath STARTS WITH 'tests/' THEN 2
832838
WHEN f.relPath STARTS WITH 'scripts/' THEN 2
833839
WHEN f.relPath CONTAINS '.test.' OR f.relPath CONTAINS '.spec.' THEN 2
840+
WHEN f.relPath CONTAINS 'target/' THEN 2
841+
WHEN f.relPath CONTAINS 'vendor/' THEN 2
834842
ELSE 0
835843
END AS filePenalty,
836844
CASE s.kind
@@ -861,8 +869,9 @@ async function searchSymbolsSingleTerm(
861869
s.invariantsJson AS invariantsJson,
862870
s.sideEffectsJson AS sideEffectsJson,
863871
s.updatedAt AS updatedAt
864-
ORDER BY exactNameRank, ciExactNameRank, filePenalty, kindRank, nameMatchRank`,
865-
{ repoId, query: term },
872+
ORDER BY exactNameRank, ciExactNameRank, wordBoundaryRank, filePenalty, kindRank, nameMatchRank
873+
LIMIT $limit`,
874+
{ repoId, query: term, limit: 200 },
866875
);
867876
}
868877

@@ -923,7 +932,16 @@ export interface SearchSymbolLiteRow {
923932
export function splitSearchTerms(query: string): string[] {
924933
const trimmed = query.trim();
925934
if (!trimmed) return [];
926-
if (!trimmed.includes(" ")) return [trimmed];
935+
if (!trimmed.includes(" ")) {
936+
// Split camelCase/PascalCase into words for multi-term search
937+
const words = trimmed.match(
938+
/[A-Z]{2,}(?=[A-Z][a-z]|$)|[A-Z]?[a-z]+|[A-Z]+|[0-9]+/g,
939+
);
940+
if (words && words.length > 1) {
941+
return words.map((w) => w.toLowerCase());
942+
}
943+
return [trimmed];
944+
}
927945
return trimmed.split(/\s+/).filter((t) => t.length > 0);
928946
}
929947

@@ -941,11 +959,19 @@ async function searchSymbolsLiteSingleTerm(
941959
WITH s, f,
942960
CASE WHEN s.name = $query THEN 0 ELSE 1 END AS exactNameRank,
943961
CASE WHEN lower(s.name) = lower($query) THEN 0 ELSE 1 END AS ciExactNameRank,
962+
CASE
963+
WHEN lower(coalesce(s.searchText, '')) CONTAINS (' ' || lower($query) || ' ')
964+
OR lower(coalesce(s.searchText, '')) STARTS WITH (lower($query) || ' ')
965+
OR lower(coalesce(s.searchText, '')) ENDS WITH (' ' || lower($query))
966+
THEN 0 ELSE 1
967+
END AS wordBoundaryRank,
944968
CASE
945969
WHEN f.relPath CONTAINS '/adapter/' THEN 2
946970
WHEN f.relPath CONTAINS '/tests/' OR f.relPath STARTS WITH 'tests/' THEN 2
947971
WHEN f.relPath STARTS WITH 'scripts/' THEN 2
948972
WHEN f.relPath CONTAINS '.test.' OR f.relPath CONTAINS '.spec.' THEN 2
973+
WHEN f.relPath CONTAINS 'target/' THEN 2
974+
WHEN f.relPath CONTAINS 'vendor/' THEN 2
949975
ELSE 0
950976
END AS filePenalty,
951977
CASE s.kind
@@ -963,8 +989,9 @@ async function searchSymbolsLiteSingleTerm(
963989
s.name AS name,
964990
f.fileId AS fileId,
965991
s.kind AS kind
966-
ORDER BY exactNameRank, ciExactNameRank, filePenalty, kindRank, nameMatchRank`,
967-
{ repoId, query: term },
992+
ORDER BY exactNameRank, ciExactNameRank, wordBoundaryRank, filePenalty, kindRank, nameMatchRank
993+
LIMIT $limit`,
994+
{ repoId, query: term, limit: 200 },
968995
);
969996
}
970997

src/graph/metrics.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,19 @@ const MAX_BFS_VISITED = 500;
249249
*/
250250
function isTestFile(relPath: string): boolean {
251251
const normalized = relPath.replace(/\\/g, "/");
252+
253+
// Exclude fixture/testdata directories — these are test inputs, not tests
254+
if (
255+
normalized.includes("/fixtures/") ||
256+
normalized.includes("/__fixtures__/") ||
257+
normalized.includes("/testdata/") ||
258+
normalized.includes("/test-data/") ||
259+
normalized.includes("/mocks/") ||
260+
normalized.includes("/stubs/")
261+
) {
262+
return false;
263+
}
264+
252265
if (
253266
normalized.endsWith(".test.ts") ||
254267
normalized.endsWith(".test.js") ||
@@ -263,7 +276,14 @@ function isTestFile(relPath: string): boolean {
263276
normalized.includes("/__tests__/") ||
264277
normalized.startsWith("__tests__/")
265278
) {
266-
return true;
279+
// Only count as test file if it has a test-like extension
280+
return (
281+
normalized.endsWith(".ts") ||
282+
normalized.endsWith(".js") ||
283+
normalized.endsWith(".tsx") ||
284+
normalized.endsWith(".jsx") ||
285+
normalized.endsWith(".mjs")
286+
);
267287
}
268288
return false;
269289
}

src/graph/score.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,19 @@ function calculateStructuralSpecificity(file?: FileRow): number {
185185
specificity *= 0.75;
186186
}
187187

188+
if (
189+
relPath.includes("/target/") ||
190+
relPath.startsWith("target/") ||
191+
relPath.includes("/vendor/") ||
192+
relPath.startsWith("vendor/")
193+
) {
194+
specificity *= 0.3;
195+
}
196+
197+
if (relPath.endsWith(".min.js") || relPath.endsWith(".min.css")) {
198+
specificity *= 0.2;
199+
}
200+
188201
if (/(^|\/)(index|tools|types|main|mod|util|utils)\.[^.]+$/.test(relPath)) {
189202
specificity *= 0.72;
190203
}

src/graph/slice/start-node-resolver.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,31 +76,62 @@ export const START_NODE_SOURCE_SCORE: Record<StartNodeSource, number> = {
7676
export const TASK_TEXT_STOP_WORDS = new Set([
7777
// English stop words
7878
"a",
79+
"about",
7980
"an",
8081
"and",
8182
"are",
8283
"as",
8384
"at",
8485
"be",
86+
"been",
87+
"between",
8588
"by",
89+
"can",
90+
"could",
8691
"do",
8792
"does",
93+
"end",
94+
"explain",
8895
"for",
8996
"from",
97+
"had",
98+
"have",
99+
"how",
90100
"id",
91101
"in",
92102
"is",
93103
"it",
104+
"its",
105+
"just",
106+
"know",
107+
"much",
94108
"of",
95109
"on",
110+
"only",
96111
"or",
97112
"please",
113+
"some",
98114
"task",
115+
"than",
99116
"that",
100117
"the",
118+
"them",
119+
"they",
101120
"this",
121+
"through",
102122
"to",
123+
"understand",
124+
"very",
125+
"was",
126+
"were",
127+
"what",
128+
"when",
129+
"where",
130+
"which",
131+
"who",
132+
"why",
103133
"with",
134+
"would",
104135
// Code-common words that produce excessive false-positive matches
105136
// across unrelated symbols when used as taskText tokens.
106137
"add",

src/indexer/edge-builder.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export { cleanupUnresolvedEdges } from "./edge-builder/cleanup.js";
2121
export {
2222
findEnclosingSymbolByRange,
2323
resolvePass2Targets,
24+
resolveUnresolvedImportEdges,
2425
resolveTsCallEdgesPass2,
2526
} from "./edge-builder/pass2.js";
2627
export type {

0 commit comments

Comments
 (0)