codebuff 1.0.511 → 1.0.514

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