wealthbox-cli 2.3.1__tar.gz → 2.4.1__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 (112) hide show
  1. wealthbox_cli-2.4.1/.github/dependabot.yml +20 -0
  2. wealthbox_cli-2.4.1/.github/workflows/ci.yml +111 -0
  3. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/.github/workflows/release-binaries.yml +2 -2
  4. wealthbox_cli-2.4.1/.pre-commit-config.yaml +22 -0
  5. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/CHANGELOG.md +129 -0
  6. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/CONTRIBUTING.md +79 -3
  7. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/PKG-INFO +26 -1
  8. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/README.md +23 -0
  9. wealthbox_cli-2.4.1/docs/cli-reference.md +854 -0
  10. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/pyproject.toml +4 -1
  11. wealthbox_cli-2.4.1/src/wealthbox_tools/cli/_client.py +138 -0
  12. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/cli/_config.py +4 -1
  13. wealthbox_cli-2.4.1/src/wealthbox_tools/cli/_factory.py +289 -0
  14. wealthbox_cli-2.4.1/src/wealthbox_tools/cli/_format.py +259 -0
  15. wealthbox_cli-2.4.1/src/wealthbox_tools/cli/_resolve.py +175 -0
  16. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/cli/_skill_bootstrap.py +6 -1
  17. wealthbox_cli-2.4.1/src/wealthbox_tools/cli/_util.py +103 -0
  18. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/cli/doctor.py +2 -1
  19. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/cli/events.py +25 -30
  20. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/cli/notes.py +23 -22
  21. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/cli/opportunities.py +23 -29
  22. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/cli/projects.py +21 -21
  23. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/cli/tasks.py +30 -20
  24. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/cli/workflows.py +23 -21
  25. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/client/activity.py +6 -3
  26. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/client/base.py +71 -10
  27. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/client/categories.py +9 -6
  28. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/client/comments.py +10 -6
  29. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/client/contacts.py +18 -12
  30. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/client/events.py +16 -10
  31. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/client/households.py +9 -5
  32. wealthbox_cli-2.4.1/src/wealthbox_tools/client/me.py +14 -0
  33. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/client/notes.py +15 -9
  34. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/client/opportunities.py +16 -10
  35. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/client/projects.py +15 -9
  36. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/client/tasks.py +16 -10
  37. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/client/users.py +9 -6
  38. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/client/workflows.py +21 -13
  39. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/self_upgrade.py +3 -2
  40. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/uv.lock +186 -1
  41. wealthbox_cli-2.3.1/.github/workflows/ci.yml +0 -61
  42. wealthbox_cli-2.3.1/docs/cli-reference.md +0 -364
  43. wealthbox_cli-2.3.1/src/wealthbox_tools/cli/_util.py +0 -557
  44. wealthbox_cli-2.3.1/src/wealthbox_tools/client/me.py +0 -11
  45. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/.github/workflows/docs.yml +0 -0
  46. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/.gitignore +0 -0
  47. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/LICENSE +0 -0
  48. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/SECURITY.md +0 -0
  49. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/docs/agents/domain.md +0 -0
  50. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/docs/agents/issue-tracker.md +0 -0
  51. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/docs/agents/triage-labels.md +0 -0
  52. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/docs/getting-started.md +0 -0
  53. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/docs/index.md +0 -0
  54. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/docs/overrides/main.html +0 -0
  55. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/docs/promotion.md +0 -0
  56. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/mkdocs.yml +0 -0
  57. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/__init__.py +0 -0
  58. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/cli/__init__.py +0 -0
  59. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/cli/_skill_paths.py +0 -0
  60. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/cli/_skill_platforms.py +0 -0
  61. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/cli/activity.py +0 -0
  62. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/cli/categories.py +0 -0
  63. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/cli/config.py +0 -0
  64. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/cli/contacts.py +0 -0
  65. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/cli/firm.py +0 -0
  66. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/cli/households.py +0 -0
  67. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/cli/internals.py +0 -0
  68. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/cli/main.py +0 -0
  69. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/cli/me.py +0 -0
  70. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/cli/prefs.py +0 -0
  71. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/cli/self_cmd.py +0 -0
  72. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/cli/skills.py +0 -0
  73. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/cli/users.py +0 -0
  74. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/client/__init__.py +0 -0
  75. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/firm/__init__.py +0 -0
  76. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/firm/archive.py +0 -0
  77. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/firm/diff.py +0 -0
  78. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/internals/__init__.py +0 -0
  79. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/internals/skill_ref_gen.py +0 -0
  80. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/models/__init__.py +0 -0
  81. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/models/activity.py +0 -0
  82. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/models/comments.py +0 -0
  83. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/models/common.py +0 -0
  84. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/models/contacts.py +0 -0
  85. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/models/custom_fields.py +0 -0
  86. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/models/enums.py +0 -0
  87. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/models/events.py +0 -0
  88. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/models/households.py +0 -0
  89. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/models/notes.py +0 -0
  90. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/models/opportunities.py +0 -0
  91. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/models/projects.py +0 -0
  92. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/models/tasks.py +0 -0
  93. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/models/workflows.py +0 -0
  94. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/py.typed +0 -0
  95. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/skills/wealthbox-crm/SKILL.md +0 -0
  96. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/skills/wealthbox-crm/bootstrap.md +0 -0
  97. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/skills/wealthbox-crm/firm-examples/contacts.md +0 -0
  98. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/skills/wealthbox-crm/firm-examples/events.md +0 -0
  99. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/skills/wealthbox-crm/firm-examples/notes.md +0 -0
  100. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/skills/wealthbox-crm/firm-examples/opportunities.md +0 -0
  101. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/skills/wealthbox-crm/firm-examples/projects.md +0 -0
  102. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/skills/wealthbox-crm/firm-examples/tasks.md +0 -0
  103. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/skills/wealthbox-crm/firm-examples/workflows.md +0 -0
  104. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/skills/wealthbox-crm/references/contacts.md +0 -0
  105. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/skills/wealthbox-crm/references/events.md +0 -0
  106. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/skills/wealthbox-crm/references/households.md +0 -0
  107. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/skills/wealthbox-crm/references/lookups.md +0 -0
  108. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/skills/wealthbox-crm/references/notes.md +0 -0
  109. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/skills/wealthbox-crm/references/opportunities.md +0 -0
  110. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/skills/wealthbox-crm/references/projects.md +0 -0
  111. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/skills/wealthbox-crm/references/tasks.md +0 -0
  112. {wealthbox_cli-2.3.1 → wealthbox_cli-2.4.1}/src/wealthbox_tools/skills/wealthbox-crm/references/workflows.md +0 -0
