sensemaking 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/README.md +107 -132
  2. package/dist/cjs/cli.js +172 -182
  3. package/dist/cjs/cli.js.map +1 -1
  4. package/dist/cjs/commands/find.d.cts +3 -0
  5. package/dist/cjs/commands/find.d.ts +3 -0
  6. package/dist/cjs/commands/find.js +73 -0
  7. package/dist/cjs/commands/find.js.map +1 -0
  8. package/dist/cjs/commands/index.d.cts +5 -0
  9. package/dist/cjs/commands/index.d.ts +5 -0
  10. package/dist/cjs/commands/index.js +94 -0
  11. package/dist/cjs/commands/index.js.map +1 -0
  12. package/dist/cjs/commands/init.d.cts +3 -0
  13. package/dist/cjs/commands/init.d.ts +3 -0
  14. package/dist/cjs/commands/init.js +18 -0
  15. package/dist/cjs/commands/init.js.map +1 -0
  16. package/dist/cjs/commands/map.d.cts +3 -0
  17. package/dist/cjs/commands/map.d.ts +3 -0
  18. package/dist/cjs/commands/map.js +21 -0
  19. package/dist/cjs/commands/map.js.map +1 -0
  20. package/dist/cjs/commands/named.d.cts +2 -0
  21. package/dist/cjs/commands/named.d.ts +2 -0
  22. package/dist/cjs/commands/named.js +23 -0
  23. package/dist/cjs/commands/named.js.map +1 -0
  24. package/dist/cjs/commands/peek.d.cts +3 -0
  25. package/dist/cjs/commands/peek.d.ts +3 -0
  26. package/dist/cjs/commands/peek.js +69 -0
  27. package/dist/cjs/commands/peek.js.map +1 -0
  28. package/dist/cjs/commands/query.d.cts +3 -0
  29. package/dist/cjs/commands/query.d.ts +3 -0
  30. package/dist/cjs/commands/query.js +43 -0
  31. package/dist/cjs/commands/query.js.map +1 -0
  32. package/dist/cjs/commands/rebuild.d.cts +3 -0
  33. package/dist/cjs/commands/rebuild.d.ts +3 -0
  34. package/dist/cjs/commands/rebuild.js +20 -0
  35. package/dist/cjs/commands/rebuild.js.map +1 -0
  36. package/dist/cjs/commands/status.d.cts +3 -0
  37. package/dist/cjs/commands/status.d.ts +3 -0
  38. package/dist/cjs/commands/status.js +24 -0
  39. package/dist/cjs/commands/status.js.map +1 -0
  40. package/dist/cjs/commands/types.d.cts +15 -0
  41. package/dist/cjs/commands/types.d.ts +15 -0
  42. package/dist/cjs/commands/types.js +5 -0
  43. package/dist/cjs/commands/types.js.map +1 -0
  44. package/dist/cjs/commands/vault.d.cts +6 -0
  45. package/dist/cjs/commands/vault.d.ts +6 -0
  46. package/dist/cjs/commands/vault.js +94 -0
  47. package/dist/cjs/commands/vault.js.map +1 -0
  48. package/dist/cjs/commands/watch.d.cts +3 -0
  49. package/dist/cjs/commands/watch.d.ts +3 -0
  50. package/dist/cjs/commands/watch.js +173 -0
  51. package/dist/cjs/commands/watch.js.map +1 -0
  52. package/dist/cjs/config.d.cts +11 -1
  53. package/dist/cjs/config.d.ts +11 -1
  54. package/dist/cjs/config.js +82 -9
  55. package/dist/cjs/config.js.map +1 -1
  56. package/dist/cjs/db.d.cts +1 -1
  57. package/dist/cjs/db.d.ts +1 -1
  58. package/dist/cjs/db.js +107 -16
  59. package/dist/cjs/db.js.map +1 -1
  60. package/dist/cjs/errors.d.cts +1 -1
  61. package/dist/cjs/errors.d.ts +1 -1
  62. package/dist/cjs/errors.js.map +1 -1
  63. package/dist/cjs/features/index.d.cts +7 -0
  64. package/dist/cjs/features/index.d.ts +7 -0
  65. package/dist/cjs/features/index.js +36 -0
  66. package/dist/cjs/features/index.js.map +1 -0
  67. package/dist/cjs/features/links.d.cts +4 -0
  68. package/dist/cjs/features/links.d.ts +4 -0
  69. package/dist/cjs/features/links.js +217 -0
  70. package/dist/cjs/features/links.js.map +1 -0
  71. package/dist/cjs/features/rank.d.cts +2 -0
  72. package/dist/cjs/features/rank.d.ts +2 -0
  73. package/dist/cjs/features/rank.js +95 -0
  74. package/dist/cjs/features/rank.js.map +1 -0
  75. package/dist/cjs/features/sections.d.cts +9 -0
  76. package/dist/cjs/features/sections.d.ts +9 -0
  77. package/dist/cjs/features/sections.js +54 -0
  78. package/dist/cjs/features/sections.js.map +1 -0
  79. package/dist/cjs/features/types.d.cts +11 -0
  80. package/dist/cjs/features/types.d.ts +11 -0
  81. package/dist/cjs/features/types.js +5 -0
  82. package/dist/cjs/features/types.js.map +1 -0
  83. package/dist/cjs/graph.d.cts +7 -0
  84. package/dist/cjs/graph.d.ts +7 -0
  85. package/dist/cjs/graph.js +237 -0
  86. package/dist/cjs/graph.js.map +1 -0
  87. package/dist/cjs/index.d.cts +9 -2
  88. package/dist/cjs/index.d.ts +9 -2
  89. package/dist/cjs/index.js +39 -0
  90. package/dist/cjs/index.js.map +1 -1
  91. package/dist/cjs/output.d.cts +21 -0
  92. package/dist/cjs/output.d.ts +21 -0
  93. package/dist/cjs/output.js +112 -3
  94. package/dist/cjs/output.js.map +1 -1
  95. package/dist/cjs/scan.d.cts +3 -1
  96. package/dist/cjs/scan.d.ts +3 -1
  97. package/dist/cjs/scan.js +62 -14
  98. package/dist/cjs/scan.js.map +1 -1
  99. package/dist/cjs/verbs.d.cts +31 -0
  100. package/dist/cjs/verbs.d.ts +31 -0
  101. package/dist/cjs/verbs.js +284 -0
  102. package/dist/cjs/verbs.js.map +1 -0
  103. package/dist/esm/cli.js +63 -107
  104. package/dist/esm/cli.js.map +1 -1
  105. package/dist/esm/commands/find.d.ts +3 -0
  106. package/dist/esm/commands/find.js +14 -0
  107. package/dist/esm/commands/find.js.map +1 -0
  108. package/dist/esm/commands/index.d.ts +5 -0
  109. package/dist/esm/commands/index.js +13 -0
  110. package/dist/esm/commands/index.js.map +1 -0
  111. package/dist/esm/commands/init.d.ts +3 -0
  112. package/dist/esm/commands/init.js +7 -0
  113. package/dist/esm/commands/init.js.map +1 -0
  114. package/dist/esm/commands/map.d.ts +3 -0
  115. package/dist/esm/commands/map.js +10 -0
  116. package/dist/esm/commands/map.js.map +1 -0
  117. package/dist/esm/commands/named.d.ts +2 -0
  118. package/dist/esm/commands/named.js +12 -0
  119. package/dist/esm/commands/named.js.map +1 -0
  120. package/dist/esm/commands/peek.d.ts +3 -0
  121. package/dist/esm/commands/peek.js +12 -0
  122. package/dist/esm/commands/peek.js.map +1 -0
  123. package/dist/esm/commands/query.d.ts +3 -0
  124. package/dist/esm/commands/query.js +7 -0
  125. package/dist/esm/commands/query.js.map +1 -0
  126. package/dist/esm/commands/rebuild.d.ts +3 -0
  127. package/dist/esm/commands/rebuild.js +9 -0
  128. package/dist/esm/commands/rebuild.js.map +1 -0
  129. package/dist/esm/commands/status.d.ts +3 -0
  130. package/dist/esm/commands/status.js +13 -0
  131. package/dist/esm/commands/status.js.map +1 -0
  132. package/dist/esm/commands/types.d.ts +15 -0
  133. package/dist/esm/commands/types.js +1 -0
  134. package/dist/esm/commands/types.js.map +1 -0
  135. package/dist/esm/commands/vault.d.ts +6 -0
  136. package/dist/esm/commands/vault.js +29 -0
  137. package/dist/esm/commands/vault.js.map +1 -0
  138. package/dist/esm/commands/watch.d.ts +3 -0
  139. package/dist/esm/commands/watch.js +19 -0
  140. package/dist/esm/commands/watch.js.map +1 -0
  141. package/dist/esm/config.d.ts +11 -1
  142. package/dist/esm/config.js +68 -12
  143. package/dist/esm/config.js.map +1 -1
  144. package/dist/esm/db.d.ts +1 -1
  145. package/dist/esm/db.js +27 -9
  146. package/dist/esm/db.js.map +1 -1
  147. package/dist/esm/errors.d.ts +1 -1
  148. package/dist/esm/errors.js.map +1 -1
  149. package/dist/esm/features/index.d.ts +7 -0
  150. package/dist/esm/features/index.js +14 -0
  151. package/dist/esm/features/index.js.map +1 -0
  152. package/dist/esm/features/links.d.ts +4 -0
  153. package/dist/esm/features/links.js +81 -0
  154. package/dist/esm/features/links.js.map +1 -0
  155. package/dist/esm/features/rank.d.ts +2 -0
  156. package/dist/esm/features/rank.js +17 -0
  157. package/dist/esm/features/rank.js.map +1 -0
  158. package/dist/esm/features/sections.d.ts +9 -0
  159. package/dist/esm/features/sections.js +41 -0
  160. package/dist/esm/features/sections.js.map +1 -0
  161. package/dist/esm/features/types.d.ts +11 -0
  162. package/dist/esm/features/types.js +3 -0
  163. package/dist/esm/features/types.js.map +1 -0
  164. package/dist/esm/graph.d.ts +7 -0
  165. package/dist/esm/graph.js +79 -0
  166. package/dist/esm/graph.js.map +1 -0
  167. package/dist/esm/index.d.ts +9 -2
  168. package/dist/esm/index.js +4 -1
  169. package/dist/esm/index.js.map +1 -1
  170. package/dist/esm/output.d.ts +21 -0
  171. package/dist/esm/output.js +28 -0
  172. package/dist/esm/output.js.map +1 -1
  173. package/dist/esm/scan.d.ts +3 -1
  174. package/dist/esm/scan.js +56 -7
  175. package/dist/esm/scan.js.map +1 -1
  176. package/dist/esm/verbs.d.ts +31 -0
  177. package/dist/esm/verbs.js +139 -0
  178. package/dist/esm/verbs.js.map +1 -0
  179. package/package.json +4 -4
  180. package/schema.json +23 -4
  181. package/skills/sense/EXAMPLES.md +46 -81
  182. package/skills/sense/SKILL.md +58 -92
