Merge branch 'develop'

- fix todo for server without tk
- split claude.md into rules
- integrate rtk for optimisation agent IA
- todo to add command for claude
- todo i18n use english key
- more space when print command into execute.py
- todo integrate deploy local clone
- script format
This commit is contained in:
Mathieu Benoit 2026-03-14 23:26:50 -04:00
commit b1eeeb6053
22 changed files with 1387 additions and 785 deletions

View file

@ -0,0 +1,14 @@
# Versions supportées
| Odoo | Python | Poetry | Statut |
|-------|----------|--------|------------|
| 18.0 | 3.12.10 | 2.1.3 | **Défaut** |
| 17.0 | 3.10.18 | 1.8.3 | Actif |
| 16.0 | 3.10.18 | 1.8.3 | Actif |
| 15.0 | 3.8.20 | 1.8.3 | Déprécié |
| 14.0 | 3.8.20 | 1.5.0 | Déprécié |
| 13.0 | 3.7.17 | 1.5.0 | Déprécié |
| 12.0 | 3.7.17 | 1.5.0 | Déprécié |
Configuration dans `conf/supported_version_erplibre.json`.
Fichiers de version : `.odoo-version`, `.erplibre-version`, `.poetry-version`, `.python-odoo-version`.

View file

@ -0,0 +1,45 @@
# Structure du projet
```
erplibre/
├── Makefile # Orchestrateur principal (inclut conf/make.*.Makefile)
├── run.sh / odoo_bin.sh # Lanceurs Odoo (venv + PYTHONPATH)
├── env_var.sh # Variables d'environnement globales
├── conf/ # Configuration : Makefiles modulaires, versions, manifests CSV
│ ├── make.installation.Makefile
│ ├── make.test.Makefile
│ ├── make.database.Makefile
│ ├── make.docker.Makefile
│ ├── make.code_generator.Makefile
│ ├── make.installation.poetry.Makefile
│ └── supported_version_erplibre.json
├── manifest/ # Manifests Google Repo (XML) par version Odoo
│ └── git_manifest_odoo{12..18}.0.xml
├── requirement/ # Dépendances par version
│ ├── pyproject.odooXX.0_pythonY.Z.toml
│ ├── poetry.odooXX.0_pythonY.Z.lock
│ └── requirements.odooXX.0_pythonY.Z.txt
├── script/ # Scripts utilitaires (32+ catégories)
│ ├── todo/ # CLI interactif principal (todo.py)
│ ├── database/ # Opérations DB (restore, migrate, image_db)
│ ├── addons/ # Gestion des modules (install, update, uninstall)
│ ├── code_generator/ # Génération de modules Odoo
│ ├── version/ # Changement de version
│ ├── git/ # Opérations Git et Google Repo
│ ├── maintenance/ # Formatage (black, isort, prettier)
│ ├── test/ # Tests parallèles + coverage
│ ├── docker/ # Build/run Docker
│ ├── poetry/ # Gestion Poetry
│ ├── deployment/ # Déploiement production
│ └── selenium/ # Tests web automatisés
├── docker/ # Dockerfiles + docker-compose par version
├── addons/ # Répertoire des addons (géré par Google Repo)
│ ├── OCA_*/ # Modules OCA
│ ├── ERPLibre_*/ # Modules ERPLibre
│ ├── TechnoLibre_*/ # Code generator + templates
│ └── MathBenTech_*/ # Modules spécialisés
├── odoo{12..18}.0/ # Sources Odoo par version
├── doc/ # Documentation (DEVELOPMENT, PRODUCTION, MIGRATION, etc.)
├── test/ # Framework de test
└── private/ # Fichiers privés (non versionné)
```

View file

@ -0,0 +1,63 @@
# Commandes essentielles
## Changement de version Odoo
```bash
make switch_odoo_18 # Passer à Odoo 18
make switch_odoo_17 # Passer à Odoo 17
make switch_odoo_16 # Passer à Odoo 16 (défaut)
make switch_odoo_15 # Passer à Odoo 15
make switch_odoo_14 # Passer à Odoo 14
make switch_odoo_13 # Passer à Odoo 13
make switch_odoo_12 # Passer à Odoo 12
make switch_odoo_16_update # Passer + mettre à jour les dépendances
```
## Exécution
```bash
make run # Lancer Odoo (port 8069)
make run_test # Lancer avec la DB "test"
./run.sh -d ma_base # Lancer avec une DB spécifique
./odoo_bin.sh [args] # Exécuter odoo-bin directement
```
## Base de données
```bash
make db_create_db_test # Créer la DB "test"
make db_clone_test_to_test2 # Cloner test -> test2
./script/database/db_restore.py --database NOM --image IMAGE
./script/addons/install_addons_dev.sh DB module1,module2
./script/addons/update_addons_all.sh DB
```
## Tests
```bash
make test # Tests de base + format
make test_full_fast # Tests complets en parallèle
make test_full_fast_coverage # Avec couverture de code
```
## Formatage du code
```bash
make format # Fichiers à commiter uniquement
make format_all # Tout formater (parallèle)
```
## Docker
```bash
make docker_run_daemon # Lancer en production
make docker_build_odoo_18 # Builder l'image Odoo 18
```
## Google Repo (gestion multi-dépôts)
```bash
make repo_show_status # Statut de tous les dépôts addons
make repo_configure_all # Configurer tous les dépôts
make repo_do_stash # Stash tous les dépôts
.venv.erplibre/bin/repo sync # Synchroniser les dépôts
```
## Installation
```bash
make install_os # Dépendances système
make install_odoo_18 # Installer Odoo 18 complet
```

View file

@ -0,0 +1,24 @@
# Conventions de code
## Python
- Formateur : **Black** (profil par défaut, ligne max 79 pour les modules Odoo)
- Imports : **isort** avec profil `black`, longueur de ligne 79
- Linting : **Flake8** avec bugbear, max-line-length 80, max-complexity 16
- Ignorer : E203, E501, W503 (compatibilité Black)
## XML / JSON / YAML
- Formateur : **Prettier** (via npm)
- Indentation : 4 espaces (XML/CSS/JS), 2 espaces (JSON/YAML)
## Fichiers
- Encodage : UTF-8
- Fins de ligne : LF (Unix)
- Indentation : 4 espaces (Python, XML, CSS, JS), 2 espaces (JSON, YAML, RST, MD)
- Retour à la ligne final : oui
- Espaces en fin de ligne : supprimés
## Git
- Branches : `develop` (développement), `master` (production)
- Pas de submodules Git — utilise **Google Repo** pour les addons
- Manifests XML dans `manifest/` pour chaque version Odoo
- Format de commit : `[TYPE] description` (ex: `[FIX]`, `[UPD]`, `[ADD]`, `[REM]`)

View file

@ -0,0 +1,22 @@
# Architecture des environnements virtuels
```
.venv.erplibre/ # Venv ERPLibre (outils : repo, poetry, coverage)
.venv.odoo18/ # Venv Odoo 18 (Python 3.12)
.venv.odoo17/ # Venv Odoo 17 (Python 3.10)
.venv.odoo16/ # Venv Odoo 16 (Python 3.10)
.venv.odoo14/ # Venv Odoo 14 (Python 3.8)
.venv.odoo12/ # Venv Odoo 12 (Python 3.7)
```
Géré via **pyenv** pour les multiples versions de Python.
## Système de dépendances
Chaque version Odoo a son propre ensemble dans `requirement/` :
- `pyproject.odooXX.0_pythonY.Z.toml` — Configuration Poetry
- `poetry.odooXX.0_pythonY.Z.lock` — Lock file Poetry
- `requirements.odooXX.0_pythonY.Z.txt` — Requirements pip (fallback)
- `ignore_requirements.odooXX.0.txt` — Paquets à ignorer
Mise à jour : `./script/poetry/poetry_update.py`

View file

@ -0,0 +1,9 @@
# Code Generator
ERPLibre inclut un système de génération de modules Odoo :
- `script/code_generator/new_project.py` — Créer un nouveau module
- `script/code_generator/create_from_existing_module.py` — Cloner un module existant
- `addons/TechnoLibre_odoo-code-generator/` — Moteur de génération
- `addons/TechnoLibre_odoo-code-generator-template/` — Templates
Documentation : `doc/CODE_GENERATOR.md`

View file

@ -0,0 +1,55 @@
# Documentation multilingue
La documentation est bilingue (anglais/français) via **mmg** (Multilingual Markdown Generator).
## Fonctionnement
- Les fichiers sources sont les `.base.md` (contiennent les deux langues)
- `mmg` génère : `FICHIER.md` (anglais) et `FICHIER.fr.md` (français)
- Marqueurs : `<!-- [en] -->`, `<!-- [fr] -->`, `<!-- [common] -->` (blocs de code partagés)
## Commandes
```bash
make doc_markdown # Regénérer toute la doc multilingue
```
## Convention
- **Ne jamais modifier directement** les fichiers `.md` ou `.fr.md` générés
- Toujours modifier le fichier `.base.md` correspondant, puis exécuter `make doc_markdown`
- Les blocs de code vont dans `<!-- [common] -->`, le texte dans `<!-- [en] -->` et `<!-- [fr] -->`
- En-tête obligatoire dans chaque `.base.md` :
```
<!---------------------------->
<!-- multilingual suffix: en, fr -->
<!-- no suffix: en -->
<!---------------------------->
```
## Fichiers concernés (30 fichiers)
- Racine : `README`, `CHANGELOG`, `TODO`
- `doc/` : DEVELOPMENT, PRODUCTION, DISCOVER, RUN, MIGRATION, WINDOWS_INSTALLATION, FAQ, GIT_REPO, POETRY, RELEASE, UPDATE, CONTRIBUTION, HOWTO, TODO, CODE_GENERATOR
- `docker/` : README
- `script/*/` : database, deployment, fork_github_repo, nginx, restful, selenium (2), todo, odoo/migration
- `.github/ISSUE_TEMPLATE/` : bug_report, feature_request
## Internationalisation du CLI TODO (i18n)
Le CLI interactif `script/todo/todo.py` supporte le français et l'anglais.
### Architecture
- **`script/todo/todo_i18n.py`** — Module de traduction (dictionnaire `TRANSLATIONS`, fonctions `t()`, `get_lang()`, `set_lang()`)
- Les chaînes traduisibles utilisent `t("clé")` au lieu de texte en dur
- Les entrées de `todo.json` peuvent avoir un champ `prompt_description_key` résolu via `t()` (fallback sur `prompt_description`)
### Résolution de la langue (priorité)
1. Variable d'environnement `EL_LANG` (définie dans `env_var.sh`, défaut `"fr"`)
2. Défaut : `"fr"`
### Comportement
- Première exécution : prompt bilingue demande à l'utilisateur de choisir sa langue
- Le choix est persisté dans `env_var.sh`
- Changement de langue possible via le menu Execute > Langue/Language
### Ajouter une traduction
1. Ajouter la clé dans `TRANSLATIONS` de `todo_i18n.py` avec les valeurs `"fr"` et `"en"`
2. Remplacer la chaîne en dur par `t("ma_clé")` dans `todo.py`
3. Pour les entrées JSON : ajouter `"prompt_description_key": "ma_clé"` dans `todo.json`

