jaz-clio 4.34.6 → 4.35.1

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 (76) 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/api-action.js +22 -22
  8. package/dist/commands/attachments.js +8 -8
  9. package/dist/commands/auth.js +50 -48
  10. package/dist/commands/bank-rules.js +14 -14
  11. package/dist/commands/bank.js +27 -27
  12. package/dist/commands/bills.js +35 -35
  13. package/dist/commands/bookmarks.js +8 -8
  14. package/dist/commands/calc.js +2 -2
  15. package/dist/commands/capsules.js +14 -14
  16. package/dist/commands/cash-entry.js +16 -16
  17. package/dist/commands/cash-transfer.js +4 -4
  18. package/dist/commands/cashflow.js +2 -2
  19. package/dist/commands/contact-groups.js +10 -10
  20. package/dist/commands/contacts.js +14 -14
  21. package/dist/commands/currencies.js +2 -2
  22. package/dist/commands/currency-rates.js +8 -8
  23. package/dist/commands/custom-fields.js +12 -12
  24. package/dist/commands/customer-credit-notes.js +30 -30
  25. package/dist/commands/draft-helpers.js +12 -12
  26. package/dist/commands/exports.js +4 -4
  27. package/dist/commands/fixed-assets.js +23 -23
  28. package/dist/commands/format-helpers.js +28 -14
  29. package/dist/commands/help-examples.js +2 -2
  30. package/dist/commands/init.js +26 -28
  31. package/dist/commands/inventory.js +7 -7
  32. package/dist/commands/invoices.js +36 -36
  33. package/dist/commands/items.js +11 -11
  34. package/dist/commands/jobs.js +80 -78
  35. package/dist/commands/journals.js +27 -27
  36. package/dist/commands/kb.js +6 -6
  37. package/dist/commands/magic.js +44 -46
  38. package/dist/commands/nano-classifiers.js +8 -8
  39. package/dist/commands/org-users.js +5 -5
  40. package/dist/commands/org.js +8 -8
  41. package/dist/commands/output.js +5 -4
  42. package/dist/commands/pagination.js +10 -18
  43. package/dist/commands/parsers.js +20 -20
  44. package/dist/commands/payments.js +9 -9
  45. package/dist/commands/picker.js +38 -59
  46. package/dist/commands/quick-fix.js +8 -8
  47. package/dist/commands/recipe.js +16 -16
  48. package/dist/commands/reports.js +11 -11
  49. package/dist/commands/resolve.js +8 -8
  50. package/dist/commands/schedulers.js +25 -25
  51. package/dist/commands/schema.js +15 -24
  52. package/dist/commands/search.js +4 -4
  53. package/dist/commands/subscriptions.js +17 -17
  54. package/dist/commands/supplier-credit-notes.js +28 -28
  55. package/dist/commands/table-formatter.js +3 -82
  56. package/dist/commands/tags.js +10 -10
  57. package/dist/commands/tax-profiles.js +11 -11
  58. package/dist/commands/ui/banner.js +21 -0
  59. package/dist/commands/ui/error.js +96 -0
  60. package/dist/commands/ui/index.js +7 -0
  61. package/dist/commands/ui/progress.js +32 -0
  62. package/dist/commands/ui/record.js +72 -0
  63. package/dist/commands/ui/table.js +109 -0
  64. package/dist/commands/ui/theme.js +55 -0
  65. package/dist/commands/update.js +2 -2
  66. package/dist/commands/versions.js +3 -3
  67. package/dist/core/calc/format.js +126 -126
  68. package/dist/core/jobs/document-collection/tools/ingest/format.js +25 -25
  69. package/dist/core/jobs/format.js +23 -23
  70. package/dist/core/jobs/payment-run/tools/bank-file/format.js +7 -7
  71. package/dist/core/jobs/payment-run/tools/outstanding/format.js +9 -9
  72. package/dist/core/jobs/statutory-filing/tools/sg-tax/format-sg.js +19 -19
  73. package/dist/core/registry/tools.js +0 -2
  74. package/dist/index.js +30 -31
  75. package/dist/utils/logger.js +17 -7
  76. package/package.json +4 -3
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-api
3
- version: 4.34.6
3
+ version: 4.35.1
4
4
  description: >-
5
5
  Use this skill whenever you call, debug, or review code that touches the Jaz
6
6
  REST API. Covers field names, response shapes, 117 production gotchas, error
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-cli
3
- version: 4.34.6
3
+ version: 4.35.1
4
4
  description: >-
