sensemaking 0.17.0 → 0.18.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/README.md +15 -13
- package/dist/cjs/cli/map.js +147 -6
- package/dist/cjs/cli/map.js.map +1 -1
- package/dist/cjs/cli/named.js +20 -12
- package/dist/cjs/cli/named.js.map +1 -1
- package/dist/cjs/cli/path.js +190 -29
- package/dist/cjs/cli/path.js.map +1 -1
- package/dist/cjs/cli/peek.js +148 -7
- package/dist/cjs/cli/peek.js.map +1 -1
- package/dist/cjs/cli/related.js +5 -5
- package/dist/cjs/cli/related.js.map +1 -1
- package/dist/cjs/cli/search.js +5 -5
- package/dist/cjs/cli/search.js.map +1 -1
- package/dist/cjs/cli/shared.d.cts +3 -3
- package/dist/cjs/cli/shared.d.ts +3 -3
- package/dist/cjs/cli/shared.js +156 -58
- package/dist/cjs/cli/shared.js.map +1 -1
- package/dist/cjs/cli/sql.js +151 -10
- package/dist/cjs/cli/sql.js.map +1 -1
- package/dist/cjs/cli/status.js +187 -62
- package/dist/cjs/cli/status.js.map +1 -1
- package/dist/cjs/commands/map.d.cts +2 -2
- package/dist/cjs/commands/map.d.ts +2 -2
- package/dist/cjs/commands/map.js +373 -67
- package/dist/cjs/commands/map.js.map +1 -1
- package/dist/cjs/commands/peek.d.cts +2 -2
- package/dist/cjs/commands/peek.d.ts +2 -2
- package/dist/cjs/commands/peek.js +321 -79
- package/dist/cjs/commands/peek.js.map +1 -1
- package/dist/cjs/commands/related.d.cts +2 -2
- package/dist/cjs/commands/related.d.ts +2 -2
- package/dist/cjs/commands/related.js +55 -10
- package/dist/cjs/commands/related.js.map +1 -1
- package/dist/cjs/commands/scope.d.cts +7 -7
- package/dist/cjs/commands/scope.d.ts +7 -7
- package/dist/cjs/commands/scope.js +382 -45
- package/dist/cjs/commands/scope.js.map +1 -1
- package/dist/cjs/commands/search.d.cts +2 -2
- package/dist/cjs/commands/search.d.ts +2 -2
- package/dist/cjs/commands/search.js +292 -99
- package/dist/cjs/commands/search.js.map +1 -1
- package/dist/cjs/commands/signals.d.cts +4 -10
- package/dist/cjs/commands/signals.d.ts +4 -10
- package/dist/cjs/commands/signals.js +91 -45
- package/dist/cjs/commands/signals.js.map +1 -1
- package/dist/cjs/commands/status.d.cts +2 -2
- package/dist/cjs/commands/status.d.ts +2 -2
- package/dist/cjs/commands/status.js +247 -10
- package/dist/cjs/commands/status.js.map +1 -1
- package/dist/cjs/config/access.d.cts +1 -0
- package/dist/cjs/config/access.d.ts +1 -0
- package/dist/cjs/config/access.js +7 -0
- package/dist/cjs/config/access.js.map +1 -1
- package/dist/cjs/config/index.d.cts +1 -1
- package/dist/cjs/config/index.d.ts +1 -1
- package/dist/cjs/config/index.js +3 -0
- package/dist/cjs/config/index.js.map +1 -1
- package/dist/cjs/config/types.d.cts +1 -0
- package/dist/cjs/config/types.d.ts +1 -0
- package/dist/cjs/config/types.js.map +1 -1
- package/dist/cjs/config/validate.js +10 -1
- package/dist/cjs/config/validate.js.map +1 -1
- package/dist/cjs/embed/distribution.d.cts +4 -0
- package/dist/cjs/embed/distribution.d.ts +4 -0
- package/dist/cjs/embed/distribution.js +186 -0
- package/dist/cjs/embed/distribution.js.map +1 -0
- package/dist/cjs/embed/langfit.d.cts +2 -5
- package/dist/cjs/embed/langfit.d.ts +2 -5
- package/dist/cjs/embed/langfit.js +73 -63
- package/dist/cjs/embed/langfit.js.map +1 -1
- package/dist/cjs/embed/query.d.cts +6 -13
- package/dist/cjs/embed/query.d.ts +6 -13
- package/dist/cjs/embed/query.js +56 -168
- package/dist/cjs/embed/query.js.map +1 -1
- package/dist/cjs/embed/static.js +13 -3
- package/dist/cjs/embed/static.js.map +1 -1
- package/dist/cjs/embed/types.d.cts +1 -0
- package/dist/cjs/embed/types.d.ts +1 -0
- package/dist/cjs/embed/types.js +11 -2
- package/dist/cjs/embed/types.js.map +1 -1
- package/dist/cjs/errors.d.cts +1 -1
- package/dist/cjs/errors.d.ts +1 -1
- package/dist/cjs/errors.js.map +1 -1
- package/dist/cjs/features/embed.js +218 -11
- package/dist/cjs/features/embed.js.map +1 -1
- package/dist/cjs/features/index.d.cts +1 -1
- package/dist/cjs/features/index.d.ts +1 -1
- package/dist/cjs/features/index.js +6 -0
- package/dist/cjs/features/index.js.map +1 -1
- package/dist/cjs/features/links.d.cts +7 -2
- package/dist/cjs/features/links.d.ts +7 -2
- package/dist/cjs/features/links.js +710 -198
- package/dist/cjs/features/links.js.map +1 -1
- package/dist/cjs/features/rank.js +231 -31
- package/dist/cjs/features/rank.js.map +1 -1
- package/dist/cjs/features/sections.js +218 -8
- package/dist/cjs/features/sections.js.map +1 -1
- package/dist/cjs/features/tags.js +236 -40
- package/dist/cjs/features/tags.js.map +1 -1
- package/dist/cjs/features/types.d.cts +9 -5
- package/dist/cjs/features/types.d.ts +9 -5
- package/dist/cjs/graph/traverse.d.cts +2 -2
- package/dist/cjs/graph/traverse.d.ts +2 -2
- package/dist/cjs/graph/traverse.js +321 -42
- package/dist/cjs/graph/traverse.js.map +1 -1
- package/dist/cjs/index.d.cts +3 -2
- package/dist/cjs/index.d.ts +3 -2
- package/dist/cjs/index.js +2 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/lib/guarded-tick.d.cts +1 -0
- package/dist/cjs/lib/guarded-tick.d.ts +1 -0
- package/dist/cjs/lib/guarded-tick.js +19 -0
- package/dist/cjs/lib/guarded-tick.js.map +1 -0
- package/dist/cjs/output/column-hint.d.cts +1 -2
- package/dist/cjs/output/column-hint.d.ts +1 -2
- package/dist/cjs/output/column-hint.js +1 -4
- package/dist/cjs/output/column-hint.js.map +1 -1
- package/dist/cjs/output/output.d.cts +2 -1
- package/dist/cjs/output/output.d.ts +2 -1
- package/dist/cjs/output/output.js +515 -97
- package/dist/cjs/output/output.js.map +1 -1
- package/dist/cjs/scan/frontmatter.js.map +1 -1
- package/dist/cjs/scan/index.js.map +1 -1
- package/dist/cjs/scan/reparse.d.cts +10 -0
- package/dist/cjs/scan/reparse.d.ts +10 -0
- package/dist/cjs/scan/reparse.js +104 -0
- package/dist/cjs/scan/reparse.js.map +1 -0
- package/dist/cjs/store/duckdb/batch.d.cts +16 -0
- package/dist/cjs/store/duckdb/batch.d.ts +16 -0
- package/dist/cjs/store/duckdb/batch.js +208 -0
- package/dist/cjs/store/duckdb/batch.js.map +1 -0
- package/dist/cjs/store/duckdb/connection.d.cts +5 -0
- package/dist/cjs/store/duckdb/connection.d.ts +5 -0
- package/dist/cjs/store/duckdb/connection.js +772 -0
- package/dist/cjs/store/duckdb/connection.js.map +1 -0
- package/dist/cjs/store/duckdb/lexical.d.cts +10 -0
- package/dist/cjs/store/duckdb/lexical.d.ts +10 -0
- package/dist/cjs/store/duckdb/lexical.js +502 -0
- package/dist/cjs/store/duckdb/lexical.js.map +1 -0
- package/dist/cjs/store/duckdb/native.d.cts +3 -0
- package/dist/cjs/store/duckdb/native.d.ts +3 -0
- package/dist/cjs/store/duckdb/native.js +360 -0
- package/dist/cjs/store/duckdb/native.js.map +1 -0
- package/dist/cjs/store/duckdb/open.d.cts +12 -0
- package/dist/cjs/store/duckdb/open.d.ts +12 -0
- package/dist/cjs/store/duckdb/open.js +501 -0
- package/dist/cjs/store/duckdb/open.js.map +1 -0
- package/dist/cjs/store/duckdb/reconcile.d.cts +6 -0
- package/dist/cjs/store/duckdb/reconcile.d.ts +6 -0
- package/dist/cjs/store/duckdb/reconcile.js +753 -0
- package/dist/cjs/store/duckdb/reconcile.js.map +1 -0
- package/dist/cjs/store/duckdb/sql-functions.d.cts +2 -0
- package/dist/cjs/store/duckdb/sql-functions.d.ts +2 -0
- package/dist/cjs/store/duckdb/sql-functions.js +203 -0
- package/dist/cjs/store/duckdb/sql-functions.js.map +1 -0
- package/dist/cjs/store/duckdb/store.d.cts +5 -0
- package/dist/cjs/store/duckdb/store.d.ts +5 -0
- package/dist/cjs/store/duckdb/store.js +504 -0
- package/dist/cjs/store/duckdb/store.js.map +1 -0
- package/dist/cjs/store/duckdb/vectors.d.cts +9 -0
- package/dist/cjs/store/duckdb/vectors.d.ts +9 -0
- package/dist/cjs/store/duckdb/vectors.js +502 -0
- package/dist/cjs/store/duckdb/vectors.js.map +1 -0
- package/dist/cjs/store/index.d.cts +7 -0
- package/dist/cjs/store/index.d.ts +7 -0
- package/dist/cjs/store/index.js +201 -0
- package/dist/cjs/store/index.js.map +1 -0
- package/dist/cjs/store/meta.d.cts +3 -0
- package/dist/cjs/store/meta.d.ts +3 -0
- package/dist/cjs/store/meta.js +215 -0
- package/dist/cjs/store/meta.js.map +1 -0
- package/dist/cjs/store/shared.d.cts +5 -0
- package/dist/cjs/store/shared.d.ts +5 -0
- package/dist/cjs/store/shared.js +252 -0
- package/dist/cjs/store/shared.js.map +1 -0
- package/dist/cjs/store/sql-functions.d.cts +2 -0
- package/dist/cjs/store/sql-functions.d.ts +2 -0
- package/dist/cjs/store/sql-functions.js +48 -0
- package/dist/cjs/store/sql-functions.js.map +1 -0
- package/dist/cjs/store/sqlite/connection.d.cts +3 -0
- package/dist/cjs/store/sqlite/connection.d.ts +3 -0
- package/dist/cjs/store/sqlite/connection.js +516 -0
- package/dist/cjs/store/sqlite/connection.js.map +1 -0
- package/dist/cjs/store/sqlite/lexical.d.cts +2 -0
- package/dist/cjs/store/sqlite/lexical.d.ts +2 -0
- package/dist/cjs/store/sqlite/lexical.js +177 -0
- package/dist/cjs/store/sqlite/lexical.js.map +1 -0
- package/dist/cjs/{db → store/sqlite}/open.d.cts +5 -5
- package/dist/cjs/{db → store/sqlite}/open.d.ts +5 -5
- package/dist/cjs/store/sqlite/open.js +604 -0
- package/dist/cjs/store/sqlite/open.js.map +1 -0
- package/dist/cjs/store/sqlite/reconcile.d.cts +10 -0
- package/dist/cjs/store/sqlite/reconcile.d.ts +10 -0
- package/dist/cjs/store/sqlite/reconcile.js +1020 -0
- package/dist/cjs/store/sqlite/reconcile.js.map +1 -0
- package/dist/cjs/store/sqlite/sql-functions.js +34 -0
- package/dist/cjs/store/sqlite/sql-functions.js.map +1 -0
- package/dist/cjs/store/sqlite/store.d.cts +5 -0
- package/dist/cjs/store/sqlite/store.d.ts +5 -0
- package/dist/cjs/store/sqlite/store.js +588 -0
- package/dist/cjs/store/sqlite/store.js.map +1 -0
- package/dist/cjs/store/sqlite/vectors.d.cts +13 -0
- package/dist/cjs/store/sqlite/vectors.d.ts +13 -0
- package/dist/cjs/store/sqlite/vectors.js +470 -0
- package/dist/cjs/store/sqlite/vectors.js.map +1 -0
- package/dist/cjs/store/transaction.d.cts +5 -0
- package/dist/cjs/store/transaction.d.ts +5 -0
- package/dist/cjs/store/transaction.js +297 -0
- package/dist/cjs/store/transaction.js.map +1 -0
- package/dist/cjs/store/types.d.cts +91 -0
- package/dist/cjs/store/types.d.ts +91 -0
- package/dist/cjs/store/types.js +15 -0
- package/dist/cjs/store/types.js.map +1 -0
- package/dist/cjs/watch.d.cts +3 -0
- package/dist/cjs/watch.d.ts +3 -0
- package/dist/cjs/watch.js +215 -73
- package/dist/cjs/watch.js.map +1 -1
- package/dist/esm/cli/map.js +5 -5
- package/dist/esm/cli/map.js.map +1 -1
- package/dist/esm/cli/named.js +3 -3
- package/dist/esm/cli/named.js.map +1 -1
- package/dist/esm/cli/path.js +6 -5
- package/dist/esm/cli/path.js.map +1 -1
- package/dist/esm/cli/peek.js +5 -5
- package/dist/esm/cli/peek.js.map +1 -1
- package/dist/esm/cli/related.js +3 -3
- package/dist/esm/cli/related.js.map +1 -1
- package/dist/esm/cli/search.js +2 -2
- package/dist/esm/cli/search.js.map +1 -1
- package/dist/esm/cli/shared.d.ts +3 -3
- package/dist/esm/cli/shared.js +17 -17
- package/dist/esm/cli/shared.js.map +1 -1
- package/dist/esm/cli/sql.js +2 -2
- package/dist/esm/cli/sql.js.map +1 -1
- package/dist/esm/cli/status.js +25 -24
- package/dist/esm/cli/status.js.map +1 -1
- package/dist/esm/commands/map.d.ts +2 -2
- package/dist/esm/commands/map.js +51 -41
- package/dist/esm/commands/map.js.map +1 -1
- package/dist/esm/commands/peek.d.ts +2 -2
- package/dist/esm/commands/peek.js +58 -50
- package/dist/esm/commands/peek.js.map +1 -1
- package/dist/esm/commands/related.d.ts +2 -2
- package/dist/esm/commands/related.js +9 -9
- package/dist/esm/commands/related.js.map +1 -1
- package/dist/esm/commands/scope.d.ts +7 -7
- package/dist/esm/commands/scope.js +28 -17
- package/dist/esm/commands/scope.js.map +1 -1
- package/dist/esm/commands/search.d.ts +2 -2
- package/dist/esm/commands/search.js +42 -35
- package/dist/esm/commands/search.js.map +1 -1
- package/dist/esm/commands/signals.d.ts +4 -10
- package/dist/esm/commands/signals.js +17 -13
- package/dist/esm/commands/signals.js.map +1 -1
- package/dist/esm/commands/status.d.ts +2 -2
- package/dist/esm/commands/status.js +13 -7
- package/dist/esm/commands/status.js.map +1 -1
- package/dist/esm/config/access.d.ts +1 -0
- package/dist/esm/config/access.js +5 -0
- package/dist/esm/config/access.js.map +1 -1
- package/dist/esm/config/index.d.ts +1 -1
- package/dist/esm/config/index.js +1 -1
- package/dist/esm/config/index.js.map +1 -1
- package/dist/esm/config/types.d.ts +1 -0
- package/dist/esm/config/types.js.map +1 -1
- package/dist/esm/config/validate.js +10 -1
- package/dist/esm/config/validate.js.map +1 -1
- package/dist/esm/embed/distribution.d.ts +4 -0
- package/dist/esm/embed/distribution.js +11 -0
- package/dist/esm/embed/distribution.js.map +1 -0
- package/dist/esm/embed/langfit.d.ts +2 -5
- package/dist/esm/embed/langfit.js +13 -13
- package/dist/esm/embed/langfit.js.map +1 -1
- package/dist/esm/embed/query.d.ts +6 -13
- package/dist/esm/embed/query.js +23 -84
- package/dist/esm/embed/query.js.map +1 -1
- package/dist/esm/embed/static.js +6 -1
- package/dist/esm/embed/static.js.map +1 -1
- package/dist/esm/embed/types.d.ts +1 -0
- package/dist/esm/embed/types.js +5 -3
- package/dist/esm/embed/types.js.map +1 -1
- package/dist/esm/errors.d.ts +1 -1
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/features/embed.js +22 -10
- package/dist/esm/features/embed.js.map +1 -1
- package/dist/esm/features/index.d.ts +1 -1
- package/dist/esm/features/index.js +1 -1
- package/dist/esm/features/index.js.map +1 -1
- package/dist/esm/features/links.d.ts +7 -2
- package/dist/esm/features/links.js +124 -97
- package/dist/esm/features/links.js.map +1 -1
- package/dist/esm/features/rank.js +15 -8
- package/dist/esm/features/rank.js.map +1 -1
- package/dist/esm/features/sections.js +20 -7
- package/dist/esm/features/sections.js.map +1 -1
- package/dist/esm/features/tags.js +19 -18
- package/dist/esm/features/tags.js.map +1 -1
- package/dist/esm/features/types.d.ts +9 -5
- package/dist/esm/features/types.js +1 -1
- package/dist/esm/features/types.js.map +1 -1
- package/dist/esm/graph/traverse.d.ts +2 -2
- package/dist/esm/graph/traverse.js +26 -23
- package/dist/esm/graph/traverse.js.map +1 -1
- package/dist/esm/index.d.ts +3 -2
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lib/guarded-tick.d.ts +1 -0
- package/dist/esm/lib/guarded-tick.js +8 -0
- package/dist/esm/lib/guarded-tick.js.map +1 -0
- package/dist/esm/output/column-hint.d.ts +1 -2
- package/dist/esm/output/column-hint.js +3 -2
- package/dist/esm/output/column-hint.js.map +1 -1
- package/dist/esm/output/output.d.ts +2 -1
- package/dist/esm/output/output.js +27 -23
- package/dist/esm/output/output.js.map +1 -1
- package/dist/esm/scan/frontmatter.js +1 -1
- package/dist/esm/scan/frontmatter.js.map +1 -1
- package/dist/esm/scan/index.js +1 -1
- package/dist/esm/scan/index.js.map +1 -1
- package/dist/esm/scan/reparse.d.ts +10 -0
- package/dist/esm/scan/reparse.js +30 -0
- package/dist/esm/scan/reparse.js.map +1 -0
- package/dist/esm/store/duckdb/batch.d.ts +16 -0
- package/dist/esm/store/duckdb/batch.js +96 -0
- package/dist/esm/store/duckdb/batch.js.map +1 -0
- package/dist/esm/store/duckdb/connection.d.ts +5 -0
- package/dist/esm/store/duckdb/connection.js +97 -0
- package/dist/esm/store/duckdb/connection.js.map +1 -0
- package/dist/esm/store/duckdb/lexical.d.ts +10 -0
- package/dist/esm/store/duckdb/lexical.js +163 -0
- package/dist/esm/store/duckdb/lexical.js.map +1 -0
- package/dist/esm/store/duckdb/native.d.ts +3 -0
- package/dist/esm/store/duckdb/native.js +96 -0
- package/dist/esm/store/duckdb/native.js.map +1 -0
- package/dist/esm/store/duckdb/open.d.ts +12 -0
- package/dist/esm/store/duckdb/open.js +127 -0
- package/dist/esm/store/duckdb/open.js.map +1 -0
- package/dist/esm/store/duckdb/reconcile.d.ts +6 -0
- package/dist/esm/store/duckdb/reconcile.js +141 -0
- package/dist/esm/store/duckdb/reconcile.js.map +1 -0
- package/dist/esm/store/duckdb/sql-functions.d.ts +2 -0
- package/dist/esm/store/duckdb/sql-functions.js +56 -0
- package/dist/esm/store/duckdb/sql-functions.js.map +1 -0
- package/dist/esm/store/duckdb/store.d.ts +5 -0
- package/dist/esm/store/duckdb/store.js +104 -0
- package/dist/esm/store/duckdb/store.js.map +1 -0
- package/dist/esm/store/duckdb/vectors.d.ts +9 -0
- package/dist/esm/store/duckdb/vectors.js +171 -0
- package/dist/esm/store/duckdb/vectors.js.map +1 -0
- package/dist/esm/store/index.d.ts +7 -0
- package/dist/esm/store/index.js +35 -0
- package/dist/esm/store/index.js.map +1 -0
- package/dist/esm/store/meta.d.ts +3 -0
- package/dist/esm/store/meta.js +17 -0
- package/dist/esm/store/meta.js.map +1 -0
- package/dist/esm/store/shared.d.ts +5 -0
- package/dist/esm/store/shared.js +24 -0
- package/dist/esm/store/shared.js.map +1 -0
- package/dist/esm/store/sql-functions.d.ts +2 -0
- package/dist/esm/store/sql-functions.js +29 -0
- package/dist/esm/store/sql-functions.js.map +1 -0
- package/dist/esm/store/sqlite/connection.d.ts +3 -0
- package/dist/esm/store/sqlite/connection.js +50 -0
- package/dist/esm/store/sqlite/connection.js.map +1 -0
- package/dist/esm/store/sqlite/lexical.d.ts +2 -0
- package/dist/esm/store/sqlite/lexical.js +21 -0
- package/dist/esm/store/sqlite/lexical.js.map +1 -0
- package/dist/esm/{db → store/sqlite}/open.d.ts +5 -5
- package/dist/esm/store/sqlite/open.js +193 -0
- package/dist/esm/store/sqlite/open.js.map +1 -0
- package/dist/esm/store/sqlite/reconcile.d.ts +10 -0
- package/dist/esm/{db → store/sqlite}/reconcile.js +101 -99
- package/dist/esm/store/sqlite/reconcile.js.map +1 -0
- package/dist/esm/store/sqlite/sql-functions.js +23 -0
- package/dist/esm/store/sqlite/sql-functions.js.map +1 -0
- package/dist/esm/store/sqlite/store.d.ts +5 -0
- package/dist/esm/store/sqlite/store.js +82 -0
- package/dist/esm/store/sqlite/store.js.map +1 -0
- package/dist/esm/store/sqlite/vectors.d.ts +13 -0
- package/dist/esm/store/sqlite/vectors.js +90 -0
- package/dist/esm/store/sqlite/vectors.js.map +1 -0
- package/dist/esm/store/transaction.d.ts +5 -0
- package/dist/esm/store/transaction.js +57 -0
- package/dist/esm/store/transaction.js.map +1 -0
- package/dist/esm/store/types.d.ts +91 -0
- package/dist/esm/store/types.js +11 -0
- package/dist/esm/store/types.js.map +1 -0
- package/dist/esm/watch.d.ts +3 -0
- package/dist/esm/watch.js +39 -22
- package/dist/esm/watch.js.map +1 -1
- package/package.json +3 -1
- package/schema.json +5 -0
- package/skills/sense/EXAMPLES.md +3 -3
- package/skills/sense/SKILL.md +16 -14
- package/skills/sense-bases/EXAMPLES.md +2 -2
- package/skills/sense-bases/SKILL.md +6 -6
- package/skills/sense-setup/EXAMPLES.md +1 -1
- package/skills/sense-setup/SKILL.md +5 -4
- package/dist/cjs/db/index.d.cts +0 -4
- package/dist/cjs/db/index.d.ts +0 -4
- package/dist/cjs/db/index.js +0 -42
- package/dist/cjs/db/index.js.map +0 -1
- package/dist/cjs/db/open.js +0 -230
- package/dist/cjs/db/open.js.map +0 -1
- package/dist/cjs/db/reconcile.d.cts +0 -10
- package/dist/cjs/db/reconcile.d.ts +0 -10
- package/dist/cjs/db/reconcile.js +0 -579
- package/dist/cjs/db/reconcile.js.map +0 -1
- package/dist/cjs/db/shared.d.cts +0 -5
- package/dist/cjs/db/shared.d.ts +0 -5
- package/dist/cjs/db/shared.js +0 -45
- package/dist/cjs/db/shared.js.map +0 -1
- package/dist/cjs/db/sql-functions.js +0 -64
- package/dist/cjs/db/sql-functions.js.map +0 -1
- package/dist/esm/db/index.d.ts +0 -4
- package/dist/esm/db/index.js +0 -5
- package/dist/esm/db/index.js.map +0 -1
- package/dist/esm/db/open.js +0 -165
- package/dist/esm/db/open.js.map +0 -1
- package/dist/esm/db/reconcile.d.ts +0 -10
- package/dist/esm/db/reconcile.js.map +0 -1
- package/dist/esm/db/shared.d.ts +0 -5
- package/dist/esm/db/shared.js +0 -20
- package/dist/esm/db/shared.js.map +0 -1
- package/dist/esm/db/sql-functions.js +0 -47
- package/dist/esm/db/sql-functions.js.map +0 -1
- /package/dist/cjs/{db → store/sqlite}/sql-functions.d.cts +0 -0
- /package/dist/cjs/{db → store/sqlite}/sql-functions.d.ts +0 -0
- /package/dist/esm/{db → store/sqlite}/sql-functions.d.ts +0 -0
|
@@ -17,54 +17,62 @@ export function resolveNote(paths, arg) {
|
|
|
17
17
|
const PEEK_LIST_LIMIT = 20;
|
|
18
18
|
// peek: everything about one note except its prose -- frontmatter, outline with line
|
|
19
19
|
// ranges + token estimates (so the follow-up Read is a range, not the file), links both ways.
|
|
20
|
-
export function peek(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
outbound = [
|
|
41
|
-
|
|
42
|
-
];
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
frontmatter
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
20
|
+
export async function peek(store, cfg, pathArg, overrides = {}) {
|
|
21
|
+
return store.transaction(async ()=>{
|
|
22
|
+
var _row__parse_error, _row__size;
|
|
23
|
+
var _this;
|
|
24
|
+
const pathsStmt = await store.prepare('SELECT "path" FROM frontmatter');
|
|
25
|
+
const paths = (await pathsStmt.all()).map((r)=>r.path);
|
|
26
|
+
const path = resolveNote(paths, pathArg);
|
|
27
|
+
const rowStmt = await store.prepare('SELECT * FROM frontmatter WHERE "path" = ?');
|
|
28
|
+
const row = await rowStmt.get(path);
|
|
29
|
+
const parseError = (_row__parse_error = row._parse_error) !== null && _row__parse_error !== void 0 ? _row__parse_error : null;
|
|
30
|
+
const frontmatter = {};
|
|
31
|
+
for (const [key, value] of Object.entries(row)){
|
|
32
|
+
if (!INTERNAL_COLUMNS.has(key) && value !== null) frontmatter[key] = value;
|
|
33
|
+
}
|
|
34
|
+
let sectionsTotal = 0;
|
|
35
|
+
let sections = [];
|
|
36
|
+
if (featureEnabled(cfg, 'sections')) {
|
|
37
|
+
sectionsTotal = (await (await store.prepare('SELECT COUNT(*) AS n FROM sections WHERE "path" = ?')).get(path)).n;
|
|
38
|
+
sections = await (await store.prepare('SELECT level, heading, start_line, end_line, tokens FROM sections WHERE "path" = ? ORDER BY idx LIMIT ?')).all(path, PEEK_LIST_LIMIT);
|
|
39
|
+
}
|
|
40
|
+
let outbound = [];
|
|
41
|
+
let backlinks = [];
|
|
42
|
+
let unresolved = [];
|
|
43
|
+
let backlinksTotal = 0;
|
|
44
|
+
await scopedPaths(store, cfg, overrides); // validates --preset/--where/--include/--exclude even though peek's own output isn't scope-filtered
|
|
45
|
+
if (featureEnabled(cfg, 'links')) {
|
|
46
|
+
const out = await (await store.prepare('SELECT target, dst FROM links WHERE src = ? AND (dst IS NULL OR dst != src) ORDER BY target')).all(path);
|
|
47
|
+
outbound = [
|
|
48
|
+
...new Set(out.filter((l)=>l.dst !== null).map((l)=>l.dst))
|
|
49
|
+
];
|
|
50
|
+
unresolved = out.filter((l)=>l.dst === null).map((l)=>l.target);
|
|
51
|
+
backlinksTotal = (await (await store.prepare('SELECT COUNT(DISTINCT src) AS n FROM links WHERE dst = ? AND src != dst')).get(path)).n;
|
|
52
|
+
backlinks = (await (await store.prepare('SELECT DISTINCT src FROM links WHERE dst = ? AND src != dst ORDER BY src LIMIT ?')).all(path, PEEK_LIST_LIMIT)).map((r)=>r.src);
|
|
53
|
+
}
|
|
54
|
+
// content.text is the stripped body already computed at reconcile time (no extra file read),
|
|
55
|
+
// floored by the byte/4 estimate so stripped-away syntax and frontmatter can't vanish from the price.
|
|
56
|
+
const body = (_this = await (await store.prepare('SELECT text FROM content WHERE "path" = ?')).get(path)) === null || _this === void 0 ? void 0 : _this.text;
|
|
57
|
+
const byteTokens = Math.ceil(((_row__size = row._size) !== null && _row__size !== void 0 ? _row__size : 0) / 4);
|
|
58
|
+
const tokens = body !== undefined ? Math.max(Math.ceil(estimateTokens(body)), byteTokens) : byteTokens;
|
|
59
|
+
return {
|
|
60
|
+
path,
|
|
61
|
+
tokens,
|
|
62
|
+
frontmatter,
|
|
63
|
+
parseError,
|
|
64
|
+
sections,
|
|
65
|
+
outbound: outbound.slice(0, PEEK_LIST_LIMIT),
|
|
66
|
+
backlinks,
|
|
67
|
+
unresolved: unresolved.slice(0, PEEK_LIST_LIMIT),
|
|
68
|
+
sectionsTotal,
|
|
69
|
+
outboundTotal: outbound.length,
|
|
70
|
+
backlinksTotal,
|
|
71
|
+
unresolvedTotal: unresolved.length,
|
|
72
|
+
off: [
|
|
73
|
+
'sections',
|
|
74
|
+
'links'
|
|
75
|
+
].filter((name)=>!featureEnabled(cfg, name))
|
|
76
|
+
};
|
|
77
|
+
});
|
|
70
78
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/peek.ts"],"sourcesContent":["import posix from 'node:path/posix';\nimport
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/peek.ts"],"sourcesContent":["import posix from 'node:path/posix';\nimport { estimateTokens } from '../chunk/index.ts';\nimport type { FeatureName, ResolvedConfig, SearchOverrides } from '../config/index.ts';\nimport { featureEnabled } from '../config/index.ts';\nimport { SenseError } from '../errors.ts';\nimport type { Row } from '../output/output.ts';\nimport type { Store } from '../store/types.ts';\nimport { INTERNAL_COLUMNS, scopedPaths } from './scope.ts';\n\n// Note resolution shared by peek and path: an exact path, or a unique basename (case\n// insensitive, .md stripped).\nexport function resolveNote(paths: string[], arg: string): string {\n const exact = paths.find((p) => p === arg);\n if (exact) return exact;\n const base = posix.basename(arg).replace(/\\.md$/i, '').toLowerCase();\n const matches = paths.filter((p) => posix.basename(p).replace(/\\.md$/i, '').toLowerCase() === base);\n if (matches.length === 1) return matches[0];\n if (matches.length > 1) throw new SenseError('NOTE_AMBIGUOUS', `\"${arg}\" is ambiguous: ${matches.join(', ')}`);\n throw new SenseError('NOTE_NOT_FOUND', `no note matches \"${arg}\"`);\n}\n\nexport interface Peek {\n path: string;\n tokens: number;\n frontmatter: Row;\n // Set when the note's frontmatter was refused, so an empty frontmatter block reads as \"did\n // not parse\" rather than \"has none\". Same reason `_parse_error` sits in the row.\n parseError: string | null;\n sections: Row[];\n outbound: string[];\n backlinks: string[];\n unresolved: string[];\n // Totals before truncation: a hub can have thousands of backlinks (or a note thousands of\n // headings), and peek's whole point is bounded output. Query the sections/links tables\n // directly for the full list.\n sectionsTotal: number;\n outboundTotal: number;\n backlinksTotal: number;\n unresolvedTotal: number;\n // Bounded k-hop expansion beyond the immediate ring already shown by outbound/backlinks\n // (depth starts at 2).\n off: FeatureName[]; // disabled features whose blocks are omitted (not empty)\n}\n\nconst PEEK_LIST_LIMIT = 20;\n\n// peek: everything about one note except its prose -- frontmatter, outline with line\n// ranges + token estimates (so the follow-up Read is a range, not the file), links both ways.\nexport async function peek(store: Store, cfg: ResolvedConfig, pathArg: string, overrides: SearchOverrides = {}): Promise<Peek> {\n return store.transaction(async () => {\n const pathsStmt = await store.prepare('SELECT \"path\" FROM frontmatter');\n const paths = ((await pathsStmt.all()) as Array<{ path: string }>).map((r) => r.path);\n const path = resolveNote(paths, pathArg);\n\n const rowStmt = await store.prepare('SELECT * FROM frontmatter WHERE \"path\" = ?');\n const row = (await rowStmt.get(path)) as Row;\n const parseError = (row._parse_error as string | null) ?? null;\n const frontmatter: Row = {};\n for (const [key, value] of Object.entries(row)) {\n if (!INTERNAL_COLUMNS.has(key) && value !== null) frontmatter[key] = value;\n }\n\n let sectionsTotal = 0;\n let sections: Row[] = [];\n if (featureEnabled(cfg, 'sections')) {\n sectionsTotal = ((await (await store.prepare('SELECT COUNT(*) AS n FROM sections WHERE \"path\" = ?')).get(path)) as { n: number }).n;\n sections = (await (await store.prepare('SELECT level, heading, start_line, end_line, tokens FROM sections WHERE \"path\" = ? ORDER BY idx LIMIT ?')).all(path, PEEK_LIST_LIMIT)) as Row[];\n }\n\n let outbound: string[] = [];\n let backlinks: string[] = [];\n let unresolved: string[] = [];\n let backlinksTotal = 0;\n await scopedPaths(store, cfg, overrides); // validates --preset/--where/--include/--exclude even though peek's own output isn't scope-filtered\n if (featureEnabled(cfg, 'links')) {\n const out = (await (await store.prepare('SELECT target, dst FROM links WHERE src = ? AND (dst IS NULL OR dst != src) ORDER BY target')).all(path)) as Array<{ target: string; dst: string | null }>;\n outbound = [...new Set(out.filter((l) => l.dst !== null).map((l) => l.dst as string))];\n unresolved = out.filter((l) => l.dst === null).map((l) => l.target);\n backlinksTotal = ((await (await store.prepare('SELECT COUNT(DISTINCT src) AS n FROM links WHERE dst = ? AND src != dst')).get(path)) as { n: number }).n;\n backlinks = ((await (await store.prepare('SELECT DISTINCT src FROM links WHERE dst = ? AND src != dst ORDER BY src LIMIT ?')).all(path, PEEK_LIST_LIMIT)) as Array<{ src: string }>).map((r) => r.src);\n }\n\n // content.text is the stripped body already computed at reconcile time (no extra file read),\n // floored by the byte/4 estimate so stripped-away syntax and frontmatter can't vanish from the price.\n const body = ((await (await store.prepare('SELECT text FROM content WHERE \"path\" = ?')).get(path)) as { text: string } | undefined)?.text;\n const byteTokens = Math.ceil(((row._size as number) ?? 0) / 4);\n const tokens = body !== undefined ? Math.max(Math.ceil(estimateTokens(body)), byteTokens) : byteTokens;\n\n return {\n path,\n tokens,\n frontmatter,\n parseError,\n sections,\n outbound: outbound.slice(0, PEEK_LIST_LIMIT),\n backlinks,\n unresolved: unresolved.slice(0, PEEK_LIST_LIMIT),\n sectionsTotal,\n outboundTotal: outbound.length,\n backlinksTotal,\n unresolvedTotal: unresolved.length,\n off: (['sections', 'links'] as FeatureName[]).filter((name) => !featureEnabled(cfg, name)),\n };\n });\n}\n"],"names":["posix","estimateTokens","featureEnabled","SenseError","INTERNAL_COLUMNS","scopedPaths","resolveNote","paths","arg","exact","find","p","base","basename","replace","toLowerCase","matches","filter","length","join","PEEK_LIST_LIMIT","peek","store","cfg","pathArg","overrides","transaction","row","pathsStmt","prepare","all","map","r","path","rowStmt","get","parseError","_parse_error","frontmatter","key","value","Object","entries","has","sectionsTotal","sections","n","outbound","backlinks","unresolved","backlinksTotal","out","Set","l","dst","target","src","body","text","byteTokens","Math","ceil","_size","tokens","undefined","max","slice","outboundTotal","unresolvedTotal","off","name"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { DatabaseSync } from 'node:sqlite';
|
|
2
1
|
import type { ResolvedConfig, SearchOverrides } from '../config/index.js';
|
|
3
|
-
|
|
2
|
+
import type { Store } from '../store/types.js';
|
|
3
|
+
export declare function relatedNotes(store: Store, cfg: ResolvedConfig, pathArg: string, overrides: SearchOverrides, k: number): Promise<Array<{
|
|
4
4
|
path: string;
|
|
5
5
|
similarity: number;
|
|
6
6
|
}>>;
|
|
@@ -6,11 +6,11 @@ import { resolveNote } from './peek.js';
|
|
|
6
6
|
import { scopedPaths, scopeHasEmbeddings } from './scope.js';
|
|
7
7
|
// Notes most similar by cosine, excluding self and everything already linked either way.
|
|
8
8
|
// Its own command, not a peek section: a full embeddings scan is ~480ms at 26k notes.
|
|
9
|
-
export async function relatedNotes(
|
|
10
|
-
const paths =
|
|
9
|
+
export async function relatedNotes(store, cfg, pathArg, overrides, k) {
|
|
10
|
+
const paths = (await (await store.prepare('SELECT "path" FROM frontmatter')).all()).map((r)=>r.path);
|
|
11
11
|
const path = resolveNote(paths, pathArg);
|
|
12
|
-
const outbound =
|
|
13
|
-
const backlinks =
|
|
12
|
+
const outbound = (await (await store.prepare('SELECT DISTINCT dst FROM links WHERE src = ? AND dst IS NOT NULL AND dst != src')).all(path)).map((r)=>r.dst);
|
|
13
|
+
const backlinks = (await (await store.prepare('SELECT DISTINCT src FROM links WHERE dst = ? AND src != dst')).all(path)).map((r)=>r.src);
|
|
14
14
|
const exclude = new Set([
|
|
15
15
|
path,
|
|
16
16
|
...outbound,
|
|
@@ -35,15 +35,15 @@ export async function relatedNotes(db, cfg, pathArg, overrides, k) {
|
|
|
35
35
|
if (localModelMissing(e)) {
|
|
36
36
|
throw new SenseError('EMBED_MODEL_MISSING', `related ranks notes by meaning, so it needs the embedding model, but the local model path "${e.model}" is missing ${MODEL_FILENAMES}; point embed.model at a directory containing them (search still works without it, on words and links)`);
|
|
37
37
|
}
|
|
38
|
-
const allowed = scopedPaths(
|
|
38
|
+
const allowed = await scopedPaths(store, cfg, overrides);
|
|
39
39
|
// Top up pending rows before the seed check, or a fresh index reports every note as
|
|
40
40
|
// having no indexed text until some search has run.
|
|
41
|
-
await embedPending(
|
|
42
|
-
if (!hasEmbedding(
|
|
41
|
+
await embedPending(store, cfg, cfg.baseDir);
|
|
42
|
+
if (!await hasEmbedding(store, path)) {
|
|
43
43
|
throw new SenseError('NOTE_NOT_EMBEDDED', `${path} has no indexed text to compare -- a note that is frontmatter only, or empty, has nothing to rank by meaning`);
|
|
44
44
|
}
|
|
45
|
-
if (!scopeHasEmbeddings(
|
|
46
|
-
return similarNotes(
|
|
45
|
+
if (!await scopeHasEmbeddings(store, cfg, allowed)) return [];
|
|
46
|
+
return similarNotes(store, cfg, path, {
|
|
47
47
|
exclude,
|
|
48
48
|
allowed,
|
|
49
49
|
k
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/related.ts"],"sourcesContent":["import type {
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/related.ts"],"sourcesContent":["import type { ResolvedConfig, SearchOverrides } from '../config/index.ts';\nimport { embedConfig, embedEnabled, resolveSearch } from '../config/index.ts';\nimport { embedPending, hasEmbedding, similarNotes } from '../embed/query.ts';\nimport { localModelMissing, MODEL_FILENAMES } from '../embed/store.ts';\nimport { SenseError } from '../errors.ts';\nimport type { Store } from '../store/types.ts';\nimport { resolveNote } from './peek.ts';\nimport { scopedPaths, scopeHasEmbeddings } from './scope.ts';\n\n// Notes most similar by cosine, excluding self and everything already linked either way.\n// Its own command, not a peek section: a full embeddings scan is ~480ms at 26k notes.\nexport async function relatedNotes(store: Store, cfg: ResolvedConfig, pathArg: string, overrides: SearchOverrides, k: number): Promise<Array<{ path: string; similarity: number }>> {\n const paths = ((await (await store.prepare('SELECT \"path\" FROM frontmatter')).all()) as Array<{ path: string }>).map((r) => r.path);\n const path = resolveNote(paths, pathArg);\n\n const outbound = ((await (await store.prepare('SELECT DISTINCT dst FROM links WHERE src = ? AND dst IS NOT NULL AND dst != src')).all(path)) as Array<{ dst: string }>).map((r) => r.dst);\n const backlinks = ((await (await store.prepare('SELECT DISTINCT src FROM links WHERE dst = ? AND src != dst')).all(path)) as Array<{ src: string }>).map((r) => r.src);\n const exclude = new Set([path, ...outbound, ...backlinks]);\n\n // Vectors are the only signal `related` has, so every way of not having them is an error\n // naming the cause. An empty table then means one thing: nothing near in meaning that this\n // note does not already link to, which is a real answer.\n const effective = resolveSearch(cfg, overrides);\n if (!embedEnabled(cfg)) {\n throw new SenseError('EMBED_DISABLED', 'related ranks notes by meaning, and this tree has no embedding model; add an \"embed\" block naming one to sense.config.json (a Hugging Face id fetches automatically at first use; `sense download` prefetches it) -- search works without it, on words and links');\n }\n // search gates on the same signal (see wantsVectors above); reading it here too keeps a\n // preset's declared signals meaning one thing. Without this, an overlapping vectors-on\n // preset's vectors would answer for a scope that declined them.\n if (effective.signals.vectors === undefined) {\n throw new SenseError('PRESET_NOT_SEMANTIC', `preset \"${effective.presetName}\" has no \"vectors\" signal, so this scope has no vectors and related has no other signal; search it instead (words and links), or add \"vectors\" to that preset's signals`);\n }\n // A downloadable HF id proceeds -- embedPending's getProvider call fetches it lazily on\n // consent. Only a local path with missing files can never fetch itself.\n const e = embedConfig(cfg); // embedEnabled(cfg) above guarantees this is set\n if (localModelMissing(e)) {\n throw new SenseError('EMBED_MODEL_MISSING', `related ranks notes by meaning, so it needs the embedding model, but the local model path \"${e.model}\" is missing ${MODEL_FILENAMES}; point embed.model at a directory containing them (search still works without it, on words and links)`);\n }\n const allowed = await scopedPaths(store, cfg, overrides);\n // Top up pending rows before the seed check, or a fresh index reports every note as\n // having no indexed text until some search has run.\n await embedPending(store, cfg, cfg.baseDir);\n if (!(await hasEmbedding(store, path))) {\n throw new SenseError('NOTE_NOT_EMBEDDED', `${path} has no indexed text to compare -- a note that is frontmatter only, or empty, has nothing to rank by meaning`);\n }\n if (!(await scopeHasEmbeddings(store, cfg, allowed))) return [];\n return similarNotes(store, cfg, path, { exclude, allowed, k });\n}\n"],"names":["embedConfig","embedEnabled","resolveSearch","embedPending","hasEmbedding","similarNotes","localModelMissing","MODEL_FILENAMES","SenseError","resolveNote","scopedPaths","scopeHasEmbeddings","relatedNotes","store","cfg","pathArg","overrides","k","paths","prepare","all","map","r","path","outbound","dst","backlinks","src","exclude","Set","effective","signals","vectors","undefined","presetName","e","model","allowed","baseDir"],"mappings":"AACA,SAASA,WAAW,EAAEC,YAAY,EAAEC,aAAa,QAAQ,qBAAqB;AAC9E,SAASC,YAAY,EAAEC,YAAY,EAAEC,YAAY,QAAQ,oBAAoB;AAC7E,SAASC,iBAAiB,EAAEC,eAAe,QAAQ,oBAAoB;AACvE,SAASC,UAAU,QAAQ,eAAe;AAE1C,SAASC,WAAW,QAAQ,YAAY;AACxC,SAASC,WAAW,EAAEC,kBAAkB,QAAQ,aAAa;AAE7D,yFAAyF;AACzF,sFAAsF;AACtF,OAAO,eAAeC,aAAaC,KAAY,EAAEC,GAAmB,EAAEC,OAAe,EAAEC,SAA0B,EAAEC,CAAS;IAC1H,MAAMC,QAAQ,AAAE,CAAA,MAAM,AAAC,CAAA,MAAML,MAAMM,OAAO,CAAC,iCAAgC,EAAGC,GAAG,EAAC,EAA+BC,GAAG,CAAC,CAACC,IAAMA,EAAEC,IAAI;IAClI,MAAMA,OAAOd,YAAYS,OAAOH;IAEhC,MAAMS,WAAW,AAAE,CAAA,MAAM,AAAC,CAAA,MAAMX,MAAMM,OAAO,CAAC,kFAAiF,EAAGC,GAAG,CAACG,KAAI,EAA8BF,GAAG,CAAC,CAACC,IAAMA,EAAEG,GAAG;IACxL,MAAMC,YAAY,AAAE,CAAA,MAAM,AAAC,CAAA,MAAMb,MAAMM,OAAO,CAAC,8DAA6D,EAAGC,GAAG,CAACG,KAAI,EAA8BF,GAAG,CAAC,CAACC,IAAMA,EAAEK,GAAG;IACrK,MAAMC,UAAU,IAAIC,IAAI;QAACN;WAASC;WAAaE;KAAU;IAEzD,yFAAyF;IACzF,2FAA2F;IAC3F,yDAAyD;IACzD,MAAMI,YAAY5B,cAAcY,KAAKE;IACrC,IAAI,CAACf,aAAaa,MAAM;QACtB,MAAM,IAAIN,WAAW,kBAAkB;IACzC;IACA,wFAAwF;IACxF,uFAAuF;IACvF,gEAAgE;IAChE,IAAIsB,UAAUC,OAAO,CAACC,OAAO,KAAKC,WAAW;QAC3C,MAAM,IAAIzB,WAAW,uBAAuB,CAAC,QAAQ,EAAEsB,UAAUI,UAAU,CAAC,uKAAuK,CAAC;IACtP;IACA,wFAAwF;IACxF,wEAAwE;IACxE,MAAMC,IAAInC,YAAYc,MAAM,iDAAiD;IAC7E,IAAIR,kBAAkB6B,IAAI;QACxB,MAAM,IAAI3B,WAAW,uBAAuB,CAAC,2FAA2F,EAAE2B,EAAEC,KAAK,CAAC,aAAa,EAAE7B,gBAAgB,sGAAsG,CAAC;IAC1R;IACA,MAAM8B,UAAU,MAAM3B,YAAYG,OAAOC,KAAKE;IAC9C,oFAAoF;IACpF,oDAAoD;IACpD,MAAMb,aAAaU,OAAOC,KAAKA,IAAIwB,OAAO;IAC1C,IAAI,CAAE,MAAMlC,aAAaS,OAAOU,OAAQ;QACtC,MAAM,IAAIf,WAAW,qBAAqB,GAAGe,KAAK,4GAA4G,CAAC;IACjK;IACA,IAAI,CAAE,MAAMZ,mBAAmBE,OAAOC,KAAKuB,UAAW,OAAO,EAAE;IAC/D,OAAOhC,aAAaQ,OAAOC,KAAKS,MAAM;QAAEK;QAASS;QAASpB;IAAE;AAC9D"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type { DatabaseSync } from 'node:sqlite';
|
|
2
1
|
import type { ResolvedConfig, SearchOverrides } from '../config/index.js';
|
|
2
|
+
import type { Store } from '../store/types.js';
|
|
3
3
|
export declare const INTERNAL_COLUMNS: Set<string>;
|
|
4
4
|
export declare function inScope(path: string, include: string[], exclude?: string[]): boolean;
|
|
5
|
-
export declare function scopeHasEmbeddings(
|
|
6
|
-
export declare function rawScope(
|
|
7
|
-
export declare function narrowByWhere(
|
|
8
|
-
export declare function scopedPaths(
|
|
9
|
-
export declare function materializeScope(
|
|
10
|
-
export declare function setupMapScope(
|
|
5
|
+
export declare function scopeHasEmbeddings(store: Store, cfg: ResolvedConfig, scopedPaths: Set<string>): Promise<boolean>;
|
|
6
|
+
export declare function rawScope(store: Store, cfg: ResolvedConfig, overrides: SearchOverrides, allPaths?: string[]): Promise<Set<string>>;
|
|
7
|
+
export declare function narrowByWhere(store: Store, paths: Set<string>, where: string | undefined): Promise<Set<string>>;
|
|
8
|
+
export declare function scopedPaths(store: Store, cfg: ResolvedConfig, overrides: SearchOverrides): Promise<Set<string>>;
|
|
9
|
+
export declare function materializeScope(store: Store, table: string, paths: Set<string>): Promise<void>;
|
|
10
|
+
export declare function setupMapScope(store: Store, paths: Set<string>): Promise<void>;
|
|
@@ -17,29 +17,38 @@ export function inScope(path, include, exclude) {
|
|
|
17
17
|
if (exclude === null || exclude === void 0 ? void 0 : exclude.some((g)=>matchesGlob(path, g))) return false;
|
|
18
18
|
return true;
|
|
19
19
|
}
|
|
20
|
-
export function scopeHasEmbeddings(
|
|
20
|
+
export async function scopeHasEmbeddings(store, cfg, scopedPaths) {
|
|
21
21
|
if (!anyPresetEmbeds(cfg)) return false; // the embeddings table doesn't exist at all in this case
|
|
22
|
-
const
|
|
22
|
+
const stmt = await store.prepare('SELECT DISTINCT "path" FROM embeddings');
|
|
23
|
+
const rows = await stmt.all();
|
|
23
24
|
return rows.some((r)=>scopedPaths.has(r.path));
|
|
24
25
|
}
|
|
25
26
|
// Scope only (no --where): preset_files for a named preset, JS glob matching for an ad hoc
|
|
26
27
|
// include/exclude override. Shared by scopedPaths() and search(), which also needs the
|
|
27
28
|
// pre-where set to size the candidate-pool filter.
|
|
28
|
-
export function rawScope(
|
|
29
|
+
export async function rawScope(store, cfg, overrides, allPaths) {
|
|
29
30
|
const effective = resolveSearch(cfg, overrides);
|
|
30
31
|
const { include, exclude } = effective;
|
|
31
32
|
const adHocScope = overrides.include !== undefined || overrides.exclude !== undefined || overrides.noExclude === true;
|
|
32
|
-
if (!adHocScope)
|
|
33
|
-
|
|
33
|
+
if (!adHocScope) {
|
|
34
|
+
const stmt = await store.prepare('SELECT "path" FROM preset_files WHERE preset = ?');
|
|
35
|
+
return new Set((await stmt.all(effective.presetName)).map((r)=>r.path));
|
|
36
|
+
}
|
|
37
|
+
const paths = allPaths !== null && allPaths !== void 0 ? allPaths : await allFrontmatterPaths(store);
|
|
34
38
|
return new Set(paths.filter((p)=>inScope(p, include, exclude)));
|
|
35
39
|
}
|
|
36
|
-
|
|
40
|
+
async function allFrontmatterPaths(store) {
|
|
41
|
+
const stmt = await store.prepare('SELECT "path" FROM frontmatter');
|
|
42
|
+
return (await stmt.all()).map((r)=>r.path);
|
|
43
|
+
}
|
|
44
|
+
export async function narrowByWhere(store, paths, where) {
|
|
37
45
|
if (!where) return paths;
|
|
38
46
|
let whereRows;
|
|
39
47
|
try {
|
|
40
|
-
|
|
48
|
+
const stmt = await store.prepare(`SELECT "path" FROM frontmatter f WHERE (${where})`);
|
|
49
|
+
whereRows = await stmt.all();
|
|
41
50
|
} catch (err) {
|
|
42
|
-
throw columnHint(
|
|
51
|
+
throw columnHint(await store.docs.columns(), err);
|
|
43
52
|
}
|
|
44
53
|
const wherePaths = new Set(whereRows.map((r)=>r.path));
|
|
45
54
|
return new Set([
|
|
@@ -48,19 +57,21 @@ export function narrowByWhere(db, paths, where) {
|
|
|
48
57
|
}
|
|
49
58
|
// The scope resolver for non-search commands (path, peek, map): same coverage rule search()
|
|
50
59
|
// applies, then narrowed by the resolved `where`.
|
|
51
|
-
export function scopedPaths(
|
|
60
|
+
export async function scopedPaths(store, cfg, overrides) {
|
|
52
61
|
const effective = resolveSearch(cfg, overrides);
|
|
53
|
-
return narrowByWhere(
|
|
62
|
+
return narrowByWhere(store, await rawScope(store, cfg, overrides), effective.where);
|
|
54
63
|
}
|
|
55
64
|
// Materializes a path set into a named temp table (same shape as traverse.ts's allowed_nodes)
|
|
56
65
|
// so a query can join/filter against it cheaply instead of binding a parameter per path.
|
|
57
|
-
export function materializeScope(
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
66
|
+
export async function materializeScope(store, table, paths) {
|
|
67
|
+
await store.exec(`DROP TABLE IF EXISTS ${table}`);
|
|
68
|
+
await store.exec(`CREATE TEMP TABLE ${table} ("path" TEXT PRIMARY KEY)`);
|
|
69
|
+
await store.runBatch(`INSERT INTO ${table} ("path") VALUES (?)`, [
|
|
61
70
|
...paths
|
|
62
|
-
])
|
|
71
|
+
].map((p)=>[
|
|
72
|
+
p
|
|
73
|
+
]));
|
|
63
74
|
}
|
|
64
|
-
export function setupMapScope(
|
|
65
|
-
materializeScope(
|
|
75
|
+
export async function setupMapScope(store, paths) {
|
|
76
|
+
await materializeScope(store, '_map_scope', paths);
|
|
66
77
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/scope.ts"],"sourcesContent":["import { matchesGlob } from 'node:path';\nimport type {
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/scope.ts"],"sourcesContent":["import { matchesGlob } from 'node:path';\nimport type { ResolvedConfig, SearchOverrides } from '../config/index.ts';\nimport { anyPresetEmbeds, resolveSearch } from '../config/index.ts';\nimport { columnHint } from '../output/column-hint.ts';\nimport type { Store } from '../store/types.ts';\n\nexport const INTERNAL_COLUMNS = new Set(['path', '_mtime', '_ctime', '_size', '_rank', '_parse_error']);\n\n// node:path's matchesGlob is experimental (stable behind an unstable-API flag) as of the\n// engines floor (Node >=22.20); scope filtering only ever needs single-pattern matching, so\n// it's used here in JS rather than running a directory walk in the query path.\nexport function inScope(path: string, include: string[], exclude?: string[]): boolean {\n if (!include.some((g) => matchesGlob(path, g))) return false;\n if (exclude?.some((g) => matchesGlob(path, g))) return false;\n return true;\n}\n\nexport async function scopeHasEmbeddings(store: Store, cfg: ResolvedConfig, scopedPaths: Set<string>): Promise<boolean> {\n if (!anyPresetEmbeds(cfg)) return false; // the embeddings table doesn't exist at all in this case\n const stmt = await store.prepare('SELECT DISTINCT \"path\" FROM embeddings');\n const rows = (await stmt.all()) as Array<{ path: string }>;\n return rows.some((r) => scopedPaths.has(r.path));\n}\n\n// Scope only (no --where): preset_files for a named preset, JS glob matching for an ad hoc\n// include/exclude override. Shared by scopedPaths() and search(), which also needs the\n// pre-where set to size the candidate-pool filter.\nexport async function rawScope(store: Store, cfg: ResolvedConfig, overrides: SearchOverrides, allPaths?: string[]): Promise<Set<string>> {\n const effective = resolveSearch(cfg, overrides);\n const { include, exclude } = effective;\n const adHocScope = overrides.include !== undefined || overrides.exclude !== undefined || overrides.noExclude === true;\n if (!adHocScope) {\n const stmt = await store.prepare('SELECT \"path\" FROM preset_files WHERE preset = ?');\n return new Set(((await stmt.all(effective.presetName)) as Array<{ path: string }>).map((r) => r.path));\n }\n const paths = allPaths ?? (await allFrontmatterPaths(store));\n return new Set(paths.filter((p) => inScope(p, include, exclude)));\n}\n\nasync function allFrontmatterPaths(store: Store): Promise<string[]> {\n const stmt = await store.prepare('SELECT \"path\" FROM frontmatter');\n return ((await stmt.all()) as Array<{ path: string }>).map((r) => r.path);\n}\n\nexport async function narrowByWhere(store: Store, paths: Set<string>, where: string | undefined): Promise<Set<string>> {\n if (!where) return paths;\n let whereRows: Array<{ path: string }>;\n try {\n const stmt = await store.prepare(`SELECT \"path\" FROM frontmatter f WHERE (${where})`);\n whereRows = (await stmt.all()) as Array<{ path: string }>;\n } catch (err) {\n throw columnHint(await store.docs.columns(), err as Error);\n }\n const wherePaths = new Set(whereRows.map((r) => r.path));\n return new Set([...paths].filter((p) => wherePaths.has(p)));\n}\n\n// The scope resolver for non-search commands (path, peek, map): same coverage rule search()\n// applies, then narrowed by the resolved `where`.\nexport async function scopedPaths(store: Store, cfg: ResolvedConfig, overrides: SearchOverrides): Promise<Set<string>> {\n const effective = resolveSearch(cfg, overrides);\n return narrowByWhere(store, await rawScope(store, cfg, overrides), effective.where);\n}\n\n// Materializes a path set into a named temp table (same shape as traverse.ts's allowed_nodes)\n// so a query can join/filter against it cheaply instead of binding a parameter per path.\nexport async function materializeScope(store: Store, table: string, paths: Set<string>): Promise<void> {\n await store.exec(`DROP TABLE IF EXISTS ${table}`);\n await store.exec(`CREATE TEMP TABLE ${table} (\"path\" TEXT PRIMARY KEY)`);\n await store.runBatch(\n `INSERT INTO ${table} (\"path\") VALUES (?)`,\n [...paths].map((p) => [p])\n );\n}\n\nexport async function setupMapScope(store: Store, paths: Set<string>): Promise<void> {\n await materializeScope(store, '_map_scope', paths);\n}\n"],"names":["matchesGlob","anyPresetEmbeds","resolveSearch","columnHint","INTERNAL_COLUMNS","Set","inScope","path","include","exclude","some","g","scopeHasEmbeddings","store","cfg","scopedPaths","stmt","prepare","rows","all","r","has","rawScope","overrides","allPaths","effective","adHocScope","undefined","noExclude","presetName","map","paths","allFrontmatterPaths","filter","p","narrowByWhere","where","whereRows","err","docs","columns","wherePaths","materializeScope","table","exec","runBatch","setupMapScope"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { DatabaseSync } from 'node:sqlite';
|
|
2
1
|
import type { ResolvedConfig } from '../config/index.js';
|
|
3
2
|
import type { Row } from '../output/output.js';
|
|
3
|
+
import type { Store } from '../store/types.js';
|
|
4
4
|
export interface SearchOptions {
|
|
5
5
|
k?: number;
|
|
6
6
|
where?: string;
|
|
@@ -9,4 +9,4 @@ export interface SearchOptions {
|
|
|
9
9
|
exclude?: string[];
|
|
10
10
|
noExclude?: boolean;
|
|
11
11
|
}
|
|
12
|
-
export declare function search(
|
|
12
|
+
export declare function search(store: Store, cfg: ResolvedConfig, terms: string, opts?: SearchOptions): Promise<Row[]>;
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
import {
|
|
3
|
+
import { embedConfig, featureEnabled, resolveSearch } from '../config/index.js';
|
|
4
4
|
import { localModelMissing, MODEL_FILENAMES } from '../embed/store.js';
|
|
5
5
|
import { SenseError } from '../errors.js';
|
|
6
6
|
import { searchError } from '../output/search-error.js';
|
|
7
|
-
import { segmentMatch } from '../text/segment.js';
|
|
8
7
|
import { materializeScope, narrowByWhere, rawScope, scopeHasEmbeddings } from './scope.js';
|
|
9
8
|
import { linksCandidates, vectorsCandidates, wordsCandidates } from './signals.js';
|
|
10
9
|
// snippet() re-tokenizes each candidate doc, superlinearly: ~10s for one 1MB doc
|
|
@@ -97,25 +96,25 @@ function lineNumberAt(text, offset) {
|
|
|
97
96
|
for(let i = 0; i < offset; i++)if (text.charCodeAt(i) === 10) line++;
|
|
98
97
|
return line;
|
|
99
98
|
}
|
|
100
|
-
function lineRangeFor(
|
|
101
|
-
const
|
|
99
|
+
async function lineRangeFor(store, path, line) {
|
|
100
|
+
const stmt = await store.prepare('SELECT start_line, end_line FROM sections WHERE "path" = ? AND start_line <= ? AND end_line >= ? ORDER BY start_line DESC LIMIT 1');
|
|
101
|
+
const row = await stmt.get(path, line, line);
|
|
102
102
|
return row ? `L${row.start_line}-${row.end_line}` : null;
|
|
103
103
|
}
|
|
104
104
|
// The declared or defaulted signals compose via RRF; `via` names which ones produced each row.
|
|
105
105
|
// `opts` arrives already resolved (config.ts:resolveSearch).
|
|
106
|
-
export async function search(
|
|
107
|
-
var _hits_get, _chunkLines_get, _chunkSimilarity_get;
|
|
106
|
+
export async function search(store, cfg, terms, opts = {}) {
|
|
108
107
|
const effective = resolveSearch(cfg, opts);
|
|
109
108
|
const { k, signals } = effective;
|
|
110
|
-
const allPaths =
|
|
111
|
-
const scopePaths = rawScope(
|
|
109
|
+
const allPaths = (await (await store.prepare('SELECT "path" FROM frontmatter')).all()).map((r)=>r.path);
|
|
110
|
+
const scopePaths = await rawScope(store, cfg, opts, allPaths);
|
|
112
111
|
const scopeActive = scopePaths.size < allPaths.length;
|
|
113
112
|
// The set every candidate pool must be filtered to before truncation: scope narrowed by
|
|
114
113
|
// --where, the same composition scopedPaths() gives the other commands. Runs the same where
|
|
115
114
|
// fragment matchSql does, so a bad column gets the same attributed error either way.
|
|
116
115
|
let allowedPaths;
|
|
117
116
|
try {
|
|
118
|
-
allowedPaths = narrowByWhere(
|
|
117
|
+
allowedPaths = await narrowByWhere(store, scopePaths, effective.where);
|
|
119
118
|
} catch (err) {
|
|
120
119
|
throw searchError(err, terms, effective.where);
|
|
121
120
|
}
|
|
@@ -129,17 +128,9 @@ export async function search(db, cfg, terms, opts = {}) {
|
|
|
129
128
|
throw new SenseError('EMBED_MODEL_MISSING', `preset "${effective.presetName}" searches with vectors, but the local model path "${e.model}" is missing ${MODEL_FILENAMES}; point embed.model at a directory containing them, or drop "vectors" from that preset's signals to search without them`);
|
|
130
129
|
}
|
|
131
130
|
}
|
|
132
|
-
const semanticEnabled = wantsVectors && scopeHasEmbeddings(
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
// ahead of it retargets its _seg column), matching how it was indexed. Text that already
|
|
136
|
-
// carries boundaries comes through untouched, so this is a no-op for the languages that
|
|
137
|
-
// never needed it. Gated on the same predicate that decided whether the sidecars were
|
|
138
|
-
// populated (contentTokenize(cfg) === undefined), so index and query can't disagree. Invalid
|
|
139
|
-
// syntax still errors rather than being rewritten.
|
|
140
|
-
// --where
|
|
141
|
-
// applies inside the candidate query (a post-filter would drop matches ranked past the pool)
|
|
142
|
-
// and again on the final select, for link-derived rows.
|
|
131
|
+
const semanticEnabled = wantsVectors && await scopeHasEmbeddings(store, cfg, allowedPaths);
|
|
132
|
+
// --where applies inside the candidate query (a post-filter would drop matches ranked past
|
|
133
|
+
// the pool) and again on the final select, for link-derived rows.
|
|
143
134
|
const scope = effective.where;
|
|
144
135
|
const whereJoin = scope ? `JOIN frontmatter f ON f."path" = content.path` : '';
|
|
145
136
|
const whereCond = scope ? `AND (${scope})` : '';
|
|
@@ -147,16 +138,16 @@ export async function search(db, cfg, terms, opts = {}) {
|
|
|
147
138
|
// after -- otherwise scoped notes ranking below the global top-`fetch` never reach the
|
|
148
139
|
// filter. A join against a temp table, not a bound parameter list: real scopes run to
|
|
149
140
|
// thousands of paths, past SQLITE_MAX_VARIABLE_NUMBER on older builds.
|
|
150
|
-
if (scopeActive) materializeScope(
|
|
141
|
+
if (scopeActive) await materializeScope(store, '_search_scope', scopePaths);
|
|
151
142
|
const scopeCond = scopeActive ? `AND content.path IN (SELECT "path" FROM _search_scope)` : '';
|
|
152
|
-
const query = contentTokenize(cfg) === undefined ? segmentMatch(terms) : terms;
|
|
153
143
|
const candidates = new Map();
|
|
154
144
|
let matchRows = [];
|
|
155
|
-
// A query that
|
|
156
|
-
//
|
|
157
|
-
|
|
145
|
+
// A query that is only whitespace has no searchable words: zero lexical rows, and the other
|
|
146
|
+
// signals still compose. Any store-specific rewrite of `terms` (sqlite's grapheme phrase for
|
|
147
|
+
// unspaced scripts, duckdb's word/substring split) happens inside store.lexical.query itself.
|
|
148
|
+
if (signals.words !== undefined && terms.trim() !== '') {
|
|
158
149
|
try {
|
|
159
|
-
matchRows = wordsCandidates(
|
|
150
|
+
matchRows = await wordsCandidates(store, candidates, terms, whereJoin, whereCond, scopeCond, fetch, signals.words);
|
|
160
151
|
} catch (err) {
|
|
161
152
|
throw searchError(err, terms, scope);
|
|
162
153
|
}
|
|
@@ -168,16 +159,31 @@ export async function search(db, cfg, terms, opts = {}) {
|
|
|
168
159
|
// hit === null here means the bound suppressed snippet(), not "no match" -- distinguish
|
|
169
160
|
// from via='link' rows (never in matchRows, so absent from this set) below.
|
|
170
161
|
const oversized = new Set(matchRows.filter((r)=>r.hit === null).map((r)=>r.path));
|
|
171
|
-
if (signals.links !== undefined) linksCandidates(
|
|
162
|
+
if (signals.links !== undefined) await linksCandidates(store, candidates, matchRows, allPaths, allowedPaths, fetch, signals.links);
|
|
172
163
|
let chunkLines = new Map();
|
|
173
164
|
let chunkSimilarity = new Map();
|
|
174
165
|
if (semanticEnabled) {
|
|
175
|
-
({ chunkLines, chunkSimilarity } = await vectorsCandidates(
|
|
166
|
+
({ chunkLines, chunkSimilarity } = await vectorsCandidates(store, cfg, candidates, terms, fetch, allowedPaths, signals.vectors));
|
|
167
|
+
}
|
|
168
|
+
await store.exec('DROP TABLE IF EXISTS _search');
|
|
169
|
+
// DOUBLE, not REAL: sqlite's REAL is an 8-byte double but duckdb's is a 4-byte float, and the
|
|
170
|
+
// rounded score/similarity are printed at full precision in rows.
|
|
171
|
+
await store.exec('CREATE TEMP TABLE _search ("path" TEXT PRIMARY KEY, score DOUBLE, via TEXT, hit TEXT, lines TEXT, similarity DOUBLE)');
|
|
172
|
+
if (candidates.size > 0) {
|
|
173
|
+
await store.runBatch('INSERT INTO _search ("path", score, via, hit, lines, similarity) VALUES (?, ?, ?, ?, ?, ?)', [
|
|
174
|
+
...candidates
|
|
175
|
+
].map(([path, c])=>{
|
|
176
|
+
var _hits_get, _chunkLines_get, _chunkSimilarity_get;
|
|
177
|
+
return [
|
|
178
|
+
path,
|
|
179
|
+
c.score,
|
|
180
|
+
c.via,
|
|
181
|
+
(_hits_get = hits.get(path)) !== null && _hits_get !== void 0 ? _hits_get : null,
|
|
182
|
+
(_chunkLines_get = chunkLines.get(path)) !== null && _chunkLines_get !== void 0 ? _chunkLines_get : null,
|
|
183
|
+
(_chunkSimilarity_get = chunkSimilarity.get(path)) !== null && _chunkSimilarity_get !== void 0 ? _chunkSimilarity_get : null
|
|
184
|
+
];
|
|
185
|
+
}));
|
|
176
186
|
}
|
|
177
|
-
db.exec('DROP TABLE IF EXISTS _search');
|
|
178
|
-
db.exec('CREATE TEMP TABLE _search ("path" TEXT PRIMARY KEY, score REAL, via TEXT, hit TEXT, lines TEXT, similarity REAL)');
|
|
179
|
-
const insert = db.prepare('INSERT INTO _search ("path", score, via, hit, lines, similarity) VALUES (?, ?, ?, ?, ?, ?)');
|
|
180
|
-
for (const [path, c] of candidates)insert.run(path, c.score, c.via, (_hits_get = hits.get(path)) !== null && _hits_get !== void 0 ? _hits_get : null, (_chunkLines_get = chunkLines.get(path)) !== null && _chunkLines_get !== void 0 ? _chunkLines_get : null, (_chunkSimilarity_get = chunkSimilarity.get(path)) !== null && _chunkSimilarity_get !== void 0 ? _chunkSimilarity_get : null);
|
|
181
187
|
// All three candidate paths (match, link, vector) already filtered to scope+where before
|
|
182
188
|
// reaching _search; this reapplies --where anyway since the join to frontmatter is already
|
|
183
189
|
// needed for the path column.
|
|
@@ -185,9 +191,10 @@ export async function search(db, cfg, terms, opts = {}) {
|
|
|
185
191
|
// lines is always present now: semantic rows carry their chunk's range, oversized-doc
|
|
186
192
|
// lexical rows gain one below, everything else stays null. similarity stays semantic-only.
|
|
187
193
|
const similarityCol = semanticEnabled ? ', _search.similarity' : '';
|
|
188
|
-
const
|
|
194
|
+
const selectStmt = await store.prepare(`SELECT f."path" AS path, content.title, content.summary, _search.hit, _search.via, round(_search.score, 4) AS score, _search.lines${similarityCol}
|
|
189
195
|
FROM _search JOIN frontmatter f ON f."path" = _search."path" JOIN content ON content.path = _search."path"
|
|
190
|
-
${where} ORDER BY _search.score DESC LIMIT ?`)
|
|
196
|
+
${where} ORDER BY _search.score DESC LIMIT ?`);
|
|
197
|
+
const rows = await selectStmt.all(k);
|
|
191
198
|
if (oversized.size > 0) {
|
|
192
199
|
const bareTerms = extractBareTerms(terms);
|
|
193
200
|
for (const row of rows){
|
|
@@ -200,7 +207,7 @@ export async function search(db, cfg, terms, opts = {}) {
|
|
|
200
207
|
}
|
|
201
208
|
const { excerpt, offset } = computeExcerpt(text, bareTerms);
|
|
202
209
|
row.hit = excerpt;
|
|
203
|
-
if (row.lines == null) row.lines = featureEnabled(cfg, 'sections') ? lineRangeFor(
|
|
210
|
+
if (row.lines == null) row.lines = featureEnabled(cfg, 'sections') ? await lineRangeFor(store, row.path, lineNumberAt(text, offset)) : null;
|
|
204
211
|
}
|
|
205
212
|
}
|
|
206
213
|
return rows;
|