jaz-clio 4.35.0 → 4.35.2

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 (69) hide show
  1. package/assets/skills/api/SKILL.md +1 -1
  2. package/assets/skills/cli/SKILL.md +1 -1
  3. package/assets/skills/conversion/SKILL.md +1 -1
  4. package/assets/skills/jobs/SKILL.md +1 -1
  5. package/assets/skills/transaction-recipes/SKILL.md +1 -1
  6. package/dist/commands/accounts.js +6 -6
  7. package/dist/commands/attachments.js +8 -8
  8. package/dist/commands/auth.js +41 -41
  9. package/dist/commands/bank-rules.js +14 -14
  10. package/dist/commands/bank.js +27 -27
  11. package/dist/commands/bills.js +35 -35
  12. package/dist/commands/bookmarks.js +8 -8
  13. package/dist/commands/calc.js +2 -2
  14. package/dist/commands/capsules.js +14 -14
  15. package/dist/commands/cash-entry.js +16 -16
  16. package/dist/commands/cash-transfer.js +4 -4
  17. package/dist/commands/cashflow.js +2 -2
  18. package/dist/commands/contact-groups.js +10 -10
  19. package/dist/commands/contacts.js +14 -14
  20. package/dist/commands/currencies.js +2 -2
  21. package/dist/commands/currency-rates.js +8 -8
  22. package/dist/commands/custom-fields.js +12 -12
  23. package/dist/commands/customer-credit-notes.js +30 -30
  24. package/dist/commands/draft-helpers.js +12 -12
  25. package/dist/commands/exports.js +4 -4
  26. package/dist/commands/fixed-assets.js +23 -23
  27. package/dist/commands/format-helpers.js +5 -0
  28. package/dist/commands/help-examples.js +2 -2
  29. package/dist/commands/init.js +8 -8
  30. package/dist/commands/inventory.js +7 -7
  31. package/dist/commands/invoices.js +36 -36
  32. package/dist/commands/items.js +11 -11
  33. package/dist/commands/jobs.js +33 -33
  34. package/dist/commands/journals.js +27 -27
  35. package/dist/commands/kb.js +6 -6
  36. package/dist/commands/magic.js +40 -40
  37. package/dist/commands/nano-classifiers.js +8 -8
  38. package/dist/commands/org-users.js +5 -5
  39. package/dist/commands/org.js +8 -8
  40. package/dist/commands/output.js +3 -3
  41. package/dist/commands/pagination.js +2 -2
  42. package/dist/commands/parsers.js +20 -20
  43. package/dist/commands/payments.js +9 -9
  44. package/dist/commands/picker.js +88 -90
  45. package/dist/commands/quick-fix.js +8 -8
  46. package/dist/commands/recipe.js +16 -16
  47. package/dist/commands/reports.js +11 -11
  48. package/dist/commands/resolve.js +8 -8
  49. package/dist/commands/schedulers.js +25 -25
  50. package/dist/commands/schema.js +15 -24
  51. package/dist/commands/search.js +4 -4
  52. package/dist/commands/subscriptions.js +17 -17
  53. package/dist/commands/supplier-credit-notes.js +28 -28
  54. package/dist/commands/tags.js +10 -10
  55. package/dist/commands/tax-profiles.js +11 -11
  56. package/dist/commands/ui/index.js +1 -0
  57. package/dist/commands/ui/picker.js +180 -0
  58. package/dist/commands/ui/table.js +2 -3
  59. package/dist/commands/ui/theme.js +1 -0
  60. package/dist/commands/update.js +2 -2
  61. package/dist/commands/versions.js +3 -3
  62. package/dist/core/calc/format.js +126 -126
  63. package/dist/core/jobs/document-collection/tools/ingest/format.js +25 -25
  64. package/dist/core/jobs/format.js +23 -23
  65. package/dist/core/jobs/payment-run/tools/bank-file/format.js +7 -7
  66. package/dist/core/jobs/payment-run/tools/outstanding/format.js +9 -9
  67. package/dist/core/jobs/statutory-filing/tools/sg-tax/format-sg.js +19 -19
  68. package/dist/index.js +11 -11
  69. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import chalk from 'chalk';
1
+ import { danger, success, accent, warning, muted, highlight } from './ui/theme.js';
2
2
  import { formatStatus, formatId, formatReference, formatCurrency } from './format-helpers.js';
3
3
  import { listInvoices, getInvoice, searchInvoices, createInvoice, updateInvoice, deleteInvoice, createInvoicePayment, applyCreditsToInvoice, downloadInvoicePdf, finalizeInvoice, listInvoicePayments, listInvoiceCredits, reverseInvoiceCredit, } from '../core/api/invoices.js';
4
4
  import { listAttachments } from '../core/api/attachments.js';
@@ -48,19 +48,19 @@ export function registerInvoicesCommand(program) {
48
48
  console.log(JSON.stringify(inv, null, 2));
49
49
  }
