dsh-code 0.1.0 → 0.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.
- package/README.md +11 -3
- package/README.zh.md +11 -3
- package/cordis.patch.yml +16 -5
- package/lib/index.mjs +890 -88
- package/lib/invariant.mjs +1 -1
- package/lib/startup.mjs +70 -0
- package/lib/types/app.d.ts +35 -9
- package/lib/types/approval.d.ts +57 -0
- package/lib/types/commands.d.ts +37 -0
- package/lib/types/index.d.ts +19 -7
- package/lib/types/invariant.d.ts +2 -2
- package/lib/types/models.d.ts +37 -0
- package/lib/types/render/projection.d.ts +24 -3
- package/lib/types/render/text.d.ts +18 -0
- package/lib/types/skills.d.ts +45 -0
- package/lib/types/startup.d.ts +44 -0
- package/lib/types/store.d.ts +8 -2
- package/package.json +28 -3
- package/src/app.ts +452 -42
- package/src/approval.ts +126 -0
- package/src/commands.ts +71 -0
- package/src/index.ts +289 -40
- package/src/invariant.ts +3 -3
- package/src/models.ts +66 -0
- package/src/render/projection.ts +70 -4
- package/src/render/text.ts +24 -0
- package/src/skills.ts +104 -0
- package/src/startup.ts +91 -0
- package/src/store.ts +10 -4
package/lib/invariant.mjs
CHANGED
package/lib/startup.mjs
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { parseCmdline } from "@deepseek-ai/dsh-cmdline";
|
|
3
|
+
//#region src/startup.ts
|
|
4
|
+
/**
|
|
5
|
+
* The interactive terminal app's command-line provider: parses `--resume`,
|
|
6
|
+
* `--continue`, `--session`, and `--help`, then publishes
|
|
7
|
+
* {@link TUI_STARTUP_SERVICE} for the runner to consume lazily. Follows the
|
|
8
|
+
* headless bundle's startup shape (a commander action publishing a service
|
|
9
|
+
* through {@link parseCmdline}).
|
|
10
|
+
*
|
|
11
|
+
* Semantics:
|
|
12
|
+
* - `--resume <id|prefix>` — continue the persisted session whose id or unique
|
|
13
|
+
* id-prefix matches; the TUI replays its transcript and appends to the same
|
|
14
|
+
* durable log.
|
|
15
|
+
* - `--continue` / `-c` — resume the most recently modified persisted session
|
|
16
|
+
* whose project directory matches the current working directory.
|
|
17
|
+
* - `--session <id>` — create a new session under an explicit identity (the
|
|
18
|
+
* id must not exist yet).
|
|
19
|
+
* - no flags — a fresh session with a minted id.
|
|
20
|
+
*
|
|
21
|
+
* @module @deepseek-ai/dsh-tui/startup
|
|
22
|
+
*/
|
|
23
|
+
/** Stable Cordis plugin name. */
|
|
24
|
+
const name = "tui-startup";
|
|
25
|
+
/** Services required before the invocation can be resolved. */
|
|
26
|
+
const inject = ["cmdlineArgs"];
|
|
27
|
+
/** Service provided by this plugin and injected by the terminal runner. */
|
|
28
|
+
const TUI_STARTUP_SERVICE = "tuiStartup";
|
|
29
|
+
/**
|
|
30
|
+
* This app's command: the launcher's flags this app owns, its description,
|
|
31
|
+
* and its help text.
|
|
32
|
+
* @returns a fresh program, so one process can parse more than once (tests).
|
|
33
|
+
*/
|
|
34
|
+
function tuiCommand() {
|
|
35
|
+
return new Command().name("dsh --profile cli").description("Claude-Code-style interactive terminal for DeepSeek Harness.").helpOption("-h, --help", "show this help").option("-r, --resume <session>", "resume the persisted session with this id (or unique id prefix)").option("-c, --continue", "resume the most recent persisted session for this working directory").option("--session <id>", "create a new session under this explicit id").addHelpText("after", `
|
|
36
|
+
Examples:
|
|
37
|
+
dsh --profile cli fresh session, minted id
|
|
38
|
+
dsh --profile cli --resume abc123 resume session by id prefix
|
|
39
|
+
dsh --profile cli --continue resume the latest local session
|
|
40
|
+
`);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Parse the invocation and publish the startup service. Mutual exclusions are
|
|
44
|
+
* usage errors rejected from the action before anything is provided.
|
|
45
|
+
* @param ctx - plugin context carrying the command line and exit request.
|
|
46
|
+
*/
|
|
47
|
+
function apply(ctx) {
|
|
48
|
+
const program = tuiCommand();
|
|
49
|
+
program.action(() => {
|
|
50
|
+
const options = program.opts();
|
|
51
|
+
if ([
|
|
52
|
+
options.resume !== void 0,
|
|
53
|
+
options.continue === true,
|
|
54
|
+
options.session !== void 0
|
|
55
|
+
].filter(Boolean).length > 1) program.error("error: --resume, --continue, and --session are mutually exclusive");
|
|
56
|
+
if (options.session !== void 0 && options.session === "") program.error("error: --session needs an id");
|
|
57
|
+
if (options.resume !== void 0 && options.resume === "") program.error("error: --resume needs a session id or id prefix");
|
|
58
|
+
const startup = options.resume !== void 0 ? {
|
|
59
|
+
kind: "resume",
|
|
60
|
+
sessionId: options.resume
|
|
61
|
+
} : options.continue === true ? { kind: "latest" } : options.session !== void 0 ? {
|
|
62
|
+
kind: "named",
|
|
63
|
+
sessionId: options.session
|
|
64
|
+
} : { kind: "fresh" };
|
|
65
|
+
ctx.provide(TUI_STARTUP_SERVICE, { startup });
|
|
66
|
+
});
|
|
67
|
+
parseCmdline(ctx, program);
|
|
68
|
+
}
|
|
69
|
+
//#endregion
|
|
70
|
+
export { TUI_STARTUP_SERVICE, apply, inject, name };
|
package/lib/types/app.d.ts
CHANGED
|
@@ -1,23 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The Ink terminal app: whale-and-wordmark header in DeepSeek blue, the live
|
|
3
|
-
* transcript, the
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* transcript, the todo panel, the streaming line, the approval bar, the model
|
|
4
|
+
* panel, local notices, and the input box with history and slash-command
|
|
5
|
+
* completion. All state arrives through the transcript store (derived from
|
|
6
|
+
* the durable session log) plus local input state; the app owns no session
|
|
7
|
+
* mutation of its own.
|
|
6
8
|
*
|
|
7
9
|
* Element construction uses `createElement` (not JSX): the `dsh` source launch
|
|
8
10
|
* compiles this file through tsx's ESM-only hook, which does not adopt this
|
|
9
11
|
* package's `jsx: react-jsx` compiler option, and the classic JSX runtime
|
|
10
12
|
* would demand a React global.
|
|
11
13
|
*
|
|
12
|
-
* @module @deepseek-ai/dsh-
|
|
14
|
+
* @module @deepseek-ai/dsh-code/app
|
|
13
15
|
*/
|
|
14
16
|
import { type ReactElement } from 'react';
|
|
15
17
|
import type { TranscriptStore } from './store.ts';
|
|
18
|
+
import type { ApprovalStore } from './approval.ts';
|
|
19
|
+
import type { CommandsView } from './commands.ts';
|
|
20
|
+
import type { ModelDirectory, ModelRow } from './models.ts';
|
|
21
|
+
import type { SkillsView } from './skills.ts';
|
|
16
22
|
/** Props the runner hands the app; callbacks stay owned by the runner. */
|
|
17
23
|
export interface AppProps {
|
|
18
24
|
/** Event-fed transcript store for the live session. */
|
|
19
25
|
store: TranscriptStore;
|
|
20
|
-
/**
|
|
26
|
+
/** Approval-question store fed by the answerer listener. */
|
|
27
|
+
approval: ApprovalStore;
|
|
28
|
+
/** Live slash-command descriptor list (completion candidates). */
|
|
29
|
+
commands: CommandsView;
|
|
30
|
+
/** Live user-invocable skill catalog (completion candidates). */
|
|
31
|
+
skills: SkillsView;
|
|
32
|
+
/** `provider/model` selection serving this session (updated on /model). */
|
|
21
33
|
model: string;
|
|
22
34
|
/** Working-directory basename the session serves. */
|
|
23
35
|
cwd: string;
|
|
@@ -25,10 +37,24 @@ export interface AppProps {
|
|
|
25
37
|
branch: string;
|
|
26
38
|
/** Short session identifier. */
|
|
27
39
|
sessionId: string;
|
|
28
|
-
/**
|
|
29
|
-
|
|
40
|
+
/** Whether this session was resumed from persistence. */
|
|
41
|
+
resumed: boolean;
|
|
42
|
+
/** Submit one line: slash commands to the registry, other text to the agent. */
|
|
43
|
+
dispatch(text: string): void;
|
|
44
|
+
/** Submit steering: consumed at the running turn's next step boundary. */
|
|
45
|
+
steer(text: string): void;
|
|
46
|
+
/** Interrupt the running turn (Esc); true when a turn was cancelled. */
|
|
47
|
+
interrupt(): boolean;
|
|
30
48
|
/** Quit: unmount, flush, and request process exit. */
|
|
31
|
-
|
|
49
|
+
quit(): void;
|
|
50
|
+
/** Load the selectable model directory (called when /model opens). */
|
|
51
|
+
loadModels(): Promise<ModelDirectory>;
|
|
52
|
+
/** Apply one /model selection; returns the display label. */
|
|
53
|
+
selectModel(row: ModelRow): string;
|
|
54
|
+
/** Registers the app's notice channel with the runner (called once on mount). */
|
|
55
|
+
onBridgeReady(bridge: {
|
|
56
|
+
notify(text: string): void;
|
|
57
|
+
}): void;
|
|
32
58
|
}
|
|
33
59
|
/** The whole terminal app; state arrives via the store, output via Ink. */
|
|
34
|
-
export declare function App(
|
|
60
|
+
export declare function App(props: AppProps): ReactElement;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The terminal approval answerer: one `approval/request` waterfall listener
|
|
3
|
+
* that renders the pending question as a y/n bar and resolves the decision
|
|
4
|
+
* back into the waterfall. Mirrors the web host's composer takeover — the
|
|
5
|
+
* service (audit pair, policy gate, fail-closed defaults) all live in
|
|
6
|
+
* dsh-base; this module only answers for agents this TUI owns.
|
|
7
|
+
*
|
|
8
|
+
* Vocabulary note: a client answerer may only ever resolve `'allowed-once'`
|
|
9
|
+
* or `'rejected'`; `'cancelled'` belongs to the request signal and
|
|
10
|
+
* `'unavailable'` to the fail-closed waterfall default.
|
|
11
|
+
*
|
|
12
|
+
* @module @deepseek-ai/dsh-code/approval
|
|
13
|
+
*/
|
|
14
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
15
|
+
import type { Agent } from '@deepseek-ai/dsh-agent';
|
|
16
|
+
import type { ApprovalRequest } from '@deepseek-ai/dsh-user-approval';
|
|
17
|
+
/** The answer values a client answerer may resolve with. */
|
|
18
|
+
export type ApprovalAnswer = 'allowed-once' | 'rejected';
|
|
19
|
+
/** One pending approval question, derived from the request for rendering. */
|
|
20
|
+
export interface PendingApproval {
|
|
21
|
+
/** The asker's human-readable explanation, or a generic fallback. */
|
|
22
|
+
headline: string;
|
|
23
|
+
/** The tool the question is about. */
|
|
24
|
+
toolName: string;
|
|
25
|
+
/** Command-line preview resolved from the paired streaming tool call. */
|
|
26
|
+
command: string;
|
|
27
|
+
/** Resolve the ask; calling twice is inert (one-shot latch). */
|
|
28
|
+
answer(outcome: ApprovalAnswer): void;
|
|
29
|
+
}
|
|
30
|
+
/** The pending-question snapshot the renderer subscribes to. */
|
|
31
|
+
export interface ApprovalSnapshot {
|
|
32
|
+
/** The pending question, or undefined when none is being asked. */
|
|
33
|
+
pending: PendingApproval | undefined;
|
|
34
|
+
/** Presentational: an answer was submitted, the ask has not settled yet. */
|
|
35
|
+
answered: boolean;
|
|
36
|
+
}
|
|
37
|
+
/** Store the pending question lands in; the renderer reads, the answerer writes. */
|
|
38
|
+
export interface ApprovalStore {
|
|
39
|
+
/** Subscribe to pending-state changes; returns the unsubscribe function. */
|
|
40
|
+
subscribe(listener: () => void): () => void;
|
|
41
|
+
/** Read the current snapshot (identity-stable between changes). */
|
|
42
|
+
getSnapshot(): ApprovalSnapshot;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Create the approval store and mount the answerer listener on the context.
|
|
46
|
+
* The listener claims only requests for `owns`-owned agents and defers every
|
|
47
|
+
* other request back into the waterfall (`next()`), so sibling answerers stay
|
|
48
|
+
* usable. An aborted ask never reaches the human. Plugin teardown removes the
|
|
49
|
+
* listener; the service then fails its own question closed.
|
|
50
|
+
* @param ctx - plugin context whose event bus carries `approval/request`.
|
|
51
|
+
* @param owns - agents this terminal answers for.
|
|
52
|
+
* @param preview - resolves a tool-call preview for a pending request (the
|
|
53
|
+
* request contract carries no arguments; the UI self-serves from the
|
|
54
|
+
* transcript projection via `callId`).
|
|
55
|
+
* @returns the store the renderer subscribes to.
|
|
56
|
+
*/
|
|
57
|
+
export declare function mountApprovalAnswerer(ctx: Context, owns: (agent: Agent) => boolean, preview: (request: ApprovalRequest) => string): ApprovalStore;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slash-command bridge: forwards terminal command lines into the shared
|
|
3
|
+
* `ctx.commands` registry (the same surface the web composer dispatches
|
|
4
|
+
* through) and exposes the live descriptor list as completion candidates.
|
|
5
|
+
* The runner keeps only its own TUI-local commands (`/help`, `/quit`,
|
|
6
|
+
* `/clear`, `/model`) ahead of the registry dispatch.
|
|
7
|
+
*
|
|
8
|
+
* @module @deepseek-ai/dsh-tui/commands
|
|
9
|
+
*/
|
|
10
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
11
|
+
import type { Agent } from '@deepseek-ai/dsh-agent';
|
|
12
|
+
import type { CommandDescriptor } from '@deepseek-ai/dsh-commands';
|
|
13
|
+
/** Descriptor list snapshot the completion menu renders from. */
|
|
14
|
+
export interface CommandsView {
|
|
15
|
+
/** Name-sorted descriptors after scoped shadowing. */
|
|
16
|
+
readonly descriptors: readonly CommandDescriptor[];
|
|
17
|
+
/** Subscribe to list changes (`commands/change`); returns the unsubscribe function. */
|
|
18
|
+
subscribe(listener: () => void): () => void;
|
|
19
|
+
/** Retarget the agent whose scoped view the list is read through. */
|
|
20
|
+
setAgent(agent: Agent): void;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Watch the live command registry. Reads the current list immediately and
|
|
24
|
+
* re-reads on every registry mutation or agent retarget; notification
|
|
25
|
+
* failures are contained by the registry itself, so this watcher only ever
|
|
26
|
+
* re-reads. Without a `commands` service the view stays empty and all lines
|
|
27
|
+
* fall through to normal prompts.
|
|
28
|
+
* @param ctx - context carrying the `commands` service (optional).
|
|
29
|
+
* @returns the view the completion menu subscribes to.
|
|
30
|
+
*/
|
|
31
|
+
export declare function watchCommands(ctx: Context): CommandsView;
|
|
32
|
+
/**
|
|
33
|
+
* Whether one command line is a syntactically valid slash command.
|
|
34
|
+
* @param line - the complete candidate line.
|
|
35
|
+
* @returns true when the line parses as `/name` or `/name input`.
|
|
36
|
+
*/
|
|
37
|
+
export declare function isSlashLine(line: string): boolean;
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,20 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @deepseek-ai/dsh-
|
|
2
|
+
* @deepseek-ai/dsh-code — the interactive terminal driver. The bundle patch
|
|
3
3
|
* rides over dsh-base without Host, HTTP, or browser plugins; this runner
|
|
4
|
-
* creates one Agent through the core registry, mounts the Ink
|
|
5
|
-
* blue, whale wordmark), folds submitted prompts into the same
|
|
6
|
-
* session,
|
|
7
|
-
* and
|
|
4
|
+
* creates (or resumes) one Agent through the core registry, mounts the Ink
|
|
5
|
+
* app (DeepSeek blue, whale wordmark), folds submitted prompts into the same
|
|
6
|
+
* durable session, answers approval asks with a y/n bar, dispatches slash
|
|
7
|
+
* commands through the shared registry, and on quit flushes and requests
|
|
8
|
+
* process exit.
|
|
8
9
|
*
|
|
9
|
-
* @module @deepseek-ai/dsh-
|
|
10
|
+
* @module @deepseek-ai/dsh-code
|
|
10
11
|
*/
|
|
11
12
|
import type { Context } from '@deepseek-ai/cordis';
|
|
13
|
+
import z from '@deepseek-ai/schemastery';
|
|
12
14
|
/** Stable Cordis plugin name. */
|
|
13
15
|
export declare const name = "tui-runner";
|
|
14
16
|
/** Core services required before the interactive session can start. */
|
|
15
17
|
export declare const inject: string[];
|
|
18
|
+
/** Plugin config: the startup resolved from this app's injected provider service. */
|
|
19
|
+
export interface Config {
|
|
20
|
+
/** How this invocation obtains its session identity (validated loosely; narrowed in {@link apply}). */
|
|
21
|
+
startup: {
|
|
22
|
+
kind: string;
|
|
23
|
+
sessionId?: string;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export declare const Config: z<Config>;
|
|
16
27
|
/**
|
|
17
28
|
* Mount the interactive terminal driver.
|
|
18
29
|
* @param ctx - plugin context carrying core services and the launcher-provided exit request.
|
|
30
|
+
* @param config - validated startup config resolved from the tuiStartup provider.
|
|
19
31
|
*/
|
|
20
|
-
export declare function apply(ctx: Context): void;
|
|
32
|
+
export declare function apply(ctx: Context, config: Config): void;
|
package/lib/types/invariant.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Package-owned invariant companion for
|
|
3
|
-
* @module
|
|
2
|
+
* Package-owned invariant companion for `dsh-code`.
|
|
3
|
+
* @module dsh-code/invariant
|
|
4
4
|
*/
|
|
5
5
|
import type { Context } from '@deepseek-ai/cordis';
|
|
6
6
|
/** Cordis companion plugin name. */
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model directory for the `/model` panel: the in-process equivalent of the
|
|
3
|
+
* web host's model catalog (`buildModelCatalog`), reading the advisory
|
|
4
|
+
* `ctx.llm` registry directly. Catalog membership is advisory — a route
|
|
5
|
+
* serving a model it stopped advertising stays usable — so selection never
|
|
6
|
+
* fails on catalog absence alone.
|
|
7
|
+
*
|
|
8
|
+
* @module @deepseek-ai/dsh-tui/models
|
|
9
|
+
*/
|
|
10
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
11
|
+
/** One selectable row in the `/model` panel. */
|
|
12
|
+
export interface ModelRow {
|
|
13
|
+
/** Registered provider route. */
|
|
14
|
+
provider: string;
|
|
15
|
+
/** Display name of the provider route. */
|
|
16
|
+
providerName: string;
|
|
17
|
+
/** Provider-owned model id. */
|
|
18
|
+
model: string;
|
|
19
|
+
/** Human-readable model name. */
|
|
20
|
+
modelName: string;
|
|
21
|
+
}
|
|
22
|
+
/** The resolved directory: rows plus per-provider discovery failures. */
|
|
23
|
+
export interface ModelDirectory {
|
|
24
|
+
/** Advisory rows, provider-major in registry order. */
|
|
25
|
+
rows: readonly ModelRow[];
|
|
26
|
+
/** Provider ids whose model listing failed; those providers contribute no rows. */
|
|
27
|
+
failures: readonly string[];
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Load the selectable model directory from the live `ctx.llm` registry.
|
|
31
|
+
* Providers are listed synchronously; each provider's models are discovered
|
|
32
|
+
* with a bounded parallel fan-out whose failures degrade to that provider
|
|
33
|
+
* contributing no rows (mirrors the web catalog's per-provider failures).
|
|
34
|
+
* @param ctx - context carrying the `llm` service.
|
|
35
|
+
* @returns the resolved directory; empty rows when `llm` is unavailable.
|
|
36
|
+
*/
|
|
37
|
+
export declare function loadModelDirectory(ctx: Context): Promise<ModelDirectory>;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module @deepseek-ai/dsh-tui/render/projection
|
|
8
8
|
*/
|
|
9
|
-
import type { SessionEvent } from '@deepseek-ai/dsh-session';
|
|
9
|
+
import type { SessionEvent, TodoItem } from '@deepseek-ai/dsh-session';
|
|
10
10
|
/** One user prompt line. */
|
|
11
11
|
export interface UserEntry {
|
|
12
12
|
kind: 'user';
|
|
@@ -33,6 +33,20 @@ export interface ToolEntry {
|
|
|
33
33
|
/** Bounded first text block of the result, empty until it lands. */
|
|
34
34
|
summary: string;
|
|
35
35
|
}
|
|
36
|
+
/** One slash-command execution dispatched through `ctx.commands`. */
|
|
37
|
+
export interface CommandEntry {
|
|
38
|
+
kind: 'command';
|
|
39
|
+
/** Pairing id shared with the matching `command/done`. */
|
|
40
|
+
commandId: string;
|
|
41
|
+
/** Lowercase command name without the leading slash. */
|
|
42
|
+
name: string;
|
|
43
|
+
/** Verbatim text following the command name. */
|
|
44
|
+
args: string;
|
|
45
|
+
/** Execution state; `running` until the paired lifecycle event lands. */
|
|
46
|
+
state: 'running' | 'done' | 'error';
|
|
47
|
+
/** Handler outcome text, empty until it lands. */
|
|
48
|
+
summary: string;
|
|
49
|
+
}
|
|
36
50
|
/** One turn-level failure surfaced from `turn/end`. */
|
|
37
51
|
export interface ErrorEntry {
|
|
38
52
|
kind: 'error';
|
|
@@ -40,7 +54,7 @@ export interface ErrorEntry {
|
|
|
40
54
|
text: string;
|
|
41
55
|
}
|
|
42
56
|
/** Ordered transcript items the renderer draws. */
|
|
43
|
-
export type TranscriptEntry = UserEntry | AssistantEntry | ToolEntry | ErrorEntry;
|
|
57
|
+
export type TranscriptEntry = UserEntry | AssistantEntry | ToolEntry | CommandEntry | ErrorEntry;
|
|
44
58
|
/** Cumulative token accounting folded from `assistant/message` usage reports. */
|
|
45
59
|
export interface UsageTotals {
|
|
46
60
|
/** Prompt-side billed tokens: `inputTokens` plus both cache buckets. */
|
|
@@ -70,11 +84,18 @@ export interface TranscriptView {
|
|
|
70
84
|
/** Text accumulated from `assistant/chunk` deltas since the last flush. */
|
|
71
85
|
streaming: string;
|
|
72
86
|
/** Latest whole-list todo snapshot from `todo/write`, empty when none. */
|
|
73
|
-
todos:
|
|
87
|
+
todos: readonly TodoItem[];
|
|
74
88
|
/** True while a durable turn is open (`turn/start` … `turn/end`). */
|
|
75
89
|
busy: boolean;
|
|
76
90
|
/** Figures the status line renders. */
|
|
77
91
|
stats: TranscriptStats;
|
|
92
|
+
/**
|
|
93
|
+
* The `provider/model` pair of the last `request/header` snapshot — the
|
|
94
|
+
* session's own model record, which a resumed TUI prefers over the
|
|
95
|
+
* deployment default (mirrors the web host's resume selection order).
|
|
96
|
+
* Empty before the session's first request.
|
|
97
|
+
*/
|
|
98
|
+
model: string;
|
|
78
99
|
/**
|
|
79
100
|
* Fold-internal timing anchors, never rendered: open step and tool-call
|
|
80
101
|
* start timestamps the next `assistant/message` / `tool/result` resolves
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Display-boundary sanitization for externally sourced text (model output,
|
|
3
|
+
* tool payloads, skill descriptions). Control characters — including ANSI
|
|
4
|
+
* CSI/OSC escape sequences — would otherwise pass through Ink into the
|
|
5
|
+
* terminal, letting output rewrite the screen or inject prompts. Newlines
|
|
6
|
+
* and tabs survive; everything else in C0/C1 plus DEL becomes a visible
|
|
7
|
+
* `\xNN` escape.
|
|
8
|
+
*
|
|
9
|
+
* @module @deepseek-ai/dsh-code/render/text
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Escape control characters so externally sourced text cannot drive the
|
|
13
|
+
* terminal.
|
|
14
|
+
* @param text - raw text from a session event, tool payload, or catalog.
|
|
15
|
+
* @returns text with every control character (except `\n`, `\t`) rendered
|
|
16
|
+
* as a literal `\xNN` escape.
|
|
17
|
+
*/
|
|
18
|
+
export declare function displayText(text: string): string;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User-invocable skill watch for the `/` completion menu: the in-process
|
|
3
|
+
* equivalent of the web ui-skill trigger source. Skills are NOT commands —
|
|
4
|
+
* picking one lands the literal `/name ` text in the input, and submitting
|
|
5
|
+
* it as a normal prompt lets the host's tool-skill pre-step inject the body
|
|
6
|
+
* (the only entry point for model-disabled skills). Command descriptors win
|
|
7
|
+
* on a name collision; see the runner's dispatch.
|
|
8
|
+
*
|
|
9
|
+
* @module @deepseek-ai/dsh-code/skills
|
|
10
|
+
*/
|
|
11
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
12
|
+
import type { Agent } from '@deepseek-ai/dsh-agent';
|
|
13
|
+
/** One completion-menu row derived from a user-invocable skill. */
|
|
14
|
+
export interface SkillRow {
|
|
15
|
+
/** Skill name; the literal `/name` text is what a pick lands. */
|
|
16
|
+
name: string;
|
|
17
|
+
/** Human-readable description (suffixed when model-invocation is off). */
|
|
18
|
+
description: string;
|
|
19
|
+
/** Whether the model may also invoke this skill by name. */
|
|
20
|
+
modelInvocable: boolean;
|
|
21
|
+
}
|
|
22
|
+
/** The skill-catalog snapshot the completion menu subscribes to. */
|
|
23
|
+
export interface SkillsView {
|
|
24
|
+
/** Name-sorted user-invocable rows; empty until the first load lands. */
|
|
25
|
+
readonly rows: readonly SkillRow[];
|
|
26
|
+
/** Subscribe to catalog changes; returns the unsubscribe function. */
|
|
27
|
+
subscribe(listener: () => void): () => void;
|
|
28
|
+
/** Retarget the agent whose workspace the catalog is read for. */
|
|
29
|
+
setAgent(agent: Agent): void;
|
|
30
|
+
}
|
|
31
|
+
/** Internal shape shared by {@link watchSkills} and its test doubles. */
|
|
32
|
+
interface SkillsWatch extends SkillsView {
|
|
33
|
+
setAgent(agent: Agent): void;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Watch the user-invocable skill catalog for one agent's workspace. The first
|
|
37
|
+
* load starts when the owning agent is known (`setAgent`); `skills/change`
|
|
38
|
+
* and agent retargets re-read. Read failures keep the last good rows (the
|
|
39
|
+
* next change notification is the retry surface) — a missing `skills`
|
|
40
|
+
* service leaves the view permanently empty.
|
|
41
|
+
* @param ctx - context carrying the `skills` service (optional).
|
|
42
|
+
* @returns the view the completion menu subscribes to.
|
|
43
|
+
*/
|
|
44
|
+
export declare function watchSkills(ctx: Context): SkillsWatch;
|
|
45
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The interactive terminal app's command-line provider: parses `--resume`,
|
|
3
|
+
* `--continue`, `--session`, and `--help`, then publishes
|
|
4
|
+
* {@link TUI_STARTUP_SERVICE} for the runner to consume lazily. Follows the
|
|
5
|
+
* headless bundle's startup shape (a commander action publishing a service
|
|
6
|
+
* through {@link parseCmdline}).
|
|
7
|
+
*
|
|
8
|
+
* Semantics:
|
|
9
|
+
* - `--resume <id|prefix>` — continue the persisted session whose id or unique
|
|
10
|
+
* id-prefix matches; the TUI replays its transcript and appends to the same
|
|
11
|
+
* durable log.
|
|
12
|
+
* - `--continue` / `-c` — resume the most recently modified persisted session
|
|
13
|
+
* whose project directory matches the current working directory.
|
|
14
|
+
* - `--session <id>` — create a new session under an explicit identity (the
|
|
15
|
+
* id must not exist yet).
|
|
16
|
+
* - no flags — a fresh session with a minted id.
|
|
17
|
+
*
|
|
18
|
+
* @module @deepseek-ai/dsh-tui/startup
|
|
19
|
+
*/
|
|
20
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
21
|
+
/** Stable Cordis plugin name. */
|
|
22
|
+
export declare const name = "tui-startup";
|
|
23
|
+
/** Services required before the invocation can be resolved. */
|
|
24
|
+
export declare const inject: string[];
|
|
25
|
+
/** Service provided by this plugin and injected by the terminal runner. */
|
|
26
|
+
export declare const TUI_STARTUP_SERVICE = "tuiStartup";
|
|
27
|
+
/** How the runner obtains its session identity. */
|
|
28
|
+
export type TuiStartup = {
|
|
29
|
+
readonly kind: 'fresh';
|
|
30
|
+
} | {
|
|
31
|
+
readonly kind: 'named';
|
|
32
|
+
readonly sessionId: string;
|
|
33
|
+
} | {
|
|
34
|
+
readonly kind: 'resume';
|
|
35
|
+
readonly sessionId: string;
|
|
36
|
+
} | {
|
|
37
|
+
readonly kind: 'latest';
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Parse the invocation and publish the startup service. Mutual exclusions are
|
|
41
|
+
* usage errors rejected from the action before anything is provided.
|
|
42
|
+
* @param ctx - plugin context carrying the command line and exit request.
|
|
43
|
+
*/
|
|
44
|
+
export declare function apply(ctx: Context): void;
|
package/lib/types/store.d.ts
CHANGED
|
@@ -18,7 +18,13 @@ export interface TranscriptStore {
|
|
|
18
18
|
apply(event: SessionEvent): void;
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
|
-
* Create one transcript store.
|
|
21
|
+
* Create one transcript store, optionally seeded with replayed history. The
|
|
22
|
+
* seed folds synchronously BEFORE the first render, so a resumed session
|
|
23
|
+
* paints its full transcript on mount (no live `session/event` fires for
|
|
24
|
+
* constructor seeds — the store's `session/event` feed only carries new
|
|
25
|
+
* appends).
|
|
26
|
+
* @param replay - persisted events in `seq` order (e.g. a resumed session's
|
|
27
|
+
* constructor seed); folded once and never re-notified.
|
|
22
28
|
* @returns the store the runner feeds and the renderer subscribes to.
|
|
23
29
|
*/
|
|
24
|
-
export declare function createTranscriptStore(): TranscriptStore;
|
|
30
|
+
export declare function createTranscriptStore(replay?: readonly SessionEvent[]): TranscriptStore;
|
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.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.mjs",
|
|
7
7
|
"types": "lib/types/index.d.ts",
|
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
"types": "./lib/types/index.d.ts",
|
|
11
11
|
"default": "./lib/index.mjs"
|
|
12
12
|
},
|
|
13
|
+
"./startup": {
|
|
14
|
+
"types": "./lib/types/startup.d.ts",
|
|
15
|
+
"default": "./lib/startup.mjs"
|
|
16
|
+
},
|
|
13
17
|
"./invariant": {
|
|
14
18
|
"types": "./lib/types/invariant.d.ts",
|
|
15
19
|
"default": "./lib/invariant.mjs"
|
|
@@ -24,6 +28,16 @@
|
|
|
24
28
|
"src"
|
|
25
29
|
],
|
|
26
30
|
"license": "MIT",
|
|
31
|
+
"keywords": [
|
|
32
|
+
"deepseek",
|
|
33
|
+
"dsh",
|
|
34
|
+
"deepseek-harness",
|
|
35
|
+
"tui",
|
|
36
|
+
"terminal",
|
|
37
|
+
"claude-code",
|
|
38
|
+
"agent",
|
|
39
|
+
"ink"
|
|
40
|
+
],
|
|
27
41
|
"repository": {
|
|
28
42
|
"type": "git",
|
|
29
43
|
"url": "git+https://github.com/unlinearity/dsh-code.git"
|
|
@@ -37,13 +51,16 @@
|
|
|
37
51
|
"build": "tsdown && tsc -p tsconfig.json",
|
|
38
52
|
"test": "vitest run",
|
|
39
53
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
40
|
-
"gen:whale": "tsx scripts/gen-whale-glyph.ts"
|
|
54
|
+
"gen:whale": "tsx scripts/gen-whale-glyph.ts",
|
|
55
|
+
"prepublishOnly": "pnpm typecheck && pnpm test && pnpm build"
|
|
41
56
|
},
|
|
42
57
|
"engines": {
|
|
43
58
|
"node": "^22.19 || >=24"
|
|
44
59
|
},
|
|
45
60
|
"dependencies": {
|
|
61
|
+
"@deepseek-ai/schemastery": "^3.18.1",
|
|
46
62
|
"chalk": "^5.6.2",
|
|
63
|
+
"commander": "^14.0.2",
|
|
47
64
|
"ink": "^5.2.1",
|
|
48
65
|
"react": "^18.3.1"
|
|
49
66
|
},
|
|
@@ -54,11 +71,19 @@
|
|
|
54
71
|
"@deepseek-ai/dsh-agent-default-model": "^0.1.0-rc.6",
|
|
55
72
|
"@deepseek-ai/dsh-cmdline": "^0.1.0-rc.6",
|
|
56
73
|
"@deepseek-ai/dsh-code-runtime-worker-thread": "^0.1.0-rc.6",
|
|
74
|
+
"@deepseek-ai/dsh-commands": "^0.1.0-rc.6",
|
|
57
75
|
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
|
|
58
76
|
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
|
|
59
|
-
"@deepseek-ai/dsh-session": "^0.1.0-rc.6"
|
|
77
|
+
"@deepseek-ai/dsh-session": "^0.1.0-rc.6",
|
|
78
|
+
"@deepseek-ai/dsh-session-persistence": "^0.1.0-rc.6",
|
|
79
|
+
"@deepseek-ai/dsh-skill": "^0.1.0-rc.6",
|
|
80
|
+
"@deepseek-ai/dsh-user-approval": "^0.1.0-rc.6"
|
|
60
81
|
},
|
|
61
82
|
"devDependencies": {
|
|
83
|
+
"@deepseek-ai/dsh-commands": "^0.1.0-rc.6",
|
|
84
|
+
"@deepseek-ai/dsh-session-persistence": "^0.1.0-rc.6",
|
|
85
|
+
"@deepseek-ai/dsh-skill": "^0.1.0-rc.6",
|
|
86
|
+
"@deepseek-ai/dsh-user-approval": "^0.1.0-rc.6",
|
|
62
87
|
"@types/node": "^24.0.0",
|
|
63
88
|
"@types/react": "~18.3.1",
|
|
64
89
|
"tsdown": "^0.22.2",
|