View file

@ -0,0 +1,9 @@
# Déploiement
- **Docker** : `docker-compose.yml` (PostgreSQL 18 + PostGIS 3.6)
- **Systemd** : `script/systemd/` pour les services
- **Nginx** : `script/nginx/` pour le reverse proxy
- **SSL** : Certbot pour les certificats
- **DNS** : `script/deployment/update_dns_cloudflare.py`
Plateformes supportées : Ubuntu 20.04-25.04, Linux Mint 22.3, Debian 12, Arch Linux, macOS (pyenv), Windows (WSL/Docker).

View file

@ -0,0 +1,49 @@
# Workflow Orchestration
## 1. Plan Mode Default
- Enter plan mode for ANY non-trivial task (3+ steps or architectural decisions)
- If something goes sideways, STOP and re-plan immediately
- Don't keep pushing.
- Use plan mode for verification steps, not just building
- Write detailed specs upfront to reduce ambiguity
## 2. Subagent Strategy
- Use subagents liberally to keep the main context window clean.
- Offload research, exploration, and parallel analysis to subagents
- For complex problems, throw more compute at it via subagents
- One task per subagent for focused execution
## 3. Self-Improvement Loop
- Capture Lessons: Update AGENT.md with any change in approach the user has asked you to make.
- Write rules for yourself that prevent the same mistake
- Ruthlessly iterate on these lessons until the mistake rate drops.
- Review lessons at session start for relevant project
## 4. Verification Before Done
- Never mark a task complete without proving it works
- Diff behavior between main and your changes when relevant
- Ask yourself, "Would a staff engineer approve this?"
- Run tests, check logs, and demonstrate correctness.
## 5. Demand Elegance (Balanced)
- For non-trivial changes, pause and ask, "Is there a more elegant way?"
- If a fix feels hacky: "Knowing everything I know now, implement the elegant solution"
- Skip this for simple, obvious fixes
- Don't over-engineer.
- Challenge your own work before presenting it
## 6. Autonomous Bug Fixing
- When given a bug report, just fix it. Don't ask for hand-holding
- Point at logs, errors, and failing tests.
- Then resolve them.
- Zero context switching required from the user
- Go fix failing CI tests without being told how
## Task Management
1. **Plan First**: Write a plan to `tasks/todo.md` with checkable items.
2. **Verify Plan**: Check in before starting implementation.
3. **Track Progress**: Mark items complete as you go.
4. **Explain Changes**: High-level summary at each step.
5. **Document Results**: Add review section to `tasks/todo.md`
6. **Capture Lessons**: Update `tasks/lessons.md` after corrections

316
CLAUDE.md
View file