50
50
  else {
51
- console.log(chalk.bold('Reference:'), inv.reference || '(none)');
52
- console.log(chalk.bold('ID:'), inv.resourceId);
53
- console.log(chalk.bold('Status:'), inv.status);
54
- console.log(chalk.bold('Date:'), normalizeDate(inv.valueDate));
55
- console.log(chalk.bold('Due:'), normalizeDate(inv.dueDate));
51
+ console.log(highlight('Reference:'), inv.reference || '(none)');
52
+ console.log(highlight('ID:'), inv.resourceId);
53
+ console.log(highlight('Status:'), inv.status);
54
+ console.log(highlight('Date:'), normalizeDate(inv.valueDate));
55
+ console.log(highlight('Due:'), normalizeDate(inv.dueDate));
56
56
  if (inv.contactName)
57
- console.log(chalk.bold('Contact:'), inv.contactName);
57
+ console.log(highlight('Contact:'), inv.contactName);
58
58
  if (inv.totalAmount !== undefined)
59
- console.log(chalk.bold('Total:'), inv.totalAmount.toFixed(2));
59
+ console.log(highlight('Total:'), inv.totalAmount.toFixed(2));
60
60
  if (inv.amountDue !== undefined)
61
- console.log(chalk.bold('Amount Due:'), inv.amountDue.toFixed(2));
61
+ console.log(highlight('Amount Due:'), inv.amountDue.toFixed(2));
62
62
  if (inv.lineItems?.length) {
63
- console.log(chalk.bold('Line Items:'));
63
+ console.log(highlight('Line Items:'));
64
64
  for (const li of inv.lineItems) {
65
65
  const qty = li.quantity ?? 1;
66
66
  const price = li.unitPrice ?? 0;
@@ -68,7 +68,7 @@ export function registerInvoicesCommand(program) {
68
68
  }
69
69
  }
70
70
  if (inv.notes)
71
- console.log(chalk.bold('Notes:'), inv.notes);
71
+ console.log(highlight('Notes:'), inv.notes);
72
72
  }
73
73
  })(opts));
74
74
  // ── clio invoices search ────────────────────────────────────────
@@ -178,8 +178,8 @@ export function registerInvoicesCommand(program) {
178
178
  }
179
179
  else {
180
180
  const status = opts.finalize ? 'finalized' : 'draft';
181
- console.log(chalk.green(`Invoice created (${status}): ${res.data.reference || res.data.resourceId}`));
182
- console.log(chalk.bold('ID:'), res.data.resourceId);
181
+ console.log(success(`Invoice created (${status}): ${res.data.reference || res.data.resourceId}`));
182
+ console.log(highlight('ID:'), res.data.resourceId);
183
183
  }
184
184
  }));
185
185
  // ── clio invoices update ────────────────────────────────────────
@@ -224,7 +224,7 @@ export function registerInvoicesCommand(program) {
224
224
  console.log(JSON.stringify(res.data, null, 2));
225
225
  }
226
226
  else {
227
- console.log(chalk.green(`Invoice updated: ${res.data.reference || res.data.resourceId}`));
227
+ console.log(success(`Invoice updated: ${res.data.reference || res.data.resourceId}`));
228
228
  }
229
229
  })(opts));
230
230
  // ── clio invoices delete ────────────────────────────────────────
@@ -239,7 +239,7 @@ export function registerInvoicesCommand(program) {
239
239
  console.log(JSON.stringify({ deleted: true, resourceId }));
240
240
  }
241
241
  else {
242
- console.log(chalk.green(`Invoice ${resourceId} deleted.`));
242
+ console.log(success(`Invoice ${resourceId} deleted.`));
243
243
  }
244
244
  })(opts));
245
245
  // ── clio invoices pay ───────────────────────────────────────────
@@ -296,7 +296,7 @@ export function registerInvoicesCommand(program) {
296
296
  console.log(JSON.stringify(res.data, null, 2));
297
297
  }
298
298
  else {
299
- console.log(chalk.green(`Payment recorded for invoice ${resourceId}`));
299
+ console.log(success(`Payment recorded for invoice ${resourceId}`));
300
300
  }
301
301
  })(opts));
302
302
  // ── clio invoices apply-credits ─────────────────────────────────
@@ -330,7 +330,7 @@ export function registerInvoicesCommand(program) {
330
330
  console.log(JSON.stringify(res.data, null, 2));
331
331
  }
332
332
  else {
333
- console.log(chalk.green(`Applied ${credits.length} credit(s) to invoice ${resourceId}`));
333
+ console.log(success(`Applied ${credits.length} credit(s) to invoice ${resourceId}`));
334
334
  }
335
335
  })(opts));
336
336
  // ── clio invoices download ──────────────────────────────────────
@@ -345,7 +345,7 @@ export function registerInvoicesCommand(program) {
345
345
  console.log(JSON.stringify(res.data, null, 2));
346
346
  }
347
347
  else {
348
- console.log(chalk.bold('PDF URL:'), res.data.fileUrl);
348
+ console.log(highlight('PDF URL:'), res.data.fileUrl);
349
349
  }
350
350
  })(opts));
351
351
  // ── clio invoices draft ───────────────────────────────────────
@@ -421,7 +421,7 @@ export function registerInvoicesCommand(program) {
421
421
  console.log(JSON.stringify({ finalized: false, error: msg }));
422
422
  }
423
423
  else {
424
- console.error(chalk.red(msg));
424
+ console.error(danger(msg));
425
425
  }
426
426
  process.exit(1);
427
427
  }
@@ -484,13 +484,13 @@ export function registerInvoicesCommand(program) {
484
484
  }
