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/lib/invariant.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region src/invariant.ts
2
- const PACKAGE_NAME = "@deepseek-ai/dsh-tui";
2
+ const PACKAGE_NAME = "dsh-code";
3
3
  /** Cordis companion plugin name. */
4
4
  const name = "tui-invariant";
5
5
  /** Service required before the companion can register. */
@@ -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 };
@@ -1,23 +1,35 @@
1
1
  /**
2
2
  * The Ink terminal app: whale-and-wordmark header in DeepSeek blue, the live
3
- * transcript, the streaming line, local notices, and the input box. All state
4
- * arrives through the transcript store (derived from the durable session log)
5
- * plus local input state; the app owns no session mutation of its own.
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-tui/app
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
- /** `provider/model` selection serving this session. */
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
- /** Submit one human prompt; the runner folds it into the session. */
29
- onSubmit(text: string): void;
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
- onQuit(): void;
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({ store, model, cwd, branch, sessionId, onSubmit, onQuit }: AppProps): ReactElement;
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;
@@ -1,20 +1,32 @@
1
1
  /**
2
- * @deepseek-ai/dsh-tui — the interactive terminal driver. The bundle patch
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 app (DeepSeek
5
- * blue, whale wordmark), folds submitted prompts into the same durable
6
- * session, streams `session/event` into the transcript, and on quit flushes
7
- * and requests process exit.
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-tui
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;
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Package-owned invariant companion for `@deepseek-ai/dsh-tui`.
3
- * @module @deepseek-ai/dsh-tui/invariant
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: SessionEvent<'todo/write'>['data']['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;
@@ -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.1.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",