sensemaking 0.24.7 → 0.25.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 (242) hide show
  1. package/README.md +20 -11
  2. package/dist/cjs/cli/build.d.cts +3 -0
  3. package/dist/cjs/cli/build.d.ts +3 -0
  4. package/dist/cjs/cli/build.js +195 -0
  5. package/dist/cjs/cli/build.js.map +1 -0
  6. package/dist/cjs/cli/index.d.cts +7 -6
  7. package/dist/cjs/cli/index.d.ts +7 -6
  8. package/dist/cjs/cli/index.js +12 -6
  9. package/dist/cjs/cli/index.js.map +1 -1
  10. package/dist/cjs/cli/init.js +2 -2
  11. package/dist/cjs/cli/init.js.map +1 -1
  12. package/dist/cjs/cli/map.js +7 -2
  13. package/dist/cjs/cli/map.js.map +1 -1
  14. package/dist/cjs/cli/named.js +63 -21
  15. package/dist/cjs/cli/named.js.map +1 -1
  16. package/dist/cjs/cli/path.js +7 -2
  17. package/dist/cjs/cli/path.js.map +1 -1
  18. package/dist/cjs/cli/peek.js +7 -2
  19. package/dist/cjs/cli/peek.js.map +1 -1
  20. package/dist/cjs/cli/related.js +55 -6
  21. package/dist/cjs/cli/related.js.map +1 -1
  22. package/dist/cjs/cli/search.js +54 -15
  23. package/dist/cjs/cli/search.js.map +1 -1
  24. package/dist/cjs/cli/shared.d.cts +9 -3
  25. package/dist/cjs/cli/shared.d.ts +9 -3
  26. package/dist/cjs/cli/shared.js +109 -74
  27. package/dist/cjs/cli/shared.js.map +1 -1
  28. package/dist/cjs/cli/sql.js +8 -3
  29. package/dist/cjs/cli/sql.js.map +1 -1
  30. package/dist/cjs/cli/status.js +142 -50
  31. package/dist/cjs/cli/status.js.map +1 -1
  32. package/dist/cjs/cli/types.d.cts +4 -2
  33. package/dist/cjs/cli/types.d.ts +4 -2
  34. package/dist/cjs/cli.js +10 -4
  35. package/dist/cjs/cli.js.map +1 -1
  36. package/dist/cjs/commands/map.js +10 -6
  37. package/dist/cjs/commands/map.js.map +1 -1
  38. package/dist/cjs/commands/peek.js +10 -6
  39. package/dist/cjs/commands/peek.js.map +1 -1
  40. package/dist/cjs/commands/related.d.cts +1 -0
  41. package/dist/cjs/commands/related.d.ts +1 -0
  42. package/dist/cjs/commands/related.js +56 -21
  43. package/dist/cjs/commands/related.js.map +1 -1
  44. package/dist/cjs/commands/scope.js +1 -1
  45. package/dist/cjs/commands/scope.js.map +1 -1
  46. package/dist/cjs/commands/search.js +102 -84
  47. package/dist/cjs/commands/search.js.map +1 -1
  48. package/dist/cjs/config/index.d.cts +1 -1
  49. package/dist/cjs/config/index.d.ts +1 -1
  50. package/dist/cjs/config/index.js.map +1 -1
  51. package/dist/cjs/config/load.d.cts +6 -1
  52. package/dist/cjs/config/load.d.ts +6 -1
  53. package/dist/cjs/config/load.js +38 -1
  54. package/dist/cjs/config/load.js.map +1 -1
  55. package/dist/cjs/config/types.d.cts +2 -1
  56. package/dist/cjs/config/types.d.ts +2 -1
  57. package/dist/cjs/config/types.js +1 -1
  58. package/dist/cjs/config/types.js.map +1 -1
  59. package/dist/cjs/config/validate.js +20 -0
  60. package/dist/cjs/config/validate.js.map +1 -1
  61. package/dist/cjs/embed/query.d.cts +2 -1
  62. package/dist/cjs/embed/query.d.ts +2 -1
  63. package/dist/cjs/embed/query.js +186 -109
  64. package/dist/cjs/embed/query.js.map +1 -1
  65. package/dist/cjs/errors.d.cts +1 -1
  66. package/dist/cjs/errors.d.ts +1 -1
  67. package/dist/cjs/errors.js.map +1 -1
  68. package/dist/cjs/features/embed.js.map +1 -1
  69. package/dist/cjs/graph/traverse.js +11 -6
  70. package/dist/cjs/graph/traverse.js.map +1 -1
  71. package/dist/cjs/index.d.cts +3 -3
  72. package/dist/cjs/index.d.ts +3 -3
  73. package/dist/cjs/index.js +3 -0
  74. package/dist/cjs/index.js.map +1 -1
  75. package/dist/cjs/lib/atomic-write.js +20 -6
  76. package/dist/cjs/lib/atomic-write.js.map +1 -1
  77. package/dist/cjs/lib/serial-query.d.cts +1 -0
  78. package/dist/cjs/lib/serial-query.d.ts +1 -0
  79. package/dist/cjs/lib/serial-query.js +22 -0
  80. package/dist/cjs/lib/serial-query.js.map +1 -0
  81. package/dist/cjs/scan/index.d.cts +5 -0
  82. package/dist/cjs/scan/index.d.ts +5 -0
  83. package/dist/cjs/scan/index.js +12 -4
  84. package/dist/cjs/scan/index.js.map +1 -1
  85. package/dist/cjs/scan/pool.d.cts +5 -0
  86. package/dist/cjs/scan/pool.d.ts +5 -0
  87. package/dist/cjs/scan/pool.js +167 -55
  88. package/dist/cjs/scan/pool.js.map +1 -1
  89. package/dist/cjs/store/builder.d.cts +6 -1
  90. package/dist/cjs/store/builder.d.ts +6 -1
  91. package/dist/cjs/store/builder.js +135 -5
  92. package/dist/cjs/store/builder.js.map +1 -1
  93. package/dist/cjs/store/duckdb/lexical.d.cts +2 -0
  94. package/dist/cjs/store/duckdb/lexical.d.ts +2 -0
  95. package/dist/cjs/store/duckdb/lexical.js +139 -7
  96. package/dist/cjs/store/duckdb/lexical.js.map +1 -1
  97. package/dist/cjs/store/duckdb/open.d.cts +3 -3
  98. package/dist/cjs/store/duckdb/open.d.ts +3 -3
  99. package/dist/cjs/store/duckdb/open.js +53 -41
  100. package/dist/cjs/store/duckdb/open.js.map +1 -1
  101. package/dist/cjs/store/duckdb/reconcile.js +2 -2
  102. package/dist/cjs/store/duckdb/reconcile.js.map +1 -1
  103. package/dist/cjs/store/index.d.cts +23 -4
  104. package/dist/cjs/store/index.d.ts +23 -4
  105. package/dist/cjs/store/index.js +224 -15
  106. package/dist/cjs/store/index.js.map +1 -1
  107. package/dist/cjs/store/open.d.cts +9 -1
  108. package/dist/cjs/store/open.d.ts +9 -1
  109. package/dist/cjs/store/open.js +479 -128
  110. package/dist/cjs/store/open.js.map +1 -1
  111. package/dist/cjs/store/reconcile.js +93 -63
  112. package/dist/cjs/store/reconcile.js.map +1 -1
  113. package/dist/cjs/store/sqlite/open.d.cts +3 -3
  114. package/dist/cjs/store/sqlite/open.d.ts +3 -3
  115. package/dist/cjs/store/sqlite/open.js +44 -36
  116. package/dist/cjs/store/sqlite/open.js.map +1 -1
  117. package/dist/cjs/store/turso/open.d.cts +3 -3
  118. package/dist/cjs/store/turso/open.d.ts +3 -3
  119. package/dist/cjs/store/turso/open.js +83 -66
  120. package/dist/cjs/store/turso/open.js.map +1 -1
  121. package/dist/cjs/store/turso/store.d.cts +3 -1
  122. package/dist/cjs/store/turso/store.d.ts +3 -1
  123. package/dist/cjs/store/turso/store.js +46 -31
  124. package/dist/cjs/store/turso/store.js.map +1 -1
  125. package/dist/cjs/store/types.d.cts +12 -3
  126. package/dist/cjs/store/types.d.ts +12 -3
  127. package/dist/cjs/store/types.js.map +1 -1
  128. package/dist/cjs/watch.js +155 -35
  129. package/dist/cjs/watch.js.map +1 -1
  130. package/dist/esm/cli/build.d.ts +3 -0
  131. package/dist/esm/cli/build.js +18 -0
  132. package/dist/esm/cli/build.js.map +1 -0
  133. package/dist/esm/cli/index.d.ts +7 -6
  134. package/dist/esm/cli/index.js +8 -6
  135. package/dist/esm/cli/index.js.map +1 -1
  136. package/dist/esm/cli/init.js +2 -2
  137. package/dist/esm/cli/init.js.map +1 -1
  138. package/dist/esm/cli/map.js +8 -2
  139. package/dist/esm/cli/map.js.map +1 -1
  140. package/dist/esm/cli/named.js +38 -14
  141. package/dist/esm/cli/named.js.map +1 -1
  142. package/dist/esm/cli/path.js +8 -2
  143. package/dist/esm/cli/path.js.map +1 -1
  144. package/dist/esm/cli/peek.js +8 -2
  145. package/dist/esm/cli/peek.js.map +1 -1
  146. package/dist/esm/cli/related.js +21 -3
  147. package/dist/esm/cli/related.js.map +1 -1
  148. package/dist/esm/cli/search.js +28 -7
  149. package/dist/esm/cli/search.js.map +1 -1
  150. package/dist/esm/cli/shared.d.ts +9 -3
  151. package/dist/esm/cli/shared.js +44 -24
  152. package/dist/esm/cli/shared.js.map +1 -1
  153. package/dist/esm/cli/sql.js +8 -2
  154. package/dist/esm/cli/sql.js.map +1 -1
  155. package/dist/esm/cli/status.js +166 -111
  156. package/dist/esm/cli/status.js.map +1 -1
  157. package/dist/esm/cli/types.d.ts +4 -2
  158. package/dist/esm/cli/types.js.map +1 -1
  159. package/dist/esm/cli.js +9 -4
  160. package/dist/esm/cli.js.map +1 -1
  161. package/dist/esm/commands/map.js +5 -1
  162. package/dist/esm/commands/map.js.map +1 -1
  163. package/dist/esm/commands/peek.js +5 -1
  164. package/dist/esm/commands/peek.js.map +1 -1
  165. package/dist/esm/commands/related.d.ts +1 -0
  166. package/dist/esm/commands/related.js +19 -10
  167. package/dist/esm/commands/related.js.map +1 -1
  168. package/dist/esm/commands/scope.js +1 -1
  169. package/dist/esm/commands/scope.js.map +1 -1
  170. package/dist/esm/commands/search.js +11 -10
  171. package/dist/esm/commands/search.js.map +1 -1
  172. package/dist/esm/config/index.d.ts +1 -1
  173. package/dist/esm/config/index.js.map +1 -1
  174. package/dist/esm/config/load.d.ts +6 -1
  175. package/dist/esm/config/load.js +32 -3
  176. package/dist/esm/config/load.js.map +1 -1
  177. package/dist/esm/config/types.d.ts +2 -1
  178. package/dist/esm/config/types.js +1 -1
  179. package/dist/esm/config/types.js.map +1 -1
  180. package/dist/esm/config/validate.js +20 -0
  181. package/dist/esm/config/validate.js.map +1 -1
  182. package/dist/esm/embed/query.d.ts +2 -1
  183. package/dist/esm/embed/query.js +40 -34
  184. package/dist/esm/embed/query.js.map +1 -1
  185. package/dist/esm/errors.d.ts +1 -1
  186. package/dist/esm/errors.js.map +1 -1
  187. package/dist/esm/features/embed.js.map +1 -1
  188. package/dist/esm/graph/traverse.js +5 -1
  189. package/dist/esm/graph/traverse.js.map +1 -1
  190. package/dist/esm/index.d.ts +3 -3
  191. package/dist/esm/index.js +1 -1
  192. package/dist/esm/index.js.map +1 -1
  193. package/dist/esm/lib/atomic-write.js +14 -6
  194. package/dist/esm/lib/atomic-write.js.map +1 -1
  195. package/dist/esm/lib/serial-query.d.ts +1 -0
  196. package/dist/esm/lib/serial-query.js +13 -0
  197. package/dist/esm/lib/serial-query.js.map +1 -0
  198. package/dist/esm/scan/index.d.ts +5 -0
  199. package/dist/esm/scan/index.js +7 -3
  200. package/dist/esm/scan/index.js.map +1 -1
  201. package/dist/esm/scan/pool.d.ts +5 -0
  202. package/dist/esm/scan/pool.js +41 -16
  203. package/dist/esm/scan/pool.js.map +1 -1
  204. package/dist/esm/store/builder.d.ts +6 -1
  205. package/dist/esm/store/builder.js +19 -0
  206. package/dist/esm/store/builder.js.map +1 -1
  207. package/dist/esm/store/duckdb/lexical.d.ts +2 -0
  208. package/dist/esm/store/duckdb/lexical.js +32 -4
  209. package/dist/esm/store/duckdb/lexical.js.map +1 -1
  210. package/dist/esm/store/duckdb/open.d.ts +3 -3
  211. package/dist/esm/store/duckdb/open.js +13 -6
  212. package/dist/esm/store/duckdb/open.js.map +1 -1
  213. package/dist/esm/store/duckdb/reconcile.js +2 -2
  214. package/dist/esm/store/duckdb/reconcile.js.map +1 -1
  215. package/dist/esm/store/index.d.ts +23 -4
  216. package/dist/esm/store/index.js +80 -15
  217. package/dist/esm/store/index.js.map +1 -1
  218. package/dist/esm/store/open.d.ts +9 -1
  219. package/dist/esm/store/open.js +175 -84
  220. package/dist/esm/store/open.js.map +1 -1
  221. package/dist/esm/store/reconcile.js +12 -0
  222. package/dist/esm/store/reconcile.js.map +1 -1
  223. package/dist/esm/store/sqlite/open.d.ts +3 -3
  224. package/dist/esm/store/sqlite/open.js +9 -6
  225. package/dist/esm/store/sqlite/open.js.map +1 -1
  226. package/dist/esm/store/turso/open.d.ts +3 -3
  227. package/dist/esm/store/turso/open.js +14 -10
  228. package/dist/esm/store/turso/open.js.map +1 -1
  229. package/dist/esm/store/turso/store.d.ts +3 -1
  230. package/dist/esm/store/turso/store.js +5 -1
  231. package/dist/esm/store/turso/store.js.map +1 -1
  232. package/dist/esm/store/types.d.ts +12 -3
  233. package/dist/esm/store/types.js.map +1 -1
  234. package/dist/esm/watch.js +57 -19
  235. package/dist/esm/watch.js.map +1 -1
  236. package/package.json +1 -1
  237. package/schema.json +7 -2
  238. package/skills/sense/SKILL.md +3 -3
  239. package/skills/sense-bases/SKILL.md +1 -2
  240. package/skills/sense-setup/SKILL.md +1 -1
  241. package/skills/sense-setup/references/embeddings.md +1 -1
  242. package/skills/sense-setup/references/store-benchmarks.md +6 -6
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/cli/status.ts"],"sourcesContent":["import { dirname } from 'node:path';\nimport { presetCoverage } from '../commands/status.ts';\nimport { embedConfig, featureStates, SUPPORTED_CONFIG_VERSION } from '../config/index.ts';\nimport { languageDistribution } from '../embed/distribution.ts';\nimport { probeReachable } from '../embed/http.ts';\nimport { isDownloadable, MODEL_FILENAMES, modelDir, modelPresent, readLanguages } from '../embed/store.ts';\nimport { featuresLine, presetsLines, stringifyJson } from '../output/output.ts';\nimport { docCount, getMeta, openStore } from '../store/index.ts';\nimport type { Store } from '../store/types.ts';\nimport { readWatchClaim } from '../watch-claim.ts';\nimport { USAGE } from './index.ts';\nimport { CONFIG, FORMAT, formatOf, parse, printWarnings } from './shared.ts';\nimport type { Command } from './types.ts';\n\n// status names what you cannot read off the config file: locations sense chose, values it derived, state it stored. Config content itself is not echoed, since duplicating its globs here would create two places that disagree after an edit.\n// The exception is the embed block's url and key, whose effect is remote and otherwise invisible.\n\n// Vectors are computed on the first semantic query, not at reconcile, so a tree can have a semantic-on preset with nothing embedded yet.\n// Reported here because a fast reconcile on such a tree otherwise reads as \"the feature isn't on\".\nasync function vectorState(store: Store): Promise<{ embedded: number; pending: number } | null> {\n try {\n const stmt = await store.prepare('SELECT COUNT(vector) AS embedded, COUNT(*) - COUNT(vector) AS pending FROM embeddings');\n return (await stmt.get()) as { embedded: number; pending: number };\n } catch {\n return null;\n }\n}\n\nasync function parseErrorCount(store: Store): Promise<number> {\n const stmt = await store.prepare(`SELECT COUNT(*) AS n FROM frontmatter WHERE \"_parse_error\" IS NOT NULL`);\n return ((await stmt.get()) as { n: number }).n;\n}\n\n// A URL can carry credentials in its userinfo (https://user:pass@host/v1). status output gets\n// pasted into issues and transcripts, so it is always safe to paste.\nexport function redactUrl(url: string): string {\n try {\n const parsed = new URL(url);\n if (!parsed.username && !parsed.password) return url;\n parsed.username = '';\n parsed.password = '';\n return `${parsed.toString().replace(/\\/$/, '')} (credentials redacted)`;\n } catch {\n return url;\n }\n}\n\n// A stopped Ollama/LM Studio/Cohere endpoint prints identically to a running one otherwise; any\n// HTTP response counts as reachable (probeReachable), so this only distinguishes up from down.\nasync function embedReachable(e: { provider: string; url?: string }): Promise<boolean | null> {\n if (e.provider === 'static' || (e.provider === 'openai' && !e.url)) return null;\n const base = (e.url ?? 'https://api.cohere.com').replace(/\\/+$/, '');\n return probeReachable(e.provider === 'openai' ? `${base}/models` : base);\n}\n\ntype LanguagesState = 'declared' | 'none' | 'unresolved' | null;\n\n// Config-declared languages win over the model card, for every provider, as registry.ts does.\n// A local-path model has no card by design, so it is never \"unresolved\": that means an HF id.\nfunction languagesInfo(e: NonNullable<ReturnType<typeof embedConfig>>): { languages: string[] | null; state: LanguagesState; source: 'config' | 'card' | null } {\n if (e.languages) return { languages: e.languages, state: 'declared', source: 'config' };\n if (e.provider !== 'static' || !isDownloadable(e.model)) return { languages: null, state: null, source: null };\n const cardLanguages = readLanguages(e.model);\n if (cardLanguages === undefined) return { languages: null, state: 'unresolved', source: null };\n if (cardLanguages.length === 0) return { languages: null, state: 'none', source: null };\n return { languages: cardLanguages, state: 'declared', source: 'card' };\n}\n\nconst status: Command = async (ctx) => {\n const { values } = parse(ctx.argv, `usage: ${ctx.name} ${USAGE.status}`, { ...FORMAT, ...CONFIG });\n const format = formatOf(values);\n const cfg = ctx.resolveConfig(values.config as string | undefined);\n const { store, dbPath, warnings } = await openStore(cfg);\n printWarnings(warnings);\n\n const features = featureStates(cfg);\n const e = embedConfig(cfg);\n const hasModel = e ? modelPresent(cfg) : false;\n const reachable = e ? await embedReachable(e) : null;\n const vectors = e ? await vectorState(store) : null;\n const presets = await presetCoverage(store, cfg);\n const { languages: declaredLanguages, state: languagesState, source: languagesSource } = e ? languagesInfo(e) : { languages: null, state: null, source: null };\n const detectedLanguages = (await languageDistribution(store)) ?? null;\n // Written by the store's open() on every open, so it is this cache's true version on either\n // store (sqlite 18, duckdb 2); a version mismatch already triggered a rebuild before this read.\n const cacheSchema = await getMeta(store, 'schema_version');\n // Each store owns what it reports and how it is worded (Store.engineStatus); this command\n // prints entries generically, without branching on any store's name.\n const engine = await store.engineStatus();\n const watcher = readWatchClaim(cfg.configDir ?? cfg.rootDir ?? cfg.baseDir);\n // The env var holds the token; only its name and whether it is set are ever reported.\n const keyEnv = e?.key ? { name: e.key, set: (process.env[e.key] ?? '') !== '' } : null;\n const result = {\n config: cfg.configPath,\n configVersion: SUPPORTED_CONFIG_VERSION,\n migratedFrom: cfg.migratedFrom ?? null,\n unknownConfigKeys: cfg.unknownKeys ?? null,\n tree: cfg.rootDir ?? cfg.baseDir,\n treeRoot: cfg.rootDir ?? cfg.baseDir,\n configDir: cfg.configDir ?? cfg.baseDir,\n cache: dirname(dbPath),\n db: dbPath,\n cacheSchema,\n docs: await docCount(store),\n unparseableFrontmatter: await parseErrorCount(store),\n features: features.on,\n featuresOff: features.off,\n embed: e ? { provider: e.provider, model: e.model, dir: e.provider === 'static' ? modelDir(e.model) : null, url: e.url ? redactUrl(e.url) : null, reachable, keyEnv, downloaded: hasModel, languages: declaredLanguages, languagesState, detectedLanguages, ...(vectors ?? {}) } : null,\n presets,\n queries: Object.keys(cfg.queries ?? {}).length,\n watcherPid: watcher ? String(watcher.pid) : null,\n watcherHeartbeatSecondsAgo: watcher ? Math.round((Date.now() - watcher.heartbeatMs) / 1000) : null,\n engine,\n };\n\n if (format === 'json') {\n console.log(stringifyJson(result, 2));\n } else {\n const migrated = cfg.migratedFrom !== undefined ? `, migrated from v${cfg.migratedFrom} on this run` : '';\n console.log(`config: ${result.config} (v${SUPPORTED_CONFIG_VERSION}${migrated})`);\n // A load-time stderr warning has scrolled away by the time anyone looks here.\n if (cfg.unknownKeys) console.log(` unknown keys, ignored: ${cfg.unknownKeys.join(', ')}`);\n console.log(`tree root: ${result.treeRoot}`);\n console.log(`configDir: ${result.configDir}`);\n console.log(`cache: ${result.cache} (schema ${result.cacheSchema}; delete this directory to reset)`);\n console.log(`docs: ${result.docs}${result.unparseableFrontmatter > 0 ? ` (${result.unparseableFrontmatter} with unparseable frontmatter: WHERE _parse_error IS NOT NULL)` : ''}`);\n console.log('');\n console.log(featuresLine(features));\n // One authoritative block, off states included: an absent model reads as a degraded search,\n // not as an empty corpus.\n if (!e) console.log('embed: off (no preset asks for vectors)');\n else {\n console.log(`embed: ${e.provider} ${e.model}`);\n if (e.provider === 'static') console.log(` model: ${modelDir(e.model)} (${hasModel ? 'present' : `missing, ${isDownloadable(e.model) ? `fetches automatically at first search, or run \\`${ctx.name} download\\` to prefetch` : `no ${MODEL_FILENAMES} in that directory`}`})`);\n if (declaredLanguages && languagesSource === 'config') console.log(` languages: ${declaredLanguages.join(', ')} (declared in embed.languages)`);\n else if (declaredLanguages && languagesSource === 'card') console.log(` languages: ${declaredLanguages.join(', ')} (declared by the model card)`);\n else if (languagesState === 'none') console.log(' languages: none declared by the model card');\n else if (languagesState === 'unresolved') console.log(` languages: unresolved (model card unreachable; run \\`${ctx.name} download\\`)`);\n // Cohere defaults to api.cohere.com with no url in the config; show it anyway so the\n // reachability probe below has something to name.\n if (e.url || reachable !== null) {\n const shown = e.url ? redactUrl(e.url) : 'https://api.cohere.com (default)';\n console.log(` url: ${shown}${reachable === null ? '' : reachable ? ' (reachable)' : ' (unreachable)'}`);\n }\n if (keyEnv) console.log(` key: env ${keyEnv.name} (${keyEnv.set ? 'set' : 'NOT SET'})`);\n if (!hasModel) console.log(' vectors: off until the model is present; search runs on words and links');\n else if (vectors) console.log(` vectors: ${vectors.embedded} embedded, ${vectors.pending} pending (embedded on the first semantic search)`);\n if (detectedLanguages) {\n const total = Object.values(detectedLanguages).reduce((a, b) => a + b, 0);\n const breakdown = Object.entries(detectedLanguages)\n .sort((a, b) => b[1] - a[1])\n .map(([code, n]) => `${code} ${Math.round((n / total) * 100)}%`)\n .join(', ');\n console.log(` detected: ${breakdown} (${total} chunks classified)`);\n }\n }\n console.log('');\n for (const line of presetsLines(presets)) console.log(line);\n console.log('');\n console.log(`queries: ${result.queries} saved (${ctx.name} --list)`);\n const pid = result.watcherPid ? `pid ${result.watcherPid}, ` : '';\n console.log(result.watcherHeartbeatSecondsAgo === null ? 'watcher: none' : `watcher: ${pid}last heartbeat ${result.watcherHeartbeatSecondsAgo}s ago`);\n for (const [key, value] of Object.entries(result.engine)) console.log(`${`${store.name}:`.padEnd(10)}${key} ${value}`);\n }\n await store.close();\n};\nexport default status;\n"],"names":["dirname","presetCoverage","embedConfig","featureStates","SUPPORTED_CONFIG_VERSION","languageDistribution","probeReachable","isDownloadable","MODEL_FILENAMES","modelDir","modelPresent","readLanguages","featuresLine","presetsLines","stringifyJson","docCount","getMeta","openStore","readWatchClaim","USAGE","CONFIG","FORMAT","formatOf","parse","printWarnings","vectorState","store","stmt","prepare","get","parseErrorCount","n","redactUrl","url","parsed","URL","username","password","toString","replace","embedReachable","e","provider","base","languagesInfo","languages","state","source","model","cardLanguages","undefined","length","status","ctx","cfg","process","values","argv","name","format","resolveConfig","config","dbPath","warnings","features","hasModel","reachable","vectors","presets","declaredLanguages","languagesState","languagesSource","detectedLanguages","cacheSchema","engine","engineStatus","watcher","configDir","rootDir","baseDir","keyEnv","key","set","env","result","configPath","configVersion","migratedFrom","unknownConfigKeys","unknownKeys","tree","treeRoot","cache","db","docs","unparseableFrontmatter","on","featuresOff","off","embed","dir","downloaded","queries","Object","keys","watcherPid","String","pid","watcherHeartbeatSecondsAgo","Math","round","Date","now","heartbeatMs","console","log","migrated","join","shown","embedded","pending","total","reduce","a","b","breakdown","entries","sort","map","code","line","value","padEnd","close"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/cli/status.ts"],"sourcesContent":["import { dirname, join } from 'node:path';\nimport { presetCoverage } from '../commands/status.ts';\nimport { embedConfig, featureStates, STATE_DIR, SUPPORTED_CONFIG_VERSION } from '../config/index.ts';\nimport { languageDistribution } from '../embed/distribution.ts';\nimport { probeReachable } from '../embed/http.ts';\nimport { isDownloadable, MODEL_FILENAMES, modelDir, modelPresent, readLanguages } from '../embed/store.ts';\nimport { SenseError } from '../errors.ts';\nimport { featuresLine, presetsLines, stringifyJson } from '../output/output.ts';\nimport type { OpenResult } from '../store/index.ts';\nimport { docCount, getMeta, lexicalReadiness, openStore, storeFilename } from '../store/index.ts';\nimport type { Store } from '../store/types.ts';\nimport { readWatchClaim } from '../watch-claim.ts';\nimport { USAGE } from './index.ts';\nimport { CONFIG, FORMAT, formatOf, parse, printWarnings } from './shared.ts';\nimport type { Command } from './types.ts';\n\n// status names what you cannot read off the config file: locations sense chose, values it derived, state it stored. Config content itself is not echoed, since duplicating its globs here would create two places that disagree after an edit.\n// The exception is the embed block's url and key, whose effect is remote and otherwise invisible.\n\n// A failed or interrupted vector preparation can leave core and lexical state usable. Status is\n// observational, so it reports that pending capability instead of repairing it.\nasync function vectorState(store: Store): Promise<{ embedded: number; pending: number } | null> {\n try {\n const stmt = await store.prepare('SELECT COUNT(vector) AS embedded, COUNT(*) - COUNT(vector) AS pending FROM embeddings');\n return (await stmt.get()) as { embedded: number; pending: number };\n } catch {\n return null;\n }\n}\n\nasync function parseErrorCount(store: Store): Promise<number> {\n const stmt = await store.prepare(`SELECT COUNT(*) AS n FROM frontmatter WHERE \"_parse_error\" IS NOT NULL`);\n return ((await stmt.get()) as { n: number }).n;\n}\n\n// A URL can carry credentials in its userinfo (https://user:pass@host/v1). status output gets\n// pasted into issues and transcripts, so it is always safe to paste.\nexport function redactUrl(url: string): string {\n try {\n const parsed = new URL(url);\n if (!parsed.username && !parsed.password) return url;\n parsed.username = '';\n parsed.password = '';\n return `${parsed.toString().replace(/\\/$/, '')} (credentials redacted)`;\n } catch {\n return url;\n }\n}\n\n// A stopped Ollama/LM Studio/Cohere endpoint prints identically to a running one otherwise; any\n// HTTP response counts as reachable (probeReachable), so this only distinguishes up from down.\nasync function embedReachable(e: { provider: string; url?: string }): Promise<boolean | null> {\n if (e.provider === 'static' || (e.provider === 'openai' && !e.url)) return null;\n const base = (e.url ?? 'https://api.cohere.com').replace(/\\/+$/, '');\n return probeReachable(e.provider === 'openai' ? `${base}/models` : base);\n}\n\ntype LanguagesState = 'declared' | 'none' | 'unresolved' | null;\n\n// Config-declared languages win over the model card, for every provider, as registry.ts does.\n// A local-path model has no card by design, so it is never \"unresolved\": that means an HF id.\nfunction languagesInfo(e: NonNullable<ReturnType<typeof embedConfig>>): { languages: string[] | null; state: LanguagesState; source: 'config' | 'card' | null } {\n if (e.languages) return { languages: e.languages, state: 'declared', source: 'config' };\n if (e.provider !== 'static' || !isDownloadable(e.model)) return { languages: null, state: null, source: null };\n const cardLanguages = readLanguages(e.model);\n if (cardLanguages === undefined) return { languages: null, state: 'unresolved', source: null };\n if (cardLanguages.length === 0) return { languages: null, state: 'none', source: null };\n return { languages: cardLanguages, state: 'declared', source: 'card' };\n}\n\nconst status: Command = async (ctx) => {\n const { values } = parse(ctx.argv, `usage: ${ctx.name} ${USAGE.status}`, { ...FORMAT, ...CONFIG });\n const format = formatOf(values);\n const cfg = ctx.resolveConfig(values.config as string | undefined, { writeMigration: false });\n const lexical = await lexicalReadiness(cfg);\n let opened: OpenResult;\n try {\n opened = await openStore(cfg, { build: false });\n } catch (err) {\n if (!(err instanceof SenseError) || err.code !== 'INDEX_NOT_READY') throw err;\n const configDir = cfg.configDir ?? cfg.rootDir ?? cfg.baseDir;\n const dbPath = join(configDir, STATE_DIR, storeFilename(cfg));\n const watcher = readWatchClaim(configDir);\n const result = {\n config: cfg.configPath,\n configVersion: SUPPORTED_CONFIG_VERSION,\n migratedFrom: cfg.migratedFrom ?? null,\n unknownConfigKeys: cfg.unknownKeys ?? null,\n tree: cfg.rootDir ?? cfg.baseDir,\n treeRoot: cfg.rootDir ?? cfg.baseDir,\n configDir,\n cache: dirname(dbPath),\n db: dbPath,\n indexReady: false,\n indexError: err.message,\n lexicalReady: lexical.ready,\n lexicalError: lexical.error,\n watcherPid: watcher ? String(watcher.pid) : null,\n watcherHeartbeatSecondsAgo: watcher ? Math.round((Date.now() - watcher.heartbeatMs) / 1000) : null,\n };\n if (format === 'json') {\n console.log(stringifyJson(result, 2));\n } else {\n const migrated = cfg.migratedFrom !== undefined ? `, migration from v${cfg.migratedFrom} pending` : '';\n console.log(`config: ${result.config} (v${SUPPORTED_CONFIG_VERSION}${migrated})`);\n console.log(`tree root: ${result.treeRoot}`);\n console.log(`configDir: ${result.configDir}`);\n console.log(`cache: ${result.cache}`);\n console.log(`index: not ready (${result.indexError})`);\n console.log(`lexical: ${result.lexicalReady ? 'ready' : `not ready (${result.lexicalError})`}`);\n const pid = result.watcherPid ? `pid ${result.watcherPid}, ` : '';\n console.log(result.watcherHeartbeatSecondsAgo === null ? 'watcher: none' : `watcher: ${pid}last heartbeat ${result.watcherHeartbeatSecondsAgo}s ago`);\n }\n return;\n }\n const { store, dbPath, warnings } = opened;\n try {\n printWarnings(warnings);\n\n const features = featureStates(cfg);\n const e = embedConfig(cfg);\n const hasModel = e ? modelPresent(cfg) : false;\n const reachable = e ? await embedReachable(e) : null;\n const vectors = e ? await vectorState(store) : null;\n const presets = await presetCoverage(store, cfg);\n const { languages: declaredLanguages, state: languagesState, source: languagesSource } = e ? languagesInfo(e) : { languages: null, state: null, source: null };\n const detectedLanguages = (await languageDistribution(store)) ?? null;\n // This is the compatible cache's stored version. Observational open refuses a mismatch rather\n // than rebuilding it before this read.\n const cacheSchema = await getMeta(store, 'schema_version');\n // Each store owns what it reports and how it is worded (Store.engineStatus); this command\n // prints entries generically, without branching on any store's name.\n const engine = await store.engineStatus();\n const watcher = readWatchClaim(cfg.configDir ?? cfg.rootDir ?? cfg.baseDir);\n // The env var holds the token; only its name and whether it is set are ever reported.\n const keyEnv = e?.key ? { name: e.key, set: (process.env[e.key] ?? '') !== '' } : null;\n const result = {\n config: cfg.configPath,\n configVersion: SUPPORTED_CONFIG_VERSION,\n migratedFrom: cfg.migratedFrom ?? null,\n unknownConfigKeys: cfg.unknownKeys ?? null,\n tree: cfg.rootDir ?? cfg.baseDir,\n treeRoot: cfg.rootDir ?? cfg.baseDir,\n configDir: cfg.configDir ?? cfg.baseDir,\n cache: dirname(dbPath),\n db: dbPath,\n indexReady: true,\n indexError: null,\n lexicalReady: lexical.ready,\n lexicalError: lexical.error,\n cacheSchema,\n docs: await docCount(store),\n unparseableFrontmatter: await parseErrorCount(store),\n features: features.on,\n featuresOff: features.off,\n embed: e ? { provider: e.provider, model: e.model, dir: e.provider === 'static' ? modelDir(e.model) : null, url: e.url ? redactUrl(e.url) : null, reachable, keyEnv, downloaded: hasModel, languages: declaredLanguages, languagesState, detectedLanguages, ...(vectors ?? {}) } : null,\n presets,\n queries: Object.keys(cfg.queries ?? {}).length,\n watcherPid: watcher ? String(watcher.pid) : null,\n watcherHeartbeatSecondsAgo: watcher ? Math.round((Date.now() - watcher.heartbeatMs) / 1000) : null,\n engine,\n };\n\n if (format === 'json') {\n console.log(stringifyJson(result, 2));\n } else {\n const migrated = cfg.migratedFrom !== undefined ? `, migration from v${cfg.migratedFrom} applied in memory` : '';\n console.log(`config: ${result.config} (v${SUPPORTED_CONFIG_VERSION}${migrated})`);\n // A load-time stderr warning has scrolled away by the time anyone looks here.\n if (cfg.unknownKeys) console.log(` unknown keys, ignored: ${cfg.unknownKeys.join(', ')}`);\n console.log(`tree root: ${result.treeRoot}`);\n console.log(`configDir: ${result.configDir}`);\n console.log(`cache: ${result.cache} (schema ${result.cacheSchema}; ${ctx.name} build --force to recreate)`);\n console.log(`lexical: ${result.lexicalReady ? 'ready' : `not ready (${result.lexicalError})`}`);\n console.log(`docs: ${result.docs}${result.unparseableFrontmatter > 0 ? ` (${result.unparseableFrontmatter} with unparseable frontmatter: WHERE _parse_error IS NOT NULL)` : ''}`);\n console.log('');\n console.log(featuresLine(features));\n // One authoritative block, off states included: an absent model reads as a degraded search,\n // not as an empty corpus.\n if (!e) console.log('embed: off (no preset asks for vectors)');\n else {\n console.log(`embed: ${e.provider} ${e.model}`);\n if (e.provider === 'static') console.log(` model: ${modelDir(e.model)} (${hasModel ? 'present' : `missing, ${isDownloadable(e.model) ? `fetches when build, watch, or a vector query needs it; run \\`${ctx.name} download\\` to prefetch` : `no ${MODEL_FILENAMES} in that directory`}`})`);\n if (declaredLanguages && languagesSource === 'config') console.log(` languages: ${declaredLanguages.join(', ')} (declared in embed.languages)`);\n else if (declaredLanguages && languagesSource === 'card') console.log(` languages: ${declaredLanguages.join(', ')} (declared by the model card)`);\n else if (languagesState === 'none') console.log(' languages: none declared by the model card');\n else if (languagesState === 'unresolved') console.log(` languages: unresolved (model card unreachable; run \\`${ctx.name} download\\`)`);\n // Cohere defaults to api.cohere.com with no url in the config; show it anyway so the\n // reachability probe below has something to name.\n if (e.url || reachable !== null) {\n const shown = e.url ? redactUrl(e.url) : 'https://api.cohere.com (default)';\n console.log(` url: ${shown}${reachable === null ? '' : reachable ? ' (reachable)' : ' (unreachable)'}`);\n }\n if (keyEnv) console.log(` key: env ${keyEnv.name} (${keyEnv.set ? 'set' : 'NOT SET'})`);\n if (!hasModel) console.log(' vectors: unavailable until the model is present; semantic queries fail, while lexical-only presets remain usable');\n else if (vectors) console.log(` vectors: ${vectors.embedded} embedded, ${vectors.pending} pending${vectors.pending > 0 ? ` (run \\`${ctx.name} build\\` to prepare all configured vectors)` : ''}`);\n if (detectedLanguages) {\n const total = Object.values(detectedLanguages).reduce((a, b) => a + b, 0);\n const breakdown = Object.entries(detectedLanguages)\n .sort((a, b) => b[1] - a[1])\n .map(([code, n]) => `${code} ${Math.round((n / total) * 100)}%`)\n .join(', ');\n console.log(` detected: ${breakdown} (${total} chunks classified)`);\n }\n }\n console.log('');\n for (const line of presetsLines(presets)) console.log(line);\n console.log('');\n console.log(`queries: ${result.queries} saved (${ctx.name} --list)`);\n const pid = result.watcherPid ? `pid ${result.watcherPid}, ` : '';\n console.log(result.watcherHeartbeatSecondsAgo === null ? 'watcher: none' : `watcher: ${pid}last heartbeat ${result.watcherHeartbeatSecondsAgo}s ago`);\n for (const [key, value] of Object.entries(result.engine)) console.log(`${`${store.name}:`.padEnd(10)}${key} ${value}`);\n }\n } finally {\n await store.close();\n }\n};\nexport default status;\n"],"names":["dirname","join","presetCoverage","embedConfig","featureStates","STATE_DIR","SUPPORTED_CONFIG_VERSION","languageDistribution","probeReachable","isDownloadable","MODEL_FILENAMES","modelDir","modelPresent","readLanguages","SenseError","featuresLine","presetsLines","stringifyJson","docCount","getMeta","lexicalReadiness","openStore","storeFilename","readWatchClaim","USAGE","CONFIG","FORMAT","formatOf","parse","printWarnings","vectorState","store","stmt","prepare","get","parseErrorCount","n","redactUrl","url","parsed","URL","username","password","toString","replace","embedReachable","e","provider","base","languagesInfo","languages","state","source","model","cardLanguages","undefined","length","status","ctx","values","argv","name","format","cfg","resolveConfig","config","writeMigration","lexical","opened","build","err","code","configDir","rootDir","baseDir","dbPath","watcher","result","configPath","configVersion","migratedFrom","unknownConfigKeys","unknownKeys","tree","treeRoot","cache","db","indexReady","indexError","message","lexicalReady","ready","lexicalError","error","watcherPid","String","pid","watcherHeartbeatSecondsAgo","Math","round","Date","now","heartbeatMs","console","log","migrated","warnings","process","features","hasModel","reachable","vectors","presets","declaredLanguages","languagesState","languagesSource","detectedLanguages","cacheSchema","engine","engineStatus","keyEnv","key","set","env","docs","unparseableFrontmatter","on","featuresOff","off","embed","dir","downloaded","queries","Object","keys","shown","embedded","pending","total","reduce","a","b","breakdown","entries","sort","map","line","value","padEnd","close"],"mappings":"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"}
@@ -1,8 +1,10 @@
1
- import type { ResolvedConfig } from '../config/index.js';
1
+ import type { LoadConfigOptions, ResolvedConfig } from '../config/index.js';
2
2
  export interface Ctx {
3
3
  name: string;
4
4
  argv: string[];
5
- resolveConfig(configPath?: string): ResolvedConfig;
5
+ resolveConfig(configPath?: string, options?: LoadConfigOptions & {
6
+ query?: boolean;
7
+ }): ResolvedConfig;
6
8
  usageError(message: string): never;
7
9
  }
