feat(pack,docs): pack de démarrage 20 prompts, README, tests, CI, build
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:
Spark
2026-10-11 10:17:30 -04:00
parent 348036daf3
commit 258287b2ea
29 changed files with 2083 additions and 0 deletions
+21
View File
@@ -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
View File
@@ -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
+22
View File
@@ -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`).
+21
View File
@@ -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.
+115
View File
@@ -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.
+85
View File
@@ -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}}
+30
View File
@@ -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}}
+24
View File
@@ -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}}
+111
View File
@@ -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.")
+137
View File
@@ -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)