Merge pull request 'main' (#2) from main into push-rxysllxsymtz
Reviewed-on: #2
This commit was merged in pull request #2.
This commit is contained in:
@@ -1,22 +1,45 @@
|
||||
# Projet CrazyClaude
|
||||
## Instructions pour Claude
|
||||
|
||||
### Profil utilisateur
|
||||
Programmeur expérimenté. Connaît parfaitement son projet, ses outils, et son environnement.
|
||||
|
||||
### Comportement attendu
|
||||
- Faire ce qui est demandé, directement.
|
||||
- Ne pas expliquer des choses triviales ou évidentes.
|
||||
- Ne pas répéter ce qui vient d'être dit.
|
||||
- Faire confiance au jugement de l'utilisateur.
|
||||
- Si quelque chose ne fonctionne pas, chercher le bug — ne pas réexpliquer comment utiliser l'outil.
|
||||
- Avant de demander la validation d'un plan, toujours afficher la totalité du plan dans le chat. L'utilisateur est responsable du code et décide en toute connaissance de cause.
|
||||
|
||||
## Stack
|
||||
- Rust (edition 2024), Go 1.24
|
||||
- Tests : cargo test, go test
|
||||
- Lint : clippy (Rust), golangci-lint (Go)
|
||||
- Python 3.12, R 4.4, bash (scripts shell)
|
||||
- Tests :
|
||||
- Rust : `cargo test`
|
||||
- Go : `go test`
|
||||
- Python : `pytest` (avec coverage)
|
||||
- R : `testthat` (via `Rscript -e "testthat::test_dir('tests')"`)
|
||||
- bash : `bats` (Bash Automated Testing System)
|
||||
- Lint :
|
||||
- Rust : `clippy`
|
||||
- Go : `golangci-lint`
|
||||
- Python : `ruff` (ou `flake8` + `black`)
|
||||
- R : `lintr`
|
||||
- bash : `shellcheck`
|
||||
|
||||
## 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 d'API publique
|
||||
- Pas de modification d’API 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)
|
||||
|
||||
Traite toi-même si :
|
||||
- Refactoring cross-module
|
||||
- Conception d'architecture
|
||||
- Conception d’architecture
|
||||
- Debugging avec contexte multi-fichiers
|
||||
- Modification de traits/interfaces publics
|
||||
|
||||
@@ -24,3 +47,4 @@ Traite toi-même si :
|
||||
- Ne jamais passer `rm -rf` sans confirmation explicite
|
||||
- Ne jamais committer sans que les tests passent
|
||||
- Toujours vérifier `git diff` avant un 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
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
Agent loop local (~120 lignes) pour interagir avec LM Studio via l'API OpenAI-compatible.
|
||||
Usage : python3 .claude/mcp/qwen3-mcp/agent_lm.py --task "..." --files file1.py file2.go
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
@@ -11,8 +12,10 @@ from pathlib import Path
|
||||
|
||||
import requests
|
||||
|
||||
LMSTUDIO_URL = "http://localhost:1248/v1/chat/completions"
|
||||
LMSTUDIO_MODELS_URL = "http://localhost:1248/v1/models"
|
||||
SERVER = "http://localhost"
|
||||
PORT = 8888 # 1248
|
||||
LMSTUDIO_URL = f"{SERVER}:{PORT}/v1/chat/completions"
|
||||
LMSTUDIO_MODELS_URL = f"{SERVER}:{PORT}/v1/models"
|
||||
DEFAULT_MODEL = "qwen/qwen3-coder-next"
|
||||
|
||||
SYSTEM_PROMPT = """Tu es un assistant de codage spécialisé. Tu effectues des tâches atomiques sur des fichiers source.
|
||||
@@ -49,7 +52,11 @@ def read_files(paths: list[str]) -> str:
|
||||
|
||||
def call_lmstudio(task: str, file_context: str, model: str) -> str:
|
||||
"""Envoie la tâche à LM Studio et retourne la réponse."""
|
||||
user_message = f"TÂCHE: {task}\n\nFICHIERS:\n{file_context}" if file_context else f"TÂCHE: {task}"
|
||||
user_message = (
|
||||
f"TÂCHE: {task}\n\nFICHIERS:\n{file_context}"
|
||||
if file_context
|
||||
else f"TÂCHE: {task}"
|
||||
)
|
||||
|
||||
payload = {
|
||||
"model": model,
|
||||
@@ -90,13 +97,14 @@ def run_lint(file_path: str) -> tuple[bool, str]:
|
||||
|
||||
if file_path.endswith(".rs"):
|
||||
result = subprocess.run(
|
||||
["cargo", "clippy", "--quiet"],
|
||||
capture_output=True, text=True, timeout=60
|
||||
["cargo", "clippy", "--quiet"], capture_output=True, text=True, timeout=60
|
||||
)
|
||||
elif file_path.endswith(".go"):
|
||||
result = subprocess.run(
|
||||
["golangci-lint", "run", file_path],
|
||||
capture_output=True, text=True, timeout=60
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=60,
|
||||
)
|
||||
else:
|
||||
return True, "no linter for this file type"
|
||||
@@ -106,10 +114,21 @@ def run_lint(file_path: str) -> tuple[bool, str]:
|
||||
return passed, output
|
||||
|
||||
|
||||
def agent_loop(task: str, input_files: list[str], output_file: str | None, model: str, max_retries: int = 2) -> dict:
|
||||
def agent_loop(
|
||||
task: str,
|
||||
input_files: list[str],
|
||||
output_file: str | None,
|
||||
model: str,
|
||||
max_retries: int = 2,
|
||||
) -> dict:
|
||||
"""Boucle principale : génère, écrit, lint, corrige (max_retries fois)."""
|
||||
file_context = read_files(input_files) if input_files else ""
|
||||
result = {"status": "failure", "files_modified": [], "summary": "", "lint": "skipped"}
|
||||
result = {
|
||||
"status": "failure",
|
||||
"files_modified": [],
|
||||
"summary": "",
|
||||
"lint": "skipped",
|
||||
}
|
||||
|
||||
for attempt in range(max_retries + 1):
|
||||
response = call_lmstudio(task, file_context, model)
|
||||
@@ -133,7 +152,9 @@ def agent_loop(task: str, input_files: list[str], output_file: str | None, model
|
||||
|
||||
if lint_ok:
|
||||
result["status"] = "success"
|
||||
result["summary"] = f"Attempt {attempt + 1}: task completed successfully"
|
||||
result["summary"] = (
|
||||
f"Attempt {attempt + 1}: task completed successfully"
|
||||
)
|
||||
break
|
||||
elif attempt < max_retries:
|
||||
task = f"{task}\n\nCORRECTION REQUISE (tentative {attempt + 1}):\n{lint_output}"
|
||||
@@ -149,10 +170,16 @@ def main():
|
||||
parser = argparse.ArgumentParser(description="Agent loop local pour LM Studio")
|
||||
parser.add_argument("--task", required=True, help="Description de la tâche")
|
||||
parser.add_argument("--files", nargs="*", default=[], help="Fichiers source à lire")
|
||||
parser.add_argument("--output", help="Fichier de sortie (défaut: premier fichier input)")
|
||||
parser.add_argument(
|
||||
"--output", help="Fichier de sortie (défaut: premier fichier input)"
|
||||
)
|
||||
parser.add_argument("--model", default=DEFAULT_MODEL, help="Modèle LM Studio")
|
||||
parser.add_argument("--list-models", action="store_true", help="Liste les modèles disponibles")
|
||||
parser.add_argument("--json", action="store_true", dest="json_output", help="Sortie JSON")
|
||||
parser.add_argument(
|
||||
"--list-models", action="store_true", help="Liste les modèles disponibles"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--json", action="store_true", dest="json_output", help="Sortie JSON"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.list_models:
|
||||
|
||||
@@ -3,25 +3,38 @@
|
||||
MCP server stdio exposant un outil `qwen3_task`.
|
||||
Appelé par Claude Code via : claude mcp add --transport stdio qwen3 -- .claude/venv/bin/python3 .claude/mcp/qwen3-mcp/server.py
|
||||
"""
|
||||
import sys
|
||||
|
||||
import json
|
||||
import sys
|
||||
|
||||
import requests
|
||||
|
||||
LMSTUDIO_URL = "http://localhost:1248/v1/chat/completions"
|
||||
SERVER = "http://localhost"
|
||||
PORT = 8888 # 1248
|
||||
LMSTUDIO_URL = f"{SERVER}:{PORT}/v1/chat/completions"
|
||||
QWEN3_MODEL = "qwen/qwen3-coder-next"
|
||||
|
||||
TOOLS = [{
|
||||
"name": "qwen3_task",
|
||||
"description": "Délègue une tâche de codage atomique à Qwen3-Coder via LM Studio.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"task": {"type": "string", "description": "Description précise de la tâche"},
|
||||
"files": {"type": "array", "items": {"type": "string"}, "description": "Chemins des fichiers concernés"}
|
||||
TOOLS = [
|
||||
{
|
||||
"name": "qwen3_task",
|
||||
"description": "Délègue une tâche de codage atomique à Qwen3-Coder via LM Studio.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"task": {
|
||||
"type": "string",
|
||||
"description": "Description précise de la tâche",
|
||||
},
|
||||
"files": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
"description": "Chemins des fichiers concernés",
|
||||
},
|
||||
},
|
||||
"required": ["task"],
|
||||
},
|
||||
"required": ["task"]
|
||||
}
|
||||
}]
|
||||
]
|
||||
|
||||
|
||||
def send(obj: dict):
|
||||
@@ -65,15 +78,17 @@ 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": "1.0.0"},
|
||||
},
|
||||
}
|
||||
)
|
||||
elif method == "notifications/initialized":
|
||||
pass # notification, pas de réponse
|
||||
elif method == "tools/list":
|
||||
@@ -85,13 +100,40 @@ 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}"},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
for line in sys.stdin:
|
||||
|
||||
90
AGENTS.md
Normal file
90
AGENTS.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# AGENTS.md
|
||||
|
||||
This file documents the project's coding standards, build/test/lint commands, and operational guidelines for agentic AI assistants.
|
||||
|
||||
## Build, Lint, and Test Commands
|
||||
|
||||
- **Rust**: `cargo build --all-targets && cargo test --all-targets`
|
||||
- **Go**: `go build ./... && go vet ./... && go test ./...`
|
||||
- **Python**: `python -m pytest tests/` (single test: `python -m pytest tests/test_module.py::test_function`)
|
||||
- **R**: `Rscript -e "devtools::test()"` (single test: `Rscript -e "testthat::test_file('tests/testthat/test_module.R')"`)
|
||||
- **Bash**: Run tests directly or use `bash -n script.sh` for syntax check
|
||||
|
||||
For all languages: run linters before committing (see hooks in `.claude/hooks/`)
|
||||
|
||||
## Code Style Guidelines
|
||||
|
||||
### General Principles
|
||||
- Follow language-specific idioms and conventions
|
||||
- Prioritize readability over cleverness
|
||||
- Use descriptive names for variables, functions, and types
|
||||
- Keep functions small and focused on a single responsibility
|
||||
|
||||
### Imports and Dependencies
|
||||
- **Rust**: Use `use` statements at top of file; group std, external crates, and local modules
|
||||
- **Go**: Import blocks organized: standard library, then external packages, then local packages
|
||||
- **Python**: Standard library imports first, then third-party, then local imports
|
||||
- **R**: Attach packages with `library()` at top of script; use `pkg::function()` for occasional calls
|
||||
- **Bash**: Source local modules with absolute paths or relative from script location
|
||||
|
||||
### Naming Conventions
|
||||
- **Rust**: Types/CamelCase, functions/snake_case, constants/SCREAMING_SNAKE_CASE
|
||||
- **Go**: Exported names start with uppercase, unexported with lowercase; use CamelCase for acronyms
|
||||
- **Python**: Classes/CamelCase, functions/variables/snake_case; use _private for internal
|
||||
- **R**: Functions/lowercase_with_underscores; avoid naming conflicts with base R functions
|
||||
- **Bash**: Functions/snake_case; variables lowercase; constants UPPERCASE
|
||||
|
||||
### Error Handling
|
||||
- **Rust**: Use `Result<T, E>` and `?` operator; define custom error types for libraries
|
||||
- **Go**: Return `(result, error)` tuple; handle errors immediately after function call
|
||||
- **Python**: Raise specific exception types; catch only what you can handle meaningfully
|
||||
- **R**: Use `stop()` for errors, `warning()` for warnings; consider tryCatch for recovery
|
||||
- **Bash**: Check return codes with `$?`; use `set -e` for strict error handling
|
||||
|
||||
### Types and Documentation
|
||||
- **Rust**: Specify all types explicitly; use `cargo doc` for documentation
|
||||
- **Go**: Document exported functions with comments; use type aliases for clarity
|
||||
- **Python**: Use type hints (PEP 484); docstrings with Google or NumPy style
|
||||
- **R**: Use roxygen2 for function documentation; document all exported functions
|
||||
- **Bash**: Comment complex logic; use `set -x` for debugging trace
|
||||
|
||||
## Cursor Rules
|
||||
|
||||
- Run linters after any file write (see `.claude/hooks/post-write-lint.sh`)
|
||||
- For Rust/Go projects, run `cargo clippy` or `go vet` before committing
|
||||
- Verify tests pass after changes: `cargo test`, `go test`, `pytest`
|
||||
- When modifying multiple files, run full test suite before finalizing
|
||||
|
||||
## Copilot Rules
|
||||
|
||||
- Delegation: Use Qwen3-Coder for atomic tasks via MCP server at port 1248
|
||||
- Code review: Invoke code-reviewer agent for PRs and significant changes
|
||||
- Planning: Use task-planner to break complex tasks into atomic steps
|
||||
- Always verify agent outputs before committing or pushing
|
||||
|
||||
## Hooks and Automation
|
||||
|
||||
- **post-write-lint.sh**: Runs linters after file writes
|
||||
- **subagent-stop-log.sh**: Logs subagent completion and exit codes
|
||||
- **pre-bash-guard.sh**: Validates bash scripts before execution
|
||||
|
||||
## MCP Server Configuration
|
||||
|
||||
- **Endpoint**: `http://localhost:1248`
|
||||
- **Model**: `qwen/qwen3-coder-next`
|
||||
- **Local agent loop**: `.claude/mcp/qwen3-mcp/agent_lm.py`
|
||||
- **Server script**: `.claude/mcp/qwen3-mcp/server.py`
|
||||
|
||||
## Project Structure
|
||||
|
||||
- `.claude/agents/` - Agent definitions (qwen3-worker, code-reviewer, task-planner)
|
||||
- `.claude/skills/` - Domain-specific skills and conventions
|
||||
- `.claude/hooks/` - Git hooks for automated checks
|
||||
- `.claude/mcp/qwen3-mcp/` - MCP server and agent integration
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
- Run tests after any code change
|
||||
- For single test execution, use language-specific commands above
|
||||
- CI/CD should run full test suite on every push
|
||||
- Code coverage targets: 80% minimum for production code
|
||||
Reference in New Issue
Block a user