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
7 changes: 7 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,13 @@ kbagent component sync-action ACTION_NAME --component-id ID --project ALIAS (--c
# sends explicit configData verbatim (skips fetch); branchId omitted from body for production.
kbagent config examples --component-id ID [--project NAME] [--row]
kbagent config new --component-id ID [--name NAME] [--project NAME] [--output-dir DIR] [--push --no-files --description D --configuration JSON|@file|- --configuration-file PATH --no-validate --branch ID --dry-run --allow-plaintext-on-encrypt-failure]
# config new --push --output-dir (vNEXT, #644): the written scaffold now records the created
# config's ID (_keboola.config_id, quoted so numeric IDs stay strings) and is placed in the
# subtree of the branch the config was ACTUALLY created in (--branch or active branch;
# unregistered branches are added to the manifest like `sync pull --branch` would). Before,
# the scaffold had no ID and always landed in the default branch tree, so the next
# `sync push` created a DUPLICATE (34-config incident). With --configuration, the local file
# mirrors the pushed encrypted body -- placeholders would overwrite the remote on next push.

# sync: GitOps -- configs as local files. init/pull/push/diff are filesystem-local (no serve REST surface).
kbagent sync init --project ALIAS [--directory DIR] [--git-branching] [--adopt-existing]
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 @@ -129,7 +129,7 @@ been retired, so its absence is NOT a promise (see §1 Rule 6).
| Inspect dev branch | `kbagent branch list --project P`, `kbagent branch use --project P --branch ID` | -- | acting on `main` when a dev branch exists |
| Audit project capabilities / features | `kbagent project info --project P` -- project id, name, backend, enabled features, quota limits, metrics | -- | inspecting the UI project settings manually |
| Manage feature flags (stack / project / user) | `kbagent feature list\|project-show\|project-add\|project-remove\|user-show\|user-add\|user-remove --project P [--email E] [--feature NAME] [--dry-run]` -- Manage API, needs a SUPER-ADMIN token (interactive prompt; `--allow-env-manage-token` for CI) | `kbagent project info` for a project's *enabled* features (read-only, no super-admin) | raw `/manage/...` calls; a manage token passed as a CLI flag |
| Create a new config (one-shot remote, no scaffold to disk) | `kbagent config new --project P --component-id C --name N --push --no-files [--configuration @body.json]` -- default body `{}` skips validation; an explicit body is schema-validated (`--no-validate` opts out); works for every component type | `kbagent config new --output-dir D` then edit + `kbagent sync push` | raw `POST /v2/storage/components/.../configs` (no schema validation, no encryption) |
| Create a new config (one-shot remote, no scaffold to disk) | `kbagent config new --project P --component-id C --name N --push --no-files [--configuration @body.json]` -- default body `{}` skips validation; an explicit body is schema-validated (`--no-validate` opts out); works for every component type. `--output-dir` + `--push` together is safe only on vNEXT+ (scaffold records `_keboola.config_id`, lands in the created branch's subtree); older kbagent writes an ID-less scaffold that the next `sync push` DUPLICATES (issue #644) -- there, scaffold and push in two steps | `kbagent config new --output-dir D` then edit + `kbagent sync push` | raw `POST /v2/storage/components/.../configs` (no schema validation, no encryption) |
| Create / update / delete a config row | `kbagent config row-create\|row-update\|row-delete --project P --component-id C --config-id K [--row-id R] [--yes]` -- `row-delete` is destructive; all three are branch-aware | -- | raw REST against `/configs/K/rows` |
| Read or write a config's runtime state | `kbagent config state-get` / `config state-set --state JSON` (0.84.2+) -- the dedicated state endpoint | -- | `config update --set 'state...'` (hard error since 0.84.2; before that it silently wrote `configuration.state.*` and left runtime state untouched) |
| Get OAuth authorization URL | `kbagent config oauth-url --project P --component-id C --config-id K` | -- | raw `GET .../oauth/authorize` |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ Requires a **super-admin** Manage API token (same kind as `org setup`). Same def
- `config delete --project NAME --component-id ID --config-id ID [--branch ID] [--dry-run]` -- SOFT-delete a configuration into the Storage trash (restorable). Since 0.89.0 it locates the config first and a config already in the trash is NOT deleted again -- the raw API purges permanently on a second DELETE (the retry-after-timeout trap); kbagent reports `already_in_trash` and exits 0 instead. `--dry-run` reports the located state without writing. Undo with `config restore`. Permission class `destructive`
- `config restore --project NAME --component-id ID --config-id ID [--branch ID]` *(since v0.89.0)* -- restore a trashed configuration (versions, rows and metadata come back). Only works on a config currently in the trash. Permission class `write`
- `config trash-list --project NAME [--component-id ID] [--branch ID]` *(since v0.89.0)* -- list configurations in the trash; each row carries `component_id`, `config_id`, `name`, `version` and `deleted_at`, which is exactly what `config restore` needs. Permission class `read`
- `config new --component-id ID [--project NAME] [--name NAME] [--output-dir DIR] [--push --no-files --description D --configuration JSON|@file|- --configuration-file PATH --no-validate --branch ID --dry-run --allow-plaintext-on-encrypt-failure]` -- **two modes**. **Default (no `--push`)**: scaffold new config from component schema; writes files to `--output-dir` or prints to stdout. **Zero API calls.** **With `--push`** (0.33.0+, requires `--project` + non-empty `--name`): also POSTs to `/v2/storage/components/{cid}/configs` for a one-shot remote create. `#`-prefixed secrets in the pushed body auto-encrypt via the Encryption API first (fail-closed; since 0.54.0, #378; `--allow-plaintext-on-encrypt-failure` overrides). `--no-files` skips the filesystem step entirely (FIIA-style empty-shell pattern). `--configuration` / `--configuration-file` override the POSTed body (default is `{}`, with validation auto-skipped for the default empty shell). `--dry-run` previews the planned POST + validation result without creating. Schema validation runs by default when an explicit body is given (fail-closed: `ConfigError` exit 5 on mismatch) but skips silently if the AI Service has no schema for the component or returns an error; `--no-validate` opts out. Works for ALL component types including `keboola.snowflake-transformation`.
- `config new --component-id ID [--project NAME] [--name NAME] [--output-dir DIR] [--push --no-files --description D --configuration JSON|@file|- --configuration-file PATH --no-validate --branch ID --dry-run --allow-plaintext-on-encrypt-failure]` -- **two modes**. **Default (no `--push`)**: scaffold new config from component schema; writes files to `--output-dir` or prints to stdout. **Zero API calls.** **With `--push`** (0.33.0+, requires `--project` + non-empty `--name`): also POSTs to `/v2/storage/components/{cid}/configs` for a one-shot remote create. `#`-prefixed secrets in the pushed body auto-encrypt via the Encryption API first (fail-closed; since 0.54.0, #378; `--allow-plaintext-on-encrypt-failure` overrides). `--no-files` skips the filesystem step entirely (FIIA-style empty-shell pattern). `--configuration` / `--configuration-file` override the POSTed body (default is `{}`, with validation auto-skipped for the default empty shell). `--dry-run` previews the planned POST + validation result without creating. Since vNEXT, `--push` + `--output-dir` writes the scaffold WITH `_keboola.config_id` into the subtree of the branch the config was created in (registering the branch in the manifest when missing), so the next `sync push` adopts it instead of duplicating (issue #644); with an explicit body the local file mirrors the pushed encrypted configuration. Schema validation runs by default when an explicit body is given (fail-closed: `ConfigError` exit 5 on mismatch) but skips silently if the AI Service has no schema for the component or returns an error; `--no-validate` opts out. Works for ALL component types including `keboola.snowflake-transformation`.
- `config clone --project P --component-id ID --config-id ID --name NAME [--target-project P2] [--description D] [--set PATH=VALUE ...] [--secret PATH=VALUE ...] [--branch ID] [--target-branch ID] [--dry-run] [--allow-plaintext-on-encrypt-failure]` (0.84.2+, #587) -- duplicate a configuration **whole**. Reach for this instead of reading `config detail` and rebuilding a body: copying only `configuration["parameters"]` silently drops its siblings (`runtime`, `storage`, `authorization`), and a lost `runtime.parallelism` makes Keboola fall back to `parallelism: 1` -- the reporter's 65-row writer went sequential, 140 min instead of ~60-90, with nothing in any output pointing at it. **Same project** (default): server-side copy via `POST .../configs/{id}/versions/{v}/create`; rows and `KBC::` encrypted values travel with it (verified live). `--set PATH=VALUE` is applied as a follow-up update on the copy, so an override can never be the reason a key went missing. **Cross project** (`--target-project`): reassembled client-side and rows recreated one by one, because encrypted values **cannot** travel -- a Keboola ciphertext is scoped to the project it was encrypted in. Any `KBC::` value makes the clone **fail with exit 5**, listing every path, until re-supplied via `--secret PATH=VALUE` (encrypted in the TARGET project on write). `--dry-run` reports those paths instead of refusing -- run it first to learn what to gather. Storage bucket/table IDs are copied **verbatim, never remapped**; `sync clone` is the command that remaps.
- `config variables-set --project NAME --component-id ID --config-id ID --var KEY=VALUE [--var ...] [--replace] [--variables-id ID] [--values-id ID] [--branch ID] [--dry-run] [--allow-plaintext-on-encrypt-failure] [--yes]` -- attach variable values to a config. Auto-creates a sibling `keboola.variables` config + default row on first use and links it via the parent's `runtime.variables_id` / `variables_values_id`. Defaults to merge; `--replace` drops keys not in `--var`. `#`-prefixed values encrypt via the Encryption API (fail-closed; exit non-zero on `ENCRYPTION_FAILED`). See `variables-workflow.md`
- `config variables-get --project NAME --component-id ID --config-id ID [--branch ID]` -- resolve `variables_id` + `values_id` from the parent config and fetch the current KEY=VALUE map. Returns `{linked: bool, variables_id, values_id, values}`; `linked=false` means the parent has no variables attached
Expand Down
29 changes: 29 additions & 0 deletions plugins/kbagent/skills/kbagent/references/gotchas.md
Original file line number Diff line number Diff line change
Expand Up @@ -1152,6 +1152,35 @@ events and emits a final `done` SSE frame mirroring the same record.
/openapi.json` returns the full schema, which lets the AI pick the
right route + body shape without hard-coded knowledge.

## `config new --push --output-dir` scaffolds carry the created config's ID and land in the right branch subtree (since vNEXT)

- **Before vNEXT this combo was a duplicate factory** (issue #644): the
scaffold was written WITHOUT `_keboola.config_id` (the "assigned on first
push" comment was wrong on this path -- the config already existed), and it
always landed in the DEFAULT branch's tree even when the config was created
in a dev branch (`--branch`, or the active branch set by `branch create` /
`branch use`). The next `sync push` then classified the directory as a new
config and POSTed a duplicate -- a real incident created 34 duplicates.
- **Since vNEXT** the written `_config.yml` records `_keboola.config_id`
(double-quoted, so legacy numeric IDs stay YAML strings) and the files are
written into the subtree of the branch the config was ACTUALLY created in;
an unregistered branch is added to `manifest.branches` exactly like
`sync pull --branch` would. If that registration fails, files fall back to
`branch-{id}/` -- never to the default tree -- with a warning in
`warnings[]`.
- The follow-up `sync diff` reports the new config as `modified` (placeholder
scaffold vs the pushed empty body) -- that is the expected "now edit and
push" state, NOT drift. With an explicit `--configuration` body the local
directory mirrors the pushed (already encrypted) body instead, materialized
exactly like `sync pull` would (a transformation body's `parameters.blocks`
become a REAL `transform.sql`/`transform.py`, a non-empty description
becomes `_description.md`) -- so the diff is clean and a later push cannot
regress the remote to TODO placeholders.
- `--json` gains an additive `local_scaffold: {directory, files, config_id}`
key on this path.
- Agents on kbagent < vNEXT: do NOT recommend `--output-dir` + `--push`
together; use scaffold-only then `sync push`, or `--push --no-files`.

## `kbagent config new --push` is one-shot remote create; default is scaffold-only (since v0.33.0)

- **Pre-v0.33.0**, `kbagent config new` was scaffold-only -- it wrote
Expand Down
11 changes: 10 additions & 1 deletion plugins/kbagent/skills/kbagent/references/scaffold-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ kbagent --json config new --component-id COMPONENT_ID --project ALIAS --name "Co
--push --no-files

# Scaffold AND remote create in one step (writes files AND POSTs)
# Since vNEXT the written scaffold records the created config's ID
# (_keboola.config_id) and lands in the subtree of the branch the config was
# created in -- the next `sync push` ADOPTS the config (reported as
# `modified` until you edit + push) instead of creating a duplicate.
# On older versions this combo wrote an ID-less scaffold: the next
# `sync push` DUPLICATED the config (issue #644). There, use the two-step
# path (scaffold without --push, edit, `sync push`) instead.
kbagent config new --component-id COMPONENT_ID --project ALIAS --name "Config Name" \
--output-dir . --push
```
Expand Down Expand Up @@ -144,7 +151,9 @@ Rules:

- `_config.yml` format follows the kbc CLI dev-friendly YAML structure
- The `_keboola.component_id` field in `_config.yml` is required for push to work
- `config_id` is assigned by Keboola on first push -- don't set it manually
- `config_id` is assigned by Keboola on first push -- don't set it manually.
Exception: `config new --push --output-dir` (vNEXT+) writes it itself,
because on that path the config already exists remotely
- Secret fields use Keboola convention: any key starting with `#` is a secret
- Scaffold marks secret placeholders with `# encrypted by Keboola on push` comments
- Encrypted values look like `KBC::ProjectSecure::...`
Expand Down
Loading