outrank-cli 0.2.0 → 0.4.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.
- package/README.md +43 -6
- package/SKILL.md +311 -264
- package/package.json +1 -1
- package/src/commands/improvements.js +277 -0
- package/src/commands/keywords.js +17 -24
- package/src/index.js +6 -0
package/README.md
CHANGED
|
@@ -48,8 +48,13 @@ This repo currently targets the agent endpoints that already exist in `outrank`:
|
|
|
48
48
|
- `products linking target-config get|update`
|
|
49
49
|
- `products linking targets list|add-manual|upsert|clear-customizations|delete`
|
|
50
50
|
- `products backlinks get|update|credits|earned|unverified|check-removal|reverify|subscription|subscribe-quote`
|
|
51
|
-
- `keywords list|get|export|generation-status|add|suggest|generate|
|
|
51
|
+
- `keywords list|get|export|generation-status|add|update|suggest|generate|bulk-reschedule|delete|bulk-delete`
|
|
52
52
|
- `articles list|get|get-content|update|update-content|replace|bulk-replace|upload-image|upload-featured-image|retry-publish|delete|precreate|generate`
|
|
53
|
+
- `improvements settings get|update`
|
|
54
|
+
- `improvements analyze`
|
|
55
|
+
- `improvements candidates list|brief|edit-brief|schedule`
|
|
56
|
+
- `improvements tasks list|reschedule|cancel|edit-brief|execute`
|
|
57
|
+
- `improvements executions list|get|decisions|push`
|
|
53
58
|
- `subscription status`
|
|
54
59
|
- `usage stats`
|
|
55
60
|
- `billing portal-url`
|
|
@@ -97,6 +102,7 @@ outrank-cli auth status
|
|
|
97
102
|
outrank-cli auth whoami
|
|
98
103
|
outrank-cli products list --limit 20
|
|
99
104
|
outrank-cli keywords list --product 00000000-0000-0000-0000-000000000000 --scope all
|
|
105
|
+
outrank-cli keywords list --product 00000000-0000-0000-0000-000000000000 --scope scheduled --q "best seo tools"
|
|
100
106
|
outrank-cli keywords generation-status --product 00000000-0000-0000-0000-000000000000
|
|
101
107
|
outrank-cli articles list --product 00000000-0000-0000-0000-000000000000 --status published
|
|
102
108
|
outrank-cli articles get 00000000-0000-0000-0000-000000000000
|
|
@@ -109,6 +115,11 @@ outrank-cli articles replace 00000000-0000-0000-0000-000000000000 --json '{"find
|
|
|
109
115
|
outrank-cli articles retry-publish 00000000-0000-0000-0000-000000000000
|
|
110
116
|
outrank-cli articles precreate 00000000-0000-0000-0000-000000000000
|
|
111
117
|
outrank-cli articles generate 00000000-0000-0000-0000-000000000000
|
|
118
|
+
outrank-cli improvements settings get 00000000-0000-0000-0000-000000000000
|
|
119
|
+
outrank-cli improvements candidates list 00000000-0000-0000-0000-000000000000 --type ctr_gap --limit 20
|
|
120
|
+
outrank-cli improvements candidates schedule 00000000-0000-0000-0000-000000000000 --scheduled-for 2026-06-01
|
|
121
|
+
outrank-cli improvements tasks execute 00000000-0000-0000-0000-000000000000
|
|
122
|
+
outrank-cli improvements executions push 00000000-0000-0000-0000-000000000000
|
|
112
123
|
outrank-cli subscription status
|
|
113
124
|
outrank-cli usage stats
|
|
114
125
|
outrank-cli products get 00000000-0000-0000-0000-000000000000
|
|
@@ -152,9 +163,10 @@ outrank-cli products integrations test 00000000-0000-0000-0000-000000000000 1111
|
|
|
152
163
|
outrank-cli keywords suggest --product 00000000-0000-0000-0000-000000000000 --theme "email marketing"
|
|
153
164
|
outrank-cli keywords generate --product 00000000-0000-0000-0000-000000000000 --count 10 --source both
|
|
154
165
|
outrank-cli keywords add --product 00000000-0000-0000-0000-000000000000 --json-file ./keywords.json
|
|
166
|
+
outrank-cli keywords update 00000000-0000-0000-0000-000000000000 --json '{"instructions":"Write for beginners and include a step-by-step setup section"}'
|
|
155
167
|
outrank-cli keywords export --product 00000000-0000-0000-0000-000000000000
|
|
156
168
|
outrank-cli products create --json-file ./product.json
|
|
157
|
-
# products create
|
|
169
|
+
# products create auto-queues the initial ~30-day keyword plan; poll with keywords generation-status / keywords list
|
|
158
170
|
outrank-cli billing portal-url
|
|
159
171
|
outrank-cli products update 00000000-0000-0000-0000-000000000000 --json '{"daily_article_count":2}'
|
|
160
172
|
outrank-cli products output-settings update 00000000-0000-0000-0000-000000000000 --json-file ./output-settings.json
|
|
@@ -188,7 +200,7 @@ outrank-cli products backlinks check-removal 00000000-0000-0000-0000-00000000000
|
|
|
188
200
|
outrank-cli products backlinks reverify 00000000-0000-0000-0000-000000000000 11111111-1111-1111-1111-111111111111
|
|
189
201
|
outrank-cli products backlinks subscription 00000000-0000-0000-0000-000000000000
|
|
190
202
|
outrank-cli products backlinks subscribe-quote 00000000-0000-0000-0000-000000000000 --plan premium
|
|
191
|
-
outrank-cli keywords
|
|
203
|
+
outrank-cli keywords update 00000000-0000-0000-0000-000000000000 --json '{"scheduled_for":"2026-05-20"}'
|
|
192
204
|
outrank-cli billing purchase-products quote --additional-products 2
|
|
193
205
|
```
|
|
194
206
|
|
|
@@ -272,13 +284,22 @@ Example payload:
|
|
|
272
284
|
## Keyword mutation rules
|
|
273
285
|
|
|
274
286
|
- Run `outrank-cli keywords generation-status --product <product-id>` before keyword writes when you are not sure the product is writable.
|
|
275
|
-
- `keywords generate`
|
|
287
|
+
- `keywords generate` mirrors the dashboard Generate Keywords flow. Ask the human how many keywords to generate before calling it if they did not specify a count.
|
|
276
288
|
- The allowed generation count is 5-30, but the real upper bound depends on free scheduler slots. Check `keywords generation-status` first when you need the current capacity.
|
|
277
289
|
- Stored keywords are read-only inventory. They can be promoted into the plan with `keywords add`, but they cannot be deleted directly.
|
|
278
290
|
- Scheduled keywords with created or pre-created articles are read-only.
|
|
279
291
|
- Keywords in `generating_article` are read-only until generation finishes.
|
|
280
292
|
- Past keywords are read-only except past `scheduled` or `disabled` keywords without an article, which can still be rescheduled.
|
|
281
293
|
- If the main subscription is `past_due`, keyword mutations are blocked and reads still work.
|
|
294
|
+
- `keywords list` defaults to planner-first ordering for scheduled results, so page 1 prioritizes upcoming scheduled items before past ones.
|
|
295
|
+
- Use `keywords list --product <product-id> --scope scheduled --q "<keyword>"` when looking for one planner keyword by text. If `next_offset` is not `null`, keep paging before claiming the keyword is absent.
|
|
296
|
+
- Keyword records return top-level `article_type`, `article_subtype`, `expected_length`, and label fields. Read those from the keyword itself when answering article-type or expected-length questions; do not look in product output-settings for per-keyword length.
|
|
297
|
+
- Use `keywords update <keyword-id> --json '{"instructions":"..."}'` to add or change per-keyword instructions after creation. This matches the dashboard flow, which creates the keyword first and updates the brief afterward.
|
|
298
|
+
- Use `keywords update <keyword-id> --json '{"quality_questions":[{"id":"...","question":"...","answer":"..."}]}'` to answer or replace the keyword's quality questions through the same PATCH route.
|
|
299
|
+
- Use `keywords update <keyword-id> --json '{"scheduled_for":"YYYY-MM-DD","instructions":"..."}'` when you need to change the date and brief in one PATCH request.
|
|
300
|
+
- Use `keywords update <keyword-id> --json '{"scheduled_for":"YYYY-MM-DD"}'` for date-only rescheduling too; there is no separate reschedule command.
|
|
301
|
+
- `keywords get <keyword-id>` returns `article_type_options`, which lists the valid dropdown values for `article_type`, `article_subtype`, and `expected_length`.
|
|
302
|
+
- Use `keywords update --json '{"article_type":"guide","article_subtype":"explainer","expected_length":"long"}'` to mirror the scheduler card's "Article Type Settings" modal. The API rejects values outside the returned option lists.
|
|
282
303
|
|
|
283
304
|
## Article behavior notes
|
|
284
305
|
|
|
@@ -286,13 +307,29 @@ Example payload:
|
|
|
286
307
|
- Featured image can be updated either by hosted URL through `articles update --json '{"image_url":"..."}'` or by local-file upload through `articles upload-featured-image`.
|
|
287
308
|
- In-content images can be uploaded from a local file with `articles upload-image`; the command returns the hosted URL plus generated alt text so the agent can insert or replace the markdown image reference.
|
|
288
309
|
- Per-article instructions are stored on the linked scheduled keyword, so `articles update --json '{"instructions":"..."}'` only works for articles that still have a linked planner keyword.
|
|
289
|
-
- `articles
|
|
310
|
+
- `articles get <article-id>` returns `article_type_options`, which lists the valid dropdown values for `article_type`, `article_subtype`, and `expected_length`.
|
|
311
|
+
- Article records with a linked scheduled keyword also return top-level `article_type`, `article_subtype`, `expected_length`, and label fields for direct reads.
|
|
312
|
+
- Use `articles update --json '{"article_type":"guide","article_subtype":"comparison","expected_length":"long"}'` to mirror the dashboard's "Article Type Settings" modal. The API rejects values outside the returned option lists.
|
|
313
|
+
- `articles update --json '{"status":"published"}'` attempts a real publish through the product's active integration for eligible attached articles; it is not just a local status flip.
|
|
290
314
|
- Slug is returned for read-only reference but cannot be changed through the agent API.
|
|
291
315
|
- `articles generate <keyword-id>` is only valid for scheduled keywords due today; it mirrors the dashboard's create-and-publish action.
|
|
292
316
|
- `articles precreate <keyword-id>` is only valid for eligible future scheduled keywords, is unavailable on trial subscriptions, and should be polled through the keyword state afterward.
|
|
293
317
|
- `articles update --json '{"status":"published"}'` cannot be used to manually publish a future scheduled article before its due date.
|
|
294
318
|
- `articles retry-publish <article-id>` is only valid for generated articles with a publish error.
|
|
295
319
|
|
|
320
|
+
## Article improvements behavior notes
|
|
321
|
+
|
|
322
|
+
- Article improvements are a separate surface from generation: they rewrite an already-published post based on Search Console signals, then push the result to the CMS. They require the All-in-One plan. On Basic plan, `improvements candidates list` returns an empty list and write actions return `PLAN_UPGRADE_REQUIRED`.
|
|
323
|
+
- `improvements settings update --json '{"enabled":true}'` starts an unattended loop: weekly opportunity discovery, daily auto-scheduling on `publishing_days` (at most 2 weekday codes from Mon–Sun), and execution every ~15 minutes. Surface this to the human before enabling.
|
|
324
|
+
- `improvements settings update --json '{"auto_push":true}'` publishes every successful rewrite to the CMS without human review (still skipped automatically on a hard validation failure or an unverified backlink injection).
|
|
325
|
+
- `improvements settings get` returns `readiness`, which says whether the connected CMS can actually receive a pushed update (`ready`) or not (`not_connected`, `action_required`, `unsupported`). A push to an unsupported/disconnected CMS is reported as `skipped` with `requires_manual_publish`.
|
|
326
|
+
- `improvements analyze`, `improvements candidates brief`, and `improvements tasks execute` run in Firebase and can take minutes. If the call times out at the network edge, the work still completes server-side — re-list candidates or poll the execution rather than retrying.
|
|
327
|
+
- After `improvements candidates brief`, poll the candidate (via `improvements candidates list`) until its `brief_status` is `ready`. After `improvements tasks execute`, poll `improvements executions get <execution-id>` until `status` is `completed`, then read `review_status`.
|
|
328
|
+
- A candidate (or its underlying article) can have only one active scheduled task at a time, and an article can have only one active (pending/running) execution. Conflicts return `409`.
|
|
329
|
+
- `improvements executions get` returns `before`/`after`, paragraph-level `hunks` (each with an id), and `validation_issues`. Push with no JSON to accept all changes; send `{"decisions":{"<hunkId>":"rejected"},"edited_hunks":{"<hunkId>":"..."}}` to reject or edit specific hunks. `decisions` and `push` only work while `review_status` is `awaiting_review`.
|
|
330
|
+
- A push updates the article inside Outrank first, then dispatches to the CMS. `CMS_PUSH_FAILED` means the content was saved in Outrank but the CMS rejected it; inspect `error.details.cms_response` and retry.
|
|
331
|
+
- `improvements candidates list` and `improvements executions list` use offset pagination through `data.next_offset`.
|
|
332
|
+
|
|
296
333
|
## Linking behavior notes
|
|
297
334
|
|
|
298
335
|
- Linking writes are blocked unless the product has an active backlink subscription.
|
|
@@ -337,7 +374,7 @@ Current scope note:
|
|
|
337
374
|
|
|
338
375
|
- Webhook, Ghost, Shopify, Webflow, and Wix support direct validate/create/update/test. Framer supports validate, provider-specific discovery, create, and test for the newer Server API flow. WordPress Plugin supports prepare plus validate/create/update/test. Next.js Blog supports one-step create plus the shared list|get|delete surface, returning a one-time API key on create. WordPress.com supports OAuth connect plus poll, then the shared list|get|delete surface. Notion supports OAuth connect plus poll, provider-specific database/schema discovery, then update/test on the saved row.
|
|
339
376
|
- V2 products are intended to keep one publishing integration. If `list` returns multiple rows, that usually means legacy V1 residue and stale rows should be reviewed and deleted.
|
|
340
|
-
- Backlink exchange management
|
|
377
|
+
- Backlink exchange management covers configuration, status reads, earned/unverified review queues, and reverification actions. Purchasing or changing backlink add-ons still requires the dashboard checkout flow.
|
|
341
378
|
- Other providers are still mostly read/delete only while create, update, test, and provider-specific discovery are added one by one.
|
|
342
379
|
|
|
343
380
|
## Custom API target (advanced)
|
package/SKILL.md
CHANGED
|
@@ -1,53 +1,60 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: outrank
|
|
3
|
-
description: Drive the Outrank agent API from the terminal via outrank-cli. Use when the user asks to list/create/update Outrank products, inspect or manage publishing integrations, inspect or manage keyword plans, inspect or edit articles, manage article settings, target audiences, competitors, Google Search Console connections, internal linking configuration, or quote/buy product slots. Requires outrank-cli on PATH and an Outrank API key (env OUTRANK_API_KEY or stored via auth login).
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Outrank Skill
|
|
7
|
-
|
|
8
|
-
Use `outrank-cli` to drive Outrank from the terminal. The CLI is JSON-first and every command returns `agent_guidance`, so read that field after each step and let it steer the next call.
|
|
9
|
-
|
|
1
|
+
---
|
|
2
|
+
name: outrank
|
|
3
|
+
description: Drive the Outrank agent API from the terminal via outrank-cli. Use when the user asks to list/create/update Outrank products, inspect or manage publishing integrations, inspect or manage keyword plans, inspect or edit articles, discover/schedule/run/review/publish article improvements for existing posts, manage article settings, target audiences, competitors, Google Search Console connections, internal linking configuration, or quote/buy product slots. Requires outrank-cli on PATH and an Outrank API key (env OUTRANK_API_KEY or stored via auth login).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Outrank Skill
|
|
7
|
+
|
|
8
|
+
Use `outrank-cli` to drive Outrank from the terminal. The CLI is JSON-first and every command returns `agent_guidance`, so read that field after each step and let it steer the next call.
|
|
9
|
+
|
|
10
10
|
In human-facing responses, call `products get` / `products update` the product's General settings. These cover the non-editorial product basics such as name, URL, description, sitemap URL, blog root URL, publishing cadence, and similar core product fields.
|
|
11
11
|
|
|
12
12
|
In human-facing responses, call product `output-settings` article settings or article preferences. Use `output-settings` only when you are quoting the literal CLI command or API path.
|
|
13
13
|
Use `products backlinks ...` for backlink exchange settings, credits, review queues, and reverification actions.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
|
|
15
|
+
Use `improvements ...` for article improvements: discovering, briefing, scheduling, running, reviewing, and publishing AI rewrites of **existing published articles** based on Search Console performance. This is a distinct area from article generation (`articles generate`/`precreate`) — offer it whenever the human wants to refresh, update, optimize, or improve existing/older posts, or act on GSC opportunities. It needs the All-in-One plan.
|
|
16
|
+
|
|
17
|
+
## Setup
|
|
18
|
+
|
|
17
19
|
1. If `OUTRANK_API_KEY` is not set, ask the human for an Outrank API key from `https://outrank.so/dashboard/settings/api-keys`.
|
|
18
|
-
2. Store it with `outrank-cli auth login <api-key>`.
|
|
19
|
-
3. Verify access with `outrank-cli auth whoami`.
|
|
20
|
-
4. If a command fails with auth errors, tell the human exactly how to refresh the key, then retry.
|
|
21
|
-
5. If the environment blocks writes to the default home directory, set `OUTRANK_CONFIG_DIR` to a writable folder before using `auth login`.
|
|
20
|
+
2. Store it with `outrank-cli auth login <api-key>`.
|
|
21
|
+
3. Verify access with `outrank-cli auth whoami`.
|
|
22
|
+
4. If a command fails with auth errors, tell the human exactly how to refresh the key, then retry.
|
|
23
|
+
5. If the environment blocks writes to the default home directory, set `OUTRANK_CONFIG_DIR` to a writable folder before using `auth login`.
|
|
22
24
|
6. If the API is hosted on a non-default Outrank deployment, set `OUTRANK_BASE_URL=<deployment-url>`. Use `--base-url <deployment-url>` only as a per-command override when needed.
|
|
23
|
-
|
|
24
|
-
## Discovery
|
|
25
|
-
|
|
26
|
-
Run `outrank-cli --help` first if you need the command tree.
|
|
27
|
-
|
|
28
|
-
Run group help when needed:
|
|
29
|
-
|
|
30
|
-
- `outrank-cli auth --help`
|
|
31
|
-
- `outrank-cli products --help`
|
|
32
|
-
- `outrank-cli keywords --help`
|
|
33
|
-
- `outrank-cli articles --help`
|
|
34
|
-
- `outrank-cli
|
|
35
|
-
- `outrank-cli
|
|
36
|
-
- `outrank-cli
|
|
37
|
-
- `outrank-cli
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
- `outrank-cli products
|
|
43
|
-
- `outrank-cli products
|
|
44
|
-
- `outrank-cli
|
|
45
|
-
- `outrank-cli
|
|
46
|
-
- `outrank-cli articles
|
|
47
|
-
- `outrank-cli articles get
|
|
48
|
-
- `outrank-cli articles
|
|
49
|
-
- `outrank-cli articles upload-
|
|
50
|
-
- `outrank-cli
|
|
25
|
+
|
|
26
|
+
## Discovery
|
|
27
|
+
|
|
28
|
+
Run `outrank-cli --help` first if you need the command tree.
|
|
29
|
+
|
|
30
|
+
Run group help when needed:
|
|
31
|
+
|
|
32
|
+
- `outrank-cli auth --help`
|
|
33
|
+
- `outrank-cli products --help`
|
|
34
|
+
- `outrank-cli keywords --help`
|
|
35
|
+
- `outrank-cli articles --help`
|
|
36
|
+
- `outrank-cli improvements --help`
|
|
37
|
+
- `outrank-cli subscription --help`
|
|
38
|
+
- `outrank-cli usage --help`
|
|
39
|
+
- `outrank-cli billing --help`
|
|
40
|
+
- `outrank-cli auth status`
|
|
41
|
+
|
|
42
|
+
Prefer inspecting before mutating:
|
|
43
|
+
|
|
44
|
+
- `outrank-cli products list --limit 20`
|
|
45
|
+
- `outrank-cli products get <productId>` for General settings
|
|
46
|
+
- `outrank-cli products output-settings get <productId>`
|
|
47
|
+
- `outrank-cli keywords list --product <productId> --scope all`
|
|
48
|
+
- `outrank-cli articles list --product <productId>`
|
|
49
|
+
- `outrank-cli articles get <articleId>`
|
|
50
|
+
- `outrank-cli articles get-content <articleId>`
|
|
51
|
+
- `outrank-cli articles upload-image --file <path> --article <articleId>`
|
|
52
|
+
- `outrank-cli articles upload-featured-image <articleId> --file <path>`
|
|
53
|
+
- `outrank-cli improvements settings get <productId>`
|
|
54
|
+
- `outrank-cli improvements candidates list <productId>`
|
|
55
|
+
- `outrank-cli improvements tasks list <productId>`
|
|
56
|
+
- `outrank-cli improvements executions list <productId>`
|
|
57
|
+
- `outrank-cli keywords export --product <productId>`
|
|
51
58
|
- `outrank-cli products target-audiences list <productId>`
|
|
52
59
|
- `outrank-cli products competitors list <productId>`
|
|
53
60
|
- `outrank-cli products integrations providers`
|
|
@@ -79,26 +86,26 @@ Prefer inspecting before mutating:
|
|
|
79
86
|
- `outrank-cli products backlinks subscription <productId>`
|
|
80
87
|
- `outrank-cli subscription status`
|
|
81
88
|
- `outrank-cli usage stats`
|
|
82
|
-
|
|
83
|
-
Interpret `auth whoami` carefully:
|
|
84
|
-
|
|
85
|
-
- `organization.product_count` may represent purchased product slots, not the literal number of rows returned by `products list`.
|
|
86
|
-
|
|
87
|
-
## Write pattern
|
|
88
|
-
|
|
89
|
-
For complex writes, prefer JSON via file or stdin over long shell escaping.
|
|
90
|
-
|
|
91
|
-
Accepted input forms:
|
|
92
|
-
|
|
93
|
-
- `--json '{...}'`
|
|
94
|
-
- `--json-file payload.json`
|
|
95
|
-
- `--json-stdin`
|
|
96
|
-
|
|
97
|
-
Write commands currently supported:
|
|
98
|
-
|
|
99
|
-
- `outrank-cli products create --json-file product.json`
|
|
100
|
-
After create, the initial ~30-day keyword plan is queued automatically. Poll with `outrank-cli keywords generation-status --product <productId>` and then inspect `outrank-cli keywords list --product <productId>`.
|
|
101
|
-
- `outrank-cli products update <productId> --json-file patch.json` for General settings
|
|
89
|
+
|
|
90
|
+
Interpret `auth whoami` carefully:
|
|
91
|
+
|
|
92
|
+
- `organization.product_count` may represent purchased product slots, not the literal number of rows returned by `products list`.
|
|
93
|
+
|
|
94
|
+
## Write pattern
|
|
95
|
+
|
|
96
|
+
For complex writes, prefer JSON via file or stdin over long shell escaping.
|
|
97
|
+
|
|
98
|
+
Accepted input forms:
|
|
99
|
+
|
|
100
|
+
- `--json '{...}'`
|
|
101
|
+
- `--json-file payload.json`
|
|
102
|
+
- `--json-stdin`
|
|
103
|
+
|
|
104
|
+
Write commands currently supported:
|
|
105
|
+
|
|
106
|
+
- `outrank-cli products create --json-file product.json`
|
|
107
|
+
After create, the initial ~30-day keyword plan is queued automatically. Poll with `outrank-cli keywords generation-status --product <productId>` and then inspect `outrank-cli keywords list --product <productId>`.
|
|
108
|
+
- `outrank-cli products update <productId> --json-file patch.json` for General settings
|
|
102
109
|
- `outrank-cli products output-settings update <productId> --json-file output-settings.json`
|
|
103
110
|
- `outrank-cli products target-audiences replace <productId> --json-file target-audiences.json`
|
|
104
111
|
- `outrank-cli products competitors replace <productId> --json-file competitors.json`
|
|
@@ -138,112 +145,113 @@ Write commands currently supported:
|
|
|
138
145
|
- `outrank-cli products backlinks reverify <productId> <backlinkId>`
|
|
139
146
|
- `outrank-cli products backlinks subscribe-quote <productId> --plan premium|enterprise`
|
|
140
147
|
- `outrank-cli keywords add --product <productId> --json-file keywords.json`
|
|
148
|
+
- `outrank-cli keywords update <keywordId> --json-file keyword-update.json`
|
|
141
149
|
- `outrank-cli keywords bulk-reschedule --json-file keyword-reschedule.json`
|
|
142
150
|
- `outrank-cli keywords bulk-delete --json-file keyword-delete.json`
|
|
143
|
-
- `outrank-cli articles update <articleId> --json-file article-patch.json`
|
|
144
|
-
- `outrank-cli articles update-content <articleId> --json-file article-content.json`
|
|
145
|
-
- `outrank-cli articles replace <articleId> --json-file replace.json`
|
|
146
|
-
- `outrank-cli articles bulk-replace --json-file bulk-replace.json`
|
|
147
|
-
- `outrank-cli articles upload-image --file local-image.png --article <articleId>`
|
|
148
|
-
- `outrank-cli articles upload-featured-image <articleId> --file hero.jpg`
|
|
149
|
-
|
|
150
|
-
Billing-management helper:
|
|
151
|
-
|
|
152
|
-
- `outrank-cli billing portal-url`
|
|
153
|
-
|
|
154
|
-
## New-product preflight
|
|
155
|
-
|
|
156
|
-
When the human asks to create or add a new product, do not jump straight to `products create` or a billing purchase.
|
|
157
|
-
|
|
158
|
-
Treat product creation as blocked until you have either:
|
|
159
|
-
|
|
160
|
-
1. Collected the key product and article-settings inputs, or
|
|
161
|
-
2. Explicit human approval to keep the current defaults for anything they did not specify.
|
|
162
|
-
|
|
163
|
-
Ask for missing information explicitly. Do not silently rely on defaults for editorial behavior.
|
|
164
|
-
When the account already has products, inspect at least one existing product's article settings first so you can see the current backend's actual setting keys before you ask the human to configure a new one.
|
|
165
|
-
|
|
166
|
-
Use this inspection pattern before asking article-setting questions:
|
|
167
|
-
|
|
168
|
-
- `outrank-cli products list --limit 20`
|
|
169
|
-
- `outrank-cli products output-settings get <existingProductId>`
|
|
170
|
-
|
|
171
|
-
Use the returned `output-settings` object as the authoritative checklist of fields currently visible through the live API for that account. Do not rely on a hardcoded short list if the live object contains more agent-visible settings. Do not infer dashboard-only or internal generation fields that are intentionally hidden from the agent API, including `outline`, `outrank_prompt`, and `not_included_words`.
|
|
172
|
-
|
|
173
|
-
When the response includes `data._meta`:
|
|
174
|
-
|
|
175
|
-
- if `data._human` is present, use that as the default source for any human-facing summary
|
|
176
|
-
- use `data._meta.sections` to decide which settings to show humans and how to group them
|
|
177
|
-
- use `data._meta.fields.<field>.label` and `.description` instead of inventing meanings from snake_case names
|
|
178
|
-
- use `data._meta.fields.<field>.options` for enum choices and `constraints` for numeric/color limits before proposing or writing a new value
|
|
179
|
-
- treat `data._meta.hidden_from_human_summary_fields` as bookkeeping fields that usually should not be surfaced in prose
|
|
180
|
-
- if a field is missing from `data._meta.fields`, do not guess what it means; say the live API did not define that field's semantics clearly
|
|
181
|
-
- never mention database table names, raw API field names, raw enum keys, or internal ids in normal human-facing explanations unless the user explicitly asks for implementation details
|
|
182
|
-
|
|
183
|
-
Minimum product brief to collect before creation:
|
|
184
|
-
|
|
185
|
-
- product name
|
|
186
|
-
- canonical website URL
|
|
187
|
-
- product description
|
|
188
|
-
- target audiences
|
|
189
|
-
- competitor set
|
|
190
|
-
- language
|
|
191
|
-
- keyword region
|
|
192
|
-
|
|
193
|
-
Article settings to confirm before creation or immediately before the first write:
|
|
194
|
-
|
|
195
|
-
- tone and style
|
|
196
|
-
- whether to auto-publish
|
|
197
|
-
- whether to include a table of contents
|
|
198
|
-
- whether to mention similar products
|
|
199
|
-
- whether to include CTAs
|
|
200
|
-
- whether to include YouTube videos, if that field exists in the inspected article settings
|
|
201
|
-
- featured-image style, article-image style, or similar image fields, if those fields exist in the inspected article settings
|
|
202
|
-
|
|
203
|
-
The list above is a minimum reminder, not the full agent-visible schema. If the inspected live output-settings object includes additional visible fields, ask about those too. Do not ask the human to configure hidden internal fields that are absent from the live object.
|
|
204
|
-
|
|
205
|
-
For preset-backed fields, do not ask for free-form prose unless the backend clearly accepts arbitrary text. Many settings are select-style options. Examples may include tone, image style, or featured-image style.
|
|
206
|
-
|
|
207
|
-
If the live API or current repo context does not expose the full allowed option list for a preset-backed field:
|
|
208
|
-
|
|
209
|
-
- do not pretend the value is free-form
|
|
210
|
-
- ask the human for the exact option label they want as it appears in Outrank
|
|
211
|
-
- if helpful, show the current value from an existing product as a reference point
|
|
212
|
-
- say clearly when the CLI exposes the field's current value but not the full enum catalog
|
|
213
|
-
|
|
151
|
+
- `outrank-cli articles update <articleId> --json-file article-patch.json`
|
|
152
|
+
- `outrank-cli articles update-content <articleId> --json-file article-content.json`
|
|
153
|
+
- `outrank-cli articles replace <articleId> --json-file replace.json`
|
|
154
|
+
- `outrank-cli articles bulk-replace --json-file bulk-replace.json`
|
|
155
|
+
- `outrank-cli articles upload-image --file local-image.png --article <articleId>`
|
|
156
|
+
- `outrank-cli articles upload-featured-image <articleId> --file hero.jpg`
|
|
157
|
+
|
|
158
|
+
Billing-management helper:
|
|
159
|
+
|
|
160
|
+
- `outrank-cli billing portal-url`
|
|
161
|
+
|
|
162
|
+
## New-product preflight
|
|
163
|
+
|
|
164
|
+
When the human asks to create or add a new product, do not jump straight to `products create` or a billing purchase.
|
|
165
|
+
|
|
166
|
+
Treat product creation as blocked until you have either:
|
|
167
|
+
|
|
168
|
+
1. Collected the key product and article-settings inputs, or
|
|
169
|
+
2. Explicit human approval to keep the current defaults for anything they did not specify.
|
|
170
|
+
|
|
171
|
+
Ask for missing information explicitly. Do not silently rely on defaults for editorial behavior.
|
|
172
|
+
When the account already has products, inspect at least one existing product's article settings first so you can see the current backend's actual setting keys before you ask the human to configure a new one.
|
|
173
|
+
|
|
174
|
+
Use this inspection pattern before asking article-setting questions:
|
|
175
|
+
|
|
176
|
+
- `outrank-cli products list --limit 20`
|
|
177
|
+
- `outrank-cli products output-settings get <existingProductId>`
|
|
178
|
+
|
|
179
|
+
Use the returned `output-settings` object as the authoritative checklist of fields currently visible through the live API for that account. Do not rely on a hardcoded short list if the live object contains more agent-visible settings. Do not infer dashboard-only or internal generation fields that are intentionally hidden from the agent API, including `outline`, `outrank_prompt`, and `not_included_words`.
|
|
180
|
+
|
|
181
|
+
When the response includes `data._meta`:
|
|
182
|
+
|
|
183
|
+
- if `data._human` is present, use that as the default source for any human-facing summary
|
|
184
|
+
- use `data._meta.sections` to decide which settings to show humans and how to group them
|
|
185
|
+
- use `data._meta.fields.<field>.label` and `.description` instead of inventing meanings from snake_case names
|
|
186
|
+
- use `data._meta.fields.<field>.options` for enum choices and `constraints` for numeric/color limits before proposing or writing a new value
|
|
187
|
+
- treat `data._meta.hidden_from_human_summary_fields` as bookkeeping fields that usually should not be surfaced in prose
|
|
188
|
+
- if a field is missing from `data._meta.fields`, do not guess what it means; say the live API did not define that field's semantics clearly
|
|
189
|
+
- never mention database table names, raw API field names, raw enum keys, or internal ids in normal human-facing explanations unless the user explicitly asks for implementation details
|
|
190
|
+
|
|
191
|
+
Minimum product brief to collect before creation:
|
|
192
|
+
|
|
193
|
+
- product name
|
|
194
|
+
- canonical website URL
|
|
195
|
+
- product description
|
|
196
|
+
- target audiences
|
|
197
|
+
- competitor set
|
|
198
|
+
- language
|
|
199
|
+
- keyword region
|
|
200
|
+
|
|
201
|
+
Article settings to confirm before creation or immediately before the first write:
|
|
202
|
+
|
|
203
|
+
- tone and style
|
|
204
|
+
- whether to auto-publish
|
|
205
|
+
- whether to include a table of contents
|
|
206
|
+
- whether to mention similar products
|
|
207
|
+
- whether to include CTAs
|
|
208
|
+
- whether to include YouTube videos, if that field exists in the inspected article settings
|
|
209
|
+
- featured-image style, article-image style, or similar image fields, if those fields exist in the inspected article settings
|
|
210
|
+
|
|
211
|
+
The list above is a minimum reminder, not the full agent-visible schema. If the inspected live output-settings object includes additional visible fields, ask about those too. Do not ask the human to configure hidden internal fields that are absent from the live object.
|
|
212
|
+
|
|
213
|
+
For preset-backed fields, do not ask for free-form prose unless the backend clearly accepts arbitrary text. Many settings are select-style options. Examples may include tone, image style, or featured-image style.
|
|
214
|
+
|
|
215
|
+
If the live API or current repo context does not expose the full allowed option list for a preset-backed field:
|
|
216
|
+
|
|
217
|
+
- do not pretend the value is free-form
|
|
218
|
+
- ask the human for the exact option label they want as it appears in Outrank
|
|
219
|
+
- if helpful, show the current value from an existing product as a reference point
|
|
220
|
+
- say clearly when the CLI exposes the field's current value but not the full enum catalog
|
|
221
|
+
|
|
214
222
|
Business-critical settings that may matter before article generation:
|
|
215
|
-
|
|
216
|
-
- blog URL or publishing destination
|
|
217
|
-
- internal linking preferences
|
|
218
|
-
- image style or image-generation preferences
|
|
219
|
-
- article examples or brand-writing examples
|
|
220
|
-
- publishing workflow preferences such as draft-first vs direct publish
|
|
221
|
-
|
|
222
|
-
If the human has not provided those business-critical settings yet, say so clearly. Explain which of them are currently exposed by `outrank-cli` and which are not. Internal linking is
|
|
223
|
-
|
|
224
|
-
- proceed with existing/default Outrank settings for now, or
|
|
225
|
-
- stop product creation until those settings are provided and/or configured elsewhere
|
|
226
|
-
|
|
227
|
-
Before any billing or create step, present a short confirmation summary that includes:
|
|
228
|
-
|
|
229
|
-
- what will be created
|
|
230
|
-
- what competitors and audiences will be set
|
|
231
|
-
- which article settings will be written
|
|
232
|
-
- which fields were confirmed from the live output-settings schema versus inferred from docs/examples
|
|
233
|
-
- which important settings are still defaulting because they were not provided or are not yet exposed in the CLI
|
|
234
|
-
- whether a real billing charge approval is still required
|
|
235
|
-
|
|
236
|
-
## Product workflow
|
|
237
|
-
|
|
223
|
+
|
|
224
|
+
- blog URL or publishing destination
|
|
225
|
+
- internal linking preferences
|
|
226
|
+
- image style or image-generation preferences
|
|
227
|
+
- article examples or brand-writing examples
|
|
228
|
+
- publishing workflow preferences such as draft-first vs direct publish
|
|
229
|
+
|
|
230
|
+
If the human has not provided those business-critical settings yet, say so clearly. Explain which of them are currently exposed by `outrank-cli` and which are not. Internal linking is exposed through the `products linking ...` subtree. For settings that are not yet supported by the current CLI surface, do not invent commands. Instead, ask the human whether to:
|
|
231
|
+
|
|
232
|
+
- proceed with existing/default Outrank settings for now, or
|
|
233
|
+
- stop product creation until those settings are provided and/or configured elsewhere
|
|
234
|
+
|
|
235
|
+
Before any billing or create step, present a short confirmation summary that includes:
|
|
236
|
+
|
|
237
|
+
- what will be created
|
|
238
|
+
- what competitors and audiences will be set
|
|
239
|
+
- which article settings will be written
|
|
240
|
+
- which fields were confirmed from the live output-settings schema versus inferred from docs/examples
|
|
241
|
+
- which important settings are still defaulting because they were not provided or are not yet exposed in the CLI
|
|
242
|
+
- whether a real billing charge approval is still required
|
|
243
|
+
|
|
244
|
+
## Product workflow
|
|
245
|
+
|
|
238
246
|
For a new product:
|
|
239
|
-
|
|
240
|
-
1. Gather the new-product preflight inputs above. If anything important is missing, ask the human before continuing.
|
|
241
|
-
2. If another paid product slot is needed, quote the billing change before creating anything.
|
|
242
|
-
3. After the human approves the plan, create it with `products create`.
|
|
243
|
-
4. Read the returned `agent_guidance`.
|
|
244
|
-
5. Tune article behavior with `products output-settings update`.
|
|
245
|
-
6. Replace the target-audience set with `products target-audiences replace`.
|
|
246
|
-
7. Replace the competitor set with `products competitors replace`.
|
|
247
|
+
|
|
248
|
+
1. Gather the new-product preflight inputs above. If anything important is missing, ask the human before continuing.
|
|
249
|
+
2. If another paid product slot is needed, quote the billing change before creating anything.
|
|
250
|
+
3. After the human approves the plan, create it with `products create`.
|
|
251
|
+
4. Read the returned `agent_guidance`.
|
|
252
|
+
5. Tune article behavior with `products output-settings update`.
|
|
253
|
+
6. Replace the target-audience set with `products target-audiences replace`.
|
|
254
|
+
7. Replace the competitor set with `products competitors replace`.
|
|
247
255
|
8. Re-read the product, article settings, target audiences, and competitors to confirm the final state.
|
|
248
256
|
|
|
249
257
|
When you only need a small change, use `products update` rather than recreating the product.
|
|
@@ -371,22 +379,22 @@ Important behavior:
|
|
|
371
379
|
- the current CLI surface still does not expose create, update, or test for some providers, and only WordPress.com and Notion currently expose an OAuth connect flow; if the human asks for unsupported operations, say they are not shipped in this CLI yet instead of inventing commands
|
|
372
380
|
|
|
373
381
|
## Keyword workflow
|
|
374
|
-
|
|
375
|
-
Keyword plans
|
|
376
|
-
|
|
377
|
-
- scheduled keywords: already on the content calendar
|
|
378
|
-
- stored keywords: unscheduled inventory waiting to be promoted into the plan
|
|
379
|
-
|
|
380
|
-
Inspect before mutating:
|
|
381
|
-
|
|
382
|
-
- `outrank-cli keywords list --product <productId> --scope scheduled`
|
|
383
|
-
- `outrank-cli keywords list --product <productId> --scope stored`
|
|
384
|
-
- `outrank-cli keywords list --product <productId> --scope all`
|
|
385
|
-
- `outrank-cli keywords export --product <productId>`
|
|
386
|
-
- `outrank-cli keywords generation-status --product <productId>`
|
|
387
|
-
|
|
388
|
-
Use the returned `data.scope`, `data.scheduled`, and `data.stored` structure literally. Do not invent a different keyword model.
|
|
389
|
-
|
|
382
|
+
|
|
383
|
+
Keyword plans have two buckets:
|
|
384
|
+
|
|
385
|
+
- scheduled keywords: already on the content calendar
|
|
386
|
+
- stored keywords: unscheduled inventory waiting to be promoted into the plan
|
|
387
|
+
|
|
388
|
+
Inspect before mutating:
|
|
389
|
+
|
|
390
|
+
- `outrank-cli keywords list --product <productId> --scope scheduled`
|
|
391
|
+
- `outrank-cli keywords list --product <productId> --scope stored`
|
|
392
|
+
- `outrank-cli keywords list --product <productId> --scope all`
|
|
393
|
+
- `outrank-cli keywords export --product <productId>`
|
|
394
|
+
- `outrank-cli keywords generation-status --product <productId>`
|
|
395
|
+
|
|
396
|
+
Use the returned `data.scope`, `data.scheduled`, and `data.stored` structure literally. Do not invent a different keyword model.
|
|
397
|
+
|
|
390
398
|
When the user asks for keyword ideas:
|
|
391
399
|
|
|
392
400
|
- use `outrank-cli keywords suggest --product <productId> --theme "<theme>"` for related-keyword exploration around a theme or seed term
|
|
@@ -400,96 +408,135 @@ Before `keywords generate`:
|
|
|
400
408
|
- if the human did not specify how many keywords to generate, ask them
|
|
401
409
|
- the allowed range is 5-30, but the real maximum depends on free scheduler slots
|
|
402
410
|
- check `outrank-cli keywords generation-status --product <productId>` when you need the current slot capacity
|
|
403
|
-
|
|
404
|
-
To add keywords:
|
|
405
|
-
|
|
406
|
-
- for direct adds, use `outrank-cli keywords add --product <productId> --json-file keywords.json`
|
|
407
|
-
- for promoting existing stored keywords, include `stored_keyword_ids` in that JSON payload
|
|
408
|
-
|
|
409
|
-
Important behavior to remember:
|
|
410
|
-
|
|
411
|
-
- add is idempotent where possible; existing scheduled keywords come back as existing rather than being duplicated
|
|
412
|
-
- stored keywords are unscheduled inventory, not disabled scheduled keywords
|
|
413
|
-
- stored keywords are read-only inventory; they can be promoted into the plan but not deleted directly
|
|
414
|
-
- auto-scheduling respects the backend's real planner logic, including publish days and pending downgrade limits
|
|
415
|
-
- if keyword generation status says writes are blocked, treat both scheduled and stored keywords as read-only until the blocker clears
|
|
416
|
-
- if the main subscription is `past_due`, keyword mutations are blocked and only reads are allowed
|
|
417
|
-
- scheduled keywords with created or pre-created articles are read-only
|
|
418
|
-
- keywords in `generating_article` are read-only until generation finishes
|
|
419
|
-
- past keywords are read-only except past `scheduled` or `disabled` keywords without an article, which may still be rescheduled
|
|
420
|
-
- if the response reports skipped keywords, explain the actual skip reason to the human instead of pretending the add fully succeeded
|
|
421
|
-
|
|
422
|
-
For schedule changes:
|
|
423
|
-
|
|
424
|
-
- check writability first when there is any doubt: `outrank-cli keywords generation-status --product <productId>`
|
|
425
|
-
-
|
|
426
|
-
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
- `outrank-cli
|
|
431
|
-
- `outrank-cli
|
|
432
|
-
-
|
|
433
|
-
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
-
|
|
439
|
-
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
- `outrank-cli
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
- `outrank-cli
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
411
|
+
|
|
412
|
+
To add keywords:
|
|
413
|
+
|
|
414
|
+
- for direct adds, use `outrank-cli keywords add --product <productId> --json-file keywords.json`
|
|
415
|
+
- for promoting existing stored keywords, include `stored_keyword_ids` in that JSON payload
|
|
416
|
+
|
|
417
|
+
Important behavior to remember:
|
|
418
|
+
|
|
419
|
+
- add is idempotent where possible; existing scheduled keywords come back as existing rather than being duplicated
|
|
420
|
+
- stored keywords are unscheduled inventory, not disabled scheduled keywords
|
|
421
|
+
- stored keywords are read-only inventory; they can be promoted into the plan but not deleted directly
|
|
422
|
+
- auto-scheduling respects the backend's real planner logic, including publish days and pending downgrade limits
|
|
423
|
+
- if keyword generation status says writes are blocked, treat both scheduled and stored keywords as read-only until the blocker clears
|
|
424
|
+
- if the main subscription is `past_due`, keyword mutations are blocked and only reads are allowed
|
|
425
|
+
- scheduled keywords with created or pre-created articles are read-only
|
|
426
|
+
- keywords in `generating_article` are read-only until generation finishes
|
|
427
|
+
- past keywords are read-only except past `scheduled` or `disabled` keywords without an article, which may still be rescheduled
|
|
428
|
+
- if the response reports skipped keywords, explain the actual skip reason to the human instead of pretending the add fully succeeded
|
|
429
|
+
|
|
430
|
+
For schedule changes:
|
|
431
|
+
|
|
432
|
+
- check writability first when there is any doubt: `outrank-cli keywords generation-status --product <productId>`
|
|
433
|
+
- `keywords list` uses planner-first ordering for scheduled results, so page 1 prioritizes upcoming scheduled items before past ones
|
|
434
|
+
- confirm keyword presence with `outrank-cli keywords list --product <productId> --scope scheduled --q "<keyword>"`; if `next_offset` is not `null`, keep paging before claiming it is missing
|
|
435
|
+
- keyword records return top-level `article_type`, `article_subtype`, `expected_length`, and label fields; use those directly for article-type or expected-length questions instead of checking product output-settings
|
|
436
|
+
- update one keyword's mutable fields: `outrank-cli keywords update <keywordId> --json '{"instructions":"..."}'`
|
|
437
|
+
- answer or replace the keyword's quality questions with `outrank-cli keywords update <keywordId> --json '{"quality_questions":[{"id":"...","question":"...","answer":"..."}]}'`
|
|
438
|
+
- `outrank-cli keywords get <keywordId>` returns `article_type_options`; use those values when patching `article_type`, `article_subtype`, or `expected_length`
|
|
439
|
+
- `outrank-cli keywords update <keywordId> --json '{"article_type":"guide","article_subtype":"explainer","expected_length":"long"}'` mirrors the scheduler card Article Type Settings modal and rejects values outside the allowed option lists
|
|
440
|
+
- reschedule one: `outrank-cli keywords update <keywordId> --json '{"scheduled_for":"YYYY-MM-DD"}'`
|
|
441
|
+
- reschedule many: `outrank-cli keywords bulk-reschedule --json-file keyword-reschedule.json`
|
|
442
|
+
|
|
443
|
+
For article actions:
|
|
444
|
+
|
|
445
|
+
- `outrank-cli articles generate <keywordId>` is only for keywords scheduled for today
|
|
446
|
+
- `outrank-cli articles precreate <keywordId>` is only for eligible future scheduled keywords
|
|
447
|
+
- article records with a linked scheduled keyword also return top-level `article_type`, `article_subtype`, `expected_length`, and label fields
|
|
448
|
+
- trial users cannot pre-create articles
|
|
449
|
+
- past-due subscriptions block article actions
|
|
450
|
+
- future scheduled articles cannot be manually published early through `articles update`
|
|
451
|
+
|
|
452
|
+
For deletion:
|
|
453
|
+
|
|
454
|
+
- delete one: `outrank-cli keywords delete <keywordId>`
|
|
455
|
+
- delete many: `outrank-cli keywords bulk-delete --json-file keyword-delete.json`
|
|
456
|
+
|
|
457
|
+
When answering humans, call the scheduled set the content plan or keyword plan. Call stored keywords unscheduled inventory or stored keywords. Do not talk about internal table names.
|
|
458
|
+
|
|
459
|
+
## Pause and resume
|
|
460
|
+
|
|
461
|
+
Use:
|
|
462
|
+
|
|
463
|
+
- `outrank-cli products pause <productId>`
|
|
464
|
+
- `outrank-cli products resume <productId>`
|
|
465
|
+
|
|
466
|
+
These affect billing-adjacent product state. Read the returned guidance carefully and relay any human-only step.
|
|
467
|
+
|
|
468
|
+
## Article workflow
|
|
469
|
+
|
|
470
|
+
Inspect before mutating:
|
|
471
|
+
|
|
472
|
+
- `outrank-cli articles list --product <productId>`
|
|
473
|
+
- `outrank-cli articles get <articleId>`
|
|
474
|
+
- `outrank-cli articles get-content <articleId>`
|
|
475
|
+
|
|
476
|
+
Important behavior to remember:
|
|
477
|
+
|
|
462
478
|
- article metadata and history mirror the current app behavior, including soft-delete instead of hard-delete
|
|
463
479
|
- article bodies are markdown in the backend; `--format html` is rendered output, not the source of truth
|
|
464
480
|
- featured image is separate from the markdown body and can be updated either through the normal article update payload via `image_url` or by local-file upload with `articles upload-featured-image`
|
|
465
481
|
- in-content images can be uploaded from local files with `articles upload-image`; the command returns a hosted URL and alt text for markdown insertion or replacement
|
|
466
482
|
- per-article instructions live on the linked scheduled keyword, so they are only editable while that planner relationship still exists and is writable
|
|
483
|
+
- `articles get <articleId>` returns `article_type_options`; use those values when patching `article_type`, `article_subtype`, or `expected_length`
|
|
484
|
+
- `articles update --json '{"article_type":"guide","article_subtype":"howTo","expected_length":"medium"}'` mirrors the dashboard Article Type Settings modal and rejects values outside the allowed option lists
|
|
467
485
|
- `articles update --json '{"status":"published"}'` attempts a real publish through the product's active integration for eligible attached articles; if the remote publish fails, inspect the returned error and the article's `publish_error`
|
|
468
486
|
- slug is readable for reference but read-only in the agent API
|
|
469
487
|
- `articles generate <keywordId>` mirrors the current create-and-publish flow for a scheduled keyword
|
|
470
488
|
- `articles precreate <keywordId>` mirrors the current pre-generation flow; after calling it, inspect the keyword state until it moves from `generating_article` to `pregenerated` or back to `scheduled`
|
|
471
|
-
- `articles retry-publish <articleId>` only works for generated articles with a publish error
|
|
472
|
-
- `articles bulk-replace` is preview-first; review the returned excerpts, then rerun with `"confirm": true` in the JSON payload to write changes
|
|
473
|
-
|
|
474
|
-
##
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
-
|
|
488
|
-
-
|
|
489
|
-
|
|
490
|
-
-
|
|
491
|
-
|
|
489
|
+
- `articles retry-publish <articleId>` only works for generated articles with a publish error
|
|
490
|
+
- `articles bulk-replace` is preview-first; review the returned excerpts, then rerun with `"confirm": true` in the JSON payload to write changes
|
|
491
|
+
|
|
492
|
+
## Article improvements workflow
|
|
493
|
+
|
|
494
|
+
Article improvements take an already-published post, analyze its Search Console performance, draft a rewrite brief, run a multi-step rewrite, and push the improved version back to the CMS. This is a separate surface from article generation. It requires the All-in-One plan; on Basic plan candidate lists come back empty and write actions return `PLAN_UPGRADE_REQUIRED`.
|
|
495
|
+
|
|
496
|
+
Inspect before mutating:
|
|
497
|
+
|
|
498
|
+
- `outrank-cli improvements settings get <productId>` — shows `enabled`, `auto_push`, `publishing_days`, the CMS `readiness` (whether a push can actually land), and `activity` counts
|
|
499
|
+
- `outrank-cli improvements candidates list <productId> [--type <opportunity_type>]` — opportunities available to schedule
|
|
500
|
+
- `outrank-cli improvements tasks list <productId>` — scheduled tasks, each with its `latest_execution` for progress polling
|
|
501
|
+
- `outrank-cli improvements executions list <productId> --review-status awaiting_review` — the review inbox
|
|
502
|
+
|
|
503
|
+
Typical manual flow:
|
|
504
|
+
|
|
505
|
+
1. `outrank-cli improvements analyze <productId>` — discover/refresh opportunities (heavy, multi-minute; re-list candidates after)
|
|
506
|
+
2. `outrank-cli improvements candidates brief <candidateId>` — generate the rewrite brief (heavy; poll the candidate until `brief_status` is `ready`)
|
|
507
|
+
3. optionally `outrank-cli improvements candidates edit-brief <candidateId> --json-file brief.json` to refine it
|
|
508
|
+
4. `outrank-cli improvements candidates schedule <candidateId> --scheduled-for <iso-date>` — create the task
|
|
509
|
+
5. `outrank-cli improvements tasks execute <taskId>` — run the rewrite now (heavy; poll `improvements executions get <executionId>` until `status` is `completed`)
|
|
510
|
+
6. review and publish: `outrank-cli improvements executions get <executionId>`, then `outrank-cli improvements executions push <executionId>`
|
|
511
|
+
|
|
512
|
+
Important behavior to remember:
|
|
513
|
+
|
|
514
|
+
- turning settings `enabled` on starts an unattended loop — weekly opportunity discovery, daily auto-scheduling on `publishing_days` (max 2 weekdays), and execution every ~15 minutes. Always tell the human this before enabling.
|
|
515
|
+
- with `auto_push` on, each successful rewrite is published to the CMS with no human review (still skipped automatically on a hard validation failure or an unverified backlink). Only enable it via `settings update` after the human confirms.
|
|
516
|
+
- analyze, candidates brief, and tasks execute run in Firebase and can take minutes; if a call times out at the edge the work continues server-side, so re-list or poll the execution instead of retrying
|
|
517
|
+
- `executions get` returns `before`/`after`, paragraph-level `hunks` (each with an id), and `validation_issues`; to accept everything, `executions push` with no JSON; to reject or edit specific hunks, send `{"decisions":{"<hunkId>":"rejected"},"edited_hunks":{"<hunkId>":"..."}}` to `executions decisions` or directly in the push body
|
|
518
|
+
- decisions and push only work while `review_status` is `awaiting_review`
|
|
519
|
+
- a push always updates the article inside Outrank first; if `cms_response.status` is `skipped` the CMS is not connected/supported for auto-push (publish manually); a `CMS_PUSH_FAILED` error means the CMS rejected it — inspect `error.details.cms_response` and retry
|
|
520
|
+
- `tasks execute --improve-images false` skips image regeneration (default regenerates images)
|
|
521
|
+
|
|
522
|
+
## Buying more product slots
|
|
523
|
+
|
|
524
|
+
Always do this in two steps:
|
|
525
|
+
|
|
526
|
+
1. Quote the change: `outrank-cli billing purchase-products quote --additional-products <n>`
|
|
527
|
+
2. If the human approves, submit the purchase with the exact quoted recurring total:
|
|
528
|
+
`outrank-cli billing purchase-products buy --additional-products <n> --confirm-charge-cents <quoted> --idempotency-key <stable-random-key>`
|
|
529
|
+
|
|
530
|
+
Never guess `confirm-charge-cents`. Use the server-quoted number.
|
|
531
|
+
Do not ask for billing approval until you have already clarified the intended product configuration and any defaulted article/publishing settings.
|
|
532
|
+
|
|
533
|
+
## Error handling
|
|
534
|
+
|
|
535
|
+
- If `ok` is `false`, read both `error.message` and `agent_guidance`.
|
|
536
|
+
- For `401`, help the human refresh `OUTRANK_API_KEY`.
|
|
537
|
+
- For `403` or `409`, the guidance usually tells you whether to inspect state, pause another product, or ask the human to approve a billing step.
|
|
538
|
+
- For `402`, stop retrying and ask the human to fix billing.
|
|
539
|
+
|
|
492
540
|
## Current limitation
|
|
493
541
|
|
|
494
|
-
The implementation plan is broader than the current live API. Keywords, core article flows, linking, backlink exchange management, and the current integration-management flows are
|
|
495
|
-
|
|
542
|
+
The implementation plan is broader than the current live API. Keywords, core article flows, article improvements, linking, backlink exchange management, and the current integration-management flows are exposed in `outrank-cli`, including direct setup for webhook, Ghost, Shopify, Webflow, Wix, Framer, WordPress Plugin, and Next.js Blog, plus OAuth flows for WordPress.com and Notion. Purchasing or changing backlink add-ons and some onboarding flows still require the dashboard. If the user asks for something not present in `outrank-cli --help`, say that exact command surface is not shipped yet and do not invent unsupported commands.
|
package/package.json
CHANGED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { parseArgs, getRequiredPositional, readJsonInput, hasFlag, parseIntegerOption, getOption } from '../core/args.js';
|
|
2
|
+
import { requestAgentApi } from '../core/api.js';
|
|
3
|
+
import { cliSuccess } from '../core/errors.js';
|
|
4
|
+
|
|
5
|
+
export const IMPROVEMENTS_HELP = `Usage:
|
|
6
|
+
outrank-cli improvements settings get <product-id>
|
|
7
|
+
outrank-cli improvements settings update <product-id> --json <object>
|
|
8
|
+
outrank-cli improvements analyze <product-id>
|
|
9
|
+
outrank-cli improvements candidates list <product-id> [--type outsider|ctr_gap|declining|content_gap|thin_content] [--limit <n>] [--offset <n>]
|
|
10
|
+
outrank-cli improvements candidates brief <candidate-id> [--task <task-id>]
|
|
11
|
+
outrank-cli improvements candidates edit-brief <candidate-id> --json <object>
|
|
12
|
+
outrank-cli improvements candidates schedule <candidate-id> --scheduled-for <iso-date>
|
|
13
|
+
outrank-cli improvements tasks list <product-id>
|
|
14
|
+
outrank-cli improvements tasks reschedule <task-id> --scheduled-for <iso-date>
|
|
15
|
+
outrank-cli improvements tasks cancel <task-id>
|
|
16
|
+
outrank-cli improvements tasks edit-brief <task-id> --json <object>
|
|
17
|
+
outrank-cli improvements tasks execute <task-id> [--improve-images true|false]
|
|
18
|
+
outrank-cli improvements executions list <product-id> [--review-status awaiting_review|pushed|auto_pushed|rejected|all] [--limit <n>] [--offset <n>]
|
|
19
|
+
outrank-cli improvements executions get <execution-id>
|
|
20
|
+
outrank-cli improvements executions decisions <execution-id> --json <object>
|
|
21
|
+
outrank-cli improvements executions push <execution-id> [--json <object>]
|
|
22
|
+
|
|
23
|
+
JSON Input:
|
|
24
|
+
Use one of: --json '<object>', --json-file payload.json, or --json-stdin
|
|
25
|
+
|
|
26
|
+
Notes:
|
|
27
|
+
Article improvements need the All-in-One plan. Turning settings on starts an unattended loop
|
|
28
|
+
(weekly opportunity discovery, daily auto-scheduling on the configured publishing_days, and
|
|
29
|
+
execution every ~15 minutes). With auto_push on, each successful rewrite is published to the CMS
|
|
30
|
+
without review. analyze, candidates brief, and tasks execute are heavy multi-minute operations:
|
|
31
|
+
if a call times out at the edge, the work continues server-side — re-list/poll instead of retrying.
|
|
32
|
+
|
|
33
|
+
Examples:
|
|
34
|
+
outrank-cli improvements settings get 00000000-0000-0000-0000-000000000000
|
|
35
|
+
outrank-cli improvements settings update 00000000-0000-0000-0000-000000000000 --json '{"enabled":true,"auto_push":false,"publishing_days":["Mon","Thu"]}'
|
|
36
|
+
outrank-cli improvements analyze 00000000-0000-0000-0000-000000000000
|
|
37
|
+
outrank-cli improvements candidates list 00000000-0000-0000-0000-000000000000 --type ctr_gap --limit 20
|
|
38
|
+
outrank-cli improvements candidates brief 00000000-0000-0000-0000-000000000000
|
|
39
|
+
outrank-cli improvements candidates edit-brief 00000000-0000-0000-0000-000000000000 --json-file brief.json
|
|
40
|
+
outrank-cli improvements candidates schedule 00000000-0000-0000-0000-000000000000 --scheduled-for 2026-06-01
|
|
41
|
+
outrank-cli improvements tasks list 00000000-0000-0000-0000-000000000000
|
|
42
|
+
outrank-cli improvements tasks execute 00000000-0000-0000-0000-000000000000 --improve-images false
|
|
43
|
+
outrank-cli improvements executions list 00000000-0000-0000-0000-000000000000 --review-status awaiting_review
|
|
44
|
+
outrank-cli improvements executions get 00000000-0000-0000-0000-000000000000
|
|
45
|
+
outrank-cli improvements executions push 00000000-0000-0000-0000-000000000000
|
|
46
|
+
outrank-cli improvements executions push 00000000-0000-0000-0000-000000000000 --json '{"decisions":{"para-3":"rejected"}}'
|
|
47
|
+
`;
|
|
48
|
+
|
|
49
|
+
const invalidUsage = (message, guidance) =>
|
|
50
|
+
Object.assign(new Error(message), {
|
|
51
|
+
cliCode: 'CLI_INVALID_USAGE',
|
|
52
|
+
guidance: guidance || 'Run `outrank-cli improvements --help` to see available improvement commands.',
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const buildQueryPath = (basePath, searchParams) => {
|
|
56
|
+
const url = new URL(`https://local${basePath}`);
|
|
57
|
+
for (const [key, value] of Object.entries(searchParams)) {
|
|
58
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
59
|
+
url.searchParams.set(key, String(value));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return `${url.pathname}${url.search}`;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const hasJsonSource = (parsed) =>
|
|
66
|
+
Boolean(getOption(parsed, 'json') || getOption(parsed, 'json-file') || hasFlag(parsed, 'json-stdin'));
|
|
67
|
+
|
|
68
|
+
const parseImproveImages = (parsed) => {
|
|
69
|
+
const raw = getOption(parsed, 'improve-images');
|
|
70
|
+
if (raw === undefined) return undefined;
|
|
71
|
+
if (raw === 'true') return true;
|
|
72
|
+
if (raw === 'false') return false;
|
|
73
|
+
throw invalidUsage('--improve-images must be true or false.', 'Pass `--improve-images true` or `--improve-images false`, or omit it to use the default (images are regenerated).');
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const requireScheduledFor = (parsed) => {
|
|
77
|
+
const value = getOption(parsed, 'scheduled-for');
|
|
78
|
+
if (!value) {
|
|
79
|
+
throw invalidUsage('Missing required option: --scheduled-for <iso-date>.', 'Pass a date or ISO timestamp, e.g. `--scheduled-for 2026-06-01`.');
|
|
80
|
+
}
|
|
81
|
+
return value;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const handleAnalyze = async (parsed) => {
|
|
85
|
+
const productId = getRequiredPositional(parsed, 0, '<product-id>');
|
|
86
|
+
const response = await requestAgentApi(parsed, {
|
|
87
|
+
method: 'POST',
|
|
88
|
+
path: `/products/${productId}/improvements/analyze`,
|
|
89
|
+
});
|
|
90
|
+
return response.payload;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const handleSettings = async (action, parsed) => {
|
|
94
|
+
const productId = getRequiredPositional(parsed, 0, '<product-id>');
|
|
95
|
+
if (action === 'get') {
|
|
96
|
+
const response = await requestAgentApi(parsed, {
|
|
97
|
+
method: 'GET',
|
|
98
|
+
path: `/products/${productId}/improvements/settings`,
|
|
99
|
+
});
|
|
100
|
+
return response.payload;
|
|
101
|
+
}
|
|
102
|
+
if (action === 'update') {
|
|
103
|
+
const body = await readJsonInput(parsed);
|
|
104
|
+
const response = await requestAgentApi(parsed, {
|
|
105
|
+
method: 'PATCH',
|
|
106
|
+
path: `/products/${productId}/improvements/settings`,
|
|
107
|
+
body,
|
|
108
|
+
});
|
|
109
|
+
return response.payload;
|
|
110
|
+
}
|
|
111
|
+
throw invalidUsage(`Unknown settings action: ${action ?? '(none)'}.`, 'Use: improvements settings get|update <product-id>.');
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const handleCandidates = async (action, parsed) => {
|
|
115
|
+
if (action === 'list') {
|
|
116
|
+
const productId = getRequiredPositional(parsed, 0, '<product-id>');
|
|
117
|
+
const path = buildQueryPath(`/products/${productId}/improvements/candidates`, {
|
|
118
|
+
type: getOption(parsed, 'type'),
|
|
119
|
+
limit: parseIntegerOption(parsed, 'limit', { min: 1, max: 50 }),
|
|
120
|
+
offset: parseIntegerOption(parsed, 'offset', { min: 0 }),
|
|
121
|
+
});
|
|
122
|
+
const response = await requestAgentApi(parsed, { method: 'GET', path });
|
|
123
|
+
return response.payload;
|
|
124
|
+
}
|
|
125
|
+
if (action === 'brief') {
|
|
126
|
+
const candidateId = getRequiredPositional(parsed, 0, '<candidate-id>');
|
|
127
|
+
const taskId = getOption(parsed, 'task');
|
|
128
|
+
const response = await requestAgentApi(parsed, {
|
|
129
|
+
method: 'POST',
|
|
130
|
+
path: `/improvements/candidates/${candidateId}/brief`,
|
|
131
|
+
body: taskId ? { task_id: taskId } : {},
|
|
132
|
+
});
|
|
133
|
+
return response.payload;
|
|
134
|
+
}
|
|
135
|
+
if (action === 'edit-brief') {
|
|
136
|
+
const candidateId = getRequiredPositional(parsed, 0, '<candidate-id>');
|
|
137
|
+
const body = await readJsonInput(parsed);
|
|
138
|
+
const response = await requestAgentApi(parsed, {
|
|
139
|
+
method: 'PATCH',
|
|
140
|
+
path: `/improvements/candidates/${candidateId}/brief`,
|
|
141
|
+
body,
|
|
142
|
+
});
|
|
143
|
+
return response.payload;
|
|
144
|
+
}
|
|
145
|
+
if (action === 'schedule') {
|
|
146
|
+
const candidateId = getRequiredPositional(parsed, 0, '<candidate-id>');
|
|
147
|
+
const scheduledFor = requireScheduledFor(parsed);
|
|
148
|
+
const response = await requestAgentApi(parsed, {
|
|
149
|
+
method: 'POST',
|
|
150
|
+
path: `/improvements/candidates/${candidateId}/schedule`,
|
|
151
|
+
body: { scheduled_for: scheduledFor },
|
|
152
|
+
});
|
|
153
|
+
return response.payload;
|
|
154
|
+
}
|
|
155
|
+
throw invalidUsage(`Unknown candidates action: ${action ?? '(none)'}.`, 'Use: improvements candidates list|brief|edit-brief|schedule.');
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const handleTasks = async (action, parsed) => {
|
|
159
|
+
if (action === 'list') {
|
|
160
|
+
const productId = getRequiredPositional(parsed, 0, '<product-id>');
|
|
161
|
+
const response = await requestAgentApi(parsed, {
|
|
162
|
+
method: 'GET',
|
|
163
|
+
path: `/products/${productId}/improvements/tasks`,
|
|
164
|
+
});
|
|
165
|
+
return response.payload;
|
|
166
|
+
}
|
|
167
|
+
if (action === 'reschedule') {
|
|
168
|
+
const taskId = getRequiredPositional(parsed, 0, '<task-id>');
|
|
169
|
+
const scheduledFor = requireScheduledFor(parsed);
|
|
170
|
+
const response = await requestAgentApi(parsed, {
|
|
171
|
+
method: 'PATCH',
|
|
172
|
+
path: `/improvements/tasks/${taskId}`,
|
|
173
|
+
body: { scheduled_for: scheduledFor },
|
|
174
|
+
});
|
|
175
|
+
return response.payload;
|
|
176
|
+
}
|
|
177
|
+
if (action === 'cancel') {
|
|
178
|
+
const taskId = getRequiredPositional(parsed, 0, '<task-id>');
|
|
179
|
+
const response = await requestAgentApi(parsed, {
|
|
180
|
+
method: 'DELETE',
|
|
181
|
+
path: `/improvements/tasks/${taskId}`,
|
|
182
|
+
});
|
|
183
|
+
return response.payload;
|
|
184
|
+
}
|
|
185
|
+
if (action === 'edit-brief') {
|
|
186
|
+
const taskId = getRequiredPositional(parsed, 0, '<task-id>');
|
|
187
|
+
const body = await readJsonInput(parsed);
|
|
188
|
+
const response = await requestAgentApi(parsed, {
|
|
189
|
+
method: 'PATCH',
|
|
190
|
+
path: `/improvements/tasks/${taskId}/brief`,
|
|
191
|
+
body,
|
|
192
|
+
});
|
|
193
|
+
return response.payload;
|
|
194
|
+
}
|
|
195
|
+
if (action === 'execute') {
|
|
196
|
+
const taskId = getRequiredPositional(parsed, 0, '<task-id>');
|
|
197
|
+
const improveImages = parseImproveImages(parsed);
|
|
198
|
+
const response = await requestAgentApi(parsed, {
|
|
199
|
+
method: 'POST',
|
|
200
|
+
path: `/improvements/tasks/${taskId}/execute`,
|
|
201
|
+
body: improveImages === undefined ? {} : { improve_images: improveImages },
|
|
202
|
+
});
|
|
203
|
+
return response.payload;
|
|
204
|
+
}
|
|
205
|
+
throw invalidUsage(`Unknown tasks action: ${action ?? '(none)'}.`, 'Use: improvements tasks list|reschedule|cancel|edit-brief|execute.');
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const handleExecutions = async (action, parsed) => {
|
|
209
|
+
if (action === 'list') {
|
|
210
|
+
const productId = getRequiredPositional(parsed, 0, '<product-id>');
|
|
211
|
+
const path = buildQueryPath(`/products/${productId}/improvements/executions`, {
|
|
212
|
+
review_status: getOption(parsed, 'review-status'),
|
|
213
|
+
limit: parseIntegerOption(parsed, 'limit', { min: 1, max: 100 }),
|
|
214
|
+
offset: parseIntegerOption(parsed, 'offset', { min: 0 }),
|
|
215
|
+
});
|
|
216
|
+
const response = await requestAgentApi(parsed, { method: 'GET', path });
|
|
217
|
+
return response.payload;
|
|
218
|
+
}
|
|
219
|
+
if (action === 'get') {
|
|
220
|
+
const executionId = getRequiredPositional(parsed, 0, '<execution-id>');
|
|
221
|
+
const response = await requestAgentApi(parsed, {
|
|
222
|
+
method: 'GET',
|
|
223
|
+
path: `/improvements/executions/${executionId}`,
|
|
224
|
+
});
|
|
225
|
+
return response.payload;
|
|
226
|
+
}
|
|
227
|
+
if (action === 'decisions') {
|
|
228
|
+
const executionId = getRequiredPositional(parsed, 0, '<execution-id>');
|
|
229
|
+
const body = await readJsonInput(parsed);
|
|
230
|
+
const response = await requestAgentApi(parsed, {
|
|
231
|
+
method: 'PATCH',
|
|
232
|
+
path: `/improvements/executions/${executionId}/decisions`,
|
|
233
|
+
body,
|
|
234
|
+
});
|
|
235
|
+
return response.payload;
|
|
236
|
+
}
|
|
237
|
+
if (action === 'push') {
|
|
238
|
+
const executionId = getRequiredPositional(parsed, 0, '<execution-id>');
|
|
239
|
+
const body = hasJsonSource(parsed) ? await readJsonInput(parsed) : {};
|
|
240
|
+
const response = await requestAgentApi(parsed, {
|
|
241
|
+
method: 'POST',
|
|
242
|
+
path: `/improvements/executions/${executionId}/push`,
|
|
243
|
+
body,
|
|
244
|
+
});
|
|
245
|
+
return response.payload;
|
|
246
|
+
}
|
|
247
|
+
throw invalidUsage(`Unknown executions action: ${action ?? '(none)'}.`, 'Use: improvements executions list|get|decisions|push.');
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
export const handleImprovementsCommand = async (argv) => {
|
|
251
|
+
const subgroup = argv[0];
|
|
252
|
+
const wantsHelp =
|
|
253
|
+
!subgroup || subgroup === '--help' || subgroup === '-h' || subgroup === 'help';
|
|
254
|
+
|
|
255
|
+
if (wantsHelp) {
|
|
256
|
+
return cliSuccess(
|
|
257
|
+
{
|
|
258
|
+
usage: IMPROVEMENTS_HELP.trim(),
|
|
259
|
+
},
|
|
260
|
+
'Choose a sub-noun: settings, analyze, candidates, tasks, or executions. The typical flow is analyze -> candidates brief -> candidates schedule -> tasks execute -> executions push.',
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (subgroup === 'analyze') {
|
|
265
|
+
return handleAnalyze(parseArgs(argv.slice(1)));
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const action = argv[1];
|
|
269
|
+
const parsed = parseArgs(argv.slice(2));
|
|
270
|
+
|
|
271
|
+
if (subgroup === 'settings') return handleSettings(action, parsed);
|
|
272
|
+
if (subgroup === 'candidates') return handleCandidates(action, parsed);
|
|
273
|
+
if (subgroup === 'tasks') return handleTasks(action, parsed);
|
|
274
|
+
if (subgroup === 'executions') return handleExecutions(action, parsed);
|
|
275
|
+
|
|
276
|
+
throw invalidUsage(`Unknown improvements sub-noun: ${subgroup}.`);
|
|
277
|
+
};
|
package/src/commands/keywords.js
CHANGED
|
@@ -16,10 +16,10 @@ export const KEYWORDS_HELP = `Usage:
|
|
|
16
16
|
outrank-cli keywords export --product <product-id>
|
|
17
17
|
outrank-cli keywords generation-status --product <product-id>
|
|
18
18
|
outrank-cli keywords add --product <product-id> --json <object>
|
|
19
|
+
outrank-cli keywords update <keyword-id> --json <object>
|
|
19
20
|
outrank-cli keywords suggest --product <product-id> (--keyword <text> | --theme <text>) [--keyword-region <code>] [--limit <n>]
|
|
20
21
|
outrank-cli keywords generate --product <product-id> --count <n> [--source competitors|audiences|both] [--competitor <competitor-id> ...] [--audience <text> ...]
|
|
21
22
|
outrank-cli keywords bulk-process --product <product-id> --json <object> [--keyword-region <code>]
|
|
22
|
-
outrank-cli keywords reschedule <keyword-id> --scheduled-for <date>
|
|
23
23
|
outrank-cli keywords bulk-reschedule --json <object>
|
|
24
24
|
outrank-cli keywords delete <keyword-id>
|
|
25
25
|
outrank-cli keywords bulk-delete --json <object>
|
|
@@ -29,14 +29,16 @@ JSON Input:
|
|
|
29
29
|
|
|
30
30
|
Examples:
|
|
31
31
|
outrank-cli keywords list --product 00000000-0000-0000-0000-000000000000 --scope all
|
|
32
|
+
outrank-cli keywords list --product 00000000-0000-0000-0000-000000000000 --scope scheduled --q "best seo tools"
|
|
32
33
|
outrank-cli keywords export --product 00000000-0000-0000-0000-000000000000
|
|
33
34
|
outrank-cli keywords generation-status --product 00000000-0000-0000-0000-000000000000
|
|
34
35
|
outrank-cli keywords add --product 00000000-0000-0000-0000-000000000000 --json-file keywords.json
|
|
36
|
+
outrank-cli keywords update 00000000-0000-0000-0000-000000000000 --json '{"instructions":"Write for beginners and include a step-by-step setup section"}'
|
|
37
|
+
outrank-cli keywords update 00000000-0000-0000-0000-000000000000 --json '{"scheduled_for":"2026-05-20"}'
|
|
35
38
|
outrank-cli keywords suggest --product 00000000-0000-0000-0000-000000000000 --theme "email marketing" --limit 8
|
|
36
39
|
outrank-cli keywords generate --product 00000000-0000-0000-0000-000000000000 --count 10 --source both
|
|
37
40
|
outrank-cli keywords generate --product 00000000-0000-0000-0000-000000000000 --count 5 --source competitors --competitor 11111111-1111-1111-1111-111111111111
|
|
38
41
|
outrank-cli keywords bulk-process --product 00000000-0000-0000-0000-000000000000 --json '{"keywords":["keyword one","keyword two"]}'
|
|
39
|
-
outrank-cli keywords reschedule 00000000-0000-0000-0000-000000000000 --scheduled-for 2026-05-20
|
|
40
42
|
outrank-cli keywords bulk-reschedule --json-file keyword-reschedule.json
|
|
41
43
|
outrank-cli keywords delete 00000000-0000-0000-0000-000000000000
|
|
42
44
|
`;
|
|
@@ -131,6 +133,17 @@ const handleKeywordsAdd = async (parsed) => {
|
|
|
131
133
|
return response.payload;
|
|
132
134
|
};
|
|
133
135
|
|
|
136
|
+
const handleKeywordsUpdate = async (parsed) => {
|
|
137
|
+
const keywordId = getRequiredPositional(parsed, 0, '<keyword-id>');
|
|
138
|
+
const body = await readJsonInput(parsed);
|
|
139
|
+
const response = await requestAgentApi(parsed, {
|
|
140
|
+
method: 'PATCH',
|
|
141
|
+
path: `/keywords/${keywordId}`,
|
|
142
|
+
body,
|
|
143
|
+
});
|
|
144
|
+
return response.payload;
|
|
145
|
+
};
|
|
146
|
+
|
|
134
147
|
const handleKeywordsSuggest = async (parsed) => {
|
|
135
148
|
const keyword = getOption(parsed, 'keyword');
|
|
136
149
|
const theme = getOption(parsed, 'theme');
|
|
@@ -228,26 +241,6 @@ const handleKeywordsBulkProcess = async (parsed) => {
|
|
|
228
241
|
return response.payload;
|
|
229
242
|
};
|
|
230
243
|
|
|
231
|
-
const handleKeywordsReschedule = async (parsed) => {
|
|
232
|
-
const keywordId = getRequiredPositional(parsed, 0, '<keyword-id>');
|
|
233
|
-
const scheduledFor = getOption(parsed, 'scheduled-for');
|
|
234
|
-
if (!scheduledFor) {
|
|
235
|
-
throw Object.assign(new Error('Missing required option: --scheduled-for <date>.'), {
|
|
236
|
-
cliCode: 'CLI_INVALID_USAGE',
|
|
237
|
-
guidance: 'Pass the target schedule date with `--scheduled-for YYYY-MM-DD` or an ISO timestamp.',
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
const response = await requestAgentApi(parsed, {
|
|
242
|
-
method: 'PATCH',
|
|
243
|
-
path: `/keywords/${keywordId}`,
|
|
244
|
-
body: {
|
|
245
|
-
scheduled_for: scheduledFor,
|
|
246
|
-
},
|
|
247
|
-
});
|
|
248
|
-
return response.payload;
|
|
249
|
-
};
|
|
250
|
-
|
|
251
244
|
const handleKeywordsBulkReschedule = async (parsed) => {
|
|
252
245
|
const body = await readJsonInput(parsed);
|
|
253
246
|
const response = await requestAgentApi(parsed, {
|
|
@@ -287,7 +280,7 @@ export const handleKeywordsCommand = async (argv) => {
|
|
|
287
280
|
{
|
|
288
281
|
usage: KEYWORDS_HELP.trim(),
|
|
289
282
|
},
|
|
290
|
-
'Choose one of: list, get, export, generation-status, add, suggest, generate, bulk-process,
|
|
283
|
+
'Choose one of: list, get, export, generation-status, add, update, suggest, generate, bulk-process, bulk-reschedule, delete, bulk-delete.',
|
|
291
284
|
);
|
|
292
285
|
}
|
|
293
286
|
|
|
@@ -296,10 +289,10 @@ export const handleKeywordsCommand = async (argv) => {
|
|
|
296
289
|
if (subcommand === 'export') return handleKeywordsExport(parsed);
|
|
297
290
|
if (subcommand === 'generation-status') return handleKeywordsGenerationStatus(parsed);
|
|
298
291
|
if (subcommand === 'add') return handleKeywordsAdd(parsed);
|
|
292
|
+
if (subcommand === 'update') return handleKeywordsUpdate(parsed);
|
|
299
293
|
if (subcommand === 'suggest') return handleKeywordsSuggest(parsed);
|
|
300
294
|
if (subcommand === 'generate') return handleKeywordsGenerate(parsed);
|
|
301
295
|
if (subcommand === 'bulk-process') return handleKeywordsBulkProcess(parsed);
|
|
302
|
-
if (subcommand === 'reschedule') return handleKeywordsReschedule(parsed);
|
|
303
296
|
if (subcommand === 'bulk-reschedule') return handleKeywordsBulkReschedule(parsed);
|
|
304
297
|
if (subcommand === 'delete') return handleKeywordsDelete(parsed);
|
|
305
298
|
if (subcommand === 'bulk-delete') return handleKeywordsBulkDelete(parsed);
|
package/src/index.js
CHANGED
|
@@ -12,6 +12,7 @@ import { handleSubscriptionCommand } from './commands/subscription.js';
|
|
|
12
12
|
import { handleUsageCommand } from './commands/usage.js';
|
|
13
13
|
import { handleKeywordsCommand } from './commands/keywords.js';
|
|
14
14
|
import { handleArticlesCommand } from './commands/articles.js';
|
|
15
|
+
import { handleImprovementsCommand } from './commands/improvements.js';
|
|
15
16
|
|
|
16
17
|
const getVersion = () => {
|
|
17
18
|
try {
|
|
@@ -35,6 +36,7 @@ Groups:
|
|
|
35
36
|
products List, create, inspect, and configure products, integrations, article settings, Google Search Console, linking, and backlinks.
|
|
36
37
|
keywords Inspect, check write status, add, reschedule, delete, suggest, generate, and export keyword plans.
|
|
37
38
|
articles Inspect article metadata and bodies, edit content, retry publish, precreate, and generate.
|
|
39
|
+
improvements Discover, brief, schedule, run, review, and publish article improvements for existing posts.
|
|
38
40
|
subscription Inspect plan, trial, quota, and add-on status.
|
|
39
41
|
usage Inspect current usage and quota consumption.
|
|
40
42
|
billing Product-slot purchase flows.
|
|
@@ -55,6 +57,8 @@ Examples:
|
|
|
55
57
|
outrank-cli products list --limit 20
|
|
56
58
|
outrank-cli keywords list --product 00000000-0000-0000-0000-000000000000 --scope all
|
|
57
59
|
outrank-cli articles list --product 00000000-0000-0000-0000-000000000000 --status published
|
|
60
|
+
outrank-cli improvements candidates list 00000000-0000-0000-0000-000000000000 --type ctr_gap
|
|
61
|
+
outrank-cli improvements tasks execute 00000000-0000-0000-0000-000000000000
|
|
58
62
|
outrank-cli subscription status
|
|
59
63
|
outrank-cli usage stats
|
|
60
64
|
outrank-cli products create --json-file product.json
|
|
@@ -111,6 +115,8 @@ const main = async () => {
|
|
|
111
115
|
result = await handleKeywordsCommand(rest);
|
|
112
116
|
} else if (group === 'articles') {
|
|
113
117
|
result = await handleArticlesCommand(rest);
|
|
118
|
+
} else if (group === 'improvements') {
|
|
119
|
+
result = await handleImprovementsCommand(rest);
|
|
114
120
|
} else if (group === 'subscription') {
|
|
115
121
|
result = await handleSubscriptionCommand(rest);
|
|
116
122
|
} else if (group === 'usage') {
|