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
25 changes: 24 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ src/keboola_agent_cli/
models.py # Pydantic models shared across layers
effective_branch.py # resolve_branch(): the ONLY code that applies the `branch use` active branch;
# records project + branch for the `Target:` line / `targets` key (#766)
project_ref.py # resolve_project_ref(): a project ID given where an alias is expected -> the
# alias (CLI-22); applied by commands/_project_ref.py and serve's dependency
output.py # OutputFormatter: JSON vs Rich dual-mode output
errors.py # KeboolaApiError, ConfigError, ErrorCode enum, mask_token()
config_store.py # JSON persistence for config.json (0600 permissions)
Expand Down Expand Up @@ -303,6 +305,20 @@ Full author checklist: see `CONTRIBUTING.md` > "Releasing a beta (pre-release) v
`tests/test_effective_branch.py` fails on a new direct read. A read that
only shows or manages the active branch must be on its list, with a reason.

20. **Services receive project aliases, never project IDs.** `--project`
(and `project use`, `KBAGENT_PROJECT`, serve's `{project}` / `?project=`)
also takes a project ID; the root command group
(`commands/_project_ref.py`) translates it to the alias before the command
runs. A new command gets this without extra code. A new command whose
`--project` is not a registry lookup (a NEW alias like `project add`, an
offline filter like `lineage show`) must be added to `NO_LOOKUP_COMMANDS`
there, and an option with another name that takes an existing alias (like
`config clone --target-project`) to `ALIAS_OPTIONS`.
`tests/test_project_ref.py` fails on a new option whose flag contains
`project`, `alias` or `stack` until it is in `ALIAS_OPTIONS` or in the
test's `NOT_AN_ALIAS` list; an option with any other name (like
`sync clone --target`) needs that decision by hand.

## Claude Code Plugin

The plugin lives here in `plugins/kbagent/` and is **published through `keboola/ai-kit`**. It exposes: a CLI (`kbagent`), three skills (`kbagent`, `kbagent-cicd-migration`, `kbagent-promotion-pipeline`), three slash commands (`/kbagent:setup`, `/keboola`, `/kbagent:review`), and two specialist subagents (`keboola-expert`, `kbagent-pr-reviewer`). All are namespaced under `kbagent:`. `/kbagent:setup` is the documented one-command first-run path (install CLI -> connect project -> `doctor`); it runs in the main context and spawns no subagent.
Expand Down Expand Up @@ -367,6 +383,13 @@ plugins/kbagent/
# settings.json -> env). Neither set = header omitted, as before. Version gate for this entry
# lives in gotchas.md -- a `(since vNEXT)` tag cannot be written on these `# ` comment lines,
# because check_version_gates.py parses them as ATX markdown headings (where a `vNEXT` is fatal).
# --project (CLI-22) takes a registered alias OR a project ID. An alias wins; an ID registered under
# several aliases is CONFIG_ERROR (exit 5) listing them, unless all are on one stack and exactly one is a
# session alias. Same for KBAGENT_PROJECT, `project use`, `config clone --target-project`,
# `sync clone --target`, the `semantic-layer promote/diff` project options, `auth * --stack`, and
# serve's {project} / ?project= / ?stack= (path and query only, not request bodies). Not for
# `project add` / `project create` (new alias) or `lineage show` (offline filter). Version gate in
# gotchas.md.
# Headless / token-only (0.50.0+): export KBAGENT_PROJECT_FROM_ENV=1 + KBC_TOKEN + KBC_STORAGE_API_URL to synthesize an in-memory `__env__` project (no `project add`, no config.json on disk; token never persisted). Use `--project __env__`. Same env setup also powers `kbagent serve`.

