@apmantza/greedysearch-pi 1.4.2 → 1.5.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.
@@ -5,141 +5,70 @@ description: Multi-engine AI web search — greedy_search, deep_research, and co
5
5
 
6
6
  # GreedySearch Tools
7
7
 
8
- ## Tool Overview
9
-
10
- | Tool | Speed | Use for |
8
+ | Tool | Speed | Use For |
11
9
  |------|-------|---------|
12
- | `greedy_search` | 15-90s | Quick lookups, comparisons, debugging errors |
13
- | `deep_research` | 60-120s | Architecture decisions, thorough research, source-backed answers |
14
- | `coding_task` | 60-180s | Second opinions on code, reviews, debugging tricky issues |
15
-
16
- ## When to Use Which
10
+ | `greedy_search` | 15-90s | Quick lookups, current info |
11
+ | `deep_research` | 60-120s | Architecture decisions, source-backed research |
12
+ | `coding_task` | 60-180s | Debug, review, plan modes for hard problems |
17
13
 
18
- - **`greedy_search`** — Default. Fast enough for most things. Use when you need current info.
19
- - **`deep_research`** — When the answer *matters*. Gives you a structured document with confidence scores, deduplicated sources ranked by consensus, Gemini synthesis, AND actual content from top sources.
20
- - **`coding_task`** — When you need a "second opinion" on hard problems. Best for `debug` and `plan` modes on tricky issues.
21
-
22
- ---
14
+ ## greedy_search
23
15
 
24
- # greedy_search
16
+ Multi-engine AI search (Perplexity, Bing, Google).
25
17
 
26
- Multi-engine AI web search with streaming progress.
27
-
28
- ```greedy_search({ query: "what changed in React 19", engine: "all" })```
18
+ ```greedy_search({ query: "React 19 changes", engine: "all" })```
29
19
 
30
20
  | Parameter | Type | Default | Description |
31
21
  |-----------|------|---------|-------------|
32
- | `query` | string | required | The search question |
22
+ | `query` | string | required | Search question |
33
23
  | `engine` | string | `"all"` | `all`, `perplexity`, `bing`, `google`, `gemini` |
34
- | `synthesize` | boolean | `false` | Synthesize via Gemini |
35
- | `fullAnswer` | boolean | `false` | Complete answer vs ~300 char summary |
36
-
37
- **When to use:** Quick lookups, error messages, comparing tools, "what's new in X".
24
+ | `synthesize` | boolean | `false` | Gemini synthesis (+30s, higher quality) |
25
+ | `fullAnswer` | boolean | `false` | Complete vs ~300 char summary |
38
26
 
39
- ---
27
+ **When to use:** Current info, version changes, comparisons, debugging errors.
28
+ **vs web_search:** Slower but higher quality — 3 engines cross-verify.
40
29
 
41
- # deep_research
30
+ **Engine Selection:**
31
+ - `all` (default): 30-90s, highest confidence
32
+ - `perplexity`: 15-30s, technical Q&A
33
+ - `bing`: 15-30s, recent news
34
+ - `google`: 15-30s, broad coverage
35
+ - `gemini`: 15-30s, different training data
42
36
 
43
- Comprehensive research with source fetching and synthesis. Returns a structured document.
37
+ ## deep_research
44
38
 
45
- ```deep_research({ query: "RAG vs fine-tuning for production" })```
39
+ Comprehensive research with source fetching and synthesis.
46
40
 
47
- Returns:
48
- - Full answers from all 3 engines (Perplexity, Bing, Google)
49
- - Gemini synthesis combining all perspectives
50
- - Deduplicated sources ranked by consensus (3/3 > 2/3 > 1/3)
51
- - Fetched content from top 5 sources (no CDP — uses native fetch)
52
- - Confidence metadata (which engines responded, consensus score)
41
+ ```deep_research({ query: "RAG vs fine-tuning tradeoffs" })```
53
42
 
54
- **When to use:** Architecture decisions, "which library should I use", research for a writeup, anything where you need source-backed confidence.
43
+ Returns: Full answers + Gemini synthesis + deduplicated sources (ranked by consensus [3/3, 2/3, 1/3]) + fetched content from top sources.
55
44
 
56
- ---
45
+ **When to use:** Research that matters — library comparisons, architecture decisions, source-backed confidence.
57
46
 
