@williamthorsen/kb 0.5.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 +4 -6
- package/bin/kb.js +2 -0
- package/dist/esm/check/enumerate.js +3 -4
- package/dist/esm/cli/commands/check.js +2 -2
- package/dist/esm/cli/commands/create.js +2 -2
- package/dist/esm/cli/commands/set-default.js +3 -3
- package/dist/esm/cli/commands/taxonomy.js +2 -2
- package/dist/esm/cli/index.js +2 -2
- package/dist/esm/cli/run.js +2 -1
- package/dist/esm/cli/select-kb-prompt.js +1 -1
- package/dist/esm/cli/targeting/resolve-changed-paths.js +2 -1
- package/dist/esm/config/kb-loader-error.d.ts +1 -1
- package/dist/esm/config/kb-loader-error.js +2 -2
- package/dist/esm/config/load-config.js +2 -2
- package/dist/esm/discovery/load-registry.js +7 -6
- package/dist/esm/frontmatter/parse-note.js +1 -1
- package/dist/esm/note-io/write-note.js +5 -1
- package/dist/esm/records/event.js +2 -1
- package/dist/esm/tags/load-aliases.js +4 -4
- package/dist/esm/taxonomy/load-taxonomy.js +2 -2
- package/dist/esm/taxonomy/write-taxonomy.js +7 -2
- package/dist/esm/vault-integrity/check-vault-integrity.js +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -5,15 +5,13 @@ Provides knowledge-base discovery, registry loading, frontmatter parsing and wri
|
|
|
5
5
|
It underpins the knowledge-base skills — among them `kb-retrieve` (assertion recall) and `kb-retrieve-events` (event recall), `kb-add`, `kb-curate`, `capture-event`, and `kb-update-events` — and the planned `@williamthorsen/kb-mcp` server.
|
|
6
6
|
|
|
7
7
|
<!-- section:release-notes -->
|
|
8
|
-
## Release notes — v0.
|
|
8
|
+
## Release notes — v0.6.0 (2026-08-13)
|
|
9
9
|
|
|
10
10
|
### 🎉 Features
|
|
11
11
|
|
|
12
|
-
-
|
|
12
|
+
- Attach causes to kb's loader errors and retire its lint deferral (#1272)
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
Also fixes an issue where creating a knowledge base under an empty name could leave a stray registry entry. Across the CLI, a flag given an empty value is now refused.
|
|
14
|
+
Errors `kb` raises on malformed input now carry the underlying parse error as their cause. Callers constructing one of these errors themselves can attach a cause. When `kb` reports a thrown error that carries no message, the diagnostic now names the error's class instead of trailing off empty.
|
|
17
15
|
<!-- /section:release-notes -->
|
|
18
16
|
|
|
19
17
|
## Exports
|
|
@@ -284,7 +282,7 @@ Without `--merge`, a store that already declares a taxonomy is left untouched an
|
|
|
284
282
|
|
|
285
283
|
## Error and exception model
|
|
286
284
|
|
|
287
|
-
The checks **return** findings; they never throw. Loaders (`loadKbConfig`, `loadAliases`, `loadTaxonomy`) **throw** a typed `KbLoaderError` on structural defects or malformed YAML, with the offending file path named in the message. `KbLoaderError` (exported from `@williamthorsen/kb/config`) carries a `kind: 'KbLoaderError'` discriminant — and an `isKbLoaderError` type guard — so a caller can distinguish a recoverable config or alias defect from any other throw. `loadKbRegistry` throws a plain `Error` on its own structural defects. I/O errors other than a missing optional file propagate.
|
|
285
|
+
The checks **return** findings; they never throw. Loaders (`loadKbConfig`, `loadAliases`, `loadTaxonomy`) **throw** a typed `KbLoaderError` on structural defects or malformed YAML, with the offending file path named in the message. `KbLoaderError` (exported from `@williamthorsen/kb/config`) carries a `kind: 'KbLoaderError'` discriminant — and an `isKbLoaderError` type guard — so a caller can distinguish a recoverable config or alias defect from any other throw. An underlying failure, such as a YAML parse error, is attached as the thrown error's `cause`. `loadKbRegistry` throws a plain `Error` on its own structural defects. I/O errors other than a missing optional file propagate.
|
|
288
286
|
|
|
289
287
|
## MCP wrappability
|
|
290
288
|
|
package/bin/kb.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// Imports only node builtins: a top-level import resolves before the gate below runs, so an
|
|
4
|
+
// unresolvable dependency would replace this file's build-first message with ERR_MODULE_NOT_FOUND.
|
|
3
5
|
import { existsSync } from 'node:fs';
|
|
4
6
|
|
|
5
7
|
// Thin wrapper so pnpm can symlink the bin at install time, before `dist/` exists.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readdir, readFile } from 'node:fs/promises';
|
|
2
2
|
import { join, relative, sep } from 'node:path';
|
|
3
3
|
import process from 'node:process';
|
|
4
|
+
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
4
5
|
import { createNoteScopeMatcher } from "../config/note-scope.js";
|
|
5
6
|
import { readNoteContent } from "../note-io/read-note.js";
|
|
6
7
|
import { isGlobSegment } from "./glob-segments.js";
|
|
@@ -26,8 +27,7 @@ export async function enumerateNotes(input) {
|
|
|
26
27
|
});
|
|
27
28
|
}
|
|
28
29
|
catch (error) {
|
|
29
|
-
|
|
30
|
-
process.stderr.write(`kb: warning: could not read note ${path}; skipping: ${message}\n`);
|
|
30
|
+
process.stderr.write(`kb: warning: could not read note ${path}; skipping: ${describeError(error)}\n`);
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
return notes;
|
|
@@ -58,8 +58,7 @@ async function walk(input) {
|
|
|
58
58
|
entries = await readdir(dir, { withFileTypes: true });
|
|
59
59
|
}
|
|
60
60
|
catch (error) {
|
|
61
|
-
|
|
62
|
-
process.stderr.write(`kb: warning: could not read directory ${dir}; skipping: ${message}\n`);
|
|
61
|
+
process.stderr.write(`kb: warning: could not read directory ${dir}; skipping: ${describeError(error)}\n`);
|
|
63
62
|
return;
|
|
64
63
|
}
|
|
65
64
|
const atRoot = dir === root;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
1
2
|
import { check } from "../../check/check.js";
|
|
2
3
|
import { isKbLoaderError } from "../../config/kb-loader-error.js";
|
|
3
4
|
import { formatHuman, formatJson, summarize } from "../format.js";
|
|
@@ -119,8 +120,7 @@ export function parseCheckArgs(argv) {
|
|
|
119
120
|
return { kb, json, help, patterns, vs };
|
|
120
121
|
}
|
|
121
122
|
function buildUsageError(error) {
|
|
122
|
-
|
|
123
|
-
return { exitCode: 2, stdout: '', stderr: `kb check: ${message}\n${CHECK_HELP}` };
|
|
123
|
+
return { exitCode: 2, stdout: '', stderr: `kb check: ${describeError(error)}\n${CHECK_HELP}` };
|
|
124
124
|
}
|
|
125
125
|
async function resolveSelection(input) {
|
|
126
126
|
const { options, store, result } = input;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { homedir } from 'node:os';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
+
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
3
4
|
import { create } from "../../create/create.js";
|
|
4
5
|
import { takeInlineValue, takeValue } from "../parse-flag-value.js";
|
|
5
6
|
import { runSetDefault } from "./set-default.js";
|
|
@@ -109,8 +110,7 @@ export function parseCreateArgs(argv) {
|
|
|
109
110
|
}
|
|
110
111
|
const UNSET_DEFAULT_HINT = 'Multiple knowledge bases are registered and no default is set. Run `kb set-default` to choose one.\n';
|
|
111
112
|
function buildUsageError(error) {
|
|
112
|
-
|
|
113
|
-
return { exitCode: 2, stdout: '', stderr: `kb create: ${message}\n${CREATE_HELP}` };
|
|
113
|
+
return { exitCode: 2, stdout: '', stderr: `kb create: ${describeError(error)}\n${CREATE_HELP}` };
|
|
114
114
|
}
|
|
115
115
|
function formatCreated(created, registryPath) {
|
|
116
116
|
const lines = [`Created knowledge base "${created.name}" at ${created.storePath}`];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { homedir } from 'node:os';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
+
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
3
4
|
import { tryLoadKbRegistry } from "../../discovery/load-registry.js";
|
|
4
5
|
import { clearDefaultKb, setDefaultKb } from "../../discovery/set-default-kb.js";
|
|
5
6
|
export const SET_DEFAULT_HELP = `Usage: kb set-default [name] [options]
|
|
@@ -38,11 +39,11 @@ export async function runSetDefault(input) {
|
|
|
38
39
|
if (error !== undefined) {
|
|
39
40
|
return { exitCode: 2, stdout: '', stderr: `kb set-default: ${error}\n` };
|
|
40
41
|
}
|
|
41
|
-
const entries = config.entries;
|
|
42
42
|
if (options.none) {
|
|
43
43
|
await clearDefaultKb({ registryPath });
|
|
44
44
|
return { exitCode: 0, stdout: 'The default knowledge base has been cleared.\n', stderr: '' };
|
|
45
45
|
}
|
|
46
|
+
const entries = config.entries;
|
|
46
47
|
if (options.name !== null) {
|
|
47
48
|
if (entries.length === 0) {
|
|
48
49
|
return { exitCode: 2, stdout: '', stderr: buildNoStoresMessage() };
|
|
@@ -118,6 +119,5 @@ function buildSetConfirmation(name) {
|
|
|
118
119
|
return `Default knowledge base has been set to "${name}".\n`;
|
|
119
120
|
}
|
|
120
121
|
function buildUsageError(error) {
|
|
121
|
-
|
|
122
|
-
return { exitCode: 2, stdout: '', stderr: `kb set-default: ${message}\n${SET_DEFAULT_HELP}` };
|
|
122
|
+
return { exitCode: 2, stdout: '', stderr: `kb set-default: ${describeError(error)}\n${SET_DEFAULT_HELP}` };
|
|
123
123
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
1
2
|
import { enumerateNotes } from "../../check/enumerate.js";
|
|
2
3
|
import { isKbLoaderError } from "../../config/kb-loader-error.js";
|
|
3
4
|
import { loadKbConfig } from "../../config/load-config.js";
|
|
@@ -34,8 +35,7 @@ export async function runTaxonomy(input) {
|
|
|
34
35
|
options = parseTaxonomyArgs(input.argv);
|
|
35
36
|
}
|
|
36
37
|
catch (error) {
|
|
37
|
-
|
|
38
|
-
return { exitCode: 2, stdout: '', stderr: `kb taxonomy: ${message}\n${TAXONOMY_HELP}` };
|
|
38
|
+
return { exitCode: 2, stdout: '', stderr: `kb taxonomy: ${describeError(error)}\n${TAXONOMY_HELP}` };
|
|
39
39
|
}
|
|
40
40
|
if (options.help) {
|
|
41
41
|
return { exitCode: 0, stdout: TAXONOMY_HELP, stderr: '' };
|
package/dist/esm/cli/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import process from 'node:process';
|
|
2
|
+
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
2
3
|
import { run } from "./run.js";
|
|
3
4
|
import { readlineSelectKbPrompt } from "./select-kb-prompt.js";
|
|
4
5
|
async function main() {
|
|
@@ -12,8 +13,7 @@ async function main() {
|
|
|
12
13
|
});
|
|
13
14
|
}
|
|
14
15
|
catch (error) {
|
|
15
|
-
|
|
16
|
-
process.stderr.write(`kb: unexpected error: ${message}\n`);
|
|
16
|
+
process.stderr.write(`kb: unexpected error: ${describeError(error)}\n`);
|
|
17
17
|
process.exit(2);
|
|
18
18
|
}
|
|
19
19
|
if (output.stdout !== '')
|
package/dist/esm/cli/run.js
CHANGED
|
@@ -12,9 +12,10 @@ Commands:
|
|
|
12
12
|
|
|
13
13
|
Run "kb <command> --help" for command options.
|
|
14
14
|
`;
|
|
15
|
+
const HELP_COMMANDS = new Set([undefined, '--help', '-h']);
|
|
15
16
|
export async function run(input) {
|
|
16
17
|
const [command, ...rest] = input.argv;
|
|
17
|
-
if (command
|
|
18
|
+
if (HELP_COMMANDS.has(command)) {
|
|
18
19
|
return { exitCode: 0, stdout: HELP, stderr: '' };
|
|
19
20
|
}
|
|
20
21
|
if (command === 'check') {
|
|
@@ -14,7 +14,7 @@ export function parseSelection(answer, kbCount) {
|
|
|
14
14
|
if (answer === '')
|
|
15
15
|
return { kind: 'cancel' };
|
|
16
16
|
const choice = Number(answer);
|
|
17
|
-
if (!Number.
|
|
17
|
+
if (!Number.isSafeInteger(choice))
|
|
18
18
|
return null;
|
|
19
19
|
if (choice >= 1 && choice <= kbCount)
|
|
20
20
|
return { kind: 'kb', index: choice - 1 };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { execFileSync } from 'node:child_process';
|
|
2
2
|
import { realpathSync } from 'node:fs';
|
|
3
3
|
import { join, relative, sep } from 'node:path';
|
|
4
|
+
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
4
5
|
import { isRecord } from "../../type-guards.js";
|
|
5
6
|
export function resolveChangedPaths(input) {
|
|
6
7
|
const { storeRoot, ref } = input;
|
|
@@ -37,7 +38,7 @@ function extractGitErrorMessage(error) {
|
|
|
37
38
|
if (isRecord(error) && typeof error.stderr === 'string' && error.stderr.trim() !== '') {
|
|
38
39
|
return error.stderr.trim();
|
|
39
40
|
}
|
|
40
|
-
return
|
|
41
|
+
return describeError(error);
|
|
41
42
|
}
|
|
42
43
|
function tryGit(storeRoot, args) {
|
|
43
44
|
try {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
+
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
3
4
|
import { parse } from 'yaml';
|
|
4
5
|
import { CONFIG_FILE } from "../layout/index.js";
|
|
5
6
|
import { isEnoent } from "../type-guards.js";
|
|
@@ -22,8 +23,7 @@ export async function loadKbConfig(input) {
|
|
|
22
23
|
parsed = parse(text);
|
|
23
24
|
}
|
|
24
25
|
catch (error) {
|
|
25
|
-
|
|
26
|
-
throw new KbLoaderError(`${path}: malformed YAML — ${message}`);
|
|
26
|
+
throw new KbLoaderError(`${path}: malformed YAML: ${describeError(error)}`, { cause: error });
|
|
27
27
|
}
|
|
28
28
|
const result = configFileShape.safeParse(parsed ?? {});
|
|
29
29
|
if (!result.success) {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
3
|
import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
4
|
+
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
5
|
+
import { chainError } from '@williamthorsen/toolbelt.errors/candidate';
|
|
4
6
|
import { parse } from 'yaml';
|
|
5
7
|
import { isEnoent } from "../type-guards.js";
|
|
6
8
|
import { kbRegistryFileSchema } from "./kb-registry-schema.js";
|
|
@@ -36,8 +38,7 @@ export async function tryLoadKbRegistry(input = {}) {
|
|
|
36
38
|
return { config: await loadKbRegistry(input) };
|
|
37
39
|
}
|
|
38
40
|
catch (error) {
|
|
39
|
-
|
|
40
|
-
return { config: { entries: [], sources: {} }, error: message };
|
|
41
|
+
return { config: { entries: [], sources: {} }, error: describeError(error) };
|
|
41
42
|
}
|
|
42
43
|
}
|
|
43
44
|
function expandTilde(value, home) {
|
|
@@ -65,8 +66,7 @@ async function loadRegistryFile(path, source, home) {
|
|
|
65
66
|
parsed = parse(text);
|
|
66
67
|
}
|
|
67
68
|
catch (error) {
|
|
68
|
-
|
|
69
|
-
throw new Error(`${path}: malformed YAML — ${message}`);
|
|
69
|
+
throw chainError(`${path}: malformed YAML`, error);
|
|
70
70
|
}
|
|
71
71
|
if (parsed === null || parsed === undefined) {
|
|
72
72
|
return { entries: [] };
|
|
@@ -77,7 +77,8 @@ async function loadRegistryFile(path, source, home) {
|
|
|
77
77
|
}
|
|
78
78
|
const configDir = dirname(path);
|
|
79
79
|
const entries = [];
|
|
80
|
-
|
|
80
|
+
const fileEntries = Object.entries(result.data.kbs ?? {});
|
|
81
|
+
for (const [name, fileEntry] of fileEntries) {
|
|
81
82
|
entries.push({
|
|
82
83
|
name,
|
|
83
84
|
path: resolvePath(fileEntry.path, configDir, home),
|
|
@@ -96,7 +97,7 @@ function mergeEntries(userEntries, projectEntries) {
|
|
|
96
97
|
for (const entry of projectEntries) {
|
|
97
98
|
byName.set(entry.name, entry);
|
|
98
99
|
}
|
|
99
|
-
return
|
|
100
|
+
return byName.values().toArray();
|
|
100
101
|
}
|
|
101
102
|
function resolveDefaultKb(entries, name, sourcePath) {
|
|
102
103
|
if (name === undefined) {
|
|
@@ -91,7 +91,7 @@ function toFrontmatter(doc) {
|
|
|
91
91
|
tags = stringList(item.value);
|
|
92
92
|
break;
|
|
93
93
|
default:
|
|
94
|
-
extra[key] = key
|
|
94
|
+
extra[key] = Object.hasOwn(plainRecord, key) ? plainRecord[key] : null;
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
return { title, recordType, created, updated, tags, extra };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { asStringList, isValidDate } from "../note-io/field-validators.js";
|
|
2
2
|
export const EVENT_IMPACT_LEVELS = ['low', 'medium', 'high', 'critical'];
|
|
3
3
|
const IMPACT_LEVEL_SET = new Set(EVENT_IMPACT_LEVELS);
|
|
4
|
+
const NOT_SUPPLIED_VALUES = new Set([undefined, null, '']);
|
|
4
5
|
export function isEventImpact(value) {
|
|
5
6
|
return typeof value === 'string' && IMPACT_LEVEL_SET.has(value);
|
|
6
7
|
}
|
|
@@ -110,7 +111,7 @@ function readListField(value, field, errors) {
|
|
|
110
111
|
return list;
|
|
111
112
|
}
|
|
112
113
|
function readOptionalNonEmptyString(value, field, errors) {
|
|
113
|
-
if (value
|
|
114
|
+
if (NOT_SUPPLIED_VALUES.has(value)) {
|
|
114
115
|
return undefined;
|
|
115
116
|
}
|
|
116
117
|
if (typeof value === 'string') {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
+
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
4
|
+
import { chainError } from '@williamthorsen/toolbelt.errors/candidate';
|
|
3
5
|
import { parse } from 'yaml';
|
|
4
6
|
import { KbLoaderError } from "../config/kb-loader-error.js";
|
|
5
7
|
import { ALIASES_FILE } from "../layout/index.js";
|
|
@@ -20,8 +22,7 @@ export async function loadAliases(input) {
|
|
|
20
22
|
return parseAliases(text, path);
|
|
21
23
|
}
|
|
22
24
|
catch (error) {
|
|
23
|
-
|
|
24
|
-
throw new KbLoaderError(message);
|
|
25
|
+
throw new KbLoaderError(describeError(error), { cause: error });
|
|
25
26
|
}
|
|
26
27
|
}
|
|
27
28
|
export function parseAliases(text, contextLabel = 'tag-aliases') {
|
|
@@ -30,8 +31,7 @@ export function parseAliases(text, contextLabel = 'tag-aliases') {
|
|
|
30
31
|
parsed = parse(text);
|
|
31
32
|
}
|
|
32
33
|
catch (error) {
|
|
33
|
-
|
|
34
|
-
throw new Error(`${contextLabel}: malformed YAML — ${message}`);
|
|
34
|
+
throw chainError(`${contextLabel}: malformed YAML`, error);
|
|
35
35
|
}
|
|
36
36
|
if (!isRecord(parsed)) {
|
|
37
37
|
throw new Error(`${contextLabel}: top-level must be a mapping`);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
+
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
3
4
|
import { parse } from 'yaml';
|
|
4
5
|
import { KbLoaderError } from "../config/kb-loader-error.js";
|
|
5
6
|
import { TAXONOMY_FILE } from "../layout/index.js";
|
|
@@ -22,8 +23,7 @@ export async function loadTaxonomy(input) {
|
|
|
22
23
|
parsed = parse(text);
|
|
23
24
|
}
|
|
24
25
|
catch (error) {
|
|
25
|
-
|
|
26
|
-
throw new KbLoaderError(`${path}: malformed YAML — ${message}`);
|
|
26
|
+
throw new KbLoaderError(`${path}: malformed YAML: ${describeError(error)}`, { cause: error });
|
|
27
27
|
}
|
|
28
28
|
const result = taxonomyFileShape.safeParse(parsed ?? {});
|
|
29
29
|
if (!result.success) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomBytes } from 'node:crypto';
|
|
2
2
|
import { readFile, rename, unlink, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
+
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
4
5
|
import { isMap, isPair, isScalar, parseDocument } from 'yaml';
|
|
5
6
|
import { KbLoaderError } from "../config/kb-loader-error.js";
|
|
6
7
|
import { TAXONOMY_FILE } from "../layout/index.js";
|
|
@@ -98,7 +99,7 @@ async function readDocument(path) {
|
|
|
98
99
|
const document = parseDocument(text);
|
|
99
100
|
const firstError = document.errors[0];
|
|
100
101
|
if (firstError !== undefined) {
|
|
101
|
-
throw new KbLoaderError(`${path}: malformed YAML
|
|
102
|
+
throw new KbLoaderError(`${path}: malformed YAML: ${describeError(firstError)}`, { cause: firstError });
|
|
102
103
|
}
|
|
103
104
|
if (document.contents !== null && !isMap(document.contents)) {
|
|
104
105
|
throw new KbLoaderError(`${path}: top-level must be a mapping`);
|
|
@@ -136,7 +137,11 @@ async function writeAtomic(path, content) {
|
|
|
136
137
|
await rename(tempPath, path);
|
|
137
138
|
}
|
|
138
139
|
catch (error) {
|
|
139
|
-
|
|
140
|
+
try {
|
|
141
|
+
await unlink(tempPath);
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
}
|
|
140
145
|
throw error;
|
|
141
146
|
}
|
|
142
147
|
}
|
|
@@ -6,7 +6,7 @@ export function checkVaultIntegrity(notes) {
|
|
|
6
6
|
}
|
|
7
7
|
function basenameFindings(vaultIndex) {
|
|
8
8
|
const findings = [];
|
|
9
|
-
for (const key of
|
|
9
|
+
for (const key of vaultIndex.keys().toArray().toSorted()) {
|
|
10
10
|
const paths = vaultIndex.get(key);
|
|
11
11
|
if (paths === undefined || paths.size < 2)
|
|
12
12
|
continue;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@williamthorsen/kb",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Knowledge-base foundation: discovery, config, frontmatter parsing, records, tags, and vault-integrity checks",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"frontmatter",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"license": "ISC",
|
|
21
21
|
"author": "William Thorsen <william@thorsen.dev> (https://github.com/williamthorsen)",
|
|
22
|
+
"sideEffects": false,
|
|
22
23
|
"type": "module",
|
|
23
24
|
"exports": {
|
|
24
25
|
".": {
|
|
@@ -96,6 +97,7 @@
|
|
|
96
97
|
"dist"
|
|
97
98
|
],
|
|
98
99
|
"dependencies": {
|
|
100
|
+
"@williamthorsen/toolbelt.errors": "0.3.0",
|
|
99
101
|
"picomatch": "4.0.5",
|
|
100
102
|
"yaml": "2.9.0",
|
|
101
103
|
"zod": "4.4.3"
|
|
@@ -109,7 +111,5 @@
|
|
|
109
111
|
"publishConfig": {
|
|
110
112
|
"access": "public"
|
|
111
113
|
},
|
|
112
|
-
"scripts": {
|
|
113
|
-
"build": "nmr compile"
|
|
114
|
-
}
|
|
114
|
+
"scripts": {}
|
|
115
115
|
}
|