@anysiteio/agent-skills 1.2.0 → 1.3.0

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.
@@ -1,292 +1,548 @@
1
1
  ---
2
2
  name: anysite-cli
3
- description: Operate the anysite command-line tool for web data extraction, batch API processing, multi-source dataset pipelines with scheduling/transforms/exports, and database operations. Use when users ask to collect data from LinkedIn, Instagram, Twitter, or any web source via CLI; create or run dataset pipelines; schedule automated collection; batch-process API calls; query collected data with SQL; load data into PostgreSQL or SQLite; or work with anysite commands. Triggers on anysite CLI usage, data collection, dataset creation, scraping, API batch calls, scheduling, or database loading tasks.
3
+ description: Operate the anysite command-line tool for web data extraction, batch API processing, multi-source dataset pipelines with scheduling/transforms/exports, database operations, and LLM-powered data analysis. Use when users ask to collect data from LinkedIn, Instagram, Twitter, or any web source via CLI; create or run dataset pipelines; schedule automated collection; batch-process API calls; query collected data with SQL; load data into PostgreSQL or SQLite; analyze data with LLM (summarize, classify, enrich, match, deduplicate); or work with anysite commands. Triggers on anysite CLI usage, data collection, dataset creation, scraping, API batch calls, scheduling, database loading, or LLM analysis tasks.
4
4
  ---
5
5
 
6
6
  # Anysite CLI
7
7
 
8
- Command-line tool for web data extraction, dataset pipelines, and database operations. All commands use `anysite` prefix and execute via Bash.
8
+ Command-line tool for web data extraction, dataset pipelines, and database operations.
9
9
 
10
- ## Prerequisites
10
+ ## Agent Planning Workflow
11
+
12
+ **BEFORE planning any data collection task, follow this sequence:**
13
+
14
+ 1. **Discover available endpoints**
15
+ ```bash
16
+ anysite describe --search "<keyword>" # Search by domain (linkedin, company, user, etc.)
17
+ ```
18
+
19
+ 2. **Select endpoints needed for the task** — identify which endpoints will provide the required data
20
+
21
+ 3. **Inspect each selected endpoint**
22
+ ```bash
23
+ anysite describe /api/linkedin/company # View input params and output fields
24
+ ```
25
+
26
+ 4. **Only then plan** — now you know the exact parameters, field names, and data structure to build your config or API calls
27
+
28
+ This prevents errors from wrong endpoint paths, missing required parameters, or incorrect field names in dependencies.
29
+
30
+ ## Best Practices
31
+
32
+ 1. **Use dataset pipelines for multi-step tasks**
33
+ - If a task requires sequential API calls, LLM enrichment, or chained data processing — create a `dataset.yaml` config instead of running multiple ad-hoc commands
34
+ - Dataset pipelines handle dependencies, incremental collection, and error recovery automatically
35
+ - Even for "simple" tasks that grow in scope, a dataset config is easier to maintain
36
+ - Benefits: run history, incremental sync, scheduling, notifications, DB loading
37
+
38
+ 2. **Save data in Parquet format by default** — unless user requests another format or CSV/JSON fits better
39
+
40
+ 3. **Prefer datasets over ad-hoc scripts** — one dataset.yaml replaces dozens of shell commands
41
+
42
+ ## Quick Start Checklist
43
+
44
+ Before any data collection task:
11
45
 
12
46
  ```bash
13
- # Ensure CLI is installed
47
+ # 1. Check CLI is available and see latest changes
14
48
  anysite --version
49
+ anysite changelog --last 1 --json # Check what's new in this version
50
+ # If not found: source .venv/bin/activate or pip install anysite-cli
15
51
 
16
- # Configure API key (one-time)
17
- anysite config set api_key sk-xxxxx
18
-
19
- # Update schema cache (required for endpoint discovery and type inference)
52
+ # 2. Update schema cache (required for endpoint discovery)
20
53
  anysite schema update
54
+
55
+ # 3. Verify API key
56
+ anysite config get api_key
57
+ # If not set: anysite config set api_key sk-xxxxx
21
58
  ```
22
59
 
23
- ## Workflow 1: Single API Call
60
+ After upgrading, run `anysite changelog --since <old_version> --json` to discover new features.
61
+
62
+ ## Endpoint Discovery
63
+
64
+ **ALWAYS discover endpoints before writing API calls or dataset configs:**
24
65
 
