wealthbox-cli 2.3.0__tar.gz → 2.4.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 (111) hide show
  1. wealthbox_cli-2.4.0/.github/dependabot.yml +20 -0
  2. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/.github/workflows/ci.yml +34 -3
  3. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/.github/workflows/release-binaries.yml +2 -2
  4. wealthbox_cli-2.4.0/.pre-commit-config.yaml +22 -0
  5. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/CHANGELOG.md +107 -0
  6. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/CONTRIBUTING.md +78 -3
  7. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/PKG-INFO +26 -1
  8. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/README.md +23 -0
  9. wealthbox_cli-2.4.0/docs/cli-reference.md +854 -0
  10. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/pyproject.toml +4 -1
  11. wealthbox_cli-2.4.0/src/wealthbox_tools/cli/_client.py +138 -0
  12. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/cli/_config.py +4 -1
  13. wealthbox_cli-2.4.0/src/wealthbox_tools/cli/_factory.py +284 -0
  14. wealthbox_cli-2.4.0/src/wealthbox_tools/cli/_format.py +259 -0
  15. wealthbox_cli-2.4.0/src/wealthbox_tools/cli/_resolve.py +175 -0
  16. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/cli/_skill_bootstrap.py +6 -1
  17. wealthbox_cli-2.4.0/src/wealthbox_tools/cli/_util.py +103 -0
  18. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/cli/doctor.py +2 -1
  19. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/cli/events.py +25 -30
  20. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/cli/internals.py +2 -2
  21. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/cli/notes.py +23 -22
  22. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/cli/opportunities.py +23 -29
  23. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/cli/projects.py +21 -21
  24. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/cli/tasks.py +30 -20
  25. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/cli/workflows.py +23 -21
  26. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/client/activity.py +6 -3
  27. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/client/base.py +71 -10
  28. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/client/categories.py +9 -6
  29. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/client/comments.py +10 -6
  30. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/client/contacts.py +18 -12
  31. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/client/events.py +16 -10
  32. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/client/households.py +9 -5
  33. wealthbox_cli-2.4.0/src/wealthbox_tools/client/me.py +14 -0
  34. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/client/notes.py +15 -9
  35. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/client/opportunities.py +16 -10
  36. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/client/projects.py +15 -9
  37. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/client/tasks.py +16 -10
  38. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/client/users.py +9 -6
  39. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/client/workflows.py +21 -13
  40. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/self_upgrade.py +3 -2
  41. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/uv.lock +186 -1
  42. wealthbox_cli-2.3.0/docs/cli-reference.md +0 -364
  43. wealthbox_cli-2.3.0/src/wealthbox_tools/cli/_util.py +0 -557
  44. wealthbox_cli-2.3.0/src/wealthbox_tools/client/me.py +0 -11
  45. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/.github/workflows/docs.yml +0 -0
  46. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/.gitignore +0 -0
  47. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/LICENSE +0 -0
  48. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/SECURITY.md +0 -0
  49. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/docs/agents/domain.md +0 -0
  50. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/docs/agents/issue-tracker.md +0 -0
  51. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/docs/agents/triage-labels.md +0 -0
  52. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/docs/getting-started.md +0 -0
  53. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/docs/index.md +0 -0
  54. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/docs/overrides/main.html +0 -0
  55. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/docs/promotion.md +0 -0
  56. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/mkdocs.yml +0 -0
  57. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/__init__.py +0 -0
  58. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/cli/__init__.py +0 -0
  59. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/cli/_skill_paths.py +0 -0
  60. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/cli/_skill_platforms.py +0 -0
  61. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/cli/activity.py +0 -0
  62. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/cli/categories.py +0 -0
  63. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/cli/config.py +0 -0
  64. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/cli/contacts.py +0 -0
  65. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/cli/firm.py +0 -0
  66. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/cli/households.py +0 -0
  67. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/cli/main.py +0 -0
  68. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/cli/me.py +0 -0
  69. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/cli/prefs.py +0 -0
  70. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/cli/self_cmd.py +0 -0
  71. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/cli/skills.py +0 -0
  72. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/cli/users.py +0 -0
  73. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/client/__init__.py +0 -0
  74. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/firm/__init__.py +0 -0
  75. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/firm/archive.py +0 -0
  76. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/firm/diff.py +0 -0
  77. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/internals/__init__.py +0 -0
  78. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/internals/skill_ref_gen.py +0 -0
  79. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/models/__init__.py +0 -0
  80. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/models/activity.py +0 -0
  81. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/models/comments.py +0 -0
  82. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/models/common.py +0 -0
  83. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/models/contacts.py +0 -0
  84. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/models/custom_fields.py +0 -0
  85. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/models/enums.py +0 -0
  86. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/models/events.py +0 -0
  87. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/models/households.py +0 -0
  88. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/models/notes.py +0 -0
  89. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/models/opportunities.py +0 -0
  90. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/models/projects.py +0 -0
  91. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/models/tasks.py +0 -0
  92. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/models/workflows.py +0 -0
  93. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/py.typed +0 -0
  94. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/skills/wealthbox-crm/SKILL.md +0 -0
  95. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/skills/wealthbox-crm/bootstrap.md +0 -0
  96. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/skills/wealthbox-crm/firm-examples/contacts.md +0 -0
  97. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/skills/wealthbox-crm/firm-examples/events.md +0 -0
  98. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/skills/wealthbox-crm/firm-examples/notes.md +0 -0
  99. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/skills/wealthbox-crm/firm-examples/opportunities.md +0 -0
  100. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/skills/wealthbox-crm/firm-examples/projects.md +0 -0
  101. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/skills/wealthbox-crm/firm-examples/tasks.md +0 -0
  102. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/skills/wealthbox-crm/firm-examples/workflows.md +0 -0
  103. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/skills/wealthbox-crm/references/contacts.md +0 -0
  104. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/skills/wealthbox-crm/references/events.md +0 -0
  105. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/skills/wealthbox-crm/references/households.md +0 -0
  106. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/skills/wealthbox-crm/references/lookups.md +0 -0
  107. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/skills/wealthbox-crm/references/notes.md +0 -0
  108. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/skills/wealthbox-crm/references/opportunities.md +0 -0
  109. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/skills/wealthbox-crm/references/projects.md +0 -0
  110. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/src/wealthbox_tools/skills/wealthbox-crm/references/tasks.md +0 -0
  111. {wealthbox_cli-2.3.0 → wealthbox_cli-2.4.0}/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)"
