---
type: howto
titre: "đ§° REGISTRE DES SOLUTIONS IMPORTANTES â OLYMPUS"
dossier: 03_HOWTO
etage: long
source: true # SOURCE (autorite) - les projections (Chroma/graphe) en derivent
maj: 2026-05-20
hash: bec4bf7f
---
ï»ż---
type: REGISTRE_SOLUTIONS
project: OLYMPUS (transverse)
status: active
created: 2026-04-28 (mission BUILD-002 nocturne)
diĂĄtaxis: how-to
audience: [tous_les_chats_OLYMPUS, futurs_connecteurs_MCP]
auto_indexed_by_codex: true
mis_a_jour_par: chaque chat qui découvre une solution générale réutilisable
---
# đ§° REGISTRE DES SOLUTIONS IMPORTANTES â OLYMPUS
> **But** : capitaliser les **dĂ©couvertes techniques importantes** trouvĂ©es par chaque chat OLYMPUS, pour qu'**aucun autre chat ne refasse 2Ă le mĂȘme problĂšme**.
> **Pattern industrie** : Postmortem Knowledge Base · Stack Overflow interne · Architecture Decision Record (ADR léger).
> **Auto-indexé par CODEX** : ce fichier remonte dans CODEX.html à chaque régénération (toutes les 30 min).
---
## đ Index par catĂ©gorie
- [đ€ CLI / Subprocess / Sandboxing](#cli) · S001
- [đŹ Chat-Ă -chat / Communication entre Claude](#chat-claude) · S008
- [đ WebSocket / FastAPI / NEXUS](#websocket) · S002
- [đŒïž Capture Ă©cran / OmniParser / Vision](#vision) · S003
- [đȘ Always-on-top / Window management / FenĂȘtres natives](#window) · S004 S009
- [đŠ Imports / Path Windows](#path) · S005
- [đĄïž Mona Lisa / Snapshots / Backups](#mona-lisa) · S006 S007
- [đ Claude embedded per-app / Vision "Claude partout"](#claude-partout) · S010
---
## đ€ CLI / Subprocess / Sandboxing
### S001 â Claude CLI (`claude.exe`) inaccessible depuis Python (sandboxing Windows AppData)
> â
**RĂSOLU 2026-05-01** â option (2) appliquĂ©e : `claude.exe` copiĂ© vers `C:\OLYMPUS\AGORA\bin\claude.exe` (243 MB). Python peut l'exĂ©cuter sans souci. `_find_claude_exe()` mis Ă jour pour prĂ©fĂ©rer ce path. Endpoint `/api/canvas/chat` testĂ© : **200 OK · 5.68s · vraie rĂ©ponse Claude · accents+emojis UTF-8 propres**. Garder cette section pour mĂ©moire historique du diagnostic.
**Découvert par** : BUILD-002 GRAPHISME-001 (Claude Code, 2026-04-28 nuit)
**SymptĂŽme** :
- `claude.exe` localisé via `find` Bash à `C:\Users\vivie\AppData\Roaming\Claude\claude-code\2.1.119\claude.exe` (254 MB)
- `os.path.exists()` Python renvoie `False` sur ce path
- `subprocess.run([...])` échoue : `FileNotFoundError [WinError 2]`
- `cmd /c "...claude.exe" --version` â "chemin d'accĂšs introuvable"
- Pourtant `dir` cmd voit bien le dossier (vide en apparence)
- Et Bash msys peut **lister + exécuter** le binaire normalement
**Cause probable** :
- Dossier `AppData\Roaming\Claude\` est dans une zone de **redirection / virtualisation** Windows (style App-V ou OneDrive Backup cloud-only)
- Python 3.14 standalone n'a pas les permissions ou ne suit pas le redirect
- Bash msys utilise un autre layer de filesystem qui contourne
**Solutions de contournement (ordre de préférence)** :
1. **Clé API Anthropic** + appel HTTP direct depuis NEXUS :
```python
import anthropic
client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])
resp = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
messages=[{"role": "user", "content": "..."}],
)
```
Avantages : pay-as-you-go, pas d'abonnement, latence API directe (~3-5s)
Inconvénients : besoin d'une clé (gratuit à créer sur console.anthropic.com)
2. **Copier `claude.exe` vers un path accessible** :
```bash
cp "C:/Users/vivie/AppData/Roaming/Claude/claude-code/2.1.119/claude.exe" "C:/OLYMPUS/AGORA/bin/claude.exe"
```
Avantages : utilise le binaire local existant
Inconvénients : 254 MB · à refaire à chaque update Claude Code · besoin shell qui peut copier (Bash OK)
3. **Ollama local** :
- Lancer Ollama (`ollama serve`) sur port 11434
- HTTP POST Ă `http://localhost:11434/api/chat`
Avantages : 100% offline, gratuit
Inconvénients : qualité moindre que Claude · GPU recommandé · install lourde
4. **Chercher dans `WindowsApps` aliases** :
```bash
ls C:/Users/vivie/AppData/Local/Microsoft/WindowsApps/ | grep -i claude
```
Si Claude Code a publié un AppExecutionAlias, il sera là .
**Pattern source** : Diagnostic Bash vs Python sandboxing â tester multiple subprocess methods.
**Fichiers concernés** :
- `C:\OLYMPUS\01_SERVEUR\NEXUS\AXIOM\api_canvas.py` â fonction `_find_claude_exe()` retourne None gracefully
- Endpoint `/api/canvas/chat` â fallback EN CHANTIER message clair
---
## đŹ Chat-Ă -chat / Communication entre Claude (subprocess)
### S008 â Pattern "1 chat Claude par tuile" via subprocess `claude.exe --print` (stateless)
**DĂ©couvert par** : BUILD-002 / GRAPHISME-001 (2026-04-30 nuit Vivien â POC `/api/canvas/chat` validĂ©)
**Idée centrale** : `claude.exe --print ""` est un **moteur de réponse stateless universel**. Chaque appel = un Claude frais, sans historique, sans contexte global. Parfait pour intégrer un mini-chat dans **n'importe quel composant UI** sans gérer de session.
**Cas d'usage parfait** : "**1 chat par tuile**" dans le Cockpit. Chaque tuile (MĂ©tĂ©o, Agenda, Code, Notes, MailâŠ) reçoit son propre input texte. L'utilisateur tape sa demande â POST â `claude.exe --print` â reply. La tuile peut ĂȘtre **modifiĂ©e Ă la volĂ©e** sans contexte global ni state partagĂ©.
**Architecture flow** :
```
[Tuile UI] âPOST {"text":"âŠ"}ââ /api//chat (NEXUS Python)
â
subprocess.run([claude.exe, "--print", text])
â
stdout = réponse markdown
â
[Tuile UI] ââ{ok:true, reply:"âŠ", duration_sec:N}ââ JSON
```
**Code de référence (validé en prod)** : `C:\OLYMPUS\01_SERVEUR\NEXUS\AXIOM\api_canvas.py` lignes 308-398 (endpoint `/api/canvas/chat`).
**Recette minimale (Ă copier dans `api_.py`)** :
```python
import glob, subprocess, asyncio, os, time
from fastapi import APIRouter, Body
from typing import Dict, Any
router = APIRouter()
def _find_claude_exe() -> str | None:
"""Auto-détecte le path le plus récent de claude.exe."""
candidates = glob.glob(r"C:/Users/vivie/AppData/Roaming/Claude/claude-code/*/claude.exe")
return sorted(candidates)[-1] if candidates else None
@router.post("/api//chat", tags=[""])
async def chat(payload: Dict[str, Any] = Body(...)) -> Dict[str, Any]:
text = (payload.get("text") or "").strip()
if not text:
return {"ok": False, "error": "text vide"}
claude_exe = _find_claude_exe()
if not claude_exe:
# Fallback EN CHANTIER (cf. S001 : sandboxing AppData Windows)
return {
"ok": True,
"reply": f"đŹ EN CHANTIER : claude.exe inaccessible. Tu m'as dit : « {text} ».",
"status": "EN_CHANTIER",
}
# CRITIQUE : fix UTF-8 Windows cp1252 â UTF-8 (sinon "Ă©" devient "Ă©", "đŻ" devient "Ă°ĆžĆœÂŻ")
env = os.environ.copy()
env['PYTHONIOENCODING'] = 'utf-8'
env['PYTHONUTF8'] = '1'
env['LANG'] = 'fr_FR.UTF-8'
started = time.time()
try:
# Subprocess en thread pour ne pas bloquer asyncio
loop = asyncio.get_event_loop()
result = await loop.run_in_executor(
None,
lambda: subprocess.run(
[claude_exe, "--print", text],
capture_output=True,
text=False, # bytes â on dĂ©code nous-mĂȘme (plus fiable)
timeout=90,
env=env,
),
)
# Décodage robuste multi-encoding
def decode_safe(data: bytes) -> str:
if not data: return ""
for enc in ('utf-8', 'cp65001', 'cp1252', 'latin-1'):
try: return data.decode(enc)
except UnicodeDecodeError: continue
return data.decode('utf-8', errors='replace')
stdout = decode_safe(result.stdout)
stderr = decode_safe(result.stderr)
duration = round(time.time() - started, 2)
if result.returncode != 0:
return {"ok": False, "error": f"exit {result.returncode}", "stderr": stderr[:500], "duration_sec": duration}
return {"ok": True, "reply": stdout.strip(), "duration_sec": duration}
except subprocess.TimeoutExpired:
return {"ok": False, "error": "timeout 90s"}
except Exception as e:
return {"ok": False, "error": f"{type(e).__name__}: {e}"}
```
**Personnalisation par tuile (prompt-prefix injecté)** :
```python
TUILE_PROMPTS = {
"weather": "Tu es météo expert. Réponds en 3 lignes max. Question: ",
"calendar": "Tu es agenda Vivien. Réponds en bullets courts. Question: ",
"code": "Tu es Senior Dev OLYMPUS. Code uniquement, pas d'explication verbeuse. Question: ",
"notes": "Tu es preneur de notes. Reformule + tag #thĂšme. Note: ",
"mail": "Tu es rédacteur email FR pro. Style clair. Brief: ",
}
prefix = TUILE_PROMPTS.get(tuile_id, "")
result = subprocess.run([claude_exe, "--print", prefix + text], ...)
```
**Quand utiliser ce pattern** :
- â
"1 chat par tuile" Cockpit (cas d'usage Vivien validé 30/04/2026)
- â
Tùches stateless courtes : "résume ce texte", "génÚre un email", "explique ce code"
- â
Pas besoin d'historique de conversation (chaque message = fresh start)
- â
Personnalisation par contexte : préfixe rÎle + question utilisateur
**Quand NE PAS utiliser** :
- â Conversations longues avec mĂ©moire (utiliser API Anthropic + state local)
- â Streaming de rĂ©ponse (subprocess `--print` bloque jusqu'Ă fin)
- â Beaucoup de tuiles en parallĂšle (chaque subprocess Claude = 200-500 MB RAM)
**Fix UTF-8 critique (bug observé Vivien 01/05/2026)** :
- Sans `PYTHONUTF8=1` + `decode_safe()` : `Ă©` devient `Ă©`, `đŻ` devient `Ă°ĆžĆœÂŻ`
- Cause : Windows console default cp1252, Claude CLI sort UTF-8, Python dĂ©code en cp1252 â cassĂ©
- Fix testĂ© en prod sur `/api/canvas/chat` â
**Pré-requis (S001)** :
- `claude.exe` doit ĂȘtre trouvable. Si `_find_claude_exe()` renvoie `None` â fallback `EN_CHANTIER` (sans crasher l'UI).
- Solutions de déblocage en cas de sandboxing AppData : voir S001 (clé API · copier exe vers AGORA/bin · Ollama local).
**Fichiers concernés** :
- `C:\OLYMPUS\01_SERVEUR\NEXUS\AXIOM\api_canvas.py` (ref impl ligne 308-398)
- à répliquer dans `api_.py` pour chaque tuile Cockpit qui veut son chat
**Pattern industrie** :
- **Process-as-API** (Unix philosophy 1978)
- **Stateless microservices** (Heroku 12-factor 2011)
- **Sidecar pattern** (Kubernetes 2015) â chaque tuile = son propre worker
---
### đŻ Usages connus + futurs (formalisation 01/05/2026 â demande Vivien)
**Vivien (01/05 13h00)** : *"j'espÚre que la méthode pour activer des chat claude est marquée bien quelque part dans nexus il faudra se servir de ça plus tard pour la sphÚre goku et pour que je parle avec Claude en direct projet jarvis , formalise tout ça"*
| Module | Statut | Endpoint | Notes |
|---|---|---|---|
| **Canvas Vivant** | â
en prod | `/api/canvas/chat` | Référence implémentation, validé chat LGS |
| **Cockpit V3 â chat-par-tuile** | â
livré 01/05 | `/api/canvas/chat` (réutilisé) | 4 tuiles GridStack démo cliquables |
| **SphĂšre Goku** | đ
à venir | `/api/sphere/chat` (à créer) | Vivien parlera à Goku via subprocess Claude |
| **Jarvis (parler en direct)** | đ
à venir | `/api/jarvis/chat` ou WS streaming | Conversation continue, peut nécessiter API directe pour streaming |
| **Modules autres** | đ
possible | `/api//chat` | Chaque module peut avoir son chat dédié si besoin |
### â ïž Limites du pattern (Ă connaĂźtre pour les usages futurs)
| Limite | Détail | Mitigation |
|---|---|---|
| **Internet requis** | `claude.exe` appelle api.anthropic.com en HTTPS | Fallback Ollama local pour offline |
| **Latence 5-15s** | subprocess + appel API Anthropic | UI loader obligatoire ("Claude rĂ©flĂ©chitâŠ") |
| **RAM ~300 MB par appel** | Chaque `claude.exe` lance un process | Limiter Ă 2-3 chats parallĂšles (queue/throttle) |
| **Stateless** | Aucun contexte préservé entre appels | OK pour modifications ponctuelles, pas pour conversation longue |
| **Pas de streaming** | `--print` bloque jusqu'Ă la rĂ©ponse complĂšte | Pour streaming â API Anthropic directe (clĂ© requise) |
### đ ïž Pattern fallback offline (Ă coder pour Jarvis si besoin)
```python
async def chat_with_fallback(text: str) -> dict:
# 1. Essayer claude.exe (cloud)
try:
result = await run_claude_subprocess(text, timeout=15)
if result["ok"]: return result
except Exception: pass
# 2. Fallback Ollama local (offline)
try:
async with aiohttp.ClientSession() as session:
async with session.post(
"http://localhost:11434/api/chat",
json={"model": "llama3.1", "messages": [{"role": "user", "content": text}]}
) as r:
data = await r.json()
return {"ok": True, "reply": data["message"]["content"], "source": "ollama_local"}
except Exception:
return {"ok": False, "error": "Aucun LLM disponible (cloud + offline KO)"}
```
### đ Recette pour intĂ©grer le chat-par-X dans un nouveau module
1. **Frontend** : copier le pattern HTML+CSS+JS de cockpit_v3 (`tile-chat-wrap`, `tile-chat-input`, `tile-chat-reply`)
2. **Backend** : soit réutiliser `/api/canvas/chat` (chat générique), soit créer un endpoint dédié `/api//chat` avec préfixe rÎle (cf. `TUILE_PROMPTS` dans BRIEF)
3. **Fallback** : si offline critique â ajouter le pattern Ollama ci-dessus
4. **Test E2E** : `curl -X POST .../chat -d '{"text":"hello"}'` doit répondre en <30s
---
## đ WebSocket / FastAPI / NEXUS
### S002 â Ajouter un WebSocket Ă NEXUS sans casser l'existant
**Découvert par** : BUILD-002 (Canvas Vivant V2)
**Recette** :
1. Créer `AXIOM/api_.py` avec :
```python
from fastapi import APIRouter, WebSocket, WebSocketDisconnect
router = APIRouter()
_active_connections: list[WebSocket] = []
@router.websocket("/ws/")
async def my_ws(ws: WebSocket):
await ws.accept()
_active_connections.append(ws)
try:
while True:
data = await ws.receive_text()
# ...
except WebSocketDisconnect:
pass
finally:
if ws in _active_connections:
_active_connections.remove(ws)
```
2. Ajouter dans `server.py` la liste `_TRANCHE_IMPORTS` :
```python
("api_", ""),
```
3. Le router est inclus automatiquement par la boucle `app.include_router()`.
4. Pour broadcast : `for ws in list(_active_connections): await ws.send_text(json.dumps(payload))`
**Fichiers concernés** : `api_canvas.py` (référence implémentation)
---
## đŒïž Capture Ă©cran / OmniParser / Vision
### S003 â Capture Ă©cran light pour transmission WebSocket (PIL JPEG base64)
**Découvert par** : BUILD-002 (Canvas Vivant V3)
**Code minimal** :
```python
from PIL import ImageGrab
import io, base64
def capture_light(scale: float = 0.4, quality: int = 60) -> str:
img = ImageGrab.grab(all_screens=False)
new_size = (int(img.width * scale), int(img.height * scale))
img = img.resize(new_size).convert("RGB")
buf = io.BytesIO()
img.save(buf, format="JPEG", quality=quality, optimize=True)
return f"data:image/jpeg;base64,{base64.b64encode(buf.getvalue()).decode('ascii')}"
```
**Stats** : un Ă©cran 4K (3840Ă2160) â JPEG 1536Ă864 qualitĂ© 60 â ~110 KB base64 â transmission WS instantanĂ©e.
**Pourquoi pas PNG** : trop lourd pour un screenshot photo (~1-2 MB). JPEG 60 = compromis taille/qualité parfait pour preview.
---
## đȘ Always-on-top / Window management
### S009 â FenĂȘtre native Windows avec site web embarquĂ© (PyQt6 + WebEngine)
**DĂ©couvert par** : BUILD-002 (Vivien 2026-05-01) â V20.1 livrĂ©e et validĂ©e visuellement.
**Vision** : afficher **claude.ai** (ou n'importe quel site web) dans une vraie fenĂȘtre Windows native, sans Chrome, toujours visible (always-on-top), Ă droite de l'Ă©cran. Permet d'avoir "Claude in Chrome" sans dĂ©pendre du navigateur.
**Stack** :
- **PyQt6** + **PyQt6-WebEngine** (open source, gratuit, MIT)
- Install : `pip install PyQt6 PyQt6-WebEngine` (~150 MB une fois pour toutes)
- Profil persistant `QWebEngineProfile` â garde cookies/session entre lancements (pas besoin de re-login)
**Recette minimale (50 lignes)** :
```python
from PyQt6.QtCore import Qt, QUrl
from PyQt6.QtWidgets import QApplication, QMainWindow
from PyQt6.QtWebEngineWidgets import QWebEngineView
from PyQt6.QtWebEngineCore import QWebEngineProfile, QWebEnginePage
from pathlib import Path
import sys
class NativeWebWindow(QMainWindow):
def __init__(self, url: str, width: int = 420):
super().__init__()
self.setWindowFlags(
Qt.WindowType.FramelessWindowHint
| Qt.WindowType.WindowStaysOnTopHint
| Qt.WindowType.Tool # pas dans la barre des tĂąches
)
# Position : ancrée à droite
screen = QApplication.primaryScreen().availableGeometry()
self.setGeometry(screen.right() - width - 10, 30,
width, screen.height() - 90)
# Profil persistant (cookies/session)
storage = Path.home() / ".my_native_app_session"
storage.mkdir(exist_ok=True)
profile = QWebEngineProfile("MyApp", self)
profile.setPersistentStoragePath(str(storage))
profile.setPersistentCookiesPolicy(
QWebEngineProfile.PersistentCookiesPolicy.ForcePersistentCookies
)
page = QWebEnginePage(profile, self)
view = QWebEngineView()
view.setPage(page)
view.load(QUrl(url))
self.setCentralWidget(view)
if __name__ == "__main__":
app = QApplication(sys.argv)
win = NativeWebWindow("https://claude.ai")
win.show()
sys.exit(app.exec())
```
**Bascule auto online/offline** :
```python
import socket
def is_online(host="claude.ai", port=443, timeout=2):
try:
with socket.create_connection((host, port), timeout=timeout):
return True
except (socket.timeout, OSError):
return False
# Charger URL en ligne ou fallback local
url = "https://claude.ai" if is_online() else "http://127.0.0.1:10001/.../offline.html"
```
**Use-cases** :
- â
Embed claude.ai dans fenĂȘtre OLYMPUS Canvas Vivant (V20.1 livrĂ©e)
- â
Embed n'importe quel SaaS (Notion, Google Calendar, Linear) en sidebar fixe
- â
Multi-instances : 1 fenĂȘtre par projet/contexte (chacune avec son storage path)
**Avantages vs Chrome `--app=`** :
- â
Pas de barre d'adresse Chrome au-dessus
- â
Vraie fenĂȘtre Windows native (pas un onglet Chrome dĂ©guisĂ©)
- â
ZĂ©ro dĂ©pendance Chrome (marche mĂȘme si Chrome fermĂ©)
- â
Always-on-top fiable (Chrome a des bugs avec z-order)
- â
Multi-fenĂȘtres faciles (1 par projet)
**Limites** :
- â ïž Install Python + PyQt6 (~150 MB)
- â ïž PremiĂšre fois : login manuel sur le site
- â ïž Pas d'extensions Chrome (Claude in Chrome, Grammarly, etc.)
**Fichier de référence livré** : `C:\OLYMPUS\AGORA\scripts\canvas_vivant_v20_1_claude_ai.py` (188 lignes)
- Frameless · always-on-top · profil persistant · bascule online/offline auto · drag clic-droit · Echap pour fermer
**Pattern industrie** :
- **Electron** (GitHub 2013) â mĂȘme idĂ©e mais lourd (~150 MB par app)
- **Tauri** (Rust 2020) â lighter
- **PyQt WebEngine** (Riverbank 2020+) â choix Python, dĂ©jĂ installĂ© sur ce poste
---
### S004 â Always-on-top fenĂȘtre Chrome via AutoHotkey v2
**Découvert par** : BUILD-002 (Canvas Vivant V6)
**Recette** :
1. Lancer Chrome en mode app : `chrome --app=URL --window-size=W,H --window-position=X,Y --user-data-dir=PATH`
2. Script `.ahk` (AutoHotkey v2) qui dĂ©tecte le titre de la fenĂȘtre et force always-on-top :
```autohotkey
#SingleInstance Force
SetTitleMatchMode 2
Loop 120 {
if WinExist("Canvas Vivant") {
WinSetAlwaysOnTop true, "Canvas Vivant"
break
}
Sleep 500
}
F12:: WinSetAlwaysOnTop -1, "Canvas Vivant" ; toggle
```
3. Hotkeys globaux F10 (reposition) F11 (cacher/afficher) F12 (toggle on/off)
**Fichier rĂ©fĂ©rence** : `C:\Users\vivie\Desktop\OLYMPUS\đȘ Canvas Vivant ALWAYS-ON-TOP.ahk`
---
## đȘ Hooks clavier · Auto-hide · System prompts par app (CLAUDE_PARTOUT)
### S011 â Hook clavier global Windows (Ctrl+Shift+C) via pynput
**Découvert par** : BUILD-002 mission nocturne 2026-05-02 (Phase 4 bis)
**Source d'inspiration** : Espanso (Rust) â `espanso-detect/src/win32/` clonĂ© dans `AGORA/REFERENCES_OPENSOURCE/espanso/`.
**ProblĂšme** : LGS doit pouvoir ĂȘtre appelĂ© d'un coup depuis n'importe quelle app (style Cmd+Space Mac · Win+G Windows · Cmd+K Cursor).
**Solution Python** : lib `pynput` (déjà installée 1.8.1, pas besoin admin).
```python
from pynput import keyboard
HOTKEY_COMBO = {keyboard.Key.ctrl, keyboard.Key.shift, keyboard.KeyCode.from_char('c')}
current_pressed = set()
def on_press(key):
# Normalise ctrl_l/ctrl_r â ctrl
if key in (keyboard.Key.ctrl_l, keyboard.Key.ctrl_r):
key = keyboard.Key.ctrl
elif key in (keyboard.Key.shift_l, keyboard.Key.shift_r):
key = keyboard.Key.shift
current_pressed.add(key)
if HOTKEY_COMBO.issubset(current_pressed):
handle_hotkey() # â spawn Claude pour app active
def on_release(key):
# idem normalisation puis discard
...
with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:
listener.join()
```
**Fichier de référence livré** : `C:\OLYMPUS\AGORA\scripts\lgs_hotkey_listener.py`
**Pattern industrie** :
- Espanso (Rust + C++) â hook bas niveau via `RegisterHotKey` Win32 API
- pynput (Python) â abstraction multi-OS (Windows, macOS, Linux)
---
### S012 â Auto-hide overlay PyQt6 (slide-in/slide-out animĂ©)
**Découvert par** : BUILD-002 mission nocturne 2026-05-02 (LGS-V10.2)
**Source d'inspiration** : Pattern barre des tĂąches Windows + PowerToys overlay.
**IdĂ©e** : la fenĂȘtre LGS se rĂ©tracte hors Ă©cran (sliver 3px Ă droite) quand pas utilisĂ©e. Souris approche bord droit â slide-in. Souris quitte â 1.5s grace period â slide-out.
```python
from PyQt6.QtCore import QTimer, QPropertyAnimation, QEasingCurve, QPoint
from PyQt6.QtGui import QCursor
# Setup dans __init__ (aprĂšs tray)
self._slide_anim = None
self._hide_timer = QTimer(self)
self._hide_timer.setSingleShot(True)
self._hide_timer.timeout.connect(self._slide_out)
self._mouse_check_timer = QTimer(self)
self._mouse_check_timer.timeout.connect(self._check_mouse_position)
self._mouse_check_timer.start(200) # poll 5Ă/sec
def _check_mouse_position(self):
if self.is_pinned:
return # mode épinglé : pas d'auto-hide
screen = QApplication.primaryScreen().availableGeometry()
cursor_pos = QCursor.pos()
is_near_right = cursor_pos.x() >= screen.right() - 15
is_over_window = self.geometry().contains(cursor_pos)
if is_near_right or is_over_window:
self._hide_timer.stop()
self._slide_in()
else:
if not self._hide_timer.isActive():
self._hide_timer.start(1500)
def _slide_in(self):
target_x = screen.right() - WIDTH - MARGIN_RIGHT
self._animate_to(target_x, 200, QEasingCurve.Type.OutCubic)
def _slide_out(self):
target_x = screen.right() - 3 # sliver 3px
self._animate_to(target_x, 300, QEasingCurve.Type.InCubic)
def _animate_to(self, target_x, duration, easing):
if self._slide_anim and self._slide_anim.state() == QPropertyAnimation.State.Running:
self._slide_anim.stop()
anim = QPropertyAnimation(self, b"pos")
anim.setDuration(duration)
anim.setStartValue(QPoint(self.x(), self.y()))
anim.setEndValue(QPoint(target_x, self.y()))
anim.setEasingCurve(easing)
anim.start()
self._slide_anim = anim
```
**Toggle pin** : si bouton đ cliquĂ© (pinned=True), `_check_mouse_position` retourne tĂŽt â reste visible.
**Fichier de référence livré** : `C:\OLYMPUS\AGORA\scripts\canvas_vivant_v20_1_claude_ai.py` lignes 369-440.
---
### S013 â Subprocess Claude.exe avec timeout + windowsHide (pattern Cline)
**Découvert par** : BUILD-002 mission nocturne 2026-05-02
**Source d'inspiration** : Cline (`src/core/hooks/HookProcess.ts`) â pattern spawn avec timeout + cleanup.
**Idée** : améliorer S008 avec :
- `creationflags=0x08000000` (CREATE_NO_WINDOW Windows) â pas de console qui flash
- `timeout` propre avec auto-kill
- Decode robuste UTF-8 (déjà en S008)
```python
import subprocess, os
def spawn_claude_app(claude_exe: str, system_prompt: str, app_name: str, timeout_s: int = 90):
"""Spawn 1 Claude dédié à app_name. Retourne reply markdown."""
full_prompt = f"Tu es Claude-{app_name}, l'assistant Vivien. {system_prompt}"
env = os.environ.copy()
env['PYTHONIOENCODING'] = 'utf-8'
env['PYTHONUTF8'] = '1'
env['LANG'] = 'fr_FR.UTF-8'
creation_flags = 0x08000000 if os.name == 'nt' else 0 # CREATE_NO_WINDOW
try:
result = subprocess.run(
[claude_exe, "--print", full_prompt],
capture_output=True, text=False,
timeout=timeout_s, env=env,
creationflags=creation_flags,
)
# decode_safe (cf. S008)
for enc in ('utf-8', 'cp65001', 'cp1252', 'latin-1'):
try:
return result.stdout.decode(enc).strip()
except UnicodeDecodeError:
continue
return result.stdout.decode('utf-8', errors='replace').strip()
except subprocess.TimeoutExpired:
return f"[TIMEOUT aprĂšs {timeout_s}s]"
```
**Différences vs S008** :
- â
`creation_flags=CREATE_NO_WINDOW` â pas de console qui flash sur Windows
- â
Pattern réutilisable pour spawn par app (Claude-Excel, Claude-Word, etc.)
---
### S014 â System prompts par app (config JSON par process .exe)
**Découvert par** : BUILD-002 mission nocturne 2026-05-02 (Phase 4 ter)
**Source d'inspiration** : Open Interpreter (`computer_use/loop.py`) â SYSTEM_PROMPT dynamique par OS.
**Idée** : 1 fichier JSON, key = nom du process Windows (`EXCEL.EXE`, `WINWORD.EXE`, ...), value = system prompt + métadonnées.
**Fichier livré** : `C:\OLYMPUS\MNEMOSYNE\07_PREFS\lgs_system_prompts.json` (12 apps + default).
**Format** :
```json
{
"EXCEL.EXE": {
"name": "Claude-Excel",
"icon": "đ",
"system_prompt": "Tu es Claude-Excel, l'assistant dédié de Vivien...",
"deep_integration": false
}
}
```
**Usage** :
```python
import json
from pathlib import Path
PROMPTS_PATH = Path("C:/OLYMPUS/MNEMOSYNE/07_PREFS/lgs_system_prompts.json")
with PROMPTS_PATH.open(encoding="utf-8") as f:
PROMPTS = json.load(f)
def get_prompt_for_app(process_name: str) -> dict:
"""Retourne config + prompt pour un .exe. Fallback sur _default."""
return PROMPTS.get(process_name, PROMPTS["_default"])
# Usage dans listener Ctrl+Shift+C
info = get_active_window() # â {"process": "EXCEL.EXE", ...}
config = get_prompt_for_app(info["process"])
prompt_full = config["system_prompt"] + " " + user_question
reply = spawn_claude_app(claude_exe, prompt_full, config["name"])
```
**Apps couvertes (V1)** : Excel, Word, PowerPoint, Outlook, VS Code, Chrome, Notepad, Notepad++, Steam, Spotify, Discord, Explorer, default.
**Pattern industrie** :
- Open Interpreter â SYSTEM_PROMPT par OS
- MS Copilot Office â system prompt par app Microsoft
- Apple Intelligence â Writing Tools per-context
---
## đ MĂ©ta-pattern â extraction de ressources opensource
### S015 â Catalogue exhaustif d'inspirations open source (BIBLE pattern)
**Découvert par** : BUILD-002 mission étendue 2026-05-02 (matin)
**Demande Vivien** : *"il faut rĂ©cupĂ©rer toutes les idĂ©es d'ergonomie mĂȘme si on peut pas copier... extraire toutes les donnĂ©es pertinentes... compiler dans un fichier pour ne pas avoir Ă tous relire"*.
**Pattern** : extraction massive + compilation indexée pour ne plus avoir à relire les sources.
**Recette appliquée** (9 phases, ~90 min) :
1. **Phase A** : `git clone --depth=1` de 19 repos prioritaires en parallĂšle (~2.6 GB)
2. **Phase B** : `WebFetch` des pages closed-source (Cursor, Apple Intelligence, MS Copilot)
3. **Phase C** : `grep #[0-9A-Fa-f]{6}` pour extraire palettes couleurs des CSS
4. **Phase D** : `grep -E "Ctrl+|Cmd+|hotkey"` pour extraire raccourcis clavier
5. **Phase E** : `grep "animation:|transition:|@keyframes"` pour extraire animations CSS
6. **Phase F** : `WebSearch` pour articles + vidéos sur closed-source
7. **Phase G** : `curl --max-filesize 5MB` pour télécharger screenshots/GIFs README
8. **Phase H** : compilation `BIBLE_RESSOURCES_OLYMPUS.md` (~50 KB exhaustif)
9. **Phase I** : création `INDEX_RESSOURCES.md` (~5 KB navigation rapide)
**Livrables** :
- `MNEMOSYNE/03_HOWTO/BIBLE_RESSOURCES_OLYMPUS.md` (catalogue 22 sources)
- `MNEMOSYNE/03_HOWTO/INDEX_RESSOURCES.md` (navigation rapide)
- `MNEMOSYNE/03_HOWTO/PATTERNS_EXTRAITS_OPENSOURCE.md` (patterns code détaillés)
- `AGORA/REFERENCES_OPENSOURCE/` (sources locales 2.6 GB · 19 repos)
- `AGORA/REFERENCES_OPENSOURCE/_ASSETS/` (50 fichiers visuels · 8.2 MB)
- `AGORA/REFERENCES_OPENSOURCE/_INDEX/` (données brutes pour audit)
**Pattern industrie** :
- **Swipe file** (collections d'inspirations centralisĂ©es) â copywriters / designers
- **Library catalog** (Dewey, LoC) â index lĂ©ger â emplacement source
- **Wikipedia model** â un index pointe vers les articles, pas un rĂ©sumĂ© global
**Leçons apprises** :
- â MĂ©ga-fichier compilĂ© = vite obsolĂšte + paraphrase la source
- â
Index léger + sources locales = toujours à jour + précis
- â
Lazy evaluation : on n'extrait QUE ce qu'on utilise réellement aprÚs
- â
Le REGISTRE grandit avec ce qui est RĂELLEMENT utilisĂ© (S016+ futurs)
**Quand l'appliquer** :
- Avant un gros projet : 1Ă pour cataloguer le state-of-the-art
- Ă mi-projet : enrichir si nouveaux outils sortent
**Limites** :
- Closed-source â seulement web research indirect
- VidĂ©os lourdes (>5 MB) â URLs only
- Demande discipline : à chaque feature codée, ajouter S0NN au REGISTRE
---
## đŠ Imports / Path Windows
### S005 â Path Windows en Python : forward slash OK, sauf pour AppData\Roaming protĂ©gĂ©
**Découvert par** : BUILD-002
**RĂšgle** :
- `os.path.exists("C:/Users/...")` marche partout
- SAUF dans certains dossiers protégés (`AppData\Roaming\Claude\` cf. S001)
- En cas de doute : tester `os.access(p, os.R_OK)` avant `subprocess.run([...])`
---
## đĄïž Mona Lisa / Snapshots / Backups
### S007 â Rotation snapshots (Mona Lisa) â Ă©viter accumulation infinie
**Découvert par** : BUILD-002 (Vivien 2026-04-28 : "il faudra penser à nettoyer si y a trop de snapshots")
**ProblĂšme** : `CERBER/sauvegardes/zips/` accumule des ZIP Ă chaque modif majeure (avant V2 backend, avant V5, avant V12, etc). En 1 nuit on en a fait 4. Ă ce rythme, 50 ZIP Ă 5 MB = 250 MB inutile.
**Solution** : script `C:\OLYMPUS\AGORA\scripts\rotate_snapshots.py` :
- Garde les **N derniers** snapshots par préfixe (NEXUS_, NEXUS_DEV_, CERBER_)
- Déplace les anciens vers `MNEMOSYNE/09_ARCHIVES/snapshots_anciens/` (Mona Lisa : jamais `rm`)
- Default N = 5
- Mode `--dry-run` pour preview
**Usage** :
```bash
python C:/OLYMPUS/AGORA/scripts/rotate_snapshots.py --keep 5 --dry-run # preview
python C:/OLYMPUS/AGORA/scripts/rotate_snapshots.py --keep 5 # exécute
```
**Cron-isable** : à brancher en tùche planifiée Windows (1à par semaine).
---
### S006 â Snapshot ZIP rapide de NEXUS sans dĂ©pendance externe
**Découvert par** : BUILD-002
**Recette** (fonctionne sans `zip` cmd Windows) :
```bash
python -c "import shutil; shutil.make_archive('C:/OLYMPUS/.../snapshot_NAME', 'zip', 'C:/OLYMPUS/01_SERVEUR', 'NEXUS')"
```
**Avant TOUT chantier sur NEXUS / NEXUS_DEV : snapshot d'abord** (R4 + Doctrine Mona Lisa).
---
## đ Comment ajouter une solution Ă ce registre
1. Trouve la catégorie qui correspond (ou crée-en une nouvelle)
2. Numéro `S00N` séquentiel
3. Sections obligatoires : **Découvert par** · **SymptÎme** · **Cause** · **Solution(s)** · **Fichiers concernés**
4. Régénérer CODEX (`POST /api/codex/regenerate`) pour que les autres chats voient
---
## đ€ Roadmap : convertir ce registre en MCP connecteur
> Quand le registre fera 50+ entrées, créer un connecteur MCP `olympus-registre` exposant :
> - `solution_search(query: str)` â cherche dans le registre
> - `solution_add(category, title, content)` â ajoute une nouvelle entrĂ©e
> - Permet Ă Claude de **demander des solutions** sans relire tout le registre.
---
## đ Claude embedded per-app / Vision "Claude partout"
### S010 â Claude-partout (Niveau 1 universel · Niveau 2 par app)
> **Note numĂ©rotation** : S009 Ă©tait initialement rĂ©servĂ© Ă cette entrĂ©e par Vivien, mais S009 est dĂ©jĂ occupĂ© par "FenĂȘtre native PyQt6 + WebEngine" (catĂ©gorie đȘ). Donc S010 ici, et S009 reste sa rĂ©servation historique pour la brique UI sous-jacente.
**DĂ©couvert par** : claude-code (2026-05-01) â formalisation aprĂšs pose du LGS le matin mĂȘme par Vivien.
**Vision en 1 phrase** : gĂ©nĂ©raliser le pattern **S008** (1 chat Claude par tuile UI) Ă TOUTES les applications du PC. Quand Vivien ouvre Excel â Claude-Excel apparaĂźt. Notepad â Claude-Notepad. Steam â Claude-Steam. Comme Claude in Chrome, mais pour TOUTES les apps.
**Idée centrale** :
- 1 chose = 1 Claude dédié, lancé à la demande, mort aprÚs usage
- LGS (Le Grand Superviseur) orchestre, les Claudes-app exécutent
- 2 niveaux : **N1 universel** (overlay générique sans code par app) · **N2 deep** (intégration profonde par app prioritaire avec lib Python dédiée)
**Pattern source** : S008 (chat-par-tuile via subprocess `claude.exe --print`) + S009 (fenĂȘtre PyQt6 frameless ancrĂ©e).
**Document maĂźtre** : `MNEMOSYNE/06_PROJETS/CLAUDE_PARTOUT/CONCEPTION.md`
- 11 sections (pitch · principe · archi · niveaux · état de l'art · existant · archi cible ASCII · roadmap · ergo à voler · codes à récupérer · questions ouvertes · sources)
- 30+ projets open source référencés (Open Interpreter, AnythingLLM, Continue.dev, AutoGen, Cline, Open WebUI, LobeChat, Jan, Flow Launcher, etc.)
- Ergonomie volée à : Apple Intelligence Sequoia, Microsoft Copilot, Cursor, Raycast, Notion AI, Claude in Chrome, ChatGPT Desktop
**Stack envisagée (Phase 2)** :
```python
# Pseudo-code endpoint NEXUS /api/claude_partout/spawn
@router.post("/api/claude_partout/spawn")
async def spawn_claude_for_app():
aw = await get("/api/god/active_window") # détecte app devant
app_name = aw["exe_name"] # ex: "EXCEL.EXE"
sysprompt = f"Tu es l'assistant {app_name} de Vivien. Réponds court, utile."
# Spawn fenĂȘtre PyQt6 ancrĂ©e Ă droite (rĂ©utilise S009)
# Wrapping subprocess claude.exe --print (réutilise S008)
# Kill auto Ă fermeture app
```
**Roadmap** :
- Phase 1 (â
acquis) : LGS posé, S008 prod, S009 native window, GOD MODE, Chrome MCP pairé
- Phase 2 (đŻ next) : N1 universel + raccourci global `Ctrl+Shift+C` + endpoint `/api/claude_partout/spawn`
- Phase 3 (đ” backlog) : N2 par app prioritaire (Excel, Outlook, Steam, Chrome, Krita, Notepad++)
- Phase 4 (đ futur) : autonomie nocturne + API Anthropic + smart reply universel
**Parent direct** : S008 (réf code `api_canvas.py` lignes 308-398).
**Brique UI** : S009 (PyQt6 frameless, profil persistant cookies).
**Bras exécuteur** : Open Interpreter (déjà installé, couplé codex).
**Filet offline** : ollama port 11434 + llama3.1 8B (déjà installé).
**Pattern industrie** :
- **Sidecar pattern** (Kubernetes 2015) â chaque Claude-app = sidecar de l'app cible
- **Per-app assistant** (Microsoft Copilot Office 2023) â modĂšle UX directement applicable
- **MCP fan-out** (Anthropic 2024) â chaque app future devient un MCP connector branchĂ© sur LGS
**Fichiers concernés** :
- `MNEMOSYNE/06_PROJETS/CLAUDE_PARTOUT/CONCEPTION.md` (ce projet, 535 lignes)
- `MNEMOSYNE/06_PROJETS/LE_GRAND_SUPERVISEUR/CONCEPTION.md` (parent direct)
- `MNEMOSYNE/06_PROJETS/OLYMPUS/GOD_OLYMPUS.html` §INDEX MODIFICATIONS (entrée 01/05/2026)
- Ă venir : `01_SERVEUR/NEXUS/AXIOM/api_claude_partout.py` (endpoint Phase 2)
---
## S011 â Windows-MCP clonĂ© et opĂ©rationnel (BĂTA)
> â
**AJOUTĂ 2026-05-01** par INSTALL-001
**DĂ©couverte** : le repo CursorTouch/Windows-MCP (2M+ users Claude Desktop) est un MCP server stdio prĂȘt Ă l'emploi via uvx windows-mcp (Python 3.13+).
**Path local** : `C:\OLYMPUS\AGORA\connecteurs_externes\windows-mcp\`
**Activation** : ajouter au `%APPDATA%\Claude\claude_desktop_config.json` :
```json
"windows-mcp": {
"command": "uvx",
"args": ["windows-mcp"]
}
```
**Coexistence avec mode-dieu-ultime** : OUI â pas de conflit, les 2 MCPs servent en stdio sur des canaux diffĂ©rents.
**Statut REST cÎté NEXUS** : `GET /api/win/windows_mcp/status` (ne pilote pas Windows-MCP, juste vérifie le clone).
**Pourquoi DOUBLE l'avoir + mode-dieu-ultime** : Windows-MCP est maintenu par une commu active (CursorTouch), update réguliÚrement. mode-dieu-ultime reste notre **super connecteur OLYMPUS-spécifique** avec le wiring Vivien (NEXUS · ports · checks). Les 2 cohabitent.
---
## S012 â OmniParser V2 clonĂ©, weights Ă tĂ©lĂ©charger
> â
**AJOUTĂ 2026-05-01** par INSTALL-001
**DĂ©couverte** : Microsoft Research a publiĂ© OmniParser V2 (HF model `microsoft/OmniParser-v2.0`) â vision GUI parser YOLO-icon-detect + Florence2-caption. Permet Ă un LLM de comprendre n'importe quel screenshot d'Ă©cran sans accessibility tree.
**Path local** : `C:\OLYMPUS\AGORA\connecteurs_externes\omniparser\`
**Endpoints NEXUS exposés** :
- `GET /api/vision/omniparser/status` â diagnostic complet (weights · torch · cuda)
- `POST /api/vision/parse` `{image_path, box_threshold, iou_threshold}` â appel parser
**Reste Ă faire pour activation complĂšte** :
1. `pip install torch torchvision easyocr ultralytics transformers accelerate paddleocr paddlepaddle` (~3 GB)
2. Télécharger les weights : `huggingface-cli download microsoft/OmniParser-v2.0 --local-dir omniparser/weights/` (~500 MB)
3. Tester : `POST /api/vision/parse {"image_path": "F:\\screenshot.png"}`
**Cas d'usage attendus** :
- Comprendre un écran de jeu (qui ne donne pas l'accessibility tree)
- Parser le DOM d'une app native sans pywin32 (Krita, photoshop, etc.)
- Cliquer sur un bouton "vu" plutÎt que "selecté par UIA"
**Limitations connues** :
- Nécessite GPU NVIDIA pour latence acceptable (CPU = ~30s par image)
- ModÚles téléchargés depuis HF (refus abonnements respecté : HF est gratuit)
---
## S078 â Super god mod ultime v2.1 enrichi (ENHANCE-GOD-001)
> â
**AJOUTĂ 2026-05-01** par claude-ENHANCE-GOD-001
**Contexte** : `mode_dieu_ultime_v2.py` était en PREVIEW (75 wrappers HTTP NEXUS). Il manquait les imports Python natifs des 12 `tools_extras` + les bridges pour Whisper/Piper TTS/OpenWakeWord/ffmpeg/ImageMagick/Open Interpreter. Mission : finaliser le super-connecteur.
**Path canonique** : `C:\OLYMPUS\AGORA\connecteurs_maison\mode-dieu-ultime\mode_dieu_ultime_v2.py`
**Doctrine appliquée** :
- Mona Lisa : v1 `mode_dieu_ultime.py` **inchangé**, PREVIEW snapshoté dans `99_BACKUP/mode_dieu_ultime_v2.py.PRE-ENHANCE-GOD-001.bak`
- Append-only : v2 réutilise l'instance FastMCP de v1 + ajoute des `@mcp.tool()` au-dessus
- Imports `tools_extras` enveloppĂ©s dans `_try_import_extra()` â libs optionnelles, jamais de crash si une lib manque
**Architecture finale (v2.1)** :
```
v1 mode_dieu_ultime â 26 tools natifs (pyautogui/uia/win32/psutil)
+ tools_extras (12 modules) â ~25 wrappers MCP (audio/notif/clipboard/webcam/
scanner/BT/USB/monitors/window-mgmt/hash/power/sniff)
+ bridges NEXUS â ~85 wrappers (winmcp UIA · vision OmniParser ·
voix Whisper/Piper/Wakeword · ffmpeg · ImageMagick ·
Open Interpreter · WiFi · BT/USB · Box · LAN ·
speedtest · eBay · LBC · OBS · YouTube · gaming ·
foobar2000 · VLC · Tailscale · Discord)
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
Total â 140 tools dans une seule instance MCP
```
**Tools de diagnostic ajoutés** :
- `extras_status()` â Ă©tat import des 12 modules `tools_extras` (ok/error dĂ©tail)
- `v2_inventory()` â inventaire complet par catĂ©gorie + skipped + extras_status_summary
**Skipped (out-of-scope ENHANCE-GOD)** :
- Spotify (Vivien préfÚre foobar2000)
- LangChain / LlamaIndex / agents / web automation (pas du contrĂŽle PC, restent dans NEXUS REST)
**Comment activer** : modifier `claude_desktop_config.json` :
```json
"mode-dieu-ultime-v2": {
"command": "F:\\\\OLYMPUS\\\\DEPENDENCIES\\\\python\\\\python.exe",
"args": ["F:\\\\OLYMPUS\\\\AGORA\\\\connecteurs_maison\\\\mode-dieu-ultime\\\\mode_dieu_ultime_v2.py"]
}
```
â ïž Si v2 lancĂ© : dĂ©sactiver v1 (sinon collision FastMCP mĂȘme nom).
**Limitation observée** : test `python compile` formel non effectué dans cette session car le mount sandbox Linux gardait un cache figé du fichier d'origine. Validation visuelle réalisée (130 occurrences `"""` paires + structure cohérente). Vivien doit relancer Claude Desktop pour test live.
**Rollback** : copier `99_BACKUP/mode_dieu_ultime_v2.py.PRE-ENHANCE-GOD-001.bak` â `mode_dieu_ultime_v2.py`.
---
---
_Migre F:->C: par docs_portability_light 2026-05-10_