dsh-plugin-guide 0.3.31 → 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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-guide",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.32",
|
|
4
4
|
"description": "DSH plugin development as an on-demand agent skill: official docs mirror, version-compatibility matrix, migration seams, measured pitfalls, plus a scaffolder, checker and pack verifier CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -5,14 +5,17 @@
|
|
|
5
5
|
审计(`v2-2`、`v2-7`)建议剔除零引用内容以回收约 80% 体积。**精确复测后这个建议不成立**,
|
|
6
6
|
因为"零引用"是按**窄口径**统计的,而本库自己声明的路由是**宽 glob**。
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 复测数据(可复跑)
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
复现命令:`node scripts/measure-mirror-references.mjs`(随包发布,不需要网络)。
|
|
11
|
+
|
|
12
|
+
扫描 33 份自有文档,把镜像文件按"被引用强度"分档:
|
|
11
13
|
|
|
12
14
|
| 引用强度 | 文件数 | 体积 |
|
|
13
15
|
|---|---:|---:|
|
|
14
|
-
| 按确切路径被点名 |
|
|
15
|
-
| 仅被目录 glob 覆盖 |
|
|
16
|
+
| 按确切路径被点名 | 79 | 2.29 MB |
|
|
17
|
+
| 仅被目录 glob 覆盖 | 617 | 20.10 MB |
|
|
18
|
+
| 纯按 basename 命中 | 5 | 0.04 MB |
|
|
16
19
|
| **完全无引用** | **3** | **≈4 KB** |
|
|
17
20
|
|
|
18
21
|
仅被 glob 覆盖的部分按扩展名拆开:
|
|
@@ -20,10 +23,14 @@
|
|
|
20
23
|
| 扩展名 | 文件数 | 体积 |
|
|
21
24
|
|---|---:|---:|
|
|
22
25
|
| `.json`(历史 session-format schema) | 45 | **13.97 MB** |
|
|
23
|
-
| `.md` |
|
|
26
|
+
| `.md` | 356 | 5.19 MB |
|
|
24
27
|
| `.yaml`(`.i18n.yaml` 翻译哈希) | 212 | 0.54 MB |
|
|
25
28
|
| `.png` | 4 | 0.39 MB |
|
|
26
29
|
|
|
30
|
+
> **口径**:只有当 glob 目录是路径的**前缀**时才算覆盖(`docs/persistence-changes/**` 覆盖其下文件,
|
|
31
|
+
> 而裸 `docs/**` 不能推导出"`docs/` 下全部都被引用")。最初的审计按"段名出现在任意位置"匹配,
|
|
32
|
+
> 因此把镜像算成"绝大多数无引用"——**这正是本轮复测推翻它的地方**。
|
|
33
|
+
|
|
27
34
|
## 为什么不能按"零引用"删
|
|
28
35
|
|
|
29
36
|
本库**自己声明**的路由就是宽 glob,这些 glob 出现在 `SKILL.md` 与多份指南里:
|
|
@@ -68,5 +75,5 @@
|
|
|
68
75
|
**镜像保持逐字完整**。本文件的作用是把 R1 从"待清理"改判为
|
|
69
76
|
"**已评估、结论是不做,理由是被 glob 覆盖而非零引用**",避免下一个会话重复提出同一建议。
|
|
70
77
|
|
|
71
|
-
> 复跑方式:`node
|
|
72
|
-
>
|
|
78
|
+
> 复跑方式:`node scripts/measure-mirror-references.mjs`
|
|
79
|
+
> (脚本随包发布,在 `scripts/` 下;无需网络,不依赖任何一次性工作稿。)
|
|
@@ -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
|
+
}
|