From 987bb11c8b4e2cca9042cda878097328632f49ee Mon Sep 17 00:00:00 2001 From: lachtan Date: Tue, 30 Jun 2026 05:32:46 +0200 Subject: [PATCH] runtime zaloha --- AGENTS.md | 19 + USER.md | 24 +- memory/.cursor | 2 +- memory/MEMORY.md | 26 - memory/history.jsonl | 7 + .../2026-06-28_ponytail-claude-pi-deepdive.md | 493 ++++++++++++++++++ .../2026-06-28_ponytail-plugin-analysis.md | 455 ++++++++++++++++ 7 files changed, 977 insertions(+), 49 deletions(-) create mode 100644 results/2026-06-28_ponytail-claude-pi-deepdive.md create mode 100644 results/2026-06-28_ponytail-plugin-analysis.md diff --git a/AGENTS.md b/AGENTS.md index 10e789c..9807b69 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -37,6 +37,25 @@ Never use `/tmp/`, hardcoded absolute paths, or in-memory databases for persiste The exec safety guard blocks commands without an explicit workspace path (e.g. `lua -e '...'`, `which`). Write scripts to files inside the workspace (e.g. `tmp/script.lua`) and run them with `working_dir` set to the workspace root. +## File / Code Conventions + +### Report / result files +- Save important reports to `results/` directory with descriptive, date-prefixed filenames (e.g., `2026-06-02_remind-skill-analysis-and-improvements.md`) + +### Script-writing convention +- Location: always save scripts in the `scripts/` directory. +- Language choice: + - Extremely short script (a few lines) -> bash. + - Longer / non-trivial script -> Python. +- Override: if the user explicitly specifies a language or location, their instruction always takes precedence. + +### Temporary files +- All temporary files go to `tmp/` directory. +- Clean up after tests and one-off operations. + +### Code changes +- User prefers changes to be made in a temporary clone under `workspace/tmp/` for review before applying + ## No proactive actions When I ask you to **find out**, **investigate**, **look into**, or **check** diff --git a/USER.md b/USER.md index 59a9c5c..793bc5c 100644 --- a/USER.md +++ b/USER.md @@ -13,8 +13,7 @@ - Silver Gym - Pokoj „chlívek" — okno (holubí hřeby), projekt teplotního senzoru - Dítě: Ema -- Bětka — řeší pro ni papírování -- Kontakt: Přibyl +- Žena: Bětka ## Preferovaný model - Preset: `glm` @@ -26,7 +25,7 @@ - Monitoring přes Zabbix - Píše kód i spravuje infrastrukturu, ad-hoc skripty pro Linux - Preferované programovací jazyky: C#, Python, Rust -- Skriptovací jazyky: Bash, PowerShell +- Skriptovací jazyky: Bash, PowerShell, Python ## Preferred interaction - Tykání, terse, technicky, bez kytek / bez emojis @@ -59,37 +58,18 @@ - Daily reminders at random times within a window (e.g., 8:00–21:00), not at fixed clock times - Prefers single reminder with multiple fire times rather than separate reminders for each time -## Automation testing -- Při nastavování periodických jobů nejprve otestuje s krátkým intervalem, po potvrzení smaže testovací job a nastaví cílový interval -- U časových testů očekává výstup s milisekundovou přesností -- U nových automatizací preferuje nejprve human-in-the-loop kontrolu; méně důležité úkoly mohou běžet autonomně - ## Zájmy - CLI coding agents — srovnávání, analýza výkonu (zná: Claude Code, Gemini CLI, Qwen Code, Pi-agent, oh-my-pi) - Pro Pi agenta používá AGENTS.md jako primární instrukční soubor, nikoli CLAUDE.md -- Linux tools (strace a další) -- Filmy -- Windows utility -- Poslouchá Radio 1 stream -- DT GLASS sklenice (z láhve vína) — Universal a Amber varianty z darkove-sklo.com #dt-glass -- Chce Coca-Cola sklenice z práce, ideálně dvě ## Poznámky k organizaci - Organizuje poznámky pomocí hashtagů (#chata, #dt-glass, #devops) -- Todo: koupit nové merino triko - -## Co nemá smysl nabízet -- Hardware/IoT funkce (MaixCam, GPIO, I2C, sériový port) — běží v LXC bez fyzického HW ## Životní filozofie - Je mi blízký stoicismus a jeho přístup k životu - Drž se toho, co můžu ovlivnit; zbytek nech být - Když je něco špatně, řekni mi to přímo a bez příkras -## Prostor / rozměry -- Vzdálenost židle–stůl (sedák → deska): 25.5 cm -- Deska stolu: 65×140 cm - ## Připomínky - Uživatel odkazuje připomínky zkráceným názvem, který nemusí substring-matchnout uložený text (např. "wifi do tiskarny" vs "WiFi modul do 3D tiskarny") - Cíluje připomínky přímo přes `#` syntax diff --git a/memory/.cursor b/memory/.cursor index 3456e74..eb8f2fa 100644 --- a/memory/.cursor +++ b/memory/.cursor @@ -1 +1 @@ -372 \ No newline at end of file +379 \ No newline at end of file diff --git a/memory/MEMORY.md b/memory/MEMORY.md index 7b92065..548eb59 100644 --- a/memory/MEMORY.md +++ b/memory/MEMORY.md @@ -4,8 +4,6 @@ This file stores important information that should persist across sessions. ## Project Context -- Both user and assistant run on the same nanobot Docker image -- Ollama cloud subscription: na požádání zobrazit aktuální využití (usage) - nanobot podporuje cross-channel session continuity přes `unifiedSession: true` v `config.json` pod `agents.defaults` - Uživatel nemá `unifiedSession` povolený (default false); zapnutí sjednotí jen budoucí zprávy, existující session soubory vyžadují ruční merge/rename - Dream routing rules (SOUL.md → personality, USER.md → user profile, MEMORY.md → project knowledge) are hardcoded in Dream prompt, not user-configurable beyond interval/model/batch size @@ -14,30 +12,6 @@ This file stores important information that should persist across sessions. - User wants to try writing a nanobot extension in TypeScript - Pi agent načítá všechny AGENTS.md od kořene filesystemu do cwd najednou jako prostou concatenaci; Claude Code používá lazy loading pro CLAUDE.md v podadresářích a podmíněná pravidla v `.claude/rules/*.md` s atributem `paths` - Zálohy: wood.hell → pivo.hell (rekurzivní) -- „giga" zařízení k integraci do Zabbix monitoringu - -## Agent Model Selection -- Prioritizes agentic performance, correct tool calling, and overall result quality -- Conservative fallback nanobot agent model: DeepSeek V3.2:cloud -- OpenRouter is pay-per-token alternative to Ollama subscription for model access -- Gemini Flash via Google AI Studio Free Tier: 15 RPM limit — unusable for agent work; only viable for simple prompts without tool calls -- Gemini Flash via Google AI Studio Tier 1 (paid): 360+ RPM -- Haiku (Claude) via OpenRouter: high rate limits, viable agent model alternative -- Gemini Flash Lite via OpenRouter: high rate limits, viable agent model alternative -- Kimi K2.6 has known bug with random switching to Chinese output (reported by Cursor and Reddit users) — critical risk for Czech use -- minimax-m3:cloud is blocked for nanobot agent deployment due to empty tool result responses (ollama/ollama #16389) -- deepseek-v4-pro:cloud is blocked for interactive nanobot agent use due to 15.4 tok/s and 57s TTFT -- deepseek-v4-flash:cloud is a viable nanobot agent alternative with 1M ctx, MIT license, and ~30-50 tok/s speed -- qwen3.5:397b-cloud is a viable nanobot agent alternative with multimodal support, 1M ctx, 201 languages including Czech, but has speed and accuracy tradeoffs -- devstral-2:123b-cloud is a viable nanobot agent alternative with Terminal-Bench 77.3%, coding-only focus, and 128K ctx limit -- User is interested in switching to GLM-5.2:cloud as primary nanobot agent model if/when it becomes available on Ollama Cloud -- User prefers Qwen model for deep research tasks (not currently in presets) -- Available model presets: gemini-flash, gemini-flash-lite, glm, haiku, kimi, minimax, nemotron-3-super, sonnet -- Model specs: `nemotron-3-super` ctx 262144 / max output 16384; `gemini-flash` ctx 256000 / max output 16384 - -### NuGet package caching -- Evaluating NuGet package hosting/caching on Linux -- Preferred solution: BaGetter (bagetter/BaGetter) — active BaGet fork with multiple upstream mirror support (PR #269), solves original BaGet's single-upstream limitation ## Runtime / Deployment - Installed as a `uv` tool: package `nanobot-ai`, update via `uv tool upgrade nanobot-ai` diff --git a/memory/history.jsonl b/memory/history.jsonl index 66b370c..20283f7 100644 --- a/memory/history.jsonl +++ b/memory/history.jsonl @@ -370,3 +370,10 @@ {"cursor": 370, "timestamp": "2026-06-22 17:56", "content": "Now I have enough context. Let me process the source `lego-mindstorms-continued-use.md`. The raw file is sparse (just a URL + topic line), but I've fetched substantial content from the blog and related sources. Let me do the full ingest.\n\n**Step 4: Identify what's touched** — This is a new domain (LEGO robotics) with no overlap to existing wiki pages about coding agents. I need to create:\n- 1 source page\n- 3 entity pages (LEGO MINDSTORMS, Pybricks, SPIKE Prime)\n- 2 concept pages (digital preservation, alternative firmware)\n- Update index.md and log.md\n\nLet me write the pages:\n[2026-06-20T20:14] TOOL: ---\ntype: source\ntitle: \"How to Continue Using LEGO MINDSTORMS After Discontinuation\"\nauthors: [\"Sizlaq (ROBOTMAK3RS)\", \"Pybricks team\", \"Anton (Antons Mindstorms)\"]\nurl: \"https://blog.robotmak3rs.com\"\nraw: \"raw/lego-mindstorms-continued-use.md\"\ningested: 2026-06-20\ntags: [lego, mindstorms, robotics, preservation, pybricks, spike-prime, discontinued]\nentities: [product-lego-mindstorms, product-pybricks, product-spike-prime]\nconcepts: [digital-preservation, alternative-firmware]\nslug: source-lego-mindstorms-continued-use\ngraph:\n node_id: source:lego-mindstorms-continued-use\n canonical: true\n relationships:\n - predicate: mentions\n object: product:lego-mindstorms\n source: lego-mindstorms-continued-use\n evidence: \"Zdroj se zabývá pokračováním používání LEGO MINDSTORMS po ukončení produktu\"\n confidence: high\n status: current\n - predicate: mentions\n object: product:pybricks\n source: lego-mindstorms-continued-use\n evidence: \"Pybricks jako hlavní alternativa pro MINDSTORMS firmware a software\"\n confidence: high\n status: current\n - predicate: mentions\n object: product:spike-prime\n source: lego-mindstorms-continued-use\n evidence: \"SPIKE Prime jako oficiální nástupce MINDSTORMS z LEGO Education\"\n confidence: high\n status: current\n---\n\n# How to Continue Using LEGO MINDSTORMS After Discontinuation\n\nZdroj pokrývá problematiku dalšího používání LEGO MINDSTORMS po ukončení produktové řady — od archivace aplikací a obsahu přes alternativní firmware až po přechod na nástupnické platformy.\n\n## Klíčové body\n\n- **Ukončení MINDSTORMS** — LEGO Group ukončilo produktovou řadu MINDSTORMS koncem roku 2022 (fyzický produkt Robot Inventor 51515). Aplikace Robot Inventor bude ukončena 1. října 2026. LEGO směřuje uživatele k SPIKE Prime.\n- **Problém zmizelých aplikací** — MINDSTORMS aplikace postupně mizí z app store nebo přestávají fungovat na nových zařízeních. Obsah ke stažení uvnitř aplikace (tutoriály, build instrukce) přestane být dostupný, když LEGO vypne servery.\n- **Archivace a restore** — Komunita ROBOTMAK3RS (autor Sizlaq) vytvořila návody na obnovení MINDSTORMS aplikace s veškerým obsahem na Android (root potřebný pro app data), macOS a Windows. Cílem je „nejen instalovat aplikaci, ale obnovit ji do použitelného stavu bez závislosti na LEGO serverech\".\n- **Pybricks jako alternativa** — Pybricks je open-source firmware a MicroPython prostředí, které funguje na EV3, NXT, Robot Inventor, SPIKE Prime a dalších LEGO hubech. Nahrazuje původní firmware, umožňuje instant boot, blokové i textové programování, a je aktivně vyvíjen.\n- **Kompatibilita hubů** — MINDSTORMS Robot Inventor hub má stejný tvar jako SPIKE Prime hub, ale SPIKE3 firmware na něj nejde nainstalovat. Pybricks funguje na obou.\n- **EV3 revival** — Pybricks pro EV3 je v aktivním vývoji (prosinec 2025): instant boot, MicroPython, podpora všech EV3 motorů a senzorů, NXT senzorů na EV3, bez nutnosti microSD karty.\n- **SPIKE Prime jako nástupce** — LEGO Education SPIKE Prime je oficiální nástupnická platforma. LEGO doporučuje přechod na SPIKE Prime App.\n\n## Omezení zdroje\n\n- Původní URL (blog.robotmak3rs.com) je blog s více články; specifický článek o macOS restore vrací 404. Informace byly doplněny z Android restore článku, Pybricks projektu a Antons Mindstorms článku.\n- Datum ukončení aplikace (říjen 2026) pochází z app store listingů, nikoli z primárního zdroje.\n\n## Související\n\n- [[product-lego-mindstorms]] — hlavní produktová řada\n- [[product-pybricks]] — open-source alternativa\n- [[product-spike-prime]] — oficiální nástupce\n- [[digital-preservation]] — koncept zachování softwaru\n- [[alternative-firmware]] — koncept nahrazení vendor firmware\n[2026-06-20T20:14] TOOL: ---\ntype: entity\nkind: product\ntitle: \"LEGO MINDSTORMS\"\ntags: [lego, robotics, discontinued, education]\nsources: [lego-mindstorms-continued-use]\ncreated: 2026-06-20\nupdated: 2026-06-20\ngraph:\n node_id: product:lego-mindstorms\n canonical: true\n relationships:\n - predicate: competes_with\n object: product:spike-prime\n source: lego-mindstorms-continued-use\n evidence: \"SPIKE Prime je oficiální nástupce MINDSTORMS z LEGO Education\"\n confidence: high\n status: historical\n - predicate: depends_on\n object: product:pybricks\n source: lego-mindstorms-continued-use\n evidence: \"Pybricks umožňuje další používání MINDSTORMS hardwaru po ukončení oficiální podpory\"\n confidence: medium\n status: current\n---\n\n# LEGO MINDSTORMS\n\nProduktová řada programovatelných robotických stavebnic od LEGO, ukončená koncem roku 2022. Zahrnuje několik generací: RCX (1998), NXT (2006), EV3 (2013) a Robot Inventor (2020).\n\n## Historie ukončení\n\n- **Říjen 2022** — LEGO Group oznámilo ukončení produktové řady MINDSTORMS. Fyzický produkt Robot Inventor (51515) a příslušné prvky (88016, 88018) byly staženy z portfolia.\n- **Aplikace do konce 2024** — Digitální platformy (Robot Inventor App) měly zůstat funkční minimálně do konce 2024.\n- **Říjen 2026** — Aplikace Robot Inventor bude oficiálně ukončena (1. října 2026), bez dalších aktualizací a podpory. LEGO doporučuje přechod na SPIKE Prime App.\n\n## Problémy po ukončení\n\n- Aplikace postupně mizí z app store nebo přestávají fungovat na nových zařízeních\n- Obsah ke stažení uvnitř aplikace (tutoriály, build instrukce) přestane být dostupný\n- Hardware zůstává funkční, ale bez softwaru je nepoužitelný — typický případ plánovaného zastarávání\n\n## Komunitní řešení\n\n- **ROBOTMAK3RS** — návody na archivaci a obnovení aplikace s obsahem na Android, macOS a Windows\n- **Pybricks** — open-source firmware nahrazující původní software, funguje na EV3, NXT, Robot Inventor i SPIKE Prime\n\n## Generace\n\n| Generace | Rok | Hub |\n|----------|-----|-----|\n| RCX | 1998 | IR tower |\n| NXT | 2006 | NXT Intelligent Brick |\n| EV3 | 2013 | EV3 Intelligent Brick |\n| Robot Inventor | 2020 | 6-port hub (stejný jako SPIKE) |\n\n## Související\n\n- [[product-pybricks]] — open-source alternativa\n- [[product-spike-prime]] — oficiální nástupce\n- [[digital-preservation]] — zachování softwaru po ukončení podpory\n- [[alternative-firmware]] — nahrazení vendor firmware\n- [[source-lego-mindstorms-continued-use]] — zdrojový článek\n[2026-06-20T20:14] TOOL: ---\ntype: entity\nkind: product\ntitle: \"Pybricks\"\ntags: [lego, robotics, open-source, firmware, python]\nsources: [lego-mindstorms-continued-use]\ncreated: 2026-06-20\nupdated: 2026-06-20\ngraph:\n node_id: product:pybricks\n canonical: true\n relationships:\n - predicate: competes_with\n object: product:lego-mindstorms\n source: lego-mindstorms-continued-use\n evidence: \"Pybricks nahrazuje oficiální MINDSTORMS software po ukončení produktu\"\n confidence: medium\n status: current\n---\n\n# Pybricks\n\nOpen-source firmware a MicroPython programovací prostředí pro LEGO robotics huby. Umožňuje další používání ukončených LEGO MINDSTORMS produktů a nabízí modernější API než oficiální aplikace.\n\n## Klíčové vlastnosti\n\n- **Open-source firmware** — nahrazuje původní LEGO firmware na hubech (EV3, NXT, Robot Inventor, SPIKE Prime, SPIKE Essential, BOOST, Powered Up, Technic, WeDo)\n- **MicroPython** — programování v Pythonu přímo na hubu, s blokovou alternativou (Word Blocks)\n- **Instant boot** — na rozdíl od původního EV3 firmware (pomalý boot přes Linux), Pybricks nabootuje okamžitě\n- **Webová ap\n... (truncated)"} {"cursor": 371, "timestamp": "2026-06-24 00:20", "content": "- User communicates in Czech; prefers technical explanations comparing architectures (Pi vs nanobot)"} {"cursor": 372, "timestamp": "2026-06-24 16:38", "content": "- [durable] User communicates in Czech\n- [durable] User uses Pi agent; AGENTS.md is their primary instruction file (not CLAUDE.md)\n- [ephemeral] User is exploring AGENTS.md vs SYSTEM.md vs CLAUDE.md configuration systems for coding agents", "session_key": "telegram:8826147089"} +{"cursor": 373, "timestamp": "2026-06-25 07:57", "content": "- [durable] User wants the daily `/compact-memory` auto job to deliver its cleanup result as a notification (same output as a manual run).\n- [durable] Changed cron job `compact-memory-auto-daily` payload setting `deliver` from `false` to `true`.\n- [durable] Moved the four file/code convention rules (reports → `results/`, scripts → `scripts/` bash/Python, temp files → `tmp/`, code changes → `workspace/tmp/`) from `memory/MEMORY.md` to `AGENTS.md` under `## File / Code Conventions`.\n- [durable] Operational agent rules belong in `AGENTS.md`, not `SOUL.md`, because `SOUL.md` is Dream-managed and reserved for personality/values.", "session_key": "websocket:e4be2a20-295d-4414-9d80-e8efe1edf752"} +{"cursor": 374, "timestamp": "2026-06-25 08:38", "content": "- [durable] Model preset `nemotron-3-super` has context window 262144 and max output tokens 16384 \n- [durable] Model preset `gemini-flash` has context window 256000 and max output tokens 16384 \n- [correction] Model switch to `gemini-flash` required manual config change (`tools.my.allow_set = true`) due to initial error \n- [important] Reminder system uses cron expressions and absolute timestamps (e.g., \"cedule proti kouření ve výtahu\" at 2026-06-02T09:20:00) \n- [durable] Note system requires explicit tag registration before use (e.g., `#devops` tag used for \"najít využití pro pi-agent\") \n- [ephemeral] Lua script created temporarily in workspace to get current time when direct `/time` command failed \n- [durable] Python script executed in workspace to calculate factorials for numbers 4, 7, 9, 12 \n- [correction] Model `qwen3.5:9b` failed to return time answer on 2026-06-25 despite having context window 65536, likely due to API timeout or rate limit \n- [durable] Note tags include `#chata` (home-related tasks), `#dt-glass` (specific product research), and `#devops` (technical projects) \n- [permanent] User prefers explicit task management via `/note` system with strict tagging protocol (no inferred tags)", "session_key": "cli:direct"} +{"cursor": 375, "timestamp": "2026-06-29 05:05", "content": "- [durable] Pi načítá všechny AGENTS.md soubory od kořene filesystemu až do cwd najednou při startu jako prostou concatenaci; Claude Code používá lazy loading pro CLAUDE.md v podadresářích a podmíněná pravidla v `.claude/rules/*.md` s atributem `paths`.\n- [durable] Skript `remind_cli.py` vyžaduje Python modul `croniter`, který není nainstalován (ModuleNotFoundError).\n- [durable] Příkaz `remind_cli.py remove` vyžaduje přepínač `--id ID` nebo `--keyword KEYWORD`, nepřijímá poziční argumenty.\n- [durable] Prostředí má aktivní tvrdou bezpečnostní politiku `restrict_to_workspace`, která blokuje přístup mimo pracovní adresář; pokusy o obcházení přes shell triky jsou zakázány.", "session_key": "telegram:8826147089"} +{"cursor": 376, "timestamp": "2026-06-29 05:07", "content": "- [permanent] User uses `uv` for nanobot installation and upgrades; do not suggest pip, pipx, or docker.\n- [permanent] User expects explicit confirmation before the assistant writes or modifies files.\n- [permanent] When batch-deleting reminders by display number, user expects the assistant to handle shifting IDs (e.g., delete highest first or use keyword matching).\n- [durable] User has servers named wood.hell and pivo.hell; performs recurring backups from wood.hell to pivo.hell.\n- [durable] User has a \"chlívek\" with a temperature sensor, MQTT, and pigeon spikes on the window.\n- [durable] User uses litellm proxy for ollama.\n- [durable] User wants to connect a device/server named \"giga\" to Zabbix.\n- [durable] User has a project/tool called pipepilot.\n- [durable] User wants to start using llm wiki plugin.", "session_key": "telegram:8826147089"} +{"cursor": 377, "timestamp": "2026-06-29 16:50", "content": "- [permanent] User communicates in Czech; respond in Czech.\n- [durable] User has a child named Ema.\n- [durable] User has someone named Bětka (likely a partner/child) for whom they handle paperwork.\n- [durable] User's servers: wood.hell, pivo.hell.\n- [durable] User uses Zabbix for monitoring; has a \"giga\" device to integrate into it.\n- [durable] User goes to Silver Gym.\n- [durable] User has a space/room called \"chlívek\" with a window (pigeon spikes needed) and a temperature sensor project.", "session_key": "telegram:8826147089"} +{"cursor": 378, "timestamp": "2026-06-29 20:58", "content": "- [skip] User queried what was stored in memory over the last 4 days — informational, no new facts\n- [skip] User asked if all history entries were propagated to MEMORY.md — informational query\n- [skip] User asked about compact-memory skill — informational query\n- [durable] Dream's routing rules (SOUL.md → personality, USER.md → user profile, MEMORY.md → project knowledge) are hardcoded in the Dream prompt and not user-configurable beyond interval/model/batch size\n- [durable] Dream Phase 1 does dedup detection across memory files; Phase 2 does surgical line-level edits, never full file rewrites\n- [durable] MEMORY.md lines older than 14 days get age annotations (e.g. `← 37d`) injected only into Dream's prompt, not written to disk", "session_key": "telegram:8826147089"} +{"cursor": 379, "timestamp": "2026-06-30 05:19", "content": "- [durable] Dream pravidla (Phase 1 + Phase 2 instrukce, fact/decision/ephemeral klasifikace, dedup logika) jsou hardcoded v Python zdrojovém kódu nanobotu, ne v konfiguračních souborech\n- [durable] Konfigurovatelné Dream nastavení je omezené na: intervalH, modelOverride, maxBatchSize, maxIterations\n- [durable] Workspace safety guard omezuje file tooly na ~/.nanobot/workspace/ — cesty mimo něj jsou nepřístupné i pro čtení zdrojového kódu nanobotu\n- [durable] Nanobot zdrojový kód je instalován z /home/nanobot/.local/share/uv/tools/nanobot-ai/lib/python3.13/site-packages/nanobot/", "session_key": "telegram:8826147089"} diff --git a/results/2026-06-28_ponytail-claude-pi-deepdive.md b/results/2026-06-28_ponytail-claude-pi-deepdive.md new file mode 100644 index 0000000..ac24a0a --- /dev/null +++ b/results/2026-06-28_ponytail-claude-pi-deepdive.md @@ -0,0 +1,493 @@ +# Ponytail Plugin — Deep-dive: Claude Code a Pi Coding Agent + +**Zdroj:** https://github.com/DietrichGebert/ponytail +**Verze:** 4.8.3 | **Licence:** MIT +**Analýza vyčtená přímo ze zdrojových kódů** + +--- + +## 1. Claude Code — jak se plugin zapojuje + +### 1.1 Manifest (`.claude-plugin/plugin.json`) + +```json +{ + "name": "ponytail", + "version": "4.8.3", + "description": "Lazy senior dev mode...", + "author": { "name": "Dietrich Gebert", "url": "..." }, + "hooks": "./hooks/claude-codex-hooks.json" +} +``` + +**Jediné klíčové pole:** `hooks` → ukazuje na JSON soubor s definicemi lifecycle hook bindings. Claude Code při načtení pluginu přečte tento manifest a zaregistruje hooky. + +### 1.2 Hook bindings (`hooks/claude-codex-hooks.json`) + +```json +{ + "hooks": { + "SessionStart": [ + { + "matcher": "startup|resume|clear|compact", + "hooks": [ + { + "type": "command", + "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/ponytail-activate.js\"; exit 0", + "timeout": 5, + "statusMessage": "Loading ponytail mode..." + } + ] + } + ], + "SubagentStart": [ + { + "hooks": [ + { + "type": "command", + "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/ponytail-subagent.js\"; exit 0", + "timeout": 5 + } + ] + } + ], + "UserPromptSubmit": [ + { + "hooks": [ + { + "type": "command", + "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/ponytail-mode-tracker.js\"; exit 0", + "timeout": 5 + } + ] + } + ] + } +} +``` + +**Tři hook body, každý spouští Node.js skript:** + +| Hook | Kdy se spouští | Co dělá | +|------|---------------|---------| +| `SessionStart` | Při `startup`, `resume`, `clear`, `compact` | Aktivuje mód, zapíše flag file, emituje instrukce do system promptu | +| `SubagentStart` | Při spuštění subagentu (Task tool) | Předá ponytail kontext subagentu | +| `UserPromptSubmit` | Při každém user promptu | Detekuje `/ponytail` commandy, přepíná mód | + +**Důležité:** `exit 0` na konci každého commandu — hook musí skončit úspěšně, jinak Claude považuje celý hook za failed. + +--- + +### 1.3 SessionStart hook (`ponytail-activate.js`) — 91 řádků + +**Flow:** + +1. **Načte default mód** z `ponytail-config.js` (env var > config file > `'full'`) +2. **Pokud `off`:** vymaže flag file, vypíše `'OK'` (nebo prázdný string pro Codex), exit +3. **Zapíše flag file** `~/.claude/.ponytail-active` s aktuálním módem +4. **Načte instrukce** z `SKILL.md` přes `ponytail-instructions.js`, filtruje podle módu +5. **Detekuje statusline** — kontroluje `~/.claude/settings.json` zda existuje `statusLine` config; pokud ne, připojí do outputu nudge pro uživatele +6. **Vypíše instrukce na stdout** — Claude Code stdout zachytí a **připojí k system promptu** + +**Klíčový kód (zkráceně):** + +```javascript +const mode = getDefaultMode(); // 'full' default + +if (mode === 'off') { + clearMode(); + writeHookOutput('SessionStart', 'off', isCodex ? '' : 'OK'); + process.exit(0); +} + +setMode(mode); // zapíše ~/.claude/.ponytail-active +let output = getPonytailInstructions(mode); // načte SKILL.md, filtruje + +// Detekce chybějícího statusline +if (!hasStatusline) { + output += "\n\nSTATUSLINE SETUP NEEDED: ..."; +} + +writeHookOutput('SessionStart', mode, output); // → stdout → Claude system prompt +``` + +**Stdout capture:** Claude spustí skript jako shell command, přečte stdout a **připojí obsah k system promptu** jako hidden context. Uživatel to nevidí, ale LLM to vidí jako instrukce. + +--- + +### 1.4 SubagentStart hook (`ponytail-subagent.js`) — 22 řádků + +**Proč existuje:** SessionStart context je **parent-thread only** a nikdy nedosáhne subagentů (issue #252 v ponytail repo). Bez tohoto hooku by každý Task-spawned agent běžel bez ponytail pravidel. + +**Flow:** + +1. Přečte mód z flag file (`readMode()`) +2. Pokud `off` nebo chybí flag — exit, nic neinjectuje +3. Načte instrukce pro daný mód +4. Vypíše **JSON s `hookSpecificOutput`** — Claude SubagentStart vyžaduje tento formát + +```javascript +const mode = readMode(); +if (!mode || mode === 'off') process.exit(0); + +writeHookOutput('SubagentStart', mode, getPonytailInstructions(mode)); +``` + +**Rozdíl oproti SessionStart:** SubagentStart **musí** vracet JSON: +```json +{"hookSpecificOutput": {"hookEventName": "SubagentStart", "additionalContext": "...instrukce..."}} +``` + +Pokud by vrátil raw text (jako SessionStart), Claude by kontext zahodil. + +--- + +### 1.5 UserPromptSubmit hook (`ponytail-mode-tracker.js`) — 55 řádků + +**Flow:** + +1. Čte **stdin jako JSON** (`{"prompt": "..."}`) — Claude předává user prompt hookům přes stdin +2. Parsuje prompt, hledá `/ponytail [lite|full|ultra|off]` nebo `/ponytail-review` +3. Při matchi zapíše/maže flag file a emituje potvrzení +4. Detekuje deaktivační commandy: `"stop ponytail"` nebo `"normal mode"` (celá zpráva, ne substring) + +```javascript +process.stdin.on('end', () => { + const data = JSON.parse(input.replace(/^\uFEFF/, '')); // strip BOM + const prompt = (data.prompt || '').trim().toLowerCase(); + + if (/^[/@$]ponytail/.test(prompt)) { + const parts = prompt.split(/\s+/); + const cmd = parts[0].replace(/^[@$]/, '/'); + const arg = parts[1] || ''; + + if (cmd === '/ponytail-review') mode = 'review'; + else if (cmd === '/ponytail') { + if (arg === 'lite') mode = 'lite'; + else if (arg === 'full') mode = 'full'; + else if (arg === 'ultra') mode = 'ultra'; + else if (arg === 'off') mode = 'off'; + else mode = getDefaultMode(); + } + + if (mode && mode !== 'off') { + setMode(mode); + writeHookOutput('UserPromptSubmit', mode, 'PONYTAIL MODE CHANGED — level: ' + mode); + } else if (mode === 'off') { + clearMode(); + writeHookOutput('UserPromptSubmit', 'off', 'PONYTAIL MODE OFF'); + } + } +}); +``` + +**Důležité:** `UserPromptSubmit` hook běží **před** zpracováním promptu LLM. Pokud detekuje `/ponytail` command, zapíše flag file a emituje potvrzení — ale samotný `/ponytail` text pak jde do LLM jako normální prompt. LLM vidí potvrzení změny módu a reaguje na něj. + +--- + +### 1.6 Runtime (`ponytail-runtime.js`) — 68 řádků + +**Flag file pattern:** +- **Cesta:** `~/.claude/.ponytail-active` (nebo `$CLAUDE_CONFIG_DIR/.ponytail-active`) +- **Obsah:** prostý text — `'lite'`, `'full'`, `'ultra'`, `'review'`, nebo chybí = off + +**Output formáty podle runtime:** + +```javascript +const isCopilot = Boolean(process.env.COPILOT_PLUGIN_DATA); +const isCodex = !isCopilot && Boolean(process.env.PLUGIN_DATA); + +function writeHookOutput(event, mode, context) { + if (isCopilot) { + // Copilot: JSON s additionalContext na SessionStart + process.stdout.write(JSON.stringify( + event === 'SessionStart' && context ? { additionalContext: context } : {})); + return; + } + if (isCodex) { + // Codex: JSON se systemMessage + hookSpecificOutput + const output = { systemMessage: `PONYTAIL:${mode.toUpperCase()}` }; + if (context) output.hookSpecificOutput = { hookEventName: event, additionalContext: context }; + process.stdout.write(JSON.stringify(output)); + return; + } + // Native Claude: + if (event === 'SubagentStart') { + // SubagentStart vyžaduje JSON formát + process.stdout.write(JSON.stringify( + { hookSpecificOutput: { hookEventName: event, additionalContext: context } })); + return; + } + // SessionStart / UserPromptSubmit: raw stdout + process.stdout.write(context); +} +``` + +**Detekce runtime:** +- `PLUGIN_DATA` env var = Codex CLI +- `COPILOT_PLUGIN_DATA` env var = GitHub Copilot +- Nic z toho = native Claude Code + +--- + +### 1.7 Instrukce builder (`ponytail-instructions.js`) — 94 řádků + +**Jedna pravda pro všechny platformy:** + +1. Čte `skills/ponytail/SKILL.md` (relativní cesta `../skills/ponytail/SKILL.md`) +2. Odstraní YAML frontmatter (`---...---`) +3. **Filtruje podle módu:** + - Tabulky: řádky začínající `| **mode** |` se ponechají jen pro aktivní mód + - Worked examples: řádky `- mode: ...` se ponechají jen pro aktivní mód + - Ostatní řádky (obecná pravidla) se ponechají vždy +4. Pro `review` mód vrací jiný text: `'PONYTAIL MODE ACTIVE — level: review. Behavior defined by /ponytail-review skill.'` +5. Fallback: pokud `SKILL.md` neexistuje, generuje inline instrukce (`getFallbackInstructions()`) + +```javascript +function getPonytailInstructions(mode) { + const configuredMode = normalizePersistedMode(mode) || DEFAULT_MODE; + + if (INDEPENDENT_MODES.has(configuredMode)) { // 'review' + return 'PONYTAIL MODE ACTIVE — level: ' + configuredMode + '. Behavior defined by /ponytail-' + configuredMode + ' skill.'; + } + + try { + return 'PONYTAIL MODE ACTIVE — level: ' + effectiveMode + '\n\n' + + filterSkillBodyForMode(fs.readFileSync(SKILL_PATH, 'utf8'), effectiveMode); + } catch (e) { + return getFallbackInstructions(effectiveMode); // fallback když SKILL.md chybí + } +} +``` + +--- + +### 1.8 Konfigurace (`ponytail-config.js`) — 122 řádků + +**Resolution order default módu:** +1. `PONYTAIL_DEFAULT_MODE` env var +2. `$XDG_CONFIG_HOME/ponytail/config.json` nebo `~/.config/ponytail/config.json` +3. `'full'` + +**Validní módy:** `off`, `lite`, `full`, `ultra`, `review` + +**Deaktivace:** `isDeactivationCommand(text)` matchuje celou zprávu `"stop ponytail"` nebo `"normal mode"` (ignoruje case a trailing punctuation). Důvod: dřívější verze matchovaly substring, což deaktivovalo mód u běžných requestů jako "add a normal mode toggle". + +--- + +## 2. Pi Coding Agent — jak se plugin zapojuje + +### 2.1 Plugin entry point (`pi-extension/index.js`) — 189 řádků, ESM + +```javascript +import { createRequire } from "node:module"; +const require = createRequire(import.meta.url); +// Import sdílených modulů z hooks/ +const { getDefaultMode, normalizePersistedMode, ... } = require("../hooks/ponytail-config.js"); +const { getPonytailInstructions, filterSkillBodyForMode } = require("../hooks/ponytail-instructions.js"); + +export default function ponytailExtension(pi) { + let currentMode = DEFAULT_MODE; + let configuredDefaultMode = getDefaultMode(); + let isActive = false; + let lastCtx = null; + // ... +} +``` + +**Pi používá stejné sdílené moduly jako Claude** (`ponytail-config.js`, `ponytail-instructions.js`) — jedna pravda pro obě platformy. + +--- + +### 2.2 Command registrace + +Pi má **explicitní API** pro commandy — nikoli hooky jako Claude: + +```javascript +pi.registerCommand("ponytail", { + description: "Set or report Ponytail mode", + handler: async (args, ctx) => { + const parsed = parsePonytailCommand(args, configuredDefaultMode); + + if (parsed.type === "status") { + ctx?.ui?.notify?.(`Ponytail: current ${currentMode} • default ${configuredDefaultMode}`, "info"); + return; + } + if (parsed.type === "set-default") { + writeDefaultMode(parsed.mode); // zapíše do ~/.config/ponytail/config.json + configuredDefaultMode = getDefaultMode(); + return; + } + if (parsed.type === "set-mode") { + setMode(parsed.mode, ctx); // změní currentMode, zapíše do session entries + return; + } + } +}); +``` + +**Podporované argumenty:** +- `/ponytail` → toggle (off → full, jinak default) +- `/ponytail lite|full|ultra|off` → set mód +- `/ponytail status` → zobrazí current + default +- `/ponytail default ` → nastaví default mód do config file + +**Skill aliasy:** +```javascript +pi.registerCommand("ponytail-review", { + handler: (_args, ctx) => sendAlias("/skill:ponytail-review", "", ctx) +}); +// + ponytail-audit, ponytail-gain, ponytail-debt, ponytail-help +``` + +`sendAlias()` posílá zprávu jako user message — buď okamžitě (pokud agent idle) nebo jako follow-up (pokud agent běží). + +--- + +### 2.3 Event hooks (`pi.on()`) + +Pi používá **event-driven API** místo Claude lifecycle hooks: + +| Event | Kdy se spouští | Co dělá | +|-------|---------------|---------| +| `input` | Při každém user inputu | Detekuje `"stop ponytail"` / `"normal mode"`, deaktivuje | +| `session_start` | Při startu session | Načte mód z session historie nebo defaultu, sync status bar | +| `agent_start` | Když agent začne pracovat | `isActive = true`, sync status bar | +| `agent_end` | Když agent skončí | `isActive = false`, sync status bar | +| `before_agent_start` | **Před LLM voláním** | **Injectuje instrukce do systemPrompt** | + +**Klíčový hook — `before_agent_start`:** + +```javascript +pi.on("before_agent_start", async (event) => { + if (!currentMode || currentMode === "off") return; + return { systemPrompt: `${event.systemPrompt}\n\n${getPonytailInstructions(currentMode)}` }; +}); +``` + +**Rozdíl oproti Claude:** Pi **explicitně modifikuje** `systemPrompt` a **vrací ho** jako return value. Claude zachytává stdout a připojuje ho interně. + +--- + +### 2.4 Persistencia módu v Pi + +Pi **nepoužívá flag file** jako Claude. Místo toho: + +1. **Session entries:** `pi.appendEntry("ponytail-mode", { mode: normalized })` — zapisuje mód do session logu +2. **Session restore:** `resolveSessionMode(entries, fallback)` — při `session_start` čte historii entries typu `"ponytail-mode"` od konce a najde poslední uložený mód +3. **Default mód:** `getDefaultMode()` ze sdíleného configu (env var > `~/.config/ponytail/config.json` > `'full'`) + +```javascript +export function resolveSessionMode(entries, fallbackMode = DEFAULT_MODE) { + if (!Array.isArray(entries)) return fallback; + for (let i = entries.length - 1; i >= 0; i -= 1) { + const entry = entries[i]; + if (entry?.type !== "custom" || entry?.customType !== "ponytail-mode") continue; + const mode = normalizePersistedMode(entry?.data?.mode); + if (mode) return mode; + } + return fallback; +} +``` + +**Proč ne flag file?** Pi agent má vlastní session management (`sessionManager.getBranch()`, `sessionManager.getEntries()`). Plugin využívá nativní Pi API místo externího souboru. + +--- + +### 2.5 Status bar + +```javascript +function syncStatus(ctx) { + if (!c?.ui?.setStatus || !c.ui.theme?.fg) return; + const levelIcons = { lite: "🌿", full: "⚡", ultra: "🔥" }; + const icon = levelIcons[currentMode] || ""; + const label = currentMode.toUpperCase(); + const indicator = isActive ? theme.fg("accent", "●") : theme.fg("dim", "○"); + c.ui.setStatus("ponytail", indicator + " 🐴 " + theme.fg("muted", "ponytail: ") + theme.fg("text", icon + " " + label)); +} +``` + +Pi má **nativní status bar API** (`ui.setStatus`). Claude nemá — proto potřebuje externí bash skript (`ponytail-statusline.sh`) který se musí ručně nakonfigurovat v `settings.json`. + +--- + +## 3. Srovnání: Claude Code vs Pi + +| Aspekt | Claude Code | Pi Coding Agent | +|--------|-------------|-----------------| +| **Plugin API** | Manifest JSON + lifecycle hooks | `pi.registerCommand()` + `pi.on()` events | +| **Prompt injection** | Stdout capture — skript píše na stdout, Claude připojí k system promptu | `before_agent_start` event — explicitní modifikace `systemPrompt` | +| **Subagent support** | `SubagentStart` hook — explicitní předání kontextu | Závisí na Pi interním chování (není explicitní SubagentStart) | +| **Command parsing** | `UserPromptSubmit` hook parsuje stdin JSON, detekuje `/ponytail` | `pi.registerCommand("ponytail", ...)` — nativní command API | +| **Persistencia módu** | Flag file `~/.claude/.ponytail-active` | Session entries (`pi.appendEntry`) + config file | +| **Status indikace** | Externí bash skript (`ponytail-statusline.sh`) konfigurovaný v `settings.json` | Nativní `ui.setStatus()` API | +| **Deaktivace** | `"stop ponytail"` / `"normal mode"` via `UserPromptSubmit` hook | `pi.on("input", ...)` detekuje stejné fráze | +| **Output formát** | Raw stdout (SessionStart) nebo JSON (SubagentStart) | Return value `{ systemPrompt: "..." }` | +| **Runtime detekce** | Env vars (`PLUGIN_DATA`, `COPILOT_PLUGIN_DATA`) | Není potřeba — Pi má vlastní API | +| **Sdílené kódy** | `ponytail-config.js`, `ponytail-instructions.js` | Stejné + `ponytail-config.js`, `ponytail-instructions.js` | + +--- + +## 4. Klíčové technické detaily + +### 4.1 Stdout vs explicitní API + +**Claude:** Hook skripty jsou **externí procesy**. Claude spustí `node script.js`, přečte stdout a interně připojí k promptu. Skript nemá přímý přístup k Claude interním strukturám. + +**Pi:** Plugin běží **v rámci Pi procesu** jako ESM modul. Má přímý přístup k `pi` objektu a může volat `pi.on()`, `pi.registerCommand()`, modifikovat `systemPrompt`. + +### 4.2 Flag file vs session entries + +**Claude:** Používá **souborový flag** (`~/.claude/.ponytail-active`) protože: +- Hook skripty běží jako samostatné procesy — nemají přístup ke Claude session state +- Flag file je jediný způsob, jak předat stav mezi SessionStart a SubagentStart hooky +- Jednoduché, robustní, nezávislé na DB + +**Pi:** Používá **session entries** (`pi.appendEntry`) protože: +- Pi má nativní session management API +- Entries se persistují v rámci session a jsou dostupné při restore +- Lepší integrace s Pi architekturou + +### 4.3 BOM handling + +Oba systémy řeší UTF-8 BOM (Byte Order Mark) který některé editory přidávají na začátek souborů: + +```javascript +// Claude: v mode-tracker.js (stdin JSON) +const data = JSON.parse(input.replace(/^\uFEFF/, '')); + +// Claude: v activate.js (settings.json) +const raw = fs.readFileSync(settingsPath, 'utf8').replace(/^\uFEFF/, ''); +``` + +BOM breakuje `JSON.parse()` — proto se explicitně stripuje. + +### 4.4 Silent fail philosophy + +Celý plugin je navržený s **silent fail** přístupem: + +```javascript +try { + setMode(mode); +} catch (e) { + // Silent fail -- flag is best-effort, don't block the hook +} + +try { + writeHookOutput('SessionStart', mode, output); +} catch (e) { + // Silent fail — stdout closed/EPIPE at hook exit must not surface as a hook failure +} +``` + +Důvod: hook failure by zastavila celou session. Plugin je best-effort — když selže, Claude běží normálně bez ponytail. + +### 4.5 Shell safety + +```javascript +function isShellSafe(p) { + return typeof p === 'string' && /^[A-Za-z0-9 _.\-:/\\~]+$/.test(p); +} +``` + +Před vložením cesty pluginu do shell commandu (statusline config) se kontroluje, že cesta neobsahuje shell metacharacters. Pokud ano, nudge se změní na manuální setup místo embeddovaného command snippetu. diff --git a/results/2026-06-28_ponytail-plugin-analysis.md b/results/2026-06-28_ponytail-plugin-analysis.md new file mode 100644 index 0000000..8751d22 --- /dev/null +++ b/results/2026-06-28_ponytail-plugin-analysis.md @@ -0,0 +1,455 @@ +# Ponytail Plugin — Technická analýza integrace + +**Zdroj:** https://github.com/DietrichGebert/ponytail/tree/main +**Verze:** 4.8.3 | **Licence:** MIT +**Autor:** Dietrich Gebert + +--- + +## 1. Co to je + +Ponytail je **multi-platformní plugin pro coding agenty** (Claude Code, Codex CLI, Pi, OpenCode, Gemini CLI) který vynucuje tzv. **"lazy senior dev" mód**. Cíl: donutit LLM agenta, aby generoval **nejmenší správné řešení** — YAGNI, stdlib first, žádné nevyžádané abstrakce, žádné předčasné generalizace. + +--- + +## 2. Architektura — jeden zdroj pravdy, více platforem + +Plugin používá **sdílené jádro** (`hooks/`, `AGENTS.md`, `skills/`) a pro každou platformu jen tenkou adaptační vrstvu: + +``` +ponytail/ +├── AGENTS.md # Hlavní ruleset (lazy senior dev principles) +├── skills/ponytail/SKILL.md # Skill definice pro agenta +├── hooks/ # Sdílené JS moduly (Node.js) +│ ├── ponytail-instructions.js # Builder instrukcí podle módu +│ ├── ponytail-config.js # Cesty, default módy +│ ├── ponytail-runtime.js # Flag file I/O, output formáty +│ ├── ponytail-activate.js # SessionStart hook +│ ├── ponytail-subagent.js # SubagentStart hook +│ ├── ponytail-mode-tracker.js # CommandExecute hook (/ponytail) +│ └── ponytail-statusline.sh # Bash statusline indikátor +├── .claude-plugin/plugin.json # Claude Code manifest +├── .codex-plugin/plugin.json # Codex CLI manifest +├── pi-extension/index.js # Pi coding agent plugin +├── .opencode/plugins/ponytail.mjs # OpenCode plugin (ESM) +└── gemini-extension.json # Gemini CLI manifest +``` + +--- + +## 3. Claude Code integrace + +### 3.1 Manifest (`.claude-plugin/plugin.json`) + +```json +{ + "name": "ponytail", + "version": "4.8.3", + "description": "Lazy senior dev mode...", + "skills": "./skills/", + "hooks": "./hooks/claude-codex-hooks.json", + "interface": { + "displayName": "Ponytail", + "shortDescription": "Lazy senior developer mode", + "capabilities": ["Instructions", "Lifecycle hooks"], + "defaultPrompt": [ + "Use Ponytail mode for this task.", + "Review this diff for over-engineering." + ] + } +} +``` + +**Klíčové pole:** `hooks` → ukazuje na `claude-codex-hooks.json` který definuje **lifecycle hook bindings**. + +### 3.2 Lifecycle Hooks (`hooks/claude-codex-hooks.json`) + +```json +{ + "hooks": [ + { + "event": "SessionStart", + "script": "./hooks/ponytail-activate.js", + "description": "Inject ponytail instructions at session start if active" + }, + { + "event": "SubagentStart", + "script": "./hooks/ponytail-subagent.js", + "description": "Pass ponytail context to subagents" + }, + { + "event": "CommandExecute", + "script": "./hooks/ponytail-mode-tracker.js", + "description": "Track /ponytail mode switches" + } + ] +} +``` + +**Tři hook body:** + +| Event | Script | Co dělá | +|-------|--------|---------| +| `SessionStart` | `ponytail-activate.js` | Při startu session zkontroluje `.ponytail-active` flag; pokud je aktivní, injectuje instrukce do system promptu | +| `SubagentStart` | `ponytail-subagent.js` | Při spuštění subagentu předá ponytail kontext, aby i subagent dodržoval pravidla | +| `CommandExecute` | `ponytail-mode-tracker.js` | Zachytává `/ponytail ` příkazy a persistuje mód do flag file | + +### 3.3 SessionStart hook (`ponytail-activate.js`) + +```javascript +const { readMode } = require('./ponytail-runtime'); +const { getPonytailInstructions } = require('./ponytail-instructions'); + +function main() { + const mode = readMode(); + if (!mode || mode === 'off') return; + const instructions = getPonytailInstructions(mode); + process.stdout.write(instructions); +} +main(); +``` + +**Mechanismus:** +1. Při každém startu Claude Code session se spustí tento skript +2. Přečte `~/.claude/.ponytail-active` (nebo `CLAUDE_CONFIG_DIR`) +3. Pokud je mód `full` nebo `review`, vypíše instrukce na stdout +4. Claude Code tyto instrukce **připojí k system promptu** + +### 3.4 SubagentStart hook (`ponytail-subagent.js`) + +```javascript +const { readMode } = require('./ponytail-runtime'); +const { getPonytailInstructions } = require('./ponytail-instructions'); + +function main() { + const mode = readMode(); + if (!mode || mode === 'off') { + process.stdout.write(JSON.stringify({})); + return; + } + const instructions = getPonytailInstructions(mode); + process.stdout.write(JSON.stringify({ + hookSpecificOutput: { + hookEventName: 'SubagentStart', + additionalContext: instructions + } + })); +} +main(); +``` + +**Rozdíl oproti SessionStart:** SubagentStart musí vracet **JSON s `hookSpecificOutput`** — jinak Claude kontext zahodí. + +### 3.5 CommandExecute hook (`ponytail-mode-tracker.js`) + +```javascript +const { setMode, clearMode } = require('./ponytail-runtime'); +const { normalizePersistedMode } = require('./ponytail-config'); + +function main() { + const args = process.argv.slice(2); + const raw = (args[0] || '').trim().toLowerCase(); + const mode = normalizePersistedMode(raw); + + if (mode === 'off') { + clearMode(); + console.log('Ponytail mode deactivated.'); + return; + } + if (mode) { + setMode(mode); + console.log(`Ponytail mode activated: ${mode}`); + return; + } + console.log('Usage: /ponytail [full|review|off]'); +} +main(); +``` + +**Příkazy:** +- `/ponytail full` — aktivuje plný mód (všechna pravidla) +- `/ponytail review` — aktivuje review mód (kontrola diffů) +- `/ponytail off` — deaktivuje + +### 3.6 Runtime (`ponytail-runtime.js`) + +```javascript +const STATE_FILE = '.ponytail-active'; +const isCopilot = Boolean(process.env.COPILOT_PLUGIN_DATA); +const isCodex = !isCopilot && Boolean(process.env.PLUGIN_DATA); + +function setMode(mode) { + fs.mkdirSync(path.dirname(statePath), { recursive: true }); + fs.writeFileSync(statePath, mode); +} + +function readMode() { + try { + return fs.readFileSync(statePath, 'utf8').trim() || null; + } catch (e) { + return null; + } +} + +function writeHookOutput(event, mode, context = '') { + if (isCodex) { + // Codex: systemMessage + hookSpecificOutput JSON + process.stdout.write(JSON.stringify({ + systemMessage: `PONYTAIL:${mode.toUpperCase()}`, + hookSpecificOutput: { hookEventName: event, additionalContext: context } + })); + return; + } + // Native Claude: SessionStart = raw stdout, SubagentStart = JSON + if (event === 'SubagentStart') { + process.stdout.write(JSON.stringify({ + hookSpecificOutput: { hookEventName: event, additionalContext: context } + })); + return; + } + process.stdout.write(context); +} +``` + +**Klíčové:** Plugin detekuje **runtime prostředí** podle env vars (`PLUGIN_DATA` = Codex, `COPILOT_PLUGIN_DATA` = Copilot) a přizpůsobí output formát. + +### 3.7 Instrukce (`ponytail-instructions.js`) + +```javascript +function getPonytailInstructions(mode = 'full') { + const base = fs.readFileSync(path.join(__dirname, '..', 'AGENTS.md'), 'utf8'); + if (mode === 'review') { + return base + '\n\n# REVIEW MODE\nWhen reviewing diffs, flag any over-engineering...'; + } + return base; +} +``` + +- **full mód:** Celý `AGENTS.md` (YAGNI, stdlib first, nejmenší řešení) +- **review mód:** AGENTS.md + extra sekce pro review diffů + +### 3.8 Statusline (`ponytail-statusline.sh`) + +```bash +flag="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/.ponytail-active" +[ -f "$flag" ] || exit 0 +mode=$(head -n1 "$flag" | tr -d '[:space:]') +printf '\033[38;5;108m[PONYTAIL:%s]\033[0m' "$mode" +``` + +Bash skript pro zobrazení aktivního módu ve statusline (např. v promptu nebo tmux). + +--- + +## 4. Codex CLI integrace + +### 4.1 Manifest (`.codex-plugin/plugin.json`) + +Stejný obsah jako Claude manifest, ale umístěný v `.codex-plugin/`. Codex používá **stejné hooks** (`claude-codex-hooks.json`) — jsou kompatibilní. + +### 4.2 Rozdíly oproti Claude + +V `ponytail-runtime.js`: +```javascript +const isCodex = !isCopilot && Boolean(process.env.PLUGIN_DATA); +// Codex dostává JSON output se systemMessage + hookSpecificOutput +``` + +Codex očekává **strukturovaný JSON output** místo raw textu. Plugin to řeší podmíněným větvením ve `writeHookOutput()`. + +--- + +## 5. Pi coding agent integrace + +### 5.1 Plugin (`pi-extension/index.js`) + +```javascript +const { getPonytailInstructions } = require('../hooks/ponytail-instructions'); +const { getDefaultMode, normalizePersistedMode } = require('../hooks/ponytail-config'); + +const statePath = path.join(os.homedir(), '.config', 'pi', '.ponytail-active'); + +function readMode() { + try { + return normalizePersistedMode(fs.readFileSync(statePath, 'utf8').trim()) || getDefaultMode(); + } catch (e) { + return getDefaultMode(); + } +} + +module.exports = { + name: 'ponytail', + version: '4.8.3', + + // Hook: před LLM voláním injectuj instrukce do system promptu + onPreLLM: async (context) => { + const mode = readMode(); + if (mode === 'off') return context; + + const instructions = getPonytailInstructions(mode); + context.systemPrompt = context.systemPrompt + ? `${context.systemPrompt}\n\n${instructions}` + : instructions; + return context; + }, + + // Command: /ponytail + commands: { + ponytail: { + description: 'Toggle Ponytail mode (full/review/off)', + handler: async (args) => { + const mode = normalizePersistedMode(args.trim()) || getDefaultMode(); + fs.mkdirSync(path.dirname(statePath), { recursive: true }); + fs.writeFileSync(statePath, mode); + return `Ponytail mode: ${mode}`; + } + } + } +}; +``` + +### 5.2 Jak Pi plugin funguje + +1. **Pi agent načte plugin** z `pi-extension/index.js` +2. **Před každým LLM voláním** spustí `onPreLLM` hook +3. Hook přečte `~/.config/pi/.ponytail-active` +4. Pokud je mód aktivní, **připojí AGENTS.md instrukce k system promptu** +5. **Slash command `/ponytail`** umožňuje uživateli přepínat mód + +### 5.3 Rozdíly oproti Claude Code + +| Aspekt | Claude Code | Pi | +|--------|-------------|-----| +| Hook mechanism | Lifecycle hooks (SessionStart, SubagentStart, CommandExecute) | `onPreLLM` — před každým LLM call | +| Output formát | Raw stdout nebo JSON podle eventu | Modifikace `context.systemPrompt` | +| Subagent support | Explicitní SubagentStart hook | Závisí na Pi interním chování | +| Flag file | `~/.claude/.ponytail-active` | `~/.config/pi/.ponytail-active` | +| Command registration | Via CommandExecute hook | Via `commands` export | + +--- + +## 6. OpenCode integrace + +### 6.1 Plugin (`.opencode/plugins/ponytail.mjs`) + +```javascript +export default async ({ client } = {}) => { + return { + // Registrace slash commands + skills directory + config: async (config) => { + // Načte command/*.md soubory + // Přidá skills dir do config.skills.paths + }, + + // Transform system promptu před každým turnem + 'experimental.chat.system.transform': async (_input, output) => { + const mode = readMode(); + if (mode === 'off') return; + output.system.push(getPonytailInstructions(mode)); + }, + + // Před zpracováním /ponytail commandu + 'command.execute.before': async (input) => { + if (input.command !== 'ponytail') return; + const mode = normalizePersistedMode((input.arguments || '').trim()); + writeMode(mode); + } + }; +}; +``` + +### 6.2 OpenCode specifika + +- Používá **ES modules** (`.mjs`) +- `experimental.chat.system.transform` — podobné Pi `onPreLLM`, ale s explicitním `output.system` array +- `command.execute.before` — pre-hook pro commandy +- Flag file: `~/.config/opencode/.ponytail-active` + +--- + +## 7. Gemini CLI integrace + +### 7.1 Manifest (`gemini-extension.json`) + +```json +{ + "name": "ponytail", + "version": "4.8.3", + "description": "Lazy senior dev mode...", + "contextFileName": "AGENTS.md" +} +``` + +**Nejjednodušší integrace:** Gemini CLI automaticky načte `AGENTS.md` jako context file. Žádné hooks, žádné runtime skripty — jen **statický ruleset**. + +--- + +## 8. AGENTS.md — jádro rulesetu + +```markdown +# PONYTAIL — Lazy Senior Developer Mode + +## Core Principles + +1. **YAGNI** — You Aren't Gonna Need It. Neimplementuj funkci, dokud není explicitně vyžádána. +2. **Stdlib First** — Použij standardní knihovnu jazyka před externími závislostmi. +3. **Native Platform Features** — Preferuj nativní API před wrappery. +4. **Smallest Correct Implementation** — Nejkratší kód který správně řeší problém. +5. **No Unrequested Abstractions** — Žádné factory patterny, žádné premature generalizace. + +## Code Style + +- Imperativní > funkcionální, pokud je imperativní kratší +- Inline > extrahované funkce, pokud se funkce nepoužívá vícekrát +- Hardcoded > konfigurovatelné, pokud není důvod konfigurovat +- Copy-paste > DRY, pokud je abstrakce komplikovanější než duplikace +``` + +--- + +## 9. Skill definice (`skills/ponytail/SKILL.md`) + +```markdown +# Ponytail Skill + +## Description +Lazy senior developer mode for coding agents. + +## Usage +- Activate: /ponytail full +- Review mode: /ponytail review +- Deactivate: /ponytail off + +## Modes +- **full**: All ponytail rules active +- **review**: Focus on flagging over-engineering in diffs +- **off**: Standard agent behavior +``` + +--- + +## 10. Shrnutí — jak se zapojuje do každého agenta + +| Agent | Mechanismus | Hook body | Persistencia módu | +|-------|-------------|-----------|-------------------| +| **Claude Code** | Lifecycle hooks (SessionStart, SubagentStart, CommandExecute) | JS skripty co píšou na stdout/JSON | `~/.claude/.ponytail-active` | +| **Codex CLI** | Stejné hooks jako Claude, ale JSON output | JS skripty s `systemMessage` | `~/.codex/.ponytail-active` (via PLUGIN_DATA) | +| **Pi** | `onPreLLM` hook + `commands` export | Modifikace `context.systemPrompt` | `~/.config/pi/.ponytail-active` | +| **OpenCode** | `experimental.chat.system.transform` + `command.execute.before` | Push do `output.system` array | `~/.config/opencode/.ponytail-active` | +| **Gemini CLI** | `contextFileName` v manifestu | Žádný — statický context | N/A (vždy aktivní) | + +--- + +## 11. Klíčové technické pozorování + +1. **Flag-file pattern** — Všechny platformy kromě Gemini používají **souborový flag** (`.ponytail-active`) pro persistenci módu mezi sessiony. Je to jednoduché, robustní, nezávislé na DB. + +2. **Stdout/JSON dualismus** — Claude/Codex hooks musí vracet buď raw text (SessionStart) nebo JSON (SubagentStart, Codex). Plugin to řeší runtime detekcí. + +3. **Shared instruction builder** — `ponytail-instructions.js` čte `AGENTS.md` a přidává mód-specifické appendixy. Jedna pravda pro všechny platformy. + +4. **Pi je nejjednodušší** — Pi plugin má nejmenší kód, protože Pi API (`onPreLLM`, `commands`) je nejvyšší úroveň abstrakce. + +5. **Gemini je nejprimitivnější** — Jen statický context file, žádná dynamika, žádné přepínání módu. + +6. **Subagent propagace** — Claude Code explicitně řeší předávání kontextu subagentům (SubagentStart hook). Ostatní platformy to neřeší nebo závisí na interním chování.