package/README.md CHANGED
@@ -1,175 +1,150 @@
1
1
  # sensemaking
2
2
 
3
- Query a knowledge base you build with an agent: filter notes by frontmatter, then search inside them.
3
+ SQL over a tree of markdown notes: filter by frontmatter, search the prose, follow the links,
4
+ read the structure. A CLI that starts, answers, and exits — no server, no build step.
4
5
 
5
- ## The problem
6
+ ## Problem
6
7
 
7
- When you work with an AI agent on anything substantial, you end up with a pile of small notes
8
- findings, decisions, sources, summaries. Small, categorized notes are how knowledge accumulates
9
- instead of being re-derived every session: structured frontmatter over free-form prose.
8
+ Working with AI agents produces piles of small notes. Past a few dozen, finding the right ones
9
+ means grepping or reading whole folders into context. The structure that makes notes navigable
10
+ frontmatter, wikilinks, headings is exactly what an agent needs, but nothing exposes it as a
11
+ query surface.
10
12
 
11
- But accumulation only pays off if it's findable. Past a couple dozen notes, an agent can't tell
12
- which fifteen of two hundred bear on its task, so it greps, reads whole folders into context, or
13
- rebuilds knowledge that already exists three files away.
13
+ `sense` indexes all of it into SQLite and reconciles against file timestamps on every query, so
14
+ results are never stale and nothing has to be running.
14
15
 