8
10
  export type Command = (ctx: Ctx) => Promise<void> | void;
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/cli/types.ts"],"sourcesContent":["import type { ResolvedConfig } from '../config/index.ts';\n\n// Handed to every command; each command parses its own argv via shared.ts's `parse`.\nexport interface Ctx {\n name: string;\n argv: string[]; // argv after the command word\n resolveConfig(configPath?: string): ResolvedConfig;\n usageError(message: string): never;\n}\n\nexport type Command = (ctx: Ctx) => Promise<void> | void;\n"],"names":[],"mappings":"AAUA,WAAyD"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/cli/types.ts"],"sourcesContent":["import type { LoadConfigOptions, ResolvedConfig } from '../config/index.ts';\n\n// Handed to every command; each command parses its own argv via shared.ts's `parse`.\nexport interface Ctx {\n name: string;\n argv: string[]; // argv after the command word\n resolveConfig(configPath?: string, options?: LoadConfigOptions & { query?: boolean }): ResolvedConfig;\n usageError(message: string): never;\n}\n\nexport type Command = (ctx: Ctx) => Promise<void> | void;\n"],"names":[],"mappings":"AAUA,WAAyD"}
package/dist/esm/cli.js CHANGED
@@ -28,10 +28,15 @@ function usage(name) {
28
28
  ` ${name} --version`
29
29
  ].join('\n');
