zames_pro 2.80.1 → 2.80.2

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/CHANGELOG.md CHANGED
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.80.2] - 2026-10-20
11
+
12
+ ### Fixed
13
+
14
+ - wrapToWidth keeps the indentation and inner spaces of a line
15
+ - **undo:** derive the backup name hash with sha1 instead of a path prefix
16
+
10
17
  ## [2.80.1] - 2026-10-20
11
18
 
12
19
  ### Fixed
@@ -183,41 +183,67 @@ export function wrapToWidth(s, maxCols) {
183
183
  out.push('');
184
184
  continue;
185
185
  }
186
- let line = '';
187
- let lineW = 0;
188
- for (const word of raw.split(' ')) {
186
+ // A line that already fits is returned VERBATIM: wrapping is a width
187
+ // operation, not a normalizer. Splitting on spaces and re-joining ate the
188
+ // leading indentation (and emptied a line of only spaces), so an indented
189
+ // warning / echoed operator line lost its layout.
190
+ if (visLen(raw) <= width) {
191
+ out.push(raw);
192
+ continue;
193
+ }
194
+ // Wrapping is a WIDTH operation, not a normalizer: the spaces BETWEEN words
195
+ // (and the leading indentation) belong to the line and are reproduced
196
+ // verbatim. The old split(' ')/join(' ') glued the words back with ONE
197
+ // separator, so `'aaa bbb'` at width 7 became `'aaabbb'` and an indented
198
+ // warning lost its indent.
199
+ const words = raw.split(/ +/).filter((w) => w !== '');
200
+ const indent = raw.slice(0, raw.length - raw.trimStart().length);
201
+ let line = indent;
202
+ let lineW = visLen(indent);
203
+ let first = true;
204
+ for (let i = 0; i < words.length; i++) {
205
+ const word = words[i];
189
206
  const w = visLen(word);
190
207
  // Hard-split a word longer than a whole line.
191
208
  if (w > width) {
192
- if (line) {
209
+ if (!first) {
193
210
  out.push(line);
211
+ line = '';
212
+ lineW = 0;
194
213
  }
195
214
  let piece = '';
196
215
  let pieceW = 0;
197
216
  for (const ch of word) {
198
217
  const cw = charWidth(ch.codePointAt(0));
199
218
  if (pieceW + cw > width) {
200
- out.push(piece);
219
+ out.push(line + piece);
220
+ line = '';
221
+ lineW = 0;
201
222
  piece = '';
202
223
  pieceW = 0;
203
224
  }
204
225
  piece += ch;
205
226
  pieceW += cw;
206
227
  }
207
- line = piece;
208
- lineW = pieceW;
228
+ line += piece;
229
+ lineW += pieceW;
230
+ first = false;
209
231
  continue;
210
232
  }
211
- const sep = line ? 1 : 0;
212
- if (lineW + sep + w > width) {
233
+ if (!first && lineW + 1 + w > width) {
213
234
  out.push(line);
214
235
  line = word;
215
236
  lineW = w;
237
+ first = false;
238
+ continue;
216
239
  }
217
- else {
218
- line = line ? line + ' ' + word : word;
219
- lineW += sep + w;
240
+ if (!first) {
241
+ line += ' ';
242
+ lineW += 1;
220
243
  }
244
+ line += word;
245
+ lineW += w;
246
+ first = false;
221
247
  }
222
248
  out.push(line);
223
249
  }
package/dist/undo.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import fs from 'fs/promises';
2
2
  import path from 'path';
3
3
  import os from 'os';
4
- import { randomBytes } from 'crypto';
4
+ import { randomBytes, createHash } from 'crypto';
5
5
  import { writeJsonAtomic } from './fsutil.js';
6
6
  const UNDO_DIR = path.join(os.homedir(), '.zames', 'undo');
7
7
  const INDEX = path.join(UNDO_DIR, 'index.json');
@@ -42,13 +42,17 @@ export class UndoStore {
42
42
  throw e;
43
43
  }
44
44
  const stamp = Date.now();
45
- const hash = Buffer.from(filePath).toString('hex').slice(0, 16);
45
+ // The name only needs to be SHORT and unique; the backup name also
46
+ // carries a random suffix. A 16-hex prefix of the hex-encoded path is
47
+ // the first 8 BYTES of the path, which for files in one directory is
48
+ // the SAME string (and identical without the random part).
49
+ const hash = createHash('sha1').update(filePath).digest('hex').slice(0, 16);
46
50
  let backupFile = null;
47
51
  if (existed) {
48
- // The stamp is only ms-precision and the hash is the first 8 bytes of
49
- // the path, so two SIBLING files edited in the same millisecond would
50
- // collide and the second write would clobber the first (losing the
51
- // backup). Add a random suffix so a backup name is unique.
52
+ // The stamp is only ms-precision, so two edits in the same
53
+ // millisecond would share a backup name and the second write would
54
+ // clobber the first (losing the backup). Add a random suffix so a
55
+ // backup name is unique.
52
56
  const uniq = randomBytes(4).toString('hex');
53
57
  backupFile = path.join(UNDO_DIR, `${stamp}-${hash}-${uniq}.bak`);
54
58
  await fs.writeFile(backupFile, content);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zames_pro",
3
- "version": "2.80.1",
3
+ "version": "2.80.2",
4
4
  "description": "Terminal coding agent that drives a real browser to chat with DeepSeek, Qwen and other web LLMs — no API key required. Reads and edits files, runs commands, commits to git, like Claude Code / Codex CLI.",
5
5
  "type": "module",
6
6
  "bin": {