bkper 4.0.1 → 4.0.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 (41) hide show
  1. package/CHANGELOG.md +48 -28
  2. package/lib/bkper-factory.d.ts.map +1 -1
  3. package/lib/bkper-factory.js +5 -0
  4. package/lib/bkper-factory.js.map +1 -1
  5. package/lib/cli.js +100 -32
  6. package/lib/cli.js.map +1 -1
  7. package/lib/commands/accounts/create.d.ts.map +1 -1
  8. package/lib/commands/accounts/create.js +16 -5
  9. package/lib/commands/accounts/create.js.map +1 -1
  10. package/lib/commands/accounts/update.d.ts.map +1 -1
  11. package/lib/commands/accounts/update.js +13 -5
  12. package/lib/commands/accounts/update.js.map +1 -1
  13. package/lib/commands/books/create.d.ts.map +1 -1
  14. package/lib/commands/books/create.js +10 -2
  15. package/lib/commands/books/create.js.map +1 -1
  16. package/lib/commands/books/update.d.ts.map +1 -1
  17. package/lib/commands/books/update.js +13 -5
  18. package/lib/commands/books/update.js.map +1 -1
  19. package/lib/commands/groups/create.d.ts.map +1 -1
  20. package/lib/commands/groups/create.js +16 -5
  21. package/lib/commands/groups/create.js.map +1 -1
  22. package/lib/commands/groups/update.d.ts.map +1 -1
  23. package/lib/commands/groups/update.js +13 -5
  24. package/lib/commands/groups/update.js.map +1 -1
  25. package/lib/commands/transactions/create.d.ts.map +1 -1
  26. package/lib/commands/transactions/create.js +23 -7
  27. package/lib/commands/transactions/create.js.map +1 -1
  28. package/lib/commands/transactions/merge.d.ts.map +1 -1
  29. package/lib/commands/transactions/merge.js +6 -2
  30. package/lib/commands/transactions/merge.js.map +1 -1
  31. package/lib/commands/transactions/update.d.ts.map +1 -1
  32. package/lib/commands/transactions/update.js +23 -7
  33. package/lib/commands/transactions/update.js.map +1 -1
  34. package/lib/render/table-formatter.d.ts.map +1 -1
  35. package/lib/render/table-formatter.js +22 -4
  36. package/lib/render/table-formatter.js.map +1 -1
  37. package/lib/utils/validation.d.ts +38 -0
  38. package/lib/utils/validation.d.ts.map +1 -0
  39. package/lib/utils/validation.js +46 -0
  40. package/lib/utils/validation.js.map +1 -0
  41. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,45 +4,65 @@
4
4
 
5
5
  ### **February 2026**
6
6
 