15
- The classification an agent adds while writing — `status`, `type`, `tags`, `source` — is what keeps
16
- the pile navigable. `sensemaking` is the layer that acts on it, without an app running, a build
17
- step, or you re-explaining anything. The intended shape is a small vault per project, not one big
18
- one — starting a new one is a single command with no schema to design.
16
+ ## Quick start
19
17
 
20
- ## The model
18
+ ```bash
19
+ npm install -g sensemaking
20
+ cd your-notes && sense init
21
+ ```
22
+
23
+ ```bash
24
+ sense map # orient: fields, hub notes, recent changes
25
+ sense find "revenue OR earnings" --k 10 # locate: ranked references with excerpts
26
+ sense peek notes/q3-report.md # structure: outline + links, before reading
27
+ sense query "SELECT path FROM frontmatter WHERE has(tags, ?)" urgent
28
+ ```
21
29
 
22
- Every file becomes a row; every frontmatter key becomes a column; the prose becomes a full-text
23
- index you can join against. You write named SQL queries once and run them by name.
30
+ ## Model
24
31
 
25
- ```markdown
26
- ---
27
- title: Ship the Q3 report
28
- summary: where the Q3 numbers came from and who signed off
29
- status: active
30
- tags: [urgent, reports]
31
- ---
32
+ Every file becomes rows in four tables:
32
33
 
33
- Notes about the report…
34
- ```
34
+ | table | holds | for |
35
+ |---|---|---|
36
+ | `frontmatter` | one column per key, plus `path`, `_mtime`, `_size`, `_rank` | filtering |
37
+ | `content` | FTS5 index: `title`, `summary`, `text` | search and ranking |
38
+ | `links` | `src`, `target` as written, `dst` resolved (`NULL` = dead link) | graph |
39
+ | `sections` | heading, `level`, `start_line`, `end_line`, `tokens` estimate | structure |
40
+
41
+ Results are references — path, title, summary, excerpt — never file contents. Reading happens
42
+ afterward through the filesystem, scoped to the line ranges `peek` returns. This is the
43
+ [just-in-time context pattern](https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents):
44
+ the agent holds lightweight identifiers and loads payloads only when needed.
45
+
46
+ Measured costs: a `find` row is ~20–40 tokens, a `peek` is ~17% of reading the file, and the
47
+ section it points at is a direct `Read` range.
35
48
 
