clearai-dsh 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  All notable changes to this project are recorded here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
4
4
 
5
+ ## [0.2.2] — 2026-09-18
6
+
7
+ **装的时候不再吓人。** 0.2.1 的 `npx clearai-dsh install` 会打出一串 peer 警告(react / graphology-types …),读起来像装坏了——而它们一个字都不影响运行。这一版把安装面收窄到运行时真正需要的那一个依赖,并让安装侧 CLI 按系统语言出话。
8
+
9
+ ### Changed
10
+
11
+ - **安装面只剩一个运行时依赖。** `@xyflow/react` / `graphology` / `graphology-layout-forceatlas2` / `docx` 挪进 `devDependencies`:前三个只在构建期打 vendor(`lib/client.js` 里是**内联**的,装机后不解析 npm),`docx` 只给营销 docx 脚本用。于是 profile 里不再多装一批包,也不会再有那些注定填不上的 peer 警告——React 由 **DSH 宿主**提供(客户端半 `require('react')` 是问宿主拿的),`graphology-types` 只是类型包。运行时唯一保留的是 `zod`(宿主半 `lib/host.js` 真的 `from 'zod'`)。
12
+ - **安装侧 CLI 跟系统语言走。** `doctor` / `install` / `root-yaml` / `seed` / `unseed` 的每一句都在中英两份文案表里(并排放在一处,改的时候不会只改一边);判据是 `--lang zh|en` > `CLEARAI_LANG` > `LC_ALL` / `LC_MESSAGES` / `LANG` > ICU 的默认 locale,`C` / `POSIX` 当「没有语言信息」按英文处理。0.2.1 之前是无论系统是什么都说中文。
13
+
5
14
  ## [0.2.1] — 2026-09-18
6
15
 
7
16
  **知识任务是循环的原生行为,不是另一个模式。** 本体、实体、认识论早就在,但普通研究的最短路径仍然是「检索 → 总结 → 写报告」——要建本体得用户先想起来说一句。这一版修的是**接线缺口**:把知识任务的判据做成结构的(目标还开着 + 带着登记过的假设),系统自己进知识模式;并把图从手写 SVG 换成 React Flow,给了它一个真正的全屏工作区。
package/README.md CHANGED
@@ -65,6 +65,8 @@ ClearAI does **not** claim recursive self-improvement. It provides the epistemic
65
65
  npx clearai-dsh install
66
66
  ```
67
67
 
68
+ The installer's output follows your system language (`--lang zh|en` overrides it, `doctor` / `seed` / `unseed` take the same flag). Its only runtime dependency is `zod`; the graph stack is bundled into the client half at build time.
69
+
68
70
  Restart `dsh web` afterwards (`npx @deepseek-ai/dsh web`), then **create a session and switch to the `ClearAI` mode in the picker at the top**:
69
71
 
70
72
  1. Open `dsh web` and click "New session";
package/README.zh-CN.md CHANGED
@@ -65,6 +65,8 @@ ClearAI **不**声称递归自我改进。它提供的是自我改进系统所
65
65
  npx clearai-dsh install
66
66
  ```
67
67
 
68
+ 安装侧的输出**跟系统语言走**(`--lang zh|en` 可覆盖;`doctor` / `seed` / `unseed` 同样认这个开关)。运行时依赖只有 `zod`——图谱那套栈在构建期就打进客户端半了。
69
+
68
70
  装完重启 `dsh web`(`npx @deepseek-ai/dsh web`),然后**新建会话,在顶部的模式选择器里切换到 `ClearAI`**:
69
71
 
70
72
  1. 打开 `dsh web`,点「新建会话」;
package/bin/clearai.mjs CHANGED
@@ -44,6 +44,143 @@ const value = (name, fallback) => {
44
44
  }
45
45
  const profile = value('profile', 'web')
46
46
 
