@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 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
+ }