36
49
  ```sql
37
- -- active notes that actually discuss revenue, best match first
38
- SELECT f.path, content.title, content.summary, snippet(content, -1, '«', '»', '…', 10) AS hit
50
+ -- filter and search compose in one query
51
+ SELECT f.path, content.title, snippet(content, -1, '«', '»', '…', 10) AS hit
39
52
  FROM frontmatter f JOIN content ON content.path = f.path
40
53
  WHERE f.status = 'active' AND content MATCH 'revenue'
41
54
  ORDER BY bm25(content, 10.0, 5.0, 1.0) LIMIT 10
42
55
  ```
43
56
 
44
- The filter narrows the set, the search ranks it, and each row that comes back —
45
- path, title, summary, matching excerpt — is enough to decide whether to open the file, without
46
- carrying the file. On a real 26-note vault that's a few hundred tokens, against ~6,000 to read the
47
- three files it points at. Cheap enough to run before deciding what to open; reading afterward is
48
- the expensive step, and it happens through the filesystem, not SQL.
49
-
50
- ## Use it
57
+ ## Verbs
51
58
 
52
- ```bash
53
- npm install -g sensemaking
54
- cd your-notes && sense init # writes a minimal sense.config.json (globs only, no queries)
55
- ```
59
+ | verb | does |
60
+ |---|---|
61
+ | `map` | doc count, frontmatter field coverage, top hubs by link rank, recent changes |
62
+ | `find "<terms>" [--where "<sql>"] [--k n]` | BM25 + link-graph expansion, fused; `via` marks each row `match`, `link`, or `match+link` |
63
+ | `peek <path>` | frontmatter + heading outline (`[L143-162, ~380t]`) + links both ways, capped at 20 per list |
64
+ | `query "<sql>" [params...]` | ad-hoc SQL over all four tables; `?` binds positional args |
56
65
 
