Files
MuyueWorkspace/CRUSH_ARCHITECTURE_REPORT.md
Augustin 12000e523c
All checks were successful
Beta Release / beta (push) Successful in 1m1s
fix: token persistence, context windows, CSS tables/bullets/hr, image attachments
- Fix token count reset on app restart: persist realTokens in conversation.json
- Fix token/context window values: Studio 150K (summarize at 120K), Terminal 100K
- Fix table rendering in terminal tab: correct thead/tbody display model
- Fix copy button always top-right in Studio code blocks
- Add markdown horizontal rule (---) support in Studio and Terminal
- Fix bullet list double dot: remove CSS ::before duplicate bullet point
- Add image attachments support (VLM description, file mentions @file.ext)
- Add sudo detection with cache (sync.Once)
- Fix message content serialization (TextContent wrapper)
- Guide AI to use read_file instead of cat in studio prompt

💘 Generated with Crush

Assisted-by: GLM-5.1 via Crush <crush@charm.land>
2026-04-26 15:19:26 +02:00

1074 lines
39 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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)*