Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
170 changes: 152 additions & 18 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,164 @@

## [Unreleased]

### Added
- _Нет незарелиженных изменений — всё ниже разнесено по версиям._

## [4.0.19] - 2026-09-15

### Added
- **Учёт токенов диалога**: панель «Токены диалога» с оценкой всего диалога из DOM (CJK 0.6 / ASCII 0.3), сохранение по `sessionId` в localStorage, обновление раз в секунду, на `popstate`/`hashchange` и `beforeunload`; формат K/M вместо китайского «万»
- **Токены под ответом AI**: оценка токенов именно этого ответа под каждым сообщением
- **Перехват серверных токенов DeepSeek** (`token-interceptor`): точные значения usage из ответов DeepSeek + настройка видимости
- **What's New**: модальное окно нововведений при первом запуске после обновления
- `src/main/changelog-parser.js` — парсер CHANGELOG.md (диапазон версий, semver-сравнение)
- `src/main/whats-new.js` — логика check-and-mark, атомарная запись состояния
- `src/preload/dom/whats-new.js` — одноразовый модал в рендерере; CHANGELOG.md включается в сборку
- **Диагностика интеграции** (`src/preload/dom/diagnostics.js`) — инструмент и UI проверки связки main/preload/сайт

### Changed
- **Удалён Claude-провайдер** (`src/providers/claude.js`, промпт, лого, тесты)
- Удалён мёртвый код network-hook (`src/interceptor/`, `intercept-observer.js`, `serverTokenUsage`, `useIntercept`)
- Обновлён UI вкладки настроек (`settings-tab.js`)
- Убран устаревший хелпер tryOpenInVSCode

## [4.0.18] - 2026-09-14

### Added
- **Авто-форматтеры** после `write`/`edit`: файл автоматически приводится к стилю проекта
- Встроенный реестр: prettier, biome, gofmt, ruff, rustfmt, shfmt, clang-format
- Определение по расширению + наличию конфига (`package.json`, `biome.json`, `pyproject.toml`, `.clang-format`) + бинарю в `PATH` или `node_modules/.bin`
- Определение по расширению + наличию конфига + бинарю в `PATH` или `node_modules/.bin`
- Отключается флагом `formattersEnabled: false` в `cuckoo-settings.json`
- Ошибки форматтера никогда не ломают `write`/`edit` — это вторичная операция
- **Telegram-бот** (`botsrc/`): лёгкий клиент без зависимостей (long-polling, без VPS)
- Уведомления о вызовах инструментов: имя, аргументы, результат; для `edit` — новый код (до 2000 символов)
- Дублирование ответов AI в Telegram (человекочитаемый текст без code-блоков)
- Приём сообщений из Telegram → в чат DeepSeek (нативный Enter)
- Команда `/todos` — список текущих задач активного окна из Telegram
- Уведомление «🎉 Все задачи выполнены» со списком задач, когда весь список todo становится `completed` (однократно, с повторным срабатыванием после изменения списка)
- Настройки в **Настройки → Cookie Code → Telegram-бот** (токен/chat ID/флаги)
- **@-упоминание папок** в поле ввода: теперь в автодополнении по `@` показываются и файлы, и папки проекта (папки сверху, с иконкой 📁); исключаются `node_modules`, `.git`, `dist`, `build` и т.п.
- Кнопка экстренной остановки (Kill) активных дочерних процессов в плашке «Выполняется»
- Декорирование tool-блоков: горячий путь обходит все ответы с необработанными блоками (ленивая догрузка истории DeepSeek)
- RGB-переливание приветственного текста на стартовой странице
- Перенос дизайна страницы настроек в оверлей (карточки, стекло, акценты)
- Ошибки форматтера никогда не ломают `write`/`edit`
- Кнопка вкл/выкл в настройках Cookie Code; документация в README (EN + RU)
- **Режим плана (Plan Mode)**: кнопка-тумблер, per-session режим, перетаскиваемый диалог плана, инструмент `exit_plan_mode`
- **Produced files** под ответом AI (факт-ориентированно, только успешные операции), ru/en i18n, настройка `showProducedFiles`, тесты
- **Бейджи +/− строк** по каждому блоку diff в ответе
- **Telegram-бот**: команды `/help` и `/settings` с полным меню настроек