57
- Query immediately ad-hoc SQL needs no config beyond the globs:
66
+ `find` seeds a personalized-PageRank walk with the BM25 matches, so a note that never contains
67
+ the terms but is linked from ones that do still surfaces. Invalid FTS5 syntax falls back to
68
+ OR over the words. `--where` takes a frontmatter condition against alias `f`.
69
+ `--format json` on any verb returns structured output.
58
70
 
59
- ```
60
- sense query "SELECT … FROM frontmatter" # ad-hoc SQL; positional args bind to ? placeholders
61
- sense query "…" --format json # structured output (the default is a table)
62
- sense --list # what named queries exist
63
- sense status | rebuild # cache info / delete .sense/ and re-crawl
64
- ```
71
+ ## Config
65
72
 
66
- When a query proves worth reusing, name it in `sense.config.json` (plain JSON; the `$schema` line
67
- gives your editor autocomplete and validation) and run it as `sense <name> [params...]`:
73
+ `sense init` writes `sense.config.json`; discovery walks up from cwd like git
74
+ (`--config <path>` overrides). Name reusable queries and run them as `sense <name> [params...]`:
68
75
 
69
76
  ```json
70
77
  {
71
78
  "$schema": "https://unpkg.com/sensemaking/schema.json",
72
- "version": 1,
79
+ "version": 2,
73
80
  "scan": { "include": ["**/*.md"] },
81
+ "features": { "links": true, "sections": true, "rank": true },
74
82
  "queries": {
75
- "by-tag": "SELECT path, title, summary FROM frontmatter WHERE has(tags, ?) ORDER BY path"
83
+ "by-tag": "SELECT path, title FROM frontmatter WHERE has(tags, ?) ORDER BY path"
76
84
  }
77
85
  }
78
86
  ```
79
87
 
