@nuxtseo/cli 0.3.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 +4 -8
- package/dist/cli.js +3 -16
- package/dist/commands.js +6 -111
- package/dist/pull.js +0 -4
- package/dist/render.d.ts +0 -4
- package/dist/render.js +0 -21
- package/dist/state/auth.js +2 -2
- package/dist/state/files.js +6 -6
- package/package.json +4 -4
- package/skills/nuxtseo-cli/SKILL.md +185 -309
- package/skills/nuxtseo-cli/references/commands.md +10 -8
- package/skills/nuxtseo-cli/references/indexing.md +3 -2
- package/skills/nuxtseo-cli/references/protocol.md +25 -13
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
#
|
|
1
|
+
# `nuxtseo` CLI
|
|
2
2
|
|
|
3
|
-
Use
|
|
3
|
+
Use Nuxt SEO public Site operations from a terminal, script, or coding agent.
|
|
4
4
|
The CLI sends feature requests through `@nuxtseo/sdk`. It does not call MCP or
|
|
5
5
|
private routes. Live research uses declared public API operations.
|
|
6
6
|
|
|
@@ -81,7 +81,7 @@ When keychain support is unavailable, it warns on stderr and uses
|
|
|
81
81
|
scope before the request that needs it.
|
|
82
82
|
|
|
83
83
|
`nuxtseo logout` removes the local credential. Revoke the token from the
|
|
84
|
-
|
|
84
|
+
Nuxt SEO dashboard when it must stop working everywhere. An environment token
|
|
85
85
|
always wins and is unchanged by login or logout.
|
|
86
86
|
|
|
87
87
|
API host resolution order is:
|
|
@@ -216,9 +216,6 @@ nuxtseo audit link-opportunities
|
|
|
216
216
|
nuxtseo audit link-structure
|
|
217
217
|
nuxtseo audit content-decay
|
|
218
218
|
nuxtseo audit duplicates
|
|
219
|
-
nuxtseo content briefs list
|
|
220
|
-
nuxtseo content briefs show <brief-id>
|
|
221
|
-
nuxtseo content briefs create <keyword>
|
|
222
219
|
nuxtseo timeline list
|
|
223
220
|
nuxtseo skill install
|
|
224
221
|
```
|
|
@@ -256,7 +253,7 @@ reads retained rows and spends nothing.
|
|
|
256
253
|
`search indexing summary` reads retained URL Inspection coverage.
|
|
257
254
|
`search index-history` dates an indexing change against known releases.
|
|
258
255
|
`search inspect <url>` reads Google's own verdict for one URL.
|
|
259
|
-
`page inspect <url>` reads the
|
|
256
|
+
`page inspect <url>` reads the Nuxt SEO observation store for the same URL.
|
|
260
257
|
`research keywords`, `research serp`, and `research rankings` can use the Team
|
|
261
258
|
research limit. Keyword responses report cache use in `evidence`. SERP and
|
|
262
259
|
ranking responses report `cached: true`. A cached response spends nothing.
|
|
@@ -279,7 +276,6 @@ The CLI fetches one page per invocation by default. It never merges responses.
|
|
|
279
276
|
| `search analytics` | `--limit 1..100`, `--page >=1` | `--limit 25 --page 1` | row views only |
|
|
280
277
|
| `search indexing` | `--limit 1..500`, `--offset >=0` | `--limit 50 --offset 0` | `urls` view only |
|
|
281
278
|
| `sitemaps urls` | `--cursor`, `--limit 1..1000` | `--limit 500` | yes |
|
|
282
|
-
| `content briefs list` | `--limit 1..100`, `--offset >=0` | `--limit 25 --offset 0` | yes |
|
|
283
279
|
| `timeline list` | `--kind`, `--feature`, `--severity`, `--since`, `--cursor`, `--limit 1..100` | `--limit 25` | no |
|
|
284
280
|
|
|
285
281
|
Keep the server order for actions. For another page, pass the next offset or
|
package/dist/cli.js
CHANGED
|
@@ -202,7 +202,7 @@ async function interactiveBare(runtime, globals) {
|
|
|
202
202
|
return fromStateError(apiUrl.error);
|
|
203
203
|
if (config._tag === 'Err')
|
|
204
204
|
return fromStateError(config.error);
|
|
205
|
-
prompts.intro(`
|
|
205
|
+
prompts.intro(`nuxtseo ${VERSION}`, { input: runtime.input, output: runtime.error });
|
|
206
206
|
prompts.note([
|
|
207
207
|
`Authentication: ${auth.value.authenticated ? auth.value.source : 'not configured'}`,
|
|
208
208
|
`API: ${apiUrl.value.apiUrl}`,
|
|
@@ -215,7 +215,7 @@ async function interactiveBare(runtime, globals) {
|
|
|
215
215
|
{ value: 'fix', label: 'Fix my Site', hint: 'Issues, Pages, and Scans' },
|
|
216
216
|
{ value: 'performance', label: 'Understand performance', hint: 'Performance and Search Console' },
|
|
217
217
|
{ value: 'research', label: 'Research growth', hint: 'Keywords, SERPs, and competitors' },
|
|
218
|
-
{ value: 'content', label: 'Plan content', hint: '
|
|
218
|
+
{ value: 'content', label: 'Plan content', hint: 'Decay, duplicates, and annotations' },
|
|
219
219
|
{ value: 'account', label: 'Account and setup', hint: config.value.siteId ?? 'no Site selected' },
|
|
220
220
|
{ value: 'exit', label: 'Exit' },
|
|
221
221
|
],
|
|
@@ -356,8 +356,6 @@ async function interactiveBare(runtime, globals) {
|
|
|
356
356
|
const task = await prompts.select({
|
|
357
357
|
message: 'Plan content',
|
|
358
358
|
options: [
|
|
359
|
-
{ value: 'briefs', label: 'Content Briefs' },
|
|
360
|
-
{ value: 'create', label: 'Create a Content Brief' },
|
|
361
359
|
{ value: 'decay', label: 'Content decay' },
|
|
362
360
|
{ value: 'duplicates', label: 'Duplicate clusters' },
|
|
363
361
|
{ value: 'annotations', label: 'Chart annotations', hint: 'mark what you shipped' },
|
|
@@ -368,22 +366,11 @@ async function interactiveBare(runtime, globals) {
|
|
|
368
366
|
});
|
|
369
367
|
if (typeof task === 'symbol')
|
|
370
368
|
return ok(undefined);
|
|
371
|
-
if (task === 'briefs')
|
|
372
|
-
return runInteractiveCommand(['content', 'briefs', 'list'], runtime, globals);
|
|
373
369
|
if (task === 'decay')
|
|
374
370
|
return runInteractiveCommand(['audit', 'content-decay'], runtime, globals);
|
|
375
371
|
if (task === 'duplicates')
|
|
376
372
|
return runInteractiveCommand(['audit', 'duplicates'], runtime, globals);
|
|
377
|
-
|
|
378
|
-
return runInteractiveCommand(['annotations', 'list'], runtime, globals);
|
|
379
|
-
const keyword = await promptTextValue(runtime, {
|
|
380
|
-
message: 'Which target keyword?',
|
|
381
|
-
placeholder: 'nuxt seo',
|
|
382
|
-
label: 'Keyword',
|
|
383
|
-
});
|
|
384
|
-
return keyword._tag === 'Err'
|
|
385
|
-
? keyword
|
|
386
|
-
: runInteractiveCommand(['content', 'briefs', 'create', keyword.value], runtime, globals);
|
|
373
|
+
return runInteractiveCommand(['annotations', 'list'], runtime, globals);
|
|
387
374
|
}
|
|
388
375
|
const task = await prompts.select({
|
|
389
376
|
message: 'Account and setup',
|
package/dist/commands.js
CHANGED
|
@@ -9,7 +9,7 @@ import { EXIT_CODE, fail, fromSdkFailure, ok } from './failures.js';
|
|
|
9
9
|
import { awaitPairingApproval, createCliPairingClient, startPairing } from './pairing.js';
|
|
10
10
|
import { parseAbsolutePageUrl, parseChoice, parseInteger, parsePageUrlOrPath, parseRatio } from './parse.js';
|
|
11
11
|
import { PULL_PERIODS, runPull } from './pull.js';
|
|
12
|
-
import { renderAccountToken, renderAction, renderActionDismissal, renderActionResolution, renderActions, renderAnalytics, renderAnnotation, renderAnnotationDeleted, renderAnnotations, renderAuditChanges, renderBacklinkAnchors, renderBacklinksHistory, renderBacklinksSummary,
|
|
12
|
+
import { renderAccountToken, renderAction, renderActionDismissal, renderActionResolution, renderActions, renderAnalytics, renderAnnotation, renderAnnotationDeleted, renderAnnotations, renderAuditChanges, renderBacklinkAnchors, renderBacklinksHistory, renderBacklinksSummary, renderContentDecay, renderDomainAvailability, renderDomainTraffic, renderDuplicateClusters, renderFieldVitalFindings, renderFieldVitals, renderIndexCohorts, renderIndexingDiagnostics, renderIndexingHistory, renderKeywordResearch, renderLinkOpportunities, renderLinkStructure, renderMentions, renderMonitoredPages, renderPageInspection, renderPageIssues, renderPageScan, renderPerformance, renderRankingsResearch, renderRecoverableBacklinks, renderReferringDomains, renderResearchOverview, renderScanDetail, renderScans, renderSearchAnalytics, renderSearchStatus, renderSerpResearch, renderSitemapAction, renderSitemaps, renderSitemapUrls, renderSites, renderSiteStatus, renderTimeline, renderUrlInspection, renderUsage, } from './render.js';
|
|
13
13
|
import { writeCliResponse, writeDiagnostic, writeOutput, writeProtocolResponse } from './runtime.js';
|
|
14
14
|
import { resolveSite } from './site.js';
|
|
15
15
|
import { installSkill, SKILL_AGENTS } from './skill.js';
|
|
@@ -231,7 +231,7 @@ async function logout(runtime, globals) {
|
|
|
231
231
|
else {
|
|
232
232
|
writeOutput(runtime, cleared.value.removed ? 'Local credential removed.' : 'No stored credential.');
|
|
233
233
|
}
|
|
234
|
-
writeDiagnostic(runtime, 'Token revocation remains in the
|
|
234
|
+
writeDiagnostic(runtime, 'Token revocation remains in the Nuxt SEO dashboard. NUXTSEO_TOKEN, when set, is unchanged.');
|
|
235
235
|
return ok(undefined);
|
|
236
236
|
}
|
|
237
237
|
async function whoami(runtime, globals) {
|
|
@@ -370,7 +370,7 @@ async function feedbackSubmit(runtime, globals, args) {
|
|
|
370
370
|
const body = accountFeedbackBodySchema.safeParse({ ...args, cliVersion: VERSION });
|
|
371
371
|
if (!body.success)
|
|
372
372
|
return fail(EXIT_CODE.invalidInput, 'Pass valid --command, --comment, and --agent values. Read feedback submit --help for limits.', undefined, 'invalid_cli_input');
|
|
373
|
-
const confirmed = await confirmMutation(runtime, globals, 'Submit this agent report to
|
|
373
|
+
const confirmed = await confirmMutation(runtime, globals, 'Submit this agent report to Nuxt SEO feedback?');
|
|
374
374
|
if (confirmed._tag === 'Err')
|
|
375
375
|
return confirmed;
|
|
376
376
|
if (!confirmed.value) {
|
|
@@ -1088,65 +1088,6 @@ async function timelineEntries(runtime, globals, args) {
|
|
|
1088
1088
|
}, { signal: runtime.requestSignal }));
|
|
1089
1089
|
return present(runtime, globals, response, value => renderTimeline(value.data));
|
|
1090
1090
|
}
|
|
1091
|
-
async function contentBriefList(runtime, globals, args) {
|
|
1092
|
-
const status = args.status === undefined
|
|
1093
|
-
? ok(undefined)
|
|
1094
|
-
: parseChoice(args.status, {
|
|
1095
|
-
name: '--status',
|
|
1096
|
-
choices: ['queued', 'researching', 'ready', 'written', 'published', 'stale', 'failed', 'archived'],
|
|
1097
|
-
});
|
|
1098
|
-
if (status._tag === 'Err')
|
|
1099
|
-
return status;
|
|
1100
|
-
const limit = parseInteger(args.limit, { name: '--limit', minimum: 1, maximum: 100, defaultValue: 25 });
|
|
1101
|
-
if (limit._tag === 'Err')
|
|
1102
|
-
return limit;
|
|
1103
|
-
const offset = parseInteger(args.offset, { name: '--offset', minimum: 0, defaultValue: 0 });
|
|
1104
|
-
if (offset._tag === 'Err')
|
|
1105
|
-
return offset;
|
|
1106
|
-
const resolved = await apiAndSite(runtime, globals);
|
|
1107
|
-
if (resolved._tag === 'Err')
|
|
1108
|
-
return resolved;
|
|
1109
|
-
return collect(runtime, globals, {
|
|
1110
|
-
all: args.all === true,
|
|
1111
|
-
message: 'Loading Content Briefs',
|
|
1112
|
-
start: { offset: offset.value },
|
|
1113
|
-
fetch: position => resolved.value.api.client.content.listBriefs({
|
|
1114
|
-
params: { siteId: resolved.value.siteId },
|
|
1115
|
-
query: { status: status.value, limit: limit.value, offset: position.offset ?? 0 },
|
|
1116
|
-
}, { signal: runtime.requestSignal }),
|
|
1117
|
-
render: value => renderContentBriefs(value.data),
|
|
1118
|
-
next: value => offsetPage(value.data.page),
|
|
1119
|
-
});
|
|
1120
|
-
}
|
|
1121
|
-
async function contentBriefShow(runtime, globals, briefId) {
|
|
1122
|
-
const resolved = await apiAndSite(runtime, globals);
|
|
1123
|
-
if (resolved._tag === 'Err')
|
|
1124
|
-
return resolved;
|
|
1125
|
-
const response = await withSpinner(runtime, 'Loading Content Brief', () => resolved.value.api.client.content.showBrief({
|
|
1126
|
-
params: { siteId: resolved.value.siteId, briefId },
|
|
1127
|
-
}, { signal: runtime.requestSignal }));
|
|
1128
|
-
return present(runtime, globals, response, value => renderContentBrief(value.data));
|
|
1129
|
-
}
|
|
1130
|
-
async function contentBriefCreate(runtime, globals, args) {
|
|
1131
|
-
const targetPage = args.targetPage === undefined ? ok(undefined) : parseAbsolutePageUrl(args.targetPage);
|
|
1132
|
-
if (targetPage._tag === 'Err')
|
|
1133
|
-
return targetPage;
|
|
1134
|
-
const resolved = await apiAndSite(runtime, globals);
|
|
1135
|
-
if (resolved._tag === 'Err')
|
|
1136
|
-
return resolved;
|
|
1137
|
-
const confirmed = await confirmMutation(runtime, globals, `Create a Content Brief for ${args.keyword}?`);
|
|
1138
|
-
if (confirmed._tag === 'Err')
|
|
1139
|
-
return confirmed;
|
|
1140
|
-
if (!confirmed.value) {
|
|
1141
|
-
writeDiagnostic(runtime, 'Mutation cancelled.');
|
|
1142
|
-
return ok(undefined);
|
|
1143
|
-
}
|
|
1144
|
-
const response = await withSpinner(runtime, 'Creating Content Brief', () => resolved.value.api.client.content.createBrief({
|
|
1145
|
-
params: { siteId: resolved.value.siteId },
|
|
1146
|
-
body: { keyword: args.keyword, targetPage: targetPage.value },
|
|
1147
|
-
}, { signal: runtime.requestSignal }));
|
|
1148
|
-
return present(runtime, globals, response, value => renderContentBriefCreated(value.data));
|
|
1149
|
-
}
|
|
1150
1091
|
const VITALS_FORM_FACTORS = ['phone', 'desktop', 'all'];
|
|
1151
1092
|
const VITALS_METRICS = ['lcp', 'inp', 'cls'];
|
|
1152
1093
|
const VITALS_FORM_FACTOR_WIRE = {
|
|
@@ -1969,51 +1910,6 @@ export function createRootCommand(runtime, globals, execution) {
|
|
|
1969
1910
|
}),
|
|
1970
1911
|
},
|
|
1971
1912
|
});
|
|
1972
|
-
const content = defineCommand({
|
|
1973
|
-
meta: { name: 'content', description: 'Read and create Content Briefs' },
|
|
1974
|
-
subCommands: {
|
|
1975
|
-
briefs: defineCommand({
|
|
1976
|
-
meta: { name: 'briefs', description: 'Manage Content Briefs' },
|
|
1977
|
-
subCommands: {
|
|
1978
|
-
list: defineCommand({
|
|
1979
|
-
meta: { name: 'list', description: 'List Content Briefs' },
|
|
1980
|
-
args: {
|
|
1981
|
-
status: {
|
|
1982
|
-
type: 'enum',
|
|
1983
|
-
options: ['queued', 'researching', 'ready', 'written', 'published', 'stale', 'failed', 'archived'],
|
|
1984
|
-
description: 'Filter by status',
|
|
1985
|
-
},
|
|
1986
|
-
limit: { type: 'string', description: 'Maximum Content Briefs, 1 to 100' },
|
|
1987
|
-
offset: { type: 'string', description: 'Pagination offset' },
|
|
1988
|
-
all: { type: 'boolean', description: 'Repeat until every page is written; one envelope per page' },
|
|
1989
|
-
},
|
|
1990
|
-
run: ({ args }) => capture(execution, () => contentBriefList(runtime, globals, {
|
|
1991
|
-
status: args.status,
|
|
1992
|
-
limit: args.limit,
|
|
1993
|
-
offset: args.offset,
|
|
1994
|
-
all: args.all,
|
|
1995
|
-
}), args._, 0)(),
|
|
1996
|
-
}),
|
|
1997
|
-
show: defineCommand({
|
|
1998
|
-
meta: { name: 'show', description: 'Show one Content Brief' },
|
|
1999
|
-
args: { briefId: positional('brief-id', 'Content Brief ID') },
|
|
2000
|
-
run: ({ args }) => capture(execution, () => contentBriefShow(runtime, globals, args.briefId), args._, 1)(),
|
|
2001
|
-
}),
|
|
2002
|
-
create: defineCommand({
|
|
2003
|
-
meta: { name: 'create', description: 'Create a Content Brief' },
|
|
2004
|
-
args: {
|
|
2005
|
-
'keyword': positional('keyword', 'Target keyword'),
|
|
2006
|
-
'target-page': { type: 'string', description: 'Absolute target Page URL' },
|
|
2007
|
-
},
|
|
2008
|
-
run: ({ args }) => capture(execution, () => contentBriefCreate(runtime, globals, {
|
|
2009
|
-
keyword: args.keyword,
|
|
2010
|
-
targetPage: args['target-page'],
|
|
2011
|
-
}), args._, 1)(),
|
|
2012
|
-
}),
|
|
2013
|
-
},
|
|
2014
|
-
}),
|
|
2015
|
-
},
|
|
2016
|
-
});
|
|
2017
1913
|
const search = defineCommand({
|
|
2018
1914
|
meta: { name: 'search', description: 'Read Search Console state' },
|
|
2019
1915
|
subCommands: {
|
|
@@ -2217,10 +2113,10 @@ export function createRootCommand(runtime, globals, execution) {
|
|
|
2217
2113
|
meta: {
|
|
2218
2114
|
name: 'nuxtseo',
|
|
2219
2115
|
version: VERSION,
|
|
2220
|
-
description: 'Use
|
|
2116
|
+
description: 'Use Nuxt SEO from a terminal or automation',
|
|
2221
2117
|
},
|
|
2222
2118
|
args: {
|
|
2223
|
-
'api-url': { type: 'string', description: 'Override the
|
|
2119
|
+
'api-url': { type: 'string', description: 'Override the Nuxt SEO API host' },
|
|
2224
2120
|
'site': { type: 'string', description: 'Override the selected Site' },
|
|
2225
2121
|
'json': { type: 'boolean', description: 'Write machine JSON; protocol envelopes remain unchanged' },
|
|
2226
2122
|
'no-input': { type: 'boolean', description: 'Disable every interactive prompt' },
|
|
@@ -2255,7 +2151,7 @@ export function createRootCommand(runtime, globals, execution) {
|
|
|
2255
2151
|
}),
|
|
2256
2152
|
sites,
|
|
2257
2153
|
feedback: defineCommand({
|
|
2258
|
-
meta: { name: 'feedback', description: 'Report CLI problems to
|
|
2154
|
+
meta: { name: 'feedback', description: 'Report CLI problems to Nuxt SEO' },
|
|
2259
2155
|
subCommands: {
|
|
2260
2156
|
submit: defineCommand({
|
|
2261
2157
|
meta: { name: 'submit', description: 'Submit sanitized agent feedback without selecting a Site' },
|
|
@@ -2290,7 +2186,6 @@ export function createRootCommand(runtime, globals, execution) {
|
|
|
2290
2186
|
actions,
|
|
2291
2187
|
audit,
|
|
2292
2188
|
backlinks,
|
|
2293
|
-
content,
|
|
2294
2189
|
mentions,
|
|
2295
2190
|
page,
|
|
2296
2191
|
performance: defineCommand({
|
package/dist/pull.js
CHANGED
|
@@ -129,10 +129,6 @@ export const PULL_OPERATIONS = [
|
|
|
129
129
|
params: { siteId: context.siteId },
|
|
130
130
|
query: { limit: 100, includeFiltered: publicV1BooleanFlagValue(false) },
|
|
131
131
|
}, { signal: context.signal })),
|
|
132
|
-
operation('content briefs list', context => context.client.content.listBriefs({
|
|
133
|
-
params: { siteId: context.siteId },
|
|
134
|
-
query: { limit: 25, offset: 0 },
|
|
135
|
-
}, { signal: context.signal })),
|
|
136
132
|
operation('timeline list', context => context.client.timeline.entries({
|
|
137
133
|
params: { siteId: context.siteId },
|
|
138
134
|
query: { limit: 25 },
|
package/dist/render.d.ts
CHANGED
|
@@ -4,7 +4,6 @@ import type { ActionDismiss, ActionList, ActionResolve, ActionShow } from '@nuxt
|
|
|
4
4
|
import type { AnalyticsQueryData, AnalyticsView } from '@nuxtseo/protocol/v1/analytics';
|
|
5
5
|
import type { AuditChanges, AuditContentDecay, AuditDuplicateClusters, AuditLinkOpportunities, AuditLinkStructure } from '@nuxtseo/protocol/v1/audit';
|
|
6
6
|
import type { BacklinkAnchors, BacklinksHistory, BacklinksSummary, MentionsData, RecoverableBacklinksData, ReferringDomains } from '@nuxtseo/protocol/v1/backlinks';
|
|
7
|
-
import type { ContentBrief, ContentBriefCreateData, ContentBriefListData } from '@nuxtseo/protocol/v1/content';
|
|
8
7
|
import type { IndexCohorts, IndexingDiagnosticsData, IndexingHistoryData, SearchAnalyticsData, SearchStatusData, UrlInspectionData } from '@nuxtseo/protocol/v1/gsc';
|
|
9
8
|
import type { PageInspectData, PageIssues, PageScan } from '@nuxtseo/protocol/v1/pages';
|
|
10
9
|
import type { FieldVitalFindings, FieldVitals, MonitoredPages, PerformanceScanDetail, PerformanceScans, SitePerformanceOverview } from '@nuxtseo/protocol/v1/performance';
|
|
@@ -39,9 +38,6 @@ export declare function renderResearchOverview(data: StoredResearchOverview): st
|
|
|
39
38
|
export declare function renderKeywordResearch(data: KeywordResearchData): string;
|
|
40
39
|
export declare function renderSerpResearch(data: SerpResearchData): string;
|
|
41
40
|
export declare function renderRankingsResearch(data: RankingsResearchData): string;
|
|
42
|
-
export declare function renderContentBriefs(data: ContentBriefListData): string;
|
|
43
|
-
export declare function renderContentBrief(data: ContentBrief): string;
|
|
44
|
-
export declare function renderContentBriefCreated(data: ContentBriefCreateData): string;
|
|
45
41
|
export declare function renderContentDecay(data: AuditContentDecay): string;
|
|
46
42
|
export declare function renderDuplicateClusters(data: AuditDuplicateClusters): string;
|
|
47
43
|
export declare function renderLinkOpportunities(data: AuditLinkOpportunities): string;
|
package/dist/render.js
CHANGED
|
@@ -482,27 +482,6 @@ export function renderRankingsResearch(data) {
|
|
|
482
482
|
...data.keywords.map(row => `${row.position}. ${row.keyword} ${row.volume} volume ${row.traffic} traffic\n ${row.url}`),
|
|
483
483
|
].join('\n');
|
|
484
484
|
}
|
|
485
|
-
export function renderContentBriefs(data) {
|
|
486
|
-
if (data.briefs.length === 0)
|
|
487
|
-
return 'No Content Briefs match these filters.';
|
|
488
|
-
return [
|
|
489
|
-
`Content Briefs (${data.briefs.length} of ${data.page.total})`,
|
|
490
|
-
...data.briefs.map(brief => `${brief.id} ${brief.status}\n ${brief.keyword}`),
|
|
491
|
-
].join('\n');
|
|
492
|
-
}
|
|
493
|
-
export function renderContentBrief(data) {
|
|
494
|
-
return fields([
|
|
495
|
-
['Content Brief', data.id],
|
|
496
|
-
['Keyword', data.keyword],
|
|
497
|
-
['Status', data.status],
|
|
498
|
-
['Generated', data.generatedAt],
|
|
499
|
-
['Updated', data.updatedAt],
|
|
500
|
-
['Error', data.error],
|
|
501
|
-
]);
|
|
502
|
-
}
|
|
503
|
-
export function renderContentBriefCreated(data) {
|
|
504
|
-
return `${data.created ? 'Created' : 'Found existing'} Content Brief.\n${renderContentBrief(data.brief)}`;
|
|
505
|
-
}
|
|
506
485
|
export function renderContentDecay(data) {
|
|
507
486
|
if (!data.connected)
|
|
508
487
|
return 'Connect Search Console to find content decay.';
|
package/dist/state/auth.js
CHANGED
|
@@ -65,7 +65,7 @@ function keyringFailure(operation, paths, cause) {
|
|
|
65
65
|
_tag: 'KeychainFailure',
|
|
66
66
|
operation,
|
|
67
67
|
path: paths.authFile,
|
|
68
|
-
message: `Could not ${operation} the
|
|
68
|
+
message: `Could not ${operation} the Nuxt SEO credential in the OS keychain. Local auth state is recorded at ${paths.authFile}.`,
|
|
69
69
|
cause,
|
|
70
70
|
});
|
|
71
71
|
}
|
|
@@ -148,7 +148,7 @@ export async function saveCredential(value, options = {}) {
|
|
|
148
148
|
return err({
|
|
149
149
|
_tag: 'InvalidCredential',
|
|
150
150
|
source: 'input',
|
|
151
|
-
message: 'The
|
|
151
|
+
message: 'The Nuxt SEO API token cannot be empty.',
|
|
152
152
|
});
|
|
153
153
|
}
|
|
154
154
|
const paths = options.paths ?? defaultStatePaths();
|
package/dist/state/files.js
CHANGED
|
@@ -19,7 +19,7 @@ function statePath(paths, kind) {
|
|
|
19
19
|
function corruptState(kind, path, detail, cause) {
|
|
20
20
|
const common = {
|
|
21
21
|
path,
|
|
22
|
-
message: `Could not parse
|
|
22
|
+
message: `Could not parse Nuxt SEO ${kind} state at ${path}: ${detail}`,
|
|
23
23
|
...(cause === undefined ? {} : { cause }),
|
|
24
24
|
};
|
|
25
25
|
return kind === 'auth'
|
|
@@ -37,7 +37,7 @@ export async function readStateJson(paths, kind) {
|
|
|
37
37
|
kind,
|
|
38
38
|
operation: 'read',
|
|
39
39
|
path,
|
|
40
|
-
message: `Could not read
|
|
40
|
+
message: `Could not read Nuxt SEO ${kind} state at ${path}.`,
|
|
41
41
|
cause: content,
|
|
42
42
|
});
|
|
43
43
|
}
|
|
@@ -76,7 +76,7 @@ export async function writeStateJson(paths, kind, value) {
|
|
|
76
76
|
kind,
|
|
77
77
|
operation: 'write',
|
|
78
78
|
path,
|
|
79
|
-
message: `Could not write
|
|
79
|
+
message: `Could not write Nuxt SEO ${kind} state at ${path}: the filesystem is read-only or permission was denied.`,
|
|
80
80
|
cause,
|
|
81
81
|
}
|
|
82
82
|
: {
|
|
@@ -84,7 +84,7 @@ export async function writeStateJson(paths, kind, value) {
|
|
|
84
84
|
kind,
|
|
85
85
|
operation: 'write',
|
|
86
86
|
path,
|
|
87
|
-
message: `Could not write
|
|
87
|
+
message: `Could not write Nuxt SEO ${kind} state at ${path}.`,
|
|
88
88
|
cause,
|
|
89
89
|
}));
|
|
90
90
|
if (written._tag === 'Err' && temporaryCreated) {
|
|
@@ -110,7 +110,7 @@ export async function removeStateFile(paths, kind) {
|
|
|
110
110
|
kind,
|
|
111
111
|
operation: 'delete',
|
|
112
112
|
path,
|
|
113
|
-
message: `Could not remove
|
|
113
|
+
message: `Could not remove Nuxt SEO ${kind} state at ${path}: the filesystem is read-only or permission was denied.`,
|
|
114
114
|
cause,
|
|
115
115
|
}
|
|
116
116
|
: {
|
|
@@ -118,7 +118,7 @@ export async function removeStateFile(paths, kind) {
|
|
|
118
118
|
kind,
|
|
119
119
|
operation: 'delete',
|
|
120
120
|
path,
|
|
121
|
-
message: `Could not remove
|
|
121
|
+
message: `Could not remove Nuxt SEO ${kind} state at ${path}.`,
|
|
122
122
|
cause,
|
|
123
123
|
});
|
|
124
124
|
});
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nuxtseo/cli",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
5
|
-
"description": "Command line interface for the
|
|
4
|
+
"version": "0.4.0",
|
|
5
|
+
"description": "Command line interface for the Nuxt SEO public API.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://nuxtseo.com/pro",
|
|
8
8
|
"repository": {
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@clack/prompts": "^1.8.0",
|
|
36
|
-
"@nuxtseo/protocol": "^0.
|
|
37
|
-
"@nuxtseo/sdk": "^0.
|
|
36
|
+
"@nuxtseo/protocol": "^0.4.0",
|
|
37
|
+
"@nuxtseo/sdk": "^0.4.0",
|
|
38
38
|
"citty": "^0.2.2",
|
|
39
39
|
"pathe": "^2.0.3"
|
|
40
40
|
},
|
|
@@ -1,149 +1,85 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: nuxtseo-cli
|
|
3
|
-
description: Drives the `nuxtseo` CLI to triage a Site through the
|
|
3
|
+
description: Drives the `nuxtseo` CLI to triage a Site through the Nuxt SEO public API: Site status and ranked issues, Search Console rows, route-family indexing, field and lab Core Web Vitals, Lighthouse Scans, Page Issues, keyword competitor and domain research, backlinks, SERP analysis, rankings, link opportunities, content decay, duplicate clusters, chart annotations, and issue resolution. Use whenever the user mentions NuxtSEO, the `nuxtseo` command, Site SEO work, or Nuxt SEO automation.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# `nuxtseo` CLI
|
|
7
7
|
|
|
8
|
-
`nuxtseo` reads a Site through the
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
Use it to answer "what is wrong with this site, and what did I break". Then fix
|
|
13
|
-
the code in the repository you are working in.
|
|
8
|
+
`nuxtseo` reads a Site through the Nuxt SEO public API. Use it to answer "what
|
|
9
|
+
is wrong with this site, and what did I break", then fix the cause in the
|
|
10
|
+
repository you are working in. There is no MCP or private-route fallback.
|
|
14
11
|
|
|
15
12
|
## Reference files
|
|
16
13
|
|
|
17
|
-
Read
|
|
18
|
-
|
|
19
|
-
- [references/commands.md](references/commands.md): every command, with its
|
|
20
|
-
flags, ranges, and defaults. Read it instead of guessing a flag.
|
|
21
|
-
- [references/protocol.md](references/protocol.md): response envelopes, paging,
|
|
22
|
-
and exit codes. Read it before you parse output or handle a failure.
|
|
23
|
-
- [references/indexing.md](references/indexing.md): how to answer "is this
|
|
24
|
-
indexed". Read it before you report an indexed count, or name the URLs
|
|
25
|
-
Google indexed.
|
|
26
|
-
|
|
27
|
-
## Get the binary
|
|
28
|
-
|
|
29
|
-
```sh
|
|
30
|
-
pnpm dlx @nuxtseo/cli@latest --version # no install
|
|
31
|
-
pnpm add -g @nuxtseo/cli # or npm install --global
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
Node 22 or newer is required.
|
|
35
|
-
|
|
36
|
-
Inside the NuxtSEO monorepo the package is not published. Run the built entry
|
|
37
|
-
directly:
|
|
38
|
-
|
|
39
|
-
```sh
|
|
40
|
-
node packages/cli/dist/cli-entry.js --version
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
Exit `127` or `command not found` means the CLI is absent. That is not exit
|
|
44
|
-
`3`; no token has been checked yet. Install it, then continue.
|
|
45
|
-
|
|
46
|
-
## Before the first command
|
|
47
|
-
|
|
48
|
-
```sh
|
|
49
|
-
nuxtseo whoami --json # validates the token and reports its scopes
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
`whoami` returns the Team, the role, the granted scopes, and the token expiry.
|
|
53
|
-
Read `data.scopes` before a write command. A missing scope is exit `4`, and the
|
|
54
|
-
scope list tells you that before you spend the request.
|
|
55
|
-
|
|
56
|
-
Exit `3` means there is no working token. Ask the user to run `nuxtseo login`
|
|
57
|
-
themselves. It opens a browser, they approve the pairing, and the CLI stores
|
|
58
|
-
the token. Do not run `login` for them. It needs a person at the browser, and
|
|
59
|
-
in a non-interactive shell it falls back to reading a token from stdin.
|
|
60
|
-
|
|
61
|
-
The alternative is a token created at
|
|
62
|
-
<https://nuxtseo.com/pro/dashboard/settings/api-tokens> and exported as
|
|
63
|
-
`NUXTSEO_TOKEN`. Either way the token is bound to a Team role, and that role
|
|
64
|
-
gates what it can do.
|
|
65
|
-
|
|
66
|
-
Never put a token in a command argument, a file you write, or a commit.
|
|
67
|
-
`login` reads a token from a hidden prompt or stdin only.
|
|
68
|
-
|
|
69
|
-
Resolve the Site once and reuse it. `nuxtseo sites list --json` prints every
|
|
70
|
-
accessible Site with its ID.
|
|
71
|
-
|
|
72
|
-
## How to call it as an agent
|
|
14
|
+
Read the matching file before you act on its subject:
|
|
73
15
|
|
|
74
|
-
|
|
75
|
-
`feedback submit` needs no Site.
|
|
76
|
-
|
|
77
|
-
```sh
|
|
78
|
-
nuxtseo actions list --site site_123 --json
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
`--json` writes the complete protocol envelope to stdout and one newline. It
|
|
82
|
-
also disables prompts. Diagnostics, warnings, spinners, and failure messages go
|
|
83
|
-
to stderr, so stdout stays parseable.
|
|
84
|
-
|
|
85
|
-
Read `data.scopes` from `whoami` before you plan a loop, not when a step fails.
|
|
86
|
-
A Team token carries only the scopes its role allows. A `viewer` token carries
|
|
87
|
-
every `*:read` scope plus `feedback:write`, and nothing else. That token reads
|
|
88
|
-
the whole Site and cannot write, so steps 5, 6, and 7 of the triage loop below
|
|
89
|
-
all exit `4`: `page scan` needs `page:write`, `actions resolve` and `actions
|
|
90
|
-
dismiss` need `actions:write`, and the `annotations` writes need
|
|
91
|
-
`timeline:write`. `content briefs create` needs `content:write`, and the
|
|
92
|
-
`sitemaps` writes need `sites:write`. Live research reads need only
|
|
93
|
-
`research:read`, so a `viewer` token can still run them.
|
|
94
|
-
|
|
95
|
-
If a write scope is missing, do steps 1 to 4, then hand the user the exact
|
|
96
|
-
commands for the rest. Do not spend calls discovering the block one step at a
|
|
97
|
-
time.
|
|
98
|
-
|
|
99
|
-
`--site` matters for a second reason. An explicit Site ID goes straight to the
|
|
100
|
-
operation and skips the Sites read. A token whose role cannot list Sites still
|
|
101
|
-
works. If more than one Site is accessible and `--site` is absent, the CLI
|
|
102
|
-
exits `5` rather than guessing.
|
|
103
|
-
|
|
104
|
-
Other flags worth knowing:
|
|
105
|
-
|
|
106
|
-
| Flag | Use it when |
|
|
16
|
+
| File | Read it before you |
|
|
107
17
|
| --- | --- |
|
|
108
|
-
|
|
|
109
|
-
|
|
|
110
|
-
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
18
|
+
| [references/commands.md](references/commands.md) | use a flag, range, or default. Never guess a flag |
|
|
19
|
+
| [references/protocol.md](references/protocol.md) | parse output, page through a list, or handle an exit code |
|
|
20
|
+
| [references/indexing.md](references/indexing.md) | report an indexed count or name indexed URLs |
|
|
21
|
+
|
|
22
|
+
## Setup
|
|
23
|
+
|
|
24
|
+
1. **Binary.** Exit `127` means the CLI is absent. Install it, then continue:
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
pnpm dlx @nuxtseo/cli@latest --version # no install
|
|
28
|
+
pnpm add -g @nuxtseo/cli # global install, Node 22+
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Inside the Nuxt SEO monorepo, run `node packages/cli/dist/cli-entry.js`
|
|
32
|
+
instead.
|
|
33
|
+
|
|
34
|
+
2. **Skill version.** Run `nuxtseo --version --json`. If stderr carries a line
|
|
35
|
+
like `skill 0.3.0 installed, CLI 0.4.0. Refresh with: nuxtseo skill install
|
|
36
|
+
--agent claude`, run that command and re-read this file. A stale skill hides
|
|
37
|
+
commands, so a missing command reads as a missing feature. If
|
|
38
|
+
`NUXTSEO_NO_UPDATE_CHECK` is set, the line never appears; compare this
|
|
39
|
+
file's frontmatter `version` with the binary instead.
|
|
40
|
+
|
|
41
|
+
3. **Token.** Run `nuxtseo whoami --json`. It returns the Team, role, scopes,
|
|
42
|
+
and token expiry. Exit `3` means no working token. Ask the user to run
|
|
43
|
+
`nuxtseo login` themselves, because a person must approve it in a browser.
|
|
44
|
+
The other route is a token from
|
|
45
|
+
<https://nuxtseo.com/pro/dashboard/settings/api-tokens>, exported as
|
|
46
|
+
`NUXTSEO_TOKEN`. Never put a token in an argument, a file, or a commit.
|
|
47
|
+
|
|
48
|
+
4. **Scopes.** Read `data.scopes` now, not when a step fails. A `viewer` token
|
|
49
|
+
carries every `*:read` scope plus `feedback:write`. It can run every read,
|
|
50
|
+
including live research, and no write:
|
|
51
|
+
|
|
52
|
+
| Write | Scope |
|
|
53
|
+
| --- | --- |
|
|
54
|
+
| `page scan` | `page:write` |
|
|
55
|
+
| `actions resolve`, `actions dismiss` | `actions:write` |
|
|
56
|
+
| `annotations` writes | `timeline:write` |
|
|
57
|
+
| `sitemaps submit`, `sitemaps delete` | `sites:write` |
|
|
58
|
+
|
|
59
|
+
If a write scope is missing, do the reads, then hand the user the exact
|
|
60
|
+
write commands. Do not discover the block one exit `4` at a time.
|
|
61
|
+
|
|
62
|
+
5. **Site.** Run `nuxtseo sites list --json` once and reuse the ID. If more
|
|
63
|
+
than one Site is accessible and `--site` is absent, the CLI exits `5`
|
|
64
|
+
rather than guess.
|
|
65
|
+
|
|
66
|
+
## Calling convention
|
|
67
|
+
|
|
68
|
+
- Always pass `--json` and `--site <site-id>`. `feedback submit` needs no Site.
|
|
69
|
+
- `--json` writes one envelope to stdout. Diagnostics go to stderr. Parse the
|
|
70
|
+
JSON; never scrape human output.
|
|
71
|
+
- A mutation needs `--yes`. Without it, a non-interactive run exits `2`.
|
|
72
|
+
- `--timeout-ms` raises the 30000 ms deadline, up to 300000.
|
|
73
|
+
- An unknown option exits `2` before any network work.
|
|
74
|
+
- Branch on the exit code, never on message text. The table is in
|
|
75
|
+
[references/protocol.md](references/protocol.md).
|
|
138
76
|
|
|
139
77
|
## The triage loop
|
|
140
78
|
|
|
141
|
-
|
|
142
|
-
your progress:
|
|
79
|
+
Copy this checklist and track your progress:
|
|
143
80
|
|
|
144
81
|
```
|
|
145
82
|
Triage progress:
|
|
146
|
-
- [ ] 0. version check: this skill matches the binary
|
|
147
83
|
- [ ] 1. status: read the verdict and the Next Action
|
|
148
84
|
- [ ] 2. actions list: pick a ranked action, check its freshness
|
|
149
85
|
- [ ] 3. actions show: read the evidence behind it
|
|
@@ -153,116 +89,90 @@ Triage progress:
|
|
|
153
89
|
- [ ] 7. annotations create: mark the day the fix shipped
|
|
154
90
|
```
|
|
155
91
|
|
|
156
|
-
**
|
|
92
|
+
**1. Read the verdict.**
|
|
157
93
|
|
|
158
94
|
```sh
|
|
159
|
-
nuxtseo --
|
|
95
|
+
nuxtseo status --site <site-id> --json
|
|
160
96
|
```
|
|
161
97
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
98
|
+
```json
|
|
99
|
+
{
|
|
100
|
+
"data": {
|
|
101
|
+
"available": true,
|
|
102
|
+
"dataQuality": { "status": "complete", "reason": null },
|
|
103
|
+
"nextAction": { "kind": "action", "actionId": "act_01JXYZ" }
|
|
104
|
+
},
|
|
105
|
+
"meta": { "requestId": "req_01JXYZ", "version": "1.0" }
|
|
106
|
+
}
|
|
170
107
|
```
|
|
171
108
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
```sh
|
|
179
|
-
nuxtseo status --site <site-id> --json
|
|
180
|
-
```
|
|
109
|
+
- `available: false` means the first assessment has not run.
|
|
110
|
+
- Read `dataQuality.status` before you quote the verdict. `degraded` means
|
|
111
|
+
proofs were missing, so the verdict is Provisional. `unavailable` means it
|
|
112
|
+
has not run.
|
|
113
|
+
- If `nextAction.kind` is `action`, pass its `actionId` to step 3.
|
|
181
114
|
|
|
182
|
-
|
|
183
|
-
|
|
115
|
+
For the whole Site instead of the next action, run one `pull`. It performs
|
|
116
|
+
every spend-free Site read and writes one envelope per line:
|
|
184
117
|
|
|
185
118
|
```sh
|
|
186
119
|
nuxtseo pull --site <site-id> --json > site.ndjson
|
|
187
120
|
```
|
|
188
121
|
|
|
189
|
-
|
|
190
|
-
line. It spends nothing, and it never mutates. Read
|
|
191
|
-
[references/commands.md](references/commands.md) for its filters and its
|
|
192
|
-
failure behaviour. Then return to step 2 with the rows it gave you.
|
|
193
|
-
|
|
194
|
-
This is the cheapest orientation: the verdict, the single ranked Next Action,
|
|
195
|
-
and what changed recently. Read `dataQuality.status` before quoting it.
|
|
196
|
-
`degraded` means the assessment ran with proofs missing, so the verdict is real
|
|
197
|
-
but Provisional. `unavailable` means it has not run. If `nextAction.kind` is
|
|
198
|
-
`action`, its `actionId` goes straight into step 3.
|
|
199
|
-
|
|
200
|
-
**Step 2. Pick a ranked action.**
|
|
122
|
+
**2. Pick a ranked action.**
|
|
201
123
|
|
|
202
124
|
```sh
|
|
203
125
|
nuxtseo actions list --site <site-id> --json
|
|
204
126
|
```
|
|
205
127
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
since the observation.
|
|
128
|
+
Keep server order. Check `evidence.freshness` on each row. If `verdict` is
|
|
129
|
+
`aged` with a large `ageHours`, live-check a cheap sample before you fix. The
|
|
130
|
+
Site may have changed since the observation.
|
|
210
131
|
|
|
211
|
-
**
|
|
132
|
+
**3. Read the evidence.**
|
|
212
133
|
|
|
213
134
|
```sh
|
|
214
135
|
nuxtseo actions show <action-id> --site <site-id> --json
|
|
215
136
|
```
|
|
216
137
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
Two reads deepen this step when the action's own evidence is not enough:
|
|
138
|
+
It names the pages, the finding type, and when it was observed. Two reads go
|
|
139
|
+
deeper:
|
|
220
140
|
|
|
221
|
-
- `
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
membership.
|
|
226
|
-
- `nuxtseo scans show <scan-id> --site <site-id> --json` returns the failing
|
|
227
|
-
checks behind a Lighthouse score, so you can locate the cause without
|
|
228
|
-
re-scanning.
|
|
141
|
+
- `page issues --action-id <action-id>`: the raw observations, with URLs,
|
|
142
|
+
status codes, redirect targets, and Lighthouse selectors. This is the
|
|
143
|
+
mutable observation store, never canonical action membership.
|
|
144
|
+
- `scans show <scan-id>`: the failing checks behind a Lighthouse score.
|
|
229
145
|
|
|
230
|
-
**
|
|
146
|
+
**4. Fix the cause.** Map the URLs in the evidence to routes, components, or
|
|
147
|
+
config.
|
|
231
148
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
**Step 5. Re-scan a page you changed.**
|
|
235
|
-
|
|
236
|
-
Deploy or preview the fix first. Then ask for fresh Scans:
|
|
149
|
+
**5. Re-scan.** Deploy or preview the fix first:
|
|
237
150
|
|
|
238
151
|
```sh
|
|
239
152
|
nuxtseo page scan <url> --site <site-id> --yes --json
|
|
240
153
|
```
|
|
241
154
|
|
|
242
|
-
**
|
|
155
|
+
**6. Claim the action.**
|
|
243
156
|
|
|
244
157
|
```sh
|
|
245
158
|
nuxtseo actions resolve <action-id> --site <site-id> --yes --json
|
|
246
159
|
```
|
|
247
160
|
|
|
248
|
-
The server verifies
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
exits `5` with `stale_evidence`. Re-run step 3 and decide again.
|
|
161
|
+
The server verifies the claim; the CLI never marks anything fixed. Resolve
|
|
162
|
+
sends the current `artifactVersion` for you. If the evidence moved, it exits
|
|
163
|
+
`5` with `stale_evidence`: re-run step 3 and decide again.
|
|
252
164
|
|
|
253
|
-
|
|
254
|
-
|
|
165
|
+
If the fix is a deliberate removal, dismiss instead. Read `artifactVersion`
|
|
166
|
+
from `actions show`, then:
|
|
255
167
|
|
|
256
168
|
```sh
|
|
257
|
-
nuxtseo actions show <action-id> --site <site-id> --json # read artifactVersion
|
|
258
169
|
nuxtseo actions dismiss <action-id> --site <site-id> --artifact-version <v> --yes --json
|
|
259
170
|
```
|
|
260
171
|
|
|
261
|
-
|
|
262
|
-
evidence and no internal referrers.
|
|
263
|
-
used to hide work.
|
|
172
|
+
The server admits a dismiss only for a broken-page action with complete 404 or
|
|
173
|
+
410 evidence and no internal referrers.
|
|
264
174
|
|
|
265
|
-
**
|
|
175
|
+
**7. Mark the day.**
|
|
266
176
|
|
|
267
177
|
```sh
|
|
268
178
|
nuxtseo annotations create --site <site-id> --date "$(date -u +%F)" --title "Fixed canonicals on /docs" --yes --json
|
|
@@ -270,127 +180,93 @@ nuxtseo annotations create --site <site-id> --date "$(date -u +%F)" --title "Fix
|
|
|
270
180
|
|
|
271
181
|
The next traffic move then has a cause beside it.
|
|
272
182
|
|
|
273
|
-
##
|
|
274
|
-
|
|
275
|
-
Two datasets share the word "vitals":
|
|
276
|
-
|
|
277
|
-
- `performance` and `scans *` read Lighthouse **lab** Scans.
|
|
278
|
-
- `vitals summary`, `vitals trend`, and `vitals findings` read **field**
|
|
279
|
-
(real-user) data. `vitals` reads CrUX. `vitals findings` reads the Site's own
|
|
280
|
-
Web Analytics provider.
|
|
281
|
-
|
|
282
|
-
A lab score and a field p75 disagreeing is normal, not a fault.
|
|
283
|
-
|
|
284
|
-
Two commands inspect the same URL and answer different questions. `page inspect`
|
|
285
|
-
reads the NuxtSEO observation store. `search inspect` reads Google's index
|
|
286
|
-
verdict.
|
|
287
|
-
|
|
288
|
-
`status`, `performance`, and `vitals` answer different questions. `status` is
|
|
289
|
-
the verdict. `performance` is the lab score. `vitals` is what real users
|
|
290
|
-
measured. Never substitute one for another.
|
|
291
|
-
|
|
292
|
-
The Search Console reads also split:
|
|
183
|
+
## Reading results correctly
|
|
293
184
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
`search inspect` read retained Search Console evidence through the public API.
|
|
297
|
-
- `sitemaps submit` and `sitemaps delete` reach Google through the Site's
|
|
298
|
-
stored Search Console credential. Both are mutations.
|
|
185
|
+
**Empty is not clean.** Each read names its own kind of empty. Say which one
|
|
186
|
+
you got:
|
|
299
187
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
`
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
188
|
+
| Command | Empty signal |
|
|
189
|
+
| --- | --- |
|
|
190
|
+
| `page inspect` | `observations.coverage` |
|
|
191
|
+
| `page issues` | `availableKeys` |
|
|
192
|
+
| `search cohorts` | `analysed: false`: no completed crawl, or no Google index state |
|
|
193
|
+
| `vitals summary` | `available: false` |
|
|
194
|
+
| `research keywords`, `research rankings`, `research domain-*`, `page issues`, `search cohorts`, `vitals findings` | `data.message`, and `data.tip` for the repair |
|
|
195
|
+
|
|
196
|
+
Read `data.message` before the list. For example, `research keywords` refuses
|
|
197
|
+
a seed over three words, still exits `0`, and returns `keywords: []` with
|
|
198
|
+
`evidence._tag: "no-provider"`. That is a refused argument, not zero demand.
|
|
199
|
+
|
|
200
|
+
**A refusal is not an empty Site.** Exit `4` is a plan, scope, or entitlement
|
|
201
|
+
blocker, for example `entitlement_required`. Exit `5` on `status`, `page
|
|
202
|
+
issues`, or `search cohorts` can mean an archived or paused Site. Report the
|
|
203
|
+
blocker and stop. Do not read it as "no data" or "nothing wrong", and do not
|
|
204
|
+
retry unchanged.
|
|
205
|
+
|
|
206
|
+
**Quote the count the result ships.** Never quote the length of a list. Use
|
|
207
|
+
`data.total` from `page issues`, and `coverage.accessibilityChecksTotal` and
|
|
208
|
+
`coverage.bestPracticesChecksTotal` from `scans show`. `possiblyTruncated:
|
|
209
|
+
true` marks the list as a floor.
|
|
210
|
+
|
|
211
|
+
**Keep these datasets apart:**
|
|
212
|
+
|
|
213
|
+
- `performance` and `scans *` read Lighthouse **lab** Scans. `vitals summary`
|
|
214
|
+
and `vitals trend` read CrUX **field** data. `vitals findings` reads the
|
|
215
|
+
Site's own Web Analytics provider. A lab score and a field p75 that disagree
|
|
216
|
+
is normal.
|
|
217
|
+
- `status` is the verdict, `performance` the lab score, `vitals` what real
|
|
218
|
+
users measured. Never substitute one for another.
|
|
219
|
+
- `page inspect` reads the Nuxt SEO observation store. `search inspect` reads
|
|
220
|
+
Google's index verdict for the same URL.
|
|
221
|
+
- `search status` reads the stored connection and never waits for Google.
|
|
222
|
+
`search analytics`, `search indexing`, `search index-history`, and
|
|
223
|
+
`search inspect` read retained Search Console evidence.
|
|
224
|
+
- An indexed count comes from `search indexing`, never from analytics rows.
|
|
225
|
+
See [references/indexing.md](references/indexing.md).
|
|
226
|
+
|
|
227
|
+
**Route families: `established` versus `ranked`.** Read `search cohorts`
|
|
228
|
+
before you list single not-indexed URLs. Never merge its two lists:
|
|
229
|
+
|
|
230
|
+
- `established` holds only families whose not-indexed rate is significant
|
|
231
|
+
against the rest of the Site, after a Bonferroni correction. Only these rows
|
|
232
|
+
are a finding or a cause.
|
|
233
|
+
- `ranked` orders every family by raw rate, with no significance test. A row
|
|
234
|
+
with `statisticallyEstablished: false` is a lead to verify, for example by
|
|
235
|
+
inspecting its URLs. Presenting it as a cause is a hard failure.
|
|
306
236
|
|
|
307
|
-
|
|
308
|
-
not-indexed rate of their **own complement** after a Bonferroni correction.
|
|
309
|
-
These are the only rows you may report as a finding or a cause.
|
|
310
|
-
- `ranked` orders every family by raw rate, with **no significance test**. It
|
|
311
|
-
always answers "where is indexing worst", even when nothing is provable. A
|
|
312
|
-
row with `statisticallyEstablished: false` is a lead to verify, never a
|
|
313
|
-
conclusion. Presenting one as established is a hard failure.
|
|
237
|
+
## Guardrails
|
|
314
238
|
|
|
315
|
-
|
|
316
|
-
|
|
239
|
+
- **Get consent before a mutation.** `page scan`, `actions resolve`, `actions
|
|
240
|
+
dismiss`, `sitemaps submit`, `sitemaps delete`, and the `annotations` writes
|
|
241
|
+
change server state. `sitemaps` writes also reach Google. `--yes` is consent
|
|
242
|
+
you borrow from the user, so ask first unless they asked for that exact
|
|
243
|
+
action.
|
|
244
|
+
- **Spend limits.** `page scan` spends Lighthouse limit. `research *` and the
|
|
245
|
+
`backlinks` reads spend the Team research limit, except `backlinks
|
|
246
|
+
recoverable` and `mentions list`, which read retained rows. A `cache` or
|
|
247
|
+
`no-provider` evidence tag, or `cached: true`, means nothing was spent.
|
|
248
|
+
Check `usage` before any run over many Pages, keywords, or domains.
|
|
249
|
+
- **No shell loops.** Use `pull` for Site reads and `--all` for paging. `--all`
|
|
250
|
+
stops at 50 requests with exit `9`; treat that as a stop and resume with the
|
|
251
|
+
argument stderr names.
|
|
317
252
|
|
|
318
253
|
## Report CLI feedback
|
|
319
254
|
|
|
320
|
-
If CLI
|
|
321
|
-
|
|
322
|
-
|
|
255
|
+
If the CLI blocks or slows your work, report it once per distinct problem, then
|
|
256
|
+
continue the task. This skill authorizes that; no confirmation is needed unless
|
|
257
|
+
the user forbids reporting.
|
|
323
258
|
|
|
324
259
|
```sh
|
|
325
|
-
nuxtseo feedback submit --command "search cohorts" --agent "
|
|
326
|
-
--comment "Agent report by
|
|
260
|
+
nuxtseo feedback submit --command "search cohorts" --agent "<your agent name>" \
|
|
261
|
+
--comment "Agent report by <your agent name>. Expected a reason for missing data. Received an empty list. Used search status instead." \
|
|
327
262
|
--yes --json
|
|
328
263
|
```
|
|
329
264
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
Self-reporting sanitized CLI feedback is authorized by this skill. No separate confirmation is needed.
|
|
340
|
-
This permission covers feedback only. Respect any user instruction that forbids reporting.
|
|
341
|
-
The endpoint requires a working credential with `feedback:write`, available to every Team role.
|
|
342
|
-
It uses no paid limit and allows ten reports per credential each hour.
|
|
343
|
-
|
|
344
|
-
Success returns `data.id` and `data.status: "new"`. Keep the ID with your task notes.
|
|
345
|
-
If reporting fails, mention the failure and continue. Never retry uncertain submissions or report failures recursively.
|
|
346
|
-
If authentication is broken, describe the feedback to the user. Do not invent another endpoint.
|
|
347
|
-
|
|
348
|
-
Reports use the existing feedback queue through `POST /api/v1/account/feedback`.
|
|
349
|
-
Health checks show unresolved reports across all dates.
|
|
350
|
-
Operators use **Pro feedback** in admin to record a fix reference and set `fixed` after verification.
|
|
351
|
-
Fixed, replied, closed, and ignored reports leave the health check list. Reopening restores them.
|
|
352
|
-
|
|
353
|
-
## Guardrails
|
|
354
|
-
|
|
355
|
-
- **Get consent before a mutation.** `actions resolve`, `actions dismiss`,
|
|
356
|
-
`page scan`, `sitemaps submit`, `sitemaps delete`, `content briefs create`,
|
|
357
|
-
and the `annotations` writes all change server state. `page scan` also spends
|
|
358
|
-
against the Lighthouse limit. `--yes` is consent you borrow from the user, so
|
|
359
|
-
ask first, unless the user already asked for that exact action. `actions
|
|
360
|
-
dismiss` says the page stays removed, so use it only when that is the
|
|
361
|
-
decision.
|
|
362
|
-
- **Live research spends against the Team research limit.** `research
|
|
363
|
-
keywords`, `research serp`, `research rankings`, `research domain-traffic`,
|
|
364
|
-
`research domain-availability`, and the `backlinks` reads other than
|
|
365
|
-
`backlinks recoverable` can all start it. Each one reports what it did.
|
|
366
|
-
Keyword, domain, and backlink JSON report cache use in `evidence`; a `cache`
|
|
367
|
-
or `no-provider` tag means nothing was spent. SERP and ranking JSON report
|
|
368
|
-
`cached`. `backlinks recoverable` and `mentions list` read retained rows and
|
|
369
|
-
spend nothing.
|
|
370
|
-
- **Prefer one `pull` over a shell loop.** `pull` performs every spend-free
|
|
371
|
-
Site read in one invocation. Never write a shell loop over `nuxtseo` calls.
|
|
372
|
-
- **Never loop unattended.** Check `usage` before any run over Pages, keywords,
|
|
373
|
-
or domains. Use `--all` rather than your own offset loop. It writes one
|
|
374
|
-
envelope per line and stops at 50 requests with exit `9`. Treat exit `9` as a
|
|
375
|
-
stop, then resume with the argument stderr names.
|
|
376
|
-
- **Read `data.message` before you read a list.** `research keywords` refuses a
|
|
377
|
-
seed over three words and still exits `0`, with `keywords: []` and
|
|
378
|
-
`evidence._tag: "no-provider"`. The reason sits in `data.message`, and the
|
|
379
|
-
repair sits in `data.tip`. A `no-provider` tag can mean a refused argument.
|
|
380
|
-
Never read it as proof that the topic has no demand. `research rankings`,
|
|
381
|
-
`research domain-traffic`, `research domain-availability`, `page issues`,
|
|
382
|
-
`search cohorts`, and `vitals findings` carry the same `data.message`. The
|
|
383
|
-
full list is in references/protocol.md.
|
|
384
|
-
- **Report the result as the CLI gave it.** Report failures as they are; there
|
|
385
|
-
is no MCP or private-route fallback. Never treat an empty result as clean:
|
|
386
|
-
`page inspect` names the kind of empty through `observations.coverage`,
|
|
387
|
-
`page issues` through `availableKeys`, `search cohorts` through
|
|
388
|
-
`analysed: false`, and `vitals summary` through `available: false`. A refusal
|
|
389
|
-
reads differently again: `status`, `page issues` and `search cohorts` exit
|
|
390
|
-
`4` or `5` on an archived or paused Site, which is never an empty Site. Other
|
|
391
|
-
commands may still return Provisional evidence. Quote the counts a result
|
|
392
|
-
ships with, never the length of its list: `data.total` from `page issues`,
|
|
393
|
-
then `coverage.accessibilityChecksTotal` and
|
|
394
|
-
`coverage.bestPracticesChecksTotal` from `scans show`. Neither command
|
|
395
|
-
reports the dropped row count, so `possiblyTruncated: true` marks the list as
|
|
396
|
-
a floor.
|
|
265
|
+
- Start the comment with an agent disclosure. Give reproduction steps, the
|
|
266
|
+
expected and actual behaviour, and any workaround.
|
|
267
|
+
- Pass `--request-id` when the response had one. Use `--intent improvement`
|
|
268
|
+
for a suggestion; the default is `bug`.
|
|
269
|
+
- Send sanitized details only: no tokens, cookies, personal data, private URLs,
|
|
270
|
+
customer content, raw logs, or full response bodies.
|
|
271
|
+
- The limit is ten reports per credential per hour. If a report fails, say so
|
|
272
|
+
and continue. Never retry it or report the failure.
|
|
@@ -10,6 +10,11 @@ Pass `--json` and `--site <site-id>` on every command below, except `whoami`,
|
|
|
10
10
|
`Mutation` marks a command that changes server state. A mutation needs `--yes`
|
|
11
11
|
in a non-interactive shell.
|
|
12
12
|
|
|
13
|
+
Global options work on every command: `--site`, `--json`, `--yes` (`-y`),
|
|
14
|
+
`--timeout-ms` (1 to 300000, default 30000), `--api-url` for a non-production
|
|
15
|
+
host, and `--no-input` to disable prompts in a TTY. `--json` already implies
|
|
16
|
+
`--no-input`.
|
|
17
|
+
|
|
13
18
|
`Needs a Site URL` marks a command that reads the Site's own domain. If the Site
|
|
14
19
|
has no URL, the command exits `2`. Set the Site URL first; retrying will not
|
|
15
20
|
help.
|
|
@@ -17,7 +22,7 @@ help.
|
|
|
17
22
|
`--all` repeats a paged read until every page is written, one envelope per line.
|
|
18
23
|
It is available on `actions list`, `page inspect`, `page issues`, `vitals
|
|
19
24
|
findings`, `backlinks recoverable`, `search analytics` row views, `search
|
|
20
|
-
indexing urls
|
|
25
|
+
indexing urls` and `sitemaps urls`.
|
|
21
26
|
|
|
22
27
|
The `search analytics` row views are `pages`, `keywords`, `countries`,
|
|
23
28
|
`devices`, and `analysis`. `--all` on any other view exits `2`.
|
|
@@ -34,7 +39,7 @@ The `search analytics` row views are `pages`, `keywords`, `countries`,
|
|
|
34
39
|
- Web Analytics
|
|
35
40
|
- Research, backlinks, and mentions
|
|
36
41
|
- Crawl audit
|
|
37
|
-
-
|
|
42
|
+
- Timeline and annotations
|
|
38
43
|
- Ask the CLI for its own help
|
|
39
44
|
|
|
40
45
|
## Which command answers which question
|
|
@@ -51,7 +56,7 @@ a research unit.
|
|
|
51
56
|
| What should I fix first | `actions list` |
|
|
52
57
|
| Why does this action exist | `actions show <action-id>` |
|
|
53
58
|
| Which exact URLs does it affect | `page issues`, with `--action-id` |
|
|
54
|
-
| What does
|
|
59
|
+
| What does Nuxt SEO know about one URL | `page inspect <url>` |
|
|
55
60
|
| Did my last deploy break anything | `audit changes` |
|
|
56
61
|
| How much of the Site is indexed | `search indexing summary` |
|
|
57
62
|
| Which route family is Google declining | `search cohorts` |
|
|
@@ -80,7 +85,7 @@ reads with one invocation.
|
|
|
80
85
|
|
|
81
86
|
`pull` excludes every mutation, and every read that needs an argument it cannot
|
|
82
87
|
invent, such as `page inspect`, `page issues`, `search inspect`, `actions show`,
|
|
83
|
-
|
|
88
|
+
and `scans show`. Run those yourself with the IDs `pull`
|
|
84
89
|
returned.
|
|
85
90
|
|
|
86
91
|
`pull` makes one request per read. It never follows pages, so a run costs
|
|
@@ -227,13 +232,10 @@ research boundary in SKILL.md first.
|
|
|
227
232
|
| `audit content-decay` | Stored decaying Pages, with Search Console loss evidence. Takes no flags. If `connected` is `false`, the Site has no Search Console data. Never report that as clean. `truncated: true` marks the list as a floor |
|
|
228
233
|
| `audit duplicates` | Stored duplicate clusters, with members and keep candidate. Takes no flags. `crawlSettingsId: null` means the Site never completed a crawl. Then `total: 0` proves nobody looked. Never report it as a clean Site |
|
|
229
234
|
|
|
230
|
-
##
|
|
235
|
+
## Timeline and annotations
|
|
231
236
|
|
|
232
237
|
| Command | What it returns, and its flags |
|
|
233
238
|
| --- | --- |
|
|
234
|
-
| `content briefs list` | Content Brief summaries. `--status`, `--limit`, `--offset` |
|
|
235
|
-
| `content briefs show <brief-id>` | One Content Brief, including its grounded payload |
|
|
236
|
-
| `content briefs create <keyword>` | Mutation. Creates one Content Brief. `--target-page` is optional |
|
|
237
239
|
| `timeline list` | Stored Timeline Entries. `--kind`, `--feature`, `--severity`, `--since`, `--limit`, `--cursor`, `--include-closed` |
|
|
238
240
|
| `annotations list` | This Site's own chart annotations: date-anchored markers on traffic and ranking charts |
|
|
239
241
|
| `annotations create` | Mutation, so it needs `--yes`. Marks the day you shipped a fix. `--date YYYY-MM-DD` and `--title` are required. `--note` and `--url` are optional |
|
|
@@ -45,8 +45,9 @@ Page row:
|
|
|
45
45
|
nuxtseo search analytics pages --site <site-id> --period 7d --all --json
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
Keep rows where `row.impressions > 0`. Count
|
|
49
|
-
|
|
48
|
+
Keep rows where `row.impressions > 0`. Count distinct `row.gscPage` values,
|
|
49
|
+
not rows. `row.url` is canonicalised, so one URL can span several rows. Report
|
|
50
|
+
distinct `row.url` values when the user asks which URLs appeared. Do not use `data.total` for this count.
|
|
50
51
|
It also includes comparison-period rows with zero current impressions.
|
|
51
52
|
|
|
52
53
|
Keep these reads separate:
|
|
@@ -94,31 +94,44 @@ infer a cursor. Keep server order for actions. Never re-rank merged results.
|
|
|
94
94
|
|
|
95
95
|
## List key and paging shape per command
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
Lists are converging on one envelope: `data.rows` plus `data.page`, where
|
|
98
|
+
`data.page` holds `total`, `limit`, `offset`, and `hasMore`. `page.total` counts
|
|
99
|
+
the whole result set, not this page. Prefer that envelope when the command has
|
|
100
|
+
it. If `data.rows` is absent, the server is older than the CLI; read the domain
|
|
101
|
+
key in the table instead.
|
|
102
|
+
|
|
103
|
+
The domain keys `data.sites`, `data.actions`, `data.clusters`, `data.items` on
|
|
104
|
+
`backlinks recoverable` and `mentions list`, and `data.observations.pagination`
|
|
105
|
+
are deprecated aliases. They are removed in a later minor version. Do not build
|
|
106
|
+
on them.
|
|
107
|
+
|
|
108
|
+
Commands without the envelope have no single list key. Read the key this
|
|
109
|
+
command returns; never guess a chain. Verified against the response schemas.
|
|
99
110
|
|
|
100
111
|
| Command | List key | Paging fields |
|
|
101
112
|
| --- | --- | --- |
|
|
102
|
-
| `sites list` | `data.sites` |
|
|
113
|
+
| `sites list` | `data.rows` (alias `data.sites`) | `data.page`; `meta.total` |
|
|
103
114
|
| `usage` | `data.meters` | none |
|
|
104
|
-
| `actions list` | `data.actions` | `data.page.total`, `.limit`, `.offset`, `.hasMore` |
|
|
115
|
+
| `actions list` | `data.rows` (alias `data.actions`) | `data.page.total`, `.limit`, `.offset`, `.hasMore` |
|
|
105
116
|
| `actions show` | `data.evidenceGroups[].items` | `data.evidenceGroups[].nextCursor` |
|
|
106
|
-
| `page inspect` | `data.observations.rows` | `data.observations.
|
|
117
|
+
| `page inspect` | `data.observations.rows` | `data.observations.page` (alias `data.observations.pagination`); `data.observations.total` |
|
|
107
118
|
| `page issues` | `data.issues` | `data.total`, `data.limit`, `data.offset` |
|
|
108
119
|
| `scans list` | `data.scans` | `data.total`, `data.limit` |
|
|
109
120
|
| `scans pages` | `data.pages` | `data.total` |
|
|
110
121
|
| `vitals findings` | `data.findings` | `data.total`, `data.limit`, `data.offset` |
|
|
111
|
-
| `search analytics`
|
|
122
|
+
| `search analytics` `pages`, `keywords`, `countries`, `devices` | `data.rows` | `data.page`; `data.total`; page with `--page` |
|
|
123
|
+
| `search analytics` `analysis` | `data.rows` | `data.total`; page with `--page` |
|
|
124
|
+
| `search analytics` `page-detail`, `keyword-detail` | `data.rows` | none |
|
|
112
125
|
| `search analytics timeseries` | `data.daily` | none |
|
|
113
126
|
| `search indexing urls` | `data.urls` | `data.total`, `data.limit`, `data.offset`, `data.hasMore` |
|
|
114
127
|
| `search index-history` | `data.changes` | none |
|
|
115
128
|
| `search cohorts` | `data.established` and `data.ranked` | `data.rankedTotal` |
|
|
116
129
|
| `sitemaps list` | `data.sitemaps` | none |
|
|
117
130
|
| `sitemaps urls` | `data.items` | `data.page.nextCursor`, `data.page.limit` |
|
|
118
|
-
| `audit content-decay` | `data.rows` | `data.total`, `data.truncated` |
|
|
119
|
-
| `audit link-opportunities` | `data.rows` | `data.total` |
|
|
120
|
-
| `audit duplicates` | `data.clusters` | `data.total` |
|
|
121
|
-
| `audit link-structure` | `data.templateSlots`, `data.deadEnds`, `data.genericAnchors` |
|
|
131
|
+
| `audit content-decay` | `data.rows` | `data.page`; `data.total`, `data.truncated` |
|
|
132
|
+
| `audit link-opportunities` | `data.rows` | `data.page`; `data.total` |
|
|
133
|
+
| `audit duplicates` | `data.rows` (alias `data.clusters`) | `data.page`; `data.total` |
|
|
134
|
+
| `audit link-structure` | `data.templateSlots`, `data.deadEnds`, `data.genericAnchors` | `data.templateSlotTotal`; `data.deadEndGroupTotal` and `data.deadEndPageTotal`; `data.genericAnchorTotal` |
|
|
122
135
|
| `research keywords` | `data.keywords` | `data.totalFound` |
|
|
123
136
|
| `research serp` | `data.results` | none; `data.depth` |
|
|
124
137
|
| `research rankings` | `data.keywords` | `data.totalFound` |
|
|
@@ -126,9 +139,8 @@ chain. Verified against the response schemas.
|
|
|
126
139
|
| `backlinks referring-domains` | `data.items` | `data.total`, `data.limit` |
|
|
127
140
|
| `backlinks anchors` | `data.items` | `data.total`, `data.limit` |
|
|
128
141
|
| `backlinks history` | `data.items` | `data.total` |
|
|
129
|
-
| `backlinks recoverable` | `data.items` | `data.total`, `data.limit`, `data.offset` |
|
|
130
|
-
| `mentions list` | `data.items` | `data.limit` |
|
|
131
|
-
| `content briefs list` | `data.briefs` | `data.page.total`, `.limit`, `.offset`, `.hasMore` |
|
|
142
|
+
| `backlinks recoverable` | `data.rows` (alias `data.items`) | `data.page`; `data.total`, `data.limit`, `data.offset` |
|
|
143
|
+
| `mentions list` | `data.rows` (alias `data.items`) | `data.page`; `data.total`, `data.limit` |
|
|
132
144
|
| `timeline list` | `data.entries` | `data.nextCursor` |
|
|
133
145
|
| `annotations list` | `data.annotations` | `data.total`, `data.limit` |
|
|
134
146
|
|