25
66
  ```bash
26
- # Basic call — parameters as key=value pairs
27
- anysite api /api/linkedin/user user=satyanadella
67
+ anysite describe # List all endpoints
68
+ anysite describe --search "company" # Search with dependency context
69
+ anysite describe /api/linkedin/company # Full details: params, output, connections
28
70
 
29
- # With output options
30
- anysite api /api/linkedin/company company=anthropic --format table
31
- anysite api /api/linkedin/search/users title=CTO count=50 --format csv --output ctos.csv
71
+ ```
32
72
 
33
- # Field selection
34
- anysite api /api/linkedin/user user=satyanadella --fields "name,headline,follower_count"
35
- anysite api /api/linkedin/user user=satyanadella --exclude "certifications,patents"
73
+ Search returns matched endpoints plus upstream providers (who can supply input IDs) and downstream consumers (who can use output IDs). Use this to plan endpoint chains for dataset pipelines.
36
74
 
37
- # Quiet mode for piping
38
- anysite api /api/linkedin/user user=satyanadella -q | jq '.follower_count'
75
+ Input params show type, description, examples, and defaults. Array params show item structure:
39
76
  ```
77
+ Input parameters:
78
+ * urn string User URN, only fsd_profile urn type is allowed
79
+ example: "urn:li:fsd_profile:ACoAABXy1234"
80
+ count integer Number of posts to return
81
+ default: 20
82
+ companies array[object{type,value}] Company URNs
83
+ example: [{"type": "company", "value": "14064608"}]
84
+ ```
85
+
86
+ ## Prerequisites
40
87
 
41
- **Discover endpoints first:**
42
88
  ```bash
43
- anysite describe # List all endpoints
44
- anysite describe /api/linkedin/company # Show input params + output fields
45
- anysite describe --search "company" # Search by keyword
89
+ pip install "anysite-cli[data]" # DuckDB + PyArrow for dataset commands
90
+ pip install "anysite-cli[llm]" # LLM analysis (openai/anthropic)
91
+ pip install "anysite-cli[postgres]" # PostgreSQL adapter
92
+ pip install "anysite-cli[clickhouse]" # ClickHouse adapter
93
+
94
+ anysite config set api_key sk-xxxxx # Configure API key
95
+ anysite schema update # Update schema cache
96
+ anysite llm setup # Interactive setup (human)
97
+ anysite llm setup --provider openai --api-key sk-xxx --no-test # Non-interactive (agent)
98
+ anysite llm setup --provider anthropic --api-key-env ANTHROPIC_API_KEY --no-test
99
+ anysite db add pg --type postgres --host localhost --database mydb --user app --password secret
100
+ # Or via env var: anysite db add pg ... --password-env PGPASS
101
+ anysite db add ch --type clickhouse --host ch.example.com --port 8443 --database analytics --user app --password secret --ssl
46
102
  ```
47
103
 
48
- See [api-reference.md](references/api-reference.md) for complete option reference.
104
+ ## Authentication
49
105
 
50
- ## Workflow 2: Batch Processing
106
+ ```bash
107
+ anysite auth login # Interactive browser-based OAuth2 (human)
108
+ anysite auth login --force --no-browser # Re-authenticate without confirmation (agent)
109
+ anysite auth status # Check current auth status
110
+ anysite auth status --json # Machine-readable auth status
111
+ anysite auth logout # Interactive logout (human)
112
+ anysite auth logout --force # Logout without confirmation (agent)
113
+ ```
51
114
 
52
- Process multiple inputs from file or stdin with parallel execution and rate limiting.
115
+ ## Single API Call
53
116
 
54
117
  ```bash
55
- # From text file (one value per line)
56
- anysite api /api/linkedin/user --from-file users.txt --input-key user --parallel 5
118
+ anysite api /api/linkedin/user user=satyanadella
119
+ anysite api /api/linkedin/company company=anthropic --format table
120
+ anysite api /api/linkedin/search/users keywords="CTO" count=50 --format csv --output ctos.csv
121
+ anysite api /api/linkedin/user user=satyanadella --fields "name,headline,urn.value" -q | jq
122
+ ```
57
123
 
58
- # From JSONL
59
- anysite api /api/linkedin/user --from-file users.jsonl --parallel 3 --on-error skip
124
+ ### URN/Name Parameter Formats
60
125
 
61
- # With rate limiting and progress
62
- anysite api /api/linkedin/user --from-file users.txt --input-key user \
63
- --rate-limit "10/s" --on-error skip --progress --stats
126
+ Parameters like `location`, `current_companies`, `industry` accept two formats:
127
+
128
+ ```bash
129
+ # Single name (text search) — resolves to URNs automatically
130
+ location="London"
131
+ current_companies="Microsoft"
64
132
 
65
- # Pipe from stdin
66
- cat companies.txt | anysite api /api/linkedin/company --stdin --input-key company \
67
- --format csv --output results.csv
133
+ # Multiple URNs (direct) — use JSON array in single quotes
134
+ 'location=["urn:li:geo:101165590", "urn:li:geo:101282230"]'
135
+ 'current_companies=["urn:li:company:1035", "urn:li:company:1441"]'
68
136
  ```
