dsh-code 1.0.7 → 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 (81) hide show
  1. package/README.en.md +70 -24
  2. package/README.md +71 -25
  3. package/bin/deepseek.mjs +202 -39
  4. package/cordis.patch.yml +13 -4
  5. package/lib/index.mjs +4063 -844
  6. package/lib/session-query.mjs +3 -2
  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 +100 -63
  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 +73 -1
  14. package/lib/types/input-split.d.ts +1 -1
  15. package/lib/types/kernel-panels.d.ts +86 -31
  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/inspector.d.ts +26 -0
  25. package/lib/types/render/lines.d.ts +21 -1
  26. package/lib/types/render/markdown.d.ts +1 -1
  27. package/lib/types/render/projection.d.ts +95 -4
  28. package/lib/types/render/status.d.ts +8 -8
  29. package/lib/types/render/text.d.ts +6 -0
  30. package/lib/types/render/usage.d.ts +113 -0
  31. package/lib/types/session-directory.d.ts +17 -0
  32. package/lib/types/startup.d.ts +1 -1
  33. package/lib/types/terminal-title.d.ts +8 -0
  34. package/lib/types/theme-panel.d.ts +2 -2
  35. package/lib/types/theme.d.ts +271 -52
  36. package/lib/types/update-panel.d.ts +5 -5
  37. package/lib/types/update.d.ts +10 -1
  38. package/lib/types/version.d.ts +4 -3
  39. package/package.json +24 -7
  40. package/src/app.ts +1155 -478
  41. package/src/approval.ts +166 -166
  42. package/src/authorization-panel.ts +19 -16
  43. package/src/editor-keys.ts +371 -371
  44. package/src/git-workflow.ts +229 -3
  45. package/src/i18n.ts +68 -0
  46. package/src/index.ts +412 -76
  47. package/src/input-split.ts +3 -3
  48. package/src/kernel-panels.ts +471 -89
  49. package/src/keyboard.ts +5 -4
  50. package/src/language-panel.ts +53 -0
  51. package/src/locales/en.ts +489 -0
  52. package/src/locales/zh.ts +488 -0
  53. package/src/mentions.ts +8 -4
  54. package/src/models.ts +264 -212
  55. package/src/panel-accent.ts +41 -0
  56. package/src/presets.ts +1 -1
  57. package/src/provider-settings.ts +1 -1
  58. package/src/rainbow.ts +208 -0
  59. package/src/render/animations.ts +104 -6
  60. package/src/render/editor.ts +20 -20
  61. package/src/render/export.ts +116 -95
  62. package/src/render/inspector.ts +42 -0
  63. package/src/render/lines.ts +628 -415
  64. package/src/render/markdown.ts +15 -3
  65. package/src/render/projection.ts +429 -19
  66. package/src/render/status.ts +41 -35
  67. package/src/render/text.ts +14 -0
  68. package/src/render/tool-preview.ts +77 -77
  69. package/src/render/usage.ts +430 -0
  70. package/src/render/width.ts +2 -2
  71. package/src/session-directory.ts +8 -6
  72. package/src/session-query.ts +8 -4
  73. package/src/startup.ts +3 -3
  74. package/src/subagents.ts +229 -229
  75. package/src/terminal-title.ts +22 -5
  76. package/src/theme-panel.ts +17 -21
  77. package/src/theme.ts +281 -33
  78. package/src/update-panel.ts +37 -27
  79. package/src/update.ts +19 -3
  80. package/src/version.ts +58 -20
  81. package/src/whale-glyph.ts +23 -23
@@ -4,26 +4,40 @@
4
4
  * (`packages/client/ui-theme/src/styles/design-platform.css`). Truecolor RGB
5
5
  * rides chalk, which degrades automatically on terminals without truecolor.
6
6
  *
7
- * Two palettes — `dark` (the default) and `light` — share the same token keys
8
- * with different values. Painters and the palette accessor read the ACTIVE
7
+ * Three palettes — `dark` (the default), `light`, and `prismatic` (the
8
+ * neon synthwave skin) share the same token keys with different values.
9
+ * Painters and the palette accessor read the ACTIVE
9
10
  * palette selected through {@link setTheme}, so a theme switch recolors every
10
- * painted surface on the next render without touching call sites. Raw token
11
- * consumers keep reading {@link TUI_RGB} (the dark values) until the
12
- * theme-aware integration replaces those call sites with
13
- * `inkColor(getPalette().token)`.
11
+ * painted surface on the next render without touching call sites; consumers
12
+ * read colors through {@link getPalette} or the painters below only.
14
13
  *
15
14
  * @module @deepseek-ai/dsh-tui/theme
16
15
  */
17
16
  /** One RGB triple for a palette token. */
18
17
  export type RgbTriple = readonly [number, number, number];
19
18
  /** Palette token keys shared by every theme. */
