jaz-clio 4.25.4 → 4.25.6

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.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-api
3
- version: 4.25.4
3
+ version: 4.25.6
4
4
  description: Complete reference for the Jaz REST API — the accounting platform backend. Use this skill whenever building, modifying, debugging, or extending any code that calls the API — including API clients, integrations, data seeding, test data, or new endpoint work. Contains every field name, response shape, error, gotcha, and edge case discovered through live production testing.
5
5
  license: MIT
6
6
  compatibility: Requires Jaz API key (x-jk-api-key header). Works with Claude Code, Google Antigravity, OpenAI Codex, GitHub Copilot, Cursor, and any agent that reads markdown.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-conversion
3
- version: 4.25.4
3
+ version: 4.25.6
4
4
  description: Accounting data conversion skill — migrates customer data from Xero, QuickBooks, Sage, MYOB, and Excel exports to Jaz. Covers config, quick, and full conversion workflows, Excel parsing, CoA/contact/tax/items mapping, clearing accounts, TTB, and TB verification.
5
5
  ---
6
6
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-jobs
3
- version: 4.25.4
3
+ version: 4.25.6
4
4
  description: 12 accounting jobs for SMB bookkeepers and accountants — month-end, quarter-end, and year-end close playbooks plus 9 ad-hoc operational jobs (bank recon, document collection, GST/VAT filing, payment runs, credit control, supplier recon, audit prep, fixed asset review, statutory filing). Jobs can have paired tools as nested subcommands (e.g., `clio jobs bank-recon match`, `clio jobs document-collection ingest`, `clio jobs statutory-filing sg-cs`). Paired with an interactive CLI blueprint generator (clio jobs).
5
5
  license: MIT
6
6
  compatibility: Works with Claude Code, Claude Cowork, Claude.ai, and any agent that reads markdown. For API payloads, load the jaz-api skill. For individual transaction patterns, load the jaz-recipes skill.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-recipes
3
- version: 4.25.4
3
+ version: 4.25.6
4
4
  description: 16 IFRS-compliant recipes for complex multi-step accounting in Jaz — prepaid amortization, deferred revenue, loan schedules, IFRS 16 leases, hire purchase, fixed deposits, asset disposal, FX revaluation, ECL provisioning, IAS 37 provisions, dividends, intercompany, and capital WIP. Each recipe includes journal entries, capsule structure, and verification steps. Paired with 13 financial calculators that produce execution-ready blueprints with workings.
5
5
  license: MIT
6
6
  compatibility: Works with Claude Code, Claude Cowork, Claude.ai, and any agent that reads markdown. For API payloads, load the jaz-api skill alongside this one.
@@ -3,6 +3,12 @@ import { readFileSync } from 'node:fs';
3
3
  import { basename, extname, resolve } from 'node:path';
4
4
  import { listAttachments, addAttachment, fetchAttachmentTable } from '../core/api/attachments.js';
5
5
  import { apiAction } from './api-action.js';
6
+ import { outputList } from './output.js';
7
+ import { formatId } from './format-helpers.js';
8
+ const ATTACHMENTS_COLUMNS = [
9
+ { key: 'resourceId', header: 'ID', format: formatId },
10
+ { key: 'fileName', header: 'File Name' },
11
+ ];
6
12
  const BT_TYPES = ['invoices', 'bills', 'journals', 'scheduled_journals', 'customer-credit-notes', 'supplier-credit-notes'];