@ -6,256 +6,6 @@ ERPLibre est un fork communautaire d'Odoo Community Edition (OCE) supportant les
Version actuelle : **1.6.0** | Licence : **AGPL-3.0+**
Version Odoo par défaut : **18.0** (support officiel ERPLibre 1.6.0)
## Versions supportées
| Odoo | Python | Poetry | Statut |
|-------|----------|--------|------------|
| 18.0 | 3.12.10 | 2.1.3 | **Défaut** |
| 17.0 | 3.10.18 | 1.8.3 | Actif |
| 16.0 | 3.10.18 | 1.8.3 | Actif |
| 15.0 | 3.8.20 | 1.8.3 | Déprécié |
| 14.0 | 3.8.20 | 1.5.0 | Déprécié |
| 13.0 | 3.7.17 | 1.5.0 | Déprécié |
| 12.0 | 3.7.17 | 1.5.0 | Déprécié |
Configuration dans `conf/supported_version_erplibre.json`.
Fichiers de version : `.odoo-version`, `.erplibre-version`, `.poetry-version`, `.python-odoo-version`.
## Structure du projet
```
erplibre/
├── Makefile # Orchestrateur principal (inclut conf/make.*.Makefile)
├── run.sh / odoo_bin.sh # Lanceurs Odoo (venv + PYTHONPATH)
├── env_var.sh # Variables d'environnement globales
├── conf/ # Configuration : Makefiles modulaires, versions, manifests CSV
│ ├── make.installation.Makefile
│ ├── make.test.Makefile
│ ├── make.database.Makefile
│ ├── make.docker.Makefile
│ ├── make.code_generator.Makefile
│ ├── make.installation.poetry.Makefile
│ └── supported_version_erplibre.json
├── manifest/ # Manifests Google Repo (XML) par version Odoo
│ └── git_manifest_odoo{12..18}.0.xml
├── requirement/ # Dépendances par version
│ ├── pyproject.odooXX.0_pythonY.Z.toml
│ ├── poetry.odooXX.0_pythonY.Z.lock
│ └── requirements.odooXX.0_pythonY.Z.txt
├── script/ # Scripts utilitaires (32+ catégories)
│ ├── todo/ # CLI interactif principal (todo.py)
│ ├── database/ # Opérations DB (restore, migrate, image_db)
│ ├── addons/ # Gestion des modules (install, update, uninstall)
│ ├── code_generator/ # Génération de modules Odoo
│ ├── version/ # Changement de version
│ ├── git/ # Opérations Git et Google Repo
│ ├── maintenance/ # Formatage (black, isort, prettier)
│ ├── test/ # Tests parallèles + coverage
│ ├── docker/ # Build/run Docker
│ ├── poetry/ # Gestion Poetry
│ ├── deployment/ # Déploiement production
│ └── selenium/ # Tests web automatisés
├── docker/ # Dockerfiles + docker-compose par version
├── addons/ # Répertoire des addons (géré par Google Repo)
│ ├── OCA_*/ # Modules OCA
│ ├── ERPLibre_*/ # Modules ERPLibre
│ ├── TechnoLibre_*/ # Code generator + templates
│ └── MathBenTech_*/ # Modules spécialisés
├── odoo{12..18}.0/ # Sources Odoo par version
├── doc/ # Documentation (DEVELOPMENT, PRODUCTION, MIGRATION, etc.)
├── test/ # Framework de test
└── private/ # Fichiers privés (non versionné)
```
## Commandes essentielles
### Changement de version Odoo
```bash
make switch_odoo_18 # Passer à Odoo 18
make switch_odoo_17 # Passer à Odoo 17
make switch_odoo_16 # Passer à Odoo 16 (défaut)
make switch_odoo_15 # Passer à Odoo 15
make switch_odoo_14 # Passer à Odoo 14
make switch_odoo_13 # Passer à Odoo 13
make switch_odoo_12 # Passer à Odoo 12
make switch_odoo_16_update # Passer + mettre à jour les dépendances
```
### Exécution
```bash
make run # Lancer Odoo (port 8069)
make run_test # Lancer avec la DB "test"
./run.sh -d ma_base # Lancer avec une DB spécifique
./odoo_bin.sh [args] # Exécuter odoo-bin directement
```
### Base de données
```bash
make db_create_db_test # Créer la DB "test"
make db_clone_test_to_test2 # Cloner test -> test2
./script/database/db_restore.py --database NOM --image IMAGE
./script/addons/install_addons_dev.sh DB module1,module2
./script/addons/update_addons_all.sh DB
```
### Tests
```bash
make test # Tests de base + format
make test_full_fast # Tests complets en parallèle
make test_full_fast_coverage # Avec couverture de code
```
### Formatage du code
```bash
make format # Fichiers à commiter uniquement
make format_all # Tout formater (parallèle)
```
Outils : **Black** (Python, ligne max 79), **isort** (profil black, ligne max 79), **Prettier** (XML/JSON), **Flake8** (ligne max 80, complexité max 16).
### Docker
```bash
make docker_run_daemon # Lancer en production
make docker_build_odoo_18 # Builder l'image Odoo 18
```
### Google Repo (gestion multi-dépôts)
```bash
make repo_show_status # Statut de tous les dépôts addons
make repo_configure_all # Configurer tous les dépôts
make repo_do_stash # Stash tous les dépôts
.venv.erplibre/bin/repo sync # Synchroniser les dépôts
```
### Installation
```bash
make install_os # Dépendances système
make install_odoo_18 # Installer Odoo 18 complet
```
## Conventions de code
### Python
- Formateur : **Black** (profil par défaut, ligne max 79 pour les modules Odoo)
- Imports : **isort** avec profil `black`, longueur de ligne 79
- Linting : **Flake8** avec bugbear, max-line-length 80, max-complexity 16
- Ignorer : E203, E501, W503 (compatibilité Black)
### XML / JSON / YAML
- Formateur : **Prettier** (via npm)
- Indentation : 4 espaces (XML/CSS/JS), 2 espaces (JSON/YAML)
### Fichiers
- Encodage : UTF-8
- Fins de ligne : LF (Unix)
- Indentation : 4 espaces (Python, XML, CSS, JS), 2 espaces (JSON, YAML, RST, MD)
- Retour à la ligne final : oui
- Espaces en fin de ligne : supprimés
### Git
- Branches : `develop` (développement), `master` (production)
- Pas de submodules Git — utilise **Google Repo** pour les addons
- Manifests XML dans `manifest/` pour chaque version Odoo
## Architecture des environnements virtuels
```
.venv.erplibre/ # Venv ERPLibre (outils : repo, poetry, coverage)
.venv.odoo18/ # Venv Odoo 18 (Python 3.12)
.venv.odoo17/ # Venv Odoo 17 (Python 3.10)
.venv.odoo16/ # Venv Odoo 16 (Python 3.10)
.venv.odoo14/ # Venv Odoo 14 (Python 3.8)
.venv.odoo12/ # Venv Odoo 12 (Python 3.7)
```
Géré via **pyenv** pour les multiples versions de Python.
## Système de dépendances
Chaque version Odoo a son propre ensemble dans `requirement/` :
- `pyproject.odooXX.0_pythonY.Z.toml` — Configuration Poetry
- `poetry.odooXX.0_pythonY.Z.lock` — Lock file Poetry
- `requirements.odooXX.0_pythonY.Z.txt` — Requirements pip (fallback)
- `ignore_requirements.odooXX.0.txt` — Paquets à ignorer
Mise à jour : `./script/poetry/poetry_update.py`
## Code Generator
ERPLibre inclut un système de génération de modules Odoo :
- `script/code_generator/new_project.py` — Créer un nouveau module
- `script/code_generator/create_from_existing_module.py` — Cloner un module existant
- `addons/TechnoLibre_odoo-code-generator/` — Moteur de génération
- `addons/TechnoLibre_odoo-code-generator-template/` — Templates
Documentation : `doc/CODE_GENERATOR.md`
## Documentation multilingue
La documentation est bilingue (anglais/français) via **mmg** (Multilingual Markdown Generator).
### Fonctionnement
- Les fichiers sources sont les `.base.md` (contiennent les deux langues)
- `mmg` génère : `FICHIER.md` (anglais) et `FICHIER.fr.md` (français)
- Marqueurs : `<!-- [en] -->`, `<!-- [fr] -->`, `<!-- [common] -->` (blocs de code partagés)
### Commandes
```bash
make doc_markdown # Regénérer toute la doc multilingue
```
### Convention
- **Ne jamais modifier directement** les fichiers `.md` ou `.fr.md` générés
- Toujours modifier le fichier `.base.md` correspondant, puis exécuter `make doc_markdown`
- Les blocs de code vont dans `<!-- [common] -->`, le texte dans `<!-- [en] -->` et `<!-- [fr] -->`
- En-tête obligatoire dans chaque `.base.md` :
```
<!---------------------------->
<!-- multilingual suffix: en, fr -->
<!-- no suffix: en -->
<!---------------------------->
```
### Fichiers concernés (30 fichiers)
- Racine : `README`, `CHANGELOG`, `TODO`
- `doc/` : DEVELOPMENT, PRODUCTION, DISCOVER, RUN, MIGRATION, WINDOWS_INSTALLATION, FAQ, GIT_REPO, POETRY, RELEASE, UPDATE, CONTRIBUTION, HOWTO, TODO, CODE_GENERATOR
- `docker/` : README
- `script/*/` : database, deployment, fork_github_repo, nginx, restful, selenium (2), todo, odoo/migration
- `.github/ISSUE_TEMPLATE/` : bug_report, feature_request
## Internationalisation du CLI TODO (i18n)
Le CLI interactif `script/todo/todo.py` supporte le français et l'anglais.
### Architecture
- **`script/todo/todo_i18n.py`** — Module de traduction (dictionnaire `TRANSLATIONS`, fonctions `t()`, `get_lang()`, `set_lang()`)
- Les chaînes traduisibles utilisent `t("clé")` au lieu de texte en dur
- Les entrées de `todo.json` peuvent avoir un champ `prompt_description_key` résolu via `t()` (fallback sur `prompt_description`)
### Résolution de la langue (priorité)
1. Variable d'environnement `EL_LANG` (définie dans `env_var.sh`, défaut `"fr"`)
2. Défaut : `"fr"`
### Comportement
- Première exécution : prompt bilingue demande à l'utilisateur de choisir sa langue
- Le choix est persisté dans `env_var.sh`
- Changement de langue possible via le menu Execute > Langue/Language
### Ajouter une traduction
1. Ajouter la clé dans `TRANSLATIONS` de `todo_i18n.py` avec les valeurs `"fr"` et `"en"`
2. Remplacer la chaîne en dur par `t("ma_clé")` dans `todo.py`
3. Pour les entrées JSON : ajouter `"prompt_description_key": "ma_clé"` dans `todo.json`
## Déploiement
- **Docker** : `docker-compose.yml` (PostgreSQL 18 + PostGIS 3.6)
- **Systemd** : `script/systemd/` pour les services
- **Nginx** : `script/nginx/` pour le reverse proxy
- **SSL** : Certbot pour les certificats
- **DNS** : `script/deployment/update_dns_cloudflare.py`
Plateformes supportées : Ubuntu 20.04-25.04, Linux Mint 22.3, Debian 12, Arch Linux, macOS (pyenv), Windows (WSL/Docker).
## Points d'attention pour Claude
- Toujours vérifier la version Odoo active avant de modifier du code (`cat .odoo-version`)
@ -270,58 +20,24 @@ Plateformes supportées : Ubuntu 20.04-25.04, Linux Mint 22.3, Debian 12, Arch L
- Pour la documentation : modifier les `.base.md`, jamais les `.md` ou `.fr.md` directement
- Outil mmg disponible via `source .venv.erplibre/bin/activate && mmg`
## Workflow Orchestration
### 1. Plan Mode Default
- Enter plan mode for ANY non-trivial task (3+ steps or architectural decisions)
- If something goes sideways, STOP and re-plan immediately
- Don't keep pushing.
- Use plan mode for verification steps, not just building
- Write detailed specs upfront to reduce ambiguity
### 2. Subagent Strategy
- Use subagents liberally to keep the main context window clean.
- Offload research, exploration, and parallel analysis to subagents
- For complex problems, throw more compute at it via subagents
- One task per subagent for focused execution
### 3. Self-Improvement Loop
- Capture Lessons: Update AGENT.md with any change in approach the user has asked you to make.
- Write rules for yourself that prevent the same mistake
- Ruthlessly iterate on these lessons until the mistake rate drops.
- Review lessons at session start for relevant project
### 4. Verification Before Done
- Never mark a task complete without proving it works
- Diff behavior between main and your changes when relevant
- Ask yourself, "Would a staff engineer approve this?"
- Run tests, check logs, and demonstrate correctness.
### 5. Demand Elegance (Balanced)
- For non-trivial changes, pause and ask, "Is there a more elegant way?"
- If a fix feels hacky: "Knowing everything I know now, implement the elegant solution"
- Skip this for simple, obvious fixes
- Don't over-engineer.
- Challenge your own work before presenting it
### 6. Autonomous Bug Fixing
- When given a bug report, just fix it. Don't ask for hand-holding
- Point at logs, errors, and failing tests.
- Then resolve them.
- Zero context switching required from the user
- Go fix failing CI tests without being told how
## Task Management
1. **Plan First**: Write a plan to `tasks/todo.md` with checkable items.
2. **Verify Plan**: Check in before starting implementation.
3. **Track Progress**: Mark items complete as you go.
4. **Explain Changes**: High-level summary at each step.
5. **Document Results**: Add review section to `tasks/todo.md`
6. **Capture Lessons**: Update `tasks/lessons.md` after corrections
## Core Principles
- **Simplicity First**: Make every change as simple as possible. Impact minimal code.
- **No Laziness**: Find root causes. No temporary fixes. Senior developer standards.
- **Minimal Impact**: Changes should only touch what's necessary. Avoid introducing bugs.
## Règles détaillées
Les instructions détaillées sont dans `.claude/rules/` :
| Fichier | Contenu |
|---------|---------|
| `01-versions.md` | Versions Odoo/Python/Poetry supportées |
| `02-project-structure.md` | Arborescence du projet |
| `03-commands.md` | Commandes essentielles (make, scripts) |
| `04-code-conventions.md` | Conventions Python, XML, fichiers, Git |
| `05-environments.md` | Venvs, pyenv, système de dépendances |
| `06-code-generator.md` | Génération de modules Odoo |
| `07-documentation.md` | Documentation multilingue (mmg) + i18n CLI |
| `08-deployment.md` | Docker, systemd, nginx, SSL, DNS |
| `09-workflow.md` | Workflow orchestration + task management |

View file