69
137
 
70
- **Key options:** `--parallel N`, `--rate-limit "10/s"`, `--on-error stop|skip|retry`, `--progress`, `--stats`
138
+ **Note:** List of names `["Microsoft", "Google"]` is NOT supported — use either one name OR multiple URNs.
71
139
 
72
- ## Workflow 3: Dataset Pipeline (Multi-Source Collection)
140
+ ## Batch Processing
73
141
 
74
- For complex data collection with dependencies between sources. Full guide: [dataset-guide.md](references/dataset-guide.md).
142
+ ```bash
143
+ anysite api /api/linkedin/user --from-file users.txt --input-key user \
144
+ --parallel 5 --rate-limit "10/s" --on-error skip --progress --stats
145
+ ```
146
+
147
+ ## Dataset Pipeline (Multi-Source Collection)
148
+
149
+ For complex data collection with dependencies, LLM enrichment, scheduling — use dataset pipelines.
150
+
151
+ ### Initialize
75
152
 
76
- ### Step 1: Initialize
77
153
  ```bash
78
154
  anysite dataset init my-dataset
79
155
  # Creates my-dataset/dataset.yaml with template config
80
156
  ```
81
157
 
82
- ### Step 2: Configure dataset.yaml
83
-
84
- Three source types:
85
- - **Independent** — single API call with static `params`
86
- - **from_file** — batch calls iterating over input file values
87
- - **Dependent** — batch calls using values extracted from a parent source
158
+ ### Six Source Types
88
159
 
89
- Per-source optional blocks: `transform` (filter/fields/add_columns for exports), `export` (file/webhook), `db_load` (fields for DB loading).
160
+ 1. **Independent** single API call with static `input`
161
+ 2. **from_file** — batch calls iterating over input file values
162
+ 3. **Dependent** — batch calls using values extracted from a parent source
163
+ 4. **Union (type: union)** — combine records from multiple parent sources into one
164
+ 5. **LLM (type: llm)** — process parent data through LLM without API calls
165
+ 6. **SQL (type: sql)** — query a database connection or dataset Parquet files via DuckDB
90
166
 
91
- Top-level optional blocks: `schedule` (cron), `notifications` (webhooks on complete/failure).
167
+ ### Comprehensive Dataset YAML Reference
92
168
 
