zames_pro 1.0.3 → 1.1.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.
@@ -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
@@ -1,6 +1,6 @@
1
1
  import path from 'path'
2
2
  import fs from 'fs/promises'
3
- import chalk from 'chalk'
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
- chalk.red(
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(chalk.gray(`\n📸 Снапшот: ${snapDir}`))
77
- console.log(chalk.gray(`Файлов: ${copied.length} — ${copied.join(', ')}`))
78
- console.log(chalk.gray('Начинаю самообзор...\n'))
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(chalk.yellow(`🔧 ${name}`), chalk.gray(preview))
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(chalk.gray(` → ${t.slice(0, 200).replace(/\n/g, ' ↵ ')}\n`))
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(chalk.green('\n📋 Отчёт:\n'))
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(chalk.gray('─'.repeat(60)))
120
- console.log(chalk.gray(`Отчёт: ${reportPath}`))
121
- console.log(chalk.gray(`Снапшот: ${snapDir}`))
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
- chalk.green(
124
+ theme.assistant(
125
125
  `Изменено: ${changed.length} файл(ов): ${changed.join(', ')}`,
126
126
  ),
127
127
  )
128
128
  } else {
129
- console.log(chalk.gray('Изменено: (ничего — только отчёт)'))
129
+ console.log(theme.system('Изменено: (ничего — только отчёт)'))
130
130
  }
