dsh-code 0.4.0 → 0.6.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 (49) hide show
  1. package/README.en.md +217 -0
  2. package/README.md +216 -67
  3. package/bin/deepseek.mjs +70 -0
  4. package/cordis.patch.yml +62 -6
  5. package/lib/devtools-CdTl3MNy.mjs +3643 -0
  6. package/lib/index.mjs +27467 -673
  7. package/lib/rolldown-runtime-CMFfr-1z.mjs +26 -0
  8. package/lib/startup.mjs +34 -17
  9. package/lib/types/app.d.ts +29 -1
  10. package/lib/types/commands.d.ts +2 -0
  11. package/lib/types/history.d.ts +79 -0
  12. package/lib/types/index.d.ts +5 -5
  13. package/lib/types/internals.d.ts +2 -0
  14. package/lib/types/kernel-panels.d.ts +48 -0
  15. package/lib/types/plugin-inventory.d.ts +11 -0
  16. package/lib/types/presets.d.ts +32 -0
  17. package/lib/types/render/animations.d.ts +10 -1
  18. package/lib/types/render/inspector.d.ts +6 -0
  19. package/lib/types/render/projection.d.ts +21 -1
  20. package/lib/types/render/status.d.ts +131 -14
  21. package/lib/types/render/text.d.ts +9 -0
  22. package/lib/types/session-directory.d.ts +54 -0
  23. package/lib/types/session-switch.d.ts +17 -0
  24. package/lib/types/skills.d.ts +2 -0
  25. package/lib/types/startup.d.ts +11 -1
  26. package/package.json +117 -112
  27. package/src/app.ts +2543 -1969
  28. package/src/commands.ts +15 -1
  29. package/src/history.ts +136 -0
  30. package/src/index.ts +550 -155
  31. package/src/internals.ts +5 -0
  32. package/src/kernel-panels.ts +419 -0
  33. package/src/plugin-inventory.ts +47 -0
  34. package/src/presets.ts +64 -0
  35. package/src/render/animations.ts +14 -1
  36. package/src/render/export.ts +4 -0
  37. package/src/render/inspector.ts +23 -5
  38. package/src/render/lines.ts +21 -10
  39. package/src/render/markdown.ts +15 -1
  40. package/src/render/projection.ts +71 -8
  41. package/src/render/status.ts +522 -65
  42. package/src/render/text.ts +34 -6
  43. package/src/session-directory.ts +102 -0
  44. package/src/session-switch.ts +58 -0
  45. package/src/skills.ts +20 -7
  46. package/src/startup.ts +38 -20
  47. package/src/whale-glyph.ts +23 -23
  48. package/README.zh.md +0 -65
  49. package/src/pictures/1.png +0 -0