58
- # coding_task
47
+ ## coding_task
59
48
 
60
- Browser-based coding assistant using Gemini and/or Copilot.
49
+ Browser-based coding assistant via Gemini/Copilot.
61
50
 
62
- ```coding_task({ task: "debug this race condition", mode: "debug", engine: "all" })```
51
+ ```coding_task({ task: "debug race condition", mode: "debug", engine: "gemini" })```
63
52
 
64
53
  | Parameter | Type | Default | Description |
65
54
  |-----------|------|---------|-------------|
66
- | `task` | string | required | The coding task/question |
67
- | `engine` | string | `"gemini"` | `gemini`, `copilot`, or `all` |
68
- | `mode` | string | `"code"` | See modes below |
55
+ | `task` | string | required | Coding task/question |
56
+ | `engine` | string | `"gemini"` | `gemini`, `copilot`, `all` |
57
+ | `mode` | string | `"code"` | `debug`, `plan`, `review`, `test`, `code` |
69
58
  | `context` | string | — | Code snippet to include |
70
59
 
71
60
  **Modes:**
61
+ - `debug`: Stuck on tricky bug — fresh eyes catch different failure modes
62
+ - `plan`: Big refactor coming — Gemini plays devil's advocate
63
+ - `review`: High-stakes code review before merge
64
+ - `test`: Edge cases the author missed
65
+ - `code`: Simple generation (but you're probably faster)
72
66
 
73
- | Mode | Use when |
74
- |------|----------|
75
- | `debug` | Stuck on a tricky bug. Fresh eyes catch different failure modes. |
76
- | `plan` | About to refactor something big. Gemini plays devil's advocate. |
77
- | `review` | Code review before merge. High-stakes code benefits from second opinion. |
78
- | `test` | Need edge cases the author missed. |
79
- | `code` | Just need the code written (but you can probably do this yourself faster). |
80
-
81
- **When to use:** Debugging tricky issues, planning major refactors, security-critical reviews. **Skip for** simple code generation — you're faster.
82
-
83
- ## Greedy Search vs Built-in Web Search
84
-
85
- | | `web_search` | `greedy_search` |
86
- |---|---|---|
87
- | Speed | Instant (~2s) | 15-60s (one engine) / 30-90s (all engines) |
88
- | Quality | Good for simple lookups | Higher — 3 AI engines cross-verify |
89
- | Synthesis | Single engine answer | Optional Gemini synthesis (cleanest answer) |
90
- | Use for | Quick facts, simple questions | Research, decisions, complex topics |
91
-
92
- **Rule of thumb:** Use `web_search` for quick facts. Use `greedy_search` when the answer matters — architecture decisions, comparing libraries, understanding new releases, debugging tricky errors.
93
-
94
- ## When to Use
95
-
96
- - **Version-specific changes** — "What changed in React 19?" / "Breaking changes in FastAPI 0.100"
97
- - **Choosing between tools** — "Prisma vs Drizzle in 2026" / "Best auth library for Next.js 15"
98
- - **Debugging** — User pastes an error message or stack trace
99
- - **Research tasks** — When you need to synthesize information from multiple sources
100
- - **Best practices** — "How to structure a monorepo" / "Auth patterns for SaaS"
101
- - **Anything where training data might be stale** — 2025+, 2026+, "latest", "current", "still maintained"
102
-
103
- ## Engine Selection
104
-
105
- ```greedy_search({ query: "what changed in React 19", engine: "all" })```
106
-
107
- | Engine | Latency | Best for |
108
- |---|---|---|
109
- | `all` (default) | 30-90s | Highest confidence — all 3 engines in parallel |
110
- | `perplexity` | 15-30s | Technical Q&A, code explanations, documentation |
111
- | `bing` | 15-30s | Recent news, Microsoft ecosystem |
112
- | `google` | 15-30s | Broad coverage, multiple perspectives |
113
- | `gemini` | 15-30s | Google's perspective, different training data |
114
-
115
- Use a single engine when speed matters and the question isn't contentious.
116
-
117
- ## Synthesis Mode
118
-
119
- For complex research questions, use `synthesize: true` with `engine: "all"`:
120
-
121
- ```greedy_search({ query: "best auth patterns for SaaS in 2026", engine: "all", synthesize: true })```
122
-
123
- This deduplicates sources across engines and feeds them to Gemini for one clean, synthesized answer. Adds ~30s but produces the highest quality output — ideal for research tasks where you'd otherwise need to parse 3 separate answers.
124
-
125
- Use synthesis when:
126
- - You need one definitive answer, not multiple perspectives
127
- - You're researching a topic to write about or make a decision
128
- - The question has a lot of noise and you want the signal
129
-
130
- Skip synthesis when:
131
- - You want to see where engines disagree (useful for controversial topics)
132
- - Speed matters
133
-
134
- ## Full vs Short Answers
135
-
136
- Default mode returns ~300 char summaries to save tokens. Use `fullAnswer: true` when you need the complete response:
137
-
138
- ```greedy_search({ query: "explain the React compiler", engine: "perplexity", fullAnswer: true })```
67
+ **When to use:** Second opinions on hard problems. Skip for simple code.
139
68
 
140
69
  ## Interpreting Results
141
70
 
142
71
  - **All 3 agree** → High confidence, present as fact
143
- - **2 agree, 1 differs** → Likely correct but note the dissent
144
- - **All differ** → Present the different perspectives to the user
145
- - **Sources with `[3/3]` or `[2/3]`** → Cited by multiple engines, higher confidence
72
+ - **2 agree, 1 differs** → Likely correct, note the dissent
73
+ - **All differ** → Present different perspectives
74
+ - **Sources [3/3] or [2/3]** → Cited by multiple engines, higher confidence
@@ -1,105 +0,0 @@
1
- # New Feature Ideas
2
-
3
- Ideas for future features — thinking from the perspective of an AI assistant using these tools.
4
-
5
- ---
6
-
7
- ## 1. Source Verification
8
-
9
- **Problem:** I get sources but can't verify if they're live, updated, or actually support the claimed content.
10
-
11
- ```
12
- verify_sources({ urls: ["https://...", "https://..."] })
13
- → [{ url, status: 200, title, snippet, lastModified, claim: "supports X" }]
14
- ```
15
-
16
- **Use cases:**
17
- - Before citing a source, verify it's not 404
18
- - Check if a page actually contains the claimed information
19
- - Get last-modified dates to assess freshness
20
-
21
- ---
22
-
23
- ## 2. Incremental / Continuation Research
24
-
25
- **Problem:** After deep_research on "RAG vs fine-tuning", going deeper on just RAG means re-running everything with a new query and losing original context.
26
-
27
- ```
28
- deep_research({ query: "RAG vs fine-tuning", ... }) // initial
29
- continue_research({ previousId: "...", query: "production RAG architectures" }) // goes deeper on RAG
30
- ```
31
-
32
- **Use cases:**
33
- - Drill into a specific aspect after initial broad research
34
- - Build on previous results without re-fetching everything
35
- - Progressive disclosure of complex topics
36
-
37
- ---
38
-
39
- ## 3. Multi-Query Synthesis
40
-
41
- **Problem:** One query isn't enough for complex research. I chain multiple greedy_search calls manually.
42
-
43
- ```
44
- multi_research({
45
- queries: ["auth best practices", "NextAuth vs Clerk vs Lucia", "Next.js auth security"],
46
- synthesize: true
47
- })
48
- ```
49
-
50
- **Use cases:**
51
- - "Best auth for Next.js" needs multiple angles
52
- - Research with different facets (comparison, security, performance)
53
- - Casting a wider net when single query returns narrow results
54
-
55
- ---
56
-
57
- ## 4. Structured Extraction
58
-
59
- **Problem:** When researching "which libraries are maintained", I want tables (name, stars, last commit, license), not prose.
60
-
61
- ```
62
- extract_structured({
63
- query: "Python HTTP client libraries 2026",
64
- schema: { name: "string", stars: "number", lastUpdated: "date", async: "boolean" }
65
- })
66
- ```
67
-
68
- **Use cases:**
69
- - Library comparisons as structured data
70
- - Dependency audits
71
- - Feature matrices for tools/frameworks
72
-
73
- ---
74
-
75
- ## 5. Confidence Scoring on Specific Claims
76
-
77
- **Problem:** I say "high confidence" but it's hand-wavy. What if I could ask: "how confident are we that library X is actively maintained?"
78
-
79
- ```
80
- verify_claim({
81
- claim: "Prisma is actively maintained",
82
- evidence: ["last commit: 2 weeks ago", "open issues: 45", "npm downloads: 2M/week"]
83
- })
84
- → { confidence: 0.95, reasoning: "..." }
85
- ```
86
-
87
- ---
88
-
89
- ## 6. Research Cache / History
90
-
91
- **Problem:** I do expensive deep_research, then the user asks a follow-up. I have to re-run everything.
92
-
93
- ```
94
- get_research(id: "...") // retrieve previous results
95
- list_research({ query: "RAG" }) // find related previous research
96
- ```
97
-
98
- ---
99
-
100
- ## Priority
101
-
102
- 1. **Source verification** — high value, relatively simple, fixes trust gap
103
- 2. **Multi-query synthesis** — high value, complex but powerful
104
- 3. **Incremental research** — medium value, nice UX improvement
105
- 4. **Structured extraction** — medium value, specialized use cases
package/test.sh DELETED
@@ -1,298 +0,0 @@
1
- #!/usr/bin/env bash
2
- # test.sh — GreedySearch test suite
3
- #
4
- # Usage:
5
- # ./test.sh # run all tests
6
- # ./test.sh parallel # run only parallel test
7
- # ./test.sh quick # skip slow tests (parallel + stress)
8
- #
9
- # Tests verify:
10
- # - No crashes/errors from extractors
11
- # - All engines complete in "all" mode
12
- # - Correct queries in results (not mixed up)
13
- # - Parallel searches don't race on shared tabs
14
-
15
- set -e
16
-
17
- cd "$(dirname "$0")"
18
- RESULTS_DIR="results/test_$(date +%Y%m%d_%H%M%S)"
19
- mkdir -p "$RESULTS_DIR"
20
-
21
- RED='\033[0;31m'
22
- GREEN='\033[0;32m'
23
- YELLOW='\033[1;33m'
24
- NC='\033[0m'
25
-
26
- PASS=0
27
- FAIL=0
28
- FAILURES=() # Array to store failure details for report
29
-
30
- pass() { PASS=$((PASS+1)); echo -e " ${GREEN}✓${NC} $1"; }
31
- fail() {
32
- FAIL=$((FAIL+1));
33
- echo -e " ${RED}✗${NC} $1"
34
- FAILURES+=("$1")
35
- }
36
-
37
- check_no_errors() {
38
- local file="$1"
39
- local errors=$(node -e "
40
- const d = JSON.parse(require('fs').readFileSync('$file','utf8'));
41
- const errs = [];
42
- if (d.perplexity?.error) errs.push('perplexity: ' + d.perplexity.error);
43
- if (d.bing?.error) errs.push('bing: ' + d.bing.error);
44
- if (d.google?.error) errs.push('google: ' + d.google.error);
45
- console.log(errs.join('; ') || '');
46
- " 2>/dev/null)
47
- echo "$errors"
48
- }
49
-
50
- check_correct_queries() {
51
- local file="$1"
52
- local expected="$2"
53
- local result=$(node -e "
54
- const d = JSON.parse(require('fs').readFileSync('$file','utf8'));
55
- const queries = [d.perplexity?.query, d.bing?.query, d.google?.query].filter(Boolean);
56
- const allMatch = queries.every(q => q === '$expected');
57
- console.log(allMatch ? 'ok' : 'queries: ' + queries.join(', '));
58
- " 2>/dev/null)
59
- echo "$result"
60
- }
61
-
62
- check_all_engines_completed() {
63
- local file="$1"
64
- local result=$(node -e "
65
- const d = JSON.parse(require('fs').readFileSync('$file','utf8'));
66
- const hasAnswer = (e) => d[e]?.answer && d[e].answer.length > 10;
67
- const engines = ['perplexity', 'bing', 'google'];
68
- const ok = engines.every(hasAnswer);
69
- console.log(ok ? 'ok' : 'missing: ' + engines.filter(e => !hasAnswer(e)).join(', '));
70
- " 2>/dev/null)
71
- echo "$result"
72
- }
73
-
74
- # ─────────────────────────────────────────────────────────
75
- echo -e "\n${YELLOW}═══ GreedySearch Test Suite ═══${NC}\n"
76
-
77
- # ── Test 1: Single engine mode ──────────────────────────
78
- if [[ "$1" != "parallel" ]]; then
79
- echo "Test 1: Single engine mode"
80
-
81
- for engine in perplexity bing google gemini; do
82
- outfile="$RESULTS_DIR/single_${engine}.json"
83
- node search.mjs "$engine" "explain $engine attention mechanism" --out "$outfile" 2>/dev/null
84
- if [[ $? -eq 0 && -f "$outfile" ]]; then
85
- errors=$(check_no_errors "$outfile")
86
- if [[ -z "$errors" ]]; then
87
- pass "$engine completed without errors"
88
- else
89
- fail "$engine errors: $errors"
90
- fi
91
- else
92
- fail "$engine failed to run"
93
- fi
94
- done
95
- fi
96
-
97
- # ── Test 2: Sequential "all" mode ───────────────────────
98
- if [[ "$1" != "parallel" ]]; then
99
- echo -e "\nTest 2: Sequential 'all' mode (3 runs)"
100
-
101
- for i in 1 2 3; do
102
- outfile="$RESULTS_DIR/seq_${i}.json"
103
- query="LLM inference optimization techniques $i"
104
- node search.mjs all "$query" --out "$outfile" 2>/dev/null
105
-
106
- if [[ $? -eq 0 && -f "$outfile" ]]; then
107
- errors=$(check_no_errors "$outfile")
108
- if [[ -z "$errors" ]]; then
109
- pass "Run $i: no errors"
110
- else
111
- fail "Run $i errors: $errors"
112
- fi
113
-
114
- correct=$(check_correct_queries "$outfile" "$query")
115
- if [[ "$correct" == "ok" ]]; then
116
- pass "Run $i: correct queries"
117
- else
118
- fail "Run $i: $correct"
119
- fi
120
- else
121
- fail "Run $i: failed to run"
122
- fi
123
- done
124
- fi
125
-
126
- # ── Test 3: Parallel "all" mode (race condition test) ───
127
- if [[ "$1" != "quick" && "$1" != "sequential" ]]; then
128
- echo -e "\nTest 3: Parallel 'all' mode (5 concurrent searches)"
129
-
130
- PARALLEL_QUERIES=(
131
- "what are transformer architectures in LLMs"
132
- "explain RLHF fine-tuning process"
133
- "difference between GPT and BERT models"
134
- "how does chain of thought prompting work"
135
- "what is retrieval augmented generation"
136
- )
137
-
138
- PIDS=()
139
- for i in "${!PARALLEL_QUERIES[@]}"; do
140
- outfile="$RESULTS_DIR/parallel_${i}.json"
141
- query="${PARALLEL_QUERIES[$i]}"
142
- node search.mjs all "$query" --out "$outfile" 2>/dev/null &
143
- PIDS+=($!)
144
- done
145
-
146
- # Wait for all to complete
147
- FAILED=0
148
- for i in "${!PIDS[@]}"; do
149
- if ! wait "${PIDS[$i]}"; then
150
- fail "Parallel $i: process exited with error"
151
- ((FAILED++))
152
- fi
153
- done
154
-
155
- if [[ $FAILED -eq 0 ]]; then
156
- # Check results
157
- for i in "${!PARALLEL_QUERIES[@]}"; do
158
- outfile="$RESULTS_DIR/parallel_${i}.json"
159
- query="${PARALLEL_QUERIES[$i]}"
160
-
161
- if [[ -f "$outfile" ]]; then
162
- errors=$(check_no_errors "$outfile")
163
- if [[ -z "$errors" ]]; then
164
- pass "Parallel $i: no errors"
165
- else
166
- fail "Parallel $i: $errors"
167
- fi
168
-
169
- correct=$(check_correct_queries "$outfile" "$query")
170
- if [[ "$correct" == "ok" ]]; then
171
- pass "Parallel $i: correct query"
172
- else
173
- fail "Parallel $i: $correct (TAB RACE DETECTED)"
174
- fi
175
-
176
- all_done=$(check_all_engines_completed "$outfile")
177
- if [[ "$all_done" == "ok" ]]; then
178
- pass "Parallel $i: all engines answered"
179
- else
180
- fail "Parallel $i: $all_done"
181
- fi
182
- else
183
- fail "Parallel $i: no result file"
184
- fi
185
- done
186
- fi
187
- fi
188
-
189
- # ── Test 4: Synthesis mode ──────────────────────────────
190
- if [[ "$1" != "parallel" && "$1" != "quick" ]]; then
191
- echo -e "\nTest 4: Synthesis mode"
192
-
193
- outfile="$RESULTS_DIR/synthesis.json"
194
- node search.mjs all "what is Mixture of Experts in neural networks" --synthesize --out "$outfile" 2>/dev/null
195
-
196
- if [[ $? -eq 0 && -f "$outfile" ]]; then
197
- has_synthesis=$(node -e "
198
- const d = JSON.parse(require('fs').readFileSync('$outfile','utf8'));
199
- console.log(d._synthesis?.answer ? 'ok' : 'missing');
200
- " 2>/dev/null)
201
-
202
- if [[ "$has_synthesis" == "ok" ]]; then
203
- pass "Synthesis completed"
204
- else
205
- fail "Synthesis missing"
206
- fi
207
-
208
- errors=$(check_no_errors "$outfile")
209
- if [[ -z "$errors" ]]; then
210
- pass "Synthesis: no engine errors"
211
- else
212
- fail "Synthesis: $errors"
213
- fi
214
- else
215
- fail "Synthesis failed to run"
216
- fi
217
- fi
218
-
219
- # ─────────────────────────────────────────────────────────
220
- # Generate test report
221
- REPORT_FILE="$RESULTS_DIR/REPORT.md"
222
-
223
- cat > "$REPORT_FILE" << EOF
224
- # GreedySearch Test Report
225
-
226
- **Date:** $(date)
227
- **Test run:** $RESULTS_DIR
228
-
229
- ## Summary
230
-
231
- | Result | Count |
232
- |--------|-------|
233
- | ✅ Passed | $PASS |
234
- | ❌ Failed | $FAIL |
235
- | Total | $((PASS + FAIL)) |
236
-
237
- ## Failures
238
-
239
- EOF
240
-
241
- if [[ ${#FAILURES[@]} -eq 0 ]]; then
242
- echo "No failures — all tests passed! 🎉" >> "$REPORT_FILE"
243
- else
244
- for i in "${!FAILURES[@]}"; do
245
- echo "$((i+1)). ${FAILURES[$i]}" >> "$REPORT_FILE"
246
- done
247
-
248
- cat >> "$REPORT_FILE" << 'EOF'
249
-
250
- ## Common Issues
251
-
252
- ### Bing Copilot "copy button did not appear"
253
- This usually means:
254
- - **Verification challenge appeared** — Cloudflare Turnstile or Microsoft auth
255
- - **Page didn't load** — network issue or Copilot slow to respond
256
- - **UI changed** — selector no longer matches Copilot's DOM
257
-
258
- To debug: check the result JSON file for the full error message.
259
-
260
- ### Google "verification required"
261
- Google sometimes shows CAPTCHAs that can't be auto-solved.
262
- Manual intervention required in the Chrome window.
263
-
264
- ### Perplexity "Clipboard interceptor returned empty text"
265
- Perplexity's UI may have changed. Check if the copy button selector still works.
266
-
267
- EOF
268
- fi
269
-
270
- cat >> "$REPORT_FILE" << EOF
271
-
272
- ## Result Files
273
-
274
- \`\`\`
275
- $(ls -la "$RESULTS_DIR"/*.json 2>/dev/null | awk '{print $NF}' | xargs -I{} basename {})
276
- \`\`\`
277
-
278
- ---
279
- *Generated by test.sh*
280
- EOF
281
-
282
- echo -e "\n${YELLOW}═══ Results ═══${NC}"
283
- echo -e " ${GREEN}Passed: $PASS${NC}"
284
- [[ $FAIL -gt 0 ]] && echo -e " ${RED}Failed: $FAIL${NC}" || echo " Failed: 0"
285
- echo " Results in: $RESULTS_DIR"
286
- echo " Report: $REPORT_FILE"
287
- echo ""
288
-
289
- # Print failure details to console too
290
- if [[ ${#FAILURES[@]} -gt 0 ]]; then
291
- echo -e "${RED}Failures:${NC}"
292
- for f in "${FAILURES[@]}"; do
293
- echo -e " ${RED}•${NC} $f"
294
- done
295
- echo ""
296
- fi
297
-
298
- [[ $FAIL -eq 0 ]] && exit 0 || exit 1