@cheapcode/cli 0.20.3
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 +29 -0
- package/README.md +156 -0
- package/bin/cheapcode +197 -0
- package/dist/cli.mjs +10610 -0
- package/package.json +175 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
NOTICE
|
|
2
|
+
|
|
3
|
+
This repository contains code derived from Anthropic's Claude Code CLI.
|
|
4
|
+
|
|
5
|
+
The original Claude Code source is proprietary software:
|
|
6
|
+
Copyright (c) Anthropic PBC. All rights reserved.
|
|
7
|
+
Subject to Anthropic's Commercial Terms of Service.
|
|
8
|
+
|
|
9
|
+
Modifications and additions by OpenClaude contributors are offered under
|
|
10
|
+
the MIT License where legally permissible:
|
|
11
|
+
|
|
12
|
+
MIT License
|
|
13
|
+
Copyright (c) 2026 OpenClaude contributors (modifications only)
|
|
14
|
+
|
|
15
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
16
|
+
a copy of the modifications made by OpenClaude contributors, to deal
|
|
17
|
+
in those modifications without restriction, including without limitation
|
|
18
|
+
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
19
|
+
and/or sell copies, subject to the following conditions:
|
|
20
|
+
|
|
21
|
+
The above copyright notice and this permission notice shall be included
|
|
22
|
+
in all copies or substantial portions of the modifications.
|
|
23
|
+
|
|
24
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
|
|
25
|
+
|
|
26
|
+
The underlying derived code remains subject to Anthropic's copyright.
|
|
27
|
+
This project does not have Anthropic's authorization to distribute
|
|
28
|
+
their proprietary source. Users and contributors should evaluate their
|
|
29
|
+
own legal position.
|
package/README.md
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Cheap Code
|
|
2
|
+
|
|
3
|
+
Cheap Code is the coding-agent terminal for CheapVibeCode.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
Requires Node.js 22 or newer.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g @cheapcode/cli
|
|
11
|
+
cheapcode
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Run once without a global installation:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npx --yes @cheapcode/cli
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Public Windows PowerShell install:
|
|
21
|
+
|
|
22
|
+
```powershell
|
|
23
|
+
$env:CVC_API_KEY='sk-cvc-...'; iex(irm 'https://cheapvibecode.ru/cheapcode-windows')
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Public Linux/macOS install:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
bash <(curl -fsSL 'https://cheapvibecode.ru/cheapcode') 'sk-cvc-...'
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The public installer downloads the packaged CLI archive and installs the
|
|
33
|
+
`cheapcode` command globally with npm. Release
|
|
34
|
+
archives are selected through the CheapVibeCode update manifest and verified
|
|
35
|
+
with their published SHA-256 digest before installation.
|
|
36
|
+
|
|
37
|
+
For local development from the repository root on Windows PowerShell:
|
|
38
|
+
|
|
39
|
+
```powershell
|
|
40
|
+
.\scripts\install-cheapcode-cli.ps1
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
On Linux/macOS:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
bash scripts/install-cheapcode-cli.sh
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The installer checks Node.js, installs Bun if needed, installs dependencies,
|
|
50
|
+
builds `dist/cli.mjs`, and links these commands globally:
|
|
51
|
+
|
|
52
|
+
- `cheapcode`
|
|
53
|
+
|
|
54
|
+
To build the downloadable archive served by the website:
|
|
55
|
+
|
|
56
|
+
```powershell
|
|
57
|
+
.\scripts\package-cheapcode-cli.ps1
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Run
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
cheapcode
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Useful maintenance commands:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
cheapcode --help
|
|
70
|
+
cheapcode --version
|
|
71
|
+
cheapcode update
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Long sessions default to an 8 GB Node.js old-space limit. Override it with
|
|
75
|
+
either supported launcher form:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
cheapcode --max-memory=4096
|
|
79
|
+
cheapcode --max-memory 4096
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
For provider variables stored in a local env file:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
cheapcode --provider-env-file .env
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The launcher stores Cheap Code-specific CLI state in
|
|
89
|
+
`~/.cheapcode-cli` by default. Override it with
|
|
90
|
+
`CHEAPCODE_CLI_CONFIG_DIR` if needed.
|
|
91
|
+
|
|
92
|
+
## Token and context efficiency
|
|
93
|
+
|
|
94
|
+
Cheap Code defers MCP tool schemas until the model actually needs them,
|
|
95
|
+
compresses old tool results, preserves prompt-cache prefixes, and batches
|
|
96
|
+
automatic memory extraction. The default memory extractor runs once per five
|
|
97
|
+
eligible user turns instead of spawning a background agent after every turn.
|
|
98
|
+
|
|
99
|
+
Use `/config` to change **Background memory extraction**:
|
|
100
|
+
|
|
101
|
+
- `every 5 turns` (default) — balanced persistence and cost
|
|
102
|
+
- `every 10 turns` — cheaper for high-volume sessions
|
|
103
|
+
- `every turn` — maximum memory freshness, highest token use
|
|
104
|
+
- `off` — disables automatic extraction but keeps explicit memory reads and
|
|
105
|
+
writes available
|
|
106
|
+
|
|
107
|
+
For one session, the environment variable takes precedence over settings:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
CHEAPCODE_MEMORY_EXTRACTION_INTERVAL=10 cheapcode
|
|
111
|
+
CHEAPCODE_MEMORY_EXTRACTION_INTERVAL=off cheapcode
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Two curated MCP integrations ship as enabled built-in plugins. Disable either
|
|
115
|
+
from `/plugin` if it is unnecessary for a project:
|
|
116
|
+
|
|
117
|
+
- `context7-docs@builtin` — current, version-specific library documentation.
|
|
118
|
+
It uses Context7's remote OAuth endpoint; do not include confidential code in
|
|
119
|
+
documentation queries.
|
|
120
|
+
- `serena-code-intelligence@builtin` — symbol-level navigation and editing for
|
|
121
|
+
large repositories. It requires `uvx`; the plugin pins Serena rather than
|
|
122
|
+
executing a moving Git branch and disables the automatic web dashboard.
|
|
123
|
+
|
|
124
|
+
Useful diagnostics:
|
|
125
|
+
|
|
126
|
+
```text
|
|
127
|
+
/context # what currently occupies the context window
|
|
128
|
+
/ctx # compact context visualization
|
|
129
|
+
/cache-stats # prompt-cache reads, writes, and hit ratio
|
|
130
|
+
/doctor # oversized memory, agent, and MCP warnings
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Avoid enabling multiple code-indexing MCPs at once. Pick one semantic/symbolic
|
|
134
|
+
retriever and compare it against the built-in search on your real workload.
|
|
135
|
+
Third-party hooks that intercept Bash or rewrite tool output should remain
|
|
136
|
+
experimental until their interaction with tool-history compression and context
|
|
137
|
+
collapse has been measured.
|
|
138
|
+
|
|
139
|
+
## Develop
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
cd cli
|
|
143
|
+
bun install --frozen-lockfile
|
|
144
|
+
bun run build
|
|
145
|
+
node bin/cheapcode --version
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Run the complete release gate before packaging:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
bun run check
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
It covers type checking, generated integration drift, release-manifest
|
|
155
|
+
consistency, a production smoke build, the privacy guard, dead-code analysis,
|
|
156
|
+
and the full test suite.
|
package/bin/cheapcode
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Cheap Code -- coding-agent terminal for CheapVibeCode
|
|
5
|
+
*
|
|
6
|
+
* When dist/cli.mjs exists (built), run that.
|
|
7
|
+
* Otherwise, tell the user to build first or use `bun run dev`.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { existsSync } from 'fs'
|
|
11
|
+
import { join, dirname } from 'path'
|
|
12
|
+
import { fileURLToPath, pathToFileURL } from 'url'
|
|
13
|
+
import { spawnSync } from 'child_process'
|
|
14
|
+
|
|
15
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
16
|
+
const distPath = join(__dirname, '..', 'dist', 'cli.mjs')
|
|
17
|
+
|
|
18
|
+
const HEAP_RELAUNCHED_ENV = 'CHEAPCODE_HEAP_RELAUNCHED'
|
|
19
|
+
const DISABLE_HEAP_RELAUNCH_ENV = 'CHEAPCODE_DISABLE_HEAP_RELAUNCH'
|
|
20
|
+
const HEAP_SIZE_ENV = 'CHEAPCODE_NODE_MAX_OLD_SPACE_SIZE_MB'
|
|
21
|
+
const DEFAULT_HEAP_SIZE_MB = 8192
|
|
22
|
+
const CONFIG_DIR_ENV = 'CHEAPCODE_CLI_CONFIG_DIR'
|
|
23
|
+
|
|
24
|
+
if (!process.env.CHEAPCODE_CONFIG_DIR) {
|
|
25
|
+
process.env.CHEAPCODE_CONFIG_DIR =
|
|
26
|
+
process.env[CONFIG_DIR_ENV] ||
|
|
27
|
+
join(process.env.USERPROFILE || process.env.HOME || '.', '.cheapcode-cli')
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function hasNodeFlag(args, flag) {
|
|
31
|
+
return args.some(arg => arg === flag || arg.startsWith(`${flag}=`))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function hasNodeOptionFlag(flag) {
|
|
35
|
+
return hasNodeFlag([
|
|
36
|
+
...process.execArgv,
|
|
37
|
+
...(process.env.NODE_OPTIONS || '').split(/\s+/).filter(Boolean),
|
|
38
|
+
], flag)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function parsePositiveMemoryMb(rawValue) {
|
|
42
|
+
if (!/^[1-9]\d*$/.test(rawValue)) return undefined
|
|
43
|
+
|
|
44
|
+
const parsed = Number(rawValue)
|
|
45
|
+
return Number.isSafeInteger(parsed) ? parsed : undefined
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function parseLauncherArgs(args) {
|
|
49
|
+
const childArgs = []
|
|
50
|
+
let maxMemoryMb
|
|
51
|
+
let sawMaxMemory = false
|
|
52
|
+
let parsingOptions = true
|
|
53
|
+
|
|
54
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
55
|
+
const arg = args[index]
|
|
56
|
+
|
|
57
|
+
if (arg === '--') {
|
|
58
|
+
parsingOptions = false
|
|
59
|
+
childArgs.push(arg)
|
|
60
|
+
continue
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (parsingOptions && (arg === '--max-memory' || arg.startsWith('--max-memory='))) {
|
|
64
|
+
if (sawMaxMemory) {
|
|
65
|
+
return {
|
|
66
|
+
error: '--max-memory may only be specified once',
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
sawMaxMemory = true
|
|
70
|
+
|
|
71
|
+
let rawValue
|
|
72
|
+
if (arg === '--max-memory') {
|
|
73
|
+
const nextArg = args[index + 1]
|
|
74
|
+
if (nextArg === undefined || nextArg === '--' || nextArg.startsWith('--')) {
|
|
75
|
+
return {
|
|
76
|
+
error: '--max-memory requires a positive integer value in MB',
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
rawValue = nextArg
|
|
80
|
+
index += 1
|
|
81
|
+
} else {
|
|
82
|
+
rawValue = arg.slice('--max-memory='.length)
|
|
83
|
+
if (!rawValue) {
|
|
84
|
+
return {
|
|
85
|
+
error: '--max-memory requires a positive integer value in MB',
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
maxMemoryMb = parsePositiveMemoryMb(rawValue)
|
|
91
|
+
if (maxMemoryMb === undefined) {
|
|
92
|
+
return {
|
|
93
|
+
error: `--max-memory must be a positive integer in MB; received ${JSON.stringify(rawValue)}`,
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
continue
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
childArgs.push(arg)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return { childArgs, maxMemoryMb }
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const parsedLauncherArgs = parseLauncherArgs(process.argv.slice(2))
|
|
106
|
+
if (parsedLauncherArgs.error) {
|
|
107
|
+
console.error(`cheapcode: ${parsedLauncherArgs.error}`)
|
|
108
|
+
process.exit(2)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
process.argv = [
|
|
112
|
+
process.argv[0],
|
|
113
|
+
process.argv[1],
|
|
114
|
+
...parsedLauncherArgs.childArgs,
|
|
115
|
+
]
|
|
116
|
+
|
|
117
|
+
if (parsedLauncherArgs.maxMemoryMb !== undefined) {
|
|
118
|
+
const value = String(parsedLauncherArgs.maxMemoryMb)
|
|
119
|
+
process.env[HEAP_SIZE_ENV] = value
|
|
120
|
+
process.env.CHEAPCODE_MAX_MEMORY_MB = value
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function getHeapSizeMb() {
|
|
124
|
+
const raw = process.env[HEAP_SIZE_ENV]
|
|
125
|
+
if (!raw) return DEFAULT_HEAP_SIZE_MB
|
|
126
|
+
const parsed = Number.parseInt(raw, 10)
|
|
127
|
+
return Number.isSafeInteger(parsed) && parsed > 0
|
|
128
|
+
? parsed
|
|
129
|
+
: DEFAULT_HEAP_SIZE_MB
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function relaunchWithLongSessionHeapIfNeeded() {
|
|
133
|
+
if (process.env[DISABLE_HEAP_RELAUNCH_ENV] === '1') return
|
|
134
|
+
if (process.env[HEAP_RELAUNCHED_ENV] === '1') return
|
|
135
|
+
const hasHeapLimit = hasNodeOptionFlag('--max-old-space-size')
|
|
136
|
+
const hasExplicitGc = hasNodeOptionFlag('--expose-gc')
|
|
137
|
+
const hasLauncherHeapOverride = parsedLauncherArgs.maxMemoryMb !== undefined
|
|
138
|
+
if (hasHeapLimit && hasExplicitGc && !hasLauncherHeapOverride) return
|
|
139
|
+
|
|
140
|
+
const execArgv = [...process.execArgv]
|
|
141
|
+
if (!hasHeapLimit || hasLauncherHeapOverride) {
|
|
142
|
+
execArgv.push(`--max-old-space-size=${getHeapSizeMb()}`)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Expose explicit GC for long interactive sessions. NODE_OPTIONS cannot
|
|
146
|
+
// carry --expose-gc, so the executable wrapper must add it before startup.
|
|
147
|
+
if (!hasExplicitGc) {
|
|
148
|
+
execArgv.push('--expose-gc')
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Preserve the original argv[1] (which may be a symlink like
|
|
152
|
+
// /usr/local/bin/cheapcode) instead of resolving it via import.meta.url.
|
|
153
|
+
// Resolving symlinks here defeats install-type detection downstream: a real
|
|
154
|
+
// npm global install (symlink → node_modules/@cheapcode/cli/bin) would
|
|
155
|
+
// resolve to the package's real path inside node_modules, which is fine, but
|
|
156
|
+
// a `npm install -g .` dev symlink resolves back to the repo and looks like
|
|
157
|
+
// a source-tree dev run. Using argv[1] keeps the invocation path stable so
|
|
158
|
+
// doctorDiagnostic's npm-global path markers can match correctly.
|
|
159
|
+
const launcherPath = process.argv[1] || fileURLToPath(import.meta.url)
|
|
160
|
+
|
|
161
|
+
const result = spawnSync(process.execPath, [
|
|
162
|
+
...execArgv,
|
|
163
|
+
launcherPath,
|
|
164
|
+
...process.argv.slice(2),
|
|
165
|
+
], {
|
|
166
|
+
stdio: 'inherit',
|
|
167
|
+
env: {
|
|
168
|
+
...process.env,
|
|
169
|
+
[HEAP_RELAUNCHED_ENV]: '1',
|
|
170
|
+
},
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
if (result.error) {
|
|
174
|
+
console.error(`cheapcode: failed to restart with long-session heap: ${result.error.message}`)
|
|
175
|
+
process.exit(1)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
process.exit(result.status ?? 1)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (existsSync(distPath)) {
|
|
182
|
+
relaunchWithLongSessionHeapIfNeeded()
|
|
183
|
+
await import(pathToFileURL(distPath).href)
|
|
184
|
+
} else {
|
|
185
|
+
console.error(`
|
|
186
|
+
cheapcode: dist/cli.mjs not found.
|
|
187
|
+
|
|
188
|
+
Build first:
|
|
189
|
+
bun run build
|
|
190
|
+
|
|
191
|
+
Or run directly with Bun:
|
|
192
|
+
bun run dev
|
|
193
|
+
|
|
194
|
+
See README.md for setup instructions.
|
|
195
|
+
`)
|
|
196
|
+
process.exit(1)
|
|
197
|
+
}
|