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.
- package/LICENSE +21 -0
- package/README.md +65 -0
- package/README.zh.md +65 -0
- package/agent-presets/code-cli/agent.cordis.yml +322 -0
- package/agent-presets/code-cli/preset.yml +3 -0
- package/bin/codsh.mjs +61 -0
- package/cordis.patch.yml +165 -0
- package/lib/index.js +4008 -0
- package/lib/invariant.js +23 -0
- package/lib/startup.js +64 -0
- package/lib/types/approval.d.ts +57 -0
- package/lib/types/banner.d.ts +30 -0
- package/lib/types/completion.d.ts +45 -0
- package/lib/types/console.d.ts +152 -0
- package/lib/types/custom-commands.d.ts +54 -0
- package/lib/types/editor.d.ts +186 -0
- package/lib/types/index.d.ts +48 -0
- package/lib/types/inputbox.d.ts +45 -0
- package/lib/types/invariant.d.ts +15 -0
- package/lib/types/keys.d.ts +103 -0
- package/lib/types/markdown.d.ts +73 -0
- package/lib/types/preset-install.d.ts +34 -0
- package/lib/types/prompt.d.ts +135 -0
- package/lib/types/questions.d.ts +65 -0
- package/lib/types/selector.d.ts +99 -0
- package/lib/types/spinner.d.ts +57 -0
- package/lib/types/startup.d.ts +31 -0
- package/lib/types/status.d.ts +94 -0
- package/lib/types/streaming.d.ts +68 -0
- package/lib/types/theme.d.ts +74 -0
- package/lib/types/transcript.d.ts +129 -0
- package/package.json +149 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 codsh contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# codsh
|
|
2
|
+
|
|
3
|
+
> npm: [`codsh-cli`](https://www.npmjs.com/package/codsh-cli) · command: `codsh`
|
|
4
|
+
|
|
5
|
+
English | [中文](README.zh.md)
|
|
6
|
+
|
|
7
|
+
A Claude Code-style coding agent for the terminal, composed on the [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (dsh) plugin runtime. codsh is a dsh *bundle*: it ships the interactive TTY surface and a coding agent preset, and everything underneath — the agent loop, tools, sessions, sandboxing, model adapters — is the released dsh packages from npm.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npm install -g codsh-cli
|
|
13
|
+
codsh
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The first run registers this package into a dsh `code` profile under `$DSH_HOME` (default `~/.dsh`) and installs the packaged `code-cli` agent preset; every later run boots straight into the prompt. The model key is read from `DEEPSEEK_API_KEY` (environment or `.env`).
|
|
17
|
+
|
|
18
|
+
`codsh` is exactly `dsh --profile code` — the wrapper only performs the one-time profile registration. Flags after `codsh` reach the app: `codsh --resume <session-id>`, `codsh --continue`, `codsh -p "one-shot task"`.
|
|
19
|
+
|
|
20
|
+
## What you get
|
|
21
|
+
|
|
22
|
+
- **An input box that owns the keyboard**: multi-line editing (Alt-Enter), history across sessions, completion for commands, arguments, and `@`-mentioned files (fuzzy, workspace-wide), opened as you type.
|
|
23
|
+
- **Streaming rendering**: Markdown with code highlighting and table layout, reasoning models' thinking dim under `✻ thinking`, tool calls as presenter-driven cards with diffs, and Ctrl-O to reprint the last clipped output in full.
|
|
24
|
+
- **Decisions as selections**: approvals, questions, `/model`, and `/resume` are arrow-key widgets; plan mode toggles on Shift-Tab and tints the box frame.
|
|
25
|
+
- **Session flow**: `/clear` starts fresh in place, `/resume` picks from recorded sessions with titles and ages, Escape twice recalls your previous message for editing, and `!cmd` runs in your shell with the outcome injected as model-visible context — no turn spent.
|
|
26
|
+
- **Canned prompts**: `/init` drafts an `AGENTS.md`; your own Markdown files under `$DSH_HOME/commands/` or `<workspace>/.dsh/commands/` become slash commands with `$ARGUMENTS` templating.
|
|
27
|
+
- Status line (model, preset, permissions, tokens, context left, branch), terminal-title updates, a bell when a decision waits, and a `--print` mode for scripts.
|
|
28
|
+
|
|
29
|
+
Off a TTY (pipes, scripts) the same surface degrades to a line reader: selections become typed answers, lists replace widgets, and nothing draws.
|
|
30
|
+
|
|
31
|
+
## Development
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
pnpm install
|
|
35
|
+
pnpm run dev # build → sync into .dev-home → boot; seconds per iteration
|
|
36
|
+
MOCK=markdown pnpm run dev # keyless, against the e2e mock model
|
|
37
|
+
pnpm run build # tsdown runtime bundles + tsc declarations into lib/
|
|
38
|
+
pnpm run typecheck
|
|
39
|
+
pnpm test # unit suites (pure modules: editor, markdown, transcript, …)
|
|
40
|
+
pnpm run test:e2e # packs this repo, registers it into a dsh profile, and
|
|
41
|
+
# drives the INSTALLED dsh binary through pipes and a real PTY
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`pnpm run dev` keeps a repo-local dsh home in `.dev-home`: the first run does a real profile install of the packed tree, and every later run just copies the fresh `lib/` over the profile's unpacked package — so edits reach the running surface in seconds. `MOCK=<write|bash|slow|markdown|reasoning|echo|tall>` swaps in the keyless e2e model for UI work without a key, arguments pass through (`pnpm run dev -- --resume <id>`), and `INSPECT=1` opens the Node inspector on the app process (`chrome://inspect` or a VS Code attach). Off-TTY logic is easiest to step through piped — `printf 'task\n/exit\n' | MOCK=echo pnpm run dev` — where raw mode and the repaint region are out of the picture; keep `console.*` out of debug prints on a TTY (they tear the managed region) and log to a file instead.
|
|
45
|
+
|
|
46
|
+
The e2e suites test the release artifact: `npm pack` output installed into a real profile, booted by the dsh launcher from npm, with a keyless mock model. What passes there is what a user installs.
|
|
47
|
+
|
|
48
|
+
### Debugging against dsh sources
|
|
49
|
+
|
|
50
|
+
Day to day, dsh is an ordinary npm dependency. To step into harness code, clone [deepseek-harness](https://github.com/deepseek-ai/deepseek-harness) beside this repo, build it (`pnpm install && pnpm run build`), then point the packages you are debugging at the checkout:
|
|
51
|
+
|
|
52
|
+
```jsonc
|
|
53
|
+
// package.json — remove again when done; keep npm as the default
|
|
54
|
+
"pnpm": {
|
|
55
|
+
"overrides": {
|
|
56
|
+
"@deepseek-ai/dsh-agent-loop": "link:../deepseek-harness/packages/core/agent-loop"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
and re-run `pnpm install`. Changes you want upstream go to the harness repo as ordinary PRs; this repository never forks it.
|
|
62
|
+
|
|
63
|
+
## License
|
|
64
|
+
|
|
65
|
+
MIT
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# codsh
|
|
2
|
+
|
|
3
|
+
> npm 包名:[`codsh-cli`](https://www.npmjs.com/package/codsh-cli) · 命令:`codsh`
|
|
4
|
+
|
|
5
|
+
[English](README.md) | 中文
|
|
6
|
+
|
|
7
|
+
面向终端的 Claude Code 风格编码 agent,组合在 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(dsh)插件运行时之上。codsh 是一个 dsh *bundle*:它携带交互式 TTY 界面与编码 agent preset,其下的一切——agent 循环、工具、会话、沙箱、模型适配器——都是 npm 上已发布的 dsh 包。
|
|
8
|
+
|
|
9
|
+
## 安装
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npm install -g codsh-cli
|
|
13
|
+
codsh
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
首次运行会把本包注册进 `$DSH_HOME`(默认 `~/.dsh`)下的 dsh `code` profile 并安装自带的 `code-cli` agent preset;之后每次运行直接进入提示符。模型密钥从 `DEEPSEEK_API_KEY` 读取(环境变量或 `.env`)。
|
|
17
|
+
|
|
18
|
+
`codsh` 严格等价于 `dsh --profile code`——包装器只做一次性的 profile 注册。`codsh` 之后的参数直达应用:`codsh --resume <会话 id>`、`codsh --continue`、`codsh -p "一次性任务"`。
|
|
19
|
+
|
|
20
|
+
## 你会得到什么
|
|
21
|
+
|
|
22
|
+
- **接管键盘的输入框**:多行编辑(Alt-Enter)、跨会话历史,命令、参数与 `@` 文件提及(全工作区模糊搜索)的补全随输入自动打开。
|
|
23
|
+
- **流式渲染**:Markdown 带代码高亮与表格排版,推理模型的思考在 `✻ thinking` 下暗色显示,工具调用按 presenter 驱动的卡片渲染(含 diff),Ctrl-O 完整重印最近被截断的输出。
|
|
24
|
+
- **决定用选择**:审批、提问、`/model` 与 `/resume` 都是方向键组件;Shift-Tab 切换 plan 模式并为框着色。
|
|
25
|
+
- **会话流**:`/clear` 原地开新会话,`/resume` 从带标题和时间的会话列表里选,连按两次 Escape 召回上一条消息编辑,`!cmd` 在你自己的 shell 里运行并把结果注入为模型可见上下文——不花回合。
|
|
26
|
+
- **固定 prompt**:`/init` 起草 `AGENTS.md`;`$DSH_HOME/commands/` 或 `<工作区>/.dsh/commands/` 下的 Markdown 文件即成为斜杠命令,支持 `$ARGUMENTS` 模板。
|
|
27
|
+
- 状态行(模型、preset、权限、token、剩余上下文、分支)、终端标题跟随、有决定等待时的铃声,以及面向脚本的 `--print` 模式。
|
|
28
|
+
|
|
29
|
+
非 TTY 环境(管道、脚本)下同一界面降级为行读取器:选择变为键入回答、组件变为列表、不绘制任何东西。
|
|
30
|
+
|
|
31
|
+
## 开发
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
pnpm install
|
|
35
|
+
pnpm run dev # build → 同步进 .dev-home → 启动;秒级迭代
|
|
36
|
+
MOCK=markdown pnpm run dev # 无 key,对着 e2e mock 模型
|
|
37
|
+
pnpm run build # tsdown 运行时 bundle + tsc 声明文件,输出到 lib/
|
|
38
|
+
pnpm run typecheck
|
|
39
|
+
pnpm test # 单测(纯函数模块:editor、markdown、transcript……)
|
|
40
|
+
pnpm run test:e2e # 打包本仓库、注册进 dsh profile,然后经管道与真实 PTY
|
|
41
|
+
# 驱动 npm 安装的 dsh 可执行文件
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`pnpm run dev` 在 `.dev-home` 维护一个仓库本地的 dsh home:首次运行对打包后的工作树做一次真实 profile 安装,之后每次只把新构建的 `lib/` 覆盖到 profile 里解包的本包上——改动秒级到达运行中的界面。`MOCK=<write|bash|slow|markdown|reasoning|echo|tall>` 换上 keyless e2e 模型、无 key 调 UI;参数透传(`pnpm run dev -- --resume <id>`);`INSPECT=1` 在应用进程上打开 Node inspector(`chrome://inspect` 或 VS Code attach)。逻辑问题优先用管道形态单步——`printf 'task\n/exit\n' | MOCK=echo pnpm run dev`——那里没有 raw mode 和重绘区域的干扰;在 TTY 上调试不要用 `console.*` 打点(会撕裂受管理区域),改写文件日志。
|
|
45
|
+
|
|
46
|
+
e2e 测试的是发布产物:`npm pack` 的输出装进真实 profile,由 npm 上的 dsh launcher 启动,配 keyless mock 模型。那里通过的就是用户装到的。
|
|
47
|
+
|
|
48
|
+
### 对着 dsh 源码调试
|
|
49
|
+
|
|
50
|
+
日常里 dsh 是普通的 npm 依赖。需要单步进 harness 代码时,把 [deepseek-harness](https://github.com/deepseek-ai/deepseek-harness) clone 到本仓库旁边并构建(`pnpm install && pnpm run build`),然后把要调试的包指向 checkout:
|
|
51
|
+
|
|
52
|
+
```jsonc
|
|
53
|
+
// package.json —— 调完删掉;npm 始终是默认
|
|
54
|
+
"pnpm": {
|
|
55
|
+
"overrides": {
|
|
56
|
+
"@deepseek-ai/dsh-agent-loop": "link:../deepseek-harness/packages/core/agent-loop"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
再执行 `pnpm install`。想进上游的改动以普通 PR 提交到 harness 仓库;本仓库绝不 fork 它。
|
|
62
|
+
|
|
63
|
+
## 许可
|
|
64
|
+
|
|
65
|
+
MIT
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
# The `code-cli` agent preset: the `standard` coding agent plus the capabilities a
|
|
2
|
+
# terminal surface needs — persistent terminal sessions, optional LSP code
|
|
3
|
+
# navigation, and web fetch.
|
|
4
|
+
#
|
|
5
|
+
# This file is an AGENT-PLANE composition. The roster mounts it ONCE under a
|
|
6
|
+
# standing scope; every session naming it joins by scope parentage, so the
|
|
7
|
+
# tools and prompt sections registered here cover each joined agent while a
|
|
8
|
+
# session's own state stays keyed per Session/Agent inside the plugins. The
|
|
9
|
+
# host composition (`base.cordis.yml` + the `dsh-coding-cli` bundle patch) keeps
|
|
10
|
+
# everything a preset must not own: the registries themselves, the sandbox and
|
|
11
|
+
# approval stack, persistence, and the model route.
|
|
12
|
+
#
|
|
13
|
+
# A service row here MUST sit inside a group carrying an `isolate` realm.
|
|
14
|
+
# Without one it publishes into the root realm, where it is process-global —
|
|
15
|
+
# another preset publishing the same name collides, and a host reader would
|
|
16
|
+
# resolve one preset's instance for every session; `dsh-agent-presets` rejects
|
|
17
|
+
# that at mount. `true` means an entry-local realm: this standing mount's own
|
|
18
|
+
# private instance, apart from every other preset's. (A shared label does NOT
|
|
19
|
+
# pool instances — `provide()` throws on the second registration under the
|
|
20
|
+
# same realm symbol; labels join REALMS, and are not what this file needs.)
|
|
21
|
+
#
|
|
22
|
+
# The `app:cli-surface` prompt section that orients the model to the terminal
|
|
23
|
+
# belongs to the `dsh-coding-cli` bundle, not here: it describes the surface
|
|
24
|
+
# that booted the process, and every preset a terminal session mounts needs it.
|
|
25
|
+
|
|
26
|
+
# ── identity ────────────────────────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
# The preset's own persona, shadowing the deployment default for this agent.
|
|
29
|
+
# `{{model}}` and `{{cwd}}` resolve from the agent's own route and workspace.
|
|
30
|
+
- id: persona
|
|
31
|
+
name: '@deepseek-ai/dsh-persona'
|
|
32
|
+
config:
|
|
33
|
+
text: >-
|
|
34
|
+
You are a coding agent powered by the {{model}} model. Your working directory is {{cwd}}.
|
|
35
|
+
|
|
36
|
+
- id: agent-instructions
|
|
37
|
+
name: '@deepseek-ai/dsh-agent-instructions'
|
|
38
|
+
config:
|
|
39
|
+
maxBytes: 65536
|
|
40
|
+
|
|
41
|
+
# ── shell ───────────────────────────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
# `shell-env` stays in the HOST composition: a host row that injects a service is
|
|
44
|
+
# the criterion for host-plane ownership — injection resolves before any session
|
|
45
|
+
# exists, so there is no agent to key by. Both shell tools consume the host
|
|
46
|
+
# registry from here; their executors (`bash-sandbox`/`pwsh-sandbox`) are
|
|
47
|
+
# host-plane too.
|
|
48
|
+
- id: tool-bash
|
|
49
|
+
name: '@deepseek-ai/dsh-tool-bash'
|
|
50
|
+
disabled: !!js process.platform === 'win32'
|
|
51
|
+
|
|
52
|
+
- id: tool-pwsh
|
|
53
|
+
name: '@deepseek-ai/dsh-tool-pwsh'
|
|
54
|
+
disabled: !!js process.platform !== 'win32'
|
|
55
|
+
|
|
56
|
+
# ── persistent terminals ────────────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
# `terminals` is an agent-owned registry, so it lives in an entry-local realm;
|
|
59
|
+
# the backend still consumes the host sandbox policy and subprocess
|
|
60
|
+
# implementation. A terminal outlives one tool call, which is what separates it
|
|
61
|
+
# from `tool-bash`: the agent starts a dev server, a watcher, or a REPL and
|
|
62
|
+
# reads from it across turns instead of blocking a call until the process exits.
|
|
63
|
+
#
|
|
64
|
+
# `dsh-terminal-bash` is the only shipped backend, so the whole group is off on
|
|
65
|
+
# Windows — `tool-terminal` without a backend would answer every `terminal_open`
|
|
66
|
+
# with an unavailable registry rather than a missing tool.
|
|
67
|
+
- id: terminals
|
|
68
|
+
name: cordis:group
|
|
69
|
+
group: true
|
|
70
|
+
disabled: !!js process.platform === 'win32'
|
|
71
|
+
isolate:
|
|
72
|
+
terminals: true
|
|
73
|
+
config:
|
|
74
|
+
- id: pty
|
|
75
|
+
name: '@deepseek-ai/dsh-terminal'
|
|
76
|
+
|
|
77
|
+
- id: terminal-bash
|
|
78
|
+
name: '@deepseek-ai/dsh-terminal-bash'
|
|
79
|
+
config:
|
|
80
|
+
timeoutMs: 300000
|
|
81
|
+
|
|
82
|
+
- id: tool-terminal
|
|
83
|
+
name: '@deepseek-ai/dsh-tool-terminal'
|
|
84
|
+
|
|
85
|
+
# ── code navigation ─────────────────────────────────────────────────────────
|
|
86
|
+
|
|
87
|
+
# `lsp` is an agent-owned provider registry, so it and its stdio provider share
|
|
88
|
+
# one entry-local realm with the tool that reads them.
|
|
89
|
+
#
|
|
90
|
+
# The group is off by default because `lsp-stdio` spawns `command` from the host
|
|
91
|
+
# PATH: enabling it on a machine without that binary turns every `lsp` call into
|
|
92
|
+
# a spawn failure. Install the server, then delete the `disabled` line below.
|
|
93
|
+
# The `servers` table maps stable provider ids to independent local servers, and
|
|
94
|
+
# each provider reserves its `extensionToLanguage` extensions exclusively.
|
|
95
|
+
- id: code-navigation
|
|
96
|
+
name: cordis:group
|
|
97
|
+
group: true
|
|
98
|
+
disabled: true
|
|
99
|
+
isolate:
|
|
100
|
+
lsp: true
|
|
101
|
+
config:
|
|
102
|
+
- id: lsp
|
|
103
|
+
name: '@deepseek-ai/dsh-lsp'
|
|
104
|
+
|
|
105
|
+
- id: lsp-stdio
|
|
106
|
+
name: '@deepseek-ai/dsh-lsp-stdio'
|
|
107
|
+
config:
|
|
108
|
+
servers:
|
|
109
|
+
typescript:
|
|
110
|
+
command: typescript-language-server
|
|
111
|
+
args: ['--stdio']
|
|
112
|
+
extensionToLanguage:
|
|
113
|
+
.ts: typescript
|
|
114
|
+
.tsx: typescriptreact
|
|
115
|
+
.mts: typescript
|
|
116
|
+
.cts: typescript
|
|
117
|
+
.js: javascript
|
|
118
|
+
.jsx: javascriptreact
|
|
119
|
+
.mjs: javascript
|
|
120
|
+
.cjs: javascript
|
|
121
|
+
|
|
122
|
+
- id: tool-lsp
|
|
123
|
+
name: '@deepseek-ai/dsh-tool-lsp'
|
|
124
|
+
|
|
125
|
+
# ── filesystem ──────────────────────────────────────────────────────────────
|
|
126
|
+
|
|
127
|
+
# Both register into the host `tools` registry and provide nothing, so
|
|
128
|
+
# they need no realm. The `fs` service and its policy stay in the host.
|
|
129
|
+
- id: tool-fs
|
|
130
|
+
name: '@deepseek-ai/dsh-tool-fs'
|
|
131
|
+
|
|
132
|
+
- id: tool-fs-search
|
|
133
|
+
name: '@deepseek-ai/dsh-tool-fs-search'
|
|
134
|
+
config:
|
|
135
|
+
sampleOverCapGlobResults: false
|
|
136
|
+
|
|
137
|
+
# ── background jobs ────────────────────────────────────────────────────────
|
|
138
|
+
|
|
139
|
+
# Only the model-facing controls. The task REGISTRY stays on the host plane:
|
|
140
|
+
# its producers sit outside any realm this file could put it in — `tool-bash`
|
|
141
|
+
# above resolves it with `ctx.get`, and an entry-local realm here is invisible
|
|
142
|
+
# to every sibling row, so `run_in_background` would answer "background jobs
|
|
143
|
+
# unavailable" while these controls sat in the catalog. The registry is keyed by
|
|
144
|
+
# owning agent anyway, so one host instance serves every session. What a preset
|
|
145
|
+
# chooses is whether its agent can collect and stop background work at all.
|
|
146
|
+
- id: tool-jobs
|
|
147
|
+
name: '@deepseek-ai/dsh-tool-jobs'
|
|
148
|
+
|
|
149
|
+
# ── skills ──────────────────────────────────────────────────────────────────
|
|
150
|
+
|
|
151
|
+
# The skill REGISTRY lives in the host composition and is layered per scope:
|
|
152
|
+
# these rows register into THIS preset's layer of it, so they need no realm.
|
|
153
|
+
# `skill-filesystem` contributes local-root discovery for agents on this preset, and
|
|
154
|
+
# `tool-skill` gives them the catalog and loader; the merged catalog also
|
|
155
|
+
# carries whatever the deployment registered globally (repository plugins).
|
|
156
|
+
- id: skill-filesystem
|
|
157
|
+
name: '@deepseek-ai/dsh-skill-filesystem'
|
|
158
|
+
|
|
159
|
+
- id: tool-skill
|
|
160
|
+
name: '@deepseek-ai/dsh-tool-skill'
|
|
161
|
+
|
|
162
|
+
# ── goals ───────────────────────────────────────────────────────────────────
|
|
163
|
+
|
|
164
|
+
# Only the model-facing tool. The goal SERVICE and its session driver stay on
|
|
165
|
+
# the host plane; the registry is keyed by session anyway, so one host instance
|
|
166
|
+
# serves every session. What a preset chooses is whether its agent can call the
|
|
167
|
+
# goal tool.
|
|
168
|
+
- id: tool-goal
|
|
169
|
+
name: '@deepseek-ai/dsh-tool-goal'
|
|
170
|
+
|
|
171
|
+
# ── plan mode ───────────────────────────────────────────────────────────────
|
|
172
|
+
|
|
173
|
+
# Plan state is per-agent by nature, so an entry-local realm is not a
|
|
174
|
+
# workaround here — it is the correct lifetime.
|
|
175
|
+
- id: planning
|
|
176
|
+
name: cordis:group
|
|
177
|
+
group: true
|
|
178
|
+
isolate:
|
|
179
|
+
planMode: true
|
|
180
|
+
config:
|
|
181
|
+
- id: plan-mode
|
|
182
|
+
name: '@deepseek-ai/dsh-plan-mode'
|
|
183
|
+
config:
|
|
184
|
+
section: |
|
|
185
|
+
You are in plan mode. Stay in plan mode until exit_plan_mode succeeds or the user switches the session mode. Imperative language to implement changes means plan the implementation, not execute it. A user's conversational agreement — including an answer confirming something you asked — approves nothing and does not end plan mode; fold the confirmed decision into the plan and submit it through exit_plan_mode.
|
|
186
|
+
|
|
187
|
+
Explore first. Use non-mutating reads, searches, static analysis, and checks to ground the plan in the actual repository. Do not edit or write files, change configuration, run formatters or code generation that rewrites tracked files, commit, or otherwise carry out the plan. Prefer existing functions and patterns over new machinery.
|
|
188
|
+
|
|
189
|
+
The tool catalog stays the same across modes for request-cache stability. These plan-mode rules override any later tool description or guidance that suggests using mutation tools; those tools remain listed to keep the tool catalog unchanged. Do not use todo_write to track this planning phase: it tracks implementation after an approved plan, while the plan itself belongs in exit_plan_mode.
|
|
190
|
+
|
|
191
|
+
Resolve discoverable facts by inspection. Use ask_user_question only for user-owned choices or material ambiguity that inspection cannot answer. Do not ask the user where code lives or how current behavior works when you can find out.
|
|
192
|
+
|
|
193
|
+
Make the plan decision-complete: state the goal and success criteria; group implementation changes by subsystem; identify public API, schema, and data-flow changes; cover edge cases, failure modes, tests, acceptance criteria, and explicit assumptions. Keep it concise enough to review but detailed enough that another engineer can implement it without making design decisions.
|
|
194
|
+
|
|
195
|
+
When ready, call exit_plan_mode with the complete plan markdown, starting with a # title. Make exit_plan_mode the only and final tool call in that assistant response: it presents the plan for approval, and implementation begins only in a later step after approval. Do not paste the final plan as a plain reply or ask "should I proceed?" through prose or ask_user_question. If review rejects it, incorporate the feedback and present again. If the review channel is unavailable or aborted, stay in plan mode and ask the user to switch modes manually; do not proceed with implementation.
|
|
196
|
+
|
|
197
|
+
# ── compaction ──────────────────────────────────────────────────────────────
|
|
198
|
+
|
|
199
|
+
# `compaction-basic` reads `toolResultPrune` through `ctx.get`, so the pruner must
|
|
200
|
+
# share this realm rather than sit outside it.
|
|
201
|
+
#
|
|
202
|
+
# `tokenMeter` is deliberately NOT in this realm: the meter stays on the HOST
|
|
203
|
+
# plane, and the rows here resolve that one instance. It takes no configuration,
|
|
204
|
+
# keys every fold by Session, and owns the context-meter projection units the
|
|
205
|
+
# terminal status line reads for every session — behind a realm those units
|
|
206
|
+
# would come and go with whichever presets happen to be mounted. What a preset
|
|
207
|
+
# chooses is whether its agent compacts at all, which is `compaction-basic`.
|
|
208
|
+
- id: compaction
|
|
209
|
+
name: cordis:group
|
|
210
|
+
group: true
|
|
211
|
+
isolate:
|
|
212
|
+
compaction: true
|
|
213
|
+
toolResultPruner: true
|
|
214
|
+
config:
|
|
215
|
+
- id: compaction-basic
|
|
216
|
+
name: '@deepseek-ai/dsh-compaction-basic'
|
|
217
|
+
|
|
218
|
+
- id: command-compact
|
|
219
|
+
name: '@deepseek-ai/dsh-command-compact'
|
|
220
|
+
|
|
221
|
+
- id: tool-result-pruner
|
|
222
|
+
name: '@deepseek-ai/dsh-compaction-tool-result-pruner'
|
|
223
|
+
config:
|
|
224
|
+
thresholdChars: 8192
|
|
225
|
+
headChars: 4096
|
|
226
|
+
tailChars: 1024
|
|
227
|
+
|
|
228
|
+
# ── delegation and workflows ────────────────────────────────────────────────
|
|
229
|
+
|
|
230
|
+
# The `subagents` registry and its spawn/fork backends live in the HOST
|
|
231
|
+
# composition: the registry is a process singleton, and a provider name may only
|
|
232
|
+
# be registered once. This preset contributes the delegation TOOLS, which
|
|
233
|
+
# resolve that host registry.
|
|
234
|
+
#
|
|
235
|
+
# `workflows` is different — nothing outside an agent reads it — so every row
|
|
236
|
+
# that reaches it shares one entry-local realm here, and a consumer left
|
|
237
|
+
# outside would resolve a host registry this preset does not populate.
|
|
238
|
+
#
|
|
239
|
+
# `tool-subagent-report` is host-plane for the same reason as the registry,
|
|
240
|
+
# not because a preset may not want it: it registers a CONTINUABLE SETUP on
|
|
241
|
+
# that singleton rather than a tool this agent calls, and the setup list is
|
|
242
|
+
# not scope-aware — one copy per mounted preset means every child gets
|
|
243
|
+
# `report` registered once per live session, which throws on the second.
|
|
244
|
+
- id: delegation
|
|
245
|
+
name: cordis:group
|
|
246
|
+
group: true
|
|
247
|
+
isolate:
|
|
248
|
+
workflowEngine: true
|
|
249
|
+
config:
|
|
250
|
+
- id: tool-subagent-control
|
|
251
|
+
name: '@deepseek-ai/dsh-tool-subagent-control'
|
|
252
|
+
|
|
253
|
+
- id: tool-subagent-list-agents
|
|
254
|
+
name: '@deepseek-ai/dsh-tool-subagent-control/list-agents'
|
|
255
|
+
|
|
256
|
+
- id: tool-subagent
|
|
257
|
+
name: '@deepseek-ai/dsh-tool-subagent'
|
|
258
|
+
config:
|
|
259
|
+
provider: spawn
|
|
260
|
+
toolName: subagent
|
|
261
|
+
backgroundMode: continuable
|
|
262
|
+
|
|
263
|
+
- id: tool-subagent-fork
|
|
264
|
+
name: '@deepseek-ai/dsh-tool-subagent'
|
|
265
|
+
config:
|
|
266
|
+
provider: fork
|
|
267
|
+
toolName: subagent_fork
|
|
268
|
+
backgroundMode: continuable
|
|
269
|
+
|
|
270
|
+
# Product providers are host-plane singletons. Copy this preset, then
|
|
271
|
+
# remove `disabled` from either ordinary tool row to expose that product
|
|
272
|
+
# only to agents composed from the copy.
|
|
273
|
+
- id: tool-subagent-codex
|
|
274
|
+
name: '@deepseek-ai/dsh-tool-subagent'
|
|
275
|
+
disabled: true
|
|
276
|
+
config:
|
|
277
|
+
provider: codex
|
|
278
|
+
toolName: subagent_codex
|
|
279
|
+
enableRunInBackground: false
|
|
280
|
+
maxDepth: provider-managed
|
|
281
|
+
|
|
282
|
+
- id: tool-subagent-claude-code
|
|
283
|
+
name: '@deepseek-ai/dsh-tool-subagent'
|
|
284
|
+
disabled: true
|
|
285
|
+
config:
|
|
286
|
+
provider: claude-code
|
|
287
|
+
toolName: subagent_claude_code
|
|
288
|
+
enableRunInBackground: false
|
|
289
|
+
maxDepth: provider-managed
|
|
290
|
+
|
|
291
|
+
- id: workflow-worker-thread
|
|
292
|
+
name: '@deepseek-ai/dsh-workflow-worker-thread'
|
|
293
|
+
config:
|
|
294
|
+
provider: spawn
|
|
295
|
+
|
|
296
|
+
- id: tool-workflow
|
|
297
|
+
name: '@deepseek-ai/dsh-tool-workflow'
|
|
298
|
+
|
|
299
|
+
- id: tool-ralph
|
|
300
|
+
name: '@deepseek-ai/dsh-tool-ralph'
|
|
301
|
+
config:
|
|
302
|
+
subagentProvider: spawn
|
|
303
|
+
maxRounds: 64
|
|
304
|
+
|
|
305
|
+
# ── remaining model-facing rows ─────────────────────────────────────────────
|
|
306
|
+
|
|
307
|
+
- id: tool-ask-user
|
|
308
|
+
name: '@deepseek-ai/dsh-tool-ask-user'
|
|
309
|
+
|
|
310
|
+
- id: tool-todo
|
|
311
|
+
name: '@deepseek-ai/dsh-tool-todo'
|
|
312
|
+
config:
|
|
313
|
+
allowParallelInProgress: true
|
|
314
|
+
|
|
315
|
+
# The `web` service and its providers stay in the host composition; only the
|
|
316
|
+
# model-facing tool is per-session. `fetch` is on here because a terminal
|
|
317
|
+
# session has no browser beside it to open a result in.
|
|
318
|
+
- id: tool-web
|
|
319
|
+
name: '@deepseek-ai/dsh-tool-web'
|
|
320
|
+
config:
|
|
321
|
+
fetch: true
|
|
322
|
+
searchTimeoutMs: 60000
|
package/bin/codsh.mjs
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* The `codsh` command: the dsh launcher booted onto the code profile, with
|
|
4
|
+
* this package registered as that profile's bundle on first run.
|
|
5
|
+
*
|
|
6
|
+
* dsh owns profiles, plugin installation, and the boot; this wrapper only
|
|
7
|
+
* makes `codsh` a one-command experience — it is exactly
|
|
8
|
+
* `dsh plugin --profile code add <this package>` once, then
|
|
9
|
+
* `dsh --profile code <args>` every time.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { spawnSync } from 'node:child_process'
|
|
13
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync } from 'node:fs'
|
|
14
|
+
import { homedir, tmpdir } from 'node:os'
|
|
15
|
+
import { dirname, join } from 'node:path'
|
|
16
|
+
import { fileURLToPath } from 'node:url'
|
|
17
|
+
import { createRequire } from 'node:module'
|
|
18
|
+
|
|
19
|
+
const require = createRequire(import.meta.url)
|
|
20
|
+
const ownDir = fileURLToPath(new URL('..', import.meta.url))
|
|
21
|
+
const dshPackage = require.resolve('@deepseek-ai/dsh/package.json')
|
|
22
|
+
const dshBinField = JSON.parse(readFileSync(dshPackage, 'utf8')).bin
|
|
23
|
+
const dshBin = join(dirname(dshPackage), typeof dshBinField === 'string' ? dshBinField : dshBinField.dsh)
|
|
24
|
+
|
|
25
|
+
/** Whether the code profile already carries this package. */
|
|
26
|
+
function registered() {
|
|
27
|
+
const home = process.env.DSH_HOME ?? join(homedir(), '.dsh')
|
|
28
|
+
const manifest = join(home, 'profiles', 'code', 'package.json')
|
|
29
|
+
if (!existsSync(manifest)) return false
|
|
30
|
+
try {
|
|
31
|
+
const profile = JSON.parse(readFileSync(manifest, 'utf8'))
|
|
32
|
+
return 'codsh-cli' in (profile.dependencies ?? {})
|
|
33
|
+
} catch {
|
|
34
|
+
return false
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!registered()) {
|
|
39
|
+
console.error('codsh: registering this package into the dsh code profile (first run)')
|
|
40
|
+
// A bare directory installs as `link:`, whose dependencies never reach the
|
|
41
|
+
// profile's resolver. A development checkout (it has node_modules) packs to
|
|
42
|
+
// the release tarball; an installed copy is clean and `file:` copies it,
|
|
43
|
+
// with its dependencies resolved by the profile install either way.
|
|
44
|
+
let spec = `file:${ownDir}`
|
|
45
|
+
let scratch
|
|
46
|
+
if (existsSync(join(ownDir, 'node_modules'))) {
|
|
47
|
+
scratch = mkdtempSync(join(tmpdir(), 'codsh-pack-'))
|
|
48
|
+
const packed = spawnSync('npm', ['pack', '--pack-destination', scratch], { cwd: ownDir, encoding: 'utf8' })
|
|
49
|
+
if (packed.status !== 0) {
|
|
50
|
+
console.error(packed.stderr ?? 'codsh: npm pack failed')
|
|
51
|
+
process.exit(packed.status ?? 1)
|
|
52
|
+
}
|
|
53
|
+
spec = join(scratch, packed.stdout.trim().split('\n').at(-1) ?? '')
|
|
54
|
+
}
|
|
55
|
+
const setup = spawnSync(process.execPath, [dshBin, 'plugin', '--profile', 'code', 'add', spec], { stdio: 'inherit' })
|
|
56
|
+
if (scratch !== undefined) rmSync(scratch, { recursive: true, force: true })
|
|
57
|
+
if (setup.status !== 0) process.exit(setup.status ?? 1)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const run = spawnSync(process.execPath, [dshBin, '--profile', 'code', ...process.argv.slice(2)], { stdio: 'inherit' })
|
|
61
|
+
process.exit(run.status ?? 0)
|