@1a35e1/sonar-cli 0.2.1 → 0.3.4

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 (43) hide show
  1. package/README.md +151 -166
  2. package/dist/commands/{inbox/archive.js → archive.js} +2 -2
  3. package/dist/commands/config/data/download.js +2 -2
  4. package/dist/commands/config/data/sync.js +2 -2
  5. package/dist/commands/config/nuke.js +20 -2
  6. package/dist/commands/feed.js +105 -155
  7. package/dist/commands/index.js +172 -4
  8. package/dist/commands/{inbox/later.js → later.js} +2 -2
  9. package/dist/commands/refresh.js +41 -0
  10. package/dist/commands/{inbox/skip.js → skip.js} +2 -2
  11. package/dist/commands/status.js +128 -0
  12. package/dist/commands/sync/bookmarks.js +35 -0
  13. package/dist/commands/topics/add.js +71 -0
  14. package/dist/commands/topics/delete.js +42 -0
  15. package/dist/commands/topics/edit.js +97 -0
  16. package/dist/commands/topics/index.js +54 -0
  17. package/dist/commands/topics/suggest.js +125 -0
  18. package/dist/commands/topics/view.js +48 -0
  19. package/dist/components/AccountCard.js +1 -1
  20. package/dist/components/Banner.js +11 -0
  21. package/dist/components/InteractiveSession.js +95 -210
  22. package/dist/components/Spinner.js +5 -4
  23. package/dist/components/TopicCard.js +15 -0
  24. package/dist/components/TweetCard.js +76 -0
  25. package/dist/lib/ai.js +85 -0
  26. package/dist/lib/client.js +66 -40
  27. package/dist/lib/config.js +3 -2
  28. package/dist/lib/data-queries.js +1 -3
  29. package/dist/lib/skill.js +66 -226
  30. package/package.json +13 -3
  31. package/dist/commands/account.js +0 -75
  32. package/dist/commands/inbox/index.js +0 -103
  33. package/dist/commands/inbox/read.js +0 -41
  34. package/dist/commands/ingest/bookmarks.js +0 -55
  35. package/dist/commands/ingest/index.js +0 -5
  36. package/dist/commands/ingest/tweets.js +0 -55
  37. package/dist/commands/interests/create.js +0 -107
  38. package/dist/commands/interests/index.js +0 -56
  39. package/dist/commands/interests/match.js +0 -33
  40. package/dist/commands/interests/update.js +0 -153
  41. package/dist/commands/monitor.js +0 -93
  42. package/dist/commands/quickstart.js +0 -231
  43. package/dist/components/InterestCard.js +0 -10
package/README.md CHANGED
@@ -33,21 +33,19 @@ export SONAR_API_KEY=snr_xxxxx
33
33
  sonar config setup key=<YOUR_API_KEY>
34
34
  ```
35
35
 
36
- View your account to ensure evrything works.
36
+ View your account status:
37
37
 
38
38
  ```sh
39
- sonar account
39
+ sonar status
40
40
  ```
41
41
 
42
- Ingest your first `tweets` and check to `monitor` progress.
42
+ Run your first refresh to index tweets and generate suggestions:
43
43
 
44
- > The first time this you run this command it will take some time.
44
+ > The first time you run this it will take some time.
45
45
 
46
46
  ```sh
47
- sonar ingest tweets
48
-
49
- sonar monitor
50
- sonar monitor --watch
47
+ sonar refresh
48
+ sonar status --watch
51
49
  ```
52
50
 
53
51
  ---
@@ -72,12 +70,10 @@ Setting up your own social data pipeline is genuinely awful. You're looking at O
72
70
 
73
71
  **Sonar skips all of that. Get actionalable data for OpenClaw in 15 minutes.**
74
72
 
75
- We believe your data is yours. So you want to go deeper than our platform allows — build your own models, run custom queries, pipe it into your own tooling — you can download everything we have indexed on your behalf into a local SQLite database and do whatever you want with it:
73
+ We believe your data is yours. If you want to go deeper than our platform allows — build your own models, run custom queries, pipe it into your own tooling — you can sync everything we have indexed on your behalf into a local SQLite database:
76
74
 
77
75
  ```bash
