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.
Files changed (62) hide show
  1. package/.claude/commands/ai-consultants:consult.md +94 -0
  2. package/.claude/commands/ai-consultants:debate.md +103 -0
  3. package/.claude/commands/ai-consultants:help.md +30 -0
  4. package/CLAUDE.md +839 -0
  5. package/LICENSE +21 -0
  6. package/README.md +758 -0
  7. package/SKILL.md +156 -0
  8. package/bin/ai-consultants +96 -0
  9. package/docs/COST_RATES.md +172 -0
  10. package/docs/JSON_SCHEMA.md +305 -0
  11. package/docs/SETUP.md +879 -0
  12. package/docs/SMART_ROUTING.md +219 -0
  13. package/docs/cost_rates.json +143 -0
  14. package/package.json +47 -0
  15. package/references/configuration.md +141 -0
  16. package/references/details.md +60 -0
  17. package/scripts/build_context.sh +562 -0
  18. package/scripts/classify_question.sh +148 -0
  19. package/scripts/config.sh +753 -0
  20. package/scripts/configure.sh +1093 -0
  21. package/scripts/consult_all.sh +934 -0
  22. package/scripts/debate_round.sh +421 -0
  23. package/scripts/doctor.sh +642 -0
  24. package/scripts/followup.sh +197 -0
  25. package/scripts/install.sh +319 -0
  26. package/scripts/lib/api.sh +640 -0
  27. package/scripts/lib/api_query.sh +228 -0
  28. package/scripts/lib/cache.sh +338 -0
  29. package/scripts/lib/chunking.sh +880 -0
  30. package/scripts/lib/code_optimizer.sh +671 -0
  31. package/scripts/lib/common.sh +1088 -0
  32. package/scripts/lib/costs.sh +674 -0
  33. package/scripts/lib/personas.sh +372 -0
  34. package/scripts/lib/progress.sh +251 -0
  35. package/scripts/lib/reflection.sh +447 -0
  36. package/scripts/lib/routing.sh +566 -0
  37. package/scripts/lib/schema.json +165 -0
  38. package/scripts/lib/session.sh +327 -0
  39. package/scripts/lib/symbol_map.sh +445 -0
  40. package/scripts/lib/voting.sh +495 -0
  41. package/scripts/peer_review.sh +460 -0
  42. package/scripts/preflight_check.sh +452 -0
  43. package/scripts/query_aider.sh +83 -0
  44. package/scripts/query_amp.sh +67 -0
  45. package/scripts/query_claude.sh +99 -0
  46. package/scripts/query_codex.sh +92 -0
  47. package/scripts/query_cursor.sh +72 -0
  48. package/scripts/query_deepseek.sh +17 -0
  49. package/scripts/query_gemini.sh +85 -0
  50. package/scripts/query_glm.sh +17 -0
  51. package/scripts/query_grok.sh +17 -0
  52. package/scripts/query_kilo.sh +140 -0
  53. package/scripts/query_kimi.sh +67 -0
  54. package/scripts/query_minimax.sh +17 -0
  55. package/scripts/query_mistral.sh +97 -0
  56. package/scripts/query_ollama.sh +328 -0
  57. package/scripts/query_qwen3.sh +93 -0
  58. package/scripts/setup_wizard.sh +367 -0
  59. package/scripts/skillport-install.sh +196 -0
  60. package/scripts/synthesize.sh +332 -0
  61. package/templates/consultation_report.md +323 -0
  62. package/templates/synthesis_prompt.md +120 -0