20
- export type ThemeToken = 'brand' | 'brandBright' | 'brandMid' | 'brandDeep' | 'dim' | 'success' | 'error' | 'warn' | 'text' | 'code' | 'composerBand';
19
+ export type ThemeToken = 'brand' | 'brandBright' | 'brandMid' | 'brandDeep' | 'dim' | 'success' | 'error' | 'warn' | 'text' | 'code' | 'composerBand' | 'diffAdd' | 'diffDel' | 'diffAddFg' | 'diffDelFg' | 'surface' | 'prompt' | 'queued' | 'steered';
21
20
  /** One full color palette: every token key mapped to an RGB triple. */
22
21
  export type ThemePalette = Readonly<Record<ThemeToken, RgbTriple>>;
23
- /** Selectable theme names: dark, light, or auto (terminal-sensed). */
24
- export type ThemeName = 'dark' | 'light' | 'auto';
22
+ /** Selectable theme names: dark, light, prismatic, rainbow, or auto (terminal-sensed). */
23
+ export type ThemeName = 'dark' | 'light' | 'prismatic' | 'rainbow' | 'auto';
25
24
  /** Valid theme names in canonical picker order. */
26
25
  export declare const THEME_NAMES: readonly ThemeName[];
26
+ /** One /theme picker row: the theme id plus its display copy. */
27
+ export interface ThemeDescriptor {
28
+ /** The selectable theme name. */
29
+ readonly id: ThemeName;
30
+ /** Short row label shown in the picker. */
31
+ readonly label: string;
32
+ /** One-line description shown after the label. */
33
+ readonly description: string;
34
+ }
35
+ /**
36
+ * Every theme's picker metadata in canonical order — the single source the
37
+ * /theme panel rows derive from; {@link THEME_NAMES} stays the validation
38
+ * list. Adding a theme means adding its palette plus one row here.
39
+ */
40
+ export declare const THEMES: readonly ThemeDescriptor[];
27
41
  /**
28
42
  * DeepSeek dark palette: the original TUI colors, one entry per
29
43
  * design-platform token in use. Keep names and values in sync with the CSS
@@ -52,15 +66,34 @@ export declare const DARK_PALETTE: {
52
66
  readonly code: readonly [125, 211, 252];
53
67
  /** Composer three-row band base — neutral light gray, hue-free so wave tints read on it. */
54
68
  readonly composerBand: readonly [46, 48, 52];
69
+ /** Diff added-line background — Codex's muted dark green tint (#213A2B). */
70
+ readonly diffAdd: readonly [33, 58, 43];
71
+ /** Diff removed-line background — Codex's muted dark red tint (#4A221D). */
72
+ readonly diffDel: readonly [74, 34, 29];
73
+ /** Diff added-line foreground — green-500, 5.4:1 on the diffAdd tint. */
74
+ readonly diffAddFg: readonly [34, 197, 94];
75
+ /** Diff removed-line foreground — red-400, 4.9:1 on the diffDel tint (error's red-500 sinks to 3.6:1 on it). */
76
+ readonly diffDelFg: readonly [248, 113, 113];
77
+ /** Terminal background this palette is designed against; row tints blend toward it. */
78
+ readonly surface: readonly [0, 0, 0];
79
+ /**
80
+ * The three prompt-row colors, one per delivery kind. They are the palette's
81
+ * own accents (bright brand, warning amber, violet) rather than fixed RGBs,
82
+ * and every row's bar is these colors laid over {@link surface}.
83
+ */
84
+ readonly prompt: readonly [103, 158, 254];
85
+ readonly queued: readonly [245, 158, 11];
86
+ readonly steered: readonly [167, 139, 250];
55
87
  };
56
88
  /**
57
89
  * Light palette tuned for white terminals: the same token keys as dark with
58
90
  * contrast-driven values (AA on a white background). Brand keeps its dark
59
- * value (≈4.9:1); the bright/mid/deep blues, muted grays, and status colors
60
- * deepen so they stay legible on bright backgrounds.
91
+ * value (≈4.2:1 on white reserved for bold/accent spans; body-size brand
92
+ * text uses brandBright at ≈5.4:1); the bright/mid/deep blues, muted grays,
93
+ * and status colors deepen so they stay legible on bright backgrounds.
61
94
  */
