cited-cli 0.3.2__py3-none-any.whl

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 (48) hide show
  1. cited_cli/__init__.py +1 -0
  2. cited_cli/__main__.py +3 -0
  3. cited_cli/api/__init__.py +0 -0
  4. cited_cli/api/client.py +111 -0
  5. cited_cli/api/endpoints.py +88 -0
  6. cited_cli/app.py +199 -0
  7. cited_cli/auth/__init__.py +0 -0
  8. cited_cli/auth/oauth_server.py +123 -0
  9. cited_cli/auth/store.py +83 -0
  10. cited_cli/commands/__init__.py +0 -0
  11. cited_cli/commands/agent.py +129 -0
  12. cited_cli/commands/analytics.py +83 -0
  13. cited_cli/commands/audit.py +177 -0
  14. cited_cli/commands/auth.py +353 -0
  15. cited_cli/commands/business.py +217 -0
  16. cited_cli/commands/config_cmd.py +111 -0
  17. cited_cli/commands/hq.py +125 -0
  18. cited_cli/commands/job.py +106 -0
  19. cited_cli/commands/mcp_cmd.py +30 -0
  20. cited_cli/commands/named_audit.py +244 -0
  21. cited_cli/commands/recommend.py +211 -0
  22. cited_cli/commands/solution.py +154 -0
  23. cited_cli/config/__init__.py +0 -0
  24. cited_cli/config/constants.py +48 -0
  25. cited_cli/config/manager.py +69 -0
  26. cited_cli/mcp/__init__.py +1 -0
  27. cited_cli/mcp/context.py +12 -0
  28. cited_cli/mcp/server.py +54 -0
  29. cited_cli/mcp/tools/__init__.py +1 -0
  30. cited_cli/mcp/tools/_helpers.py +33 -0
  31. cited_cli/mcp/tools/audit.py +129 -0
  32. cited_cli/mcp/tools/auth.py +23 -0
  33. cited_cli/mcp/tools/business.py +96 -0
  34. cited_cli/mcp/tools/job.py +57 -0
  35. cited_cli/mcp/tools/recommend.py +96 -0
  36. cited_cli/mcp/tools/solution.py +66 -0
  37. cited_cli/output/__init__.py +0 -0
  38. cited_cli/output/formatter.py +61 -0
  39. cited_cli/output/progress.py +56 -0
  40. cited_cli/output/tables.py +49 -0
  41. cited_cli/utils/__init__.py +0 -0
  42. cited_cli/utils/errors.py +31 -0
  43. cited_cli/utils/interactive.py +83 -0
  44. cited_cli-0.3.2.dist-info/METADATA +515 -0
  45. cited_cli-0.3.2.dist-info/RECORD +48 -0
  46. cited_cli-0.3.2.dist-info/WHEEL +4 -0
  47. cited_cli-0.3.2.dist-info/entry_points.txt +2 -0
  48. cited_cli-0.3.2.dist-info/licenses/LICENSE +4 -0