### Fixed
- `meta.produced` label: «Создано» → «Затронуто» / Produced → Affected
- i18n overlay: `OVERLAY_HTML` → `buildOverlayHTML()` — пересборка при смене языка

## [4.0.17] - 2026-09-14

### Added
- **File-path chips**: кликабельные чипы путей (абсолютный + относительный через `projectDir`) с открытием в VS Code / проводнике и настройкой видимости
- **Produced files** под ответом AI (базовый вариант) + ru/en i18n
- Override QR-кода скачивания приложения DeepSeek — свой QR и ссылка на Telegram
- `safe()`-обёртка для защиты от изменений DOM DeepSeek

### Fixed
- `meta.produced` label: Создано → Затронуто / Produced → Affected

## [4.0.16] - 2026-09-14

### Added
- Модуль review context для сбора diff
- Команда `summarize` — краткий итог текущей сессии

### Fixed
- Ненулевой код выхода `bash`/`pwsh` → `❌` в уведомлении Telegram
- Текст ошибки «Слишком частые сообщения» (DeepSeek rate limit) — красный жирный
- Панель/таб Todo больше не всплывает после вызова сторонних инструментов: событие `todo-updated` из `execute-js` шлётся только при реальном изменении списка, а флаг ручного закрытия в рендере сбрасывается только при изменении состава задач
- Устранён краш «Invalid or unexpected token» в `botsrc/index.js` из-за неверного экранирования `\n` в шаблонных строках
- Остановка задачи работает без дочернего процесса

## [4.0.15] - 2026-09-13

### Added
- Стеклянный эффект (glass) для поля ввода DeepSeek

### Fixed
- Иконка приложения в окне и уведомлениях

## [4.0.14] - 2026-09-13

### Changed
- Доработаны UI подтверждения (approval) и inline-результатов

## [4.0.13] - 2026-09-13

### Changed
- Технический релиз (bump версии)

## [4.0.12] - 2026-09-13

### Added
- Подтверждение вызовов инструментов через Telegram (approve tool calls)

## [4.0.11] - 2026-09-13

### Changed
- Скрытие системных сообщений отключено по умолчанию (`hideSystemMessages: false`)

## [4.0.10] - 2026-09-13

### Added
- **Approval gate**: подтверждение вызовов инструментов и JS-блоков
- Inline-результаты инструментов внутри карточек вызовов
- Скрытие service-сообщений в чате для чистого UI

### Fixed
- Stealth-режим пропускал service-сообщения в актуальном UI DeepSeek

## [4.0.9] - 2026-09-13

### Added
- Инструмент `read_photo` — вставка изображений в чат
- Ответы на вопросы AI через inline-клавиатуру Telegram

## [4.0.8] - 2026-09-13

### Added
- Цвета темы, кастомные фоны, перетаскиваемый overlay
- Унифицированы кнопки настроек, добавлена секция Service

## [4.0.7] - 2026-09-12

### Added
- Иконки приложения + папка кастомных фонов

## [4.0.6] - 2026-09-12

### Added
- Принудительная тёмная тема на сайте

## [4.0.5] - 2026-09-12

### Added
- Интерактивный инструмент вопросов пользователю (user question tool)
- Глобальный тумблер кастомизации

## [4.0.3] - 2026-09-12

### Changed
- Язык по умолчанию берётся из системной локали

## [4.0.2] - 2026-09-12

### Changed
- CI: bump версии от max(package.json, последний тег) — защита от коллизий тегов
- CI: исправлен парсинг YAML (`build.yml`), релизный пайплайн снова включён
- Синхронизированы CI-воркфлоу, упрощён `release.bat`, скриншоты в README

### Fixed
- Восстановлен оригинальный скриншот Telegram-бота

## [4.0.1] - 2026-09-12

