# Rapport d'Architecture : CharmBracelet Crush

> Analyse complète de l'application [charmbracelet/crush](https://github.com/charmbracelet/crush.git) — comment elle communique avec les IA, gère les outils, optimise les tokens et les performances.

---

## Table des matières

1. [Architecture globale](#1-architecture-globale)
2. [Ce qui est envoyé à l'IA](#2-ce-qui-est-envoyé-à-lia)
3. [Système de prompts](#3-système-de-prompts)
4. [Système de résumé / compaction](#4-système-de-résumé--compaction)
5. [Fonctionnement des outils (Tools)](#5-fonctionnement-des-outils-tools)
6. [Optimisation de la consommation de tokens](#6-optimisation-de-la-consommation-de-tokens)
7. [Optimisations de performance](#7-optimisations-de-performance)
8. [Système de permissions](#8-système-de-permissions)
9. [Providers et modèles](#9-providers-et-modèles)
10. [Système de skills](#10-système-de-skills)
11. [Intégration LSP](#11-intégration-lsp)
12. [Intégration MCP](#12-intégration-mcp)
13. [Structure de la base de données](#13-structure-de-la-base-de-données)
14. [Fichiers clés à explorer](#14-fichiers-clés-à-explorer)
15. [Leçons pour notre application](#15-leçons-pour-notre-application)

---

## 1. Architecture globale

```
┌──────────────┐     ┌──────────────┐     ┌──────────────────┐
│   TUI / CLI  │────▶│   Backend    │────▶│  Coordinator     │
│  (UI chat)   │     │ (workspace)  │     │  (orchestrateur)  │
└──────────────┘     └──────────────┘     └────────┬─────────┘
                                                   │
                                          ┌────────▼─────────┐
                                          │  SessionAgent    │
                                          │  (moteur IA)     │
                                          └────────┬─────────┘
                                                   │
                              ┌────────────────────┼────────────────────┐
                              │                    │                    │
                     ┌────────▼──────┐   ┌────────▼──────┐   ┌────────▼──────┐
                     │  fantasy      │   │  SQLite DB    │   │  Tools        │
                     │  (LLM client) │   │  (messages)   │   │  (22+ outils) │
                     └───────────────┘   └───────────────┘   └───────────────┘
```

**Flow de données complet :**

```
User Prompt
  → Backend.SendMessage()
  → Coordinator.Run()
      → UpdateModels() (recharge models + tools)
      → mergeCallOptions() (merge JSON des options provider)
      → SessionAgent.Run()
          → preparePrompt() (construit l'historique, filtre les orphelins)
          → fantasy.Agent.Stream() avec :
              - System prompt = coder.md.tpl + instructions MCP
              - System prompt prefix = prefix provider
              - Messages = historique filtré (tronqué au résumé si existe)
              - Files = pièces jointes binaires
              - Tools = ensemble filtré d'outils
              - Provider options = options merged Anthropic/OpenAI/Google/etc.
              - Cache control = ephemeral sur dernier system + 2 derniers messages
          → Auto-summarize si fenêtre de contexte quasi pleine
          → Queue + recurse pour messages en attente
```

### Fichiers clés de l'architecture

| Fichier | Rôle |
|---------|------|
| `internal/agent/agent.go` | Moteur principal — construction des messages, streaming, résumé |
| `internal/agent/coordinator.go` | Orchestration — création agents, assembly tools/models |
| `internal/agent/prompts.go` | Factory de prompts système |
| `internal/agent/templates/coder.md.tpl` | Template du prompt système principal (405 lignes) |
| `internal/backend/backend.go` | Gestion des workspaces |
| `internal/backend/agent.go` | API transport-agnostic vers le coordinator |

---

## 2. Ce qui est envoyé à l'IA

### 2.1 System Prompt

Le system prompt est construit à partir du template `coder.md.tpl` et contient :

```
System Message:
├── <critical_rules>          — 13 règles absolues (read before edit, autonomous, etc.)
├── <communication_style>     — Style de réponse (concis, <4 lignes)
├── <workflow>                — Séquence de travail (search → read → edit → test)
├── <decision_making>         — Décisions autonomes vs. bloquantes
├── <editing_files>           — Règles d'édition (exact match, whitespace)
├── <whitespace_and_exact_matching> — Checklist précision
├── <task_completion>         — Complétion exhaustive des tâches
├── <error_handling>          — Stratégies de récupération d'erreurs
├── <memory_instructions>     — Gestion des fichiers mémoire
├── <code_conventions>        — Conventions de code
├── <testing>                 — Règles de test après changements
├── <tool_usage>              — Utilisation des outils
├── <proactiveness>           — Proactivité vs. intention utilisateur
├── <final_answers>           — Format des réponses finales
├── <env>                     — Variables dynamiques :
│   ├── Working Directory: {{.WorkingDir}}
│   ├── Is Git Repo: {{.IsGitRepo}}
│   ├── Platform: {{.Platform}}
│   ├── Date: {{.Date}}
│   └── Git Status (branch, git status --short | head -20, git log --oneline -n 3)
├── <lsp> (optionnel)         — État des serveurs LSP
├── <available_skills>        — Skills disponibles en XML
├── <skills_usage>            — Instructions d'utilisation des skills
├── <memory>                  — Fichiers de contexte (crush.md, AGENTS.md, etc.)
└── <mcp-instructions>        — Instructions MCP injectées dynamiquement
```

### 2.2 Historique des messages

Chaque appel à l'IA inclut l'historique complet de la session, formaté comme :

```
Messages (array de fantasy.Message):
├── [0] User:   "<system_reminder>This is a reminder that your todo list is currently empty...</system_reminder>"
├── [1] User:   "Premier prompt de l'utilisateur"
├── [2] Assistant: "Réponse IA + tool_calls"
├── [3] Tool:   "Résultat du tool call"
├── [4] Assistant: "Réponse suivante"
├── ...
└── [N] User:   "Nouveau prompt"
```

**Types de contenu dans les messages :**
- `ReasoningContent` — Chaîne de pensée (pour modèles thinking)
- `TextContent` — Texte brut
- `ImageURLContent` — Images (URL ou base64)
- `BinaryContent` — Fichiers binaires
- `ToolCall` — Appel d'outil (ID, nom, input)
- `ToolResult` — Résultat d'outil (text, error, ou media)
- `Finish` — Métadonnées de fin (end_turn, max_tokens, tool_use, canceled, error)

### 2.3 Fichiers joints

Les pièces jointes sont traitées différemment selon leur type :
- **Fichiers texte** : contenus inline dans le prompt utilisateur via `PromptWithTextAttachments()`
- **Fichiers binaires** : envoyés comme `FilePart` séparés
- **Images** : envoyées comme `ImageURLContent` (base64) si le modèle les supporte

### 2.4 Options provider fusionnées

Les options sont fusionnées en 3 couches (le plus profond gagne) :

```
1. catwalk.Model.Options.ProviderOptions  (défauts du catalogue)
2. ProviderConfig.ProviderOptions          (config du provider)
3. SelectedModel.ProviderOptions           (choix utilisateur)
→ JSON merge avec sémantique "deepest wins"
```

Options spécifiques par provider :
- **Anthropic** : thinking, reasoning_effort, beta headers (`interleaved-thinking-2025-05-14`)
- **OpenAI** : responses API, reasoning
- **Google** : thinking_config
- **OpenRouter** : reasoning, suffixe `:exacto`

---

## 3. Système de prompts

### 3.1 Templates embarqués

| Template | But | Taille |
|----------|-----|--------|
| `coder.md.tpl` | Prompt système principal de l'agent coder | ~405 lignes |
| `task.md.tpl` | Prompt système des sous-agents | ~15 lignes |
| `initialize.md.tpl` | Prompt d'initialisation du codebase | Analyse + génération AGENTS.md |
| `summary.md` | Prompt de résumé/compaction | Sections structurées obligatoires |
| `title.md` | Génération de titre de session | ≤50 chars |
| `agent_tool.md` | Instructions du tool `agent` | Sous-agent read-only |
| `agentic_fetch.md` | Instructions du tool `agentic_fetch` | Sous-agent web |
| `agentic_fetch_prompt.md.tpl` | Prompt du sous-agent de fetch | Template dynamique |

### 3.2 Prompt de résumé (`summary.md`)

Ce prompt est crucial — il définit comment la conversation est compactée :

**Sections obligatoires du résumé :**
1. **Current State** — État actuel de la tâche
2. **Files & Changes** — Tous les fichiers modifiés et les changements
3. **Technical Context** — Stack technique, dépendances, patterns
4. **Strategy & Approach** — Stratégie adoptée
5. **Exact Next Steps** — Prochaines étapes exactes

**Philosophie :** *"No limit. Err on the side of too much detail rather than too little."*

### 3.3 Prompt de sous-agent (`task.md.tpl`)

Minimal et ciblé :
```
Rules:
1. Be concise, direct
2. Share file names and paths
3. Use absolute paths only

<env>
Working Directory: {{.WorkingDir}}
Is Git Repo: {{.IsGitRepo}}
Platform: {{.Platform}}
Date: {{.Date}}
</env>
```

### 3.4 Variables dynamiques injectées

Le système injecte des données en temps réel via le template :

```go
type PromptData struct {
    WorkingDir    string
    IsGitRepo     bool
    Platform      string
    Date          string
    GitStatus     string    // branch + git status --short | head -20 + git log --oneline -n 3
    Config        Config
    AvailSkillXML string    // XML des skills disponibles
    ContextFiles  string    // Contenu des fichiers de contexte
}
```

### 3.5 Fichiers de contexte (Memory)

Chemins par défaut explorés et injectés dans `<memory>` :
```
.github/copilot-instructions.md
.cursorrules
.cursor/rules/
CLAUDE.md
CLAUDE.local.md
GEMINI.md
crush.md
CRUSH.md
AGENTS.md
```

---

## 4. Système de résumé / compaction

### 4.1 Déclenchement automatique

Deux conditions de déclenchement vérifiées après **chaque étape** de l'agent :

```
remaining = contextWindow - (completionTokens + promptTokens)

if contextWindow > 200,000:
    threshold = 20,000 tokens    (buffer fixe)
else:
    threshold = 20% × contextWindow  (ratio)

if remaining ≤ threshold AND !disableAutoSummarize:
    → DÉCLENCHE LE RÉSUMÉ

if contextWindow == 0 (modèle inconnu/local):
    → PAS de résumé auto (évite la troncature aveugle)
```

**Constantes clés :**
- `largeContextWindowThreshold = 200,000`
- `largeContextWindowBuffer = 20,000`
- `smallContextWindowRatio = 0.2`

### 4.2 Processus de résumé

```
┌─────────────────────────────────────────────┐
│  1. Récupérer tous les messages de session   │
│  2. Convertir en fantasy.Message[]           │
│  3. Créer message assistant                  │
│     avec IsSummaryMessage: true              │
│  4. Streamer le résumé via summary.md prompt │
│     + todo list courante                     │
│  5. Update session:                          │
│     - SummaryMessageID = summary.ID          │
│     - CompletionTokens = résumé output       │
│     - PromptTokens = 0                       │
└─────────────────────────────────────────────┘
```

### 4.3 Comment la compaction fonctionne au chargement

```go
func getSessionMessages(session):
    msgs = loadAllMessages(sessionID)

    if session.SummaryMessageID != "":
        summaryIndex = findIndex(msgs, summaryMessageID)
        msgs = msgs[summaryIndex:]        // TRONQUE tout avant le résumé
        msgs[0].Role = "user"             // Change rôle assistant → user

    return msgs
```

**Résultat :** Seuls le résumé + les messages après le résumé sont envoyés à l'IA. Tout l'historique précédent est éliminé du contexte.

### 4.4 Détection de boucle (Loop Detection)

Mécanisme secondaire de déclenchement du résumé :

```
- Fenêtre glissante de 10 dernières étapes
- Signature = SHA-256(ToolName + \x00 + Input + \x00 + Output + \x00)
- Si une signature apparaît > 5 fois dans la fenêtre → BOUCLE DÉTECTÉE
- Déclenche le résumé + arrêt de l'agent
```

### 4.5 Reprise après interruption

Si le résumé est déclenché pendant des appels d'outils en cours :
```
Re-queue le prompt original avec :
"The previous session was interrupted because it got too long,
the initial user request was: <original_prompt>"
```

---

## 5. Fonctionnement des outils (Tools)

### 5.1 Liste complète des outils (22+)

| Outil | Type | Séquentiel/Parallèle | But |
|-------|------|---------------------|-----|
| `bash` | Core | Séquentiel | Exécution de commandes shell |
| `edit` | Core | Séquentiel | Remplacement find-and-replace dans un fichier |
| `multiedit` | Core | Séquentiel | Multiples remplacements séquentiels |
| `write` | Core | Séquentiel | Création/écrasement de fichier |
| `view` | Core | Séquentiel | Lecture de fichier avec line numbers |
| `ls` | Core | Séquentiel | Arborescence de répertoire |
| `glob` | Core | Séquentiel | Recherche de fichiers par pattern |
| `grep` | Core | Séquentiel | Recherche dans le contenu de fichiers |
| `fetch` | Core | Parallèle | Fetch URL brut (text/markdown/html) |
| `agentic_fetch` | Core | Parallèle | Fetch IA avec extraction/résumé |
| `sourcegraph` | Core | Parallèle | Recherche de code sur GitHub |
| `download` | Core | Parallèle | Téléchargement de fichier |
| `agent` | Agent | Parallèle | Sous-agent de recherche/analyse |
| `todos` | Core | Séquentiel | Gestion de la todo list |
| `crush_info` | Core | Séquentiel | État runtime de Crush |
| `crush_logs` | Core | Séquentiel | Logs internes de Crush |
| `job_output` | Core | Séquentiel | Output de processus background |
| `job_kill` | Core | Séquentiel | Terminaison de processus background |
| `lsp_diagnostics` | LSP | Séquentiel | Diagnostics LSP |
| `lsp_references` | LSP | Séquentiel | Références LSP |
| `lsp_restart` | LSP | Séquentiel | Redémarrage LSP |
| `list_mcp_resources` | MCP | Parallèle | Liste ressources MCP |
| `read_mcp_resource` | MCP | Parallèle | Lecture ressource MCP |
| `mcp_{server}_{tool}` | MCP | Parallèle | Outils dynamiques MCP |

### 5.2 Architecture d'un outil

Chaque outil suit le pattern :

```go
fantasy.NewAgentTool(name, description, params, func(ctx context.Context, params Params) (fantasy.ToolResponse, error) {
    // 1. Validation des paramètres
    // 2. Extraction du contexte (sessionID, messageID, workingDir)
    // 3. Vérification de permission (si mutation)
    // 4. Exécution de la logique
    // 5. Retour de la réponse avec métadonnées
})
```

**Deux constructeurs :**
- `fantasy.NewAgentTool` — Outil séquentiel (bloquant)
- `fantasy.NewParallelAgentTool` — Outil parallèle (peut tourner en //)

### 5.3 Types de réponses

```go
fantasy.NewTextResponse(content)          // Succès texte
fantasy.NewTextErrorResponse(message)     // Erreur (IsError=true)
fantasy.NewImageResponse(data, mimeType)  // Image
fantasy.NewMediaResponse(data, mimeType)  // Autre média
fantasy.WithResponseMetadata(resp, meta)  // Attache métadonnées typées
```

### 5.4 Détails des outils clés

#### `bash` — Exécution de commandes

```
Paramètres: {Description, Command, WorkingDir, RunInBackground, AutoBackgroundAfter}

Sécurité:
├── safeCommands: ls, cat, head, tail, pwd, echo, which, env, git status/diff/log
│   → Pas de permission requise (read-only)
├── Banned: curl, wget, sudo, apt, npm, etc.
│   → Bloqué au niveau shell
├── Background: si RunInBackground=true ou > AutoBackgroundAfter (60s)
│   → Retourne ShellID pour suivi
└── Output: tronqué à 30,000 chars (début + fin + compte lignes tronquées)
```

#### `view` — Lecture de fichiers

```
Paramètres: {FilePath, Offset, Limit}

Comportement:
├── crush: prefix → lecture depuis FS embarqué (skills builtins)
├── Chemins relatifs → résolus via SmartJoin(workingDir, ...)
├── Permission requise si hors workingDir
├── Max fichier: 100KB, défaut 2000 lignes
├── Images JPG/PNG/GIF/WebP → NewImageResponse
├── Ajoute numéros de ligne (padding 6 chars)
├── LSP: ouvre fichier, attend 300ms pour diagnostics
├── Enregistre lecture via filetracker.RecordRead()
└── Suggestions si fichier non trouvé
```

#### `edit` — Édition de fichiers

```
Paramètres: {FilePath, OldString, NewString, ReplaceAll}

3 modes:
├── OldString="" → Créer nouveau fichier
├── NewString="" → Supprimer contenu
└── Les deux → Remplacer contenu

Sécurité:
├── Doit avoir lu le fichier avant (filetracker check)
├── Fichier non modifié depuis la lecture (ModTime check)
├── OldString unique (sauf ReplaceAll=true)
├── Permission "write" avec diff affiché
├── Gestion CRLF automatique
└── LSP notifié après écriture
```

#### `grep` — Recherche de contenu

```
Paramètres: {Pattern, Path, Include, LiteralText}

Optimisations:
├── ripgrep (rg --json) en priorité, fallback Go regex
├── Respecte .gitignore et .crushignore
├── Cache de regex compilés (csync.Map thread-safe)
├── Résultats triés par date de modification (plus récent d'abord)
├── Max 100 résultats
├── Lignes tronquées à 500 chars
└── Timeout configurable
```

### 5.5 Outils spéciaux

#### `agent` — Sous-agent

```
Flow:
1. Valide params.Prompt non vide
2. Crée une session enfant (CreateTaskSession)
3. Lance un SessionAgent séparé (NonInteractive: true)
4. Outils du sous-agent: glob, grep, ls, sourcegraph, view (READ-ONLY)
5. Propage le coût de la session enfant → session parent
```

#### `agentic_fetch` — Fetch intelligent

```
Flow:
1. Mode URL: fetch + convert (HTML→MD)
   → Si contenu > 50KB: sauve en temp file, dit au sous-agent d'utiliser view/grep
2. Mode Search: construit prompt pour web_search + web_fetch
3. Sous-agent avec petit modèle + outils restreints
4. Auto-approve des permissions pour le sous-agent
```

---

## 6. Optimisation de la consommation de tokens

### 6.1 Résumé automatique (Auto-Summarization)

**La plus grande optimisation.** Voir [Section 4](#4-système-de-résumé--compaction).

Quand la fenêtre de contexte approche sa limite, toute la conversation est remplacée par un résumé détaillé. Ce résumé devient le nouveau point de départ.

### 6.2 Anthropic Prompt Caching

Marqueurs `ephemeral` ajoutés automatiquement :

```go
// Cache control placement:
├── Dernier message système     → ephemeral
├── Avant-dernier message       → ephemeral
└── Antépénultième message      → ephemeral
```

Cela permet à Anthropic de mettre en cache ces messages et de réduire les tokens d'input sur les tours suivants.

### 6.3 Filtrage des orphelins de tool calls

```go
// Avant d'envoyer à l'IA:
filterOrphanedToolResults()           // Supprime tool results sans tool call correspondant
syntheticToolResultsForOrphanedCalls() // Injecte erreurs synthétiques pour tool calls orphelins
```

Cela nettoie l'historique des messages inutiles qui consomment des tokens.

### 6.4 Deux modèles (Large/Small)

```
├── Large Model: tâches principales (coder, résumé)
└── Small Model: titre de session, agentic_fetch
    → Économise les tokens de haute qualité pour les tâches simples
```

### 6.5 Limitations de taille des outils

| Outil | Limite | Impact token |
|-------|--------|-------------|
| bash output | 30,000 chars | Tronque les longues sorties |
| view | 100KB max, 2000 lignes | Limite les gros fichiers |
| grep | 100 résultats, 500 chars/ligne | Limite les recherches larges |
| glob | 100 fichiers | Limite les résultats |
| ls | 1000 fichiers | Limite les répertoires massifs |
| fetch | 100KB | Limite les pages web |
| sourcegraph | 20 résultats max | Limite les recherches code |
| description tools | FirstLineDescription() | N'envoie que la 1ère ligne de description |

### 6.6 Short Tool Descriptions

```go
// Par défaut: seul le premier ligne non-vide de la description est envoyé
FirstLineDescription(fullMarkdownDescription)

// Désactivable via: CRUSH_SHORT_TOOL_DESCRIPTIONS=0
```

Chaque outil a une description markdown complète, mais seule la première ligne est envoyée à l'IA — économie significative sur 22+ outils.

### 6.7 Workaround média par provider

Pour les providers non-Anthropic/Bedrock, les images dans les tool results sont converties en messages utilisateur séparés avec pièces jointes — évitant les erreurs API et les tokens gaspillés.

### 6.8 Injection system_reminder minimale

Seul un rappel minimal est injecté comme premier message utilisateur :
```
"<system_reminder>This is a reminder that your todo list is currently empty.
DO NOT mention this to the user explicitly...</system_reminder>"
```

Ce message est court et sert de garde-fou sans consommer beaucoup de tokens.

### 6.9 Estimation de tokens

```go
ApproxTokenCount(text) = len(text) / 4  // Heuristique 4 chars = 1 token
```

Utilisé pour le logging et les métriques, pas pour le contrôle strict.

---

## 7. Optimisations de performance

### 7.1 Concurrence

```
├── sync.WaitGroup pour chargement providers (Catwalk + Hyper en //)
├── sync.OnceValue pour cache Hyper (computed once)
├── fastwalk pour découverte des skills (concurent, suit symlinks)
├── errgroup.Group pour readiness des agents (system prompt + tools en //)
├── csync.Map pour maps concurrent-safe (providers, regex cache)
├── sync.RWMutex pour skill tracker
└── Shell mutex sérialise les commandes par instance shell
```

### 7.2 File d'attente de messages (Message Queue)

```
Si agent occupé → message en queue
↓
Dans PrepareStep → injecte messages en queue comme user messages supplémentaires
↓
Pas de perte de messages, traitement séquentiel garanti
```

### 7.3 Cache providers

```
├── Cache JSON: $XDG_DATA_HOME/crush/providers.json
├── ETag support pour revalidation HTTP
├── Fallback: frais → cache → embarqué
└── Chargement concurrent Catwalk + Hyper
```

### 7.4 CGO et GC

```
├── CGO_ENABLED=0 (pas de CGO overhead)
└── GOEXPERIMENT=greenteagc (GC optimisé)
```

### 7.5 Base de données SQLite

```
├── SQLC pour code SQL type-safe généré
├── Transactions avec retry (3 tentatives) pour conflits UNIQUE
├── Atomic SQL increment pour token usage (évite race conditions)
└── Index sur (session_id, created_at) pour requêtes messages rapides
```

### 7.6 Pub/Sub

```
├── Système d'événements découplé
├── Canal d'événements par workspace
└── Pas de polling — push-based
```

---

## 8. Système de permissions

### 8.1 Actions

| Action | Quand | Outils concernés |
|--------|-------|-----------------|
| `"read"` | Lecture hors workingDir | view, ls |
| `"write"` | Modification de fichiers | edit, multiedit, write |
| `"execute"` | Commande shell non-safe | bash |
| `"fetch"` | Requête réseau | fetch, agentic_fetch |
| `"download"` | Téléchargement | download |
| `"list"` | Liste ressources MCP | list_mcp_resources |
| `"read"` | Lecture ressource MCP | read_mcp_resource |

### 8.2 Auto-approbations

- Commandes bash `safeCommands` (ls, cat, pwd, git status, etc.) → **pas de permission**
- Docker MCP tools whitelistés (`mcp_docker_mcp-find`, etc.) → **auto-approuvé**
- Sous-agents (agent, agentic_fetch) → **auto-approuvé**

### 8.3 Sécurité shell

```
Banned commands: alias, aria2c, axel, chrome, curl, curlie, firefox,
http-prompt, httpie, links, lynx, nc, safari, scp, ssh, telnet, w3m,
wget, xh, doas, su, sudo, apk, apt, apt-cache, apt-get, dnf, dpkg,
emerge, home-manager, makepkg, opkg, pacman, paru, pkg, pkg_add,
pkg_delete, portage, rpm, yay, yum, zypper, at, batch, chkconfig,
crontab, fdisk, mkfs, mount, parted, service, systemctl, umount,
firewall-cmd, ifconfig, ip, iptables, netstat, pfctl, route, ufw
```

---

## 9. Providers et modèles

### 9.1 Types de providers supportés

```
├── OpenAI
├── Anthropic
├── OpenRouter (suffixe :exacto pour modèles supportés)
├── Vercel
├── Azure
├── AWS Bedrock
├── Google (Gemini)
├── Google Vertex AI
├── OpenAI-compatible (générique)
└── Hyper (Charm's meta-provider)
```

### 9.2 Hyper Provider

```
├── Endpoint: https://hyper.charm.land/api/v1/fantasy
├── Activation: HYPER, HYPERCRUSH, HYPER_ENABLE, HYPER_ENABLED env vars
├── Modèles: GLM-5, GLM-5.1, gpt-oss-120b, Kimi K2.5, Kimi K2.6
├── Routage: Anthropic/OpenAI/Google/OpenAI-compat selon model ID
└── Header: x-crush-id pour identification
```

### 9.3 Construction du provider

```go
buildProvider(config) → fantasy.Provider:
    1. Parse le type de provider
    2. Configure base URL, API key, headers
    3. Ajoute beta headers si thinking model (Anthropic)
    4. Pour Hyper: route vers le bon provider selon model ID
    5. Pour OpenRouter: ajoute :exacto si supporté
```

---

## 10. Système de skills

### 10.1 Standard Agent Skills

Crush implémente le standard ouvert [agentskills.io](https://agentskills.io).

### 10.2 Découverte

```
Chemins explorés:
├── $CRUSH_SKILLS_DIR
├── ~/.config/agents/skills/
├── ~/.config/crush/skills/
├── .agents/skills/
├── .crush/skills/
├── .claude/skills/
├── .cursor/skills/
└── Custom via options.skills_paths

Recherche:
├── fastwalk (concurent, suit symlinks)
├── Cherche fichiers SKILL.md
├── Parse YAML frontmatter (entre ---)
├── Validation: nom alphanum-hyphens ≤64 chars, description ≤1024 chars
└── Déduplication: dernier occurence gagne (user > builtin)
```

### 10.3 Injection dans le prompt

```xml
<available_skills>
  <skill>
    <name>skill-name</name>
    <description>Description courte</description>
    <location>/path/to/SKILL.md</location>
    <type>builtin</type>
  </skill>
</available_skills>

<skills_usage>
  When a user task matches a skill's description, read the skill's SKILL.md file...
</skills_usage>
```

**Important :** Seules les métadonnées (nom, description, chemin) sont injectées dans le system prompt. Les **instructions complètes** ne sont lues que quand l'agent décide d'activer le skill — économie de tokens.

### 10.4 Skill Tracker

```go
type Tracker struct {
    active map[string]bool  // Skills actifs (post-dedup, post-filter)
    loaded map[string]bool  // Skills dont les instructions ont été lues
    mu     sync.RWMutex
}

MarkLoaded(name)   // Marque un skill comme lu (uniquement si dans active set)
IsLoaded(name)     // Vérifie si déjà chargé
LoadedNames()      // Liste des skills chargés
```

---

## 11. Intégration LSP

### 11.1 Configuration

```yaml
lsp:
  - command: "gopls"
    args: ["serve"]
    env: {}
    file_types: [".go"]
    root_markers: ["go.mod"]
    init_options: {}
```

### 11.2 Outils LSP

- `lsp_diagnostics` — Diagnostics par fichier ou projet entier (max 10 fichiers, 5s wait)
- `lsp_references` — Recherche de références symboliques (grep + LSP FindReferences)
- `lsp_restart` — Redémarrage d'un ou tous les clients LSP

### 11.3 Intégration dans les outils

```
view   → openInLSPs + wait 300ms pour diagnostics
edit   → notifyLSPs + wait 5s pour diagnostics
write  → notifyLSPs + wait 5s pour diagnostics
```

Les diagnostics sont appendés dans les réponses des outils via des tags XML :
```xml
<file_diagnostics>...</file_diagnostics>
<project_diagnostics>...</project_diagnostics>
<diagnostic_summary>errors: N, warnings: M</diagnostic_summary>
```

---

## 12. Intégration MCP

### 12.1 Configuration

```yaml
mcp:
  - name: "my-server"
    command: "npx"        # Mode stdio
    args: ["-y", "my-mcp"]
    env: {}
    url: ""               # OU mode HTTP/SSE
    timeout: 30s
    disabled_tools: []
```

### 12.2 Outils MCP dynamiques

- Outils nommés `mcp_{server}_{tool}`
- Schéma extrait de `InputSchema` MCP
- Permission requise (sauf whitelist Docker)
- Support image/média dans les résultats

### 12.3 Instructions MCP injectées

Les instructions des serveurs MCP connectés sont injectées dans le system prompt via `<mcp-instructions>`.

---

## 13. Structure de la base de données

### 13.1 Tables principales

```sql
-- Sessions
sessions (
    id, title, prompt_tokens, completion_tokens,
    summary_message_id,          -- ID du message résumé (NULL si pas de résumé)
    cost, todos,                  -- Coût et todo list
    created_at, updated_at
)

-- Messages
messages (
    id, session_id, role,         -- user/assistant/system/tool
    parts,                        -- JSON array de {type, data}
    model, provider,
    is_summary_message,           -- Flag de compaction
    created_at, finished_at
)

-- Fichiers (version history)
files (
    id, session_id, path,
    content, version,             -- Auto-incrémenté par path
    is_new,
    created_at
)

-- Fichiers lus (read tracking)
read_files (
    path, session_id,
    last_read_time                -- Pour "read before edit"
)
```

### 13.2 Requêtes clés

```sql
-- Messages d'une session (chronologique)
SELECT * FROM messages WHERE session_id = ? ORDER BY created_at ASC;

-- Update atomique des tokens (évite race conditions)
UPDATE sessions SET
    prompt_tokens = prompt_tokens + ?,
    completion_tokens = completion_tokens + ?,
    cost = cost + ?
WHERE id = ?;

-- Derniers fichiers par path (version max)
SELECT f.* FROM files f
INNER JOIN (
    SELECT path, MAX(version) as max_ver
    FROM files WHERE session_id = ?
    GROUP BY path
) latest ON f.path = latest.path AND f.version = latest.max_ver;

-- Stats: utilisation des outils via JSON
SELECT json_extract(part.data, '$.name') as tool_name, COUNT(*)
FROM messages, json_each(messages.parts) as part
WHERE json_extract(part.value, '$.type') = 'tool_call'
GROUP BY tool_name;
```

---

## 14. Fichiers clés à explorer

### Architecture et flow principal

| Fichier | Ce qu'il contient |
|---------|------------------|
| `internal/agent/agent.go` | Moteur IA — construction messages, streaming, résumé, queue |
| `internal/agent/coordinator.go` | Orchestration — création agents, tools, models, provider options |
| `internal/agent/prompts.go` | Factory de prompts système |
| `internal/agent/loop_detection.go` | Détection de boucles (SHA-256 signatures) |

### Templates (ce qui est envoyé à l'IA)

| Fichier | Ce qu'il contient |
|---------|------------------|
| `internal/agent/templates/coder.md.tpl` | System prompt principal (405 lignes) |
| `internal/agent/templates/task.md.tpl` | Prompt sous-agent |
| `internal/agent/templates/summary.md` | Prompt de résumé/compaction |
| `internal/agent/templates/title.md` | Prompt de génération de titre |
| `internal/agent/templates/agent_tool.md` | Instructions tool agent |
| `internal/agent/templates/agentic_fetch_prompt.md.tpl` | Prompt sous-agent fetch |

### Outils

| Fichier | Ce qu'il contient |
|---------|------------------|
| `internal/agent/agent_tool.go` | Tool agent (sous-agent spawner) |
| `internal/agent/agentic_fetch_tool.go` | Tool agentic_fetch |
| `internal/agent/tools/bash.go` | Tool bash |
| `internal/agent/tools/edit.go` | Tool edit |
| `internal/agent/tools/multiedit.go` | Tool multiedit |
| `internal/agent/tools/write.go` | Tool write |
| `internal/agent/tools/view.go` | Tool view |
| `internal/agent/tools/glob.go` | Tool glob |
| `internal/agent/tools/grep.go` | Tool grep |
| `internal/agent/tools/ls.go` | Tool ls |
| `internal/agent/tools/fetch.go` | Tool fetch |
| `internal/agent/tools/sourcegraph.go` | Tool sourcegraph |
| `internal/agent/tools/web_search.go` | Tool web_search (DuckDuckGo) |
| `internal/agent/tools/web_fetch.go` | Tool web_fetch |
| `internal/agent/tools/download.go` | Tool download |
| `internal/agent/tools/todos.go` | Tool todos |
| `internal/agent/tools/diagnostics.go` | Tool LSP diagnostics + helpers |
| `internal/agent/tools/references.go` | Tool LSP references |
| `internal/agent/tools/lsp_restart.go` | Tool LSP restart |
| `internal/agent/tools/crush_info.go` | Tool crush_info |
| `internal/agent/tools/crush_logs.go` | Tool crush_logs |
| `internal/agent/tools/mcp-tools.go` | Outils MCP dynamiques |

### Messages et données

| Fichier | Ce qu'il contient |
|---------|------------------|
| `internal/message/message.go` | Service de messages (CRUD + events) |
| `internal/message/content.go` | Types de contenu + conversion fantasy.Message |
| `internal/message/attachment.go` | Pièces jointes |
| `internal/history/file.go` | Historique de versions fichiers |
| `internal/db/sql/sessions.sql` | Requêtes SQL sessions |
| `internal/db/sql/messages.sql` | Requêtes SQL messages |
| `internal/db/sql/files.sql` | Requêtes SQL fichiers |
| `internal/db/sql/stats.sql` | Requêtes SQL statistiques |

### Configuration et providers

| Fichier | Ce qu'il contient |
|---------|------------------|
| `internal/config/config.go` | Structures de config (models, providers, tools, agents) |
| `internal/config/provider.go` | Chargement des providers (Catwalk + Hyper + cache) |
| `internal/backend/config.go` | API config backend |
| `internal/agent/hyper/provider.go` | Provider Hyper |
| `internal/agent/hyper/provider.json` | Définition des modèles Hyper |

### Infrastructure

| Fichier | Ce qu'il contient |
|---------|------------------|
| `internal/shell/shell.go` | Shell POSIX cross-platform (mvdan/sh) |
| `internal/diff/diff.go` | Génération de diffs unifiés |
| `internal/skills/skills.go` | Système de skills (découverte, injection) |
| `internal/skills/tracker.go` | Suivi des skills chargés |
| `internal/lsp/manager.go` | Gestionnaire LSP |
| `internal/filetracker/service.go` | Suivi des fichiers lus |

---

## 15. Leçons pour notre application

### 15.1 Patterns à adopter

| Pattern | Pourquoi | Comment Crush le fait |
|---------|----------|----------------------|
| **Résumé automatique adaptatif** | Gère les longues conversations sans perte | Seuil 20K tokens (fenêtres >200K) ou 20% (fenêtres <200K), skip si fenêtre inconnue |
| **Deux niveaux de modèle** | Économise les tokens coûteux | Large pour coder, Small pour titres et fetch |
| **Skills avec lazy loading** | N'injecte que les métadonnées, charge les instructions à la demande | Métadonnées dans system prompt, instructions lues via view |
| **Short tool descriptions** | Économise des tokens sur 22+ outils | FirstLineDescription() par défaut |
| **Anthropic prompt caching** | Réduit les tokens d'input sur les tours suivants | `ephemeral` sur dernier system + 2 derniers messages |
| **Filetracker read-before-edit** | Sécurité + contexte pour l'IA | Enregistre chaque lecture, vérifie avant édition |
| **Loop detection** | Arrête les boucles infinies coûteuses | SHA-256 signature sur fenêtre de 10 étapes, max 5 répétitions |
| **Orphan filtering** | Nettoie l'historique des messages inutiles | Supprime tool results orphelins, injecte erreurs synthétiques |
| **Atomic SQL increments** | Pas de race conditions sur les compteurs | `prompt_tokens = prompt_tokens + ?` au lieu de read-modify-write |
| **Message queue** | Pas de perte de messages | Queue + injection dans PrepareStep |

### 15.2 Optimisations token spécifiques

1. **Troncature de sortie** — bash (30K), grep (500 chars/ligne), view (2000 lignes)
2. **Limites de résultats** — glob (100), grep (100), ls (1000), sourcegraph (20)
3. **Première ligne de description** — Seulement la 1ère ligne des descriptions markdown d'outils
4. **Estimation heuristique** — `len(text) / 4` pour logging sans appel API
5. **Résumé détaillé structuré** — Sections obligatoires assurent pas de perte d'information critique
6. **Reset des compteurs après résumé** — PromptTokens=0, CompletionTokens=résumé seulement

### 15.3 Points d'attention

- **Le system prompt fait ~405 lignes** — C'est énorme mais structuré en sections XML claires
- **Pas de troncature du system prompt** — Les skills et context files sont injectés en entier
- **Le résumé n'a pas de limite** — *"Err on the side of too much detail"*
- **La détection de boucle est basique** — SHA-256 sur ToolName+Input+Output, mais ne détecte pas les boucles sémantiques
- **Pas de token counting précis** — Heuristique 4 chars/token, pas de tiktoken
- **Le file history persiste** — Les versions de fichiers survivent au résumé (pas dans le contexte LLM mais disponibles dans l'UI)

### 15.4 Architecture recommandée

```
Pour notre app, inspirons-nous de:

1. Coordinator Pattern
   └── Orchestrateur central qui assemble models + tools + prompts
   └── Agents par session avec state isolé

2. Summary/Compaction Pipeline
   └── Seuils adaptatifs (fenêtre large vs small)
   └── Résumé structuré avec sections obligatoires
   └── Reset des compteurs après compaction

3. Tool Architecture
   └── Interface uniforme (params, execute, response)
   └── Permission system par action
   └── Output truncation systématique
   └── Metadata sur chaque réponse

4. Provider Layer
   └── JSON merge en 3 couches pour options
   └── Support multi-provider avec routage
   └── Cache avec ETag + fallback

5. Lazy Skill Loading
   └── Métadonnées dans system prompt
   └── Instructions complètes à la demande
   └── Tracker pour éviter les rechargements
```

---

## Annexe : Schéma de dépendances

```
main.go
  └── internal/cmd/ (CLI commands)
       └── internal/backend/ (workspace management)
            └── internal/app/ (application logic)
                 └── internal/agent/coordinator.go (orchestration)
                      ├── internal/agent/agent.go (moteur IA)
                      │    ├── internal/agent/templates/ (prompts)
                      │    ├── internal/agent/prompt/ (prompt builder)
                      │    ├── internal/message/ (message types)
                      │    └── internal/agent/loop_detection.go
                      ├── internal/agent/tools/ (22+ outils)
                      ├── internal/skills/ (skill discovery)
                      ├── internal/config/ (configuration)
                      ├── internal/lsp/ (LSP management)
                      ├── internal/shell/ (shell execution)
                      ├── internal/db/ (SQLite persistence)
                      ├── internal/filetracker/ (read tracking)
                      └── internal/diff/ (diff generation)
```

---

*Rapport généré le 26 avril 2026 — Basé sur l'analyse du commit `HEAD` de [charmbracelet/crush](https://github.com/charmbracelet/crush)*
