@optave/codegraph 3.0.4 → 3.1.1
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/README.md +59 -52
- package/grammars/tree-sitter-go.wasm +0 -0
- package/package.json +9 -10
- package/src/ast-analysis/rules/csharp.js +201 -0
- package/src/ast-analysis/rules/go.js +182 -0
- package/src/ast-analysis/rules/index.js +82 -0
- package/src/ast-analysis/rules/java.js +175 -0
- package/src/ast-analysis/rules/javascript.js +246 -0
- package/src/ast-analysis/rules/php.js +219 -0
- package/src/ast-analysis/rules/python.js +196 -0
- package/src/ast-analysis/rules/ruby.js +204 -0
- package/src/ast-analysis/rules/rust.js +173 -0
- package/src/ast-analysis/shared.js +223 -0
- package/src/ast.js +15 -28
- package/src/audit.js +4 -5
- package/src/boundaries.js +1 -1
- package/src/branch-compare.js +84 -79
- package/src/builder.js +274 -159
- package/src/cfg.js +111 -341
- package/src/check.js +3 -3
- package/src/cli.js +122 -167
- package/src/cochange.js +1 -1
- package/src/communities.js +13 -16
- package/src/complexity.js +196 -1239
- package/src/cycles.js +1 -1
- package/src/dataflow.js +274 -697
- package/src/db/connection.js +88 -0
- package/src/db/migrations.js +312 -0
- package/src/db/query-builder.js +280 -0
- package/src/db/repository.js +134 -0
- package/src/db.js +19 -392
- package/src/embedder.js +145 -141
- package/src/export.js +1 -1
- package/src/flow.js +160 -228
- package/src/index.js +36 -2
- package/src/kinds.js +49 -0
- package/src/manifesto.js +3 -8
- package/src/mcp.js +97 -20
- package/src/owners.js +132 -132
- package/src/parser.js +58 -131
- package/src/queries-cli.js +866 -0
- package/src/queries.js +1356 -2261
- package/src/resolve.js +11 -2
- package/src/result-formatter.js +21 -0
- package/src/sequence.js +364 -0
- package/src/structure.js +200 -199
- package/src/test-filter.js +7 -0
- package/src/triage.js +120 -162
- package/src/viewer.js +1 -1
package/src/embedder.js
CHANGED
|
@@ -625,37 +625,39 @@ export async function searchData(query, customDbPath, opts = {}) {
|
|
|
625
625
|
if (!prepared) return null;
|
|
626
626
|
const { db, rows, modelKey, storedDim } = prepared;
|
|
627
627
|
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
if (storedDim && dim !== storedDim) {
|
|
634
|
-
console.log(
|
|
635
|
-
`Warning: query model dimension (${dim}) doesn't match stored embeddings (${storedDim}).`,
|
|
636
|
-
);
|
|
637
|
-
console.log(` Re-run \`codegraph embed\` with the same model, or use --model to match.`);
|
|
638
|
-
db.close();
|
|
639
|
-
return null;
|
|
640
|
-
}
|
|
628
|
+
try {
|
|
629
|
+
const {
|
|
630
|
+
vectors: [queryVec],
|
|
631
|
+
dim,
|
|
632
|
+
} = await embed([query], modelKey);
|
|
641
633
|
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
634
|
+
if (storedDim && dim !== storedDim) {
|
|
635
|
+
console.log(
|
|
636
|
+
`Warning: query model dimension (${dim}) doesn't match stored embeddings (${storedDim}).`,
|
|
637
|
+
);
|
|
638
|
+
console.log(` Re-run \`codegraph embed\` with the same model, or use --model to match.`);
|
|
639
|
+
return null;
|
|
640
|
+
}
|
|
647
641
|
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
642
|
+
const hc = new Map();
|
|
643
|
+
const results = [];
|
|
644
|
+
for (const row of rows) {
|
|
645
|
+
const vec = new Float32Array(new Uint8Array(row.vector).buffer);
|
|
646
|
+
const sim = cosineSim(queryVec, vec);
|
|
647
|
+
|
|
648
|
+
if (sim >= minScore) {
|
|
649
|
+
results.push({
|
|
650
|
+
...normalizeSymbol(row, db, hc),
|
|
651
|
+
similarity: sim,
|
|
652
|
+
});
|
|
653
|
+
}
|
|
653
654
|
}
|
|
654
|
-
}
|
|
655
655
|
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
656
|
+
results.sort((a, b) => b.similarity - a.similarity);
|
|
657
|
+
return { results: results.slice(0, limit) };
|
|
658
|
+
} finally {
|
|
659
|
+
db.close();
|
|
660
|
+
}
|
|
659
661
|
}
|
|
660
662
|
|
|
661
663
|
/**
|
|
@@ -671,82 +673,84 @@ export async function multiSearchData(queries, customDbPath, opts = {}) {
|
|
|
671
673
|
if (!prepared) return null;
|
|
672
674
|
const { db, rows, modelKey, storedDim } = prepared;
|
|
673
675
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
for (let
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
`
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
676
|
+
try {
|
|
677
|
+
const { vectors: queryVecs, dim } = await embed(queries, modelKey);
|
|
678
|
+
|
|
679
|
+
// Warn about similar queries that may bias RRF results
|
|
680
|
+
const SIMILARITY_WARN_THRESHOLD = 0.85;
|
|
681
|
+
for (let i = 0; i < queryVecs.length; i++) {
|
|
682
|
+
for (let j = i + 1; j < queryVecs.length; j++) {
|
|
683
|
+
const sim = cosineSim(queryVecs[i], queryVecs[j]);
|
|
684
|
+
if (sim >= SIMILARITY_WARN_THRESHOLD) {
|
|
685
|
+
warn(
|
|
686
|
+
`Queries "${queries[i]}" and "${queries[j]}" are very similar ` +
|
|
687
|
+
`(${(sim * 100).toFixed(0)}% cosine similarity). ` +
|
|
688
|
+
`This may bias RRF results toward their shared matches. ` +
|
|
689
|
+
`Consider using more distinct queries.`,
|
|
690
|
+
);
|
|
691
|
+
}
|
|
688
692
|
}
|
|
689
693
|
}
|
|
690
|
-
}
|
|
691
694
|
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
}
|
|
695
|
+
if (storedDim && dim !== storedDim) {
|
|
696
|
+
console.log(
|
|
697
|
+
`Warning: query model dimension (${dim}) doesn't match stored embeddings (${storedDim}).`,
|
|
698
|
+
);
|
|
699
|
+
console.log(` Re-run \`codegraph embed\` with the same model, or use --model to match.`);
|
|
700
|
+
return null;
|
|
701
|
+
}
|
|
700
702
|
|
|
701
|
-
|
|
702
|
-
|
|
703
|
+
// Parse row vectors once
|
|
704
|
+
const rowVecs = rows.map((row) => new Float32Array(new Uint8Array(row.vector).buffer));
|
|
703
705
|
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
706
|
+
// For each query: compute similarities, filter by minScore, rank
|
|
707
|
+
const perQueryRanked = queries.map((_query, qi) => {
|
|
708
|
+
const scored = [];
|
|
709
|
+
for (let ri = 0; ri < rows.length; ri++) {
|
|
710
|
+
const sim = cosineSim(queryVecs[qi], rowVecs[ri]);
|
|
711
|
+
if (sim >= minScore) {
|
|
712
|
+
scored.push({ rowIndex: ri, similarity: sim });
|
|
713
|
+
}
|
|
711
714
|
}
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
});
|
|
715
|
+
scored.sort((a, b) => b.similarity - a.similarity);
|
|
716
|
+
// Assign 1-indexed ranks
|
|
717
|
+
return scored.map((item, rank) => ({ ...item, rank: rank + 1 }));
|
|
718
|
+
});
|
|
717
719
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
720
|
+
// Fuse results using RRF: for each unique row, sum 1/(k + rank_i) across queries
|
|
721
|
+
const fusionMap = new Map(); // rowIndex -> { rrfScore, queryScores[] }
|
|
722
|
+
for (let qi = 0; qi < queries.length; qi++) {
|
|
723
|
+
for (const item of perQueryRanked[qi]) {
|
|
724
|
+
if (!fusionMap.has(item.rowIndex)) {
|
|
725
|
+
fusionMap.set(item.rowIndex, { rrfScore: 0, queryScores: [] });
|
|
726
|
+
}
|
|
727
|
+
const entry = fusionMap.get(item.rowIndex);
|
|
728
|
+
entry.rrfScore += 1 / (k + item.rank);
|
|
729
|
+
entry.queryScores.push({
|
|
730
|
+
query: queries[qi],
|
|
731
|
+
similarity: item.similarity,
|
|
732
|
+
rank: item.rank,
|
|
733
|
+
});
|
|
724
734
|
}
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// Build results sorted by RRF score
|
|
738
|
+
const hc = new Map();
|
|
739
|
+
const results = [];
|
|
740
|
+
for (const [rowIndex, entry] of fusionMap) {
|
|
741
|
+
const row = rows[rowIndex];
|
|
742
|
+
results.push({
|
|
743
|
+
...normalizeSymbol(row, db, hc),
|
|
744
|
+
rrf: entry.rrfScore,
|
|
745
|
+
queryScores: entry.queryScores,
|
|
731
746
|
});
|
|
732
747
|
}
|
|
733
|
-
}
|
|
734
748
|
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
const row = rows[rowIndex];
|
|
740
|
-
results.push({
|
|
741
|
-
...normalizeSymbol(row, db, hc),
|
|
742
|
-
rrf: entry.rrfScore,
|
|
743
|
-
queryScores: entry.queryScores,
|
|
744
|
-
});
|
|
749
|
+
results.sort((a, b) => b.rrf - a.rrf);
|
|
750
|
+
return { results: results.slice(0, limit) };
|
|
751
|
+
} finally {
|
|
752
|
+
db.close();
|
|
745
753
|
}
|
|
746
|
-
|
|
747
|
-
results.sort((a, b) => b.rrf - a.rrf);
|
|
748
|
-
db.close();
|
|
749
|
-
return { results: results.slice(0, limit) };
|
|
750
754
|
}
|
|
751
755
|
|
|
752
756
|
/**
|
|
@@ -788,64 +792,64 @@ export function ftsSearchData(query, customDbPath, opts = {}) {
|
|
|
788
792
|
|
|
789
793
|
const db = openReadonlyOrFail(customDbPath);
|
|
790
794
|
|
|
791
|
-
|
|
792
|
-
db
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
const ftsQuery = sanitizeFtsQuery(query);
|
|
797
|
-
if (!ftsQuery) {
|
|
798
|
-
db.close();
|
|
799
|
-
return { results: [] };
|
|
800
|
-
}
|
|
795
|
+
try {
|
|
796
|
+
if (!hasFtsIndex(db)) {
|
|
797
|
+
return null;
|
|
798
|
+
}
|
|
801
799
|
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
JOIN nodes n ON f.rowid = n.id
|
|
807
|
-
WHERE fts_index MATCH ?
|
|
808
|
-
`;
|
|
809
|
-
const params = [ftsQuery];
|
|
800
|
+
const ftsQuery = sanitizeFtsQuery(query);
|
|
801
|
+
if (!ftsQuery) {
|
|
802
|
+
return { results: [] };
|
|
803
|
+
}
|
|
810
804
|
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
805
|
+
let sql = `
|
|
806
|
+
SELECT f.rowid AS node_id, rank AS bm25_score,
|
|
807
|
+
n.name, n.kind, n.file, n.line, n.end_line, n.role
|
|
808
|
+
FROM fts_index f
|
|
809
|
+
JOIN nodes n ON f.rowid = n.id
|
|
810
|
+
WHERE fts_index MATCH ?
|
|
811
|
+
`;
|
|
812
|
+
const params = [ftsQuery];
|
|
813
|
+
|
|
814
|
+
if (opts.kind) {
|
|
815
|
+
sql += ' AND n.kind = ?';
|
|
816
|
+
params.push(opts.kind);
|
|
817
|
+
}
|
|
815
818
|
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
819
|
+
const isGlob = opts.filePattern && /[*?[\]]/.test(opts.filePattern);
|
|
820
|
+
if (opts.filePattern && !isGlob) {
|
|
821
|
+
sql += ' AND n.file LIKE ?';
|
|
822
|
+
params.push(`%${opts.filePattern}%`);
|
|
823
|
+
}
|
|
821
824
|
|
|
822
|
-
|
|
823
|
-
|
|
825
|
+
sql += ' ORDER BY rank LIMIT ?';
|
|
826
|
+
params.push(limit * 5); // fetch generous set for post-filtering
|
|
824
827
|
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
}
|
|
828
|
+
let rows;
|
|
829
|
+
try {
|
|
830
|
+
rows = db.prepare(sql).all(...params);
|
|
831
|
+
} catch {
|
|
832
|
+
// Invalid FTS5 query syntax — return empty
|
|
833
|
+
return { results: [] };
|
|
834
|
+
}
|
|
833
835
|
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
836
|
+
if (isGlob) {
|
|
837
|
+
rows = rows.filter((row) => globMatch(row.file, opts.filePattern));
|
|
838
|
+
}
|
|
839
|
+
if (noTests) {
|
|
840
|
+
rows = rows.filter((row) => !TEST_PATTERN.test(row.file));
|
|
841
|
+
}
|
|
840
842
|
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
843
|
+
const hc = new Map();
|
|
844
|
+
const results = rows.slice(0, limit).map((row) => ({
|
|
845
|
+
...normalizeSymbol(row, db, hc),
|
|
846
|
+
bm25Score: -row.bm25_score, // FTS5 rank is negative; negate for display
|
|
847
|
+
}));
|
|
846
848
|
|
|
847
|
-
|
|
848
|
-
|
|
849
|
+
return { results };
|
|
850
|
+
} finally {
|
|
851
|
+
db.close();
|
|
852
|
+
}
|
|
849
853
|
}
|
|
850
854
|
|
|
851
855
|
/**
|