zames_pro 1.2.0 → 2.0.0

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 (57) hide show
  1. package/dist/agent-loop.js +652 -0
  2. package/dist/agent-loop.js.map +1 -0
  3. package/dist/browser.js +573 -0
  4. package/dist/browser.js.map +1 -0
  5. package/dist/config.js +74 -0
  6. package/dist/config.js.map +1 -0
  7. package/dist/confirm.js +82 -0
  8. package/dist/confirm.js.map +1 -0
  9. package/dist/diff.js +38 -0
  10. package/dist/diff.js.map +1 -0
  11. package/dist/gitTools.js +219 -0
  12. package/dist/gitTools.js.map +1 -0
  13. package/dist/index.js +1365 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/input.js +524 -0
  16. package/dist/input.js.map +1 -0
  17. package/dist/markdown.js +48 -0
  18. package/dist/markdown.js.map +1 -0
  19. package/dist/self-review.js +281 -0
  20. package/dist/self-review.js.map +1 -0
  21. package/dist/sessions.js +123 -0
  22. package/dist/sessions.js.map +1 -0
  23. package/dist/spinner.js +138 -0
  24. package/dist/spinner.js.map +1 -0
  25. package/{src → dist}/system-prompt.js +10 -14
  26. package/dist/system-prompt.js.map +1 -0
  27. package/{src → dist}/theme.js +17 -19
  28. package/dist/theme.js.map +1 -0
  29. package/dist/tools.js +189 -0
  30. package/dist/tools.js.map +1 -0
  31. package/dist/transcript.js +51 -0
  32. package/dist/transcript.js.map +1 -0
  33. package/dist/types.js +4 -0
  34. package/dist/types.js.map +1 -0
  35. package/dist/undo.js +100 -0
  36. package/dist/undo.js.map +1 -0
  37. package/dist/web.js +220 -0
  38. package/dist/web.js.map +1 -0
  39. package/dist/xml-toolcall.js +68 -0
  40. package/dist/xml-toolcall.js.map +1 -0
  41. package/package.json +13 -5
  42. package/src/agent-loop.js +0 -575
  43. package/src/browser.js +0 -627
  44. package/src/config.js +0 -81
  45. package/src/confirm.js +0 -85
  46. package/src/diff.js +0 -32
  47. package/src/gitTools.js +0 -256
  48. package/src/index.js +0 -1461
  49. package/src/input.js +0 -447
  50. package/src/markdown.js +0 -49
  51. package/src/self-review.js +0 -319
  52. package/src/spinner.js +0 -144
  53. package/src/tools.js +0 -206
  54. package/src/transcript.js +0 -46
  55. package/src/undo.js +0 -101
  56. package/src/web.js +0 -266
  57. package/src/xml-toolcall.js +0 -69
