nx-sk 0.11.0 → 0.14.0

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 (97) hide show
  1. package/CHANGELOG.md +231 -0
  2. package/README.md +92 -41
  3. package/assets/kv-source/SKILL.md +166 -0
  4. package/assets/kv-source/references/10-sync.md +155 -0
  5. package/assets/kv-source/references/20-intake.md +77 -0
  6. package/assets/nx-sk/SKILL.md +83 -4
  7. package/assets/nx-sk/references/00-design.md +2 -2
  8. package/assets/nx-sk/references/10-sections-entries.md +36 -3
  9. package/assets/nx-sk/references/20-secrets.md +12 -6
  10. package/assets/nx-sk/references/40-files.md +4 -2
  11. package/assets/nx-sk/references/50-deploy.md +4 -2
  12. package/assets/nx-sk/references/60-transfer.md +68 -28
  13. package/assets/sk-needdo/SKILL.md +152 -0
  14. package/assets/sk-needdo/references/10-inbox.md +64 -0
  15. package/assets/sk-needdo/references/20-schema.md +108 -0
  16. package/assets/sk-needdo/references/30-fill.md +105 -0
  17. package/assets/sk-needdo/references/40-pitfalls.md +68 -0
  18. package/assets/sk-needdo/references/50-example.md +140 -0
  19. package/assets/sk-pdf/SKILL.md +212 -0
  20. package/assets/sk-pdf/references/10-extract.md +69 -0
  21. package/assets/sk-pdf/references/20-sop-schema.md +108 -0
  22. package/assets/sk-pdf/references/30-pitfalls.md +89 -0
  23. package/assets/sk-pdf/scripts/_deps.py +33 -0
  24. package/assets/sk-pdf/scripts/pdf_extract.py +144 -0
  25. package/assets/sk-pdf/scripts/pdf_probe.py +84 -0
  26. package/assets/sk-thesis/SKILL.md +58 -0
  27. package/assets/sk-thesis/references/10-format.md +97 -0
  28. package/assets/sk-thesis/references/20-structure.md +82 -0
  29. package/assets/sk-thesis/references/30-pitfalls.md +91 -0
  30. package/assets/sk-thesis/scripts/make_thesis_template.py +987 -0
  31. package/assets/sk-thesis/templates//346/257/225/344/270/232/350/256/272/346/226/207/346/250/241/346/235/277-/350/256/276/350/256/241/345/274/200/345/217/221/347/261/273.docx +0 -0
  32. package/package.json +6 -2
  33. package/src/core/attachments.js +41 -16
  34. package/src/core/fields.js +189 -4
  35. package/src/core/paths.js +4 -2
  36. package/src/core/render.js +36 -2
  37. package/src/core/securezip.js +72 -0
  38. package/src/core/source.js +190 -0
  39. package/src/core/store.js +4 -46
  40. package/src/core/vault.js +9 -2
  41. package/src/index.js +1 -2
  42. package/src/modules/entries/index.js +104 -0
  43. package/src/modules/entries/kvTable.jsx +1 -1
  44. package/src/modules/entries/service.js +514 -18
  45. package/src/modules/entries/view.jsx +186 -32
  46. package/src/modules/export/index.js +9 -34
  47. package/src/modules/export/view.jsx +13 -152
  48. package/src/modules/files/service.js +10 -13
  49. package/src/modules/files/view.jsx +4 -4
  50. package/src/modules/sections/service.js +33 -16
  51. package/src/modules/sections/view.jsx +55 -4
  52. package/src/modules/settings/service.js +3 -8
  53. package/src/modules/settings/view.jsx +1 -43
  54. package/src/modules/system/service.js +1 -2
  55. package/src/modules/transfer/index.js +19 -12
  56. package/src/modules/transfer/service.js +231 -78
  57. package/src/runtime/cli.js +11 -4
  58. package/src/runtime/registry.js +1 -2
  59. package/src/runtime/spec.js +13 -2
  60. package/src/web/frontend/App.jsx +10 -3
  61. package/src/web/frontend/activeView.js +2 -2
  62. package/src/web/frontend/api/zip.js +12 -0
  63. package/src/web/frontend/components/fieldEditor.jsx +145 -89
  64. package/src/web/frontend/components/mdView.jsx +193 -0
  65. package/src/web/frontend/components/navModel.js +10 -0
  66. package/src/web/frontend/components/sourcePanel.jsx +359 -0
  67. package/src/web/frontend/components/transferPanel.jsx +316 -116
  68. package/src/web/frontend/components/ui.jsx +13 -7
  69. package/src/web/frontend/mdInline.js +74 -3
  70. package/src/web/frontend/mdSmart.js +164 -0
  71. package/src/web/frontend/mdSource.js +41 -0
  72. package/src/web/frontend/registry.js +1 -4
  73. package/src/web/frontend/styles.css +318 -11
  74. package/src/web/public/assets/{files-DuwKZ5bu.js → files-D5FYZdp3.js} +1 -1
  75. package/src/web/public/assets/index-CK6204A2.js +41 -0
  76. package/src/web/public/assets/index-DtcEFmKx.css +1 -0
  77. package/src/web/public/assets/view-BeNH5xFQ.js +1 -0
  78. package/src/web/public/assets/view-BkhydqIS.js +1 -0
  79. package/src/web/public/assets/view-DSR4LaGb.js +1 -0
  80. package/src/web/public/assets/view-DaUnjbX8.js +35 -0
  81. package/src/web/public/assets/view-NWwrB4dg.js +1 -0
  82. package/src/web/public/assets/zip-gQ69BVQr.js +1 -0
  83. package/src/web/public/index.html +2 -2
  84. package/assets/nx-sk/references/30-export-backup.md +0 -104
  85. package/src/modules/backup/index.js +0 -46
  86. package/src/modules/backup/service.js +0 -678
  87. package/src/modules/backup/view.jsx +0 -251
  88. package/src/modules/export/service.js +0 -192
  89. package/src/web/public/assets/index-C8-8IXxK.css +0 -1
  90. package/src/web/public/assets/index-CyHzNRPd.js +0 -41
  91. package/src/web/public/assets/view-B2C1AKyl.js +0 -7
  92. package/src/web/public/assets/view-B3dDfoL5.js +0 -1
  93. package/src/web/public/assets/view-BDMftvuZ.js +0 -7
  94. package/src/web/public/assets/view-B_XDtZn-.js +0 -1
  95. package/src/web/public/assets/view-CC3A5XfV.js +0 -1
  96. package/src/web/public/assets/view-MdIYFfFy.js +0 -5
  97. package/src/web/public/assets/zip-CS8JXp8H.js +0 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,237 @@
2
2
 
