refactor: migrate MCP to aichat and integrate llm-functions

Replace LM Studio direct integration with aichat-based MCP server, enabling Qwen3-Coder to autonomously use filesystem, shell and web tools via llm-functions. Key changes:
- Update server.py to launch aichat on a dynamic port and handle tool_calls loop
- Add llm-functions directory with prebuilt tools (fs_cat, fs_ls, execute_command, etc.)
- Revise delegation rules to remove file count limit and emphasize autonomous tool usage
- Update agents and skills docs to reflect new architecture and workflow
- Add setup instructions for aichat, argc, jq and llm-functions build step
- Bump MCP server version to 3.0.0
This commit is contained in:
2026-03-27 09:13:47 +01:00
parent 33191c54b2
commit cbb9de7c30
6 changed files with 379 additions and 153 deletions

View File

@@ -29,18 +29,20 @@ Programmeur expérimenté. Connaît parfaitement son projet, ses outils, et son
## Heuristiques de délégation
Délègue à `qwen3-worker` si la tâche est **atomique** et satisfait TOUS les critères :
- ≤ 3 fichiers concernés
- Pas de modification dAPI publique
- Pas de dépendance externe non encore importée
- Tâches typiques : génération de tests unitaires, reformatage, documentation inline,
conversion de types simples, scaffolding de stubs
- Pour le bash, éviter les commandes destructrices (voir garde-fous)
Délègue à `qwen3-worker` (via MCP `qwen3_task`) si la tâche satisfait TOUS les critères :
- Objectif clair et bien délimité
- Pas de modification dAPI publique (traits Rust, interfaces Go exportées)
- Pas de vision cross-module requise
- Tâches typiques : génération de tests, documentation inline, scaffolding de stubs,
reformatage, recherche web, exécution de scripts, création de fichiers
Qwen3 dispose doutils autonomes (filesystem, shell, web) — inutile de lui passer
le contenu des fichiers, il les lit lui-même.
Traite toi-même si :
- Refactoring cross-module
- Conception darchitecture
- Debugging avec contexte multi-fichiers
- Debugging avec stacktrace multi-fichiers
- Modification de traits/interfaces publics
## Garde-fous absolus

View File

@@ -1,30 +1,56 @@
---
name: qwen3-worker
description: >
Délègue à Qwen3-Coder via LM Studio les tâches atomiques sur 1-3 fichiers :
génération de tests unitaires, documentation inline, scaffolding de stubs,
reformatage de code. N'utilise PAS pour du refactoring cross-module ou de
Délègue à Qwen3-Coder via le MCP tool `qwen3_task` les tâches de codage autonomes :
génération de tests, documentation, scaffolding, reformatage, recherche web,
exécution de scripts. Qwen3 dispose d'outils filesystem, shell et web — il lit et
écrit les fichiers lui-même. N'utilise PAS pour du refactoring cross-module ou de
la conception d'architecture.
tools: [Read, Write, Bash]
tools: [mcp__qwen3__qwen3_task]
model: inherit
---
Tu es un assistant de codage spécialisé exécutant des tâches courtes et précises.
Tu es un orchestrateur qui délègue des tâches de codage à Qwen3-Coder via l'outil MCP `qwen3_task`.
## Comportement
1. Lis les fichiers nécessaires avec l'outil Read (ne reçois pas le contenu en entrée).
2. Effectue la transformation demandée.
3. Écris le résultat avec Write.
4. Exécute le linter approprié (clippy pour Rust, golangci-lint pour Go) et corrige
les erreurs éventuelles (max 2 tentatives).
5. Retourne un résumé : fichiers modifiés, changements effectués, résultat du lint.
1. Formule une description précise de la tâche.
2. Appelle `qwen3_task` avec le champ `task`. Le champ `files` est optionnel — ne l'utilise
que pour donner un contexte initial ; Qwen3 peut lire les fichiers dont il a besoin lui-même.
3. Qwen3 opère de façon autonome : il lit, écrit, exécute des commandes et fait des requêtes
web via ses propres outils (llm-functions). Tu n'as pas à gérer ces étapes.
4. Retourne le résultat de `qwen3_task` à l'orchestrateur principal.
## Outils disponibles pour Qwen3
Qwen3 dispose des outils suivants (définis dans `.claude/llm-functions/tools.txt`) :
| Outil | Description |
|-------|-------------|
| `fs_cat` | Lit un fichier |
| `fs_ls` | Liste un répertoire |
| `fs_mkdir` | Crée un répertoire |
| `fs_write` | Écrit/crée un fichier |
| `fs_patch` | Modifie un fichier par patch |
| `execute_command` | Exécute une commande shell |
| `fetch_url_via_curl` | Requête HTTP GET |
| `web_search_aichat` | Recherche web |
| `execute_py_code` | Exécute du code Python |
## Format de sortie
Qwen3 retourne une réponse en langage naturel. Résume-la ainsi :
```
STATUS: success|partial|failure
FILES_MODIFIED: liste des fichiers
FILES_MODIFIED: liste des fichiers (si applicable)
SUMMARY: description des changements
LINT: passed|failed (+ détail si failed)
```
Ne génère pas de fonctions non demandées. Ne modifie pas les signatures publiques.
Si Qwen3 retourne une erreur (`ERROR:`), signale-la avec `STATUS: failure`.
## Contraintes
- Ne modifie pas les signatures publiques (traits Rust, interfaces Go exportées).
- Ne génère pas de fonctions non demandées.
- Max 2 re-délégations en cas d'échec partiel ; au-delà, remonte le problème.