7
- **CLI**
8
-
9
- - Table-formatted output is now the default for all commands
10
- - Added `--json` global flag to output raw JSON instead of formatted tables
11
- - Added `-b, --book` required option for scoping commands to a book
12
- - Added `-p, --properties` flag to `transaction list` to include custom properties
13
- - Transaction tables show formatted dates and values with IDs
14
- - Group tables render as indented trees showing hierarchy
15
- - Single-item commands display as indented key-value pairs
16
- - Removed MCP server (`mcp start` command)
7
+ - **CLI**
8
+ - Table-formatted output is now the default for all commands
9
+ - Added `--json` global flag to output raw JSON instead of formatted tables
10
+ - Added `-b, --book` option for scoping commands to a specific [Book](https://bkper.com/docs)
11
+ - Added `-p, --properties` repeatable flag for setting custom properties as `key=value` pairs
12
+ - [Transaction](https://bkper.com/docs) tables show formatted dates and values with IDs
13
+ - [Group](https://bkper.com/docs) tables render as indented trees showing hierarchy
14
+ - Single-item commands display as indented key-value pairs
15
+ - Removed MCP server now maintained as a separate project
16
+ - **Data Management**
17
+ - Added [Book](https://bkper.com/docs) create command
18
+ - Added [Collection](https://bkper.com/docs) commands: create, list, get, update, delete, add-book, remove-book
19
+ - Added [Transaction](https://bkper.com/docs) update command
20
+ - Renamed `balance get` to `balance list` for consistency
21
+ - **Authentication**
22
+ - Switched to PKCE-based OAuth flow — no client secret required
23
+ - Branded OAuth callback pages for a polished sign-in experience
24
+ - **App Development**
25
+ - Local development now uses Cloudflare Tunnel for event handling — no cloud deployment needed during development
26
+ - Renamed `dev` environment to `preview` for clarity
27
+ - Added `--no-open` flag to suppress automatic browser launch during dev
28
+
29
+ ### **January 2026**
30
+
31
+ - **App Platform**
32
+ - Added [`app init`](https://bkper.com/docs) command to scaffold new apps from template
33
+ - Added [`app deploy`](https://bkper.com/docs) and [`app undeploy`](https://bkper.com/docs) commands for managing deployments
34
+ - Added [`app status`](https://bkper.com/docs) to view current deployment information
35
+ - Added [`app dev`](https://bkper.com/docs) and [`app build`](https://bkper.com/docs) commands for local development and build workflows
36
+ - Added [`app secrets`](https://bkper.com/docs) management — put, list, and delete secrets for your apps
37
+ - Added [`app sync`](https://bkper.com/docs) command to push `bkper.yaml` configuration to the platform
38
+ - Support for shared packages in monorepo setups with hot reload
39
+ - Asset file uploads included in deployments
40
+ - Migrated app configuration from `bkperapp.yaml` to `bkper.yaml`
17
41
 
18
42
  ## 2025
19
43
 
20
44
  ### **October 2025**
21
45
 
22
- **MCP Server**
23
-
24
- - Added smart transaction merging - combine multiple transactions intelligently based on date and account matching
25
- - Simplified transaction creation - accounts are now optional, making it easier to record simple income and expenses
26
- - Improved transaction data responses for better AI assistant integration
46
+ - **MCP Server**
47
+ - Added smart [Transaction](https://bkper.com/docs) merging — combine multiple transactions based on date and account matching
48
+ - Simplified [Transaction](https://bkper.com/docs) creation accounts are now optional for recording simple income and expenses
49
+ - Improved transaction data responses for better AI assistant integration
27
50
 
28
51
  ### **September 2025**
29
52
 
30
- **MCP Server**
31
-
32
- - Streamlined transaction data for cleaner AI assistant responses
33
- - Fixed credential storage to follow standard configuration directories
53
+ - **MCP Server**
54
+ - Streamlined transaction data for cleaner AI assistant responses
55
+ - Fixed credential storage to follow standard configuration directories
34
56
 
35
57
  ### **July 2025**
36
58
 
37
- **MCP Server**
38
-
39
- - Added support for AI assistants to analyze your books with monthly and year-to-date balances
40
- - Improved date filtering with more intuitive `before:` operator
41
- - Added setup instructions for Claude Desktop and other AI tools
59
+ - **MCP Server**
60
+ - Added monthly and year-to-date [Balance](https://bkper.com/docs) analysis for AI assistants
61
+ - Improved date filtering with `before:` operator
62
+ - Added setup instructions for Claude Desktop and other AI tools
42
63
 
43
64
  ### **June 2025**
44
65
 
45
- **bkper-node CLI**
46
-
47
- - Introduced MCP server - connect AI assistants to your Bkper books with `bkper mcp start`
48
- - Added book name filtering to quickly find specific books
66
+ - **CLI**
67
+ - Introduced MCP server — connect AI assistants to your Bkper [Books](https://bkper.com/docs) with `bkper mcp start`
68
+ - Added [Book](https://bkper.com/docs) name filtering to quickly find specific books
@@ -1 +1 @@
1
- {"version":3,"file":"bkper-factory.d.ts","sourceRoot":"","sources":["../src/bkper-factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAKjC;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,KAAK,CAiBxC;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,SAUzB"}
1
+ {"version":3,"file":"bkper-factory.d.ts","sourceRoot":"","sources":["../src/bkper-factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAKjC;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,KAAK,CAiBxC;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,SAezB"}
@@ -43,6 +43,11 @@ export function setupBkper() {
43
43
  Bkper.setConfig({
44
44
  apiKeyProvider: apiKey ? () => __awaiter(this, void 0, void 0, function* () { return apiKey; }) : undefined,
45
45
  oauthTokenProvider: () => getOAuthToken(),
46
+ requestHeadersProvider: () => __awaiter(this, void 0, void 0, function* () {
47
+ return {
48
+ 'bkper-agent-id': 'bkper-cli',
49
+ };
50
+ }),
46
51
  //@ts-ignore
47
52
  apiBaseUrl,
48
53
  });
@@ -1 +1 @@
1
- {"version":3,"file":"bkper-factory.js","sourceRoot":"","sources":["../src/bkper-factory.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAE7D,IAAI,uBAAuB,GAAsB,SAAS,CAAC;AAE3D;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB;IAC5B,8CAA8C;IAC9C,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,MAAM,IAAK,UAAkB,CAAC,WAAW,EAAE,CAAC;QACrE,OAAQ,UAAkB,CAAC,WAAW,IAAI,KAAK,CAAC;IACpD,CAAC;IAED,+CAA+C;IAC/C,IAAI,uBAAuB,EAAE,CAAC;QAC1B,OAAO,uBAAuB,CAAC;IACnC,CAAC;IAED,sCAAsC;IACtC,UAAU,EAAE,CAAC;IACb,gCAAgC;IAChC,uBAAuB,GAAG,IAAI,KAAK,EAAE,CAAC;IAEtC,OAAO,uBAAuB,CAAC;AACnC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU;IACtB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;IACzC,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;IAE7C,KAAK,CAAC,SAAS,CAAC;QACZ,cAAc,EAAE,MAAM,CAAC,CAAC,CAAC,GAAS,EAAE,gDAAC,OAAA,MAAM,CAAA,GAAA,CAAC,CAAC,CAAC,SAAS;QACvD,kBAAkB,EAAE,GAAG,EAAE,CAAC,aAAa,EAAE;QACzC,YAAY;QACZ,UAAU;KACb,CAAC,CAAC;AACP,CAAC"}
1
+ {"version":3,"file":"bkper-factory.js","sourceRoot":"","sources":["../src/bkper-factory.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAE7D,IAAI,uBAAuB,GAAsB,SAAS,CAAC;AAE3D;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB;IAC5B,8CAA8C;IAC9C,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,MAAM,IAAK,UAAkB,CAAC,WAAW,EAAE,CAAC;QACrE,OAAQ,UAAkB,CAAC,WAAW,IAAI,KAAK,CAAC;IACpD,CAAC;IAED,+CAA+C;IAC/C,IAAI,uBAAuB,EAAE,CAAC;QAC1B,OAAO,uBAAuB,CAAC;IACnC,CAAC;IAED,sCAAsC;IACtC,UAAU,EAAE,CAAC;IACb,gCAAgC;IAChC,uBAAuB,GAAG,IAAI,KAAK,EAAE,CAAC;IAEtC,OAAO,uBAAuB,CAAC;AACnC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU;IACtB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;IACzC,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;IAE7C,KAAK,CAAC,SAAS,CAAC;QACZ,cAAc,EAAE,MAAM,CAAC,CAAC,CAAC,GAAS,EAAE,gDAAC,OAAA,MAAM,CAAA,GAAA,CAAC,CAAC,CAAC,SAAS;QACvD,kBAAkB,EAAE,GAAG,EAAE,CAAC,aAAa,EAAE;QACzC,sBAAsB,EAAE,GAAS,EAAE;YAC/B,OAAO;gBACH,gBAAgB,EAAE,WAAW;aAChC,CAAC;QACN,CAAC,CAAA;QACD,YAAY;QACZ,UAAU;KACb,CAAC,CAAC;AACP,CAAC"}
package/lib/cli.js CHANGED
@@ -10,7 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  import 'dotenv/config'; // Must be first to load env vars before other imports
12
12
  import { program } from 'commander';
13
- import { BooksDataTableBuilder, AccountsDataTableBuilder, GroupsDataTableBuilder, } from 'bkper-js';
13
+ import { AccountsDataTableBuilder, GroupsDataTableBuilder, } from 'bkper-js';
14
14
  import { login, logout } from './auth/local-auth-service.js';
15
15
  import { setupBkper } from './bkper-factory.js';
16
16
  import { listApps, syncApp, deployApp, undeployApp, statusApp, initApp, secretsPut, secretsList, secretsDelete, dev, build, installApp, uninstallApp, } from './commands/apps/index.js';
@@ -21,6 +21,7 @@ import { listTransactions, createTransaction, updateTransaction, postTransaction
21
21
  import { listBalancesMatrix } from './commands/balances/index.js';
22
22
  import { listCollections, getCollection, createCollection, updateCollection, deleteCollection, addBookToCollection, removeBookFromCollection, } from './commands/collections/index.js';
23
23
  import { renderTable, renderItem } from './render/index.js';
24
+ import { validateRequiredOptions, throwIfErrors } from './utils/validation.js';
24
25
  function collectProperty(value, previous) {
25
26
  return previous ? [...previous, value] : [value];
26
27
  }
@@ -221,9 +222,10 @@ secretsCommand
221
222
  appCommand
222
223
  .command('install <appId>')
223
224
  .description('Install an app into a book')
224
- .requiredOption('-b, --book <bookId>', 'Book ID')
225
+ .option('-b, --book <bookId>', 'Book ID')
225
226
  .action((appId, options) => __awaiter(void 0, void 0, void 0, function* () {
226
227
  try {
228
+ throwIfErrors(validateRequiredOptions(options, [{ name: 'book', flag: '--book' }]));
227
229
  setupBkper();
228
230
  const integration = yield installApp(options.book, appId);
229
231
  renderItem(integration.json(), isJson());
@@ -236,9 +238,10 @@ appCommand
236
238
  appCommand
237
239
  .command('uninstall <appId>')
238
240
  .description('Uninstall an app from a book')
239
- .requiredOption('-b, --book <bookId>', 'Book ID')
241
+ .option('-b, --book <bookId>', 'Book ID')
240
242
  .action((appId, options) => __awaiter(void 0, void 0, void 0, function* () {
241
243
  try {
244
+ throwIfErrors(validateRequiredOptions(options, [{ name: 'book', flag: '--book' }]));
242
245
  setupBkper();
243
246
  const integration = yield uninstallApp(options.book, appId);
244
247
  renderItem(integration.json(), isJson());
@@ -255,6 +258,7 @@ bookCommand
255
258
  .description('List all books')
256
259
  .option('-q, --query <query>', 'Search query')
257
260
  .action((options) => __awaiter(void 0, void 0, void 0, function* () {
261
+ var _a;
258
262
  try {
259
263
  setupBkper();
260
264
  const books = yield listBooks(options.query);
@@ -262,7 +266,33 @@ bookCommand
262
266
  console.log(JSON.stringify(books.map(b => b.json()), null, 2));
263
267
  }
264
268
  else {
265
- const matrix = new BooksDataTableBuilder(books).ids(true).build();
269
+ if (books.length === 0) {
270
+ console.log('No results found.');
271
+ return;
272
+ }
273
+ books.sort((a, b) => {
274
+ var _a, _b;
275
+ const collA = (_a = a.getCollection()) === null || _a === void 0 ? void 0 : _a.getName();
276
+ const collB = (_b = b.getCollection()) === null || _b === void 0 ? void 0 : _b.getName();
277
+ if (collA && !collB)
278
+ return -1;
279
+ if (!collA && collB)
280
+ return 1;
281
+ let ret = (collA || '').localeCompare(collB || '');
282
+ if (ret === 0) {
283
+ ret = (a.getName() || '').localeCompare(b.getName() || '');
284
+ }
285
+ return ret;
286
+ });
287
+ const matrix = [['Book Id', 'Name', 'Owner', 'Collection']];
288
+ for (const book of books) {
289
+ matrix.push([
290
+ book.getId() || '',
291
+ book.getName() || '',
292
+ book.getOwnerName() || '',
293
+ ((_a = book.getCollection()) === null || _a === void 0 ? void 0 : _a.getName()) || '',
294
+ ]);
295
+ }
266
296
  renderTable(matrix, false);
267
297
  }
268
298
  }
@@ -288,7 +318,7 @@ bookCommand
288
318
  bookCommand
289
319
  .command('create')
290
320
  .description('Create a new book')
291
- .requiredOption('--name <name>', 'Book name')
321
+ .option('--name <name>', 'Book name')
292
322
  .option('--fraction-digits <digits>', 'Number of decimal places (0-8)', parseInt)
293
323
  .option('--date-pattern <pattern>', 'Date format pattern (dd/MM/yyyy, MM/dd/yyyy, or yyyy/MM/dd)')
294
324
  .option('--decimal-separator <separator>', 'Decimal separator (DOT or COMMA)')
@@ -297,6 +327,7 @@ bookCommand
297
327
  .option('-p, --property <key=value>', 'Set a property (repeatable)', collectProperty)
298
328
  .action((options) => __awaiter(void 0, void 0, void 0, function* () {
299
329
  try {
330
+ throwIfErrors(validateRequiredOptions(options, [{ name: 'name', flag: '--name' }]));
300
331
  setupBkper();
301
332
  const book = yield createBook({
302
333
  name: options.name,
@@ -352,9 +383,10 @@ const accountCommand = program.command('account').description('Manage Accounts')
352
383
  accountCommand
353
384
  .command('list')
354
385
  .description('List all accounts in a book')
355
- .requiredOption('-b, --book <bookId>', 'Book ID')
386
+ .option('-b, --book <bookId>', 'Book ID')
356
387
  .action((options) => __awaiter(void 0, void 0, void 0, function* () {
357
388
  try {
389
+ throwIfErrors(validateRequiredOptions(options, [{ name: 'book', flag: '--book' }]));
358
390
  setupBkper();
359
391
  const bookId = options.book;
360
392
  const accounts = yield listAccounts(bookId);
@@ -377,9 +409,10 @@ accountCommand
377
409
  accountCommand
378
410
  .command('get <idOrName>')
379
411
  .description('Get an account by ID or name')
380
- .requiredOption('-b, --book <bookId>', 'Book ID')
412
+ .option('-b, --book <bookId>', 'Book ID')
381
413
  .action((idOrName, options) => __awaiter(void 0, void 0, void 0, function* () {
382
414
  try {
415
+ throwIfErrors(validateRequiredOptions(options, [{ name: 'book', flag: '--book' }]));
383
416
  setupBkper();
384
417
  const bookId = options.book;
385
418
  const account = yield getAccount(bookId, idOrName);
@@ -393,14 +426,18 @@ accountCommand
393
426
  accountCommand
394
427
  .command('create')
395
428
  .description('Create a new account')
396
- .requiredOption('-b, --book <bookId>', 'Book ID')
397
- .requiredOption('--name <name>', 'Account name')
429
+ .option('-b, --book <bookId>', 'Book ID')
430
+ .option('--name <name>', 'Account name')
398
431
  .option('--type <type>', 'Account type (ASSET, LIABILITY, INCOMING, OUTGOING)')
399
432
  .option('--description <description>', 'Account description')
400
433
  .option('--groups <groups>', 'Comma-separated group names')
401
434
  .option('-p, --property <key=value>', 'Set a property (repeatable)', collectProperty)
402
435
  .action((options) => __awaiter(void 0, void 0, void 0, function* () {
403
436
  try {
437
+ throwIfErrors(validateRequiredOptions(options, [
438
+ { name: 'book', flag: '--book' },
439
+ { name: 'name', flag: '--name' },
440
+ ]));
404
441
  setupBkper();
405
442
  const bookId = options.book;
406
443
  const account = yield createAccount(bookId, {
@@ -422,13 +459,14 @@ accountCommand
422
459
  accountCommand
423
460
  .command('update <idOrName>')
424
461
  .description('Update an account')
425
- .requiredOption('-b, --book <bookId>', 'Book ID')
462
+ .option('-b, --book <bookId>', 'Book ID')
426
463
  .option('--name <name>', 'Account name')
427
464
  .option('--type <type>', 'Account type (ASSET, LIABILITY, INCOMING, OUTGOING)')
428
465
  .option('--archived <archived>', 'Archive status (true/false)')
429
466
  .option('-p, --property <key=value>', 'Set a property (repeatable)', collectProperty)
430
467
  .action((idOrName, options) => __awaiter(void 0, void 0, void 0, function* () {
431
468
  try {
469
+ throwIfErrors(validateRequiredOptions(options, [{ name: 'book', flag: '--book' }]));
432
470
  setupBkper();
433
471
  const bookId = options.book;
434
472
  const account = yield updateAccount(bookId, idOrName, {
@@ -447,9 +485,10 @@ accountCommand
447
485
  accountCommand
448
486
  .command('delete <idOrName>')
449
487
  .description('Delete an account')
450
- .requiredOption('-b, --book <bookId>', 'Book ID')
488
+ .option('-b, --book <bookId>', 'Book ID')
451
489
  .action((idOrName, options) => __awaiter(void 0, void 0, void 0, function* () {
452
490
  try {
491
+ throwIfErrors(validateRequiredOptions(options, [{ name: 'book', flag: '--book' }]));
453
492
  setupBkper();
454
493
  const bookId = options.book;
455
494
  const account = yield deleteAccount(bookId, idOrName);
@@ -465,9 +504,10 @@ const groupCommand = program.command('group').description('Manage Groups');
465
504
  groupCommand
466
505
  .command('list')
467
506
  .description('List all groups in a book')
468
- .requiredOption('-b, --book <bookId>', 'Book ID')
507
+ .option('-b, --book <bookId>', 'Book ID')
469
508
  .action((options) => __awaiter(void 0, void 0, void 0, function* () {
470
509
  try {
510
+ throwIfErrors(validateRequiredOptions(options, [{ name: 'book', flag: '--book' }]));
471
511
  setupBkper();
472
512
  const bookId = options.book;
473
513
  const groups = yield listGroups(bookId);
@@ -487,9 +527,10 @@ groupCommand
487
527
  groupCommand
488
528
  .command('get <idOrName>')
489
529
  .description('Get a group by ID or name')
490
- .requiredOption('-b, --book <bookId>', 'Book ID')
530
+ .option('-b, --book <bookId>', 'Book ID')
491
531
  .action((idOrName, options) => __awaiter(void 0, void 0, void 0, function* () {
492
532
  try {
533
+ throwIfErrors(validateRequiredOptions(options, [{ name: 'book', flag: '--book' }]));
493
534
  setupBkper();
494
535
  const bookId = options.book;
495
536
  const group = yield getGroup(bookId, idOrName);
@@ -503,13 +544,17 @@ groupCommand
503
544
  groupCommand
504
545
  .command('create')
505
546
  .description('Create a new group')
506
- .requiredOption('-b, --book <bookId>', 'Book ID')
507
- .requiredOption('--name <name>', 'Group name')
547
+ .option('-b, --book <bookId>', 'Book ID')
548
+ .option('--name <name>', 'Group name')
508
549
  .option('--parent <parent>', 'Parent group name or ID')
509
550
  .option('--hidden', 'Hide the group')
510
551
  .option('-p, --property <key=value>', 'Set a property (repeatable)', collectProperty)
511
552
  .action((options) => __awaiter(void 0, void 0, void 0, function* () {
512
553
  try {
554
+ throwIfErrors(validateRequiredOptions(options, [
555
+ { name: 'book', flag: '--book' },
556
+ { name: 'name', flag: '--name' },
557
+ ]));
513
558
  setupBkper();
514
559
  const bookId = options.book;
515
560
  const group = yield createGroup(bookId, {
@@ -528,12 +573,13 @@ groupCommand
528
573
  groupCommand
529
574
  .command('update <idOrName>')
530
575
  .description('Update a group')
531
- .requiredOption('-b, --book <bookId>', 'Book ID')
576
+ .option('-b, --book <bookId>', 'Book ID')
532
577
  .option('--name <name>', 'Group name')
533
578
  .option('--hidden <hidden>', 'Hide status (true/false)')
534
579
  .option('-p, --property <key=value>', 'Set a property (repeatable)', collectProperty)
535
580
  .action((idOrName, options) => __awaiter(void 0, void 0, void 0, function* () {
536
581
  try {
582
+ throwIfErrors(validateRequiredOptions(options, [{ name: 'book', flag: '--book' }]));
537
583
  setupBkper();
538
584
  const bookId = options.book;
539
585
  const group = yield updateGroup(bookId, idOrName, {
@@ -551,9 +597,10 @@ groupCommand
551
597
  groupCommand
552
598
  .command('delete <idOrName>')
553
599
  .description('Delete a group')
554
- .requiredOption('-b, --book <bookId>', 'Book ID')
600
+ .option('-b, --book <bookId>', 'Book ID')
555
601
  .action((idOrName, options) => __awaiter(void 0, void 0, void 0, function* () {
556
602
  try {
603
+ throwIfErrors(validateRequiredOptions(options, [{ name: 'book', flag: '--book' }]));
557
604
  setupBkper();
558
605
  const bookId = options.book;
559
606
  const group = yield deleteGroup(bookId, idOrName);
@@ -569,13 +616,17 @@ const transactionCommand = program.command('transaction').description('Manage Tr
569
616
  transactionCommand
570
617
  .command('list')
571
618
  .description('List transactions in a book')
572
- .requiredOption('-b, --book <bookId>', 'Book ID')
573
- .requiredOption('-q, --query <query>', 'Search query')
619
+ .option('-b, --book <bookId>', 'Book ID')
620
+ .option('-q, --query <query>', 'Search query')
574
621
  .option('-l, --limit <limit>', 'Maximum number of results (1-1000)', parseInt)
575
622
  .option('-c, --cursor <cursor>', 'Pagination cursor')
576
623
  .option('-p, --properties', 'Include custom properties')
577
624
  .action((options) => __awaiter(void 0, void 0, void 0, function* () {
578
625
  try {
626
+ throwIfErrors(validateRequiredOptions(options, [
627
+ { name: 'book', flag: '--book' },
628
+ { name: 'query', flag: '--query' },
629
+ ]));
579
630
  setupBkper();
580
631
  const bookId = options.book;
581
632
  const result = yield listTransactions(bookId, {
@@ -614,9 +665,9 @@ transactionCommand
614
665
  transactionCommand
615
666
  .command('create')
616
667
  .description('Create a transaction')
617
- .requiredOption('-b, --book <bookId>', 'Book ID')
618
- .requiredOption('--date <date>', 'Transaction date')
619
- .requiredOption('--amount <amount>', 'Transaction amount')
668
+ .option('-b, --book <bookId>', 'Book ID')
669
+ .option('--date <date>', 'Transaction date')
670
+ .option('--amount <amount>', 'Transaction amount')
620
671
  .option('--description <description>', 'Transaction description')
621
672
  .option('--from <from>', 'Credit account (source)')
622
673
  .option('--to <to>', 'Debit account (destination)')
@@ -625,6 +676,11 @@ transactionCommand
625
676
  .option('-p, --property <key=value>', 'Set a property (repeatable, empty value deletes)', collectProperty)
626
677
  .action((options) => __awaiter(void 0, void 0, void 0, function* () {
627
678
  try {
679
+ throwIfErrors(validateRequiredOptions(options, [
680
+ { name: 'book', flag: '--book' },
681
+ { name: 'date', flag: '--date' },
682
+ { name: 'amount', flag: '--amount' },
683
+ ]));
628
684
  setupBkper();
629
685
  const bookId = options.book;
630
686
  const transaction = yield createTransaction(bookId, {
@@ -647,9 +703,10 @@ transactionCommand
647
703
  transactionCommand
648
704
  .command('post <transactionId>')
649
705
  .description('Post a transaction')
650
- .requiredOption('-b, --book <bookId>', 'Book ID')
706
+ .option('-b, --book <bookId>', 'Book ID')
651
707
  .action((transactionId, options) => __awaiter(void 0, void 0, void 0, function* () {
652
708
  try {
709
+ throwIfErrors(validateRequiredOptions(options, [{ name: 'book', flag: '--book' }]));
653
710
  setupBkper();
654
711
  const bookId = options.book;
655
712
  const transaction = yield postTransaction(bookId, transactionId);
@@ -663,7 +720,7 @@ transactionCommand
663
720
  transactionCommand
664
721
  .command('update <transactionId>')
665
722
  .description('Update a transaction')
666
- .requiredOption('-b, --book <bookId>', 'Book ID')
723
+ .option('-b, --book <bookId>', 'Book ID')
667
724
  .option('--date <date>', 'Transaction date')
668
725
  .option('--amount <amount>', 'Transaction amount')
669
726
  .option('--description <description>', 'Transaction description')
@@ -673,6 +730,7 @@ transactionCommand
673
730
  .option('-p, --property <key=value>', 'Set a property (repeatable, empty value deletes)', collectProperty)
674
731
  .action((transactionId, options) => __awaiter(void 0, void 0, void 0, function* () {
675
732
  try {
733
+ throwIfErrors(validateRequiredOptions(options, [{ name: 'book', flag: '--book' }]));
676
734
  setupBkper();
677
735
  const bookId = options.book;
678
736
  const transaction = yield updateTransaction(bookId, transactionId, {
@@ -694,9 +752,10 @@ transactionCommand
694
752
  transactionCommand
695
753
  .command('check <transactionId>')
696
754
  .description('Check a transaction')
697
- .requiredOption('-b, --book <bookId>', 'Book ID')
755
+ .option('-b, --book <bookId>', 'Book ID')
698
756
  .action((transactionId, options) => __awaiter(void 0, void 0, void 0, function* () {
699
757
  try {
758
+ throwIfErrors(validateRequiredOptions(options, [{ name: 'book', flag: '--book' }]));
700
759
  setupBkper();
701
760
  const bookId = options.book;
702
761
  const transaction = yield checkTransaction(bookId, transactionId);
@@ -710,9 +769,10 @@ transactionCommand
710
769
  transactionCommand
711
770
  .command('trash <transactionId>')
712
771
  .description('Trash a transaction')
713
- .requiredOption('-b, --book <bookId>', 'Book ID')
772
+ .option('-b, --book <bookId>', 'Book ID')
714
773
  .action((transactionId, options) => __awaiter(void 0, void 0, void 0, function* () {
715
774
  try {
775
+ throwIfErrors(validateRequiredOptions(options, [{ name: 'book', flag: '--book' }]));
716
776
  setupBkper();
717
777
  const bookId = options.book;
718
778
  const transaction = yield trashTransaction(bookId, transactionId);
@@ -726,9 +786,10 @@ transactionCommand
726
786
  transactionCommand
727
787
  .command('merge <transactionId1> <transactionId2>')
728
788
  .description('Merge two transactions')
729
- .requiredOption('-b, --book <bookId>', 'Book ID')
789
+ .option('-b, --book <bookId>', 'Book ID')
730
790
  .action((transactionId1, transactionId2, options) => __awaiter(void 0, void 0, void 0, function* () {
731
791
  try {
792
+ throwIfErrors(validateRequiredOptions(options, [{ name: 'book', flag: '--book' }]));
732
793
  setupBkper();
733
794
  const bookId = options.book;
734
795
  const result = yield mergeTransactions(bookId, transactionId1, transactionId2);
@@ -753,11 +814,15 @@ const balanceCommand = program.command('balance').description('Manage Balances')
753
814
  balanceCommand
754
815
  .command('list')
755
816
  .description('List balances')
756
- .requiredOption('-b, --book <bookId>', 'Book ID')
757
- .requiredOption('-q, --query <query>', 'Balances query')
817
+ .option('-b, --book <bookId>', 'Book ID')
818
+ .option('-q, --query <query>', 'Balances query')
758
819
  .option('--expanded <level>', 'Expand groups to specified depth (0+)', parseInt)
759
820
  .action((options) => __awaiter(void 0, void 0, void 0, function* () {
760
821
  try {
822
+ throwIfErrors(validateRequiredOptions(options, [
823
+ { name: 'book', flag: '--book' },
824
+ { name: 'query', flag: '--query' },
825
+ ]));
761
826
  setupBkper();
762
827
  const bookId = options.book;
763
828
  const matrix = yield listBalancesMatrix(bookId, {
@@ -818,9 +883,10 @@ collectionCommand
818
883
  collectionCommand
819
884
  .command('create')
820
885
  .description('Create a new collection')
821
- .requiredOption('--name <name>', 'Collection name')
886
+ .option('--name <name>', 'Collection name')
822
887
  .action((options) => __awaiter(void 0, void 0, void 0, function* () {
823
888
  try {
889
+ throwIfErrors(validateRequiredOptions(options, [{ name: 'name', flag: '--name' }]));
824
890
  setupBkper();
825
891
  const collection = yield createCollection({ name: options.name });
826
892
  renderItem(collection.json(), isJson());
@@ -867,9 +933,10 @@ function collectBook(value, previous) {
867
933
  collectionCommand
868
934
  .command('add-book <collectionId>')
869
935
  .description('Add books to a collection')
870
- .requiredOption('-b, --book <bookId>', 'Book ID (repeatable)', collectBook)
936
+ .option('-b, --book <bookId>', 'Book ID (repeatable)', collectBook)
871
937
  .action((collectionId, options) => __awaiter(void 0, void 0, void 0, function* () {
872
938
  try {
939
+ throwIfErrors(validateRequiredOptions(options, [{ name: 'book', flag: '--book' }]));
873
940
  setupBkper();
874
941
  const books = yield addBookToCollection(collectionId, options.book);
875
942
  if (isJson()) {
@@ -887,9 +954,10 @@ collectionCommand
887
954
  collectionCommand
888
955
  .command('remove-book <collectionId>')
889
956
  .description('Remove books from a collection')
890
- .requiredOption('-b, --book <bookId>', 'Book ID (repeatable)', collectBook)
957
+ .option('-b, --book <bookId>', 'Book ID (repeatable)', collectBook)
891
958
  .action((collectionId, options) => __awaiter(void 0, void 0, void 0, function* () {
892
959
  try {
960
+ throwIfErrors(validateRequiredOptions(options, [{ name: 'book', flag: '--book' }]));
893
961
  setupBkper();
894
962
  const books = yield removeBookFromCollection(collectionId, options.book);
895
963
  if (isJson()) {