- 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>
39 KiB
Rapport d'Architecture : CharmBracelet Crush
Analyse complĂšte de l'application charmbracelet/crush â comment elle communique avec les IA, gĂšre les outils, optimise les tokens et les performances.
Table des matiĂšres
- Architecture globale
- Ce qui est envoyé à l'IA
- SystĂšme de prompts
- SystÚme de résumé / compaction
- Fonctionnement des outils (Tools)
- Optimisation de la consommation de tokens
- Optimisations de performance
- SystĂšme de permissions
- Providers et modĂšles
- SystĂšme de skills
- Intégration LSP
- Intégration MCP
- Structure de la base de données
- Fichiers clés à explorer
- 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 brutImageURLContentâ Images (URL ou base64)BinaryContentâ Fichiers binairesToolCallâ 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
FilePartsé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é :
- Current State â Ătat actuel de la tĂąche
- Files & Changes â Tous les fichiers modifiĂ©s et les changements
- Technical Context â Stack technique, dĂ©pendances, patterns
- Strategy & Approach â StratĂ©gie adoptĂ©e
- 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 :
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,000largeContextWindowBuffer = 20,000smallContextWindowRatio = 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
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 :
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
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.
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 :
// 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
// 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
// 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
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
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.
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
<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
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
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 :
<file_diagnostics>...</file_diagnostics>
<project_diagnostics>...</project_diagnostics>
<diagnostic_summary>errors: N, warnings: M</diagnostic_summary>
12. Intégration MCP
12.1 Configuration
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
InputSchemaMCP - 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
-- 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
-- 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
- Troncature de sortie â bash (30K), grep (500 chars/ligne), view (2000 lignes)
- Limites de rĂ©sultats â glob (100), grep (100), ls (1000), sourcegraph (20)
- PremiĂšre ligne de description â Seulement la 1Ăšre ligne des descriptions markdown d'outils
- Estimation heuristique â
len(text) / 4pour logging sans appel API - RĂ©sumĂ© dĂ©taillĂ© structurĂ© â Sections obligatoires assurent pas de perte d'information critique
- 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