78
- pnpm run cli -- data download # full snapshot → ~/.sonar/data.db
79
- pnpm run cli -- data sync # incremental updates
80
- pnpm run cli -- data sql # drop into a sqlite3 shell
76
+ sonar sync # sync data to ~/.sonar/data.db
81
77
  ```
82
78
 
83
79
  No lock-in. If you outgrow us, you leave with your data intact.
@@ -110,91 +106,86 @@ This is what API-first looks like in the agentic era: strong contracts at the se
110
106
  Pull everything relevant that happened while you slept:
111
107
 
112
108
  ```bash
113
- pnpm run cli -- feed --hours 8 --render card
114
- pnpm run cli -- inbox --status inbox
109
+ sonar feed --hours 8
115
110
  ```
116
111
 
117
- ### Track a topic you care about — right now
112
+ ### Stream your feed in real time
118
113
 
119
- Create a new interest from a plain English prompt and get content immediately:
114
+ Watch for new items as they appear:
120
115
 
121
116
  ```bash
122
- pnpm run cli -- interests create \
123
- --from-prompt "I want to follow AI evals and agent infrastructure"
124
-
125
- pnpm run cli -- index suggestions --days 1
126
- pnpm run cli -- feed --hours 24
117
+ sonar feed --follow # visual cards, polls every 30s
118
+ sonar feed --follow --json | jq .score # NDJSON stream for agents
127
119
  ```
128
120
 
129
- Sonar generates keywords and topics from your prompt, kicks off indexing, and your feed updates with relevant posts.
121
+ ### Discover new topics with AI
130
122
 
131
- ### Build a scriptable news digest
132
-
133
- Combine `--json` output with `jq` to pipe Sonar content wherever you want:
123
+ Let Sonar suggest topics based on your interests and feed:
134
124
 
135
125
  ```bash
136
- # Get today's top feed items as JSON
137
- pnpm run cli -- feed --hours 24 --json | jq '.[] | {author, text, url}'
138
-
139
- # Summarize your inbox with an LLM
140
- pnpm run cli -- inbox --json | jq '.[].text' | your-summarizer-script
126
+ sonar topics suggest # interactive accept/reject
127
+ sonar topics suggest --count 3 # just 3 suggestions
141
128
  ```
142
129
 
143
- ### Keep your local data fresh and queryable
130
+ ### Track a topic you care about
144
131
 
145
- Download a full SQLite snapshot of your Sonar data and query it directly:
132
+ Add a topic, then refresh:
146
133
 
147
134
  ```bash
148
- pnpm run cli -- data download
149
- pnpm run cli -- data sql
150
- # Now you have a full sqlite3 shell — write any query you want
135
+ sonar topics add "AI agents"
136
+ sonar refresh
137
+ sonar feed --hours 24
151
138
  ```
152
139
 
153
- Run incremental syncs on a cron to keep it current:
140
+ Sonar rebuilds your social graph, indexes recent tweets, and generates suggestions matched against your topics and interest profile.
141
+
142
+ ### Build a scriptable news digest
143
+
144
+ Combine `--json` output with `jq` to pipe Sonar content wherever you want:
154
145
 
155
146
  ```bash
156
- # crontab: sync every 30 minutes
157
- */30 * * * * cd /your/project && pnpm run cli -- data sync
147
+ # Get today's feed as JSON
148
+ sonar feed --hours 24 --json | jq '.[] | {author: .tweet.user.username, text: .tweet.text}'
149
+
150
+ # Summarize with an LLM
151
+ sonar feed --json | jq '.[].tweet.text' | your-summarizer-script
152
+
153
+ # Stream high-score items to a file
154
+ sonar feed --follow --json | jq --unbuffered 'select(.score > 0.7)' >> highlights.jsonl
158
155
  ```
159
156
 
160
- ### Interactive triage
157
+ ### Monitor the pipeline
161
158
 
162
- Work through your inbox without leaving the terminal:
159
+ Watch the queue in real time while refresh runs:
163
160
 
164
161
  ```bash
165
- pnpm run cli -- inbox --interactive
166
- pnpm run cli -- feed --interactive
162
+ sonar refresh
163
+ sonar status --watch
167
164
  ```
168
165
 
169
- Mark suggestions as read, skip, archive, or save for later — keyboard-driven.
170
-
171
- ### Monitor indexing jobs
166
+ ### Interactive triage
172
167
 
173
- Watch the queue in real time while you trigger a full re-index:
168
+ Work through suggestions without leaving the terminal:
174
169
 
175
170
  ```bash
