sensemaking 0.27.0 → 0.27.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/dist/cjs/chunk/extract.js.map +1 -1
- package/dist/cjs/chunk/group.js.map +1 -1
- package/dist/cjs/chunk/index.js.map +1 -1
- package/dist/cjs/chunk/parse.js.map +1 -1
- package/dist/cjs/chunk/parser.js.map +1 -1
- package/dist/cjs/chunk/tokens.js.map +1 -1
- package/dist/cjs/chunk/version.js.map +1 -1
- package/dist/cjs/cli/build.js.map +1 -1
- package/dist/cjs/cli/download.js.map +1 -1
- package/dist/cjs/cli/index.js.map +1 -1
- package/dist/cjs/cli/init.js.map +1 -1
- package/dist/cjs/cli/map.js.map +1 -1
- package/dist/cjs/cli/named.js.map +1 -1
- package/dist/cjs/cli/path.js.map +1 -1
- package/dist/cjs/cli/peek.js.map +1 -1
- package/dist/cjs/cli/related.js.map +1 -1
- package/dist/cjs/cli/search.js.map +1 -1
- package/dist/cjs/cli/shared.js.map +1 -1
- package/dist/cjs/cli/sql.js.map +1 -1
- package/dist/cjs/cli/status.js.map +1 -1
- package/dist/cjs/cli/watch.js.map +1 -1
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/commands/index.js.map +1 -1
- package/dist/cjs/commands/map.js.map +1 -1
- package/dist/cjs/commands/path.js.map +1 -1
- package/dist/cjs/commands/peek.js.map +1 -1
- package/dist/cjs/commands/related.js.map +1 -1
- package/dist/cjs/commands/scope.js.map +1 -1
- package/dist/cjs/commands/search.js.map +1 -1
- package/dist/cjs/commands/signals.js.map +1 -1
- package/dist/cjs/commands/snapshot.js.map +1 -1
- package/dist/cjs/commands/status.js.map +1 -1
- package/dist/cjs/config/access.js.map +1 -1
- package/dist/cjs/config/index.js.map +1 -1
- package/dist/cjs/config/load.js.map +1 -1
- package/dist/cjs/config/resolve.js.map +1 -1
- package/dist/cjs/config/signals.js.map +1 -1
- package/dist/cjs/config/types.js.map +1 -1
- package/dist/cjs/config/validate.js.map +1 -1
- package/dist/cjs/embed/cohere.js.map +1 -1
- package/dist/cjs/embed/distribution.js.map +1 -1
- package/dist/cjs/embed/endpoint.js.map +1 -1
- package/dist/cjs/embed/handoff.js.map +1 -1
- package/dist/cjs/embed/http.js.map +1 -1
- package/dist/cjs/embed/identity.js.map +1 -1
- package/dist/cjs/embed/langfit.js.map +1 -1
- package/dist/cjs/embed/languages.js.map +1 -1
- package/dist/cjs/embed/openai.js.map +1 -1
- package/dist/cjs/embed/query.js.map +1 -1
- package/dist/cjs/embed/registry.js.map +1 -1
- package/dist/cjs/embed/static.js.map +1 -1
- package/dist/cjs/embed/store.js.map +1 -1
- package/dist/cjs/embed/types.js.map +1 -1
- package/dist/cjs/errors.js.map +1 -1
- package/dist/cjs/features/embed.js.map +1 -1
- package/dist/cjs/features/fences.js.map +1 -1
- package/dist/cjs/features/index.js.map +1 -1
- package/dist/cjs/features/links.js.map +1 -1
- package/dist/cjs/features/rank.js.map +1 -1
- package/dist/cjs/features/sections.js.map +1 -1
- package/dist/cjs/features/tags.js.map +1 -1
- package/dist/cjs/graph/graph.js.map +1 -1
- package/dist/cjs/graph/traverse.js.map +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/lib/atomic-write.js.map +1 -1
- package/dist/cjs/lib/guarded-tick.js.map +1 -1
- package/dist/cjs/lib/serial-query.js.map +1 -1
- package/dist/cjs/lib/watch-notification.js.map +1 -1
- package/dist/cjs/lib/worker-file.js.map +1 -1
- package/dist/cjs/output/column-hint.js.map +1 -1
- package/dist/cjs/output/output.js.map +1 -1
- package/dist/cjs/output/progress.js.map +1 -1
- package/dist/cjs/output/search-error.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/list.js.map +1 -1
- package/dist/cjs/scan/pool.js.map +1 -1
- package/dist/cjs/scan/reparse.js.map +1 -1
- package/dist/cjs/scan/worker-error.js.map +1 -1
- package/dist/cjs/store/batch.js.map +1 -1
- package/dist/cjs/store/builder.js.map +1 -1
- package/dist/cjs/store/cache.js.map +1 -1
- package/dist/cjs/store/duckdb/batch.js.map +1 -1
- package/dist/cjs/store/duckdb/connection.js.map +1 -1
- package/dist/cjs/store/duckdb/fieldStats.js.map +1 -1
- package/dist/cjs/store/duckdb/lexical.d.cts +1 -6
- package/dist/cjs/store/duckdb/lexical.d.ts +1 -6
- package/dist/cjs/store/duckdb/lexical.js +28 -39
- package/dist/cjs/store/duckdb/lexical.js.map +1 -1
- package/dist/cjs/store/duckdb/native.d.cts +1 -2
- package/dist/cjs/store/duckdb/native.d.ts +1 -2
- package/dist/cjs/store/duckdb/native.js +1 -6
- package/dist/cjs/store/duckdb/native.js.map +1 -1
- package/dist/cjs/store/duckdb/open.js.map +1 -1
- package/dist/cjs/store/duckdb/ordered-bm25.d.cts +3 -16
- package/dist/cjs/store/duckdb/ordered-bm25.d.ts +3 -16
- package/dist/cjs/store/duckdb/ordered-bm25.js +27 -79
- package/dist/cjs/store/duckdb/ordered-bm25.js.map +1 -1
- package/dist/cjs/store/duckdb/reconcile.js.map +1 -1
- package/dist/cjs/store/duckdb/sql-functions.js.map +1 -1
- package/dist/cjs/store/duckdb/store.js.map +1 -1
- package/dist/cjs/store/duckdb/vectors.js.map +1 -1
- package/dist/cjs/store/embed-scope.js.map +1 -1
- package/dist/cjs/store/feature-scope.js.map +1 -1
- package/dist/cjs/store/index.js.map +1 -1
- package/dist/cjs/store/lock-wait.js.map +1 -1
- package/dist/cjs/store/native.js.map +1 -1
- package/dist/cjs/store/open.js.map +1 -1
- package/dist/cjs/store/preset-scope.js.map +1 -1
- package/dist/cjs/store/readiness.js.map +1 -1
- package/dist/cjs/store/reconcile.js.map +1 -1
- package/dist/cjs/store/shared.js.map +1 -1
- package/dist/cjs/store/signature.js.map +1 -1
- package/dist/cjs/store/sql-functions.js.map +1 -1
- package/dist/cjs/store/sqlite/connection.js.map +1 -1
- package/dist/cjs/store/sqlite/fieldStats.js.map +1 -1
- package/dist/cjs/store/sqlite/lexical.js.map +1 -1
- package/dist/cjs/store/sqlite/open.js.map +1 -1
- package/dist/cjs/store/sqlite/reconcile.js.map +1 -1
- package/dist/cjs/store/sqlite/sql-functions.js.map +1 -1
- package/dist/cjs/store/sqlite/store.js.map +1 -1
- package/dist/cjs/store/sqlite/vectors.js.map +1 -1
- package/dist/cjs/store/stages.js.map +1 -1
- package/dist/cjs/store/transaction.js.map +1 -1
- package/dist/cjs/store/turso/connection.js.map +1 -1
- package/dist/cjs/store/turso/fieldStats.js.map +1 -1
- package/dist/cjs/store/turso/lexical-text.js.map +1 -1
- package/dist/cjs/store/turso/lexical.js.map +1 -1
- package/dist/cjs/store/turso/native.js.map +1 -1
- package/dist/cjs/store/turso/open.js.map +1 -1
- package/dist/cjs/store/turso/reconcile.js.map +1 -1
- package/dist/cjs/store/turso/sql-functions.js.map +1 -1
- package/dist/cjs/store/turso/store.js.map +1 -1
- package/dist/cjs/store/turso/vectors.js.map +1 -1
- package/dist/cjs/store/types.js.map +1 -1
- package/dist/cjs/store/vectors.js.map +1 -1
- package/dist/cjs/text/segment.js.map +1 -1
- package/dist/cjs/text/strip.js.map +1 -1
- package/dist/cjs/watch-claim.js.map +1 -1
- package/dist/cjs/watch.js.map +1 -1
- package/dist/cjs/workers/parse.js.map +1 -1
- package/dist/cjs/workers/watch-heartbeat.js.map +1 -1
- package/dist/esm/chunk/extract.js.map +1 -1
- package/dist/esm/chunk/group.js.map +1 -1
- package/dist/esm/chunk/index.js.map +1 -1
- package/dist/esm/chunk/parse.js.map +1 -1
- package/dist/esm/chunk/parser.js.map +1 -1
- package/dist/esm/chunk/tokens.js.map +1 -1
- package/dist/esm/chunk/types.js.map +1 -1
- package/dist/esm/chunk/version.js.map +1 -1
- package/dist/esm/cli/build.js.map +1 -1
- package/dist/esm/cli/download.js.map +1 -1
- package/dist/esm/cli/index.js.map +1 -1
- package/dist/esm/cli/init.js.map +1 -1
- package/dist/esm/cli/map.js.map +1 -1
- package/dist/esm/cli/named.js.map +1 -1
- package/dist/esm/cli/path.js.map +1 -1
- package/dist/esm/cli/peek.js.map +1 -1
- package/dist/esm/cli/related.js.map +1 -1
- package/dist/esm/cli/search.js.map +1 -1
- package/dist/esm/cli/shared.js.map +1 -1
- package/dist/esm/cli/sql.js.map +1 -1
- package/dist/esm/cli/status.js.map +1 -1
- package/dist/esm/cli/types.js.map +1 -1
- package/dist/esm/cli/watch.js.map +1 -1
- package/dist/esm/cli.js.map +1 -1
- package/dist/esm/commands/index.js.map +1 -1
- package/dist/esm/commands/map.js.map +1 -1
- package/dist/esm/commands/path.js.map +1 -1
- package/dist/esm/commands/peek.js.map +1 -1
- package/dist/esm/commands/related.js.map +1 -1
- package/dist/esm/commands/scope.js.map +1 -1
- package/dist/esm/commands/search.js.map +1 -1
- package/dist/esm/commands/signals.js.map +1 -1
- package/dist/esm/commands/snapshot.js.map +1 -1
- package/dist/esm/commands/status.js.map +1 -1
- package/dist/esm/config/access.js.map +1 -1
- package/dist/esm/config/index.js.map +1 -1
- package/dist/esm/config/load.js.map +1 -1
- package/dist/esm/config/resolve.js.map +1 -1
- package/dist/esm/config/signals.js.map +1 -1
- package/dist/esm/config/types.js.map +1 -1
- package/dist/esm/config/validate.js.map +1 -1
- package/dist/esm/embed/cohere.js.map +1 -1
- package/dist/esm/embed/distribution.js.map +1 -1
- package/dist/esm/embed/endpoint.js.map +1 -1
- package/dist/esm/embed/handoff.js.map +1 -1
- package/dist/esm/embed/http.js.map +1 -1
- package/dist/esm/embed/identity.js.map +1 -1
- package/dist/esm/embed/langfit.js.map +1 -1
- package/dist/esm/embed/languages.js.map +1 -1
- package/dist/esm/embed/openai.js.map +1 -1
- package/dist/esm/embed/query.js.map +1 -1
- package/dist/esm/embed/registry.js.map +1 -1
- package/dist/esm/embed/static.js.map +1 -1
- package/dist/esm/embed/store.js.map +1 -1
- package/dist/esm/embed/types.js.map +1 -1
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/features/embed.js.map +1 -1
- package/dist/esm/features/fences.js.map +1 -1
- package/dist/esm/features/index.js.map +1 -1
- package/dist/esm/features/links.js.map +1 -1
- package/dist/esm/features/rank.js.map +1 -1
- package/dist/esm/features/sections.js.map +1 -1
- package/dist/esm/features/tags.js.map +1 -1
- package/dist/esm/features/types.js.map +1 -1
- package/dist/esm/graph/graph.js.map +1 -1
- package/dist/esm/graph/traverse.js.map +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lib/atomic-write.js.map +1 -1
- package/dist/esm/lib/guarded-tick.js.map +1 -1
- package/dist/esm/lib/serial-query.js.map +1 -1
- package/dist/esm/lib/watch-notification.js.map +1 -1
- package/dist/esm/lib/worker-file.js.map +1 -1
- package/dist/esm/output/column-hint.js.map +1 -1
- package/dist/esm/output/output.js.map +1 -1
- package/dist/esm/output/progress.js.map +1 -1
- package/dist/esm/output/search-error.js.map +1 -1
- package/dist/esm/scan/frontmatter.js.map +1 -1
- package/dist/esm/scan/index.js.map +1 -1
- package/dist/esm/scan/list.js.map +1 -1
- package/dist/esm/scan/pool.js.map +1 -1
- package/dist/esm/scan/reparse.js.map +1 -1
- package/dist/esm/scan/worker-error.js.map +1 -1
- package/dist/esm/store/batch.js.map +1 -1
- package/dist/esm/store/builder.js.map +1 -1
- package/dist/esm/store/cache.js.map +1 -1
- package/dist/esm/store/duckdb/batch.js.map +1 -1
- package/dist/esm/store/duckdb/connection.js.map +1 -1
- package/dist/esm/store/duckdb/fieldStats.js.map +1 -1
- package/dist/esm/store/duckdb/lexical.d.ts +1 -6
- package/dist/esm/store/duckdb/lexical.js +13 -17
- package/dist/esm/store/duckdb/lexical.js.map +1 -1
- package/dist/esm/store/duckdb/native.d.ts +1 -2
- package/dist/esm/store/duckdb/native.js +1 -3
- package/dist/esm/store/duckdb/native.js.map +1 -1
- package/dist/esm/store/duckdb/open.js.map +1 -1
- package/dist/esm/store/duckdb/ordered-bm25.d.ts +3 -16
- package/dist/esm/store/duckdb/ordered-bm25.js +18 -54
- package/dist/esm/store/duckdb/ordered-bm25.js.map +1 -1
- package/dist/esm/store/duckdb/reconcile.js.map +1 -1
- package/dist/esm/store/duckdb/sql-functions.js.map +1 -1
- package/dist/esm/store/duckdb/store.js.map +1 -1
- package/dist/esm/store/duckdb/vectors.js.map +1 -1
- package/dist/esm/store/embed-scope.js.map +1 -1
- package/dist/esm/store/feature-scope.js.map +1 -1
- package/dist/esm/store/index.js.map +1 -1
- package/dist/esm/store/lock-wait.js.map +1 -1
- package/dist/esm/store/native.js.map +1 -1
- package/dist/esm/store/open.js.map +1 -1
- package/dist/esm/store/preset-scope.js.map +1 -1
- package/dist/esm/store/readiness.js.map +1 -1
- package/dist/esm/store/reconcile.js.map +1 -1
- package/dist/esm/store/shared.js.map +1 -1
- package/dist/esm/store/signature.js.map +1 -1
- package/dist/esm/store/sql-functions.js.map +1 -1
- package/dist/esm/store/sqlite/connection.js.map +1 -1
- package/dist/esm/store/sqlite/fieldStats.js.map +1 -1
- package/dist/esm/store/sqlite/lexical.js.map +1 -1
- package/dist/esm/store/sqlite/open.js.map +1 -1
- package/dist/esm/store/sqlite/reconcile.js.map +1 -1
- package/dist/esm/store/sqlite/sql-functions.js.map +1 -1
- package/dist/esm/store/sqlite/store.js.map +1 -1
- package/dist/esm/store/sqlite/vectors.js.map +1 -1
- package/dist/esm/store/stages.js.map +1 -1
- package/dist/esm/store/transaction.js.map +1 -1
- package/dist/esm/store/turso/connection.js.map +1 -1
- package/dist/esm/store/turso/fieldStats.js.map +1 -1
- package/dist/esm/store/turso/lexical-text.js.map +1 -1
- package/dist/esm/store/turso/lexical.js.map +1 -1
- package/dist/esm/store/turso/native.js.map +1 -1
- package/dist/esm/store/turso/open.js.map +1 -1
- package/dist/esm/store/turso/reconcile.js.map +1 -1
- package/dist/esm/store/turso/sql-functions.js.map +1 -1
- package/dist/esm/store/turso/store.js.map +1 -1
- package/dist/esm/store/turso/vectors.js.map +1 -1
- package/dist/esm/store/types.js.map +1 -1
- package/dist/esm/store/vectors.js.map +1 -1
- package/dist/esm/text/segment.js.map +1 -1
- package/dist/esm/text/strip.js.map +1 -1
- package/dist/esm/watch-claim.js.map +1 -1
- package/dist/esm/watch.js.map +1 -1
- package/dist/esm/workers/parse.js.map +1 -1
- package/dist/esm/workers/watch-heartbeat.js.map +1 -1
- package/package.json +2 -2
package/dist/esm/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/open-source/ai/sensemaking/src/cli.ts"],"sourcesContent":["import { createRequire } from 'node:module';\nimport { parseArgs } from 'node:util';\nimport { COMMANDS, USAGE } from './cli/index.ts';\nimport type { Ctx } from './cli/types.ts';\nimport type { LoadConfigOptions } from './config/index.ts';\nimport { loadConfig, SUPPORTED_CONFIG_VERSION } from './config/index.ts';\n\n// Parsing and dispatch only; commands lazy-load from src/cli/, so nothing heavy is imported\n// here. Flags parse per command -- this file handles only --version/--help/--list/--config.\n\n// Works from dist/cjs and dist/esm alike: walk up past the dist type-marker package.json.\nfunction packageVersion(): string {\n const load = createRequire(import.meta.url);\n for (const rel of ['../package.json', '../../package.json', '../../../package.json']) {\n try {\n const pkg = load(rel) as { name?: string; version?: string };\n if (pkg.name === 'sensemaking' && pkg.version) return pkg.version;\n } catch {}\n }\n return 'unknown';\n}\n\nfunction usage(name: string): string {\n const lines = Object.values(USAGE).map((u) => ` ${name} ${u}`);\n return [`usage: ${name} <name> [params...] [--format table|json|csv] [--config path]`, ...lines, ` ${name} --list`, ` ${name} --version`].join('\\n');\n}\n\nfunction resolveConfigFor(name: string, configPath: string | undefined, options: LoadConfigOptions & { query?: boolean } = {}) {\n // Read the query policy before allowing a migration to write configuration state.\n let cfg = loadConfig(configPath, options.query ? { writeMigration: false } : options);\n const writeMigration = options.writeMigration !== false && (!options.query || cfg.build !== false);\n if (options.query && writeMigration && cfg.migratedFrom !== undefined) cfg = loadConfig(configPath);\n if (cfg.migratedFrom !== undefined) {\n console.warn(\n !writeMigration\n ? `${name}: ${cfg.configPath} needs migration from config version ${cfg.migratedFrom} to ${SUPPORTED_CONFIG_VERSION}; using the migrated form in memory only (run ${name} build to update the file)`\n : `${name}: migrated ${cfg.configPath} from config version ${cfg.migratedFrom} to ${SUPPORTED_CONFIG_VERSION}`\n );\n }\n if (cfg.unknownKeys !== undefined) {\n console.warn(`${name}: ${cfg.configPath} sets ${cfg.unknownKeys.join(', ')}, which this build does not read (no effect); see schema.json for the keys it does`);\n }\n return cfg;\n}\n\n// Thrown errors -> exit 1 with the message verbatim; usage errors exit 2 directly.\nexport default async function cli(argv: string[], name: string): Promise<void> {\n // A closed pipe reader's EPIPE arrives async, after the write loop returns: console.log absorbs it, process.stdout.write (streaming formats) does not, so an unhandled listener kills the process.\n // A throw here is uncaught (async); process.exit() would skip `sense watch`'s cleanup, leaving a heartbeat behind that makes a dead watcher look alive.\n process.stdout.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EPIPE') return;\n console.error(`sense: ${err.message}`);\n process.exitCode = 1;\n });\n\n const first = argv[0];\n\n // No command word, or the first token looks like a flag: the only flags this level knows.\n if (!first || first.startsWith('-')) {\n let values: Record<string, string | boolean | undefined>;\n try {\n ({ values } = parseArgs({\n args: argv,\n options: {\n version: { type: 'boolean', default: false, short: 'v' },\n help: { type: 'boolean', default: false, short: 'h' },\n list: { type: 'boolean', default: false },\n config: { type: 'string' },\n },\n allowPositionals: true,\n }));\n } catch (err) {\n console.error((err as Error).message);\n console.error(usage(name));\n process.exit(2);\n }\n\n try {\n if (values.version) {\n console.log(packageVersion());\n return;\n }\n if (values.help) {\n console.log(usage(name));\n return;\n }\n if (values.list) {\n const queries = resolveConfigFor(name, values.config as string | undefined).queries;\n for (const queryName of Object.keys(queries).sort()) {\n // Every entry names its verb, so --list can label both kinds rather than only\n // marking the exception.\n console.log(`${queryName} (${'search' in queries[queryName] ? 'search' : 'sql'})`);\n }\n return;\n }\n } catch (err) {\n console.error((err as Error).message);\n process.exit(1);\n }\n\n console.error(usage(name));\n process.exit(2);\n }\n\n // Breaking renames get one release of a pointer instead of an \"unknown\" error: find ->\n // search, and query -> sql (which took its name back from the search sense of \"query\").\n const RENAMED: Record<string, string> = { find: 'search', query: 'sql' };\n if (RENAMED[first]) {\n console.error(`${name}: ${first} is now ${RENAMED[first]}`);\n console.error(usage(name));\n process.exit(2);\n }\n\n const ctx: Ctx = {\n name,\n argv: argv.slice(1),\n resolveConfig: (configPath, options) => resolveConfigFor(name, configPath, options),\n usageError(message) {\n console.error(message);\n process.exit(2);\n },\n };\n\n try {\n const load = COMMANDS[first];\n if (load) await (await load()).default(ctx);\n else await (await import('./cli/named.ts')).default(ctx, first);\n } catch (err) {\n console.error((err as Error).message);\n process.exit(1);\n }\n}\n"],"names":["createRequire","parseArgs","COMMANDS","USAGE","loadConfig","SUPPORTED_CONFIG_VERSION","packageVersion","load","url","rel","pkg","name","version","usage","lines","Object","values","map","u","join","resolveConfigFor","configPath","options","cfg","query","writeMigration","build","migratedFrom","undefined","console","warn","unknownKeys","cli","argv","process","stdout","on","err","code","error","message","exitCode","first","startsWith","args","type","default","short","help","list","config","allowPositionals","exit","log","queries","queryName","keys","sort","RENAMED","find","ctx","slice","resolveConfig","usageError"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/open-source/ai/sensemaking/src/commands/index.ts"],"sourcesContent":["// The three commands: mapTree (orient), search (locate), peek (structure).\n// Each returns data; cli.ts renders. All of them degrade when a feature is off.\n\nexport type { TreeMap, TreeMapField, TreeMapHub, TreeMapRecent } from './map.ts';\nexport { mapTree } from './map.ts';\nexport type { PathOptions } from './path.ts';\nexport { findPath } from './path.ts';\nexport type { Peek, PeekOptions, PeekSection } from './peek.ts';\nexport { peek, resolveNote } from './peek.ts';\nexport type { RelatedOptions, RelatedResult } from './related.ts';\nexport { relatedNotes } from './related.ts';\nexport { scopedPaths } from './scope.ts';\nexport type { SearchOptions, SearchResult, SearchResultVia, SearchSignalContribution } from './search.ts';\nexport { search } from './search.ts';\nexport type { PresetCoverage } from './status.ts';\nexport { presetCoverage } from './status.ts';\n"],"names":["mapTree","findPath","peek","resolveNote","relatedNotes","scopedPaths","search","presetCoverage"],"mappings":"AAAA,2EAA2E;AAC3E,gFAAgF;AAGhF,SAASA,OAAO,QAAQ,WAAW;AAEnC,SAASC,QAAQ,QAAQ,YAAY;AAErC,SAASC,IAAI,EAAEC,WAAW,QAAQ,YAAY;AAE9C,SAASC,YAAY,QAAQ,eAAe;AAC5C,SAASC,WAAW,QAAQ,aAAa;AAEzC,SAASC,MAAM,QAAQ,cAAc;AAErC,SAASC,cAAc,QAAQ,cAAc"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/open-source/ai/sensemaking/src/commands/map.ts"],"sourcesContent":["import type { FeatureName, ResolvedConfig, SearchOverrides } from '../config/index.ts';\nimport { featureEnabled, featureStates } from '../config/index.ts';\nimport { serialQuery } from '../lib/serial-query.ts';\nimport type { FieldStat, Store } from '../store/types.ts';\nimport { INTERNAL_COLUMNS, scopedPaths, setupMapScope } from './scope.ts';\nimport { assertQuerySnapshot } from './snapshot.ts';\nimport type { PresetCoverage } from './status.ts';\nimport { presetCoverage } from './status.ts';\n\nexport interface TreeMap {\n docs: { count: number; bytes: number };\n fields: TreeMapField[]; // top 20 by coverage; fieldsTotal carries the real count\n fieldsTotal: number;\n features: { on: FeatureName[]; off: FeatureName[] };\n presets: PresetCoverage[];\n hubs: TreeMapHub[];\n recent: TreeMapRecent[];\n recentCaveat: string | null;\n}\n\nexport type TreeMapField = FieldStat;\n\nexport type TreeMapHub = {\n path: string;\n rank: number;\n title: string;\n};\n\nexport type TreeMapRecent = {\n path: string;\n modified: string;\n};\n\n// A result row is capped at SQLITE_MAX_COLUMN (2000, default); two aggregate expressions per\n// field keeps a chunk's row width safely under that regardless of how many fields the tree has.\nconst MAP_COLUMN_CHUNK = 300;\n\n// \"YYYY-MM-DD HH:MM:SS\" UTC, the exact shape sqlite's datetime(ms / 1000, 'unixepoch') prints;\n// formatted in JS so the query stays dialect-free.\nfunction utcSecond(ms: number): string {\n return new Date(ms).toISOString().slice(0, 19).replace('T', ' ');\n}\n\nfunction chunk<T>(items: T[], size: number): T[][] {\n const out: T[][] = [];\n for (let i = 0; i < items.length; i += size) out.push(items.slice(i, i + size));\n return out;\n}\n\n// What is this scope: fixed-size output regardless of tree size. Coverage and features stay\n// global -- they describe the tree, not the current question.\nexport function mapTree(store: Store, cfg: ResolvedConfig, overrides: SearchOverrides = {}): Promise<TreeMap> {\n return serialQuery(store, () => mapIndexed(store, cfg, overrides));\n}\n\nasync function mapIndexed(store: Store, cfg: ResolvedConfig, overrides: SearchOverrides): Promise<TreeMap> {\n return store.transaction(async () => {\n await assertQuerySnapshot(store, cfg);\n await setupMapScope(store, await scopedPaths(store, cfg, overrides));\n const scopeWhere = 'WHERE \"path\" IN (SELECT \"path\" FROM _map_scope)';\n const scopeAnd = 'AND f.\"path\" IN (SELECT \"path\" FROM _map_scope)';\n\n const docs = (await (await store.prepare(`SELECT COUNT(*) AS count, COALESCE(SUM(\"_size\"), 0) AS bytes FROM frontmatter ${scopeWhere}`)).get()) as { count: number; bytes: number };\n\n const columns = (await store.docs.columns()).filter((name) => !INTERNAL_COLUMNS.has(name));\n // Observed types, not declared: a field can be text in most notes and numeric in a few, so\n // every distinct type observed across the scope is reported, not just the first.\n const allFields: FieldStat[] = [];\n for (const group of chunk(columns, MAP_COLUMN_CHUNK)) allFields.push(...(await store.docs.fieldStats(group, scopeWhere)));\n allFields.sort((a, b) => b.coverage - a.coverage);\n const fields = allFields.slice(0, 20);\n\n const hubs = featureEnabled(cfg, 'rank')\n ? ((await (await store.prepare(`SELECT f.\"path\" AS path, round(f.\"_rank\" * 100, 2) AS rank, content.title FROM frontmatter f JOIN content ON content.path = f.\"path\" WHERE f.\"_rank\" IS NOT NULL ${scopeAnd} ORDER BY f.\"_rank\" DESC, f.\"path\" ASC LIMIT 8`)).all()) as TreeMapHub[])\n : [];\n\n const recent = ((await (await store.prepare(`SELECT \"path\", \"_mtime\" AS mtime FROM frontmatter ${scopeWhere} ORDER BY \"_mtime\" DESC, \"path\" ASC LIMIT 5`)).all()) as Array<{ path: string; mtime: number }>).map((r) => ({ path: r.path, modified: utcSecond(r.mtime) }));\n\n // A fresh clone/copy stamps files with checkout time, not edit history; second granularity\n // matches the `recent` table above and is coarse enough to catch that without an exact-ms match.\n const topSecond = (await (await store.prepare(`SELECT COUNT(*) AS n FROM frontmatter ${scopeWhere} GROUP BY CAST(\"_mtime\" / 1000 AS INTEGER) ORDER BY n DESC LIMIT 1`)).get()) as { n: number } | undefined;\n const recentCaveat = topSecond && docs.count > 1 && topSecond.n > docs.count / 2 ? `${topSecond.n} of ${docs.count} files share one modified second, so recency likely reflects a checkout or copy, not edit history` : null;\n\n return { docs, fields, fieldsTotal: allFields.length, features: featureStates(cfg), presets: await presetCoverage(store, cfg), hubs, recent, recentCaveat };\n });\n}\n"],"names":["featureEnabled","featureStates","serialQuery","INTERNAL_COLUMNS","scopedPaths","setupMapScope","assertQuerySnapshot","presetCoverage","MAP_COLUMN_CHUNK","utcSecond","ms","Date","toISOString","slice","replace","chunk","items","size","out","i","length","push","mapTree","store","cfg","overrides","mapIndexed","transaction","scopeWhere","scopeAnd","docs","prepare","get","columns","filter","name","has","allFields","group","fieldStats","sort","a","b","coverage","fields","hubs","all","recent","map","r","path","modified","mtime","topSecond","recentCaveat","count","n","fieldsTotal","features","presets"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/open-source/ai/sensemaking/src/commands/path.ts"],"sourcesContent":["import type { ResolvedConfig, SearchOverrides } from '../config/index.ts';\nimport { resolveSearch } from '../config/index.ts';\nimport { SenseError } from '../errors.ts';\nimport { findPathInSnapshot } from '../graph/traverse.ts';\nimport { serialQuery } from '../lib/serial-query.ts';\nimport type { Store } from '../store/types.ts';\nimport { resolveNote } from './peek.ts';\nimport { scopedPaths } from './scope.ts';\nimport { assertQuerySnapshot } from './snapshot.ts';\n\n/**\n * Options for {@link findPath}. Scope fields constrain intermediate nodes; `maxDepth` limits the\n * number of traversed links and must be a positive integer when supplied.\n */\nexport type PathOptions = Omit<SearchOverrides, 'k'> & {\n maxDepth?: number;\n};\n\n/**\n * Finds the shortest undirected link path between two indexed notes. Endpoints resolve across\n * the indexed tree and remain permitted outside the scope applied to intermediate nodes.\n */\nexport function findPath(store: Store, cfg: ResolvedConfig, fromArg: string, toArg: string, options: PathOptions = {}): Promise<string[] | null> {\n return serialQuery(store, () => findScopedPath(store, cfg, fromArg, toArg, options));\n}\n\nasync function findScopedPath(store: Store, cfg: ResolvedConfig, fromArg: string, toArg: string, options: PathOptions): Promise<string[] | null> {\n if (options.maxDepth !== undefined && (!Number.isFinite(options.maxDepth) || !Number.isInteger(options.maxDepth) || options.maxDepth <= 0)) {\n throw new SenseError('SEARCH_OPTION_INVALID', `path option \"maxDepth\" must be a positive finite integer, got ${String(options.maxDepth)}`);\n }\n // Validate the named preset before opening the snapshot; repeat it inside because preset_files\n // and the compatibility assertion below belong to the final published generation.\n resolveSearch(cfg, options);\n return store.transaction(async () => {\n await assertQuerySnapshot(store, cfg);\n resolveSearch(cfg, options);\n const paths = ((await (await store.prepare('SELECT \"path\" FROM frontmatter')).all()) as Array<{ path: string }>).map((row) => row.path);\n const from = resolveNote(paths, fromArg);\n const to = resolveNote(paths, toArg);\n const allowed = await scopedPaths(store, cfg, options);\n return findPathInSnapshot(store, from, to, { maxDepth: options.maxDepth, allowed });\n });\n}\n"],"names":["resolveSearch","SenseError","findPathInSnapshot","serialQuery","resolveNote","scopedPaths","assertQuerySnapshot","findPath","store","cfg","fromArg","toArg","options","findScopedPath","maxDepth","undefined","Number","isFinite","isInteger","String","transaction","paths","prepare","all","map","row","path","from","to","allowed"],"mappings":"AACA,SAASA,aAAa,QAAQ,qBAAqB;AACnD,SAASC,UAAU,QAAQ,eAAe;AAC1C,SAASC,kBAAkB,QAAQ,uBAAuB;AAC1D,SAASC,WAAW,QAAQ,yBAAyB;AAErD,SAASC,WAAW,QAAQ,YAAY;AACxC,SAASC,WAAW,QAAQ,aAAa;AACzC,SAASC,mBAAmB,QAAQ,gBAAgB;AAUpD;;;CAGC,GACD,OAAO,SAASC,SAASC,KAAY,EAAEC,GAAmB,EAAEC,OAAe,EAAEC,KAAa,EAAEC,UAAuB,CAAC,CAAC;IACnH,OAAOT,YAAYK,OAAO,IAAMK,eAAeL,OAAOC,KAAKC,SAASC,OAAOC;AAC7E;AAEA,eAAeC,eAAeL,KAAY,EAAEC,GAAmB,EAAEC,OAAe,EAAEC,KAAa,EAAEC,OAAoB;IACnH,IAAIA,QAAQE,QAAQ,KAAKC,aAAc,CAAA,CAACC,OAAOC,QAAQ,CAACL,QAAQE,QAAQ,KAAK,CAACE,OAAOE,SAAS,CAACN,QAAQE,QAAQ,KAAKF,QAAQE,QAAQ,IAAI,CAAA,GAAI;QAC1I,MAAM,IAAIb,WAAW,yBAAyB,CAAC,8DAA8D,EAAEkB,OAAOP,QAAQE,QAAQ,GAAG;IAC3I;IACA,+FAA+F;IAC/F,kFAAkF;IAClFd,cAAcS,KAAKG;IACnB,OAAOJ,MAAMY,WAAW,CAAC;QACvB,MAAMd,oBAAoBE,OAAOC;QACjCT,cAAcS,KAAKG;QACnB,MAAMS,QAAQ,AAAE,CAAA,MAAM,AAAC,CAAA,MAAMb,MAAMc,OAAO,CAAC,iCAAgC,EAAGC,GAAG,EAAC,EAA+BC,GAAG,CAAC,CAACC,MAAQA,IAAIC,IAAI;QACtI,MAAMC,OAAOvB,YAAYiB,OAAOX;QAChC,MAAMkB,KAAKxB,YAAYiB,OAAOV;QAC9B,MAAMkB,UAAU,MAAMxB,YAAYG,OAAOC,KAAKG;QAC9C,OAAOV,mBAAmBM,OAAOmB,MAAMC,IAAI;YAAEd,UAAUF,QAAQE,QAAQ;YAAEe;QAAQ;IACnF;AACF"}
|
|
@@ -1 +1 @@
|
|
|
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 { serialQuery } from '../lib/serial-query.ts';\nimport type { Row } from '../output/output.ts';\nimport type { Store } from '../store/types.ts';\nimport { INTERNAL_COLUMNS, materializeScope, scopedPaths } from './scope.ts';\nimport { assertQuerySnapshot } from './snapshot.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\n/** Structured, bounded metadata returned by the root {@link peek} API. */\nexport interface Peek {\n path: string;\n tokens: number;\n frontmatter: Row;\n /** Set when frontmatter parsing failed, distinguishing that case from an empty block. */\n parseError: string | null;\n sections: PeekSection[];\n outbound: string[];\n backlinks: string[];\n unresolved: string[];\n /** Totals before each bounded list is truncated. */\n sectionsTotal: number;\n outboundTotal: number;\n backlinksTotal: number;\n unresolvedTotal: number;\n /** Disabled features whose result blocks are omitted rather than returned empty. */\n off: FeatureName[];\n}\n\n/** A section description, with an inclusive source line range and token estimate, not section prose. */\nexport interface PeekSection {\n level: number;\n heading: string;\n start_line: number;\n end_line: number;\n tokens: number;\n}\n\n/** Options for the root {@link peek} API. */\nexport interface PeekOptions extends SearchOverrides {\n /** Maximum section descriptions. Defaults to 20; must be a positive safe integer. */\n sectionCountLimit?: number;\n /** Maximum entries in each link group independently. Defaults to 20; must be a positive safe integer. */\n linkCountLimit?: number;\n}\n\nconst PEEK_COUNT_LIMIT = 20;\n\n/**\n * Returns indexed metadata for a note in scope. Exact excluded paths fail before scoped basename\n * resolution; resolved neighbors stay in scope, while unresolved written targets remain visible.\n */\nexport function peek(store: Store, cfg: ResolvedConfig, pathArg: string, overrides: PeekOptions = {}): Promise<Peek> {\n return serialQuery(store, () => peekIndexed(store, cfg, pathArg, overrides));\n}\n\nasync function peekIndexed(store: Store, cfg: ResolvedConfig, pathArg: string, overrides: PeekOptions): Promise<Peek> {\n for (const name of ['sectionCountLimit', 'linkCountLimit'] as const) {\n const value = overrides[name];\n if (value !== undefined && (!Number.isSafeInteger(value) || value <= 0)) {\n throw new SenseError('SEARCH_OPTION_INVALID', `peek option \"${name}\" must be a positive safe integer, got ${String(value)}`);\n }\n }\n const sectionCountLimit = overrides.sectionCountLimit ?? PEEK_COUNT_LIMIT;\n const linkCountLimit = overrides.linkCountLimit ?? PEEK_COUNT_LIMIT;\n return store.transaction(async () => {\n await assertQuerySnapshot(store, cfg);\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 allowed = await scopedPaths(store, cfg, overrides);\n if (paths.includes(pathArg) && !allowed.has(pathArg)) {\n throw new SenseError('NOTE_NOT_FOUND', `note \"${pathArg}\" exists but is outside the current scope; change the preset, include/exclude patterns, or where condition`);\n }\n let path: string;\n try {\n path = resolveNote([...allowed], pathArg);\n } catch (err) {\n if (err instanceof SenseError && err.code === 'NOTE_NOT_FOUND') {\n let excludedPath: string;\n try {\n excludedPath = resolveNote(paths, pathArg);\n } catch (unscopedErr) {\n if (unscopedErr instanceof SenseError && unscopedErr.code === 'NOTE_AMBIGUOUS') {\n throw new SenseError('NOTE_NOT_FOUND', `no note in the current scope matches \"${pathArg}\"; indexed matches exist outside the scope, so change the preset, include/exclude patterns, or where condition`);\n }\n throw err;\n }\n throw new SenseError('NOTE_NOT_FOUND', `note \"${excludedPath}\" exists but is outside the current scope; change the preset, include/exclude patterns, or where condition`);\n }\n throw err;\n }\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: PeekSection[] = [];\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, sectionCountLimit)) as PeekSection[];\n }\n\n let outbound: string[] = [];\n let backlinks: string[] = [];\n let unresolved: string[] = [];\n let outboundTotal = 0;\n let backlinksTotal = 0;\n let unresolvedTotal = 0;\n if (featureEnabled(cfg, 'links')) {\n await materializeScope(store, '_peek_scope', allowed);\n outboundTotal = ((await (await store.prepare('SELECT COUNT(DISTINCT dst) AS n FROM links WHERE src = ? AND dst != src AND dst IN (SELECT \"path\" FROM _peek_scope)')).get(path)) as { n: number }).n;\n // The first written target determines each resolved destination's position.\n outbound = ((await (await store.prepare('SELECT dst FROM links WHERE src = ? AND dst != src AND dst IN (SELECT \"path\" FROM _peek_scope) GROUP BY dst ORDER BY MIN(target) LIMIT ?')).all(path, linkCountLimit)) as Array<{ dst: string }>).map((r) => r.dst);\n unresolvedTotal = ((await (await store.prepare('SELECT COUNT(*) AS n FROM links WHERE src = ? AND dst IS NULL')).get(path)) as { n: number }).n;\n unresolved = ((await (await store.prepare('SELECT target FROM links WHERE src = ? AND dst IS NULL ORDER BY target LIMIT ?')).all(path, linkCountLimit)) as Array<{ target: string }>).map((r) => r.target);\n backlinksTotal = ((await (await store.prepare('SELECT COUNT(DISTINCT src) AS n FROM links WHERE dst = ? AND src != dst AND src IN (SELECT \"path\" FROM _peek_scope)')).get(path)) as { n: number }).n;\n backlinks = ((await (await store.prepare('SELECT DISTINCT src FROM links WHERE dst = ? AND src != dst AND src IN (SELECT \"path\" FROM _peek_scope) ORDER BY src LIMIT ?')).all(path, linkCountLimit)) 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,\n backlinks,\n unresolved,\n sectionsTotal,\n outboundTotal,\n backlinksTotal,\n unresolvedTotal,\n off: (['sections', 'links'] as FeatureName[]).filter((name) => !featureEnabled(cfg, name)),\n };\n });\n}\n"],"names":["posix","estimateTokens","featureEnabled","SenseError","serialQuery","INTERNAL_COLUMNS","materializeScope","scopedPaths","assertQuerySnapshot","resolveNote","paths","arg","exact","find","p","base","basename","replace","toLowerCase","matches","filter","length","join","PEEK_COUNT_LIMIT","peek","store","cfg","pathArg","overrides","peekIndexed","name","value","undefined","Number","isSafeInteger","String","sectionCountLimit","linkCountLimit","transaction","row","pathsStmt","prepare","all","map","r","path","allowed","includes","has","err","code","excludedPath","unscopedErr","rowStmt","get","parseError","_parse_error","frontmatter","key","Object","entries","sectionsTotal","sections","n","outbound","backlinks","unresolved","outboundTotal","backlinksTotal","unresolvedTotal","dst","target","src","body","text","byteTokens","Math","ceil","_size","tokens","max","off"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/open-source/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 { serialQuery } from '../lib/serial-query.ts';\nimport type { Row } from '../output/output.ts';\nimport type { Store } from '../store/types.ts';\nimport { INTERNAL_COLUMNS, materializeScope, scopedPaths } from './scope.ts';\nimport { assertQuerySnapshot } from './snapshot.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\n/** Structured, bounded metadata returned by the root {@link peek} API. */\nexport interface Peek {\n path: string;\n tokens: number;\n frontmatter: Row;\n /** Set when frontmatter parsing failed, distinguishing that case from an empty block. */\n parseError: string | null;\n sections: PeekSection[];\n outbound: string[];\n backlinks: string[];\n unresolved: string[];\n /** Totals before each bounded list is truncated. */\n sectionsTotal: number;\n outboundTotal: number;\n backlinksTotal: number;\n unresolvedTotal: number;\n /** Disabled features whose result blocks are omitted rather than returned empty. */\n off: FeatureName[];\n}\n\n/** A section description, with an inclusive source line range and token estimate, not section prose. */\nexport interface PeekSection {\n level: number;\n heading: string;\n start_line: number;\n end_line: number;\n tokens: number;\n}\n\n/** Options for the root {@link peek} API. */\nexport interface PeekOptions extends SearchOverrides {\n /** Maximum section descriptions. Defaults to 20; must be a positive safe integer. */\n sectionCountLimit?: number;\n /** Maximum entries in each link group independently. Defaults to 20; must be a positive safe integer. */\n linkCountLimit?: number;\n}\n\nconst PEEK_COUNT_LIMIT = 20;\n\n/**\n * Returns indexed metadata for a note in scope. Exact excluded paths fail before scoped basename\n * resolution; resolved neighbors stay in scope, while unresolved written targets remain visible.\n */\nexport function peek(store: Store, cfg: ResolvedConfig, pathArg: string, overrides: PeekOptions = {}): Promise<Peek> {\n return serialQuery(store, () => peekIndexed(store, cfg, pathArg, overrides));\n}\n\nasync function peekIndexed(store: Store, cfg: ResolvedConfig, pathArg: string, overrides: PeekOptions): Promise<Peek> {\n for (const name of ['sectionCountLimit', 'linkCountLimit'] as const) {\n const value = overrides[name];\n if (value !== undefined && (!Number.isSafeInteger(value) || value <= 0)) {\n throw new SenseError('SEARCH_OPTION_INVALID', `peek option \"${name}\" must be a positive safe integer, got ${String(value)}`);\n }\n }\n const sectionCountLimit = overrides.sectionCountLimit ?? PEEK_COUNT_LIMIT;\n const linkCountLimit = overrides.linkCountLimit ?? PEEK_COUNT_LIMIT;\n return store.transaction(async () => {\n await assertQuerySnapshot(store, cfg);\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 allowed = await scopedPaths(store, cfg, overrides);\n if (paths.includes(pathArg) && !allowed.has(pathArg)) {\n throw new SenseError('NOTE_NOT_FOUND', `note \"${pathArg}\" exists but is outside the current scope; change the preset, include/exclude patterns, or where condition`);\n }\n let path: string;\n try {\n path = resolveNote([...allowed], pathArg);\n } catch (err) {\n if (err instanceof SenseError && err.code === 'NOTE_NOT_FOUND') {\n let excludedPath: string;\n try {\n excludedPath = resolveNote(paths, pathArg);\n } catch (unscopedErr) {\n if (unscopedErr instanceof SenseError && unscopedErr.code === 'NOTE_AMBIGUOUS') {\n throw new SenseError('NOTE_NOT_FOUND', `no note in the current scope matches \"${pathArg}\"; indexed matches exist outside the scope, so change the preset, include/exclude patterns, or where condition`);\n }\n throw err;\n }\n throw new SenseError('NOTE_NOT_FOUND', `note \"${excludedPath}\" exists but is outside the current scope; change the preset, include/exclude patterns, or where condition`);\n }\n throw err;\n }\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: PeekSection[] = [];\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, sectionCountLimit)) as PeekSection[];\n }\n\n let outbound: string[] = [];\n let backlinks: string[] = [];\n let unresolved: string[] = [];\n let outboundTotal = 0;\n let backlinksTotal = 0;\n let unresolvedTotal = 0;\n if (featureEnabled(cfg, 'links')) {\n await materializeScope(store, '_peek_scope', allowed);\n outboundTotal = ((await (await store.prepare('SELECT COUNT(DISTINCT dst) AS n FROM links WHERE src = ? AND dst != src AND dst IN (SELECT \"path\" FROM _peek_scope)')).get(path)) as { n: number }).n;\n // The first written target determines each resolved destination's position.\n outbound = ((await (await store.prepare('SELECT dst FROM links WHERE src = ? AND dst != src AND dst IN (SELECT \"path\" FROM _peek_scope) GROUP BY dst ORDER BY MIN(target) LIMIT ?')).all(path, linkCountLimit)) as Array<{ dst: string }>).map((r) => r.dst);\n unresolvedTotal = ((await (await store.prepare('SELECT COUNT(*) AS n FROM links WHERE src = ? AND dst IS NULL')).get(path)) as { n: number }).n;\n unresolved = ((await (await store.prepare('SELECT target FROM links WHERE src = ? AND dst IS NULL ORDER BY target LIMIT ?')).all(path, linkCountLimit)) as Array<{ target: string }>).map((r) => r.target);\n backlinksTotal = ((await (await store.prepare('SELECT COUNT(DISTINCT src) AS n FROM links WHERE dst = ? AND src != dst AND src IN (SELECT \"path\" FROM _peek_scope)')).get(path)) as { n: number }).n;\n backlinks = ((await (await store.prepare('SELECT DISTINCT src FROM links WHERE dst = ? AND src != dst AND src IN (SELECT \"path\" FROM _peek_scope) ORDER BY src LIMIT ?')).all(path, linkCountLimit)) 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,\n backlinks,\n unresolved,\n sectionsTotal,\n outboundTotal,\n backlinksTotal,\n unresolvedTotal,\n off: (['sections', 'links'] as FeatureName[]).filter((name) => !featureEnabled(cfg, name)),\n };\n });\n}\n"],"names":["posix","estimateTokens","featureEnabled","SenseError","serialQuery","INTERNAL_COLUMNS","materializeScope","scopedPaths","assertQuerySnapshot","resolveNote","paths","arg","exact","find","p","base","basename","replace","toLowerCase","matches","filter","length","join","PEEK_COUNT_LIMIT","peek","store","cfg","pathArg","overrides","peekIndexed","name","value","undefined","Number","isSafeInteger","String","sectionCountLimit","linkCountLimit","transaction","row","pathsStmt","prepare","all","map","r","path","allowed","includes","has","err","code","excludedPath","unscopedErr","rowStmt","get","parseError","_parse_error","frontmatter","key","Object","entries","sectionsTotal","sections","n","outbound","backlinks","unresolved","outboundTotal","backlinksTotal","unresolvedTotal","dst","target","src","body","text","byteTokens","Math","ceil","_size","tokens","max","off"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/open-source/ai/sensemaking/src/commands/related.ts"],"sourcesContent":["import type { ResolvedConfig, SearchOverrides } from '../config/index.ts';\nimport { embedConfig, embedEnabled, resolveSearch } from '../config/index.ts';\nimport { ensureDocumentEmbeddings, hasEmbedding, similarNotes } from '../embed/query.ts';\nimport { localModelMissing, MODEL_FILENAMES } from '../embed/store.ts';\nimport { SenseError } from '../errors.ts';\nimport { serialQuery } from '../lib/serial-query.ts';\nimport type { Store } from '../store/types.ts';\nimport { resolveNote } from './peek.ts';\nimport { scopedPaths, scopeHasEmbeddings, withMaterializedVectorScope } from './scope.ts';\nimport { assertQuerySnapshot } from './snapshot.ts';\n\nexport const RELATED_DEFAULT_K = 5;\n\n/**\n * Options for {@link relatedNotes}. Scope fields filter result candidates, not the seed note;\n * `k` defaults to 5, matching the CLI.\n */\nexport interface RelatedOptions extends SearchOverrides {\n k?: number;\n}\n\nexport type RelatedResult = {\n path: string;\n similarity: number;\n};\n\nexport async function resolveRelatedSeed(store: Store, pathArg: string): Promise<string> {\n const paths = ((await (await store.prepare('SELECT \"path\" FROM frontmatter')).all()) as Array<{ path: string }>).map((row) => row.path);\n return resolveNote(paths, pathArg);\n}\n\n/**\n * Returns the notes most similar to an indexed seed, excluding the seed and notes linked either\n * way. The seed resolves across the indexed tree even when it is outside the candidate scope.\n */\nexport function relatedNotes(store: Store, cfg: ResolvedConfig, pathArg: string, options: RelatedOptions = {}): Promise<RelatedResult[]> {\n return serialQuery(store, () => relatedIndexed(store, cfg, pathArg, options, options.k ?? RELATED_DEFAULT_K));\n}\n\nasync function relatedIndexed(store: Store, cfg: ResolvedConfig, pathArg: string, overrides: RelatedOptions, k: number): Promise<RelatedResult[]> {\n if (!Number.isFinite(k) || !Number.isInteger(k) || k <= 0) {\n throw new SenseError('SEARCH_OPTION_INVALID', `related option \"k\" must be a positive finite integer, got ${String(k)}`);\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 result means one thing, nothing near in meaning.\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, so a preset's declared signals mean one thing: without\n // this check an overlapping vectors-on preset 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 can be fetched by preparation. Only a local path with missing files\n // 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\n return store.transaction(async () => {\n await assertQuerySnapshot(store, cfg);\n // Repeat effective preset validation inside the snapshot that supplies its durable scope.\n const current = resolveSearch(cfg, overrides);\n if (current.signals.vectors === undefined) {\n throw new SenseError('PRESET_NOT_SEMANTIC', `preset \"${current.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\n const path = await resolveRelatedSeed(store, pathArg);\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 const allowed = await scopedPaths(store, cfg, overrides);\n // The seed may sit outside a narrowed candidate scope; both it and every eligible result must\n // be ready, while pending vectors in unrelated notes do not block this operation.\n await ensureDocumentEmbeddings(store, cfg, new Set([...allowed, path]));\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 withMaterializedVectorScope(store, allowed, () => similarNotes(store, cfg, path, { exclude, allowed, k }));\n });\n}\n"],"names":["embedConfig","embedEnabled","resolveSearch","ensureDocumentEmbeddings","hasEmbedding","similarNotes","localModelMissing","MODEL_FILENAMES","SenseError","serialQuery","resolveNote","scopedPaths","scopeHasEmbeddings","withMaterializedVectorScope","assertQuerySnapshot","RELATED_DEFAULT_K","resolveRelatedSeed","store","pathArg","paths","prepare","all","map","row","path","relatedNotes","cfg","options","relatedIndexed","k","overrides","Number","isFinite","isInteger","String","effective","signals","vectors","undefined","presetName","e","model","transaction","current","outbound","r","dst","backlinks","src","exclude","Set","allowed"],"mappings":"AACA,SAASA,WAAW,EAAEC,YAAY,EAAEC,aAAa,QAAQ,qBAAqB;AAC9E,SAASC,wBAAwB,EAAEC,YAAY,EAAEC,YAAY,QAAQ,oBAAoB;AACzF,SAASC,iBAAiB,EAAEC,eAAe,QAAQ,oBAAoB;AACvE,SAASC,UAAU,QAAQ,eAAe;AAC1C,SAASC,WAAW,QAAQ,yBAAyB;AAErD,SAASC,WAAW,QAAQ,YAAY;AACxC,SAASC,WAAW,EAAEC,kBAAkB,EAAEC,2BAA2B,QAAQ,aAAa;AAC1F,SAASC,mBAAmB,QAAQ,gBAAgB;AAEpD,OAAO,MAAMC,oBAAoB,EAAE;AAenC,OAAO,eAAeC,mBAAmBC,KAAY,EAAEC,OAAe;IACpE,MAAMC,QAAQ,AAAE,CAAA,MAAM,AAAC,CAAA,MAAMF,MAAMG,OAAO,CAAC,iCAAgC,EAAGC,GAAG,EAAC,EAA+BC,GAAG,CAAC,CAACC,MAAQA,IAAIC,IAAI;IACtI,OAAOd,YAAYS,OAAOD;AAC5B;AAEA;;;CAGC,GACD,OAAO,SAASO,aAAaR,KAAY,EAAES,GAAmB,EAAER,OAAe,EAAES,UAA0B,CAAC,CAAC;IAC3G,OAAOlB,YAAYQ,OAAO;YAAmDU;eAA7CC,eAAeX,OAAOS,KAAKR,SAASS,UAASA,aAAAA,QAAQE,CAAC,cAATF,wBAAAA,aAAaZ;;AAC5F;AAEA,eAAea,eAAeX,KAAY,EAAES,GAAmB,EAAER,OAAe,EAAEY,SAAyB,EAAED,CAAS;IACpH,IAAI,CAACE,OAAOC,QAAQ,CAACH,MAAM,CAACE,OAAOE,SAAS,CAACJ,MAAMA,KAAK,GAAG;QACzD,MAAM,IAAIrB,WAAW,yBAAyB,CAAC,0DAA0D,EAAE0B,OAAOL,IAAI;IACxH;IACA,yFAAyF;IACzF,8EAA8E;IAC9E,MAAMM,YAAYjC,cAAcwB,KAAKI;IACrC,IAAI,CAAC7B,aAAayB,MAAM;QACtB,MAAM,IAAIlB,WAAW,kBAAkB;IACzC;IACA,0FAA0F;IAC1F,2FAA2F;IAC3F,IAAI2B,UAAUC,OAAO,CAACC,OAAO,KAAKC,WAAW;QAC3C,MAAM,IAAI9B,WAAW,uBAAuB,CAAC,QAAQ,EAAE2B,UAAUI,UAAU,CAAC,uKAAuK,CAAC;IACtP;IACA,2FAA2F;IAC3F,0BAA0B;IAC1B,MAAMC,IAAIxC,YAAY0B,MAAM,iDAAiD;IAC7E,IAAIpB,kBAAkBkC,IAAI;QACxB,MAAM,IAAIhC,WAAW,uBAAuB,CAAC,2FAA2F,EAAEgC,EAAEC,KAAK,CAAC,aAAa,EAAElC,gBAAgB,sGAAsG,CAAC;IAC1R;IAEA,OAAOU,MAAMyB,WAAW,CAAC;QACvB,MAAM5B,oBAAoBG,OAAOS;QACjC,0FAA0F;QAC1F,MAAMiB,UAAUzC,cAAcwB,KAAKI;QACnC,IAAIa,QAAQP,OAAO,CAACC,OAAO,KAAKC,WAAW;YACzC,MAAM,IAAI9B,WAAW,uBAAuB,CAAC,QAAQ,EAAEmC,QAAQJ,UAAU,CAAC,uKAAuK,CAAC;QACpP;QAEA,MAAMf,OAAO,MAAMR,mBAAmBC,OAAOC;QAC7C,MAAM0B,WAAW,AAAE,CAAA,MAAM,AAAC,CAAA,MAAM3B,MAAMG,OAAO,CAAC,kFAAiF,EAAGC,GAAG,CAACG,KAAI,EAA8BF,GAAG,CAAC,CAACuB,IAAMA,EAAEC,GAAG;QACxL,MAAMC,YAAY,AAAE,CAAA,MAAM,AAAC,CAAA,MAAM9B,MAAMG,OAAO,CAAC,8DAA6D,EAAGC,GAAG,CAACG,KAAI,EAA8BF,GAAG,CAAC,CAACuB,IAAMA,EAAEG,GAAG;QACrK,MAAMC,UAAU,IAAIC,IAAI;YAAC1B;eAASoB;eAAaG;SAAU;QACzD,MAAMI,UAAU,MAAMxC,YAAYM,OAAOS,KAAKI;QAC9C,8FAA8F;QAC9F,kFAAkF;QAClF,MAAM3B,yBAAyBc,OAAOS,KAAK,IAAIwB,IAAI;eAAIC;YAAS3B;SAAK;QACrE,IAAI,CAAE,MAAMpB,aAAaa,OAAOO,OAAQ;YACtC,MAAM,IAAIhB,WAAW,qBAAqB,GAAGgB,KAAK,4GAA4G,CAAC;QACjK;QACA,IAAI,CAAE,MAAMZ,mBAAmBK,OAAOS,KAAKyB,UAAW,OAAO,EAAE;QAC/D,OAAOtC,4BAA4BI,OAAOkC,SAAS,IAAM9C,aAAaY,OAAOS,KAAKF,MAAM;gBAAEyB;gBAASE;gBAAStB;YAAE;IAChH;AACF"}
|
|
@@ -1 +1 @@
|
|
|
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';\nimport { NATIVE_VECTOR_SCOPE_TABLE, reserveNativeVectorScope } from '../store/vectors.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) at the\n// Node >=22.20 engines floor; used here in JS rather than 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 WHERE vector IS NOT NULL');\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()'s candidate-pool sizing.\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\n// Native vector adapters can consume a small exact scope through their connection-local temp\n// table. The reservation is tied to this Store's VectorStore wrapper, so another Store cannot\n// activate it by receiving the same Set object.\nexport async function withMaterializedVectorScope<T>(store: Store, allowed: Set<string>, callback: () => Promise<T>, knownTotal?: number): Promise<T> {\n const reservation = reserveNativeVectorScope(store.vectors, allowed);\n if (!reservation) return callback();\n\n if (allowed.size === 0) {\n reservation.invalidate();\n return callback();\n }\n\n let total = knownTotal;\n if (total === undefined) {\n try {\n const stmt = await store.prepare('SELECT COUNT(*) AS count FROM frontmatter');\n const row = (await stmt.get()) as { count: number | bigint };\n total = Number(row.count);\n } catch (err) {\n reservation.invalidate();\n throw err;\n }\n }\n // Materialization only buys work when this is a nonempty true narrowing and the allowed side\n // is no larger than its complement. Direct adapter calls and broader scopes retain JS filtering.\n if (allowed.size >= total || allowed.size > total - allowed.size) {\n reservation.invalidate();\n return callback();\n }\n\n let operationFailed = false;\n let operationError: unknown;\n let result!: T;\n try {\n await materializeScope(store, NATIVE_VECTOR_SCOPE_TABLE, allowed);\n reservation.activate();\n result = await callback();\n } catch (err) {\n operationFailed = true;\n operationError = err;\n }\n\n let cleanupFailed = false;\n let cleanupError: unknown;\n try {\n reservation.deactivate();\n await store.exec(`DROP TABLE IF EXISTS ${NATIVE_VECTOR_SCOPE_TABLE}`);\n } catch (err) {\n cleanupFailed = true;\n cleanupError = err;\n } finally {\n reservation.invalidate();\n }\n\n if (operationFailed && cleanupFailed) throw new AggregateError([operationError, cleanupError], 'native vector scope operation and cleanup failed');\n if (operationFailed) throw operationError;\n if (cleanupFailed) throw cleanupError;\n return result;\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","NATIVE_VECTOR_SCOPE_TABLE","reserveNativeVectorScope","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","withMaterializedVectorScope","allowed","callback","knownTotal","reservation","vectors","size","invalidate","total","row","get","Number","count","operationFailed","operationError","result","activate","cleanupFailed","cleanupError","deactivate","AggregateError","setupMapScope"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/open-source/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';\nimport { NATIVE_VECTOR_SCOPE_TABLE, reserveNativeVectorScope } from '../store/vectors.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) at the\n// Node >=22.20 engines floor; used here in JS rather than 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 WHERE vector IS NOT NULL');\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()'s candidate-pool sizing.\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\n// Native vector adapters can consume a small exact scope through their connection-local temp\n// table. The reservation is tied to this Store's VectorStore wrapper, so another Store cannot\n// activate it by receiving the same Set object.\nexport async function withMaterializedVectorScope<T>(store: Store, allowed: Set<string>, callback: () => Promise<T>, knownTotal?: number): Promise<T> {\n const reservation = reserveNativeVectorScope(store.vectors, allowed);\n if (!reservation) return callback();\n\n if (allowed.size === 0) {\n reservation.invalidate();\n return callback();\n }\n\n let total = knownTotal;\n if (total === undefined) {\n try {\n const stmt = await store.prepare('SELECT COUNT(*) AS count FROM frontmatter');\n const row = (await stmt.get()) as { count: number | bigint };\n total = Number(row.count);\n } catch (err) {\n reservation.invalidate();\n throw err;\n }\n }\n // Materialization only buys work when this is a nonempty true narrowing and the allowed side\n // is no larger than its complement. Direct adapter calls and broader scopes retain JS filtering.\n if (allowed.size >= total || allowed.size > total - allowed.size) {\n reservation.invalidate();\n return callback();\n }\n\n let operationFailed = false;\n let operationError: unknown;\n let result!: T;\n try {\n await materializeScope(store, NATIVE_VECTOR_SCOPE_TABLE, allowed);\n reservation.activate();\n result = await callback();\n } catch (err) {\n operationFailed = true;\n operationError = err;\n }\n\n let cleanupFailed = false;\n let cleanupError: unknown;\n try {\n reservation.deactivate();\n await store.exec(`DROP TABLE IF EXISTS ${NATIVE_VECTOR_SCOPE_TABLE}`);\n } catch (err) {\n cleanupFailed = true;\n cleanupError = err;\n } finally {\n reservation.invalidate();\n }\n\n if (operationFailed && cleanupFailed) throw new AggregateError([operationError, cleanupError], 'native vector scope operation and cleanup failed');\n if (operationFailed) throw operationError;\n if (cleanupFailed) throw cleanupError;\n return result;\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","NATIVE_VECTOR_SCOPE_TABLE","reserveNativeVectorScope","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","withMaterializedVectorScope","allowed","callback","knownTotal","reservation","vectors","size","invalidate","total","row","get","Number","count","operationFailed","operationError","result","activate","cleanupFailed","cleanupError","deactivate","AggregateError","setupMapScope"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/search.ts"],"sourcesContent":["import { channel } from 'node:diagnostics_channel';\nimport { stemmer } from 'stemmer';\nimport type { EffectiveSearch, ResolvedConfig } from '../config/index.ts';\nimport { embedConfig, featureEnabled, resolveSearch } from '../config/index.ts';\nimport type { PreparedSemanticQuery } from '../embed/query.ts';\nimport { ensureDocumentEmbeddings, prepareSemanticQuery } from '../embed/query.ts';\nimport { localModelMissing, MODEL_FILENAMES } from '../embed/store.ts';\nimport { SenseError } from '../errors.ts';\nimport { serialQuery } from '../lib/serial-query.ts';\nimport { bareTermSyntaxError, searchError } from '../output/search-error.ts';\nimport type { LexicalHit, Store } from '../store/types.ts';\nimport { foldForSearch, hasUnspacedRun, searchTokens, unspacedRuns } from '../text/segment.ts';\nimport { materializeScope, narrowByWhere, rawScope, scopeHasEmbeddings } from './scope.ts';\nimport type { Candidates, SearchResultVia, SearchSignalContribution } from './signals.ts';\nimport { linksCandidates, vectorsCandidates, wordsCandidates } from './signals.ts';\nimport { assertQuerySnapshot } from './snapshot.ts';\n\nexport type { SearchResultVia, SearchSignalContribution } from './signals.ts';\n\n// Default budget behind --snippet-char-limit/--snippet-count-limit: the limit is whatever the\n// caller passes, these only say what happens when they pass nothing (DESIGN.md \"Search snippets\").\nexport const SNIPPET_CHAR_LIMIT_DEFAULT = 80;\nexport const SNIPPET_COUNT_LIMIT_DEFAULT = 1;\n\nconst searchCandidatesRead = channel('sensemaking.search.candidates-read');\n\n// Bare terms from an FTS5 query string: strips operators/quoting, not real terms, so the\n// snippet scan matches what the query matched -- FTS5 operators are uppercase-only, so this is too.\nfunction extractBareTerms(query: string): string[] {\n const cleaned = query\n .replace(/\"/g, ' ')\n .replace(/[()*]/g, ' ')\n .replace(/\\b(AND|OR|NOT|NEAR)\\b(\\/\\d+)?/g, ' ');\n return cleaned\n .split(/\\s+/)\n .map((tok) => tok.replace(/^[A-Za-z_]\\w*:/, '')) // column filter, e.g. title:term\n .flatMap((tok) => searchTokens(tok).map(({ text }) => text))\n .map((tok) => tok.toLowerCase().trim())\n .filter((tok) => tok.length > 0);\n}\n\n// House convention (src/chunk/group.ts): Intl.Segmenter word boundaries, not a bare \\b, which\n// is ASCII-only. Built on first use: construction is a few ms, paid only when a term matches.\nlet wordSegmenter: Intl.Segmenter | undefined;\n\nfunction findOccurrences(text: string, terms: string[]): Array<{ start: number; end: number; term: string }> {\n const occ: Array<{ start: number; end: number; term: string }> = [];\n const textLower = text.toLowerCase();\n\n // Unspaced scripts (UNSPACED_SCRIPTS) mark no word boundaries: the sense-setup skill promises\n // substring semantics there, so a matching term is found wherever it occurs in the run.\n const unspacedTerms = terms.filter(hasUnspacedRun);\n if (unspacedTerms.length > 0) {\n for (const run of unspacedRuns(text)) {\n const runLower = textLower.slice(run.start, run.end);\n for (const term of unspacedTerms) {\n let idx = 0;\n for (;;) {\n const found = runLower.indexOf(term, idx);\n if (found === -1) break;\n occ.push({ start: run.start + found, end: run.start + found + term.length, term });\n idx = found + term.length;\n }\n }\n }\n }\n\n // Spaced scripts: a word is marked when its porter stem equals a query term's stem, the same\n // algorithm both indexes tokenize with (sqlite's TOKENIZE = 'porter unicode61'; duckdb's fts default).\n const spacedTerms = terms.filter((t) => !hasUnspacedRun(t));\n if (spacedTerms.length > 0) {\n const termByStem = new Map(spacedTerms.map((t) => [stemmer(foldForSearch(t)), t]));\n const stemInitials = new Set(Array.from(termByStem.keys(), (term) => term[0]));\n for (const match of searchTokens(text)) {\n const segment = match.text;\n if (match.unspaced) continue;\n const folded = foldForSearch(segment);\n // Porter stemming changes suffixes only, so a different initial cannot match a query stem.\n if (!stemInitials.has(folded[0])) continue;\n const term = termByStem.get(stemmer(folded));\n if (term !== undefined) occ.push({ start: match.start, end: match.end, term });\n }\n }\n\n return occ.sort((a, b) => a.start - b.start || b.end - a.end);\n}\n\n// Segment boundaries of the whole text, word granularity: the only cut points a snippet edge\n// may land on. One rule for every script (DESIGN.md \"Cutting\") -- a regex \\b is ASCII-only and\n// would mis-cut Japanese, Chinese and Thai, which Intl.Segmenter divides by dictionary instead.\nfunction wordBoundaries(text: string): number[] {\n wordSegmenter ??= new Intl.Segmenter('en', { granularity: 'word' });\n const bounds = [0];\n for (const { index, segment } of wordSegmenter.segment(text)) bounds.push(index + segment.length);\n return bounds;\n}\n\n// bounds is sorted: snaps are binary searches, since each runs once per candidate window.\nfunction snapForward(bounds: number[], pos: number): number {\n let lo = 0;\n let hi = bounds.length - 1;\n while (lo < hi) {\n const mid = (lo + hi) >> 1;\n if (bounds[mid] < pos) lo = mid + 1;\n else hi = mid;\n }\n return bounds[lo];\n}\n\nfunction snapBackward(bounds: number[], pos: number): number {\n let lo = 0;\n let hi = bounds.length - 1;\n let result = bounds[0];\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1;\n if (bounds[mid] <= pos) {\n lo = mid;\n result = bounds[mid];\n } else hi = mid - 1;\n }\n return result;\n}\n\nfunction firstBoundAfter(bounds: number[], pos: number): number | undefined {\n let lo = 0;\n let hi = bounds.length;\n while (lo < hi) {\n const mid = (lo + hi) >> 1;\n if (bounds[mid] <= pos) lo = mid + 1;\n else hi = mid;\n }\n return bounds[lo];\n}\n\n// A raw, unmarked upper bound: [start, end) inside [anchor, anchor+charLimit), edges on\n// boundaries. Markup (guillemets, ellipses) added on top of this can push the rendered result\n// past charLimit, which is what fitEnd() below corrects for; this is only the starting guess.\n// A single segment spanning the whole target window (a word longer than the char limit) is the\n// one case where holding the boundary rule means the budget is not a hard ceiling.\nfunction cutWindow(text: string, bounds: number[], anchor: number, charLimit: number): { start: number; end: number } {\n const rawEnd = Math.min(text.length, anchor + charLimit);\n const start = snapForward(bounds, anchor);\n let end = snapBackward(bounds, rawEnd);\n if (end <= start) end = firstBoundAfter(bounds, start) ?? text.length;\n return { start, end: Math.min(end, text.length) };\n}\n\n// The largest boundary <= upperBound, > floor, whose rendered [*, end) fits charLimit. Markup\n// only ever adds characters as end grows, so rendered length is non-decreasing in end and\n// shrinking from upperBound is guaranteed to find the true fit rather than guess at it.\n// `floor` is the one end this never shrinks past (the anchor's own marked word, or the first\n// word in the unmarked fallback): DESIGN.md's budget is a ceiling except here, where returning\n// nothing is worse than a single word that overruns it.\nfunction fitEnd(render: (end: number) => string, bounds: number[], floor: number, upperBound: number, charLimit: number): number {\n let lo = 0;\n let hi = bounds.length - 1;\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1;\n if (bounds[mid] <= upperBound) lo = mid;\n else hi = mid - 1;\n }\n for (; lo >= 0 && bounds[lo] > floor; lo--) {\n if (render(bounds[lo]).length <= charLimit) return bounds[lo];\n }\n return floor;\n}\n\ninterface Span {\n start: number;\n end: number;\n}\n\ninterface SnippetWindow extends Span {\n distinct: number;\n count: number;\n}\n\n// occ is start-sorted: a window's first candidate occurrence is a binary search, not a walk.\nfunction firstOccAtOrAfter(occ: Array<{ start: number; end: number; term: string }>, pos: number): number {\n let lo = 0;\n let hi = occ.length;\n while (lo < hi) {\n const mid = (lo + hi) >> 1;\n if (occ[mid].start < pos) lo = mid + 1;\n else hi = mid;\n }\n return lo;\n}\n\nfunction renderWindow(text: string, occ: Array<{ start: number; end: number; term: string }>, win: Span): string {\n let out = '';\n let cursor = win.start;\n for (let i = firstOccAtOrAfter(occ, win.start); i < occ.length && occ[i].start < win.end; i++) {\n const o = occ[i];\n if (o.end > win.end) continue;\n // Occurrences of duplicate or substring-overlapping terms (\"test tests\") can overlap;\n // emitting each would duplicate document text. Keep the first, absorb the rest.\n if (o.start < cursor) continue;\n out += `${text.slice(cursor, o.start)}«${text.slice(o.start, o.end)}»`;\n cursor = o.end;\n }\n out += text.slice(cursor, win.end);\n const prefix = win.start > 0 ? '…' : '';\n const suffix = win.end < text.length ? '…' : '';\n return `${prefix}${out.replace(/\\s+/g, ' ')}${suffix}`;\n}\n\n// Scores every occurrence-anchored candidate window: most distinct terms wins, ties broken by\n// most total hits -- coverage now that occurrences are real words, not the false-fragment counts\n// a substring scan produced. Selection itself is unevaluated (DESIGN.md \"Selection, unsettled\").\n// The window's `end` is fitted to the *rendered* text, markup included, not the raw span: a\n// window dense with marks (every guillemet pair adds two characters) shrinks to fit the same\n// budget a sparse one fills almost exactly.\nfunction scoreWindows(occ: Array<{ start: number; end: number; term: string }>, bounds: number[], text: string, charLimit: number): SnippetWindow[] {\n const candidates: SnippetWindow[] = [];\n for (let i = 0; i < occ.length; i++) {\n const winEnd = occ[i].start + charLimit;\n const seen = new Set<string>();\n let count = 0;\n for (let j = i; j < occ.length && occ[j].start < winEnd; j++) {\n seen.add(occ[j].term);\n count++;\n }\n const start = snapForward(bounds, occ[i].start);\n const floor = Math.max(start, occ[i].end);\n const upperBound = Math.max(floor, cutWindow(text, bounds, occ[i].start, charLimit).end);\n const end = fitEnd((e) => renderWindow(text, occ, { start, end: e }), bounds, floor, upperBound, charLimit);\n candidates.push({ start, end, distinct: seen.size, count });\n }\n return candidates;\n}\n\nfunction overlaps(a: Span, b: Span): boolean {\n return a.start < b.end && b.start < a.end;\n}\n\n// Same scoring function as a single snippet, kept rather than discarded (DESIGN.md \"Several\n// snippets per note\"): rank by score, take the top non-overlapping windows, then reorder to the\n// note's own order, since several passages from one note read as an abridged note.\nfunction selectWindows(candidates: SnippetWindow[], countLimit: number): { chosen: SnippetWindow[]; best: SnippetWindow } {\n const ranked = candidates.slice().sort((a, b) => b.distinct - a.distinct || b.count - a.count);\n const chosen: SnippetWindow[] = [];\n for (const c of ranked) {\n if (chosen.length >= countLimit) break;\n if (chosen.some((s) => overlaps(s, c))) continue;\n chosen.push(c);\n }\n chosen.sort((a, b) => a.start - b.start);\n return { chosen, best: ranked[0] };\n}\n\n// Builds bounded marked passages and returns the best window's offset for section lookup.\nexport function computeSnippets(text: string, terms: string[], charLimit: number, countLimit: number): { snippets: string[]; offset: number } {\n const occ = findOccurrences(text, terms);\n const bounds = wordBoundaries(text);\n if (occ.length === 0) {\n // A term matched only via title/summary, not this body text, finds no occurrence here;\n // fall back to the doc's start, unmarked.\n const render = (end: number): string => `${text.slice(0, end).replace(/\\s+/g, ' ').trim()}${end < text.length ? '…' : ''}`;\n const floor = bounds.find((b) => b > 0) ?? text.length;\n const upperBound = Math.max(floor, cutWindow(text, bounds, 0, charLimit).end);\n const end = fitEnd(render, bounds, floor, upperBound, charLimit);\n return { snippets: [render(end)], offset: 0 };\n }\n const candidates = scoreWindows(occ, bounds, text, charLimit);\n const { chosen, best } = selectWindows(candidates, countLimit);\n return { snippets: chosen.map((win) => renderWindow(text, occ, win)), offset: best.start };\n}\n\n// Converts an offset in the original text to its one-based line number.\nexport function lineNumberAt(text: string, offset: number): number {\n let line = 1;\n for (let i = 0; i < offset; i++) if (text.charCodeAt(i) === 10) line++;\n return line;\n}\n\nasync function lineRangeFor(store: Store, path: string, line: number): Promise<string | null> {\n 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');\n const row = (await stmt.get(path, line, line)) as { start_line: number; end_line: number } | undefined;\n return row ? `L${row.start_line}-${row.end_line}` : null;\n}\n\n/** Options for the root {@link search} API. */\nexport interface SearchOptions {\n k?: number;\n /** Collects ranking contributions for returned candidates; omitted or false preserves ordinary output. */\n explain?: boolean;\n where?: string; // SQL fragment against frontmatter alias `f`, e.g. \"f.status = 'active'\"\n preset?: string; // named preset; unknown name throws listing declared presets, undefined -> \"default\"\n include?: string[]; // ad hoc scope override (repeatable --include); independent of exclude\n exclude?: string[]; // ad hoc scope override (repeatable --exclude); independent of include\n noExclude?: boolean; // --no-exclude: drop the preset's exclude for this command\n snippetCharLimit?: number; // --snippet-char-limit; defaults to SNIPPET_CHAR_LIMIT_DEFAULT\n snippetCountLimit?: number; // --snippet-count-limit; defaults to SNIPPET_COUNT_LIMIT_DEFAULT\n /** Cancels supported provider I/O and stops between phases after active native work finishes. */\n signal?: AbortSignal;\n}\n\nexport type SearchResult = {\n path: string;\n title: string;\n summary: string;\n snippets: string[];\n via: SearchResultVia;\n score: number;\n lines: string | null;\n similarity?: number | null;\n /** Present only with explain: true, in words/links/vectors order; contributions can differ from via labels. */\n explanation?: SearchSignalContribution[];\n};\n\nexport interface SearchHydrationRow {\n path: string;\n snippets?: string[];\n lines?: string | null;\n}\n\nfunction validateSearchOptions(opts: SearchOptions): void {\n if (opts.explain !== undefined && typeof opts.explain !== 'boolean') {\n throw new SenseError('SEARCH_OPTION_INVALID', `search option \"explain\" must be a boolean, got ${String(opts.explain)}`);\n }\n for (const [name, value] of [\n ['k', opts.k],\n ['snippetCharLimit', opts.snippetCharLimit],\n ['snippetCountLimit', opts.snippetCountLimit],\n ] as const) {\n if (value !== undefined && (!Number.isFinite(value) || !Number.isInteger(value) || value <= 0)) {\n throw new SenseError('SEARCH_OPTION_INVALID', `search option \"${name}\" must be a positive finite integer, got ${String(value)}`);\n }\n }\n}\n\n// Hydrates already-ranked rows with snippets and containing-section ranges without reranking.\nexport async function hydrateSearchRows(store: Store, cfg: ResolvedConfig, rows: SearchHydrationRow[], matchedPaths: Set<string>, terms: string, opts: Pick<SearchOptions, 'snippetCharLimit' | 'snippetCountLimit'> = {}): Promise<void> {\n if (matchedPaths.size === 0) return;\n const bareTerms = extractBareTerms(terms);\n const charLimit = opts.snippetCharLimit ?? SNIPPET_CHAR_LIMIT_DEFAULT;\n const countLimit = opts.snippetCountLimit ?? SNIPPET_COUNT_LIMIT_DEFAULT;\n const sourceStmt = await store.prepare('SELECT text FROM indexed_sources WHERE \"path\" = ?');\n for (const row of rows) {\n if (!matchedPaths.has(row.path)) continue;\n const source = (await sourceStmt.get(row.path)) as { text: string } | undefined;\n if (!source) throw new SenseError('INDEX_NOT_READY', `indexed source text is missing for ${String(row.path)}; run \\`sense build --force\\` (or library build(config, { force: true })) before querying`);\n const text = source.text;\n const { snippets, offset } = computeSnippets(text, bareTerms, charLimit, countLimit);\n row.snippets = snippets;\n if (row.lines == null) row.lines = featureEnabled(cfg, 'sections') ? await lineRangeFor(store, row.path, lineNumberAt(text, offset)) : null;\n }\n}\n\n/**\n * Searches one committed published generation. Provider work precedes the final read transaction;\n * cancellation waits for active native work and transaction cleanup before rejection.\n */\nexport function search(store: Store, cfg: ResolvedConfig, terms: string, opts: SearchOptions = {}): Promise<SearchResult[]> {\n if (opts.signal?.aborted) return Promise.reject(opts.signal.reason);\n return serialQuery(store, () => searchIndexed(store, cfg, terms, opts));\n}\n\ninterface SearchScope {\n signature: string;\n effective: EffectiveSearch;\n allPaths: string[];\n scopePaths: Set<string>;\n allowedPaths: Set<string>;\n semanticEligible: boolean;\n}\n\nasync function readSearchScope(store: Store, cfg: ResolvedConfig, terms: string, opts: SearchOptions): Promise<SearchScope> {\n const signature = await assertQuerySnapshot(store, cfg);\n const effective = resolveSearch(cfg, opts);\n const allPaths = ((await (await store.prepare('SELECT \"path\" FROM frontmatter')).all()) as Array<{ path: string }>).map((r) => r.path);\n const scopePaths = await rawScope(store, cfg, opts, allPaths);\n let allowedPaths: Set<string>;\n try {\n allowedPaths = await narrowByWhere(store, scopePaths, effective.where);\n } catch (err) {\n throw searchError(err as Error, terms, effective.where);\n }\n const wantsVectors = effective.signals.vectors !== undefined;\n if (wantsVectors) await ensureDocumentEmbeddings(store, cfg, allowedPaths);\n const semanticEligible = wantsVectors && (await scopeHasEmbeddings(store, cfg, allowedPaths));\n return { signature, effective, allPaths, scopePaths, allowedPaths, semanticEligible };\n}\n\ninterface SemanticPreparationNeeded {\n signature: string;\n}\n\nfunction needsSemanticPreparation(result: SearchResult[] | SemanticPreparationNeeded): result is SemanticPreparationNeeded {\n return !Array.isArray(result);\n}\n\nasync function searchIndexed(store: Store, cfg: ResolvedConfig, terms: string, opts: SearchOptions): Promise<SearchResult[]> {\n validateSearchOptions(opts);\n const initial = resolveSearch(cfg, opts);\n opts.signal?.throwIfAborted();\n // A downloadable HF id proceeds -- getProvider fetches it lazily on consent. Only a\n // local path with missing files errors here, since nothing will ever fetch it for itself.\n const wantsVectors = initial.signals.vectors !== undefined;\n if (wantsVectors) {\n const e = embedConfig(cfg); // validate.ts guarantees this is set whenever \"vectors\" is declared\n if (localModelMissing(e)) {\n throw new SenseError('EMBED_MODEL_MISSING', `preset \"${initial.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`);\n }\n }\n\n let prepared: PreparedSemanticQuery | undefined;\n let preparedSignature: string | undefined;\n if (wantsVectors) {\n // This first snapshot only decides whether a semantic query can contribute. It closes before\n // provider/model work, and the complete scope/readiness decision is repeated below.\n const preflight = await store.transaction(() => readSearchScope(store, cfg, terms, opts));\n opts.signal?.throwIfAborted();\n if (preflight.semanticEligible) {\n preparedSignature = preflight.signature;\n prepared = await prepareSemanticQuery(cfg, terms, { signal: opts.signal });\n opts.signal?.throwIfAborted();\n }\n }\n\n opts.signal?.throwIfAborted();\n let result = await store.transaction(() => searchSnapshot(store, cfg, terms, opts, prepared, preparedSignature));\n opts.signal?.throwIfAborted();\n if (!needsSemanticPreparation(result)) return result;\n\n // The scope became vector-eligible after an empty/ineligible preflight. Leave that snapshot,\n // prepare once, then take a fresh final snapshot rather than waiting on the provider inside it.\n preparedSignature = result.signature;\n prepared = await prepareSemanticQuery(cfg, terms, { signal: opts.signal });\n opts.signal?.throwIfAborted();\n result = await store.transaction(() => searchSnapshot(store, cfg, terms, opts, prepared, preparedSignature));\n opts.signal?.throwIfAborted();\n if (needsSemanticPreparation(result)) throw new SenseError('INDEX_NOT_READY', 'the semantic search scope changed while its query was being prepared; retry the search against a stable published index');\n return result;\n}\n\nasync function searchSnapshot(store: Store, cfg: ResolvedConfig, terms: string, opts: SearchOptions, prepared: PreparedSemanticQuery | undefined, preparedSignature: string | undefined): Promise<SearchResult[] | SemanticPreparationNeeded> {\n const { signature, effective, allPaths, scopePaths, allowedPaths, semanticEligible } = await readSearchScope(store, cfg, terms, opts);\n if (prepared !== undefined && signature !== preparedSignature) {\n throw new SenseError('INDEX_NOT_READY', 'the index configuration changed while the semantic query was being prepared; retry after the current build finishes, or rebuild the index for this configuration');\n }\n if (semanticEligible && prepared === undefined) return { signature };\n\n const { k, signals } = effective;\n const fetch = Math.max(k * 3, 30);\n const scopeActive = scopePaths.size < allPaths.length;\n const semanticEnabled = semanticEligible && prepared !== undefined;\n\n // --where applies inside the candidate query (a post-filter would drop matches ranked past\n // the pool) and again on the final select, for link-derived rows.\n const scope = effective.where;\n const whereJoin = scope ? `JOIN frontmatter f ON f.\"path\" = content.path` : '';\n const whereCond = scope ? `AND (${scope})` : '';\n // Filtered before LIMIT, not after, or scoped notes ranking below the global top-`fetch`\n // never reach the filter; joined against a temp table since real scopes exceed SQLITE_MAX_VARIABLE_NUMBER.\n if (scopeActive) await materializeScope(store, '_search_scope', scopePaths);\n const scopeCond = scopeActive ? `AND content.path IN (SELECT \"path\" FROM _search_scope)` : '';\n\n const candidates: Candidates = new Map();\n let matchRows: LexicalHit[] = [];\n const hasEmptyPhrase = [...terms.matchAll(/\"([^\"]*)\"/g)].some((match) => !/[\\p{L}\\p{N}_]/u.test(match[1]));\n const hasBareSyntaxOnly = /[()]/u.test(terms.replace(/\"[^\"]*\"/g, ' '));\n // A query that is only whitespace or punctuation has no lexical rows; other declared signals\n // compose normally. In particular, unspaced-script punctuation can rewrite to an empty MATCH.\n if (signals.words !== undefined && terms.trim() !== '' && (/[\\p{L}\\p{N}_]/u.test(terms) || hasBareSyntaxOnly) && !hasEmptyPhrase) {\n const syntaxError = bareTermSyntaxError(terms);\n if (syntaxError) throw syntaxError;\n try {\n matchRows = await wordsCandidates(store, candidates, terms, whereJoin, whereCond, scopeCond, fetch, signals.words, opts.explain);\n } catch (err) {\n throw searchError(err as Error, terms, scope);\n }\n }\n // Every store's lexical `snippets` is NULL; the snippet text is always computed below.\n // Distinguish from via='link' rows, which never appear in matchRows.\n const matchedPaths = new Set(matchRows.map((r) => r.path));\n\n if (signals.links !== undefined) await linksCandidates(store, candidates, matchRows, allPaths, allowedPaths, fetch, signals.links, opts.explain);\n\n let chunkLines = new Map<string, string>();\n let chunkSimilarity = new Map<string, number>();\n if (semanticEnabled && signals.vectors !== undefined && prepared !== undefined) {\n ({ chunkLines, chunkSimilarity } = await vectorsCandidates(store, prepared, candidates, fetch, allowedPaths, allPaths.length, signals.vectors, opts.explain));\n }\n\n await store.exec('DROP TABLE IF EXISTS _search');\n // DOUBLE, not REAL: sqlite's REAL is an 8-byte double but duckdb's is a 4-byte float, and the\n // rounded score/similarity are printed at full precision in rows.\n await store.exec('CREATE TEMP TABLE _search (\"path\" TEXT PRIMARY KEY, score DOUBLE, via TEXT, lines TEXT, similarity DOUBLE)');\n if (candidates.size > 0) {\n await store.runBatch(\n 'INSERT INTO _search (\"path\", score, via, lines, similarity) VALUES (?, ?, ?, ?, ?)',\n [...candidates].map(([path, c]) => [path, c.score, c.via, chunkLines.get(path) ?? null, chunkSimilarity.get(path) ?? null])\n );\n }\n\n // Reapplies --where even though _search is already scope+where filtered, since the join to\n // frontmatter is already needed for the path column.\n const where = scope ? `WHERE (${scope})` : '';\n // lines: semantic rows carry their chunk's range, lexical rows gain one below,\n // everything else stays null; similarity stays semantic-only.\n const similarityCol = semanticEnabled ? ', _search.similarity' : '';\n // NULL AS snippets holds this column's position (between summary and via) for every row;\n // overwritten below, always to an array, never left null (DESIGN.md).\n const selectStmt = await store.prepare(\n `SELECT f.\"path\" AS path, content.title, content.summary, NULL AS snippets, _search.via, round(_search.score, 4) AS score, _search.lines${similarityCol}\n FROM _search JOIN frontmatter f ON f.\"path\" = _search.\"path\" JOIN content ON content.path = _search.\"path\"\n ${where} ORDER BY _search.score DESC, _search.\"path\" ASC LIMIT ?`\n );\n const rows = (await selectStmt.all(k)) as SearchResult[];\n if (searchCandidatesRead.hasSubscribers) searchCandidatesRead.publish({ paths: rows.map((row) => row.path) });\n for (const row of rows) row.snippets = [];\n\n await hydrateSearchRows(store, cfg, rows, matchedPaths, terms, opts);\n if (opts.explain) {\n for (const row of rows) row.explanation = candidates.get(row.path)?.explanation ?? [];\n }\n\n return rows;\n}\n"],"names":["channel","stemmer","embedConfig","featureEnabled","resolveSearch","ensureDocumentEmbeddings","prepareSemanticQuery","localModelMissing","MODEL_FILENAMES","SenseError","serialQuery","bareTermSyntaxError","searchError","foldForSearch","hasUnspacedRun","searchTokens","unspacedRuns","materializeScope","narrowByWhere","rawScope","scopeHasEmbeddings","linksCandidates","vectorsCandidates","wordsCandidates","assertQuerySnapshot","SNIPPET_CHAR_LIMIT_DEFAULT","SNIPPET_COUNT_LIMIT_DEFAULT","searchCandidatesRead","extractBareTerms","query","cleaned","replace","split","map","tok","flatMap","text","toLowerCase","trim","filter","length","wordSegmenter","findOccurrences","terms","occ","textLower","unspacedTerms","run","runLower","slice","start","end","term","idx","found","indexOf","push","spacedTerms","t","termByStem","Map","stemInitials","Set","Array","from","keys","match","segment","unspaced","folded","has","get","undefined","sort","a","b","wordBoundaries","Intl","Segmenter","granularity","bounds","index","snapForward","pos","lo","hi","mid","snapBackward","result","firstBoundAfter","cutWindow","anchor","charLimit","rawEnd","Math","min","fitEnd","render","floor","upperBound","firstOccAtOrAfter","renderWindow","win","out","cursor","i","o","prefix","suffix","scoreWindows","candidates","winEnd","seen","count","j","add","max","e","distinct","size","overlaps","selectWindows","countLimit","ranked","chosen","c","some","s","best","computeSnippets","find","snippets","offset","lineNumberAt","line","charCodeAt","lineRangeFor","store","path","stmt","prepare","row","start_line","end_line","validateSearchOptions","opts","explain","String","name","value","k","snippetCharLimit","snippetCountLimit","Number","isFinite","isInteger","hydrateSearchRows","cfg","rows","matchedPaths","bareTerms","sourceStmt","source","lines","search","signal","aborted","Promise","reject","reason","searchIndexed","readSearchScope","signature","effective","allPaths","all","r","scopePaths","allowedPaths","where","err","wantsVectors","signals","vectors","semanticEligible","needsSemanticPreparation","isArray","initial","throwIfAborted","presetName","model","prepared","preparedSignature","preflight","transaction","searchSnapshot","fetch","scopeActive","semanticEnabled","scope","whereJoin","whereCond","scopeCond","matchRows","hasEmptyPhrase","matchAll","test","hasBareSyntaxOnly","words","syntaxError","links","chunkLines","chunkSimilarity","exec","runBatch","score","via","similarityCol","selectStmt","hasSubscribers","publish","paths","explanation"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/open-source/ai/sensemaking/src/commands/search.ts"],"sourcesContent":["import { channel } from 'node:diagnostics_channel';\nimport { stemmer } from 'stemmer';\nimport type { EffectiveSearch, ResolvedConfig } from '../config/index.ts';\nimport { embedConfig, featureEnabled, resolveSearch } from '../config/index.ts';\nimport type { PreparedSemanticQuery } from '../embed/query.ts';\nimport { ensureDocumentEmbeddings, prepareSemanticQuery } from '../embed/query.ts';\nimport { localModelMissing, MODEL_FILENAMES } from '../embed/store.ts';\nimport { SenseError } from '../errors.ts';\nimport { serialQuery } from '../lib/serial-query.ts';\nimport { bareTermSyntaxError, searchError } from '../output/search-error.ts';\nimport type { LexicalHit, Store } from '../store/types.ts';\nimport { foldForSearch, hasUnspacedRun, searchTokens, unspacedRuns } from '../text/segment.ts';\nimport { materializeScope, narrowByWhere, rawScope, scopeHasEmbeddings } from './scope.ts';\nimport type { Candidates, SearchResultVia, SearchSignalContribution } from './signals.ts';\nimport { linksCandidates, vectorsCandidates, wordsCandidates } from './signals.ts';\nimport { assertQuerySnapshot } from './snapshot.ts';\n\nexport type { SearchResultVia, SearchSignalContribution } from './signals.ts';\n\n// Default budget behind --snippet-char-limit/--snippet-count-limit: the limit is whatever the\n// caller passes, these only say what happens when they pass nothing (DESIGN.md \"Search snippets\").\nexport const SNIPPET_CHAR_LIMIT_DEFAULT = 80;\nexport const SNIPPET_COUNT_LIMIT_DEFAULT = 1;\n\nconst searchCandidatesRead = channel('sensemaking.search.candidates-read');\n\n// Bare terms from an FTS5 query string: strips operators/quoting, not real terms, so the\n// snippet scan matches what the query matched -- FTS5 operators are uppercase-only, so this is too.\nfunction extractBareTerms(query: string): string[] {\n const cleaned = query\n .replace(/\"/g, ' ')\n .replace(/[()*]/g, ' ')\n .replace(/\\b(AND|OR|NOT|NEAR)\\b(\\/\\d+)?/g, ' ');\n return cleaned\n .split(/\\s+/)\n .map((tok) => tok.replace(/^[A-Za-z_]\\w*:/, '')) // column filter, e.g. title:term\n .flatMap((tok) => searchTokens(tok).map(({ text }) => text))\n .map((tok) => tok.toLowerCase().trim())\n .filter((tok) => tok.length > 0);\n}\n\n// House convention (src/chunk/group.ts): Intl.Segmenter word boundaries, not a bare \\b, which\n// is ASCII-only. Built on first use: construction is a few ms, paid only when a term matches.\nlet wordSegmenter: Intl.Segmenter | undefined;\n\nfunction findOccurrences(text: string, terms: string[]): Array<{ start: number; end: number; term: string }> {\n const occ: Array<{ start: number; end: number; term: string }> = [];\n const textLower = text.toLowerCase();\n\n // Unspaced scripts (UNSPACED_SCRIPTS) mark no word boundaries: the sense-setup skill promises\n // substring semantics there, so a matching term is found wherever it occurs in the run.\n const unspacedTerms = terms.filter(hasUnspacedRun);\n if (unspacedTerms.length > 0) {\n for (const run of unspacedRuns(text)) {\n const runLower = textLower.slice(run.start, run.end);\n for (const term of unspacedTerms) {\n let idx = 0;\n for (;;) {\n const found = runLower.indexOf(term, idx);\n if (found === -1) break;\n occ.push({ start: run.start + found, end: run.start + found + term.length, term });\n idx = found + term.length;\n }\n }\n }\n }\n\n // Spaced scripts: a word is marked when its porter stem equals a query term's stem, the same\n // algorithm both indexes tokenize with (sqlite's TOKENIZE = 'porter unicode61'; duckdb's fts default).\n const spacedTerms = terms.filter((t) => !hasUnspacedRun(t));\n if (spacedTerms.length > 0) {\n const termByStem = new Map(spacedTerms.map((t) => [stemmer(foldForSearch(t)), t]));\n const stemInitials = new Set(Array.from(termByStem.keys(), (term) => term[0]));\n for (const match of searchTokens(text)) {\n const segment = match.text;\n if (match.unspaced) continue;\n const folded = foldForSearch(segment);\n // Porter stemming changes suffixes only, so a different initial cannot match a query stem.\n if (!stemInitials.has(folded[0])) continue;\n const term = termByStem.get(stemmer(folded));\n if (term !== undefined) occ.push({ start: match.start, end: match.end, term });\n }\n }\n\n return occ.sort((a, b) => a.start - b.start || b.end - a.end);\n}\n\n// Segment boundaries of the whole text, word granularity: the only cut points a snippet edge\n// may land on. One rule for every script (DESIGN.md \"Cutting\") -- a regex \\b is ASCII-only and\n// would mis-cut Japanese, Chinese and Thai, which Intl.Segmenter divides by dictionary instead.\nfunction wordBoundaries(text: string): number[] {\n wordSegmenter ??= new Intl.Segmenter('en', { granularity: 'word' });\n const bounds = [0];\n for (const { index, segment } of wordSegmenter.segment(text)) bounds.push(index + segment.length);\n return bounds;\n}\n\n// bounds is sorted: snaps are binary searches, since each runs once per candidate window.\nfunction snapForward(bounds: number[], pos: number): number {\n let lo = 0;\n let hi = bounds.length - 1;\n while (lo < hi) {\n const mid = (lo + hi) >> 1;\n if (bounds[mid] < pos) lo = mid + 1;\n else hi = mid;\n }\n return bounds[lo];\n}\n\nfunction snapBackward(bounds: number[], pos: number): number {\n let lo = 0;\n let hi = bounds.length - 1;\n let result = bounds[0];\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1;\n if (bounds[mid] <= pos) {\n lo = mid;\n result = bounds[mid];\n } else hi = mid - 1;\n }\n return result;\n}\n\nfunction firstBoundAfter(bounds: number[], pos: number): number | undefined {\n let lo = 0;\n let hi = bounds.length;\n while (lo < hi) {\n const mid = (lo + hi) >> 1;\n if (bounds[mid] <= pos) lo = mid + 1;\n else hi = mid;\n }\n return bounds[lo];\n}\n\n// A raw, unmarked upper bound: [start, end) inside [anchor, anchor+charLimit), edges on\n// boundaries. Markup (guillemets, ellipses) added on top of this can push the rendered result\n// past charLimit, which is what fitEnd() below corrects for; this is only the starting guess.\n// A single segment spanning the whole target window (a word longer than the char limit) is the\n// one case where holding the boundary rule means the budget is not a hard ceiling.\nfunction cutWindow(text: string, bounds: number[], anchor: number, charLimit: number): { start: number; end: number } {\n const rawEnd = Math.min(text.length, anchor + charLimit);\n const start = snapForward(bounds, anchor);\n let end = snapBackward(bounds, rawEnd);\n if (end <= start) end = firstBoundAfter(bounds, start) ?? text.length;\n return { start, end: Math.min(end, text.length) };\n}\n\n// The largest boundary <= upperBound, > floor, whose rendered [*, end) fits charLimit. Markup\n// only ever adds characters as end grows, so rendered length is non-decreasing in end and\n// shrinking from upperBound is guaranteed to find the true fit rather than guess at it.\n// `floor` is the one end this never shrinks past (the anchor's own marked word, or the first\n// word in the unmarked fallback): DESIGN.md's budget is a ceiling except here, where returning\n// nothing is worse than a single word that overruns it.\nfunction fitEnd(render: (end: number) => string, bounds: number[], floor: number, upperBound: number, charLimit: number): number {\n let lo = 0;\n let hi = bounds.length - 1;\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1;\n if (bounds[mid] <= upperBound) lo = mid;\n else hi = mid - 1;\n }\n for (; lo >= 0 && bounds[lo] > floor; lo--) {\n if (render(bounds[lo]).length <= charLimit) return bounds[lo];\n }\n return floor;\n}\n\ninterface Span {\n start: number;\n end: number;\n}\n\ninterface SnippetWindow extends Span {\n distinct: number;\n count: number;\n}\n\n// occ is start-sorted: a window's first candidate occurrence is a binary search, not a walk.\nfunction firstOccAtOrAfter(occ: Array<{ start: number; end: number; term: string }>, pos: number): number {\n let lo = 0;\n let hi = occ.length;\n while (lo < hi) {\n const mid = (lo + hi) >> 1;\n if (occ[mid].start < pos) lo = mid + 1;\n else hi = mid;\n }\n return lo;\n}\n\nfunction renderWindow(text: string, occ: Array<{ start: number; end: number; term: string }>, win: Span): string {\n let out = '';\n let cursor = win.start;\n for (let i = firstOccAtOrAfter(occ, win.start); i < occ.length && occ[i].start < win.end; i++) {\n const o = occ[i];\n if (o.end > win.end) continue;\n // Occurrences of duplicate or substring-overlapping terms (\"test tests\") can overlap;\n // emitting each would duplicate document text. Keep the first, absorb the rest.\n if (o.start < cursor) continue;\n out += `${text.slice(cursor, o.start)}«${text.slice(o.start, o.end)}»`;\n cursor = o.end;\n }\n out += text.slice(cursor, win.end);\n const prefix = win.start > 0 ? '…' : '';\n const suffix = win.end < text.length ? '…' : '';\n return `${prefix}${out.replace(/\\s+/g, ' ')}${suffix}`;\n}\n\n// Scores every occurrence-anchored candidate window: most distinct terms wins, ties broken by\n// most total hits -- coverage now that occurrences are real words, not the false-fragment counts\n// a substring scan produced. Selection itself is unevaluated (DESIGN.md \"Selection, unsettled\").\n// The window's `end` is fitted to the *rendered* text, markup included, not the raw span: a\n// window dense with marks (every guillemet pair adds two characters) shrinks to fit the same\n// budget a sparse one fills almost exactly.\nfunction scoreWindows(occ: Array<{ start: number; end: number; term: string }>, bounds: number[], text: string, charLimit: number): SnippetWindow[] {\n const candidates: SnippetWindow[] = [];\n for (let i = 0; i < occ.length; i++) {\n const winEnd = occ[i].start + charLimit;\n const seen = new Set<string>();\n let count = 0;\n for (let j = i; j < occ.length && occ[j].start < winEnd; j++) {\n seen.add(occ[j].term);\n count++;\n }\n const start = snapForward(bounds, occ[i].start);\n const floor = Math.max(start, occ[i].end);\n const upperBound = Math.max(floor, cutWindow(text, bounds, occ[i].start, charLimit).end);\n const end = fitEnd((e) => renderWindow(text, occ, { start, end: e }), bounds, floor, upperBound, charLimit);\n candidates.push({ start, end, distinct: seen.size, count });\n }\n return candidates;\n}\n\nfunction overlaps(a: Span, b: Span): boolean {\n return a.start < b.end && b.start < a.end;\n}\n\n// Same scoring function as a single snippet, kept rather than discarded (DESIGN.md \"Several\n// snippets per note\"): rank by score, take the top non-overlapping windows, then reorder to the\n// note's own order, since several passages from one note read as an abridged note.\nfunction selectWindows(candidates: SnippetWindow[], countLimit: number): { chosen: SnippetWindow[]; best: SnippetWindow } {\n const ranked = candidates.slice().sort((a, b) => b.distinct - a.distinct || b.count - a.count);\n const chosen: SnippetWindow[] = [];\n for (const c of ranked) {\n if (chosen.length >= countLimit) break;\n if (chosen.some((s) => overlaps(s, c))) continue;\n chosen.push(c);\n }\n chosen.sort((a, b) => a.start - b.start);\n return { chosen, best: ranked[0] };\n}\n\n// Builds bounded marked passages and returns the best window's offset for section lookup.\nexport function computeSnippets(text: string, terms: string[], charLimit: number, countLimit: number): { snippets: string[]; offset: number } {\n const occ = findOccurrences(text, terms);\n const bounds = wordBoundaries(text);\n if (occ.length === 0) {\n // A term matched only via title/summary, not this body text, finds no occurrence here;\n // fall back to the doc's start, unmarked.\n const render = (end: number): string => `${text.slice(0, end).replace(/\\s+/g, ' ').trim()}${end < text.length ? '…' : ''}`;\n const floor = bounds.find((b) => b > 0) ?? text.length;\n const upperBound = Math.max(floor, cutWindow(text, bounds, 0, charLimit).end);\n const end = fitEnd(render, bounds, floor, upperBound, charLimit);\n return { snippets: [render(end)], offset: 0 };\n }\n const candidates = scoreWindows(occ, bounds, text, charLimit);\n const { chosen, best } = selectWindows(candidates, countLimit);\n return { snippets: chosen.map((win) => renderWindow(text, occ, win)), offset: best.start };\n}\n\n// Converts an offset in the original text to its one-based line number.\nexport function lineNumberAt(text: string, offset: number): number {\n let line = 1;\n for (let i = 0; i < offset; i++) if (text.charCodeAt(i) === 10) line++;\n return line;\n}\n\nasync function lineRangeFor(store: Store, path: string, line: number): Promise<string | null> {\n 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');\n const row = (await stmt.get(path, line, line)) as { start_line: number; end_line: number } | undefined;\n return row ? `L${row.start_line}-${row.end_line}` : null;\n}\n\n/** Options for the root {@link search} API. */\nexport interface SearchOptions {\n k?: number;\n /** Collects ranking contributions for returned candidates; omitted or false preserves ordinary output. */\n explain?: boolean;\n where?: string; // SQL fragment against frontmatter alias `f`, e.g. \"f.status = 'active'\"\n preset?: string; // named preset; unknown name throws listing declared presets, undefined -> \"default\"\n include?: string[]; // ad hoc scope override (repeatable --include); independent of exclude\n exclude?: string[]; // ad hoc scope override (repeatable --exclude); independent of include\n noExclude?: boolean; // --no-exclude: drop the preset's exclude for this command\n snippetCharLimit?: number; // --snippet-char-limit; defaults to SNIPPET_CHAR_LIMIT_DEFAULT\n snippetCountLimit?: number; // --snippet-count-limit; defaults to SNIPPET_COUNT_LIMIT_DEFAULT\n /** Cancels supported provider I/O and stops between phases after active native work finishes. */\n signal?: AbortSignal;\n}\n\nexport type SearchResult = {\n path: string;\n title: string;\n summary: string;\n snippets: string[];\n via: SearchResultVia;\n score: number;\n lines: string | null;\n similarity?: number | null;\n /** Present only with explain: true, in words/links/vectors order; contributions can differ from via labels. */\n explanation?: SearchSignalContribution[];\n};\n\nexport interface SearchHydrationRow {\n path: string;\n snippets?: string[];\n lines?: string | null;\n}\n\nfunction validateSearchOptions(opts: SearchOptions): void {\n if (opts.explain !== undefined && typeof opts.explain !== 'boolean') {\n throw new SenseError('SEARCH_OPTION_INVALID', `search option \"explain\" must be a boolean, got ${String(opts.explain)}`);\n }\n for (const [name, value] of [\n ['k', opts.k],\n ['snippetCharLimit', opts.snippetCharLimit],\n ['snippetCountLimit', opts.snippetCountLimit],\n ] as const) {\n if (value !== undefined && (!Number.isFinite(value) || !Number.isInteger(value) || value <= 0)) {\n throw new SenseError('SEARCH_OPTION_INVALID', `search option \"${name}\" must be a positive finite integer, got ${String(value)}`);\n }\n }\n}\n\n// Hydrates already-ranked rows with snippets and containing-section ranges without reranking.\nexport async function hydrateSearchRows(store: Store, cfg: ResolvedConfig, rows: SearchHydrationRow[], matchedPaths: Set<string>, terms: string, opts: Pick<SearchOptions, 'snippetCharLimit' | 'snippetCountLimit'> = {}): Promise<void> {\n if (matchedPaths.size === 0) return;\n const bareTerms = extractBareTerms(terms);\n const charLimit = opts.snippetCharLimit ?? SNIPPET_CHAR_LIMIT_DEFAULT;\n const countLimit = opts.snippetCountLimit ?? SNIPPET_COUNT_LIMIT_DEFAULT;\n const sourceStmt = await store.prepare('SELECT text FROM indexed_sources WHERE \"path\" = ?');\n for (const row of rows) {\n if (!matchedPaths.has(row.path)) continue;\n const source = (await sourceStmt.get(row.path)) as { text: string } | undefined;\n if (!source) throw new SenseError('INDEX_NOT_READY', `indexed source text is missing for ${String(row.path)}; run \\`sense build --force\\` (or library build(config, { force: true })) before querying`);\n const text = source.text;\n const { snippets, offset } = computeSnippets(text, bareTerms, charLimit, countLimit);\n row.snippets = snippets;\n if (row.lines == null) row.lines = featureEnabled(cfg, 'sections') ? await lineRangeFor(store, row.path, lineNumberAt(text, offset)) : null;\n }\n}\n\n/**\n * Searches one committed published generation. Provider work precedes the final read transaction;\n * cancellation waits for active native work and transaction cleanup before rejection.\n */\nexport function search(store: Store, cfg: ResolvedConfig, terms: string, opts: SearchOptions = {}): Promise<SearchResult[]> {\n if (opts.signal?.aborted) return Promise.reject(opts.signal.reason);\n return serialQuery(store, () => searchIndexed(store, cfg, terms, opts));\n}\n\ninterface SearchScope {\n signature: string;\n effective: EffectiveSearch;\n allPaths: string[];\n scopePaths: Set<string>;\n allowedPaths: Set<string>;\n semanticEligible: boolean;\n}\n\nasync function readSearchScope(store: Store, cfg: ResolvedConfig, terms: string, opts: SearchOptions): Promise<SearchScope> {\n const signature = await assertQuerySnapshot(store, cfg);\n const effective = resolveSearch(cfg, opts);\n const allPaths = ((await (await store.prepare('SELECT \"path\" FROM frontmatter')).all()) as Array<{ path: string }>).map((r) => r.path);\n const scopePaths = await rawScope(store, cfg, opts, allPaths);\n let allowedPaths: Set<string>;\n try {\n allowedPaths = await narrowByWhere(store, scopePaths, effective.where);\n } catch (err) {\n throw searchError(err as Error, terms, effective.where);\n }\n const wantsVectors = effective.signals.vectors !== undefined;\n if (wantsVectors) await ensureDocumentEmbeddings(store, cfg, allowedPaths);\n const semanticEligible = wantsVectors && (await scopeHasEmbeddings(store, cfg, allowedPaths));\n return { signature, effective, allPaths, scopePaths, allowedPaths, semanticEligible };\n}\n\ninterface SemanticPreparationNeeded {\n signature: string;\n}\n\nfunction needsSemanticPreparation(result: SearchResult[] | SemanticPreparationNeeded): result is SemanticPreparationNeeded {\n return !Array.isArray(result);\n}\n\nasync function searchIndexed(store: Store, cfg: ResolvedConfig, terms: string, opts: SearchOptions): Promise<SearchResult[]> {\n validateSearchOptions(opts);\n const initial = resolveSearch(cfg, opts);\n opts.signal?.throwIfAborted();\n // A downloadable HF id proceeds -- getProvider fetches it lazily on consent. Only a\n // local path with missing files errors here, since nothing will ever fetch it for itself.\n const wantsVectors = initial.signals.vectors !== undefined;\n if (wantsVectors) {\n const e = embedConfig(cfg); // validate.ts guarantees this is set whenever \"vectors\" is declared\n if (localModelMissing(e)) {\n throw new SenseError('EMBED_MODEL_MISSING', `preset \"${initial.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`);\n }\n }\n\n let prepared: PreparedSemanticQuery | undefined;\n let preparedSignature: string | undefined;\n if (wantsVectors) {\n // This first snapshot only decides whether a semantic query can contribute. It closes before\n // provider/model work, and the complete scope/readiness decision is repeated below.\n const preflight = await store.transaction(() => readSearchScope(store, cfg, terms, opts));\n opts.signal?.throwIfAborted();\n if (preflight.semanticEligible) {\n preparedSignature = preflight.signature;\n prepared = await prepareSemanticQuery(cfg, terms, { signal: opts.signal });\n opts.signal?.throwIfAborted();\n }\n }\n\n opts.signal?.throwIfAborted();\n let result = await store.transaction(() => searchSnapshot(store, cfg, terms, opts, prepared, preparedSignature));\n opts.signal?.throwIfAborted();\n if (!needsSemanticPreparation(result)) return result;\n\n // The scope became vector-eligible after an empty/ineligible preflight. Leave that snapshot,\n // prepare once, then take a fresh final snapshot rather than waiting on the provider inside it.\n preparedSignature = result.signature;\n prepared = await prepareSemanticQuery(cfg, terms, { signal: opts.signal });\n opts.signal?.throwIfAborted();\n result = await store.transaction(() => searchSnapshot(store, cfg, terms, opts, prepared, preparedSignature));\n opts.signal?.throwIfAborted();\n if (needsSemanticPreparation(result)) throw new SenseError('INDEX_NOT_READY', 'the semantic search scope changed while its query was being prepared; retry the search against a stable published index');\n return result;\n}\n\nasync function searchSnapshot(store: Store, cfg: ResolvedConfig, terms: string, opts: SearchOptions, prepared: PreparedSemanticQuery | undefined, preparedSignature: string | undefined): Promise<SearchResult[] | SemanticPreparationNeeded> {\n const { signature, effective, allPaths, scopePaths, allowedPaths, semanticEligible } = await readSearchScope(store, cfg, terms, opts);\n if (prepared !== undefined && signature !== preparedSignature) {\n throw new SenseError('INDEX_NOT_READY', 'the index configuration changed while the semantic query was being prepared; retry after the current build finishes, or rebuild the index for this configuration');\n }\n if (semanticEligible && prepared === undefined) return { signature };\n\n const { k, signals } = effective;\n const fetch = Math.max(k * 3, 30);\n const scopeActive = scopePaths.size < allPaths.length;\n const semanticEnabled = semanticEligible && prepared !== undefined;\n\n // --where applies inside the candidate query (a post-filter would drop matches ranked past\n // the pool) and again on the final select, for link-derived rows.\n const scope = effective.where;\n const whereJoin = scope ? `JOIN frontmatter f ON f.\"path\" = content.path` : '';\n const whereCond = scope ? `AND (${scope})` : '';\n // Filtered before LIMIT, not after, or scoped notes ranking below the global top-`fetch`\n // never reach the filter; joined against a temp table since real scopes exceed SQLITE_MAX_VARIABLE_NUMBER.\n if (scopeActive) await materializeScope(store, '_search_scope', scopePaths);\n const scopeCond = scopeActive ? `AND content.path IN (SELECT \"path\" FROM _search_scope)` : '';\n\n const candidates: Candidates = new Map();\n let matchRows: LexicalHit[] = [];\n const hasEmptyPhrase = [...terms.matchAll(/\"([^\"]*)\"/g)].some((match) => !/[\\p{L}\\p{N}_]/u.test(match[1]));\n const hasBareSyntaxOnly = /[()]/u.test(terms.replace(/\"[^\"]*\"/g, ' '));\n // A query that is only whitespace or punctuation has no lexical rows; other declared signals\n // compose normally. In particular, unspaced-script punctuation can rewrite to an empty MATCH.\n if (signals.words !== undefined && terms.trim() !== '' && (/[\\p{L}\\p{N}_]/u.test(terms) || hasBareSyntaxOnly) && !hasEmptyPhrase) {\n const syntaxError = bareTermSyntaxError(terms);\n if (syntaxError) throw syntaxError;\n try {\n matchRows = await wordsCandidates(store, candidates, terms, whereJoin, whereCond, scopeCond, fetch, signals.words, opts.explain);\n } catch (err) {\n throw searchError(err as Error, terms, scope);\n }\n }\n // Every store's lexical `snippets` is NULL; the snippet text is always computed below.\n // Distinguish from via='link' rows, which never appear in matchRows.\n const matchedPaths = new Set(matchRows.map((r) => r.path));\n\n if (signals.links !== undefined) await linksCandidates(store, candidates, matchRows, allPaths, allowedPaths, fetch, signals.links, opts.explain);\n\n let chunkLines = new Map<string, string>();\n let chunkSimilarity = new Map<string, number>();\n if (semanticEnabled && signals.vectors !== undefined && prepared !== undefined) {\n ({ chunkLines, chunkSimilarity } = await vectorsCandidates(store, prepared, candidates, fetch, allowedPaths, allPaths.length, signals.vectors, opts.explain));\n }\n\n await store.exec('DROP TABLE IF EXISTS _search');\n // DOUBLE, not REAL: sqlite's REAL is an 8-byte double but duckdb's is a 4-byte float, and the\n // rounded score/similarity are printed at full precision in rows.\n await store.exec('CREATE TEMP TABLE _search (\"path\" TEXT PRIMARY KEY, score DOUBLE, via TEXT, lines TEXT, similarity DOUBLE)');\n if (candidates.size > 0) {\n await store.runBatch(\n 'INSERT INTO _search (\"path\", score, via, lines, similarity) VALUES (?, ?, ?, ?, ?)',\n [...candidates].map(([path, c]) => [path, c.score, c.via, chunkLines.get(path) ?? null, chunkSimilarity.get(path) ?? null])\n );\n }\n\n // Reapplies --where even though _search is already scope+where filtered, since the join to\n // frontmatter is already needed for the path column.\n const where = scope ? `WHERE (${scope})` : '';\n // lines: semantic rows carry their chunk's range, lexical rows gain one below,\n // everything else stays null; similarity stays semantic-only.\n const similarityCol = semanticEnabled ? ', _search.similarity' : '';\n // NULL AS snippets holds this column's position (between summary and via) for every row;\n // overwritten below, always to an array, never left null (DESIGN.md).\n const selectStmt = await store.prepare(\n `SELECT f.\"path\" AS path, content.title, content.summary, NULL AS snippets, _search.via, round(_search.score, 4) AS score, _search.lines${similarityCol}\n FROM _search JOIN frontmatter f ON f.\"path\" = _search.\"path\" JOIN content ON content.path = _search.\"path\"\n ${where} ORDER BY _search.score DESC, _search.\"path\" ASC LIMIT ?`\n );\n const rows = (await selectStmt.all(k)) as SearchResult[];\n if (searchCandidatesRead.hasSubscribers) searchCandidatesRead.publish({ paths: rows.map((row) => row.path) });\n for (const row of rows) row.snippets = [];\n\n await hydrateSearchRows(store, cfg, rows, matchedPaths, terms, opts);\n if (opts.explain) {\n for (const row of rows) row.explanation = candidates.get(row.path)?.explanation ?? [];\n }\n\n return rows;\n}\n"],"names":["channel","stemmer","embedConfig","featureEnabled","resolveSearch","ensureDocumentEmbeddings","prepareSemanticQuery","localModelMissing","MODEL_FILENAMES","SenseError","serialQuery","bareTermSyntaxError","searchError","foldForSearch","hasUnspacedRun","searchTokens","unspacedRuns","materializeScope","narrowByWhere","rawScope","scopeHasEmbeddings","linksCandidates","vectorsCandidates","wordsCandidates","assertQuerySnapshot","SNIPPET_CHAR_LIMIT_DEFAULT","SNIPPET_COUNT_LIMIT_DEFAULT","searchCandidatesRead","extractBareTerms","query","cleaned","replace","split","map","tok","flatMap","text","toLowerCase","trim","filter","length","wordSegmenter","findOccurrences","terms","occ","textLower","unspacedTerms","run","runLower","slice","start","end","term","idx","found","indexOf","push","spacedTerms","t","termByStem","Map","stemInitials","Set","Array","from","keys","match","segment","unspaced","folded","has","get","undefined","sort","a","b","wordBoundaries","Intl","Segmenter","granularity","bounds","index","snapForward","pos","lo","hi","mid","snapBackward","result","firstBoundAfter","cutWindow","anchor","charLimit","rawEnd","Math","min","fitEnd","render","floor","upperBound","firstOccAtOrAfter","renderWindow","win","out","cursor","i","o","prefix","suffix","scoreWindows","candidates","winEnd","seen","count","j","add","max","e","distinct","size","overlaps","selectWindows","countLimit","ranked","chosen","c","some","s","best","computeSnippets","find","snippets","offset","lineNumberAt","line","charCodeAt","lineRangeFor","store","path","stmt","prepare","row","start_line","end_line","validateSearchOptions","opts","explain","String","name","value","k","snippetCharLimit","snippetCountLimit","Number","isFinite","isInteger","hydrateSearchRows","cfg","rows","matchedPaths","bareTerms","sourceStmt","source","lines","search","signal","aborted","Promise","reject","reason","searchIndexed","readSearchScope","signature","effective","allPaths","all","r","scopePaths","allowedPaths","where","err","wantsVectors","signals","vectors","semanticEligible","needsSemanticPreparation","isArray","initial","throwIfAborted","presetName","model","prepared","preparedSignature","preflight","transaction","searchSnapshot","fetch","scopeActive","semanticEnabled","scope","whereJoin","whereCond","scopeCond","matchRows","hasEmptyPhrase","matchAll","test","hasBareSyntaxOnly","words","syntaxError","links","chunkLines","chunkSimilarity","exec","runBatch","score","via","similarityCol","selectStmt","hasSubscribers","publish","paths","explanation"],"mappings":"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"}
|