sensemaking 0.9.4 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/README.md +50 -100
  2. package/dist/cjs/cli/check.js +7 -13
  3. package/dist/cjs/cli/check.js.map +1 -1
  4. package/dist/cjs/cli/download.d.cts +3 -0
  5. package/dist/cjs/cli/download.d.ts +3 -0
  6. package/dist/cjs/cli/download.js +214 -0
  7. package/dist/cjs/cli/download.js.map +1 -0
  8. package/dist/cjs/cli/index.d.cts +6 -3
  9. package/dist/cjs/cli/index.d.ts +6 -3
  10. package/dist/cjs/cli/index.js +23 -5
  11. package/dist/cjs/cli/index.js.map +1 -1
  12. package/dist/cjs/cli/init.js +4 -1
  13. package/dist/cjs/cli/init.js.map +1 -1
  14. package/dist/cjs/cli/map.js +2 -2
  15. package/dist/cjs/cli/map.js.map +1 -1
  16. package/dist/cjs/cli/named.js +10 -18
  17. package/dist/cjs/cli/named.js.map +1 -1
  18. package/dist/cjs/cli/path.d.cts +3 -0
  19. package/dist/cjs/cli/path.d.ts +3 -0
  20. package/dist/cjs/cli/path.js +161 -0
  21. package/dist/cjs/cli/path.js.map +1 -0
  22. package/dist/cjs/cli/peek.js +3 -2
  23. package/dist/cjs/cli/peek.js.map +1 -1
  24. package/dist/cjs/cli/related.d.cts +3 -0
  25. package/dist/cjs/cli/related.d.ts +3 -0
  26. package/dist/cjs/cli/related.js +279 -0
  27. package/dist/cjs/cli/related.js.map +1 -0
  28. package/dist/cjs/cli/search.js +3 -7
  29. package/dist/cjs/cli/search.js.map +1 -1
  30. package/dist/cjs/cli/shared.d.cts +3 -1
  31. package/dist/cjs/cli/shared.d.ts +3 -1
  32. package/dist/cjs/cli/shared.js +28 -5
  33. package/dist/cjs/cli/shared.js.map +1 -1
  34. package/dist/cjs/cli/sql.d.cts +3 -0
  35. package/dist/cjs/cli/sql.d.ts +3 -0
  36. package/dist/cjs/cli/{query.js → sql.js} +4 -4
  37. package/dist/cjs/cli/sql.js.map +1 -0
  38. package/dist/cjs/cli/status.js +9 -2
  39. package/dist/cjs/cli/status.js.map +1 -1
  40. package/dist/cjs/cli.js +14 -15
  41. package/dist/cjs/cli.js.map +1 -1
  42. package/dist/cjs/commands.d.cts +12 -4
  43. package/dist/cjs/commands.d.ts +12 -4
  44. package/dist/cjs/commands.js +138 -58
  45. package/dist/cjs/commands.js.map +1 -1
  46. package/dist/cjs/config.d.cts +6 -4
  47. package/dist/cjs/config.d.ts +6 -4
  48. package/dist/cjs/config.js +137 -58
  49. package/dist/cjs/config.js.map +1 -1
  50. package/dist/cjs/db.js +14 -34
  51. package/dist/cjs/db.js.map +1 -1
  52. package/dist/cjs/errors.d.cts +1 -1
  53. package/dist/cjs/errors.d.ts +1 -1
  54. package/dist/cjs/errors.js.map +1 -1
  55. package/dist/cjs/features/embed.d.cts +14 -0
  56. package/dist/cjs/features/embed.d.ts +14 -0
  57. package/dist/cjs/features/embed.js +248 -54
  58. package/dist/cjs/features/embed.js.map +1 -1
  59. package/dist/cjs/features/links.js +7 -16
  60. package/dist/cjs/features/links.js.map +1 -1
  61. package/dist/cjs/features/rank.js +2 -6
  62. package/dist/cjs/features/rank.js.map +1 -1
  63. package/dist/cjs/index.d.cts +1 -1
  64. package/dist/cjs/index.d.ts +1 -1
  65. package/dist/cjs/index.js.map +1 -1
  66. package/dist/cjs/output.js +11 -14
  67. package/dist/cjs/output.js.map +1 -1
  68. package/dist/cjs/progress.js +2 -4
  69. package/dist/cjs/progress.js.map +1 -1
  70. package/dist/cjs/scan.d.cts +1 -0
  71. package/dist/cjs/scan.d.ts +1 -0
  72. package/dist/cjs/scan.js +67 -32
  73. package/dist/cjs/scan.js.map +1 -1
  74. package/dist/cjs/search-error.js +4 -6
  75. package/dist/cjs/search-error.js.map +1 -1
  76. package/dist/cjs/traverse.d.cts +8 -0
  77. package/dist/cjs/traverse.d.ts +8 -0
  78. package/dist/cjs/traverse.js +97 -0
  79. package/dist/cjs/traverse.js.map +1 -0
  80. package/dist/esm/cli/check.js +7 -13
  81. package/dist/esm/cli/check.js.map +1 -1
  82. package/dist/esm/cli/download.d.ts +3 -0
  83. package/dist/esm/cli/download.js +27 -0
  84. package/dist/esm/cli/download.js.map +1 -0
  85. package/dist/esm/cli/index.d.ts +6 -3
  86. package/dist/esm/cli/index.js +13 -7
  87. package/dist/esm/cli/index.js.map +1 -1
  88. package/dist/esm/cli/init.js +4 -1
  89. package/dist/esm/cli/init.js.map +1 -1
  90. package/dist/esm/cli/map.js +3 -2
  91. package/dist/esm/cli/map.js.map +1 -1
  92. package/dist/esm/cli/named.js +10 -16
  93. package/dist/esm/cli/named.js.map +1 -1
  94. package/dist/esm/cli/path.d.ts +3 -0
  95. package/dist/esm/cli/path.js +51 -0
  96. package/dist/esm/cli/path.js.map +1 -0
  97. package/dist/esm/cli/peek.js +4 -2
  98. package/dist/esm/cli/peek.js.map +1 -1
  99. package/dist/esm/cli/related.d.ts +3 -0
  100. package/dist/esm/cli/related.js +26 -0
  101. package/dist/esm/cli/related.js.map +1 -0
  102. package/dist/esm/cli/search.js +2 -5
  103. package/dist/esm/cli/search.js.map +1 -1
  104. package/dist/esm/cli/shared.d.ts +3 -1
  105. package/dist/esm/cli/shared.js +28 -5
  106. package/dist/esm/cli/shared.js.map +1 -1
  107. package/dist/esm/cli/sql.d.ts +3 -0
  108. package/dist/esm/cli/{query.js → sql.js} +4 -4
  109. package/dist/esm/cli/sql.js.map +1 -0
  110. package/dist/esm/cli/status.js +8 -1
  111. package/dist/esm/cli/status.js.map +1 -1
  112. package/dist/esm/cli.js +13 -10
  113. package/dist/esm/cli.js.map +1 -1
  114. package/dist/esm/commands.d.ts +12 -4
  115. package/dist/esm/commands.js +111 -68
  116. package/dist/esm/commands.js.map +1 -1
  117. package/dist/esm/config.d.ts +6 -4
  118. package/dist/esm/config.js +126 -75
  119. package/dist/esm/config.js.map +1 -1
  120. package/dist/esm/db.js +16 -38
  121. package/dist/esm/db.js.map +1 -1
  122. package/dist/esm/errors.d.ts +1 -1
  123. package/dist/esm/errors.js.map +1 -1
  124. package/dist/esm/features/embed.d.ts +14 -0
  125. package/dist/esm/features/embed.js +110 -35
  126. package/dist/esm/features/embed.js.map +1 -1
  127. package/dist/esm/features/links.js +8 -17
  128. package/dist/esm/features/links.js.map +1 -1
  129. package/dist/esm/features/rank.js +2 -6
  130. package/dist/esm/features/rank.js.map +1 -1
  131. package/dist/esm/features/types.js.map +1 -1
  132. package/dist/esm/index.d.ts +1 -1
  133. package/dist/esm/index.js.map +1 -1
  134. package/dist/esm/output.js +13 -16
  135. package/dist/esm/output.js.map +1 -1
  136. package/dist/esm/progress.js +2 -4
  137. package/dist/esm/progress.js.map +1 -1
  138. package/dist/esm/scan.d.ts +1 -0
  139. package/dist/esm/scan.js +41 -34
  140. package/dist/esm/scan.js.map +1 -1
  141. package/dist/esm/search-error.js +4 -6
  142. package/dist/esm/search-error.js.map +1 -1
  143. package/dist/esm/traverse.d.ts +8 -0
  144. package/dist/esm/traverse.js +71 -0
  145. package/dist/esm/traverse.js.map +1 -0
  146. package/package.json +18 -5
  147. package/schema.json +12 -16
  148. package/skills/sense/EXAMPLES.md +13 -22
  149. package/skills/sense/SKILL.md +65 -140
  150. package/skills/sense-setup/EXAMPLES.md +30 -46
  151. package/skills/sense-setup/SKILL.md +22 -83
  152. package/dist/cjs/cli/query.d.cts +0 -3
  153. package/dist/cjs/cli/query.d.ts +0 -3
  154. package/dist/cjs/cli/query.js.map +0 -1
  155. package/dist/esm/cli/query.d.ts +0 -3
  156. package/dist/esm/cli/query.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/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 { loadConfig, SUPPORTED_CONFIG_VERSION } from './config.ts';\n\n// Parsing and dispatch only. Commands live in src/cli/, one file each, lazy-loaded --\n// nothing tree- or dependency-heavy may be imported at the top of this file. Flags parse\n// per command (each command's own parse() call); this file only handles the bare top-level\n// flags (--version/--help/--list/--config) and dispatch.\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] [--config path]`, ...lines, ` ${name} --list`, ` ${name} --version`].join('\\n');\n}\n\nfunction resolveConfigFor(name: string, configPath: string | undefined) {\n const cfg = loadConfig(configPath);\n if (cfg.migratedFrom !== undefined) {\n console.warn(`${name}: migrated ${cfg.configPath} from config version ${cfg.migratedFrom} to ${SUPPORTED_CONFIG_VERSION}`);\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 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 const entry = queries[queryName];\n const isSavedSearch = typeof entry === 'object' && entry !== null && 'search' in entry;\n console.log(isSavedSearch ? `${queryName} (search)` : queryName);\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 // find -> search (breaking rename): one release of a pointer instead of \"unknown query\".\n if (first === 'find') {\n console.error(`${name}: find is now search`);\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) => resolveConfigFor(name, configPath),\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":["cli","packageVersion","load","createRequire","rel","pkg","name","version","usage","lines","Object","values","USAGE","map","u","join","resolveConfigFor","configPath","cfg","loadConfig","migratedFrom","undefined","console","warn","SUPPORTED_CONFIG_VERSION","unknownKeys","argv","first","queries","queryName","entry","isSavedSearch","ctx","err","startsWith","parseArgs","args","options","type","default","short","help","list","config","allowPositionals","error","message","process","exit","log","keys","sort","slice","resolveConfig","usageError","COMMANDS"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/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 { loadConfig, SUPPORTED_CONFIG_VERSION } from './config.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] [--config path]`, ...lines, ` ${name} --list`, ` ${name} --version`].join('\\n');\n}\n\nfunction resolveConfigFor(name: string, configPath: string | undefined) {\n const cfg = loadConfig(configPath);\n if (cfg.migratedFrom !== undefined) {\n console.warn(`${name}: migrated ${cfg.configPath} from config version ${cfg.migratedFrom} to ${SUPPORTED_CONFIG_VERSION}`);\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 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) => resolveConfigFor(name, configPath),\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":["cli","packageVersion","load","createRequire","rel","pkg","name","version","usage","lines","Object","values","USAGE","map","u","join","resolveConfigFor","configPath","cfg","loadConfig","migratedFrom","undefined","console","warn","SUPPORTED_CONFIG_VERSION","unknownKeys","argv","first","queries","queryName","RENAMED","ctx","err","startsWith","parseArgs","args","options","type","default","short","help","list","config","allowPositionals","error","message","process","exit","log","keys","sort","find","query","slice","resolveConfig","usageError","COMMANDS"],"mappings":"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"}
