@go-labs-sg/bb 1.21.0 → 2.1.0

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.
package/README.md CHANGED
@@ -6,28 +6,28 @@ For AI agents: bills and claims are stored as the same underlying bill records.
6
6
 
7
7
  For AI agents using this CLI: always check whether a newer `@go-labs-sg/bb` version is available before running Budget Builder commands, then update the CLI if needed so command flags and API behavior stay current.
8
8
 
9
- For AI agents using this CLI: run `bb whoami` before reading or changing Budget Builder data. The CLI displays every command regardless of the authenticated user's role, but the API authorizes each request using the API-key owner's role, resource ownership or project assignment, current workflow state, and approval assignment. See the [role-aware agent guide](./role-aware-agent-guide.md) before choosing commands.
9
+ For AI agents using this CLI: run `bb auth whoami` before reading or changing Budget Builder data. The CLI displays every command regardless of the authenticated user's role, but the API authorizes each request using the API-key owner's role, resource ownership or project assignment, current workflow state, and approval assignment. See the [role-aware agent guide](./role-aware-agent-guide.md) before choosing commands.
10
10
 
11
- For AI agents using this CLI: do not execute approval decisions, approval requests, status updates, estimate emails, customer-invoice changes, integration retries, `mark-budget-won`, `submit-quotation`, or supplier approval request side effects until the user explicitly confirms the exact entity, target state, and side effects. Inspect the entity first, summarize what will change and whether emails/integrations will run, then wait for a clear confirmation from the user. Sensitive commands print a workflow preview with separate **Action**, **Target**, and **Effects** fields before an interactive `CONFIRM` prompt, and abort in non-interactive shells.
11
+ For AI agents using this CLI: do not execute mutations until the user explicitly confirms the exact entity, target state, and side effects. Inspect the entity first, summarize what will change and whether emails, external systems, deletions, or financial records are involved, then wait for a clear confirmation from the user. Canonical v2 mutations print an effect-aware workflow preview before one interactive `CONFIRM` prompt. Non-interactive automation must supply every applicable granular `--allow-*` flag; the CLI rejects the command before its first API call when any flag is missing.
12
12
 