485
485
  else {
486
486
  if (ready) {
487
- console.log(chalk.green(`✓ ${merged.reference || resourceId} is ready to finalize.`));
487
+ console.log(success(`✓ ${merged.reference || resourceId} is ready to finalize.`));
488
488
  }
489
489
  else {
490
- console.error(chalk.yellow(`✗ ${merged.reference || resourceId} — ${missingCount} issue${missingCount > 1 ? 's' : ''} remaining:`));
490
+ console.error(warning(`✗ ${merged.reference || resourceId} — ${missingCount} issue${missingCount > 1 ? 's' : ''} remaining:`));
491
491
  for (const f of missingFields) {
492
492
  const spec = INVOICE_REQUIRED_FIELDS.find((s) => f === s.field || f.endsWith(`.${s.field}`));
493
- console.log(` ${f}: ${chalk.red('MISSING')} — ${spec?.hint ?? ''}`);
493
+ console.log(` ${f}: ${danger('MISSING')} — ${spec?.hint ?? ''}`);
494
494
  }
495
495
  }
496
496
  }
@@ -503,12 +503,12 @@ export function registerInvoicesCommand(program) {
503
503
  console.log(JSON.stringify({ finalized: false, resourceId, reference: merged.reference || null, ready: false, missingCount, missingFields, validation }, null, 2));
504
504
  }
505
505
  else {
506
- console.error(chalk.red(`\n✗ Cannot finalize ${merged.reference || resourceId} — ${missingCount} issue${missingCount > 1 ? 's' : ''} remaining:\n`));
506
+ console.error(danger(`\n✗ Cannot finalize ${merged.reference || resourceId} — ${missingCount} issue${missingCount > 1 ? 's' : ''} remaining:\n`));
507
507
  for (const f of missingFields) {
508
508
  const spec = INVOICE_REQUIRED_FIELDS.find((s) => f === s.field || f.endsWith(`.${s.field}`));
509
- console.error(` ${f}: ${chalk.red('MISSING')} — ${spec?.hint ?? ''}`);
509
+ console.error(` ${f}: ${danger('MISSING')} — ${spec?.hint ?? ''}`);
510
510
  }
511
- console.error(chalk.dim(`\n Fix the issues and retry:\n clio invoices draft finalize ${resourceId} ...\n`));
511
+ console.error(muted(`\n Fix the issues and retry:\n clio invoices draft finalize ${resourceId} ...\n`));
512
512
  }
513
513
  process.exit(1);
514
514
  }
@@ -522,10 +522,10 @@ export function registerInvoicesCommand(program) {
522
522
  console.log(JSON.stringify({ finalized: true, resourceId: updated.resourceId, reference: updated.reference || null, status: updated.status, fieldsUpdated }, null, 2));
523
523
  }
524
524
  else {
525
- console.log(chalk.green(`\n✓ Invoice finalized: ${updated.reference || updated.resourceId}`));
526
- console.log(chalk.bold(' Status:'), updated.status);
525
+ console.log(success(`\n✓ Invoice finalized: ${updated.reference || updated.resourceId}`));
526
+ console.log(highlight(' Status:'), updated.status);
527
527
  if (fieldsUpdated.length > 0) {
528
- console.log(chalk.bold(' Updated:'), fieldsUpdated.join(', '));
528
+ console.log(highlight(' Updated:'), fieldsUpdated.join(', '));
529
529
  }
530
530
  }
531
531
  })(opts));
@@ -548,11 +548,11 @@ export function registerInvoicesCommand(program) {
548
548
  console.log(`No attachments for invoice ${inv.reference || resourceId}.`);
549
549
  return;
550
550
  }
551
- console.log(chalk.bold(`Attachments for ${inv.reference || resourceId}:\n`));
551
+ console.log(highlight(`Attachments for ${inv.reference || resourceId}:\n`));
552
552
  for (const att of attachments) {
553
- console.log(` ${chalk.cyan(att.resourceId)} ${att.fileName || '(unnamed)'}`);
553
+ console.log(` ${accent(att.resourceId)} ${att.fileName || '(unnamed)'}`);
554
554
  if (att.fileUrl)
555
- console.log(` ${chalk.dim(att.fileUrl)}`);
555
+ console.log(` ${muted(att.fileUrl)}`);
556
556
  }
557
557
  }
558
558
  })(opts));
@@ -573,9 +573,9 @@ export function registerInvoicesCommand(program) {
573
573
  console.log('No payments recorded for this invoice.');
574
574
  return;
575
575
  }
576
- console.log(chalk.bold(`Payments for invoice ${resourceId}:\n`));
576
+ console.log(highlight(`Payments for invoice ${resourceId}:\n`));
577
577
  for (const p of payments) { // eslint-disable-line @typescript-eslint/no-explicit-any
578
- console.log(` ${chalk.cyan(p.resourceId)} ${p.paymentAmount ?? p.amount ?? ''} on ${p.valueDate ?? ''}`);
578
+ console.log(` ${accent(p.resourceId)} ${p.paymentAmount ?? p.amount ?? ''} on ${p.valueDate ?? ''}`);
579
579
  }
580
580
  }
581
581
  })(opts));
@@ -596,9 +596,9 @@ export function registerInvoicesCommand(program) {
596
596
  console.log('No credits applied to this invoice.');
597
597
  return;
598
598
  }
599
- console.log(chalk.bold(`Credits for invoice ${resourceId}:\n`));
599
+ console.log(highlight(`Credits for invoice ${resourceId}:\n`));
600
600
  for (const c of credits) { // eslint-disable-line @typescript-eslint/no-explicit-any
601
- console.log(` ${chalk.cyan(c.resourceId)} ${c.amount ?? ''}`);
601
+ console.log(` ${accent(c.resourceId)} ${c.amount ?? ''}`);
602
602
  }
603
603
  }
604
604
  })(opts));
