codecane 1.0.413 → 1.0.420-beta.10

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 +20 -16
  2. package/index.js +199 -62
  3. package/package.json +5 -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,20 @@ 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!
72
+
73
+ <!-- Test comment for staging workflow -->
package/index.js CHANGED
@@ -3,30 +3,41 @@
3
3
  const fs = require('fs')
4
4
  const path = require('path')
5
5
  const os = require('os')
6
- const { spawn, execSync } = require('child_process')
6
+ const { spawn } = require('child_process')
7
7
  const https = require('https')
8
8
  const zlib = require('zlib')
9
9
  const tar = require('tar')
10
10
 
11
- // Configuration
12
- const CONFIG = {
13
- homeDir: os.homedir(),
14
- configDir: path.join(os.homedir(), '.config', 'manicode'),
15
- binaryName: process.platform === 'win32' ? 'codebuff.exe' : 'codebuff',
16
- githubRepo: 'CodebuffAI/codebuff-community',
17
- userAgent: 'codebuff-cli',
18
- requestTimeout: 10000,
11
+ // Hardcoded package name for codecane
12
+ const packageName = 'codecane'
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
+ githubRepo: 'CodebuffAI/codebuff-community',
26
+ userAgent: `${packageName}-cli`,
27
+ requestTimeout: 20000,
28
+ isPrerelease: true, // codecane always looks for prereleases
29
+ }
19
30
  }
20
31
 
21
- CONFIG.binaryPath = path.join(CONFIG.configDir, CONFIG.binaryName)
32
+ const CONFIG = createConfig(packageName)
22
33
 
23
34
  // Platform target mapping
24
35
  const PLATFORM_TARGETS = {
25
- 'linux-x64': 'codebuff-linux-x64.tar.gz',
26
- 'linux-arm64': 'codebuff-linux-arm64.tar.gz',
27
- 'darwin-x64': 'codebuff-darwin-x64.tar.gz',
28
- 'darwin-arm64': 'codebuff-darwin-arm64.tar.gz',
29
- 'win32-x64': 'codebuff-win32-x64.tar.gz',
36
+ 'linux-x64': `${packageName}-linux-x64.tar.gz`,
37
+ 'linux-arm64': `${packageName}-linux-arm64.tar.gz`,
38
+ 'darwin-x64': `${packageName}-darwin-x64.tar.gz`,
39
+ 'darwin-arm64': `${packageName}-darwin-arm64.tar.gz`,
40
+ 'win32-x64': `${packageName}-win32-x64.tar.gz`,
30
41
  }
31
42
 
32
43
  // Terminal utilities
@@ -80,37 +91,60 @@ function httpGet(url, options = {}) {
80
91
  const timeout = options.timeout || CONFIG.requestTimeout
81
92
  req.setTimeout(timeout, () => {
82
93
  req.destroy()
83
- reject(new Error('Request timeout'))
94
+ reject(new Error('Request timeout.'))
84
95
  })
85
96
  })
86
97
  }
87
98
 
