wealthbox-cli 2.0.0__tar.gz → 2.2.0__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 (104) hide show
  1. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/.github/workflows/ci.yml +15 -16
  2. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/.github/workflows/docs.yml +5 -4
  3. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/CHANGELOG.md +58 -0
  4. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/CONTRIBUTING.md +22 -14
  5. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/PKG-INFO +1 -2
  6. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/SECURITY.md +1 -1
  7. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/docs/getting-started.md +1 -7
  8. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/mkdocs.yml +1 -0
  9. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/pyproject.toml +1 -3
  10. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/cli/_util.py +2 -10
  11. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/cli/doctor.py +3 -11
  12. wealthbox_cli-2.2.0/src/wealthbox_tools/cli/me.py +68 -0
  13. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/cli/skills.py +1 -1
  14. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/cli/users.py +1 -1
  15. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/skills/wealthbox-crm/SKILL.md +1 -1
  16. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/skills/wealthbox-crm/references/contacts.md +1 -1
  17. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/skills/wealthbox-crm/references/lookups.md +29 -1
  18. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/skills/wealthbox-crm/references/tasks.md +1 -1
  19. wealthbox_cli-2.2.0/uv.lock +635 -0
  20. wealthbox_cli-2.0.0/.env.example +0 -2
  21. wealthbox_cli-2.0.0/src/wealthbox_tools/cli/me.py +0 -25
  22. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/.github/workflows/release-binaries.yml +0 -0
  23. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/.gitignore +0 -0
  24. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/LICENSE +0 -0
  25. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/README.md +0 -0
  26. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/docs/agents/domain.md +0 -0
  27. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/docs/agents/issue-tracker.md +0 -0
  28. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/docs/agents/triage-labels.md +0 -0
  29. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/docs/cli-reference.md +0 -0
  30. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/docs/index.md +0 -0
  31. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/docs/overrides/main.html +0 -0
  32. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/docs/promotion.md +0 -0
  33. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/__init__.py +0 -0
  34. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/cli/__init__.py +0 -0
  35. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/cli/_config.py +0 -0
  36. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/cli/_skill_bootstrap.py +0 -0
  37. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/cli/_skill_paths.py +0 -0
  38. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/cli/_skill_platforms.py +0 -0
  39. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/cli/activity.py +0 -0
  40. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/cli/categories.py +0 -0
  41. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/cli/config.py +0 -0
  42. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/cli/contacts.py +0 -0
  43. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/cli/events.py +0 -0
  44. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/cli/firm.py +0 -0
  45. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/cli/households.py +0 -0
  46. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/cli/internals.py +0 -0
  47. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/cli/main.py +0 -0
  48. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/cli/notes.py +0 -0
  49. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/cli/opportunities.py +0 -0
  50. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/cli/prefs.py +0 -0
  51. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/cli/projects.py +0 -0
  52. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/cli/self_cmd.py +0 -0
  53. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/cli/tasks.py +0 -0
  54. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/cli/workflows.py +0 -0
  55. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/client/__init__.py +0 -0
  56. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/client/activity.py +0 -0
  57. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/client/base.py +0 -0
  58. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/client/categories.py +0 -0
  59. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/client/comments.py +0 -0
  60. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/client/contacts.py +0 -0
  61. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/client/events.py +0 -0
  62. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/client/households.py +0 -0
  63. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/client/me.py +0 -0
  64. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/client/notes.py +0 -0
  65. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/client/opportunities.py +0 -0
  66. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/client/projects.py +0 -0
  67. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/client/tasks.py +0 -0
  68. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/client/users.py +0 -0
  69. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/client/workflows.py +0 -0
  70. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/firm/__init__.py +0 -0
  71. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/firm/archive.py +0 -0
  72. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/firm/diff.py +0 -0
  73. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/internals/__init__.py +0 -0
  74. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/internals/skill_ref_gen.py +0 -0
  75. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/models/__init__.py +0 -0
  76. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/models/activity.py +0 -0
  77. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/models/comments.py +0 -0
  78. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/models/common.py +0 -0
  79. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/models/contacts.py +0 -0
  80. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/models/custom_fields.py +0 -0
  81. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/models/enums.py +0 -0
  82. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/models/events.py +0 -0
  83. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/models/households.py +0 -0
  84. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/models/notes.py +0 -0
  85. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/models/opportunities.py +0 -0
  86. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/models/projects.py +0 -0
  87. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/models/tasks.py +0 -0
  88. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/models/workflows.py +0 -0
  89. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/py.typed +0 -0
  90. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/self_upgrade.py +0 -0
  91. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/skills/wealthbox-crm/bootstrap.md +0 -0
  92. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/skills/wealthbox-crm/firm-examples/contacts.md +0 -0
  93. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/skills/wealthbox-crm/firm-examples/events.md +0 -0
  94. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/skills/wealthbox-crm/firm-examples/notes.md +0 -0
  95. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/skills/wealthbox-crm/firm-examples/opportunities.md +0 -0
  96. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/skills/wealthbox-crm/firm-examples/projects.md +0 -0
  97. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/skills/wealthbox-crm/firm-examples/tasks.md +0 -0
  98. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/skills/wealthbox-crm/firm-examples/workflows.md +0 -0
  99. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/skills/wealthbox-crm/references/events.md +0 -0
  100. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/skills/wealthbox-crm/references/households.md +0 -0
  101. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/skills/wealthbox-crm/references/notes.md +0 -0
  102. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/skills/wealthbox-crm/references/opportunities.md +0 -0
  103. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/skills/wealthbox-crm/references/projects.md +0 -0
  104. {wealthbox_cli-2.0.0 → wealthbox_cli-2.2.0}/src/wealthbox_tools/skills/wealthbox-crm/references/workflows.md +0 -0