@@ -1,14 +1,17 @@
1
1
  import type { DatabaseSync } from 'node:sqlite';
2
- import type { FeatureName, ResolvedConfig } from './config.js';
2
+ import type { FeatureName, ResolvedConfig, SearchOverrides } from './config.js';
3
3
  import type { Row } from './output.js';
4
4
  export interface SearchOptions {
5
5
  k?: number;
6
6
  where?: string;
7
7
  preset?: string;
8
8
  include?: string[];
9
- semantic?: boolean;
9
+ exclude?: string[];
10
+ noExclude?: boolean;
11
+ probe?: boolean;
10
12
  }
11
13
  export declare function search(db: DatabaseSync, cfg: ResolvedConfig, terms: string, opts?: SearchOptions): Promise<Row[]>;
14
+ export declare function scopedPaths(db: DatabaseSync, cfg: ResolvedConfig, overrides: SearchOverrides): Set<string>;
12
15
  export interface PresetCoverage {
13
16
  name: string;
14
17
  files: number;
@@ -30,7 +33,8 @@ export interface TreeMap {
30
33
  hubs: Row[];
31
34
  recent: Row[];
32
35
  }
33
- export declare function mapTree(db: DatabaseSync, cfg: ResolvedConfig): TreeMap;
36
+ export declare function mapTree(db: DatabaseSync, cfg: ResolvedConfig, overrides?: SearchOverrides): TreeMap;
37
+ export declare function resolveNote(paths: string[], arg: string): string;
34
38
  export interface Peek {
35
39
  path: string;
36
40
  tokens: number;
@@ -45,4 +49,8 @@ export interface Peek {
45
49
  unresolvedTotal: number;
46
50
  off: FeatureName[];
47
51
  }
48
- export declare function peek(db: DatabaseSync, cfg: ResolvedConfig, pathArg: string): Peek;
52
+ export declare function peek(db: DatabaseSync, cfg: ResolvedConfig, pathArg: string, overrides?: SearchOverrides): Peek;
53
+ export declare function relatedNotes(db: DatabaseSync, cfg: ResolvedConfig, pathArg: string, overrides: SearchOverrides, k: number): Promise<Array<{
54
+ path: string;
55
+ similarity: number;
56
+ }>>;
@@ -1,14 +1,17 @@
1
1
  import type { DatabaseSync } from 'node:sqlite';
2
- import type { FeatureName, ResolvedConfig } from './config.js';
2
+ import type { FeatureName, ResolvedConfig, SearchOverrides } from './config.js';
3
3
  import type { Row } from './output.js';
4
4
  export interface SearchOptions {
5
5
  k?: number;
6
6
  where?: string;
7
7
  preset?: string;
8
8
  include?: string[];
9
- semantic?: boolean;
9
+ exclude?: string[];
10
+ noExclude?: boolean;
11
+ probe?: boolean;
10
12
  }
11
13
  export declare function search(db: DatabaseSync, cfg: ResolvedConfig, terms: string, opts?: SearchOptions): Promise<Row[]>;
14
+ export declare function scopedPaths(db: DatabaseSync, cfg: ResolvedConfig, overrides: SearchOverrides): Set<string>;
12
15
  export interface PresetCoverage {
13
16
  name: string;
14
17
  files: number;
@@ -30,7 +33,8 @@ export interface TreeMap {
30
33
  hubs: Row[];
31
34
  recent: Row[];
32
35
  }
33
- export declare function mapTree(db: DatabaseSync, cfg: ResolvedConfig): TreeMap;
36
+ export declare function mapTree(db: DatabaseSync, cfg: ResolvedConfig, overrides?: SearchOverrides): TreeMap;
37
+ export declare function resolveNote(paths: string[], arg: string): string;
34
38
  export interface Peek {
35
39
  path: string;
36
40
  tokens: number;
@@ -45,4 +49,8 @@ export interface Peek {
45
49
  unresolvedTotal: number;
46
50
  off: FeatureName[];
47
51
  }
48
- export declare function peek(db: DatabaseSync, cfg: ResolvedConfig, pathArg: string): Peek;
52
+ export declare function peek(db: DatabaseSync, cfg: ResolvedConfig, pathArg: string, overrides?: SearchOverrides): Peek;
53
+ export declare function relatedNotes(db: DatabaseSync, cfg: ResolvedConfig, pathArg: string, overrides: SearchOverrides, k: number): Promise<Array<{
54
+ path: string;
55
+ similarity: number;
56
+ }>>;
@@ -18,6 +18,15 @@ _export(exports, {
18
18
  get presetCoverage () {
19
19
  return presetCoverage;
20
20
  },
21
+ get relatedNotes () {
22
+ return relatedNotes;
23
+ },
24
+ get resolveNote () {
25
+ return resolveNote;
26
+ },
27
+ get scopedPaths () {
28
+ return scopedPaths;
29
+ },
21
30
  get search () {
22
31
  return search;
23
32
  }
@@ -226,11 +235,8 @@ function _ts_generator(thisArg, body) {
226
235
  // Each returns data; cli.ts renders. All of them degrade when a feature is off.
227
236
  var WEIGHTED_BM25 = 'bm25(content, 10.0, 5.0, 1.0)';
228
237
  var RRF_K = 60;
229
- // FTS5 snippet() re-tokenizes the whole doc per candidate row, superlinearly: measured
230
- // 2ms at 4KB docs but 189ms at 64KB for one query's pool, and ~10s for a single 1MB doc
231
- // (plans/performance-findings.md finding A). 16KB keeps the whole pool's snippet cost in
232
- // the low-ms range; bigger docs get the JS-computed excerpt, linear and only for the rows
233
- // actually returned.
238
+ // snippet() re-tokenizes each candidate doc, superlinearly: ~10s for one 1MB doc
239
+ // (BENCHMARKING.md). Past this bound, rows get the linear JS excerpt instead.
234
240
  var SNIPPET_BOUND = 16384;
235
241
  var EXCERPT_WINDOW = 160;
236
242
  // Bare terms from an FTS5 query string: strips operators/quoting so the oversized-doc excerpt
@@ -367,8 +373,8 @@ function lineRangeFor(db, path, line) {
367
373
  return row ? "L".concat(row.start_line, "-").concat(row.end_line) : null;
368
374
  }
369
375
  // node:path's matchesGlob is experimental (stable behind an unstable-API flag) as of the
370
- // engines floor (Node >=22.16); scope filtering only ever needs single-pattern matching, so
371
- // it's used here in JS rather than pulling fast-glob's directory walk into the query path.
376
+ // engines floor (Node >=22.20); scope filtering only ever needs single-pattern matching, so
377
+ // it's used here in JS rather than running a directory walk in the query path.
372
378
  function inScope(path, include, exclude) {
373
379
  if (!include.some(function(g) {
374
380
  return (0, _nodepath.matchesGlob)(path, g);
@@ -387,7 +393,7 @@ function scopeHasEmbeddings(db, cfg, scopedPaths) {
387
393
  }
388
394
  function search(_0, _1, _2) {
389
395
  return _async_to_generator(function(db, cfg, terms) {
390
- var opts, _hits_get, _chunkLines_get, _chunkSimilarity_get, effective, k, include, exclude, allPaths, scopedPaths, scopeActive, fetch, semanticEnabled, scope, whereJoin, whereCond, matchSql, matchRows, hits, oversized, candidates, edges, linked, seeds, ranked, chunkLines, chunkSimilarity, vec, insert, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, _step_value, path, c, where, similarityCol, rows, bareTerms, _iteratorNormalCompletion1, _didIteratorError1, _iteratorError1, _iterator1, _step1, row, text, _computeExcerpt, excerpt, offset;
396
+ var opts, _hits_get, _chunkLines_get, _chunkSimilarity_get, effective, k, include, exclude, allPaths, adHocScope, scopedPaths, scopeActive, fetch, wantsVectors, semanticEnabled, scope, whereJoin, whereCond, matchSql, matchRows, hits, oversized, candidates, edges, linked, seeds, ranked, chunkLines, chunkSimilarity, vec, insert, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, _step_value, path, c, where, similarityCol, rows, bareTerms, _iteratorNormalCompletion1, _didIteratorError1, _iteratorError1, _iterator1, _step1, row, text, _computeExcerpt, excerpt, offset;
391
397
  var _arguments = arguments;
392
398
  return _ts_generator(this, function(_state) {
393
399
  switch(_state.label){
@@ -398,11 +404,10 @@ function search(_0, _1, _2) {
398
404
  allPaths = db.prepare('SELECT "path" FROM frontmatter').all().map(function(r) {
399
405
  return r.path;
400
406
  });
401
- // A preset scope reads preset_files -- the coverage reconcile actually indexed with
402
- // (fast-glob semantics); re-matching globs here with node:path's matchesGlob could
403
- // silently disagree with it. Ad-hoc --include has no persisted coverage, so only that
404
- // path pays the JS glob match.
405
- scopedPaths = opts.include ? new Set(allPaths.filter(function(p) {
407
+ // A preset scope reads the coverage reconcile indexed; re-matching globs here could disagree
408
+ // with it. Only ad-hoc scope flags, which have no persisted coverage, pay the JS match.
409
+ adHocScope = opts.include !== undefined || opts.exclude !== undefined || opts.noExclude === true;
410
+ scopedPaths = adHocScope ? new Set(allPaths.filter(function(p) {
406
411
  return inScope(p, include, exclude);
407
412
  })) : new Set(db.prepare('SELECT "path" FROM preset_files WHERE preset = ?').all(effective.presetName).map(function(r) {
408
413
  return r.path;
@@ -412,16 +417,16 @@ function search(_0, _1, _2) {
412
417
  // list that a plain over-fetch wouldn't have reached.
413
418
  scopeActive = scopedPaths.size < allPaths.length;
414
419
  fetch = scopeActive ? Math.max(k * 5, 50) : Math.max(k * 3, 30);
415
- // Semantic participation: vectors run only when the effective setting allows it AND the
416
- // scope actually has embedded files -- a scope covered only by semantic-false presets (or
417
- // by no preset embedding at all) searches lexically, automatically, no error.
418
- semanticEnabled = effective.semantic && scopeHasEmbeddings(db, cfg, scopedPaths);
419
- // Terms pass verbatim to FTS5 MATCH: bare words AND-join, operators are the caller's.
420
- // Invalid syntax propagates as an error, zero matches return zero -- no silent rewrites.
421
- // --where applies inside the candidate query (a post-filter over the top-N would drop
422
- // matches ranked past the pool) and again on the final select for link-derived rows.
423
- // An explicit --where replaces the preset's declared where rather than ANDing with it,
424
- // so a caller can always widen back to the whole scope.
420
+ // Asking for vectors without a model is a misconfiguration, not a mode: degrading would make
421
+ // the same search answer differently before and after a download. semantic:false never asks.
422
+ wantsVectors = !opts.probe && effective.semantic && (0, _configts.anyPresetEmbeds)(cfg);
423
+ if (wantsVectors && !(0, _embedts.modelPresent)(cfg)) {
424
+ throw new _errorsts.SenseError('EMBED_MODEL_MISSING', 'preset "'.concat(effective.presetName, '" searches with vectors, but the embedding model is not available; run `sense download`, or set "semantic": false on that preset to search on words and links'));
425
+ }
426
+ semanticEnabled = wantsVectors && scopeHasEmbeddings(db, cfg, scopedPaths);
427
+ // Terms pass verbatim to MATCH; invalid syntax errors rather than being rewritten. --where
428
+ // applies inside the candidate query (a post-filter would drop matches ranked past the pool)
429
+ // and again on the final select, for link-derived rows.
425
430
  scope = effective.where;
426
431
  whereJoin = scope ? 'JOIN frontmatter f ON f."path" = content.path' : '';
427
432
  whereCond = scope ? "AND (".concat(scope, ")") : '';
@@ -461,10 +466,8 @@ function search(_0, _1, _2) {
461
466
  });
462
467
  edges = (0, _configts.featureEnabled)(cfg, 'links') && matchRows.length > 0 ? (0, _indexts.linkEdges)(db) : [];
463
468
  if (edges.length > 0) {
464
- // `linked` gates the label only, not the score: PPR restart mass gives every seed a
465
- // nonzero rank even without an incident edge, which is not link evidence — but dropping
466
- // that mass from the score list reweights fusion toward connectivity and measurably
467
- // wrecks ranking on link-dense corpora (FEVER hit@10 0.997 -> 0.907; fusion-tuning.md).
469
+ // Gates the label only, not the score: restart mass ranks every seed without an incident
470
+ // edge, but dropping it from the score reweights fusion (FEVER hit@10 0.997 -> 0.907).
468
471
  linked = new Set(edges.flat());
469
472
  seeds = new Map(matchRows.map(function(r, i) {
470
473
  return [
@@ -525,9 +528,9 @@ function search(_0, _1, _2) {
525
528
  });
526
529
  _state.label = 2;
527
530
  case 2:
528
- db.exec('DROP TABLE IF EXISTS _find');
529
- db.exec('CREATE TEMP TABLE _find ("path" TEXT PRIMARY KEY, score REAL, via TEXT, hit TEXT, lines TEXT, similarity REAL)');
530
- insert = db.prepare('INSERT INTO _find ("path", score, via, hit, lines, similarity) VALUES (?, ?, ?, ?, ?, ?)');
531
+ db.exec('DROP TABLE IF EXISTS _search');
532
+ db.exec('CREATE TEMP TABLE _search ("path" TEXT PRIMARY KEY, score REAL, via TEXT, hit TEXT, lines TEXT, similarity REAL)');
533
+ insert = db.prepare('INSERT INTO _search ("path", score, via, hit, lines, similarity) VALUES (?, ?, ?, ?, ?, ?)');
531
534
  _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
532
535
  try {
533
536
  for(_iterator = candidates[Symbol.iterator](); !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
@@ -548,15 +551,13 @@ function search(_0, _1, _2) {
548
551
  }
549
552
  }
550
553
  }
551
- // Every candidate list was already filtered to scopedPaths above, so _find only ever holds
552
- // in-scope rows -- the final select just needs --where again, same as before, for rows
553
- // whichever candidate query didn't itself apply it to (link-derived rows never touched
554
- // whereCond above).
554
+ // Already scope-filtered above; the final select reapplies --where only for link-derived
555
+ // rows, which never passed through whereCond.
555
556
  where = scope ? "WHERE (".concat(scope, ")") : '';
556
557
  // lines is always present now: semantic rows carry their chunk's range, oversized-doc
557
558
  // lexical rows gain one below, everything else stays null. similarity stays semantic-only.
558
- similarityCol = semanticEnabled ? ', _find.similarity' : '';
559
- rows = db.prepare('SELECT f."path" AS path, content.title, content.summary, _find.hit, _find.via, round(_find.score, 4) AS score, _find.lines'.concat(similarityCol, '\n FROM _find JOIN frontmatter f ON f."path" = _find."path" JOIN content ON content.path = _find."path"\n ').concat(where, " ORDER BY _find.score DESC LIMIT ?")).all(k);
559
+ similarityCol = semanticEnabled ? ', _search.similarity' : '';
560
+ rows = db.prepare('SELECT f."path" AS path, content.title, content.summary, _search.hit, _search.via, round(_search.score, 4) AS score, _search.lines'.concat(similarityCol, '\n FROM _search JOIN frontmatter f ON f."path" = _search."path" JOIN content ON content.path = _search."path"\n ').concat(where, " ORDER BY _search.score DESC LIMIT ?")).all(k);
560
561
  if (oversized.size > 0) {
561
562
  bareTerms = extractBareTerms(terms);
562
563
  _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
@@ -597,6 +598,27 @@ function search(_0, _1, _2) {
597
598
  });
598
599
  }).apply(this, arguments);
599
600
  }
601
+ function scopedPaths(db, cfg, overrides) {
602
+ var effective = (0, _configts.resolveSearch)(cfg, overrides);
603
+ var include = effective.include, exclude = effective.exclude, where = effective.where;
604
+ var allPaths = db.prepare('SELECT "path" FROM frontmatter').all().map(function(r) {
605
+ return r.path;
606
+ });
607
+ var adHocScope = overrides.include !== undefined || overrides.exclude !== undefined || overrides.noExclude === true;
608
+ var paths = adHocScope ? new Set(allPaths.filter(function(p) {
609
+ return inScope(p, include, exclude);
610
+ })) : new Set(db.prepare('SELECT "path" FROM preset_files WHERE preset = ?').all(effective.presetName).map(function(r) {
611
+ return r.path;
612
+ }));
613
+ if (!where) return paths;
614
+ var whereRows = db.prepare('SELECT "path" FROM frontmatter f WHERE ('.concat(where, ")")).all();
615
+ var wherePaths = new Set(whereRows.map(function(r) {
616
+ return r.path;
617
+ }));
618
+ return new Set(_to_consumable_array(paths).filter(function(p) {
619
+ return wherePaths.has(p);
620
+ }));
621
+ }
600
622
  function presetCoverage(db, cfg) {
601
623
  var embedActive = (0, _configts.anyPresetEmbeds)(cfg);
602
624
  return (0, _configts.presetNames)(cfg).map(function(name) {
@@ -623,19 +645,26 @@ function chunk(items, size) {
623
645
  for(var i = 0; i < items.length; i += size)out.push(items.slice(i, i + size));
624
646
  return out;
625
647
  }
648
+ // Materializes the resolved scope into a temp table (same shape as traverse.ts's
649
+ // allowed_nodes) so every mapTree query can join/filter against it cheaply.
650
+ function setupMapScope(db, paths) {
651
+ db.exec('DROP TABLE IF EXISTS _map_scope');
652
+ db.exec('CREATE TEMP TABLE _map_scope ("path" TEXT PRIMARY KEY)');
653
+ db.prepare('INSERT INTO _map_scope SELECT DISTINCT value FROM json_each(?1)').run(JSON.stringify(_to_consumable_array(paths)));
654
+ }
626
655
  function mapTree(db, cfg) {
627
- var docs = db.prepare('SELECT COUNT(*) AS count, COALESCE(SUM("_size"), 0) AS bytes FROM frontmatter').get();
656
+ var overrides = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : {};
657
+ setupMapScope(db, scopedPaths(db, cfg, overrides));
658
+ var scopeWhere = 'WHERE "path" IN (SELECT "path" FROM _map_scope)';
659
+ var scopeAnd = 'AND f."path" IN (SELECT "path" FROM _map_scope)';
660
+ var docs = db.prepare('SELECT COUNT(*) AS count, COALESCE(SUM("_size"), 0) AS bytes FROM frontmatter '.concat(scopeWhere)).get();
628
661
  var columns = db.prepare('PRAGMA table_info(frontmatter)').all().map(function(r) {
629
662
  return r.name;
630
663
  }).filter(function(name) {
631
664
  return !INTERNAL_COLUMNS.has(name);
632
665
  });
633
- // Observed storage class, not a declared one: these columns are added dynamically and
634
- // SQLite types per value, so a field can be text in most notes and numeric in a few.
635
- // Listing every distinct type makes both the type and any drift visible.
636
- // One aggregate scan per chunk of columns rather than one scan per column: COUNT already
637
- // ignores NULL, and FILTER keeps typeof() looking only at the non-null rows COUNT counted,
638
- // matching the old per-column `WHERE ... IS NOT NULL` exactly.
666
+ // Observed types, not declared: SQLite types per value, so a field can be text in most notes
667
+ // and numeric in a few. One aggregate scan per chunk of columns; FILTER matches COUNT's nulls.
639
668
  var allFields = [];
640
669
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
641
670
  try {
@@ -645,7 +674,7 @@ function mapTree(db, cfg) {
645
674
  var quoted = '"'.concat(name.split('"').join('""'), '"');
646
675
  return "COUNT(".concat(quoted, ") AS n").concat(i, ", GROUP_CONCAT(DISTINCT typeof(").concat(quoted, ")) FILTER (WHERE ").concat(quoted, " IS NOT NULL) AS t").concat(i);
647
676
  });
648
- var result = db.prepare("SELECT ".concat(exprs.join(', '), " FROM frontmatter")).get();
677
+ var result = db.prepare("SELECT ".concat(exprs.join(', '), " FROM frontmatter ").concat(scopeWhere)).get();
649
678
  group.forEach(function(name, i) {
650
679
  var _result_;
651
680
  return allFields.push({
@@ -674,8 +703,8 @@ function mapTree(db, cfg) {
674
703
  return b.coverage - a.coverage;
675
704
  });
676
705
  var fields = allFields.slice(0, 20);
677
- var hubs = (0, _configts.featureEnabled)(cfg, 'rank') ? db.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 ORDER BY f."_rank" DESC LIMIT 8').all() : [];
678
- var recent = db.prepare('SELECT "path", datetime("_mtime" / 1000, \'unixepoch\') AS modified FROM frontmatter ORDER BY "_mtime" DESC LIMIT 5').all();
706
+ var hubs = (0, _configts.featureEnabled)(cfg, 'rank') ? db.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 '.concat(scopeAnd, ' ORDER BY f."_rank" DESC LIMIT 8')).all() : [];
707
+ var recent = db.prepare('SELECT "path", datetime("_mtime" / 1000, \'unixepoch\') AS modified FROM frontmatter '.concat(scopeWhere, ' ORDER BY "_mtime" DESC LIMIT 5')).all();
679
708
  return {
680
709
  docs: docs,
681
710
  fields: fields,
@@ -686,24 +715,27 @@ function mapTree(db, cfg) {
686
715
  recent: recent
687
716
  };
688
717
  }
718
+ function resolveNote(paths, arg) {
719
+ var exact = paths.find(function(p) {
720
+ return p === arg;
721
+ });
722
+ if (exact) return exact;
723
+ var base = _posix.default.basename(arg).replace(/\.md$/i, '').toLowerCase();
724
+ var matches = paths.filter(function(p) {
725
+ return _posix.default.basename(p).replace(/\.md$/i, '').toLowerCase() === base;
726
+ });
727
+ if (matches.length === 1) return matches[0];
728
+ if (matches.length > 1) throw new _errorsts.SenseError('NOTE_AMBIGUOUS', '"'.concat(arg, '" is ambiguous: ').concat(matches.join(', ')));
729
+ throw new _errorsts.SenseError('NOTE_NOT_FOUND', 'no note matches "'.concat(arg, '"'));
730
+ }
689
731
  var PEEK_LIST_LIMIT = 20;
690
732
  function peek(db, cfg, pathArg) {
733
+ var overrides = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : {};
691
734
  var _row__size;
692
735
  var paths = db.prepare('SELECT "path" FROM frontmatter').all().map(function(r) {
693
736
  return r.path;
694
737
  });
695
- var path = paths.find(function(p) {
696
- return p === pathArg;
697
- });
698
- if (!path) {
699
- var base = _posix.default.basename(pathArg).replace(/\.md$/i, '').toLowerCase();
700
- var matches = paths.filter(function(p) {
701
- return _posix.default.basename(p).replace(/\.md$/i, '').toLowerCase() === base;
702
- });
703
- if (matches.length === 1) path = matches[0];
704
- else if (matches.length > 1) throw new _errorsts.SenseError('NOTE_AMBIGUOUS', '"'.concat(pathArg, '" is ambiguous: ').concat(matches.join(', ')));
705
- else throw new _errorsts.SenseError('NOTE_NOT_FOUND', 'no note matches "'.concat(pathArg, '"'));
706
- }
738
+ var path = resolveNote(paths, pathArg);
707
739
  var row = db.prepare('SELECT * FROM frontmatter WHERE "path" = ?').get(path);
708
740
  var frontmatter = {};
709
741
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
@@ -732,6 +764,7 @@ function peek(db, cfg, pathArg) {
732
764
  var backlinks = [];
733
765
  var unresolved = [];
734
766
  var backlinksTotal = 0;
767
+ var _allowed = scopedPaths(db, cfg, overrides);
735
768
  if ((0, _configts.featureEnabled)(cfg, 'links')) {
736
769
  var out = db.prepare('SELECT target, dst FROM links WHERE src = ? ORDER BY target').all(path);
737
770
  outbound = _to_consumable_array(new Set(out.filter(function(l) {
@@ -769,4 +802,51 @@ function peek(db, cfg, pathArg) {
769
802
  })
770
803
  };
771
804
  }
805
+ function relatedNotes(db, cfg, pathArg, overrides, k) {
806
+ return _async_to_generator(function() {
807
+ var paths, path, outbound, backlinks, exclude, allowed;
808
+ return _ts_generator(this, function(_state) {
809
+ switch(_state.label){
810
+ case 0:
811
+ paths = db.prepare('SELECT "path" FROM frontmatter').all().map(function(r) {
812
+ return r.path;
813
+ });
814
+ path = resolveNote(paths, pathArg);
815
+ outbound = db.prepare('SELECT DISTINCT dst FROM links WHERE src = ? AND dst IS NOT NULL').all(path).map(function(r) {
816
+ return r.dst;
817
+ });
818
+ backlinks = db.prepare('SELECT DISTINCT src FROM links WHERE dst = ?').all(path).map(function(r) {
819
+ return r.src;
820
+ });
821
+ exclude = new Set([
822
+ path
823
+ ].concat(_to_consumable_array(outbound), _to_consumable_array(backlinks)));
824
+ allowed = scopedPaths(db, cfg, overrides);
825
+ if (!scopeHasEmbeddings(db, cfg, allowed)) return [
826
+ 2,
827
+ []
828
+ ];
829
+ // Vectors are the only signal, so [] would read as "nothing is related" rather than "no
830
+ // model". Top up pending rows too, or a fresh index answers 0 until some search runs.
831
+ if (!(0, _embedts.modelPresent)(cfg)) {
832
+ throw new _errorsts.SenseError('EMBED_MODEL_MISSING', 'related ranks notes by meaning, so it needs the embedding model, which is not downloaded; run `sense download` (search still works without it, on words and links)');
833
+ }
834
+ return [
835
+ 4,
836
+ (0, _embedts.embedPending)(db, cfg, cfg.baseDir)
837
+ ];
838
+ case 1:
839
+ _state.sent();
840
+ return [
841
+ 2,
842
+ (0, _embedts.similarNotes)(db, cfg, path, {
843
+ exclude: exclude,
844
+ allowed: allowed,
845
+ k: k
846
+ })
847
+ ];
848
+ }
849
+ });
850
+ })();
851
+ }
772
852
  /* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }