sensemaking 0.9.3 → 0.9.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.
package/README.md CHANGED
@@ -1,18 +1,12 @@
1
1
  # sensemaking
2
2
 
3
- Query and search a tree of markdown notes: SQL over frontmatter and links, ranked search over
4
- the prose — words, links, and meaning fused. A CLI that starts, answers, and exits — no
5
- server, no build step.
3
+ Query and search your markdown notes with context-aware progressive disclosure: SQL over frontmatter, links, and text, plus semantic search and link-graph ranking. No server, no build step.
6
4
 
7
5
  ## Problem
8
6
 
9
- Working with AI agents produces piles of small notes. Past a few dozen, finding the right ones
10
- means grepping or reading whole folders into context. The structure that makes notes navigable —
11
- frontmatter, wikilinks, headings — is exactly what an agent needs, but nothing exposes it as a
12
- query surface.
7
+ Markdown notes accumulate: research, decisions, meeting notes, agent output. Past a few dozen, finding the ones relevant to what you're doing means grepping or reading whole folders into context. The structure that makes notes navigable (frontmatter, wikilinks, headings) is exactly what a query needs, but nothing exposes it as a query surface.
13
8
 
14
- `sense` indexes all of it into SQLite and reconciles against file timestamps on every query, so
15
- results are never stale and nothing has to be running.
9
+ `sense` indexes all of it into SQLite and reconciles against file timestamps on every query, so results are never stale and nothing has to be running.
16
10
 
17
11
  ## Quick start
18
12
 
@@ -21,8 +15,7 @@ npm install -g sensemaking
21
15
  cd your-notes && sense init
22
16
  ```
23
17
 
24
- Needs Node 22.16 or newer: that is the first release whose built-in SQLite carries FTS5, which
25
- `sense search` indexes prose with.
18
+ Needs Node 22.16 or newer: that is the first release whose built-in SQLite carries FTS5, which `sense search` indexes prose with.
26
19
 
27
20
  ```bash
28
21
  sense map # orient: fields, hub notes, recent changes
@@ -42,14 +35,9 @@ Every file becomes rows in these tables, plus whatever an enabled feature adds o
42
35
  | `links` | `src`, `target` as written, `dst` resolved (`NULL` = dead link) | graph |
43
36
  | `sections` | heading, `level`, `start_line`, `end_line`, `tokens` estimate | structure |
44
37
 
45
- Results are references path, title, summary, excerpt never file contents. Reading happens
46
- afterward through the filesystem, scoped to the line ranges `peek` returns. This is the
47
- [just-in-time context pattern](https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents):
48
- the agent holds lightweight identifiers and loads payloads only when needed.
38
+ Results are references (path, title, summary, excerpt), 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.
49
39
 
50
- Output size is a contract, measured per release ([BENCHMARKING.md](BENCHMARKING.md)): `map` is
51
- fixed-size, a `find` row is tens of tokens, and a `peek` stays flat however large the note is —
52
- so what it saves over reading grows with the file, and a small note is cheaper read whole.
40
+ Output size is a contract, measured per release ([BENCHMARKING.md](BENCHMARKING.md)): `map` is fixed-size, a `find` 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.
53
41
 
54
42
  ```sql
55
43
  -- filter and search compose in one query
@@ -65,7 +53,7 @@ ORDER BY bm25(content, 10.0, 5.0, 1.0) LIMIT 10
65
53
  |---|---|
66
54
  | `map` | doc count, frontmatter field coverage, top hubs by link rank, recent changes |
67
55
  | `search "<text>" [--preset name] [--include glob] [--where "<sql>"] [--k n] [--lexical]` | words + links + vectors, one fused ranked list; `via` labels each row's evidence |
68
- | `peek <path>` | frontmatter + heading outline (`[L143-162, ~380t]`) + links both ways first 20 per list, each with its total; the `links` table has the rest |
56
+ | `peek <path>` | frontmatter + heading outline (`[L143-162, ~380t]`) + links both ways: first 20 per list, each with its total; the `links` table has the rest |
69
57
  | `query "<sql>" [params...]` | ad-hoc SQL over all the tables; `?` binds positional args |
70
58
  | `<name> [params...]` | run a query saved in the config; `--list` names them |
71
59
  | `init` | write a starter `sense.config.json` |
@@ -74,21 +62,11 @@ ORDER BY bm25(content, 10.0, 5.0, 1.0) LIMIT 10
74
62
  | `rebuild` | delete the cache and re-crawl |
75
63
  | `watch` | keep the index warm in the background (optional; see [WATCH.md](WATCH.md)) |
76
64
 
77
- `search` runs one text through every engine its scope has FTS5 word match (BM25-ranked,
78
- bare words AND-join, operators are yours), a personalized-PageRank walk over the link graph,
79
- and vector similarity — fused into one list. `via` labels each row's evidence (`match`,
80
- `link`, `vector`, combinations); `similarity` is the cosine against the best-matching chunk;
81
- `lines` points at the section that earned the row — a direct read range. Rows that only
82
- vectors produced are the "these words aren't in the tree, this is what's near in meaning"
83
- signal. `--preset` picks a named settings bundle from the config, `--lexical` skips vectors
84
- for one command, `--where` filters on frontmatter. `--format json` on any reporting command returns
85
- structured output; `--version` and `--help` do what they say.
65
+ `search` runs one text through every engine its scope has: FTS5 word match (BM25-ranked, bare words AND-join, operators are yours), 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); `similarity` is the cosine against the best-matching 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, `--lexical` skips vectors for one command, `--where` filters on frontmatter. `--format json` on any reporting command returns structured output; `--version` and `--help` do what they say.
86
66
 
87
67
  ## Config
88
68
 
89
- `sense init` writes `sense.config.json`; discovery walks up from cwd like git
90
- (`--config <path>` overrides). Three keys: **presets** (named, self-contained setting
91
- bundles), **queries** (saved commands), and the version.
69
+ `sense init` writes `sense.config.json`; discovery walks up from cwd like git (`--config <path>` overrides).
92
70
 
93
71
  ```json
94
72
  {
@@ -106,46 +84,27 @@ bundles), **queries** (saved commands), and the version.
106
84
  }
