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.
- package/README.en.md +217 -0
- package/README.md +216 -67
- package/bin/deepseek.mjs +70 -0
- package/cordis.patch.yml +62 -6
- package/lib/devtools-CdTl3MNy.mjs +3643 -0
- package/lib/index.mjs +27467 -673
- package/lib/rolldown-runtime-CMFfr-1z.mjs +26 -0
- package/lib/startup.mjs +34 -17
- package/lib/types/app.d.ts +29 -1
- package/lib/types/commands.d.ts +2 -0
- package/lib/types/history.d.ts +79 -0
- package/lib/types/index.d.ts +5 -5
- package/lib/types/internals.d.ts +2 -0
- package/lib/types/kernel-panels.d.ts +48 -0
- package/lib/types/plugin-inventory.d.ts +11 -0
- package/lib/types/presets.d.ts +32 -0
- package/lib/types/render/animations.d.ts +10 -1
- package/lib/types/render/inspector.d.ts +6 -0
- package/lib/types/render/projection.d.ts +21 -1
- package/lib/types/render/status.d.ts +131 -14
- package/lib/types/render/text.d.ts +9 -0
- package/lib/types/session-directory.d.ts +54 -0
- package/lib/types/session-switch.d.ts +17 -0
- package/lib/types/skills.d.ts +2 -0
- package/lib/types/startup.d.ts +11 -1
- package/package.json +117 -112
- package/src/app.ts +2543 -1969
- package/src/commands.ts +15 -1
- package/src/history.ts +136 -0
- package/src/index.ts +550 -155
- package/src/internals.ts +5 -0
- package/src/kernel-panels.ts +419 -0
- package/src/plugin-inventory.ts +47 -0
- package/src/presets.ts +64 -0
- package/src/render/animations.ts +14 -1
- package/src/render/export.ts +4 -0
- package/src/render/inspector.ts +23 -5
- package/src/render/lines.ts +21 -10
- package/src/render/markdown.ts +15 -1
- package/src/render/projection.ts +71 -8
- package/src/render/status.ts +522 -65
- package/src/render/text.ts +34 -6
- package/src/session-directory.ts +102 -0
- package/src/session-switch.ts +58 -0
- package/src/skills.ts +20 -7
- package/src/startup.ts +38 -20
- package/src/whale-glyph.ts +23 -23
- package/README.zh.md +0 -65
- 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
|
-
|
|
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
|
+
}
|