93
169
  ```yaml
94
- name: my-dataset
170
+ name: my-dataset # Dataset name (required)
171
+ description: Optional description # Human-readable description
172
+
95
173
  sources:
174
+ # === TYPE 1: Independent source (single API call) ===
175
+ - id: search_results # Unique identifier (required)
176
+ endpoint: /api/linkedin/search/users # API endpoint (required for type: api)
177
+ input: # Static API parameters
178
+ keywords: "software engineer"
179
+ count: 50
180
+ parallel: 1 # Concurrent requests: 1-10 (default: 1)
181
+ rate_limit: "10/s" # Rate limit: "N/s", "N/m", "N/h"
182
+ on_error: stop # Error handling: stop | skip (default: stop)
183
+
184
+ - id: search_extra # Another search (can be combined with union)
185
+ endpoint: /api/linkedin/search/users
186
+ input: { keywords: "data engineer", count: 50 }
187
+
188
+ # === TYPE 2: from_file source (batch from file) ===
96
189
  - id: companies
97
190
  endpoint: /api/linkedin/company
98
- from_file: companies.txt
99
- input_key: company
191
+ from_file: companies.txt # Input file: .txt (line per value), .csv, .jsonl
192
+ file_field: company_slug # CSV column name (for CSV files only)
193
+ input_key: company # API parameter to fill with each value
100
194
  parallel: 3
101
- transform: # Applied to exports only (Parquet keeps all fields)
102
- filter: '.employee_count > 10'
103
- fields: [name, url, employee_count]
104
- add_columns:
105
- batch: "q1-2026"
106
- export: # Export after Parquet write
107
- - type: file
108
- path: ./output/companies-{{date}}.csv
109
- format: csv
110
- db_load:
111
- fields: [name, url, employee_count]
112
195
 
196
+ # === TYPE 3: Dependent source (values from parent) ===
113
197
  - id: employees
114
198
  endpoint: /api/linkedin/company/employees
115
199
  dependency:
116
- from_source: companies
117
- field: urn.value # Dot-notation for nested JSON fields
118
- dedupe: true
119
- input_key: companies
120
- input_template: # Transform extracted values
200
+ from_source: companies # Parent source ID (required)
201
+ field: urn.value # Dot-notation path to extract from parent records
202
+ match_by: name # Alternative: fuzzy match instead of exact field
203
+ dedupe: true # Remove duplicate values (default: false)
204
+ input_key: companies # API parameter for extracted values
205
+ input_template: # Transform values before API call
121
206
  companies:
122
207
  - type: company
123
- value: "{value}"
208
+ value: "{value}" # {value} = extracted value placeholder
124
209
  count: 5
125
- parallel: 3
126
- on_error: skip
210
+ refresh: auto # Incremental behavior: auto (default) | always | never
211
+ # never = skip if data exists
212
+
213
+ # === Shorthand for dependent sources ===
214
+ # ${source.field} auto-expands to dependency + input_key:
215
+ - id: profiles
216
+ endpoint: /api/linkedin/user
217
+ input:
218
+ user: ${companies.urn.value} # Equivalent to dependency + input_key above
219
+
220
+ # === TYPE 4: Union source (combine multiple sources) ===
221
+ - id: all_search_results
222
+ type: union # Source type: api (default) | union | llm | sql
223
+ sources: [search_results, search_extra] # Parent source IDs to combine (required)
224
+ dedupe_by: urn.value # Optional: field path for deduplication (dot-notation)
225
+ # NOTE: type: union cannot have endpoint, dependency, from_file, input_key, input
226
+ # NOTE: all sources in the list must have the same endpoint (same data structure)
227
+ # Records are annotated with _union_source = parent source ID
228
+
229
+ # === TYPE 5: LLM source (process parent data without API) ===
230
+ - id: employees_analyzed
231
+ type: llm # Source type: api (default) | union | llm | sql
232
+ dependency:
233
+ from_source: employees
234
+ field: name # Required by schema (not used for LLM sources)
235
+ llm: # LLM enrichment steps (required for type: llm)
236
+ - type: classify # Step types: classify | enrich | summarize | generate
237
+ categories: "developer,recruiter,executive" # Comma-separated (omit for auto-detect)
238
+ output_column: role_type # Output column name (default: category)
239
+ fields: [headline] # Record fields to include in LLM prompt
240
+ - type: enrich
241
+ add: # Field specs (required for enrich)
242
+ - "sentiment:positive/negative/neutral" # Enum: value1/value2/value3
243
+ - "language:string" # Types: string | number | integer | boolean
244
+ - "quality_score:1-10" # Range: min-max
245
+ fields: [headline, summary]
246
+ temperature: 0.0 # LLM temperature: 0.0-1.0 (default: 0.0)
247
+ provider: openai # Provider override: openai | anthropic
248
+ model: gpt-4o-mini # Model override
249
+ - type: summarize
250
+ max_length: 50 # Max words (default: 100)
251
+ output_column: bio
252
+ - type: generate
253
+ prompt: "Write pitch for {name}" # Template with {field} placeholders (required)
254
+ output_column: pitch
255
+ temperature: 0.7 # Higher for creative text
256
+ export: # Export destinations (runs after Parquet write)
257
+ - type: file
258
+ path: ./output/{{source}}-{{date}}.csv # Templates: {{date}}, {{source}}, {{dataset}}
259
+ format: csv # Format: json | jsonl | csv
260
+ # NOTE: type: llm cannot have endpoint, from_file, input_key, input
261
+
262
+ # === TYPE 6: SQL source (query a database) ===
263
+ # Mode A: Named connection (query external database)
264
+ - id: billing_users
265
+ type: sql
266
+ connection: billing # Named connection from 'anysite db add'
267
+ query: "SELECT name, email FROM subscriptions WHERE status = 'inactive'"
268
+ # query_file: queries/inactive.sql # Alternative: read SQL from file
269
+ filter: ".email != null" # All base fields work: filter, llm, transform, export, db_load
270
+
271
+ # Mode B: Dataset views (no connection — query Parquet via DuckDB)
272
+ # Each collected source becomes a view by its id (hyphens → underscores)
273
+ - id: enriched_profiles
274
+ type: sql
275
+ query: |
276
+ SELECT u.*, c.description as company_desc
277
+ FROM user_profiles u
278
+ LEFT JOIN company_profiles c ON u._input_value = c._input_value
279
+ # Use for cross-source JOINs within the pipeline — no external DB needed
280
+ # NOTE: type: sql cannot have endpoint, from_file, input_key, input, parallel, rate_limit, on_error
281
+
282
+ # === OPTIONAL BLOCKS (any source type) ===
283
+ # THREE-LEVEL FILTERING:
284
+ # Level 1: source.filter — before LLM + Parquet (saves tokens, drops records entirely)
285
+ # Level 2: transform.filter — before exports only (Parquet keeps all records)
286
+ # Level 3: db_load.filter — before DB loading (Parquet keeps all records)
287
+ # All use same syntax: .field op value, booleans (true/false), and/or
288
+ - id: profiles
289
+ endpoint: /api/linkedin/user
290
+ dependency: { from_source: employees, field: internal_id.value }
291
+ input_key: user
292
+ filter: '.follower_count > 100' # Level 1: early filter (before LLM + Parquet)
293
+ transform: # Level 2: export filter (Parquet keeps all)
294
+ filter: '.location != ""' # Safe expression
295
+ fields: # Field selection with dot-notation aliases
296
+ - name
297
+ - urn.value AS urn_id
298
+ - headline
299
+ add_columns: # Static columns to inject
300
+ batch: "q1-2026"
301
+ export:
302
+ - type: file
303
+ path: ./output/profiles.csv
304
+ format: csv
305
+ - type: webhook
306
+ url: https://example.com/hook
307
+ headers: { X-Token: abc }
308
+ db_load: # Database loading config
309
+ filter: '.active == true' # Level 3: DB filter (only active to DB)
310
+ table: people # Custom table name (default: source ID)
311
+ key: urn.value # Unique key for diff-based incremental sync
312
+ sync: full # Sync mode: full (default) | append (no DELETE)
313
+ fields: # Fields to load (default: all except _input_value)
314
+ - name
315
+ - urn.value AS urn_id
316
+ - headline
317
+ exclude: [_input_value, raw_html] # Fields to skip
127
318
 
128
319
  storage:
129
- format: parquet
130
- path: ./data/
320
+ format: parquet # Storage format (only parquet supported)
321
+ path: ./data/ # Storage directory (relative to dataset.yaml)
131
322
 
132
323
  schedule:
133
- cron: "0 9 * * *" # Daily at 9 AM
324
+ cron: "0 9 * * *" # Cron expression for scheduling
134
325
 
135
326
  notifications:
136
327
  on_complete:
137
328
  - url: "https://hooks.slack.com/xxx"
329
+ headers: { Authorization: "Bearer token" }
138
330
  on_failure:
139
331
  - url: "https://alerts.example.com/fail"
140
332
  ```