62
95
  export declare const LIGHT_PALETTE: {
63
- /** Primary brand blue — unchanged, ≈4.9:1 AA on white. */
96
+ /** Primary brand blue — unchanged design-platform value; ≈4.2:1 on white, so bold/accent spans only (body-size brand text uses brandBright). */
64
97
  readonly brand: readonly [65, 118, 230];
65
98
  /** Brighter brand blue deepened for white backgrounds (was 2.7:1). */
66
99
  readonly brandBright: readonly [72, 104, 178];
@@ -82,8 +115,77 @@ export declare const LIGHT_PALETTE: {
82
115
  readonly code: readonly [14, 116, 144];
83
116
  /** Composer three-row band base — neutral light gray, hue-free so wave tints read on it. */
84
117
  readonly composerBand: readonly [229, 231, 235];
118
+ /** Diff added-line background — GitHub's pastel green (#dafbe1), Codex's light pick. */
119
+ readonly diffAdd: readonly [218, 251, 225];
120
+ /** Diff removed-line background — GitHub's pastel red (#ffebe9), Codex's light pick. */
121
+ readonly diffDel: readonly [255, 235, 233];
122
+ /** Diff added-line foreground — green-800, 6.4:1 on the pastel tint (green-700 clears 4.5:1 by a hair). */
123
+ readonly diffAddFg: readonly [22, 101, 52];
124
+ /** Diff removed-line foreground — red-700, 5.6:1 on the pastel tint (error's red-600 is 3.9:1). */
125
+ readonly diffDelFg: readonly [185, 28, 28];
126
+ /** Terminal background this palette is designed against; row tints blend toward it. */
127
+ readonly surface: readonly [255, 255, 255];
128
+ /**
129
+ * Prompt-row colors deepened for white: the amber and violet the dark theme
130
+ * uses sit at the AA edge on their own pastel bars, so each kind carries a
131
+ * value that clears 4.5:1 both on the bar and on plain white.
132
+ */
133
+ readonly prompt: readonly [47, 76, 143];
134
+ readonly queued: readonly [124, 53, 10];
135
+ readonly steered: readonly [109, 40, 217];
136
+ };
137
+ /**
138
+ * Prismatic palette — the neon synthwave skin (docs/prismatic-theme-design.md):
139
+ * electric violet replaces the brand blues, neon fuchsia carries live
140
+ * emphasis, neon cyan paints code, and a lavender gray dims captions. Paints
141
+ * on a black terminal; every value meets the same WCAG thresholds as the
142
+ * other palettes (body tokens ≥4.5:1, accents ≥3:1 on pure black). Semantic
143
+ * colors — success/error/warn and the four diff tokens — reuse the dark
144
+ * values verbatim so green still means added and red still means removed.
145
+ */
146
+ export declare const PRISMATIC_PALETTE: {
147
+ /** Primary electric purple — violet-500 (#8B5CF6), 5.0:1 on black. */
148
+ readonly brand: readonly [139, 92, 246];
149
+ /** Live/streaming emphasis — neon fuchsia-300 (#F0ABFC), 11.9:1 on black. */
150
+ readonly brandBright: readonly [240, 171, 252];
151
+ /** Intermediate violet — violet-400 (#A78BFA), 7.7:1 on black. */
152
+ readonly brandMid: readonly [167, 139, 250];
153
+ /** Deep secondary chrome — violet-600 (#7C3AED), 3.7:1 on black (violet-700 misses 3:1). */
154
+ readonly brandDeep: readonly [124, 58, 237];
155
+ /** Muted captions — lavender gray (#A6A3B8), 8.6:1 on black. */
156
+ readonly dim: readonly [166, 163, 184];
157
+ /** Success green — unchanged from dark. */
158
+ readonly success: readonly [34, 197, 94];
159
+ /** Error red — unchanged from dark. */
160
+ readonly error: readonly [239, 68, 68];
161
+ /** Warning amber — unchanged from dark. */
162
+ readonly warn: readonly [245, 158, 11];
163
+ /** Default foreground text — white with a lavender cast (#F0EEF9). */
164
+ readonly text: readonly [240, 238, 249];
165
+ /** Inline/fenced code — neon cyan-300 (#67E8F9), 14.5:1 on black. */
166
+ readonly code: readonly [103, 232, 249];
167
+ /** Composer three-row band base — neutral dark gray, hue-free so wave tints read on it. */
168
+ readonly composerBand: readonly [46, 46, 52];
169
+ /** Diff added-line background — unchanged from dark. */
170
+ readonly diffAdd: readonly [33, 58, 43];
171
+ /** Diff removed-line background — unchanged from dark. */
172
+ readonly diffDel: readonly [74, 34, 29];
173
+ /** Diff added-line foreground — unchanged from dark. */
174
+ readonly diffAddFg: readonly [34, 197, 94];
175
+ /** Diff removed-line foreground — unchanged from dark. */
176
+ readonly diffDelFg: readonly [248, 113, 113];
177
+ /** Terminal background this palette is designed against; row tints blend toward it. */
178
+ readonly surface: readonly [0, 0, 0];
179
+ /** Prompt rows in the skin's own neon: fuchsia, amber, cyan. */
180
+ readonly prompt: readonly [240, 171, 252];
181
+ readonly queued: readonly [245, 158, 11];
182
+ readonly steered: readonly [103, 232, 249];
85
183
  };
86
- /** Every palette by theme name; auto resolves through {@link resolveTheme}. */
184
+ /**
185
+ * Every STATIC palette by theme name; auto resolves through {@link resolveTheme}
186
+ * and rainbow is rolled per launch ({@link rainbowRoll}), so neither lives
187
+ * here — {@link setTheme} resolves both through the same key space.
188
+ */
87
189
  export declare const PALETTES: {
88
190
  readonly dark: {
89
191
  /** Primary brand blue — `--dsw-static-deepseek-500`. */
@@ -108,9 +210,27 @@ export declare const PALETTES: {
108
210
  readonly code: readonly [125, 211, 252];
109
211
  /** Composer three-row band base — neutral light gray, hue-free so wave tints read on it. */
110
212
  readonly composerBand: readonly [46, 48, 52];
213
+ /** Diff added-line background — Codex's muted dark green tint (#213A2B). */
214
+ readonly diffAdd: readonly [33, 58, 43];
215
+ /** Diff removed-line background — Codex's muted dark red tint (#4A221D). */
216
+ readonly diffDel: readonly [74, 34, 29];
217
+ /** Diff added-line foreground — green-500, 5.4:1 on the diffAdd tint. */
218
+ readonly diffAddFg: readonly [34, 197, 94];
219
+ /** Diff removed-line foreground — red-400, 4.9:1 on the diffDel tint (error's red-500 sinks to 3.6:1 on it). */
220
+ readonly diffDelFg: readonly [248, 113, 113];
221
+ /** Terminal background this palette is designed against; row tints blend toward it. */
222
+ readonly surface: readonly [0, 0, 0];
223
+ /**
224
+ * The three prompt-row colors, one per delivery kind. They are the palette's
225
+ * own accents (bright brand, warning amber, violet) rather than fixed RGBs,
226
+ * and every row's bar is these colors laid over {@link surface}.
227
+ */
228
+ readonly prompt: readonly [103, 158, 254];
229
+ readonly queued: readonly [245, 158, 11];
230
+ readonly steered: readonly [167, 139, 250];
111
231
  };
112
232
  readonly light: {
113
- /** Primary brand blue — unchanged, ≈4.9:1 AA on white. */
233
+ /** Primary brand blue — unchanged design-platform value; ≈4.2:1 on white, so bold/accent spans only (body-size brand text uses brandBright). */
114
234
  readonly brand: readonly [65, 118, 230];
115
235
  /** Brighter brand blue deepened for white backgrounds (was 2.7:1). */
116
236
  readonly brandBright: readonly [72, 104, 178];
@@ -132,49 +252,73 @@ export declare const PALETTES: {
132
252
  readonly code: readonly [14, 116, 144];
133
253
  /** Composer three-row band base — neutral light gray, hue-free so wave tints read on it. */
134
254
  readonly composerBand: readonly [229, 231, 235];
255
+ /** Diff added-line background — GitHub's pastel green (#dafbe1), Codex's light pick. */
256
+ readonly diffAdd: readonly [218, 251, 225];
257
+ /** Diff removed-line background — GitHub's pastel red (#ffebe9), Codex's light pick. */
258
+ readonly diffDel: readonly [255, 235, 233];
259
+ /** Diff added-line foreground — green-800, 6.4:1 on the pastel tint (green-700 clears 4.5:1 by a hair). */
260
+ readonly diffAddFg: readonly [22, 101, 52];
261
+ /** Diff removed-line foreground — red-700, 5.6:1 on the pastel tint (error's red-600 is 3.9:1). */
262
+ readonly diffDelFg: readonly [185, 28, 28];
263
+ /** Terminal background this palette is designed against; row tints blend toward it. */
264
+ readonly surface: readonly [255, 255, 255];
265
+ /**
266
+ * Prompt-row colors deepened for white: the amber and violet the dark theme
267
+ * uses sit at the AA edge on their own pastel bars, so each kind carries a
268
+ * value that clears 4.5:1 both on the bar and on plain white.
269
+ */
270
+ readonly prompt: readonly [47, 76, 143];
271
+ readonly queued: readonly [124, 53, 10];
272
+ readonly steered: readonly [109, 40, 217];
273
+ };
274
+ readonly prismatic: {
275
+ /** Primary electric purple — violet-500 (#8B5CF6), 5.0:1 on black. */
276
+ readonly brand: readonly [139, 92, 246];
277
+ /** Live/streaming emphasis — neon fuchsia-300 (#F0ABFC), 11.9:1 on black. */
278
+ readonly brandBright: readonly [240, 171, 252];
279
+ /** Intermediate violet — violet-400 (#A78BFA), 7.7:1 on black. */
280
+ readonly brandMid: readonly [167, 139, 250];
281
+ /** Deep secondary chrome — violet-600 (#7C3AED), 3.7:1 on black (violet-700 misses 3:1). */
282
+ readonly brandDeep: readonly [124, 58, 237];
283
+ /** Muted captions — lavender gray (#A6A3B8), 8.6:1 on black. */
284
+ readonly dim: readonly [166, 163, 184];
285
+ /** Success green — unchanged from dark. */
286
+ readonly success: readonly [34, 197, 94];
287
+ /** Error red — unchanged from dark. */
288
+ readonly error: readonly [239, 68, 68];
289
+ /** Warning amber — unchanged from dark. */
290
+ readonly warn: readonly [245, 158, 11];
291
+ /** Default foreground text — white with a lavender cast (#F0EEF9). */
292
+ readonly text: readonly [240, 238, 249];
293
+ /** Inline/fenced code — neon cyan-300 (#67E8F9), 14.5:1 on black. */
294
+ readonly code: readonly [103, 232, 249];
295
+ /** Composer three-row band base — neutral dark gray, hue-free so wave tints read on it. */
296
+ readonly composerBand: readonly [46, 46, 52];
297
+ /** Diff added-line background — unchanged from dark. */
298
+ readonly diffAdd: readonly [33, 58, 43];
299
+ /** Diff removed-line background — unchanged from dark. */
300
+ readonly diffDel: readonly [74, 34, 29];
301
+ /** Diff added-line foreground — unchanged from dark. */
302
+ readonly diffAddFg: readonly [34, 197, 94];
303
+ /** Diff removed-line foreground — unchanged from dark. */
304
+ readonly diffDelFg: readonly [248, 113, 113];
305
+ /** Terminal background this palette is designed against; row tints blend toward it. */
306
+ readonly surface: readonly [0, 0, 0];
307
+ /** Prompt rows in the skin's own neon: fuchsia, amber, cyan. */
308
+ readonly prompt: readonly [240, 171, 252];
309
+ readonly queued: readonly [245, 158, 11];
310
+ readonly steered: readonly [103, 232, 249];
135
311
  };
136
- };
137
- /**
138
- * The dark palette under its original name: call sites that predate the
139
- * two-palette switch keep compiling and painting identically (the default
140
- * theme IS dark). New code should read the active palette through
141
- * {@link getPalette} so a theme switch reaches it.
142
- *
143
- * @deprecated Read the active palette through {@link getPalette}; this
144
- * compatibility alias is removed in the next minor release.
145
- */
146
- export declare const TUI_RGB: {
147
- /** Primary brand blue — `--dsw-static-deepseek-500`. */
148
- readonly brand: readonly [65, 118, 230];
149
- /** Brighter brand blue for live/streaming emphasis — `--dsw-static-deepseek-400`. */
150
- readonly brandBright: readonly [103, 158, 254];
151
- /** Intermediate brand blue between brand and brandBright — `--dsw-static-deepseek-450`. */
152
- readonly brandMid: readonly [86, 134, 254];
153
- /** Deep brand blue for secondary chrome — `--dsw-static-deepseek-600`. */
154
- readonly brandDeep: readonly [72, 104, 178];
155
- /** Muted caption gray — `--dsw-static-neutral-bluish-600`. */
156
- readonly dim: readonly [129, 133, 140];
157
- /** Success green — `--dsw-static-green-500`. */
158
- readonly success: readonly [34, 197, 94];
159
- /** Error red — `--dsw-static-red-500`. */
160
- readonly error: readonly [239, 68, 68];
161
- /** Warning amber — `--dsw-static-amber-500`. */
162
- readonly warn: readonly [245, 158, 11];
163
- /** Default foreground text — `--dsw-static-neutral-50`. */
164
- readonly text: readonly [236, 240, 246];
165
- /** Inline/fenced code — soft sky blue, distinct from brand accents. */
166
- readonly code: readonly [125, 211, 252];
167
- /** Composer three-row band base — neutral light gray, hue-free so wave tints read on it. */
168
- readonly composerBand: readonly [46, 48, 52];
169
312
  };
170
313
  /**
171
314
  * Resolve a theme name to the palette actually in use. `auto` detection
172
315
  * (OSC 11 terminal background query) is a later enhancement; until it lands,
173
- * auto falls back to the dark palette.
316
+ * auto falls back to the dark palette (prismatic and rainbow are always
317
+ * explicit choices, never auto-resolved).
174
318
  * @param name - the requested theme name.
175
- * @returns 'dark' or 'light' — the palette key to paint with.
319
+ * @returns 'dark', 'light', 'prismatic', or 'rainbow' — the palette key.
176
320
  */
177
- export declare function resolveTheme(name: ThemeName): 'dark' | 'light';
321
+ export declare function resolveTheme(name: ThemeName): 'dark' | 'light' | 'prismatic' | 'rainbow';
178
322
  /**
179
323
  * Switch the active theme: painters and {@link getPalette} reflect the new
180
324
  * palette from the next render onward. The default is dark, so a process
@@ -191,14 +335,89 @@ export declare function getTheme(): ThemeName;
191
335
  /** The palette in force; theme-aware call sites read colors through it. */
192
336
  export declare function getPalette(): ThemePalette;
193
337
  /**
194
- * Parse a persisted theme name: only 'light' and 'auto' survive; anything
195
- * else (missing, corrupt, or unknown) falls back to the dark default.
338
+ * Parse a persisted theme name: only names in {@link THEME_NAMES} survive;
339
+ * anything else (missing, corrupt, or unknown) falls back to the dark default.
196
340
  * @param value - the raw parsed JSON value (expected string).
197
341
  * @returns a valid theme name.
198
342
  */
199
343
  export declare function parseThemeName(value: unknown): ThemeName;
200
344
  /** Ink `color` string for one RGB triple. */
201
345
  export declare function inkColor(triple: RgbTriple): string;
346
+ /**
347
+ * Diff-line background as an Ink `backgroundColor` string, theme-aware and
348
+ * depth-gated (the Codex diff renderer's rule): 16-color terminals paint
349
+ * backgrounds from the saturated system palette, which drowns the text, so
350
+ * they keep the foreground-only look; 256-color and truecolor terminals get
351
+ * the palette's tint (chalk quantizes the RGB form down automatically).
352
+ * @param token - which diff background, added or removed lines.
353
+ * @returns the Ink background color, or undefined to paint foreground-only.
354
+ */
355
+ export declare function diffBackground(token: 'diffAdd' | 'diffDel'): string | undefined;
356
+ /** One prompt-row color: the palette token whose color paints the row. */
357
+ export interface PromptRowTokens {
358
+ readonly fg: 'prompt' | 'queued' | 'steered';
359
+ }
360
+ /**
361
+ * The color family one prompt row wears, chosen by how it was delivered: the
362
+ * ordinary brand accent, the warning amber for a queued prompt, and the
363
+ * palette's own third accent for a steered one. The bar behind it is derived
364
+ * from that same color, so every theme — including a freshly rolled rainbow —
365
+ * carries its own look instead of a fixed triple.
366
+ * @param delivery - how the prompt was delivered; undefined is an ordinary one.
367
+ * @returns the palette token whose color paints the row.
368
+ */
369
+ export declare function promptRowTokens(delivery: 'queued' | 'steered' | undefined): PromptRowTokens;
370
+ /**
371
+ * Blended prompt-row background for one row color: the palette color laid
372
+ * over the surface the palette is designed for (dark themes over black, light
373
+ * over white), depth-gated exactly like {@link diffBackground} — a 16-color
374
+ * terminal keeps the foreground-only look rather than painting a saturated
375
+ * system background behind the text.
376
+ * @param color - the row's palette token.
377
+ * @returns the Ink background color, or undefined to paint foreground-only.
378
+ */
379
+ export declare function rowBackground(color: PromptRowTokens['fg']): string | undefined;
380
+ /**
381
+ * The prismatic surface ring: four neon accents panels cycle through by
382
+ * mount order (cyan → fuchsia → violet → lime), all ≥3:1 on black. Dark and
383
+ * light ignore the ring — {@link surfaceAccent} falls back to the passed-in
384
+ * base color so those themes stay pixel-identical.
385
+ */
386
+ export declare const ACCENT_RING: readonly RgbTriple[];
387
+ /**
388
+ * The prismatic flow anchors: brand violet → neon fuchsia → neon cyan. All
389
+ * three clear 4.5:1 on black, so a flowing foreground stays AA throughout
390
+ * the oscillation (render/animations.ts walks the triangle).
391
+ */
392
+ export declare const FLOW_ANCHORS: readonly RgbTriple[];
393
+ /**
394
+ * Whether the active theme is prismatic (the neon skin with surface rings
395
+ * and flowing accents).
396
+ */
397
+ export declare function isPrismatic(): boolean;
398
+ /** Whether the active theme is rainbow (the per-launch carnival roll). */
399
+ export declare function isRainbow(): boolean;
400
+ /** The active theme's flow walk, if it has one (anchors plus phase offset). */
401
+ export interface ThemeFlow {
402
+ /** Anchor colors walked in order over one 2.4s lap. */
403
+ readonly anchors: readonly RgbTriple[];
404
+ /** Phase offset into the lap, milliseconds (0 for prismatic). */
405
+ readonly phaseMs: number;
406
+ }
407
+ /**
408
+ * The flowing theme's anchor walk: prismatic rides the fixed violet→fuchsia→
409
+ * cyan triangle; rainbow rides its rolled full-spectrum anchors with a random
410
+ * phase; dark and light have no flow (undefined) and keep static accents.
411
+ */
412
+ export declare function themeFlow(): ThemeFlow | undefined;
413
+ /**
414
+ * The accent for one surface slot: prismatic rotates the {@link ACCENT_RING}
415
+ * by slot index; every other theme gets the caller's base color back.
416
+ * @param index - the slot's position in the mount-order sequence.
417
+ * @param base - the color to use outside prismatic (the surface's usual one).
418
+ * @returns the accent to paint the surface's border/title with.
419
+ */
420
+ export declare function surfaceAccent(index: number, base: RgbTriple): RgbTriple;
202
421
  /** Paint with the primary brand blue: whale, wordmark, tool names, accents. */
203
422
  export declare function brand(text: string): string;
204
423
  /** Paint with the bright brand blue: streaming output and active spinners. */
@@ -30,7 +30,7 @@ export declare function updatePlanView(status: LauncherUpdateStatus): UpdatePlan
30
30
  /** Panel lifecycle phases; the footer names the keys each phase accepts. */
31
31
  export type UpdatePhase = 'probe' | 'error' | 'plan' | 'apply' | 'done';
32
32
  /** Footer hint line per phase; the plan phase names the confirm key only when runnable. */
33
- export declare function updateFooter(phase: UpdatePhase, runnable: boolean, upToDate: boolean): string;
33
+ export declare function updateFooter(phase: UpdatePhase, runnable: boolean, upToDate: boolean, aheadOfRegistry?: boolean): string;
34
34
  /**
35
35
  * The /update surface: probe on open (and on r), confirm with enter/y,
36
36
  * stream the aligned apply, and land on a bounded result view. Escape is
@@ -39,11 +39,11 @@ export declare function updateFooter(phase: UpdatePhase, runnable: boolean, upTo
39
39
  */
40
40
  export declare function UpdatePanel({ probe, apply, close, notify }: {
41
41
  /** Read-only probe of the launcher update status (never installs). */
42
- probe(): Promise<LauncherUpdateStatus>;
42
+ probe: () => Promise<LauncherUpdateStatus>;
43
43
  /** Run the aligned update; streams sanitized progress lines. */
44
- apply(onLine: (line: string) => void): Promise<number>;
44
+ apply: (onLine: (line: string) => void, plan: LauncherUpdateStatus['plan']) => Promise<number>;
45
45
  /** Close the panel (App keeps ownership of the flag). */
46
- close(): void;
46
+ close: () => void;
47
47
  /** One bounded cross-surface notice (phase completions). */
48
- notify(text: string, tone?: 'info' | 'warning' | 'error'): void;
48
+ notify: (text: string, tone?: 'info' | 'warning' | 'error') => void;
49
49
  }): ReactElement;
@@ -37,6 +37,14 @@ export interface LauncherUpdateStatus {
37
37
  readonly localCheckout: readonly string[] | null;
38
38
  };
39
39
  readonly upToDate: boolean;
40
+ /** True when this install is newer than npm latest (apply would downgrade). */
41
+ readonly aheadOfRegistry?: boolean;
42
+ }
43
+ /** Exact specs the TUI confirmed; apply must install these, not re-query latest. */
44
+ export interface LauncherUpdatePlan {
45
+ readonly dshSpec: string;
46
+ readonly codeSpec: string;
47
+ readonly pluginSpecs: readonly string[];
40
48
  }
41
49
  /** The launcher entrypoint that ships beside this bundle (lib/../bin). */
42
50
  export declare function launcherUpdateCommand(args: readonly string[], moduleUrl?: string): {
@@ -63,4 +71,5 @@ export declare function probeLauncherUpdate(spawnProcess?: SpawnLike): Promise<L
63
71
  * exit code (0 success); rejects only when the process could not start.
64
72
  * No timeout: an npm install may legitimately take minutes.
65
73
  */
66
- export declare function applyLauncherUpdate(onLine: (line: string) => void, spawnProcess?: SpawnLike): Promise<number>;
74
+ export declare function applyPlanArgs(plan: LauncherUpdatePlan): string[];
75
+ export declare function applyLauncherUpdate(onLine: (line: string) => void, spawnProcess?: SpawnLike, plan?: LauncherUpdatePlan): Promise<number>;
@@ -3,9 +3,10 @@
3
3
  export declare const DSH_CODE_VERSION: string;
4
4
  /**
5
5
  * Resolve the running dsh CLI host's version from its entry file
6
- * (`process.argv[1]`, e.g. `.../@deepseek-ai/dsh/lib/bin.js`). Only a manifest
7
- * literally named `@deepseek-ai/dsh` counts, so an unrelated entry (vitest, a
8
- * plain node script) resolves to undefined instead of faking a kernel version.
6
+ * (`process.argv[1]`, e.g. `.../@deepseek-ai/dsh/lib/bin.js` or a PATH
7
+ * symlink `.../bin/dsh` that file). Only a manifest literally named
8
+ * `@deepseek-ai/dsh` counts, so an unrelated entry (vitest, a plain node
9
+ * script) resolves to undefined instead of faking a kernel version.
9
10
  */
10
11
  export declare function resolveDshHostVersion(entry?: string | undefined): string | undefined;
11
12
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-code",
3
3
  "description": "DeepSeek Harness CLI core bundle: interactive coding terminal, durable sessions, and model management for dsh --profile cli",
4
- "version": "1.0.7",
4
+ "version": "1.2.0",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "deepseek": "./bin/deepseek.mjs",
@@ -58,18 +58,24 @@
58
58
  "scripts": {
59
59
  "build": "tsdown && tsc -p tsconfig.json",
60
60
  "test": "vitest run",
61
+ "test:coverage": "vitest run --coverage",
61
62
  "typecheck": "tsc -p tsconfig.json --noEmit",
63
+ "typecheck:tests": "tsc -p tsconfig.test.json --noEmit",
64
+ "lint": "eslint .",
65
+ "lint:fix": "eslint . --fix",
66
+ "verify": "pnpm lint && pnpm typecheck && pnpm typecheck:tests && pnpm test",
62
67
  "gen:whale": "tsx scripts/gen-whale-glyph.ts",
63
68
  "prepare": "pnpm build",
64
- "prepublishOnly": "pnpm typecheck && pnpm test && pnpm build"
69
+ "prepublishOnly": "pnpm typecheck && pnpm typecheck:tests && pnpm test && pnpm build"
65
70
  },
66
71
  "engines": {
67
72
  "node": "^22.19 || >=24"
68
73
  },
69
74
  "dependencies": {
70
75
  "@deepseek-ai/dsh-authorization": "0.1.5-rc.2",
71
- "@deepseek-ai/dsh-web-fetch-http": "0.1.5-rc.2",
76
+ "@deepseek-ai/dsh-tool-session-query": "0.1.5-rc.2",
72
77
  "@deepseek-ai/dsh-util-values": "0.1.5-rc.2",
78
+ "@deepseek-ai/dsh-web-fetch-http": "0.1.5-rc.2",
73
79
  "@deepseek-ai/schemastery": "^3.18.2",
74
80
  "chalk": "^5.6.2",
75
81
  "commander": "^15.0.0",
@@ -107,6 +113,7 @@
107
113
  "@deepseek-ai/dsh-session": "0.1.5-rc.2",
108
114
  "@deepseek-ai/dsh-session-persistence": "0.1.5-rc.2",
109
115
  "@deepseek-ai/dsh-session-persistence-jsonl": "0.1.5-rc.2",
116
+ "@deepseek-ai/dsh-session-projection": "0.1.5-rc.2",
110
117
  "@deepseek-ai/dsh-session-query": "0.1.5-rc.2",
111
118
  "@deepseek-ai/dsh-session-query-sqlite": "0.1.5-rc.2",
112
119
  "@deepseek-ai/dsh-session-reference": "0.1.5-rc.2",
@@ -117,8 +124,8 @@
117
124
  "@deepseek-ai/dsh-terminal-bash": "0.1.5-rc.2",
118
125
  "@deepseek-ai/dsh-time-context": "0.1.5-rc.2",
119
126
  "@deepseek-ai/dsh-tmux-context": "0.1.5-rc.2",
127
+ "@deepseek-ai/dsh-token-meter": "0.1.5-rc.2",
120
128
  "@deepseek-ai/dsh-tool-lsp": "0.1.5-rc.2",
121
- "@deepseek-ai/dsh-tool-session-query": "0.1.5-rc.2",
122
129
  "@deepseek-ai/dsh-tool-terminal": "0.1.5-rc.2",
123
130
  "@deepseek-ai/dsh-user-approval": "0.1.5-rc.2",
124
131
  "@deepseek-ai/dsh-user-questions": "0.1.5-rc.2"
@@ -214,6 +221,9 @@
214
221
  "@deepseek-ai/dsh-session-persistence-jsonl": {
215
222
  "optional": true
216
223
  },
224
+ "@deepseek-ai/dsh-session-projection": {
225
+ "optional": true
226
+ },
217
227
  "@deepseek-ai/dsh-session-query": {
218
228
  "optional": true
219
229
  },
@@ -244,10 +254,10 @@
244
254
  "@deepseek-ai/dsh-tmux-context": {
245
255
  "optional": true
246
256
  },
247
- "@deepseek-ai/dsh-tool-lsp": {
257
+ "@deepseek-ai/dsh-token-meter": {
248
258
  "optional": true
249
259
  },
250
- "@deepseek-ai/dsh-tool-session-query": {
260
+ "@deepseek-ai/dsh-tool-lsp": {
251
261
  "optional": true
252
262
  },
253
263
  "@deepseek-ai/dsh-tool-terminal": {
@@ -280,6 +290,7 @@
280
290
  "@deepseek-ai/dsh-schedule": "0.1.5-rc.2",
281
291
  "@deepseek-ai/dsh-session-persistence": "0.1.5-rc.2",
282
292
  "@deepseek-ai/dsh-session-persistence-jsonl": "0.1.5-rc.2",
293
+ "@deepseek-ai/dsh-session-projection": "0.1.5-rc.2",
283
294
  "@deepseek-ai/dsh-session-query": "0.1.5-rc.2",
284
295
  "@deepseek-ai/dsh-session-query-sqlite": "0.1.5-rc.2",
285
296
  "@deepseek-ai/dsh-session-reference": "0.1.5-rc.2",
@@ -291,17 +302,23 @@
291
302
  "@deepseek-ai/dsh-terminal-bash": "0.1.5-rc.2",
292
303
  "@deepseek-ai/dsh-time-context": "0.1.5-rc.2",
293
304
  "@deepseek-ai/dsh-tmux-context": "0.1.5-rc.2",
305
+ "@deepseek-ai/dsh-token-meter": "0.1.5-rc.2",
294
306
  "@deepseek-ai/dsh-tool-lsp": "0.1.5-rc.2",
295
- "@deepseek-ai/dsh-tool-session-query": "0.1.5-rc.2",
296
307
  "@deepseek-ai/dsh-tool-terminal": "0.1.5-rc.2",
297
308
  "@deepseek-ai/dsh-tool-todo": "0.1.5-rc.2",
298
309
  "@deepseek-ai/dsh-user-approval": "0.1.5-rc.2",
299
310
  "@deepseek-ai/dsh-user-questions": "0.1.5-rc.2",
311
+ "@types/js-yaml": "^4.0.9",
300
312
  "@types/node": "^24.0.0",
301
313
  "@types/react": "~18.3.1",
314
+ "@vitest/coverage-v8": "^3.2.7",
315
+ "eslint": "^10.10.0",
316
+ "eslint-plugin-react-hooks": "^7.1.1",
317
+ "js-yaml": "^4.3.1",
302
318
  "tsdown": "^0.22.2",
303
319
  "tsx": "^4.22.4",
304
320
  "typescript": "^5.9.0",
321
+ "typescript-eslint": "^8.70.0",
305
322
  "vitest": "^3.0.0"
306
323
  }
307
324
  }