80
- Discovery walks up from your cwd git-style, so you can run `sense` from anywhere in the tree
81
- (`--config <path>` overrides). Exit codes: `0` ok, `1` real error (SQLite's message verbatim),
82
- `2` usage error.
83
-
84
- The one custom SQL function is `has(field, value)`: array membership on a JSON-array field (like
85
- `tags`), substring match on a string, always false on a missing key. Reserved names: `path`,
86
- `_mtime`, `_size`, `content`.
88
+ Features toggle independently; disabling one degrades its verb instead of failing it (`find`
89
+ goes BM25-only without `links`, `map` drops hubs without `rank`, `peek` drops the outline
90
+ without `sections`). Toggling rebuilds the cache. Older config versions auto-migrate on load,
91
+ noted on stderr.
87
92
 
88
- ### Content search
93
+ ## Reference
89
94
 
90
- `content` is an FTS5 table with columns `title`, `summary`, `text`, and `path` (for the join).
91
- `content MATCH ?` takes FTS5 syntax (`a OR b`, `"exact phrase"`, `pref*`, `NEAR(a b, 5)`,
92
- `summary: term`), with stemming on so `negotiate` matches "negotiating". Markdown syntax is
93
- stripped at index time — search for the words, not the formatting around them, and excerpts come
94
- back as clean prose. `bm25()` weights follow column order, so `bm25(content, 10.0, 5.0, 1.0)`
95
- ranks a title hit above a passing mention; `snippet(content, -1, )` excerpts whichever column
96
- matched.
95
+ - `has(field, value)` the one custom SQL function: array membership on JSON-array fields,
96
+ substring on strings, false on missing keys.
97
+ - Reserved frontmatter keys (dropped with a warning): `path`, `_mtime`, `_size`, `_rank`,
98
+ `content`, `links`, `sections`.
99
+ - FTS5 syntax in `MATCH`: `a OR b`, `"phrase"`, `pref*`, `NEAR(a b, 5)`, `summary: term`.
100
+ Stemmed; markdown is stripped at index time. `bm25(content, 10.0, 5.0, 1.0)` weights title
101
+ over summary over body.
102
+ - A one-line `summary:` in frontmatter is both a selectable column and a weighted search field.
103
+ - Exit codes: `0` ok, `1` error (SQLite message verbatim), `2` usage.
104
+ - Frontmatter parsing is lenient: syntax errors (an alias starting with `@`, say) are per-file
105
+ warnings and the values are still indexed.
97
106
 
98
- Prose is deliberately **not** a column on `frontmatter`, so `SELECT * FROM frontmatter` can never
99
- dump your notes into an agent's context — reaching it takes an explicit join. Select `path`,
100
- `title`, `summary`, and a `snippet()`, keep a `LIMIT`, and read the files worth reading. `sense`
101
- warns on stderr when a result grows past 50 KB.
107
+ ## Scale
102
108
 
103
- A one-line `summary:` in frontmatter is worth adding as you write what's on the page and when
104
- it's worth opening, like a skill's `description:`. It's both a column you can select (a search
105
- result row often answers the question with no file read at all) and a weighted search field.
109
+ Every query starts with a freshness check against the cache in `.sense/`; only changed files are
110
+ re-parsed. Measured per release in [BENCHMARKING.md](BENCHMARKING.md) at 6,566 notes: full
111
+ crawl ~5 s, warm query ~100 ms, one-file change ~200 ms. `sense watch` (optional) moves
112
+ re-parsing into the background — see [WATCH.md](WATCH.md). `sense rebuild` deletes the cache and
113
+ re-crawls.
106
114
 
107
- ### For AI agents
115
+ ## For AI agents
108
116
 
109
117
  ```bash
110
- npm install -g sensemaking # the CLI
111
- npx skills add kmalakoff/sensemaking # the agent skill (add -g for global, -a claude-code to target)
118
+ npx skills add kmalakoff/sensemaking # -g for global, -a claude-code to target
112
119
  ```
113
120
 
