dsh-code 1.0.6 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.en.md +123 -26
  2. package/README.md +124 -27
  3. package/bin/deepseek.mjs +283 -35
  4. package/cordis.patch.yml +97 -0
  5. package/lib/index.mjs +5008 -881
  6. package/lib/session-query.mjs +150 -0
  7. package/lib/startup.mjs +4 -4
  8. package/lib/{theme-DCT8Y2xf.mjs → theme-7u5Qo3dF.mjs} +657 -20
  9. package/lib/types/app.d.ts +106 -62
  10. package/lib/types/authorization-panel.d.ts +3 -3
  11. package/lib/types/git-workflow.d.ts +91 -2
  12. package/lib/types/i18n.d.ts +39 -0
  13. package/lib/types/index.d.ts +100 -1
  14. package/lib/types/input-split.d.ts +1 -1
  15. package/lib/types/kernel-panels.d.ts +107 -29
  16. package/lib/types/language-panel.d.ts +12 -0
  17. package/lib/types/locales/en.d.ts +450 -0
  18. package/lib/types/locales/zh.d.ts +9 -0
  19. package/lib/types/mentions.d.ts +7 -3
  20. package/lib/types/models.d.ts +14 -0
  21. package/lib/types/panel-accent.d.ts +28 -0
  22. package/lib/types/rainbow.d.ts +69 -0
  23. package/lib/types/render/animations.d.ts +42 -0
  24. package/lib/types/render/editor.d.ts +4 -3
  25. package/lib/types/render/ime-cursor.d.ts +60 -0
  26. package/lib/types/render/inspector.d.ts +26 -0
  27. package/lib/types/render/lines.d.ts +21 -1
  28. package/lib/types/render/markdown.d.ts +1 -1
  29. package/lib/types/render/projection.d.ts +130 -4
  30. package/lib/types/render/status.d.ts +9 -9
  31. package/lib/types/render/text.d.ts +6 -0
  32. package/lib/types/render/usage.d.ts +113 -0
  33. package/lib/types/session-directory.d.ts +17 -0
  34. package/lib/types/session-query.d.ts +92 -0
  35. package/lib/types/startup.d.ts +1 -1
  36. package/lib/types/terminal-title.d.ts +66 -0
  37. package/lib/types/theme-panel.d.ts +2 -2
  38. package/lib/types/theme.d.ts +271 -52
  39. package/lib/types/update-panel.d.ts +49 -0
  40. package/lib/types/update.d.ts +75 -0
  41. package/lib/types/version.d.ts +4 -3
  42. package/package.json +246 -90
  43. package/src/app.ts +1369 -509
  44. package/src/approval.ts +166 -166
  45. package/src/authorization-panel.ts +19 -16
  46. package/src/editor-keys.ts +371 -371
  47. package/src/git-workflow.ts +229 -3
  48. package/src/i18n.ts +68 -0
  49. package/src/index.ts +534 -80
  50. package/src/input-split.ts +3 -3
  51. package/src/internals.ts +5 -0
  52. package/src/kernel-panels.ts +554 -86
  53. package/src/keyboard.ts +5 -4
  54. package/src/language-panel.ts +53 -0
  55. package/src/locales/en.ts +489 -0
  56. package/src/locales/zh.ts +488 -0
  57. package/src/mentions.ts +8 -4
  58. package/src/models.ts +264 -212
  59. package/src/panel-accent.ts +41 -0
  60. package/src/presets.ts +1 -1
  61. package/src/provider-settings.ts +1 -1
  62. package/src/rainbow.ts +208 -0
  63. package/src/render/animations.ts +104 -6
  64. package/src/render/editor.ts +25 -24
  65. package/src/render/export.ts +116 -95
  66. package/src/render/ime-cursor.ts +147 -0
  67. package/src/render/inspector.ts +42 -0
  68. package/src/render/lines.ts +628 -415
  69. package/src/render/markdown.ts +15 -3
  70. package/src/render/projection.ts +572 -21
  71. package/src/render/status.ts +59 -39
  72. package/src/render/text.ts +14 -0
  73. package/src/render/tool-preview.ts +77 -77
  74. package/src/render/usage.ts +430 -0
  75. package/src/render/width.ts +2 -2
  76. package/src/session-directory.ts +8 -6
  77. package/src/session-query.ts +239 -0
  78. package/src/startup.ts +3 -3
  79. package/src/subagents.ts +229 -229
  80. package/src/terminal-title.ts +190 -0
  81. package/src/theme-panel.ts +17 -21
  82. package/src/theme.ts +281 -33
  83. package/src/update-panel.ts +256 -0
  84. package/src/update.ts +126 -0
  85. package/src/version.ts +58 -20
  86. package/src/whale-glyph.ts +23 -23