141
333
 
142
- ### Step 3: Collect
143
- ```bash
144
- # Preview plan
145
- anysite dataset collect dataset.yaml --dry-run
334
+ ### type: union Source Details
146
335
 
147
- # Run collection
148
- anysite dataset collect dataset.yaml
336
+ The `type: union` source combines records from multiple parent sources:
337
+
338
+ - **Requires**: non-empty `sources` list (parent source IDs)
339
+ - **Optional**: `dedupe_by` field path for removing duplicates (supports dot-notation)
340
+ - **Cannot have**: `endpoint`, `dependency`, `from_file`, `input_key`, `input`
341
+ - **Validation**: all parent sources must have the same endpoint (same data structure)
342
+ - **Use case**: merge multiple search results before a single dependent source processes them
149
343
 
150
- # Collect and auto-load into database
151
- anysite dataset collect dataset.yaml --load-db pg
344
+ ```yaml
345
+ sources:
346
+ - id: search_cto
347
+ endpoint: /api/linkedin/search/users
348
+ input: { keywords: "CTO fintech", count: 50 }
152
349
 
153
- # Incremental (skip already-collected inputs)
154
- anysite dataset collect dataset.yaml --incremental --load-db pg
350
+ - id: search_vp
351
+ endpoint: /api/linkedin/search/users
352
+ input: { keywords: "VP Engineering", count: 50 }
155
353
 
156
- # Single source and its dependencies
157
- anysite dataset collect dataset.yaml --source employees
354
+ # Union combines all search results
355
+ - id: all_candidates
356
+ type: union
357
+ sources: [search_cto, search_vp]
358
+ dedupe_by: urn.value # Remove duplicates by URN
359
+
360
+ # Single dependent source processes all candidates
361
+ - id: profiles
362
+ endpoint: /api/linkedin/user
363
+ dependency:
364
+ from_source: all_candidates
365
+ field: urn.value
366
+ input_key: user
158
367
  ```
159
368
 
160
- ### Step 4: Query with DuckDB
161
- ```bash
162
- # SQL query
163
- anysite dataset query dataset.yaml --sql "SELECT * FROM companies LIMIT 10"
369
+ Records from union sources are annotated with `_union_source` (the parent source ID they came from).
164
370
 