3
3
  本文件格式参考 [Keep a Changelog](https://keepachangelog.com/),中文正文。
4
4
 
5
+ ## [Unreleased]
6
+
7
+ ## 0.15.0 (2026-10-20)
8
+
9
+ **原始资料袋:每个栏目都自带一个「乱入区」**
10
+
11
+ ### Added
12
+
13
+ - **毕业论文栏目模板(`section add --template thesis`)+ 随包 skill `sk-thesis`**:把信息工程学院
14
+ 2022 版撰写规范(设计开发类)做成 9 组 / 59 个字段——页面与字体硬指标、章节结构、图表与三线表、
15
+ 代码要求、参考文献(GB/T 7714-2015)、打印装订与日期红线,外加一份可直接勾选的「交稿自检」清单。
16
+ 配套 `assets/sk-thesis/templates/毕业论文模板-设计开发类.docx`:A4、页边距 2.5/2/2.5/2 cm、
17
+ 1.5 倍行距、宋体小四、标题黑体四号 / 黑体小四 / 宋体小四、三线表 1.5 磅 / 1 磅、目录域、
18
+ 分节页码(封面与目录不编、标题页起为 1),关键位置带 23 条格式批注。
19
+ `scripts/make_thesis_template.py` 可重新生成;`references/10-format` 是数值速查,
20
+ `20-structure` 是章节骨架与装订顺序,`30-pitfalls` 记录真实错法(一级标题加粗、
21
+ 图题「图 1」多空格、(1)用自动编号带悬挂缩进、批注未删导致 PDF 整页缩到 74%)。
22
+
23
+ - **原始资料袋(`source` 字段):每个栏目都自带一个的「乱入区」**——杂乱资料(邮件、截图、
24
+ PDF 摘录、手抄的表)先**原样**收进袋子,之后由 agent 或人按栏目字段字典整理成结构化字段。
25
+ 「先乱入、后整理」因此成立:没有这个袋子,用户只有把乱数据硬塞进结构化字段(把结构搞脏)
26
+ 或干脆不存(信息丢了)两个坏选择。
27
+ - **面板**:条目表单底部一条折叠条「原始资料 · N 条」,点开是右侧抽屉——左清单、中正文,
28
+ 文件拖进去即入袋(同时给了「选择文件」按钮与粘贴框:WCAG 2.2 AA 要求拖拽不能是唯一入口)。
29
+ 抽屉内焦点有可见指示器(2px 描边),关闭后焦点归还折叠条。
30
+ - **CLI**:`source list / get / add / update / remove / ensure`;`--file` 先进附件库、
31
+ **原件保留不搬走**(「加一条资料」不该搬用户本机的文件,那是 `file add` 的语义)。
32
+ - **双向同步**:整理完用 `source update --mapped 字段key` 给用上的资料打锚点,
33
+ 字段值也可回写进袋子留出处——「哪个字段的值来自哪条原始资料」永远反查得回来。
34
+ - 三条纪律写进代码与文档:**原文不改写**、**不确定就留空**、**敏感内容不进袋**(袋是明文的,
35
+ 密码走「密钥」栏目或 `sensitive` 字段);袋**不计完整度**(暂存区不是「该填的格子」)。
36
+ - 新栏目(模板或自定义)都自带这个字段;老栏目用 `nx-sk source ensure` 一次补齐。
37
+ - **`kv-source` skill**(随包分发,`nx-sk skill install kv-source`):讲透「原始资料 ↔ 字段」
38
+ 的双向同步、映射表纪律(把握不足就不写)、以及各种文件(PDF/截图/邮件/表格)怎么进袋。
39
+
40
+ ### Fixed
41
+
42
+ - **数组型 flag 的逗号没被切开**:`--unset a,b`、`--tags a,b` 会把整串当成**一个**值
43
+ (CLI 解析层把 array flag 收成数组,而强转那一步见数组就跳过了切分)——
44
+ `--unset a,b` 直接报「没有字段: a,b」。现在每个元素都再按逗号切一次。
45
+
46
+ ## 0.14.0 (2026-10-19)
47
+
48
+ **统一列表机制:跨栏目关键 key 台账 + 容器平台字段模板 + 结构清单**
49
+
50
+ ### Added
51
+
52
+ - **关键 key 台账(`nx-sk entry keys [--reveal]`)**:跨栏目聚合 链接 / 账号 / 密码 / 镜像 / token 等
53
+ 关键字段统一列表——不管是 job/school 还是自己手搓的「容器平台」栏目里的 accessUrl / account /
54
+ password 字段,全部进同一张台账。值默认打码(链接类保留原文便于点击;账号/密码/token 一律掩码);
55
+ `--reveal` 出明文(密文字段走解密)。给 agent 一句「把川农控制台链接和账号密码贴出来」就能直接拿到。
56
+ - **容器平台模板(`nx-sk section add --template platform`)**:一个平台(KubeSphere / Rancher / 自建 k8s…)
57
+ 一个条目,字段:基本信息(平台名/提供方/环境)/ 访问凭据(控制台链接/账号/密码[密文])/ 镜像与部署
58
+ (项目命名空间/仓库/镜像名/部署清单附件)/ 网络入口(应用入口链接)/ 备注。
59
+ 配合 `entry keys` 把分散在每个平台条目里的链接/账号/密码自动汇总成台账。
60
+ - **条目页左右栏可拖宽**:条目列表 ↔ 内容之间加了分隔把手(骑在原 14px 间隙上,间距节奏不变),
61
+ 拖动即调整左栏宽度(240–560px 夹紧),双击恢复默认 300px;宽度记在 `ui.listW`
62
+ (localStorage),跨会话保留。拖动过程只改本地 state,松手才落盘,不因 mousemove 重渲染表单。
63
+
64
+ - **条目定位面包屑:点击即复制,方便把「改哪个条目」一键贴给 agent**。条目列表每行悬浮出 ⧉
65
+ (hover 才显示,不打扰),右侧详情头的「栏目 › 条目 ⧉」整条可点;复制出的定位同时带
66
+ 人读路径(栏目「标题」(id) › 条目「标题」(id))与可直接执行的 CLI
67
+ (`entry get` / `entry update --section <栏目id> <条目id>`)——标题可能重名或被改,
68
+ 定位与命令一律用 id,agent 拿到就能精确动手,不用再猜「是哪一条」。
69
+
70
+ - **`entrylist` 字段类型:结构清单(变长行)**——一个字段的值是**行对象的数组**,行数不限;
71
+ 列(子字段)在字段定义的 `fields` 里声明。为「一个条目挂多组 链接/账号/密码」这类密集结构而生:
72
+ tags 存不了结构,拆成多个字段又不知道会有几组。
73
+ - 面板渲染成**密集迷你表**(列头 + 行输入框,直接增删行);「新增字段」选「结构清单(变长行)」
74
+ 预填 名称 / 链接 / 账号 / 密码(密文) 四列,列可增删、可勾「密文」。
75
+ - 敏感列(`sensitive: true`)与顶层敏感字段同一条边界:AES-256-GCM 密文落盘、默认读原文、
76
+ `--mask` 打码、打码占位原样回传保留原密文(想改密码却只拷了打码值会当场报错);
77
+ ZIP 导出前同样把清单里的密文解开。
78
+ - CLI 写入用 JSON:`--data '{"账号清单":[{"名称":"…","链接":"https://…"}]}'`(`--set` 认 JSON 串,
79
+ 空串 = 清空);没登记过的键直接写行数组会自动登记成 entrylist 并从行推断列;
80
+ `entry get` 里清单一行一条、带列标签;模板漂移把列定义也算进去(列变了 sync 会报)。
81
+
82
+ - **`sk-pdf` skill:把 PDF 转成图文 SOP 栏目**(随包分发,`nx-sk skill install sk-pdf`)。
83
+ 解决「照着一份 PDF 手册把流程沉淀进 nx-sk」这件事每次都要重新摸索的问题 ——
84
+ 流程、脚本、坑都固化下来了。
85
+ - 两个脚本(唯一第三方依赖 PyMuPDF,其余全是标准库):
86
+ `pdf_probe.py` 先探查(页数 / 每页图数 / 图片尺寸分布 / 是不是扫描件,
87
+ 图 ≤5 张就别走图文流程);`pdf_extract.py` 出**带图片占位符的 markdown** + 导出图片。
88
+ - **按版面坐标(y)排序拼图文**,不按页码也不按 xref 顺序 —— 这是最关键的一点:
89
+ 顺序错了文字全对而截图全错位,用户照着图文对不上完全做不下去。
90
+ - 图片编号 `页码_页内序号`(如 `006_1`),挑图时用编号对话不易错,事后可回溯来源。
91
+ - **技术栈(实测后写定,不是猜的)**:只用 **PyMuPDF** 一个第三方库,其余全是标准库;
92
+ Pillow / pypdf / pdfplumber 实测都不需要,不装(少一个依赖就少一处「换机器跑不起来」)。
93
+ 选 PyMuPDF 的唯一理由是它能给 `get_image_rects()`(图片在页面上的**摆放矩形**)——
94
+ 图文拼接靠的就是 y 坐标,别的库给不出位置就不适用。
95
+ 缺依赖时脚本会打印**可操作提示**(装什么、装到哪、Windows 上装完还报错多半是解释器指错了),
96
+ 而不是只丢一句 `No module named`。
97
+ - 栏目结构模板:简介 / **准备材料** / 入口与地址 / 操作手册 / 常见问题 / 自检清单,
98
+ 以及账号密码各自一个 `sensitive` 字段、正文只写「见〈某字段〉」。
99
+ - `30-pitfalls` 记的是真做一份 52 页手册时踩到的:51 张图只该留 18 张、
100
+ 占位符忘换附件 id、本地路径渲染不出、`file add` 默认移动原件、
101
+ 扫描件抽出来是空、md 图片曾被附件中心误判「未引用」。
102
+ - 已端到端验证:探查 → 抽图 48 张 → 上传 → 建栏目/字段/条目 → 写 md →
103
+ `file get` 的 `usedBy` 正确指向本条目、未引用数 = 0。
104
+ - 主 skill 补了 `md` 字段与图片引用的说明(此前完全没提,是缺口)。
105
+
106
+
107
+ ### Fixed
108
+
109
+ - **短明文打码**:原规则长度 ≤ 12 的值直接显示全明文——账号 14611 整条明文。现在
110
+ 账号/密码/token/secret/身份证/电话/银行等敏感类字段一律 `maskValue`(maskValue('14611')='**');
111
+ 链接/镜像等 URL 类仍明文。
112
+ - **关键 key 字段识别**:从文件名启发式改为 key/label 文本匹配(`account|password|token|...`),不再
113
+ 依赖文件名含数字(`sqlh` 等字母名字段也能正确入台账)。
114
+ - **md 正文里的图片在附件中心被显示成「未引用」**:`fileReferences` 只扫 `type: 'file'` 的字段,
115
+ 完全不看 `md` 字段正文里的 `![说明](f_xxx)` —— 而图片是内嵌在 markdown 里的正式引用。
116
+ 实际影响:一份 18 张截图的 SOP 容器平台栏目,全部 18 张图都被判成孤儿,
117
+ 「未引用」筛选会把它们捞出来,看着像附件丢了。
118
+ - 现在 `file` 与 `md` 两种引用都反查,并给出栏目 / 条目 / 字段的完整定位。
119
+ - `collectFileSections` 同步放开:只有 `md` 字段的栏目也出现在附件中心的栏目筛选里
120
+ (原先只有 `file` 字段的栏目才出现,按栏目筛选时根本找不到 md 图片所在栏目)。
121
+ - **顺带修掉一个更隐蔽的坑**:命中判定原先是 `findFileRecord(store, id) === rec`
122
+ (对象引用相等)。调用方拿到的通常是 `listFiles` 加工过的**副本**,引用不同 →
123
+ 静默判成「未引用」。改成按 id / 唯一文件名判定,副本传参也能命中。
124
+
125
+ ## [0.13.0] - 2026-10-18
126
+
127
+ > **BREAKING**:`nx-sk export run` / `export list`(可读导出 JSON / Markdown)已移除。
128
+ > 导出现在**只有 `nx-sk transfer` 一条路径**(`export` / `import` / `bundle-export` /
129
+ > `bundle-import` / `import-new`)。详见 [Removed](#removed)。
130
+
131
+ ### Removed
132
+
133
+ - **可读导出(JSON / Markdown)整体删除**:`nx-sk export run` / `export list`、
134
+ `POST /api/export`、`GET /api/exports`、`components/readableExport.jsx`、
135
+ `store.exports`(历次导出历史)与 `MAX_EXPORT_HISTORY` 全部移除。
136
+ 它和备份是同一类东西 —— 「往本机目录再写一份副本」,而 ZIP 导出已经把这件事做完了,
137
+ 两套口径并存只会漂移(谁也不知道该看哪一份)。
138
+ - 设置页移除:可读导出面板(格式 / 输出目录 / 凭据开关 / 历次导出 / 目录内文件),
139
+ 以及「默认导出格式」「默认导出目录」「导出默认带明文密钥」三项设置;
140
+ `SETTING_KEYS` 里的 `exportDir` / `exportFormat` / `includeSecretsInExport` 一并去掉。
141
+ - 栏目页「更多」里的「导出 JSON / Markdown」菜单项移除(ZIP 导出 / 导入不动)。
142
+ - **「导出导入」页与 transfer 五条命令完全不动** —— 那是本项目唯一的导出路径。
143
+
144
+ ## [0.12.0] - 2026-10-18
145
+
146
+ > **BREAKING**:`nx-sk backup *` 命令与 `.nxskbak` 格式已移除。改用 `nx-sk transfer export` /
147
+ > `transfer import-new`。旧的 `.nxskbak` 文件不再可导入,请在本版发布前用 0.11.x 导出一份。
148
+ > 详见 [Removed](#removed)。
149
+
150
+ ### Added
151
+
152
+ - **导出包可设口令加密**(按栏目 ZIP 与全局 bundle 都支持):导出选项里填口令(输两次),
153
+ 下载的包整体 AES-256-GCM 加密。密钥由口令经 scrypt 派生,与密钥库同一套参数;
154
+ 每次导出盐与 IV 都重新随机 —— 同一个包加密两次密文不同。
155
+ - 导入是同一个入口:自动识别加密包,**先问口令**再预览 —— 不会把「缺口令」
156
+ 误报成「包坏了」。口令错 / 内容被改都在 GCM 认证阶段报错,不会解出垃圾。
157
+ - CLI:`--passphrase`,也认环境变量 `NX_SK_TRANSFER_PASSPHRASE`(命令行口令会进进程列表)。
158
+ - **导入预览列出冲突明细**:哪条撞上哪条(同 id / 同标题)、另存会变成什么标题
159
+ (选「另存」后实时显示 `→ 甲(导入)」)。单栏目与整库预览口径一致。
160
+ - **零依赖 ZIP 实现**(`src/core/zip.js`:store/deflate、CRC32、路径安全校验),
161
+ 不引第三方压缩库就能产出合法的跨平台 ZIP。
162
+ - **图文(`md`)正文改用 markdown 组件渲染**(新增依赖 `react-markdown` + `remark-gfm` + `remark-breaks`,
163
+ 新组件 `web/frontend/components/mdView.jsx`):标题、有序/无序列表、表格、代码块、引用、删除线、
164
+ 裸网址自动链接都按标准 markdown 渲染。此前手写解析只认图片与链接,SOP 里的结构在预览里全是字面量。
165
+ - **待办可点**:`- [ ] 步骤` 在预览里就是勾选框,点一下写回正文的 `[x]`(`mdSource.toggleTaskAtLine`)。
166
+ 按解析器给的**行号**定位,不是「第几个勾选框」——嵌套列表与有序列表都不会串行;
167
+ 勾选状态只有正文一份,不另存,所以导出、CLI 读到的就是勾完的样子。
168
+ - **图片与链接的行为保持不变**:`![说明](f_xxx)` 仍走附件库 + 放大镜(`[说明](f_xxx)` 直接打开附件);
169
+ 只放行 http(s) 与能查到的附件 id,其余按普通文字渲染,`javascript:` 不会被执行。
170
+ - **裸地址自动识别**(`web/frontend/mdSmart.js`,remark 插件,排在 gfm 之后):
171
+ `125.67.197.2:7443`、`10.0.0.1`、`localhost:7866` 渲染成虚下划线的「点击即复制」;
172
+ `qcloud.sicau.edu.cn/login`、`example.com:8080/a?b=1` 自动补 `http://` 后点击打开。
173
+ 宁可严:TLD 黑名单(`config.json`、`README.md` 这类文件名不会被当成域名)、端口 1–65535 校验、
174
+ 前面是字母/数字/`@`/`/`/`:` 时不算起点(不在已有 URL、邮箱、路径里再识别一次);
175
+ 代码块与行内代码原样保留。工具栏统计同步区分「链接」与「IP」。
176
+ - **补掉解析器带进来的两个坑**:GFM 自动链接一直吃到空白,中文句子又不用空格,
177
+ `见 https://a.com。另见` 会被整段算进 href(点开 404)—— `cleanHttpUrl` / `linkTextParts`
178
+ 按「解码后遇到中文标点即断」截断,断下来的标点回到正文;`remark-breaks` 把单个换行渲染成 `<br>`,
179
+ 保住手写那版 `pre-wrap` 的「一行一步」手感,已有 SOP 不会挤成一整段。
180
+ - 统计与下方图库仍走 `mdInline.js`(只读解析,不参与渲染);新增 `tests/unit/md-source.test.mjs`,
181
+ `md-inline.test.mjs` 补了 URL 截断的用例。
182
+
183
+ ### Changed
184
+
185
+ - **加密包改为「内部数据先加密,再套合法 ZIP」**:之前整个文件是密文,Windows 会报
186
+ 「压缩(zipped)文件夹错误」。现在外层始终是标准 ZIP(内含 `payload.enc` + `README.txt`),
187
+ 系统压缩工具能正常打开,只看到一个看不懂的文件;判定是否加密也只需看条目名,
188
+ 不用嗅探 magic。旧 `NXSKENC1` 整文件密文**仍可导入**,只是不再产出。
189
+ - **去掉「打码 / 明文」二选,口令即保护**(BREAKING,flag 层):`--no-secrets` 移除。
190
+ 要分享就设口令,不设口令的包自己收好。读取侧仍兼容 0.11.0 及更早的打码包
191
+ (遇到 `******` 跳过该字段并计数,不会把打码串当新密码存进去)。
192
+ - **「按栏目 / 整库备份」两个 tab 合并成一个面板**:栏目的全选就是整库导出,
193
+ 「整库」按钮只是全选的快捷方式,产物与逐栏勾选是同一个包、同一套导入逻辑。
194
+ - **导出包支持 `--no-files`**:只带元数据不带附件原件,跨机传配置时省体积。
195
+ - **`transfer import-new` 自动识别包结构**:不用手动挑单栏目导入还是整库导入。
196
+ - **面板重排(UI)**:左右两栏(左 = 选内容 + 设口令,右 = 解密导入);
197
+ 栏目横向 chips,**指针移上去自动展开该栏目条目**(点击/键盘同样可用,不只靠悬浮),
198
+ 「整库 / 清空」预设;口令与「导出 ZIP」提到卡片头部常驻,条目再多也不用滚动找按钮。
199
+ chips 用原生 `<button>` + `aria-pressed`/`aria-expanded`,长标题省略号 + title 兜底。
200
+ - **附件中心分页**:面板每页 24 个,避免一次加载全部图片把浏览器撑爆;
201
+ CLI 默认仍全量返回。
202
+
203
+ ### Fixed
204
+
205
+ - **头部导航的分组角标显示成了条目数之和**:「求职资料」分组只有 6 个栏目,角标却是 16 ——
206
+ 那是组内所有栏目的条目数加总。角标紧挨组名,本该回答「这一坨里有多少个栏目」;
207
+ 加总出来的数字看着像「这个分组本身是个栏目、底下挂着 16 条记录」,而展开后每行
208
+ 又各有自己的条目数,既冗余又误导。现在显示**栏目数**,并给 button 补了
209
+ `title="求职资料:6 个栏目"`。平铺 tab 不变(它的角标本就是该栏目的条目数)。
210
+ - **`entryIds` 在路由层没透传**:服务层单测是绿的,但 ZIP 仍打全量。
211
+ 修复后浏览器解包确认只含所选条目。
212
+ - **group 回落不一致**:多处各自兜底成不同栏目名,统一到一处。
213
+ - **阅读态下没有图片的 `md` 字段不再显示空的图片区**:纯文字字段(上线检查清单、常用链接)
214
+ 底下会多出「图片(0)」和一句「正文里还没有图片」,纯噪音。现在只有正文真有图、
215
+ 或正在编辑时才渲染那块;编辑态保留,因为它是「贴进来的图会出现在这里」的落点提示。
216
+ - **「另存」的标题在极端情况下会重复加「(导入)」**:`uniqueTitle` 的基准原本传的是
217
+ `标题(导入)`,二次重名会变成 `A(导入)(导入)`,与预览里算给用户看的 `copyTitle` 也对不上。
218
+ 现在基准统一用原标题,预览与实际落盘一致。
219
+ - **空栏目被整库选中时,chip 的 `aria-pressed` 是 false**:选中态原先按条目数推导
220
+ (`!!n`),0 条目的栏目于是即使用户勾了也读成「未选」—— 读屏用户得到错误状态。
221
+ 选中态现在与条目数无关。
222
+
223
+ ### Removed
224
+
225
+ - **备份模块整体删除**(BREAKING):`nx-sk backup *` 命令、`.nxskbak` 格式、
226
+ `src/modules/backup/` 与 `references/30-export-backup.md` 全部移除。
227
+ 有文件导出就够了,本地快照备份是重复的第二套回退机制,而且两边口径必然漂移。
228
+ - 删条目 / 栏目 / 附件**不再留快照** —— 改为「先导出再删」,`transfer export`
229
+ 出来的包就是唯一回退手段。
230
+ - 跨机敏感字段的重新加密逻辑迁到 `transfer`:导入后用**本机 vaultKey** 重加密落盘,
231
+ 与手填字段没有区别。
232
+ - 导航名由「导出与备份」改为「导出导入」;旧书签 `#/backup` 自动落到「设置」。
233
+ - 旧命令 `nx-sk backup export|import` 报「未知命令」,请改用 `transfer export` /
234
+ `transfer import-new`(旧 `.nxskbak` 文件不再可导入)。
235
+
5
236
  ## [0.11.0] - 2026-10-18
6
237
 
7
238
  ### Added
package/README.md CHANGED
@@ -74,6 +74,29 @@ ln -s "$(pwd)/assets/nx-sk" "$HOME/.workbuddy/skills/nx-sk"
74
74
  > nx-sk skill install --force # 改了 assets/nx-sk/ 之后必须重装,否则是旧版
75
75
  > nx-sk skill get nx-sk --json # 外部 agent 一键拿全上下文(四元:skillName/ref/content/contentBytes/install)
76
76
 
77
+ ## 从 0.11.x 升级
78
+
79
+ **导出只剩 `nx-sk transfer` 一条路径。** 备份(0.12.0 删)与可读导出 JSON / Markdown
80
+ (0.13.0 删)都是「往本机目录再写一份副本」,而 ZIP 导出已经覆盖了这两件事。
81
+
82
+ | 原来 | 现在 |
83
+ | --- | --- |
84
+ | `nx-sk backup export --password 口令` | `nx-sk transfer export <栏目> --passphrase 口令 --out x.zip` |
85
+ | `nx-sk backup import --file x.nxskbak` | `nx-sk transfer import-new --file x.zip` |
86
+ | `nx-sk export run --format both --out DIR` | `nx-sk transfer bundle-export --out x.zip`(ZIP,不是可读副本) |
87
+ | `nx-sk export list` | ——(已删;文件由你放到哪就在哪) |
88
+ | 面板「导出与备份」 | 面板「导出导入」(栏目全选即整库) |
89
+
90
+ 仍然可用的命令:`transfer export` / `transfer import` / `transfer bundle-export` /
91
+ `transfer bundle-import` / `transfer import-new`。
92
+
93
+ - **旧的 `.nxskbak` 文件不再可导入。** 升级前请先在 0.11.x 下导出一份
94
+ (`nx-sk transfer bundle-export --out x.zip`),升级后用 `transfer import-new --file x.zip` 导回。
95
+ - 破坏性删除(删栏目 / 条目 / 附件)**不再自动留快照** —— 删之前先导一份。
96
+ - `transfer export --no-secrets` 移除:包里恒为明文,保护只靠 `--passphrase`。
97
+ - 加密包格式变了(外层是合法 ZIP、内含 `payload.enc`),旧包仍可导入;
98
+ 但 Windows 上不再报「压缩文件夹错误」了。
99
+
77
100
  ## 它是什么
78
101
 
79
102
  一个「本机工具类」项目的标准骨架实现(server-cli-web):**serve 驱动 CLI 与 Web,
@@ -130,13 +153,12 @@ CLI 与 API 同源,skill 驱动 agent**。落到具体功能上:
130
153
  | `nx-sk file restore <id> [--to DIR] [--copy]` | **可逆**:移回原始位置并撤销台账;`--copy` 只导出副本 |
131
154
  | `nx-sk file content <id>` | 浏览器链接(内联预览 / `?download=1` 下载);CLI 打印物理路径 |
132
155
  | `nx-sk file open <id>` | 用系统默认程序打开原件 |
133
- | `nx-sk export run [--format json\|md\|both] [--section id] [--with-secrets] [--out DIR] [--dry-run]` | 整体导出 |
134
- | `nx-sk export list` | 历次导出与目录内文件 |
135
- | `nx-sk transfer export <栏目> [--entries 条目,条目] [--out 文件] [--no-secrets]` | 栏目/选中条目 + 引用附件打成 ZIP(栏目页「更多」里同源下载) |
156
+ | `nx-sk transfer export <栏目> [--entries 条目,条目] [--passphrase 口令] [--out 文件]` | 栏目/选中条目 + 引用附件打成 ZIP,可设口令加密(栏目页「更多」里同源下载) |
136
157
  | `nx-sk transfer import <栏目> [--file ZIP] [--on-conflict skip\|copy\|overwrite] [--dry-run]` | 预览/导入到指定栏目;附件按 sha256 复用 |
137
158
  | `nx-sk transfer import-new [--file ZIP] [--dry-run]` | **零配置**:按包内结构新建栏目(id 冲突加序号) |
138
- | `nx-sk backup export [--password 口令] [--out DIR] [--with-files] [--dry-run]` | 口令对称加密整库,导出单个 `.nxskbak`(口令不落盘;`--with-files` 连附件原件) |
139
- | `nx-sk backup import --file <备份> [--password 口令] [--replace] [--dry-run]` | 解密导入:缺省「合并」(只增补、不覆盖已有值,冲突列清单),`--replace` 整份替换;动手前自动留安全快照 |
159
+ | `nx-sk transfer bundle-export [--sections 栏目,栏目] [--passphrase 口令] [--no-files] [--out 文件]` | 多栏目/整库打包成 ZIP(`bundle.json + sections/<id>.zip`),可设口令加密 |
160
+ | `nx-sk transfer bundle-import [--file ZIP] [--on-conflict skip\|copy\|overwrite] [--dry-run]` | 整库包解密导入;缺省「合并」只增补不覆盖 |
161
+ | `nx-sk transfer import-new [--file ZIP] [--dry-run]` | **零配置**:自动识别单栏目包 / 整库包,按包内结构新建(id 冲突加序号) |
140
162
  | `nx-sk setting get \| set` | 全局设置 |
141
163
  | `nx-sk skill list \| install \| get` | 把内置 skill 装给 agent / 给外部 agent 取上下文 |
142
164
 
@@ -222,53 +244,37 @@ nx-sk setting set --set 'navGroups=[]' # 清空 = 全部栏目恢复平铺
222
244
 
223
245
  分组只是**显示配置**(存在 `settings.navGroups`):不动栏目与条目的数据,删掉栏目时头部自动略过。
224
246
 
225
- 整体导出(凭据默认**含明文**——本机自己用;要分享给别人时加 `--no-secrets` 打码):
226
-
227
- ```bash
228
- nx-sk export run --format both --out D:/备份/nx-sk
229
- nx-sk export list
230
- ```
231
-
232
- 加密备份 / 恢复(口令对称加密,AES-256-GCM;导出设口令、恢复输口令):
233
-
234
- ```bash
235
- nx-sk backup export --password '你的备份口令' # 写成 ~/nx-sk/backup/*.nxskbak
236
- nx-sk backup export --password '你的备份口令' --with-files # 连附件原件一起打包
237
- nx-sk backup import --file ~/nx-sk/backup/xxx.nxskbak --password '你的备份口令' --dry-run # 先看合并计划
238
- nx-sk backup import --file ~/nx-sk/backup/xxx.nxskbak --password '你的备份口令' # 缺省「合并」导入
239
- nx-sk backup import --file ~/nx-sk/backup/xxx.nxskbak --password '你的备份口令' --replace # 才是整份替换
240
- # 口令也可用环境变量 NX_SK_BACKUP_PASSWORD 提供
241
- ```
247
+ ### 全局导出 / 导入(多栏目)
242
248
 
243
- **导入缺省是「合并」**:只增补(补缺失的栏目/条目/附件、补空字段、结构只增不减),
244
- 不覆盖你已填过的值——两边都有值且不同时保留本地、列入冲突清单由你处理;
245
- 导入完成给出完整报告。`--replace` 才整份替换(灾难恢复 / 跨机器迁移用)。
246
- 两种方式都先自动留安全快照。
249
+ **项目里只有一个导出导入模块,没有独立的「备份」模块。** 导出的包就是唯一回退手段:
250
+ 删栏目 / 条目 / 附件之前先导一份。
247
251
 
248
- Web 面板的「导出与备份」页同样支持:导出在浏览器下载 `.nxskbak`(可勾选连附件一起打包),
249
- 导入选择文件并输入口令、报告以弹窗呈现;口令错误或文件损坏会明确报错、不改动当前数据。
252
+ 「导出导入」页顶部:栏目横向排成 chips,悬浮或点击展开条目可逐条勾选;
253
+ 顶部「整库」按钮 = 所有栏目全选。选好内容 → 输两次口令(可留空)→「导出 ZIP」浏览器直接下载。
254
+ 把 ZIP 拖进右侧框即预览(栏目、条目新增/冲突、附件新增/复用),选冲突策略后确认。
250
255
 
251
- ### 全局导出 / 导入(多栏目)
256
+ 口令用**对称加密(AES-256-GCM)**:实现是「内部数据先加密,再套一层合法 ZIP」,
257
+ 所以外层始终能正常打开(只看到一个 `payload.enc`),不会报「文件夹无效」;
258
+ 缺口令时明确提示要口令,不会误报成坏包。
252
259
 
253
- 「导出与备份」页顶部:栏目树可勾选栏目,展开后能只选部分条目;点「导出选中的 ZIP」下载。
254
- 把 ZIP 拖进下方框即预览(栏目、条目新增/冲突、附件新增/复用、凭据),选冲突策略后确认。
255
- 全局包 `bundle.json + sections/<id>.zip`,与单栏目包同构;导入时逐栏目复用同一套逻辑。
260
+ 整库包结构 `bundle.json + sections/<id>.zip`,与单栏目包同构;导入时逐栏目复用同一套逻辑。
256
261
 
257
262
  ```bash
258
263
  nx-sk transfer bundle-export --sections job,projects --out all.zip
259
264
  nx-sk transfer bundle-import --file all.zip --dry-run
260
265
  nx-sk transfer bundle-import --file all.zip --on-conflict copy
266
+ nx-sk transfer import-new --file all.zip # 零配置:自动识别单栏目包 / 整库包
261
267
  ```
262
268
 
263
-
264
269
  ### 只搬一个栏目:ZIP 导出 / 导入
265
270
 
266
- 整库备份之外,栏目可以单独打包(含条目、栏目定义、实际引用到的附件):
271
+ 栏目也可以单独打包(含条目、栏目定义、实际引用到的附件):
267
272
 
268
273
  ```bash
269
274
  nx-sk transfer export deploy --out D:/tmp/deploy.zip # 含凭据明文
270
- nx-sk transfer export deploy --out D:/tmp/share.zip --no-secrets # 打码,给别人
275
+ nx-sk transfer export deploy --passphrase 口令 --out D:/tmp/share.zip # 设口令再给别人
271
276
  nx-sk transfer export deploy --entries "k8s-deployment,compose" --out D:/tmp/part.zip # 只导选中条目
277
+ nx-sk transfer bundle-export --sections job,deploy --passphrase 口令 --out D:/tmp/enc.zip # 整包加密
272
278
  nx-sk transfer import deploy2 --file D:/tmp/deploy.zip --dry-run # 先预览:新增/冲突/补字段/附件
273
279
  nx-sk transfer import deploy2 --file D:/tmp/deploy.zip # 栏目不存在就新建
274
280
  nx-sk transfer import deploy2 --file D:/tmp/deploy.zip --on-conflict copy # 另存
@@ -276,7 +282,7 @@ nx-sk transfer import deploy2 --file D:/tmp/deploy.zip --on-conflict overwrite #
276
282
  nx-sk transfer import-new --file D:/tmp/deploy.zip # 零配置:按包内结构新建
277
283
  ```
278
284
 
279
- 面板里是栏目页右上角「更多」:导出 ZIP(含凭据 / 打码)、导入 ZIP…;
285
+ 面板里是栏目页右上角「更多」:导出 ZIP、导入 ZIP…;
280
286
  条目列表可勾选(表头全选/清空),选中后导出**只带这些条目及其附件**。
281
287
  把 zip **拖到栏目页任意位置**会导入当前栏目;拖到「栏目」tab 则**零配置新建一个栏目**。
282
288
  预览会显示来源栏目、目标栏目、条目冲突、将补的字段、附件新增/复用与凭据是否打码,
@@ -291,10 +297,10 @@ nx-sk transfer import-new --file D:/tmp/deploy.zip # 零配置:
291
297
  | 数据目录 | `~/nx-sk/`,可用 `NX_SK_HOME` 覆盖 |
292
298
  | 状态文件 | `~/nx-sk/store.json`,可用 `NX_SK_STORE` 覆盖;**原子写**(临时文件 + rename) |
293
299
  | 密文密钥 | 环境变量 `NX_SK_PASSPHRASE`(scrypt 派生 + 盐)优先,否则 `~/nx-sk/.vaultkey`(32 字节随机,0600) |
294
- | 快照 | 每次破坏性写入前整库备份到 `~/nx-sk/backup/`(最多 20 份),拷回即回滚 |
300
+ | 迁移 | 破坏性删除**不留本地快照**:先 `nx-sk transfer export` 导一份,导出的包就是唯一回退手段 |
295
301
  | 导出 | `~/nx-sk/export/`,默认不含凭据明文 |
296
302
  | 附件 | `~/nx-sk/files/` 存二进制原件,元数据在 `store.json` 的 `files`(sha256 去重;条目 `file` 字段存附件 id 即建立链接;`file restore` 可逆) |
297
- | 加密备份 | `.nxskbak`:整库经 scrypt(口令)+AES-256-GCM 加密,口令不落盘;附件台账恒随包(`--with-files` 连原件);导入缺省「合并」只增不减,`--replace` 整份替换;动手前自动留安全快照 |
303
+ | 加密导出 | `transfer` 的 `--passphrase`:内部数据经 scrypt(口令)+AES-256-GCM 加密成 `payload.enc`,再套一层**合法 ZIP**(外层能正常打开,不会报「文件夹无效」);口令不落盘,导入时缺口令明确提示 |
298
304
  | 服务绑定 | 默认 `127.0.0.1`,不暴露到局域网;写操作校验 `Origin` |
299
305
  | 数据损坏 | `store.json` 解析失败会先被改名为 `.corrupt-<时间戳>.json`,不会静默覆盖 |
300
306
 
@@ -309,20 +315,65 @@ export NX_SK_PASSPHRASE='你的长口令' # 之后所有密文都用它派
309
315
  ## 给 agent 用
310
316
 
311
317
  ```bash
312
- nx-sk skill install # 装到 ~/.claude/skills/nx-sk(三态,不静默覆盖)
318
+ nx-sk skill list # 内置 skill 清单
319
+ nx-sk skill install # 装到 ~/.claude/skills/nx-sk(三态,不静默覆盖)
313
320
  nx-sk skill get nx-sk --json # {skillName, ref, content, contentBytes, install}
314
321
  nx-sk skill get nx-sk 10-sections-entries # 裸名 → references/10-sections-entries.md
315
322
  ```
316
323
 
317
- 内置 skill 的 ref 路由表:
324
+ `nx-sk` 的 ref 路由表:
318
325
 
319
326
  | ref | 何时读 |
320
327
  | --- | --- |
321
328
  | `00-design` | 为什么这么分层、错误码与 `{status}` 的分工 |
322
329
  | `10-sections-entries` | 填/改信息的完整命令与 AI 补全流程 |
323
330
  | `20-secrets` | 密文机制、密钥来源、排查「解不开」 |
324
- | `30-export-backup` | 导出、快照与回滚 |
325
331
  | `40-files` | 附件库:导入(默认移动)与还原、`file` 字段与条目的链接、附件中心由栏目驱动 |
332
+ | `50-deploy` | 存/取 Dockerfile、compose、k8s 清单 |
333
+ | `60-transfer` | 栏目 ZIP:导出、预览、导入、冲突策略 |
334
+
335
+ ### sk-pdf:PDF → 图文 SOP 栏目
336
+
337
+ 把用户手册、操作指南转成 nx-sk 里的图文 SOP 栏目(抽正文、按版面挑截图、
338
+ 上传成附件、在 `md` 字段里 `![]()` 引用),带两个已验证的抽取脚本:
339
+
340
+ ```bash
341
+ nx-sk skill install sk-pdf
342
+ nx-sk skill get sk-pdf # 全文
343
+ nx-sk skill get sk-pdf 30-pitfalls # 踩过的坑
344
+
345
+ # 不装也能用(唯一第三方依赖是 PyMuPDF)
346
+ python assets/sk-pdf/scripts/pdf_probe.py book.pdf # 先探查:有没有图、是不是扫描件
347
+ python assets/sk-pdf/scripts/pdf_extract.py book.pdf --out /tmp/out
348
+ ```
349
+
350
+ | ref | 何时读 |
351
+ | --- | --- |
352
+ | `10-extract` | 为什么必须按版面坐标排序取图(顺序错 = 图全配错步骤) |
353
+ | `20-sop-schema` | 栏目六段结构(简介/准备材料/入口/手册/常见问题/自检)、账号怎么拆 sensitive 字段 |
354
+ | `30-pitfalls` | 占位符忘换 id、本地路径渲染不出、密码泄漏、md 图片被误判未引用… |
355
+
356
+ ### sk-thesis:毕业论文格式与结构
357
+
358
+ 按信息工程学院 2022 版规范(设计开发类)把格式做成**可查的字段**与**可用的 Word 模板**:
359
+ 页面 2.5/2/2.5/2 cm、1.5 倍行距、宋体小四、标题黑体四号/黑体小四/宋体小四、三线表 1.5·1 磅、
360
+ 目录域与分节页码(封面与目录不编、标题页起为 1)。
361
+
362
+ ```bash
363
+ nx-sk section add thesis --template thesis # 新建「毕业论文」栏目(9 组 / 59 字段)
364
+ nx-sk section get thesis # 全部硬指标 + 交稿自检清单
365
+ nx-sk skill get sk-thesis 10-format # 数值速查
366
+
367
+ # 重新生成模板(本机 managed venv 已装 python-docx)
368
+ ~/.workbuddy/binaries/python/envs/default/Scripts/python.exe \
369
+ assets/sk-thesis/scripts/make_thesis_template.py
370
+ ```
371
+
372
+ | ref | 何时读 |
373
+ | --- | --- |
374
+ | `10-format` | 页边距、字号、标题层级、图表、参考文献、页码的全部数值 |
375
+ | `20-structure` | 章节骨架、每章写什么、开题与论文的格式差异、装订顺序 |
376
+ | `30-pitfalls` | 真实错法:一级标题加粗、图题「图 1」多空格、(1)自动编号带悬挂缩进、批注没删 |
326
377
 
327
378
  ## 开发
328
379
 
@@ -0,0 +1,166 @@
1
+ ---
2
+ name: kv-source
3
+ description: 在 nx-sk 的栏目里做「原始资料 ↔ 结构化字段」的双向同步——把杂乱资料(邮件、聊天记录、截图、PDF 摘录、手抄的表、网址)先原样收进条目的「原始资料」袋,再按栏目字段字典整理成结构化字段;也支持反向把已确认的字段值回写进袋子留痕。当用户说"把这些乱的整理进 nx-sk"、"这堆资料归到字段里"、"source 和字段同步一下"、"把邮件/截图/文档存下来并归档"、"原始资料整理成表"、"回写一下出处",或给来一堆没格式的内容却已经指定了栏目/条目时使用。触发词:source、原始资料、乱入、整理、同步、归档、乱数据、收件箱、字段同步、资料袋。不适用:用户已经说清要写哪个字段(直接 entry update 即可)、栏目结构本身还没定(先走 sk-needdo 定结构)、PDF 要转成图文 SOP(用 sk-pdf)。
4
+ ---
5
+
6
+ # kv-source · 原始资料 ↔ 字段 的双向同步
7
+
8
+ 一句话:**先原样收下,再按字典整理;整理完留痕,两头能对回去。**
9
+
10
+ 每个栏目都自带一个「原始资料」袋(`source` 字段)。它是**乱入区**:
11
+ 真实数据几乎从不是一次填对的——邮件、截图、PDF 摘录、一段手抄的表,全都先有个
12
+ 「还没整理」的阶段。没有这个袋子,用户只有两个坏选择:把乱数据硬塞进结构化字段
13
+ (把结构搞脏),或者干脆不存(信息丢了)。
14
+
15
+ ## 三条铁律
16
+
17
+ 1. **原文不改写。** 袋里的东西是**原始证据**:摘要是可以重写的,原文不能只剩摘要。
18
+ 整理的结果写进**字段**(或写进**新的一条**),不就地改写原件——否则「原始」这个词就没意义了。
19
+ 2. **不确定就留空。** 袋里的资料经常是半截的、含糊的、有歧义的。
20
+ 拿不准的值**不写进字段**(留空比写错好:写错会被当成事实传下去),把猜测写进该条的 `note`。
21
+ 3. **敏感内容不进袋。** 袋是**明文**的(不加密),密码/token 走「密钥」栏目或 `sensitive` 字段。
22
+ 往袋里贴明文密码 = 把凭据从密文降级成明文,这是不可逆的降级。
23
+
24
+ ## 冲突:以新的为准,旧的留痕
25
+
26
+ 同一件事有了新版本(又来一封邮件、密码改了)——**不要覆盖、不要删**:
27
+
28
+ - **同名即冲突**。时间新的那条是「当前版本」;旧的被标成「已被取代」但**仍留在袋里**
29
+ (原始证据要能翻回去看)。面板默认把旧的收起,点「已被取代的 N 条(留痕)」可展开。
30
+ - **时间以 `--at` 为准**:`--at 2026-10-19` 记的是**资料自身的时间**(邮件时间),
31
+ 缺省用入袋时间。所以「谁更新」看的是资料的时间,不是你什么时候加进去的。
32
+ - 名字不同但确实是同一件事(如「分配邮件」vs「李晶老师的邮件」):用
33
+ `--supersedes <旧id>` 显式指明——旧条同样被标成已取代。
34
+ - **字段也要跟着更新**:新资料覆盖字段里的旧值(`entry update --data`),
35
+ 旧资料留在袋里 ——「现在的值」与「它是怎么来的」同时成立。
36
+ - 手动标:`nx-sk source update <条目> --id <旧id> --superseded`。
37
+
38
+ ```bash
39
+ nx-sk source add <条目> --text "新密码…" --name "分配邮件" --at 2026-10-19
40
+ nx-sk source add <条目> --text "…" --name "李晶老师的邮件" --supersedes s_abc123
41
+ ```
42
+
43
+ ## 命令速查
44
+
45
+ ```bash
46
+ nx-sk source list # 各栏目的袋概况(多少条目有袋、袋里多少条)
47
+ nx-sk source get <条目> --json # 看某个条目的袋(item 明细 + 附件信息)
48
+ nx-sk source add <条目> --text "…" # 加一条文字(原样存)
49
+ nx-sk source add <条目> --file <路径> # 加一个文件(先进附件库,原件保留不搬走)
50
+ nx-sk source add <条目> --url <网址> # 加一个网址
51
+ nx-sk source update <条目> --id <itemId> --mapped 字段key # 标注「这条已整理到哪些字段」
52
+ nx-sk source remove <条目> --id <itemId> # 删一条(默认不动,删之前先确认)
53
+ nx-sk source ensure # 给缺 source 字段的栏目补上(KV 表除外)
54
+ ```
55
+
56
+ 带时间与取代关系的写法:
57
+
58
+ ```bash
59
+ nx-sk source add <条目> --text "…" --name "分配邮件" --at 2026-10-19 # 记资料自身的时间
60
+ nx-sk source add <条目> --text "…" --name "李晶老师的邮件" --supersedes s_abc # 显式取代旧的
61
+ nx-sk source update <条目> --id s_abc --superseded # 手动标「已被取代」
62
+ ```
63
+
64
+ ## 流程(五步)
65
+
66
+ ### 第 0 步:找到落点
67
+
68
+ ```bash
69
+ nx-sk source list # 哪个栏目有袋、哪些条目有袋
70
+ nx-sk entry list --section <栏目> # 找目标条目(同名用 id)
71
+ ```
72
+
73
+ 用户没指定条目时:**先问**(是新建一条,还是并进已有条目?)。
74
+ 并进已有条目前先 `nx-sk source get <条目>` 看一眼袋里已经有什么,避免重复收。
75
+
76
+ ### 第 1 步:原样收下(先别整理)
77
+
78
+ 一份资料一条 item。文字、文件、网址各是一个 kind:
79
+
80
+ ```bash
81
+ nx-sk source add <条目> --text "邮件原文照抄……" --name "李晶老师的分配邮件"
82
+ nx-sk source add <条目> --file "D:\截图\控制台.png" --name "控制台首屏"
83
+ nx-sk source add <条目> --url "https://qcloud.sicau.edu.cn/login"
84
+ ```
85
+
86
+ - `--file` **默认复制不搬走**原件(这是「加一条资料」,不是 `file add` 的搬运语义)。
87
+ - 一次要收很多份:面板里在「原始资料」抽屉中**多选文件**拖进去,或用 `--file` 重复调用。
88
+ - 文件分发细则(PDF 怎么拆、截图怎么批量进、表格怎么变成清单行)见 [[20-intake]]。
89
+
90
+ ### 第 2 步:读袋子
91
+
92
+ ```bash
93
+ nx-sk source get <条目> --json
94
+ ```
95
+
96
+ 返回的 `items[]` 形如 `{id, kind, name, text|fileId|url, addedAt, note, mapped}`。
97
+ `file` kind 的 item 要读内容就看 `files[fileId].path`(物理路径)或 `files[fileId].downloadUrl`。
98
+
99
+ ### 第 3 步:映射(source → 字段)
100
+
101
+ 先看**目标栏目的字段台账**(不要凭印象猜字段名):
102
+
103
+ ```bash
104
+ nx-sk entry fields --section <栏目> --json
105
+ ```
106
+
107
+ 逐条 item → 字段的映射表(心里列出来,或写进回复给用户过目):
108
+
109
+ | item | 内容 | 目标字段 | 把握 |
110
+ | --- | --- | --- | --- |
111
+ | 分配邮件 | VPN 账户 14611 / Lj198900@# | `vpnAccount` / `vpnPassword` | 高(原文直给)|
112
+ | 截图 | 控制台地址 | `accessUrl` | 中(OCR 有误风险)|
113
+
114
+ **把握不足的字段不写**——留空,并在该 item 的 `note` 里写清「疑似是什么、为什么没填」。
115
+
116
+ 写入(**先 dry-run**):
117
+
118
+ ```bash
119
+ nx-sk entry update <条目> --data @整理.json --dry-run # 先看会改什么
120
+ nx-sk entry update <条目> --data @整理.json # 确认后真写
121
+ ```
122
+
123
+ `整理.json` 形如 `{"vpnAccount":"14611","vpnPassword":"Lj198900@#"}`
124
+ (键用字段 key 或中文标签都行;敏感字段是 `sensitive` 的话值照写,加密由服务端做)。
125
+
126
+ ### 第 4 步:留痕(字段 → source,双向闭环)
127
+
128
+ **整理完给用上的 item 打锚点**,这样下一次同步知道「这条已经用过」,不会重复整理:
129
+
130
+ ```bash
131
+ nx-sk source update <条目> --id <itemId> --mapped vpnAccount,vpnPassword
132
+ ```
133
+
134
+ 反过来(**字段 → source**):把已确认的字段值也回写进袋子,出处留痕:
135
+
136
+ ```bash
137
+ nx-sk source add <条目> --text "14611" --name "VPN 账号(已确认)" --mapped vpnAccount --note "整理自 vpnAccount 字段"
138
+ ```
139
+
140
+ 这样「哪个字段的值是从哪条原始资料来的」永远能反查回来。
141
+
142
+ ### 第 5 步:自检
143
+
144
+ ```bash
145
+ nx-sk entry get <条目> --json # 字段值对不对
146
+ nx-sk source get <条目> --json # 袋里每条的 mapped 对不对
147
+ nx-sk section dump <栏目> --json # 完整度与未填清单(袋子不计入完整度)
148
+ ```
149
+
150
+ ## 面板里怎么用
151
+
152
+ 条目表单底部有一条**折叠条**「原始资料 · N 条」(暂存区不该在日常填表时占地方)。
153
+ 点开是右侧抽屉:左边清单、中间正文。
154
+
155
+ - 拖文件进左栏 → 自动上传并入袋
156
+ - 「+ 选择文件」按钮 → 同样的事(**拖拽不是唯一入口**:WCAG 2.2 AA 要求键盘/单指针也能完成)
157
+ - 粘贴框 + Ctrl+Enter → 加一条文字
158
+ - 左栏点一条 → 右边看内容;「移除」删掉这一条
159
+
160
+ ## 常见坑
161
+
162
+ - **同名条目**:一个栏目里两条同名时 `entry get` 会报 CONFLICT,用 id 定位。
163
+ - **袋里没有 source 字段**:老栏目是模板实例化时建的,source 字段是后加的 → `nx-sk source ensure` 补齐。
164
+ - **KV 栏目(密钥)没有袋**:它的形状就是一行一个键值对,加字段会散掉。密钥走 `nx-sk key set`。
165
+ - **`--mapped` 是数组**:可以 `--mapped a,b`(逗号分隔),也可以重复给。
166
+ - **别用 `entry update --set source=...` 手写整袋 JSON**:那是给程序用的口子,人用 `source add` 更安全(少写错一个括号就毁掉整袋)。