30
30
  }
31
- function resolveConfigFor(name, configPath) {
32
- const cfg = loadConfig(configPath);
31
+ function resolveConfigFor(name, configPath, options = {}) {
32
+ // Read the query policy before allowing a migration to write configuration state.
33
+ let cfg = loadConfig(configPath, options.query ? {
34
+ writeMigration: false
35
+ } : options);
36
+ const writeMigration = options.writeMigration !== false && (!options.query || cfg.build !== false);
37
+ if (options.query && writeMigration && cfg.migratedFrom !== undefined) cfg = loadConfig(configPath);
33
38
  if (cfg.migratedFrom !== undefined) {
34
- console.warn(`${name}: migrated ${cfg.configPath} from config version ${cfg.migratedFrom} to ${SUPPORTED_CONFIG_VERSION}`);
39
+ console.warn(!writeMigration ? `${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)` : `${name}: migrated ${cfg.configPath} from config version ${cfg.migratedFrom} to ${SUPPORTED_CONFIG_VERSION}`);
35
40
  }
36
41
  if (cfg.unknownKeys !== undefined) {
37
42
  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`);
@@ -119,7 +124,7 @@ export default async function cli(argv, name) {
119
124
  const ctx = {
120
125
  name,
121
126
  argv: argv.slice(1),
122
- resolveConfig: (configPath)=>resolveConfigFor(name, configPath),
127
+ resolveConfig: (configPath, options)=>resolveConfigFor(name, configPath, options),
123
128
  usageError (message) {
124
129
  console.error(message);
125
130
  process.exit(2);
@@ -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/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) {\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 // 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) => 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":["createRequire","parseArgs","COMMANDS","USAGE","loadConfig","SUPPORTED_CONFIG_VERSION","packageVersion","load","url","rel","pkg","name","version","usage","lines","Object","values","map","u","join","resolveConfigFor","configPath","cfg","migratedFrom","undefined","console","warn","unknownKeys","cli","argv","process","stdout","on","err","code","error","message","exitCode","first","startsWith","args","options","type","default","short","help","list","config","allowPositionals","exit","log","queries","queryName","keys","sort","RENAMED","find","query","ctx","slice","resolveConfig","usageError"],"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 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,4 +1,5 @@
1
1
  import { featureEnabled, featureStates } from '../config/index.js';
2
+ import { serialQuery } from '../lib/serial-query.js';
2
3
  import { INTERNAL_COLUMNS, scopedPaths, setupMapScope } from './scope.js';
3
4
  import { presetCoverage } from './status.js';
4
5
  // A result row is capped at SQLITE_MAX_COLUMN (2000, default); two aggregate expressions per
@@ -16,7 +17,10 @@ function chunk(items, size) {
16
17
  }
17
18
  // What is this scope: fixed-size output regardless of tree size. Coverage and features stay
18
19
  // global -- they describe the tree, not the current question.
19
- export async function mapTree(store, cfg, overrides = {}) {
20
+ export function mapTree(store, cfg, overrides = {}) {
21
+ return serialQuery(store, ()=>mapIndexed(store, cfg, overrides));
22
+ }
23
+ async function mapIndexed(store, cfg, overrides) {
20
24
  return store.transaction(async ()=>{
21
25
  await setupMapScope(store, await scopedPaths(store, cfg, overrides));
22
26
  const scopeWhere = 'WHERE "path" IN (SELECT "path" FROM _map_scope)';
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/map.ts"],"sourcesContent":["import type { FeatureName, ResolvedConfig, SearchOverrides } from '../config/index.ts';\nimport { featureEnabled, featureStates } from '../config/index.ts';\nimport type { Row } from '../output/output.ts';\nimport type { FieldStat, Store } from '../store/types.ts';\nimport { INTERNAL_COLUMNS, scopedPaths, setupMapScope } from './scope.ts';\nimport type { PresetCoverage } from './status.ts';\nimport { presetCoverage } from './status.ts';\n\nexport interface TreeMap {\n docs: { count: number; bytes: number };\n fields: Row[]; // top 20 by coverage; fieldsTotal carries the real count\n fieldsTotal: number;\n features: { on: FeatureName[]; off: FeatureName[] };\n presets: PresetCoverage[];\n hubs: Row[];\n recent: Row[];\n recentCaveat: string | null;\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 async function mapTree(store: Store, cfg: ResolvedConfig, overrides: SearchOverrides = {}): Promise<TreeMap> {\n return store.transaction(async () => {\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) as unknown as Row[];\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 Row[])\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","INTERNAL_COLUMNS","scopedPaths","setupMapScope","presetCoverage","MAP_COLUMN_CHUNK","utcSecond","ms","Date","toISOString","slice","replace","chunk","items","size","out","i","length","push","mapTree","store","cfg","overrides","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":"AACA,SAASA,cAAc,EAAEC,aAAa,QAAQ,qBAAqB;AAGnE,SAASC,gBAAgB,EAAEC,WAAW,EAAEC,aAAa,QAAQ,aAAa;AAE1E,SAASC,cAAc,QAAQ,cAAc;AAa7C,6FAA6F;AAC7F,gGAAgG;AAChG,MAAMC,mBAAmB;AAEzB,+FAA+F;AAC/F,mDAAmD;AACnD,SAASC,UAAUC,EAAU;IAC3B,OAAO,IAAIC,KAAKD,IAAIE,WAAW,GAAGC,KAAK,CAAC,GAAG,IAAIC,OAAO,CAAC,KAAK;AAC9D;AAEA,SAASC,MAASC,KAAU,EAAEC,IAAY;IACxC,MAAMC,MAAa,EAAE;IACrB,IAAK,IAAIC,IAAI,GAAGA,IAAIH,MAAMI,MAAM,EAAED,KAAKF,KAAMC,IAAIG,IAAI,CAACL,MAAMH,KAAK,CAACM,GAAGA,IAAIF;IACzE,OAAOC;AACT;AAEA,4FAA4F;AAC5F,8DAA8D;AAC9D,OAAO,eAAeI,QAAQC,KAAY,EAAEC,GAAmB,EAAEC,YAA6B,CAAC,CAAC;IAC9F,OAAOF,MAAMG,WAAW,CAAC;QACvB,MAAMpB,cAAciB,OAAO,MAAMlB,YAAYkB,OAAOC,KAAKC;QACzD,MAAME,aAAa;QACnB,MAAMC,WAAW;QAEjB,MAAMC,OAAQ,MAAM,AAAC,CAAA,MAAMN,MAAMO,OAAO,CAAC,CAAC,8EAA8E,EAAEH,YAAY,CAAA,EAAGI,GAAG;QAE5I,MAAMC,UAAU,AAAC,CAAA,MAAMT,MAAMM,IAAI,CAACG,OAAO,EAAC,EAAGC,MAAM,CAAC,CAACC,OAAS,CAAC9B,iBAAiB+B,GAAG,CAACD;QACpF,2FAA2F;QAC3F,iFAAiF;QACjF,MAAME,YAAyB,EAAE;QACjC,KAAK,MAAMC,SAAStB,MAAMiB,SAASxB,kBAAmB4B,UAAUf,IAAI,IAAK,MAAME,MAAMM,IAAI,CAACS,UAAU,CAACD,OAAOV;QAC5GS,UAAUG,IAAI,CAAC,CAACC,GAAGC,IAAMA,EAAEC,QAAQ,GAAGF,EAAEE,QAAQ;QAChD,MAAMC,SAASP,UAAUvB,KAAK,CAAC,GAAG;QAElC,MAAM+B,OAAO1C,eAAesB,KAAK,UAC3B,MAAM,AAAC,CAAA,MAAMD,MAAMO,OAAO,CAAC,CAAC,iKAAiK,EAAEF,SAAS,8CAA8C,CAAC,CAAA,EAAGiB,GAAG,KAC/P,EAAE;QAEN,MAAMC,SAAS,AAAE,CAAA,MAAM,AAAC,CAAA,MAAMvB,MAAMO,OAAO,CAAC,CAAC,kDAAkD,EAAEH,WAAW,2CAA2C,CAAC,CAAA,EAAGkB,GAAG,EAAC,EAA8CE,GAAG,CAAC,CAACC,IAAO,CAAA;gBAAEC,MAAMD,EAAEC,IAAI;gBAAEC,UAAUzC,UAAUuC,EAAEG,KAAK;YAAE,CAAA;QAEtQ,2FAA2F;QAC3F,iGAAiG;QACjG,MAAMC,YAAa,MAAM,AAAC,CAAA,MAAM7B,MAAMO,OAAO,CAAC,CAAC,sCAAsC,EAAEH,WAAW,kEAAkE,CAAC,CAAA,EAAGI,GAAG;QAC3K,MAAMsB,eAAeD,aAAavB,KAAKyB,KAAK,GAAG,KAAKF,UAAUG,CAAC,GAAG1B,KAAKyB,KAAK,GAAG,IAAI,GAAGF,UAAUG,CAAC,CAAC,IAAI,EAAE1B,KAAKyB,KAAK,CAAC,iGAAiG,CAAC,GAAG;QAExN,OAAO;YAAEzB;YAAMc;YAAQa,aAAapB,UAAUhB,MAAM;YAAEqC,UAAUtD,cAAcqB;YAAMkC,SAAS,MAAMnD,eAAegB,OAAOC;YAAMoB;YAAME;YAAQO;QAAa;IAC5J;AACF"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/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 { Row } from '../output/output.ts';\nimport type { FieldStat, Store } from '../store/types.ts';\nimport { INTERNAL_COLUMNS, scopedPaths, setupMapScope } from './scope.ts';\nimport type { PresetCoverage } from './status.ts';\nimport { presetCoverage } from './status.ts';\n\nexport interface TreeMap {\n docs: { count: number; bytes: number };\n fields: Row[]; // top 20 by coverage; fieldsTotal carries the real count\n fieldsTotal: number;\n features: { on: FeatureName[]; off: FeatureName[] };\n presets: PresetCoverage[];\n hubs: Row[];\n recent: Row[];\n recentCaveat: string | null;\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 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) as unknown as Row[];\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 Row[])\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","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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"}
@@ -2,6 +2,7 @@ import posix from 'node:path/posix';
2
2
  import { estimateTokens } from '../chunk/index.js';
3
3
  import { featureEnabled } from '../config/index.js';
4
4
  import { SenseError } from '../errors.js';
5
+ import { serialQuery } from '../lib/serial-query.js';
5
6
  import { INTERNAL_COLUMNS, scopedPaths } from './scope.js';
6
7
  // Note resolution shared by peek and path: an exact path, or a unique basename (case
7
8
  // insensitive, .md stripped).
@@ -17,7 +18,10 @@ export function resolveNote(paths, arg) {
17
18
  const PEEK_LIST_LIMIT = 20;
18
19
  // peek: everything about one note except its prose -- frontmatter, outline with line
19
20
  // ranges + token estimates (so the follow-up Read is a range, not the file), links both ways.
20
- export async function peek(store, cfg, pathArg, overrides = {}) {
21
+ export function peek(store, cfg, pathArg, overrides = {}) {
22
+ return serialQuery(store, ()=>peekIndexed(store, cfg, pathArg, overrides));
23
+ }
24
+ async function peekIndexed(store, cfg, pathArg, overrides) {
21
25
  return store.transaction(async ()=>{
22
26
  var _row__parse_error, _row__size;
23
27
  var _this;
@@ -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 type { Row } from '../output/output.ts';\nimport type { Store } from '../store/types.ts';\nimport { INTERNAL_COLUMNS, scopedPaths } from './scope.ts';\n\n// Note resolution shared by peek and path: an exact path, or a unique basename (case\n// insensitive, .md stripped).\nexport function resolveNote(paths: string[], arg: string): string {\n const exact = paths.find((p) => p === arg);\n if (exact) return exact;\n const base = posix.basename(arg).replace(/\\.md$/i, '').toLowerCase();\n const matches = paths.filter((p) => posix.basename(p).replace(/\\.md$/i, '').toLowerCase() === base);\n if (matches.length === 1) return matches[0];\n if (matches.length > 1) throw new SenseError('NOTE_AMBIGUOUS', `\"${arg}\" is ambiguous: ${matches.join(', ')}`);\n throw new SenseError('NOTE_NOT_FOUND', `no note matches \"${arg}\"`);\n}\n\nexport interface Peek {\n path: string;\n tokens: number;\n frontmatter: Row;\n // Set when the note's frontmatter was refused, so an empty frontmatter block reads as \"did\n // not parse\" rather than \"has none\". Same reason `_parse_error` sits in the row.\n parseError: string | null;\n sections: Row[];\n outbound: string[];\n backlinks: string[];\n unresolved: string[];\n // Totals before truncation: a hub can have thousands of backlinks (or a note thousands of\n // headings), and peek's whole point is bounded output.\n sectionsTotal: number;\n outboundTotal: number;\n backlinksTotal: number;\n unresolvedTotal: number;\n // Bounded k-hop expansion beyond the immediate ring already shown by outbound/backlinks\n // (depth starts at 2).\n off: FeatureName[]; // disabled features whose blocks are omitted (not empty)\n}\n\nconst PEEK_LIST_LIMIT = 20;\n\n// peek: everything about one note except its prose -- frontmatter, outline with line\n// ranges + token estimates (so the follow-up Read is a range, not the file), links both ways.\nexport async function peek(store: Store, cfg: ResolvedConfig, pathArg: string, overrides: SearchOverrides = {}): Promise<Peek> {\n return store.transaction(async () => {\n const pathsStmt = await store.prepare('SELECT \"path\" FROM frontmatter');\n const paths = ((await pathsStmt.all()) as Array<{ path: string }>).map((r) => r.path);\n const path = resolveNote(paths, pathArg);\n\n const rowStmt = await store.prepare('SELECT * FROM frontmatter WHERE \"path\" = ?');\n const row = (await rowStmt.get(path)) as Row;\n const parseError = (row._parse_error as string | null) ?? null;\n const frontmatter: Row = {};\n for (const [key, value] of Object.entries(row)) {\n if (!INTERNAL_COLUMNS.has(key) && value !== null) frontmatter[key] = value;\n }\n\n let sectionsTotal = 0;\n let sections: Row[] = [];\n if (featureEnabled(cfg, 'sections')) {\n sectionsTotal = ((await (await store.prepare('SELECT COUNT(*) AS n FROM sections WHERE \"path\" = ?')).get(path)) as { n: number }).n;\n sections = (await (await store.prepare('SELECT level, heading, start_line, end_line, tokens FROM sections WHERE \"path\" = ? ORDER BY idx LIMIT ?')).all(path, PEEK_LIST_LIMIT)) as Row[];\n }\n\n let outbound: string[] = [];\n let backlinks: string[] = [];\n let unresolved: string[] = [];\n let backlinksTotal = 0;\n await scopedPaths(store, cfg, overrides); // validates --preset/--where/--include/--exclude even though peek's own output isn't scope-filtered\n if (featureEnabled(cfg, 'links')) {\n const out = (await (await store.prepare('SELECT target, dst FROM links WHERE src = ? AND (dst IS NULL OR dst != src) ORDER BY target')).all(path)) as Array<{ target: string; dst: string | null }>;\n outbound = [...new Set(out.filter((l) => l.dst !== null).map((l) => l.dst as string))];\n unresolved = out.filter((l) => l.dst === null).map((l) => l.target);\n backlinksTotal = ((await (await store.prepare('SELECT COUNT(DISTINCT src) AS n FROM links WHERE dst = ? AND src != dst')).get(path)) as { n: number }).n;\n backlinks = ((await (await store.prepare('SELECT DISTINCT src FROM links WHERE dst = ? AND src != dst ORDER BY src LIMIT ?')).all(path, PEEK_LIST_LIMIT)) as Array<{ src: string }>).map((r) => r.src);\n }\n\n // content.text is the stripped body already computed at reconcile time (no extra file read),\n // floored by the byte/4 estimate so stripped-away syntax and frontmatter can't vanish from the price.\n const body = ((await (await store.prepare('SELECT text FROM content WHERE \"path\" = ?')).get(path)) as { text: string } | undefined)?.text;\n const byteTokens = Math.ceil(((row._size as number) ?? 0) / 4);\n const tokens = body !== undefined ? Math.max(Math.ceil(estimateTokens(body)), byteTokens) : byteTokens;\n\n return {\n path,\n tokens,\n frontmatter,\n parseError,\n sections,\n outbound: outbound.slice(0, PEEK_LIST_LIMIT),\n backlinks,\n unresolved: unresolved.slice(0, PEEK_LIST_LIMIT),\n sectionsTotal,\n outboundTotal: outbound.length,\n backlinksTotal,\n unresolvedTotal: unresolved.length,\n off: (['sections', 'links'] as FeatureName[]).filter((name) => !featureEnabled(cfg, name)),\n };\n });\n}\n"],"names":["posix","estimateTokens","featureEnabled","SenseError","INTERNAL_COLUMNS","scopedPaths","resolveNote","paths","arg","exact","find","p","base","basename","replace","toLowerCase","matches","filter","length","join","PEEK_LIST_LIMIT","peek","store","cfg","pathArg","overrides","transaction","row","pathsStmt","prepare","all","map","r","path","rowStmt","get","parseError","_parse_error","frontmatter","key","value","Object","entries","has","sectionsTotal","sections","n","outbound","backlinks","unresolved","backlinksTotal","out","Set","l","dst","target","src","body","text","byteTokens","Math","ceil","_size","tokens","undefined","max","slice","outboundTotal","unresolvedTotal","off","name"],"mappings":"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"}
1
+ {"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, scopedPaths } from './scope.ts';\n\n// Note resolution shared by peek and path: an exact path, or a unique basename (case\n// insensitive, .md stripped).\nexport function resolveNote(paths: string[], arg: string): string {\n const exact = paths.find((p) => p === arg);\n if (exact) return exact;\n const base = posix.basename(arg).replace(/\\.md$/i, '').toLowerCase();\n const matches = paths.filter((p) => posix.basename(p).replace(/\\.md$/i, '').toLowerCase() === base);\n if (matches.length === 1) return matches[0];\n if (matches.length > 1) throw new SenseError('NOTE_AMBIGUOUS', `\"${arg}\" is ambiguous: ${matches.join(', ')}`);\n throw new SenseError('NOTE_NOT_FOUND', `no note matches \"${arg}\"`);\n}\n\nexport interface Peek {\n path: string;\n tokens: number;\n frontmatter: Row;\n // Set when the note's frontmatter was refused, so an empty frontmatter block reads as \"did\n // not parse\" rather than \"has none\". Same reason `_parse_error` sits in the row.\n parseError: string | null;\n sections: Row[];\n outbound: string[];\n backlinks: string[];\n unresolved: string[];\n // Totals before truncation: a hub can have thousands of backlinks (or a note thousands of\n // headings), and peek's whole point is bounded output.\n sectionsTotal: number;\n outboundTotal: number;\n backlinksTotal: number;\n unresolvedTotal: number;\n // Bounded k-hop expansion beyond the immediate ring already shown by outbound/backlinks\n // (depth starts at 2).\n off: FeatureName[]; // disabled features whose blocks are omitted (not empty)\n}\n\nconst PEEK_LIST_LIMIT = 20;\n\n// peek: everything about one note except its prose -- frontmatter, outline with line\n// ranges + token estimates (so the follow-up Read is a range, not the file), links both ways.\nexport function peek(store: Store, cfg: ResolvedConfig, pathArg: string, overrides: SearchOverrides = {}): Promise<Peek> {\n return serialQuery(store, () => peekIndexed(store, cfg, pathArg, overrides));\n}\n\nasync function peekIndexed(store: Store, cfg: ResolvedConfig, pathArg: string, overrides: SearchOverrides): Promise<Peek> {\n return store.transaction(async () => {\n const pathsStmt = await store.prepare('SELECT \"path\" FROM frontmatter');\n const paths = ((await pathsStmt.all()) as Array<{ path: string }>).map((r) => r.path);\n const path = resolveNote(paths, pathArg);\n\n const rowStmt = await store.prepare('SELECT * FROM frontmatter WHERE \"path\" = ?');\n const row = (await rowStmt.get(path)) as Row;\n const parseError = (row._parse_error as string | null) ?? null;\n const frontmatter: Row = {};\n for (const [key, value] of Object.entries(row)) {\n if (!INTERNAL_COLUMNS.has(key) && value !== null) frontmatter[key] = value;\n }\n\n let sectionsTotal = 0;\n let sections: Row[] = [];\n if (featureEnabled(cfg, 'sections')) {\n sectionsTotal = ((await (await store.prepare('SELECT COUNT(*) AS n FROM sections WHERE \"path\" = ?')).get(path)) as { n: number }).n;\n sections = (await (await store.prepare('SELECT level, heading, start_line, end_line, tokens FROM sections WHERE \"path\" = ? ORDER BY idx LIMIT ?')).all(path, PEEK_LIST_LIMIT)) as Row[];\n }\n\n let outbound: string[] = [];\n let backlinks: string[] = [];\n let unresolved: string[] = [];\n let backlinksTotal = 0;\n await scopedPaths(store, cfg, overrides); // validates --preset/--where/--include/--exclude even though peek's own output isn't scope-filtered\n if (featureEnabled(cfg, 'links')) {\n const out = (await (await store.prepare('SELECT target, dst FROM links WHERE src = ? AND (dst IS NULL OR dst != src) ORDER BY target')).all(path)) as Array<{ target: string; dst: string | null }>;\n outbound = [...new Set(out.filter((l) => l.dst !== null).map((l) => l.dst as string))];\n unresolved = out.filter((l) => l.dst === null).map((l) => l.target);\n backlinksTotal = ((await (await store.prepare('SELECT COUNT(DISTINCT src) AS n FROM links WHERE dst = ? AND src != dst')).get(path)) as { n: number }).n;\n backlinks = ((await (await store.prepare('SELECT DISTINCT src FROM links WHERE dst = ? AND src != dst ORDER BY src LIMIT ?')).all(path, PEEK_LIST_LIMIT)) as Array<{ src: string }>).map((r) => r.src);\n }\n\n // content.text is the stripped body already computed at reconcile time (no extra file read),\n // floored by the byte/4 estimate so stripped-away syntax and frontmatter can't vanish from the price.\n const body = ((await (await store.prepare('SELECT text FROM content WHERE \"path\" = ?')).get(path)) as { text: string } | undefined)?.text;\n const byteTokens = Math.ceil(((row._size as number) ?? 0) / 4);\n const tokens = body !== undefined ? Math.max(Math.ceil(estimateTokens(body)), byteTokens) : byteTokens;\n\n return {\n path,\n tokens,\n frontmatter,\n parseError,\n sections,\n outbound: outbound.slice(0, PEEK_LIST_LIMIT),\n backlinks,\n unresolved: unresolved.slice(0, PEEK_LIST_LIMIT),\n sectionsTotal,\n outboundTotal: outbound.length,\n backlinksTotal,\n unresolvedTotal: unresolved.length,\n off: (['sections', 'links'] as FeatureName[]).filter((name) => !featureEnabled(cfg, name)),\n };\n });\n}\n"],"names":["posix","estimateTokens","featureEnabled","SenseError","serialQuery","INTERNAL_COLUMNS","scopedPaths","resolveNote","paths","arg","exact","find","p","base","basename","replace","toLowerCase","matches","filter","length","join","PEEK_LIST_LIMIT","peek","store","cfg","pathArg","overrides","peekIndexed","transaction","row","pathsStmt","prepare","all","map","r","path","rowStmt","get","parseError","_parse_error","frontmatter","key","value","Object","entries","has","sectionsTotal","sections","n","outbound","backlinks","unresolved","backlinksTotal","out","Set","l","dst","target","src","body","text","byteTokens","Math","ceil","_size","tokens","undefined","max","slice","outboundTotal","unresolvedTotal","off","name"],"mappings":"AAAA,OAAOA,WAAW,kBAAkB;AACpC,SAASC,cAAc,QAAQ,oBAAoB;AAEnD,SAASC,cAAc,QAAQ,qBAAqB;AACpD,SAASC,UAAU,QAAQ,eAAe;AAC1C,SAASC,WAAW,QAAQ,yBAAyB;AAGrD,SAASC,gBAAgB,EAAEC,WAAW,QAAQ,aAAa;AAE3D,qFAAqF;AACrF,8BAA8B;AAC9B,OAAO,SAASC,YAAYC,KAAe,EAAEC,GAAW;IACtD,MAAMC,QAAQF,MAAMG,IAAI,CAAC,CAACC,IAAMA,MAAMH;IACtC,IAAIC,OAAO,OAAOA;IAClB,MAAMG,OAAOb,MAAMc,QAAQ,CAACL,KAAKM,OAAO,CAAC,UAAU,IAAIC,WAAW;IAClE,MAAMC,UAAUT,MAAMU,MAAM,CAAC,CAACN,IAAMZ,MAAMc,QAAQ,CAACF,GAAGG,OAAO,CAAC,UAAU,IAAIC,WAAW,OAAOH;IAC9F,IAAII,QAAQE,MAAM,KAAK,GAAG,OAAOF,OAAO,CAAC,EAAE;IAC3C,IAAIA,QAAQE,MAAM,GAAG,GAAG,MAAM,IAAIhB,WAAW,kBAAkB,CAAC,CAAC,EAAEM,IAAI,gBAAgB,EAAEQ,QAAQG,IAAI,CAAC,OAAO;IAC7G,MAAM,IAAIjB,WAAW,kBAAkB,CAAC,iBAAiB,EAAEM,IAAI,CAAC,CAAC;AACnE;AAwBA,MAAMY,kBAAkB;AAExB,qFAAqF;AACrF,8FAA8F;AAC9F,OAAO,SAASC,KAAKC,KAAY,EAAEC,GAAmB,EAAEC,OAAe,EAAEC,YAA6B,CAAC,CAAC;IACtG,OAAOtB,YAAYmB,OAAO,IAAMI,YAAYJ,OAAOC,KAAKC,SAASC;AACnE;AAEA,eAAeC,YAAYJ,KAAY,EAAEC,GAAmB,EAAEC,OAAe,EAAEC,SAA0B;IACvG,OAAOH,MAAMK,WAAW,CAAC;YAOHC,mBA6BWA;YADhB;QAlCf,MAAMC,YAAY,MAAMP,MAAMQ,OAAO,CAAC;QACtC,MAAMvB,QAAQ,AAAE,CAAA,MAAMsB,UAAUE,GAAG,EAAC,EAA+BC,GAAG,CAAC,CAACC,IAAMA,EAAEC,IAAI;QACpF,MAAMA,OAAO5B,YAAYC,OAAOiB;QAEhC,MAAMW,UAAU,MAAMb,MAAMQ,OAAO,CAAC;QACpC,MAAMF,MAAO,MAAMO,QAAQC,GAAG,CAACF;QAC/B,MAAMG,cAAcT,oBAAAA,IAAIU,YAAY,cAAhBV,+BAAAA,oBAAsC;QAC1D,MAAMW,cAAmB,CAAC;QAC1B,KAAK,MAAM,CAACC,KAAKC,MAAM,IAAIC,OAAOC,OAAO,CAACf,KAAM;YAC9C,IAAI,CAACxB,iBAAiBwC,GAAG,CAACJ,QAAQC,UAAU,MAAMF,WAAW,CAACC,IAAI,GAAGC;QACvE;QAEA,IAAII,gBAAgB;QACpB,IAAIC,WAAkB,EAAE;QACxB,IAAI7C,eAAesB,KAAK,aAAa;YACnCsB,gBAAgB,AAAE,CAAA,MAAM,AAAC,CAAA,MAAMvB,MAAMQ,OAAO,CAAC,sDAAqD,EAAGM,GAAG,CAACF,KAAI,EAAqBa,CAAC;YACnID,WAAY,MAAM,AAAC,CAAA,MAAMxB,MAAMQ,OAAO,CAAC,0GAAyG,EAAGC,GAAG,CAACG,MAAMd;QAC/J;QAEA,IAAI4B,WAAqB,EAAE;QAC3B,IAAIC,YAAsB,EAAE;QAC5B,IAAIC,aAAuB,EAAE;QAC7B,IAAIC,iBAAiB;QACrB,MAAM9C,YAAYiB,OAAOC,KAAKE,YAAY,oGAAoG;QAC9I,IAAIxB,eAAesB,KAAK,UAAU;YAChC,MAAM6B,MAAO,MAAM,AAAC,CAAA,MAAM9B,MAAMQ,OAAO,CAAC,8FAA6F,EAAGC,GAAG,CAACG;YAC5Ic,WAAW;mBAAI,IAAIK,IAAID,IAAInC,MAAM,CAAC,CAACqC,IAAMA,EAAEC,GAAG,KAAK,MAAMvB,GAAG,CAAC,CAACsB,IAAMA,EAAEC,GAAG;aAAa;YACtFL,aAAaE,IAAInC,MAAM,CAAC,CAACqC,IAAMA,EAAEC,GAAG,KAAK,MAAMvB,GAAG,CAAC,CAACsB,IAAMA,EAAEE,MAAM;YAClEL,iBAAiB,AAAE,CAAA,MAAM,AAAC,CAAA,MAAM7B,MAAMQ,OAAO,CAAC,0EAAyE,EAAGM,GAAG,CAACF,KAAI,EAAqBa,CAAC;YACxJE,YAAY,AAAE,CAAA,MAAM,AAAC,CAAA,MAAM3B,MAAMQ,OAAO,CAAC,mFAAkF,EAAGC,GAAG,CAACG,MAAMd,gBAAe,EAA8BY,GAAG,CAAC,CAACC,IAAMA,EAAEwB,GAAG;QACvM;QAEA,6FAA6F;QAC7F,sGAAsG;QACtG,MAAMC,QAAS,QAAA,MAAM,AAAC,CAAA,MAAMpC,MAAMQ,OAAO,CAAC,4CAA2C,EAAGM,GAAG,CAACF,mBAA7E,4BAAF,AAAE,MAAsHyB,IAAI;QACzI,MAAMC,aAAaC,KAAKC,IAAI,CAAC,EAAElC,aAAAA,IAAImC,KAAK,cAATnC,wBAAAA,aAAwB,KAAK;QAC5D,MAAMoC,SAASN,SAASO,YAAYJ,KAAKK,GAAG,CAACL,KAAKC,IAAI,CAAC9D,eAAe0D,QAAQE,cAAcA;QAE5F,OAAO;YACL1B;YACA8B;YACAzB;YACAF;YACAS;YACAE,UAAUA,SAASmB,KAAK,CAAC,GAAG/C;YAC5B6B;YACAC,YAAYA,WAAWiB,KAAK,CAAC,GAAG/C;YAChCyB;YACAuB,eAAepB,SAAS9B,MAAM;YAC9BiC;YACAkB,iBAAiBnB,WAAWhC,MAAM;YAClCoD,KAAK,AAAC;gBAAC;gBAAY;aAAQ,CAAmBrD,MAAM,CAAC,CAACsD,OAAS,CAACtE,eAAesB,KAAKgD;QACtF;IACF;AACF"}
@@ -1,5 +1,6 @@
1
1
  import type { ResolvedConfig, SearchOverrides } from '../config/index.js';
