dsh-config-manager 0.1.69 → 0.1.70

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 (719) hide show
  1. package/README.md +59 -18
  2. package/README.zh-CN.md +29 -9
  3. package/lib/adapters/credentials.js +6 -3
  4. package/lib/adapters/file-collection.d.ts +21 -1
  5. package/lib/adapters/file-collection.js +32 -8
  6. package/lib/adapters/mcp.d.ts +12 -2
  7. package/lib/adapters/mcp.js +30 -13
  8. package/lib/adapters/plugin-files.d.ts +8 -0
  9. package/lib/adapters/plugin-files.js +36 -4
  10. package/lib/adapters/plugins.d.ts +28 -2
  11. package/lib/adapters/plugins.js +204 -55
  12. package/lib/adapters/prompts.d.ts +12 -2
  13. package/lib/adapters/prompts.js +34 -19
  14. package/lib/adapters/providers.js +5 -4
  15. package/lib/adapters/self.d.ts +20 -0
  16. package/lib/adapters/self.js +73 -0
  17. package/lib/adapters/sessions.d.ts +8 -0
  18. package/lib/adapters/sessions.js +47 -1
  19. package/lib/adapters/settings.d.ts +21 -3
  20. package/lib/adapters/settings.js +44 -7
  21. package/lib/adapters/skills.d.ts +36 -4
  22. package/lib/adapters/skills.js +222 -3
  23. package/lib/adapters/test-helpers.d.ts +31 -2
  24. package/lib/adapters/test-helpers.js +42 -5
  25. package/lib/adapters/workspaces.js +97 -5
  26. package/lib/cli/actions.d.ts +50 -3
  27. package/lib/cli/actions.js +103 -11
  28. package/lib/cli/help.d.ts +65 -0
  29. package/lib/cli/help.js +517 -0
  30. package/lib/cli/import-source.d.ts +32 -0
  31. package/lib/cli/import-source.js +178 -0
  32. package/lib/cli/index.d.ts +18 -2
  33. package/lib/cli/index.js +118 -90
  34. package/lib/cli/web/client-script.d.ts +8 -0
  35. package/lib/cli/web/client-script.js +11 -0
  36. package/lib/cli/web/http.d.ts +5 -0
  37. package/lib/cli/web/http.js +2 -1
  38. package/lib/cli/web/page.d.ts +16 -6
  39. package/lib/cli/web/page.js +102 -25
  40. package/lib/cli/web/routes.js +24 -5
  41. package/lib/cli/web/server.js +23 -2
  42. package/lib/client.d.ts +1019 -36
  43. package/lib/client.js +5529 -1571
  44. package/lib/core/analyzer.d.ts +15 -1
  45. package/lib/core/analyzer.js +114 -13
  46. package/lib/core/backup-plan.js +17 -1
  47. package/lib/core/backup.d.ts +8 -0
  48. package/lib/core/backup.js +28 -6
  49. package/lib/core/cache-cleaner.d.ts +10 -0
  50. package/lib/core/cache-cleaner.js +63 -22
  51. package/lib/core/checkpoint.d.ts +362 -0
  52. package/lib/core/checkpoint.js +1309 -0
  53. package/lib/core/journal.d.ts +37 -0
  54. package/lib/core/journal.js +69 -0
  55. package/lib/core/messages.d.ts +13 -0
  56. package/lib/core/messages.js +26 -0
  57. package/lib/core/model-tools.js +3 -0
  58. package/lib/core/patch-layers.d.ts +83 -0
  59. package/lib/core/patch-layers.js +110 -0
  60. package/lib/core/restore.d.ts +21 -0
  61. package/lib/core/restore.js +37 -3
  62. package/lib/core/rollback.js +6 -3
  63. package/lib/core/self-update.d.ts +52 -0
  64. package/lib/core/self-update.js +70 -0
  65. package/lib/core/session-health.d.ts +29 -5
  66. package/lib/core/session-health.js +28 -3
  67. package/lib/core/types.d.ts +126 -0
  68. package/lib/foreign/antigravity.d.ts +28 -0
  69. package/lib/foreign/antigravity.js +157 -0
  70. package/lib/foreign/bundle.d.ts +25 -0
  71. package/lib/foreign/bundle.js +79 -0
  72. package/lib/foreign/chatgpt.d.ts +6 -0
  73. package/lib/foreign/chatgpt.js +25 -0
  74. package/lib/foreign/claude-code.d.ts +3 -0
  75. package/lib/foreign/claude-code.js +85 -0
  76. package/lib/foreign/claude-sessions.d.ts +100 -0
  77. package/lib/foreign/claude-sessions.js +291 -0
  78. package/lib/foreign/cline.d.ts +6 -0
  79. package/lib/foreign/cline.js +27 -0
  80. package/lib/foreign/codex.d.ts +83 -0
  81. package/lib/foreign/codex.js +348 -0
  82. package/lib/foreign/continue.d.ts +9 -0
  83. package/lib/foreign/continue.js +65 -0
  84. package/lib/foreign/copilot.d.ts +39 -0
  85. package/lib/foreign/copilot.js +95 -0
  86. package/lib/foreign/crush.d.ts +11 -0
  87. package/lib/foreign/crush.js +57 -0
  88. package/lib/foreign/cursor.d.ts +87 -0
  89. package/lib/foreign/cursor.js +171 -0
  90. package/lib/foreign/dsh.d.ts +30 -0
  91. package/lib/foreign/dsh.js +101 -0
  92. package/lib/foreign/dsh4.d.ts +3 -0
  93. package/lib/foreign/dsh4.js +13 -0
  94. package/lib/foreign/gemini.d.ts +7 -0
  95. package/lib/foreign/gemini.js +44 -0
  96. package/lib/foreign/goose.d.ts +11 -0
  97. package/lib/foreign/goose.js +53 -0
  98. package/lib/foreign/grokbuild.d.ts +8 -0
  99. package/lib/foreign/grokbuild.js +25 -0
  100. package/lib/foreign/hermes.d.ts +2 -0
  101. package/lib/foreign/hermes.js +78 -0
  102. package/lib/foreign/kernel.d.ts +132 -0
  103. package/lib/foreign/kernel.js +569 -0
  104. package/lib/foreign/kilocode.d.ts +11 -0
  105. package/lib/foreign/kilocode.js +51 -0
  106. package/lib/foreign/kimi.d.ts +7 -0
  107. package/lib/foreign/kimi.js +47 -0
  108. package/lib/foreign/mimocode.d.ts +11 -0
  109. package/lib/foreign/mimocode.js +51 -0
  110. package/lib/foreign/openclaw.d.ts +7 -0
  111. package/lib/foreign/openclaw.js +46 -0
  112. package/lib/foreign/opencode.d.ts +11 -0
  113. package/lib/foreign/opencode.js +52 -0
  114. package/lib/foreign/pi.d.ts +7 -0
  115. package/lib/foreign/pi.js +48 -0
  116. package/lib/foreign/platform-paths.d.ts +66 -0
  117. package/lib/foreign/platform-paths.js +141 -0
  118. package/lib/foreign/qoder.d.ts +10 -0
  119. package/lib/foreign/qoder.js +49 -0
  120. package/lib/foreign/qwen.d.ts +7 -0
  121. package/lib/foreign/qwen.js +45 -0
  122. package/lib/foreign/read-antigravity.d.ts +57 -0
  123. package/lib/foreign/read-antigravity.js +504 -0
  124. package/lib/foreign/read-chatgpt.d.ts +20 -0
  125. package/lib/foreign/read-chatgpt.js +509 -0
  126. package/lib/foreign/read-claude-code.d.ts +24 -0
  127. package/lib/foreign/read-claude-code.js +234 -0
  128. package/lib/foreign/read-cline.d.ts +53 -0
  129. package/lib/foreign/read-cline.js +479 -0
  130. package/lib/foreign/read-codex.d.ts +75 -0
  131. package/lib/foreign/read-codex.js +842 -0
  132. package/lib/foreign/read-continue.d.ts +87 -0
  133. package/lib/foreign/read-continue.js +489 -0
  134. package/lib/foreign/read-copilot.d.ts +42 -0
  135. package/lib/foreign/read-copilot.js +267 -0
  136. package/lib/foreign/read-crush.d.ts +54 -0
  137. package/lib/foreign/read-crush.js +478 -0
  138. package/lib/foreign/read-cursor.d.ts +141 -0
  139. package/lib/foreign/read-cursor.js +705 -0
  140. package/lib/foreign/read-dsh.d.ts +43 -0
  141. package/lib/foreign/read-dsh.js +205 -0
  142. package/lib/foreign/read-dsh4.d.ts +14 -0
  143. package/lib/foreign/read-dsh4.js +27 -0
  144. package/lib/foreign/read-gemini.d.ts +55 -0
  145. package/lib/foreign/read-gemini.js +273 -0
  146. package/lib/foreign/read-goose.d.ts +25 -0
  147. package/lib/foreign/read-goose.js +322 -0
  148. package/lib/foreign/read-grokbuild.d.ts +34 -0
  149. package/lib/foreign/read-grokbuild.js +455 -0
  150. package/lib/foreign/read-hermes.d.ts +74 -0
  151. package/lib/foreign/read-hermes.js +690 -0
  152. package/lib/foreign/read-kilocode.d.ts +17 -0
  153. package/lib/foreign/read-kilocode.js +46 -0
  154. package/lib/foreign/read-kimi.d.ts +63 -0
  155. package/lib/foreign/read-kimi.js +719 -0
  156. package/lib/foreign/read-mimocode.d.ts +12 -0
  157. package/lib/foreign/read-mimocode.js +56 -0
  158. package/lib/foreign/read-openclaw.d.ts +51 -0
  159. package/lib/foreign/read-openclaw.js +445 -0
  160. package/lib/foreign/read-opencode.d.ts +150 -0
  161. package/lib/foreign/read-opencode.js +896 -0
  162. package/lib/foreign/read-pi.d.ts +56 -0
  163. package/lib/foreign/read-pi.js +411 -0
  164. package/lib/foreign/read-qoder.d.ts +54 -0
  165. package/lib/foreign/read-qoder.js +317 -0
  166. package/lib/foreign/read-qwen.d.ts +40 -0
  167. package/lib/foreign/read-qwen.js +326 -0
  168. package/lib/foreign/read-reasonix.d.ts +66 -0
  169. package/lib/foreign/read-reasonix.js +537 -0
  170. package/lib/foreign/read-teleagent.d.ts +15 -0
  171. package/lib/foreign/read-teleagent.js +68 -0
  172. package/lib/foreign/read-trae.d.ts +67 -0
  173. package/lib/foreign/read-trae.js +492 -0
  174. package/lib/foreign/read-vibe.d.ts +28 -0
  175. package/lib/foreign/read-vibe.js +364 -0
  176. package/lib/foreign/read-workbuddy.d.ts +49 -0
  177. package/lib/foreign/read-workbuddy.js +346 -0
  178. package/lib/foreign/read-zcode.d.ts +12 -0
  179. package/lib/foreign/read-zcode.js +38 -0
  180. package/lib/foreign/read-zed.d.ts +30 -0
  181. package/lib/foreign/read-zed.js +473 -0
  182. package/lib/foreign/reasonix.d.ts +6 -0
  183. package/lib/foreign/reasonix.js +20 -0
  184. package/lib/foreign/registry.d.ts +196 -0
  185. package/lib/foreign/registry.js +573 -0
  186. package/lib/foreign/session-ir.d.ts +186 -0
  187. package/lib/foreign/session-ir.js +391 -0
  188. package/lib/foreign/session-read.d.ts +102 -0
  189. package/lib/foreign/session-read.js +254 -0
  190. package/lib/foreign/session-source.d.ts +216 -0
  191. package/lib/foreign/session-source.js +453 -0
  192. package/lib/foreign/source-modules.d.ts +45 -0
  193. package/lib/foreign/source-modules.js +52 -0
  194. package/lib/foreign/sqlite.d.ts +61 -0
  195. package/lib/foreign/sqlite.js +404 -0
  196. package/lib/foreign/teleagent.d.ts +11 -0
  197. package/lib/foreign/teleagent.js +53 -0
  198. package/lib/foreign/trae.d.ts +6 -0
  199. package/lib/foreign/trae.js +20 -0
  200. package/lib/foreign/truth-table.d.ts +62 -0
  201. package/lib/foreign/truth-table.js +333 -0
  202. package/lib/foreign/types.d.ts +272 -0
  203. package/lib/foreign/types.js +2 -0
  204. package/lib/foreign/vibe.d.ts +6 -0
  205. package/lib/foreign/vibe.js +23 -0
  206. package/lib/foreign/workbuddy.d.ts +7 -0
  207. package/lib/foreign/workbuddy.js +45 -0
  208. package/lib/foreign/zcode.d.ts +11 -0
  209. package/lib/foreign/zcode.js +51 -0
  210. package/lib/foreign/zed.d.ts +11 -0
  211. package/lib/foreign/zed.js +53 -0
  212. package/lib/index.d.ts +154 -13
  213. package/lib/index.js +661 -84
  214. package/lib/market/github-repos.d.ts +28 -0
  215. package/lib/market/github-repos.js +43 -1
  216. package/lib/market/types.d.ts +1 -3
  217. package/lib/profiles/dsh-profile-manager.d.ts +54 -2
  218. package/lib/profiles/dsh-profile-manager.js +182 -11
  219. package/lib/profiles/dsh-profile-shared.d.ts +47 -0
  220. package/lib/profiles/dsh-profile-shared.js +15 -1
  221. package/lib/profiles/index.d.ts +1 -1
  222. package/lib/profiles/index.js +1 -1
  223. package/lib/routes/consult.js +51 -5
  224. package/lib/routes/foreign-context.d.ts +43 -0
  225. package/lib/routes/foreign-context.js +65 -0
  226. package/lib/routes/foreign.d.ts +11 -0
  227. package/lib/routes/foreign.js +208 -0
  228. package/lib/routes/import.d.ts +8 -0
  229. package/lib/routes/import.js +22 -1
  230. package/lib/routes/index.d.ts +2 -2
  231. package/lib/routes/index.js +4 -0
  232. package/lib/routes/kit.d.ts +4 -3
  233. package/lib/routes/market.js +0 -1
  234. package/lib/routes/prefs.d.ts +2 -1
  235. package/lib/routes/prefs.js +14 -2
  236. package/lib/routes/recovery.js +363 -3
  237. package/lib/routes/session-export.d.ts +117 -0
  238. package/lib/routes/session-export.js +270 -0
  239. package/lib/routes/snapshots.js +12 -2
  240. package/lib/routes/sync.js +82 -10
  241. package/lib/security/redaction.d.ts +14 -0
  242. package/lib/security/redaction.js +24 -9
  243. package/lib/security/secret-scanner.d.ts +27 -0
  244. package/lib/security/secret-scanner.js +54 -1
  245. package/lib/sync/autosync-scheduler.d.ts +13 -1
  246. package/lib/sync/autosync-scheduler.js +49 -26
  247. package/lib/sync/backup-files.js +6 -0
  248. package/lib/sync/gist/gist-transport.d.ts +91 -0
  249. package/lib/sync/gist/gist-transport.js +406 -0
  250. package/lib/sync/gist/messages.d.ts +22 -0
  251. package/lib/sync/gist/messages.js +46 -0
  252. package/lib/sync/s3/messages.d.ts +53 -0
  253. package/lib/sync/s3/messages.js +83 -0
  254. package/lib/sync/s3/s3-providers.d.ts +78 -0
  255. package/lib/sync/s3/s3-providers.js +156 -0
  256. package/lib/sync/s3/s3-transport.d.ts +126 -0
  257. package/lib/sync/s3/s3-transport.js +551 -0
  258. package/lib/sync/s3/sigv4.d.ts +107 -0
  259. package/lib/sync/s3/sigv4.js +196 -0
  260. package/lib/sync/sync-config.d.ts +250 -4
  261. package/lib/sync/sync-config.js +658 -57
  262. package/lib/sync/sync-engine.d.ts +1 -0
  263. package/lib/sync/sync-engine.js +4 -2
  264. package/lib/sync/sync-history.d.ts +1 -1
  265. package/lib/sync/webdav/webdav-transport.d.ts +31 -3
  266. package/lib/sync/webdav/webdav-transport.js +102 -18
  267. package/lib/ui/commands.d.ts +4 -3
  268. package/lib/ui/commands.js +41 -9
  269. package/lib/ui/disk-usage-view.d.ts +44 -0
  270. package/lib/ui/disk-usage-view.js +55 -1
  271. package/lib/ui/dsh-profiles-view.d.ts +53 -0
  272. package/lib/ui/dsh-profiles-view.js +56 -0
  273. package/lib/ui/foreign-view.d.ts +162 -0
  274. package/lib/ui/foreign-view.js +225 -0
  275. package/lib/ui/i18n.d.ts +77 -0
  276. package/lib/ui/i18n.js +164 -0
  277. package/lib/ui/session-inventory-view.d.ts +106 -0
  278. package/lib/ui/session-inventory-view.js +58 -0
  279. package/lib/ui/session-layout-view.d.ts +265 -0
  280. package/lib/ui/session-layout-view.js +271 -0
  281. package/lib/ui/sync-repository-picker.d.ts +98 -0
  282. package/lib/ui/sync-repository-picker.js +145 -0
  283. package/lib/ui/sync-settings-view.d.ts +143 -4
  284. package/lib/ui/sync-settings-view.js +231 -7
  285. package/lib/ui/types.d.ts +47 -0
  286. package/lib/utils/asar-read.d.ts +113 -0
  287. package/lib/utils/asar-read.js +523 -0
  288. package/lib/utils/env-lock.js +7 -2
  289. package/lib/utils/patch-yaml.d.ts +25 -0
  290. package/lib/utils/patch-yaml.js +62 -0
  291. package/lib/utils/paths.d.ts +10 -1
  292. package/lib/utils/paths.js +24 -3
  293. package/lib/utils/recursive-walk.js +12 -13
  294. package/lib/utils/session-format.d.ts +35 -2
  295. package/lib/utils/session-format.js +131 -5
  296. package/lib/utils/session-health-scan.d.ts +7 -1
  297. package/lib/utils/session-health-scan.js +369 -12
  298. package/lib/utils/session-layout-repair-service.d.ts +98 -0
  299. package/lib/utils/session-layout-repair-service.js +457 -0
  300. package/lib/utils/session-log-repair.d.ts +27 -0
  301. package/lib/utils/session-log-repair.js +233 -62
  302. package/lib/utils/session-repair-service.d.ts +32 -3
  303. package/lib/utils/session-repair-service.js +148 -9
  304. package/lib/utils/session-row-facts.d.ts +73 -0
  305. package/lib/utils/session-row-facts.js +182 -0
  306. package/lib/utils/session-verify.d.ts +118 -0
  307. package/lib/utils/session-verify.js +705 -0
  308. package/lib/utils/shared-constants.d.ts +59 -0
  309. package/lib/utils/shared-constants.js +71 -0
  310. package/package.json +1 -1
  311. package/src/adapters/credentials.test.ts +27 -0
  312. package/src/adapters/credentials.ts +6 -3
  313. package/src/adapters/file-collection.ts +33 -8
  314. package/src/adapters/files.test.ts +222 -0
  315. package/src/adapters/mcp.test.ts +43 -0
  316. package/src/adapters/mcp.ts +37 -14
  317. package/src/adapters/plugin-files.ts +34 -4
  318. package/src/adapters/plugins.test.ts +241 -0
  319. package/src/adapters/plugins.ts +201 -53
  320. package/src/adapters/prompts.test.ts +49 -0
  321. package/src/adapters/prompts.ts +41 -20
  322. package/src/adapters/providers.test.ts +16 -0
  323. package/src/adapters/providers.ts +4 -3
  324. package/src/adapters/self.test.ts +77 -0
  325. package/src/adapters/self.ts +75 -0
  326. package/src/adapters/sessions.test.ts +77 -0
  327. package/src/adapters/sessions.ts +47 -1
  328. package/src/adapters/settings.test.ts +78 -0
  329. package/src/adapters/settings.ts +42 -6
  330. package/src/adapters/skills.ts +221 -3
  331. package/src/adapters/test-helpers.ts +43 -5
  332. package/src/adapters/workspaces-dup-id.test.ts +144 -0
  333. package/src/adapters/workspaces.test.ts +10 -0
  334. package/src/adapters/workspaces.ts +97 -5
  335. package/src/cli/actions.test.ts +117 -2
  336. package/src/cli/actions.ts +132 -12
  337. package/src/cli/help.ts +570 -0
  338. package/src/cli/import-source.test.ts +193 -0
  339. package/src/cli/import-source.ts +229 -0
  340. package/src/cli/index.ts +129 -94
  341. package/src/cli/web/client-script.ts +12 -0
  342. package/src/cli/web/console-fixes.test.ts +36 -0
  343. package/src/cli/web/http.ts +7 -1
  344. package/src/cli/web/page.ts +108 -25
  345. package/src/cli/web/routes.ts +23 -4
  346. package/src/cli/web/script-anchor.test.ts +33 -0
  347. package/src/cli/web/server.ts +28 -2
  348. package/src/cli/web/web.test.ts +447 -4
  349. package/src/client/ConfigManagerSection.tsx +118 -63
  350. package/src/client/about/AboutPanel.tsx +42 -3
  351. package/src/client/about/PluginUpdateDialog.tsx +185 -0
  352. package/src/client/about/ReleaseBody.tsx +144 -0
  353. package/src/client/about/ReleaseNotesDialog.tsx +3 -130
  354. package/src/client/about/about-view.ts +7 -5
  355. package/src/client/about/plugin-update-view.test.ts +25 -0
  356. package/src/client/about/plugin-update-view.ts +26 -0
  357. package/src/client/about/release-notes-view.test.ts +33 -0
  358. package/src/client/about/release-notes-view.ts +34 -0
  359. package/src/client/api.ts +145 -1
  360. package/src/client/common/Icon.tsx +3 -0
  361. package/src/client/common/RunsCenter.tsx +8 -2
  362. package/src/client/common/Select.tsx +9 -2
  363. package/src/client/common/info-hint-guard.test.ts +63 -18
  364. package/src/client/common/plan-text-redaction.test.ts +193 -8
  365. package/src/client/common/routes.ts +15 -0
  366. package/src/client/config-manager.module.css +506 -354
  367. package/src/client/css-scale-guard.test.ts +122 -0
  368. package/src/client/css-token-guard.test.ts +82 -0
  369. package/src/client/environment/EnvironmentPanel.tsx +45 -16
  370. package/src/client/{snapshots → environment/maintenance}/DiskUsageCard.tsx +21 -17
  371. package/src/client/environment/profiles-incomplete-ui.test.ts +103 -0
  372. package/src/client/export/ExportView.tsx +9 -2
  373. package/src/client/{snapshots → home}/BackupScheduleCard.tsx +13 -4
  374. package/src/client/home/HomePanel.tsx +69 -157
  375. package/src/client/icon-layer-guard.test.ts +1 -1
  376. package/src/client/import/ForeignImportView.tsx +318 -0
  377. package/src/client/import/ImportWizardView.tsx +3 -1
  378. package/src/client/import/foreign-import-view-guard.test.ts +195 -0
  379. package/src/client/import/import-file-select.test.ts +6 -6
  380. package/src/client/import/import-wizard-steps.tsx +17 -1
  381. package/src/client/import/use-import-wizard-controller.ts +3 -3
  382. package/src/client/index.ts +1 -1
  383. package/src/client/library/LibraryActions.tsx +31 -34
  384. package/src/client/library/LibraryPanel.tsx +4 -1
  385. package/src/client/{snapshots → library}/RestorePlanView.tsx +1 -1
  386. package/src/client/{snapshots → library}/files-layout.test.ts +2 -2
  387. package/src/client/{snapshots → library}/report-dismiss.test.ts +26 -3
  388. package/src/client/library/task-panel-visibility.test.ts +25 -17
  389. package/src/client/locales.ts +278 -36
  390. package/src/client/lucide-icons.d.ts +1 -0
  391. package/src/client/market/MarketPanel.tsx +11 -2
  392. package/src/client/recovery/IncompleteCopiesSection.tsx +132 -0
  393. package/src/client/recovery/RecoveryPanel.tsx +486 -59
  394. package/src/client/recovery/recovery-api.ts +30 -0
  395. package/src/client/recovery/recovery-incomplete-copies.test.ts +151 -0
  396. package/src/client/recovery/recovery-locales.test.ts +88 -0
  397. package/src/client/recovery/recovery-locales.ts +321 -6
  398. package/src/client/recovery/recovery-panel-layout.test.ts +52 -0
  399. package/src/client/recovery/recovery-view.ts +92 -2
  400. package/src/client/run-store.test.ts +201 -89
  401. package/src/client/run-store.ts +114 -143
  402. package/src/client/session-export/session-export-view.test.ts +103 -0
  403. package/src/client/session-export/session-export-view.ts +118 -0
  404. package/src/client/sync/ChannelConfigDialog.tsx +219 -14
  405. package/src/client/sync/SyncChannelEntryCard.tsx +52 -4
  406. package/src/client/sync/SyncLogList.tsx +1 -1
  407. package/src/client/sync/SyncPanel.tsx +166 -102
  408. package/src/client/sync/SyncRepositoryPicker.tsx +216 -0
  409. package/src/client/sync/SyncSettingsView.test.ts +6 -1
  410. package/src/client/sync/history-model.test.ts +94 -57
  411. package/src/client/sync/history-model.ts +44 -25
  412. package/src/client/sync/sync-api.test.ts +56 -0
  413. package/src/client/sync/sync-api.ts +92 -0
  414. package/src/client/sync/sync-locales.ts +162 -2
  415. package/src/client/sync/sync-view.test.ts +3 -8
  416. package/src/client/sync/sync-view.ts +111 -21
  417. package/src/core/analyzer-cache.test.ts +47 -0
  418. package/src/core/analyzer.ts +115 -14
  419. package/src/core/backup-plan.test.ts +35 -0
  420. package/src/core/backup-plan.ts +19 -1
  421. package/src/core/backup.test.ts +37 -1
  422. package/src/core/backup.ts +27 -6
  423. package/src/core/cache-cleaner.test.ts +120 -1
  424. package/src/core/cache-cleaner.ts +59 -22
  425. package/src/core/checkpoint.test.ts +665 -0
  426. package/src/core/checkpoint.ts +1614 -0
  427. package/src/core/credential-plan-consistency.test.ts +93 -0
  428. package/src/core/incident-wiring.test.ts +6 -2
  429. package/src/core/journal.test.ts +70 -0
  430. package/src/core/journal.ts +68 -0
  431. package/src/core/messages.ts +26 -0
  432. package/src/core/model-tools.ts +3 -0
  433. package/src/core/patch-layers.test.ts +94 -0
  434. package/src/core/patch-layers.ts +150 -0
  435. package/src/core/restore.ts +59 -3
  436. package/src/core/rollback.ts +6 -3
  437. package/src/core/section-content-consistency.test.ts +185 -0
  438. package/src/core/self-update.test.ts +110 -0
  439. package/src/core/self-update.ts +118 -0
  440. package/src/core/session-health.test.ts +91 -1
  441. package/src/core/session-health.ts +56 -9
  442. package/src/core/types.ts +127 -1
  443. package/src/foreign/antigravity.test.ts +271 -0
  444. package/src/foreign/antigravity.ts +188 -0
  445. package/src/foreign/bundle.ts +108 -0
  446. package/src/foreign/chatgpt.test.ts +279 -0
  447. package/src/foreign/chatgpt.ts +30 -0
  448. package/src/foreign/claude-code.test.ts +234 -0
  449. package/src/foreign/claude-code.ts +106 -0
  450. package/src/foreign/claude-sessions-bytes.test.ts +214 -0
  451. package/src/foreign/claude-sessions.test.ts +155 -0
  452. package/src/foreign/claude-sessions.ts +453 -0
  453. package/src/foreign/cline.test.ts +274 -0
  454. package/src/foreign/cline.ts +32 -0
  455. package/src/foreign/codex.test.ts +585 -0
  456. package/src/foreign/codex.ts +409 -0
  457. package/src/foreign/continue.test.ts +365 -0
  458. package/src/foreign/continue.ts +73 -0
  459. package/src/foreign/copilot.test.ts +273 -0
  460. package/src/foreign/copilot.ts +133 -0
  461. package/src/foreign/crush.test.ts +234 -0
  462. package/src/foreign/crush.ts +66 -0
  463. package/src/foreign/cursor.test.ts +509 -0
  464. package/src/foreign/cursor.ts +259 -0
  465. package/src/foreign/dsh.test.ts +198 -0
  466. package/src/foreign/dsh.ts +117 -0
  467. package/src/foreign/dsh4.test.ts +127 -0
  468. package/src/foreign/dsh4.ts +15 -0
  469. package/src/foreign/file-budget.test.ts +256 -0
  470. package/src/foreign/fixtures/antigravity/README.md +18 -0
  471. package/src/foreign/fixtures/antigravity/basic/gemini/antigravity/mcp_config.json +13 -0
  472. package/src/foreign/fixtures/antigravity/basic/gemini/config/mcp_config.json +24 -0
  473. package/src/foreign/fixtures/antigravity/empty/gemini/antigravity/mcp_config.json +0 -0
  474. package/src/foreign/fixtures/antigravity/empty/gemini/antigravity/mcp_oauth_tokens.json +0 -0
  475. package/src/foreign/fixtures/antigravity/empty/gemini/config/mcp_config.json +0 -0
  476. package/src/foreign/fixtures/codex/README.md +31 -0
  477. package/src/foreign/fixtures/codex/basic/.agents/skills/hello/SKILL.md +6 -0
  478. package/src/foreign/fixtures/codex/basic/.agents/skills/wrapper/nested/SKILL.md +6 -0
  479. package/src/foreign/fixtures/codex/basic/.codex/AGENTS.md +4 -0
  480. package/src/foreign/fixtures/codex/basic/.codex/config.toml +28 -0
  481. package/src/foreign/fixtures/codex/malformed/.codex/config.toml +3 -0
  482. package/src/foreign/fixtures/codex/override/.codex/AGENTS.md +1 -0
  483. package/src/foreign/fixtures/codex/override/.codex/AGENTS.override.md +3 -0
  484. package/src/foreign/fixtures/codex/sessions/.codex/archived_sessions/rollout-2026-10-05T09-00-00-22222222-2222-4222-8222-222222222222.jsonl +2 -0
  485. package/src/foreign/fixtures/codex/sessions/.codex/sessions/2026/10/06/rollout-2026-10-06T10-00-00-11111111-1111-4111-8111-111111111111.jsonl +15 -0
  486. package/src/foreign/fixtures/codex/sessions/.codex/sessions/2026/10/06/rollout-2026-10-06T11-00-00-33333333-3333-4333-8333-333333333333.jsonl +2 -0
  487. package/src/foreign/fixtures/codex/sessions/.codex/sessions/2026/10/06/rollout-2026-10-06T11-30-00-55555555-5555-4555-8555-555555555555.jsonl +2 -0
  488. package/src/foreign/fixtures/codex/sessions/.codex/sessions/2026/10/06/rollout-2026-10-06T12-00-00-44444444-4444-4444-8444-444444444444.jsonl +2 -0
  489. package/src/foreign/fixtures/codex/sessions/.codex/sessions/notes.jsonl +1 -0
  490. package/src/foreign/fixtures/copilot/README.md +22 -0
  491. package/src/foreign/fixtures/copilot/basic/copilot-instructions.md +3 -0
  492. package/src/foreign/fixtures/copilot/basic/instructions/empty.instructions.md +1 -0
  493. package/src/foreign/fixtures/copilot/basic/instructions/repo.instructions.md +5 -0
  494. package/src/foreign/fixtures/copilot/basic/mcp-config.json +24 -0
  495. package/src/foreign/fixtures/copilot/basic/skills/hello/SKILL.md +6 -0
  496. package/src/foreign/fixtures/copilot/overridden/mcp-config.json +10 -0
  497. package/src/foreign/fixtures/cursor/README.md +17 -0
  498. package/src/foreign/fixtures/cursor/alt/.cursor/mcp.json +5 -0
  499. package/src/foreign/fixtures/cursor/basic/.cursor/mcp.json +18 -0
  500. package/src/foreign/fixtures/cursor/basic/.cursor/rules/agent.mdc +5 -0
  501. package/src/foreign/fixtures/cursor/basic/.cursor/rules/always.mdc +6 -0
  502. package/src/foreign/fixtures/cursor/basic/.cursor/rules/auto.mdc +7 -0
  503. package/src/foreign/fixtures/cursor/basic/.cursor/rules/manual.mdc +3 -0
  504. package/src/foreign/fixtures/cursor/basic/.cursor/skills/.hidden/SKILL.md +6 -0
  505. package/src/foreign/fixtures/cursor/basic/.cursor/skills/hello/SKILL.md +6 -0
  506. package/src/foreign/fixtures/cursor/basic/.cursor/skills/wrapper/nested/SKILL.md +6 -0
  507. package/src/foreign/fixtures/cursor/basic/.cursor/skills/wrapper/nested/notes.md +1 -0
  508. package/src/foreign/fixtures/cursor/basic/.cursorrules +1 -0
  509. package/src/foreign/fixtures/cursor/basic/project/.cursor/mcp.json +9 -0
  510. package/src/foreign/fixtures/cursor/basic/project/.cursor/rules/project-always.mdc +5 -0
  511. package/src/foreign/fixtures/cursor/basic/project/.cursor/skills/hello/SKILL.md +6 -0
  512. package/src/foreign/fixtures/cursor/basic/project/.cursorrules +1 -0
  513. package/src/foreign/fixtures/cursor/empty/.cursor/mcp.json +0 -0
  514. package/src/foreign/fixtures/cursor/malformed/.cursor/mcp.json +3 -0
  515. package/src/foreign/fixtures/cursor/malformed/.cursor/rules/broken.mdc +6 -0
  516. package/src/foreign/fixtures/cursor/shape/.cursor/mcp.json +3 -0
  517. package/src/foreign/fixtures/hermes/README.md +14 -0
  518. package/src/foreign/fixtures/hermes/basic/SOUL.md +3 -0
  519. package/src/foreign/fixtures/hermes/basic/config.yaml +48 -0
  520. package/src/foreign/fixtures/hermes/basic/dotenv.fixture +1 -0
  521. package/src/foreign/fixtures/hermes/basic/memories/MEMORY.md +1 -0
  522. package/src/foreign/fixtures/hermes/basic/memories/USER.md +1 -0
  523. package/src/foreign/fixtures/hermes/basic/skills/se-team-design/SKILL.md +6 -0
  524. package/src/foreign/fixtures/hermes/basic/skills/se-team-design/examples/usage.md +1 -0
  525. package/src/foreign/fixtures/hermes/basic/skills/software-development/dogfood/SKILL.md +6 -0
  526. package/src/foreign/fixtures/hermes/basic/skills/software-development/dogfood/notes.md +1 -0
  527. package/src/foreign/fixtures/hermes/basic/state.db +1 -0
  528. package/src/foreign/fixtures/hermes/empty/config.yaml +0 -0
  529. package/src/foreign/gemini.test.ts +291 -0
  530. package/src/foreign/gemini.ts +52 -0
  531. package/src/foreign/goose.test.ts +196 -0
  532. package/src/foreign/goose.ts +62 -0
  533. package/src/foreign/grokbuild.test.ts +246 -0
  534. package/src/foreign/grokbuild.ts +30 -0
  535. package/src/foreign/hermes.test.ts +545 -0
  536. package/src/foreign/hermes.ts +86 -0
  537. package/src/foreign/kernel-credentials.test.ts +88 -0
  538. package/src/foreign/kernel-workspaces.test.ts +88 -0
  539. package/src/foreign/kernel.ts +584 -0
  540. package/src/foreign/kilocode.test.ts +186 -0
  541. package/src/foreign/kilocode.ts +60 -0
  542. package/src/foreign/kimi.test.ts +259 -0
  543. package/src/foreign/kimi.ts +55 -0
  544. package/src/foreign/mcp-value-shape.test.ts +286 -0
  545. package/src/foreign/mimocode.test.ts +178 -0
  546. package/src/foreign/mimocode.ts +60 -0
  547. package/src/foreign/openclaw.test.ts +248 -0
  548. package/src/foreign/openclaw.ts +54 -0
  549. package/src/foreign/opencode.test.ts +376 -0
  550. package/src/foreign/opencode.ts +61 -0
  551. package/src/foreign/pi.test.ts +275 -0
  552. package/src/foreign/pi.ts +56 -0
  553. package/src/foreign/platform-paths.ts +156 -0
  554. package/src/foreign/qoder.test.ts +266 -0
  555. package/src/foreign/qoder.ts +57 -0
  556. package/src/foreign/qwen.test.ts +203 -0
  557. package/src/foreign/qwen.ts +53 -0
  558. package/src/foreign/read-antigravity.ts +569 -0
  559. package/src/foreign/read-chatgpt.ts +506 -0
  560. package/src/foreign/read-claude-code.ts +264 -0
  561. package/src/foreign/read-cline.ts +517 -0
  562. package/src/foreign/read-codex.ts +823 -0
  563. package/src/foreign/read-continue.ts +557 -0
  564. package/src/foreign/read-copilot.ts +304 -0
  565. package/src/foreign/read-crush.ts +503 -0
  566. package/src/foreign/read-cursor.ts +778 -0
  567. package/src/foreign/read-dsh.ts +215 -0
  568. package/src/foreign/read-dsh4.ts +35 -0
  569. package/src/foreign/read-gemini.ts +306 -0
  570. package/src/foreign/read-goose.ts +333 -0
  571. package/src/foreign/read-grokbuild.ts +475 -0
  572. package/src/foreign/read-hermes.ts +724 -0
  573. package/src/foreign/read-kilocode.ts +55 -0
  574. package/src/foreign/read-kimi.ts +768 -0
  575. package/src/foreign/read-mimocode.ts +66 -0
  576. package/src/foreign/read-openclaw.ts +505 -0
  577. package/src/foreign/read-opencode.ts +994 -0
  578. package/src/foreign/read-pi.ts +459 -0
  579. package/src/foreign/read-qoder.ts +351 -0
  580. package/src/foreign/read-qwen.ts +342 -0
  581. package/src/foreign/read-reasonix.ts +590 -0
  582. package/src/foreign/read-teleagent.ts +82 -0
  583. package/src/foreign/read-trae.ts +488 -0
  584. package/src/foreign/read-vibe.ts +353 -0
  585. package/src/foreign/read-workbuddy.ts +374 -0
  586. package/src/foreign/read-zcode.ts +48 -0
  587. package/src/foreign/read-zed.ts +479 -0
  588. package/src/foreign/reasonix.test.ts +334 -0
  589. package/src/foreign/reasonix.ts +25 -0
  590. package/src/foreign/registry.test.ts +377 -0
  591. package/src/foreign/registry.ts +750 -0
  592. package/src/foreign/session-ir.test.ts +136 -0
  593. package/src/foreign/session-ir.ts +579 -0
  594. package/src/foreign/session-read.ts +295 -0
  595. package/src/foreign/session-source.ts +625 -0
  596. package/src/foreign/skill-file-caps.test.ts +171 -0
  597. package/src/foreign/source-modules.ts +86 -0
  598. package/src/foreign/source-registry.test.ts +190 -0
  599. package/src/foreign/sqlite.test.ts +313 -0
  600. package/src/foreign/sqlite.ts +446 -0
  601. package/src/foreign/teleagent.test.ts +147 -0
  602. package/src/foreign/teleagent.ts +62 -0
  603. package/src/foreign/trae.test.ts +316 -0
  604. package/src/foreign/trae.ts +25 -0
  605. package/src/foreign/truncation-report.test.ts +238 -0
  606. package/src/foreign/truth-table.ts +453 -0
  607. package/src/foreign/types.ts +312 -0
  608. package/src/foreign/vibe.test.ts +209 -0
  609. package/src/foreign/vibe.ts +28 -0
  610. package/src/foreign/workbuddy.test.ts +207 -0
  611. package/src/foreign/workbuddy.ts +53 -0
  612. package/src/foreign/zcode.test.ts +224 -0
  613. package/src/foreign/zcode.ts +60 -0
  614. package/src/foreign/zed.test.ts +240 -0
  615. package/src/foreign/zed.ts +62 -0
  616. package/src/index.facade.test.ts +254 -16
  617. package/src/index.ts +704 -82
  618. package/src/market/github-repos.test.ts +98 -1
  619. package/src/market/github-repos.ts +63 -1
  620. package/src/market/types.ts +3 -3
  621. package/src/profiles/dsh-profile-incomplete-copy.test.ts +177 -0
  622. package/src/profiles/dsh-profile-manager.test.ts +37 -0
  623. package/src/profiles/dsh-profile-manager.ts +188 -13
  624. package/src/profiles/dsh-profile-shared.ts +55 -1
  625. package/src/profiles/index.ts +2 -1
  626. package/src/routes/consult.ts +53 -4
  627. package/src/routes/foreign-context.ts +89 -0
  628. package/src/routes/foreign.test.ts +385 -0
  629. package/src/routes/foreign.ts +226 -0
  630. package/src/routes/import.ts +23 -1
  631. package/src/routes/index.ts +6 -2
  632. package/src/routes/kit.ts +4 -3
  633. package/src/routes/market.ts +0 -1
  634. package/src/routes/prefs.ts +15 -1
  635. package/src/routes/recovery.test.ts +167 -0
  636. package/src/routes/recovery.ts +331 -1
  637. package/src/routes/session-export.ts +308 -0
  638. package/src/routes/snapshots.ts +12 -2
  639. package/src/routes/sync.ts +91 -7
  640. package/src/security/redaction.test.ts +149 -0
  641. package/src/security/redaction.ts +25 -9
  642. package/src/security/secret-scanner.test.ts +186 -0
  643. package/src/security/secret-scanner.ts +57 -1
  644. package/src/sync/autosync-risk-tier.test.ts +61 -0
  645. package/src/sync/autosync-scheduler.test.ts +48 -1
  646. package/src/sync/autosync-scheduler.ts +50 -26
  647. package/src/sync/backup-files.ts +5 -0
  648. package/src/sync/gist/gist-msgkeys-catalog.test.ts +33 -0
  649. package/src/sync/gist/gist-msgkeys.test.ts +71 -0
  650. package/src/sync/gist/gist-transport.test.ts +308 -0
  651. package/src/sync/gist/gist-transport.ts +550 -0
  652. package/src/sync/gist/messages.ts +49 -0
  653. package/src/sync/retention-prune-all-off.test.ts +50 -0
  654. package/src/sync/s3/cloud-config.test.ts +283 -0
  655. package/src/sync/s3/messages.ts +102 -0
  656. package/src/sync/s3/s3-delete-index-404.test.ts +170 -0
  657. package/src/sync/s3/s3-delete-index-unreadable.test.ts +141 -0
  658. package/src/sync/s3/s3-msgkeys.test.ts +101 -0
  659. package/src/sync/s3/s3-providers.ts +213 -0
  660. package/src/sync/s3/s3-transport.test.ts +394 -0
  661. package/src/sync/s3/s3-transport.ts +651 -0
  662. package/src/sync/s3/s3-upload-index-404.test.ts +171 -0
  663. package/src/sync/s3/sigv4.test.ts +219 -0
  664. package/src/sync/s3/sigv4.ts +283 -0
  665. package/src/sync/sync-config.test.ts +237 -5
  666. package/src/sync/sync-config.ts +841 -52
  667. package/src/sync/sync-engine-download-tombstone.test.ts +73 -0
  668. package/src/sync/sync-engine.ts +5 -3
  669. package/src/sync/sync-history.ts +1 -1
  670. package/src/sync/webdav/webdav-delete-index-404.test.ts +178 -0
  671. package/src/sync/webdav/webdav-delete-index-unreadable.test.ts +143 -0
  672. package/src/sync/webdav/webdav-msgkeys.test.ts +53 -0
  673. package/src/sync/webdav/webdav-transport.test.ts +9 -5
  674. package/src/sync/webdav/webdav-transport.ts +99 -17
  675. package/src/sync/webdav/webdav-upload-index-404.test.ts +177 -0
  676. package/src/ui/commands.test.ts +4 -1
  677. package/src/ui/commands.ts +46 -12
  678. package/src/ui/disk-usage-view.test.ts +63 -1
  679. package/src/ui/disk-usage-view.ts +93 -1
  680. package/src/ui/dsh-profiles-incomplete-view.test.ts +102 -0
  681. package/src/ui/dsh-profiles-view.ts +94 -0
  682. package/src/ui/foreign-view.test.ts +241 -0
  683. package/src/ui/foreign-view.ts +362 -0
  684. package/src/ui/i18n.ts +164 -0
  685. package/src/ui/mid-ellipsis.test.ts +5 -10
  686. package/src/ui/session-inventory-view.test.ts +142 -2
  687. package/src/ui/session-inventory-view.ts +127 -0
  688. package/src/ui/session-layout-view.test.ts +361 -0
  689. package/src/ui/session-layout-view.ts +444 -0
  690. package/src/ui/sync-repository-picker.test.ts +130 -0
  691. package/src/ui/sync-repository-picker.ts +180 -0
  692. package/src/ui/sync-settings-view.test.ts +166 -0
  693. package/src/ui/sync-settings-view.ts +319 -9
  694. package/src/ui/types.ts +42 -0
  695. package/src/utils/asar-read.test.ts +330 -0
  696. package/src/utils/asar-read.ts +606 -0
  697. package/src/utils/env-lock.test.ts +55 -0
  698. package/src/utils/env-lock.ts +6 -1
  699. package/src/utils/patch-yaml.test.ts +112 -0
  700. package/src/utils/patch-yaml.ts +72 -0
  701. package/src/utils/paths.test.ts +63 -0
  702. package/src/utils/paths.ts +25 -3
  703. package/src/utils/recursive-walk.test.ts +20 -0
  704. package/src/utils/recursive-walk.ts +12 -13
  705. package/src/utils/session-format.test.ts +113 -2
  706. package/src/utils/session-format.ts +127 -5
  707. package/src/utils/session-health-scan.test.ts +364 -1
  708. package/src/utils/session-health-scan.ts +434 -15
  709. package/src/utils/session-layout-repair-service.test.ts +362 -0
  710. package/src/utils/session-layout-repair-service.ts +568 -0
  711. package/src/utils/session-log-repair.test.ts +260 -1
  712. package/src/utils/session-log-repair.ts +294 -55
  713. package/src/utils/session-repair-service.test.ts +148 -1
  714. package/src/utils/session-repair-service.ts +165 -10
  715. package/src/utils/session-row-facts.test.ts +159 -0
  716. package/src/utils/session-row-facts.ts +187 -0
  717. package/src/utils/session-verify.test.ts +762 -0
  718. package/src/utils/session-verify.ts +784 -0
  719. package/src/utils/shared-constants.ts +82 -0