@@ -19,17 +19,36 @@ jobs:
19
19
  - run: uv run ruff check src/ tests/
20
20
 
21
21
  test:
22
- runs-on: ubuntu-latest
22
+ runs-on: ${{ matrix.os }}
23
23
  strategy:
24
24
  matrix:
25
+ os: [ubuntu-latest]
25
26
  python-version: ["3.11", "3.12", "3.13"]
27
+ include:
28
+ - os: windows-latest
29
+ python-version: "3.12"
26
30
  steps:
27
31
  - uses: actions/checkout@v6
28
32
  - uses: astral-sh/setup-uv@v8.1.0
29
33
  with:
30
34
  python-version: ${{ matrix.python-version }}
31
35
  - run: uv sync --extra dev
32
- - run: uv run pytest
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/
33
52
 
34
53
  skill-ref-drift:
35
54
  needs: [lint, test]
@@ -44,7 +63,7 @@ jobs:
44
63
  - run: git diff --exit-code -- src/wealthbox_tools/skills/wealthbox-crm/references/
45
64
 
46
65
  publish:
47
- needs: [lint, test, skill-ref-drift]
66
+ needs: [lint, test, typecheck, skill-ref-drift]
48
67
  if: startsWith(github.ref, 'refs/tags/v')
49
68
  runs-on: ubuntu-latest
50
69
  permissions:
@@ -55,5 +74,17 @@ jobs:
55
74
  - uses: astral-sh/setup-uv@v8.1.0
