dsh-code 0.6.0 → 0.6.1
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 +4 -2
- package/README.md +4 -2
- package/bin/deepseek.mjs +38 -3
- package/lib/index.mjs +12 -7
- package/lib/types/render/projection.d.ts +10 -7
- package/package.json +1 -1
- package/src/index.ts +5 -0
- package/src/render/projection.ts +11 -7
package/README.en.md
CHANGED
|
@@ -37,11 +37,13 @@ The interface follows familiar terminal conventions, while runtime behavior cont
|
|
|
37
37
|
Requires Node `^22.19 || >=24`, the preview `dsh` CLI, and `DEEPSEEK_API_KEY`.
|
|
38
38
|
|
|
39
39
|
```sh
|
|
40
|
-
npm install -g @deepseek-ai/dsh
|
|
40
|
+
npm install -g @deepseek-ai/dsh dsh-code
|
|
41
41
|
npm install -g pnpm
|
|
42
|
-
dsh plugin --profile cli add dsh-code
|
|
42
|
+
dsh plugin --profile cli add dsh-code@0.6.1
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
+
> Note: pnpm ignores packages published less than 24 hours ago, so pin `dsh-code@0.6.1` on the release day; the version can be omitted afterwards. npm installs are not affected.
|
|
46
|
+
|
|
45
47
|
Available launch commands:
|
|
46
48
|
|
|
47
49
|
```sh
|
package/README.md
CHANGED
|
@@ -37,11 +37,13 @@ DSH-Code 保留这套结构,并补充适合编码任务的终端工作流。
|
|
|
37
37
|
需要 Node `^22.19 || >=24`、预览版 `dsh` CLI,以及 `DEEPSEEK_API_KEY`。
|
|
38
38
|
|
|
39
39
|
```sh
|
|
40
|
-
npm install -g @deepseek-ai/dsh
|
|
40
|
+
npm install -g @deepseek-ai/dsh dsh-code
|
|
41
41
|
npm install -g pnpm
|
|
42
|
-
dsh plugin --profile cli add dsh-code
|
|
42
|
+
dsh plugin --profile cli add dsh-code@0.6.1
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
+
> 提示:pnpm 会忽略发布不足 24 小时的包,因此发布首日请使用精确版本 `dsh-code@0.6.1`;24 小时后可省略版本号。npm 安装不受此限制。
|
|
46
|
+
|
|
45
47
|
可用的启动指令:
|
|
46
48
|
```sh
|
|
47
49
|
deepseek
|
package/bin/deepseek.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { spawn } from 'node:child_process'
|
|
4
|
-
import { existsSync, realpathSync } from 'node:fs'
|
|
4
|
+
import { existsSync, readFileSync, realpathSync } from 'node:fs'
|
|
5
5
|
import { createRequire } from 'node:module'
|
|
6
|
+
import { homedir } from 'node:os'
|
|
6
7
|
import { join, resolve } from 'node:path'
|
|
7
8
|
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
8
9
|
|
|
@@ -11,6 +12,30 @@ const packageRequire = createRequire(import.meta.url)
|
|
|
11
12
|
/** Arguments required to boot DSH-Code's conventional profile. */
|
|
12
13
|
export const profileArgs = (args = []) => ['--profile', 'cli', ...args]
|
|
13
14
|
|
|
15
|
+
/** The conventional cli profile directory under the DSH home. */
|
|
16
|
+
export function cliProfileDir(home = homedir()) {
|
|
17
|
+
return join(home, '.dsh', 'profiles', 'cli')
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Whether the cli profile already mounts dsh-code. A bare profile composes
|
|
22
|
+
* dsh-base alone and keeps the process alive with no TUI runner — the alias
|
|
23
|
+
* must fail loudly with the missing step instead of hanging.
|
|
24
|
+
*/
|
|
25
|
+
export function profileHasDshCode(profileDir = cliProfileDir(), fileExists = existsSync, readFile = readFileSync) {
|
|
26
|
+
const manifest = join(profileDir, 'package.json')
|
|
27
|
+
if (!fileExists(manifest)) return false
|
|
28
|
+
try {
|
|
29
|
+
const raw = JSON.parse(readFile(manifest, 'utf8'))
|
|
30
|
+
const bundles = raw?.dsh?.profile?.bundles
|
|
31
|
+
const dependencies = raw?.dependencies
|
|
32
|
+
return (Array.isArray(bundles) && bundles.includes('dsh-code'))
|
|
33
|
+
|| (typeof dependencies === 'object' && dependencies !== null && 'dsh-code' in dependencies)
|
|
34
|
+
} catch {
|
|
35
|
+
return false
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
14
39
|
/** Npm global-prefix roots that may contain DSH when this launcher is globally linked to a checkout. */
|
|
15
40
|
export function globalDshRoots() {
|
|
16
41
|
return [...new Set([
|
|
@@ -43,10 +68,20 @@ export function dshCommand(args, {
|
|
|
43
68
|
}
|
|
44
69
|
|
|
45
70
|
/** Launch the installed DSH CLI while preserving its exit status and stdio. */
|
|
46
|
-
export function launchDsh(
|
|
71
|
+
export function launchDsh(
|
|
72
|
+
args = process.argv.slice(2),
|
|
73
|
+
spawnProcess = spawn,
|
|
74
|
+
resolveCommand = dshCommand,
|
|
75
|
+
isProfileReady = profileHasDshCode,
|
|
76
|
+
) {
|
|
77
|
+
if (!isProfileReady()) {
|
|
78
|
+
console.error('dsh-code: the cli profile does not mount dsh-code yet. Run: dsh plugin --profile cli add dsh-code')
|
|
79
|
+
process.exitCode = 1
|
|
80
|
+
return undefined
|
|
81
|
+
}
|
|
47
82
|
const command = resolveCommand(args)
|
|
48
83
|
if (command === undefined) {
|
|
49
|
-
console.error('dsh-code: @deepseek-ai/dsh must be installed globally beside dsh-code; run: npm install -g @deepseek-ai/dsh
|
|
84
|
+
console.error('dsh-code: @deepseek-ai/dsh must be installed globally beside dsh-code; run: npm install -g @deepseek-ai/dsh dsh-code')
|
|
50
85
|
process.exitCode = 1
|
|
51
86
|
return undefined
|
|
52
87
|
}
|
package/lib/index.mjs
CHANGED
|
@@ -25430,19 +25430,23 @@ function projectEvents(events) {
|
|
|
25430
25430
|
return events.reduce(projectEvent, createTranscriptView());
|
|
25431
25431
|
}
|
|
25432
25432
|
/**
|
|
25433
|
-
*
|
|
25434
|
-
*
|
|
25435
|
-
*
|
|
25436
|
-
*
|
|
25437
|
-
*
|
|
25438
|
-
*
|
|
25439
|
-
*
|
|
25433
|
+
* The append-only flush boundary for a transcript view: the count of entries
|
|
25434
|
+
* no later event can remove. Entries at or beyond this index are mutable and
|
|
25435
|
+
* must stay in the live tree.
|
|
25436
|
+
*
|
|
25437
|
+
* `pending` rows are excluded even though they are not a running tool/retry:
|
|
25438
|
+
* the inbox claims or cancels them durably (`agent/inbox/spliced` removals,
|
|
25439
|
+
* `user/message` retirement), and an append-only `<Static>` flush cannot
|
|
25440
|
+
* erase a row that vanishes from the view — the retired row would ghost on
|
|
25441
|
+
* screen until the next source-backed replay. Everything else (including a
|
|
25442
|
+
* completed tail) is final: later events only APPEND new rows.
|
|
25440
25443
|
* @param entries - the view's transcript entries in order.
|
|
25441
25444
|
* @returns the count of entries safe to flush (0 for an empty transcript).
|
|
25442
25445
|
*/
|
|
25443
25446
|
function settledEntryCount(entries) {
|
|
25444
25447
|
for (let index = 0; index < entries.length; index++) {
|
|
25445
25448
|
const entry = entries[index];
|
|
25449
|
+
if (entry.kind === "pending") return index;
|
|
25446
25450
|
if (entry.kind === "tool" && entry.state === "running") return index;
|
|
25447
25451
|
if (entry.kind === "retry" && entry.state === "running") return index;
|
|
25448
25452
|
}
|
|
@@ -30276,6 +30280,7 @@ async function run(ctx, startup, io) {
|
|
|
30276
30280
|
mentions = next.mentions;
|
|
30277
30281
|
commands.setAgent(agent);
|
|
30278
30282
|
skills.setAgent(agent);
|
|
30283
|
+
process.stdout.write("\x1B[r\x1B[0m\x1B[H\x1B[2J\x1B[3J\x1B[H");
|
|
30279
30284
|
renderCurrent();
|
|
30280
30285
|
const queued = pendingInputs.splice(0);
|
|
30281
30286
|
for (const item of queued) deliverLine(item.text, item.mode);
|
|
@@ -235,13 +235,16 @@ export declare function projectEvent(view: TranscriptView, event: SessionEvent):
|
|
|
235
235
|
*/
|
|
236
236
|
export declare function projectEvents(events: readonly SessionEvent[]): TranscriptView;
|
|
237
237
|
/**
|
|
238
|
-
*
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
238
|
+
* The append-only flush boundary for a transcript view: the count of entries
|
|
239
|
+
* no later event can remove. Entries at or beyond this index are mutable and
|
|
240
|
+
* must stay in the live tree.
|
|
241
|
+
*
|
|
242
|
+
* `pending` rows are excluded even though they are not a running tool/retry:
|
|
243
|
+
* the inbox claims or cancels them durably (`agent/inbox/spliced` removals,
|
|
244
|
+
* `user/message` retirement), and an append-only `<Static>` flush cannot
|
|
245
|
+
* erase a row that vanishes from the view — the retired row would ghost on
|
|
246
|
+
* screen until the next source-backed replay. Everything else (including a
|
|
247
|
+
* completed tail) is final: later events only APPEND new rows.
|
|
245
248
|
* @param entries - the view's transcript entries in order.
|
|
246
249
|
* @returns the count of entries safe to flush (0 for an empty transcript).
|
|
247
250
|
*/
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-code",
|
|
3
3
|
"description": "Claude-Code-style interactive TUI bundle for DeepSeek Harness (dsh): DeepSeek-blue whale banner, live session transcript, and a blended status line",
|
|
4
|
-
"version": "0.6.
|
|
4
|
+
"version": "0.6.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"deepseek": "./bin/deepseek.mjs",
|
package/src/index.ts
CHANGED
|
@@ -523,6 +523,11 @@ async function run(ctx: Context, startup: TuiStartup, io: TuiIo): Promise<void>
|
|
|
523
523
|
mentions = next.mentions
|
|
524
524
|
commands.setAgent(agent)
|
|
525
525
|
skills.setAgent(agent)
|
|
526
|
+
// The App mounts with a placeholder key until the first input; the
|
|
527
|
+
// key-change remount below must start from a clean screen or the ghost
|
|
528
|
+
// static header stays visible above the new one (same source-backed
|
|
529
|
+
// clear the session-switch path performs).
|
|
530
|
+
process.stdout.write('\x1b[r\x1b[0m\x1b[H\x1b[2J\x1b[3J\x1b[H')
|
|
526
531
|
renderCurrent()
|
|
527
532
|
const queued = pendingInputs.splice(0)
|
|
528
533
|
for (const item of queued) deliverLine(item.text, item.mode)
|
package/src/render/projection.ts
CHANGED
|
@@ -635,19 +635,23 @@ export function projectEvents(events: readonly SessionEvent[]): TranscriptView {
|
|
|
635
635
|
}
|
|
636
636
|
|
|
637
637
|
/**
|
|
638
|
-
*
|
|
639
|
-
*
|
|
640
|
-
*
|
|
641
|
-
*
|
|
642
|
-
*
|
|
643
|
-
*
|
|
644
|
-
*
|
|
638
|
+
* The append-only flush boundary for a transcript view: the count of entries
|
|
639
|
+
* no later event can remove. Entries at or beyond this index are mutable and
|
|
640
|
+
* must stay in the live tree.
|
|
641
|
+
*
|
|
642
|
+
* `pending` rows are excluded even though they are not a running tool/retry:
|
|
643
|
+
* the inbox claims or cancels them durably (`agent/inbox/spliced` removals,
|
|
644
|
+
* `user/message` retirement), and an append-only `<Static>` flush cannot
|
|
645
|
+
* erase a row that vanishes from the view — the retired row would ghost on
|
|
646
|
+
* screen until the next source-backed replay. Everything else (including a
|
|
647
|
+
* completed tail) is final: later events only APPEND new rows.
|
|
645
648
|
* @param entries - the view's transcript entries in order.
|
|
646
649
|
* @returns the count of entries safe to flush (0 for an empty transcript).
|
|
647
650
|
*/
|
|
648
651
|
export function settledEntryCount(entries: readonly TranscriptEntry[]): number {
|
|
649
652
|
for (let index = 0; index < entries.length; index++) {
|
|
650
653
|
const entry = entries[index]
|
|
654
|
+
if (entry.kind === 'pending') return index
|
|
651
655
|
if (entry.kind === 'tool' && entry.state === 'running') return index
|
|
652
656
|
if (entry.kind === 'retry' && entry.state === 'running') return index
|
|
653
657
|
}
|