ai-consultants 2.10.1
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/.claude/commands/ai-consultants:consult.md +94 -0
- package/.claude/commands/ai-consultants:debate.md +103 -0
- package/.claude/commands/ai-consultants:help.md +30 -0
- package/CLAUDE.md +839 -0
- package/LICENSE +21 -0
- package/README.md +758 -0
- package/SKILL.md +156 -0
- package/bin/ai-consultants +96 -0
- package/docs/COST_RATES.md +172 -0
- package/docs/JSON_SCHEMA.md +305 -0
- package/docs/SETUP.md +879 -0
- package/docs/SMART_ROUTING.md +219 -0
- package/docs/cost_rates.json +143 -0
- package/package.json +47 -0
- package/references/configuration.md +141 -0
- package/references/details.md +60 -0
- package/scripts/build_context.sh +562 -0
- package/scripts/classify_question.sh +148 -0
- package/scripts/config.sh +753 -0
- package/scripts/configure.sh +1093 -0
- package/scripts/consult_all.sh +934 -0
- package/scripts/debate_round.sh +421 -0
- package/scripts/doctor.sh +642 -0
- package/scripts/followup.sh +197 -0
- package/scripts/install.sh +319 -0
- package/scripts/lib/api.sh +640 -0
- package/scripts/lib/api_query.sh +228 -0
- package/scripts/lib/cache.sh +338 -0
- package/scripts/lib/chunking.sh +880 -0
- package/scripts/lib/code_optimizer.sh +671 -0
- package/scripts/lib/common.sh +1088 -0
- package/scripts/lib/costs.sh +674 -0
- package/scripts/lib/personas.sh +372 -0
- package/scripts/lib/progress.sh +251 -0
- package/scripts/lib/reflection.sh +447 -0
- package/scripts/lib/routing.sh +566 -0
- package/scripts/lib/schema.json +165 -0
- package/scripts/lib/session.sh +327 -0
- package/scripts/lib/symbol_map.sh +445 -0
- package/scripts/lib/voting.sh +495 -0
- package/scripts/peer_review.sh +460 -0
- package/scripts/preflight_check.sh +452 -0
- package/scripts/query_aider.sh +83 -0
- package/scripts/query_amp.sh +67 -0
- package/scripts/query_claude.sh +99 -0
- package/scripts/query_codex.sh +92 -0
- package/scripts/query_cursor.sh +72 -0
- package/scripts/query_deepseek.sh +17 -0
- package/scripts/query_gemini.sh +85 -0
- package/scripts/query_glm.sh +17 -0
- package/scripts/query_grok.sh +17 -0
- package/scripts/query_kilo.sh +140 -0
- package/scripts/query_kimi.sh +67 -0
- package/scripts/query_minimax.sh +17 -0
- package/scripts/query_mistral.sh +97 -0
- package/scripts/query_ollama.sh +328 -0
- package/scripts/query_qwen3.sh +93 -0
- package/scripts/setup_wizard.sh +367 -0
- package/scripts/skillport-install.sh +196 -0
- package/scripts/synthesize.sh +332 -0
- package/templates/consultation_report.md +323 -0
- package/templates/synthesis_prompt.md +120 -0
|
@@ -0,0 +1,562 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# build_context.sh - Automatically generates a context file for AI consultation
|
|
3
|
+
#
|
|
4
|
+
# Usage: ./build_context.sh <output_file> "Your question" [file1] [file2] ...
|
|
5
|
+
#
|
|
6
|
+
# Example:
|
|
7
|
+
# ./build_context.sh /tmp/context.md "How to optimize this function?" src/utils.py src/main.py
|
|
8
|
+
#
|
|
9
|
+
# Token Optimization Modes (v2.2):
|
|
10
|
+
# - none: Include file contents as-is (no optimization)
|
|
11
|
+
# - basic: Simple byte-based truncation (legacy behavior)
|
|
12
|
+
# - ast: Extract code skeleton using AST (default)
|
|
13
|
+
# - full: AST + symbol compression + semantic chunking
|
|
14
|
+
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
|
|
17
|
+
# Load common functions for validation
|
|
18
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
19
|
+
source "$SCRIPT_DIR/lib/common.sh"
|
|
20
|
+
|
|
21
|
+
# =============================================================================
|
|
22
|
+
# TOKEN OPTIMIZATION CONFIGURATION (v2.2)
|
|
23
|
+
# =============================================================================
|
|
24
|
+
|
|
25
|
+
# Optimization mode: "none", "basic", "ast", "full"
|
|
26
|
+
TOKEN_OPTIMIZATION_MODE="${TOKEN_OPTIMIZATION_MODE:-ast}"
|
|
27
|
+
|
|
28
|
+
# Enable AST-based code skeleton extraction
|
|
29
|
+
ENABLE_AST_EXTRACTION="${ENABLE_AST_EXTRACTION:-true}"
|
|
30
|
+
|
|
31
|
+
# Enable symbol compression (replaces long identifiers with short tokens)
|
|
32
|
+
# Note: Opt-in because it requires the symbol map to decode
|
|
33
|
+
ENABLE_SYMBOL_COMPRESSION="${ENABLE_SYMBOL_COMPRESSION:-false}"
|
|
34
|
+
|
|
35
|
+
# Enable semantic chunking (splits large files into meaningful chunks)
|
|
36
|
+
ENABLE_SEMANTIC_CHUNKING="${ENABLE_SEMANTIC_CHUNKING:-true}"
|
|
37
|
+
|
|
38
|
+
# Maximum bytes per context file before optimization kicks in
|
|
39
|
+
MAX_CONTEXT_FILE_BYTES="${MAX_CONTEXT_FILE_BYTES:-8000}"
|
|
40
|
+
|
|
41
|
+
# =============================================================================
|
|
42
|
+
# MODULE LOADING WITH FALLBACK
|
|
43
|
+
# =============================================================================
|
|
44
|
+
|
|
45
|
+
# Track which optimization modules are available
|
|
46
|
+
_HAS_CODE_OPTIMIZER=false
|
|
47
|
+
_HAS_CHUNKING=false
|
|
48
|
+
_HAS_SYMBOL_MAP=false
|
|
49
|
+
|
|
50
|
+
# Try to source optimization modules (optional dependencies)
|
|
51
|
+
if [[ -f "$SCRIPT_DIR/lib/code_optimizer.sh" ]]; then
|
|
52
|
+
source "$SCRIPT_DIR/lib/code_optimizer.sh"
|
|
53
|
+
_HAS_CODE_OPTIMIZER=true
|
|
54
|
+
log_debug "Loaded code_optimizer.sh module"
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
if [[ -f "$SCRIPT_DIR/lib/chunking.sh" ]]; then
|
|
58
|
+
source "$SCRIPT_DIR/lib/chunking.sh"
|
|
59
|
+
_HAS_CHUNKING=true
|
|
60
|
+
log_debug "Loaded chunking.sh module"
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
if [[ -f "$SCRIPT_DIR/lib/symbol_map.sh" ]]; then
|
|
64
|
+
source "$SCRIPT_DIR/lib/symbol_map.sh"
|
|
65
|
+
_HAS_SYMBOL_MAP=true
|
|
66
|
+
log_debug "Loaded symbol_map.sh module"
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
# Determine effective optimization mode based on available modules
|
|
70
|
+
_determine_effective_mode() {
|
|
71
|
+
local mode="$1"
|
|
72
|
+
|
|
73
|
+
case "$mode" in
|
|
74
|
+
none|basic)
|
|
75
|
+
echo "$mode"
|
|
76
|
+
;;
|
|
77
|
+
ast|full)
|
|
78
|
+
if [[ "$_HAS_CODE_OPTIMIZER" != "true" ]]; then
|
|
79
|
+
log_warn "Mode '$mode' requires code_optimizer.sh, falling back to basic"
|
|
80
|
+
echo "basic"
|
|
81
|
+
return
|
|
82
|
+
fi
|
|
83
|
+
# Disable features if modules unavailable
|
|
84
|
+
[[ "$mode" == "full" && "$ENABLE_SYMBOL_COMPRESSION" == "true" && "$_HAS_SYMBOL_MAP" != "true" ]] && ENABLE_SYMBOL_COMPRESSION="false"
|
|
85
|
+
[[ "$mode" == "full" && "$ENABLE_SEMANTIC_CHUNKING" == "true" && "$_HAS_CHUNKING" != "true" ]] && ENABLE_SEMANTIC_CHUNKING="false"
|
|
86
|
+
echo "$mode"
|
|
87
|
+
;;
|
|
88
|
+
*)
|
|
89
|
+
log_warn "Unknown mode '$mode', using basic"
|
|
90
|
+
echo "basic"
|
|
91
|
+
;;
|
|
92
|
+
esac
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
# =============================================================================
|
|
96
|
+
# OPTIMIZATION STATISTICS TRACKING
|
|
97
|
+
# =============================================================================
|
|
98
|
+
|
|
99
|
+
# Global counters for statistics
|
|
100
|
+
_TOTAL_FILES_PROCESSED=0
|
|
101
|
+
_TOTAL_ORIGINAL_BYTES=0
|
|
102
|
+
_TOTAL_OPTIMIZED_BYTES=0
|
|
103
|
+
_FILES_OPTIMIZED=0
|
|
104
|
+
_FILES_PASSED_THROUGH=0
|
|
105
|
+
|
|
106
|
+
# Per-file tracking (using map functions from common.sh)
|
|
107
|
+
# Maps: ORIGINAL_SIZES, OPTIMIZED_SIZES, OPTIMIZATION_APPLIED
|
|
108
|
+
|
|
109
|
+
# Reset statistics
|
|
110
|
+
_reset_stats() {
|
|
111
|
+
_TOTAL_FILES_PROCESSED=0
|
|
112
|
+
_TOTAL_ORIGINAL_BYTES=0
|
|
113
|
+
_TOTAL_OPTIMIZED_BYTES=0
|
|
114
|
+
_FILES_OPTIMIZED=0
|
|
115
|
+
_FILES_PASSED_THROUGH=0
|
|
116
|
+
map_clear "ORIGINAL_SIZES"
|
|
117
|
+
map_clear "OPTIMIZED_SIZES"
|
|
118
|
+
map_clear "OPTIMIZATION_APPLIED"
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
# Record file statistics
|
|
122
|
+
# Usage: _record_file_stats "filename" original_size optimized_size "optimization_type"
|
|
123
|
+
_record_file_stats() {
|
|
124
|
+
local filename="$1"
|
|
125
|
+
local original_size="$2"
|
|
126
|
+
local optimized_size="$3"
|
|
127
|
+
local opt_type="$4"
|
|
128
|
+
|
|
129
|
+
local safe_name
|
|
130
|
+
safe_name=$(echo "$filename" | tr '/' '_' | tr '.' '_')
|
|
131
|
+
|
|
132
|
+
map_set "ORIGINAL_SIZES" "$safe_name" "$original_size"
|
|
133
|
+
map_set "OPTIMIZED_SIZES" "$safe_name" "$optimized_size"
|
|
134
|
+
map_set "OPTIMIZATION_APPLIED" "$safe_name" "$opt_type"
|
|
135
|
+
|
|
136
|
+
_TOTAL_FILES_PROCESSED=$((_TOTAL_FILES_PROCESSED + 1))
|
|
137
|
+
_TOTAL_ORIGINAL_BYTES=$((_TOTAL_ORIGINAL_BYTES + original_size))
|
|
138
|
+
_TOTAL_OPTIMIZED_BYTES=$((_TOTAL_OPTIMIZED_BYTES + optimized_size))
|
|
139
|
+
|
|
140
|
+
if [[ "$opt_type" != "none" && "$opt_type" != "passthrough" ]]; then
|
|
141
|
+
_FILES_OPTIMIZED=$((_FILES_OPTIMIZED + 1))
|
|
142
|
+
else
|
|
143
|
+
_FILES_PASSED_THROUGH=$((_FILES_PASSED_THROUGH + 1))
|
|
144
|
+
fi
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
# Log optimization statistics summary
|
|
148
|
+
_log_optimization_stats() {
|
|
149
|
+
if [[ $_TOTAL_FILES_PROCESSED -eq 0 ]]; then
|
|
150
|
+
return
|
|
151
|
+
fi
|
|
152
|
+
|
|
153
|
+
local savings_bytes=$((_TOTAL_ORIGINAL_BYTES - _TOTAL_OPTIMIZED_BYTES))
|
|
154
|
+
local savings_pct=0
|
|
155
|
+
if [[ $_TOTAL_ORIGINAL_BYTES -gt 0 ]]; then
|
|
156
|
+
savings_pct=$((savings_bytes * 100 / _TOTAL_ORIGINAL_BYTES))
|
|
157
|
+
fi
|
|
158
|
+
|
|
159
|
+
local original_tokens=$((_TOTAL_ORIGINAL_BYTES / 4))
|
|
160
|
+
local optimized_tokens=$((_TOTAL_OPTIMIZED_BYTES / 4))
|
|
161
|
+
local tokens_saved=$((original_tokens - optimized_tokens))
|
|
162
|
+
|
|
163
|
+
log_info "Context optimization summary:"
|
|
164
|
+
log_info " Files processed: $_TOTAL_FILES_PROCESSED (optimized: $_FILES_OPTIMIZED, passed through: $_FILES_PASSED_THROUGH)"
|
|
165
|
+
log_info " Original size: $_TOTAL_ORIGINAL_BYTES bytes (~$original_tokens tokens)"
|
|
166
|
+
log_info " Optimized size: $_TOTAL_OPTIMIZED_BYTES bytes (~$optimized_tokens tokens)"
|
|
167
|
+
log_info " Savings: $savings_bytes bytes (~$tokens_saved tokens, ${savings_pct}%)"
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
# =============================================================================
|
|
171
|
+
# FILE CONTENT OPTIMIZATION
|
|
172
|
+
# =============================================================================
|
|
173
|
+
|
|
174
|
+
# Get language identifier for a file extension
|
|
175
|
+
# Usage: _get_lang_for_extension "py"
|
|
176
|
+
_get_lang_for_extension() {
|
|
177
|
+
local extension="$1"
|
|
178
|
+
case "$extension" in
|
|
179
|
+
py) echo "python" ;;
|
|
180
|
+
js) echo "javascript" ;;
|
|
181
|
+
ts|tsx) echo "typescript" ;;
|
|
182
|
+
rb) echo "ruby" ;;
|
|
183
|
+
go) echo "go" ;;
|
|
184
|
+
rs) echo "rust" ;;
|
|
185
|
+
java|kt) echo "java" ;;
|
|
186
|
+
sh|bash|zsh) echo "bash" ;;
|
|
187
|
+
json) echo "json" ;;
|
|
188
|
+
yaml|yml) echo "yaml" ;;
|
|
189
|
+
toml) echo "toml" ;;
|
|
190
|
+
md) echo "markdown" ;;
|
|
191
|
+
sql) echo "sql" ;;
|
|
192
|
+
c) echo "c" ;;
|
|
193
|
+
cpp|cc|cxx) echo "cpp" ;;
|
|
194
|
+
h|hpp) echo "cpp" ;;
|
|
195
|
+
cs) echo "csharp" ;;
|
|
196
|
+
swift) echo "swift" ;;
|
|
197
|
+
php) echo "php" ;;
|
|
198
|
+
*) echo "$extension" ;;
|
|
199
|
+
esac
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
# Check if file type supports AST extraction
|
|
203
|
+
# Usage: _supports_ast_extraction "python"
|
|
204
|
+
_supports_ast_extraction() {
|
|
205
|
+
local lang="$1"
|
|
206
|
+
# Languages that typically support AST-based extraction
|
|
207
|
+
case "$lang" in
|
|
208
|
+
python|javascript|typescript|go|rust|java|cpp|c|csharp|ruby|php|swift)
|
|
209
|
+
return 0
|
|
210
|
+
;;
|
|
211
|
+
*)
|
|
212
|
+
return 1
|
|
213
|
+
;;
|
|
214
|
+
esac
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
# Generate truncated content with header
|
|
218
|
+
_truncate_file() {
|
|
219
|
+
local file_path="$1"
|
|
220
|
+
local original_size="$2"
|
|
221
|
+
echo "# File: $file_path (truncated: ${original_size} bytes, showing first ${MAX_CONTEXT_FILE_BYTES})
|
|
222
|
+
$(head -c "$MAX_CONTEXT_FILE_BYTES" -- "$file_path")
|
|
223
|
+
# ... [truncated] ..."
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
# Optimize file content based on mode
|
|
227
|
+
_optimize_file_content() {
|
|
228
|
+
local file_path="$1"
|
|
229
|
+
local mode="$2"
|
|
230
|
+
local lang="$3"
|
|
231
|
+
|
|
232
|
+
local original_size
|
|
233
|
+
original_size=$(wc -c < "$file_path" | tr -d ' ')
|
|
234
|
+
local optimized_content=""
|
|
235
|
+
local opt_type="none"
|
|
236
|
+
|
|
237
|
+
# Small files pass through unchanged
|
|
238
|
+
if [[ "$mode" != "none" && $original_size -le $MAX_CONTEXT_FILE_BYTES ]]; then
|
|
239
|
+
optimized_content=$(cat -- "$file_path")
|
|
240
|
+
opt_type="passthrough"
|
|
241
|
+
else
|
|
242
|
+
case "$mode" in
|
|
243
|
+
none)
|
|
244
|
+
optimized_content=$(cat -- "$file_path")
|
|
245
|
+
opt_type="none"
|
|
246
|
+
;;
|
|
247
|
+
|
|
248
|
+
basic)
|
|
249
|
+
optimized_content=$(_truncate_file "$file_path" "$original_size")
|
|
250
|
+
opt_type="truncated"
|
|
251
|
+
;;
|
|
252
|
+
|
|
253
|
+
ast)
|
|
254
|
+
if _supports_ast_extraction "$lang" && [[ "$_HAS_CODE_OPTIMIZER" == "true" ]]; then
|
|
255
|
+
local extracted=""
|
|
256
|
+
if extracted=$(optimize_code_file "$file_path" 2>/dev/null) && [[ -n "$extracted" ]]; then
|
|
257
|
+
optimized_content="# File: $file_path (AST skeleton: ${original_size} bytes -> optimized)
|
|
258
|
+
$extracted"
|
|
259
|
+
opt_type="ast_skeleton"
|
|
260
|
+
fi
|
|
261
|
+
fi
|
|
262
|
+
# Fallback to truncation
|
|
263
|
+
if [[ -z "$optimized_content" ]]; then
|
|
264
|
+
optimized_content=$(_truncate_file "$file_path" "$original_size")
|
|
265
|
+
opt_type="truncated"
|
|
266
|
+
fi
|
|
267
|
+
;;
|
|
268
|
+
|
|
269
|
+
full)
|
|
270
|
+
local temp_content=""
|
|
271
|
+
local applied_opts=""
|
|
272
|
+
|
|
273
|
+
# Step 1: AST extraction
|
|
274
|
+
if _supports_ast_extraction "$lang" && [[ "$_HAS_CODE_OPTIMIZER" == "true" ]]; then
|
|
275
|
+
if temp_content=$(optimize_code_file "$file_path" 2>/dev/null) && [[ -n "$temp_content" ]]; then
|
|
276
|
+
applied_opts="ast"
|
|
277
|
+
fi
|
|
278
|
+
fi
|
|
279
|
+
[[ -z "$temp_content" ]] && temp_content=$(cat -- "$file_path")
|
|
280
|
+
|
|
281
|
+
# Step 2: Symbol compression
|
|
282
|
+
if [[ "$ENABLE_SYMBOL_COMPRESSION" == "true" && "$_HAS_SYMBOL_MAP" == "true" ]]; then
|
|
283
|
+
local compressed=""
|
|
284
|
+
if compressed=$(compress_symbols "$temp_content" "$lang" 2>/dev/null) && [[ -n "$compressed" ]]; then
|
|
285
|
+
temp_content="$compressed"
|
|
286
|
+
applied_opts="${applied_opts:+$applied_opts+}symbol_compression"
|
|
287
|
+
fi
|
|
288
|
+
fi
|
|
289
|
+
|
|
290
|
+
# Step 3: Semantic chunking if still too large
|
|
291
|
+
local temp_size
|
|
292
|
+
temp_size=$(echo -n "$temp_content" | wc -c | tr -d ' ')
|
|
293
|
+
if [[ "$ENABLE_SEMANTIC_CHUNKING" == "true" && "$_HAS_CHUNKING" == "true" && $temp_size -gt $MAX_CONTEXT_FILE_BYTES ]]; then
|
|
294
|
+
local chunks_json=""
|
|
295
|
+
chunks_json=$(chunk_file_semantically "$file_path" 2>/dev/null) || chunks_json="[]"
|
|
296
|
+
if [[ -n "$chunks_json" && "$chunks_json" != "[]" ]]; then
|
|
297
|
+
local chunked_content=""
|
|
298
|
+
chunked_content=$(echo "$chunks_json" | jq -r '
|
|
299
|
+
sort_by(-.priority) | .[0:5] |
|
|
300
|
+
map("# Chunk: \(.id) (priority: \(.priority))\n\(.content)") |
|
|
301
|
+
join("\n\n---\n\n")
|
|
302
|
+
' 2>/dev/null) || chunked_content=""
|
|
303
|
+
if [[ -n "$chunked_content" ]]; then
|
|
304
|
+
temp_content="$chunked_content"
|
|
305
|
+
applied_opts="${applied_opts:+$applied_opts+}chunking"
|
|
306
|
+
fi
|
|
307
|
+
fi
|
|
308
|
+
fi
|
|
309
|
+
|
|
310
|
+
# Final truncation if still too large
|
|
311
|
+
temp_size=$(echo -n "$temp_content" | wc -c | tr -d ' ')
|
|
312
|
+
if [[ $temp_size -gt $MAX_CONTEXT_FILE_BYTES ]]; then
|
|
313
|
+
temp_content=$(echo "$temp_content" | head -c "$MAX_CONTEXT_FILE_BYTES")
|
|
314
|
+
applied_opts="${applied_opts:+$applied_opts+}truncated"
|
|
315
|
+
fi
|
|
316
|
+
|
|
317
|
+
opt_type="${applied_opts:-truncated}"
|
|
318
|
+
optimized_content="# File: $file_path (optimized: ${original_size} bytes -> ${temp_size} bytes, mode: $opt_type)
|
|
319
|
+
$temp_content"
|
|
320
|
+
;;
|
|
321
|
+
esac
|
|
322
|
+
fi
|
|
323
|
+
|
|
324
|
+
# Record statistics and output
|
|
325
|
+
local optimized_size
|
|
326
|
+
optimized_size=$(echo -n "$optimized_content" | wc -c | tr -d ' ')
|
|
327
|
+
_record_file_stats "$file_path" "$original_size" "$optimized_size" "$opt_type"
|
|
328
|
+
log_debug "Optimized $file_path: $original_size -> $optimized_size bytes (mode: $opt_type)"
|
|
329
|
+
echo "$optimized_content"
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
# =============================================================================
|
|
333
|
+
# SYMBOL MAP GENERATION
|
|
334
|
+
# =============================================================================
|
|
335
|
+
|
|
336
|
+
# Generate symbol map section for manifest
|
|
337
|
+
# Only included if symbol compression was used
|
|
338
|
+
_generate_symbol_map_section() {
|
|
339
|
+
if [[ "$ENABLE_SYMBOL_COMPRESSION" != "true" || "$_HAS_SYMBOL_MAP" != "true" ]]; then
|
|
340
|
+
return
|
|
341
|
+
fi
|
|
342
|
+
|
|
343
|
+
# Check if get_last_symbol_map function exists and returns content
|
|
344
|
+
if type get_last_symbol_map &>/dev/null; then
|
|
345
|
+
local map_content
|
|
346
|
+
if map_content=$(get_last_symbol_map 2>/dev/null) && [[ -n "$map_content" ]]; then
|
|
347
|
+
echo ""
|
|
348
|
+
echo "### Symbol Map"
|
|
349
|
+
echo "The following symbols were compressed. Use this map to decode:"
|
|
350
|
+
echo '```json'
|
|
351
|
+
echo "$map_content"
|
|
352
|
+
echo '```'
|
|
353
|
+
fi
|
|
354
|
+
fi
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
# =============================================================================
|
|
358
|
+
# CONTEXT MANIFEST GENERATION
|
|
359
|
+
# =============================================================================
|
|
360
|
+
|
|
361
|
+
# Generate the context manifest header
|
|
362
|
+
# Usage: _generate_manifest "mode"
|
|
363
|
+
_generate_manifest() {
|
|
364
|
+
local mode="$1"
|
|
365
|
+
|
|
366
|
+
local original_tokens=$((_TOTAL_ORIGINAL_BYTES / 4))
|
|
367
|
+
local optimized_tokens=$((_TOTAL_OPTIMIZED_BYTES / 4))
|
|
368
|
+
local savings_pct=0
|
|
369
|
+
if [[ $_TOTAL_ORIGINAL_BYTES -gt 0 ]]; then
|
|
370
|
+
savings_pct=$(((_TOTAL_ORIGINAL_BYTES - _TOTAL_OPTIMIZED_BYTES) * 100 / _TOTAL_ORIGINAL_BYTES))
|
|
371
|
+
fi
|
|
372
|
+
|
|
373
|
+
echo "## Context Manifest"
|
|
374
|
+
echo ""
|
|
375
|
+
echo "| Property | Value |"
|
|
376
|
+
echo "|----------|-------|"
|
|
377
|
+
echo "| Files Processed | $_TOTAL_FILES_PROCESSED |"
|
|
378
|
+
echo "| Optimization Mode | $mode |"
|
|
379
|
+
echo "| Token Estimate (before) | ~$original_tokens |"
|
|
380
|
+
echo "| Token Estimate (after) | ~$optimized_tokens |"
|
|
381
|
+
echo "| Token Savings | ${savings_pct}% |"
|
|
382
|
+
|
|
383
|
+
# Add detailed breakdown if there are files
|
|
384
|
+
if [[ $_TOTAL_FILES_PROCESSED -gt 0 ]]; then
|
|
385
|
+
echo ""
|
|
386
|
+
echo "### Optimization Details"
|
|
387
|
+
echo "| File | Original | Optimized | Method |"
|
|
388
|
+
echo "|------|----------|-----------|--------|"
|
|
389
|
+
|
|
390
|
+
# Iterate through tracked files
|
|
391
|
+
for key in $(map_keys "ORIGINAL_SIZES"); do
|
|
392
|
+
local orig
|
|
393
|
+
local opt
|
|
394
|
+
local method
|
|
395
|
+
orig=$(map_get "ORIGINAL_SIZES" "$key")
|
|
396
|
+
opt=$(map_get "OPTIMIZED_SIZES" "$key")
|
|
397
|
+
method=$(map_get "OPTIMIZATION_APPLIED" "$key")
|
|
398
|
+
# Convert key back to something readable (approximate)
|
|
399
|
+
local display_name
|
|
400
|
+
display_name=$(echo "$key" | tr '_' '/')
|
|
401
|
+
echo "| $display_name | ${orig}B | ${opt}B | $method |"
|
|
402
|
+
done
|
|
403
|
+
fi
|
|
404
|
+
|
|
405
|
+
# Add symbol map if compression was used
|
|
406
|
+
_generate_symbol_map_section
|
|
407
|
+
|
|
408
|
+
echo ""
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
# =============================================================================
|
|
412
|
+
# PARAMETERS VALIDATION
|
|
413
|
+
# =============================================================================
|
|
414
|
+
|
|
415
|
+
if [[ $# -lt 2 ]]; then
|
|
416
|
+
echo "Usage: $0 <output_file> \"question\" [file1] [file2] ..." >&2
|
|
417
|
+
echo "" >&2
|
|
418
|
+
echo "Environment variables:" >&2
|
|
419
|
+
echo " TOKEN_OPTIMIZATION_MODE - none, basic, ast, full (default: ast)" >&2
|
|
420
|
+
echo " ENABLE_AST_EXTRACTION - true/false (default: true)" >&2
|
|
421
|
+
echo " ENABLE_SYMBOL_COMPRESSION - true/false (default: false)" >&2
|
|
422
|
+
echo " ENABLE_SEMANTIC_CHUNKING - true/false (default: true)" >&2
|
|
423
|
+
echo " MAX_CONTEXT_FILE_BYTES - threshold in bytes (default: 8000)" >&2
|
|
424
|
+
exit 1
|
|
425
|
+
fi
|
|
426
|
+
|
|
427
|
+
OUTPUT_FILE="$1"
|
|
428
|
+
shift
|
|
429
|
+
QUERY="$1"
|
|
430
|
+
shift
|
|
431
|
+
|
|
432
|
+
# Validate output file path (allow /tmp for output)
|
|
433
|
+
if [[ "$OUTPUT_FILE" == /tmp/* ]]; then
|
|
434
|
+
: # Allow /tmp paths for output
|
|
435
|
+
elif ! validate_file_path "$OUTPUT_FILE" "true"; then
|
|
436
|
+
log_error "Invalid output file path: $OUTPUT_FILE"
|
|
437
|
+
exit 1
|
|
438
|
+
fi
|
|
439
|
+
|
|
440
|
+
# FILES array handling (compatible with set -u)
|
|
441
|
+
# Validate each file path
|
|
442
|
+
FILES=()
|
|
443
|
+
while [[ $# -gt 0 ]]; do
|
|
444
|
+
file_arg="$1"
|
|
445
|
+
shift
|
|
446
|
+
# Allow relative paths and /tmp paths for context files
|
|
447
|
+
if [[ "$file_arg" == /tmp/* ]] || validate_file_path "$file_arg" "false" 2>/dev/null; then
|
|
448
|
+
FILES+=("$file_arg")
|
|
449
|
+
else
|
|
450
|
+
log_warn "Skipping invalid file path: $file_arg"
|
|
451
|
+
fi
|
|
452
|
+
done
|
|
453
|
+
|
|
454
|
+
# =============================================================================
|
|
455
|
+
# BUILD CONTEXT
|
|
456
|
+
# =============================================================================
|
|
457
|
+
|
|
458
|
+
# Determine effective optimization mode
|
|
459
|
+
EFFECTIVE_MODE=$(_determine_effective_mode "$TOKEN_OPTIMIZATION_MODE")
|
|
460
|
+
log_info "Building context with optimization mode: $EFFECTIVE_MODE"
|
|
461
|
+
|
|
462
|
+
# Reset statistics
|
|
463
|
+
_reset_stats
|
|
464
|
+
|
|
465
|
+
{
|
|
466
|
+
echo "# Project Context"
|
|
467
|
+
echo ""
|
|
468
|
+
|
|
469
|
+
# Working directory
|
|
470
|
+
echo "## Working Directory"
|
|
471
|
+
echo '```'
|
|
472
|
+
pwd
|
|
473
|
+
echo '```'
|
|
474
|
+
echo ""
|
|
475
|
+
|
|
476
|
+
# Project structure (relevant files, excluding noise)
|
|
477
|
+
echo "## Project Structure"
|
|
478
|
+
echo '```'
|
|
479
|
+
find . -maxdepth 4 -type f \( \
|
|
480
|
+
-name "*.py" -o -name "*.js" -o -name "*.ts" -o -name "*.tsx" \
|
|
481
|
+
-o -name "*.go" -o -name "*.rs" -o -name "*.java" -o -name "*.kt" \
|
|
482
|
+
-o -name "*.rb" -o -name "*.php" -o -name "*.swift" -o -name "*.c" \
|
|
483
|
+
-o -name "*.cpp" -o -name "*.h" -o -name "*.hpp" -o -name "*.cs" \
|
|
484
|
+
-o -name "*.sh" -o -name "*.bash" -o -name "*.zsh" \
|
|
485
|
+
-o -name "Dockerfile" -o -name "docker-compose*.yml" \
|
|
486
|
+
-o -name "Makefile" -o -name "CMakeLists.txt" \
|
|
487
|
+
-o -name "*.json" -o -name "*.toml" -o -name "*.yaml" -o -name "*.yml" \
|
|
488
|
+
-o -name "*.md" \
|
|
489
|
+
\) \
|
|
490
|
+
-not -path '*/node_modules/*' \
|
|
491
|
+
-not -path '*/.git/*' \
|
|
492
|
+
-not -path '*/dist/*' \
|
|
493
|
+
-not -path '*/build/*' \
|
|
494
|
+
-not -path '*/__pycache__/*' \
|
|
495
|
+
-not -path '*/.venv/*' \
|
|
496
|
+
-not -path '*/venv/*' \
|
|
497
|
+
-not -path '*/target/*' \
|
|
498
|
+
-not -path '*/.next/*' \
|
|
499
|
+
2>/dev/null | sort | head -100 || true
|
|
500
|
+
echo '```'
|
|
501
|
+
echo ""
|
|
502
|
+
|
|
503
|
+
# Content of specific files
|
|
504
|
+
if [[ ${#FILES[@]} -gt 0 ]]; then
|
|
505
|
+
echo "## Relevant Files"
|
|
506
|
+
echo ""
|
|
507
|
+
|
|
508
|
+
for file_path in "${FILES[@]}"; do
|
|
509
|
+
if [[ -f "$file_path" ]]; then
|
|
510
|
+
# Extract extension for syntax highlighting
|
|
511
|
+
extension="${file_path##*.}"
|
|
512
|
+
lang=$(_get_lang_for_extension "$extension")
|
|
513
|
+
|
|
514
|
+
echo "### File: \`$file_path\`"
|
|
515
|
+
echo ""
|
|
516
|
+
|
|
517
|
+
echo "\`\`\`$lang"
|
|
518
|
+
# Use intelligent optimization
|
|
519
|
+
_optimize_file_content "$file_path" "$EFFECTIVE_MODE" "$lang"
|
|
520
|
+
echo ""
|
|
521
|
+
echo "\`\`\`"
|
|
522
|
+
echo ""
|
|
523
|
+
else
|
|
524
|
+
echo "### File: \`$file_path\`"
|
|
525
|
+
echo ""
|
|
526
|
+
echo "*File not found*"
|
|
527
|
+
echo ""
|
|
528
|
+
fi
|
|
529
|
+
done
|
|
530
|
+
fi
|
|
531
|
+
|
|
532
|
+
# Question
|
|
533
|
+
echo "---"
|
|
534
|
+
echo ""
|
|
535
|
+
echo "# Question"
|
|
536
|
+
echo ""
|
|
537
|
+
echo "$QUERY"
|
|
538
|
+
echo ""
|
|
539
|
+
|
|
540
|
+
} > "${OUTPUT_FILE}.tmp"
|
|
541
|
+
|
|
542
|
+
# Now prepend the manifest to the output
|
|
543
|
+
{
|
|
544
|
+
echo "# Project Context"
|
|
545
|
+
echo ""
|
|
546
|
+
|
|
547
|
+
# Generate manifest with statistics
|
|
548
|
+
_generate_manifest "$EFFECTIVE_MODE"
|
|
549
|
+
|
|
550
|
+
# Append the rest of the content (skip the first "# Project Context" line)
|
|
551
|
+
tail -n +3 "${OUTPUT_FILE}.tmp"
|
|
552
|
+
|
|
553
|
+
} > "$OUTPUT_FILE"
|
|
554
|
+
|
|
555
|
+
# Clean up temp file
|
|
556
|
+
rm -f "${OUTPUT_FILE}.tmp"
|
|
557
|
+
|
|
558
|
+
# Log final statistics
|
|
559
|
+
_log_optimization_stats
|
|
560
|
+
|
|
561
|
+
# Output: path of created file (for use in scripts)
|
|
562
|
+
echo "$OUTPUT_FILE"
|