@williamthorsen/kb 0.8.0 → 0.8.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.
Files changed (69) hide show
  1. package/README.md +66 -82
  2. package/bin/kb.js +2 -4
  3. package/dist/esm/check/check.d.ts +9 -17
  4. package/dist/esm/check/enumerate.d.ts +12 -15
  5. package/dist/esm/check/glob-segments.d.ts +1 -1
  6. package/dist/esm/check/resolve-foreign-stores.d.ts +9 -8
  7. package/dist/esm/cli/commands/check.d.ts +4 -10
  8. package/dist/esm/cli/commands/check.js +2 -2
  9. package/dist/esm/cli/commands/create.d.ts +3 -7
  10. package/dist/esm/cli/commands/create.js +1 -1
  11. package/dist/esm/cli/commands/scaffold.d.ts +5 -9
  12. package/dist/esm/cli/commands/scaffold.js +5 -5
  13. package/dist/esm/cli/commands/set-default.d.ts +1 -5
  14. package/dist/esm/cli/commands/set-default.js +1 -1
  15. package/dist/esm/cli/commands/taxonomy.d.ts +5 -10
  16. package/dist/esm/cli/commands/taxonomy.js +10 -9
  17. package/dist/esm/cli/format.d.ts +8 -10
  18. package/dist/esm/cli/resolve-store.d.ts +8 -8
  19. package/dist/esm/cli/run.d.ts +4 -6
  20. package/dist/esm/cli/run.js +1 -1
  21. package/dist/esm/cli/select-kb-prompt.d.ts +6 -5
  22. package/dist/esm/cli/select-kb-prompt.js +1 -1
  23. package/dist/esm/cli/targeting/resolve-changed-paths.d.ts +3 -3
  24. package/dist/esm/cli/targeting/select-notes.d.ts +2 -2
  25. package/dist/esm/config/config-schema.d.ts +10 -9
  26. package/dist/esm/config/kb-loader-error.d.ts +3 -3
  27. package/dist/esm/config/load-config.d.ts +2 -2
  28. package/dist/esm/config/load-config.js +1 -1
  29. package/dist/esm/config/note-scope.d.ts +7 -7
  30. package/dist/esm/create/create.d.ts +8 -8
  31. package/dist/esm/discovery/kb-registry-schema.d.ts +0 -2
  32. package/dist/esm/discovery/load-registry.d.ts +7 -9
  33. package/dist/esm/discovery/load-registry.js +1 -1
  34. package/dist/esm/discovery/register-store.d.ts +3 -4
  35. package/dist/esm/discovery/register-store.js +2 -2
  36. package/dist/esm/discovery/registry-document.d.ts +2 -2
  37. package/dist/esm/discovery/set-default-kb.d.ts +5 -5
  38. package/dist/esm/discovery/set-default-kb.js +3 -3
  39. package/dist/esm/filesystem/exists.d.ts +4 -4
  40. package/dist/esm/frontmatter/parse-note.d.ts +3 -3
  41. package/dist/esm/git/list-git-scope.d.ts +6 -6
  42. package/dist/esm/git/run-git.d.ts +2 -2
  43. package/dist/esm/layout/store-layout.d.ts +5 -5
  44. package/dist/esm/lints/paths.d.ts +1 -1
  45. package/dist/esm/lints/paths.js +1 -1
  46. package/dist/esm/lints/tag-alias.d.ts +1 -1
  47. package/dist/esm/lints/tag-alias.js +1 -1
  48. package/dist/esm/lints/taxonomy.d.ts +9 -8
  49. package/dist/esm/lints/taxonomy.js +2 -2
  50. package/dist/esm/note-io/field-validators.d.ts +1 -1
  51. package/dist/esm/note-io/read-note.d.ts +2 -2
  52. package/dist/esm/note-io/write-note.d.ts +3 -6
  53. package/dist/esm/records/event.d.ts +3 -3
  54. package/dist/esm/scaffold/render-seeds.d.ts +3 -3
  55. package/dist/esm/scaffold/render-seeds.js +2 -2
  56. package/dist/esm/scaffold/scaffold.d.ts +7 -8
  57. package/dist/esm/tags/load-aliases.d.ts +3 -4
  58. package/dist/esm/taxonomy/domain-paths.d.ts +6 -6
  59. package/dist/esm/taxonomy/load-taxonomy.d.ts +3 -3
  60. package/dist/esm/taxonomy/load-taxonomy.js +2 -2
  61. package/dist/esm/taxonomy/taxonomy-schema.d.ts +5 -6
  62. package/dist/esm/taxonomy/write-taxonomy.d.ts +7 -8
  63. package/dist/esm/type-guards.d.ts +1 -1
  64. package/dist/esm/types.d.ts +6 -9
  65. package/dist/esm/vault-integrity/build-vault-index.d.ts +3 -4
  66. package/dist/esm/vault-integrity/check-vault-integrity.d.ts +13 -13
  67. package/dist/esm/vault-integrity/check-vault-integrity.js +1 -1
  68. package/dist/esm/vault-integrity/wikilink-parse.d.ts +21 -16
  69. package/package.json +5 -5
@@ -1,13 +1,13 @@
1
1
  import type { KbRegistry } from '../types.js';
