sensemaking 0.24.4 → 0.24.5

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 (123) hide show
  1. package/README.md +16 -12
  2. package/dist/cjs/cli/status.js +14 -24
  3. package/dist/cjs/cli/status.js.map +1 -1
  4. package/dist/cjs/commands/search.js +22 -0
  5. package/dist/cjs/commands/search.js.map +1 -1
  6. package/dist/cjs/config/types.js.map +1 -1
  7. package/dist/cjs/errors.d.cts +1 -1
  8. package/dist/cjs/errors.d.ts +1 -1
  9. package/dist/cjs/errors.js.map +1 -1
  10. package/dist/cjs/lib/worker-file.d.cts +1 -0
  11. package/dist/cjs/lib/worker-file.d.ts +1 -0
  12. package/dist/cjs/lib/worker-file.js +34 -0
  13. package/dist/cjs/lib/worker-file.js.map +1 -0
  14. package/dist/cjs/output/search-error.js +38 -0
  15. package/dist/cjs/output/search-error.js.map +1 -1
  16. package/dist/cjs/scan/pool.js +2 -23
  17. package/dist/cjs/scan/pool.js.map +1 -1
  18. package/dist/cjs/store/builder.d.cts +2 -1
  19. package/dist/cjs/store/builder.d.ts +2 -1
  20. package/dist/cjs/store/builder.js +4 -4
  21. package/dist/cjs/store/builder.js.map +1 -1
  22. package/dist/cjs/store/duckdb/lexical.d.cts +1 -0
  23. package/dist/cjs/store/duckdb/lexical.d.ts +1 -0
  24. package/dist/cjs/store/duckdb/lexical.js +28 -9
  25. package/dist/cjs/store/duckdb/lexical.js.map +1 -1
  26. package/dist/cjs/store/duckdb/native.d.cts +2 -0
  27. package/dist/cjs/store/duckdb/native.d.ts +2 -0
  28. package/dist/cjs/store/duckdb/native.js +11 -1
  29. package/dist/cjs/store/duckdb/native.js.map +1 -1
  30. package/dist/cjs/store/duckdb/ordered-bm25.d.cts +17 -0
  31. package/dist/cjs/store/duckdb/ordered-bm25.d.ts +17 -0
  32. package/dist/cjs/store/duckdb/ordered-bm25.js +238 -0
  33. package/dist/cjs/store/duckdb/ordered-bm25.js.map +1 -0
  34. package/dist/cjs/store/lock-wait.d.cts +2 -2
  35. package/dist/cjs/store/lock-wait.d.ts +2 -2
  36. package/dist/cjs/store/lock-wait.js +14 -11
  37. package/dist/cjs/store/lock-wait.js.map +1 -1
  38. package/dist/cjs/store/native.d.cts +4 -0
  39. package/dist/cjs/store/native.d.ts +4 -0
  40. package/dist/cjs/store/native.js +64 -9
  41. package/dist/cjs/store/native.js.map +1 -1
  42. package/dist/cjs/store/open.js +11 -7
  43. package/dist/cjs/store/open.js.map +1 -1
  44. package/dist/cjs/store/reconcile.d.cts +5 -1
  45. package/dist/cjs/store/reconcile.d.ts +5 -1
  46. package/dist/cjs/store/reconcile.js +5 -4
  47. package/dist/cjs/store/reconcile.js.map +1 -1
  48. package/dist/cjs/store/turso/lexical.js +5 -1
  49. package/dist/cjs/store/turso/lexical.js.map +1 -1
  50. package/dist/cjs/text/segment.js +168 -3
  51. package/dist/cjs/text/segment.js.map +1 -1
  52. package/dist/cjs/watch-claim.d.cts +39 -0
  53. package/dist/cjs/watch-claim.d.ts +39 -0
  54. package/dist/cjs/watch-claim.js +446 -0
  55. package/dist/cjs/watch-claim.js.map +1 -0
  56. package/dist/cjs/watch.js +240 -141
  57. package/dist/cjs/watch.js.map +1 -1
  58. package/dist/cjs/workers/watch-heartbeat.d.cts +1 -0
  59. package/dist/cjs/workers/watch-heartbeat.d.ts +1 -0
  60. package/dist/cjs/workers/watch-heartbeat.js +308 -0
  61. package/dist/cjs/workers/watch-heartbeat.js.map +1 -0
  62. package/dist/esm/cli/status.js +6 -5
  63. package/dist/esm/cli/status.js.map +1 -1
  64. package/dist/esm/commands/search.js +21 -0
  65. package/dist/esm/commands/search.js.map +1 -1
  66. package/dist/esm/config/types.js.map +1 -1
  67. package/dist/esm/errors.d.ts +1 -1
  68. package/dist/esm/errors.js.map +1 -1
  69. package/dist/esm/lib/worker-file.d.ts +1 -0
  70. package/dist/esm/lib/worker-file.js +24 -0
  71. package/dist/esm/lib/worker-file.js.map +1 -0
  72. package/dist/esm/output/search-error.js +21 -0
  73. package/dist/esm/output/search-error.js.map +1 -1
  74. package/dist/esm/scan/pool.js +2 -22
  75. package/dist/esm/scan/pool.js.map +1 -1
  76. package/dist/esm/store/builder.d.ts +2 -1
  77. package/dist/esm/store/builder.js +4 -4
  78. package/dist/esm/store/builder.js.map +1 -1
  79. package/dist/esm/store/duckdb/lexical.d.ts +1 -0
  80. package/dist/esm/store/duckdb/lexical.js +16 -7
  81. package/dist/esm/store/duckdb/lexical.js.map +1 -1
  82. package/dist/esm/store/duckdb/native.d.ts +2 -0
  83. package/dist/esm/store/duckdb/native.js +5 -1
  84. package/dist/esm/store/duckdb/native.js.map +1 -1
  85. package/dist/esm/store/duckdb/ordered-bm25.d.ts +17 -0
  86. package/dist/esm/store/duckdb/ordered-bm25.js +56 -0
  87. package/dist/esm/store/duckdb/ordered-bm25.js.map +1 -0
  88. package/dist/esm/store/lock-wait.d.ts +2 -2
  89. package/dist/esm/store/lock-wait.js +17 -14
  90. package/dist/esm/store/lock-wait.js.map +1 -1
  91. package/dist/esm/store/native.d.ts +4 -0
  92. package/dist/esm/store/native.js +60 -6
  93. package/dist/esm/store/native.js.map +1 -1
  94. package/dist/esm/store/open.js +9 -6
  95. package/dist/esm/store/open.js.map +1 -1
  96. package/dist/esm/store/reconcile.d.ts +5 -1
  97. package/dist/esm/store/reconcile.js +4 -3
  98. package/dist/esm/store/reconcile.js.map +1 -1
  99. package/dist/esm/store/turso/lexical.js +5 -1
  100. package/dist/esm/store/turso/lexical.js.map +1 -1
  101. package/dist/esm/text/segment.js +130 -4
  102. package/dist/esm/text/segment.js.map +1 -1
  103. package/dist/esm/watch-claim.d.ts +39 -0
  104. package/dist/esm/watch-claim.js +176 -0
  105. package/dist/esm/watch-claim.js.map +1 -0
  106. package/dist/esm/watch.js +168 -117
  107. package/dist/esm/watch.js.map +1 -1
  108. package/dist/esm/workers/watch-heartbeat.d.ts +1 -0
  109. package/dist/esm/workers/watch-heartbeat.js +106 -0
  110. package/dist/esm/workers/watch-heartbeat.js.map +1 -0
  111. package/package.json +2 -2
  112. package/skills/sense/SKILL.md +105 -104
  113. package/skills/sense/references/search.md +47 -0
  114. package/skills/sense/references/sql.md +120 -0
  115. package/skills/sense/references/stores/duckdb.md +35 -0
  116. package/skills/sense/references/stores/sqlite.md +66 -0
  117. package/skills/sense/references/stores/turso.md +36 -0
  118. package/skills/sense-setup/SKILL.md +93 -34
  119. package/skills/sense-setup/references/embeddings.md +51 -0
  120. package/skills/sense-setup/references/store-benchmarks.md +16 -0
  121. package/skills/sense-setup/references/stores/duckdb.md +28 -0
  122. package/skills/sense-setup/references/stores/sqlite.md +21 -0
  123. package/skills/sense-setup/references/stores/turso.md +25 -0
package/README.md CHANGED
@@ -43,7 +43,7 @@ Every file becomes rows in these tables, plus whatever an enabled feature adds o
43
43
  | table | holds | for |
44
44
  |---|---|---|
45
45
  | `frontmatter` | one column per key, plus `path`, `_mtime`, `_size`, `_rank`, `_parse_error` | filtering |
46
- | `content` | `title`, `summary`, `text`, `path`; on the default `sqlite` store an FTS5 index with machine-written `title_seg`/`summary_seg`/`text_seg` sidecars for matching in Chinese, Japanese, Thai, Khmer, Lao, and Burmese, on `duckdb` a plain table that matches those scripts as substrings, on `turso` a Tantivy index with `_ngram` sidecars serving the same scripts | search and ranking |
46
+ | `content` | `title`, `summary`, `text`, `path` | text search and ranking |
47
47
  | `links` | `src`, `target` as written, `dst` resolved (`NULL` = dead link, but see the skill: a link to an attachment can never resolve) | graph |
48
48
  | `tags` | `path`, `tag`; frontmatter and inline `#tags` merged and deduplicated, nested tags stored full | tag filters |
49
49
  | `sections` | heading, `level`, `start_line`, `end_line`, `tokens` estimate | structure |
@@ -51,7 +51,7 @@ Every file becomes rows in these tables, plus whatever an enabled feature adds o
51
51
 
52
52
  Results are references (path, title, summary, snippets), never file contents. Reading happens afterward through the filesystem, scoped to the line ranges `peek` returns. This is the [just-in-time context pattern](https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents): the agent holds lightweight identifiers and loads payloads only when needed.
53
53
 
54
- Output size is a contract, measured per release ([BENCHMARKING.md](BENCHMARKING.md)): `map` is fixed-size, a `search` row is tens of tokens, and a `peek` stays flat however large the note is. What it saves over reading grows with the file; a small note is cheaper to read whole.
54
+ `map` has fixed-size output, a `search` row is tens of tokens, and a `peek` stays flat however large the note is. What it saves over reading grows with the file; a small note is cheaper to read whole.
55
55
 
