repowarden 4.12.1-next.3 → 4.13.0-next.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/agents +32 -27
- package/bin/carte +69 -0
- package/bin/garde +16 -3
- package/bin/repowarden +3 -0
- package/bin/statut +27 -0
- package/ci/tickets/commun.sh +3 -0
- package/ci/tickets/push.sh +95 -23
- package/hooks/agents/repowarden-garde-v2.js +28 -0
- package/hooks/lib/agents.sh +21 -8
- package/hooks/lib/carte.sh +339 -0
- package/hooks/lib/commentaires.sh +45 -20
- package/hooks/lib/i18n/en.sh +26 -2
- package/hooks/lib/i18n/fr.sh +26 -2
- package/hooks/post-merge +5 -0
- package/package.json +1 -1
package/bin/agents
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
# repowarden agents init [--claude] [--opencode] [--projet | --global]
|
|
4
4
|
# repowarden agents retirer [--claude] [--opencode] [--projet | --global]
|
|
5
5
|
# Claude Code hook PreToolUse dans .claude/settings.json
|
|
6
|
-
# opencode
|
|
6
|
+
# opencode plugins .opencode/plugins/repowarden-garde.js (opencode 1.x)
|
|
7
|
+
# et repowarden-garde-v2.js (opencode 2.x)
|
|
7
8
|
# Sans --claude ni --opencode : les deux. --projet (défaut) : fichiers du dépôt,
|
|
8
9
|
# à versionner ; --global : configuration de l'utilisateur, là où chaque outil
|
|
9
10
|
# la lit (CLAUDE_CONFIG_DIR ; OPENCODE_CONFIG_DIR, sinon XDG_CONFIG_HOME).
|
|
@@ -20,7 +21,6 @@ source "$ROOT/hooks/lib/agents.sh"
|
|
|
20
21
|
|
|
21
22
|
aide() { _tr help.agents; printf '%b\n' "$_T"; }
|
|
22
23
|
|
|
23
|
-
MODELE_OPENCODE="$AGENTS_MODELE_OPENCODE"
|
|
24
24
|
COMMANDE_CLAUDE="$AGENTS_COMMANDE_CLAUDE"
|
|
25
25
|
MATCHER_CLAUDE="$AGENTS_MATCHER_CLAUDE"
|
|
26
26
|
|
|
@@ -50,13 +50,13 @@ if [ "$portee" = projet ]; then
|
|
|
50
50
|
[ -n "$GIT_TOPLEVEL" ] || { err_t install.not_repo; exit 1; }
|
|
51
51
|
base="$GIT_TOPLEVEL"
|
|
52
52
|
fichier_claude="$base/.claude/settings.json"
|
|
53
|
-
|
|
53
|
+
dossier_opencode="$base/.opencode/plugins"
|
|
54
54
|
else
|
|
55
55
|
# Réglages utilisateur de Claude Code : $CLAUDE_CONFIG_DIR s'il est défini
|
|
56
56
|
# (code.claude.com/docs/en/settings) ; plugins globaux d'opencode :
|
|
57
57
|
# $OPENCODE_CONFIG_DIR, sinon $XDG_CONFIG_HOME/opencode (opencode.ai/docs/config)
|
|
58
58
|
fichier_claude="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings.json"
|
|
59
|
-
|
|
59
|
+
dossier_opencode="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/plugins"
|
|
60
60
|
fi
|
|
61
61
|
|
|
62
62
|
ecrits=()
|
|
@@ -99,26 +99,27 @@ init_claude() {
|
|
|
99
99
|
ok_t agents.added "Claude Code" "$f"
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
#
|
|
103
|
-
# repowarden est remplacé, un fichier personnel gardé et signalé
|
|
102
|
+
# Plugins opencode (1.x et 2.x) : copie du modèle ; celui d'une version
|
|
103
|
+
# antérieure de repowarden est remplacé, un fichier personnel gardé et signalé
|
|
104
104
|
init_opencode() {
|
|
105
|
-
local f
|
|
106
|
-
|
|
107
|
-
|
|
105
|
+
local p f modele
|
|
106
|
+
for p in $AGENTS_PLUGINS_OPENCODE; do
|
|
107
|
+
f="$dossier_opencode/$p" modele="$AGENTS_MODELES/$p"
|
|
108
|
+
if [ ! -f "$f" ]; then
|
|
109
|
+
mkdir -p "$dossier_opencode"
|
|
110
|
+
cp "$modele" "$f"
|
|
111
|
+
ecrits+=("$f")
|
|
112
|
+
ok_t agents.added opencode "$f"
|
|
113
|
+
elif cmp -s "$f" "$modele"; then
|
|
108
114
|
ok_t agents.present opencode "$f"
|
|
109
115
|
elif agents_plugin_repowarden "$f"; then
|
|
110
|
-
cp "$
|
|
116
|
+
cp "$modele" "$f"
|
|
111
117
|
ecrits+=("$f")
|
|
112
118
|
ok_t agents.updated opencode "$f"
|
|
113
119
|
else
|
|
114
|
-
attention_t agents.kept opencode "$f" "$
|
|
120
|
+
attention_t agents.kept opencode "$f" "$modele"
|
|
115
121
|
fi
|
|
116
|
-
|
|
117
|
-
fi
|
|
118
|
-
mkdir -p "$(dirname "$f")"
|
|
119
|
-
cp "$MODELE_OPENCODE" "$f"
|
|
120
|
-
ecrits+=("$f")
|
|
121
|
-
ok_t agents.added opencode "$f"
|
|
122
|
+
done
|
|
122
123
|
}
|
|
123
124
|
|
|
124
125
|
# Hook PreToolUse : seules les commandes « repowarden garde » retirées ; une
|
|
@@ -146,17 +147,21 @@ retirer_claude() {
|
|
|
146
147
|
ok_t agents.removed "Claude Code" "$f"
|
|
147
148
|
}
|
|
148
149
|
|
|
149
|
-
#
|
|
150
|
+
# Plugins opencode : chacun supprimé s'il est celui du modèle ; modifié, gardé
|
|
151
|
+
# et signalé
|
|
150
152
|
retirer_opencode() {
|
|
151
|
-
local f
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
153
|
+
local p f
|
|
154
|
+
for p in $AGENTS_PLUGINS_OPENCODE; do
|
|
155
|
+
f="$dossier_opencode/$p"
|
|
156
|
+
if [ ! -f "$f" ]; then
|
|
157
|
+
ok_t agents.absent opencode "$f"
|
|
158
|
+
elif cmp -s "$f" "$AGENTS_MODELES/$p"; then
|
|
159
|
+
rm -f "$f"
|
|
160
|
+
ok_t agents.removed opencode "$f"
|
|
161
|
+
else
|
|
162
|
+
attention_t agents.kept_remove opencode "$f"
|
|
163
|
+
fi
|
|
164
|
+
done
|
|
160
165
|
}
|
|
161
166
|
|
|
162
167
|
statut=0
|
package/bin/carte
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Usage : repowarden carte (re)génère la carte
|
|
3
|
+
# repowarden carte <fichier|fonction|'#n'> [--json] extrait utile
|
|
4
|
+
#
|
|
5
|
+
# Carte du projet, sans LLM (gh seulement pour la conception des tickets) : chaque fonction des scripts shell,
|
|
6
|
+
# son fichier, sa ligne et son « pourquoi » (le commentaire juste au-dessus),
|
|
7
|
+
# dans .repowarden/carte/carte.json (dossier ignoré par git). Interrogée,
|
|
8
|
+
# elle est d'abord régénérée si elle est absente ou périmée. Voir docs/carte.md.
|
|
9
|
+
set -euo pipefail
|
|
10
|
+
|
|
11
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
|
|
12
|
+
# shellcheck source=../hooks/lib/common.sh
|
|
13
|
+
source "$ROOT/hooks/lib/common.sh"
|
|
14
|
+
# shellcheck source=../hooks/lib/lang.sh
|
|
15
|
+
source "$ROOT/hooks/lib/lang.sh"
|
|
16
|
+
# shellcheck source=../hooks/lib/carte.sh
|
|
17
|
+
source "$ROOT/hooks/lib/carte.sh"
|
|
18
|
+
|
|
19
|
+
# Arguments : une cible (fichier ou fonction), --json
|
|
20
|
+
cible="" json=""
|
|
21
|
+
for a in "$@"; do
|
|
22
|
+
case "$a" in
|
|
23
|
+
--json) json=json ;;
|
|
24
|
+
-*) inconnu="$a" ;;
|
|
25
|
+
*) if [ -z "$cible" ]; then cible="$a"; else inconnu="$a"; fi ;;
|
|
26
|
+
esac
|
|
27
|
+
if [ -n "${inconnu:-}" ]; then
|
|
28
|
+
err_t install.unknown_arg "$inconnu"
|
|
29
|
+
_tr help.carte
|
|
30
|
+
printf '%b\n' "$_T" >&2
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
done
|
|
34
|
+
if [ -z "$GIT_TOPLEVEL" ]; then
|
|
35
|
+
err_t carte.no_repo
|
|
36
|
+
exit 1
|
|
37
|
+
fi
|
|
38
|
+
if ! has jq; then
|
|
39
|
+
err_t carte.no_jq
|
|
40
|
+
exit 1
|
|
41
|
+
fi
|
|
42
|
+
# Fichier existant : chemin depuis la racine du dépôt, quel que soit le dossier courant
|
|
43
|
+
chemin=""
|
|
44
|
+
if [ -n "$cible" ] && [ -e "$cible" ]; then
|
|
45
|
+
chemin="$(cd "$(dirname "$cible")" && pwd -P)/${cible##*/}"
|
|
46
|
+
chemin="${chemin#"$(cd "$GIT_TOPLEVEL" && pwd -P)"/}"
|
|
47
|
+
chemin="${chemin%/.}"
|
|
48
|
+
fi
|
|
49
|
+
cd "$GIT_TOPLEVEL"
|
|
50
|
+
|
|
51
|
+
if [ -z "$cible" ] && [ -z "$json" ]; then
|
|
52
|
+
carte_generer
|
|
53
|
+
# shellcheck disable=SC2016 # programme jq, rien à développer
|
|
54
|
+
read -r fichiers fonctions sans < <(cjq -r '[.fichiers[].fonctions[]?] as $f
|
|
55
|
+
| "\(.fichiers | length) \($f | length) \([$f[] | select(.pourquoi == "")] | length)"' "$CARTE_JSON")
|
|
56
|
+
ok_t carte.done "$fichiers" "$fonctions" "$sans" "$CARTE_JSON"
|
|
57
|
+
exit 0
|
|
58
|
+
fi
|
|
59
|
+
# Interrogation : carte absente ou périmée, régénérée d'abord
|
|
60
|
+
carte_a_jour || carte_generer
|
|
61
|
+
if [ -z "$cible" ]; then
|
|
62
|
+
cat "$CARTE_JSON"
|
|
63
|
+
elif [[ "$cible" =~ ^#([0-9]+)$ ]]; then
|
|
64
|
+
carte_ticket "${BASH_REMATCH[1]}" "$json"
|
|
65
|
+
elif [ -n "$chemin" ] || jq -e --arg f "$cible" '.fichiers | has($f)' "$CARTE_JSON" >/dev/null; then
|
|
66
|
+
carte_fichier "${chemin:-$cible}" "$json"
|
|
67
|
+
else
|
|
68
|
+
carte_fonction "$cible" "$json"
|
|
69
|
+
fi
|
package/bin/garde
CHANGED
|
@@ -43,10 +43,14 @@ args="$(jq -c ".$a // {}" <<<"$entree")"
|
|
|
43
43
|
# Refus : raison et commande à lancer, puis code 2. Ligne shell de plusieurs
|
|
44
44
|
# commandes (LIGNE_MULTIPLE) : refusée en bloc, ce que l'agent doit savoir
|
|
45
45
|
LIGNE_MULTIPLE=""
|
|
46
|
+
# Fichier existant dont la modification est vérifiée (chemin depuis la racine) :
|
|
47
|
+
# un refus rappelle de lire sa carte avant de le modifier
|
|
48
|
+
FICHIER_CARTE=""
|
|
46
49
|
refuser() {
|
|
47
50
|
_tr "$@"
|
|
48
51
|
printf '✖ repowarden : %b\n' "$_T" >&2
|
|
49
52
|
[ -z "$LIGNE_MULTIPLE" ] || { _tr garde.ligne_entiere; printf '%b\n' "$_T" >&2; }
|
|
53
|
+
[ -z "$FICHIER_CARTE" ] || { _tr garde.carte "$FICHIER_CARTE" "$FICHIER_CARTE"; printf '%b\n' "$_T" >&2; }
|
|
50
54
|
exit 2
|
|
51
55
|
}
|
|
52
56
|
|
|
@@ -133,12 +137,19 @@ protegee() {
|
|
|
133
137
|
# si le dépôt suit ses tickets)
|
|
134
138
|
verifier_fichier() {
|
|
135
139
|
dossier_r "$1"
|
|
136
|
-
|
|
140
|
+
# Sous Windows, jq finit le chemin lu par un CR (retiré) et le chemin peut
|
|
141
|
+
# être en forme Windows (D:/…), absolu lui aussi
|
|
142
|
+
local d="$REPLY" f="${1%$'\r'}"
|
|
143
|
+
case "$f" in /* | [A-Za-z]:[/\\]*) ;; *) f="$cwd/$f" ;; esac
|
|
137
144
|
(
|
|
138
145
|
cd "$d" 2>/dev/null || exit 0
|
|
139
146
|
depot_repowarden || exit 0
|
|
140
147
|
# Fichiers internes de git (.git/…) : hors du travail de l'agent
|
|
141
148
|
case "$(pwd -P)/" in "$(cd "$(git rev-parse --git-dir)" && pwd -P)"/*) exit 0 ;; esac
|
|
149
|
+
if [ -f "$f" ]; then
|
|
150
|
+
FICHIER_CARTE="$(cd "$(dirname "$f")" && pwd -P)/${f##*/}"
|
|
151
|
+
FICHIER_CARTE="${FICHIER_CARTE#"$(cd "$(git rev-parse --show-toplevel)" && pwd -P)"/}"
|
|
152
|
+
fi
|
|
142
153
|
branche="$(git symbolic-ref -q --short HEAD 2>/dev/null || true)"
|
|
143
154
|
[ -n "$branche" ] || refuser garde.detache "$GIT_TOPLEVEL"
|
|
144
155
|
if protegee "$branche"; then
|
|
@@ -431,7 +442,8 @@ verifier_shell() {
|
|
|
431
442
|
|
|
432
443
|
case "$outil" in
|
|
433
444
|
Edit | Write | MultiEdit | NotebookEdit | edit | write | multiedit)
|
|
434
|
-
|
|
445
|
+
# path : outils edit et write d'opencode 2.x
|
|
446
|
+
chemin="$(jq -r '.file_path // .filePath // .notebook_path // .path // empty' <<<"$args" 2>/dev/null)"
|
|
435
447
|
[ -z "$chemin" ] || verifier_fichier "$chemin"
|
|
436
448
|
;;
|
|
437
449
|
apply_patch)
|
|
@@ -440,7 +452,8 @@ case "$outil" in
|
|
|
440
452
|
done < <(jq -r '.patchText // empty' <<<"$args" 2>/dev/null |
|
|
441
453
|
sed -nE -e 's/^\*\*\* (Add|Update|Delete) File: //p' -e 's/^\*\*\* Move to: //p')
|
|
442
454
|
;;
|
|
443
|
-
|
|
455
|
+
# shell : nom de l'outil bash dans opencode 2.x
|
|
456
|
+
Bash | bash | shell)
|
|
444
457
|
verifier_shell "$(jq -r '.command // empty' <<<"$args" 2>/dev/null)"
|
|
445
458
|
;;
|
|
446
459
|
esac
|
package/bin/repowarden
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
# repowarden uninstall [--global] [--purge] les retire (avant npm uninstall -g)
|
|
8
8
|
# repowarden cc assistant de commit
|
|
9
9
|
# repowarden code-mort nouveau code mort de la branche
|
|
10
|
+
# repowarden carte carte du projet : fonctions et leur pourquoi
|
|
10
11
|
# repowarden ticket <n> branche du ticket (créée, liée, si besoin)
|
|
11
12
|
# repowarden ticket nouveau "<titre>" nouveau ticket
|
|
12
13
|
# repowarden ticket comment <n> "<texte>" commentaire sur un ticket
|
|
@@ -52,6 +53,7 @@ help_key_r() {
|
|
|
52
53
|
garde | guard) REPLY=help.garde ;;
|
|
53
54
|
cc | commit) REPLY=help.cc ;;
|
|
54
55
|
code-mort | deadcode) REPLY=help.code-mort ;;
|
|
56
|
+
carte | map) REPLY=help.carte ;;
|
|
55
57
|
ticket) REPLY=help.ticket ;;
|
|
56
58
|
tickets) REPLY=help.tickets ;;
|
|
57
59
|
pr) REPLY=help.pr ;;
|
|
@@ -102,6 +104,7 @@ case "$cmd" in
|
|
|
102
104
|
uninstall) exec bash "$ROOT/install.sh" --uninstall "$@" ;;
|
|
103
105
|
cc | commit) exec bash "$ROOT/bin/commit" "$@" ;;
|
|
104
106
|
code-mort | deadcode) exec bash "$ROOT/bin/code-mort" "$@" ;;
|
|
107
|
+
carte | map) exec bash "$ROOT/bin/carte" "$@" ;;
|
|
105
108
|
tickets) exec bash "$ROOT/bin/tickets" "$@" ;;
|
|
106
109
|
ticket) exec bash "$ROOT/bin/ticket" "$@" ;;
|
|
107
110
|
ticket-comment) exec bash "$ROOT/bin/ticket-comment" "$@" ;;
|
package/bin/statut
CHANGED
|
@@ -13,6 +13,8 @@ source "$ROOT/hooks/lib/common.sh"
|
|
|
13
13
|
source "$ROOT/hooks/lib/secrets.sh"
|
|
14
14
|
# shellcheck source=../hooks/lib/agents.sh
|
|
15
15
|
source "$ROOT/hooks/lib/agents.sh"
|
|
16
|
+
# shellcheck source=../hooks/lib/carte.sh
|
|
17
|
+
source "$ROOT/hooks/lib/carte.sh"
|
|
16
18
|
exec 2>&1 # une seule sortie, dans l'ordre (avertissements compris)
|
|
17
19
|
# shellcheck disable=SC2034 # lu par check_version (common.sh)
|
|
18
20
|
HOOKS_DIR="$ROOT/hooks"
|
|
@@ -75,6 +77,21 @@ if [ -n "$GIT_TOPLEVEL" ]; then
|
|
|
75
77
|
attention_t st.repo_lefthook "$f"
|
|
76
78
|
break
|
|
77
79
|
done
|
|
80
|
+
# Projet pre-commit : avec les hooks repowarden actifs, « pre-commit install »
|
|
81
|
+
# refuse ; ses hooks ne tournent que dans .git/hooks ou .repowarden, lancés par repowarden
|
|
82
|
+
if [ -f "$GIT_TOPLEVEL/.pre-commit-config.yaml" ] &&
|
|
83
|
+
[ "$(reel "$(git config --get core.hooksPath 2>/dev/null || true)")" = "$ROOT/hooks" ]; then
|
|
84
|
+
commun="$(cd "$GIT_TOPLEVEL" && reel "$(git rev-parse --git-common-dir)")"
|
|
85
|
+
lance=""
|
|
86
|
+
for h in "$commun/hooks/pre-commit" "$GIT_TOPLEVEL/.repowarden/pre-commit"; do
|
|
87
|
+
[ -x "$h" ] && grep -qsE 'File generated by pre-commit|pre-commit +(run|hook-impl)' "$h" && lance=1
|
|
88
|
+
done
|
|
89
|
+
if [ -z "$lance" ]; then
|
|
90
|
+
marche="GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=core.hooksPath GIT_CONFIG_VALUE_0= pre-commit install"
|
|
91
|
+
attention_t st.repo_precommit "$marche"
|
|
92
|
+
suivant="${suivant:-$marche}"
|
|
93
|
+
fi
|
|
94
|
+
fi
|
|
78
95
|
# Anciens noms (configuration, workflows de l'ancien dépôt, que GitHub ne
|
|
79
96
|
# redirige plus) : la migration les remplace
|
|
80
97
|
if [ -e "$GIT_TOPLEVEL/.repogarde.conf" ] || [ -d "$GIT_TOPLEVEL/.repogarde" ] ||
|
|
@@ -138,6 +155,16 @@ if [ -n "$GIT_TOPLEVEL" ]; then
|
|
|
138
155
|
sensitive_files_r
|
|
139
156
|
[ -z "$REPLY" ] || sensitive_files_warn "$REPLY"
|
|
140
157
|
fi
|
|
158
|
+
# Carte du projet (repowarden carte) : facultative ; périmée, elle ne dit
|
|
159
|
+
# plus ce que fait le code, et les agents la lisent avant de modifier
|
|
160
|
+
if [ ! -f "$GIT_TOPLEVEL/$CARTE_JSON" ]; then
|
|
161
|
+
info_t st.carte_absente
|
|
162
|
+
elif (cd "$GIT_TOPLEVEL" && carte_a_jour); then
|
|
163
|
+
ok_t st.carte_ok
|
|
164
|
+
else
|
|
165
|
+
attention_t st.carte_perimee
|
|
166
|
+
suivant="${suivant:-repowarden carte}"
|
|
167
|
+
fi
|
|
141
168
|
info_t st.repo_forge "$FORGE"
|
|
142
169
|
[ "$FORGE" != github ] || dim_t st.repo_rules
|
|
143
170
|
fi
|
package/ci/tickets/commun.sh
CHANGED
|
@@ -113,4 +113,7 @@ notice() { echo "::notice title=repowarden::$*"; }
|
|
|
113
113
|
|
|
114
114
|
notice_t() { _tr "$@"; notice "$_T"; }
|
|
115
115
|
|
|
116
|
+
# Avertissement traduit dans le journal du run : signale sans faire échouer
|
|
117
|
+
avertir_t() { _tr "$@"; echo "::warning title=repowarden::$_T"; }
|
|
118
|
+
|
|
116
119
|
# Chargé par les tests (source) : fonctions seulement
|
package/ci/tickets/push.sh
CHANGED
|
@@ -69,12 +69,13 @@ titre_pr_brouillon_r() {
|
|
|
69
69
|
suggest_pr_title_r "$branche"
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
# $1 = corps d'un ticket → REPLY :
|
|
72
|
+
# $1 = corps d'un ticket → REPLY : ses sections de critères d'acceptation
|
|
73
73
|
# (« Terminé quand… », « Critères d'acceptation ») en cases à cocher « - [ ] »,
|
|
74
|
-
# le reste
|
|
74
|
+
# RESTE_TICKET : le reste (constat, proposition), que la PR replie
|
|
75
75
|
criteres_coches_r() {
|
|
76
|
-
local corps="$1" ligne dedans=0
|
|
76
|
+
local corps="$1" ligne dedans=0 criteres="" reste=""
|
|
77
77
|
while IFS= read -r ligne; do
|
|
78
|
+
ligne="${ligne%$'\r'}"
|
|
78
79
|
if [[ "$ligne" =~ ^#{1,6}[[:space:]] ]]; then
|
|
79
80
|
dedans=0
|
|
80
81
|
case "$ligne" in
|
|
@@ -85,9 +86,16 @@ criteres_coches_r() {
|
|
|
85
86
|
ligne="$(printf '%s' "$ligne" |
|
|
86
87
|
sed -E 's/^([[:space:]]*[-*][[:space:]]+)(\[[xX ]\][[:space:]]+)?/\1[ ] /')"
|
|
87
88
|
fi
|
|
88
|
-
|
|
89
|
+
if [ "$dedans" = 1 ]; then
|
|
90
|
+
criteres="$criteres$ligne"$'\n'
|
|
91
|
+
else
|
|
92
|
+
reste="$reste$ligne"$'\n'
|
|
93
|
+
fi
|
|
89
94
|
done <<<"$corps"
|
|
90
|
-
|
|
95
|
+
while [[ "$reste" == $'\n'* ]]; do reste="${reste#$'\n'}"; done
|
|
96
|
+
sans_fin_vide_r "$reste"
|
|
97
|
+
RESTE_TICKET="$REPLY"
|
|
98
|
+
sans_fin_vide_r "$criteres"
|
|
91
99
|
}
|
|
92
100
|
|
|
93
101
|
# Fin de chaque commentaire lu d'un bloc (séparateur ASCII) : un texte écrit à
|
|
@@ -189,41 +197,98 @@ section_realise_r() {
|
|
|
189
197
|
sans_fin_vide_r "$section"
|
|
190
198
|
}
|
|
191
199
|
|
|
200
|
+
# Ligne de pied d'un commit (`repowarden cc --refs`), faite seulement de
|
|
201
|
+
# références : retirée, le pied de la PR porte déjà le ticket
|
|
202
|
+
PIED_COMMIT_RE='^[[:space:]]*(Tickets?|Refs?|Closes|Fixes|Resolves)[[:space:]]*:?[[:space:]]*#[0-9]+([[:space:],]+#[0-9]+)*[[:space:]]*$'
|
|
203
|
+
|
|
204
|
+
# $1 = commits lus par l'API (« parents sha message », chacun suivi d'une ligne
|
|
205
|
+
# SEP_COMMENTAIRE) → REPLY : pour chaque commit hors fusion, « ### en-tête (sha) »
|
|
206
|
+
# puis son corps, le pourquoi que le relecteur cherche avant le ticket
|
|
207
|
+
ce_que_fait_r() {
|
|
208
|
+
local ligne debut=1 fusion=0 entete="" bloc="" sortie="" reste
|
|
209
|
+
while IFS= read -r ligne; do
|
|
210
|
+
ligne="${ligne%$'\r'}"
|
|
211
|
+
if [ "$ligne" = "$SEP_COMMENTAIRE" ]; then
|
|
212
|
+
if [ -n "$entete" ] && [ "$fusion" = 0 ]; then
|
|
213
|
+
sans_fin_vide_r "$bloc"
|
|
214
|
+
sortie="$sortie${sortie:+$'\n\n'}$entete${REPLY:+$'\n'}$REPLY"
|
|
215
|
+
fi
|
|
216
|
+
debut=1 fusion=0 entete="" bloc=""
|
|
217
|
+
continue
|
|
218
|
+
fi
|
|
219
|
+
if [ "$debut" = 1 ]; then
|
|
220
|
+
[ -n "$ligne" ] || continue
|
|
221
|
+
debut=0
|
|
222
|
+
# plus d'un parent : commit de fusion, sans rien à dire à la revue
|
|
223
|
+
[ "${ligne%% *}" -le 1 ] 2>/dev/null || fusion=1
|
|
224
|
+
reste="${ligne#* }"
|
|
225
|
+
entete="### ${reste#* } (${reste%% *})"
|
|
226
|
+
continue
|
|
227
|
+
fi
|
|
228
|
+
[ -n "$bloc" ] || [ -n "${ligne//[[:space:]]/}" ] || continue
|
|
229
|
+
[[ ! "$ligne" =~ $PIED_COMMIT_RE ]] || continue
|
|
230
|
+
ligne_reprise_r "$ligne"
|
|
231
|
+
bloc="$bloc$REPLY"$'\n'
|
|
232
|
+
done <<<"$1"$'\n'"$SEP_COMMENTAIRE"
|
|
233
|
+
REPLY="$sortie"
|
|
234
|
+
}
|
|
235
|
+
|
|
192
236
|
# REPLY = description de la PR brouillon du ticket $1 (branche $2, PR $3 si
|
|
193
|
-
# elle existe) :
|
|
194
|
-
# commentaire, le résumé de sa conception,
|
|
195
|
-
#
|
|
237
|
+
# elle existe) : ce que fait la PR (corps des commits), les critères du ticket
|
|
238
|
+
# en cases à cocher, ses ajouts en commentaire, le résumé de sa conception, le
|
|
239
|
+
# reste du ticket replié, la section « Réalisé » de l'auteur gardée, puis
|
|
240
|
+
# « Ticket : #N ». Le bot ne régénère que ses sections.
|
|
196
241
|
description_pr_brouillon_r() {
|
|
197
|
-
local n="$1" branche="$2" pr="${3:-}" corps
|
|
242
|
+
local n="$1" branche="$2" pr="${3:-}" corps="" reste commentaires
|
|
243
|
+
ce_que_fait_r "$(gh api "repos/$GH_REPO/compare/$INTEGRATION...$branche" \
|
|
244
|
+
-q '.commits[] | "\(.parents | length) \(.sha[0:7]) \(.commit.message)\n\u001e"' 2>/dev/null || true)"
|
|
245
|
+
_tr tk.pr_does
|
|
246
|
+
[ -z "$REPLY" ] || corps="## $_T"$'\n'"$REPLY"
|
|
198
247
|
criteres_coches_r "$(gh issue view "$n" --json body -q .body)"
|
|
199
|
-
|
|
248
|
+
reste="$RESTE_TICKET"
|
|
249
|
+
[ -z "$REPLY" ] || corps="$corps${corps:+$'\n\n'}$REPLY"
|
|
200
250
|
commentaires_mainteneur_r "$n"
|
|
201
251
|
commentaires="$REPLY"
|
|
202
252
|
ajouts_ticket_r "$commentaires"
|
|
203
253
|
_tr tk.pr_additions
|
|
204
|
-
[ -z "$REPLY" ] || corps="$corps
|
|
254
|
+
[ -z "$REPLY" ] || corps="$corps${corps:+$'\n\n'}## $_T"$'\n'"$REPLY"
|
|
205
255
|
resume_conception_r "$commentaires"
|
|
206
256
|
_tr tk.pr_design
|
|
207
|
-
[ -z "$REPLY" ] || corps="$corps
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
257
|
+
[ -z "$REPLY" ] || corps="$corps${corps:+$'\n\n'}## $_T"$'\n'"$REPLY"
|
|
258
|
+
if [ -n "$reste" ]; then
|
|
259
|
+
# lignes vides autour du texte : sinon GitHub ne rend pas son Markdown
|
|
260
|
+
_tr tk.pr_ticket "$n"
|
|
261
|
+
corps="$corps${corps:+$'\n\n'}<details>"$'\n'"<summary>$_T</summary>"$'\n\n'"$reste"$'\n\n</details>'
|
|
262
|
+
fi
|
|
211
263
|
if [ -n "$pr" ]; then
|
|
212
264
|
section_realise_r "$(gh pr view "$pr" --json body -q .body 2>/dev/null || true)"
|
|
213
|
-
[ -z "$REPLY" ] || corps="$corps
|
|
265
|
+
[ -z "$REPLY" ] || corps="$corps${corps:+$'\n\n'}$REPLY"
|
|
214
266
|
fi
|
|
215
|
-
REPLY="$corps
|
|
267
|
+
REPLY="$corps${corps:+$'\n\n'}Ticket : #$n"
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
# REPLY = compte à assigner à la PR brouillon du ticket $1 : l'auteur du push
|
|
271
|
+
# s'il est une personne (GitHub refuse d'assigner un bot avec un jeton d'App),
|
|
272
|
+
# sinon le premier assigné du ticket, sinon vide
|
|
273
|
+
assigne_pr_r() {
|
|
274
|
+
REPLY=""
|
|
275
|
+
case "${PUSHER:-}" in
|
|
276
|
+
"" | *"[bot]") ;;
|
|
277
|
+
*) REPLY="$PUSHER"; return 0 ;;
|
|
278
|
+
esac
|
|
279
|
+
REPLY="$(assignes "$1")"
|
|
280
|
+
REPLY="${REPLY%%$'\n'*}"
|
|
216
281
|
}
|
|
217
282
|
|
|
218
283
|
# Premier push sur la branche d'un ticket : PR brouillon vers la branche
|
|
219
284
|
# d'intégration, titrée par le premier commit (repli : le ticket), à la
|
|
220
|
-
# description reprise du ticket, citant le
|
|
221
|
-
#
|
|
222
|
-
# des Actions, elle ne lance pas la CI : elle tourne au push suivant, ou au
|
|
285
|
+
# description reprise du ticket, citant le ticket, assignée à l'auteur du
|
|
286
|
+
# push (une personne, jamais un bot ; repli : l'assigné du ticket ; un refus
|
|
287
|
+
# n'est qu'un avertissement) ; ticket en cours. Ouverte par le jeton des Actions, elle ne lance pas la CI : elle tourne au push suivant, ou au
|
|
223
288
|
# passage « prête pour revue ». Tant qu'elle est en brouillon, chaque push met
|
|
224
289
|
# à jour son titre et sa description ; une PR prête n'est plus touchée.
|
|
225
290
|
cmd_push() {
|
|
226
|
-
local n pr titre corps url
|
|
291
|
+
local n pr titre corps url qui
|
|
227
292
|
: "${PUSH_BRANCH:?}" "${SHA:?}"
|
|
228
293
|
[ "${DELETED:-false}" != true ] || return 0
|
|
229
294
|
branch_ticket_r "$PUSH_BRANCH"
|
|
@@ -245,8 +310,15 @@ cmd_push() {
|
|
|
245
310
|
return 0
|
|
246
311
|
fi
|
|
247
312
|
url="$(gh pr create --draft --base "$INTEGRATION" --head "$PUSH_BRANCH" --title "$titre" \
|
|
248
|
-
--body "$corps"
|
|
249
|
-
|
|
313
|
+
--body "$corps")"
|
|
314
|
+
# assignation à part : un refus de GitHub ne doit pas faire échouer le suivi
|
|
315
|
+
assigne_pr_r "$n"
|
|
316
|
+
qui="$REPLY"
|
|
317
|
+
if [ -n "$qui" ]; then
|
|
318
|
+
gh pr edit "${url##*/}" --add-assignee "$qui" >/dev/null 2>&1 ||
|
|
319
|
+
avertir_t tk.assign_failed "${url##*/}" "$qui"
|
|
320
|
+
[ "$qui" != "${PUSHER:-}" ] || gh issue edit "$n" --add-assignee "$qui" >/dev/null 2>&1 || true
|
|
321
|
+
fi
|
|
250
322
|
est_valide "$n" && statut "$n" "$S_ENCOURS"
|
|
251
323
|
notice_t tk.draft_opened "${url##*/}" "$n"
|
|
252
324
|
verifier "${url##*/}" "$SHA" "$n" "$PUSH_BRANCH" || true
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Garde-fou repowarden pour opencode 2.x (écrit par repowarden agents init) :
|
|
2
|
+
// chaque outil est soumis à « repowarden garde » avant de s'exécuter. Refus
|
|
3
|
+
// (code 2) → erreur rendue à l'agent, avec la commande à lancer. repowarden
|
|
4
|
+
// absent ou en panne : rien n'est bloqué. opencode 1.x : repowarden-garde.js,
|
|
5
|
+
// à côté (un module à export par défaut y passerait pour un plugin 2.x).
|
|
6
|
+
// Plugins opencode 2.x : export par défaut { id, setup }, crochet
|
|
7
|
+
// ctx.tool.hook("execute.before") (outils edit, write, shell…)
|
|
8
|
+
import { spawnSync } from "node:child_process"
|
|
9
|
+
|
|
10
|
+
// Soumet l'appel d'outil à repowarden garde ; l'erreur levée par le crochet
|
|
11
|
+
// empêche l'outil de s'exécuter et son message est rendu à l'agent
|
|
12
|
+
function garde(tool, args, cwd) {
|
|
13
|
+
const r = spawnSync("repowarden", ["garde"], {
|
|
14
|
+
input: JSON.stringify({ tool, args, cwd }),
|
|
15
|
+
encoding: "utf8",
|
|
16
|
+
})
|
|
17
|
+
if (r.status === 2) throw new Error((r.stderr || "").trim())
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default {
|
|
21
|
+
id: "repowarden-garde",
|
|
22
|
+
// Sans ctx.tool.hook (opencode 1.x charge aussi ce fichier) : rien
|
|
23
|
+
setup: (ctx) => {
|
|
24
|
+
if (typeof ctx?.tool?.hook !== "function") return
|
|
25
|
+
const dossier = ctx.location?.directory || process.cwd()
|
|
26
|
+
return ctx.tool.hook("execute.before", (appel) => garde(appel.tool, appel.input, dossier))
|
|
27
|
+
},
|
|
28
|
+
}
|
package/hooks/lib/agents.sh
CHANGED
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
# Forme actuelle du garde-fou : un garde-fou posé par repowarden qui en diffère
|
|
6
6
|
# date d'une version antérieure et est mis à jour (#470)
|
|
7
|
-
|
|
7
|
+
AGENTS_MODELES="$(cd "$(dirname "${BASH_SOURCE[0]}")/../agents" && pwd -P)"
|
|
8
|
+
# Plugins opencode, un par API : 1.x lit l'export nommé, 2.x l'export par
|
|
9
|
+
# défaut, et 1.x ignore l'export nommé d'un module qui a les deux (#469)
|
|
10
|
+
AGENTS_PLUGINS_OPENCODE="repowarden-garde.js repowarden-garde-v2.js"
|
|
8
11
|
AGENTS_COMMANDE_CLAUDE="repowarden garde --format claude"
|
|
9
12
|
AGENTS_MATCHER_CLAUDE="Edit|Write|MultiEdit|NotebookEdit|Bash"
|
|
10
13
|
|
|
@@ -25,10 +28,10 @@ agents_plugin_repowarden() {
|
|
|
25
28
|
return 1
|
|
26
29
|
}
|
|
27
30
|
|
|
28
|
-
# Vrai si le plugin $1 vient de repowarden mais diffère du modèle actuel
|
|
29
|
-
# posé par une version antérieure, il n'a pas suivi les mises à jour
|
|
31
|
+
# Vrai si le plugin $1 vient de repowarden mais diffère du modèle actuel (même
|
|
32
|
+
# nom) : posé par une version antérieure, il n'a pas suivi les mises à jour
|
|
30
33
|
agents_plugin_perime() {
|
|
31
|
-
agents_plugin_repowarden "$1" && ! cmp -s "$1" "$
|
|
34
|
+
agents_plugin_repowarden "$1" && ! cmp -s "$1" "$AGENTS_MODELES/$(basename "$1")"
|
|
32
35
|
}
|
|
33
36
|
|
|
34
37
|
# Vrai si le settings.json $1 a un hook « repowarden garde… » d'une autre forme
|
|
@@ -47,15 +50,25 @@ agents_perimes() {
|
|
|
47
50
|
agents_dossiers_globaux
|
|
48
51
|
if [ -n "${1:-}" ]; then
|
|
49
52
|
! agents_claude_perime "$1/.claude/settings.json" || echo "projet claude $1/.claude/settings.json"
|
|
50
|
-
|
|
51
|
-
echo "projet opencode $1/.opencode/plugins/repowarden-garde.js"
|
|
53
|
+
agents_opencode_perimes projet "$1/.opencode/plugins"
|
|
52
54
|
fi
|
|
53
55
|
! agents_claude_perime "$AGENTS_CLAUDE_DIR/settings.json" || echo "global claude $AGENTS_CLAUDE_DIR/settings.json"
|
|
54
|
-
|
|
55
|
-
echo "global opencode $AGENTS_OPENCODE_DIR/plugins/repowarden-garde.js"
|
|
56
|
+
agents_opencode_perimes global "$AGENTS_OPENCODE_DIR/plugins"
|
|
56
57
|
return 0
|
|
57
58
|
}
|
|
58
59
|
|
|
60
|
+
# Plugins opencode périmés du dossier $2 (portée $1) : différents du modèle, ou
|
|
61
|
+
# absents à côté d'un plugin repowarden (posé avant le plugin 2.x, #469)
|
|
62
|
+
agents_opencode_perimes() {
|
|
63
|
+
local p
|
|
64
|
+
for p in $AGENTS_PLUGINS_OPENCODE; do
|
|
65
|
+
if agents_plugin_perime "$2/$p" ||
|
|
66
|
+
{ [ ! -e "$2/$p" ] && agents_plugin_repowarden "$2/repowarden-garde.js"; }; then
|
|
67
|
+
echo "$1 opencode $2/$p"
|
|
68
|
+
fi
|
|
69
|
+
done
|
|
70
|
+
}
|
|
71
|
+
|
|
59
72
|
# Vrai si Claude Code ou opencode est sur ce poste (commande, ou dossier de
|
|
60
73
|
# configuration) sans garde-fou branché, ni dans le dépôt $1 (racine, vide hors
|
|
61
74
|
# dépôt) ni pour l'utilisateur : ses actions ne seraient vues qu'au commit
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Carte du projet (repowarden carte) : quoi fait quoi et pourquoi, sans LLM ni
|
|
3
|
+
# réseau, pour qu'un agent (ou une personne) lise l'extrait utile au lieu
|
|
4
|
+
# d'explorer le dépôt. Fichier .repowarden/carte/carte.json, dossier ignoré
|
|
5
|
+
# par git ; clés triées, ni date ni chemin absolu : même dépôt, même fichier.
|
|
6
|
+
# Les fonctions et leur « pourquoi » viennent de la détection du contrôle
|
|
7
|
+
# « fonctions sans commentaire » (commentaires.sh) : pas de second analyseur.
|
|
8
|
+
# Demande commentaires.sh (via lang.sh) et common.sh ; lancé à la racine.
|
|
9
|
+
# La conception des tickets est résumée comme pour les PR brouillon du bot :
|
|
10
|
+
# mêmes lecteurs (ci/tickets/push.sh), pas un second.
|
|
11
|
+
# shellcheck disable=SC2016,SC2013 # programmes jq et awk entre apostrophes ; numéros de tickets, un mot par ligne
|
|
12
|
+
_carte_ci="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../ci/tickets" && pwd -P)"
|
|
13
|
+
# shellcheck source=../../ci/tickets/commun.sh
|
|
14
|
+
source "$_carte_ci/commun.sh"
|
|
15
|
+
# shellcheck source=../../ci/tickets/push.sh
|
|
16
|
+
source "$_carte_ci/push.sh"
|
|
17
|
+
|
|
18
|
+
CARTE_DIR=.repowarden/carte
|
|
19
|
+
CARTE_JSON="$CARTE_DIR/carte.json"
|
|
20
|
+
|
|
21
|
+
# jq sans retour chariot : sous Windows, jq finit ses lignes par CRLF, et un
|
|
22
|
+
# read garderait le CR collé à la dernière valeur lue
|
|
23
|
+
cjq() { jq "$@" | tr -d '\r'; }
|
|
24
|
+
|
|
25
|
+
# Fichiers lus par la carte, dans le dossier $1 : « shell » (fichiers
|
|
26
|
+
# cartographiés), « tous » (fichiers du dépôt, rattachables à des tickets),
|
|
27
|
+
# tous deux hors repowarden.exclude, et « tests » (*.bats, tous)
|
|
28
|
+
carte_fichiers() {
|
|
29
|
+
local f
|
|
30
|
+
: >"$1/shell"
|
|
31
|
+
: >"$1/tests"
|
|
32
|
+
: >"$1/tous"
|
|
33
|
+
while IFS= read -r -d '' f; do
|
|
34
|
+
[ -f "$f" ] || continue
|
|
35
|
+
case "$f" in *.bats) printf '%s\n' "$f" >>"$1/tests" ;; esac
|
|
36
|
+
is_excluded "$f" && continue
|
|
37
|
+
printf '%s\n' "$f" >>"$1/tous"
|
|
38
|
+
case "$f" in *.bats) continue ;; esac
|
|
39
|
+
comments_lang_r "$f" "$f"
|
|
40
|
+
[ "$REPLY" != shell ] || printf '%s\n' "$f" >>"$1/shell"
|
|
41
|
+
done < <(git ls-files -z -co --exclude-standard)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# Écrit la carte (remplacement atomique : une lecture concurrente voit
|
|
45
|
+
# l'ancienne ou la nouvelle) ; le dossier s'ignore lui-même pour git
|
|
46
|
+
carte_generer() {
|
|
47
|
+
local d f
|
|
48
|
+
local -a shell=() tests=()
|
|
49
|
+
mkdir -p "$CARTE_DIR"
|
|
50
|
+
[ -f "$CARTE_DIR/.gitignore" ] || printf '*\n' >"$CARTE_DIR/.gitignore"
|
|
51
|
+
d="$(mktemp -d)"
|
|
52
|
+
carte_fichiers "$d"
|
|
53
|
+
while IFS= read -r f; do shell+=("$f"); done <"$d/shell"
|
|
54
|
+
while IFS= read -r f; do tests+=("$f"); done <"$d/tests"
|
|
55
|
+
: >"$d/fonctions"
|
|
56
|
+
: >"$d/liens"
|
|
57
|
+
# « fichier<TAB>ticket » par fichier modifié par un ticket (git seul)
|
|
58
|
+
git -c core.quotePath=false log --no-color --diff-merges=first-parent --name-only \
|
|
59
|
+
--format='%x1e%s%n%b%x1f' 2>/dev/null |
|
|
60
|
+
awk -v D=$'\x1e' -v F=$'\x1f' "$CARTE_HIST_AWK" >"$d/historique" || true
|
|
61
|
+
# Conceptions en cache, « \x1d<n> » puis le résumé, ticket par ticket
|
|
62
|
+
# Hors ligne (post-merge) : seules les conceptions déjà en cache servent
|
|
63
|
+
[ -n "${REPOWARDEN_CARTE_HORS_LIGNE:-}" ] || carte_conceptions "$d/historique"
|
|
64
|
+
: >"$d/conceptions"
|
|
65
|
+
for f in $(cut -f2 "$d/historique" | sort -un); do
|
|
66
|
+
[ -s "$CARTE_DIR/conception/$f.md" ] || continue
|
|
67
|
+
printf '\035%s\n' "$f" >>"$d/conceptions"
|
|
68
|
+
cat "$CARTE_DIR/conception/$f.md" >>"$d/conceptions"
|
|
69
|
+
done
|
|
70
|
+
if [ ${#shell[@]} -gt 0 ]; then
|
|
71
|
+
# « fichier<TAB>ligne<TAB>nom<TAB>pourquoi » par fonction
|
|
72
|
+
awk -v lang=shell -v q="'" "$COMMENTS_AWK_LIB$CARTE_AWK" "${shell[@]}" >"$d/fonctions"
|
|
73
|
+
# « fichier<TAB>test » par test qui le couvre
|
|
74
|
+
[ ${#tests[@]} -eq 0 ] ||
|
|
75
|
+
awk "$CARTE_TESTS_AWK" "$d/shell" "${tests[@]}" >"$d/liens"
|
|
76
|
+
fi
|
|
77
|
+
jq -n -S --rawfile s "$d/shell" --rawfile fo "$d/fonctions" --rawfile li "$d/liens" \
|
|
78
|
+
--rawfile to "$d/tous" --rawfile hi "$d/historique" --rawfile co "$d/conceptions" '
|
|
79
|
+
def lignes($x): $x | split("\n") | map(select(length > 0) | split("\t"));
|
|
80
|
+
(lignes($fo) | group_by(.[0]) | map({key: .[0][0], value:
|
|
81
|
+
map({nom: .[2], ligne: (.[1] | tonumber), pourquoi: (.[3] // "")})
|
|
82
|
+
| sort_by(.ligne)}) | from_entries) as $fn
|
|
83
|
+
| (lignes($li) | group_by(.[0])
|
|
84
|
+
| map({key: .[0][0], value: (map(.[1]) | unique)}) | from_entries) as $te
|
|
85
|
+
| ($co | split("\u001d") | map(select(length > 0) | index("\n") as $i
|
|
86
|
+
| {key: .[:$i], value: (.[$i + 1:] | rtrimstr("\n"))}) | from_entries) as $cn
|
|
87
|
+
| (lignes($to) | map({key: .[0], value: 1}) | from_entries) as $ok
|
|
88
|
+
| (lignes($hi) | map(select($ok[.[0]] != null))) as $h
|
|
89
|
+
| ($h | group_by(.[0])
|
|
90
|
+
| map({key: .[0][0], value: (map(.[1] | tonumber) | unique)}) | from_entries) as $tf
|
|
91
|
+
| {version: 1,
|
|
92
|
+
fichiers: (($tf | map_values({tickets: .})) + (lignes($s) | map(.[0])
|
|
93
|
+
| map({key: ., value: {fonctions: ($fn[.] // []), tests: ($te[.] // []),
|
|
94
|
+
tickets: ($tf[.] // [])}})
|
|
95
|
+
| from_entries)),
|
|
96
|
+
tickets: ($h | group_by(.[1])
|
|
97
|
+
| map({key: .[0][1], value: ({fichiers: (map(.[0]) | unique)}
|
|
98
|
+
+ (if $cn[.[0][1]] then {conception: $cn[.[0][1]]} else {} end))})
|
|
99
|
+
| from_entries)}' >"$d/carte.json"
|
|
100
|
+
mv -f "$d/carte.json" "$CARTE_JSON"
|
|
101
|
+
rm -rf "$d"
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
# Vrai si la carte existe et qu'aucun fichier du dépôt (suivi, ou nouveau non
|
|
105
|
+
# ignoré) ni l'index git n'est plus récent : sinon elle ne dit plus le code
|
|
106
|
+
carte_a_jour() {
|
|
107
|
+
local f
|
|
108
|
+
[ -f "$CARTE_JSON" ] || return 1
|
|
109
|
+
f="$(git rev-parse --git-path index)"
|
|
110
|
+
[ ! "$f" -nt "$CARTE_JSON" ] || return 1
|
|
111
|
+
while IFS= read -r -d '' f; do
|
|
112
|
+
[ ! "$f" -nt "$CARTE_JSON" ] || return 1
|
|
113
|
+
done < <(git ls-files -z -co --exclude-standard)
|
|
114
|
+
return 0
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
# Fonctions du fichier $1 (chemin depuis la racine) : une ligne chacune,
|
|
118
|
+
# « ligne nom — pourquoi », puis ses tests et tickets ; JSON si $2 = json
|
|
119
|
+
carte_fichier() {
|
|
120
|
+
local f="$1" ligne nom pourquoi tests tickets n=0
|
|
121
|
+
if [ "${2:-}" = json ]; then
|
|
122
|
+
jq --arg f "$f" '{fichier: $f} + (.fichiers[$f] // {fonctions: []})' "$CARTE_JSON"
|
|
123
|
+
return 0
|
|
124
|
+
fi
|
|
125
|
+
printf '%s%s%s\n' "$UI_B" "$f" "$UI_N"
|
|
126
|
+
while IFS=$'\t' read -r ligne nom pourquoi; do
|
|
127
|
+
n=$((n + 1))
|
|
128
|
+
if [ -n "$pourquoi" ]; then
|
|
129
|
+
printf ' %s %s — %s\n' "$ligne" "$nom" "$pourquoi"
|
|
130
|
+
else
|
|
131
|
+
t carte.no_comment
|
|
132
|
+
printf ' %s %s %s\n' "$ligne" "$nom" "$REPLY"
|
|
133
|
+
fi
|
|
134
|
+
done < <(cjq -r --arg f "$f" '(.fichiers[$f].fonctions // [])[] | "\(.ligne)\t\(.nom)\t\(.pourquoi)"' "$CARTE_JSON")
|
|
135
|
+
[ "$n" -gt 0 ] || info_t carte.file_empty "$f"
|
|
136
|
+
# Tests liés (fichiers cartographiés) : ceux à écrire d'abord, puis à relancer
|
|
137
|
+
if jq -e --arg f "$f" '.fichiers[$f] | has("tests")' "$CARTE_JSON" >/dev/null 2>&1; then
|
|
138
|
+
tests="$(cjq -r --arg f "$f" '.fichiers[$f].tests | join(" ")' "$CARTE_JSON")"
|
|
139
|
+
if [ -n "$tests" ]; then
|
|
140
|
+
echo_t carte.tests "$tests"
|
|
141
|
+
echo_t carte.tests_run "bats $tests"
|
|
142
|
+
else
|
|
143
|
+
info_t carte.no_tests "$f"
|
|
144
|
+
fi
|
|
145
|
+
fi
|
|
146
|
+
# Tickets qui l'ont modifié : le pourquoi de son histoire
|
|
147
|
+
tickets="$(cjq -r --arg f "$f" '(.fichiers[$f].tickets // []) | map("#\(.)") | join(" ")' "$CARTE_JSON")"
|
|
148
|
+
[ -z "$tickets" ] || echo_t carte.tickets "$tickets"
|
|
149
|
+
# Conception de chacun de ces tickets : sa première ligne utile
|
|
150
|
+
cjq -r --arg f "$f" '. as $c | (.fichiers[$f].tickets // [])[] | tostring as $n
|
|
151
|
+
| ($c.tickets[$n].conception // empty) | split("\n")
|
|
152
|
+
| map(select(test("^\\s*(#|$)") | not)) | first // empty
|
|
153
|
+
| " #\($n) — \(sub("^\\s*[-*]\\s*"; ""))"' "$CARTE_JSON"
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
# Met en cache (conception/<n>.md, vide sans conception) le résumé de chaque
|
|
157
|
+
# ticket de l'historique $1 absent du cache : un seul appel gh, jamais bloquant
|
|
158
|
+
carte_conceptions() {
|
|
159
|
+
local n manquants="" tmp ligne num="" texte=""
|
|
160
|
+
mkdir -p "$CARTE_DIR/conception"
|
|
161
|
+
for n in $(cut -f2 "$1" | sort -un); do
|
|
162
|
+
[ -f "$CARTE_DIR/conception/$n.md" ] || manquants="$manquants $n"
|
|
163
|
+
done
|
|
164
|
+
[ -n "$manquants" ] || return 0
|
|
165
|
+
if ! has gh || ! gh auth token >/dev/null 2>&1; then
|
|
166
|
+
attention_t carte.no_gh
|
|
167
|
+
return 0
|
|
168
|
+
fi
|
|
169
|
+
tmp="$(mktemp)"
|
|
170
|
+
if ! gh issue list --state all --limit 1000 --json number,comments >"$tmp" 2>/dev/null; then
|
|
171
|
+
attention_t carte.no_gh
|
|
172
|
+
rm -f "$tmp"
|
|
173
|
+
return 0
|
|
174
|
+
fi
|
|
175
|
+
# Par ticket : « \x1d<n> » puis ses commentaires de mainteneur, chacun
|
|
176
|
+
# suivi de SEP_COMMENTAIRE (forme de commentaires_mainteneur_r) ; une
|
|
177
|
+
# dernière ligne « \x1d » seule clôt le dernier ticket
|
|
178
|
+
while IFS= read -r ligne; do
|
|
179
|
+
if [[ "$ligne" == $'\035'* ]]; then
|
|
180
|
+
if [ -n "$num" ] && [[ " $manquants " == *" $num "* ]]; then
|
|
181
|
+
carte_resume_r "$texte"
|
|
182
|
+
printf '%s' "$REPLY" >"$CARTE_DIR/conception/$num.md"
|
|
183
|
+
fi
|
|
184
|
+
num="${ligne#$'\035'}" texte=""
|
|
185
|
+
[ -n "$num" ] || break
|
|
186
|
+
continue
|
|
187
|
+
fi
|
|
188
|
+
texte="$texte$ligne"$'\n'
|
|
189
|
+
done < <(cjq -r '.[] | "\u001d\(.number)", (.comments[]
|
|
190
|
+
| select(.authorAssociation == "OWNER" or .authorAssociation == "MEMBER" or .authorAssociation == "COLLABORATOR")
|
|
191
|
+
| .body + "\n\u001e")' "$tmp"; printf '\035\n')
|
|
192
|
+
rm -f "$tmp"
|
|
193
|
+
# Tickets absents de la réponse : sans conception, pas redemandés
|
|
194
|
+
for n in $manquants; do
|
|
195
|
+
[ -f "$CARTE_DIR/conception/$n.md" ] || : >"$CARTE_DIR/conception/$n.md"
|
|
196
|
+
done
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
# REPLY = résumé de conception des commentaires $1 : « Ce qui change » et
|
|
200
|
+
# « Hors périmètre » de la dernière conception, puis les ajouts
|
|
201
|
+
carte_resume_r() {
|
|
202
|
+
local conception
|
|
203
|
+
resume_conception_r "$1"
|
|
204
|
+
conception="$REPLY"
|
|
205
|
+
ajouts_ticket_r "$1"
|
|
206
|
+
REPLY="$conception${conception:+${REPLY:+$'\n\n'}}$REPLY"
|
|
207
|
+
[ -z "$REPLY" ] || REPLY="$REPLY"$'\n'
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
# Fichiers modifiés par le ticket $1 (numéro, sans #), puis sa conception ;
|
|
211
|
+
# JSON si $2 = json.
|
|
212
|
+
# Code 1 si l'historique git ne le cite pas
|
|
213
|
+
carte_ticket() {
|
|
214
|
+
local n="$1" f c=0 conception
|
|
215
|
+
if [ "${2:-}" = json ]; then
|
|
216
|
+
jq -e -S --arg n "$n" '.tickets[$n] // empty | {ticket: ($n | tonumber)} + .' "$CARTE_JSON" && return 0
|
|
217
|
+
err_t carte.ticket_unknown "$n" "$n" "$n"
|
|
218
|
+
return 1
|
|
219
|
+
fi
|
|
220
|
+
while IFS= read -r f; do
|
|
221
|
+
c=$((c + 1))
|
|
222
|
+
[ "$c" -gt 1 ] || { t carte.ticket_title "$n"; printf '%s%s%s\n' "$UI_B" "$REPLY" "$UI_N"; }
|
|
223
|
+
printf ' %s\n' "$f"
|
|
224
|
+
done < <(cjq -r --arg n "$n" '(.tickets[$n].fichiers // [])[]' "$CARTE_JSON")
|
|
225
|
+
if [ "$c" -gt 0 ]; then
|
|
226
|
+
conception="$(cjq -r --arg n "$n" '.tickets[$n].conception // empty' "$CARTE_JSON")"
|
|
227
|
+
[ -z "$conception" ] || { echo_t carte.conception; printf '%s\n' "$conception" | sed 's/^/ /'; }
|
|
228
|
+
return 0
|
|
229
|
+
fi
|
|
230
|
+
err_t carte.ticket_unknown "$n" "$n" "$n"
|
|
231
|
+
return 1
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
# Définitions de la fonction $1 : « nom fichier:ligne » puis son pourquoi ;
|
|
235
|
+
# JSON (liste) si $2 = json. Code 1 si la carte ne la connaît pas
|
|
236
|
+
carte_fonction() {
|
|
237
|
+
local nom="$1" f ligne pourquoi n=0
|
|
238
|
+
if [ "${2:-}" = json ]; then
|
|
239
|
+
jq -e --arg n "$nom" '[.fichiers | to_entries[] | .key as $f
|
|
240
|
+
| .value.fonctions[]? | select(.nom == $n) | {fichier: $f} + .]
|
|
241
|
+
| if length > 0 then . else empty end' "$CARTE_JSON" && return 0
|
|
242
|
+
err_t carte.unknown "$nom"
|
|
243
|
+
return 1
|
|
244
|
+
fi
|
|
245
|
+
while IFS=$'\t' read -r f ligne pourquoi; do
|
|
246
|
+
n=$((n + 1))
|
|
247
|
+
printf '%s%s%s %s:%s\n' "$UI_B" "$nom" "$UI_N" "$f" "$ligne"
|
|
248
|
+
if [ -n "$pourquoi" ]; then
|
|
249
|
+
printf ' %s\n' "$pourquoi"
|
|
250
|
+
else
|
|
251
|
+
t carte.no_comment
|
|
252
|
+
printf ' %s\n' "$REPLY"
|
|
253
|
+
fi
|
|
254
|
+
done < <(cjq -r --arg n "$nom" '.fichiers | to_entries[] | .key as $f
|
|
255
|
+
| .value.fonctions[]? | select(.nom == $n) | "\($f)\t\(.ligne)\t\(.pourquoi)"' "$CARTE_JSON")
|
|
256
|
+
[ "$n" -gt 0 ] && return 0
|
|
257
|
+
err_t carte.unknown "$nom"
|
|
258
|
+
return 1
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
# Programme awk de la carte : chaque fonction de chaque fichier lu, avec sa
|
|
262
|
+
# ligne et le texte de son commentaire (vide s'il n'y en a pas)
|
|
263
|
+
# shellcheck disable=SC2016 # programme awk, rien à développer
|
|
264
|
+
CARTE_AWK='
|
|
265
|
+
# Fonctions du fichier f (lignes L[1..N])
|
|
266
|
+
function sortie( i, n) {
|
|
267
|
+
for (i = 1; i <= N; i++) {
|
|
268
|
+
n = nom(L[i])
|
|
269
|
+
if (n != "") printf "%s\t%d\t%s\t%s\n", f, i, n, commentaire(i)
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
FNR == 1 { if (NR > 1) sortie(); f = FILENAME; N = 0 }
|
|
273
|
+
# Fichier en CRLF (extraction Windows) : le CR ne fait pas partie du texte
|
|
274
|
+
{ sub(/\r$/, ""); L[FNR] = $0; N = FNR }
|
|
275
|
+
END { if (NR > 0) sortie() }'
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
# Programme awk des tests liés : lit la liste des fichiers cartographiés, puis
|
|
279
|
+
# chaque test ; relie un fichier à un test par le nommage (même nom sans
|
|
280
|
+
# extension), par son chemin cité, ou par « repowarden <commande> » (bin/…)
|
|
281
|
+
# shellcheck disable=SC2016 # programme awk, rien à développer
|
|
282
|
+
CARTE_TESTS_AWK='
|
|
283
|
+
# Nom sans dossier ni extension : bin/garde, test/garde.bats → garde
|
|
284
|
+
function base(p) { sub(/^.*\//, "", p); sub(/\.[^.\/]*$/, "", p); return p }
|
|
285
|
+
# Vrai si le texte x cite le chemin p, sans autre caractère de nom collé
|
|
286
|
+
function cite(x, p, k, av, ap) {
|
|
287
|
+
while ((k = index(x, p)) > 0) {
|
|
288
|
+
av = (k > 1) ? substr(x, k - 1, 1) : ""
|
|
289
|
+
ap = substr(x, k + length(p), 1)
|
|
290
|
+
if (av !~ /[A-Za-z0-9_-]/ && ap !~ /[A-Za-z0-9_.-]/) return 1
|
|
291
|
+
x = substr(x, k + 1)
|
|
292
|
+
}
|
|
293
|
+
return 0
|
|
294
|
+
}
|
|
295
|
+
# Liens du test t (texte complet x) vers les fichiers cartographiés
|
|
296
|
+
function liens( i, bt, r, c, cmd) {
|
|
297
|
+
bt = base(t)
|
|
298
|
+
split("", cmd)
|
|
299
|
+
r = x
|
|
300
|
+
while (match(r, /repowarden[ \t]+[a-z][a-z0-9-]*/)) {
|
|
301
|
+
c = substr(r, RSTART, RLENGTH); sub(/^repowarden[ \t]+/, "", c)
|
|
302
|
+
cmd["bin/" c] = 1
|
|
303
|
+
r = substr(r, RSTART + RLENGTH)
|
|
304
|
+
}
|
|
305
|
+
for (i = 1; i <= nf; i++)
|
|
306
|
+
if (base(F[i]) == bt || (F[i] in cmd) || cite(x, F[i])) print F[i] "\t" t
|
|
307
|
+
}
|
|
308
|
+
FILENAME == ARGV[1] { F[++nf] = $0; next }
|
|
309
|
+
FNR == 1 { if (t != "") liens(); t = FILENAME; x = "" }
|
|
310
|
+
{ sub(/\r$/, ""); x = x "\n" $0 }
|
|
311
|
+
END { if (t != "") liens() }'
|
|
312
|
+
|
|
313
|
+
# Programme awk de l'historique : lit « git log --name-only » (en-tête de
|
|
314
|
+
# commit précédé de D, fin du message marquée par F) et écrit
|
|
315
|
+
# « fichier<TAB>ticket » pour chaque ticket du commit (pieds « Ticket : #n »,
|
|
316
|
+
# branche type/<n>-sujet d'une fusion) et chaque fichier modifié
|
|
317
|
+
# shellcheck disable=SC2016 # programme awk, rien à développer
|
|
318
|
+
CARTE_HIST_AWK='
|
|
319
|
+
# Tickets cités par la ligne l, si c est un pied « Ticket(s) : #n »
|
|
320
|
+
function pied(l) {
|
|
321
|
+
if (l !~ /^[ \t]*[Tt][Ii][Cc][Kk][Ee][Tt][Ss]?[ \t]*:[ \t]*#[0-9]/) return
|
|
322
|
+
while (match(l, /#[0-9]+/)) {
|
|
323
|
+
T[substr(l, RSTART + 1, RLENGTH - 1) + 0] = 1
|
|
324
|
+
l = substr(l, RSTART + RLENGTH)
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
index($0, D) == 1 {
|
|
328
|
+
split("", T); msg = 1; $0 = substr($0, 2)
|
|
329
|
+
if ($0 ~ /^Merge / && match($0, /[A-Za-z]+\/[0-9]+-/)) {
|
|
330
|
+
b = substr($0, RSTART, RLENGTH); sub(/^[A-Za-z]+\//, "", b); T[b + 0] = 1
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
msg {
|
|
334
|
+
k = index($0, F)
|
|
335
|
+
if (k > 0) { pied(substr($0, 1, k - 1)); msg = 0 } else pied($0)
|
|
336
|
+
next
|
|
337
|
+
}
|
|
338
|
+
{ sub(/\r$/, "") }
|
|
339
|
+
$0 != "" { for (n in T) print $0 "\t" n }'
|
|
@@ -9,27 +9,17 @@
|
|
|
9
9
|
# Une ligne « fichier:ligne<TAB>nom » par fonction ajoutée sans commentaire
|
|
10
10
|
# entre $1 (commit de base) et $2 (commit, ou vide pour le contenu stagé).
|
|
11
11
|
comments_missing() {
|
|
12
|
-
local base="$1" new="$2"
|
|
12
|
+
local base="$1" new="$2" a b f old lang added tmp
|
|
13
13
|
tmp="$(mktemp -d)"
|
|
14
|
-
while IFS=$'\t' read -r
|
|
14
|
+
while IFS=$'\t' read -r _ a b; do
|
|
15
15
|
[ -n "$a" ] || continue
|
|
16
16
|
# Renommage ou copie : l'ancien chemin donne les fonctions existantes
|
|
17
17
|
if [ -n "$b" ]; then old="$a" f="$b"; else old="$a" f="$a"; fi
|
|
18
18
|
is_excluded "$f" && continue
|
|
19
|
-
case "${f##*/}" in
|
|
20
|
-
*.sh | *.bash) lang=shell ;;
|
|
21
|
-
*.py) lang=py ;;
|
|
22
|
-
*.js | *.jsx | *.mjs | *.cjs | *.ts | *.tsx) lang=js ;;
|
|
23
|
-
*.*) continue ;;
|
|
24
|
-
*) lang=shebang ;;
|
|
25
|
-
esac
|
|
26
19
|
git show "$new:$f" >"$tmp/new" 2>/dev/null || continue
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
[[ "$st" =~ ^#!.*[/\ ](ba)?sh([[:space:]]|$) ]] || continue
|
|
31
|
-
lang=shell
|
|
32
|
-
fi
|
|
20
|
+
comments_lang_r "$f" "$tmp/new"
|
|
21
|
+
lang="$REPLY"
|
|
22
|
+
[ -n "$lang" ] || continue
|
|
33
23
|
git show "$base:$old" >"$tmp/base" 2>/dev/null || : >"$tmp/base"
|
|
34
24
|
if [ -n "$new" ]; then
|
|
35
25
|
git diff -U0 --no-color --no-ext-diff "$base" "$new" -- "$f"
|
|
@@ -42,7 +32,7 @@ comments_missing() {
|
|
|
42
32
|
for (i = 0; i < n; i++) printf " %d", s + i
|
|
43
33
|
}' "$tmp/diff") "
|
|
44
34
|
[ "$added" != " " ] || continue
|
|
45
|
-
awk -v lang="$lang" -v added="$added" -v f="$f" -v q="'" "$COMMENTS_AWK" "$tmp/base" "$tmp/new"
|
|
35
|
+
awk -v lang="$lang" -v added="$added" -v f="$f" -v q="'" "$COMMENTS_AWK_LIB$COMMENTS_AWK" "$tmp/base" "$tmp/new"
|
|
46
36
|
done < <(
|
|
47
37
|
if [ -n "$new" ]; then
|
|
48
38
|
git diff --name-status -M --diff-filter=ACMR "$base" "$new"
|
|
@@ -53,6 +43,22 @@ comments_missing() {
|
|
|
53
43
|
rm -rf "$tmp"
|
|
54
44
|
}
|
|
55
45
|
|
|
46
|
+
# REPLY = langage du fichier $1 pour la détection des fonctions (shell, py,
|
|
47
|
+
# js), vide sinon ; $2 = son contenu, dont le shebang décide sans extension.
|
|
48
|
+
# Partagé par le contrôle et la carte (repowarden carte) : une seule détection.
|
|
49
|
+
comments_lang_r() {
|
|
50
|
+
local l=""
|
|
51
|
+
case "${1##*/}" in
|
|
52
|
+
*.sh | *.bash) REPLY=shell; return 0 ;;
|
|
53
|
+
*.py) REPLY=py; return 0 ;;
|
|
54
|
+
*.js | *.jsx | *.mjs | *.cjs | *.ts | *.tsx) REPLY=js; return 0 ;;
|
|
55
|
+
*.*) REPLY=""; return 0 ;;
|
|
56
|
+
esac
|
|
57
|
+
# Script sans extension (bin/…) : seul un shebang sh ou bash compte
|
|
58
|
+
IFS= read -r l <"$2" 2>/dev/null || true
|
|
59
|
+
if [[ "$l" =~ ^#!.*[/\ ](ba)?sh([[:space:]]|$) ]]; then REPLY=shell; else REPLY=""; fi
|
|
60
|
+
}
|
|
61
|
+
|
|
56
62
|
# Avertit des fonctions ajoutées sans commentaire, sans jamais bloquer : le
|
|
57
63
|
# commentaire aide la relecture, son absence n'est pas une faute. REPLY = nombre.
|
|
58
64
|
comments_check() {
|
|
@@ -66,11 +72,10 @@ comments_check() {
|
|
|
66
72
|
REPLY="$n"
|
|
67
73
|
}
|
|
68
74
|
|
|
69
|
-
#
|
|
70
|
-
# fonctions
|
|
71
|
-
# dont aucune ligne de commentaire ne précède la déclaration
|
|
75
|
+
# Bibliothèque awk commune au contrôle et à la carte : déclarations de
|
|
76
|
+
# fonctions et commentaire au-dessus (variables lang et q)
|
|
72
77
|
# shellcheck disable=SC2016 # programme awk, rien à développer
|
|
73
|
-
|
|
78
|
+
COMMENTS_AWK_LIB='
|
|
74
79
|
# Nom de la fonction déclarée par la ligne l (vide sinon), selon le langage
|
|
75
80
|
function nom(l) {
|
|
76
81
|
if (lang == "shell") {
|
|
@@ -111,6 +116,26 @@ function commentee(i, j, s) {
|
|
|
111
116
|
s = "^[ \t]*[rRuUbBfF]*[\"" q "]"
|
|
112
117
|
return j <= N && L[j] ~ s
|
|
113
118
|
}
|
|
119
|
+
# Texte des lignes de commentaire contiguës juste au-dessus de la ligne i,
|
|
120
|
+
# jointes par une espace (décorateurs et directives sautés), vide sinon
|
|
121
|
+
function commentaire(i, j, s, t) {
|
|
122
|
+
s = ""
|
|
123
|
+
for (j = i - 1; j > 0 && L[j] !~ /^#!/ && (L[j] ~ /^[ \t]*@/ || L[j] ~ /^[ \t]*(#|\/\/|\/\*|\*)/); j--) {
|
|
124
|
+
if (L[j] ~ /^[ \t]*@/ || directive(L[j])) continue
|
|
125
|
+
t = L[j]
|
|
126
|
+
sub(/^[ \t]*(#+|\/\/+|\/\*+|\*+)[ \t]*/, "", t); sub(/[ \t]*\*\/[ \t]*$/, "", t)
|
|
127
|
+
gsub(/\t/, " ", t); sub(/[ \t]+$/, "", t)
|
|
128
|
+
if (t != "") s = (s == "" ? t : t " " s)
|
|
129
|
+
}
|
|
130
|
+
return s
|
|
131
|
+
}
|
|
132
|
+
'
|
|
133
|
+
|
|
134
|
+
# Programme awk de comments_missing : lit le fichier de base (noms des
|
|
135
|
+
# fonctions existantes) puis le nouveau, et signale les fonctions nouvelles
|
|
136
|
+
# dont aucune ligne de commentaire ne précède la déclaration
|
|
137
|
+
# shellcheck disable=SC2016 # programme awk, rien à développer
|
|
138
|
+
COMMENTS_AWK='
|
|
114
139
|
FILENAME == ARGV[1] { n = nom($0); if (n != "") ancien[n] = 1; next }
|
|
115
140
|
{ L[FNR] = $0; N = FNR }
|
|
116
141
|
END {
|
package/hooks/lib/i18n/en.sh
CHANGED
|
@@ -114,13 +114,14 @@ _msg_en() {
|
|
|
114
114
|
help.alias.feat) _M="(branch: feature/ accepted)" ;;
|
|
115
115
|
help.alias.fix) _M="(branch: bugfix/, hotfix/ accepted)" ;;
|
|
116
116
|
# Help texts for commands (--help): single source shown by bin/repowarden
|
|
117
|
-
help.main) _M="Usage: repowarden <command> [options] · detailed help: repowarden <command> --help\n\n repowarden post status and next step\n repowarden statut (same)\n repowarden install [--global] [--lang fr|en] activate hooks (install.sh)\n repowarden uninstall [--global] [--purge] remove them (before npm uninstall -g)\n repowarden bots GitHub repo bots (CI, releases, tickets…)\n repowarden cc commit assistant\n repowarden code-mort new dead code in branch\n repowarden ticket ticket of the current branch\n repowarden ticket <n> branch of ticket (created, linked if needed)\n repowarden ticket nouveau \"<title>\" new ticket\n repowarden ticket comment <n> \"<text>\" add a comment\n repowarden ticket label <n> <label> [--remove] manage ticket labels\n repowarden pr label <n> <label> [--remove] manage PR labels\n repowarden pr checks <n> show PR checks\n repowarden run rerun <id|--failed|--workflow> rerun a workflow\n repowarden run list [--workflow <name>] list recent runs\n repowarden tickets init set up ticket tracking (GitHub)\n repowarden tickets sync my assigned tickets → local branches\n repowarden proteger [options] protect GitHub branches\n repowarden migrer [--pr] [--hooks] migrate from repogarde to repowarden\n repowarden npm-publication automate npm publication\n repowarden app init GitHub App for workflows (once)\n repowarden bot [init|rapport] org bot (pilot repo)\n repowarden agents init [--claude] [--opencode] AI agent guard (Claude Code, opencode)\n repowarden agents retirer removes the AI agent guard\n repowarden garde checks an agent action (called by its hook)\n repowarden --version installed version" ;;
|
|
117
|
+
help.main) _M="Usage: repowarden <command> [options] · detailed help: repowarden <command> --help\n\n repowarden post status and next step\n repowarden statut (same)\n repowarden install [--global] [--lang fr|en] activate hooks (install.sh)\n repowarden uninstall [--global] [--purge] remove them (before npm uninstall -g)\n repowarden bots GitHub repo bots (CI, releases, tickets…)\n repowarden cc commit assistant\n repowarden code-mort new dead code in branch\n repowarden carte project map: functions and their why\n repowarden ticket ticket of the current branch\n repowarden ticket <n> branch of ticket (created, linked if needed)\n repowarden ticket nouveau \"<title>\" new ticket\n repowarden ticket comment <n> \"<text>\" add a comment\n repowarden ticket label <n> <label> [--remove] manage ticket labels\n repowarden pr label <n> <label> [--remove] manage PR labels\n repowarden pr checks <n> show PR checks\n repowarden run rerun <id|--failed|--workflow> rerun a workflow\n repowarden run list [--workflow <name>] list recent runs\n repowarden tickets init set up ticket tracking (GitHub)\n repowarden tickets sync my assigned tickets → local branches\n repowarden proteger [options] protect GitHub branches\n repowarden migrer [--pr] [--hooks] migrate from repogarde to repowarden\n repowarden npm-publication automate npm publication\n repowarden app init GitHub App for workflows (once)\n repowarden bot [init|rapport] org bot (pilot repo)\n repowarden agents init [--claude] [--opencode] AI agent guard (Claude Code, opencode)\n repowarden agents retirer removes the AI agent guard\n repowarden garde checks an agent action (called by its hook)\n repowarden --version installed version" ;;
|
|
118
118
|
help.statut) _M="Usage: repowarden statut (or repowarden, no args)\n\nRead-only status of repowarden on this machine and in the current repo,\nplus the next command to run (like \"gh auth status\" or \"brew doctor\")." ;;
|
|
119
119
|
help.install) _M="Usage: repowarden install [--global] [--lang fr|en] [--bots | --sans-bots]\n\nActivate repowarden hooks (install.sh).\n --global all repos (and commit assistant: git cc)\n --lang fr|en message language (asked once if unset)\n --bots then propose repo bots (repowarden bots)\n --sans-bots never propose bots" ;;
|
|
120
120
|
help.uninstall) _M="Usage: repowarden uninstall [--global] [--purge] [--scan FOLDER]\n\nRemove repowarden hooks (only if owned). Do before npm uninstall -g.\n --global also remove global installation\n --purge remove caches and list repos still pointing to repowarden\n --scan FOLDER check repos in FOLDER still using repowarden (with --purge)" ;;
|
|
121
121
|
help.bots) _M="Usage: repowarden bots\n\nSet up GitHub repo bots (CI, releases, cleanup, tickets, App, protection,\nnpm): one global question, per-bot confirmation, preview, final confirmation.\nNothing done without consent. Can be rerun. Also via install --bots.\nNeeds a terminal, gh logged in and admin rights on the repo." ;;
|
|
122
122
|
help.bot) _M="Usage: repowarden bot [--repo o/r]… [--dry-run] [--rapport FILE]\n repowarden bot rapport FILE\n repowarden bot init\n\nOrg bot: nightly (and on demand) updates adopted projects (protection,\nmigration), publishes a report to the \"Rapport du bot repowarden\" issue.\n--dry-run shows without changes. init writes .github/workflows/bot.yml.\nUses installation token of the bot App (repowarden app init --bot)." ;;
|
|
123
123
|
help.cc) _M="Usage: repowarden cc [git commit options] (alias: git cc)\n repowarden cc -m \"description\" [--type T] [--scope S] [--breaking NOTE] [--body TEXT] [--refs \"Ticket: #12\"]\n repowarden cc --dry-run\n\nConventional commit assistant: guides header, body and footer, runs hooks.\nBatch mode (-m etc.): same checks, type/scope inferred from branch if omitted.\n --strict any refused answer stops (nothing committed)\n --dry-run show message without committing" ;;
|
|
124
|
+
help.carte) _M="Usage: repowarden carte (re)generates the map\n repowarden carte <file|function|'#n'> [--json] useful excerpt\n\nProject map, no LLM: every function of the shell scripts, with\nits file, line and \"why\" (the comment right above it),\nthe tests of each file (same name, cited path or command) and the tickets\nthat changed it (git \"Ticket : #n\" footers, no network), with the design\nof each (read with gh once, cached).\nWritten to .repowarden/carte/carte.json, a folder ignored by git; when queried,\nit is regenerated first if missing or stale. --json: output for tools.\nRequires jq." ;;
|
|
124
125
|
help.code-mort) _M="Usage: repowarden code-mort [base]\n\nDead code introduced by your branch (like CI): only added/changed lines\nsince base (commits, staged/uncommitted, new files). Default: merge-base\nwith origin/main. See docs/code-mort.md (proven vs candidates)." ;;
|
|
125
126
|
help.ticket) _M="Usage: repowarden ticket <n> branch for ticket (created, linked if needed)\n repowarden ticket ticket of current branch\n repowarden ticket nouveau \"<title>\" [--type fix] [--description \"…\"]\n repowarden ticket comment <n> \"<text>\" add comment\n repowarden ticket label <n> <label> [--remove] manage labels\n\nTicket first: taking <n> assigns to you and switches to its branch\n(created from develop if needed). \"nouveau\" creates a ticket (\"à valider\"\nif tracking is managed). Needs gh connected (https://cli.github.com)." ;;
|
|
126
127
|
help.tickets) _M="Usage: repowarden tickets init [--repo owner/name] [--sans-validation-auto]\n repowarden tickets sync\n\nSets up ticket tracking: labels, issue template, workflow; classifies\nexisting tickets; records PR branches. sync checks out local branches for\ntickets assigned to you (auto with ticketSync=auto, manual by default)." ;;
|
|
@@ -130,7 +131,7 @@ _msg_en() {
|
|
|
130
131
|
help.migrer) _M="Usage: repowarden migrer [--pr] [--hooks]\n\nMigrates from repogarde to repowarden (docs/migration-v4.md): configs,\nworkflows, renames, lists manual steps left. Safe to rerun.\n --pr on chore/migration-repowarden-vN: commit, push and PR\n --hooks also move leftover lefthook hooks to .git/hooks-lefthook/" ;;
|
|
131
132
|
help.npm-publication) _M="Usage: repowarden npm-publication [--repo owner/name] [--workflow release.yml]\n [--environnement NAME] [--dry-run]\n\nSets up automatic npm publishing per release via OIDC (trusted publishing,\nprovenance). Needs npm logged in, package exists; if interrupted resumes.\nWorkflow: release.yml publishes via npm-publish.yml." ;;
|
|
132
133
|
help.garde) _M="Usage: repowarden garde [--format claude|opencode] (reads the action on standard input)\n\nAI agent guard, called by the agent's hook before each action (repowarden\nagents init wires it): exit code 0 allowed, 2 refused with the command to\nrun. In a repowarden repository: no change on a detached HEAD, on a\nprotected branch, or outside a ticket branch when the repository tracks its\ntickets; branches created by repowarden ticket <n>, commits by repowarden\ncc (repowarden hooks active, off protected branches); no --no-verify, no\ngit add . / -A / --all. Requires jq." ;;
|
|
133
|
-
help.agents) _M="Usage: repowarden agents init [--claude] [--opencode] [--projet | --global]\n repowarden agents retirer [--claude] [--opencode] [--projet | --global]\n\nWires the guard (repowarden garde) into AI agents: Claude Code PreToolUse\nhook (.claude/settings.json), opencode
|
|
134
|
+
help.agents) _M="Usage: repowarden agents init [--claude] [--opencode] [--projet | --global]\n repowarden agents retirer [--claude] [--opencode] [--projet | --global]\n\nWires the guard (repowarden garde) into AI agents: Claude Code PreToolUse\nhook (.claude/settings.json), opencode plugins\n(.opencode/plugins/repowarden-garde.js for opencode 1.x,\nrepowarden-garde-v2.js for 2.x). Without --claude or --opencode:\nboth. --projet (default): repository files, to commit; --global: user\nconfiguration (\$CLAUDE_CONFIG_DIR, ~/.claude by default;\n\$OPENCODE_CONFIG_DIR, else \$XDG_CONFIG_HOME/opencode, ~/.config/opencode by\ndefault). Re-runnable: existing files are completed, never overwritten.\nretirer (or remove): removes only what init wrote." ;;
|
|
134
135
|
help.app) _M="Usage: repowarden app init [--repo owner/name] [--nom NAME] [--administration]\n repowarden app init --bot\n\nCreates/sets up a GitHub App for repowarden workflows (Actions token doesn't\ntrigger other workflows; App token does). Browser-based manifest, minimal\npermissions, credentials stored securely, installed on repo. --bot for org\nbot (pilot repo). Needs gh and node. Rerunnable." ;;
|
|
135
136
|
# Hooks and language plugins
|
|
136
137
|
hook.pre_commit.1) _M="Commit refused: branch name does not follow the convention." ;;
|
|
@@ -440,6 +441,21 @@ _msg_en() {
|
|
|
440
441
|
cm.missing) _M="%s: function %s has no comment right above it" ;;
|
|
441
442
|
cm.summary) _M="%s function(s) added without a comment: one or two lines right above, saying what it does and why (warning, not blocking)." ;;
|
|
442
443
|
cm.none) _M="Every added function has a comment." ;;
|
|
444
|
+
# Project map (repowarden carte)
|
|
445
|
+
carte.done) _M="Map: %s file(s), %s function(s), %s without a comment → %s" ;;
|
|
446
|
+
carte.no_repo) _M="repowarden carte: run it inside a git repository." ;;
|
|
447
|
+
carte.no_jq) _M="jq is missing: the map needs it (install jq: https://jqlang.org)." ;;
|
|
448
|
+
carte.no_comment) _M="(no comment)" ;;
|
|
449
|
+
carte.file_empty) _M="%s: no function listed in the map." ;;
|
|
450
|
+
carte.tests) _M="Tests: %s" ;;
|
|
451
|
+
carte.tickets) _M="Tickets: %s" ;;
|
|
452
|
+
carte.conception) _M="Design:" ;;
|
|
453
|
+
carte.no_gh) _M="gh missing or not logged in (gh auth login): map without the tickets' design." ;;
|
|
454
|
+
carte.ticket_title) _M="Ticket #%s: modified files" ;;
|
|
455
|
+
carte.ticket_unknown) _M="Ticket #%s: no commit cites it (footer \"Ticket : #%s\" or branch type/%s-topic)." ;;
|
|
456
|
+
carte.tests_run) _M=" → %s" ;;
|
|
457
|
+
carte.no_tests) _M="No test found for %s (no test with the same name, no test citing its path or repowarden command)." ;;
|
|
458
|
+
carte.unknown) _M="\"%s\" is neither a file nor a function of the map." ;;
|
|
443
459
|
ci.section.commits) _M="Commit messages (Conventional Commits)" ;;
|
|
444
460
|
ci.section.branch) _M="Branch name" ;;
|
|
445
461
|
ci.section.secrets) _M="Secrets (gitleaks)" ;;
|
|
@@ -502,6 +518,7 @@ _msg_en() {
|
|
|
502
518
|
st.repo) _M="Repository: %s" ;;
|
|
503
519
|
st.repo_local) _M="Repository-specific hooks (local core.hooksPath = %s), take precedence" ;;
|
|
504
520
|
st.repo_lefthook) _M="%s present: lefthook is no longer supported since repowarden 4, its commands are not run. Move them to .repowarden/<hook>, the expected version to .repowarden.conf (version)" ;;
|
|
521
|
+
st.repo_precommit) _M=".pre-commit-config.yaml present: its hooks are not run (pre-commit refuses to install with core.hooksPath set). Install them in .git/hooks, which repowarden runs on every commit: %s" ;;
|
|
505
522
|
st.repo_version_old) _M="The project requires repowarden %s; installed: %s" ;;
|
|
506
523
|
st.repo_version_ok) _M="Version required by the project (%s): met" ;;
|
|
507
524
|
st.repo_forge) _M="Platform: %s" ;;
|
|
@@ -514,6 +531,9 @@ _msg_en() {
|
|
|
514
531
|
st.agents_sans_garde) _M="AI agent without the guard (Claude Code, opencode): its actions are only checked at commit time. → %s" ;;
|
|
515
532
|
st.agents_stale) _M="Outdated repowarden guard (%s): wired by an earlier version of repowarden. → %s" ;;
|
|
516
533
|
st.next) _M="Next step:" ;;
|
|
534
|
+
st.carte_absente) _M="No project map (optional, read by agents before changing a file): repowarden carte" ;;
|
|
535
|
+
st.carte_ok) _M="Project map up to date (repowarden carte <file>)" ;;
|
|
536
|
+
st.carte_perimee) _M="Project map is stale: files changed since, it no longer tells what the code does." ;;
|
|
517
537
|
st.ready) _M="All set: git cc to commit, repowarden code-mort on a branch." ;;
|
|
518
538
|
st.help) _M="Every command: repowarden --help" ;;
|
|
519
539
|
# Tickets
|
|
@@ -669,8 +689,11 @@ _msg_en() {
|
|
|
669
689
|
tk.branch_kept) _M="Branch \`%s\` kept: %s commit(s) missing from the integration branch, open PR(s): %s. Delete it by hand if the work lives elsewhere." ;;
|
|
670
690
|
tk.draft_opened) _M="Draft PR #%s opened for ticket #%s" ;;
|
|
671
691
|
tk.draft_updated) _M="PR #%s (ticket #%s): title and description taken from the ticket" ;;
|
|
692
|
+
tk.assign_failed) _M="PR #%s: GitHub refused to assign %s, PR left unassigned" ;;
|
|
672
693
|
tk.pr_additions) _M="Ticket additions" ;;
|
|
673
694
|
tk.pr_design) _M="Design (summary)" ;;
|
|
695
|
+
tk.pr_does) _M="What this PR does" ;;
|
|
696
|
+
tk.pr_ticket) _M="Ticket #%s" ;;
|
|
674
697
|
tk.created_body) _M="Ticket created automatically for PR #%s, which cited none. To be validated by a maintainer (\"validé\" label)." ;;
|
|
675
698
|
tk.created) _M="Ticket #%s created for PR #%s and linked in its description: to be validated by a maintainer." ;;
|
|
676
699
|
tk.init_labels) _M="Labels (statuses and validation)" ;;
|
|
@@ -723,6 +746,7 @@ _msg_en() {
|
|
|
723
746
|
garde.detache) _M="change refused: detached HEAD in %s.\n → Switch to a ticket's branch: repowarden ticket <n>" ;;
|
|
724
747
|
garde.protegee) _M="change refused: \"%s\" is a protected branch.\n → Work on a ticket's branch: repowarden ticket <n>" ;;
|
|
725
748
|
garde.protegee_libre) _M="change refused: \"%s\" is a protected branch.\n → Create a work branch: git switch -c <type>/<topic>" ;;
|
|
749
|
+
garde.carte) _M=" → Before changing %s, once on the right branch: repowarden carte %s (functions, tests, tickets)" ;;
|
|
726
750
|
garde.hors_ticket) _M="change refused: branch \"%s\" is not a ticket's branch (<type>/<n>-<topic>).\n → Take the ticket: repowarden ticket <n>" ;;
|
|
727
751
|
garde.branche) _M="command refused: \"%s\" creates a branch; here, branches come from tickets.\n → repowarden ticket <n>" ;;
|
|
728
752
|
garde.commit) _M="command refused: \"%s\"; commits go through the assistant (only --amend --no-edit is allowed).\n → repowarden cc -m \"<description>\" --refs \"Ticket : #<n>\"" ;;
|
package/hooks/lib/i18n/fr.sh
CHANGED
|
@@ -237,13 +237,14 @@ _msg_fr() {
|
|
|
237
237
|
help.alias.fix) _M="(branche : bugfix/, hotfix/ acceptés)" ;;
|
|
238
238
|
# Aide des commandes (repowarden <commande> --help) : source unique,
|
|
239
239
|
# affichée par bin/repowarden avant toute action (printf '%b\n' → \n)
|
|
240
|
-
help.main) _M="Usage : repowarden <commande> [options] · aide détaillée : repowarden <commande> --help\n\n repowarden état du poste et étape suivante\n repowarden statut (idem)\n repowarden install [--global] [--lang fr|en] active les hooks (install.sh)\n repowarden uninstall [--global] [--purge] les retire (avant npm uninstall -g)\n repowarden bots bots du dépôt GitHub (CI, releases, tickets…)\n repowarden cc assistant de commit\n repowarden code-mort nouveau code mort de la branche\n repowarden ticket ticket de la branche courante\n repowarden ticket <n> branche du ticket (créée, liée, si besoin)\n repowarden ticket nouveau \"<titre>\" nouveau ticket\n repowarden ticket comment <n> \"<texte>\" commentaire sur un ticket\n repowarden ticket label <n> <label> [--remove] gère les labels d'un ticket\n repowarden pr label <n> <label> [--remove] gère les labels d'une PR\n repowarden pr checks <n> vérifie les checks d'une PR\n repowarden run rerun <id|--failed|--workflow> relance un workflow\n repowarden run list [--workflow <nom>] liste les runs récents\n repowarden tickets init suivi des tickets (GitHub)\n repowarden tickets sync mes tickets assignés → leurs branches locales\n repowarden proteger [options] protection des branches (GitHub)\n repowarden migrer [--pr] [--hooks] passage de repogarde à repowarden\n repowarden npm-publication publication npm automatique\n repowarden app init GitHub App des workflows (une fois)\n repowarden bot [init|rapport] bot d'organisation (dépôt de pilotage)\n repowarden agents init [--claude] [--opencode] garde-fou des agents IA (Claude Code, opencode)\n repowarden agents retirer retire le garde-fou des agents IA\n repowarden garde vérifie une action d'agent (appelé par son hook)\n repowarden --version version installée" ;;
|
|
240
|
+
help.main) _M="Usage : repowarden <commande> [options] · aide détaillée : repowarden <commande> --help\n\n repowarden état du poste et étape suivante\n repowarden statut (idem)\n repowarden install [--global] [--lang fr|en] active les hooks (install.sh)\n repowarden uninstall [--global] [--purge] les retire (avant npm uninstall -g)\n repowarden bots bots du dépôt GitHub (CI, releases, tickets…)\n repowarden cc assistant de commit\n repowarden code-mort nouveau code mort de la branche\n repowarden carte carte du projet : fonctions et leur pourquoi\n repowarden ticket ticket de la branche courante\n repowarden ticket <n> branche du ticket (créée, liée, si besoin)\n repowarden ticket nouveau \"<titre>\" nouveau ticket\n repowarden ticket comment <n> \"<texte>\" commentaire sur un ticket\n repowarden ticket label <n> <label> [--remove] gère les labels d'un ticket\n repowarden pr label <n> <label> [--remove] gère les labels d'une PR\n repowarden pr checks <n> vérifie les checks d'une PR\n repowarden run rerun <id|--failed|--workflow> relance un workflow\n repowarden run list [--workflow <nom>] liste les runs récents\n repowarden tickets init suivi des tickets (GitHub)\n repowarden tickets sync mes tickets assignés → leurs branches locales\n repowarden proteger [options] protection des branches (GitHub)\n repowarden migrer [--pr] [--hooks] passage de repogarde à repowarden\n repowarden npm-publication publication npm automatique\n repowarden app init GitHub App des workflows (une fois)\n repowarden bot [init|rapport] bot d'organisation (dépôt de pilotage)\n repowarden agents init [--claude] [--opencode] garde-fou des agents IA (Claude Code, opencode)\n repowarden agents retirer retire le garde-fou des agents IA\n repowarden garde vérifie une action d'agent (appelé par son hook)\n repowarden --version version installée" ;;
|
|
241
241
|
help.statut) _M="Usage : repowarden statut (ou repowarden, sans argument)\n\nÉtat de repowarden sur ce poste et dans le dépôt courant, puis la commande\nsuivante à lancer (l'équivalent de « gh auth status » ou « brew doctor »).\nLecture seule : rien n'est modifié." ;;
|
|
242
242
|
help.install) _M="Usage : repowarden install [--global] [--lang fr|en] [--bots | --sans-bots]\n\nActive les hooks repowarden (install.sh).\n --global tous les dépôts (et l'assistant de commit : git cc)\n --lang fr|en langue des messages (demandée une fois sinon)\n --bots propose ensuite les bots du dépôt GitHub (repowarden bots)\n --sans-bots ne plus jamais les proposer" ;;
|
|
243
243
|
help.uninstall) _M="Usage : repowarden uninstall [--global] [--purge] [--scan DOSSIER]\n\nRetire les hooks repowarden (seulement s'ils sont les tiens), avant\nnpm uninstall -g.\n --global retire aussi l'installation globale\n --purge retire les caches et liste les dépôts encore branchés\n --scan DOSSIER dépôts de DOSSIER encore branchés sur repowarden (avec --purge)" ;;
|
|
244
244
|
help.bots) _M="Usage : repowarden bots\n\nBots du dépôt GitHub courant : une question globale, un accord par bot (CI,\nreleases, nettoyage, tickets, App, protection, npm), l'aperçu de ce qui sera\ncréé ou modifié, puis une confirmation finale. Rien sans accord, relançable\npour compléter. Aussi : repowarden install --bots, et --sans-bots pour ne\nplus les proposer. Demande un terminal, gh connecté et les droits\nd'administration du dépôt." ;;
|
|
245
245
|
help.bot) _M="Usage : repowarden bot [--repo o/r]… [--dry-run] [--rapport FICHIER]\n repowarden bot rapport FICHIER\n repowarden bot init\n\nBot d'organisation : chaque nuit (et à la demande) il applique chaque projet\nadopté (protection, migration) et publie son rapport dans l'issue « Rapport\ndu bot repowarden » ; --dry-run voit sans rien modifier. init : écrit le\nworkflow du dépôt de pilotage (.github/workflows/bot.yml). GH_TOKEN : jeton\nd'installation de l'App (repowarden app init --bot)." ;;
|
|
246
246
|
help.cc) _M="Usage : repowarden cc [options de git commit] (alias : git cc, installé par install.sh)\n repowarden cc -m \"description\" [--type T] [--scope S] [--breaking NOTE] [--body TEXTE] [--refs \"Ticket : #12\"]\n repowarden cc --dry-run\n\nAssistant de commit conventionnel : guide l'en-tête, le corps et le pied,\npuis commit via les hooks habituels. Sans question (-m : scripts, agents) :\nmêmes contrôles, type et scope déduits de la branche s'ils sont omis.\n --strict une réponse refusée arrête tout (rien n'est commité)\n --dry-run affiche le message sans commiter" ;;
|
|
247
|
+
help.carte) _M="Usage : repowarden carte (re)génère la carte\n repowarden carte <fichier|fonction|'#n'> [--json] extrait utile\n\nCarte du projet, sans LLM : chaque fonction des scripts shell, avec\nson fichier, sa ligne et son « pourquoi » (le commentaire juste au-dessus),\nles tests de chaque fichier (même nom, chemin ou commande cités) et les\ntickets qui l'ont modifié (pieds « Ticket : #n » de git, sans réseau), avec\nla conception de chacun (lue par gh une fois, mise en cache).\nÉcrite dans .repowarden/carte/carte.json, dossier ignoré par git ; interrogée,\nelle est d'abord régénérée si elle est absente ou périmée. --json : sortie\npour les outils. Demande jq." ;;
|
|
247
248
|
help.code-mort) _M="Usage : repowarden code-mort [base]\n\nCode mort introduit par ta branche, comme en CI : seules les lignes ajoutées\nou modifiées depuis la base comptent (commits, modifications en cours,\nnouveaux fichiers). Défaut : point de départ de la branche depuis origin/main.\nVoir docs/code-mort.md : ce qui est prouvé (bloquant) et ce qui est candidat." ;;
|
|
248
249
|
help.ticket) _M="Usage : repowarden ticket <n> branche du ticket (créée, liée, si besoin)\n repowarden ticket ticket de la branche courante\n repowarden ticket nouveau \"<titre>\" [--type fix] [--description \"…\"]\n repowarden ticket comment <n> \"<texte>\" commentaire sur le ticket\n repowarden ticket label <n> <label> [--remove] labels du ticket\n\nLe ticket d'abord : prendre <n> l'assigne à toi et place sur sa branche\n(créée depuis develop au besoin) ; nouveau crée le ticket (« à valider » si\nle suivi est géré par repowarden). Demande gh connecté (https://cli.github.com)." ;;
|
|
249
250
|
help.tickets) _M="Usage : repowarden tickets init [--repo propriétaire/nom] [--sans-validation-auto]\n repowarden tickets sync\n\nPrépare le suivi des tickets d'un dépôt GitHub (relançable sans risque) :\nétiquettes (« validé », statuts et types, préfixe des branches), modèle de\nticket, workflow ; tickets existants rangés ; branches des PR inscrites sur\nleurs tickets. sync : branches locales des tickets qui me sont assignés\n(automatique avec ticketSync = auto, défaut manual)." ;;
|
|
@@ -253,7 +254,7 @@ _msg_fr() {
|
|
|
253
254
|
help.migrer) _M="Usage : repowarden migrer [--pr] [--hooks]\n\nPassage de repogarde à repowarden dans le projet courant\n(docs/migration-v4.md) : configuration (.repowarden.conf, .repowarden/),\nworkflows (uses: vers le dépôt repowarden et sa version majeure, anciens\nnoms), puis la liste de ce qui reste à faire à la main. Relançable : rien\nn'est fait deux fois.\n --pr sur la branche chore/migration-repowarden-vN : commit, push et PR\n --hooks déplace aussi les hooks lefthook résiduels dans .git/hooks-lefthook/" ;;
|
|
254
255
|
help.npm-publication) _M="Usage : repowarden npm-publication [--repo propriétaire/nom] [--workflow release.yml]\n [--environnement NOM] [--dry-run]\n\nPublication npm automatique mise en place en une commande : chaque release\npublie le paquet (workflow npm-publish.yml), sans jeton (publication de\nconfiance OIDC, provenance jointe) : connexion npm, paquet existant, puis\nrelation de confiance. Interrompue, la commande reprend où elle en était." ;;
|
|
255
256
|
help.garde) _M="Usage : repowarden garde [--format claude|opencode] (lit l'action sur l'entrée standard)\n\nGarde-fou des agents IA, appelé par leur hook avant chaque action\n(repowarden agents init le branche) : code 0 permis, code 2 refusé avec la\ncommande à lancer. Dans un dépôt repowarden : pas de modification en HEAD\ndétaché, sur une branche protégée, ni hors branche de ticket si le dépôt\nsuit ses tickets ; branche créée par repowarden ticket <n>, commit par\nrepowarden cc (hooks repowarden actifs, hors branche protégée) ; ni\n--no-verify ni git add . / -A / --all. Demande jq." ;;
|
|
256
|
-
help.agents) _M="Usage : repowarden agents init [--claude] [--opencode] [--projet | --global]\n repowarden agents retirer [--claude] [--opencode] [--projet | --global]\n\nBranche le garde-fou (repowarden garde) dans les agents IA : hook PreToolUse\nde Claude Code (.claude/settings.json),
|
|
257
|
+
help.agents) _M="Usage : repowarden agents init [--claude] [--opencode] [--projet | --global]\n repowarden agents retirer [--claude] [--opencode] [--projet | --global]\n\nBranche le garde-fou (repowarden garde) dans les agents IA : hook PreToolUse\nde Claude Code (.claude/settings.json), plugins opencode\n(.opencode/plugins/repowarden-garde.js pour opencode 1.x,\nrepowarden-garde-v2.js pour 2.x). Sans --claude ni --opencode : les\ndeux. --projet (défaut) : fichiers du dépôt, à versionner ; --global :\nconfiguration de l'utilisateur (\$CLAUDE_CONFIG_DIR, ~/.claude par défaut ;\n\$OPENCODE_CONFIG_DIR, sinon \$XDG_CONFIG_HOME/opencode, ~/.config/opencode par\ndéfaut). Relançable : fichiers existants complétés, jamais écrasés.\nretirer : enlève seulement ce qu'init a écrit." ;;
|
|
257
258
|
help.app) _M="Usage : repowarden app init [--repo propriétaire/nom] [--nom NOM] [--administration]\n repowarden app init --bot\n\nGitHub App du dépôt : identité d'automatisation des workflows repowarden (le\njeton des Actions ne déclenche aucun workflow, un jeton de l'App, si).\nCréation dans le navigateur (flux « manifest »), permissions minimales,\nidentifiants rangés sans être affichés, installation sur le dépôt. --bot :\nApp du bot d'organisation, dans le dépôt de pilotage. Relançable ; demande\ngh et node." ;;
|
|
258
259
|
# CI et protection
|
|
259
260
|
ci.check.2) _M="Pas de base : vérification ignorée." ;;
|
|
@@ -441,6 +442,21 @@ _msg_fr() {
|
|
|
441
442
|
cm.missing) _M="%s : fonction %s sans commentaire juste au-dessus" ;;
|
|
442
443
|
cm.summary) _M="%s fonction(s) ajoutée(s) sans commentaire : une à deux lignes juste au-dessus, qui disent ce qu'elle fait et pourquoi (avertissement, non bloquant)." ;;
|
|
443
444
|
cm.none) _M="Chaque fonction ajoutée est commentée." ;;
|
|
445
|
+
# Carte du projet (repowarden carte)
|
|
446
|
+
carte.done) _M="Carte : %s fichier(s), %s fonction(s), dont %s sans commentaire → %s" ;;
|
|
447
|
+
carte.no_repo) _M="repowarden carte : à lancer dans un dépôt git." ;;
|
|
448
|
+
carte.no_jq) _M="jq absent : la carte en a besoin (installer jq : https://jqlang.org)." ;;
|
|
449
|
+
carte.no_comment) _M="(sans commentaire)" ;;
|
|
450
|
+
carte.file_empty) _M="%s : aucune fonction répertoriée dans la carte." ;;
|
|
451
|
+
carte.tests) _M="Tests : %s" ;;
|
|
452
|
+
carte.tickets) _M="Tickets : %s" ;;
|
|
453
|
+
carte.conception) _M="Conception :" ;;
|
|
454
|
+
carte.no_gh) _M="gh absent ou non connecté (gh auth login) : carte sans la conception des tickets." ;;
|
|
455
|
+
carte.ticket_title) _M="Ticket #%s : fichiers modifiés" ;;
|
|
456
|
+
carte.ticket_unknown) _M="Ticket #%s : aucun commit ne le cite (pied « Ticket : #%s » ou branche type/%s-sujet)." ;;
|
|
457
|
+
carte.tests_run) _M=" → %s" ;;
|
|
458
|
+
carte.no_tests) _M="Aucun test trouvé pour %s (ni test du même nom, ni chemin ou commande repowarden cités dans un test)." ;;
|
|
459
|
+
carte.unknown) _M="« %s » n'est ni un fichier ni une fonction de la carte." ;;
|
|
444
460
|
ci.section.commits) _M="Messages de commit (Conventional Commits)" ;;
|
|
445
461
|
ci.section.branch) _M="Nom de branche" ;;
|
|
446
462
|
ci.section.secrets) _M="Secrets (gitleaks)" ;;
|
|
@@ -503,6 +519,7 @@ _msg_fr() {
|
|
|
503
519
|
st.repo) _M="Dépôt : %s" ;;
|
|
504
520
|
st.repo_local) _M="Hooks propres à ce dépôt (core.hooksPath local = %s), prioritaires" ;;
|
|
505
521
|
st.repo_lefthook) _M="%s présent : lefthook n'est plus pris en charge depuis repowarden 4, ses commandes ne sont pas lancées. Les déplacer dans .repowarden/<hook>, la version attendue dans .repowarden.conf (version)" ;;
|
|
522
|
+
st.repo_precommit) _M=".pre-commit-config.yaml présent : ses hooks ne sont pas lancés (pre-commit refuse de s'installer avec core.hooksPath). Les installer dans .git/hooks, que repowarden lance à chaque commit : %s" ;;
|
|
506
523
|
st.repo_version_old) _M="Le projet demande repowarden %s ; installé : %s" ;;
|
|
507
524
|
st.repo_version_ok) _M="Version demandée par le projet (%s) : respectée" ;;
|
|
508
525
|
st.repo_forge) _M="Plateforme : %s" ;;
|
|
@@ -515,6 +532,9 @@ _msg_fr() {
|
|
|
515
532
|
st.agents_sans_garde) _M="Agent IA sans garde-fou (Claude Code, opencode) : ses actions ne sont vérifiées qu'au commit. → %s" ;;
|
|
516
533
|
st.agents_stale) _M="Garde-fou repowarden périmé (%s) : posé par une version antérieure de repowarden. → %s" ;;
|
|
517
534
|
st.next) _M="Étape suivante :" ;;
|
|
535
|
+
st.carte_absente) _M="Carte du projet absente (facultative, lue par les agents avant de modifier un fichier) : repowarden carte" ;;
|
|
536
|
+
st.carte_ok) _M="Carte du projet à jour (repowarden carte <fichier>)" ;;
|
|
537
|
+
st.carte_perimee) _M="Carte du projet périmée : des fichiers ont changé depuis, elle ne dit plus ce que fait le code." ;;
|
|
518
538
|
st.ready) _M="Tout est prêt : git cc pour commiter, repowarden code-mort sur une branche." ;;
|
|
519
539
|
st.help) _M="Toutes les commandes : repowarden --help" ;;
|
|
520
540
|
# Tickets
|
|
@@ -670,8 +690,11 @@ _msg_fr() {
|
|
|
670
690
|
tk.branch_kept) _M="Branche \`%s\` gardée : %s commit(s) absent(s) de la branche d'intégration, PR ouverte(s) : %s. À supprimer à la main si le travail est ailleurs." ;;
|
|
671
691
|
tk.draft_opened) _M="PR #%s ouverte en brouillon pour le ticket #%s" ;;
|
|
672
692
|
tk.draft_updated) _M="PR #%s (ticket #%s) : titre et description repris du ticket" ;;
|
|
693
|
+
tk.assign_failed) _M="PR #%s : assignation de %s refusée par GitHub, PR laissée sans assigné" ;;
|
|
673
694
|
tk.pr_additions) _M="Ajouts du ticket" ;;
|
|
674
695
|
tk.pr_design) _M="Conception (résumé)" ;;
|
|
696
|
+
tk.pr_does) _M="Ce que fait cette PR" ;;
|
|
697
|
+
tk.pr_ticket) _M="Ticket #%s" ;;
|
|
675
698
|
tk.created_body) _M="Ticket créé automatiquement pour la PR #%s, qui n'en citait aucun. À valider par un mainteneur (étiquette « validé »)." ;;
|
|
676
699
|
tk.created) _M="Ticket #%s créé pour la PR #%s et relié à sa description : à valider par un mainteneur." ;;
|
|
677
700
|
tk.init_labels) _M="Étiquettes (statuts et validation)" ;;
|
|
@@ -724,6 +747,7 @@ _msg_fr() {
|
|
|
724
747
|
garde.detache) _M="modification refusée : HEAD détaché dans %s.\n → Se placer sur la branche d'un ticket : repowarden ticket <n>" ;;
|
|
725
748
|
garde.protegee) _M="modification refusée : « %s » est une branche protégée.\n → Travailler sur la branche d'un ticket : repowarden ticket <n>" ;;
|
|
726
749
|
garde.protegee_libre) _M="modification refusée : « %s » est une branche protégée.\n → Créer une branche de travail : git switch -c <type>/<sujet>" ;;
|
|
750
|
+
garde.carte) _M=" → Avant de modifier %s, une fois sur la bonne branche : repowarden carte %s (fonctions, tests, tickets)" ;;
|
|
727
751
|
garde.hors_ticket) _M="modification refusée : la branche « %s » n'est pas celle d'un ticket (<type>/<n>-<sujet>).\n → Prendre le ticket : repowarden ticket <n>" ;;
|
|
728
752
|
garde.branche) _M="commande refusée : « %s » crée une branche ; ici, la branche naît du ticket.\n → repowarden ticket <n>" ;;
|
|
729
753
|
garde.commit) _M="commande refusée : « %s » ; les commits passent par l'assistant (seul --amend --no-edit est permis).\n → repowarden cc -m \"<description>\" --refs \"Ticket : #<n>\"" ;;
|
package/hooks/post-merge
CHANGED
|
@@ -11,6 +11,11 @@ exit_if_skipped
|
|
|
11
11
|
run_local_hook "$@"
|
|
12
12
|
# Branches locales des tickets qui me sont assignés (au plus toutes les 15 min)
|
|
13
13
|
tickets_sync || true
|
|
14
|
+
# Carte du projet déjà générée (repowarden carte) : recalculée après la fusion,
|
|
15
|
+
# hors ligne (ni gh ni réseau), sans jamais faire échouer le hook
|
|
16
|
+
if [ -n "$GIT_TOPLEVEL" ] && [ -f "$GIT_TOPLEVEL/.repowarden/carte/carte.json" ] && has jq; then
|
|
17
|
+
REPOWARDEN_CARTE_HORS_LIGNE=1 bash "$(dirname "$0")/../bin/carte" >/dev/null 2>&1 || true
|
|
18
|
+
fi
|
|
14
19
|
skipped prune-branches && exit 0
|
|
15
20
|
|
|
16
21
|
current="$(git symbolic-ref --short HEAD 2>/dev/null || echo "")"
|
package/package.json
CHANGED