sensemaking 0.12.0 → 0.12.1
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/dist/cjs/commands.d.cts +1 -0
- package/dist/cjs/commands.d.ts +1 -0
- package/dist/cjs/commands.js +55 -43
- package/dist/cjs/commands.js.map +1 -1
- package/dist/cjs/features/embed.d.cts +1 -1
- package/dist/cjs/features/embed.d.ts +1 -1
- package/dist/cjs/features/embed.js +2 -1
- package/dist/cjs/features/embed.js.map +1 -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 +1 -0
- package/dist/cjs/output.d.ts +1 -0
- package/dist/cjs/output.js +3 -1
- package/dist/cjs/output.js.map +1 -1
- package/dist/esm/commands.d.ts +1 -0
- package/dist/esm/commands.js +57 -34
- package/dist/esm/commands.js.map +1 -1
- package/dist/esm/features/embed.d.ts +1 -1
- package/dist/esm/features/embed.js +2 -1
- package/dist/esm/features/embed.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 +1 -0
- package/dist/esm/output.js +3 -1
- package/dist/esm/output.js.map +1 -1
- package/package.json +1 -1
- package/skills/sense/SKILL.md +2 -2
package/dist/esm/output.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/output.ts"],"sourcesContent":["// rows -> table (default) | json | csv\n\nexport type Row = Record<string, unknown>;\n\n// map/peek/status/path render structures rather than a row set, so csv has nothing to mean\n// there; the commands that emit rows take RowFormat.\nexport type Format = 'table' | 'json';\nexport type RowFormat = Format | 'csv';\n\n// Warned about on stderr (keeps --format json stdout machine-readable), never truncated.\nconst OVERSIZE_BYTES = 50_000;\n\n// Flatten embedded newlines so they can't break table column alignment; JSON output is left alone.\nfunction cell(value: unknown): string {\n return String(value ?? '').replace(/\\s*\\n\\s*/g, ' ');\n}\n\n// RFC 4180, and deliberately not cell(): csv is the format for redirecting a result to a\n// file, so it keeps every character, embedded newlines included. What it cannot carry is\n// NULL vs empty string, or a value's type -- that is what json is for.\nfunction csvField(value: unknown): string {\n const text = value === null || value === undefined ? '' : String(value);\n return /[\"\\r\\n,]/.test(text) ? `\"${text.replace(/\"/g, '\"\"')}\"` : text;\n}\n\nfunction csvLine(values: unknown[]): string {\n return values.map(csvField).join(',');\n}\n\nfunction warnOversize(bytes: number, rowCount: number): void {\n if (bytes <= OVERSIZE_BYTES) return;\n console.warn(`warning: result is ${Math.round(bytes / 1000)} KB across ${rowCount} row(s); consider a LIMIT, fewer columns, snippet() instead of whole values, or --format csv redirected to a file`);\n}\n\n// Header names come from a row wherever there is one: a statement's column list keeps\n// duplicates that the row object has already collapsed, so `SELECT a AS x, b AS x` would print\n// b's value under both headers. `columns` is the fallback that labels a 0-row result, and with\n// no rows and no statement (a bounded command that found nothing) csv writes nothing at all --\n// a bare newline would read to a csv parser as one empty record.\nexport function printRows(rows: Row[], format: RowFormat, columns?: string[]): void {\n if (format === 'csv') {\n // Collapsed the same way a row object collapses them, so a statement naming a column\n // twice describes itself identically whether it matched anything or not.\n const names = rows.length > 0 ? Object.keys(rows[0]) : [...new Set(columns ?? [])];\n if (names.length === 0) return;\n const rendered = [csvLine(names), ...rows.map((row) => csvLine(names.map((name) => row[name])))].join('\\n');\n console.log(rendered);\n // Bytes, not code units -- CJK is 3 bytes per character.\n warnOversize(Buffer.byteLength(rendered), rows.length);\n return;\n }\n const rendered = renderRows(rows, format);\n console.log(rendered);\n warnOversize(Buffer.byteLength(rendered), rows.length);\n}\n\n// Streaming counterpart for the one unbounded caller, `sense sql`: rows arrive from an\n// iterator, so a large result is never held here as rows and again as a rendered string.\n// Table buffers by necessity (a column is as wide as its widest cell, which the last row can\n// change); json and csv write row by row. The writes are synchronous, so when stdout is a pipe\n// Node buffers what the reader has not taken yet -- the saving is this module's copies, not\n// the operating system's.\nexport function printRowStream(rows: Iterable<Row>, format: RowFormat, columns: string[]): void {\n const iterator = rows[Symbol.iterator]();\n // One step before any write. FTS5 parses a MATCH expression at step time rather than at\n // prepare time, so a bad search string throws here, with stdout still clean for the error.\n const first = iterator.next();\n const rest: Iterable<Row> = { [Symbol.iterator]: () => iterator };\n\n if (format === 'table') {\n printRows(first.done ? [] : [first.value, ...rest], format, columns);\n return;\n }\n\n let bytes = 0;\n let rowCount = 0;\n const write = (text: string): void => {\n // Bytes, not code units -- CJK is 3 bytes per character.\n bytes += Buffer.byteLength(text);\n process.stdout.write(text);\n };\n\n if (format === 'json') {\n // Byte-identical to JSON.stringify(rows, null, 2) + newline: each row re-indented one\n // level, comma-joined, inside the array brackets written here.\n // JSON.stringify throws on BigInt, so a row with an int64 past 2^53 (setReadBigInts) is\n // converted via the replacer: a value that fits a safe integer stays a number, a larger one\n // becomes its decimal string, since a JSON number cannot carry it losslessly.\n const MAX_SAFE = BigInt(Number.MAX_SAFE_INTEGER);\n const bigintSafe = (_key: string, value: unknown) => (typeof value === 'bigint' ? (value >= -MAX_SAFE && value <= MAX_SAFE ? Number(value) : value.toString()) : value);\n const indent = (row: Row) =>\n JSON.stringify(row, bigintSafe, 2)\n .split('\\n')\n .map((line) => ` ${line}`)\n .join('\\n');\n if (first.done) {\n write('[]\\n');\n } else {\n write('[\\n');\n write(indent(first.value));\n rowCount = 1;\n for (const row of rest) {\n write(',\\n');\n write(indent(row));\n rowCount++;\n }\n write('\\n]\\n');\n }\n } else if (first.done) {\n const names = [...new Set(columns)];\n if (names.length > 0) write(`${csvLine(names)}\\n`);\n } else {\n const names = Object.keys(first.value);\n write(`${csvLine(names)}\\n`);\n write(`${csvLine(names.map((name) => first.value[name]))}\\n`);\n rowCount = 1;\n for (const row of rest) {\n write(`${csvLine(names.map((name) => row[name]))}\\n`);\n rowCount++;\n }\n }\n warnOversize(bytes, rowCount);\n}\n\n// Tables are for reading, and a row wider than the terminal wraps unreadably: widest columns\n// give up space first, down to a floor. --format json is never truncated.\nconst MIN_COLUMN = 12;\nconst TRUNCATED = '…';\n\nfunction fitWidths(widths: number[], limit: number): number[] {\n const gaps = (widths.length - 1) * 2;\n const fitted = [...widths];\n for (;;) {\n const total = fitted.reduce((a, b) => a + b, 0) + gaps;\n if (total <= limit) return fitted;\n const widest = fitted.indexOf(Math.max(...fitted));\n if (fitted[widest] <= MIN_COLUMN) return fitted;\n fitted[widest] = Math.max(MIN_COLUMN, fitted[widest] - Math.max(1, total - limit));\n }\n}\n\nfunction renderRows(rows: Row[], format: Format, width = process.stdout.columns): string {\n if (format === 'json') return JSON.stringify(rows, null, 2);\n if (rows.length === 0) return '(0 rows)';\n\n const columns = Object.keys(rows[0]);\n const cells = rows.map((row) => columns.map((col) => cell(row[col])));\n const natural = columns.map((col, i) => Math.max(col.length, ...cells.map((row) => row[i].length)));\n const widths = width && width > MIN_COLUMN ? fitWidths(natural, width) : natural;\n\n const clip = (value: string, w: number) => (value.length <= w ? value.padEnd(w) : `${value.slice(0, w - 1)}${TRUNCATED}`);\n const formatRow = (values: string[]) =>\n values\n .map((value, i) => clip(value, widths[i]))\n .join(' ')\n .trimEnd();\n\n return [formatRow(columns), widths.map((w) => '-'.repeat(w)).join(' '), ...cells.map(formatRow)].join('\\n');\n}\n\n// Text renderers for the top-level commands; cli.ts prints what these return.\n\n// Names the config key that turns a feature back on. Only the three features-block toggles\n// reach here; `embed` has its own status line.\nexport function featureOffHint(name: string): string {\n return `features.${name}`;\n}\n\nexport function featuresLine(features: { on: string[]; off: string[] }): string {\n const off = features.off.length > 0 ? ` · off: ${features.off.map((name) => `${name} (${featureOffHint(name)})`).join(', ')}` : '';\n return `features: ${features.on.join(', ')}${off}`;\n}\n\nexport function presetsLines(presets: Array<{ name: string; files: number; embedded: number; semantic?: boolean }>): string[] {\n // \"0 embedded\" is ambiguous between a scope that declined vectors and one that has not built\n // them yet, so a semantic-off preset says which.\n return ['presets:', ...presets.map((p) => ` ${p.name}: ${p.files} file(s), ${p.embedded} embedded${p.semantic === false ? ' (semantic: false)' : ''}`)];\n}\n\nexport function renderMap(result: { docs: { count: number; bytes: number }; fields: Row[]; fieldsTotal: number; features: { on: string[]; off: string[] }; presets: Array<{ name: string; files: number; embedded: number; semantic?: boolean }>; hubs: Row[]; recent: Row[] }): string {\n const parts = [`docs: ${result.docs.count} (${Math.round(result.docs.bytes / 1024)} KB)`, featuresLine(result.features), '', ...presetsLines(result.presets), '', renderRows(result.fields, 'table')];\n if (result.fieldsTotal > result.fields.length) parts.push(`(+${result.fieldsTotal - result.fields.length} more fields)`);\n if (result.hubs.length > 0) parts.push('\\nhubs (by link rank):', renderRows(result.hubs, 'table'));\n else if (result.features.off.includes('rank')) parts.push(`\\nhubs: off (${featureOffHint('rank')})`);\n parts.push('\\nrecent:', renderRows(result.recent, 'table'));\n return parts.join('\\n');\n}\n\nexport function renderPeek(result: { path: string; tokens: number; frontmatter: Row; parseError?: string | null; sections: Row[]; outbound: string[]; backlinks: string[]; unresolved: string[]; sectionsTotal: number; outboundTotal: number; backlinksTotal: number; unresolvedTotal: number; off: string[] }): string {\n const lines = [`${result.path} (~${result.tokens} tokens)`];\n // Otherwise a refused parse is indistinguishable from a note that has no frontmatter, which\n // is the confusion the whole quarantine design exists to remove.\n if (result.parseError) lines.push(` frontmatter: did not parse, so none of it is indexed (${result.parseError})`);\n for (const [key, value] of Object.entries(result.frontmatter)) lines.push(` ${key}: ${value}`);\n if (result.sections.length > 0) {\n lines.push('', 'sections:');\n for (const s of result.sections) lines.push(` ${'#'.repeat(s.level as number)} ${s.heading} [L${s.start_line}-${s.end_line}, ~${s.tokens}t]`);\n if (result.sectionsTotal > result.sections.length) lines.push(` (+${result.sectionsTotal - result.sections.length} more sections -- sections table has all of them)`);\n } else if (result.off.includes('sections')) lines.push('', 'sections: off (features.sections)');\n if (result.off.includes('links')) {\n lines.push('', 'links: off (features.links)');\n } else {\n const linkLine = (label: string, shown: string[], total: number) => {\n if (total > 0) lines.push(`${label} (${total}): ${shown.join(', ')}${total > shown.length ? `, +${total - shown.length} more` : ''}`);\n };\n if (result.outboundTotal + result.unresolvedTotal + result.backlinksTotal > 0) lines.push('');\n linkLine('links out', result.outbound, result.outboundTotal);\n linkLine('unresolved', result.unresolved, result.unresolvedTotal);\n linkLine('backlinks', result.backlinks, result.backlinksTotal);\n }\n return lines.join('\\n');\n}\n"],"names":["OVERSIZE_BYTES","cell","value","String","replace","csvField","text","undefined","test","csvLine","values","map","join","warnOversize","bytes","rowCount","console","warn","Math","round","printRows","rows","format","columns","names","length","Object","keys","Set","rendered","row","name","log","Buffer","byteLength","renderRows","printRowStream","iterator","Symbol","first","next","rest","done","write","process","stdout","MAX_SAFE","BigInt","Number","MAX_SAFE_INTEGER","bigintSafe","_key","toString","indent","JSON","stringify","split","line","MIN_COLUMN","TRUNCATED","fitWidths","widths","limit","gaps","fitted","total","reduce","a","b","widest","indexOf","max","width","cells","col","natural","i","clip","w","padEnd","slice","formatRow","trimEnd","repeat","featureOffHint","featuresLine","features","off","on","presetsLines","presets","p","files","embedded","semantic","renderMap","result","parts","docs","count","fields","fieldsTotal","push","hubs","includes","recent","renderPeek","lines","path","tokens","parseError","key","entries","frontmatter","sections","s","level","heading","start_line","end_line","sectionsTotal","linkLine","label","shown","outboundTotal","unresolvedTotal","backlinksTotal","outbound","unresolved","backlinks"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/output.ts"],"sourcesContent":["// rows -> table (default) | json | csv\n\nexport type Row = Record<string, unknown>;\n\n// map/peek/status/path render structures rather than a row set, so csv has nothing to mean\n// there; the commands that emit rows take RowFormat.\nexport type Format = 'table' | 'json';\nexport type RowFormat = Format | 'csv';\n\n// Warned about on stderr (keeps --format json stdout machine-readable), never truncated.\nconst OVERSIZE_BYTES = 50_000;\n\n// Flatten embedded newlines so they can't break table column alignment; JSON output is left alone.\nfunction cell(value: unknown): string {\n return String(value ?? '').replace(/\\s*\\n\\s*/g, ' ');\n}\n\n// RFC 4180, and deliberately not cell(): csv is the format for redirecting a result to a\n// file, so it keeps every character, embedded newlines included. What it cannot carry is\n// NULL vs empty string, or a value's type -- that is what json is for.\nfunction csvField(value: unknown): string {\n const text = value === null || value === undefined ? '' : String(value);\n return /[\"\\r\\n,]/.test(text) ? `\"${text.replace(/\"/g, '\"\"')}\"` : text;\n}\n\nfunction csvLine(values: unknown[]): string {\n return values.map(csvField).join(',');\n}\n\nfunction warnOversize(bytes: number, rowCount: number): void {\n if (bytes <= OVERSIZE_BYTES) return;\n console.warn(`warning: result is ${Math.round(bytes / 1000)} KB across ${rowCount} row(s); consider a LIMIT, fewer columns, snippet() instead of whole values, or --format csv redirected to a file`);\n}\n\n// Header names come from a row wherever there is one: a statement's column list keeps\n// duplicates that the row object has already collapsed, so `SELECT a AS x, b AS x` would print\n// b's value under both headers. `columns` is the fallback that labels a 0-row result, and with\n// no rows and no statement (a bounded command that found nothing) csv writes nothing at all --\n// a bare newline would read to a csv parser as one empty record.\nexport function printRows(rows: Row[], format: RowFormat, columns?: string[]): void {\n if (format === 'csv') {\n // Collapsed the same way a row object collapses them, so a statement naming a column\n // twice describes itself identically whether it matched anything or not.\n const names = rows.length > 0 ? Object.keys(rows[0]) : [...new Set(columns ?? [])];\n if (names.length === 0) return;\n const rendered = [csvLine(names), ...rows.map((row) => csvLine(names.map((name) => row[name])))].join('\\n');\n console.log(rendered);\n // Bytes, not code units -- CJK is 3 bytes per character.\n warnOversize(Buffer.byteLength(rendered), rows.length);\n return;\n }\n const rendered = renderRows(rows, format);\n console.log(rendered);\n warnOversize(Buffer.byteLength(rendered), rows.length);\n}\n\n// Streaming counterpart for the one unbounded caller, `sense sql`: rows arrive from an\n// iterator, so a large result is never held here as rows and again as a rendered string.\n// Table buffers by necessity (a column is as wide as its widest cell, which the last row can\n// change); json and csv write row by row. The writes are synchronous, so when stdout is a pipe\n// Node buffers what the reader has not taken yet -- the saving is this module's copies, not\n// the operating system's.\nexport function printRowStream(rows: Iterable<Row>, format: RowFormat, columns: string[]): void {\n const iterator = rows[Symbol.iterator]();\n // One step before any write. FTS5 parses a MATCH expression at step time rather than at\n // prepare time, so a bad search string throws here, with stdout still clean for the error.\n const first = iterator.next();\n const rest: Iterable<Row> = { [Symbol.iterator]: () => iterator };\n\n if (format === 'table') {\n printRows(first.done ? [] : [first.value, ...rest], format, columns);\n return;\n }\n\n let bytes = 0;\n let rowCount = 0;\n const write = (text: string): void => {\n // Bytes, not code units -- CJK is 3 bytes per character.\n bytes += Buffer.byteLength(text);\n process.stdout.write(text);\n };\n\n if (format === 'json') {\n // Byte-identical to JSON.stringify(rows, null, 2) + newline: each row re-indented one\n // level, comma-joined, inside the array brackets written here.\n // JSON.stringify throws on BigInt, so a row with an int64 past 2^53 (setReadBigInts) is\n // converted via the replacer: a value that fits a safe integer stays a number, a larger one\n // becomes its decimal string, since a JSON number cannot carry it losslessly.\n const MAX_SAFE = BigInt(Number.MAX_SAFE_INTEGER);\n const bigintSafe = (_key: string, value: unknown) => (typeof value === 'bigint' ? (value >= -MAX_SAFE && value <= MAX_SAFE ? Number(value) : value.toString()) : value);\n const indent = (row: Row) =>\n JSON.stringify(row, bigintSafe, 2)\n .split('\\n')\n .map((line) => ` ${line}`)\n .join('\\n');\n if (first.done) {\n write('[]\\n');\n } else {\n write('[\\n');\n write(indent(first.value));\n rowCount = 1;\n for (const row of rest) {\n write(',\\n');\n write(indent(row));\n rowCount++;\n }\n write('\\n]\\n');\n }\n } else if (first.done) {\n const names = [...new Set(columns)];\n if (names.length > 0) write(`${csvLine(names)}\\n`);\n } else {\n const names = Object.keys(first.value);\n write(`${csvLine(names)}\\n`);\n write(`${csvLine(names.map((name) => first.value[name]))}\\n`);\n rowCount = 1;\n for (const row of rest) {\n write(`${csvLine(names.map((name) => row[name]))}\\n`);\n rowCount++;\n }\n }\n warnOversize(bytes, rowCount);\n}\n\n// Tables are for reading, and a row wider than the terminal wraps unreadably: widest columns\n// give up space first, down to a floor. --format json is never truncated.\nconst MIN_COLUMN = 12;\nconst TRUNCATED = '…';\n\nfunction fitWidths(widths: number[], limit: number): number[] {\n const gaps = (widths.length - 1) * 2;\n const fitted = [...widths];\n for (;;) {\n const total = fitted.reduce((a, b) => a + b, 0) + gaps;\n if (total <= limit) return fitted;\n const widest = fitted.indexOf(Math.max(...fitted));\n if (fitted[widest] <= MIN_COLUMN) return fitted;\n fitted[widest] = Math.max(MIN_COLUMN, fitted[widest] - Math.max(1, total - limit));\n }\n}\n\nfunction renderRows(rows: Row[], format: Format, width = process.stdout.columns): string {\n if (format === 'json') return JSON.stringify(rows, null, 2);\n if (rows.length === 0) return '(0 rows)';\n\n const columns = Object.keys(rows[0]);\n const cells = rows.map((row) => columns.map((col) => cell(row[col])));\n const natural = columns.map((col, i) => Math.max(col.length, ...cells.map((row) => row[i].length)));\n const widths = width && width > MIN_COLUMN ? fitWidths(natural, width) : natural;\n\n const clip = (value: string, w: number) => (value.length <= w ? value.padEnd(w) : `${value.slice(0, w - 1)}${TRUNCATED}`);\n const formatRow = (values: string[]) =>\n values\n .map((value, i) => clip(value, widths[i]))\n .join(' ')\n .trimEnd();\n\n return [formatRow(columns), widths.map((w) => '-'.repeat(w)).join(' '), ...cells.map(formatRow)].join('\\n');\n}\n\n// Text renderers for the top-level commands; cli.ts prints what these return.\n\n// Names the config key that turns a feature back on. Only the three features-block toggles\n// reach here; `embed` has its own status line.\nexport function featureOffHint(name: string): string {\n return `features.${name}`;\n}\n\nexport function featuresLine(features: { on: string[]; off: string[] }): string {\n const off = features.off.length > 0 ? ` · off: ${features.off.map((name) => `${name} (${featureOffHint(name)})`).join(', ')}` : '';\n return `features: ${features.on.join(', ')}${off}`;\n}\n\nexport function presetsLines(presets: Array<{ name: string; files: number; embedded: number; semantic?: boolean }>): string[] {\n // \"0 embedded\" is ambiguous between a scope that declined vectors and one that has not built\n // them yet, so a semantic-off preset says which.\n return ['presets:', ...presets.map((p) => ` ${p.name}: ${p.files} file(s), ${p.embedded} embedded${p.semantic === false ? ' (semantic: false)' : ''}`)];\n}\n\nexport function renderMap(result: { docs: { count: number; bytes: number }; fields: Row[]; fieldsTotal: number; features: { on: string[]; off: string[] }; presets: Array<{ name: string; files: number; embedded: number; semantic?: boolean }>; hubs: Row[]; recent: Row[]; recentCaveat: string | null }): string {\n const parts = [`docs: ${result.docs.count} (${Math.round(result.docs.bytes / 1024)} KB)`, featuresLine(result.features), '', ...presetsLines(result.presets), '', renderRows(result.fields, 'table')];\n if (result.fieldsTotal > result.fields.length) parts.push(`(+${result.fieldsTotal - result.fields.length} more fields)`);\n if (result.hubs.length > 0) parts.push('\\nhubs (by link rank):', renderRows(result.hubs, 'table'));\n else if (result.features.off.includes('rank')) parts.push(`\\nhubs: off (${featureOffHint('rank')})`);\n parts.push('\\nrecent:');\n if (result.recentCaveat) parts.push(result.recentCaveat);\n parts.push(renderRows(result.recent, 'table'));\n return parts.join('\\n');\n}\n\nexport function renderPeek(result: { path: string; tokens: number; frontmatter: Row; parseError?: string | null; sections: Row[]; outbound: string[]; backlinks: string[]; unresolved: string[]; sectionsTotal: number; outboundTotal: number; backlinksTotal: number; unresolvedTotal: number; off: string[] }): string {\n const lines = [`${result.path} (~${result.tokens} tokens)`];\n // Otherwise a refused parse is indistinguishable from a note that has no frontmatter, which\n // is the confusion the whole quarantine design exists to remove.\n if (result.parseError) lines.push(` frontmatter: did not parse, so none of it is indexed (${result.parseError})`);\n for (const [key, value] of Object.entries(result.frontmatter)) lines.push(` ${key}: ${value}`);\n if (result.sections.length > 0) {\n lines.push('', 'sections:');\n for (const s of result.sections) lines.push(` ${'#'.repeat(s.level as number)} ${s.heading} [L${s.start_line}-${s.end_line}, ~${s.tokens}t]`);\n if (result.sectionsTotal > result.sections.length) lines.push(` (+${result.sectionsTotal - result.sections.length} more sections -- sections table has all of them)`);\n } else if (result.off.includes('sections')) lines.push('', 'sections: off (features.sections)');\n if (result.off.includes('links')) {\n lines.push('', 'links: off (features.links)');\n } else {\n const linkLine = (label: string, shown: string[], total: number) => {\n if (total > 0) lines.push(`${label} (${total}): ${shown.join(', ')}${total > shown.length ? `, +${total - shown.length} more` : ''}`);\n };\n if (result.outboundTotal + result.unresolvedTotal + result.backlinksTotal > 0) lines.push('');\n linkLine('links out', result.outbound, result.outboundTotal);\n linkLine('unresolved', result.unresolved, result.unresolvedTotal);\n linkLine('backlinks', result.backlinks, result.backlinksTotal);\n }\n return lines.join('\\n');\n}\n"],"names":["OVERSIZE_BYTES","cell","value","String","replace","csvField","text","undefined","test","csvLine","values","map","join","warnOversize","bytes","rowCount","console","warn","Math","round","printRows","rows","format","columns","names","length","Object","keys","Set","rendered","row","name","log","Buffer","byteLength","renderRows","printRowStream","iterator","Symbol","first","next","rest","done","write","process","stdout","MAX_SAFE","BigInt","Number","MAX_SAFE_INTEGER","bigintSafe","_key","toString","indent","JSON","stringify","split","line","MIN_COLUMN","TRUNCATED","fitWidths","widths","limit","gaps","fitted","total","reduce","a","b","widest","indexOf","max","width","cells","col","natural","i","clip","w","padEnd","slice","formatRow","trimEnd","repeat","featureOffHint","featuresLine","features","off","on","presetsLines","presets","p","files","embedded","semantic","renderMap","result","parts","docs","count","fields","fieldsTotal","push","hubs","includes","recentCaveat","recent","renderPeek","lines","path","tokens","parseError","key","entries","frontmatter","sections","s","level","heading","start_line","end_line","sectionsTotal","linkLine","label","shown","outboundTotal","unresolvedTotal","backlinksTotal","outbound","unresolved","backlinks"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sensemaking",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"description": "Query and search your markdown notes with context-aware progressive disclosure: SQL over frontmatter, links, and text, plus semantic search and link-graph ranking. No server, no build step",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"markdown",
|
package/skills/sense/SKILL.md
CHANGED
|
@@ -5,7 +5,7 @@ description: "Query a markdown tree with the sense CLI: filter notes by frontmat
|
|
|
5
5
|
|
|
6
6
|
# sense
|
|
7
7
|
|
|
8
|
-
SQL over a markdown tree, kept fresh by a filesystem check on every query. Every file becomes rows in `frontmatter` (one column per key, plus `path`/`_mtime`/`_size`/`_rank`/`_parse_error`), `content` (FTS5: `title`, `summary`, `text`, `path`, plus machine-written `title_seg`/`summary_seg`/`text_seg` sidecars used for matching Chinese, Japanese, Thai, Khmer, Lao, and Burmese text, not for reading), `links` (`src`, `target`, `dst`; `NULL` dst = dead link), `sections` (heading outline with line ranges and token estimates), and `preset_files` (`path`, `preset`: which presets cover which files). Features add their own storage; `map` and `status` report which are on.
|
|
8
|
+
SQL over a markdown tree, kept fresh by a filesystem check on every query. Every file becomes rows in `frontmatter` (one column per key, plus `path`/`_mtime`/`_size`/`_rank`/`_parse_error`), `content` (FTS5: `title`, `summary`, `text`, `path`, plus machine-written `title_seg`/`summary_seg`/`text_seg` sidecars used for matching Chinese, Japanese, Thai, Khmer, Lao, and Burmese text, not for reading), `links` (`src`, `target`, `dst`; `NULL` dst = dead link; one row per distinct written target with alias and anchor stripped, so `[[Foo]]` and `[[Foo|alias]]` are one row while `[[Foo]]` and `[[notes/Foo]]` are two rows that can share a `dst`), `sections` (heading outline with line ranges and token estimates), and `preset_files` (`path`, `preset`: which presets cover which files). Features add their own storage; `map` and `status` report which are on.
|
|
9
9
|
|
|
10
10
|
## What each tool is for
|
|
11
11
|
|
|
@@ -14,7 +14,7 @@ Every result is a reference (path, metadata, excerpt), never file contents; pros
|
|
|
14
14
|
- A deterministic, factual answer over known fields (counts, filters, "which notes have X") is SQL: `sense sql`, a saved `{ sql }` entry, or `search --where`. Enumerates every match; same result regardless of phrasing.
|
|
15
15
|
- Locating notes about something is `search`, one text through every engine the scope has: word match (bare words AND-join, one absent word = zero lexical rows; write `a OR b OR c` for any-word), link-graph expansion, and vector similarity, fused into one ranked list. Read `via` per row: `match` rows contained your words; `vector`-only rows did not. A `vector`-only row means the search words don't appear in that note; it showed up because the model judged it semantically related. Vector rows are conceptual similarity, not typo-tolerance; false positives are expected, labeled, and bounded by `--k`. A scope searches with vectors when its preset has `semantic` on (the default) and the tree names an `embed` model; a `semantic: false` preset searches on words and links. A preset that asks for vectors when the model is not downloaded is an error naming `sense download`, not a quieter result: the same search must not answer differently before and after a download.
|
|
16
16
|
- `map` answers "what is this tree" (fields, hub notes, recent changes) when the tree is unfamiliar.
|
|
17
|
-
- `peek <path>` prices a file before you pay for it: outline with `[L143-162, ~380t]` ranges and links both ways. Every list shows its first 20 with the true total; the `sections` and `links` tables hold the rest, so a peek costs a few hundred tokens on any note.
|
|
17
|
+
- `peek <path>` prices a file before you pay for it: outline with `[L143-162, ~380t]` ranges and links both ways. Every list shows its first 20 with the true total; the `sections` and `links` tables hold the rest, so a peek costs a few hundred tokens on any note. Its link totals count distinct notes: "links out" dedupes written targets by resolved note and lists unresolved targets separately, while `COUNT(*) FROM links WHERE src = ?` counts every written target, resolved or not, so the raw count can read higher without either number being wrong.
|
|
18
18
|
- `path <a> <b>` walks the link graph for a chain connecting two notes, or reports none within the depth bound: it answers how they connect, not just that both exist.
|
|
19
19
|
- `related <note>` ranks notes near in meaning to one note that it does not already link to: the links it is missing. It reads the meaning-vectors, so it needs vectors on for the scope and scans them, costing about what a semantic `search` does, not what a `peek` does. Vectors need the model on disk; nothing fetches it implicitly, so `sense download` is a one-time step per machine. Without it `search` still answers on words and links (it prints a note saying vectors are off), while `related` reports the missing model instead of an empty list.
|
|
20
20
|
- When you know the file and need its contents, `Read` it. sense adds nothing there. On large files peek's ranges let you read just one section; small files are often cheaper whole.
|