@@ -0,0 +1,20 @@
1
+ version: 2
2
+
3
+ updates:
4
+ # Keep GitHub Actions up to date
5
+ - package-ecosystem: "github-actions"
6
+ directory: "/"
7
+ schedule:
8
+ interval: "weekly"
9
+ open-pull-requests-limit: 5
10
+ commit-message:
11
+ prefix: "chore(ci)"
12
+
13
+ # Keep Python dependencies up to date (pyproject.toml)
14
+ - package-ecosystem: "pip"
15
+ directory: "/"
16
+ schedule:
17
+ interval: "weekly"
18
+ open-pull-requests-limit: 10
19
+ commit-message:
20
+ prefix: "chore(deps)"
@@ -0,0 +1,111 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ tags: ["v*"]
7
+ pull_request:
8
+ branches: [main]
9
+
10
+ jobs:
11
+ lint:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v6
15
+ - uses: astral-sh/setup-uv@v8.1.0
16
+ with:
17
+ python-version: "3.12"
18
+ - run: uv sync --extra dev
19
+ - run: uv run ruff check src/ tests/
20
+
21
+ test:
22
+ runs-on: ${{ matrix.os }}
23
+ strategy:
24
+ matrix:
25
+ os: [ubuntu-latest]
26
+ python-version: ["3.11", "3.12", "3.13"]
27
+ include:
28
+ - os: windows-latest
29
+ python-version: "3.12"
30
+ steps:
31
+ - uses: actions/checkout@v6
32
+ - uses: astral-sh/setup-uv@v8.1.0
33
+ with:
34
+ python-version: ${{ matrix.python-version }}
35
+ - run: uv sync --extra dev
36
+ - name: Run tests (ubuntu, with coverage gate)
37
+ if: matrix.os == 'ubuntu-latest'
38
+ run: uv run pytest --cov=src/wealthbox_tools --cov-report=term --cov-fail-under=91
39
+ - name: Run tests (windows, no coverage gate)
40
+ if: matrix.os == 'windows-latest'
41
+ run: uv run pytest
42
+
43
+ typecheck:
44
+ runs-on: ubuntu-latest
45
+ steps:
46
+ - uses: actions/checkout@v6
47
+ - uses: astral-sh/setup-uv@v8.1.0
48
+ with:
49
+ python-version: "3.12"
50
+ - run: uv sync --extra dev
51
+ - run: uv run mypy src/
52
+
53
+ smoke:
54
+ # Install the built wheel in isolation WITHOUT dev dependencies and run the
55
+ # entrypoint, so a missing runtime dependency (e.g. a module-level import of
56
+ # a package typer no longer pulls in) is caught before publish. The normal
57
+ # test job always has dev deps present and cannot catch this.
58
+ runs-on: ${{ matrix.os }}
59
+ strategy:
60
+ matrix:
61
+ os: [ubuntu-latest, windows-latest]
62
+ steps:
63
+ - uses: actions/checkout@v6
64
+ - uses: astral-sh/setup-uv@v8.1.0
65
+ with:
66
+ python-version: "3.12"
67
+ - name: Build and smoke-test clean install
68
+ shell: bash
69
+ run: |
70
+ uv build
71
+ uv run --isolated --no-project --with "$(ls dist/*.whl)" wbox --version
72
+ uv run --isolated --no-project --with "$(ls dist/*.whl)" wbox notes --help
73
+
74
+ skill-ref-drift:
75
+ needs: [lint, test]
76
+ runs-on: ubuntu-latest
77
+ steps:
78
+ - uses: actions/checkout@v6
79
+ - uses: astral-sh/setup-uv@v8.1.0
80
+ with:
81
+ python-version: "3.12"
82
+ - run: uv sync --extra dev
83
+ - run: uv run wbox internals regen-skill-refs
84
+ - run: git diff --exit-code -- src/wealthbox_tools/skills/wealthbox-crm/references/
85
+
86
+ publish:
87
+ needs: [lint, test, typecheck, smoke, skill-ref-drift]
88
+ if: startsWith(github.ref, 'refs/tags/v')
89
+ runs-on: ubuntu-latest
90
+ permissions:
91
+ id-token: write
92
+ environment: pypi
93
+ steps:
94
+ - uses: actions/checkout@v6
95
+ - uses: astral-sh/setup-uv@v8.1.0
96
+ with:
97
+ python-version: "3.12"
98
+ - name: Check changelog matches version
99
+ run: |
100
+ python - <<'PY'
101
+ import re, pathlib, sys
102
+ ver = re.search(r'^version\s*=\s*"([^"]+)"', pathlib.Path("pyproject.toml").read_text(encoding="utf-8"), re.M).group(1)
103
+ m = re.search(r'^##\s*\[([0-9]+\.[0-9]+\.[0-9]+)\]', pathlib.Path("CHANGELOG.md").read_text(encoding="utf-8"), re.M)
104
+ top = m.group(1) if m else None
105
+ print(f"pyproject={ver} changelog={top}")
106
+ sys.exit(0 if top == ver else 1)
107
+ PY
108
+ - run: uv build
109
+ - uses: pypa/gh-action-pypi-publish@release/v1
110
+ with:
111
+ skip-existing: true
@@ -59,10 +59,10 @@ jobs:
59
59
  binary: wbox-windows-x64.exe