View File

@@ -0,0 +1 @@
2026-03-27T08:01:50Z agent=unknown

View File

@@ -1,34 +1,56 @@
#!/usr/bin/env -S .claude/venv/bin/python3
"""
MCP server stdio exposant un outil `qwen3_task`.
Lance aichat en mode serveur au démarrage et gère la boucle tool_calls.
Les outils disponibles pour Qwen3 sont chargés depuis llm-functions/functions.json.
Appelé par Claude Code via : claude mcp add --transport stdio qwen3 -- .claude/venv/bin/python3 .claude/mcp/qwen3-mcp/server.py
"""
import atexit
import json
import os
import shutil
import socket
import subprocess
import sys
import time
from pathlib import Path
import requests
SERVER = "http://localhost"
PORT = 8888 # 1248
LMSTUDIO_URL = f"{SERVER}:{PORT}/v1/chat/completions"
QWEN3_MODEL = "qwen/qwen3-coder-next"
QWEN3_MODEL = "LMStudio:qwen/qwen3-coder-next"
MAX_TOOL_ITERATIONS = 10
TOOLS = [
# Répertoire llm-functions relatif à ce script
_HERE = Path(__file__).parent
_LLM_FUNCTIONS_DIR = _HERE.parent.parent / "llm-functions"
_FUNCTIONS_JSON = _LLM_FUNCTIONS_DIR / "functions.json"
_BIN_DIR = _LLM_FUNCTIONS_DIR / "bin"
def _load_qwen3_tools() -> list[dict]:
"""Charge les outils depuis llm-functions/functions.json et les wrappe au format OpenAI."""
if not _FUNCTIONS_JSON.exists():
print(f"WARN: {_FUNCTIONS_JSON} introuvable — aucun outil disponible",
file=sys.stderr, flush=True)
return []
raw = json.loads(_FUNCTIONS_JSON.read_text())
return [{"type": "function", "function": tool} for tool in raw]
# Outil MCP exposé à Claude Code
MCP_TOOLS = [
{
"name": "qwen3_task",
"description": "Délègue une tâche de codage atomique à Qwen3-Coder via LM Studio.",
"description": "Délègue une tâche de codage à Qwen3-Coder via aichat. Qwen3 dispose d'outils filesystem, shell et web (llm-functions).",
"inputSchema": {
"type": "object",
"properties": {
"task": {
"type": "string",
"description": "Description précise de la tâche",
},
"task": {"type": "string", "description": "Description précise de la tâche"},
"files": {
"type": "array",
"items": {"type": "string"},
"description": "Chemins des fichiers concernés",
"description": "Fichiers à mettre en contexte initial (optionnel)",
},
},
"required": ["task"],
@@ -37,40 +59,148 @@ TOOLS = [
]
def send(obj: dict):
print(json.dumps(obj), flush=True)
# --- Gestion du processus aichat ---
_aichat_proc: subprocess.Popen | None = None
_aichat_url: str | None = None
def _find_free_port() -> int:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind(("127.0.0.1", 0))
return s.getsockname()[1]
def _start_aichat() -> str:
global _aichat_proc, _aichat_url
if not shutil.which("aichat"):
raise RuntimeError("aichat introuvable dans le PATH")
port = _find_free_port()
address = f"127.0.0.1:{port}"
env = os.environ.copy()
if _LLM_FUNCTIONS_DIR.exists():
env["AICHAT_FUNCTIONS_DIR"] = str(_LLM_FUNCTIONS_DIR)
_aichat_proc = subprocess.Popen(
["aichat", "--serve", address],
stdin=subprocess.DEVNULL,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
env=env,
)
atexit.register(_stop_aichat)
url = f"http://{address}"
for _ in range(30):
try:
requests.get(f"{url}/v1/models", timeout=1)
_aichat_url = url
return url
except requests.RequestException:
time.sleep(0.3)
_aichat_proc.kill()
raise RuntimeError(f"aichat n'a pas démarré sur {address}")
def _stop_aichat():
if _aichat_proc and _aichat_proc.poll() is None:
_aichat_proc.terminate()
try:
_aichat_proc.wait(timeout=5)
except subprocess.TimeoutExpired:
_aichat_proc.kill()
# --- Exécution des outils via llm-functions/bin/ ---
def _execute_tool(name: str, args: dict) -> str:
"""Exécute un outil llm-functions via son binaire dans bin/.
Les binaires attendent le JSON des arguments comme premier argument positionnel.
"""
bin_path = _BIN_DIR / name
if not bin_path.exists():
return f"ERROR: outil '{name}' introuvable dans {_BIN_DIR}"
try:
result = subprocess.run(
[str(bin_path), json.dumps(args)],
capture_output=True,
text=True,
timeout=60,
)
output = (result.stdout + result.stderr).strip()
return output or f"(exit code {result.returncode})"
except subprocess.TimeoutExpired:
return f"ERROR: timeout lors de l'exécution de '{name}'"
except Exception as e:
return f"ERROR: {e}"
# --- Boucle tool_calls → Qwen3 ---
def call_qwen3(task: str, files: list[str]) -> str:
if _aichat_url is None:
raise RuntimeError("aichat non démarré")
qwen3_tools = _load_qwen3_tools()
context = ""
for path in files:
try:
with open(path) as f:
with open(path, encoding="utf-8") as f:
context += f"--- {path} ---\n{f.read()}\n\n"
except OSError as e:
context += f"--- {path} --- ERREUR: {e}\n\n"
prompt = f"""Tu es un assistant de codage. Effectue la tâche suivante de manière précise.
user_content = f"TÂCHE: {task}"
if context:
user_content += f"\n\nFICHIERS:\n{context}"
TÂCHE: {task}
messages = [{"role": "user", "content": user_content}]
payload: dict = {
"model": QWEN3_MODEL,
"messages": messages,
"temperature": 0.2,
"max_tokens": 4096,
}
if qwen3_tools:
payload["tools"] = qwen3_tools
FICHIERS:
{context}
for _ in range(MAX_TOOL_ITERATIONS):
resp = requests.post(
f"{_aichat_url}/v1/chat/completions",
json=payload,
timeout=120,
)
resp.raise_for_status()
data = resp.json()
choice = data["choices"][0]
message = choice["message"]
payload["messages"].append(message)
Retourne uniquement le code modifié ou généré, sans explication.
"""
resp = requests.post(
LMSTUDIO_URL,
json={
"model": QWEN3_MODEL,
"messages": [{"role": "user", "content": prompt}],
"temperature": 0.2,
"max_tokens": 4096,
},
timeout=120,
)
resp.raise_for_status()
return resp.json()["choices"][0]["message"]["content"]
if choice["finish_reason"] != "tool_calls":
return message.get("content") or ""
for tc in message.get("tool_calls", []):
fn = tc["function"]
args = json.loads(fn["arguments"])
result = _execute_tool(fn["name"], args)
payload["messages"].append({
"role": "tool",
"tool_call_id": tc["id"],
"content": result,
})
return "ERROR: nombre maximum d'itérations tool_calls atteint"
# --- Protocole MCP stdio ---
def send(obj: dict):
print(json.dumps(obj), flush=True)
def handle(req: dict):
@@ -78,21 +208,19 @@ def handle(req: dict):
req_id = req.get("id")
if method == "initialize":
send(
{
"jsonrpc": "2.0",
"id": req_id,
"result": {
"protocolVersion": "2024-11-05",
"capabilities": {"tools": {}},
"serverInfo": {"name": "qwen3-mcp", "version": "1.0.0"},
},
}
)
send({
"jsonrpc": "2.0",
"id": req_id,
"result": {
"protocolVersion": "2024-11-05",
"capabilities": {"tools": {}},
"serverInfo": {"name": "qwen3-mcp", "version": "3.0.0"},
},
})
elif method == "notifications/initialized":
pass # notification, pas de réponse
pass
elif method == "tools/list":
send({"jsonrpc": "2.0", "id": req_id, "result": {"tools": TOOLS}})
send({"jsonrpc": "2.0", "id": req_id, "result": {"tools": MCP_TOOLS}})
elif method == "tools/call":
params = req.get("params", {})
name = params.get("name", "")
@@ -100,42 +228,31 @@ def handle(req: dict):
if name == "qwen3_task":
try:
text = call_qwen3(args["task"], args.get("files", []))
send(
{
"jsonrpc": "2.0",
"id": req_id,
"result": {"content": [{"type": "text", "text": text}]},
}
)
send({"jsonrpc": "2.0", "id": req_id, "result": {
"content": [{"type": "text", "text": text}]
}})
except Exception as e:
send(
{
"jsonrpc": "2.0",
"id": req_id,
"result": {
"content": [{"type": "text", "text": f"ERROR: {e}"}],
"isError": True,
},
}
)
send({"jsonrpc": "2.0", "id": req_id, "result": {
"content": [{"type": "text", "text": f"ERROR: {e}"}],
"isError": True,
}})
else:
send(
{
"jsonrpc": "2.0",
"id": req_id,
"error": {"code": -32601, "message": f"Outil inconnu : {name}"},
}
)
send({"jsonrpc": "2.0", "id": req_id,
"error": {"code": -32601, "message": f"Outil inconnu : {name}"}})
elif req_id is not None:
send(
{
"jsonrpc": "2.0",
"id": req_id,
"error": {"code": -32601, "message": f"Méthode inconnue : {method}"},
}
)
send({"jsonrpc": "2.0", "id": req_id,
"error": {"code": -32601, "message": f"Méthode inconnue : {method}"}})
# --- Démarrage ---
try:
_start_aichat()
except Exception as e:
print(json.dumps({"error": f"Impossible de démarrer aichat : {e}"}),
file=sys.stderr, flush=True)
sys.exit(1)
for line in sys.stdin:
line = line.strip()
if not line:

View File

@@ -1,32 +1,43 @@
---
name: delegation-rules
description: Règles pour décider quand et comment déléguer à qwen3-worker ou qwen3 MCP
description: Règles pour décider quand et comment déléguer à qwen3-worker via le MCP qwen3_task
---
# Règles de Délégation à Qwen3
## Critères de délégation (TOUS requis)
- ≤ 3 fichiers source concernés
- Tâche atomique, sans dépendance à résoudre
- Pas de modification de signature publique (trait, interface exportée)
- Contexte de fichier estimé < 8 000 tokens
Qwen3-Coder opère via le MCP tool `qwen3_task`. Il dispose d'outils filesystem, shell
et web (llm-functions) — il peut lire/écrire des fichiers, exécuter des commandes,
faire des recherches web et exécuter du code de façon autonome.
## Critères de délégation (TOUS requis)
- Tâche bien définie, objectif clair
- Pas de modification de signature publique (trait, interface exportée)
- Pas de refactoring cross-module nécessitant une vision globale de l'architecture
## Tâches éligibles
## Tâches typiques éligibles
- Génération de tests unitaires pour une fonction donnée
- Ajout de documentation (doc comments Rust, godoc Go)
- Scaffolding de stubs à partir d'une interface
- Conversion de types internes simples
- Reformatage / réorganisation d'imports
- Recherche d'information sur le web ou dans une URL
- Exécution et vérification d'un script Python
- Création ou modification de fichiers de configuration
## Procédure
1. Évalue les critères ci-dessus.
2. Si éligible → utilise le subagent `qwen3-worker` en précisant la tâche et les fichiers.
3. Attends le résumé de sortie (STATUS / FILES_MODIFIED / LINT).
4. Si STATUS=failure → prends en charge toi-même.
5. Si STATUS=partial → corrige les points restants directement.
2. Si éligible → utilise le subagent `qwen3-worker` avec une description précise de la tâche.
Ne pas pré-charger les fichiers : Qwen3 les lit lui-même si nécessaire.
3. Attends le résumé (STATUS / FILES_MODIFIED / SUMMARY).
4. Si `STATUS: failure` → prends en charge toi-même.
5. Si `STATUS: partial` → corrige les points restants directement.
## Ne jamais déléguer
- Refactoring impliquant > 3 fichiers
- Refactoring impliquant une vision cross-module
- Changement d'architecture ou de design pattern
- Debugging avec stacktrace à analyser
- Tâches nécessitant une recherche web ou du contexte externe
- Debugging avec stacktrace à analyser sur plusieurs fichiers
- Tâches nécessitant le contexte de la conversation en cours

151
README.md
View File

@@ -1,17 +1,38 @@
# CrazyClaude
Configuration Claude Code pour orchestrer des tâches de développement Rust/Go en déléguant les tâches atomiques à **Qwen3-Coder** via LM Studio.
Configuration Claude Code pour orchestrer des tâches de développement en déléguant les tâches atomiques à **Qwen3-Coder** via [aichat](https://github.com/sigoden/aichat).
Ce dépôt est conçu pour être intégré comme sous-répertoire `.claude/` dans n'importe quel projet. Il fournit agents, hooks, skills et un serveur MCP prêts à l'emploi.
Ce dépôt est conçu pour être intégré comme sous-répertoire `.claude/` dans n'importe quel projet. Il fournit agents, hooks, skills, un serveur MCP et une collection d'outils prêts à l'emploi.
Pour le détail de l'architecture, voir [`rapport-orchestration-claude-code.md`](./rapport-orchestration-claude-code.md).
---
## Architecture
```
Claude Code (orchestrateur)
└── MCP qwen3 (stdio)
└── aichat --serve (port dynamique)
└── LM Studio → Qwen3-Coder-Next
+ outils llm-functions
(fs, shell, web, ...)
```
Le serveur MCP lance aichat au démarrage sur un port libre. Qwen3 dispose d'outils filesystem, shell et web fournis par [llm-functions](https://github.com/sigoden/llm-functions) — sans aucune logique d'outil dans le serveur lui-même.
---
## Prérequis
- [Claude Code](https://claude.ai/code) installé (`claude` disponible dans le PATH)
- [LM Studio](https://lmstudio.ai/) avec le modèle `qwen/qwen3-coder-next` chargé, écoutant sur `http://localhost:1248`
- [Claude Code](https://claude.ai/code) (`claude` dans le PATH)
- [aichat](https://github.com/sigoden/aichat) (`aichat` dans le PATH)
- [LM Studio](https://lmstudio.ai/) avec le modèle `qwen/qwen3-coder-next` chargé et accessible depuis aichat
- [argc](https://github.com/sigoden/argc) — runner de scripts pour llm-functions
- [jq](https://jqlang.github.io/jq/) — requis par llm-functions
- Python 3.11+ (pour le serveur MCP)
- Outils selon votre stack : `cargo` + `clippy` (Rust), `golangci-lint` (Go)
@@ -28,18 +49,14 @@ Avec **Jujutsu** :
jj git clone https://gargoton.petite-maison-orange.fr/eric/CrazyClaude.git .claude-crazy
```
Puis déplacez ou liez le contenu :
Puis copiez le contenu :
```sh
# Copier le répertoire .claude dans votre projet
cp -r .claude-crazy/.claude ./.claude
# Ou, si vous préférez garder une référence au dépôt source :
# utilisez jj workspace add pour ajouter un workspace dédié
cp -r .claude-crazy/llm-functions ./.claude/llm-functions # si présent dans le clone
```
> Si vous utilisez git en sous-jacent avec jj (`jj git init --colocate`),
> vous pouvez aussi ajouter ce dépôt comme subtree :
> Avec jj colocalisé git, vous pouvez aussi utiliser subtree :
>
> ```sh
> git subtree add --prefix .claude https://gargoton.petite-maison-orange.fr/eric/CrazyClaude.git main --squash
@@ -53,24 +70,61 @@ python3 -m venv venv
venv/bin/pip install -r mcp/qwen3-mcp/requirements.txt
```
### 3. Enregistrer le serveur MCP
### 3. Installer et construire llm-functions
Si `llm-functions/` n'est pas déjà présent dans `.claude/` :
```sh
git clone https://github.com/sigoden/llm-functions .claude/llm-functions
```
Choisissez les outils à activer en éditant `.claude/llm-functions/tools.txt`, puis construisez :
```sh
cd .claude/llm-functions
argc build
```
Les outils activés par défaut :
```
fs_cat.sh
fs_ls.sh
fs_mkdir.sh
fs_write.sh
fs_patch.sh
execute_command.sh
fetch_url_via_curl.sh
web_search_aichat.sh
execute_py_code.py
```
### 4. Configurer aichat
Vérifiez que aichat peut accéder à LM Studio et que le modèle `qwen/qwen3-coder-next` est disponible :
```sh
aichat --list-models | grep qwen
```
### 5. Enregistrer le serveur MCP
```sh
claude mcp add --transport stdio qwen3 -- .claude/venv/bin/python3 .claude/mcp/qwen3-mcp/server.py
```
Vérifier que la connexion est établie :
Vérifier :
```sh
claude mcp list
# qwen3: ... - ✓ Connected
```
### 4. Adapter CLAUDE.md à votre projet
### 6. Adapter CLAUDE.md à votre projet
Éditez `.claude/CLAUDE.md` : mettez à jour la section `## Stack` et les heuristiques de délégation selon vos besoins.
Éditez `.claude/CLAUDE.md` : mettez à jour la section `## Stack` et les heuristiques de délégation.
### 5. Rendre les hooks exécutables
### 7. Rendre les hooks exécutables
```sh
chmod +x .claude/hooks/*.sh
@@ -85,7 +139,7 @@ chmod +x .claude/hooks/*.sh
├── CLAUDE.md # contexte projet + heuristiques de délégation
├── settings.json # hooks et permissions
├── agents/
│ ├── qwen3-worker.md # délégation vers LM Studio (Qwen3)
│ ├── qwen3-worker.md # délégation vers Qwen3 via MCP
│ ├── code-reviewer.md # révision code (Claude Haiku)
│ └── task-planner.md # planification complexe (Claude Opus)
├── skills/
@@ -97,66 +151,81 @@ chmod +x .claude/hooks/*.sh
│ └── pre-bash-guard.sh # blocage des commandes dangereuses
├── mcp/
│ └── qwen3-mcp/
│ ├── server.py # serveur MCP stdio (interface LM Studio)
│ ├── agent_lm.py # agent loop CLI autonome
│ ├── server.py # serveur MCP stdio : lance aichat + gère tool_calls
│ ├── agent_lm.py # agent loop CLI autonome (usage direct sans MCP)
│ └── requirements.txt
├── llm-functions/ # collection d'outils pour Qwen3 (sous-repo)
│ ├── tools.txt # outils activés
│ ├── functions.json # déclarations générées (lues par server.py)
│ ├── bin/ # binaires générés par argc build
│ └── tools/ # scripts sources (.sh, .py, .js)
├── logs/ # produits par les hooks (ignorés par jj/git)
└── venv/ # virtualenv Python (ignoré par jj/git)
```
---
## Configuration LM Studio
## Outils disponibles pour Qwen3
Le serveur MCP se connecte sur `http://localhost:1248` avec le modèle `qwen/qwen3-coder-next`.
Les outils sont définis dans `.claude/llm-functions/tools.txt` et buildés avec `argc build`.
Pour modifier ces valeurs, éditez `.claude/mcp/qwen3-mcp/server.py` :
| Outil | Description |
|-------|-------------|
| `fs_cat` | Lit un fichier |
| `fs_ls` | Liste un répertoire |
| `fs_mkdir` | Crée un répertoire |
| `fs_write` | Écrit/crée un fichier |
| `fs_patch` | Modifie un fichier par patch |
| `execute_command` | Exécute une commande shell |
| `fetch_url_via_curl` | Requête HTTP GET |
| `web_search_aichat` | Recherche web |
| `execute_py_code` | Exécute du code Python |
Pour ajouter un outil : éditez `tools.txt` et relancez `argc build`.
---
## Configuration du modèle
Le modèle Qwen3 est configuré dans `.claude/mcp/qwen3-mcp/server.py` :
```python
LMSTUDIO_URL = "http://localhost:1248/v1/chat/completions"
QWEN3_MODEL = "qwen/qwen3-coder-next"
QWEN3_MODEL = "LMStudio:qwen/qwen3-coder-next"
```
Le format `Provider:model-id` suit la convention aichat. Adaptez selon votre installation LM Studio.
---
## Fichiers à ignorer (jj / git)
Ajoutez ces entrées dans votre fichier d'ignore :
```
.claude/venv/
.claude/logs/
.claude/llm-functions/bin/
.claude/llm-functions/cache/
```
Avec Jujutsu :
```sh
echo ".claude/venv/" >> .gitignore
echo ".claude/logs/" >> .gitignore
printf ".claude/venv/\n.claude/logs/\n.claude/llm-functions/bin/\n.claude/llm-functions/cache/\n" >> .gitignore
```
---
## Mise à jour
Avec Jujutsu (workflow colocalisé git) :
Avec Jujutsu colocalisé (`jj git init --colocate`), la mise à jour est gérée côté git :
```sh
git subtree pull --prefix .claude https://gargoton.petite-maison-orange.fr/eric/CrazyClaude.git main --squash
```
Ou si vous avez cloné séparément, tirez les changements puis recopiez :
jj voit le commit résultant comme un commit ordinaire dans son graphe.
Après mise à jour, reconstruire llm-functions si `tools.txt` a changé :
```sh
cd .claude-crazy && jj git fetch && jj new main
cp -r .claude/* ../.claude/
cd .claude/llm-functions && argc build
```
## Outils necessaire
### aichat
### jq
### argc