@@ -11,12 +11,12 @@ jobs:
11
11
  lint:
12
12
  runs-on: ubuntu-latest
13
13
  steps:
14
- - uses: actions/checkout@v4
15
- - uses: actions/setup-python@v5
14
+ - uses: actions/checkout@v6
15
+ - uses: astral-sh/setup-uv@v8.1.0
16
16
  with:
17
17
  python-version: "3.12"
18
- - run: pip install -e ".[dev]"
19
- - run: ruff check src/ tests/
18
+ - run: uv sync --extra dev
19
+ - run: uv run ruff check src/ tests/
20
20
 
21
21
  test:
22
22
  runs-on: ubuntu-latest
@@ -24,23 +24,23 @@ jobs:
24
24
  matrix:
25
25
  python-version: ["3.11", "3.12", "3.13"]
26
26
  steps:
27
- - uses: actions/checkout@v4
28
- - uses: actions/setup-python@v5
27
+ - uses: actions/checkout@v6
28
+ - uses: astral-sh/setup-uv@v8.1.0
29
29
  with:
30
30
  python-version: ${{ matrix.python-version }}
31
- - run: pip install -e ".[dev]"
32
- - run: pytest
31
+ - run: uv sync --extra dev
32
+ - run: uv run pytest
33
33
 
34
34
  skill-ref-drift:
35
35
  needs: [lint, test]
36
36
  runs-on: ubuntu-latest
37
37
  steps:
38
- - uses: actions/checkout@v4
39
- - uses: actions/setup-python@v5
38
+ - uses: actions/checkout@v6
39
+ - uses: astral-sh/setup-uv@v8.1.0
40
40
  with:
41
41
  python-version: "3.12"
42
- - run: pip install -e ".[dev]"
43
- - run: wbox internals regen-skill-refs
42
+ - run: uv sync --extra dev
43
+ - run: uv run wbox internals regen-skill-refs
44
44
  - run: git diff --exit-code -- src/wealthbox_tools/skills/wealthbox-crm/references/