56
56
  ```sql
57
57
  -- filter and search compose in one query
@@ -75,13 +75,13 @@ ORDER BY bm25(content, 10.0, 5.0, 1.0) LIMIT 10
75
75
  | `init` | write a starter `sense.config.json` |
76
76
  | `status` | index location, doc count, per-preset coverage, watcher heartbeat |
77
77
  | `download` | fetch the embedding model named in the config (once per machine; the first vector search fetches it otherwise) |
78
- | `watch` | keep the index warm in the background (optional; see [DESIGN.md](DESIGN.md#watch-coordination)) |
78
+ | `watch` | keep the index warm in the background (optional; see [watch coordination](https://github.com/kmalakoff/sensemaking/blob/master/DESIGN.md#watch-coordination)) |
79
79
 
80
- `search` runs one text through every engine its scope has: FTS5 word match (BM25-ranked, bare words AND-join, operators are yours on `sqlite`; on `duckdb` and `turso` the FTS5 operators are a named error, see Config), a personalized-PageRank walk over the link graph, and vector similarity, fused into one list. `via` labels each row's evidence (`match`, `link`, `vector`, combinations); results rank by the true cosine against the best-matching canonical chunk before `similarity` is rounded to three decimals for display; a zero-direction vector has similarity `0`, exact cosine ties use bytewise path order, and equal-scoring chunks choose the earliest authored chunk. `lines` points at the section that earned the row (a direct read range). A `vector`-only row means the search words don't appear in that note; it showed up because the model judged it semantically related. `--preset` picks a named settings bundle from the config, `--where` filters on frontmatter. `--format json` on any reporting command returns structured output, and `--format csv` writes the row-returning commands one row per line, for redirecting a large result to a file instead of into context; `--version` and `--help` do what they say.
80
+ `search` runs one text through every engine its scope has: FTS5 word match (BM25-ranked, bare words AND-join, operators are yours on `sqlite`; on `duckdb` and `turso` the FTS5 operators are a named error, see Config), a personalized-PageRank walk over the link graph, and vector similarity, fused into one list. `via` labels each row's evidence (`match`, `link`, `vector`, combinations). Within the vector signal, candidates rank by the true cosine against the best-matching canonical chunk before `similarity` is rounded to three decimals for display; a zero-direction vector has similarity `0`, exact cosine ties use bytewise path order, and equal-scoring chunks choose the earliest authored chunk. The public search list ranks the combined word, link, and vector candidates by fused reciprocal-rank score. `lines` points at the section that earned the row (a direct read range). A `vector`-only row means the search words don't appear in that note; it showed up because the model judged it semantically related. `--preset` picks a named settings bundle from the config, `--where` filters on frontmatter. `--format json` on any reporting command returns structured output, and `--format csv` writes the row-returning commands one row per line, for redirecting a large result to a file instead of into context; `--version` and `--help` do what they say.
81
81
 
82
82
  Search rows carry `snippets: string[]`. Each passage is generated around the matched words, marked with `«»`, and limited to 80 characters by default. `--snippet-count-limit` returns more non-overlapping passages from a note, in document order. Link- and vector-only rows have `snippets: []`.
83
83
 
84
- Lexical words are case- and accent-insensitive. SQLite and DuckDB use their native English stem tokenizers; Turso's native Tantivy index has no stem tokenizer in the supported release, so it applies the shared Porter normalization to a derived field before native indexing. In every store, `run`, `running`, and `runs` match the same authored notes, while authored bytes are never rewritten. Quoted phrases require adjacent words, with punctuation treated as a separator, and punctuation-only input returns no lexical rows.
84
+ Lexical words are case- and accent-insensitive. SQLite and DuckDB use their native English stem tokenizers; Turso's native Tantivy index has no stem tokenizer in the supported release, so it applies the shared Porter normalization to a derived field before native indexing. In every store, `run`, `running`, and `runs` match the same authored notes, while authored bytes are never rewritten. Quoted phrases require adjacent words, with punctuation treated as a separator, and punctuation-only input returns no lexical rows. SQLite's native caret and `NEAR(...)` expressions over unspaced text use original FTS5 tokens rather than sidecar substring semantics; use an ordinary quoted search when substring findability matters, at the cost of positional filtering.
85
85
 
86
86
  ## Config
87
87
 
@@ -109,19 +109,25 @@ Lexical words are case- and accent-insensitive. SQLite and DuckDB use their nati
109
109
  | `root` | optional markdown-tree path. Relative to the config directory; omitted means that directory. Preset globs, filesystem reads, watcher events, and indexed `path` values use this root. `.sense/` state stays beside the config. Changing it rebuilds the index. |
110
110
  | `presets` | named bundles of `include`/`exclude` globs, `k` (result count), `signals` (which engines this scope searches with, `words`, `links`, `vectors`; every signal whose prerequisites hold, unless the preset lists them exhaustively), `where` (a standing SQL filter). A file is indexed if any preset includes it, embedded if a model is named and some covering preset's `signals` include `vectors`; `status` shows each preset's coverage. |
111
111
  | `embed` | the model vectors are built with. Naming one gives the tree vectors; omitting the block means none at all, whatever the presets say. `sense download` fetches it. |
112
- | `store` | backing store engine: `sqlite` (default, zero-dependency, Node's built-in SQLite), or the experimental `duckdb` and `turso`. The first command that opens such a tree installs that engine's package on its own (`@duckdb/node-api`, a one-time native download of ~110 MB; `@tursodatabase/database`, much smaller). The same commands and table names run on all three; what does not port is FTS5 syntax. Under `duckdb` and `turso`, `search` text and raw `MATCH` reject FTS5's prefix (`foo*`), boolean (`AND`/`OR`/`NOT`), `NEAR`, initial-token (`^`), and column-filter (`title:foo`) operators with a named error that says how to rephrase or set `store` to `sqlite`; bare words and quoted phrases work on all three. Raw `sql` is a per-store dialect: the tables are portable, but sqlite's FTS5 `MATCH`/`snippet()`/`bm25()` do not run under `duckdb` or `turso`, so saved queries written in FTS5 syntax are sqlite dialect, and of the SQL functions `has`/`basename` run on all three (`turso` rewrites them into portable SQL) while `segment` runs on `sqlite` and `duckdb` only, so a tree whose queries call `segment` stays off `turso`. `sense watch` runs on all three; on `duckdb` and `turso`, which lock the cache file per connection, a concurrent command waits out the watcher's current cycle instead of failing. Each store keeps its own cache file (`.sense/cache.db`, `.sense/cache.duckdb`, `.sense/cache.turso.db`); switching stores is a rebuild, not a migration. Speed is a separate axis. The current per-store figures are historical diagnostics, not an identical-work leaderboard; workload, readiness, and selected downstream work differ. Speed is not the only reason to choose, though: the cache is an ordinary database file, so a tree indexed under duckdb is readable by anything in DuckDB's ecosystem, and turso brings concurrent access, non-blocking I/O and encryption. Pick for what you intend to do with the file, then check the per-store figures. Per-store figures: [BENCHMARKING.md](BENCHMARKING.md). |
112
+ | `store` | `sqlite` by default, or the experimental `duckdb` and `turso`. Each engine uses its own cache file, so changing this setting rebuilds the index. |
113
113
  | `queries` | entries runnable as `sense <name>`, each naming the verb it runs: `{ sql }` for SQL (`?` binds positional args) or `{ search }` for a ranked search with its settings baked in, so `sense hot` needs no flags. Running an entry validates it: a typo'd column errors and exits nonzero, and a parameterised entry validates with any argument, since preparing precedes binding. |
114
114
  | `version` | schema version; older configs auto-migrate on load, noted on stderr. |
115
115
 
116
116
  Bare commands use the `default` preset; `--preset` names another; flags override single fields. Editing a preset rebuilds the cache and says which preset caused it.
117
117
 
118
+ ### Store choice
119
+
120
+ Choose `sqlite` for the smallest setup, full FTS5 query syntax, and concurrent Sense commands. Choose `duckdb` when the cache should participate in DuckDB analytical work over large datasets or in local/cloud workflows. Choose `turso` for its embedded Rust engine and Tantivy text index. The DuckDB and Turso adapters are experimental, install their native package on first use, and currently serialize Sense commands that open the same cache.
121
+
122
+ The commands and table names are shared. Raw SQL still follows the selected engine's dialect, and advanced FTS5 operators only work on `sqlite`.
123
+
118
124
  Vectors need a model. Naming a Hugging Face id in `embed.model` is consent to fetch it: it downloads lazily, on the first vector search, with progress on stderr, into `~/.sense/models` (huggingface_hub's cache layout, one snapshot directory per resolved revision, shared by every tree, 124 MB, never in the package). `sense download` prefetches the same model ahead of time, so the wait happens on your schedule instead of the first query's; it is idempotent and prints the resolved revision. `embed.model` is a Hugging Face id, or a path to a directory holding `model.safetensors` and `tokenizer.json`, which nothing fetches for you. A preset that asks for vectors when a local model path is missing those files is an error naming the fix, rather than a quieter result that would make the same search answer differently before and after; a preset whose `signals` exclude `vectors` never asks, so it is unaffected. An optional top-level `"embed": { "model", "provider", "url", "key" }` block points at any Model2Vec model, local path, or OpenAI-compatible endpoint (Ollama, LM Studio, hosted). Embedding the notes themselves happens on the first vector search, with progress.
119
125
 
120
- Two custom SQL functions. `has(field, value)`: array membership on JSON-array fields, substring on strings, false on missing keys. `segment(terms)`: rewrites a run of Chinese, Japanese, Thai, Khmer, Lao, or Burmese text into the ordered grapheme phrase the sidecar columns need, leaving everything else unchanged, so it is safe to add to any hand-written `MATCH`. Frontmatter parsing is lenient: syntax errors are per-file warnings, and the values are still indexed, so one bad note never costs you the crawl.
126
+ Sense adds `has(field, value)` for array membership or string containment and `basename(path)` for path queries on every store. SQLite and DuckDB also provide `segment(terms)` for hand-written matching over text without word spaces. A frontmatter syntax error records `_parse_error` and leaves that file's discovered fields empty; the file's content still enters the index.
121
127
 
122
128
  ## Providers
123
129
 
124
- `embed.provider` picks the wire protocol; `embed.model` names the model. [INTEGRATIONS.md](INTEGRATIONS.md) is the record of what has actually been run and verified against this codebase. Only that gets named as a recommendation.
130
+ `embed.provider` picks the wire protocol; `embed.model` names the model. The [verified integrations](https://github.com/kmalakoff/sensemaking/blob/master/INTEGRATIONS.md) record what has actually been run against this codebase. Only those integrations are named as recommendations.
125
131
 
126
132
  **static**. A local, pure-JS Model2Vec model; no network at query time.
127
133
 
@@ -153,9 +159,7 @@ Every query starts with a freshness check against the cache in `.sense/`; only c
153
159
 
154
160
  - **Work is linear in note count.** Crawl, reconcile, and the freshness check are what every invocation pays; that check is the floor cost of a query and the first thing to watch on a large tree.
155
161
  - **Output is flat.** `map`, `peek`, and a search row cost the same on a small tree as a large one: context cost is bounded by what you ask for, not by how much there is.
156
- - **Bulk changes are paid by whoever queries next.** `sense watch` moves that re-parse into the background ([DESIGN.md](DESIGN.md#watch-coordination)): it changes latency, never answers, since every query reconciles for itself. To start the cache over, delete the directory `sense status` prints.
157
-
158
- Release assessments keep these claims measured. The ordinary assessment runs required common behavior and current-store work plus full portable NFCorpus on all three stores. The explicit deep profile adds portable FEVER, pinned corpora spanning a 4x range in note count, a stress tree that packs the worst measured shapes into one place, and legacy SQLite OR-bag continuity. Historical performance, quality, and output bands warn; failed required behavior, invalid or missing required evidence, explicit caller bounds or quality floors, and approved performance guards block. Current figures: [BENCHMARKING.md](BENCHMARKING.md).
162
+ - **Bulk changes are paid by whoever queries next.** `sense watch` moves that re-parse into the background ([watch coordination](https://github.com/kmalakoff/sensemaking/blob/master/DESIGN.md#watch-coordination)): it changes latency, never answers, since every query reconciles for itself. To start the cache over, delete the directory `sense status` prints.
159
163
 
160
164
  ## For AI agents
161
165
 
@@ -163,7 +167,7 @@ Release assessments keep these claims measured. The ordinary assessment runs req
163
167
  npx skills add kmalakoff/sensemaking # -g for global, -a claude-code to target
164
168
  ```
165
169
 
166
- Three skills: `sense` for querying a tree (what each command is for, FTS5 syntax, reading the `via`/`score`/`similarity` columns, worked examples), `sense-setup` for making one, where features, frontmatter conventions, and note size are decisions with consequences either way, and `sense-bases` for translating an Obsidian Bases `.base` file into sense SQL.
170
+ Three skills: `sense` for querying a tree, including store-specific SQL and search guidance; `sense-setup` for creating one and choosing its store, presets, vectors, and note conventions; and `sense-bases` for translating an Obsidian Bases `.base` file into sense SQL.
167
171
 
168
172
  ## Prior art
169
173
 
@@ -24,6 +24,7 @@ var _httpts = require("../embed/http.js");
24
24
  var _storets = require("../embed/store.js");
25
25
  var _outputts = require("../output/output.js");
26
26
  var _indexts1 = require("../store/index.js");
27
+ var _watchclaimts = require("../watch-claim.js");
27
28
  var _indexts2 = require("./index.js");
28
29
  var _sharedts = require("./shared.js");
