@go-labs-sg/bb 1.12.0 → 1.13.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
@@ -56,7 +56,7 @@ Global options and flags use `--key=value` or `--key value` (see `bb help`).
56
56
 
57
57
  **Budget status automation:** Setting a budget to `ESTIMATE_ACCEPTED` requires a confirmed win-proof attachment. If the parent project is `PITCH` or `LOST`, the API now marks it `WON` automatically and, when the `asana-tasks` feature flag is enabled, creates the Asana project/section/tasks automatically. When rejecting the only accepted/closed budget on a commercial project, pass `--projectStatusOnCommercialRejection PITCH|LOST`.
58
58
 
59
- **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. 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).
59
+ **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).
60
60
 
61
61
  ### Command overview
62
62
 
package/dist/commands.js CHANGED
@@ -5,6 +5,7 @@ import { api } from "./api-client.js";
5
5
  import { BudgetRole, Deals, ExtendedApprovalStatus, ExtendedApprovalType, ExtendedBudgetStatus, TimeFrame, } from "./filter-enums.js";
6
6
  import { billStatusesForApi, } from "./parse-cli-enums.js";
7
7
  import { parseBudgetDiscountPayload, parseCompanyUpdatePayload, parseContactCreatePayload, parseContactUpdatePayload, parseCreateBillPayload, parseCreateBudgetPayload, parseItemCreatePayload, parseItemUpdatePayload, parseSupplierCreatePayload, parseSupplierUpdatePayload, parseUpdateBillPayload, parseUpdateBudgetCommissionPayload, parseUpdateBudgetPayload, parseUpdateProjectPayload, } from "./parse-mutation-payload.js";
8
+ import { createRichTextFromPlainText } from "./rich-text.js";
8
9
  const BUDGET = "BUDGET";
9
10
  const BILL = "BILL";
10
11
  const SUPPLIER = "SUPPLIER";
@@ -396,7 +397,17 @@ export async function addBudgetItems(opts) {
396
397
  out(result);
397
398
  }
398
399
  export async function updateBudgetItem(input) {
399
- const result = await api.budgetItem.updateBudgetItem.mutate(input);
400
+ const result = await api.budgetItem.updateBudgetItem.mutate({
401
+ ...input,
402
+ ...(input.description !== undefined &&
403
+ input.descriptionRichText === undefined && {
404
+ descriptionRichText: createRichTextFromPlainText(input.description),
405
+ }),
406
+ ...(input.note !== undefined &&
407
+ input.noteRichText === undefined && {
408
+ noteRichText: createRichTextFromPlainText(input.note),
409
+ }),
410
+ });
400
411
  out(result);
401
412
  }
402
413
  export async function removeBudgetItem(budgetItemId) {
package/dist/index.js CHANGED
@@ -137,7 +137,7 @@ Budgets
137
137
  create-budget-approval <budgetId> (also sends approval request emails)
138
138
  create-estimate <budgetId> (creates QuickBooks estimate; supports preflight updates)
139
139
  add-budget-items --budgetId --items '[{"itemId":"…","quantity":1,"markup":30},…]'
140
- update-budget-item --id <budgetItemId> [--description] [--note] [--quantity] [--markup] [--cost] [--unitPrice] [--isFreeOfCharge true|false] [--gstInclusive true|false]
140
+ update-budget-item --id <budgetItemId> [--description plain-text] [--note plain-text] [--quantity] [--markup] [--cost] [--unitPrice] [--isFreeOfCharge true|false] [--gstInclusive true|false]
141
141
  remove-budget-item <budgetItemId>
142
142
  reorder-budget-items <budgetId> --itemIds <csv>
143
143
  update-budget-item-supplier --budgetItemId --supplierId
@@ -1,3 +1,4 @@
1
+ import { normalizePlainTextRichTextField } from "./rich-text.js";
1
2
  function requireObject(raw, label) {
2
3
  if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
3
4
  throw new Error(`${label} must be a JSON object.`);
@@ -237,12 +238,20 @@ export function parseSupplierUpdatePayload(raw) {
237
238
  return raw;
238
239
  }
239
240
  export function parseItemCreatePayload(raw) {
240
- requireObject(raw, "create-item payload");
241
- return raw;
241
+ const input = requireObject(raw, "create-item payload");
242
+ return normalizePlainTextRichTextField({
243
+ input,
244
+ textField: "description",
245
+ richTextField: "descriptionRichText",
246
+ });
242
247
  }
243
248
  export function parseItemUpdatePayload(raw) {
244
- requireObject(raw, "update-item payload");
245
- return raw;
249
+ const input = requireObject(raw, "update-item payload");
250
+ return normalizePlainTextRichTextField({
251
+ input,
252
+ textField: "description",
253
+ richTextField: "descriptionRichText",
254
+ });
246
255
  }
247
256
  export function parseContactCreatePayload(raw) {
248
257
  requireObject(raw, "create-contact-person payload");
@@ -0,0 +1,34 @@
1
+ const escapeHtml = (text) => text
2
+ .replace(/&/g, "&amp;")
3
+ .replace(/</g, "&lt;")
4
+ .replace(/>/g, "&gt;")
5
+ .replace(/"/g, "&quot;")
6
+ .replace(/'/g, "&#39;");
7
+ export const createRichTextFromPlainText = (text) => {
8
+ const lines = text?.split(/\r?\n/) ?? [];
9
+ const paragraphs = lines.length ? lines : [""];
10
+ return {
11
+ json: {
12
+ type: "doc",
13
+ content: paragraphs.map((line) => ({
14
+ type: "paragraph",
15
+ ...(line ? { content: [{ type: "text", text: line }] } : {}),
16
+ })),
17
+ },
18
+ html: paragraphs.map((line) => `<p>${escapeHtml(line)}</p>`).join(""),
19
+ };
20
+ };
21
+ export const isRichTextJson = (value) => Boolean(value &&
22
+ typeof value === "object" &&
23
+ "json" in value &&
24
+ "html" in value &&
25
+ typeof value.html === "string");
26
+ export const normalizePlainTextRichTextField = ({ input, textField, richTextField, }) => {
27
+ if (isRichTextJson(input[richTextField]) || input[textField] === undefined) {
28
+ return input;
29
+ }
30
+ return {
31
+ ...input,
32
+ [richTextField]: createRichTextFromPlainText(String(input[textField] ?? "")),
33
+ };
34
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@go-labs-sg/bb",
3
- "version": "1.12.0",
3
+ "version": "1.13.0",
4
4
  "description": "Budget Builder CLI for AI agents — manage budgets, bills, and claims; bill records use isClaimable=false for bills and isClaimable=true for claims.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -21,7 +21,7 @@
21
21
  "postpack": "node scripts/restore-package-json.js"
22
22
  },
23
23
  "dependencies": {
24
- "@trpc/client": "^11.17.0",
24
+ "@trpc/client": "^11.18.0",
25
25
  "dotenv": "^17.4.2",
26
26
  "mime-types": "^3.0.2",
27
27
  "superjson": "^2.2.6"