package/README.md CHANGED
@@ -19,6 +19,7 @@ Backup, restore, export, import, migrate and sync your complete DeepSeek Harness
19
19
  - 🔐 Encrypted backups with optional credentials
20
20
  - ☁️ **Git / WebDAV** configuration sync
21
21
  - 🛒 **Configuration market** — browse & one-click install shared configs
22
+ - 🧳 **Import from other AI agents** — Claude Code, Cursor, Codex, Hermes, Antigravity … config **and chat history**
22
23
  - ↩️ Automatic snapshot and rollback before restore
23
24
 
24
25
  [English](README.md) · [简体中文](README.zh-CN.md)
@@ -46,7 +47,7 @@ DSH is your AI assistant workbench — it holds your settings: model configs, pl
46
47
 
47
48
  ### Backup DeepSeek Harness configuration
48
49
 
49
- Create a portable backup of your DSH settings, model providers, plugins, MCP servers, skills, agent presets and workspace — one ZIP file, no secret values included by default. (DSH's own profiles — `$DSH_HOME/profiles/<name>`, i.e. which plugin stack to boot — are machine-local and are not migrated; the **Profiles** page can list/create/rename/delete them and record which one the next launch should use.)
50
+ Create a portable backup of your DSH settings, model providers, plugins, MCP servers, skills, agent presets and workspace — one ZIP file, no secret values included by default. (DSH's own profiles — `$DSH_HOME/profiles/<name>`, i.e. which plugin stack to boot — are machine-local and are **not** part of the backup; the **Profiles** page manages them instead — list / create / rename / delete, plus launching or stopping an independent instance for one.)
50
51
 
51
52
  ### Restore DeepSeek Harness on another machine
52
53
 
@@ -68,6 +69,10 @@ Turn on scheduled backups (6h / 12h / 24h / 7d — or a **custom weekly weekday
68
69
 
69
70
  Browse the built-in official market for ready-made configurations (model providers, plugins, MCP servers, skills, agent presets…), preview what would be imported (dry-run), and install with one click — supply-chain warnings are always shown and every section must be explicitly approved before anything is written.
70
71
 
72
+ ### Import from another AI agent (Claude Code, Cursor, Codex, …)
73
+
74
+ Your setup — and your chat history — already live in another AI coding agent on this machine? Pick the source and DSH Config Manager **translates it into a standard bundle** (MCP servers, skills, global instructions, conversations and the workspaces they belong to) and hands it to the same review-first import flow: preview → per-item conflict decisions → automatic snapshot → apply → rollback. 30 sources are recognized and 29 of them can migrate **chat history** too. Secret **values** are never read — only the key names, so the import plan can ask you to re-enter them.
75
+
71
76
  ---
72
77
 
73
78
  ## 🆚 How it differs from the other DSH backup / sync plugins
@@ -77,7 +82,7 @@ Several DSH plugins live in this space and they solve different problems — pic
77
82
  | Plugin | Strongest at | Where DSH Config Manager goes further |
78
83
  |---|---|---|
79
84
  | [xiaoyuyu6420/dsh-backup](https://github.com/xiaoyuyu6420/dsh-backup) | One-command `~/.dsh` snapshots from the CLI, plus session doctor / upgrade snapshots / rescue console | Review-before-write GUI flow (dry-run preview, per-item conflict decisions, automatic rollback), cross-machine path remapping, encrypted credential payload, configuration marketplace |
80
- | [muyifc/dsh-config-sync](https://github.com/muyifc/dsh-config-sync) | Export / import DSH configuration to a portable, password-encrypted file, callable from tool calls | 13–14 sections incl. plugins / MCP / skills / profiles / workspaces, scheduled backups, Git + WebDAV sync per channel, session migration with path rebase |
85
+ | [muyifc/dsh-config-sync](https://github.com/muyifc/dsh-config-sync) | Export / import DSH configuration to a portable, password-encrypted file, callable from tool calls | 13–14 sections (plugins / MCP / skills / workspaces / session logs …), scheduled backups, Git + WebDAV sync per channel, session migration with path rebase |
81
86
  | [dickpy/dsh-cloud-sync](https://github.com/dickpy/dsh-cloud-sync) · [weibaohui/dsh-sync](https://github.com/weibaohui/dsh-sync) | Keeping machines consistent through WebDAV / S3 or a private Git mirror | Sync is one of five capabilities here — alongside export/import, scheduling, marketplace and profile instance launch/stop |
82
87
  | `cp -r ~/.dsh` (or Git on the home dir) | Free, zero setup, fine for a purely textual config | No secret handling, no path remapping, no capture of `link:` / `file:` plugin installs, no session-log work, no conflict handling or rollback |
83
88
 
@@ -102,10 +107,13 @@ Several DSH plugins live in this space and they solve different problems — pic
102
107
  | 🛒 | **Config Marketplace** | Browse & one-click install community configs — supply-chain warnings + per-item content selection (change summary + in-place high-risk flags) |
103
108
  | 🗂️ | **Profiles (DSH profiles)** | Manage `$DSH_HOME/profiles/<name>` directly: list / create from a shipped template / rename / hard delete / **launch this profile (independent instance)** / **stop the instance** (the row button flips between Launch and Stop with the running state) |
104
109
  | 🌐 | **Bilingual UI** | Interface, reports and error details follow the DSH app language (中文 / English) |
110
+ | 🧩 | **Local plugin migration** | `link:` / `file:` development plugins are packed into the backup, so switching machines does not lose them |
111
+ | 🗄️ | **Configurable retention (GFS tiers)** | "keep the last N + one per month + one per year" — the defaults are equivalent to the previous behaviour |
105
112
  | 🤖 | **Agent tools** | Backup / snapshot / restore / sync right from an agent session |
106
113
  | 💾 | **Disk usage report** | The backups page shows how much space the plugin's own artifacts take (backups / snapshots / sync copies / caches / staging) with a three-tier cleanup policy; **one-click cleanup only touches regenerable caches and expired backups** — snapshots and sync data are never removed there |
107
- | ⬆️ | **Update check** | The About page reads the latest version from npm and, when there is a newer one, hands you a copyable upgrade command (never auto-installs); offline failures are reported honestly and affect nothing else |
114
+ | ⬆️ | **Update check** | The About page reads the latest version from npm (read-only, cached 10 minutes); when there is a newer one it offers a copyable upgrade command **and** a one-click **Update now** that installs exactly that version — DSH must then be restarted, and the plugin never restarts it for you. Offline failures are reported honestly and affect nothing else |
108
115
  | 🧭 | **Compatibility explained** | Before importing you see "source DSH version / platform → local" plus **structured reasons** for the score (cross-platform / missing sections / newer source …) instead of a bare "partial" |
116
+ | 🧳 | **Import from other AI agents** | Read Claude Code / Cursor / Codex / Hermes / Antigravity … configuration **and chat history** on this machine, translate it into a standard bundle, then import through the usual preview / conflict / rollback flow |
109
117
 
110
118
  ---
111
119
 
@@ -307,6 +315,17 @@ When the target already has a same-named item, you choose:
307
315
  2. Lets you pick new paths
308
316
  3. Supports **batch prefix mapping** (`C:\Users\alice\` → `/Users/bob/` in one shot)
309
317
 
318
+ ### 🧳 Import from other AI agents (config + chat history)
319
+
320
+ You do not have to rebuild another agent's setup by hand.
321
+
322
+ - **30 sources recognized** — Claude Code, Hermes, Cursor, Codex, Antigravity, Gemini, OpenCode, Mimocode, ZCode, Grok Build, OpenClaw, Pi, Kimi, Kilocode, Qoder, ChatGPT, WorkBuddy, Qwen, Continue, Cline, Goose, Zed, Crush, TeleAgent, Trae, Vibe, Reasonix, Copilot, and DSH itself (importing from another DSH home — its v3 and v4 logs count as two ids). Sources **not** installed on this machine are still listed (greyed out, not selectable), so "this machine has no such tool" is never mistaken for "the feature is missing".
323
+ - **Translation, not a second import path** — the source is converted into a standard bundle v1 ZIP and handed to the **existing** import wizard: same preview, same per-item conflict decisions, same pre-import snapshot and rollback, same dry-run.
324
+ - **What travels** — MCP servers, skills, global instructions (as `AGENTS.md`), and **chat history together with the workspaces those conversations belong to** (29 sources; conversations are re-encoded into DSH's session-log format and placed by their recorded `cwd`).
325
+ - **What does not** — secret **values** are never read: only the key names are recorded so the plan can ask you to re-enter them. Structures DSH has no equivalent for (Claude Code hooks, slash commands, …) are reported as explicit codes rather than silently dropped.
326
+ - **Honest skips** — a conversation with no recorded `cwd` cannot be placed and is reported as skipped (never guessed into some other project); known lossy items are listed **before** the import runs.
327
+ - **Entry points** — Settings → "Backup & Migration" → Import → **"Import from another agent"**, the ⌘K command, or the CLI: `dcm import --from <source> [--dry-run] [--out <path>]`.
328
+
310
329
  ### 🔒 Secrets
311
330
 
312
331
  | Scenario | Behavior |
@@ -325,7 +344,7 @@ Push / pull your portable config between machines through **either of two channe
325
344
  | **Endpoint** | `repoUrl` | `webdav.url` |
326
345
  | **Credentials** | auth token in DSH credentials (`DSH_CONFIG_MANAGER_SYNC_TOKEN`) | `username` stored in the config (echoed in the UI); **password never synced / never logged** — DSH credentials `DSH_CONFIG_MANAGER_SYNC_WEBDAV_PASSWORD` |
327
346
 
328
- - **Same snapshot retention for both channels**: only the newest **10** snapshots are kept on the remote (`MAX_REMOTE_SNAPSHOTS=10`); older ones are deleted automatically.
347
+ - **Remote retention follows your backup schedule**: both channels keep the same policy as local backups — by default the newest **10** snapshots, plus optional "one per month / one per year" tiers — and the snapshot you just pushed is always kept; older ones are deleted automatically.
329
348
  - **Switching channels starts fresh**: Git and WebDAV do **not** share snapshots or a common ancestor. When you switch transport, sync begins again from the new remote's empty baseline — push a fresh snapshot first.
330
349
  - **WebDAV auth** uses HTTP Basic: the `username` is stored in the config and may be echoed back into the UI, while the `password` is read live from the DSH credentials slot `DSH_CONFIG_MANAGER_SYNC_WEBDAV_PASSWORD` — it never appears in any sync file or log.
331
350
  - **Plugins auto-install**: when pulling diffs, plugins that are new in the backup are **installed automatically** on confirm — no manual per-item ticking in the diff list. Only **version-conflict** plugins still ask you to pick "Keep Current / Use Imported".
@@ -419,22 +438,40 @@ npm install -g dsh-config-manager@latest --omit=peer
419
438
 
420
439
  > ⚠️ Installing/updating the plugin (`dsh plugin --profile web add ...`) only enables the GUI — it does **not** create the `dsh-config-manager` command. Run the install command above, then any of the commands below.
421
440
 
422
- All commands (also shown by `dsh-config-manager help`):
441
+ All commands (also shown by `dsh-config-manager help`, which groups them by risk; `dsh-config-manager <command> --help` prints one command's options, exit codes and examples):
423
442
 
424
443
  ```text
425
- dsh-config-manager help # list all commands & options
444
+ # Rescue console (local web page — the least typing)
445
+ dsh-config-manager web [--port <n>] [--no-open] [--home <dir>]
446
+ [--data-root <dir>] [--idle-timeout <min>]
447
+
448
+ # Inspect (never touches your config)
426
449
  dsh-config-manager snapshots [--data-dir <dir>] # list snapshots (newest first)
427
- dsh-config-manager restore [--id <id>] [--dry-run]
428
- [--profile <name>] [--settings <path>]
429
- dsh-config-manager reinstall [--version <v>] [--yes] [--list]
430
- [--wipe-config] [--dry-run] # one-click reinstall of DSH itself
431
- dsh-config-manager recover-stale-lock [--data-dir <dir>] # clear a leftover lock (see below)
432
- dsh-config-manager verify [--id <file|path>] [--json] # read-only check of backup ZIPs
433
- [--data-dir <dir>]
434
- dsh-config-manager backup [--sections <a,b,c>] [--out <path>] # offline file-level backup
450
+ dsh-config-manager verify [<file|path>] [--json] # read-only check of backup ZIPs
451
+ [--data-dir <dir>]
452
+ dsh-config-manager sessions list [--home <dir>] [--json] # list local sessions
453
+ dsh-config-manager sessions doctor [--home <dir>] [--json] # health check + advice
454
+
455
+ # Backup & migrate (only writes new files)
456
+ dsh-config-manager backup [--sections <a,b,c>] [--out <path>] # offline file-level backup
435
457
  [--dry-run] [--data-dir <dir>]
436
- dsh-config-manager sessions repair [--home <dir>] [--fix] # offline session layout repair
458
+ dsh-config-manager import --from <source> [--dry-run] # foreign agent config → bundle ZIP
459
+ [--out <path>] [--cwd <dir>] [--data-dir <dir>]
460
+
461
+ # Repair (writes to this machine — preview with --dry-run first)
462
+ dsh-config-manager restore [--id <id>] [--dry-run] # roll back to a pre-import snapshot
463
+ [--data-dir <dir>] [--data-root <dir>]
464
+ [--profile <name>] [--settings <path>]
465
+ dsh-config-manager sessions repair [--home <dir>] [--fix] # offline session layout repair
437
466
  [--keep <dir>] [--map old=new]...
467
+ dsh-config-manager recover-stale-lock [--data-dir <dir>] # clear a leftover lock (see below)
468
+
469
+ # Dangerous (changes your DSH install / may wipe data)
470
+ dsh-config-manager reinstall [--version <v>] [--yes] [--list]
471
+ [--wipe-config] [--dry-run] [--data-root <dir>]
472
+
473
+ # Help
474
+ dsh-config-manager help [command] # overview / one command's detail
438
475
  ```
439
476
 
440
477
  **`reinstall` — rescue when DSH is broken.** It reinstalls the `@deepseek-ai/dsh` launcher across platforms (uses the right command per OS: PowerShell on Windows, bash on Unix). By default it reinstalls the launcher + clears global caches; interactively it asks which **dangerous** clean-up items to include (settings / plugins / session data & credentials) — those are **not** selected by default, and any destructive choice requires a second confirmation by typing `YES` before anything runs. Before wiping any `~/.dsh` data it makes an emergency backup at `.reinstall-backup` (the `snapshots/` folder is deliberately never touched).
@@ -489,6 +526,8 @@ dsh-config-manager verify C:/backups/dsh-config.zip # ...or by path
489
526
 
490
527
  **Credential files never enter a backup.** `.credentials.*`, `.env`, `*.pem` and friends are excluded by an explicit blacklist, only whitelisted directories are walked (never the whole home directory), and symlinks are skipped rather than followed.
491
528
 
529
+ **Offline backup only covers on-disk skills.** Skills that live in the plugin registry (the ones the shell loads from a profile's plugin packages — see issue #71) need the DSH runtime to enumerate, so only the GUI export (or a scheduled backup while DSH is healthy) can include them; the CLI `backup` collects just the files under `$DSH_HOME/skills`. For a complete skills backup, use the GUI export while DSH is healthy.
530
+
492
531
  Structured sections (settings / UI / providers / plugins / MCP / prompts / workspaces) are **not** silently faked: they need the DSH service layer to read and redact, so they are left out and marked `false` in the manifest — the plan printout lists them under “not offline-collectable” so you know exactly what this backup does and does not contain. Use the GUI export when DSH is healthy for a full backup.
493
532
 
494
533
  ```bash
@@ -500,7 +539,7 @@ dsh-config-manager backup --sections skills,self # narrow the scope
500
539
 
501
540
  `--sections` accepts `skills,agentPresets,agentInstructions,self,pluginFiles`. `pluginFiles` is **opt-in** (as in the GUI): it copies third-party plugin files verbatim, and `dsh-ssh.json` holds plaintext host passwords — select it only when you have looked at what is in there.
502
541
 
503
- **A typical rescue flow** when DSH won't start: ① `dsh-config-manager reinstall` to bring the launcher back (plus any clean-up), ② if DSH reports a session-log error (`corrupt session log` / `duplicate JSONL session id`), run `dsh-config-manager sessions repair --fix` first — it repairs the log layout offline, ③ `dsh web` to start DSH again, ③ re-add the plugin from the registry, and ④ pull a snapshot from the remote repo (or run `dsh-config-manager restore`) to bring your config back. The CLI works at every step regardless of DSH's health.
542
+ **A typical rescue flow** when DSH won't start: ① `dsh-config-manager reinstall` to bring the launcher back (plus any clean-up), ② if DSH reports a session-log error (`corrupt session log` / `duplicate JSONL session id`), run `dsh-config-manager sessions repair --fix` — it repairs the log layout offline, ③ `dsh web` to start DSH again, ④ re-add the plugin from the registry, and ⑤ pull a snapshot from the remote repo (or run `dsh-config-manager restore`) to bring your config back. The CLI works at every step regardless of DSH's health.
504
543
 
505
544
  **`recover-stale-lock` — when every operation suddenly fails.** Before touching your config, the plugin claims a small environment lock (it records who is operating plus a heartbeat) so that two operations can never write your config at the same time. If a `dsh web` process is **force-killed** (Task Manager, `kill -9`), the lock file survives with a dead owner: the next operation is refused, and it stays refused **no matter how often you retry or restart DSH** — because the plugin deliberately never removes a lock on its own (a wrong guess could evict a live operation).
506
545
 
@@ -630,7 +669,7 @@ Yes. Dead absolute paths are detected and remapped (batch replacement supported)
630
669
  No. A checksum mismatch rejects the import outright (protects against corruption or tampering).
631
670
 
632
671
  **Q: Will re-importing duplicate things?**
633
- No. Items are deduplicated by stable IDs (plugin ID / MCP name / skill name…); existing items are skipped.
672
+ No. Items are matched by stable IDs (plugin ID / MCP name / skill name…): identical ones are skipped, and ones that differ from the target surface as **conflicts you decide** (Keep Current / Use Imported) — nothing is overwritten silently.
634
673
 
635
674
  **Q: Why is the console quiet after `dsh web` — how do I get the plugin logs back?**
636
675
  By design. Routine progress logs (mount banner, scheduler skips, export/backup completion) are emitted at `info`, and the shipped default level is `warn` — so only warnings and errors reach the terminal. Set `DSH_CONFIG_MANAGER_LOG_LEVEL=info` (or `debug`) before starting DSH to bring the verbose lines back.
@@ -645,9 +684,11 @@ Yes. The import wizard asks for the export-time encryption password and verifies
645
684
  1. **Installing / updating plugins or MCP takes effect after restarting DSH**
646
685
  2. **Some UI state is not migrated** (e.g. task board data, panel widths — they live in the browser, not in DSH's config files)
647
686
  3. **keybindings / workflow configs / commands** — DSH has no such concepts, so nothing is exported for them. Global agent rules are covered by **Agent Instructions** (`~/.dsh/AGENTS.md`, injected into every session); per-project `AGENTS.md`/`CLAUDE.md` belong to each project's repo and are not migrated
648
- 4. **History/session migration is off by default** (v1 copies files only)
687
+ 4. **History/session migration is opt-in** — DSH's own sessions are only exported/synced when you explicitly select the `sessions` section (the sync channel additionally needs both sides to allow it); **another agent's chat history is migrated when you pick that source** in the foreign import. Conversation state that DSH's session format has no room for (model reasoning traces, images, compaction checkpoints) is counted and reported, never fabricated
649
688
  5. **Encrypted backups**: a lost password means the `secrets.enc` can't be decrypted (by design — keep your password safe)
650
689
  6. **Snapshot restore is offline and honest**: entries the offline engine can't restore (settings namespaces / patch lines when the snapshot has no whole-file backup, workspace records stored in DSH storages) are reported as skipped with a pointer to online rollback; credential **values** are never auto-written (manual re-entry hint only); old snapshots without a plugin baseline only get a hint to remove added plugins manually
690
+ 7. **Foreign-agent import has two known blanks**: **Copilot has no chat-history import** (its session format could not be verified on this machine, so only its configuration is imported), and Cursor's per-project history is located through Cursor's own folder-slug scheme, which may not resolve for every project. Conversations already imported with an older build should be deleted and re-imported first (a second import of the same id is reported as a **skipped** `session-id-conflict`, not merged)
691
+ 8. **Local source plugins (`link:` / `file:`) are packed into the backup**: the export runs `npm pack` on plugins you are developing locally and stores the tarball in the backup; the import unpacks it under `$DSH_HOME/dsh-config-manager/local-plugins/` and installs it as `file:`. Three consequences: ① backup size grows with those plugins (a single plugin over 100 MB is skipped with a warning — publish it to a registry / git first); ② plugin **source code** enters the backup (this does not conflict with "no secrets in backups" — secrets stay excluded, what travels is code); ③ packing needs a working `npm` on this machine; without it the plugin falls back to the previous behaviour (the original spec is kept, and a new machine still needs a manual install)
651
692
 
652
693
  ## 💬 Feedback
653
694
 
package/README.zh-CN.md CHANGED
@@ -21,7 +21,7 @@ DSH Config Manager 是一个 DeepSeek Harness 配置备份与迁移插件:一
21
21
  > DSH 的 profile(`$DSH_HOME/profiles/<name>`)本身**不随备份迁移**(它是「用哪套插件组合启动」的机器本地选择);
22
22
  > 本插件的「档案」页可以列表 / 新建 / 重命名 / 删除它们,也可以**用某个档案另起一个独立实例并随时停止它**(真正可用的切换)。
23
23
 
24
- 把当前 DeepSeek Harness 环境导出为可移植备份,在另一台电脑上一键恢复;也支持通过 Git / WebDAV 跨机同步(密钥默认不同步,勾选「导出密钥」并加密后可随加密快照迁移);还能通过内置**配置市场**浏览、一键安装社区分享的现成配置。
24
+ 把当前 DeepSeek Harness 环境导出为可移植备份,在另一台电脑上一键恢复;也支持通过 Git / WebDAV 跨机同步(密钥默认不同步,勾选「导出密钥」并加密后可随加密快照迁移);还能通过内置**配置市场**浏览、一键安装社区分享的现成配置;并且能**从其它 AI Agent 导入**配置与历史会话(Claude Code / Cursor / Codex / Hermes / Antigravity 等 30 个来源)。
25
25
 
26
26
  [English](README.md) · [简体中文](README.zh-CN.md)
27
27
 
@@ -48,7 +48,7 @@ DSH 是你的 AI 助手工作台,里面存着你的各种设置:模型配置
48
48
 
49
49
  ### 备份 DeepSeek Harness 配置
50
50
 
51
- 把你的 DSH 设置、模型供应商、插件、MCP 服务器、技能、Agent 预设、配置档案与工作区打包成一个便携 ZIP 备份——默认不含任何密钥值。
51
+ 把你的 DSH 设置、模型供应商、插件、MCP 服务器、技能、Agent 预设与工作区打包成一个便携 ZIP 备份——默认不含任何密钥值(DSH 档案不随备份迁移,见上方说明)。
52
52
 
53
53
  ### 在另一台电脑上恢复 DeepSeek Harness
54
54
 
@@ -70,6 +70,10 @@ DSH 是你的 AI 助手工作台,里面存着你的各种设置:模型配置
70
70
 
71
71
  在内置官方市场浏览现成配置(模型 / Provider、插件、MCP、技能、Agent 预设……),先 dry-run 预览再一键安装——供应链警示恒展示,确认导入前每个分区都须显式批准。
72
72
 
73
+ ### 从其它 AI Agent 导入(Claude Code / Cursor / Codex…)
74
+
75
+ 你在这台机器上已经用过别的 AI 编程 Agent,配置和历史对话都在?选一个来源,本插件会把它**翻译成标准 bundle**(MCP、技能、全局指令、历史会话及其所属工作区),再交给同一条「先审后写」的导入流程:预览 → 逐项冲突决策 → 自动快照 → 应用 → 可回滚。共识别 **30 个来源**,其中 **29 个**可一并迁移**历史会话**。密钥**值**绝不读取,只记下名字供导入计划提示补录。
76
+
73
77
  ---
74
78
 
75
79
  ## 🆚 与其它备份 / 同步插件的区别
@@ -79,7 +83,7 @@ DSH 生态里这个方向有几个插件,它们解决的问题并不相同—
79
83
  | 插件 | 最擅长 | 本插件更进一步的地方 |
80
84
  |---|---|---|
81
85
  | [xiaoyuyu6420/dsh-backup](https://github.com/xiaoyuyu6420/dsh-backup) | 一条命令从 CLI 给整个 `~/.dsh` 打快照,另有会话体检 / 升级快照 / 救援控制台 | 写盘前可审阅的 GUI 流程(dry-run 预览、冲突逐项决策、失败自动回滚)、跨机路径重映射、加密凭据载荷、配置市场 |
82
- | [muyifc/dsh-config-sync](https://github.com/muyifc/dsh-config-sync) | 把 DSH 配置导出/导入成可移植的密码加密文件,可被工具调用 | 13–14 个分区(含插件 / MCP / 技能 / 档案 / 工作区)、定时备份、Git + WebDAV 双通道同步、会话跨机迁移与路径重定基 |
86
+ | [muyifc/dsh-config-sync](https://github.com/muyifc/dsh-config-sync) | 把 DSH 配置导出/导入成可移植的密码加密文件,可被工具调用 | 13–14 个分区(插件 / MCP / 技能 / 工作区 / 会话日志 …)、定时备份、Git + WebDAV 双通道同步、会话跨机迁移与路径重定基 |
83
87
  | [dickpy/dsh-cloud-sync](https://github.com/dickpy/dsh-cloud-sync) · [weibaohui/dsh-sync](https://github.com/weibaohui/dsh-sync) | 通过 WebDAV / S3 或私有 Git 镜像让多台机器保持一致 | 同步只是本插件五项能力之一——另有导出/导入、定时备份、配置市场与档案实例启停 |
84
88
  | `cp -r ~/.dsh`(或给 home 目录挂 Git) | 免费、零配置,纯文本配置够用 | 不处理密钥、不做路径重映射、抓不到 `link:` / `file:` 安装的本地插件、不动会话日志、没有冲突处理与回滚 |
85
89
 
@@ -105,10 +109,12 @@ DSH 生态里这个方向有几个插件,它们解决的问题并不相同—
105
109
  | 🗂️ | **档案 Profiles(DSH profile)** | 直接管理 `$DSH_HOME/profiles/<name>`:列表 / 新建(官方模板)/ 重命名 / 物理删除 / **启动该档案(独立实例)** / **停止实例**(行内按钮按运行状态在「启动 ↔ 停止」间切换) |
106
110
  | 🧩 | **本地插件随备份迁移** | `link:` / `file:` 安装的本地开发插件会被打包进备份,换机不再丢失 |
107
111
  | 🗄️ | **保留策略可配(GFS 分层)** | 「最近 N 份 + 每月留 1 份 + 每年留 1 份」,默认值等价旧行为 |
112
+ | 🌐 | **双语界面** | 界面、报告与错误详情跟随 DSH 应用语言(中文 / English) |
108
113
  | 🤖 | **Agent 工具** | Agent 会话内直接备份 / 快照 / 恢复 / 同步 |
109
114
  | 💾 | **磁盘占用体检** | 备份页逐项列出本插件自身占了多少盘(备份 / 快照 / 同步副本 / 缓存 / 暂存),三档回收策略一目了然;**一键清理只碰可重建缓存与过期备份**,快照与同步数据永不在此删除 |
110
- | ⬆️ | **版本更新检查** | 「关于」页只读探测 npm 上的最新版本,有新版本时给一条可复制的升级命令(绝不自动安装);离线失败如实显示,不影响其它功能 |
115
+ | ⬆️ | **版本更新检查** | 「关于」页只读探测 npm 上的最新版本(缓存 10 分钟);有新版本时既给可复制的升级命令,也提供一键**立即更新**(按该精确版本安装)——之后需重启 DSH 才生效,插件绝不代你重启;离线失败如实显示,不影响其它功能 |
111
116
  | 🧭 | **兼容性讲清楚** | 导入前显示「来源 DSH 版本 / 平台 → 本机」,并用**结构化原因**解释评分(跨平台 / 分区缺失 / 版本超前…),而不是只丢一句「部分兼容」 |
117
+ | 🧳 | **从其它 AI Agent 导入** | 读取本机 Claude Code / Cursor / Codex / Hermes / Antigravity 等的配置**与历史会话**,翻译成标准 bundle,再走既有的预览 / 冲突 / 回滚流程导入 |
112
118
 
113
119
  ---
114
120
 
@@ -305,6 +311,17 @@ allowBuilds:
305
311
  2. 让你选择新路径
306
312
  3. 支持**批量前缀映射**(如 `C:\Users\alice\` → `/Users/bob/` 一键替换所有相关路径)
307
313
 
314
+ ### 🧳 从其它 AI Agent 导入(配置 + 历史会话)
315
+
316
+ 不必手工重建另一个 Agent 里已经调好的那套配置。
317
+
318
+ - **识别 30 个来源** —— Claude Code、Hermes、Cursor、Codex、Antigravity、Gemini、OpenCode、Mimocode、ZCode、Grok Build、OpenClaw、Pi、Kimi、Kilocode、Qoder、ChatGPT、WorkBuddy、Qwen、Continue、Cline、Goose、Zed、Crush、TeleAgent、Trae、Vibe、Reasonix、Copilot,以及 DSH 自己(从另一个 DSH home 导入,其 v3 / v4 日志算两个 id)。本机**没装**的来源照样列出来(置灰不可选)——「这台机器没有这个工具」不会被误解成「功能没做」。
319
+ - **是翻译,不是第二套导入通道** —— 来源被转成标准 bundle v1 ZIP,交给**既有**导入向导:同一份预览、同一套逐项冲突决策、同一次导入前快照与回滚、同样的 dry-run。
320
+ - **会带走什么** —— MCP Servers、技能、全局指令(落成 `AGENTS.md`),以及**历史会话连同这些对话所属的工作区**(29 个来源;会话会重新编码成 DSH 的会话日志格式,并按它记录的 `cwd` 归位)。
321
+ - **不会带走什么** —— 密钥**值**一律不读:只记录键名,由导入计划提示补录。DSH 没有对等结构的东西(Claude Code 的 hooks、斜杠命令等)会**报出明确的码**,不会静默丢掉。
322
+ - **如实跳过** —— 没记录 `cwd` 的对话无处归位,会列为跳过(绝不猜进别的项目);已知的有损项在导入**之前**就摆明。
323
+ - **入口** —— 设置 →「备份与迁移」→ 导入 → **「从其它 agent 导入」**、⌘K 命令,或 CLI:`dcm import --from <来源> [--dry-run] [--out <路径>]`。
324
+
308
325
  ### 🔒 密钥处理
309
326
 
310
327
  | 场景 | 行为 |
@@ -323,7 +340,7 @@ allowBuilds:
323
340
  | **端点** | `repoUrl` | `webdav.url` |
324
341
  | **凭据** | 认证 token 存入 DSH credentials(`DSH_CONFIG_MANAGER_SYNC_TOKEN`) | `username` 存配置、可在界面回显;**密码永不同步、永不记日志**——存入 DSH credentials `DSH_CONFIG_MANAGER_SYNC_WEBDAV_PASSWORD` |
325
342
 
326
- - **两通道保留策略一致**:远端只保留最新 **10** 个快照(`MAX_REMOTE_SNAPSHOTS=10`),更旧的自动删除。
343
+ - **远端保留跟本地同一套策略**:两通道都按你的备份计划保留——默认最新 **10** 个快照,可另设「每月 1 份 / 每年 1 份」分层;刚推上去的那份恒保留,更旧的自动删除。
327
344
  - **切换通道重新开始**:Git 与 WebDAV 的快照 / 共同祖先**互不共享**。切换通道后,同步从新远端的空基线重新开始——请先推送一个新快照。
328
345
  - **WebDAV 认证**采用 HTTP Basic:`username` 存配置、可在界面回显;`password` 则实时从 DSH credentials 槽位 `DSH_CONFIG_MANAGER_SYNC_WEBDAV_PASSWORD` 读取——绝不出现于任何同步文件或日志。
329
346
  - **插件自动安装**:拉取差异时,备份里新增的插件会在确认导入时**自动安装**,无需在差异列表里逐项手动勾选;只有**版本冲突**的插件仍需要你决定「保留当前 / 采用备份」。
@@ -465,6 +482,8 @@ dsh-config-manager verify C:/backups/dsh-config.zip # 或按路径
465
482
 
466
483
  **凭据文件永不进入备份。** `.credentials.*`、`.env`、`*.pem` 等由显式黑名单排除;只遍历白名单目录(绝不对整个主目录递归);symlink 一律跳过而非跟随。
467
484
 
485
+ **离线备份只覆盖磁盘上的技能。** 插件注册表里的技能(外壳从 profile 的插件包里加载的那些,见 issue #71)需要 DSH 运行时才能列举,因此**只有 GUI 导出(或 DSH 健康时的定时备份)能带上它们**;CLI `backup` 只收 `$DSH_HOME/skills` 目录下的文件——想要完整的技能备份,请在 DSH 健康时用 GUI 导出。
486
+
468
487
  结构化分区(设置 / UI / providers / 插件 / MCP / prompts / workspaces)**不会**被偷偷伪造:它们需要 DSH 服务层读值并脱敏,因此一律不写入、在 manifest 里标记为 `false`——计划输出会把它们列在「离线不可收集」下,让你清楚这份备份到底有什么、没有什么。DSH 健康时想要完整备份,请用 GUI 导出。
469
488
 
470
489
  ```bash
@@ -477,7 +496,7 @@ dsh-config-manager backup --sections skills,self # 收窄范围
477
496
  `--sections` 可选值为 `skills,agentPresets,agentInstructions,self,pluginFiles`。其中 `pluginFiles` 与 GUI 侧一致属**默认关闭**:它原样复制第三方插件自有文件,而 `dsh-ssh.json` 里是明文主机密码——确认过内容之后再选它。
478
497
 
479
498
 
480
- **典型救急流程**(DSH 起不来时):① `dsh-config-manager reinstall` 先把启动器重装回来(必要时顺带清理),② `dsh web` 重新启动 DSH,③ 从仓库装回插件,④ 从远程仓库拉取快照(或执行 `dsh-config-manager restore`)把配置恢复回来。整个流程中 CLI 全程可用,与 DSH 是否健康无关。
499
+ **典型救急流程**(DSH 起不来时):① `dsh-config-manager reinstall` 先把启动器重装回来(必要时顺带清理),② 若 DSH 报会话日志错误(`corrupt session log` / `duplicate JSONL session id`),先跑 `dsh-config-manager sessions repair --fix` 把日志目录离线归位,③ `dsh web` 重新启动 DSH,④ 从仓库装回插件,⑤ 从远程仓库拉取快照(或执行 `dsh-config-manager restore`)把配置恢复回来。整个流程中 CLI 全程可用,与 DSH 是否健康无关。
481
500
 
482
501
  **`web` —— 离线救急台(不想在终端里敲命令时用这个)。** 它在本机起一个**只绑 127.0.0.1** 的小网页,
483
502
  把上面的诊断信息与救急动作都摊在浏览器里:
@@ -546,7 +565,7 @@ dsh-config-manager web # 启动并自动打开浏览器(终端里
546
565
  不能。校验和检查不通过会直接拒绝导入(防止损坏或篡改)。
547
566
 
548
567
  **Q:重复导入会重复吗?**
549
- 不会。按插件 ID / MCP 名称 / 技能名等稳定标识去重,重复导入自动跳过已有项。
568
+ 不会。按插件 ID / MCP 名称 / 技能名等稳定标识配对:完全一致的跳过,与目标机有差异的会列为**冲突由你决定**(保留本机 / 用导入的),不会静默覆盖。
550
569
 
551
570
  **Q:加密备份导入时需要密码吗?**
552
571
  需要。导入向导会要求输入导出时设置的加密密码并验证通过后才能继续执行;密码仅存于内存、绝不保存。密码错误或缺失都会阻止导入(密码正确时凭据直接从备份解密恢复,无需重新填写)。
@@ -561,10 +580,11 @@ dsh-config-manager web # 启动并自动打开浏览器(终端里
561
580
  1. **安装/更新插件或 MCP 后需重启 DSH 才生效**
562
581
  2. **部分界面状态不迁移**(如任务看板数据、面板宽度——它们存在浏览器里,不在 DSH 配置文件内)
563
582
  3. **keybindings / workflows 配置 / commands**:DSH 当前没有这些概念,因此不会导出相关内容。全局 agent 规则已由**全局指令(Agent Instructions)**承接(`~/.dsh/AGENTS.md`,注入每个会话);项目级 `AGENTS.md` / `CLAUDE.md` 属于各项目仓库本身,不在个人配置迁移范围内
564
- 4. **历史会话默认不迁移**(v1 仅支持文件级复制)
583
+ 4. **历史会话是显式可选项** —— DSH 自己的会话只有在导出 / 同步时**显式勾选 `sessions` 分区**才会带走(同步通道还要求两侧都放行);**其它 Agent 的历史会话在你选中那个来源时迁移**。DSH 会话格式里没有位置的对话状态(模型思考过程、图片、压缩检查点)一律**如实计数并报告**,绝不伪造
565
584
  5. **加密备份**:密码丢失则无法解密(设计使然——请牢记密码)
566
585
  6. **快照恢复是离线的、诚实的**:离线引擎无法恢复的条目(快照无整文件备份时的 settings namespace / patch 行、存在 DSH storages 里的 workspace 记录)会如实列为跳过并指向在线回滚;凭据**值**绝不自动改写(只提示人工补录);无插件基线的旧快照只提示人工核对新增插件
567
- 7. **本地源插件(`link:` / `file:`)随备份打包**:导出时执行 `npm pack` 把本地开发中的插件打成 tarball 一并备份,导入时解包到 `$DSH_HOME/dsh-config-manager/local-plugins/` 后按 `file:` 安装。因此:① 备份体积会随本地插件的体积增大(单插件超过 100 MB 会被跳过并告警,建议先发布到 registry / git 再备份);② 插件**源码**会进入备份(与「密钥永不进备份」不冲突——密钥仍被排除,这里进的是代码);③ 打包需要本机有可用的 `npm`,无 npm 时该插件退化为原行为(保留原 spec,换机后仍需手工安装)
586
+ 7. **从其它 Agent 导入有两处已知空白**:**Copilot 不支持导入历史会话**(其会话格式在本机无法取证,只导入它的配置);Cursor 的项目历史要靠它自己的目录 slug 反解,个别项目可能对不上。用**旧版本**导入过的会话请先删掉再重导(同一 id 二次导入会如实报**跳过** `session-id-conflict`,不会合并)
587
+ 8. **本地源插件(`link:` / `file:`)随备份打包**:导出时执行 `npm pack` 把本地开发中的插件打成 tarball 一并备份,导入时解包到 `$DSH_HOME/dsh-config-manager/local-plugins/` 后按 `file:` 安装。因此:① 备份体积会随本地插件的体积增大(单插件超过 100 MB 会被跳过并告警,建议先发布到 registry / git 再备份);② 插件**源码**会进入备份(与「密钥永不进备份」不冲突——密钥仍被排除,这里进的是代码);③ 打包需要本机有可用的 `npm`,无 npm 时该插件退化为原行为(保留原 spec,换机后仍需手工安装)
568
588
 
569
589
  ## 💬 反馈与建议
570
590
 
@@ -1,5 +1,5 @@
1
1
  import { msgOf, zhMsg } from '../core/messages.js';
2
- import { resolveNamespaces } from './settings.js';
2
+ import { namespaceFromMap, readNamespaceMap, resolveNamespaces } from './settings.js';
3
3
  import { sectionMeta } from '../schema/section-registry.js';
4
4
  import { validateJsonSection } from './json-section.js';
5
5
  /** 缺省 ref 收集:遍历 settings namespace,收集 llm apiKeyEnv / providers[].apiKeyEnv,
@@ -9,9 +9,12 @@ import { validateJsonSection } from './json-section.js';
9
9
  export function defaultCredentialRefs(namespaces) {
10
10
  return async (ctx) => {
11
11
  const refs = new Set();
12
- for (const ns of await resolveNamespaces(namespaces, ctx)) {
12
+ const names = await resolveNamespaces(namespaces, ctx);
13
+ // 快路径:宿主能一次读回全部 namespace 时只读一趟(理由见 settings.ts readNamespaceMap)
14
+ const all = names.length > 0 ? await readNamespaceMap(ctx) : null;
15
+ for (const ns of names) {
13
16
  try {
14
- const info = await ctx.settings.describe(ns, { redactSecrets: true });
17
+ const info = all !== null ? namespaceFromMap(all, ns) : await ctx.settings.describe(ns, { redactSecrets: true });
15
18
  const value = (info.value ?? {});
16
19
  // 引用类字段名(值 = env/凭据引用名,非秘密本身)
17
20
  const REFERENCE_REF_FIELDS = new Set([
@@ -103,10 +103,30 @@ export declare abstract class FileCollectionAdapter implements ConfigAdapter<Fil
103
103
  * - `size`:优先 `ctx.fs.statSize`(只 stat,不读内容);门面未实现或返回 null(文件消失 /
104
104
  * 读不到)时**退回 readFile** —— 与改造前把「读不到」暴露成同一个错误,绝不把未知体积
105
105
  * 当成 0(0 会让字节闸门少算,把本该剔除的单元放进预览,预览与导出就分叉了)。
106
+ *
107
+ * 受保护而非私有(issue #71):`SkillsAdapter` 的成员有一部分**不在磁盘上**
108
+ * (来自 `ctx.skills` 技能服务的技能被重建成 `<name>/SKILL.md` 虚拟路径),
109
+ * 需要在这里按路径分流 —— 覆写后基类的单元分组 / 字节闸门 / 告警 / 预览-导出一致性全部照旧。
106
110
  */
107
- private readMember;
111
+ protected readMember(ctx: HostContext, rel: string, mode: 'content' | 'size', statTimes?: Map<string, number>): Promise<{
112
+ data: Uint8Array;
113
+ sizeBytes: number;
114
+ contentHash: string;
115
+ }>;
108
116
  /** 单元清单(零 I/O:直接由 export 产物归并)。 */
109
117
  listUnits(section: ExportSection<FilesSection>): ExportUnit[];
118
+ /**
119
+ * 幂等比对用的内容(相对 baseDir 路径 + 盘上/包内字节 → 参与哈希比对的字节)。
120
+ *
121
+ * 缺省 = **原字节**:对文件类分区来说「相对路径 + 内容哈希」就是身份语义,不得改动。
122
+ * 覆写者只应用于剔除**不属于「配置身份」的易变字段**(见 SelfAdapter 的两个调度配置:
123
+ * 运行态每轮都被改写,整文件哈希会恒判 Conflict —— issue #73)。两条契约:
124
+ * 1. **必须收敛**:applyItem 写盘后再分析一次必须判 Skip,否则同步永不收敛;
125
+ * 2. **未改写时返回同一引用** —— 调用方据此复用导入侧已算好的 contentHash,
126
+ * 不给 sessions 这类大分区白算一遍哈希。
127
+ * 判不出可比形态(解析失败等)时必须**原样返回**:绝不把「判不出来」当成「相同」或「不同」。
128
+ */
129
+ protected comparableContent(_relativePath: string, data: Uint8Array): Uint8Array;
110
130
  analyzeImport(data: FilesSection, ctx: ImportContext): Promise<PlanItem[]>;
111
131
  applyItem(item: PlanItem, ctx: ImportContext): Promise<ApplyResult>;
112
132
  /**
@@ -262,6 +262,10 @@ export class FileCollectionAdapter {
262
262
  * - `size`:优先 `ctx.fs.statSize`(只 stat,不读内容);门面未实现或返回 null(文件消失 /
263
263
  * 读不到)时**退回 readFile** —— 与改造前把「读不到」暴露成同一个错误,绝不把未知体积
264
264
  * 当成 0(0 会让字节闸门少算,把本该剔除的单元放进预览,预览与导出就分叉了)。
265
+ *
266
+ * 受保护而非私有(issue #71):`SkillsAdapter` 的成员有一部分**不在磁盘上**
267
+ * (来自 `ctx.skills` 技能服务的技能被重建成 `<name>/SKILL.md` 虚拟路径),
268
+ * 需要在这里按路径分流 —— 覆写后基类的单元分组 / 字节闸门 / 告警 / 预览-导出一致性全部照旧。
265
269
  */
266
270
  async readMember(ctx, rel, mode, statTimes) {
267
271
  if (mode === 'size') {
@@ -285,6 +289,20 @@ export class FileCollectionAdapter {
285
289
  listUnits(section) {
286
290
  return unitsFromFiles(this.id, section.data.files, (rel) => this.unitIdOf(rel));
287
291
  }
292
+ /**
293
+ * 幂等比对用的内容(相对 baseDir 路径 + 盘上/包内字节 → 参与哈希比对的字节)。
294
+ *
295
+ * 缺省 = **原字节**:对文件类分区来说「相对路径 + 内容哈希」就是身份语义,不得改动。
296
+ * 覆写者只应用于剔除**不属于「配置身份」的易变字段**(见 SelfAdapter 的两个调度配置:
297
+ * 运行态每轮都被改写,整文件哈希会恒判 Conflict —— issue #73)。两条契约:
298
+ * 1. **必须收敛**:applyItem 写盘后再分析一次必须判 Skip,否则同步永不收敛;
299
+ * 2. **未改写时返回同一引用** —— 调用方据此复用导入侧已算好的 contentHash,
300
+ * 不给 sessions 这类大分区白算一遍哈希。
301
+ * 判不出可比形态(解析失败等)时必须**原样返回**:绝不把「判不出来」当成「相同」或「不同」。
302
+ */
303
+ comparableContent(_relativePath, data) {
304
+ return data;
305
+ }
288
306
  async analyzeImport(data, ctx) {
289
307
  const msg = ctx.msg;
290
308
  const items = [];
@@ -317,15 +335,21 @@ export class FileCollectionAdapter {
317
335
  target: { adapter: this.id, ref: file.relativePath },
318
336
  });
319
337
  }
320
- else if (sha256Hex(current) === file.contentHash) {
321
- items.push({ id, unitId, kind: 'Skip', adapter: this.id, description: msg('adapter.fileSame', { path: file.relativePath }), severity: 'info' });
322
- }
323
338
  else {
324
- items.push({
325
- id, unitId, kind: 'Conflict', adapter: this.id,
326
- description: msg('adapter.fileDiff', { path: file.relativePath }), severity: 'warning',
327
- target: { adapter: this.id, ref: file.relativePath },
328
- });
339
+ // 幂等判定统一走 comparableContent(缺省 = 原字节);包内文件未被改写时复用已算好的
340
+ // contentHash,不给 sessions 这类大分区白算一遍哈希(盘上文件本来就要现算)。
341
+ const importedComparable = this.comparableContent(file.relativePath, file.data);
342
+ const importedHash = importedComparable === file.data ? file.contentHash : sha256Hex(importedComparable);
343
+ if (sha256Hex(this.comparableContent(file.relativePath, current)) === importedHash) {
344
+ items.push({ id, unitId, kind: 'Skip', adapter: this.id, description: msg('adapter.fileSame', { path: file.relativePath }), severity: 'info' });
345
+ }
346
+ else {
347
+ items.push({
348
+ id, unitId, kind: 'Conflict', adapter: this.id,
349
+ description: msg('adapter.fileDiff', { path: file.relativePath }), severity: 'warning',
350
+ target: { adapter: this.id, ref: file.relativePath },
351
+ });
352
+ }
329
353
  }
330
354
  }
331
355
  return items;
@@ -1,18 +1,28 @@
1
1
  import type { MsgFunc } from '../core/messages.ts';
2
2
  import type { McpServerEntry } from '../schema/types.ts';
3
3
  import type { ApplyResult, ConfigAdapter, ExportOptions, ExportSection, HostContext, ImportContext, PlanItem, ValidationResult } from '../core/types.ts';
4
- /** 导出记录:McpServerEntry 之外附加来源 patch 行 id(导入写回定位用) */
4
+ /** 导出记录:McpServerEntry 之外附加来源 patch 行 id 与所在层(导入写回定位用) */
5
5
  export interface McpExportEntry extends McpServerEntry {
6
6
  sourceLineId: string;
7
+ /**
8
+ * 来源 patch 层(相对 homeDir 的 POSIX 路径;见 core/patch-layers.ts)。
9
+ * 缺省 = 用户层(旧备份包没有该字段 → 按旧行为写回 home 层)。
10
+ */
11
+ sourceFile?: string;
7
12
  }
8
13
  export interface McpExportSection {
9
14
  version: 1;
10
15
  servers: McpExportEntry[];
11
16
  }
12
- /** 从 patch 行数组中提取 MCP server 条目(以 config.serverName 存在为判定) */
17
+ /**
18
+ * 从 patch 行数组中提取 MCP server 条目(以 config.serverName 存在为判定)。
19
+ *
20
+ * 行对象可带 `file`(层身份):带上时逐条目记录 `sourceFile`,导入写回落回原层。
21
+ */
13
22
  export declare function extractMcpServers(lines: {
14
23
  lineId: string;
15
24
  raw: unknown;
25
+ file?: string;
16
26
  }[]): McpExportEntry[];
17
27
  /** 由导出条目构造写回 patch 行 raw(单行形态,与 dsh-mcp-client 组合 config 语义一致) */
18
28
  export declare function buildMcpPatchLine(lineId: string, server: McpServerEntry): Record<string, unknown>;
@@ -5,13 +5,22 @@
5
5
  * dependencyChecker 执行(§15:缺失不阻塞,标记 Requires Attention)。
6
6
  *
7
7
  * patch 行 raw 支持两种形态:单行 { id, name, config } 与块 { insert: [{ id, name, config }] }。
8
+ *
9
+ * issue #71(真机报告「备份不到外壳 mcp」):数据源是**两层** patch —— home 层
10
+ * ($DSH_HOME/cordis.patch.yml)与 profile 层($DSH_HOME/profiles/<p>/cordis.patch.yml)。
11
+ * 此前只读 home 层,而 DSH 自己的 MCP 配置工具把 server 写进 profile 层,于是备份里
12
+ * servers 恒为空数组。现在两层都读,每行记住自己来自哪个文件,写回时落回原层。
8
13
  */
9
14
  import { isDeepStrictEqual } from 'node:util';
10
15
  import { msgOf, zhMsg } from '../core/messages.js';
11
16
  import { sectionMeta } from '../schema/section-registry.js';
12
17
  import { validateJsonSection } from './json-section.js';
13
- import { USER_PATCH_FILE } from './plugins.js';
14
- /** 从 patch 行数组中提取 MCP server 条目(以 config.serverName 存在为判定) */
18
+ import { locatePatchLineLayer, readEffectivePatchLines, resolveWriteLayer } from '../core/patch-layers.js';
19
+ /**
20
+ * 从 patch 行数组中提取 MCP server 条目(以 config.serverName 存在为判定)。
21
+ *
22
+ * 行对象可带 `file`(层身份):带上时逐条目记录 `sourceFile`,导入写回落回原层。
23
+ */
15
24
  export function extractMcpServers(lines) {
16
25
  const servers = [];
17
26
  for (const line of lines) {
@@ -34,6 +43,7 @@ export function extractMcpServers(lines) {
34
43
  url: typeof c['url'] === 'string' ? c['url'] : undefined,
35
44
  headers: c['headers'] !== null && typeof c['headers'] === 'object' ? c['headers'] : undefined,
36
45
  sourceLineId: line.lineId,
46
+ ...(line.file !== undefined ? { sourceFile: line.file } : {}),
37
47
  });
38
48
  }
39
49
  }
@@ -85,14 +95,12 @@ export class McpAdapter {
85
95
  portability = sectionMeta('mcp').portability;
86
96
  async export(ctx, _options) {
87
97
  const warnings = [];
88
- let lines = [];
89
- try {
90
- lines = await ctx.patchFile.readPatchLines(USER_PATCH_FILE);
91
- }
92
- catch (err) {
93
- warnings.push(msgOf(ctx)('adapter.patchReadFailedMCP', { reason: err instanceof Error ? err.message : String(err) }));
98
+ // issue #71:两层都读(home 层优先),否则 profile 层里的 server 全部丢失
99
+ const read = await readEffectivePatchLines(ctx.patchFile, ctx.profile);
100
+ for (const f of read.failures) {
101
+ warnings.push(msgOf(ctx)('adapter.patchReadFailedMCP', { reason: `${f.file}: ${f.reason}` }));
94
102
  }
95
- const servers = extractMcpServers(lines);
103
+ const servers = extractMcpServers(read.lines);
96
104
  return {
97
105
  sectionId: 'mcp',
98
106
  data: { version: 1, servers },
@@ -103,13 +111,15 @@ export class McpAdapter {
103
111
  async analyzeImport(data, ctx) {
104
112
  const msg = ctx.msg;
105
113
  const items = [];
106
- const targetLines = await ctx.target.patchFile.readPatchLines(USER_PATCH_FILE);
114
+ // issue #71:目标行跨两层;同一 lineId 只保留优先级最高的一层(与 DSH 合并序一致)
115
+ const targetLines = (await readEffectivePatchLines(ctx.target.patchFile, ctx.target.profile)).lines;
107
116
  const targetServers = extractMcpServers(targetLines);
108
117
  for (const server of data.servers) {
109
118
  const id = `mcp:${server.serverName}`;
110
119
  const existing = targetServers.find((s) => s.serverName === server.serverName);
111
120
  const comparable = { ...server };
112
121
  delete comparable.sourceLineId;
122
+ delete comparable.sourceFile;
113
123
  if (!existing) {
114
124
  const lineId = server.sourceLineId && targetLines.some((l) => l.lineId === server.sourceLineId)
115
125
  ? `${server.sourceLineId}-imported`
@@ -119,12 +129,14 @@ export class McpAdapter {
119
129
  description: msg('adapter.mcpCreate', { serverName: server.serverName }),
120
130
  detail: `${server.type === 'stdio' ? `${server.command} ${(server.args ?? []).join(' ')}` : server.url}`,
121
131
  severity: 'info',
122
- target: { adapter: 'mcp', ref: lineId },
132
+ // 目标层随计划项一起走:导入前快照要按**这一层**记原行,回滚才能写回原文件
133
+ target: { adapter: 'mcp', ref: lineId, file: resolveWriteLayer(server.sourceFile, ctx.target.profile) },
123
134
  });
124
135
  }
125
136
  else {
126
137
  const existingComparable = { ...existing };
127
138
  delete existingComparable.sourceLineId;
139
+ delete existingComparable.sourceFile;
128
140
  if (isDeepStrictEqual(existingComparable, comparable)) {
129
141
  items.push({ id, kind: 'Skip', adapter: 'mcp', description: msg('adapter.mcpSame', { serverName: server.serverName }), severity: 'info' });
130
142
  }
@@ -134,7 +146,7 @@ export class McpAdapter {
134
146
  description: msg('adapter.mcpDiff', { serverName: server.serverName }),
135
147
  detail: `current=${JSON.stringify(existingComparable)} imported=${JSON.stringify(comparable)}`.slice(0, 200),
136
148
  severity: 'warning',
137
- target: { adapter: 'mcp', ref: existing.sourceLineId },
149
+ target: { adapter: 'mcp', ref: existing.sourceLineId, file: existing.sourceFile },
138
150
  });
139
151
  }
140
152
  }
@@ -152,7 +164,12 @@ export class McpAdapter {
152
164
  if (!ref)
153
165
  return { ok: false, message: msg('adapter.missingTargetRef') };
154
166
  const raw = buildMcpPatchLine(ref, server);
155
- await ctx.target.patchFile.applyPatchChanges(USER_PATCH_FILE, [
167
+ // issue #71:Create 落「来源层的同语义层」(来源是 profile 层 → 目标机当前 profile 层),
168
+ // Update 落「该行当前所在层」—— 两种情况都不把行搬到另一层去(否则同一 lineId 两层各一份)
169
+ const file = item.kind === 'Create'
170
+ ? resolveWriteLayer(server.sourceFile, ctx.target.profile)
171
+ : await locatePatchLineLayer(ctx.target.patchFile, ctx.target.profile, ref);
172
+ await ctx.target.patchFile.applyPatchChanges(file, [
156
173
  { lineId: ref, raw, action: item.kind === 'Create' ? 'insert' : 'update' },
157
174
  ]);
158
175
  return { ok: true, needsRestart: true, message: msg('adapter.mcpWritten', { serverName }) };
@@ -12,6 +12,14 @@ export declare class PluginFilesAdapter implements ConfigAdapter<FilesSection> {
12
12
  private readonly collectDir?;
13
13
  constructor(whitelist?: string[], collectDir?: string);
14
14
  export(ctx: HostContext, options: ExportOptions): Promise<ExportSection<FilesSection>>;
15
+ /**
16
+ * 读失败之后判定「这个路径本来就不存在」还是「存在但读不到」(ui-F2)。
17
+ *
18
+ * 为什么不用错误码:门面错误的形状并不统一(内存 mock 与各平台 EACCES 文案不同),
19
+ * 而 `exists()` 是 FileSystemFacade 契约里唯一稳定的存在性判据。`exists()` 自己也失败时
20
+ * 按「存在」处理(保守:宁可让这次导出显式失败,也不静默丢一个文件)。
21
+ */
22
+ private absentOnDisk;
15
23
  /** 单元清单(零 I/O):逐文件单元,id 与导入侧 `pluginFiles:<relPath>` 一致。 */
16
24
  listUnits(section: ExportSection<FilesSection>): ExportUnit[];
17
25
  analyzeImport(data: FilesSection, ctx: ImportContext): Promise<PlanItem[]>;