dsh-plugin-guide 0.3.30 → 0.3.32

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.
@@ -0,0 +1,117 @@
1
+ // Measure how much of the offline mirror is actually referenced.
2
+ //
3
+ // `references/mirror-scope-decision.md` records a rejected proposal: drop the
4
+ // mirror content that no authored document references, to save ~80% of the
5
+ // published package. This script is the evidence. Run it before reviving that
6
+ // proposal — the answer depends on whether directory globs count as references,
7
+ // and this repository declares several broad ones.
8
+ //
9
+ // Usage: node scripts/measure-mirror-references.mjs [--json]
10
+
11
+ import { readdirSync, readFileSync, statSync } from 'node:fs'
12
+ import { join } from 'node:path'
13
+
14
+ const root = process.cwd()
15
+ const mirrorRoot = join(root, 'references', 'official-docs')
16
+ const json = process.argv.includes('--json')
17
+
18
+ /** Documents that count as "the knowledge base" when looking for references. */
19
+ function authoredFiles() {
20
+ const out = []
21
+ const add = (p) => {
22
+ try { if (statSync(p).isFile()) out.push(p) } catch { /* optional */ }
23
+ }
24
+ add(join(root, 'SKILL.md'))
25
+ add(join(root, 'llms.txt'))
26
+ for (const dir of ['guide', 'references']) {
27
+ try {
28
+ for (const f of readdirSync(join(root, dir))) add(join(root, dir, f))
29
+ } catch { /* optional */ }
30
+ }
31
+ for (const f of readdirSync(root)) if (/^README.*\.md$/.test(f)) add(join(root, f))
32
+ return out.filter((p) => !p.includes('official-docs') && !p.includes('mirror-scope'))
33
+ }
34
+
35
+ const authored = authoredFiles()
36
+ const corpus = authored.map((p) => readFileSync(p, 'utf8'))
37
+
38
+ // Everything the corpus names: full paths, bare basenames, and directory globs.
39
+ const paths = new Set()
40
+ const names = new Set()
41
+ const globDirs = new Set()
42
+ for (const text of corpus) {
43
+ for (const m of text.matchAll(/references\/official-docs\/([A-Za-z0-9._/-]+)/g)) paths.add(m[1])
44
+ for (const m of text.matchAll(/\bdocs\/[A-Za-z0-9._/-]+/g)) paths.add(m[0])
45
+ for (const m of text.matchAll(/([A-Za-z0-9._-]+)\.(md|json|ya?ml|png)\b/g)) names.add(`${m[1]}.${m[2]}`)
46
+ for (const m of text.matchAll(/([A-Za-z0-9._/-]+)\/\*\*/g)) globDirs.add(m[1])
47
+ }
48
+
49
+ function walk(dir, base = '') {
50
+ const out = []
51
+ for (const e of readdirSync(dir, { withFileTypes: true })) {
52
+ const rel = base ? `${base}/${e.name}` : e.name
53
+ if (e.isDirectory()) out.push(...walk(join(dir, e.name), rel))
54
+ else out.push(rel)
55
+ }
56
+ return out
57
+ }
58
+
59
+ /**
60
+ * How the corpus reaches this mirror path.
61
+ *
62
+ * A glob only covers a path when the glob directory is a PREFIX of it:
63
+ * `docs/persistence-changes/**` covers `docs/persistence-changes/x.json`, but a
64
+ * bare `docs/**` does not imply everything under `docs/`. Matching a segment name
65
+ * anywhere inflates the referenced count and produced a wrong "80% unreferenced"
66
+ * figure in the original audit.
67
+ */
68
+ function referencedBy(rel) {
69
+ if (paths.has(rel) || paths.has(`docs/${rel}`)) return 'exact'
70
+ const parts = rel.split('/')
71
+ for (let i = 1; i <= parts.length; i++) {
72
+ if (globDirs.has(parts.slice(0, i).join('/'))) return 'dir-glob'
73
+ }
74
+ if (names.has(parts[parts.length - 1])) return 'basename'
75
+ return null
76
+ }
77
+
78
+ const SKIP = new Set(['README.md', 'SNAPSHOT.md', 'LICENSE', 'CLAUDE.md'])
79
+ const buckets = { exact: [], 'dir-glob': [], basename: [], unreferenced: [] }
80
+ for (const rel of walk(mirrorRoot).sort()) {
81
+ if (SKIP.has(rel)) continue
82
+ const size = statSync(join(mirrorRoot, rel)).size
83
+ buckets[referencedBy(rel) ?? 'unreferenced'].push({ rel, size })
84
+ }
85
+
86
+ const MB = (n) => (n / 1048576).toFixed(2) + ' MB'
87
+ const sum = (a) => a.reduce((s, x) => s + x.size, 0)
88
+
89
+ if (json) {
90
+ console.log(JSON.stringify({
91
+ authoredDocuments: authored.length,
92
+ buckets: Object.fromEntries(Object.entries(buckets).map(([k, v]) => [k, { files: v.length, bytes: sum(v) }])),
93
+ }, null, 2))
94
+ } else {
95
+ console.log(`authored documents scanned: ${authored.length}`)
96
+ console.log('\nmirror files by reference strength:')
97
+ for (const k of ['exact', 'dir-glob', 'basename', 'unreferenced']) {
98
+ console.log(` ${k.padEnd(14)} ${String(buckets[k].length).padStart(4)} files ${MB(sum(buckets[k])).padStart(10)}`)
99
+ }
100
+ const unreferenced = buckets.unreferenced
101
+ console.log(`\ngenuinely unreferenced: ${unreferenced.length} files, ${MB(sum(unreferenced))}`)
102
+ for (const x of unreferenced) console.log(` ${MB(x.size).padStart(10)} ${x.rel}`)
103
+
104
+ const byExt = new Map()
105
+ for (const x of buckets['dir-glob']) {
106
+ const ext = x.rel.slice(x.rel.lastIndexOf('.'))
107
+ const g = byExt.get(ext) ?? { n: 0, b: 0 }
108
+ g.n++
109
+ g.b += x.size
110
+ byExt.set(ext, g)
111
+ }
112
+ console.log('\nreachable only through a declared directory glob, by extension:')
113
+ for (const [ext, g] of [...byExt].sort((a, b) => b[1].b - a[1].b)) {
114
+ console.log(` ${ext.padEnd(12)} ${String(g.n).padStart(4)} files ${MB(g.b).padStart(10)}`)
115
+ }
116
+ console.log('\nSee references/mirror-scope-decision.md before acting on these numbers.')
117
+ }