codsh-cli 0.2.0 → 0.3.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 CHANGED
@@ -1,77 +1,7 @@
1
- # codsh
1
+ # codsh-cli
2
2
 
3
- > npm: [`codsh-cli`](https://www.npmjs.com/package/codsh-cli) · command: `codsh`
3
+ The `codsh` command: a **zero-dependency launcher** for [codsh](https://github.com/Blackman99/codsh), a Claude Code-style coding agent for the terminal composed on the DeepSeek Harness (dsh).
4
4
 
5
- English | [中文](README.zh.md)
5
+ This package bundles nothing. It finds the dsh you already have (`DSH_BIN`, a resolvable `@deepseek-ai/dsh`, or `dsh` on PATH), registers the [`codsh-bundle`](https://www.npmjs.com/package/codsh-bundle) runtime into a dsh `code` profile on first run, and boots `dsh --profile code`. No dsh yet? `npm install -g @deepseek-ai/dsh` first.
6
6
 
7
- A terminal coding agent whose interaction design fuses the best of today's agent CLIs, 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
- - **A session that is its own space**: codsh takes the alternate screen, so your shell's scrollback is untouched and waiting when you leave. The transcript scrolls in a buffer the session owns — mouse wheel, PgUp/PgDn, Shift+↑/↓ — under an input box that never moves from the bottom; scrolled back, the viewport says how far and new output keeps accumulating without yanking you to it. Every frame paints as one synchronized update, and quitting drops a two-line summary (session id, spend, the `--resume` command) into your shell.
23
- - **Select to copy**: drag with the mouse and the selection is on your clipboard the moment you release — highlighted in place, sent through OSC 52 and the platform clipboard both (`CODSH_CLIPBOARD=osc52|system|off` narrows it). Long finished blocks — thinking, tool output, and answers past a screenful — collapse to their head lines once you move on; Ctrl-O swaps every one of them between summary and full form.
24
- - **An input box that owns the keyboard**: multi-line editing (Alt-Enter), history across sessions, and completion for commands, arguments, and `@`-mentioned files (fuzzy, workspace-wide), opened as you type.
25
- - **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.
26
- - **Decisions as selections**: approvals, questions, `/model`, and `/resume` are arrow-key widgets; plan mode toggles on Shift-Tab and tints the box frame.
27
- - **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.
28
- - **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.
29
- - Status line (model, preset, permissions, tokens, context left, branch), terminal-title updates, a bell when a decision waits, and a `--print` mode for scripts.
30
-
31
- Off a TTY (pipes, scripts) the same surface degrades to a line reader: selections become typed answers, lists replace widgets, and nothing draws.
32
-
33
- ## Development
34
-
35
- ```sh
36
- pnpm install
37
- pnpm run dev # build → sync into .dev-home → boot; seconds per iteration
38
- MOCK=markdown pnpm run dev # keyless, against the e2e mock model
39
- pnpm run build # tsdown runtime bundles + tsc declarations into lib/
40
- pnpm run typecheck
41
- pnpm test # unit suites (pure modules: editor, markdown, transcript, …)
42
- pnpm run test:e2e # packs this repo, registers it into a dsh profile, and
43
- # drives the INSTALLED dsh binary through pipes and a real PTY
44
- ```
45
-
46
- `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.
47
-
48
- 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.
49
-
50
- ### Debugging against dsh sources
51
-
52
- 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:
53
-
54
- ```jsonc
55
- // package.json — remove again when done; keep npm as the default
56
- "pnpm": {
57
- "overrides": {
58
- "@deepseek-ai/dsh-agent-loop": "link:../deepseek-harness/packages/core/agent-loop"
59
- }
60
- }
61
- ```
62
-
63
- and re-run `pnpm install`. Changes you want upstream go to the harness repo as ordinary PRs; this repository never forks it.
64
-
65
- ### Keeping up with dsh releases
66
-
67
- codsh consumes the harness as published `@deepseek-ai/dsh-*` packages, so "syncing with dsh" means tracking harness **releases**, not merging source. Three layers keep it automatic:
68
-
69
- 1. **Detect** — [Renovate](renovate.json) watches `@deepseek-ai/dsh-*` (plus co-released cordis packages) and opens dependency PRs; a nightly [sync workflow](.github/workflows/sync-dsh.yml) does the same without third-party services.
70
- 2. **Bump** — `pnpm run sync:dsh` rewrites every `@deepseek-ai/dsh-*` range to the latest published release, refreshes the lockfile, writes a changeset, and exits `1` from `--check` when newer versions exist (that is what the nightly job keys on).
71
- 3. **Prove** — the same command re-verifies `cordis.patch.yml`: every plugin id it disables or configures must still be declared by an installed dsh bundle, and every package it inserts must resolve. Then it runs typecheck/build/unit (add `--e2e` to boot the real patched bundle), and CI repeats all of that on the resulting PR.
72
-
73
- Because every dsh package is a prerelease (`0.1.0-rc.N`), plain semver ranges do **not** float across releases — the sync command is the source of truth, not `pnpm update`.
74
-
75
- ## License
76
-
77
- MIT
7
+ Full documentation: [github.com/Blackman99/codsh](https://github.com/Blackman99/codsh)
package/bin/codsh.mjs CHANGED
@@ -1,61 +1,123 @@
1
1
  #!/usr/bin/env node
2
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.
3
+ * The `codsh` command: a zero-dependency launcher over the dsh you already
4
+ * have.
5
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.
6
+ * This package deliberately bundles NOTHING the ~300MB dsh runtime lives
7
+ * once on a machine, not once per tool. The wrapper finds a dsh (`DSH_BIN`,
8
+ * then a resolvable `@deepseek-ai/dsh` install, then `dsh` on PATH), registers
9
+ * the `codsh-bundle` runtime into the `code` profile on first run, and boots
10
+ * `dsh --profile code` with the arguments passed through.
10
11
  */
11
12
 
12
13
  import { spawnSync } from 'node:child_process'
13
- import { existsSync, mkdtempSync, readFileSync, rmSync } from 'node:fs'
14
- import { homedir, tmpdir } from 'node:os'
14
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs'
15
+ import { homedir } from 'node:os'
15
16
  import { dirname, join } from 'node:path'
16
- import { fileURLToPath } from 'node:url'
17
17
  import { createRequire } from 'node:module'
18
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
19
+ const requireFromHere = createRequire(import.meta.url)
20
+ const own = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'))
21
+
22
+ /** The bundle package this launcher pairs with, lockstep-versioned. */
23
+ const BUNDLE = 'codsh-bundle'
24
+
25
+ /**
26
+ * Locate a dsh launcher without carrying one.
27
+ * @returns how to spawn it, or undefined when the machine has none.
28
+ */
29
+ function findDsh() {
30
+ const pinned = process.env.DSH_BIN
31
+ if (pinned !== undefined && pinned !== '') {
32
+ // A JS entry runs through this Node; anything else is an executable.
33
+ return /\.[cm]?js$/.test(pinned)
34
+ ? { command: process.execPath, prefix: [pinned] }
35
+ : { command: pinned, prefix: [] }
36
+ }
30
37
  try {
31
- const profile = JSON.parse(readFileSync(manifest, 'utf8'))
32
- return 'codsh-cli' in (profile.dependencies ?? {})
38
+ const manifest = requireFromHere.resolve('@deepseek-ai/dsh/package.json')
39
+ const bin = JSON.parse(readFileSync(manifest, 'utf8')).bin
40
+ const entry = join(dirname(manifest), typeof bin === 'string' ? bin : bin.dsh)
41
+ return { command: process.execPath, prefix: [entry] }
33
42
  } catch {
34
- return false
43
+ // Not installed beside this package; the PATH is next.
35
44
  }
45
+ const probe = spawnSync('dsh', ['--version'], { stdio: 'ignore', shell: process.platform === 'win32' })
46
+ if (probe.error === undefined && probe.status !== null) return { command: 'dsh', prefix: [] }
47
+ return undefined
48
+ }
49
+
50
+ const dsh = findDsh()
51
+ if (dsh === undefined) {
52
+ console.error(`codsh: no dsh runtime found. codsh launches the dsh you already have —
53
+ the runtime is not bundled, so a machine never carries a second copy.
54
+
55
+ install one: npm install -g @deepseek-ai/dsh
56
+ or point at one: DSH_BIN=/path/to/dsh codsh`)
57
+ process.exit(1)
58
+ }
59
+
60
+ /** Run the found dsh with arguments, inheriting the terminal. */
61
+ function run(args) {
62
+ const result = spawnSync(dsh.command, [...dsh.prefix, ...args], { stdio: 'inherit' })
63
+ return result.status ?? 1
36
64
  }
37
65
 
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)
66
+ /** `a > b` for plain x.y.z versions, which is all this pair publishes. */
67
+ function newer(a, b) {
68
+ const pa = String(a).split('.').map(Number)
69
+ const pb = String(b).split('.').map(Number)
70
+ for (let index = 0; index < 3; index += 1) {
71
+ if ((pa[index] ?? 0) !== (pb[index] ?? 0)) return (pa[index] ?? 0) > (pb[index] ?? 0)
72
+ }
73
+ return false
74
+ }
75
+
76
+ const home = process.env.DSH_HOME ?? join(homedir(), '.dsh')
77
+ const manifestPath = join(home, 'profiles', 'code', 'package.json')
78
+
79
+ /** What the code profile currently carries, or undefined before first run. */
80
+ function profile() {
81
+ if (!existsSync(manifestPath)) return undefined
82
+ try {
83
+ return JSON.parse(readFileSync(manifestPath, 'utf8'))
84
+ } catch {
85
+ return undefined
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Decide whether — and what — to register into the profile this run.
91
+ * @returns the spec to `dsh plugin add`, or undefined when nothing is due.
92
+ */
93
+ function registration() {
94
+ const spec = process.env.CODSH_BUNDLE_SPEC ?? `${BUNDLE}@^${own.version}`
95
+ const manifest = profile()
96
+ const dependencies = manifest?.dependencies ?? {}
97
+ // The pre-split layout carried the whole runtime under this launcher's own
98
+ // name; migrate it out so both bundles don't fight over the terminal.
99
+ if (manifest !== undefined && 'codsh-cli' in dependencies) {
100
+ console.error('codsh: migrating the profile to the split codsh-bundle runtime')
101
+ delete manifest.dependencies['codsh-cli']
102
+ const bundles = manifest?.dsh?.profile?.bundles
103
+ if (Array.isArray(bundles)) {
104
+ manifest.dsh.profile.bundles = bundles.filter(name => name !== 'codsh-cli')
52
105
  }
53
- spec = join(scratch, packed.stdout.trim().split('\n').at(-1) ?? '')
106
+ writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`)
107
+ return spec
54
108
  }
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)
109
+ const current = dependencies[BUNDLE]
110
+ if (current === undefined) return spec
111
+ // A file:/link: registration is a development pin; never clobber it.
112
+ if (!current.startsWith('^')) return undefined
113
+ return newer(own.version, current.slice(1)) ? spec : undefined
114
+ }
115
+
116
+ const spec = registration()
117
+ if (spec !== undefined) {
118
+ console.error(`codsh: registering ${spec} into the dsh code profile`)
119
+ const status = run(['plugin', '--profile', 'code', 'add', spec])
120
+ if (status !== 0) process.exit(status)
58
121
  }
