@1a35e1/sonar-cli 0.2.0 → 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.
- package/README.md +151 -166
- package/dist/commands/{inbox/archive.js → archive.js} +2 -2
- package/dist/commands/config/data/download.js +2 -2
- package/dist/commands/config/data/sync.js +2 -2
- package/dist/commands/config/nuke.js +20 -2
- package/dist/commands/feed.js +105 -155
- package/dist/commands/index.js +172 -4
- package/dist/commands/{inbox/later.js → later.js} +2 -2
- package/dist/commands/refresh.js +41 -0
- package/dist/commands/{inbox/skip.js → skip.js} +2 -2
- package/dist/commands/status.js +128 -0
- package/dist/commands/sync/bookmarks.js +35 -0
- package/dist/commands/topics/add.js +71 -0
- package/dist/commands/topics/delete.js +42 -0
- package/dist/commands/topics/edit.js +97 -0
- package/dist/commands/topics/index.js +54 -0
- package/dist/commands/topics/suggest.js +125 -0
- package/dist/commands/topics/view.js +48 -0
- package/dist/components/AccountCard.js +1 -1
- package/dist/components/Banner.js +11 -0
- package/dist/components/InteractiveSession.js +95 -210
- package/dist/components/Spinner.js +5 -4
- package/dist/components/TopicCard.js +15 -0
- package/dist/components/TweetCard.js +76 -0
- package/dist/lib/ai.js +85 -0
- package/dist/lib/client.js +66 -39
- package/dist/lib/config.js +3 -2
- package/dist/lib/data-queries.js +1 -3
- package/dist/lib/skill.js +66 -226
- package/package.json +13 -3
- package/dist/commands/account.js +0 -75
- package/dist/commands/inbox/index.js +0 -103
- package/dist/commands/inbox/read.js +0 -41
- package/dist/commands/ingest/bookmarks.js +0 -55
- package/dist/commands/ingest/index.js +0 -5
- package/dist/commands/ingest/tweets.js +0 -55
- package/dist/commands/interests/create.js +0 -107
- package/dist/commands/interests/index.js +0 -56
- package/dist/commands/interests/match.js +0 -33
- package/dist/commands/interests/update.js +0 -153
- package/dist/commands/monitor.js +0 -93
- 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
|
|
36
|
+
View your account status:
|
|
37
37
|
|
|
38
38
|
```sh
|
|
39
|
-
sonar
|
|
39
|
+
sonar status
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
Run your first refresh to index tweets and generate suggestions:
|
|
43
43
|
|
|
44
|
-
> The first time
|
|
44
|
+
> The first time you run this it will take some time.
|
|
45
45
|
|
|
46
46
|
```sh
|
|
47
|
-
sonar
|
|
48
|
-
|
|
49
|
-
sonar ingest monitor
|
|
50
|
-
sonar ingest 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.
|
|
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
|
-
|
|
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
|
-
|
|
114
|
-
pnpm run cli -- inbox --status inbox
|
|
109
|
+
sonar feed --hours 8
|
|
115
110
|
```
|
|
116
111
|
|
|
117
|
-
###
|
|
112
|
+
### Stream your feed in real time
|
|
118
113
|
|
|
119
|
-
|
|
114
|
+
Watch for new items as they appear:
|
|
120
115
|
|
|
121
116
|
```bash
|
|
122
|
-
|
|
123
|
-
|
|
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
|
-
|
|
121
|
+
### Discover new topics with AI
|
|
130
122
|
|
|
131
|
-
|
|
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
|
-
|
|
137
|
-
|
|
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
|
-
###
|
|
130
|
+
### Track a topic you care about
|
|
144
131
|
|
|
145
|
-
|
|
132
|
+
Add a topic, then refresh:
|
|
146
133
|
|
|
147
134
|
```bash
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
135
|
+
sonar topics add "AI agents"
|
|
136
|
+
sonar refresh
|
|
137
|
+
sonar feed --hours 24
|
|
151
138
|
```
|
|
152
139
|
|
|
153
|
-
|
|
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
|
-
#
|
|
157
|
-
|
|
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
|
-
###
|
|
157
|
+
### Monitor the pipeline
|
|
161
158
|
|
|
162
|
-
|
|
159
|
+
Watch the queue in real time while refresh runs:
|
|
163
160
|
|
|
164
161
|
```bash
|
|
165
|
-
|
|
166
|
-
|
|
162
|
+
sonar refresh
|
|
163
|
+
sonar status --watch
|
|
167
164
|
```
|
|
168
165
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
### Monitor indexing jobs
|
|
166
|
+
### Interactive triage
|
|
172
167
|
|
|
173
|
-
|
|
168
|
+
Work through suggestions without leaving the terminal:
|
|
174
169
|
|
|
175
170
|
```bash
|
|
176
|
-
|
|
177
|
-
|
|
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
|
-
|
|
177
|
+
---
|
|
185
178
|
|
|
186
|
-
|
|
179
|
+
## How Sonar finds signal
|
|
187
180
|
|
|
188
|
-
|
|
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
|
-
*
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
232
|
+
Wrap Sonar as a reusable OpenClaw skill:
|
|
259
233
|
|
|
260
234
|
```typescript
|
|
261
235
|
// skills/sonar.ts
|
|
262
|
-
export async function
|
|
263
|
-
const { stdout } = await exec(`sonar
|
|
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
|
|
268
|
-
const { stdout } = await exec(`sonar
|
|
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
|
|
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.
|
|
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
|
|
262
|
+
pnpm add -g @1a35e1/sonar-cli@latest
|
|
290
263
|
|
|
291
264
|
export SONAR_API_KEY="your_api_key_here"
|
|
292
|
-
|
|
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
|
-
|
|
301
|
-
|
|
271
|
+
sonar status
|
|
272
|
+
sonar topics
|
|
302
273
|
```
|
|
303
274
|
|
|
304
275
|
---
|
|
305
276
|
|
|
306
277
|
## Command Reference
|
|
307
278
|
|
|
308
|
-
###
|
|
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
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
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
|
-
|
|
312
|
+
Press `q` to quit follow mode.
|
|
313
|
+
|
|
314
|
+
### Topics
|
|
319
315
|
|
|
320
316
|
```bash
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
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
|
-
|
|
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
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
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
|
-
|
|
334
|
+
Requires `OPENAI_API_KEY` or `ANTHROPIC_API_KEY` depending on vendor.
|
|
335
|
+
|
|
336
|
+
### Pipeline
|
|
354
337
|
|
|
355
338
|
```bash
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
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
|
-
###
|
|
344
|
+
### Triage
|
|
369
345
|
|
|
370
346
|
```bash
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
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
|
-
|
|
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
|
|
397
|
-
| `SONAR_API_URL` | No | GraphQL endpoint (default:
|
|
398
|
-
| `
|
|
399
|
-
| `
|
|
400
|
-
| `
|
|
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
|
|
416
|
-
Set `SONAR_API_KEY` in your environment
|
|
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
|
|
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 '
|
|
6
|
-
import { Spinner } from '
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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 (
|
|
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
|
-
|
|
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
|
}
|