Changelog¶
All notable changes to wealthbox-cli are documented here. The format follows
Keep a Changelog, and this project
adheres to Semantic Versioning.
[2.5.0] - 2026-09-20¶
Fills in the work-record commands an AI agent needs, and corrects two list defaults that were quietly returning a slice of the data.
Fixed¶
wbox tasks list --include-completedreturned completed tasks only. Wealthbox'scompletedparameter is a two-way switch, not an "include": omitted orfalsemeans open only,truemeans completed only. The flag mapped tocompleted=true, so it did the opposite of its name. It is now a hidden alias for--status alland keeps working in existing prompts.- A bad
--category,--advisor-role, or--custom-fieldvalue printed "Unexpected error". These resolvers raiseBadParameterfrom inside the command body, after Click's own parsing is finished, so the generic handler caught them.handle_errorsnow renders them as the user errors they are. The exit code was already 1 and has not changed.
Added¶
wbox tasks list --status open|completed|all(defaultopen).allissues both calls in sequence and merges byid, keeping the copy with the laterupdated_at. In a firm with 5,174 tasks, 4,994 of them complete, the default was showing 3% of the record set without saying so.wbox workflows list --status all, alongside the existingactive,completed, andscheduled. The default is now an explicitactive, which is what the API did all along when given no status.allruns three sequential calls and merges byid.wbox teams listandwbox users groups. Team IDs are what--assigned-to-teamexpects and there was no way to look one up; group IDs are whatvisible_toaccepts. Both renderid,name, andmember_count, with the full member list under--verbose.wbox comments list, filtered by--task,--event,--note,--opportunity,--project, or--workflow, or by an--updated-since/--updated-beforewindow. An unfiltered call exits 1 with a one-line reason:GET /commentscosts about 8.5 seconds per page of 100 unfiltered, against about 1.5 seconds inside a one-month window.wbox workflows nextnow lists the active step's outcomes (id,name,action,go_to_step_id) in JSON and on stderr, so they stay readable in every--format.complete-step --outcome-idis the only way to branch such a step and the IDs appeared nowhere else in the CLI.complete-step's own advance hint lists them too.--custom-field NAME=VALUEontasks add|updateandevents add|update(repeatable, accepts a name or a numeric ID). Names are resolved againstwbox categories custom-fields --document-type Task|Eventand an unknown name is a hard error listing the valid ones. That check is load-bearing: Wealthbox accepts a custom-field payload keyed by name with a 200 and then silently discards it.
Changed¶
- Help text on
wbox opportunities listand--include-closednow says that closed opportunities are dropped by default (117 records in the firm, 73 returned). No behavior change. - New
references/comments.md; the tasks, workflows, opportunities, events, and lookups references document the defaults, the new flags, and the teams/user-group lookups.
Not built¶
wbox comments add. The token-authenticated v1 REST API cannot write a comment:POST /v1/commentsreturns a response byte-identical to a nonexistent path, as doesPOST /v1/tasks/{id}/comments. The write route lives on Wealthbox's session-authenticated web surface (POST /commentswithout the/v1prefix answers 302 to/users/login), which the official Wealthbox MCP reaches over OAuth via itsCreateCommenttool. Supporting it fromwboxwould mean driving a logged-in web session with CSRF tokens, a different auth model than the rest of the CLI.references/comments.mddocuments the MCP-writes /wbox-reads split; a comment created through the MCP is immediately readable bywbox comments list.tasks add --parent. The API takes subtasks only as inline objects in a parent task's create payload, never as aparentreference on a child. The read side'sparent {id, type}object has no write counterpart. Use--more-fields '{"subtasks": [...]}'.
[2.4.1] - 2026-06-12¶
Patch release fixing a startup crash in 2.4.0. The cli/_factory.py module
(introduced in the 2.4.0 refactor) imported click at module level to
annotate a TyperGroup subclass, but click is only referenced in type
annotations. On installs where typer (>=0.26) does not pull in click as a
transitive dependency, wbox crashed on launch with
ModuleNotFoundError: No module named 'click' — the same failure class fixed
for skill_ref_gen in 2.3.1. This was not caught by CI because the test
environment always has click present via the dev dependencies.
Fixed¶
wboxstartup crash (ModuleNotFoundError: click) on clean installs that lackclick. Theimport clickincli/_factory.pyis now guarded byTYPE_CHECKING(it is only used in annotations, which are not evaluated at runtime underfrom __future__ import annotations).
Internal¶
- CI gains a
smokejob that installs the built wheel in isolation without dev dependencies and runswbox --version, so a missing-runtime-dependency startup crash can no longer reach PyPI. Thepublishjob now depends on it.
[2.4.0] - 2026-06-12¶
The notable user-facing change in this release is differentiated CLI exit
codes — scripts that branch on a specific non-zero code should read the note
below. Beyond that, 2.4.0 is largely an internal hardening release: src/ is
now mypy --strict clean and gated in CI, the test matrix gained a Windows leg
and a coverage floor, and the CLI command layer was refactored onto a shared
resource-command factory. Command syntax, options, help text, and output are
unchanged.
Changed¶
- Differentiated CLI exit codes (behavior change). Previously every error
exited with code
1.wboxnow distinguishes failure classes: validation / user errors and non-auth4xxresponses (e.g.404,422) still exit1, authentication errors (401/403) now exit2, and server errors (5xx) now exit3. Scripts that branched on a specific non-zero code for auth or server failures must be updated. (Click usage errors — unknown flags, missing arguments — continue to exit2as before; that overlap is pre-existing.)
Added¶
WBOX_DEBUGenvironment variable: set it to any non-empty value (e.g.WBOX_DEBUG=1) to print the full Python traceback to stderr alongside the friendly one-line error message. The mapped exit code is unchanged, so it is safe to enable in scripts.- Documented the existing
WBOX_BRIEF/--briefoutput mode (strips*_htmlduplicate fields to cut response size) in the README and CLI reference.
Security¶
- Token config file (
config.json) and the rate-limit state file are now written with0600permissions on POSIX so other local users cannot read a stored token. - Malformed
Retry-Afterresponse header values are truncated to 100 characters before being logged, bounding a hostile/oversized header.
Internal¶
- Typing:
src/is nowmypy --strictclean (zero errors, zerotype: ignore). Client mixins share a typed request protocol andfetch_all_pagesreturns a typed pagination envelope. A newtypecheckCI job runsmypy src/and gates publishing. - CI: added a
windows-latesttest leg (Python 3.12), a coverage floor (--cov-fail-under=91on the Ubuntu leg), a publish-time check that the top CHANGELOG version matchespyproject.toml, a weekly Dependabot config, and a.pre-commit-config.yaml(ruff, mypy, skill-ref drift). - Refactor:
cli/_util.pywas split into_client,_format,_resolve, and_factorymodules (with_utilkept as a re-export shim). Thenotes,projects,tasks,events,opportunities, andworkflowscommand groups now generate their commands from a shared resource-command factory;contactsremains hand-written. No change to the CLI surface. - Tests: added coverage for delete commands, project/workflow updates,
contacts list filters, HTTP base-client edge cases (missing/malformed
Retry-After,5xx, missing collection key), exit codes, and an update-model contract meta-test (every*UpdateInputrejects an empty payload).
[2.3.1] - 2026-06-01¶
Patch release fixing a startup crash introduced in 2.3.0. wbox crashed on
launch in clean environments because skill_ref_gen.py imported click at
module level — and typer 0.26+ dropped click as a dependency, so it was
no longer guaranteed to be present. The fix defers the import inside the
hidden regen-skill-refs dev command so normal CLI invocations never touch
it. CI also gains skip_existing on the PyPI publish step to survive
re-pushed tags without a spurious 400 error.
Fixed¶
wboxstartup crash (ModuleNotFoundError: click) in environments whereclickis not installed. Theskill_ref_genimport is now deferred to the body of the hiddenregen-skill-refscommand, which is only ever invoked during development.- PyPI publish job now uses
skip_existing: trueso re-pushing a tag (e.g. to re-run a failed release workflow) does not fail with a 400 from PyPI when the wheel was already uploaded on the first push.
[2.3.0] - 2026-06-01¶
Feature release. Adds writable contact-role support (--advisor-role),
fixing a long-standing gap where advisor assignments could not be set through
the CLI at all — even the --more-fields / --json escape hatches rejected
the payload because ContactRoleAssignment modeled the wrong write shape.
Also improves the Windows bootstrap installer (idempotent re-runs,
-SkipSkills opt-out, token re-use) and clarifies install/upgrade
documentation for uv tool and pipx users.
Added¶
--advisor-role ROLE:USERflag oncontacts add person|household|org|trustandcontacts update. Resolves role names and user substrings against the workspace category list so human-readable values like"Associate Advisor:Jane Smith"are accepted (#94).install.ps1gains a-SkipSkillsswitch for CI and non-agent installs that don't need the bundled agent skill.
Fixed¶
ContactRoleAssignmentnow uses{id, value}(the Wealthbox write shape) instead of{id, type}, unblocking all contact-role write paths including--more-fields(#94).install.ps1re-runs are now idempotent: if the skill directory already exists the installer runswbox skills upgradeinstead of erroring.install.ps1no longer prompts for a token when one is already stored in%APPDATA%\wbox\config.jsonorWEALTHBOX_TOKEN.
Changed¶
- README and
docs/getting-started.mdnow include a per-install-method upgrading table and clarify thatwbox self upgradeis for bundle installs only;uv tool upgrade/pipx upgradeare the correct paths for managed installs.
[2.2.1] - 2026-05-13¶
Bug-fix release. wbox self upgrade was silently no-op'ing for users who
installed wbox via uv tool install or pipx: it computed the install root
from sys.executable (the venv's python.exe) and tried to swap the
console-script shim there, but ~/.local/bin/wbox.exe (or pipx's equivalent)
is a separate copy uv/pipx made — replacing the venv-side shim never changed
what PATH resolved.
Fixed¶
wbox self upgradenow detects the install kind (bundle/uv-tool/pipx/pip) up front. Non-bundle installs exit1with the correct follow-up command (uv tool upgrade wealthbox-cli,pipx upgrade wealthbox-cli, orpip install --upgrade wealthbox-cli) instead of scheduling a swap that would never take effect onPATH.
[2.2.0] - 2026-05-13¶
Small feature + correctness release. Disambiguates the two IDs returned by
wbox me, fixes a silent truncation in wbox users list on large firms, and
clears Node 20 deprecation warnings from CI ahead of GitHub's June 2026 cutoff.
Added¶
wbox me user-id— printscurrent_user.id(the workspace user ID accepted by--assigned-to) as a bare integer, for composing with command substitution:wbox tasks list --assigned-to "$(wbox me user-id)"(#90, #91).wbox me --format tablenow labels the two IDs aslogin_idanduser_id (--assigned-to)so they aren't confusable at a glance. The JSON shape is unchanged — no breaking change for existing scripts (#91).- Skill docs (
lookups.md,tasks.md,contacts.md) flag the two-ID trap inline near--assigned-to.
Fixed¶
wbox users listnow paginates across all pages. Previously, firms with more than 25 users silently saw a truncated list — the CLI exposes no--pageflag, so there was no manual workaround.- mkdocs docs deploy was failing on every main push because
docs/index.mdlinked to a missingchangelog.md.CHANGELOG.mdis now staged intodocs/at build time and listed in nav (#92).
Changed¶
- CI actions bumped to Node 24 runtimes ahead of GitHub's June 2 2026 cutoff:
actions/checkoutv4→v6,astral-sh/setup-uvv6→v8.1.0 (pinned exact — setup-uv v8+ no longer publishes rolling major tags as a supply-chain mitigation),actions/setup-pythonv5→v6,actions/deploy-pagesv4→v5,actions/upload-pages-artifactv3→v5 (#92, #93).
[2.1.0] - 2026-05-09¶
Repository simplification release. Drops a runtime dep, retires .env support,
and switches dev tooling to uv.
Breaking¶
- Removed
.envfile support. Token resolution is now a 3-tier chain:--tokenflag →WEALTHBOX_TOKENenv var → config file. Users relying on a working-directory.envshould migrate towbox config set-token(preferred) or exportWEALTHBOX_TOKENin their shell. Thepython-dotenvruntime dependency was dropped.
Changed¶
- Development tooling moved to uv.
uv sync --extra devreplaces the manualpython -m venv+ activate +pip install -edance, anduv run wbox …replaces direct.venv/bin/wboxinvocation. CI now usesastral-sh/setup-uv@v6anduv buildfor the publish job. Plainpip install -e ".[dev]"continues to work —pyproject.tomlis the source of truth for both tools. scripts/run-wbox.shandscripts/smoke_test.shinvoke viauv runand no longer source a.envfile.
Removed¶
.envand.env.examplefiles at the repo root..python-versionfile.requires-python = ">=3.11"inpyproject.tomlis the authoritative interpreter constraint; uv reads it directly.
[2.0.0] - 2026-05-09¶
v2 retires the Claude marketplace plugin distribution and ships wbox as a
standalone binary plus a PyPI package. Existing v1 users must reinstall via the
new bootstrap script or pip install wealthbox-cli — see Breaking below.
Breaking¶
- Retired the Claude marketplace plugin distribution.
wboxno longer ships through the Claude marketplace. Install viapip install wealthbox-clior the bootstrap script (curl -fsSL https://github.com/massive-value/wealthbox-cli/releases/latest/download/install.sh | sh). v1'sclaude plugin installflow is gone. - Rewrote
install.shandinstall.ps1to fetch a prebuilt binary from GitHub Releases instead of detecting and preferring the Claude marketplace plugin (#42, #43). The installers now verify a checksum manifest before swapping the binary into place. - Removed v1 distribution artifacts — the in-repo plugin manifest and marketplace metadata are deleted (#27).
- Dropped plugin-cache scanning from the skill platform helpers;
wbox doctorandwbox skills listno longer probe Claude's plugin cache (#28).
Added¶
wbox firm export— end-to-end export of the configured firm's CRM data to a portable JSON snapshot (#31).wbox firm importwith--mode overwrite|merge|abort-on-conflictand--from-urlfor fetching a snapshot directly from a URL (#36, #46, #45).wbox firm diff— diff a local snapshot against the live firm to preview what an import would change (#47).- Post-import provenance metadata and a 90-day freshness warning surfaced by
wbox doctor(#48). wbox doctorpromoted to a top-level command, with a warning when the local install is more than 30 days behind the latest GitHub release (#41).wbox self upgrade— happy-path binary self-update (#32), Windows deferred-swap support (#67), and a subprocess hand-off so a freshwboxupgrades bundled skills after the binary swap (#40).wbox prefs— user-preferences slot for per-user defaults (#29).- Per-platform PyInstaller build + release workflow that publishes Linux, macOS, and Windows binaries plus a checksum manifest to GitHub Releases (#33).
- Skill-reference auto-generation markers across the bundled skill (#30, #85) and a CI drift-detection job that fails when generated skill blocks fall out of sync (#86).
- Bootstrap Q&A skips the onboarding prompts when an imported firm snapshot
already carries an
onboarded_attimestamp (#49). wbox firm applyandwbox doctornow sweep stale.old.<ts>backup files left behind by interrupted upgrades (#39).
Changed¶
- Rewrote the README around the v2 install flow — bootstrap script first, PyPI second, no marketplace path (#83).
- Documented the three-layer config resolution (flag > env > config file) and
the new
prefscommands inside the bundled skill (#34).
Fixed¶
wbox self upgradenow reads the checksum manifest under its release-asset filename, fixing the post-#33 break (#82).- Pytest no longer clobbers the developer's real
~/.wbox_rate_limit.json; the rate-limit state file is isolated per test session (#56).
1.3.0 - 2026-05-03¶
Added¶
wbox doctor— comprehensive top-level health check at the CLI root. Reports the wbox CLI version + Python version + binary location; authentication source detection (flag / env var / config file /.env) plus a smoke test against/me; agent CLI presence (claude/codexon PATH); legacy skill installs; plugin installs (managed viaclaude plugin install/codex plugin install); firm data state with file count, generated-vs-hand-edited split, and oldest-generated-file timestamp; and a Summary section listing actionable issues.wbox skills doctorkeeps working as an alias of the new top-level command — both call the same function so output never drifts.wbox skills listandwbox skills doctornow detect plugin-installed copies ofwealthbox-crmunder~/.claude/plugins/cache/.../skills/wealthbox-crm/and~/.codex/plugins/cache/.../skills/wealthbox-crm/. Previously a user who installed via the marketplace plugin path saw "not installed" everywhere even though the plugin was actively serving the skill.- Bootstrap installer (
scripts/install.sh/scripts/install.ps1) now prefers the Claude Code plugin marketplace path whenclaudeis on PATH — runsclaude plugin marketplace add+claude plugin installdirectly, then offers a separate Codex install. Falls back to the legacywbox skills installpicker when noclaudeCLI is detected. - Bootstrap installer pre-flight checks Windows PowerShell
ExecutionPolicyand offers to setRemoteSignedfor the current user if needed (no admin required), instead of bombing partway through with Astral's terse error.
Fixed¶
- Bootstrap
install.shno longer hangs at startup. The previous version did `exec