@nuxtseo/cli 0.1.3 → 0.2.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 +120 -36
- package/dist/api.js +7 -1
- package/dist/cli.js +62 -11
- package/dist/command-contract.d.ts +2 -1
- package/dist/command-contract.js +14 -2
- package/dist/commands.d.ts +19 -0
- package/dist/commands.js +1263 -89
- package/dist/failures.d.ts +2 -1
- package/dist/failures.js +21 -1
- package/dist/parse.d.ts +9 -0
- package/dist/parse.js +21 -0
- package/dist/render.d.ts +44 -8
- package/dist/render.js +648 -5
- package/dist/skill.d.ts +22 -0
- package/dist/skill.js +56 -0
- package/package.json +9 -8
- package/skills/nuxtseo-cli/SKILL.md +236 -107
- package/skills/nuxtseo-cli/references/commands.md +162 -0
- package/skills/nuxtseo-cli/references/indexing.md +60 -0
- package/skills/nuxtseo-cli/references/protocol.md +17 -5
package/dist/skill.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { CliResult } from './failures.js';
|
|
2
|
+
export declare const SKILL_NAME = "nuxtseo-cli";
|
|
3
|
+
export declare const SKILL_AGENTS: readonly ['claude', 'codex'];
|
|
4
|
+
export type SkillAgent = typeof SKILL_AGENTS[number];
|
|
5
|
+
export interface SkillInstallation {
|
|
6
|
+
agent: SkillAgent;
|
|
7
|
+
source: string;
|
|
8
|
+
destination: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The skill shipped inside this package. A global install puts it outside the
|
|
12
|
+
* project, so the documented `cp -R node_modules/...` path does not exist for
|
|
13
|
+
* the user who most needs it. Resolve it from this module instead.
|
|
14
|
+
*/
|
|
15
|
+
export declare function skillSourceDirectory(moduleUrl?: string): string;
|
|
16
|
+
export declare function skillDestination(homeDirectory: string, agent: SkillAgent): string;
|
|
17
|
+
export declare function installSkill(options: {
|
|
18
|
+
agent: SkillAgent;
|
|
19
|
+
homeDirectory: string;
|
|
20
|
+
target?: string;
|
|
21
|
+
sourceDirectory?: string;
|
|
22
|
+
}): Promise<CliResult<SkillInstallation>>;
|
package/dist/skill.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { cp, mkdir, stat } from 'node:fs/promises';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { dirname, join, relative } from 'pathe';
|
|
4
|
+
import { EXIT_CODE, fail, ok } from './failures.js';
|
|
5
|
+
export const SKILL_NAME = 'nuxtseo-cli';
|
|
6
|
+
/**
|
|
7
|
+
* Directories inside the packaged skill that exist for this repository and not
|
|
8
|
+
* for the person installing it. `npm` keeps them out of the tarball through the
|
|
9
|
+
* `files` negation; this keeps them out of an installed skill directory too,
|
|
10
|
+
* because a negation cannot reach a recursive copy.
|
|
11
|
+
*/
|
|
12
|
+
const SKILL_LOCAL_ONLY = new Set(['evals']);
|
|
13
|
+
export const SKILL_AGENTS = ['claude', 'codex'];
|
|
14
|
+
const AGENT_DIRECTORIES = {
|
|
15
|
+
claude: '.claude',
|
|
16
|
+
codex: '.codex',
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* The skill shipped inside this package. A global install puts it outside the
|
|
20
|
+
* project, so the documented `cp -R node_modules/...` path does not exist for
|
|
21
|
+
* the user who most needs it. Resolve it from this module instead.
|
|
22
|
+
*/
|
|
23
|
+
export function skillSourceDirectory(moduleUrl = import.meta.url) {
|
|
24
|
+
return join(dirname(fileURLToPath(moduleUrl)), '..', 'skills', SKILL_NAME);
|
|
25
|
+
}
|
|
26
|
+
export function skillDestination(homeDirectory, agent) {
|
|
27
|
+
return join(homeDirectory, AGENT_DIRECTORIES[agent], 'skills', SKILL_NAME);
|
|
28
|
+
}
|
|
29
|
+
export async function installSkill(options) {
|
|
30
|
+
const source = options.sourceDirectory ?? skillSourceDirectory();
|
|
31
|
+
const readable = await stat(source).catch(() => {
|
|
32
|
+
// A missing directory and an unreadable one need the same repair, and the
|
|
33
|
+
// next line reports the path either way.
|
|
34
|
+
return null;
|
|
35
|
+
});
|
|
36
|
+
if (!readable?.isDirectory()) {
|
|
37
|
+
return fail(EXIT_CODE.infrastructure, `skill_source_missing: The packaged skill is missing at ${source}.\nReinstall @nuxtseo/cli, then run the command again.`);
|
|
38
|
+
}
|
|
39
|
+
const destination = options.target
|
|
40
|
+
? join(options.target, SKILL_NAME)
|
|
41
|
+
: skillDestination(options.homeDirectory, options.agent);
|
|
42
|
+
const written = await mkdir(dirname(destination), { recursive: true })
|
|
43
|
+
.then(() => cp(source, destination, {
|
|
44
|
+
recursive: true,
|
|
45
|
+
force: true,
|
|
46
|
+
filter: (entry) => {
|
|
47
|
+
const [segment] = relative(source, entry).split('/');
|
|
48
|
+
return !segment || !SKILL_LOCAL_ONLY.has(segment);
|
|
49
|
+
},
|
|
50
|
+
}))
|
|
51
|
+
.then(() => ok(undefined))
|
|
52
|
+
.catch(cause => fail(EXIT_CODE.infrastructure, `skill_install_failed: Could not write the skill to ${destination}.\nCheck the directory permissions, then run the command again.`, cause));
|
|
53
|
+
if (written._tag === 'Err')
|
|
54
|
+
return written;
|
|
55
|
+
return ok({ agent: options.agent, source, destination });
|
|
56
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nuxtseo/cli",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"description": "Command line interface for the NuxtSEO public API.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://nuxtseo.com/pro",
|
|
@@ -25,26 +25,27 @@
|
|
|
25
25
|
},
|
|
26
26
|
"files": [
|
|
27
27
|
"dist",
|
|
28
|
-
"skills"
|
|
28
|
+
"skills",
|
|
29
|
+
"!skills/**/evals"
|
|
29
30
|
],
|
|
30
31
|
"engines": {
|
|
31
32
|
"node": ">=22"
|
|
32
33
|
},
|
|
33
34
|
"dependencies": {
|
|
34
35
|
"@clack/prompts": "^1.7.0",
|
|
36
|
+
"@nuxtseo/sdk": "^0.2.0",
|
|
35
37
|
"citty": "^0.2.2",
|
|
36
|
-
"pathe": "^2.0.3"
|
|
37
|
-
"@nuxtseo/sdk": "^0.1.3"
|
|
38
|
+
"pathe": "^2.0.3"
|
|
38
39
|
},
|
|
39
40
|
"optionalDependencies": {
|
|
40
|
-
"@napi-rs/keyring": "^
|
|
41
|
+
"@napi-rs/keyring": "^2.0.0"
|
|
41
42
|
},
|
|
42
43
|
"devDependencies": {
|
|
43
44
|
"@arethetypeswrong/cli": "^0.18.5",
|
|
44
|
-
"@
|
|
45
|
+
"@nuxtseo/protocol": "0.2.0",
|
|
46
|
+
"@types/node": "^26.4.0",
|
|
45
47
|
"publint": "^0.3.24",
|
|
46
|
-
"typescript": "npm:typescript-native-bridge@6.0.3-bridge.
|
|
47
|
-
"@nuxtseo/protocol": "0.1.3"
|
|
48
|
+
"typescript": "npm:typescript-native-bridge@6.0.3-bridge.15.tsgo.7.0.2"
|
|
48
49
|
},
|
|
49
50
|
"publishConfig": {
|
|
50
51
|
"access": "public"
|
|
@@ -1,75 +1,77 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: nuxtseo-cli
|
|
3
|
-
description:
|
|
3
|
+
description: Drives the `nuxtseo` CLI to triage a Site through the NuxtSEO 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 Briefs, content decay, duplicate clusters, chart annotations, and issue resolution. Use whenever the user mentions NuxtSEO, the `nuxtseo` command, Site SEO work, or NuxtSEO automation.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# NuxtSEO CLI
|
|
7
7
|
|
|
8
8
|
`nuxtseo` reads a Site through the NuxtSEO public API. Every command goes
|
|
9
9
|
through `@nuxtseo/sdk`. The CLI never calls MCP or private routes. Live
|
|
10
|
-
research reaches
|
|
10
|
+
research reaches a provider only through a declared public API operation.
|
|
11
11
|
|
|
12
|
-
Use it to answer "what is wrong with this site and what did I break"
|
|
13
|
-
the code in the
|
|
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.
|
|
14
14
|
|
|
15
|
-
##
|
|
15
|
+
## Reference files
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Read each one before you act on its subject:
|
|
18
18
|
|
|
19
|
-
-
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
`search status` reads stored connection state. It never waits for Google.
|
|
28
|
-
`search analytics` reads Search Console rows through the public API.
|
|
29
|
-
`search indexing` reads retained URL Inspection coverage through the public API.
|
|
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.
|
|
30
26
|
|
|
31
27
|
## Get the binary
|
|
32
28
|
|
|
33
29
|
```sh
|
|
34
|
-
|
|
30
|
+
pnpm dlx @nuxtseo/cli@latest --version # no install
|
|
35
31
|
pnpm add -g @nuxtseo/cli # or npm install --global
|
|
36
32
|
```
|
|
37
33
|
|
|
38
|
-
Node 22 or newer is required.
|
|
39
|
-
|
|
34
|
+
Node 22 or newer is required.
|
|
35
|
+
|
|
36
|
+
Inside the NuxtSEO monorepo the package is not published. Run the built entry
|
|
37
|
+
directly:
|
|
40
38
|
|
|
41
39
|
```sh
|
|
42
40
|
node packages/cli/dist/cli-entry.js --version
|
|
43
41
|
```
|
|
44
42
|
|
|
45
|
-
Exit `127` or `command not found` means the CLI is absent. That is not exit
|
|
46
|
-
no token has been checked yet. Install it, then continue.
|
|
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.
|
|
47
45
|
|
|
48
46
|
## Before the first command
|
|
49
47
|
|
|
50
48
|
```sh
|
|
51
|
-
nuxtseo whoami --json # validates the token
|
|
49
|
+
nuxtseo whoami --json # validates the token and reports its scopes
|
|
52
50
|
```
|
|
53
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
|
+
|
|
54
56
|
Exit `3` means there is no working token. Ask the user to run `nuxtseo login`
|
|
55
|
-
themselves
|
|
56
|
-
token. Do not run `login` for them
|
|
57
|
-
non-interactive shell it falls back to reading a token from stdin
|
|
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.
|
|
58
60
|
|
|
59
61
|
The alternative is a token created at
|
|
60
62
|
<https://nuxtseo.com/pro/dashboard/settings/api-tokens> and exported as
|
|
61
63
|
`NUXTSEO_TOKEN`. Either way the token is bound to a Team role, and that role
|
|
62
64
|
gates what it can do.
|
|
63
65
|
|
|
64
|
-
Never put a token in a command argument, a file you write, or a commit.
|
|
65
|
-
reads a token from a hidden prompt or stdin only.
|
|
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.
|
|
66
68
|
|
|
67
69
|
Resolve the Site once and reuse it. `nuxtseo sites list --json` prints every
|
|
68
70
|
accessible Site with its ID.
|
|
69
71
|
|
|
70
72
|
## How to call it as an agent
|
|
71
73
|
|
|
72
|
-
Always pass `--json` and
|
|
74
|
+
Always pass `--json` and `--site <site-id>`.
|
|
73
75
|
|
|
74
76
|
```sh
|
|
75
77
|
nuxtseo actions list --site site_123 --json
|
|
@@ -79,96 +81,223 @@ nuxtseo actions list --site site_123 --json
|
|
|
79
81
|
also disables prompts. Diagnostics, warnings, spinners, and failure messages go
|
|
80
82
|
to stderr, so stdout stays parseable.
|
|
81
83
|
|
|
82
|
-
`--site` matters for a second reason
|
|
83
|
-
operation and skips the Sites read
|
|
84
|
-
|
|
84
|
+
`--site` matters for a second reason. An explicit Site ID goes straight to the
|
|
85
|
+
operation and skips the Sites read. A token whose role cannot list Sites still
|
|
86
|
+
works. If more than one Site is accessible and `--site` is absent, the CLI
|
|
85
87
|
exits `5` rather than guessing.
|
|
86
88
|
|
|
87
89
|
Other flags worth knowing:
|
|
88
90
|
|
|
89
91
|
| Flag | Use it when |
|
|
90
92
|
| --- | --- |
|
|
91
|
-
| `--yes`, `-y` | The command mutates. Without it, a non-interactive run exits `2
|
|
92
|
-
| `--timeout-ms <ms>` | The default 30000 ms deadline is too short. Maximum is 300000
|
|
93
|
-
| `--api-url <url>` | Testing against a non-production host
|
|
94
|
-
| `--no-input` | Running in a TTY but no prompt is wanted. `--json` already implies this
|
|
93
|
+
| `--yes`, `-y` | The command mutates. Without it, a non-interactive run exits `2` |
|
|
94
|
+
| `--timeout-ms <ms>` | The default 30000 ms deadline is too short. Maximum is 300000 |
|
|
95
|
+
| `--api-url <url>` | Testing against a non-production host |
|
|
96
|
+
| `--no-input` | Running in a TTY but no prompt is wanted. `--json` already implies this |
|
|
95
97
|
|
|
96
98
|
Unknown options exit `2` before any network work, so a typo costs nothing.
|
|
97
99
|
|
|
98
|
-
Parse JSON. Never scrape human output.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
| `research keywords <topic>` | Live keyword ideas | Volume, difficulty, intent, and cost data |
|
|
122
|
-
| `research serp <keyword>` | Live SERP snapshot | Results, features, and fetch time |
|
|
123
|
-
| `research rankings <domain>` | Live domain rankings | Current keywords and domain metrics |
|
|
124
|
-
| `audit link-opportunities` | Stored internal link opportunities | Includes crawl coverage |
|
|
125
|
-
| `audit content-decay` | Stored decaying Pages | Includes Search Console loss evidence |
|
|
126
|
-
| `audit duplicates` | Stored duplicate clusters | Includes members and keep candidate |
|
|
127
|
-
| `content briefs list` | Content Brief summaries | `--status`, `--limit`, `--offset` |
|
|
128
|
-
| `content briefs show <brief-id>` | One Content Brief | Includes its grounded payload |
|
|
129
|
-
| `content briefs create <keyword>` | – | Mutation. `--target-page` is optional |
|
|
130
|
-
| `config` | Local config path, API host, selected Site | Local only |
|
|
131
|
-
|
|
132
|
-
`page inspect` and `page scan` take an absolute URL, for example
|
|
133
|
-
`https://example.com/about`.
|
|
134
|
-
|
|
135
|
-
`--help --json` returns a `CliHelp` value for one command level: its
|
|
136
|
-
`arguments`, the inherited `globalOptions`, and its `subcommands` as names and
|
|
137
|
-
descriptions only. To learn a subcommand's own flags, ask it directly, for
|
|
138
|
-
example `nuxtseo actions list --help --json`. Use this instead of guessing a
|
|
139
|
-
flag, and prefer the table above for anything it already answers.
|
|
100
|
+
Parse JSON. Never scrape human output.
|
|
101
|
+
|
|
102
|
+
Every `--json` run prints one envelope. For example, `status` returns this,
|
|
103
|
+
abridged:
|
|
104
|
+
|
|
105
|
+
```sh
|
|
106
|
+
nuxtseo status --site site_01JXYZ --json
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"data": {
|
|
112
|
+
"available": true,
|
|
113
|
+
"dataQuality": { "status": "complete", "reason": null },
|
|
114
|
+
"nextAction": { "kind": "action", "actionId": "act_01JXYZ" }
|
|
115
|
+
},
|
|
116
|
+
"meta": { "requestId": "req_01JXYZ", "version": "1.0" }
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Read `data.available` first; `false` means the first assessment has not run.
|
|
121
|
+
Read `data.dataQuality.status` before you quote the verdict. Pass
|
|
122
|
+
`data.nextAction.actionId` into `actions show`.
|
|
140
123
|
|
|
141
124
|
## The triage loop
|
|
142
125
|
|
|
143
|
-
This
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
126
|
+
This sequence turns CLI output into a code change. Copy the checklist and track
|
|
127
|
+
your progress:
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
Triage progress:
|
|
131
|
+
- [ ] 1. status: read the verdict and the Next Action
|
|
132
|
+
- [ ] 2. actions list: pick a ranked action, check its freshness
|
|
133
|
+
- [ ] 3. actions show: read the evidence behind it
|
|
134
|
+
- [ ] 4. Fix the cause in the repository
|
|
135
|
+
- [ ] 5. page scan: re-scan a page you changed
|
|
136
|
+
- [ ] 6. actions resolve: claim the action
|
|
137
|
+
- [ ] 7. annotations create: mark the day the fix shipped
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**Step 1. Read the Site verdict.**
|
|
141
|
+
|
|
142
|
+
```sh
|
|
143
|
+
nuxtseo status --site <site-id> --json
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
This is the cheapest orientation: the verdict, the single ranked Next Action,
|
|
147
|
+
and what changed recently. Read `dataQuality.status` before quoting it.
|
|
148
|
+
`degraded` means the assessment ran with proofs missing, so the verdict is real
|
|
149
|
+
but Provisional. `unavailable` means it has not run. If `nextAction.kind` is
|
|
150
|
+
`action`, its `actionId` goes straight into step 3.
|
|
151
|
+
|
|
152
|
+
**Step 2. Pick a ranked action.**
|
|
153
|
+
|
|
154
|
+
```sh
|
|
155
|
+
nuxtseo actions list --site <site-id> --json
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Each row carries an ID, a diagnosis, an effort, and an affected page count.
|
|
159
|
+
Check `evidence.freshness` per row. If `verdict` is `aged` with a large
|
|
160
|
+
`ageHours`, live-check a cheap sample before fixing. The site may have moved on
|
|
161
|
+
since the observation.
|
|
162
|
+
|
|
163
|
+
**Step 3. Read the evidence.**
|
|
164
|
+
|
|
165
|
+
```sh
|
|
166
|
+
nuxtseo actions show <action-id> --site <site-id> --json
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
This returns which pages, which finding type, and when it was observed.
|
|
170
|
+
|
|
171
|
+
Two reads deepen this step when the action's own evidence is not enough:
|
|
172
|
+
|
|
173
|
+
- `nuxtseo page issues --action-id <action-id> --site <site-id> --json` returns
|
|
174
|
+
the raw observations behind that action: the exact URLs, status codes,
|
|
175
|
+
redirect targets, and for a Lighthouse row the failing selectors and DOM
|
|
176
|
+
snippets. These are the mutable observation store, never canonical action
|
|
177
|
+
membership.
|
|
178
|
+
- `nuxtseo scans show <scan-id> --site <site-id> --json` returns the failing
|
|
179
|
+
checks behind a Lighthouse score, so you can locate the cause without
|
|
180
|
+
re-scanning.
|
|
181
|
+
|
|
182
|
+
**Step 4. Fix the cause in the repository.**
|
|
183
|
+
|
|
184
|
+
The evidence names URLs. Map them back to routes, components, or config.
|
|
185
|
+
|
|
186
|
+
**Step 5. Re-scan a page you changed.**
|
|
187
|
+
|
|
188
|
+
Deploy or preview the fix first. Then ask for fresh Scans:
|
|
189
|
+
|
|
190
|
+
```sh
|
|
191
|
+
nuxtseo page scan <url> --site <site-id> --yes --json
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
**Step 6. Claim the action.**
|
|
195
|
+
|
|
196
|
+
```sh
|
|
197
|
+
nuxtseo actions resolve <action-id> --site <site-id> --yes --json
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
The server verifies it. The CLI never marks anything fixed by itself. Resolve
|
|
201
|
+
reads the action first and sends the current `artifactVersion` for you. Do not
|
|
202
|
+
construct that field by hand. If the evidence moved under you, the command
|
|
203
|
+
exits `5` with `stale_evidence`. Re-run step 3 and decide again.
|
|
204
|
+
|
|
205
|
+
When the fix is a deliberate removal rather than a repair, dismiss instead of
|
|
206
|
+
resolving:
|
|
207
|
+
|
|
208
|
+
```sh
|
|
209
|
+
nuxtseo actions show <action-id> --site <site-id> --json # read artifactVersion
|
|
210
|
+
nuxtseo actions dismiss <action-id> --site <site-id> --artifact-version <v> --yes --json
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Dismiss is admitted only for a broken-page action with complete 404 or 410
|
|
214
|
+
evidence and no internal referrers. Anything else is rejected, so it cannot be
|
|
215
|
+
used to hide work.
|
|
216
|
+
|
|
217
|
+
**Step 7. Mark the day the fix shipped.**
|
|
218
|
+
|
|
219
|
+
```sh
|
|
220
|
+
nuxtseo annotations create --site <site-id> --date "$(date -u +%F)" --title "Fixed canonicals on /docs" --yes --json
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
The next traffic move then has a cause beside it.
|
|
224
|
+
|
|
225
|
+
## Datasets that must not be conflated
|
|
226
|
+
|
|
227
|
+
Two datasets share the word "vitals":
|
|
228
|
+
|
|
229
|
+
- `performance` and `scans *` read Lighthouse **lab** Scans.
|
|
230
|
+
- `vitals summary`, `vitals trend`, and `vitals findings` read **field**
|
|
231
|
+
(real-user) data. `vitals` reads CrUX. `vitals findings` reads the Site's own
|
|
232
|
+
Web Analytics provider.
|
|
233
|
+
|
|
234
|
+
A lab score and a field p75 disagreeing is normal, not a fault.
|
|
235
|
+
|
|
236
|
+
Two commands inspect the same URL and answer different questions. `page inspect`
|
|
237
|
+
reads the NuxtSEO observation store. `search inspect` reads Google's index
|
|
238
|
+
verdict.
|
|
239
|
+
|
|
240
|
+
`status`, `performance`, and `vitals` answer different questions. `status` is
|
|
241
|
+
the verdict. `performance` is the lab score. `vitals` is what real users
|
|
242
|
+
measured. Never substitute one for another.
|
|
243
|
+
|
|
244
|
+
The Search Console reads also split:
|
|
245
|
+
|
|
246
|
+
- `search status` reads stored connection state. It never waits for Google.
|
|
247
|
+
- `search analytics`, `search indexing`, `search index-history`, and
|
|
248
|
+
`search inspect` read retained Search Console evidence through the public API.
|
|
249
|
+
- `sitemaps submit` and `sitemaps delete` reach Google through the Site's
|
|
250
|
+
stored Search Console credential. Both are mutations.
|
|
251
|
+
|
|
252
|
+
## Route-family indexing: two lists, one claim
|
|
253
|
+
|
|
254
|
+
`search cohorts` answers "which template is Google declining" in one read,
|
|
255
|
+
instead of N page-level findings. Read it before you list individual
|
|
256
|
+
not-indexed URLs. Its output holds two lists that mean different things. Never
|
|
257
|
+
merge them.
|
|
258
|
+
|
|
259
|
+
- `established` holds only families whose Wilson score interval clears the
|
|
260
|
+
not-indexed rate of their **own complement** after a Bonferroni correction.
|
|
261
|
+
These are the only rows you may report as a finding or a cause.
|
|
262
|
+
- `ranked` orders every family by raw rate, with **no significance test**. It
|
|
263
|
+
always answers "where is indexing worst", even when nothing is provable. A
|
|
264
|
+
row with `statisticallyEstablished: false` is a lead to verify, never a
|
|
265
|
+
conclusion. Presenting one as established is a hard failure.
|
|
266
|
+
|
|
267
|
+
`analysed: false` means there is no completed crawl, or no Google index state
|
|
268
|
+
to join against. That is a coverage gap, not healthy indexing. Say which.
|
|
163
269
|
|
|
164
270
|
## Guardrails
|
|
165
271
|
|
|
166
|
-
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
272
|
+
- **Get consent before a mutation.** `actions resolve`, `actions dismiss`,
|
|
273
|
+
`page scan`, `sitemaps submit`, `sitemaps delete`, `content briefs create`,
|
|
274
|
+
and the `annotations` writes all change server state. `page scan` also spends
|
|
275
|
+
against the Lighthouse limit. `--yes` is consent you borrow from the user, so
|
|
276
|
+
ask first, unless the user already asked for that exact action. `actions
|
|
277
|
+
dismiss` says the page stays removed, so use it only when that is the
|
|
278
|
+
decision.
|
|
279
|
+
- **Live research spends against the Team research limit.** `research
|
|
280
|
+
keywords`, `research serp`, `research rankings`, `research domain-traffic`,
|
|
281
|
+
`research domain-availability`, and the `backlinks` reads other than
|
|
282
|
+
`backlinks recoverable` can all start it. Each one reports what it did.
|
|
283
|
+
Keyword, domain, and backlink JSON report cache use in `evidence`; a `cache`
|
|
284
|
+
or `no-provider` tag means nothing was spent. SERP and ranking JSON report
|
|
285
|
+
`cached`. `backlinks recoverable` and `mentions list` read retained rows and
|
|
286
|
+
spend nothing.
|
|
287
|
+
- **Never loop unattended.** Check `usage` before any run over Pages, keywords,
|
|
288
|
+
or domains. Use `--all` rather than your own offset loop. It writes one
|
|
289
|
+
envelope per line and stops at 50 requests with exit `9`. Treat exit `9` as a
|
|
290
|
+
stop, then resume with the argument stderr names.
|
|
291
|
+
- **Report the result as the CLI gave it.** Report failures as they are; there
|
|
292
|
+
is no MCP or private-route fallback. Never treat an empty result as clean:
|
|
293
|
+
`page inspect` names the kind of empty through `observations.coverage`,
|
|
294
|
+
`page issues` through `availableKeys`, `search cohorts` through
|
|
295
|
+
`analysed: false`, and `vitals summary` through `available: false`. A refusal
|
|
296
|
+
reads differently again: `status`, `page issues` and `search cohorts` exit
|
|
297
|
+
`4` or `5` on an archived or paused Site, which is never an empty Site. Other
|
|
298
|
+
commands may still return Provisional evidence. Quote the counts a result
|
|
299
|
+
ships with, never the length of its list: `data.total` from `page issues`,
|
|
300
|
+
then `coverage.accessibilityChecksTotal` and
|
|
301
|
+
`coverage.bestPracticesChecksTotal` from `scans show`. Neither command
|
|
302
|
+
reports the dropped row count, so `possiblyTruncated: true` marks the list as
|
|
303
|
+
a floor.
|