diff --git a/python_sdk/.claude/skills/run-vizql-data-service-py/SKILL.md b/python_sdk/.claude/skills/run-vizql-data-service-py/SKILL.md new file mode 100644 index 0000000..9a05a63 --- /dev/null +++ b/python_sdk/.claude/skills/run-vizql-data-service-py/SKILL.md @@ -0,0 +1,237 @@ +--- +name: run-vizql-data-service-py +description: Run, build, generate, lint, test, and screenshot dev tasks for the VizQL Data Service Python SDK. Use when the user asks to build the sdk, regenerate the OpenAPI stubs, run pytest, run black/isort/flake8/mypy, check the pyproject/schema version, or run the examples.py CLI. +--- + +# run-vizql-data-service-py + +Dev driver for the VizQL Data Service Python SDK. Wraps the six tasks documented across `README.md`, `CONTRIBUTING.md`, and `scripts/*` behind a pair of entry points — `driver.ps1` for Windows (Git Bash delegates the `.sh` scripts, Python tools run inline) and `driver.sh` for macOS/Linux (the `.sh` scripts run natively). Both drivers accept identical subcommands. + +All paths in this file are relative to `python_sdk/` (the SDK root — same directory as `pyproject.toml`). + +## Prerequisites + +- Python 3.9+ (verified against 3.10.11). +- pip 20+ (verified against 26.2.1). +- **Windows:** Git for Windows / Git Bash on PATH — needed to run `scripts/*.sh` unchanged. If bash is not on PATH, `driver.ps1` falls back to `C:\Program Files\Git\bin\bash.exe` and `C:\Program Files\Git\usr\bin\bash.exe`. Windows PowerShell 5.1 is fine; `pwsh` (PowerShell 7) is not required. +- **macOS / Linux:** any recent Bash (3.2+). `driver.sh` calls `scripts/*.sh` and the Python tools directly. + +## Which driver to use + +Two drivers ship with this skill; they expose the **same subcommands** with **the same behavior** and target the same repo layout. Pick by platform: + +- **Windows** → `driver.ps1` (see "Run (agent path — Windows)" below). +- **macOS / Linux** → `driver.sh` (see "Run (agent path — macOS / Linux)" below). + +The rest of this file — subcommand semantics, examples-CLI defaults, Gotchas, Troubleshooting — applies to both. Only the launch command differs. + +## Run (agent path — Windows) + +**Always pass an absolute path** to `powershell -File` — Windows PowerShell resolves `-File` against the *child* process's cwd, not the parent's, so a literal relative path (e.g. `.\driver.ps1`) breaks when the parent shell isn't sitting in `python_sdk/`. Do NOT hardcode a drive letter — resolve the path at runtime from the git repo root so the skill works no matter where the user cloned or launched from: + +```powershell +$D = Join-Path (git rev-parse --show-toplevel) 'python_sdk\.claude\skills\run-vizql-data-service-py\driver.ps1' +``` + +`git rev-parse --show-toplevel` returns the repo root regardless of the cwd (as long as it's *somewhere* inside the repo — Claude may be launched from `python_sdk/`, `python_sdk/src/examples/`, or the repo root). The result is an absolute path, which is what `powershell -File` needs. + +One-time setup (installs the package editable with the dev extras — `black`, `isort`, `flake8`, `mypy`, `pytest`, `pytest-asyncio`, `pytest-cov`, plus `datamodel-code-generator` used by `gen`): + +```powershell +powershell -NoProfile -File $D setup +``` + +Everyday subcommands: + +```powershell +powershell -NoProfile -File $D help # list subcommands +powershell -NoProfile -File $D check-version # pyproject <-> OpenAPI schema version match +powershell -NoProfile -File $D gen # regenerate src/api/openapi_generated.py from schema +powershell -NoProfile -File $D build # clean + build sdist + wheel into dist/ +powershell -NoProfile -File $D format # black + isort (write mode) +powershell -NoProfile -File $D lint # black --check, isort --check-only, flake8, mypy +powershell -NoProfile -File $D test # pytest tests -q --disable-warnings +powershell -NoProfile -File $D examples-help # examples.py --help (no server needed) +powershell -NoProfile -File $D all # gen -> check-version -> build -> lint -> test (matches CI) +``` + +Standard local pre-commit loop: + +```powershell +powershell -NoProfile -File $D format +powershell -NoProfile -File $D lint +powershell -NoProfile -File $D test +``` + +Full CI parity in one shot (regenerates the client, checks versions, builds artifacts, runs linters, runs tests): + +```powershell +powershell -NoProfile -File $D all +``` + +## Run (agent path — macOS / Linux) + +`driver.sh` accepts the same subcommands as `driver.ps1` and can be invoked from any cwd (it resolves the repo root from its own location). Resolve the path at runtime so the skill isn't tied to any specific clone location: + +```bash +D="$(git rev-parse --show-toplevel)/python_sdk/.claude/skills/run-vizql-data-service-py/driver.sh" +``` + +Everyday subcommands: + +```bash +bash "$D" help # list subcommands +bash "$D" setup # pip install -e .[dev] (one-time) +bash "$D" check-version # pyproject <-> OpenAPI schema version match +bash "$D" gen # regenerate src/api/openapi_generated.py from schema +bash "$D" build # clean + build sdist + wheel into dist/ +bash "$D" format # black + isort (write mode) +bash "$D" lint # black --check, isort --check-only, flake8, mypy +bash "$D" test # pytest tests -q --disable-warnings +bash "$D" examples-help # examples.py --help (no server needed) +bash "$D" all # gen -> check-version -> build -> lint -> test (matches CI) +``` + +Standard local pre-commit loop and full CI parity mirror the Windows path: + +```bash +bash "$D" format && bash "$D" lint && bash "$D" test +bash "$D" all +``` + +## Run (human / direct-script path) + +The `.sh` scripts are the source of truth for `gen`/`build`/`check-version` and can be run directly (Git Bash on Windows, or a native shell on macOS/Linux) from `python_sdk/`: + +```bash +bash scripts/check_version.sh +bash scripts/generate_stub.sh # runs before build; overwrites src/api/openapi_generated.py +bash scripts/build.sh # produces dist/vizql_data_service_py-*.{whl,tar.gz} +bash scripts/test.sh # HEAVYWEIGHT: destroys and recreates ./venv, then runs lint + pytest --cov +``` + +`scripts/test.sh` is intended for CI and **rebuilds the entire virtual environment** every run. For local iteration, use `driver.ps1 lint` / `driver.ps1 test` on Windows or `driver.sh lint` / `driver.sh test` on macOS/Linux instead — they call the same tools against the current interpreter. + +## Run the examples CLI + +`src/examples/examples.py` needs a live Tableau server plus one auth method. It cannot be smoke-tested against arbitrary hosts, but the driver's `examples` subcommand fills in **local-dev defaults** so a bare `examples` invocation works against a laptop-local server: + +- No `-s`/`--server` → defaults to `-s http://localhost`. +- No auth flag (`-u`/`-p`/`-n`/`-t`/`-j`) → defaults to `-u testadmin -p 123`. + +Verify the CLI at least imports and parses (no server needed): + +```powershell +powershell -NoProfile -File $D examples-help # Windows +``` + +```bash +bash "$D" examples-help # macOS / Linux +``` + +Driver-based invocations (all extra args pass through to `examples.py`): + +**Windows (`driver.ps1`):** + +```powershell +# Full local-dev defaults: -s http://localhost -u testadmin -p 123 +powershell -NoProfile -File $D examples + +# Local server, async mode, verbose - auth still defaulted +powershell -NoProfile -File $D examples --async -v + +# Explicit server, defaulted auth +powershell -NoProfile -File $D examples -s "https://10ax.online.tableau.com" + +# Explicit server + PAT (defaults do NOT fire because auth flags are present) +powershell -NoProfile -File $D examples -s "https://10ax.online.tableau.com" -n "" -t "" -S "" + +# Add the live-workbook flow (requires all three flags together) +powershell -NoProfile -File $D examples -s "https://10ax.online.tableau.com" -n "" -t "" -S "" ` + --workbook-datasource-id "" ` + --global-session-header "" ` + --x-session-id "" +``` + +**macOS / Linux (`driver.sh`):** + +```bash +# Full local-dev defaults: -s http://localhost -u testadmin -p 123 +bash "$D" examples + +# Local server, async mode, verbose - auth still defaulted +bash "$D" examples --async -v + +# Explicit server, defaulted auth +bash "$D" examples -s "https://10ax.online.tableau.com" + +# Explicit server + PAT (defaults do NOT fire because auth flags are present) +bash "$D" examples -s "https://10ax.online.tableau.com" -n "" -t "" -S "" + +# Add the live-workbook flow (requires all three flags together) +bash "$D" examples -s "https://10ax.online.tableau.com" -n "" -t "" -S "" \ + --workbook-datasource-id "" \ + --global-session-header "" \ + --x-session-id "" +``` + +Direct `python` invocations still work if you don't want defaults: + +```bash +python src/examples/examples.py -s "" -u "" -p "" +python src/examples/examples.py -s "" -n "" -t "" -S "" +python src/examples/examples.py -s "" -j "" -S "" +python src/examples/examples.py --async -s "" -u "" -p "" +``` + +The runner always attempts the published-datasource and embedded-workbook-datasource flows. The embedded-workbook-datasource flow silently skips (prints a warning) unless a `Superstore` sample workbook containing a `Sample - Superstore` embedded workbook datasource exists on the target server. The live-workbook flow (which uses `workbookDatasourceId` + session headers) is a distinct third flow that only runs when all three `--workbook-datasource-id` / `--global-session-header` / `--x-session-id` flags are supplied. + +### Assume the sample data is present + +When a user asks you to run the examples against their local server, **assume** the target has the standard sample content set up: + +- a published datasource named exactly `Superstore Datasource` +- a workbook named `Superstore` containing an embedded workbook datasource named `Sample - Superstore` + +Do not surface "the sample datasource might not be there" or "the published flow will crash if it's missing" as a hypothesis or ask the user to publish it — that's the caller's setup, not something you diagnose up front. Just run the examples. + +Only escalate to the user when the run **actually fails**, and only then explain what specifically went wrong. Common signals: + +- `FailedSignInError` / HTTP 401 on sign-in → auth problem (server may want different credentials). +- `ValueError: Datasource named 'Superstore Datasource' not found.` from `list_datasources_and_get_luid` → sample data really isn't there. Now (and only now) tell the user; do not pre-emptively warn. +- `errorCode: 400803 Unknown Field` inside `(Embedded Workbook Datasource)` output → expected for bin/parameter queries that are already skipped; only surface if a *different* query name errors out. +- Traceback outside the per-query try/except (e.g. a client construction error) → surface with the file/line. + +When you do report an error, quote the exact error line from the output so the user can act on it — don't paraphrase. + +## Gotchas + +- **PowerShell "NativeCommandError" noise on lint (Windows).** `black` writes its "All done!" banner to stderr. Windows PowerShell 5.1 wraps every native-executable stderr line in a `NativeCommandError` record and reports it in red, but the exit code is 0 and the run is fine. Ignore the red text; check the final "Success" / non-zero exit code, not the stream color. The driver checks `$LASTEXITCODE` explicitly. Not applicable to `driver.sh`. +- **`-File` and relative paths (Windows).** `powershell -NoProfile -File ` resolves against the child process's cwd, not the caller's. Always pass an absolute path — the recommended form is `$D = Join-Path (git rev-parse --show-toplevel) 'python_sdk\.claude\skills\run-vizql-data-service-py\driver.ps1'`, which works from any cwd inside the repo regardless of clone location. `driver.sh` sidesteps this entirely via `${BASH_SOURCE[0]}`. +- **`scripts/test.sh` blows away `./venv`.** Do not run it if you have anything you care about in a local `venv/` folder. Prefer `driver.ps1 lint && driver.ps1 test` (Windows) or `driver.sh lint && driver.sh test` (macOS/Linux) for local dev. +- **`gen` overwrites `src/api/openapi_generated.py`.** The intermediate `src/api/openapi_generated-raw.py` is deleted by `scripts/post_process.py`, so a clean run leaves exactly one file. If you see `openapi_generated-raw.py` lingering, post-processing crashed mid-run. +- **Version drift.** `check_version.sh` fails if the `major.minor` of `pyproject.toml` doesn't match the OpenAPI schema. When bumping schema versions, update `pyproject.toml`'s version to match before pushing — CI enforces this. +- **`examples.py` needs a real server.** There is no offline mode; `--help` is the only server-less verification the CLI supports. Do not add a "smoke test" that pretends to run it without credentials. +- **`examples` subcommand fills in local-dev defaults.** If a caller omits `-s` and any auth flag, the driver silently appends `-s http://localhost -u testadmin -p 123`. That's convenient for a laptop-local server but will noisily fail against a real Tableau host - always pass explicit `-s` (and PAT / JWT credentials) when targeting anything non-local. Both drivers detect equals-form flags (`--user=alice`) as well as space-form (`--user alice`). + +## Troubleshooting + +- **`bash: command not found` (Windows only)** — install Git for Windows, or add `C:\Program Files\Git\bin` (or `usr\bin`) to `PATH`. Not applicable to `driver.sh` on macOS/Linux. +- **`datamodel-codegen: command not found` during `gen`** — the script installs it explicitly. If installation was skipped by pip (network / proxy), run `pip install datamodel-code-generator==0.43.1` manually. +- **`black --check` reports "would be reformatted"** — run `driver.ps1 format` / `driver.sh format`, review the diff, commit. +- **`mypy` complains about `openapi_generated.py`** — that file is generated. Re-run `driver.ps1 gen` / `driver.sh gen` to make sure it's current; do not hand-edit it. +- **Tests fail with "asyncio_default_fixture_loop_scope is unset"** — that's a `pytest-asyncio` deprecation warning, not a failure. Look further up in the output for the actual failing test. + +## Files this skill drives + +``` +.claude/skills/run-vizql-data-service-py/driver.ps1 -> Windows entry point +.claude/skills/run-vizql-data-service-py/driver.sh -> macOS / Linux entry point +scripts/build.sh -> driver build +scripts/check_version.sh -> driver check-version +scripts/generate_stub.sh -> driver gen +scripts/generate_stub.bat (Windows equivalent of generate_stub.sh; drivers prefer the .sh path) +scripts/post_process.py (called by generate_stub.sh) +scripts/test.sh (heavyweight; driver test uses pytest directly instead) +src/examples/examples.py -> driver examples-help (needs credentials for a real run) +``` diff --git a/python_sdk/.claude/skills/run-vizql-data-service-py/driver.ps1 b/python_sdk/.claude/skills/run-vizql-data-service-py/driver.ps1 new file mode 100644 index 0000000..72e3c63 --- /dev/null +++ b/python_sdk/.claude/skills/run-vizql-data-service-py/driver.ps1 @@ -0,0 +1,153 @@ +# driver.ps1 - dev driver for the VizQL Data Service Python SDK. +# +# Runs from python_sdk/ (or any cwd; script resolves paths from its own location). +# .sh scripts are delegated to Git Bash; Python / pip / lint tools run inline. +# +# Usage: +# powershell -NoProfile -File .claude/skills/run-vizql-data-service-py/driver.ps1 +# (Windows PowerShell 5.1; pwsh 7 not required.) +# +# Subcommands: +# setup Install package in editable mode with [dev] extras. +# check-version Verify pyproject.toml <-> OpenAPI schema major.minor match. +# gen Regenerate src/api/openapi_generated.py from the schema. +# build Clean, build sdist + wheel into dist/. +# format Apply black + isort (writes changes). +# lint Run black --check, isort --check-only, flake8, mypy. +# test Run pytest against tests/. +# examples-help Print examples.py CLI help (no server needed). +# examples Run examples.py; extra args are forwarded (e.g. -s --async). +# If no -s/--server is given, defaults to -s http://localhost. +# If no auth flag (-u/-p/-n/-t/-j) is given, defaults to +# -u testadmin -p 123. +# all gen -> check-version -> build -> lint -> test (matches CI). +# help Show this help. + +[CmdletBinding()] +param( + [Parameter(Position = 0)] + [ValidateSet('setup','check-version','gen','build','format','lint','test','examples-help','examples','all','help')] + [string] $Subcommand = 'help', + + # Remaining args forwarded to the subcommand (used by 'examples'). + [Parameter(ValueFromRemainingArguments = $true)] + [string[]] $Rest +) + +$ErrorActionPreference = 'Stop' + +# Resolve repo root (python_sdk/) from this script's location. +# $PSScriptRoot is reliable even when a param() block runs before the body. +$SkillRoot = $PSScriptRoot +if (-not $SkillRoot) { $SkillRoot = Split-Path -Parent $PSCommandPath } +$RepoRoot = Resolve-Path (Join-Path $SkillRoot '..\..\..') +Set-Location $RepoRoot + +function Get-BashPath { + $bash = Get-Command bash -ErrorAction SilentlyContinue + if ($bash) { return $bash.Source } + $candidates = @( + 'C:\Program Files\Git\bin\bash.exe', + 'C:\Program Files\Git\usr\bin\bash.exe' + ) + foreach ($c in $candidates) { if (Test-Path $c) { return $c } } + throw "Git Bash not found. Install Git for Windows or add bash.exe to PATH." +} + +function Invoke-Bash([string] $ScriptRelPath) { + $bash = Get-BashPath + Write-Host "[driver] bash $ScriptRelPath" -ForegroundColor Cyan + & $bash $ScriptRelPath + if ($LASTEXITCODE -ne 0) { throw "bash $ScriptRelPath failed with exit $LASTEXITCODE" } +} + +function Invoke-Step([string] $Label, [scriptblock] $Body) { + Write-Host "[driver] $Label" -ForegroundColor Cyan + & $Body + if ($LASTEXITCODE -ne 0) { throw "$Label failed with exit $LASTEXITCODE" } +} + +function Cmd-Setup { + Invoke-Step "python -m pip install --upgrade pip" { python -m pip install --upgrade pip } + Invoke-Step "pip install -e .[dev]" { pip install -e ".[dev]" } +} + +function Cmd-CheckVersion { Invoke-Bash 'scripts/check_version.sh' } + +function Cmd-Gen { Invoke-Bash 'scripts/generate_stub.sh' } + +function Cmd-Build { Invoke-Bash 'scripts/build.sh' } + +function Cmd-Format { + Invoke-Step "black ." { black . } + Invoke-Step "isort ." { isort . } +} + +function Cmd-Lint { + Invoke-Step "black --check" { black . --check } + Invoke-Step "isort --check-only" { isort . --check-only } + Invoke-Step "flake8" { flake8 . } + Invoke-Step "mypy" { mypy . } +} + +function Cmd-Test { Invoke-Step "pytest" { pytest tests -q --disable-warnings } } + +function Cmd-ExamplesHelp { Invoke-Step "examples.py --help" { python src/examples/examples.py --help } } + +function Cmd-Examples([string[]] $Passthrough) { + if (-not $Passthrough) { $Passthrough = @() } + + # Match on the flag *name* only, so both space-form (`-u alice`) and + # equals-form (`--user=alice`) are detected. Without this split, + # `--user=alice` slips past and the driver silently overrides it with + # the injected default credentials. + $authFlags = @('-u','--user','-p','--password','-n','--pat-name','-t','--pat-secret','-j','--jwt-token') + $serverFlags = @('-s','--server') + + $hasAuth = $false + $hasServer = $false + foreach ($tok in $Passthrough) { + $name = ($tok -split '=', 2)[0] + if (-not $hasAuth -and $authFlags -contains $name) { $hasAuth = $true } + if (-not $hasServer -and $serverFlags -contains $name) { $hasServer = $true } + } + + $finalArgs = @('src/examples/examples.py') + $Passthrough + if (-not $hasServer) { + Write-Host "[driver] no -s/--server supplied - defaulting to -s http://localhost" -ForegroundColor Yellow + $finalArgs += @('-s','http://localhost') + } + if (-not $hasAuth) { + Write-Host "[driver] no auth flag supplied - defaulting to -u testadmin -p 123" -ForegroundColor Yellow + $finalArgs += @('-u','testadmin','-p','123') + } + + Invoke-Step "examples.py $($Passthrough -join ' ')" { python @finalArgs } +} + +function Cmd-All { + Cmd-Gen + Cmd-CheckVersion + Cmd-Build + Cmd-Lint + Cmd-Test +} + +function Cmd-Help { + $self = Join-Path $SkillRoot 'driver.ps1' + Get-Content $self | Select-String -Pattern '^#' | ForEach-Object { $_.Line -replace '^# ?','' } | Select-Object -First 24 +} + +switch ($Subcommand) { + 'setup' { Cmd-Setup } + 'check-version' { Cmd-CheckVersion } + 'gen' { Cmd-Gen } + 'build' { Cmd-Build } + 'format' { Cmd-Format } + 'lint' { Cmd-Lint } + 'test' { Cmd-Test } + 'examples-help' { Cmd-ExamplesHelp } + 'examples' { Cmd-Examples $Rest } + 'all' { Cmd-All } + 'help' { Cmd-Help } +} diff --git a/python_sdk/.claude/skills/run-vizql-data-service-py/driver.sh b/python_sdk/.claude/skills/run-vizql-data-service-py/driver.sh new file mode 100644 index 0000000..4045009 --- /dev/null +++ b/python_sdk/.claude/skills/run-vizql-data-service-py/driver.sh @@ -0,0 +1,136 @@ +#!/usr/bin/env bash +# driver.sh - dev driver for the VizQL Data Service Python SDK (macOS / Linux). +# +# Runs from python_sdk/ (or any cwd; script resolves paths from its own location). +# .sh scripts under scripts/ are called directly; Python / pip / lint tools run inline. +# +# Usage: +# bash .claude/skills/run-vizql-data-service-py/driver.sh [args...] +# +# Subcommands: +# setup Install package in editable mode with [dev] extras. +# check-version Verify pyproject.toml <-> OpenAPI schema major.minor match. +# gen Regenerate src/api/openapi_generated.py from the schema. +# build Clean, build sdist + wheel into dist/. +# format Apply black + isort (writes changes). +# lint Run black --check, isort --check-only, flake8, mypy. +# test Run pytest against tests/. +# examples-help Print examples.py CLI help (no server needed). +# examples Run examples.py; extra args are forwarded (e.g. -s --async). +# If no -s/--server is given, defaults to -s http://localhost. +# If no auth flag (-u/-p/-n/-t/-j) is given, defaults to +# -u testadmin -p 123. +# all gen -> check-version -> build -> lint -> test (matches CI). +# help Show this help. + +set -euo pipefail + +SKILL_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SKILL_ROOT/../../.." && pwd)" +cd "$REPO_ROOT" + +log() { printf '\033[36m[driver] %s\033[0m\n' "$*"; } +warn() { printf '\033[33m[driver] %s\033[0m\n' "$*"; } + +run_step() { + local label="$1"; shift + log "$label" + "$@" +} + +cmd_setup() { + run_step "python -m pip install --upgrade pip" python -m pip install --upgrade pip + run_step "pip install -e .[dev]" pip install -e ".[dev]" +} + +cmd_check_version() { run_step "bash scripts/check_version.sh" bash scripts/check_version.sh; } +cmd_gen() { run_step "bash scripts/generate_stub.sh" bash scripts/generate_stub.sh; } +cmd_build() { run_step "bash scripts/build.sh" bash scripts/build.sh; } + +cmd_format() { + run_step "black ." black . + run_step "isort ." isort . +} + +cmd_lint() { + run_step "black --check" black . --check + run_step "isort --check-only" isort . --check-only + run_step "flake8" flake8 . + run_step "mypy" mypy . +} + +cmd_test() { run_step "pytest" pytest tests -q --disable-warnings; } +cmd_examples_help() { run_step "examples.py --help" python src/examples/examples.py --help; } + +# Match on the flag *name* only, so both space-form (`-u alice`) and +# equals-form (`--user=alice`) are detected. Without splitting on '=', +# `--user=alice` would slip past and the driver would silently override +# it with the injected default credentials. +cmd_examples() { + local auth_flags=(-u --user -p --password -n --pat-name -t --pat-secret -j --jwt-token) + local server_flags=(-s --server) + local has_auth=0 has_server=0 + + local tok name + for tok in "$@"; do + name="${tok%%=*}" + if [[ $has_auth -eq 0 ]]; then + for f in "${auth_flags[@]}"; do + [[ "$name" == "$f" ]] && { has_auth=1; break; } + done + fi + if [[ $has_server -eq 0 ]]; then + for f in "${server_flags[@]}"; do + [[ "$name" == "$f" ]] && { has_server=1; break; } + done + fi + done + + local final_args=(src/examples/examples.py "$@") + if [[ $has_server -eq 0 ]]; then + warn "no -s/--server supplied - defaulting to -s http://localhost" + final_args+=(-s http://localhost) + fi + if [[ $has_auth -eq 0 ]]; then + warn "no auth flag supplied - defaulting to -u testadmin -p 123" + final_args+=(-u testadmin -p 123) + fi + + log "examples.py $*" + python "${final_args[@]}" +} + +cmd_all() { + cmd_gen + cmd_check_version + cmd_build + cmd_lint + cmd_test +} + +cmd_help() { + # Print the leading '#' comment block of this script, skipping the shebang. + sed -n '2,25{/^#/{s/^# \{0,1\}//;p;};}' "$SKILL_ROOT/driver.sh" +} + +subcommand="${1:-help}" +shift || true + +case "$subcommand" in + setup) cmd_setup ;; + check-version) cmd_check_version ;; + gen) cmd_gen ;; + build) cmd_build ;; + format) cmd_format ;; + lint) cmd_lint ;; + test) cmd_test ;; + examples-help) cmd_examples_help ;; + examples) cmd_examples "$@" ;; + all) cmd_all ;; + help|--help|-h) cmd_help ;; + *) + printf 'unknown subcommand: %s\n\n' "$subcommand" >&2 + cmd_help + exit 2 + ;; +esac