@andrzejchm/notion-cli 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -95,7 +95,10 @@ notion ls
95
95
  | `notion comments <id\|url>` | Read page comments |
96
96
  | `notion comment <id\|url> -m <text>` | Add a comment to a page |
97
97
  | `notion append <id\|url> -m <markdown>` | Append markdown blocks to a page |
98
+ | `notion edit-page <id\|url> --find <old> --replace <new>` | Search-and-replace text on a page |
99
+ | `notion edit-page <id\|url> -m <markdown>` | Replace entire page content |
98
100
  | `notion create-page --parent <id\|url> --title <title>` | Create a new page, prints URL |
101
+ | `notion update <id\|url> --prop "Name=Value"` | Update properties on a page |
99
102
  | `notion completion bash\|zsh\|fish` | Install shell tab completion |
100
103
 
101
104
  ### `notion db query` flags
@@ -188,6 +191,7 @@ Write commands require additional capabilities — enable in your integration se
188
191
  |---------|----------------------|
189
192
  | `notion append` | Read content, Insert content |
190
193
  | `notion create-page` | Read content, Insert content |
194
+ | `notion update` | Read content, Update content |
191
195
  | `notion comment` | Read content, Insert content, Read comments, Insert comments |
192
196
 
193
197
  ---
@@ -208,6 +212,12 @@ Write commands require additional capabilities — enable in your integration se
208
212
 
209
213
  ---
210
214
 
215
+ ## Roadmap & Feature Parity
216
+
217
+ See [docs/FEATURE-PARITY.md](docs/FEATURE-PARITY.md) for a detailed comparison of this CLI's capabilities against the official Notion MCP server, with prioritized gaps and planned additions.
218
+
219
+ ---
220
+
211
221
  ## License
212
222
 
213
223
  MIT © [Andrzej Chmielewski](https://github.com/andrzejchm)
package/dist/cli.js CHANGED
@@ -4,7 +4,7 @@
4
4
  import { readFileSync } from "fs";
5
5
  import { dirname, join as join3 } from "path";
6
6
  import { fileURLToPath } from "url";
7
- import { Command as Command21 } from "commander";
7
+ import { Command as Command22 } from "commander";
8
8
 
9
9
  // src/commands/append.ts
10
10
  import { Command } from "commander";
@@ -444,6 +444,12 @@ function withErrorHandling(fn) {
444
444
  });
445
445
  }
446
446
 
447
+ // src/errors/notion-errors.ts
448
+ var SELECTOR_HINT = 'Use an ellipsis selector matching page content, e.g. "## Section...end of section". Run `notion read <id>` to see the page content.';
449
+ function isNotionValidationError(error2) {
450
+ return typeof error2 === "object" && error2 !== null && "code" in error2 && error2.code === "validation_error";
451
+ }
452
+
447
453
  // src/notion/client.ts
448
454
  import { APIErrorCode, Client, isNotionClientError } from "@notionhq/client";
449
455
  async function validateToken(token) {
@@ -557,19 +563,23 @@ async function addComment(client, pageId, text, options = {}) {
557
563
  ...options.asUser && { display_name: { type: "user" } }
558
564
  });
559
565
  }
560
- async function appendMarkdown(client, pageId, markdown) {
566
+ async function appendMarkdown(client, pageId, markdown, options) {
561
567
  await client.pages.updateMarkdown({
562
568
  page_id: pageId,
563
569
  type: "insert_content",
564
- insert_content: { content: markdown }
570
+ insert_content: {
571
+ content: markdown,
572
+ ...options?.after != null && { after: options.after }
573
+ }
565
574
  });
566
575
  }
567
576
  function countOccurrences(text, sub) {
577
+ if (!sub) return 0;
568
578
  let count = 0;
569
579
  let pos = text.indexOf(sub, 0);
570
580
  while (pos !== -1) {
571
581
  count++;
572
- pos = text.indexOf(sub, pos + 1);
582
+ pos = text.indexOf(sub, pos + sub.length);
573
583
  }
574
584
  return count;
575
585
  }
@@ -588,10 +598,46 @@ function buildContentRange(content) {
588
598
  }
589
599
  return content;
590
600
  }
