dsh-code 1.0.4 → 1.0.5
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.md +9 -7
- package/bin/deepseek.mjs +221 -11
- package/cordis.patch.yml +12 -14
- package/lib/index.mjs +164 -94
- package/lib/types/git-workflow.d.ts +7 -2
- package/lib/types/history.d.ts +18 -11
- package/lib/types/presets.d.ts +4 -1
- package/lib/types/questions.d.ts +16 -12
- package/lib/types/render/projection.d.ts +2 -1
- package/lib/types/settings-file.d.ts +8 -0
- package/package.json +49 -46
- package/src/app.ts +1 -1
- package/src/git-workflow.ts +29 -10
- package/src/history.ts +22 -13
- package/src/index.ts +50 -23
- package/src/permissions.ts +1 -1
- package/src/presets.ts +19 -6
- package/src/questions.ts +57 -74
- package/src/render/export.ts +7 -7
- package/src/render/projection.ts +2 -1
- package/src/settings-file.ts +38 -6
package/src/render/export.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* @module @deepseek-ai/dsh-code/render/export
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { assertNever } from '@deepseek-ai/dsh-
|
|
9
|
+
import { assertNever } from '@deepseek-ai/dsh-util-values'
|
|
10
10
|
import { imageLabels, type TranscriptView } from './projection.ts'
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -32,12 +32,12 @@ export function buildExportMarkdown(view: TranscriptView, sessionId: string): st
|
|
|
32
32
|
out.push('## user', '', entry.text, ...(imageLabels(entry.images) === '' ? [] : [imageLabels(entry.images)]), '')
|
|
33
33
|
}
|
|
34
34
|
break
|
|
35
|
-
case 'assistant':
|
|
36
|
-
if (entry.reasoning !== '') {
|
|
37
|
-
out.push('<details><summary>thinking</summary>', '', entry.reasoning, '', '</details>', '')
|
|
38
|
-
}
|
|
39
|
-
out.push('## assistant', '', entry.text, '')
|
|
40
|
-
break
|
|
35
|
+
case 'assistant':
|
|
36
|
+
if (entry.reasoning !== '') {
|
|
37
|
+
out.push('<details><summary>thinking</summary>', '', entry.reasoning, '', '</details>', '')
|
|
38
|
+
}
|
|
39
|
+
out.push('## assistant', '', entry.text, '')
|
|
40
|
+
break
|
|
41
41
|
case 'tool':
|
|
42
42
|
out.push(`### tool \`${entry.name}\``, '')
|
|
43
43
|
if (entry.preview !== '') out.push(`- args: ${entry.preview}`)
|
package/src/render/projection.ts
CHANGED
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { boundContextSummary, type ContentBlock, type ImageBlock, type MessageId } from '@deepseek-ai/dsh-llm'
|
|
11
|
-
import type { SessionEvent
|
|
11
|
+
import type { SessionEvent } from '@deepseek-ai/dsh-session'
|
|
12
|
+
import type { TodoItem } from '@deepseek-ai/dsh-tool-todo'
|
|
12
13
|
import { graphemeWidth, splitGraphemes } from './width.ts'
|
|
13
14
|
// Type-only imports merge the plugin-owned SessionEventMap variants
|
|
14
15
|
// (agent/inbox/spliced, command/*, compaction/*, goal/change, llm/retry*,
|
package/src/settings-file.ts
CHANGED
|
@@ -16,9 +16,46 @@
|
|
|
16
16
|
* @module @deepseek-ai/dsh-code/settings-file
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
+
import { randomUUID } from 'node:crypto'
|
|
19
20
|
import { mkdir, rename, writeFile } from 'node:fs/promises'
|
|
20
21
|
import { dirname } from 'node:path'
|
|
21
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Run one file operation with a bounded retry: one initial try plus at
|
|
25
|
+
* most `retries` more. Creating or replacing a file can fail transiently
|
|
26
|
+
* with EPERM/EACCES while an antivirus scanner or search indexer holds
|
|
27
|
+
* it — the standard graceful-fs remedy, not a workaround for a
|
|
28
|
+
* persistent permission problem. A save that still fails leaves its
|
|
29
|
+
* uniquely named temp file behind, so repeated crashed saves accumulate
|
|
30
|
+
* distinct leftovers rather than corrupting a shared one.
|
|
31
|
+
*/
|
|
32
|
+
async function withTransientRetry(operation: () => Promise<void>, retries = 5): Promise<void> {
|
|
33
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
34
|
+
try {
|
|
35
|
+
await operation()
|
|
36
|
+
return
|
|
37
|
+
} catch (error: unknown) {
|
|
38
|
+
const code = (error as NodeJS.ErrnoException).code
|
|
39
|
+
if (attempt >= retries || (code !== 'EPERM' && code !== 'EACCES')) throw error
|
|
40
|
+
await new Promise(resolve => setTimeout(resolve, 30 * (attempt + 1)))
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Write one file atomically: create the parent directory, write to a
|
|
47
|
+
* uniquely named temp file, and rename it into place. A crash midway
|
|
48
|
+
* can never leave a half-written document behind. Unique temp names
|
|
49
|
+
* keep concurrent writers (two terminals, two chains in one process)
|
|
50
|
+
* from sharing one temp path.
|
|
51
|
+
*/
|
|
52
|
+
export async function writeFileAtomically(path: string, text: string): Promise<void> {
|
|
53
|
+
await mkdir(dirname(path), { recursive: true })
|
|
54
|
+
const temp = `${path}.${process.pid}.${randomUUID()}.tmp`
|
|
55
|
+
await withTransientRetry(() => writeFile(temp, text, 'utf8'))
|
|
56
|
+
await withTransientRetry(() => rename(temp, path))
|
|
57
|
+
}
|
|
58
|
+
|
|
22
59
|
/** The serialized persistence surface; flush() is handed to the quit sequence. */
|
|
23
60
|
export interface UserSettingsPersistence {
|
|
24
61
|
/**
|
|
@@ -39,12 +76,7 @@ export function createUserSettingsPersistence(): UserSettingsPersistence {
|
|
|
39
76
|
let chain: Promise<void> = Promise.resolve()
|
|
40
77
|
return {
|
|
41
78
|
save(path: string, text: string): Promise<void> {
|
|
42
|
-
const write = chain.then(
|
|
43
|
-
await mkdir(dirname(path), { recursive: true })
|
|
44
|
-
const temp = `${path}.tmp`
|
|
45
|
-
await writeFile(temp, text, 'utf8')
|
|
46
|
-
await rename(temp, path)
|
|
47
|
-
})
|
|
79
|
+
const write = chain.then(() => writeFileAtomically(path, text))
|
|
48
80
|
// A failed write must not break the chain for later saves.
|
|
49
81
|
chain = write.catch(() => {})
|
|
50
82
|
return write
|