131
- console.log(chalk.cyan(`\nДальше:`))
131
+ console.log(theme.user(`\nДальше:`))
132
132
  console.log(
133
- chalk.cyan(
133
+ theme.user(
134
134
  ` /self-diff ${path.basename(snapDir)} — посмотреть различия`,
135
135
  ),
136
136
  )
137
137
  console.log(
138
- chalk.cyan(
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(chalk.bold(`\n=== src/${f} ===`))
169
+ console.log(theme.bold(`\n=== src/${f} ===`))
170
170
  const diff = unifiedDiff(orig, next, { label: f })
171
- console.log(colorDiff(diff, chalk))
171
+ console.log(colorDiff(diff))
172
172
  }
173
173
 
174
174
  if (!anyDiff) {
175
- console.log(chalk.gray('Различий нет.'))
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(chalk.green(`\n✅ Применено: ${applied.length} файл(ов)`))
211
- console.log(chalk.gray(applied.join(', ')))
212
- console.log(chalk.gray(`Бэкап: ${backupDir}`))
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
- chalk.yellow(`\nПерезапусти агента, чтобы изменения вступили в силу.\n`),
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(chalk.gray('Снапшотов нет.'))
226
+ console.log(theme.system('Снапшотов нет.'))
227
227
  return
228
228
  }
229
229
 
230
230
  if (!entries.length) {
231
- console.log(chalk.gray('Снапшотов нет.'))
231
+ console.log(theme.system('Снапшотов нет.'))
232
232
  return
233
233
  }
234
234
 
235
235
  entries.sort()
236
- console.log(chalk.gray('Снапшоты самообзора:'))
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
- ? chalk.green(`[${changedCount} изменено]`)
249
- : chalk.gray('[без правок]')
250
- const rep = hasReport ? chalk.cyan('📋') : ' '
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(chalk.gray('Команды: /self-diff <name>, /self-apply <name>\n'))
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,44 @@
1
1
  import ora from 'ora'
2
- import chalk from 'chalk'
2
+ import { theme } from './theme.js'
3
+ import { renderMarkdown } from './markdown.js'
4
+
5
+ // Фразы для спиннера «агент работает». Выбираются в случайном порядке.
6
+ const THINKING_PHRASES = [
7
+ 'Замешиваю кал…',
8
+ 'Взбиваю кал до однородной массы…',
9
+ 'Мешаю кал с логикой…',
10
+ 'Взбалтываю кал в коктейль…',
11
+ 'Кручу венчиком по калу…',
12
+ 'Смешиваю кал с кофеином…',
13
+ 'Замешиваю тесто из кала…',
14
+ 'Взбиваю пену из кала…',
15
+ 'Перемешиваю кал лопатой…',
16
+ 'Замешиваю глину из кала…',
17
+ 'Гомогенизирую кал до состояния бетона…',
18
+ 'Взбалтываю кал до просветления…',
19
+ 'Блендерю кал в смузи…',
20
+ 'Замешиваю цемент из кала…',
21
+ 'Взбиваю кашу из кала…',
22
+ 'Мешаю кал с надеждой…',
23
+ 'Взбалтываю осадок из кала…',
24
+ 'Кручу блендером по калу…',
25
+ 'Смешиваю кал до неразличимости…',
26
+ 'Замешиваю раствор из кала…',
27
+ 'Взбиваю коктейль «кал»…',
28
+ 'Перемешиваю кал лопатой дедлайна…',
29
+ 'Замешиваю кашу из кала…',
30
+ 'Взбиваю пенку из кала…',
31
+ 'Гомогенизирую кал до однородности…',
32
+ 'Взбалтываю кал в бетономешалке…',
33
+ 'Замешиваю кал в тесто…',
34
+ 'Сбиваю кал в однородную массу…',
35
+ 'Размешиваю кал до просветления…',
36
+ 'Замешиваю кал из всего подряд…',
37
+ ]
38
+
39
+ function randomThinkingPhrase() {
40
+ return THINKING_PHRASES[Math.floor(Math.random() * THINKING_PHRASES.length)]
41
+ }
3
42
 
4
43
  export function createSpinner() {
5
44
  let spinner = null
@@ -13,43 +52,35 @@ export function createSpinner() {
13
52
  if (spinner) spinner.stop()
14
53
  }
15
54
 
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
55
  return {
25
- // Единственный метод, который ЗАПУСКАЕТ спиннер
26
- thinking: () => start(chalk.gray('Думаю...')),
56
+ thinking: () => start(theme.system(randomThinkingPhrase())),
27
57
 
28
- // Оба метода только останавливают и печатают — не перезапускают
29
58
  toolCall: (name, args) => {
30
59
  stop()
31
60
  const preview = JSON.stringify(args).slice(0, 120)
32
- console.log(chalk.yellow(`🔧 ${name}`), chalk.gray(preview))
61
+ console.log(theme.tool('🔧 ' + name), theme.dim(preview))
33
62
  },
34
63
 
35
64
  toolResult: (result) => {
36
65
  stop()
37
66
  const text = typeof result === 'string' ? result : JSON.stringify(result)
38
- const preview = text.slice(0, 200).replace(/\n/g, ' ↵ ')
39
- console.log(chalk.gray(` → ${preview}\n`))
67
+ const preview = text.slice(0, 200).split(String.fromCharCode(10)).join(' ↵ ')
68
+ console.log(theme.toolResult(' → ' + preview + String.fromCharCode(10)))
40
69
  },
41
70
 
42
71
  assistant: (msg) => {
43
72
  stop()
44
- console.log(chalk.green('\n✅ ' + msg) + '\n')
45
- },
46
-
47
- taskHeader: (task) => {
48
- console.log(chalk.bold.cyan(`\n🎯 Задача: ${task}\n`))
73
+ const NL = String.fromCharCode(10)
74
+ const rendered = renderMarkdown(msg)
75
+ // Маркер ответа модели: помогает визуально отделить его от ввода
76
+ // пользователя (который подсвечен приглашением с золотой стрелкой).
77
+ console.log(NL + theme.assistant('● Ответ') + NL)
78
+ console.log(rendered)
79
+ console.log(theme.dim('─'.repeat(60)) + NL)
49
80
  },
50
81
 
51
82
  stop,
52
- succeed,
53
- fail,
83
+ succeed: () => {},
84
+ fail: () => {},
54
85
  }
55
86
  }
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