zames_pro 1.0.0 → 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 +4 -1
- package/logo.png +0 -0
- package/package.json +3 -2
- package/src/agent-loop.js +130 -6
- package/src/index.js +0 -0
- package/src/system-prompt.js +10 -0
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# zames_pro
|
|
2
2
|
|
|
3
|
+

|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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
|
|
package/src/index.js
CHANGED
|
File without changes
|
package/src/system-prompt.js
CHANGED
|
@@ -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.
|