2
2
  import type { Store } from '../store/types.js';
3
+ export declare function resolveRelatedSeed(store: Store, pathArg: string): Promise<string>;
3
4
  export declare function relatedNotes(store: Store, cfg: ResolvedConfig, pathArg: string, overrides: SearchOverrides, k: number): Promise<Array<{
4
5
  path: string;
5
6
  similarity: number;
@@ -1,15 +1,21 @@
1
1
  import { embedConfig, embedEnabled, resolveSearch } from '../config/index.js';
2
- import { embedPending, hasEmbedding, similarNotes } from '../embed/query.js';
2
+ import { ensureDocumentEmbeddings, hasEmbedding, similarNotes } from '../embed/query.js';
3
3
  import { localModelMissing, MODEL_FILENAMES } from '../embed/store.js';
4
4
  import { SenseError } from '../errors.js';
5
+ import { serialQuery } from '../lib/serial-query.js';
5
6
  import { resolveNote } from './peek.js';
6
7
  import { scopedPaths, scopeHasEmbeddings } from './scope.js';
8
+ export async function resolveRelatedSeed(store, pathArg) {
9
+ const paths = (await (await store.prepare('SELECT "path" FROM frontmatter')).all()).map((row)=>row.path);
10
+ return resolveNote(paths, pathArg);
11
+ }
7
12
  // Notes most similar by cosine, excluding self and everything already linked either way.
8
13
  // Its own command, not a peek section: a full embeddings scan is ~480ms at 26k notes.
9
- export async function relatedNotes(store, cfg, pathArg, overrides, k) {
10
- var _cfg_rootDir;
11
- const paths = (await (await store.prepare('SELECT "path" FROM frontmatter')).all()).map((r)=>r.path);
12
- const path = resolveNote(paths, pathArg);
14
+ export function relatedNotes(store, cfg, pathArg, overrides, k) {
15
+ return serialQuery(store, ()=>relatedIndexed(store, cfg, pathArg, overrides, k));
16
+ }
17
+ async function relatedIndexed(store, cfg, pathArg, overrides, k) {
18
+ const path = await resolveRelatedSeed(store, pathArg);
13
19
  const outbound = (await (await store.prepare('SELECT DISTINCT dst FROM links WHERE src = ? AND dst IS NOT NULL AND dst != src')).all(path)).map((r)=>r.dst);
14
20
  const backlinks = (await (await store.prepare('SELECT DISTINCT src FROM links WHERE dst = ? AND src != dst')).all(path)).map((r)=>r.src);
15
21
  const exclude = new Set([
@@ -28,16 +34,19 @@ export async function relatedNotes(store, cfg, pathArg, overrides, k) {
28
34
  if (effective.signals.vectors === undefined) {
29
35
  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`);
30
36
  }
31
- // A downloadable HF id proceeds -- embedPending's getProvider call fetches it lazily on
32
- // consent. Only a local path with missing files can never fetch itself.
37
+ // A downloadable HF id can be fetched by preparation. Only a local path with missing files
38
+ // can never fetch itself.
33
39
  const e = embedConfig(cfg); // embedEnabled(cfg) above guarantees this is set
34
40
  if (localModelMissing(e)) {
35
41
  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)`);
36
42
  }
37
43
  const allowed = await scopedPaths(store, cfg, overrides);
38
- // Top up pending rows before the seed check, or a fresh index reports every note as
39
- // having no indexed text until some search has run.
40
- await embedPending(store, cfg, (_cfg_rootDir = cfg.rootDir) !== null && _cfg_rootDir !== void 0 ? _cfg_rootDir : cfg.baseDir);
44
+ // The seed may sit outside a narrowed candidate scope; both it and every eligible result must
45
+ // be ready, while pending vectors in unrelated notes do not block this operation.
46
+ await ensureDocumentEmbeddings(store, cfg, new Set([
47
+ ...allowed,
48
+ path
49
+ ]));
41
50
  if (!await hasEmbedding(store, path)) {
42
51
  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`);
43
52
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/related.ts"],"sourcesContent":["import type { ResolvedConfig, SearchOverrides } from '../config/index.ts';\nimport { embedConfig, embedEnabled, resolveSearch } from '../config/index.ts';\nimport { embedPending, hasEmbedding, similarNotes } from '../embed/query.ts';\nimport { localModelMissing, MODEL_FILENAMES } from '../embed/store.ts';\nimport { SenseError } from '../errors.ts';\nimport type { Store } from '../store/types.ts';\nimport { resolveNote } from './peek.ts';\nimport { scopedPaths, scopeHasEmbeddings } from './scope.ts';\n\n// Notes most similar by cosine, excluding self and everything already linked either way.\n// Its own command, not a peek section: a full embeddings scan is ~480ms at 26k notes.\nexport async function relatedNotes(store: Store, cfg: ResolvedConfig, pathArg: string, overrides: SearchOverrides, k: number): Promise<Array<{ path: string; similarity: number }>> {\n const paths = ((await (await store.prepare('SELECT \"path\" FROM frontmatter')).all()) as Array<{ path: string }>).map((r) => r.path);\n const path = resolveNote(paths, pathArg);\n\n const outbound = ((await (await store.prepare('SELECT DISTINCT dst FROM links WHERE src = ? AND dst IS NOT NULL AND dst != src')).all(path)) as Array<{ dst: string }>).map((r) => r.dst);\n const backlinks = ((await (await store.prepare('SELECT DISTINCT src FROM links WHERE dst = ? AND src != dst')).all(path)) as Array<{ src: string }>).map((r) => r.src);\n const exclude = new Set([path, ...outbound, ...backlinks]);\n\n // Vectors are the only signal `related` has, so every way of not having them is an error\n // naming the cause; an empty 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 proceeds -- embedPending's getProvider call fetches it lazily on\n // consent. Only a local path with missing files can never fetch itself.\n const e = embedConfig(cfg); // embedEnabled(cfg) above guarantees this is set\n if (localModelMissing(e)) {\n throw new SenseError('EMBED_MODEL_MISSING', `related ranks notes by meaning, so it needs the embedding model, but the local model path \"${e.model}\" is missing ${MODEL_FILENAMES}; point embed.model at a directory containing them (search still works without it, on words and links)`);\n }\n const allowed = await scopedPaths(store, cfg, overrides);\n // Top up pending rows before the seed check, or a fresh index reports every note as\n // having no indexed text until some search has run.\n await embedPending(store, cfg, cfg.rootDir ?? cfg.baseDir);\n if (!(await hasEmbedding(store, path))) {\n throw new SenseError('NOTE_NOT_EMBEDDED', `${path} has no indexed text to compare -- a note that is frontmatter only, or empty, has nothing to rank by meaning`);\n }\n if (!(await scopeHasEmbeddings(store, cfg, allowed))) return [];\n return similarNotes(store, cfg, path, { exclude, allowed, k });\n}\n"],"names":["embedConfig","embedEnabled","resolveSearch","embedPending","hasEmbedding","similarNotes","localModelMissing","MODEL_FILENAMES","SenseError","resolveNote","scopedPaths","scopeHasEmbeddings","relatedNotes","store","cfg","pathArg","overrides","k","paths","prepare","all","map","r","path","outbound","dst","backlinks","src","exclude","Set","effective","signals","vectors","undefined","presetName","e","model","allowed","rootDir","baseDir"],"mappings":"AACA,SAASA,WAAW,EAAEC,YAAY,EAAEC,aAAa,QAAQ,qBAAqB;AAC9E,SAASC,YAAY,EAAEC,YAAY,EAAEC,YAAY,QAAQ,oBAAoB;AAC7E,SAASC,iBAAiB,EAAEC,eAAe,QAAQ,oBAAoB;AACvE,SAASC,UAAU,QAAQ,eAAe;AAE1C,SAASC,WAAW,QAAQ,YAAY;AACxC,SAASC,WAAW,EAAEC,kBAAkB,QAAQ,aAAa;AAE7D,yFAAyF;AACzF,sFAAsF;AACtF,OAAO,eAAeC,aAAaC,KAAY,EAAEC,GAAmB,EAAEC,OAAe,EAAEC,SAA0B,EAAEC,CAAS;QA4B3FH;IA3B/B,MAAMI,QAAQ,AAAE,CAAA,MAAM,AAAC,CAAA,MAAML,MAAMM,OAAO,CAAC,iCAAgC,EAAGC,GAAG,EAAC,EAA+BC,GAAG,CAAC,CAACC,IAAMA,EAAEC,IAAI;IAClI,MAAMA,OAAOd,YAAYS,OAAOH;IAEhC,MAAMS,WAAW,AAAE,CAAA,MAAM,AAAC,CAAA,MAAMX,MAAMM,OAAO,CAAC,kFAAiF,EAAGC,GAAG,CAACG,KAAI,EAA8BF,GAAG,CAAC,CAACC,IAAMA,EAAEG,GAAG;IACxL,MAAMC,YAAY,AAAE,CAAA,MAAM,AAAC,CAAA,MAAMb,MAAMM,OAAO,CAAC,8DAA6D,EAAGC,GAAG,CAACG,KAAI,EAA8BF,GAAG,CAAC,CAACC,IAAMA,EAAEK,GAAG;IACrK,MAAMC,UAAU,IAAIC,IAAI;QAACN;WAASC;WAAaE;KAAU;IAEzD,yFAAyF;IACzF,8EAA8E;IAC9E,MAAMI,YAAY5B,cAAcY,KAAKE;IACrC,IAAI,CAACf,aAAaa,MAAM;QACtB,MAAM,IAAIN,WAAW,kBAAkB;IACzC;IACA,0FAA0F;IAC1F,2FAA2F;IAC3F,IAAIsB,UAAUC,OAAO,CAACC,OAAO,KAAKC,WAAW;QAC3C,MAAM,IAAIzB,WAAW,uBAAuB,CAAC,QAAQ,EAAEsB,UAAUI,UAAU,CAAC,uKAAuK,CAAC;IACtP;IACA,wFAAwF;IACxF,wEAAwE;IACxE,MAAMC,IAAInC,YAAYc,MAAM,iDAAiD;IAC7E,IAAIR,kBAAkB6B,IAAI;QACxB,MAAM,IAAI3B,WAAW,uBAAuB,CAAC,2FAA2F,EAAE2B,EAAEC,KAAK,CAAC,aAAa,EAAE7B,gBAAgB,sGAAsG,CAAC;IAC1R;IACA,MAAM8B,UAAU,MAAM3B,YAAYG,OAAOC,KAAKE;IAC9C,oFAAoF;IACpF,oDAAoD;IACpD,MAAMb,aAAaU,OAAOC,MAAKA,eAAAA,IAAIwB,OAAO,cAAXxB,0BAAAA,eAAeA,IAAIyB,OAAO;IACzD,IAAI,CAAE,MAAMnC,aAAaS,OAAOU,OAAQ;QACtC,MAAM,IAAIf,WAAW,qBAAqB,GAAGe,KAAK,4GAA4G,CAAC;IACjK;IACA,IAAI,CAAE,MAAMZ,mBAAmBE,OAAOC,KAAKuB,UAAW,OAAO,EAAE;IAC/D,OAAOhC,aAAaQ,OAAOC,KAAKS,MAAM;QAAEK;QAASS;QAASpB;IAAE;AAC9D"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/related.ts"],"sourcesContent":["import type { ResolvedConfig, SearchOverrides } from '../config/index.ts';\nimport { embedConfig, embedEnabled, resolveSearch } from '../config/index.ts';\nimport { 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 } from './scope.ts';\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// Notes most similar by cosine, excluding self and everything already linked either way.\n// Its own command, not a peek section: a full embeddings scan is ~480ms at 26k notes.\nexport function relatedNotes(store: Store, cfg: ResolvedConfig, pathArg: string, overrides: SearchOverrides, k: number): Promise<Array<{ path: string; similarity: number }>> {\n return serialQuery(store, () => relatedIndexed(store, cfg, pathArg, overrides, k));\n}\n\nasync function relatedIndexed(store: Store, cfg: ResolvedConfig, pathArg: string, overrides: SearchOverrides, k: number): Promise<Array<{ path: string; similarity: number }>> {\n const path = await resolveRelatedSeed(store, pathArg);\n\n const outbound = ((await (await store.prepare('SELECT DISTINCT dst FROM links WHERE src = ? AND dst IS NOT NULL AND dst != src')).all(path)) as Array<{ dst: string }>).map((r) => r.dst);\n const backlinks = ((await (await store.prepare('SELECT DISTINCT src FROM links WHERE dst = ? AND src != dst')).all(path)) as Array<{ src: string }>).map((r) => r.src);\n const exclude = new Set([path, ...outbound, ...backlinks]);\n\n // Vectors are the only signal `related` has, so every way of not having them is an error\n // naming the cause; an empty 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 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 similarNotes(store, cfg, path, { exclude, allowed, k });\n}\n"],"names":["embedConfig","embedEnabled","resolveSearch","ensureDocumentEmbeddings","hasEmbedding","similarNotes","localModelMissing","MODEL_FILENAMES","SenseError","serialQuery","resolveNote","scopedPaths","scopeHasEmbeddings","resolveRelatedSeed","store","pathArg","paths","prepare","all","map","row","path","relatedNotes","cfg","overrides","k","relatedIndexed","outbound","r","dst","backlinks","src","exclude","Set","effective","signals","vectors","undefined","presetName","e","model","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,QAAQ,aAAa;AAE7D,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,OAAOX,YAAYM,OAAOD;AAC5B;AAEA,yFAAyF;AACzF,sFAAsF;AACtF,OAAO,SAASO,aAAaR,KAAY,EAAES,GAAmB,EAAER,OAAe,EAAES,SAA0B,EAAEC,CAAS;IACpH,OAAOhB,YAAYK,OAAO,IAAMY,eAAeZ,OAAOS,KAAKR,SAASS,WAAWC;AACjF;AAEA,eAAeC,eAAeZ,KAAY,EAAES,GAAmB,EAAER,OAAe,EAAES,SAA0B,EAAEC,CAAS;IACrH,MAAMJ,OAAO,MAAMR,mBAAmBC,OAAOC;IAE7C,MAAMY,WAAW,AAAE,CAAA,MAAM,AAAC,CAAA,MAAMb,MAAMG,OAAO,CAAC,kFAAiF,EAAGC,GAAG,CAACG,KAAI,EAA8BF,GAAG,CAAC,CAACS,IAAMA,EAAEC,GAAG;IACxL,MAAMC,YAAY,AAAE,CAAA,MAAM,AAAC,CAAA,MAAMhB,MAAMG,OAAO,CAAC,8DAA6D,EAAGC,GAAG,CAACG,KAAI,EAA8BF,GAAG,CAAC,CAACS,IAAMA,EAAEG,GAAG;IACrK,MAAMC,UAAU,IAAIC,IAAI;QAACZ;WAASM;WAAaG;KAAU;IAEzD,yFAAyF;IACzF,8EAA8E;IAC9E,MAAMI,YAAYhC,cAAcqB,KAAKC;IACrC,IAAI,CAACvB,aAAasB,MAAM;QACtB,MAAM,IAAIf,WAAW,kBAAkB;IACzC;IACA,0FAA0F;IAC1F,2FAA2F;IAC3F,IAAI0B,UAAUC,OAAO,CAACC,OAAO,KAAKC,WAAW;QAC3C,MAAM,IAAI7B,WAAW,uBAAuB,CAAC,QAAQ,EAAE0B,UAAUI,UAAU,CAAC,uKAAuK,CAAC;IACtP;IACA,2FAA2F;IAC3F,0BAA0B;IAC1B,MAAMC,IAAIvC,YAAYuB,MAAM,iDAAiD;IAC7E,IAAIjB,kBAAkBiC,IAAI;QACxB,MAAM,IAAI/B,WAAW,uBAAuB,CAAC,2FAA2F,EAAE+B,EAAEC,KAAK,CAAC,aAAa,EAAEjC,gBAAgB,sGAAsG,CAAC;IAC1R;IACA,MAAMkC,UAAU,MAAM9B,YAAYG,OAAOS,KAAKC;IAC9C,8FAA8F;IAC9F,kFAAkF;IAClF,MAAMrB,yBAAyBW,OAAOS,KAAK,IAAIU,IAAI;WAAIQ;QAASpB;KAAK;IACrE,IAAI,CAAE,MAAMjB,aAAaU,OAAOO,OAAQ;QACtC,MAAM,IAAIb,WAAW,qBAAqB,GAAGa,KAAK,4GAA4G,CAAC;IACjK;IACA,IAAI,CAAE,MAAMT,mBAAmBE,OAAOS,KAAKkB,UAAW,OAAO,EAAE;IAC/D,OAAOpC,aAAaS,OAAOS,KAAKF,MAAM;QAAEW;QAASS;QAAShB;IAAE;AAC9D"}
@@ -18,7 +18,7 @@ export function inScope(path, include, exclude) {
18
18
  }
19
19
  export async function scopeHasEmbeddings(store, cfg, scopedPaths) {
20
20
  if (!anyPresetEmbeds(cfg)) return false; // the embeddings table doesn't exist at all in this case
21
- const stmt = await store.prepare('SELECT DISTINCT "path" FROM embeddings');
21
+ const stmt = await store.prepare('SELECT DISTINCT "path" FROM embeddings WHERE vector IS NOT NULL');
22
22
  const rows = await stmt.all();
23
23
  return rows.some((r)=>scopedPaths.has(r.path));
24
24
  }
@@ -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';\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');\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\nexport async function setupMapScope(store: Store, paths: Set<string>): Promise<void> {\n await materializeScope(store, '_map_scope', paths);\n}\n"],"names":["matchesGlob","anyPresetEmbeds","resolveSearch","columnHint","INTERNAL_COLUMNS","Set","inScope","path","include","exclude","some","g","scopeHasEmbeddings","store","cfg","scopedPaths","stmt","prepare","rows","all","r","has","rawScope","overrides","allPaths","effective","adHocScope","undefined","noExclude","presetName","map","paths","allFrontmatterPaths","filter","p","narrowByWhere","where","whereRows","err","docs","columns","wherePaths","materializeScope","table","exec","runBatch","setupMapScope"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/scope.ts"],"sourcesContent":["import { matchesGlob } from 'node:path';\nimport type { ResolvedConfig, SearchOverrides } from '../config/index.ts';\nimport { anyPresetEmbeds, resolveSearch } from '../config/index.ts';\nimport { columnHint } from '../output/column-hint.ts';\nimport type { Store } from '../store/types.ts';\n\nexport const INTERNAL_COLUMNS = new Set(['path', '_mtime', '_ctime', '_size', '_rank', '_parse_error']);\n\n// node:path's matchesGlob is experimental (stable behind an unstable-API flag) 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\nexport async function setupMapScope(store: Store, paths: Set<string>): Promise<void> {\n await materializeScope(store, '_map_scope', paths);\n}\n"],"names":["matchesGlob","anyPresetEmbeds","resolveSearch","columnHint","INTERNAL_COLUMNS","Set","inScope","path","include","exclude","some","g","scopeHasEmbeddings","store","cfg","scopedPaths","stmt","prepare","rows","all","r","has","rawScope","overrides","allPaths","effective","adHocScope","undefined","noExclude","presetName","map","paths","allFrontmatterPaths","filter","p","narrowByWhere","where","whereRows","err","docs","columns","wherePaths","materializeScope","table","exec","runBatch","setupMapScope"],"mappings":"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"}
@@ -1,9 +1,9 @@
1
- import { readFileSync } from 'node:fs';
2
- import { join } from 'node:path';
3
1
  import { stemmer } from 'stemmer';
4
2
  import { embedConfig, featureEnabled, resolveSearch } from '../config/index.js';
3
+ import { ensureDocumentEmbeddings } from '../embed/query.js';
5
4
  import { localModelMissing, MODEL_FILENAMES } from '../embed/store.js';
6
5
  import { SenseError } from '../errors.js';
6
+ import { serialQuery } from '../lib/serial-query.js';
7
7
  import { bareTermSyntaxError, searchError } from '../output/search-error.js';
8
8
  import { foldForSearch, hasUnspacedRun, searchTokens, unspacedRuns } from '../text/segment.js';
9
9
  import { materializeScope, narrowByWhere, rawScope, scopeHasEmbeddings } from './scope.js';
@@ -297,15 +297,12 @@ export async function hydrateSearchRows(store, cfg, rows, matchedPaths, terms, o
297
297
  const bareTerms = extractBareTerms(terms);
298
298
  const charLimit = (_opts_snippetCharLimit = opts.snippetCharLimit) !== null && _opts_snippetCharLimit !== void 0 ? _opts_snippetCharLimit : SNIPPET_CHAR_LIMIT_DEFAULT;
299
299
  const countLimit = (_opts_snippetCountLimit = opts.snippetCountLimit) !== null && _opts_snippetCountLimit !== void 0 ? _opts_snippetCountLimit : SNIPPET_COUNT_LIMIT_DEFAULT;
300
+ const sourceStmt = await store.prepare('SELECT text FROM indexed_sources WHERE "path" = ?');
300
301
  for (const row of rows){
301
302
  if (!matchedPaths.has(row.path)) continue;
302
- let text;
303
- try {
304
- var _cfg_rootDir;
305
- text = readFileSync(join((_cfg_rootDir = cfg.rootDir) !== null && _cfg_rootDir !== void 0 ? _cfg_rootDir : cfg.baseDir, row.path), 'utf8');
306
- } catch {
307
- continue; // vanished since the match; leave snippets/lines empty rather than throw
308
- }
303
+ const source = await sourceStmt.get(row.path);
304
+ 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`);
305
+ const text = source.text;
309
306
  const { snippets, offset } = computeSnippets(text, bareTerms, charLimit, countLimit);
310
307
  row.snippets = snippets;
311
308
  if (row.lines == null) row.lines = featureEnabled(cfg, 'sections') ? await lineRangeFor(store, row.path, lineNumberAt(text, offset)) : null;
@@ -313,7 +310,10 @@ export async function hydrateSearchRows(store, cfg, rows, matchedPaths, terms, o
313
310
  }
314
311
  // The declared or defaulted signals compose via RRF; `via` names which ones produced each row.
315
312
  // `opts` arrives already resolved (config.ts:resolveSearch).
316
- export async function search(store, cfg, terms, opts = {}) {
313
+ export function search(store, cfg, terms, opts = {}) {
314
+ return serialQuery(store, ()=>searchIndexed(store, cfg, terms, opts));
315
+ }
316
+ async function searchIndexed(store, cfg, terms, opts) {
317
317
  validateSearchOptions(opts);
318
318
  const effective = resolveSearch(cfg, opts);
319
319
  const { k, signals } = effective;
@@ -337,6 +337,7 @@ export async function search(store, cfg, terms, opts = {}) {
337
337
  if (localModelMissing(e)) {
338
338
  throw new SenseError('EMBED_MODEL_MISSING', `preset "${effective.presetName}" searches with vectors, but the local model path "${e.model}" is missing ${MODEL_FILENAMES}; point embed.model at a directory containing them, or drop "vectors" from that preset's signals to search without them`);
339
339
  }
340
+ await ensureDocumentEmbeddings(store, cfg, allowedPaths);
340
341
  }
341
342
  const semanticEnabled = wantsVectors && await scopeHasEmbeddings(store, cfg, allowedPaths);
342
343
  // --where applies inside the candidate query (a post-filter would drop matches ranked past