### Changed
- Лицензия переключена на MIT, новые скриншоты Telegram-бота

## [4.0.0] - 2026-09-12

### Added
- **Панель Todo**: список задач, store и инструменты; фикс всплытия панели/таба при неизменном списке
- **Панель «Изменения»** с git diff + вкладка «История» для просмотра прошлых коммитов
- **@-file mentions**: автодополнение файлов проекта при вводе `@`; результаты-директории в file picker
- **Telegram**: команда `/todos` и уведомление о завершении задач
- i18n overlay: локализованы строки ручного парсинга и выполнения инструментов
- Updater: UI переведён на английский, апдейты указывают на merfiDEV/Cookie-code

### Changed
- Лицензия переключена на MIT; `release.bat` адаптирован под origin
- Удалены `LICENSE` и `HANDOFF_MCP.md`

## [0.3.6] - 2026-09-08

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@
"build/icon.png",
"!**/*.md",
"!LICENSE",
"CHANGELOG.md",
"systemPrompt.md",
"tools/rules.md",
"src/prompt/*.md"
Expand Down
157 changes: 157 additions & 0 deletions src/main/changelog-parser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/**
* Парсер CHANGELOG.md — извлекает блоки версий для whats-new.
*
* Формат, который понимаем (наш CHANGELOG.md):
* ## [4.0.13] - 2026-09-08
* ### Fixed
* - Что-то починили
* - Ещё что-то
* ### Added
* - Новая фича
*
* ## [Unreleased]
* ### Added
* - ...
*
* Не пытаемся быть универсальным парсером Keep a Changelog —
* поддерживаем ровно тот формат, что лежит в нашем репо.
*/
const fs = require('fs');
const path = require('path');

/**
* Сравнить две semver-строки.
* @returns -1 | 0 | 1
*/
function compareSemver(a, b) {
const pa = String(a || '0').split('.').map(n => parseInt(n, 10) || 0);
const pb = String(b || '0').split('.').map(n => parseInt(n, 10) || 0);
for (let i = 0; i < 3; i++) {
const x = pa[i] || 0;
const y = pb[i] || 0;
if (x > y) return 1;
if (x < y) return -1;
}
return 0;
}

/**
* Разобрать CHANGELOG.md в массив записей.
* @param {string} md — содержимое файла
* @returns {Array<{version: string, date: string|null, unreleased: boolean, sections: {title: string, items: string[]}[]}>}
*/
function parseChangelog(md) {
if (!md || typeof md !== 'string') return [];
const lines = md.split(/\r?\n/);
const entries = [];
let current = null;
let currentSection = null;

const VER_RE = /^##\s+\[([^\]]+)\](?:\s*-\s*(.+))?\s*$/;
const SECTION_RE = /^###\s+(.+?)\s*$/;
const ITEM_RE = /^\s*[-*]\s+(.+)$/;

for (const raw of lines) {
const line = raw.replace(/\s+$/, '');
if (!line) continue;

const mVer = line.match(VER_RE);
if (mVer) {
if (current) entries.push(current);
const version = mVer[1].trim();
const date = mVer[2] ? mVer[2].trim() : null;
const unreleased = /unreleased/i.test(version);
current = { version, date, unreleased, sections: [] };
currentSection = null;
continue;
}

if (!current) continue;

const mSec = line.match(SECTION_RE);
if (mSec) {
currentSection = { title: mSec[1].trim(), items: [] };
current.sections.push(currentSection);
continue;
}

const mItem = line.match(ITEM_RE);
if (mItem && currentSection) {
// Одна строка может содержать вложенные подпункты — оставляем как есть
currentSection.items.push(mItem[1].trim());
}
}
if (current) entries.push(current);
return entries;
}