package/src/commands.ts CHANGED
@@ -16,6 +16,8 @@ import type { CommandDescriptor } from '@deepseek-ai/dsh-commands'
16
16
  export interface CommandsView {
17
17
  /** Name-sorted descriptors after scoped shadowing. */
18
18
  readonly descriptors: readonly CommandDescriptor[]
19
+ /** Latest descriptor-read failure; the help panel exposes it in place. */
20
+ readonly error?: string
19
21
  /** Subscribe to list changes (`commands/change`); returns the unsubscribe function. */
20
22
  subscribe(listener: () => void): () => void
21
23
  /** Retarget the agent whose scoped view the list is read through. */
@@ -35,10 +37,19 @@ export function watchCommands(ctx: Context): CommandsView {
35
37
  const commands = ctx.get('commands')
36
38
  let agent: Agent | undefined
37
39
  let descriptors: readonly CommandDescriptor[] = []
40
+ let error: string | undefined
38
41
  const listeners = new Set<() => void>()
39
42
  const refresh = (): void => {
40
43
  if (commands === undefined || agent === undefined) return
41
- descriptors = commands.list(agent)
44
+ try {
45
+ descriptors = commands.list(agent)
46
+ error = undefined
47
+ } catch (cause: unknown) {
48
+ // Keep the last good catalog, but change its identity so subscribers
49
+ // can render the recoverable failure in /help.
50
+ descriptors = [...descriptors]
51
+ error = cause instanceof Error ? cause.message : String(cause)
52
+ }
42
53
  for (const listener of listeners) listener()
43
54
  }
44
55
  if (commands !== undefined) {
@@ -48,6 +59,9 @@ export function watchCommands(ctx: Context): CommandsView {
48
59
  get descriptors(): readonly CommandDescriptor[] {
49
60
  return descriptors
50
61
  },
62
+ get error(): string | undefined {
63
+ return error
64
+ },
51
65
  subscribe(listener: () => void): () => void {
52
66
  listeners.add(listener)
53
67
  return () => {
package/src/history.ts ADDED
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Global input recall: persistent cross-session entries plus this process's
3
+ * submissions, with Codex `ChatComposerHistory` semantics — empty submissions
4
+ * are ignored, adjacent duplicates collapse, the recall space skips
5
+ * persistent entries that duplicate a local one (local wins), and Up/Down
6
+ * navigation is gated so interior cursor movement never hijacks the draft.
7
+ *
8
+ * @module @deepseek-ai/dsh-tui/history
9
+ */
10
+
11
+ /** Maximum entries retained in the persistent history file. */
12
+ export const HISTORY_MAX_ENTRIES = 500
13
+
14
+ /** Encode one entry for the history file (JSON keeps multi-line drafts intact). */
15
+ export function serializeHistoryEntry(text: string): string {
16
+ return JSON.stringify(text)
17
+ }
18
+
19
+ /**
20
+ * Parse a persisted history file (one JSON entry per line): invalid lines
21
+ * drop out, empty entries are ignored, adjacent duplicates collapse, and the
22
+ * result keeps only the newest `max` entries.
23
+ * @param raw - file content, empty for a missing file.
24
+ * @param max - entry cap.
25
+ * @returns persistent entries, oldest first.
26
+ */
27
+ export function parseHistoryFile(raw: string, max = HISTORY_MAX_ENTRIES): readonly string[] {
28
+ const kept: string[] = []
29
+ for (const line of raw.split('\n')) {
30
+ if (line === '') continue
31
+ let text: unknown
32
+ try {
33
+ text = JSON.parse(line)
34
+ } catch {
35
+ continue
36
+ }
37
+ if (typeof text !== 'string' || text === '') continue
38
+ if (kept.length > 0 && kept[kept.length - 1] === text) continue
39
+ kept.push(text)
40
+ }
41
+ return kept.slice(-max)
42
+ }
43
+
44
+ /**
45
+ * Append one entry to the persistent file content: JSON line, capped to the
46
+ * newest `max` entries with a trailing newline.
47
+ * @param current - existing file content.
48
+ * @param text - submission to persist.
49
+ * @param max - entry cap.
50
+ * @returns the new file content.
51
+ */
52
+ export function appendHistoryContent(current: string, text: string, max = HISTORY_MAX_ENTRIES): string {
53
+ const entries = [...parseHistoryFile(current, max), text].slice(-max)
54
+ return entries.map(serializeHistoryEntry).join('\n') + '\n'
55
+ }
56
+
57
+ /**
58
+ * Record one in-session submission: empty text is ignored and an adjacent
59
+ * duplicate collapses (Codex `record_local_submission` semantics).
60
+ * @param local - current in-session entries, oldest first.
61
+ * @param text - the submitted prompt.
62
+ * @returns the updated local list.
63
+ */
64
+ export function recordLocalEntry(local: readonly string[], text: string): readonly string[] {
65
+ if (text === '') return local
66
+ if (local.length > 0 && local[local.length - 1] === text) return local
67
+ return [...local, text]
68
+ }
69
+
70
+ /**
71
+ * Build the recall space, newest first: local entries, then persistent
72
+ * entries whose text is not duplicated locally (the local copy wins and the
73
+ * persistent twin is skipped — Codex's replay-seed dedup, applied to the
74
+ * whole local set).
75
+ * @param persistent - cross-session entries, oldest first.
76
+ * @param local - this process's submissions, oldest first.
77
+ * @returns recall entries, newest first.
78
+ */
79
+ export function recallEntries(persistent: readonly string[], local: readonly string[]): readonly string[] {
80
+ const localSet = new Set(local)
81
+ return [...persistent.filter(entry => !localSet.has(entry)), ...local].reverse()
82
+ }
83
+
84
+ /** Shell-style recall navigation over a fixed recall space. */
85
+ export interface RecallState {
86
+ /** Recall entries, newest first (frozen at navigation start). */
87
+ entries: readonly string[]
88
+ /** Current recall index; null when not browsing. */
89
+ index: number | null
90
+ /** Draft saved when browsing started; restored on Down past the newest. */
91
+ savedDraft: string
92
+ /** The recalled text currently in the composer (the boundary gate's anchor). */
93
+ lastRecalled: string | null
94
+ }
95
+
96
+ /** Fresh navigation state over one recall space. */
97
+ export function beginRecall(entries: readonly string[], draft: string): RecallState {
98
+ return { entries, index: null, savedDraft: draft, lastRecalled: null }
99
+ }
100
+
101
+ /** The outcome of one recall step. */
102
+ export interface RecallStep {
103
+ state: RecallState
104
+ /** The text to place in the composer; undefined means "no movement". */
105
+ entry: string | undefined
106
+ }
107
+
108
+ /**
109
+ * Move one entry older (Up, toward index +1 in the newest-first space). The
110
+ * first Up saves the current draft so Down past the newest can restore it
111
+ * (Claude-Code shell recall — the draft is never lost); the oldest entry
112
+ * stays put.
113
+ * @param state - current navigation state.
114
+ * @param draft - the composer text to preserve when browsing starts.
115
+ */
116
+ export function recallOlder(state: RecallState, draft: string): RecallStep {
117
+ if (state.index === null) {
118
+ const entry = state.entries[0]
119
+ if (entry === undefined) return { state, entry: undefined }
120
+ return { state: { ...state, index: 0, savedDraft: draft, lastRecalled: entry }, entry }
121
+ }
122
+ if (state.index >= state.entries.length - 1) return { state, entry: undefined }
123
+ const entry = state.entries[state.index + 1]
124
+ return { state: { ...state, index: state.index + 1, lastRecalled: entry }, entry }
125
+ }
126
+
127
+ /** Move one entry newer (Down, toward index 0); past the newest, browsing ends and the saved draft returns. */
128
+ export function recallNewer(state: RecallState): RecallStep {
129
+ if (state.index === null) return { state, entry: undefined }
130
+ const next = state.index - 1
131
+ if (next < 0) {
132
+ return { state: { ...state, index: null, lastRecalled: null }, entry: state.savedDraft }
133
+ }
134
+ const entry = state.entries[next]
135
+ return { state: { ...state, index: next, lastRecalled: entry }, entry }
136
+ }