60
60
  steps:
61
61
  - name: Checkout
62
- uses: actions/checkout@v4
62
+ uses: actions/checkout@v6
63
63
 
64
64
  - name: Set up Python
65
- uses: actions/setup-python@v5
65
+ uses: actions/setup-python@v6
66
66
  with:
67
67
  python-version: "3.12"
68
68
 
@@ -0,0 +1,22 @@
1
+ repos:
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ rev: v0.15.12
4
+ hooks:
5
+ - id: ruff
6
+ files: ^(src|tests)/
7
+
8
+ - repo: local
9
+ hooks:
10
+ - id: mypy
11
+ name: mypy (strict)
12
+ language: system
13
+ entry: uv run mypy src/
14
+ pass_filenames: false
15
+ types: [python]
16
+
17
+ - id: skill-ref-drift
18
+ name: skill-ref drift check
19
+ language: system
20
+ entry: bash -c 'uv run wbox internals regen-skill-refs && git diff --exit-code -- src/wealthbox_tools/skills/wealthbox-crm/references/'
21
+ pass_filenames: false
22
+ always_run: true
@@ -4,6 +4,135 @@ 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.4.1] - 2026-06-12
8
+
9
+ Patch release fixing a startup crash in 2.4.0. The `cli/_factory.py` module
10
+ (introduced in the 2.4.0 refactor) imported `click` at module level to
11
+ annotate a `TyperGroup` subclass, but `click` is only referenced in type
12
+ annotations. On installs where `typer` (>=0.26) does not pull in `click` as a
13
+ transitive dependency, `wbox` crashed on launch with
14
+ `ModuleNotFoundError: No module named 'click'` — the same failure class fixed
15
+ for `skill_ref_gen` in 2.3.1. This was not caught by CI because the test
16
+ environment always has `click` present via the dev dependencies.
17
+
18
+ ### Fixed
19
+ - `wbox` startup crash (`ModuleNotFoundError: click`) on clean installs that
20
+ lack `click`. The `import click` in `cli/_factory.py` is now guarded by
21
+ `TYPE_CHECKING` (it is only used in annotations, which are not evaluated at
22
+ runtime under `from __future__ import annotations`).
23
+
24
+ ### Internal
25
+ - CI gains a `smoke` job that installs the built wheel in isolation **without
26
+ dev dependencies** and runs `wbox --version`, so a missing-runtime-dependency
27
+ startup crash can no longer reach PyPI. The `publish` job now depends on it.
28
+
29
+ ## [2.4.0] - 2026-06-12
30
+
31
+ The notable user-facing change in this release is **differentiated CLI exit
32
+ codes** — scripts that branch on a specific non-zero code should read the note
33
+ below. Beyond that, 2.4.0 is largely an internal hardening release: `src/` is
34
+ now `mypy --strict` clean and gated in CI, the test matrix gained a Windows leg
35
+ and a coverage floor, and the CLI command layer was refactored onto a shared
36
+ resource-command factory. Command syntax, options, help text, and output are
37
+ unchanged.
38
+
39
+ ### Changed
40
+ - **Differentiated CLI exit codes (behavior change).** Previously every error
41
+ exited with code `1`. `wbox` now distinguishes failure classes: validation /
42
+ user errors and non-auth `4xx` responses (e.g. `404`, `422`) still exit `1`,
43
+ authentication errors (`401`/`403`) now exit **`2`**, and server errors
44
+ (`5xx`) now exit **`3`**. Scripts that branched on a specific non-zero code
45
+ for auth or server failures must be updated. (Click usage errors — unknown
46
+ flags, missing arguments — continue to exit `2` as before; that overlap is
47
+ pre-existing.)
48
+
49
+ ### Added
50
+ - `WBOX_DEBUG` environment variable: set it to any non-empty value (e.g.
51
+ `WBOX_DEBUG=1`) to print the full Python traceback to stderr alongside the
52
+ friendly one-line error message. The mapped exit code is unchanged, so it is
53
+ safe to enable in scripts.
54
+ - Documented the existing `WBOX_BRIEF` / `--brief` output mode (strips `*_html`
55
+ duplicate fields to cut response size) in the README and CLI reference.
56
+
57
+ ### Security
58
+ - Token config file (`config.json`) and the rate-limit state file are now
59
+ written with `0600` permissions on POSIX so other local users cannot read a
60
+ stored token.
61
+ - Malformed `Retry-After` response header values are truncated to 100
62
+ characters before being logged, bounding a hostile/oversized header.
63
+
64
+ ### Internal
65
+ - **Typing:** `src/` is now `mypy --strict` clean (zero errors, zero
66
+ `type: ignore`). Client mixins share a typed request protocol and
67
+ `fetch_all_pages` returns a typed pagination envelope. A new `typecheck` CI
68
+ job runs `mypy src/` and gates publishing.
69
+ - **CI:** added a `windows-latest` test leg (Python 3.12), a coverage floor
70
+ (`--cov-fail-under=91` on the Ubuntu leg), a publish-time check that the top
71
+ CHANGELOG version matches `pyproject.toml`, a weekly Dependabot config, and a
72
+ `.pre-commit-config.yaml` (ruff, mypy, skill-ref drift).
73
+ - **Refactor:** `cli/_util.py` was split into `_client`, `_format`, `_resolve`,
74
+ and `_factory` modules (with `_util` kept as a re-export shim). The `notes`,
75
+ `projects`, `tasks`, `events`, `opportunities`, and `workflows` command
76
+ groups now generate their commands from a shared resource-command factory;
77
+ `contacts` remains hand-written. No change to the CLI surface.
78
+ - **Tests:** added coverage for delete commands, project/workflow updates,
79
+ contacts list filters, HTTP base-client edge cases (missing/malformed
80
+ `Retry-After`, `5xx`, missing collection key), exit codes, and an
81
+ update-model contract meta-test (every `*UpdateInput` rejects an empty
82
+ payload).
83
+
84
+ ## [2.3.1] - 2026-06-01
85
+
86
+ Patch release fixing a startup crash introduced in 2.3.0. `wbox` crashed on
87
+ launch in clean environments because `skill_ref_gen.py` imported `click` at
88
+ module level — and `typer` 0.26+ dropped `click` as a dependency, so it was
89
+ no longer guaranteed to be present. The fix defers the import inside the
90
+ hidden `regen-skill-refs` dev command so normal CLI invocations never touch
91
+ it. CI also gains `skip_existing` on the PyPI publish step to survive
92
+ re-pushed tags without a spurious 400 error.
93
+
94
+ ### Fixed
95
+ - `wbox` startup crash (`ModuleNotFoundError: click`) in environments where
96
+ `click` is not installed. The `skill_ref_gen` import is now deferred to
97
+ the body of the hidden `regen-skill-refs` command, which is only ever
98
+ invoked during development.
99
+ - PyPI publish job now uses `skip_existing: true` so re-pushing a tag
100
+ (e.g. to re-run a failed release workflow) does not fail with a 400 from
101
+ PyPI when the wheel was already uploaded on the first push.
102
+
103
+ ## [2.3.0] - 2026-06-01
104
+
105
+ Feature release. Adds writable contact-role support (`--advisor-role`),
106
+ fixing a long-standing gap where advisor assignments could not be set through
107
+ the CLI at all — even the `--more-fields` / `--json` escape hatches rejected
108
+ the payload because `ContactRoleAssignment` modeled the wrong write shape.
109
+ Also improves the Windows bootstrap installer (idempotent re-runs,
110
+ `-SkipSkills` opt-out, token re-use) and clarifies install/upgrade
111
+ documentation for `uv tool` and `pipx` users.
112
+
113
+ ### Added
114
+ - `--advisor-role ROLE:USER` flag on `contacts add person|household|org|trust`
115
+ and `contacts update`. Resolves role names and user substrings against the
116
+ workspace category list so human-readable values like
117
+ `"Associate Advisor:Jane Smith"` are accepted (#94).
118
+ - `install.ps1` gains a `-SkipSkills` switch for CI and non-agent installs
119
+ that don't need the bundled agent skill.
120
+
121
+ ### Fixed
122
+ - `ContactRoleAssignment` now uses `{id, value}` (the Wealthbox write shape)
123
+ instead of `{id, type}`, unblocking all contact-role write paths including
124
+ `--more-fields` (#94).
125
+ - `install.ps1` re-runs are now idempotent: if the skill directory already
126
+ exists the installer runs `wbox skills upgrade` instead of erroring.
127
+ - `install.ps1` no longer prompts for a token when one is already stored in
128
+ `%APPDATA%\wbox\config.json` or `WEALTHBOX_TOKEN`.
129
+
130
+ ### Changed
131
+ - README and `docs/getting-started.md` now include a per-install-method
132
+ upgrading table and clarify that `wbox self upgrade` is for bundle installs
133
+ only; `uv tool upgrade` / `pipx upgrade` are the correct paths for
134
+ managed installs.
135
+
7
136
  ## [2.2.1] - 2026-05-13
8
137
 
9
138
  Bug-fix release. `wbox self upgrade` was silently no-op'ing for users who
@@ -42,6 +42,30 @@ uv run wbox config set-token
42
42
 
43
43
  ---
44
44
 
45
+ ## Pre-commit hooks
46
+
47
+ The repo ships a `.pre-commit-config.yaml` with three hooks: ruff lint, mypy strict, and a skill-ref drift check. Install them once after cloning:
48
+
49
+ ```bash
50
+ # uv
51
+ uv tool install pre-commit
52
+ pre-commit install
53
+
54
+ # or pip
55
+ pip install pre-commit
56
+ pre-commit install
57
+ ```
58
+
59
+ Run all hooks manually at any time:
60
+
61
+ ```bash
62
+ pre-commit run --all-files
63
+ # or without a local install:
64
+ uv tool run pre-commit run --all-files
65
+ ```
66
+
67
+ ---
68
+
45
69
  ## Running Tests
46
70
 
47
71
  ```bash
@@ -50,6 +74,14 @@ uv run pytest
50
74
 
51
75
  Tests use [respx](https://lundberg.github.io/respx/) to mock HTTP at the transport layer — no real API calls are made.
52
76
 
77
+ ### Coverage gate
78
+
79
+ CI enforces `--cov-fail-under=91` on the ubuntu test leg. Run locally with:
80
+
81
+ ```bash
82
+ uv run pytest --cov=src/wealthbox_tools --cov-report=term
83
+ ```
84
+
53
85
  ## Code Style
54
86
 
55
87
  ```bash
@@ -57,7 +89,7 @@ uv run ruff check src/ tests/
57
89
  ```
58
90
 
59
91
  - **ruff** for linting (E, F, I rules; 120-char line length)
60
- - **mypy** in strict mode
92
+ - **mypy** in strict mode (`uv run mypy src/` must exit 0)
61
93
 
62
94
  ---
63
95
 
@@ -87,14 +119,58 @@ Three layers under `src/wealthbox_tools/`:
87
119
 
88
120
  ---
89
121
 
122
+ ## Skill reference files
123
+
124
+ Any change to CLI command signatures or help text **must** regenerate the skill reference files before committing:
125
+
126
+ ```bash
127
+ uv run wbox internals regen-skill-refs
128
+ git add src/wealthbox_tools/skills/wealthbox-crm/references/
129
+ ```
130
+
131
+ The generated files live under `src/wealthbox_tools/skills/wealthbox-crm/references/` and are checked in. The `skill-ref-drift` CI job and the pre-commit hook both fail on stale refs.
132
+
133
+ ---
134
+
135
+ ## Standing verification
136
+
137
+ Run all of these before opening a PR (CI enforces each):
138
+
139
+ ```bash
140
+ uv run ruff check src/ tests/
141
+ uv run pytest
142
+ uv run mypy src/
143
+ uv run wbox internals regen-skill-refs && git diff --exit-code -- src/wealthbox_tools/skills/wealthbox-crm/references/
144
+ ```
145
+
146
+ ---
147
+
148
+ ## Releasing
149
+
150
+ Releases follow semantic versioning. Bump patch for fixes, minor for features, major for breaking changes.
151
+
152
+ 1. Bump `version` in `pyproject.toml`.
153
+ 2. Add a `## [X.Y.Z]` entry at the top of `CHANGELOG.md` (CI enforces that the changelog version matches `pyproject.toml` before publishing to PyPI).
154
+ 3. Verify locally: `uv run ruff check src/ tests/ && uv run pytest && uv run mypy src/`
155
+ 4. Commit: `vX.Y.Z: <description>`
156
+ 5. Tag: `git tag vX.Y.Z`
157
+ 6. Push: `git push origin main --tags`
158
+
159
+ CI builds and publishes to PyPI automatically on `v*` tags once lint, tests, typecheck, and skill-ref-drift all pass.
160
+
161
+ ---
162
+
90
163
  ## CI
91
164
 
92
165
  Pull requests and pushes to `main` run GitHub Actions CI:
93
166
 
94
167
  - **Lint:** `ruff check src/ tests/`
95
- - **Test:** `pytest` across Python 3.11, 3.12, 3.13
168
+ - **Test:** `pytest` across Python 3.11, 3.12, 3.13 (ubuntu) + 3.12 (windows); coverage gate `--cov-fail-under=91` on ubuntu
169
+ - **Typecheck:** `mypy src/` (strict)
170
+ - **Smoke:** installs the built wheel in isolation (no dev deps) and runs `wbox --version` / `wbox notes --help`, catching missing-runtime-dependency startup crashes
171
+ - **Skill-ref drift:** regenerates refs and asserts no git diff
96
172
 
97
- Both must pass before merging.
173
+ All jobs must pass before merging; `publish` (on `v*` tags) additionally gates on lint, test, typecheck, and smoke.
98
174
 
99
175
  ---
100
176
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wealthbox-cli
3
- Version: 2.3.1
3
+ Version: 2.4.1
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/
@@ -33,9 +33,11 @@ Requires-Dist: typer<1.0,>=0.12
33
33
  Provides-Extra: dev
34
34
  Requires-Dist: mypy>=1.10; extra == 'dev'
35
35
  Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
36
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
36
37
  Requires-Dist: pytest>=8.0; extra == 'dev'
37
38
  Requires-Dist: respx>=0.21; extra == 'dev'
38
39
  Requires-Dist: ruff>=0.4; extra == 'dev'
40
+ Requires-Dist: types-tabulate>=0.9; extra == 'dev'
39
41
  Description-Content-Type: text/markdown
40
42
 
41
43
  # Wealthbox CLI
@@ -152,6 +154,29 @@ wbox notes add "Discussed retirement plan" --contact 12345
152
154
  ```
153
155
 
154
156
  Full command reference: [CLI Reference](https://massive-value.github.io/wealthbox-cli/cli-reference/). Output formats: `json` (default), `table`, `csv`, `tsv`. Date format: ISO 8601 (`2026-05-01T10:00:00-07:00`). See [Getting Started](https://massive-value.github.io/wealthbox-cli/getting-started/) for pipx, env-var auth, and project-scoped installs.
157
+
158
+ **Exit codes** — `wbox` returns differentiated exit codes so scripts can branch on the failure class:
159
+
160
+ | Code | Meaning |
161
+ | ---- | ------- |
162
+ | `0` | Success |
163
+ | `1` | Validation / user error (bad input, a non-auth `4xx` from the API such as `404`/`422`) |
164
+ | `2` | Authentication error (API `401`/`403`) |
165
+ | `3` | Server error (API `5xx`) |
166
+
167
+ Note: Click also exits `2` for usage errors (an unknown flag or a missing required argument); that overlap with the auth code is pre-existing and acceptable, since usage errors are caught by the parser before the command runs. Set `WBOX_DEBUG=1` to print the full Python traceback to stderr alongside the friendly message (the exit code is unchanged) — handy when reporting bugs.
168
+
169
+ **Brief mode** — Wealthbox duplicates every text field as HTML markup (`content_html`, `description_html`, `body_html`, etc.), making responses 3–5x larger. Pass `--brief` or set `WBOX_BRIEF=1` to recursively strip all `*_html` keys from the JSON output before it is printed. Plain-text counterparts are kept; all other fields are unaffected. Any non-empty value is truthy (same convention as `WBOX_DEBUG`).
170
+
171
+ ```bash
172
+ # One-off: flag form
173
+ wbox --brief notes list
174
+
175
+ # Persistent in a script or agent session
176
+ export WBOX_BRIEF=1
177
+ wbox notes list
178
+ wbox tasks list
179
+ ```
155
180
  </details>
156
181
 
157
182
  <details>
@@ -112,6 +112,29 @@ wbox notes add "Discussed retirement plan" --contact 12345
112
112
  ```
113
113
 
114
114
  Full command reference: [CLI Reference](https://massive-value.github.io/wealthbox-cli/cli-reference/). Output formats: `json` (default), `table`, `csv`, `tsv`. Date format: ISO 8601 (`2026-05-01T10:00:00-07:00`). See [Getting Started](https://massive-value.github.io/wealthbox-cli/getting-started/) for pipx, env-var auth, and project-scoped installs.
115
+
116
+ **Exit codes** — `wbox` returns differentiated exit codes so scripts can branch on the failure class:
117
+
118
+ | Code | Meaning |
119
+ | ---- | ------- |
120
+ | `0` | Success |
121
+ | `1` | Validation / user error (bad input, a non-auth `4xx` from the API such as `404`/`422`) |
122
+ | `2` | Authentication error (API `401`/`403`) |
123
+ | `3` | Server error (API `5xx`) |
124
+
125
+ Note: Click also exits `2` for usage errors (an unknown flag or a missing required argument); that overlap with the auth code is pre-existing and acceptable, since usage errors are caught by the parser before the command runs. Set `WBOX_DEBUG=1` to print the full Python traceback to stderr alongside the friendly message (the exit code is unchanged) — handy when reporting bugs.
126
+
127
+ **Brief mode** — Wealthbox duplicates every text field as HTML markup (`content_html`, `description_html`, `body_html`, etc.), making responses 3–5x larger. Pass `--brief` or set `WBOX_BRIEF=1` to recursively strip all `*_html` keys from the JSON output before it is printed. Plain-text counterparts are kept; all other fields are unaffected. Any non-empty value is truthy (same convention as `WBOX_DEBUG`).
128
+
129
+ ```bash
130
+ # One-off: flag form
131
+ wbox --brief notes list
132
+
133
+ # Persistent in a script or agent session
134
+ export WBOX_BRIEF=1
135
+ wbox notes list
136
+ wbox tasks list
137
+ ```
115
138
  </details>
116
139
 
117
140
  <details>