47
+ /**
48
+ * 语言:**跟系统走,不猜**。
49
+ *
50
+ * 判据顺序:`--lang zh|en` > `CLEARAI_LANG` > `LC_ALL` / `LC_MESSAGES` / `LANG` > ICU 的默认 locale。
51
+ * `C` / `POSIX` 是「没有语言信息」,按英文处理(它们是 CI 与最小容器的默认值,不代表中文)。
52
+ * 为什么认不出来时说英文:这是发给陌生人的第一条命令输出,不该假设对方读中文。
53
+ * 两边的文案**并排放在一张表里**——同一句话的两个版本挨着,改的时候不会只改一边。
54
+ */
55
+ const lang = (() => {
56
+ const candidates = [value('lang', null), process.env.CLEARAI_LANG, process.env.LC_ALL, process.env.LC_MESSAGES, process.env.LANG, Intl.DateTimeFormat().resolvedOptions().locale]
57
+ for (const candidate of candidates) {
58
+ if (typeof candidate !== 'string' || candidate === '') continue
59
+ const tag = candidate.toLowerCase()
60
+ if (tag.startsWith('zh')) return 'zh'
61
+ if (tag === 'c' || tag === 'posix' || tag.startsWith('en')) return 'en'
62
+ }
63
+ return 'en'
64
+ })()
65
+
66
+ const TEXT = {
67
+ zh: {
68
+ pkgDir: '包目录 ',
69
+ presetSrc: '预设源 ',
70
+ presetOk: '(agent.cordis.yml ✓)',
71
+ presetMissing: '(缺 agent.cordis.yml ✗)',
72
+ dshHome: 'DSH_HOME ',
73
+ userRoot: '用户根 ',
74
+ rosterVisible: '✓ 名册看得见',
75
+ rosterAbsent: '✗ 还没有(用 seed 播种,或把 root-yaml 那一行粘进 profile)',
76
+ shadowCopy: ' ⚠️ 影子副本 {path} 与包里的那份**不一致**,而它被包的 root 遮住、永远不会被读到(自检却会优先读它)。删掉它,或用它来承载你自己的改动并换一个 id。',
77
+ profileRow: 'profile ',
78
+ profileAbsent: '(不存在:先跑一次 dsh --profile {profile})',
79
+ composeRow: '组合 ',
80
+ composeUnreachable: '(问不到:dsh 不可用或超时 —— 下面两条无法判定)',
81
+ hostRow: '宿主行 ',
82
+ hostIn: '在组合里 ✓',
83
+ hostOut: '不在组合里(先 dsh plugin --profile {profile} add <本包>)',
84
+ rosterRootRow: '名册 root ',
85
+ rosterNoRoots: '组合里没看到 roots(这个部署可能不挂名册)',
86
+ rosterExpr: '{expr}(dump 打的是表达式原文,求值在装载时)',
87
+ seedLedgerRow: '播种记账 ',
88
+ ledgerEmpty: '(空)',
89
+ ledgerCount: '{count} 个条目 · {path}',
90
+ doctorHint: '\n提示:doctor 只读;它不会替你改 profile,也不会替你播种。',
91
+ installHeader: '【安装】{name}@{version}',
92
+ installWhat: '装什么 ',
93
+ installWhere: '装到哪 ',
94
+ installWho: '谁来跑 ',
95
+ hostRowShort: '宿主行 ',
96
+ sourceSpec: '你给的 spec',
97
+ sourceTarball: '本地 tarball',
98
+ sourceDir: '本地目录',
99
+ sourceRegistry: 'registry',
100
+ routeNpx: 'npx --yes @deepseek-ai/dsh(PATH 上没有 dsh)',
101
+ pnpmMissing: '\n✗ PATH 上没有 pnpm,而 DSH 管理一个 profile 就是靠它:`dsh plugin …` 把参数转发给 pnpm。\n 装一个再来:npm install -g pnpm(或用系统包管理器,如 brew install pnpm)。\n 别用 corepack enable 抄近路 —— 它装的是版本**转发器**而不是 pnpm,而当前 Node 自带的\n 那份 corepack 可能下载一个它自己启动不了的 pnpm。\n 这里刻意不手工改 profile:那等于把宿主的 reconcile 抄成第二份实现,与宿主漂移时坏的是你的部署。',
102
+ installFailed: '\n✗ 安装失败:见上面的输出。',
103
+ hostUnknown: '(问不到组合:CLI 不可用或超时 —— 装没装进去,从这里确认不了;用 doctor 再看)',
104
+ hostAbsent: '**不在组合里** —— 装是装上了,但组合里没看到它(用 doctor 查)',
105
+ nextStep: '\n 下一步 重启 dsh web(两半都在进程里按模块 URL 缓存,只刷新浏览器不够),然后在预设选择器里选 ClearAI。',
106
+ uninstall: ' 卸载 dsh plugin --profile {profile} remove {name}(同样可以冠 npx)',
107
+ rootYamlComment1: '# ClearAI 预设的 root(由 clearai-dsh 的 bin 打印,路径已算成绝对路径)',
108
+ rootYamlComment2: ' # ⚠️ 补丁层会**替换整份 config**:下面这些键必须与你部署里那份一致,否则会丢。',
109
+ rootYamlComment3: ' # 先 `dsh --profile <p> --dump-config | grep -A 20 agent-presets` 看一眼当前值再粘。',
110
+ noPresetInPackage: '✗ 包里没有预设:{path}',
111
+ seededTo: ' 播种到 {dest}',
112
+ seedCounts: ' 新增 {seeded} · 刷新 {refreshed}',
113
+ seedDrifted: ' · **你改过、没覆盖** {count}({list})',
114
+ seedNext: ' 下一步:重启 dsh(或刷新页面)后,预设选择器里应出现「ClearAI」。',
115
+ noSeedLedger: ' 没有播种记账:什么都不做(不知道哪些是我们播的,就不删)。',
116
+ unseedRemoved: ' 删掉 {count} 个「我们播的、没被改过」的文件',
117
+ unseedKept: ' **保留** {count} 个你改过的文件(它们现在是你的):{list}',
118
+ unknownCommand: 'unknown command: {command}',
119
+ usage: '用法:clearai-dsh [doctor|install|root-yaml|seed|unseed] [--profile web] [--home <dir>] [--lang zh|en]\n install 还可以:--dist <dir> | --tarball <tgz> | --spec <spec>',
120
+ },
121
+ en: {
122
+ pkgDir: 'package ',
123
+ presetSrc: 'preset src ',
124
+ presetOk: '(agent.cordis.yml ✓)',
125
+ presetMissing: '(no agent.cordis.yml ✗)',
126
+ dshHome: 'DSH_HOME ',
127
+ userRoot: 'user root ',
128
+ rosterVisible: '✓ visible to the roster',
129
+ rosterAbsent: '✗ not there yet (seed it, or paste the root-yaml line into the profile)',
130
+ shadowCopy: ' ⚠️ shadow copy {path} differs from the copy in the package, and the package root shadows it — it will never be read (while the self-check reads it first). Delete it, or keep your changes there under a different id.',
131
+ profileRow: 'profile ',
132
+ profileAbsent: '(does not exist yet: run dsh --profile {profile} once)',
133
+ composeRow: 'composition ',
134
+ composeUnreachable: '(could not ask: dsh unavailable or timed out — the next two rows cannot be decided)',
135
+ hostRow: 'host row ',
136
+ hostIn: 'is in the composition ✓',
137
+ hostOut: 'is NOT in the composition (run dsh plugin --profile {profile} add <this package>)',
138
+ rosterRootRow: 'roster root ',
139
+ rosterNoRoots: 'no roots in the composition (this deployment may not mount the roster)',
140
+ rosterExpr: '{expr}(the dump prints the expression verbatim; it is evaluated at load time)',
141
+ seedLedgerRow: 'seed ledger ',
142
+ ledgerEmpty: '(empty)',
143
+ ledgerCount: '{count} entries · {path}',
144
+ doctorHint: '\ndoctor only reads. It will not change your profile and will not seed anything for you.',
145
+ installHeader: '[install] {name}@{version}',
146
+ installWhat: 'what ',
147
+ installWhere: 'where ',
148
+ installWho: 'who runs it ',
149
+ hostRowShort: 'host row ',
150
+ sourceSpec: 'the spec you gave',
151
+ sourceTarball: 'local tarball',
152
+ sourceDir: 'local directory',
153
+ sourceRegistry: 'registry',
154
+ routeNpx: 'npx --yes @deepseek-ai/dsh (no dsh on PATH)',
155
+ pnpmMissing: '\n✗ pnpm is not on PATH, and it is how DSH manages a profile: `dsh plugin …` forwards to it.\n Install one and come back: npm install -g pnpm (or your package manager, e.g. brew install pnpm).\n Do not take the corepack enable shortcut — that installs a version *forwarder*, not pnpm, and the\n corepack bundled with current Node may fetch a pnpm it is unable to launch.\n This verb deliberately does not edit the profile by hand: that would be a second implementation of the host\'s reconcile, and yours is the deployment that breaks when it drifts.',
156
+ installFailed: '\n✗ install failed: see the output above.',
157
+ hostUnknown: '(could not ask the composition: CLI unavailable or timed out — whether it landed cannot be decided here; run doctor)',
158
+ hostAbsent: '**NOT in the composition** — it installed, but the composition does not show it (run doctor)',
159
+ nextStep: '\n next restart dsh web (both halves are cached in the running process by module URL, so a browser refresh is not enough), then pick ClearAI in the preset picker.',
160
+ uninstall: ' uninstall dsh plugin --profile {profile} remove {name}(npx works too)',
161
+ rootYamlComment1: '# ClearAI preset root (printed by clearai-dsh; the path is already absolute)',
162
+ rootYamlComment2: ' # ⚠️ a patch layer **replaces the whole config**: these keys must match the ones in your deployment, or they are lost.',
163
+ rootYamlComment3: ' # Run `dsh --profile <p> --dump-config | grep -A 20 agent-presets` first and paste over the current values.',
164
+ noPresetInPackage: '✗ no preset in the package: {path}',
165
+ seededTo: ' seeded to {dest}',
166
+ seedCounts: ' added {seeded} · refreshed {refreshed}',
167
+ seedDrifted: ' · **kept your edits** {count}({list})',
168
+ seedNext: ' next: restart dsh (or refresh the page) — ClearAI should now appear in the preset picker.',
169
+ noSeedLedger: ' nothing was seeded: doing nothing (without the ledger there is no way to tell which files are ours, so nothing is deleted).',
170
+ unseedRemoved: ' removed {count} file(s) we seeded and you did not edit',
171
+ unseedKept: ' **kept** {count} file(s) you edited (they are yours now): {list}',
172
+ unknownCommand: 'unknown command: {command}',
173
+ usage: 'usage: clearai-dsh [doctor|install|root-yaml|seed|unseed] [--profile web] [--home <dir>] [--lang zh|en]\n install also takes: --dist <dir> | --tarball <tgz> | --spec <spec>',
174
+ },
175
+ }
176
+
177
+ /** `{name}` 是占位符;需要拼计数与列表的句子写成函数(见 doctor / seed / unseed)。 */
178
+ function t(key, params) {
179
+ const text = TEXT[lang][key] ?? TEXT.zh[key]
180
+ if (text === undefined) throw new Error(`missing text: ${key}`)
181
+ return typeof text === 'function' ? text(params ?? {}) : text.replace(/\{(\w+)\}/g, (_, name) => String(params?.[name] ?? ''))
182
+ }
183
+
47
184
  /** 递归列出文件(相对路径 → sha256 前 16 位)。只为记账,不为校验完整性。 */