5
5
  Use this skill when running Clio CLI commands, building shell scripts with
6
6
  Clio, debugging auth issues, understanding --json output, paginating results,
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-conversion
3
- version: 4.34.6
3
+ version: 4.35.1
4
4
  description: >-
5
5
  Use this skill when migrating accounting data into Jaz — importing from Xero,
6
6
  QuickBooks, Sage, MYOB, or Excel exports. Covers the full conversion pipeline:
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-jobs
3
- version: 4.34.6
3
+ version: 4.35.1
4
4
  description: >-
5
5
  Use this skill for recurring accounting workflows — month/quarter/year-end
6
6
  close, bank reconciliation, GST/VAT filing, payment runs, credit control,
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-recipes
3
- version: 4.34.6
3
+ version: 4.35.1
4
4
  description: >-
5
5
  Use this skill when modeling complex multi-step accounting transactions —
6
6
  anything that spans multiple periods, involves changing amounts, or requires
@@ -1,4 +1,3 @@
1
- import chalk from 'chalk';
2
1
  import { listAccounts, searchAccounts, createAccount, deleteAccount } from '../core/api/chart-of-accounts.js';
3
2
  import { findExistingAccount, normalizeAccountType } from '../core/api/guards.js';
4
3
  import { apiAction } from './api-action.js';
@@ -6,6 +5,7 @@ import { outputList } from './output.js';
6
5
  import { parsePositiveInt, parseNonNegativeInt, readBodyInput, requireFields } from './parsers.js';
7
6
  import { paginatedFetch } from './pagination.js';
8
7
  import { formatId } from './format-helpers.js';
8
+ import { accent, highlight, success } from './ui/theme.js';
9
9
  const ACCOUNTS_COLUMNS = [
10
10
  { key: 'resourceId', header: 'ID', format: formatId },
11
11
  { key: 'code', header: 'Code' },
@@ -74,8 +74,8 @@ export function registerAccountsCommand(program) {
74
74
  console.log(JSON.stringify(existing, null, 2));
75
75
  }
76
76
  else {
77
- console.log(chalk.cyan(`Account "${acctName}" already exists`));
78
- console.log(chalk.bold('ID:'), existing.resourceId);
77
+ console.log(accent(`Account "${acctName}" already exists`));
78
+ console.log(highlight('ID:'), existing.resourceId);
79
79
  }
80
80
  return;
81
81
  }
@@ -105,8 +105,8 @@ export function registerAccountsCommand(program) {
105
105
  console.log(JSON.stringify(res.data, null, 2));
106
106
  }
107
107
  else {
108
- console.log(chalk.green(`Account created: ${res.data.name} (${res.data.code})`));
109
- console.log(chalk.bold('ID:'), res.data.resourceId);
108
+ console.log(success(`Account created: ${res.data.name} (${res.data.code})`));
109
+ console.log(highlight('ID:'), res.data.resourceId);
110
110
  }
111
111
  }));
112
112
  // ── clio accounts delete ──────────────────────────────────────
@@ -122,7 +122,7 @@ export function registerAccountsCommand(program) {
122
122
  console.log(JSON.stringify({ deleted: true, resourceId }));
123
123
  }
124
124
  else {
125
- console.log(chalk.green(`Account ${resourceId} deleted.`));
125
+ console.log(success(`Account ${resourceId} deleted.`));
126
126
  }
127
127
  })(opts));
128
128
  }
@@ -1,15 +1,23 @@
1
- import chalk from 'chalk';
2
1
  import { JazClient, JazApiError } from '../core/api/client.js';
3
2
  import { requireAuth, AuthError, resolvedProfileLabel, resolvedAuthSource, getProfile, listProfiles } from '../core/auth/index.js';
4
3
  import { isMachineFormat } from './output.js';
4
+ import { renderOrgBanner } from './ui/banner.js';
5
+ import { formatApiError, formatAuthError, formatGenericError } from './ui/error.js';
5
6
  /**
6
7
  * Shared action wrapper for all online CLI commands.
7
- * Handles auth resolution, client creation, org banner, and error formatting.
8
+ * Handles auth resolution, client creation, org banner, spinner, and error formatting.
8
9
  *
9
10
  * Exit codes: 1 = validation, 2 = API error, 3 = auth error.
10
11
  */
