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
17 changes: 14 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,21 @@ uv run pytest tests/test_cli.py::TestProjectAdd::test_project_add_success_json -

```
src/keboola_agent_cli/
__init__.py # exposes __version__ (read at runtime via importlib.metadata; never hardcoded)
__init__.py # PUBLIC SDK ENTRYPOINT + __version__. Re-exports the importable
# facade (Client, Files, FileEntry) and the typed result models
# (JobResult, QueryResult, UploadTableResult, SyncPushResult,
# ConfigDetailResult, CloneResult, JobIdempotencyStore). Everything
# in __all__ is committed public API (semver). __version__ is read
# at runtime via importlib.metadata; never hardcoded. See docs/sdk.md.
lib.py # In-process SDK facade: Client (query/run_job/config_detail/
# upload_table + .files + .raw) over KeboolaClient. Stateless,
# config-dir-free, token passed at construction (issue #415). See docs/sdk.md.
result_models.py # Typed pydantic return contracts for the SDK facade (extra="allow",
# populate_by_name); the semver-stable shapes consumers type against (issue #428).
py.typed # PEP 561 marker -- ships in the wheel so downstream mypy/ty treat the SDK as typed.
__main__.py # python -m support
cli.py # Typer root app, global options, subcommand wiring
constants.py # Shared constants (retry params, timeouts, exit codes, defaults)
constants.py # Shared constants + dynamic APP_NAME resolution (retry params, timeouts, defaults)
json_utils.py # Deep-merge, set_nested_value, compute_diff utilities
models.py # Pydantic models shared across layers
output.py # OutputFormatter: JSON vs Rich dual-mode output
Expand Down Expand Up @@ -135,7 +146,7 @@ Seven clients, all inheriting `BaseHttpClient` (`http_base.py`) which provides s

**Single source of truth: `pyproject.toml`** (`version = "X.Y.Z"`).

- `src/keboola_agent_cli/__init__.py` reads the version at runtime via `importlib.metadata.version(APP_NAME)` (where `APP_NAME = "keboola-cli"`). **Never hardcode a version string in `__init__.py`.**
- `src/keboola_agent_cli/__init__.py` reads the version at runtime via `importlib.metadata.version(APP_NAME)`. `APP_NAME` is resolved **dynamically** in `constants.py` (`_resolve_app_name()`: prefers the current distribution `keboola-cli`, falls back to the legacy `keboola-agent-cli` so the #424 migration-bridge wheel keeps working) -- it is **not** a fixed literal. **Never hardcode a version string in `__init__.py`.**
- `plugins/kbagent/.claude-plugin/plugin.json` must match. Run `make version-sync` (or `python scripts/sync_version.py`) to update it.
- The pre-commit hook and CI automatically check version consistency.

Expand Down
38 changes: 37 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Read this **before** writing code. It will save review rounds.

### Python conventions

- **Python 3.11+** -- use modern syntax (`str | None`, not `Optional[str]`)
- **Python 3.12+** (`pyproject.toml` pins `requires-python = ">=3.12"`) -- use modern syntax (`str | None`, not `Optional[str]`)
- **Type hints** on all function signatures
- **f-strings** for string formatting (no `.format()` or `%`)
- **`pathlib.Path`** over `os.path` -- consistently used throughout the project
Expand Down Expand Up @@ -392,6 +392,42 @@ before the PR is mergeable.
- [ ] Destructive operations have `--dry-run` and `--yes` flags
- [ ] Write operations log what they did (created X, uploaded Y rows)

## Extending the importable SDK

Besides the CLI, kbagent ships an **in-process Python SDK** -- the importable
`Client` facade (`lib.py`) and its typed result models (`result_models.py`),
re-exported from the package root. A Keboola Data App, a transformation, or any
Python service can `from keboola_agent_cli import Client` and use Query Service
SQL, Storage Files, run-job, and config detail **without** a CLI subprocess or a
`kbagent serve` daemon.

**Everything exported from `keboola_agent_cli.__all__` is committed public API
under semver.** Changing it is a deliberate act, not a side effect of touching a
service. The full architecture, method reference, and the step-by-step checklist
for adding a facade method or a result model live in **[docs/sdk.md](docs/sdk.md)**
(see "Extending the SDK"). The short version:

- [ ] **Facade methods go in `lib.py`** and call `KeboolaClient` directly --
never import the service layer (it carries config-dir / orchestration
assumptions the stateless facade must not inherit). Re-assemble the
high-level shape yourself, and **state in the docstring** which service
conveniences you intentionally omit (auto-create, alias/variable resolution).
- [ ] **Return a typed model** (`result_models.py`), not a bare dict, for any
non-trivial shape. Subclass `_ApiResultModel` (`extra="allow"` +
`populate_by_name`); type **only** the stable subset, alias raw API keys via
`AliasChoices`, and never `extra="forbid"` (the API grows fields and the
contract must not raise).
- [ ] **Export it** from `__init__.py` + `__all__` -- that list *is* the public
surface. Treat a field rename or a type tightening as a **breaking change**;
prefer adding over mutating.
- [ ] **`make typecheck` stays clean** (types are a user-facing promise here),
add facade/model tests in `tests/`, and **document the addition in
[docs/sdk.md](docs/sdk.md)** (and the README "Use as a library" one-liner if
it's a headline capability).

A runnable teaching example -- a curses Storage browser built entirely on the
SDK -- lives in [`examples/storage_tui/`](examples/storage_tui/).

## Plugin synchronization map

Single-glance reference for "I changed the CLI -- what else must follow?".
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,9 @@ with Client(url=os.environ["KBC_URL"], token=os.environ["KBC_TOKEN"]) as kbc:
files = kbc.files.list(tags=["demo"]) # list[FileEntry]
```

