]> begriffs open source - ai-unix/blob - ai-grep
Simpler prompt for fixer
[ai-unix] / ai-grep
1 #!/bin/bash
2
3 # AI-Grep: Semantic search tool
4 # Beautiful implementation using composable functions
5
6 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7 source "$SCRIPT_DIR/ai-common"
8
9 show_usage() {
10     cat << 'EOF'
11 Usage: ai-grep [OPTIONS] PATTERN [FILE...]
12 Semantic search based on meaning rather than exact string matching.
13
14 Options:
15   -v    Select non-matching lines (semantic inverse)
16   -n    Show line numbers
17   -c    Show only count of matching lines
18   -h    Show this help message
19 EOF
20 }
21
22 build_grep_prompt() {
23     local pattern="$1"
24     local invert_match="$2"
25     local show_numbers="$3"
26     local count_only="$4"
27     
28     local match_instruction
29     if [[ "$invert_match" == "true" ]]; then
30         match_instruction="Find lines that do NOT semantically match this pattern. Return only those non-matching lines, one per line."
31     else
32         match_instruction="Find lines that semantically match this pattern. Return only those matching lines, one per line."
33     fi
34     
35     local output_instruction
36     if [[ "$count_only" == "true" ]]; then
37         output_instruction="Output only a single number - the count of matching lines."
38     elif [[ "$show_numbers" == "true" ]]; then
39         output_instruction="Prefix each line with its line number followed by a colon (format: 'N:line')."
40     else
41         output_instruction="Output only the lines themselves, no prefixes or formatting."
42     fi
43     
44     cat << EOF
45 You are a semantic grep tool. Analyze the following text and find lines that semantically match the pattern '$pattern'.
46
47 CRITICAL: Output ONLY the requested lines or count. Do not include explanations, introductory text, or analysis.
48
49 $match_instruction
50
51 $output_instruction
52 EOF
53 }
54
55 process_grep_response() {
56     local response="$1"
57     local count_only="$2"
58     
59     if [[ "$count_only" == "true" ]]; then
60         local count
61         if ! count=$(process_count_response "$response"); then
62             return $?
63         fi
64         
65         echo "$count"
66         if [[ "$count" -gt 0 ]]; then
67             return 0
68         else
69             return "$EXIT_NO_MATCH"
70         fi
71     else
72         process_filtered_response "$response"
73     fi
74 }
75
76 validate_and_setup() {
77     local pattern="$1"
78     shift
79     local files=("$@")
80     
81     ensure_dependencies
82     ensure_argument_provided "Pattern" "$pattern" show_usage
83     
84     [[ ${#files[@]} -gt 0 ]] && ensure_files_exist "${files[@]}"
85 }
86
87 main() {
88     local invert_match="false"
89     local show_numbers="false"
90     local count_only="false"
91     local pattern=""
92     local files=()
93     
94     # Parse options
95     while getopts "vnch" opt; do
96         case $opt in
97             v) invert_match="true" ;;
98             n) show_numbers="true" ;;
99             c) count_only="true" ;;
100             h) handle_help_option show_usage ;;
101             \?) handle_invalid_option "$OPTARG" ;;
102         esac
103     done
104     
105     shift $((OPTIND-1))
106     pattern="$1"
107     shift
108     files=("$@")
109     
110     # Early validation with immediate exit on failure
111     validate_and_setup "$pattern" "${files[@]}"
112     
113     # Process input with early exit
114     local input
115     input=$(process_input_sources "${files[@]}") || exit "$EXIT_NO_MATCH"
116     
117     # Execute LLM request with early exit
118     local prompt response
119     prompt=$(build_grep_prompt "$pattern" "$invert_match" "$show_numbers" "$count_only")
120     response=$(execute_llm_request "$prompt" "$input") || handle_llm_error $?
121     
122     # Process response with early exit
123     local result
124     result=$(process_grep_response "$response" "$count_only") || exit $?
125     
126     echo "$result"
127 }
128
129 main "$@"