59
122
 
60
- const run = spawnSync(process.execPath, [dshBin, '--profile', 'code', ...process.argv.slice(2)], { stdio: 'inherit' })
61
- process.exit(run.status ?? 0)
123
+ process.exit(run(['--profile', 'code', ...process.argv.slice(2)]))
package/package.json CHANGED
@@ -1,132 +1,24 @@
1
1
  {
2
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.2.0",
3
+ "description": "A Claude Code-style coding agent for the terminal, composed on the DeepSeek Harness (dsh). This is the zero-dependency launcher: it finds your dsh, registers the codsh-bundle runtime into a profile, and boots it.",
4
+ "version": "0.3.0",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "bin": {
8
8
  "codsh": "./bin/codsh.mjs"
9
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
10
  "files": [
30
- "lib/index.js",
31
- "lib/invariant.js",
32
- "lib/startup.js",
33
11
  "bin",
34
- "cordis.patch.yml",
35
- "agent-presets",
36
- "lib/types/**/*.d.ts"
12
+ "README.md"
37
13
  ],
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
- "string-width": "^8.2.2"
80
- },
81
- "peerDependencies": {
82
- "@deepseek-ai/cordis": "^4.0.1",
83
- "@deepseek-ai/cordis-plugin-loader": "^1.0.2",
84
- "@deepseek-ai/dsh-agent": "^0.1.0-rc.7",
85
- "@deepseek-ai/dsh-agent-default-model": "^0.1.0-rc.7",
86
- "@deepseek-ai/dsh-commands": "^0.1.0-rc.7",
87
- "@deepseek-ai/dsh-home-paths": "^0.1.0-rc.7",
88
- "@deepseek-ai/dsh-invariants": "^0.1.0-rc.7",
89
- "@deepseek-ai/dsh-llm": "^0.1.0-rc.7",
90
- "@deepseek-ai/dsh-permission-presets": "^0.1.0-rc.7",
91
- "@deepseek-ai/dsh-session": "^0.1.0-rc.7",
92
- "@deepseek-ai/dsh-session-projection": "^0.1.0-rc.7",
93
- "@deepseek-ai/dsh-session-query": "^0.1.0-rc.7",
94
- "@deepseek-ai/dsh-token-meter": "^0.1.0-rc.7",
95
- "@deepseek-ai/dsh-tools": "^0.1.0-rc.7",
96
- "@deepseek-ai/dsh-user-approval": "^0.1.0-rc.7",
97
- "@deepseek-ai/dsh-user-questions": "^0.1.0-rc.7"
98
- },
99
- "devDependencies": {
100
- "@changesets/cli": "^3.0.0",
101
- "@deepseek-ai/cordis": "^4.0.1",
102
- "@deepseek-ai/cordis-plugin-loader": "^1.0.2",
103
- "@deepseek-ai/dsh-agent": "^0.1.0-rc.7",
104
- "@deepseek-ai/dsh-agent-default-model": "^0.1.0-rc.7",
105
- "@deepseek-ai/dsh-commands": "^0.1.0-rc.7",
106
- "@deepseek-ai/dsh-home-paths": "^0.1.0-rc.7",
107
- "@deepseek-ai/dsh-invariants": "^0.1.0-rc.7",
108
- "@deepseek-ai/dsh-llm": "^0.1.0-rc.7",
109
- "@deepseek-ai/dsh-permission-presets": "^0.1.0-rc.7",
110
- "@deepseek-ai/dsh-session": "^0.1.0-rc.7",
111
- "@deepseek-ai/dsh-session-projection": "^0.1.0-rc.7",
112
- "@deepseek-ai/dsh-session-query": "^0.1.0-rc.7",
113
- "@deepseek-ai/dsh-token-meter": "^0.1.0-rc.7",
114
- "@deepseek-ai/dsh-tools": "^0.1.0-rc.7",
115
- "@deepseek-ai/dsh-user-approval": "^0.1.0-rc.7",
116
- "@deepseek-ai/dsh-user-questions": "^0.1.0-rc.7",
117
- "@types/node": "^24.0.0",
118
- "execa": "^9.0.0",
119
- "tsdown": "^0.15.0",
120
- "tsx": "^4.20.0",
121
- "typescript": "^5.9.0",
122
- "vitest": "^4.0.0"
14
+ "engines": {
15
+ "node": ">=22.19"
123
16
  },
124
17
  "repository": {
125
18
  "type": "git",
126
- "url": "git+https://github.com/Blackman99/codsh.git"
19
+ "url": "git+https://github.com/Blackman99/codsh.git",
20
+ "directory": "packages/cli"
127
21
  },
128
- "homepage": "https://github.com/Blackman99/codsh#readme",
129
- "bugs": "https://github.com/Blackman99/codsh/issues",
130
22
  "keywords": [
131
23
  "cli",
132
24
  "coding-agent",
@@ -136,16 +28,6 @@
136
28
  "ai",
137
29
  "agent"
138
30
  ],
139
- "engines": {
140
- "node": ">=22.19"
141
- },
142
- "scripts": {
143
- "build": "tsdown && tsc -p tsconfig.build.json",
144
- "typecheck": "tsc --noEmit",
145
- "test": "vitest run",
146
- "test:e2e": "pnpm run build && vitest run --config vitest.e2e.config.ts",
147
- "dev": "node scripts/dev.mjs",
148
- "sync:dsh": "node scripts/sync-dsh.mjs",
149
- "release": "pnpm run build && changeset publish"
150
- }
31
+ "homepage": "https://github.com/Blackman99/codsh#readme",
32
+ "bugs": "https://github.com/Blackman99/codsh/issues"
151
33
  }
package/README.zh.md DELETED
@@ -1,76 +0,0 @@
1
- # codsh
2
-
3
- > npm 包名:[`codsh-cli`](https://www.npmjs.com/package/codsh-cli) · 命令:`codsh`
4
-
5
- [English](README.md) | 中文
6
-
7
- 一款汲取了当今主流 agent CLI 交互体验精髓的终端编码 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
- - **会话是自己的空间**:codsh 进入备用屏幕,你的 shell 滚回历史原封不动、退出即恢复。transcript 在会话自有的缓冲里滚动——鼠标滚轮、PgUp/PgDn、Shift+↑/↓——输入框钉在底部从不移动;向上翻阅时视口会标注离尾部多远,新输出继续累积而不把你拽回去。每一帧以同步更新原子绘制;退出时向 shell 留下两行摘要(会话 id、用量、`--resume` 命令)。
23
- - **接管键盘的输入框**:多行编辑(Alt-Enter)、跨会话历史,命令、参数与 `@` 文件提及(全工作区模糊搜索)的补全随输入自动打开。
24
- - **流式渲染**:Markdown 带代码高亮与表格排版,推理模型的思考在 `✻ thinking` 下暗色显示,工具调用按 presenter 驱动的卡片渲染(含 diff),Ctrl-O 完整重印最近被截断的输出。
25
- - **决定用选择**:审批、提问、`/model` 与 `/resume` 都是方向键组件;Shift-Tab 切换 plan 模式并为框着色。
26
- - **会话流**:`/clear` 原地开新会话,`/resume` 从带标题和时间的会话列表里选,连按两次 Escape 召回上一条消息编辑,`!cmd` 在你自己的 shell 里运行并把结果注入为模型可见上下文——不花回合。
27
- - **固定 prompt**:`/init` 起草 `AGENTS.md`;`$DSH_HOME/commands/` 或 `<工作区>/.dsh/commands/` 下的 Markdown 文件即成为斜杠命令,支持 `$ARGUMENTS` 模板。
28
- - 状态行(模型、preset、权限、token、剩余上下文、分支)、终端标题跟随、有决定等待时的铃声,以及面向脚本的 `--print` 模式。
29
-
30
- 非 TTY 环境(管道、脚本)下同一界面降级为行读取器:选择变为键入回答、组件变为列表、不绘制任何东西。
31
-
32
- ## 开发
33
-
34
- ```sh
35
- pnpm install
36
- pnpm run dev # build → 同步进 .dev-home → 启动;秒级迭代
37
- MOCK=markdown pnpm run dev # 无 key,对着 e2e mock 模型
38
- pnpm run build # tsdown 运行时 bundle + tsc 声明文件,输出到 lib/
39
- pnpm run typecheck
40
- pnpm test # 单测(纯函数模块:editor、markdown、transcript……)
41
- pnpm run test:e2e # 打包本仓库、注册进 dsh profile,然后经管道与真实 PTY
42
- # 驱动 npm 安装的 dsh 可执行文件
43
- ```
44
-
45
- `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.*` 打点(会撕裂受管理区域),改写文件日志。
46
-
47
- e2e 测试的是发布产物:`npm pack` 的输出装进真实 profile,由 npm 上的 dsh launcher 启动,配 keyless mock 模型。那里通过的就是用户装到的。
48
-
49
- ### 对着 dsh 源码调试
50
-
51
- 日常里 dsh 是普通的 npm 依赖。需要单步进 harness 代码时,把 [deepseek-harness](https://github.com/deepseek-ai/deepseek-harness) clone 到本仓库旁边并构建(`pnpm install && pnpm run build`),然后把要调试的包指向 checkout:
52
-
53
- ```jsonc
54
- // package.json —— 调完删掉;npm 始终是默认
55
- "pnpm": {
56
- "overrides": {
57
- "@deepseek-ai/dsh-agent-loop": "link:../deepseek-harness/packages/core/agent-loop"
58
- }
59
- }
60
- ```
61
-
62
- 再执行 `pnpm install`。想进上游的改动以普通 PR 提交到 harness 仓库;本仓库绝不 fork 它。
63
-
64
- ### 跟随 dsh 发布版本
65
-
66
- codsh 消费的是已发布的 `@deepseek-ai/dsh-*` npm 包,所以"与 dsh 同步"跟踪的是 harness 的**发布**,不是合并源码。三层机制让这件事全自动:
67
-
68
- 1. **发现**——[Renovate](renovate.json) 盯着 `@deepseek-ai/dsh-*`(及同步发布的 cordis 包)自动开依赖 PR;[夜间 sync 工作流](.github/workflows/sync-dsh.yml) 不依赖第三方服务也能做同样的事。
69
- 2. **升级**——`pnpm run sync:dsh` 把所有 `@deepseek-ai/dsh-*` 范围改写到最新发布版、刷新锁文件、写 changeset;`--check` 模式在存在新版本时以退出码 1 报告(夜间任务就靠它判断)。
70
- 3. **验证**——同一命令重新核对 `cordis.patch.yml`:每个被禁用/配置的插件 id 必须仍被已安装的 dsh bundle 声明,每个 insert 的包必须可解析;然后跑 typecheck/build/单测(加 `--e2e` 会真正启动打补丁后的 bundle),CI 在生成的 PR 上再全部跑一遍。
71
-
72
- 因为所有 dsh 包都是预发布版本(`0.1.0-rc.N`),普通 semver 范围**不会**跨版本浮动——同步命令才是事实来源,`pnpm update` 不是。
73
-
74
- ## 许可
75
-
76
- MIT