feat(plugin): add /kbagent:setup one-command first-run setup - #625
Conversation
Collapse the documented five-step onboarding (install CLI -> project add with a hand-pasted Storage API token -> doctor -> two /plugin commands) into one slash command that runs after the plugin is installed. plugins/kbagent/commands/setup.md orchestrates existing verbs only -- `--json version` (respecting `install_channel` on standalone builds), `--json project list` (skip if already connected, never overwrite an alias), `--json auth login --register-projects` (browser PKCE, so no token to paste; relays `session_unsupported_features` from the result rather than hand-listing it) with a `project add` hidden-prompt / KBC_TOKEN fallback for headless hosts, then `--json doctor` interpreted for the user including the claude_plugin check. Every step is conditional on a check, so the command is idempotent. No new CLI surface, no change to `doctor` (its --fix removal in 0.85.0 stands), no token ever printed or passed on a command line. Doc sync for the new surface (none of these are CI-checked): plugin .claude-plugin/CLAUDE.md surfaces list and "For Claude Code users"; skills/kbagent/SKILL.md prose "First-time setup" (auto-generated table untouched); README.md and docs/TUTORIAL.md so the documented flow is install plugin -> /kbagent:setup; install.sh "Next steps"; AGENT_CONTEXT in commands/context.py. Also corrects the already-stale plugin surface inventory in CLAUDE.md and widens the CONTRIBUTING.md sync-map row from commands/keboola.md to commands/*.md with the follow-through list for adding a new slash-command file.
The next-steps hint hardcoded the Claude Code marketplace and plugin names (`keboola/cli` / `kbagent@keboola-agent-cli`). #627 moves the marketplace listing to keboola/ai-kit and does not touch install.sh, so the hardcoded pair would merge silently and then advertise deprecated names. Point at `kbagent doctor` instead, which already prints the current `/plugin` commands and is updated by #627 -- correct under either merge order.
|
Merge-order note vs #627 (which moves the plugin's marketplace listing to This PR overlaps #627 in As of this push, Generated by Claude Code |
Resolves the sync-map conflict in CONTRIBUTING.md. main moved from 0.86.0 to 0.89.0 while this branch sat, and two adjacent table rows collided: - `keboola-expert.md` row: keep main's "70 000 B prompt budget". v0.88.0 raised the ceiling from 62 000 B, and tests/test_agent_prompt.py (test_documented_budget_matches_enforced_budget, added in #586) now gates CONTRIBUTING.md against PROMPT_BYTE_BUDGET, so this branch's stale "60 KB" would fail CI. - `commands/*.md` row: keep this branch's widened row plus its follow-through list for adding a new slash-command file. No version or changelog change: main's #648 moved version bumps into dedicated release PRs, and this branch bumps nothing.
Resolves the conflicts introduced by #627 (kbagent published through keboola/ai-kit as kbagent@keboola-claude-kit). - README.md, docs/TUTORIAL.md, commands/context.py: the /plugin fences now carry main's marketplace and plugin names plus this branch's /kbagent:setup line. - CLAUDE.md: keeps main's rewritten "source here, publication there" section; its stale surfaces sentence is replaced with this branch's corrected list, made exact against the tree (3 skills, 3 slash commands, 2 subagents). Its install fence gains /kbagent:setup. - plugins/kbagent/commands/setup.md: the claude_plugin bullet no longer paraphrases doctor's drift hint (main qualifies it with the marketplace) and now tells the agent to quote doctor's own /plugin lines as the single source of truth for the names. install.sh already defers to `kbagent doctor` and hardcodes no names, so main's rename does not reach it. No version file and no CI-gated figure is touched.
|
Merged
Two things reviewers should look at, since they go slightly beyond mechanical conflict resolution:
Untouched on purpose: no version file, no Also note the PR description above is now stale — its Before/After block still shows Green: Generated by Claude Code |
soustruh
left a comment
There was a problem hiding this comment.
The "Open question for reviewers" section asks two things. Both answered here.
1. Yes — try auth login-password before the static token
In step 3, the no-browser fallback goes straight to project add with a static token. Add one step before it.
When KBC_LOGIN_EMAIL and KBC_LOGIN_PASSWORD are set — plus KBC_LOGIN_TOTP_SECRET for an MFA account — run:
kbagent --json auth login-password --register-projects
The command reads those values from the environment (commands/auth.py:388-427). No secret appears in the conversation, and none appears on a command line. Keep the static token as the last resort, for when those variables are absent.
State two guards in the text:
- Needs kbagent 0.84.0+. On an older version, go straight to the static token.
- A passkey-only account fails with
AUTH_MFA_INVALID. On that error, fall back to the static token.
This is not a new policy. The repo documents this exact order since 0.84.0 (.claude-plugin/CLAUDE.md, auth-workflow.md): browser login → account login from the environment → static token. The setup command should follow that order, not skip the middle step.
2. The two files do not contradict each other — change neither
The static-token line in keboola-expert.md sits inside the version-gate rule. It applies only when the installed kbagent is older than 0.84.0. On those versions login-password does not exist, so a static token is the only option. .claude-plugin/CLAUDE.md describes current versions. Each rule is correct in its own scope.
History confirms this. Commit 8ee25c9 (PR #565) updated both files together, and its message states the goal: the doc surfaces "get their own version-gated section/bullets instead of contradicting the CLI". The same commit added the tool-matrix row in keboola-expert.md that itself prefers login-password and says "static token if no creds". The two files state the same policy.
No change in this PR. A follow-up may add one clarifying sentence to the version-gate line — it reads as a general rule when seen in isolation, and the 2026-08-13 byte-budget trim made it terser.
Addresses @soustruh's review on #625. **Review item 1 — try `auth login-password` before a static token.** Step 3 was a two-rung fallback (browser login -> `project add --token`), skipping the middle step the repo has documented since 0.84.0. It is now an explicit three-rung ladder, 3a/3b/3c, with the reviewer's two guards stated in the text: `login-password` does not exist before 0.84.0 (compare `kbagent.version` from step 1, else skip to the static token), and `AUTH_MFA_INVALID` means a WebAuthn/passkey-only account, which drops to the static token rather than retrying or looping back to the browser rung. 3b runs only when KBC_LOGIN_EMAIL + KBC_LOGIN_PASSWORD are already exported (+ KBC_LOGIN_TOTP_SECRET for TOTP MFA); the command reads all three off the environment, so nothing lands on a command line. The "never persist a token" rule is widened to any credential, and made explicit that the command must never *solicit* a password or TOTP seed -- absent env vars close that route, they are not a prompt. **Review item 2 — the two doc surfaces.** No change, per the review: the `keboola-expert.md` static-token line sits inside the version-gate rule and is correct for pre-0.84.0, and `.claude-plugin/CLAUDE.md` describes current versions. Each is right in its own scope. **Separately: step 3 could not have worked as written.** `auth login` without `--stack` resolves the stack from the default project, and step 2 has just established there is no project registered -- so on the fresh-install path this command exists to serve, it failed with CONFIG_ERROR ("login is not stack discovery"). Verified against an empty config dir. Every login invocation now passes `--stack <STACK_URL>`, taken from $ARGUMENTS or asked for up front; the argument-hint says so. Doc surfaces re-synced (CONTRIBUTING.md "Plugin synchronization map", none CI-checked): README.md, docs/TUTORIAL.md, plugins/kbagent/.claude-plugin/ CLAUDE.md, skills/kbagent/SKILL.md prose, commands/context.py AGENT_CONTEXT. No version bump, no changelog entry, no CLI surface change. Verified: `--stack` and `--register-projects` exist on both login commands; env-var resolution satisfies the required `--email` and reaches the server with no secret on the argv; `auth login --stack` gets past stack resolution on an empty config where the flagless form errors. lint, format-check, typecheck (0 errors, 66 pre-existing warnings), version-check, version-gate-check, command-sync-check, endpoints-check, check-error-codes, check-sentinel-guards, loc-check green. Tests: 6154 passed, 181 skipped, 0 failed. skill-check flags SKILL.md only because the hand edit is uncommitted -- `make skill-gen` regenerates it byte-identical. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Both items handled in 1.
|
keboola-pr-reviewer-bot
left a comment
There was a problem hiding this comment.
Verdict: auto_approve (risk 2/5) · profile keboola-mcp-server
Docs-plus-plugin-instructions PR adding a /kbagent:setup slash command; no code path, tool, auth, or dependency change.
Concerns:
plugins/kbagent/agents/keboola-expert.md: Unreconciled auth guidance contradicts .claude-plugin/CLAUDE.md; pre-existing, left for follow-up
soustruh
left a comment
There was a problem hiding this comment.
Step 3 now follows the requested order, and the --stack fix is correct. I verified it in auth_service.py:845-872: login never derives a stack by itself, so the previous form failed on a fresh config. One routing error remains.
The session-insufficient case must skip 3b. The "Drop to 3b when…" sentence sends two cases into 3b: the user needs a feature from the session_unsupported_features list, or no browser exists. The first case does not belong there. login-password creates the same session type as browser login, with the same unsupported-feature list (.claude-plugin/CLAUDE.md: same auth mode, same downstream command support). For that user, 3b succeeds, the flow stops, and the session still cannot serve the feature they need. Send that case directly to 3c. Only the no-browser case tries 3b before 3c.
padak
left a comment
There was a problem hiding this comment.
Review of #625 — feat(plugin): add /kbagent:setup one-command first-run setup
Generated by
kbagent-pr-reviewersubagent. Verdict and findings below
are advisory; the human author retains every veto. CI-coverable issues
(lint, format, tests) are confirmed viamake check, not duplicated here.
Summary
This is a docs-and-plugin-instructions PR: it adds plugins/kbagent/commands/setup.md (a new /kbagent:setup slash command that orchestrates existing, already-tested verbs — version, project list, auth login, auth login-password, project add, doctor) and updates every hand-maintained doc surface that references the plugin's install flow (CLAUDE.md, CONTRIBUTING.md, README.md, docs/TUTORIAL.md, install.sh, plugins/kbagent/.claude-plugin/CLAUDE.md, SKILL.md prose, commands/context.py AGENT_CONTEXT). No Python business logic, no new CLI command, no permissions.py/server-route surface touched. Verdict: APPROVE. The one open item from the review thread (soustruh's CHANGES_REQUESTED, asking for a login-password rung before the static-token fallback) was already addressed in commit b1353e56 on the current HEAD, and I independently re-verified soustruh's second conclusion — keboola-expert.md §1 Rule 6 and .claude-plugin/CLAUDE.md are not actually contradictory, one is a version-gate ("if login-password doesn't exist on this install, fall back to a token"), the other describes current-version behavior — so no code change was warranted there.
Verdict
- Verdict: APPROVE
- Blocking findings: 0
- Non-blocking findings: 1
- Nits: 1
Blocking findings
(none)
Non-blocking findings
[NB-1] plugins/kbagent/agents/keboola-expert.md:77-78 — the version-gate line reads as a general policy in isolation
Both the PR author's original "Open question for reviewers" and the auto-approving bot's review independently flagged this same line as looking contradictory with .claude-plugin/CLAUDE.md's auth-preference guidance. I traced the actual semantics and confirm soustruh's read is correct — 77-78 sits entirely inside §1 Rule 6 (VERSION GATE) and only fires when the installed kbagent predates 0.84.0 (where login-password genuinely does not exist), while §2's Tool Selection Matrix (keboola-expert.md:151) already prefers login-password for CI-with-credentials on current versions. The two files are consistent, not contradictory. Since three independent readers (PR author, bot, human reviewer) tripped on the same sentence, it is worth a follow-up one-line clarification (e.g. "on kbagent < 0.84.0, ...") to stop it recurring — correctly scoped out of this PR by the author, tracked here so it isn't lost.
Nits
[NIT-1]CONTRIBUTING.md(Plugin synchronization map row forplugins/kbagent/commands/*.md) — the row now says "Slash-command UX change (rare)" immediately followed by a fairly non-rare follow-through list for new command files; consider splitting into two rows (existing-file UX tweak vs. new-file addition) next time the table is touched, purely for scanability.
Verification log
gh pr view 625 --json title,body,files,additions,deletions,...→ 9 files changed, +233/-9,feat(plugin):prefix matches (new user-facing capability) ✓gh pr diff 625(382 lines) reviewed in full — no code files (src/keboola_agent_cli/{commands,services,client,manage_client.py}etc.) touched except a pure-string addition tocommands/context.pyAGENT_CONTEXT(exempt fromloc-checkper CONTRIBUTING.md's explicit_EXEMPTlist) → 3-layer-architecture step is N/A, confirmed notyper/httpxlayer-violation greps fire (no matches, nothing to check against)- No new CLI command added (no
@*_app.command(...)) →permissions.pyOPERATION_REGISTRY,server/routers/*.py,commands-reference.md,gotchas.mdversion-tag requirement all correctly out of scope per CONTRIBUTING's own "new slash-command file" checklist (which this PR itself extends in theCONTRIBUTING.mddiff) - Walked the extended sync-map row this PR adds for
commands/*.md:.claude-plugin/CLAUDE.mdsurfaces list ✓ updated,SKILL.mdprose ✓ updated,README.md✓ updated,docs/TUTORIAL.md✓ updated,commands/context.pyAGENT_CONTEXT✓ updated,install.sh"Next steps" ✓ updated — every item on the author's own new checklist is actually present in the diff tests/test_skill_frontmatter.pyread → confirms it targetsSKILL.mdonly, no enumeration ofcommands/*.mdfiles that would need a fixture update; ran it (uv run pytest tests/test_skill_frontmatter.py tests/test_agent_prompt.py -q) → 46 passed ✓- Checked out PR HEAD (
b1353e56, the commit that already answers soustruh's review) into an isolated detached worktree (never touched the user's checkout) and ranmake check→ exit 0,ruff checkclean,ruff format --checkclean,ty checkclean (1 pre-existing unrelatedunresolved-importinfo-level diagnostic inscripts/hatch_build.py, not part of this diff),SKILL.md is up-to-date,version is in sync,check_version_gates.pyall 530 gates resolve (7 unrelatedvNEXTawaiting release),check_command_sync.pyOK 264/264 commands registered+documented,endpoints-checkup-to-date,changelog-checkOK,check_error_codes.pyOK,check_sentinel_guards.pyOK,check_file_size.pyonly pre-existing soft-ceiling warnings (none in touched files), 6154 passed, 12 skipped ✓ gh pr checks 625→ all 3 required checkspass(Windows build,check,test 3.12,pr-reviewer-bot) on current HEAD ✓- Reproduced setup.md's factual claims against the live built CLI from the PR worktree:
kbagent --json versionandkbagent --json --config-dir DIR project list→ confirmed root-callback flags (--json,--config-dir) go before the subcommand, exactly as the PR claimskbagent --json --config-dir DIR auth login --register-projects(no--stack) →CONFIG_ERROR, message"No stack to log into -- login is not stack discovery. Pass --stack <url-or-alias>, ..."— verbatim match to setup.md step 3's claimed errorkbagent --json --config-dir DIR doctor→claude_plugincheck returns apassstatus with a version-drift + deprecated-marketplace migration message naming/plugin marketplace add keboola/ai-kit//plugin install kbagent@keboola-claude-kit— matches setup.md step 4's description of what to relay verbatimsrc/keboola_agent_cli/commands/auth.py:389-427(auth_login_password) →email/password/totp_secretall declareenvvar=readingKBC_LOGIN_EMAIL/KBC_LOGIN_PASSWORD/KBC_LOGIN_TOTP_SECRET, and a--stackoption exists — matches setup.md step 3b's claims about the environment-var contract and citation ofcommands/auth.py:388-427errors.py:136(AUTH_MFA_INVALID) andservices/auth_service.py:324→ the error code exists and is raised on passkey-only MFA, matching step 3b's guardservices/_auth_registration.py:33(SESSION_UNSUPPORTED_FEATURES) andcommands/auth.py(session_unsupported_featureson the result) → matches setup.md step 3a's claim about the canonical list and its--jsonfield name
bash -n install.sh→ syntax OKgrep -rn "two slash\|a slash command (\/keboola`)"` across the repo → no stale references left un-updated elsewhere- Compared commit timeline: soustruh's
CHANGES_REQUESTEDreview was submitted 2026-08-25T12:45:05Z; commitb1353e56("fix(plugin): setup ladder -- login-password rung, and pass --stack") landed 2026-08-25T13:04:03Z, directly after and directly addressing it;keboola-pr-reviewer-botapproved at 13:17:56Z (after the fix), noting the samekeboola-expert.mdline as a pre-existing, correctly-deferred concern — consistent with my own independent trace above
Open questions for the author
(none)
padak
left a comment
There was a problem hiding this comment.
Second review pass — runtime behaviour of /kbagent:setup, not the diff's static correctness
Complementary to the kbagent-pr-reviewer pass above, which I agree with on everything it covered: the doc sync is complete, every factual claim in setup.md matches the source, and soustruh's CHANGES_REQUESTED is already answered by b1353e56 (submitted 12:45, fix landed 13:04) — so the block is procedural at this point, not substantive.
What follows is the axis that pass did not cover: what actually happens when an agent executes these instructions through a tool-run shell. setup.md is prompt text, so its bugs look like plausible instructions rather than bad code, and no CI gate reads commands/*.md at all.
🟡 1. Step 3a tells the agent to run a command the CLI explicitly says an agent must not run — and the call will be killed mid-flow
auth login's own docstring (src/keboola_agent_cli/commands/auth.py:291-295):
Requires a human at a browser -- an AI agent must not attempt this headlessly.
Root CLAUDE.md repeats it: "REQUIRES A HUMAN AT A BROWSER -- never attempt from an unattended AI agent task." Step 3a nonetheless hands the agent the command to execute. A /kbagent:setup session does have a human present, so this is not a flat violation — but the timing makes it fail in practice:
- PKCE loopback waits
AUTH_CALLBACK_TIMEOUT = 115.0s (src/keboola_agent_cli/constants.py:889). - The device-code fallback polls until
authorization.expires_in(src/keboola_agent_cli/auth/device.py:75-79) — server-set, typically several minutes.
Claude Code's Bash tool defaults to a 120 s timeout. Failure scenario: the user approves in the browser at t=130 s. The session is written to auth.json server-side and locally, but the agent saw a killed tool call with no result, cannot tell whether login landed, and re-runs step 3a — producing exactly the orphaned-session case commands/auth.py:177-182 warns about (orphaned_session_id, "kbagent auth logout will retry it").
Suggested fix: state an explicit long timeout for this one call, or hand auth login to the user to run in their own terminal — the way step 3c already does for project add's hidden prompt. Step 3c's reasoning ("that prompt needs a real TTY, which a tool-run shell does not have") applies here for the same class of reason, and it would be consistent to apply it.
🟡 2. The verification URL and device code go to stderr, and 3a's snippet does not capture it
commands/auth.py:304:
target_console = formatter.err_console if formatter.json_mode else formatter.consoleSo in --json mode the device-login panel — the URL and the user code the agent is supposed to relay — lands on stderr, as the docstring confirms ("always printed (to stderr in --json mode)").
Step 1's snippet has 2>&1. Step 3a's does not:
kbagent --json auth login --stack <STACK_URL> --register-projectsAnd the "Non-negotiable rules" block immediately above instructs: "Use --json for every check you have to parse. Parse the JSON; do not scrape human-mode Rich output." An agent following that rule literally, reading stdout, never sees the code. Suggested fix: add 2>&1 to the 3a snippet, or say in the prose that the code arrives on stderr and is not part of the JSON.
🟡 3. No Windows path in step 1
Step 1's only install route is curl -LsSf … | sh. README.md:31-48 documents that Windows needs either the winget + uv tool install PowerShell block or an explicit trip through Git Bash (& "C:\Program Files\Git\bin\bash.exe" -lc "…"). setup.md does not branch, and its retry advice (source $HOME/.local/bin/env) is POSIX-only.
On a Windows box without Git Bash, first-run setup — the exact audience this command exists for — dies at step 1 with no recovery path. A single sentence pointing at README's PowerShell block would close it.
🟢 Nits
CLAUDE.md— the prose was corrected to "three skills (kbagent,kbagent-cicd-migration,kbagent-promotion-pipeline)", but the structure tree directly below still lists onlyskills/kbagent/. The PR fixed one half of that drift and left the other.plugins/kbagent/.claude-plugin/CLAUDE.md:3— still "a skill (kbagent)", singular, now inconsistent with rootCLAUDE.md's corrected count. If the count was worth fixing in one file, it is worth fixing in both.SKILL.md— points readers at the repo pathplugins/kbagent/commands/setup.md. When the skill is loaded from~/.claude/plugins/cache/…, that path does not exist for the reader.
Verification
Everything above was read from source at origin/main, not inferred:
commands/auth.py:291-304, commands/auth.py:177-182, constants.py:889, auth/device.py:75-79, commands/project.py:172-183, services/auth_service.py:865-875, services/_auth_registration.py:33,119, services/doctor_service.py:563-640, README.md:31-48.
I also confirmed the claims setup.md makes that I did not list as findings — the --json root-callback flag placement, the CONFIG_ERROR text for a missing --stack, project add's TTY-prompt/KBC_TOKEN contract with no --token on argv, login-password's envvar= wiring, install_channel appearing only on a frozen build, and session_unsupported_features riding on LoginResult — all hold.
None of the three 🟡 items are blocking in the sense of being wrong-on-the-page; they are cases where an agent following the instructions faithfully hits a wall. Items 1 and 2 are worth fixing before this becomes the documented front door.
…ht to 3c Addresses @soustruh's second review on #625. The routing error is real. The "drop to 3b when *either* ..." sentence funnelled two different reasons for leaving 3a into the same rung. Only one belongs there. `login_password()` returns through the SAME `_finalize_login` as `login()` -- the only difference is `method="password"` -- and `session_unsupported_features` is a `default_factory=default_unsupported_features` field on the shared `LoginResult`, i.e. a copy of the one `SESSION_UNSUPPORTED_FEATURES` constant, independent of method. So for a user who needs a surface on that list, 3b succeeds, the ladder stops at the first rung that "landed", and they are left holding a session with the identical restriction they were trying to escape. Step 3 now routes on *why* 3a was not the answer: - No browser at all (headless, container, CI, SSH without forwarding) -> try 3b, then 3c. This is now the ONLY route into 3b. - Needs a surface named in `session_unsupported_features` -> straight to 3c, skipping 3b, because only a static token serves those surfaces. Two knock-on corrections that would otherwise have contradicted the fix: - The `AUTH_MFA_INVALID` guard justified "do not loop back to 3a" with "both routes into 3b" -- there is one route now, so it cites that instead. - `docs/TUTORIAL.md` explicitly sent "a surface a browser session does not serve" to `login-password`. Rewritten; it was the only downstream surface that asserted the wrong routing (README scopes 3b to headless already, and the plugin CLAUDE.md / SKILL.md / context.py summaries name the order without claiming which reason routes where). Verified in `auth_service.py` rather than from the prose: `login_password` -> `_finalize_login` (line 340), `LoginResult.session_unsupported_features` default factory (line 130), `default_unsupported_features()` returning `list(SESSION_UNSUPPORTED_FEATURES)`. lint, format-check, typecheck, skill-check, version-check, command-sync-check, endpoints-check, check-error-codes, check-sentinel-guards, loc-check all green. Tests: 6154 passed, 181 skipped, 0 failed. No Python changed this round -- two doc files only. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Dismissing prior approval — a new commit was pushed and this review was for an earlier SHA. Run @keboola-pr-reviewer-bot review to get a fresh verdict.
|
New commit on |
|
Fixed in I checked the claim in Step 3 now routes on why 3a was not the answer:
Two knock-on corrections that would otherwise have contradicted the fix:
Green: 🤖 Addressed by Claude Code |
Addresses @padak's second review pass on #625 (runtime behaviour of the instructions, an axis no CI gate covers -- nothing reads commands/*.md). **3a no longer tells the agent to run `auth login` itself.** Two independent reasons, both verified in source: - `commands/auth.py` docstring: "Requires a human at a browser -- an AI agent must not attempt this headlessly." `.claude-plugin/CLAUDE.md:97-99` is blunter still: hand the exact command back to the user and wait. 3a was handing the agent the command to execute. - It cannot finish inside a tool-run shell anyway. PKCE blocks on `AUTH_CALLBACK_TIMEOUT = 115.0` (constants.py:860) and the device fallback polls until the server's `expires_in` (auth/device.py:75). Against a ~120 s Bash timeout the call is killed mid-flow, and a kill is indistinguishable from a failure -- so the agent retries and produces the `orphaned_session_id` case commands/auth.py:178-182 warns about. 3a now hands over the command and waits, mirroring what 3c already does for `project add`'s hidden prompt, and the agent confirms afterwards with `auth status`. A standing rule was added: if a login call you did run gets interrupted, check `auth status` before re-running anything. **stderr.** The snippet drops `--json` deliberately -- a human wants the panel, and `commands/auth.py:304` routes it to `err_console` in json mode, so the device URL and code never appear in the JSON an agent would parse. Made a standing rule too, with `2>&1` for anyone who runs a login anyway. Consequence worth stating: `auth status` does NOT carry `session_unsupported_features` (only `login` / `register-projects` do -- `_auth_registration.py:119`, and context.py:189 says so explicitly). So 3a now names `auth register-projects --all` as the way to read that list programmatically, which is a no-op (`exists`) on anything already registered. **Windows.** Step 1's only route was `curl … install.sh | sh`, and its retry advice `source $HOME/.local/bin/env` is POSIX-only -- so first-run setup, the exact audience, died at step 1 on a Windows box without Git Bash. Step 1 now branches and points at README's Install section for the winget + `uv tool install` PowerShell block, including the "open a new shell after update-shell" caveat, and quotes rather than paraphrases it. Nits, all three: - Root `CLAUDE.md` prose said "three skills" while the structure tree directly below listed only `skills/kbagent/`. Tree now lists all three. - `.claude-plugin/CLAUDE.md:3` still said "a skill (`kbagent`)", singular. - `SKILL.md` pointed readers at the repo path `plugins/kbagent/commands/setup.md`, which does not exist when the skill is loaded from `~/.claude/plugins/cache/…`. Replaced with the command itself. Not changed, per both reviewers agreeing it is out of scope here: the `keboola-expert.md:77-78` version-gate wording (NB-1) and CONTRIBUTING's sync-map row split (NIT-1) are follow-ups. lint, format-check, typecheck, version-check, command-sync-check, endpoints-check, check-error-codes, check-sentinel-guards, loc-check green. Tests: 6154 passed, 181 skipped, 0 failed. Docs only -- no Python changed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
All three 🟡 items and all three 🟢 nits fixed in 🟡 1 — 3a no longer tells the agent to run
|
soustruh
left a comment
There was a problem hiding this comment.
Approve. All three review rounds are resolved on f01ca67, and I verified the new claims in the source:
- Step 3a is handed to the user, with the reason stated: the 115 s callback wait against a ~120 s tool timeout, and the command's own docstring. The agent confirms with
auth statusafterward, and the interrupted-login rule prevents the orphaned-session retry. - The stderr behavior of the auth commands is now a standing rule, and the 3a snippet drops
--jsonon purpose — correct,commands/auth.py:304routes the panel to stderr in json mode. auth statuslackssession_unsupported_features(AuthStatusResult,auth_service.py:134), soregister-projects --allis the right programmatic read: it carries the field and reportsexistson registered projects.- Step 1's Windows branch matches README's install block: winget,
uv tool install,update-shell, the new-shell requirement, and the Git Bash alternative. - All three nits are fixed. The two deferred items — the version-gate wording and the sync-map row split — stay follow-ups, as agreed by both reviewers.
Cover #625 in the 0.91.0 changelog (it merged to main while the release PR was open, landing inside the tag but outside its release notes), and move three resolved 0.91.0 version tags out of markdown headings onto section body lines per CONTRIBUTING.md release step 4. No version change.
Requested by David Esner · Slack thread
Before / After
Today, a new user follows five separate steps across two different places, in the right order, from the docs at keboola.com/cli: generate a Storage API token in the UI → open Claude Code → run the
curl … install.sh | shone-liner in a terminal →kbagent project add --project X --url Y --token …→kbagent doctor→ back in Claude Code,/plugin marketplace add keboola/ai-kitand/plugin install kbagent@keboola-claude-kit. Nothing in the repo chains those;kbagent doctordetects the missing plugin but deliberately does not fix it.After this PR, the documented flow is: install the plugin, then run one command.
/kbagent:setupdoes the rest — and there is no token to generate or paste on the default path.What it does
Adds one new plugin slash command,
/kbagent:setup, that runs first-time setup end to end — install the CLI if missing, connect a project, verify — with every step conditional on a check, so it is idempotent and safe to re-run on a half-finished setup.How
plugins/kbagent/commands/setup.mdis markdown instructions for the agent, in the same shape as the existingkeboola.md/review.md(same frontmatter keys,## Behaviornumbered steps,## Examples, a closing rationale section). It orchestrates existing, already-tested verbs — no new CLI surface, no new code path:kbagent --json version→ if the CLI is absent, run this repo's owninstall.sh, handling the installer's documented PATH caveat (kbagentis onPATHfor the installer's own process only;source $HOME/.local/bin/envor a new shell). Ifkbagent.install_channelis present, that is a standalone/packaged build — the command respects it and points atupgrade_command/upgrade_hintinstead of installing a second kbagent over the top.kbagent --json project list→ if a project is already connected, name the aliases and skip to verification. Never re-registers or overwrites an existing project or alias.kbagent --json auth login --register-projects— browser PKCE with the automatic device-code fallback, so nothing to paste. It readssession_unsupported_featuresoff the result (canonical list:SESSION_UNSUPPORTED_FEATURESinservices/_auth_registration.py) and relays it rather than hand-listing it, and falls back tokbagent --json project add --project '' --urlwhen the user needs one of those surfaces or there is no browser (headless, container, CI). The fallback never passes--tokenon a command line: eitherKBC_TOKENis already in the environment, or the command is handed to the user to run in their own terminal, becauseproject add's hidden token prompt needs a real TTY that a tool-run shell does not have.kbagent --json doctor, interpreted for the user — including theclaude_plugincheck (pass/ version drift →/plugin update kbagent;warn→ print the two/pluginlines;skip→ Claude Code not detected).The body encodes the rules explicitly: never print or persist a token,
--jsonfor anything parsed, don't re-run a step that already passes, and when a step genuinely needs a human (browser login), say so plainly instead of guessing.Flag placement was verified against the live CLI —
--jsonis a root-callback option, so it goes before the subcommand (kbagent --json project list, notproject list --json). All four invocations in the file were run locally.Doc sync (CLAUDE.md convention #17 / CONTRIBUTING.md "Plugin synchronization map")
None of these have a CI freshness check, so they are the silent-failure surfaces for this change:
plugins/kbagent/.claude-plugin/CLAUDE.md— surfaces list (two slash commands → three) and the "For Claude Code users" block now leads with/kbagent:setup.plugins/kbagent/skills/kbagent/SKILL.md— the prose "First-time setup" section points at/kbagent:setupas the one-command path, keeps the manual sequence below it for a plain shell or another agent, and swaps its staleuv tool install git+…line for theinstall.shone-liner plus the PATH caveat. The CI-checked auto-generated decision table was not touched (make skill-checkregenerates it clean).README.md— the Claude Code plugin block is now the two/pluginlines plus/kbagent:setup.docs/TUTORIAL.md— §5 install flow and the "What the plugin ships" component table.install.sh— the printed "Next steps" now leads with/kbagent:setupfor Claude Code users and keeps every existing explicit command under "Or do it by hand", so nothing regresses for people not in Claude Code.src/keboola_agent_cli/commands/context.py(AGENT_CONTEXT) — the "Claude Code Plugin" section, since this is the primary reference agents load at session start.CLAUDE.md— the plugin structure tree and surfaces sentence were already stale (still claimed one slash command and one subagent); corrected while addingsetup.md.CONTRIBUTING.md— the sync-map row forplugins/kbagent/commands/keboola.mdwas stale for the same reason. Widened tocommands/*.mdand given the explicit follow-through list for adding a new slash-command file, which the map has never had (/kbagent:reviewwas added without it).No changelog entry, and no version bump: the diff touches no version file (
pyproject.tomlis untouched) and adds no changelog entry, and CONTRIBUTING defines a release as a version bump. No new tests: per CONTRIBUTING, the mandatory test checklist is scoped to new CLI commands (service / CLI / E2E layers), and no CI gate or test readsplugins/kbagent/commands/*.mdat all —tests/test_skill_frontmatter.pytargetsskills/kbagent/SKILL.mdexclusively. Nothing enumerates plugin command files, so there was no such test to extend. Flagging that gap rather than papering over it.Checks run
Full local sweep on the pre-merge branch was green:
make lint,make format-check,make skill-check,make version-check,make command-sync-check,make check-error-codes,make check-sentinel-guards,make loc-check(pre-existing soft-ceiling warnings only, none in files this PR touches),make typecheck(0 errors),make test, andbash -n install.sh. The live CLI invocations —kbagent --json version/project list/doctor— were all verified working with the documented flag placement.Two caveats, both unchanged by the merge:
make testshows two failures locally,tests/test_config_store.py::TestPermissionDenied::{test_save_to_readonly_directory,test_load_unreadable_config_file}. Both are environmental and pre-existing: the local sandbox runs as uid 0, sochmod 0o000does not deny root and the expectedConfigErrornever raises. They pass on CI runners, and this diff touches no config-store code.make test-e2ewas not run — it needsE2E_API_TOKEN+E2E_URLagainst a real project, and no CLI behavior changed.make changelog-checkwas likewise not run: it requires theghbinary, absent from this environment. It is local/release-time only and is deliberately not part of CI's per-PRcheckjob.Rely on the CI checks on the current head for the authoritative result.
Not in this PR
kbagent setupPython CLI subcommand for agents that are not Claude Code. That is a larger change with real CI follow-through (permissions.pyOPERATION_REGISTRY,commands/context.py,commands-reference.md,CLAUDE.md## All CLI Commands,generate_skill.pySKIP_COMMANDS, plus service/CLI/E2E tests, all gated byscripts/check_command_sync.py) and deserves its own review. A slash command also has a chicken-and-egg limit worth naming: it only helps someone who already installed the plugin, so it cannot cover the/pluginstep for the person who most needs it — it can only detect and explain it.doctorwas not changed to auto-fix anything.doctor --fixwas deliberately removed in 0.85.0; reopening that is out of scope here.Open question for reviewers
The repo's own auth guidance forks, and this command had to pick a side.
plugins/kbagent/.claude-plugin/CLAUDE.md:97-105says a static Storage token is not automatically the answer for an unattended context, and preferskbagent auth login-password(0.84.0+) when account credentials exist.plugins/kbagent/agents/keboola-expert.md:77-78says the opposite: refuse session auth and point atproject add --token./kbagent:setupcurrently prefers browserauth login --register-projectswith aproject add(hidden-prompt /KBC_TOKEN) fallback — chosen because the product goal is explicitly "no token to paste", and becauselogin-passwordneeds email + password + TOTP that a first-time user is unlikely to have staged. But that is a third position, not a reconciliation. Two things reviewers should decide:auth login-passwordoverproject addwhenKBC_LOGIN_EMAIL/KBC_LOGIN_PASSWORD/KBC_LOGIN_TOTP_SECRETare present in the environment?keboola-expert.md:77-78and.claude-plugin/CLAUDE.md:97-105should be brought into agreement — they currently give an agent contradictory instructions, independent of this PR. I did not touch either, since picking a winner is a product call andkeboola-expert.mdis the highest silent-drift-risk file in the repo.