@@ -614,7 +614,7 @@ export function registerInvoicesCommand(program) {
614
614
  console.log(JSON.stringify({ reversed: true, resourceId, creditResourceId }));
615
615
  }
616
616
  else {
617
- console.log(chalk.green(`Credit ${creditResourceId} reversed from invoice ${resourceId}.`));
617
+ console.log(success(`Credit ${creditResourceId} reversed from invoice ${resourceId}.`));
618
618
  }
619
619
  })(opts));
620
620
  }
@@ -1,4 +1,4 @@
1
- import chalk from 'chalk';
1
+ import { accent, highlight, success } from './ui/theme.js';
2
2
  import { listItems, getItem, searchItems, createItem, updateItem, deleteItem, } from '../core/api/items.js';
3
3
  import { findExistingItem } from '../core/api/guards.js';
4
4
  import { apiAction } from './api-action.js';
@@ -77,15 +77,15 @@ export function registerItemsCommand(program) {
77
77
  console.log(JSON.stringify(item, null, 2));
78
78
  }
79
79
  else {
80
- console.log(chalk.bold('Name:'), item.internalName);
81
- console.log(chalk.bold('Code:'), item.itemCode);
82
- console.log(chalk.bold('ID:'), item.resourceId);
80
+ console.log(highlight('Name:'), item.internalName);
81
+ console.log(highlight('Code:'), item.itemCode);
82
+ console.log(highlight('ID:'), item.resourceId);
83
83
  const types = [
84
84
  item.appliesToSale && 'Sale',
85
85
  item.appliesToPurchase && 'Purchase',
86
86
  ].filter(Boolean).join(', ');
87
87
  if (types)
88
- console.log(chalk.bold('Applies to:'), types);
88
+ console.log(highlight('Applies to:'), types);
89
89
  }
90
90
  })(opts));
91
91
  // ── clio items create ─────────────────────────────────────────
@@ -120,8 +120,8 @@ export function registerItemsCommand(program) {
120
120
  console.log(JSON.stringify(existing, null, 2));
121
121
  }
122
122
  else {
123
- console.log(chalk.cyan(`Item with code "${itemCode}" already exists`));
124
- console.log(chalk.bold('ID:'), existing.resourceId);
123
+ console.log(accent(`Item with code "${itemCode}" already exists`));
124
+ console.log(highlight('ID:'), existing.resourceId);
125
125
  }
126
126
  return;
127
127
  }
@@ -157,8 +157,8 @@ export function registerItemsCommand(program) {
157
157
  console.log(JSON.stringify(res.data, null, 2));
158
158
  }
159
159
  else {
160
- console.log(chalk.green(`Item created: ${opts.name}`));
161
- console.log(chalk.bold('ID:'), res.data.resourceId);
160
+ console.log(success(`Item created: ${opts.name}`));
161
+ console.log(highlight('ID:'), res.data.resourceId);
162
162
  }
163
163
  }));
164
164
  // ── clio items update ─────────────────────────────────────────
@@ -201,7 +201,7 @@ export function registerItemsCommand(program) {
201
201
  console.log(JSON.stringify(res.data, null, 2));
202
202
  }
203
203
  else {
204
- console.log(chalk.green(`Item updated: ${res.data.internalName}`));
204
+ console.log(success(`Item updated: ${res.data.internalName}`));
205
205
  }
206
206
  })(opts));
207
207
  // ── clio items delete ─────────────────────────────────────────
@@ -217,7 +217,7 @@ export function registerItemsCommand(program) {
217
217
  console.log(JSON.stringify({ deleted: true, resourceId }));
218
218
  }
219
219
  else {
220
- console.log(chalk.green(`Item ${resourceId} deleted.`));
220
+ console.log(success(`Item ${resourceId} deleted.`));
221
221
  }
222
222
  })(opts));
223
223
  }
@@ -1,4 +1,4 @@
1
- import chalk from 'chalk';
1
+ import { danger, success, warning, muted, highlight } from './ui/theme.js';
2
2
  import * as p from '@clack/prompts';
3
3
  import { readFileSync, writeFileSync } from 'node:fs';
4
4
  import { resolve } from 'node:path';
@@ -47,7 +47,7 @@ function jobAction(fn) {
47
47
  }
