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.87.0",
"version": "0.88.0",
"source": "./plugins/kbagent",
"description": "AI-friendly interface to Keboola Connection projects — explore configs, jobs, lineage, sync configs as files, manage dev branches, and debug SQL in workspaces",
"category": "development"
Expand Down
12 changes: 12 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,18 @@ kbagent storage buckets [--project NAME] [--branch ID]
kbagent storage bucket-detail --project NAME --bucket-id ID [--branch ID]
kbagent storage tables [--project NAME ...] [--bucket-id ID] [--branch ID]
kbagent storage table-detail --project NAME --table-id ID [--branch ID]
# table-detail (0.88.0+, #621): also returns the raw Storage API `definition`. On BigQuery
# that object (timePartitioning / rangePartitioning / clustering / requirePartitionFilter /
# partitions[]) is the ONLY readable record of the registered layout, so it is how a
# `create-table --source-table-id ...` + `swap-tables` repartition is VERIFIED -- the table
# ID is unchanged either way, and `create-table` only echoes the layout you REQUESTED (its
# --if-not-exists skip path nulls the layout keys outright). Human mode adds Time
# partitioning / Range partitioning / Clustering / Partition filter required / Partitions
# (a COUNT) and prints nothing new when there is no layout; --json passes `definition`
# through verbatim, including the unbounded `partitions[]` (one entry per physical
# partition from INFORMATION_SCHEMA.PARTITIONS). `definition` is present on EVERY response
# -- untyped tables get one too -- so null means the stack omitted the key, NOT "untyped".
# `storage tables` (the LIST endpoint) is unaffected: the API has no `definition` include.
kbagent storage create-bucket --project NAME --stage STAGE --name NAME [--description D] [--backend B] [--branch ID]
kbagent storage create-table --project NAME --bucket-id ID --name NAME [--column COL:TYPE[(length)] ...] [--primary-key COL] [--not-null COL ...] [--default NAME=VALUE ...] [--source-table-id ID] [--source-branch-id N] [--time-partitioning-type DAY|HOUR|MONTH|YEAR] [--time-partitioning-field COL] [--time-partitioning-expiration-ms MS] [--range-partitioning-field COL --range-partitioning-start S --range-partitioning-end E --range-partitioning-interval I] [--clustering-field COL ...] [--branch ID] [--if-not-exists]
# --column XOR --source-table-id (0.66.0+, BigQuery only): --source-table-id copies an existing table's data into the requested partition/clustering layout (schema derived from source) -> swap into place with swap-tables. Partition/clustering flags work in both modes (BigQuery only); time vs range partitioning are mutually exclusive. A non-BigQuery project fails fast (pre-flight backend check).
Expand Down
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ before the PR is mergeable.

