Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"plugins": [
{
"name": "kbagent",
"version": "0.40.2",
"version": "0.40.3",
"source": "./plugins/kbagent",
"description": "AI-friendly interface to Keboola Connection projects — explore configs, jobs, lineage, call MCP tools, manage dev branches, and debug SQL in workspaces",
"category": "development"
Expand Down
2 changes: 1 addition & 1 deletion plugins/kbagent/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "kbagent",
"version": "0.40.2",
"version": "0.40.3",
"description": "AI-friendly interface to Keboola Connection projects — explore configs, jobs, lineage, call MCP tools, manage dev branches, and debug SQL in workspaces",
"author": {
"name": "Keboola",
Expand Down
26 changes: 26 additions & 0 deletions plugins/kbagent/skills/kbagent/references/gotchas.md
Original file line number Diff line number Diff line change
Expand Up @@ -1415,6 +1415,32 @@ Full CRUD for configuration rows is exposed as a separate `Rows` command panel:
- `row-delete` is **destructive** (gated behind `--allow-destructive` if the session firewall is on). 404 from the API on a non-existent row surfaces as `NOT_FOUND` exit 1 — deletion is **not** treated as idempotent success.
- `--json` mode auto-skips the interactive confirmation prompt on `row-delete`; in human mode pass `--yes` to skip.

## `project status` / `project list` expose `org_id` / `org_name`; `org_name` is Manage-API-only (since v0.40.3)

`ProjectConfig` now persists `org_id` (int | None) and `org_name` (str | None);
both are surfaced verbatim in `kbagent project status` and `kbagent project
list` JSON output. The two fields are populated from **different sources**:

- **`org_id`** comes from `data.organization.id` at the **top level** of the
Storage API `/v2/storage/tokens/verify` response (NOT under `owner`).
Populated whenever a project is added / re-verified — including the
opportunistic backfill that `/projects/status` performs for projects
registered before this release. The API returns the id as a string
(e.g. `"73"`); the parser normalises it to int.
- **`org_name`** is **Manage-API-only**. The Storage API never carries it.
It is populated only when the project flows through `kbagent org setup`
(which calls `/manage/organizations/{id}`) or when `kbagent project add`
runs in a context that has a Manage API token. Projects registered via
plain `kbagent project add` (Storage token only) keep `org_name: null`
indefinitely.

**AI agent rule of thumb**: when reading `project status` JSON, ALWAYS
handle `org_name: null` even when `org_id` is set. Do not pattern-match on
both being present; the asymmetry is the steady state for the majority of
projects. The web UI Projects table renders `#<org_id>` (e.g. `#73`) as a
fallback when only the id is known, so any agent producing a human-readable
project list should do the same — never render the bare null.

## `config oauth-url` requires a master Storage API token (since v0.30.0)

The OAuth wizard URL embeds a short-lived **child** Storage API token scoped
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "keboola-agent-cli"
version = "0.40.2"
version = "0.40.3"
description = "AI-friendly CLI for managing Keboola projects"
readme = "README.md"
requires-python = ">=3.12"
Expand Down
14 changes: 14 additions & 0 deletions src/keboola_agent_cli/changelog.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,20 @@

# Ordered newest-first. Each value is a list of brief one-line descriptions.
CHANGELOG: dict[str, list[str]] = {
"0.40.3": [
"New: `kbagent serve --ui` workspace SQL editor gains an AI-assisted SQL writer (#287). The 'Help me write this SQL' button opens an inline helper that spawns a local `claude` / `codex` / `gemini` CLI via the new `POST /workspaces/sql/improve/stream` SSE endpoint, feeds it a meta-prompt grounded in the user's workspace (project alias, backend, default schema, visible bucket catalog, backend-specific INFORMATION_SCHEMA recipes, and a MANDATORY-FIRST-STEP block forcing `kbagent storage bucket-detail` for linked-bucket FQN resolution), streams the response back, and pastes the cleaned SQL into the Monaco editor. Three transparency panels are surfaced: the full meta-prompt (so users can audit what the AI received), an Activity log (tool_use -> tool_result events the AI invoked: `-> Bash: kbagent storage bucket-detail ...`), and the final AI suggestion with copy-to-clipboard. Each panel carries an inline copy pill. The `clean_sql_helper_response` strip pipeline handles claude's Insight blocks (the user-set `explanatory` output style leaks them despite the OUTPUT CONTRACT), code fences, preambles, and JSONL duplication. Fix-mode: when a query Run fails, a 'Send to <cli> for fix' button re-opens the helper with the failing SQL + the warehouse error pre-filled; `build_sql_helper_meta_prompt` pivots framing to 'diagnose and fix'. The Snowflake backend hint mandates double-quoting of EVERY identifier including column / table / CTE aliases (`AS \"month\"` not `AS month`) -- Snowflake uppercases unquoted aliases and the resulting CSV columns came back MONTH / EMPLOYEE_COUNT instead of the lowercase names users expected.",
"Fix: `wait_for_query_job` now extracts the real warehouse error from `statements[i].error` (a plain string on Snowflake, sometimes a dict on BigQuery), not from a top-level `error` field that is ABSENT on failures. The previous extractor emitted the useless 'Query job failed: Query execution failed' constant for every failure; the SQL editor's red error box and the AI fix-mode prompt now receive messages like 'SQL compilation error: Function DATE_TRUNC does not support VARCHAR(10) argument type' verbatim. New module-level `_extract_query_job_error` helper walks statements first (with one-line `Statement N:` prefix only when multiple statements failed), falls back through top-level (string OR dict-with-message), and finally an explicit `Query execution failed (no error details from Query Service)` so the caller never gets an empty error string. 6 unit tests pin the four input shapes plus the no-info fallback.",
'New: `project status` / `project list` JSON output exposes per-project `org_id` (int) and `org_name` (str | None) fields (#290). `org_id` is parsed from the top-level `organization.id` of the `/v2/storage/tokens/verify` response and normalised from string (`"73"`) to int (`73`) so persisted `ProjectConfig.org_id` keeps its declared int type. `org_name` is **Manage-API-only** -- the Storage API only carries the id; the name is populated via `kbagent org setup` (which calls `/manage/organizations/{id}`) or by `kbagent project add` when a manage token is in scope. Opportunistic backfill: `/projects/status` writes the freshly-discovered `org_id` back to `config.json` in a single serial pass after the parallel status check completes, so the value sticks for projects registered before this release. The web UI Projects table and top-bar project picker render `Keboola Demo` when the name is known, `#73` (monospace) when only the id is known, and dash when neither is known. The React Query cache for `/projects` is invalidated automatically once `/projects/status` returns so the ORG column populates without a manual page reload. Tooltips on `#73` and dash explain how to populate the name. ProjectConfig migration is backward-compatible: legacy `config.json` files without the new fields load cleanly with both fields defaulting to `None`.',
"New: `kbagent serve --ui` auto-generates a stable `KBAGENT_CONVERSATION_ID` for the session in the format `serve-<UTC-timestamp>-<8-hex>` (e.g. `serve-20260515T091949Z-699ea57b`) and exports it to env before `create_app()`. Child processes (MCP subprocess, AI agent CLI invocations, every scheduled `kbagent http` call) inherit it and emit `X-Conversation-ID` on every Keboola API request. `kbagent doctor` flips from `warn: Conversation ID not set` to `pass: X-Conversation-ID: serve-...`. The `serve-` prefix lets observability dashboards filter human-driven sessions; the timestamp makes log lookups by session-start trivial; the hex suffix disambiguates rapid restarts in the same second. A pre-set `KBAGENT_CONVERSATION_ID` in env is respected verbatim so CI / supervisor scripts can pin a stable id across restarts. The startup banner gains a `conv id` line in both UI and API-only mode, plus the `export KBAGENT_CONVERSATION_ID=...` hint for the second-terminal `kbagent http` workflow.",
"Fix: Lineage / Sharing graph (#289). Three changes layered on top of each other. (1) Mermaid's `maxTextSize` config is bumped from the default 50 KB to ~5 MB so the typical 50+ project / 250+ edge graph renders natively instead of bouncing off the size guard. (2) When Mermaid still hits the guard, soft-error detection now post-checks the rendered SVG for the literal 'Maximum text size in diagram exceeded' marker -- the renderer does NOT throw on size limit, it embeds the failure text INSIDE the SVG, so the previous `.catch()` path never fired. The styled amber banner kicks in instead of a silent useless red box. The banner replaces the previous CLI hint (`kbagent lineage server --load ...`) with two in-UI buttons: `Open Deep Lineage tab` (one-click switch to the dedicated viewer) and `Download Mermaid source` (handoff to mermaid.live or any external renderer). (3) The diagram itself is now wrapped in a 600px fixed-height scrollable viewport with `overflow: auto` so scrolling stays INSIDE the box instead of pushing the entire page layout. Two `<select>` filter pickers (Source project / Target project, populated from unique aliases in the edge set) sit above the viewport and narrow the edge set with AND semantics; a `clear` button + edge counter (`128 / 252 edges`) round out the toolbar. Empty filter results render a friendly 'No edges match' instead of an empty Mermaid node.",
"Fix: AI helper meta-prompt (`build_sql_helper_meta_prompt`) makes `bucket-detail` a MANDATORY FIRST STEP with the specific failure mode quoted: `Schema 'KBC_USE4_<workspace_project>.\"in.c-foo\"' does not exist`. The previous LINKED BUCKETS section was passive ('CRITICAL for correctness') and the AI routinely skipped `bucket-detail` once `table-detail` returned column info -- the workflow optimisation cost users one broken SQL run per first-attempt. The new framing is imperative, numbers the steps explicitly (bucket-detail -> column discovery -> write SQL), and spells out `table-detail gives you column names, NOT the correct database`. Paired with the warehouse-error extraction above, the fix-mode loop now has the real Snowflake error to correct the first-pass mistake.",
"New: Workspace SQL editor Storage Explorer flags linked buckets with an inline `linked` pill + tooltip pointing to `kbagent storage bucket-detail` for the correct FQN. The result-table header gains `Download CSV` and `Copy as CSV` buttons per statement -- both work entirely client-side (the CSV is already on the page from `/workspaces/.../query`). Filename embeds the statement index + a UTC timestamp so consecutive Run cycles don't collide.",
"Fix: Web UI Dashboard greeting (#285) drops the hardcoded `, Petr` and renders only the time-of-day phrase (`Good Morning` / `Good Afternoon` / `Good Evening`). The hardcoded name was visible to every user and violated the no-hardcoded-defaults rule.",
"New: Top-bar project picker dropdown gains a quick-search input + wider 384px column (was 288px). The search appears only when there are at least 5 projects registered, filters case-insensitively across `alias`, `project_name`, and `org_name`, auto-focuses on open, Escape clears (or closes when empty), and Enter picks the only remaining match when the filter narrows the list to one. Multi-org setups stay distinguishable via the `org #73` suffix when name is unknown but id is.",
"Closed: issue #288 (Multi-project Kai) closed as `wontfix`. Cross-project comparison, migration assistant, and lineage root-cause tracing already work via the local Claude + multi-project CLI (`--project NAME` flags) -- no value in duplicating that in the Kai chat, which is by design per-project. Comment posted on the issue with the reasoning.",
"Tests: +37 unit tests across the release. `test_workspace_sql_helper.py` (31: meta-prompt content / discovery instructions / Snowflake quoting / BigQuery dataset path / linked-bucket MANDATORY FIRST STEP / fix-mode pivot, plus full extraction pipeline coverage including Insight chatter stripping, CTE detection, header-comment preservation, no-SQL passthrough, JSONL dedup; SSE endpoint integration with mocked `stream_ai_agent_events`). `test_client.py` (+2: top-level `organization.id` parsing + string-to-int normalisation + no-org-block fallback; +6 in `TestExtractQueryJobError` against the four input shapes). `test_services.py` (+3: opportunistic org-info backfill, no-backfill when already set, no-backfill when verify returned no org). `test_org_service.py` (+2: org_name populated from per-project payload, fallback to `get_organization` API call). `test_serve_conversation_id.py` (4: fresh ID generation, uniqueness across calls, env override, whitespace-only env fallback). Total suite: 3140 passed, 7 skipped.",
"Plugin docs: `plugins/kbagent/skills/kbagent/references/gotchas.md` gains a `(since v0.40.3)` section on the new `project status` / `project list` `org_id` / `org_name` fields, the Storage-vs-Manage-API source split, and the `org_name=None` even-when-org_id-is-set quirk. `CLAUDE.md ## All CLI Commands` unchanged (no new CLI commands introduced; all new behaviors are web-UI / serve-side). `keboola-expert.md` Rule 6 VERSION GATE and Tool Selection Matrix unchanged. `SKILL.md` auto-regenerated.",
],
"0.40.2": [
"Fix: `kbagent update` (and the startup auto-update hook) now preserves the optional `[server]` extras across upgrades. Previously `_update_kbagent` ran `uv tool install --upgrade git+...` with no `--with` flag, so the FastAPI + uvicorn extras a user originally installed with `--with 'keboola-agent-cli[server]'` were silently dropped on every upgrade -- a user who ran `kbagent serve --ui` happily yesterday got `ModuleNotFoundError: No module named 'fastapi'` after auto-update today. Now `_update_kbagent` probes `importlib.util.find_spec('fastapi')` (a reliable proxy for the `[server]` extra, which is the only thing that drags FastAPI in) and, when present, runs `uv tool install --force --with 'keboola-agent-cli[server]' git+...` instead. `--force` is paired with `--with` because uv rejects `--upgrade --with` if the additional spec resolves to a different version than the existing tool environment; `--force` is uv's documented way to reapply both flags in one shot. Pip fallback uses the PEP 508 `keboola-agent-cli[server] @ git+...` spec syntax. CLI-only users (no `[server]` extras installed) see no change.",
"UX: `kbagent serve --ui` startup banner now prints copy-paste-able `export KBAGENT_SERVE_URL=... ; export KBAGENT_SERVE_TOKEN=...` lines so you can call `kbagent http get /projects` from another terminal without grepping the previous banner for the token. The plain `kbagent serve` (no `--ui`) banner gets the same hint. Closes a paper-cut where `kbagent http` printed `requires KBAGENT_SERVE_URL and KBAGENT_SERVE_TOKEN env vars` but the serve banner didn't tell you how to set them.",
Expand Down
Loading
Loading