zames_pro 2.96.0 → 2.96.1

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,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ### Fixed
11
+
12
+ - **release-policy:** one pair of parens in the BREAKING note
13
+ - **changelog:** a group heading must start a line, not sit inside a bullet
14
+ - **backlog:** ignore a fenced section header when appending
15
+ - **dev-watch:** follow index.ts's hot-reloadable list (restore-history, input/layout)
16
+ - **shell:** block a recursive rm of ./ and ../ (trailing slash)
17
+
18
+ ## [2.96.1] - 2026-11-06
19
+
20
+ ### Fixed
21
+
22
+ - **release-policy:** one pair of parens in the BREAKING note
23
+ - **changelog:** a group heading must start a line, not sit inside a bullet
24
+ - **backlog:** ignore a fenced section header when appending
25
+ - **dev-watch:** follow index.ts's hot-reloadable list (restore-history, input/layout)
26
+ - **shell:** block a recursive rm of ./ and ../ (trailing slash)
27
+
10
28
  ## [2.96.0] - 2026-11-05
11
29
 
12
30
  ### Added
package/dist/backlog.js CHANGED
@@ -191,8 +191,25 @@ export function appendBacklogItem(text, item) {
191
191
  }
192
192
  const lines = src.split(NL);
193
193
  const headerRe = new RegExp('^##[ ]+' + prio + '(?![0-9])');
194
+ // A fenced code block is MARKDOWN, not structure: a note that documents the
195
+ // file format contains `## P1` inside ```…```, and treating it as a section
196
+ // header inserted the new item INTO that fence (parseBacklogItems skips
197
+ // fenced headings, so the item then became invisible to /improve). Track the
198
+ // fence exactly like the parser does.
199
+ let fence = '';
194
200
  let at = -1;