13
13
  **Registry:** [`@go-labs-sg/bb`](https://www.npmjs.com/package/@go-labs-sg/bb)
14
14
 
15
15
  ## Requirements
16
16
 
17
- - **Node.js** 18+ (ESM; relative imports in `dist` use `.js` extensions)
17
+ - **Node.js** 24+ (ESM; relative imports in `dist` use `.js` extensions)
18
18
  - A Budget Builder account with any active user role
19
19
  - A **Budget Builder API key** (the same key is used for the CLI and MCP server)
20
20
 
21
21
  ## Install
22
22
 
23
23
  ```bash
24
- npm install -g @go-labs-sg/bb
24
+ bun add --global @go-labs-sg/bb
25
25
  ```
26
26
 
27
27
  Or run without installing:
28
28
 
29
29
  ```bash
30
- npx @go-labs-sg/bb <command>
30
+ bunx @go-labs-sg/bb <command>
31
31
  ```
32
32
 
33
33
  ## Authentication
@@ -50,13 +50,13 @@ a local `.env`, keep it out of version control and restrict its file permissions
50
50
  Once authenticated, any user can rotate or manage their own keys from the CLI:
51
51
 
52
52
  ```bash
53
- bb whoami
54
- bb create-api-key --name "My CLI"
55
- bb list-api-keys
56
- bb revoke-api-key <api-key-id>
53
+ bb auth whoami
54
+ bb user api-key create --name "My CLI"
55
+ bb user api-key list
56
+ bb user api-key revoke <api-key-id>
57
57
  ```
58
58
 
59
- The raw key is returned only by `create-api-key`; copy it immediately. Listing
59
+ The raw key is returned only by `bb user api-key create`; copy it immediately. Listing
60
60
  keys returns metadata only. Revoking the key currently stored in `BB_API_KEY`
61
61
  will cause subsequent commands to fail until it is replaced.
62
62
 
@@ -65,13 +65,13 @@ CLI. These identities are not human web accounts: `create-user` does not create
65
65
  or link a Google OAuth account, so the resulting user cannot sign in with Google.
66
66
 
67
67
  ```bash
68
- bb create-user --email agent@example.com --name "Budget Agent" --role USER
69
- bb create-api-key --userId <user-id> --name "Budget Agent CLI"
70
- bb list-api-keys --userId <user-id>
71
- bb revoke-api-key <api-key-id> --userId <user-id>
68
+ bb user create --email agent@example.com --name "Budget Agent" --role USER
69
+ bb user api-key create --userId <user-id> --name "Budget Agent CLI"
70
+ bb user api-key list --userId <user-id>
71
+ bb user api-key revoke <api-key-id> --userId <user-id>
72
72
  ```
73
73
 
74
- The CLI talks to the production API: `https://budget-builder.getout.events`.
74
+ The CLI talks to `https://budget-builder.getout.events` by default. For staging or local development, the target precedence is `--api-url`, then `BB_API_URL`, then production. Overrides must use HTTPS, except `http://localhost` and `http://127.0.0.1` for local development. `bb version` reports the effective target.
75
75
 
76
76
  ## Role-aware agent startup
77
77
 
@@ -79,17 +79,25 @@ Every agent session that uses an API key must begin with:
79
79
 
80
80
  ```bash
81
81
  bb version
82
- bb whoami
82
+ bb auth whoami
83
83
  ```
84
84
 
85
- `whoami` returns the identity and database role attached to the active key:
85
+ `whoami` returns the identity and database role attached to the active key inside the v2 response envelope:
86
86
 
87
87
  ```json
88
88
  {
89
- "id": "...",
90
- "name": "...",
91
- "email": "...",
92
- "role": "USER"
89
+ "ok": true,
90
+ "data": {
91
+ "id": "...",
92
+ "name": "...",
93
+ "email": "...",
94
+ "role": "USER"
95
+ },
96
+ "meta": {
97
+ "command": "auth whoami",
98
+ "cliVersion": "2.0.0",
99
+ "apiBaseUrl": "https://budget-builder.getout.events"
100
+ }
93
101
  }
94
102
  ```
95
103
 
@@ -99,7 +107,7 @@ The following is a practical guide, not a client-side allowlist. The API remains
99
107
 
100
108
  | `whoami.role` | What an agent may normally do | Important limits |
101
109
  | --- | --- | --- |
102
- | `USER` | Manage its own API keys; use shared authenticated reads and catalog/CRM operations; work with resources it created or projects/budgets where its user ID is assigned; create its own bills, claims, and quotation drafts; request approval. | No admin dashboards, user provisioning, integration recovery, final finance actions, or approval decisions unless the user is the recorded approver. |
110
+ | `USER` | Manage its own API keys; use collaboration endpoints intentionally shared with every authenticated role, including company, contact, and project creation/update; work with resources it created or projects/budgets where its user ID is assigned; create its own bills, claims, and quotation drafts; request approval. | Shared collaboration endpoints are explicit API exceptions, not a general grant over every resource. No admin dashboards, user provisioning, integration recovery, final finance actions, or approval decisions. |
103
111
  | `INSIDE_SALES` | All applicable base-user operations, assigned project/budget work, and the broader supplier workflow exposed to Inside Sales. | Budget and supplier approval requests still follow the Lead/Admin workflow. Assignment, ownership, state, and recorded-approver checks still apply. |
104
112
  | `LEAD` | All applicable base-user operations plus Lead-stage budget and supplier approval work; supplier creation is auto-approved for this role. | Lead is not Admin. Do not attempt admin dashboards, user administration, integration recovery, final bill payment, or admin-only customer-invoice actions. |
105
113
  | `ACCOUNTING_TEAM` | All applicable authenticated operations plus finance-stage bill work, including checking bills and correcting allowed payment or invoice-number metadata. Act on bill approvals only when the API lists the user as an approver. | Direct `APPROVED` and `PAID` bill status changes, customer-invoice payment, user administration, and system operations remain Admin-only. |
@@ -109,7 +117,9 @@ Permissions are also resource- and state-dependent:
109
117
 
110
118
  - Budget/project mutations generally require Admin, resource ownership, or assignment as Business Development, Inside Sales, or Project Manager.
111
119
  - Bill and quotation edits/deletes are commonly limited to their creator or Admin, and only in supported statuses.
112
- - Approval commands require the authenticated user to be the recorded pending approver; having a generally elevated role is not sufficient by itself.
120
+ - Budget, supplier, bill, and quotation approval decisions require both a pending approval assigned to the authenticated user and the user's current database role. Budget/supplier decisions require `LEAD` or `ADMIN`, bill decisions require `ACCOUNTING_TEAM` or `ADMIN`, and quotation decisions require `ADMIN`; a stale assignment does not preserve authority after a role change.
121
+ - Customer-invoice approve/reject commands are Admin batch operations selected by batch ID, rather than approval-record-ID commands. The API maintains and resolves the internal pending Admin approval record as part of the batch workflow.
122
+ - Some company, contact, project, supplier, and catalog collaboration procedures intentionally allow every active authenticated role. Treat those endpoint-specific rules as exceptions; do not infer access to adjacent mutations.
113
123
  - Own-key commands are available to every active role. Passing `--userId` to manage another user's keys requires Admin.
114
124
  - `bb help` is a command catalog, not proof that the current identity is authorized.
115
125
 
@@ -120,49 +130,55 @@ If the API returns `FORBIDDEN`, stop. Report the command, target resource, `whoa
120
130
  ```bash
121
131
  bb help
122
132
  bb version
123
- bb whoami
124
- bb list-budgets
125
- bb get-budget <budget-id>
126
- bb update-budget-status <budget-id> <status> [--markProjectWon true] [--projectStatusOnCommercialRejection PITCH|LOST]
127
- bb list-bills
128
- bb approve-bill <bill-id>
129
- bb list-approvals
130
- bb list-suppliers
133
+ bb auth whoami
134
+ bb budget list
135
+ bb budget get <budget-id>
136
+ bb budget status update <budget-id> <status>
137
+ bb bill list
138
+ bb bill approve <bill-id>
139
+ bb approval list
140
+ bb supplier list
131
141
  ```
132
142
 
133
- Global options and flags use `--key=value` or `--key value` (see `bb help`).
143
+ Global options and command flags use `--key=value` or `--key value`. Run `bb help` for the canonical command catalog and `bb help --legacy` for the transition-era detailed flat-command reference.
134
144
 
135
145
  Run `bb version` before agent-driven work to record the installed package version and production API target. This is read-only and does not require `BB_API_KEY`.
136
146
 
137
- **Authoritative command list:** run `bb help` it includes every command, positional args, and flags, but does not filter the list by the current `whoami` role. MCP exposes a subset of the same tRPC surface; the CLI additionally includes a few procedures mainly used by the web UI (e.g. `reorder-budget-items`, `update-budget-item-supplier`). You can also use MCP-style `snake_case` (e.g. `bb list_bills`); it is normalized to kebab-case.
147
+ **Authoritative command list:** run `bb help` or inspect the generated `command-reference.md` / `command-manifest.json`. The catalog includes every canonical route, global option, and effect classification, but does not filter by the current `whoami` role. During the handler migration, command-specific arguments retain the compatibility dispatcher's validation and are explicitly marked `legacy-passthrough` in the manifest; use `bb help --legacy` for their detailed transition reference. Version 2 uses grouped, resource-first commands such as `bb budget list` and `bb bill attachment upload`. Flat commands such as `bb list-budgets` and their MCP-style `snake_case` aliases remain available during the v2 transition, with a deprecation notice and an equivalent grouped command in help. MCP exposes a subset of the same tRPC surface; the CLI additionally includes a few procedures mainly used by the web UI.
138
148
 
139
149
  **Budget status automation:** Setting a budget to `ESTIMATE_ACCEPTED` requires a confirmed win-proof attachment. The first accepted estimate also requires `--invoiceSettings '<json>'` while customer invoicing is enabled. Provide `depositPercentage`, `balancePercentage`, `deliveryMethod`, and the matching `billingEmail` or HTTPS `uploadUrl`; revenue at or below S$5,000 requires `100/0`. If the parent project is `PITCH` or `LOST`, the API marks it `WON` automatically and runs enabled Asana automation. When rejecting the only accepted/closed budget on a commercial project, pass `--projectStatusOnCommercialRejection PITCH|LOST`.
140
150
 
141
- **Sensitive workflow changes:** Approval decisions/requests, entity status updates, estimate emails, customer-invoice changes, integration retries, `mark-budget-won`, `submit-quotation`, and supplier approval request side effects require an interactive `CONFIRM` prompt. Non-interactive runs abort before the guarded workflow mutation. Agents must get user confirmation in chat before attempting the command; the prompt is a final runtime guard, not a replacement for user approval.
151
+ ### Mutation safety
152
+
153
+ Every canonical command is classified by effect: `state-change`, `email`, `external-write`, `delete`, and/or `financial-write`. Interactive runs require one exact `CONFIRM`. Non-interactive runs require the corresponding flags (`--allow-state-change`, `--allow-email`, `--allow-external-write`, `--allow-delete`, and `--allow-financial-write`); commands with multiple effects require every matching flag. Agents must still get user confirmation in chat first—the runtime gate is not user authorization.
142
154
 
143
155
  **Approval email exclusions:** Approval requests create pending database records for every eligible approver, including configured non-recipient admin accounts. Automated approval-request emails skip those accounts.
144
156
 
145
- **Locked estimate budget changes:** Budget-changing CLI commands require an interactive `yes` confirmation when the current budget status is `ESTIMATE_CREATED`, `ESTIMATE_SENT`, `ESTIMATE_ACCEPTED`, `ESTIMATE_REJECTED`, or `ESTIMATE_CLOSED`. There is no force/override flag; non-interactive runs abort before mutating locked budgets.
157
+ **Locked estimate budget changes:** Legacy flat commands retain their additional interactive `yes` prompt when the current budget is locked. Canonical v2 commands use the single effect-aware confirmation gate; non-interactive execution requires `--allow-state-change` plus any other effects declared for that command.
146
158
 
147
- **Mutations with `--payload`:** Commands such as `create-budget`, `create-bill`, `update-supplier`, etc. take a single JSON object (`--payload '<json>'`) matching the corresponding tRPC procedure input. Use ISO strings for date/datetime fields; the CLI coerces them where needed. The API still validates the full shape. Plain `description` fields for item create/update are converted to `descriptionRichText`; pass `descriptionRichText` directly when formatted Tiptap JSON is required. For **`update-project`**, the project window is `dateRange.from` and `dateRange.to` (optional end); there are no separate event-date fields on the project payload. **`create-budget` / `update-budget`** do not accept `asanaTaskId`; configure the deal card on the project (`update-project` / project settings).
159
+ **Mutations with `--payload`:** Commands such as `bb budget create`, `bb bill create`, and `bb supplier update` take a single JSON object (`--payload '<json>'`) matching the corresponding tRPC procedure input. Use ISO strings for date/datetime fields; the CLI coerces them where needed. The API still validates the full shape. Plain `description` fields for item create/update are converted to `descriptionRichText`; pass `descriptionRichText` directly when formatted Tiptap JSON is required. For **`bb project update`**, the project window is `dateRange.from` and `dateRange.to` (optional end); there are no separate event-date fields on the project payload. **`bb budget create` / `bb budget update`** do not accept `asanaTaskId`; configure the deal card on the project (`bb project update` / project settings).
148
160
 
149
- **Contact-person estimate email:** `send-estimate-to-contact-person --payload '<json>'` calls the same `email.sendEstimateToContactPerson` procedure as the web composer. The payload requires `budgetId`, `estimateId`, optional `estimateDocNumber`, `to`, `cc`, `replyTo`, `subject`, HTML `content`, and HTML `signature`. It sends the raw PDF returned by QuickBooks together with the standard terms and Budget Builder budget attachments, then marks the budget `ESTIMATE_SENT`. The command requires interactive `CONFIRM`; inspect the budget, recipients, and HTML first, and do not retry blindly after an ambiguous delivery failure.
161
+ **Contact-person estimate email:** `bb budget estimate send --payload '<json>'` calls the same `email.sendEstimateToContactPerson` procedure as the web composer. The payload requires `budgetId`, `estimateId`, optional `estimateDocNumber`, `to`, `cc`, `replyTo`, `subject`, HTML `content`, and HTML `signature`. It sends the raw PDF returned by QuickBooks together with the standard terms and Budget Builder budget attachments, then marks the budget `ESTIMATE_SENT`. The command requires interactive `CONFIRM`; inspect the budget, recipients, and HTML first, and do not retry blindly after an ambiguous delivery failure.
150
162
 
151
- **Customer-invoice workflow parity:** `list-customer-invoices` now uses the same global/project list procedure and metrics as the web pages; omit filters for the global list, use `--projectId` for project scope, or pass a positional budget ID as the legacy shortcut. `send-customer-invoice-to-contact-person` uses the same protected email workflow as the web composer and marks a successful invoice `SENT`. `mark-customer-invoice-paid` requires an admin, a payment date, and a local PDF/GIF/JPEG/PNG proof up to 20MB; it uploads the proof, creates a QuickBooks Payment for the live outstanding balance when necessary, attempts to attach the proof in QuickBooks, and records the result in Budget Builder. Invoice approval refuses voided invoices and closes the estimate only when approved invoice coverage totals 100%; deletion, voiding, rejection, and QBO synchronization use the same estimate-reopening and active-payment guards as the web app.
163
+ **Customer-invoice workflow parity:** `bb customer-invoice list` uses the same global/project list procedure and metrics as the web pages; omit filters for the global list or use `--projectId` for project scope. `bb customer-invoice send` uses the same protected email workflow as the web composer and marks a successful invoice `SENT`. `bb customer-invoice approve` and `bb customer-invoice reject` are Admin-only batch operations: callers select a batch ID, and the API resolves its internal pending Admin approval record. `bb customer-invoice payment mark-paid` requires an admin, a payment date, and a local PDF/GIF/JPEG/PNG proof up to 20MB; it uploads the proof, creates a QuickBooks Payment for the live outstanding balance when necessary, attempts to attach the proof in QuickBooks, records the result in Budget Builder, and sets the invoice status to `PAID`. `bb customer-invoice sync` follows QuickBooks' paid state and zero balance, restoring `SENT` or `APPROVED` if that payment is reversed. Invoice approval refuses voided invoices and closes the estimate only when approved invoice coverage totals 100%; deletion, voiding, rejection, expiry, and QBO synchronization use the same estimate-reopening and live-payment guards as the web app.
152
164
 
153
165
  For agent-driven invoice work, use this read-before-write sequence:
154
166
 
155
- 1. Run `get-customer-invoice <batchId>` and, when composing email, `get-customer-invoice-email-context <batchId>`.
156
- 2. For creation, run `list-eligible-customer-invoice-budgets <projectId>` or `check-customer-invoice-readiness <budgetId>` first.
167
+ 1. Run `bb customer-invoice get <batchId>` and, when composing email, `bb customer-invoice email-context get <batchId>`.
168
+ 2. For creation, run `bb customer-invoice eligible-budget list <projectId>` or `bb customer-invoice readiness check <budgetId>` first.
157
169
  3. State the exact invoice or batch, current status, intended mutation, QuickBooks effect, email recipients, proof file, and estimate-closing/reopening effect to the user.
158
170
  4. Wait for explicit confirmation, then run the sensitive command interactively and verify its JSON result. Do not treat the runtime `CONFIRM` prompt as user authorization.
159
171
 
160
172
  ### Command overview
161
173
 
162
- | Area | Commands (non-exhaustive) |
174
+ The table below is a behavior index keyed by the compatibility dispatcher's legacy handler labels. It is not invocation syntax. AI agents and new automation must resolve and use the grouped v2 route from `bb help`, [`command-reference.md`](./command-reference.md), or [`command-manifest.json`](./command-manifest.json); never copy a flat label from this table into a new command.
175
+
176
+ Project and budget query payloads keep the Asana identities explicit: `asanaTaskId` is the Deals task GID, while `asanaProjectGid` and `asanaProjectUrl` identify the operational project created for Won delivery work.
177
+
178
+ | Area | Legacy handler labels (reference only; non-exhaustive) |
163
179
  | --- | --- |
164
180
  | **Budgets** | `list-budgets` (full payload by default; `--summary` or `--includeDetails false` for slim list), `get-budget`, `get-budget-items`, `get-budget-details`, `get-budget-categories`, `get-budget-versions`, `rename-budget-version`, `restore-budget-version`, `update-budget-status` (`ESTIMATE_ACCEPTED` requires win proof, auto-marks `PITCH` or `LOST` projects `WON`, and auto-creates an Asana Event Ops section from Prompt 5 tasks when enabled; Prompt 5 skips quantity-zero items, combines matching non-GO-internal suppliers, keeps each GO internal item separate, removes generated work that repeats another line item or group, and schedules applicable subtasks relative to the project start date; `--projectStatusOnCommercialRejection PITCH\|LOST` is required when rejecting the only accepted/closed budget on a commercial project), `mark-budget-won` (`<budgetId>` + proof file path; `PITCH` or `LOST` projects become `WON` automatically), `create-budget` / `update-budget` (`--payload`), `delete-budget`, `create-budget-approval` (also sends approval request emails), `create-estimate`, `send-estimate-to-contact-person` (same contact-person email workflow as web), `add-budget-items`, `update-budget-item`, `remove-budget-item`, `reorder-budget-items`, `update-budget-item-supplier`, `mark-budget-items-not-utilized`, `restore-budget-item`, `create-placeholder-bill` (admin recovery), `create-budget-category`, `update-budget-category`, `delete-budget-category`, `update-budget-commission`, `delete-budget-commission`, `update-budget-discount`, `delete-budget-discount` (`--payload` where noted), `upload-budget-attachment` (`<budgetId>` + local file path; uses `attachment.requestBudgetAttachmentUpload` + PUT + `attachment.confirmBudgetAttachment`) |
165
- | **Bills / claims** | `list-bills` (`--isClaimable false` for bills, `--isClaimable true` for claims, omit for both), `list-claims` (claims only), `validate-bill-selection` (`--payload`; runs the same supplier, line-item, and quotation-coverage checks as the web flow), `stage-bill-attachment` (securely uploads invoice/payment-proof files before creation and returns attachment JSON; ownership and one-hour expiry are enforced by a server-side staged-upload record rather than encoded in the object key), `cleanup-staged-bill-attachments`, `create-bill` (`--payload`; set `isClaimable=false` for a bill, `isClaimable=true` for a claim; supplier bills require positive `extractedAmount` and `amount`, with `amount <= extractedAmount`; an already-paid supplier bill sets `alreadyPaid=true` and requires `paymentReference` plus a staged PDF in `paymentProofAttachments`; admin creation automatically queues QBO finalization while other roles remain pending approval; non-legacy supplier bills from 1 Jul 2026 00:00 SGT also require approved quotation coverage), `update-bill` (`--payload`), `update-bill-payment-evidence` (`--payload`; replaces the payment reference and payment-proof PDFs for an already-paid bill), `delete-bill`, `create-bill-approval` (also sends approval request emails), `update-bill-status` (`PAID` requires `--paymentReference`; pass `--paymentProof <receipt.pdf>` to stage and submit a PDF up to 20MB atomically, or omit it only when BB already has payment proof; moving to `PAID` runs the server's paid-bill notification workflow), `patch-bill-payment` (PAID bills: `--paymentTrackingUrl`, `--paymentReference`, `--quickbooksBillId`, `--paymentDate` ISO; clearing a paid bill's QuickBooks link is not allowed), `patch-bill-invoice-number`, `get-bill-attachments`, `upload-bill-attachment` (`<billId>` + local path), `get-bill-details` |
181
+ | **Bills / claims** | `list-bills` (`--isClaimable false` for bills, `--isClaimable true` for claims, omit for both), `list-claims` (claims only), `validate-bill-selection` (`--payload`; runs the same supplier, line-item, and quotation-coverage checks as the web flow; `alreadyPaid` never bypasses them), `stage-bill-attachment` (securely uploads invoice/payment-proof files before creation and returns attachment JSON; ownership and one-hour expiry are enforced by a server-side staged-upload record rather than encoded in the object key), `cleanup-staged-bill-attachments`, `create-bill` (`--payload`; set `isClaimable=false` for a bill, `isClaimable=true` for a claim; supplier bills require positive `extractedAmount` and `amount`, with `amount <= extractedAmount`; an already-paid supplier bill sets `alreadyPaid=true` and requires `paymentReference` plus a staged PDF in `paymentProofAttachments`; admin creation automatically queues QBO finalization while other roles remain pending approval; non-legacy supplier bills from 1 Jul 2026 00:00 SGT require approved quotation coverage even when already paid), `update-bill` (`--payload`), `update-bill-payment-evidence` (`--payload`; replaces the payment reference and payment-proof PDFs for an already-paid bill), `delete-bill`, `create-bill-approval` (also sends approval request emails), `update-bill-status` (`PAID` requires `--paymentReference`; pass `--paymentProof <receipt.pdf>` to stage and submit a PDF up to 20MB atomically, or omit it only when BB already has payment proof; moving to `PAID` runs the server's paid-bill notification workflow), `patch-bill-payment` (PAID bills: `--paymentTrackingUrl`, `--paymentReference`, `--quickbooksBillId`, `--paymentDate` ISO; clearing a paid bill's QuickBooks link is not allowed), `patch-bill-invoice-number`, `get-bill-attachments`, `upload-bill-attachment` (`<billId>` + local path), `get-bill-details` |
166
182
  | **Quotations** | `list-quotations` (supports project, budget, supplier, status, requester, and text-search filters), `get-quotation`, `upload-quotation-attachment` (`<projectId>` + PDF/JPEG/PNG path up to 20MB; returns attachment JSON for payload use), `cleanup-staged-quotation-attachments`, `create-quotation` (`--payload` for `quotation.createDraft`; include `amountWithoutGst` and `gstAmount`), `update-quotation` (`--payload`; updates a `DRAFT` or `REJECTED` quotation), `delete-quotation`, `submit-quotation`, `approve-quotation`, `reject-quotation`, `download-quotation-pdf` (`original`, `staff`, or `final`) |
167
183
  | **Customer invoices** | `check-customer-invoice-readiness`, `list-eligible-customer-invoice-budgets`, `list-customer-invoices` (global/project/budget filters plus summary metrics), `get-customer-invoice`, `get-customer-invoice-email-context`, `create-customer-invoice`, `discard-customer-invoice`, `delete-customer-invoice`, `void-customer-invoice`, `approve-customer-invoice` (admin), `reject-customer-invoice` (admin), `send-customer-invoice-to-contact-person`, `mark-customer-invoice-paid` (admin; high-level proof upload + QBO Payment workflow), `download-customer-invoice-payment-proof`, `download-customer-invoice-pdf`, `sync-customer-invoice` |
168
184
  | **Approvals** | `list-approvals` / `get-pending-approvals` (`--type budget\|supplier\|bill\|quotation\|customer_invoice\|all`), `approve-bill` / `reject-bill` (send reply email), `approve-budget` / `reject-budget` (send reply email), `approve-supplier` / `reject-supplier` (send reply email), `approve-quotation` / `reject-quotation`, `approve-customer-invoice` / `reject-customer-invoice` |
@@ -178,10 +194,11 @@ The CLI intentionally wraps low-level upload-request/confirm procedures into fil
178
194
 
179
195
  ## Output
180
196
 
181
- - **Stdout:** JSON (pretty-printed)
182
- - **Stderr:** Error objects as JSON on failure
183
- - **Action logs (default on):** Each run logs to **stderr**: (1) JSON lines for command start, then `ok` or `error` with `durationMs` (`--payload` and similar flags are redacted); (2) tRPC lines for every API procedure (path, timing). **Stdout** stays JSON-only for piping (e.g. `| jq`). Suppress with `--quiet`, `-q`, or `BB_CLI_QUIET=1`.
184
- - **Exit code:** `0` on success, `1` on error
197
+ - **Canonical stdout:** a pretty-printed `{ "ok": true, "data": ..., "meta": ... }` JSON envelope
198
+ - **Canonical stderr:** a structured `{ "ok": false, "error": { "code", "message", ... }, "meta": ... }` JSON envelope on failure
199
+ - **Diagnostics:** off by default for canonical commands; use `--debug` for sanitized stderr tracing and `--quiet`, `-q`, or `BB_CLI_QUIET=1` to suppress diagnostics
200
+ - **Legacy compatibility:** flat aliases keep their raw success JSON, `{ "error": "..." }` failures, and default action tracing during the transition
201
+ - **Exit codes:** `0` success, `2` usage/validation/confirmation failure, `1` API/auth/network/internal failure, `130` interruption
185
202
 
186
203
  A `.env` file in the **current working directory** is loaded automatically (for `BB_API_KEY`, etc.).
187
204
 
@@ -191,15 +208,15 @@ From the monorepo root (after `bun install`):
191
208
 
192
209
  ```bash
193
210
  export BB_API_KEY=...
194
- bun run bb -- list-budgets
211
+ bun run golabs -- bb budget list
195
212
  ```
196
213
 
197
- Or from `packages/cli`:
214
+ Or from `packages/budget-builder/cli`:
198
215
 
199
216
  ```bash
200
217
  bun run build # emit dist/ via tsc
201
218
  bun run typecheck # tsc --noEmit
202
- bun run src/index.ts list-budgets
219
+ bun run src/index.ts budget list
203
220
  ```
204
221
 
205
222
  ### Layout
@@ -209,4 +226,13 @@ bun run src/index.ts list-budgets
209
226
 
210
227
  ### Publish
211
228
 
212
- `prepack` strips `workspace:` / `catalog:` entries from this package’s `package.json`, runs `tsc`, then `postpack` restores the file — so the npm tarball does not contain monorepo protocol dependencies. Types from `@go-labs/budget-builder-api` are compile-time only (`import type`). Prisma enum **values** used at runtime live in `src/prisma-enums.ts` (kept in sync with `packages/budget-builder/db` generated enums) so npm installs do not need `@go-labs/budget-builder-db`.
229
+ Build the package first, then create its publishable archive with Bun:
230
+
231
+ ```bash
232
+ bun run build
233
+ bun run pack:artifact
234
+ ```
235
+
236
+ `pack:artifact` copies only the publish allowlist into a temporary staging directory and writes a sanitized publish manifest there. It never edits the source `package.json`, and the staged archive contains no workspace-only development dependencies. The release workflow packs once, validates the resulting archive, tests that exact tarball under Node and Bun, then publishes that exact tarball. Types from `@go-labs/budget-builder-api` are compile-time only (`import type`). Prisma enum **values** used at runtime live in `src/prisma-enums.ts` (kept in sync with `packages/budget-builder/db` generated enums) so npm installs do not need `@go-labs/budget-builder-db`.
237
+
238
+ The generated `command-reference.md` and `command-manifest.json` are release artifacts. Regenerate them whenever the command registry changes; the release workflow rejects a tarball that does not include both files.