codsh-cli 0.1.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.
@@ -0,0 +1,94 @@
1
+ /**
2
+ * The status readout shown with the prompt: what model and composition answer,
3
+ * where the session is, and how much context is left.
4
+ *
5
+ * Every figure is read from a durable projection or a logged fold rather than
6
+ * tracked here, so a resumed session reports the same numbers it ended with.
7
+ * @module codsh-cli/src/status
8
+ */
9
+ import type { ContextPressureProjection, TokenUsageProjection } from '@deepseek-ai/dsh-token-meter/client';
10
+ import type { Theme } from './theme.ts';
11
+ /** Everything one status line reports. */
12
+ export interface StatusFacts {
13
+ /** Model route answering this session. */
14
+ model: string;
15
+ /** Composed preset, absent when the deployment composes no roster. */
16
+ preset?: string | undefined;
17
+ /** Permission preset name, absent when none is composed. */
18
+ permission?: string | undefined;
19
+ /** Whether plan mode is holding. */
20
+ planMode: boolean;
21
+ /** Session workspace. */
22
+ cwd: string;
23
+ /** Checked-out branch, absent outside a repository. */
24
+ branch?: string | undefined;
25
+ /** Cumulative provider usage, absent before the first reported request. */
26
+ usage?: TokenUsageProjection | undefined;
27
+ /** Context occupancy, absent before the first reported request. */
28
+ context?: ContextPressureProjection | undefined;
29
+ }
30
+ /**
31
+ * Abbreviate a token count the way a status line wants it: exact while small,
32
+ * one decimal at thousands, whole at millions.
33
+ * @param tokens - the count to render.
34
+ * @returns the abbreviated figure.
35
+ */
36
+ export declare function formatTokens(tokens: number): string;
37
+ /**
38
+ * Total tokens a session has spent across every bucket.
39
+ *
40
+ * The four buckets are disjoint by contract — reasoning already sits inside
41
+ * output — so a plain sum is the session total.
42
+ * @param usage - cumulative usage, or undefined before any request.
43
+ * @returns the total, or undefined when nothing is recorded.
44
+ */
45
+ export declare function totalTokens(usage: TokenUsageProjection | undefined): number | undefined;
46
+ /**
47
+ * Percentage of the context window still free for the next request.
48
+ *
49
+ * `projectedTokens` is what the NEXT prompt would cost, which is the figure a
50
+ * person deciding whether to keep going needs; it also moves the instant a
51
+ * compaction shadows a span, where the raw sample cannot.
52
+ * @param context - the occupancy projection.
53
+ * @returns whole percent remaining, or undefined without both figures.
54
+ */
55
+ export declare function contextLeftPercent(context: ContextPressureProjection | undefined): number | undefined;
56
+ /**
57
+ * Shorten a path for display, collapsing the home directory to `~`.
58
+ * @param path - the absolute path.
59
+ * @param home - the home directory to collapse; defaults to the real one.
60
+ * @returns the display path.
61
+ */
62
+ export declare function displayPath(path: string, home?: string): string;
63
+ /**
64
+ * Read the checked-out branch by walking up to the repository's `HEAD`.
65
+ *
66
+ * Read rather than shelled out: `git` may be absent, slow, or blocked by the
67
+ * sandbox, and a status line must never be the reason a prompt stalls. A
68
+ * detached head reports no branch rather than a bare revision, which would read
69
+ * as a branch named after a hash.
70
+ * @param cwd - directory to start from.
71
+ * @returns the branch name, or undefined outside a repository or when detached.
72
+ */
73
+ export declare function gitBranch(cwd: string): Promise<string | undefined>;
74
+ /**
75
+ * Render the status line.
76
+ *
77
+ * Segments that have nothing to report are dropped rather than shown empty, so
78
+ * a fresh session reads as short rather than as broken.
79
+ * @param facts - what to report.
80
+ * @param theme - styling for the segments.
81
+ * @param columns - display columns available; a longer line is cut, never wrapped.
82
+ * @returns the line, unstyled when the theme is plain.
83
+ */
84
+ export declare function statusLine(facts: StatusFacts, theme: Theme, columns: number): string;
85
+ /**
86
+ * Render the fuller readout `/status` answers with.
87
+ *
88
+ * The status line is a glance; this is the place a person looks when the glance
89
+ * raised a question, so it names each usage bucket rather than one total.
90
+ * @param facts - what to report.
91
+ * @param session - the session identity, which `--resume` takes.
92
+ * @returns the report, one `label: value` per line.
93
+ */
94
+ export declare function statusReport(facts: StatusFacts, session: string): string;
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Assistant text as it arrives.
3
+ *
4
+ * Token-level display and Markdown rendering pull against each other: a line
5
+ * cannot be styled until it is complete, and a terminal cannot restyle a line
6
+ * that has scrolled. The split is what resolves it — the line being typed lives
7
+ * in the console's one rewritable region as raw text, and the moment it ends it
8
+ * is rendered and written permanently.
9
+ * @module codsh-cli/src/streaming
10
+ */
11
+ import type { Theme } from './theme.ts';
12
+ /** What one delta produced: finished lines, and the line still being typed. */
13
+ export interface StreamStep {
14
+ /** Rendered lines to append to the transcript. */
15
+ lines: string[];
16
+ /** The in-progress line for the live region, or undefined when none is open. */
17
+ live: string | undefined;
18
+ }
19
+ /** Accumulates assistant text deltas into rendered lines. */
20
+ export declare class TextStream {
21
+ private readonly theme;
22
+ /** Display columns, so the in-progress line never wraps the live region. */
23
+ private readonly columns;
24
+ /**
25
+ * Render lines as dim plain text instead of Markdown. Reasoning wants
26
+ * this: it is the model thinking aloud, not an answer to typeset.
27
+ */
28
+ private readonly plain;
29
+ private markdown;
30
+ private partial;
31
+ private seen;
32
+ constructor(theme: Theme,
33
+ /** Display columns, so the in-progress line never wraps the live region. */
34
+ columns: () => number,
35
+ /**
36
+ * Render lines as dim plain text instead of Markdown. Reasoning wants
37
+ * this: it is the model thinking aloud, not an answer to typeset.
38
+ */
39
+ plain?: boolean);
40
+ /** Whether this message has produced any text yet. */
41
+ get streamed(): boolean;
42
+ /**
43
+ * Take one text delta.
44
+ * @param delta - the text fragment, which may contain any number of newlines.
45
+ * @returns the lines to append and the line still open.
46
+ */
47
+ push(delta: string): StreamStep;
48
+ /**
49
+ * Close the message, rendering whatever line was still open.
50
+ *
51
+ * Called when the model finishes and when a turn is cancelled mid-line: the
52
+ * text already shown has to land in the transcript either way, or the live
53
+ * region would take it away again.
54
+ * @returns the remaining lines to append.
55
+ */
56
+ flush(): string[];
57
+ /** Render one complete line in this stream's mode. */
58
+ private renderLine;
59
+ /**
60
+ * The in-progress line as the live region should show it.
61
+ *
62
+ * Raw rather than rendered: it is not a line yet, and inside a fenced block it
63
+ * is code that Markdown must not touch. Truncated because the live region is
64
+ * one row — a wrapped live line cannot be erased by a single carriage return.
65
+ * @returns the text, or undefined when no line is open.
66
+ */
67
+ private liveText;
68
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Terminal styling and display metrics: SGR sequences that degrade to plain
3
+ * text off a TTY, and the display-column width a rendered string occupies.
4
+ * @module codsh-cli/src/theme
5
+ */
6
+ /** Style roles the renderer asks for, resolved to SGR codes by {@link createTheme}. */
7
+ export interface Theme {
8
+ /** Whether this theme emits SGR sequences at all. */
9
+ readonly colored: boolean;
10
+ dim(text: string): string;
11
+ bold(text: string): string;
12
+ /** Failures, denied approvals, and removed diff lines. */
13
+ error(text: string): string;
14
+ /** Completed work and added diff lines. */
15
+ success(text: string): string;
16
+ /** Pending state and approval prompts. */
17
+ pending(text: string): string;
18
+ /** Tool names and card titles. */
19
+ tool(text: string): string;
20
+ /** File paths and locations. */
21
+ path(text: string): string;
22
+ /** The user's own echoed input. */
23
+ user(text: string): string;
24
+ /** Roles used inside a fenced code block. */
25
+ readonly syntax: SyntaxTheme;
26
+ }
27
+ /** Styling for the token classes a code block is coloured by. */
28
+ export interface SyntaxTheme {
29
+ keyword(text: string): string;
30
+ string(text: string): string;
31
+ number(text: string): string;
32
+ comment(text: string): string;
33
+ }
34
+ /**
35
+ * Build the theme for one surface.
36
+ *
37
+ * Color is suppressed off a TTY and whenever `NO_COLOR` is set to any value,
38
+ * following the `no-color.org` convention: a redirected transcript stays
39
+ * greppable, and a pipe never receives sequences a reader would have to strip.
40
+ *
41
+ * Secondary text uses a palette gray on a 256-color terminal rather than the
42
+ * `dim` attribute: several terminals render `dim` at full brightness, and a
43
+ * hierarchy nobody can see is no hierarchy — the placeholder, the menu details,
44
+ * and the status row must sit visibly behind what the person typed.
45
+ * @param isTty - whether the output stream is a terminal.
46
+ * @param env - the environment to read `NO_COLOR` and the color depth from.
47
+ * @returns the styling functions for this surface.
48
+ */
49
+ export declare function createTheme(isTty: boolean, env: Record<string, string | undefined>): Theme;
50
+ /**
51
+ * Display columns a string occupies once printed, ignoring styling sequences.
52
+ *
53
+ * Combining marks are counted as zero and East Asian Wide/Fullwidth code
54
+ * points as two, which is what a terminal's own cursor arithmetic does. This
55
+ * covers the alignment and wrapping this surface needs; it is not a complete
56
+ * grapheme segmenter, so a ZWJ emoji sequence still counts each joined code
57
+ * point ({@link ../README.md | Known Limitations}).
58
+ * @param text - the string to measure, possibly carrying SGR sequences.
59
+ * @returns the number of display columns.
60
+ */
61
+ export declare function displayWidth(text: string): number;
62
+ /**
63
+ * Shorten a string to at most `columns` display columns, marking the cut with
64
+ * an ellipsis when anything was dropped.
65
+ *
66
+ * Styling survives: sequences cost no columns and travel with the text they
67
+ * style, and a cut that kept any styling closes it with a reset before the
68
+ * ellipsis so nothing leaks onto the next row. A string that already fits is
69
+ * returned exactly as it came — a fit is not a licence to restyle it.
70
+ * @param text - the string to shorten, possibly carrying SGR sequences.
71
+ * @param columns - the display-column budget; a budget under 2 yields the empty string.
72
+ * @returns the string, unchanged when it already fits.
73
+ */
74
+ export declare function truncate(text: string, columns: number): string;
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Session log to terminal lines. One appended {@link SessionEvent} renders to
3
+ * zero or more finished lines; the surface never rewrites a line it has
4
+ * printed, so the transcript scrolls like a shell history.
5
+ *
6
+ * Tool cards come from the registered presenters rather than from tool names:
7
+ * a tool declares its own render intent, and this module switches on the
8
+ * resulting `card` tag.
9
+ * @module codsh-cli/src/transcript
10
+ */
11
+ import type { SessionEvent } from '@deepseek-ai/dsh-session';
12
+ import type { ToolCallView, ToolResult, ToolResultView } from '@deepseek-ai/dsh-tools';
13
+ import type { Theme } from './theme.ts';
14
+ /** The registered presenters, resolved against the agent's scope by the caller. */
15
+ export interface ToolPresenters {
16
+ /**
17
+ * Render intent for a pending call.
18
+ * @param name - the tool the model called.
19
+ * @param args - the parsed call arguments.
20
+ * @returns the declared view, or undefined for the generic fallback.
21
+ */
22
+ call(name: string, args: unknown): ToolCallView | undefined;
23
+ /**
24
+ * Render intent for a completed call.
25
+ * @param name - the tool the model called.
26
+ * @param args - the parsed call arguments.
27
+ * @param result - the completed outcome.
28
+ * @returns the declared view, or undefined for the generic fallback.
29
+ */
30
+ result(name: string, args: unknown, result: ToolResult): ToolResultView | undefined;
31
+ }
32
+ /** What the renderer needs to know about the surface it writes to. */
33
+ export interface TranscriptOptions {
34
+ theme: Theme;
35
+ /** Display columns available for one line. */
36
+ columns: number;
37
+ /** Session workspace, stripped from absolute paths so cards stay short. */
38
+ cwd: string;
39
+ }
40
+ /** Renders one session's appended events as terminal lines. */
41
+ export declare class Transcript {
42
+ private readonly options;
43
+ private readonly presenters;
44
+ private readonly calls;
45
+ /** The most recent result whose body the cap clipped, kept in full. */
46
+ private clipped;
47
+ constructor(options: TranscriptOptions, presenters: ToolPresenters);
48
+ /**
49
+ * Shorten an absolute path inside the workspace to a workspace-relative one.
50
+ * @param path - the model-facing path a card carries.
51
+ * @returns the display path.
52
+ */
53
+ private relative;
54
+ /**
55
+ * Shorten every workspace path a presenter embedded in free text.
56
+ *
57
+ * A title is prose the tool composed (`Write /abs/path`), so the path inside
58
+ * it needs the same shortening as a structured `locations` entry.
59
+ * @param text - the presenter-supplied line.
60
+ * @returns the line with workspace-rooted paths made relative.
61
+ */
62
+ private relativizeIn;
63
+ /**
64
+ * Paths worth appending to a title that may already name them.
65
+ * @param title - the presenter's title, already relativized.
66
+ * @param paths - the relativized paths the card covers.
67
+ * @returns the paths the title does not mention, joined for display.
68
+ */
69
+ private extraPaths;
70
+ /**
71
+ * Render one appended event.
72
+ * @param event - the event exactly as recorded.
73
+ * @returns the lines to append to the transcript, empty when the event shows nothing.
74
+ */
75
+ render(event: SessionEvent): string[];
76
+ /**
77
+ * Render a pending call as its declared card.
78
+ * @param callId - correlation id, remembered until the result pairs with it.
79
+ * @param name - the tool the model called.
80
+ * @param rawArguments - the unparsed arguments JSON the model produced.
81
+ * @returns the pending card's lines.
82
+ */
83
+ private renderCall;
84
+ /**
85
+ * Render a completed call, pairing it with the call this transcript recorded.
86
+ * @param data - the `tool/result` payload.
87
+ * @returns the completed card's lines.
88
+ */
89
+ private renderResult;
90
+ /**
91
+ * The last clipped result, rendered without its cap.
92
+ *
93
+ * Ctrl-O's answer. The full body is kept from the render itself because a
94
+ * tool's own output limits are upstream of the log — this is everything the
95
+ * model saw, which is everything recoverable.
96
+ * @returns the header and full body, or undefined when nothing was clipped.
97
+ */
98
+ expandLast(): string[] | undefined;
99
+ /**
100
+ * Render one completed call's status suffix and body from its declared view.
101
+ * @param view - the result view, absent when no presenter answered.
102
+ * @param block - the model-facing result block, used by the generic fallback.
103
+ * @returns the suffix, the (possibly capped) body, and — when the cap dropped
104
+ * lines, or a bodiless card withheld content — the full body for Ctrl-O.
105
+ */
106
+ private outcome;
107
+ /**
108
+ * Flatten a result's content blocks to displayable text.
109
+ * @param content - the result content blocks.
110
+ * @returns the joined text.
111
+ */
112
+ private resultText;
113
+ /**
114
+ * Ask a call presenter for its view, absorbing a throwing presenter.
115
+ * @param name - the tool the model called.
116
+ * @param args - the parsed call arguments.
117
+ * @returns the view, or undefined to fall back to the generic line.
118
+ */
119
+ private safeCall;
120
+ /**
121
+ * Ask a result presenter for its view, absorbing a throwing presenter.
122
+ * @param pending - the recorded call this result pairs with.
123
+ * @param content - the model-facing result content.
124
+ * @param isError - whether the executor reported a failure.
125
+ * @param meta - the tool's private presentation payload, when it attached one.
126
+ * @returns the view, or undefined to fall back to the generic card.
127
+ */
128
+ private safeResult;
129
+ }
package/package.json ADDED
@@ -0,0 +1,149 @@
1
+ {
2
+ "name": "codsh-cli",
3
+ "description": "A Claude Code-style coding agent for the terminal, composed on the DeepSeek Harness (dsh): interactive TTY surface, plan mode, approvals, custom commands, and session management over the dsh plugin runtime",
4
+ "version": "0.1.0",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "bin": {
8
+ "codsh": "./bin/codsh.mjs"
9
+ },
10
+ "main": "lib/index.js",
11
+ "types": "lib/types/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./lib/types/index.d.ts",
15
+ "default": "./lib/index.js"
16
+ },
17
+ "./startup": {
18
+ "types": "./lib/types/startup.d.ts",
19
+ "default": "./lib/startup.js"
20
+ },
21
+ "./invariant": {
22
+ "types": "./lib/types/invariant.d.ts",
23
+ "default": "./lib/invariant.js"
24
+ },
25
+ "./cordis.patch.yml": "./cordis.patch.yml",
26
+ "./package.json": "./package.json",
27
+ "./agent-presets/*": "./agent-presets/*"
28
+ },
29
+ "files": [
30
+ "lib/index.js",
31
+ "lib/invariant.js",
32
+ "lib/startup.js",
33
+ "bin",
34
+ "cordis.patch.yml",
35
+ "agent-presets",
36
+ "lib/types/**/*.d.ts"
37
+ ],
38
+ "dsh": {
39
+ "bundle": {
40
+ "patch": "./cordis.patch.yml"
41
+ }
42
+ },
43
+ "dependencies": {
44
+ "@deepseek-ai/dsh": "^0.1.0-rc.7",
45
+ "@deepseek-ai/dsh-agent-instructions": "^0.1.0-rc.7",
46
+ "@deepseek-ai/dsh-agent-presets": "^0.1.0-rc.7",
47
+ "@deepseek-ai/dsh-cmdline": "^0.1.0-rc.7",
48
+ "@deepseek-ai/dsh-code-runtime-worker-thread": "^0.1.0-rc.7",
49
+ "@deepseek-ai/dsh-command-compact": "^0.1.0-rc.7",
50
+ "@deepseek-ai/dsh-compaction-basic": "^0.1.0-rc.7",
51
+ "@deepseek-ai/dsh-compaction-tool-result-pruner": "^0.1.0-rc.7",
52
+ "@deepseek-ai/dsh-lsp": "^0.1.0-rc.7",
53
+ "@deepseek-ai/dsh-lsp-stdio": "^0.1.0-rc.7",
54
+ "@deepseek-ai/dsh-persona": "^0.1.0-rc.7",
55
+ "@deepseek-ai/dsh-plan-mode": "^0.1.0-rc.7",
56
+ "@deepseek-ai/dsh-skill-filesystem": "^0.1.0-rc.7",
57
+ "@deepseek-ai/dsh-terminal": "^0.1.0-rc.7",
58
+ "@deepseek-ai/dsh-terminal-bash": "^0.1.0-rc.7",
59
+ "@deepseek-ai/dsh-tool-ask-user": "^0.1.0-rc.7",
60
+ "@deepseek-ai/dsh-tool-bash": "^0.1.0-rc.7",
61
+ "@deepseek-ai/dsh-tool-fs": "^0.1.0-rc.7",
62
+ "@deepseek-ai/dsh-tool-fs-search": "^0.1.0-rc.7",
63
+ "@deepseek-ai/dsh-tool-goal": "^0.1.0-rc.7",
64
+ "@deepseek-ai/dsh-tool-jobs": "^0.1.0-rc.7",
65
+ "@deepseek-ai/dsh-tool-lsp": "^0.1.0-rc.7",
66
+ "@deepseek-ai/dsh-tool-pwsh": "^0.1.0-rc.7",
67
+ "@deepseek-ai/dsh-tool-ralph": "^0.1.0-rc.7",
68
+ "@deepseek-ai/dsh-tool-skill": "^0.1.0-rc.7",
69
+ "@deepseek-ai/dsh-tool-subagent": "^0.1.0-rc.7",
70
+ "@deepseek-ai/dsh-tool-subagent-control": "^0.1.0-rc.7",
71
+ "@deepseek-ai/dsh-tool-terminal": "^0.1.0-rc.7",
72
+ "@deepseek-ai/dsh-tool-todo": "^0.1.0-rc.7",
73
+ "@deepseek-ai/dsh-tool-web": "^0.1.0-rc.7",
74
+ "@deepseek-ai/dsh-tool-workflow": "^0.1.0-rc.7",
75
+ "@deepseek-ai/dsh-workflow-worker-thread": "^0.1.0-rc.7",
76
+ "@deepseek-ai/schemastery": "^3.18.1",
77
+ "commander": "^15.0.0",
78
+ "diff": "^9.0.0"
79
+ },
80
+ "peerDependencies": {
81
+ "@deepseek-ai/cordis": "^4.0.1",
82
+ "@deepseek-ai/cordis-plugin-loader": "^1.0.2",
83
+ "@deepseek-ai/dsh-agent": "^0.1.0-rc.7",
84
+ "@deepseek-ai/dsh-agent-default-model": "^0.1.0-rc.7",
85
+ "@deepseek-ai/dsh-commands": "^0.1.0-rc.7",
86
+ "@deepseek-ai/dsh-home-paths": "^0.1.0-rc.7",
87
+ "@deepseek-ai/dsh-invariants": "^0.1.0-rc.7",
88
+ "@deepseek-ai/dsh-llm": "^0.1.0-rc.7",
89
+ "@deepseek-ai/dsh-permission-presets": "^0.1.0-rc.7",
90
+ "@deepseek-ai/dsh-session": "^0.1.0-rc.7",
91
+ "@deepseek-ai/dsh-session-projection": "^0.1.0-rc.7",
92
+ "@deepseek-ai/dsh-session-query": "^0.1.0-rc.7",
93
+ "@deepseek-ai/dsh-token-meter": "^0.1.0-rc.7",
94
+ "@deepseek-ai/dsh-tools": "^0.1.0-rc.7",
95
+ "@deepseek-ai/dsh-user-approval": "^0.1.0-rc.7",
96
+ "@deepseek-ai/dsh-user-questions": "^0.1.0-rc.7"
97
+ },
98
+ "devDependencies": {
99
+ "@changesets/cli": "^3.0.0",
100
+ "@deepseek-ai/cordis": "^4.0.1",
101
+ "@deepseek-ai/cordis-plugin-loader": "^1.0.2",
102
+ "@deepseek-ai/dsh-agent": "^0.1.0-rc.7",
103
+ "@deepseek-ai/dsh-agent-default-model": "^0.1.0-rc.7",
104
+ "@deepseek-ai/dsh-commands": "^0.1.0-rc.7",
105
+ "@deepseek-ai/dsh-home-paths": "^0.1.0-rc.7",
106
+ "@deepseek-ai/dsh-invariants": "^0.1.0-rc.7",
107
+ "@deepseek-ai/dsh-llm": "^0.1.0-rc.7",
108
+ "@deepseek-ai/dsh-permission-presets": "^0.1.0-rc.7",
109
+ "@deepseek-ai/dsh-session": "^0.1.0-rc.7",
110
+ "@deepseek-ai/dsh-session-projection": "^0.1.0-rc.7",
111
+ "@deepseek-ai/dsh-session-query": "^0.1.0-rc.7",
112
+ "@deepseek-ai/dsh-token-meter": "^0.1.0-rc.7",
113
+ "@deepseek-ai/dsh-tools": "^0.1.0-rc.7",
114
+ "@deepseek-ai/dsh-user-approval": "^0.1.0-rc.7",
115
+ "@deepseek-ai/dsh-user-questions": "^0.1.0-rc.7",
116
+ "@types/node": "^24.0.0",
117
+ "execa": "^9.0.0",
118
+ "tsdown": "^0.15.0",
119
+ "tsx": "^4.20.0",
120
+ "typescript": "^5.9.0",
121
+ "vitest": "^4.0.0"
122
+ },
123
+ "repository": {
124
+ "type": "git",
125
+ "url": "git+https://github.com/Blackman99/codsh.git"
126
+ },
127
+ "homepage": "https://github.com/Blackman99/codsh#readme",
128
+ "bugs": "https://github.com/Blackman99/codsh/issues",
129
+ "keywords": [
130
+ "cli",
131
+ "coding-agent",
132
+ "terminal",
133
+ "deepseek",
134
+ "dsh",
135
+ "ai",
136
+ "agent"
137
+ ],
138
+ "engines": {
139
+ "node": ">=22.19"
140
+ },
141
+ "scripts": {
142
+ "build": "tsdown && tsc -p tsconfig.build.json",
143
+ "typecheck": "tsc --noEmit",
144
+ "test": "vitest run",
145
+ "test:e2e": "pnpm run build && vitest run --config vitest.e2e.config.ts",
146
+ "dev": "node scripts/dev.mjs",
147
+ "release": "pnpm run build && changeset publish"
148
+ }
149
+ }