45
45
 
46
46
  publish:
@@ -51,10 +51,9 @@ jobs:
51
51
  id-token: write
52
52
  environment: pypi
53
53
  steps:
54
- - uses: actions/checkout@v4
55
- - uses: actions/setup-python@v5
54
+ - uses: actions/checkout@v6
55
+ - uses: astral-sh/setup-uv@v8.1.0
56
56
  with:
57
57
  python-version: "3.12"
58
- - run: pip install build
59
- - run: python -m build
58
+ - run: uv build
60
59
  - uses: pypa/gh-action-pypi-publish@release/v1
@@ -18,14 +18,15 @@ jobs:
18
18
  build:
19
19
  runs-on: ubuntu-latest
20
20
  steps:
21
- - uses: actions/checkout@v4
22
- - uses: actions/setup-python@v5
21
+ - uses: actions/checkout@v6
22
+ - uses: actions/setup-python@v6
23
23
  with:
24
24
  python-version: "3.12"
25
25
  - run: pip install mkdocs-material
26
26
  - run: cp CONTRIBUTING.md docs/contributing.md
27
+ - run: cp CHANGELOG.md docs/changelog.md
27
28
  - run: mkdocs build --strict
28
- - uses: actions/upload-pages-artifact@v3
29
+ - uses: actions/upload-pages-artifact@v5
29
30
  with:
30
31
  path: site/
31
32
 
@@ -37,4 +38,4 @@ jobs:
37
38
  url: ${{ steps.deployment.outputs.page_url }}
38
39
  steps:
39
40
  - id: deployment
40
- uses: actions/deploy-pages@v4
41
+ uses: actions/deploy-pages@v5
@@ -4,6 +4,64 @@ All notable changes to `wealthbox-cli` are documented here. The format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
5
5
  adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [2.2.0] - 2026-05-13