114
- The skill teaches an agent the essentials: discovery, `--list`, `--format json`, `has()`, when to
115
- use ad-hoc `query` versus saving a named one, and when to `rebuild`.
116
-
117
- ## How it works, how it scales
118
-
119
- `sense` is a command, not an app. Nothing has to be running: it starts, answers,
120
- and exits.
121
-
122
- Every query begins with a freshness check: each file's timestamp and size is
123
- compared against the SQLite cache in `.sense/`, changed files are re-parsed, and
124
- deleted files are dropped. When nothing has changed, that check is the entire
125
- cost. Results are never stale, and the cache is disposable `sense rebuild`
126
- deletes and rebuilds it.
127
-
128
- For a vault of a few hundred notes, the check takes a few milliseconds.
129
-
130
- On a large vault (10,000+ files), the check grows with file count — roughly a
131
- tenth of a second at 10k and the first query after editing many files pays to
132
- re-parse them. `sense watch` moves that parsing into the background: a job that
133
- re-parses files as they change, so queries find the work already done. It is
134
- optional queries always run their own check, so a stopped watcher never causes
135
- a wrong answer, only a slower next query. launchd and systemd examples:
136
- [WATCH.md](WATCH.md).
137
-
138
- ## Why not …
139
-
140
- - **Obsidian (Bases/Dataview):** filters this well, but only inside the running Electron app —
141
- scripts and agents can't query it headless. Your vault works unmodified either way; Obsidian
142
- stays a fine viewer for the same files.
143
- - **RAG / semantic search:** ranks by similarity, not hard constraints like "only active notes from
144
- this project" — that needs a `WHERE` clause. The two compose: filter first, rank later; vector
145
- similarity, if added later, would join the same way.
146
- - **An LLM-written index file** (à la Karpathy's llm-wiki `index.md`): a good instinct, but a
147
- second artifact that drifts out of sync. `frontmatter` is that catalog, derived from the notes
148
- themselves on every query.
149
- - **Index-on-build tools (MarkdownDB and similar):** query a snapshot that's stale the moment a
150
- file changes, instead of reconciling live.
151
- - **Note CLIs (zk and similar):** good at their own model — tags, links, full text — but can't
152
- filter on arbitrary frontmatter fields, which is the whole point here.
153
- - **grep / one-off scripts:** fine until you want named, reusable, parameterized queries with real
154
- AND/OR/ORDER BY — at which point you're writing your own query engine.
155
-
156
- `sensemaking` is deliberately thin glue: [gray-matter](https://github.com/jonschlinkert/gray-matter)
157
- parses, [remove-markdown](https://github.com/zuchka/remove-markdown) cleans the prose for indexing,
158
- [fast-glob](https://github.com/mrmlnc/fast-glob) walks, and Node's built-in SQLite (`node:sqlite`)
159
- does all the querying. Three small dependencies, no native builds, no background services required.
160
-
161
- ## Roadmap
162
-
163
- Vector similarity is the natural next facet — a `doc_vec` table joined the same way `content` is,
164
- so semantic recall composes with the frontmatter filter instead of living in a separate tool
165
- (`SELECT path, distance`, never the embedding). It's deferred, not planned: today it would require
166
- a native SQLite extension (sqlite-vec is pre-v1 and ships platform binaries), which breaks the
167
- no-native-builds line above. It becomes worth revisiting when Node can do it dependency-free — and
168
- only if BM25 demonstrably misses things; on a curated vault of a few hundred notes it often
169
- doesn't.
170
-
171
- Beyond that, the corpus model isn't tied to markdown — anything carrying structured metadata
172
- (document properties, sidecar JSON) can join it without changing the query surface.
121
+ The skill teaches the descent: `map` to orient, `find` to locate, `peek` before reading,
122
+ `Read` line ranges instead of files.
123
+
124
+ ## Prior art
125
+
126
+ - [Effective context engineering for AI agents](https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents)
127
+ (Anthropic): agents should hold lightweight identifiers — file paths, links — and load payloads
128
+ just in time, because context is a finite resource. The four verbs implement that pattern as a CLI.
129
+ - [llm-wiki](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) (Karpathy): an
130
+ agent-maintained wiki navigated by an `index.md` and links, which he notes needs real search
131
+ infrastructure past a few hundred pages. `sense map` derives that index from the notes instead of
132
+ maintaining it; `find` is the hybrid local search it calls for.
133
+
134
+ ## Alternatives
135
+
136
+ - **Obsidian Bases/Dataview** — same filters, but only inside the running app; agents can't
137
+ query it headless.
138
+ - **Index-on-build tools (MarkdownDB)**query a snapshot; `sense` reconciles on every query.
139
+ - **Note CLIs (zk)** fixed schema; `sense` filters on arbitrary frontmatter.
140
+ - **RAG / vector stores** similarity can't express `WHERE status = 'active'`. Vector recall is
141
+ planned as an optional `embed` feature (pure-JS provider package, fused into `find`) not
142
+ blocked (`node:sqlite` can load extensions since Node 22.13), just deferred until BM25 + links
143
+ measurably miss.
144
+
145
+ Dependencies: [yaml](https://github.com/eemeli/yaml),
146
+ [remove-markdown](https://github.com/zuchka/remove-markdown),
147
+ [fast-glob](https://github.com/mrmlnc/fast-glob), and Node's built-in SQLite. No native builds.
173
148
 
174
149
  ## License
175
150