48
48
  catch (err) {
49
49
  if (err instanceof JobValidationError || err instanceof CalcValidationError || err instanceof TaxValidationError || err instanceof BankFileValidationError) {
50
- console.error(chalk.red(`Error: ${err.message}`));
50
+ console.error(danger(`Error: ${err.message}`));
51
51
  process.exit(1);
52
52
  }
53
53
  throw err;
@@ -140,7 +140,7 @@ async function fetchOutstandingForBankFile(opts, format) {
140
140
  // Auth + client
141
141
  const auth = requireAuth(opts.apiKey);
142
142
  const client = new JazClient(auth);
143
- process.stderr.write(chalk.dim(' Fetching outstanding bills...\n'));
143
+ process.stderr.write(muted(' Fetching outstanding bills...\n'));
144
144
  // Build search filter — UNPAID + balance > 0
145
145
  const filter = {
146
146
  status: { eq: 'UNPAID' },
@@ -181,12 +181,12 @@ async function fetchOutstandingForBankFile(opts, format) {
181
181
  dueBefore: opts.dueBefore,
182
182
  });
183
183
  // Display summary
184
- process.stderr.write(chalk.bold(` Found ${outstanding.supplierCount} supplier${outstanding.supplierCount !== 1 ? 's' : ''}`) +
185
- chalk.bold(` · ${outstanding.totalBills} bill${outstanding.totalBills !== 1 ? 's' : ''}`) +
186
- chalk.bold(` · $${outstanding.totalBalance.toLocaleString(undefined, { minimumFractionDigits: 2 })} ${outstanding.currency}\n\n`));
184
+ process.stderr.write(highlight(` Found ${outstanding.supplierCount} supplier${outstanding.supplierCount !== 1 ? 's' : ''}`) +
185
+ highlight(` · ${outstanding.totalBills} bill${outstanding.totalBills !== 1 ? 's' : ''}`) +
186
+ highlight(` · $${outstanding.totalBalance.toLocaleString(undefined, { minimumFractionDigits: 2 })} ${outstanding.currency}\n\n`));
187
187
  // Build multiselect choices from suppliers
188
188
  const choices = outstanding.suppliers.map(s => ({
189
- title: `${s.contactName} ${chalk.dim(`${s.billCount} bill${s.billCount !== 1 ? 's' : ''} $${s.totalBalance.toLocaleString(undefined, { minimumFractionDigits: 2 })}`)}`,
189
+ title: `${s.contactName} ${muted(`${s.billCount} bill${s.billCount !== 1 ? 's' : ''} $${s.totalBalance.toLocaleString(undefined, { minimumFractionDigits: 2 })}`)}`,
190
190
  value: s.contactResourceId,
191
191
  selected: true,
192
192
  }));
@@ -253,13 +253,13 @@ async function fetchOutstandingForBankFile(opts, format) {
253
253
  payees,
254
254
  };
255
255
  if (hasPlaceholders) {
256
- process.stderr.write(chalk.yellow('\n Note: Payee bank details (accountNumber, bankCode) are not available from the API.\n') +
257
- chalk.yellow(' Saving pre-filled template — fill in bank details, then re-run:\n\n') +
258
- chalk.dim(` clio jobs payment-run bank-file --${format} --input ${templatePath}\n\n`));
256
+ process.stderr.write(warning('\n Note: Payee bank details (accountNumber, bankCode) are not available from the API.\n') +
257
+ warning(' Saving pre-filled template — fill in bank details, then re-run:\n\n') +
258
+ muted(` clio jobs payment-run bank-file --${format} --input ${templatePath}\n\n`));
259
259
  writeFileSync(templatePath, JSON.stringify(templateData, null, 2) + '\n', { encoding: 'utf-8', mode: 0o600, flag: 'wx' });
260
- process.stderr.write(chalk.green(` Saved: ${templatePath}\n`));
261
- process.stderr.write(chalk.dim(` ${payees.length} payee${payees.length !== 1 ? 's' : ''} · $${selectedSuppliers.reduce((s, g) => s + g.totalBalance, 0).toLocaleString(undefined, { minimumFractionDigits: 2 })} ${outstanding.currency}\n`));
262
- process.stderr.write(chalk.dim(' Replace FILL_IN_ACCOUNT_NUMBER and FILL_IN_BANK_CODE for each payee.\n\n'));
260
+ process.stderr.write(success(` Saved: ${templatePath}\n`));
261
+ process.stderr.write(muted(` ${payees.length} payee${payees.length !== 1 ? 's' : ''} · $${selectedSuppliers.reduce((s, g) => s + g.totalBalance, 0).toLocaleString(undefined, { minimumFractionDigits: 2 })} ${outstanding.currency}\n`));
262
+ process.stderr.write(muted(' Replace FILL_IN_ACCOUNT_NUMBER and FILL_IN_BANK_CODE for each payee.\n\n'));
263
263
  process.exit(0);
264
264
  }
265
265
  return templateData;
@@ -507,7 +507,7 @@ export function registerJobsCommand(program) {
507
507
  truncated: res.totalElements > res.data.length,
508
508
  };
509
509
  if (fetched.truncated && !opts.json) {
510
- process.stderr.write(chalk.yellow(` Warning: ${res.totalElements.toLocaleString()} bills found, showing ${res.data.length.toLocaleString()} (API pagination bug — see api-dx-issues.md #9)\n`));
510
+ process.stderr.write(warning(` Warning: ${res.totalElements.toLocaleString()} bills found, showing ${res.data.length.toLocaleString()} (API pagination bug — see api-dx-issues.md #9)\n`));
511
511
  }
512
512
  // Enrich bills with contact names (batch lookup)
513
513
  const uniqueContactIds = Array.from(new Set(fetched.data.map(b => b.contactResourceId).filter(Boolean)));
@@ -645,7 +645,7 @@ export function registerJobsCommand(program) {
645
645
  };
646
646
  const forceType = opts.type ? typeMap[opts.type.toLowerCase()] : undefined;
647
647
  if (opts.type && !forceType) {
648
- console.error(chalk.red(`Error: Invalid --type "${opts.type}". Use: invoice, bill, credit-note-customer, credit-note-supplier, or bank-statement`));
648
+ console.error(danger(`Error: Invalid --type "${opts.type}". Use: invoice, bill, credit-note-customer, credit-note-supplier, or bank-statement`));
649
649
  process.exit(1);
650
650
  }
651
651
  // ── Scan + Classify ─────────────────────────────────────────
@@ -659,7 +659,7 @@ export function registerJobsCommand(program) {
659
659
  });
660
660
  }
