zames_pro 1.0.1 → 1.0.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/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # zames_pro
2
2
 
3
+ ![zames logo](logo.png)
4
+
3
5
  Terminal coding agent, работающий поверх chat.deepseek.com через Playwright.
4
6
  По духу похож на Claude Code / Codex CLI: запускается в текущей директории, читает и правит файлы, запускает команды, коммитит в git.
5
7
 
@@ -33,6 +35,7 @@ zames --task <текст задачи>
33
35
  zames --headless
34
36
  zames --debug
35
37
  zames --dir <путь>
38
+ zames --version показать версию
36
39
  zames --help
37
40
  ```
38
41
 
@@ -43,6 +46,6 @@ zames --help
43
46
 
44
47
  Данные агента хранятся в `~/.zames`: профиль браузера, логи, история undo, снапшоты self-review.
45
48
 
46
- ## Лицензия
49
+ ## Лицензия и авторство
47
50
 
48
51
  MIT
package/logo.png ADDED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zames_pro",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Terminal coding agent over chat.deepseek.com via Playwright",
5
5
  "type": "module",
6
6
  "bin": {
@@ -9,7 +9,8 @@
9
9
  "files": [
10
10
  "src",
11
11
  "README.md",
12
- "LICENSE"
12
+ "LICENSE",
13
+ "logo.png"
13
14
  ],
14
15
  "scripts": {
15
16
  "start": "node ./src/index.js"
package/src/agent-loop.js CHANGED
@@ -175,6 +175,68 @@ function tryParseArray(str) {
175
175
  }
176
176
  }
177
177
 
178
+ // Жадный разбор args для грязного JSON (незаэкранированные кавычки в строке).
179
+ function parseArgsGreedy(str) {
180
+ const result = {}
181
+ let i = str.indexOf('{') + 1
182
+ if (i === 0) return null
183
+ const skipWs = () => {
184
+ while (i < str.length && /[\s,]/.test(str[i])) i++
185
+ }
186
+ while (i < str.length) {
187
+ skipWs()
188
+ if (i >= str.length || str[i] === '}') break
189
+ if (str[i] !== '"') return null
190
+ const keyEnd = str.indexOf('"', i + 1)
191
+ if (keyEnd === -1) return null
192
+ const key = str.slice(i + 1, keyEnd)
193
+ i = keyEnd + 1
194
+ while (i < str.length && /\s/.test(str[i])) i++
195
+ if (str[i] !== ':') return null
196
+ i++
197
+ while (i < str.length && /\s/.test(str[i])) i++
198
+ if (str[i] === '"') {
199
+ let lastEnd = -1
200
+ let k = i + 1
201
+ while (k < str.length) {
202
+ if (str.charCodeAt(k) === 92) { k += 2; continue }
203
+ if (str[k] === '"') {
204
+ let t = k + 1
205
+ while (t < str.length && /\s/.test(str[t])) t++
206
+ if (t >= str.length || str[t] === ',' || str[t] === '}') lastEnd = k
207
+ }
208
+ k++
209
+ }
210
+ if (lastEnd === -1) return null
211
+ result[key] = unescapeValue(str.slice(i + 1, lastEnd))
212
+ i = lastEnd + 1
213
+ continue
214
+ }
215
+ if (str[i] === '{' || str[i] === '[') {
216
+ const open = str[i]
217
+ const close = open === '{' ? '}' : ']'
218
+ const end = findMatching(str, i, open, close)
219
+ if (end === -1) return null
220
+ let parsed = null
221
+ try { parsed = JSON.parse(str.slice(i, end + 1)) }
222
+ catch { if (open === '{') parsed = parseArgsPermissive(str.slice(i, end + 1)) }
223
+ if (parsed === null) return null
224
+ result[key] = parsed
225
+ i = end + 1
226
+ continue
227
+ }
228
+ let j = i
229
+ while (j < str.length && !/[,}]/.test(str[j])) j++
230
+ const raw = str.slice(i, j).trim()
231
+ if (raw === 'true') result[key] = true
232
+ else if (raw === 'false') result[key] = false
233
+ else if (raw === 'null') result[key] = null
234
+ else { const n = Number(raw); result[key] = Number.isNaN(n) ? raw : n }
235
+ i = j
236
+ }
237
+ return result
238
+ }
239
+
178
240
  function parseArgsPermissive(str) {
179
241
  const result = {}
180
242
  let i = 1
@@ -225,7 +287,22 @@ function parseArgsPermissive(str) {
225
287
  result[key] = unescapeValue(value)
226
288
  i = j + 1
227
289
  } else if (str[i] === '{' || str[i] === '[') {
228
- return null
290
+ // Вложенный объект/массив: находим сбалансированный фрагмент и
291
+ // пытаемся распарсить его как JSON (или как вложенный permissive).
292
+ const open = str[i]
293
+ const close = open === '{' ? '}' : ']'
294
+ const end = findMatching(str, i, open, close)
295
+ if (end === -1) return null
296
+ const frag = str.slice(i, end + 1)
297
+ let parsedFrag = null
298
+ try {
299
+ parsedFrag = JSON.parse(frag)
300
+ } catch {
301
+ if (open === '{') parsedFrag = parseArgsPermissive(frag)
302
+ }
303
+ if (parsedFrag === null) return null
304
+ result[key] = parsedFrag
305
+ i = end + 1
229
306
  } else {
230
307
  let j = i
231
308
  while (j < str.length && !/[,}]/.test(str[j])) j++
@@ -308,15 +385,51 @@ function parseToolCallPermissive(text) {
308
385
  const openIdx = text.indexOf('{', argsIdx)
309
386
  if (openIdx === -1) return null
310
387
 
311
- for (let i = openIdx + 1; i < text.length; i++) {
312
- if (text[i] !== '}') continue
313
- const argsStr = text.slice(openIdx, i + 1)
314
- const args = parseArgsPermissive(argsStr)
315
- if (args) return { tool, args }
388
+ // Берём сбалансированный по скобкам фрагмент args (с учётом строк),
389
+ // а не первый попавшийся '}'. Иначе вложенные объекты/массивы или
390
+ // фигурные скобки внутри строк ломают разбор.
391
+ const endIdx = findMatching(text, openIdx, '{', '}')
392
+ if (endIdx === -1) return null
393
+
394
+ const argsStr = text.slice(openIdx, endIdx + 1)
395
+ if (tool === 'Edit') {
396
+ const e = parseEditArgs(argsStr)
397
+ if (e) return { tool, args: e }
316
398
  }
399
+ const args = parseArgsPermissive(argsStr)
400
+ if (args) return { tool, args }
401
+ const greedy = parseArgsGreedy(argsStr)
402
+ if (greedy) return { tool, args: greedy }
317
403
  return null
318
404
  }
319
405
 
406
+ // Специализированный разбор args для Edit: ровно три поля path/old_string/
407
+ // new_string, значения могут содержать сырые кавычки и переводы строк.
408
+ function parseEditArgs(str) {
409
+ const keyRe = (name) => new RegExp('"' + name + '"\\s*:\\s*"')
410
+ const readValue = (name, nextNames) => {
411
+ const m = keyRe(name).exec(str)
412
+ if (!m) return null
413
+ const start = m.index + m[0].length
414
+ let end = str.length
415
+ for (const n of nextNames) {
416
+ const mm = keyRe(n).exec(str.slice(start))
417
+ if (mm) {
418
+ const absIdx = start + mm.index
419
+ if (absIdx < end) end = absIdx
420
+ }
421
+ }
422
+ let val = str.slice(start, end)
423
+ val = val.replace(/"\s*[,}]?\s*$/, '')
424
+ return val
425
+ }
426
+ const path = readValue('path', ['old_string', 'new_string'])
427
+ const oldStr = readValue('old_string', ['new_string'])
428
+ const newStr = readValue('new_string', [])
429
+ if (path === null || oldStr === null || newStr === null) return null
430
+ return { path: path, old_string: oldStr, new_string: newStr }
431
+ }
432
+
320
433
  function extractJsonObjects(text) {
321
434
  const objects = []
322
435
  let i = 0
@@ -403,6 +516,17 @@ function parseToolCall(text) {
403
516
  const permissive = parseToolCallPermissive(cleaned)
404
517
  if (permissive) return { ...permissive, _permissive: true }
405
518
 
519
+ // Fallback: модель могла обернуть JSON в прозу и/или добавить мусорные
520
+ // теги после него. Отрезаем всё до первого '"tool"' и всё, что похоже
521
+ // на XML/DSML-хвост, затем пробуем permissive-разбор ещё раз.
522
+ const toolIdx = cleaned.search(/["']?tool["']?\s:/)
523
+ if (toolIdx > 0) {
524
+ let tail = cleaned.slice(toolIdx)
525
+ tail = tail.replace(/<[^>]>.*$/s, '').trim()
526
+ const tailPermissive = parseToolCallPermissive('{"' + tail)
527
+ if (tailPermissive) return { ...tailPermissive, _permissive: true }
528
+ }
529
+
406
530
  return null
407
531
  }
408
532
 
@@ -62,6 +62,16 @@ Inside that string:
62
62
  - OR avoid quotes entirely. For Windows paths use forward slashes.
63
63
  - Pipes, redirects and chained commands work: "cd dir && dir /b"
64
64
 
65
+ ## CRITICAL: output format
66
+
67
+ - Your ENTIRE response MUST be exactly ONE JSON object or ONE JSON array.
68
+ - Do NOT write any text before or after the JSON. No explanations, no greetings, no plans.
69
+ - Do NOT wrap JSON in markdown fences.
70
+ - Do NOT use XML-like tags such as tool_calls, invoke, parameter, or their DSML variants.
71
+ - If you want to tell the user something, call the respond tool with a message.
72
+ - If JSON parsing fails, the tool call will NOT run and the user will see raw text. Keep JSON valid: escape every double quote inside strings, escape backslashes, use
73
+ for newlines.
74
+
65
75
  ## Rules
66
76
 
67
77
  - Always read a file before editing it.