11
12
  export function apiAction(fn) {
12
13
  return async (opts) => {
14
+ let spinner;
15
+ // Hoist machine flag — computed once, safe to use in catch even if resolveFormat would throw
16
+ let machine = false;
17
+ try {
18
+ machine = isMachineFormat(opts);
19
+ }
20
+ catch { /* --json + --format conflict — treat as human mode for error display */ }
13
21
  try {
14
22
  // Conflict check: --api-key and --org are mutually exclusive
15
23
  if (opts.apiKey && opts.org) {
@@ -17,9 +25,8 @@ export function apiAction(fn) {
17
25
  }
18
26
  const auth = requireAuth(opts.apiKey);
19
27
  const client = new JazClient(auth);
20
- // Org banner — show which org we're hitting (suppressed in machine formats)
21
- // Visual guard: yellow warning when unpinned + multi-org, dim banner otherwise
22
- if (!isMachineFormat(opts)) {
28
+ // Org banner — stderr, suppressed in machine formats
29
+ if (!machine) {
23
30
  const label = resolvedProfileLabel();
24
31
  if (label) {
25
32
  const entry = getProfile(label);
@@ -31,43 +38,36 @@ export function apiAction(fn) {
31
38
  orgCount = Object.keys(listProfiles() ?? {}).length;
32
39
  }
33
40
  catch { /* best-effort */ }
34
- if (!isPinned && orgCount > 1) {
35
- // UNPINNED multi-org: prominent yellow warning
36
- process.stderr.write(chalk.yellow(` \u26A0 ${label} \u00B7 ${entry.orgName} (${entry.currency})`) +
37
- chalk.dim(` \u2014 not pinned to this terminal\n`) +
38
- chalk.dim(` Pin: export JAZ_ORG=${label} or --org ${label}\n`));
39
- }
40
- else {
41
- // Pinned or single-org: normal dim banner
42
- process.stderr.write(chalk.dim(` \u25B8 ${label} \u00B7 ${entry.orgName} (${entry.currency})\n`));
43
- }
41
+ renderOrgBanner({ label, orgName: entry.orgName, currency: entry.currency }, isPinned, orgCount > 1);
44
42
  }
45
43
  }
46
44
  }
47
45
  await fn(client, opts, auth);
48
46
  }
49
47
  catch (err) {
50
- const machine = isMachineFormat(opts);
51
48
  if (err instanceof AuthError) {
52
49
  if (machine)
53
- console.log(JSON.stringify({ error: { code: 'AUTH_ERROR', message: err.message } }));
50
+ console.error(JSON.stringify({ error: { code: 'AUTH_ERROR', message: err.message } }));
54
51
  else
55
- console.error(chalk.red(`Error: ${err.message}`));
52
+ console.error(formatAuthError(err.message));
56
53
  process.exit(3);
57
54
  }
58
55
  if (err instanceof JazApiError) {
59
56
  if (machine)
60
- console.log(JSON.stringify({ error: { code: 'API_ERROR', status: err.status, message: err.message } }));
57
+ console.error(JSON.stringify({ error: { code: 'API_ERROR', status: err.status, message: err.message } }));
61
58
  else
62
- console.error(chalk.red(`API Error (${err.status}): ${err.message}`));
59
+ console.error(formatApiError(err.status, err.message, err.endpoint));
63
60
  process.exit(2);
64
61
  }
65
62
  const message = err.message;
66
63
  if (machine)
67
- console.log(JSON.stringify({ error: { code: 'UNKNOWN_ERROR', message } }));
64
+ console.error(JSON.stringify({ error: { code: 'UNKNOWN_ERROR', message } }));
68
65
  else
69
- console.error(chalk.red(`Error: ${message}`));
66
+ console.error(formatGenericError(message));
70
67
  process.exit(2);
71
68
  }
69
+ finally {
70
+ spinner?.stop();
71
+ }
72
72
  };
73
73
  }
@@ -1,10 +1,10 @@
1
- import chalk from 'chalk';
2
1
  import { readFileSync } from 'node:fs';
3
2
  import { basename, extname, resolve } from 'node:path';
4
3
  import { listAttachments, addAttachment, deleteAttachment, fetchAttachmentTable } from '../core/api/attachments.js';
5
4
  import { apiAction } from './api-action.js';
6
5
  import { outputList } from './output.js';
7
6
  import { formatId } from './format-helpers.js';
7
+ import { danger, success } from './ui/theme.js';
8
8
  const ATTACHMENTS_COLUMNS = [
9
9
  { key: 'resourceId', header: 'ID', format: formatId },
10
10
  { key: 'fileName', header: 'File Name' },
@@ -38,7 +38,7 @@ export function registerAttachmentsCommand(program) {
38
38
  .action(apiAction(async (client, opts) => {
39
39
  const btType = opts.type;
40
40
  if (!BT_TYPES.includes(btType)) {
41
- console.error(chalk.red(`Invalid type. Use one of: ${BT_TYPES.join(', ')}`));
41
+ console.error(danger(`Invalid type. Use one of: ${BT_TYPES.join(', ')}`));
42
42
  process.exit(1);
43
43
  }
44
44
  const result = await listAttachments(client, btType, opts.id);
@@ -65,11 +65,11 @@ export function registerAttachmentsCommand(program) {
65
65
  .action(apiAction(async (client, opts) => {
66
66
  const btType = opts.type;
67
67
  if (!BT_TYPES.includes(btType)) {
68
- console.error(chalk.red(`Invalid type. Use one of: ${BT_TYPES.join(', ')}`));
68
+ console.error(danger(`Invalid type. Use one of: ${BT_TYPES.join(', ')}`));
69
69
  process.exit(1);
70
70
  }
71
71
  if (!opts.attachmentId && !opts.url && !opts.file) {
72
- console.error(chalk.red('Provide --file, --url, or --attachment-id'));
72
+ console.error(danger('Provide --file, --url, or --attachment-id'));
73
73
  process.exit(1);
74
74
  }
75
75
  let file;
@@ -87,7 +87,7 @@ export function registerAttachmentsCommand(program) {
87
87
  // Download from URL, then upload as file
88
88
  const res = await fetch(opts.url);
89
89
  if (!res.ok) {
90
- console.error(chalk.red(`Failed to download: ${res.status} ${res.statusText}`));
90
+ console.error(danger(`Failed to download: ${res.status} ${res.statusText}`));
91
91
  process.exit(1);
92
92
  }
93
93
  const buffer = await res.arrayBuffer();
@@ -108,7 +108,7 @@ export function registerAttachmentsCommand(program) {
108
108
  console.log(JSON.stringify(result, null, 2));
109
109
  }
110
110
  else {
111
- console.log(chalk.green('Attachment added.'));
111
+ console.log(success('Attachment added.'));
112
112
  }
113
113
  }));
114
114
  // ── clio attachments delete ────────────────────────────────────
@@ -123,7 +123,7 @@ export function registerAttachmentsCommand(program) {
123
123
  .action((attachmentResourceId, rawOpts) => {
124
124
  const btType = rawOpts.type;
125
125
  if (!BT_TYPES.includes(btType)) {
126
- console.error(chalk.red(`Invalid type. Use one of: ${BT_TYPES.join(', ')}`));
126
+ console.error(danger(`Invalid type. Use one of: ${BT_TYPES.join(', ')}`));
127
127
  process.exit(1);
128
128
  }
129
129
  return apiAction(async (client) => {
@@ -132,7 +132,7 @@ export function registerAttachmentsCommand(program) {
132
132
  console.log(JSON.stringify(result, null, 2));
133
133
  }
134
134
  else {
135
- console.log(chalk.green('Attachment deleted.'));
135
+ console.log(success('Attachment deleted.'));
136
136
  }
137
137
  })(rawOpts);
138
138
  });
@@ -1,5 +1,5 @@
1
- import chalk from 'chalk';
2
- import prompts from 'prompts';
1
+ import { danger, success, warning, muted, highlight } from './ui/theme.js';
2
+ import * as p from '@clack/prompts';
3
3
  import { clearStoredCredentials, requireAuth, getProfile, setProfile, removeProfile, getActiveLabel, setActiveLabel, listProfiles, findLabelByApiKey, resolvedAuthSource, } from '../core/auth/index.js';
4
4
  import { JazClient } from '../core/api/client.js';
5
5
  import { getOrganization } from '../core/api/organization.js';
@@ -26,7 +26,7 @@ async function validateKey(apiKey) {
26
26
  /** Shared add-org logic used by both `auth add` and `auth set-key`. */
27
27
  async function addOrg(key, opts) {
28
28
  if (!key.startsWith('jk-')) {
29
- console.error(chalk.red('Error: API key must start with "jk-"'));
29
+ console.error(danger('Error: API key must start with "jk-"'));
30
30
  process.exit(1);
31
31
  }
32
32
  // Check for duplicate key under the same (or default) label
@@ -36,7 +36,7 @@ async function addOrg(key, opts) {
36
36
  console.log(JSON.stringify({ error: 'duplicate', existingLabel }));
37
37
  }
38
38
  else {
39
- console.error(chalk.yellow(`This key is already registered as '${existingLabel}'.`));
39
+ console.error(warning(`This key is already registered as '${existingLabel}'.`));
40
40
  }
41
41
  return;
42
42
  }
@@ -46,14 +46,14 @@ async function addOrg(key, opts) {
46
46
  org = await validateKey(key);
47
47
  }
48
48
  catch {
49
- console.error(chalk.red('Error: API key is invalid or the API is unreachable.'));
49
+ console.error(danger('Error: API key is invalid or the API is unreachable.'));
50
50
  process.exit(2);
51
51
  }
52
52
  const label = opts.as ?? slugify(org.name);
53
53
  // Warn if same key exists under different label
54
54
  if (existingLabel && existingLabel !== label) {
55
55
  if (!opts.json) {
56
- console.error(chalk.yellow(`Note: This key is also registered as '${existingLabel}'.`));
56
+ console.error(warning(`Note: This key is also registered as '${existingLabel}'.`));
57
57
  }
58
58
  }
59
59
  // Overwrite protection — don't silently replace a different key under the same label
@@ -63,7 +63,7 @@ async function addOrg(key, opts) {
63
63
  console.log(JSON.stringify({ error: 'label_taken', label, existingOrgName: existingProfile.orgName }));
64
64
  }
65
65
  else {
66
- console.error(chalk.red(`Label '${label}' is already used by ${existingProfile.orgName}. Use --as <label> to choose a different label.`));
66
+ console.error(danger(`Label '${label}' is already used by ${existingProfile.orgName}. Use --as <label> to choose a different label.`));
67
67
  }
68
68
  process.exit(1);
69
69
  }
@@ -80,10 +80,10 @@ async function addOrg(key, opts) {
80
80
  console.log(JSON.stringify({ registered: true, label, orgName: org.name, currency: org.currency, country: org.countryCode }));
81
81
  }
82
82
  else {
83
- console.log(chalk.green(`\u2713 Registered: ${label} \u2014 ${org.name} (${org.currency}, ${org.countryCode})`));
83
+ console.log(success(`\u2713 Registered: ${label} \u2014 ${org.name} (${org.currency}, ${org.countryCode})`));
84
84
  const active = getActiveLabel();
85
85
  if (active === label) {
86
- console.log(chalk.dim(` Active org set to ${label}`));
86
+ console.log(muted(` Active org set to ${label}`));
87
87
  }
88
88
  }
89
89
  }
@@ -108,35 +108,37 @@ export function registerAuthCommand(program) {
108
108
  const orgs = listProfiles();
109
109
  const labels = Object.keys(orgs);
110
110
  if (labels.length === 0) {
111
- console.error(chalk.red('No orgs registered. Run `clio auth add <key>` first.'));
111
+ console.error(danger('No orgs registered. Run `clio auth add <key>` first.'));
112
112
  process.exit(1);
113
113
  }
114
114
  let target = label;
115
115
  // Interactive picker if no label provided
116
116
  if (!target) {
117
117
  if (opts.json || opts.export) {
118
- console.error(chalk.red('Error: --json and --export require a label argument.'));
118
+ console.error(danger('Error: --json and --export require a label argument.'));
119
+ process.exit(1);
120
+ }
121
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
122
+ console.error(danger('Error: interactive picker requires a TTY. Provide a label argument.'));
119
123
  process.exit(1);
120
124
  }
121
125
  const active = getActiveLabel();
122
- const response = await prompts({
123
- type: 'select',
124
- name: 'label',
126
+ const selected = await p.select({
125
127
  message: 'Switch to:',
126
- choices: labels.map(l => ({
127
- title: `${l === active ? '\u2605 ' : ' '}${l} \u2014 ${orgs[l].orgName} (${orgs[l].currency})`,
128
+ options: labels.map(l => ({
129
+ label: `${l === active ? '\u2605 ' : ' '}${l} \u2014 ${orgs[l].orgName} (${orgs[l].currency})`,
128
130
  value: l,
129
131
  })),
130
132
  });
131
- if (!response.label)
133
+ if (p.isCancel(selected))
132
134
  return; // User cancelled
133
- target = response.label;
135
+ target = selected;
134
136
  }
135
137
  try {
136
138
  setActiveLabel(target);
137
139
  }
138
140
  catch (err) {
139
- console.error(chalk.red(err.message));
141
+ console.error(danger(err.message));
140
142
  process.exit(1);
141
143
  }
142
144
  const entry = orgs[target];
@@ -149,10 +151,10 @@ export function registerAuthCommand(program) {
149
151
  console.log(JSON.stringify({ switched: true, label: target, orgName: entry.orgName, currency: entry.currency }));
150
152
  }
151
153
  else {
152
- console.log(chalk.green(`Switched to: ${target} \u2014 ${entry.orgName} (${entry.currency}, ${entry.country})`));
154
+ console.log(success(`Switched to: ${target} \u2014 ${entry.orgName} (${entry.currency}, ${entry.country})`));
153
155
  if (labels.length > 1) {
154
- console.log(chalk.dim(` Tip: Pin to this terminal: eval "$(clio auth switch ${target} --export)"`));
155
- console.log(chalk.dim(` Or add shell integration: eval "$(clio auth shell-init)"`));
156
+ console.log(muted(` Tip: Pin to this terminal: eval "$(clio auth switch ${target} --export)"`));
157
+ console.log(muted(` Or add shell integration: eval "$(clio auth shell-init)"`));
156
158
  }
157
159
  }
158
160
  });
@@ -197,13 +199,13 @@ export function registerAuthCommand(program) {
197
199
  }
198
200
  const labels = Object.keys(orgs);
199
201
  if (labels.length === 0) {
200
- console.error(chalk.yellow('No orgs registered. Run `clio auth add <key>` to get started.'));
202
+ console.error(warning('No orgs registered. Run `clio auth add <key>` to get started.'));
201
203
  return;
202
204
  }
203
205
  for (const label of labels) {
204
206
  const entry = orgs[label];
205
- const marker = label === active ? chalk.yellow('\u2605') : ' ';
206
- const labelStr = label === active ? chalk.bold(label) : label;
207
+ const marker = label === active ? warning('\u2605') : ' ';
208
+ const labelStr = label === active ? highlight(label) : label;
207
209
  const countryStr = entry.country ? ` ${entry.country}` : '';
208
210
  console.log(` ${marker} ${labelStr.padEnd(18)} ${entry.orgName.padEnd(22)} ${entry.currency}${countryStr}`);
209
211
  }
@@ -211,7 +213,7 @@ export function registerAuthCommand(program) {
211
213
  const pinnedOrg = process.env.JAZ_ORG;
212
214
  if (pinnedOrg) {
213
215
  const safe = pinnedOrg.replace(/[\x00-\x1F\x7F]/g, '');
214
- console.log(chalk.dim(`\n This terminal is pinned to: ${safe} (via JAZ_ORG)`));
216
+ console.log(muted(`\n This terminal is pinned to: ${safe} (via JAZ_ORG)`));
215
217
  }
216
218
  });
217
219
  // ── clio auth remove <label> ────────────────────────────────────
@@ -225,17 +227,17 @@ export function registerAuthCommand(program) {
225
227
  const orgs = listProfiles();
226
228
  const available = Object.keys(orgs);
227
229
  const hint = available.length > 0 ? ` Available: ${available.join(', ')}` : '';
228
- console.error(chalk.red(`Org '${label}' not found.${hint}`));
230
+ console.error(danger(`Org '${label}' not found.${hint}`));
229
231
  process.exit(1);
230
232
  }
231
233
  if (opts.json) {
232
234
  console.log(JSON.stringify({ removed: true, label }));
233
235
  }
234
236
  else {
235
- console.log(chalk.green(`Removed: ${label}`));
237
+ console.log(success(`Removed: ${label}`));
236
238
  const active = getActiveLabel();
237
239
  if (!active) {
238
- console.error(chalk.yellow('No active org. Run `clio auth switch <label>` to set one.'));
240
+ console.error(warning('No active org. Run `clio auth switch <label>` to set one.'));
239
241
  }
240
242
  }
241
243
  });
@@ -253,7 +255,7 @@ export function registerAuthCommand(program) {
253
255
  if (!entry) {
254
256
  const available = Object.keys(listProfiles());
255
257
  const hint = available.length > 0 ? ` Available: ${available.join(', ')}` : '';
256
- console.error(chalk.red(`Org '${opts.org}' not found.${hint}`));
258
+ console.error(danger(`Org '${opts.org}' not found.${hint}`));
257
259
  process.exit(1);
258
260
  }
259
261
  if (opts.json) {
@@ -274,14 +276,14 @@ export function registerAuthCommand(program) {
274
276
  }
275
277
  else {
276
278
  if (active) {
277
- console.log(chalk.bold('Label:'), active);
279
+ console.log(highlight('Label:'), active);
278
280
  }
279
- console.log(chalk.bold('Organization:'), org.name);
280
- console.log(chalk.bold('Currency:'), org.currency);
281
- console.log(chalk.bold('Country:'), org.countryCode);
282
- console.log(chalk.bold('Status:'), org.status);
281
+ console.log(highlight('Organization:'), org.name);
282
+ console.log(highlight('Currency:'), org.currency);
283
+ console.log(highlight('Country:'), org.countryCode);
284
+ console.log(highlight('Status:'), org.status);
283
285
  if (org.lockDate) {
284
- console.log(chalk.bold('Lock Date:'), org.lockDate);
286
+ console.log(highlight('Lock Date:'), org.lockDate);
285
287
  }
286
288
  if (source) {
287
289
  const sourceHuman = {
@@ -293,13 +295,13 @@ export function registerAuthCommand(program) {
293
295
  };
294
296
  const isPinned = source === 'env-org' || source === 'flag-org' || source === 'flag-api-key' || source === 'env-api-key';
295
297
  const sourceStr = sourceHuman[source] ?? source;
296
- console.log(chalk.bold('Source:'), isPinned ? chalk.green(sourceStr) : chalk.yellow(sourceStr));
298
+ console.log(highlight('Source:'), isPinned ? success(sourceStr) : warning(sourceStr));
297
299
  }
298
300
  }
299
301
  }
300
302
  catch (err) {
301
303
  const isAuthError = err.name === 'AuthError';
302
- console.error(chalk.red(`Error: ${err.message}`));
304
+ console.error(danger(`Error: ${err.message}`));
303
305
  process.exit(isAuthError ? 3 : 2);
304
306
  }
305
307
  });
@@ -321,10 +323,10 @@ export function registerAuthCommand(program) {
321
323
  console.log(JSON.stringify({ removed }));
322
324
  }
323
325
  else if (removed) {
324
- console.log(chalk.green('All credentials removed.'));
326
+ console.log(success('All credentials removed.'));
325
327
  }
326
328
  else {
327
- console.error(chalk.yellow('No credentials file found.'));
329
+ console.error(warning('No credentials file found.'));
328
330
  }
329
331
  });
330
332
  // ── clio auth shell-init ────────────────────────────────────────
@@ -346,20 +348,20 @@ export function registerAuthCommand(program) {
346
348
  }
347
349
  if (process.env.JAZ_ORG) {
348
350
  const safe = process.env.JAZ_ORG.replace(/[\x00-\x1F\x7F]/g, '');
349
- console.log(chalk.green(`Session is pinned to: ${safe}`));
350
- console.log(chalk.dim(' To unpin: eval "$(clio auth unpin --export)"'));
351
- console.log(chalk.dim(' Or simply: unset JAZ_ORG'));
351
+ console.log(success(`Session is pinned to: ${safe}`));
352
+ console.log(muted(' To unpin: eval "$(clio auth unpin --export)"'));
353
+ console.log(muted(' Or simply: unset JAZ_ORG'));
352
354
  }
353
355
  else {
354
- console.log(chalk.dim('Session is not pinned (JAZ_ORG is not set).'));
356
+ console.log(muted('Session is not pinned (JAZ_ORG is not set).'));
355
357
  }
356
358
  });
357
359
  }
358
360
  function printProfile(label, entry) {
359
- console.log(chalk.bold('Label:'), label);
360
- console.log(chalk.bold('Organization:'), entry.orgName);
361
- console.log(chalk.bold('Currency:'), entry.currency);
362
- console.log(chalk.bold('Country:'), entry.country);
361
+ console.log(highlight('Label:'), label);
362
+ console.log(highlight('Organization:'), entry.orgName);
363
+ console.log(highlight('Currency:'), entry.currency);
364
+ console.log(highlight('Country:'), entry.country);
363
365
  }
364
366
  /** Generate shell function for session auto-pinning. Works in bash and zsh. */
365
367
  function generateShellInit() {
@@ -1,4 +1,4 @@
1
- import chalk from 'chalk';
1
+ import { danger, success, muted, highlight } from './ui/theme.js';
2
2
  import { listBankRules, getBankRule, searchBankRules, createBankRule, updateBankRule, deleteBankRule, } from '../core/api/bank-rules.js';
3
3
  import { apiAction } from './api-action.js';
4
4
  import { outputList } from './output.js';
@@ -44,9 +44,9 @@ Strings are replaced with actual bank record values during reconciliation.`);
44
44
  console.log(JSON.stringify(res.data, null, 2));
45
45
  return;
46
46
  }
47
- console.log(chalk.bold('Name:'), res.data.name);
48
- console.log(chalk.bold('Action:'), res.data.actionType);
49
- console.log(chalk.bold('ID:'), res.data.resourceId);
47
+ console.log(highlight('Name:'), res.data.name);
48
+ console.log(highlight('Action:'), res.data.actionType);
49
+ console.log(highlight('ID:'), res.data.resourceId);
50
50
  })(opts));
51
51
  cmd
52
52
  .command('search <query>')
@@ -81,8 +81,8 @@ Strings are replaced with actual bank record values during reconciliation.`);
81
81
  let body = readBodyInput(opts);
82
82
  if (!body) {
83
83
  if (!opts.name || !opts.account) {
84
- console.error(chalk.red('Required: --name, --account'));
85
- console.error(chalk.dim('Or use --input <file> to provide full JSON body.'));
84
+ console.error(danger('Required: --name, --account'));
85
+ console.error(muted('Or use --input <file> to provide full JSON body.'));
86
86
  process.exit(1);
87
87
  }
88
88
  body = {
@@ -94,7 +94,7 @@ Strings are replaced with actual bank record values during reconciliation.`);
94
94
  body.configuration = JSON.parse(opts.config);
95
95
  }
96
96
  catch {
97
- console.error(chalk.red('Invalid --config JSON'));
97
+ console.error(danger('Invalid --config JSON'));
98
98
  process.exit(1);
99
99
  }
100
100
  }
@@ -105,8 +105,8 @@ Strings are replaced with actual bank record values during reconciliation.`);
105
105
  console.log(JSON.stringify(res.data, null, 2));
106
106
  return;
107
107
  }
108
- console.log(chalk.green('Bank rule created.'));
109
- console.log(chalk.bold('ID:'), res.data.resourceId);
108
+ console.log(success('Bank rule created.'));
109
+ console.log(highlight('ID:'), res.data.resourceId);
110
110
  }));
