sensemaking 0.4.0 → 0.6.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 +16 -7
- package/dist/cjs/cli.js +5 -1
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/commands/find.js +173 -10
- package/dist/cjs/commands/find.js.map +1 -1
- package/dist/cjs/commands/map.js +1 -1
- package/dist/cjs/commands/map.js.map +1 -1
- package/dist/cjs/commands/peek.js +1 -1
- package/dist/cjs/commands/peek.js.map +1 -1
- package/dist/cjs/commands/shared.d.cts +1 -1
- package/dist/cjs/commands/shared.d.ts +1 -1
- package/dist/cjs/commands/shared.js +166 -8
- package/dist/cjs/commands/shared.js.map +1 -1
- package/dist/cjs/commands/status.js +10 -1
- package/dist/cjs/commands/status.js.map +1 -1
- package/dist/cjs/commands/types.d.cts +1 -0
- package/dist/cjs/commands/types.d.ts +1 -0
- package/dist/cjs/config.d.cts +26 -2
- package/dist/cjs/config.d.ts +26 -2
- package/dist/cjs/config.js +171 -10
- package/dist/cjs/config.js.map +1 -1
- package/dist/cjs/db.js +46 -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/embed.d.cts +14 -0
- package/dist/cjs/features/embed.d.ts +14 -0
- package/dist/cjs/features/embed.js +840 -0
- package/dist/cjs/features/embed.js.map +1 -0
- package/dist/cjs/features/index.js +3 -1
- package/dist/cjs/features/index.js.map +1 -1
- package/dist/cjs/features/types.d.cts +4 -1
- package/dist/cjs/features/types.d.ts +4 -1
- package/dist/cjs/index.d.cts +3 -10
- package/dist/cjs/index.d.ts +3 -10
- package/dist/cjs/index.js +2 -52
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/output.d.cts +5 -0
- package/dist/cjs/output.d.ts +5 -0
- package/dist/cjs/output.js +14 -1
- package/dist/cjs/output.js.map +1 -1
- package/dist/cjs/scan.js +9 -8
- package/dist/cjs/scan.js.map +1 -1
- package/dist/cjs/verbs.d.cts +8 -2
- package/dist/cjs/verbs.d.ts +8 -2
- package/dist/cjs/verbs.js +263 -76
- package/dist/cjs/verbs.js.map +1 -1
- package/dist/esm/cli.js +5 -1
- package/dist/esm/cli.js.map +1 -1
- package/dist/esm/commands/find.js +5 -4
- package/dist/esm/commands/find.js.map +1 -1
- package/dist/esm/commands/map.js +1 -3
- package/dist/esm/commands/map.js.map +1 -1
- package/dist/esm/commands/peek.js +1 -1
- package/dist/esm/commands/peek.js.map +1 -1
- package/dist/esm/commands/shared.d.ts +1 -1
- package/dist/esm/commands/shared.js +2 -2
- package/dist/esm/commands/shared.js.map +1 -1
- package/dist/esm/commands/status.js +8 -1
- package/dist/esm/commands/status.js.map +1 -1
- package/dist/esm/commands/types.d.ts +1 -0
- package/dist/esm/commands/types.js.map +1 -1
- package/dist/esm/config.d.ts +26 -2
- package/dist/esm/config.js +88 -10
- package/dist/esm/config.js.map +1 -1
- package/dist/esm/db.js +19 -10
- 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/embed.d.ts +14 -0
- package/dist/esm/features/embed.js +290 -0
- package/dist/esm/features/embed.js.map +1 -0
- package/dist/esm/features/index.js +3 -1
- package/dist/esm/features/index.js.map +1 -1
- package/dist/esm/features/types.d.ts +4 -1
- package/dist/esm/features/types.js.map +1 -1
- package/dist/esm/index.d.ts +3 -10
- package/dist/esm/index.js +4 -6
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/output.d.ts +5 -0
- package/dist/esm/output.js +11 -1
- package/dist/esm/output.js.map +1 -1
- package/dist/esm/scan.js +9 -8
- package/dist/esm/scan.js.map +1 -1
- package/dist/esm/verbs.d.ts +8 -2
- package/dist/esm/verbs.js +51 -13
- package/dist/esm/verbs.js.map +1 -1
- package/package.json +2 -1
- package/schema.json +17 -1
- package/skills/sense/EXAMPLES.md +16 -0
- package/skills/sense/SKILL.md +41 -19
- package/skills/sense-setup/SKILL.md +73 -0
package/dist/cjs/config.d.cts
CHANGED
|
@@ -1,15 +1,27 @@
|
|
|
1
1
|
export declare const CONFIG_FILENAME = "sense.config.json";
|
|
2
2
|
export declare const STATE_DIR = ".sense";
|
|
3
3
|
export declare const SUPPORTED_CONFIG_VERSION = 2;
|
|
4
|
-
|
|
4
|
+
declare const FEATURE_NAMES: readonly ["links", "sections", "rank", "embed"];
|
|
5
5
|
export type FeatureName = (typeof FEATURE_NAMES)[number];
|
|
6
|
+
export interface EmbedConfig {
|
|
7
|
+
model?: string;
|
|
8
|
+
type?: 'static' | 'api';
|
|
9
|
+
url?: string;
|
|
10
|
+
key?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare const DEFAULT_EMBED_MODEL = "minishlab/potion-retrieval-32M";
|
|
6
13
|
export interface Config {
|
|
7
14
|
$schema?: string;
|
|
8
15
|
version?: number;
|
|
9
16
|
scan: {
|
|
10
17
|
include: string[];
|
|
11
18
|
};
|
|
12
|
-
features?:
|
|
19
|
+
features?: {
|
|
20
|
+
links?: boolean;
|
|
21
|
+
sections?: boolean;
|
|
22
|
+
rank?: boolean;
|
|
23
|
+
embed?: boolean | EmbedConfig;
|
|
24
|
+
};
|
|
13
25
|
queries: Record<string, string>;
|
|
14
26
|
}
|
|
15
27
|
export interface ResolvedConfig extends Config {
|
|
@@ -19,6 +31,17 @@ export interface ResolvedConfig extends Config {
|
|
|
19
31
|
}
|
|
20
32
|
export declare function featureEnabled(cfg: Config, name: FeatureName): boolean;
|
|
21
33
|
export declare function enabledFeatures(cfg: Config): FeatureName[];
|
|
34
|
+
export declare function featureStates(cfg: Config): {
|
|
35
|
+
on: FeatureName[];
|
|
36
|
+
off: FeatureName[];
|
|
37
|
+
};
|
|
38
|
+
export declare function embedConfig(cfg: Config): {
|
|
39
|
+
model: string;
|
|
40
|
+
type: 'static' | 'api';
|
|
41
|
+
url?: string;
|
|
42
|
+
key?: string;
|
|
43
|
+
} | null;
|
|
44
|
+
export declare function featureSignature(cfg: Config): string;
|
|
22
45
|
export declare function migrateConfig(cfg: Config): {
|
|
23
46
|
cfg: Config;
|
|
24
47
|
from: number;
|
|
@@ -26,3 +49,4 @@ export declare function migrateConfig(cfg: Config): {
|
|
|
26
49
|
export declare function initConfig(dir: string): string;
|
|
27
50
|
export declare function findConfigPath(startDir: string): string | null;
|
|
28
51
|
export declare function loadConfig(explicitPath?: string): ResolvedConfig;
|
|
52
|
+
export {};
|
package/dist/cjs/config.d.ts
CHANGED
|
@@ -1,15 +1,27 @@
|
|
|
1
1
|
export declare const CONFIG_FILENAME = "sense.config.json";
|
|
2
2
|
export declare const STATE_DIR = ".sense";
|
|
3
3
|
export declare const SUPPORTED_CONFIG_VERSION = 2;
|
|
4
|
-
|
|
4
|
+
declare const FEATURE_NAMES: readonly ["links", "sections", "rank", "embed"];
|
|
5
5
|
export type FeatureName = (typeof FEATURE_NAMES)[number];
|
|
6
|
+
export interface EmbedConfig {
|
|
7
|
+
model?: string;
|
|
8
|
+
type?: 'static' | 'api';
|
|
9
|
+
url?: string;
|
|
10
|
+
key?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare const DEFAULT_EMBED_MODEL = "minishlab/potion-retrieval-32M";
|
|
6
13
|
export interface Config {
|
|
7
14
|
$schema?: string;
|
|
8
15
|
version?: number;
|
|
9
16
|
scan: {
|
|
10
17
|
include: string[];
|
|
11
18
|
};
|
|
12
|
-
features?:
|
|
19
|
+
features?: {
|
|
20
|
+
links?: boolean;
|
|
21
|
+
sections?: boolean;
|
|
22
|
+
rank?: boolean;
|
|
23
|
+
embed?: boolean | EmbedConfig;
|
|
24
|
+
};
|
|
13
25
|
queries: Record<string, string>;
|
|
14
26
|
}
|
|
15
27
|
export interface ResolvedConfig extends Config {
|
|
@@ -19,6 +31,17 @@ export interface ResolvedConfig extends Config {
|
|
|
19
31
|
}
|
|
20
32
|
export declare function featureEnabled(cfg: Config, name: FeatureName): boolean;
|
|
21
33
|
export declare function enabledFeatures(cfg: Config): FeatureName[];
|
|
34
|
+
export declare function featureStates(cfg: Config): {
|
|
35
|
+
on: FeatureName[];
|
|
36
|
+
off: FeatureName[];
|
|
37
|
+
};
|
|
38
|
+
export declare function embedConfig(cfg: Config): {
|
|
39
|
+
model: string;
|
|
40
|
+
type: 'static' | 'api';
|
|
41
|
+
url?: string;
|
|
42
|
+
key?: string;
|
|
43
|
+
} | null;
|
|
44
|
+
export declare function featureSignature(cfg: Config): string;
|
|
22
45
|
export declare function migrateConfig(cfg: Config): {
|
|
23
46
|
cfg: Config;
|
|
24
47
|
from: number;
|
|
@@ -26,3 +49,4 @@ export declare function migrateConfig(cfg: Config): {
|
|
|
26
49
|
export declare function initConfig(dir: string): string;
|
|
27
50
|
export declare function findConfigPath(startDir: string): string | null;
|
|
28
51
|
export declare function loadConfig(explicitPath?: string): ResolvedConfig;
|
|
52
|
+
export {};
|
package/dist/cjs/config.js
CHANGED
|
@@ -12,8 +12,8 @@ _export(exports, {
|
|
|
12
12
|
get CONFIG_FILENAME () {
|
|
13
13
|
return CONFIG_FILENAME;
|
|
14
14
|
},
|
|
15
|
-
get
|
|
16
|
-
return
|
|
15
|
+
get DEFAULT_EMBED_MODEL () {
|
|
16
|
+
return DEFAULT_EMBED_MODEL;
|
|
17
17
|
},
|
|
18
18
|
get STATE_DIR () {
|
|
19
19
|
return STATE_DIR;
|
|
@@ -21,12 +21,21 @@ _export(exports, {
|
|
|
21
21
|
get SUPPORTED_CONFIG_VERSION () {
|
|
22
22
|
return SUPPORTED_CONFIG_VERSION;
|
|
23
23
|
},
|
|
24
|
+
get embedConfig () {
|
|
25
|
+
return embedConfig;
|
|
26
|
+
},
|
|
24
27
|
get enabledFeatures () {
|
|
25
28
|
return enabledFeatures;
|
|
26
29
|
},
|
|
27
30
|
get featureEnabled () {
|
|
28
31
|
return featureEnabled;
|
|
29
32
|
},
|
|
33
|
+
get featureSignature () {
|
|
34
|
+
return featureSignature;
|
|
35
|
+
},
|
|
36
|
+
get featureStates () {
|
|
37
|
+
return featureStates;
|
|
38
|
+
},
|
|
30
39
|
get findConfigPath () {
|
|
31
40
|
return findConfigPath;
|
|
32
41
|
},
|
|
@@ -43,6 +52,14 @@ _export(exports, {
|
|
|
43
52
|
var _nodefs = require("node:fs");
|
|
44
53
|
var _nodepath = require("node:path");
|
|
45
54
|
var _errorsts = require("./errors.js");
|
|
55
|
+
function _array_like_to_array(arr, len) {
|
|
56
|
+
if (len == null || len > arr.length) len = arr.length;
|
|
57
|
+
for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
|
|
58
|
+
return arr2;
|
|
59
|
+
}
|
|
60
|
+
function _array_with_holes(arr) {
|
|
61
|
+
if (Array.isArray(arr)) return arr;
|
|
62
|
+
}
|
|
46
63
|
function _define_property(obj, key, value) {
|
|
47
64
|
if (key in obj) {
|
|
48
65
|
Object.defineProperty(obj, key, {
|
|
@@ -56,6 +73,33 @@ function _define_property(obj, key, value) {
|
|
|
56
73
|
}
|
|
57
74
|
return obj;
|
|
58
75
|
}
|
|
76
|
+
function _iterable_to_array_limit(arr, i) {
|
|
77
|
+
var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
|
|
78
|
+
if (_i == null) return;
|
|
79
|
+
var _arr = [];
|
|
80
|
+
var _n = true;
|
|
81
|
+
var _d = false;
|
|
82
|
+
var _s, _e;
|
|
83
|
+
try {
|
|
84
|
+
for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
|
|
85
|
+
_arr.push(_s.value);
|
|
86
|
+
if (i && _arr.length === i) break;
|
|
87
|
+
}
|
|
88
|
+
} catch (err) {
|
|
89
|
+
_d = true;
|
|
90
|
+
_e = err;
|
|
91
|
+
} finally{
|
|
92
|
+
try {
|
|
93
|
+
if (!_n && _i["return"] != null) _i["return"]();
|
|
94
|
+
} finally{
|
|
95
|
+
if (_d) throw _e;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return _arr;
|
|
99
|
+
}
|
|
100
|
+
function _non_iterable_rest() {
|
|
101
|
+
throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
102
|
+
}
|
|
59
103
|
function _object_spread(target) {
|
|
60
104
|
for(var i = 1; i < arguments.length; i++){
|
|
61
105
|
var source = arguments[i] != null ? arguments[i] : {};
|
|
@@ -95,17 +139,42 @@ function _object_spread_props(target, source) {
|
|
|
95
139
|
}
|
|
96
140
|
return target;
|
|
97
141
|
}
|
|
142
|
+
function _sliced_to_array(arr, i) {
|
|
143
|
+
return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest();
|
|
144
|
+
}
|
|
145
|
+
function _type_of(obj) {
|
|
146
|
+
"@swc/helpers - typeof";
|
|
147
|
+
return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
|
|
148
|
+
}
|
|
149
|
+
function _unsupported_iterable_to_array(o, minLen) {
|
|
150
|
+
if (!o) return;
|
|
151
|
+
if (typeof o === "string") return _array_like_to_array(o, minLen);
|
|
152
|
+
var n = Object.prototype.toString.call(o).slice(8, -1);
|
|
153
|
+
if (n === "Object" && o.constructor) n = o.constructor.name;
|
|
154
|
+
if (n === "Map" || n === "Set") return Array.from(n);
|
|
155
|
+
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
|
|
156
|
+
}
|
|
98
157
|
var CONFIG_FILENAME = 'sense.config.json';
|
|
99
158
|
var STATE_DIR = '.sense';
|
|
100
159
|
var SUPPORTED_CONFIG_VERSION = 2;
|
|
160
|
+
// Each feature owns its tables, parse-time extraction, and reconcile step; verbs degrade when one is off.
|
|
161
|
+
// links/sections/rank are opt-out (absent = on); embed is opt-in (absent = off) -- most trees don't need vectors.
|
|
101
162
|
var FEATURE_NAMES = [
|
|
163
|
+
'links',
|
|
164
|
+
'sections',
|
|
165
|
+
'rank',
|
|
166
|
+
'embed'
|
|
167
|
+
];
|
|
168
|
+
var OPT_OUT_NAMES = [
|
|
102
169
|
'links',
|
|
103
170
|
'sections',
|
|
104
171
|
'rank'
|
|
105
172
|
];
|
|
173
|
+
var DEFAULT_EMBED_MODEL = 'minishlab/potion-retrieval-32M';
|
|
106
174
|
function featureEnabled(cfg, name) {
|
|
107
|
-
var _cfg_features;
|
|
108
|
-
|
|
175
|
+
var _cfg_features, _cfg_features1;
|
|
176
|
+
if (name === 'embed') return Boolean((_cfg_features = cfg.features) === null || _cfg_features === void 0 ? void 0 : _cfg_features.embed);
|
|
177
|
+
var enabled = ((_cfg_features1 = cfg.features) === null || _cfg_features1 === void 0 ? void 0 : _cfg_features1[name]) !== false;
|
|
109
178
|
if (name === 'rank') return enabled && featureEnabled(cfg, 'links');
|
|
110
179
|
return enabled;
|
|
111
180
|
}
|
|
@@ -114,13 +183,44 @@ function enabledFeatures(cfg) {
|
|
|
114
183
|
return featureEnabled(cfg, name);
|
|
115
184
|
});
|
|
116
185
|
}
|
|
186
|
+
function featureStates(cfg) {
|
|
187
|
+
return {
|
|
188
|
+
on: FEATURE_NAMES.filter(function(name) {
|
|
189
|
+
return featureEnabled(cfg, name);
|
|
190
|
+
}),
|
|
191
|
+
off: FEATURE_NAMES.filter(function(name) {
|
|
192
|
+
return !featureEnabled(cfg, name);
|
|
193
|
+
})
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
function embedConfig(cfg) {
|
|
197
|
+
var _o_model, _o_type;
|
|
198
|
+
var _cfg_features;
|
|
199
|
+
var e = (_cfg_features = cfg.features) === null || _cfg_features === void 0 ? void 0 : _cfg_features.embed;
|
|
200
|
+
if (!e) return null;
|
|
201
|
+
var o = e === true ? {} : e;
|
|
202
|
+
return {
|
|
203
|
+
model: (_o_model = o.model) !== null && _o_model !== void 0 ? _o_model : DEFAULT_EMBED_MODEL,
|
|
204
|
+
type: (_o_type = o.type) !== null && _o_type !== void 0 ? _o_type : 'static',
|
|
205
|
+
url: o.url,
|
|
206
|
+
key: o.key
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
function featureSignature(cfg) {
|
|
210
|
+
return enabledFeatures(cfg).map(function(name) {
|
|
211
|
+
if (name !== 'embed') return name;
|
|
212
|
+
var e = embedConfig(cfg);
|
|
213
|
+
return "embed:".concat(e === null || e === void 0 ? void 0 : e.type, ":").concat(e === null || e === void 0 ? void 0 : e.model);
|
|
214
|
+
}).join(',');
|
|
215
|
+
}
|
|
117
216
|
// Pure per-version steps; loadConfig chains them from the file's version up to SUPPORTED_CONFIG_VERSION.
|
|
118
217
|
var MIGRATIONS = {
|
|
119
|
-
// v1 -> v2: features block introduced,
|
|
218
|
+
// v1 -> v2: features block introduced, opt-out features enabled (matches the old implicit
|
|
219
|
+
// behavior of `links` etc. not existing). embed stays absent -- opt-in.
|
|
120
220
|
1: function(cfg) {
|
|
121
221
|
return _object_spread_props(_object_spread({}, cfg), {
|
|
122
222
|
version: 2,
|
|
123
|
-
features: Object.fromEntries(
|
|
223
|
+
features: Object.fromEntries(OPT_OUT_NAMES.map(function(name) {
|
|
124
224
|
return [
|
|
125
225
|
name,
|
|
126
226
|
true
|
|
@@ -152,7 +252,7 @@ function starterConfig() {
|
|
|
152
252
|
'**/*.md'
|
|
153
253
|
]
|
|
154
254
|
},
|
|
155
|
-
features: Object.fromEntries(
|
|
255
|
+
features: Object.fromEntries(OPT_OUT_NAMES.map(function(name) {
|
|
156
256
|
return [
|
|
157
257
|
name,
|
|
158
258
|
true
|
|
@@ -179,8 +279,66 @@ function findConfigPath(startDir) {
|
|
|
179
279
|
dir = parent;
|
|
180
280
|
}
|
|
181
281
|
}
|
|
282
|
+
// Shape check for hand-edited files: a typo'd config fails with a named error, not a
|
|
283
|
+
// TypeError from whatever code touched the missing field first. `queries` is optional
|
|
284
|
+
// on disk (absent = none); `scan.include` has no usable default.
|
|
285
|
+
function validateConfig(parsed, configPath) {
|
|
286
|
+
var _cfg_features;
|
|
287
|
+
if ((typeof parsed === "undefined" ? "undefined" : _type_of(parsed)) !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
288
|
+
throw new _errorsts.SenseError('CONFIG_INVALID', "".concat(configPath, ": config must be a JSON object"));
|
|
289
|
+
}
|
|
290
|
+
var cfg = parsed;
|
|
291
|
+
var scan = cfg.scan;
|
|
292
|
+
if (!scan || !Array.isArray(scan.include) || scan.include.length === 0 || !scan.include.every(function(g) {
|
|
293
|
+
return typeof g === 'string';
|
|
294
|
+
})) {
|
|
295
|
+
throw new _errorsts.SenseError('CONFIG_INVALID', "".concat(configPath, ": scan.include must be a non-empty array of glob strings"));
|
|
296
|
+
}
|
|
297
|
+
if (cfg.queries === undefined) cfg.queries = {};
|
|
298
|
+
if (_type_of(cfg.queries) !== 'object' || cfg.queries === null || Array.isArray(cfg.queries) || !Object.values(cfg.queries).every(function(q) {
|
|
299
|
+
return typeof q === 'string';
|
|
300
|
+
})) {
|
|
301
|
+
throw new _errorsts.SenseError('CONFIG_INVALID', "".concat(configPath, ": queries must be an object of name -> SQL string"));
|
|
302
|
+
}
|
|
303
|
+
if (cfg.features !== undefined && (_type_of(cfg.features) !== 'object' || cfg.features === null || Array.isArray(cfg.features))) {
|
|
304
|
+
throw new _errorsts.SenseError('CONFIG_INVALID', "".concat(configPath, ": features must be an object of name -> boolean"));
|
|
305
|
+
}
|
|
306
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
307
|
+
try {
|
|
308
|
+
var _loop = function() {
|
|
309
|
+
var _step_value = _sliced_to_array(_step.value, 2), name = _step_value[0], value = _step_value[1];
|
|
310
|
+
if (typeof value === 'boolean') return "continue";
|
|
311
|
+
// embed alone takes an object form: { model?, type?: static|api, url?, key? }.
|
|
312
|
+
var embed = value;
|
|
313
|
+
var shapeOk = name === 'embed' && (typeof value === "undefined" ? "undefined" : _type_of(value)) === 'object' && value !== null && !Array.isArray(value) && [
|
|
314
|
+
'model',
|
|
315
|
+
'url',
|
|
316
|
+
'key'
|
|
317
|
+
].every(function(f) {
|
|
318
|
+
return embed[f] === undefined || typeof embed[f] === 'string';
|
|
319
|
+
}) && (embed.type === undefined || embed.type === 'static' || embed.type === 'api');
|
|
320
|
+
if (!shapeOk) {
|
|
321
|
+
throw new _errorsts.SenseError('CONFIG_INVALID', "".concat(configPath, ": features.").concat(name, " must be a boolean").concat(name === 'embed' ? ' or { model?, type?: "static"|"api", url?, key? }' : ''));
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
for(var _iterator = Object.entries((_cfg_features = cfg.features) !== null && _cfg_features !== void 0 ? _cfg_features : {})[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true)_loop();
|
|
325
|
+
} catch (err) {
|
|
326
|
+
_didIteratorError = true;
|
|
327
|
+
_iteratorError = err;
|
|
328
|
+
} finally{
|
|
329
|
+
try {
|
|
330
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
331
|
+
_iterator.return();
|
|
332
|
+
}
|
|
333
|
+
} finally{
|
|
334
|
+
if (_didIteratorError) {
|
|
335
|
+
throw _iteratorError;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return cfg;
|
|
340
|
+
}
|
|
182
341
|
function loadConfig(explicitPath) {
|
|
183
|
-
var _cfg_version;
|
|
184
342
|
var configPath;
|
|
185
343
|
if (explicitPath) {
|
|
186
344
|
configPath = (0, _nodepath.resolve)(process.cwd(), explicitPath);
|
|
@@ -193,11 +351,14 @@ function loadConfig(explicitPath) {
|
|
|
193
351
|
configPath = found;
|
|
194
352
|
}
|
|
195
353
|
var raw = (0, _nodefs.readFileSync)(configPath, 'utf8');
|
|
196
|
-
var
|
|
197
|
-
|
|
354
|
+
var parsed = JSON.parse(raw);
|
|
355
|
+
// Version gate before shape validation: a config written by a newer sense should fail
|
|
356
|
+
// with "requires a newer sense", not with shape errors its own version may not have.
|
|
357
|
+
var version = (typeof parsed === "undefined" ? "undefined" : _type_of(parsed)) === 'object' && parsed !== null && typeof parsed.version === 'number' ? parsed.version : 1;
|
|
198
358
|
if (version > SUPPORTED_CONFIG_VERSION) {
|
|
199
359
|
throw new _errorsts.SenseError('CONFIG_VERSION_UNSUPPORTED', "config version ".concat(version, " requires a newer sense"));
|
|
200
360
|
}
|
|
361
|
+
var cfg = validateConfig(parsed, configPath);
|
|
201
362
|
var migratedFrom;
|
|
202
363
|
if (version < SUPPORTED_CONFIG_VERSION) {
|
|
203
364
|
var result = migrateConfig(cfg);
|
package/dist/cjs/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/config.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport { SenseError } from './errors.ts';\n\nexport const CONFIG_FILENAME = 'sense.config.json';\nexport const STATE_DIR = '.sense';\n\n// Highest sense.config.json `version` this build understands. Older versions auto-migrate on load.\nexport const SUPPORTED_CONFIG_VERSION = 2;\n\n// Each feature owns its tables, parse-time extraction, and reconcile step; verbs degrade when one is off.\nexport const FEATURE_NAMES = ['links', 'sections', 'rank'] as const;\nexport type FeatureName = (typeof FEATURE_NAMES)[number];\n\nexport interface Config {\n // Editor-only pointer to schema.json; never read by sense.\n $schema?: string;\n version?: number;\n scan: { include: string[] };\n features?: Partial<Record<FeatureName, boolean>>;\n queries: Record<string, string>;\n}\n\nexport interface ResolvedConfig extends Config {\n baseDir: string;\n configPath: string | null;\n // Set when loadConfig auto-migrated the file on disk; cli reports it.\n migratedFrom?: number;\n}\n\n// Absent block or key means enabled -- features are opt-out. `rank` additionally requires `links`.\nexport function featureEnabled(cfg: Config, name: FeatureName): boolean {\n const enabled = cfg.features?.[name] !== false;\n if (name === 'rank') return enabled && featureEnabled(cfg, 'links');\n return enabled;\n}\n\nexport function enabledFeatures(cfg: Config): FeatureName[] {\n return FEATURE_NAMES.filter((name) => featureEnabled(cfg, name));\n}\n\n// Pure per-version steps; loadConfig chains them from the file's version up to SUPPORTED_CONFIG_VERSION.\nconst MIGRATIONS: Record<number, (cfg: Config) => Config> = {\n // v1 -> v2: features block introduced, everything enabled (matches the old implicit behavior of `links` etc. not existing).\n 1: (cfg) => ({ ...cfg, version: 2, features: Object.fromEntries(FEATURE_NAMES.map((name) => [name, true])) as Config['features'] }),\n};\n\nexport function migrateConfig(cfg: Config): { cfg: Config; from: number } {\n const from = cfg.version ?? 1;\n let current = cfg;\n for (let v = from; v < SUPPORTED_CONFIG_VERSION; v++) {\n const step = MIGRATIONS[v];\n if (!step) throw new SenseError('CONFIG_VERSION_UNSUPPORTED', `no migration from config version ${v}`);\n current = step(current);\n }\n return { cfg: current, from };\n}\n\nfunction starterConfig(): Config {\n return {\n $schema: 'https://unpkg.com/sensemaking/schema.json',\n version: SUPPORTED_CONFIG_VERSION,\n scan: { include: ['**/*.md'] },\n features: Object.fromEntries(FEATURE_NAMES.map((name) => [name, true])) as Config['features'],\n queries: {},\n };\n}\n\n// Refuses to overwrite an existing config.\nexport function initConfig(dir: string): string {\n const configPath = join(dir, CONFIG_FILENAME);\n if (existsSync(configPath)) {\n throw new SenseError('CONFIG_EXISTS', `${CONFIG_FILENAME} already exists in ${dir}`);\n }\n writeFileSync(configPath, `${JSON.stringify(starterConfig(), null, 2)}\\n`);\n return configPath;\n}\n\nexport function findConfigPath(startDir: string): string | null {\n let dir = resolve(startDir);\n for (;;) {\n const candidate = join(dir, CONFIG_FILENAME);\n if (existsSync(candidate)) return candidate;\n const parent = dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\nexport function loadConfig(explicitPath?: string): ResolvedConfig {\n let configPath: string;\n if (explicitPath) {\n configPath = resolve(process.cwd(), explicitPath);\n if (!existsSync(configPath)) throw new SenseError('CONFIG_NOT_FOUND', `config not found: ${configPath}`);\n } else {\n const found = findConfigPath(process.cwd());\n if (!found) {\n throw new SenseError('CONFIG_NOT_FOUND', `could not find ${CONFIG_FILENAME} in ${process.cwd()} or any parent directory`);\n }\n configPath = found;\n }\n\n const raw = readFileSync(configPath, 'utf8');\n let cfg = JSON.parse(raw) as Config;\n\n const version = cfg.version ?? 1;\n if (version > SUPPORTED_CONFIG_VERSION) {\n throw new SenseError('CONFIG_VERSION_UNSUPPORTED', `config version ${version} requires a newer sense`);\n }\n\n let migratedFrom: number | undefined;\n if (version < SUPPORTED_CONFIG_VERSION) {\n const result = migrateConfig(cfg);\n cfg = result.cfg;\n migratedFrom = result.from;\n writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}\\n`);\n }\n\n return { ...cfg, baseDir: dirname(configPath), configPath, migratedFrom };\n}\n"],"names":["CONFIG_FILENAME","FEATURE_NAMES","STATE_DIR","SUPPORTED_CONFIG_VERSION","enabledFeatures","featureEnabled","findConfigPath","initConfig","loadConfig","migrateConfig","cfg","name","enabled","features","filter","MIGRATIONS","version","Object","fromEntries","map","from","current","v","step","SenseError","starterConfig","$schema","scan","include","queries","dir","configPath","join","existsSync","writeFileSync","JSON","stringify","startDir","resolve","candidate","parent","dirname","explicitPath","process","cwd","found","raw","readFileSync","parse","migratedFrom","result","baseDir"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/config.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport { SenseError } from './errors.ts';\n\nexport const CONFIG_FILENAME = 'sense.config.json';\nexport const STATE_DIR = '.sense';\n\n// Highest sense.config.json `version` this build understands. Older versions auto-migrate on load.\nexport const SUPPORTED_CONFIG_VERSION = 2;\n\n// Each feature owns its tables, parse-time extraction, and reconcile step; verbs degrade when one is off.\n// links/sections/rank are opt-out (absent = on); embed is opt-in (absent = off) -- most trees don't need vectors.\nconst FEATURE_NAMES = ['links', 'sections', 'rank', 'embed'] as const;\nconst OPT_OUT_NAMES = ['links', 'sections', 'rank'] as const;\nexport type FeatureName = (typeof FEATURE_NAMES)[number];\n\n// `true` = all defaults (static type, default model). A local `model` path skips the network.\nexport interface EmbedConfig {\n model?: string;\n type?: 'static' | 'api';\n url?: string; // api type: OpenAI-compatible base URL, e.g. http://localhost:11434/v1\n key?: string; // api type: name of the env var holding the bearer token, if any\n}\n\nexport const DEFAULT_EMBED_MODEL = 'minishlab/potion-retrieval-32M';\n\nexport interface Config {\n // Editor-only pointer to schema.json; never read by sense.\n $schema?: string;\n version?: number;\n scan: { include: string[] };\n features?: { links?: boolean; sections?: boolean; rank?: boolean; embed?: boolean | EmbedConfig };\n queries: Record<string, string>;\n}\n\nexport interface ResolvedConfig extends Config {\n baseDir: string;\n configPath: string | null;\n // Set when loadConfig auto-migrated the file on disk; cli reports it.\n migratedFrom?: number;\n}\n\n// Opt-out features: absent block or key means enabled. `rank` additionally requires `links`.\n// `embed` is the opposite: enabled only when the config says so.\nexport function featureEnabled(cfg: Config, name: FeatureName): boolean {\n if (name === 'embed') return Boolean(cfg.features?.embed);\n const enabled = cfg.features?.[name] !== false;\n if (name === 'rank') return enabled && featureEnabled(cfg, 'links');\n return enabled;\n}\n\nexport function enabledFeatures(cfg: Config): FeatureName[] {\n return FEATURE_NAMES.filter((name) => featureEnabled(cfg, name));\n}\n\n// Every feature with its current state; verbs surface this so \"off\" and \"empty\" stay\n// distinguishable in output.\nexport function featureStates(cfg: Config): { on: FeatureName[]; off: FeatureName[] } {\n return {\n on: FEATURE_NAMES.filter((name) => featureEnabled(cfg, name)),\n off: FEATURE_NAMES.filter((name) => !featureEnabled(cfg, name)),\n };\n}\n\n// Resolved embed settings, or null when the feature is off.\nexport function embedConfig(cfg: Config): { model: string; type: 'static' | 'api'; url?: string; key?: string } | null {\n const e = cfg.features?.embed;\n if (!e) return null;\n const o = e === true ? {} : e;\n return { model: o.model ?? DEFAULT_EMBED_MODEL, type: o.type ?? 'static', url: o.url, key: o.key };\n}\n\n// Cache-key string: embed carries its type + model so a model change rebuilds like a toggle.\nexport function featureSignature(cfg: Config): string {\n return enabledFeatures(cfg)\n .map((name) => {\n if (name !== 'embed') return name;\n const e = embedConfig(cfg);\n return `embed:${e?.type}:${e?.model}`;\n })\n .join(',');\n}\n\n// Pure per-version steps; loadConfig chains them from the file's version up to SUPPORTED_CONFIG_VERSION.\nconst MIGRATIONS: Record<number, (cfg: Config) => Config> = {\n // v1 -> v2: features block introduced, opt-out features enabled (matches the old implicit\n // behavior of `links` etc. not existing). embed stays absent -- opt-in.\n 1: (cfg) => ({ ...cfg, version: 2, features: Object.fromEntries(OPT_OUT_NAMES.map((name) => [name, true])) as Config['features'] }),\n};\n\nexport function migrateConfig(cfg: Config): { cfg: Config; from: number } {\n const from = cfg.version ?? 1;\n let current = cfg;\n for (let v = from; v < SUPPORTED_CONFIG_VERSION; v++) {\n const step = MIGRATIONS[v];\n if (!step) throw new SenseError('CONFIG_VERSION_UNSUPPORTED', `no migration from config version ${v}`);\n current = step(current);\n }\n return { cfg: current, from };\n}\n\nfunction starterConfig(): Config {\n return {\n $schema: 'https://unpkg.com/sensemaking/schema.json',\n version: SUPPORTED_CONFIG_VERSION,\n scan: { include: ['**/*.md'] },\n features: Object.fromEntries(OPT_OUT_NAMES.map((name) => [name, true])) as Config['features'],\n queries: {},\n };\n}\n\n// Refuses to overwrite an existing config.\nexport function initConfig(dir: string): string {\n const configPath = join(dir, CONFIG_FILENAME);\n if (existsSync(configPath)) {\n throw new SenseError('CONFIG_EXISTS', `${CONFIG_FILENAME} already exists in ${dir}`);\n }\n writeFileSync(configPath, `${JSON.stringify(starterConfig(), null, 2)}\\n`);\n return configPath;\n}\n\nexport function findConfigPath(startDir: string): string | null {\n let dir = resolve(startDir);\n for (;;) {\n const candidate = join(dir, CONFIG_FILENAME);\n if (existsSync(candidate)) return candidate;\n const parent = dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\n// Shape check for hand-edited files: a typo'd config fails with a named error, not a\n// TypeError from whatever code touched the missing field first. `queries` is optional\n// on disk (absent = none); `scan.include` has no usable default.\nfunction validateConfig(parsed: unknown, configPath: string): Config {\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: config must be a JSON object`);\n }\n const cfg = parsed as Record<string, unknown>;\n const scan = cfg.scan as { include?: unknown } | undefined;\n if (!scan || !Array.isArray(scan.include) || scan.include.length === 0 || !scan.include.every((g) => typeof g === 'string')) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: scan.include must be a non-empty array of glob strings`);\n }\n if (cfg.queries === undefined) cfg.queries = {};\n if (typeof cfg.queries !== 'object' || cfg.queries === null || Array.isArray(cfg.queries) || !Object.values(cfg.queries).every((q) => typeof q === 'string')) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries must be an object of name -> SQL string`);\n }\n if (cfg.features !== undefined && (typeof cfg.features !== 'object' || cfg.features === null || Array.isArray(cfg.features))) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: features must be an object of name -> boolean`);\n }\n for (const [name, value] of Object.entries(cfg.features ?? {})) {\n if (typeof value === 'boolean') continue;\n // embed alone takes an object form: { model?, type?: static|api, url?, key? }.\n const embed = value as Record<string, unknown>;\n const shapeOk = name === 'embed' && typeof value === 'object' && value !== null && !Array.isArray(value) && ['model', 'url', 'key'].every((f) => embed[f] === undefined || typeof embed[f] === 'string') && (embed.type === undefined || embed.type === 'static' || embed.type === 'api');\n if (!shapeOk) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: features.${name} must be a boolean${name === 'embed' ? ' or { model?, type?: \"static\"|\"api\", url?, key? }' : ''}`);\n }\n }\n return cfg as unknown as Config;\n}\n\nexport function loadConfig(explicitPath?: string): ResolvedConfig {\n let configPath: string;\n if (explicitPath) {\n configPath = resolve(process.cwd(), explicitPath);\n if (!existsSync(configPath)) throw new SenseError('CONFIG_NOT_FOUND', `config not found: ${configPath}`);\n } else {\n const found = findConfigPath(process.cwd());\n if (!found) {\n throw new SenseError('CONFIG_NOT_FOUND', `could not find ${CONFIG_FILENAME} in ${process.cwd()} or any parent directory`);\n }\n configPath = found;\n }\n\n const raw = readFileSync(configPath, 'utf8');\n const parsed: unknown = JSON.parse(raw);\n\n // Version gate before shape validation: a config written by a newer sense should fail\n // with \"requires a newer sense\", not with shape errors its own version may not have.\n const version = typeof parsed === 'object' && parsed !== null && typeof (parsed as { version?: unknown }).version === 'number' ? (parsed as { version: number }).version : 1;\n if (version > SUPPORTED_CONFIG_VERSION) {\n throw new SenseError('CONFIG_VERSION_UNSUPPORTED', `config version ${version} requires a newer sense`);\n }\n\n let cfg = validateConfig(parsed, configPath);\n\n let migratedFrom: number | undefined;\n if (version < SUPPORTED_CONFIG_VERSION) {\n const result = migrateConfig(cfg);\n cfg = result.cfg;\n migratedFrom = result.from;\n writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}\\n`);\n }\n\n return { ...cfg, baseDir: dirname(configPath), configPath, migratedFrom };\n}\n"],"names":["CONFIG_FILENAME","DEFAULT_EMBED_MODEL","STATE_DIR","SUPPORTED_CONFIG_VERSION","embedConfig","enabledFeatures","featureEnabled","featureSignature","featureStates","findConfigPath","initConfig","loadConfig","migrateConfig","FEATURE_NAMES","OPT_OUT_NAMES","cfg","name","Boolean","features","embed","enabled","filter","on","off","o","e","model","type","url","key","map","join","MIGRATIONS","version","Object","fromEntries","from","current","v","step","SenseError","starterConfig","$schema","scan","include","queries","dir","configPath","existsSync","writeFileSync","JSON","stringify","startDir","resolve","candidate","parent","dirname","validateConfig","parsed","Array","isArray","length","every","g","undefined","values","q","value","shapeOk","f","entries","explicitPath","process","cwd","found","raw","readFileSync","parse","migratedFrom","result","baseDir"],"mappings":"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"}
|
package/dist/cjs/db.js
CHANGED
|
@@ -126,7 +126,7 @@ function ensureSchema(db, cfg) {
|
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
if (getMeta(db, 'schema_version') === null) setMeta(db, 'schema_version', SCHEMA_VERSION);
|
|
129
|
-
if (getMeta(db, 'features') === null) setMeta(db, 'features', (0, _configts.
|
|
129
|
+
if (getMeta(db, 'features') === null) setMeta(db, 'features', (0, _configts.featureSignature)(cfg));
|
|
130
130
|
}
|
|
131
131
|
function getMeta(db, key) {
|
|
132
132
|
var row = db.prepare('SELECT value FROM meta WHERE key = ?').get(key);
|
|
@@ -301,6 +301,7 @@ function reconcile(db, cfg, baseDir) {
|
|
|
301
301
|
var _loop = function() {
|
|
302
302
|
var doc = _step5.value;
|
|
303
303
|
var _insert;
|
|
304
|
+
var _feature_store;
|
|
304
305
|
var values = allColumns.map(function(col) {
|
|
305
306
|
var _doc_data_col;
|
|
306
307
|
if (col === 'path') return doc.relPath;
|
|
@@ -309,27 +310,52 @@ function reconcile(db, cfg, baseDir) {
|
|
|
309
310
|
return (_doc_data_col = doc.data[col]) !== null && _doc_data_col !== void 0 ? _doc_data_col : null;
|
|
310
311
|
});
|
|
311
312
|
(_insert = insert).run.apply(_insert, _to_consumable_array(values));
|
|
312
|
-
|
|
313
|
+
// Delete-before-insert only for docs that have rows: an FTS5 DELETE by column
|
|
314
|
+
// cannot use an index and scans the whole table, so running it per doc on a
|
|
315
|
+
// cold build (empty table, nothing to delete) made the crawl quadratic --
|
|
316
|
+
// measured 4x time per note-count doubling at 13k/26k notes.
|
|
317
|
+
if (existing.has(doc.relPath)) {
|
|
318
|
+
var _feature_remove;
|
|
319
|
+
delBody.run(doc.relPath);
|
|
320
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
321
|
+
try {
|
|
322
|
+
for(var _iterator = features[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
323
|
+
var feature = _step.value;
|
|
324
|
+
(_feature_remove = feature.remove) === null || _feature_remove === void 0 ? void 0 : _feature_remove.call(feature, db, doc.relPath);
|
|
325
|
+
}
|
|
326
|
+
} catch (err) {
|
|
327
|
+
_didIteratorError = true;
|
|
328
|
+
_iteratorError = err;
|
|
329
|
+
} finally{
|
|
330
|
+
try {
|
|
331
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
332
|
+
_iterator.return();
|
|
333
|
+
}
|
|
334
|
+
} finally{
|
|
335
|
+
if (_didIteratorError) {
|
|
336
|
+
throw _iteratorError;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
313
341
|
insertBody.run(doc.search.title, doc.search.summary, doc.search.text, doc.relPath);
|
|
314
|
-
var
|
|
342
|
+
var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
|
|
315
343
|
try {
|
|
316
|
-
for(var
|
|
317
|
-
var
|
|
318
|
-
|
|
319
|
-
(_feature_remove = feature.remove) === null || _feature_remove === void 0 ? void 0 : _feature_remove.call(feature, db, doc.relPath);
|
|
320
|
-
(_feature_store = feature.store) === null || _feature_store === void 0 ? void 0 : _feature_store.call(feature, db, doc.relPath, doc.extracted[feature.name]);
|
|
344
|
+
for(var _iterator1 = features[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
|
|
345
|
+
var feature1 = _step1.value;
|
|
346
|
+
(_feature_store = feature1.store) === null || _feature_store === void 0 ? void 0 : _feature_store.call(feature1, db, doc.relPath, doc.extracted[feature1.name]);
|
|
321
347
|
}
|
|
322
348
|
} catch (err) {
|
|
323
|
-
|
|
324
|
-
|
|
349
|
+
_didIteratorError1 = true;
|
|
350
|
+
_iteratorError1 = err;
|
|
325
351
|
} finally{
|
|
326
352
|
try {
|
|
327
|
-
if (!
|
|
328
|
-
|
|
353
|
+
if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
|
|
354
|
+
_iterator1.return();
|
|
329
355
|
}
|
|
330
356
|
} finally{
|
|
331
|
-
if (
|
|
332
|
-
throw
|
|
357
|
+
if (_didIteratorError1) {
|
|
358
|
+
throw _iteratorError1;
|
|
333
359
|
}
|
|
334
360
|
}
|
|
335
361
|
}
|
|
@@ -388,14 +414,18 @@ function open(cfg) {
|
|
|
388
414
|
var dbPath = (0, _nodepath.join)(stateDir, DB_FILENAME);
|
|
389
415
|
var db = new _nodesqlite.DatabaseSync(dbPath);
|
|
390
416
|
db.exec('PRAGMA journal_mode = WAL');
|
|
391
|
-
|
|
417
|
+
// Covers a concurrent watcher's bulk reconcile: the write transaction for 500 changed
|
|
418
|
+
// files measures ~5s at 26k notes, so 5s expired exactly at the boundary and queries
|
|
419
|
+
// racing the watcher got SQLITE_BUSY. 30s bounds the wait at ~3x the largest measured
|
|
420
|
+
// reconcile; a query that outwaits it still fails loudly.
|
|
421
|
+
db.exec('PRAGMA busy_timeout = 30000');
|
|
392
422
|
registerFunctions(db);
|
|
393
423
|
db.exec('CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT)');
|
|
394
424
|
// Schema-version or feature-set mismatch: reconcile only reparses changed files, so an
|
|
395
425
|
// old cache can't be patched incrementally -- rebuild instead (cheap: nothing expensive lives here).
|
|
396
426
|
var version = getMeta(db, 'schema_version');
|
|
397
427
|
var features = getMeta(db, 'features');
|
|
398
|
-
var wantFeatures = (0, _configts.
|
|
428
|
+
var wantFeatures = (0, _configts.featureSignature)(cfg);
|
|
399
429
|
if (version !== null && version !== SCHEMA_VERSION || features !== null && features !== wantFeatures) {
|
|
400
430
|
db.close();
|
|
401
431
|
(0, _nodefs.rmSync)(stateDir, {
|
package/dist/cjs/db.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/db.ts"],"sourcesContent":["import { mkdirSync, rmSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { DatabaseSync } from 'node:sqlite';\nimport type { Config, ResolvedConfig } from './config.ts';\nimport { enabledFeatures, STATE_DIR } from './config.ts';\nimport { activeFeatures } from './features/index.ts';\nimport type { ParsedDoc } from './scan.ts';\nimport { listFiles, parseFile } from './scan.ts';\n\n// Rows -> SQLite: core schema, reconcile loop, has(). Parsing lives in scan.ts;\n// everything beyond frontmatter + content lives in src/features/.\n\nexport const DB_FILENAME = 'cache.db';\n// Cache shape version, independent of the config's own `version`.\nexport const SCHEMA_VERSION = '3';\n\nexport interface OpenResult {\n db: DatabaseSync;\n cfg: ResolvedConfig;\n dbPath: string;\n parsed: number;\n warnings: string[];\n}\n\nfunction quoteIdent(name: string): string {\n return `\"${name.split('\"').join('\"\"')}\"`;\n}\n\n// has(field, value): JSON-array field -> membership, string field -> substring, NULL -> false.\nfunction registerFunctions(db: DatabaseSync): void {\n db.function('has', { deterministic: true, varargs: false }, (field: unknown, value: unknown): number => {\n if (field === null || field === undefined) return 0;\n\n const needle = String(value);\n\n if (typeof field === 'string') {\n if (field.startsWith('[')) {\n try {\n const parsed = JSON.parse(field);\n if (Array.isArray(parsed)) {\n return parsed.some((item) => String(item) === needle) ? 1 : 0;\n }\n } catch {}\n }\n return field.includes(needle) ? 1 : 0;\n }\n\n return String(field).includes(needle) ? 1 : 0;\n });\n}\n\nfunction getColumns(db: DatabaseSync): Set<string> {\n const rows = db.prepare('PRAGMA table_info(frontmatter)').all() as Array<{ name: string }>;\n return new Set(rows.map((r) => r.name));\n}\n\n// Content is a separate table (not a column on frontmatter) so `SELECT * FROM frontmatter`\n// can't dump file text into context. Features add their own tables after the core ones.\nfunction ensureSchema(db: DatabaseSync, cfg: Config): void {\n db.exec(`CREATE TABLE IF NOT EXISTS frontmatter (\"path\" TEXT PRIMARY KEY, \"_mtime\" REAL, \"_size\" INTEGER)`);\n db.exec(`CREATE VIRTUAL TABLE IF NOT EXISTS content USING fts5(title, summary, text, path UNINDEXED, tokenize = 'porter unicode61')`);\n for (const feature of activeFeatures(cfg)) feature.schema(db);\n if (getMeta(db, 'schema_version') === null) setMeta(db, 'schema_version', SCHEMA_VERSION);\n if (getMeta(db, 'features') === null) setMeta(db, 'features', enabledFeatures(cfg).join(','));\n}\n\nexport function getMeta(db: DatabaseSync, key: string): string | null {\n const row = db.prepare('SELECT value FROM meta WHERE key = ?').get(key) as { value: string } | undefined;\n return row ? row.value : null;\n}\n\nexport function setMeta(db: DatabaseSync, key: string, value: string | null): void {\n if (value === null) {\n db.prepare('DELETE FROM meta WHERE key = ?').run(key);\n return;\n }\n db.prepare('INSERT INTO meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value').run(key, value);\n}\n\nexport function docCount(db: DatabaseSync): number {\n const row = db.prepare('SELECT COUNT(*) AS n FROM frontmatter').get() as { n: number };\n return row.n;\n}\n\nexport function reconcile(db: DatabaseSync, cfg: Config, baseDir: string): { parsed: number; warnings: string[] } {\n const files = listFiles(cfg, baseDir);\n const currentSet = new Set(files.map((f) => f.relPath));\n\n const existingRows = db.prepare(`SELECT \"path\", \"_mtime\", \"_size\" FROM frontmatter`).all() as Array<{\n path: string;\n _mtime: number;\n _size: number;\n }>;\n const existing = new Map(existingRows.map((r) => [r.path, r]));\n const vanished = existingRows.filter((r) => !currentSet.has(r.path)).map((r) => r.path);\n\n const toReparse = files.filter((f) => {\n const row = existing.get(f.relPath);\n return !row || row._mtime !== f.mtimeMs || row._size !== f.size;\n });\n\n if (vanished.length === 0 && toReparse.length === 0) return { parsed: 0, warnings: [] };\n\n const features = activeFeatures(cfg);\n const seenColumns = getColumns(db);\n const newColumns: string[] = [];\n const parsedDocs: ParsedDoc[] = [];\n const warnings: string[] = [];\n\n for (const file of toReparse) {\n const { doc, warnings: fileWarnings } = parseFile(file, features);\n warnings.push(...fileWarnings);\n for (const key of Object.keys(doc.data)) {\n if (!seenColumns.has(key)) {\n seenColumns.add(key);\n newColumns.push(key);\n }\n }\n parsedDocs.push(doc);\n }\n\n const allColumns = [...seenColumns];\n const insertSql = `INSERT OR REPLACE INTO frontmatter (${allColumns.map(quoteIdent).join(', ')}) VALUES (${allColumns.map(() => '?').join(', ')})`;\n\n db.exec('BEGIN');\n try {\n for (const col of newColumns) db.exec(`ALTER TABLE frontmatter ADD COLUMN ${quoteIdent(col)}`);\n // FTS5 has no upsert, so delete-before-insert into `content`.\n const delBody = db.prepare(`DELETE FROM content WHERE \"path\" = ?`);\n if (vanished.length > 0) {\n const del = db.prepare(`DELETE FROM frontmatter WHERE \"path\" = ?`);\n for (const path of vanished) {\n del.run(path);\n delBody.run(path);\n for (const feature of features) feature.remove?.(db, path);\n }\n }\n if (parsedDocs.length > 0) {\n const insert = db.prepare(insertSql);\n const insertBody = db.prepare(`INSERT INTO content (title, summary, text, \"path\") VALUES (?, ?, ?, ?)`);\n for (const doc of parsedDocs) {\n const values = allColumns.map((col) => {\n if (col === 'path') return doc.relPath;\n if (col === '_mtime') return doc.mtimeMs;\n if (col === '_size') return doc.size;\n return doc.data[col] ?? null;\n });\n insert.run(...values);\n delBody.run(doc.relPath);\n insertBody.run(doc.search.title, doc.search.summary, doc.search.text, doc.relPath);\n for (const feature of features) {\n feature.remove?.(db, doc.relPath);\n feature.store?.(db, doc.relPath, doc.extracted[feature.name]);\n }\n }\n }\n for (const feature of features) feature.afterReconcile?.(db, files);\n db.exec('COMMIT');\n } catch (err) {\n db.exec('ROLLBACK');\n throw err;\n }\n\n return { parsed: parsedDocs.length, warnings };\n}\n\nexport function open(cfg: ResolvedConfig): OpenResult {\n const stateDir = join(cfg.baseDir, STATE_DIR);\n mkdirSync(stateDir, { recursive: true });\n const dbPath = join(stateDir, DB_FILENAME);\n\n const db = new DatabaseSync(dbPath);\n db.exec('PRAGMA journal_mode = WAL');\n db.exec('PRAGMA busy_timeout = 5000');\n registerFunctions(db);\n\n db.exec('CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT)');\n\n // Schema-version or feature-set mismatch: reconcile only reparses changed files, so an\n // old cache can't be patched incrementally -- rebuild instead (cheap: nothing expensive lives here).\n const version = getMeta(db, 'schema_version');\n const features = getMeta(db, 'features');\n const wantFeatures = enabledFeatures(cfg).join(',');\n if ((version !== null && version !== SCHEMA_VERSION) || (features !== null && features !== wantFeatures)) {\n db.close();\n rmSync(stateDir, { recursive: true, force: true });\n return open(cfg);\n }\n\n ensureSchema(db, cfg);\n const { parsed, warnings } = reconcile(db, cfg, cfg.baseDir);\n\n return { db, cfg, dbPath, parsed, warnings };\n}\n\n// Manual reset for a doubted cache.\nexport function rebuild(cfg: ResolvedConfig): OpenResult {\n rmSync(join(cfg.baseDir, STATE_DIR), { recursive: true, force: true });\n return open(cfg);\n}\n"],"names":["DB_FILENAME","SCHEMA_VERSION","docCount","getMeta","open","rebuild","reconcile","setMeta","quoteIdent","name","split","join","registerFunctions","db","function","deterministic","varargs","field","value","undefined","needle","String","startsWith","parsed","JSON","parse","Array","isArray","some","item","includes","getColumns","rows","prepare","all","Set","map","r","ensureSchema","cfg","exec","activeFeatures","feature","schema","enabledFeatures","key","row","get","run","n","baseDir","files","listFiles","currentSet","f","relPath","existingRows","existing","Map","path","vanished","filter","has","toReparse","_mtime","mtimeMs","_size","size","length","warnings","features","seenColumns","newColumns","parsedDocs","file","parseFile","doc","fileWarnings","push","Object","keys","data","add","allColumns","insertSql","col","delBody","del","remove","insert","insertBody","values","search","title","summary","text","store","extracted","afterReconcile","err","stateDir","STATE_DIR","mkdirSync","recursive","dbPath","DatabaseSync","version","wantFeatures","close","rmSync","force"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/db.ts"],"sourcesContent":["import { mkdirSync, rmSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { DatabaseSync } from 'node:sqlite';\nimport type { Config, ResolvedConfig } from './config.ts';\nimport { featureSignature, STATE_DIR } from './config.ts';\nimport { activeFeatures } from './features/index.ts';\nimport type { ParsedDoc } from './scan.ts';\nimport { listFiles, parseFile } from './scan.ts';\n\n// Rows -> SQLite: core schema, reconcile loop, has(). Parsing lives in scan.ts;\n// everything beyond frontmatter + content lives in src/features/.\n\nexport const DB_FILENAME = 'cache.db';\n// Cache shape version, independent of the config's own `version`.\nexport const SCHEMA_VERSION = '3';\n\nexport interface OpenResult {\n db: DatabaseSync;\n cfg: ResolvedConfig;\n dbPath: string;\n parsed: number;\n warnings: string[];\n}\n\nfunction quoteIdent(name: string): string {\n return `\"${name.split('\"').join('\"\"')}\"`;\n}\n\n// has(field, value): JSON-array field -> membership, string field -> substring, NULL -> false.\nfunction registerFunctions(db: DatabaseSync): void {\n db.function('has', { deterministic: true, varargs: false }, (field: unknown, value: unknown): number => {\n if (field === null || field === undefined) return 0;\n\n const needle = String(value);\n\n if (typeof field === 'string') {\n if (field.startsWith('[')) {\n try {\n const parsed = JSON.parse(field);\n if (Array.isArray(parsed)) {\n return parsed.some((item) => String(item) === needle) ? 1 : 0;\n }\n } catch {}\n }\n return field.includes(needle) ? 1 : 0;\n }\n\n return String(field).includes(needle) ? 1 : 0;\n });\n}\n\nfunction getColumns(db: DatabaseSync): Set<string> {\n const rows = db.prepare('PRAGMA table_info(frontmatter)').all() as Array<{ name: string }>;\n return new Set(rows.map((r) => r.name));\n}\n\n// Content is a separate table (not a column on frontmatter) so `SELECT * FROM frontmatter`\n// can't dump file text into context. Features add their own tables after the core ones.\nfunction ensureSchema(db: DatabaseSync, cfg: Config): void {\n db.exec(`CREATE TABLE IF NOT EXISTS frontmatter (\"path\" TEXT PRIMARY KEY, \"_mtime\" REAL, \"_size\" INTEGER)`);\n db.exec(`CREATE VIRTUAL TABLE IF NOT EXISTS content USING fts5(title, summary, text, path UNINDEXED, tokenize = 'porter unicode61')`);\n for (const feature of activeFeatures(cfg)) feature.schema(db);\n if (getMeta(db, 'schema_version') === null) setMeta(db, 'schema_version', SCHEMA_VERSION);\n if (getMeta(db, 'features') === null) setMeta(db, 'features', featureSignature(cfg));\n}\n\nexport function getMeta(db: DatabaseSync, key: string): string | null {\n const row = db.prepare('SELECT value FROM meta WHERE key = ?').get(key) as { value: string } | undefined;\n return row ? row.value : null;\n}\n\nexport function setMeta(db: DatabaseSync, key: string, value: string | null): void {\n if (value === null) {\n db.prepare('DELETE FROM meta WHERE key = ?').run(key);\n return;\n }\n db.prepare('INSERT INTO meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value').run(key, value);\n}\n\nexport function docCount(db: DatabaseSync): number {\n const row = db.prepare('SELECT COUNT(*) AS n FROM frontmatter').get() as { n: number };\n return row.n;\n}\n\nexport function reconcile(db: DatabaseSync, cfg: Config, baseDir: string): { parsed: number; warnings: string[] } {\n const files = listFiles(cfg, baseDir);\n const currentSet = new Set(files.map((f) => f.relPath));\n\n const existingRows = db.prepare(`SELECT \"path\", \"_mtime\", \"_size\" FROM frontmatter`).all() as Array<{\n path: string;\n _mtime: number;\n _size: number;\n }>;\n const existing = new Map(existingRows.map((r) => [r.path, r]));\n const vanished = existingRows.filter((r) => !currentSet.has(r.path)).map((r) => r.path);\n\n const toReparse = files.filter((f) => {\n const row = existing.get(f.relPath);\n return !row || row._mtime !== f.mtimeMs || row._size !== f.size;\n });\n\n if (vanished.length === 0 && toReparse.length === 0) return { parsed: 0, warnings: [] };\n\n const features = activeFeatures(cfg);\n const seenColumns = getColumns(db);\n const newColumns: string[] = [];\n const parsedDocs: ParsedDoc[] = [];\n const warnings: string[] = [];\n\n for (const file of toReparse) {\n const { doc, warnings: fileWarnings } = parseFile(file, features);\n warnings.push(...fileWarnings);\n for (const key of Object.keys(doc.data)) {\n if (!seenColumns.has(key)) {\n seenColumns.add(key);\n newColumns.push(key);\n }\n }\n parsedDocs.push(doc);\n }\n\n const allColumns = [...seenColumns];\n const insertSql = `INSERT OR REPLACE INTO frontmatter (${allColumns.map(quoteIdent).join(', ')}) VALUES (${allColumns.map(() => '?').join(', ')})`;\n\n db.exec('BEGIN');\n try {\n for (const col of newColumns) db.exec(`ALTER TABLE frontmatter ADD COLUMN ${quoteIdent(col)}`);\n // FTS5 has no upsert, so delete-before-insert into `content`.\n const delBody = db.prepare(`DELETE FROM content WHERE \"path\" = ?`);\n if (vanished.length > 0) {\n const del = db.prepare(`DELETE FROM frontmatter WHERE \"path\" = ?`);\n for (const path of vanished) {\n del.run(path);\n delBody.run(path);\n for (const feature of features) feature.remove?.(db, path);\n }\n }\n if (parsedDocs.length > 0) {\n const insert = db.prepare(insertSql);\n const insertBody = db.prepare(`INSERT INTO content (title, summary, text, \"path\") VALUES (?, ?, ?, ?)`);\n for (const doc of parsedDocs) {\n const values = allColumns.map((col) => {\n if (col === 'path') return doc.relPath;\n if (col === '_mtime') return doc.mtimeMs;\n if (col === '_size') return doc.size;\n return doc.data[col] ?? null;\n });\n insert.run(...values);\n // Delete-before-insert only for docs that have rows: an FTS5 DELETE by column\n // cannot use an index and scans the whole table, so running it per doc on a\n // cold build (empty table, nothing to delete) made the crawl quadratic --\n // measured 4x time per note-count doubling at 13k/26k notes.\n if (existing.has(doc.relPath)) {\n delBody.run(doc.relPath);\n for (const feature of features) feature.remove?.(db, doc.relPath);\n }\n insertBody.run(doc.search.title, doc.search.summary, doc.search.text, doc.relPath);\n for (const feature of features) feature.store?.(db, doc.relPath, doc.extracted[feature.name]);\n }\n }\n for (const feature of features) feature.afterReconcile?.(db, files);\n db.exec('COMMIT');\n } catch (err) {\n db.exec('ROLLBACK');\n throw err;\n }\n\n return { parsed: parsedDocs.length, warnings };\n}\n\nexport function open(cfg: ResolvedConfig): OpenResult {\n const stateDir = join(cfg.baseDir, STATE_DIR);\n mkdirSync(stateDir, { recursive: true });\n const dbPath = join(stateDir, DB_FILENAME);\n\n const db = new DatabaseSync(dbPath);\n db.exec('PRAGMA journal_mode = WAL');\n // Covers a concurrent watcher's bulk reconcile: the write transaction for 500 changed\n // files measures ~5s at 26k notes, so 5s expired exactly at the boundary and queries\n // racing the watcher got SQLITE_BUSY. 30s bounds the wait at ~3x the largest measured\n // reconcile; a query that outwaits it still fails loudly.\n db.exec('PRAGMA busy_timeout = 30000');\n registerFunctions(db);\n\n db.exec('CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT)');\n\n // Schema-version or feature-set mismatch: reconcile only reparses changed files, so an\n // old cache can't be patched incrementally -- rebuild instead (cheap: nothing expensive lives here).\n const version = getMeta(db, 'schema_version');\n const features = getMeta(db, 'features');\n const wantFeatures = featureSignature(cfg);\n if ((version !== null && version !== SCHEMA_VERSION) || (features !== null && features !== wantFeatures)) {\n db.close();\n rmSync(stateDir, { recursive: true, force: true });\n return open(cfg);\n }\n\n ensureSchema(db, cfg);\n const { parsed, warnings } = reconcile(db, cfg, cfg.baseDir);\n\n return { db, cfg, dbPath, parsed, warnings };\n}\n\n// Manual reset for a doubted cache.\nexport function rebuild(cfg: ResolvedConfig): OpenResult {\n rmSync(join(cfg.baseDir, STATE_DIR), { recursive: true, force: true });\n return open(cfg);\n}\n"],"names":["DB_FILENAME","SCHEMA_VERSION","docCount","getMeta","open","rebuild","reconcile","setMeta","quoteIdent","name","split","join","registerFunctions","db","function","deterministic","varargs","field","value","undefined","needle","String","startsWith","parsed","JSON","parse","Array","isArray","some","item","includes","getColumns","rows","prepare","all","Set","map","r","ensureSchema","cfg","exec","activeFeatures","feature","schema","featureSignature","key","row","get","run","n","baseDir","files","listFiles","currentSet","f","relPath","existingRows","existing","Map","path","vanished","filter","has","toReparse","_mtime","mtimeMs","_size","size","length","warnings","features","seenColumns","newColumns","parsedDocs","file","parseFile","doc","fileWarnings","push","Object","keys","data","add","allColumns","insertSql","col","delBody","del","remove","insert","insertBody","values","search","title","summary","text","store","extracted","afterReconcile","err","stateDir","STATE_DIR","mkdirSync","recursive","dbPath","DatabaseSync","version","wantFeatures","close","rmSync","force"],"mappings":"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"}
|
package/dist/cjs/errors.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type SenseErrorCode = 'CONFIG_NOT_FOUND' | 'CONFIG_EXISTS' | 'CONFIG_VERSION_UNSUPPORTED' | 'WATCH_ACTIVE' | 'NOTE_NOT_FOUND' | 'NOTE_AMBIGUOUS';
|
|
1
|
+
export type SenseErrorCode = 'CONFIG_NOT_FOUND' | 'CONFIG_EXISTS' | 'CONFIG_INVALID' | 'CONFIG_VERSION_UNSUPPORTED' | 'WATCH_ACTIVE' | 'NOTE_NOT_FOUND' | 'NOTE_AMBIGUOUS' | 'EMBED_DISABLED' | 'EMBED_MODEL';
|
|
2
2
|
export declare class SenseError extends Error {
|
|
3
3
|
code: SenseErrorCode;
|
|
4
4
|
constructor(code: SenseErrorCode, message: string);
|