diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index ae9024c3..ccd3f40d 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -14,7 +14,7 @@ "name": "tinyfish", "source": "./plugins/tinyfish", "description": "The complete web toolkit for your agent. Search the web, fetch clean content from URLs, automate browsers with natural language, and spin up headless browsers for full programmatic control.", - "version": "1.2.3", + "version": "1.2.4", "author": { "name": "TinyFish", "url": "https://tinyfish.ai" diff --git a/plugins/tinyfish/.claude-plugin/plugin.json b/plugins/tinyfish/.claude-plugin/plugin.json index 0676df72..833e805e 100644 --- a/plugins/tinyfish/.claude-plugin/plugin.json +++ b/plugins/tinyfish/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "tinyfish", - "version": "1.2.3", + "version": "1.2.4", "description": "The complete web toolkit for your agent. Search the web and get answers in milliseconds. Fetch any URL and get clean markdown content back. Send a browser agent to navigate sites, fill forms, and extract structured data. Spin up a headless browser for full programmatic control when you need it.", "author": { "name": "TinyFish", diff --git a/plugins/tinyfish/CHANGELOG.md b/plugins/tinyfish/CHANGELOG.md index 96249bb1..934fba9f 100644 --- a/plugins/tinyfish/CHANGELOG.md +++ b/plugins/tinyfish/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## 1.2.4 (2026-08-18) + +### Fixed +- Skill: `/tinyfish:doctor` describes `schema_version` `3` (CLI 0.22+). It still said `1` and `2` and bailed above `2`, so with `@latest` pinned it degraded every run to `--pretty`. Adds the two verdicts (`ok_harnesses`, `ok_cli`), `checks[].scope`, `repairs[].action`, and the exit code coming from the harness scope alone — `ok_cli: false` beside exit `0` is a real state. Drops the `registered, API key present but unverified` string the CLI no longer emits, and runs the `--fix` examples through `npx` like step 1. +- Skill: `/tinyfish:feedback` quotes the title as well as the body: `--title "$title" --body-file "$body_file"`. The summary is derived from the user's free-form text too, so backticks or `$(…)` in it were evaluated by the filing shell. Also prohibits `eval` and constructed command strings. +- Plugin README lists `~/.grok` among the config directories doctor reads; the CLI added a grok harness. +- Skill: `/tinyfish:doctor` scopes the Cursor `unattended_safe` caveat to `2` and `3`; the CLI still gates `connect cursor` on its own authenticated call. +- Skill: `/tinyfish:doctor` reads reach off `proves_harness_reach` rather than off `auth_mode`. A harness whose own client reports a live connection is wire evidence — Claude Code on OAuth included — and the skill was telling the agent to report it as unverified presence. +- Skill: `/tinyfish:doctor` says to prefix `repairs[].command`; the CLI emits bare `tinyfish …`, which is not on PATH on the npx-only installs the skill is written for. +- Skill: `/tinyfish:feedback` writes the title to a file like the body. A `title=…` assignment evaluates backticks and `$(…)` exactly as the command line does, so quoting the expansion alone moved the injection upstream rather than closing it. +- Plugin README: `doctor` reports every run to TinyFish, not only when a repair runs, and diagnosis makes authenticated calls rather than only reading config. + ## 1.2.3 (2026-08-17) ### Changed diff --git a/plugins/tinyfish/README.md b/plugins/tinyfish/README.md index 4284f357..a2aeb876 100644 --- a/plugins/tinyfish/README.md +++ b/plugins/tinyfish/README.md @@ -29,5 +29,5 @@ TinyFish's privacy policy: https://www.tinyfish.ai/privacy-policy The two setup skills do touch your machine: -- **`/tinyfish:doctor`** runs the TinyFish CLI, which reads your agent config directories (`~/.claude`, `~/.codex`, `~/.cursor`, `~/.hermes`, `~/.openclaw`, `~/.config/opencode`) and the CLI credential store (`~/.tinyfish/config.json`) to find where TinyFish is registered. Diagnosis only reads; the repair step rewrites those registrations, and only commands the CLI itself proposes. `doctor` sends no analytics, though a repair re-runs `tinyfish connect`, which does — set `TINYFISH_NO_TELEMETRY` to suppress it. +- **`/tinyfish:doctor`** runs the TinyFish CLI, which reads your agent config directories (`~/.claude`, `~/.codex`, `~/.cursor`, `~/.grok`, `~/.hermes`, `~/.openclaw`, `~/.config/opencode`) and the CLI credential store (`~/.tinyfish/config.json`) to find where TinyFish is registered. Diagnosis reads those files and makes authenticated calls to TinyFish to test what it found; the repair step rewrites the registrations, and only commands the CLI itself proposes. `doctor` reports its own run to TinyFish — set `TINYFISH_NO_TELEMETRY` to suppress that and the `tinyfish connect` a repair re-runs. - **`/tinyfish:feedback`** shells out to `gh issue create` against a public repo, after showing you the exact issue text and waiting for your approval. diff --git a/plugins/tinyfish/skills/doctor/SKILL.md b/plugins/tinyfish/skills/doctor/SKILL.md index 0ba6b8a7..b469c3d3 100644 --- a/plugins/tinyfish/skills/doctor/SKILL.md +++ b/plugins/tinyfish/skills/doctor/SKILL.md @@ -5,7 +5,7 @@ description: Diagnose and repair your TinyFish setup — MCP registration, auth, # TinyFish Doctor -`tinyfish doctor` (CLI 0.18+) owns the diagnosis. Your job is to run it, do the one +`tinyfish doctor` (CLI 0.22+) owns the diagnosis. Your job is to run it, do the one check it structurally cannot do, and act on what comes back. Never hand-edit config files — every repair goes through the CLI, which carries backup and merge rigor. @@ -17,31 +17,36 @@ the command from step 1 to run themselves. ## 1. Run doctor -``` +```sh npx -y @tiny-fish/cli@latest doctor --harness claude-code ``` -JSON on stdout: `schema_version`, `checks[]`, `harnesses[]`, `repairs[]`. +JSON on stdout: `schema_version`, `cli_version`, `ok_harnesses`, `ok_cli`, `checks[]`, +`harnesses[]`, `repairs[]`. -Read `schema_version` before the fields. This skill describes `1` and `2`. The command pins -`@latest`, so a newer CLI can hand you a shape you do not know: above `2`, stop reading -fields, show the user `--pretty` output instead, and rely on step 2 for the verdict. +Read `schema_version` before the fields. This skill describes `3` (CLI 0.22+). The command +pins `@latest`, so a newer CLI can hand you a shape you do not know: above `3`, stop reading +fields, show the user `--pretty` output instead, and rely on step 2 for the verdict. Below +`3` a single `ok` replaces the two verdicts and `checks[]` carry no `scope`, so read only +`checks[]` and the exit code there. -The difference between the two is what a keyed registration proves. `2` tests an API-key -registration on the wire, so a stale key header fails outright and earns a `connect` repair. -`1` passes it on config presence alone, and step 2 is the only thing that catches it. +**Two verdicts, not one.** `ok_harnesses` answers whether the user's agents can reach +TinyFish; `ok_cli` answers whether the CLI's own credential works. `checks[].scope` +(`harness`, `cli`, `info`) says which one a check counts toward, and only the harness scope +moves the exit code — `ok_cli: false` beside exit `0` is a real state, not a contradiction. +Report it as the CLI's own credential, not as a broken harness. | Exit | Meaning | |---|---| -| `0` | every check passed | -| `1` | a check failed — read `checks[]` | +| `0` | no harness check failed — `ok_cli` can still be `false` | +| `1` | a harness check failed — read `checks[]` | | `2` | doctor could not run; **stdout is empty**, the reason is on stderr | A `warn` is not a failure and does not move the exit code: doctor is saying it could not -check something, not that it is broken. On `2`, `registered, API key present but unverified` -means the key exists but doctor cannot read its value to test it, which is every Codex -install and any harness whose config redacts the header. Never repair on a warn, prove it -in step 2. +check something, not that it is broken. A registration warn whose detail says the key was +not readable, or was not verified, means the key exists but doctor could not test its value +— every Codex install, and any harness whose config redacts the header. Never repair on a +warn, prove it in step 2. `--pretty` only when showing a human the list. Never put `--debug` output in a report — it is the one channel carrying raw stacks and absolute paths. @@ -49,8 +54,9 @@ it is the one channel carrying raw stacks and absolute paths. ## 2. Prove the harness reach — the part doctor cannot do `harnesses[].proves_harness_reach` is `false` whenever doctor could not prove that *this* -harness authenticates. For OAuth harnesses it is always false, because the CLI cannot borrow -the harness's token. You are the only one who can close that gap. +harness authenticates. It is `true` only where the harness's own client reports a live +connection, or where a key doctor could read verified on the wire — the CLI cannot borrow an +OAuth token, so every harness that reports no connection state leaves the gap to you. `--harness claude-code` narrows `harnesses[]` to exactly one entry, so there is no ambiguity about which harness it describes. @@ -68,25 +74,28 @@ namespace names it. | Auth error, but doctor says `registered: yes` | Registration exists; the credential behind it is broken | | TinyFish tools absent entirely | Server not loaded in this session — the user must restart the agent | -What a `registration: pass` proves depends on `schema_version`. On `2` an API-key +What a `registration: pass` proves depends on `schema_version`. On `2` and `3` an API-key registration was tested on the wire, so a stale key header is already a `fail` with a -`connect` repair beside it. On `1`, and on every OAuth or `auth_mode: unknown` registration -at either version, pass is presence only: doctor read config, not the wire, and a stale key -still passes while every call 401s. Neither version says anything about siblings, and a +`connect` repair beside it. A pass carrying `proves_harness_reach: true` is the harness's own +client reporting a live connection — wire evidence at any version, whatever `auth_mode` says. +Every other pass is presence only: doctor read config, not the wire, and a stale key still +passes while every call 401s. No version says anything about siblings, and a healthy sibling will answer cheerfully while the broken one stays broken. ## 3. Repair -Run only commands that appear in `repairs[]`, and show `command` before running it. Keep -the order they arrive in: `auth login` comes before `connect` because `connect` writes -whichever key is stored, so a dead one has to be replaced first. +Run only commands that appear in `repairs[]`, and show `command` before running it. They +arrive as bare `tinyfish …`, which is not on PATH under `npx` — swap that leading word for +`npx -y @tiny-fish/cli@latest` when there is no global install. Keep +the order they arrive in: `action: auth-login` comes before `action: connect` because +`connect` writes whichever key is stored, so a dead one has to be replaced first. -- Terminal with the user present → `doctor --fix --harness claude-code` -- Non-interactive → `doctor --fix --yes`; only `unattended_safe: true` repairs run and the +- Terminal with the user present → `npx -y @tiny-fish/cli@latest doctor --fix --harness claude-code` +- Non-interactive → `npx -y @tiny-fish/cli@latest doctor --fix --yes`; only `unattended_safe: true` repairs run and the rest return as skipped. Never report a skipped repair as a fix. - `unattended_safe: false` → hand it to the user, do not run it. Expect most repairs to be false: `auth login` always is, and `connect ` is unsafe for every harness except - Cursor — and on `2` Cursor only while the CLI's own authenticated call passes, since a + Cursor — and on `2` and `3` Cursor only while the CLI's own authenticated call passes, since a revoked key still resolves as a credential. Read the field, do not infer it. - OAuth credential failures have no CLI repair: tell the user to run `/mcp`, pick tinyfish, and sign in. diff --git a/plugins/tinyfish/skills/feedback/SKILL.md b/plugins/tinyfish/skills/feedback/SKILL.md index aa441cd2..6cbe607d 100644 --- a/plugins/tinyfish/skills/feedback/SKILL.md +++ b/plugins/tinyfish/skills/feedback/SKILL.md @@ -20,7 +20,7 @@ doctor produced no JSON — say so instead of attaching an empty report. ## Structure -``` +```markdown ### What I was doing … ### What happened @@ -40,12 +40,14 @@ filing. Any edit they request happens before filing. ## File -- Preferred: write the body to a file, then - `gh issue create --repo tinyfish-io/tinyfish-cookbook --title "" --body-file ` (only if `gh` is installed and - authenticated). Never interpolate the body into the command — it carries - the user's free-form text and doctor's JSON, and backticks or `$(…)` in - either would be evaluated by your shell. +- Preferred: write the body and the title to files, then `gh issue create + --repo tinyfish-io/tinyfish-cookbook --title "$(cat "$title_file")" + --body-file "$body_file"` (only if `gh` is installed and authenticated). + Both values come from the user's free-form text (the body also carries + doctor's JSON), so neither may reach the shell as literal text — including + in a `title=…` assignment, where backticks or `$(…)` are evaluated just the + same. `cat`'s output is not re-parsed. Never build the command as a string + or run it through `eval`. - Fallback: open a prefilled issue URL (`https://github.com/tinyfish-io/tinyfish-cookbook/issues/new?title=…&body=…`). Percent-encode both values — an unencoded `#` truncates the body and `&` diff --git a/scripts/generate-harness-skills.mjs b/scripts/generate-harness-skills.mjs index e59244eb..5411bf06 100644 --- a/scripts/generate-harness-skills.mjs +++ b/scripts/generate-harness-skills.mjs @@ -17,6 +17,7 @@ const GENERIC_REAUTH = `re-authenticate in the harness itself. | Codex, Hermes | no login command — auth runs on first tool use; trigger a TinyFish tool and finish the browser sign-in | | OpenCode | \`opencode mcp auth tinyfish\` | | Claude Code | \`/mcp\` in-app, or \`claude mcp login tinyfish\` | + | Grok | no login command — in Grok Build, \`/mcps\`, select \`tinyfish\`, press \`i\`. A keyed registration instead reads \`TINYFISH_API_KEY\` from Grok's environment and has no OAuth fallback: re-run \`tinyfish connect grok --api-key \` and start Grok from a new terminal | | OpenClaw, Cursor | key-based — \`tinyfish auth login\`, then \`tinyfish connect \` to rewrite the header |`; const TARGETS = { @@ -34,7 +35,7 @@ const TARGETS = { vars: { HARNESS_FLAG: "", HARNESS_ENTRY: - "Run without `--harness`, so `harnesses[]` carries one entry per harness doctor knows — installed or not. Read the entry whose `harness` matches the agent you are running in, never the first one, and check its `detected` first: an absent harness reports `detected: false`, `registered: \"no\"`, `auth_mode: \"unknown\"`, which is not a fault to repair. doctor only knows `claude-code`, `codex`, `cursor`, `hermes`, `openclaw`, `opencode`; if you are none of those, no entry describes you and step 2 is your only evidence.", + "Run without `--harness`, so `harnesses[]` carries one entry per harness doctor knows — installed or not. Read the entry whose `harness` matches the agent you are running in, never the first one, and check its `detected` first: an absent harness reports `detected: false`, `registered: \"no\"`, `auth_mode: \"unknown\"`, which is not a fault to repair. doctor only knows `claude-code`, `codex`, `cursor`, `grok`, `hermes`, `openclaw`, `opencode`; if you are none of those, no entry describes you and step 2 is your only evidence.", REAUTH: GENERIC_REAUTH, FEEDBACK: "file it at https://github.com/tinyfish-io/tinyfish-cookbook/issues", }, diff --git a/skills-src/doctor.md b/skills-src/doctor.md index f43c4809..e67efeea 100644 --- a/skills-src/doctor.md +++ b/skills-src/doctor.md @@ -5,7 +5,7 @@ description: Diagnose and repair your TinyFish setup — MCP registration, auth, # TinyFish Doctor -`tinyfish doctor` (CLI 0.18+) owns the diagnosis. Your job is to run it, do the one +`tinyfish doctor` (CLI 0.22+) owns the diagnosis. Your job is to run it, do the one check it structurally cannot do, and act on what comes back. Never hand-edit config files — every repair goes through the CLI, which carries backup and merge rigor. @@ -17,31 +17,36 @@ the command from step 1 to run themselves. ## 1. Run doctor -``` +```sh npx -y @tiny-fish/cli@latest doctor{{HARNESS_FLAG}} ``` -JSON on stdout: `schema_version`, `checks[]`, `harnesses[]`, `repairs[]`. +JSON on stdout: `schema_version`, `cli_version`, `ok_harnesses`, `ok_cli`, `checks[]`, +`harnesses[]`, `repairs[]`. -Read `schema_version` before the fields. This skill describes `1` and `2`. The command pins -`@latest`, so a newer CLI can hand you a shape you do not know: above `2`, stop reading -fields, show the user `--pretty` output instead, and rely on step 2 for the verdict. +Read `schema_version` before the fields. This skill describes `3` (CLI 0.22+). The command +pins `@latest`, so a newer CLI can hand you a shape you do not know: above `3`, stop reading +fields, show the user `--pretty` output instead, and rely on step 2 for the verdict. Below +`3` a single `ok` replaces the two verdicts and `checks[]` carry no `scope`, so read only +`checks[]` and the exit code there. -The difference between the two is what a keyed registration proves. `2` tests an API-key -registration on the wire, so a stale key header fails outright and earns a `connect` repair. -`1` passes it on config presence alone, and step 2 is the only thing that catches it. +**Two verdicts, not one.** `ok_harnesses` answers whether the user's agents can reach +TinyFish; `ok_cli` answers whether the CLI's own credential works. `checks[].scope` +(`harness`, `cli`, `info`) says which one a check counts toward, and only the harness scope +moves the exit code — `ok_cli: false` beside exit `0` is a real state, not a contradiction. +Report it as the CLI's own credential, not as a broken harness. | Exit | Meaning | |---|---| -| `0` | every check passed | -| `1` | a check failed — read `checks[]` | +| `0` | no harness check failed — `ok_cli` can still be `false` | +| `1` | a harness check failed — read `checks[]` | | `2` | doctor could not run; **stdout is empty**, the reason is on stderr | A `warn` is not a failure and does not move the exit code: doctor is saying it could not -check something, not that it is broken. On `2`, `registered, API key present but unverified` -means the key exists but doctor cannot read its value to test it, which is every Codex -install and any harness whose config redacts the header. Never repair on a warn, prove it -in step 2. +check something, not that it is broken. A registration warn whose detail says the key was +not readable, or was not verified, means the key exists but doctor could not test its value +— every Codex install, and any harness whose config redacts the header. Never repair on a +warn, prove it in step 2. `--pretty` only when showing a human the list. Never put `--debug` output in a report — it is the one channel carrying raw stacks and absolute paths. @@ -49,8 +54,9 @@ it is the one channel carrying raw stacks and absolute paths. ## 2. Prove the harness reach — the part doctor cannot do `harnesses[].proves_harness_reach` is `false` whenever doctor could not prove that *this* -harness authenticates. For OAuth harnesses it is always false, because the CLI cannot borrow -the harness's token. You are the only one who can close that gap. +harness authenticates. It is `true` only where the harness's own client reports a live +connection, or where a key doctor could read verified on the wire — the CLI cannot borrow an +OAuth token, so every harness that reports no connection state leaves the gap to you. {{HARNESS_ENTRY}} @@ -68,25 +74,28 @@ namespace names it. | Auth error, but doctor says `registered: yes` | Registration exists; the credential behind it is broken | | TinyFish tools absent entirely | Server not loaded in this session — the user must restart the agent | -What a `registration: pass` proves depends on `schema_version`. On `2` an API-key +What a `registration: pass` proves depends on `schema_version`. On `2` and `3` an API-key registration was tested on the wire, so a stale key header is already a `fail` with a -`connect` repair beside it. On `1`, and on every OAuth or `auth_mode: unknown` registration -at either version, pass is presence only: doctor read config, not the wire, and a stale key -still passes while every call 401s. Neither version says anything about siblings, and a +`connect` repair beside it. A pass carrying `proves_harness_reach: true` is the harness's own +client reporting a live connection — wire evidence at any version, whatever `auth_mode` says. +Every other pass is presence only: doctor read config, not the wire, and a stale key still +passes while every call 401s. No version says anything about siblings, and a healthy sibling will answer cheerfully while the broken one stays broken. ## 3. Repair -Run only commands that appear in `repairs[]`, and show `command` before running it. Keep -the order they arrive in: `auth login` comes before `connect` because `connect` writes -whichever key is stored, so a dead one has to be replaced first. +Run only commands that appear in `repairs[]`, and show `command` before running it. They +arrive as bare `tinyfish …`, which is not on PATH under `npx` — swap that leading word for +`npx -y @tiny-fish/cli@latest` when there is no global install. Keep +the order they arrive in: `action: auth-login` comes before `action: connect` because +`connect` writes whichever key is stored, so a dead one has to be replaced first. -- Terminal with the user present → `doctor --fix{{HARNESS_FLAG}}` -- Non-interactive → `doctor --fix --yes`; only `unattended_safe: true` repairs run and the +- Terminal with the user present → `npx -y @tiny-fish/cli@latest doctor --fix{{HARNESS_FLAG}}` +- Non-interactive → `npx -y @tiny-fish/cli@latest doctor --fix --yes`; only `unattended_safe: true` repairs run and the rest return as skipped. Never report a skipped repair as a fix. - `unattended_safe: false` → hand it to the user, do not run it. Expect most repairs to be false: `auth login` always is, and `connect ` is unsafe for every harness except - Cursor — and on `2` Cursor only while the CLI's own authenticated call passes, since a + Cursor — and on `2` and `3` Cursor only while the CLI's own authenticated call passes, since a revoked key still resolves as a credential. Read the field, do not infer it. - OAuth credential failures have no CLI repair: {{REAUTH}} diff --git a/skills-src/feedback.md b/skills-src/feedback.md index aa441cd2..6cbe607d 100644 --- a/skills-src/feedback.md +++ b/skills-src/feedback.md @@ -20,7 +20,7 @@ doctor produced no JSON — say so instead of attaching an empty report. ## Structure -``` +```markdown ### What I was doing … ### What happened @@ -40,12 +40,14 @@ filing. Any edit they request happens before filing. ## File -- Preferred: write the body to a file, then - `gh issue create --repo tinyfish-io/tinyfish-cookbook --title "" --body-file ` (only if `gh` is installed and - authenticated). Never interpolate the body into the command — it carries - the user's free-form text and doctor's JSON, and backticks or `$(…)` in - either would be evaluated by your shell. +- Preferred: write the body and the title to files, then `gh issue create + --repo tinyfish-io/tinyfish-cookbook --title "$(cat "$title_file")" + --body-file "$body_file"` (only if `gh` is installed and authenticated). + Both values come from the user's free-form text (the body also carries + doctor's JSON), so neither may reach the shell as literal text — including + in a `title=…` assignment, where backticks or `$(…)` are evaluated just the + same. `cat`'s output is not re-parsed. Never build the command as a string + or run it through `eval`. - Fallback: open a prefilled issue URL (`https://github.com/tinyfish-io/tinyfish-cookbook/issues/new?title=…&body=…`). Percent-encode both values — an unencoded `#` truncates the body and `&` diff --git a/skills/tinyfish-doctor/SKILL.md b/skills/tinyfish-doctor/SKILL.md index 72c9117b..55275983 100644 --- a/skills/tinyfish-doctor/SKILL.md +++ b/skills/tinyfish-doctor/SKILL.md @@ -5,7 +5,7 @@ description: Diagnose and repair your TinyFish setup — MCP registration, auth, # TinyFish Doctor -`tinyfish doctor` (CLI 0.18+) owns the diagnosis. Your job is to run it, do the one +`tinyfish doctor` (CLI 0.22+) owns the diagnosis. Your job is to run it, do the one check it structurally cannot do, and act on what comes back. Never hand-edit config files — every repair goes through the CLI, which carries backup and merge rigor. @@ -17,31 +17,36 @@ the command from step 1 to run themselves. ## 1. Run doctor -``` +```sh npx -y @tiny-fish/cli@latest doctor ``` -JSON on stdout: `schema_version`, `checks[]`, `harnesses[]`, `repairs[]`. +JSON on stdout: `schema_version`, `cli_version`, `ok_harnesses`, `ok_cli`, `checks[]`, +`harnesses[]`, `repairs[]`. -Read `schema_version` before the fields. This skill describes `1` and `2`. The command pins -`@latest`, so a newer CLI can hand you a shape you do not know: above `2`, stop reading -fields, show the user `--pretty` output instead, and rely on step 2 for the verdict. +Read `schema_version` before the fields. This skill describes `3` (CLI 0.22+). The command +pins `@latest`, so a newer CLI can hand you a shape you do not know: above `3`, stop reading +fields, show the user `--pretty` output instead, and rely on step 2 for the verdict. Below +`3` a single `ok` replaces the two verdicts and `checks[]` carry no `scope`, so read only +`checks[]` and the exit code there. -The difference between the two is what a keyed registration proves. `2` tests an API-key -registration on the wire, so a stale key header fails outright and earns a `connect` repair. -`1` passes it on config presence alone, and step 2 is the only thing that catches it. +**Two verdicts, not one.** `ok_harnesses` answers whether the user's agents can reach +TinyFish; `ok_cli` answers whether the CLI's own credential works. `checks[].scope` +(`harness`, `cli`, `info`) says which one a check counts toward, and only the harness scope +moves the exit code — `ok_cli: false` beside exit `0` is a real state, not a contradiction. +Report it as the CLI's own credential, not as a broken harness. | Exit | Meaning | |---|---| -| `0` | every check passed | -| `1` | a check failed — read `checks[]` | +| `0` | no harness check failed — `ok_cli` can still be `false` | +| `1` | a harness check failed — read `checks[]` | | `2` | doctor could not run; **stdout is empty**, the reason is on stderr | A `warn` is not a failure and does not move the exit code: doctor is saying it could not -check something, not that it is broken. On `2`, `registered, API key present but unverified` -means the key exists but doctor cannot read its value to test it, which is every Codex -install and any harness whose config redacts the header. Never repair on a warn, prove it -in step 2. +check something, not that it is broken. A registration warn whose detail says the key was +not readable, or was not verified, means the key exists but doctor could not test its value +— every Codex install, and any harness whose config redacts the header. Never repair on a +warn, prove it in step 2. `--pretty` only when showing a human the list. Never put `--debug` output in a report — it is the one channel carrying raw stacks and absolute paths. @@ -49,10 +54,11 @@ it is the one channel carrying raw stacks and absolute paths. ## 2. Prove the harness reach — the part doctor cannot do `harnesses[].proves_harness_reach` is `false` whenever doctor could not prove that *this* -harness authenticates. For OAuth harnesses it is always false, because the CLI cannot borrow -the harness's token. You are the only one who can close that gap. +harness authenticates. It is `true` only where the harness's own client reports a live +connection, or where a key doctor could read verified on the wire — the CLI cannot borrow an +OAuth token, so every harness that reports no connection state leaves the gap to you. -Run without `--harness`, so `harnesses[]` carries one entry per harness doctor knows — installed or not. Read the entry whose `harness` matches the agent you are running in, never the first one, and check its `detected` first: an absent harness reports `detected: false`, `registered: "no"`, `auth_mode: "unknown"`, which is not a fault to repair. doctor only knows `claude-code`, `codex`, `cursor`, `hermes`, `openclaw`, `opencode`; if you are none of those, no entry describes you and step 2 is your only evidence. +Run without `--harness`, so `harnesses[]` carries one entry per harness doctor knows — installed or not. Read the entry whose `harness` matches the agent you are running in, never the first one, and check its `detected` first: an absent harness reports `detected: false`, `registered: "no"`, `auth_mode: "unknown"`, which is not a fault to repair. doctor only knows `claude-code`, `codex`, `cursor`, `grok`, `hermes`, `openclaw`, `opencode`; if you are none of those, no entry describes you and step 2 is your only evidence. **Count the TinyFish servers first.** A plugin, a CLI-written entry, and an account-level connector can all be registered at once, all pointing at the same endpoint. doctor inspects @@ -68,25 +74,28 @@ namespace names it. | Auth error, but doctor says `registered: yes` | Registration exists; the credential behind it is broken | | TinyFish tools absent entirely | Server not loaded in this session — the user must restart the agent | -What a `registration: pass` proves depends on `schema_version`. On `2` an API-key +What a `registration: pass` proves depends on `schema_version`. On `2` and `3` an API-key registration was tested on the wire, so a stale key header is already a `fail` with a -`connect` repair beside it. On `1`, and on every OAuth or `auth_mode: unknown` registration -at either version, pass is presence only: doctor read config, not the wire, and a stale key -still passes while every call 401s. Neither version says anything about siblings, and a +`connect` repair beside it. A pass carrying `proves_harness_reach: true` is the harness's own +client reporting a live connection — wire evidence at any version, whatever `auth_mode` says. +Every other pass is presence only: doctor read config, not the wire, and a stale key still +passes while every call 401s. No version says anything about siblings, and a healthy sibling will answer cheerfully while the broken one stays broken. ## 3. Repair -Run only commands that appear in `repairs[]`, and show `command` before running it. Keep -the order they arrive in: `auth login` comes before `connect` because `connect` writes -whichever key is stored, so a dead one has to be replaced first. +Run only commands that appear in `repairs[]`, and show `command` before running it. They +arrive as bare `tinyfish …`, which is not on PATH under `npx` — swap that leading word for +`npx -y @tiny-fish/cli@latest` when there is no global install. Keep +the order they arrive in: `action: auth-login` comes before `action: connect` because +`connect` writes whichever key is stored, so a dead one has to be replaced first. -- Terminal with the user present → `doctor --fix` -- Non-interactive → `doctor --fix --yes`; only `unattended_safe: true` repairs run and the +- Terminal with the user present → `npx -y @tiny-fish/cli@latest doctor --fix` +- Non-interactive → `npx -y @tiny-fish/cli@latest doctor --fix --yes`; only `unattended_safe: true` repairs run and the rest return as skipped. Never report a skipped repair as a fix. - `unattended_safe: false` → hand it to the user, do not run it. Expect most repairs to be false: `auth login` always is, and `connect ` is unsafe for every harness except - Cursor — and on `2` Cursor only while the CLI's own authenticated call passes, since a + Cursor — and on `2` and `3` Cursor only while the CLI's own authenticated call passes, since a revoked key still resolves as a credential. Read the field, do not infer it. - OAuth credential failures have no CLI repair: re-authenticate in the harness itself. @@ -95,6 +104,7 @@ whichever key is stored, so a dead one has to be replaced first. | Codex, Hermes | no login command — auth runs on first tool use; trigger a TinyFish tool and finish the browser sign-in | | OpenCode | `opencode mcp auth tinyfish` | | Claude Code | `/mcp` in-app, or `claude mcp login tinyfish` | + | Grok | no login command — in Grok Build, `/mcps`, select `tinyfish`, press `i`. A keyed registration instead reads `TINYFISH_API_KEY` from Grok's environment and has no OAuth fallback: re-run `tinyfish connect grok --api-key ` and start Grok from a new terminal | | OpenClaw, Cursor | key-based — `tinyfish auth login`, then `tinyfish connect ` to rewrite the header | Re-run step 2 after any repair. Success means showing the real search result — the user