107
85
  ```
108
86
 
109
- A preset bundles `include`/`exclude` globs (which files), `k` (result count), `semantic`
110
- (vectors, on unless `false`), and `where` (a standing SQL filter). Bare commands use
111
- `default`; `--preset` names another; flags override single fields. **Indexing derives from
112
- the presets**: a file is indexed if any preset includes it, and embedded if any covering
113
- preset has semantic on so a `semantic: false` preset's files cost no vectors, and
114
- `status` shows each preset's coverage. Editing a preset rebuilds the cache and says which
115
- preset caused it.
87
+ | key | holds |
88
+ |---|---|
89
+ | `presets` | named bundles of `include`/`exclude` globs, `k` (result count), `semantic` (vectors, on unless `false`), `where` (a standing SQL filter). A file is indexed if any preset includes it, embedded if any covering preset has `semantic` on. A `semantic: false` preset's files cost no vectors, and `status` shows each preset's coverage. |
90
+ | `queries` | a SQL string (`?` binds positional args) or a saved search with its settings baked in: `sense hot` needs no flags. `sense check` runs them all, so a typo'd column fails at check time instead of mid-task. |
91
+ | `version` | schema version; older configs auto-migrate on load, noted on stderr. |
116
92
 
117
- A query entry is a SQL string (`?` binds positional args) or a saved search with its
118
- settings baked in — `sense hot` needs no flags. `sense check` runs them all, so a typo'd
119
- column fails at check time instead of mid-task. Older config versions auto-migrate on
120
- load, noted on stderr.
93
+ 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.
121
94
 
122
- Vectors use the built-in static model (downloaded to `~/.cache/sensemaking` on first use,
123
- never in the package); an optional top-level `"embed": { "model", "type", "url", "key" }`
124
- block points at any Model2Vec model, local path, or OpenAI-compatible endpoint (Ollama,
125
- LM Studio, hosted). Embedding happens on the first semantic search, with progress.
95
+ Vectors use the built-in static model (downloaded to `~/.cache/sensemaking` on first use, never in the package); an optional top-level `"embed": { "model", "type", "url", "key" }` block points at any Model2Vec model, local path, or OpenAI-compatible endpoint (Ollama, LM Studio, hosted). Embedding happens on the first semantic search, with progress.
126
96
 
127
- `has(field, value)` is the one custom SQL function: array membership on JSON-array fields,
128
- substring on strings, false on missing keys. Frontmatter parsing is lenient — syntax errors are
129
- per-file warnings and the values are still indexed, so one bad note never costs you the crawl.
97
+ `has(field, value)` is the one custom SQL function: array membership on JSON-array fields, substring on strings, false on missing keys. 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.
130
98
 
131
99
  ## Scale
132
100
 
133
- Every query starts with a freshness check against the cache in `.sense/`; only changed files are
134
- re-parsed. What to expect as a tree grows:
101
+ Every query starts with a freshness check against the cache in `.sense/`; only changed files are re-parsed. What to expect as a tree grows:
135
102
 
136
- - **Work is linear in note count** — crawl, reconcile, and the freshness check every invocation
137
- pays. That check is the floor cost of a query and the first thing to watch on a large tree.
138
- - **Output is flat.** `map`, `peek`, and a `find` row cost the same on a small tree as a large
139
- one: context cost is bounded by what you ask for, not by how much there is.
140
- - **Bulk changes are paid by whoever queries next.** `sense watch` moves that re-parse into the
141
- background ([WATCH.md](WATCH.md)) — it changes latency, never answers, since every query
142
- reconciles for itself. `sense rebuild` starts the cache over.
103
+ - **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.
104
+ - **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.
105
+ - **Bulk changes are paid by whoever queries next.** `sense watch` moves that re-parse into the background ([WATCH.md](WATCH.md)): it changes latency, never answers, since every query reconciles for itself. `sense rebuild` starts the cache over.
143
106
 
144
- These are release gates rather than hopes: every release regenerates the numbers on pinned
145
- corpora spanning a 4x range in note count plus a stress tree that packs the worst measured
146
- shapes into one place — a megabyte-scale note, heading-dense outlines, dense link graphs,
147
- hundreds of frontmatter fields. A row that grows faster than linearly, or a token count
148
- that grows at all, blocks the release. Current figures: [BENCHMARKING.md](BENCHMARKING.md).
107
+ These are release gates rather than hopes: every release regenerates the numbers on pinned corpora spanning a 4x range in note count plus a stress tree that packs the worst measured shapes into one place: a megabyte-scale note, heading-dense outlines, dense link graphs, hundreds of frontmatter fields. A row that grows faster than linearly, or a token count that grows at all, blocks the release. Current figures: [BENCHMARKING.md](BENCHMARKING.md).
149
108
 
150
109
  ## For AI agents
151
110
 
@@ -153,42 +112,25 @@ that grows at all, blocks the release. Current figures: [BENCHMARKING.md](BENCHM
153
112
  npx skills add kmalakoff/sensemaking # -g for global, -a claude-code to target
154
113
  ```
155
114
 
156
- Two skills: `sense` for querying a tree what each command is for, FTS5 syntax, reading the
157
- `via`/`score`/`similarity` columns, worked examples — and `sense-setup` for making one, where
158
- features, frontmatter conventions, and note size are decisions with consequences either way.
115
+ Two skills: `sense` for querying a tree (what each command is for, FTS5 syntax, reading the `via`/`score`/`similarity` columns, worked examples) and `sense-setup` for making one, where features, frontmatter conventions, and note size are decisions with consequences either way.
159
116
 
160
117
  ## Prior art
161
118
 
162
- - [Effective context engineering for AI agents](https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents)
163
- (Anthropic): agents should hold lightweight identifiers file paths, links and load payloads
164
- just in time, because context is a finite resource. The commands implement that pattern as a CLI.
165
- - [llm-wiki](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) (Karpathy): an
166
- agent-maintained wiki navigated by an `index.md` and links, which he notes needs real search
167
- infrastructure past a few hundred pages. `sense map` derives that index from the notes instead of
168
- maintaining it; `find` is the hybrid local search it calls for.
169
- - Agent memory patterns — llm-wiki's raw/wiki split, Claude Code's dreaming-style nightly
170
- consolidation — are trees of small notes with metadata, links, and layers of differing
171
- authority. sense is the query layer such patterns need (filter by metadata and age, scope
172
- by layer, surface near-duplicates semantically), not an implementation of any one of them.
119
+ - [Effective context engineering for AI agents](https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents) (Anthropic): agents should hold lightweight identifiers (file paths, links) and load payloads just in time, because context is a finite resource. The commands implement that pattern as a CLI.
120
+ - [llm-wiki](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) (Karpathy): an agent-maintained wiki navigated by an `index.md` and links, which he notes needs real search infrastructure past a few hundred pages. `sense map` derives that index from the notes instead of maintaining it; `find` is the hybrid local search it calls for.
121
+ - Agent memory patterns (llm-wiki's raw/wiki split, Claude Code's dreaming-style nightly consolidation) are trees of small notes with metadata, links, and layers of differing authority. sense is the query layer such patterns need: filter by metadata and age, scope by layer, surface near-duplicates semantically. It isn't an implementation of any one of them.
173
122
 