88
99
  async function getLatestVersion() {
89
- const res = await httpGet(
90
- `https://api.github.com/repos/${CONFIG.githubRepo}/releases/latest`
91
- )
100
+ try {
101
+ const res = await httpGet(
102
+ `https://github.com/${CONFIG.githubRepo}/releases.atom`
103
+ )
92
104
 
93
- /* ── simple rate-limit fallback ─────────────────────────── */
94
- if (
95
- res.statusCode === 403 &&
96
- res.headers['x-ratelimit-remaining'] === '0'
97
- ) {
98
- term.writeLine(
99
- 'GitHub API rate-limit reached. Skipping version check – either wait an hour or set GITHUB_TOKEN and try again.'
105
+ if (res.statusCode !== 200) return null
106
+
107
+ const body = await streamToString(res)
108
+
109
+ // Parse the Atom XML to extract releases
110
+ const tagMatches = body.match(
111
+ /<id>tag:github\.com,2008:Repository\/\d+\/([^<]+)<\/id>/g
100
112
  )
101
- return null
102
- }
103
113
 
104
- if (res.statusCode !== 200) return null // other errors
114
+ if (!tagMatches) return null
115
+
116
+ // Extract all version tags
117
+ const versions = tagMatches
118
+ .map((match) => {
119
+ const tagMatch = match.match(
120
+ /<id>tag:github\.com,2008:Repository\/\d+\/([^<]+)<\/id>/
121
+ )
122
+ return tagMatch ? tagMatch[1].replace(/^v/, '') : null
123
+ })
124
+ .filter(Boolean)
125
+
126
+ if (versions.length === 0) return null
127
+
128
+ // Filter versions based on whether we want prereleases or stable releases
129
+ const filteredVersions = versions.filter((version) => {
130
+ const isPrerelease = version.includes('-')
131
+ return CONFIG.isPrerelease === isPrerelease
132
+ })
133
+
134
+ if (filteredVersions.length === 0) return null
105
135
 
106
- const body = await streamToString(res)
107
- return JSON.parse(body).tag_name?.replace(/^v/, '') || null
136
+ // Sort and return the latest version
137
+ filteredVersions.sort(compareVersions)
138
+ return filteredVersions[filteredVersions.length - 1]
139
+ } catch (error) {
140
+ return null
141
+ }
108
142
  }
109
143
 
110
144
  function streamToString(stream) {
111
145
  return new Promise((resolve, reject) => {
112
146
  let data = ''
113
- stream.on('data', chunk => (data += chunk))
147
+ stream.on('data', (chunk) => (data += chunk))
114
148
  stream.on('end', () => resolve(data))
115
149
  stream.on('error', reject)
116
150
  })
@@ -120,33 +154,112 @@ function getCurrentVersion() {
120
154
  if (!fs.existsSync(CONFIG.binaryPath)) return null
121
155
 
122
156
  try {
123
- const result = execSync(`"${CONFIG.binaryPath}" --version`, {
124
- cwd: os.homedir(),
125
- encoding: 'utf-8',
126
- stdio: 'pipe',
127
- timeout: 1000,
157
+ return new Promise((resolve, reject) => {
158
+ const child = spawn(CONFIG.binaryPath, ['--version'], {
159
+ cwd: os.homedir(),
160
+ stdio: 'pipe',
161
+ })
162
+
163
+ let output = ''
164
+ let errorOutput = ''
165
+
166
+ child.stdout.on('data', (data) => {
167
+ output += data.toString()
168
+ })
169
+
170
+ child.stderr.on('data', (data) => {
171
+ errorOutput += data.toString()
172
+ })
173
+
174
+ const timeout = setTimeout(() => {
175
+ child.kill('SIGTERM')
176
+ setTimeout(() => {
177
+ if (!child.killed) {
178
+ child.kill('SIGKILL')
179
+ }
180
+ }, 1000)
181
+ resolve('error')
182
+ }, 1000)
183
+
184
+ child.on('exit', (code) => {
185
+ clearTimeout(timeout)
186
+ if (code === 0) {
187
+ resolve(output.trim())
188
+ } else {
189
+ resolve('error')
190
+ }
191
+ })
192
+
193
+ child.on('error', () => {
194
+ clearTimeout(timeout)
195
+ resolve('error')
196
+ })
128
197
  })
129
- return result.trim()
130
198
  } catch (error) {
131
- return null
199
+ return 'error'
132
200
  }
133
201
  }
134
202
 
135
203
  function compareVersions(v1, v2) {
136
204
  if (!v1 || !v2) return 0
137
205
 
138
- const parts1 = v1.split('.').map(Number)
139
- const parts2 = v2.split('.').map(Number)
206
+ const parseVersion = (version) => {
207
+ const parts = version.split('-')
208
+ const mainParts = parts[0].split('.').map(Number)
209
+ const prereleaseParts = parts[1] ? parts[1].split('.') : []
210
+ return { main: mainParts, prerelease: prereleaseParts }
211
+ }
212
+
213
+ const p1 = parseVersion(v1)
214
+ const p2 = parseVersion(v2)
140
215
 
141
- for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
142
- const p1 = parts1[i] || 0
143
- const p2 = parts2[i] || 0
216
+ // Compare main version parts
217
+ for (let i = 0; i < Math.max(p1.main.length, p2.main.length); i++) {
218
+ const n1 = p1.main[i] || 0
219
+ const n2 = p2.main[i] || 0
144
220
 
145
- if (p1 < p2) return -1
146
- if (p1 > p2) return 1
221
+ if (n1 < n2) return -1
222
+ if (n1 > n2) return 1
147
223
  }
148
224
 
149
- return 0
225
+ // If main versions are equal, compare prerelease parts
226
+ if (p1.prerelease.length === 0 && p2.prerelease.length === 0) {
227
+ return 0 // No prerelease, versions are equal
228
+ } else if (p1.prerelease.length === 0) {
229
+ return 1 // v1 is a release, v2 is prerelease, so v1 > v2
230
+ } else if (p2.prerelease.length === 0) {
231
+ return -1 // v2 is a release, v1 is prerelease, so v1 < v2
232
+ } else {
233
+ // Both have prerelease parts, compare them
234
+ for (
235
+ let i = 0;
236
+ i < Math.max(p1.prerelease.length, p2.prerelease.length);
237
+ i++
238
+ ) {
239
+ const pr1 = p1.prerelease[i] || ''
240
+ const pr2 = p2.prerelease[i] || ''
241
+
242
+ // Handle numeric vs. string parts
243
+ const isNum1 = !isNaN(parseInt(pr1))
244
+ const isNum2 = !isNaN(parseInt(pr2))
245
+
246
+ if (isNum1 && isNum2) {
247
+ const num1 = parseInt(pr1)
248
+ const num2 = parseInt(pr2)
249
+ if (num1 < num2) return -1
250
+ if (num1 > num2) return 1
251
+ } else if (isNum1 && !isNum2) {
252
+ return 1 // Numeric prerelease is generally higher than alpha/beta
253
+ } else if (!isNum1 && isNum2) {
254
+ return -1
255
+ } else {
256
+ // Lexicographical comparison for string parts
257
+ if (pr1 < pr2) return -1
258
+ if (pr1 > pr2) return 1
259
+ }
260
+ }
261
+ return 0 // Prerelease parts are equal
262
+ }
150
263
  }
151
264
 
152
265
  function formatBytes(bytes) {
@@ -176,6 +289,10 @@ async function downloadBinary(version) {
176
289
  // Ensure config directory exists
177
290
  fs.mkdirSync(CONFIG.configDir, { recursive: true })
178
291
 
292
+ if (fs.existsSync(CONFIG.binaryPath)) {
293
+ fs.unlinkSync(CONFIG.binaryPath)
294
+ }
295
+
179
296
  term.write('Downloading...')
180
297
 
181
298
  const res = await httpGet(downloadUrl)
@@ -213,8 +330,6 @@ async function downloadBinary(version) {
213
330
  .on('finish', resolve)
214
331
  .on('error', reject)
215
332
  })
216
- term.clearLine()
217
- console.log('Download and extract complete!')
218
333
 
219
334
  try {
220
335
  // Find the extracted binary - it should be named "codebuff" or "codebuff.exe"
@@ -230,13 +345,14 @@ async function downloadBinary(version) {
230
345
  `Binary not found after extraction. Expected: ${extractedPath}, Available files: ${files.join(', ')}`
231
346
  )
232
347
  }
233
-
234
- term.write('Starting Codebuff...')
235
348
  } catch (error) {
236
349
  term.clearLine()
237
350
  console.error(`Extraction failed: ${error.message}`)
238
351
  process.exit(1)
239
352
  }
353
+
354
+ term.clearLine()
355
+ console.log('Download complete! Starting Codecane...')
240
356
  }
241
357
 
242
358
  async function ensureBinaryExists() {
@@ -253,7 +369,7 @@ async function ensureBinaryExists() {
253
369
  } catch (error) {
254
370
  term.clearLine()
255
371
  console.error('❌ Failed to download codebuff:', error.message)
256
- console.error('Please try again later.')
372
+ console.error('Please check your internet connection and try again')
257
373
  process.exit(1)
258
374
  }
259
375
  }
@@ -261,13 +377,17 @@ async function ensureBinaryExists() {
261
377
 
262
378
  async function checkForUpdates(runningProcess, exitListener) {
263
379
  try {
264
- const currentVersion = getCurrentVersion()
380
+ const currentVersion = await getCurrentVersion()
265
381
  if (!currentVersion) return
266
382
 
267
383
  const latestVersion = await getLatestVersion()
268
384
  if (!latestVersion) return
269
385
 
270
- if (compareVersions(currentVersion, latestVersion) < 0) {
386
+ if (
387
+ // Download new version if current binary errors.
388
+ currentVersion === 'error' ||
389
+ compareVersions(currentVersion, latestVersion) < 0
390
+ ) {
271
391
  term.clearLine()
272
392
 
273
393
  // Remove the specific exit listener to prevent it from interfering with the update
@@ -293,10 +413,14 @@ async function checkForUpdates(runningProcess, exitListener) {
293
413
  await downloadBinary(latestVersion)
294
414
 
295
415
  // Restart with new binary - this replaces the current process
296
- const newChild = spawn(CONFIG.binaryPath, process.argv.slice(2), {
297
- stdio: 'inherit',
298
- detached: false,
299
- })
416
+ const newChild = spawn(
417
+ CONFIG.binaryPath,
418
+ [packageName, ...process.argv.slice(2)],
419
+ {
420
+ stdio: 'inherit',
421
+ detached: false,
422
+ }
423
+ )
300
424
 
301
425
  // Set up exit handler for the new process
302
426
  newChild.on('exit', (code) => {
@@ -312,12 +436,25 @@ async function checkForUpdates(runningProcess, exitListener) {
312
436
  }
313
437
 
314
438
  async function main() {
439
+ // Bold, bright warning for staging environment
440
+ console.log('\x1b[1m\x1b[91m' + '='.repeat(60) + '\x1b[0m')
441
+ console.log('\x1b[1m\x1b[93m❄️ CODECANE STAGING ENVIRONMENT ❄️\x1b[0m')
442
+ console.log(
443
+ '\x1b[1m\x1b[91mFOR TESTING PURPOSES ONLY - NOT FOR PRODUCTION USE\x1b[0m'
444
+ )
445
+ console.log('\x1b[1m\x1b[91m' + '='.repeat(60) + '\x1b[0m')
446
+ console.log('')
447
+
315
448
  await ensureBinaryExists()
316
449
 
317
- // Start codebuff
318
- const child = spawn(CONFIG.binaryPath, process.argv.slice(2), {
319
- stdio: 'inherit',
320
- })
450
+ // Start the binary with codecane argument
451
+ const child = spawn(
452
+ CONFIG.binaryPath,
453
+ [packageName, ...process.argv.slice(2)],
454
+ {
455
+ stdio: 'inherit',
456
+ }
457
+ )
321
458
 
322
459
  // Store reference to the exit listener so we can remove it during updates
323
460
  const exitListener = (code) => {
package/package.json CHANGED
@@ -1,11 +1,14 @@
1
1
  {
2
2
  "name": "codecane",
3
- "version": "1.0.413",
4
- "description": "AI coding agent",
3
+ "version": "1.0.420-beta.10",
4
+ "description": "AI coding agent (staging)",
5
5
  "license": "MIT",
6
6
  "bin": {
7
7
  "codecane": "index.js"
8
8
  },
9
+ "scripts": {
10
+ "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' ? 'codecane.exe' : 'codecane'); try { fs.unlinkSync(binaryPath) } catch (e) { /* ignore if file doesn't exist */ }\""
11
+ },
9
12
  "files": [
10
13
  "index.js",
11
14
  "README.md"