@ -0,0 +1,143 @@
---
name: todo_add_command
description: "Add a new menu command to script/todo/todo.py with i18n support and optional todo.json entry."
allowed-tools:
- Read
- Edit
- Write
- Grep
- Glob
- Bash(python3 -m py_compile:*)
- Bash(python3 -c:*)
---
## Context
- Current todo.py menu structure: !`grep -n "def prompt_execute" script/todo/todo.py | head -20`
- Current todo.json sections: !`python3 -c "import json; d=json.load(open('script/todo/todo.json')); print('\n'.join(d.keys()))"`
- Current i18n keys count: !`grep -c fr.: script/todo/todo_i18n.py`
## Architecture Reference
### Files to modify
| File | Role |
|------|------|
| `script/todo/todo.py` | Main CLI — menu methods, business logic |
| `script/todo/todo_i18n.py` | Translations dict `TRANSLATIONS` with `"fr"` and `"en"` keys |
| `script/todo/todo.json` | Config-driven entries (optional, for `bash_command` or `makefile_cmd`) |
### Pattern A — Hardcoded menu entry (interactive logic)
Used when the command needs Python logic (user prompts, conditionals, API calls).
1. **Add i18n keys** in `todo_i18n.py` `TRANSLATIONS` dict:
```python
"my_feature_description": {
"fr": "Description en français",
"en": "English description",
},
```
2. **Add choice** in the parent menu method (e.g. `prompt_execute_git`):
```python
choices = [
...,
{"prompt_description": t("my_feature_description")},
]
```
3. **Add elif branch** in the same menu's while loop:
```python
elif status == "N":
self._my_feature()
```
4. **Add method** to the `TODO` class:
```python
def _my_feature(self):
# Implementation here
pass
```
### Pattern B — Config-driven entry (simple bash command)
Used when the command just runs a bash command or a make target.
1. **Add i18n key** in `todo_i18n.py` (use `prompt_description_key`).
2. **Add entry** in `todo.json` under the appropriate `*_from_makefile` section:
```json
{
"prompt_description_key": "my_i18n_key",
"bash_command": "my-command --flag"
}
```
Or for make targets:
```json
{
"prompt_description_key": "my_i18n_key",
"makefile_cmd": "my_make_target"
}
```
These are automatically picked up by `execute_from_configuration()`.
### Available menu sections
| Menu | Method | JSON key |
|------|--------|----------|
| Automation | `prompt_execute_function` | `function` |
| Code | `prompt_execute_code` | `code_from_makefile` |
| Config | `prompt_execute_config` | — |
| Database | `prompt_execute_database` | — |
| Doc | `prompt_execute_doc` | — |
| Git | `prompt_execute_git` | `git_from_makefile` |
| GPT code | `prompt_execute_gpt_code` | — |
| Network | `prompt_execute_network` | — |
| Process | `prompt_execute_process` | — |
| Run | `prompt_execute_instance` | `instance` |
| Security | `prompt_execute_security` | — |
| Test | `prompt_execute_test` | — |
| Update | `prompt_execute_update` | `update_from_makefile` |
### Key conventions
- i18n keys: `snake_case`, grouped by section with a comment header
- Method names: `_private_method` for actions, `prompt_execute_*` for submenus
- All user-facing strings must use `t("key")` — never hardcoded text
- Use `self.execute.exec_command_live(cmd, source_erplibre=False)` to run bash
- Use `input(t("prompt_key")).strip()` for user input
- Use `click.prompt(help_info)` for menu navigation
- Menu methods return `False` on back, use `self.fill_help_info(choices)` for formatting
## Task
Add a new command to the todo.py menu system. The user will describe what they want.
### Steps
1. **Determine the target menu section** from the user's description
2. **Choose Pattern A or B** based on complexity:
- Simple bash/make command → Pattern B (todo.json entry)
- Needs user interaction or logic → Pattern A (hardcoded method)
3. **Add i18n translations** in `todo_i18n.py` — always both `"fr"` and `"en"`
4. **Implement the feature** following the appropriate pattern
5. **Validate syntax**:
```bash
python3 -m py_compile script/todo/todo.py
python3 -m py_compile script/todo/todo_i18n.py
python3 -c "import json; json.load(open('script/todo/todo.json'))"
```
### Rules
- Keep menu numbering sequential — update all `elif` branches if inserting
- Group i18n keys with a `# Section name` comment
- Match existing code style (Black, 79 char lines for Odoo modules)
- Do not break existing menu entries or their numbering
- Test that `fill_help_info` and `execute_from_configuration` handle the new entry
## User Request
$ARGUMENTS

View file

@ -96,9 +96,7 @@ def commit_by_directory():
run_git_command(f'cd "{config.path}" && git rm -r {directory}')
# Crée le commit
commit_message = (
f"[{mig_prefix_msg}] {directory}: Migration to {config.odoo_version}"
)
commit_message = f"[{mig_prefix_msg}] {directory}: Migration to {config.odoo_version}"
commit_result = run_git_command(
f'cd "{config.path}" && git commit -m "{commit_message}"'
)

View file

@ -10,28 +10,31 @@ def get_db_list_xmlrpc(odoo_url):
Retrieves the list of Odoo databases using the XML-RPC API.
"""
try:
common = xmlrpc.client.ServerProxy(f'{odoo_url}/xmlrpc/db')
common = xmlrpc.client.ServerProxy(f"{odoo_url}/xmlrpc/db")
db_list = common.list()
return db_list
except xmlrpc.client.Fault as e:
print(f"XML-RPC Error: {e.faultCode} - {e.faultString}", file=sys.stderr)
print(
f"XML-RPC Error: {e.faultCode} - {e.faultString}", file=sys.stderr
)
return []
except Exception as e:
print(f"Connection Error: {e}", file=sys.stderr)
return []
# --- CLI using Click ---
@click.command()
@click.option(
'--odoo-url',
default='http://localhost:8069',
help='URL of the Odoo server.',
show_default=True
"--odoo-url",
default="http://localhost:8069",
help="URL of the Odoo server.",
show_default=True,
)
@click.option(
'--raw',
"--raw",
is_flag=True,
help='Output one database per line, without extra formatting. Useful for scripting.'
help="Output one database per line, without extra formatting. Useful for scripting.",
)
def list_databases(odoo_url, raw=False):
"""
@ -56,5 +59,5 @@ def list_databases(odoo_url, raw=False):
# --- Script Execution ---
if __name__ == '__main__':
if __name__ == "__main__":
list_databases()

View file

@ -115,7 +115,7 @@ class Execute:
command = self.cmd_source_default % command
if not quiet:
print("🏠 ⬇ Execute command :")
print("🏠 ⬇ Execute command :\n")
print(command)
output_lines = []
@ -172,10 +172,10 @@ class Execute:
duration_delta = datetime.timedelta(seconds=duration_sec)
human_time = humanize.precisedelta(duration_delta)
if not quiet:
print(f"🏠 ⬆ Executed ({human_time}) :")
print(f"🏠 ⬆ Executed ({human_time}) :\n")
else:
if not quiet:
print(f"🏠 ⬆ Executed ({duration_sec:.2f} sec.) :")
print(f"🏠 ⬆ Executed ({duration_sec:.2f} sec.) :\n")
if not quiet:
print(command)
print()

View file

