tableau-cli 0.1.4__tar.gz → 0.1.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. tableau_cli-0.1.5/.agents/plugins/marketplace.json +21 -0
  2. tableau_cli-0.1.5/.claude-plugin/marketplace.json +14 -0
  3. tableau_cli-0.1.5/.claude-plugin/plugin.json +11 -0
  4. tableau_cli-0.1.5/.codex-plugin/plugin.json +17 -0
  5. tableau_cli-0.1.5/.github/workflows/ci.yml +26 -0
  6. tableau_cli-0.1.5/.github/workflows/release.yml +75 -0
  7. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/PKG-INFO +39 -29
  8. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/README.md +31 -22
  9. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/pyproject.toml +8 -7
  10. {tableau_cli-0.1.4/skills → tableau_cli-0.1.5/skills/tableau-cli}/references/cli.md +5 -3
  11. {tableau_cli-0.1.4/skills → tableau_cli-0.1.5/skills/tableau-cli}/references/installation.md +3 -1
  12. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/src/tableau_cli/cli.py +1 -1
  13. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/src/tableau_cli/commands/convert_cmd.py +6 -0
  14. tableau_cli-0.1.5/src/tableau_cli/utils/_hyper_export.py +81 -0
  15. tableau_cli-0.1.5/src/tableau_cli/utils/convert.py +113 -0
  16. tableau_cli-0.1.5/tests/test_cli.py +20 -0
  17. tableau_cli-0.1.5/tests/test_conversion.py +283 -0
  18. tableau_cli-0.1.4/.claude/settings.local.json +0 -8
  19. tableau_cli-0.1.4/src/tableau_cli/utils/_convert_worker.py +0 -55
  20. tableau_cli-0.1.4/src/tableau_cli/utils/convert.py +0 -177
  21. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/.gitignore +0 -0
  22. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/LICENSE +0 -0
  23. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/NOTICE +0 -0
  24. {tableau_cli-0.1.4/skills → tableau_cli-0.1.5/skills/tableau-cli}/SKILL.md +0 -0
  25. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/src/tableau_cli/__init__.py +0 -0
  26. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/src/tableau_cli/api/__init__.py +0 -0
  27. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/src/tableau_cli/api/client.py +0 -0
  28. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/src/tableau_cli/auth/__init__.py +0 -0
  29. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/src/tableau_cli/auth/with_auth.py +0 -0
  30. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/src/tableau_cli/commands/__init__.py +0 -0
  31. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/src/tableau_cli/commands/config_cmd.py +0 -0
  32. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/src/tableau_cli/commands/datasources_cmd.py +0 -0
  33. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/src/tableau_cli/commands/projects_cmd.py +0 -0
  34. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/src/tableau_cli/commands/search_cmd.py +0 -0
  35. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/src/tableau_cli/commands/views_cmd.py +0 -0
  36. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/src/tableau_cli/commands/workbooks_cmd.py +0 -0
  37. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/src/tableau_cli/config/__init__.py +0 -0
  38. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/src/tableau_cli/config/store.py +0 -0
  39. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/src/tableau_cli/config/types.py +0 -0
  40. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/src/tableau_cli/errors/__init__.py +0 -0
  41. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/src/tableau_cli/errors/cli_error.py +0 -0
  42. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/src/tableau_cli/errors/vds_error_handler.py +0 -0
  43. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/src/tableau_cli/output/__init__.py +0 -0
  44. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/src/tableau_cli/output/format.py +0 -0
  45. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/src/tableau_cli/output/json_output.py +0 -0
  46. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/src/tableau_cli/output/table_output.py +0 -0
  47. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/src/tableau_cli/utils/__init__.py +0 -0
  48. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/src/tableau_cli/utils/datasource_metadata_utils.py +0 -0
  49. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/src/tableau_cli/utils/lineage_utils.py +0 -0
  50. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/src/tableau_cli/utils/paginate.py +0 -0
  51. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/src/tableau_cli/utils/search_content_utils.py +0 -0
  52. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/src/tableau_cli/utils/tableau_version.py +0 -0
  53. {tableau_cli-0.1.4 → tableau_cli-0.1.5}/src/tableau_cli/utils/web_url.py +0 -0
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "tableau-cli",
3
+ "interface": {
4
+ "displayName": "Tableau CLI"
5
+ },
6
+ "plugins": [
7
+ {
8
+ "name": "tableau-cli",
9
+ "description": "Interact with Tableau Server / Cloud via tableau-cli: search content, query datasources, download files, export view images and data, convert TDSX/HYPER to Parquet/CSV.",
10
+ "source": {
11
+ "source": "local",
12
+ "path": "./"
13
+ },
14
+ "policy": {
15
+ "installation": "AVAILABLE",
16
+ "authentication": "ON_INSTALL"
17
+ },
18
+ "category": "Productivity"
19
+ }
20
+ ]
21
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "tableau-cli",
3
+ "description": "Claude Code plugin for tableau-cli — a Tableau Server / Cloud CLI designed for AI agent integration.",
4
+ "owner": {
5
+ "name": "Richard Wang"
6
+ },
7
+ "plugins": [
8
+ {
9
+ "name": "tableau-cli",
10
+ "source": "./",
11
+ "description": "Interact with Tableau Server / Cloud via tableau-cli: search content, query datasources, download files, export view images and data, convert TDSX/HYPER to Parquet/CSV."
12
+ }
13
+ ]
14
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "tableau-cli",
3
+ "description": "Interact with Tableau Server / Cloud via tableau-cli: search content, query datasources, download files, export view images and data, convert TDSX/HYPER to Parquet/CSV.",
4
+ "version": "0.1.4",
5
+ "author": {
6
+ "name": "Richard Wang"
7
+ },
8
+ "homepage": "https://github.com/i-richardwang/tableau-cli",
9
+ "repository": "https://github.com/i-richardwang/tableau-cli",
10
+ "license": "Apache-2.0"
11
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "tableau-cli",
3
+ "version": "0.1.4",
4
+ "description": "Interact with Tableau Server / Cloud via tableau-cli: search content, query datasources, download files, export view images and data, convert TDSX/HYPER to Parquet/CSV.",
5
+ "author": {
6
+ "name": "Richard Wang"
7
+ },
8
+ "homepage": "https://github.com/i-richardwang/tableau-cli",
9
+ "repository": "https://github.com/i-richardwang/tableau-cli",
10
+ "license": "Apache-2.0",
11
+ "skills": "./skills/",
12
+ "interface": {
13
+ "displayName": "Tableau CLI",
14
+ "shortDescription": "Drive Tableau Server / Cloud from an agent via tableau-cli",
15
+ "category": "Productivity"
16
+ }
17
+ }
@@ -0,0 +1,26 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - "**"
7
+ pull_request:
8
+
9
+ jobs:
10
+ conversion:
11
+ name: Hyper exports (${{ matrix.os }}, Python ${{ matrix.python }})
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ os: [macos-14, macos-15-intel, windows-latest, ubuntu-latest]
16
+ python: ['3.11', '3.14']
17
+ runs-on: ${{ matrix.os }}
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+ - uses: astral-sh/setup-uv@v6
21
+ - run: uv python install ${{ matrix.python }}
22
+ - run: uv venv --python ${{ matrix.python }}
23
+ - run: uv pip install -e ".[convert,dev]" "tableauhyperapi==0.0.26479"
24
+ - run: uv run --no-sync ruff check .
25
+ - run: uv run --no-sync pytest
26
+ - run: uv build
@@ -0,0 +1,75 @@
1
+ name: "Publish release to PyPI"
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ # Publish on version tags, e.g. v0.1.0
7
+ - "v[0-9]+.[0-9]+.[0-9]+"
8
+ - "v[0-9]+.[0-9]+.[0-9]+rc[0-9]+"
9
+ - "v[0-9]+.[0-9]+.[0-9]+[ab][0-9]+"
10
+
11
+ jobs:
12
+ build:
13
+ runs-on: ubuntu-latest
14
+ permissions:
15
+ contents: read
16
+ steps:
17
+ - name: Checkout
18
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
19
+ with:
20
+ persist-credentials: false
21
+
22
+ - name: Check tag matches package version
23
+ run: |
24
+ version="$(python3 -c 'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])')"
25
+ tag="${GITHUB_REF_NAME#v}"
26
+ if [ "$version" != "$tag" ]; then
27
+ echo "::error::Tag ${GITHUB_REF_NAME} does not match pyproject.toml version ${version}."
28
+ exit 1
29
+ fi
30
+
31
+ - name: Install uv
32
+ uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
33
+ with:
34
+ enable-cache: false
35
+
36
+ - name: Build
37
+ run: uv build
38
+
39
+ - name: Smoke test (wheel)
40
+ run: uv run --isolated --no-project --with dist/*.whl tableau-cli --version | grep -F -x "tableau-cli, version ${GITHUB_REF_NAME#v}"
41
+
42
+ - name: Smoke test (source distribution)
43
+ run: uv run --isolated --no-project --with dist/*.tar.gz tableau-cli --version | grep -F -x "tableau-cli, version ${GITHUB_REF_NAME#v}"
44
+
45
+ - name: Upload distributions as artifacts
46
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
47
+ with:
48
+ name: dist
49
+ path: dist/
50
+
51
+ publish:
52
+ needs:
53
+ - build
54
+ runs-on: ubuntu-latest
55
+ environment:
56
+ name: pypi
57
+ permissions:
58
+ id-token: write
59
+ steps:
60
+ - name: Install uv
61
+ uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
62
+ with:
63
+ enable-cache: false
64
+
65
+ - name: Download distributions artifact
66
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
67
+ with:
68
+ name: dist
69
+ path: dist/
70
+
71
+ - name: Generate PEP 740 attestations
72
+ uses: astral-sh/attest-action@f589a42a7efb6fe400b4f400de60b4bc90390027 # v0.0.6
73
+
74
+ - name: Publish
75
+ run: uv publish --trusted-publishing always
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: tableau-cli
3
- Version: 0.1.4
3
+ Version: 0.1.5
4
4
  Summary: CLI for Tableau Server/Cloud, designed for AI agent integration
5
5
  Project-URL: Repository, https://github.com/i-richardwang/tableau-cli
6
6
  Author: Richard Wang
@@ -12,20 +12,21 @@ Classifier: Environment :: Console
12
12
  Classifier: Intended Audience :: Developers
13
13
  Classifier: License :: OSI Approved :: Apache Software License
14
14
  Classifier: Programming Language :: Python :: 3
15
- Classifier: Programming Language :: Python :: 3.10
16
15
  Classifier: Programming Language :: Python :: 3.11
17
16
  Classifier: Programming Language :: Python :: 3.12
18
17
  Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
19
  Classifier: Topic :: Software Development :: Libraries
20
- Requires-Python: >=3.10
20
+ Requires-Python: >=3.11
21
21
  Requires-Dist: click>=8.0
22
22
  Requires-Dist: httpx>=0.27
23
23
  Provides-Extra: convert
24
- Requires-Dist: pantab>=4.0; extra == 'convert'
25
- Requires-Dist: polars>=1.0; extra == 'convert'
26
- Requires-Dist: pyarrow>=15.0; extra == 'convert'
24
+ Requires-Dist: tableauhyperapi>=0.0.26479; extra == 'convert'
27
25
  Provides-Extra: dev
26
+ Requires-Dist: pyarrow>=15.0; extra == 'dev'
27
+ Requires-Dist: pytest>=8.0; extra == 'dev'
28
28
  Requires-Dist: ruff>=0.11; extra == 'dev'
29
+ Requires-Dist: tzdata; extra == 'dev'
29
30
  Description-Content-Type: text/markdown
30
31
 
31
32
  # tableau-cli
@@ -36,7 +37,7 @@ A command-line interface for Tableau Server / Tableau Cloud, designed for AI age
36
37
 
37
38
  MCP (Model Context Protocol) servers continuously occupy agent context — every tool description is loaded into the system prompt, even when only one command is needed. A CLI follows a simpler call-execute-exit pattern: the agent invokes a command, reads the JSON output, and moves on.
38
39
 
39
- The companion Claude Code skill under `skills/` extends the same idea to command knowledge — the full reference is loaded only when the agent is about to execute a command, not kept in context upfront. See [Use with Claude Code](#use-with-claude-code) below.
40
+ The companion agent skill under `skills/` extends the same idea to command knowledge — the full reference is loaded only when the agent is about to execute a command, not kept in context upfront. See [Use with Coding Agents](#use-with-coding-agents) below.
40
41
 
41
42
  This project provides the same capabilities as the [tableau-mcp](https://github.com/tableau/tableau-mcp) server, with behavioral alignment in data transformations, error handling, and output structures.
42
43
 
@@ -44,7 +45,7 @@ This project provides the same capabilities as the [tableau-mcp](https://github.
44
45
 
45
46
  ### Prerequisites
46
47
 
47
- - Python >= 3.10
48
+ - Python >= 3.11
48
49
  - A Tableau Server or Tableau Cloud instance
49
50
  - A [Personal Access Token (PAT)](https://help.tableau.com/current/server/en-us/security_personal_access_tokens.htm)
50
51
 
@@ -83,28 +84,29 @@ Environment variables take precedence over the config file. `siteName` defaults
83
84
  tableau-cli config show
84
85
  ```
85
86
 
86
- ## Use with Claude Code
87
+ ## Use with Coding Agents
87
88
 
88
- This repository ships with a Claude Code skill under `skills/`. Once loaded, it gives an agent enough context to route a user's request to the right command — without putting the full CLI reference into the system prompt.
89
+ This repository ships an agent skill (`skills/tableau-cli/`) that routes a user's request to the right command and loads the full CLI reference only at execution time. The repository doubles as its own plugin marketplace for both Claude Code and Codex.
90
+
91
+ **Claude Code**
89
92
 
90
93
  ```
91
- skills/
92
- ├── SKILL.md # Intent routing + environment check
93
- └── references/
94
- ├── cli.md # Full command reference (loaded before executing)
95
- └── installation.md # Install + auth setup (loaded if not configured)
94
+ /plugin marketplace add i-richardwang/tableau-cli
95
+ /plugin install tableau-cli@tableau-cli
96
96
  ```
97
97
 
98
- Once Claude Code has loaded the skill, a typical interaction looks like:
98
+ To receive updates, enable auto-update for the `tableau-cli` marketplace in `/plugin`, or run `/plugin marketplace update tableau-cli` manually.
99
+
100
+ **Codex**
99
101
 
100
- 1. The agent runs `tableau-cli --help` to verify the CLI is installed and configured. If not, it loads `references/installation.md` and stops until setup is complete.
101
- 2. The agent maps the user's intent to a subcommand using the Intent Routing table in `SKILL.md` (e.g., "find datasources with Sales in the name" → `ds list --filter "name:has:Sales"`).
102
- 3. Before constructing the actual command, the agent loads `references/cli.md` — the skill explicitly forbids guessing flags from memory, so the reference is the source of truth for syntax.
103
- 4. The agent runs the command and parses the structured JSON output, including the `hint` field on errors.
102
+ ```
103
+ codex plugin marketplace add i-richardwang/tableau-cli
104
+ codex plugin add tableau-cli@tableau-cli
105
+ ```
104
106
 
105
- Common workflows (e.g., `ds download --to parquet → load with Polars/Pandas`) are pre-defined under Intent Routing in `SKILL.md`, so the agent doesn't have to reason about chaining from scratch.
107
+ To receive updates, run `codex plugin marketplace upgrade`.
106
108
 
107
- You can of course use `tableau-cli` directly from a shell without the skill — the skill is only needed when you want an agent to drive the tool.
109
+ The skill is only needed when an agent drives the tool — the CLI works standalone from any shell.
108
110
 
109
111
  ## Commands
110
112
 
@@ -128,7 +130,7 @@ tableau-cli ds list --filter "name:has:Sales" --limit 50
128
130
  # Download datasource file (.tdsx)
129
131
  tableau-cli datasources download <datasourceId> -o ./data/
130
132
 
131
- # Download and convert to Parquet or CSV in one step (requires tableau-cli[convert])
133
+ # Download and convert to Parquet or CSV in one step (requires tableau-cli[convert] or uv)
132
134
  tableau-cli ds download <datasourceId> -o ./data/ --to parquet
133
135
  tableau-cli ds download <datasourceId> -o ./data/ --to csv
134
136
 
@@ -168,10 +170,14 @@ tableau-cli views image <viewId> --vf "Region=West" -o west.png
168
170
 
169
171
  Convert local TDSX/HYPER files to Parquet or CSV.
170
172
 
171
- Conversion needs heavier packages (`pantab`, `polars`, `pyarrow`). You have two options:
173
+ Conversion runs locally through the official `tableauhyperapi` package. Its native Hyper engine exports directly to Parquet or CSV, without loading the entire extract into a DataFrame. You have two options:
172
174
 
173
- - Install them into the CLI: `pip install tableau-cli[convert]`.
174
- - Don't install anything: if [uv](https://docs.astral.sh/uv/) is on your `PATH`, conversion transparently falls back to running the step in an ephemeral `uv run --with ...` environment, so those packages never land in your host Python. The first run provisions the environment (a few seconds); later runs use uv's cache. This is the recommended path when the CLI itself was installed via `uv tool install tableau-cli`.
175
+ - Install the export dependency into the CLI: `pip install tableau-cli[convert]`.
176
+ - Don't install anything: if [uv](https://docs.astral.sh/uv/) is on your `PATH`, conversion transparently falls back to running the step in an ephemeral `uv run --with ...` environment, so the conversion dependency never lands in your host Python. The first run provisions the environment (a few seconds); later runs use uv's cache. This is the recommended path when the CLI itself was installed via `uv tool install tableau-cli`.
177
+
178
+ The conversion runtime requires native 64-bit Python 3.11 or newer. Supported targets are Apple Silicon Macs (macOS 13+), Intel Macs, Windows x86_64, and the Linux x86_64 distributions supported by [Hyper API](https://tableau.github.io/hyper-db/docs/installation/). Package installation selects the engine for the interpreter's platform; Apple Silicon uses the native ARM package.
179
+
180
+ Each extract must contain exactly one table. Exports preserve column order and use Hyper's native [Parquet and CSV mappings](https://developer.salesforce.com/docs/data/data-cloud-query-guide/references/dc-sql-reference/copy-to.html). CSV includes a header and uses ISO dates. The destination is replaced only after a complete export. Download-and-convert commands check that the local engine can start before downloading the datasource.
175
181
 
176
182
  For most use cases, `ds download --to parquet` (or `--to csv`) is simpler — it downloads and converts in one step. The `convert` command is useful when you already have a `.tdsx` or `.hyper` file on disk.
177
183
 
@@ -273,8 +279,12 @@ Error types include: `authentication-error`, `feature-disabled`, `tableau-api-er
273
279
  # Run directly
274
280
  python -m tableau_cli.cli views list
275
281
 
276
- # Install in editable mode
277
- pip install -e .
282
+ # Install development and conversion dependencies
283
+ pip install -e ".[convert,dev]"
284
+
285
+ # Validate local exports and isolated uv execution
286
+ ruff check .
287
+ pytest
278
288
  ```
279
289
 
280
290
  ## Acknowledgements
@@ -6,7 +6,7 @@ A command-line interface for Tableau Server / Tableau Cloud, designed for AI age
6
6
 
7
7
  MCP (Model Context Protocol) servers continuously occupy agent context — every tool description is loaded into the system prompt, even when only one command is needed. A CLI follows a simpler call-execute-exit pattern: the agent invokes a command, reads the JSON output, and moves on.
8
8
 
9
- The companion Claude Code skill under `skills/` extends the same idea to command knowledge — the full reference is loaded only when the agent is about to execute a command, not kept in context upfront. See [Use with Claude Code](#use-with-claude-code) below.
9
+ The companion agent skill under `skills/` extends the same idea to command knowledge — the full reference is loaded only when the agent is about to execute a command, not kept in context upfront. See [Use with Coding Agents](#use-with-coding-agents) below.
10
10
 
11
11
  This project provides the same capabilities as the [tableau-mcp](https://github.com/tableau/tableau-mcp) server, with behavioral alignment in data transformations, error handling, and output structures.
12
12
 
@@ -14,7 +14,7 @@ This project provides the same capabilities as the [tableau-mcp](https://github.
14
14
 
15
15
  ### Prerequisites
16
16
 
17
- - Python >= 3.10
17
+ - Python >= 3.11
18
18
  - A Tableau Server or Tableau Cloud instance
19
19
  - A [Personal Access Token (PAT)](https://help.tableau.com/current/server/en-us/security_personal_access_tokens.htm)
20
20
 
@@ -53,28 +53,29 @@ Environment variables take precedence over the config file. `siteName` defaults
53
53
  tableau-cli config show
54
54
  ```
55
55
 
56
- ## Use with Claude Code
56
+ ## Use with Coding Agents
57
57
 
58
- This repository ships with a Claude Code skill under `skills/`. Once loaded, it gives an agent enough context to route a user's request to the right command — without putting the full CLI reference into the system prompt.
58
+ This repository ships an agent skill (`skills/tableau-cli/`) that routes a user's request to the right command and loads the full CLI reference only at execution time. The repository doubles as its own plugin marketplace for both Claude Code and Codex.
59
+
60
+ **Claude Code**
59
61
 
60
62
  ```
61
- skills/
62
- ├── SKILL.md # Intent routing + environment check
63
- └── references/
64
- ├── cli.md # Full command reference (loaded before executing)
65
- └── installation.md # Install + auth setup (loaded if not configured)
63
+ /plugin marketplace add i-richardwang/tableau-cli
64
+ /plugin install tableau-cli@tableau-cli
66
65
  ```
67
66
 
68
- Once Claude Code has loaded the skill, a typical interaction looks like:
67
+ To receive updates, enable auto-update for the `tableau-cli` marketplace in `/plugin`, or run `/plugin marketplace update tableau-cli` manually.
68
+
69
+ **Codex**
69
70
 
70
- 1. The agent runs `tableau-cli --help` to verify the CLI is installed and configured. If not, it loads `references/installation.md` and stops until setup is complete.
71
- 2. The agent maps the user's intent to a subcommand using the Intent Routing table in `SKILL.md` (e.g., "find datasources with Sales in the name" → `ds list --filter "name:has:Sales"`).
72
- 3. Before constructing the actual command, the agent loads `references/cli.md` — the skill explicitly forbids guessing flags from memory, so the reference is the source of truth for syntax.
73
- 4. The agent runs the command and parses the structured JSON output, including the `hint` field on errors.
71
+ ```
72
+ codex plugin marketplace add i-richardwang/tableau-cli
73
+ codex plugin add tableau-cli@tableau-cli
74
+ ```
74
75
 
75
- Common workflows (e.g., `ds download --to parquet → load with Polars/Pandas`) are pre-defined under Intent Routing in `SKILL.md`, so the agent doesn't have to reason about chaining from scratch.
76
+ To receive updates, run `codex plugin marketplace upgrade`.
76
77
 
77
- You can of course use `tableau-cli` directly from a shell without the skill — the skill is only needed when you want an agent to drive the tool.
78
+ The skill is only needed when an agent drives the tool — the CLI works standalone from any shell.
78
79
 
79
80
  ## Commands
80
81
 
@@ -98,7 +99,7 @@ tableau-cli ds list --filter "name:has:Sales" --limit 50
98
99
  # Download datasource file (.tdsx)
99
100
  tableau-cli datasources download <datasourceId> -o ./data/
100
101
 
101
- # Download and convert to Parquet or CSV in one step (requires tableau-cli[convert])
102
+ # Download and convert to Parquet or CSV in one step (requires tableau-cli[convert] or uv)
102
103
  tableau-cli ds download <datasourceId> -o ./data/ --to parquet
103
104
  tableau-cli ds download <datasourceId> -o ./data/ --to csv
104
105
 
@@ -138,10 +139,14 @@ tableau-cli views image <viewId> --vf "Region=West" -o west.png
138
139
 
139
140
  Convert local TDSX/HYPER files to Parquet or CSV.
140
141
 
141
- Conversion needs heavier packages (`pantab`, `polars`, `pyarrow`). You have two options:
142
+ Conversion runs locally through the official `tableauhyperapi` package. Its native Hyper engine exports directly to Parquet or CSV, without loading the entire extract into a DataFrame. You have two options:
142
143
 
143
- - Install them into the CLI: `pip install tableau-cli[convert]`.
144
- - Don't install anything: if [uv](https://docs.astral.sh/uv/) is on your `PATH`, conversion transparently falls back to running the step in an ephemeral `uv run --with ...` environment, so those packages never land in your host Python. The first run provisions the environment (a few seconds); later runs use uv's cache. This is the recommended path when the CLI itself was installed via `uv tool install tableau-cli`.
144
+ - Install the export dependency into the CLI: `pip install tableau-cli[convert]`.
145
+ - Don't install anything: if [uv](https://docs.astral.sh/uv/) is on your `PATH`, conversion transparently falls back to running the step in an ephemeral `uv run --with ...` environment, so the conversion dependency never lands in your host Python. The first run provisions the environment (a few seconds); later runs use uv's cache. This is the recommended path when the CLI itself was installed via `uv tool install tableau-cli`.
146
+
147
+ The conversion runtime requires native 64-bit Python 3.11 or newer. Supported targets are Apple Silicon Macs (macOS 13+), Intel Macs, Windows x86_64, and the Linux x86_64 distributions supported by [Hyper API](https://tableau.github.io/hyper-db/docs/installation/). Package installation selects the engine for the interpreter's platform; Apple Silicon uses the native ARM package.
148
+
149
+ Each extract must contain exactly one table. Exports preserve column order and use Hyper's native [Parquet and CSV mappings](https://developer.salesforce.com/docs/data/data-cloud-query-guide/references/dc-sql-reference/copy-to.html). CSV includes a header and uses ISO dates. The destination is replaced only after a complete export. Download-and-convert commands check that the local engine can start before downloading the datasource.
145
150
 
146
151
  For most use cases, `ds download --to parquet` (or `--to csv`) is simpler — it downloads and converts in one step. The `convert` command is useful when you already have a `.tdsx` or `.hyper` file on disk.
147
152
 
@@ -243,8 +248,12 @@ Error types include: `authentication-error`, `feature-disabled`, `tableau-api-er
243
248
  # Run directly
244
249
  python -m tableau_cli.cli views list
245
250
 
246
- # Install in editable mode
247
- pip install -e .
251
+ # Install development and conversion dependencies
252
+ pip install -e ".[convert,dev]"
253
+
254
+ # Validate local exports and isolated uv execution
255
+ ruff check .
256
+ pytest
248
257
  ```
249
258
 
250
259
  ## Acknowledgements
@@ -4,10 +4,10 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "tableau-cli"
7
- version = "0.1.4"
7
+ version = "0.1.5"
8
8
  description = "CLI for Tableau Server/Cloud, designed for AI agent integration"
9
9
  license = "Apache-2.0"
10
- requires-python = ">=3.10"
10
+ requires-python = ">=3.11"
11
11
  readme = "README.md"
12
12
  authors = [
13
13
  { name = "Richard Wang" },
@@ -18,10 +18,10 @@ classifiers = [
18
18
  "Intended Audience :: Developers",
19
19
  "License :: OSI Approved :: Apache Software License",
20
20
  "Programming Language :: Python :: 3",
21
- "Programming Language :: Python :: 3.10",
22
21
  "Programming Language :: Python :: 3.11",
23
22
  "Programming Language :: Python :: 3.12",
24
23
  "Programming Language :: Python :: 3.13",
24
+ "Programming Language :: Python :: 3.14",
25
25
  "Topic :: Software Development :: Libraries",
26
26
  ]
27
27
  dependencies = [
@@ -34,12 +34,13 @@ Repository = "https://github.com/i-richardwang/tableau-cli"
34
34
 
35
35
  [project.optional-dependencies]
36
36
  convert = [
37
- "pantab>=4.0",
38
- "polars>=1.0",
39
- "pyarrow>=15.0",
37
+ "tableauhyperapi>=0.0.26479",
40
38
  ]
41
39
  dev = [
42
40
  "ruff>=0.11",
41
+ "pytest>=8.0",
42
+ "pyarrow>=15.0",
43
+ "tzdata",
43
44
  ]
44
45
 
45
46
  [project.scripts]
@@ -49,7 +50,7 @@ tableau-cli = "tableau_cli.cli:main"
49
50
  packages = ["src/tableau_cli"]
50
51
 
51
52
  [tool.ruff]
52
- target-version = "py310"
53
+ target-version = "py311"
53
54
  line-length = 120
54
55
 
55
56
  [tool.ruff.lint]
@@ -78,7 +78,7 @@ tableau-cli ds download <datasource-id> -o ./output/ --to csv
78
78
 
79
79
  Output: `{"filePath": "<absolute path>"}`
80
80
 
81
- When `--to parquet` or `--to csv` is specified, the datasource is downloaded, converted in a temporary directory, and only the final file is written to the output path. Requires `tableau-cli[convert]` extras.
81
+ When `--to parquet` or `--to csv` is specified, the datasource is downloaded, converted in a temporary directory, and only the final file is written to the output path. Runs locally through Hyper API; requires `tableau-cli[convert]` or `uv` on `PATH`.
82
82
 
83
83
  ### ds metadata
84
84
 
@@ -256,7 +256,7 @@ Results include name, description, parent project, content permissions, owner, a
256
256
 
257
257
  ## convert
258
258
 
259
- Local file conversion: TDSX/HYPER → Parquet/CSV. Requires `tableau-cli[convert]`.
259
+ Local file conversion: TDSX/HYPER → Parquet/CSV. Uses the official local Hyper engine; requires `tableau-cli[convert]` or `uv` on `PATH`.
260
260
 
261
261
  ```bash
262
262
  tableau-cli convert data.tdsx
@@ -273,7 +273,9 @@ tableau-cli convert extract.hyper -o ./output/result.parquet
273
273
 
274
274
  Output: `{"filePath": "<absolute path>"}`
275
275
 
276
- Reports `missing-dependencies` error if convert extras are not installed, with hint to run `pip install "tableau-cli[convert]"`.
276
+ Requires exactly one table per extract. CSV includes a header and uses ISO dates; Parquet uses Hyper's native type mappings. The output is replaced only after a successful export.
277
+
278
+ Reports `missing-dependencies` when neither Hyper API nor `uv` is available. Engine startup and export failures are reported as `convert-error`.
277
279
 
278
280
  ---
279
281
 
@@ -9,7 +9,9 @@ description: Installation and authentication setup for tableau-cli. Load when th
9
9
  pip install "tableau-cli[convert]"
10
10
  ```
11
11
 
12
- Without `[convert]`, only core features are available (search, datasources, views, workbooks) — the `convert` command and `ds download --to parquet/csv` will not work.
12
+ Requires Python 3.11 or newer. The `[convert]` extra installs the official `tableauhyperapi` package for local Parquet/CSV exports. Use native 64-bit Python on Apple Silicon or Intel macOS, Windows x86_64, or a supported Linux x86_64 distribution.
13
+
14
+ Alternatively, install the core CLI and keep `uv` on `PATH`. Conversion then runs in an isolated environment provisioned automatically by `uv`; the first run installs the export dependency and later runs reuse the cache. Core commands do not need conversion dependencies.
13
15
 
14
16
  ## Upgrade
15
17
 
@@ -123,7 +123,7 @@ class TableauCli(click.Group):
123
123
 
124
124
 
125
125
  @click.group(cls=TableauCli)
126
- @click.version_option("0.1.4")
126
+ @click.version_option(package_name="tableau-cli")
127
127
  def cli():
128
128
  """CLI tool for interacting with Tableau Server/Cloud."""
129
129
 
@@ -43,6 +43,12 @@ def convert_command(input_path, to_fmt, output_path):
43
43
  else:
44
44
  out_p = Path(output_path)
45
45
 
46
+ if input_p.resolve() == out_p.resolve():
47
+ raise CliError(
48
+ error_type="invalid-input",
49
+ message="The output path must differ from the input file.",
50
+ )
51
+
46
52
  # Convert (in-process if deps present, else via an ephemeral uv environment)
47
53
  if suffix == ".hyper":
48
54
  run_conversion(input_p, out_p, to_fmt)
@@ -0,0 +1,81 @@
1
+ """Local Hyper exports, shared by the CLI and isolated Python execution."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import sys
7
+ from pathlib import Path
8
+ from tempfile import TemporaryDirectory
9
+
10
+ HYPER_REQUIREMENT = "tableauhyperapi>=0.0.26479"
11
+ SUPPORTED_FORMATS = ("parquet", "csv")
12
+
13
+
14
+ def _start_hyper():
15
+ from tableauhyperapi import HyperException, HyperProcess, Telemetry
16
+
17
+ try:
18
+ return HyperProcess(
19
+ Telemetry.DO_NOT_SEND_USAGE_DATA_TO_TABLEAU,
20
+ parameters={"log_config": "", "date_style": "YMD"},
21
+ )
22
+ except HyperException as exc:
23
+ raise RuntimeError(
24
+ "Unable to start the local Hyper engine. Use a native 64-bit Python on a supported platform "
25
+ f"and reinstall tableau-cli[convert]. Details: {exc}"
26
+ ) from exc
27
+
28
+
29
+ def check_runtime() -> None:
30
+ """Verify that the installed Hyper engine can start."""
31
+ with _start_hyper():
32
+ pass
33
+
34
+
35
+ def export_hyper(hyper_path: Path, output_path: Path, to_fmt: str) -> None:
36
+ """Export a single-table Hyper file, replacing the destination only on success."""
37
+ from tableauhyperapi import Connection, escape_string_literal
38
+
39
+ if to_fmt not in SUPPORTED_FORMATS:
40
+ raise ValueError(f"Unsupported format: {to_fmt}")
41
+ source = hyper_path.resolve()
42
+ destination = output_path.absolute()
43
+ if source == destination.resolve():
44
+ raise ValueError("The output path must differ from the input Hyper file.")
45
+
46
+ with TemporaryDirectory(prefix=".tableau-export-", dir=destination.parent) as td:
47
+ staged = Path(td) / f"data.{to_fmt}"
48
+ with _start_hyper() as hyper, Connection(hyper.endpoint, str(source)) as connection:
49
+ tables = [
50
+ table
51
+ for schema in connection.catalog.get_schema_names()
52
+ for table in connection.catalog.get_table_names(schema)
53
+ ]
54
+ if not tables:
55
+ raise ValueError(f"No tables found in {hyper_path.name}")
56
+ if len(tables) != 1:
57
+ raise ValueError(f"Found {len(tables)} tables in {hyper_path.name}, expected 1")
58
+
59
+ options = f"FORMAT => {escape_string_literal(to_fmt)}"
60
+ if to_fmt == "csv":
61
+ options += ", HEADER => true"
62
+ connection.execute_command(f"COPY {tables[0]} TO {escape_string_literal(str(staged))} WITH ({options})")
63
+ staged.replace(destination)
64
+
65
+
66
+ def main() -> None:
67
+ """Report worker failures as structured JSON for the parent CLI."""
68
+ try:
69
+ if sys.argv[1:] == ["--check"]:
70
+ check_runtime()
71
+ elif len(sys.argv) == 4:
72
+ export_hyper(Path(sys.argv[1]), Path(sys.argv[2]), sys.argv[3])
73
+ else:
74
+ raise ValueError("Expected --check or <hyper_path> <output_path> <parquet|csv>")
75
+ except Exception as exc:
76
+ print(json.dumps({"error_type": "convert-error", "message": str(exc)}))
77
+ sys.exit(1)
78
+
79
+
80
+ if __name__ == "__main__":
81
+ main()