`query()` reads results inline (fast, native JSON types) and returns rows keyed by column name; `files` returns a uniform `FileEntry` shape and reads bytes straight into memory. Everything exported from `keboola_agent_cli` is committed public API (semver). For lower-level endpoints, reach for `Client.raw` (the underlying `KeboolaClient`).
`query()` reads results inline and returns rows keyed by column name; `files` returns a uniform `FileEntry` shape and reads bytes straight into memory; `run_job()` / `config_detail()` / `upload_table()` return typed pydantic models. Everything exported from `keboola_agent_cli` is committed public API (semver). For lower-level endpoints, reach for `Client.raw` (the underlying `KeboolaClient`).

**Full SDK reference:** [docs/sdk.md](docs/sdk.md) -- the deep guide (every method, the typed result-model contract, `py.typed`, idempotent `run_job`, gotchas, and how to extend the SDK). **Runnable demo:** [`examples/storage_tui/`](examples/storage_tui/) -- a terminal Storage browser built entirely on this `Client`.

## 30-second demo

Expand Down Expand Up @@ -259,6 +261,8 @@ kbagent init | context | doctor | version | update | changelog
|-------|---------------|
| [Tutorial](docs/TUTORIAL.md) | End-to-end walkthrough: register projects (1, N, whole org), global vs local config, plugin install, using the specialist subagent and `/keboola` slash command. |
| [User Guide](docs/guide.md) | Configuration, permissions, per-directory isolation, workflows |
| [Python SDK](docs/sdk.md) | The in-process importable `Client`: method reference, typed result models, `py.typed`, idempotent jobs, gotchas, and how to extend the SDK. Demo: [`examples/storage_tui/`](examples/storage_tui/). |
| [Build a REST client](docs/build-your-own-client.md) | The `kbagent serve` HTTP API spec for non-Python callers (JS, Go, Slack bots, Web UIs). |
| [Contributing](CONTRIBUTING.md) | Architecture, coding style, adding commands, testing checklist |

## Development
Expand Down
Loading