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.
- package/README-es.md +28 -1
- package/README-hi.md +28 -1
- package/README-pt.md +28 -1
- package/README-zh.md +26 -1
- package/README.md +41 -11
- package/SKILL.md +3 -0
- package/dist/dsh-plugin-dev.js +19 -19
- package/guide/plugin-dev-guide.md +98 -2
- package/guide/quick-reference.es.md +2 -0
- package/guide/quick-reference.hi.md +2 -0
- package/guide/quick-reference.pt.md +2 -0
- package/package.json +2 -1
- package/references/community-repo-deep-dive.md +90 -125
- package/references/harness-repo.md +19 -0
- package/references/mirror-scope-decision.md +79 -0
- package/references/website-pages.md +5 -0
- package/scripts/check-readme-parity.mjs +149 -0
- package/scripts/check-readme-sync.mjs +14 -9
- package/scripts/measure-mirror-references.mjs +117 -0
|
@@ -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
|
+
}
|