package/src/confirm.js DELETED
@@ -1,85 +0,0 @@
1
- import * as readlinePromises from 'readline/promises'
2
- import { theme } from './theme.js'
3
- import { unifiedDiff, colorDiff } from './diff.js'
4
-
5
- export class ConfirmManager {
6
- constructor({ config }) {
7
- const c = config || {}
8
- this.settings = {
9
- write: c.write !== false,
10
- edit: c.edit !== false,
11
- bash: c.bash !== false,
12
- }
13
- this.alwaysConfirm = (c.alwaysConfirm || []).map((p) => new RegExp(p, 'i'))
14
- this.allowedForSession = new Set()
15
- }
16
-
17
- _matchesAlwaysConfirm(command) {
18
- return this.alwaysConfirm.some((re) => re.test(command))
19
- }
20
-
21
- shouldAsk(kind, target = null) {
22
- if (kind === 'bash' && target && this._matchesAlwaysConfirm(target)) {
23
- return true
24
- }
25
- if (!this.settings[kind]) return false
26
- if (this.allowedForSession.has(kind)) return false
27
- return true
28
- }
29
-
30
- async ask({ kind, target, preview = null, message }) {
31
- if (!this.shouldAsk(kind, target)) return true
32
-
33
- if (preview) {
34
- console.log(preview)
35
- }
36
-
37
- const label = message || `Разрешить ${kind}?`
38
- const question =
39
- theme.warn(`${label} `) +
40
- theme.system('[') +
41
- theme.assistant('y') +
42
- theme.system(' = да, ') +
43
- theme.error('n') +
44
- theme.system(' = нет, ') +
45
- theme.user('a') +
46
- theme.system(' = всегда для этого типа] ')
47
-
48
- const answer = await this._prompt(question)
49
-
50
- if (answer === 'a') {
51
- if (kind !== 'bash' || !this._matchesAlwaysConfirm(target)) {
52
- this.allowedForSession.add(kind)
53
- }
54
- return true
55
- }
56
- return answer === 'y'
57
- }
58
-
59
- async _prompt(question) {
60
- process.stdin.resume()
61
- if (process.stdin.isTTY && process.stdin.setRawMode) {
62
- process.stdin.setRawMode(false)
63
- }
64
-
65
- const rl = readlinePromises.createInterface({
66
- input: process.stdin,
67
- output: process.stdout,
68
- })
69
-
70
- try {
71
- const ans = await rl.question(question)
72
- const v = (ans || '').trim().toLowerCase()
73
- if (v === 'a' || v === 'always' || v === 'в') return 'a'
74
- if (v === 'y' || v === 'yes' || v === 'д' || v === 'да') return 'y'
75
- return 'n'
76
- } finally {
77
- rl.close()
78
- }
79
- }
80
- }
81
-
82
- export function formatDiffPreview(oldText, newText, { label } = {}) {
83
- const diff = unifiedDiff(oldText, newText, { label })
84
- return colorDiff(diff)
85
- }
package/src/diff.js DELETED
@@ -1,32 +0,0 @@
1
- import { theme } from './theme.js'
2
-
3
- export function unifiedDiff(a, b, { label = '' } = {}) {
4
- const la = a.split(String.fromCharCode(10))
5
- const lb = b.split(String.fromCharCode(10))
6
- const n = Math.max(la.length, lb.length)
7
- const out = []
8
- if (label) out.push('... ' + label)
9
- for (let i = 0; i < n; i++) {
10
- const x = la[i]
11
- const y = lb[i]
12
- if (x === y) continue
13
- if (x !== undefined) out.push('-' + x)
14
- if (y !== undefined) out.push('+' + y)
15
- }
16
- return out.join(String.fromCharCode(10))
17
- }
18
-
19
- export function colorDiff(diffText) {
20
- return diffText
21
- .split(String.fromCharCode(10))
22
- .map((line) => {
23
- if (line.startsWith('+++') || line.startsWith('---')) return theme.bold(line)
24
- if (line.startsWith('+')) return theme.assistant(line)
25
- if (line.startsWith('-')) return theme.error(line)
26
- if (line.startsWith('...')) return theme.system(line)
27
- return line
28
- })
29
- .join(String.fromCharCode(10))
30
- }
31
-
32
- export default { unifiedDiff, colorDiff }
package/src/gitTools.js DELETED
@@ -1,256 +0,0 @@
1
- import { exec } from 'child_process'
2
-
3
- function runGit(cmd, cwd, timeout = 15_000) {
4
- return new Promise((resolve) => {
5
- exec(
6
- cmd,
7
- {
8
- cwd,
9
- timeout,
10
- windowsHide: true,
11
- maxBuffer: 1024 * 1024 * 8,
12
- env: {
13
- ...process.env,
14
- GIT_TERMINAL_PROMPT: '0',
15
- GIT_PAGER: 'cat',
16
- PAGER: 'cat',
17
- },
18
- },
19
- (err, stdout, stderr) => {
20
- const out = (stdout || '').toString()
21
- const errStr = (stderr || '').toString()
22
-
23
- if (!err) {
24
- const combined = (out + errStr).trim()
25
- resolve(combined || '(команда выполнена, вывода нет)')
26
- return
27
- }
28
-
29
- const parts = []
30
- if (err.killed) parts.push(`⏱ Таймаут ${timeout}ms`)
31
- else if (err.code !== undefined && err.code !== null)
32
- parts.push(`Exit code: ${err.code}`)
33
- else parts.push(`Ошибка: ${err.message}`)
34
- if (out.trim()) parts.push(out.trim())
35
- if (errStr.trim()) parts.push(errStr.trim())
36
- resolve(parts.join('\n'))
37
- },
38
- )
39
- })
40
- }
41
-
42
- // Безопасно квотит пути/аргументы для передачи в shell.
43
- // Строка или массив -> строка с двойными кавычками и экранированием.
44
- function quoteArgs(input) {
45
- const list = Array.isArray(input) ? input : [input]
46
- return list
47
- .map((v) => String(v).trim())
48
- .filter(Boolean)
49
- .map((v) => JSON.stringify(v))
50
- .join(' ')
51
- }
52
-
53
- // Проверяет, является ли директория git-репозиторием, и собирает контекст.
54
- export async function getGitContext(workdir) {
55
- const probe = await runGit(
56
- 'git rev-parse --is-inside-work-tree',
57
- workdir,
58
- 5000,
59
- )
60
- if (probe.trim() !== 'true') return null
61
-
62
- const branchR = await runGit('git branch --show-current', workdir, 5000)
63
- const statusR = await runGit('git status --porcelain', workdir, 5000)
64
- const remoteR = await runGit('git remote', workdir, 5000)
65
- const aheadR = await runGit(
66
- 'git rev-list --left-right --count @{upstream}...HEAD',
67
- workdir,
68
- 5000,
69
- )
70
-
71
- const branch = branchR.trim() || '(detached HEAD)'
72
- const statusLines = statusR
73
- .split('\n')
74
- .map((l) => l.trimEnd())
75
- .filter(Boolean)
76
- const remotes = remoteR
77
- .split('\n')
78
- .map((l) => l.trim())
79
- .filter(Boolean)
80
- const hasOrigin = remotes.includes('origin')
81
-
82
- let ahead = 0
83
- let behind = 0
84
- const m = aheadR.match(/^(\d+)\s+(\d+)/)
85
- if (m) {
86
- behind = Number(m[1])
87
- ahead = Number(m[2])
88
- }
89
-
90
- return {
91
- branch,
92
- changedFiles: statusLines.length,
93
- statusPreview: statusLines.slice(0, 30).join('\n'),
94
- hasOrigin,
95
- ahead,
96
- behind,
97
- }
98
- }
99
-
100
- export function formatGitContext(ctx) {
101
- if (!ctx) return 'Not a git repository (or git is not installed).'
102
-
103
- let s = `- Branch: \`${ctx.branch}\``
104
- if (ctx.hasOrigin) {
105
- s += `\n- Remote: origin (ahead ${ctx.ahead}, behind ${ctx.behind})`
106
- } else {
107
- s += '\n- Remote: (none)'
108
- }
109
-
110
- if (ctx.changedFiles > 0) {
111
- s += `\n- Uncommitted changes: ${ctx.changedFiles} file(s)\n`
112
- s += '```\n' + ctx.statusPreview + '\n```'
113
- } else {
114
- s += '\n- Working tree: clean'
115
- }
116
- return s
117
- }
118
-
119
- export function createGitTools(workdir) {
120
- const ensureRepo = async () => {
121
- const probe = await runGit(
122
- 'git rev-parse --is-inside-work-tree',
123
- workdir,
124
- 5000,
125
- )
126
- if (probe.trim() !== 'true') {
127
- return (
128
- `Not a git repository: ${workdir}\n` +
129
- `Hint: run \`git init\` here, or cd into an existing repo.\n` +
130
- `(raw probe output: ${JSON.stringify(probe.trim())})`
131
- )
132
- }
133
- return null
134
- }
135
-
136
- return [
137
- {
138
- name: 'GitStatus',
139
- description:
140
- 'Показать git status рабочей директории. Возвращает ошибку, если это не git-репозиторий.',
141
- parameters: {},
142
- fn: async () => {
143
- const err = await ensureRepo()
144
- if (err) return err
145
- return runGit('git status', workdir, 10_000)
146
- },
147
- },
148
-
149
- {
150
- name: 'GitDiff',
151
- description:
152
- 'Показать git diff. По умолчанию незастейдженные изменения. Можно ограничить path и указать staged=true.',
153
- parameters: { path: 'string?', staged: 'boolean?' },
154
- fn: async ({ path: p, staged }) => {
155
- const err = await ensureRepo()
156
- if (err) return err
157
- const parts = ['git diff']
158
- if (staged) parts.push('--staged')
159
- if (p) parts.push('--', JSON.stringify(p))
160
- return runGit(parts.join(' '), workdir, 20_000)
161
- },
162
- },
163
-
164
- {
165
- name: 'GitLog',
166
- description: 'Показать последние N коммитов (по умолчанию 10).',
167
- parameters: { count: 'number?' },
168
- fn: async ({ count }) => {
169
- const err = await ensureRepo()
170
- if (err) return err
171
- return runGit(
172
- `git log --oneline --decorate -n ${Number(count) || 10}`,
173
- workdir,
174
- 10_000,
175
- )
176
- },
177
- },
178
-
179
- {
180
- name: 'GitAdd',
181
- description:
182
- 'Добавить файлы в индекс. Если paths не задан — git add -A (всё).',
183
- parameters: { paths: 'string?' },
184
- fn: async ({ paths }) => {
185
- const err = await ensureRepo()
186
- if (err) return err
187
- return runGit(
188
- paths && String(paths).trim()
189
- ? 'git add -- ' + quoteArgs(paths)
190
- : 'git add -A',
191
- workdir,
192
- 20_000,
193
- )
194
- },
195
- },
196
-
197
- {
198
- name: 'GitCommit',
199
- description:
200
- 'Закоммитить застейдженное (если ничего не застейджено — сначала git add -A). Не пушит.',
201
- parameters: { message: 'string' },
202
- fn: async ({ message }) => {
203
- const err = await ensureRepo()
204
- if (err) return err
205
-
206
- if (!message || !message.trim()) {
207
- return 'Ошибка: message пустой.'
208
- }
209
-
210
- // Индексируем всё только если в индексе пусто (как обещает описание).
211
- const staged = await runGit('git diff --cached --name-only', workdir, 10_000)
212
- const hasStaged = !/^\s*$/.test(staged) && staged.trim() !== '(команда выполнена, вывода нет)'
213
- const addResult = hasStaged
214
- ? '(индекс уже не пуст — add -A пропущен)'
215
- : await runGit('git add -A', workdir, 20_000)
216
-
217
- const fs = await import('fs/promises')
218
- const path = await import('path')
219
- const os = await import('os')
220
- const tmp = path.join(os.tmpdir(), `dsa-commit-${Date.now()}.txt`)
221
- await fs.writeFile(tmp, message, 'utf-8')
222
- try {
223
- const result = await runGit(
224
- `git commit -F ${JSON.stringify(tmp)}`,
225
- workdir,
226
- 30_000,
227
- )
228
- return `add: ${addResult}\ncommit: ${result}`
229
- } finally {
230
- await fs.unlink(tmp).catch(() => {})
231
- }
232
- },
233
- },
234
-
235
- {
236
- name: 'GitPush',
237
- description:
238
- 'Запушить ветку в origin. Требует настроенных креденшелов (SSH или Windows Credential Manager).',
239
- parameters: { branch: 'string?', setUpstream: 'boolean?' },
240
- fn: async ({ branch, setUpstream }) => {
241
- const err = await ensureRepo()
242
- if (err) return err
243
-
244
- const b =
245
- branch && branch.trim()
246
- ? branch.trim()
247
- : (await runGit('git branch --show-current', workdir, 5000)).trim()
248
- if (!b) return 'Ошибка: не удалось определить ветку для push.'
249
-
250
- const flag = setUpstream ? '-u ' : ''
251
- const target = quoteArgs(b)
252
- return runGit(`git push ${flag}origin ${target}`, workdir, 120_000)
253
- },
254
- },
255
- ]
256
- }