feat(pack,docs): pack de démarrage 20 prompts, README, tests, CI, build
ci / verifier-pack (push) Successful in 1m29s
ci / verifier-pack (push) Successful in 1m29s
- packs/essentiels : 20 prompts en 4 catégories (Général, Écriture, Code, Analyse), en-têtes conformes au parseur, variables déclarées = utilisées - tests/run_tests.ahk : suites Frontmatter, Variables, JSON, Recherche, encodage UTF-8 (à exécuter sous Windows avec AutoHotkey v2) - scripts/check_pack.py + .gitea/workflows/ci.yml : cohérence du pack - build.ps1 : tests puis compilation Ahk2Exe et zip portable - docs : document d'architecture et de conception v1.0
This commit is contained in:
@@ -0,0 +1,21 @@
|
|||||||
|
# PromptKey — CI Gitea Actions
|
||||||
|
# Sur chaque push : présence des fichiers attendus et cohérence du pack
|
||||||
|
# de démarrage (20 prompts, en-têtes avec id + title, variables déclarées).
|
||||||
|
# Le contrôle lui-même vit dans scripts/check_pack.py (exécutable aussi
|
||||||
|
# en local : python3 scripts/check_pack.py).
|
||||||
|
|
||||||
|
name: ci
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
verifier-pack:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Récupérer le dépôt
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Vérifier les fichiers et le pack de prompts
|
||||||
|
run: python3 scripts/check_pack.py
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
# PromptKey — fichiers locaux, jamais versionnés
|
||||||
|
|
||||||
|
# Données d'usage et caches (régénérés automatiquement)
|
||||||
|
usage.json
|
||||||
|
index-cache.json
|
||||||
|
**/usage.json
|
||||||
|
**/index-cache.json
|
||||||
|
|
||||||
|
# Journal d'exécution
|
||||||
|
promptkey.log
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Paramètres locaux de la machine
|
||||||
|
config.ini
|
||||||
|
|
||||||
|
# Binaires compilés et livrables
|
||||||
|
*.exe
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# Bibliothèque locale de test (les vrais prompts vivent dans leur propre dossier/dépôt)
|
||||||
|
Bibliotheque-test/
|
||||||
|
Bibliothèque-test/
|
||||||
|
bibliotheque-test/
|
||||||
|
|
||||||
|
# Sauvegardes créées par l'Éditeur
|
||||||
|
*.bak
|
||||||
|
|
||||||
|
# Divers
|
||||||
|
Thumbs.db
|
||||||
|
desktop.ini
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Journal des changements — PromptKey
|
||||||
|
|
||||||
|
Le format suit [Keep a Changelog](https://keepachangelog.com/fr/1.0.0/) et le projet utilise le versionnage sémantique.
|
||||||
|
|
||||||
|
## [1.0.0] — 2026-10-11
|
||||||
|
|
||||||
|
Première version.
|
||||||
|
|
||||||
|
### Ajouté
|
||||||
|
|
||||||
|
- Application tray AutoHotkey v2 : instance unique, aucun appel réseau, démarrage avec Windows sur demande.
|
||||||
|
- Quick Picker (`Ctrl+Alt+Espace`) : recherche floue instantanée insensible aux accents, aperçu du prompt, navigation entièrement au clavier, retour du focus à l'application d'origine.
|
||||||
|
- Bibliothèque de prompts en fichiers Markdown : un prompt = un fichier `.md`, catégorie = dossier de premier niveau, en-tête structuré (id, title, tags, targets, favorite, épinglé, raccourci propre, hotstring, variables).
|
||||||
|
- Gestionnaire et Éditeur intégrés : créer, modifier, dupliquer, supprimer (corbeille Windows), créer un prompt depuis la sélection ou le presse-papiers, validation des variables en direct.
|
||||||
|
- Moteur de variables : types `text`, `multiline`, `enum`, `number`, `date`, `bool`, `password` ; variables automatiques `{{selection}}`, `{{clipboard}}`, `{{date}}`, `{{datetime}}`, `{{time}}`, `{{app}}`, `{{cursor}}` ; mémorisation des derniers choix par prompt (sauf `password`).
|
||||||
|
- Moteur d'injection : presse-papiers sauvegardé puis restauré (méthode par défaut), frappe simulée (`SendText`) en repli, aucun envoi automatique par défaut.
|
||||||
|
- Profils de destination par IA (Claude, ChatGPT, Gemini, Qwen, DeepSeek, terminal, courriel…) : préambule, méthode d'injection, délais, option « envoyer après insertion » gardée et désactivée par défaut.
|
||||||
|
- Raccourcis globaux configurables, épinglés `Ctrl+Alt+1…9`, hotstrings (`::resu;`…), rapport de conflits de raccourcis.
|
||||||
|
- Historique d'usage local (`usage.json`) alimentant les listes Épinglés / Récents / Fréquents du Picker.
|
||||||
|
- Import/export de prompts (Markdown, dossiers, zip, JSON d'échange) et mode portable intégral.
|
||||||
|
- Pack de démarrage « essentiels » : 20 prompts en français, 4 catégories (Général, Écriture, Code, Analyse), dans `packs/essentiels/`.
|
||||||
|
- Compilation reproductible par `build.ps1` (tests, Ahk2Exe, archive portable, sommes SHA256) et CI Gitea Actions vérifiant la cohérence du pack (`scripts/check_pack.py`).
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 Bruno
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
# PromptKey
|
||||||
|
|
||||||
|
**Gestionnaire de prompts pour intelligences artificielles, sous Windows, écrit en AutoHotkey v2.**
|
||||||
|
|
||||||
|
PromptKey vit dans la zone de notification et insère, en quelques frappes, le bon prompt dans n'importe quelle application — ChatGPT, Claude, Gemini, Qwen, DeepSeek, un terminal, un éditeur, un courriel. Une seule bibliothèque de prompts, pour toutes les IA.
|
||||||
|
|
||||||
|
## Le geste central
|
||||||
|
|
||||||
|
```
|
||||||
|
Ctrl+Alt+Espace → taper « résu… » → Entrée → remplir 1-2 variables si demandé → le prompt est collé
|
||||||
|
```
|
||||||
|
|
||||||
|
Le texte sélectionné dans l'application d'origine est capturé automatiquement (`{{selection}}`) ; PromptKey rend ensuite le focus à l'application et colle le prompt résolu. Il **insère** le texte — il ne l'envoie jamais à votre place (sauf option explicite par profil, désactivée par défaut).
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Prérequis :
|
||||||
|
|
||||||
|
- Windows 10 ou 11
|
||||||
|
- [AutoHotkey v2.0](https://www.autohotkey.com/) (64 bits) — seulement pour lancer la version source ou recompiler
|
||||||
|
|
||||||
|
Deux façons de lancer :
|
||||||
|
|
||||||
|
1. **Version compilée** : `PromptKey.exe` (voir « Compilation » ci-dessous ou l'archive portable `dist\PromptKey-portable.zip`). Aucune installation : double-cliquer, l'icône apparaît dans la zone de notification.
|
||||||
|
2. **Version source** : double-cliquer sur `PromptKey.ahk`, ou `AutoHotkey64.exe PromptKey.ahk` en ligne de commande.
|
||||||
|
|
||||||
|
Premier lancement : le dossier de bibliothèque est créé automatiquement (`Documents\PromptKey\Bibliothèque`, ou le dossier `Bibliothèque\` à côté du programme en mode portable) et, s'il est vide, le **pack de démarrage** (`packs\essentiels\`, 20 prompts prêts à l'emploi en 4 catégories : Général, Écriture, Code, Analyse) y est recopié. Le dossier peut ensuite être changé dans les Paramètres.
|
||||||
|
|
||||||
|
Pour démarrer PromptKey avec Windows : créer un raccourci vers `PromptKey.ahk` (ou l'exécutable compilé) dans le dossier Démarrage (`shell:startup`).
|
||||||
|
|
||||||
|
## Raccourcis par défaut
|
||||||
|
|
||||||
|
| Raccourci | Action |
|
||||||
|
|-----------|--------|
|
||||||
|
| `Ctrl+Alt+Espace` | Ouvrir le Quick Picker (rechercher et insérer un prompt) |
|
||||||
|
| `Ctrl+Alt+M` | Ouvrir le Gestionnaire (bibliothèque complète) |
|
||||||
|
| `Ctrl+Alt+N` | Nouveau prompt (depuis la sélection, s'il y en a une) |
|
||||||
|
| `Ctrl+Alt+1` … `Ctrl+Alt+9` | Insérer directement le prompt épinglé n° 1…9 |
|
||||||
|
| `Ctrl+Alt+V` | Réinsérer le dernier prompt utilisé |
|
||||||
|
|
||||||
|
Dans le Picker : `↑`/`↓` naviguer, `Entrée` utiliser, `Ctrl+Entrée` utiliser avec les valeurs par défaut, `Ctrl+E` modifier, `Échap` fermer sans rien insérer.
|
||||||
|
|
||||||
|
Chaque prompt peut aussi avoir son propre raccourci (`hotkey:`) ou une abréviation tapée (`hotstring:`, ex. taper `resu;` insère le prompt « Résumer en 5 points »). Tous les raccourcis sont modifiables dans les Paramètres.
|
||||||
|
|
||||||
|
## Format d'un prompt
|
||||||
|
|
||||||
|
**Un prompt = un fichier Markdown.** La catégorie est le dossier qui le contient ; l'en-tête entre `---` décrit le prompt, le corps est le texte du prompt avec ses variables `{{…}}` :
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
id: 96d0d5b7-7a07-43a1-af84-dfa7024b4bcf
|
||||||
|
title: Résumer en 5 points
|
||||||
|
tags: [résumé, synthèse, court]
|
||||||
|
targets: [défaut, claude, chatgpt, gemini, qwen]
|
||||||
|
favorite: true
|
||||||
|
created: 2026-10-11
|
||||||
|
updated: 2026-10-11
|
||||||
|
variables:
|
||||||
|
- name: ton
|
||||||
|
label: Ton de la réponse
|
||||||
|
type: enum
|
||||||
|
choices: [neutre, formel, amical, direct]
|
||||||
|
default: neutre
|
||||||
|
---
|
||||||
|
|
||||||
|
Résume le texte suivant, ton {{ton}}.
|
||||||
|
|
||||||
|
Texte :
|
||||||
|
{{selection}}
|
||||||
|
```
|
||||||
|
|
||||||
|
Variables automatiques (jamais demandées) : `{{selection}}` (texte sélectionné), `{{clipboard}}` (presse-papiers), `{{date}}`, `{{datetime}}`, `{{time}}`, `{{app}}` (application d'origine), `{{cursor}}` (position du curseur après insertion).
|
||||||
|
|
||||||
|
Le format complet (sous-ensemble d'en-tête accepté, profils de destination, moteur d'injection) est spécifié dans le document d'architecture et de conception de PromptKey, §7 et suivants.
|
||||||
|
|
||||||
|
## Structure du projet
|
||||||
|
|
||||||
|
```
|
||||||
|
PromptKey\
|
||||||
|
├─ PromptKey.ahk point d'entrée (tray, démarrage)
|
||||||
|
├─ lib\ services : magasin de prompts, index/recherche,
|
||||||
|
│ moteur de variables, injecteur, presse-papiers…
|
||||||
|
├─ gui\ Quick Picker, Gestionnaire, Éditeur, formulaires
|
||||||
|
├─ packs\essentiels\ pack de démarrage : 20 prompts, 4 catégories
|
||||||
|
├─ scripts\check_pack.py contrôle de cohérence du pack (utilisé par la CI)
|
||||||
|
├─ tests\ suites de tests AutoHotkey
|
||||||
|
├─ build.ps1 compilation + archive portable (voir ci-dessous)
|
||||||
|
├─ config.ini paramètres (créé au premier lancement, local)
|
||||||
|
└─ README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
La bibliothèque de prompts peut vivre ailleurs (dossier au choix dans les Paramètres) ; elle est faite de simples fichiers `.md`, versionnables sous Git — ce dépôt en est la démonstration.
|
||||||
|
|
||||||
|
## Compilation
|
||||||
|
|
||||||
|
Avec PowerShell, à la racine du projet :
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\build.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
Le script vérifie la présence d'AutoHotkey v2 et d'**Ahk2Exe**, lance les tests de `tests\` s'ils sont présents, compile `PromptKey.ahk` en `dist\PromptKey.exe`, puis assemble l'archive portable `dist\PromptKey-portable.zip` (exe + packs + README + LICENSE).
|
||||||
|
|
||||||
|
Ahk2Exe est fourni avec AutoHotkey v2 (dossier `Compiler\`) ou via le programme d'installation d'AutoHotkey (option « Compile »).
|
||||||
|
|
||||||
|
## Sécurité et vie privée
|
||||||
|
|
||||||
|
- **Zéro réseau.** PromptKey n'appelle aucun service, n'envoie aucune télémétrie, ne parle à aucune API d'IA : il prépare du texte et le colle localement.
|
||||||
|
- **Presse-papiers restauré.** L'injection par presse-papiers sauvegarde son contenu, colle le prompt, puis restaure l'original après un court délai (abandon propre si vous avez copié autre chose entre-temps). Les prompts marqués `sensitive: true` vident le presse-papiers après injection au lieu de le restaurer.
|
||||||
|
- **Aucun contenu journalisé.** Le journal ne contient ni prompts, ni sélections, ni valeurs de variables ; les variables de type `password` ne sont jamais persistées.
|
||||||
|
- **Ne mettez pas de secrets dans vos prompts** : ce sont des fichiers texte en clair, pensés pour être versionnés.
|
||||||
|
|
||||||
|
## Licence
|
||||||
|
|
||||||
|
MIT — voir [LICENSE](LICENSE). Copyright © 2026 Bruno.
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
# PromptKey — compilation et assemblage de l'archive portable.
|
||||||
|
#
|
||||||
|
# Usage : .\build.ps1
|
||||||
|
#
|
||||||
|
# Étapes :
|
||||||
|
# 1. Vérifie la présence d'AutoHotkey v2 et du compilateur Ahk2Exe.
|
||||||
|
# 2. Lance les scripts de tests\ s'il y en a (échec = arrêt du build).
|
||||||
|
# 3. Compile PromptKey.ahk en dist\PromptKey.exe.
|
||||||
|
# 4. Assemble dist\PromptKey-portable.zip (exe + packs + README + LICENSE).
|
||||||
|
# 5. Affiche la somme SHA256 de l'exécutable.
|
||||||
|
#
|
||||||
|
# Prérequis : AutoHotkey v2.0 (64 bits) installé, Ahk2Exe fourni avec
|
||||||
|
# (dossier Compiler\ de l'installation AutoHotkey).
|
||||||
|
|
||||||
|
#Requires -Version 5.1
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
$Racine = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||||
|
Set-Location $Racine
|
||||||
|
|
||||||
|
function Echouer($Message) {
|
||||||
|
Write-Host "ERREUR : $Message" -ForegroundColor Red
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- 1. Localiser AutoHotkey v2 et Ahk2Exe ----------------------------------
|
||||||
|
$AhkExe = $null
|
||||||
|
$Ahk2Exe = $null
|
||||||
|
$Candidats = @(
|
||||||
|
"$env:ProgramFiles\AutoHotkey",
|
||||||
|
"${env:ProgramFiles(x86)}\AutoHotkey",
|
||||||
|
"$env:LOCALAPPDATA\Programs\AutoHotkey"
|
||||||
|
)
|
||||||
|
foreach ($Dossier in $Candidats) {
|
||||||
|
if (Test-Path $Dossier) {
|
||||||
|
$Trouve = Get-ChildItem -Path $Dossier -Recurse -Filter 'AutoHotkey64.exe' -ErrorAction SilentlyContinue | Select-Object -First 1
|
||||||
|
if ($Trouve) { $AhkExe = $Trouve.FullName }
|
||||||
|
$Trouve = Get-ChildItem -Path $Dossier -Recurse -Filter 'Ahk2Exe.exe' -ErrorAction SilentlyContinue | Select-Object -First 1
|
||||||
|
if ($Trouve) { $Ahk2Exe = $Trouve.FullName }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (-not $AhkExe) { Echouer "AutoHotkey v2 introuvable. Installez AutoHotkey v2.0 (64 bits) depuis https://www.autohotkey.com/" }
|
||||||
|
if (-not $Ahk2Exe) { Echouer "Ahk2Exe introuvable. Il est fourni avec AutoHotkey v2 (dossier Compiler\) ; réinstallez en incluant l'option de compilation." }
|
||||||
|
|
||||||
|
# Vérifier qu'il s'agit bien de la v2 (le champ FileVersion commence par 2.)
|
||||||
|
$Version = (Get-Item $AhkExe).VersionInfo.FileVersion
|
||||||
|
if ($Version -notmatch '^2\.') { Echouer "AutoHotkey v2 requis, version trouvée : $Version ($AhkExe)" }
|
||||||
|
Write-Host "AutoHotkey $Version : $AhkExe"
|
||||||
|
Write-Host "Ahk2Exe : $Ahk2Exe"
|
||||||
|
|
||||||
|
# --- 2. Tests (si présents) --------------------------------------------------
|
||||||
|
$DossierTests = Join-Path $Racine 'tests'
|
||||||
|
if (Test-Path $DossierTests) {
|
||||||
|
$Scripts = Get-ChildItem -Path $DossierTests -Filter '*.ahk'
|
||||||
|
foreach ($Script in $Scripts) {
|
||||||
|
Write-Host "Test : $($Script.Name)"
|
||||||
|
& $AhkExe /ErrorStdOut $Script.FullName
|
||||||
|
if ($LASTEXITCODE -ne 0) { Echouer "Échec du test $($Script.Name) (code $LASTEXITCODE). Build interrompu." }
|
||||||
|
}
|
||||||
|
if ($Scripts.Count -eq 0) { Write-Host "Aucun script de test dans tests\ — étape sautée." }
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- 3. Compilation ----------------------------------------------------------
|
||||||
|
$Dist = Join-Path $Racine 'dist'
|
||||||
|
if (-not (Test-Path $Dist)) { New-Item -ItemType Directory -Path $Dist | Out-Null }
|
||||||
|
$Source = Join-Path $Racine 'PromptKey.ahk'
|
||||||
|
if (-not (Test-Path $Source)) { Echouer "Fichier source introuvable : $Source" }
|
||||||
|
$Exe = Join-Path $Dist 'PromptKey.exe'
|
||||||
|
if (Test-Path $Exe) { Remove-Item $Exe -Force }
|
||||||
|
|
||||||
|
Write-Host "Compilation de PromptKey.ahk ..."
|
||||||
|
& $Ahk2Exe /in $Source /out $Exe /base $AhkExe
|
||||||
|
if ($LASTEXITCODE -ne 0 -or -not (Test-Path $Exe)) { Echouer "La compilation a échoué." }
|
||||||
|
Write-Host "Exécutable produit : $Exe"
|
||||||
|
|
||||||
|
# --- 4. Archive portable -----------------------------------------------------
|
||||||
|
$Zip = Join-Path $Dist 'PromptKey-portable.zip'
|
||||||
|
if (Test-Path $Zip) { Remove-Item $Zip -Force }
|
||||||
|
$AInclure = @($Exe, (Join-Path $Racine 'README.md'), (Join-Path $Racine 'LICENSE'), (Join-Path $Racine 'CHANGELOG.md'), (Join-Path $Racine 'packs'))
|
||||||
|
Compress-Archive -Path $AInclure -DestinationPath $Zip
|
||||||
|
Write-Host "Archive portable : $Zip"
|
||||||
|
|
||||||
|
# --- 5. Somme de contrôle ----------------------------------------------------
|
||||||
|
$Hash = Get-FileHash -Algorithm SHA256 $Exe
|
||||||
|
Write-Host "SHA256 de l'exe : $($Hash.Hash)"
|
||||||
|
Write-Host "Build terminé."
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
id: 712359c5-f741-4439-8ca9-a795d7ee41b8
|
||||||
|
title: Comparer deux options
|
||||||
|
tags: [analyse, comparaison, décision]
|
||||||
|
targets: [défaut, claude, chatgpt, gemini, qwen]
|
||||||
|
favorite: false
|
||||||
|
created: 2026-10-11
|
||||||
|
updated: 2026-10-11
|
||||||
|
variables:
|
||||||
|
- name: contexte
|
||||||
|
label: Mes critères de choix
|
||||||
|
type: multiline
|
||||||
|
required: false
|
||||||
|
---
|
||||||
|
|
||||||
|
Compare les deux options décrites ci-dessous, selon mes critères : {{contexte}}
|
||||||
|
|
||||||
|
- Présente un tableau Markdown : Critère | Option A | Option B — une ligne par critère pertinent, en commençant par mes critères s'ils sont fournis.
|
||||||
|
- Après le tableau, donne les avantages décisifs de chaque option en une phrase.
|
||||||
|
- Termine par une recommandation argumentée, et précise quelle information supplémentaire pourrait la faire changer.
|
||||||
|
|
||||||
|
Options :
|
||||||
|
{{selection}}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
id: 0f5a51ee-e852-4a26-8fda-a5def71136eb
|
||||||
|
title: Extraire décisions et tâches
|
||||||
|
tags: [compte rendu, décisions, tâches, extraction]
|
||||||
|
targets: [défaut, claude, chatgpt, gemini, qwen]
|
||||||
|
favorite: false
|
||||||
|
created: 2026-10-11
|
||||||
|
updated: 2026-10-11
|
||||||
|
---
|
||||||
|
|
||||||
|
À partir du compte rendu ci-dessous, extrais uniquement ce qui y figure réellement :
|
||||||
|
|
||||||
|
1. **Décisions prises** — liste, une décision par ligne, formulée comme dans le texte.
|
||||||
|
2. **Tâches** — tableau Markdown : Tâche | Responsable | Échéance. Si le responsable ou l'échéance n'est pas indiqué, écris « à définir » — n'invente rien.
|
||||||
|
3. **Points en suspens** — questions ouvertes ou sujets reportés.
|
||||||
|
|
||||||
|
Compte rendu :
|
||||||
|
{{selection}}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
id: c2bf8e1f-0ab4-45a5-990a-132ca10ebe2f
|
||||||
|
title: Pour / contre structuré
|
||||||
|
tags: [analyse, décision, pour-contre]
|
||||||
|
targets: [défaut, claude, chatgpt, gemini, qwen]
|
||||||
|
favorite: false
|
||||||
|
created: 2026-10-11
|
||||||
|
updated: 2026-10-11
|
||||||
|
variables:
|
||||||
|
- name: contexte
|
||||||
|
label: Contexte ou critères personnels
|
||||||
|
type: multiline
|
||||||
|
required: false
|
||||||
|
---
|
||||||
|
|
||||||
|
Analyse la décision ou le sujet ci-dessous de façon structurée.
|
||||||
|
|
||||||
|
1. **Pour** — les arguments favorables, du plus fort au plus faible.
|
||||||
|
2. **Contre** — les arguments défavorables, du plus fort au plus faible.
|
||||||
|
3. **Conditions** — ce qui ferait pencher la balance d'un côté ou de l'autre.
|
||||||
|
4. **Conclusion** — une recommandation en deux phrases, en tenant compte du contexte fourni (s'il y en a un).
|
||||||
|
|
||||||
|
Contexte : {{contexte}}
|
||||||
|
|
||||||
|
Sujet :
|
||||||
|
{{selection}}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
id: 77d2e25e-08fb-454e-9178-973248fdd7e9
|
||||||
|
title: Résumé exécutif
|
||||||
|
tags: [analyse, résumé, direction]
|
||||||
|
targets: [défaut, claude, chatgpt, gemini, qwen]
|
||||||
|
favorite: false
|
||||||
|
created: 2026-10-11
|
||||||
|
updated: 2026-10-11
|
||||||
|
variables:
|
||||||
|
- name: longueur
|
||||||
|
label: Longueur visée
|
||||||
|
type: enum
|
||||||
|
choices: ["1 page", "10 lignes", "5 lignes"]
|
||||||
|
default: "1 page"
|
||||||
|
- name: public
|
||||||
|
label: Public du résumé
|
||||||
|
type: enum
|
||||||
|
choices: [direction, équipe, client]
|
||||||
|
default: direction
|
||||||
|
---
|
||||||
|
|
||||||
|
Rédige un résumé exécutif du document ci-dessous (longueur : {{longueur}} ; public : {{public}}).
|
||||||
|
|
||||||
|
- Ouvre sur la conclusion ou la décision essentielle, pas sur le contexte.
|
||||||
|
- Puis : les 3 à 5 faits qui comptent, les risques ou points d'attention, et la suite attendue.
|
||||||
|
- Style factuel, phrases courtes, aucun jargon inutile ; conserve les chiffres clés.
|
||||||
|
- N'ajoute aucune information absente du document.
|
||||||
|
|
||||||
|
Document :
|
||||||
|
{{selection}}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
id: 7ffb9069-4566-4741-b0f0-1409ec1fb10c
|
||||||
|
title: Transformer en tableau Markdown
|
||||||
|
tags: [analyse, tableau, markdown, données]
|
||||||
|
targets: [défaut, claude, chatgpt, gemini, qwen]
|
||||||
|
favorite: false
|
||||||
|
created: 2026-10-11
|
||||||
|
updated: 2026-10-11
|
||||||
|
variables:
|
||||||
|
- name: tri
|
||||||
|
label: Tri des lignes
|
||||||
|
type: enum
|
||||||
|
choices: [ordre d'apparition, alphabétique, aucun]
|
||||||
|
default: ordre d'apparition
|
||||||
|
---
|
||||||
|
|
||||||
|
Transforme les données ci-dessous en un tableau Markdown propre (tri des lignes : {{tri}}).
|
||||||
|
|
||||||
|
- Déduis les colonnes du contenu ; en-têtes courts et explicites.
|
||||||
|
- Une information par cellule ; si une valeur manque, laisse la cellule vide.
|
||||||
|
- Aligne les nombres à droite si le format le permet.
|
||||||
|
- Donne uniquement le tableau, sans commentaire avant ni après.
|
||||||
|
|
||||||
|
Données :
|
||||||
|
{{selection}}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
id: bb28f77f-bf2a-4f46-bd3f-e18be6e47736
|
||||||
|
title: Convertir dans un autre langage
|
||||||
|
tags: [code, conversion, portage]
|
||||||
|
targets: [défaut, claude, chatgpt, qwen, deepseek]
|
||||||
|
favorite: false
|
||||||
|
created: 2026-10-11
|
||||||
|
updated: 2026-10-11
|
||||||
|
variables:
|
||||||
|
- name: langage_cible
|
||||||
|
label: Langage cible
|
||||||
|
type: enum
|
||||||
|
choices: [Python, JavaScript, TypeScript, Go, Rust, AutoHotkey v2]
|
||||||
|
default: Python
|
||||||
|
---
|
||||||
|
|
||||||
|
Convertis le code ci-dessous en {{langage_cible}}.
|
||||||
|
|
||||||
|
- Comportement identique : mêmes entrées, mêmes sorties, mêmes cas d'erreur.
|
||||||
|
- Code idiomatique dans le langage cible, pas une traduction mot à mot.
|
||||||
|
- Si une bibliothèque d'origine n'a pas d'équivalent direct, signale-le en commentaire à l'endroit concerné.
|
||||||
|
- Donne uniquement le code converti, avec un court commentaire d'en-tête indiquant la source.
|
||||||
|
|
||||||
|
Code source :
|
||||||
|
{{selection}}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
id: 747677e2-8379-4e93-b760-64711fb43331
|
||||||
|
title: Écrire un test pour cette fonction
|
||||||
|
tags: [code, tests, qualité]
|
||||||
|
targets: [défaut, claude, chatgpt, qwen, deepseek]
|
||||||
|
favorite: false
|
||||||
|
created: 2026-10-11
|
||||||
|
updated: 2026-10-11
|
||||||
|
variables:
|
||||||
|
- name: langage
|
||||||
|
label: Langage du code
|
||||||
|
type: enum
|
||||||
|
choices: [Python, JavaScript, TypeScript, AutoHotkey, PHP, autre]
|
||||||
|
default: Python
|
||||||
|
- name: type_test
|
||||||
|
label: Type de test
|
||||||
|
type: enum
|
||||||
|
choices: [unitaire, intégration, cas limites]
|
||||||
|
default: unitaire
|
||||||
|
---
|
||||||
|
|
||||||
|
Écris un test {{type_test}} pour la fonction {{langage}} ci-dessous.
|
||||||
|
|
||||||
|
- Utilise le framework de test usuel du langage, sans l'importer si ce n'est pas nécessaire à la lisibilité.
|
||||||
|
- Couvre le cas nominal et, selon le type choisi, les entrées vides, nulles ou aux bornes.
|
||||||
|
- Noms de tests explicites ; un seul comportement vérifié par test.
|
||||||
|
- Donne uniquement le code du test, précédé d'une ligne de commentaire indiquant où le placer.
|
||||||
|
|
||||||
|
Fonction :
|
||||||
|
{{selection}}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
id: e0b91c9f-557f-4da1-8d48-734844a61102
|
||||||
|
title: Expliquer ce code
|
||||||
|
tags: [code, explication, pédagogie]
|
||||||
|
targets: [défaut, claude, chatgpt, qwen, deepseek]
|
||||||
|
favorite: false
|
||||||
|
created: 2026-10-11
|
||||||
|
updated: 2026-10-11
|
||||||
|
variables:
|
||||||
|
- name: detail
|
||||||
|
label: Niveau de détail
|
||||||
|
type: enum
|
||||||
|
choices: [par blocs, ligne par ligne, résumé puis détail]
|
||||||
|
default: par blocs
|
||||||
|
---
|
||||||
|
|
||||||
|
Explique le code ci-dessous (détail demandé : {{detail}}).
|
||||||
|
|
||||||
|
- Commence par ce que le code fait, en une phrase.
|
||||||
|
- Puis explique chaque partie dans l'ordre, en nommant le rôle des variables importantes.
|
||||||
|
- Signale les effets de bord, les hypothèses implicites et les points fragiles.
|
||||||
|
- Termine par un exemple d'entrée et le résultat attendu.
|
||||||
|
|
||||||
|
Code :
|
||||||
|
{{selection}}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
id: 992e31fa-6259-4716-b968-d0845f7f091a
|
||||||
|
title: Message de commit à partir du diff
|
||||||
|
tags: [git, commit, diff]
|
||||||
|
targets: [défaut, claude, chatgpt, qwen, deepseek]
|
||||||
|
favorite: false
|
||||||
|
created: 2026-10-11
|
||||||
|
updated: 2026-10-11
|
||||||
|
variables:
|
||||||
|
- name: style
|
||||||
|
label: Style du message
|
||||||
|
type: enum
|
||||||
|
choices: [conventionnel, simple, détaillé]
|
||||||
|
default: conventionnel
|
||||||
|
---
|
||||||
|
|
||||||
|
À partir du diff ci-dessous (presse-papiers), écris un message de commit (style : {{style}}).
|
||||||
|
|
||||||
|
- Style « conventionnel » = préfixe de type (`feat:`, `fix:`, `docs:`…) suivi d'un résumé à l'impératif, 72 caractères au maximum.
|
||||||
|
- Style « détaillé » = résumé, ligne vide, puis 2 à 4 puces expliquant le pourquoi des changements.
|
||||||
|
- Décris l'intention du changement, pas la liste mécanique des fichiers.
|
||||||
|
- Donne uniquement le message, sans guillemets ni bloc de code.
|
||||||
|
|
||||||
|
Diff :
|
||||||
|
{{clipboard}}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
id: 87aa26f5-ad2b-497e-b382-0b49bb50cd3c
|
||||||
|
title: Revue de code
|
||||||
|
tags: [code, revue, qualité]
|
||||||
|
targets: [défaut, claude, chatgpt, qwen, deepseek]
|
||||||
|
favorite: true
|
||||||
|
created: 2026-10-11
|
||||||
|
updated: 2026-10-11
|
||||||
|
variables:
|
||||||
|
- name: langage
|
||||||
|
label: Langage du code
|
||||||
|
type: enum
|
||||||
|
choices: [Python, JavaScript, TypeScript, AutoHotkey, PHP, autre]
|
||||||
|
default: Python
|
||||||
|
- name: niveau
|
||||||
|
label: Niveau de sévérité
|
||||||
|
type: enum
|
||||||
|
choices: [rapide, standard, strict]
|
||||||
|
default: standard
|
||||||
|
---
|
||||||
|
|
||||||
|
Tu es un relecteur de code senior. Relis le code {{langage}} suivant (niveau de revue : {{niveau}}) et signale, par ordre de gravité :
|
||||||
|
1. les bogues probables et les cas limites non gérés ;
|
||||||
|
2. les risques de sécurité ou de perte de données ;
|
||||||
|
3. les problèmes de lisibilité qui gênent la maintenance.
|
||||||
|
|
||||||
|
Pour chaque point : explique le problème en une phrase et propose la correction minimale. Termine par un verdict en une ligne : prêt, à corriger, ou à retravailler. Ne réécris pas tout le code.
|
||||||
|
|
||||||
|
Code :
|
||||||
|
{{selection}}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
id: 8d5b0278-9098-41af-b964-f0ef95b003b4
|
||||||
|
title: Expliquer simplement
|
||||||
|
tags: [explication, vulgarisation]
|
||||||
|
targets: [défaut, claude, chatgpt, gemini, qwen]
|
||||||
|
favorite: false
|
||||||
|
created: 2026-10-11
|
||||||
|
updated: 2026-10-11
|
||||||
|
variables:
|
||||||
|
- name: public
|
||||||
|
label: Public visé
|
||||||
|
type: enum
|
||||||
|
choices: [grand public, débutant, connaisseur]
|
||||||
|
default: grand public
|
||||||
|
---
|
||||||
|
|
||||||
|
Explique simplement le sujet ou le texte suivant, pour un public « {{public}} ».
|
||||||
|
|
||||||
|
- Commence par l'idée essentielle en une phrase.
|
||||||
|
- Utilise un exemple concret de la vie courante.
|
||||||
|
- Évite le jargon ; quand un terme technique est indispensable, définis-le en quelques mots.
|
||||||
|
- Termine par un résumé en trois points au maximum.
|
||||||
|
- Réponds en français.
|
||||||
|
|
||||||
|
Sujet ou texte à expliquer :
|
||||||
|
{{selection}}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
id: 9da30617-2cc8-4b1d-94ad-2fd92030ee9c
|
||||||
|
title: Expliquer une erreur
|
||||||
|
tags: [débogage, erreur, pédagogie]
|
||||||
|
targets: [défaut, claude, chatgpt, gemini, qwen]
|
||||||
|
favorite: false
|
||||||
|
created: 2026-10-11
|
||||||
|
updated: 2026-10-11
|
||||||
|
variables:
|
||||||
|
- name: niveau
|
||||||
|
label: Mon niveau sur ce sujet
|
||||||
|
type: enum
|
||||||
|
choices: [débutant, intermédiaire, avancé]
|
||||||
|
default: intermédiaire
|
||||||
|
---
|
||||||
|
|
||||||
|
Explique l'erreur ci-dessous à un niveau {{niveau}} :
|
||||||
|
1. ce qu'elle signifie, en une phrase simple ;
|
||||||
|
2. la cause la plus probable dans ce contexte ;
|
||||||
|
3. la correction minimale à appliquer, avec l'exemple corrigé seulement.
|
||||||
|
|
||||||
|
Message d'erreur et contexte :
|
||||||
|
{{selection}}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
id: 3a8e4180-1cbe-4910-b740-2d62215f88e6
|
||||||
|
title: Plan d'action à partir d'un texte
|
||||||
|
tags: [plan, actions, organisation]
|
||||||
|
targets: [défaut, claude, chatgpt, gemini, qwen]
|
||||||
|
favorite: false
|
||||||
|
created: 2026-10-11
|
||||||
|
updated: 2026-10-11
|
||||||
|
variables:
|
||||||
|
- name: horizon
|
||||||
|
label: Horizon du plan
|
||||||
|
type: enum
|
||||||
|
choices: [aujourd'hui, cette semaine, ce mois]
|
||||||
|
default: cette semaine
|
||||||
|
---
|
||||||
|
|
||||||
|
À partir du texte ci-dessous, construis un plan d'action concret pour l'horizon « {{horizon}} ».
|
||||||
|
|
||||||
|
Présente-le ainsi :
|
||||||
|
1. **Objectif** — en une phrase.
|
||||||
|
2. **Étapes** — liste numérotée, chaque étape commence par un verbe d'action et tient en une ligne.
|
||||||
|
3. **Priorités** — classe les étapes en « indispensable » et « si le temps le permet ».
|
||||||
|
4. **Points à clarifier** — ce que le texte ne dit pas et qui bloque une étape.
|
||||||
|
|
||||||
|
N'invente ni échéance ni responsable qui ne figurent pas dans le texte.
|
||||||
|
|
||||||
|
Texte :
|
||||||
|
{{selection}}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
id: 4d799fba-15c0-40b7-b46a-761a373d3fa2
|
||||||
|
title: Questions de clarification
|
||||||
|
tags: [clarification, précision, cadrage]
|
||||||
|
targets: [défaut, claude, chatgpt, gemini, qwen]
|
||||||
|
favorite: false
|
||||||
|
created: 2026-10-11
|
||||||
|
updated: 2026-10-11
|
||||||
|
variables:
|
||||||
|
- name: nombre
|
||||||
|
label: Nombre de questions
|
||||||
|
type: enum
|
||||||
|
choices: ["3", "5", "10"]
|
||||||
|
default: "5"
|
||||||
|
---
|
||||||
|
|
||||||
|
Avant de répondre à la demande ci-dessous, pose exactement {{nombre}} questions de clarification.
|
||||||
|
|
||||||
|
- Choisis les questions dont la réponse changera le plus le résultat.
|
||||||
|
- Formule chaque question de façon courte, avec si possible des choix suggérés entre parenthèses.
|
||||||
|
- Ne réponds pas encore à la demande : termine en invitant à répondre aux questions.
|
||||||
|
|
||||||
|
Demande :
|
||||||
|
{{selection}}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
id: 96d0d5b7-7a07-43a1-af84-dfa7024b4bcf
|
||||||
|
title: Résumer en 5 points
|
||||||
|
tags: [résumé, synthèse, court]
|
||||||
|
targets: [défaut, claude, chatgpt, gemini, qwen]
|
||||||
|
favorite: true
|
||||||
|
pinned: 1
|
||||||
|
hotstring: resu;
|
||||||
|
created: 2026-10-11
|
||||||
|
updated: 2026-10-11
|
||||||
|
variables:
|
||||||
|
- name: ton
|
||||||
|
label: Ton de la réponse
|
||||||
|
type: enum
|
||||||
|
choices: [neutre, formel, amical, direct]
|
||||||
|
default: neutre
|
||||||
|
- name: longueur
|
||||||
|
label: Longueur max
|
||||||
|
type: enum
|
||||||
|
choices: ["5 points", "1 paragraphe", "détaillé"]
|
||||||
|
default: "5 points"
|
||||||
|
---
|
||||||
|
|
||||||
|
Résume le texte suivant en {{longueur}}, ton {{ton}}.
|
||||||
|
Réponds en français. Conserve les faits, les chiffres et les noms propres ; ne reformule pas la première phrase et n'ajoute aucune information absente du texte.
|
||||||
|
|
||||||
|
Texte :
|
||||||
|
{{selection}}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
id: 3cf4a9c9-caf6-4143-ab7c-5c8e098fc3dd
|
||||||
|
title: Corriger sans changer le style
|
||||||
|
tags: [correction, orthographe, style]
|
||||||
|
targets: [défaut, claude, chatgpt, gemini, qwen]
|
||||||
|
favorite: false
|
||||||
|
created: 2026-10-11
|
||||||
|
updated: 2026-10-11
|
||||||
|
---
|
||||||
|
|
||||||
|
Corrige le texte suivant : orthographe, grammaire, ponctuation et fautes de frappe uniquement.
|
||||||
|
|
||||||
|
- Ne change ni le style, ni le ton, ni le vocabulaire, ni l'ordre des phrases.
|
||||||
|
- Conserve la mise en forme (paragraphes, listes, sauts de ligne).
|
||||||
|
- Donne uniquement le texte corrigé, sans explication ; si tu hésites sur une correction, laisse le texte d'origine.
|
||||||
|
|
||||||
|
Texte :
|
||||||
|
{{selection}}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
---
|
||||||
|
id: fe9eb1b8-64d4-4930-bc78-0271f37463ed
|
||||||
|
title: Courriel poli à partir de notes brutes
|
||||||
|
tags: [courriel, notes, rédaction]
|
||||||
|
targets: [défaut, claude, chatgpt, gemini, qwen]
|
||||||
|
favorite: false
|
||||||
|
created: 2026-10-11
|
||||||
|
updated: 2026-10-11
|
||||||
|
variables:
|
||||||
|
- name: destinataire
|
||||||
|
label: Destinataire
|
||||||
|
type: text
|
||||||
|
default: la personne concernée
|
||||||
|
- name: ton
|
||||||
|
label: Ton du courriel
|
||||||
|
type: enum
|
||||||
|
choices: [poli, formel, amical professionnel]
|
||||||
|
default: poli
|
||||||
|
---
|
||||||
|
|
||||||
|
Transforme les notes brutes ci-dessous en un courriel prêt à envoyer à {{destinataire}}, avec un ton {{ton}}.
|
||||||
|
|
||||||
|
- Propose d'abord une ligne « Objet : … » courte et explicite.
|
||||||
|
- Formules de politesse adaptées au ton, paragraphes courts, demande ou information principale dès le début.
|
||||||
|
- N'invente aucun fait, chiffre ou engagement absent des notes.
|
||||||
|
- Termine par une formule de conclusion simple, sans signature.
|
||||||
|
|
||||||
|
Notes brutes :
|
||||||
|
{{selection}}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
id: 655bf6aa-4181-4ee5-89b4-85861ea7db23
|
||||||
|
title: Raccourcir de moitié
|
||||||
|
tags: [raccourcir, concision, synthèse]
|
||||||
|
targets: [défaut, claude, chatgpt, gemini, qwen]
|
||||||
|
favorite: false
|
||||||
|
created: 2026-10-11
|
||||||
|
updated: 2026-10-11
|
||||||
|
variables:
|
||||||
|
- name: priorite
|
||||||
|
label: À conserver en priorité
|
||||||
|
type: enum
|
||||||
|
choices: [idées essentielles, chiffres et faits, ton original]
|
||||||
|
default: idées essentielles
|
||||||
|
---
|
||||||
|
|
||||||
|
Raccourcis le texte suivant d'environ moitié, en conservant en priorité : {{priorite}}.
|
||||||
|
|
||||||
|
- Supprime les répétitions, les détours et les formules vides.
|
||||||
|
- Ne supprime aucune information indispensable à la compréhension.
|
||||||
|
- Conserve la structure générale et le ton.
|
||||||
|
- Donne uniquement le texte raccourci.
|
||||||
|
|
||||||
|
Texte :
|
||||||
|
{{selection}}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
id: 2f103340-ed13-4e83-8f62-3e874caa75da
|
||||||
|
title: Reformuler
|
||||||
|
tags: [reformulation, réécriture, style]
|
||||||
|
targets: [défaut, claude, chatgpt, gemini, qwen]
|
||||||
|
favorite: true
|
||||||
|
created: 2026-10-11
|
||||||
|
updated: 2026-10-11
|
||||||
|
variables:
|
||||||
|
- name: ton
|
||||||
|
label: Ton souhaité
|
||||||
|
type: enum
|
||||||
|
choices: [neutre, formel, amical, direct]
|
||||||
|
default: neutre
|
||||||
|
---
|
||||||
|
|
||||||
|
Reformule le texte suivant avec un ton {{ton}}.
|
||||||
|
|
||||||
|
- Conserve exactement le sens, les faits et les chiffres.
|
||||||
|
- Améliore la fluidité et corrige les lourdeurs, sans raccourcir ni allonger sensiblement.
|
||||||
|
- Donne une seule version, sans commentaires ni variantes.
|
||||||
|
|
||||||
|
Texte :
|
||||||
|
{{selection}}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
---
|
||||||
|
id: 802ebc80-4634-49b9-b438-e7489dadb5fc
|
||||||
|
title: Traduire FR-EN
|
||||||
|
tags: [traduction, anglais, français]
|
||||||
|
targets: [défaut, claude, chatgpt, gemini, qwen]
|
||||||
|
favorite: false
|
||||||
|
created: 2026-10-11
|
||||||
|
updated: 2026-10-11
|
||||||
|
variables:
|
||||||
|
- name: direction
|
||||||
|
label: Sens de traduction
|
||||||
|
type: enum
|
||||||
|
choices: [FR vers EN, EN vers FR]
|
||||||
|
default: FR vers EN
|
||||||
|
- name: registre
|
||||||
|
label: Registre
|
||||||
|
type: enum
|
||||||
|
choices: [neutre, formel, familier]
|
||||||
|
default: neutre
|
||||||
|
---
|
||||||
|
|
||||||
|
Traduis le texte suivant (sens : {{direction}}), registre {{registre}}.
|
||||||
|
|
||||||
|
- Conserve exactement la mise en forme : titres, listes, gras, liens et sauts de ligne.
|
||||||
|
- Ne traduis pas les noms propres ni les termes techniques consacrés dans la langue d'arrivée.
|
||||||
|
- Donne uniquement la traduction, sans commentaire.
|
||||||
|
|
||||||
|
Texte :
|
||||||
|
{{selection}}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""Contrôle de cohérence du projet PromptKey (utilisé par la CI Gitea Actions).
|
||||||
|
|
||||||
|
Vérifie :
|
||||||
|
1. la présence des fichiers attendus à la racine du dépôt ;
|
||||||
|
2. le pack de démarrage : 20 prompts, 5 par catégorie (Général, Écriture,
|
||||||
|
Code, Analyse) sous packs/essentiels/ ;
|
||||||
|
3. chaque prompt : en-tête délimité par ---, contenant au minimum `id`
|
||||||
|
(GUID valide et unique) et `title` ;
|
||||||
|
4. les noms de fichiers : minuscules, tirets, sans accents ;
|
||||||
|
5. toute variable {{...}} du corps est soit automatique (selection,
|
||||||
|
clipboard, date, datetime, time, app, cursor), soit déclarée dans le
|
||||||
|
bloc `variables:` de l'en-tête.
|
||||||
|
|
||||||
|
Usage : python3 scripts/check_pack.py [racine_du_depot]
|
||||||
|
Code de sortie : 0 si tout est conforme, 1 sinon (liste des erreurs).
|
||||||
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import uuid
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
RACINE = Path(sys.argv[1]).resolve() if len(sys.argv) > 1 else Path(__file__).resolve().parent.parent
|
||||||
|
|
||||||
|
FICHIERS_ATTENDUS = ["README.md", "CHANGELOG.md", "LICENSE", ".gitignore", "build.ps1"]
|
||||||
|
|
||||||
|
CATEGORIES = {"Général": 5, "Écriture": 5, "Code": 5, "Analyse": 5}
|
||||||
|
|
||||||
|
VARIABLES_AUTO = {"selection", "clipboard", "date", "datetime", "time", "app", "cursor"}
|
||||||
|
|
||||||
|
MOTIF_NOM_FICHIER = re.compile(r"^[a-z0-9]+(-[a-z0-9]+)*\.md$")
|
||||||
|
MOTIF_VARIABLE = re.compile(r"\{\{\s*([A-Za-z0-9_]+)\s*\}\}")
|
||||||
|
|
||||||
|
erreurs = []
|
||||||
|
|
||||||
|
|
||||||
|
def erreur(message):
|
||||||
|
erreurs.append(message)
|
||||||
|
print(f"ERREUR : {message}")
|
||||||
|
|
||||||
|
|
||||||
|
# --- 1. Fichiers attendus ---------------------------------------------------
|
||||||
|
for nom in FICHIERS_ATTENDUS:
|
||||||
|
if not (RACINE / nom).is_file():
|
||||||
|
erreur(f"fichier attendu manquant : {nom}")
|
||||||
|
|
||||||
|
# --- 2 à 5. Pack de démarrage ------------------------------------------------
|
||||||
|
pack = RACINE / "packs" / "essentiels"
|
||||||
|
if not pack.is_dir():
|
||||||
|
erreur(f"dossier du pack introuvable : {pack}")
|
||||||
|
else:
|
||||||
|
ids_vus = {}
|
||||||
|
total = 0
|
||||||
|
for categorie, attendu in CATEGORIES.items():
|
||||||
|
dossier = pack / categorie
|
||||||
|
fichiers = sorted(dossier.glob("*.md")) if dossier.is_dir() else []
|
||||||
|
if len(fichiers) != attendu:
|
||||||
|
erreur(f"catégorie {categorie} : {len(fichiers)} prompt(s), {attendu} attendus")
|
||||||
|
for fichier in fichiers:
|
||||||
|
total += 1
|
||||||
|
rel = fichier.relative_to(RACINE)
|
||||||
|
if not MOTIF_NOM_FICHIER.match(fichier.name):
|
||||||
|
erreur(f"{rel} : nom de fichier non conforme (minuscules, tirets, sans accents)")
|
||||||
|
|
||||||
|
texte = fichier.read_text(encoding="utf-8")
|
||||||
|
lignes = texte.splitlines()
|
||||||
|
if not lignes or lignes[0].strip() != "---" or "---" not in lignes[1:]:
|
||||||
|
erreur(f"{rel} : en-tête --- absent ou non fermé")
|
||||||
|
continue
|
||||||
|
fin = lignes[1:].index("---") + 1
|
||||||
|
entete = lignes[1:fin]
|
||||||
|
corps = "\n".join(lignes[fin + 1:])
|
||||||
|
|
||||||
|
champs = {}
|
||||||
|
declarees = set()
|
||||||
|
for ligne in entete:
|
||||||
|
m = re.match(r"^([a-z_]+):\s*(.*)$", ligne)
|
||||||
|
if m:
|
||||||
|
champs[m.group(1)] = m.group(2).strip()
|
||||||
|
m2 = re.match(r"^\s*- name:\s*(\S+)\s*$", ligne)
|
||||||
|
if m2:
|
||||||
|
declarees.add(m2.group(1).lower())
|
||||||
|
|
||||||
|
for champ in ("id", "title"):
|
||||||
|
if not champs.get(champ):
|
||||||
|
erreur(f"{rel} : champ obligatoire absent dans l'en-tête : {champ}")
|
||||||
|
|
||||||
|
guid = champs.get("id", "")
|
||||||
|
try:
|
||||||
|
uuid.UUID(guid)
|
||||||
|
except (ValueError, AttributeError):
|
||||||
|
erreur(f"{rel} : id n'est pas un GUID valide : {guid!r}")
|
||||||
|
if guid in ids_vus:
|
||||||
|
erreur(f"{rel} : id dupliqué (déjà dans {ids_vus[guid]})")
|
||||||
|
ids_vus[guid] = str(rel)
|
||||||
|
|
||||||
|
for nom_var in MOTIF_VARIABLE.findall(corps):
|
||||||
|
if nom_var.lower() not in VARIABLES_AUTO and nom_var.lower() not in declarees:
|
||||||
|
erreur(f"{rel} : variable {{{{{nom_var}}}}} utilisée mais non déclarée")
|
||||||
|
|
||||||
|
if total != 20:
|
||||||
|
erreur(f"pack : {total} prompt(s) au total, 20 attendus")
|
||||||
|
else:
|
||||||
|
print(f"Pack : {total} prompts, {len(ids_vus)} id uniques.")
|
||||||
|
|
||||||
|
if erreurs:
|
||||||
|
print(f"\nÉCHEC : {len(erreurs)} erreur(s).")
|
||||||
|
sys.exit(1)
|
||||||
|
print("OK : fichiers attendus présents, pack cohérent.")
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
#Requires AutoHotkey v2.0
|
||||||
|
; Tests autonomes de PromptKey (architecture §22) — à lancer sous Windows :
|
||||||
|
; AutoHotkey64.exe tests\run_tests.ahk
|
||||||
|
; Sortie : lignes OK / ÉCHEC sur la console (ou dans le fichier passé en premier argument),
|
||||||
|
; code de sortie 0 si tout passe, 1 sinon. Aucune GUI, aucune injection.
|
||||||
|
#SingleInstance Off
|
||||||
|
|
||||||
|
#Include ..\lib\Util.ahk
|
||||||
|
#Include ..\lib\Json.ahk
|
||||||
|
#Include ..\lib\Frontmatter.ahk
|
||||||
|
#Include ..\lib\VariableEngine.ahk
|
||||||
|
#Include ..\lib\PromptStore.ahk
|
||||||
|
#Include ..\lib\Search.ahk
|
||||||
|
|
||||||
|
global failures := 0
|
||||||
|
global outFile := A_Args.Length > 0 ? A_Args[1] : ""
|
||||||
|
|
||||||
|
Report(ok, name, detail := "") {
|
||||||
|
global failures, outFile
|
||||||
|
if (!ok)
|
||||||
|
failures++
|
||||||
|
line := (ok ? "OK " : "ÉCHEC") " " name (detail != "" ? " — " detail : "") "`n"
|
||||||
|
if (outFile != "")
|
||||||
|
FileAppend(line, outFile, "UTF-8")
|
||||||
|
else
|
||||||
|
FileAppend(line, "*")
|
||||||
|
}
|
||||||
|
|
||||||
|
Check(cond, name, detail := "") {
|
||||||
|
Report(cond, name, detail)
|
||||||
|
}
|
||||||
|
|
||||||
|
; ---------- Util : normalisation ----------
|
||||||
|
Check(Util.Normalize("Résumé ÉÉ") = "resume ee", "Normalize accents")
|
||||||
|
Check(Util.Slugify("Revue de code !") = "revue-de-code", "Slugify")
|
||||||
|
|
||||||
|
; ---------- Frontmatter : analyse ----------
|
||||||
|
sample := "
|
||||||
|
(
|
||||||
|
---
|
||||||
|
id: test-id-1
|
||||||
|
title: Résumer en 5 points
|
||||||
|
tags: [résumé, synthèse, court]
|
||||||
|
favorite: true
|
||||||
|
pinned: 2
|
||||||
|
variables:
|
||||||
|
- name: ton
|
||||||
|
label: Ton de la réponse
|
||||||
|
type: enum
|
||||||
|
choices: [neutre, formel, amical]
|
||||||
|
default: neutre
|
||||||
|
---
|
||||||
|
|
||||||
|
Résume avec un ton {{ton}}.
|
||||||
|
|
||||||
|
Texte :
|
||||||
|
{{selection}}
|
||||||
|
)"
|
||||||
|
parsed := Frontmatter.Parse(sample)
|
||||||
|
Check(parsed["errors"].Length = 0, "Frontmatter sans erreur", parsed["errors"].Length ? parsed["errors"][1] : "")
|
||||||
|
Check(parsed["meta"]["title"] = "Résumer en 5 points", "Frontmatter title")
|
||||||
|
Check(parsed["meta"]["tags"].Length = 3, "Frontmatter tags (3)")
|
||||||
|
Check(parsed["meta"]["favorite"] = 1, "Frontmatter favorite booléen")
|
||||||
|
Check(parsed["meta"]["variables"].Length = 1, "Frontmatter 1 variable")
|
||||||
|
Check(parsed["meta"]["variables"][1]["choices"].Length = 3, "Frontmatter choices (3)")
|
||||||
|
Check(InStr(parsed["body"], "{{selection}}") > 0, "Frontmatter corps conservé")
|
||||||
|
|
||||||
|
; Fichier sans en-tête : accepté, corps = tout.
|
||||||
|
plain := Frontmatter.Parse("Juste un texte.")
|
||||||
|
Check(plain["hasHeader"] = false && plain["body"] = "Juste un texte.", "Sans en-tête accepté")
|
||||||
|
|
||||||
|
; Erreur : en-tête jamais fermé.
|
||||||
|
bad := Frontmatter.Parse("---`nid: x`ntitle: y`ncorps sans fermeture")
|
||||||
|
Check(bad["errors"].Length > 0, "En-tête non fermé = erreur signalée")
|
||||||
|
|
||||||
|
; Aller-retour sérialisation.
|
||||||
|
item := PromptItem("test.md", "Général", parsed)
|
||||||
|
ser := Frontmatter.Serialize(item.Meta, item.Body)
|
||||||
|
reparsed := Frontmatter.Parse(ser)
|
||||||
|
Check(reparsed["errors"].Length = 0 && reparsed["meta"]["title"] = "Résumer en 5 points", "Aller-retour Serialize/Parse")
|
||||||
|
|
||||||
|
; ---------- Variables ----------
|
||||||
|
ctx := Map("selection", "TEXTE SÉLECTIONNÉ", "clipboardText", "PRESSE", "process", "chrome.exe")
|
||||||
|
defs := VariableEngine.ManualVars(item)
|
||||||
|
Check(defs.Length = 1 && defs[1]["name"] = "ton", "ManualVars : ton seulement (selection est automatique)")
|
||||||
|
resolved := VariableEngine.Resolve(item.Body, Map("ton", "formel"), ctx)
|
||||||
|
Check(InStr(resolved["text"], "ton formel") > 0, "Resolve remplace {{ton}}")
|
||||||
|
Check(InStr(resolved["text"], "TEXTE SÉLECTIONNÉ") > 0, "Resolve remplace {{selection}}")
|
||||||
|
Check(resolved["unresolved"].Length = 0, "Resolve sans non-résolue")
|
||||||
|
withCursor := VariableEngine.Resolve("Début {{cursor}} fin", Map(), ctx)
|
||||||
|
Check(withCursor["cursorFromEnd"] = 4 && withCursor["text"] = "Début fin", "Marqueur cursor : position et retrait")
|
||||||
|
|
||||||
|
; ---------- JSON ----------
|
||||||
|
obj := Json.Parse('{"a": 1, "b": [1, 2, "trois"], "c": {"d": "x\ny"}}')
|
||||||
|
Check(obj["a"] = 1 && obj["b"].Length = 3 && obj["c"]["d"] = "x`ny", "JSON parse imbriqué")
|
||||||
|
round := Json.Parse(Json.Stringify(obj))
|
||||||
|
Check(round["b"][3] = "trois", "JSON aller-retour")
|
||||||
|
|
||||||
|
; ---------- Recherche ----------
|
||||||
|
class FakePrompt {
|
||||||
|
__New(title, cat, tags, body) {
|
||||||
|
this.Id := title
|
||||||
|
this.Title := title
|
||||||
|
this.Category := cat
|
||||||
|
this.Tags := tags
|
||||||
|
this.Body := body
|
||||||
|
this.Pinned := 0
|
||||||
|
this.Favorite := 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prompts := [
|
||||||
|
FakePrompt("Résumer en 5 points", "Général", ["résumé"], "Résume le texte"),
|
||||||
|
FakePrompt("Revue de code", "Code", ["code"], "Relis ce code"),
|
||||||
|
FakePrompt("Traduire en anglais", "Écriture", ["traduction"], "Traduis")]
|
||||||
|
found := Search.Filter(prompts, "resu", Map())
|
||||||
|
Check(found.Length = 1 && found[1].Title = "Résumer en 5 points", "Recherche 'resu' trouve 'Résumer' (accents)")
|
||||||
|
found2 := Search.Filter(prompts, "code", Map())
|
||||||
|
Check(found2.Length >= 1 && found2[1].Title = "Revue de code", "Recherche 'code' classe la revue en premier")
|
||||||
|
all := Search.Filter(prompts, "", Map())
|
||||||
|
Check(all.Length = 3, "Recherche vide : toute la bibliothèque")
|
||||||
|
|
||||||
|
; ---------- Encodage : aller-retour UTF-8 bit à bit (§19.4) ----------
|
||||||
|
tmpFile := A_Temp "\promptkey-test-encodage.md"
|
||||||
|
tricky := "é à — ‘’ « » 😀`r`n{{variable}} deuxième ligne"
|
||||||
|
f := FileOpen(tmpFile, "w", "UTF-8")
|
||||||
|
f.Write(tricky)
|
||||||
|
f.Close()
|
||||||
|
back := FileRead(tmpFile, "UTF-8")
|
||||||
|
Check(back = tricky, "UTF-8 aller-retour identique")
|
||||||
|
FileDelete(tmpFile)
|
||||||
|
|
||||||
|
; ---------- Bilan ----------
|
||||||
|
if (outFile != "")
|
||||||
|
FileAppend("----`n" (failures = 0 ? "TOUS LES TESTS PASSENT" : failures " ÉCHEC(S)") "`n", outFile, "UTF-8")
|
||||||
|
else
|
||||||
|
FileAppend("----`n" (failures = 0 ? "TOUS LES TESTS PASSENT" : failures " ÉCHEC(S)") "`n", "*")
|
||||||
|
ExitApp(failures = 0 ? 0 : 1)
|
||||||
Reference in New Issue
Block a user