176
- pnpm run cli -- index # trigger all jobs
177
- pnpm run cli -- index status --watch # watch until complete
171
+ sonar # interactive triage is on by default
172
+ sonar --no-interactive # disable for scripting
178
173
  ```
179
174
 
180
- ---
181
-
182
- ## What Sonar doesn't do
175
+ Mark suggestions as skip, later, or archive — keyboard-driven.
183
176
 
184
- Sonar is **not a global search engine**. It won't crawl the entire internet or index trending posts from people you've never heard of.
177
+ ---
185
178
 
186
- Instead, it searches within your social graph — your followers and the people you follow — up to **2 degrees of separation**. That's it. This is an intentional constraint, not a limitation we're working around.
179
+ ## How Sonar finds signal
187
180
 
188
- The reason is practical: API rate limits make broad crawling impossible at any useful refresh frequency. But the reason it works is more interesting **the people in your network are already a curated signal layer**. The accounts you follow, and the accounts they follow, are a surprisingly high-quality filter for what's relevant to your domain. Sonar's job is to surface what's moving through that graph before it reaches mainstream feeds.
181
+ Sonar surfaces relevant content from your X social graph the people you follow and who follow you. Your network is already a curated signal layer. Sonar's job is to surface what's moving through that graph before it reaches mainstream feeds.
189
182
 
190
183
  What this means in practice:
191
184
 
192
185
  * Results reflect your network's attention, not global virality
193
- * You won't see noise from accounts you have no connection to
194
186
  * The feed gets more useful the more intentional you are about who you follow
195
- * Adding interests with specific keywords and topics sharpens what Sonar surfaces *within* that graph
196
-
197
- If you want global trend monitoring, tools like Brandwatch or Twitter's native search are better fits. Sonar is for developers who want a focused, low-noise signal from a network they've already curated.
187
+ * Bookmarking and liking content improves your recommendations over time
188
+ * Topics sharpen what Sonar surfaces within your graph
198
189
 
199
190
  ---
200
191
 
@@ -206,71 +197,54 @@ Sonar + OpenClaw is a natural stack: **Sonar handles the signal filtering and cu
206
197
 
207
198
  ### Morning briefing delivered to your phone
208
199
 
209
- Set up a cron job in OpenClaw to run your Sonar digest and pipe it back to you on Telegram every morning:
200
+ Set up a cron job in OpenClaw to run your Sonar digest every morning:
210
201
 
211
202
  ```
212
203
  # In OpenClaw: schedule a daily 8am briefing
213
- "Every morning at 8am, run `sonar feed --hours 8 --json` and summarize the top 5 posts for me"
204
+ "Every morning at 8am, run `sonar --hours 8 --json` and summarize the top 5 posts for me"
214
205
  ```
215
206
 
216
- OpenClaw will execute the CLI, pass the JSON output to your LLM, and send a clean summary straight to your phone — no dashboard to open.
207
+ OpenClaw will execute the CLI, pass the JSON output to your LLM, and send a clean summary straight to your phone.
217
208
 
218
209
  ### Ask your feed questions in natural language
219
210
 
220
- Because `--json` makes Sonar output composable, OpenClaw can reason over it conversationally:
211
+ Because `--json` makes Sonar output composable, OpenClaw can reason over it:
221
212
 
222
213
  ```
223
214
  # Example prompts you can send OpenClaw via WhatsApp:
224
215
  "What's the most discussed topic in my Sonar feed today?"
225
216
  "Did anyone in my feed mention Uniswap V4 in the last 48 hours?"
226
- "Summarize my unread Sonar inbox"
227
- ```
228
-
229
- Wire it up once as an OpenClaw skill and your feed becomes queryable from any messaging app.
230
-
231
- ### Triage your inbox hands-free
232
-
233
- Combine OpenClaw's scheduling with Sonar's inbox API to automatically mark low-signal suggestions:
234
-
235
- ```bash
236
- # Shell script you can hand to OpenClaw as a scheduled skill
237
- sonar inbox --json | \
238
- jq '[.[] | select(.score < 0.4) | .id]' | \
239
- xargs -I{} sonar inbox skip {}
217
+ "Summarize my Sonar suggestions"
240
218
  ```
241
219
 
242
- Run this nightly and your inbox stays clean without manual triage.
243
-
244
220
  ### Get alerted when a topic spikes
245
221
 
246
- Use OpenClaw's Heartbeat (scheduled wake-up) to watch for signal surges and notify you:
222
+ Use OpenClaw's Heartbeat to watch for signal surges:
247
223
 
248
224
  ```