661
661
  catch (err) {
662
- console.error(chalk.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
662
+ console.error(danger(`Error: ${err instanceof Error ? err.message : String(err)}`));
663
663
  process.exit(1);
664
664
  }
665
665
  // ── Scan-only mode (no --upload) ─────────────────────────────
@@ -673,21 +673,21 @@ export function registerJobsCommand(program) {
673
673
  .filter(f => f.documentType === 'UNKNOWN')
674
674
  .map(f => ` ${f.folder}/ (${f.count} files)`)
675
675
  .join('\n');
676
- console.error(chalk.red('Error: Cannot upload — some folders could not be classified:'));
677
- console.error(chalk.yellow(unknownFolders));
676
+ console.error(danger('Error: Cannot upload — some folders could not be classified:'));
677
+ console.error(warning(unknownFolders));
678
678
  console.error();
679
- console.error(chalk.dim('Fix: Re-run with --type to force a type for all files,'));
680
- console.error(chalk.dim(' or rename folders to match: invoices/, bills/, bank-statements/'));
679
+ console.error(muted('Fix: Re-run with --type to force a type for all files,'));
680
+ console.error(muted(' or rename folders to match: invoices/, bills/, bank-statements/'));
681
681
  process.exit(1);
682
682
  }
683
683
  if (plan.summary.uploadable === 0) {
684
- console.error(chalk.red('Error: No uploadable files found in the source.'));
684
+ console.error(danger('Error: No uploadable files found in the source.'));
685
685
  process.exit(1);
686
686
  }
687
687
  const hasBankStatements = plan.folders.some(f => f.documentType === 'BANK_STATEMENT');
688
688
  if (hasBankStatements && !opts.bankAccount) {
689
- console.error(chalk.red('Error: --bank-account is required when uploading bank statements.'));
690
- console.error(chalk.dim('Get your bank account ID: clio bank accounts --json'));
689
+ console.error(danger('Error: --bank-account is required when uploading bank statements.'));
690
+ console.error(muted('Get your bank account ID: clio bank accounts --json'));
691
691
  process.exit(1);
692
692
  }
693
693
  // ── Encrypted PDF checks ──────────────────────────────────────
@@ -706,11 +706,11 @@ export function registerJobsCommand(program) {
706
706
  }));
707
707
  }
708
708
  else {
709
- console.error(chalk.red('Error: Encrypted PDFs found but qpdf is not installed.'));
710
- console.error(chalk.yellow('Install qpdf to decrypt before upload:'));
711
- console.error(chalk.dim(' macOS: brew install qpdf'));
712
- console.error(chalk.dim(' Ubuntu: sudo apt install qpdf'));
713
- console.error(chalk.dim(' Windows: choco install qpdf'));
709
+ console.error(danger('Error: Encrypted PDFs found but qpdf is not installed.'));
710
+ console.error(warning('Install qpdf to decrypt before upload:'));
711
+ console.error(muted(' macOS: brew install qpdf'));
712
+ console.error(muted(' Ubuntu: sudo apt install qpdf'));
713
+ console.error(muted(' Windows: choco install qpdf'));
714
714
  }
715
715
  process.exit(1);
716
716
  }
@@ -727,14 +727,14 @@ export function registerJobsCommand(program) {
727
727
  process.exit(1);
728
728
  }
729
729
  // Interactive mode — prompt user for each encrypted file
730
- console.error(chalk.yellow(`\n${needPassword.length} encrypted PDF(s) need a password:\n`));
730
+ console.error(warning(`\n${needPassword.length} encrypted PDF(s) need a password:\n`));
731
731
  for (const f of needPassword) {
732
732
  const password = await p.text({
733
733
  message: `PDF password for ${f.folder}/${f.filename}`,
734
734
  });
735
735
  if (p.isCancel(password) || !password) {
736
- console.error(chalk.red('Aborted — no password provided.'));
737
- console.error(chalk.dim('Tip: embed password in filename to skip prompts: filename__pw__password.pdf'));
736
+ console.error(danger('Aborted — no password provided.'));
737
+ console.error(muted('Tip: embed password in filename to skip prompts: filename__pw__password.pdf'));
738
738
  process.exit(1);
739
739
  }
740
740
  f.filePassword = password;
@@ -748,11 +748,11 @@ export function registerJobsCommand(program) {
748
748
  const resolved = await resolveBankAccount(client, opts.bankAccount);
749
749
  bankAccountId = resolved.resourceId;
750
750
  if (!apiOpts.json && resolved.name !== resolved.resourceId) {
751
- process.stderr.write(chalk.dim(` Bank account: ${resolved.name} (${resolved.resourceId})\n`));
751
+ process.stderr.write(muted(` Bank account: ${resolved.name} (${resolved.resourceId})\n`));
752
752
  }
753
753
  }
754
754
  if (!apiOpts.json) {
755
- process.stderr.write(chalk.dim(`\n Uploading ${plan.summary.uploadable} files...\n`));
755
+ process.stderr.write(muted(`\n Uploading ${plan.summary.uploadable} files...\n`));
756
756
  }
757
757
  const upload = await uploadClassifiedFiles({
758
758
  plan,
@@ -1,4 +1,4 @@
1
- import chalk from 'chalk';
1
+ import { danger, success, accent, warning, muted, highlight } from './ui/theme.js';
2
2
  import { formatStatus, formatId, formatReference, formatCurrency } from './format-helpers.js';
3
3
  import { listJournals, searchJournals, createJournal, getJournal, updateJournal, deleteJournal, finalizeJournal, } from '../core/api/journals.js';
4
4
  import { createTransferTrialBalance } from '../core/api/transfer-trial-balance.js';
@@ -106,8 +106,8 @@ export function registerJournalsCommand(program) {
106
106
  }
107
107
  else {
108
108
  const status = opts.finalize ? 'finalized' : 'draft';
109
- console.log(chalk.green(`Journal created (${status}): ${res.data.reference || res.data.resourceId}`));
110
- console.log(chalk.bold('ID:'), res.data.resourceId);
109
+ console.log(success(`Journal created (${status}): ${res.data.reference || res.data.resourceId}`));
110
+ console.log(highlight('ID:'), res.data.resourceId);
111
111
  }
112
112
  }));
