sensemaking 0.2.0 → 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.
- package/README.md +107 -126
- package/dist/cjs/cli.js +172 -182
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/commands/find.d.cts +3 -0
- package/dist/cjs/commands/find.d.ts +3 -0
- package/dist/cjs/commands/find.js +73 -0
- package/dist/cjs/commands/find.js.map +1 -0
- package/dist/cjs/commands/index.d.cts +5 -0
- package/dist/cjs/commands/index.d.ts +5 -0
- package/dist/cjs/commands/index.js +94 -0
- package/dist/cjs/commands/index.js.map +1 -0
- package/dist/cjs/commands/init.d.cts +3 -0
- package/dist/cjs/commands/init.d.ts +3 -0
- package/dist/cjs/commands/init.js +18 -0
- package/dist/cjs/commands/init.js.map +1 -0
- package/dist/cjs/commands/map.d.cts +3 -0
- package/dist/cjs/commands/map.d.ts +3 -0
- package/dist/cjs/commands/map.js +21 -0
- package/dist/cjs/commands/map.js.map +1 -0
- package/dist/cjs/commands/named.d.cts +2 -0
- package/dist/cjs/commands/named.d.ts +2 -0
- package/dist/cjs/commands/named.js +23 -0
- package/dist/cjs/commands/named.js.map +1 -0
- package/dist/cjs/commands/peek.d.cts +3 -0
- package/dist/cjs/commands/peek.d.ts +3 -0
- package/dist/cjs/commands/peek.js +69 -0
- package/dist/cjs/commands/peek.js.map +1 -0
- package/dist/cjs/commands/query.d.cts +3 -0
- package/dist/cjs/commands/query.d.ts +3 -0
- package/dist/cjs/commands/query.js +43 -0
- package/dist/cjs/commands/query.js.map +1 -0
- package/dist/cjs/commands/rebuild.d.cts +3 -0
- package/dist/cjs/commands/rebuild.d.ts +3 -0
- package/dist/cjs/commands/rebuild.js +20 -0
- package/dist/cjs/commands/rebuild.js.map +1 -0
- package/dist/cjs/commands/status.d.cts +3 -0
- package/dist/cjs/commands/status.d.ts +3 -0
- package/dist/cjs/commands/status.js +24 -0
- package/dist/cjs/commands/status.js.map +1 -0
- package/dist/cjs/commands/types.d.cts +15 -0
- package/dist/cjs/commands/types.d.ts +15 -0
- package/dist/cjs/commands/types.js +5 -0
- package/dist/cjs/commands/types.js.map +1 -0
- package/dist/cjs/commands/vault.d.cts +6 -0
- package/dist/cjs/commands/vault.d.ts +6 -0
- package/dist/cjs/commands/vault.js +94 -0
- package/dist/cjs/commands/vault.js.map +1 -0
- package/dist/cjs/commands/watch.d.cts +3 -0
- package/dist/cjs/commands/watch.d.ts +3 -0
- package/dist/cjs/commands/watch.js +173 -0
- package/dist/cjs/commands/watch.js.map +1 -0
- package/dist/cjs/config.d.cts +11 -1
- package/dist/cjs/config.d.ts +11 -1
- package/dist/cjs/config.js +82 -9
- package/dist/cjs/config.js.map +1 -1
- package/dist/cjs/db.d.cts +1 -1
- package/dist/cjs/db.d.ts +1 -1
- package/dist/cjs/db.js +107 -16
- package/dist/cjs/db.js.map +1 -1
- package/dist/cjs/errors.d.cts +1 -1
- package/dist/cjs/errors.d.ts +1 -1
- package/dist/cjs/errors.js.map +1 -1
- package/dist/cjs/features/index.d.cts +7 -0
- package/dist/cjs/features/index.d.ts +7 -0
- package/dist/cjs/features/index.js +36 -0
- package/dist/cjs/features/index.js.map +1 -0
- package/dist/cjs/features/links.d.cts +4 -0
- package/dist/cjs/features/links.d.ts +4 -0
- package/dist/cjs/features/links.js +217 -0
- package/dist/cjs/features/links.js.map +1 -0
- package/dist/cjs/features/rank.d.cts +2 -0
- package/dist/cjs/features/rank.d.ts +2 -0
- package/dist/cjs/features/rank.js +95 -0
- package/dist/cjs/features/rank.js.map +1 -0
- package/dist/cjs/features/sections.d.cts +9 -0
- package/dist/cjs/features/sections.d.ts +9 -0
- package/dist/cjs/features/sections.js +54 -0
- package/dist/cjs/features/sections.js.map +1 -0
- package/dist/cjs/features/types.d.cts +11 -0
- package/dist/cjs/features/types.d.ts +11 -0
- package/dist/cjs/features/types.js +5 -0
- package/dist/cjs/features/types.js.map +1 -0
- package/dist/cjs/graph.d.cts +7 -0
- package/dist/cjs/graph.d.ts +7 -0
- package/dist/cjs/graph.js +237 -0
- package/dist/cjs/graph.js.map +1 -0
- package/dist/cjs/index.d.cts +9 -2
- package/dist/cjs/index.d.ts +9 -2
- package/dist/cjs/index.js +39 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/output.d.cts +21 -0
- package/dist/cjs/output.d.ts +21 -0
- package/dist/cjs/output.js +112 -3
- package/dist/cjs/output.js.map +1 -1
- package/dist/cjs/scan.d.cts +3 -1
- package/dist/cjs/scan.d.ts +3 -1
- package/dist/cjs/scan.js +62 -14
- package/dist/cjs/scan.js.map +1 -1
- package/dist/cjs/verbs.d.cts +31 -0
- package/dist/cjs/verbs.d.ts +31 -0
- package/dist/cjs/verbs.js +284 -0
- package/dist/cjs/verbs.js.map +1 -0
- package/dist/esm/cli.js +63 -107
- package/dist/esm/cli.js.map +1 -1
- package/dist/esm/commands/find.d.ts +3 -0
- package/dist/esm/commands/find.js +14 -0
- package/dist/esm/commands/find.js.map +1 -0
- package/dist/esm/commands/index.d.ts +5 -0
- package/dist/esm/commands/index.js +13 -0
- package/dist/esm/commands/index.js.map +1 -0
- package/dist/esm/commands/init.d.ts +3 -0
- package/dist/esm/commands/init.js +7 -0
- package/dist/esm/commands/init.js.map +1 -0
- package/dist/esm/commands/map.d.ts +3 -0
- package/dist/esm/commands/map.js +10 -0
- package/dist/esm/commands/map.js.map +1 -0
- package/dist/esm/commands/named.d.ts +2 -0
- package/dist/esm/commands/named.js +12 -0
- package/dist/esm/commands/named.js.map +1 -0
- package/dist/esm/commands/peek.d.ts +3 -0
- package/dist/esm/commands/peek.js +12 -0
- package/dist/esm/commands/peek.js.map +1 -0
- package/dist/esm/commands/query.d.ts +3 -0
- package/dist/esm/commands/query.js +7 -0
- package/dist/esm/commands/query.js.map +1 -0
- package/dist/esm/commands/rebuild.d.ts +3 -0
- package/dist/esm/commands/rebuild.js +9 -0
- package/dist/esm/commands/rebuild.js.map +1 -0
- package/dist/esm/commands/status.d.ts +3 -0
- package/dist/esm/commands/status.js +13 -0
- package/dist/esm/commands/status.js.map +1 -0
- package/dist/esm/commands/types.d.ts +15 -0
- package/dist/esm/commands/types.js +1 -0
- package/dist/esm/commands/types.js.map +1 -0
- package/dist/esm/commands/vault.d.ts +6 -0
- package/dist/esm/commands/vault.js +29 -0
- package/dist/esm/commands/vault.js.map +1 -0
- package/dist/esm/commands/watch.d.ts +3 -0
- package/dist/esm/commands/watch.js +19 -0
- package/dist/esm/commands/watch.js.map +1 -0
- package/dist/esm/config.d.ts +11 -1
- package/dist/esm/config.js +68 -12
- package/dist/esm/config.js.map +1 -1
- package/dist/esm/db.d.ts +1 -1
- package/dist/esm/db.js +27 -9
- package/dist/esm/db.js.map +1 -1
- package/dist/esm/errors.d.ts +1 -1
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/features/index.d.ts +7 -0
- package/dist/esm/features/index.js +14 -0
- package/dist/esm/features/index.js.map +1 -0
- package/dist/esm/features/links.d.ts +4 -0
- package/dist/esm/features/links.js +81 -0
- package/dist/esm/features/links.js.map +1 -0
- package/dist/esm/features/rank.d.ts +2 -0
- package/dist/esm/features/rank.js +17 -0
- package/dist/esm/features/rank.js.map +1 -0
- package/dist/esm/features/sections.d.ts +9 -0
- package/dist/esm/features/sections.js +41 -0
- package/dist/esm/features/sections.js.map +1 -0
- package/dist/esm/features/types.d.ts +11 -0
- package/dist/esm/features/types.js +3 -0
- package/dist/esm/features/types.js.map +1 -0
- package/dist/esm/graph.d.ts +7 -0
- package/dist/esm/graph.js +79 -0
- package/dist/esm/graph.js.map +1 -0
- package/dist/esm/index.d.ts +9 -2
- package/dist/esm/index.js +4 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/output.d.ts +21 -0
- package/dist/esm/output.js +28 -0
- package/dist/esm/output.js.map +1 -1
- package/dist/esm/scan.d.ts +3 -1
- package/dist/esm/scan.js +56 -7
- package/dist/esm/scan.js.map +1 -1
- package/dist/esm/verbs.d.ts +31 -0
- package/dist/esm/verbs.js +139 -0
- package/dist/esm/verbs.js.map +1 -0
- package/package.json +4 -4
- package/schema.json +23 -4
- package/skills/sense/EXAMPLES.md +46 -81
- package/skills/sense/SKILL.md +58 -92
package/schema.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
3
|
"title": "sense.config.json",
|
|
4
|
-
"description": "Config for sense -- SQL over the frontmatter of a markdown tree.",
|
|
4
|
+
"description": "Config for sense -- SQL over the frontmatter, content, links, and structure of a markdown tree.",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"additionalProperties": false,
|
|
7
7
|
"required": ["scan", "queries"],
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
},
|
|
13
13
|
"version": {
|
|
14
14
|
"type": "integer",
|
|
15
|
-
"enum": [1],
|
|
16
|
-
"description": "Config format version.
|
|
15
|
+
"enum": [1, 2],
|
|
16
|
+
"description": "Config format version. Older versions are auto-migrated (and the file rewritten) on load; a version newer than this sense build supports makes it exit with an error rather than misinterpret the file. Omit to default to 1."
|
|
17
17
|
},
|
|
18
18
|
"scan": {
|
|
19
19
|
"type": "object",
|
|
@@ -29,9 +29,28 @@
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
},
|
|
32
|
+
"features": {
|
|
33
|
+
"type": "object",
|
|
34
|
+
"additionalProperties": false,
|
|
35
|
+
"description": "Independently toggleable features. Absent block or key means enabled; set false to disable. Toggling a feature rebuilds the cache. Verbs degrade rather than fail: `find` without links is BM25-only, `map` without rank omits hubs, `peek` without sections omits the outline.",
|
|
36
|
+
"properties": {
|
|
37
|
+
"links": {
|
|
38
|
+
"type": "boolean",
|
|
39
|
+
"description": "Extract wikilinks and relative markdown links into the `links` table (src, target as written, dst resolved path or NULL for dead links). Powers backlinks, graph expansion in `find`, and `peek`'s link lists."
|
|
40
|
+
},
|
|
41
|
+
"sections": {
|
|
42
|
+
"type": "boolean",
|
|
43
|
+
"description": "Extract headings into the `sections` table (path, idx, level, heading, start_line, end_line, tokens estimate). Powers `peek`'s outline so agents can read line ranges instead of whole files."
|
|
44
|
+
},
|
|
45
|
+
"rank": {
|
|
46
|
+
"type": "boolean",
|
|
47
|
+
"description": "PageRank over resolved links into `frontmatter._rank` at reconcile -- a static importance prior. Powers `map`'s hub list; usable in any ORDER BY. Requires `links`."
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
},
|
|
32
51
|
"queries": {
|
|
33
52
|
"type": "object",
|
|
34
|
-
"description": "Named SQL queries runnable as `sense <name> [params...]`. Tables: `frontmatter` (one row per file, one column per discovered frontmatter key, plus `path`/`_mtime`/`_size`)
|
|
53
|
+
"description": "Named SQL queries runnable as `sense <name> [params...]`. Tables: `frontmatter` (one row per file, one column per discovered frontmatter key, plus `path`/`_mtime`/`_size`/`_rank`), `content` (FTS5: `title`, `summary`, `text`, `path`), `links` (`src`, `target`, `dst`), and `sections` (`path`, `idx`, `level`, `heading`, `start_line`, `end_line`, `tokens`). `?` placeholders bind to CLI positional args in order. `has(field, value)`: array membership on a JSON-array field, substring match on a string, false on NULL. Canonical query: `SELECT f.path, content.title, content.summary, snippet(content, -1, '«', '»', '…', 10) AS hit FROM frontmatter f JOIN content ON content.path = f.path WHERE content MATCH ? ORDER BY bm25(content, 10.0, 5.0, 1.0) LIMIT 10`. Reserved frontmatter keys: `path`, `_mtime`, `_size`, `_rank`, `content`, `links`, `sections`. Reserved query names (unreachable as subcommands): `init`, `query`, `find`, `map`, `peek`, `watch`, `status`, `rebuild`.",
|
|
35
54
|
"additionalProperties": { "type": "string" }
|
|
36
55
|
}
|
|
37
56
|
}
|
package/skills/sense/EXAMPLES.md
CHANGED
|
@@ -1,115 +1,80 @@
|
|
|
1
1
|
# sense: worked examples
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Outputs below are illustrative. Every result is a reference; reading happens afterward, through
|
|
4
|
+
the filesystem, on the paths that earned it.
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
reading the files it points at.
|
|
8
|
-
|
|
9
|
-
## A. "Does the vault say anything about X?" (discovery search)
|
|
10
|
-
|
|
11
|
-
The question an agent should ask *before* reading anything — cheap enough to run speculatively.
|
|
6
|
+
## A. "Does the vault say anything about X?"
|
|
12
7
|
|
|
13
8
|
```
|
|
14
|
-
sense
|
|
15
|
-
FROM frontmatter f JOIN content ON content.path = f.path
|
|
16
|
-
WHERE content MATCH ?
|
|
17
|
-
ORDER BY bm25(content, 10.0, 5.0, 1.0) LIMIT 10" "compensation OR salary" --format json
|
|
9
|
+
sense find "pricing OR billing OR invoicing" --k 10 --format json
|
|
18
10
|
```
|
|
19
11
|
|
|
20
12
|
```json
|
|
21
13
|
[
|
|
22
|
-
{
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
"summary": "
|
|
26
|
-
"hit": "«Compensation» floor Kevin's «compensation» preference for the later job…"
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
"path": "methodology/jobs-table-schema.md",
|
|
30
|
-
"title": "Jobs table schema",
|
|
31
|
-
"summary": "",
|
|
32
|
-
"hit": "…B Company employer C «Salary» as posted; blank if not…"
|
|
33
|
-
}
|
|
14
|
+
{ "path": "notes/pricing-model.md", "title": "Pricing model",
|
|
15
|
+
"summary": "Tiered per-seat pricing; floor and discount rules", "hit": "«Pricing» floor for annual…", "via": "match+link", "score": 0.0333 },
|
|
16
|
+
{ "path": "notes/renewal-playbook.md", "title": "Renewal playbook",
|
|
17
|
+
"summary": "Renewal sequence and owners", "hit": "…«billing» contact confirms the PO…", "via": "match", "score": 0.0313 }
|
|
34
18
|
]
|
|
35
19
|
```
|
|
36
20
|
|
|
37
|
-
~
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
Note the `content.title`/`content.summary` spelling: those columns always exist on `content`
|
|
42
|
-
(empty string when a note lacks the key), so this query works on any vault. `f.title`/`f.summary`
|
|
43
|
-
only work once some note actually declares the key — `frontmatter` columns are discovered, not fixed.
|
|
21
|
+
~30 tokens per row; often the `summary` answers the question with no read at all. A row with
|
|
22
|
+
`via: "link"` never contained the terms — it is linked from notes that did, usually a signal
|
|
23
|
+
worth following, not noise.
|
|
44
24
|
|
|
45
|
-
## B.
|
|
46
|
-
|
|
47
|
-
When the fields are known — typically because an agent wrote the notes to a schema — plain SQL on
|
|
48
|
-
`frontmatter` is the whole query. No join needed when you aren't searching prose:
|
|
25
|
+
## B. Known-field filtering (no search)
|
|
49
26
|
|
|
50
27
|
```
|
|
51
|
-
sense query "SELECT path, title,
|
|
52
|
-
WHERE status = 'active' AND has(track, ?) ORDER BY updated DESC" within-tech --format json
|
|
28
|
+
sense query "SELECT path, title, status FROM frontmatter WHERE status = 'active' AND has(tags, ?)" pricing --format json
|
|
53
29
|
```
|
|
54
30
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
*no* note in the vault declares a key does its column not exist at all — pattern D's pragma shows
|
|
58
|
-
what's there.)
|
|
59
|
-
|
|
60
|
-
## C. Filter + search + rank in one query
|
|
31
|
+
Plain SQL on discovered columns. Combine with search by joining `content` and adding
|
|
32
|
+
`AND content MATCH ?` — filter and rank in one query.
|
|
61
33
|
|
|
62
|
-
|
|
63
|
-
ranking, composed.
|
|
34
|
+
## C. Structure before reading
|
|
64
35
|
|
|
65
36
|
```
|
|
66
|
-
sense
|
|
67
|
-
FROM frontmatter f JOIN content ON content.path = f.path
|
|
68
|
-
WHERE f.status = 'active' AND has(f.track, 'within-tech') AND content MATCH ?
|
|
69
|
-
ORDER BY bm25(content, 10.0, 5.0, 1.0) LIMIT 10" remote --format json
|
|
37
|
+
sense peek notes/architecture-decisions.md
|
|
70
38
|
```
|
|
71
39
|
|
|
72
|
-
The frontmatter conditions are hard filters (a non-active note can never appear); the `MATCH` +
|
|
73
|
-
`bm25()` ranks whatever survives. The join exists **only** for `MATCH`/`bm25()`/`snippet()` —
|
|
74
|
-
never use it to fetch text.
|
|
75
|
-
|
|
76
|
-
## D. Cold start: a vault you didn't build
|
|
77
|
-
|
|
78
40
|
```
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
41
|
+
notes/architecture-decisions.md (~4400 tokens)
|
|
42
|
+
title: Architecture decisions
|
|
43
|
+
sections:
|
|
44
|
+
## Storage layer — why SQLite [L41-88, ~610t]
|
|
45
|
+
## Queue — rejected options [L89-120, ~380t]
|
|
46
|
+
...
|
|
47
|
+
links out (7): notes/pricing-model.md, ...
|
|
48
|
+
backlinks (2): notes/_index.md, notes/roadmap.md
|
|
83
49
|
```
|
|
84
50
|
|
|
85
|
-
|
|
86
|
-
|
|
51
|
+
The note is ~4,400 tokens; the peek is ~500. If only one section matters, `Read` its line range
|
|
52
|
+
(~400 tokens) — a tenth of the file.
|
|
87
53
|
|
|
88
|
-
##
|
|
54
|
+
## D. The graph
|
|
89
55
|
|
|
90
56
|
```
|
|
91
|
-
"
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
title: retro OR text: retrospective
|
|
95
|
-
NEAR(salary negotiate, 8)
|
|
57
|
+
sense query "SELECT src FROM links WHERE dst = ?" notes/pricing-model.md # who cites this
|
|
58
|
+
sense query "SELECT src, target FROM links WHERE dst IS NULL" # dead links
|
|
59
|
+
sense query "SELECT path, round(_rank*100,2) r FROM frontmatter ORDER BY _rank DESC LIMIT 5" # load-bearing notes
|
|
96
60
|
```
|
|
97
61
|
|
|
98
|
-
|
|
99
|
-
for the words, not the syntax around them.
|
|
62
|
+
`sense map` prints the hub list without SQL; use these when you need it filtered or joined.
|
|
100
63
|
|
|
101
|
-
##
|
|
64
|
+
## E. Cold start on an unknown vault
|
|
102
65
|
|
|
103
66
|
```
|
|
104
|
-
sense
|
|
67
|
+
sense map # fields, hubs, recent — read this first
|
|
68
|
+
sense --list # named queries someone already saved
|
|
69
|
+
sense query "SELECT DISTINCT type FROM frontmatter" # what a field's values are
|
|
105
70
|
```
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
71
|
+
|
|
72
|
+
## Anti-patterns
|
|
73
|
+
|
|
74
|
+
| don't | because | instead |
|
|
75
|
+
|---|---|---|
|
|
76
|
+
| `SELECT text FROM content` | dumps the vault into context | `snippet(content, -1, '«', '»', '…', 10)` |
|
|
77
|
+
| `sense find "pricing"` | one word misses synonyms | `"pricing OR billing OR invoicing"` |
|
|
78
|
+
| `Read` a 4,000-token file for one section | 10× the tokens needed | `peek` first, `Read` the line range |
|
|
79
|
+
| queries without `LIMIT` | unbounded output | `LIMIT 10`, widen only if all rows look wrong |
|
|
80
|
+
| save every query to config | config churn for one-offs | ad-hoc `sense query`; save only reusable views |
|
package/skills/sense/SKILL.md
CHANGED
|
@@ -1,112 +1,78 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sense
|
|
3
|
-
description: Query a markdown tree with
|
|
3
|
+
description: Query a markdown tree with the sense CLI — filter notes by frontmatter, full-text search the prose, follow wikilinks/backlinks, and read note outlines. Use when the user wants to query, filter, count, search, or report on a folder of markdown notes, when you need to find which notes discuss a topic before reading them, when you want a note's backlinks or structure, when a directory has a sense.config.json, or when asked to add a named query to one.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# sense
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
SQL over a markdown tree, kept fresh by a filesystem check on every query. Four tables per file:
|
|
9
|
+
`frontmatter` (one column per key, plus `path`/`_mtime`/`_size`/`_rank`), `content` (FTS5:
|
|
10
|
+
`title`, `summary`, `text`), `links` (`src`, `target`, `dst` — `NULL` dst = dead link),
|
|
11
|
+
`sections` (heading outline with line ranges and token estimates).
|
|
9
12
|
|
|
10
|
-
|
|
11
|
-
|---------------|------------------------------------------------------------------------|-------------------|
|
|
12
|
-
| `frontmatter` | one column per frontmatter key (+ reserved `path`, `_mtime`, `_size`) | filtering |
|
|
13
|
-
| `content` | FTS5 index: `title`, `summary`, `text` (+ `path` to join on) | searching/ranking |
|
|
13
|
+
## The descent
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Spend tokens in this order (progressive disclosure: metadata first, payloads just in time); go
|
|
16
|
+
only as deep as the question needs.
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
1. `sense map` — orient once: fields, hub notes, recent changes. Fixed-size output.
|
|
19
|
+
2. `sense find "<terms>"` — locate: ranked references with excerpts, ~30 tokens/row.
|
|
20
|
+
3. `sense peek <path>` — structure before reading: outline with `[L143-162, ~380t]` ranges,
|
|
21
|
+
links both ways. ~17% the cost of reading the file.
|
|
22
|
+
4. `Read` the line range peek gave you — not the whole file.
|
|
18
23
|
|
|
19
|
-
|
|
20
|
-
present), `hit` (matching excerpt) — enough to decide whether a file is worth opening at ~2% of
|
|
21
|
-
the token cost of reading it. Then `Read` the one or two paths that matter.
|
|
24
|
+
Every result is a reference, never file contents. Prefer `--format json` when consuming output.
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
sense query "SELECT f.path, content.title, content.summary, snippet(content, -1, '«', '»', '…', 10) AS hit
|
|
25
|
-
FROM frontmatter f JOIN content ON content.path = f.path
|
|
26
|
-
WHERE content MATCH ?
|
|
27
|
-
ORDER BY bm25(content, 10.0, 5.0, 1.0) LIMIT 10" "compensation OR equity" --format json
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
Select `title`/`summary` from `content`, where they always exist (empty when a note lacks the
|
|
31
|
-
key) — on `frontmatter` they're discovered columns, present only if some note declares them, so
|
|
32
|
-
`f.summary` errors on a vault with no summaries yet. Add frontmatter conditions
|
|
33
|
-
(`f.status = 'active' AND has(f.track, ?)`) to the same WHERE — the filter and the search compose
|
|
34
|
-
in one query. Worked traces for the common cases (discovery,
|
|
35
|
-
known-field filtering, cold start, anti-patterns): [EXAMPLES.md](EXAMPLES.md).
|
|
36
|
-
|
|
37
|
-
## Before anything else
|
|
38
|
-
|
|
39
|
-
Confirm `sense` is installed (`sense --list` or `which sense`; if missing,
|
|
40
|
-
`npm install -g sensemaking`) and that a config exists. Discovery walks **up** from cwd looking for
|
|
41
|
-
`sense.config.json`, same as git looks for `.git` — you don't need to `cd` to the project root, and
|
|
42
|
-
`--config <path>` bypasses discovery entirely. If no config exists yet, `sense init` at the tree's
|
|
43
|
-
root writes a minimal one (globs only, no queries — ad-hoc `sense query` works immediately).
|
|
44
|
-
|
|
45
|
-
## Cold start: discover what's queryable
|
|
26
|
+
## Verbs
|
|
46
27
|
|
|
47
28
|
```
|
|
48
|
-
sense --
|
|
49
|
-
sense
|
|
50
|
-
sense
|
|
29
|
+
sense find "pricing OR billing OR invoicing" --where "f.status = 'active'" --k 10
|
|
30
|
+
sense peek notes/pricing-model.md # a unique basename also works
|
|
31
|
+
sense map
|
|
32
|
+
sense query "<sql>" [params...] # ad-hoc SQL; ? binds positional args, count-checked
|
|
33
|
+
sense <name> [params...] # named query from sense.config.json
|
|
34
|
+
sense --list | status | rebuild
|
|
51
35
|
```
|
|
52
36
|
|
|
53
|
-
|
|
37
|
+
- **Expand terms before searching.** Write `pricing OR billing OR invoicing`, not one word — you
|
|
38
|
+
know the synonyms; the index only knows the words in the files.
|
|
39
|
+
- **Over-fetch, then choose.** `--k 20`, read the rows, open the 2–3 that matter.
|
|
40
|
+
- `find` fuses BM25 with link-graph expansion; the `via` column says what produced each row —
|
|
41
|
+
`match` (terms hit), `link` (connected to notes that hit), `match+link` (both).
|
|
42
|
+
- `--where` takes a frontmatter condition against alias `f`, e.g. `"f.status = 'active' AND has(f.tags, 'x')"`.
|
|
54
43
|
|
|
55
|
-
##
|
|
44
|
+
## SQL, when the verbs aren't enough
|
|
56
45
|
|
|
57
46
|
```
|
|
58
|
-
sense
|
|
59
|
-
sense query "
|
|
47
|
+
sense query "SELECT name FROM pragma_table_info('frontmatter')" # what fields exist
|
|
48
|
+
sense query "SELECT DISTINCT status FROM frontmatter" # what values a field takes
|
|
49
|
+
sense query "SELECT src FROM links WHERE dst = ?" notes/pricing-model.md # backlinks
|
|
50
|
+
sense query "SELECT src, target FROM links WHERE dst IS NULL" # dead links
|
|
51
|
+
sense query "SELECT path FROM frontmatter WHERE path NOT IN (SELECT dst FROM links WHERE dst IS NOT NULL)" # orphans
|
|
52
|
+
sense query "SELECT heading, start_line, tokens FROM sections WHERE path = ?" a.md # budget a read
|
|
60
53
|
```
|
|
61
54
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
- `
|
|
70
|
-
`
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
- `
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
Give every note a one-line `summary:` in its frontmatter — what's on the page and when it's worth
|
|
88
|
-
opening, like a skill's `description:`. It pays twice: it appears in result rows (often answering
|
|
89
|
-
the question with no file read at all), and it's a weighted search field. Keep it to one line.
|
|
90
|
-
|
|
91
|
-
Reserved frontmatter key names (dropped with a warning): `path`, `_mtime`, `_size`, `content`.
|
|
92
|
-
|
|
93
|
-
## `has()` semantics
|
|
94
|
-
|
|
95
|
-
The one custom SQL function, for frontmatter fields that are arrays or free text:
|
|
96
|
-
|
|
97
|
-
| field type | `has(field, value)` means |
|
|
98
|
-
|--------------------------|---------------------------|
|
|
99
|
-
| JSON array (e.g. `tags`) | array membership |
|
|
100
|
-
| string | substring match |
|
|
101
|
-
| NULL (key absent) | always false |
|
|
102
|
-
|
|
103
|
-
## Exit codes
|
|
104
|
-
|
|
105
|
-
`0` success · `1` a real error (bad config, SQL error — the SQLite message is printed verbatim) ·
|
|
106
|
-
`2` usage error (missing query name, unknown query name, wrong parameter count).
|
|
107
|
-
|
|
108
|
-
## When results look stale
|
|
109
|
-
|
|
110
|
-
`sense rebuild` deletes the local `.sense/` cache and re-crawls from scratch. Rarely needed since
|
|
111
|
-
every query reconciles on open — use it if you doubt the cache. `sense status` shows doc count, db
|
|
112
|
-
path, and whether a background `sense watch` (optional pre-warmer) is running.
|
|
55
|
+
- `content MATCH` takes FTS5 syntax: `a OR b`, `"phrase"`, `pref*`, `NEAR(a b, 5)`,
|
|
56
|
+
`summary: term`. Stemmed; markdown stripped at index time.
|
|
57
|
+
- Rank with `ORDER BY bm25(content, 10.0, 5.0, 1.0)` (title > summary > body); excerpt with
|
|
58
|
+
`snippet(content, -1, '«', '»', '…', 10)`.
|
|
59
|
+
- Select `content.title`/`content.summary` (always exist, empty when absent) rather than
|
|
60
|
+
`f.title`/`f.summary` (discovered columns — error on vaults that never declare them).
|
|
61
|
+
- `has(field, value)`: array membership on JSON-array fields, substring on strings, false on NULL.
|
|
62
|
+
- Never `SELECT text FROM content` — that is the whole vault into context. `SELECT * FROM
|
|
63
|
+
frontmatter` is safe; prose is not a frontmatter column. Always `LIMIT`.
|
|
64
|
+
|
|
65
|
+
Worked traces: [EXAMPLES.md](EXAMPLES.md).
|
|
66
|
+
|
|
67
|
+
## Setup and upkeep
|
|
68
|
+
|
|
69
|
+
- Missing CLI: `npm install -g sensemaking`. Missing config: `sense init` at the tree root.
|
|
70
|
+
Discovery walks up from cwd; `--config <path>` overrides.
|
|
71
|
+
- Save a query into `sense.config.json` only when it will be reused; run ad-hoc otherwise.
|
|
72
|
+
- When writing notes, give each a one-line `summary:` — it appears in every result row and is a
|
|
73
|
+
weighted search field.
|
|
74
|
+
- Reserved frontmatter keys (dropped with a warning): `path`, `_mtime`, `_size`, `_rank`,
|
|
75
|
+
`content`, `links`, `sections`.
|
|
76
|
+
- Exit codes: `0` ok, `1` error (SQLite message verbatim), `2` usage (unknown query, wrong
|
|
77
|
+
param count).
|
|
78
|
+
- Doubted cache: `sense rebuild`. Rarely needed — every query reconciles first.
|