kbagent auth login [--stack URL|alias] [--device-code] [--register-projects]
Expand Down Expand Up @@ -418,7 +441,7 @@ kbagent auth register-projects [--stack URL|alias] [--all] [--project-id ID ...]
# AUTH_BROWSER_UNAVAILABLE, AUTH_STATE_MISMATCH, SESSION_EXPIRED, SESSION_NOT_FOUND.
# `auth register-projects` (0.80.0+): fixes the usability gap where nothing was registered unless
# --register-projects was passed at login, and where the alias offered was a slug of the project
# NAME (never the numeric id, so `--project 9840` never resolves). Lists every project the session
# NAME (never the numeric id; the id itself resolves as `--project` once registered, CLI-22). Lists every project the session
# can access with a collision-free suggested alias, then lets the caller pick which to register.
# --all selects every candidate; --project-id ID (repeatable) selects specific ones (unknown id ->
# ConfigError); omitting both runs an interactive arrow-key + spacebar checkbox picker (every
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,7 @@ When adding a new command (e.g., `kbagent storage create-foo`), you must update
- [ ] **Command function** in `commands/` -- Typer options, formatter, error handling
- [ ] **Permission registration** in `permissions.py` (`OPERATION_REGISTRY` dict)
- [ ] **Branch choice** through `resolve_branch()` in `effective_branch.py` when the command takes `--branch` or uses the active branch -- never read `ProjectConfig.active_branch_id` directly. The function reports the branch (`Target:` line, `targets` in `--json`); `tests/test_effective_branch.py` fails on a new direct read.
- [ ] **`--project` naming a NEW alias** -- every `--project` option also takes a project ID, translated to the alias before the command runs (`commands/_project_ref.py`, CLI-22). A command whose `--project` is not a registry lookup (a new alias like `project add`, an offline filter like `lineage show`) must be added to `NO_LOOKUP_COMMANDS` there, and an option with another name that takes an existing alias (like `config clone --target-project`) to `ALIAS_OPTIONS`. `tests/test_project_ref.py` fails on a new option whose flag contains `project`, `alias` or `stack` until it is in `ALIAS_OPTIONS` or in the test's `NOT_AN_ALIAS` list (with a reason); an option with any other name needs this decision by hand.
- [ ] **Service wiring** in `cli.py` if adding a new service class
- [ ] **HTTP API endpoint** in `src/keboola_agent_cli/server/routers/<group>.py` -- `kbagent serve` exposes the CLI as a REST API so external applications (Web UI, scheduled AI agents, Slack bots, Streamlit dashboards, CI pipelines) can call the platform without forking CLI subprocesses. The current convention is **1:1**: every command in a group has a matching endpoint in that group's router (e.g. `commands/flow.py` has 8 commands, `server/routers/flows.py` has 8 routes). If you add a new command, add the corresponding route. **Skip allowed** only for genuinely terminal-only commands (interactive prompts, Rich-rendered output that has no useful JSON shape, `doctor`/`init`/`update`-style infrastructure that manages kbagent itself rather than Keboola). Document any skip in the PR description with a one-line reason so reviewers don't flag it.

Expand Down
22 changes: 18 additions & 4 deletions plugins/kbagent/agents/keboola-expert.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,20 @@ its absence is NOT a promise the entry is version-independent (see §1 Rule 6).
or run `project use`. On <= 0.90.1 the same commands silently used the FIRST
registered project and ignored the pin (issue #684). gotchas.md.

**`--project` given a numeric project ID (vNEXT+)**
- `--project`, `KBAGENT_PROJECT`, `project use`, the other alias options
(`config clone --target-project`, `sync clone --target`, `semantic-layer
promote` / `diff` project options, `auth * --stack`) and serve `{project}`
/ `?project=` (path and query only, never a request body) take a
registered project's ID and use its alias. A registered
alias wins over an ID. An ID registered under several aliases is exit 5
(`CONFIG_ERROR`) listing them -- pick one; only a lone session alias on one
stack wins by itself. Output names the alias. A digits-only alias that is
also another project's ID wins, with a stderr warning naming that project
(also under `--json`). Below vNEXT an ID is "not found". `project add` /
`project create` treat the value as a NEW alias; `lineage show --project`
is an offline filter, not translated. gotchas.md (CLI-22).

**Which branch did a command use? (vNEXT+)**
- Every command that picks a branch names it: `Target: project 'P', branch ID
(from 'kbagent branch use')` on stderr, `targets` in `--json`
Expand Down Expand Up @@ -417,10 +431,10 @@ its absence is NOT a promise the entry is version-independent (see §1 Rule 6).
Confirming **revokes the session it gave you** -- that is the design, not
a failure: follow it with `auth login --stack URL`, and note the alias
survives (the sentinel keys on project id + stack, never the session).
- **Aliases derive from the project NAME, never the numeric id** --
`--project 9840` never resolves. Use `kbagent project list` or
`auth register-projects` to find/register the real alias; it never overwrites
an existing registration.
- **Aliases derive from the project NAME, never the numeric id.**
`--project 9840` resolves only once project 9840 is registered (vNEXT+;
never below). Use `kbagent project list` or `auth register-projects` to
find/register the real alias; it never overwrites an existing registration.
- **Session auth covers almost every command** -- only three features still
need a static token: `kbagent kai`, `semantic-layer token --encrypt`, and the
importable SDK (`keboola_agent_cli.Client`). **Do NOT reconstruct that list
Expand Down
Loading
Loading