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