113
113
  // ── clio journals get ──────────────────────────────────────────
@@ -123,13 +123,13 @@ export function registerJournalsCommand(program) {
123
123
  }
124
124
  else {
125
125
  const status = formatStatus(j.status);
126
- console.log(chalk.bold('Journal:'), j.reference || '(no ref)');
127
- console.log(chalk.bold(' ID:'), chalk.cyan(j.resourceId));
128
- console.log(chalk.bold(' Status:'), status);
129
- console.log(chalk.bold(' Date:'), normalizeDate(j.valueDate));
130
- console.log(chalk.bold(' Notes:'), j.notes || '—');
126
+ console.log(highlight('Journal:'), j.reference || '(no ref)');
127
+ console.log(highlight(' ID:'), accent(j.resourceId));
128
+ console.log(highlight(' Status:'), status);
129
+ console.log(highlight(' Date:'), normalizeDate(j.valueDate));
130
+ console.log(highlight(' Notes:'), j.notes || '—');
131
131
  if (j.journalEntries?.length) {
132
- console.log(chalk.bold(` Entries (${j.journalEntries.length}):`));
132
+ console.log(highlight(` Entries (${j.journalEntries.length}):`));
133
133
  for (const e of j.journalEntries) {
134
134
  // GET response has richer fields than the create JournalEntry type
135
135
  const entry = e;
@@ -137,7 +137,7 @@ export function registerJournalsCommand(program) {
137
137
  const dr = entry.debitAmount;
138
138
  const cr = entry.creditAmount;
139
139
  const amt = dr != null ? `DR ${dr}` : `CR ${cr}`;
140
- console.log(` ${chalk.dim(String(acct).slice(0, 12) + '...')} ${amt} ${e.description || ''}`);
140
+ console.log(` ${muted(String(acct).slice(0, 12) + '...')} ${amt} ${e.description || ''}`);
141
141
  }
142
142
  }
143
143
  }
@@ -186,7 +186,7 @@ export function registerJournalsCommand(program) {
186
186
  }
187
187
  else {
188
188
  const status = opts.finalize ? 'finalized' : 'draft';
189
- console.log(chalk.green(`Journal updated (${status}): ${res.data.reference || res.data.resourceId}`));
189
+ console.log(success(`Journal updated (${status}): ${res.data.reference || res.data.resourceId}`));
190
190
  }
191
191
  })(opts));
192
192
  // ── clio journals delete ────────────────────────────────────────
@@ -201,7 +201,7 @@ export function registerJournalsCommand(program) {
201
201
  console.log(JSON.stringify({ deleted: true, resourceId }));
202
202
  }
203
203
  else {
204
- console.log(chalk.green(`Journal ${resourceId} deleted.`));
204
+ console.log(success(`Journal ${resourceId} deleted.`));
205
205
  }
206
206
  })(opts));
207
207
  // ── clio journals transfer-trial-balance ─────────────────────
@@ -239,8 +239,8 @@ export function registerJournalsCommand(program) {
239
239
  console.log(JSON.stringify(res.data, null, 2));
240
240
  }
241
241
  else {
242
- console.log(chalk.green('Transfer Trial Balance created.'));
243
- console.log(chalk.bold('ID:'), res.data.resourceId);
242
+ console.log(success('Transfer Trial Balance created.'));
243
+ console.log(highlight('ID:'), res.data.resourceId);
244
244
  }
245
245
  }));
246
246
  // ── clio journals draft ──────────────────────────────────────
@@ -316,7 +316,7 @@ export function registerJournalsCommand(program) {
316
316
  console.log(JSON.stringify({ finalized: false, error: msg }));
317
317
  }
318
318
  else {
319
- console.error(chalk.red(msg));
319
+ console.error(danger(msg));
320
320
  }
321
321
  process.exit(1);
322
322
  }
@@ -362,13 +362,13 @@ export function registerJournalsCommand(program) {
362
362
  }
363
363
  else {
364
364
  if (ready) {
365
- console.log(chalk.green(`✓ ${merged.reference || resourceId} is ready to finalize.`));
365
+ console.log(success(`✓ ${merged.reference || resourceId} is ready to finalize.`));
366
366
  }
367
367
  else {
368
- console.error(chalk.yellow(`✗ ${merged.reference || resourceId} — ${missingCount} issue${missingCount > 1 ? 's' : ''} remaining:`));
368
+ console.error(warning(`✗ ${merged.reference || resourceId} — ${missingCount} issue${missingCount > 1 ? 's' : ''} remaining:`));
369
369
  for (const f of missingFields) {
370
370
  const spec = JOURNAL_REQUIRED_FIELDS.find((s) => f === s.field || f.endsWith(`.${s.field}`));
371
- console.log(` ${f}: ${chalk.red('MISSING')} — ${spec?.hint ?? ''}`);
371
+ console.log(` ${f}: ${danger('MISSING')} — ${spec?.hint ?? ''}`);
372
372
  }
373
373
  }
374
374
  }