@@ -0,0 +1,83 @@
1
+ from __future__ import annotations
2
+
3
+ import sys
4
+
5
+ import typer
6
+
7
+ from cited_cli.output.formatter import OutputContext, print_error
8
+ from cited_cli.utils.errors import ExitCode
9
+
10
+
11
+ def is_interactive() -> bool:
12
+ """Return True when stdin is a TTY (interactive terminal)."""
13
+ return sys.stdin.isatty()
14
+
15
+
16
+ def can_prompt(out: OutputContext) -> bool:
17
+ """Return True when interactive prompting is allowed (not JSON mode, is TTY)."""
18
+ return not out.json_mode and is_interactive()
19
+
20
+
21
+ def prompt_if_missing(
22
+ value: str | None,
23
+ flag_name: str,
24
+ prompt_text: str,
25
+ out: OutputContext,
26
+ ) -> str:
27
+ """Return *value* if set, prompt interactively if possible, or error."""
28
+ if value is not None:
29
+ return value
30
+ if can_prompt(out):
31
+ result: str = typer.prompt(prompt_text)
32
+ return result
33
+ print_error(f"Missing required option: {flag_name}", out)
34
+ raise typer.Exit(ExitCode.VALIDATION_ERROR)
35
+
36
+
37
+ def prompt_choice(
38
+ value: str | None,
39
+ flag_name: str,
40
+ prompt_text: str,
41
+ choices: list[str],
42
+ out: OutputContext,
43
+ ) -> str:
44
+ """Return *value* if set, show a numbered menu if interactive, or error."""
45
+ if value is not None:
46
+ if value not in choices:
47
+ print_error(
48
+ f"Invalid value '{value}' for {flag_name}. "
49
+ f"Valid: {', '.join(choices)}",
50
+ out,
51
+ )
52
+ raise typer.Exit(ExitCode.VALIDATION_ERROR)
53
+ return value
54
+ if can_prompt(out):
55
+ out.console.print(f"\n[bold]{prompt_text}[/bold]")
56
+ for i, choice in enumerate(choices, 1):
57
+ out.console.print(f" {i}. {choice}")
58
+ while True:
59
+ raw: str = typer.prompt("Enter number or value")
60
+ if raw in choices:
61
+ return raw
62
+ try:
63
+ idx = int(raw)
64
+ if 1 <= idx <= len(choices):
65
+ return choices[idx - 1]
66
+ except ValueError:
67
+ pass
68
+ out.console.print(
69
+ f"[yellow]Invalid selection. Choose 1–{len(choices)}"
70
+ " or type the value.[/yellow]"
71
+ )
72
+ print_error(f"Missing required option: {flag_name}", out)
73
+ raise typer.Exit(ExitCode.VALIDATION_ERROR)
74
+
75
+
76
+ def confirm_action(message: str, out: OutputContext, *, skip: bool = False) -> None:
77
+ """Ask for confirmation. Skips if *skip* is True or in JSON mode."""
78
+ if skip or out.json_mode:
79
+ return
80
+ if not can_prompt(out):
81
+ print_error("Cannot confirm in non-interactive mode. Use --yes to skip.", out)
82
+ raise typer.Exit(ExitCode.VALIDATION_ERROR)
83
+ typer.confirm(message, abort=True)
@@ -0,0 +1,515 @@
1
+ Metadata-Version: 2.4
2
+ Name: cited-cli
3
+ Version: 0.3.2
4
+ Summary: CLI for the Cited GEO platform
5
+ Author-email: Cited <support@youcited.com>
6
+ License-Expression: LicenseRef-Proprietary
7
+ License-File: LICENSE
8
+ Requires-Python: >=3.11
9
+ Requires-Dist: cited-core>=0.3.2
10
+ Requires-Dist: rich>=13.0.0
11
+ Requires-Dist: typer>=0.15.0
12
+ Provides-Extra: dev
13
+ Requires-Dist: cited-mcp>=0.3.2; extra == 'dev'
14
+ Requires-Dist: mypy>=1.10; extra == 'dev'
15
+ Requires-Dist: pytest>=8.0; extra == 'dev'
16
+ Requires-Dist: respx>=0.21; extra == 'dev'
17
+ Requires-Dist: ruff>=0.4; extra == 'dev'
18
+ Provides-Extra: mcp
19
+ Requires-Dist: cited-mcp>=0.3.2; extra == 'mcp'
20
+ Description-Content-Type: text/markdown
21
+
22
+ # Cited CLI
23
+
24
+ Command-line interface for the [Cited](https://youcited.com) GEO (Generative Engine Optimization) platform. Audit how often your business is cited by AI assistants, generate recommendations, and produce AI-ready content solutions — all from the terminal or a Python script.
25
+
26
+ ## Installation
27
+
28
+ ### Homebrew (recommended)
29
+
30
+ ```bash
31
+ brew tap youcited/cited
32
+ brew install cited
33
+ ```
34
+
35
+ ### From source (development)
36
+
37
+ ```bash
38
+ git clone https://github.com/YouCited/cited-cli.git
39
+ cd cited-cli
40
+ pip install -e ".[dev]"
41
+ ```
42
+
43
+ ## Quick Start
44
+
45
+ ```bash
46
+ # Log in (opens browser by default)
47
+ cited login
48
+
49
+ # Check you're connected
50
+ cited status
51
+
52
+ # List your businesses
53
+ cited business list
54
+
55
+ # See all commands
56
+ cited --help
57
+ cited audit --help
58
+ cited audit template --help
59
+ ```
60
+
61
+ For the full end-to-end workflow — creating a business, running a GEO audit, and generating content solutions — see the [GEO Audit Pipeline](#geo-audit-pipeline) section below.
62
+
63
+ ---
64
+
65
+ ## Authentication
66
+
67
+ ```bash
68
+ # Browser login (default — opens youcited.com login page)
69
+ cited login
70
+
71
+ # Specific OAuth provider
72
+ cited login --provider google
73
+ cited login --provider microsoft
74
+ cited login --provider github
75
+
76
+ # Password login (non-interactive / CI)
77
+ cited login --email you@example.com --password "Secret123!"
78
+
79
+ # Check status
80
+ cited auth status
81
+
82
+ # Log out
83
+ cited logout
84
+ ```
85
+
86
+ **Password registration** is a two-step email-verification flow:
87
+
88
+ ```bash
89
+ cited register --email you@example.com --name "Your Name" --password "Secret123!"
90
+ # → Check your inbox, paste the verification URL when prompted
91
+ ```
92
+
93
+ ---
94
+
95
+ ## Global Flags
96
+
97
+ These flags apply to every command and must be placed immediately after `cited`:
98
+
99
+ | Flag | Short | Description |
100
+ |------|-------|-------------|
101
+ | `--json` | `-j` | Machine-readable JSON output (great for scripting) |
102
+ | `--text` | `-t` | Human-readable text output (overrides `output` config) |
103
+ | `--env` | `-e` | Environment: `dev`, `prod`, `local` (default: `prod`) |
104
+ | `--profile` | `-p` | Config profile to use |
105
+ | `--verbose` | `-v` | Debug logging |
106
+ | `--quiet` | `-q` | Minimal output |
107
+ | `--no-color` | | Disable colors (auto-applied when stdout is not a TTY) |
108
+
109
+ ---
110
+
111
+ ## GEO Audit Pipeline
112
+
113
+ The complete workflow from business registration through AI-ready content solutions. Each step captures an ID for the next — just like piping commands together.
114
+
115
+ ### Prerequisites
116
+
117
+ - `jq` installed (`brew install jq`) for ID extraction in bash
118
+ - A publicly resolvable website domain (not `example.com`)
119
+ - Account logged in: `cited login`
120
+
121
+ ### Step 1 — Create your business profile
122
+
123
+ ```bash
124
+ BUSINESS_ID=$(cited --json business create \
125
+ --name "Acme Corp" \
126
+ --website "https://acme.com" \
127
+ --description "Acme builds AI-powered tools for enterprise productivity teams." \
128
+ --industry "technology" \
129
+ | jq -r '.id')
130
+
131
+ echo "Business: $BUSINESS_ID"
132
+ ```
133
+
134
+ > **`--industry` must be one of:**
135
+ > `automotive` `beauty` `consulting` `education` `entertainment` `finance`
136
+ > `fitness` `government` `healthcare` `home_services` `hospitality` `legal`
137
+ > `manufacturing` `non_profit` `real_estate` `restaurant` `retail` `technology` `other`
138
+
139
+ ### Step 2 — Scan your website
140
+
141
+ The crawler reads your site so the platform understands your content, products, and brand signals.
142
+
143
+ ```bash
144
+ CRAWL_JOB=$(cited --json business crawl $BUSINESS_ID | jq -r '.job_id')
145
+ cited job watch $CRAWL_JOB
146
+
147
+ # View crawl results as health score bars
148
+ cited business health $BUSINESS_ID
149
+ ```
150
+
151
+ ### Step 3 — Create an audit template
152
+
153
+ An audit template defines the questions you want AI assistants to answer. Ask questions your target buyers would actually type.
154
+
155
+ ```bash
156
+ TEMPLATE_ID=$(cited --json audit template create \
157
+ --name "Q4 GEO Audit" \
158
+ --business $BUSINESS_ID \
159
+ --question "Are we cited when people ask about AI productivity tools?" \
160
+ --question "Does our product appear in enterprise software recommendations?" \
161
+ --question "Are we mentioned when users research our core use case?" \
162
+ | jq -r '.id')
163
+
164
+ echo "Template: $TEMPLATE_ID"
165
+
166
+ # List all templates for a business
167
+ cited audit template list --business $BUSINESS_ID
168
+
169
+ # Inspect a template
170
+ cited audit template get $TEMPLATE_ID
171
+ ```
172
+
173
+ Auto-generated questions are rarely perfect out of the box. Refine them before running the audit:
174
+
175
+ ```bash
176
+ # Replace all questions (--question flags replace the full list)
177
+ cited audit template update $TEMPLATE_ID \
178
+ --question "Are we cited when enterprise buyers ask about AI safety?" \
179
+ --question "Does our product appear in responsible AI recommendations?" \
180
+ --question "Are we mentioned when developers compare AI platforms?"
181
+
182
+ # Update just the name (keeps existing questions)
183
+ cited audit template update $TEMPLATE_ID --name "Q4 GEO Audit v2"
184
+
185
+ # Verify changes
186
+ cited audit template get $TEMPLATE_ID
187
+ ```
188
+
189
+ ### Step 4 — Run the audit
190
+
191
+ ```bash
192
+ AUDIT_JOB=$(cited --json audit start $TEMPLATE_ID --business $BUSINESS_ID \
193
+ | jq -r '.job_id')
194
+
195
+ # Watch live progress bar
196
+ cited job watch $AUDIT_JOB
197
+
198
+ # View results: per-question citation data across providers
199
+ cited audit result $AUDIT_JOB
200
+ ```
201
+
202
+ ### Step 5 — Generate recommendations
203
+
204
+ ```bash
205
+ RECO_JOB=$(cited --json recommend start $AUDIT_JOB | jq -r '.job_id')
206
+ cited job watch $RECO_JOB
207
+ ```
208
+
209
+ ### Step 6 — Discover what to fix
210
+
211
+ `recommend insights` prints a table of all actionable items with the source IDs you'll need for the next step:
212
+
213
+ ```bash
214
+ cited recommend insights $RECO_JOB
215
+ ```
216
+
217
+ ```
218
+ Available Insights
219
+ ┏━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━┓
220
+ ┃ # ┃ Type ┃ Source ID ┃ Description ┃
221
+ ┡━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━┩
222
+ │ 1 │ question_insight │ d5b17bda-fa5b-48ff-... │ Are we cited for AI... │
223
+ │ 2 │ head_to_head │ wikipedia.org │ wikipedia.org │
224
+ │ 3 │ strengthening_tip │ llms_txt │ Create llms.txt for AI │
225
+ └───┴───────────────────┴──────────────────────────┴──────────────────────────┘
226
+
227
+ Run a solution: cited solution start $RECO_JOB --type <type> --source <source_id>
228
+ ```
229
+
230
+ ### Step 7 — Generate a content solution
231
+
232
+ Pick a row from the insights table and pass its type and source ID:
233
+
234
+ ```bash
235
+ cited solution start $RECO_JOB \
236
+ --type question_insight \
237
+ --source d5b17bda-fa5b-48ff-8238-a644c78b404a
238
+
239
+ # → ✓ Solution Started
240
+ # → Track progress: cited job watch <sol_job_id>
241
+ # → View artifacts: https://app.youcited.com/solutions/<sol_job_id>
242
+ ```
243
+
244
+ **`--type` values:** `question_insight` · `head_to_head` · `strengthening_tip` · `priority_action`
245
+
246
+ > Rich solution artifacts (blog posts, FAQ schemas, structured content) are rendered in the web app.
247
+ > The CLI prints the direct link so you can jump straight there.
248
+
249
+ ---
250
+
251
+ ## Scripting & Automation
252
+
253
+ Every command supports `--json` output, making the CLI a first-class building block for automation pipelines — in bash, Python, or any language that can run a subprocess.
254
+
255
+ ### Bash with `jq`
256
+
257
+ ```bash
258
+ # Capture IDs by piping --json output through jq
259
+ BUSINESS_ID=$(cited --json business create --name "..." --website "..." \
260
+ --description "..." --industry "technology" | jq -r '.id')
261
+
262
+ TEMPLATE_ID=$(cited --json audit template create \
263
+ --name "My Audit" --business $BUSINESS_ID \
264
+ --question "Are we cited for X?" | jq -r '.id')
265
+
266
+ AUDIT_JOB=$(cited --json audit start $TEMPLATE_ID | jq -r '.job_id')
267
+ cited job watch $AUDIT_JOB
268
+
269
+ RECO_JOB=$(cited --json recommend start $AUDIT_JOB | jq -r '.job_id')
270
+ cited job watch $RECO_JOB
271
+
272
+ # Extract first question insight source_id
273
+ SOURCE_ID=$(cited --json recommend result $RECO_JOB \
274
+ | jq -r '.question_insights[0].question_id')
275
+
276
+ cited solution start $RECO_JOB --type question_insight --source $SOURCE_ID
277
+ ```
278
+
279
+ ### Python
280
+
281
+ The CLI is a thin, authenticated HTTP wrapper — Python scripts can drive it directly via `subprocess` with no extra dependencies. This is the same pattern used by the integration test suite.
282
+
283
+ ```python
284
+ #!/usr/bin/env python3
285
+ """
286
+ cited-pipeline.py — Automate a full GEO audit pipeline.
287
+
288
+ Requirements: `cited` CLI installed and authenticated (`cited login` first).
289
+ Usage: python3 cited-pipeline.py
290
+ """
291
+ import json
292
+ import subprocess
293
+
294
+ ENV = "dev" # or "prod"
295
+
296
+
297
+ def cited_json(*args: str) -> dict | list:
298
+ """Run a cited command with --json and return parsed output."""
299
+ result = subprocess.run(
300
+ ["cited", "--env", ENV, "--json", *args],
301
+ capture_output=True, text=True, check=True,
302
+ )
303
+ return json.loads(result.stdout)
304
+
305
+
306
+ def cited_watch(*args: str) -> None:
307
+ """Run a cited command streaming human output live (progress bars, tables)."""
308
+ subprocess.run(["cited", "--env", ENV, *args], check=True)
309
+
310
+
311
+ # ── Step 1: Create business ───────────────────────────────────────────────────
312
+ print("→ Creating business...")
313
+ business = cited_json(
314
+ "business", "create",
315
+ "--name", "Acme Corp",
316
+ "--website", "https://acme.com",
317
+ "--description", "Acme builds AI-powered tools for enterprise productivity teams.",
318
+ "--industry", "technology",
319
+ )
320
+ business_id = business["id"]
321
+ print(f" Business: {business_id}")
322
+
323
+ # ── Step 2: Scan the website ──────────────────────────────────────────────────
324
+ print("→ Scanning website...")
325
+ crawl = cited_json("business", "crawl", business_id)
326
+ if crawl_job := crawl.get("job_id"):
327
+ cited_watch("job", "watch", crawl_job)
328
+
329
+ # ── Step 3: Create audit template ────────────────────────────────────────────
330
+ print("→ Creating audit template...")
331
+ template = cited_json(
332
+ "audit", "template", "create",
333
+ "--name", "Q4 GEO Audit",
334
+ "--business", business_id,
335
+ "--question", "Are we cited when people ask about AI productivity tools?",
336
+ "--question", "Does our product appear in enterprise software recommendations?",
337
+ "--question", "Are we mentioned when users research our core use case?",
338
+ )
339
+ template_id = template["id"]
340
+ print(f" Template: {template_id}")
341
+
342
+ # ── Step 3b: Refine questions (auto-generated questions usually need tuning) ─
343
+ print("→ Refining template questions...")
344
+ updated = cited_json(
345
+ "audit", "template", "update", template_id,
346
+ "--question", "Are we cited when enterprise buyers ask about AI safety?",
347
+ "--question", "Does our product appear in responsible AI recommendations?",
348
+ "--question", "Are we mentioned when developers compare AI platforms?",
349
+ )
350
+ print(f" Updated: {len(updated['questions'])} questions")
351
+
352
+ # ── Step 4: Run the audit ─────────────────────────────────────────────────────
353
+ print("→ Starting audit...")
354
+ audit = cited_json("audit", "start", template_id, "--business", business_id)
355
+ audit_job = audit["job_id"]
356
+ cited_watch("job", "watch", audit_job)
357
+
358
+ # ── Step 5: Generate recommendations ─────────────────────────────────────────
359
+ print("→ Generating recommendations...")
360
+ recommend = cited_json("recommend", "start", audit_job)
361
+ reco_job = recommend["job_id"]
362
+ cited_watch("job", "watch", reco_job)
363
+
364
+ # ── Step 6: View available insights ──────────────────────────────────────────
365
+ print("→ Available insights:")
366
+ cited_watch("recommend", "insights", reco_job)
367
+
368
+ # ── Step 7: Start a content solution ─────────────────────────────────────────
369
+ # Extract source IDs directly from the JSON result
370
+ result = cited_json("recommend", "result", reco_job)
371
+ question_insights = result.get("question_insights", [])
372
+
373
+ if question_insights:
374
+ source_id = question_insights[0]["question_id"]
375
+ print(f"→ Solving: {question_insights[0]['question_text'][:60]}...")
376
+ solution = cited_json(
377
+ "solution", "start", reco_job,
378
+ "--type", "question_insight",
379
+ "--source", source_id,
380
+ )
381
+ sol_job = solution["job_id"]
382
+ print(f" Solution job: {sol_job}")
383
+ print(f" View artifacts: https://{ENV}.youcited.com/solutions/{sol_job}")
384
+
385
+ print("\n✓ Pipeline complete!")
386
+ ```
387
+
388
+ The `cited_json()` / `cited_watch()` pattern — capture IDs from `--json` output, stream human output for long-running jobs — is exactly what the integration test suite uses internally with mocked HTTP responses.
389
+
390
+ ---
391
+
392
+ ## Command Reference
393
+
394
+ ```
395
+ cited login Log in (top-level alias)
396
+ cited logout Log out (top-level alias)
397
+ cited register Register new account (top-level alias)
398
+ cited status API health check
399
+ cited version Show CLI version
400
+
401
+ cited auth login|logout|register|status|token
402
+
403
+ cited config set|get|show|environments
404
+
405
+ cited business list|get|create|update|delete|health|crawl
406
+
407
+ cited audit template list|get|create|update|delete ← Manage audit templates
408
+ cited audit start|status|result|list|export ← Run and inspect audits
409
+
410
+ cited recommend start|status|result|list ← Generate recommendations
411
+ cited recommend insights ← View actionable source table
412
+
413
+ cited solution start|status|result|list ← Generate content solutions
414
+
415
+ cited hq <business_id> [--full] [--personas] [--products] [--intents] [--actions]
416
+
417
+ cited analytics compare|trends|summary
418
+
419
+ cited agent facts|claims|comparison|semantic-health|buyer-fit
420
+
421
+ cited job watch|cancel
422
+ ```
423
+
424
+ ---
425
+
426
+ ## Configuration
427
+
428
+ Config is stored in `~/.cited/config.toml`:
429
+
430
+ ```bash
431
+ # Set default environment (saves typing --env dev every time)
432
+ cited config set environment dev
433
+
434
+ # Set default output format (saves typing --json every time)
435
+ cited config set output json # or "text" (default)
436
+
437
+ # Set a default business (used by commands that accept --business)
438
+ cited config set default_business_id <uuid>
439
+
440
+ # Set Agent API key (for cited agent commands)
441
+ cited config set agent_api_key <key>
442
+
443
+ # Inspect current config
444
+ cited config show
445
+ cited config environments
446
+ ```
447
+
448
+ > **Tip:** With `output` set to `json`, use `--text` on any command to get human-readable output for that one invocation.
449
+
450
+ ---
451
+
452
+ ## JSON Output
453
+
454
+ All commands write clean JSON to stdout with `--json`, enabling pipelines with `jq`, `python3 -c`, or any other tool:
455
+
456
+ ```bash
457
+ # Extract specific fields
458
+ cited --json business list | jq '.[].name'
459
+ cited --json audit result <id> | jq '.overall_citation_rate'
460
+ cited --json recommend result <id> | jq '.question_insights[].question_text'
461
+
462
+ # Save full results
463
+ cited --json audit result <id> > audit.json
464
+ cited --json recommend result <id> > recommendations.json
465
+
466
+ # Count citations by provider
467
+ cited --json audit result <id> | jq '[.citations_pulled[].provider_name] | group_by(.) | map({provider: .[0], count: length})'
468
+ ```
469
+
470
+ Errors always go to **stderr** as `{"error": true, "message": "..."}` so they never corrupt stdout pipelines.
471
+
472
+ ---
473
+
474
+ ## Exit Codes
475
+
476
+ | Code | Meaning |
477
+ |------|---------|
478
+ | 0 | Success |
479
+ | 1 | General error |
480
+ | 2 | Authentication error |
481
+ | 3 | Not found |
482
+ | 4 | Validation error |
483
+ | 5 | Rate limited |
484
+
485
+ ---
486
+
487
+ ## Development
488
+
489
+ ```bash
490
+ pip install -e ".[dev]"
491
+ pytest -v # All tests
492
+ pytest tests/test_pipeline.py -v # Pipeline integration tests
493
+ ruff check src/
494
+ mypy src/cited_cli/ --ignore-missing-imports
495
+ ```
496
+
497
+ ## Releasing
498
+
499
+ A single script handles the full release pipeline — version bump, GitHub release, and Homebrew tap update:
500
+
501
+ ```bash
502
+ ./scripts/release.sh 0.2.0
503
+ ```
504
+
505
+ This will:
506
+ 1. Bump the version in `pyproject.toml` and `src/cited_cli/__init__.py`
507
+ 2. Commit, tag `v0.2.0`, and push to origin (triggers GitHub Actions release)
508
+ 3. Wait for the GitHub release to be created
509
+ 4. Download the tarball, compute SHA256, and resolve all Python dependency hashes from PyPI
510
+ 5. Regenerate `Formula/cited.rb` in `~/repos/homebrew-cited` with the updated formula
511
+ 6. Commit and push the tap update
512
+
513
+ After the script completes, users can update with `brew update && brew upgrade cited`.
514
+
515
+ **Prerequisites:** `gh` CLI authenticated, `.venv` with cited-cli installed, `~/repos/homebrew-cited` cloned.
@@ -0,0 +1,48 @@
1
+ cited_cli/__init__.py,sha256=gDOj8CpX0QNKB-3joytCMB7YD6En0QfqOnPDQlLpg4U,64
2
+ cited_cli/__main__.py,sha256=QRhi-qN_dODYuCRZxoxMjtDyBQ1vRbA14hO1XIdylqA,39
3
+ cited_cli/app.py,sha256=Y2J-XMA_pTnxYrZ3zQtyASqPppUiCd0iW4AJlbWb1IA,6350
4
+ cited_cli/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ cited_cli/api/client.py,sha256=lx9QGk2sBzWBTnBEuNrYSuwPNUcLy36KQ8g11jRVIzE,3572
6
+ cited_cli/api/endpoints.py,sha256=_5Iz-Xq-lTYAexAOOBJZnxxfsLVAwbOuBlpul-WfQ2c,3259
7
+ cited_cli/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ cited_cli/auth/oauth_server.py,sha256=mhH99cFrV-m7GPajcqsE001LTgQAXgzgXPG7zEWYiQ8,4607
9
+ cited_cli/auth/store.py,sha256=cKxHNOJQj9ezYoIgxXp6qedJL5LahN4YQ0CvKE814tw,2616
10
+ cited_cli/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ cited_cli/commands/agent.py,sha256=jabe9WQO9gHkxXrsN11OSY4H-Y5sQYGORJ9RuRkfVY4,4125
12
+ cited_cli/commands/analytics.py,sha256=u5IQmsuL7ovU7zSJDa_-4IICKb_EZ6yECLCEg8RWcS4,2774
13
+ cited_cli/commands/audit.py,sha256=_68MvIc5-DF8eLBrKxwgys-37-1_V-ZZ_e74SVlEqso,5998
14
+ cited_cli/commands/auth.py,sha256=Uv6v9DiHXYe1UKUTjAQqACzuS9T4iS05TNPTP-T0AAk,12240
15
+ cited_cli/commands/business.py,sha256=iScTkjxRpUzhv-Jlyjse_S8zNuyCbHYwodCie4H3wG0,7996
16
+ cited_cli/commands/config_cmd.py,sha256=NTq3XIlKke81Pd0oAVjMaeSEId0RvAjVXe49ehbGRpc,4058
17
+ cited_cli/commands/hq.py,sha256=yioO2SObqKp_3901wXVr652BRPo_CsJYaKttJl9P_dY,5061
18
+ cited_cli/commands/job.py,sha256=kWYuOJ5R6KA8erHPnj7gyXOVG8a3yj3L41-9A1PAksQ,3796
19
+ cited_cli/commands/mcp_cmd.py,sha256=MPZrsPqZG4nIHlgHX6YEJsjjeYRqq-uFFCuXFbqAi4Y,780
20
+ cited_cli/commands/named_audit.py,sha256=njQ7nNIO2483EKuDGlBSAyU2L1BquAlR7YmWz6iplzo,8885
21
+ cited_cli/commands/recommend.py,sha256=KgZFALSjU1zoK80AObb88pmWSQuacIzb-YHIgCsiJtM,7783
22
+ cited_cli/commands/solution.py,sha256=G8qiDK6wMmXbh0vVgo8TkCVNE53KB04Ccan4UyRBC_o,5554
23
+ cited_cli/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
+ cited_cli/config/constants.py,sha256=jT0Q4-UHk2wgeyQAyYwDmtspIz1pZ5uqh77nO2yKp3c,982
25
+ cited_cli/config/manager.py,sha256=7OWWmn84bN1Xax-8N3C3G4U4aCS1eBSc0QtEOTePjIE,2153
26
+ cited_cli/mcp/__init__.py,sha256=U4S_2y3zgLZVfMenHRaJFBW8yqh2mUBuI291LGQVOJ8,35
27
+ cited_cli/mcp/context.py,sha256=CtOw9iaBwSypza0P9kaWWBTqS1cLpG55xu_na20OfH8,203
28
+ cited_cli/mcp/server.py,sha256=V5zwtRyrQiIkJw5AfRMmtHaaatJD17ItQklmdxJT4zE,1719
29
+ cited_cli/mcp/tools/__init__.py,sha256=U4S_2y3zgLZVfMenHRaJFBW8yqh2mUBuI291LGQVOJ8,35
30
+ cited_cli/mcp/tools/_helpers.py,sha256=twAZAbRRJTQ2aCm8hI7Bz7Fm3FHeWSBWv58rWBMqX00,940
31
+ cited_cli/mcp/tools/audit.py,sha256=ihoGrRJmgsgIRwLRNOKSTTL2sXdRt1h6ZeAE46WnQ0Y,4000
32
+ cited_cli/mcp/tools/auth.py,sha256=fK7lL2SCiLLcPiHAzN_sfH5i08xElhTn7lRJdpKDJcg,732
33
+ cited_cli/mcp/tools/business.py,sha256=jozC86oHA3t4lfFAxe7sMrqE8Gjgds7wOFhMKoJZBDI,3126
34
+ cited_cli/mcp/tools/job.py,sha256=MrnxR79EcuZEYFiS29JDV9j265OxvRYmuAbYFI4SDbQ,1700
35
+ cited_cli/mcp/tools/recommend.py,sha256=AIkEiHKxoRjtDqD3aowPI10drkynNo9KtNZ35LGE95E,3479
36
+ cited_cli/mcp/tools/solution.py,sha256=8DiZ7no-zwwGXOg3lDuaCAVlY9jexoQfbdAZkg-nzF8,2144
37
+ cited_cli/output/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
+ cited_cli/output/formatter.py,sha256=bBjJmFa1-o6HE1_eNcexN0A2EYVmEaio1KavKi_JzMQ,1802
39
+ cited_cli/output/progress.py,sha256=LFtmaDVCXkJaFhxxmg6V-j_TiwAd1_Fel3N-zryqtMY,1773
40
+ cited_cli/output/tables.py,sha256=zWK-bbdpQ_pEx1pm0t8qwtZbhyr0xclFJzTsAA1eWVc,1272
41
+ cited_cli/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
+ cited_cli/utils/errors.py,sha256=1zCbzdxJiNnUkwSAV9eqGzNoCYnfT8M3jFn50ad-yY0,929
43
+ cited_cli/utils/interactive.py,sha256=jAsUwPkkiKkSzgiu_vacWfyhaCLZiE1ptQSKYcvyayE,2656
44
+ cited_cli-0.3.2.dist-info/METADATA,sha256=RZy-DhfFAW65f9x-lZnDFecRc-xA0bi5aJLhongDTkw,17665
45
+ cited_cli-0.3.2.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
46
+ cited_cli-0.3.2.dist-info/entry_points.txt,sha256=MDl8Bg3zdeiNAvIf4mD260CTPFh-u1xNpKBBMUnirC4,45
47
+ cited_cli-0.3.2.dist-info/licenses/LICENSE,sha256=J8ows7EpVDUQ2PTC4ChM1GjA2IviGO9TbjyFT5DrQ8g,216
48
+ cited_cli-0.3.2.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ cited = cited_cli.app:main
@@ -0,0 +1,4 @@
1
+ Copyright (c) 2024-2026 Cited (AdvantageGEO). All rights reserved.
2
+
3
+ This software is proprietary and confidential. Unauthorized copying,
4
+ distribution, or use of this software, via any medium, is strictly prohibited.