zames_pro 1.0.3 → 1.1.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.
- package/README.md +1 -1
- package/package.json +14 -3
- package/src/agent-loop.js +5 -1
- package/src/browser.js +150 -13
- package/src/confirm.js +10 -10
- package/src/diff.js +21 -113
- package/src/index.js +159 -107
- package/src/markdown.js +49 -0
- package/src/self-review.js +30 -30
- package/src/spinner.js +15 -22
- package/src/theme.js +34 -0
package/src/markdown.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { render } from 'markdansi'
|
|
2
|
+
import { highlight } from 'cli-highlight'
|
|
3
|
+
|
|
4
|
+
// Рендер Markdown-ответов модели в терминал через markdansi.
|
|
5
|
+
// GFM (заголовки, списки, таблицы, цитаты, task lists) + подсветка кода.
|
|
6
|
+
// Тема собрана из нашей спокойной палитры, без кислотных цветов.
|
|
7
|
+
|
|
8
|
+
const theme = {
|
|
9
|
+
heading: { color: '#9fb8d8', bold: true },
|
|
10
|
+
strong: { color: '#e6e0cc', bold: true },
|
|
11
|
+
emph: { color: '#cfc9b0', italic: true },
|
|
12
|
+
inlineCode: { color: '#9ecb9e' },
|
|
13
|
+
blockCode: { color: '#c9d1c9' },
|
|
14
|
+
code: { color: '#9ecb9e' },
|
|
15
|
+
link: { color: '#8fb8d8', underline: true },
|
|
16
|
+
quote: { color: '#8a8f9a', italic: true },
|
|
17
|
+
hr: { color: '#5b616e' },
|
|
18
|
+
listMarker: { color: '#8a9bb5' },
|
|
19
|
+
tableHeader: { color: '#9fb8d8', bold: true },
|
|
20
|
+
tableCell: { color: '#d0d0d0' },
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Подсветка кода через cli-highlight (транзитивно уже есть у нас).
|
|
24
|
+
function highlighter(code, lang) {
|
|
25
|
+
try {
|
|
26
|
+
return highlight(code, { language: lang || 'plaintext', ignoreIllegals: true })
|
|
27
|
+
} catch {
|
|
28
|
+
return code
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function renderMarkdown(text) {
|
|
33
|
+
if (!text) return ''
|
|
34
|
+
try {
|
|
35
|
+
return render(String(text), {
|
|
36
|
+
width: Math.min(process.stdout.columns || 80, 100),
|
|
37
|
+
theme,
|
|
38
|
+
highlighter,
|
|
39
|
+
codeBox: true,
|
|
40
|
+
tableBorder: 'unicode',
|
|
41
|
+
listIndent: 2,
|
|
42
|
+
hyperlinks: false,
|
|
43
|
+
})
|
|
44
|
+
} catch {
|
|
45
|
+
return String(text)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export default renderMarkdown
|
package/src/self-review.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
2
|
import fs from 'fs/promises'
|
|
3
|
-
import
|
|
3
|
+
import { theme } from './theme.js'
|
|
4
4
|
import { fileURLToPath } from 'url'
|
|
5
5
|
|
|
6
6
|
import { ZAMES_HOME } from './config.js'
|
|
@@ -56,7 +56,7 @@ export async function selfReview({ browser, config, focus, transcript }) {
|
|
|
56
56
|
|
|
57
57
|
if (copied.length === 0) {
|
|
58
58
|
console.error(
|
|
59
|
-
|
|
59
|
+
theme.error(
|
|
60
60
|
`\n✖ Самообзор отменён: в ${SRC_DIR} нет .js файлов.\n` +
|
|
61
61
|
`Проверь, что src/ не пуст и ты запускаешь агента из корня проекта.\n`,
|
|
62
62
|
),
|
|
@@ -73,9 +73,9 @@ export async function selfReview({ browser, config, focus, transcript }) {
|
|
|
73
73
|
await fs.writeFile(path.join(snapDir, 'package.json'), pkg, 'utf-8')
|
|
74
74
|
} catch {}
|
|
75
75
|
|
|
76
|
-
console.log(
|
|
77
|
-
console.log(
|
|
78
|
-
console.log(
|
|
76
|
+
console.log(theme.system(`\n📸 Снапшот: ${snapDir}`))
|
|
77
|
+
console.log(theme.system(`Файлов: ${copied.length} — ${copied.join(', ')}`))
|
|
78
|
+
console.log(theme.system('Начинаю самообзор...\n'))
|
|
79
79
|
|
|
80
80
|
const taskPrompt = buildReviewPrompt({ focus, snapDir })
|
|
81
81
|
|
|
@@ -95,15 +95,15 @@ export async function selfReview({ browser, config, focus, transcript }) {
|
|
|
95
95
|
onThinking: () => {},
|
|
96
96
|
onToolCall: (name, args) => {
|
|
97
97
|
const preview = JSON.stringify(args).slice(0, 120)
|
|
98
|
-
console.log(
|
|
98
|
+
console.log(theme.warn(`🔧 ${name}`), theme.system(preview))
|
|
99
99
|
},
|
|
100
100
|
onToolResult: (r) => {
|
|
101
101
|
const t = typeof r === 'string' ? r : JSON.stringify(r)
|
|
102
|
-
console.log(
|
|
102
|
+
console.log(theme.system(` → ${t.slice(0, 200).replace(/\n/g, ' ↵ ')}\n`))
|
|
103
103
|
},
|
|
104
104
|
onAssistantMessage: (msg) => {
|
|
105
105
|
finalMessage = msg
|
|
106
|
-
console.log(
|
|
106
|
+
console.log(theme.assistant('\n📋 Отчёт:\n'))
|
|
107
107
|
console.log(msg)
|
|
108
108
|
console.log()
|
|
109
109
|
},
|
|
@@ -116,26 +116,26 @@ export async function selfReview({ browser, config, focus, transcript }) {
|
|
|
116
116
|
// Считаем, что изменилось
|
|
117
117
|
const changed = await diffFiles(SRC_DIR, snapDir)
|
|
118
118
|
|
|
119
|
-
console.log(
|
|
120
|
-
console.log(
|
|
121
|
-
console.log(
|
|
119
|
+
console.log(theme.system('─'.repeat(60)))
|
|
120
|
+
console.log(theme.system(`Отчёт: ${reportPath}`))
|
|
121
|
+
console.log(theme.system(`Снапшот: ${snapDir}`))
|
|
122
122
|
if (changed.length) {
|
|
123
123
|
console.log(
|
|
124
|
-
|
|
124
|
+
theme.assistant(
|
|
125
125
|
`Изменено: ${changed.length} файл(ов): ${changed.join(', ')}`,
|
|
126
126
|
),
|
|
127
127
|
)
|
|
128
128
|
} else {
|
|
129
|
-
console.log(
|
|
129
|
+
console.log(theme.system('Изменено: (ничего — только отчёт)'))
|
|
130
130
|
}
|
|
131
|
-
console.log(
|
|
131
|
+
console.log(theme.user(`\nДальше:`))
|
|
132
132
|
console.log(
|
|
133
|
-
|
|
133
|
+
theme.user(
|
|
134
134
|
` /self-diff ${path.basename(snapDir)} — посмотреть различия`,
|
|
135
135
|
),
|
|
136
136
|
)
|
|
137
137
|
console.log(
|
|
138
|
-
|
|
138
|
+
theme.user(
|
|
139
139
|
` /self-apply ${path.basename(snapDir)} — применить к живому src/`,
|
|
140
140
|
),
|
|
141
141
|
)
|
|
@@ -166,13 +166,13 @@ export async function selfDiff({ config, name }) {
|
|
|
166
166
|
if (orig === next) continue
|
|
167
167
|
|
|
168
168
|
anyDiff = true
|
|
169
|
-
console.log(
|
|
169
|
+
console.log(theme.bold(`\n=== src/${f} ===`))
|
|
170
170
|
const diff = unifiedDiff(orig, next, { label: f })
|
|
171
|
-
console.log(colorDiff(diff
|
|
171
|
+
console.log(colorDiff(diff))
|
|
172
172
|
}
|
|
173
173
|
|
|
174
174
|
if (!anyDiff) {
|
|
175
|
-
console.log(
|
|
175
|
+
console.log(theme.system('Различий нет.'))
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
178
|
|
|
@@ -207,11 +207,11 @@ export async function selfApply({ config, name }) {
|
|
|
207
207
|
applied.push(f)
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
-
console.log(
|
|
211
|
-
console.log(
|
|
212
|
-
console.log(
|
|
210
|
+
console.log(theme.assistant(`\n✅ Применено: ${applied.length} файл(ов)`))
|
|
211
|
+
console.log(theme.system(applied.join(', ')))
|
|
212
|
+
console.log(theme.system(`Бэкап: ${backupDir}`))
|
|
213
213
|
console.log(
|
|
214
|
-
|
|
214
|
+
theme.warn(`\nПерезапусти агента, чтобы изменения вступили в силу.\n`),
|
|
215
215
|
)
|
|
216
216
|
}
|
|
217
217
|
|
|
@@ -223,17 +223,17 @@ export async function selfList({ config }) {
|
|
|
223
223
|
try {
|
|
224
224
|
entries = await fs.readdir(snapRoot)
|
|
225
225
|
} catch {
|
|
226
|
-
console.log(
|
|
226
|
+
console.log(theme.system('Снапшотов нет.'))
|
|
227
227
|
return
|
|
228
228
|
}
|
|
229
229
|
|
|
230
230
|
if (!entries.length) {
|
|
231
|
-
console.log(
|
|
231
|
+
console.log(theme.system('Снапшотов нет.'))
|
|
232
232
|
return
|
|
233
233
|
}
|
|
234
234
|
|
|
235
235
|
entries.sort()
|
|
236
|
-
console.log(
|
|
236
|
+
console.log(theme.system('Снапшоты самообзора:'))
|
|
237
237
|
for (const e of entries) {
|
|
238
238
|
const full = path.join(snapRoot, e)
|
|
239
239
|
const stat = await fs.stat(full).catch(() => null)
|
|
@@ -245,13 +245,13 @@ export async function selfList({ config }) {
|
|
|
245
245
|
.catch(() => false)
|
|
246
246
|
const changedCount = (await diffFiles(SRC_DIR, full)).length
|
|
247
247
|
const tag = changedCount
|
|
248
|
-
?
|
|
249
|
-
:
|
|
250
|
-
const rep = hasReport ?
|
|
248
|
+
? theme.assistant(`[${changedCount} изменено]`)
|
|
249
|
+
: theme.system('[без правок]')
|
|
250
|
+
const rep = hasReport ? theme.user('📋') : ' '
|
|
251
251
|
console.log(` ${rep} ${e} ${tag}`)
|
|
252
252
|
}
|
|
253
253
|
console.log()
|
|
254
|
-
console.log(
|
|
254
|
+
console.log(theme.system('Команды: /self-diff <name>, /self-apply <name>\n'))
|
|
255
255
|
}
|
|
256
256
|
|
|
257
257
|
// ---------- helpers ----------
|
package/src/spinner.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import ora from 'ora'
|
|
2
|
-
import
|
|
2
|
+
import { theme } from './theme.js'
|
|
3
|
+
import { renderMarkdown } from './markdown.js'
|
|
3
4
|
|
|
4
5
|
export function createSpinner() {
|
|
5
6
|
let spinner = null
|
|
@@ -13,43 +14,35 @@ export function createSpinner() {
|
|
|
13
14
|
if (spinner) spinner.stop()
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
const succeed = (text) => {
|
|
17
|
-
if (spinner) spinner.succeed(text)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const fail = (text) => {
|
|
21
|
-
if (spinner) spinner.fail(text)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
17
|
return {
|
|
25
|
-
|
|
26
|
-
thinking: () => start(chalk.gray('Думаю...')),
|
|
18
|
+
thinking: () => start(theme.system('Думаю...')),
|
|
27
19
|
|
|
28
|
-
// Оба метода только останавливают и печатают — не перезапускают
|
|
29
20
|
toolCall: (name, args) => {
|
|
30
21
|
stop()
|
|
31
22
|
const preview = JSON.stringify(args).slice(0, 120)
|
|
32
|
-
console.log(
|
|
23
|
+
console.log(theme.tool('🔧 ' + name), theme.dim(preview))
|
|
33
24
|
},
|
|
34
25
|
|
|
35
26
|
toolResult: (result) => {
|
|
36
27
|
stop()
|
|
37
28
|
const text = typeof result === 'string' ? result : JSON.stringify(result)
|
|
38
|
-
const preview = text.slice(0, 200).
|
|
39
|
-
console.log(
|
|
29
|
+
const preview = text.slice(0, 200).split(String.fromCharCode(10)).join(' ↵ ')
|
|
30
|
+
console.log(theme.toolResult(' → ' + preview + String.fromCharCode(10)))
|
|
40
31
|
},
|
|
41
32
|
|
|
42
33
|
assistant: (msg) => {
|
|
43
34
|
stop()
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
console.log(
|
|
35
|
+
const NL = String.fromCharCode(10)
|
|
36
|
+
const rendered = renderMarkdown(msg)
|
|
37
|
+
// Маркер ответа модели: помогает визуально отделить его от ввода
|
|
38
|
+
// пользователя (который подсвечен приглашением с золотой стрелкой).
|
|
39
|
+
console.log(NL + theme.assistant('● Ответ') + NL)
|
|
40
|
+
console.log(rendered)
|
|
41
|
+
console.log(theme.dim('─'.repeat(60)) + NL)
|
|
49
42
|
},
|
|
50
43
|
|
|
51
44
|
stop,
|
|
52
|
-
succeed,
|
|
53
|
-
fail,
|
|
45
|
+
succeed: () => {},
|
|
46
|
+
fail: () => {},
|
|
54
47
|
}
|
|
55
48
|
}
|
package/src/theme.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import chalk from 'chalk'
|
|
2
|
+
|
|
3
|
+
// Спокойная палитра для тёмного терминала.
|
|
4
|
+
// Приглушённые, слегка «выцветшие» тона без высокой насыщенности
|
|
5
|
+
// (в духе Tokyo Night / Nord). Задача — комфортное чтение длинных
|
|
6
|
+
// ответов, а не максимальная контрастность.
|
|
7
|
+
//
|
|
8
|
+
// Роли:
|
|
9
|
+
// - user — мягкий лавандово-серый (служебные сообщения)
|
|
10
|
+
// - prompt — тёплое золотистое (приглашение ввода, заметное)
|
|
11
|
+
// - dir — светло-голубое (имя рабочей директории)
|
|
12
|
+
// - assistant — тёплый песочный (ответы модели)
|
|
13
|
+
// - tool — приглушённый янтарный
|
|
14
|
+
// - toolResult — серо-голубой
|
|
15
|
+
// - system — нейтральный серый
|
|
16
|
+
// - warn — спокойный охра
|
|
17
|
+
// - error — приглушённый терракот
|
|
18
|
+
|
|
19
|
+
export const theme = {
|
|
20
|
+
user: chalk.hex('#b4b8d0'), // мягкий лавандово-серый
|
|
21
|
+
prompt: chalk.hex('#c8b06a').bold, // приглашение ввода — тёплое золотистое, заметное
|
|
22
|
+
dir: chalk.hex('#7fc4f0'), // имя рабочей директории — заметно голубое
|
|
23
|
+
assistant: chalk.hex('#cfc9b0'), // тёплый песочный
|
|
24
|
+
tool: chalk.hex('#c6a97e'), // приглушённый янтарный
|
|
25
|
+
toolResult: chalk.hex('#8a9bb5'), // серо-голубой
|
|
26
|
+
system: chalk.hex('#808896'), // нейтральный серый
|
|
27
|
+
dim: chalk.hex('#5b616e'), // тёмно-серый
|
|
28
|
+
warn: chalk.hex('#c9a86a'), // спокойный охра
|
|
29
|
+
error: chalk.hex('#c98a80'), // приглушённый терракот
|
|
30
|
+
success: chalk.hex('#a9c08c'), // мягкий шалфейный
|
|
31
|
+
bold: chalk.bold,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export default theme
|