56
75
  with:
57
76
  python-version: "3.12"
77
+ - name: Check changelog matches version
78
+ run: |
79
+ python - <<'PY'
80
+ import re, pathlib, sys
81
+ ver = re.search(r'^version\s*=\s*"([^"]+)"', pathlib.Path("pyproject.toml").read_text(encoding="utf-8"), re.M).group(1)
82
+ m = re.search(r'^##\s*\[([0-9]+\.[0-9]+\.[0-9]+)\]', pathlib.Path("CHANGELOG.md").read_text(encoding="utf-8"), re.M)
83
+ top = m.group(1) if m else None
84
+ print(f"pyproject={ver} changelog={top}")
85
+ sys.exit(0 if top == ver else 1)
86
+ PY
58
87
  - run: uv build
59
88
  - uses: pypa/gh-action-pypi-publish@release/v1
89
+ with:
90
+ 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,113 @@ 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.0] - 2026-06-12
8
+
9
+ The notable user-facing change in this release is **differentiated CLI exit
10
+ codes** — scripts that branch on a specific non-zero code should read the note
11
+ below. Beyond that, 2.4.0 is largely an internal hardening release: `src/` is
12
+ now `mypy --strict` clean and gated in CI, the test matrix gained a Windows leg
13
+ and a coverage floor, and the CLI command layer was refactored onto a shared
14
+ resource-command factory. Command syntax, options, help text, and output are
15
+ unchanged.
16
+
17
+ ### Changed
18
+ - **Differentiated CLI exit codes (behavior change).** Previously every error
19
+ exited with code `1`. `wbox` now distinguishes failure classes: validation /
20
+ user errors and non-auth `4xx` responses (e.g. `404`, `422`) still exit `1`,
21
+ authentication errors (`401`/`403`) now exit **`2`**, and server errors
22
+ (`5xx`) now exit **`3`**. Scripts that branched on a specific non-zero code
23
+ for auth or server failures must be updated. (Click usage errors — unknown
24
+ flags, missing arguments — continue to exit `2` as before; that overlap is
25
+ pre-existing.)
26
+
27
+ ### Added
28
+ - `WBOX_DEBUG` environment variable: set it to any non-empty value (e.g.
29
+ `WBOX_DEBUG=1`) to print the full Python traceback to stderr alongside the
30
+ friendly one-line error message. The mapped exit code is unchanged, so it is
31
+ safe to enable in scripts.
32
+ - Documented the existing `WBOX_BRIEF` / `--brief` output mode (strips `*_html`
33
+ duplicate fields to cut response size) in the README and CLI reference.
34
+
35
+ ### Security
36
+ - Token config file (`config.json`) and the rate-limit state file are now
37
+ written with `0600` permissions on POSIX so other local users cannot read a
38
+ stored token.
39
+ - Malformed `Retry-After` response header values are truncated to 100
40
+ characters before being logged, bounding a hostile/oversized header.
41
+
42
+ ### Internal
43
+ - **Typing:** `src/` is now `mypy --strict` clean (zero errors, zero
44
+ `type: ignore`). Client mixins share a typed request protocol and
45
+ `fetch_all_pages` returns a typed pagination envelope. A new `typecheck` CI
46
+ job runs `mypy src/` and gates publishing.
47
+ - **CI:** added a `windows-latest` test leg (Python 3.12), a coverage floor
48
+ (`--cov-fail-under=91` on the Ubuntu leg), a publish-time check that the top
49
+ CHANGELOG version matches `pyproject.toml`, a weekly Dependabot config, and a
50
+ `.pre-commit-config.yaml` (ruff, mypy, skill-ref drift).
51
+ - **Refactor:** `cli/_util.py` was split into `_client`, `_format`, `_resolve`,
52
+ and `_factory` modules (with `_util` kept as a re-export shim). The `notes`,
53
+ `projects`, `tasks`, `events`, `opportunities`, and `workflows` command
54
+ groups now generate their commands from a shared resource-command factory;
55
+ `contacts` remains hand-written. No change to the CLI surface.
56
+ - **Tests:** added coverage for delete commands, project/workflow updates,
57
+ contacts list filters, HTTP base-client edge cases (missing/malformed
58
+ `Retry-After`, `5xx`, missing collection key), exit codes, and an
59
+ update-model contract meta-test (every `*UpdateInput` rejects an empty
60
+ payload).
61
+
62
+ ## [2.3.1] - 2026-06-01
63
+
64
+ Patch release fixing a startup crash introduced in 2.3.0. `wbox` crashed on
65
+ launch in clean environments because `skill_ref_gen.py` imported `click` at
66
+ module level — and `typer` 0.26+ dropped `click` as a dependency, so it was
67
+ no longer guaranteed to be present. The fix defers the import inside the
68
+ hidden `regen-skill-refs` dev command so normal CLI invocations never touch
69
+ it. CI also gains `skip_existing` on the PyPI publish step to survive
70
+ re-pushed tags without a spurious 400 error.
71
+
72
+ ### Fixed
73
+ - `wbox` startup crash (`ModuleNotFoundError: click`) in environments where
74
+ `click` is not installed. The `skill_ref_gen` import is now deferred to
75
+ the body of the hidden `regen-skill-refs` command, which is only ever
76
+ invoked during development.
77
+ - PyPI publish job now uses `skip_existing: true` so re-pushing a tag
78
+ (e.g. to re-run a failed release workflow) does not fail with a 400 from
79
+ PyPI when the wheel was already uploaded on the first push.
80
+
81
+ ## [2.3.0] - 2026-06-01
82
+
83
+ Feature release. Adds writable contact-role support (`--advisor-role`),
84
+ fixing a long-standing gap where advisor assignments could not be set through
85
+ the CLI at all — even the `--more-fields` / `--json` escape hatches rejected
86
+ the payload because `ContactRoleAssignment` modeled the wrong write shape.
87
+ Also improves the Windows bootstrap installer (idempotent re-runs,
88
+ `-SkipSkills` opt-out, token re-use) and clarifies install/upgrade
89
+ documentation for `uv tool` and `pipx` users.
90
+
91
+ ### Added
92
+ - `--advisor-role ROLE:USER` flag on `contacts add person|household|org|trust`
93
+ and `contacts update`. Resolves role names and user substrings against the
94
+ workspace category list so human-readable values like
95
+ `"Associate Advisor:Jane Smith"` are accepted (#94).
96
+ - `install.ps1` gains a `-SkipSkills` switch for CI and non-agent installs
97
+ that don't need the bundled agent skill.
98
+
99
+ ### Fixed
100
+ - `ContactRoleAssignment` now uses `{id, value}` (the Wealthbox write shape)
101
+ instead of `{id, type}`, unblocking all contact-role write paths including
102
+ `--more-fields` (#94).
103
+ - `install.ps1` re-runs are now idempotent: if the skill directory already
104
+ exists the installer runs `wbox skills upgrade` instead of erroring.
105
+ - `install.ps1` no longer prompts for a token when one is already stored in
106
+ `%APPDATA%\wbox\config.json` or `WEALTHBOX_TOKEN`.
107
+
108
+ ### Changed
109
+ - README and `docs/getting-started.md` now include a per-install-method
110
+ upgrading table and clarify that `wbox self upgrade` is for bundle installs
111
+ only; `uv tool upgrade` / `pipx upgrade` are the correct paths for
112
+ managed installs.
113
+
7
114
  ## [2.2.1] - 2026-05-13
8
115
 
9
116
  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,57 @@ 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
+ - **Skill-ref drift:** regenerates refs and asserts no git diff
96
171
 
97
- Both must pass before merging.
172
+ All four jobs must pass before merging.
98
173
 
99
174
  ---
100
175
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wealthbox-cli
3
- Version: 2.3.0
3
+ Version: 2.4.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/
@@ -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>