174
123
  ## Alternatives
175
124
 
176
- - **Obsidian Bases/Dataview** same filters, but only inside the running app; agents can't
177
- query it headless.
178
- - **Index-on-build tools (MarkdownDB)** query a snapshot; `sense` reconciles on every query.
179
- - **Note CLIs (zk)** fixed schema; `sense` filters on arbitrary frontmatter.
180
- - **RAG / vector stores** similarity can't express `WHERE status = 'active'`. Here vectors
181
- are one signal inside `search`: same SQLite file, filters compose, every row labels its
182
- evidence (`via`), and a preset turns vectors off per layer of the tree no second store,
183
- no daemon, no native builds.
184
-
185
- Dependencies: [yaml](https://github.com/eemeli/yaml),
186
- [remove-markdown](https://github.com/zuchka/remove-markdown),
187
- [fast-glob](https://github.com/mrmlnc/fast-glob),
188
- [@huggingface/tokenizers](https://github.com/huggingface/tokenizers.js) (pure JS),
189
- [getopts-compat](https://github.com/kmalakoff/getopts) and
190
- [exit-compat](https://github.com/kmalakoff/exit-compat) for the CLI, and Node's
191
- built-in SQLite. No native builds.
125
+ - **Obsidian Bases/Dataview:** same filters, but only inside the running app; agents can't query it headless.
126
+ - **Index-on-build tools (MarkdownDB):** query a snapshot; `sense` reconciles on every query.
127
+ - **Note CLIs (zk):** fixed schema; `sense` filters on arbitrary frontmatter.
128
+ - **Graph/LSP tools (IWE):** structural queries over a markdown graph via LSP/CLI/MCP, retrieval by structure rather than similarity; no SQL, no vector search.
129
+ - **Markdown vector stores (markdown-vdb):** hybrid BM25 + vector search over markdown files, no frontmatter filtering; `sense` treats vectors as one signal alongside SQL, not the whole store.
130
+ - **RAG / vector stores:** similarity can't express `WHERE status = 'active'`. Here vectors are one signal inside `search`: same SQLite file, filters compose, every row labels its evidence (`via`), and a preset turns vectors off per layer of the tree. No second store, no daemon, no native builds.
131
+ - **Document-OS apps (Anytype, Logseq, SilverBullet, Capacities):** full applications with their own UI and storage. `sense` is headless: your files stay files, there's no app to run.
132
+
133
+ Dependencies: [yaml](https://github.com/eemeli/yaml), [remove-markdown](https://github.com/zuchka/remove-markdown), [fast-glob](https://github.com/mrmlnc/fast-glob), [@huggingface/tokenizers](https://github.com/huggingface/tokenizers.js) (pure JS), and Node's built-in SQLite. No native builds.
192
134
 
193
135
  ## License
194
136
 
@@ -11,7 +11,6 @@ Object.defineProperty(exports, "default", {
11
11
  var _commandsts = require("../commands.js");
12
12
  var _dbts = require("../db.js");
13
13
  var _outputts = require("../output.js");
14
- var _exitts = require("./exit.js");
15
14
  var _indexts = require("./index.js");
16
15
  var _sharedts = require("./shared.js");
17
16
  function _array_like_to_array(arr, len) {
@@ -387,7 +386,7 @@ var check = function check(ctx) {
387
386
  ];
388
387
  }
389
388
  (0, _outputts.printRows)(rows, format);
390
- if (failed > 0) throw new _exitts.ExitError(1);
389
+ if (failed > 0) process.exit(1);
391
390
  return [
392
391
  2
393
392
  ];
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/cli/check.ts"],"sourcesContent":["import { search } from '../commands.ts';\nimport { open } from '../db.ts';\nimport type { Row } from '../output.ts';\nimport { printRows } from '../output.ts';\nimport { ExitError } from './exit.ts';\nimport { USAGE } from './index.ts';\nimport { CONFIG, FORMAT, formatOf, parse, printWarnings } from './shared.ts';\nimport type { Command } from './types.ts';\n\n// A saved query that returns zero rows looks identical to a true empty result, so a broken\n// one can sit unnoticed -- one field report had `WHERE flag = 'true'` against a numeric\n// column reading as \"nothing tagged yet\" for hours. Preparing each query catches syntax and\n// unknown-column errors without executing it; queries that take no parameters are also run\n// for a row count. Parameterised queries are validated but not counted: inventing arguments\n// would report a count for a query nobody ran. Saved searches run lexically with k=1 -- that\n// validates the `where` fragment, the FTS5 terms, and the `preset` name (resolveSearch throws\n// on an unknown one, the same breakage class prepare() catches for SQL strings) -- but never\n// semantically (a semantic pass costs model time and, on api trees, network). Whether a\n// result being empty is good or bad is the reader's judgment -- there is no assertion path.\nconst check: Command = async (ctx) => {\n const { values } = parse(ctx.argv, `usage: ${ctx.name} ${USAGE.check}`, { ...FORMAT, ...CONFIG });\n const format = formatOf(values);\n const cfg = ctx.resolveConfig(values.config as string | undefined);\n const { db, warnings } = open(cfg);\n printWarnings(warnings);\n\n const rows: Row[] = [];\n let failed = 0;\n for (const [name, entry] of Object.entries(cfg.queries ?? {})) {\n if (typeof entry === 'object' && entry !== null && 'search' in entry) {\n try {\n const count = (await search(db, cfg, entry.search, { k: 1, where: entry.where, preset: entry.preset, include: entry.include, semantic: false })).length;\n rows.push({ query: name, params: '—', rows: '—', status: count === 0 ? 'ok, but matches 0 notes (lexical probe)' : 'ok (probe run with k=1)' });\n } catch (err) {\n failed++;\n rows.push({ query: name, params: '—', rows: '—', status: `FAILED: ${(err as Error).message}` });\n }\n continue;\n }\n const sql = typeof entry === 'string' ? entry : entry.sql;\n const params = (sql.match(/\\?/g) ?? []).length;\n try {\n const statement = db.prepare(sql);\n if (params > 0) {\n rows.push({ query: name, params, rows: '—', status: 'ok (not run: needs parameters)' });\n continue;\n }\n const count = (statement.all() as unknown[]).length;\n rows.push({ query: name, params, rows: count, status: count === 0 ? 'ok, but returns 0 rows' : 'ok' });\n } catch (err) {\n failed++;\n rows.push({ query: name, params, rows: '—', status: `FAILED: ${(err as Error).message}` });\n }\n }\n\n db.close();\n if (rows.length === 0) {\n console.log('no saved queries in config');\n return;\n }\n printRows(rows, format);\n if (failed > 0) throw new ExitError(1);\n};\nexport default check;\n"],"names":["check","ctx","cfg","values","format","open","db","warnings","rows","failed","name","entry","sql","count","err","params","statement","parse","argv","USAGE","FORMAT","CONFIG","formatOf","resolveConfig","config","printWarnings","Object","entries","queries","search","k","where","preset","include","semantic","length","push","query","status","message","match","prepare","all","close","console","log","printRows","ExitError"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/cli/check.ts"],"sourcesContent":["import { search } from '../commands.ts';\nimport { open } from '../db.ts';\nimport type { Row } from '../output.ts';\nimport { printRows } from '../output.ts';\nimport { USAGE } from './index.ts';\nimport { CONFIG, FORMAT, formatOf, parse, printWarnings } from './shared.ts';\nimport type { Command } from './types.ts';\n\n// A saved query that returns zero rows looks identical to a true empty result, so a broken\n// one can sit unnoticed -- one field report had `WHERE flag = 'true'` against a numeric\n// column reading as \"nothing tagged yet\" for hours. Preparing each query catches syntax and\n// unknown-column errors without executing it; queries that take no parameters are also run\n// for a row count. Parameterised queries are validated but not counted: inventing arguments\n// would report a count for a query nobody ran. Saved searches run lexically with k=1 -- that\n// validates the `where` fragment, the FTS5 terms, and the `preset` name (resolveSearch throws\n// on an unknown one, the same breakage class prepare() catches for SQL strings) -- but never\n// semantically (a semantic pass costs model time and, on api trees, network). Whether a\n// result being empty is good or bad is the reader's judgment -- there is no assertion path.\nconst check: Command = async (ctx) => {\n const { values } = parse(ctx.argv, `usage: ${ctx.name} ${USAGE.check}`, { ...FORMAT, ...CONFIG });\n const format = formatOf(values);\n const cfg = ctx.resolveConfig(values.config as string | undefined);\n const { db, warnings } = open(cfg);\n printWarnings(warnings);\n\n const rows: Row[] = [];\n let failed = 0;\n for (const [name, entry] of Object.entries(cfg.queries ?? {})) {\n if (typeof entry === 'object' && entry !== null && 'search' in entry) {\n try {\n const count = (await search(db, cfg, entry.search, { k: 1, where: entry.where, preset: entry.preset, include: entry.include, semantic: false })).length;\n rows.push({ query: name, params: '—', rows: '—', status: count === 0 ? 'ok, but matches 0 notes (lexical probe)' : 'ok (probe run with k=1)' });\n } catch (err) {\n failed++;\n rows.push({ query: name, params: '—', rows: '—', status: `FAILED: ${(err as Error).message}` });\n }\n continue;\n }\n const sql = typeof entry === 'string' ? entry : entry.sql;\n const params = (sql.match(/\\?/g) ?? []).length;\n try {\n const statement = db.prepare(sql);\n if (params > 0) {\n rows.push({ query: name, params, rows: '—', status: 'ok (not run: needs parameters)' });\n continue;\n }\n const count = (statement.all() as unknown[]).length;\n rows.push({ query: name, params, rows: count, status: count === 0 ? 'ok, but returns 0 rows' : 'ok' });\n } catch (err) {\n failed++;\n rows.push({ query: name, params, rows: '—', status: `FAILED: ${(err as Error).message}` });\n }\n }\n\n db.close();\n if (rows.length === 0) {\n console.log('no saved queries in config');\n return;\n }\n printRows(rows, format);\n if (failed > 0) process.exit(1);\n};\nexport default check;\n"],"names":["check","ctx","cfg","values","format","open","db","warnings","rows","failed","name","entry","sql","count","err","params","statement","parse","argv","USAGE","FORMAT","CONFIG","formatOf","resolveConfig","config","printWarnings","Object","entries","queries","search","k","where","preset","include","semantic","length","push","query","status","message","match","prepare","all","close","console","log","printRows","process","exit"],"mappings":"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"}
@@ -14,7 +14,6 @@ Object.defineProperty(exports, // Fallback when the first positional is not a co
14
14
  });
15
15
  var _commandsts = require("../commands.js");
16
16
  var _outputts = require("../output.js");
17
- var _exitts = require("./exit.js");
18
17
  var _sharedts = require("./shared.js");
19
18
  function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
20
19
  try {
@@ -184,7 +183,11 @@ function named(ctx, queryName) {
184
183
  configPath = values.config;
185
184
  cfg = ctx.resolveConfig(configPath);
186
185
  entry = cfg.queries[queryName];
187
- if (entry === undefined) (0, _exitts.usageError)('unknown query: "'.concat(queryName, '"'), "valid queries: ".concat(Object.keys(cfg.queries).sort().join(', ')));
186
+ if (entry === undefined) {
187
+ console.error('unknown query: "'.concat(queryName, '"'));
188
+ console.error("valid queries: ".concat(Object.keys(cfg.queries).sort().join(', ')));
189
+ process.exit(2);
190
+ }
188
191
  if (typeof entry === 'string') {
189
192
  (0, _sharedts.runSql)(cfg, entry, params, format, 'query "'.concat(queryName, '"'));
190
193
  return [
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/cli/named.ts"],"sourcesContent":["import { search } from '../commands.ts';\nimport { printRows } from '../output.ts';\nimport { usageError } from './exit.ts';\nimport { CONFIG, FORMAT, formatOf, parse, parseK, runSql, SEARCH_FLAGS, withDb } from './shared.ts';\nimport type { Ctx } from './types.ts';\n\n// Fallback when the first positional is not a command: a named query from config, either a\n// SQL string (run as-is) or a saved search (run through the `search` command's own machinery).\n// Flags cover both shapes -- SEARCH_FLAGS override a saved search's fields the same way they\n// override a preset's.\nexport default async function named(ctx: Ctx, queryName: string): Promise<void> {\n const usage = `usage: ${ctx.name} ${queryName} [params...] [--format table|json] [--config path] [--where \"<sql>\"] [--k n] [--preset name] [--include glob ...] [--lexical]`;\n const { values, positionals: params } = parse(ctx.argv, usage, { ...SEARCH_FLAGS, ...FORMAT, ...CONFIG });\n const format = formatOf(values);\n const configPath = values.config as string | undefined;\n\n const cfg = ctx.resolveConfig(configPath);\n const entry = cfg.queries[queryName];\n if (entry === undefined) usageError(`unknown query: \"${queryName}\"`, `valid queries: ${Object.keys(cfg.queries).sort().join(', ')}`);\n\n if (typeof entry === 'string') {\n runSql(cfg, entry, params, format, `query \"${queryName}\"`);\n return;\n }\n if ('sql' in entry) {\n runSql(cfg, entry.sql, params, format, `query \"${queryName}\"`);\n return;\n }\n\n // A saved search's text is fixed in config; there is nowhere for a positional to bind.\n if (params.length > 0) {\n ctx.usageError(`\"${queryName}\" is a saved search and takes no positional parameters; edit its \"search\" in sense.config.json, or use \"${ctx.name} search\" directly`);\n }\n const k = parseK(values.k as string | undefined, ctx.usageError) ?? entry.k;\n const where = (values.where as string | undefined) ?? entry.where;\n const preset = (values.preset as string | undefined) ?? entry.preset;\n const include = (values.include as string[] | undefined) ?? entry.include;\n // --lexical upgrades a saved search's semantic behavior the same way --where/--k\n // override; absent means the saved value (the flag has no default false override).\n const semantic = values.lexical ? false : entry.semantic;\n await withDb(ctx, configPath, async (db, resolvedCfg) => printRows(await search(db, resolvedCfg, entry.search, { k, where, preset, include, semantic }), format));\n}\n"],"names":["named","ctx","queryName","parseK","values","usage","parse","params","format","configPath","cfg","entry","k","where","preset","include","semantic","name","argv","SEARCH_FLAGS","FORMAT","CONFIG","positionals","formatOf","config","resolveConfig","queries","undefined","usageError","Object","keys","sort","join","runSql","sql","length","lexical","withDb","db","resolvedCfg","search","printRows"],"mappings":";;;;+BAMA,2FAA2F;AAC3F,+FAA+F;AAC/F,6FAA6F;AAC7F,uBAAuB;AACvB;;;eAA8BA;;;0BAVP;wBACG;sBACC;wBAC2D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAOvE,SAAeA,MAAMC,GAAQ,EAAEC,SAAiB;;YAuBnDC,SACKC,eACCA,gBACCA,iBAzBXC,OACkCC,QAAhCF,QAAqBG,QACvBC,QACAC,YAEAC,KACAC,OAgBAC,GACAC,OACAC,QACAC,SAGAC;;;;oBA5BAX,QAAQ,AAAC,UAAqBH,OAAZD,IAAIgB,IAAI,EAAC,KAAa,OAAVf,WAAU;oBACNI,SAAAA,IAAAA,eAAK,EAACL,IAAIiB,IAAI,EAAEb,OAAO,mBAAKc,sBAAY,EAAKC,gBAAM,EAAKC,gBAAM,IAA9FjB,SAAgCE,OAAhCF,QAAqBG,SAAWD,OAAxBgB;oBACVd,SAASe,IAAAA,kBAAQ,EAACnB;oBAClBK,aAAaL,OAAOoB,MAAM;oBAE1Bd,MAAMT,IAAIwB,aAAa,CAAChB;oBACxBE,QAAQD,IAAIgB,OAAO,CAACxB,UAAU;oBACpC,IAAIS,UAAUgB,WAAWC,IAAAA,kBAAU,EAAC,AAAC,mBAA4B,OAAV1B,WAAU,MAAI,AAAC,kBAA4D,OAA3C2B,OAAOC,IAAI,CAACpB,IAAIgB,OAAO,EAAEK,IAAI,GAAGC,IAAI,CAAC;oBAE5H,IAAI,OAAOrB,UAAU,UAAU;wBAC7BsB,IAAAA,gBAAM,EAACvB,KAAKC,OAAOJ,QAAQC,QAAQ,AAAC,UAAmB,OAAVN,WAAU;wBACvD;;;oBACF;oBACA,IAAI,SAASS,OAAO;wBAClBsB,IAAAA,gBAAM,EAACvB,KAAKC,MAAMuB,GAAG,EAAE3B,QAAQC,QAAQ,AAAC,UAAmB,OAAVN,WAAU;wBAC3D;;;oBACF;oBAEA,uFAAuF;oBACvF,IAAIK,OAAO4B,MAAM,GAAG,GAAG;wBACrBlC,IAAI2B,UAAU,CAAC,AAAC,IAAuH3B,OAApHC,WAAU,4GAAmH,OAATD,IAAIgB,IAAI,EAAC;oBAClJ;oBACML,KAAIT,UAAAA,IAAAA,gBAAM,EAACC,OAAOQ,CAAC,EAAwBX,IAAI2B,UAAU,eAArDzB,qBAAAA,UAA0DQ,MAAMC,CAAC;oBACrEC,SAAST,gBAAAA,OAAOS,KAAK,cAAZT,2BAAAA,gBAAuCO,MAAME,KAAK;oBAC3DC,UAAUV,iBAAAA,OAAOU,MAAM,cAAbV,4BAAAA,iBAAwCO,MAAMG,MAAM;oBAC9DC,WAAWX,kBAAAA,OAAOW,OAAO,cAAdX,6BAAAA,kBAA2CO,MAAMI,OAAO;oBACzE,iFAAiF;oBACjF,mFAAmF;oBAC7EC,WAAWZ,OAAOgC,OAAO,GAAG,QAAQzB,MAAMK,QAAQ;oBACxD;;wBAAMqB,IAAAA,gBAAM,EAACpC,KAAKQ,YAAY,SAAO6B,IAAIC;;;;;4CAA0B;;gDAAMC,IAAAA,kBAAM,EAACF,IAAIC,aAAa5B,MAAM6B,MAAM,EAAE;oDAAE5B,GAAAA;oDAAGC,OAAAA;oDAAOC,QAAAA;oDAAQC,SAAAA;oDAASC,UAAAA;gDAAS;;;;;gDAA5FyB,mBAAS;oDAAC;oDAAsFjC;;;;;;;;;oBAAzJ;;;;;;IACF"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/cli/named.ts"],"sourcesContent":["import { search } from '../commands.ts';\nimport { printRows } from '../output.ts';\nimport { CONFIG, FORMAT, formatOf, parse, parseK, runSql, SEARCH_FLAGS, withDb } from './shared.ts';\nimport type { Ctx } from './types.ts';\n\n// Fallback when the first positional is not a command: a named query from config, either a\n// SQL string (run as-is) or a saved search (run through the `search` command's own machinery).\n// Flags cover both shapes -- SEARCH_FLAGS override a saved search's fields the same way they\n// override a preset's.\nexport default async function named(ctx: Ctx, queryName: string): Promise<void> {\n const usage = `usage: ${ctx.name} ${queryName} [params...] [--format table|json] [--config path] [--where \"<sql>\"] [--k n] [--preset name] [--include glob ...] [--lexical]`;\n const { values, positionals: params } = parse(ctx.argv, usage, { ...SEARCH_FLAGS, ...FORMAT, ...CONFIG });\n const format = formatOf(values);\n const configPath = values.config as string | undefined;\n\n const cfg = ctx.resolveConfig(configPath);\n const entry = cfg.queries[queryName];\n if (entry === undefined) {\n console.error(`unknown query: \"${queryName}\"`);\n console.error(`valid queries: ${Object.keys(cfg.queries).sort().join(', ')}`);\n process.exit(2);\n }\n\n if (typeof entry === 'string') {\n runSql(cfg, entry, params, format, `query \"${queryName}\"`);\n return;\n }\n if ('sql' in entry) {\n runSql(cfg, entry.sql, params, format, `query \"${queryName}\"`);\n return;\n }\n\n // A saved search's text is fixed in config; there is nowhere for a positional to bind.\n if (params.length > 0) {\n ctx.usageError(`\"${queryName}\" is a saved search and takes no positional parameters; edit its \"search\" in sense.config.json, or use \"${ctx.name} search\" directly`);\n }\n const k = parseK(values.k as string | undefined, ctx.usageError) ?? entry.k;\n const where = (values.where as string | undefined) ?? entry.where;\n const preset = (values.preset as string | undefined) ?? entry.preset;\n const include = (values.include as string[] | undefined) ?? entry.include;\n // --lexical upgrades a saved search's semantic behavior the same way --where/--k\n // override; absent means the saved value (the flag has no default false override).\n const semantic = values.lexical ? false : entry.semantic;\n await withDb(ctx, configPath, async (db, resolvedCfg) => printRows(await search(db, resolvedCfg, entry.search, { k, where, preset, include, semantic }), format));\n}\n"],"names":["named","ctx","queryName","parseK","values","usage","parse","params","format","configPath","cfg","entry","k","where","preset","include","semantic","name","argv","SEARCH_FLAGS","FORMAT","CONFIG","positionals","formatOf","config","resolveConfig","queries","undefined","console","error","Object","keys","sort","join","process","exit","runSql","sql","length","usageError","lexical","withDb","db","resolvedCfg","search","printRows"],"mappings":";;;;+BAKA,2FAA2F;AAC3F,+FAA+F;AAC/F,6FAA6F;AAC7F,uBAAuB;AACvB;;;eAA8BA;;;0BATP;wBACG;wBAC4D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAOvE,SAAeA,MAAMC,GAAQ,EAAEC,SAAiB;;YA2BnDC,SACKC,eACCA,gBACCA,iBA7BXC,OACkCC,QAAhCF,QAAqBG,QACvBC,QACAC,YAEAC,KACAC,OAoBAC,GACAC,OACAC,QACAC,SAGAC;;;;oBAhCAX,QAAQ,AAAC,UAAqBH,OAAZD,IAAIgB,IAAI,EAAC,KAAa,OAAVf,WAAU;oBACNI,SAAAA,IAAAA,eAAK,EAACL,IAAIiB,IAAI,EAAEb,OAAO,mBAAKc,sBAAY,EAAKC,gBAAM,EAAKC,gBAAM,IAA9FjB,SAAgCE,OAAhCF,QAAqBG,SAAWD,OAAxBgB;oBACVd,SAASe,IAAAA,kBAAQ,EAACnB;oBAClBK,aAAaL,OAAOoB,MAAM;oBAE1Bd,MAAMT,IAAIwB,aAAa,CAAChB;oBACxBE,QAAQD,IAAIgB,OAAO,CAACxB,UAAU;oBACpC,IAAIS,UAAUgB,WAAW;wBACvBC,QAAQC,KAAK,CAAC,AAAC,mBAA4B,OAAV3B,WAAU;wBAC3C0B,QAAQC,KAAK,CAAC,AAAC,kBAA4D,OAA3CC,OAAOC,IAAI,CAACrB,IAAIgB,OAAO,EAAEM,IAAI,GAAGC,IAAI,CAAC;wBACrEC,QAAQC,IAAI,CAAC;oBACf;oBAEA,IAAI,OAAOxB,UAAU,UAAU;wBAC7ByB,IAAAA,gBAAM,EAAC1B,KAAKC,OAAOJ,QAAQC,QAAQ,AAAC,UAAmB,OAAVN,WAAU;wBACvD;;;oBACF;oBACA,IAAI,SAASS,OAAO;wBAClByB,IAAAA,gBAAM,EAAC1B,KAAKC,MAAM0B,GAAG,EAAE9B,QAAQC,QAAQ,AAAC,UAAmB,OAAVN,WAAU;wBAC3D;;;oBACF;oBAEA,uFAAuF;oBACvF,IAAIK,OAAO+B,MAAM,GAAG,GAAG;wBACrBrC,IAAIsC,UAAU,CAAC,AAAC,IAAuHtC,OAApHC,WAAU,4GAAmH,OAATD,IAAIgB,IAAI,EAAC;oBAClJ;oBACML,KAAIT,UAAAA,IAAAA,gBAAM,EAACC,OAAOQ,CAAC,EAAwBX,IAAIsC,UAAU,eAArDpC,qBAAAA,UAA0DQ,MAAMC,CAAC;oBACrEC,SAAST,gBAAAA,OAAOS,KAAK,cAAZT,2BAAAA,gBAAuCO,MAAME,KAAK;oBAC3DC,UAAUV,iBAAAA,OAAOU,MAAM,cAAbV,4BAAAA,iBAAwCO,MAAMG,MAAM;oBAC9DC,WAAWX,kBAAAA,OAAOW,OAAO,cAAdX,6BAAAA,kBAA2CO,MAAMI,OAAO;oBACzE,iFAAiF;oBACjF,mFAAmF;oBAC7EC,WAAWZ,OAAOoC,OAAO,GAAG,QAAQ7B,MAAMK,QAAQ;oBACxD;;wBAAMyB,IAAAA,gBAAM,EAACxC,KAAKQ,YAAY,SAAOiC,IAAIC;;;;;4CAA0B;;gDAAMC,IAAAA,kBAAM,EAACF,IAAIC,aAAahC,MAAMiC,MAAM,EAAE;oDAAEhC,GAAAA;oDAAGC,OAAAA;oDAAOC,QAAAA;oDAAQC,SAAAA;oDAASC,UAAAA;gDAAS;;;;;gDAA5F6B,mBAAS;oDAAC;oDAAsFrC;;;;;;;;;oBAAzJ;;;;;;IACF"}
@@ -1,23 +1,18 @@
1
+ import type { ParseArgsOptionsConfig } from 'node:util';
1
2
  import type { ResolvedConfig } from '../config.js';
2
3
  import type { OpenResult } from '../db.js';
3
4
  import type { Ctx } from './types.js';
4
- export interface Flag {
5
- type: 'string' | 'boolean';
6
- default?: string | boolean;
7
- multiple?: boolean;
8
- short?: string;
9
- }
10
- export type Flags = Record<string, Flag>;
11
- export declare const FORMAT: Flags;
12
- export declare const CONFIG: Flags;
13
- export declare const SEARCH_FLAGS: Flags;
14
- export type Values = Record<string, string | boolean | string[] | undefined>;
5
+ export declare const FORMAT: ParseArgsOptionsConfig;
6
+ export declare const CONFIG: ParseArgsOptionsConfig;
7
+ export declare const SEARCH_FLAGS: ParseArgsOptionsConfig;
8
+ type Values = Record<string, string | boolean | string[] | undefined>;
15
9
  export declare function formatOf(values: Values): 'table' | 'json';
16
- export declare function parse(argv: string[], usage: string, flags?: Flags): {
10
+ export declare function parse(argv: string[], usage: string, options: ParseArgsOptionsConfig): {
17
11
  values: Values;
18
12
  positionals: string[];
19
13
  };
20
- export declare function parseK(k: string | undefined, usageErrorFn: (message: string) => never): number | undefined;
14
+ export declare function parseK(k: string | undefined, usageError: (message: string) => never): number | undefined;
21
15
  export declare function printWarnings(warnings: string[]): void;
22
16
  export declare function withDb(ctx: Ctx, configPath: string | undefined, fn: (db: OpenResult['db'], cfg: ResolvedConfig) => void | Promise<void>): Promise<void>;
23
17
  export declare function runSql(cfg: ResolvedConfig, sql: string, params: string[], format: 'table' | 'json', label: string): void;
18
+ export {};
@@ -1,23 +1,18 @@
1
+ import type { ParseArgsOptionsConfig } from 'node:util';
1
2
  import type { ResolvedConfig } from '../config.js';
2
3
  import type { OpenResult } from '../db.js';
3
4
  import type { Ctx } from './types.js';
4
- export interface Flag {
5
- type: 'string' | 'boolean';
6
- default?: string | boolean;
7
- multiple?: boolean;
8
- short?: string;
9
- }
10
- export type Flags = Record<string, Flag>;
11
- export declare const FORMAT: Flags;
12
- export declare const CONFIG: Flags;
13
- export declare const SEARCH_FLAGS: Flags;
14
- export type Values = Record<string, string | boolean | string[] | undefined>;
5
+ export declare const FORMAT: ParseArgsOptionsConfig;
6
+ export declare const CONFIG: ParseArgsOptionsConfig;
7
+ export declare const SEARCH_FLAGS: ParseArgsOptionsConfig;
8
+ type Values = Record<string, string | boolean | string[] | undefined>;
15
9
  export declare function formatOf(values: Values): 'table' | 'json';
16
- export declare function parse(argv: string[], usage: string, flags?: Flags): {
10
+ export declare function parse(argv: string[], usage: string, options: ParseArgsOptionsConfig): {
17
11
  values: Values;
18
12
  positionals: string[];
19
13
  };
20
- export declare function parseK(k: string | undefined, usageErrorFn: (message: string) => never): number | undefined;
14
+ export declare function parseK(k: string | undefined, usageError: (message: string) => never): number | undefined;
21
15
  export declare function printWarnings(warnings: string[]): void;
22
16
  export declare function withDb(ctx: Ctx, configPath: string | undefined, fn: (db: OpenResult['db'], cfg: ResolvedConfig) => void | Promise<void>): Promise<void>;
23
17
  export declare function runSql(cfg: ResolvedConfig, sql: string, params: string[], format: 'table' | 'json', label: string): void;
18
+ export {};
@@ -37,19 +37,15 @@ _export(exports, {
37
37
  return withDb;
38
38
  }
39
39
  });
40
- var _getoptscompat = /*#__PURE__*/ _interop_require_default(require("getopts-compat"));
40
+ var _nodeutil = require("node:util");
41
41
  var _dbts = require("../db.js");
42
42
  var _outputts = require("../output.js");
43
43
  var _searcherrorts = require("../search-error.js");
44
- var _exitts = require("./exit.js");
45
44
  function _array_like_to_array(arr, len) {
46
45
  if (len == null || len > arr.length) len = arr.length;
47
46
  for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
48
47
  return arr2;
49
48
  }
50
- function _array_with_holes(arr) {
51
- if (Array.isArray(arr)) return arr;
52
- }
53
49
  function _array_without_holes(arr) {
54
50
  if (Array.isArray(arr)) return _array_like_to_array(arr);
55
51
  }
@@ -95,41 +91,9 @@ function _define_property(obj, key, value) {
95
91
  }
96
92
  return obj;
97
93
  }
98
- function _interop_require_default(obj) {
99
- return obj && obj.__esModule ? obj : {
100
- default: obj
101
- };
102
- }
103
94
  function _iterable_to_array(iter) {
104
95
  if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
105
96
  }
106
- function _iterable_to_array_limit(arr, i) {
107
- var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
108
- if (_i == null) return;
109
- var _arr = [];
110
- var _n = true;
111
- var _d = false;
112
- var _s, _e;
113
- try {
114
- for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
115
- _arr.push(_s.value);
116
- if (i && _arr.length === i) break;
117
- }
118
- } catch (err) {
119
- _d = true;
120
- _e = err;
121
- } finally{
122
- try {
123
- if (!_n && _i["return"] != null) _i["return"]();
124
- } finally{
125
- if (_d) throw _e;
126
- }
127
- }
128
- return _arr;
129
- }
130
- function _non_iterable_rest() {
131
- throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
132
- }
133
97
  function _non_iterable_spread() {
134
98
  throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
135
99
  }
@@ -172,9 +136,6 @@ function _object_spread_props(target, source) {
172
136
  }
173
137
  return target;
174
138
  }
175
- function _sliced_to_array(arr, i) {
176
- return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest();
177
- }
178
139
  function _to_consumable_array(arr) {
179
140
  return _array_without_holes(arr) || _iterable_to_array(arr) || _unsupported_iterable_to_array(arr) || _non_iterable_spread();
180
141
  }
@@ -318,100 +279,41 @@ var SEARCH_FLAGS = {
318
279
  function formatOf(values) {
319
280
  return values.format === 'json' ? 'json' : 'table';
320
281
  }
321
- function parse(argv, usage) {
322
- var flags = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : {};
323
- var table = _object_spread_props(_object_spread({}, flags), {
324
- help: {
325
- type: 'boolean',
326
- default: false,
327
- short: 'h'
328
- }
329
- });
330
- var string = [];
331
- var boolean = [];
332
- var alias = {};
333
- var defaults = {};
334
- var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
282
+ function parse(argv, usage, options) {
283
+ var values;
284
+ var positionals;
335
285
  try {
336
- for(var _iterator = Object.entries(table)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
337
- var _step_value = _sliced_to_array(_step.value, 2), name = _step_value[0], flag = _step_value[1];
338
- (flag.type === 'boolean' ? boolean : string).push(name);
339
- if (flag.default !== undefined) defaults[name] = flag.default;
340
- if (flag.short !== undefined) alias[name] = flag.short;
341
- }
286
+ var ref;
287
+ ref = (0, _nodeutil.parseArgs)({
288
+ args: argv,
289
+ options: _object_spread_props(_object_spread({}, options), {
290
+ help: {
291
+ type: 'boolean',
292
+ default: false,
293
+ short: 'h'
294
+ }
295
+ }),
296
+ strict: true,
297
+ allowPositionals: true
298
+ }), values = ref.values, positionals = ref.positionals, ref;
342
299
  } catch (err) {
343
- _didIteratorError = true;
344
- _iteratorError = err;
345
- } finally{
346
- try {
347
- if (!_iteratorNormalCompletion && _iterator.return != null) {
348
- _iterator.return();
349
- }
350
- } finally{
351
- if (_didIteratorError) {
352
- throw _iteratorError;
353
- }
354
- }
300
+ console.error(err.message);
301
+ console.error(usage);
302
+ process.exit(2);
355
303
  }
356
- // getopts is lenient about undeclared flags -- they land in the result and are silently
357
- // ignored, which is the bug 0.9.2 fixed. Record the first and fail below; returning false
358
- // also keeps it out of the result. `--k -1` arrives here as unknown option "1", because
359
- // getopts reads a leading dash as a new option rather than as --k's value; the message is
360
- // blunter than the old one but it is still a usage error rather than a silent default.
361
- var unknown;
362
- var parsed = (0, _getoptscompat.default)(argv, {
363
- string: string,
364
- boolean: boolean,
365
- alias: alias,
366
- default: defaults,
367
- unknown: function unknown1(name) {
368
- if (unknown === undefined) unknown = name;
369
- return false;
370
- }
371
- });
372
- if (unknown !== undefined) (0, _exitts.usageError)("unknown option: ".concat(unknown), usage);
373
- if (parsed.help) {
304
+ if (values.help) {
374
305
  console.log(usage);
375
- throw new _exitts.ExitError(0);
376
- }
377
- // Two getopts shapes the callers must not see: an unset string flag reads as "" rather
378
- // than undefined, and a flag passed once reads as a string rather than a one-element
379
- // array. Both matter -- `?? saved.field` means "flag absent", and --include is a list.
380
- var values = {};
381
- var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
382
- try {
383
- for(var _iterator1 = Object.entries(table)[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
384
- var _step_value1 = _sliced_to_array(_step1.value, 2), name1 = _step_value1[0], flag1 = _step_value1[1];
385
- var raw = parsed[name1];
386
- if (flag1.type === 'boolean') values[name1] = raw;
387
- else if (flag1.multiple) values[name1] = raw === '' ? undefined : Array.isArray(raw) ? raw : [
388
- raw
389
- ];
390
- else values[name1] = raw === '' ? undefined : raw;
391
- }
392
- } catch (err) {
393
- _didIteratorError1 = true;
394
- _iteratorError1 = err;
395
- } finally{
396
- try {
397
- if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
398
- _iterator1.return();
399
- }
400
- } finally{
401
- if (_didIteratorError1) {
402
- throw _iteratorError1;
403
- }
404
- }
306
+ process.exit(0);
405
307
  }
406
308
  return {
407
309
  values: values,
408
- positionals: parsed._
310
+ positionals: positionals
409
311
  };
410
312
  }
411
- function parseK(k, usageErrorFn) {
313
+ function parseK(k, usageError) {
412
314
  if (k === undefined) return undefined;
413
315
  var parsed = Number(k);
414
- if (!Number.isInteger(parsed) || parsed <= 0) usageErrorFn('--k expects a positive integer, got "'.concat(k, '"'));
316
+ if (!Number.isInteger(parsed) || parsed <= 0) usageError('--k expects a positive integer, got "'.concat(k, '"'));
415
317
  return parsed;
416
318
  }
417
319
  function printWarnings(warnings) {
@@ -479,7 +381,10 @@ function withDb(ctx, configPath, fn) {
479
381
  function runSql(cfg, sql, params, format, label) {
480
382
  var _sql_match;
481
383
  var placeholderCount = ((_sql_match = sql.match(/\?/g)) !== null && _sql_match !== void 0 ? _sql_match : []).length;
482
- if (params.length !== placeholderCount) (0, _exitts.usageError)("".concat(label, " expects ").concat(placeholderCount, " parameter(s), got ").concat(params.length));
384
+ if (params.length !== placeholderCount) {
385
+ console.error("".concat(label, " expects ").concat(placeholderCount, " parameter(s), got ").concat(params.length));
386
+ process.exit(2);
387
+ }
483
388
  var _open = (0, _dbts.open)(cfg), db = _open.db, warnings = _open.warnings;
484
389
  printWarnings(warnings);
485
390
  var rows;