zames_pro 1.0.2 → 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.
@@ -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,16 +1,30 @@
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
- import { createTools } from './tools.js'
7
- import { runAgentLoop } from './agent-loop.js'
8
6
  import { ZAMES_HOME } from './config.js'
9
7
 
10
8
  const __dirname = path.dirname(fileURLToPath(import.meta.url))
11
9
  const SRC_DIR = __dirname
12
10
  const SNAP_ROOT = path.join(ZAMES_HOME, 'snapshots')
13
11
 
12
+ // Динамический импорт логики с timestamp — чтобы при /reload (или авто-reload)
13
+ // self-review использовал СВЕЖИЕ tools/agent-loop, а не закэшированные при
14
+ // первой загрузке. Иначе reload не доходил бы до зависимостей self-review.
15
+ async function loadFresh() {
16
+ const stamp = Date.now()
17
+ const toolsUrl = new URL('./tools.js', import.meta.url)
18
+ toolsUrl.searchParams.set('t', String(stamp))
19
+ const loopUrl = new URL('./agent-loop.js', import.meta.url)
20
+ loopUrl.searchParams.set('t', String(stamp))
21
+ const [{ createTools }, { runAgentLoop }] = await Promise.all([
22
+ import(toolsUrl.href),
23
+ import(loopUrl.href),
24
+ ])
25
+ return { createTools, runAgentLoop }
26
+ }
27
+
14
28
  async function ensureDir(p) {
15
29
  await fs.mkdir(p, { recursive: true })
16
30
  }