111
111
  cmd
112
112
  .command('update <resourceId>')
@@ -130,8 +130,8 @@ Strings are replaced with actual bank record values during reconciliation.`);
130
130
  // appliesToReconciliationAccount as an object (not UUID), so we can't
131
131
  // round-trip it. Require the UUID explicitly.
132
132
  if (!opts.account) {
133
- console.error(chalk.red('--account is required for update (PUT is full replacement — GET does not return the UUID)'));
134
- console.error(chalk.dim('Use: clio bank-rules update <id> --account <bank-account-uuid> [--name ...] [--config ...]'));
133
+ console.error(danger('--account is required for update (PUT is full replacement — GET does not return the UUID)'));
134
+ console.error(muted('Use: clio bank-rules update <id> --account <bank-account-uuid> [--name ...] [--config ...]'));
135
135
  process.exitCode = 1;
136
136
  return;
137
137
  }
@@ -148,7 +148,7 @@ Strings are replaced with actual bank record values during reconciliation.`);
148
148
  data.configuration = JSON.parse(opts.config);
149
149
  }
150
150
  catch {
151
- console.error(chalk.red('Invalid --config JSON'));
151
+ console.error(danger('Invalid --config JSON'));
152
152
  process.exit(1);
153
153
  }
154
154
  }
@@ -159,7 +159,7 @@ Strings are replaced with actual bank record values during reconciliation.`);
159
159
  console.log(JSON.stringify(res.data, null, 2));
160
160
  return;
161
161
  }
162
- console.log(chalk.green(`Bank rule ${resourceId} updated.`));
162
+ console.log(success(`Bank rule ${resourceId} updated.`));
163
163
  })(opts));
164
164
  cmd
165
165
  .command('delete <resourceId>')
@@ -173,6 +173,6 @@ Strings are replaced with actual bank record values during reconciliation.`);
173
173
  console.log(JSON.stringify({ deleted: true, resourceId }));
174
174
  return;
175
175
  }
176
- console.log(chalk.green(`Bank rule ${resourceId} deleted.`));
176
+ console.log(success(`Bank rule ${resourceId} deleted.`));
177
177
  })(opts));
178
178
  }