Merge pull request 'refactor: rename MCP tool from qwen3_task to task and update delegation flow' (#5) from push-wwlmzkkxwqtp into main

Reviewed-on: #5
This commit was merged in pull request #5.
This commit is contained in:
2026-03-27 17:32:45 +01:00
13 changed files with 361 additions and 44 deletions

2
.gitignore vendored
View File

@@ -1,7 +1,9 @@
venv/ venv/
logs/ logs/
test_qwen3_output/
llm-functions/bin/ llm-functions/bin/
llm-functions/cache/ llm-functions/cache/
__pycache__/ __pycache__/
*.pyc *.pyc
.claude .claude
.claude/

View File

@@ -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.** **Règle par défaut : délègue à Qwen3. Traite toi-même seulement si tu ne peux pas faire autrement.**
Qwen3 dispose doutils autonomes (filesystem, shell, web) via `qwen3_task`. Il lit et écrit Délègue en appelant **directement** loutil MCP `mcp__qwen3__task` — ne pas passer par
un sous-agent (Agent tool), car les sous-agents nont pas accès aux MCP. Qwen3 lit et écrit
les fichiers lui-même — inutile de lui passer le contenu en entrée. 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 : Délègue systématiquement pour :
- Créer ou modifier un fichier (code, config, doc, script) - Créer ou modifier un fichier (code, config, doc, script)
- Générer des tests, de la documentation, des stubs - Générer des tests, de la documentation, des stubs

View File

@@ -1,21 +1 @@
--- "---\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```"
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)
```

View File

@@ -1,25 +1,27 @@
--- ---
name: qwen3-worker name: qwen3-worker
description: > 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, 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 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 écrit les fichiers lui-même. N'utilise PAS pour du refactoring cross-module ou de
la conception d'architecture. la conception d'architecture.
tools: [mcp__qwen3__qwen3_task] tools: [mcp__qwen3__task]
model: inherit 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 ## Comportement
1. Formule une description précise de la tâche. 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. 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 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. 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 ## Outils disponibles pour Qwen3

View File

@@ -12,7 +12,7 @@ Tu es un architecte logiciel expérimenté en Rust et Go.
Pour la demande reçue : Pour la demande reçue :
1. Analyse le périmètre et les dépendances (lis les fichiers pertinents). 1. Analyse le périmètre et les dépendances (lis les fichiers pertinents).
2. Décompose en étapes atomiques ordonnées. 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. 4. Estime les risques et propose des points de contrôle.
Format de sortie : Format de sortie :

View File

@@ -7,6 +7,16 @@
set -euo pipefail set -euo pipefail
CLAUDE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 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")" PROJECT_DIR="$(dirname "$CLAUDE_DIR")"
require() { require() {
@@ -31,7 +41,25 @@ if [[ ! -f "$CLAUDE_DIR/llm-functions/Argcfile.sh" ]]; then
else else
echo "déjà présent" echo "déjà présent"
fi 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) (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 "OK"
echo "" echo ""

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env -S venv/bin/python3 #!/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. 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. 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 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" _BIN_DIR = _LLM_FUNCTIONS_DIR / "bin"
_qwen3_tools: list[dict] | None = None
def _load_qwen3_tools() -> list[dict]: 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(): if not _FUNCTIONS_JSON.exists():
print(f"WARN: {_FUNCTIONS_JSON} introuvable — aucun outil disponible", print(f"WARN: {_FUNCTIONS_JSON} introuvable — aucun outil disponible",
file=sys.stderr, flush=True) file=sys.stderr, flush=True)
return [] _qwen3_tools = []
return _qwen3_tools
raw = json.loads(_FUNCTIONS_JSON.read_text()) 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 # Outil MCP exposé à Claude Code
MCP_TOOLS = [ 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).", "description": "Délègue une tâche de codage à Qwen3-Coder via aichat. Qwen3 dispose d'outils filesystem, shell et web (llm-functions).",
"inputSchema": { "inputSchema": {
"type": "object", "type": "object",
@@ -159,7 +167,18 @@ def call_qwen3(task: str, files: list[str]) -> str:
if context: if context:
user_content += f"\n\nFICHIERS:\n{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 = { payload: dict = {
"model": QWEN3_MODEL, "model": QWEN3_MODEL,
"messages": messages, "messages": messages,
@@ -225,7 +244,7 @@ def handle(req: dict):
params = req.get("params", {}) params = req.get("params", {})
name = params.get("name", "") name = params.get("name", "")
args = params.get("arguments", {}) args = params.get("arguments", {})
if name == "qwen3_task": if name == "task":
try: try:
text = call_qwen3(args["task"], args.get("files", [])) text = call_qwen3(args["task"], args.get("files", []))
send({"jsonrpc": "2.0", "id": req_id, "result": { send({"jsonrpc": "2.0", "id": req_id, "result": {

View File

@@ -1,13 +1,14 @@
--- ---
name: delegation-rules 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 # Règles de Délégation à Qwen3
Qwen3-Coder opère via le MCP tool `qwen3_task`. Il dispose d'outils filesystem, shell Qwen3-Coder opère via l'outil MCP `mcp__qwen3__task`, appelé **directement** — jamais
et web (llm-functions) — il peut lire/écrire des fichiers, exécuter des commandes, via un sous-agent (Agent tool). Il dispose d'outils filesystem, shell et web (llm-functions)
faire des recherches web et exécuter du code de façon autonome. — 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) ## 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 ## Procédure
1. Évalue les critères ci-dessus. 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. 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). 3. Attends le résumé (STATUS / FILES_MODIFIED / SUMMARY).
4. Si `STATUS: failure` → prends en charge toi-même. 4. Si `STATUS: failure` → prends en charge toi-même.
5. Si `STATUS: partial` → corrige les points restants directement. 5. Si `STATUS: partial` → corrige les points restants directement.

46
skills/test_qwen3.md Normal file
View File

@@ -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)

73
test_qwen3.sh Executable file
View File

@@ -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 ]]

9
tools-local/README.md Normal file
View File

@@ -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.

119
tools-local/run-tool.sh Executable file
View File

@@ -0,0 +1,119 @@
#!/usr/bin/env bash
# Usage: ./run-tool.sh <tool-name> <tool-data>
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 <tool-name> <tool-data>"
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 <<EOF
$(echo -e "\e[2m")----------------------
$(cat "$LLM_OUTPUT")
----------------------$(echo -e "\e[0m")
EOF
fi
}
die() {
echo "$*" >&2
exit 1
}
main "$@"

View File

@@ -9,6 +9,20 @@ set -euo pipefail
CLAUDE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" CLAUDE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LLM_DIR="$CLAUDE_DIR/llm-functions" 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 dry_run=0
[[ "${1:-}" == "--dry-run" ]] && dry_run=1 [[ "${1:-}" == "--dry-run" ]] && dry_run=1
@@ -44,5 +58,23 @@ else
(cd "$LLM_DIR" && argc build) (cd "$LLM_DIR" && argc build)
fi fi
echo ""
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 ""
echo "OK — outils reconstruits dans $LLM_DIR/bin/" echo "OK — outils reconstruits dans $LLM_DIR/bin/"