@@ -0,0 +1,445 @@
1
+ #!/bin/bash
2
+ # symbol_map.sh - Symbol compression for AI Consultants v2.1
3
+ # Replaces long identifiers with short symbols (f1, v1, c1) to reduce tokens.
4
+
5
+ # Source common utilities
6
+ _SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7
+ source "$_SCRIPT_DIR/common.sh" 2>/dev/null || true
8
+
9
+ # Logging stubs if common.sh unavailable
10
+ if ! command -v log_debug &>/dev/null; then
11
+ log_debug() { :; }
12
+ log_info() { echo "[INFO] $1" >&2; }
13
+ log_warn() { echo "[WARN] $1" >&2; }
14
+ log_error() { echo "[ERROR] $1" >&2; }
15
+ fi
16
+
17
+ # Map function stubs if common.sh unavailable
18
+ if ! command -v map_set &>/dev/null; then
19
+ _SYMBOL_MAP_FILE="/tmp/symbol_map_$$"
20
+ map_clear() { rm -f "${_SYMBOL_MAP_FILE}_$1" 2>/dev/null; touch "${_SYMBOL_MAP_FILE}_$1"; }
21
+ map_set() { echo "$2=$3" >> "${_SYMBOL_MAP_FILE}_$1"; }
22
+ map_get() { grep "^$2=" "${_SYMBOL_MAP_FILE}_$1" 2>/dev/null | tail -1 | cut -d= -f2-; }
23
+ map_has() { grep -q "^$2=" "${_SYMBOL_MAP_FILE}_$1" 2>/dev/null; }
24
+ map_keys() { cut -d= -f1 "${_SYMBOL_MAP_FILE}_$1" 2>/dev/null | sort -u | tr '\n' ' '; }
25
+ fi
26
+
27
+ # =============================================================================
28
+ # CONFIGURATION
29
+ # =============================================================================
30
+
31
+ # Enable/disable symbol compression
32
+ ENABLE_SYMBOL_COMPRESSION="${ENABLE_SYMBOL_COMPRESSION:-true}"
33
+
34
+ # Minimum identifier length to compress (shorter ones are kept as-is)
35
+ MIN_IDENTIFIER_LENGTH="${MIN_IDENTIFIER_LENGTH:-8}"
36
+
37
+ # Preserve exported/public names (function exports, class exports)
38
+ PRESERVE_EXPORTS="${PRESERVE_EXPORTS:-true}"
39
+
40
+ # =============================================================================
41
+ # LANGUAGE KEYWORDS (preserved, not compressed)
42
+ # =============================================================================
43
+
44
+ # Common keywords across multiple languages
45
+ KEYWORDS_COMMON="if else elif fi then for while do done case esac in return break continue function"
46
+
47
+ # JavaScript/TypeScript keywords
48
+ KEYWORDS_JS="const let var function class extends implements interface type enum async await import export default from new this super static public private protected readonly typeof instanceof void null undefined true false try catch finally throw"
49
+
50
+ # Python keywords
51
+ KEYWORDS_PYTHON="def class return if elif else for while in not and or is None True False import from as try except finally raise with lambda pass break continue global nonlocal assert yield del"
52
+
53
+ # Go keywords
54
+ KEYWORDS_GO="func package import var const type struct interface map chan go defer return if else for range switch case default break continue fallthrough select"
55
+
56
+ # Java/C# keywords
57
+ KEYWORDS_JAVA="public private protected static final abstract class interface extends implements new this super return if else for while do switch case default break continue try catch finally throw throws void int long float double boolean char String null true false import package"
58
+
59
+ # Rust keywords
60
+ KEYWORDS_RUST="fn let mut const static struct enum impl trait pub mod use crate self super where async await move return if else for while loop match break continue"
61
+
62
+ # Bash keywords
63
+ KEYWORDS_BASH="if then else elif fi case esac for while until do done in function return exit local export readonly declare typeset source"
64
+
65
+ # All keywords combined (space-separated)
66
+ ALL_KEYWORDS="$KEYWORDS_COMMON $KEYWORDS_JS $KEYWORDS_PYTHON $KEYWORDS_GO $KEYWORDS_JAVA $KEYWORDS_RUST $KEYWORDS_BASH"
67
+
68
+ # =============================================================================
69
+ # BUILT-IN FUNCTIONS (preserved)
70
+ # =============================================================================
71
+
72
+ BUILTINS_JS="console log warn error info debug setTimeout setInterval clearTimeout clearInterval fetch Promise JSON Math Array Object String Number Date RegExp Map Set parseInt parseFloat isNaN isFinite encodeURI decodeURI require module exports"
73
+
74
+ BUILTINS_PYTHON="print len range type str int float list dict set tuple bool open input abs min max sum sorted reversed enumerate zip map filter any all isinstance issubclass hasattr getattr setattr delattr"
75
+
76
+ BUILTINS_GO="fmt Println Printf Sprintf Errorf make len cap append copy delete panic recover new close"
77
+
78
+ BUILTINS_BASH="echo printf read eval exec source exit return test true false cd pwd ls cat grep sed awk cut sort uniq wc head tail tr"
79
+
80
+ ALL_BUILTINS="$BUILTINS_JS $BUILTINS_PYTHON $BUILTINS_GO $BUILTINS_BASH"
81
+
82
+ # =============================================================================
83
+ # SYMBOL COUNTERS (use file-based counters for Bash 3.2 subshell compatibility)
84
+ # =============================================================================
85
+
86
+ # Temporary files (avoids subshell variable isolation issues)
87
+ _SYM_COUNTER_FILE="${TMPDIR:-/tmp}/.symbol_map_counters_$$"
88
+ _SYM_MAP_FILE="${TMPDIR:-/tmp}/.symbol_map_json_$$"
89
+
90
+ # Initialize counter file
91
+ _init_symbol_counters() {
92
+ cat > "$_SYM_COUNTER_FILE" << 'EOF'
93
+ _SYM_FUNC_COUNTER=0
94
+ _SYM_VAR_COUNTER=0
95
+ _SYM_CLASS_COUNTER=0
96
+ _SYM_METHOD_COUNTER=0
97
+ _SYM_TYPE_COUNTER=0
98
+ EOF
99
+ }
100
+
101
+ # Reset all counters
102
+ _reset_symbol_counters() {
103
+ _init_symbol_counters
104
+ }
105
+
106
+ # Load current counter values
107
+ _load_symbol_counters() {
108
+ if [[ -f "$_SYM_COUNTER_FILE" ]]; then
109
+ source "$_SYM_COUNTER_FILE"
110
+ else
111
+ _init_symbol_counters
112
+ source "$_SYM_COUNTER_FILE"
113
+ fi
114
+ }
115
+
116
+ # Save counter values
117
+ _save_symbol_counters() {
118
+ cat > "$_SYM_COUNTER_FILE" << EOF
119
+ _SYM_FUNC_COUNTER=$_SYM_FUNC_COUNTER
120
+ _SYM_VAR_COUNTER=$_SYM_VAR_COUNTER
121
+ _SYM_CLASS_COUNTER=$_SYM_CLASS_COUNTER
122
+ _SYM_METHOD_COUNTER=$_SYM_METHOD_COUNTER
123
+ _SYM_TYPE_COUNTER=$_SYM_TYPE_COUNTER
124
+ EOF
125
+ }
126
+
127
+ # Cleanup temporary files on exit
128
+ _cleanup_symbol_counters() {
129
+ rm -f "$_SYM_COUNTER_FILE" 2>/dev/null
130
+ rm -f "$_SYM_MAP_FILE" 2>/dev/null
131
+ }
132
+
133
+ # Save symbol map to file for cross-subshell access
134
+ _save_symbol_map() {
135
+ local map_json="$1"
136
+ echo "$map_json" > "$_SYM_MAP_FILE"
137
+ }
138
+
139
+ # Load symbol map from file
140
+ _load_symbol_map() {
141
+ if [[ -f "$_SYM_MAP_FILE" ]]; then
142
+ cat "$_SYM_MAP_FILE"
143
+ else
144
+ echo "{}"
145
+ fi
146
+ }
147
+
148
+ # Register cleanup (only if not already registered)
149
+ if [[ -z "${_SYM_CLEANUP_REGISTERED:-}" ]]; then
150
+ trap _cleanup_symbol_counters EXIT
151
+ _SYM_CLEANUP_REGISTERED=1
152
+ fi
153
+
154
+ # =============================================================================
155
+ # HELPER FUNCTIONS
156
+ # =============================================================================
157
+
158
+ # Check if a word is a keyword or builtin
159
+ # Usage: _is_preserved_word "word"
160
+ # Returns: 0 if preserved, 1 if not
161
+ _is_preserved_word() {
162
+ local word="$1"
163
+ local preserved=" $ALL_KEYWORDS $ALL_BUILTINS "
164
+
165
+ if [[ "$preserved" == *" $word "* ]]; then
166
+ return 0
167
+ fi
168
+ return 1
169
+ }
170
+
171
+ # Check if identifier should be preserved (export, short, or keyword)
172
+ # Usage: _should_preserve "identifier" "is_exported"
173
+ _should_preserve() {
174
+ local identifier="$1"
175
+ local is_exported="${2:-false}"
176
+
177
+ # Preserve keywords and builtins
178
+ if _is_preserved_word "$identifier"; then
179
+ return 0
180
+ fi
181
+
182
+ # Preserve short identifiers
183
+ local len=${#identifier}
184
+ if [[ $len -lt $MIN_IDENTIFIER_LENGTH ]]; then
185
+ return 0
186
+ fi
187
+
188
+ # Preserve exports if configured
189
+ if [[ "$PRESERVE_EXPORTS" == "true" && "$is_exported" == "true" ]]; then
190
+ return 0
191
+ fi
192
+
193
+ return 1
194
+ }
195
+
196
+ # Get next symbol for a category
197
+ # Usage: _get_next_symbol "function|variable|class|method|type"
198
+ # Note: Uses file-based counters for subshell compatibility
199
+ _get_next_symbol() {
200
+ local category="$1"
201
+ local symbol=""
202
+
203
+ # Load current counter values from file
204
+ _load_symbol_counters
205
+
206
+ case "$category" in
207
+ function)
208
+ _SYM_FUNC_COUNTER=$((_SYM_FUNC_COUNTER + 1))
209
+ symbol="f${_SYM_FUNC_COUNTER}"
210
+ ;;
211
+ variable)
212
+ _SYM_VAR_COUNTER=$((_SYM_VAR_COUNTER + 1))
213
+ symbol="v${_SYM_VAR_COUNTER}"
214
+ ;;
215
+ class)
216
+ _SYM_CLASS_COUNTER=$((_SYM_CLASS_COUNTER + 1))
217
+ symbol="c${_SYM_CLASS_COUNTER}"
218
+ ;;
219
+ method)
220
+ _SYM_METHOD_COUNTER=$((_SYM_METHOD_COUNTER + 1))
221
+ symbol="m${_SYM_METHOD_COUNTER}"
222
+ ;;
223
+ type)
224
+ _SYM_TYPE_COUNTER=$((_SYM_TYPE_COUNTER + 1))
225
+ symbol="t${_SYM_TYPE_COUNTER}"
226
+ ;;
227
+ *)
228
+ _SYM_VAR_COUNTER=$((_SYM_VAR_COUNTER + 1))
229
+ symbol="v${_SYM_VAR_COUNTER}"
230
+ ;;
231
+ esac
232
+
233
+ # Save updated counters back to file
234
+ _save_symbol_counters
235
+
236
+ echo "$symbol"
237
+ }
238
+
239
+ # Detect identifier category based on context
240
+ # Usage: _detect_category "identifier" "context_before"
241
+ _detect_category() {
242
+ local identifier="$1"
243
+ local context="$2"
244
+
245
+ # Function detection patterns
246
+ if echo "$context" | grep -qE '(function|def|func|fn)\s*$'; then
247
+ echo "function"
248
+ return
249
+ fi
250
+
251
+ # Method detection (after dot or within class)
252
+ if echo "$context" | grep -qE '\.\s*$'; then
253
+ echo "method"
254
+ return
255
+ fi
256
+
257
+ # Class detection patterns
258
+ if echo "$context" | grep -qE '(class|struct|interface|type)\s*$'; then
259
+ echo "class"
260
+ return
261
+ fi
262
+
263
+ # Type detection (after colon for type annotations)
264
+ if echo "$context" | grep -qE ':\s*$'; then
265
+ echo "type"
266
+ return
267
+ fi
268
+
269
+ # Variable detection (after assignment operators, var, let, const)
270
+ if echo "$context" | grep -qE '(var|let|const|local)\s*$'; then
271
+ echo "variable"
272
+ return
273
+ fi
274
+
275
+ # Default to variable for unrecognized patterns
276
+ echo "variable"
277
+ }
278
+
279
+ # =============================================================================
280
+ # MAIN FUNCTIONS
281
+ # =============================================================================
282
+
283
+ # Generate a symbol map from code
284
+ # Extracts identifiers and creates mapping for compression
285
+ #
286
+ # Usage: generate_symbol_map "code"
287
+ # Output: JSON object with mappings { "originalName": {"symbol": "f1", "category": "function"}, ... }
288
+ generate_symbol_map() {
289
+ local code="$1"
290
+
291
+ if [[ "$ENABLE_SYMBOL_COMPRESSION" != "true" ]]; then
292
+ echo "{}"
293
+ return
294
+ fi
295
+
296
+ # Reset counters for fresh mapping
297
+ _reset_symbol_counters
298
+
299
+ # Clear any previous mapping
300
+ map_clear "SYMMAP"
301
+ map_clear "SYMMAP_CATEGORY"
302
+
303
+ # Remove string literals and comments to avoid extracting identifiers from them
304
+ local clean_code
305
+ clean_code=$(echo "$code" | sed -E '
306
+ # Remove single-line comments (// and #)
307
+ s|//[^"]*$||g
308
+ s|#[^"]*$||g
309
+ # Remove multi-line comment markers (simplified)
310
+ s|/\*[^*]*\*/||g
311
+ ')
312
+
313
+ # Extract potential identifiers (alphanumeric + underscore, starting with letter or underscore)
314
+ # Pattern matches camelCase, snake_case, PascalCase identifiers
315
+ local identifiers
316
+ identifiers=$(echo "$clean_code" | grep -oE '\b[a-zA-Z_][a-zA-Z0-9_]*\b' | sort -u)
317
+
318
+ local json_entries=""
319
+ local first_entry=true
320
+
321
+ for identifier in $identifiers; do
322
+ # Skip if should be preserved
323
+ if _should_preserve "$identifier" "false"; then
324
+ continue
325
+ fi
326
+
327
+ # Skip if already mapped
328
+ if map_has "SYMMAP" "$identifier"; then
329
+ continue
330
+ fi
331
+
332
+ # Detect category from code context
333
+ local context_line
334
+ context_line=$(echo "$code" | grep -m1 -E "(^|[^a-zA-Z0-9_])$identifier([^a-zA-Z0-9_]|$)" | head -1)
335
+ local context_before
336
+ context_before=$(echo "$context_line" | sed -E "s/$identifier.*//")
337
+
338
+ local category
339
+ category=$(_detect_category "$identifier" "$context_before")
340
+
341
+ # Get symbol for this category
342
+ local symbol
343
+ symbol=$(_get_next_symbol "$category")
344
+
345
+ # Store in map
346
+ map_set "SYMMAP" "$identifier" "$symbol"
347
+ map_set "SYMMAP_CATEGORY" "$identifier" "$category"
348
+
349
+ # Build JSON entry
350
+ if [[ "$first_entry" == "true" ]]; then
351
+ first_entry=false
352
+ else
353
+ json_entries="$json_entries,"
354
+ fi
355
+
356
+ # Escape special characters in identifier for JSON
357
+ local safe_identifier
358
+ safe_identifier=$(echo "$identifier" | sed 's/\\/\\\\/g; s/"/\\"/g')
359
+
360
+ json_entries="$json_entries
361
+ \"$safe_identifier\": {\"symbol\": \"$symbol\", \"category\": \"$category\"}"
362
+ done
363
+
364
+ local result="{$json_entries
365
+ }"
366
+
367
+ # Save to file for cross-subshell access
368
+ _save_symbol_map "$result"
369
+
370
+ echo "$result"
371
+ }
372
+
373
+ # Portable word boundary replacement (works on BSD and GNU sed)
374
+ # Replaces identifier only when surrounded by non-word characters
375
+ # Usage: _replace_word "text" "old_word" "new_word"
376
+ _replace_word() {
377
+ local text="$1"
378
+ local old_word="$2"
379
+ local new_word="$3"
380
+
381
+ # Use sed with explicit boundary patterns (portable across BSD/GNU)
382
+ # Match word at: start of line, after non-word char, before non-word char, end of line
383
+ echo "$text" | sed -E "
384
+ s/(^|[^a-zA-Z0-9_])${old_word}([^a-zA-Z0-9_]|$)/\1${new_word}\2/g
385
+ s/(^|[^a-zA-Z0-9_])${old_word}([^a-zA-Z0-9_]|$)/\1${new_word}\2/g
386
+ "
387
+ }
388
+
389
+ # Compress symbols in code using generated or provided map
390
+ #
391
+ # Usage: compress_symbols "code" [symbol_map_json]
392
+ # Output: Compressed code with identifiers replaced by short symbols
393
+ # Also sets SYMBOL_MAP_JSON global variable with the map
394
+ compress_symbols() {
395
+ local code="$1"
396
+ local provided_map="${2:-}"
397
+
398
+ if [[ "$ENABLE_SYMBOL_COMPRESSION" != "true" ]]; then
399
+ echo "$code"
400
+ return
401
+ fi
402
+
403
+ # Generate map if not provided
404
+ local map_json
405
+ if [[ -z "$provided_map" ]]; then
406
+ map_json=$(generate_symbol_map "$code")
407
+ else
408
+ map_json="$provided_map"
409
+ fi
410
+
411
+ # Export map for later decompression
412
+ SYMBOL_MAP_JSON="$map_json"
413
+
414
+ # Check if map is empty
415
+ if [[ "$map_json" == "{}" || "$map_json" == "{
416
+ }" ]]; then
417
+ echo "$code"
418
+ return
419
+ fi
420
+
421
+ local result="$code"
422
+
423
+ # Process each mapping (extract from JSON without jq dependency for portability)
424
+ # Parse JSON entries: "identifier": {"symbol": "f1", ...}
425
+ local entries
426
+ entries=$(echo "$map_json" | grep -oE '"[^"]+": \{"symbol": "[^"]+"' | sed 's/": {"symbol": "/ /; s/"//g')
427
+
428
+ # Sort by identifier length (longest first) to avoid partial replacements
429
+ entries=$(echo "$entries" | awk '{print length($1), $0}' | sort -rn | cut -d' ' -f2-)
430
+
431
+ while IFS=' ' read -r identifier symbol; do
432
+ if [[ -n "$identifier" && -n "$symbol" ]]; then
433
+ # Replace identifier with symbol using portable word boundaries
434
+ result=$(_replace_word "$result" "$identifier" "$symbol")
435
+ fi
436
+ done <<< "$entries"
437
+
438
+ echo "$result"
439
+ }
440
+
441
+ # Get the last generated symbol map (useful after compress_symbols in subshell)
442
+ # Usage: map_json=$(get_last_symbol_map)
443
+ get_last_symbol_map() {
444
+ _load_symbol_map
445
+ }