165
- # Shorthand with dot-notation field extraction
166
- anysite dataset query dataset.yaml --source profiles \
167
- --fields "name, urn.value AS urn_id, headline"
371
+ ### type: llm Source Details
168
372
 
169
- # Interactive SQL shell
170
- anysite dataset query dataset.yaml --interactive
373
+ The `type: llm` source processes existing parent data without making API calls:
171
374
 
172
- # Stats and profiling
173
- anysite dataset stats dataset.yaml --source companies
174
- anysite dataset profile dataset.yaml
175
- ```
375
+ - **Requires**: `dependency` (parent source) and non-empty `llm` list
376
+ - **Cannot have**: `endpoint`, `from_file`, `input_key`, `input`
377
+ - **Use case**: Run LLM enrichment on already-collected data, re-analyze with different prompts
176
378
 
177
- ### Step 5: Load into Database
178
379
  ```bash
179
- # Load all sources with FK linking
180
- anysite dataset load-db dataset.yaml -c pg --drop-existing
380
+ # Collect only the LLM source (reads parent Parquet, applies LLM steps)
381
+ anysite dataset collect dataset.yaml --source employees_analyzed
382
+ ```
383
+
384
+ ### Validate & Collect Commands
181
385
 
182
- # Dry run
183
- anysite dataset load-db dataset.yaml -c pg --dry-run
386
+ ```bash
387
+ anysite dataset validate dataset.yaml # Validate config (catches errors early)
388
+ anysite dataset collect dataset.yaml --dry-run # Preview plan
389
+ anysite dataset collect dataset.yaml # Run collection
390
+ anysite dataset collect dataset.yaml --load-db pg # Collect + auto-load to DB
391
+ anysite dataset collect dataset.yaml --incremental # Skip already-collected inputs
392
+ anysite dataset collect dataset.yaml --source employees # Single source + dependencies
393
+ anysite dataset collect dataset.yaml --no-llm # Skip LLM enrichment steps
394
+ anysite dataset collect dataset.yaml --limit 100 # Pilot run: max 100 inputs per source
184
395
  ```
185
396
 
186
- `load-db` auto-creates tables with inferred schema, adds `id` primary key, and links child tables to parents via `{parent}_id` FK columns using provenance data.
397
+ ### Incremental Collection (Resume Where You Left Off)
398
+
399
+ For `from_file` and `dependency` sources, anysite tracks collected input values in `metadata.json`. This enables resuming collection without re-fetching existing data.
400
+
401
+ **Workflow:**
402
+ ```bash
403
+ # First run — collects all inputs
404
+ anysite dataset collect dataset.yaml
405
+
406
+ # Later: add new items to input file, run with --incremental
407
+ anysite dataset collect dataset.yaml --incremental
408
+ # → Only new items are collected, existing ones skipped
187
409
 
188
- Optional `db_load` config per source controls which fields go to DB:
410
+ # Force full re-collection
411
+ anysite dataset reset-cursor dataset.yaml
412
+ anysite dataset collect dataset.yaml
413
+ ```
414
+
415
+ **Per-source `refresh` option:**
189
416
  ```yaml
417
+ sources:
190
418
  - id: profiles
191
- endpoint: /api/linkedin/user
192
- db_load:
193
- table: people # Custom table name
194
- fields: # Select specific fields
195
- - name
196
- - urn.value AS urn_id # Dot-notation extraction
197
- - headline
198
- - experience
199
- exclude: [_input_value] # Fields to skip
200
- ```
419
+ refresh: auto # (default) respects --incremental
420
+
421
+ - id: posts
422
+ refresh: always # always re-collects, ignores --incremental
423
+ # use for time-sensitive data (feeds, activity)
201
424
 
202
- ## Workflow 4: Database Operations
425
+ - id: companies
426
+ refresh: never # skip if data exists (any snapshot)
427
+ ```
203
428
 
429
+ **Reset cursor:**
204
430
  ```bash
205
- # Add connection
206
- anysite db add pg # Interactive prompts for type, host, port, etc.
431
+ anysite dataset reset-cursor dataset.yaml # all sources
432
+ anysite dataset reset-cursor dataset.yaml --source posts # specific source
433
+ ```
207
434
 
208
- # Test and inspect
209
- anysite db test pg
210
- anysite db list
211
- anysite db schema pg --table users
435
+ ### Query with DuckDB
212
436
 
