@sdsrs/llm-wiki 0.7.3 → 0.7.4

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/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.4 (2026-07-12)
4
+
5
+ Two robustness fixes surfaced by an adversarial QA pass over the CLI. No config
6
+ schema change, no KB migration, no retrieval-default change. Suite 230 → 234.
7
+
8
+ **Fixes:**
9
+
10
+ - **`index` warns when a page is skipped for invalid frontmatter.** `buildIndex`
11
+ silently excluded any page with unparseable YAML frontmatter from
12
+ `index.md`/`graph.json`/`llms.txt`. Because the docs route hand-editors to
13
+ `index` (not `lint`) after editing, a broken-YAML edit made the page vanish
14
+ from every query surface with no signal — `indexed N pages`, exit 0, nothing
15
+ else. `buildIndex` now returns the skipped pages and the CLI prints a one-line
16
+ stderr warning naming them and pointing at `lint`; stdout stays the clean
17
+ count, and `lint` still owns the detailed per-page report.
18
+ - **`scan` near-duplicate detection uses 128 minhash permutations (was 32).**
19
+ Near-dup similarity was estimated from a 32-permutation minhash and compared
20
+ against the 0.85 threshold, but at 32 perms the estimate's standard error
21
+ (~0.05) let a genuine near-duplicate (true Jaccard 0.898) estimate 0.844 and
22
+ be reported as `near: 0`. 128 perms tightens the estimate (SD ~0.026) so real
23
+ near-dups clear the threshold; the cost is trivial next to the per-file
24
+ sha256+read scan already does, and the signature is stripped before the plan
25
+ is persisted (no on-disk change).
26
+
3
27
  ## 0.7.3 (2026-07-12)
4
28
 
5
29
  Two robustness fixes from the final targeted dogfooding sweep. No config schema
package/bin/llm-wiki.mjs CHANGED
@@ -65,6 +65,12 @@ program.command('index')
65
65
  .action((opts) => {
66
66
  const r = buildIndex(opts.kb)
67
67
  console.log(`indexed ${r.pageCount} pages${r.topicsSplit ? ' (split into topics/)' : ''}`)
68
+ // A page with unparseable frontmatter is excluded from index.md/graph.json/llms.txt. Surface
69
+ // it on stderr (stdout stays the clean count) so a hand-edit that broke the YAML doesn't
70
+ // silently vanish — the README points hand-editors here, not at `lint`.
71
+ if (r.skipped?.length) {
72
+ console.error(`warning: skipped ${r.skipped.length} page(s) with invalid frontmatter (run 'lint' for details): ${r.skipped.map(s => s.relPath).join(', ')}`)
73
+ }
68
74
  })
69
75
 
70
76
  program.command('embed')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sdsrs/llm-wiki",
3
- "version": "0.7.3",
3
+ "version": "0.7.4",
4
4
  "description": "Compile messy document directories into Karpathy-style llm_wiki knowledge bases — standalone Q&A CLI + Claude Code/Codex skills",
5
5
  "directories": {
6
6
  "test": "test"
package/src/hashing.mjs CHANGED
@@ -13,7 +13,12 @@ function fnv1a(str) {
13
13
  return h
14
14
  }
15
15
 
16
- export function minhashSignature(text, perms = 32) {
16
+ // 128 permutations, not 32: the near-dup estimate's standard error is ~sqrt(J(1-J)/perms),
17
+ // so at 32 perms a genuine near-duplicate (true Jaccard ~0.90) estimated as low as 0.84 and
18
+ // fell below NEAR_DUP_THRESHOLD (0.85), silently missing it (QA73-002). 128 perms tightens the
19
+ // estimate (SD ~0.026) so real near-dups clear the threshold; the cost is trivial next to the
20
+ // per-file sha256+read scan already does, and `_sig` is stripped before the plan is persisted.
21
+ export function minhashSignature(text, perms = 128) {
17
22
  const norm = text.toLowerCase().replace(/\s+/g, ' ')
18
23
  const shingles = new Set()
19
24
  for (let i = 0; i <= norm.length - 5; i++) shingles.add(norm.slice(i, i + 5))
package/src/indexer.mjs CHANGED
@@ -26,7 +26,13 @@ const inline = (s) => String(s ?? '').replace(/[\r\n]+/g, ' ')
26
26
  export function buildIndex(kbRoot) {
27
27
  const p = kbPaths(kbRoot)
28
28
  const cfg = loadKbConfig(kbRoot)
29
- const pages = listWikiPages(kbRoot).filter(pg => !pg.error)
29
+ const allPages = listWikiPages(kbRoot)
30
+ const pages = allPages.filter(pg => !pg.error)
31
+ // Pages with unparseable/missing frontmatter are excluded from every derived file. Return
32
+ // them so the CLI can warn instead of silently dropping a page — the README routes
33
+ // hand-editors to `index` (not `lint`), so a bad edit would otherwise vanish with no signal
34
+ // (QA73-001). lint still owns the detailed per-page report.
35
+ const skipped = allPages.filter(pg => pg.error).map(pg => ({ relPath: pg.relPath, error: pg.error }))
30
36
 
31
37
  // Preserve the pending section, bounded at the next `## ` heading — a greedy
32
38
  // match to EOF would swallow user-added sections into "pending" forever.
@@ -122,5 +128,5 @@ export function buildIndex(kbRoot) {
122
128
  ...pages.filter(pg => !isInvalidated(pg)).map(pg => `- [${inline(pg.data.title)}](wiki/${pg.relPath}): ${inline(pg.data.description)}`)].join('\n') + '\n'
123
129
  writeFileAtomic(p.llmsTxt, llms)
124
130
 
125
- return { pageCount: pages.length, topicsSplit }
131
+ return { pageCount: pages.length, topicsSplit, skipped }
126
132
  }