29
30
  function _array_like_to_array(arr, len) {
@@ -354,7 +355,7 @@ function languagesInfo(e) {
354
355
  }
355
356
  var status = function status(ctx) {
356
357
  return _async_to_generator(function() {
357
- var _ref, _process_env_e_key, _cfg_migratedFrom, _cfg_unknownKeys, _cfg_rootDir, _cfg_rootDir1, _cfg_configDir, _cfg_queries, values, format, cfg, _ref1, store, dbPath, warnings, features, e, hasModel, reachable, _tmp, vectors, _tmp1, presets, _ref2, declaredLanguages, languagesState, languagesSource, detectedLanguages, heartbeat, cacheSchema, engine, keyEnv, result, _tmp2, migrated, shown, total, breakdown, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, line, pid, _iteratorNormalCompletion1, _didIteratorError1, _iteratorError1, _iterator1, _step1, _step_value, key, value;
358
+ var _ref, _ref1, _cfg_configDir, _process_env_e_key, _cfg_migratedFrom, _cfg_unknownKeys, _cfg_rootDir, _cfg_rootDir1, _cfg_configDir1, _cfg_queries, values, format, cfg, _ref2, store, dbPath, warnings, features, e, hasModel, reachable, _tmp, vectors, _tmp1, presets, _ref3, declaredLanguages, languagesState, languagesSource, detectedLanguages, cacheSchema, engine, watcher, keyEnv, result, _tmp2, migrated, shown, total, breakdown, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, line, pid, _iteratorNormalCompletion1, _didIteratorError1, _iteratorError1, _iterator1, _step1, _step_value, key, value;
358
359
  return _ts_generator(this, function(_state) {
359
360
  switch(_state.label){
360
361
  case 0:
@@ -366,7 +367,7 @@ var status = function status(ctx) {
366
367
  (0, _indexts1.openStore)(cfg)
367
368
  ];
368
369
  case 1:
369
- _ref1 = _state.sent(), store = _ref1.store, dbPath = _ref1.dbPath, warnings = _ref1.warnings;
370
+ _ref2 = _state.sent(), store = _ref2.store, dbPath = _ref2.dbPath, warnings = _ref2.warnings;
370
371
  (0, _sharedts.printWarnings)(warnings);
371
372
  features = (0, _indexts.featureStates)(cfg);
372
373
  e = (0, _indexts.embedConfig)(cfg);
@@ -415,35 +416,30 @@ var status = function status(ctx) {
415
416
  ];
416
417
  case 8:
417
418
  presets = _state.sent();
418
- _ref2 = e ? languagesInfo(e) : {
419
+ _ref3 = e ? languagesInfo(e) : {
419
420
  languages: null,
420
421
  state: null,
421
422
  source: null
422
- }, declaredLanguages = _ref2.languages, languagesState = _ref2.state, languagesSource = _ref2.source;
423
+ }, declaredLanguages = _ref3.languages, languagesState = _ref3.state, languagesSource = _ref3.source;
423
424
  return [
424
425
  4,
425
426
  (0, _distributionts.languageDistribution)(store)
426
427
  ];
427
428
  case 9:
428
429
  detectedLanguages = (_ref = _state.sent()) !== null && _ref !== void 0 ? _ref : null;
429
- return [
430
- 4,
431
- (0, _indexts1.getMeta)(store, 'watch_heartbeat')
432
- ];
433
- case 10:
434
- heartbeat = _state.sent();
435
430
  return [
436
431
  4,
437
432
  (0, _indexts1.getMeta)(store, 'schema_version')
438
433
  ];
439
- case 11:
434
+ case 10:
440
435
  cacheSchema = _state.sent();
441
436
  return [
442
437
  4,
443
438
  store.engineStatus()
444
439
  ];
445
- case 12:
440
+ case 11:
446
441
  engine = _state.sent();
442
+ watcher = (0, _watchclaimts.readWatchClaim)((_ref1 = (_cfg_configDir = cfg.configDir) !== null && _cfg_configDir !== void 0 ? _cfg_configDir : cfg.rootDir) !== null && _ref1 !== void 0 ? _ref1 : cfg.baseDir);
447
443
  // The env var holds the token; only its name and whether it is set are ever reported.
448
444
  keyEnv = (e === null || e === void 0 ? void 0 : e.key) ? {
449
445
  name: e.key,
@@ -456,7 +452,7 @@ var status = function status(ctx) {
456
452
  unknownConfigKeys: (_cfg_unknownKeys = cfg.unknownKeys) !== null && _cfg_unknownKeys !== void 0 ? _cfg_unknownKeys : null,
457
453
  tree: (_cfg_rootDir = cfg.rootDir) !== null && _cfg_rootDir !== void 0 ? _cfg_rootDir : cfg.baseDir,
458
454
  treeRoot: (_cfg_rootDir1 = cfg.rootDir) !== null && _cfg_rootDir1 !== void 0 ? _cfg_rootDir1 : cfg.baseDir,
459
- configDir: (_cfg_configDir = cfg.configDir) !== null && _cfg_configDir !== void 0 ? _cfg_configDir : cfg.baseDir,
455
+ configDir: (_cfg_configDir1 = cfg.configDir) !== null && _cfg_configDir1 !== void 0 ? _cfg_configDir1 : cfg.baseDir,
460
456
  cache: (0, _nodepath.dirname)(dbPath),
461
457
  db: dbPath,
462
458
  cacheSchema: cacheSchema
@@ -465,14 +461,14 @@ var status = function status(ctx) {
465
461
  4,
466
462
  (0, _indexts1.docCount)(store)
467
463
  ];
468
- case 13:
464
+ case 12:
469
465
  _tmp2.docs = _state.sent();
470
466
  return [
471
467
  4,
472
468
  parseErrorCount(store)
473
469
  ];
474
- case 14:
475
- _tmp2.unparseableFrontmatter = _state.sent(), _tmp2.features = features.on, _tmp2.featuresOff = features.off, _tmp2.embed = e ? _object_spread({
470
+ case 13:
471
+ result = (_tmp2.unparseableFrontmatter = _state.sent(), _tmp2.features = features.on, _tmp2.featuresOff = features.off, _tmp2.embed = e ? _object_spread({
476
472
  provider: e.provider,
477
473
  model: e.model,
478
474
  dir: e.provider === 'static' ? (0, _storets.modelDir)(e.model) : null,
@@ -483,13 +479,7 @@ var status = function status(ctx) {
483
479
  languages: declaredLanguages,
484
480
  languagesState: languagesState,
485
481
  detectedLanguages: detectedLanguages
486
- }, vectors !== null && vectors !== void 0 ? vectors : {}) : null, _tmp2.presets = presets, _tmp2.queries = Object.keys((_cfg_queries = cfg.queries) !== null && _cfg_queries !== void 0 ? _cfg_queries : {}).length;
487
- return [
488
- 4,
489
- (0, _indexts1.getMeta)(store, 'watch_pid')
490
- ];
491
- case 15:
492
- result = (_tmp2.watcherPid = _state.sent(), _tmp2.watcherHeartbeatSecondsAgo = heartbeat ? Math.round((Date.now() - Date.parse(heartbeat)) / 1000) : null, _tmp2.engine = engine, _tmp2);
482
+ }, vectors !== null && vectors !== void 0 ? vectors : {}) : null, _tmp2.presets = presets, _tmp2.queries = Object.keys((_cfg_queries = cfg.queries) !== null && _cfg_queries !== void 0 ? _cfg_queries : {}).length, _tmp2.watcherPid = watcher ? String(watcher.pid) : null, _tmp2.watcherHeartbeatSecondsAgo = watcher ? Math.round((Date.now() - watcher.heartbeatMs) / 1000) : null, _tmp2.engine = engine, _tmp2);
493
483
  if (format === 'json') {
494
484
  console.log((0, _outputts.stringifyJson)(result, 2));
495
485
  } else {
@@ -585,7 +575,7 @@ var status = function status(ctx) {
585
575
  4,
586
576
  store.close()
587
577
  ];
588
- case 16:
578
+ case 14:
589
579
  _state.sent();
590
580
  return [
591
581
  2
@@ -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 { 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 const heartbeat = await getMeta(store, 'watch_heartbeat');\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 // 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: await getMeta(store, 'watch_pid'),\n watcherHeartbeatSecondsAgo: heartbeat ? Math.round((Date.now() - Date.parse(heartbeat)) / 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":["redactUrl","vectorState","store","stmt","prepare","get","parseErrorCount","n","url","parsed","URL","username","password","toString","replace","embedReachable","e","base","provider","probeReachable","languagesInfo","languages","state","source","isDownloadable","model","cardLanguages","readLanguages","undefined","length","status","ctx","process","cfg","values","format","dbPath","warnings","features","hasModel","reachable","vectors","presets","declaredLanguages","languagesState","languagesSource","detectedLanguages","heartbeat","cacheSchema","engine","keyEnv","result","migrated","shown","total","breakdown","line","pid","key","value","parse","argv","USAGE","name","FORMAT","CONFIG","formatOf","resolveConfig","config","openStore","printWarnings","featureStates","embedConfig","modelPresent","presetCoverage","languageDistribution","getMeta","engineStatus","set","env","configPath","configVersion","SUPPORTED_CONFIG_VERSION","migratedFrom","unknownConfigKeys","unknownKeys","tree","rootDir","baseDir","treeRoot","configDir","cache","dirname","db","docCount","docs","unparseableFrontmatter","on","featuresOff","off","embed","dir","modelDir","downloaded","queries","Object","keys","watcherPid","watcherHeartbeatSecondsAgo","Math","round","Date","now","console","log","stringifyJson","join","featuresLine","MODEL_FILENAMES","embedded","pending","reduce","a","b","entries","sort","map","code","presetsLines","padEnd","close"],"mappings":"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"}
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":["redactUrl","vectorState","store","stmt","prepare","get","parseErrorCount","n","url","parsed","URL","username","password","toString","replace","embedReachable","e","base","provider","probeReachable","languagesInfo","languages","state","source","isDownloadable","model","cardLanguages","readLanguages","undefined","length","status","ctx","cfg","process","values","format","dbPath","warnings","features","hasModel","reachable","vectors","presets","declaredLanguages","languagesState","languagesSource","detectedLanguages","cacheSchema","engine","watcher","keyEnv","result","migrated","shown","total","breakdown","line","pid","key","value","parse","argv","USAGE","name","FORMAT","CONFIG","formatOf","resolveConfig","config","openStore","printWarnings","featureStates","embedConfig","modelPresent","presetCoverage","languageDistribution","getMeta","engineStatus","readWatchClaim","configDir","rootDir","baseDir","set","env","configPath","configVersion","SUPPORTED_CONFIG_VERSION","migratedFrom","unknownConfigKeys","unknownKeys","tree","treeRoot","cache","dirname","db","docCount","docs","unparseableFrontmatter","on","featuresOff","off","embed","dir","modelDir","downloaded","queries","Object","keys","watcherPid","String","watcherHeartbeatSecondsAgo","Math","round","Date","now","heartbeatMs","console","log","stringifyJson","join","featuresLine","MODEL_FILENAMES","embedded","pending","reduce","a","b","entries","sort","map","code","presetsLines","padEnd","close"],"mappings":";;;;;;;;;;;QAsKA;eAAA;;QAnIgBA;eAAAA;;;wBAnCQ;wBACO;uBACsC;8BAChC;sBACN;uBACwD;wBAC7B;wBACb;4BAEd;wBACT;wBACyC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAG/D,+OAA+O;AAC/O,kGAAkG;AAElG,yIAAyI;AACzI,mGAAmG;AACnG,SAAeC,YAAYC,KAAY;;YAE7BC;;;;;;;;;;oBAAO;;wBAAMD,MAAME,OAAO,CAAC;;;oBAA3BD,OAAO;oBACL;;wBAAMA,KAAKE,GAAG;;;oBAAtB;;wBAAQ;;;;oBAER;;wBAAO;;;;;;;;IAEX;;AAEA,SAAeC,gBAAgBJ,KAAY;;YACnCC;;;;oBAAO;;wBAAMD,MAAME,OAAO,CAAC;;;oBAA3BD,OAAO;oBACJ;;wBAAMA,KAAKE,GAAG;;;oBAAvB;;wBAAS,cAAoCE,CAAC;;;;IAChD;;AAIO,SAASP,UAAUQ,GAAW;IACnC,IAAI;QACF,IAAMC,SAAS,IAAIC,IAAIF;QACvB,IAAI,CAACC,OAAOE,QAAQ,IAAI,CAACF,OAAOG,QAAQ,EAAE,OAAOJ;QACjDC,OAAOE,QAAQ,GAAG;QAClBF,OAAOG,QAAQ,GAAG;QAClB,OAAO,AAAC,GAAuC,OAArCH,OAAOI,QAAQ,GAAGC,OAAO,CAAC,OAAO,KAAI;IACjD,EAAE,eAAM;QACN,OAAON;IACT;AACF;AAEA,gGAAgG;AAChG,+FAA+F;AAC/F,SAAeO,eAAeC,CAAqC;;YAEnDA,QAARC;;YADN,IAAID,EAAEE,QAAQ,KAAK,YAAaF,EAAEE,QAAQ,KAAK,YAAY,CAACF,EAAER,GAAG,EAAG;;gBAAO;;YACrES,OAAO,EAACD,SAAAA,EAAER,GAAG,cAALQ,oBAAAA,SAAS,0BAA0BF,OAAO,CAAC,QAAQ;YACjE;;gBAAOK,IAAAA,sBAAc,EAACH,EAAEE,QAAQ,KAAK,WAAW,AAAC,GAAO,OAALD,MAAK,aAAWA;;;IACrE;;AAIA,8FAA8F;AAC9F,8FAA8F;AAC9F,SAASG,cAAcJ,CAA8C;IACnE,IAAIA,EAAEK,SAAS,EAAE,OAAO;QAAEA,WAAWL,EAAEK,SAAS;QAAEC,OAAO;QAAYC,QAAQ;IAAS;IACtF,IAAIP,EAAEE,QAAQ,KAAK,YAAY,CAACM,IAAAA,uBAAc,EAACR,EAAES,KAAK,GAAG,OAAO;QAAEJ,WAAW;QAAMC,OAAO;QAAMC,QAAQ;IAAK;IAC7G,IAAMG,gBAAgBC,IAAAA,sBAAa,EAACX,EAAES,KAAK;IAC3C,IAAIC,kBAAkBE,WAAW,OAAO;QAAEP,WAAW;QAAMC,OAAO;QAAcC,QAAQ;IAAK;IAC7F,IAAIG,cAAcG,MAAM,KAAK,GAAG,OAAO;QAAER,WAAW;QAAMC,OAAO;QAAQC,QAAQ;IAAK;IACtF,OAAO;QAAEF,WAAWK;QAAeJ,OAAO;QAAYC,QAAQ;IAAO;AACvE;AAEA,IAAMO,SAAkB,gBAAOC;;YAcF,MAOIC,OAAAA,gBAEcC,oBAI7BD,mBACKA,kBACbA,cACIA,eACCA,iBAUUA,cAxCfE,QACFC,QACAH,KAC8B,OAA5B9B,OAAOkC,QAAQC,UAGjBC,UACAtB,GACAuB,UACAC,iBACAC,gBACAC,SACmF1B,OAAtE2B,mBAA0BC,gBAAwBC,iBAC/DC,mBAGAC,aAGAC,QACAC,SAEAC,QACAC,eA0BEC,UAuBIC,OAOAC,OACAC,WAQL,2BAAA,mBAAA,gBAAA,WAAA,OAAMC,MAGLC,KAED,4BAAA,oBAAA,iBAAA,YAAA,qBAAOC,KAAKC;;;;oBA7FXzB,SAAW0B,IAAAA,eAAK,EAAC7B,IAAI8B,IAAI,EAAE,AAAC,UAAqBC,OAAZ/B,IAAIgC,IAAI,EAAC,KAAgB,OAAbD,eAAK,CAAChC,MAAM,GAAI,mBAAKkC,gBAAM,EAAKC,gBAAM,GAAvF/B;oBACFC,SAAS+B,IAAAA,kBAAQ,EAAChC;oBAClBF,MAAMD,IAAIoC,aAAa,CAACjC,OAAOkC,MAAM;oBACP;;wBAAMC,IAAAA,mBAAS,EAACrC;;;oBAAhB,QAAA,eAA5B9B,QAA4B,MAA5BA,OAAOkC,SAAqB,MAArBA,QAAQC,WAAa,MAAbA;oBACvBiC,IAAAA,uBAAa,EAACjC;oBAERC,WAAWiC,IAAAA,sBAAa,EAACvC;oBACzBhB,IAAIwD,IAAAA,oBAAW,EAACxC;oBAChBO,WAAWvB,IAAIyD,IAAAA,qBAAY,EAACzC,OAAO;yBACvBhB,GAAAA;;;;oBAAI;;wBAAMD,eAAeC;;;2BAArB;;;;;;2BAA0B;;;oBAA1CwB;yBACUxB,GAAAA;;;;oBAAI;;wBAAMf,YAAYC;;;4BAAlB;;;;;;4BAA2B;;;oBAAzCuC;oBACU;;wBAAMiC,IAAAA,wBAAc,EAACxE,OAAO8B;;;oBAAtCU,UAAU;oBACyE1B,QAAAA,IAAII,cAAcJ,KAAK;wBAAEK,WAAW;wBAAMC,OAAO;wBAAMC,QAAQ;oBAAK,GAA1IoB,oBAAsE3B,MAAjFK,WAAqCuB,iBAA4C5B,MAAnDM,OAA+BuB,kBAAoB7B,MAA5BO;oBAClC;;wBAAMoD,IAAAA,oCAAoB,EAACzE;;;oBAAhD4C,qBAAqB,OAAA,2BAAA,kBAAA,OAAsC;oBAG7C;;wBAAM8B,IAAAA,iBAAO,EAAC1E,OAAO;;;oBAAnC6C,cAAc;oBAGL;;wBAAM7C,MAAM2E,YAAY;;;oBAAjC7B,SAAS;oBACTC,UAAU6B,IAAAA,4BAAc,GAAC9C,SAAAA,iBAAAA,IAAI+C,SAAS,cAAb/C,4BAAAA,iBAAiBA,IAAIgD,OAAO,cAA5BhD,mBAAAA,QAAgCA,IAAIiD,OAAO;oBAC1E,sFAAsF;oBAChF/B,SAASlC,CAAAA,cAAAA,wBAAAA,EAAG0C,GAAG,IAAG;wBAAEK,MAAM/C,EAAE0C,GAAG;wBAAEwB,KAAK,EAACjD,qBAAAA,QAAQkD,GAAG,CAACnE,EAAE0C,GAAG,CAAC,cAAlBzB,gCAAAA,qBAAsB,QAAQ;oBAAG,IAAI;;wBAEhFmC,QAAQpC,IAAIoD,UAAU;wBACtBC,eAAeC,iCAAwB;wBACvCC,YAAY,GAAEvD,oBAAAA,IAAIuD,YAAY,cAAhBvD,+BAAAA,oBAAoB;wBAClCwD,iBAAiB,GAAExD,mBAAAA,IAAIyD,WAAW,cAAfzD,8BAAAA,mBAAmB;wBACtC0D,IAAI,GAAE1D,eAAAA,IAAIgD,OAAO,cAAXhD,0BAAAA,eAAeA,IAAIiD,OAAO;wBAChCU,QAAQ,GAAE3D,gBAAAA,IAAIgD,OAAO,cAAXhD,2BAAAA,gBAAeA,IAAIiD,OAAO;wBACpCF,SAAS,GAAE/C,kBAAAA,IAAI+C,SAAS,cAAb/C,6BAAAA,kBAAiBA,IAAIiD,OAAO;wBACvCW,OAAOC,IAAAA,iBAAO,EAACzD;wBACf0D,IAAI1D;wBACJW,aAAAA;;oBACM;;wBAAMgD,IAAAA,kBAAQ,EAAC7F;;;0BAArB8F,OAAM;oBACkB;;wBAAM1F,gBAAgBJ;;;oBAZ1CiD,gBAYJ8C,yBAAwB,qBACxB3D,WAAUA,SAAS4D,EAAE,QACrBC,cAAa7D,SAAS8D,GAAG,QACzBC,QAAOrF,IAAI;wBAAEE,UAAUF,EAAEE,QAAQ;wBAAEO,OAAOT,EAAES,KAAK;wBAAE6E,KAAKtF,EAAEE,QAAQ,KAAK,WAAWqF,IAAAA,iBAAQ,EAACvF,EAAES,KAAK,IAAI;wBAAMjB,KAAKQ,EAAER,GAAG,GAAGR,UAAUgB,EAAER,GAAG,IAAI;wBAAMgC,WAAAA;wBAAWU,QAAAA;wBAAQsD,YAAYjE;wBAAUlB,WAAWsB;wBAAmBC,gBAAAA;wBAAgBE,mBAAAA;uBAAuBL,oBAAAA,qBAAAA,UAAW,CAAC,KAAO,YACnRC,UAAAA,eACA+D,UAASC,OAAOC,IAAI,EAAC3E,eAAAA,IAAIyE,OAAO,cAAXzE,0BAAAA,eAAe,CAAC,GAAGH,MAAM,QAC9C+E,aAAY3D,UAAU4D,OAAO5D,QAAQQ,GAAG,IAAI,YAC5CqD,6BAA4B7D,UAAU8D,KAAKC,KAAK,CAAC,AAACC,CAAAA,KAAKC,GAAG,KAAKjE,QAAQkE,WAAW,AAAD,IAAK,QAAQ,YAC9FnE,SAAAA;oBAGF,IAAIb,WAAW,QAAQ;wBACrBiF,QAAQC,GAAG,CAACC,IAAAA,uBAAa,EAACnE,QAAQ;oBACpC,OAAO;wBACCC,WAAWpB,IAAIuD,YAAY,KAAK3D,YAAY,AAAC,oBAAoC,OAAjBI,IAAIuD,YAAY,EAAC,kBAAgB;wBACvG6B,QAAQC,GAAG,CAAC,AAAC,aAA+B/B,OAAnBnC,OAAOiB,MAAM,EAAC,OAAgChB,OAA3BkC,iCAAwB,EAAY,OAATlC,UAAS;wBAChF,8EAA8E;wBAC9E,IAAIpB,IAAIyD,WAAW,EAAE2B,QAAQC,GAAG,CAAC,AAAC,oCAA8D,OAA3BrF,IAAIyD,WAAW,CAAC8B,IAAI,CAAC;wBAC1FH,QAAQC,GAAG,CAAC,AAAC,cAA6B,OAAhBlE,OAAOwC,QAAQ;wBACzCyB,QAAQC,GAAG,CAAC,AAAC,cAA8B,OAAjBlE,OAAO4B,SAAS;wBAC1CqC,QAAQC,GAAG,CAAC,AAAC,aAAoClE,OAAxBA,OAAOyC,KAAK,EAAC,aAA8B,OAAnBzC,OAAOJ,WAAW,EAAC;wBACpEqE,QAAQC,GAAG,CAAC,AAAC,aAA0BlE,OAAdA,OAAO6C,IAAI,EAAgJ,OAA7I7C,OAAO8C,sBAAsB,GAAG,IAAI,AAAC,MAAmC,OAA9B9C,OAAO8C,sBAAsB,EAAC,oEAAkE;wBACjLmB,QAAQC,GAAG,CAAC;wBACZD,QAAQC,GAAG,CAACG,IAAAA,sBAAY,EAAClF;wBACzB,4FAA4F;wBAC5F,0BAA0B;wBAC1B,IAAI,CAACtB,GAAGoG,QAAQC,GAAG,CAAC;6BACf;4BACHD,QAAQC,GAAG,CAAC,AAAC,aAA0BrG,OAAdA,EAAEE,QAAQ,EAAC,KAAW,OAARF,EAAES,KAAK;4BAC9C,IAAIT,EAAEE,QAAQ,KAAK,UAAUkG,QAAQC,GAAG,CAAC,AAAC,sBAA2C9E,OAAtBgE,IAAAA,iBAAQ,EAACvF,EAAES,KAAK,GAAE,MAAoM,OAAhMc,WAAW,YAAY,AAAC,YAAsK,OAA3Jf,IAAAA,uBAAc,EAACR,EAAES,KAAK,IAAI,AAAC,kDAA2D,OAATM,IAAIgC,IAAI,EAAC,4BAA2B,AAAC,MAAqB,OAAhB0D,wBAAe,EAAC,wBAAsB;4BACrR,IAAI9E,qBAAqBE,oBAAoB,UAAUuE,QAAQC,GAAG,CAAC,AAAC,wBAAoD,OAA7B1E,kBAAkB4E,IAAI,CAAC,OAAM;iCACnH,IAAI5E,qBAAqBE,oBAAoB,QAAQuE,QAAQC,GAAG,CAAC,AAAC,wBAAoD,OAA7B1E,kBAAkB4E,IAAI,CAAC,OAAM;iCACtH,IAAI3E,mBAAmB,QAAQwE,QAAQC,GAAG,CAAC;iCAC3C,IAAIzE,mBAAmB,cAAcwE,QAAQC,GAAG,CAAC,AAAC,iEAA0E,OAATtF,IAAIgC,IAAI,EAAC;4BACjI,qFAAqF;4BACrF,kDAAkD;4BAClD,IAAI/C,EAAER,GAAG,IAAIgC,cAAc,MAAM;gCACzBa,QAAQrC,EAAER,GAAG,GAAGR,UAAUgB,EAAER,GAAG,IAAI;gCACzC4G,QAAQC,GAAG,CAAC,AAAC,sBAA6B7E,OAARa,OAAgF,OAAxEb,cAAc,OAAO,KAAKA,YAAY,iBAAiB;4BACnG;4BACA,IAAIU,QAAQkE,QAAQC,GAAG,CAAC,AAAC,0BAAyCnE,OAAhBA,OAAOa,IAAI,EAAC,MAAmC,OAA/Bb,OAAOgC,GAAG,GAAG,QAAQ,WAAU;4BACjG,IAAI,CAAC3C,UAAU6E,QAAQC,GAAG,CAAC;iCACtB,IAAI5E,SAAS2E,QAAQC,GAAG,CAAC,AAAC,sBAAmD5E,OAA9BA,QAAQiF,QAAQ,EAAC,eAA6B,OAAhBjF,QAAQkF,OAAO,EAAC;4BAClG,IAAI7E,mBAAmB;gCACfQ,QAAQoD,OAAOxE,MAAM,CAACY,mBAAmB8E,MAAM,CAAC,SAACC,GAAGC;2CAAMD,IAAIC;mCAAG;gCACjEvE,YAAYmD,OAAOqB,OAAO,CAACjF,mBAC9BkF,IAAI,CAAC,SAACH,GAAGC;2CAAMA,CAAC,CAAC,EAAE,GAAGD,CAAC,CAAC,EAAE;mCAC1BI,GAAG,CAAC;6EAAEC,kBAAM3H;2CAAO,AAAC,GAAUwG,OAARmB,MAAK,KAAiC,OAA9BnB,KAAKC,KAAK,CAAC,AAACzG,IAAI+C,QAAS,MAAK;mCAC5DiE,IAAI,CAAC;gCACRH,QAAQC,GAAG,CAAC,AAAC,uBAAoC/D,OAAdC,WAAU,MAAU,OAAND,OAAM;4BACzD;wBACF;wBACA8D,QAAQC,GAAG,CAAC;wBACP,kCAAA,2BAAA;;4BAAL,IAAK,YAAcc,IAAAA,sBAAY,EAACzF,+BAA3B,6BAAA,QAAA,yBAAA;gCAAMc,OAAN;gCAAqC4D,QAAQC,GAAG,CAAC7D;;;4BAAjD;4BAAA;;;qCAAA,6BAAA;oCAAA;;;oCAAA;0CAAA;;;;wBACL4D,QAAQC,GAAG,CAAC;wBACZD,QAAQC,GAAG,CAAC,AAAC,aAAqCtF,OAAzBoB,OAAOsD,OAAO,EAAC,YAAmB,OAAT1E,IAAIgC,IAAI,EAAC;wBACrDN,MAAMN,OAAOyD,UAAU,GAAG,AAAC,OAAwB,OAAlBzD,OAAOyD,UAAU,EAAC,QAAM;wBAC/DQ,QAAQC,GAAG,CAAClE,OAAO2D,0BAA0B,KAAK,OAAO,mBAAmB,AAAC,aAAiC3D,OAArBM,KAAI,mBAAmD,OAAlCN,OAAO2D,0BAA0B,EAAC;wBAC3I,mCAAA,4BAAA;;4BAAL,IAAK,aAAsBJ,OAAOqB,OAAO,CAAC5E,OAAOH,MAAM,wBAAlD,8BAAA,SAAA,0BAAA;+DAAA,kBAAOU,sBAAKC;gCAAyCyD,QAAQC,GAAG,CAAC,AAAC,GAAgC3D,OAA9B,AAAC,GAAa,OAAXxD,MAAM6D,IAAI,EAAC,KAAGqE,MAAM,CAAC,KAAazE,OAAPD,KAAI,KAAS,OAANC;;;4BAAzG;4BAAA;;;qCAAA,8BAAA;oCAAA;;;oCAAA;0CAAA;;;;oBACP;oBACA;;wBAAMzD,MAAMmI,KAAK;;;oBAAjB;;;;;;IACF;;IACA,WAAevG"}
@@ -627,6 +627,27 @@ function lineRangeFor(store, path, line) {
627
627
  });
628
628
  })();
629
629
  }
630
+ function validateSearchOptions(opts) {
631
+ for(var _i = 0, _iter = [
632
+ [
633
+ 'k',
634
+ opts.k
635
+ ],
636
+ [
637
+ 'snippetCharLimit',
638
+ opts.snippetCharLimit
639
+ ],
640
+ [
641
+ 'snippetCountLimit',
642
+ opts.snippetCountLimit
643
+ ]
644
+ ]; _i < _iter.length; _i++){
645
+ var _iter__i = _sliced_to_array(_iter[_i], 2), name = _iter__i[0], value = _iter__i[1];
646
+ if (value !== undefined && (!Number.isFinite(value) || !Number.isInteger(value) || value <= 0)) {
647
+ throw new _errorsts.SenseError('SEARCH_OPTION_INVALID', 'search option "'.concat(name, '" must be a positive finite integer, got ').concat(String(value)));
648
+ }
649
+ }
650
+ }
630
651
  function hydrateSearchRows(_0, _1, _2, _3, _4) {
631
652
  return _async_to_generator(function(store, cfg, rows, matchedPaths, terms) {
632
653
  var opts, _opts_snippetCharLimit, _opts_snippetCountLimit, bareTerms, charLimit, countLimit, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, row, text, _cfg_rootDir, _computeSnippets, snippets, offset, _tmp, err;
@@ -746,6 +767,7 @@ function search(_0, _1, _2) {
746
767
  switch(_state.label){
747
768
  case 0:
748
769
  opts = _arguments.length > 3 && _arguments[3] !== void 0 ? _arguments[3] : {};
770
+ validateSearchOptions(opts);
749
771
  effective = (0, _indexts.resolveSearch)(cfg, opts);
750
772
  k = effective.k, signals = effective.signals;
751
773
  return [
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/search.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { stemmer } from 'stemmer';\nimport type { ResolvedConfig } from '../config/index.ts';\nimport { embedConfig, featureEnabled, resolveSearch } from '../config/index.ts';\nimport { localModelMissing, MODEL_FILENAMES } from '../embed/store.ts';\nimport { SenseError } from '../errors.ts';\nimport type { Row } from '../output/output.ts';\nimport { bareTermSyntaxError, searchError } from '../output/search-error.ts';\nimport type { LexicalHit, Store } from '../store/types.ts';\nimport { foldForSearch, hasUnspacedRun, searchTokens, unspacedRuns } from '../text/segment.ts';\nimport { materializeScope, narrowByWhere, rawScope, scopeHasEmbeddings } from './scope.ts';\nimport { linksCandidates, vectorsCandidates, wordsCandidates } from './signals.ts';\n\n// Default budget behind --snippet-char-limit/--snippet-count-limit: the limit is whatever the\n// caller passes, these only say what happens when they pass nothing (DESIGN.md \"Search snippets\").\nexport const SNIPPET_CHAR_LIMIT_DEFAULT = 80;\nexport const SNIPPET_COUNT_LIMIT_DEFAULT = 1;\n\n// Bare terms from an FTS5 query string: strips operators/quoting, not real terms, so the\n// snippet scan matches what the query matched -- FTS5 operators are uppercase-only, so this is too.\nfunction extractBareTerms(query: string): string[] {\n const cleaned = query\n .replace(/\"/g, ' ')\n .replace(/[()*]/g, ' ')\n .replace(/\\b(AND|OR|NOT|NEAR)\\b(\\/\\d+)?/g, ' ');\n return cleaned\n .split(/\\s+/)\n .map((tok) => tok.replace(/^[A-Za-z_]\\w*:/, '')) // column filter, e.g. title:term\n .flatMap((tok) => searchTokens(tok).map(({ text }) => text))\n .map((tok) => tok.toLowerCase().trim())\n .filter((tok) => tok.length > 0);\n}\n\n// House convention (src/chunk/group.ts): Intl.Segmenter word boundaries, not a bare \\b, which\n// is ASCII-only. Built on first use: construction is a few ms, paid only when a term matches.\nlet wordSegmenter: Intl.Segmenter | undefined;\n\nfunction findOccurrences(text: string, terms: string[]): Array<{ start: number; end: number; term: string }> {\n const occ: Array<{ start: number; end: number; term: string }> = [];\n const textLower = text.toLowerCase();\n\n // Unspaced scripts (UNSPACED_SCRIPTS) mark no word boundaries: the sense-setup skill promises\n // substring semantics there, so a matching term is found wherever it occurs in the run.\n const unspacedTerms = terms.filter(hasUnspacedRun);\n if (unspacedTerms.length > 0) {\n for (const run of unspacedRuns(text)) {\n const runLower = textLower.slice(run.start, run.end);\n for (const term of unspacedTerms) {\n let idx = 0;\n for (;;) {\n const found = runLower.indexOf(term, idx);\n if (found === -1) break;\n occ.push({ start: run.start + found, end: run.start + found + term.length, term });\n idx = found + term.length;\n }\n }\n }\n }\n\n // Spaced scripts: a word is marked when its porter stem equals a query term's stem, the same\n // algorithm both indexes tokenize with (sqlite's TOKENIZE = 'porter unicode61'; duckdb's fts default).\n const spacedTerms = terms.filter((t) => !hasUnspacedRun(t));\n if (spacedTerms.length > 0) {\n const termByStem = new Map(spacedTerms.map((t) => [stemmer(foldForSearch(t)), t]));\n const stemInitials = new Set(Array.from(termByStem.keys(), (term) => term[0]));\n for (const match of searchTokens(text)) {\n const segment = match.text;\n if (match.unspaced) continue;\n const folded = foldForSearch(segment);\n // Porter stemming changes suffixes only, so a different initial cannot match a query stem.\n if (!stemInitials.has(folded[0])) continue;\n const term = termByStem.get(stemmer(folded));\n if (term !== undefined) occ.push({ start: match.start, end: match.end, term });\n }\n }\n\n return occ.sort((a, b) => a.start - b.start || b.end - a.end);\n}\n\n// Segment boundaries of the whole text, word granularity: the only cut points a snippet edge\n// may land on. One rule for every script (DESIGN.md \"Cutting\") -- a regex \\b is ASCII-only and\n// would mis-cut Japanese, Chinese and Thai, which Intl.Segmenter divides by dictionary instead.\nfunction wordBoundaries(text: string): number[] {\n wordSegmenter ??= new Intl.Segmenter('en', { granularity: 'word' });\n const bounds = [0];\n for (const { index, segment } of wordSegmenter.segment(text)) bounds.push(index + segment.length);\n return bounds;\n}\n\n// bounds is sorted: snaps are binary searches, since each runs once per candidate window.\nfunction snapForward(bounds: number[], pos: number): number {\n let lo = 0;\n let hi = bounds.length - 1;\n while (lo < hi) {\n const mid = (lo + hi) >> 1;\n if (bounds[mid] < pos) lo = mid + 1;\n else hi = mid;\n }\n return bounds[lo];\n}\n\nfunction snapBackward(bounds: number[], pos: number): number {\n let lo = 0;\n let hi = bounds.length - 1;\n let result = bounds[0];\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1;\n if (bounds[mid] <= pos) {\n lo = mid;\n result = bounds[mid];\n } else hi = mid - 1;\n }\n return result;\n}\n\nfunction firstBoundAfter(bounds: number[], pos: number): number | undefined {\n let lo = 0;\n let hi = bounds.length;\n while (lo < hi) {\n const mid = (lo + hi) >> 1;\n if (bounds[mid] <= pos) lo = mid + 1;\n else hi = mid;\n }\n return bounds[lo];\n}\n\n// A raw, unmarked upper bound: [start, end) inside [anchor, anchor+charLimit), edges on\n// boundaries. Markup (guillemets, ellipses) added on top of this can push the rendered result\n// past charLimit, which is what fitEnd() below corrects for; this is only the starting guess.\n// A single segment spanning the whole target window (a word longer than the char limit) is the\n// one case where holding the boundary rule means the budget is not a hard ceiling.\nfunction cutWindow(text: string, bounds: number[], anchor: number, charLimit: number): { start: number; end: number } {\n const rawEnd = Math.min(text.length, anchor + charLimit);\n const start = snapForward(bounds, anchor);\n let end = snapBackward(bounds, rawEnd);\n if (end <= start) end = firstBoundAfter(bounds, start) ?? text.length;\n return { start, end: Math.min(end, text.length) };\n}\n\n// The largest boundary <= upperBound, > floor, whose rendered [*, end) fits charLimit. Markup\n// only ever adds characters as end grows, so rendered length is non-decreasing in end and\n// shrinking from upperBound is guaranteed to find the true fit rather than guess at it.\n// `floor` is the one end this never shrinks past (the anchor's own marked word, or the first\n// word in the unmarked fallback): DESIGN.md's budget is a ceiling except here, where returning\n// nothing is worse than a single word that overruns it.\nfunction fitEnd(render: (end: number) => string, bounds: number[], floor: number, upperBound: number, charLimit: number): number {\n let lo = 0;\n let hi = bounds.length - 1;\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1;\n if (bounds[mid] <= upperBound) lo = mid;\n else hi = mid - 1;\n }\n for (; lo >= 0 && bounds[lo] > floor; lo--) {\n if (render(bounds[lo]).length <= charLimit) return bounds[lo];\n }\n return floor;\n}\n\ninterface Span {\n start: number;\n end: number;\n}\n\ninterface SnippetWindow extends Span {\n distinct: number;\n count: number;\n}\n\n// occ is start-sorted: a window's first candidate occurrence is a binary search, not a walk.\nfunction firstOccAtOrAfter(occ: Array<{ start: number; end: number; term: string }>, pos: number): number {\n let lo = 0;\n let hi = occ.length;\n while (lo < hi) {\n const mid = (lo + hi) >> 1;\n if (occ[mid].start < pos) lo = mid + 1;\n else hi = mid;\n }\n return lo;\n}\n\nfunction renderWindow(text: string, occ: Array<{ start: number; end: number; term: string }>, win: Span): string {\n let out = '';\n let cursor = win.start;\n for (let i = firstOccAtOrAfter(occ, win.start); i < occ.length && occ[i].start < win.end; i++) {\n const o = occ[i];\n if (o.end > win.end) continue;\n // Occurrences of duplicate or substring-overlapping terms (\"test tests\") can overlap;\n // emitting each would duplicate document text. Keep the first, absorb the rest.\n if (o.start < cursor) continue;\n out += `${text.slice(cursor, o.start)}«${text.slice(o.start, o.end)}»`;\n cursor = o.end;\n }\n out += text.slice(cursor, win.end);\n const prefix = win.start > 0 ? '…' : '';\n const suffix = win.end < text.length ? '…' : '';\n return `${prefix}${out.replace(/\\s+/g, ' ')}${suffix}`;\n}\n\n// Scores every occurrence-anchored candidate window: most distinct terms wins, ties broken by\n// most total hits -- coverage now that occurrences are real words, not the false-fragment counts\n// a substring scan produced. Selection itself is unevaluated (DESIGN.md \"Selection, unsettled\").\n// The window's `end` is fitted to the *rendered* text, markup included, not the raw span: a\n// window dense with marks (every guillemet pair adds two characters) shrinks to fit the same\n// budget a sparse one fills almost exactly.\nfunction scoreWindows(occ: Array<{ start: number; end: number; term: string }>, bounds: number[], text: string, charLimit: number): SnippetWindow[] {\n const candidates: SnippetWindow[] = [];\n for (let i = 0; i < occ.length; i++) {\n const winEnd = occ[i].start + charLimit;\n const seen = new Set<string>();\n let count = 0;\n for (let j = i; j < occ.length && occ[j].start < winEnd; j++) {\n seen.add(occ[j].term);\n count++;\n }\n const start = snapForward(bounds, occ[i].start);\n const floor = Math.max(start, occ[i].end);\n const upperBound = Math.max(floor, cutWindow(text, bounds, occ[i].start, charLimit).end);\n const end = fitEnd((e) => renderWindow(text, occ, { start, end: e }), bounds, floor, upperBound, charLimit);\n candidates.push({ start, end, distinct: seen.size, count });\n }\n return candidates;\n}\n\nfunction overlaps(a: Span, b: Span): boolean {\n return a.start < b.end && b.start < a.end;\n}\n\n// Same scoring function as a single snippet, kept rather than discarded (DESIGN.md \"Several\n// snippets per note\"): rank by score, take the top non-overlapping windows, then reorder to the\n// note's own order, since several passages from one note read as an abridged note.\nfunction selectWindows(candidates: SnippetWindow[], countLimit: number): { chosen: SnippetWindow[]; best: SnippetWindow } {\n const ranked = candidates.slice().sort((a, b) => b.distinct - a.distinct || b.count - a.count);\n const chosen: SnippetWindow[] = [];\n for (const c of ranked) {\n if (chosen.length >= countLimit) break;\n if (chosen.some((s) => overlaps(s, c))) continue;\n chosen.push(c);\n }\n chosen.sort((a, b) => a.start - b.start);\n return { chosen, best: ranked[0] };\n}\n\n// Builds bounded marked passages and returns the best window's offset for section lookup.\nexport function computeSnippets(text: string, terms: string[], charLimit: number, countLimit: number): { snippets: string[]; offset: number } {\n const occ = findOccurrences(text, terms);\n const bounds = wordBoundaries(text);\n if (occ.length === 0) {\n // A term matched only via title/summary, not this body text, finds no occurrence here;\n // fall back to the doc's start, unmarked.\n const render = (end: number): string => `${text.slice(0, end).replace(/\\s+/g, ' ').trim()}${end < text.length ? '…' : ''}`;\n const floor = bounds.find((b) => b > 0) ?? text.length;\n const upperBound = Math.max(floor, cutWindow(text, bounds, 0, charLimit).end);\n const end = fitEnd(render, bounds, floor, upperBound, charLimit);\n return { snippets: [render(end)], offset: 0 };\n }\n const candidates = scoreWindows(occ, bounds, text, charLimit);\n const { chosen, best } = selectWindows(candidates, countLimit);\n return { snippets: chosen.map((win) => renderWindow(text, occ, win)), offset: best.start };\n}\n\n// Converts an offset in the original text to its one-based line number.\nexport function lineNumberAt(text: string, offset: number): number {\n let line = 1;\n for (let i = 0; i < offset; i++) if (text.charCodeAt(i) === 10) line++;\n return line;\n}\n\nasync function lineRangeFor(store: Store, path: string, line: number): Promise<string | null> {\n const stmt = await store.prepare('SELECT start_line, end_line FROM sections WHERE \"path\" = ? AND start_line <= ? AND end_line >= ? ORDER BY start_line DESC LIMIT 1');\n const row = (await stmt.get(path, line, line)) as { start_line: number; end_line: number } | undefined;\n return row ? `L${row.start_line}-${row.end_line}` : null;\n}\n\nexport interface SearchOptions {\n k?: number;\n where?: string; // SQL fragment against frontmatter alias `f`, e.g. \"f.status = 'active'\"\n preset?: string; // named preset; unknown name throws listing declared presets, undefined -> \"default\"\n include?: string[]; // ad hoc scope override (repeatable --include); independent of exclude\n exclude?: string[]; // ad hoc scope override (repeatable --exclude); independent of include\n noExclude?: boolean; // --no-exclude: drop the preset's exclude for this command\n snippetCharLimit?: number; // --snippet-char-limit; defaults to SNIPPET_CHAR_LIMIT_DEFAULT\n snippetCountLimit?: number; // --snippet-count-limit; defaults to SNIPPET_COUNT_LIMIT_DEFAULT\n}\n\n// Hydrates already-ranked rows with snippets and containing-section ranges without reranking.\nexport async function hydrateSearchRows(store: Store, cfg: ResolvedConfig, rows: Row[], matchedPaths: Set<string>, terms: string, opts: Pick<SearchOptions, 'snippetCharLimit' | 'snippetCountLimit'> = {}): Promise<void> {\n if (matchedPaths.size === 0) return;\n const bareTerms = extractBareTerms(terms);\n const charLimit = opts.snippetCharLimit ?? SNIPPET_CHAR_LIMIT_DEFAULT;\n const countLimit = opts.snippetCountLimit ?? SNIPPET_COUNT_LIMIT_DEFAULT;\n for (const row of rows) {\n if (!matchedPaths.has(row.path as string)) continue;\n let text: string;\n try {\n text = readFileSync(join(cfg.rootDir ?? cfg.baseDir, row.path as string), 'utf8');\n } catch {\n continue; // vanished since the match; leave snippets/lines empty rather than throw\n }\n const { snippets, offset } = computeSnippets(text, bareTerms, charLimit, countLimit);\n row.snippets = snippets;\n if (row.lines == null) row.lines = featureEnabled(cfg, 'sections') ? await lineRangeFor(store, row.path as string, lineNumberAt(text, offset)) : null;\n }\n}\n\n// The declared or defaulted signals compose via RRF; `via` names which ones produced each row.\n// `opts` arrives already resolved (config.ts:resolveSearch).\nexport async function search(store: Store, cfg: ResolvedConfig, terms: string, opts: SearchOptions = {}): Promise<Row[]> {\n const effective = resolveSearch(cfg, opts);\n const { k, signals } = effective;\n\n const allPaths = ((await (await store.prepare('SELECT \"path\" FROM frontmatter')).all()) as Array<{ path: string }>).map((r) => r.path);\n const scopePaths = await rawScope(store, cfg, opts, allPaths);\n const scopeActive = scopePaths.size < allPaths.length;\n // The set every candidate pool must be filtered to before truncation: scope narrowed by\n // --where, the same composition scopedPaths() gives the other commands.\n let allowedPaths: Set<string>;\n try {\n allowedPaths = await narrowByWhere(store, scopePaths, effective.where);\n } catch (err) {\n throw searchError(err as Error, terms, effective.where);\n }\n const fetch = Math.max(k * 3, 30);\n\n // A downloadable HF id proceeds -- getProvider fetches it lazily on consent. Only a\n // local path with missing files errors here, since nothing will ever fetch it for itself.\n const wantsVectors = signals.vectors !== undefined;\n if (wantsVectors) {\n const e = embedConfig(cfg); // validate.ts guarantees this is set whenever \"vectors\" is declared\n if (localModelMissing(e)) {\n throw new SenseError('EMBED_MODEL_MISSING', `preset \"${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`);\n }\n }\n const semanticEnabled = wantsVectors && (await scopeHasEmbeddings(store, cfg, allowedPaths));\n\n // --where applies inside the candidate query (a post-filter would drop matches ranked past\n // the pool) and again on the final select, for link-derived rows.\n const scope = effective.where;\n const whereJoin = scope ? `JOIN frontmatter f ON f.\"path\" = content.path` : '';\n const whereCond = scope ? `AND (${scope})` : '';\n // Filtered before LIMIT, not after, or scoped notes ranking below the global top-`fetch`\n // never reach the filter; joined against a temp table since real scopes exceed SQLITE_MAX_VARIABLE_NUMBER.\n if (scopeActive) await materializeScope(store, '_search_scope', scopePaths);\n const scopeCond = scopeActive ? `AND content.path IN (SELECT \"path\" FROM _search_scope)` : '';\n\n const candidates = new Map<string, { score: number; via: string }>();\n let matchRows: LexicalHit[] = [];\n const hasEmptyPhrase = [...terms.matchAll(/\"([^\"]*)\"/g)].some((match) => !/[\\p{L}\\p{N}_]/u.test(match[1]));\n const hasBareSyntaxOnly = /[()]/u.test(terms.replace(/\"[^\"]*\"/g, ' '));\n // A query that is only whitespace or punctuation has no lexical rows; other declared signals\n // compose normally. In particular, unspaced-script punctuation can rewrite to an empty MATCH.\n if (signals.words !== undefined && terms.trim() !== '' && (/[\\p{L}\\p{N}_]/u.test(terms) || hasBareSyntaxOnly) && !hasEmptyPhrase) {\n const syntaxError = bareTermSyntaxError(terms);\n if (syntaxError) throw syntaxError;\n try {\n matchRows = await wordsCandidates(store, candidates, terms, whereJoin, whereCond, scopeCond, fetch, signals.words);\n } catch (err) {\n throw searchError(err as Error, terms, scope);\n }\n }\n // Every store's lexical `snippets` is NULL; the snippet text is always computed below.\n // Distinguish from via='link' rows, which never appear in matchRows.\n const matchedPaths = new Set(matchRows.map((r) => r.path));\n\n if (signals.links !== undefined) await linksCandidates(store, candidates, matchRows, allPaths, allowedPaths, fetch, signals.links);\n\n let chunkLines = new Map<string, string>();\n let chunkSimilarity = new Map<string, number>();\n if (semanticEnabled) {\n ({ chunkLines, chunkSimilarity } = await vectorsCandidates(store, cfg, candidates, terms, fetch, allowedPaths, signals.vectors as number));\n }\n\n await store.exec('DROP TABLE IF EXISTS _search');\n // DOUBLE, not REAL: sqlite's REAL is an 8-byte double but duckdb's is a 4-byte float, and the\n // rounded score/similarity are printed at full precision in rows.\n await store.exec('CREATE TEMP TABLE _search (\"path\" TEXT PRIMARY KEY, score DOUBLE, via TEXT, lines TEXT, similarity DOUBLE)');\n if (candidates.size > 0) {\n await store.runBatch(\n 'INSERT INTO _search (\"path\", score, via, lines, similarity) VALUES (?, ?, ?, ?, ?)',\n [...candidates].map(([path, c]) => [path, c.score, c.via, chunkLines.get(path) ?? null, chunkSimilarity.get(path) ?? null])\n );\n }\n\n // Reapplies --where even though _search is already scope+where filtered, since the join to\n // frontmatter is already needed for the path column.\n const where = scope ? `WHERE (${scope})` : '';\n // lines: semantic rows carry their chunk's range, lexical rows gain one below,\n // everything else stays null; similarity stays semantic-only.\n const similarityCol = semanticEnabled ? ', _search.similarity' : '';\n // NULL AS snippets holds this column's position (between summary and via) for every row;\n // overwritten below, always to an array, never left null (DESIGN.md).\n const selectStmt = await store.prepare(\n `SELECT f.\"path\" AS path, content.title, content.summary, NULL AS snippets, _search.via, round(_search.score, 4) AS score, _search.lines${similarityCol}\n FROM _search JOIN frontmatter f ON f.\"path\" = _search.\"path\" JOIN content ON content.path = _search.\"path\"\n ${where} ORDER BY _search.score DESC LIMIT ?`\n );\n const rows = (await selectStmt.all(k)) as Row[];\n for (const row of rows) row.snippets = [];\n\n await hydrateSearchRows(store, cfg, rows, matchedPaths, terms, opts);\n\n return rows;\n}\n"],"names":["SNIPPET_CHAR_LIMIT_DEFAULT","SNIPPET_COUNT_LIMIT_DEFAULT","computeSnippets","hydrateSearchRows","lineNumberAt","search","extractBareTerms","query","cleaned","replace","split","map","tok","flatMap","searchTokens","text","toLowerCase","trim","filter","length","wordSegmenter","findOccurrences","terms","occ","textLower","unspacedTerms","hasUnspacedRun","unspacedRuns","run","runLower","slice","start","end","term","idx","found","indexOf","push","spacedTerms","t","termByStem","Map","stemmer","foldForSearch","stemInitials","Set","Array","from","keys","match","segment","unspaced","folded","has","get","undefined","sort","a","b","wordBoundaries","Intl","Segmenter","granularity","bounds","index","snapForward","pos","lo","hi","mid","snapBackward","result","firstBoundAfter","cutWindow","anchor","charLimit","rawEnd","Math","min","fitEnd","render","floor","upperBound","firstOccAtOrAfter","renderWindow","win","out","cursor","i","o","prefix","suffix","scoreWindows","winEnd","seen","count","j","add","max","e","candidates","distinct","size","overlaps","selectWindows","countLimit","ranked","chosen","c","some","s","best","find","snippets","offset","line","charCodeAt","lineRangeFor","store","path","stmt","row","prepare","start_line","end_line","cfg","rows","matchedPaths","opts","bareTerms","snippetCharLimit","snippetCountLimit","readFileSync","join","rootDir","baseDir","lines","featureEnabled","effective","k","signals","allPaths","scopePaths","scopeActive","allowedPaths","err","fetch","wantsVectors","semanticEnabled","scope","whereJoin","whereCond","scopeCond","matchRows","hasEmptyPhrase","hasBareSyntaxOnly","syntaxError","chunkLines","chunkSimilarity","where","similarityCol","selectStmt","resolveSearch","all","r","rawScope","narrowByWhere","searchError","vectors","embedConfig","localModelMissing","SenseError","presetName","MODEL_FILENAMES","model","scopeHasEmbeddings","materializeScope","matchAll","test","words","bareTermSyntaxError","wordsCandidates","links","linksCandidates","vectorsCandidates","exec","runBatch","score","via"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/search.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { stemmer } from 'stemmer';\nimport type { ResolvedConfig } from '../config/index.ts';\nimport { embedConfig, featureEnabled, resolveSearch } from '../config/index.ts';\nimport { localModelMissing, MODEL_FILENAMES } from '../embed/store.ts';\nimport { SenseError } from '../errors.ts';\nimport type { Row } from '../output/output.ts';\nimport { bareTermSyntaxError, searchError } from '../output/search-error.ts';\nimport type { LexicalHit, Store } from '../store/types.ts';\nimport { foldForSearch, hasUnspacedRun, searchTokens, unspacedRuns } from '../text/segment.ts';\nimport { materializeScope, narrowByWhere, rawScope, scopeHasEmbeddings } from './scope.ts';\nimport { linksCandidates, vectorsCandidates, wordsCandidates } from './signals.ts';\n\n// Default budget behind --snippet-char-limit/--snippet-count-limit: the limit is whatever the\n// caller passes, these only say what happens when they pass nothing (DESIGN.md \"Search snippets\").\nexport const SNIPPET_CHAR_LIMIT_DEFAULT = 80;\nexport const SNIPPET_COUNT_LIMIT_DEFAULT = 1;\n\n// Bare terms from an FTS5 query string: strips operators/quoting, not real terms, so the\n// snippet scan matches what the query matched -- FTS5 operators are uppercase-only, so this is too.\nfunction extractBareTerms(query: string): string[] {\n const cleaned = query\n .replace(/\"/g, ' ')\n .replace(/[()*]/g, ' ')\n .replace(/\\b(AND|OR|NOT|NEAR)\\b(\\/\\d+)?/g, ' ');\n return cleaned\n .split(/\\s+/)\n .map((tok) => tok.replace(/^[A-Za-z_]\\w*:/, '')) // column filter, e.g. title:term\n .flatMap((tok) => searchTokens(tok).map(({ text }) => text))\n .map((tok) => tok.toLowerCase().trim())\n .filter((tok) => tok.length > 0);\n}\n\n// House convention (src/chunk/group.ts): Intl.Segmenter word boundaries, not a bare \\b, which\n// is ASCII-only. Built on first use: construction is a few ms, paid only when a term matches.\nlet wordSegmenter: Intl.Segmenter | undefined;\n\nfunction findOccurrences(text: string, terms: string[]): Array<{ start: number; end: number; term: string }> {\n const occ: Array<{ start: number; end: number; term: string }> = [];\n const textLower = text.toLowerCase();\n\n // Unspaced scripts (UNSPACED_SCRIPTS) mark no word boundaries: the sense-setup skill promises\n // substring semantics there, so a matching term is found wherever it occurs in the run.\n const unspacedTerms = terms.filter(hasUnspacedRun);\n if (unspacedTerms.length > 0) {\n for (const run of unspacedRuns(text)) {\n const runLower = textLower.slice(run.start, run.end);\n for (const term of unspacedTerms) {\n let idx = 0;\n for (;;) {\n const found = runLower.indexOf(term, idx);\n if (found === -1) break;\n occ.push({ start: run.start + found, end: run.start + found + term.length, term });\n idx = found + term.length;\n }\n }\n }\n }\n\n // Spaced scripts: a word is marked when its porter stem equals a query term's stem, the same\n // algorithm both indexes tokenize with (sqlite's TOKENIZE = 'porter unicode61'; duckdb's fts default).\n const spacedTerms = terms.filter((t) => !hasUnspacedRun(t));\n if (spacedTerms.length > 0) {\n const termByStem = new Map(spacedTerms.map((t) => [stemmer(foldForSearch(t)), t]));\n const stemInitials = new Set(Array.from(termByStem.keys(), (term) => term[0]));\n for (const match of searchTokens(text)) {\n const segment = match.text;\n if (match.unspaced) continue;\n const folded = foldForSearch(segment);\n // Porter stemming changes suffixes only, so a different initial cannot match a query stem.\n if (!stemInitials.has(folded[0])) continue;\n const term = termByStem.get(stemmer(folded));\n if (term !== undefined) occ.push({ start: match.start, end: match.end, term });\n }\n }\n\n return occ.sort((a, b) => a.start - b.start || b.end - a.end);\n}\n\n// Segment boundaries of the whole text, word granularity: the only cut points a snippet edge\n// may land on. One rule for every script (DESIGN.md \"Cutting\") -- a regex \\b is ASCII-only and\n// would mis-cut Japanese, Chinese and Thai, which Intl.Segmenter divides by dictionary instead.\nfunction wordBoundaries(text: string): number[] {\n wordSegmenter ??= new Intl.Segmenter('en', { granularity: 'word' });\n const bounds = [0];\n for (const { index, segment } of wordSegmenter.segment(text)) bounds.push(index + segment.length);\n return bounds;\n}\n\n// bounds is sorted: snaps are binary searches, since each runs once per candidate window.\nfunction snapForward(bounds: number[], pos: number): number {\n let lo = 0;\n let hi = bounds.length - 1;\n while (lo < hi) {\n const mid = (lo + hi) >> 1;\n if (bounds[mid] < pos) lo = mid + 1;\n else hi = mid;\n }\n return bounds[lo];\n}\n\nfunction snapBackward(bounds: number[], pos: number): number {\n let lo = 0;\n let hi = bounds.length - 1;\n let result = bounds[0];\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1;\n if (bounds[mid] <= pos) {\n lo = mid;\n result = bounds[mid];\n } else hi = mid - 1;\n }\n return result;\n}\n\nfunction firstBoundAfter(bounds: number[], pos: number): number | undefined {\n let lo = 0;\n let hi = bounds.length;\n while (lo < hi) {\n const mid = (lo + hi) >> 1;\n if (bounds[mid] <= pos) lo = mid + 1;\n else hi = mid;\n }\n return bounds[lo];\n}\n\n// A raw, unmarked upper bound: [start, end) inside [anchor, anchor+charLimit), edges on\n// boundaries. Markup (guillemets, ellipses) added on top of this can push the rendered result\n// past charLimit, which is what fitEnd() below corrects for; this is only the starting guess.\n// A single segment spanning the whole target window (a word longer than the char limit) is the\n// one case where holding the boundary rule means the budget is not a hard ceiling.\nfunction cutWindow(text: string, bounds: number[], anchor: number, charLimit: number): { start: number; end: number } {\n const rawEnd = Math.min(text.length, anchor + charLimit);\n const start = snapForward(bounds, anchor);\n let end = snapBackward(bounds, rawEnd);\n if (end <= start) end = firstBoundAfter(bounds, start) ?? text.length;\n return { start, end: Math.min(end, text.length) };\n}\n\n// The largest boundary <= upperBound, > floor, whose rendered [*, end) fits charLimit. Markup\n// only ever adds characters as end grows, so rendered length is non-decreasing in end and\n// shrinking from upperBound is guaranteed to find the true fit rather than guess at it.\n// `floor` is the one end this never shrinks past (the anchor's own marked word, or the first\n// word in the unmarked fallback): DESIGN.md's budget is a ceiling except here, where returning\n// nothing is worse than a single word that overruns it.\nfunction fitEnd(render: (end: number) => string, bounds: number[], floor: number, upperBound: number, charLimit: number): number {\n let lo = 0;\n let hi = bounds.length - 1;\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1;\n if (bounds[mid] <= upperBound) lo = mid;\n else hi = mid - 1;\n }\n for (; lo >= 0 && bounds[lo] > floor; lo--) {\n if (render(bounds[lo]).length <= charLimit) return bounds[lo];\n }\n return floor;\n}\n\ninterface Span {\n start: number;\n end: number;\n}\n\ninterface SnippetWindow extends Span {\n distinct: number;\n count: number;\n}\n\n// occ is start-sorted: a window's first candidate occurrence is a binary search, not a walk.\nfunction firstOccAtOrAfter(occ: Array<{ start: number; end: number; term: string }>, pos: number): number {\n let lo = 0;\n let hi = occ.length;\n while (lo < hi) {\n const mid = (lo + hi) >> 1;\n if (occ[mid].start < pos) lo = mid + 1;\n else hi = mid;\n }\n return lo;\n}\n\nfunction renderWindow(text: string, occ: Array<{ start: number; end: number; term: string }>, win: Span): string {\n let out = '';\n let cursor = win.start;\n for (let i = firstOccAtOrAfter(occ, win.start); i < occ.length && occ[i].start < win.end; i++) {\n const o = occ[i];\n if (o.end > win.end) continue;\n // Occurrences of duplicate or substring-overlapping terms (\"test tests\") can overlap;\n // emitting each would duplicate document text. Keep the first, absorb the rest.\n if (o.start < cursor) continue;\n out += `${text.slice(cursor, o.start)}«${text.slice(o.start, o.end)}»`;\n cursor = o.end;\n }\n out += text.slice(cursor, win.end);\n const prefix = win.start > 0 ? '…' : '';\n const suffix = win.end < text.length ? '…' : '';\n return `${prefix}${out.replace(/\\s+/g, ' ')}${suffix}`;\n}\n\n// Scores every occurrence-anchored candidate window: most distinct terms wins, ties broken by\n// most total hits -- coverage now that occurrences are real words, not the false-fragment counts\n// a substring scan produced. Selection itself is unevaluated (DESIGN.md \"Selection, unsettled\").\n// The window's `end` is fitted to the *rendered* text, markup included, not the raw span: a\n// window dense with marks (every guillemet pair adds two characters) shrinks to fit the same\n// budget a sparse one fills almost exactly.\nfunction scoreWindows(occ: Array<{ start: number; end: number; term: string }>, bounds: number[], text: string, charLimit: number): SnippetWindow[] {\n const candidates: SnippetWindow[] = [];\n for (let i = 0; i < occ.length; i++) {\n const winEnd = occ[i].start + charLimit;\n const seen = new Set<string>();\n let count = 0;\n for (let j = i; j < occ.length && occ[j].start < winEnd; j++) {\n seen.add(occ[j].term);\n count++;\n }\n const start = snapForward(bounds, occ[i].start);\n const floor = Math.max(start, occ[i].end);\n const upperBound = Math.max(floor, cutWindow(text, bounds, occ[i].start, charLimit).end);\n const end = fitEnd((e) => renderWindow(text, occ, { start, end: e }), bounds, floor, upperBound, charLimit);\n candidates.push({ start, end, distinct: seen.size, count });\n }\n return candidates;\n}\n\nfunction overlaps(a: Span, b: Span): boolean {\n return a.start < b.end && b.start < a.end;\n}\n\n// Same scoring function as a single snippet, kept rather than discarded (DESIGN.md \"Several\n// snippets per note\"): rank by score, take the top non-overlapping windows, then reorder to the\n// note's own order, since several passages from one note read as an abridged note.\nfunction selectWindows(candidates: SnippetWindow[], countLimit: number): { chosen: SnippetWindow[]; best: SnippetWindow } {\n const ranked = candidates.slice().sort((a, b) => b.distinct - a.distinct || b.count - a.count);\n const chosen: SnippetWindow[] = [];\n for (const c of ranked) {\n if (chosen.length >= countLimit) break;\n if (chosen.some((s) => overlaps(s, c))) continue;\n chosen.push(c);\n }\n chosen.sort((a, b) => a.start - b.start);\n return { chosen, best: ranked[0] };\n}\n\n// Builds bounded marked passages and returns the best window's offset for section lookup.\nexport function computeSnippets(text: string, terms: string[], charLimit: number, countLimit: number): { snippets: string[]; offset: number } {\n const occ = findOccurrences(text, terms);\n const bounds = wordBoundaries(text);\n if (occ.length === 0) {\n // A term matched only via title/summary, not this body text, finds no occurrence here;\n // fall back to the doc's start, unmarked.\n const render = (end: number): string => `${text.slice(0, end).replace(/\\s+/g, ' ').trim()}${end < text.length ? '…' : ''}`;\n const floor = bounds.find((b) => b > 0) ?? text.length;\n const upperBound = Math.max(floor, cutWindow(text, bounds, 0, charLimit).end);\n const end = fitEnd(render, bounds, floor, upperBound, charLimit);\n return { snippets: [render(end)], offset: 0 };\n }\n const candidates = scoreWindows(occ, bounds, text, charLimit);\n const { chosen, best } = selectWindows(candidates, countLimit);\n return { snippets: chosen.map((win) => renderWindow(text, occ, win)), offset: best.start };\n}\n\n// Converts an offset in the original text to its one-based line number.\nexport function lineNumberAt(text: string, offset: number): number {\n let line = 1;\n for (let i = 0; i < offset; i++) if (text.charCodeAt(i) === 10) line++;\n return line;\n}\n\nasync function lineRangeFor(store: Store, path: string, line: number): Promise<string | null> {\n const stmt = await store.prepare('SELECT start_line, end_line FROM sections WHERE \"path\" = ? AND start_line <= ? AND end_line >= ? ORDER BY start_line DESC LIMIT 1');\n const row = (await stmt.get(path, line, line)) as { start_line: number; end_line: number } | undefined;\n return row ? `L${row.start_line}-${row.end_line}` : null;\n}\n\nexport interface SearchOptions {\n k?: number;\n where?: string; // SQL fragment against frontmatter alias `f`, e.g. \"f.status = 'active'\"\n preset?: string; // named preset; unknown name throws listing declared presets, undefined -> \"default\"\n include?: string[]; // ad hoc scope override (repeatable --include); independent of exclude\n exclude?: string[]; // ad hoc scope override (repeatable --exclude); independent of include\n noExclude?: boolean; // --no-exclude: drop the preset's exclude for this command\n snippetCharLimit?: number; // --snippet-char-limit; defaults to SNIPPET_CHAR_LIMIT_DEFAULT\n snippetCountLimit?: number; // --snippet-count-limit; defaults to SNIPPET_COUNT_LIMIT_DEFAULT\n}\n\nfunction validateSearchOptions(opts: SearchOptions): void {\n for (const [name, value] of [\n ['k', opts.k],\n ['snippetCharLimit', opts.snippetCharLimit],\n ['snippetCountLimit', opts.snippetCountLimit],\n ] as const) {\n if (value !== undefined && (!Number.isFinite(value) || !Number.isInteger(value) || value <= 0)) {\n throw new SenseError('SEARCH_OPTION_INVALID', `search option \"${name}\" must be a positive finite integer, got ${String(value)}`);\n }\n }\n}\n\n// Hydrates already-ranked rows with snippets and containing-section ranges without reranking.\nexport async function hydrateSearchRows(store: Store, cfg: ResolvedConfig, rows: Row[], matchedPaths: Set<string>, terms: string, opts: Pick<SearchOptions, 'snippetCharLimit' | 'snippetCountLimit'> = {}): Promise<void> {\n if (matchedPaths.size === 0) return;\n const bareTerms = extractBareTerms(terms);\n const charLimit = opts.snippetCharLimit ?? SNIPPET_CHAR_LIMIT_DEFAULT;\n const countLimit = opts.snippetCountLimit ?? SNIPPET_COUNT_LIMIT_DEFAULT;\n for (const row of rows) {\n if (!matchedPaths.has(row.path as string)) continue;\n let text: string;\n try {\n text = readFileSync(join(cfg.rootDir ?? cfg.baseDir, row.path as string), 'utf8');\n } catch {\n continue; // vanished since the match; leave snippets/lines empty rather than throw\n }\n const { snippets, offset } = computeSnippets(text, bareTerms, charLimit, countLimit);\n row.snippets = snippets;\n if (row.lines == null) row.lines = featureEnabled(cfg, 'sections') ? await lineRangeFor(store, row.path as string, lineNumberAt(text, offset)) : null;\n }\n}\n\n// The declared or defaulted signals compose via RRF; `via` names which ones produced each row.\n// `opts` arrives already resolved (config.ts:resolveSearch).\nexport async function search(store: Store, cfg: ResolvedConfig, terms: string, opts: SearchOptions = {}): Promise<Row[]> {\n validateSearchOptions(opts);\n const effective = resolveSearch(cfg, opts);\n const { k, signals } = effective;\n\n const allPaths = ((await (await store.prepare('SELECT \"path\" FROM frontmatter')).all()) as Array<{ path: string }>).map((r) => r.path);\n const scopePaths = await rawScope(store, cfg, opts, allPaths);\n const scopeActive = scopePaths.size < allPaths.length;\n // The set every candidate pool must be filtered to before truncation: scope narrowed by\n // --where, the same composition scopedPaths() gives the other commands.\n let allowedPaths: Set<string>;\n try {\n allowedPaths = await narrowByWhere(store, scopePaths, effective.where);\n } catch (err) {\n throw searchError(err as Error, terms, effective.where);\n }\n const fetch = Math.max(k * 3, 30);\n\n // A downloadable HF id proceeds -- getProvider fetches it lazily on consent. Only a\n // local path with missing files errors here, since nothing will ever fetch it for itself.\n const wantsVectors = signals.vectors !== undefined;\n if (wantsVectors) {\n const e = embedConfig(cfg); // validate.ts guarantees this is set whenever \"vectors\" is declared\n if (localModelMissing(e)) {\n throw new SenseError('EMBED_MODEL_MISSING', `preset \"${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`);\n }\n }\n const semanticEnabled = wantsVectors && (await scopeHasEmbeddings(store, cfg, allowedPaths));\n\n // --where applies inside the candidate query (a post-filter would drop matches ranked past\n // the pool) and again on the final select, for link-derived rows.\n const scope = effective.where;\n const whereJoin = scope ? `JOIN frontmatter f ON f.\"path\" = content.path` : '';\n const whereCond = scope ? `AND (${scope})` : '';\n // Filtered before LIMIT, not after, or scoped notes ranking below the global top-`fetch`\n // never reach the filter; joined against a temp table since real scopes exceed SQLITE_MAX_VARIABLE_NUMBER.\n if (scopeActive) await materializeScope(store, '_search_scope', scopePaths);\n const scopeCond = scopeActive ? `AND content.path IN (SELECT \"path\" FROM _search_scope)` : '';\n\n const candidates = new Map<string, { score: number; via: string }>();\n let matchRows: LexicalHit[] = [];\n const hasEmptyPhrase = [...terms.matchAll(/\"([^\"]*)\"/g)].some((match) => !/[\\p{L}\\p{N}_]/u.test(match[1]));\n const hasBareSyntaxOnly = /[()]/u.test(terms.replace(/\"[^\"]*\"/g, ' '));\n // A query that is only whitespace or punctuation has no lexical rows; other declared signals\n // compose normally. In particular, unspaced-script punctuation can rewrite to an empty MATCH.\n if (signals.words !== undefined && terms.trim() !== '' && (/[\\p{L}\\p{N}_]/u.test(terms) || hasBareSyntaxOnly) && !hasEmptyPhrase) {\n const syntaxError = bareTermSyntaxError(terms);\n if (syntaxError) throw syntaxError;\n try {\n matchRows = await wordsCandidates(store, candidates, terms, whereJoin, whereCond, scopeCond, fetch, signals.words);\n } catch (err) {\n throw searchError(err as Error, terms, scope);\n }\n }\n // Every store's lexical `snippets` is NULL; the snippet text is always computed below.\n // Distinguish from via='link' rows, which never appear in matchRows.\n const matchedPaths = new Set(matchRows.map((r) => r.path));\n\n if (signals.links !== undefined) await linksCandidates(store, candidates, matchRows, allPaths, allowedPaths, fetch, signals.links);\n\n let chunkLines = new Map<string, string>();\n let chunkSimilarity = new Map<string, number>();\n if (semanticEnabled) {\n ({ chunkLines, chunkSimilarity } = await vectorsCandidates(store, cfg, candidates, terms, fetch, allowedPaths, signals.vectors as number));\n }\n\n await store.exec('DROP TABLE IF EXISTS _search');\n // DOUBLE, not REAL: sqlite's REAL is an 8-byte double but duckdb's is a 4-byte float, and the\n // rounded score/similarity are printed at full precision in rows.\n await store.exec('CREATE TEMP TABLE _search (\"path\" TEXT PRIMARY KEY, score DOUBLE, via TEXT, lines TEXT, similarity DOUBLE)');\n if (candidates.size > 0) {\n await store.runBatch(\n 'INSERT INTO _search (\"path\", score, via, lines, similarity) VALUES (?, ?, ?, ?, ?)',\n [...candidates].map(([path, c]) => [path, c.score, c.via, chunkLines.get(path) ?? null, chunkSimilarity.get(path) ?? null])\n );\n }\n\n // Reapplies --where even though _search is already scope+where filtered, since the join to\n // frontmatter is already needed for the path column.\n const where = scope ? `WHERE (${scope})` : '';\n // lines: semantic rows carry their chunk's range, lexical rows gain one below,\n // everything else stays null; similarity stays semantic-only.\n const similarityCol = semanticEnabled ? ', _search.similarity' : '';\n // NULL AS snippets holds this column's position (between summary and via) for every row;\n // overwritten below, always to an array, never left null (DESIGN.md).\n const selectStmt = await store.prepare(\n `SELECT f.\"path\" AS path, content.title, content.summary, NULL AS snippets, _search.via, round(_search.score, 4) AS score, _search.lines${similarityCol}\n FROM _search JOIN frontmatter f ON f.\"path\" = _search.\"path\" JOIN content ON content.path = _search.\"path\"\n ${where} ORDER BY _search.score DESC LIMIT ?`\n );\n const rows = (await selectStmt.all(k)) as Row[];\n for (const row of rows) row.snippets = [];\n\n await hydrateSearchRows(store, cfg, rows, matchedPaths, terms, opts);\n\n return rows;\n}\n"],"names":["SNIPPET_CHAR_LIMIT_DEFAULT","SNIPPET_COUNT_LIMIT_DEFAULT","computeSnippets","hydrateSearchRows","lineNumberAt","search","extractBareTerms","query","cleaned","replace","split","map","tok","flatMap","searchTokens","text","toLowerCase","trim","filter","length","wordSegmenter","findOccurrences","terms","occ","textLower","unspacedTerms","hasUnspacedRun","unspacedRuns","run","runLower","slice","start","end","term","idx","found","indexOf","push","spacedTerms","t","termByStem","Map","stemmer","foldForSearch","stemInitials","Set","Array","from","keys","match","segment","unspaced","folded","has","get","undefined","sort","a","b","wordBoundaries","Intl","Segmenter","granularity","bounds","index","snapForward","pos","lo","hi","mid","snapBackward","result","firstBoundAfter","cutWindow","anchor","charLimit","rawEnd","Math","min","fitEnd","render","floor","upperBound","firstOccAtOrAfter","renderWindow","win","out","cursor","i","o","prefix","suffix","scoreWindows","winEnd","seen","count","j","add","max","e","candidates","distinct","size","overlaps","selectWindows","countLimit","ranked","chosen","c","some","s","best","find","snippets","offset","line","charCodeAt","lineRangeFor","store","path","stmt","row","prepare","start_line","end_line","validateSearchOptions","opts","k","snippetCharLimit","snippetCountLimit","name","value","Number","isFinite","isInteger","SenseError","String","cfg","rows","matchedPaths","bareTerms","readFileSync","join","rootDir","baseDir","lines","featureEnabled","effective","signals","allPaths","scopePaths","scopeActive","allowedPaths","err","fetch","wantsVectors","semanticEnabled","scope","whereJoin","whereCond","scopeCond","matchRows","hasEmptyPhrase","hasBareSyntaxOnly","syntaxError","chunkLines","chunkSimilarity","where","similarityCol","selectStmt","resolveSearch","all","r","rawScope","narrowByWhere","searchError","vectors","embedConfig","localModelMissing","presetName","MODEL_FILENAMES","model","scopeHasEmbeddings","materializeScope","matchAll","test","words","bareTermSyntaxError","wordsCandidates","links","linksCandidates","vectorsCandidates","exec","runBatch","score","via"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/config/types.ts"],"sourcesContent":["import type { SignalWeights } from './signals.ts';\n\nexport const CONFIG_FILENAME = 'sense.config.json';\nexport const STATE_DIR = '.sense';\n\n// Highest sense.config.json `version` this build understands. Older versions auto-migrate on load.\nexport const SUPPORTED_CONFIG_VERSION = 5;\n\n// Each feature owns its tables, parse-time extraction, and reconcile step; commands degrade when one is off.\n// links/sections/tags/rank are opt-out toggles in `features`; embed is on exactly when an `embed` block names a model.\nexport const FEATURE_NAMES = ['links', 'sections', 'tags', 'rank', 'embed'] as const;\nexport type FeatureName = (typeof FEATURE_NAMES)[number];\n\n// `embed` names the model and gives the tree vectors at all; a preset's `signals` says which\n// engines that scope's searches use.\nexport interface EmbedConfig {\n model?: string;\n provider?: 'static' | 'openai' | 'cohere';\n url?: string; // openai provider: OpenAI-compatible base URL, e.g. http://localhost:11434/v1\n key?: string; // openai/cohere provider: name of the env var holding the bearer token, if any\n languages?: string[]; // owner-declared model languages; enables the language-fit check where no card exists\n chunkTokens?: number; // chunk size ceiling in estimated tokens for small-context models; default 500\n}\n\nexport const DEFAULT_EMBED_MODEL = 'minishlab/potion-retrieval-32M';\n\n// The single declaration every other store name derives from (Store.name, the store/index.ts\n// registry, validate.ts's accepted set). An array, not a bare union, so runtime checks read the same list.\nexport const STORE_NAMES = ['sqlite', 'duckdb', 'turso'] as const;\nexport type StoreName = (typeof STORE_NAMES)[number];\n\n// A named, self-contained file-selection scope: include/exclude are globby patterns resolved\n// relative to the config file. No inheritance between presets; they may overlap freely (views, not partitions).\nexport interface Preset {\n include: string[];\n exclude?: string[];\n k?: number; // result count for `search` scoped to this preset; default 10\n // Exhaustive when present: every signal this preset's searches compose, each mapped to its RRF\n // weight (finite > 0; presence is enablement). Absent = every signal whose prerequisite holds, at weight 1 (words always, links when on, vectors when a model is named).\n signals?: SignalWeights;\n where?: string; // standing SQL condition against frontmatter alias `f`\n}\n\n// A saved `search` invocation: `sense <name>` runs like\n// `sense search <search> [--preset] [--include] [--exclude] [--where] [--k]`.\nexport interface SavedSearch {\n search: string;\n preset?: string;\n include?: string[];\n exclude?: string[];\n where?: string;\n k?: number;\n}\n\n// An entry names the verb it runs, one to one with the commands: { sql } runs like\n// `sense sql`, { search } like `sense search`.\nexport type SavedQuery = { sql: string } | SavedSearch;\n\nexport interface Config {\n // Editor-only pointer to schema.json; never read by sense.\n $schema?: string;\n version?: number;\n // Markdown tree to index. Relative paths resolve from the configuration file's directory;\n // omitted keeps the historic colocated-config behavior.\n root?: string;\n // File selection, index-time, and per-preset search defaults. A file is indexed iff any\n // preset's include/exclude covers it (union). `default` is used when a command names no preset.\n presets: Record<string, Preset>;\n // Global feature defaults; absent block or key means enabled. embed is not a member here --\n // see the `embed` block.\n features?: { links?: boolean; sections?: boolean; tags?: boolean; rank?: boolean };\n // Names the embedding model. Present means every indexed file gets vectors; absent means none do.\n embed?: EmbedConfig;\n // Backing store engine: \"sqlite\" (default, zero-dependency), \"duckdb\", or \"turso\" (optional\n // dependency, installed on first use). What differs per store is declared through CAPABILITIES (store/types.ts).\n store?: StoreName;\n queries: Record<string, SavedQuery>;\n}\n\nexport interface ResolvedConfig extends Config {\n // Configuration and state ownership are deliberately separate from the indexed tree.\n configDir?: string;\n rootDir?: string;\n // Compatibility for callers that construct a ResolvedConfig for `open()` directly. New code\n // uses rootDir/configDir; openStore normalizes this alias to both when necessary.\n baseDir: string;\n configPath: string | null;\n // Keys the file declares that this build does not read; cli reports them.\n unknownKeys?: string[];\n // Set when loadConfig auto-migrated the file on disk; cli reports it.\n migratedFrom?: number;\n}\n\nexport interface SearchOverrides {\n preset?: string;\n k?: number;\n where?: string;\n include?: string[];\n exclude?: string[];\n noExclude?: boolean; // drop the preset's exclude for this command; --exclude still applies\n}\n\nexport interface EffectiveSearch {\n presetName: string;\n k: number;\n where?: string;\n include: string[];\n exclude?: string[];\n signals: SignalWeights; // this preset's effective signal weights\n}\n"],"names":["CONFIG_FILENAME","DEFAULT_EMBED_MODEL","FEATURE_NAMES","STATE_DIR","STORE_NAMES","SUPPORTED_CONFIG_VERSION"],"mappings":";;;;;;;;;;;QAEaA;eAAAA;;QAsBAC;eAAAA;;QAdAC;eAAAA;;QAPAC;eAAAA;;QAyBAC;eAAAA;;QAtBAC;eAAAA;;;AAJN,IAAML,kBAAkB;AACxB,IAAMG,YAAY;AAGlB,IAAME,2BAA2B;AAIjC,IAAMH,gBAAgB;IAAC;IAAS;IAAY;IAAQ;IAAQ;CAAQ;AAcpE,IAAMD,sBAAsB;AAI5B,IAAMG,cAAc;IAAC;IAAU;IAAU;CAAQ"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/config/types.ts"],"sourcesContent":["import type { SignalWeights } from './signals.ts';\n\nexport const CONFIG_FILENAME = 'sense.config.json';\nexport const STATE_DIR = '.sense';\n\n// Highest sense.config.json `version` this build understands. Older versions auto-migrate on load.\nexport const SUPPORTED_CONFIG_VERSION = 5;\n\n// Each feature owns its tables, parse-time extraction, and reconcile step; commands degrade when one is off.\n// links/sections/tags/rank are opt-out toggles in `features`; embed is on exactly when an `embed` block names a model.\nexport const FEATURE_NAMES = ['links', 'sections', 'tags', 'rank', 'embed'] as const;\nexport type FeatureName = (typeof FEATURE_NAMES)[number];\n\n// `embed` names the model and gives the tree vectors at all; a preset's `signals` says which\n// engines that scope's searches use.\nexport interface EmbedConfig {\n model?: string;\n provider?: 'static' | 'openai' | 'cohere';\n url?: string; // openai provider: OpenAI-compatible base URL, e.g. http://localhost:11434/v1\n key?: string; // openai/cohere provider: name of the env var holding the bearer token, if any\n languages?: string[]; // owner-declared model languages; enables the language-fit check where no card exists\n chunkTokens?: number; // chunk size ceiling in estimated tokens for small-context models; default 500\n}\n\nexport const DEFAULT_EMBED_MODEL = 'minishlab/potion-retrieval-32M';\n\n// The single declaration every other store name derives from (Store.name, the store/index.ts\n// registry, validate.ts's accepted set). An array, not a bare union, so runtime checks read the same list.\nexport const STORE_NAMES = ['sqlite', 'duckdb', 'turso'] as const;\nexport type StoreName = (typeof STORE_NAMES)[number];\n\n// A named, self-contained file-selection scope: include/exclude are globby patterns resolved\n// relative to the configured tree root. No inheritance between presets; they may overlap freely (views, not partitions).\nexport interface Preset {\n include: string[];\n exclude?: string[];\n k?: number; // result count for `search` scoped to this preset; default 10\n // Exhaustive when present: every signal this preset's searches compose, each mapped to its RRF\n // weight (finite > 0; presence is enablement). Absent = every signal whose prerequisite holds, at weight 1 (words always, links when on, vectors when a model is named).\n signals?: SignalWeights;\n where?: string; // standing SQL condition against frontmatter alias `f`\n}\n\n// A saved `search` invocation: `sense <name>` runs like\n// `sense search <search> [--preset] [--include] [--exclude] [--where] [--k]`.\nexport interface SavedSearch {\n search: string;\n preset?: string;\n include?: string[];\n exclude?: string[];\n where?: string;\n k?: number;\n}\n\n// An entry names the verb it runs, one to one with the commands: { sql } runs like\n// `sense sql`, { search } like `sense search`.\nexport type SavedQuery = { sql: string } | SavedSearch;\n\nexport interface Config {\n // Editor-only pointer to schema.json; never read by sense.\n $schema?: string;\n version?: number;\n // Markdown tree to index. Relative paths resolve from the configuration file's directory;\n // omitted keeps the historic colocated-config behavior.\n root?: string;\n // File selection, index-time, and per-preset search defaults. A file is indexed iff any\n // preset's include/exclude covers it (union). `default` is used when a command names no preset.\n presets: Record<string, Preset>;\n // Global feature defaults; absent block or key means enabled. embed is not a member here --\n // see the `embed` block.\n features?: { links?: boolean; sections?: boolean; tags?: boolean; rank?: boolean };\n // Names the embedding model. Present means every indexed file gets vectors; absent means none do.\n embed?: EmbedConfig;\n // Backing store engine: \"sqlite\" (default, zero-dependency), \"duckdb\", or \"turso\" (optional\n // dependency, installed on first use). What differs per store is declared through CAPABILITIES (store/types.ts).\n store?: StoreName;\n queries: Record<string, SavedQuery>;\n}\n\nexport interface ResolvedConfig extends Config {\n // Configuration and state ownership are deliberately separate from the indexed tree.\n configDir?: string;\n rootDir?: string;\n // Compatibility for callers that construct a ResolvedConfig for `open()` directly. New code\n // uses rootDir/configDir; openStore normalizes this alias to both when necessary.\n baseDir: string;\n configPath: string | null;\n // Keys the file declares that this build does not read; cli reports them.\n unknownKeys?: string[];\n // Set when loadConfig auto-migrated the file on disk; cli reports it.\n migratedFrom?: number;\n}\n\nexport interface SearchOverrides {\n preset?: string;\n k?: number;\n where?: string;\n include?: string[];\n exclude?: string[];\n noExclude?: boolean; // drop the preset's exclude for this command; --exclude still applies\n}\n\nexport interface EffectiveSearch {\n presetName: string;\n k: number;\n where?: string;\n include: string[];\n exclude?: string[];\n signals: SignalWeights; // this preset's effective signal weights\n}\n"],"names":["CONFIG_FILENAME","DEFAULT_EMBED_MODEL","FEATURE_NAMES","STATE_DIR","STORE_NAMES","SUPPORTED_CONFIG_VERSION"],"mappings":";;;;;;;;;;;QAEaA;eAAAA;;QAsBAC;eAAAA;;QAdAC;eAAAA;;QAPAC;eAAAA;;QAyBAC;eAAAA;;QAtBAC;eAAAA;;;AAJN,IAAML,kBAAkB;AACxB,IAAMG,YAAY;AAGlB,IAAME,2BAA2B;AAIjC,IAAMH,gBAAgB;IAAC;IAAS;IAAY;IAAQ;IAAQ;CAAQ;AAcpE,IAAMD,sBAAsB;AAI5B,IAAMG,cAAc;IAAC;IAAU;IAAU;CAAQ"}
@@ -1,4 +1,4 @@
1
- export type SenseErrorCode = 'CONFIG_NOT_FOUND' | 'CONFIG_EXISTS' | 'CONFIG_INVALID' | 'CONFIG_VERSION_UNSUPPORTED' | 'WATCH_ACTIVE' | 'NOTE_NOT_FOUND' | 'NOTE_AMBIGUOUS' | 'EMBED_DISABLED' | 'EMBED_MODEL' | 'EMBED_MODEL_MISSING' | 'EMBED_MODEL_MISMATCH' | 'SEARCH_SYNTAX' | 'COLUMN_LIMIT' | 'PRESET_UNKNOWN' | 'PRESET_NOT_SEMANTIC' | 'NOTE_NOT_EMBEDDED' | 'STORE_UNKNOWN' | 'STORE_DEPENDENCY_MISSING' | 'STORE_CAPABILITY_MISSING' | 'STORE_BUSY' | 'LEXICAL_NULL_PATH';
1
+ export type SenseErrorCode = 'CONFIG_NOT_FOUND' | 'CONFIG_EXISTS' | 'CONFIG_INVALID' | 'CONFIG_VERSION_UNSUPPORTED' | 'WATCH_ACTIVE' | 'NOTE_NOT_FOUND' | 'NOTE_AMBIGUOUS' | 'EMBED_DISABLED' | 'EMBED_MODEL' | 'EMBED_MODEL_MISSING' | 'EMBED_MODEL_MISMATCH' | 'SEARCH_OPTION_INVALID' | 'SEARCH_SYNTAX' | 'COLUMN_LIMIT' | 'PRESET_UNKNOWN' | 'PRESET_NOT_SEMANTIC' | 'NOTE_NOT_EMBEDDED' | 'STORE_UNKNOWN' | 'STORE_DEPENDENCY_MISSING' | 'STORE_CAPABILITY_MISSING' | 'STORE_BUSY' | 'LEXICAL_NULL_PATH';
2
2
  export declare class SenseError extends Error {
3
3
  code: SenseErrorCode;
4
4
  constructor(code: SenseErrorCode, message: string);