---
name: cehoisland-overview
description: "CehoIsland — свой бесплатный аналог Vibe Island (notch-панель для AI-агентов на macOS), ~/Разработки/CehoIsland"
metadata: 
  node_type: memory
  type: project
  originSessionId: 105208c1-99d2-4780-8b6c-5b4d452c951b
---

CehoIsland (2026-07-15) — нативное Swift-приложение: Dynamic Island вокруг чёлки MacBook для AI-агентов. Свой код (Vibe Island НЕ копировался — их установка живёт параллельно). Имя CehoVibe полностью выпилено (репо, бандл, пути, Obsidian, память).

- Репо: `~/Разработки/CehoIsland`; сборка `./scripts/make-app.sh`, DMG `./scripts/make-dmg.sh`; SPM-таргет CehoIsland; bundle com.cebers.cehoisland
- Шина: unix socket `~/.cehoisland/agent.sock`, line-JSON (ключ `"type"`, НЕ "cmd"); hook-клиент на C `~/.cehoisland/bin/cehoisland-hook <event> [--wait] [--agent x] [--payload json]`. nc -U на macOS глючит с ответом — проверять python AF_UNIX клиентом
- Интеграции авто-ставятся в онбординге (HooksInstaller.installAll(selectedIDs:) — чипы агентов переключаемы, снятая галочка = не ставим). Бэкапы конфигов. Claude/Gemini/Cursor/Copilot/ZCode/Droid/Qoder/Qwen/CodeBuddy/WorkBuddy hooks; OpenCode/MiMoCode/Antigravity — detectOnly (JS-плагин ещё не написан)
- **Codex**: НЕ читает hooks.json (Claude-формат) — использует config.toml `notify=[".../codex-notify.sh"]` (shape .codexNotify, installCodexNotify правит одну top-level строку notify+бэкап, [plugins.*] целы). Ограничение: notify только по завершении хода → Codex всегда idle/«закончил», живого «работает» нет (нет блокирующего permission-хука)
- **Заголовок чата = задача** (AgentSession.title): Claude берёт первую строку prompt из user_prompt (обрезка 80), Codex — input-messages/last-assistant-message. Папка·агент·статус в подзаголовке. Иначе два чата в одной папке выглядели одинаково
- **Точки статуса** StatusDot: работает=яркая зелёная+пульс-кольцо+glow, ждёт=оранжевая, простой=полый серый контур
- Молот=Мьёльнир Тора: прямоугольный стальной блок с фасками+обвязка+заклёпки+короткий черенок (drawHammerHead, hw/hh); анимация ForgeScene вокруг лого-наковальни, forgeState-driven
- Панель: nonactivating NSPanel canBecomeKey=true + FirstMouseHostingView (acceptsFirstMouse) — иначе SwiftUI-кнопки (крестик/Allow/Deny) не ловят первый клик; hover через глобальный NSEvent.mouseMoved монитор; кнопка ✕ (onHide→orderOut)
- **Тихие вылеты EXC_GUARD (РЕШЕНО)**: SocketServer.readData — reply-замыкание держало сырой fd; после close(fd) дескриптор переиспользовался под guarded FileHandle (title-резолверы/квоты активно их открывают) → write в чужой guarded fd = смертельный EXC_GUARD. Фикс: поколение-токен connID[fd], reply пишет только если fd=то же живое соединение; closeConnection чистит токен; +signal(SIGPIPE,SIG_IGN). Краши в ~/Library/Logs/DiagnosticReports/*.ips (JSON: заголовок+тело). Урок: сырой fd в замыкании через асинхронную границу = грабли, всегда валидируй актуальность fd
- **Модель+рассуждение чата** (AgentSession.model/reasoning, чип в SessionRowView): Claude — последний assistant.model + блоки thinking из jsonl (ClaudeTitleResolver.info); Codex — model+effort из последнего turn_context в хвосте rollout (CodexWatcher.modelEffort); shortModel сокращает (Opus 4.8 / GPT-5.6)
- **Разрешения — само-объясняющий шаг онбординга** (permissions, индекс 3): карточки «зачем»+кнопка запроса (Уведомления, Универсальный доступ). AX-промпт НЕ на старте (висел каждый запуск) — requestAccessibilityOnce (флаг axPromptShown) лениво при первом GUI-прыжке. Gatekeeper first-open обойти изнутри нельзя (нужна нотаризация/ПКМ→Открыть)
- **Подпись стабильная** «CehoIsland Signing» (self-signed, make-app.sh) — иначе ad-hoc-переподпись при каждой сборке слетала TCC-грант Accessibility. DMG для друзей: ~/Downloads/CehoIsland.dmg
- Квоты: Codex wham/usage (окна по limit_window_seconds), Claude OAuth usage (код есть, кредов на машине нет)
- Фирстиль КодоЦех: акцент #2e8c66, фон #141916, текст #e9efea, glow rgba(46,140,102,.15); лого kodoceh-icon (наковальня); звуки свой тембр (треугольник)
- **Переход в чат** (TerminalJumper): Claude `claude://resume?session=` даёт ДУБЛЬ (это import CLI-сессии) — НЕ использовать; вместо этого WindowFocuser поднимает существующее окно по заголовку (AXWindows→AXTitle match→kAXRaiseAction, нужен Accessibility — запрашиваем при старте, как оригинал Vibe Island; иначе fallback = активация приложения). Codex `codex://threads/<threadId>` оставлен — SPA-навигация, дубля нет (Codex внутри /Applications/ChatGPT.app, схема codex:). Терминалы Terminal/iTerm → точная вкладка по tty (AppleScript). Строка сессии = Button (не onTapGesture — тот не ловит первый клик в nonactivating-панели). Оригинал фокусит по CGWindowID+title scan, для VS Code/Cursor — расширение vibe-island.terminal-focus
- **Заголовки чатов**: Claude из `custom-title` в ~/.claude/projects/<enc>/<sid>.jsonl (ClaudeTitleResolver, хвост 512КБ); Codex desktop из session_index.jsonl thread_name (CodexWatcher, вкл. номера «776»)
- **Пуши**: неподписанной сборке macOS запрещает нативные UNUserNotification («Notifications are not allowed») → фолбэк `osascript display notification` (Notifier). Тумблер pushEnabled. Нативные заработают только для подписанной сборки в /Applications
- Анимация вопроса: `Text` в масштабированном Canvas растеризуется в ноль — рисовать в уменьшенном подслое (крупный шрифт × малый scaleBy). Состояния: работа=искры, вопрос=амбер-облачко «?», ожидание=пульс-кольцо, простой=уголёк
- Рендер-харнесс `CehoIsland --render/-anim/-gif/-ui <dir>` offscreen (ScrollView+интерактив offscreen не рисуются = артефакт)
- НЕ проверено вживую: hover/клики/онбординг/launch-at-login — нужен рестарт Claude для Screen Recording (=обрыв сессии)+Accessibility для cliclick. Проверяю через socket-симуляцию+рендер
- **Комментарий в карточке одобрения**: TextField «Комментарий при отклонении — что сделать иначе»; resolveApproval(comment:)→decision.message. Контракт сверен (офиц. доки + SDK PermissionResult): PermissionRequest allow=`decision.behavior:"allow"`, deny=`{behavior:"deny",message}` (message доходит до модели). Allow свободный коммент НЕ несёт (только updatedInput) — поле реально для deny. Ввод в nonactivating-панель: `becomesKeyOnlyIfNeeded=true` (под живую проверку). Блокирующий permission есть у Claude(PermissionRequest) и Copilot(другой формат); у Codex/Gemini/Cursor НЕТ → карточки нет by design
- **Antigravity Desktop (нативная панель, РАБОТАЕТ)**: НЕ JS-плагин (тот — для отдельного `agy`-CLI, не проверялся живьём). Реальный механизм вскрыт зонд-тестом: hooks.json (PreInvocation/PostToolUse/PostInvocation/Stop, command-type, как у Claude) в `~/.gemini/config/plugins/cehoisland/{plugin.json,hooks.json}` — эталон уже стоявший там vibe-island. Payload несёт `conversationId`(не session_id)+`workspacePaths`(массив,не cwd)+`modelName`+`transcriptPath`(НЕТ текста промпта). AntigravityTitleResolver читает transcript_full.jsonl→последний USER_INPUT→<USER_REQUEST>. Antigravity-"Stop"=конец ХОДА(fullyIdle)→маппится на "stop", не session_end. PermissionRequest НЕ поддерживается Desktop (даже эталон его не ставит) — трекинг есть, авто-одобрение невозможно. Claude Code/Codex/Gemini как расширения Antigravity — отдельно уже видны через ~/.claude,~/.codex (общие хуки), owner=«Antigravity». opencode(реальный проект)/mimocode — opencode оставлен JS-плагином (независимо подтверждён формат через живой vibe-island.js), mimocode вернул detectOnly (не проверялся)
- **Grok CLI (РАБОТАЕТ, включая Allow/Deny)**: формат = точно как Claude, но файл-на-плагин в ~/.grok/hooks/<name>.json (эталон — уже стоявший vibe-island.json). Payload camelCase (sessionId/cwd/workspaceRoot/toolName/toolInput, промпт в <user_query> тегах). PreToolUse блокирует; решение читает ТОЛЬКО exit code (0/2) — JSON/текст на stdout сбивает Grok в default-allow; resources/grok-decide.sh — wrapper (--wait+парсинг decision.behavior→exit 2). Комментарий до модели Grok НЕ доходит (как у Antigravity) — только факт блокировки. Баг: Grok иногда дублирует lifecycle-хук без --agent → фикс в AppDelegate по признаку payload.workspaceRoot
- Доки: Obsidian Projects/CehoIsland (5 файлов). ops_dashboard не нужен (не сервис на mini)

**Why:** повторная работа с notch/hooks/квотами пойдёт отсюда.
**How to apply:** правки — в репо; диагностика — socket state/quotas python-клиентом; см. [[codex-acct]] по квотам Codex.
