@phuetz/code-buddy 0.1.0 → 0.1.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 (206) hide show
  1. package/.codebuddy/skills/bundled/brave-search/SKILL.md +490 -0
  2. package/.codebuddy/skills/bundled/exa-search/SKILL.md +1122 -0
  3. package/.codebuddy/skills/bundled/perplexity/SKILL.md +748 -0
  4. package/.codebuddy/skills/bundled/playwright/SKILL.md +520 -0
  5. package/.codebuddy/skills/bundled/puppeteer/SKILL.md +708 -0
  6. package/.codebuddy/skills/bundled/web-fetch/SKILL.md +1003 -0
  7. package/README.md +56 -0
  8. package/dist/agent/agent-state.d.ts +3 -3
  9. package/dist/agent/agent-state.js +6 -6
  10. package/dist/agent/agent-state.js.map +1 -1
  11. package/dist/agent/base-agent.d.ts +4 -4
  12. package/dist/agent/base-agent.js +22 -9
  13. package/dist/agent/base-agent.js.map +1 -1
  14. package/dist/agent/cache-trace.d.ts +56 -0
  15. package/dist/agent/cache-trace.js +98 -0
  16. package/dist/agent/cache-trace.js.map +1 -0
  17. package/dist/agent/codebuddy-agent.js +3 -2
  18. package/dist/agent/codebuddy-agent.js.map +1 -1
  19. package/dist/agent/execution/agent-executor.d.ts +4 -4
  20. package/dist/agent/execution/agent-executor.js +41 -7
  21. package/dist/agent/execution/agent-executor.js.map +1 -1
  22. package/dist/agent/facades/agent-context-facade.js +1 -3
  23. package/dist/agent/facades/agent-context-facade.js.map +1 -1
  24. package/dist/agent/facades/message-history-manager.js +14 -12
  25. package/dist/agent/facades/message-history-manager.js.map +1 -1
  26. package/dist/agent/facades/session-facade.d.ts +3 -3
  27. package/dist/agent/facades/session-facade.js +6 -6
  28. package/dist/agent/facades/session-facade.js.map +1 -1
  29. package/dist/agent/history-repair.d.ts +37 -0
  30. package/dist/agent/history-repair.js +124 -0
  31. package/dist/agent/history-repair.js.map +1 -0
  32. package/dist/agent/specialized/archive-agent.d.ts +3 -0
  33. package/dist/agent/specialized/archive-agent.js +71 -31
  34. package/dist/agent/specialized/archive-agent.js.map +1 -1
  35. package/dist/agent/specialized/security-review/agent.js +19 -8
  36. package/dist/agent/specialized/security-review/agent.js.map +1 -1
  37. package/dist/agent/tool-executor.js +5 -0
  38. package/dist/agent/tool-executor.js.map +1 -1
  39. package/dist/agent/turn-diff-tracker.d.ts +79 -0
  40. package/dist/agent/turn-diff-tracker.js +195 -0
  41. package/dist/agent/turn-diff-tracker.js.map +1 -0
  42. package/dist/checkpoints/checkpoint-versioning.js +78 -20
  43. package/dist/checkpoints/checkpoint-versioning.js.map +1 -1
  44. package/dist/cli/config-loader.js +2 -4
  45. package/dist/cli/config-loader.js.map +1 -1
  46. package/dist/commands/handlers/fcs-handlers.js +1 -1
  47. package/dist/commands/handlers/fcs-handlers.js.map +1 -1
  48. package/dist/commands/handlers/memory-handlers.js +2 -1
  49. package/dist/commands/handlers/memory-handlers.js.map +1 -1
  50. package/dist/commands/handlers/worktree-handlers.js +11 -0
  51. package/dist/commands/handlers/worktree-handlers.js.map +1 -1
  52. package/dist/commands/mcp.d.ts +1 -0
  53. package/dist/commands/mcp.js +66 -7
  54. package/dist/commands/mcp.js.map +1 -1
  55. package/dist/commands/pipeline.js +25 -13
  56. package/dist/commands/pipeline.js.map +1 -1
  57. package/dist/config/model-tools.d.ts +41 -0
  58. package/dist/config/model-tools.js +194 -0
  59. package/dist/config/model-tools.js.map +1 -0
  60. package/dist/context/context-manager-v2.d.ts +2 -1
  61. package/dist/context/context-manager-v2.js +34 -5
  62. package/dist/context/context-manager-v2.js.map +1 -1
  63. package/dist/daemon/daemon-manager.js +23 -19
  64. package/dist/daemon/daemon-manager.js.map +1 -1
  65. package/dist/database/database-manager.d.ts +4 -0
  66. package/dist/database/database-manager.js +16 -7
  67. package/dist/database/database-manager.js.map +1 -1
  68. package/dist/desktop-automation/nutjs-provider.js +89 -0
  69. package/dist/desktop-automation/nutjs-provider.js.map +1 -1
  70. package/dist/fcs/builtins.d.ts +2 -6
  71. package/dist/fcs/builtins.js +2 -568
  72. package/dist/fcs/builtins.js.map +1 -1
  73. package/dist/fcs/codebuddy-bindings.d.ts +3 -43
  74. package/dist/fcs/codebuddy-bindings.js +2 -606
  75. package/dist/fcs/codebuddy-bindings.js.map +1 -1
  76. package/dist/fcs/index.d.ts +2 -27
  77. package/dist/fcs/index.js +2 -53
  78. package/dist/fcs/index.js.map +1 -1
  79. package/dist/fcs/lexer.d.ts +2 -37
  80. package/dist/fcs/lexer.js +2 -459
  81. package/dist/fcs/lexer.js.map +1 -1
  82. package/dist/fcs/parser.d.ts +2 -68
  83. package/dist/fcs/parser.js +2 -893
  84. package/dist/fcs/parser.js.map +1 -1
  85. package/dist/fcs/runtime.d.ts +2 -59
  86. package/dist/fcs/runtime.js +2 -623
  87. package/dist/fcs/runtime.js.map +1 -1
  88. package/dist/fcs/script-registry.d.ts +3 -69
  89. package/dist/fcs/script-registry.js +2 -219
  90. package/dist/fcs/script-registry.js.map +1 -1
  91. package/dist/fcs/sync-bindings.d.ts +3 -101
  92. package/dist/fcs/sync-bindings.js +2 -410
  93. package/dist/fcs/sync-bindings.js.map +1 -1
  94. package/dist/fcs/types.d.ts +2 -285
  95. package/dist/fcs/types.js +2 -103
  96. package/dist/fcs/types.js.map +1 -1
  97. package/dist/hooks/use-input-handler.d.ts +1 -1
  98. package/dist/index.js +5 -2
  99. package/dist/index.js.map +1 -1
  100. package/dist/input/voice-control.js +11 -5
  101. package/dist/input/voice-control.js.map +1 -1
  102. package/dist/integrations/json-rpc/server.js +5 -5
  103. package/dist/integrations/json-rpc/server.js.map +1 -1
  104. package/dist/integrations/mcp/mcp-server.js +1 -1
  105. package/dist/integrations/mcp/mcp-server.js.map +1 -1
  106. package/dist/mcp/client.js +2 -1
  107. package/dist/mcp/client.js.map +1 -1
  108. package/dist/mcp/config.js +89 -5
  109. package/dist/mcp/config.js.map +1 -1
  110. package/dist/mcp/mcp-client.js +65 -14
  111. package/dist/mcp/mcp-client.js.map +1 -1
  112. package/dist/mcp/transports.d.ts +0 -1
  113. package/dist/mcp/transports.js +1 -5
  114. package/dist/mcp/transports.js.map +1 -1
  115. package/dist/mcp/types.d.ts +2 -0
  116. package/dist/persistence/session-lock.d.ts +42 -0
  117. package/dist/persistence/session-lock.js +165 -0
  118. package/dist/persistence/session-lock.js.map +1 -0
  119. package/dist/persistence/session-store.d.ts +18 -3
  120. package/dist/persistence/session-store.js +90 -21
  121. package/dist/persistence/session-store.js.map +1 -1
  122. package/dist/plugins/isolated-plugin-runner.d.ts +6 -0
  123. package/dist/plugins/isolated-plugin-runner.js +19 -1
  124. package/dist/plugins/isolated-plugin-runner.js.map +1 -1
  125. package/dist/providers/local-llm-provider.js +21 -4
  126. package/dist/providers/local-llm-provider.js.map +1 -1
  127. package/dist/sandbox/docker-sandbox.js +7 -4
  128. package/dist/sandbox/docker-sandbox.js.map +1 -1
  129. package/dist/scripting/builtins.d.ts +8 -3
  130. package/dist/scripting/builtins.js +506 -355
  131. package/dist/scripting/builtins.js.map +1 -1
  132. package/dist/scripting/codebuddy-bindings.d.ts +47 -0
  133. package/dist/scripting/codebuddy-bindings.js +487 -0
  134. package/dist/scripting/codebuddy-bindings.js.map +1 -0
  135. package/dist/scripting/index.d.ts +33 -30
  136. package/dist/scripting/index.js +41 -36
  137. package/dist/scripting/index.js.map +1 -1
  138. package/dist/scripting/lexer.d.ts +31 -13
  139. package/dist/scripting/lexer.js +379 -292
  140. package/dist/scripting/lexer.js.map +1 -1
  141. package/dist/scripting/parser.d.ts +63 -44
  142. package/dist/scripting/parser.js +700 -473
  143. package/dist/scripting/parser.js.map +1 -1
  144. package/dist/scripting/runtime.d.ts +55 -24
  145. package/dist/scripting/runtime.js +600 -288
  146. package/dist/scripting/runtime.js.map +1 -1
  147. package/dist/scripting/script-registry.d.ts +54 -0
  148. package/dist/scripting/script-registry.js +202 -0
  149. package/dist/scripting/script-registry.js.map +1 -0
  150. package/dist/scripting/sync-bindings.d.ts +105 -0
  151. package/dist/scripting/sync-bindings.js +353 -0
  152. package/dist/scripting/sync-bindings.js.map +1 -0
  153. package/dist/scripting/types.d.ts +297 -199
  154. package/dist/scripting/types.js +86 -60
  155. package/dist/scripting/types.js.map +1 -1
  156. package/dist/search/usearch-index.js +42 -7
  157. package/dist/search/usearch-index.js.map +1 -1
  158. package/dist/security/bash-parser.d.ts +51 -0
  159. package/dist/security/bash-parser.js +327 -0
  160. package/dist/security/bash-parser.js.map +1 -0
  161. package/dist/security/skill-scanner.d.ts +36 -0
  162. package/dist/security/skill-scanner.js +149 -0
  163. package/dist/security/skill-scanner.js.map +1 -0
  164. package/dist/security/trust-folders.d.ts +1 -0
  165. package/dist/security/trust-folders.js +19 -1
  166. package/dist/security/trust-folders.js.map +1 -1
  167. package/dist/server/websocket/handler.js +15 -5
  168. package/dist/server/websocket/handler.js.map +1 -1
  169. package/dist/skills/eligibility.js +26 -4
  170. package/dist/skills/eligibility.js.map +1 -1
  171. package/dist/tasks/background-tasks.js +5 -1
  172. package/dist/tasks/background-tasks.js.map +1 -1
  173. package/dist/tools/apply-patch.d.ts +55 -0
  174. package/dist/tools/apply-patch.js +273 -0
  175. package/dist/tools/apply-patch.js.map +1 -0
  176. package/dist/tools/registry/bash-tools.js +6 -3
  177. package/dist/tools/registry/bash-tools.js.map +1 -1
  178. package/dist/tools/registry/misc-tools.js +1 -2
  179. package/dist/tools/registry/misc-tools.js.map +1 -1
  180. package/dist/tools/registry/search-tools.js +1 -1
  181. package/dist/tools/registry/search-tools.js.map +1 -1
  182. package/dist/tools/registry/text-editor-tools.js +1 -1
  183. package/dist/tools/registry/text-editor-tools.js.map +1 -1
  184. package/dist/tools/registry/todo-tools.js +37 -5
  185. package/dist/tools/registry/todo-tools.js.map +1 -1
  186. package/dist/tools/registry/tool-registry.js +5 -4
  187. package/dist/tools/registry/tool-registry.js.map +1 -1
  188. package/dist/tools/registry/web-tools.d.ts +1 -1
  189. package/dist/tools/registry/web-tools.js +28 -8
  190. package/dist/tools/registry/web-tools.js.map +1 -1
  191. package/dist/tools/text-editor.d.ts +1 -1
  192. package/dist/tools/text-editor.js +23 -5
  193. package/dist/tools/text-editor.js.map +1 -1
  194. package/dist/tools/web-search.d.ts +52 -37
  195. package/dist/tools/web-search.js +368 -163
  196. package/dist/tools/web-search.js.map +1 -1
  197. package/dist/ui/components/ChatInterface.d.ts +1 -1
  198. package/dist/utils/head-tail-truncation.d.ts +34 -0
  199. package/dist/utils/head-tail-truncation.js +98 -0
  200. package/dist/utils/head-tail-truncation.js.map +1 -0
  201. package/dist/utils/sanitize.d.ts +5 -0
  202. package/dist/utils/sanitize.js +19 -0
  203. package/dist/utils/sanitize.js.map +1 -1
  204. package/dist/utils/settings-manager.js +4 -4
  205. package/dist/utils/settings-manager.js.map +1 -1
  206. package/package.json +3 -1