- [ ] **`plugins/kbagent/agents/keboola-expert.md`** -- the subagent system prompt. **Highest silent-drift risk in the repo.** Update at minimum:
- [ ] **§1 Rule 6 VERSION GATE** examples (e.g. `flow update needs 0.22.0+`) when adding a command that introduces or relaxes a minimum-version requirement, or when an example version reference is now stale enough to mislead.
- [ ] **§2 Tool Selection Matrix** -- one row **per command GROUP**, not per command. When you add a new write/destructive *group* (e.g. `dev-portal`), give it a single row with `First choice / Fallback / NEVER`. Adding a command to an *existing* group needs no new row. Exhaustive per-command detail belongs in `AGENT_CONTEXT` (`kbagent context`), which is loaded dynamically on demand -- `keboola-expert.md` is a static system prompt loaded into every subagent run and carries a hard 60 KB budget, so it must stay a high-signal decision matrix, not a command catalogue. If a one-row addition would push the file over budget, trim stale content first; do **not** raise the cap. *Severity note:* authors are expected to add the group row, but `/kbagent:review` flags a missing row only **NON-BLOCKING** -- `AGENT_CONTEXT` (a BLOCKING surface above) is the authoritative command catalogue, so a missing matrix row degrades subagent ergonomics without making a command undiscoverable. Don't deprioritize it just because it's non-blocking.
- [ ] **§2 Tool Selection Matrix** -- one row **per command GROUP**, not per command. When you add a new write/destructive *group* (e.g. `dev-portal`), give it a single row with `First choice / Fallback / NEVER`. Adding a command to an *existing* group needs no new row. Exhaustive per-command detail belongs in `AGENT_CONTEXT` (`kbagent context`), which is loaded dynamically on demand -- `keboola-expert.md` is a static system prompt loaded into every subagent run and carries a hard 70 000 B budget, so it must stay a high-signal decision matrix, not a command catalogue. If a one-row addition would push the file over budget, trim stale content first; do **not** raise the cap. The ceiling is enforced by `tests/test_agent_prompt.py::TestPilotAgentFile::test_agent_prompt_under_token_budget`, which is the single source of truth -- check with `wc -c plugins/kbagent/agents/keboola-expert.md` before adding, and remember CI builds the merge commit, so a PR that is individually under budget can still fail once `main` has moved. *Severity note:* authors are expected to add the group row, but `/kbagent:review` flags a missing row only **NON-BLOCKING** -- `AGENT_CONTEXT` (a BLOCKING surface above) is the authoritative command catalogue, so a missing matrix row degrades subagent ergonomics without making a command undiscoverable. Don't deprioritize it just because it's non-blocking.
- [ ] **§3 Inline Gotchas** when behavior changed in a way the agent will get wrong by default (e.g. dev-branch auto-materialization, native column-type whitelisting).
- [ ] **`plugins/kbagent/skills/kbagent/SKILL.md`** non-table portions -- update the `description:` trigger keywords when introducing a new topic area (so description-matching auto-triggers the skill); add a workflow row to the bottom table if you created a new `references/<topic>-workflow.md`.
- [ ] **`plugins/kbagent/skills/kbagent/references/commands-reference.md`** -- add the new command bullet under the appropriate section. Hand-maintained, NOT auto-generated. (Yes, this partly duplicates the auto-generated SKILL.md table -- the reference carries denser per-command notes, the table is the at-a-glance picker.)
Expand Down Expand Up @@ -461,7 +461,7 @@ release checklist below.
| `CLAUDE.md` (`## All CLI Commands`) | Adding/removing/renaming commands | NO |
| `plugins/kbagent/.claude-plugin/plugin.json` | Every release (auto-synced) | YES (`make version-check`; pre-commit auto-stages) |
| `plugins/kbagent/.claude-plugin/CLAUDE.md` | Changing delegation strategy / when-to-delegate rules | NO |
| `plugins/kbagent/agents/keboola-expert.md` | New write/destructive command **group** (one matrix row per group, not per command -- file has a hard 60 KB prompt budget); new minimum-version requirement (Rule 6 VERSION GATE); behavior change (gotchas) | NO -- **highest silent-drift risk** |
| `plugins/kbagent/agents/keboola-expert.md` | New write/destructive command **group** (one matrix row per group, not per command -- file has a hard 70 000 B prompt budget); new minimum-version requirement (Rule 6 VERSION GATE); behavior change (gotchas) | NO -- **highest silent-drift risk** |
| `plugins/kbagent/commands/keboola.md` | `/keboola` slash-command UX change (rare) | NO |
| `plugins/kbagent/skills/kbagent/SKILL.md` -- table | Auto-generated by `make skill-gen` | YES (`make skill-check`; pre-commit auto-stages) |
| `plugins/kbagent/skills/kbagent/SKILL.md` -- description / rules / workflow links | New topic area in `description` triggers; new workflow file added to bottom table | NO |
Expand Down Expand Up @@ -592,7 +592,7 @@ manual safety net for the silent-drift risks summarized in the
4. **Run `make skill-gen`** -- regenerates the decision table in `SKILL.md`. Idempotent if no commands changed since the previous release.
5. **Manually review `plugins/kbagent/agents/keboola-expert.md`**:
- **§1 Rule 6 VERSION GATE examples** -- if any feature this release shipped (or any feature shipped in a previous release that you missed) was previously missing-and-now-present, document it with the right minimum version. Remove stale "since X.Y.Z" mentions that no longer matter to live users.
- **§2 Tool Selection Matrix** -- did you add a new write/destructive command *group* since last release? Is it present with one `First choice / Fallback / NEVER` row (per group, not per command)? Mind the hard 60 KB prompt budget: trim stale content rather than raising the cap. New commands inside an existing group need no new row.
- **§2 Tool Selection Matrix** -- did you add a new write/destructive command *group* since last release? Is it present with one `First choice / Fallback / NEVER` row (per group, not per command)? Mind the hard 70 000 B prompt budget: trim stale content rather than raising the cap. New commands inside an existing group need no new row.
- **§3 Inline Gotchas** -- new behavior the agent would get wrong by default? Add it.
6. **Manually review `plugins/kbagent/skills/kbagent/references/gotchas.md`** -- every behavior introduced or changed this release that an AI agent would not infer from `--help` should have its own `(since vX.Y.Z)` entry. The version tag is non-optional.
7. **Manually review `CLAUDE.md` `## All CLI Commands`** -- diff against `kbagent --help` output (and against `kbagent context`). Hand-maintained; CI does not catch drift here.
Expand Down
2 changes: 1 addition & 1 deletion docs/e2e-scenarios.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Resources are prefixed with `e2e-{timestamp}` and cleaned up via yield fixture e
| 9 | `storage create-table` | Table created with typed columns (INTEGER, STRING) and primary key |
| 10 | `storage upload-table` | 5-row CSV uploaded, `imported_rows: 5` |
| 11 | `storage upload-table --incremental` | 3 more rows appended, download verifies 8 total rows |
| 12 | `storage tables`, `storage table-detail` | Table in listing, column details match (id, name, value) |
| 12 | `storage tables`, `storage table-detail` | Table in listing, column details match (id, name, value), `definition` passed through (#621) |
| 13 | `storage download-table` | Full download: 8 rows, correct IDs. With `--columns`/`--limit`: subset verified |
| 14 | `storage unload-table --download` | Table exported to file storage, file_id > 0, file downloaded |
| 15 | `storage load-file` | CSV uploaded as file, then loaded into table via `load-file` |
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.87.0",
"version": "0.88.0",
"description": "AI-friendly interface to Keboola Connection projects — explore configs, jobs, lineage, sync configs as files, manage dev branches, and debug SQL in workspaces",
"author": {
"name": "Keboola",
Expand Down
2 changes: 1 addition & 1 deletion plugins/kbagent/agents/kbagent-pr-reviewer.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ verify the file IS updated in the diff. If not, flag it. Specifically:
|---|---|---|
| `src/keboola_agent_cli/commands/context.py` (`AGENT_CONTEXT`) | Does the new command appear under the right `### <Subgroup>` heading? | BLOCKING |
| `CLAUDE.md` `## All CLI Commands` | Does the new command's signature appear in the top-level command list? | BLOCKING |
| `plugins/kbagent/agents/keboola-expert.md` §2 Tool Selection Matrix | One row **per command GROUP**, not per command. If the PR adds a new write/destructive *group*, is there a `\| ... \| First choice \| Fallback \| NEVER \|` row? A new command inside an existing group needs no new row. The file has a hard 60 KB prompt budget; exhaustive per-command coverage lives in `AGENT_CONTEXT` (loaded dynamically), so a missing matrix row is **never BLOCKING** -- flag it NON-BLOCKING only when the new group has zero rows AND `AGENT_CONTEXT` also omits it. | NON-BLOCKING |
| `plugins/kbagent/agents/keboola-expert.md` §2 Tool Selection Matrix | One row **per command GROUP**, not per command. If the PR adds a new write/destructive *group*, is there a `\| ... \| First choice \| Fallback \| NEVER \|` row? A new command inside an existing group needs no new row. The file has a hard 70 000 B prompt budget (enforced by `tests/test_agent_prompt.py`); exhaustive per-command coverage lives in `AGENT_CONTEXT` (loaded dynamically), so a missing matrix row is **never BLOCKING** -- flag it NON-BLOCKING only when the new group has zero rows AND `AGENT_CONTEXT` also omits it. | NON-BLOCKING |
| `plugins/kbagent/agents/keboola-expert.md` §1 Rule 6 VERSION GATE | If feature is version-gated, are example version refs (`flow update needs 0.22.0+`) still accurate after this PR? | NON-BLOCKING (informational) |
| `plugins/kbagent/agents/keboola-expert.md` §3 Inline Gotchas | If behavior is non-obvious, is there a bullet describing it? | NON-BLOCKING |
| `plugins/kbagent/skills/kbagent/references/commands-reference.md` | New command bullet under correct section? | BLOCKING |
Expand Down
2 changes: 1 addition & 1 deletion plugins/kbagent/agents/keboola-expert.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ a critical failure.
| Create typed table with native types | `kbagent storage create-table --column pk:VARCHAR(40) --column amount:NUMBER(18,2) --not-null pk --default amount=0` (0.25.0+) | -- | re-creating via raw REST to `/v2/storage/...tables-definition` |
| Add one column to an existing table | `kbagent storage add-column --project P --table-id in.c-foo.data --column status:VARCHAR(20) [--not-null] [--default active]` (0.62.0+) -- synchronous Storage endpoint, same `name:TYPE(length)` grammar as `create-table`; the add-side mirror of `delete-column` | -- | re-creating the whole table just to add a field (loses data/PK/dependents); raw `POST /v2/storage/tables/.../columns` |
| Promote typed rebuild back into the original name | `kbagent storage swap-tables --project P --table-id in.c-foo.data --target-table-id in.c-foo.data_change_log --branch <ID> --yes` (0.28.0+) -- async storage job (`tableSwap`); client polls to completion. Service refuses without a branch; any branch incl. prod | -- | renaming or deleting + re-uploading (loses history; downstream configs need to be rewritten) |
| Repartition / recluster a populated BigQuery table | `kbagent storage create-table --project P --bucket-id in.c-main --name events_repart --source-table-id in.c-main.events --time-partitioning-type DAY --time-partitioning-field created_at --clustering-field tenant_id --primary-key id` (0.66.0+, BigQuery only) to copy the data into the new layout, then `kbagent storage swap-tables --table-id in.c-main.events --target-table-id in.c-main.events_repart --branch <ID> --yes` to flip it into place. `--source-table-id` derives the schema from the source so `--column` is forbidden (mutually exclusive); a non-BigQuery project fails fast (pre-flight backend check, exit 2). Range partitioning instead: `--range-partitioning-field/-start/-end/-interval` (all four; bounds are strings; mutually exclusive with time partitioning) | -- | raw `POST /v2/storage/buckets/.../tables-definition` with a `source` object, then manual swap; or a `CREATE TABLE ... AS SELECT` in a workspace (drops NOT NULL + primary key) |
| Repartition / recluster a populated BigQuery table | `kbagent storage create-table --project P --bucket-id in.c-main --name events_repart --source-table-id in.c-main.events --time-partitioning-type DAY --time-partitioning-field created_at --clustering-field tenant_id --primary-key id` (0.66.0+, BigQuery only) to copy the data into the new layout, then `kbagent storage swap-tables --table-id in.c-main.events --target-table-id in.c-main.events_repart --branch <ID> --yes` to flip it into place. `--source-table-id` derives the schema from the source so `--column` is forbidden (mutually exclusive); a non-BigQuery project fails fast (pre-flight backend check, exit 2). Range partitioning instead: `--range-partitioning-field/-start/-end/-interval` (all four; bounds are strings; mutually exclusive with time partitioning). VERIFY the swap with `storage table-detail --json` -> `.definition.timePartitioning` / `.clustering` (0.88.0+); `create-table` only echoes the layout you REQUESTED, so it proves nothing | -- | raw `POST /v2/storage/buckets/.../tables-definition` with a `source` object, then manual swap; or a `CREATE TABLE ... AS SELECT` in a workspace (drops NOT NULL + primary key) |
| Re-seed a table without losing its schema / PK / dependents | `kbagent storage truncate-table --project P --table-id in.c-foo.data [--branch ID] [--dry-run] [--yes]` (0.32.0+) -- DELETE `/tables/{id}/rows?allowTruncate=1`; endpoint is uniformly async on every branch (returns a queued `tableRowsDelete` job; client polls via `_wait_for_storage_job`). Do NOT pass `async=true` -- the API rejects it. Batch via repeated `--table-id`. Returns `{truncated[], failed[], dry_run, project_alias}` with `truncated[]` entries carrying `{table_id, rows_before, rows_after, branch_id}`. Permission class: `destructive` | -- | drop + recreate the table (loses descriptions, PK, sharing edges, and breaks every downstream config reference); deleting rows via raw SQL in a workspace (bypasses the Storage API audit trail) |
| Point-in-time backup of a table before a risky change | `kbagent storage snapshot-create --project P --table-id in.c-foo.data [--description D]` (0.75.0+, #512) -- async `tableSnapshotCreate` job; receipt carries `snapshot_id`. List with `storage snapshots --table-id ...`, inspect with `snapshot-detail --snapshot-id ID` (embeds the source table object) | -- | exporting to CSV as a "backup" (loses column types + PK); relying on the destructive command's `--dry-run` alone |
| Restore a snapshot as a NEW table | `kbagent storage table-from-snapshot --project P --snapshot-id ID --bucket-id in.c-foo --name restored [--dry-run]` (0.75.0+, #512) -- classic `tables-async` endpoint; restores data + columns + PK. `--name` REQUIRED (API rejects empty; PHP-client "defaults to snapshot name" docblock is stale). No overwrite: restore under a new name, verify, then `swap-tables`/`delete-table` to promote | -- | trying `create-table --snapshot-id` (not a thing -- `tables-definition` does not accept snapshots); restoring straight onto the production name (duplicate-name error) |
Expand Down
2 changes: 1 addition & 1 deletion plugins/kbagent/skills/kbagent/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ When working inside a git repository or project directory, run `kbagent init` (o
| List storage buckets with sharing/linked bucket information | `kbagent storage buckets` |
| Show detailed bucket info including backend-native direct access paths | `kbagent storage bucket-detail --project PROJECT --bucket-id BUCKET-ID` |
| List storage tables from one or more projects | `kbagent storage tables` |
| Show detailed table info including columns and types | `kbagent storage table-detail --project PROJECT --table-id TABLE-ID` |
| Show detailed table info including columns, types and physical layout | `kbagent storage table-detail --project PROJECT --table-id TABLE-ID` |
| Create a new storage bucket | `kbagent storage create-bucket --project PROJECT --stage STAGE --name NAME` |
| Create a new storage table with typed columns | `kbagent storage create-table --project PROJECT --bucket-id BUCKET-ID --name NAME` |
| Upload a CSV file into a storage table | `kbagent storage upload-table --project PROJECT --table-id TABLE-ID --file FILE` |
Expand Down
Loading