package/bin/deepseek.mjs CHANGED
@@ -8,7 +8,8 @@ import { dirname, join, resolve } from 'node:path'
8
8
  import { fileURLToPath, pathToFileURL } from 'node:url'
9
9
 
10
10
  const packageRequire = createRequire(import.meta.url)
11
- const packageVersion = packageRequire('../package.json').version
11
+ /** This launcher's release version (exported for probes and tests). */
12
+ export const packageVersion = packageRequire('../package.json').version
12
13
 
13
14
  /** Arguments required to boot DSH-Code's conventional profile. */
14
15
  export const profileArgs = (args = []) => ['--profile', 'cli', ...args]
@@ -115,6 +116,97 @@ export function harnessLineFromPeers(peers) {
115
116
  return undefined
116
117
  }
117
118
 
119
+ /**
120
+ * The exact global install that pairs with this launcher: the harness line
121
+ * its own package declares in peers, and its own release. The guidance a
122
+ * missing host prints must not send the user to an unpinned `dsh-code`,
123
+ * which could mount a newer bundle beside this older wrapper.
124
+ */
125
+ export function selfInstallHint(options = {}) {
126
+ const version = options.version ?? packageVersion
127
+ // An explicitly passed peers value — including undefined, the "no peers
128
+ // were readable" case — must win over this launcher's own manifest, which
129
+ // a destructuring default would conflate with the argument being absent.
130
+ const peers = 'peers' in options ? options.peers : packageRequire('../package.json').peerDependencies
131
+ const line = harnessLineFromPeers(peers)
132
+ return `npm install -g @deepseek-ai/dsh${line === undefined ? '' : `@${line}`} dsh-code@${version}`
133
+ }
134
+
135
+ /**
136
+ * Refusal line when npm's latest dsh-code is OLDER than this running
137
+ * launcher — a registry rollback, or a launcher installed from a GitHub
138
+ * release that sits ahead of the registry — and applying would downgrade
139
+ * the bundle back over the user's profile. Undefined when the upgrade may
140
+ * proceed.
141
+ */
142
+ export function bundleDowngradeRefusal(latest, running = packageVersion) {
143
+ return latest !== undefined && compareHarnessLines(latest, running) < 0
144
+ ? `dsh-code: npm latest is dsh-code@${latest}, older than this launcher (dsh-code@${running}); refusing to downgrade the bundle`
145
+ : undefined
146
+ }
147
+
148
+ /**
149
+ * Refusal reason when a local-checkout profile would pair an older local
150
+ * build with the newer global host this upgrade installs, or undefined when
151
+ * the upgrade may proceed (no checkout mounted, or the checkout already
152
+ * matches the release). The caller prints the lines verbatim.
153
+ */
154
+ export function localCheckoutRefusal(mounted, latestCode, codeSpec) {
155
+ if (mounted === latestCode) return undefined
156
+ if (mounted === undefined) {
157
+ return [
158
+ 'dsh-code: the cli profile mounts a local checkout, but its version could not be read',
159
+ 'dsh-code: refusing to upgrade the global host beside an unread checkout',
160
+ 'dsh-code: update the checkout first (git pull, pnpm install, pnpm build), or switch the profile to the published package:',
161
+ `dsh-code: dsh plugin --profile cli remove dsh-code && dsh plugin --profile cli add ${codeSpec}`,
162
+ ]
163
+ }
164
+ return [
165
+ `dsh-code: the cli profile mounts a local checkout of dsh-code ${mounted}, while this upgrade would install ${latestCode} globally`,
166
+ 'dsh-code: upgrading the host beside an older local checkout pairs incompatible code; refusing to install',
167
+ 'dsh-code: update the checkout first (git pull, pnpm install, pnpm build), or switch the profile to the published package:',
168
+ `dsh-code: dsh plugin --profile cli remove dsh-code && dsh plugin --profile cli add ${codeSpec}`,
169
+ ]
170
+ }
171
+
172
+ /** Version after the last @ in an npm spec (`dsh-code@1.0.8`). */
173
+ export function specVersion(spec) {
174
+ if (typeof spec !== 'string') return undefined
175
+ const at = spec.lastIndexOf('@')
176
+ if (at <= 0 || at === spec.length - 1) return undefined
177
+ return spec.slice(at + 1)
178
+ }
179
+
180
+ /**
181
+ * Exact install plan passed to `update --apply` after a probe. Missing
182
+ * `--code` means apply should query npm itself (CLI one-shot).
183
+ */
184
+ export function parsePinnedPlan(args) {
185
+ let dshSpec
186
+ let codeSpec
187
+ const pluginSpecs = []
188
+ for (let index = 0; index < args.length; index += 1) {
189
+ const flag = args[index]
190
+ const value = args[index + 1]
191
+ if (flag === '--dsh' && value !== undefined) {
192
+ dshSpec = value
193
+ index += 1
194
+ continue
195
+ }
196
+ if (flag === '--code' && value !== undefined) {
197
+ codeSpec = value
198
+ index += 1
199
+ continue
200
+ }
201
+ if (flag === '--plugin' && value !== undefined) {
202
+ pluginSpecs.push(value)
203
+ index += 1
204
+ }
205
+ }
206
+ if (codeSpec === undefined || dshSpec === undefined) return undefined
207
+ return { dshSpec, codeSpec, pluginSpecs }
208
+ }
209
+
118
210
  /**
119
211
  * Decide what `update --apply` installs. The global DSH launcher is pinned
120
212
  * to the harness line the target dsh-code release declares in its peers,
@@ -138,6 +230,56 @@ export function updatePlan({ latestCode, peers, profileSpec, profilePlugins = []
138
230
  }
139
231
  }
140
232
 
233
+ /**
234
+ * The concrete steps `update --apply` runs. The host install and the
235
+ * dsh-code mount are fatal: a failure there strands the global host and the
236
+ * profile bundle on different release lines, so the sequence stops. The
237
+ * companion plugin carries are best-effort — one plugin without a build on
238
+ * the target line must not strand the rest of the upgrade, so each reports
239
+ * its manual retry command and lets the sequence finish. Unresolvable dsh
240
+ * commands come back as blockers: they are enforced before any step runs,
241
+ * because starting the host install without a working profile step is
242
+ * exactly the half-updated state the fatal marking exists to prevent.
243
+ */
244
+ export function updateSteps({ plan, npm = npmInvocation(), resolveCommand = rawDshCommand }) {
245
+ const steps = [{
246
+ command: npm.command,
247
+ args: [...npm.args, 'install', '-g', plan.dshSpec, plan.codeSpec],
248
+ label: 'npm install',
249
+ fatal: true,
250
+ remedy: `npm install -g ${plan.dshSpec} ${plan.codeSpec}`,
251
+ }]
252
+ if (!plan.profileStep) return { steps, blockers: [] }
253
+ const blockers = []
254
+ const codeCommand = resolveCommand(['plugin', '--profile', 'cli', 'add', plan.codeSpec])
255
+ if (codeCommand === undefined) {
256
+ blockers.push('dsh-code: could not resolve the dsh command for the profile update')
257
+ } else {
258
+ steps.push({
259
+ command: codeCommand.command,
260
+ args: codeCommand.args,
261
+ label: 'dsh plugin add',
262
+ fatal: true,
263
+ remedy: `dsh plugin --profile cli add ${plan.codeSpec}`,
264
+ })
265
+ }
266
+ for (const pluginSpec of plan.pluginSpecs) {
267
+ const pluginCommand = resolveCommand(['plugin', '--profile', 'cli', 'add', pluginSpec])
268
+ if (pluginCommand === undefined) {
269
+ blockers.push(`dsh-code: could not resolve the dsh command for ${pluginSpec}`)
270
+ continue
271
+ }
272
+ steps.push({
273
+ command: pluginCommand.command,
274
+ args: pluginCommand.args,
275
+ label: `dsh plugin add ${pluginSpec}`,
276
+ fatal: false,
277
+ remedy: `dsh plugin --profile cli add ${pluginSpec}`,
278
+ })
279
+ }
280
+ return { steps, blockers }
281
+ }
282
+
141
283
  /**
142
284
  * Compare two @deepseek-ai/dsh release-line versions (`0.1.x`, `0.1.x-rc.N`,
143
285
  * `0.1.x-alpha.N`). Positive when `left` is newer, negative when older, and 0
@@ -184,23 +326,37 @@ export function installedGlobalDshVersion(roots = globalDshRoots(), fileExists =
184
326
  return undefined
185
327
  }
186
328
 
187
- /** Run wrapper-owned child steps in order, stopping at the first failure. */
329
+ /**
330
+ * Run wrapper-owned child steps in order. A step marked fatal stops the
331
+ * sequence at its failure (the default: the next step depends on this one);
332
+ * a `fatal: false` step is best-effort — its failure is reported with the
333
+ * step's manual retry command, the sequence continues, and the resolved
334
+ * code stays non-zero so the caller still sees the incomplete pass.
335
+ */
188
336
  export function runSequence(steps, spawnProcess = spawn) {
189
337
  return new Promise(resolve => {
338
+ const failures = []
190
339
  const run = index => {
191
340
  const step = steps[index]
192
341
  if (step === undefined) {
193
- resolve(0)
342
+ resolve(failures.length === 0 ? 0 : failures[failures.length - 1])
194
343
  return
195
344
  }
196
345
  const needsShell = process.platform === 'win32' && /\.(cmd|bat)$/iu.test(step.command)
197
346
  const child = spawnProcess(step.command, step.args, { stdio: 'inherit', ...(needsShell ? { shell: true } : {}) })
347
+ const report = code => {
348
+ if (step.remedy !== undefined) console.error(`dsh-code: to retry ${step.label} manually: ${step.remedy}`)
349
+ failures.push(code ?? 1)
350
+ run(index + 1)
351
+ }
198
352
  child.once('error', error => {
199
353
  console.error(`dsh-code: ${step.label} failed: ${error.message}`)
200
- resolve(1)
354
+ if (step.fatal === false) report(undefined)
355
+ else resolve(1)
201
356
  })
202
- child.once('exit', (code, signal) => {
357
+ child.once('exit', code => {
203
358
  if (code === 0) run(index + 1)
359
+ else if (step.fatal === false) report(code)
204
360
  else resolve(code ?? 1)
205
361
  })
206
362
  }
@@ -384,6 +540,62 @@ function printUpdateStatus() {
384
540
  console.log('Run `deepseek update --apply` to upgrade the global packages and the cli profile together.')
385
541
  }
386
542
 
543
+ /**
544
+ * The structured form of the update status for machine consumers
545
+ * (`update --json`, consumed by the TUI /update panel): what is running,
546
+ * what npm serves, the exact steps `update --apply` would run, and every
547
+ * refusal that would stop it. One JSON object on stdout; `null` marks an
548
+ * unreadable value so the payload shape stays constant.
549
+ */
550
+ export function buildUpdateStatus({
551
+ view = viewJson,
552
+ installedDsh = installedDshVersion,
553
+ readSpec = profileDependencySpec,
554
+ readMounted = profileMountedVersion,
555
+ readPlugins = profilePluginDependencies,
556
+ } = {}) {
557
+ const codeLatest = view(['dsh-code', 'version'])
558
+ const installed = installedDsh()
559
+ const spec = readSpec()
560
+ const mounted = readMounted()
561
+ const peers = codeLatest === undefined ? undefined : view([`dsh-code@${codeLatest}`, 'peerDependencies'])
562
+ const plan = updatePlan({
563
+ latestCode: codeLatest ?? 'unknown',
564
+ peers,
565
+ profileSpec: spec,
566
+ profilePlugins: readPlugins(),
567
+ })
568
+ const registry = codeLatest === undefined ? 'could not read the latest dsh-code version from npm' : undefined
569
+ // The same refusals applyUpdate enforces, precomputed so a caller can
570
+ // show them BEFORE any confirmation instead of failing mid-flight.
571
+ const downgrade = plan.line !== undefined && installed !== undefined && compareHarnessLines(plan.line, installed) < 0
572
+ const localCheckout = plan.profileStep ? undefined : localCheckoutRefusal(mounted, codeLatest, plan.codeSpec)
573
+ const hostOnLine = installed !== undefined && (plan.line === undefined || compareHarnessLines(installed, plan.line) === 0)
574
+ // A launcher ahead of the registry (a GitHub-release install) has nothing
575
+ // npm can offer: the registry's older latest must not present as a pending
576
+ // update, and applyUpdate refuses it anyway.
577
+ const bundleAhead = codeLatest !== undefined && compareHarnessLines(codeLatest, packageVersion) < 0
578
+ const upToDate = bundleAhead || (registry === undefined
579
+ && codeLatest === packageVersion
580
+ && !downgrade
581
+ && localCheckout === undefined
582
+ && hostOnLine
583
+ && plan.pluginSpecs.length === 0
584
+ && (mounted === undefined || mounted === codeLatest))
585
+ return {
586
+ code: { running: packageVersion, latest: codeLatest ?? null },
587
+ host: { installed: installed ?? null, targetLine: plan.line ?? null },
588
+ profile: { spec: spec ?? null, mounted: mounted ?? null, localCheckout: !plan.profileStep },
589
+ plan: { dshSpec: plan.dshSpec, codeSpec: plan.codeSpec, pluginSpecs: plan.pluginSpecs },
590
+ // JSON.stringify drops undefined members; every unreadable value must
591
+ // serialize as an explicit null so machine consumers never see a missing
592
+ // key drift into a truthy comparison.
593
+ blockers: { registry: registry ?? null, downgrade, localCheckout: localCheckout ?? null },
594
+ upToDate,
595
+ aheadOfRegistry: bundleAhead,
596
+ }
597
+ }
598
+
387
599
  /**
388
600
  * Upgrade the global launcher and the cli profile plugin together. The
389
601
  * global install is pinned to the harness line the new dsh-code release
@@ -394,21 +606,53 @@ async function applyUpdate({
394
606
  view = viewJson,
395
607
  resolveCommand = rawDshCommand,
396
608
  spawnProcess = spawn,
609
+ pinned,
397
610
  } = {}) {
398
611
  const npm = npmInvocation()
399
- const latestCode = view(['dsh-code', 'version'], undefined, npm)
400
- if (latestCode === undefined) {
401
- console.error('dsh-code: could not read the latest dsh-code version from npm')
612
+ const profileSpec = profileDependencySpec()
613
+ const profilePlugins = profilePluginDependencies()
614
+ let latestCode
615
+ let plan
616
+ if (pinned !== undefined) {
617
+ latestCode = specVersion(pinned.codeSpec)
618
+ if (latestCode === undefined) {
619
+ console.error(`dsh-code: could not read a version from ${pinned.codeSpec}`)
620
+ process.exitCode = 1
621
+ return
622
+ }
623
+ const line = specVersion(pinned.dshSpec)
624
+ plan = {
625
+ dshSpec: pinned.dshSpec,
626
+ line,
627
+ lineLocked: line !== undefined,
628
+ codeSpec: pinned.codeSpec,
629
+ profileStep: !(typeof profileSpec === 'string' && /^(link|file):/iu.test(profileSpec)),
630
+ pluginSpecs: pinned.pluginSpecs,
631
+ }
632
+ } else {
633
+ latestCode = view(['dsh-code', 'version'], undefined, npm)
634
+ if (latestCode === undefined) {
635
+ console.error('dsh-code: could not read the latest dsh-code version from npm')
636
+ process.exitCode = 1
637
+ return
638
+ }
639
+ const peers = view([`dsh-code@${latestCode}`, 'peerDependencies'], undefined, npm)
640
+ plan = updatePlan({
641
+ latestCode,
642
+ peers,
643
+ profileSpec,
644
+ profilePlugins,
645
+ })
646
+ }
647
+ // A launcher installed from a GitHub release (or any source ahead of the
648
+ // registry) must not be walked back by an apply targeting npm's older
649
+ // latest - the downgrade guard below only covers the host line.
650
+ const bundleRefusal = bundleDowngradeRefusal(latestCode)
651
+ if (bundleRefusal !== undefined) {
652
+ console.error(bundleRefusal)
402
653
  process.exitCode = 1
403
654
  return
404
655
  }
405
- const peers = view([`dsh-code@${latestCode}`, 'peerDependencies'], undefined, npm)
406
- const plan = updatePlan({
407
- latestCode,
408
- peers,
409
- profileSpec: profileDependencySpec(),
410
- profilePlugins: profilePluginDependencies(),
411
- })
412
656
  if (!plan.lineLocked) {
413
657
  console.log('dsh-code: could not read the compatible harness line; installing @deepseek-ai/dsh@latest')
414
658
  }
@@ -424,26 +668,26 @@ async function applyUpdate({
424
668
  process.exitCode = 1
425
669
  return
426
670
  }
427
- const steps = [{ command: npm.command, args: [...npm.args, 'install', '-g', plan.dshSpec, plan.codeSpec], label: 'npm install' }]
428
- if (plan.profileStep) {
429
- const command = resolveCommand(['plugin', '--profile', 'cli', 'add', plan.codeSpec])
430
- if (command === undefined) {
431
- console.error('dsh-code: could not resolve the dsh command for the profile update')
671
+ // Local-checkout guard: a link/file-mounted profile runs the checkout's
672
+ // own build, and this command would upgrade the global host beside it.
673
+ // When the checkout is older than the release being installed, upgrading
674
+ // pairs a new host with old local code — the terminal breaks on the next
675
+ // launch. Refuse until the checkout is updated or unmounted.
676
+ if (!plan.profileStep) {
677
+ const refusal = localCheckoutRefusal(profileMountedVersion(), latestCode, plan.codeSpec)
678
+ if (refusal !== undefined) {
679
+ for (const line of refusal) console.error(line)
432
680
  process.exitCode = 1
433
681
  return
434
682
  }
435
- steps.push({ command: command.command, args: command.args, label: 'dsh plugin add' })
436
- // Companion plugins ride the same line: without this, `dsh plugin add`-ed
437
- // extras stay pinned to the old harness line after an upgrade.
438
- for (const pluginSpec of plan.pluginSpecs) {
439
- const pluginCommand = resolveCommand(['plugin', '--profile', 'cli', 'add', pluginSpec])
440
- if (pluginCommand === undefined) {
441
- console.error(`dsh-code: could not resolve the dsh command for ${pluginSpec}`)
442
- process.exitCode = 1
443
- return
444
- }
445
- steps.push({ command: pluginCommand.command, args: pluginCommand.args, label: `dsh plugin add ${pluginSpec}` })
446
- }
683
+ }
684
+ const { steps, blockers } = updateSteps({ plan, npm, resolveCommand })
685
+ if (blockers.length > 0) {
686
+ for (const line of blockers) console.error(line)
687
+ process.exitCode = 1
688
+ return
689
+ }
690
+ if (plan.profileStep) {
447
691
  if (plan.pluginSpecs.length > 0) {
448
692
  console.log(`carrying ${plan.pluginSpecs.length} profile plugin${plan.pluginSpecs.length === 1 ? '' : 's'} to the same line: ${plan.pluginSpecs.join(', ')}`)
449
693
  }
@@ -483,15 +727,19 @@ export function launchOperation(args = process.argv.slice(2)) {
483
727
  // Setup must mount the exact globally installed release on launch day.
484
728
  const command = rawDshCommand(['plugin', '--profile', 'cli', 'add', setupBundle(args)])
485
729
  if (command === undefined) {
486
- console.error('dsh-code: @deepseek-ai/dsh is not installed; run: npm install -g @deepseek-ai/dsh dsh-code')
730
+ console.error(`dsh-code: @deepseek-ai/dsh is not installed; run: ${selfInstallHint()}`)
487
731
  process.exitCode = 1
488
732
  return true
489
733
  }
490
734
  return launchChild(command.command, command.args)
491
735
  }
492
736
  if (operation === 'update') {
737
+ if (args.includes('--json')) {
738
+ console.log(JSON.stringify(buildUpdateStatus()))
739
+ return true
740
+ }
493
741
  if (args.includes('--apply')) {
494
- void applyUpdate()
742
+ void applyUpdate({ pinned: parsePinnedPlan(args) })
495
743
  return true
496
744
  }
497
745
  printUpdateStatus()
@@ -522,7 +770,7 @@ export function launchDsh(
522
770
  }
523
771
  const command = resolveCommand(args)
524
772
  if (command === undefined) {
525
- console.error('dsh-code: @deepseek-ai/dsh must be installed globally beside dsh-code; run: npm install -g @deepseek-ai/dsh dsh-code')
773
+ console.error(`dsh-code: @deepseek-ai/dsh must be installed globally beside dsh-code; run: ${selfInstallHint()}`)
526
774
  process.exitCode = 1
527
775
  return undefined
528
776
  }
package/cordis.patch.yml CHANGED
@@ -24,6 +24,17 @@
24
24
  config:
25
25
  mode: !!js process.env.DSH_TOOLS_MODE
26
26
 
27
+ # The base row keeps full-text search off entirely (openAt: never) so nothing
28
+ # imports node:sqlite at boot; the terminal replaces it with the bundle's
29
+ # skip-tolerant subclass below. An override row cannot retarget a row's
30
+ # MODULE (name is only honored on inserts), so the base row is disabled and
31
+ # an equivalent row mounts the subclass: the derived index opens lazily on
32
+ # the first search (no startup cost, no Node 22 experimental warning), and
33
+ # one unreadable source (a pre-release artifact the frozen codecs reject)
34
+ # is skipped with a warning instead of failing every cross-session search.
35
+ - id: session-query-sqlite
36
+ disabled: true
37
+
27
38
  # Agent-plane rows are mounted by the selected preset. Keep this list aligned
28
39
  # with the upstream Web bundle so two sessions can use different compositions
29
40
  # without leaking model-facing registrations through the host layer.
@@ -132,6 +143,92 @@
132
143
  - id: authorization
133
144
  name: '@deepseek-ai/dsh-authorization'
134
145
 
146
+ # The bundle's skip-tolerant session-query engine replacing the disabled
147
+ # base row above: same SQLite FTS5 service, observation loop patched to
148
+ # skip unreadable sources instead of failing every search.
149
+ - id: session-query-skip
150
+ name: 'dsh-code/session-query'
151
+ config:
152
+ path: ':memory:'
153
+ openAt: first-search
154
+
155
+ # Model-facing session-log search/trace/read tools over the sessionQuery
156
+ # service (read-only, workspace-scoped by exact cwd). A host-plane mount
157
+ # is deployment-global: every preset session sees the five tools. Kept
158
+ # ENABLED deliberately — read-only search leaks no capability — but note
159
+ # it does reach minimal-preset sessions too. The host CLI does not bundle
160
+ # this package, so it ships as a real dependency and resolves from the
161
+ # profile's own node_modules.
162
+ - id: tool-session-query
163
+ name: '@deepseek-ai/dsh-tool-session-query'
164
+
165
+ # Durable reminders: the schedule service arms per-root timers, injects
166
+ # due prompts as plugin-source user messages, and writes schedule/change
167
+ # events the projection folds into the /schedule panel. time-context
168
+ # gives the model a clock reading so 'at' times are phrased against the
169
+ # actual time; the 30s throttle bounds durable snapshot growth.
170
+ - id: schedule
171
+ name: '@deepseek-ai/dsh-schedule'
172
+
173
+ - id: time-context
174
+ name: '@deepseek-ai/dsh-time-context'
175
+ config:
176
+ refreshIntervalMs: 30000
177
+
178
+ # Persistent PTY plumbing: the owner-scoped registry and the one backend
179
+ # package in both dialects (pwsh is the Windows/ConPTY path, bash covers
180
+ # POSIX) mount as host SERVICES — no model-facing surface, so they break
181
+ # no preset boundary. The six terminal_* TOOLS ship DISABLED like LSP:
182
+ # enabling them at the host plane would hand every preset (including the
183
+ # shell-restricted minimal one) raw shell capability, the exact leak the
184
+ # disables above exist to prevent. Flip tool-terminal to disabled: false
185
+ # in the PROFILE layer to opt the deployment into PTY tools — but install
186
+ # @deepseek-ai/dsh-tool-terminal into the profile first (the host CLI does
187
+ # not bundle it; an enabled row over a missing package fails the boot):
188
+ # dsh plugin --profile cli add @deepseek-ai/dsh-tool-terminal
189
+ - id: pty
190
+ name: '@deepseek-ai/dsh-terminal'
191
+
192
+ - id: terminal-pwsh
193
+ name: '@deepseek-ai/dsh-terminal-bash'
194
+ disabled: !!js process.platform !== 'win32'
195
+ config:
196
+ shellDialect: pwsh
197
+
198
+ - id: terminal-bash
199
+ name: '@deepseek-ai/dsh-terminal-bash'
200
+ disabled: !!js process.platform === 'win32'
201
+
202
+ - id: tool-terminal
203
+ name: '@deepseek-ai/dsh-tool-terminal'
204
+ disabled: true
205
+
206
+ # LSP navigation ships DISABLED: lsp-stdio resolves every server command
207
+ # at mount and a missing binary would fail the whole composition boot.
208
+ # Enable in the PROFILE layer with real servers, e.g.:
209
+ # lsp / lsp-stdio / tool-lsp rows with disabled: false and
210
+ # lsp-stdio config.servers mapping extensions to server commands.
211
+ # The three packages are also not bundled by the host CLI: install them
212
+ # into the profile first or the enabled rows fail the boot.
213
+ - id: lsp
214
+ name: '@deepseek-ai/dsh-lsp'
215
+ disabled: true
216
+
217
+ - id: lsp-stdio
218
+ name: '@deepseek-ai/dsh-lsp-stdio'
219
+ disabled: true
220
+
221
+ - id: tool-lsp
222
+ name: '@deepseek-ai/dsh-tool-lsp'
223
+ disabled: true
224
+
225
+ # MCP servers (dsh-mcp-client) and hooks bridges (dsh-hooks-claude-code /
226
+ # -codex) take per-instance config (server transports, hooks.json paths),
227
+ # so they mount as rows in the USER profile layer, not here; the host CLI
228
+ # bundles these peers, so those rows resolve. README documents the row
229
+ # shapes, and rows over packages the host does not bundle need the package
230
+ # added to the profile first.
231
+
135
232
  - id: tui-startup
136
233
  name: 'dsh-code/startup'
137
234