@@ -42,7 +56,7 @@ export async function selfReview({ browser, config, focus, transcript }) {
42
56
 
43
57
  if (copied.length === 0) {
44
58
  console.error(
45
- chalk.red(
59
+ theme.error(
46
60
  `\n✖ Самообзор отменён: в ${SRC_DIR} нет .js файлов.\n` +
47
61
  `Проверь, что src/ не пуст и ты запускаешь агента из корня проекта.\n`,
48
62
  ),
@@ -59,12 +73,13 @@ export async function selfReview({ browser, config, focus, transcript }) {
59
73
  await fs.writeFile(path.join(snapDir, 'package.json'), pkg, 'utf-8')
60
74
  } catch {}
61
75
 
62
- console.log(chalk.gray(`\n📸 Снапшот: ${snapDir}`))
63
- console.log(chalk.gray(`Файлов: ${copied.length} — ${copied.join(', ')}`))
64
- 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'))
65
79
 
66
80
  const taskPrompt = buildReviewPrompt({ focus, snapDir })
67
81
 
82
+ const { createTools, runAgentLoop } = await loadFresh()
68
83
  const tools = createTools(snapDir, { undo: null })
69
84
  let finalMessage = ''
70
85
 
@@ -80,15 +95,15 @@ export async function selfReview({ browser, config, focus, transcript }) {
80
95
  onThinking: () => {},
81
96
  onToolCall: (name, args) => {
82
97
  const preview = JSON.stringify(args).slice(0, 120)
83
- console.log(chalk.yellow(`🔧 ${name}`), chalk.gray(preview))
98
+ console.log(theme.warn(`🔧 ${name}`), theme.system(preview))
84
99
  },
85
100
  onToolResult: (r) => {
86
101
  const t = typeof r === 'string' ? r : JSON.stringify(r)
87
- 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`))
88
103
  },
89
104
  onAssistantMessage: (msg) => {
90
105
  finalMessage = msg
91
- console.log(chalk.green('\n📋 Отчёт:\n'))
106
+ console.log(theme.assistant('\n📋 Отчёт:\n'))
92
107
  console.log(msg)
93
108
  console.log()
94
109
  },
@@ -101,26 +116,26 @@ export async function selfReview({ browser, config, focus, transcript }) {
101
116
  // Считаем, что изменилось
102
117
  const changed = await diffFiles(SRC_DIR, snapDir)
103
118
 
104
- console.log(chalk.gray('─'.repeat(60)))
105
- console.log(chalk.gray(`Отчёт: ${reportPath}`))
106
- console.log(chalk.gray(`Снапшот: ${snapDir}`))
119
+ console.log(theme.system('─'.repeat(60)))
120
+ console.log(theme.system(`Отчёт: ${reportPath}`))
121
+ console.log(theme.system(`Снапшот: ${snapDir}`))
107
122
  if (changed.length) {
108
123
  console.log(
109
- chalk.green(
124
+ theme.assistant(
110
125
  `Изменено: ${changed.length} файл(ов): ${changed.join(', ')}`,
111
126
  ),
112
127
  )
113
128
  } else {
114
- console.log(chalk.gray('Изменено: (ничего — только отчёт)'))
129
+ console.log(theme.system('Изменено: (ничего — только отчёт)'))
115
130
  }
116
- console.log(chalk.cyan(`\nДальше:`))
131
+ console.log(theme.user(`\nДальше:`))
117
132
  console.log(
118
- chalk.cyan(
133
+ theme.user(
119
134
  ` /self-diff ${path.basename(snapDir)} — посмотреть различия`,
120
135
  ),
121
136
  )
122
137
  console.log(
123
- chalk.cyan(
138
+ theme.user(
124
139
  ` /self-apply ${path.basename(snapDir)} — применить к живому src/`,
125
140
  ),
126
141
  )
@@ -151,13 +166,13 @@ export async function selfDiff({ config, name }) {
151
166
  if (orig === next) continue
152
167
 
153
168
  anyDiff = true
154
- console.log(chalk.bold(`\n=== src/${f} ===`))
169
+ console.log(theme.bold(`\n=== src/${f} ===`))
155
170
  const diff = unifiedDiff(orig, next, { label: f })
156
- console.log(colorDiff(diff, chalk))
171
+ console.log(colorDiff(diff))
157
172
  }
158
173
 
159
174
  if (!anyDiff) {
160
- console.log(chalk.gray('Различий нет.'))
175
+ console.log(theme.system('Различий нет.'))
161
176
  }
162
177
  }
163
178
 
@@ -192,11 +207,11 @@ export async function selfApply({ config, name }) {
192
207
  applied.push(f)
193
208
  }
194
209
 
195
- console.log(chalk.green(`\n✅ Применено: ${applied.length} файл(ов)`))
196
- console.log(chalk.gray(applied.join(', ')))
197
- 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}`))
198
213
  console.log(
199
- chalk.yellow(`\nПерезапусти агента, чтобы изменения вступили в силу.\n`),
214
+ theme.warn(`\nПерезапусти агента, чтобы изменения вступили в силу.\n`),
200
215
  )
201
216
  }
202
217
 
@@ -208,17 +223,17 @@ export async function selfList({ config }) {
208
223
  try {
209
224
  entries = await fs.readdir(snapRoot)
210
225
  } catch {
211
- console.log(chalk.gray('Снапшотов нет.'))
226
+ console.log(theme.system('Снапшотов нет.'))
212
227
  return
213
228
  }
214
229
 
215
230
  if (!entries.length) {
216
- console.log(chalk.gray('Снапшотов нет.'))
231
+ console.log(theme.system('Снапшотов нет.'))
217
232
  return
218
233
  }
219
234
 
220
235
  entries.sort()
221
- console.log(chalk.gray('Снапшоты самообзора:'))
236
+ console.log(theme.system('Снапшоты самообзора:'))
222
237
  for (const e of entries) {
223
238
  const full = path.join(snapRoot, e)
224
239
  const stat = await fs.stat(full).catch(() => null)
@@ -230,13 +245,13 @@ export async function selfList({ config }) {
230
245
  .catch(() => false)
231
246
  const changedCount = (await diffFiles(SRC_DIR, full)).length
232
247
  const tag = changedCount
233
- ? chalk.green(`[${changedCount} изменено]`)
234
- : chalk.gray('[без правок]')
235
- const rep = hasReport ? chalk.cyan('📋') : ' '
248
+ ? theme.assistant(`[${changedCount} изменено]`)
249
+ : theme.system('[без правок]')
250
+ const rep = hasReport ? theme.user('📋') : ' '
236
251
  console.log(` ${rep} ${e} ${tag}`)
237
252
  }
238
253
  console.log()
239
- console.log(chalk.gray('Команды: /self-diff <name>, /self-apply <name>\n'))
254
+ console.log(theme.system('Команды: /self-diff <name>, /self-apply <name>\n'))
240
255
  }
241
256
 
242
257
  // ---------- helpers ----------
package/src/spinner.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import ora from 'ora'
2
- import chalk from 'chalk'
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(chalk.yellow(`🔧 ${name}`), chalk.gray(preview))
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).replace(/\n/g, ' ↵ ')
39
- console.log(chalk.gray(` → ${preview}\n`))
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
- console.log(chalk.green('\n✅ ' + msg) + '\n')
45
- },
46
-
47
- taskHeader: (task) => {
48
- console.log(chalk.bold.cyan(`\n🎯 Задача: ${task}\n`))
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