213
- # Insert data (auto-create table from schema inference)
214
- cat data.jsonl | anysite db insert pg --table users --stdin --auto-create
437
+ ```bash
438
+ anysite dataset query dataset.yaml --sql "SELECT * FROM companies LIMIT 10"
439
+ anysite dataset query dataset.yaml --source profiles --fields "name, urn.value AS id"
440
+ anysite dataset query dataset.yaml --source profiles --exclude "_input_value,_parent_source"
441
+ anysite dataset query dataset.yaml --interactive # SQL shell
442
+ anysite dataset stats dataset.yaml --source companies
443
+ anysite dataset profile dataset.yaml
444
+ ```
215
445
 
216
- # Upsert
217
- cat updates.jsonl | anysite db upsert pg --table users --conflict-columns id --stdin
446
+ ### Load into Database
218
447
 
219
- # Query
220
- anysite db query pg --sql "SELECT * FROM users" --format table
448
+ ```bash
449
+ anysite dataset load-db dataset.yaml -c pg --drop-existing # Full load
450
+ anysite dataset load-db dataset.yaml -c pg # Incremental sync (when db_load.key set)
451
+ anysite dataset load-db dataset.yaml -c pg --snapshot 2026-01-15
452
+ ```
221
453
 
222
- # Pipe API output directly to database
223
- anysite api /api/linkedin/user user=satyanadella -q --format jsonl \
224
- | anysite db insert pg --table profiles --stdin --auto-create
454
+ ### Compare Snapshots
455
+
456
+ ```bash
457
+ anysite dataset diff dataset.yaml --source profiles --key urn.value
458
+ anysite dataset diff dataset.yaml --source profiles --key urn.value --from 2026-01-30 --to 2026-02-01
225
459
  ```
226
460
 
227
- ### Step 6: History, Scheduling, and Notifications
461
+ ### Scheduling & History
462
+
228
463
  ```bash
229
- # View run history
464
+ anysite dataset schedule dataset.yaml --incremental --load-db pg # Generate cron entry
230
465
  anysite dataset history my-dataset
231
-
232
- # View logs for a specific run
233
466
  anysite dataset logs my-dataset --run 42
467
+ anysite dataset reset-cursor dataset.yaml # Clear incremental state
468
+ ```
234
469
 
235
- # Generate cron entry (with auto-load to DB)
236
- anysite dataset schedule dataset.yaml --incremental --load-db pg
470
+ ## Database Operations
237
471
 
238
- # Generate systemd timer units
239
- anysite dataset schedule dataset.yaml --systemd --incremental --load-db pg
472
+ ```bash
473
+ # Connection management
474
+ anysite db add pg --type postgres --host localhost --database mydb --user app --password secret
475
+ anysite db add pg --type postgres --host localhost --database mydb --user app --password-env PGPASS
476
+ anysite db add ch --type clickhouse --host ch.example.com --port 8443 --database analytics --user app --password secret --ssl
477
+ anysite db add local --type sqlite --path ./data.db
478
+ anysite db add replica --type postgres --host replica.example.com --read-only
479
+ anysite db list
480
+ anysite db test pg
240
481
 
241
- # Reset incremental state (re-collect everything)
242
- anysite dataset reset-cursor dataset.yaml
243
- anysite dataset reset-cursor dataset.yaml --source profiles
244
- ```
482
+ # Data operations
483
+ cat data.jsonl | anysite db insert pg --table users --stdin --auto-create
484
+ anysite db query pg --sql "SELECT * FROM users" --format table
485
+ anysite db query pg --sql "SELECT * FROM users" --format parquet --output users.parquet
486
+ anysite db query pg --sql "SELECT * FROM users" --format csv --output "reports/{{date}}/users.csv"
245
487
 
246
- ## Key Patterns
488
+ # Pipe API output to database
489
+ anysite api /api/linkedin/user user=satyanadella -q --format jsonl \
490
+ | anysite db insert pg --table profiles --stdin --auto-create
247
491
 