7
13
  const ATTACHMENT_MIME_MAP = {
8
14
  '.pdf': 'application/pdf',
@@ -27,6 +33,7 @@ export function registerAttachmentsCommand(program) {
27
33
  .requiredOption('--type <type>', `Transaction type (${BT_TYPES.join(', ')})`)
28
34
  .requiredOption('--id <resourceId>', 'Transaction resourceId')
29
35
  .option('--api-key <key>', 'API key')
36
+ .option('--format <type>', 'Output format: table, json, csv, yaml')
30
37
  .option('--json', 'Output as JSON')
31
38
  .action(apiAction(async (client, opts) => {
32
39
  const btType = opts.type;
@@ -35,16 +42,14 @@ export function registerAttachmentsCommand(program) {
35
42
  process.exit(1);
36
43
  }
37
44
  const result = await listAttachments(client, btType, opts.id);
38
- if (opts.json) {
39
- console.log(JSON.stringify(result, null, 2));
40
- }
41
- else {
42
- const items = result.data ?? [];
43
- console.log(chalk.bold(`${items.length} attachment(s)`));
44
- for (const a of items) {
45
- console.log(` ${a.resourceId} ${a.fileName}`);
46
- }
47
- }
45
+ const items = result.data ?? [];
46
+ const wrapped = {
47
+ data: items,
48
+ totalElements: result.totalElements ?? items.length, // eslint-disable-line @typescript-eslint/no-explicit-any
49
+ totalPages: result.totalPages ?? 1, // eslint-disable-line @typescript-eslint/no-explicit-any
50
+ truncated: result.truncated ?? false, // eslint-disable-line @typescript-eslint/no-explicit-any
51
+ };
52
+ outputList(wrapped, ATTACHMENTS_COLUMNS, opts, 'Attachments'); // eslint-disable-line @typescript-eslint/no-explicit-any
48
53
  }));
49
54
  // ── clio attachments add ───────────────────────────────────────
50
55
  attachments
@@ -3,6 +3,7 @@ import prompts from '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';
6
+ import { outputList } from './output.js';
6
7
  /** Escape a string for safe use in shell export statements. */
7
8
  function shellEscape(s) {
8
9
  if (/^[a-zA-Z0-9_-]+$/.test(s))
@@ -159,10 +160,12 @@ export function registerAuthCommand(program) {
159
160
  auth
160
161
  .command('list')
161
162
  .description('List all registered orgs')
163
+ .option('--format <type>', 'Output format: table, json, csv, yaml')
162
164
  .option('--json', 'Output as JSON')
163
165
  .action((opts) => {
164
166
  const orgs = listProfiles();
165
167
  const active = getActiveLabel();
168
+ // Preserve original --json envelope (backwards compat)
166
169
  if (opts.json) {
167
170
  const entries = Object.entries(orgs).map(([label, entry]) => ({
168
171
  label,
@@ -172,6 +175,26 @@ export function registerAuthCommand(program) {
172
175
  console.log(JSON.stringify({ active, orgs: entries }, null, 2));
173
176
  return;
174
177
  }
178
+ // --format (table/csv/yaml) via outputList
179
+ if (opts.format) {
180
+ const entries = Object.entries(orgs).map(([label, entry]) => ({
181
+ label,
182
+ active: label === active,
183
+ orgName: entry.orgName,
184
+ currency: entry.currency,
185
+ country: entry.country,
186
+ }));
187
+ const columns = [
188
+ { key: 'label', header: 'Label' },
189
+ { key: 'active', header: 'Active' },
190
+ { key: 'orgName', header: 'Org Name' },
191
+ { key: 'currency', header: 'Currency' },
192
+ { key: 'country', header: 'Country' },
193
+ ];
194
+ const wrapped = { data: entries, totalElements: entries.length, totalPages: 1, truncated: false }; // eslint-disable-line @typescript-eslint/no-explicit-any
195
+ outputList(wrapped, columns, opts, 'Auth Profiles');
196
+ return;
197
+ }
175
198
  const labels = Object.keys(orgs);
176
199
  if (labels.length === 0) {
177
200
  console.error(chalk.yellow('No orgs registered. Run `clio auth add <key>` to get started.'));
@@ -2,7 +2,15 @@ import chalk from 'chalk';
2
2
  import { listBookmarks, getBookmark, createBookmarks, updateBookmark, } from '../core/api/bookmarks.js';
3
3
  import { apiAction } from './api-action.js';
4
4
  import { parsePositiveInt, parseNonNegativeInt, readBodyInput, requireFields } from './parsers.js';
5
- import { paginatedFetch, paginatedJson, displaySlice } from './pagination.js';
5
+ import { paginatedFetch } from './pagination.js';
6
+ import { outputList } from './output.js';
7
+ import { formatId } from './format-helpers.js';
8
+ const BOOKMARKS_COLUMNS = [
9
+ { key: 'resourceId', header: 'ID', format: formatId },
10
+ { key: 'name', header: 'Name' },
11
+ { key: 'categoryCode', header: 'Category' },
12
+ { key: 'value', header: 'Value' },
13
+ ];
6
14
  export function registerBookmarksCommand(program) {
7
15
  const bm = program
8
16
  .command('bookmarks')
@@ -16,21 +24,11 @@ export function registerBookmarksCommand(program) {
16
24
  .option('--all', 'Fetch all pages')
17
25
  .option('--max-rows <n>', 'Max rows for --all (default 10000)', parsePositiveInt)
18
26
  .option('--api-key <key>', 'API key (overrides stored/env)')
27
+ .option('--format <type>', 'Output format: table, json, csv, yaml')
19
28
  .option('--json', 'Output as JSON')
20
29
  .action(apiAction(async (client, opts) => {
21
30
  const result = await paginatedFetch(opts, (p) => listBookmarks(client, p), { label: 'Fetching bookmarks' });
22
- if (opts.json) {
23
- console.log(paginatedJson(result, opts));
24
- }
25
- else {
26
- console.log(chalk.bold(`Bookmarks (${result.data.length} of ${result.totalElements}):\n`));
27
- const { items, overflow } = displaySlice(result.data);
28
- for (const b of items) {
29
- console.log(` ${chalk.cyan(b.resourceId)} ${b.name} ${chalk.dim(b.categoryCode)} ${b.value}`);
30
- }
31
- if (overflow > 0)
32
- console.log(chalk.dim(` ... and ${overflow.toLocaleString()} more (use --json for full output)`));
33
- }
31
+ outputList(result, BOOKMARKS_COLUMNS, opts, 'Bookmarks'); // eslint-disable-line @typescript-eslint/no-explicit-any
34
32
  }));
35
33
  // ── clio bookmarks get ────────────────────────────────────────
36
34
  bm
@@ -2,7 +2,22 @@ import chalk from 'chalk';
2
2
  import { listOrgUsers, searchOrgUsers, inviteOrgUser, updateOrgUser, removeOrgUser, } from '../core/api/org-users.js';
3
3
  import { apiAction } from './api-action.js';
4
4
  import { parsePositiveInt, parseNonNegativeInt, readBodyInput, requireFields } from './parsers.js';
5
- import { paginatedFetch, paginatedJson, displaySlice } from './pagination.js';
5
+ import { paginatedFetch } from './pagination.js';
6
+ import { outputList } from './output.js';
7
+ import { formatId } from './format-helpers.js';
8
+ const ORG_USERS_COLUMNS = [
9
+ { key: 'resourceId', header: 'ID', format: formatId },
10
+ { key: 'firstName', header: 'First Name' },
11
+ { key: 'lastName', header: 'Last Name' },
12
+ { key: 'email', header: 'Email' },
13
+ {
14
+ key: 'moduleRoles',
15
+ header: 'Roles',
16
+ format: (roles) => Array.isArray(roles)
17
+ ? roles.map((r) => `${r.moduleName}:${r.roleCode}`).join(', ')
18
+ : String(roles ?? ''),
19
+ },
20
+ ];
6
21
  export function registerOrgUsersCommand(program) {
7
22
  const ou = program
8
23
  .command('org-users')
@@ -16,22 +31,11 @@ export function registerOrgUsersCommand(program) {
16
31
  .option('--all', 'Fetch all pages')
17
32
  .option('--max-rows <n>', 'Max rows for --all (default 10000)', parsePositiveInt)
18
33
  .option('--api-key <key>', 'API key (overrides stored/env)')
34
+ .option('--format <type>', 'Output format: table, json, csv, yaml')
19
35
  .option('--json', 'Output as JSON')
20
36
  .action(apiAction(async (client, opts) => {
21
37
  const result = await paginatedFetch(opts, (p) => listOrgUsers(client, p), { label: 'Fetching org users' });
22
- if (opts.json) {
23
- console.log(paginatedJson(result, opts));
24
- }
25
- else {
26
- console.log(chalk.bold(`Org Users (${result.data.length} of ${result.totalElements}):\n`));
27
- const { items, overflow } = displaySlice(result.data);
28
- for (const u of items) {
29
- const roles = u.moduleRoles.map((r) => `${r.moduleName}:${r.roleCode}`).join(', ');
30
- console.log(` ${chalk.cyan(u.resourceId)} ${u.firstName} ${u.lastName} ${chalk.dim(u.email)} ${roles}`);
31
- }
32
- if (overflow > 0)
33
- console.log(chalk.dim(` ... and ${overflow.toLocaleString()} more (use --json for full output)`));
34
- }
38
+ outputList(result, ORG_USERS_COLUMNS, opts, 'Org Users'); // eslint-disable-line @typescript-eslint/no-explicit-any
35
39
  }));
36
40
  // ── clio org-users search ─────────────────────────────────────
37
41
  ou
@@ -44,6 +48,7 @@ export function registerOrgUsersCommand(program) {
44
48
  .option('--all', 'Fetch all pages')
45
49
  .option('--max-rows <n>', 'Max rows for --all (default 10000)', parsePositiveInt)
46
50
  .option('--api-key <key>', 'API key (overrides stored/env)')
51
+ .option('--format <type>', 'Output format: table, json, csv, yaml')
47
52
  .option('--json', 'Output as JSON')
48
53
  .action((query, opts) => apiAction(async (client) => {
49
54
  const filter = {
@@ -55,22 +60,7 @@ export function registerOrgUsersCommand(program) {
55
60
  };
56
61
  const sort = { sortBy: [opts.sort ?? 'firstName'], order: (opts.order ?? 'ASC') };
57
62
  const result = await paginatedFetch(opts, ({ limit, offset }) => searchOrgUsers(client, { filter, limit, offset, sort }), { label: 'Searching org users', defaultLimit: 20 });
58
- if (opts.json) {
59
- console.log(paginatedJson(result, opts));
60
- }
61
- else {
62
- if (result.data.length === 0) {
63
- console.log('No users found.');
64
- return;
65
- }
66
- console.log(chalk.bold(`Found ${result.data.length} user(s):\n`));
67
- const { items, overflow } = displaySlice(result.data);
68
- for (const u of items) {
69
- console.log(` ${chalk.cyan(u.resourceId)} ${u.firstName} ${u.lastName} ${chalk.dim(u.email)}`);
70
- }
71
- if (overflow > 0)
72
- console.log(chalk.dim(` ... and ${overflow.toLocaleString()} more (use --json for full output)`));
73
- }
63
+ outputList(result, ORG_USERS_COLUMNS, opts, 'Org Users'); // eslint-disable-line @typescript-eslint/no-explicit-any
74
64
  })(opts));
75
65
  // ── clio org-users invite ─────────────────────────────────────
76
66
  ou
@@ -2,7 +2,19 @@ import chalk from 'chalk';
2
2
  import { listTaxProfiles, listTaxTypes, createTaxProfile, getTaxProfile, searchTaxProfiles, updateTaxProfile, listWithholdingTaxCodes, } from '../core/api/tax-profiles.js';
3
3
  import { apiAction } from './api-action.js';
4
4
  import { parsePositiveInt, parseNonNegativeInt, requireFields } from './parsers.js';
5
- import { paginatedFetch, paginatedJson, displaySlice } from './pagination.js';
5
+ import { paginatedFetch } from './pagination.js';
6
+ import { outputList } from './output.js';
7
+ import { formatId } from './format-helpers.js';
8
+ const TAX_PROFILES_COLUMNS = [
9
+ { key: 'resourceId', header: 'ID', format: formatId },
10
+ { key: 'name', header: 'Name' },
11
+ { key: 'taxRate', header: 'Rate (%)' },
12
+ { key: 'taxTypeCode', header: 'Tax Type' },
13
+ ];
14
+ const TAX_TYPES_COLUMNS = [
15
+ { key: 'code', header: 'Code' },
16
+ { key: 'name', header: 'Name' },
17
+ ];
6
18
  export function registerTaxProfilesCommand(program) {
7
19
  const tp = program
8
20
  .command('tax-profiles')
@@ -16,21 +28,11 @@ export function registerTaxProfilesCommand(program) {
16
28
  .option('--all', 'Fetch all pages')
17
29
  .option('--max-rows <n>', 'Max rows for --all (default 10000)', parsePositiveInt)
18
30
  .option('--api-key <key>', 'API key (overrides stored/env)')
31
+ .option('--format <type>', 'Output format: table, json, csv, yaml')
19
32
  .option('--json', 'Output as JSON')
20
33
  .action(apiAction(async (client, opts) => {
21
34
  const result = await paginatedFetch(opts, (p) => listTaxProfiles(client, p), { label: 'Fetching tax profiles' });
22
- if (opts.json) {
23
- console.log(paginatedJson(result, opts));
24
- }
25
- else {
26
- console.log(chalk.bold(`Tax Profiles (${result.data.length} of ${result.totalElements}):\n`));
27
- const { items, overflow } = displaySlice(result.data);
28
- for (const tp of items) {
29
- console.log(` ${chalk.cyan(tp.resourceId)} ${tp.name} ${tp.taxRate}% ${chalk.dim(tp.taxTypeCode)}`);
30
- }
31
- if (overflow > 0)
32
- console.log(chalk.dim(` ... and ${overflow.toLocaleString()} more (use --json for full output)`));
33
- }
35
+ outputList(result, TAX_PROFILES_COLUMNS, opts, 'Tax Profiles'); // eslint-disable-line @typescript-eslint/no-explicit-any
34
36
  }));
35
37
  // ── clio tax-profiles types ───────────────────────────────────
36
38
  tp
@@ -41,21 +43,11 @@ export function registerTaxProfilesCommand(program) {
41
43
  .option('--all', 'Fetch all pages')
42
44
  .option('--max-rows <n>', 'Max rows for --all (default 10000)', parsePositiveInt)
43
45
  .option('--api-key <key>', 'API key (overrides stored/env)')
46
+ .option('--format <type>', 'Output format: table, json, csv, yaml')
44
47
  .option('--json', 'Output as JSON')
45
48
  .action(apiAction(async (client, opts) => {
46
49
  const result = await paginatedFetch(opts, (p) => listTaxTypes(client, p), { label: 'Fetching tax types' });
47
- if (opts.json) {
48
- console.log(paginatedJson(result, opts));
49
- }
50
- else {
51
- console.log(chalk.bold(`Tax Types (${result.data.length} of ${result.totalElements}):\n`));
52
- const { items, overflow } = displaySlice(result.data);
53
- for (const tt of items) {
54
- console.log(` ${chalk.cyan(tt.code)} ${tt.name}`);
55
- }
56
- if (overflow > 0)
57
- console.log(chalk.dim(` ... and ${overflow.toLocaleString()} more (use --json for full output)`));
58
- }
50
+ outputList(result, TAX_TYPES_COLUMNS, opts, 'Tax Types'); // eslint-disable-line @typescript-eslint/no-explicit-any
59
51
  }));
60
52
  // ── clio tax-profiles get ────────────────────────────────────
61
53
  tp
@@ -89,6 +81,7 @@ export function registerTaxProfilesCommand(program) {
89
81
  .option('--all', 'Fetch all pages')
90
82
  .option('--max-rows <n>', 'Max rows for --all (default 10000)', parsePositiveInt)
91
83
  .option('--api-key <key>', 'API key (overrides stored/env)')
84
+ .option('--format <type>', 'Output format: table, json, csv, yaml')
92
85
  .option('--json', 'Output as JSON')
93
86
  .action(apiAction(async (client, opts) => {
94
87
  const filter = {};
@@ -99,22 +92,7 @@ export function registerTaxProfilesCommand(program) {
99
92
  const searchFilter = Object.keys(filter).length > 0 ? filter : undefined;
100
93
  const sort = { sortBy: [opts.sort ?? 'name'], order: (opts.order ?? 'ASC') };
101
94
  const result = await paginatedFetch(opts, ({ limit, offset }) => searchTaxProfiles(client, { filter: searchFilter, limit, offset, sort }), { label: 'Searching tax profiles', defaultLimit: 20 });
102
- if (opts.json) {
103
- console.log(paginatedJson(result, opts));
104
- }
105
- else {
106
- if (result.data.length === 0) {
107
- console.log('No tax profiles found.');
108
- return;
109
- }
110
- console.log(chalk.bold(`Found ${result.data.length} tax profile(s):\n`));
111
- const { items, overflow } = displaySlice(result.data);
112
- for (const p of items) {
113
- console.log(` ${chalk.cyan(p.resourceId)} ${p.name} ${p.taxRate}% ${chalk.dim(p.taxTypeCode)}`);
114
- }
115
- if (overflow > 0)
116
- console.log(chalk.dim(` ... and ${overflow.toLocaleString()} more (use --json for full output)`));
117
- }
95
+ outputList(result, TAX_PROFILES_COLUMNS, opts, 'Tax Profiles'); // eslint-disable-line @typescript-eslint/no-explicit-any
118
96
  }));
119
97
  // ── clio tax-profiles update ────────────────────────────────
120
98
  tp
@@ -212,18 +212,36 @@ export const TOOL_DEFINITIONS = [
212
212
  params: {
213
213
  name: { type: 'string', description: 'Account name' },
214
214
  code: { type: 'string', description: 'Account code (unique)' },
215
- accountType: { type: 'string', description: 'Account type (e.g., "Current Assets", "Revenue")' },
215
+ accountType: { type: 'string', description: 'Exact API values: "Bank Accounts", "Cash", "Current Asset", "Fixed Asset", "Inventory", "Current Liability", "Non-current Liability", "Shareholders Equity", "Operating Revenue", "Other Revenue", "Operating Expense", "Direct Costs"' },
216
216
  currencyCode: { type: 'string', description: 'Currency code (e.g., "SGD")' },
217
217
  },
218
218
  required: ['name', 'code', 'accountType'],
219
219
  group: 'accounts',
220
220
  readOnly: false,
221
- execute: async (ctx, input) => createAccount(ctx.client, {
222
- code: input.code,
223
- name: input.name,
224
- accountType: input.accountType,
225
- currencyCode: input.currencyCode,
226
- }),
221
+ execute: async (ctx, input) => {
222
+ // Normalize common LLM variations to exact API values
223
+ const typeMap = {
224
+ 'current assets': 'Current Asset', 'current asset': 'Current Asset',
225
+ 'fixed assets': 'Fixed Asset', 'fixed asset': 'Fixed Asset',
226
+ 'bank account': 'Bank Accounts', 'bank accounts': 'Bank Accounts', 'bank': 'Bank Accounts',
227
+ 'current liabilities': 'Current Liability', 'current liability': 'Current Liability',
228
+ 'non-current liabilities': 'Non-current Liability', 'non-current liability': 'Non-current Liability',
229
+ 'equity': 'Shareholders Equity', 'shareholders equity': 'Shareholders Equity', "shareholders' equity": 'Shareholders Equity', "shareholder's equity": 'Shareholders Equity',
230
+ 'revenue': 'Operating Revenue', 'operating revenue': 'Operating Revenue',
231
+ 'other revenue': 'Other Revenue', 'other income': 'Other Revenue',
232
+ 'expense': 'Operating Expense', 'operating expense': 'Operating Expense', 'expenses': 'Operating Expense',
233
+ 'direct costs': 'Direct Costs', 'cost of goods sold': 'Direct Costs', 'cogs': 'Direct Costs',
234
+ 'cash': 'Cash', 'inventory': 'Inventory',
235
+ };
236
+ const rawType = input.accountType;
237
+ const accountType = typeMap[rawType.toLowerCase()] ?? rawType;
238
+ return createAccount(ctx.client, {
239
+ code: input.code,
240
+ name: input.name,
241
+ accountType,
242
+ currencyCode: input.currencyCode,
243
+ });
244
+ },
227
245
  },
228
246
  {
229
247
  name: 'update_account',
@@ -316,24 +334,32 @@ export const TOOL_DEFINITIONS = [
316
334
  listTool('list_invoices', 'List invoices. Returns reference, date, status, contact, totalAmount. Paginated — response includes totalElements. Use limit/offset to page.', 'invoices', (client, off, lim) => listInvoices(client, { limit: lim, offset: off })),
317
335
  {
318
336
  name: 'search_invoices',
319
- description: 'Search invoices by reference or contact name. Returns up to 100 by default. Use limit/offset to page through large result sets.',
337
+ description: 'Search invoices by reference and/or contact name. Provide query for reference search, contactName for contact search, or both.',
320
338
  params: {
321
- query: { type: 'string', description: 'Search term (reference or contact name)' },
339
+ query: { type: 'string', description: 'Search by invoice reference number' },
340
+ contactName: { type: 'string', description: 'Search by contact name' },
322
341
  ...SEARCH_PARAMS,
323
342
  },
324
- required: ['query'],
343
+ required: [],
325
344
  group: 'invoices',
326
345
  readOnly: true,
327
346
  execute: async (ctx, input) => {
328
347
  const { mode, limit, offset, sortBy, sortOrder } = extractPaginationInput(input);
329
348
  const query = input.query;
349
+ const contactName = input.contactName;
350
+ // Build filter — avoid nested `contact` inside `or` (causes 400)
351
+ let filter;
352
+ if (query && contactName) {
353
+ filter = { reference: { contains: query }, contact: { name: { contains: contactName } } };
354
+ }
355
+ else if (query) {
356
+ filter = { reference: { contains: query } };
357
+ }
358
+ else if (contactName) {
359
+ filter = { contact: { name: { contains: contactName } } };
360
+ }
330
361
  return handlePaginationMode(mode, (off, lim) => searchInvoices(ctx.client, {
331
- filter: query ? {
332
- or: [
333
- { reference: { contains: query } },
334
- { contact: { name: { contains: query } } },
335
- ],
336
- } : undefined,
362
+ filter,
337
363
  limit: lim, offset: off,
338
364
  sort: { sortBy: [sortBy ?? 'valueDate'], order: (sortOrder ?? 'DESC') },
339
365
  }), limit, offset, 100);
@@ -437,7 +463,7 @@ export const TOOL_DEFINITIONS = [
437
463
  accountResourceId: input.accountResourceId,
438
464
  valueDate: input.valueDate,
439
465
  dueDate: input.valueDate,
440
- reference: input.reference ?? '',
466
+ reference: input.reference || `PMT-${Date.now()}`,
441
467
  paymentMethod: (input.paymentMethod ?? 'BANK_TRANSFER'),
442
468
  saveAsDraft: false,
443
469
  customFields: input.customFields, // eslint-disable-line @typescript-eslint/no-explicit-any
@@ -501,24 +527,31 @@ export const TOOL_DEFINITIONS = [
501
527
  listTool('list_bills', 'List bills (purchase invoices). Returns reference, date, status, contact, amounts. Paginated — response includes totalElements. Use limit/offset to page.', 'bills', (client, off, lim) => listBills(client, { limit: lim, offset: off })),
502
528
  {
503
529
  name: 'search_bills',
504
- description: 'Search bills by reference or contact name. Returns up to 100 by default. Use limit/offset to page through large result sets.',
530
+ description: 'Search bills by reference and/or contact name. Provide query for reference search, contactName for contact search, or both.',
505
531
  params: {
506
- query: { type: 'string', description: 'Search term' },
532
+ query: { type: 'string', description: 'Search by bill reference number' },
533
+ contactName: { type: 'string', description: 'Search by contact name' },
507
534
  ...SEARCH_PARAMS,
508
535
  },
509
- required: ['query'],
536
+ required: [],
510
537
  group: 'bills',
511
538
  readOnly: true,
512
539
  execute: async (ctx, input) => {
513
540
  const { mode, limit, offset, sortBy, sortOrder } = extractPaginationInput(input);
514
541
  const query = input.query;
542
+ const contactName = input.contactName;
543
+ let filter;
544
+ if (query && contactName) {
545
+ filter = { reference: { contains: query }, contact: { name: { contains: contactName } } };
546
+ }
547
+ else if (query) {
548
+ filter = { reference: { contains: query } };
549
+ }
550
+ else if (contactName) {
551
+ filter = { contact: { name: { contains: contactName } } };
552
+ }
515
553
  return handlePaginationMode(mode, (off, lim) => searchBills(ctx.client, {
516
- filter: query ? {
517
- or: [
518
- { reference: { contains: query } },
519
- { contact: { name: { contains: query } } },
520
- ],
521
- } : undefined,
554
+ filter,
522
555
  limit: lim, offset: off,
523
556
  sort: { sortBy: [sortBy ?? 'valueDate'], order: (sortOrder ?? 'DESC') },
524
557
  }), limit, offset, 100);
@@ -625,7 +658,7 @@ export const TOOL_DEFINITIONS = [
625
658
  accountResourceId: input.accountResourceId,
626
659
  valueDate: input.valueDate,
627
660
  dueDate: input.valueDate,
628
- reference: input.reference ?? '',
661
+ reference: input.reference || `PMT-${Date.now()}`,
629
662
  paymentMethod: (input.paymentMethod ?? 'BANK_TRANSFER'),
630
663
  saveAsDraft: false,
631
664
  customFields: input.customFields, // eslint-disable-line @typescript-eslint/no-explicit-any
@@ -1553,7 +1586,12 @@ export const TOOL_DEFINITIONS = [
1553
1586
  required: ['valueDate', 'accountResourceId', 'journalEntries'],
1554
1587
  group: 'cash_entries',
1555
1588
  readOnly: false,
1556
- execute: async (ctx, input) => createCashIn(ctx.client, input),
1589
+ execute: async (ctx, input) => {
1590
+ const data = { ...input };
1591
+ if (!data.reference)
1592
+ data.reference = `CI-${Date.now()}`;
1593
+ return createCashIn(ctx.client, data);
1594
+ },
1557
1595
  },
1558
1596
  listTool('list_cash_out', 'List cash-out entries (direct cash disbursements). Paginated.', 'cash_entries', (client, off, lim) => listCashOut(client, { limit: lim, offset: off })),
1559
1597
  {
@@ -1584,7 +1622,12 @@ export const TOOL_DEFINITIONS = [
1584
1622
  required: ['valueDate', 'accountResourceId', 'journalEntries'],
1585
1623
  group: 'cash_entries',
1586
1624
  readOnly: false,
1587
- execute: async (ctx, input) => createCashOut(ctx.client, input),
1625
+ execute: async (ctx, input) => {
1626
+ const data = { ...input };
1627
+ if (!data.reference)
1628
+ data.reference = `CO-${Date.now()}`;
1629
+ return createCashOut(ctx.client, data);
1630
+ },
1588
1631
  },
1589
1632
  {
1590
1633
  name: 'get_cash_in',
@@ -1858,10 +1901,10 @@ export const TOOL_DEFINITIONS = [
1858
1901
  items: {
1859
1902
  type: 'object',
1860
1903
  properties: {
1861
- module: { type: 'string', description: 'Module (e.g., ACCOUNTING, SALES)' },
1862
- role: { type: 'string', description: 'Role (e.g., ADMIN, VIEWER)' },
1904
+ moduleName: { type: 'string', description: 'Module name: ORGANIZATION, USER_MANAGEMENT, ACCOUNTING, SALES, PURCHASES, REPORTS, or FIXED_ASSET' },
1905
+ roleCode: { type: 'string', description: 'Role code: ADMIN, EDITOR, VIEWER, or NONE' },
1863
1906
  },
1864
- required: ['module', 'role'],
1907
+ required: ['moduleName', 'roleCode'],
1865
1908
  },
1866
1909
  description: 'Module role assignments',
1867
1910
  },
@@ -1881,10 +1924,10 @@ export const TOOL_DEFINITIONS = [
1881
1924
  items: {
1882
1925
  type: 'object',
1883
1926
  properties: {
1884
- module: { type: 'string' },
1885
- role: { type: 'string' },
1927
+ moduleName: { type: 'string', description: 'Module name: ORGANIZATION, USER_MANAGEMENT, ACCOUNTING, SALES, PURCHASES, REPORTS, or FIXED_ASSET' },
1928
+ roleCode: { type: 'string', description: 'Role code: ADMIN, EDITOR, VIEWER, or NONE' },
1886
1929
  },
1887
- required: ['module', 'role'],
1930
+ required: ['moduleName', 'roleCode'],
1888
1931
  },
1889
1932
  },
1890
1933
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jaz-clio",
3
- "version": "4.25.4",
3
+ "version": "4.25.6",
4
4
  "description": "Clio — Command Line Interface Orchestrator for Jaz AI.",
5
5
  "type": "module",
6
6
  "bin": {