2
2
  /**
3
- * Load and merge the user-global (`~/.agents/kb.yaml`) and project-local
3
+ * Loads and merges the user-global (`~/.agents/kb.yaml`) and project-local
4
4
  * (`.agents/kb.yaml`) KB registries into a normalized `KbRegistry`.
5
5
  *
6
6
  * Project entries replace user entries by name on collision and append new names. The top-level `default_kb`
7
7
  * pointer resolves by name against the merged entries (the project's value overriding the user's); the resolved
8
8
  * entry is exposed as `defaultKb`.
9
- * Within a single file, relative `path` values resolve against that file's directory and a leading `~/` expands
10
- * against `$HOME`. Both files are optional; when neither exists the result has no entries.
9
+ * Within a single file, relative `path` values resolve against that file's directory and a leading `~` or `~/`
10
+ * expands against `$HOME`. Both files are optional; when neither exists the result has no entries.
11
11
  * Malformed YAML, a structural defect, or a `default_kb` that names no registered KB throw.
12
12
  */
13
13
  export declare function loadKbRegistry(input?: {
@@ -23,13 +23,11 @@ export interface KbRegistryLoadResult {
23
23
  error?: string;
24
24
  }
25
25
  /**
26
- * Load the merged `kb.yaml` registry without throwing, capturing any failure message instead of presenting it.
26
+ * Loads the merged `kb.yaml` registry without throwing, capturing any failure message instead of presenting it.
27
27
  *
28
- * On success including the legitimate "no registry files present" case, which `loadKbRegistry` already returns as an
29
- * empty config `error` is absent. On a malformed file, a schema violation, an unresolvable `default_kb`, or a
30
- * non-ENOENT read failure, the result degrades to an empty config and carries the thrown message in `error`. Each
31
- * caller decides whether and how to surface that message; this wrapper neither writes to stderr nor builds a
32
- * diagnostic.
28
+ * When `loadKbRegistry` throws, the result degrades to an empty config and contains the thrown message in `error`; the
29
+ * caller decides whether and how to report that message. Absent registry files are a success, which `loadKbRegistry`
30
+ * returns as an empty config.
33
31
  */
34
32
  export declare function tryLoadKbRegistry(input?: {
35
33
  userConfigPath?: string;
@@ -73,7 +73,7 @@ async function loadRegistryFile(path, source, home) {
73
73
  }
74
74
  const result = kbRegistryFileSchema.safeParse(parsed);
75
75
  if (!result.success) {
76
- throw new Error(`${path}: invalid kb.yaml ${result.error.issues[0]?.message ?? 'unknown error'}`);
76
+ throw new Error(`${path}: invalid kb.yaml: ${result.error.issues[0]?.message ?? 'unknown error'}`);
77
77
  }
78
78
  const configDir = dirname(path);
79
79
  const entries = [];
@@ -1,4 +1,3 @@
1
- /** The outcome of a registry write. */
2
1
  export interface RegisterStoreResult {
3
2
  /** `added` when a new entry was written; `already-present` when an entry of the same name already existed. */
4
3
  status: 'added' | 'already-present';
@@ -7,9 +6,9 @@ export interface RegisterStoreResult {
7
6
  * Inserts a knowledge-base entry under `kbs:` in a `kb.yaml` registry, creating the file and its parent directory when
8
7
  * absent and preserving any existing comments and formatting. Every write leaves the `kbs:` entries alphabetically
9
8
  * ordered, so a registry that has already drifted converges rather than merely staying sorted. An existing entry of the
10
- * same name is left untouched and reported as `already-present`; that path writes nothing, so re-registering never
11
- * reorders the file. The registry is validated against its schema both before mutation (so an already-corrupt file
12
- * throws rather than being silently extended) and after (so an invalid entry, such as an empty `storePath` from a
9
+ * same name is left untouched and reported as `already-present`; because that path writes nothing, re-registering never
10
+ * reorders the file. The registry is validated against its schema both before mutation (so that an already-corrupt file
11
+ * throws rather than being silently extended) and after (so that an invalid entry, such as an empty `storePath` from a
13
12
  * direct caller, throws rather than being written), never producing a corrupt file.
14
13
  */
15
14
  export declare function registerStore(input: {
@@ -7,7 +7,7 @@ export async function registerStore(input) {
7
7
  const doc = await loadRegistryDocument(input.registryPath);
8
8
  const existing = kbRegistryFileSchema.safeParse(doc.toJS() ?? {});
9
9
  if (!existing.success) {
10
- throw new Error(`${input.registryPath}: invalid kb.yaml ${existing.error.issues[0]?.message ?? 'unknown error'}`);
10
+ throw new Error(`${input.registryPath}: invalid kb.yaml: ${existing.error.issues[0]?.message ?? 'unknown error'}`);
11
11
  }
12
12
  if (doc.hasIn(['kbs', input.name])) {
13
13
  return { status: 'already-present' };
@@ -20,7 +20,7 @@ export async function registerStore(input) {
20
20
  sortRegistryEntries(doc);
21
21
  const result = kbRegistryFileSchema.safeParse(doc.toJS());
22
22
  if (!result.success) {
23
- throw new Error(`${input.registryPath}: cannot register "${input.name}" ${result.error.issues[0]?.message ?? 'invalid entry'}`);
23
+ throw new Error(`${input.registryPath}: cannot register "${input.name}": ${result.error.issues[0]?.message ?? 'invalid entry'}`);
24
24
  }
25
25
  await mkdir(dirname(input.registryPath), { recursive: true });
26
26
  await writeFile(input.registryPath, doc.toString(), 'utf8');
@@ -1,7 +1,7 @@
1
1
  import { Document } from 'yaml';
2
2
  /**
3
3
  * Reads and parses a `kb.yaml` registry as a `yaml` Document, returning a fresh empty document when the file is absent
4
- * or empty. Parsing through `parseDocument` preserves comments and formatting so registry writers can mutate in place
5
- * without discarding them. Shared by every registry writer (`registerStore`, `setDefaultKb`, `clearDefaultKb`).
4
+ * or empty. Parsing through `parseDocument` preserves comments and formatting so that registry writers can mutate in
5
+ * place without discarding them.
6
6
  */
7
7
  export declare function loadRegistryDocument(path: string): Promise<Document>;
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Removes the top-level `default_kb` pointer from a `kb.yaml` registry, preserving existing comments and formatting. A
3
- * no-op when no default is set (or the file is absent): the file is left untouched rather than rewritten, so clearing is
4
- * idempotent and never reformats. Validates an existing registry against its schema first, so a corrupt file throws
5
- * rather than being rewritten.
3
+ * no-op when no default is set (or the file is absent): The file is left untouched rather than rewritten, so clearing is
4
+ * idempotent and never reformats. Validates an existing registry against its schema first, so that a corrupt file
5
+ * throws rather than being rewritten.
6
6
  */
7
7
  export declare function clearDefaultKb(input: {
8
8
  registryPath: string;
@@ -10,8 +10,8 @@ export declare function clearDefaultKb(input: {
10
10
  /**
11
11
  * Sets the top-level `default_kb` pointer in a `kb.yaml` registry to `name`, creating the file and its parent directory
12
12
  * when absent and preserving existing comments and formatting. Validates the registry against its schema before mutating
13
- * (so an already-corrupt file throws rather than being rewritten) and asserts `name` is registered under `kbs` in the
14
- * file (a `default_kb` naming no registered KB would fail every subsequent load), then re-validates the result.
13
+ * (so that an already-corrupt file throws rather than being rewritten) and asserts `name` is registered under `kbs` in
14
+ * the file (a `default_kb` naming no registered KB would fail every subsequent load), then re-validates the result.
15
15
  */
16
16
  export declare function setDefaultKb(input: {
17
17
  registryPath: string;
@@ -7,7 +7,7 @@ export async function clearDefaultKb(input) {
7
7
  const doc = await loadRegistryDocument(input.registryPath);
8
8
  const existing = kbRegistryFileSchema.safeParse(doc.toJS() ?? {});
9
9
  if (!existing.success) {
10
- throw new Error(`${input.registryPath}: invalid kb.yaml ${existing.error.issues[0]?.message ?? 'unknown error'}`);
10
+ throw new Error(`${input.registryPath}: invalid kb.yaml: ${existing.error.issues[0]?.message ?? 'unknown error'}`);
11
11
  }
12
12
  if (!doc.has('default_kb')) {
13
13
  return;
@@ -19,7 +19,7 @@ export async function setDefaultKb(input) {
19
19
  const doc = await loadRegistryDocument(input.registryPath);
20
20
  const existing = kbRegistryFileSchema.safeParse(doc.toJS() ?? {});
21
21
  if (!existing.success) {
22
- throw new Error(`${input.registryPath}: invalid kb.yaml ${existing.error.issues[0]?.message ?? 'unknown error'}`);
22
+ throw new Error(`${input.registryPath}: invalid kb.yaml: ${existing.error.issues[0]?.message ?? 'unknown error'}`);
23
23
  }
24
24
  if (!doc.hasIn(['kbs', input.name])) {
25
25
  throw new Error(`${input.registryPath}: "${input.name}" is not a registered knowledge base`);
@@ -27,7 +27,7 @@ export async function setDefaultKb(input) {
27
27
  doc.set('default_kb', input.name);
28
28
  const result = kbRegistryFileSchema.safeParse(doc.toJS());
29
29
  if (!result.success) {
30
- throw new Error(`${input.registryPath}: cannot set default_kb ${result.error.issues[0]?.message ?? 'invalid registry'}`);
30
+ throw new Error(`${input.registryPath}: cannot set default_kb: ${result.error.issues[0]?.message ?? 'invalid registry'}`);
31
31
  }
32
32
  await mkdir(dirname(input.registryPath), { recursive: true });
33
33
  await writeFile(input.registryPath, doc.toString(), 'utf8');
@@ -1,15 +1,15 @@
1
1
  /** Controls which `stat` failures a filesystem-existence check treats as "absent". */
2
2
  export interface ExistsOptions {
3
3
  /**
4
- * Stat-error codes treated as "absent" (the helper returns `false`). Any other failure is re-thrown so a genuine
5
- * error most importantly a permission denial on a path that does exist is not silently read as absence.
4
+ * Stat-error codes treated as "absent" (the helper returns `false`). Any other failure is re-thrown so that a
5
+ * genuine error (most importantly a permission denial on a path that does exist) is not silently read as absence.
6
6
  * Defaults to `['ENOENT']`.
7
7
  */
8
8
  absentCodes?: readonly string[];
9
9
  /**
10
10
  * When `true`, every `stat` failure is treated as "absent" (`false`), including permission errors.
11
- * Reserved for best-effort probes such as an ancestor walk, where an unreadable path should be skipped rather than
12
- * abort the operation. Takes precedence over `absentCodes`.
11
+ * Reserved for best-effort probes such as an ancestor walk, in which an unreadable path should be skipped rather
12
+ * than abort the operation. Takes precedence over `absentCodes`.
13
13
  */
14
14
  treatErrorsAsAbsent?: boolean;
15
15
  }
@@ -1,14 +1,14 @@
1
1
  import type { ParsedNote } from '../types.js';
2
2
  /**
3
- * Reads a note from disk and parse it into a `ParsedNote`. I/O errors (e.g. a missing file) are thrown;
3
+ * Reads a note from disk and parses it into a `ParsedNote`. I/O errors (e.g. a missing file) are thrown;
4
4
  * YAML parse errors are not.
5
5
  */
6
6
  export declare function parseNote(input: {
7
7
  path: string;
8
8
  }): Promise<ParsedNote>;
9
9
  /**
10
- * Parse a note from a literal string into a `ParsedNote` carrying typed frontmatter.
11
- * Parse errors are recorded in `frontmatterRaw.parseError`, never thrown the rule layer decides how to report them.
10
+ * Parses a note from a literal string into a `ParsedNote` containing typed frontmatter.
11
+ * Parse errors are recorded in `frontmatterRaw.parseError`, never thrown; the rule layer decides how to report them.
12
12
  * `path` defaults to `<string>` and labels the result for diagnostics.
13
13
  */
14
14
  export declare function parseNoteContent(input: {
@@ -1,14 +1,14 @@
1
1
  /**
2
2
  * Returns every path that git tracks under `root`, plus every untracked path that no ignore rule covers, as an
3
- * NFC-normalized set of root-relative, slash-separated paths. Returns `undefined` where git holds no opinion,
4
- * because `root` lies outside a working tree or git cannot be run at all, so a caller keeps its own scope instead of
3
+ * NFC-normalized set of root-relative, slash-separated paths. Returns `undefined` when git cannot list the paths,
4
+ * because `root` is outside a working tree or git cannot be run at all, so a caller keeps its own scope instead of
5
5
  * reading an empty set as "git ignores everything".
6
6
  *
7
- * A tracked file stays in scope even where an ignore rule matches it, since every clone still sees it. That is why
8
- * the scope unions the two `ls-files` forms rather than asking `git check-ignore`, which would report it ignored.
7
+ * A tracked file stays in scope even when an ignore rule matches it, since every clone still contains it. That is why
8
+ * the function unions the two `ls-files` forms rather than running `git check-ignore`, which would report it ignored.
9
9
  *
10
- * Paths are normalized because git reports NFC where `readdir` reports NFD on macOS, and comparing the two forms
11
- * unnormalized drops every path whose name carries a combining mark.
10
+ * Paths are normalized because git reports NFC but `readdir` reports NFD on macOS, and comparing the two forms
11
+ * unnormalized drops every path whose name contains a combining mark.
12
12
  */
13
13
  export declare function listGitScope(input: {
14
14
  root: string;
@@ -7,8 +7,8 @@ export type GitResult = {
7
7
  message: string;
8
8
  };
9
9
  /**
10
- * Runs `git -C cwd <args>` and returns its stdout, or a failure carrying git's own stderr. A git that cannot be
11
- * spawned fails the same way as one that exits non-zero, so both reach the caller as a single "git did not answer".
10
+ * Runs `git -C cwd <args>` and returns its stdout, or a failure that contains git's own stderr. A git that cannot be
11
+ * spawned fails the same way as one that exits non-zero, so the caller receives both as one kind of failure.
12
12
  */
13
13
  export declare function runGit(input: {
14
14
  cwd: string;
@@ -4,17 +4,17 @@ export declare const KB_DIR = ".kb";
4
4
  export declare const CONTENT_DIR = "content";
5
5
  /** The `content/` subdirectory holding assertion records. Named on its own because a caller may need to recognize the segment, not just the path. */
6
6
  export declare const ASSERTIONS_SEGMENT = "assertions";
7
- /** The tag-alias map. */
8
7
  export declare const ALIASES_FILE = ".kb/tag-aliases.yaml";
9
- /** The directory holding the store's assertion records. */
10
8
  export declare const ASSERTIONS_DIR = "content/assertions";
11
9
  /** The check configuration. */
12
10
  export declare const CONFIG_FILE = ".kb/config.yaml";
13
- /** The editor and formatter settings. Sits at the store root, where every tool that reads it looks. */
11
+ /** The editor and formatter settings. Located at the store root, where every tool that reads it looks. */
14
12
  export declare const EDITORCONFIG_FILE = ".editorconfig";
15
- /** The directory holding the store's event records. */
16
13
  export declare const EVENTS_DIR = "content/events";
17
- /** The formatting configuration. Sits at the store root rather than under `.kb/`, because Prettier discovers it there. */
14
+ /**
15
+ * The formatting configuration. Located at the store root rather than under `.kb/`, because Prettier discovers it
16
+ * there.
17
+ */
18
18
  export declare const PRETTIER_CONFIG_FILE = ".prettierrc.yaml";
19
19
  /** The declared assertion taxonomy. */
20
20
  export declare const TAXONOMY_FILE = ".kb/taxonomy.yaml";
@@ -1,5 +1,5 @@
1
1
  import type { Finding } from '../types.js';
2
- /** The note fields the paths lint reads: its path and its full raw content. */
2
+ /** The note fields that the paths lint reads: its path and its full raw content. */
3
3
  export interface PathsNote {
4
4
  path: string;
5
5
  content: string;
@@ -5,7 +5,7 @@ export function pathsFindings(note) {
5
5
  line: countNewlines(note.content, match.index) + 1,
6
6
  rule: 'paths.user-home',
7
7
  severity: 'error',
8
- message: `hardcoded "${match[0]}" replace with ~/`,
8
+ message: `hardcoded "${match[0]}"; replace with ~/`,
9
9
  }));
10
10
  return findings;
11
11
  }
@@ -1,5 +1,5 @@
1
1
  import type { AliasMap, Finding } from '../types.js';
2
- /** The note fields the tag-alias lint reads: its path and its raw frontmatter field map. */
2
+ /** The note fields that the tag-alias lint reads: its path and its raw frontmatter field map. */
3
3
  export interface TagAliasNote {
4
4
  path: string;
5
5
  fields: Record<string, unknown>;
@@ -14,7 +14,7 @@ export function tagAliasFindings(note, aliases) {
14
14
  path: note.path,
15
15
  rule: 'tag-alias',
16
16
  severity: 'warning',
17
- message: `tag "${tag}" is an alias use canonical form "${canonical}"`,
17
+ message: `tag "${tag}" is an alias; use canonical form "${canonical}"`,
18
18
  });
19
19
  }
20
20
  return findings;
@@ -1,22 +1,23 @@
1
1
  import type { KbConfig } from '../config/config-schema.js';
2
2
  import type { Taxonomy } from '../taxonomy/taxonomy-schema.js';
3
3
  import type { Finding } from '../types.js';
4
- /** The note fields the taxonomy rules read. */
4
+ /** The note fields that the taxonomy rules read. */
5
5
  export interface TaxonomyNote {
6
6
  /** The note's path relative to the KB root, slash-separated. */
7
7
  relativePath: string;
8
8
  }
9
9
  /**
10
- * Reports where a store's assertion folders and its declared taxonomy disagree: `taxonomy.undeclared` for a folder
11
- * holding notes that no domain declares, `taxonomy.unused` for a declared domain holding no note at or beneath it, and
12
- * `taxonomy.orphan` for a declared domain whose parent is undeclared. All are warnings, so drift is reported without
13
- * failing the run, and all are vault-scoped, so a run narrowed to selected notes still sees them.
10
+ * Reports each disagreement between a store's assertion folders and its declared taxonomy: `taxonomy.undeclared` for a
11
+ * folder holding notes that no domain declares, `taxonomy.unused` for a declared domain holding no note at or beneath
12
+ * it, and `taxonomy.orphan` for a declared domain whose parent is undeclared. All are warnings, so drift is reported
13
+ * without failing the run. All are vault-scoped: A run narrowed to selected notes still reports them.
14
14
  *
15
15
  * A taxonomy declaring nothing disables all three, whether because the file is absent or because it declares no
16
- * domains. A store that has not adopted a taxonomy is therefore silent rather than reporting every folder it owns.
16
+ * domains. The rules therefore report nothing for a store that has not adopted a taxonomy, rather than flagging
17
+ * every folder that the store owns.
17
18
  *
18
- * The observed structure comes from the enumerated notes' own paths rather than a directory listing, so the rules add
19
- * no filesystem traversal and see exactly the notes the run's `targets` and `exclude` admitted.
19
+ * Because the observed structure comes from the enumerated notes' own paths rather than a directory listing, the rules
20
+ * add no filesystem traversal and consider exactly the notes that the run's `targets` and `exclude` admitted.
20
21
  */
21
22
  export declare function taxonomyFindings(input: {
22
23
  notes: readonly TaxonomyNote[];
@@ -18,14 +18,14 @@ export function taxonomyFindings(input) {
18
18
  const findings = [];
19
19
  for (const domain of [...observed].toSorted()) {
20
20
  if (!taxonomy.has(domain)) {
21
- findings.push(buildFinding(taxonomyPath, 'undeclared', `folder "${domain}" holds notes but no domain declares it`));
21
+ findings.push(buildFinding(taxonomyPath, 'undeclared', `folder "${domain}" contains notes but no domain declares it`));
22
22
  }
23
23
  }
24
24
  for (const domain of declared) {
25
25
  if (holdsNote(domain, observed) || matcher.isExcluded(`${ASSERTIONS_DIR}/${domain}`)) {
26
26
  continue;
27
27
  }
28
- findings.push(buildFinding(taxonomyPath, 'unused', `domain "${domain}" is declared but holds no notes`));
28
+ findings.push(buildFinding(taxonomyPath, 'unused', `domain "${domain}" is declared but contains no notes`));
29
29
  }
30
30
  for (const domain of declared) {
31
31
  const parent = resolveParent(domain);
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Coerces a frontmatter field value to a string list: the array's string members when it is a sequence, an empty list
3
- * when the value is absent, or `null` when the value is present but not list-shaped (so a parser can flag it).
3
+ * when the value is absent, or `null` when the value is present but not list-shaped (so that a parser can flag it).
4
4
  */
5
5
  export declare function asStringList(value: unknown): string[] | null;
6
6
  /**
@@ -1,4 +1,4 @@
1
- /** The outcome of splitting a note: its frontmatter field map, its body, the body's start line, and any parse error. */
1
+ /** A note split into its frontmatter field map and its body. */
2
2
  export interface ReadNote {
3
3
  fields: Record<string, unknown>;
4
4
  body: string;
@@ -10,6 +10,6 @@ export interface ReadNote {
10
10
  export declare function readNote(path: string): Promise<ReadNote>;
11
11
  /**
12
12
  * Splits note content into a frontmatter field map and body. A missing frontmatter block or a YAML parse error is
13
- * reported in `error` (never thrown), with the full content returned as the body so callers can still surface it.
13
+ * reported in `error` (never thrown), with the full content returned as the body so that a caller can still show it.
14
14
  */
15
15
  export declare function readNoteContent(content: string): ReadNote;
@@ -1,11 +1,8 @@
1
1
  /**
2
2
  * Renders a field map and body to note content: the opening fence, the frontmatter, the closing fence, one blank line,
3
- * then the body. A single leading newline on the body is dropped so the blank line is not doubled, keeping the output
4
- * stable across read/write cycles.
3
+ * then the body. A single leading newline on the body is dropped so that the blank line is not doubled, keeping the
4
+ * output stable across read/write cycles.
5
5
  */
6
6
  export declare function renderNote(fields: Record<string, unknown>, body: string): string;
7
- /**
8
- * Atomically writes a note to `path`, so a concurrent reader never sees a partial write. See {@link writeAtomic} for
9
- * the guarantee and its failure behavior.
10
- */
7
+ /** Atomically writes a note to `path`. See {@link writeAtomic} for the guarantee and its failure behavior. */
11
8
  export declare function writeNote(path: string, fields: Record<string, unknown>, body: string): Promise<void>;
@@ -1,4 +1,4 @@
1
- /** The impact levels an event may carry, ordered lowest to highest. */
1
+ /** The impact levels that an event may declare, ordered lowest to highest. */
2
2
  export declare const EVENT_IMPACT_LEVELS: readonly ["low", "medium", "high", "critical"];
3
3
  /** An event's impact: the author's subjective, revisable rating of how much addressing the event matters. */
4
4
  export type EventImpact = (typeof EVENT_IMPACT_LEVELS)[number];
@@ -31,8 +31,8 @@ export type ParseEventResult = {
31
31
  export declare function parseEvent(fields: Record<string, unknown>, body: string): ParseEventResult;
32
32
  /**
33
33
  * Projects an event back to a frontmatter field map (declared fields first, then preserved `extra`) plus its body. An
34
- * empty `session` is omitted like an absent one, mirroring {@link parseEvent}: the two spellings of "no session" have a
35
- * single representation on both edges of the module, so no record can reacquire the empty field on a write.
34
+ * empty `session` is omitted like an absent one, mirroring {@link parseEvent}: The two forms of "no session" have one
35
+ * representation on both parse and render, so no record can reacquire the empty field on a write.
36
36
  */
37
37
  export declare function renderEvent(record: KbEvent): {
38
38
  fields: Record<string, unknown>;
@@ -1,6 +1,6 @@
1
1
  /**
2
- * The formatting option every store carries that `.editorconfig` cannot express. Held here rather than in the seed
3
- * prose, so that the file that a store receives cannot diverge from the value that kb documents. See
2
+ * The formatting option that every store sets and that `.editorconfig` cannot express. Defined here rather than in
3
+ * the seed prose, so that the file that a store receives cannot diverge from the value that kb documents. See
4
4
  * {@link renderPrettierSeed}'s header for what it prevents.
5
5
  */
6
6
  export declare const canonicalPrettierConfig: {
@@ -10,7 +10,7 @@ export declare const canonicalPrettierConfig: {
10
10
  export declare function renderAliasesSeed(): string;
11
11
  /** Renders `.kb/config.yaml`: a fully-commented stub whose example values are the live `defaultKbConfig`. */
12
12
  export declare function renderConfigSeed(): string;
13
- /** Renders `.editorconfig`: the editor and formatter settings every store shares. */
13
+ /** Renders `.editorconfig`: the editor and formatter settings that every store shares. */
14
14
  export declare function renderEditorconfigSeed(): string;
15
15
  /** Renders `.prettierrc.yaml`: the canonical formatting option under a header explaining why it is set. */
16
16
  export declare function renderPrettierSeed(): string;
@@ -12,7 +12,7 @@ const CONFIG_HEADER = `# Check configuration for this knowledge store.
12
12
  # Every key is optional and falls back to the default shown below; an absent file uses these defaults too. Uncomment
13
13
  # and edit to override. \`targets\` selects which notes \`kb check\` enumerates and \`exclude\` removes matches;
14
14
  # patterns are slash-separated and relative to the store root. \`visibility\` declares how widely the store is
15
- # published, which decides what may link into it: a \`[[store:Note title]]\` link resolves only into a store as
15
+ # published, which decides what may link into it: A \`[[store:Note title]]\` link resolves only into a store as
16
16
  # shareable as its own or more so.
17
17
  #
18
18
  `;
@@ -36,7 +36,7 @@ const PRETTIER_HEADER = `# Formatting configuration for this knowledge store.
36
36
  #
37
37
  # \`embeddedLanguageFormatting: off\` leaves a note's YAML frontmatter unformatted. Formatted, a long \`tags\` or
38
38
  # \`addressed-by\` list breaks across several lines, which the note writer puts back onto one the next time it writes
39
- # the note, so the formatter and the writer would rewrite each other's output without end.
39
+ # the note. The formatter and the writer would rewrite each other's output without end.
40
40
  #
41
41
  # Width, indentation, and line endings are set in \`.editorconfig\`, which Prettier reads and every editor reads too.
42
42
  #
@@ -1,21 +1,20 @@
1
1
  /**
2
- * What {@link scaffold} did about one canonical path: `created` where it was absent, `present` where it was left as
3
- * found, `replaced` where `force` overwrote it.
2
+ * What {@link scaffold} did about one canonical path: `created` when it was absent, `present` when it was left as
3
+ * found, `replaced` when `force` overwrote it.
4
4
  */
5
5
  export type ScaffoldAction = 'created' | 'present' | 'replaced';
6
6
  /** One canonical path and what the scaffold did about it. */
7
7
  export interface ScaffoldEntry {
8
- /** The store-relative path, directories carrying a trailing slash. */
8
+ /** The store-relative path, directories with a trailing slash. */
9
9
  path: string;
10
10
  action: ScaffoldAction;
11
11
  }
12
12
  /**
13
- * Writes the canonical files and directories every knowledge-base store holds, leaving an existing file as it found it
14
- * unless `force` is set. Directories are ensured either way, since a directory has no content to replace.
13
+ * Writes the canonical files and directories that every knowledge-base store contains, leaving an existing file as it
14
+ * found it unless `force` is set. Directories are ensured either way, since a directory has no content to replace.
15
15
  *
16
- * The function asserts nothing about the store: a caller that requires one to exist, or requires one not to, checks
17
- * that itself. `create` calls it on a directory it has just confirmed holds no store, and `kb scaffold` on one it has
18
- * just confirmed does.
16
+ * The function asserts nothing about the store: A caller that requires one to exist, or requires one not to, checks
17
+ * that itself.
19
18
  */
20
19
  export declare function scaffold(input: {
21
20
  storePath: string;
@@ -1,15 +1,14 @@
1
1
  import type { AliasMap, KbRoot } from '../types.js';
2
2
  /**
3
3
  * Loads `.kb/tag-aliases.yaml` from a KB root into a typed `AliasMap`, returning an empty map when the file is absent.
4
- * The thin I/O wrapper around {@link parseAliases}; structural defects throw with the file path included.
4
+ * The thin I/O wrapper around {@link parseAliases}; on a structural defect it throws, naming the file path.
5
5
  */
6
6
  export declare function loadAliases(input: {
7
7
  kbRoot: KbRoot;
8
8
  }): Promise<AliasMap>;
9
9
  /**
10
10
  * Parses a tag-aliases registry from a string into an `AliasMap`.
11
- * Aliases are lowercased on insertion so callers can look up case-insensitively.
12
- * Throws on any structural defect non-object top level, missing `aliases` key, non-string entries, self-aliases,
13
- * or cross-canonical collisions — with `contextLabel` prefixed onto every message.
11
+ * Aliases are lowercased on insertion so that callers can look up case-insensitively.
12
+ * Throws on malformed YAML and on any structural defect, with `contextLabel` prefixed onto every message.
14
13
  */
15
14
  export declare function parseAliases(text: string, contextLabel?: string): AliasMap;
@@ -1,13 +1,13 @@
1
1
  /**
2
- * Derives every domain a note set implies: the domain each note sits in, plus each of that domain's ancestors, sorted.
3
- * A grouping domain that holds only subfolders is included, because the rules treat it as in use and would otherwise
4
- * report a back-filled taxonomy's own entries as unused.
2
+ * Derives every domain implied by a note set: each note's domain, plus each of that domain's ancestors, sorted.
3
+ * A grouping domain that contains only subfolders is included, because the rules treat it as in use and would
4
+ * otherwise report a back-filled taxonomy's own entries as unused.
5
5
  */
6
6
  export declare function deriveDomains(relativePaths: Iterable<string>): string[];
7
7
  /**
8
- * Derives the domain a note sits in, or `undefined` when the note is not an assertion or sits at the assertions root.
9
- * Scoping to the assertions root is what keeps event records from registering as domains.
8
+ * Derives a note's domain, or `undefined` when the note is not an assertion or is at the assertions root.
9
+ * Scoping to the assertions root keeps event records from registering as domains.
10
10
  */
11
11
  export declare function resolveDomain(relativePath: string): string | undefined;
12
- /** Derives a slash-path's parent, or `undefined` when it has no separator and so sits at the top level. */
12
+ /** Derives a slash-path's parent, or `undefined` when it has no separator and so is at the top level. */
13
13
  export declare function resolveParent(path: string): string | undefined;
@@ -3,10 +3,10 @@ import { type Taxonomy } from './taxonomy-schema.js';
3
3
  /**
4
4
  * Loads `.kb/taxonomy.yaml` into a single keyed map, returning an empty taxonomy when the file is absent or declares
5
5
  * nothing. The two on-disk blocks are a file-format concern: a consumer looks a domain up once and reads `provisional`
6
- * off the entry it finds.
6
+ * off the entry that it finds.
7
7
  *
8
- * Mirrors {@link loadKbConfig}: structural defects (malformed YAML, a wrong type, a malformed key, a path declared in
9
- * both blocks) throw a {@link KbLoaderError} naming the file. I/O errors other than a missing file propagate.
8
+ * Structural defects (malformed YAML, a wrong type, a malformed key, a path declared in both blocks) throw a
9
+ * {@link KbLoaderError} naming the file. I/O errors other than a missing file propagate.
10
10
  */
11
11
  export declare function loadTaxonomy(input: {
12
12
  kbRoot: KbRoot;
@@ -53,8 +53,8 @@ function collectBlock(input) {
53
53
  function describeIssueLocation(error) {
54
54
  const issue = error.issues[0];
55
55
  if (issue === undefined) {
56
- return ' unknown error';
56
+ return ': unknown error';
57
57
  }
58
58
  const location = issue.path.length > 0 ? ` at ${issue.path.join('.')}` : '';
59
- return `${location} ${issue.message}`;
59
+ return `${location}: ${issue.message}`;
60
60
  }
@@ -2,12 +2,11 @@ import { z } from 'zod';
2
2
  /**
3
3
  * Describes why a domain key cannot be used, or returns `undefined` when the key is well-formed. Keys are relative to
4
4
  * the assertions root, so a restated `content/assertions/` prefix would declare the domain a level deeper than the
5
- * author meant; `kb-add` refuses the same mistake on the note-write path.
5
+ * author meant.
6
6
  */
7
7
  export declare function describeKeyDefect(key: string): string | undefined;
8
8
  /** A knowledge base's declared assertion structure, keyed by assertions-root-relative slash-path. */
9
9
  export type Taxonomy = ReadonlyMap<string, TaxonomyEntry>;
10
- /** A single declared domain. */
11
10
  export interface TaxonomyEntry {
12
11
  /** The domain's one-line description; empty when it was declared without one. */
13
12
  description: string;
@@ -18,11 +17,11 @@ export interface TaxonomyEntry {
18
17
  * The on-disk `.kb/taxonomy.yaml` shape.
19
18
  *
20
19
  * Both blocks are optional and nullable, so a file may declare only one, and a block header left with nothing under it
21
- * loads as declaring nothing rather than failing. YAML reads such a header as null, and it is the state promoting the
22
- * last entry out of a block leaves behind.
20
+ * loads as declaring nothing rather than failing. YAML reads such a header as null, and it is the state left behind by
21
+ * promoting the last entry out of a block.
23
22
  *
24
- * A description may likewise be null, so a key written bare (`engineering/tooling:`) loads: that is what a hand editor
25
- * types and what a domain declared without a description round-trips to.
23
+ * Because a description may likewise be null, a key written bare (`engineering/tooling:`) loads: That is what a hand
24
+ * editor types and what a domain declared without a description round-trips to.
26
25
  */
27
26
  export declare const taxonomyFileShape: z.ZodObject<{
28
27
  domains: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodNullable<z.ZodString>>>>;
@@ -1,9 +1,8 @@
1
1
  import type { KbRoot } from '../types.js';
2
- /** A domain to declare. */
3
2
  export interface TaxonomyDeclaration {
4
3
  /** The domain's assertions-root-relative slash-path. */
5
4
  path: string;
6
- /** The one-line description; absent or empty writes the key bare, with no description. */
5
+ /** The one-line description; when absent or empty, `writeTaxonomy` writes the key bare, with no description. */
7
6
  description?: string;
8
7
  /** Whether to declare under `provisional:` rather than `domains:`. */
9
8
  provisional: boolean;
@@ -12,13 +11,13 @@ export interface TaxonomyDeclaration {
12
11
  * Declares domains in `.kb/taxonomy.yaml`, creating the file and either block as needed, and returns the paths added.
13
12
  *
14
13
  * Edits the parsed document rather than re-serializing a plain object, so existing comments, key order, and formatting
15
- * survive; a plain parse-and-stringify round trip would discard every comment in a hand-curated file. New keys append
16
- * to the end of their block in path order, and a block header left with nothing under it is filled in place rather
17
- * than moved.
14
+ * survive; a plain parse-and-stringify round trip would discard every comment in a hand-curated file. New keys
15
+ * are appended to the end of their block in path order, and a block header left with nothing under it is filled in
16
+ * place rather than moved.
18
17
  *
19
- * A path either block already declares is skipped rather than overwritten, so a repeat call adds nothing; when nothing
20
- * is left to add, the file is not opened for writing at all. The write goes through the shared `writeAtomic` helper,
21
- * so an interrupted call cannot truncate the taxonomy.
18
+ * A path already declared by either block is skipped rather than overwritten. A repeat call adds nothing, and when
19
+ * nothing is left to add, the file is not opened for writing at all. The write goes through the shared `writeAtomic`
20
+ * helper, so an interrupted call cannot truncate the taxonomy.
22
21
  *
23
22
  * Throws a {@link KbLoaderError} on a malformed key, or on an existing file that cannot be safely appended to: one
24
23
  * that fails to parse, one whose top level is not a mapping, and one whose `domains` or `provisional` block holds
@@ -4,7 +4,7 @@
4
4
  */
5
5
  export declare function isEnoent(error: unknown): boolean;
6
6
  /**
7
- * Returns true when `error` carries the given Node `code` string (e.g. `'ENOENT'`, `'EACCES'`).
7
+ * Returns true when `error` has the given Node `code` string (e.g. `'ENOENT'`, `'EACCES'`).
8
8
  * @internal
9
9
  */
10
10
  export declare function isErrorCode(error: unknown, code: string): boolean;