@@ -381,12 +381,12 @@ export function registerJournalsCommand(program) {
381
381
  console.log(JSON.stringify({ finalized: false, resourceId, reference: merged.reference || null, ready: false, missingCount, missingFields, validation }, null, 2));
382
382
  }
383
383
  else {
384
- console.error(chalk.red(`\n✗ Cannot finalize ${merged.reference || resourceId} — ${missingCount} issue${missingCount > 1 ? 's' : ''} remaining:\n`));
384
+ console.error(danger(`\n✗ Cannot finalize ${merged.reference || resourceId} — ${missingCount} issue${missingCount > 1 ? 's' : ''} remaining:\n`));
385
385
  for (const f of missingFields) {
386
386
  const spec = JOURNAL_REQUIRED_FIELDS.find((s) => f === s.field || f.endsWith(`.${s.field}`));
387
- console.error(` ${f}: ${chalk.red('MISSING')} — ${spec?.hint ?? ''}`);
387
+ console.error(` ${f}: ${danger('MISSING')} — ${spec?.hint ?? ''}`);
388
388
  }
389
- console.error(chalk.dim(`\n Fix the issues and retry:\n clio journals draft finalize ${resourceId} ...\n`));
389
+ console.error(muted(`\n Fix the issues and retry:\n clio journals draft finalize ${resourceId} ...\n`));
390
390
  }
391
391
  process.exit(1);
392
392
  }
@@ -400,10 +400,10 @@ export function registerJournalsCommand(program) {
400
400
  console.log(JSON.stringify({ finalized: true, resourceId: updated.resourceId, reference: updated.reference || null, status: updated.status, fieldsUpdated }, null, 2));
401
401
  }
402
402
  else {
403
- console.log(chalk.green(`\n✓ Journal finalized: ${updated.reference || updated.resourceId}`));
404
- console.log(chalk.bold(' Status:'), updated.status);
403
+ console.log(success(`\n✓ Journal finalized: ${updated.reference || updated.resourceId}`));
404
+ console.log(highlight(' Status:'), updated.status);
405
405
  if (fieldsUpdated.length > 0) {
406
- console.log(chalk.bold(' Updated:'), fieldsUpdated.join(', '));
406
+ console.log(highlight(' Updated:'), fieldsUpdated.join(', '));
407
407
  }
408
408
  }
409
409
  })(opts));
@@ -426,11 +426,11 @@ export function registerJournalsCommand(program) {
426
426
  console.log(`No attachments for journal ${journal.reference || resourceId}.`);
427
427
  return;
428
428
  }
429
- console.log(chalk.bold(`Attachments for ${journal.reference || resourceId}:\n`));
429
+ console.log(highlight(`Attachments for ${journal.reference || resourceId}:\n`));
430
430
  for (const att of attachments) {
431
- console.log(` ${chalk.cyan(att.resourceId)} ${att.fileName || '(unnamed)'}`);
431
+ console.log(` ${accent(att.resourceId)} ${att.fileName || '(unnamed)'}`);
432
432
  if (att.fileUrl)
433
- console.log(` ${chalk.dim(att.fileUrl)}`);
433
+ console.log(` ${muted(att.fileUrl)}`);
434
434
  }
435
435
  }
436
436
  })(opts));
@@ -1,6 +1,6 @@
1
- import chalk from 'chalk';
2
1
  import { loadIndex, searchArticles, } from '../core/kb/index.js';
3
2
  import { loadEmbeddings, semanticSearch, isSemanticAvailable, } from '../core/kb/embeddings.js';
3
+ import { accent, danger, highlight, muted } from './ui/theme.js';
4
4
  export function registerKbCommand(program) {
5
5
  program
6
6
  .command('help-center')
@@ -46,22 +46,22 @@ export function registerKbCommand(program) {
46
46
  return;
47
47
  }
48
48
  console.log('');
49
- console.log(chalk.bold(` ${results.length} result${results.length !== 1 ? 's' : ''} for "${query}"`));
49
+ console.log(highlight(` ${results.length} result${results.length !== 1 ? 's' : ''} for "${query}"`));
50
50
  console.log('');
51
51
  for (let i = 0; i < results.length; i++) {
52
52
  const r = results[i];
53
- console.log(chalk.cyan(` ${i + 1}. ${r.article.title}`) + chalk.dim(` (${r.section.name})`));
53
+ console.log(accent(` ${i + 1}. ${r.article.title}`) + muted(` (${r.section.name})`));
54
54
  if (r.article.snippet) {
55
- console.log(chalk.dim(` ${r.article.snippet.slice(0, 120)}${r.article.snippet.length > 120 ? '...' : ''}`));
55
+ console.log(muted(` ${r.article.snippet.slice(0, 120)}${r.article.snippet.length > 120 ? '...' : ''}`));
56
56
  }
57
57
  if (r.article.sourceUrl) {
58
- console.log(chalk.dim(` ${r.article.sourceUrl}`));
58
+ console.log(muted(` ${r.article.sourceUrl}`));
59
59
  }
60
60
  console.log('');
61
61
  }
62
62
  }
63
63
  catch (err) {
64
- console.error(chalk.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
64
+ console.error(danger(`Error: ${err instanceof Error ? err.message : String(err)}`));
65
65
  process.exit(1);
66
66
  }
67
67
  });