@ -151,8 +151,7 @@ Use --production-ready for /srv/git (requires root).
type=int,
default=DEFAULT_JOBS,
help=(
"Parallel jobs for init/remote/push"
f" (default: {DEFAULT_JOBS})"
"Parallel jobs for init/remote/push" f" (default: {DEFAULT_JOBS})"
),
)
parser.add_argument(
@ -247,11 +246,7 @@ def get_erplibre_root_project(erplibre_root):
capture_output=True,
text=True,
)
revision = (
result.stdout.strip()
if result.returncode == 0
else "master"
)
revision = result.stdout.strip() if result.returncode == 0 else "master"
return {
"name": ERPLIBRE_REPO_NAME,
@ -278,19 +273,13 @@ async def _init_single_bare_repo(git_path, project, semaphore):
return "skipped"
_logger.info(f" Creating: {bare_path}")
_, err, rc = await _run_git(
"git", "init", "--bare", bare_path
)
_, err, rc = await _run_git("git", "init", "--bare", bare_path)
if rc != 0:
_logger.warning(
f" Init failed: {bare_path}: {err.strip()}"
)
_logger.warning(f" Init failed: {bare_path}: {err.strip()}")
return "error"
# Enable git daemon export
export_file = os.path.join(
bare_path, "git-daemon-export-ok"
)
export_file = os.path.join(bare_path, "git-daemon-export-ok")
open(export_file, "w").close()
# Allow pushing from shallow clones
@ -312,10 +301,7 @@ async def init_bare_repos(git_path, projects, jobs):
semaphore = asyncio.Semaphore(jobs)
results = await asyncio.gather(
*[
_init_single_bare_repo(git_path, p, semaphore)
for p in projects
]
*[_init_single_bare_repo(git_path, p, semaphore) for p in projects]
)
created = results.count("created")
@ -352,9 +338,7 @@ async def _add_single_remote(
):
"""Add or update remote for a single repo (async)."""
async with semaphore:
repo_path = os.path.join(
erplibre_root, project["path"]
)
repo_path = os.path.join(erplibre_root, project["path"])
if not os.path.isdir(repo_path):
_logger.warning(f" Not found: {repo_path}")
return "error"
@ -368,9 +352,7 @@ async def _add_single_remote(
)
# Check if remote already exists
stdout, _, _ = await _run_git(
"git", "-C", repo_path, "remote"
)
stdout, _, _ = await _run_git("git", "-C", repo_path, "remote")
existing_remotes = stdout.strip().split("\n")
if remote_name in existing_remotes:
@ -403,8 +385,7 @@ async def _add_single_remote(
)
if rc != 0:
_logger.warning(
f" add failed for"
f" {project['path']}: {err.strip()}"
f" add failed for" f" {project['path']}: {err.strip()}"
)
return "error"
_logger.info(f" Added: {project['path']}")
@ -447,10 +428,7 @@ async def add_remotes(
added = results.count("added")
updated = results.count("updated")
errors = results.count("error")
print(
f"Remotes: {added} added, {updated} updated,"
f" {errors} errors"
)
print(f"Remotes: {added} added, {updated} updated," f" {errors} errors")
# --- Async workers for push ---
@ -458,9 +436,7 @@ async def add_remotes(
async def _is_detached_head(repo_path):
"""Check if a repo is in detached HEAD state."""
_, _, rc = await _run_git(
"git", "-C", repo_path, "symbolic-ref", "HEAD"
)
_, _, rc = await _run_git("git", "-C", repo_path, "symbolic-ref", "HEAD")
return rc != 0
@ -471,9 +447,7 @@ async def _checkout_manifest_branch(repo_path, revision):
on the exact commit. We create/checkout a local branch
matching the manifest revision so git push works.
"""
branch = (
revision.split("/")[-1] if "/" in revision else revision
)
branch = revision.split("/")[-1] if "/" in revision else revision
# Check if local branch already exists
_, _, rc = await _run_git(
@ -485,9 +459,7 @@ async def _checkout_manifest_branch(repo_path, revision):
f"refs/heads/{branch}",
)
if rc == 0:
await _run_git(
"git", "-C", repo_path, "checkout", branch
)
await _run_git("git", "-C", repo_path, "checkout", branch)
else:
await _run_git(
"git",
@ -514,9 +486,7 @@ async def _update_bare_head(git_path, project):
revision = project.get("revision", "")
if not revision:
return
branch = (
revision.split("/")[-1] if "/" in revision else revision
)
branch = revision.split("/")[-1] if "/" in revision else revision
await _run_git(
"git",
"-C",
@ -529,20 +499,10 @@ async def _update_bare_head(git_path, project):
async def _try_unshallow(repo_path, project, remote_name):
"""Try to unshallow a repo by fetching full history."""
shallow_file = os.path.join(
repo_path, ".git", "shallow"
)
_logger.info(
f" Unshallowing {project['path']}..."
)
stdout, _, _ = await _run_git(
"git", "-C", repo_path, "remote"
)
remotes = [
r
for r in stdout.strip().split("\n")
if r and r != remote_name
]
shallow_file = os.path.join(repo_path, ".git", "shallow")
_logger.info(f" Unshallowing {project['path']}...")
stdout, _, _ = await _run_git("git", "-C", repo_path, "remote")
remotes = [r for r in stdout.strip().split("\n") if r and r != remote_name]
manifest_remote = project.get("remote", "")
if manifest_remote in remotes:
remotes.remove(manifest_remote)
@ -562,9 +522,7 @@ async def _try_unshallow(repo_path, project, remote_name):
except asyncio.TimeoutError:
continue
if not os.path.exists(shallow_file):
_logger.info(
f" Unshallowed via {try_remote}"
)
_logger.info(f" Unshallowed via {try_remote}")
return
if os.path.exists(shallow_file):
_logger.warning(
@ -585,30 +543,22 @@ async def _push_single_repo(
):
"""Push a single repo to local remote (async)."""
async with semaphore:
repo_path = os.path.join(
erplibre_root, project["path"]
)
repo_path = os.path.join(erplibre_root, project["path"])
if not os.path.isdir(repo_path):
_logger.warning(f" Not found: {repo_path}")
return "error", False
# Handle shallow repos
shallow_file = os.path.join(
repo_path, ".git", "shallow"
)
shallow_file = os.path.join(repo_path, ".git", "shallow")
if os.path.exists(shallow_file):
if unshallow:
await _try_unshallow(
repo_path, project, remote_name
)
await _try_unshallow(repo_path, project, remote_name)
else:
# Enable shallow push on bare repo
repo_name = project["name"]
if not repo_name.endswith(".git"):
repo_name += ".git"
bare_path = os.path.join(
git_path, repo_name
)
bare_path = os.path.join(git_path, repo_name)
if os.path.isdir(bare_path):
await _run_git(
"git",
@ -618,19 +568,14 @@ async def _push_single_repo(
"receive.shallowUpdate",
"true",
)
_logger.info(
f" Shallow push for"
f" {project['path']}"
)
_logger.info(f" Shallow push for" f" {project['path']}")
# Handle detached HEAD: checkout manifest branch
did_checkout = False
if await _is_detached_head(repo_path):
revision = project.get("revision", "")
if revision:
branch = await _checkout_manifest_branch(
repo_path, revision
)
branch = await _checkout_manifest_branch(repo_path, revision)
_logger.info(
f" Checkout {branch} for"
f" {project['path']}"
@ -663,9 +608,7 @@ async def _push_single_repo(
"push",
remote_name,
]
_, err, rc = await _run_git(
*cmd, timeout=120
)
_, err, rc = await _run_git(*cmd, timeout=120)
if rc != 0:
_logger.warning(
f" Push failed for"
@ -675,14 +618,10 @@ async def _push_single_repo(
return "error", did_checkout
else:
await _update_bare_head(git_path, project)
_logger.info(
f" Pushed: {project['path']}"
)
_logger.info(f" Pushed: {project['path']}")
return "pushed", did_checkout
except asyncio.TimeoutError:
_logger.warning(
f" Timeout pushing {project['path']}"
)
_logger.warning(f" Timeout pushing {project['path']}")
return "error", did_checkout
@ -744,8 +683,7 @@ def print_clone_commands(git_path, projects, port):
if clone_path == ".":
clone_path = "erplibre"
lines.append(
f" git clone {base_url}/{repo_name}"
f" {clone_path}"
f" git clone {base_url}/{repo_name}" f" {clone_path}"
)
lines.sort()
for line in lines:

View file

@ -26,17 +26,10 @@ def get_pull_request_repo(
parsed_url = parse(upstream_url)
status, user = gh.user.get()
user_name = (
user["login"] if not organization_name else organization_name
)
status, lst_pull = (
gh.repos[user_name][parsed_url.repo].pulls.get()
)
user_name = user["login"] if not organization_name else organization_name
status, lst_pull = gh.repos[user_name][parsed_url.repo].pulls.get()
if type(lst_pull) is dict:
print(
f"For url {upstream_url},"
f" got {lst_pull.get('message')}"
)
print(f"For url {upstream_url}," f" got {lst_pull.get('message')}")
return False
else:
for pull in lst_pull:
@ -53,26 +46,21 @@ def fork_repo(
parsed_url = parse(upstream_url)
status, user = gh.user.get()
user_name = (
user["login"] if not organization_name else organization_name
)
status, forked_repo = (
gh.repos[user_name][parsed_url.repo].get()
)
user_name = user["login"] if not organization_name else organization_name
status, forked_repo = gh.repos[user_name][parsed_url.repo].get()
if status == 404:
status, upstream_repo = (
gh.repos[parsed_url.owner][parsed_url.repo].get()
)
status, upstream_repo = gh.repos[parsed_url.owner][
parsed_url.repo
].get()
if status == 404:
print("Unable to find repo %s" % upstream_url)
exit(1)
args = {}
if organization_name:
args["organization"] = organization_name
status, forked_repo = (
gh.repos[parsed_url.owner][parsed_url.repo]
.forks.post(**args)
)
status, forked_repo = gh.repos[parsed_url.owner][
parsed_url.repo
].forks.post(**args)
if status == 404:
print(
f"{Fore.RED}Error{Style.RESET_ALL} when forking"
@ -82,23 +70,16 @@ def fork_repo(
else:
try:
print(
"Forked %s to %s"
% (upstream_url, forked_repo["html_url"])
"Forked %s to %s" % (upstream_url, forked_repo["html_url"])
)
except Exception as e:
print(e)
print(forked_repo)
print(upstream_url)
elif status == 202:
print(
"Forked repo %s already exists"
% forked_repo["full_name"]
)
print("Forked repo %s already exists" % forked_repo["full_name"])
elif status != 200:
print(
"Status not supported: %s - %s"
% (status, forked_repo)
)
print("Status not supported: %s - %s" % (status, forked_repo))
exit(1)
@ -110,9 +91,7 @@ def add_and_fetch_remote(
"""
try:
working_repo = Repo(repo_info.relative_path)
if repo_info.organization in [
a.name for a in working_repo.remotes
]:
if repo_info.organization in [a.name for a in working_repo.remotes]:
print(
f'Remote "{repo_info.organization}" already exist'
f" in {repo_info.relative_path}"
@ -122,8 +101,7 @@ def add_and_fetch_remote(
print(f"New repo {repo_info.relative_path}")
if not root_repo:
print(
"Missing git repository to root for repo"
f" {repo_info.path}"
"Missing git repository to root for repo" f" {repo_info.path}"
)
return
if branch_name:
@ -149,16 +127,14 @@ def add_and_fetch_remote(
# Add remote
upstream_remote = retry(
wait_exponential_multiplier=1000, stop_max_delay=15000
)(working_repo.create_remote)(
repo_info.organization, repo_info.url_https
)
)(working_repo.create_remote)(repo_info.organization, repo_info.url_https)
print(
'Remote "%s" created for %s'
% (repo_info.organization, repo_info.url_https)
)
# Fetch the remote
retry(
wait_exponential_multiplier=1000, stop_max_delay=15000
)(upstream_remote.fetch)()
retry(wait_exponential_multiplier=1000, stop_max_delay=15000)(
upstream_remote.fetch
)()
print('Remote "%s" fetched' % repo_info.organization)

View file

@ -26,7 +26,9 @@ _logger = logging.getLogger(__name__)
PROJECT_NAME = os.path.basename(os.getcwd())
IDEA_PATH = "./.idea"
DEFAULT_ODOO_BIN = "./odoo/odoo-bin" # Need this to replace static configuration
DEFAULT_ODOO_BIN = (
"./odoo/odoo-bin" # Need this to replace static configuration
)
IDEA_MISC = os.path.join(IDEA_PATH, "misc.xml")
IDEA_WORKSPACE = os.path.join(IDEA_PATH, "workspace.xml")
VCS_WORKSPACE = os.path.join(IDEA_PATH, "vcs.xml")

View file

@ -2,7 +2,9 @@
# © 2021-2026 TechnoLibre (http://www.technolibre.ca)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
installed_modules = env["ir.module.module"].search([('state', '=', 'installed')])
installed_modules = env["ir.module.module"].search(
[("state", "=", "installed")]
)
print("Installed modules:")

View file

@ -10,18 +10,20 @@ print("Running a script in the Odoo shell!")
# ERROR: insert or update on table "discuss_channel_member" violates foreign key constraint "discuss_channel_member_channel_id_fkey"
# DÉTAIL : Key (channel_id)=(3) is not present in table "discuss_channel".
env.cr.execute("""
env.cr.execute(
"""
ALTER TABLE discuss_channel
DROP CONSTRAINT discuss_channel_channel_type_not_null;
""")
"""
)
env.cr.commit()
#env.cr.execute("SELECT to_regclass('public.discuss_channel');")
#print("table:", env.cr.fetchone())
# env.cr.execute("SELECT to_regclass('public.discuss_channel');")
# print("table:", env.cr.fetchone())
#print(
# print(
# "model discuss.channel:",
# env["ir.model"].search([("model", "=", "discuss.channel")]),
#)
# )
print("End fix migration with update mail postgresql 17 to postgresql 18.")

View file

@ -10,47 +10,52 @@
},
"instance": [
{
"prompt_description_key": "json_instance_test",
"prompt_description_key": "Test - Minimal base instance",
"makefile_cmd": "db_restore_erplibre_base_db_test",
"database": "test"
},
{
"prompt_description_key": "json_robot_minimal",
"prompt_description_key": "Open RobotLibre 🤖 minimal",
"makefile_cmd": "robot_libre",
"database": "robotlibre"
},
{
"prompt_description_key": "json_robot_search",
"prompt_description_key": "Open RobotLibre 🤖 with search enabled",
"makefile_cmd": "robot_libre_all",
"database": "robotlibre"
}
],
"function": [
{
"prompt_description_key": "json_open_erplibre_todo",
"prompt_description_key": "Open ERPLibre with TODO 🤖",
"command": "./.venv.erplibre/bin/python ./script/selenium/web_login.py"
}
],
"update_from_makefile": [
{
"prompt_description_key": "json_update_erplibre_base_test",
"prompt_description_key": "Update all erplibre_base on database test",
"makefile_cmd": "db_erplibre_base_db_test_update_all",
"database": "test"
}
],
"code_from_makefile": [
{
"prompt_description_key": "json_show_code_status",
"prompt_description_key": "Show code status",
"makefile_cmd": "repo_show_status"
},
{
"prompt_description_key": "json_stash_all_code",
"prompt_description_key": "Stash all code",
"makefile_cmd": "repo_do_stash"
},
{
"prompt_description_key": "json_format_modified_code",
"prompt_description_key": "Format modified code",
"makefile_cmd": "format"
}
],
"git_from_makefile": []
"git_from_makefile": [
{
"prompt_description_key": "Configure git local editor to vim",
"bash_command": "git config --global core.editor \"vim\""
}
]
}

File diff suppressed because it is too large Load diff

View file

@ -15,539 +15,732 @@ TRANSLATIONS = {
"fr": "L'importation est un succès!",
"en": "Importation success!",
},
"opening": {
"Opening TODO ...": {
"fr": "Ouverture de TODO en cours ...",
"en": "Opening TODO ...",
},
"enter_directives": {
"=> Enter your choice by number and press Enter!": {
"fr": "=> Entre tes directives par son chiffre et fait Entrée!",
"en": "=> Enter your choice by number and press Enter!",
},
"command": {
"Command:": {
"fr": "Commande :",
"en": "Command:",
},
"menu_execute": {
"Execute": {
"fr": "Exécution",
"en": "Execute",
},
"menu_install": {
"Install": {
"fr": "Installation",
"en": "Install",
},
"menu_question": {
"Question": {
"fr": "Question",
"en": "Question",
},
"menu_fork": {
"Fork - Open TODO in a new tab": {
"fr": "Fork - Ouvre TODO dans une nouvelle tabulation",
"en": "Fork - Open TODO in a new tab",
},
"menu_quit": {
"Quit": {
"fr": "Quitter",
"en": "Quit",
},
"cmd_not_found": {
"Command not found !": {
"fr": "Commande non trouvée !",
"en": "Command not found !",
},
"back": {
"Back": {
"fr": "Retour",
"en": "Back",
},
# Execute submenu
"menu_run": {
"Run - Execute and install an instance": {
"fr": "Run - Exécuter et installer une instance",
"en": "Run - Execute and install an instance",
},
"menu_automation": {
"Automation - Demonstration of developed features": {
"fr": "Automatisation - Demonstration des fonctions développées",
"en": "Automation - Demonstration of developed features",
},
"menu_update": {
"Update - Update all developed staging source code": {
"fr": "Mise à jour - Update all developed staging source code",
"en": "Update - Update all developed staging source code",
},
"menu_code": {
"Code - Developer tools": {
"fr": "Code - Outil pour développeur",
"en": "Code - Developer tools",
},
"menu_doc": {
"Doc - Documentation search": {
"fr": "Doc - Recherche de documentation",
"en": "Doc - Documentation search",
},
"menu_database": {
"Database - Database tools": {
"fr": "Database - Outils sur les bases de données",
"en": "Database - Database tools",
},
"menu_process": {
"Process - Execution tools": {
"fr": "Process - Outils sur les executions",
"en": "Process - Execution tools",
},
"menu_config": {
"Config - Configuration file management": {
"fr": "Config - Traitement du fichier de configuration",
"en": "Config - Configuration file management",
},
"menu_network": {
"Network - Network tools": {
"fr": "Réseau - Outil réseautique",
"en": "Network - Network tools",
},
"menu_security": {
"Security - Dependency security audit": {
"fr": "Sécurité - Audit de sécurité des dépendances",
"en": "Security - Dependency security audit",
},
"menu_lang": {
"RTK - CLI proxy to reduce LLM token consumption": {
"fr": "RTK - Proxy CLI pour réduire la consommation de tokens LLM",
"en": "RTK - CLI proxy to reduce LLM token consumption",
},
"Language - Change language / Changer la langue": {
"fr": "Langue - Changer la langue / Change language",
"en": "Language - Change language / Changer la langue",
},
# Deploy section
"Deploy - Deploy ERPLibre locally": {
"fr": "Déploiement - Déployer ERPLibre localement",
"en": "Deploy - Deploy ERPLibre locally",
},
"Deploy ERPLibre to a local directory!": {
"fr": "Déployer ERPLibre dans un répertoire local!",
"en": "Deploy ERPLibre to a local directory!",
},
"Clone ERPLibre locally (git clone)": {
"fr": "Cloner ERPLibre localement (git clone)",
"en": "Clone ERPLibre locally (git clone)",
},
"Target directory path (default: ~/erplibre): ": {
"fr": "Chemin du répertoire cible (défaut: ~/erplibre) : ",
"en": "Target directory path (default: ~/erplibre): ",
},
"Directory already exists: ": {
"fr": "Le répertoire existe déjà : ",
"en": "Directory already exists: ",
},
"Cloning ERPLibre...": {
"fr": "Clonage d'ERPLibre en cours...",
"en": "Cloning ERPLibre...",
},
"ERPLibre cloned successfully to: ": {
"fr": "ERPLibre cloné avec succès dans : ",
"en": "ERPLibre cloned successfully to: ",
},
"Error cloning ERPLibre: ": {
"fr": "Erreur lors du clonage d'ERPLibre : ",
"en": "Error cloning ERPLibre: ",
},
# RTK (Rust Token Killer)
"Manage RTK (Rust Token Killer) for token optimization!": {
"fr": "Gérer RTK (Rust Token Killer) pour optimiser les tokens!",
"en": "Manage RTK (Rust Token Killer) for token optimization!",
},
"Install RTK": {
"fr": "Installer RTK",
"en": "Install RTK",
},
"Check RTK version": {
"fr": "Vérifier la version de RTK",
"en": "Check RTK version",
},
"Show cumulative token savings": {
"fr": "Afficher les économies de tokens cumulées",
"en": "Show cumulative token savings",
},
"Discover optimization opportunities": {
"fr": "Identifier les opportunités d'optimisation",
"en": "Discover optimization opportunities",
},
"Initialize global auto-rewrite hook": {
"fr": "Initialiser le hook auto-rewrite global",
"en": "Initialize global auto-rewrite hook",
},
"Check RTK status": {
"fr": "Vérifier le statut de RTK",
"en": "Check RTK status",
},
"RTK is not installed. Use option 1 to install it.": {
"fr": "RTK n'est pas installé. Utilisez l'option 1 pour l'installer.",
"en": "RTK is not installed. Use option 1 to install it.",
},
"RTK is installed, version: ": {
"fr": "RTK est installé, version : ",
"en": "RTK is installed, version: ",
},
"Global auto-rewrite hook: active": {
"fr": "Hook auto-rewrite global : actif",
"en": "Global auto-rewrite hook: active",
},
"Global auto-rewrite hook: inactive": {
"fr": "Hook auto-rewrite global : inactif",
"en": "Global auto-rewrite hook: inactive",
},
"Installation method:": {
"fr": "Méthode d'installation :",
"en": "Installation method:",
},
"curl - Automatic install script": {
"fr": "curl - Script d'installation automatique",
"en": "curl - Automatic install script",
},
"brew - Homebrew (macOS/Linux)": {
"fr": "brew - Homebrew (macOS/Linux)",
"en": "brew - Homebrew (macOS/Linux)",
},
"cargo - Build from source (Rust required)": {
"fr": "cargo - Compilation depuis les sources (Rust requis)",
"en": "cargo - Build from source (Rust required)",
},
# Prompts and messages
"enter_password": {
"Enter your password: ": {
"fr": "Entrez votre mot de passe : ",
"en": "Enter your password: ",
},
"ia_prompt": {
"Write your question ": {
"fr": "Écrit moi ta question ",
"en": "Write your question ",
},
"new_instance_confirm": {
"Do you want a new instance?": {
"fr": "Voulez-vous une nouvelle instance?",
"en": "Do you want a new instance?",
},
"ssh_port_forwarding": {
"SSH port-forwarding": {
"fr": "SSH port-forwarding",
"en": "SSH port-forwarding",
},
"network_performance_request_per_second": {
"Network performance request per second": {
"fr": "Performance réseau en requêtes par seconde",
"en": "Network performance request per second",
},
"setup_queue_job_for_parallelism": {
"Setup queue job for parallelism": {
"fr": "Configurer la file d'attente pour l'exécution parallèle",
"en": "Setup queue job for parallelism",
},
"choose_database": {
"Choose your database": {
"fr": "Choisir sa base de données",
"en": "Choose your database",
},
"update_dev": {
"Development update": {
"fr": "Mise à jour du développement",
"en": "Development update",
},
"code_need": {
"What do you need for development?": {
"fr": "Qu'avez-vous de besoin pour développer?",
"en": "What do you need for development?",
},
"doc_search": {
"Looking for documentation?": {
"fr": "Vous cherchez de la documentation?",
"en": "Looking for documentation?",
},
"db_modify": {
"Make changes to databases!": {
"fr": "Faites des modifications sur les bases de données!",
"en": "Make changes to databases!",
},
"process_manage": {
"Manage execution processes!": {
"fr": "Manipuler les processus d'exécution!",
"en": "Manage execution processes!",
},
"config_manage": {
"Manage ERPLibre and Odoo configuration!": {
"fr": "Manipuler la configuration ERPLibre et Odoo!",
"en": "Manage ERPLibre and Odoo configuration!",
},
"network_tools": {
"Network tools!": {
"fr": "Outil réseautique!",
"en": "Network tools!",
},
"security_audit": {
"Dependency security audit!": {
"fr": "Audit de securite des dépendances!",
"en": "Dependency security audit!",
},
"script_failed": {
"The Bash script failed with return code": {
"fr": "Le script Bash a échoué avec le code de retour",
"en": "The Bash script failed with return code",
},
"no_env_installed": {
"No installed environment found. Install an Odoo version first.": {
"fr": "Aucun environnement installe trouve. Installez d'abord une version d'Odoo.",
"en": "No installed environment found. Install an Odoo version first.",
},
"choose_env_audit": {
"Choose an environment for the audit:": {
"fr": "Choisir un environnement pour l'audit :",
"en": "Choose an environment for the audit:",
},
"selection": {
"Select: ": {
"fr": "Sélection : ",
"en": "Select: ",
},
"error_value": {
"Error, cannot understand value": {
"fr": "Erreur, impossible de comprendre la valeur",
"en": "Error, cannot understand value",
},
"dep_file_not_found": {
"Dependencies file not found: ": {
"fr": "Fichier de dépendances introuvable : ",
"en": "Dependencies file not found: ",
},
"execution": {
"Execution: ": {
"fr": "Execution : ",
"en": "Execution: ",
},
"current": {
"Current": {
"fr": "Actuel",
"en": "Current",
},
"default": {
"Default": {
"fr": "Défaut",
"en": "Default",
},
"reboot_todo": {
"Reboot TODO ...": {
"fr": "Reboot TODO ...",
"en": "Reboot TODO ...",
},
"pip_audit_desc": {
"pip-audit - Check vulnerabilities on Python environments": {
"fr": "pip-audit - Verifier les vulnérabilités sur les environnements Python",
"en": "pip-audit - Check vulnerabilities on Python environments",
},
"will_execute": {
"Will execute:": {
"fr": "Va exécuter :",
"en": "Will execute:",
},
"choose_version": {
"Choose a version:": {
"fr": "Choisir une version :",
"en": "Choose a version:",
},
"error_cannot_understand": {
"fr": "Erreur, impossible de comprendre la valeur",
"en": "Error, cannot understand value",
},
# todo.json translatable prompt_descriptions
"json_instance_test": {
"Test - Minimal base instance": {
"fr": "Test - Instance de base minimale",
"en": "Test - Minimal base instance",
},
"json_robot_minimal": {
"Open RobotLibre 🤖 minimal": {
"fr": "Ouvrir RobotLibre 🤖 minimal",
"en": "Open RobotLibre 🤖 minimal",
},
"json_robot_search": {
"Open RobotLibre 🤖 with search enabled": {
"fr": "Ouvrir RobotLibre 🤖 en activant la recherche",
"en": "Open RobotLibre 🤖 with search enabled",
},
"json_open_erplibre_todo": {
"Open ERPLibre with TODO 🤖": {
"fr": "Ouvrir ERPLibre avec TODO 🤖",
"en": "Open ERPLibre with TODO 🤖",
},
"json_update_erplibre_base_test": {
"Update all erplibre_base on database test": {
"fr": "Mise à jour de tous les erplibre_base sur la base de données test",
"en": "Update all erplibre_base on database test",
},
"json_show_code_status": {
"Show code status": {
"fr": "Afficher le statut du code",
"en": "Show code status",
},
"json_stash_all_code": {
"Stash all code": {
"fr": "Remiser tout le code",
"en": "Stash all code",
},
"json_format_modified_code": {
"Format modified code": {
"fr": "Formater le code modifié",
"en": "Format modified code",
},
# todo.py hardcoded prompt_descriptions
"mobile_compile_run": {
"Mobile - Compile and run software": {
"fr": "Mobile - Compiler et exécuter le logiciel",
"en": "Mobile - Compile and run software",
},
"upgrade_odoo_migration": {
"Upgrade Odoo - Migration Database": {
"fr": "Mise à jour Odoo - Migration de base de données",
"en": "Upgrade Odoo - Migration Database",
},
"upgrade_poetry_dependency": {
"Upgrade Poetry - Dependency of Odoo": {
"fr": "Mise à jour Poetry - Dépendances d'Odoo",
"en": "Upgrade Poetry - Dependency of Odoo",
},
"open_shell": {
"Open SHELL": {
"fr": "Ouvrir le SHELL",
"en": "Open SHELL",
},
"upgrade_module": {
"Upgrade Module": {
"fr": "Mise à jour de module",
"en": "Upgrade Module",
},
"debug": {
"Debug": {
"fr": "Débogage",
"en": "Debug",
},
"migration_module_coverage": {
"Migration module coverage": {
"fr": "Couverture de migration des modules",
"en": "Migration module coverage",
},
"what_change_between_version": {
"What change between version": {
"fr": "Quels changements entre les versions",
"en": "What change between version",
},
"oca_guidelines": {
"OCA guidelines": {
"fr": "Directives OCA",
"en": "OCA guidelines",
},
"oca_migration_odoo_19": {
"OCA migration Odoo 19 milestone": {
"fr": "Migration OCA Odoo 19 - Jalons",
"en": "OCA migration Odoo 19 milestone",
},
"download_db_backup": {
"Download database to create backup (.zip)": {
"fr": "Télécharger une base de données pour créer une sauvegarde (.zip)",
"en": "Download database to create backup (.zip)",
},
"restore_from_backup": {
"Restore from backup (.zip)": {
"fr": "Restaurer a partir d'une sauvegarde (.zip)",
"en": "Restore from backup (.zip)",
},
"create_backup": {
"Create backup (.zip)": {
"fr": "Créer une sauvegarde (.zip)",
"en": "Create backup (.zip)",
},
"kill_process_port": {
"Kill Odoo process from actual port": {
"fr": "Terminer le processus Odoo du port actuel",
"en": "Kill Odoo process from actual port",
},
"kill_git_daemon": {
"Kill git daemon server process": {
"fr": "Terminer le processus du serveur git daemon",
"en": "Kill git daemon server process",
},
"kill_git_daemon_done": {
"Git daemon process killed.": {
"fr": "Processus git daemon terminé.",
"en": "Git daemon process killed.",
},
"generate_all_config": {
"Generate all configuration": {
"fr": "Générer toute la configuration",
"en": "Generate all configuration",
},
"generate_from_preconfig": {
"Generate from pre-configuration": {
"fr": "Générer a partir de la pre-configuration",
"en": "Generate from pre-configuration",
},
"generate_from_backup": {
"Generate from backup file": {
"fr": "Générer a partir d'un fichier de sauvegarde",
"en": "Generate from backup file",
},
"generate_from_database": {
"Generate from database": {
"fr": "Générer a partir de la base de données",
"en": "Generate from database",
},
"preconfig_base": {
"base": {
"fr": "base",
"en": "base",
},
"preconfig_base_code_generator": {
"base + code_generator": {
"fr": "base + code_generator",
"en": "base + code_generator",
},
"preconfig_base_image_db": {
"base + image_db": {
"fr": "base + image_db",
"en": "base + image_db",
},
"preconfig_all": {
"all": {
"fr": "tout",
"en": "all",
},
"debug_todo_py": {
"Debug todo.py": {
"fr": "Débogage todo.py",
"en": "Debug todo.py",
},
# Test section
"menu_test": {
"Test - Test an Odoo module": {
"fr": "Test - Tester un module Odoo",
"en": "Test - Test an Odoo module",
},
"test_description": {
"Test an Odoo module on a temporary database!": {
"fr": "Tester un module Odoo sur une base de données temporaire!",
"en": "Test an Odoo module on a temporary database!",
},
"test_run_module": {
"Test a module": {
"fr": "Tester un module",
"en": "Test a module",
},
"test_run_module_coverage": {
"Test a module with code coverage": {
"fr": "Tester un module avec couverture de code",
"en": "Test a module with code coverage",
},
"test_run_unit_tests": {
"ERPLibre unit tests": {
"fr": "Tests unitaires ERPLibre",
"en": "ERPLibre unit tests",
},
"test_unit_running": {
"Running unit tests": {
"fr": "Exécution des tests unitaires",
"en": "Running unit tests",
},
"test_unit_success": {
"All unit tests passed": {
"fr": "Tous les tests unitaires ont réussi",
"en": "All unit tests passed",
},
"test_unit_failed": {
"Some unit tests failed, exit code": {
"fr": "Des tests unitaires ont échoué, code de sortie",
"en": "Some unit tests failed, exit code",
},
"test_enter_module_name": {
"Module name to test: ": {
"fr": "Nom du module à tester : ",
"en": "Module name to test: ",
},
"test_db_name": {
"Temporary database name (default: test_todo_tmp): ": {
"fr": "Nom de la base de données temporaire (défaut: test_todo_tmp) : ",
"en": "Temporary database name (default: test_todo_tmp): ",
},
"test_install_extra_modules": {
"Extra modules to install (comma-separated, empty for none): ": {
"fr": "Modules supplémentaires à installer (séparés par des virgules, vide pour aucun) : ",
"en": "Extra modules to install (comma-separated, empty for none): ",
},
"test_log_level": {
"Log level (default: test): ": {
"fr": "Niveau de log (défaut: test) : ",
"en": "Log level (default: test): ",
},
"test_creating_db": {
"Creating temporary database": {
"fr": "Création de la base de données temporaire",
"en": "Creating temporary database",
},
"test_installing_modules": {
"Installing modules": {
"fr": "Installation des modules",
"en": "Installing modules",
},
"test_running": {
"Running tests": {
"fr": "Exécution des tests",
"en": "Running tests",
},
"test_cleaning_db": {
"Cleaning up temporary database": {
"fr": "Suppression de la base de données temporaire",
"en": "Cleaning up temporary database",
},
"test_keep_db": {
"Keep the temporary database? (y/N): ": {
"fr": "Conserver la base de données temporaire? (o/N) : ",
"en": "Keep the temporary database? (y/N): ",
},
"test_db_kept": {
"Database kept": {
"fr": "Base de données conservée",
"en": "Database kept",
},
"test_success": {
"Tests completed successfully!": {
"fr": "Tests terminés avec succès!",
"en": "Tests completed successfully!",
},
"test_failed": {
"Tests failed with return code": {
"fr": "Les tests ont échoué avec le code de retour",
"en": "Tests failed with return code",
},
"test_module_required": {
"Module name is required!": {
"fr": "Le nom du module est requis!",
"en": "Module name is required!",
},
# Git section
"menu_git": {
"Git - Git tools": {
"fr": "Git - Outils Git",
"en": "Git - Git tools",
},
"git_manage": {
"Git management tools!": {
"fr": "Outils de gestion Git!",
"en": "Git management tools!",
},
"git_local_server": {
"Local git server": {
"fr": "Serveur git local",
"en": "Local git server",
},
"git_repo_manage": {
"Manage local git repository server!": {
"fr": "Gérer le serveur de dépôts git local!",
"en": "Manage local git repository server!",
},
"git_repo_deploy_local": {
"Deploy a local git server (~/.git-server)": {
"fr": "Déployer un serveur git local (~/.git-server)",
"en": "Deploy a local git server (~/.git-server)",
},
"git_repo_deploy_production": {
"Deploy a production git server (/srv/git, root required)": {
"fr": "Déployer un serveur git production (/srv/git, root requis)",
"en": "Deploy a production git server (/srv/git, root required)",
},
"git_repo_deploy_starting": {
"Starting git server deployment...": {
"fr": "Démarrage du déploiement du serveur git...",
"en": "Starting git server deployment...",
},
"git_mode_local": {
"Local mode (~/.git-server)": {
"fr": "Mode local (~/.git-server)",
"en": "Local mode (~/.git-server)",
},
"git_mode_production": {
"Production mode (/srv/git, root required)": {
"fr": "Mode production (/srv/git, root requis)",
"en": "Production mode (/srv/git, root required)",
},
"git_action_all": {
"Run all (init + remote + push + serve)": {
"fr": "Tout exécuter (init + remote + push + serve)",
"en": "Run all (init + remote + push + serve)",
},
"git_action_init": {
"Init - Create bare repos": {
"fr": "Init - Créer les bare repos",
"en": "Init - Create bare repos",
},
"git_action_remote": {
"Remote - Add local remotes": {
"fr": "Remote - Ajouter les remotes locaux",
"en": "Remote - Add local remotes",
},
"git_action_push": {
"Push - Push to local server": {
"fr": "Push - Pousser vers le serveur local",
"en": "Push - Push to local server",
},
"git_action_serve": {
"Serve - Start git daemon": {
"fr": "Serve - Démarrer le daemon git",
"en": "Serve - Start git daemon",
},
# Git remote add
"Add a remote to a local repository": {
"fr": "Ajouter un remote vers un dépôt local",
"en": "Add a remote to a local repository",
},
"Remote name (default: localhost): ": {
"fr": "Nom du remote (défaut: localhost) : ",
"en": "Remote name (default: localhost): ",
},
"Repository address (e.g.: git://192.168.1.100/my-repo.git): ": {
"fr": "Adresse du dépôt (ex: git://192.168.1.100/mon-repo.git) : ",
"en": "Repository address (e.g.: git://192.168.1.100/my-repo.git): ",
},
"Repository address is required!": {
"fr": "L'adresse du dépôt est requise!",
"en": "Repository address is required!",
},
"Remote added successfully!": {
"fr": "Remote ajouté avec succès!",
"en": "Remote added successfully!",
},
"Error adding remote: ": {
"fr": "Erreur lors de l'ajout du remote : ",
"en": "Error adding remote: ",
},
# Git config vim
"Configure git local editor to vim": {
"fr": "Configuration git local par vim",
"en": "Configure git local editor to vim",
},
"Git editor configured to vim successfully!": {
"fr": "Éditeur git configuré sur vim avec succès!",
"en": "Git editor configured to vim successfully!",
},
"Error during configuration: ": {
"fr": "Erreur lors de la configuration : ",
"en": "Error during configuration: ",
},
# GPT code - Claude automation
"Add an automation with Claude in todo.py": {
"fr": "Ajouter une automatisation avec Claude dans todo.py",
"en": "Add an automation with Claude in todo.py",
},
"Description of the command to add: ": {
"fr": "Description de la commande à ajouter : ",
"en": "Description of the command to add: ",
},
"Bash command to execute: ": {
"fr": "Commande bash à exécuter : ",
"en": "Bash command to execute: ",
},
"Menu section (git/code/config/network/process): ": {
"fr": "Section du menu (git/code/config/network/process) : ",
"en": "Menu section (git/code/config/network/process): ",
},
"Automation added successfully in todo.json!": {
"fr": "Automatisation ajoutée avec succès dans todo.json!",
"en": "Automation added successfully in todo.json!",
},
"Error adding automation: ": {
"fr": "Erreur lors de l'ajout de l'automatisation : ",
"en": "Error adding automation: ",
},
# Language selection
"lang_prompt": {
"Choose language / Choisir la langue": {
"fr": "Choisir la langue / Choose language",
"en": "Choose language / Choisir la langue",
},
"lang_french": {
"French": {
"fr": "Francais",
"en": "French",
},
"lang_english": {
"English": {
"fr": "Anglais",
"en": "English",
},
"lang_changed": {
"Language changed to: English": {
"fr": "Langue changée pour : Francais",
"en": "Language changed to: English",
},
"execution_time": {
"TODO execution time": {
"fr": "Temps d'exécution TODO",
"en": "TODO execution time",
},
"keyboard_interrupt": {
"Keyboard interrupt": {
"fr": "Interruption clavier",
"en": "Keyboard interrupt",
},
# GPT code section
"menu_gpt_code": {
"GPT code - AI assistant tools": {
"fr": "GPT code - Outils d'assistant IA",
"en": "GPT code - AI assistant tools",
},
"gpt_code_manage": {
"AI assistant tools for development!": {
"fr": "Outils d'assistant IA pour le développement!",
"en": "AI assistant tools for development!",
},
"gpt_code_claude_commit": {
"fr": "Configurer le commit Claude Code",
"en": "Configure Claude Code commit",
"Configure Claude Code configurations": {
"fr": "Configurer les configurations Claude Code",
"en": "Configure Claude Code configurations",
},
"gpt_code_enter_name": {
"Commit - OCA/Odoo commit command": {
"fr": "Commit - Commande de commit OCA/Odoo",
"en": "Commit - OCA/Odoo commit command",
},
"Todo Add Command - Add a command to todo.py menu": {
"fr": "Todo Add Command - Ajouter une commande au menu todo.py",
"en": "Todo Add Command - Add a command to todo.py menu",
},
"Enter your full name: ": {
"fr": "Entrez votre nom complet : ",
"en": "Enter your full name: ",
},
"gpt_code_enter_email": {
"Enter your email: ": {
"fr": "Entrez votre courriel : ",
"en": "Enter your email: ",
},
"gpt_code_commit_exists": {
"fr": "Le fichier ~/.claude/commands/commit.md existe déjà. Aucune action effectuée.",
"en": "File ~/.claude/commands/commit.md already exists. No action taken.",
"Deploy Claude Code commands!": {
"fr": "Déployer les commandes Claude Code!",
"en": "Deploy Claude Code commands!",
},
"gpt_code_commit_created": {
"fr": "Fichier ~/.claude/commands/commit.md créé avec succès!",
"en": "File ~/.claude/commands/commit.md created successfully!",
"Show installed custom commands": {
"fr": "Afficher les commandes personnalisées installées",
"en": "Show installed custom commands",
},
"gpt_code_commit_error": {
"No custom commands found in ~/.claude/commands/": {
"fr": "Aucune commande personnalisée trouvée dans ~/.claude/commands/",
"en": "No custom commands found in ~/.claude/commands/",
},
"Claude Code custom commands:": {
"fr": "Commandes personnalisées Claude Code :",
"en": "Claude Code custom commands:",
},
"Total:": {
"fr": "Total :",
"en": "Total:",
},
"File already exists: ": {
"fr": "Le fichier existe déjà : ",
"en": "File already exists: ",
},
"Do you want to overwrite the file? (y/Y): ": {
"fr": "Voulez-vous écraser le fichier? (y/Y) : ",
"en": "Do you want to overwrite the file? (y/Y): ",
},
"Nothing to do.": {
"fr": "Rien à faire.",
"en": "Nothing to do.",
},
"File created successfully: ": {
"fr": "Fichier créé avec succès : ",
"en": "File created successfully: ",
},
"Error creating file: ": {
"fr": "Erreur lors de la création du fichier : ",
"en": "Error creating file: ",
},