48
185
  function hashTree(root) {
49
186
  const out = {}
@@ -128,14 +265,14 @@ function composeQuery(profileName, env) {
128
265
 
129
266
  function doctor() {
130
267
  const rows = []
131
- rows.push(`包目录 ${PKG_DIR}`)
132
- rows.push(`预设源 ${PRESET_SRC}${existsSync(join(PRESET_SRC, 'agent.cordis.yml')) ? '(agent.cordis.yml ✓)' : '(缺 agent.cordis.yml ✗)'}`)
133
- rows.push(`DSH_HOME ${DSH_HOME}`)
268
+ rows.push(`${t('pkgDir')}${PKG_DIR}`)
269
+ rows.push(`${t('presetSrc')}${PRESET_SRC}${existsSync(join(PRESET_SRC, 'agent.cordis.yml')) ? t('presetOk') : t('presetMissing')}`)
270
+ rows.push(`${t('dshHome')}${DSH_HOME}`)
134
271
  const roots = rosterRoots()
135
272
  for (const root of roots) {
136
273
  const dir = join(root, PRESET_ID)
137
274
  const present = existsSync(join(dir, 'agent.cordis.yml'))
138
- rows.push(`用户根 ${dir} ${present ? '✓ 名册看得见' : '✗ 还没有(用 seed 播种,或把 root-yaml 那一行粘进 profile)'}`)
275
+ rows.push(`${t('userRoot')}${dir} ${present ? t('rosterVisible') : t('rosterAbsent')}`)
139
276
  /**
140
277
  * 用户根里那份**会不会被包的 root 遮住**。
141
278
  *
@@ -152,10 +289,10 @@ function doctor() {
152
289
  } catch {
153
290
  same = false
154
291
  }
155
- if (!same) rows.push(` ⚠️ 影子副本 ${theirs} 与包里的那份**不一致**,而它被包的 root 遮住、永远不会被读到(自检却会优先读它)。删掉它,或用它来承载你自己的改动并换一个 id。`)
292
+ if (!same) rows.push(t('shadowCopy', { path: theirs }))
156
293
  }
157
294
  const profileDir = join(DSH_HOME, 'profiles', profile)
158
- rows.push(`profile ${profileDir}${existsSync(profileDir) ? '' : '(不存在:先跑一次 dsh --profile ' + profile + ')'}`)
295
+ rows.push(`${t('profileRow')}${profileDir}${existsSync(profileDir) ? '' : t('profileAbsent', { profile })}`)
159
296
  /**
160
297
  * 宿主行与名册 root **问组合**,不问某个文件(2026-09-11 修:装出来的形态下这两行来自
161
298
  * **包的补丁层**,不在 profile 的 cordis.patch.yml 里 —— 读文件的写法会误报「还没挂」)。
@@ -163,18 +300,18 @@ function doctor() {
163
300
  */
164
301
  const composed = composeQuery(profile, { ...process.env, DSH_HOME })
165
302
  if (composed === null) {
166
- rows.push('组合 (问不到:dsh 不可用或超时 —— 下面两条无法判定)')
303
+ rows.push(`${t('composeRow')}${t('composeUnreachable')}`)
167
304
  } else {
168
305
  const rowIds = [...composed.matchAll(/^- id: (\S+)$/gm)].map((match) => match[1])
169
- rows.push(`宿主行 ${rowIds.includes('clearai-host') ? '在组合里 ✓' : '不在组合里(先 dsh plugin --profile ' + profile + ' add <本包>)'}`)
306
+ rows.push(`${t('hostRow')}${rowIds.includes('clearai-host') ? t('hostIn') : t('hostOut', { profile })}`)
170
307
  const roster = /- id: agent-presets[\s\S]{0,600}?\n\s*- path:[^\n]*/.exec(composed)
171
308
  const expr = roster === null ? null : roster[0].split('\n').pop().trim()
172
- rows.push(`名册 root ${expr === null ? '组合里没看到 roots(这个部署可能不挂名册)' : `${expr}(dump 打的是表达式原文,求值在装载时)`}`)
309
+ rows.push(`${t('rosterRootRow')}${expr === null ? t('rosterNoRoots') : t('rosterExpr', { expr })}`)
173
310
  }
174
311
  const ledger = readLedger()
175
- rows.push(`播种记账 ${Object.keys(ledger).length === 0 ? '(空)' : `${Object.keys(ledger).length} 个条目 · ${LEDGER}`}`)
312
+ rows.push(`${t('seedLedgerRow')}${Object.keys(ledger).length === 0 ? t('ledgerEmpty') : t('ledgerCount', { count: Object.keys(ledger).length, path: LEDGER })}`)
176
313
  for (const row of rows) console.log(` ${row}`)
177
- console.log('\n提示:doctor 只读;它不会替你改 profile,也不会替你播种。')
314
+ console.log(t('doctorHint'))
178
315
  }
179
316
 
180
317
  /**
@@ -204,28 +341,24 @@ function install() {
204
341
  * npx 缓存还在。`--dist` / `--tarball` / `--spec` 是给开发与 E2E 用的另一条入口。
205
342
  */
206
343
  const spec = explicit ?? (tarball !== null ? resolve(tarball) : dist !== null ? `file:${resolve(dist)}` : `${manifest.name}@${manifest.version}`)
207
- const source = explicit !== null ? '你给的 spec' : tarball !== null ? '本地 tarball' : dist !== null ? '本地目录' : 'registry'
344
+ const source = explicit !== null ? t('sourceSpec') : tarball !== null ? t('sourceTarball') : dist !== null ? t('sourceDir') : t('sourceRegistry')
208
345
  /** PATH 上有 `dsh` 就用它;没有就用 npx 取官方 CLI(`--yes`:一键安装不该卡在一个确认提示上)。 */
209
346
  const dshPath = findOnPath('dsh')
210
- const route = dshPath === null ? 'npx --yes @deepseek-ai/dsh(PATH 上没有 dsh)' : dshPath
347
+ const route = dshPath === null ? t('routeNpx') : dshPath
211
348
  /**
212
349
  * 子进程**继承 stdio**:安装进度、以及 CLI 那句 `initialized profile …` 都如实流到用户眼前;
213
350
  * 失败时他看到的也是真实报错,而不是我截出来的尾巴。
214
351
  */
215
352
  const dsh = (args) => (dshPath === null ? spawnSync('npx', ['--yes', '@deepseek-ai/dsh', ...args], { env, stdio: 'inherit', timeout: 900000 }) : spawnSync(dshPath, args, { env, stdio: 'inherit', timeout: 900000 }))
216
353
 
217
- console.log(`【安装】${manifest.name}@${manifest.version}`)
218
- console.log(` 装什么 ${spec}(${source})`)
219
- console.log(` 装到哪 ${profileDir}`)
220
- console.log(` 谁来跑 ${route}`)
354
+ console.log(t('installHeader', { name: manifest.name, version: manifest.version }))
355
+ console.log(` ${t('installWhat')}${spec}(${source})`)
356
+ console.log(` ${t('installWhere')}${profileDir}`)
357
+ console.log(` ${t('installWho')}${route}`)
221
358
 
222
359
  const pnpmPath = findOnPath('pnpm')
223
360
  if (pnpmPath === null) {
224
- console.error('\n✗ PATH 上没有 pnpm,而 DSH 管理一个 profile 就是靠它:`dsh plugin …` 把参数转发给 pnpm。')
225
- console.error(' 装一个再来:npm install -g pnpm(或用系统包管理器,如 brew install pnpm)。')
226
- console.error(' 别用 corepack enable 抄近路 —— 它装的是版本**转发器**而不是 pnpm,而当前 Node 自带的')
227
- console.error(' 那份 corepack 可能下载一个它自己启动不了的 pnpm。')
228
- console.error(' 这里刻意不手工改 profile:那等于把宿主的 reconcile 抄成第二份实现,与宿主漂移时坏的是你的部署。')
361
+ console.error(t('pnpmMissing'))
229
362
  process.exit(1)
230
363
  }
231
364
 
@@ -238,28 +371,28 @@ function install() {
238
371
  */
239
372
  const added = dsh(['plugin', '--profile', profile, 'add', spec])
240
373
  if (added.status !== 0) {
241
- console.error('\n✗ 安装失败:见上面的输出。')
374
+ console.error(t('installFailed'))
242
375
  process.exit(1)
243
376
  }
244
377
 
245
378
  const composed = composeQuery(profile, env)
246
379
  if (composed === null) {
247
- console.log(' 宿主行 (问不到组合:CLI 不可用或超时 —— 装没装进去,从这里确认不了;用 doctor 再看)')
380
+ console.log(` ${t('hostRowShort')}${t('hostUnknown')}`)
248
381
  } else {
249
382
  const inCompose = /^- id: clearai-host$/m.test(composed)
250
- console.log(` 宿主行 ${inCompose ? '在组合里 ✓' : '**不在组合里** —— 装是装上了,但组合里没看到它(用 doctor 查)'}`)
383
+ console.log(` ${t('hostRowShort')}${inCompose ? t('hostIn') : t('hostAbsent')}`)
251
384
  }
252
- console.log('\n 下一步 重启 dsh web(两半都在进程里按模块 URL 缓存,只刷新浏览器不够),然后在预设选择器里选 ClearAI。')
253
- console.log(` 卸载 dsh plugin --profile ${profile} remove ${manifest.name}(同样可以冠 npx)`)
385
+ console.log(t('nextStep'))
386
+ console.log(t('uninstall', { profile, name: manifest.name }))
254
387
  }
255
388
 
256
389
  function rootYaml() {
257
390
  const line = [
258
- '# ClearAI 预设的 root(由 clearai-dsh 的 bin 打印,路径已算成绝对路径)',
391
+ t('rootYamlComment1'),
259
392
  '- id: \'@deepseek-ai/dsh-agent-presets\'',
260
393
  ' config:',
261
- ' # ⚠️ 补丁层会**替换整份 config**:下面这些键必须与你部署里那份一致,否则会丢。',
262
- ' # 先 `dsh --profile <p> --dump-config | grep -A 20 agent-presets` 看一眼当前值再粘。',
394
+ t('rootYamlComment2'),
395
+ t('rootYamlComment3'),
263
396
  ' default: standard',
264
397
  ' includeShippedRoot: true',
265
398
  ' includeUserRoot: true',
@@ -272,7 +405,7 @@ function rootYaml() {
272
405
 
273
406
  function seed() {
274
407
  if (!existsSync(join(PRESET_SRC, 'agent.cordis.yml'))) {
275
- console.error(`✗ 包里没有预设:${PRESET_SRC}`)
408
+ console.error(t('noPresetInPackage', { path: PRESET_SRC }))
276
409
  process.exit(1)
277
410
  }
278
411
  const root = value('root', rosterRoots()[0])
@@ -303,16 +436,16 @@ function seed() {
303
436
  }
304
437
  before[PRESET_ID] = { files: next, at: new Date().toISOString(), dest }
305
438
  writeLedger(before)
306
- console.log(` 播种到 ${dest}`)
307
- console.log(` 新增 ${seeded.length} · 刷新 ${refreshed.length}${drifted.length === 0 ? '' : ` · **你改过、没覆盖** ${drifted.length}(${drifted.slice(0, 5).join('、')})`}`)
308
- console.log(' 下一步:重启 dsh(或刷新页面)后,预设选择器里应出现「ClearAI」。')
439
+ console.log(t('seededTo', { dest }))
440
+ console.log(`${t('seedCounts', { seeded: seeded.length, refreshed: refreshed.length })}${drifted.length === 0 ? '' : t('seedDrifted', { count: drifted.length, list: drifted.slice(0, 5).join(lang === 'zh' ? '、' : ', ') })}`)
441
+ console.log(t('seedNext'))
309
442
  }
310
443
 
311
444
  function unseed() {
312
445
  const ledger = readLedger()
313
446
  const entry = ledger[PRESET_ID]
314
447
  if (entry === undefined) {
315
- console.log(' 没有播种记账:什么都不做(不知道哪些是我们播的,就不删)。')
448
+ console.log(t('noSeedLedger'))
316
449
  return
317
450
  }
318
451
  const dest = entry.dest ?? join(rosterRoots()[0], PRESET_ID)
@@ -331,8 +464,8 @@ function unseed() {
331
464
  }
332
465
  delete ledger[PRESET_ID]
333
466
  writeLedger(ledger)
334
- console.log(` 删掉 ${removed.length} 个「我们播的、没被改过」的文件`)
335
- if (kept.length > 0) console.log(` **保留** ${kept.length} 个你改过的文件(它们现在是你的):${kept.slice(0, 5).join('、')}`)
467
+ console.log(t('unseedRemoved', { count: removed.length }))
468
+ if (kept.length > 0) console.log(t('unseedKept', { count: kept.length, list: kept.slice(0, 5).join(lang === 'zh' ? '、' : ', ') }))
336
469
  // 空目录收掉(只收我们知道的那些;非空绝不动)
337
470
  try {
338
471
  if (existsSync(dest) && readdirSync(dest).length === 0) rmSync(dest, { recursive: true })
@@ -348,10 +481,6 @@ else if (command === 'seed') seed()
348
481
  else if (command === 'unseed') unseed()
349
482
  else if (command === 'version' || flag('version')) console.log(JSON.parse(readFileSync(join(PKG_DIR, 'package.json'), 'utf8')).version)
350
483
  else {
351
- console.error(
352
- `unknown command: ${command}\n` +
353
- '用法:clearai-dsh [doctor|install|root-yaml|seed|unseed] [--profile web] [--home <dir>]\n' +
354
- ' install 还可以:--dist <dir> | --tarball <tgz> | --spec <spec>',
355
- )
484
+ console.error(`${t('unknownCommand', { command })}\n${t('usage')}`)
356
485
  process.exit(2)
357
486
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clearai-dsh",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "ClearAI: The Epistemic Loop, native to DSH.",
5
5
  "type": "module",
6
6
  "private": false,
@@ -35,10 +35,6 @@
35
35
  "node": ">=22"
36
36
  },
37
37
  "dependencies": {
38
- "@xyflow/react": "^12.11.6",
39
- "docx": "^9.7.1",
40
- "graphology": "^0.26.0",
41
- "graphology-layout-forceatlas2": "^0.10.1",
42
38
  "zod": "^4.6.1"
43
39
  },
44
40
  "dsh": {
@@ -77,7 +73,11 @@
77
73
  "shots": "node tools/graph-shots.mjs"
78
74
  },
79
75
  "devDependencies": {
76
+ "@xyflow/react": "^12.11.6",
77
+ "docx": "^9.7.1",
80
78
  "esbuild": "^0.28.2",
79
+ "graphology": "^0.26.0",
80
+ "graphology-layout-forceatlas2": "^0.10.1",
81
81
  "playwright-core": "^1.63.0"
82
82
  }
83
83
  }