sensemaking 0.5.0 → 0.7.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 +13 -6
- package/dist/cjs/cli.js +8 -1
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/commands/check.d.cts +3 -0
- package/dist/cjs/commands/check.d.ts +3 -0
- package/dist/cjs/commands/check.js +130 -0
- package/dist/cjs/commands/check.js.map +1 -0
- package/dist/cjs/commands/find.js +173 -10
- package/dist/cjs/commands/find.js.map +1 -1
- package/dist/cjs/commands/index.js +5 -0
- package/dist/cjs/commands/index.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 +72 -5
- 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 +32 -2
- package/dist/cjs/config.d.ts +32 -2
- package/dist/cjs/config.js +199 -11
- 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 +47 -17
- 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 +15 -0
- package/dist/cjs/features/embed.d.ts +15 -0
- package/dist/cjs/features/embed.js +844 -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 +1 -1
- package/dist/cjs/index.d.ts +1 -1
- 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 +48 -9
- package/dist/cjs/output.js.map +1 -1
- package/dist/cjs/scan.d.cts +1 -1
- package/dist/cjs/scan.d.ts +1 -1
- package/dist/cjs/scan.js +17 -10
- 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 +308 -81
- package/dist/cjs/verbs.js.map +1 -1
- package/dist/esm/cli.js +8 -1
- package/dist/esm/cli.js.map +1 -1
- package/dist/esm/commands/check.d.ts +3 -0
- package/dist/esm/commands/check.js +56 -0
- package/dist/esm/commands/check.js.map +1 -0
- package/dist/esm/commands/find.js +5 -4
- package/dist/esm/commands/find.js.map +1 -1
- package/dist/esm/commands/index.js +1 -0
- package/dist/esm/commands/index.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 +43 -5
- 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 +32 -2
- package/dist/esm/config.js +98 -9
- package/dist/esm/config.js.map +1 -1
- package/dist/esm/db.d.ts +1 -1
- package/dist/esm/db.js +20 -11
- 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 +15 -0
- package/dist/esm/features/embed.js +299 -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 +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/output.d.ts +5 -0
- package/dist/esm/output.js +40 -8
- package/dist/esm/output.js.map +1 -1
- package/dist/esm/scan.d.ts +1 -1
- package/dist/esm/scan.js +17 -10
- package/dist/esm/scan.js.map +1 -1
- package/dist/esm/verbs.d.ts +8 -2
- package/dist/esm/verbs.js +92 -18
- package/dist/esm/verbs.js.map +1 -1
- package/package.json +2 -1
- package/schema.json +35 -1
- package/skills/sense/EXAMPLES.md +16 -0
- package/skills/sense/SKILL.md +60 -20
- package/skills/sense-setup/SKILL.md +91 -0
package/dist/esm/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/cli.ts"],"sourcesContent":["import { createRequire } from 'node:module';\nimport { parseArgs } from 'node:util';\nimport { COMMANDS } from './commands/index.ts';\nimport type { Ctx } from './commands/types.ts';\nimport { loadConfig, SUPPORTED_CONFIG_VERSION } from './config.ts';\n\n// Parsing and dispatch only. Commands live in src/commands/, one file each, lazy-loaded --\n// nothing tree- or dependency-heavy may be imported at the top of this file.\n\n// Works from dist/cjs and dist/esm alike: walk up past the dist type-marker package.json.\nfunction packageVersion(): string {\n const load = createRequire(import.meta.url);\n for (const rel of ['../package.json', '../../package.json', '../../../package.json']) {\n try {\n const pkg = load(rel) as { name?: string; version?: string };\n if (pkg.name === 'sensemaking' && pkg.version) return pkg.version;\n } catch {}\n }\n return 'unknown';\n}\n\nfunction usage(name: string): string {\n return (\n `usage: ${name} <name> [params...] [--format table|json] [--config path]\\n` +\n ` ${name} query \"<sql>\" [params...]\\n` +\n ` ${name} find \"<terms>\" [--where \"<sql>\"] [--k n]\\n` +\n ` ${name} map\\n` +\n ` ${name} peek <path>\\n` +\n ` ${name} --list\\n` +\n ` ${name} init\\n` +\n ` ${name} watch [--force]\\n` +\n ` ${name} status\\n` +\n ` ${name} rebuild\\n` +\n ` ${name} --version`\n );\n}\n\n// Thrown errors -> exit 1 with the message verbatim; usage errors exit 2 directly.\nexport default async function cli(argv: string[], name: string): Promise<void> {\n let values: Record<string, string | boolean | undefined>;\n let positionals: string[];\n try {\n ({ values, positionals } = parseArgs({\n args: argv,\n options: {\n format: { type: 'string', default: 'table' },\n config: { type: 'string' },\n where: { type: 'string' },\n k: { type: 'string' },\n list: { type: 'boolean', default: false },\n force: { type: 'boolean', default: false },\n version: { type: 'boolean', default: false, short: 'v' },\n help: { type: 'boolean', default: false, short: 'h' },\n },\n allowPositionals: true,\n }));\n } catch (err) {\n console.error((err as Error).message);\n console.error(usage(name));\n process.exit(2);\n }\n\n if (values.version) {\n console.log(`v${packageVersion()}`);\n return;\n }\n if (values.help) {\n console.log(usage(name));\n return;\n }\n\n const ctx: Ctx = {\n name,\n rest: positionals.slice(1),\n format: values.format === 'json' ? 'json' : 'table',\n values: values as Ctx['values'],\n resolveConfig() {\n const cfg = loadConfig(values.config as string | undefined);\n if (cfg.migratedFrom !== undefined) {\n console.warn(`${name}: migrated ${cfg.configPath} from config version ${cfg.migratedFrom} to ${SUPPORTED_CONFIG_VERSION}`);\n }\n return cfg;\n },\n usageError(message) {\n console.error(message);\n process.exit(2);\n },\n };\n\n try {\n if (values.list) {\n for (const queryName of Object.keys(ctx.resolveConfig().queries).sort()) console.log(queryName);\n return;\n }\n\n const [first, ...params] = positionals;\n if (!first) {\n console.error(usage(name));\n process.exit(2);\n }\n\n const load = COMMANDS[first];\n if (load) await (await load()).default(ctx);\n else (await import('./commands/named.ts')).default(ctx, first, params);\n } catch (err) {\n console.error((err as Error).message);\n process.exit(1);\n }\n}\n"],"names":["createRequire","parseArgs","COMMANDS","loadConfig","SUPPORTED_CONFIG_VERSION","packageVersion","load","url","rel","pkg","name","version","usage","cli","argv","values","positionals","args","options","format","type","default","config","where","k","list","force","short","help","allowPositionals","err","console","error","message","process","exit","log","ctx","rest","slice","resolveConfig","cfg","migratedFrom","undefined","warn","configPath","usageError","queryName","Object","keys","queries","sort","first","params"],"mappings":"AAAA,SAASA,aAAa,QAAQ,cAAc;AAC5C,SAASC,SAAS,QAAQ,YAAY;AACtC,SAASC,QAAQ,QAAQ,sBAAsB;AAE/C,SAASC,UAAU,EAAEC,wBAAwB,QAAQ,cAAc;AAEnE,2FAA2F;AAC3F,6EAA6E;AAE7E,0FAA0F;AAC1F,SAASC;IACP,MAAMC,OAAON,cAAc,YAAYO,GAAG;IAC1C,KAAK,MAAMC,OAAO;QAAC;QAAmB;QAAsB;KAAwB,CAAE;QACpF,IAAI;YACF,MAAMC,MAAMH,KAAKE;YACjB,IAAIC,IAAIC,IAAI,KAAK,iBAAiBD,IAAIE,OAAO,EAAE,OAAOF,IAAIE,OAAO;QACnE,EAAE,OAAM,CAAC;IACX;IACA,OAAO;AACT;AAEA,SAASC,MAAMF,IAAY;IACzB,OACE,CAAC,OAAO,EAAEA,KAAK,2DAA2D,CAAC,GAC3E,CAAC,OAAO,EAAEA,KAAK,4BAA4B,CAAC,GAC5C,CAAC,OAAO,EAAEA,KAAK,
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/cli.ts"],"sourcesContent":["import { createRequire } from 'node:module';\nimport { parseArgs } from 'node:util';\nimport { COMMANDS } from './commands/index.ts';\nimport type { Ctx } from './commands/types.ts';\nimport { loadConfig, SUPPORTED_CONFIG_VERSION } from './config.ts';\n\n// Parsing and dispatch only. Commands live in src/commands/, one file each, lazy-loaded --\n// nothing tree- or dependency-heavy may be imported at the top of this file.\n\n// Works from dist/cjs and dist/esm alike: walk up past the dist type-marker package.json.\nfunction packageVersion(): string {\n const load = createRequire(import.meta.url);\n for (const rel of ['../package.json', '../../package.json', '../../../package.json']) {\n try {\n const pkg = load(rel) as { name?: string; version?: string };\n if (pkg.name === 'sensemaking' && pkg.version) return pkg.version;\n } catch {}\n }\n return 'unknown';\n}\n\nfunction usage(name: string): string {\n return (\n `usage: ${name} <name> [params...] [--format table|json] [--config path]\\n` +\n ` ${name} query \"<sql>\" [params...]\\n` +\n ` ${name} find \"<terms>\" [--where \"<sql>\"] [--k n] [--semantic]\\n` +\n ` ${name} map\\n` +\n ` ${name} peek <path>\\n` +\n ` ${name} --list\\n` +\n ` ${name} init\\n` +\n ` ${name} watch [--force]\\n` +\n ` ${name} status\\n` +\n ` ${name} rebuild\\n` +\n ` ${name} --version`\n );\n}\n\n// Thrown errors -> exit 1 with the message verbatim; usage errors exit 2 directly.\nexport default async function cli(argv: string[], name: string): Promise<void> {\n let values: Record<string, string | boolean | undefined>;\n let positionals: string[];\n try {\n ({ values, positionals } = parseArgs({\n args: argv,\n options: {\n format: { type: 'string', default: 'table' },\n config: { type: 'string' },\n where: { type: 'string' },\n k: { type: 'string' },\n semantic: { type: 'boolean', default: false },\n list: { type: 'boolean', default: false },\n force: { type: 'boolean', default: false },\n version: { type: 'boolean', default: false, short: 'v' },\n help: { type: 'boolean', default: false, short: 'h' },\n },\n allowPositionals: true,\n }));\n } catch (err) {\n console.error((err as Error).message);\n console.error(usage(name));\n process.exit(2);\n }\n\n if (values.version) {\n console.log(`v${packageVersion()}`);\n return;\n }\n if (values.help) {\n console.log(usage(name));\n return;\n }\n\n const ctx: Ctx = {\n name,\n rest: positionals.slice(1),\n format: values.format === 'json' ? 'json' : 'table',\n values: values as Ctx['values'],\n resolveConfig() {\n const cfg = loadConfig(values.config as string | undefined);\n if (cfg.migratedFrom !== undefined) {\n console.warn(`${name}: migrated ${cfg.configPath} from config version ${cfg.migratedFrom} to ${SUPPORTED_CONFIG_VERSION}`);\n }\n if (cfg.unknownKeys !== undefined) {\n console.warn(`${name}: ${cfg.configPath} sets ${cfg.unknownKeys.join(', ')}, which this build does not read (no effect); see schema.json for the keys it does`);\n }\n return cfg;\n },\n usageError(message) {\n console.error(message);\n process.exit(2);\n },\n };\n\n try {\n if (values.list) {\n for (const queryName of Object.keys(ctx.resolveConfig().queries).sort()) console.log(queryName);\n return;\n }\n\n const [first, ...params] = positionals;\n if (!first) {\n console.error(usage(name));\n process.exit(2);\n }\n\n const load = COMMANDS[first];\n if (load) await (await load()).default(ctx);\n else (await import('./commands/named.ts')).default(ctx, first, params);\n } catch (err) {\n console.error((err as Error).message);\n process.exit(1);\n }\n}\n"],"names":["createRequire","parseArgs","COMMANDS","loadConfig","SUPPORTED_CONFIG_VERSION","packageVersion","load","url","rel","pkg","name","version","usage","cli","argv","values","positionals","args","options","format","type","default","config","where","k","semantic","list","force","short","help","allowPositionals","err","console","error","message","process","exit","log","ctx","rest","slice","resolveConfig","cfg","migratedFrom","undefined","warn","configPath","unknownKeys","join","usageError","queryName","Object","keys","queries","sort","first","params"],"mappings":"AAAA,SAASA,aAAa,QAAQ,cAAc;AAC5C,SAASC,SAAS,QAAQ,YAAY;AACtC,SAASC,QAAQ,QAAQ,sBAAsB;AAE/C,SAASC,UAAU,EAAEC,wBAAwB,QAAQ,cAAc;AAEnE,2FAA2F;AAC3F,6EAA6E;AAE7E,0FAA0F;AAC1F,SAASC;IACP,MAAMC,OAAON,cAAc,YAAYO,GAAG;IAC1C,KAAK,MAAMC,OAAO;QAAC;QAAmB;QAAsB;KAAwB,CAAE;QACpF,IAAI;YACF,MAAMC,MAAMH,KAAKE;YACjB,IAAIC,IAAIC,IAAI,KAAK,iBAAiBD,IAAIE,OAAO,EAAE,OAAOF,IAAIE,OAAO;QACnE,EAAE,OAAM,CAAC;IACX;IACA,OAAO;AACT;AAEA,SAASC,MAAMF,IAAY;IACzB,OACE,CAAC,OAAO,EAAEA,KAAK,2DAA2D,CAAC,GAC3E,CAAC,OAAO,EAAEA,KAAK,4BAA4B,CAAC,GAC5C,CAAC,OAAO,EAAEA,KAAK,wDAAwD,CAAC,GACxE,CAAC,OAAO,EAAEA,KAAK,MAAM,CAAC,GACtB,CAAC,OAAO,EAAEA,KAAK,cAAc,CAAC,GAC9B,CAAC,OAAO,EAAEA,KAAK,SAAS,CAAC,GACzB,CAAC,OAAO,EAAEA,KAAK,OAAO,CAAC,GACvB,CAAC,OAAO,EAAEA,KAAK,kBAAkB,CAAC,GAClC,CAAC,OAAO,EAAEA,KAAK,SAAS,CAAC,GACzB,CAAC,OAAO,EAAEA,KAAK,UAAU,CAAC,GAC1B,CAAC,OAAO,EAAEA,KAAK,UAAU,CAAC;AAE9B;AAEA,mFAAmF;AACnF,eAAe,eAAeG,IAAIC,IAAc,EAAEJ,IAAY;IAC5D,IAAIK;IACJ,IAAIC;IACJ,IAAI;QACD,CAAA,EAAED,MAAM,EAAEC,WAAW,EAAE,GAAGf,UAAU;YACnCgB,MAAMH;YACNI,SAAS;gBACPC,QAAQ;oBAAEC,MAAM;oBAAUC,SAAS;gBAAQ;gBAC3CC,QAAQ;oBAAEF,MAAM;gBAAS;gBACzBG,OAAO;oBAAEH,MAAM;gBAAS;gBACxBI,GAAG;oBAAEJ,MAAM;gBAAS;gBACpBK,UAAU;oBAAEL,MAAM;oBAAWC,SAAS;gBAAM;gBAC5CK,MAAM;oBAAEN,MAAM;oBAAWC,SAAS;gBAAM;gBACxCM,OAAO;oBAAEP,MAAM;oBAAWC,SAAS;gBAAM;gBACzCV,SAAS;oBAAES,MAAM;oBAAWC,SAAS;oBAAOO,OAAO;gBAAI;gBACvDC,MAAM;oBAAET,MAAM;oBAAWC,SAAS;oBAAOO,OAAO;gBAAI;YACtD;YACAE,kBAAkB;QACpB,EAAC;IACH,EAAE,OAAOC,KAAK;QACZC,QAAQC,KAAK,CAAC,AAACF,IAAcG,OAAO;QACpCF,QAAQC,KAAK,CAACrB,MAAMF;QACpByB,QAAQC,IAAI,CAAC;IACf;IAEA,IAAIrB,OAAOJ,OAAO,EAAE;QAClBqB,QAAQK,GAAG,CAAC,CAAC,CAAC,EAAEhC,kBAAkB;QAClC;IACF;IACA,IAAIU,OAAOc,IAAI,EAAE;QACfG,QAAQK,GAAG,CAACzB,MAAMF;QAClB;IACF;IAEA,MAAM4B,MAAW;QACf5B;QACA6B,MAAMvB,YAAYwB,KAAK,CAAC;QACxBrB,QAAQJ,OAAOI,MAAM,KAAK,SAAS,SAAS;QAC5CJ,QAAQA;QACR0B;YACE,MAAMC,MAAMvC,WAAWY,OAAOO,MAAM;YACpC,IAAIoB,IAAIC,YAAY,KAAKC,WAAW;gBAClCZ,QAAQa,IAAI,CAAC,GAAGnC,KAAK,WAAW,EAAEgC,IAAII,UAAU,CAAC,qBAAqB,EAAEJ,IAAIC,YAAY,CAAC,IAAI,EAAEvC,0BAA0B;YAC3H;YACA,IAAIsC,IAAIK,WAAW,KAAKH,WAAW;gBACjCZ,QAAQa,IAAI,CAAC,GAAGnC,KAAK,EAAE,EAAEgC,IAAII,UAAU,CAAC,MAAM,EAAEJ,IAAIK,WAAW,CAACC,IAAI,CAAC,MAAM,kFAAkF,CAAC;YAChK;YACA,OAAON;QACT;QACAO,YAAWf,OAAO;YAChBF,QAAQC,KAAK,CAACC;YACdC,QAAQC,IAAI,CAAC;QACf;IACF;IAEA,IAAI;QACF,IAAIrB,OAAOW,IAAI,EAAE;YACf,KAAK,MAAMwB,aAAaC,OAAOC,IAAI,CAACd,IAAIG,aAAa,GAAGY,OAAO,EAAEC,IAAI,GAAItB,QAAQK,GAAG,CAACa;YACrF;QACF;QAEA,MAAM,CAACK,OAAO,GAAGC,OAAO,GAAGxC;QAC3B,IAAI,CAACuC,OAAO;YACVvB,QAAQC,KAAK,CAACrB,MAAMF;YACpByB,QAAQC,IAAI,CAAC;QACf;QAEA,MAAM9B,OAAOJ,QAAQ,CAACqD,MAAM;QAC5B,IAAIjD,MAAM,MAAM,AAAC,CAAA,MAAMA,MAAK,EAAGe,OAAO,CAACiB;aAClC,AAAC,CAAA,MAAM,MAAM,CAAC,sBAAqB,EAAGjB,OAAO,CAACiB,KAAKiB,OAAOC;IACjE,EAAE,OAAOzB,KAAK;QACZC,QAAQC,KAAK,CAAC,AAACF,IAAcG,OAAO;QACpCC,QAAQC,IAAI,CAAC;IACf;AACF"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { open } from '../db.js';
|
|
2
|
+
import { printRows } from '../output.js';
|
|
3
|
+
import { printWarnings } from './shared.js';
|
|
4
|
+
// A saved query that returns zero rows looks identical to a true empty result, so a broken
|
|
5
|
+
// one can sit unnoticed -- one field report had `WHERE flag = 'true'` against a numeric
|
|
6
|
+
// column reading as "nothing tagged yet" for hours. Preparing each query catches syntax and
|
|
7
|
+
// unknown-column errors without executing it; queries that take no parameters are also run
|
|
8
|
+
// for a row count. Parameterised queries are validated but not counted: inventing arguments
|
|
9
|
+
// would report a count for a query nobody ran.
|
|
10
|
+
const check = (ctx)=>{
|
|
11
|
+
var _cfg_queries;
|
|
12
|
+
const cfg = ctx.resolveConfig();
|
|
13
|
+
const { db, warnings } = open(cfg);
|
|
14
|
+
printWarnings(warnings);
|
|
15
|
+
const rows = [];
|
|
16
|
+
let failed = 0;
|
|
17
|
+
for (const [name, sql] of Object.entries((_cfg_queries = cfg.queries) !== null && _cfg_queries !== void 0 ? _cfg_queries : {})){
|
|
18
|
+
var _sql_match;
|
|
19
|
+
const params = ((_sql_match = sql.match(/\?/g)) !== null && _sql_match !== void 0 ? _sql_match : []).length;
|
|
20
|
+
try {
|
|
21
|
+
const statement = db.prepare(sql);
|
|
22
|
+
if (params > 0) {
|
|
23
|
+
rows.push({
|
|
24
|
+
query: name,
|
|
25
|
+
params,
|
|
26
|
+
rows: '—',
|
|
27
|
+
status: 'ok (not run: needs parameters)'
|
|
28
|
+
});
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const count = statement.all().length;
|
|
32
|
+
rows.push({
|
|
33
|
+
query: name,
|
|
34
|
+
params,
|
|
35
|
+
rows: count,
|
|
36
|
+
status: count === 0 ? 'ok, but returns 0 rows' : 'ok'
|
|
37
|
+
});
|
|
38
|
+
} catch (err) {
|
|
39
|
+
failed++;
|
|
40
|
+
rows.push({
|
|
41
|
+
query: name,
|
|
42
|
+
params,
|
|
43
|
+
rows: '—',
|
|
44
|
+
status: `FAILED: ${err.message}`
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
db.close();
|
|
49
|
+
if (rows.length === 0) {
|
|
50
|
+
console.log('no saved queries in config');
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
printRows(rows, ctx.format);
|
|
54
|
+
if (failed > 0) process.exit(1);
|
|
55
|
+
};
|
|
56
|
+
export default check;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/check.ts"],"sourcesContent":["import { open } from '../db.ts';\nimport type { Row } from '../output.ts';\nimport { printRows } from '../output.ts';\nimport { printWarnings } from './shared.ts';\nimport type { Command } from './types.ts';\n\n// A saved query that returns zero rows looks identical to a true empty result, so a broken\n// one can sit unnoticed -- one field report had `WHERE flag = 'true'` against a numeric\n// column reading as \"nothing tagged yet\" for hours. Preparing each query catches syntax and\n// unknown-column errors without executing it; queries that take no parameters are also run\n// for a row count. Parameterised queries are validated but not counted: inventing arguments\n// would report a count for a query nobody ran.\nconst check: Command = (ctx) => {\n const cfg = ctx.resolveConfig();\n const { db, warnings } = open(cfg);\n printWarnings(warnings);\n\n const rows: Row[] = [];\n let failed = 0;\n for (const [name, sql] of Object.entries(cfg.queries ?? {})) {\n const params = (sql.match(/\\?/g) ?? []).length;\n try {\n const statement = db.prepare(sql);\n if (params > 0) {\n rows.push({ query: name, params, rows: '—', status: 'ok (not run: needs parameters)' });\n continue;\n }\n const count = (statement.all() as unknown[]).length;\n rows.push({ query: name, params, rows: count, status: count === 0 ? 'ok, but returns 0 rows' : 'ok' });\n } catch (err) {\n failed++;\n rows.push({ query: name, params, rows: '—', status: `FAILED: ${(err as Error).message}` });\n }\n }\n\n db.close();\n if (rows.length === 0) {\n console.log('no saved queries in config');\n return;\n }\n printRows(rows, ctx.format);\n if (failed > 0) process.exit(1);\n};\nexport default check;\n"],"names":["open","printRows","printWarnings","check","ctx","cfg","resolveConfig","db","warnings","rows","failed","name","sql","Object","entries","queries","params","match","length","statement","prepare","push","query","status","count","all","err","message","close","console","log","format","process","exit"],"mappings":"AAAA,SAASA,IAAI,QAAQ,WAAW;AAEhC,SAASC,SAAS,QAAQ,eAAe;AACzC,SAASC,aAAa,QAAQ,cAAc;AAG5C,2FAA2F;AAC3F,wFAAwF;AACxF,4FAA4F;AAC5F,2FAA2F;AAC3F,4FAA4F;AAC5F,+CAA+C;AAC/C,MAAMC,QAAiB,CAACC;QAOmBC;IANzC,MAAMA,MAAMD,IAAIE,aAAa;IAC7B,MAAM,EAAEC,EAAE,EAAEC,QAAQ,EAAE,GAAGR,KAAKK;IAC9BH,cAAcM;IAEd,MAAMC,OAAc,EAAE;IACtB,IAAIC,SAAS;IACb,KAAK,MAAM,CAACC,MAAMC,IAAI,IAAIC,OAAOC,OAAO,EAACT,eAAAA,IAAIU,OAAO,cAAXV,0BAAAA,eAAe,CAAC,GAAI;YAC3CO;QAAhB,MAAMI,SAAS,EAACJ,aAAAA,IAAIK,KAAK,CAAC,oBAAVL,wBAAAA,aAAoB,EAAE,EAAEM,MAAM;QAC9C,IAAI;YACF,MAAMC,YAAYZ,GAAGa,OAAO,CAACR;YAC7B,IAAII,SAAS,GAAG;gBACdP,KAAKY,IAAI,CAAC;oBAAEC,OAAOX;oBAAMK;oBAAQP,MAAM;oBAAKc,QAAQ;gBAAiC;gBACrF;YACF;YACA,MAAMC,QAAQ,AAACL,UAAUM,GAAG,GAAiBP,MAAM;YACnDT,KAAKY,IAAI,CAAC;gBAAEC,OAAOX;gBAAMK;gBAAQP,MAAMe;gBAAOD,QAAQC,UAAU,IAAI,2BAA2B;YAAK;QACtG,EAAE,OAAOE,KAAK;YACZhB;YACAD,KAAKY,IAAI,CAAC;gBAAEC,OAAOX;gBAAMK;gBAAQP,MAAM;gBAAKc,QAAQ,CAAC,QAAQ,EAAE,AAACG,IAAcC,OAAO,EAAE;YAAC;QAC1F;IACF;IAEApB,GAAGqB,KAAK;IACR,IAAInB,KAAKS,MAAM,KAAK,GAAG;QACrBW,QAAQC,GAAG,CAAC;QACZ;IACF;IACA7B,UAAUQ,MAAML,IAAI2B,MAAM;IAC1B,IAAIrB,SAAS,GAAGsB,QAAQC,IAAI,CAAC;AAC/B;AACA,eAAe9B,MAAM"}
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { printRows } from '../output.js';
|
|
2
2
|
import { find } from '../verbs.js';
|
|
3
3
|
import { withDb } from './shared.js';
|
|
4
|
-
const findCmd = (ctx)=>{
|
|
4
|
+
const findCmd = async (ctx)=>{
|
|
5
5
|
const [terms] = ctx.rest;
|
|
6
|
-
if (!terms) ctx.usageError(`usage: ${ctx.name} find "<terms>" [--where "<sql>"] [--k n]`);
|
|
6
|
+
if (!terms) ctx.usageError(`usage: ${ctx.name} find "<terms>" [--where "<sql>"] [--k n] [--semantic]`);
|
|
7
7
|
const k = ctx.values.k === undefined ? undefined : Number.parseInt(ctx.values.k, 10);
|
|
8
8
|
if (ctx.values.k !== undefined && !Number.isInteger(k)) ctx.usageError(`--k expects an integer, got "${ctx.values.k}"`);
|
|
9
|
-
withDb(ctx, (db, cfg)=>printRows(find(db, cfg, terms, {
|
|
9
|
+
await withDb(ctx, async (db, cfg)=>printRows(await find(db, cfg, terms, {
|
|
10
10
|
k,
|
|
11
|
-
where: ctx.values.where
|
|
11
|
+
where: ctx.values.where,
|
|
12
|
+
semantic: ctx.values.semantic
|
|
12
13
|
}), ctx.format));
|
|
13
14
|
};
|
|
14
15
|
export default findCmd;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/find.ts"],"sourcesContent":["import { printRows } from '../output.ts';\nimport { find } from '../verbs.ts';\nimport { withDb } from './shared.ts';\nimport type { Command } from './types.ts';\n\nconst findCmd: Command = (ctx) => {\n const [terms] = ctx.rest;\n if (!terms) ctx.usageError(`usage: ${ctx.name} find \"<terms>\" [--where \"<sql>\"] [--k n]`);\n const k = ctx.values.k === undefined ? undefined : Number.parseInt(ctx.values.k, 10);\n if (ctx.values.k !== undefined && !Number.isInteger(k)) ctx.usageError(`--k expects an integer, got \"${ctx.values.k}\"`);\n withDb(ctx, (db, cfg) => printRows(find(db, cfg, terms, { k, where: ctx.values.where }), ctx.format));\n};\nexport default findCmd;\n"],"names":["printRows","find","withDb","findCmd","ctx","terms","rest","usageError","name","k","values","undefined","Number","parseInt","isInteger","db","cfg","where","format"],"mappings":"AAAA,SAASA,SAAS,QAAQ,eAAe;AACzC,SAASC,IAAI,QAAQ,cAAc;AACnC,SAASC,MAAM,QAAQ,cAAc;AAGrC,MAAMC,UAAmB,
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/find.ts"],"sourcesContent":["import { printRows } from '../output.ts';\nimport { find } from '../verbs.ts';\nimport { withDb } from './shared.ts';\nimport type { Command } from './types.ts';\n\nconst findCmd: Command = async (ctx) => {\n const [terms] = ctx.rest;\n if (!terms) ctx.usageError(`usage: ${ctx.name} find \"<terms>\" [--where \"<sql>\"] [--k n] [--semantic]`);\n const k = ctx.values.k === undefined ? undefined : Number.parseInt(ctx.values.k, 10);\n if (ctx.values.k !== undefined && !Number.isInteger(k)) ctx.usageError(`--k expects an integer, got \"${ctx.values.k}\"`);\n await withDb(ctx, async (db, cfg) => printRows(await find(db, cfg, terms, { k, where: ctx.values.where, semantic: ctx.values.semantic }), ctx.format));\n};\nexport default findCmd;\n"],"names":["printRows","find","withDb","findCmd","ctx","terms","rest","usageError","name","k","values","undefined","Number","parseInt","isInteger","db","cfg","where","semantic","format"],"mappings":"AAAA,SAASA,SAAS,QAAQ,eAAe;AACzC,SAASC,IAAI,QAAQ,cAAc;AACnC,SAASC,MAAM,QAAQ,cAAc;AAGrC,MAAMC,UAAmB,OAAOC;IAC9B,MAAM,CAACC,MAAM,GAAGD,IAAIE,IAAI;IACxB,IAAI,CAACD,OAAOD,IAAIG,UAAU,CAAC,CAAC,OAAO,EAAEH,IAAII,IAAI,CAAC,sDAAsD,CAAC;IACrG,MAAMC,IAAIL,IAAIM,MAAM,CAACD,CAAC,KAAKE,YAAYA,YAAYC,OAAOC,QAAQ,CAACT,IAAIM,MAAM,CAACD,CAAC,EAAE;IACjF,IAAIL,IAAIM,MAAM,CAACD,CAAC,KAAKE,aAAa,CAACC,OAAOE,SAAS,CAACL,IAAIL,IAAIG,UAAU,CAAC,CAAC,6BAA6B,EAAEH,IAAIM,MAAM,CAACD,CAAC,CAAC,CAAC,CAAC;IACtH,MAAMP,OAAOE,KAAK,OAAOW,IAAIC,MAAQhB,UAAU,MAAMC,KAAKc,IAAIC,KAAKX,OAAO;YAAEI;YAAGQ,OAAOb,IAAIM,MAAM,CAACO,KAAK;YAAEC,UAAUd,IAAIM,MAAM,CAACQ,QAAQ;QAAC,IAAId,IAAIe,MAAM;AACtJ;AACA,eAAehB,QAAQ"}
|
|
@@ -5,6 +5,7 @@ export const COMMANDS = {
|
|
|
5
5
|
init: ()=>import('./init.js'),
|
|
6
6
|
watch: ()=>import('./watch.js'),
|
|
7
7
|
status: ()=>import('./status.js'),
|
|
8
|
+
check: ()=>import('./check.js'),
|
|
8
9
|
rebuild: ()=>import('./rebuild.js'),
|
|
9
10
|
query: ()=>import('./query.js'),
|
|
10
11
|
find: ()=>import('./find.js'),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/index.ts"],"sourcesContent":["import type { Command } from './types.ts';\n\n// Lazy registry: a command's imports load only when it runs, so a heavy dependency in one\n// command never taxes the others (or --version/--help). These names are reserved: a named\n// query in config can never shadow them.\nexport const COMMANDS: Record<string, () => Promise<{ default: Command }>> = {\n init: () => import('./init.ts'),\n watch: () => import('./watch.ts'),\n status: () => import('./status.ts'),\n rebuild: () => import('./rebuild.ts'),\n query: () => import('./query.ts'),\n find: () => import('./find.ts'),\n map: () => import('./map.ts'),\n peek: () => import('./peek.ts'),\n};\n\nexport type { Command, Ctx } from './types.ts';\n"],"names":["COMMANDS","init","watch","status","rebuild","query","find","map","peek"],"mappings":"AAEA,0FAA0F;AAC1F,0FAA0F;AAC1F,yCAAyC;AACzC,OAAO,MAAMA,WAAgE;IAC3EC,MAAM,IAAM,MAAM,CAAC;IACnBC,OAAO,IAAM,MAAM,CAAC;IACpBC,QAAQ,IAAM,MAAM,CAAC;IACrBC,SAAS,IAAM,MAAM,CAAC;IACtBC,OAAO,IAAM,MAAM,CAAC;IACpBC,MAAM,IAAM,MAAM,CAAC;IACnBC,KAAK,IAAM,MAAM,CAAC;IAClBC,MAAM,IAAM,MAAM,CAAC;AACrB,EAAE"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/index.ts"],"sourcesContent":["import type { Command } from './types.ts';\n\n// Lazy registry: a command's imports load only when it runs, so a heavy dependency in one\n// command never taxes the others (or --version/--help). These names are reserved: a named\n// query in config can never shadow them.\nexport const COMMANDS: Record<string, () => Promise<{ default: Command }>> = {\n init: () => import('./init.ts'),\n watch: () => import('./watch.ts'),\n status: () => import('./status.ts'),\n check: () => import('./check.ts'),\n rebuild: () => import('./rebuild.ts'),\n query: () => import('./query.ts'),\n find: () => import('./find.ts'),\n map: () => import('./map.ts'),\n peek: () => import('./peek.ts'),\n};\n\nexport type { Command, Ctx } from './types.ts';\n"],"names":["COMMANDS","init","watch","status","check","rebuild","query","find","map","peek"],"mappings":"AAEA,0FAA0F;AAC1F,0FAA0F;AAC1F,yCAAyC;AACzC,OAAO,MAAMA,WAAgE;IAC3EC,MAAM,IAAM,MAAM,CAAC;IACnBC,OAAO,IAAM,MAAM,CAAC;IACpBC,QAAQ,IAAM,MAAM,CAAC;IACrBC,OAAO,IAAM,MAAM,CAAC;IACpBC,SAAS,IAAM,MAAM,CAAC;IACtBC,OAAO,IAAM,MAAM,CAAC;IACpBC,MAAM,IAAM,MAAM,CAAC;IACnBC,KAAK,IAAM,MAAM,CAAC;IAClBC,MAAM,IAAM,MAAM,CAAC;AACrB,EAAE"}
|
package/dist/esm/commands/map.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { renderMap } from '../output.js';
|
|
2
2
|
import { mapTree } from '../verbs.js';
|
|
3
3
|
import { withDb } from './shared.js';
|
|
4
|
-
const map = (ctx)=>{
|
|
5
|
-
withDb(ctx, (db, cfg)=>{
|
|
4
|
+
const map = (ctx)=>withDb(ctx, (db, cfg)=>{
|
|
6
5
|
const result = mapTree(db, cfg);
|
|
7
6
|
console.log(ctx.format === 'json' ? JSON.stringify(result, null, 2) : renderMap(result));
|
|
8
7
|
});
|
|
9
|
-
};
|
|
10
8
|
export default map;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/map.ts"],"sourcesContent":["import { renderMap } from '../output.ts';\nimport { mapTree } from '../verbs.ts';\nimport { withDb } from './shared.ts';\nimport type { Command } from './types.ts';\n\nconst map: Command = (ctx)
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/map.ts"],"sourcesContent":["import { renderMap } from '../output.ts';\nimport { mapTree } from '../verbs.ts';\nimport { withDb } from './shared.ts';\nimport type { Command } from './types.ts';\n\nconst map: Command = (ctx) =>\n withDb(ctx, (db, cfg) => {\n const result = mapTree(db, cfg);\n console.log(ctx.format === 'json' ? JSON.stringify(result, null, 2) : renderMap(result));\n });\nexport default map;\n"],"names":["renderMap","mapTree","withDb","map","ctx","db","cfg","result","console","log","format","JSON","stringify"],"mappings":"AAAA,SAASA,SAAS,QAAQ,eAAe;AACzC,SAASC,OAAO,QAAQ,cAAc;AACtC,SAASC,MAAM,QAAQ,cAAc;AAGrC,MAAMC,MAAe,CAACC,MACpBF,OAAOE,KAAK,CAACC,IAAIC;QACf,MAAMC,SAASN,QAAQI,IAAIC;QAC3BE,QAAQC,GAAG,CAACL,IAAIM,MAAM,KAAK,SAASC,KAAKC,SAAS,CAACL,QAAQ,MAAM,KAAKP,UAAUO;IAClF;AACF,eAAeJ,IAAI"}
|
|
@@ -4,7 +4,7 @@ import { withDb } from './shared.js';
|
|
|
4
4
|
const peekCmd = (ctx)=>{
|
|
5
5
|
const [pathArg] = ctx.rest;
|
|
6
6
|
if (!pathArg) ctx.usageError(`usage: ${ctx.name} peek <path>`);
|
|
7
|
-
withDb(ctx, (db, cfg)=>{
|
|
7
|
+
return withDb(ctx, (db, cfg)=>{
|
|
8
8
|
const result = peek(db, cfg, pathArg);
|
|
9
9
|
console.log(ctx.format === 'json' ? JSON.stringify(result, null, 2) : renderPeek(result));
|
|
10
10
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/peek.ts"],"sourcesContent":["import { renderPeek } from '../output.ts';\nimport { peek } from '../verbs.ts';\nimport { withDb } from './shared.ts';\nimport type { Command } from './types.ts';\n\nconst peekCmd: Command = (ctx) => {\n const [pathArg] = ctx.rest;\n if (!pathArg) ctx.usageError(`usage: ${ctx.name} peek <path>`);\n withDb(ctx, (db, cfg) => {\n const result = peek(db, cfg, pathArg);\n console.log(ctx.format === 'json' ? JSON.stringify(result, null, 2) : renderPeek(result));\n });\n};\nexport default peekCmd;\n"],"names":["renderPeek","peek","withDb","peekCmd","ctx","pathArg","rest","usageError","name","db","cfg","result","console","log","format","JSON","stringify"],"mappings":"AAAA,SAASA,UAAU,QAAQ,eAAe;AAC1C,SAASC,IAAI,QAAQ,cAAc;AACnC,SAASC,MAAM,QAAQ,cAAc;AAGrC,MAAMC,UAAmB,CAACC;IACxB,MAAM,CAACC,QAAQ,GAAGD,IAAIE,IAAI;IAC1B,IAAI,CAACD,SAASD,IAAIG,UAAU,CAAC,CAAC,OAAO,EAAEH,IAAII,IAAI,CAAC,YAAY,CAAC;
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/peek.ts"],"sourcesContent":["import { renderPeek } from '../output.ts';\nimport { peek } from '../verbs.ts';\nimport { withDb } from './shared.ts';\nimport type { Command } from './types.ts';\n\nconst peekCmd: Command = (ctx) => {\n const [pathArg] = ctx.rest;\n if (!pathArg) ctx.usageError(`usage: ${ctx.name} peek <path>`);\n return withDb(ctx, (db, cfg) => {\n const result = peek(db, cfg, pathArg);\n console.log(ctx.format === 'json' ? JSON.stringify(result, null, 2) : renderPeek(result));\n });\n};\nexport default peekCmd;\n"],"names":["renderPeek","peek","withDb","peekCmd","ctx","pathArg","rest","usageError","name","db","cfg","result","console","log","format","JSON","stringify"],"mappings":"AAAA,SAASA,UAAU,QAAQ,eAAe;AAC1C,SAASC,IAAI,QAAQ,cAAc;AACnC,SAASC,MAAM,QAAQ,cAAc;AAGrC,MAAMC,UAAmB,CAACC;IACxB,MAAM,CAACC,QAAQ,GAAGD,IAAIE,IAAI;IAC1B,IAAI,CAACD,SAASD,IAAIG,UAAU,CAAC,CAAC,OAAO,EAAEH,IAAII,IAAI,CAAC,YAAY,CAAC;IAC7D,OAAON,OAAOE,KAAK,CAACK,IAAIC;QACtB,MAAMC,SAASV,KAAKQ,IAAIC,KAAKL;QAC7BO,QAAQC,GAAG,CAACT,IAAIU,MAAM,KAAK,SAASC,KAAKC,SAAS,CAACL,QAAQ,MAAM,KAAKX,WAAWW;IACnF;AACF;AACA,eAAeR,QAAQ"}
|
|
@@ -2,5 +2,5 @@ import type { ResolvedConfig } from '../config.js';
|
|
|
2
2
|
import type { OpenResult } from '../db.js';
|
|
3
3
|
import type { Ctx } from './types.js';
|
|
4
4
|
export declare function printWarnings(warnings: string[]): void;
|
|
5
|
-
export declare function withDb(ctx: Ctx, fn: (db: OpenResult['db'], cfg: ResolvedConfig) => void): void
|
|
5
|
+
export declare function withDb(ctx: Ctx, fn: (db: OpenResult['db'], cfg: ResolvedConfig) => void | Promise<void>): Promise<void>;
|
|
6
6
|
export declare function runSql(cfg: ResolvedConfig, sql: string, params: string[], format: 'table' | 'json', label: string): void;
|
|
@@ -4,12 +4,12 @@ import { printRows } from '../output.js';
|
|
|
4
4
|
export function printWarnings(warnings) {
|
|
5
5
|
for (const w of warnings)console.warn(w);
|
|
6
6
|
}
|
|
7
|
-
export function withDb(ctx, fn) {
|
|
7
|
+
export async function withDb(ctx, fn) {
|
|
8
8
|
const cfg = ctx.resolveConfig();
|
|
9
9
|
const { db, warnings } = open(cfg);
|
|
10
10
|
printWarnings(warnings);
|
|
11
11
|
try {
|
|
12
|
-
fn(db, cfg);
|
|
12
|
+
await fn(db, cfg);
|
|
13
13
|
} finally{
|
|
14
14
|
db.close();
|
|
15
15
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/shared.ts"],"sourcesContent":["import type { ResolvedConfig } from '../config.ts';\nimport type { OpenResult } from '../db.ts';\nimport { open } from '../db.ts';\nimport type { Row } from '../output.ts';\nimport { printRows } from '../output.ts';\nimport type { Ctx } from './types.ts';\n\n// Shared open-query-close envelope for commands that touch the tree.\n\nexport function printWarnings(warnings: string[]): void {\n for (const w of warnings) console.warn(w);\n}\n\nexport function withDb(ctx: Ctx, fn: (db: OpenResult['db'], cfg: ResolvedConfig) => void): void {\n const cfg = ctx.resolveConfig();\n const { db, warnings } = open(cfg);\n printWarnings(warnings);\n try {\n fn(db, cfg);\n } finally {\n db.close();\n }\n}\n\n// An unbound `?` silently binds NULL, so mismatched param counts fail loudly instead.\nexport function runSql(cfg: ResolvedConfig, sql: string, params: string[], format: 'table' | 'json', label: string): void {\n const placeholderCount = (sql.match(/\\?/g) ?? []).length;\n if (params.length !== placeholderCount) {\n console.error(`${label} expects ${placeholderCount} parameter(s), got ${params.length}`);\n process.exit(2);\n }\n const { db, warnings } = open(cfg);\n printWarnings(warnings);\n printRows(db.prepare(sql).all(...params) as Row[], format);\n db.close();\n}\n"],"names":["open","printRows","printWarnings","warnings","w","console","warn","withDb","ctx","fn","cfg","resolveConfig","db","close","runSql","sql","params","format","label","placeholderCount","match","length","error","process","exit","prepare","all"],"mappings":"AAEA,SAASA,IAAI,QAAQ,WAAW;AAEhC,SAASC,SAAS,QAAQ,eAAe;AAGzC,qEAAqE;AAErE,OAAO,SAASC,cAAcC,QAAkB;IAC9C,KAAK,MAAMC,KAAKD,SAAUE,QAAQC,IAAI,CAACF;AACzC;AAEA,OAAO,
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/shared.ts"],"sourcesContent":["import type { ResolvedConfig } from '../config.ts';\nimport type { OpenResult } from '../db.ts';\nimport { open } from '../db.ts';\nimport type { Row } from '../output.ts';\nimport { printRows } from '../output.ts';\nimport type { Ctx } from './types.ts';\n\n// Shared open-query-close envelope for commands that touch the tree.\n\nexport function printWarnings(warnings: string[]): void {\n for (const w of warnings) console.warn(w);\n}\n\nexport async function withDb(ctx: Ctx, fn: (db: OpenResult['db'], cfg: ResolvedConfig) => void | Promise<void>): Promise<void> {\n const cfg = ctx.resolveConfig();\n const { db, warnings } = open(cfg);\n printWarnings(warnings);\n try {\n await fn(db, cfg);\n } finally {\n db.close();\n }\n}\n\n// An unbound `?` silently binds NULL, so mismatched param counts fail loudly instead.\nexport function runSql(cfg: ResolvedConfig, sql: string, params: string[], format: 'table' | 'json', label: string): void {\n const placeholderCount = (sql.match(/\\?/g) ?? []).length;\n if (params.length !== placeholderCount) {\n console.error(`${label} expects ${placeholderCount} parameter(s), got ${params.length}`);\n process.exit(2);\n }\n const { db, warnings } = open(cfg);\n printWarnings(warnings);\n printRows(db.prepare(sql).all(...params) as Row[], format);\n db.close();\n}\n"],"names":["open","printRows","printWarnings","warnings","w","console","warn","withDb","ctx","fn","cfg","resolveConfig","db","close","runSql","sql","params","format","label","placeholderCount","match","length","error","process","exit","prepare","all"],"mappings":"AAEA,SAASA,IAAI,QAAQ,WAAW;AAEhC,SAASC,SAAS,QAAQ,eAAe;AAGzC,qEAAqE;AAErE,OAAO,SAASC,cAAcC,QAAkB;IAC9C,KAAK,MAAMC,KAAKD,SAAUE,QAAQC,IAAI,CAACF;AACzC;AAEA,OAAO,eAAeG,OAAOC,GAAQ,EAAEC,EAAuE;IAC5G,MAAMC,MAAMF,IAAIG,aAAa;IAC7B,MAAM,EAAEC,EAAE,EAAET,QAAQ,EAAE,GAAGH,KAAKU;IAC9BR,cAAcC;IACd,IAAI;QACF,MAAMM,GAAGG,IAAIF;IACf,SAAU;QACRE,GAAGC,KAAK;IACV;AACF;AAEA,sFAAsF;AACtF,OAAO,SAASC,OAAOJ,GAAmB,EAAEK,GAAW,EAAEC,MAAgB,EAAEC,MAAwB,EAAEC,KAAa;QACtFH;IAA1B,MAAMI,mBAAmB,EAACJ,aAAAA,IAAIK,KAAK,CAAC,oBAAVL,wBAAAA,aAAoB,EAAE,EAAEM,MAAM;IACxD,IAAIL,OAAOK,MAAM,KAAKF,kBAAkB;QACtCd,QAAQiB,KAAK,CAAC,GAAGJ,MAAM,SAAS,EAAEC,iBAAiB,mBAAmB,EAAEH,OAAOK,MAAM,EAAE;QACvFE,QAAQC,IAAI,CAAC;IACf;IACA,MAAM,EAAEZ,EAAE,EAAET,QAAQ,EAAE,GAAGH,KAAKU;IAC9BR,cAAcC;IACdF,UAAUW,GAAGa,OAAO,CAACV,KAAKW,GAAG,IAAIV,SAAkBC;IACnDL,GAAGC,KAAK;AACV"}
|
|
@@ -1,13 +1,51 @@
|
|
|
1
|
+
import { embedConfig, featureStates } from '../config.js';
|
|
1
2
|
import { docCount, getMeta, open } from '../db.js';
|
|
2
3
|
import { printWarnings } from './shared.js';
|
|
4
|
+
// Vectors are computed on the first semantic query, not at reconcile, so a tree can be
|
|
5
|
+
// embed-enabled with nothing embedded yet -- reported here because a fast `rebuild` on an
|
|
6
|
+
// embed tree otherwise reads as "the feature isn't on".
|
|
7
|
+
function vectorState(db) {
|
|
8
|
+
try {
|
|
9
|
+
const row = db.prepare('SELECT COUNT(vector) AS embedded, COUNT(*) - COUNT(vector) AS pending FROM embeddings').get();
|
|
10
|
+
return row;
|
|
11
|
+
} catch {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
3
15
|
const status = (ctx)=>{
|
|
4
|
-
|
|
16
|
+
var _ref;
|
|
17
|
+
var _cfg_defaults_find, _cfg_defaults;
|
|
18
|
+
const cfg = ctx.resolveConfig();
|
|
19
|
+
const { db, dbPath, warnings } = open(cfg);
|
|
5
20
|
printWarnings(warnings);
|
|
6
|
-
|
|
7
|
-
|
|
21
|
+
const features = featureStates(cfg);
|
|
22
|
+
const e = embedConfig(cfg);
|
|
23
|
+
const vectors = e ? vectorState(db) : null;
|
|
8
24
|
const heartbeat = getMeta(db, 'watch_heartbeat');
|
|
9
|
-
|
|
10
|
-
|
|
25
|
+
const result = {
|
|
26
|
+
db: dbPath,
|
|
27
|
+
docs: docCount(db),
|
|
28
|
+
features: features.on,
|
|
29
|
+
featuresOff: features.off,
|
|
30
|
+
embed: e ? {
|
|
31
|
+
type: e.type,
|
|
32
|
+
model: e.model,
|
|
33
|
+
...vectors !== null && vectors !== void 0 ? vectors : {}
|
|
34
|
+
} : null,
|
|
35
|
+
findDefaultWhere: (_ref = (_cfg_defaults = cfg.defaults) === null || _cfg_defaults === void 0 ? void 0 : (_cfg_defaults_find = _cfg_defaults.find) === null || _cfg_defaults_find === void 0 ? void 0 : _cfg_defaults_find.where) !== null && _ref !== void 0 ? _ref : null,
|
|
36
|
+
watcherHeartbeatSecondsAgo: heartbeat ? Math.round((Date.now() - Date.parse(heartbeat)) / 1000) : null
|
|
37
|
+
};
|
|
38
|
+
if (ctx.format === 'json') {
|
|
39
|
+
console.log(JSON.stringify(result, null, 2));
|
|
40
|
+
} else {
|
|
41
|
+
console.log(`db: ${result.db}`);
|
|
42
|
+
console.log(`docs: ${result.docs}`);
|
|
43
|
+
const off = features.off.length > 0 ? ` · off: ${features.off.map((name)=>`${name} (features.${name})`).join(', ')}` : '';
|
|
44
|
+
console.log(`features: ${features.on.join(', ')}${off}`);
|
|
45
|
+
if (e) console.log(`embed: ${e.type} ${e.model}${vectors ? ` (vectors: ${vectors.embedded} embedded, ${vectors.pending} pending — embedded on first --semantic query)` : ''}`);
|
|
46
|
+
if (result.findDefaultWhere) console.log(`find default scope: ${result.findDefaultWhere} (--where replaces it)`);
|
|
47
|
+
console.log(result.watcherHeartbeatSecondsAgo === null ? 'watcher: no watcher' : `watcher: last heartbeat ${result.watcherHeartbeatSecondsAgo}s ago`);
|
|
48
|
+
}
|
|
11
49
|
db.close();
|
|
12
50
|
};
|
|
13
51
|
export default status;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/status.ts"],"sourcesContent":["import { docCount, getMeta, open } from '../db.ts';\nimport { printWarnings } from './shared.ts';\nimport type { Command } from './types.ts';\n\nconst status: Command = (ctx) => {\n const { db, dbPath, warnings } = open(
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/status.ts"],"sourcesContent":["import { embedConfig, featureStates } from '../config.ts';\nimport { docCount, getMeta, open } from '../db.ts';\nimport { printWarnings } from './shared.ts';\nimport type { Command } from './types.ts';\n\n// Vectors are computed on the first semantic query, not at reconcile, so a tree can be\n// embed-enabled with nothing embedded yet -- reported here because a fast `rebuild` on an\n// embed tree otherwise reads as \"the feature isn't on\".\nfunction vectorState(db: ReturnType<typeof open>['db']): { embedded: number; pending: number } | null {\n try {\n const row = db.prepare('SELECT COUNT(vector) AS embedded, COUNT(*) - COUNT(vector) AS pending FROM embeddings').get() as { embedded: number; pending: number };\n return row;\n } catch {\n return null;\n }\n}\n\nconst status: Command = (ctx) => {\n const cfg = ctx.resolveConfig();\n const { db, dbPath, warnings } = open(cfg);\n printWarnings(warnings);\n\n const features = featureStates(cfg);\n const e = embedConfig(cfg);\n const vectors = e ? vectorState(db) : null;\n const heartbeat = getMeta(db, 'watch_heartbeat');\n const result = {\n db: dbPath,\n docs: docCount(db),\n features: features.on,\n featuresOff: features.off,\n embed: e ? { type: e.type, model: e.model, ...(vectors ?? {}) } : null,\n findDefaultWhere: cfg.defaults?.find?.where ?? null,\n watcherHeartbeatSecondsAgo: heartbeat ? Math.round((Date.now() - Date.parse(heartbeat)) / 1000) : null,\n };\n\n if (ctx.format === 'json') {\n console.log(JSON.stringify(result, null, 2));\n } else {\n console.log(`db: ${result.db}`);\n console.log(`docs: ${result.docs}`);\n const off = features.off.length > 0 ? ` · off: ${features.off.map((name) => `${name} (features.${name})`).join(', ')}` : '';\n console.log(`features: ${features.on.join(', ')}${off}`);\n if (e) console.log(`embed: ${e.type} ${e.model}${vectors ? ` (vectors: ${vectors.embedded} embedded, ${vectors.pending} pending — embedded on first --semantic query)` : ''}`);\n if (result.findDefaultWhere) console.log(`find default scope: ${result.findDefaultWhere} (--where replaces it)`);\n console.log(result.watcherHeartbeatSecondsAgo === null ? 'watcher: no watcher' : `watcher: last heartbeat ${result.watcherHeartbeatSecondsAgo}s ago`);\n }\n db.close();\n};\nexport default status;\n"],"names":["embedConfig","featureStates","docCount","getMeta","open","printWarnings","vectorState","db","row","prepare","get","status","ctx","cfg","resolveConfig","dbPath","warnings","features","e","vectors","heartbeat","result","docs","on","featuresOff","off","embed","type","model","findDefaultWhere","defaults","find","where","watcherHeartbeatSecondsAgo","Math","round","Date","now","parse","format","console","log","JSON","stringify","length","map","name","join","embedded","pending","close"],"mappings":"AAAA,SAASA,WAAW,EAAEC,aAAa,QAAQ,eAAe;AAC1D,SAASC,QAAQ,EAAEC,OAAO,EAAEC,IAAI,QAAQ,WAAW;AACnD,SAASC,aAAa,QAAQ,cAAc;AAG5C,uFAAuF;AACvF,0FAA0F;AAC1F,wDAAwD;AACxD,SAASC,YAAYC,EAAiC;IACpD,IAAI;QACF,MAAMC,MAAMD,GAAGE,OAAO,CAAC,yFAAyFC,GAAG;QACnH,OAAOF;IACT,EAAE,OAAM;QACN,OAAO;IACT;AACF;AAEA,MAAMG,SAAkB,CAACC;;QAeHC,oBAAAA;IAdpB,MAAMA,MAAMD,IAAIE,aAAa;IAC7B,MAAM,EAAEP,EAAE,EAAEQ,MAAM,EAAEC,QAAQ,EAAE,GAAGZ,KAAKS;IACtCR,cAAcW;IAEd,MAAMC,WAAWhB,cAAcY;IAC/B,MAAMK,IAAIlB,YAAYa;IACtB,MAAMM,UAAUD,IAAIZ,YAAYC,MAAM;IACtC,MAAMa,YAAYjB,QAAQI,IAAI;IAC9B,MAAMc,SAAS;QACbd,IAAIQ;QACJO,MAAMpB,SAASK;QACfU,UAAUA,SAASM,EAAE;QACrBC,aAAaP,SAASQ,GAAG;QACzBC,OAAOR,IAAI;YAAES,MAAMT,EAAES,IAAI;YAAEC,OAAOV,EAAEU,KAAK;eAAMT,oBAAAA,qBAAAA,UAAW,CAAC,CAAhB;QAAmB,IAAI;QAClEU,gBAAgB,WAAEhB,gBAAAA,IAAIiB,QAAQ,cAAZjB,qCAAAA,qBAAAA,cAAckB,IAAI,cAAlBlB,yCAAAA,mBAAoBmB,KAAK,uCAAI;QAC/CC,4BAA4Bb,YAAYc,KAAKC,KAAK,CAAC,AAACC,CAAAA,KAAKC,GAAG,KAAKD,KAAKE,KAAK,CAAClB,UAAS,IAAK,QAAQ;IACpG;IAEA,IAAIR,IAAI2B,MAAM,KAAK,QAAQ;QACzBC,QAAQC,GAAG,CAACC,KAAKC,SAAS,CAACtB,QAAQ,MAAM;IAC3C,OAAO;QACLmB,QAAQC,GAAG,CAAC,CAAC,IAAI,EAAEpB,OAAOd,EAAE,EAAE;QAC9BiC,QAAQC,GAAG,CAAC,CAAC,MAAM,EAAEpB,OAAOC,IAAI,EAAE;QAClC,MAAMG,MAAMR,SAASQ,GAAG,CAACmB,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE3B,SAASQ,GAAG,CAACoB,GAAG,CAAC,CAACC,OAAS,GAAGA,KAAK,WAAW,EAAEA,KAAK,CAAC,CAAC,EAAEC,IAAI,CAAC,OAAO,GAAG;QACzHP,QAAQC,GAAG,CAAC,CAAC,UAAU,EAAExB,SAASM,EAAE,CAACwB,IAAI,CAAC,QAAQtB,KAAK;QACvD,IAAIP,GAAGsB,QAAQC,GAAG,CAAC,CAAC,OAAO,EAAEvB,EAAES,IAAI,CAAC,CAAC,EAAET,EAAEU,KAAK,GAAGT,UAAU,CAAC,WAAW,EAAEA,QAAQ6B,QAAQ,CAAC,WAAW,EAAE7B,QAAQ8B,OAAO,CAAC,8CAA8C,CAAC,GAAG,IAAI;QAC7K,IAAI5B,OAAOQ,gBAAgB,EAAEW,QAAQC,GAAG,CAAC,CAAC,oBAAoB,EAAEpB,OAAOQ,gBAAgB,CAAC,sBAAsB,CAAC;QAC/GW,QAAQC,GAAG,CAACpB,OAAOY,0BAA0B,KAAK,OAAO,wBAAwB,CAAC,wBAAwB,EAAEZ,OAAOY,0BAA0B,CAAC,KAAK,CAAC;IACtJ;IACA1B,GAAG2C,KAAK;AACV;AACA,eAAevC,OAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/types.ts"],"sourcesContent":["import type { ResolvedConfig } from '../config.ts';\n\n// Handed to every command; argv parsing stays in cli.ts.\nexport interface Ctx {\n name: string;\n rest: string[]; // positionals after the command word\n format: 'table' | 'json';\n values: { config?: string; where?: string; k?: string; force: boolean };\n resolveConfig(): ResolvedConfig;\n usageError(message: string): never;\n}\n\nexport type Command = (ctx: Ctx) => Promise<void> | void;\n"],"names":[],"mappings":"AAYA,WAAyD"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/types.ts"],"sourcesContent":["import type { ResolvedConfig } from '../config.ts';\n\n// Handed to every command; argv parsing stays in cli.ts.\nexport interface Ctx {\n name: string;\n rest: string[]; // positionals after the command word\n format: 'table' | 'json';\n values: { config?: string; where?: string; k?: string; force: boolean; semantic: boolean };\n resolveConfig(): ResolvedConfig;\n usageError(message: string): never;\n}\n\nexport type Command = (ctx: Ctx) => Promise<void> | void;\n"],"names":[],"mappings":"AAYA,WAAyD"}
|
package/dist/esm/config.d.ts
CHANGED
|
@@ -1,24 +1,53 @@
|
|
|
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
|
+
};
|
|
25
|
+
defaults?: {
|
|
26
|
+
find?: {
|
|
27
|
+
where?: string;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
13
30
|
queries: Record<string, string>;
|
|
14
31
|
}
|
|
15
32
|
export interface ResolvedConfig extends Config {
|
|
16
33
|
baseDir: string;
|
|
17
34
|
configPath: string | null;
|
|
35
|
+
unknownKeys?: string[];
|
|
18
36
|
migratedFrom?: number;
|
|
19
37
|
}
|
|
20
38
|
export declare function featureEnabled(cfg: Config, name: FeatureName): boolean;
|
|
21
39
|
export declare function enabledFeatures(cfg: Config): FeatureName[];
|
|
40
|
+
export declare function featureStates(cfg: Config): {
|
|
41
|
+
on: FeatureName[];
|
|
42
|
+
off: FeatureName[];
|
|
43
|
+
};
|
|
44
|
+
export declare function embedConfig(cfg: Config): {
|
|
45
|
+
model: string;
|
|
46
|
+
type: 'static' | 'api';
|
|
47
|
+
url?: string;
|
|
48
|
+
key?: string;
|
|
49
|
+
} | null;
|
|
50
|
+
export declare function featureSignature(cfg: Config): string;
|
|
22
51
|
export declare function migrateConfig(cfg: Config): {
|
|
23
52
|
cfg: Config;
|
|
24
53
|
from: number;
|
|
@@ -26,3 +55,4 @@ export declare function migrateConfig(cfg: Config): {
|
|
|
26
55
|
export declare function initConfig(dir: string): string;
|
|
27
56
|
export declare function findConfigPath(startDir: string): string | null;
|
|
28
57
|
export declare function loadConfig(explicitPath?: string): ResolvedConfig;
|
|
58
|
+
export {};
|
package/dist/esm/config.js
CHANGED
|
@@ -6,28 +6,69 @@ export const STATE_DIR = '.sense';
|
|
|
6
6
|
// Highest sense.config.json `version` this build understands. Older versions auto-migrate on load.
|
|
7
7
|
export const SUPPORTED_CONFIG_VERSION = 2;
|
|
8
8
|
// Each feature owns its tables, parse-time extraction, and reconcile step; verbs degrade when one is off.
|
|
9
|
-
|
|
9
|
+
// links/sections/rank are opt-out (absent = on); embed is opt-in (absent = off) -- most trees don't need vectors.
|
|
10
|
+
const FEATURE_NAMES = [
|
|
11
|
+
'links',
|
|
12
|
+
'sections',
|
|
13
|
+
'rank',
|
|
14
|
+
'embed'
|
|
15
|
+
];
|
|
16
|
+
const OPT_OUT_NAMES = [
|
|
10
17
|
'links',
|
|
11
18
|
'sections',
|
|
12
19
|
'rank'
|
|
13
20
|
];
|
|
14
|
-
|
|
21
|
+
export const DEFAULT_EMBED_MODEL = 'minishlab/potion-retrieval-32M';
|
|
22
|
+
// Opt-out features: absent block or key means enabled. `rank` additionally requires `links`.
|
|
23
|
+
// `embed` is the opposite: enabled only when the config says so.
|
|
15
24
|
export function featureEnabled(cfg, name) {
|
|
16
|
-
var _cfg_features;
|
|
17
|
-
|
|
25
|
+
var _cfg_features, _cfg_features1;
|
|
26
|
+
if (name === 'embed') return Boolean((_cfg_features = cfg.features) === null || _cfg_features === void 0 ? void 0 : _cfg_features.embed);
|
|
27
|
+
const enabled = ((_cfg_features1 = cfg.features) === null || _cfg_features1 === void 0 ? void 0 : _cfg_features1[name]) !== false;
|
|
18
28
|
if (name === 'rank') return enabled && featureEnabled(cfg, 'links');
|
|
19
29
|
return enabled;
|
|
20
30
|
}
|
|
21
31
|
export function enabledFeatures(cfg) {
|
|
22
32
|
return FEATURE_NAMES.filter((name)=>featureEnabled(cfg, name));
|
|
23
33
|
}
|
|
34
|
+
// Every feature with its current state; verbs surface this so "off" and "empty" stay
|
|
35
|
+
// distinguishable in output.
|
|
36
|
+
export function featureStates(cfg) {
|
|
37
|
+
return {
|
|
38
|
+
on: FEATURE_NAMES.filter((name)=>featureEnabled(cfg, name)),
|
|
39
|
+
off: FEATURE_NAMES.filter((name)=>!featureEnabled(cfg, name))
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
// Resolved embed settings, or null when the feature is off.
|
|
43
|
+
export function embedConfig(cfg) {
|
|
44
|
+
var _o_model, _o_type;
|
|
45
|
+
var _cfg_features;
|
|
46
|
+
const e = (_cfg_features = cfg.features) === null || _cfg_features === void 0 ? void 0 : _cfg_features.embed;
|
|
47
|
+
if (!e) return null;
|
|
48
|
+
const o = e === true ? {} : e;
|
|
49
|
+
return {
|
|
50
|
+
model: (_o_model = o.model) !== null && _o_model !== void 0 ? _o_model : DEFAULT_EMBED_MODEL,
|
|
51
|
+
type: (_o_type = o.type) !== null && _o_type !== void 0 ? _o_type : 'static',
|
|
52
|
+
url: o.url,
|
|
53
|
+
key: o.key
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
// Cache-key string: embed carries its type + model so a model change rebuilds like a toggle.
|
|
57
|
+
export function featureSignature(cfg) {
|
|
58
|
+
return enabledFeatures(cfg).map((name)=>{
|
|
59
|
+
if (name !== 'embed') return name;
|
|
60
|
+
const e = embedConfig(cfg);
|
|
61
|
+
return `embed:${e === null || e === void 0 ? void 0 : e.type}:${e === null || e === void 0 ? void 0 : e.model}`;
|
|
62
|
+
}).join(',');
|
|
63
|
+
}
|
|
24
64
|
// Pure per-version steps; loadConfig chains them from the file's version up to SUPPORTED_CONFIG_VERSION.
|
|
25
65
|
const MIGRATIONS = {
|
|
26
|
-
// v1 -> v2: features block introduced,
|
|
66
|
+
// v1 -> v2: features block introduced, opt-out features enabled (matches the old implicit
|
|
67
|
+
// behavior of `links` etc. not existing). embed stays absent -- opt-in.
|
|
27
68
|
1: (cfg)=>({
|
|
28
69
|
...cfg,
|
|
29
70
|
version: 2,
|
|
30
|
-
features: Object.fromEntries(
|
|
71
|
+
features: Object.fromEntries(OPT_OUT_NAMES.map((name)=>[
|
|
31
72
|
name,
|
|
32
73
|
true
|
|
33
74
|
]))
|
|
@@ -56,7 +97,7 @@ function starterConfig() {
|
|
|
56
97
|
'**/*.md'
|
|
57
98
|
]
|
|
58
99
|
},
|
|
59
|
-
features: Object.fromEntries(
|
|
100
|
+
features: Object.fromEntries(OPT_OUT_NAMES.map((name)=>[
|
|
60
101
|
name,
|
|
61
102
|
true
|
|
62
103
|
])),
|
|
@@ -85,7 +126,36 @@ export function findConfigPath(startDir) {
|
|
|
85
126
|
// Shape check for hand-edited files: a typo'd config fails with a named error, not a
|
|
86
127
|
// TypeError from whatever code touched the missing field first. `queries` is optional
|
|
87
128
|
// on disk (absent = none); `scan.include` has no usable default.
|
|
129
|
+
// Keys each block actually reads. A key outside these sets is accepted and ignored, which
|
|
130
|
+
// looks identical to working -- one field report set `scan.exclude`, saw no change in doc
|
|
131
|
+
// count and no warning, and reasonably concluded the filter had applied. Reported as a
|
|
132
|
+
// warning rather than an error so a config carrying an unknown key still runs.
|
|
133
|
+
const KNOWN_KEYS = new Set([
|
|
134
|
+
'$schema',
|
|
135
|
+
'version',
|
|
136
|
+
'scan',
|
|
137
|
+
'features',
|
|
138
|
+
'defaults',
|
|
139
|
+
'queries'
|
|
140
|
+
]);
|
|
141
|
+
const KNOWN_SCAN_KEYS = new Set([
|
|
142
|
+
'include'
|
|
143
|
+
]);
|
|
144
|
+
function unknownConfigKeys(cfg) {
|
|
145
|
+
const unknown = Object.keys(cfg).filter((k)=>!KNOWN_KEYS.has(k)).map((k)=>k);
|
|
146
|
+
const scan = cfg.scan;
|
|
147
|
+
if (scan && typeof scan === 'object' && !Array.isArray(scan)) {
|
|
148
|
+
unknown.push(...Object.keys(scan).filter((k)=>!KNOWN_SCAN_KEYS.has(k)).map((k)=>`scan.${k}`));
|
|
149
|
+
}
|
|
150
|
+
const features = cfg.features;
|
|
151
|
+
if (features && typeof features === 'object' && !Array.isArray(features)) {
|
|
152
|
+
unknown.push(...Object.keys(features).filter((k)=>!FEATURE_NAMES.includes(k)).map((k)=>`features.${k}`));
|
|
153
|
+
}
|
|
154
|
+
return unknown;
|
|
155
|
+
}
|
|
88
156
|
function validateConfig(parsed, configPath) {
|
|
157
|
+
var _cfg_features;
|
|
158
|
+
var _defaults_find;
|
|
89
159
|
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
90
160
|
throw new SenseError('CONFIG_INVALID', `${configPath}: config must be a JSON object`);
|
|
91
161
|
}
|
|
@@ -98,9 +168,26 @@ function validateConfig(parsed, configPath) {
|
|
|
98
168
|
if (typeof cfg.queries !== 'object' || cfg.queries === null || Array.isArray(cfg.queries) || !Object.values(cfg.queries).every((q)=>typeof q === 'string')) {
|
|
99
169
|
throw new SenseError('CONFIG_INVALID', `${configPath}: queries must be an object of name -> SQL string`);
|
|
100
170
|
}
|
|
101
|
-
|
|
171
|
+
const defaults = cfg.defaults;
|
|
172
|
+
if (defaults !== undefined && (typeof defaults !== 'object' || defaults === null || Array.isArray(defaults) || defaults.find !== undefined && typeof ((_defaults_find = defaults.find) === null || _defaults_find === void 0 ? void 0 : _defaults_find.where) !== 'string')) {
|
|
173
|
+
throw new SenseError('CONFIG_INVALID', `${configPath}: defaults.find.where must be a SQL condition string`);
|
|
174
|
+
}
|
|
175
|
+
if (cfg.features !== undefined && (typeof cfg.features !== 'object' || cfg.features === null || Array.isArray(cfg.features))) {
|
|
102
176
|
throw new SenseError('CONFIG_INVALID', `${configPath}: features must be an object of name -> boolean`);
|
|
103
177
|
}
|
|
178
|
+
for (const [name, value] of Object.entries((_cfg_features = cfg.features) !== null && _cfg_features !== void 0 ? _cfg_features : {})){
|
|
179
|
+
if (typeof value === 'boolean') continue;
|
|
180
|
+
// embed alone takes an object form: { model?, type?: static|api, url?, key? }.
|
|
181
|
+
const embed = value;
|
|
182
|
+
const shapeOk = name === 'embed' && typeof value === 'object' && value !== null && !Array.isArray(value) && [
|
|
183
|
+
'model',
|
|
184
|
+
'url',
|
|
185
|
+
'key'
|
|
186
|
+
].every((f)=>embed[f] === undefined || typeof embed[f] === 'string') && (embed.type === undefined || embed.type === 'static' || embed.type === 'api');
|
|
187
|
+
if (!shapeOk) {
|
|
188
|
+
throw new SenseError('CONFIG_INVALID', `${configPath}: features.${name} must be a boolean${name === 'embed' ? ' or { model?, type?: "static"|"api", url?, key? }' : ''}`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
104
191
|
return cfg;
|
|
105
192
|
}
|
|
106
193
|
export function loadConfig(explicitPath) {
|
|
@@ -131,10 +218,12 @@ export function loadConfig(explicitPath) {
|
|
|
131
218
|
migratedFrom = result.from;
|
|
132
219
|
writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}\n`);
|
|
133
220
|
}
|
|
221
|
+
const unknownKeys = unknownConfigKeys(cfg);
|
|
134
222
|
return {
|
|
135
223
|
...cfg,
|
|
136
224
|
baseDir: dirname(configPath),
|
|
137
225
|
configPath,
|
|
138
|
-
migratedFrom
|
|
226
|
+
migratedFrom,
|
|
227
|
+
unknownKeys: unknownKeys.length > 0 ? unknownKeys : undefined
|
|
139
228
|
};
|
|
140
229
|
}
|