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.
- cited_cli/__init__.py +1 -0
- cited_cli/__main__.py +3 -0
- cited_cli/api/__init__.py +0 -0
- cited_cli/api/client.py +111 -0
- cited_cli/api/endpoints.py +88 -0
- cited_cli/app.py +199 -0
- cited_cli/auth/__init__.py +0 -0
- cited_cli/auth/oauth_server.py +123 -0
- cited_cli/auth/store.py +83 -0
- cited_cli/commands/__init__.py +0 -0
- cited_cli/commands/agent.py +129 -0
- cited_cli/commands/analytics.py +83 -0
- cited_cli/commands/audit.py +177 -0
- cited_cli/commands/auth.py +353 -0
- cited_cli/commands/business.py +217 -0
- cited_cli/commands/config_cmd.py +111 -0
- cited_cli/commands/hq.py +125 -0
- cited_cli/commands/job.py +106 -0
- cited_cli/commands/mcp_cmd.py +30 -0
- cited_cli/commands/named_audit.py +244 -0
- cited_cli/commands/recommend.py +211 -0
- cited_cli/commands/solution.py +154 -0
- cited_cli/config/__init__.py +0 -0
- cited_cli/config/constants.py +48 -0
- cited_cli/config/manager.py +69 -0
- cited_cli/mcp/__init__.py +1 -0
- cited_cli/mcp/context.py +12 -0
- cited_cli/mcp/server.py +54 -0
- cited_cli/mcp/tools/__init__.py +1 -0
- cited_cli/mcp/tools/_helpers.py +33 -0
- cited_cli/mcp/tools/audit.py +129 -0
- cited_cli/mcp/tools/auth.py +23 -0
- cited_cli/mcp/tools/business.py +96 -0
- cited_cli/mcp/tools/job.py +57 -0
- cited_cli/mcp/tools/recommend.py +96 -0
- cited_cli/mcp/tools/solution.py +66 -0
- cited_cli/output/__init__.py +0 -0
- cited_cli/output/formatter.py +61 -0
- cited_cli/output/progress.py +56 -0
- cited_cli/output/tables.py +49 -0
- cited_cli/utils/__init__.py +0 -0
- cited_cli/utils/errors.py +31 -0
- cited_cli/utils/interactive.py +83 -0
- cited_cli-0.3.2.dist-info/METADATA +515 -0
- cited_cli-0.3.2.dist-info/RECORD +48 -0
- cited_cli-0.3.2.dist-info/WHEEL +4 -0
- cited_cli-0.3.2.dist-info/entry_points.txt +2 -0
- 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,,
|