195
201
  for (let i = 0; i < lines.length; i++) {
202
+ const fm = /^\s*(`{3,}|~{3,})/.exec(lines[i]);
203
+ if (fm) {
204
+ const mark = fm[1][0];
205
+ if (!fence)
206
+ fence = mark;
207
+ else if (mark === fence)
208
+ fence = '';
209
+ continue;
210
+ }
211
+ if (fence)
212
+ continue;
196
213
  if (headerRe.test(lines[i])) {
197
214
  // Insert right after the header and any blank lines that follow it, so
198
215
  // the newest note sits at the top of its priority block.
package/dist/changelog.js CHANGED
@@ -91,31 +91,29 @@ export function syncUnreleased(changelog, body) {
91
91
  const rest = changelog.slice(afterAnchor);
92
92
  const next = rest.search(/\n## \[/);
93
93
  const tail = next < 0 ? '' : rest.slice(next);
94
- const cleanBody = String(body || '')
94
+ // A `## ` that does not START a line is FREE TEXT inside a bullet (a commit
95
+ // description that documents a markdown heading, `ignore a ## P<n> header`).
96
+ // Matching it ANYWHERE treated that text as a group heading: the bullet was
97
+ // split and a FAKE `### P<n>` section appeared in the file. Every group
98
+ // heading is therefore recognized on WHOLE LINES only.
99
+ const lines = String(body || '')
95
100
  .replace(/^## \[Unreleased\]\s*/m, '')
96
- // A drafted group heading must START a line of its own. When the draft
97
- // carries several groups (`## Added` … `## Fixed`) without a blank line
98
- // between them, the next group used to stay glued to the last bullet
99
- // (`- a### Fixed`) — it was not a heading at all, so the item vanished
100
- // from the rendered changelog. The lookahead is what keeps a CONTINUATION
101
- // line of a multi-line bullet in place: gluing on `\n` as well joined the
102
- // indent line to the next bullet and truncated the note.
103
- // The draft's groups are still `## ` at this point (they become `### `
104
- // below), and the FIRST group is already preceded by the blank line of
105
- // the draft — so a group always starts a line, and this rule only has to
106
- // punch in the missing blank line after a bullet.
107
- .replace(/([^\n])(?=## )/g, '$1' + nl + nl)
108
- .trim()
109
- // The draft renders groups as `## Added` (top-level), but INSIDE the file
110
- // a version's groups are `### Added` (a subsection of the version heading).
111
- // `## [` (the next version) is left alone. The FIRST group follows the
112
- // version heading directly (no bullet before it), so the blank-line rule
113
- // above never fired for it — `## [Unreleased]` came out as a bare `#`
114
- // followed by `# Added`. The heading owns a blank line, always.
101
+ .split(nl);
102
+ const out = [];
103
+ for (const line of lines) {
104
+ // A heading glued to the previous bullet (`- one## Added`) does not render
105
+ // as a heading at all: break it onto its own line, keeping the bullet.
106
+ const glued = /^(?!#)(\S.*[^\s`])(## .*)$/.exec(line);
107
+ if (glued)
108
+ out.push(glued[1], '', glued[2]);
109
+ else
110
+ out.push(line);
111
+ }
112
+ const cleanBody = out
113
+ .join(nl)
115
114
  .replace(/^## (?!\[)/gm, '### ')
116
- // One blank line between the version heading and its first group, not
117
- // the three the rules above can stack up.
118
- .replace(/^\n+(?=### )/gm, '');
115
+ .replace(/^\n+(?=### )/, '')
116
+ .trim();
119
117
  const section = cleanBody ? nl + nl + cleanBody + nl : nl;
120
118
  return changelog.slice(0, afterAnchor) + section + tail;
121
119
  }
package/dist/dev-watch.js CHANGED
@@ -28,6 +28,12 @@ export const HOT_RELOADABLE = new Set([
28
28
  'config',
29
29
  'fsutil',
30
30
  'backlog',
31
+ // index.ts reloads these too; leaving them out of THIS list made the dev
32
+ // watcher report a false "restart zames" warning on every edit to them.
33
+ 'restore-history',
34
+ // A NESTED module: only its basename is known here, which is what the
35
+ // watcher compares (src/input/layout.ts).
36
+ 'layout',
31
37
  'commands',
32
38
  'run-task',
33
39
  'attach-refs',
@@ -67,9 +67,13 @@ function kindsOf(list) {
67
67
  }
68
68
  function breakingNote(list) {
69
69
  const first = list[0];
70
+ // One pair of parentheses around the WHOLE label: the scope used to close
71
+ // its own paren (`(cli): x)`) and leave a stray `)` in the operator report.
72
+ const label = first.scope
73
+ ? first.scope + ': ' + first.description
74
+ : first.description;
70
75
  return ('a BREAKING change (' +
71
- (first.scope ? first.scope + '): ' : '') +
72
- first.description +
76
+ label +
73
77
  ') must be released by the operator, not by an unattended /loop');
74
78
  }
75
79
  /**
package/dist/shell.js CHANGED
@@ -88,13 +88,20 @@ export function assertCommandInsideRoot(root, command) {
88
88
  const raw = w.replace(/["']/g, '');
89
89
  if (!raw)
90
90
  continue;
91
- if (raw === '/' ||
92
- raw === '.' ||
93
- raw === '..' ||
94
- raw === '~' ||
95
- raw.startsWith('~/') ||
96
- raw.startsWith('/') ||
97
- raw.startsWith('..')) {
91
+ // A shell NORMALIZES a trailing separator before running the command:
92
+ // `rm -rf ./` and `rm -rf ../` delete the project root and its parent,
93
+ // but the raw word is neither `.` nor `..`, so the checks below let it
94
+ // through. Compare without the trailing slashes (a bare `//` is the
95
+ // root on POSIX and must stay one).
96
+ const norm = raw.replace(/\/+$/, '');
97
+ const bare = norm === '' ? raw.charAt(0) : norm;
98
+ if (bare === '/' ||
99
+ bare === '.' ||
100
+ bare === '..' ||
101
+ bare === '~' ||
102
+ bare.startsWith('~/') ||
103
+ bare.startsWith('/') ||
104
+ bare.startsWith('..')) {
98
105
  throw new Error('Sandbox: recursive rm of an absolute/outside path is forbidden (' +
99
106
  raw +
100
107
  ')');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zames_pro",
3
- "version": "2.96.0",
3
+ "version": "2.96.1",
4
4
  "description": "Terminal coding agent that drives a real browser to chat with DeepSeek, Qwen or GLM — 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": {