Repository navigation
Expand file tree
/
Copy pathcpcode
More file actions
executable file
·220 lines (194 loc) · 8.99 KB
/
Copy pathcpcode
File metadata and controls
executable file
·220 lines (194 loc) · 8.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
#!/bin/bash
########################################################################
# cpcode — Regroupe des fichiers par extension dans le presse-papiers
#
# Usage : cpcode [--json] [--maxfilesize N] <extension1> [ext2 ...] <dossier>
#
# Options :
# --json Sortie JSON compatible code_assistant / LLM tool
# --maxfilesize N Limite la taille par fichier en octets (défaut: illimité)
# Ex: --maxfilesize 102400 (100 Ko par fichier)
# --help Affiche cette aide
########################################################################
show_help() {
echo "Usage : $0 [--json] [--maxfilesize N] [--exclude <pattern>] [--well-known] <ext1> [ext2 ...] <dossier>"
echo ""
echo "Options :"
echo " --json Sortie JSON (compatible code_assistant / LLM tool)"
echo " --maxfilesize N Limite par fichier en octets (tronque)"
echo " --exclude <pattern> Exclure les fichiers dont le chemin contient <pattern> (répétable)"
echo " Ex: --exclude 'jquery' --exclude 'bootstrap'"
echo " --well-known Exclure les bibliothèques tierces répandues (jQuery, Bootstrap,"
echo " Leaflet, p5.js, nacl, scrypt, blake2b, axios, mermaid…)"
echo " --help Affiche cette aide."
exit 0
}
_ME="${BASH_SOURCE[0]##*/}"
_MY_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
[[ ! -L ~/.local/bin/${_ME} ]] && ln -sf "${_MY_PATH}/${_ME}" ~/.local/bin/${_ME} \
&& echo "[cpcode] Auto Install into ~/.local/bin/${_ME}"
# ── Parsing des options ───────────────────────────────────────────────────
JSON_MODE=false
MAX_FILE_SIZE=0
EXCLUDE_PATTERNS=()
WELL_KNOWN=false
POSITIONAL=()
# Bibliothèques tierces répandues — liste centralisée dans tools/well_known_libs.sh
# shellcheck source=tools/well_known_libs.sh
_CPCODE_REAL_DIR="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")"
source "${_CPCODE_REAL_DIR}/tools/well_known_libs.sh"
_WELL_KNOWN_PATTERNS=("${WELL_KNOWN_LIBS[@]}")
while [[ $# -gt 0 ]]; do
case "$1" in
--help) show_help ;;
--json) JSON_MODE=true; shift ;;
--maxfilesize) MAX_FILE_SIZE="$2"; shift 2 ;;
--maxfilesize=*) MAX_FILE_SIZE="${1#--maxfilesize=}"; shift ;;
--exclude) EXCLUDE_PATTERNS+=("$2"); shift 2 ;;
--exclude=*) EXCLUDE_PATTERNS+=("${1#--exclude=}"); shift ;;
--well-known) WELL_KNOWN=true; shift ;;
*) POSITIONAL+=("$1"); shift ;;
esac
done
if $WELL_KNOWN; then
EXCLUDE_PATTERNS+=("${_WELL_KNOWN_PATTERNS[@]}")
fi
if [ "${#POSITIONAL[@]}" -lt 2 ]; then
echo "Erreur : Arguments manquants."
show_help
exit 1
fi
# Séparer extensions (tout sauf dernier) et dossier (dernier)
EXTENSIONS=("${POSITIONAL[@]:0:${#POSITIONAL[@]}-1}")
SOURCE_DIR="${POSITIONAL[-1]}"
if [ ! -d "$SOURCE_DIR" ]; then
echo "Erreur : Le dossier source '$SOURCE_DIR' n'existe pas."
exit 1
fi
MOATS=$(date -u +"%Y%m%d%H%M%S%4N")
TEMP_FILE="/tmp/$MOATS.txt"
> "$TEMP_FILE"
NB_FILES=0
JSON_FILE_COUNT=0
# ── Traitement des fichiers ───────────────────────────────────────────────
for EXT in "${EXTENSIONS[@]}"; do
while IFS= read -r FILE; do
# Vérifier les patterns d'exclusion
SKIP=false
for _PAT in "${EXCLUDE_PATTERNS[@]}"; do
if [[ "$FILE" == *${_PAT}* ]]; then
SKIP=true; break
fi
done
if $SKIP; then
echo " [EXCLU] $FILE"
continue
fi
# Vérifier que c'est un fichier texte (grep -I échoue sur les binaires)
if ! grep -Iq . "$FILE" 2>/dev/null; then
echo " [IGNORÉ - binaire] $FILE"
continue
fi
RELATIVE_PATH=$(realpath --relative-to="$SOURCE_DIR" "$FILE")
FILENAME=$(basename "$FILE" | sed "s/\.$EXT$//")
echo "Ajout de : $FILE"
# --- Remplacement de la lecture classique par RTK ---
if command -v rtk >/dev/null 2>&1; then
# Utilise rtk read pour un contenu optimisé en tokens
# On passe --no-header pour ne pas doubler les infos de chemin
CONTENT=$(rtk read --no-header "$FILE" 2>/dev/null)
# Si rtk échoue ou renvoie vide (ex: fichier ignoré par rtk), fallback
if [ -z "$CONTENT" ]; then
CONTENT=$(cat "$FILE")
fi
elif [ "$MAX_FILE_SIZE" -gt 0 ]; then
CONTENT=$(head -c "$MAX_FILE_SIZE" "$FILE")
# ... (ton code de troncature actuel)
else
CONTENT=$(cat "$FILE")
fi
if $JSON_MODE; then
# Sortie JSON : un objet par fichier
local COMMA=""
[ "$JSON_FILE_COUNT" -gt 0 ] && COMMA=","
local CONTENT_JSON
CONTENT_JSON=$(python3 -c "import json,sys; print(json.dumps(sys.stdin.read()))" <<< "$CONTENT" 2>/dev/null || echo '""')
{
echo "${COMMA}{"
printf ' "path": %s,\n' "$(python3 -c "import json; print(json.dumps('$RELATIVE_PATH'))" 2>/dev/null || echo '"'"$RELATIVE_PATH"'"')"
printf ' "filename": %s,\n' "$(python3 -c "import json; print(json.dumps('$FILENAME'))" 2>/dev/null || echo '"'"$FILENAME"'"')"
printf ' "extension": "%s",\n' "$EXT"
printf ' "content": %s\n' "$CONTENT_JSON"
echo "}"
} >> "$TEMP_FILE"
(( JSON_FILE_COUNT++ )) || true
else
# Sortie texte classique (écriture directe, pas de RESULT+= pour éviter corruption)
{
echo "Chemin : $RELATIVE_PATH"
echo "Titre : $FILENAME"
echo ""
echo "$CONTENT"
echo ""
} >> "$TEMP_FILE"
fi
(( NB_FILES++ )) || true
done < <(find "$SOURCE_DIR" -type f -name "*.$EXT" | grep -v '/\.')
done
# ── Enveloppe JSON finale ────────────────────────────────────────────────
if $JSON_MODE; then
TOTAL_CHARS=$(wc -c < "$TEMP_FILE" 2>/dev/null || echo 0)
TOTAL_TOKENS=$(( TOTAL_CHARS * 10 / 35 ))
JSON_FINAL="/tmp/${MOATS}.json"
{
echo "{"
printf ' "tool": "cpcode",\n'
printf ' "source_dir": %s,\n' "$(python3 -c "import json; print(json.dumps('$(realpath "$SOURCE_DIR")'))" 2>/dev/null)"
printf ' "extensions": %s,\n' "$(python3 -c "import json; print(json.dumps($(printf '[%s]' "$(printf '"%s",' "${EXTENSIONS[@]}" | sed 's/,$//')" )))" 2>/dev/null || echo "[]")"
printf ' "stats": { "files_count": %d, "total_chars": %d, "total_tokens": %d },\n' \
"$NB_FILES" "$TOTAL_CHARS" "$TOTAL_TOKENS"
echo ' "files": ['
cat "$TEMP_FILE"
echo ' ]'
echo "}"
} > "$JSON_FINAL"
# Compresser le JSON final avec rtk
if command -v rtk >/dev/null 2>&1; then
rtk json < "$JSON_FINAL" > "${JSON_FINAL}.tmp" 2>/dev/null && mv "${JSON_FINAL}.tmp" "$JSON_FINAL"
fi
mv "$JSON_FINAL" "$TEMP_FILE"
fi
# ── Validation ────────────────────────────────────────────────────────────
if [ ! -s "$TEMP_FILE" ]; then
echo "Erreur : Aucun fichier trouvé avec les extensions spécifiées dans '$SOURCE_DIR'."
rm -f "$TEMP_FILE"
exit 1
fi
TOTAL_CHARS=$(wc -c < "$TEMP_FILE" 2>/dev/null || echo 0)
TOTAL_TOKENS=$(( TOTAL_CHARS * 10 / 35 ))
_fmt_num() {
local n=$1
if (( n >= 1000000 )); then printf "%d.%dM" $(( n / 1000000 )) $(( (n % 1000000) / 100000 ))
elif (( n >= 1000 )); then printf "%dK" $(( n / 1000 ))
else printf "%d" "$n"
fi
}
echo ""
echo "=== $NB_FILES fichier(s) — $(_fmt_num "$TOTAL_CHARS") chars (~$(_fmt_num "$TOTAL_TOKENS") tokens) ==="
# ── Presse-papiers ────────────────────────────────────────────────────────
COPIED=false
if [ -n "$DISPLAY" ] && command -v xclip &>/dev/null; then
if cat "$TEMP_FILE" | xclip -selection clipboard 2>/dev/null; then
COPIED=true; echo "Contenu copié dans le presse-papiers (xclip)."
fi
elif [ -n "$WAYLAND_DISPLAY" ] && command -v wl-copy &>/dev/null; then
if cat "$TEMP_FILE" | wl-copy 2>/dev/null; then
COPIED=true; echo "Contenu copié dans le presse-papiers (wl-copy)."
fi
elif command -v pbcopy &>/dev/null; then
if cat "$TEMP_FILE" | pbcopy 2>/dev/null; then
COPIED=true; echo "Contenu copié dans le presse-papiers (pbcopy)."
fi
fi
[ "$COPIED" = false ] && echo "Avertissement : Impossible de copier dans le presse-papiers."
echo "Résultat écrit dans : $TEMP_FILE"