/**
* Выбрать записи версий в диапазоне (from, to].
* from может быть null — тогда возвращаем только to.
* to может быть null — тогда возвращаем всё от from до последнего релиза.
* Unreleased пропускаем.
*/
function extractSince(entries, from, to) {
if (!Array.isArray(entries) || entries.length === 0) return [];
const result = [];
for (const e of entries) {
if (e.unreleased) continue;
// Приводим версию к чистому виду (без префикса v)
const v = String(e.version).replace(/^v/, '').trim();
if (!/^\d+\.\d+\.\d+/.test(v)) continue;

if (from && compareSemver(v, from) <= 0) continue;
if (to && compareSemver(v, to) > 0) continue;
result.push({ ...e, version: v });
}
// Сортируем по убыванию версии (свежие — сверху)
result.sort((a, b) => compareSemver(b.version, a.version));
return result;
}

/**
* Загрузить CHANGELOG.md из встроенного файла.
* Ищем рядом с main-процессом: <appRoot>/CHANGELOG.md.
* В electron-builder файл включён в сборку (не отфильтрован).
*/
function loadBuiltinChangelog() {
const candidates = [
path.join(__dirname, '..', '..', 'CHANGELOG.md'),
path.join(process.resourcesPath || '', 'CHANGELOG.md'),
];
for (const p of candidates) {
try {
if (fs.existsSync(p)) {
return fs.readFileSync(p, 'utf-8');
}
} catch (_) {}
}
return '';
}

/**
* Собрать markdown-строку из выбранных записей — для показа в whats-new.
* Формат компактнее оригинального: версия → разделы → пункты.
*/
function toMarkdown(entries) {
if (!entries || entries.length === 0) return '';
const out = [];
for (const e of entries) {
out.push('### v' + e.version + (e.date ? ' — ' + e.date : ''));
for (const s of e.sections) {
if (!s.items.length) continue;
out.push('**' + s.title + '**');
for (const it of s.items) out.push('- ' + it);
}
out.push('');
}
return out.join('\n').trim();
}

module.exports = {
parseChangelog,
extractSince,
loadBuiltinChangelog,
toMarkdown,
compareSemver,
};
31 changes: 31 additions & 0 deletions src/main/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ if (RENDERER_LOG_DIR) {
}

const { registerIpcHandlers } = require('./ipc');
const whatsNew = require('./whats-new');

// Whats-new: проверить факт обновления ДО создания окна.
// Внутри — атомарное сохранение новой lastSeenVersion, чтобы повторный
// запуск той же версии не показывал список ещё раз.
const __whatsNewCheck = whatsNew.checkAndMark();

// 退出前需要 flush 的 sessions
const sessionsToFlush = new Set();
Expand Down Expand Up @@ -127,10 +133,35 @@ function createWindow(profile) {
// Всегда открываем homeUrl провайдера (DeepSeek). Выбор платформы отключён.
mainWindow.loadURL(provider.homeUrl);

// Инжект перехватчика токенов в ОСНОВНОЙ мир: preload в изолированном мире
// (contextIsolation: true) не видит window.fetch сайта, поэтому патч ставим
// через executeJavaScript и шлём данные в preload через postMessage.
const injectTokenInterceptor = () => {
if (!mainWindow || mainWindow.isDestroyed()) return;
try {
const { buildTokenInterceptorScript } = require('./token-interceptor-inject');
mainWindow.webContents.executeJavaScript(buildTokenInterceptorScript(), true).catch(() => {});
} catch (_) {}
};
mainWindow.webContents.on('dom-ready', injectTokenInterceptor);

mainWindow.webContents.on('did-finish-load', () => {
if (mainWindow && !mainWindow.isDestroyed()) {
injectTokenInterceptor();
mainWindow.webContents.send('page-loaded');
sessionStore.tryRestoreSessionFromUrl(mainWindow);

// Whats-new: отправить в окно один раз, если был апдейт.
// consumePendingForWindow() очищает pending, поэтому на F5 или
// повторный did-finish-load модалка не появится снова.
if (__whatsNewCheck && __whatsNewCheck.shouldShow) {
const pending = whatsNew.consumePendingForWindow();
if (pending) {
try {
mainWindow.webContents.send('whats-new-show', pending);
} catch (_) {}
}
}
}
});

Expand Down
Loading
Loading