248
- ### Output Formats
249
- `--format json` (default) | `jsonl` | `csv` | `table`
492
+ # Database discovery (schema introspection, sample data, LLM descriptions)
493
+ anysite db discover mydb # Discover schema
494
+ anysite db discover mydb --with-llm # Add LLM table/column descriptions
495
+ anysite db discover mydb --tables users,posts # Filter tables
496
+ anysite db discover mydb --exclude-tables _migrations
497
+
498
+ # View saved catalogs
499
+ anysite db catalog # List all catalogs
500
+ anysite db catalog mydb # Show full catalog
501
+ anysite db catalog mydb --table users # Show specific table
502
+ anysite db catalog mydb --json # JSON output for agents
503
+ ```
250
504
 
251
- ### Field Selection
252
- - Include: `--fields "name,headline,urn.value"`
253
- - Exclude: `--exclude "certifications,patents"`
254
- - Presets: `--fields-preset minimal|contact|recruiting`
255
- - Dot-notation for nested: `experience.company`, `urn.value`
505
+ Credentials: `--password` saves directly in `~/.anysite/connections.yaml`, `--password-env` references an env var. Direct value takes priority. LLM API keys follow the same pattern via `anysite llm setup`.
256
506
 
257
- ### Error Handling
258
- - `--on-error stop` — halt on first error (default)
259
- - `--on-error skip` — continue processing, skip failures
260
- - `--on-error retry` — auto-retry with backoff
507
+ Supported databases: SQLite, PostgreSQL, ClickHouse. ClickHouse uses `clickhouse-connect` driver (HTTP protocol, port 8123 default, 8443 for HTTPS/SSL).
261
508
 
262
- ### Config Priority
263
- CLI args > Environment vars (`ANYSITE_API_KEY`) > `~/.anysite/config.yaml` > defaults
509
+ ## LLM Analysis Commands
264
510
 
265
- ## Common Recipes
511
+ Analyze collected dataset records using LLM. Requires `anysite llm setup` first.
266
512
 
267
- ### Collect company intel and store in Postgres
513
+ Non-interactive setup (for agents):
268
514
  ```bash
269
- anysite dataset init company-intel
270
- # Edit dataset.yaml with sources, transform, schedule, notifications...
271
- anysite dataset collect company-intel/dataset.yaml --load-db pg
272
- anysite db query pg --sql "SELECT c.name, COUNT(e.id) FROM companies c JOIN employees e ON e.companies_id = c.id GROUP BY c.name" --format table
273
-
274
- # Set up daily schedule
275
- anysite dataset schedule company-intel/dataset.yaml --incremental --load-db pg
276
- # Add output to crontab
515
+ anysite llm setup --provider openai --api-key <key> --no-test
516
+ anysite llm setup --provider anthropic --api-key-env ANTHROPIC_API_KEY --model claude-sonnet-4-5-20250514 --no-test
277
517
  ```
278
518
 
279
- ### Batch lookup and save to CSV
280
519
  ```bash
281
- anysite api /api/linkedin/user --from-file people.txt --input-key user \
282
- --parallel 5 --rate-limit "10/s" --on-error skip \
283
- --fields "name,headline,location,follower_count" \
284
- --format csv --output people.csv --stats
520
+ anysite llm summarize dataset.yaml --source profiles --fields "name,headline" --format table
521
+ anysite llm classify dataset.yaml --source posts --categories "positive,negative,neutral"
522
+ anysite llm enrich dataset.yaml --source profiles \
523
+ --add "seniority:junior/mid/senior" --add "is_technical:boolean"
524
+ anysite llm generate dataset.yaml --source profiles \
525
+ --prompt "Write intro for {name} who works as {headline}" --temperature 0.7
526
+ anysite llm match dataset.yaml --source-a profiles --source-b companies --top-k 3
527
+ anysite llm deduplicate dataset.yaml --source profiles --key name --threshold 0.8
528
+ anysite llm cache-stats
529
+ anysite llm cache-clear
285
530
  ```
286
531
 
287
- ### Quick endpoint exploration
288
- ```bash
289
- anysite describe --search "linkedin"
290
- anysite describe /api/linkedin/company
291
- anysite api /api/linkedin/company company=anthropic --format table
292
- ```
532
+
533
+ Use `anysite describe --search <keyword>` for more endpoints.
534
+
535
+ ## Key Patterns
536
+
537
+ - **Output formats**: `--format json|jsonl|csv|table|parquet` (parquet requires `--output`)
538
+ - **Array params**: `keywords=a,b,c` auto-wraps as `["a","b","c"]`
539
+ - **Field selection**: `--fields "name,headline,urn.value"` (dot-notation for nested)
540
+ - **Error handling**: `--on-error stop|skip|retry`
541
+ - **Config priority**: CLI args > ENV vars > `~/.anysite/config.yaml` > defaults
542
+
543
+ ## References
544
+
545
+ For detailed option tables and advanced configuration:
546
+ - [api-reference.md](references/api-reference.md) — all CLI options
547
+ - [llm-reference.md](references/llm-reference.md) — LLM provider config, cache, prompts
548
+ - [dataset-guide.md](references/dataset-guide.md) — full YAML schema, advanced features