591
- async function replaceMarkdown(client, pageId, newMarkdown) {
601
+ async function searchAndReplace(client, pageId, updates, options) {
602
+ await client.pages.updateMarkdown({
603
+ page_id: pageId,
604
+ type: "update_content",
605
+ update_content: {
606
+ content_updates: updates.map((u) => ({
607
+ old_str: u.oldStr,
608
+ new_str: u.newStr,
609
+ ...options?.replaceAll && { replace_all_matches: true }
610
+ })),
611
+ ...options?.allowDeletingContent && { allow_deleting_content: true }
612
+ }
613
+ });
614
+ }
615
+ async function replacePageContent(client, pageId, newContent, options) {
616
+ await client.pages.updateMarkdown({
617
+ page_id: pageId,
618
+ type: "replace_content",
619
+ replace_content: {
620
+ new_str: newContent,
621
+ ...options?.allowDeletingContent && { allow_deleting_content: true }
622
+ }
623
+ });
624
+ }
625
+ async function replaceMarkdown(client, pageId, newMarkdown, options) {
592
626
  const current = await client.pages.retrieveMarkdown({ page_id: pageId });
593
627
  const currentContent = current.markdown.trim();
628
+ if (current.truncated && !options?.range) {
629
+ throw new CliError(
630
+ ErrorCodes.API_ERROR,
631
+ "Page content is too large for full-page replace (markdown was truncated by the API).",
632
+ "Use --range to replace a specific section instead."
633
+ );
634
+ }
594
635
  if (!currentContent) {
636
+ if (options?.range) {
637
+ process.stderr.write(
638
+ "Warning: page is empty, --range ignored, content inserted as-is.\n"
639
+ );
640
+ }
595
641
  if (newMarkdown.trim()) {
596
642
  await client.pages.updateMarkdown({
597
643
  page_id: pageId,
@@ -601,13 +647,15 @@ async function replaceMarkdown(client, pageId, newMarkdown) {
601
647
  }
602
648
  return;
603
649
  }
650
+ const contentRange = options?.range ?? buildContentRange(currentContent);
651
+ const allowDeletingContent = options?.allowDeletingContent ?? options?.range == null;
604
652
  await client.pages.updateMarkdown({
605
653
  page_id: pageId,
606
654
  type: "replace_content_range",
607
655
  replace_content_range: {
608
656
  content: newMarkdown,
609
- content_range: buildContentRange(currentContent),
610
- allow_deleting_content: true
657
+ content_range: contentRange,
658
+ allow_deleting_content: allowDeletingContent
611
659
  }
612
660
  });
613
661
  }
@@ -621,10 +669,11 @@ async function createPage(client, parentId, title, markdown) {
621
669
  },
622
670
  ...markdown.trim() ? { markdown } : {}
623
671
  });
624
- return response.url;
672
+ const url = "url" in response ? response.url : response.id;
673
+ return url;
625
674
  }
626
675
 
627
- // src/commands/append.ts
676
+ // src/utils/stdin.ts
628
677
  async function readStdin() {
629
678
  const chunks = [];
630
679
  for await (const chunk of process.stdin) {
@@ -632,34 +681,58 @@ async function readStdin() {
632
681
  }
633
682
  return Buffer.concat(chunks).toString("utf-8");
634
683
  }
684
+
685
+ // src/commands/append.ts
635
686
  function appendCommand() {
636
687
  const cmd = new Command("append");
637
- cmd.description("append markdown content to a Notion page").argument("<id/url>", "Notion page ID or URL").option("-m, --message <markdown>", "markdown content to append").action(
638
- withErrorHandling(async (idOrUrl, opts) => {
639
- const { token, source } = await resolveToken();
640
- reportTokenSource(source);
641
- const client = createNotionClient(token);
642
- let markdown = "";
643
- if (opts.message) {
644
- markdown = opts.message;
645
- } else if (!process.stdin.isTTY) {
646
- markdown = await readStdin();
647
- } else {
648
- throw new CliError(
649
- ErrorCodes.INVALID_ARG,
650
- "No content to append.",
651
- "Pass markdown via -m/--message or pipe it through stdin"
652
- );
653
- }
654
- if (!markdown.trim()) {
655
- process.stdout.write("Nothing to append.\n");
656
- return;
688
+ cmd.description("append markdown content to a Notion page").argument("<id/url>", "Notion page ID or URL").option("-m, --message <markdown>", "markdown content to append").option(
689
+ "--after <selector>",
690
+ 'insert after matched content \u2014 ellipsis selector, e.g. "## Section...end of section"'
691
+ ).action(
692
+ withErrorHandling(
693
+ async (idOrUrl, opts) => {
694
+ const { token, source } = await resolveToken();
695
+ reportTokenSource(source);
696
+ const client = createNotionClient(token);
697
+ let markdown = "";
698
+ if (opts.message) {
699
+ markdown = opts.message;
700
+ } else if (!process.stdin.isTTY) {
701
+ markdown = await readStdin();
702
+ } else {
703
+ throw new CliError(
704
+ ErrorCodes.INVALID_ARG,
705
+ "No content to append.",
706
+ "Pass markdown via -m/--message or pipe it through stdin"
707
+ );
708
+ }
709
+ if (!markdown.trim()) {
710
+ process.stdout.write("Nothing to append.\n");
711
+ return;
712
+ }
713
+ const pageId = parseNotionId(idOrUrl);
714
+ const uuid = toUuid(pageId);
715
+ try {
716
+ await appendMarkdown(
717
+ client,
718
+ uuid,
719
+ markdown,
720
+ opts.after ? { after: opts.after } : void 0
721
+ );
722
+ } catch (error2) {
723
+ if (opts.after && isNotionValidationError(error2)) {
724
+ throw new CliError(
725
+ ErrorCodes.INVALID_ARG,
726
+ `Selector not found: "${opts.after}". ${error2.message}`,
727
+ SELECTOR_HINT,
728
+ error2
729
+ );
730
+ }
731
+ throw error2;
732
+ }
733
+ process.stdout.write("Appended.\n");
657
734
  }
658
- const pageId = parseNotionId(idOrUrl);
659
- const uuid = toUuid(pageId);
660
- await appendMarkdown(client, uuid, markdown);
661
- process.stdout.write("Appended.\n");
662
- })
735
+ )
663
736
  );
664
737
  return cmd;
665
738
  }
@@ -1379,13 +1452,6 @@ function completionCommand() {
1379
1452
 
1380
1453
  // src/commands/create-page.ts
1381
1454
  import { Command as Command8 } from "commander";
1382
- async function readStdin2() {
1383
- const chunks = [];
1384
- for await (const chunk of process.stdin) {
1385
- chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
1386
- }
1387
- return Buffer.concat(chunks).toString("utf-8");
1388
- }
1389
1455
  function createPageCommand() {
1390
1456
  const cmd = new Command8("create-page");
1391
1457
  cmd.description("create a new Notion page under a parent page").requiredOption("--parent <id/url>", "parent page ID or URL").requiredOption("--title <title>", "page title").option(
@@ -1401,7 +1467,7 @@ function createPageCommand() {
1401
1467
  if (opts.message) {
1402
1468
  markdown = opts.message;
1403
1469
  } else if (!process.stdin.isTTY) {
1404
- markdown = await readStdin2();
1470
+ markdown = await readStdin();
1405
1471
  }
1406
1472
  const parentUuid = toUuid(parseNotionId(opts.parent));
1407
1473
  const url = await createPage(
@@ -1695,47 +1761,101 @@ function dbSchemaCommand() {
1695
1761
 
1696
1762
  // src/commands/edit-page.ts
1697
1763
  import { Command as Command11 } from "commander";
1698
- async function readStdin3() {
1699
- const chunks = [];
1700
- for await (const chunk of process.stdin) {
1701
- chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
1702
- }
1703
- return Buffer.concat(chunks).toString("utf-8");
1764
+ function collect2(val, acc) {
1765
+ acc.push(val);
1766
+ return acc;
1704
1767
  }
1705
1768
  function editPageCommand() {
1706
1769
  const cmd = new Command11("edit-page");
1707
1770
  cmd.description(
1708
- "replace the entire content of a Notion page with new markdown"
1771
+ "replace a Notion page's content \u2014 full page or a targeted section"
1709
1772
  ).argument("<id/url>", "Notion page ID or URL").option(
1710
1773
  "-m, --message <markdown>",
1711
1774
  "new markdown content for the page body"
1775
+ ).option(
1776
+ "--find <text>",
1777
+ "text to find (repeatable, pair with --replace)",
1778
+ collect2,
1779
+ []
1780
+ ).option(
1781
+ "--replace <text>",
1782
+ "replacement text (repeatable, pair with --find)",
1783
+ collect2,
1784
+ []
1785
+ ).option("--all", "replace all matches of each --find pattern").option(
1786
+ "--range <selector>",
1787
+ '[deprecated] ellipsis selector to replace only a section, e.g. "## My Section...last line"'
1788
+ ).option(
1789
+ "--allow-deleting-content",
1790
+ "allow deletion of child pages/databases"
1712
1791
  ).action(
1713
1792
  withErrorHandling(async (idOrUrl, opts) => {
1714
1793
  const { token, source } = await resolveToken();
1715
1794
  reportTokenSource(source);
1716
1795
  const client = createNotionClient(token);
1796
+ const pageId = parseNotionId(idOrUrl);
1797
+ const uuid = toUuid(pageId);
1798
+ if (opts.find.length > 0) {
1799
+ if (opts.find.length !== opts.replace.length) {
1800
+ throw new CliError(
1801
+ ErrorCodes.INVALID_ARG,
1802
+ `Mismatched --find/--replace: got ${opts.find.length} --find and ${opts.replace.length} --replace flags.`,
1803
+ "Provide the same number of --find and --replace flags, paired by position."
1804
+ );
1805
+ }
1806
+ const updates = opts.find.map((oldStr, i) => ({
1807
+ oldStr,
1808
+ newStr: opts.replace[i]
1809
+ }));
1810
+ await searchAndReplace(client, uuid, updates, {
1811
+ replaceAll: opts.all ?? false,
1812
+ allowDeletingContent: opts.allowDeletingContent ?? false
1813
+ });
1814
+ process.stdout.write("Page content updated.\n");
1815
+ return;
1816
+ }
1717
1817
  let markdown = "";
1718
1818
  if (opts.message) {
1719
1819
  markdown = opts.message;
1720
1820
  } else if (!process.stdin.isTTY) {
1721
- markdown = await readStdin3();
1821
+ markdown = await readStdin();
1722
1822
  if (!markdown.trim()) {
1723
1823
  throw new CliError(
1724
1824
  ErrorCodes.INVALID_ARG,
1725
1825
  "No content provided (stdin was empty).",
1726
- "Pass markdown via -m/--message or pipe non-empty content through stdin"
1826
+ "Pass content via -m/--message for full replacement, --find/--replace for targeted edits, or pipe content through stdin"
1727
1827
  );
1728
1828
  }
1729
1829
  } else {
1730
1830
  throw new CliError(
1731
1831
  ErrorCodes.INVALID_ARG,
1732
1832
  "No content provided.",
1733
- "Pass markdown via -m/--message or pipe it through stdin"
1833
+ "Pass content via -m/--message for full replacement, --find/--replace for targeted edits, or pipe content through stdin"
1734
1834
  );
1735
1835
  }
1736
- const pageId = parseNotionId(idOrUrl);
1737
- const uuid = toUuid(pageId);
1738
- await replaceMarkdown(client, uuid, markdown);
1836
+ if (opts.range) {
1837
+ try {
1838
+ await replaceMarkdown(client, uuid, markdown, {
1839
+ range: opts.range,
1840
+ allowDeletingContent: opts.allowDeletingContent ?? false
1841
+ });
1842
+ } catch (error2) {
1843
+ if (isNotionValidationError(error2)) {
1844
+ throw new CliError(
1845
+ ErrorCodes.INVALID_ARG,
1846
+ `Selector not found: "${opts.range}". ${error2.message}`,
1847
+ SELECTOR_HINT,
1848
+ error2
1849
+ );
1850
+ }
1851
+ throw error2;
1852
+ }
1853
+ process.stdout.write("Page content replaced.\n");
1854
+ return;
1855
+ }
1856
+ await replacePageContent(client, uuid, markdown, {
1857
+ allowDeletingContent: opts.allowDeletingContent ?? false
1858
+ });
1739
1859
  process.stdout.write("Page content replaced.\n");
1740
1860
  })
1741
1861
  );
@@ -2268,8 +2388,183 @@ function searchCommand() {
2268
2388
  return cmd;
2269
2389
  }
2270
2390
 
2271
- // src/commands/users.ts
2391
+ // src/commands/update.ts
2272
2392
  import { Command as Command20 } from "commander";
2393
+
2394
+ // src/services/update.service.ts
2395
+ var UNSUPPORTED_TYPES = /* @__PURE__ */ new Set([
2396
+ "relation",
2397
+ "formula",
2398
+ "rollup",
2399
+ "created_time",
2400
+ "created_by",
2401
+ "last_edited_time",
2402
+ "last_edited_by",
2403
+ "files",
2404
+ "unique_id",
2405
+ "verification",
2406
+ "button"
2407
+ ]);
2408
+ function buildPropertyUpdate(propName, propType, value) {
2409
+ if (UNSUPPORTED_TYPES.has(propType)) {
2410
+ throw new CliError(
2411
+ ErrorCodes.INVALID_ARG,
2412
+ `Property "${propName}" has type "${propType}" which cannot be set via the CLI.`,
2413
+ "Supported types: title, rich_text, select, status, multi_select, number, checkbox, url, email, phone_number, date"
2414
+ );
2415
+ }
2416
+ if (value === "") {
2417
+ return null;
2418
+ }
2419
+ switch (propType) {
2420
+ case "title":
2421
+ return { title: [{ type: "text", text: { content: value } }] };
2422
+ case "rich_text":
2423
+ return { rich_text: [{ type: "text", text: { content: value } }] };
2424
+ case "select":
2425
+ return { select: { name: value } };
2426
+ case "status":
2427
+ return { status: { name: value } };
2428
+ case "multi_select":
2429
+ return {
2430
+ multi_select: value.split(",").map((v) => v.trim()).filter(Boolean).map((v) => ({ name: v }))
2431
+ };
2432
+ case "number": {
2433
+ const n = Number(value);
2434
+ if (Number.isNaN(n)) {
2435
+ throw new CliError(
2436
+ ErrorCodes.INVALID_ARG,
2437
+ `Invalid number value "${value}" for property "${propName}".`,
2438
+ 'Provide a numeric value, e.g. --prop "Count=42"'
2439
+ );
2440
+ }
2441
+ return { number: n };
2442
+ }
2443
+ case "checkbox": {
2444
+ const lower = value.toLowerCase();
2445
+ return { checkbox: lower === "true" || lower === "yes" };
2446
+ }
2447
+ case "url":
2448
+ return { url: value };
2449
+ case "email":
2450
+ return { email: value };
2451
+ case "phone_number":
2452
+ return { phone_number: value };
2453
+ case "date": {
2454
+ const parts = value.split(",");
2455
+ const start = parts[0].trim();
2456
+ const end = parts[1]?.trim();
2457
+ return { date: end ? { start, end } : { start } };
2458
+ }
2459
+ default:
2460
+ throw new CliError(
2461
+ ErrorCodes.INVALID_ARG,
2462
+ `Property "${propName}" has unsupported type "${propType}".`,
2463
+ "Supported types: title, rich_text, select, status, multi_select, number, checkbox, url, email, phone_number, date"
2464
+ );
2465
+ }
2466
+ }
2467
+ function buildPropertiesPayload(propStrings, page) {
2468
+ const result = {};
2469
+ for (const propString of propStrings) {
2470
+ const eqIdx = propString.indexOf("=");
2471
+ if (eqIdx === -1) {
2472
+ throw new CliError(
2473
+ ErrorCodes.INVALID_ARG,
2474
+ `Invalid --prop value: "${propString}". Expected format: "PropertyName=Value".`,
2475
+ 'Example: --prop "Status=Done"'
2476
+ );
2477
+ }
2478
+ const propName = propString.slice(0, eqIdx).trim();
2479
+ const value = propString.slice(eqIdx + 1);
2480
+ const schemaProp = page.properties[propName];
2481
+ if (!schemaProp) {
2482
+ const available = Object.keys(page.properties).join(", ");
2483
+ throw new CliError(
2484
+ ErrorCodes.INVALID_ARG,
2485
+ `Property "${propName}" not found on this page.`,
2486
+ `Available properties: ${available}`
2487
+ );
2488
+ }
2489
+ const propType = schemaProp.type;
2490
+ const payload = buildPropertyUpdate(propName, propType, value);
2491
+ result[propName] = payload;
2492
+ }
2493
+ return result;
2494
+ }
2495
+ async function updatePageProperties(client, pageId, properties) {
2496
+ const response = await client.pages.update({
2497
+ page_id: pageId,
2498
+ properties
2499
+ });
2500
+ return response;
2501
+ }
2502
+
2503
+ // src/commands/update.ts
2504
+ function collectProps(val, acc) {
2505
+ acc.push(val);
2506
+ return acc;
2507
+ }
2508
+ function updateCommand() {
2509
+ const cmd = new Command20("update");
2510
+ cmd.description("update properties on a Notion page").argument("<id/url>", "Notion page ID or URL").option(
2511
+ "--prop <property=value>",
2512
+ "set a property value (repeatable)",
2513
+ collectProps,
2514
+ []
2515
+ ).option("--title <title>", "set the page title").action(
2516
+ withErrorHandling(async (idOrUrl, opts) => {
2517
+ if (opts.title === void 0 && opts.prop.length === 0) {
2518
+ throw new CliError(
2519
+ ErrorCodes.INVALID_ARG,
2520
+ "No properties to update.",
2521
+ 'Provide at least one --prop "Name=Value" or --title "New Title"'
2522
+ );
2523
+ }
2524
+ const { token, source } = await resolveToken();
2525
+ reportTokenSource(source);
2526
+ const client = createNotionClient(token);
2527
+ const id = parseNotionId(idOrUrl);
2528
+ const uuid = toUuid(id);
2529
+ const page = await client.pages.retrieve({
2530
+ page_id: uuid
2531
+ });
2532
+ const properties = buildPropertiesPayload(opts.prop, page);
2533
+ if (opts.title !== void 0) {
2534
+ const titleEntry = Object.entries(page.properties).find(
2535
+ ([, prop]) => prop.type === "title"
2536
+ );
2537
+ if (!titleEntry) {
2538
+ throw new CliError(
2539
+ ErrorCodes.INVALID_ARG,
2540
+ "This page has no title property.",
2541
+ "Use --prop to set properties by name instead"
2542
+ );
2543
+ }
2544
+ const [titlePropName] = titleEntry;
2545
+ properties[titlePropName] = {
2546
+ title: [{ type: "text", text: { content: opts.title } }]
2547
+ };
2548
+ }
2549
+ const updatedPage = await updatePageProperties(
2550
+ client,
2551
+ uuid,
2552
+ properties
2553
+ );
2554
+ const mode = getOutputMode();
2555
+ if (mode === "json") {
2556
+ process.stdout.write(`${formatJSON(updatedPage)}
2557
+ `);
2558
+ } else {
2559
+ process.stdout.write("Page updated.\n");
2560
+ }
2561
+ })
2562
+ );
2563
+ return cmd;
2564
+ }
2565
+
2566
+ // src/commands/users.ts
2567
+ import { Command as Command21 } from "commander";
2273
2568
  function getEmailOrWorkspace(user) {
2274
2569
  if (user.type === "person") {
2275
2570
  return user.person.email ?? "\u2014";
@@ -2281,7 +2576,7 @@ function getEmailOrWorkspace(user) {
2281
2576
  return "\u2014";
2282
2577
  }
2283
2578
  function usersCommand() {
2284
- const cmd = new Command20("users");
2579
+ const cmd = new Command21("users");
2285
2580
  cmd.description("list all users in the workspace").option("--json", "output as JSON").action(
2286
2581
  withErrorHandling(async (opts) => {
2287
2582
  if (opts.json) setOutputMode("json");
@@ -2312,7 +2607,7 @@ var __dirname = dirname(__filename);
2312
2607
  var pkg = JSON.parse(
2313
2608
  readFileSync(join3(__dirname, "../package.json"), "utf-8")
2314
2609
  );
2315
- var program = new Command21();
2610
+ var program = new Command22();
2316
2611
  program.name("notion").description("Notion CLI \u2014 read Notion pages and databases from the terminal").version(pkg.version);
2317
2612
  program.option("--verbose", "show API requests/responses").option("--color", "force color output").option("--json", "force JSON output (overrides TTY detection)").option("--md", "force markdown output for page content");
2318
2613
  program.configureOutput({
@@ -2333,7 +2628,7 @@ program.hook("preAction", (thisCommand) => {
2333
2628
  setOutputMode("md");
2334
2629
  }
2335
2630
  });
2336
- var authCmd = new Command21("auth").description("manage Notion authentication");
2631
+ var authCmd = new Command22("auth").description("manage Notion authentication");
2337
2632
  authCmd.action(authDefaultAction(authCmd));
2338
2633
  authCmd.addCommand(loginCommand());
2339
2634
  authCmd.addCommand(logoutCommand());
@@ -2343,7 +2638,7 @@ authCmd.addCommand(profileUseCommand());
2343
2638
  authCmd.addCommand(profileRemoveCommand());
2344
2639
  program.addCommand(authCmd);
2345
2640
  program.addCommand(initCommand(), { hidden: true });
2346
- var profileCmd = new Command21("profile").description(
2641
+ var profileCmd = new Command22("profile").description(
2347
2642
  "manage authentication profiles"
2348
2643
  );
2349
2644
  profileCmd.addCommand(profileListCommand());
@@ -2360,7 +2655,8 @@ program.addCommand(commentAddCommand());
2360
2655
  program.addCommand(appendCommand());
2361
2656
  program.addCommand(createPageCommand());
2362
2657
  program.addCommand(editPageCommand());
2363
- var dbCmd = new Command21("db").description("Database operations");
2658
+ program.addCommand(updateCommand());
2659
+ var dbCmd = new Command22("db").description("Database operations");
2364
2660
  dbCmd.addCommand(dbSchemaCommand());
2365
2661
  dbCmd.addCommand(dbQueryCommand());
2366
2662
  program.addCommand(dbCmd);