codebuff 0.0.1-legacy.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.
Files changed (3) hide show
  1. package/README.md +69 -0
  2. package/index.js +445 -0
  3. package/package.json +41 -0
package/README.md ADDED
@@ -0,0 +1,69 @@
1
+ # The most powerful coding agent
2
+
3
+ Codebuff is a CLI tool that writes code for you.
4
+
5
+ 1. Run `codebuff` from your project directory
6
+ 2. Tell it what to do
7
+ 3. It will read and write to files and run commands to produce the code you want
8
+
9
+ Note: Codebuff will run commands in your terminal as it deems necessary to fulfill your request.
10
+
11
+ ## Installation
12
+
13
+ To install Codebuff legacy, run:
14
+
15
+ ```bash
16
+ npm install -g codebuff@legacy
17
+ ```
18
+
19
+ (Use `sudo` if you get a permission error.)
20
+
21
+ ## Usage
22
+
23
+ After installation, you can start Codebuff by running:
24
+
25
+ ```bash
26
+ codebuff [project-directory]
27
+ ```
28
+
29
+ If no project directory is specified, Codebuff will use the current directory.
30
+
31
+ Once running, simply chat with Codebuff to say what coding task you want done.
32
+
33
+ ## Features
34
+
35
+ - Understands your whole codebase
36
+ - Creates and edits multiple files based on your request
37
+ - Can run your tests or type checker or linter; can install packages
38
+ - It's powerful: ask Codebuff to keep working until it reaches a condition and it will.
39
+
40
+ Our users regularly use Codebuff to implement new features, write unit tests, refactor code,write scripts, or give advice.
41
+
42
+ ## Knowledge Files
43
+
44
+ To unlock the full benefits of modern LLMs, we recommend storing knowledge alongside your code. Add a `knowledge.md` file anywhere in your project to provide helpful context, guidance, and tips for the LLM as it performs tasks for you.
45
+
46
+ Codebuff can fluently read and write files, so it will add knowledge as it goes. You don't need to write knowledge manually!
47
+
48
+ Some have said every change should be paired with a unit test. In 2024, every change should come with a knowledge update!
49
+
50
+ ## Tips
51
+
52
+ 1. Type '/help' or just '/' to see available commands.
53
+ 2. Create a `knowledge.md` file and collect specific points of advice. The assistant will use this knowledge to improve its responses.
54
+ 3. Type `undo` or `redo` to revert or reapply file changes from the conversation.
55
+ 4. Press `Esc` or `Ctrl+C` while Codebuff is generating a response to stop it.
56
+
57
+ ## Troubleshooting
58
+
59
+ If you are getting permission errors during installation, try using sudo:
60
+
61
+ ```
62
+ sudo npm install -g codebuff
63
+ ```
64
+
65
+ If you still have errors, it's a good idea to [reinstall Node](https://nodejs.org/en/download).
66
+
67
+ ## Feedback
68
+
69
+ We value your input! Please email your feedback to `founders@codebuff.com`. Thank you for using Codebuff!
package/index.js ADDED
@@ -0,0 +1,445 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { spawn } = require('child_process')
4
+ const fs = require('fs')
5
+ const https = require('https')
6
+ const os = require('os')
7
+ const path = require('path')
8
+ const zlib = require('zlib')
9
+
10
+ const { Command } = require('commander')
11
+ const tar = require('tar')
12
+
13
+ const CONFIG = {
14
+ homeDir: os.homedir(),
15
+ configDir: path.join(os.homedir(), '.config', 'manicode'),
16
+ binaryName: process.platform === 'win32' ? 'codebuff.exe' : 'codebuff',
17
+ githubRepo: 'CodebuffAI/codebuff-community',
18
+ userAgent: 'codebuff-cli',
19
+ requestTimeout: 20000,
20
+ }
21
+
22
+ CONFIG.binaryPath = path.join(CONFIG.configDir, CONFIG.binaryName)
23
+
24
+ // Platform target mapping
25
+ const PLATFORM_TARGETS = {
26
+ 'linux-x64': 'codebuff-linux-x64.tar.gz',
27
+ 'linux-arm64': 'codebuff-linux-arm64.tar.gz',
28
+ 'darwin-x64': 'codebuff-darwin-x64.tar.gz',
29
+ 'darwin-arm64': 'codebuff-darwin-arm64.tar.gz',
30
+ 'win32-x64': 'codebuff-win32-x64.tar.gz',
31
+ }
32
+
33
+ // Terminal utilities
34
+ let isPrintMode = false
35
+ const term = {
36
+ clearLine: () => {
37
+ if (!isPrintMode && process.stderr.isTTY) {
38
+ process.stderr.write('\r\x1b[K')
39
+ }
40
+ },
41
+ write: (text) => {
42
+ if (!isPrintMode) {
43
+ term.clearLine()
44
+ process.stderr.write(text)
45
+ }
46
+ },
47
+ writeLine: (text) => {
48
+ if (!isPrintMode) {
49
+ term.clearLine()
50
+ process.stderr.write(text + '\n')
51
+ }
52
+ },
53
+ }
54
+
55
+ // Utility functions
56
+ function httpGet(url, options = {}) {
57
+ return new Promise((resolve, reject) => {
58
+ const parsedUrl = new URL(url)
59
+ const reqOptions = {
60
+ hostname: parsedUrl.hostname,
61
+ path: parsedUrl.pathname + parsedUrl.search,
62
+ headers: {
63
+ 'User-Agent': CONFIG.userAgent,
64
+ ...options.headers,
65
+ },
66
+ }
67
+
68
+ // Add GitHub token if available
69
+ const token = process.env.GITHUB_TOKEN
70
+ if (token) {
71
+ reqOptions.headers.Authorization = `Bearer ${token}`
72
+ }
73
+
74
+ const req = https.get(reqOptions, (res) => {
75
+ if (res.statusCode === 302 || res.statusCode === 301) {
76
+ return httpGet(new URL(res.headers.location, url).href, options)
77
+ .then(resolve)
78
+ .catch(reject)
79
+ }
80
+ resolve(res)
81
+ })
82
+
83
+ req.on('error', reject)
84
+
85
+ const timeout = options.timeout || CONFIG.requestTimeout
86
+ req.setTimeout(timeout, () => {
87
+ req.destroy()
88
+ reject(new Error('Request timeout.'))
89
+ })
90
+ })
91
+ }
92
+
93
+ async function getLatestLegacyVersion() {
94
+ try {
95
+ const res = await httpGet(
96
+ `https://github.com/${CONFIG.githubRepo}/releases.atom`,
97
+ )
98
+
99
+ if (res.statusCode !== 200) return null
100
+
101
+ const body = await streamToString(res)
102
+
103
+ // Parse the Atom XML to extract the latest release tag
104
+ const tagMatch = body.match(
105
+ /<id>tag:github\.com,2008:Repository\/\d+\/([^<]+)-legacy.\d+<\/id>/,
106
+ )
107
+ if (tagMatch && tagMatch[1]) {
108
+ return tagMatch[1].replace(/^v/, '')
109
+ }
110
+
111
+ return null
112
+ } catch (error) {
113
+ return null
114
+ }
115
+ }
116
+
117
+ function streamToString(stream) {
118
+ return new Promise((resolve, reject) => {
119
+ let data = ''
120
+ stream.on('data', (chunk) => (data += chunk))
121
+ stream.on('end', () => resolve(data))
122
+ stream.on('error', reject)
123
+ })
124
+ }
125
+
126
+ function getCurrentVersion() {
127
+ return new Promise((resolve, reject) => {
128
+ try {
129
+ if (!fs.existsSync(CONFIG.binaryPath)) {
130
+ resolve('error')
131
+ return
132
+ }
133
+
134
+ const child = spawn(CONFIG.binaryPath, ['--version'], {
135
+ cwd: os.homedir(),
136
+ stdio: 'pipe',
137
+ })
138
+
139
+ let output = ''
140
+ let errorOutput = ''
141
+
142
+ child.stdout.on('data', (data) => {
143
+ output += data.toString()
144
+ })
145
+
146
+ child.stderr.on('data', (data) => {
147
+ errorOutput += data.toString()
148
+ })
149
+
150
+ const timeout = setTimeout(() => {
151
+ child.kill('SIGTERM')
152
+ setTimeout(() => {
153
+ if (!child.killed) {
154
+ child.kill('SIGKILL')
155
+ }
156
+ }, 1000)
157
+ resolve('error')
158
+ }, 1000)
159
+
160
+ child.on('exit', (code) => {
161
+ clearTimeout(timeout)
162
+ if (code === 0) {
163
+ resolve(output.trim())
164
+ } else {
165
+ resolve('error')
166
+ }
167
+ })
168
+
169
+ child.on('error', () => {
170
+ clearTimeout(timeout)
171
+ resolve('error')
172
+ })
173
+ } catch (error) {
174
+ resolve('error')
175
+ }
176
+ })
177
+ }
178
+
179
+ function compareVersions(v1, v2) {
180
+ if (!v1 || !v2) return 0
181
+
182
+ const parts1 = v1.split('.').map(Number)
183
+ const parts2 = v2.split('.').map(Number)
184
+
185
+ for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
186
+ const p1 = parts1[i] || 0
187
+ const p2 = parts2[i] || 0
188
+
189
+ if (p1 < p2) return -1
190
+ if (p1 > p2) return 1
191
+ }
192
+
193
+ return 0
194
+ }
195
+
196
+ function formatBytes(bytes) {
197
+ if (bytes === 0) return '0 B'
198
+ const k = 1024
199
+ const sizes = ['B', 'KB', 'MB', 'GB']
200
+ const i = Math.floor(Math.log(bytes) / Math.log(k))
201
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]
202
+ }
203
+
204
+ function createProgressBar(percentage, width = 30) {
205
+ const filled = Math.round((width * percentage) / 100)
206
+ const empty = width - filled
207
+ return '[' + '█'.repeat(filled) + '░'.repeat(empty) + ']'
208
+ }
209
+
210
+ async function downloadBinary(version) {
211
+ const platformKey = `${process.platform}-${process.arch}`
212
+ const fileName = PLATFORM_TARGETS[platformKey]
213
+
214
+ if (!fileName) {
215
+ throw new Error(`Unsupported platform: ${process.platform} ${process.arch}`)
216
+ }
217
+
218
+ // Use proxy endpoint that handles version mapping
219
+ const downloadUrl = process.env.NEXT_PUBLIC_CODEBUFF_APP_URL
220
+ ? `${process.env.NEXT_PUBLIC_CODEBUFF_APP_URL}/api/releases/download/${version}/${fileName}`
221
+ : `https://codebuff.com/api/releases/download/${version}/${fileName}`
222
+
223
+ // Ensure config directory exists
224
+ fs.mkdirSync(CONFIG.configDir, { recursive: true })
225
+
226
+ if (fs.existsSync(CONFIG.binaryPath)) {
227
+ fs.unlinkSync(CONFIG.binaryPath)
228
+ }
229
+
230
+ term.write('Downloading...')
231
+
232
+ const res = await httpGet(downloadUrl)
233
+
234
+ if (res.statusCode !== 200) {
235
+ throw new Error(`Download failed: HTTP ${res.statusCode}`)
236
+ }
237
+
238
+ const totalSize = parseInt(res.headers['content-length'] || '0', 10)
239
+ let downloadedSize = 0
240
+ let lastProgressTime = Date.now()
241
+
242
+ res.on('data', (chunk) => {
243
+ downloadedSize += chunk.length
244
+ const now = Date.now()
245
+ if (now - lastProgressTime >= 100 || downloadedSize === totalSize) {
246
+ lastProgressTime = now
247
+ if (totalSize > 0) {
248
+ const pct = Math.round((downloadedSize / totalSize) * 100)
249
+ term.write(
250
+ `Downloading... ${createProgressBar(pct)} ${pct}% of ${formatBytes(
251
+ totalSize,
252
+ )}`,
253
+ )
254
+ } else {
255
+ term.write(`Downloading... ${formatBytes(downloadedSize)}`)
256
+ }
257
+ }
258
+ })
259
+
260
+ await new Promise((resolve, reject) => {
261
+ res
262
+ .pipe(zlib.createGunzip())
263
+ .pipe(tar.x({ cwd: CONFIG.configDir }))
264
+ .on('finish', resolve)
265
+ .on('error', reject)
266
+ })
267
+
268
+ try {
269
+ // Find the extracted binary - it should be named "codebuff" or "codebuff.exe"
270
+ const files = fs.readdirSync(CONFIG.configDir)
271
+ const extractedPath = path.join(CONFIG.configDir, CONFIG.binaryName)
272
+
273
+ if (fs.existsSync(extractedPath)) {
274
+ if (process.platform !== 'win32') {
275
+ fs.chmodSync(extractedPath, 0o755)
276
+ }
277
+ } else {
278
+ throw new Error(
279
+ `Binary not found after extraction. Expected: ${extractedPath}, Available files: ${files.join(', ')}`,
280
+ )
281
+ }
282
+ } catch (error) {
283
+ term.clearLine()
284
+ if (!isPrintMode) {
285
+ console.error(`Extraction failed: ${error.message}`)
286
+ }
287
+ process.exit(1)
288
+ }
289
+
290
+ term.clearLine()
291
+ if (isPrintMode) {
292
+ console.log(
293
+ JSON.stringify({ type: 'download', version, status: 'complete' }),
294
+ )
295
+ } else {
296
+ console.log('Download complete! Starting Codebuff...')
297
+ }
298
+ }
299
+
300
+ async function ensureBinaryExists() {
301
+ const currentVersion = await getCurrentVersion()
302
+ if (currentVersion !== null && currentVersion !== 'error') {
303
+ return
304
+ }
305
+
306
+ const version = await getLatestLegacyVersion()
307
+ if (!version) {
308
+ if (isPrintMode) {
309
+ console.error(
310
+ JSON.stringify({
311
+ type: 'error',
312
+ message: 'Failed to determine latest version.',
313
+ }),
314
+ )
315
+ } else {
316
+ console.error('❌ Failed to determine latest version')
317
+ console.error('Please check your internet connection and try again')
318
+ }
319
+ process.exit(1)
320
+ }
321
+
322
+ try {
323
+ await downloadBinary(version)
324
+ } catch (error) {
325
+ term.clearLine()
326
+ if (isPrintMode) {
327
+ console.error(
328
+ JSON.stringify({
329
+ type: 'error',
330
+ message: `Failed to download codebuff: ${error.message}`,
331
+ }),
332
+ )
333
+ } else {
334
+ console.error('❌ Failed to download codebuff:', error.message)
335
+ console.error('Please check your internet connection and try again')
336
+ }
337
+ process.exit(1)
338
+ }
339
+ }
340
+
341
+ async function checkForUpdates(runningProcess, exitListener, retry) {
342
+ try {
343
+ const currentVersion = await getCurrentVersion()
344
+
345
+ const latestVersion = await getLatestLegacyVersion()
346
+ if (!latestVersion) return
347
+
348
+ if (
349
+ // Download new version if current binary errors.
350
+ currentVersion === 'error' ||
351
+ compareVersions(currentVersion, latestVersion) < 0
352
+ ) {
353
+ term.clearLine()
354
+
355
+ // Remove the specific exit listener to prevent it from interfering with the update
356
+ runningProcess.removeListener('exit', exitListener)
357
+
358
+ // Kill the running process
359
+ runningProcess.kill('SIGTERM')
360
+
361
+ // Wait for the process to actually exit
362
+ await new Promise((resolve) => {
363
+ runningProcess.on('exit', resolve)
364
+ // Fallback timeout in case the process doesn't exit gracefully
365
+ setTimeout(() => {
366
+ if (!runningProcess.killed) {
367
+ runningProcess.kill('SIGKILL')
368
+ }
369
+ resolve()
370
+ }, 5000)
371
+ })
372
+
373
+ if (!isPrintMode) {
374
+ console.log(`Update available: ${currentVersion} → ${latestVersion}`)
375
+ }
376
+
377
+ await downloadBinary(latestVersion)
378
+
379
+ await retry(isPrintMode)
380
+ }
381
+ } catch (error) {
382
+ // Silently ignore update check errors
383
+ }
384
+ }
385
+
386
+ async function main(firstRun = false, printMode = false) {
387
+ isPrintMode = printMode
388
+ await ensureBinaryExists()
389
+
390
+ let error = null
391
+ try {
392
+ // Start codebuff
393
+ const child = spawn(CONFIG.binaryPath, process.argv.slice(2), {
394
+ stdio: 'inherit',
395
+ })
396
+
397
+ // Store reference to the exit listener so we can remove it during updates
398
+ const exitListener = (code) => {
399
+ process.exit(code || 0)
400
+ }
401
+
402
+ child.on('exit', exitListener)
403
+
404
+ if (firstRun) {
405
+ // Check for updates in background
406
+ setTimeout(() => {
407
+ if (!error) {
408
+ checkForUpdates(child, exitListener, () => main(false, isPrintMode))
409
+ }
410
+ }, 100)
411
+ }
412
+ } catch (err) {
413
+ error = err
414
+ if (firstRun) {
415
+ if (!isPrintMode) {
416
+ console.error('❌ Codebuff failed to start:', error.message)
417
+ console.log('Redownloading Codebuff...')
418
+ }
419
+ // Binary could be corrupted (killed before download completed), so delete and retry.
420
+ fs.unlinkSync(CONFIG.binaryPath)
421
+ await main(false, isPrintMode)
422
+ }
423
+ }
424
+ }
425
+
426
+ // Setup commander
427
+ const program = new Command()
428
+ program
429
+ .name('codebuff')
430
+ .description('AI coding agent')
431
+ .helpOption(false)
432
+ .option('-p, --print', 'print mode - suppress wrapper output')
433
+ .allowUnknownOption()
434
+ .parse()
435
+
436
+ const options = program.opts()
437
+ isPrintMode = options.print
438
+
439
+ // Run the main function
440
+ main(true, isPrintMode).catch((error) => {
441
+ if (!isPrintMode) {
442
+ console.error('❌ Unexpected error:', error.message)
443
+ }
444
+ process.exit(1)
445
+ })
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "codebuff",
3
+ "version": "0.0.1-legacy.0",
4
+ "description": "AI coding agent",
5
+ "license": "MIT",
6
+ "bin": {
7
+ "codebuff": "index.js",
8
+ "cb": "index.js"
9
+ },
10
+ "scripts": {
11
+ "preuninstall": "node -e \"const fs = require('fs'); const path = require('path'); const os = require('os'); const binaryPath = path.join(os.homedir(), '.config', 'manicode', process.platform === 'win32' ? 'codebuff.exe' : 'codebuff'); try { fs.unlinkSync(binaryPath) } catch (e) { /* ignore if file doesn't exist */ }\""
12
+ },
13
+ "files": [
14
+ "index.js",
15
+ "README.md"
16
+ ],
17
+ "os": [
18
+ "darwin",
19
+ "linux",
20
+ "win32"
21
+ ],
22
+ "cpu": [
23
+ "x64",
24
+ "arm64"
25
+ ],
26
+ "engines": {
27
+ "node": ">=16"
28
+ },
29
+ "dependencies": {
30
+ "commander": "^12.0.0",
31
+ "tar": "^6.2.0"
32
+ },
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "https://github.com/CodebuffAI/codebuff.git"
36
+ },
37
+ "homepage": "https://codebuff.com",
38
+ "publishConfig": {
39
+ "access": "public"
40
+ }
41
+ }