diff --git a/.gitignore b/.gitignore index c3c957a..7327d07 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,9 @@ venv/ logs/ +test_qwen3_output/ llm-functions/bin/ llm-functions/cache/ __pycache__/ *.pyc .claude +.claude/ diff --git a/CLAUDE.md b/CLAUDE.md index dc7e6ae..c77364d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -31,9 +31,13 @@ Programmeur expérimenté. Connaît parfaitement son projet, ses outils, et son **Règle par défaut : délègue à Qwen3. Traite toi-même seulement si tu ne peux pas faire autrement.** -Qwen3 dispose d’outils autonomes (filesystem, shell, web) via `qwen3_task`. Il lit et écrit +Délègue en appelant **directement** l’outil MCP `mcp__qwen3__task` — ne pas passer par +un sous-agent (Agent tool), car les sous-agents n’ont pas accès aux MCP. Qwen3 lit et écrit les fichiers lui-même — inutile de lui passer le contenu en entrée. +Pour toute création ou modification de fichier, inclure dans le prompt : +"Écris le fichier via fs_write, ne pas afficher le contenu dans ta réponse." + Délègue systématiquement pour : - Créer ou modifier un fichier (code, config, doc, script) - Générer des tests, de la documentation, des stubs @@ -55,4 +59,4 @@ En cas de doute : délègue. - Ne jamais committer sans que les tests passent - Toujours vérifier `git diff` avant un commit - Ne jamais ajouter de `Co-Authored-By: Claude` ou toute mention d'IA dans les messages de commit -- Pour le bash : ne jamais exécuter de commande avec `eval` non contrôlé ; privilégier l’utilisation de `shellcheck` pour détecter les erreurs courantes +- Pour le bash : ne jamais exécuter de commande avec `eval` non contrôlé ; privilégier l’utilisation de `shellcheck` pour détecter les erreurs courantes \ No newline at end of file diff --git a/agents/code-reviewer.md b/agents/code-reviewer.md index b27cc7c..7f9bcb5 100644 --- a/agents/code-reviewer.md +++ b/agents/code-reviewer.md @@ -1,21 +1 @@ ---- -name: code-reviewer -description: > - Révise du code Rust ou Go pour détecter bugs, régressions, problèmes de style. - Utilise après génération par qwen3-worker ou avant un commit. -tools: [Read, Grep, Glob, Bash] -model: claude-haiku-4-5-20251001 ---- - -Tu es un reviewer expérimenté en Rust et Go. - -Pour chaque fichier fourni : -1. Vérifie la cohérence des types et la gestion d'erreurs. -2. Identifie les patterns non idiomatiques. -3. Signale les tests manquants pour les chemins critiques. - -Format de sortie : -``` -VERDICT: approved|changes_requested -ISSUES: liste numérotée (vide si approved) -``` +"---\nname: code-reviewer\ndescription: >\n Révise du code Rust ou Go pour détecter bugs, régressions, problèmes de style.\n Utilise après délégation via mcp__qwen3__task ou avant un commit.\ntools: [Read, Grep, Glob, Bash]\nmodel: claude-haiku-4-5-20251001\n---\n\nTu es un reviewer expérimenté en Rust et Go.\n\nPour chaque fichier fourni :\n1. Vérifie la cohérence des types et la gestion d'erreurs.\n2. Identifie les patterns non idiomatiques.\n3. Signale les tests manquants pour les chemins critiques.\n\nFormat de sortie :\n```\nVERDICT: approved|changes_requested\nISSUES: liste numérotée (vide si approved)\n```" \ No newline at end of file diff --git a/agents/qwen3-worker.md b/agents/qwen3-worker.md index e67da64..fc04ea7 100644 --- a/agents/qwen3-worker.md +++ b/agents/qwen3-worker.md @@ -1,25 +1,27 @@ --- name: qwen3-worker description: > - Délègue à Qwen3-Coder via le MCP tool `qwen3_task` les tâches de codage autonomes : + Délègue à Qwen3-Coder via le MCP tool `mcp__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: [mcp__qwen3__qwen3_task] +tools: [mcp__qwen3__task] model: inherit --- -Tu es un orchestrateur qui délègue des tâches de codage à Qwen3-Coder via l'outil MCP `qwen3_task`. +⚠️ Ce fichier est une documentation de référence. Ne pas invoquer comme sous-agent via l'Agent tool — les sous-agents n'ont pas accès aux MCP. Appeler `mcp__qwen3__task` directement depuis la conversation principale. + +Tu es un orchestrateur qui délègue des tâches de codage à Qwen3-Coder via l'outil MCP `mcp__qwen3__task`. ## Comportement 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 +2. Appelle `mcp__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. +4. Retourne le résultat de `mcp__qwen3__task` à l'orchestrateur principal. ## Outils disponibles pour Qwen3 diff --git a/agents/task-planner.md b/agents/task-planner.md index b03b54c..e7bad21 100644 --- a/agents/task-planner.md +++ b/agents/task-planner.md @@ -12,7 +12,7 @@ Tu es un architecte logiciel expérimenté en Rust et Go. Pour la demande reçue : 1. Analyse le périmètre et les dépendances (lis les fichiers pertinents). 2. Décompose en étapes atomiques ordonnées. -3. Identifie lesquelles sont délégables à `qwen3-worker` (≤ 3 fichiers, sans changement d'API publique). +3. Identifie lesquelles sont délégables via `mcp__qwen3__task` (≤ 3 fichiers, sans changement d'API publique). 4. Estime les risques et propose des points de contrôle. Format de sortie : @@ -22,4 +22,4 @@ PLAN: 2. ... RISQUES: liste POINTS_DE_CONTRÔLE: liste -``` +``` \ No newline at end of file diff --git a/install.sh b/install.sh index e1c4a95..cca670f 100755 --- a/install.sh +++ b/install.sh @@ -7,6 +7,16 @@ set -euo pipefail CLAUDE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Bootstrap : si on lance install.sh depuis le repo CrazyClaude lui-même +# (et non depuis .claude/ d'un autre projet), on s'installe en tant que .claude/ +if [[ "$(basename "$CLAUDE_DIR")" != ".claude" ]]; then + TARGET="${1:-$(pwd)/.claude}" + echo "==> Bootstrap : installation de CrazyClaude dans $TARGET" + git clone "$CLAUDE_DIR" "$TARGET" + exec bash "$TARGET/install.sh" +fi + PROJECT_DIR="$(dirname "$CLAUDE_DIR")" require() { @@ -31,7 +41,25 @@ if [[ ! -f "$CLAUDE_DIR/llm-functions/Argcfile.sh" ]]; then else echo "déjà présent" fi +if [[ ! -f "$CLAUDE_DIR/llm-functions/tools.txt" ]]; then + echo "Génération de tools.txt..." + find "$CLAUDE_DIR/llm-functions/tools" -maxdepth 1 -type f \ + ! -name 'demo_*' \ + | sed 's|.*/||' \ + | sort > "$CLAUDE_DIR/llm-functions/tools.txt" + echo " $(wc -l < "$CLAUDE_DIR/llm-functions/tools.txt") outils activés" +fi (cd "$CLAUDE_DIR/llm-functions" && argc build) +LOCAL_TOOLS_DIR="$CLAUDE_DIR/tools-local" +if [[ -d "$LOCAL_TOOLS_DIR" ]]; then + for src in "$LOCAL_TOOLS_DIR"/*; do + [[ -f "$src" ]] || continue + name="$(basename "$src")" + cp "$src" "$CLAUDE_DIR/llm-functions/bin/$name" + chmod +x "$CLAUDE_DIR/llm-functions/bin/$name" + done + echo " overrides locaux appliqués" +fi echo "OK" echo "" diff --git a/mcp/qwen3-mcp/server.py b/mcp/qwen3-mcp/server.py index 2c7492e..75d3423 100644 --- a/mcp/qwen3-mcp/server.py +++ b/mcp/qwen3-mcp/server.py @@ -1,6 +1,6 @@ #!/usr/bin/env -S venv/bin/python3 """ -MCP server stdio exposant un outil `qwen3_task`. +MCP server stdio exposant un outil `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 @@ -28,20 +28,28 @@ _FUNCTIONS_JSON = _LLM_FUNCTIONS_DIR / "functions.json" _BIN_DIR = _LLM_FUNCTIONS_DIR / "bin" +_qwen3_tools: list[dict] | None = None + + def _load_qwen3_tools() -> list[dict]: - """Charge les outils depuis llm-functions/functions.json et les wrappe au format OpenAI.""" + """Charge les outils depuis llm-functions/functions.json (mise en cache au premier appel).""" + global _qwen3_tools + if _qwen3_tools is not None: + return _qwen3_tools if not _FUNCTIONS_JSON.exists(): print(f"WARN: {_FUNCTIONS_JSON} introuvable — aucun outil disponible", file=sys.stderr, flush=True) - return [] + _qwen3_tools = [] + return _qwen3_tools raw = json.loads(_FUNCTIONS_JSON.read_text()) - return [{"type": "function", "function": tool} for tool in raw] + _qwen3_tools = [{"type": "function", "function": tool} for tool in raw] + return _qwen3_tools # Outil MCP exposé à Claude Code MCP_TOOLS = [ { - "name": "qwen3_task", + "name": "task", "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", @@ -159,7 +167,18 @@ def call_qwen3(task: str, files: list[str]) -> str: if context: user_content += f"\n\nFICHIERS:\n{context}" - messages = [{"role": "user", "content": user_content}] + system = ( + "Tu es un assistant de développement logiciel. " + "Pour toute tâche impliquant la création ou modification de fichiers, " + "utilise TOUJOURS les outils fs_write ou fs_patch — ne jamais afficher " + "le contenu dans ta réponse. " + "Utilise execute_command pour vérifier le résultat si nécessaire. " + "Réponds en français, de façon concise." + ) + messages = [ + {"role": "system", "content": system}, + {"role": "user", "content": user_content}, + ] payload: dict = { "model": QWEN3_MODEL, "messages": messages, @@ -225,7 +244,7 @@ def handle(req: dict): params = req.get("params", {}) name = params.get("name", "") args = params.get("arguments", {}) - if name == "qwen3_task": + if name == "task": try: text = call_qwen3(args["task"], args.get("files", [])) send({"jsonrpc": "2.0", "id": req_id, "result": { diff --git a/skills/delegation-rules.md b/skills/delegation-rules.md index 94cb392..a368ffa 100644 --- a/skills/delegation-rules.md +++ b/skills/delegation-rules.md @@ -1,13 +1,14 @@ --- name: delegation-rules -description: Règles pour décider quand et comment déléguer à qwen3-worker via le MCP qwen3_task +description: Règles pour décider quand et comment déléguer à Qwen3 via mcp__qwen3__task --- # Règles de Délégation à Qwen3 -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. +Qwen3-Coder opère via l'outil MCP `mcp__qwen3__task`, appelé **directement** — jamais +via un sous-agent (Agent tool). 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) @@ -29,8 +30,10 @@ faire des recherches web et exécuter du code de façon autonome. ## Procédure 1. Évalue les critères ci-dessus. -2. Si éligible → utilise le subagent `qwen3-worker` avec une description précise de la tâche. +2. Si éligible → appelle directement `mcp__qwen3__task` avec une description précise de la tâche. Ne pas pré-charger les fichiers : Qwen3 les lit lui-même si nécessaire. + Pour toute création/modification de fichier, préciser dans le prompt : + "Écris le fichier via fs_write, ne pas afficher le contenu dans ta réponse." 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. @@ -40,4 +43,4 @@ faire des recherches web et exécuter du code de façon autonome. - Refactoring impliquant une vision cross-module - Changement d'architecture ou de design pattern - Debugging avec stacktrace à analyser sur plusieurs fichiers -- Tâches nécessitant le contexte de la conversation en cours +- Tâches nécessitant le contexte de la conversation en cours \ No newline at end of file diff --git a/skills/test_qwen3.md b/skills/test_qwen3.md new file mode 100644 index 0000000..bd3b681 --- /dev/null +++ b/skills/test_qwen3.md @@ -0,0 +1,46 @@ +--- +name: test-qwen3 +description: Évalue Qwen3 en lui déléguant des tâches via mcp__qwen3__task et en vérifiant les fichiers produits. +--- + +Exécute les tests suivants dans l'ordre. Pour chaque test : +1. Appelle `mcp__qwen3__task` avec la tâche +2. Vérifie que le fichier attendu existe dans `test_qwen3_output/` +3. Vérifie que le contenu est propre (pas de balises markdown résiduelles, pas de sérialisation JSON) +4. Valide syntaxiquement si applicable +5. Affiche ✓ PASS ou ✗ FAIL avec la raison + +Répertoire de sortie : `test_qwen3_output/` à la racine du projet (le créer si absent). + +## Tests + +**T1 — Python basique** +Tâche : "Crée le fichier test_qwen3_output/hello.py qui affiche Hello, World!" +Vérif : fichier existe, contient `print`, `python3` produit "Hello, World!", pas de balises ``` dans le fichier + +**T2 — Bash boucle** +Tâche : "Crée le fichier test_qwen3_output/count.sh, script bash qui affiche les nombres de 1 à 5 un par ligne" +Vérif : fichier existe, `bash -n` passe, exécution produit "5" en dernière ligne, pas de balises ``` + +**T3 — Python récursion** +Tâche : "Crée le fichier test_qwen3_output/fibonacci.py avec une fonction fib(n) et à la fin print(fib(10))" +Vérif : fichier existe, `python3` retourne 55, pas de balises ``` + +**T4 — Markdown titre** +Tâche : "Crée le fichier test_qwen3_output/intro.md avec un titre H1 CrazyClaude et un paragraphe de présentation" +Vérif : fichier existe, commence par `#`, plus de 50 caractères, pas de balises ``` englobantes + +**T5 — Markdown documentation** +Tâche : "Crée le fichier test_qwen3_output/api.md documentant la fonction process(data, options) avec les sections Description, Paramètres, Retour, Exemple" +Vérif : 4 sections présentes, contenu structuré, pas de balises ``` + +**T6 — Markdown changelog** +Tâche : "Crée le fichier test_qwen3_output/changelog.md avec 3 entrées au format ## [version] - date avec sous-sections Added/Fixed/Changed" +Vérif : au moins 3 entrées `## [`, sous-sections présentes + +## Résumé final + +Affiche : +- Le score N/6 +- Pour les FAIL : contenu du fichier produit (ou absence) pour diagnostic +- Évaluation qualitative : ce que Qwen3 fait bien/mal (utilisation des outils, qualité du contenu, propreté des fichiers) diff --git a/test_qwen3.sh b/test_qwen3.sh new file mode 100755 index 0000000..841d283 --- /dev/null +++ b/test_qwen3.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash +# Test end-to-end de Qwen3 — tâches de difficulté croissante +set -uo pipefail + +TESTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/test_qwen3_output" +mkdir -p "$TESTDIR" +pass=0 +fail=0 + +check() { + local n="$1" desc="$2" cond="$3" reason="$4" + if eval "$cond" 2>/dev/null; then + echo " ✓ PASS" + pass=$((pass + 1)) + else + echo " ✗ FAIL: $reason" + fail=$((fail + 1)) + fi +} + +run() { + local n="$1" desc="$2" file="$3" prompt="$4" + echo "[TEST $n] $desc" + content="$(aichat --no-stream "$prompt" 2>/dev/null | sed 's/^```[a-z]*$//' | sed '/^```$/d')" || true + printf '%s\n' "$content" > "$file" +} + +# --- Programmation --- + +run 1 "hello.py — Python basique" "$TESTDIR/hello.py" \ + "Génère uniquement le contenu d'un fichier Python qui affiche 'Hello, World!' avec print. Sans balises markdown, sans explication." +check 1 "hello.py" \ + "[[ -f '$TESTDIR/hello.py' ]] && grep -q 'print' '$TESTDIR/hello.py'" \ + "fichier absent ou pas de print" + +run 2 "count.sh — boucle bash 1 à 5" "$TESTDIR/count.sh" \ + "Génère uniquement le contenu d'un script bash qui affiche les nombres de 1 à 5 un par ligne avec une boucle for. Sans balises markdown, sans explication." +chmod +x "$TESTDIR/count.sh" 2>/dev/null || true +check 2 "count.sh" \ + "[[ -f '$TESTDIR/count.sh' ]] && bash '$TESTDIR/count.sh' | grep -q '5'" \ + "fichier absent ou ne produit pas '5'" + +run 3 "fibonacci.py — récursion" "$TESTDIR/fibonacci.py" \ + "Génère uniquement le contenu d'un fichier Python avec une fonction fib(n) (fib(1)=1, fib(2)=1) et à la fin print(fib(10)). Sans balises markdown, sans explication." +check 3 "fibonacci.py" \ + "[[ -f '$TESTDIR/fibonacci.py' ]] && [[ \"\$(python3 '$TESTDIR/fibonacci.py' 2>/dev/null)\" == '55' ]]" \ + "fichier absent ou fib(10) != 55" + +# --- Markdown --- + +run 4 "intro.md — titre + paragraphe" "$TESTDIR/intro.md" \ + "Génère uniquement le contenu markdown d'un fichier avec un titre H1 'CrazyClaude' et un paragraphe de présentation d'au moins 50 caractères. Sans balises de code." +check 4 "intro.md" \ + "[[ -f '$TESTDIR/intro.md' ]] && head -1 '$TESTDIR/intro.md' | grep -q '^#' && [[ \$(wc -c < '$TESTDIR/intro.md') -gt 50 ]]" \ + "fichier absent, pas de H1 ou trop court" + +run 5 "api.md — documentation de fonction" "$TESTDIR/api.md" \ + "Génère uniquement le contenu markdown documentant la fonction process(data, options) avec ces 4 sections : Description, Paramètres, Retour, Exemple. Sans balises de code." +check 5 "api.md" \ + "[[ -f '$TESTDIR/api.md' ]] && grep -qi 'description' '$TESTDIR/api.md' && grep -qi 'param' '$TESTDIR/api.md' && grep -qi 'retour\|return' '$TESTDIR/api.md' && grep -qi 'exemple\|example' '$TESTDIR/api.md'" \ + "fichier absent ou sections manquantes" + +run 6 "changelog.md — format semver avec 3 entrées" "$TESTDIR/changelog.md" \ + "Génère uniquement le contenu markdown d'un changelog avec 3 entrées au format '## [1.x.0] - YYYY-MM-DD', chacune avec au moins une sous-section ### Added, ### Fixed ou ### Changed. Sans balises de code." +check 6 "changelog.md" \ + "[[ -f '$TESTDIR/changelog.md' ]] && [[ \$(grep -c '## \[' '$TESTDIR/changelog.md') -ge 3 ]]" \ + "fichier absent ou moins de 3 entrées versionnées" + +# --- Résumé --- +echo "" +echo "Sorties conservées dans $TESTDIR" +echo "=== Résultats : $pass/6 tests passés ===" +[[ $fail -eq 0 ]] diff --git a/tools-local/README.md b/tools-local/README.md new file mode 100644 index 0000000..e696457 --- /dev/null +++ b/tools-local/README.md @@ -0,0 +1,9 @@ +# tools-local + +Overrides locaux pour les outils llm-functions. + +Les fichiers ici sont copiés dans `llm-functions/bin/` après chaque `argc build`, +écrasant les versions upstream si elles ont le même nom (sans extension). + +Utilisation : placer un script exécutable portant le même nom que le binaire à remplacer. +Exemple : `run-tool.sh` remplacera `llm-functions/bin/run-tool.sh` après le build. diff --git a/tools-local/run-tool.sh b/tools-local/run-tool.sh new file mode 100755 index 0000000..7b1f477 --- /dev/null +++ b/tools-local/run-tool.sh @@ -0,0 +1,119 @@ +#!/usr/bin/env bash + +# Usage: ./run-tool.sh + +set -e + +main() { + root_dir="$(cd -- "$( dirname -- "${BASH_SOURCE[0]}" )/.." &> /dev/null && pwd)" + self_name=run-tool.sh + parse_argv "$@" + setup_env + tool_path="$root_dir/tools/$tool_name.sh" + run +} + +parse_argv() { + if [[ "$0" == *"$self_name" ]]; then + tool_name="$1" + tool_data="$2" + else + tool_name="$(basename "$0")" + tool_data="$1" + fi + if [[ "$tool_name" == *.sh ]]; then + tool_name="${tool_name:0:$((${#tool_name}-3))}" + fi + if [[ -z "$tool_data" ]] || [[ -z "$tool_name" ]]; then + die "usage: ./run-tool.sh " + fi +} + +setup_env() { + load_env "$root_dir/.env" + export LLM_ROOT_DIR="$root_dir" + export LLM_TOOL_NAME="$tool_name" + export LLM_TOOL_CACHE_DIR="$LLM_ROOT_DIR/cache/$tool_name" +} + +load_env() { + local env_file="$1" env_vars + if [[ -f "$env_file" ]]; then + while IFS='=' read -r key value; do + if [[ "$key" == $'#'* ]] || [[ -z "$key" ]]; then + continue + fi + if [[ -z "${!key+x}" ]]; then + env_vars="$env_vars $key=$value" + fi + done < <(cat "$env_file"; echo "") + if [[ -n "$env_vars" ]]; then + eval "export $env_vars" + fi + fi +} + +run() { + if [[ -z "$tool_data" ]]; then + die "error: no JSON data" + fi + + if [[ "$OS" == "Windows_NT" ]]; then + set -o igncr + tool_path="$(cygpath -w "$tool_path")" + tool_data="$(echo "$tool_data" | sed 's/\\/\\\\/g')" + fi + + jq_script="$(cat <<-'EOF' +def escape_shell_word: + if type == "string" then . else tostring end + | gsub("'"; "'\"'\"'") + | gsub("\n"; "'$'\\n''") + | "'\(.)'"; +def to_args: + to_entries | .[] | + (.key | split("_") | join("-")) as $key | + if .value | type == "array" then + .value | .[] | "--\($key) \(. | escape_shell_word)" + elif .value | type == "boolean" then + if .value then "--\($key)" else "" end + else + "--\($key) \(.value | escape_shell_word)" + end; +[ to_args ] | join(" ") +EOF +)" + args="$(echo "$tool_data" | jq -r "$jq_script" 2>/dev/null)" || { + die "error: invalid JSON data" + } + if [[ -z "$LLM_OUTPUT" ]]; then + is_temp_llm_output=1 + export LLM_OUTPUT="$(mktemp)" + fi + eval "'$tool_path' $args" + if [[ "$is_temp_llm_output" -eq 1 ]]; then + cat "$LLM_OUTPUT" + else + dump_result "$tool_name" + fi +} + +dump_result() { + if [[ "$LLM_OUTPUT" == "/dev/stdout" ]] || [[ -z "$LLM_DUMP_RESULTS" ]] || [[ ! -t 1 ]]; then + return; + fi + if grep -q -w -E "$LLM_DUMP_RESULTS" <<<"$1"; then + cat <&2 + exit 1 +} + +main "$@" diff --git a/update_crazyclaude.sh b/update_crazyclaude.sh index 768b13d..4e31e39 100755 --- a/update_crazyclaude.sh +++ b/update_crazyclaude.sh @@ -9,6 +9,20 @@ set -euo pipefail CLAUDE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" LLM_DIR="$CLAUDE_DIR/llm-functions" +# Bootstrap : si on lance depuis le repo CrazyClaude lui-même (pas depuis .claude/) +# on recopie les fichiers sources vers .claude/ puis on rebuild les outils +if [[ "$(basename "$CLAUDE_DIR")" != ".claude" ]]; then + TARGET="${1:-$(pwd)/.claude}" + echo "==> Bootstrap : synchronisation de $CLAUDE_DIR → $TARGET" + rsync -a --exclude='.git' --exclude='llm-functions' --exclude='venv' \ + "$CLAUDE_DIR/" "$TARGET/" + echo "==> Rebuild des outils" + exec bash "$TARGET/update_crazyclaude.sh" "${@:2}" +fi + +# On est maintenant dans .claude/, on peut continuer normalement + + dry_run=0 [[ "${1:-}" == "--dry-run" ]] && dry_run=1 @@ -45,4 +59,22 @@ else fi echo "" -echo "OK — outils reconstruits dans $LLM_DIR/bin/" +echo "=== Application des overrides locaux ===" +LOCAL_TOOLS_DIR="$CLAUDE_DIR/tools-local" +if [[ -d "$LOCAL_TOOLS_DIR" ]]; then + count=0 + for src in "$LOCAL_TOOLS_DIR"/*; do + [[ -f "$src" ]] || continue + name="$(basename "$src")" + dest="$CLAUDE_DIR/llm-functions/bin/$name" + cp "$src" "$dest" + chmod +x "$dest" + count=$((count + 1)) + done + echo " $count override(s) appliqué(s)" +else + echo " (aucun répertoire tools-local/)" +fi + +echo "" +echo "OK — outils reconstruits dans $LLM_DIR/bin/" \ No newline at end of file