8
+
9
+ Small feature + correctness release. Disambiguates the two IDs returned by
10
+ `wbox me`, fixes a silent truncation in `wbox users list` on large firms, and
11
+ clears Node 20 deprecation warnings from CI ahead of GitHub's June 2026 cutoff.
12
+
13
+ ### Added
14
+ - `wbox me user-id` — prints `current_user.id` (the workspace user ID accepted
15
+ by `--assigned-to`) as a bare integer, for composing with command
16
+ substitution: `wbox tasks list --assigned-to "$(wbox me user-id)"` (#90, #91).
17
+ - `wbox me --format table` now labels the two IDs as `login_id` and
18
+ `user_id (--assigned-to)` so they aren't confusable at a glance. The JSON
19
+ shape is unchanged — no breaking change for existing scripts (#91).
20
+ - Skill docs (`lookups.md`, `tasks.md`, `contacts.md`) flag the two-ID trap
21
+ inline near `--assigned-to`.
22
+
23
+ ### Fixed
24
+ - `wbox users list` now paginates across all pages. Previously, firms with
25
+ more than 25 users silently saw a truncated list — the CLI exposes no
26
+ `--page` flag, so there was no manual workaround.
27
+ - mkdocs docs deploy was failing on every main push because `docs/index.md`
28
+ linked to a missing `changelog.md`. `CHANGELOG.md` is now staged into
29
+ `docs/` at build time and listed in nav (#92).
30
+
31
+ ### Changed
32
+ - CI actions bumped to Node 24 runtimes ahead of GitHub's June 2 2026 cutoff:
33
+ `actions/checkout` v4→v6, `astral-sh/setup-uv` v6→v8.1.0 (pinned exact —
34
+ setup-uv v8+ no longer publishes rolling major tags as a supply-chain
35
+ mitigation), `actions/setup-python` v5→v6, `actions/deploy-pages` v4→v5,
36
+ `actions/upload-pages-artifact` v3→v5 (#92, #93).
37
+
38
+ ## [2.1.0] - 2026-05-09
39
+
40
+ Repository simplification release. Drops a runtime dep, retires `.env` support,
41
+ and switches dev tooling to [uv](https://docs.astral.sh/uv/).
42
+
43
+ ### Breaking
44
+ - **Removed `.env` file support.** Token resolution is now a 3-tier chain:
45
+ `--token` flag → `WEALTHBOX_TOKEN` env var → config file. Users relying on a
46
+ working-directory `.env` should migrate to `wbox config set-token` (preferred)
47
+ or export `WEALTHBOX_TOKEN` in their shell. The `python-dotenv` runtime
48
+ dependency was dropped.
49
+
50
+ ### Changed
51
+ - Development tooling moved to [uv](https://docs.astral.sh/uv/). `uv sync
52
+ --extra dev` replaces the manual `python -m venv` + activate + `pip install
53
+ -e` dance, and `uv run wbox …` replaces direct `.venv/bin/wbox` invocation.
54
+ CI now uses `astral-sh/setup-uv@v6` and `uv build` for the publish job. Plain
55
+ `pip install -e ".[dev]"` continues to work — `pyproject.toml` is the source
56
+ of truth for both tools.
57
+ - `scripts/run-wbox.sh` and `scripts/smoke_test.sh` invoke via `uv run` and no
58
+ longer source a `.env` file.
59
+
60
+ ### Removed
61
+ - `.env` and `.env.example` files at the repo root.
62
+ - `.python-version` file. `requires-python = ">=3.11"` in `pyproject.toml` is
63
+ the authoritative interpreter constraint; uv reads it directly.
64
+
7
65
  ## [2.0.0] - 2026-05-09
8
66
 
9
67
  v2 retires the Claude marketplace plugin distribution and ships `wbox` as a
@@ -6,38 +6,46 @@ Thank you for your interest in contributing to **wealthbox-cli**!
6
6
 
7
7
  ## Development Setup
8
8
 
9
+ This project uses [uv](https://docs.astral.sh/uv/) for dependency management. Install it first if you don't have it:
10
+
9
11
  ```bash
10
- git clone https://github.com/massive-value/wealthbox-cli
11
- cd wealthbox-cli
12
- python -m venv .venv
12
+ # macOS / Linux
13
+ curl -LsSf https://astral.sh/uv/install.sh | sh
14
+
15
+ # Windows (PowerShell)
16
+ irm https://astral.sh/uv/install.ps1 | iex
13
17
  ```
14
18
 
15
- Activate the virtual environment:
19
+ Then clone and sync:
16
20
 
17
- | Platform | Command |
18
- |----------|---------|
19
- | macOS/Linux | `source .venv/bin/activate` |
20
- | Windows (PowerShell) | `.venv\Scripts\Activate.ps1` |
21
- | Windows (Command Prompt) | `.venv\Scripts\activate.bat` |
21
+ ```bash
22
+ git clone https://github.com/massive-value/wealthbox-cli
23
+ cd wealthbox-cli
24
+ uv sync --extra dev
25
+ ```
22
26
 
23
- Then install with dev dependencies:
27
+ `uv sync` creates a project-local `.venv/` and installs the package in editable mode with dev dependencies. There is nothing to "activate" — prefix commands with `uv run`:
24
28
 
25
29
  ```bash
26
- pip install -e ".[dev]"
30
+ uv run wbox --version
31
+ uv run pytest
32
+ uv run ruff check src/ tests/
27
33
  ```
28
34
 
29
35
  Configure your Wealthbox API token (optional — tests don't require a real token):
30
36
 
31
37
  ```bash
32
- wbox config set-token
38
+ uv run wbox config set-token
33
39
  ```
34
40
 
41
+ > Prefer plain `pip`? `pip install -e ".[dev]"` still works — `pyproject.toml` is the source of truth for both tools.
42
+
35
43
  ---
36
44
 
37
45
  ## Running Tests
38
46
 
39
47
  ```bash
40
- pytest
48
+ uv run pytest
41
49
  ```
42
50
 
43
51
  Tests use [respx](https://lundberg.github.io/respx/) to mock HTTP at the transport layer — no real API calls are made.
@@ -45,7 +53,7 @@ Tests use [respx](https://lundberg.github.io/respx/) to mock HTTP at the transpo
45
53
  ## Code Style
46
54
 
47
55
  ```bash
48
- ruff check src/ tests/
56
+ uv run ruff check src/ tests/
49
57
  ```
50
58
 
51
59
  - **ruff** for linting (E, F, I rules; 120-char line length)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wealthbox-cli
3
- Version: 2.0.0
3
+ Version: 2.2.0
4
4
  Summary: CLI and client library for the Wealthbox CRM API
5
5
  Project-URL: Homepage, https://github.com/massive-value/wealthbox-cli
6
6
  Project-URL: Documentation, https://massive-value.github.io/wealthbox-cli/
@@ -28,7 +28,6 @@ Classifier: Typing :: Typed
28
28
  Requires-Python: >=3.11
29
29
  Requires-Dist: httpx<1.0,>=0.27
30
30
  Requires-Dist: pydantic<3.0,>=2.0
31
- Requires-Dist: python-dotenv<2.0,>=1.0
32
31
  Requires-Dist: tabulate<1.0,>=0.9
33
32
  Requires-Dist: typer<1.0,>=0.12
34
33
  Provides-Extra: dev
@@ -18,7 +18,7 @@ You will receive a response within 5 business days. If the issue is confirmed, a
18
18
 
19
19
  Your Wealthbox API token grants access to your CRM data. Keep it safe:
20
20
 
21
- - **Never commit your token to version control.** Use `.env` (already in `.gitignore`) or `wbox config set-token` to store it securely.
21
+ - **Never commit your token to version control.** Use `wbox config set-token` to store it securely (written to your platform config dir, not the repo).
22
22
  - **Never share your token** in issues, PRs, or chat.
23
23
  - If a token is accidentally exposed, regenerate it immediately in your Wealthbox account settings under **Settings > API Access**.
24
24
 
@@ -89,19 +89,13 @@ For CI, scripting, or containers:
89
89
  export WEALTHBOX_TOKEN="your_api_token_here"
90
90
  ```
91
91
 
92
- === ".env file"
93
-
94
- ```bash
95
- echo 'WEALTHBOX_TOKEN=your_api_token_here' > .env
96
- ```
97
-
98
92
  === "Per-command flag"
99
93
 
100
94
  ```bash
101
95
  wbox contacts list --token your_api_token_here
102
96
  ```
103
97
 
104
- Token is resolved in this order: `--token` flag → `WEALTHBOX_TOKEN` env var → config file → `.env` file.
98
+ Token is resolved in this order: `--token` flag → `WEALTHBOX_TOKEN` env var → config file.
105
99
 
106
100
  ---
107
101
 
@@ -59,6 +59,7 @@ nav:
59
59
  - Getting Started: getting-started.md
60
60
  - CLI Reference: cli-reference.md
61
61
  - Contributing: contributing.md
62
+ - Changelog: changelog.md
62
63
 
63
64
  extra:
64
65
  social:
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "wealthbox-cli"
7
- version = "2.0.0"
7
+ version = "2.2.0"
8
8
  description = "CLI and client library for the Wealthbox CRM API"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -44,7 +44,6 @@ dependencies = [
44
44
  "pydantic>=2.0,<3.0",
45
45
  "httpx>=0.27,<1.0",
46
46
  "typer>=0.12,<1.0",
47
- "python-dotenv>=1.0,<2.0",
48
47
  "tabulate>=0.9,<1.0",
49
48
  ]
50
49
 
@@ -77,7 +76,6 @@ exclude = [
77
76
  "resources/",
78
77
  "scripts/",
79
78
  "dev/",
80
- ".python-version",
81
79
  "docs/superpowers/",
82
80
  ]
83
81
 
@@ -49,25 +49,17 @@ COMMENT_RESOURCE_TYPES: dict[str, str] = {
49
49
 
50
50
 
51
51
  def get_client(token: str | None = None) -> WealthboxClient:
52
- """Create a WealthboxClient with token resolution: --token flag > env var > config file > .env."""
52
+ """Create a WealthboxClient with token resolution: --token flag > env var > config file."""
53
53
  import os
54
54
  if token is None:
55
55
  token = os.environ.get("WEALTHBOX_TOKEN")
56
56
  if token is None:
57
57
  from ._config import get_stored_token
58
58
  token = get_stored_token()
59
- if token is None:
60
- try:
61
- from dotenv import load_dotenv
62
- load_dotenv()
63
- token = os.environ.get("WEALTHBOX_TOKEN")
64
- except ImportError:
65
- pass
66
59
  if not token:
67
60
  raise ValueError(
68
61
  "Wealthbox token required. Provide one via any of: "
69
- "--token flag, WEALTHBOX_TOKEN env var, "
70
- "`wbox config set-token`, or a .env file in the working directory."
62
+ "--token flag, WEALTHBOX_TOKEN env var, or `wbox config set-token`."
71
63
  )
72
64
  return WealthboxClient(token=token)
73
65
 
@@ -28,7 +28,7 @@ from .self_cmd import _default_install_root
28
28
 
29
29
  def _detect_token_source(token_arg: str | None) -> tuple[str | None, str]:
30
30
  """Return (token, source_label) matching get_client's resolution order:
31
- --token flag > WEALTHBOX_TOKEN env > config file > .env (loaded as env)."""
31
+ --token flag > WEALTHBOX_TOKEN env > config file."""
32
32
  if token_arg:
33
33
  return token_arg, "--token flag"
34
34
  env_token = os.environ.get("WEALTHBOX_TOKEN")
@@ -38,14 +38,6 @@ def _detect_token_source(token_arg: str | None) -> tuple[str | None, str]:
38
38
  cfg_token = cfg.get("token")
39
39
  if cfg_token:
40
40
  return cfg_token, f"config file ({_config_path()})"
41
- try:
42
- from dotenv import load_dotenv
43
- load_dotenv()
44
- dotenv_token = os.environ.get("WEALTHBOX_TOKEN")
45
- if dotenv_token:
46
- return dotenv_token, ".env file in working directory"
47
- except ImportError:
48
- pass
49
41
  return None, "not set"
50
42
 
51
43
 
@@ -257,7 +249,7 @@ def _ensure_firm_migrated():
257
249
  def doctor_cmd(
258
250
  token: str | None = typer.Option(
259
251
  None, "--token",
260
- help="Override the API token for the smoke test. Overrides env var, config, .env.",
252
+ help="Override the API token for the smoke test. Overrides env var and config.",
261
253
  ),
262
254
  ) -> None:
263
255
  """Run the comprehensive doctor.
@@ -266,6 +258,6 @@ def doctor_cmd(
266
258
  is informational, not a CI gate. (A future --strict flag could change
267
259
  this for scripting.) Token is intentionally NOT bound to
268
260
  `envvar=WEALTHBOX_TOKEN` so the doctor can report whether the token came
269
- from the flag, env var, config file, or .env file.
261
+ from the flag, env var, or config file.
270
262
  """
271
263
  run_doctor(token=token)
@@ -0,0 +1,68 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ import typer
6
+
7
+ from ._util import (
8
+ OutputFormat,
9
+ _flatten_record,
10
+ _render_kv_table,
11
+ handle_errors,
12
+ output_result,
13
+ run_client,
14
+ )
15
+
16
+ app = typer.Typer(
17
+ context_settings={"help_option_names": ["-h", "--help"]},
18
+ help="Show info about the current authenticated user.",
19
+ )
20
+
21
+
22
+ def _relabel_for_table(data: dict[str, Any]) -> dict[str, Any]:
23
+ """Rename top-level `id` to `login_id` and add `user_id (--assigned-to)`
24
+ so the two IDs aren't confusable in tabular output.
25
+
26
+ The top-level `id` is a login profile (not usable for `--assigned-to`);
27
+ `current_user.id` is the workspace user ID that filters accept.
28
+ """
29
+ relabeled: dict[str, Any] = {}
30
+ for key, value in data.items():
31
+ relabeled["login_id" if key == "id" else key] = value
32
+ current_user = data.get("current_user")
33
+ if isinstance(current_user, dict) and "id" in current_user:
34
+ relabeled["user_id (--assigned-to)"] = current_user["id"]
35
+ return relabeled
36
+
37
+
38
+ @app.callback(
39
+ invoke_without_command=True,
40
+ help="Show info about the current authenticated user. Use subcommands for more specific info.",
41
+ )
42
+ @handle_errors
43
+ def get_me(
44
+ ctx: typer.Context,
45
+ token: str | None = typer.Option(None, envvar="WEALTHBOX_TOKEN", hidden=True),
46
+ fmt: OutputFormat = typer.Option(OutputFormat.JSON, "--format"),
47
+ ) -> None:
48
+ if ctx.invoked_subcommand is not None:
49
+ return
50
+ data = run_client(token, lambda c: c.get_me())
51
+ if fmt == OutputFormat.TABLE and isinstance(data, dict):
52
+ # Bypass output_result's collection-detection heuristic: once we drop
53
+ # the top-level `id` key, it would mistake `users[]` for a collection.
54
+ typer.echo(_render_kv_table(_flatten_record(_relabel_for_table(data))))
55
+ return
56
+ output_result(data, fmt)
57
+
58
+
59
+ @app.command("user-id", help="Print the workspace user ID (for --assigned-to) as a bare integer.")
60
+ @handle_errors
61
+ def user_id(
62
+ token: str | None = typer.Option(None, envvar="WEALTHBOX_TOKEN", hidden=True),
63
+ ) -> None:
64
+ data = run_client(token, lambda c: c.get_me())
65
+ current_user = data.get("current_user") if isinstance(data, dict) else None
66
+ if not isinstance(current_user, dict) or "id" not in current_user:
67
+ raise ValueError("`current_user.id` missing from /me response; cannot determine user ID.")
68
+ typer.echo(current_user["id"])
@@ -295,7 +295,7 @@ def doctor_cmd(
295
295
  # canonical top-level form. Both call the same function so the output
296
296
  # never drifts. Token is intentionally not bound to envvar= so the
297
297
  # doctor can report whether the token came from the flag, env var,
298
- # config file, or .env file.
298
+ # or config file.
299
299
  from .doctor import run_doctor
300
300
  run_doctor(token=token)
301
301
 
@@ -21,4 +21,4 @@ def list_users(
21
21
  token: str | None = typer.Option(None, envvar="WEALTHBOX_TOKEN", hidden=True),
22
22
  fmt: OutputFormat = typer.Option(OutputFormat.JSON, "--format"),
23
23
  ) -> None:
24
- output_result(run_client(token, lambda c: c.list_users()), fmt, fields=None if verbose else _DEFAULT_FIELDS)
24
+ output_result(run_client(token, lambda c: c.list_all_users()), fmt, fields=None if verbose else _DEFAULT_FIELDS)
@@ -101,7 +101,7 @@ across every installed platform while preserving `firm/`.
101
101
  # Windows (PowerShell)
102
102
  irm https://raw.githubusercontent.com/massive-value/wealthbox-cli/main/scripts/install.ps1 | iex
103
103
  ```
104
- - Token configured via one of: `wbox config set-token`, `WEALTHBOX_TOKEN` env var, or `.env` file
104
+ - Token configured via one of: `wbox config set-token` or `WEALTHBOX_TOKEN` env var
105
105
 
106
106
  Verify with: `wbox me`
107
107
 
@@ -20,7 +20,7 @@ wbox contacts list [OPTIONS]
20
20
  | `--household-title` | STR | Filter by household title |
21
21
  | `--tags` | STR | Comma-separated tag names |
22
22
  | `--order` | asc\|desc | Sort order |
23
- | `--assigned-to` | INT | User ID (fetches all pages, client-side filter) |
23
+ | `--assigned-to` | INT | User ID (fetches all pages, client-side filter). Get yours via `wbox me user-id`, **not** `wbox me \| jq .id` (that's the login profile, which silently returns zero results). |
24
24
  | `--updated-since` | ISO datetime | Modified after this datetime |
25
25
  | `--updated-before` | ISO datetime | Modified before this datetime |
26
26
  | `--deleted-since` | ISO datetime | Deleted after this datetime |
@@ -6,10 +6,35 @@ Read-only resources: categories, users, activity feed, and current user info.
6
6
 
7
7
  ```bash
8
8
  wbox me [--format json|table|csv|tsv]
9
+ wbox me user-id
9
10
  ```
10
11
 
11
12
  Returns info about the authenticated user. Good for verifying token setup and getting your user ID.
12
13
 
14
+ ### Two IDs — don't confuse them
15
+
16
+ `wbox me` returns two distinct ID fields, and they are NOT interchangeable:
17
+
18
+ | JSON field | What it is | Use for `--assigned-to`? |
19
+ |------------|------------|--------------------------|
20
+ | top-level `id` | Login profile ID (one per Wealthbox account, across workspaces) | **No** |
21
+ | `current_user.id` (also `users[].id` for the active workspace) | Workspace user ID | **Yes** |
22
+
23
+ Filters like `--assigned-to`, `--assigned-to-team`, and `--created-by` expect the workspace user ID. Passing the top-level `id` returns zero results silently — the API just ignores the filter.
24
+
25
+ The safe ways to get your workspace user ID:
26
+
27
+ ```bash
28
+ wbox me user-id # prints just the integer, ready for shell substitution
29
+ wbox me --format table # labels the rows `login_id` vs `user_id (--assigned-to)`
30
+ ```
31
+
32
+ Composing it:
33
+
34
+ ```bash
35
+ wbox tasks list --assigned-to "$(wbox me user-id)"
36
+ ```
37
+
13
38
  ## Users
14
39
 
15
40
  ```bash
@@ -64,7 +89,10 @@ The contact-related types are also reachable as aliases under `wbox contacts cat
64
89
  ## Examples
65
90
 
66
91
  ```bash
67
- # Get your user ID
92
+ # Get your workspace user ID (for --assigned-to)
93
+ wbox me user-id
94
+
95
+ # Or see both IDs side by side
68
96
  wbox me --format table
69
97
 
70
98
  # Find a user ID for assignment
@@ -13,7 +13,7 @@ wbox tasks list [OPTIONS]
13
13
  | `--contact` | INT | Filter by linked contact ID |
14
14
  | `--project` | INT | Filter by linked project ID |
15
15
  | `--opportunity` | INT | Filter by linked opportunity ID |
16
- | `--assigned-to` | INT | Filter by assigned user ID |
16
+ | `--assigned-to` | INT | Filter by assigned user ID — get yours via `wbox me user-id`, **not** `wbox me \| jq .id` (that's the login profile, which silently returns zero results). |
17
17
  | `--assigned-to-team` | INT | Filter by assigned team ID |
18
18
  | `--created-by` | INT | Filter by creator user ID |
19
19
  | `--include-completed` | flag | Include completed tasks (default: outstanding only) |