@@ -0,0 +1,490 @@
1
+ ---
2
+ name: brave-search
3
+ version: 1.0.0
4
+ description: Brave Search API for web search, local results, news, images, and videos
5
+ author: Code Buddy
6
+ tags: search, web, api, brave, news, images, local-search, real-time
7
+ env:
8
+ BRAVE_SEARCH_API_KEY: ""
9
+ ---
10
+
11
+ # Brave Search
12
+
13
+ Brave Search API provides privacy-focused web search with support for general web results, local business searches, news, images, and videos. Unlike other search APIs, Brave Search maintains user privacy by not tracking searches or building user profiles.
14
+
15
+ ## Direct Control (CLI / API / Scripting)
16
+
17
+ ### API Authentication
18
+
19
+ All requests require an API key via the `X-Subscription-Token` header. Get your key at https://api.search.brave.com/
20
+
21
+ ### Web Search
22
+
23
+ **cURL Example:**
24
+ ```bash
25
+ curl -s "https://api.search.brave.com/res/v1/web/search?q=artificial+intelligence+trends+2026&count=10" \
26
+ -H "Accept: application/json" \
27
+ -H "X-Subscription-Token: ${BRAVE_SEARCH_API_KEY}"
28
+ ```
29
+
30
+ **Node.js Example:**
31
+ ```javascript
32
+ import fetch from 'node-fetch';
33
+
34
+ async function braveWebSearch(query, options = {}) {
35
+ const params = new URLSearchParams({
36
+ q: query,
37
+ count: options.count || 10,
38
+ offset: options.offset || 0,
39
+ safesearch: options.safesearch || 'moderate',
40
+ freshness: options.freshness || '', // pd (past day), pw (past week), pm (past month), py (past year)
41
+ text_decorations: options.textDecorations !== false,
42
+ spellcheck: options.spellcheck !== false
43
+ });
44
+
45
+ const response = await fetch(`https://api.search.brave.com/res/v1/web/search?${params}`, {
46
+ headers: {
47
+ 'Accept': 'application/json',
48
+ 'X-Subscription-Token': process.env.BRAVE_SEARCH_API_KEY
49
+ }
50
+ });
51
+
52
+ if (!response.ok) {
53
+ throw new Error(`Brave Search API error: ${response.status}`);
54
+ }
55
+
56
+ return await response.json();
57
+ }
58
+
59
+ // Usage
60
+ const results = await braveWebSearch('TypeScript best practices', {
61
+ count: 20,
62
+ freshness: 'py'
63
+ });
64
+
65
+ results.web.results.forEach(result => {
66
+ console.log(`${result.title}\n${result.url}\n${result.description}\n`);
67
+ });
68
+ ```
69
+
70
+ **Python Example:**
71
+ ```python
72
+ import requests
73
+ import os
74
+
75
+ def brave_web_search(query, count=10, freshness=None):
76
+ params = {
77
+ 'q': query,
78
+ 'count': count,
79
+ 'safesearch': 'moderate',
80
+ 'text_decorations': True,
81
+ 'spellcheck': True
82
+ }
83
+
84
+ if freshness:
85
+ params['freshness'] = freshness
86
+
87
+ response = requests.get(
88
+ 'https://api.search.brave.com/res/v1/web/search',
89
+ params=params,
90
+ headers={
91
+ 'Accept': 'application/json',
92
+ 'X-Subscription-Token': os.environ['BRAVE_SEARCH_API_KEY']
93
+ }
94
+ )
95
+
96
+ response.raise_for_status()
97
+ return response.json()
98
+
99
+ # Usage
100
+ results = brave_web_search('machine learning tutorials', count=15, freshness='pm')
101
+
102
+ for result in results.get('web', {}).get('results', []):
103
+ print(f"{result['title']}")
104
+ print(f"{result['url']}")
105
+ print(f"{result['description']}\n")
106
+ ```
107
+
108
+ ### Local Search (Places/Businesses)
109
+
110
+ ```bash
111
+ # Search for restaurants near a location
112
+ curl -s "https://api.search.brave.com/res/v1/web/search?q=restaurants+in+San+Francisco&search_lang=en&result_filter=locations" \
113
+ -H "X-Subscription-Token: ${BRAVE_SEARCH_API_KEY}"
114
+ ```
115
+
116
+ **Node.js Local Search:**
117
+ ```javascript
118
+ async function braveLocalSearch(query, location) {
119
+ const params = new URLSearchParams({
120
+ q: `${query} in ${location}`,
121
+ result_filter: 'locations',
122
+ count: 20
123
+ });
124
+
125
+ const response = await fetch(`https://api.search.brave.com/res/v1/web/search?${params}`, {
126
+ headers: {
127
+ 'X-Subscription-Token': process.env.BRAVE_SEARCH_API_KEY
128
+ }
129
+ });
130
+
131
+ const data = await response.json();
132
+ return data.locations?.results || [];
133
+ }
134
+
135
+ // Usage
136
+ const places = await braveLocalSearch('coffee shops', 'Portland, OR');
137
+ places.forEach(place => {
138
+ console.log(`${place.title} - ${place.address}`);
139
+ console.log(`Rating: ${place.rating || 'N/A'} | ${place.phone || ''}`);
140
+ });
141
+ ```
142
+
143
+ ### News Search
144
+
145
+ ```bash
146
+ # Search recent news with freshness filter
147
+ curl -s "https://api.search.brave.com/res/v1/web/search?q=climate+change+policy&freshness=pd&result_filter=news" \
148
+ -H "X-Subscription-Token: ${BRAVE_SEARCH_API_KEY}"
149
+ ```
150
+
151
+ **Python News Search:**
152
+ ```python
153
+ def brave_news_search(query, freshness='pw'):
154
+ """Search news articles with freshness filter (pd/pw/pm/py)"""
155
+ params = {
156
+ 'q': query,
157
+ 'freshness': freshness,
158
+ 'result_filter': 'news',
159
+ 'count': 20
160
+ }
161
+
162
+ response = requests.get(
163
+ 'https://api.search.brave.com/res/v1/web/search',
164
+ params=params,
165
+ headers={'X-Subscription-Token': os.environ['BRAVE_SEARCH_API_KEY']}
166
+ )
167
+
168
+ return response.json().get('news', {}).get('results', [])
169
+
170
+ # Get today's news
171
+ articles = brave_news_search('artificial intelligence', freshness='pd')
172
+ for article in articles:
173
+ print(f"[{article.get('age', 'N/A')}] {article['title']}")
174
+ print(f"Source: {article.get('source', 'Unknown')}")
175
+ print(f"{article['url']}\n")
176
+ ```
177
+
178
+ ### Image Search
179
+
180
+ ```bash
181
+ curl -s "https://api.search.brave.com/res/v1/images/search?q=northern+lights+photography&count=20&safesearch=strict" \
182
+ -H "X-Subscription-Token: ${BRAVE_SEARCH_API_KEY}"
183
+ ```
184
+
185
+ **Node.js Image Search:**
186
+ ```javascript
187
+ async function braveImageSearch(query, options = {}) {
188
+ const params = new URLSearchParams({
189
+ q: query,
190
+ count: options.count || 20,
191
+ safesearch: options.safesearch || 'moderate',
192
+ spellcheck: true
193
+ });
194
+
195
+ const response = await fetch(`https://api.search.brave.com/res/v1/images/search?${params}`, {
196
+ headers: {
197
+ 'X-Subscription-Token': process.env.BRAVE_SEARCH_API_KEY
198
+ }
199
+ });
200
+
201
+ return await response.json();
202
+ }
203
+
204
+ // Usage
205
+ const images = await braveImageSearch('sustainable architecture', { count: 50 });
206
+ images.results.forEach(img => {
207
+ console.log(`${img.title}`);
208
+ console.log(`${img.url} (${img.properties.width}x${img.properties.height})`);
209
+ });
210
+ ```
211
+
212
+ ### Video Search
213
+
214
+ ```javascript
215
+ async function braveVideoSearch(query, options = {}) {
216
+ const params = new URLSearchParams({
217
+ q: query,
218
+ count: options.count || 20,
219
+ safesearch: options.safesearch || 'moderate'
220
+ });
221
+
222
+ const response = await fetch(`https://api.search.brave.com/res/v1/videos/search?${params}`, {
223
+ headers: {
224
+ 'X-Subscription-Token': process.env.BRAVE_SEARCH_API_KEY
225
+ }
226
+ });
227
+
228
+ return await response.json();
229
+ }
230
+
231
+ // Usage
232
+ const videos = await braveVideoSearch('TypeScript tutorials');
233
+ videos.results.forEach(video => {
234
+ console.log(`${video.title} - ${video.duration || 'N/A'}`);
235
+ console.log(`${video.url}\n`);
236
+ });
237
+ ```
238
+
239
+ ## MCP Server Integration
240
+
241
+ Add to `.codebuddy/mcp.json`:
242
+
243
+ ```json
244
+ {
245
+ "mcpServers": {
246
+ "brave-search": {
247
+ "command": "npx",
248
+ "args": [
249
+ "-y",
250
+ "@modelcontextprotocol/server-brave-search"
251
+ ],
252
+ "env": {
253
+ "BRAVE_API_KEY": "${BRAVE_SEARCH_API_KEY}"
254
+ }
255
+ }
256
+ }
257
+ }
258
+ ```
259
+
260
+ ### Available MCP Tools
261
+
262
+ Once configured, the following tools become available:
263
+
264
+ 1. **brave_web_search**
265
+ - Search the web with Brave Search
266
+ - Parameters: `query` (string), `count` (number, default 10)
267
+ - Returns web results with titles, URLs, descriptions
268
+
269
+ 2. **brave_local_search**
270
+ - Search for local businesses and places
271
+ - Parameters: `query` (string), `count` (number)
272
+ - Returns location results with addresses, ratings, phone numbers
273
+
274
+ Example MCP usage:
275
+ ```
276
+ Ask Code Buddy: "Use brave_web_search to find the latest React 19 documentation"
277
+ Ask Code Buddy: "Find coffee shops near downtown Seattle using brave_local_search"
278
+ ```
279
+
280
+ ## Common Workflows
281
+
282
+ ### 1. Research Topic with Recent Results
283
+
284
+ **Goal:** Research a technical topic and get only recent, relevant articles
285
+
286
+ ```bash
287
+ # Step 1: Search with freshness filter for past month
288
+ curl -s "https://api.search.brave.com/res/v1/web/search?q=Rust+async+programming+best+practices&freshness=pm&count=20" \
289
+ -H "X-Subscription-Token: ${BRAVE_SEARCH_API_KEY}" | jq '.web.results[] | {title, url, description, age}'
290
+
291
+ # Step 2: Filter by domain if needed (in Node.js)
292
+ const results = await braveWebSearch('Rust async programming best practices', {
293
+ freshness: 'pm',
294
+ count: 20
295
+ });
296
+
297
+ const officialDocs = results.web.results.filter(r =>
298
+ r.url.includes('doc.rust-lang.org') ||
299
+ r.url.includes('rust-lang.github.io')
300
+ );
301
+
302
+ # Step 3: Extract URLs for further processing
303
+ const urls = officialDocs.map(r => r.url);
304
+
305
+ # Step 4: Use Code Buddy to fetch and summarize
306
+ // Pass URLs to web_fetch or ask Code Buddy to analyze content
307
+ ```
308
+
309
+ ### 2. Monitor News on Specific Topic
310
+
311
+ **Goal:** Track breaking news and recent developments
312
+
313
+ ```python
314
+ import time
315
+ from datetime import datetime
316
+
317
+ def monitor_news_topic(query, check_interval=3600):
318
+ """Check for new articles every hour"""
319
+ seen_urls = set()
320
+
321
+ while True:
322
+ articles = brave_news_search(query, freshness='pd')
323
+ new_articles = [a for a in articles if a['url'] not in seen_urls]
324
+
325
+ if new_articles:
326
+ print(f"\n[{datetime.now()}] Found {len(new_articles)} new articles:")
327
+ for article in new_articles:
328
+ print(f" - {article['title']}")
329
+ print(f" {article['url']}")
330
+ seen_urls.add(article['url'])
331
+
332
+ time.sleep(check_interval)
333
+
334
+ # Monitor AI policy news
335
+ monitor_news_topic('AI regulation policy', check_interval=3600)
336
+ ```
337
+
338
+ ### 3. Find Local Businesses with Filtering
339
+
340
+ **Goal:** Search for businesses matching specific criteria
341
+
342
+ ```javascript
343
+ async function findTopRatedPlaces(type, location, minRating = 4.0) {
344
+ // Step 1: Search for places
345
+ const places = await braveLocalSearch(type, location);
346
+
347
+ // Step 2: Filter by rating
348
+ const topRated = places.filter(p =>
349
+ p.rating && parseFloat(p.rating) >= minRating
350
+ );
351
+
352
+ // Step 3: Sort by rating (descending)
353
+ topRated.sort((a, b) => parseFloat(b.rating || 0) - parseFloat(a.rating || 0));
354
+
355
+ // Step 4: Return formatted results
356
+ return topRated.map(place => ({
357
+ name: place.title,
358
+ address: place.address,
359
+ rating: place.rating,
360
+ phone: place.phone,
361
+ url: place.url
362
+ }));
363
+ }
364
+
365
+ // Usage
366
+ const restaurants = await findTopRatedPlaces('Italian restaurants', 'Boston, MA', 4.5);
367
+ console.log(JSON.stringify(restaurants, null, 2));
368
+ ```
369
+
370
+ ### 4. Comparative Search Analysis
371
+
372
+ **Goal:** Compare search results across different time periods
373
+
374
+ ```javascript
375
+ async function compareSearchTrends(query) {
376
+ // Step 1: Get recent results (past day)
377
+ const recent = await braveWebSearch(query, { freshness: 'pd', count: 10 });
378
+
379
+ // Step 2: Get older results (past year)
380
+ const older = await braveWebSearch(query, { freshness: 'py', count: 10 });
381
+
382
+ // Step 3: Analyze domain distribution
383
+ const getDomains = (results) => {
384
+ return results.web.results.map(r => new URL(r.url).hostname);
385
+ };
386
+
387
+ const recentDomains = getDomains(recent);
388
+ const olderDomains = getDomains(older);
389
+
390
+ // Step 4: Identify trending sources
391
+ const newSources = recentDomains.filter(d => !olderDomains.includes(d));
392
+
393
+ console.log('New sources in past day:', newSources);
394
+
395
+ // Step 5: Extract common themes from titles
396
+ const recentTitles = recent.web.results.map(r => r.title).join(' ');
397
+ console.log('\nRecent focus:', recentTitles);
398
+
399
+ return {
400
+ recent: recent.web.results,
401
+ older: older.web.results,
402
+ newSources
403
+ };
404
+ }
405
+
406
+ // Track how coverage of a topic evolves
407
+ await compareSearchTrends('quantum computing breakthroughs');
408
+ ```
409
+
410
+ ### 5. Multi-Modal Content Discovery
411
+
412
+ **Goal:** Find related content across web, images, videos, and news
413
+
414
+ ```javascript
415
+ async function comprehensiveSearch(topic) {
416
+ console.log(`Searching for: ${topic}\n`);
417
+
418
+ // Step 1: Web search for overview
419
+ const webResults = await braveWebSearch(topic, { count: 5 });
420
+ console.log('Top Web Results:');
421
+ webResults.web.results.slice(0, 3).forEach(r => {
422
+ console.log(` ${r.title} - ${r.url}`);
423
+ });
424
+
425
+ // Step 2: Recent news articles
426
+ const news = await braveNewsSearch(topic, { freshness: 'pw' });
427
+ console.log('\nRecent News:');
428
+ news.slice(0, 3).forEach(article => {
429
+ console.log(` [${article.age}] ${article.title}`);
430
+ });
431
+
432
+ // Step 3: Find relevant images
433
+ const images = await braveImageSearch(topic, { count: 10 });
434
+ console.log('\nTop Images:');
435
+ images.results.slice(0, 3).forEach(img => {
436
+ console.log(` ${img.title} - ${img.url}`);
437
+ });
438
+
439
+ // Step 4: Video content
440
+ const videos = await braveVideoSearch(topic, { count: 10 });
441
+ console.log('\nVideo Resources:');
442
+ videos.results.slice(0, 3).forEach(video => {
443
+ console.log(` ${video.title} - ${video.url}`);
444
+ });
445
+
446
+ // Step 5: Compile report
447
+ return {
448
+ overview: webResults.web.results[0]?.description,
449
+ topLinks: webResults.web.results.slice(0, 5).map(r => r.url),
450
+ newsCount: news.length,
451
+ imageCount: images.results.length,
452
+ videoCount: videos.results.length
453
+ };
454
+ }
455
+
456
+ // Comprehensive research on a topic
457
+ const report = await comprehensiveSearch('sustainable energy solutions 2026');
458
+ console.log('\nResearch Summary:', report);
459
+ ```
460
+
461
+ ## Best Practices
462
+
463
+ 1. **Use Freshness Filters:** For time-sensitive queries, always specify `freshness` parameter (pd/pw/pm/py)
464
+ 2. **Result Filtering:** Use `result_filter` to get specific content types (news, locations, videos)
465
+ 3. **Rate Limiting:** Respect API rate limits (typically 1 req/sec for free tier, check your plan)
466
+ 4. **Safe Search:** Set appropriate `safesearch` level (off/moderate/strict) based on use case
467
+ 5. **Pagination:** Use `offset` parameter for paginated results (offset = page * count)
468
+ 6. **Spellcheck:** Keep `spellcheck=true` to handle typos in queries
469
+ 7. **Privacy:** Brave doesn't track searches, but still avoid including PII in queries
470
+
471
+ ## Troubleshooting
472
+
473
+ **API Key Issues:**
474
+ ```bash
475
+ # Test API key validity
476
+ curl -I "https://api.search.brave.com/res/v1/web/search?q=test" \
477
+ -H "X-Subscription-Token: ${BRAVE_SEARCH_API_KEY}"
478
+
479
+ # Should return 200 OK if valid, 401 if invalid
480
+ ```
481
+
482
+ **No Results:**
483
+ - Check query spelling and try with `spellcheck=true`
484
+ - Remove restrictive filters (freshness, result_filter)
485
+ - Try broader search terms
486
+
487
+ **Rate Limit Errors (429):**
488
+ - Add delays between requests (min 1 second)
489
+ - Upgrade to higher tier plan
490
+ - Implement exponential backoff retry logic