249
225
  # OpenClaw cron: check every 2 hours
250
- "Run `sonar feed --hours 2 --json` — if there are more than 10 posts about
226
+ "Run `sonar --hours 2 --json` — if there are more than 10 posts about
251
227
  'token launchpad' or 'LVR', send me a Telegram alert with the highlights"
252
228
  ```
253
229
 
254
- Effectively a custom Google Alert, but filtered through your actual interest graph.
255
-
256
230
  ### Build a Sonar skill for OpenClaw
257
231
 
258
- The cleanest integration is wrapping Sonar as a reusable OpenClaw skill. Drop a skill file in your OpenClaw workspace:
232
+ Wrap Sonar as a reusable OpenClaw skill:
259
233
 
260
234
  ```typescript
261
235
  // skills/sonar.ts
262
- export async function getFeed(hours = 12) {
263
- const { stdout } = await exec(`sonar feed --hours ${hours} --json`);
236
+ export async function getSuggestions(hours = 12) {
237
+ const { stdout } = await exec(`sonar --hours ${hours} --json`);
264
238
  return JSON.parse(stdout);
265
239
  }
266
240
 
267
- export async function getInbox() {
268
- const { stdout } = await exec(`sonar inbox --json`);
241
+ export async function getStatus() {
242
+ const { stdout } = await exec(`sonar status --json`);
269
243
  return JSON.parse(stdout);
270
244
  }
271
245
  ```
272
246
 
273
- Once registered, OpenClaw can call these tools autonomously whenever it decides they're relevant — no manual prompting required.
247
+ Once registered, OpenClaw can call these tools autonomously whenever it decides they're relevant.
274
248
 
275
249
  ---
276
250
 
@@ -280,111 +254,112 @@ Once registered, OpenClaw can call these tools autonomously whenever it decides
280
254
 
281
255
  * Node.js 20+
282
256
  * `pnpm`
283
- * A Sonar API key from [sonar.sh/account](https://sonar.sh/account?tab=api-keys)
284
- * Optional: `sqlite3` CLI (only needed for `data sql`)
257
+ * A Sonar API key from [sonar.8640p.info](https://sonar.8640p.info/)
285
258
 
286
259
  ### Install and authenticate
287
260
 
288
261
  ```bash
289
- pnpm install
262
+ pnpm add -g @1a35e1/sonar-cli@latest
290
263
 
291
264
  export SONAR_API_KEY="your_api_key_here"
292
- pnpm run cli -- init
265
+ sonar config setup key=<YOUR_API_KEY>
293
266
  ```
294
267
 
295
- `init` writes your config to `~/.sonar/config.json`. If `SONAR_API_KEY` is set in your environment, it always takes precedence.
296
-
297
268
  Verify it works:
298
269
 
299
270
  ```bash
300
- pnpm run cli -- account
301
- pnpm run cli -- interests
271
+ sonar status
272
+ sonar topics
302
273
  ```
303
274
 
304
275
  ---
305
276
 
306
277
  ## Command Reference
307
278
 
308
- ### Account & Config
279
+ ### Default triage suggestions
280
+
281
+ ```bash
282
+ sonar # interactive triage (default)
283
+ sonar --hours 24 # widen time window
284
+ sonar --days 3 # last 3 days
285
+ sonar --kind bookmarks # default | bookmarks | followers | following
286
+ sonar --render table --limit 50 # table layout
287
+ sonar --json # raw JSON output
288
+ sonar --no-interactive # disable interactive mode
289
+ ```
290
+
291
+ ### Feed — read-only view
292
+
293
+ ```bash
294
+ sonar feed # read-only feed (last 12h, limit 20)
295
+ sonar feed --hours 48 --limit 50 # widen window
296
+ sonar feed --kind bookmarks # bookmarks | followers | following
297
+ sonar feed --render table # table layout
298
+ sonar feed --json | jq . # pipe to jq
299
+ ```
300
+
301
+ #### Streaming with --follow
302
+
303
+ Poll for new items continuously and stream them to your terminal or another process:
309
304
 
310
305
  ```bash
311
- pnpm run cli -- account # plan, usage, suggestion counters
312
- pnpm run cli -- config # show current config
313
- pnpm run cli -- config set vendor anthropic # or openai
314
- pnpm run cli -- config set feed-render card # or table
315
- pnpm run cli -- config set feed-width 100
306
+ sonar feed --follow # poll every 30s, visual cards
307
+ sonar feed --follow --interval 10 # poll every 10s
308
+ sonar feed --follow --json # NDJSON stream (one JSON per line)
309
+ sonar feed --follow --json | jq --unbuffered '.score'
316
310
  ```
317
311
 
318
- ### Interests
312
+ Press `q` to quit follow mode.
313
+
314
+ ### Topics
319
315
 
320
316
  ```bash
321
- pnpm run cli -- interests # list all
322
- pnpm run cli -- interests --json # JSON output
323
-
324
- # Create manually
325
- pnpm run cli -- interests create \
326
- --name "Rust Systems" \
327
- --description "Rust, compilers, and systems tooling" \
328
- --keywords "rust,cargo,wasm" \
329
- --topics "systems programming,performance"
330
-
331
- # Create from a natural language prompt (requires OPENAI_API_KEY or ANTHROPIC_API_KEY)
332
- pnpm run cli -- interests create \
333
- --from-prompt "I want to follow AI evals and agent infra"
334
-
335
- # Update
336
- pnpm run cli -- interests update --id <id> --name "New Name"
337
- pnpm run cli -- interests update --id <id> --add-keywords "mcp,langgraph"
338
- pnpm run cli -- interests update --id <id> --remove-topics "old-topic"
317
+ sonar topics # list all topics
318
+ sonar topics --json # JSON output
319
+ sonar topics add "AI agents" # add a topic
320
+ sonar topics edit --id <id> --name "New Name"
339
321
  ```
340
322
 
341
- ### Feed
323
+ #### AI-powered topic suggestions
324
+
325
+ Let Sonar suggest new topics based on your existing interests and recent feed:
342
326
 
343
327
  ```bash
344
- pnpm run cli -- feed # last 12h, limit 20, card render
345
- pnpm run cli -- feed --hours 24
346
- pnpm run cli -- feed --days 3
347
- pnpm run cli -- feed --kind bookmarks # default | bookmarks | followers | following
348
- pnpm run cli -- feed --render table --limit 50
349
- pnpm run cli -- feed --interactive
350
- pnpm run cli -- feed --json
328
+ sonar topics suggest # interactive y/n/q per suggestion
329
+ sonar topics suggest --count 3 # limit to 3 suggestions
330
+ sonar topics suggest --vendor anthropic # use Anthropic instead of OpenAI
331
+ sonar topics suggest --json # raw suggestions as JSON
351
332
  ```
352
333
 
353
- ### Inbox
334
+ Requires `OPENAI_API_KEY` or `ANTHROPIC_API_KEY` depending on vendor.
335
+
336
+ ### Pipeline
354
337
 
355
338
  ```bash
356
- pnpm run cli -- inbox # list inbox suggestions
357
- pnpm run cli -- inbox --all
358
- pnpm run cli -- inbox --status inbox --limit 50
359
- pnpm run cli -- inbox --interactive
360
- pnpm run cli -- inbox --json
361
-
362
- pnpm run cli -- inbox read --id <suggestion_id>
363
- pnpm run cli -- inbox skip --id <suggestion_id>
364
- pnpm run cli -- inbox later --id <suggestion_id>
365
- pnpm run cli -- inbox archive --id <suggestion_id>
339
+ sonar refresh # full pipeline: graph tweets suggestions
340
+ sonar status # account status, queue activity
341
+ sonar status --watch # poll every 2s
366
342
  ```
367
343
 
368
- ### Indexing
344
+ ### Triage
369
345
 
370
346
  ```bash
371
- pnpm run cli -- reindex # run all jobs
372
- pnpm run cli -- reindex tweets
373
- pnpm run cli -- reindex graph
374
- pnpm run cli -- reindex graph --force
375
- pnpm run cli -- reindex suggestions --days 1
376
- pnpm run cli -- reindex bookmarks
377
- pnpm run cli -- reindex status
378
- pnpm run cli -- reindex status --watch
347
+ sonar skip --id <suggestion_id> # skip a suggestion
348
+ sonar later --id <suggestion_id> # save for later
349
+ sonar archive # archive old suggestions
350
+ ```
351
+
352
+ ### Config
353
+
354
+ ```bash
355
+ sonar config # show current config
356
+ sonar config setup key=<API_KEY> # set API key
379
357
  ```
380
358
 
381
359
  ### Local Data
382
360
 
383
361
  ```bash
384
- pnpm run cli -- data download # full download → ~/.sonar/data.db
385
- pnpm run cli -- data sync # incremental sync
386
- pnpm run cli -- data path # print DB path
387
- pnpm run cli -- data sql # open sqlite3 shell
362
+ sonar sync # sync data to local SQLite
388
363
  ```
389
364
 
390
365
  ---
@@ -393,13 +368,11 @@ pnpm run cli -- data sql # open sqlite3 shell
393
368
 
394
369
  | Variable | Required | Purpose |
395
370
  |---|---|---|
396
- | `SONAR_API_KEY` | Yes (unless saved by `init`) | Auth token |
397
- | `SONAR_API_URL` | No | GraphQL endpoint (default: `http://localhost:8000/graphql`) |
398
- | `SONAR_AI_VENDOR` | No | AI vendor for prompt generation (`openai` or `anthropic`) |
399
- | `SONAR_FEED_RENDER` | No | Default render style (`card` or `table`) |
400
- | `SONAR_FEED_WIDTH` | No | Default card width |
401
- | `OPENAI_API_KEY` | Sometimes | Required for OpenAI-powered `--from-prompt` |
402
- | `ANTHROPIC_API_KEY` | Sometimes | Required for Anthropic-powered `--from-prompt` |
371
+ | `SONAR_API_KEY` | Yes | Auth token from [sonar.8640p.info](https://sonar.8640p.info/) |
372
+ | `SONAR_API_URL` | No | GraphQL endpoint (default: production API) |
373
+ | `SONAR_MAX_RETRIES` | No | Max retry attempts on transient failures (default: 3, 0 to disable) |
374
+ | `OPENAI_API_KEY` | For `topics suggest` | Required when using OpenAI vendor for AI suggestions |
375
+ | `ANTHROPIC_API_KEY` | For `topics suggest` | Required when using Anthropic vendor for AI suggestions |
403
376
 
404
377
  ## Local Files
405
378
 
@@ -410,13 +383,25 @@ pnpm run cli -- data sql # open sqlite3 shell
410
383
 
411
384
  ---
412
385
 
386
+ ## Drift Prevention Checks
387
+
388
+ ```bash
389
+ # Run all drift checks (surface/docs/data/schema)
390
+ pnpm drift:check
391
+
392
+ # Refresh committed command snapshot after intentional command changes
393
+ pnpm drift:surface:update
394
+ ```
395
+
396
+ `drift:schema:check` validates GraphQL documents against the live schema.
397
+ Locally, it skips when offline; in CI (`CI=true`) it is enforced.
398
+
399
+ ---
400
+
413
401
  ## Troubleshooting
414
402
 
415
- **`No token found. Set SONAR_API_KEY or run: sonar init`**
416
- Set `SONAR_API_KEY` in your environment, then run `pnpm run cli -- init`.
403
+ **`No token found. Set SONAR_API_KEY or run: sonar config setup`**
404
+ Set `SONAR_API_KEY` in your environment or run `sonar config setup key=<YOUR_KEY>`.
417
405
 
418
406
  **`Unable to reach server, please try again shortly.`**
419
- Check `SONAR_API_URL`, your network, and API availability.
420
-
421
- **`OPENAI_API_KEY is not set` / `ANTHROPIC_API_KEY is not set`**
422
- Set the key for your chosen vendor before using `--from-prompt` or interactive reply generation.
407
+ Check your network connection and API availability. The CLI automatically retries transient failures (network errors, 5xx) up to 3 times with exponential backoff. Use `--debug` to see retry attempts. Set `SONAR_MAX_RETRIES=0` to disable retries.
@@ -2,8 +2,8 @@ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useEffect, useState } from 'react';
3
3
  import zod from 'zod';
4
4
  import { Text } from 'ink';
5
- import { gql } from '../../lib/client.js';
6
- import { Spinner } from '../../components/Spinner.js';
5
+ import { gql } from '../lib/client.js';
6
+ import { Spinner } from '../components/Spinner.js';
7
7
  export const options = zod.object({
8
8
  id: zod.string().describe('Suggestion ID to archive'),
9
9
  });
@@ -28,7 +28,7 @@ export default function DataDownload() {
28
28
  upsertTweet(db, s.tweet);
29
29
  upsertSuggestion(db, { suggestionId: s.suggestionId, tweetId: s.tweet.id, score: s.score, status: s.status, relevance: null, projectsMatched: s.projectsMatched });
30
30
  }
31
- for (const i of interestsResult.projects) {
31
+ for (const i of interestsResult.topics) {
32
32
  upsertInterest(db, i);
33
33
  }
34
34
  setSyncState(db, 'last_synced_at', new Date().toISOString());
@@ -36,7 +36,7 @@ export default function DataDownload() {
36
36
  setResult({
37
37
  feedCount: feedResult.feed.length,
38
38
  suggestionsCount: suggestionsResult.suggestions.length,
39
- interestsCount: interestsResult.projects.length,
39
+ interestsCount: interestsResult.topics.length,
40
40
  });
41
41
  }
42
42
  catch (err) {
@@ -32,12 +32,12 @@ export default function DataSync() {
32
32
  upsertTweet(freshDb, s.tweet);
33
33
  upsertSuggestion(freshDb, { suggestionId: s.suggestionId, tweetId: s.tweet.id, score: s.score, status: s.status, relevance: null, projectsMatched: s.projectsMatched });
34
34
  }
35
- for (const i of interestsResult.projects) {
35
+ for (const i of interestsResult.topics) {
36
36
  upsertInterest(freshDb, i);
37
37
  }
38
38
  setSyncState(freshDb, 'last_synced_at', new Date().toISOString());
39
39
  freshDb.close();
40
- setResult({ feedCount: feedResult.feed.length, suggestionsCount: suggestionsResult.suggestions.length, interestsCount: interestsResult.projects.length });
40
+ setResult({ feedCount: feedResult.feed.length, suggestionsCount: suggestionsResult.suggestions.length, interestsCount: interestsResult.topics.length });
41
41
  return;
42
42
  }
43
43
  const hoursSinceSync = Math.min(Math.ceil((Date.now() - new Date(lastSyncedAt).getTime()) / 3600000), 168);
@@ -3,17 +3,35 @@ import { useEffect } from 'react';
3
3
  import { configExists, deleteConfig, deleteDatabase } from '../../lib/config.js';
4
4
  import { Text } from 'ink';
5
5
  import zod from 'zod';
6
+ import { existsSync } from 'node:fs';
7
+ import { DB_PATH } from '../../lib/db.js';
6
8
  export const options = zod.object({
7
9
  confirm: zod.boolean().default(false).describe('Pass to confirm deletion'),
8
10
  });
9
11
  export default function Nuke({ options: flags }) {
10
12
  useEffect(() => {
11
- if (configExists() && flags.confirm) {
13
+ if (!flags.confirm) {
14
+ return;
15
+ }
16
+ const hadConfig = configExists();
17
+ const hadDb = existsSync(DB_PATH);
18
+ if (hadConfig) {
12
19
  deleteConfig();
20
+ }
21
+ if (hadDb) {
13
22
  deleteDatabase();
14
- process.stdout.write('Workspace deleted at ~/.sonar/config.json and ~/.sonar/database.sqlite\n');
23
+ }
24
+ if (!hadConfig && !hadDb) {
25
+ process.stdout.write('Nothing to delete. No local Sonar config or data database found.\n');
15
26
  process.exit(0);
16
27
  }
28
+ const deleted = [];
29
+ if (hadConfig)
30
+ deleted.push('~/.sonar/config.json');
31
+ if (hadDb)
32
+ deleted.push(DB_PATH);
33
+ process.stdout.write(`Deleted: ${deleted.join(', ')}\n`);
34
+ process.exit(0);
17
35
  }, []);
18
36
  return _jsxs(Text, { dimColor: true, children: ["Tip. (pass ", _jsx(Text, { color: "cyan", children: "--confirm" }), " to nuke)"] });
19
37
  }