im-nobsidian 0.4.1 โ†’ 0.9.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.
package/dist/index.js CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { createRequire } from "module";
5
- import { Command as Command12 } from "commander";
5
+ import { Command as Command13 } from "commander";
6
6
 
7
7
  // src/commands/init.ts
8
8
  import { Command } from "commander";
9
9
  import {
10
- ConfigManager,
10
+ ConfigManager as ConfigManager2,
11
11
  NOTION_TOKEN_PREFIX,
12
12
  NotionClient,
13
13
  notionIdFromInput
@@ -111,14 +111,69 @@ function failedItem(path, error, direction) {
111
111
  console.log(` ${icons.fail} ${where}: ${chalk.red(error)}`);
112
112
  }
113
113
 
114
+ // src/utils/vault.ts
115
+ import { existsSync } from "fs";
116
+ import { dirname, join, relative } from "path";
117
+ import {
118
+ ConfigManager,
119
+ NodeVaultFS,
120
+ StateDB,
121
+ matchesPathScope,
122
+ ScopedVaultFS,
123
+ findConnectionFolders,
124
+ OBSIDIAN_CONFIG_DIR
125
+ } from "@im-nobsidian/core";
126
+ async function nestedConnections(cwd) {
127
+ return (await findConnectionFolders(cwd)).filter((folder) => folder !== "");
128
+ }
129
+ async function openVaultFs(cwd, paths) {
130
+ const base = new NodeVaultFS(cwd, paths);
131
+ const nested = await nestedConnections(cwd);
132
+ return nested.length === 0 ? base : new ScopedVaultFS(base, "", ["", ...nested]);
133
+ }
134
+ function obsidianConfigDir(cwd) {
135
+ for (let dir = cwd; ; dir = dirname(dir)) {
136
+ if (existsSync(join(dir, OBSIDIAN_CONFIG_DIR))) {
137
+ return relative(cwd, join(dir, OBSIDIAN_CONFIG_DIR)).split("\\").join("/");
138
+ }
139
+ if (dirname(dir) === dir) return OBSIDIAN_CONFIG_DIR;
140
+ }
141
+ }
142
+ function outerConnectionTracking(cwd) {
143
+ for (let dir = dirname(cwd); ; dir = dirname(dir)) {
144
+ const manager = new ConfigManager(dir);
145
+ if (existsSync(manager.dbPath)) {
146
+ const scope = relative(dir, cwd).split("\\").join("/");
147
+ const db = StateDB.open(manager.dbPath);
148
+ try {
149
+ const notes = db.getAll().filter(
150
+ (r) => r.obsidianPath !== scope && matchesPathScope(r.obsidianPath, scope)
151
+ ).length;
152
+ return notes > 0 ? { folder: dir, notes } : null;
153
+ } finally {
154
+ db.close();
155
+ }
156
+ }
157
+ if (dirname(dir) === dir) return null;
158
+ }
159
+ }
160
+
114
161
  // src/commands/init.ts
115
162
  var ROOT_PAGE_UNREADABLE = "\uB8E8\uD2B8 \uD398\uC774\uC9C0\uC5D0\uC11C Notion \uD398\uC774\uC9C0 ID(32\uC790\uB9AC)\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4 \u2014 \uD398\uC774\uC9C0 \uC8FC\uC18C\uB098 ID \uB97C \uB123\uC73C\uC138\uC694.";
116
163
  var initCommand = new Command("init").description("Im-Nobsidian \uCD08\uAE30 \uC124\uC815").option("--token <token>", "Notion Integration Token").option("--root-page-id <id>", "\uB8E8\uD2B8 \uD398\uC774\uC9C0 ID \uB610\uB294 \uD398\uC774\uC9C0 \uC8FC\uC18C").option("--non-interactive", "\uBE44\uB300\uD654\uD615 \uBAA8\uB4DC").action(async (options) => {
117
164
  const cwd = process.cwd();
118
- const configManager = new ConfigManager(cwd);
165
+ const configManager = new ConfigManager2(cwd);
119
166
  console.log(`
120
167
  ${header(chalk2.cyan(" Im-Nobsidian Setup"))}`);
121
168
  console.log(` ${separator()}`);
169
+ const outer = outerConnectionTracking(cwd);
170
+ if (outer) {
171
+ console.error(
172
+ `\uC774 \uD3F4\uB354\uB294 \uC774\uBBF8 ${outer.folder} \uC758 \uC5F0\uACB0\uC774 \uB178\uD2B8 ${outer.notes}\uAC1C\uB97C \uB3D9\uAE30\uD654\uD558\uACE0 \uC788\uC5B4 \uB530\uB85C \uC5F0\uACB0\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`
173
+ );
174
+ process.exitCode = 1;
175
+ return;
176
+ }
122
177
  if (options.nonInteractive) {
123
178
  if (!options.token || !options.rootPageId) {
124
179
  console.error("\uBE44\uB300\uD654\uD615 \uBAA8\uB4DC\uC5D0\uC11C\uB294 --token\uACFC --root-page-id\uAC00 \uD544\uC218\uC785\uB2C8\uB2E4.");
@@ -223,13 +278,7 @@ function extractPageTitle(page) {
223
278
 
224
279
  // src/commands/push.ts
225
280
  import { Command as Command2 } from "commander";
226
- import {
227
- ConfigManager as ConfigManager2,
228
- StateDB,
229
- NotionClient as NotionClient2,
230
- SyncOrchestrator,
231
- NodeVaultFS
232
- } from "@im-nobsidian/core";
281
+ import { ConfigManager as ConfigManager3, StateDB as StateDB2, NotionClient as NotionClient2, SyncOrchestrator } from "@im-nobsidian/core";
233
282
 
234
283
  // src/utils/json-output.ts
235
284
  import { setLogger } from "@im-nobsidian/core";
@@ -244,7 +293,8 @@ function pushJson(result) {
244
293
  updated: result.updated,
245
294
  deleted: result.deleted,
246
295
  moved: result.moved,
247
- churn: result.created + result.updated + result.deleted + result.moved,
296
+ churn: result.created + result.updated + result.deleted + result.moved + (result.createdDatabases ?? 0),
297
+ createdDatabases: result.createdDatabases ?? 0,
248
298
  failed: result.failed,
249
299
  durationMs: result.duration
250
300
  };
@@ -276,12 +326,12 @@ var pushCommand = new Command2("push").description("\uB85C\uCEEC \uBCC0\uACBD\uC
276
326
  const json = options.json === true;
277
327
  if (json) enableJsonMode();
278
328
  const cwd = process.cwd();
279
- const configManager = new ConfigManager2(cwd);
329
+ const configManager = new ConfigManager3(cwd);
280
330
  const config = await configManager.load();
281
- const stateDb = StateDB.open(configManager.dbPath);
331
+ const stateDb = StateDB2.open(configManager.dbPath);
282
332
  try {
283
333
  const client = NotionClient2.fromConfig(config);
284
- const vaultFs = new NodeVaultFS(cwd, config.paths);
334
+ const vaultFs = await openVaultFs(cwd, config.paths);
285
335
  const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);
286
336
  if (!json) {
287
337
  console.log(`
@@ -310,6 +360,9 @@ ${header(" Pushing to Notion...")}`);
310
360
  console.log(
311
361
  ` ${summary(result.created, result.updated, result.deleted, result.moved)} ${failedCountText(failedCount)}`
312
362
  );
363
+ if (result.createdDatabases) {
364
+ console.log(` ${options.dryRun ? "\uB9CC\uB4E4 DB" : "\uB9CC\uB4E0 DB"} ${result.createdDatabases}`);
365
+ }
313
366
  console.log(` ${duration(result.duration)}`);
314
367
  if (failedCount > 0) {
315
368
  console.log("");
@@ -325,13 +378,7 @@ ${header(" Pushing to Notion...")}`);
325
378
 
326
379
  // src/commands/pull.ts
327
380
  import { Command as Command3, Option } from "commander";
328
- import {
329
- ConfigManager as ConfigManager3,
330
- StateDB as StateDB2,
331
- NotionClient as NotionClient3,
332
- SyncOrchestrator as SyncOrchestrator2,
333
- NodeVaultFS as NodeVaultFS2
334
- } from "@im-nobsidian/core";
381
+ import { ConfigManager as ConfigManager4, StateDB as StateDB3, NotionClient as NotionClient3, SyncOrchestrator as SyncOrchestrator2 } from "@im-nobsidian/core";
335
382
  import chalk3 from "chalk";
336
383
  var pullCommand = new Command3("pull").description("Notion \uBCC0\uACBD\uC0AC\uD56D\uC744 \uB85C\uCEEC\uC5D0 \uBC18\uC601").option("--dry-run", "\uC2E4\uC81C \uBC18\uC601 \uC5C6\uC774 \uBCC0\uACBD\uC0AC\uD56D\uB9CC \uD45C\uC2DC").option("-p, --path <paths...>", "\uD2B9\uC815 \uACBD\uB85C\uB9CC pull").option(
337
384
  "--force",
@@ -345,12 +392,12 @@ var pullCommand = new Command3("pull").description("Notion \uBCC0\uACBD\uC0AC\uD
345
392
  const json = options.json === true;
346
393
  if (json) enableJsonMode();
347
394
  const cwd = process.cwd();
348
- const configManager = new ConfigManager3(cwd);
395
+ const configManager = new ConfigManager4(cwd);
349
396
  const config = await configManager.load();
350
- const stateDb = StateDB2.open(configManager.dbPath);
397
+ const stateDb = StateDB3.open(configManager.dbPath);
351
398
  try {
352
399
  const client = NotionClient3.fromConfig(config);
353
- const vaultFs = new NodeVaultFS2(cwd, config.paths);
400
+ const vaultFs = await openVaultFs(cwd, config.paths);
354
401
  const orchestrator = new SyncOrchestrator2(config, stateDb, client, vaultFs);
355
402
  if (!json) {
356
403
  console.log(`
@@ -423,13 +470,7 @@ ${header(" Pulling from Notion...")}`);
423
470
 
424
471
  // src/commands/sync.ts
425
472
  import { Command as Command4 } from "commander";
426
- import {
427
- ConfigManager as ConfigManager4,
428
- StateDB as StateDB3,
429
- NotionClient as NotionClient4,
430
- SyncOrchestrator as SyncOrchestrator3,
431
- NodeVaultFS as NodeVaultFS3
432
- } from "@im-nobsidian/core";
473
+ import { ConfigManager as ConfigManager5, StateDB as StateDB4, NotionClient as NotionClient4, SyncOrchestrator as SyncOrchestrator3 } from "@im-nobsidian/core";
433
474
  import chalk4 from "chalk";
434
475
  var syncCommand = new Command4("sync").description("\uC591\uBC29\uD5A5 \uB3D9\uAE30\uD654 (Pull \u2192 Push)").option("--dry-run", "\uC2E4\uC81C \uBC18\uC601 \uC5C6\uC774 \uBCC0\uACBD\uC0AC\uD56D\uB9CC \uD45C\uC2DC").option("--json", "\uACB0\uACFC\uB97C JSON \uD55C \uC904\uB85C \uCD9C\uB825 (\uC790\uB3D9\uD654\uC6A9 \u2014 \uB85C\uADF8\uB294 stderr)").action(async (options) => {
435
476
  const json = options.json === true;
@@ -437,12 +478,12 @@ var syncCommand = new Command4("sync").description("\uC591\uBC29\uD5A5 \uB3D9\uA
437
478
  const log = json ? () => {
438
479
  } : (line) => console.log(line);
439
480
  const cwd = process.cwd();
440
- const configManager = new ConfigManager4(cwd);
481
+ const configManager = new ConfigManager5(cwd);
441
482
  const config = await configManager.load();
442
- const stateDb = StateDB3.open(configManager.dbPath);
483
+ const stateDb = StateDB4.open(configManager.dbPath);
443
484
  try {
444
485
  const client = NotionClient4.fromConfig(config);
445
- const vaultFs = new NodeVaultFS3(cwd, config.paths);
486
+ const vaultFs = await openVaultFs(cwd, config.paths);
446
487
  const orchestrator = new SyncOrchestrator3(config, stateDb, client, vaultFs);
447
488
  log(`
448
489
  ${header(" Bidirectional Sync")}`);
@@ -523,22 +564,21 @@ ${header(" Bidirectional Sync")}`);
523
564
  // src/commands/status.ts
524
565
  import { Command as Command5 } from "commander";
525
566
  import {
526
- ConfigManager as ConfigManager5,
527
- StateDB as StateDB4,
567
+ ConfigManager as ConfigManager6,
568
+ StateDB as StateDB5,
528
569
  NotionClient as NotionClient5,
529
570
  SyncOrchestrator as SyncOrchestrator4,
530
- NodeVaultFS as NodeVaultFS4,
531
571
  isRemoteDeletion
532
572
  } from "@im-nobsidian/core";
533
573
  import chalk5 from "chalk";
534
574
  var statusCommand = new Command5("status").description("\uB3D9\uAE30\uD654 \uC0C1\uD0DC \uD655\uC778 (--full: \uC6D0\uACA9 \uBCC0\uACBD \uD3EC\uD568)").option("--full", "Notion \uC6D0\uACA9 \uBCC0\uACBD\uAE4C\uC9C0 \uC591\uBC29\uD5A5 \uD655\uC778 (API \uD638\uCD9C, \uB290\uB9BC)").action(async (opts) => {
535
575
  const cwd = process.cwd();
536
- const configManager = new ConfigManager5(cwd);
576
+ const configManager = new ConfigManager6(cwd);
537
577
  const config = await configManager.load();
538
- const stateDb = StateDB4.open(configManager.dbPath);
578
+ const stateDb = StateDB5.open(configManager.dbPath);
539
579
  try {
540
580
  const client = NotionClient5.fromConfig(config);
541
- const vaultFs = new NodeVaultFS4(cwd, config.paths);
581
+ const vaultFs = await openVaultFs(cwd, config.paths);
542
582
  const orchestrator = new SyncOrchestrator4(config, stateDb, client, vaultFs);
543
583
  const status = opts.full ? await orchestrator.status() : await orchestrator.statusLocal();
544
584
  console.log(`
@@ -554,6 +594,11 @@ ${header(" Sync Status")}`);
554
594
  console.log(` ${dimText("Last sync:")} ${chalk5.yellow("never")}`);
555
595
  }
556
596
  console.log(` ${dimText("Full scan:")} ${lastFullScanText(status.lastFullScanAt)}`);
597
+ for (const folder of await nestedConnections(cwd)) {
598
+ console.log(
599
+ ` ${dimText("Connection:")} ${folder}/ ${dimText(`(separate \u2014 cd ${folder} && nobsi status)`)}`
600
+ );
601
+ }
557
602
  const allRecords = stateDb.getAll();
558
603
  const total = allRecords.length;
559
604
  const conflictCount = status.conflictRecords.length;
@@ -716,14 +761,13 @@ ${header(" Sync Status")}`);
716
761
  // src/commands/diff.ts
717
762
  import { Command as Command6 } from "commander";
718
763
  import {
719
- ConfigManager as ConfigManager6,
720
- StateDB as StateDB5,
764
+ ConfigManager as ConfigManager7,
765
+ StateDB as StateDB6,
721
766
  NotionClient as NotionClient6,
722
767
  SyncOrchestrator as SyncOrchestrator5,
723
- NodeVaultFS as NodeVaultFS5,
724
768
  formatUnifiedDiff,
725
769
  lineDiff,
726
- matchesPathScope
770
+ matchesPathScope as matchesPathScope2
727
771
  } from "@im-nobsidian/core";
728
772
 
729
773
  // src/utils/diff-output.ts
@@ -750,15 +794,15 @@ var diffCommand = new Command6("diff").description(
750
794
  "\uB85C\uCEEC \uBCC0\uACBD\uC0AC\uD56D \uD45C\uC2DC (\uAE30\uBCF8: \uB9C8\uC9C0\uB9C9 \uB3D9\uAE30\uD654 \uC2DC\uC810\uACFC \uBE44\uAD50, --remote: Notion \uD604\uC7AC\uBCF8\uACFC \uBE44\uAD50)"
751
795
  ).argument("[path]", "\uD2B9\uC815 \uD30C\uC77C \uACBD\uB85C (\uC0DD\uB7B5 \uC2DC \uBCC0\uACBD\uB41C \uBAA8\uB4E0 \uD30C\uC77C)").option("--color", "\uCEEC\uB7EC \uCD9C\uB825", true).option("--remote", "Notion \uD604\uC7AC \uBCF8\uBB38\uACFC \uBE44\uAD50 (API \uD638\uCD9C, \uB290\uB9BC)").action(async (targetPath, options) => {
752
796
  const cwd = process.cwd();
753
- const configManager = new ConfigManager6(cwd);
797
+ const configManager = new ConfigManager7(cwd);
754
798
  const config = await configManager.load();
755
- const stateDb = StateDB5.open(configManager.dbPath);
799
+ const stateDb = StateDB6.open(configManager.dbPath);
756
800
  try {
757
801
  const client = NotionClient6.fromConfig(config);
758
- const vaultFs = new NodeVaultFS5(cwd, config.paths);
802
+ const vaultFs = await openVaultFs(cwd, config.paths);
759
803
  const orchestrator = new SyncOrchestrator5(config, stateDb, client, vaultFs);
760
804
  const status = await orchestrator.statusLocal();
761
- const changes = targetPath ? status.localChanges.filter((c) => matchesPathScope(c.path, targetPath)) : status.localChanges;
805
+ const changes = targetPath ? status.localChanges.filter((c) => matchesPathScope2(c.path, targetPath)) : status.localChanges;
762
806
  if (changes.length === 0) {
763
807
  console.log("\uBCC0\uACBD\uC0AC\uD56D \uC5C6\uC74C");
764
808
  return;
@@ -824,21 +868,15 @@ function printChangeDiff(diff, color) {
824
868
 
825
869
  // src/commands/discard.ts
826
870
  import { Command as Command7 } from "commander";
827
- import {
828
- ConfigManager as ConfigManager7,
829
- StateDB as StateDB6,
830
- NotionClient as NotionClient7,
831
- SyncOrchestrator as SyncOrchestrator6,
832
- NodeVaultFS as NodeVaultFS6
833
- } from "@im-nobsidian/core";
871
+ import { ConfigManager as ConfigManager8, StateDB as StateDB7, NotionClient as NotionClient7, SyncOrchestrator as SyncOrchestrator6 } from "@im-nobsidian/core";
834
872
  var discardCommand = new Command7("discard").description("\uB85C\uCEEC \uBCC0\uACBD\uC744 \uC9C0\uB09C \uB3D9\uAE30\uD654 \uB54C\uC758 \uAE00\uB85C \uB418\uB3CC\uB9AC\uAE30 (Notion \uC740 \uADF8\uB300\uB85C)").argument("<paths...>", "\uB418\uB3CC\uB9B4 \uB178\uD2B8 \uACBD\uB85C \u2014 status \uAC00 \uBCF4\uC778 \uBCFC\uD2B8 \uAE30\uC900 \uACBD\uB85C").action(async (paths) => {
835
873
  const cwd = process.cwd();
836
- const configManager = new ConfigManager7(cwd);
874
+ const configManager = new ConfigManager8(cwd);
837
875
  const config = await configManager.load();
838
- const stateDb = StateDB6.open(configManager.dbPath);
876
+ const stateDb = StateDB7.open(configManager.dbPath);
839
877
  try {
840
878
  const client = NotionClient7.fromConfig(config);
841
- const vaultFs = new NodeVaultFS6(cwd, config.paths);
879
+ const vaultFs = await openVaultFs(cwd, config.paths);
842
880
  const orchestrator = new SyncOrchestrator6(config, stateDb, client, vaultFs);
843
881
  let failed = 0;
844
882
  for (const path of paths) {
@@ -858,22 +896,16 @@ var discardCommand = new Command7("discard").description("\uB85C\uCEEC \uBCC0\uA
858
896
 
859
897
  // src/commands/fetch.ts
860
898
  import { Command as Command8 } from "commander";
861
- import {
862
- ConfigManager as ConfigManager8,
863
- StateDB as StateDB7,
864
- NotionClient as NotionClient8,
865
- SyncOrchestrator as SyncOrchestrator7,
866
- NodeVaultFS as NodeVaultFS7
867
- } from "@im-nobsidian/core";
899
+ import { ConfigManager as ConfigManager9, StateDB as StateDB8, NotionClient as NotionClient8, SyncOrchestrator as SyncOrchestrator7 } from "@im-nobsidian/core";
868
900
  import chalk6 from "chalk";
869
901
  var fetchCommand = new Command8("fetch").description("Notion \uB9AC\uBAA8\uD2B8 \uC0C1\uD0DC \uC2A4\uCE94 (\uC0AD\uC81C \uAC10\uC9C0 \uD3EC\uD568)").action(async () => {
870
902
  const cwd = process.cwd();
871
- const configManager = new ConfigManager8(cwd);
903
+ const configManager = new ConfigManager9(cwd);
872
904
  const config = await configManager.load();
873
- const stateDb = StateDB7.open(configManager.dbPath);
905
+ const stateDb = StateDB8.open(configManager.dbPath);
874
906
  try {
875
907
  const client = NotionClient8.fromConfig(config);
876
- const vaultFs = new NodeVaultFS7(cwd, config.paths);
908
+ const vaultFs = await openVaultFs(cwd, config.paths);
877
909
  const orchestrator = new SyncOrchestrator7(config, stateDb, client, vaultFs);
878
910
  console.log(dimText(" Scanning remote pages..."));
879
911
  const result = await orchestrator.fetch();
@@ -909,11 +941,10 @@ ${header(" Remote Scan")}`);
909
941
  import { Command as Command9 } from "commander";
910
942
  import { select, confirm as confirm2 } from "@inquirer/prompts";
911
943
  import {
912
- ConfigManager as ConfigManager9,
913
- StateDB as StateDB8,
944
+ ConfigManager as ConfigManager10,
945
+ StateDB as StateDB9,
914
946
  NotionClient as NotionClient9,
915
947
  SyncOrchestrator as SyncOrchestrator8,
916
- NodeVaultFS as NodeVaultFS8,
917
948
  applicableChoices,
918
949
  isRemoteDeletion as isRemoteDeletion2
919
950
  } from "@im-nobsidian/core";
@@ -960,13 +991,13 @@ var resolveCommand = new Command9("resolve").description("\uCDA9\uB3CC\uC744 \uD
960
991
  return;
961
992
  }
962
993
  const cwd = process.cwd();
963
- const configManager = new ConfigManager9(cwd);
994
+ const configManager = new ConfigManager10(cwd);
964
995
  let stateDb;
965
996
  try {
966
997
  const config = await configManager.load();
967
- stateDb = StateDB8.open(configManager.dbPath);
998
+ stateDb = StateDB9.open(configManager.dbPath);
968
999
  const client = NotionClient9.fromConfig(config);
969
- const vaultFs = new NodeVaultFS8(cwd, config.paths);
1000
+ const vaultFs = await openVaultFs(cwd, config.paths);
970
1001
  const orchestrator = new SyncOrchestrator8(config, stateDb, client, vaultFs);
971
1002
  const conflicts = await orchestrator.listConflicts();
972
1003
  if (conflicts.length === 0) {
@@ -1093,11 +1124,10 @@ ${"\u2500".repeat(60)}`);
1093
1124
  // src/commands/watch.ts
1094
1125
  import { Command as Command10 } from "commander";
1095
1126
  import {
1096
- ConfigManager as ConfigManager10,
1097
- StateDB as StateDB9,
1127
+ ConfigManager as ConfigManager11,
1128
+ StateDB as StateDB10,
1098
1129
  NotionClient as NotionClient10,
1099
1130
  SyncOrchestrator as SyncOrchestrator9,
1100
- NodeVaultFS as NodeVaultFS9,
1101
1131
  WatchSyncService,
1102
1132
  DEFAULT_WATCH_DEBOUNCE_MS
1103
1133
  } from "@im-nobsidian/core";
@@ -1131,11 +1161,11 @@ var watchCommand = new Command10("watch").description("\uD30C\uC77C \uBCC0\uACBD
1131
1161
  return;
1132
1162
  }
1133
1163
  const cwd = process.cwd();
1134
- const configManager = new ConfigManager10(cwd);
1164
+ const configManager = new ConfigManager11(cwd);
1135
1165
  const config = await configManager.load();
1136
- const stateDb = StateDB9.open(configManager.dbPath);
1166
+ const stateDb = StateDB10.open(configManager.dbPath);
1137
1167
  const client = NotionClient10.fromConfig(config);
1138
- const vaultFs = new NodeVaultFS9(cwd, config.paths);
1168
+ const vaultFs = await openVaultFs(cwd, config.paths);
1139
1169
  const orchestrator = new SyncOrchestrator9(config, stateDb, client, vaultFs);
1140
1170
  const service = new WatchSyncService(cwd, orchestrator, {
1141
1171
  debounceMs,
@@ -1187,13 +1217,7 @@ var watchCommand = new Command10("watch").description("\uD30C\uC77C \uBCC0\uACBD
1187
1217
 
1188
1218
  // src/commands/verify.ts
1189
1219
  import { Command as Command11 } from "commander";
1190
- import {
1191
- ConfigManager as ConfigManager11,
1192
- StateDB as StateDB10,
1193
- NotionClient as NotionClient11,
1194
- SyncOrchestrator as SyncOrchestrator10,
1195
- NodeVaultFS as NodeVaultFS10
1196
- } from "@im-nobsidian/core";
1220
+ import { ConfigManager as ConfigManager12, StateDB as StateDB11, NotionClient as NotionClient11, SyncOrchestrator as SyncOrchestrator10 } from "@im-nobsidian/core";
1197
1221
  import chalk7 from "chalk";
1198
1222
  var SAMPLE_LIMIT = 10;
1199
1223
  function printIdSample(label, ids, color) {
@@ -1284,12 +1308,12 @@ function printReport(report2) {
1284
1308
  }
1285
1309
  var verifyCommand = new Command11("verify").description("\uC644\uACB0\uC131 \uAC80\uC99D \u2014 \uC6D0\uACA9 DB \uD589\xB7\uD398\uC774\uC9C0\uAC00 \uBCFC\uD2B8\uC5D0 \uBE60\uC9D0\uC5C6\uC774 \uC788\uB294\uC9C0 \uB300\uC870 (API \uD638\uCD9C)").option("--json", "\uACB0\uACFC\uB97C JSON \uC73C\uB85C \uCD9C\uB825").action(async (opts) => {
1286
1310
  const cwd = process.cwd();
1287
- const configManager = new ConfigManager11(cwd);
1311
+ const configManager = new ConfigManager12(cwd);
1288
1312
  const config = await configManager.load();
1289
- const stateDb = StateDB10.open(configManager.dbPath);
1313
+ const stateDb = StateDB11.open(configManager.dbPath);
1290
1314
  try {
1291
1315
  const client = NotionClient11.fromConfig(config);
1292
- const vaultFs = new NodeVaultFS10(cwd, config.paths);
1316
+ const vaultFs = await openVaultFs(cwd, config.paths);
1293
1317
  const orchestrator = new SyncOrchestrator10(config, stateDb, client, vaultFs);
1294
1318
  const report2 = await orchestrator.verifyCompleteness();
1295
1319
  if (opts.json) {
@@ -1303,6 +1327,37 @@ var verifyCommand = new Command11("verify").description("\uC644\uACB0\uC131 \uAC
1303
1327
  }
1304
1328
  });
1305
1329
 
1330
+ // src/commands/prepare-db.ts
1331
+ import { Command as Command12 } from "commander";
1332
+ import { ConfigManager as ConfigManager13, StateDB as StateDB12, NotionClient as NotionClient12, SyncOrchestrator as SyncOrchestrator11 } from "@im-nobsidian/core";
1333
+ import chalk8 from "chalk";
1334
+ var prepareDbCommand = new Command12("prepare-db").description("\uBCFC\uD2B8\uC758 \uB178\uD2B8 \uD3F4\uB354\uB97C \uB2E4\uC74C push \uC5D0\uC11C Notion DB \uB85C \uB9CC\uB4E4\uB3C4\uB85D \uC900\uBE44 (Notion \uC740 \uADF8\uB300\uB85C)").argument("<folder>", "DB \uB85C \uB9CC\uB4E4 \uD3F4\uB354 \u2014 \uBCFC\uD2B8 \uAE30\uC900 \uACBD\uB85C. \uBC14\uB85C \uC544\uB798 \uB178\uD2B8\uAC00 \uD589\uC774 \uB41C\uB2E4").action(async (folder) => {
1335
+ const cwd = process.cwd();
1336
+ const configManager = new ConfigManager13(cwd);
1337
+ const config = await configManager.load();
1338
+ const stateDb = StateDB12.open(configManager.dbPath);
1339
+ try {
1340
+ const client = NotionClient12.fromConfig(config);
1341
+ const vaultFs = await openVaultFs(cwd, config.paths);
1342
+ const orchestrator = new SyncOrchestrator11(config, stateDb, client, vaultFs);
1343
+ const report2 = await orchestrator.prepareFolderDatabase(folder, obsidianConfigDir(cwd));
1344
+ console.log(` ${icons.success} ${report2.sidecarPath} \u2014 \uD589 ${report2.rows}\uAC1C`);
1345
+ for (const property of report2.properties) {
1346
+ console.log(` ${property.name}: ${property.type} ${chalk8.dim(`(${property.reason})`)}`);
1347
+ }
1348
+ if (report2.views.length > 0) console.log(` \uBCF4\uAE30: ${report2.views.join(", ")}`);
1349
+ for (const skip of report2.skipped) {
1350
+ console.log(chalk8.dim(` \uC62E\uAE30\uC9C0 \uC54A\uC74C \u2014 ${skip.what}: ${skip.reason}`));
1351
+ }
1352
+ console.log(chalk8.dim(" \uD0C0\uC785\uC744 \uBC14\uAFB8\uB824\uBA74 \uC0AC\uC774\uB4DC\uCE74\uB97C \uACE0\uCE5C \uB4A4 push \uD558\uC138\uC694."));
1353
+ } catch (error) {
1354
+ failedItem(folder, error instanceof Error ? error.message : String(error));
1355
+ process.exitCode = 1;
1356
+ } finally {
1357
+ stateDb.close();
1358
+ }
1359
+ });
1360
+
1306
1361
  // src/utils/exit.ts
1307
1362
  var FORCED_EXIT_DELAY_MS = 100;
1308
1363
  function resolveExitCode(exitCode) {
@@ -1320,7 +1375,7 @@ function scheduleForcedExit(proc, schedule = setTimeout) {
1320
1375
  // src/index.ts
1321
1376
  var requireJson = createRequire(import.meta.url);
1322
1377
  var { version } = requireJson("../package.json");
1323
- var program = new Command12();
1378
+ var program = new Command13();
1324
1379
  program.name("im-nobsidian").description("Im-Nobsidian: Obsidian \u2194 Notion \uC591\uBC29\uD5A5 \uB3D9\uAE30\uD654 CLI").version(version).option("--verbose", "\uC0C1\uC138 \uB85C\uADF8 \uCD9C\uB825").option("--quiet", "\uCD5C\uC18C \uCD9C\uB825");
1325
1380
  program.addCommand(initCommand);
1326
1381
  program.addCommand(pushCommand);
@@ -1333,6 +1388,7 @@ program.addCommand(fetchCommand);
1333
1388
  program.addCommand(resolveCommand);
1334
1389
  program.addCommand(watchCommand);
1335
1390
  program.addCommand(verifyCommand);
1391
+ program.addCommand(prepareDbCommand);
1336
1392
  program.parseAsync().then(
1337
1393
  () => {
1338
1394
  scheduleForcedExit(process);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/commands/init.ts","../src/utils/format.ts","../src/commands/push.ts","../src/utils/json-output.ts","../src/commands/pull.ts","../src/commands/sync.ts","../src/commands/status.ts","../src/commands/diff.ts","../src/utils/diff-output.ts","../src/commands/discard.ts","../src/commands/fetch.ts","../src/commands/resolve.ts","../src/utils/state-db-failure.ts","../src/commands/watch.ts","../src/commands/verify.ts","../src/utils/exit.ts"],"sourcesContent":["import { createRequire } from \"node:module\";\nimport { Command } from \"commander\";\nimport { initCommand } from \"./commands/init.js\";\nimport { pushCommand } from \"./commands/push.js\";\nimport { pullCommand } from \"./commands/pull.js\";\nimport { syncCommand } from \"./commands/sync.js\";\nimport { statusCommand } from \"./commands/status.js\";\nimport { diffCommand } from \"./commands/diff.js\";\nimport { discardCommand } from \"./commands/discard.js\";\nimport { fetchCommand } from \"./commands/fetch.js\";\nimport { resolveCommand } from \"./commands/resolve.js\";\nimport { watchCommand } from \"./commands/watch.js\";\nimport { verifyCommand } from \"./commands/verify.js\";\nimport { scheduleForcedExit } from \"./utils/exit.js\";\nimport { reportStateDbFailure } from \"./utils/state-db-failure.js\";\n\nconst requireJson = createRequire(import.meta.url);\nconst { version } = requireJson(\"../package.json\") as { version: string };\n\nconst program = new Command();\n\nprogram\n .name(\"im-nobsidian\")\n .description(\"Im-Nobsidian: Obsidian โ†” Notion ์–‘๋ฐฉํ–ฅ ๋™๊ธฐํ™” CLI\")\n .version(version)\n .option(\"--verbose\", \"์ƒ์„ธ ๋กœ๊ทธ ์ถœ๋ ฅ\")\n .option(\"--quiet\", \"์ตœ์†Œ ์ถœ๋ ฅ\");\n\nprogram.addCommand(initCommand);\nprogram.addCommand(pushCommand);\nprogram.addCommand(pullCommand);\nprogram.addCommand(syncCommand);\nprogram.addCommand(statusCommand);\nprogram.addCommand(diffCommand);\nprogram.addCommand(discardCommand);\nprogram.addCommand(fetchCommand);\nprogram.addCommand(resolveCommand);\nprogram.addCommand(watchCommand);\nprogram.addCommand(verifyCommand);\n\n// ๊ฐ•์ œ ์ข…๋ฃŒํ•˜๋˜ ๋ช…๋ น์ด ์ •ํ•œ ์ข…๋ฃŒ ์ฝ”๋“œ๋ฅผ ๋ณด์กดํ•œ๋‹ค โ€” ๊ทผ๊ฑฐ๋Š” utils/exit.ts ์ฐธ์กฐ(R11-C).\n// ์ƒํƒœ DB ๋ฅผ ์“ธ ์ˆ˜ ์—†์–ด ๋ช…๋ น์ด ๋˜์กŒ์œผ๋ฉด ์ด์œ ์™€ ํ•  ์ผ์„ ๋ณด์ด๊ณ  1 ๋กœ ๋๋‚ธ๋‹ค โ€” utils/state-db-failure.ts.\nprogram.parseAsync().then(\n () => {\n scheduleForcedExit(process);\n },\n (error: unknown) => {\n reportStateDbFailure(error, process);\n scheduleForcedExit(process);\n },\n);\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n NOTION_TOKEN_PREFIX,\n NotionClient,\n notionIdFromInput,\n} from \"@im-nobsidian/core\";\nimport { input, confirm } from \"@inquirer/prompts\";\nimport ora from \"ora\";\nimport chalk from \"chalk\";\nimport { header, separator, icons, dimText } from \"../utils/format.js\";\n\n/** ๋ฃจํŠธ ํŽ˜์ด์ง€์—์„œ ID ๋ฅผ ๋ชป ์ฐพ์•˜์„ ๋•Œ โ€” ์ฃผ์†Œ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ถ™์—ฌ ๋„ฃ์–ด๋„ ๋œ๋‹ค๊ณ  ๋งํ•œ๋‹ค. */\nconst ROOT_PAGE_UNREADABLE =\n \"๋ฃจํŠธ ํŽ˜์ด์ง€์—์„œ Notion ํŽ˜์ด์ง€ ID(32์ž๋ฆฌ)๋ฅผ ์ฐพ์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค โ€” ํŽ˜์ด์ง€ ์ฃผ์†Œ๋‚˜ ID ๋ฅผ ๋„ฃ์œผ์„ธ์š”.\";\n\nexport const initCommand = new Command(\"init\")\n .description(\"Im-Nobsidian ์ดˆ๊ธฐ ์„ค์ •\")\n .option(\"--token <token>\", \"Notion Integration Token\")\n .option(\"--root-page-id <id>\", \"๋ฃจํŠธ ํŽ˜์ด์ง€ ID ๋˜๋Š” ํŽ˜์ด์ง€ ์ฃผ์†Œ\")\n .option(\"--non-interactive\", \"๋น„๋Œ€ํ™”ํ˜• ๋ชจ๋“œ\")\n .action(async (options: { token?: string; rootPageId?: string; nonInteractive?: boolean }) => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n\n console.log(`\\n${header(chalk.cyan(\" Im-Nobsidian Setup\"))}`);\n console.log(` ${separator()}`);\n\n if (options.nonInteractive) {\n if (!options.token || !options.rootPageId) {\n console.error(\"๋น„๋Œ€ํ™”ํ˜• ๋ชจ๋“œ์—์„œ๋Š” --token๊ณผ --root-page-id๊ฐ€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.\");\n process.exitCode = 1;\n return;\n }\n\n if (!options.token.startsWith(NOTION_TOKEN_PREFIX)) {\n console.error(`ํ† ํฐ์€ ${NOTION_TOKEN_PREFIX}์œผ๋กœ ์‹œ์ž‘ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.`);\n process.exitCode = 1;\n return;\n }\n\n const rootPageId = notionIdFromInput(options.rootPageId);\n if (rootPageId === null) {\n console.error(ROOT_PAGE_UNREADABLE);\n process.exitCode = 1;\n return;\n }\n\n const spinner = ora(\"Notion ์—ฐ๊ฒฐ ํ™•์ธ ์ค‘...\").start();\n try {\n const client = new NotionClient({ token: options.token });\n await client.search({ pageSize: 1, filter: { property: \"object\", value: \"page\" } });\n spinner.stop();\n console.log(` ${icons.success} Token validated`);\n\n await configManager.init({ token: options.token, rootPageId });\n console.log(`\\n ${icons.success} Config saved to ${dimText(configManager.configPath)}`);\n console.log(\n `\\n ${header(chalk.green(\"Ready!\"))} Run ${chalk.cyan(\"nobsi sync\")} to start syncing.`,\n );\n } catch (error) {\n spinner.fail(\"Notion ์—ฐ๊ฒฐ ์‹คํŒจ\");\n console.error(error instanceof Error ? error.message : error);\n process.exitCode = 1;\n }\n return;\n }\n\n const isInit = await configManager.isInitialized();\n if (isInit) {\n const overwrite = await confirm({\n message: \"์ด๋ฏธ ์ดˆ๊ธฐํ™”๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์‹œ ์„ค์ •ํ• ๊นŒ์š”?\",\n default: false,\n });\n if (!overwrite) return;\n }\n\n const token = await input({\n message: \"Notion Integration Token:\",\n validate: (v) =>\n v.startsWith(NOTION_TOKEN_PREFIX) ||\n `${NOTION_TOKEN_PREFIX}์œผ๋กœ ์‹œ์ž‘ํ•˜๋Š” ํ† ํฐ์„ ์ž…๋ ฅํ•˜์„ธ์š”`,\n });\n\n const spinner = ora(\"Notion ์—ฐ๊ฒฐ ํ™•์ธ ์ค‘...\").start();\n\n try {\n const client = new NotionClient({ token });\n const result = await client.search({\n pageSize: 10,\n filter: { property: \"object\", value: \"page\" },\n });\n spinner.stop();\n console.log(` ${icons.success} Token validated`);\n\n if (result.results.length === 0) {\n console.log(\n `\\n ${chalk.yellow(\"โš \")} ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ ํŽ˜์ด์ง€๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. Integration์— ํŽ˜์ด์ง€๋ฅผ ๊ณต์œ ํ•ด์ฃผ์„ธ์š”.`,\n );\n return;\n }\n\n console.log(`\\n Select root page:`);\n for (const [i, page] of result.results.entries()) {\n const title = extractPageTitle(page);\n console.log(` ${dimText(`${i + 1}.`)} ${title}`);\n }\n\n const rootPageInput = await input({\n message: \"๋ฃจํŠธ ํŽ˜์ด์ง€ ID ๋˜๋Š” ์ฃผ์†Œ (์œ„์—์„œ ์„ ํƒํ•˜๊ฑฐ๋‚˜ ์ง์ ‘ ์ž…๋ ฅ):\",\n default: result.results[0]?.id,\n validate: (value) => notionIdFromInput(value) !== null || ROOT_PAGE_UNREADABLE,\n });\n const rootPageId = notionIdFromInput(rootPageInput)!;\n\n await configManager.init({ token, rootPageId });\n console.log(`\\n ${icons.success} Config saved to ${dimText(configManager.configPath)}`);\n console.log(\n `\\n ${header(chalk.green(\"Ready!\"))} Run ${chalk.cyan(\"nobsi sync\")} to start syncing.`,\n );\n } catch (error) {\n spinner.fail(\"Notion ์—ฐ๊ฒฐ ์‹คํŒจ\");\n console.error(error instanceof Error ? error.message : error);\n process.exitCode = 1;\n }\n });\n\nfunction extractPageTitle(page: Record<string, unknown>): string {\n const properties = page[\"properties\"] as Record<string, unknown> | undefined;\n if (!properties) return \"(์ œ๋ชฉ ์—†์Œ)\";\n\n for (const prop of Object.values(properties)) {\n const p = prop as { type?: string; title?: Array<{ plain_text?: string }> };\n if (p.type === \"title\" && p.title?.[0]?.plain_text) {\n return p.title[0].plain_text;\n }\n }\n return \"(์ œ๋ชฉ ์—†์Œ)\";\n}\n","import chalk from \"chalk\";\nimport type { FullScanReason, ProgressItem, RemoteScanInfo } from \"@im-nobsidian/core\";\n\nexport const icons = {\n create: chalk.green(\"+\"),\n update: chalk.yellow(\"~\"),\n delete: chalk.red(\"-\"),\n move: chalk.cyan(\"โ†’\"),\n success: chalk.green(\"โœ“\"),\n fail: chalk.red(\"โœ•\"),\n conflict: chalk.magenta(\"!\"),\n synced: chalk.green(\"โ—\"),\n modified: chalk.yellow(\"โ—\"),\n newFile: chalk.cyan(\"โ—\"),\n conflictDot: chalk.magenta(\"โ—\"),\n} as const;\n\nexport function header(text: string): string {\n return chalk.bold(text);\n}\n\nexport function separator(width = 35): string {\n return chalk.dim(\"โ”\".repeat(width));\n}\n\nexport function operationIcon(op: ProgressItem[\"operation\"]): string {\n return icons[op];\n}\n\nexport function operationLabel(op: ProgressItem[\"operation\"]): string {\n switch (op) {\n case \"create\":\n return chalk.green(\"created\");\n case \"update\":\n return chalk.yellow(\"updated\");\n case \"delete\":\n return chalk.red(\"deleted\");\n case \"move\":\n return chalk.cyan(\"moved\");\n }\n}\n\n/** ๊ฒฐ๊ณผ ์ˆ˜ ํ•œ ์ค„. `moved` ๋Š” push ๋งŒ ์„ผ๋‹ค โ€” ์˜ฎ๊ธด ๋…ธํŠธ ยท ํด๋”(๋ณธ๋ฌธ์„ ํ•จ๊ป˜ ๊ณ ์นœ ๊ฒƒ๋„). */\nexport function summary(created: number, updated: number, deleted: number, moved = 0): string {\n const parts: string[] = [];\n if (created > 0) parts.push(chalk.green(`${created} created`));\n if (updated > 0) parts.push(chalk.yellow(`${updated} updated`));\n if (moved > 0) parts.push(chalk.cyan(`${moved} moved`));\n if (deleted > 0) parts.push(chalk.red(`${deleted} deleted`));\n if (parts.length === 0) parts.push(chalk.dim(\"no changes\"));\n return parts.join(\" \");\n}\n\n/**\n * ์™„๋ฃŒ ํ—ค๋” โ€” ์‹คํŒจ๊ฐ€ ํ•˜๋‚˜๋ผ๋„ ์žˆ์œผ๋ฉด ์ดˆ๋ก \"complete\" ๋กœ ๋๋‚ด์ง€ ์•Š๋Š”๋‹ค.\n *\n * pull/push/sync ๊ฐ€ ๋ชจ๋‘ ๊ฒฐ๊ณผ์™€ ๋ฌด๊ด€ํ•˜๊ฒŒ ์ดˆ๋ก `... complete` ๋ฅผ ์ฐ์—ˆ๋‹ค. ์ˆ˜์‹ญ ๊ฑด์ด ์‹คํŒจํ•ด๋„\n * ํ™”๋ฉด ๋งจ ์•„๋ž˜๋Š” ์„ฑ๊ณต์œผ๋กœ ์ฝํ˜€, ์‚ฌ๋žŒ์€ ๋ฌผ๋ก  ๋กœ๊ทธ๋ฅผ ํ›‘๋Š” ์Šคํฌ๋ฆฝํŠธ๊นŒ์ง€ ๋„˜์–ด๊ฐ”๋‹ค.\n * ํ—ค๋” ๋ฌธ๊ตฌยท์ƒ‰์„ ๊ฒฐ๊ณผ์—์„œ ํŒŒ์ƒ์‹œ์ผœ ์„ธ ๋ช…๋ น์ด ๊ฐ™์€ ํŒ์ •์„ ๊ณต์œ ํ•œ๋‹ค.\n */\nexport function completionHeader(label: string, failedCount: number): string {\n return failedCount > 0\n ? header(chalk.yellow(`${label} finished with errors`))\n : header(chalk.green(`${label} complete`));\n}\n\n/** ์‹คํŒจ ๊ฑด์ˆ˜ ํ‘œ๊ธฐ. dim ์€ \"์—†๋Š” ์…ˆ\" ์œผ๋กœ ์ฝํžˆ๋ฏ€๋กœ 0 ์ด ์•„๋‹ ๋•Œ๋งŒ dim ์„ ๋ฒ—๋Š”๋‹ค. */\nexport function failedCountText(count: number): string {\n return count > 0 ? chalk.red(`${count} failed`) : chalk.dim(\"0 failed\");\n}\n\nexport function duration(ms: number): string {\n return chalk.dim(`Done in ${(ms / 1000).toFixed(1)}s`);\n}\n\n/** ๋กœ์ปฌ ์‹œ๊ฐ `YYYY-MM-DD HH:MM:SS`. ์ฝ์„ ์ˆ˜ ์—†๋Š” ๊ฐ’์€ ๊ทธ๋Œ€๋กœ ๋ณด์ธ๋‹ค โ€” NaN ์œผ๋กœ ๋ฐ”๊พธ์ง€ ์•Š๋Š”๋‹ค. */\nexport function localDateTime(iso: string): string {\n const date = new Date(iso);\n if (Number.isNaN(date.getTime())) return iso;\n const pad = (n: number): string => String(n).padStart(2, \"0\");\n return (\n `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ` +\n `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`\n );\n}\n\n/** ์ „์ฒด ๋Œ€์กฐํ•œ ์ด์œ . ์ฝ”์–ด์˜ ์ด์œ  ์ฝ”๋“œ๊ฐ€ ๋Š˜๋ฉด ์—ฌ๊ธฐ๊ฐ€ ์ปดํŒŒ์ผ๋˜์ง€ ์•Š๋Š”๋‹ค โ€” ๋น ์ง„ ๋ง์„ ์กฐ์šฉํžˆ ๋‘์ง€ ์•Š๋Š”๋‹ค. */\nconst FULL_SCAN_REASON: Record<FullScanReason, string> = {\n first: \"first pull\",\n forced: \"--force\",\n \"database-mode\": \"database mode\",\n \"every-pull\": \"every pull\",\n due: \"scheduled\",\n};\n\n/**\n * ์ด๋ฒˆ์— ์›๊ฒฉ์„ ์–ผ๋งˆ๋‚˜ ํ›‘์—ˆ๋‚˜ (ADR-027). ๋ฐ”๋€ ๊ฒƒ๋งŒ ์ฐพ์•˜์œผ๋ฉด Notion ์—์„œ ์ง€์šด ๊ฒƒ์€ ์•„์ง ๋ณผํŠธ์— ๋ฐ˜์˜๋˜์ง€\n * ์•Š์•˜์„ ์ˆ˜ ์žˆ๋‹ค โ€” ๊ทธ ์‚ฌ์‹ค๊ณผ ์–ธ์ œ ๋ฐ˜์˜๋˜๋Š”์ง€๋ฅผ ํ•จ๊ป˜ ์ ๋Š”๋‹ค. ์ ์ง€ ์•Š์œผ๋ฉด ยซNotion ์—์„œ ์ง€์› ๋Š”๋ฐ ๋ณผํŠธ์—\n * ๋‚จ์•˜๋‹คยป ๋ฅผ ๊ฒฐํ•จ์œผ๋กœ ์ฝ๋Š”๋‹ค. ์›๊ฒฉ ์‚ญ์ œ๋ฅผ ๋ฐ˜์˜ํ•˜์ง€ ์•Š๋Š” ์„ค์ •(`sync.deleteSync` ๊บผ์ง)์ด๋ฉด ์ ์ง€ ์•Š๋Š”๋‹ค.\n */\nexport function remoteScanLines(scan: RemoteScanInfo): string[] {\n if (scan.kind === \"full\") {\n return [dimText(scan.reason ? `Full scan (${FULL_SCAN_REASON[scan.reason]})` : \"Full scan\")];\n }\n const skipped = scan.skippedDatabases ?? 0;\n const skippedText =\n skipped > 0\n ? ` ยท ${skipped} unchanged ${skipped === 1 ? \"database\" : \"databases\"} skipped`\n : \"\";\n const lines = [dimText(`Changes-only scan${skippedText}`)];\n if (scan.deletionsDeferred) lines.push(remoteDeletionHint(scan));\n return lines;\n}\n\n/** ๋ฏธ๋ฃฌ ์›๊ฒฉ ์‚ญ์ œ๊ฐ€ ์–ธ์ œ ๋ฐ˜์˜๋˜๋‚˜ โ€” {@link RemoteScanInfo.deletionsDeferred} ์ผ ๋•Œ ์“ด๋‹ค. */\nexport function remoteDeletionHint(scan: RemoteScanInfo): string {\n const when = scan.nextFullAt\n ? `the next full scan (${localDateTime(scan.nextFullAt)})`\n : \"the next pull (full scan due)\";\n return `${dimText(`Deletions in Notion apply at ${when} โ€” or run`)} ${chalk.cyan(\"nobsi pull --force\")}`;\n}\n\n/** ๋งˆ์ง€๋ง‰ ์ „์ฒด ๋Œ€์กฐ ์‹œ๊ฐ โ€” ํ•œ ๋ฒˆ๋„ ์—†์œผ๋ฉด never. */\nexport function lastFullScanText(lastFullScanAt: string | null): string {\n return lastFullScanAt ? localDateTime(lastFullScanAt) : chalk.yellow(\"never\");\n}\n\nexport function progress(current: number, total: number): string {\n return chalk.dim(`[${current}/${total}]`);\n}\n\nexport function dimText(text: string): string {\n return chalk.dim(text);\n}\n\n/**\n * ์‹คํŒจ ํ•œ ์ค„. ๋ณผํŠธ ์ตœ์ƒ์œ„์˜ ์‹คํŒจ(DB ์ž๋™ ๋ฐœ๊ฒฌ ๋“ฑ)๋Š” ๋นˆ ๊ฒฝ๋กœ๋กœ ์˜ค๋ฏ€๋กœ `/` ๋กœ ๋ณด์ธ๋‹ค. `direction` ์€\n * sync ๊ฐ€ ๋ฐ›๊ธฐ(โ–ผ) ยท ์˜ฌ๋ฆฌ๊ธฐ(โ–ฒ)๋ฅผ ๊ฐ€๋ฅผ ๋•Œ ์“ด๋‹ค.\n */\nexport function failedItem(path: string, error: string, direction?: string): void {\n const where = `${direction ? `${direction} ` : \"\"}${path || \"/\"}`;\n console.log(` ${icons.fail} ${where}: ${chalk.red(error)}`);\n}\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n NodeVaultFS,\n} from \"@im-nobsidian/core\";\nimport {\n header,\n operationIcon,\n operationLabel,\n progress,\n summary,\n duration,\n failedItem,\n dimText,\n completionHeader,\n failedCountText,\n} from \"../utils/format.js\";\nimport { enableJsonMode, printJson, pushJson } from \"../utils/json-output.js\";\n\nexport const pushCommand = new Command(\"push\")\n .description(\"๋กœ์ปฌ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ Notion์— ๋ฐ˜์˜\")\n .option(\"--dry-run\", \"์‹ค์ œ ๋ฐ˜์˜ ์—†์ด ๋ณ€๊ฒฝ์‚ฌํ•ญ๋งŒ ํ‘œ์‹œ\")\n .option(\"-p, --path <paths...>\", \"ํŠน์ • ๊ฒฝ๋กœ๋งŒ push\")\n .option(\"--json\", \"๊ฒฐ๊ณผ๋ฅผ JSON ํ•œ ์ค„๋กœ ์ถœ๋ ฅ (์ž๋™ํ™”์šฉ โ€” ๋กœ๊ทธ๋Š” stderr)\")\n .action(async (options) => {\n const json = options.json === true;\n if (json) enableJsonMode();\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n try {\n const client = NotionClient.fromConfig(config);\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n if (!json) {\n console.log(`\\n${header(\" Pushing to Notion...\")}`);\n if (options.dryRun) console.log(dimText(\" (dry-run mode)\"));\n console.log(\"\");\n }\n\n const result = await orchestrator.push({\n dryRun: options.dryRun,\n paths: options.path,\n onProgress: json\n ? undefined\n : (current, total, item) => {\n const icon = operationIcon(item.operation);\n const label = operationLabel(item.operation);\n const prog = progress(current, total);\n console.log(` ${icon} ${item.path} ${prog} ${label}`);\n },\n });\n\n if (json) {\n printJson(pushJson(result));\n if (result.failed.length > 0) process.exitCode = 1;\n return;\n }\n\n const failedCount = result.failed.length;\n console.log(`\\n ${completionHeader(\"Push\", failedCount)}`);\n console.log(\n ` ${summary(result.created, result.updated, result.deleted, result.moved)} ${failedCountText(failedCount)}`,\n );\n console.log(` ${duration(result.duration)}`);\n\n if (failedCount > 0) {\n console.log(\"\");\n for (const f of result.failed) {\n failedItem(f.path, f.error);\n }\n // ์ข…๋ฃŒ ์ฝ”๋“œ๊นŒ์ง€ ์‹คํŒจ๋กœ ๋‚จ๊ธด๋‹ค โ€” push ๊ฐ€ ๋ฐ˜์ฏค ์‹คํŒจํ•œ ๊ฑธ ์ž๋™ํ™”๊ฐ€ ์„ฑ๊ณต์œผ๋กœ ๋ณด๋ฉด ์•ˆ ๋œ๋‹ค.\n process.exitCode = 1;\n }\n } finally {\n stateDb.close();\n }\n });\n","import { setLogger } from \"@im-nobsidian/core\";\nimport type { FailedOperation, PullResult, PushResult, RemoteScanInfo } from \"@im-nobsidian/core\";\n\n/**\n * `--json` ๋ชจ๋“œ โ€” ์ž๋™ํ™”(E2E ํ•˜๋‹ˆ์Šค ยท CI)๊ฐ€ ์‚ฌ๋žŒ์šฉ ๋ฌธ๊ตฌ๋ฅผ grep ํ•˜์ง€ ์•Š๊ณ  ๊ฒฐ๊ณผ๋ฅผ ์ฝ๊ฒŒ ํ•œ๋‹ค.\n *\n * ๋ฌธ๊ตฌ grep ์€ ์กฐ์šฉํžˆ ํ‹€๋ ธ๋‹ค. sync ๋กœ๊ทธ์— Pull ์ชฝ \"no changes\" ๊ฐ€ ํ•œ ์ค„๋งŒ ์žˆ์–ด๋„ Push ์ชฝ\n * ๋ณ€๊ฒฝ๊ณผ ๋ฌด๊ด€ํ•˜๊ฒŒ churn 0 ์œผ๋กœ ํŒ์ •ํ–ˆ๊ณ , restored ยท deleted ๋Š” ์•„์˜ˆ ์„ธ์ง€ ์•Š์•˜๋‹ค.\n *\n * ๊ณ„์•ฝ: stdout ์—๋Š” JSON ํ•œ ์ค„๋งŒ ์“ด๋‹ค. ์ฝ”์–ด ๋กœ๊ทธ๋Š” ์ „๋ถ€ stderr ๋กœ ๋Œ๋ฆฐ๋‹ค.\n */\nexport function enableJsonMode(): void {\n const toStderr = (message: string, ...args: unknown[]): void => console.error(message, ...args);\n setLogger({ warn: toStderr, error: toStderr, info: toStderr, debug: () => {} });\n}\n\nexport interface PushJson {\n readonly created: number;\n readonly updated: number;\n readonly deleted: number;\n /** ์˜ฎ๊น€์„ ๋ฐ˜์˜ํ•œ ๋…ธํŠธ ยท ํด๋” ์ˆ˜ โ€” updated ์™€ ๊ฒน์น˜์ง€ ์•Š๋Š”๋‹ค. */\n readonly moved: number;\n /** ์›๊ฒฉ์— ์‹ค์ œ๋กœ ์“ด(๋˜๋Š” dry-run ์ด๋ฉด ์“ธ) ํ•ญ๋ชฉ ์ˆ˜. ๋ฉฑ๋“ฑ ํŒ์ •์˜ ๊ธฐ์ค€๊ฐ’. */\n readonly churn: number;\n readonly failed: readonly FailedOperation[];\n readonly durationMs: number;\n}\n\nexport interface PullJson {\n readonly created: number;\n readonly updated: number;\n readonly deleted: number;\n readonly restored: number;\n /** ๋ณผํŠธ์— ์‹ค์ œ๋กœ ์“ด ํ•ญ๋ชฉ ์ˆ˜ โ€” ๋ณต์›๋„ ์“ฐ๊ธฐ๋‹ค. ๋ฉฑ๋“ฑ ํŒ์ •์˜ ๊ธฐ์ค€๊ฐ’. */\n readonly churn: number;\n readonly conflicts: number;\n readonly failed: readonly FailedOperation[];\n readonly writtenPaths: number;\n readonly images: number;\n readonly files: number;\n readonly links: number;\n readonly durationMs: number;\n /** ์›๊ฒฉ์„ ์–ผ๋งˆ๋‚˜ ํ›‘์—ˆ๋‚˜ โ€” ๋ฐ”๋€ ๊ฒƒ๋งŒ ์ฐพ์•˜์œผ๋ฉด Notion ์—์„œ ์ง€์šด ๊ฒƒ์€ ๋‹ค์Œ ์ „์ฒด ๋Œ€์กฐ๊ฐ€ ๋ฐ˜์˜ํ•œ๋‹ค. */\n readonly remoteScan: RemoteScanInfo | null;\n}\n\nexport function pushJson(result: PushResult): PushJson {\n return {\n created: result.created,\n updated: result.updated,\n deleted: result.deleted,\n moved: result.moved,\n churn: result.created + result.updated + result.deleted + result.moved,\n failed: result.failed,\n durationMs: result.duration,\n };\n}\n\nexport function pullJson(result: PullResult): PullJson {\n return {\n created: result.created,\n updated: result.updated,\n deleted: result.deleted,\n restored: result.restored,\n churn: result.created + result.updated + result.deleted + result.restored,\n conflicts: result.conflicts.length,\n failed: result.failed,\n writtenPaths: result.writtenPaths.length,\n images: result.imageCount,\n files: result.fileCount,\n links: result.linkCount,\n durationMs: result.duration,\n remoteScan: result.remoteScan ?? null,\n };\n}\n\nexport function printJson(value: unknown): void {\n process.stdout.write(`${JSON.stringify(value)}\\n`);\n}\n","import { Command, Option } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n NodeVaultFS,\n} from \"@im-nobsidian/core\";\nimport chalk from \"chalk\";\nimport {\n header,\n operationIcon,\n operationLabel,\n progress,\n summary,\n duration,\n icons,\n failedItem,\n dimText,\n completionHeader,\n failedCountText,\n remoteScanLines,\n} from \"../utils/format.js\";\nimport { enableJsonMode, printJson, pullJson } from \"../utils/json-output.js\";\n\nexport const pullCommand = new Command(\"pull\")\n .description(\"Notion ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ๋กœ์ปฌ์— ๋ฐ˜์˜\")\n .option(\"--dry-run\", \"์‹ค์ œ ๋ฐ˜์˜ ์—†์ด ๋ณ€๊ฒฝ์‚ฌํ•ญ๋งŒ ํ‘œ์‹œ\")\n .option(\"-p, --path <paths...>\", \"ํŠน์ • ๊ฒฝ๋กœ๋งŒ pull\")\n .option(\n \"--force\",\n \"์ฆ๋ถ„ ๊ฐ์ง€๋ฅผ ๊ฑด๋„ˆ๋›ฐ๊ณ  ์ „์ฒด ์Šค์บ” (search ์ธ๋ฑ์‹ฑ ์ง€์—ฐ์œผ๋กœ ๋ˆ„๋ฝ๋œ ์‹ ๊ทœ ํŽ˜์ด์ง€ยทDB ๋ณต๊ตฌ)\",\n )\n // ๋‹ค์‹œ ๊ทธ๋ ค ๋ด์•ผ ๋ฐ”๋€Œ๋Š”์ง€ ์•ˆ๋‹ค โ€” ๋ฏธ๋ฆฌ ๋ณด๊ธฐ๋กœ๋Š” ๋ณด์ผ ๊ฒƒ์ด ์—†๋‹ค(B14).\n .addOption(\n new Option(\n \"--rerender\",\n \"Notion ์—์„œ ๋ฐ”๋€Œ์ง€ ์•Š์€ ๋ฐ›์•„ ๋‘” ๋…ธํŠธ๋„ ์ง€๊ธˆ ๋ณ€ํ™˜๊ธฐ๋กœ ๋‹ค์‹œ ๊ทธ๋ฆผ (๋ณผํŠธ์—์„œ ๊ณ ์นœ ๋…ธํŠธ๋Š” ๊ทธ๋Œ€๋กœ)\",\n ).conflicts(\"dryRun\"),\n )\n .option(\"--json\", \"๊ฒฐ๊ณผ๋ฅผ JSON ํ•œ ์ค„๋กœ ์ถœ๋ ฅ (์ž๋™ํ™”์šฉ โ€” ๋กœ๊ทธ๋Š” stderr)\")\n .action(async (options) => {\n const json = options.json === true;\n if (json) enableJsonMode();\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n try {\n const client = NotionClient.fromConfig(config);\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n if (!json) {\n console.log(`\\n${header(\" Pulling from Notion...\")}`);\n if (options.dryRun) console.log(dimText(\" (dry-run mode)\"));\n console.log(\"\");\n }\n\n const result = await orchestrator.pull({\n dryRun: options.dryRun,\n paths: options.path,\n force: options.force,\n rerender: options.rerender,\n onProgress: json\n ? undefined\n : (current, total, item) => {\n // ๋ฐ›์•„ ๋ดค์ง€๋งŒ ๋ฐ”๋€Œ์ง€ ์•Š์€ ๋…ธํŠธ โ€” ๋‹ค์‹œ ๊ทธ๋ฆฌ๊ธฐ๋Š” ๋ฐ›์•„ ๋‘” ๋…ธํŠธ๋ฅผ ๋ชจ๋‘ ๋ฐ›์•„ ๋ณธ๋‹ค(B14).\n if (item.unchanged) return;\n const icon = operationIcon(item.operation);\n const label = operationLabel(item.operation);\n const prog = progress(current, total);\n console.log(` ${icon} ${item.path} ${prog} ${label}`);\n },\n });\n\n if (json) {\n printJson(pullJson(result));\n if (result.failed.length > 0) process.exitCode = 1;\n return;\n }\n\n if (result.imageCount > 0) {\n console.log(\n `\\n ${icons.success} ${result.imageCount} images โ†’ ${dimText(`${config.paths.attachments}/`)}`,\n );\n }\n if (result.fileCount > 0) {\n console.log(\n ` ${icons.success} ${result.fileCount} files โ†’ ${dimText(`${config.paths.attachments}/`)}`,\n );\n }\n if (result.linkCount > 0) {\n console.log(` ${icons.success} ${result.linkCount} links resolved`);\n }\n\n const failedCount = result.failed.length;\n console.log(`\\n ${completionHeader(\"Pull\", failedCount)}`);\n console.log(\n ` ${summary(result.created, result.updated, result.deleted)} ${failedCountText(failedCount)}`,\n );\n // ๋ณต์›์€ ๋ณ„๋„ ์ค„๋กœ ์•Œ๋ฆฐ๋‹ค โ€” ๋ณผํŠธ์—์„œ ํŒŒ์ผ์ด ์‚ฌ๋ผ์กŒ์—ˆ๋‹ค๋Š” ์‚ฌ์‹ค์€ ์กฐ์šฉํžˆ ๋„˜๊ธธ ์ผ์ด ์•„๋‹ˆ๋‹ค.\n if (result.restored > 0) {\n console.log(` ${icons.success} ${chalk.yellow(`${result.restored} restored`)}`);\n }\n console.log(` ${duration(result.duration)}`);\n if (result.remoteScan) {\n for (const line of remoteScanLines(result.remoteScan)) console.log(` ${line}`);\n }\n\n if (result.conflicts.length > 0) {\n console.log(\n `\\n ${icons.conflict} ${chalk.magenta(`${result.conflicts.length} conflicts`)} โ€” run ${chalk.cyan(\"nobsi resolve\")}`,\n );\n }\n\n if (failedCount > 0) {\n console.log(\"\");\n for (const f of result.failed) {\n failedItem(f.path, f.error);\n }\n // ์ข…๋ฃŒ ์ฝ”๋“œ๊นŒ์ง€ ์‹คํŒจ๋กœ ๋‚จ๊ธด๋‹ค โ€” ์ž๋™ํ™”๊ฐ€ pull ์‹คํŒจ๋ฅผ ์„ฑ๊ณต์œผ๋กœ ์ง‘๊ณ„ํ•˜๋ฉด ์•ˆ ๋œ๋‹ค.\n process.exitCode = 1;\n }\n } finally {\n stateDb.close();\n }\n });\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n NodeVaultFS,\n} from \"@im-nobsidian/core\";\nimport chalk from \"chalk\";\nimport {\n header,\n separator,\n operationIcon,\n operationLabel,\n summary,\n duration,\n icons,\n dimText,\n completionHeader,\n failedItem,\n remoteScanLines,\n} from \"../utils/format.js\";\nimport { enableJsonMode, printJson, pullJson, pushJson } from \"../utils/json-output.js\";\n\nexport const syncCommand = new Command(\"sync\")\n .description(\"์–‘๋ฐฉํ–ฅ ๋™๊ธฐํ™” (Pull โ†’ Push)\")\n .option(\"--dry-run\", \"์‹ค์ œ ๋ฐ˜์˜ ์—†์ด ๋ณ€๊ฒฝ์‚ฌํ•ญ๋งŒ ํ‘œ์‹œ\")\n .option(\"--json\", \"๊ฒฐ๊ณผ๋ฅผ JSON ํ•œ ์ค„๋กœ ์ถœ๋ ฅ (์ž๋™ํ™”์šฉ โ€” ๋กœ๊ทธ๋Š” stderr)\")\n .action(async (options) => {\n const json = options.json === true;\n if (json) enableJsonMode();\n // ์ง„ํ–‰ ๋กœ๊ทธ๋Š” ์‚ฌ๋žŒ์šฉ์ด๋‹ค โ€” JSON ๋ชจ๋“œ์—์„œ๋Š” stdout ๊ณ„์•ฝ(JSON ํ•œ ์ค„)์„ ์ง€ํ‚ค๋ ค๊ณ  ๋ˆ๋‹ค.\n const log = json ? () => {} : (line: string) => console.log(line);\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n try {\n const client = NotionClient.fromConfig(config);\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n log(`\\n${header(\" Bidirectional Sync\")}`);\n log(` ${separator()}`);\n if (options.dryRun) log(dimText(\" (dry-run mode)\"));\n\n // ๋ฐ›๊ธฐ ยท ์˜ฌ๋ฆฌ๊ธฐ๋ฅผ ํ•œ ๋ฒˆ์— ๋ˆ๋‹ค โ€” ํ”Œ๋Ÿฌ๊ทธ์ธ ยท watch ์™€ ๊ฐ™์€ sync ๋‹ค. push ๋Š” pull ์ด ๋ฐฉ๊ธˆ ์“ด ๋…ธํŠธ๋ฅผ\n // ๋‹ค์‹œ ์˜ฌ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค(F-j). ์˜ˆ์ „์—๋Š” pull ยท push ๋ฅผ ๋”ฐ๋กœ ๋ถˆ๋Ÿฌ ๋‘ ยซsyncยป ๊ฐ€ ๋”ฐ๋กœ ์ž๋ž๋‹ค.\n log(`\\n ${header(chalk.blue(\"โ–ผ Pull\"))} ${dimText(\"(Notion โ†’ Obsidian)\")}`);\n let pushShown = false;\n const showPush = (): void => {\n if (pushShown) return;\n pushShown = true;\n log(`\\n ${header(chalk.magenta(\"โ–ฒ Push\"))} ${dimText(\"(Obsidian โ†’ Notion)\")}`);\n };\n\n const result = await orchestrator.sync({\n dryRun: options.dryRun,\n onProgress: (_current, _total, item) => {\n if (item.direction === \"push\") showPush();\n if (item.unchanged) return;\n const icon = operationIcon(item.operation);\n const label = operationLabel(item.operation);\n log(` ${icon} ${item.path} ${label}`);\n },\n });\n showPush();\n const { pull: pullResult, push: pushResult } = result;\n\n if (json) {\n const pull = pullJson(pullResult);\n const push = pushJson(pushResult);\n printJson({\n pull,\n push,\n churn: pull.churn + push.churn,\n conflicts: pull.conflicts,\n durationMs: result.duration,\n });\n if (pullResult.failed.length + pushResult.failed.length > 0) process.exitCode = 1;\n return;\n }\n\n // Summary\n // ์˜ˆ์ „์—” ์–‘์ชฝ failed ๋ฅผ ์•„์˜ˆ ์ฐ์ง€ ์•Š์•„, ์ „ ๊ฑด ์‹คํŒจํ•œ sync ๋„ ํ™”๋ฉด ๋์€ ์ดˆ๋ก \"complete\"\n // ์˜€๋‹ค โ€” ์‹คํŒจ๋ฅผ ํ—ค๋”์™€ ๋ชฉ๋ก ์–‘์ชฝ์— ๋“œ๋Ÿฌ๋‚ธ๋‹ค.\n const pullFailed = pullResult.failed.length;\n const pushFailed = pushResult.failed.length;\n console.log(`\\n ${completionHeader(\"Sync\", pullFailed + pushFailed)}`);\n console.log(\n ` ${chalk.blue(\"Pull:\")} ${summary(pullResult.created, pullResult.updated, pullResult.deleted)}` +\n // ์‚ฌ๋ผ์กŒ๋˜ ํŒŒ์ผ์ด ๋˜์‚ด์•„๋‚œ ๊ฒฝ์šฐ์—๋งŒ ๋ง๋ถ™์ธ๋‹ค โ€” ํ‰์ƒ์‹œ ์š”์•ฝ์„ ์–ด์ง€๋Ÿฝํžˆ์ง€ ์•Š๋Š”๋‹ค.\n (pullResult.restored > 0 ? ` ${chalk.yellow(`${pullResult.restored} restored`)}` : \"\") +\n (pullFailed > 0 ? ` ${chalk.red(`${pullFailed} failed`)}` : \"\"),\n );\n if (pullResult.imageCount > 0) {\n console.log(` ${icons.success} ${pullResult.imageCount} images downloaded`);\n }\n if (pullResult.linkCount > 0) {\n console.log(` ${icons.success} ${pullResult.linkCount} links resolved`);\n }\n console.log(\n ` ${chalk.magenta(\"Push:\")} ${summary(pushResult.created, pushResult.updated, pushResult.deleted, pushResult.moved)}` +\n (pushFailed > 0 ? ` ${chalk.red(`${pushFailed} failed`)}` : \"\"),\n );\n console.log(` ${duration(result.duration)}`);\n if (pullResult.remoteScan) {\n for (const line of remoteScanLines(pullResult.remoteScan)) console.log(` ${line}`);\n }\n\n if (result.conflicts.length > 0) {\n console.log(\n `\\n ${icons.conflict} ${chalk.magenta(`${result.conflicts.length} conflicts`)} โ€” run ${chalk.cyan(\"nobsi resolve\")}`,\n );\n }\n\n if (pullFailed + pushFailed > 0) {\n console.log(\"\");\n for (const f of pullResult.failed) failedItem(f.path, f.error, \"โ–ผ\");\n for (const f of pushResult.failed) failedItem(f.path, f.error, \"โ–ฒ\");\n process.exitCode = 1;\n }\n } finally {\n stateDb.close();\n }\n });\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n NodeVaultFS,\n isRemoteDeletion,\n} from \"@im-nobsidian/core\";\nimport chalk from \"chalk\";\nimport {\n header,\n separator,\n icons,\n dimText,\n localDateTime,\n lastFullScanText,\n remoteDeletionHint,\n} from \"../utils/format.js\";\n\nexport const statusCommand = new Command(\"status\")\n .description(\"๋™๊ธฐํ™” ์ƒํƒœ ํ™•์ธ (--full: ์›๊ฒฉ ๋ณ€๊ฒฝ ํฌํ•จ)\")\n .option(\"--full\", \"Notion ์›๊ฒฉ ๋ณ€๊ฒฝ๊นŒ์ง€ ์–‘๋ฐฉํ–ฅ ํ™•์ธ (API ํ˜ธ์ถœ, ๋А๋ฆผ)\")\n .action(async (opts: { full?: boolean }) => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n try {\n const client = NotionClient.fromConfig(config);\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n const status = opts.full ? await orchestrator.status() : await orchestrator.statusLocal();\n\n console.log(`\\n${header(\" Sync Status\")}`);\n console.log(` ${separator(50)}`);\n\n const rootId = config.notion.rootPageId;\n const direction = config.sync.direction === \"both\" ? \"bidirectional\" : config.sync.direction;\n console.log(` ${dimText(\"Root page:\")} ${rootId.slice(0, 8)}...`);\n console.log(` ${dimText(\"Direction:\")} ${direction}`);\n if (status.lastSyncAt) {\n console.log(` ${dimText(\"Last sync:\")} ${localDateTime(status.lastSyncAt)}`);\n } else {\n console.log(` ${dimText(\"Last sync:\")} ${chalk.yellow(\"never\")}`);\n }\n // Notion ์—์„œ ์ง€์šด ๋…ธํŠธ๋Š” ์ „์ฒด ๋Œ€์กฐ ๋•Œ ๋ณผํŠธ์— ๋ฐ˜์˜๋œ๋‹ค โ€” ๊ทธ๋•Œ๋ฅผ ํ•จ๊ป˜ ๋ณด์ธ๋‹ค.\n console.log(` ${dimText(\"Full scan:\")} ${lastFullScanText(status.lastFullScanAt)}`);\n\n // Tracked files summary\n const allRecords = stateDb.getAll();\n const total = allRecords.length;\n const conflictCount = status.conflictRecords.length;\n const createdChanges = status.localChanges.filter((c) => c.type === \"created\");\n const modifiedChanges = status.localChanges.filter((c) => c.type === \"modified\");\n const deletedChanges = status.localChanges.filter((c) => c.type === \"deleted\");\n const movedChanges = status.localChanges.filter((c) => c.type === \"moved\");\n\n // ์ถ”์  ํŒŒ์ผ ๋‚ด์—ญ์€ ์„œ๋กœ ๊ฒน์น˜์ง€ ์•Š์•„์•ผ ํ•œ๋‹ค. DB ์˜ status ๋Š” push/pull ์‹œ์ ์—๋งŒ ๊ฐฑ์‹ ๋˜๋ฏ€๋กœ\n // ๋ฐฉ๊ธˆ ๊ณ ์นœ ํŒŒ์ผ๋„ ๋ ˆ์ฝ”๋“œ์ƒ์œผ๋ก  ์—ฌ์ „ํžˆ 'synced' ๋‹ค โ€” ๊ทธ๋Œ€๋กœ ์„ธ๋ฉด ๊ฐ™์€ ํŒŒ์ผ์ด synced ์™€\n // modified ์–‘์ชฝ์— ์žกํ˜€ ๋‚ด์—ญ ํ•ฉ์ด ์ด๊ณ„๋ฅผ ๋„˜๋Š” ํ‘œ๊ฐ€ ๋‚˜์˜จ๋‹ค(๋ฌด์—‡์ด ๋ฐ€๋ ธ๋Š”์ง€ ๋ชป ์ฝ๋Š”๋‹ค).\n // ์‹ค์ œ ๋ณ€๊ฒฝ์ด ๊ฑธ๋ฆฐ ๊ฒฝ๋กœ๋ฅผ ๋จผ์ € ๋œ์–ด ๋‚ด๊ณ  ๋‚จ์€ ๊ฒƒ๋งŒ synced ๋กœ ์„ผ๋‹ค.\n // ์˜ฎ๊ธด ๋…ธํŠธ์˜ ๋ ˆ์ฝ”๋“œ๋Š” ์•„์ง ์˜› ๊ฒฝ๋กœ(movedFrom)๋ฅผ ์ ๊ณ  ์žˆ๋‹ค โ€” ๋‘ ๊ฒฝ๋กœ๋ฅผ ๋‹ค ๋œ์–ด ๋‚ธ๋‹ค.\n const dirtyPaths = new Set([\n ...modifiedChanges.map((c) => c.path),\n ...deletedChanges.map((c) => c.path),\n ...movedChanges.flatMap((c) => [c.path, c.movedFrom ?? c.path]),\n ...status.conflictRecords.map((r) => r.obsidianPath),\n ]);\n const synced = allRecords.filter(\n (r) => r.status === \"synced\" && !dirtyPaths.has(r.obsidianPath),\n ).length;\n const remoteCreated = status.remoteChanges.filter((c) => c.type === \"created\");\n const remoteModified = status.remoteChanges.filter((c) => c.type === \"modified\");\n const remoteDeleted = status.remoteChanges.filter((c) => c.type === \"deleted\");\n const remoteMoved = status.remoteChanges.filter((c) => c.type === \"moved\");\n const { folderMoves } = status;\n\n console.log(`\\n ${header(`Tracked files: ${total}`)}`);\n console.log(` ${icons.synced} ${chalk.green(\"synced\")} ${synced}`);\n if (modifiedChanges.length > 0) {\n console.log(` ${icons.modified} ${chalk.yellow(\"modified\")} ${modifiedChanges.length}`);\n }\n if (movedChanges.length > 0) {\n console.log(` ${icons.move} ${chalk.cyan(\"moved\")} ${movedChanges.length}`);\n }\n if (deletedChanges.length > 0) {\n console.log(` ${chalk.red(\"-\")} ${chalk.red(\"deleted\")} ${deletedChanges.length}`);\n }\n if (conflictCount > 0) {\n console.log(` ${icons.conflictDot} ${chalk.magenta(\"conflict\")} ${conflictCount}`);\n }\n // new ๋Š” ์•„์ง ์ถ”์  ๋ ˆ์ฝ”๋“œ๊ฐ€ ์—†๋Š” ํŒŒ์ผ์ด๋ผ ์œ„ ์ด๊ณ„(Tracked files)์— ๋“ค์–ด๊ฐ€์ง€ ์•Š๋Š”๋‹ค.\n // ๊ฐ™์€ ๋ธ”๋ก์— ์ˆซ์ž๋งŒ ๋Š˜์–ด๋†“์œผ๋ฉด ํ•ฉ์ด ์•ˆ ๋งž์•„ ๋ณด์ด๋ฏ€๋กœ ๊ทธ ์‚ฌ์‹ค์„ ํ•จ๊ป˜ ์ ๋Š”๋‹ค.\n if (createdChanges.length > 0) {\n console.log(\n ` ${icons.newFile} ${chalk.cyan(\"new\")} ${createdChanges.length} ${dimText(\"(untracked)\")}`,\n );\n }\n\n // Modified files\n if (modifiedChanges.length > 0) {\n console.log(`\\n ${header(\"Modified files:\")}`);\n for (const c of modifiedChanges.slice(0, 10)) {\n console.log(\n ` ${chalk.yellow(\"~\")} ${c.path.padEnd(30)} ${dimText(\"(local changed)\")}`,\n );\n }\n if (modifiedChanges.length > 10) {\n console.log(dimText(` ... and ${modifiedChanges.length - 10} more`));\n }\n }\n\n // Moved โ€” ํด๋”๋ฅผ ๋จผ์ € ๋ณด์ธ๋‹ค. ๊ทธ ์•ˆ์˜ ๋…ธํŠธ๋Š” ์•„๋ž˜์— ๋…ธํŠธ๋งˆ๋‹ค ๋”ฐ๋กœ ์žˆ๋‹ค.\n if (folderMoves.length + movedChanges.length > 0) {\n console.log(`\\n ${header(\"Moved:\")}`);\n for (const move of folderMoves) {\n console.log(` ${icons.move} ${move.from}/ โ†’ ${move.to}/ ${dimText(\"(folder)\")}`);\n }\n for (const c of movedChanges.slice(0, 10)) {\n console.log(` ${icons.move} ${c.movedFrom ?? \"?\"} โ†’ ${c.path}`);\n }\n if (movedChanges.length > 10) {\n console.log(dimText(` ... and ${movedChanges.length - 10} more`));\n }\n }\n\n // Deleted files โ€” ์ˆ˜๋งŒ ๋ณด์ด๋ฉด ๋ฌด์—‡์ด Notion ์—์„œ ์ง€์›Œ์งˆ์ง€ ๋ชจ๋ฅธ๋‹ค.\n if (deletedChanges.length > 0) {\n console.log(`\\n ${header(\"Deleted files:\")}`);\n for (const c of deletedChanges.slice(0, 10)) {\n console.log(` ${chalk.red(\"-\")} ${c.path}`);\n }\n if (deletedChanges.length > 10) {\n console.log(dimText(` ... and ${deletedChanges.length - 10} more`));\n }\n }\n\n // New files\n if (createdChanges.length > 0) {\n console.log(`\\n ${header(\"New files:\")}`);\n for (const c of createdChanges.slice(0, 10)) {\n console.log(` ${chalk.cyan(\"+\")} ${c.path.padEnd(30)} ${dimText(\"(untracked)\")}`);\n }\n if (createdChanges.length > 10) {\n console.log(dimText(` ... and ${createdChanges.length - 10} more`));\n }\n }\n\n // Remote changes (Notion-side)\n if (status.remoteChanges.length > 0) {\n console.log(`\\n ${header(`Remote changes (Notion): ${status.remoteChanges.length}`)}`);\n if (remoteCreated.length > 0) {\n console.log(` ${chalk.cyan(\"+\")} ${chalk.cyan(\"new\")} ${remoteCreated.length}`);\n }\n if (remoteModified.length > 0) {\n console.log(\n ` ${chalk.yellow(\"~\")} ${chalk.yellow(\"modified\")} ${remoteModified.length}`,\n );\n }\n if (remoteMoved.length > 0) {\n console.log(` ${icons.move} ${chalk.cyan(\"moved\")} ${remoteMoved.length}`);\n }\n if (remoteDeleted.length > 0) {\n console.log(` ${chalk.red(\"-\")} ${chalk.red(\"deleted\")} ${remoteDeleted.length}`);\n }\n for (const c of status.remoteChanges.slice(0, 10)) {\n const icon =\n c.type === \"created\"\n ? chalk.cyan(\"+\")\n : c.type === \"modified\"\n ? chalk.yellow(\"~\")\n : c.type === \"moved\"\n ? icons.move\n : chalk.red(\"-\");\n // ๋ณผํŠธ์— ์žˆ๋Š” ๋…ธํŠธ๋Š” ๊ฒฝ๋กœ, ์•„์ง ์—†๋Š” ์ƒˆ ํŽ˜์ด์ง€๋Š” Notion ์ œ๋ชฉ โ€” ๋‚ด๋ถ€ id ๋Š” ๋‘˜ ๋‹ค ์—†์„ ๋•Œ๋งŒ.\n const name = c.path ?? (c.title ? `${c.title} ${dimText(\"(new page)\")}` : undefined);\n console.log(\n ` ${icon} ${name ?? `${c.pageId.slice(0, 8)}...`} ${dimText(`(${c.type})`)}`,\n );\n }\n if (status.remoteChanges.length > 10) {\n console.log(dimText(` ... and ${status.remoteChanges.length - 10} more`));\n }\n }\n\n // Conflicts\n if (conflictCount > 0) {\n console.log(`\\n ${header(chalk.magenta(\"Conflicts:\"))}`);\n // ์›๊ฒฉ์„ ์ฝ์€ ์ƒํƒœ(--full)๋งŒ Notion ์—์„œ ์ง€์šด ๋…ธํŠธ๋ฅผ ๊ฐ€๋ฅธ๋‹ค.\n const remoteDeleted = new Set(\n status.conflicts.filter(isRemoteDeletion).map((c) => c.syncRecord.obsidianPath),\n );\n for (const r of status.conflictRecords) {\n const reason = remoteDeleted.has(r.obsidianPath)\n ? \"(deleted in Notion, local edits not pushed)\"\n : \"(both sides changed)\";\n console.log(` ${icons.conflict} ${r.obsidianPath.padEnd(30)} ${dimText(reason)}`);\n }\n }\n\n // Helpful commands\n const hasChanges =\n status.localChanges.length > 0 ||\n folderMoves.length > 0 ||\n status.remoteChanges.length > 0 ||\n conflictCount > 0;\n if (hasChanges) {\n console.log(\"\");\n if (conflictCount > 0) {\n console.log(\n ` ${dimText(\"Run\")} ${chalk.cyan(\"nobsi resolve\")} ${dimText(\"to resolve conflicts\")}`,\n );\n }\n console.log(\n ` ${dimText(\"Run\")} ${chalk.cyan(\"nobsi sync\")} ${dimText(\"to push/pull changes\")}`,\n );\n // ๋˜๋Œ๋ฆด ์ˆ˜ ์žˆ๋Š” ๊ฒƒ(๊ณ ์นœ ยท ์ง€์šด ๋…ธํŠธ)์ด ์žˆ์„ ๋•Œ๋งŒ โ€” git status ์˜ `git restore` ์•ˆ๋‚ด์™€ ๊ฐ™๋‹ค.\n if (modifiedChanges.length + deletedChanges.length > 0) {\n console.log(\n ` ${dimText(\"Run\")} ${chalk.cyan(\"nobsi discard <path>\")} ${dimText(\"to discard local edits\")}`,\n );\n }\n } else {\n console.log(`\\n ${chalk.green(\"Everything up to date\")} โœ“`);\n }\n if (!opts.full) {\n console.log(\n `\\n ${dimText(\"Run\")} ${chalk.cyan(\"nobsi status --full\")} ${dimText(\"to check Notion remote changes\")}`,\n );\n } else if (status.remoteScan?.deletionsDeferred) {\n // ๋ฐ”๋€ ๊ฒƒ๋งŒ ์ฐพ์•˜๋‹ค โ€” ์œ„ ์›๊ฒฉ ๋ณ€๊ฒฝ์—๋Š” Notion ์—์„œ ์ง€์šด ๊ฒƒ์ด ์•„์ง ์—†๋‹ค.\n console.log(`\\n ${remoteDeletionHint(status.remoteScan)}`);\n }\n } finally {\n stateDb.close();\n }\n });\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n NodeVaultFS,\n formatUnifiedDiff,\n lineDiff,\n matchesPathScope,\n} from \"@im-nobsidian/core\";\nimport type { ChangeDiff } from \"@im-nobsidian/core\";\nimport { printUnifiedDiff } from \"../utils/diff-output.js\";\n\n// ๊ธฐ๋ณธ ๋น„๊ต ๋Œ€์ƒ์€ **๋งˆ์ง€๋ง‰ ๋™๊ธฐํ™” ์Šค๋ƒ…์ƒท**์ด๋‹ค(๋กœ์ปฌ์—์„œ ๋ฌด์—‡์„ ๊ณ ์ณค๋Š”์ง€). Notion ํ˜„์žฌ\n// ๋ณธ๋ฌธ๊ณผ์˜ ๋น„๊ต๋Š” API ํ˜ธ์ถœ์ด ํ•„์š”ํ•˜๋ฏ€๋กœ `--remote` ๋กœ ๋ช…์‹œํ•  ๋•Œ๋งŒ ํ•œ๋‹ค โ€” ์˜ˆ์ „ ์„ค๋ช…์€\n// \"๋กœ์ปฌ๊ณผ Notion ๊ฐ„์˜ ์ฐจ์ด\"๋ผ๊ณ ๋งŒ ์ ์–ด ๋‘๊ณ  ์‹ค์ œ๋กœ๋Š” ์›๊ฒฉ์„ ํ•œ ๋ฒˆ๋„ ์ฝ์ง€ ์•Š์•˜๋‹ค.\nexport const diffCommand = new Command(\"diff\")\n .description(\n \"๋กœ์ปฌ ๋ณ€๊ฒฝ์‚ฌํ•ญ ํ‘œ์‹œ (๊ธฐ๋ณธ: ๋งˆ์ง€๋ง‰ ๋™๊ธฐํ™” ์‹œ์ ๊ณผ ๋น„๊ต, --remote: Notion ํ˜„์žฌ๋ณธ๊ณผ ๋น„๊ต)\",\n )\n .argument(\"[path]\", \"ํŠน์ • ํŒŒ์ผ ๊ฒฝ๋กœ (์ƒ๋žต ์‹œ ๋ณ€๊ฒฝ๋œ ๋ชจ๋“  ํŒŒ์ผ)\")\n .option(\"--color\", \"์ปฌ๋Ÿฌ ์ถœ๋ ฅ\", true)\n .option(\"--remote\", \"Notion ํ˜„์žฌ ๋ณธ๋ฌธ๊ณผ ๋น„๊ต (API ํ˜ธ์ถœ, ๋А๋ฆผ)\")\n .action(async (targetPath: string | undefined, options) => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n try {\n const client = NotionClient.fromConfig(config);\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n const status = await orchestrator.statusLocal();\n\n const changes = targetPath\n ? status.localChanges.filter((c) => matchesPathScope(c.path, targetPath))\n : status.localChanges;\n\n if (changes.length === 0) {\n console.log(\"๋ณ€๊ฒฝ์‚ฌํ•ญ ์—†์Œ\");\n return;\n }\n\n let failed = 0;\n for (const change of changes) {\n // --remote: ๋งˆ์ง€๋ง‰ ์Šค๋ƒ…์ƒท์ด ์•„๋‹ˆ๋ผ Notion ์˜ ํ˜„์žฌ ๋ณธ๋ฌธ์„ ๊ธฐ์ค€์„ ์œผ๋กœ ์‚ผ๋Š”๋‹ค.\n // ์›๊ฒฉ์ด ๊ทธ ์‚ฌ์ด ๋ฐ”๋€Œ์—ˆ์„ ๋•Œ \"๋‚ด๊ฐ€ ๋ญ˜ ๋ฎ์–ด์“ฐ๊ฒŒ ๋˜๋Š”์ง€\"๋Š” ์ด ๋น„๊ต๋กœ๋งŒ ๋ณด์ธ๋‹ค.\n if (options.remote && change.type !== \"deleted\") {\n // ์˜ฎ๊ธด ๋…ธํŠธ๋Š” Notion ์— ๋ฐ˜์˜ํ•˜๊ธฐ ์ „๊นŒ์ง€ ์ถ”์  ๊ธฐ๋ก์ด ์˜› ์ž๋ฆฌ์— ์žˆ๋‹ค โ€” Notion ์ชฝ ์ด๋ฆ„๋„ ๊ทธ ์ž๋ฆฌ๋‹ค.\n let remotePath = change.path;\n let remote = await orchestrator.renderRemoteSnapshot(remotePath);\n if (remote === null && change.movedFrom) {\n remotePath = change.movedFrom;\n remote = await orchestrator.renderRemoteSnapshot(remotePath);\n }\n if (remote === null) {\n console.log(`--- (์›๊ฒฉ ์—†์Œ) ${change.path}`);\n console.log(`+++ b/${change.path}`);\n console.log(`(์•„์ง Notion ์— ์—†๋Š” ํŒŒ์ผ์ž…๋‹ˆ๋‹ค)`);\n console.log(\"\");\n continue;\n }\n const currentContent = await vaultFs.readFile(change.path);\n const hunks = lineDiff(remote, currentContent);\n const lines = formatUnifiedDiff(hunks, `notion/${remotePath}`, `b/${change.path}`);\n // ์ด๋ฆ„ ์ค„๋งŒ ์ฐ๊ณ  ๋๋‚˜๋ฉด ๊ฐ™์€ ๊ฒƒ์ธ์ง€ ๋ชป ์ฝ์€ ๊ฒƒ์ธ์ง€ ์•Œ ์ˆ˜ ์—†๋‹ค โ€” ๊ฐ™๋‹ค๊ณ  ์ ๋Š”๋‹ค.\n if (hunks.length === 0) lines.push(\"(Notion ๊ณผ ๋‚ด์šฉ์ด ๊ฐ™์Šต๋‹ˆ๋‹ค)\");\n printUnifiedDiff(lines, options.color);\n console.log(\"\");\n continue;\n }\n\n // ์ง€๋‚œ ๋™๊ธฐํ™” ๋•Œ์˜ ๊ธ€๊ณผ ์ง€๊ธˆ ๊ธ€ โ€” ํ”Œ๋Ÿฌ๊ทธ์ธ ๋ณ€๊ฒฝ ํŒจ๋„์˜ ๋น„๊ต์™€ ๊ฐ™์€ ๋‘ ๊ธ€์ด๋‹ค.\n let diff: ChangeDiff;\n try {\n diff = await orchestrator.localChangeDiff(change);\n } catch (error) {\n failed++;\n console.log(`--- a/${change.path}`);\n console.log(`(${error instanceof Error ? error.message : String(error)})`);\n console.log(\"\");\n continue;\n }\n printChangeDiff(diff, options.color);\n console.log(\"\");\n }\n // ๋ณด์ด์ง€ ๋ชปํ•œ ๋น„๊ต๊ฐ€ ์žˆ์œผ๋ฉด ์‹คํŒจ๋กœ ๋๋‚ธ๋‹ค โ€” ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๋น ์ง„ ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๊ฒŒ.\n if (failed > 0) process.exitCode = 1;\n } finally {\n stateDb.close();\n }\n });\n\n/** ์˜ฎ๊ธด ๋…ธํŠธ๋Š” Git ์ฒ˜๋Ÿผ ์˜› ์ž๋ฆฌ ยท ์ƒˆ ์ž๋ฆฌ๋ฅผ ๋จผ์ € ์ ๋Š”๋‹ค. ๋‚ด์šฉ์ด ๊ฐ™์œผ๋ฉด ๋น„๊ต ๋Œ€์‹  ๊ทธ๋ ‡๋‹ค๊ณ  ์•Œ๋ฆฐ๋‹ค. */\nfunction printChangeDiff(diff: ChangeDiff, color: boolean): void {\n const oldPath = diff.before === null ? \"/dev/null\" : `a/${diff.movedFrom ?? diff.path}`;\n const newPath = diff.after === null ? \"/dev/null\" : `b/${diff.path}`;\n if (diff.movedFrom) {\n console.log(`rename from ${diff.movedFrom}`);\n console.log(`rename to ${diff.path}`);\n if (diff.before === diff.after) {\n console.log(\"(๋‚ด์šฉ์€ ๊ทธ๋Œ€๋กœ์ž…๋‹ˆ๋‹ค โ€” ์ž๋ฆฌ๋งŒ ์˜ฎ๊ฒผ์Šต๋‹ˆ๋‹ค)\");\n return;\n }\n }\n printUnifiedDiff(\n formatUnifiedDiff(lineDiff(diff.before ?? \"\", diff.after ?? \"\"), oldPath, newPath),\n color,\n );\n}\n","import { DIFF_SIGN } from \"@im-nobsidian/core\";\n\nconst BOLD = \"\\x1b[1m\";\nconst RED = \"\\x1b[31m\";\nconst GREEN = \"\\x1b[32m\";\nconst CYAN = \"\\x1b[36m\";\nconst RESET = \"\\x1b[0m\";\n\n/** ์ค„ ์ฒซ ๊ธ€์ž๋งˆ๋‹ค์˜ ์ƒ‰ โ€” ๋”ํ•œ ์ค„ ยท ์ง€์šด ์ค„์˜ ๋ถ€ํ˜ธ๋Š” core ์˜ ๊ฒƒ์„ ๋”ฐ๋ฅธ๋‹ค. `@` ๋Š” ๋ฌถ์Œ ๋จธ๋ฆฌ. */\nconst LINE_COLOR: Readonly<Record<string, string>> = {\n [DIFF_SIGN.added]: GREEN,\n [DIFF_SIGN.removed]: RED,\n \"@\": CYAN,\n};\n\n/**\n * ํ†ตํ•ฉ diff ์ค„(core `formatUnifiedDiff`)์„ ์ฐ๋Š”๋‹ค โ€” `diff` ยท `resolve` ๊ฐ€ ๊ฐ™์€ ๋ชจ์–‘ ยท ๊ฐ™์€ ์ƒ‰์œผ๋กœ ๋ณด์ธ๋‹ค.\n *\n * ์˜› ๊ธ€ ยท ์ƒˆ ๊ธ€ ์ด๋ฆ„ ์ค„์€ ์ฒซ ๊ธ€์ž๊ฐ€ ์•„๋‹ˆ๋ผ ์ž๋ฆฌ(์•ž ๋‘ ์ค„)๋กœ ๊ฐ€๋ฅธ๋‹ค โ€” ๊ตฌ๋ถ„์„ (`---`)์„ ์ง€์šด ์ค„์€ `----` ๋กœ\n * ์‹œ์ž‘ํ•ด ์ฒซ ๊ธ€์ž๋กœ๋Š” ์ด๋ฆ„ ์ค„๊ณผ ๊ตฌ๋ณ„๋˜์ง€ ์•Š๋Š”๋‹ค.\n */\nexport function printUnifiedDiff(lines: readonly string[], color: boolean): void {\n lines.forEach((line, index) => {\n const code = index < 2 ? BOLD : LINE_COLOR[line.charAt(0)];\n console.log(color && code ? `${code}${line}${RESET}` : line);\n });\n}\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n NodeVaultFS,\n} from \"@im-nobsidian/core\";\nimport { icons, failedItem } from \"../utils/format.js\";\n\n// ๋ณ€๊ฒฝ ๋ชฉ๋ก(status)์—์„œ ๊ณ ๋ฅธ ๋…ธํŠธ๋ฅผ ์ง€๋‚œ ๋™๊ธฐํ™” ๋•Œ์˜ ๊ธ€๋กœ ๋˜๋Œ๋ฆฐ๋‹ค โ€” Git ์˜ `restore` ์™€ ๊ฐ™๋‹ค.\n// ํ”Œ๋Ÿฌ๊ทธ์ธ ๋ณ€๊ฒฝ ํŒจ๋„์˜ โ†บ ์™€ ๊ฐ™์€ ๋™์ž‘(orchestrator.discardLocalChange)์ด๊ณ  Notion ์€ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค.\n// ํ•˜๋‚˜๋ฅผ ๋˜๋Œ๋ฆฌ์ง€ ๋ชปํ•ด๋„ ๋‚˜๋จธ์ง€๋Š” ๋˜๋Œ๋ฆฐ๋‹ค โ€” ๋ฌด์—‡์ด ์™œ ์•ˆ ๋๋Š”์ง€ ๊ฒฝ๋กœ๋งˆ๋‹ค ์•Œ๋ฆฌ๊ณ  ์ข…๋ฃŒ ์ฝ”๋“œ๋กœ ๋‚จ๊ธด๋‹ค.\nexport const discardCommand = new Command(\"discard\")\n .description(\"๋กœ์ปฌ ๋ณ€๊ฒฝ์„ ์ง€๋‚œ ๋™๊ธฐํ™” ๋•Œ์˜ ๊ธ€๋กœ ๋˜๋Œ๋ฆฌ๊ธฐ (Notion ์€ ๊ทธ๋Œ€๋กœ)\")\n .argument(\"<paths...>\", \"๋˜๋Œ๋ฆด ๋…ธํŠธ ๊ฒฝ๋กœ โ€” status ๊ฐ€ ๋ณด์ธ ๋ณผํŠธ ๊ธฐ์ค€ ๊ฒฝ๋กœ\")\n .action(async (paths: string[]) => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n try {\n const client = NotionClient.fromConfig(config);\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n let failed = 0;\n for (const path of paths) {\n try {\n await orchestrator.discardLocalChange(path);\n console.log(` ${icons.success} ${path}`);\n } catch (error) {\n failed++;\n failedItem(path, error instanceof Error ? error.message : String(error));\n }\n }\n if (failed > 0) process.exitCode = 1;\n } finally {\n stateDb.close();\n }\n });\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n NodeVaultFS,\n} from \"@im-nobsidian/core\";\nimport chalk from \"chalk\";\nimport { header, separator, dimText } from \"../utils/format.js\";\n\nexport const fetchCommand = new Command(\"fetch\")\n .description(\"Notion ๋ฆฌ๋ชจํŠธ ์ƒํƒœ ์Šค์บ” (์‚ญ์ œ ๊ฐ์ง€ ํฌํ•จ)\")\n .action(async () => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n try {\n const client = NotionClient.fromConfig(config);\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n console.log(dimText(\" Scanning remote pages...\"));\n\n const result = await orchestrator.fetch();\n\n console.log(`\\n${header(\" Remote Scan\")}`);\n console.log(` ${separator(40)}`);\n\n if (result.newPages > 0) {\n console.log(` ${chalk.cyan(\"+\")} New pages: ${result.newPages}`);\n }\n if (result.modifiedPages > 0) {\n console.log(` ${chalk.yellow(\"~\")} Modified pages: ${result.modifiedPages}`);\n }\n if (result.deletedPages > 0) {\n console.log(` ${chalk.red(\"-\")} Deleted pages: ${result.deletedPages}`);\n }\n\n const total = result.newPages + result.modifiedPages + result.deletedPages;\n if (total === 0) {\n console.log(` ${chalk.green(\"Remote is up to date\")} โœ“`);\n } else {\n console.log(\n `\\n ${dimText(\"Run\")} ${chalk.cyan(\"nobsi pull\")} ${dimText(\"to apply remote changes\")}`,\n );\n }\n\n console.log(dimText(`\\n Completed in ${(result.duration / 1000).toFixed(1)}s`));\n } finally {\n stateDb.close();\n }\n });\n","import { Command } from \"commander\";\nimport { select, confirm } from \"@inquirer/prompts\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n NodeVaultFS,\n applicableChoices,\n isRemoteDeletion,\n} from \"@im-nobsidian/core\";\nimport type { Conflict, ResolutionChoice } from \"@im-nobsidian/core\";\nimport { printUnifiedDiff } from \"../utils/diff-output.js\";\nimport { describeFailure } from \"../utils/state-db-failure.js\";\n\n/** ํ—ˆ์šฉ ์ „๋žต โ€” ์˜คํƒ€๋ฅผ ์กฐ์šฉํžˆ ํ˜๋ฆฌ์ง€ ์•Š๋„๋ก ์‹คํ–‰ ์ „์— ํ•œ ๋ฒˆ ๊ฒ€์ฆํ•œ๋‹ค. */\nconst STRATEGIES = [\"local-first\", \"remote-first\", \"duplicate\"] as const;\ntype Strategy = (typeof STRATEGIES)[number];\n\nconst CHOICE_NAMES: Record<ResolutionChoice, string> = {\n local: \"๋กœ์ปฌ ์œ ์ง€ (Obsidian ๋ฒ„์ „ ์œ ์ง€)\",\n remote: \"์›๊ฒฉ ์œ ์ง€ (Notion ๋ฒ„์ „์œผ๋กœ ๋ฎ์–ด์“ฐ๊ธฐ)\",\n merge: \"์ž๋™ ๋ณ‘ํ•ฉ (3-way merge)\",\n duplicate: \"๋ณต์ œ (์›๋ณธ ์œ ์ง€ + ์˜ฌ๋ฆฌ์ง€ ์•Š๋Š” .conflict ์‚ฌ๋ณธ ์ƒ์„ฑ)\",\n};\n\n/** Notion ์—์„œ ์ง€์šด ๋…ธํŠธ๋Š” ๊ฐ™์€ ์„ ํƒ์ด ๋‹ค๋ฅธ ์ผ์„ ํ•œ๋‹ค โ€” ๋กœ์ปฌ ์œ ์ง€๋Š” ๋‹ค์‹œ ๋งŒ๋“ค๊ณ , ์›๊ฒฉ ์œ ์ง€๋Š” ์ง€์šด๋‹ค. */\nconst REMOTE_DELETION_CHOICE_NAMES: Partial<Record<ResolutionChoice, string>> = {\n local: \"๋กœ์ปฌ ์œ ์ง€ (Notion ์— ์ƒˆ ํŽ˜์ด์ง€๋กœ ๋‹ค์‹œ ๋งŒ๋“ค๊ธฐ)\",\n remote: \"์‚ญ์ œ ๋”ฐ๋ฅด๊ธฐ (Obsidian ํŒŒ์ผ๋„ ์ง€์šฐ๊ธฐ)\",\n};\n\n/** ๋ฐ›๊ธฐ ์ „์šฉ(direction: pull)์€ ํ•ด๊ฒฐ ๊ฒฐ๊ณผ๋ฅผ Notion ์— ์˜ฌ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค โ€” ๋‹ค์‹œ ๋งŒ๋“ค์ง€๋„ ์•Š๋Š”๋‹ค. */\nconst PULL_ONLY_REMOTE_DELETION_CHOICE_NAMES: Partial<Record<ResolutionChoice, string>> = {\n local: \"๋กœ์ปฌ ์œ ์ง€ (Obsidian ํŒŒ์ผ๋งŒ ๋‘๊ธฐ โ€” ๋ฐ›๊ธฐ ์ „์šฉ์ด๋ผ Notion ์— ๋งŒ๋“ค์ง€ ์•Š์Œ)\",\n};\n\nfunction choiceName(conflict: Conflict, choice: ResolutionChoice, uploads: boolean): string {\n const deleted = isRemoteDeletion(conflict);\n return (\n (deleted && !uploads ? PULL_ONLY_REMOTE_DELETION_CHOICE_NAMES[choice] : undefined) ??\n (deleted ? REMOTE_DELETION_CHOICE_NAMES[choice] : undefined) ??\n CHOICE_NAMES[choice] ??\n choice\n );\n}\n\n/**\n * ๋Œ€ํ™”ํ˜• ํ”„๋กฌํ”„ํŠธ๋ฅผ ๋„์šธ ์ˆ˜ ์žˆ๋Š” ํ™˜๊ฒฝ์ธ์ง€.\n *\n * ํŒŒ์ดํ”„ยทcronยทCI ์ฒ˜๋Ÿผ TTY ๊ฐ€ ์—†๋Š” ๊ณณ์—์„œ @inquirer ํ”„๋กฌํ”„ํŠธ๋ฅผ ๋„์šฐ๋ฉด ์›์ธ์„ ์•Œ ์ˆ˜ ์—†๋Š”\n * ์˜ค๋ฅ˜๋กœ ์ฃฝ๊ฑฐ๋‚˜ ์ž…๋ ฅ์„ ๊ธฐ๋‹ค๋ฆฌ๋ฉฐ ๋ฉŽ๋Š”๋‹ค. ๋จผ์ € ํŒ์ •ํ•ด ๋ฌด์—‡์„ ์–ด๋–ป๊ฒŒ ํ•˜๋ผ๊ณ  ๋งํ•ด ์ค€๋‹ค.\n */\nfunction isInteractive(): boolean {\n return process.stdin.isTTY === true && process.stdout.isTTY === true;\n}\n\nexport const resolveCommand = new Command(\"resolve\")\n .description(\"์ถฉ๋Œ์„ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค\")\n .option(\"-s, --strategy <strategy>\", `์ž๋™ ํ•ด๊ฒฐ ์ „๋žต (${STRATEGIES.join(\" | \")})`)\n .option(\"-y, --yes\", \"ํ™•์ธ ํ”„๋กฌํ”„ํŠธ ์—†์ด ์ง„ํ–‰ (๋น„๋Œ€ํ™”ํ˜•/CI)\")\n .action(async (options: { strategy?: string; yes?: boolean }) => {\n if (options.strategy && !STRATEGIES.includes(options.strategy as Strategy)) {\n console.error(`์•Œ ์ˆ˜ ์—†๋Š” ์ „๋žต '${options.strategy}' โ€” ์‚ฌ์šฉ ๊ฐ€๋Šฅ: ${STRATEGIES.join(\", \")}`);\n process.exitCode = 1;\n return;\n }\n\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n let stateDb: StateDB | undefined;\n\n try {\n const config = await configManager.load();\n stateDb = StateDB.open(configManager.dbPath);\n const client = NotionClient.fromConfig(config);\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n // ์ถฉ๋Œ ๋ชฉ๋ก์€ **์ถฉ๋Œ๋กœ ํ‘œ์‹œ๋œ ํŽ˜์ด์ง€๋งŒ** ์ฝ์–ด ์˜จ๋‹ค. ์˜ˆ์ „์—” ๋ชฉ๋ก์„ ์–ป๊ฒ ๋‹ค๊ณ  ์ „์ฒด pull ์„\n // ๋Œ๋ ธ๋Š”๋ฐ, ๊ทธ๊ฑด ํ•ด์†Œํ•˜๊ธฐ๋„ ์ „์— ๋ณผํŠธ๋ฅผ ์›๊ฒฉ์œผ๋กœ ๋ฎ์–ด์“ฐ๋Š” ์ง“์ด๋ผ ์ˆœ์„œ๊ฐ€ ๊ฑฐ๊พธ๋กœ์˜€๋‹ค.\n const conflicts = await orchestrator.listConflicts();\n\n if (conflicts.length === 0) {\n console.log(\"์ถฉ๋Œ์ด ์—†์Šต๋‹ˆ๋‹ค.\");\n return;\n }\n\n // ์–‘์ชฝ์ด ์ด๋ฏธ ๊ฐ™์•„์ง„ ํ•ญ๋ชฉ์€ ๋ฌผ์–ด๋ณผ ๊ฒŒ ์—†๋‹ค. ๊ทธ๋Œ€๋กœ ๋‘๋ฉด push ๊ฐ€ ๊ทธ ํŒŒ์ผ์„ ์˜์˜\n // ๊ฑด๋„ˆ๋›ฐ์–ด(์ถฉ๋Œ ์ƒํƒœ๋Š” push ์ œ์™ธ) ์ดํ›„ ํŽธ์ง‘์ด ์กฐ์šฉํžˆ ์ •์ฒดํ•˜๋ฏ€๋กœ ๋จผ์ € ๊ฑท์–ด๋‚ธ๋‹ค.\n const cleared = new Set(orchestrator.clearStaleConflicts(conflicts));\n for (const path of cleared) {\n console.log(` \\x1b[32mโœ“\\x1b[0m ${path} โ†’ ์–‘์ชฝ ๋™์ผ, ์ถฉ๋Œ ํ‘œ์‹œ๋งŒ ํ•ด์ œ`);\n }\n\n const pending = conflicts.filter((c) => !cleared.has(c.syncRecord.obsidianPath));\n if (pending.length === 0) {\n console.log(`\\n${cleared.size}๊ฑด ๋ชจ๋‘ ์ด๋ฏธ ํ•ด์†Œ๋œ ์ƒํƒœ์˜€์Šต๋‹ˆ๋‹ค โ€” ์ƒํƒœ๋งŒ ์ •๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.`);\n return;\n }\n\n console.log(`\\n์ถฉ๋Œ ${pending.length}๊ฑด ๋ฐœ๊ฒฌ\\n`);\n const uploads = config.sync.direction !== \"pull\";\n if (!uploads) {\n console.log(\n \" ๋ฐ›๊ธฐ ์ „์šฉ(direction: pull) โ€” ํ•ด๊ฒฐ ๊ฒฐ๊ณผ๋Š” Obsidian ์—๋งŒ ๋‘๊ณ  Notion ์—๋Š” ์˜ฌ๋ฆฌ์ง€ ์•Š์Šต๋‹ˆ๋‹ค\\n\",\n );\n }\n\n if (options.strategy) {\n const strategy = options.strategy as Strategy;\n // ์›๊ฒฉ์—์„œ ์ง€์šด ๋…ธํŠธ๋Š” ์ „๋žต์ด ํŒŒ์ผ์„ ์ง€์šฐ๊ฑฐ๋‚˜ ํŽ˜์ด์ง€๋ฅผ ์ƒˆ๋กœ ๋งŒ๋“ ๋‹ค โ€” ๋ฌป๊ธฐ ์ „์— ์•Œ๋ฆฐ๋‹ค.\n const deletions = pending.filter(isRemoteDeletion).length;\n if (deletions > 0) {\n console.log(\n ` Notion ์—์„œ ์‚ญ์ œ๋œ ๋…ธํŠธ ${deletions}๊ฑด โ€” ${\n strategy === \"remote-first\"\n ? \"Obsidian ํŒŒ์ผ๋„ ์ง€์›๋‹ˆ๋‹ค\"\n : uploads\n ? \"Notion ์— ์ƒˆ ํŽ˜์ด์ง€๋กœ ๋‹ค์‹œ ๋งŒ๋“ญ๋‹ˆ๋‹ค\"\n : \"Obsidian ํŒŒ์ผ์„ ๊ทธ๋Œ€๋กœ ๋‘ก๋‹ˆ๋‹ค\"\n }`,\n );\n }\n\n if (!options.yes) {\n if (!isInteractive()) {\n console.error(\n `๋น„๋Œ€ํ™”ํ˜• ํ™˜๊ฒฝ์ž…๋‹ˆ๋‹ค โ€” '${strategy}' ์ „๋žต์œผ๋กœ ${pending.length}๊ฑด์„ ์ผ๊ด„ ํ•ด๊ฒฐํ•˜๋ ค๋ฉด --yes ๋ฅผ ํ•จ๊ป˜ ์ง€์ •ํ•˜์„ธ์š”.`,\n );\n process.exitCode = 1;\n return;\n }\n const shouldProceed = await confirm({\n message: `${pending.length}๊ฑด์˜ ์ถฉ๋Œ์„ '${strategy}' ์ „๋žต์œผ๋กœ ์ผ๊ด„ ํ•ด๊ฒฐํ• ๊นŒ์š”?`,\n });\n if (!shouldProceed) return;\n }\n\n const results = await orchestrator.resolveAllConflicts(pending, strategy);\n let failed = 0;\n for (const [index, result] of results.entries()) {\n if (!result.success) failed++;\n const icon = result.success ? \"\\x1b[32mโœ“\\x1b[0m\" : \"\\x1b[31mโœ—\\x1b[0m\";\n const reason = result.error ? ` โ€” ${result.error}` : \"\";\n console.log(\n ` ${icon} ${result.path} โ†’ ${choiceName(pending[index]!, result.choice, uploads)}${reason}`,\n );\n }\n report(results.length - failed, failed);\n return;\n }\n\n if (!isInteractive()) {\n console.error(\n `๋น„๋Œ€ํ™”ํ˜• ํ™˜๊ฒฝ์—์„œ๋Š” ๋Œ€ํ™”ํ˜• ํ•ด๊ฒฐ์„ ์“ธ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค โ€” --strategy <${STRATEGIES.join(\"|\")}> --yes ๋ฅผ ์ง€์ •ํ•˜์„ธ์š”.`,\n );\n process.exitCode = 1;\n return;\n }\n\n let resolved = 0;\n let failed = 0;\n for (const conflict of pending) {\n if (await resolveInteractive(conflict, orchestrator, uploads)) resolved++;\n else failed++;\n }\n report(resolved, failed);\n } catch (error) {\n console.error(\"์˜ค๋ฅ˜:\", describeFailure(error));\n process.exitCode = 1;\n } finally {\n // ์˜ˆ์ „์—” ๋ถ„๊ธฐ๋งˆ๋‹ค close ๋ฅผ ํฉ๋ฟŒ๋ ค ๋‘๊ณ  catch ๊ฒฝ๋กœ์—์„  ์•„์˜ˆ ๋‹ซ์ง€ ์•Š์•˜๋‹ค โ€” ์ž ๊ธˆ์ด ๋‚จ๋Š”๋‹ค.\n stateDb?.close();\n }\n });\n\n/** ๊ฒฐ๊ณผ๋ฅผ ์‚ฌ์‹ค๋Œ€๋กœ ์•Œ๋ฆฐ๋‹ค โ€” ์‹คํŒจ๊ฐ€ ํ•˜๋‚˜๋ผ๋„ ์žˆ์œผ๋ฉด \"์™„๋ฃŒ\"๋ผ๊ณ  ๋งํ•˜์ง€ ์•Š๋Š”๋‹ค. */\nfunction report(resolved: number, failed: number): void {\n if (failed === 0) {\n console.log(`\\n์ถฉ๋Œ ํ•ด๊ฒฐ ์™„๋ฃŒ โ€” ${resolved}๊ฑด`);\n return;\n }\n console.log(`\\n\\x1b[33m์ถฉ๋Œ ${resolved}๊ฑด ํ•ด๊ฒฐ ยท ${failed}๊ฑด ๋ฏธํ•ด๊ฒฐ\\x1b[0m`);\n process.exitCode = 1;\n}\n\n/** @returns ํ•ด์†Œ์— ์„ฑ๊ณตํ–ˆ์œผ๋ฉด true. ๋ณ‘ํ•ฉ ๋งˆ์ปค๊ฐ€ ๋‚จ์€ ๊ฒฝ์šฐ๋Š” ๋ฏธํ•ด๊ฒฐ๋กœ ์„ผ๋‹ค. */\nasync function resolveInteractive(\n conflict: Conflict,\n orchestrator: SyncOrchestrator,\n uploads: boolean,\n): Promise<boolean> {\n const path = conflict.syncRecord.obsidianPath;\n\n console.log(`\\n${\"โ”€\".repeat(60)}`);\n console.log(`ํŒŒ์ผ: \\x1b[1m${path}\\x1b[0m`);\n console.log(`${\"โ”€\".repeat(60)}`);\n\n if (isRemoteDeletion(conflict)) {\n // ๊ฒฌ์ค„ ์›๊ฒฉ ๋ณธ๋ฌธ์ด ์—†๋‹ค โ€” ์ค„ ๋น„๊ต๋Š” ๋กœ์ปฌ ์ „๋ถ€๋ฅผ ์ง€์šด ๊ฒƒ์œผ๋กœ ๋ณด์—ฌ ์ค€๋‹ค.\n console.log(\n \"\\x1b[33mNotion ์—์„œ ์‚ญ์ œ๋œ ๋…ธํŠธ์ž…๋‹ˆ๋‹ค โ€” Notion ์— ์˜ฌ๋ฆฌ์ง€ ์•Š์€ ๋กœ์ปฌ ํŽธ์ง‘์ด ๋‚จ์•„ ์žˆ์Šต๋‹ˆ๋‹ค.\\x1b[0m\",\n );\n } else {\n printUnifiedDiff(orchestrator.generateConflictDiff(conflict).split(\"\\n\"), true);\n }\n\n const choice = await select<ResolutionChoice>({\n message: \"์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ• ๊นŒ์š”?\",\n choices: applicableChoices(conflict).map((value) => ({\n name: choiceName(conflict, value, uploads),\n value,\n })),\n });\n\n const result = await orchestrator.resolveConflict(conflict, choice);\n\n if (result.success) {\n console.log(`\\x1b[32mโœ“ ${path} โ†’ ${choiceName(conflict, choice, uploads)}\\x1b[0m`);\n return true;\n }\n if (result.mergeHadConflicts) {\n console.log(`\\x1b[33mโš  ${path} โ†’ ์ž๋™ ๋ณ‘ํ•ฉ ์™„๋ฃŒ (์ˆ˜๋™ ํ™•์ธ ํ•„์š”ํ•œ ์ถฉ๋Œ ๋งˆ์ปค ์žˆ์Œ)\\x1b[0m`);\n return false;\n }\n console.log(`\\x1b[31mโœ— ${path} โ†’ ํ•ด๊ฒฐ ์‹คํŒจ\\x1b[0m`);\n return false;\n}\n","/**\n * ์ƒํƒœ DB ๋ฅผ ์“ธ ์ˆ˜ ์—†์„ ๋•Œ์˜ CLI ์ถœ๋ ฅ โ€” ์ด์œ  ๋’ค์— ํ•  ์ผ์„ ๋ถ™์ธ๋‹ค.\n *\n * ๋ช…๋ น์€ ์ฒ˜์Œ์— ์ƒํƒœ DB ๋ฅผ ์—ฐ๋‹ค(`StateDB.open`). ํŒŒ์ผ์ด ๋น„์—ˆ๊ฑฐ๋‚˜ ์ž˜๋ ธ๊ฑฐ๋‚˜(`SavedStateDbError`), ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๊ฐ™์€\n * ๋ณผํŠธ๋ฅผ ์“ฐ๋Š” ์ค‘์ด๋ฉด(`StateDbLockedError`) ๋˜์ง€๋Š”๋ฐ, ๋ช…๋ น ๋ฐ–์œผ๋กœ ๋‚˜์˜ค๋ฉด Node ๊ฐ€ ์ฝ”๋“œ ์œ„์น˜(์Šคํƒ)์™€ ํ•จ๊ป˜ ์ด์œ ๋งŒ\n * ๋ณด์˜€๋‹ค โ€” ์‚ฌ์šฉ์ž๊ฐ€ ํ•  ์ผ์€ ์–ด๋””์—๋„ ์—†์—ˆ๋‹ค.\n */\nimport { StateDbUnavailableError } from \"@im-nobsidian/core\";\nimport type { ExitableProcess } from \"./exit.js\";\n\n/** ์‹คํŒจ๋ฅผ ๋ณด์ผ ๋ง โ€” ์ƒํƒœ DB ๋ฅผ ์“ธ ์ˆ˜ ์—†๋Š” ๊ฒƒ์ด๋ฉด ํ•  ์ผ์„ ๋ถ™์ด๊ณ , ๋‹ค๋ฅธ ์‹คํŒจ๋Š” ๊ทธ ๋ง ๊ทธ๋Œ€๋กœ๋‹ค. */\nexport function describeFailure(error: unknown): string {\n const message = error instanceof Error ? error.message : String(error);\n if (!(error instanceof StateDbUnavailableError)) return message;\n return `${message} โ€” ${error.guidance(\"๋ช…๋ น์„ ๋‹ค์‹œ ์‹คํ–‰ํ•˜์„ธ์š”\")}`;\n}\n\n/**\n * ๋ช…๋ น ๋ฐ–์œผ๋กœ ๋‚˜์˜จ ์‹คํŒจ๋ฅผ ์•Œ๋ฆฐ๋‹ค. ์ƒํƒœ DB ๋ฅผ ์“ธ ์ˆ˜ ์—†๋Š” ๊ฒƒ์€ ์‚ฌ์šฉ์ž๊ฐ€ ํ’€ ์ผ์ด๋ผ ์ด์œ ์™€ ํ•  ์ผ๋งŒ ๋ณด์ด๊ณ  ์‹คํŒจ(1)๋กœ\n * ๋๋‚ธ๋‹ค. ๋‹ค๋ฅธ ์‹คํŒจ๋Š” ๊ทธ๋Œ€๋กœ ๋‹ค์‹œ ๋˜์ง„๋‹ค โ€” ์ง€๊ธˆ๊นŒ์ง€์ฒ˜๋Ÿผ Node ๊ฐ€ ์Šคํƒ๊ณผ ํ•จ๊ป˜ ๋ณด์ด๊ณ  1 ๋กœ ๋๋‚œ๋‹ค.\n */\nexport function reportStateDbFailure(error: unknown, proc: ExitableProcess): void {\n if (!(error instanceof StateDbUnavailableError)) throw error;\n console.error(`์˜ค๋ฅ˜: ${describeFailure(error)}`);\n proc.exitCode = 1;\n}\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n NodeVaultFS,\n WatchSyncService,\n DEFAULT_WATCH_DEBOUNCE_MS,\n} from \"@im-nobsidian/core\";\nimport type { SyncResult, WatchSyncScope } from \"@im-nobsidian/core\";\n\nfunction formatResult(result: SyncResult): string {\n const pull = `Pull: +${result.pull.created} ~${result.pull.updated} -${result.pull.deleted}`;\n const moved = result.push.moved > 0 ? ` โ†’${result.push.moved}` : \"\";\n const push = `Push: +${result.push.created} ~${result.push.updated} -${result.push.deleted}${moved}`;\n const parts = [pull, push];\n\n if (result.conflicts.length > 0) {\n parts.push(`์ถฉ๋Œ ${result.conflicts.length}๊ฑด`);\n }\n\n return parts.join(\" | \");\n}\n\nfunction timestamp(): string {\n return new Date().toLocaleTimeString(\"ko-KR\", { hour12: false });\n}\n\n/** ๋กœ๊ทธ์˜ sync ์ด๋ฆ„ โ€” ์ฃผ๊ธฐ sync ๋Š” ๋ณผํŠธ ์ „์ฒด๋ฅผ ๋ณธ๋‹ค. */\nfunction syncLabel(scope: WatchSyncScope): string {\n return scope === \"full\" ? \"์ฃผ๊ธฐ์  ๋™๊ธฐํ™”\" : \"๋™๊ธฐํ™”\";\n}\n\nexport const watchCommand = new Command(\"watch\")\n .description(\"ํŒŒ์ผ ๋ณ€๊ฒฝ ๊ฐ์ง€ + ์ž๋™ ๋™๊ธฐํ™”\")\n .option(\"-d, --debounce <ms>\", \"๋””๋ฐ”์šด์Šค ๋Œ€๊ธฐ ์‹œ๊ฐ„(ms)\", String(DEFAULT_WATCH_DEBOUNCE_MS))\n .option(\"-i, --interval <seconds>\", \"ํ’€ ๋™๊ธฐํ™” ์ฃผ๊ธฐ(์ดˆ, 0์ด๋ฉด ๋น„ํ™œ์„ฑ)\", \"0\")\n .action(async (options: { debounce: string; interval: string }) => {\n // ์ˆซ์ž๊ฐ€ ์•„๋‹ˆ๋ฉด ๋””๋ฐ”์šด์Šค๊ฐ€ 0 ์ด ๋˜๊ฑฐ๋‚˜ ์ฃผ๊ธฐ ๋™๊ธฐํ™”๊ฐ€ ๋ง์—†์ด ๊บผ์ง„๋‹ค โ€” ์‹œ์ž‘ํ•˜๊ธฐ ์ „์— ๊ฑฐ์ ˆํ•œ๋‹ค.\n const debounceMs = Number(options.debounce);\n const intervalSec = Number(options.interval);\n if (!Number.isInteger(debounceMs) || debounceMs < 0) {\n console.error(`--debounce ๋Š” 0 ์ด์ƒ์˜ ์ •์ˆ˜(ms)์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค: ${options.debounce}`);\n process.exitCode = 1;\n return;\n }\n if (!Number.isInteger(intervalSec) || intervalSec < 0) {\n console.error(`--interval ์€ 0 ์ด์ƒ์˜ ์ •์ˆ˜(์ดˆ)์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค: ${options.interval}`);\n process.exitCode = 1;\n return;\n }\n\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n const client = NotionClient.fromConfig(config);\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n const service = new WatchSyncService(cwd, orchestrator, {\n debounceMs,\n onFileChange: (event, path) => {\n console.log(`[${timestamp()}] ${event}: ${path}`);\n },\n onSyncStart: (scope) => {\n console.log(`[${timestamp()}] ${syncLabel(scope)} ์‹œ์ž‘...`);\n },\n onSyncComplete: (result, scope) => {\n console.log(`[${timestamp()}] ${syncLabel(scope)} ์™„๋ฃŒ โ€” ${formatResult(result)}`);\n },\n onSyncCancelled: (result, scope) => {\n console.log(\n `[${timestamp()}] ${syncLabel(scope)} ์ทจ์†Œ โ€” ๋ฉˆ์ถ”๊ธฐ ์ „๊นŒ์ง€ ${formatResult(result)}`,\n );\n },\n onSyncError: (error, scope) => {\n console.error(`[${timestamp()}] ${syncLabel(scope)} ์‹คํŒจ: ${error.message}`);\n },\n });\n\n let pullTimer: ReturnType<typeof setInterval> | null = null;\n let shuttingDown = false;\n\n const shutdown = async (): Promise<void> => {\n // ๋„๋Š” sync ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋Š” ์ค‘์— ํ•œ ๋ฒˆ ๋” ๋ˆ„๋ฅด๋ฉด ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  ๋๋‚ธ๋‹ค.\n if (shuttingDown) {\n console.log(`[${timestamp()}] ๊ฐ•์ œ ์ข…๋ฃŒ`);\n return process.exit(1);\n }\n shuttingDown = true;\n console.log(`\\n[${timestamp()}] ๊ฐ์‹œ ์ข…๋ฃŒ ์ค‘... (๋„๋Š” ๋™๊ธฐํ™”๋ฅผ ๋ฉˆ์ถ”๊ณ  ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค)`);\n if (pullTimer) clearInterval(pullTimer);\n await service.stop();\n stateDb.close();\n console.log(`[${timestamp()}] ์ข…๋ฃŒ ์™„๋ฃŒ`);\n process.exit(0);\n };\n\n process.on(\"SIGINT\", () => void shutdown());\n process.on(\"SIGTERM\", () => void shutdown());\n\n service.start();\n console.log(`[${timestamp()}] ํŒŒ์ผ ๊ฐ์‹œ ์‹œ์ž‘ (debounce: ${debounceMs}ms)`);\n\n if (intervalSec > 0) {\n console.log(`[${timestamp()}] ํ’€ ๋™๊ธฐํ™” ์ฃผ๊ธฐ: ${intervalSec}์ดˆ`);\n // ํŒŒ์ผ ๋ณ€๊ฒฝ sync ์™€ ๊ฐ™์€ ์ค„์— ์„ธ์šด๋‹ค โ€” ๋„๋Š” sync ๊ฐ€ ์žˆ์œผ๋ฉด ๋๋‚œ ๋’ค ํ•œ ๋ฒˆ ๋ˆ๋‹ค(S-09).\n pullTimer = setInterval(() => service.requestFullSync(), intervalSec * 1000);\n }\n\n await new Promise(() => {});\n });\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n NodeVaultFS,\n} from \"@im-nobsidian/core\";\nimport type {\n CompletenessReport,\n PageCompletenessReport,\n VaultCompletenessReport,\n} from \"@im-nobsidian/core\";\nimport chalk from \"chalk\";\nimport { header, separator, icons, dimText } from \"../utils/format.js\";\n\n/** ํ™”๋ฉด์— ๋Š˜์–ด๋†“์„ id ์ตœ๋Œ€ ๊ฐœ์ˆ˜ โ€” ๋‚˜๋จธ์ง€๋Š” ๊ฑด์ˆ˜๋กœ ์ ‘๋Š”๋‹ค. */\nconst SAMPLE_LIMIT = 10;\n\nfunction printIdSample(label: string, ids: readonly string[], color: (s: string) => string): void {\n console.log(` ${color(label)} ${ids.length}`);\n for (const id of ids.slice(0, SAMPLE_LIMIT)) {\n console.log(` ${dimText(id)}`);\n }\n if (ids.length > SAMPLE_LIMIT) {\n console.log(dimText(` ... and ${ids.length - SAMPLE_LIMIT} more`));\n }\n}\n\nfunction printDatabaseSection(report: CompletenessReport): void {\n console.log(`\\n${header(\" DB Completeness\")}`);\n console.log(` ${separator(50)}`);\n console.log(` ${dimText(\"Databases:\")} ${report.databases.length}`);\n console.log(` ${dimText(\"Remote rows:\")} ${report.remoteTotal}`);\n console.log(` ${dimText(\"Vault rows:\")} ${report.localTotal}`);\n\n const broken = report.databases.filter((d) => d.missingIds.length > 0 || d.extraIds.length > 0);\n\n if (broken.length > 0) {\n console.log(`\\n ${header(chalk.red(`Incomplete databases: ${broken.length}`))}`);\n for (const db of broken) {\n console.log(\n ` ${icons.fail} ${db.databaseId} ${dimText(`remote ${db.remoteRows} / vault ${db.localRows}`)}`,\n );\n if (db.missingIds.length > 0) printIdSample(\"missing\", db.missingIds, chalk.red);\n if (db.extraIds.length > 0) printIdSample(\"extra\", db.extraIds, chalk.yellow);\n }\n }\n\n if (report.failures.length > 0) {\n console.log(`\\n ${header(chalk.yellow(`Query failures: ${report.failures.length}`))}`);\n for (const failure of report.failures) {\n console.log(` ${icons.fail} ${failure.databaseId} ${dimText(failure.error)}`);\n }\n }\n\n if (report.complete) {\n console.log(`\\n ${chalk.green(\"All database rows present in vault\")} ${icons.success}`);\n }\n}\n\n/**\n * ํŽ˜์ด์ง€ ๋Œ€์กฐ ์ถœ๋ ฅ (R12-C).\n *\n * `local-only` ๋Š” ์‹คํŒจ๊ฐ€ ์•„๋‹ˆ๋ผ ์ •๋ณด๋‹ค โ€” ์•„์ง push ํ•˜์ง€ ์•Š์€ ๋กœ์ปฌ ๋…ธํŠธยทroot ํŽ˜์ด์ง€ ์ž์‹ ยท\n * search ์ƒ‰์ธ ์ง€์—ฐ์ด ์ „๋ถ€ ์—ฌ๊ธฐ์— ์ •์ƒ์ ์œผ๋กœ ๋“ค์–ด์˜จ๋‹ค. ์ƒ‰์„ ๋‹ฌ๋ฆฌ ์ค˜์„œ \"๊ณ ์ณ์•ผ ํ•  ๊ฒƒ\"๊ณผ\n * \"๊ทธ๋ƒฅ ์•Œ์•„ ๋‘˜ ๊ฒƒ\"์ด ํ™”๋ฉด์—์„œ ์„ž์ด์ง€ ์•Š๊ฒŒ ํ•œ๋‹ค.\n */\nfunction printPageSection(report: PageCompletenessReport): void {\n console.log(`\\n${header(\" Page Completeness\")}`);\n console.log(` ${separator(50)}`);\n console.log(` ${dimText(\"Remote pages:\")} ${report.remotePages}`);\n console.log(` ${dimText(\"Vault pages:\")} ${report.vaultPages}`);\n\n if (report.error) {\n console.log(`\\n ${icons.fail} ${chalk.yellow(\"Page enumeration failed\")}`);\n console.log(` ${dimText(report.error)}`);\n return;\n }\n\n if (report.missingIds.length > 0) {\n console.log(\n `\\n ${header(chalk.red(`Pages missing from vault: ${report.missingIds.length}`))}`,\n );\n printIdSample(\"missing\", report.missingIds, chalk.red);\n }\n\n if (report.localOnlyIds.length > 0) {\n console.log(\n `\\n ${dimText(`local-only pages: ${report.localOnlyIds.length} (informational)`)}`,\n );\n printIdSample(\"local-only\", report.localOnlyIds, dimText);\n }\n\n if (report.complete) {\n console.log(`\\n ${chalk.green(\"All remote pages present in vault\")} ${icons.success}`);\n }\n}\n\nfunction printReport(report: VaultCompletenessReport): void {\n printDatabaseSection(report.databases);\n\n if (report.pages) {\n printPageSection(report.pages);\n } else {\n // DB ๋ชจ๋“œ์—๋Š” root ์„œ๋ธŒํŠธ๋ฆฌ๊ฐ€ ์—†์–ด ๋Œ€์กฐํ•  ํŽ˜์ด์ง€ ์ž์ฒด๊ฐ€ ์—†๋‹ค โ€” ์ƒ๋žต์„ ๋ช…์‹œํ•œ๋‹ค.\n console.log(`\\n ${dimText(\"Page completeness: skipped (database mode)\")}`);\n }\n\n if (!report.complete) {\n console.log(\n `\\n ${dimText(\"Run\")} ${chalk.cyan(\"nobsi pull\")} ${dimText(\"to fetch what is missing\")}`,\n );\n }\n}\n\nexport const verifyCommand = new Command(\"verify\")\n .description(\"์™„๊ฒฐ์„ฑ ๊ฒ€์ฆ โ€” ์›๊ฒฉ DB ํ–‰ยทํŽ˜์ด์ง€๊ฐ€ ๋ณผํŠธ์— ๋น ์ง์—†์ด ์žˆ๋Š”์ง€ ๋Œ€์กฐ (API ํ˜ธ์ถœ)\")\n .option(\"--json\", \"๊ฒฐ๊ณผ๋ฅผ JSON ์œผ๋กœ ์ถœ๋ ฅ\")\n .action(async (opts: { json?: boolean }) => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n try {\n const client = NotionClient.fromConfig(config);\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n const report = await orchestrator.verifyCompleteness();\n\n if (opts.json) {\n console.log(JSON.stringify(report, null, 2));\n } else {\n printReport(report);\n }\n\n // ๋ฏธ๋ฐœ๊ฒฌยท์ž”์žฌยท์กฐํšŒ ์‹คํŒจ๊ฐ€ ์žˆ์œผ๋ฉด ์‹คํŒจ ์ข…๋ฃŒํ•œ๋‹ค โ€” CIยทE2E ํ•˜๋‹ˆ์Šค๊ฐ€ ์ด ์ฝ”๋“œ๋กœ ํŒ์ •ํ•œ๋‹ค.\n if (!report.complete) process.exitCode = 1;\n } finally {\n stateDb.close();\n }\n });\n","/**\n * CLI ์ข…๋ฃŒ ์ฒ˜๋ฆฌ โ€” ๋‚จ์€ ํ•ธ๋“ค ๋•Œ๋ฌธ์— ๊ฐ•์ œ ์ข…๋ฃŒํ•˜๋˜, **๋ช…๋ น์ด ์ •ํ•œ ์ข…๋ฃŒ ์ฝ”๋“œ๋Š” ๋ณด์กดํ•œ๋‹ค**.\n *\n * better-sqlite3 ํ•ธ๋“ค๊ณผ rate limiter ํƒ€์ด๋จธ๊ฐ€ ์ด๋ฒคํŠธ ๋ฃจํ”„๋ฅผ ๋ถ™์žก๊ณ  ์žˆ์–ด ๋ช…๋ น์ด ๋๋‚˜๋„\n * ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ž์—ฐ ์ข…๋ฃŒ๋˜์ง€ ์•Š๋Š”๋‹ค. ๊ทธ๋ž˜์„œ ์งง์€ ์ง€์—ฐ ๋’ค ๊ฐ•์ œ ์ข…๋ฃŒํ•˜๋Š”๋ฐ, ์—ฌ๊ธฐ์„œ ์ฝ”๋“œ๋ฅผ\n * 0 ์œผ๋กœ ๋ชป๋ฐ•์œผ๋ฉด ๊ฐ ๋ช…๋ น์ด ์‹คํŒจ๋งˆ๋‹ค ์„ธ์›Œ ๋‘” `process.exitCode = 1`\n * (initยทpullยทpushยทsyncยทresolveยทverify)์ด **์ „๋ถ€ ์ง€์›Œ์ง„๋‹ค** โ€” ์‹คํŒจํ•œ ์‹คํ–‰๋„ ์…ธ๊ณผ CI ์—๋Š”\n * ์„ฑ๊ณต์œผ๋กœ ๋ณด์ด๊ณ , ์ข…๋ฃŒ ์ฝ”๋“œ๋กœ ํŒ์ •ํ•˜๋Š” ์Šคํฌ๋ฆฝํŠธ๋Š” ์•„๋ฌด๊ฒƒ๋„ ๋ชป ์žก๋Š”๋‹ค(R11-C).\n *\n * ์ข…๋ฃŒ ์ฝ”๋“œ ๊ฒฐ์ •์„ ์ด ํ•จ์ˆ˜ ํ•˜๋‚˜์— ๋‘์–ด, \"๊ฐ•์ œ ์ข…๋ฃŒํ•œ๋‹ค\"์™€ \"์„ฑ๊ณต์œผ๋กœ ์ข…๋ฃŒํ•œ๋‹ค\"๊ฐ€ ๋‹ค์‹œ\n * ํ•œ ์ค„์— ์„ž์ด์ง€ ์•Š๊ฒŒ ํ•œ๋‹ค.\n */\n\n/** ๊ฐ•์ œ ์ข…๋ฃŒ๊นŒ์ง€์˜ ์ง€์—ฐ โ€” ๋งˆ์ง€๋ง‰ stdout flush ์—ฌ์œ . */\nexport const FORCED_EXIT_DELAY_MS = 100;\n\n/**\n * {@link scheduleForcedExit} ๊ฐ€ ์“ฐ๋Š” ํ”„๋กœ์„ธ์Šค ํ‘œ๋ฉด(ํ…Œ์ŠคํŠธ ๋Œ€์—ญ ์ฃผ์ž…์šฉ).\n *\n * `exitCode` ๋Š” Node ์˜ `process.exitCode` ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐ›๋Š”๋‹ค โ€” ๋ช…์‹œ์ ์œผ๋กœ ์ง€์šด ๊ฒฝ์šฐ\n * `null` ์ด ๋  ์ˆ˜ ์žˆ์–ด ๊ทธ ์œ ๋‹ˆ์˜จ๊นŒ์ง€ ํฌํ•จํ•ด์•ผ ์‹ค์ œ `process` ๋ฅผ ๋„˜๊ธธ ์ˆ˜ ์žˆ๋‹ค.\n */\nexport interface ExitableProcess {\n exitCode?: number | string | null | undefined;\n exit(code?: number): void;\n}\n\n/** `process.exitCode` ๋ฅผ ์ข…๋ฃŒ ์ฝ”๋“œ๋กœ ์ •๊ทœํ™”ํ•œ๋‹ค. ์ˆซ์ž๊ฐ€ ์•„๋‹ˆ๋ฉด ์„ฑ๊ณต(0). */\nexport function resolveExitCode(exitCode: number | string | null | undefined): number {\n if (typeof exitCode === \"number\") return exitCode;\n if (typeof exitCode === \"string\") {\n const parsed = Number(exitCode);\n return Number.isFinite(parsed) ? parsed : 0;\n }\n return 0;\n}\n\n/** ์ง€์—ฐ ํ›„ ๊ฐ•์ œ ์ข…๋ฃŒ๋ฅผ ์˜ˆ์•ฝํ•œ๋‹ค โ€” ์ข…๋ฃŒ ์ฝ”๋“œ๋Š” ๋ช…๋ น์ด ์„ธ์šด ๊ฐ’์„ ๊ทธ๋Œ€๋กœ ์“ด๋‹ค. */\nexport function scheduleForcedExit(\n proc: ExitableProcess,\n schedule: (fn: () => void, ms: number) => unknown = setTimeout,\n): void {\n schedule(() => proc.exit(resolveExitCode(proc.exitCode)), FORCED_EXIT_DELAY_MS);\n}\n"],"mappings":"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","names":["Command","chalk","chalk","spinner","Command","ConfigManager","NotionClient","Command","ConfigManager","NotionClient","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","chalk","Command","ConfigManager","StateDB","NotionClient","NodeVaultFS","SyncOrchestrator","chalk","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","chalk","Command","ConfigManager","StateDB","NotionClient","NodeVaultFS","SyncOrchestrator","chalk","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","chalk","Command","ConfigManager","StateDB","NotionClient","NodeVaultFS","SyncOrchestrator","chalk","remoteDeleted","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","Command","ConfigManager","StateDB","NotionClient","NodeVaultFS","SyncOrchestrator","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","Command","ConfigManager","StateDB","NotionClient","NodeVaultFS","SyncOrchestrator","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","chalk","Command","ConfigManager","StateDB","NotionClient","NodeVaultFS","SyncOrchestrator","chalk","Command","confirm","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","isRemoteDeletion","isRemoteDeletion","Command","ConfigManager","StateDB","NotionClient","NodeVaultFS","SyncOrchestrator","confirm","failed","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","chalk","report","chalk","Command","ConfigManager","StateDB","NotionClient","NodeVaultFS","SyncOrchestrator","Command"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/commands/init.ts","../src/utils/format.ts","../src/utils/vault.ts","../src/commands/push.ts","../src/utils/json-output.ts","../src/commands/pull.ts","../src/commands/sync.ts","../src/commands/status.ts","../src/commands/diff.ts","../src/utils/diff-output.ts","../src/commands/discard.ts","../src/commands/fetch.ts","../src/commands/resolve.ts","../src/utils/state-db-failure.ts","../src/commands/watch.ts","../src/commands/verify.ts","../src/commands/prepare-db.ts","../src/utils/exit.ts"],"sourcesContent":["import { createRequire } from \"node:module\";\nimport { Command } from \"commander\";\nimport { initCommand } from \"./commands/init.js\";\nimport { pushCommand } from \"./commands/push.js\";\nimport { pullCommand } from \"./commands/pull.js\";\nimport { syncCommand } from \"./commands/sync.js\";\nimport { statusCommand } from \"./commands/status.js\";\nimport { diffCommand } from \"./commands/diff.js\";\nimport { discardCommand } from \"./commands/discard.js\";\nimport { fetchCommand } from \"./commands/fetch.js\";\nimport { resolveCommand } from \"./commands/resolve.js\";\nimport { watchCommand } from \"./commands/watch.js\";\nimport { verifyCommand } from \"./commands/verify.js\";\nimport { prepareDbCommand } from \"./commands/prepare-db.js\";\nimport { scheduleForcedExit } from \"./utils/exit.js\";\nimport { reportStateDbFailure } from \"./utils/state-db-failure.js\";\n\nconst requireJson = createRequire(import.meta.url);\nconst { version } = requireJson(\"../package.json\") as { version: string };\n\nconst program = new Command();\n\nprogram\n .name(\"im-nobsidian\")\n .description(\"Im-Nobsidian: Obsidian โ†” Notion ์–‘๋ฐฉํ–ฅ ๋™๊ธฐํ™” CLI\")\n .version(version)\n .option(\"--verbose\", \"์ƒ์„ธ ๋กœ๊ทธ ์ถœ๋ ฅ\")\n .option(\"--quiet\", \"์ตœ์†Œ ์ถœ๋ ฅ\");\n\nprogram.addCommand(initCommand);\nprogram.addCommand(pushCommand);\nprogram.addCommand(pullCommand);\nprogram.addCommand(syncCommand);\nprogram.addCommand(statusCommand);\nprogram.addCommand(diffCommand);\nprogram.addCommand(discardCommand);\nprogram.addCommand(fetchCommand);\nprogram.addCommand(resolveCommand);\nprogram.addCommand(watchCommand);\nprogram.addCommand(verifyCommand);\nprogram.addCommand(prepareDbCommand);\n\n// ๊ฐ•์ œ ์ข…๋ฃŒํ•˜๋˜ ๋ช…๋ น์ด ์ •ํ•œ ์ข…๋ฃŒ ์ฝ”๋“œ๋ฅผ ๋ณด์กดํ•œ๋‹ค โ€” ๊ทผ๊ฑฐ๋Š” utils/exit.ts ์ฐธ์กฐ(R11-C).\n// ์ƒํƒœ DB ๋ฅผ ์“ธ ์ˆ˜ ์—†์–ด ๋ช…๋ น์ด ๋˜์กŒ์œผ๋ฉด ์ด์œ ์™€ ํ•  ์ผ์„ ๋ณด์ด๊ณ  1 ๋กœ ๋๋‚ธ๋‹ค โ€” utils/state-db-failure.ts.\nprogram.parseAsync().then(\n () => {\n scheduleForcedExit(process);\n },\n (error: unknown) => {\n reportStateDbFailure(error, process);\n scheduleForcedExit(process);\n },\n);\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n NOTION_TOKEN_PREFIX,\n NotionClient,\n notionIdFromInput,\n} from \"@im-nobsidian/core\";\nimport { input, confirm } from \"@inquirer/prompts\";\nimport ora from \"ora\";\nimport chalk from \"chalk\";\nimport { header, separator, icons, dimText } from \"../utils/format.js\";\nimport { outerConnectionTracking } from \"../utils/vault.js\";\n\n/** ๋ฃจํŠธ ํŽ˜์ด์ง€์—์„œ ID ๋ฅผ ๋ชป ์ฐพ์•˜์„ ๋•Œ โ€” ์ฃผ์†Œ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ถ™์—ฌ ๋„ฃ์–ด๋„ ๋œ๋‹ค๊ณ  ๋งํ•œ๋‹ค. */\nconst ROOT_PAGE_UNREADABLE =\n \"๋ฃจํŠธ ํŽ˜์ด์ง€์—์„œ Notion ํŽ˜์ด์ง€ ID(32์ž๋ฆฌ)๋ฅผ ์ฐพ์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค โ€” ํŽ˜์ด์ง€ ์ฃผ์†Œ๋‚˜ ID ๋ฅผ ๋„ฃ์œผ์„ธ์š”.\";\n\nexport const initCommand = new Command(\"init\")\n .description(\"Im-Nobsidian ์ดˆ๊ธฐ ์„ค์ •\")\n .option(\"--token <token>\", \"Notion Integration Token\")\n .option(\"--root-page-id <id>\", \"๋ฃจํŠธ ํŽ˜์ด์ง€ ID ๋˜๋Š” ํŽ˜์ด์ง€ ์ฃผ์†Œ\")\n .option(\"--non-interactive\", \"๋น„๋Œ€ํ™”ํ˜• ๋ชจ๋“œ\")\n .action(async (options: { token?: string; rootPageId?: string; nonInteractive?: boolean }) => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n\n console.log(`\\n${header(chalk.cyan(\" Im-Nobsidian Setup\"))}`);\n console.log(` ${separator()}`);\n\n // ๋ฐ”๊นฅ ์—ฐ๊ฒฐ์ด ์ด๋ฏธ ์ด ํด๋”๋ฅผ ๋™๊ธฐํ™”ํ•˜๋ฉด ๋”ฐ๋กœ ์—ฐ๊ฒฐํ•˜์ง€ ์•Š๋Š”๋‹ค(ADR-038) โ€” ๋ฐ”๊นฅ ์—ฐ๊ฒฐ์ด ๋ฉˆ์ถ˜๋‹ค.\n const outer = outerConnectionTracking(cwd);\n if (outer) {\n console.error(\n `์ด ํด๋”๋Š” ์ด๋ฏธ ${outer.folder} ์˜ ์—ฐ๊ฒฐ์ด ๋…ธํŠธ ${outer.notes}๊ฐœ๋ฅผ ๋™๊ธฐํ™”ํ•˜๊ณ  ์žˆ์–ด ๋”ฐ๋กœ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.`,\n );\n process.exitCode = 1;\n return;\n }\n\n if (options.nonInteractive) {\n if (!options.token || !options.rootPageId) {\n console.error(\"๋น„๋Œ€ํ™”ํ˜• ๋ชจ๋“œ์—์„œ๋Š” --token๊ณผ --root-page-id๊ฐ€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.\");\n process.exitCode = 1;\n return;\n }\n\n if (!options.token.startsWith(NOTION_TOKEN_PREFIX)) {\n console.error(`ํ† ํฐ์€ ${NOTION_TOKEN_PREFIX}์œผ๋กœ ์‹œ์ž‘ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.`);\n process.exitCode = 1;\n return;\n }\n\n const rootPageId = notionIdFromInput(options.rootPageId);\n if (rootPageId === null) {\n console.error(ROOT_PAGE_UNREADABLE);\n process.exitCode = 1;\n return;\n }\n\n const spinner = ora(\"Notion ์—ฐ๊ฒฐ ํ™•์ธ ์ค‘...\").start();\n try {\n const client = new NotionClient({ token: options.token });\n await client.search({ pageSize: 1, filter: { property: \"object\", value: \"page\" } });\n spinner.stop();\n console.log(` ${icons.success} Token validated`);\n\n await configManager.init({ token: options.token, rootPageId });\n console.log(`\\n ${icons.success} Config saved to ${dimText(configManager.configPath)}`);\n console.log(\n `\\n ${header(chalk.green(\"Ready!\"))} Run ${chalk.cyan(\"nobsi sync\")} to start syncing.`,\n );\n } catch (error) {\n spinner.fail(\"Notion ์—ฐ๊ฒฐ ์‹คํŒจ\");\n console.error(error instanceof Error ? error.message : error);\n process.exitCode = 1;\n }\n return;\n }\n\n const isInit = await configManager.isInitialized();\n if (isInit) {\n const overwrite = await confirm({\n message: \"์ด๋ฏธ ์ดˆ๊ธฐํ™”๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์‹œ ์„ค์ •ํ• ๊นŒ์š”?\",\n default: false,\n });\n if (!overwrite) return;\n }\n\n const token = await input({\n message: \"Notion Integration Token:\",\n validate: (v) =>\n v.startsWith(NOTION_TOKEN_PREFIX) ||\n `${NOTION_TOKEN_PREFIX}์œผ๋กœ ์‹œ์ž‘ํ•˜๋Š” ํ† ํฐ์„ ์ž…๋ ฅํ•˜์„ธ์š”`,\n });\n\n const spinner = ora(\"Notion ์—ฐ๊ฒฐ ํ™•์ธ ์ค‘...\").start();\n\n try {\n const client = new NotionClient({ token });\n const result = await client.search({\n pageSize: 10,\n filter: { property: \"object\", value: \"page\" },\n });\n spinner.stop();\n console.log(` ${icons.success} Token validated`);\n\n if (result.results.length === 0) {\n console.log(\n `\\n ${chalk.yellow(\"โš \")} ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ ํŽ˜์ด์ง€๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. Integration์— ํŽ˜์ด์ง€๋ฅผ ๊ณต์œ ํ•ด์ฃผ์„ธ์š”.`,\n );\n return;\n }\n\n console.log(`\\n Select root page:`);\n for (const [i, page] of result.results.entries()) {\n const title = extractPageTitle(page);\n console.log(` ${dimText(`${i + 1}.`)} ${title}`);\n }\n\n const rootPageInput = await input({\n message: \"๋ฃจํŠธ ํŽ˜์ด์ง€ ID ๋˜๋Š” ์ฃผ์†Œ (์œ„์—์„œ ์„ ํƒํ•˜๊ฑฐ๋‚˜ ์ง์ ‘ ์ž…๋ ฅ):\",\n default: result.results[0]?.id,\n validate: (value) => notionIdFromInput(value) !== null || ROOT_PAGE_UNREADABLE,\n });\n const rootPageId = notionIdFromInput(rootPageInput)!;\n\n await configManager.init({ token, rootPageId });\n console.log(`\\n ${icons.success} Config saved to ${dimText(configManager.configPath)}`);\n console.log(\n `\\n ${header(chalk.green(\"Ready!\"))} Run ${chalk.cyan(\"nobsi sync\")} to start syncing.`,\n );\n } catch (error) {\n spinner.fail(\"Notion ์—ฐ๊ฒฐ ์‹คํŒจ\");\n console.error(error instanceof Error ? error.message : error);\n process.exitCode = 1;\n }\n });\n\nfunction extractPageTitle(page: Record<string, unknown>): string {\n const properties = page[\"properties\"] as Record<string, unknown> | undefined;\n if (!properties) return \"(์ œ๋ชฉ ์—†์Œ)\";\n\n for (const prop of Object.values(properties)) {\n const p = prop as { type?: string; title?: Array<{ plain_text?: string }> };\n if (p.type === \"title\" && p.title?.[0]?.plain_text) {\n return p.title[0].plain_text;\n }\n }\n return \"(์ œ๋ชฉ ์—†์Œ)\";\n}\n","import chalk from \"chalk\";\nimport type { FullScanReason, ProgressItem, RemoteScanInfo } from \"@im-nobsidian/core\";\n\nexport const icons = {\n create: chalk.green(\"+\"),\n update: chalk.yellow(\"~\"),\n delete: chalk.red(\"-\"),\n move: chalk.cyan(\"โ†’\"),\n success: chalk.green(\"โœ“\"),\n fail: chalk.red(\"โœ•\"),\n conflict: chalk.magenta(\"!\"),\n synced: chalk.green(\"โ—\"),\n modified: chalk.yellow(\"โ—\"),\n newFile: chalk.cyan(\"โ—\"),\n conflictDot: chalk.magenta(\"โ—\"),\n} as const;\n\nexport function header(text: string): string {\n return chalk.bold(text);\n}\n\nexport function separator(width = 35): string {\n return chalk.dim(\"โ”\".repeat(width));\n}\n\nexport function operationIcon(op: ProgressItem[\"operation\"]): string {\n return icons[op];\n}\n\nexport function operationLabel(op: ProgressItem[\"operation\"]): string {\n switch (op) {\n case \"create\":\n return chalk.green(\"created\");\n case \"update\":\n return chalk.yellow(\"updated\");\n case \"delete\":\n return chalk.red(\"deleted\");\n case \"move\":\n return chalk.cyan(\"moved\");\n }\n}\n\n/** ๊ฒฐ๊ณผ ์ˆ˜ ํ•œ ์ค„. `moved` ๋Š” push ๋งŒ ์„ผ๋‹ค โ€” ์˜ฎ๊ธด ๋…ธํŠธ ยท ํด๋”(๋ณธ๋ฌธ์„ ํ•จ๊ป˜ ๊ณ ์นœ ๊ฒƒ๋„). */\nexport function summary(created: number, updated: number, deleted: number, moved = 0): string {\n const parts: string[] = [];\n if (created > 0) parts.push(chalk.green(`${created} created`));\n if (updated > 0) parts.push(chalk.yellow(`${updated} updated`));\n if (moved > 0) parts.push(chalk.cyan(`${moved} moved`));\n if (deleted > 0) parts.push(chalk.red(`${deleted} deleted`));\n if (parts.length === 0) parts.push(chalk.dim(\"no changes\"));\n return parts.join(\" \");\n}\n\n/**\n * ์™„๋ฃŒ ํ—ค๋” โ€” ์‹คํŒจ๊ฐ€ ํ•˜๋‚˜๋ผ๋„ ์žˆ์œผ๋ฉด ์ดˆ๋ก \"complete\" ๋กœ ๋๋‚ด์ง€ ์•Š๋Š”๋‹ค.\n *\n * pull/push/sync ๊ฐ€ ๋ชจ๋‘ ๊ฒฐ๊ณผ์™€ ๋ฌด๊ด€ํ•˜๊ฒŒ ์ดˆ๋ก `... complete` ๋ฅผ ์ฐ์—ˆ๋‹ค. ์ˆ˜์‹ญ ๊ฑด์ด ์‹คํŒจํ•ด๋„\n * ํ™”๋ฉด ๋งจ ์•„๋ž˜๋Š” ์„ฑ๊ณต์œผ๋กœ ์ฝํ˜€, ์‚ฌ๋žŒ์€ ๋ฌผ๋ก  ๋กœ๊ทธ๋ฅผ ํ›‘๋Š” ์Šคํฌ๋ฆฝํŠธ๊นŒ์ง€ ๋„˜์–ด๊ฐ”๋‹ค.\n * ํ—ค๋” ๋ฌธ๊ตฌยท์ƒ‰์„ ๊ฒฐ๊ณผ์—์„œ ํŒŒ์ƒ์‹œ์ผœ ์„ธ ๋ช…๋ น์ด ๊ฐ™์€ ํŒ์ •์„ ๊ณต์œ ํ•œ๋‹ค.\n */\nexport function completionHeader(label: string, failedCount: number): string {\n return failedCount > 0\n ? header(chalk.yellow(`${label} finished with errors`))\n : header(chalk.green(`${label} complete`));\n}\n\n/** ์‹คํŒจ ๊ฑด์ˆ˜ ํ‘œ๊ธฐ. dim ์€ \"์—†๋Š” ์…ˆ\" ์œผ๋กœ ์ฝํžˆ๋ฏ€๋กœ 0 ์ด ์•„๋‹ ๋•Œ๋งŒ dim ์„ ๋ฒ—๋Š”๋‹ค. */\nexport function failedCountText(count: number): string {\n return count > 0 ? chalk.red(`${count} failed`) : chalk.dim(\"0 failed\");\n}\n\nexport function duration(ms: number): string {\n return chalk.dim(`Done in ${(ms / 1000).toFixed(1)}s`);\n}\n\n/** ๋กœ์ปฌ ์‹œ๊ฐ `YYYY-MM-DD HH:MM:SS`. ์ฝ์„ ์ˆ˜ ์—†๋Š” ๊ฐ’์€ ๊ทธ๋Œ€๋กœ ๋ณด์ธ๋‹ค โ€” NaN ์œผ๋กœ ๋ฐ”๊พธ์ง€ ์•Š๋Š”๋‹ค. */\nexport function localDateTime(iso: string): string {\n const date = new Date(iso);\n if (Number.isNaN(date.getTime())) return iso;\n const pad = (n: number): string => String(n).padStart(2, \"0\");\n return (\n `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ` +\n `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`\n );\n}\n\n/** ์ „์ฒด ๋Œ€์กฐํ•œ ์ด์œ . ์ฝ”์–ด์˜ ์ด์œ  ์ฝ”๋“œ๊ฐ€ ๋Š˜๋ฉด ์—ฌ๊ธฐ๊ฐ€ ์ปดํŒŒ์ผ๋˜์ง€ ์•Š๋Š”๋‹ค โ€” ๋น ์ง„ ๋ง์„ ์กฐ์šฉํžˆ ๋‘์ง€ ์•Š๋Š”๋‹ค. */\nconst FULL_SCAN_REASON: Record<FullScanReason, string> = {\n first: \"first pull\",\n forced: \"--force\",\n \"database-mode\": \"database mode\",\n \"every-pull\": \"every pull\",\n due: \"scheduled\",\n};\n\n/**\n * ์ด๋ฒˆ์— ์›๊ฒฉ์„ ์–ผ๋งˆ๋‚˜ ํ›‘์—ˆ๋‚˜ (ADR-027). ๋ฐ”๋€ ๊ฒƒ๋งŒ ์ฐพ์•˜์œผ๋ฉด Notion ์—์„œ ์ง€์šด ๊ฒƒ์€ ์•„์ง ๋ณผํŠธ์— ๋ฐ˜์˜๋˜์ง€\n * ์•Š์•˜์„ ์ˆ˜ ์žˆ๋‹ค โ€” ๊ทธ ์‚ฌ์‹ค๊ณผ ์–ธ์ œ ๋ฐ˜์˜๋˜๋Š”์ง€๋ฅผ ํ•จ๊ป˜ ์ ๋Š”๋‹ค. ์ ์ง€ ์•Š์œผ๋ฉด ยซNotion ์—์„œ ์ง€์› ๋Š”๋ฐ ๋ณผํŠธ์—\n * ๋‚จ์•˜๋‹คยป ๋ฅผ ๊ฒฐํ•จ์œผ๋กœ ์ฝ๋Š”๋‹ค. ์›๊ฒฉ ์‚ญ์ œ๋ฅผ ๋ฐ˜์˜ํ•˜์ง€ ์•Š๋Š” ์„ค์ •(`sync.deleteSync` ๊บผ์ง)์ด๋ฉด ์ ์ง€ ์•Š๋Š”๋‹ค.\n */\nexport function remoteScanLines(scan: RemoteScanInfo): string[] {\n if (scan.kind === \"full\") {\n return [dimText(scan.reason ? `Full scan (${FULL_SCAN_REASON[scan.reason]})` : \"Full scan\")];\n }\n const skipped = scan.skippedDatabases ?? 0;\n const skippedText =\n skipped > 0\n ? ` ยท ${skipped} unchanged ${skipped === 1 ? \"database\" : \"databases\"} skipped`\n : \"\";\n const lines = [dimText(`Changes-only scan${skippedText}`)];\n if (scan.deletionsDeferred) lines.push(remoteDeletionHint(scan));\n return lines;\n}\n\n/** ๋ฏธ๋ฃฌ ์›๊ฒฉ ์‚ญ์ œ๊ฐ€ ์–ธ์ œ ๋ฐ˜์˜๋˜๋‚˜ โ€” {@link RemoteScanInfo.deletionsDeferred} ์ผ ๋•Œ ์“ด๋‹ค. */\nexport function remoteDeletionHint(scan: RemoteScanInfo): string {\n const when = scan.nextFullAt\n ? `the next full scan (${localDateTime(scan.nextFullAt)})`\n : \"the next pull (full scan due)\";\n return `${dimText(`Deletions in Notion apply at ${when} โ€” or run`)} ${chalk.cyan(\"nobsi pull --force\")}`;\n}\n\n/** ๋งˆ์ง€๋ง‰ ์ „์ฒด ๋Œ€์กฐ ์‹œ๊ฐ โ€” ํ•œ ๋ฒˆ๋„ ์—†์œผ๋ฉด never. */\nexport function lastFullScanText(lastFullScanAt: string | null): string {\n return lastFullScanAt ? localDateTime(lastFullScanAt) : chalk.yellow(\"never\");\n}\n\nexport function progress(current: number, total: number): string {\n return chalk.dim(`[${current}/${total}]`);\n}\n\nexport function dimText(text: string): string {\n return chalk.dim(text);\n}\n\n/**\n * ์‹คํŒจ ํ•œ ์ค„. ๋ณผํŠธ ์ตœ์ƒ์œ„์˜ ์‹คํŒจ(DB ์ž๋™ ๋ฐœ๊ฒฌ ๋“ฑ)๋Š” ๋นˆ ๊ฒฝ๋กœ๋กœ ์˜ค๋ฏ€๋กœ `/` ๋กœ ๋ณด์ธ๋‹ค. `direction` ์€\n * sync ๊ฐ€ ๋ฐ›๊ธฐ(โ–ผ) ยท ์˜ฌ๋ฆฌ๊ธฐ(โ–ฒ)๋ฅผ ๊ฐ€๋ฅผ ๋•Œ ์“ด๋‹ค.\n */\nexport function failedItem(path: string, error: string, direction?: string): void {\n const where = `${direction ? `${direction} ` : \"\"}${path || \"/\"}`;\n console.log(` ${icons.fail} ${where}: ${chalk.red(error)}`);\n}\n","import { existsSync } from \"node:fs\";\nimport { dirname, join, relative } from \"node:path\";\nimport {\n ConfigManager,\n NodeVaultFS,\n StateDB,\n matchesPathScope,\n ScopedVaultFS,\n findConnectionFolders,\n OBSIDIAN_CONFIG_DIR,\n type PathFilterConfig,\n type VaultFS,\n} from \"@im-nobsidian/core\";\n\n/** ์ด ํด๋” ์•ˆ์ชฝ์˜ ๋‹ค๋ฅธ ์—ฐ๊ฒฐ ํด๋”(ADR-038) โ€” ์ด ํด๋”์˜ ๋™๊ธฐํ™”๊ฐ€ ๋น„์ผœ ๊ฐ„๋‹ค. */\nexport async function nestedConnections(cwd: string): Promise<string[]> {\n return (await findConnectionFolders(cwd)).filter((folder) => folder !== \"\");\n}\n\n/**\n * ํ˜„์žฌ ํด๋”๋ฅผ ๋ณผํŠธ๋กœ ๋‹ค๋ฃจ๋Š” VaultFS. ์•ˆ์ชฝ์— ๋‹ค๋ฅธ ์—ฐ๊ฒฐ ํด๋”๊ฐ€ ์žˆ์œผ๋ฉด ๊ทธ ํด๋”๋Š” ์˜ฌ๋ฆฌ์ง€๋„ ๋ฎ์–ด์“ฐ์ง€๋„ ์•Š๋Š”๋‹ค โ€”\n * ๊ทธ ํด๋”๋Š” ์ž๊ธฐ ์„ค์ • ยท Notion ์œผ๋กœ ๋”ฐ๋กœ ๋™๊ธฐํ™”ํ•œ๋‹ค(`cd <ํด๋”> && nobsi sync`).\n */\nexport async function openVaultFs(cwd: string, paths: PathFilterConfig): Promise<VaultFS> {\n const base = new NodeVaultFS(cwd, paths);\n const nested = await nestedConnections(cwd);\n return nested.length === 0 ? base : new ScopedVaultFS(base, \"\", [\"\", ...nested]);\n}\n\n/**\n * Obsidian ์„ค์ • ํด๋”๊นŒ์ง€์˜ ์ƒ๋Œ€ ๊ฒฝ๋กœ โ€” ์—ฐ๊ฒฐ ํด๋”๊ฐ€ ๋ณผํŠธ ์•ˆ์ชฝ์ด๋ฉด ์œ„๋กœ ์˜ฌ๋ผ๊ฐ€ ์ฐพ๋Š”๋‹ค. ๋ชป ์ฐพ์œผ๋ฉด `.obsidian`\n * (์—†์œผ๋ฉด ์†์„ฑ ํƒ€์ž…์„ ๊ฐ’์œผ๋กœ ์ •ํ•œ๋‹ค).\n */\nexport function obsidianConfigDir(cwd: string): string {\n for (let dir = cwd; ; dir = dirname(dir)) {\n if (existsSync(join(dir, OBSIDIAN_CONFIG_DIR))) {\n return relative(cwd, join(dir, OBSIDIAN_CONFIG_DIR)).split(\"\\\\\").join(\"/\");\n }\n if (dirname(dir) === dir) return OBSIDIAN_CONFIG_DIR;\n }\n}\n\n/**\n * ์ด ํด๋”๋ฅผ ์ด๋ฏธ ๋™๊ธฐํ™”ํ•˜๋Š” ๋ฐ”๊นฅ ์—ฐ๊ฒฐ โ€” ๊ทธ ์—ฐ๊ฒฐ ํด๋”์™€ ์ด ํด๋” ์•ˆ์—์„œ ์ถ”์ ํ•˜๋Š” ๋…ธํŠธ ์ˆ˜. ์—†์œผ๋ฉด null.\n *\n * ์ด๋ฏธ ๋™๊ธฐํ™”ํ•˜๋˜ ํด๋”๋ฅผ ๋”ฐ๋กœ ์—ฐ๊ฒฐํ•˜๋ฉด ๋ฐ”๊นฅ ์—ฐ๊ฒฐ์ด ๋ฉˆ์ถ˜๋‹ค(ConnectionScopeError) โ€” init ์ด ๋จผ์ € ๋ง‰๋Š”๋‹ค.\n */\nexport function outerConnectionTracking(cwd: string): { folder: string; notes: number } | null {\n for (let dir = dirname(cwd); ; dir = dirname(dir)) {\n const manager = new ConfigManager(dir);\n if (existsSync(manager.dbPath)) {\n const scope = relative(dir, cwd).split(\"\\\\\").join(\"/\");\n const db = StateDB.open(manager.dbPath);\n try {\n // ํด๋” ์ž์‹ ์˜ ๋ ˆ์ฝ”๋“œ(ํด๋” ํŽ˜์ด์ง€)๋Š” ๋นผ๊ณ  ๊ทธ ์•ˆ์˜ ๊ฒƒ๋งŒ ์„ผ๋‹ค.\n const notes = db\n .getAll()\n .filter(\n (r) => r.obsidianPath !== scope && matchesPathScope(r.obsidianPath, scope),\n ).length;\n return notes > 0 ? { folder: dir, notes } : null;\n } finally {\n db.close();\n }\n }\n if (dirname(dir) === dir) return null;\n }\n}\n","import { Command } from \"commander\";\nimport { ConfigManager, StateDB, NotionClient, SyncOrchestrator } from \"@im-nobsidian/core\";\nimport {\n header,\n operationIcon,\n operationLabel,\n progress,\n summary,\n duration,\n failedItem,\n dimText,\n completionHeader,\n failedCountText,\n} from \"../utils/format.js\";\nimport { enableJsonMode, printJson, pushJson } from \"../utils/json-output.js\";\nimport { openVaultFs } from \"../utils/vault.js\";\n\nexport const pushCommand = new Command(\"push\")\n .description(\"๋กœ์ปฌ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ Notion์— ๋ฐ˜์˜\")\n .option(\"--dry-run\", \"์‹ค์ œ ๋ฐ˜์˜ ์—†์ด ๋ณ€๊ฒฝ์‚ฌํ•ญ๋งŒ ํ‘œ์‹œ\")\n .option(\"-p, --path <paths...>\", \"ํŠน์ • ๊ฒฝ๋กœ๋งŒ push\")\n .option(\"--json\", \"๊ฒฐ๊ณผ๋ฅผ JSON ํ•œ ์ค„๋กœ ์ถœ๋ ฅ (์ž๋™ํ™”์šฉ โ€” ๋กœ๊ทธ๋Š” stderr)\")\n .action(async (options) => {\n const json = options.json === true;\n if (json) enableJsonMode();\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n try {\n const client = NotionClient.fromConfig(config);\n const vaultFs = await openVaultFs(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n if (!json) {\n console.log(`\\n${header(\" Pushing to Notion...\")}`);\n if (options.dryRun) console.log(dimText(\" (dry-run mode)\"));\n console.log(\"\");\n }\n\n const result = await orchestrator.push({\n dryRun: options.dryRun,\n paths: options.path,\n onProgress: json\n ? undefined\n : (current, total, item) => {\n const icon = operationIcon(item.operation);\n const label = operationLabel(item.operation);\n const prog = progress(current, total);\n console.log(` ${icon} ${item.path} ${prog} ${label}`);\n },\n });\n\n if (json) {\n printJson(pushJson(result));\n if (result.failed.length > 0) process.exitCode = 1;\n return;\n }\n\n const failedCount = result.failed.length;\n console.log(`\\n ${completionHeader(\"Push\", failedCount)}`);\n console.log(\n ` ${summary(result.created, result.updated, result.deleted, result.moved)} ${failedCountText(failedCount)}`,\n );\n if (result.createdDatabases) {\n console.log(` ${options.dryRun ? \"๋งŒ๋“ค DB\" : \"๋งŒ๋“  DB\"} ${result.createdDatabases}`);\n }\n console.log(` ${duration(result.duration)}`);\n\n if (failedCount > 0) {\n console.log(\"\");\n for (const f of result.failed) {\n failedItem(f.path, f.error);\n }\n // ์ข…๋ฃŒ ์ฝ”๋“œ๊นŒ์ง€ ์‹คํŒจ๋กœ ๋‚จ๊ธด๋‹ค โ€” push ๊ฐ€ ๋ฐ˜์ฏค ์‹คํŒจํ•œ ๊ฑธ ์ž๋™ํ™”๊ฐ€ ์„ฑ๊ณต์œผ๋กœ ๋ณด๋ฉด ์•ˆ ๋œ๋‹ค.\n process.exitCode = 1;\n }\n } finally {\n stateDb.close();\n }\n });\n","import { setLogger } from \"@im-nobsidian/core\";\nimport type { FailedOperation, PullResult, PushResult, RemoteScanInfo } from \"@im-nobsidian/core\";\n\n/**\n * `--json` ๋ชจ๋“œ โ€” ์ž๋™ํ™”(E2E ํ•˜๋‹ˆ์Šค ยท CI)๊ฐ€ ์‚ฌ๋žŒ์šฉ ๋ฌธ๊ตฌ๋ฅผ grep ํ•˜์ง€ ์•Š๊ณ  ๊ฒฐ๊ณผ๋ฅผ ์ฝ๊ฒŒ ํ•œ๋‹ค.\n *\n * ๋ฌธ๊ตฌ grep ์€ ์กฐ์šฉํžˆ ํ‹€๋ ธ๋‹ค. sync ๋กœ๊ทธ์— Pull ์ชฝ \"no changes\" ๊ฐ€ ํ•œ ์ค„๋งŒ ์žˆ์–ด๋„ Push ์ชฝ\n * ๋ณ€๊ฒฝ๊ณผ ๋ฌด๊ด€ํ•˜๊ฒŒ churn 0 ์œผ๋กœ ํŒ์ •ํ–ˆ๊ณ , restored ยท deleted ๋Š” ์•„์˜ˆ ์„ธ์ง€ ์•Š์•˜๋‹ค.\n *\n * ๊ณ„์•ฝ: stdout ์—๋Š” JSON ํ•œ ์ค„๋งŒ ์“ด๋‹ค. ์ฝ”์–ด ๋กœ๊ทธ๋Š” ์ „๋ถ€ stderr ๋กœ ๋Œ๋ฆฐ๋‹ค.\n */\nexport function enableJsonMode(): void {\n const toStderr = (message: string, ...args: unknown[]): void => console.error(message, ...args);\n setLogger({ warn: toStderr, error: toStderr, info: toStderr, debug: () => {} });\n}\n\nexport interface PushJson {\n readonly created: number;\n readonly updated: number;\n readonly deleted: number;\n /** ์˜ฎ๊น€์„ ๋ฐ˜์˜ํ•œ ๋…ธํŠธ ยท ํด๋” ์ˆ˜ โ€” updated ์™€ ๊ฒน์น˜์ง€ ์•Š๋Š”๋‹ค. */\n readonly moved: number;\n /** ์›๊ฒฉ์— ์‹ค์ œ๋กœ ์“ด(๋˜๋Š” dry-run ์ด๋ฉด ์“ธ) ํ•ญ๋ชฉ ์ˆ˜. ๋ฉฑ๋“ฑ ํŒ์ •์˜ ๊ธฐ์ค€๊ฐ’. */\n readonly churn: number;\n /** ์‚ฌ์ด๋“œ์นด๋กœ ๋งŒ๋“ (dry-run ์€ ๋งŒ๋“ค) Notion DB ์ˆ˜ โ€” churn ์—๋„ ๋“ ๋‹ค. */\n readonly createdDatabases: number;\n readonly failed: readonly FailedOperation[];\n readonly durationMs: number;\n}\n\nexport interface PullJson {\n readonly created: number;\n readonly updated: number;\n readonly deleted: number;\n readonly restored: number;\n /** ๋ณผํŠธ์— ์‹ค์ œ๋กœ ์“ด ํ•ญ๋ชฉ ์ˆ˜ โ€” ๋ณต์›๋„ ์“ฐ๊ธฐ๋‹ค. ๋ฉฑ๋“ฑ ํŒ์ •์˜ ๊ธฐ์ค€๊ฐ’. */\n readonly churn: number;\n readonly conflicts: number;\n readonly failed: readonly FailedOperation[];\n readonly writtenPaths: number;\n readonly images: number;\n readonly files: number;\n readonly links: number;\n readonly durationMs: number;\n /** ์›๊ฒฉ์„ ์–ผ๋งˆ๋‚˜ ํ›‘์—ˆ๋‚˜ โ€” ๋ฐ”๋€ ๊ฒƒ๋งŒ ์ฐพ์•˜์œผ๋ฉด Notion ์—์„œ ์ง€์šด ๊ฒƒ์€ ๋‹ค์Œ ์ „์ฒด ๋Œ€์กฐ๊ฐ€ ๋ฐ˜์˜ํ•œ๋‹ค. */\n readonly remoteScan: RemoteScanInfo | null;\n}\n\nexport function pushJson(result: PushResult): PushJson {\n return {\n created: result.created,\n updated: result.updated,\n deleted: result.deleted,\n moved: result.moved,\n churn:\n result.created +\n result.updated +\n result.deleted +\n result.moved +\n (result.createdDatabases ?? 0),\n createdDatabases: result.createdDatabases ?? 0,\n failed: result.failed,\n durationMs: result.duration,\n };\n}\n\nexport function pullJson(result: PullResult): PullJson {\n return {\n created: result.created,\n updated: result.updated,\n deleted: result.deleted,\n restored: result.restored,\n churn: result.created + result.updated + result.deleted + result.restored,\n conflicts: result.conflicts.length,\n failed: result.failed,\n writtenPaths: result.writtenPaths.length,\n images: result.imageCount,\n files: result.fileCount,\n links: result.linkCount,\n durationMs: result.duration,\n remoteScan: result.remoteScan ?? null,\n };\n}\n\nexport function printJson(value: unknown): void {\n process.stdout.write(`${JSON.stringify(value)}\\n`);\n}\n","import { Command, Option } from \"commander\";\nimport { ConfigManager, StateDB, NotionClient, SyncOrchestrator } from \"@im-nobsidian/core\";\nimport chalk from \"chalk\";\nimport {\n header,\n operationIcon,\n operationLabel,\n progress,\n summary,\n duration,\n icons,\n failedItem,\n dimText,\n completionHeader,\n failedCountText,\n remoteScanLines,\n} from \"../utils/format.js\";\nimport { enableJsonMode, printJson, pullJson } from \"../utils/json-output.js\";\nimport { openVaultFs } from \"../utils/vault.js\";\n\nexport const pullCommand = new Command(\"pull\")\n .description(\"Notion ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ๋กœ์ปฌ์— ๋ฐ˜์˜\")\n .option(\"--dry-run\", \"์‹ค์ œ ๋ฐ˜์˜ ์—†์ด ๋ณ€๊ฒฝ์‚ฌํ•ญ๋งŒ ํ‘œ์‹œ\")\n .option(\"-p, --path <paths...>\", \"ํŠน์ • ๊ฒฝ๋กœ๋งŒ pull\")\n .option(\n \"--force\",\n \"์ฆ๋ถ„ ๊ฐ์ง€๋ฅผ ๊ฑด๋„ˆ๋›ฐ๊ณ  ์ „์ฒด ์Šค์บ” (search ์ธ๋ฑ์‹ฑ ์ง€์—ฐ์œผ๋กœ ๋ˆ„๋ฝ๋œ ์‹ ๊ทœ ํŽ˜์ด์ง€ยทDB ๋ณต๊ตฌ)\",\n )\n // ๋‹ค์‹œ ๊ทธ๋ ค ๋ด์•ผ ๋ฐ”๋€Œ๋Š”์ง€ ์•ˆ๋‹ค โ€” ๋ฏธ๋ฆฌ ๋ณด๊ธฐ๋กœ๋Š” ๋ณด์ผ ๊ฒƒ์ด ์—†๋‹ค(B14).\n .addOption(\n new Option(\n \"--rerender\",\n \"Notion ์—์„œ ๋ฐ”๋€Œ์ง€ ์•Š์€ ๋ฐ›์•„ ๋‘” ๋…ธํŠธ๋„ ์ง€๊ธˆ ๋ณ€ํ™˜๊ธฐ๋กœ ๋‹ค์‹œ ๊ทธ๋ฆผ (๋ณผํŠธ์—์„œ ๊ณ ์นœ ๋…ธํŠธ๋Š” ๊ทธ๋Œ€๋กœ)\",\n ).conflicts(\"dryRun\"),\n )\n .option(\"--json\", \"๊ฒฐ๊ณผ๋ฅผ JSON ํ•œ ์ค„๋กœ ์ถœ๋ ฅ (์ž๋™ํ™”์šฉ โ€” ๋กœ๊ทธ๋Š” stderr)\")\n .action(async (options) => {\n const json = options.json === true;\n if (json) enableJsonMode();\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n try {\n const client = NotionClient.fromConfig(config);\n const vaultFs = await openVaultFs(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n if (!json) {\n console.log(`\\n${header(\" Pulling from Notion...\")}`);\n if (options.dryRun) console.log(dimText(\" (dry-run mode)\"));\n console.log(\"\");\n }\n\n const result = await orchestrator.pull({\n dryRun: options.dryRun,\n paths: options.path,\n force: options.force,\n rerender: options.rerender,\n onProgress: json\n ? undefined\n : (current, total, item) => {\n // ๋ฐ›์•„ ๋ดค์ง€๋งŒ ๋ฐ”๋€Œ์ง€ ์•Š์€ ๋…ธํŠธ โ€” ๋‹ค์‹œ ๊ทธ๋ฆฌ๊ธฐ๋Š” ๋ฐ›์•„ ๋‘” ๋…ธํŠธ๋ฅผ ๋ชจ๋‘ ๋ฐ›์•„ ๋ณธ๋‹ค(B14).\n if (item.unchanged) return;\n const icon = operationIcon(item.operation);\n const label = operationLabel(item.operation);\n const prog = progress(current, total);\n console.log(` ${icon} ${item.path} ${prog} ${label}`);\n },\n });\n\n if (json) {\n printJson(pullJson(result));\n if (result.failed.length > 0) process.exitCode = 1;\n return;\n }\n\n if (result.imageCount > 0) {\n console.log(\n `\\n ${icons.success} ${result.imageCount} images โ†’ ${dimText(`${config.paths.attachments}/`)}`,\n );\n }\n if (result.fileCount > 0) {\n console.log(\n ` ${icons.success} ${result.fileCount} files โ†’ ${dimText(`${config.paths.attachments}/`)}`,\n );\n }\n if (result.linkCount > 0) {\n console.log(` ${icons.success} ${result.linkCount} links resolved`);\n }\n\n const failedCount = result.failed.length;\n console.log(`\\n ${completionHeader(\"Pull\", failedCount)}`);\n console.log(\n ` ${summary(result.created, result.updated, result.deleted)} ${failedCountText(failedCount)}`,\n );\n // ๋ณต์›์€ ๋ณ„๋„ ์ค„๋กœ ์•Œ๋ฆฐ๋‹ค โ€” ๋ณผํŠธ์—์„œ ํŒŒ์ผ์ด ์‚ฌ๋ผ์กŒ์—ˆ๋‹ค๋Š” ์‚ฌ์‹ค์€ ์กฐ์šฉํžˆ ๋„˜๊ธธ ์ผ์ด ์•„๋‹ˆ๋‹ค.\n if (result.restored > 0) {\n console.log(` ${icons.success} ${chalk.yellow(`${result.restored} restored`)}`);\n }\n console.log(` ${duration(result.duration)}`);\n if (result.remoteScan) {\n for (const line of remoteScanLines(result.remoteScan)) console.log(` ${line}`);\n }\n\n if (result.conflicts.length > 0) {\n console.log(\n `\\n ${icons.conflict} ${chalk.magenta(`${result.conflicts.length} conflicts`)} โ€” run ${chalk.cyan(\"nobsi resolve\")}`,\n );\n }\n\n if (failedCount > 0) {\n console.log(\"\");\n for (const f of result.failed) {\n failedItem(f.path, f.error);\n }\n // ์ข…๋ฃŒ ์ฝ”๋“œ๊นŒ์ง€ ์‹คํŒจ๋กœ ๋‚จ๊ธด๋‹ค โ€” ์ž๋™ํ™”๊ฐ€ pull ์‹คํŒจ๋ฅผ ์„ฑ๊ณต์œผ๋กœ ์ง‘๊ณ„ํ•˜๋ฉด ์•ˆ ๋œ๋‹ค.\n process.exitCode = 1;\n }\n } finally {\n stateDb.close();\n }\n });\n","import { Command } from \"commander\";\nimport { ConfigManager, StateDB, NotionClient, SyncOrchestrator } from \"@im-nobsidian/core\";\nimport chalk from \"chalk\";\nimport {\n header,\n separator,\n operationIcon,\n operationLabel,\n summary,\n duration,\n icons,\n dimText,\n completionHeader,\n failedItem,\n remoteScanLines,\n} from \"../utils/format.js\";\nimport { enableJsonMode, printJson, pullJson, pushJson } from \"../utils/json-output.js\";\nimport { openVaultFs } from \"../utils/vault.js\";\n\nexport const syncCommand = new Command(\"sync\")\n .description(\"์–‘๋ฐฉํ–ฅ ๋™๊ธฐํ™” (Pull โ†’ Push)\")\n .option(\"--dry-run\", \"์‹ค์ œ ๋ฐ˜์˜ ์—†์ด ๋ณ€๊ฒฝ์‚ฌํ•ญ๋งŒ ํ‘œ์‹œ\")\n .option(\"--json\", \"๊ฒฐ๊ณผ๋ฅผ JSON ํ•œ ์ค„๋กœ ์ถœ๋ ฅ (์ž๋™ํ™”์šฉ โ€” ๋กœ๊ทธ๋Š” stderr)\")\n .action(async (options) => {\n const json = options.json === true;\n if (json) enableJsonMode();\n // ์ง„ํ–‰ ๋กœ๊ทธ๋Š” ์‚ฌ๋žŒ์šฉ์ด๋‹ค โ€” JSON ๋ชจ๋“œ์—์„œ๋Š” stdout ๊ณ„์•ฝ(JSON ํ•œ ์ค„)์„ ์ง€ํ‚ค๋ ค๊ณ  ๋ˆ๋‹ค.\n const log = json ? () => {} : (line: string) => console.log(line);\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n try {\n const client = NotionClient.fromConfig(config);\n const vaultFs = await openVaultFs(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n log(`\\n${header(\" Bidirectional Sync\")}`);\n log(` ${separator()}`);\n if (options.dryRun) log(dimText(\" (dry-run mode)\"));\n\n // ๋ฐ›๊ธฐ ยท ์˜ฌ๋ฆฌ๊ธฐ๋ฅผ ํ•œ ๋ฒˆ์— ๋ˆ๋‹ค โ€” ํ”Œ๋Ÿฌ๊ทธ์ธ ยท watch ์™€ ๊ฐ™์€ sync ๋‹ค. push ๋Š” pull ์ด ๋ฐฉ๊ธˆ ์“ด ๋…ธํŠธ๋ฅผ\n // ๋‹ค์‹œ ์˜ฌ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค(F-j). ์˜ˆ์ „์—๋Š” pull ยท push ๋ฅผ ๋”ฐ๋กœ ๋ถˆ๋Ÿฌ ๋‘ ยซsyncยป ๊ฐ€ ๋”ฐ๋กœ ์ž๋ž๋‹ค.\n log(`\\n ${header(chalk.blue(\"โ–ผ Pull\"))} ${dimText(\"(Notion โ†’ Obsidian)\")}`);\n let pushShown = false;\n const showPush = (): void => {\n if (pushShown) return;\n pushShown = true;\n log(`\\n ${header(chalk.magenta(\"โ–ฒ Push\"))} ${dimText(\"(Obsidian โ†’ Notion)\")}`);\n };\n\n const result = await orchestrator.sync({\n dryRun: options.dryRun,\n onProgress: (_current, _total, item) => {\n if (item.direction === \"push\") showPush();\n if (item.unchanged) return;\n const icon = operationIcon(item.operation);\n const label = operationLabel(item.operation);\n log(` ${icon} ${item.path} ${label}`);\n },\n });\n showPush();\n const { pull: pullResult, push: pushResult } = result;\n\n if (json) {\n const pull = pullJson(pullResult);\n const push = pushJson(pushResult);\n printJson({\n pull,\n push,\n churn: pull.churn + push.churn,\n conflicts: pull.conflicts,\n durationMs: result.duration,\n });\n if (pullResult.failed.length + pushResult.failed.length > 0) process.exitCode = 1;\n return;\n }\n\n // Summary\n // ์˜ˆ์ „์—” ์–‘์ชฝ failed ๋ฅผ ์•„์˜ˆ ์ฐ์ง€ ์•Š์•„, ์ „ ๊ฑด ์‹คํŒจํ•œ sync ๋„ ํ™”๋ฉด ๋์€ ์ดˆ๋ก \"complete\"\n // ์˜€๋‹ค โ€” ์‹คํŒจ๋ฅผ ํ—ค๋”์™€ ๋ชฉ๋ก ์–‘์ชฝ์— ๋“œ๋Ÿฌ๋‚ธ๋‹ค.\n const pullFailed = pullResult.failed.length;\n const pushFailed = pushResult.failed.length;\n console.log(`\\n ${completionHeader(\"Sync\", pullFailed + pushFailed)}`);\n console.log(\n ` ${chalk.blue(\"Pull:\")} ${summary(pullResult.created, pullResult.updated, pullResult.deleted)}` +\n // ์‚ฌ๋ผ์กŒ๋˜ ํŒŒ์ผ์ด ๋˜์‚ด์•„๋‚œ ๊ฒฝ์šฐ์—๋งŒ ๋ง๋ถ™์ธ๋‹ค โ€” ํ‰์ƒ์‹œ ์š”์•ฝ์„ ์–ด์ง€๋Ÿฝํžˆ์ง€ ์•Š๋Š”๋‹ค.\n (pullResult.restored > 0 ? ` ${chalk.yellow(`${pullResult.restored} restored`)}` : \"\") +\n (pullFailed > 0 ? ` ${chalk.red(`${pullFailed} failed`)}` : \"\"),\n );\n if (pullResult.imageCount > 0) {\n console.log(` ${icons.success} ${pullResult.imageCount} images downloaded`);\n }\n if (pullResult.linkCount > 0) {\n console.log(` ${icons.success} ${pullResult.linkCount} links resolved`);\n }\n console.log(\n ` ${chalk.magenta(\"Push:\")} ${summary(pushResult.created, pushResult.updated, pushResult.deleted, pushResult.moved)}` +\n (pushFailed > 0 ? ` ${chalk.red(`${pushFailed} failed`)}` : \"\"),\n );\n console.log(` ${duration(result.duration)}`);\n if (pullResult.remoteScan) {\n for (const line of remoteScanLines(pullResult.remoteScan)) console.log(` ${line}`);\n }\n\n if (result.conflicts.length > 0) {\n console.log(\n `\\n ${icons.conflict} ${chalk.magenta(`${result.conflicts.length} conflicts`)} โ€” run ${chalk.cyan(\"nobsi resolve\")}`,\n );\n }\n\n if (pullFailed + pushFailed > 0) {\n console.log(\"\");\n for (const f of pullResult.failed) failedItem(f.path, f.error, \"โ–ผ\");\n for (const f of pushResult.failed) failedItem(f.path, f.error, \"โ–ฒ\");\n process.exitCode = 1;\n }\n } finally {\n stateDb.close();\n }\n });\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n isRemoteDeletion,\n} from \"@im-nobsidian/core\";\nimport chalk from \"chalk\";\nimport {\n header,\n separator,\n icons,\n dimText,\n localDateTime,\n lastFullScanText,\n remoteDeletionHint,\n} from \"../utils/format.js\";\nimport { nestedConnections, openVaultFs } from \"../utils/vault.js\";\n\nexport const statusCommand = new Command(\"status\")\n .description(\"๋™๊ธฐํ™” ์ƒํƒœ ํ™•์ธ (--full: ์›๊ฒฉ ๋ณ€๊ฒฝ ํฌํ•จ)\")\n .option(\"--full\", \"Notion ์›๊ฒฉ ๋ณ€๊ฒฝ๊นŒ์ง€ ์–‘๋ฐฉํ–ฅ ํ™•์ธ (API ํ˜ธ์ถœ, ๋А๋ฆผ)\")\n .action(async (opts: { full?: boolean }) => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n try {\n const client = NotionClient.fromConfig(config);\n const vaultFs = await openVaultFs(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n const status = opts.full ? await orchestrator.status() : await orchestrator.statusLocal();\n\n console.log(`\\n${header(\" Sync Status\")}`);\n console.log(` ${separator(50)}`);\n\n const rootId = config.notion.rootPageId;\n const direction = config.sync.direction === \"both\" ? \"bidirectional\" : config.sync.direction;\n console.log(` ${dimText(\"Root page:\")} ${rootId.slice(0, 8)}...`);\n console.log(` ${dimText(\"Direction:\")} ${direction}`);\n if (status.lastSyncAt) {\n console.log(` ${dimText(\"Last sync:\")} ${localDateTime(status.lastSyncAt)}`);\n } else {\n console.log(` ${dimText(\"Last sync:\")} ${chalk.yellow(\"never\")}`);\n }\n // Notion ์—์„œ ์ง€์šด ๋…ธํŠธ๋Š” ์ „์ฒด ๋Œ€์กฐ ๋•Œ ๋ณผํŠธ์— ๋ฐ˜์˜๋œ๋‹ค โ€” ๊ทธ๋•Œ๋ฅผ ํ•จ๊ป˜ ๋ณด์ธ๋‹ค.\n console.log(` ${dimText(\"Full scan:\")} ${lastFullScanText(status.lastFullScanAt)}`);\n // ์•ˆ์ชฝ ์—ฐ๊ฒฐ ํด๋”(ADR-038)๋Š” ์—ฌ๊ธฐ์„œ ๋ณด์ง€ ์•Š๋Š”๋‹ค โ€” ๊ทธ ํด๋”์—์„œ ๋”ฐ๋กœ ๋ณธ๋‹ค.\n for (const folder of await nestedConnections(cwd)) {\n console.log(\n ` ${dimText(\"Connection:\")} ${folder}/ ${dimText(`(separate โ€” cd ${folder} && nobsi status)`)}`,\n );\n }\n\n // Tracked files summary\n const allRecords = stateDb.getAll();\n const total = allRecords.length;\n const conflictCount = status.conflictRecords.length;\n const createdChanges = status.localChanges.filter((c) => c.type === \"created\");\n const modifiedChanges = status.localChanges.filter((c) => c.type === \"modified\");\n const deletedChanges = status.localChanges.filter((c) => c.type === \"deleted\");\n const movedChanges = status.localChanges.filter((c) => c.type === \"moved\");\n\n // ์ถ”์  ํŒŒ์ผ ๋‚ด์—ญ์€ ์„œ๋กœ ๊ฒน์น˜์ง€ ์•Š์•„์•ผ ํ•œ๋‹ค. DB ์˜ status ๋Š” push/pull ์‹œ์ ์—๋งŒ ๊ฐฑ์‹ ๋˜๋ฏ€๋กœ\n // ๋ฐฉ๊ธˆ ๊ณ ์นœ ํŒŒ์ผ๋„ ๋ ˆ์ฝ”๋“œ์ƒ์œผ๋ก  ์—ฌ์ „ํžˆ 'synced' ๋‹ค โ€” ๊ทธ๋Œ€๋กœ ์„ธ๋ฉด ๊ฐ™์€ ํŒŒ์ผ์ด synced ์™€\n // modified ์–‘์ชฝ์— ์žกํ˜€ ๋‚ด์—ญ ํ•ฉ์ด ์ด๊ณ„๋ฅผ ๋„˜๋Š” ํ‘œ๊ฐ€ ๋‚˜์˜จ๋‹ค(๋ฌด์—‡์ด ๋ฐ€๋ ธ๋Š”์ง€ ๋ชป ์ฝ๋Š”๋‹ค).\n // ์‹ค์ œ ๋ณ€๊ฒฝ์ด ๊ฑธ๋ฆฐ ๊ฒฝ๋กœ๋ฅผ ๋จผ์ € ๋œ์–ด ๋‚ด๊ณ  ๋‚จ์€ ๊ฒƒ๋งŒ synced ๋กœ ์„ผ๋‹ค.\n // ์˜ฎ๊ธด ๋…ธํŠธ์˜ ๋ ˆ์ฝ”๋“œ๋Š” ์•„์ง ์˜› ๊ฒฝ๋กœ(movedFrom)๋ฅผ ์ ๊ณ  ์žˆ๋‹ค โ€” ๋‘ ๊ฒฝ๋กœ๋ฅผ ๋‹ค ๋œ์–ด ๋‚ธ๋‹ค.\n const dirtyPaths = new Set([\n ...modifiedChanges.map((c) => c.path),\n ...deletedChanges.map((c) => c.path),\n ...movedChanges.flatMap((c) => [c.path, c.movedFrom ?? c.path]),\n ...status.conflictRecords.map((r) => r.obsidianPath),\n ]);\n const synced = allRecords.filter(\n (r) => r.status === \"synced\" && !dirtyPaths.has(r.obsidianPath),\n ).length;\n const remoteCreated = status.remoteChanges.filter((c) => c.type === \"created\");\n const remoteModified = status.remoteChanges.filter((c) => c.type === \"modified\");\n const remoteDeleted = status.remoteChanges.filter((c) => c.type === \"deleted\");\n const remoteMoved = status.remoteChanges.filter((c) => c.type === \"moved\");\n const { folderMoves } = status;\n\n console.log(`\\n ${header(`Tracked files: ${total}`)}`);\n console.log(` ${icons.synced} ${chalk.green(\"synced\")} ${synced}`);\n if (modifiedChanges.length > 0) {\n console.log(` ${icons.modified} ${chalk.yellow(\"modified\")} ${modifiedChanges.length}`);\n }\n if (movedChanges.length > 0) {\n console.log(` ${icons.move} ${chalk.cyan(\"moved\")} ${movedChanges.length}`);\n }\n if (deletedChanges.length > 0) {\n console.log(` ${chalk.red(\"-\")} ${chalk.red(\"deleted\")} ${deletedChanges.length}`);\n }\n if (conflictCount > 0) {\n console.log(` ${icons.conflictDot} ${chalk.magenta(\"conflict\")} ${conflictCount}`);\n }\n // new ๋Š” ์•„์ง ์ถ”์  ๋ ˆ์ฝ”๋“œ๊ฐ€ ์—†๋Š” ํŒŒ์ผ์ด๋ผ ์œ„ ์ด๊ณ„(Tracked files)์— ๋“ค์–ด๊ฐ€์ง€ ์•Š๋Š”๋‹ค.\n // ๊ฐ™์€ ๋ธ”๋ก์— ์ˆซ์ž๋งŒ ๋Š˜์–ด๋†“์œผ๋ฉด ํ•ฉ์ด ์•ˆ ๋งž์•„ ๋ณด์ด๋ฏ€๋กœ ๊ทธ ์‚ฌ์‹ค์„ ํ•จ๊ป˜ ์ ๋Š”๋‹ค.\n if (createdChanges.length > 0) {\n console.log(\n ` ${icons.newFile} ${chalk.cyan(\"new\")} ${createdChanges.length} ${dimText(\"(untracked)\")}`,\n );\n }\n\n // Modified files\n if (modifiedChanges.length > 0) {\n console.log(`\\n ${header(\"Modified files:\")}`);\n for (const c of modifiedChanges.slice(0, 10)) {\n console.log(\n ` ${chalk.yellow(\"~\")} ${c.path.padEnd(30)} ${dimText(\"(local changed)\")}`,\n );\n }\n if (modifiedChanges.length > 10) {\n console.log(dimText(` ... and ${modifiedChanges.length - 10} more`));\n }\n }\n\n // Moved โ€” ํด๋”๋ฅผ ๋จผ์ € ๋ณด์ธ๋‹ค. ๊ทธ ์•ˆ์˜ ๋…ธํŠธ๋Š” ์•„๋ž˜์— ๋…ธํŠธ๋งˆ๋‹ค ๋”ฐ๋กœ ์žˆ๋‹ค.\n if (folderMoves.length + movedChanges.length > 0) {\n console.log(`\\n ${header(\"Moved:\")}`);\n for (const move of folderMoves) {\n console.log(` ${icons.move} ${move.from}/ โ†’ ${move.to}/ ${dimText(\"(folder)\")}`);\n }\n for (const c of movedChanges.slice(0, 10)) {\n console.log(` ${icons.move} ${c.movedFrom ?? \"?\"} โ†’ ${c.path}`);\n }\n if (movedChanges.length > 10) {\n console.log(dimText(` ... and ${movedChanges.length - 10} more`));\n }\n }\n\n // Deleted files โ€” ์ˆ˜๋งŒ ๋ณด์ด๋ฉด ๋ฌด์—‡์ด Notion ์—์„œ ์ง€์›Œ์งˆ์ง€ ๋ชจ๋ฅธ๋‹ค.\n if (deletedChanges.length > 0) {\n console.log(`\\n ${header(\"Deleted files:\")}`);\n for (const c of deletedChanges.slice(0, 10)) {\n console.log(` ${chalk.red(\"-\")} ${c.path}`);\n }\n if (deletedChanges.length > 10) {\n console.log(dimText(` ... and ${deletedChanges.length - 10} more`));\n }\n }\n\n // New files\n if (createdChanges.length > 0) {\n console.log(`\\n ${header(\"New files:\")}`);\n for (const c of createdChanges.slice(0, 10)) {\n console.log(` ${chalk.cyan(\"+\")} ${c.path.padEnd(30)} ${dimText(\"(untracked)\")}`);\n }\n if (createdChanges.length > 10) {\n console.log(dimText(` ... and ${createdChanges.length - 10} more`));\n }\n }\n\n // Remote changes (Notion-side)\n if (status.remoteChanges.length > 0) {\n console.log(`\\n ${header(`Remote changes (Notion): ${status.remoteChanges.length}`)}`);\n if (remoteCreated.length > 0) {\n console.log(` ${chalk.cyan(\"+\")} ${chalk.cyan(\"new\")} ${remoteCreated.length}`);\n }\n if (remoteModified.length > 0) {\n console.log(\n ` ${chalk.yellow(\"~\")} ${chalk.yellow(\"modified\")} ${remoteModified.length}`,\n );\n }\n if (remoteMoved.length > 0) {\n console.log(` ${icons.move} ${chalk.cyan(\"moved\")} ${remoteMoved.length}`);\n }\n if (remoteDeleted.length > 0) {\n console.log(` ${chalk.red(\"-\")} ${chalk.red(\"deleted\")} ${remoteDeleted.length}`);\n }\n for (const c of status.remoteChanges.slice(0, 10)) {\n const icon =\n c.type === \"created\"\n ? chalk.cyan(\"+\")\n : c.type === \"modified\"\n ? chalk.yellow(\"~\")\n : c.type === \"moved\"\n ? icons.move\n : chalk.red(\"-\");\n // ๋ณผํŠธ์— ์žˆ๋Š” ๋…ธํŠธ๋Š” ๊ฒฝ๋กœ, ์•„์ง ์—†๋Š” ์ƒˆ ํŽ˜์ด์ง€๋Š” Notion ์ œ๋ชฉ โ€” ๋‚ด๋ถ€ id ๋Š” ๋‘˜ ๋‹ค ์—†์„ ๋•Œ๋งŒ.\n const name = c.path ?? (c.title ? `${c.title} ${dimText(\"(new page)\")}` : undefined);\n console.log(\n ` ${icon} ${name ?? `${c.pageId.slice(0, 8)}...`} ${dimText(`(${c.type})`)}`,\n );\n }\n if (status.remoteChanges.length > 10) {\n console.log(dimText(` ... and ${status.remoteChanges.length - 10} more`));\n }\n }\n\n // Conflicts\n if (conflictCount > 0) {\n console.log(`\\n ${header(chalk.magenta(\"Conflicts:\"))}`);\n // ์›๊ฒฉ์„ ์ฝ์€ ์ƒํƒœ(--full)๋งŒ Notion ์—์„œ ์ง€์šด ๋…ธํŠธ๋ฅผ ๊ฐ€๋ฅธ๋‹ค.\n const remoteDeleted = new Set(\n status.conflicts.filter(isRemoteDeletion).map((c) => c.syncRecord.obsidianPath),\n );\n for (const r of status.conflictRecords) {\n const reason = remoteDeleted.has(r.obsidianPath)\n ? \"(deleted in Notion, local edits not pushed)\"\n : \"(both sides changed)\";\n console.log(` ${icons.conflict} ${r.obsidianPath.padEnd(30)} ${dimText(reason)}`);\n }\n }\n\n // Helpful commands\n const hasChanges =\n status.localChanges.length > 0 ||\n folderMoves.length > 0 ||\n status.remoteChanges.length > 0 ||\n conflictCount > 0;\n if (hasChanges) {\n console.log(\"\");\n if (conflictCount > 0) {\n console.log(\n ` ${dimText(\"Run\")} ${chalk.cyan(\"nobsi resolve\")} ${dimText(\"to resolve conflicts\")}`,\n );\n }\n console.log(\n ` ${dimText(\"Run\")} ${chalk.cyan(\"nobsi sync\")} ${dimText(\"to push/pull changes\")}`,\n );\n // ๋˜๋Œ๋ฆด ์ˆ˜ ์žˆ๋Š” ๊ฒƒ(๊ณ ์นœ ยท ์ง€์šด ๋…ธํŠธ)์ด ์žˆ์„ ๋•Œ๋งŒ โ€” git status ์˜ `git restore` ์•ˆ๋‚ด์™€ ๊ฐ™๋‹ค.\n if (modifiedChanges.length + deletedChanges.length > 0) {\n console.log(\n ` ${dimText(\"Run\")} ${chalk.cyan(\"nobsi discard <path>\")} ${dimText(\"to discard local edits\")}`,\n );\n }\n } else {\n console.log(`\\n ${chalk.green(\"Everything up to date\")} โœ“`);\n }\n if (!opts.full) {\n console.log(\n `\\n ${dimText(\"Run\")} ${chalk.cyan(\"nobsi status --full\")} ${dimText(\"to check Notion remote changes\")}`,\n );\n } else if (status.remoteScan?.deletionsDeferred) {\n // ๋ฐ”๋€ ๊ฒƒ๋งŒ ์ฐพ์•˜๋‹ค โ€” ์œ„ ์›๊ฒฉ ๋ณ€๊ฒฝ์—๋Š” Notion ์—์„œ ์ง€์šด ๊ฒƒ์ด ์•„์ง ์—†๋‹ค.\n console.log(`\\n ${remoteDeletionHint(status.remoteScan)}`);\n }\n } finally {\n stateDb.close();\n }\n });\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n formatUnifiedDiff,\n lineDiff,\n matchesPathScope,\n} from \"@im-nobsidian/core\";\nimport type { ChangeDiff } from \"@im-nobsidian/core\";\nimport { printUnifiedDiff } from \"../utils/diff-output.js\";\nimport { openVaultFs } from \"../utils/vault.js\";\n\n// ๊ธฐ๋ณธ ๋น„๊ต ๋Œ€์ƒ์€ **๋งˆ์ง€๋ง‰ ๋™๊ธฐํ™” ์Šค๋ƒ…์ƒท**์ด๋‹ค(๋กœ์ปฌ์—์„œ ๋ฌด์—‡์„ ๊ณ ์ณค๋Š”์ง€). Notion ํ˜„์žฌ\n// ๋ณธ๋ฌธ๊ณผ์˜ ๋น„๊ต๋Š” API ํ˜ธ์ถœ์ด ํ•„์š”ํ•˜๋ฏ€๋กœ `--remote` ๋กœ ๋ช…์‹œํ•  ๋•Œ๋งŒ ํ•œ๋‹ค โ€” ์˜ˆ์ „ ์„ค๋ช…์€\n// \"๋กœ์ปฌ๊ณผ Notion ๊ฐ„์˜ ์ฐจ์ด\"๋ผ๊ณ ๋งŒ ์ ์–ด ๋‘๊ณ  ์‹ค์ œ๋กœ๋Š” ์›๊ฒฉ์„ ํ•œ ๋ฒˆ๋„ ์ฝ์ง€ ์•Š์•˜๋‹ค.\nexport const diffCommand = new Command(\"diff\")\n .description(\n \"๋กœ์ปฌ ๋ณ€๊ฒฝ์‚ฌํ•ญ ํ‘œ์‹œ (๊ธฐ๋ณธ: ๋งˆ์ง€๋ง‰ ๋™๊ธฐํ™” ์‹œ์ ๊ณผ ๋น„๊ต, --remote: Notion ํ˜„์žฌ๋ณธ๊ณผ ๋น„๊ต)\",\n )\n .argument(\"[path]\", \"ํŠน์ • ํŒŒ์ผ ๊ฒฝ๋กœ (์ƒ๋žต ์‹œ ๋ณ€๊ฒฝ๋œ ๋ชจ๋“  ํŒŒ์ผ)\")\n .option(\"--color\", \"์ปฌ๋Ÿฌ ์ถœ๋ ฅ\", true)\n .option(\"--remote\", \"Notion ํ˜„์žฌ ๋ณธ๋ฌธ๊ณผ ๋น„๊ต (API ํ˜ธ์ถœ, ๋А๋ฆผ)\")\n .action(async (targetPath: string | undefined, options) => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n try {\n const client = NotionClient.fromConfig(config);\n const vaultFs = await openVaultFs(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n const status = await orchestrator.statusLocal();\n\n const changes = targetPath\n ? status.localChanges.filter((c) => matchesPathScope(c.path, targetPath))\n : status.localChanges;\n\n if (changes.length === 0) {\n console.log(\"๋ณ€๊ฒฝ์‚ฌํ•ญ ์—†์Œ\");\n return;\n }\n\n let failed = 0;\n for (const change of changes) {\n // --remote: ๋งˆ์ง€๋ง‰ ์Šค๋ƒ…์ƒท์ด ์•„๋‹ˆ๋ผ Notion ์˜ ํ˜„์žฌ ๋ณธ๋ฌธ์„ ๊ธฐ์ค€์„ ์œผ๋กœ ์‚ผ๋Š”๋‹ค.\n // ์›๊ฒฉ์ด ๊ทธ ์‚ฌ์ด ๋ฐ”๋€Œ์—ˆ์„ ๋•Œ \"๋‚ด๊ฐ€ ๋ญ˜ ๋ฎ์–ด์“ฐ๊ฒŒ ๋˜๋Š”์ง€\"๋Š” ์ด ๋น„๊ต๋กœ๋งŒ ๋ณด์ธ๋‹ค.\n if (options.remote && change.type !== \"deleted\") {\n // ์˜ฎ๊ธด ๋…ธํŠธ๋Š” Notion ์— ๋ฐ˜์˜ํ•˜๊ธฐ ์ „๊นŒ์ง€ ์ถ”์  ๊ธฐ๋ก์ด ์˜› ์ž๋ฆฌ์— ์žˆ๋‹ค โ€” Notion ์ชฝ ์ด๋ฆ„๋„ ๊ทธ ์ž๋ฆฌ๋‹ค.\n let remotePath = change.path;\n let remote = await orchestrator.renderRemoteSnapshot(remotePath);\n if (remote === null && change.movedFrom) {\n remotePath = change.movedFrom;\n remote = await orchestrator.renderRemoteSnapshot(remotePath);\n }\n if (remote === null) {\n console.log(`--- (์›๊ฒฉ ์—†์Œ) ${change.path}`);\n console.log(`+++ b/${change.path}`);\n console.log(`(์•„์ง Notion ์— ์—†๋Š” ํŒŒ์ผ์ž…๋‹ˆ๋‹ค)`);\n console.log(\"\");\n continue;\n }\n const currentContent = await vaultFs.readFile(change.path);\n const hunks = lineDiff(remote, currentContent);\n const lines = formatUnifiedDiff(hunks, `notion/${remotePath}`, `b/${change.path}`);\n // ์ด๋ฆ„ ์ค„๋งŒ ์ฐ๊ณ  ๋๋‚˜๋ฉด ๊ฐ™์€ ๊ฒƒ์ธ์ง€ ๋ชป ์ฝ์€ ๊ฒƒ์ธ์ง€ ์•Œ ์ˆ˜ ์—†๋‹ค โ€” ๊ฐ™๋‹ค๊ณ  ์ ๋Š”๋‹ค.\n if (hunks.length === 0) lines.push(\"(Notion ๊ณผ ๋‚ด์šฉ์ด ๊ฐ™์Šต๋‹ˆ๋‹ค)\");\n printUnifiedDiff(lines, options.color);\n console.log(\"\");\n continue;\n }\n\n // ์ง€๋‚œ ๋™๊ธฐํ™” ๋•Œ์˜ ๊ธ€๊ณผ ์ง€๊ธˆ ๊ธ€ โ€” ํ”Œ๋Ÿฌ๊ทธ์ธ ๋ณ€๊ฒฝ ํŒจ๋„์˜ ๋น„๊ต์™€ ๊ฐ™์€ ๋‘ ๊ธ€์ด๋‹ค.\n let diff: ChangeDiff;\n try {\n diff = await orchestrator.localChangeDiff(change);\n } catch (error) {\n failed++;\n console.log(`--- a/${change.path}`);\n console.log(`(${error instanceof Error ? error.message : String(error)})`);\n console.log(\"\");\n continue;\n }\n printChangeDiff(diff, options.color);\n console.log(\"\");\n }\n // ๋ณด์ด์ง€ ๋ชปํ•œ ๋น„๊ต๊ฐ€ ์žˆ์œผ๋ฉด ์‹คํŒจ๋กœ ๋๋‚ธ๋‹ค โ€” ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๋น ์ง„ ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๊ฒŒ.\n if (failed > 0) process.exitCode = 1;\n } finally {\n stateDb.close();\n }\n });\n\n/** ์˜ฎ๊ธด ๋…ธํŠธ๋Š” Git ์ฒ˜๋Ÿผ ์˜› ์ž๋ฆฌ ยท ์ƒˆ ์ž๋ฆฌ๋ฅผ ๋จผ์ € ์ ๋Š”๋‹ค. ๋‚ด์šฉ์ด ๊ฐ™์œผ๋ฉด ๋น„๊ต ๋Œ€์‹  ๊ทธ๋ ‡๋‹ค๊ณ  ์•Œ๋ฆฐ๋‹ค. */\nfunction printChangeDiff(diff: ChangeDiff, color: boolean): void {\n const oldPath = diff.before === null ? \"/dev/null\" : `a/${diff.movedFrom ?? diff.path}`;\n const newPath = diff.after === null ? \"/dev/null\" : `b/${diff.path}`;\n if (diff.movedFrom) {\n console.log(`rename from ${diff.movedFrom}`);\n console.log(`rename to ${diff.path}`);\n if (diff.before === diff.after) {\n console.log(\"(๋‚ด์šฉ์€ ๊ทธ๋Œ€๋กœ์ž…๋‹ˆ๋‹ค โ€” ์ž๋ฆฌ๋งŒ ์˜ฎ๊ฒผ์Šต๋‹ˆ๋‹ค)\");\n return;\n }\n }\n printUnifiedDiff(\n formatUnifiedDiff(lineDiff(diff.before ?? \"\", diff.after ?? \"\"), oldPath, newPath),\n color,\n );\n}\n","import { DIFF_SIGN } from \"@im-nobsidian/core\";\n\nconst BOLD = \"\\x1b[1m\";\nconst RED = \"\\x1b[31m\";\nconst GREEN = \"\\x1b[32m\";\nconst CYAN = \"\\x1b[36m\";\nconst RESET = \"\\x1b[0m\";\n\n/** ์ค„ ์ฒซ ๊ธ€์ž๋งˆ๋‹ค์˜ ์ƒ‰ โ€” ๋”ํ•œ ์ค„ ยท ์ง€์šด ์ค„์˜ ๋ถ€ํ˜ธ๋Š” core ์˜ ๊ฒƒ์„ ๋”ฐ๋ฅธ๋‹ค. `@` ๋Š” ๋ฌถ์Œ ๋จธ๋ฆฌ. */\nconst LINE_COLOR: Readonly<Record<string, string>> = {\n [DIFF_SIGN.added]: GREEN,\n [DIFF_SIGN.removed]: RED,\n \"@\": CYAN,\n};\n\n/**\n * ํ†ตํ•ฉ diff ์ค„(core `formatUnifiedDiff`)์„ ์ฐ๋Š”๋‹ค โ€” `diff` ยท `resolve` ๊ฐ€ ๊ฐ™์€ ๋ชจ์–‘ ยท ๊ฐ™์€ ์ƒ‰์œผ๋กœ ๋ณด์ธ๋‹ค.\n *\n * ์˜› ๊ธ€ ยท ์ƒˆ ๊ธ€ ์ด๋ฆ„ ์ค„์€ ์ฒซ ๊ธ€์ž๊ฐ€ ์•„๋‹ˆ๋ผ ์ž๋ฆฌ(์•ž ๋‘ ์ค„)๋กœ ๊ฐ€๋ฅธ๋‹ค โ€” ๊ตฌ๋ถ„์„ (`---`)์„ ์ง€์šด ์ค„์€ `----` ๋กœ\n * ์‹œ์ž‘ํ•ด ์ฒซ ๊ธ€์ž๋กœ๋Š” ์ด๋ฆ„ ์ค„๊ณผ ๊ตฌ๋ณ„๋˜์ง€ ์•Š๋Š”๋‹ค.\n */\nexport function printUnifiedDiff(lines: readonly string[], color: boolean): void {\n lines.forEach((line, index) => {\n const code = index < 2 ? BOLD : LINE_COLOR[line.charAt(0)];\n console.log(color && code ? `${code}${line}${RESET}` : line);\n });\n}\n","import { Command } from \"commander\";\nimport { ConfigManager, StateDB, NotionClient, SyncOrchestrator } from \"@im-nobsidian/core\";\nimport { icons, failedItem } from \"../utils/format.js\";\nimport { openVaultFs } from \"../utils/vault.js\";\n\n// ๋ณ€๊ฒฝ ๋ชฉ๋ก(status)์—์„œ ๊ณ ๋ฅธ ๋…ธํŠธ๋ฅผ ์ง€๋‚œ ๋™๊ธฐํ™” ๋•Œ์˜ ๊ธ€๋กœ ๋˜๋Œ๋ฆฐ๋‹ค โ€” Git ์˜ `restore` ์™€ ๊ฐ™๋‹ค.\n// ํ”Œ๋Ÿฌ๊ทธ์ธ ๋ณ€๊ฒฝ ํŒจ๋„์˜ โ†บ ์™€ ๊ฐ™์€ ๋™์ž‘(orchestrator.discardLocalChange)์ด๊ณ  Notion ์€ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค.\n// ํ•˜๋‚˜๋ฅผ ๋˜๋Œ๋ฆฌ์ง€ ๋ชปํ•ด๋„ ๋‚˜๋จธ์ง€๋Š” ๋˜๋Œ๋ฆฐ๋‹ค โ€” ๋ฌด์—‡์ด ์™œ ์•ˆ ๋๋Š”์ง€ ๊ฒฝ๋กœ๋งˆ๋‹ค ์•Œ๋ฆฌ๊ณ  ์ข…๋ฃŒ ์ฝ”๋“œ๋กœ ๋‚จ๊ธด๋‹ค.\nexport const discardCommand = new Command(\"discard\")\n .description(\"๋กœ์ปฌ ๋ณ€๊ฒฝ์„ ์ง€๋‚œ ๋™๊ธฐํ™” ๋•Œ์˜ ๊ธ€๋กœ ๋˜๋Œ๋ฆฌ๊ธฐ (Notion ์€ ๊ทธ๋Œ€๋กœ)\")\n .argument(\"<paths...>\", \"๋˜๋Œ๋ฆด ๋…ธํŠธ ๊ฒฝ๋กœ โ€” status ๊ฐ€ ๋ณด์ธ ๋ณผํŠธ ๊ธฐ์ค€ ๊ฒฝ๋กœ\")\n .action(async (paths: string[]) => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n try {\n const client = NotionClient.fromConfig(config);\n const vaultFs = await openVaultFs(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n let failed = 0;\n for (const path of paths) {\n try {\n await orchestrator.discardLocalChange(path);\n console.log(` ${icons.success} ${path}`);\n } catch (error) {\n failed++;\n failedItem(path, error instanceof Error ? error.message : String(error));\n }\n }\n if (failed > 0) process.exitCode = 1;\n } finally {\n stateDb.close();\n }\n });\n","import { Command } from \"commander\";\nimport { ConfigManager, StateDB, NotionClient, SyncOrchestrator } from \"@im-nobsidian/core\";\nimport chalk from \"chalk\";\nimport { header, separator, dimText } from \"../utils/format.js\";\nimport { openVaultFs } from \"../utils/vault.js\";\n\nexport const fetchCommand = new Command(\"fetch\")\n .description(\"Notion ๋ฆฌ๋ชจํŠธ ์ƒํƒœ ์Šค์บ” (์‚ญ์ œ ๊ฐ์ง€ ํฌํ•จ)\")\n .action(async () => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n try {\n const client = NotionClient.fromConfig(config);\n const vaultFs = await openVaultFs(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n console.log(dimText(\" Scanning remote pages...\"));\n\n const result = await orchestrator.fetch();\n\n console.log(`\\n${header(\" Remote Scan\")}`);\n console.log(` ${separator(40)}`);\n\n if (result.newPages > 0) {\n console.log(` ${chalk.cyan(\"+\")} New pages: ${result.newPages}`);\n }\n if (result.modifiedPages > 0) {\n console.log(` ${chalk.yellow(\"~\")} Modified pages: ${result.modifiedPages}`);\n }\n if (result.deletedPages > 0) {\n console.log(` ${chalk.red(\"-\")} Deleted pages: ${result.deletedPages}`);\n }\n\n const total = result.newPages + result.modifiedPages + result.deletedPages;\n if (total === 0) {\n console.log(` ${chalk.green(\"Remote is up to date\")} โœ“`);\n } else {\n console.log(\n `\\n ${dimText(\"Run\")} ${chalk.cyan(\"nobsi pull\")} ${dimText(\"to apply remote changes\")}`,\n );\n }\n\n console.log(dimText(`\\n Completed in ${(result.duration / 1000).toFixed(1)}s`));\n } finally {\n stateDb.close();\n }\n });\n","import { Command } from \"commander\";\nimport { select, confirm } from \"@inquirer/prompts\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n applicableChoices,\n isRemoteDeletion,\n} from \"@im-nobsidian/core\";\nimport type { Conflict, ResolutionChoice } from \"@im-nobsidian/core\";\nimport { printUnifiedDiff } from \"../utils/diff-output.js\";\nimport { describeFailure } from \"../utils/state-db-failure.js\";\nimport { openVaultFs } from \"../utils/vault.js\";\n\n/** ํ—ˆ์šฉ ์ „๋žต โ€” ์˜คํƒ€๋ฅผ ์กฐ์šฉํžˆ ํ˜๋ฆฌ์ง€ ์•Š๋„๋ก ์‹คํ–‰ ์ „์— ํ•œ ๋ฒˆ ๊ฒ€์ฆํ•œ๋‹ค. */\nconst STRATEGIES = [\"local-first\", \"remote-first\", \"duplicate\"] as const;\ntype Strategy = (typeof STRATEGIES)[number];\n\nconst CHOICE_NAMES: Record<ResolutionChoice, string> = {\n local: \"๋กœ์ปฌ ์œ ์ง€ (Obsidian ๋ฒ„์ „ ์œ ์ง€)\",\n remote: \"์›๊ฒฉ ์œ ์ง€ (Notion ๋ฒ„์ „์œผ๋กœ ๋ฎ์–ด์“ฐ๊ธฐ)\",\n merge: \"์ž๋™ ๋ณ‘ํ•ฉ (3-way merge)\",\n duplicate: \"๋ณต์ œ (์›๋ณธ ์œ ์ง€ + ์˜ฌ๋ฆฌ์ง€ ์•Š๋Š” .conflict ์‚ฌ๋ณธ ์ƒ์„ฑ)\",\n};\n\n/** Notion ์—์„œ ์ง€์šด ๋…ธํŠธ๋Š” ๊ฐ™์€ ์„ ํƒ์ด ๋‹ค๋ฅธ ์ผ์„ ํ•œ๋‹ค โ€” ๋กœ์ปฌ ์œ ์ง€๋Š” ๋‹ค์‹œ ๋งŒ๋“ค๊ณ , ์›๊ฒฉ ์œ ์ง€๋Š” ์ง€์šด๋‹ค. */\nconst REMOTE_DELETION_CHOICE_NAMES: Partial<Record<ResolutionChoice, string>> = {\n local: \"๋กœ์ปฌ ์œ ์ง€ (Notion ์— ์ƒˆ ํŽ˜์ด์ง€๋กœ ๋‹ค์‹œ ๋งŒ๋“ค๊ธฐ)\",\n remote: \"์‚ญ์ œ ๋”ฐ๋ฅด๊ธฐ (Obsidian ํŒŒ์ผ๋„ ์ง€์šฐ๊ธฐ)\",\n};\n\n/** ๋ฐ›๊ธฐ ์ „์šฉ(direction: pull)์€ ํ•ด๊ฒฐ ๊ฒฐ๊ณผ๋ฅผ Notion ์— ์˜ฌ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค โ€” ๋‹ค์‹œ ๋งŒ๋“ค์ง€๋„ ์•Š๋Š”๋‹ค. */\nconst PULL_ONLY_REMOTE_DELETION_CHOICE_NAMES: Partial<Record<ResolutionChoice, string>> = {\n local: \"๋กœ์ปฌ ์œ ์ง€ (Obsidian ํŒŒ์ผ๋งŒ ๋‘๊ธฐ โ€” ๋ฐ›๊ธฐ ์ „์šฉ์ด๋ผ Notion ์— ๋งŒ๋“ค์ง€ ์•Š์Œ)\",\n};\n\nfunction choiceName(conflict: Conflict, choice: ResolutionChoice, uploads: boolean): string {\n const deleted = isRemoteDeletion(conflict);\n return (\n (deleted && !uploads ? PULL_ONLY_REMOTE_DELETION_CHOICE_NAMES[choice] : undefined) ??\n (deleted ? REMOTE_DELETION_CHOICE_NAMES[choice] : undefined) ??\n CHOICE_NAMES[choice] ??\n choice\n );\n}\n\n/**\n * ๋Œ€ํ™”ํ˜• ํ”„๋กฌํ”„ํŠธ๋ฅผ ๋„์šธ ์ˆ˜ ์žˆ๋Š” ํ™˜๊ฒฝ์ธ์ง€.\n *\n * ํŒŒ์ดํ”„ยทcronยทCI ์ฒ˜๋Ÿผ TTY ๊ฐ€ ์—†๋Š” ๊ณณ์—์„œ @inquirer ํ”„๋กฌํ”„ํŠธ๋ฅผ ๋„์šฐ๋ฉด ์›์ธ์„ ์•Œ ์ˆ˜ ์—†๋Š”\n * ์˜ค๋ฅ˜๋กœ ์ฃฝ๊ฑฐ๋‚˜ ์ž…๋ ฅ์„ ๊ธฐ๋‹ค๋ฆฌ๋ฉฐ ๋ฉŽ๋Š”๋‹ค. ๋จผ์ € ํŒ์ •ํ•ด ๋ฌด์—‡์„ ์–ด๋–ป๊ฒŒ ํ•˜๋ผ๊ณ  ๋งํ•ด ์ค€๋‹ค.\n */\nfunction isInteractive(): boolean {\n return process.stdin.isTTY === true && process.stdout.isTTY === true;\n}\n\nexport const resolveCommand = new Command(\"resolve\")\n .description(\"์ถฉ๋Œ์„ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค\")\n .option(\"-s, --strategy <strategy>\", `์ž๋™ ํ•ด๊ฒฐ ์ „๋žต (${STRATEGIES.join(\" | \")})`)\n .option(\"-y, --yes\", \"ํ™•์ธ ํ”„๋กฌํ”„ํŠธ ์—†์ด ์ง„ํ–‰ (๋น„๋Œ€ํ™”ํ˜•/CI)\")\n .action(async (options: { strategy?: string; yes?: boolean }) => {\n if (options.strategy && !STRATEGIES.includes(options.strategy as Strategy)) {\n console.error(`์•Œ ์ˆ˜ ์—†๋Š” ์ „๋žต '${options.strategy}' โ€” ์‚ฌ์šฉ ๊ฐ€๋Šฅ: ${STRATEGIES.join(\", \")}`);\n process.exitCode = 1;\n return;\n }\n\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n let stateDb: StateDB | undefined;\n\n try {\n const config = await configManager.load();\n stateDb = StateDB.open(configManager.dbPath);\n const client = NotionClient.fromConfig(config);\n const vaultFs = await openVaultFs(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n // ์ถฉ๋Œ ๋ชฉ๋ก์€ **์ถฉ๋Œ๋กœ ํ‘œ์‹œ๋œ ํŽ˜์ด์ง€๋งŒ** ์ฝ์–ด ์˜จ๋‹ค. ์˜ˆ์ „์—” ๋ชฉ๋ก์„ ์–ป๊ฒ ๋‹ค๊ณ  ์ „์ฒด pull ์„\n // ๋Œ๋ ธ๋Š”๋ฐ, ๊ทธ๊ฑด ํ•ด์†Œํ•˜๊ธฐ๋„ ์ „์— ๋ณผํŠธ๋ฅผ ์›๊ฒฉ์œผ๋กœ ๋ฎ์–ด์“ฐ๋Š” ์ง“์ด๋ผ ์ˆœ์„œ๊ฐ€ ๊ฑฐ๊พธ๋กœ์˜€๋‹ค.\n const conflicts = await orchestrator.listConflicts();\n\n if (conflicts.length === 0) {\n console.log(\"์ถฉ๋Œ์ด ์—†์Šต๋‹ˆ๋‹ค.\");\n return;\n }\n\n // ์–‘์ชฝ์ด ์ด๋ฏธ ๊ฐ™์•„์ง„ ํ•ญ๋ชฉ์€ ๋ฌผ์–ด๋ณผ ๊ฒŒ ์—†๋‹ค. ๊ทธ๋Œ€๋กœ ๋‘๋ฉด push ๊ฐ€ ๊ทธ ํŒŒ์ผ์„ ์˜์˜\n // ๊ฑด๋„ˆ๋›ฐ์–ด(์ถฉ๋Œ ์ƒํƒœ๋Š” push ์ œ์™ธ) ์ดํ›„ ํŽธ์ง‘์ด ์กฐ์šฉํžˆ ์ •์ฒดํ•˜๋ฏ€๋กœ ๋จผ์ € ๊ฑท์–ด๋‚ธ๋‹ค.\n const cleared = new Set(orchestrator.clearStaleConflicts(conflicts));\n for (const path of cleared) {\n console.log(` \\x1b[32mโœ“\\x1b[0m ${path} โ†’ ์–‘์ชฝ ๋™์ผ, ์ถฉ๋Œ ํ‘œ์‹œ๋งŒ ํ•ด์ œ`);\n }\n\n const pending = conflicts.filter((c) => !cleared.has(c.syncRecord.obsidianPath));\n if (pending.length === 0) {\n console.log(`\\n${cleared.size}๊ฑด ๋ชจ๋‘ ์ด๋ฏธ ํ•ด์†Œ๋œ ์ƒํƒœ์˜€์Šต๋‹ˆ๋‹ค โ€” ์ƒํƒœ๋งŒ ์ •๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.`);\n return;\n }\n\n console.log(`\\n์ถฉ๋Œ ${pending.length}๊ฑด ๋ฐœ๊ฒฌ\\n`);\n const uploads = config.sync.direction !== \"pull\";\n if (!uploads) {\n console.log(\n \" ๋ฐ›๊ธฐ ์ „์šฉ(direction: pull) โ€” ํ•ด๊ฒฐ ๊ฒฐ๊ณผ๋Š” Obsidian ์—๋งŒ ๋‘๊ณ  Notion ์—๋Š” ์˜ฌ๋ฆฌ์ง€ ์•Š์Šต๋‹ˆ๋‹ค\\n\",\n );\n }\n\n if (options.strategy) {\n const strategy = options.strategy as Strategy;\n // ์›๊ฒฉ์—์„œ ์ง€์šด ๋…ธํŠธ๋Š” ์ „๋žต์ด ํŒŒ์ผ์„ ์ง€์šฐ๊ฑฐ๋‚˜ ํŽ˜์ด์ง€๋ฅผ ์ƒˆ๋กœ ๋งŒ๋“ ๋‹ค โ€” ๋ฌป๊ธฐ ์ „์— ์•Œ๋ฆฐ๋‹ค.\n const deletions = pending.filter(isRemoteDeletion).length;\n if (deletions > 0) {\n console.log(\n ` Notion ์—์„œ ์‚ญ์ œ๋œ ๋…ธํŠธ ${deletions}๊ฑด โ€” ${\n strategy === \"remote-first\"\n ? \"Obsidian ํŒŒ์ผ๋„ ์ง€์›๋‹ˆ๋‹ค\"\n : uploads\n ? \"Notion ์— ์ƒˆ ํŽ˜์ด์ง€๋กœ ๋‹ค์‹œ ๋งŒ๋“ญ๋‹ˆ๋‹ค\"\n : \"Obsidian ํŒŒ์ผ์„ ๊ทธ๋Œ€๋กœ ๋‘ก๋‹ˆ๋‹ค\"\n }`,\n );\n }\n\n if (!options.yes) {\n if (!isInteractive()) {\n console.error(\n `๋น„๋Œ€ํ™”ํ˜• ํ™˜๊ฒฝ์ž…๋‹ˆ๋‹ค โ€” '${strategy}' ์ „๋žต์œผ๋กœ ${pending.length}๊ฑด์„ ์ผ๊ด„ ํ•ด๊ฒฐํ•˜๋ ค๋ฉด --yes ๋ฅผ ํ•จ๊ป˜ ์ง€์ •ํ•˜์„ธ์š”.`,\n );\n process.exitCode = 1;\n return;\n }\n const shouldProceed = await confirm({\n message: `${pending.length}๊ฑด์˜ ์ถฉ๋Œ์„ '${strategy}' ์ „๋žต์œผ๋กœ ์ผ๊ด„ ํ•ด๊ฒฐํ• ๊นŒ์š”?`,\n });\n if (!shouldProceed) return;\n }\n\n const results = await orchestrator.resolveAllConflicts(pending, strategy);\n let failed = 0;\n for (const [index, result] of results.entries()) {\n if (!result.success) failed++;\n const icon = result.success ? \"\\x1b[32mโœ“\\x1b[0m\" : \"\\x1b[31mโœ—\\x1b[0m\";\n const reason = result.error ? ` โ€” ${result.error}` : \"\";\n console.log(\n ` ${icon} ${result.path} โ†’ ${choiceName(pending[index]!, result.choice, uploads)}${reason}`,\n );\n }\n report(results.length - failed, failed);\n return;\n }\n\n if (!isInteractive()) {\n console.error(\n `๋น„๋Œ€ํ™”ํ˜• ํ™˜๊ฒฝ์—์„œ๋Š” ๋Œ€ํ™”ํ˜• ํ•ด๊ฒฐ์„ ์“ธ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค โ€” --strategy <${STRATEGIES.join(\"|\")}> --yes ๋ฅผ ์ง€์ •ํ•˜์„ธ์š”.`,\n );\n process.exitCode = 1;\n return;\n }\n\n let resolved = 0;\n let failed = 0;\n for (const conflict of pending) {\n if (await resolveInteractive(conflict, orchestrator, uploads)) resolved++;\n else failed++;\n }\n report(resolved, failed);\n } catch (error) {\n console.error(\"์˜ค๋ฅ˜:\", describeFailure(error));\n process.exitCode = 1;\n } finally {\n // ์˜ˆ์ „์—” ๋ถ„๊ธฐ๋งˆ๋‹ค close ๋ฅผ ํฉ๋ฟŒ๋ ค ๋‘๊ณ  catch ๊ฒฝ๋กœ์—์„  ์•„์˜ˆ ๋‹ซ์ง€ ์•Š์•˜๋‹ค โ€” ์ž ๊ธˆ์ด ๋‚จ๋Š”๋‹ค.\n stateDb?.close();\n }\n });\n\n/** ๊ฒฐ๊ณผ๋ฅผ ์‚ฌ์‹ค๋Œ€๋กœ ์•Œ๋ฆฐ๋‹ค โ€” ์‹คํŒจ๊ฐ€ ํ•˜๋‚˜๋ผ๋„ ์žˆ์œผ๋ฉด \"์™„๋ฃŒ\"๋ผ๊ณ  ๋งํ•˜์ง€ ์•Š๋Š”๋‹ค. */\nfunction report(resolved: number, failed: number): void {\n if (failed === 0) {\n console.log(`\\n์ถฉ๋Œ ํ•ด๊ฒฐ ์™„๋ฃŒ โ€” ${resolved}๊ฑด`);\n return;\n }\n console.log(`\\n\\x1b[33m์ถฉ๋Œ ${resolved}๊ฑด ํ•ด๊ฒฐ ยท ${failed}๊ฑด ๋ฏธํ•ด๊ฒฐ\\x1b[0m`);\n process.exitCode = 1;\n}\n\n/** @returns ํ•ด์†Œ์— ์„ฑ๊ณตํ–ˆ์œผ๋ฉด true. ๋ณ‘ํ•ฉ ๋งˆ์ปค๊ฐ€ ๋‚จ์€ ๊ฒฝ์šฐ๋Š” ๋ฏธํ•ด๊ฒฐ๋กœ ์„ผ๋‹ค. */\nasync function resolveInteractive(\n conflict: Conflict,\n orchestrator: SyncOrchestrator,\n uploads: boolean,\n): Promise<boolean> {\n const path = conflict.syncRecord.obsidianPath;\n\n console.log(`\\n${\"โ”€\".repeat(60)}`);\n console.log(`ํŒŒ์ผ: \\x1b[1m${path}\\x1b[0m`);\n console.log(`${\"โ”€\".repeat(60)}`);\n\n if (isRemoteDeletion(conflict)) {\n // ๊ฒฌ์ค„ ์›๊ฒฉ ๋ณธ๋ฌธ์ด ์—†๋‹ค โ€” ์ค„ ๋น„๊ต๋Š” ๋กœ์ปฌ ์ „๋ถ€๋ฅผ ์ง€์šด ๊ฒƒ์œผ๋กœ ๋ณด์—ฌ ์ค€๋‹ค.\n console.log(\n \"\\x1b[33mNotion ์—์„œ ์‚ญ์ œ๋œ ๋…ธํŠธ์ž…๋‹ˆ๋‹ค โ€” Notion ์— ์˜ฌ๋ฆฌ์ง€ ์•Š์€ ๋กœ์ปฌ ํŽธ์ง‘์ด ๋‚จ์•„ ์žˆ์Šต๋‹ˆ๋‹ค.\\x1b[0m\",\n );\n } else {\n printUnifiedDiff(orchestrator.generateConflictDiff(conflict).split(\"\\n\"), true);\n }\n\n const choice = await select<ResolutionChoice>({\n message: \"์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ• ๊นŒ์š”?\",\n choices: applicableChoices(conflict).map((value) => ({\n name: choiceName(conflict, value, uploads),\n value,\n })),\n });\n\n const result = await orchestrator.resolveConflict(conflict, choice);\n\n if (result.success) {\n console.log(`\\x1b[32mโœ“ ${path} โ†’ ${choiceName(conflict, choice, uploads)}\\x1b[0m`);\n return true;\n }\n if (result.mergeHadConflicts) {\n console.log(`\\x1b[33mโš  ${path} โ†’ ์ž๋™ ๋ณ‘ํ•ฉ ์™„๋ฃŒ (์ˆ˜๋™ ํ™•์ธ ํ•„์š”ํ•œ ์ถฉ๋Œ ๋งˆ์ปค ์žˆ์Œ)\\x1b[0m`);\n return false;\n }\n console.log(`\\x1b[31mโœ— ${path} โ†’ ํ•ด๊ฒฐ ์‹คํŒจ\\x1b[0m`);\n return false;\n}\n","/**\n * ์ƒํƒœ DB ๋ฅผ ์“ธ ์ˆ˜ ์—†์„ ๋•Œ์˜ CLI ์ถœ๋ ฅ โ€” ์ด์œ  ๋’ค์— ํ•  ์ผ์„ ๋ถ™์ธ๋‹ค.\n *\n * ๋ช…๋ น์€ ์ฒ˜์Œ์— ์ƒํƒœ DB ๋ฅผ ์—ฐ๋‹ค(`StateDB.open`). ํŒŒ์ผ์ด ๋น„์—ˆ๊ฑฐ๋‚˜ ์ž˜๋ ธ๊ฑฐ๋‚˜(`SavedStateDbError`), ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๊ฐ™์€\n * ๋ณผํŠธ๋ฅผ ์“ฐ๋Š” ์ค‘์ด๋ฉด(`StateDbLockedError`) ๋˜์ง€๋Š”๋ฐ, ๋ช…๋ น ๋ฐ–์œผ๋กœ ๋‚˜์˜ค๋ฉด Node ๊ฐ€ ์ฝ”๋“œ ์œ„์น˜(์Šคํƒ)์™€ ํ•จ๊ป˜ ์ด์œ ๋งŒ\n * ๋ณด์˜€๋‹ค โ€” ์‚ฌ์šฉ์ž๊ฐ€ ํ•  ์ผ์€ ์–ด๋””์—๋„ ์—†์—ˆ๋‹ค.\n */\nimport { StateDbUnavailableError } from \"@im-nobsidian/core\";\nimport type { ExitableProcess } from \"./exit.js\";\n\n/** ์‹คํŒจ๋ฅผ ๋ณด์ผ ๋ง โ€” ์ƒํƒœ DB ๋ฅผ ์“ธ ์ˆ˜ ์—†๋Š” ๊ฒƒ์ด๋ฉด ํ•  ์ผ์„ ๋ถ™์ด๊ณ , ๋‹ค๋ฅธ ์‹คํŒจ๋Š” ๊ทธ ๋ง ๊ทธ๋Œ€๋กœ๋‹ค. */\nexport function describeFailure(error: unknown): string {\n const message = error instanceof Error ? error.message : String(error);\n if (!(error instanceof StateDbUnavailableError)) return message;\n return `${message} โ€” ${error.guidance(\"๋ช…๋ น์„ ๋‹ค์‹œ ์‹คํ–‰ํ•˜์„ธ์š”\")}`;\n}\n\n/**\n * ๋ช…๋ น ๋ฐ–์œผ๋กœ ๋‚˜์˜จ ์‹คํŒจ๋ฅผ ์•Œ๋ฆฐ๋‹ค. ์ƒํƒœ DB ๋ฅผ ์“ธ ์ˆ˜ ์—†๋Š” ๊ฒƒ์€ ์‚ฌ์šฉ์ž๊ฐ€ ํ’€ ์ผ์ด๋ผ ์ด์œ ์™€ ํ•  ์ผ๋งŒ ๋ณด์ด๊ณ  ์‹คํŒจ(1)๋กœ\n * ๋๋‚ธ๋‹ค. ๋‹ค๋ฅธ ์‹คํŒจ๋Š” ๊ทธ๋Œ€๋กœ ๋‹ค์‹œ ๋˜์ง„๋‹ค โ€” ์ง€๊ธˆ๊นŒ์ง€์ฒ˜๋Ÿผ Node ๊ฐ€ ์Šคํƒ๊ณผ ํ•จ๊ป˜ ๋ณด์ด๊ณ  1 ๋กœ ๋๋‚œ๋‹ค.\n */\nexport function reportStateDbFailure(error: unknown, proc: ExitableProcess): void {\n if (!(error instanceof StateDbUnavailableError)) throw error;\n console.error(`์˜ค๋ฅ˜: ${describeFailure(error)}`);\n proc.exitCode = 1;\n}\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n WatchSyncService,\n DEFAULT_WATCH_DEBOUNCE_MS,\n} from \"@im-nobsidian/core\";\nimport type { SyncResult, WatchSyncScope } from \"@im-nobsidian/core\";\nimport { openVaultFs } from \"../utils/vault.js\";\n\nfunction formatResult(result: SyncResult): string {\n const pull = `Pull: +${result.pull.created} ~${result.pull.updated} -${result.pull.deleted}`;\n const moved = result.push.moved > 0 ? ` โ†’${result.push.moved}` : \"\";\n const push = `Push: +${result.push.created} ~${result.push.updated} -${result.push.deleted}${moved}`;\n const parts = [pull, push];\n\n if (result.conflicts.length > 0) {\n parts.push(`์ถฉ๋Œ ${result.conflicts.length}๊ฑด`);\n }\n\n return parts.join(\" | \");\n}\n\nfunction timestamp(): string {\n return new Date().toLocaleTimeString(\"ko-KR\", { hour12: false });\n}\n\n/** ๋กœ๊ทธ์˜ sync ์ด๋ฆ„ โ€” ์ฃผ๊ธฐ sync ๋Š” ๋ณผํŠธ ์ „์ฒด๋ฅผ ๋ณธ๋‹ค. */\nfunction syncLabel(scope: WatchSyncScope): string {\n return scope === \"full\" ? \"์ฃผ๊ธฐ์  ๋™๊ธฐํ™”\" : \"๋™๊ธฐํ™”\";\n}\n\nexport const watchCommand = new Command(\"watch\")\n .description(\"ํŒŒ์ผ ๋ณ€๊ฒฝ ๊ฐ์ง€ + ์ž๋™ ๋™๊ธฐํ™”\")\n .option(\"-d, --debounce <ms>\", \"๋””๋ฐ”์šด์Šค ๋Œ€๊ธฐ ์‹œ๊ฐ„(ms)\", String(DEFAULT_WATCH_DEBOUNCE_MS))\n .option(\"-i, --interval <seconds>\", \"ํ’€ ๋™๊ธฐํ™” ์ฃผ๊ธฐ(์ดˆ, 0์ด๋ฉด ๋น„ํ™œ์„ฑ)\", \"0\")\n .action(async (options: { debounce: string; interval: string }) => {\n // ์ˆซ์ž๊ฐ€ ์•„๋‹ˆ๋ฉด ๋””๋ฐ”์šด์Šค๊ฐ€ 0 ์ด ๋˜๊ฑฐ๋‚˜ ์ฃผ๊ธฐ ๋™๊ธฐํ™”๊ฐ€ ๋ง์—†์ด ๊บผ์ง„๋‹ค โ€” ์‹œ์ž‘ํ•˜๊ธฐ ์ „์— ๊ฑฐ์ ˆํ•œ๋‹ค.\n const debounceMs = Number(options.debounce);\n const intervalSec = Number(options.interval);\n if (!Number.isInteger(debounceMs) || debounceMs < 0) {\n console.error(`--debounce ๋Š” 0 ์ด์ƒ์˜ ์ •์ˆ˜(ms)์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค: ${options.debounce}`);\n process.exitCode = 1;\n return;\n }\n if (!Number.isInteger(intervalSec) || intervalSec < 0) {\n console.error(`--interval ์€ 0 ์ด์ƒ์˜ ์ •์ˆ˜(์ดˆ)์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค: ${options.interval}`);\n process.exitCode = 1;\n return;\n }\n\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n const client = NotionClient.fromConfig(config);\n const vaultFs = await openVaultFs(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n const service = new WatchSyncService(cwd, orchestrator, {\n debounceMs,\n onFileChange: (event, path) => {\n console.log(`[${timestamp()}] ${event}: ${path}`);\n },\n onSyncStart: (scope) => {\n console.log(`[${timestamp()}] ${syncLabel(scope)} ์‹œ์ž‘...`);\n },\n onSyncComplete: (result, scope) => {\n console.log(`[${timestamp()}] ${syncLabel(scope)} ์™„๋ฃŒ โ€” ${formatResult(result)}`);\n },\n onSyncCancelled: (result, scope) => {\n console.log(\n `[${timestamp()}] ${syncLabel(scope)} ์ทจ์†Œ โ€” ๋ฉˆ์ถ”๊ธฐ ์ „๊นŒ์ง€ ${formatResult(result)}`,\n );\n },\n onSyncError: (error, scope) => {\n console.error(`[${timestamp()}] ${syncLabel(scope)} ์‹คํŒจ: ${error.message}`);\n },\n });\n\n let pullTimer: ReturnType<typeof setInterval> | null = null;\n let shuttingDown = false;\n\n const shutdown = async (): Promise<void> => {\n // ๋„๋Š” sync ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋Š” ์ค‘์— ํ•œ ๋ฒˆ ๋” ๋ˆ„๋ฅด๋ฉด ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  ๋๋‚ธ๋‹ค.\n if (shuttingDown) {\n console.log(`[${timestamp()}] ๊ฐ•์ œ ์ข…๋ฃŒ`);\n return process.exit(1);\n }\n shuttingDown = true;\n console.log(`\\n[${timestamp()}] ๊ฐ์‹œ ์ข…๋ฃŒ ์ค‘... (๋„๋Š” ๋™๊ธฐํ™”๋ฅผ ๋ฉˆ์ถ”๊ณ  ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค)`);\n if (pullTimer) clearInterval(pullTimer);\n await service.stop();\n stateDb.close();\n console.log(`[${timestamp()}] ์ข…๋ฃŒ ์™„๋ฃŒ`);\n process.exit(0);\n };\n\n process.on(\"SIGINT\", () => void shutdown());\n process.on(\"SIGTERM\", () => void shutdown());\n\n service.start();\n console.log(`[${timestamp()}] ํŒŒ์ผ ๊ฐ์‹œ ์‹œ์ž‘ (debounce: ${debounceMs}ms)`);\n\n if (intervalSec > 0) {\n console.log(`[${timestamp()}] ํ’€ ๋™๊ธฐํ™” ์ฃผ๊ธฐ: ${intervalSec}์ดˆ`);\n // ํŒŒ์ผ ๋ณ€๊ฒฝ sync ์™€ ๊ฐ™์€ ์ค„์— ์„ธ์šด๋‹ค โ€” ๋„๋Š” sync ๊ฐ€ ์žˆ์œผ๋ฉด ๋๋‚œ ๋’ค ํ•œ ๋ฒˆ ๋ˆ๋‹ค(S-09).\n pullTimer = setInterval(() => service.requestFullSync(), intervalSec * 1000);\n }\n\n await new Promise(() => {});\n });\n","import { Command } from \"commander\";\nimport { ConfigManager, StateDB, NotionClient, SyncOrchestrator } from \"@im-nobsidian/core\";\nimport type {\n CompletenessReport,\n PageCompletenessReport,\n VaultCompletenessReport,\n} from \"@im-nobsidian/core\";\nimport chalk from \"chalk\";\nimport { header, separator, icons, dimText } from \"../utils/format.js\";\nimport { openVaultFs } from \"../utils/vault.js\";\n\n/** ํ™”๋ฉด์— ๋Š˜์–ด๋†“์„ id ์ตœ๋Œ€ ๊ฐœ์ˆ˜ โ€” ๋‚˜๋จธ์ง€๋Š” ๊ฑด์ˆ˜๋กœ ์ ‘๋Š”๋‹ค. */\nconst SAMPLE_LIMIT = 10;\n\nfunction printIdSample(label: string, ids: readonly string[], color: (s: string) => string): void {\n console.log(` ${color(label)} ${ids.length}`);\n for (const id of ids.slice(0, SAMPLE_LIMIT)) {\n console.log(` ${dimText(id)}`);\n }\n if (ids.length > SAMPLE_LIMIT) {\n console.log(dimText(` ... and ${ids.length - SAMPLE_LIMIT} more`));\n }\n}\n\nfunction printDatabaseSection(report: CompletenessReport): void {\n console.log(`\\n${header(\" DB Completeness\")}`);\n console.log(` ${separator(50)}`);\n console.log(` ${dimText(\"Databases:\")} ${report.databases.length}`);\n console.log(` ${dimText(\"Remote rows:\")} ${report.remoteTotal}`);\n console.log(` ${dimText(\"Vault rows:\")} ${report.localTotal}`);\n\n const broken = report.databases.filter((d) => d.missingIds.length > 0 || d.extraIds.length > 0);\n\n if (broken.length > 0) {\n console.log(`\\n ${header(chalk.red(`Incomplete databases: ${broken.length}`))}`);\n for (const db of broken) {\n console.log(\n ` ${icons.fail} ${db.databaseId} ${dimText(`remote ${db.remoteRows} / vault ${db.localRows}`)}`,\n );\n if (db.missingIds.length > 0) printIdSample(\"missing\", db.missingIds, chalk.red);\n if (db.extraIds.length > 0) printIdSample(\"extra\", db.extraIds, chalk.yellow);\n }\n }\n\n if (report.failures.length > 0) {\n console.log(`\\n ${header(chalk.yellow(`Query failures: ${report.failures.length}`))}`);\n for (const failure of report.failures) {\n console.log(` ${icons.fail} ${failure.databaseId} ${dimText(failure.error)}`);\n }\n }\n\n if (report.complete) {\n console.log(`\\n ${chalk.green(\"All database rows present in vault\")} ${icons.success}`);\n }\n}\n\n/**\n * ํŽ˜์ด์ง€ ๋Œ€์กฐ ์ถœ๋ ฅ (R12-C).\n *\n * `local-only` ๋Š” ์‹คํŒจ๊ฐ€ ์•„๋‹ˆ๋ผ ์ •๋ณด๋‹ค โ€” ์•„์ง push ํ•˜์ง€ ์•Š์€ ๋กœ์ปฌ ๋…ธํŠธยทroot ํŽ˜์ด์ง€ ์ž์‹ ยท\n * search ์ƒ‰์ธ ์ง€์—ฐ์ด ์ „๋ถ€ ์—ฌ๊ธฐ์— ์ •์ƒ์ ์œผ๋กœ ๋“ค์–ด์˜จ๋‹ค. ์ƒ‰์„ ๋‹ฌ๋ฆฌ ์ค˜์„œ \"๊ณ ์ณ์•ผ ํ•  ๊ฒƒ\"๊ณผ\n * \"๊ทธ๋ƒฅ ์•Œ์•„ ๋‘˜ ๊ฒƒ\"์ด ํ™”๋ฉด์—์„œ ์„ž์ด์ง€ ์•Š๊ฒŒ ํ•œ๋‹ค.\n */\nfunction printPageSection(report: PageCompletenessReport): void {\n console.log(`\\n${header(\" Page Completeness\")}`);\n console.log(` ${separator(50)}`);\n console.log(` ${dimText(\"Remote pages:\")} ${report.remotePages}`);\n console.log(` ${dimText(\"Vault pages:\")} ${report.vaultPages}`);\n\n if (report.error) {\n console.log(`\\n ${icons.fail} ${chalk.yellow(\"Page enumeration failed\")}`);\n console.log(` ${dimText(report.error)}`);\n return;\n }\n\n if (report.missingIds.length > 0) {\n console.log(\n `\\n ${header(chalk.red(`Pages missing from vault: ${report.missingIds.length}`))}`,\n );\n printIdSample(\"missing\", report.missingIds, chalk.red);\n }\n\n if (report.localOnlyIds.length > 0) {\n console.log(\n `\\n ${dimText(`local-only pages: ${report.localOnlyIds.length} (informational)`)}`,\n );\n printIdSample(\"local-only\", report.localOnlyIds, dimText);\n }\n\n if (report.complete) {\n console.log(`\\n ${chalk.green(\"All remote pages present in vault\")} ${icons.success}`);\n }\n}\n\nfunction printReport(report: VaultCompletenessReport): void {\n printDatabaseSection(report.databases);\n\n if (report.pages) {\n printPageSection(report.pages);\n } else {\n // DB ๋ชจ๋“œ์—๋Š” root ์„œ๋ธŒํŠธ๋ฆฌ๊ฐ€ ์—†์–ด ๋Œ€์กฐํ•  ํŽ˜์ด์ง€ ์ž์ฒด๊ฐ€ ์—†๋‹ค โ€” ์ƒ๋žต์„ ๋ช…์‹œํ•œ๋‹ค.\n console.log(`\\n ${dimText(\"Page completeness: skipped (database mode)\")}`);\n }\n\n if (!report.complete) {\n console.log(\n `\\n ${dimText(\"Run\")} ${chalk.cyan(\"nobsi pull\")} ${dimText(\"to fetch what is missing\")}`,\n );\n }\n}\n\nexport const verifyCommand = new Command(\"verify\")\n .description(\"์™„๊ฒฐ์„ฑ ๊ฒ€์ฆ โ€” ์›๊ฒฉ DB ํ–‰ยทํŽ˜์ด์ง€๊ฐ€ ๋ณผํŠธ์— ๋น ์ง์—†์ด ์žˆ๋Š”์ง€ ๋Œ€์กฐ (API ํ˜ธ์ถœ)\")\n .option(\"--json\", \"๊ฒฐ๊ณผ๋ฅผ JSON ์œผ๋กœ ์ถœ๋ ฅ\")\n .action(async (opts: { json?: boolean }) => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n try {\n const client = NotionClient.fromConfig(config);\n const vaultFs = await openVaultFs(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n const report = await orchestrator.verifyCompleteness();\n\n if (opts.json) {\n console.log(JSON.stringify(report, null, 2));\n } else {\n printReport(report);\n }\n\n // ๋ฏธ๋ฐœ๊ฒฌยท์ž”์žฌยท์กฐํšŒ ์‹คํŒจ๊ฐ€ ์žˆ์œผ๋ฉด ์‹คํŒจ ์ข…๋ฃŒํ•œ๋‹ค โ€” CIยทE2E ํ•˜๋‹ˆ์Šค๊ฐ€ ์ด ์ฝ”๋“œ๋กœ ํŒ์ •ํ•œ๋‹ค.\n if (!report.complete) process.exitCode = 1;\n } finally {\n stateDb.close();\n }\n });\n","import { Command } from \"commander\";\nimport { ConfigManager, StateDB, NotionClient, SyncOrchestrator } from \"@im-nobsidian/core\";\nimport chalk from \"chalk\";\nimport { icons, failedItem } from \"../utils/format.js\";\nimport { obsidianConfigDir, openVaultFs } from \"../utils/vault.js\";\n\n// ๋ณผํŠธ์—์„œ ๋งŒ๋“  ๋…ธํŠธ ํด๋”๋ฅผ Notion DB ๋กœ ์˜ฌ๋ฆด ์ค€๋น„(#75) โ€” frontmatter ยท `.base` ๋กœ ์‚ฌ์ด๋“œ์นด๋ฅผ ์จ ๋‘”๋‹ค.\n// Notion ์—๋Š” ์“ฐ์ง€ ์•Š๋Š”๋‹ค. ์‚ฌ์ด๋“œ์นด๋Š” JSON ์ด๋ผ push ์ „์— ๊ณ ์น  ์ˆ˜ ์žˆ๋‹ค โ€” ๋ฌด์—‡์„ ์™œ ๊ทธ๋ ‡๊ฒŒ ์ •ํ–ˆ๋Š”์ง€ ๋ณด์ธ๋‹ค.\nexport const prepareDbCommand = new Command(\"prepare-db\")\n .description(\"๋ณผํŠธ์˜ ๋…ธํŠธ ํด๋”๋ฅผ ๋‹ค์Œ push ์—์„œ Notion DB ๋กœ ๋งŒ๋“ค๋„๋ก ์ค€๋น„ (Notion ์€ ๊ทธ๋Œ€๋กœ)\")\n .argument(\"<folder>\", \"DB ๋กœ ๋งŒ๋“ค ํด๋” โ€” ๋ณผํŠธ ๊ธฐ์ค€ ๊ฒฝ๋กœ. ๋ฐ”๋กœ ์•„๋ž˜ ๋…ธํŠธ๊ฐ€ ํ–‰์ด ๋œ๋‹ค\")\n .action(async (folder: string) => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n try {\n const client = NotionClient.fromConfig(config);\n const vaultFs = await openVaultFs(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n const report = await orchestrator.prepareFolderDatabase(folder, obsidianConfigDir(cwd));\n\n console.log(` ${icons.success} ${report.sidecarPath} โ€” ํ–‰ ${report.rows}๊ฐœ`);\n for (const property of report.properties) {\n console.log(` ${property.name}: ${property.type} ${chalk.dim(`(${property.reason})`)}`);\n }\n if (report.views.length > 0) console.log(` ๋ณด๊ธฐ: ${report.views.join(\", \")}`);\n for (const skip of report.skipped) {\n console.log(chalk.dim(` ์˜ฎ๊ธฐ์ง€ ์•Š์Œ โ€” ${skip.what}: ${skip.reason}`));\n }\n console.log(chalk.dim(\" ํƒ€์ž…์„ ๋ฐ”๊พธ๋ ค๋ฉด ์‚ฌ์ด๋“œ์นด๋ฅผ ๊ณ ์นœ ๋’ค push ํ•˜์„ธ์š”.\"));\n } catch (error) {\n failedItem(folder, error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n } finally {\n stateDb.close();\n }\n });\n","/**\n * CLI ์ข…๋ฃŒ ์ฒ˜๋ฆฌ โ€” ๋‚จ์€ ํ•ธ๋“ค ๋•Œ๋ฌธ์— ๊ฐ•์ œ ์ข…๋ฃŒํ•˜๋˜, **๋ช…๋ น์ด ์ •ํ•œ ์ข…๋ฃŒ ์ฝ”๋“œ๋Š” ๋ณด์กดํ•œ๋‹ค**.\n *\n * better-sqlite3 ํ•ธ๋“ค๊ณผ rate limiter ํƒ€์ด๋จธ๊ฐ€ ์ด๋ฒคํŠธ ๋ฃจํ”„๋ฅผ ๋ถ™์žก๊ณ  ์žˆ์–ด ๋ช…๋ น์ด ๋๋‚˜๋„\n * ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ž์—ฐ ์ข…๋ฃŒ๋˜์ง€ ์•Š๋Š”๋‹ค. ๊ทธ๋ž˜์„œ ์งง์€ ์ง€์—ฐ ๋’ค ๊ฐ•์ œ ์ข…๋ฃŒํ•˜๋Š”๋ฐ, ์—ฌ๊ธฐ์„œ ์ฝ”๋“œ๋ฅผ\n * 0 ์œผ๋กœ ๋ชป๋ฐ•์œผ๋ฉด ๊ฐ ๋ช…๋ น์ด ์‹คํŒจ๋งˆ๋‹ค ์„ธ์›Œ ๋‘” `process.exitCode = 1`\n * (initยทpullยทpushยทsyncยทresolveยทverify)์ด **์ „๋ถ€ ์ง€์›Œ์ง„๋‹ค** โ€” ์‹คํŒจํ•œ ์‹คํ–‰๋„ ์…ธ๊ณผ CI ์—๋Š”\n * ์„ฑ๊ณต์œผ๋กœ ๋ณด์ด๊ณ , ์ข…๋ฃŒ ์ฝ”๋“œ๋กœ ํŒ์ •ํ•˜๋Š” ์Šคํฌ๋ฆฝํŠธ๋Š” ์•„๋ฌด๊ฒƒ๋„ ๋ชป ์žก๋Š”๋‹ค(R11-C).\n *\n * ์ข…๋ฃŒ ์ฝ”๋“œ ๊ฒฐ์ •์„ ์ด ํ•จ์ˆ˜ ํ•˜๋‚˜์— ๋‘์–ด, \"๊ฐ•์ œ ์ข…๋ฃŒํ•œ๋‹ค\"์™€ \"์„ฑ๊ณต์œผ๋กœ ์ข…๋ฃŒํ•œ๋‹ค\"๊ฐ€ ๋‹ค์‹œ\n * ํ•œ ์ค„์— ์„ž์ด์ง€ ์•Š๊ฒŒ ํ•œ๋‹ค.\n */\n\n/** ๊ฐ•์ œ ์ข…๋ฃŒ๊นŒ์ง€์˜ ์ง€์—ฐ โ€” ๋งˆ์ง€๋ง‰ stdout flush ์—ฌ์œ . */\nexport const FORCED_EXIT_DELAY_MS = 100;\n\n/**\n * {@link scheduleForcedExit} ๊ฐ€ ์“ฐ๋Š” ํ”„๋กœ์„ธ์Šค ํ‘œ๋ฉด(ํ…Œ์ŠคํŠธ ๋Œ€์—ญ ์ฃผ์ž…์šฉ).\n *\n * `exitCode` ๋Š” Node ์˜ `process.exitCode` ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐ›๋Š”๋‹ค โ€” ๋ช…์‹œ์ ์œผ๋กœ ์ง€์šด ๊ฒฝ์šฐ\n * `null` ์ด ๋  ์ˆ˜ ์žˆ์–ด ๊ทธ ์œ ๋‹ˆ์˜จ๊นŒ์ง€ ํฌํ•จํ•ด์•ผ ์‹ค์ œ `process` ๋ฅผ ๋„˜๊ธธ ์ˆ˜ ์žˆ๋‹ค.\n */\nexport interface ExitableProcess {\n exitCode?: number | string | null | undefined;\n exit(code?: number): void;\n}\n\n/** `process.exitCode` ๋ฅผ ์ข…๋ฃŒ ์ฝ”๋“œ๋กœ ์ •๊ทœํ™”ํ•œ๋‹ค. ์ˆซ์ž๊ฐ€ ์•„๋‹ˆ๋ฉด ์„ฑ๊ณต(0). */\nexport function resolveExitCode(exitCode: number | string | null | undefined): number {\n if (typeof exitCode === \"number\") return exitCode;\n if (typeof exitCode === \"string\") {\n const parsed = Number(exitCode);\n return Number.isFinite(parsed) ? parsed : 0;\n }\n return 0;\n}\n\n/** ์ง€์—ฐ ํ›„ ๊ฐ•์ œ ์ข…๋ฃŒ๋ฅผ ์˜ˆ์•ฝํ•œ๋‹ค โ€” ์ข…๋ฃŒ ์ฝ”๋“œ๋Š” ๋ช…๋ น์ด ์„ธ์šด ๊ฐ’์„ ๊ทธ๋Œ€๋กœ ์“ด๋‹ค. */\nexport function scheduleForcedExit(\n proc: ExitableProcess,\n schedule: (fn: () => void, ms: number) => unknown = setTimeout,\n): void {\n schedule(() => proc.exit(resolveExitCode(proc.exitCode)), FORCED_EXIT_DELAY_MS);\n}\n"],"mappings":";;;AAAA,SAAS,qBAAqB;AAC9B,SAAS,WAAAA,iBAAe;;;ACDxB,SAAS,eAAe;AACxB;AAAA,EACE,iBAAAC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,OAAO,eAAe;AAC/B,OAAO,SAAS;AAChB,OAAOC,YAAW;;;ACTlB,OAAO,WAAW;AAGX,IAAM,QAAQ;AAAA,EACnB,QAAQ,MAAM,MAAM,GAAG;AAAA,EACvB,QAAQ,MAAM,OAAO,GAAG;AAAA,EACxB,QAAQ,MAAM,IAAI,GAAG;AAAA,EACrB,MAAM,MAAM,KAAK,QAAG;AAAA,EACpB,SAAS,MAAM,MAAM,QAAG;AAAA,EACxB,MAAM,MAAM,IAAI,QAAG;AAAA,EACnB,UAAU,MAAM,QAAQ,GAAG;AAAA,EAC3B,QAAQ,MAAM,MAAM,QAAG;AAAA,EACvB,UAAU,MAAM,OAAO,QAAG;AAAA,EAC1B,SAAS,MAAM,KAAK,QAAG;AAAA,EACvB,aAAa,MAAM,QAAQ,QAAG;AAChC;AAEO,SAAS,OAAO,MAAsB;AAC3C,SAAO,MAAM,KAAK,IAAI;AACxB;AAEO,SAAS,UAAU,QAAQ,IAAY;AAC5C,SAAO,MAAM,IAAI,SAAI,OAAO,KAAK,CAAC;AACpC;AAEO,SAAS,cAAc,IAAuC;AACnE,SAAO,MAAM,EAAE;AACjB;AAEO,SAAS,eAAe,IAAuC;AACpE,UAAQ,IAAI;AAAA,IACV,KAAK;AACH,aAAO,MAAM,MAAM,SAAS;AAAA,IAC9B,KAAK;AACH,aAAO,MAAM,OAAO,SAAS;AAAA,IAC/B,KAAK;AACH,aAAO,MAAM,IAAI,SAAS;AAAA,IAC5B,KAAK;AACH,aAAO,MAAM,KAAK,OAAO;AAAA,EAC7B;AACF;AAGO,SAAS,QAAQ,SAAiB,SAAiB,SAAiB,QAAQ,GAAW;AAC5F,QAAM,QAAkB,CAAC;AACzB,MAAI,UAAU,EAAG,OAAM,KAAK,MAAM,MAAM,GAAG,OAAO,UAAU,CAAC;AAC7D,MAAI,UAAU,EAAG,OAAM,KAAK,MAAM,OAAO,GAAG,OAAO,UAAU,CAAC;AAC9D,MAAI,QAAQ,EAAG,OAAM,KAAK,MAAM,KAAK,GAAG,KAAK,QAAQ,CAAC;AACtD,MAAI,UAAU,EAAG,OAAM,KAAK,MAAM,IAAI,GAAG,OAAO,UAAU,CAAC;AAC3D,MAAI,MAAM,WAAW,EAAG,OAAM,KAAK,MAAM,IAAI,YAAY,CAAC;AAC1D,SAAO,MAAM,KAAK,IAAI;AACxB;AASO,SAAS,iBAAiB,OAAe,aAA6B;AAC3E,SAAO,cAAc,IACjB,OAAO,MAAM,OAAO,GAAG,KAAK,uBAAuB,CAAC,IACpD,OAAO,MAAM,MAAM,GAAG,KAAK,WAAW,CAAC;AAC7C;AAGO,SAAS,gBAAgB,OAAuB;AACrD,SAAO,QAAQ,IAAI,MAAM,IAAI,GAAG,KAAK,SAAS,IAAI,MAAM,IAAI,UAAU;AACxE;AAEO,SAAS,SAAS,IAAoB;AAC3C,SAAO,MAAM,IAAI,YAAY,KAAK,KAAM,QAAQ,CAAC,CAAC,GAAG;AACvD;AAGO,SAAS,cAAc,KAAqB;AACjD,QAAM,OAAO,IAAI,KAAK,GAAG;AACzB,MAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,EAAG,QAAO;AACzC,QAAM,MAAM,CAAC,MAAsB,OAAO,CAAC,EAAE,SAAS,GAAG,GAAG;AAC5D,SACE,GAAG,KAAK,YAAY,CAAC,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,IAAI,KAAK,QAAQ,CAAC,CAAC,IACrE,IAAI,KAAK,SAAS,CAAC,CAAC,IAAI,IAAI,KAAK,WAAW,CAAC,CAAC,IAAI,IAAI,KAAK,WAAW,CAAC,CAAC;AAE/E;AAGA,IAAM,mBAAmD;AAAA,EACvD,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,iBAAiB;AAAA,EACjB,cAAc;AAAA,EACd,KAAK;AACP;AAOO,SAAS,gBAAgB,MAAgC;AAC9D,MAAI,KAAK,SAAS,QAAQ;AACxB,WAAO,CAAC,QAAQ,KAAK,SAAS,cAAc,iBAAiB,KAAK,MAAM,CAAC,MAAM,WAAW,CAAC;AAAA,EAC7F;AACA,QAAM,UAAU,KAAK,oBAAoB;AACzC,QAAM,cACJ,UAAU,IACN,SAAM,OAAO,cAAc,YAAY,IAAI,aAAa,WAAW,aACnE;AACN,QAAM,QAAQ,CAAC,QAAQ,oBAAoB,WAAW,EAAE,CAAC;AACzD,MAAI,KAAK,kBAAmB,OAAM,KAAK,mBAAmB,IAAI,CAAC;AAC/D,SAAO;AACT;AAGO,SAAS,mBAAmB,MAA8B;AAC/D,QAAM,OAAO,KAAK,aACd,uBAAuB,cAAc,KAAK,UAAU,CAAC,MACrD;AACJ,SAAO,GAAG,QAAQ,gCAAgC,IAAI,gBAAW,CAAC,IAAI,MAAM,KAAK,oBAAoB,CAAC;AACxG;AAGO,SAAS,iBAAiB,gBAAuC;AACtE,SAAO,iBAAiB,cAAc,cAAc,IAAI,MAAM,OAAO,OAAO;AAC9E;AAEO,SAAS,SAAS,SAAiB,OAAuB;AAC/D,SAAO,MAAM,IAAI,IAAI,OAAO,IAAI,KAAK,GAAG;AAC1C;AAEO,SAAS,QAAQ,MAAsB;AAC5C,SAAO,MAAM,IAAI,IAAI;AACvB;AAMO,SAAS,WAAW,MAAc,OAAe,WAA0B;AAChF,QAAM,QAAQ,GAAG,YAAY,GAAG,SAAS,MAAM,EAAE,GAAG,QAAQ,GAAG;AAC/D,UAAQ,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,CAAC,EAAE;AAC7D;;;AC9IA,SAAS,kBAAkB;AAC3B,SAAS,SAAS,MAAM,gBAAgB;AACxC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAGP,eAAsB,kBAAkB,KAAgC;AACtE,UAAQ,MAAM,sBAAsB,GAAG,GAAG,OAAO,CAAC,WAAW,WAAW,EAAE;AAC5E;AAMA,eAAsB,YAAY,KAAa,OAA2C;AACxF,QAAM,OAAO,IAAI,YAAY,KAAK,KAAK;AACvC,QAAM,SAAS,MAAM,kBAAkB,GAAG;AAC1C,SAAO,OAAO,WAAW,IAAI,OAAO,IAAI,cAAc,MAAM,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC;AACjF;AAMO,SAAS,kBAAkB,KAAqB;AACrD,WAAS,MAAM,OAAO,MAAM,QAAQ,GAAG,GAAG;AACxC,QAAI,WAAW,KAAK,KAAK,mBAAmB,CAAC,GAAG;AAC9C,aAAO,SAAS,KAAK,KAAK,KAAK,mBAAmB,CAAC,EAAE,MAAM,IAAI,EAAE,KAAK,GAAG;AAAA,IAC3E;AACA,QAAI,QAAQ,GAAG,MAAM,IAAK,QAAO;AAAA,EACnC;AACF;AAOO,SAAS,wBAAwB,KAAuD;AAC7F,WAAS,MAAM,QAAQ,GAAG,KAAK,MAAM,QAAQ,GAAG,GAAG;AACjD,UAAM,UAAU,IAAI,cAAc,GAAG;AACrC,QAAI,WAAW,QAAQ,MAAM,GAAG;AAC9B,YAAM,QAAQ,SAAS,KAAK,GAAG,EAAE,MAAM,IAAI,EAAE,KAAK,GAAG;AACrD,YAAM,KAAK,QAAQ,KAAK,QAAQ,MAAM;AACtC,UAAI;AAEF,cAAM,QAAQ,GACX,OAAO,EACP;AAAA,UACC,CAAC,MAAM,EAAE,iBAAiB,SAAS,iBAAiB,EAAE,cAAc,KAAK;AAAA,QAC3E,EAAE;AACJ,eAAO,QAAQ,IAAI,EAAE,QAAQ,KAAK,MAAM,IAAI;AAAA,MAC9C,UAAE;AACA,WAAG,MAAM;AAAA,MACX;AAAA,IACF;AACA,QAAI,QAAQ,GAAG,MAAM,IAAK,QAAO;AAAA,EACnC;AACF;;;AFrDA,IAAM,uBACJ;AAEK,IAAM,cAAc,IAAI,QAAQ,MAAM,EAC1C,YAAY,wCAAoB,EAChC,OAAO,mBAAmB,0BAA0B,EACpD,OAAO,uBAAuB,iFAAqB,EACnD,OAAO,qBAAqB,uCAAS,EACrC,OAAO,OAAO,YAA+E;AAC5F,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,gBAAgB,IAAIC,eAAc,GAAG;AAE3C,UAAQ,IAAI;AAAA,EAAK,OAAOC,OAAM,KAAK,sBAAsB,CAAC,CAAC,EAAE;AAC7D,UAAQ,IAAI,KAAK,UAAU,CAAC,EAAE;AAG9B,QAAM,QAAQ,wBAAwB,GAAG;AACzC,MAAI,OAAO;AACT,YAAQ;AAAA,MACN,0CAAY,MAAM,MAAM,2CAAa,MAAM,KAAK;AAAA,IAClD;AACA,YAAQ,WAAW;AACnB;AAAA,EACF;AAEA,MAAI,QAAQ,gBAAgB;AAC1B,QAAI,CAAC,QAAQ,SAAS,CAAC,QAAQ,YAAY;AACzC,cAAQ,MAAM,4HAA4C;AAC1D,cAAQ,WAAW;AACnB;AAAA,IACF;AAEA,QAAI,CAAC,QAAQ,MAAM,WAAW,mBAAmB,GAAG;AAClD,cAAQ,MAAM,sBAAO,mBAAmB,2DAAc;AACtD,cAAQ,WAAW;AACnB;AAAA,IACF;AAEA,UAAM,aAAa,kBAAkB,QAAQ,UAAU;AACvD,QAAI,eAAe,MAAM;AACvB,cAAQ,MAAM,oBAAoB;AAClC,cAAQ,WAAW;AACnB;AAAA,IACF;AAEA,UAAMC,WAAU,IAAI,4CAAmB,EAAE,MAAM;AAC/C,QAAI;AACF,YAAM,SAAS,IAAI,aAAa,EAAE,OAAO,QAAQ,MAAM,CAAC;AACxD,YAAM,OAAO,OAAO,EAAE,UAAU,GAAG,QAAQ,EAAE,UAAU,UAAU,OAAO,OAAO,EAAE,CAAC;AAClF,MAAAA,SAAQ,KAAK;AACb,cAAQ,IAAI,KAAK,MAAM,OAAO,kBAAkB;AAEhD,YAAM,cAAc,KAAK,EAAE,OAAO,QAAQ,OAAO,WAAW,CAAC;AAC7D,cAAQ,IAAI;AAAA,IAAO,MAAM,OAAO,oBAAoB,QAAQ,cAAc,UAAU,CAAC,EAAE;AACvF,cAAQ;AAAA,QACN;AAAA,IAAO,OAAOD,OAAM,MAAM,QAAQ,CAAC,CAAC,QAAQA,OAAM,KAAK,YAAY,CAAC;AAAA,MACtE;AAAA,IACF,SAAS,OAAO;AACd,MAAAC,SAAQ,KAAK,kCAAc;AAC3B,cAAQ,MAAM,iBAAiB,QAAQ,MAAM,UAAU,KAAK;AAC5D,cAAQ,WAAW;AAAA,IACrB;AACA;AAAA,EACF;AAEA,QAAM,SAAS,MAAM,cAAc,cAAc;AACjD,MAAI,QAAQ;AACV,UAAM,YAAY,MAAM,QAAQ;AAAA,MAC9B,SAAS;AAAA,MACT,SAAS;AAAA,IACX,CAAC;AACD,QAAI,CAAC,UAAW;AAAA,EAClB;AAEA,QAAM,QAAQ,MAAM,MAAM;AAAA,IACxB,SAAS;AAAA,IACT,UAAU,CAAC,MACT,EAAE,WAAW,mBAAmB,KAChC,GAAG,mBAAmB;AAAA,EAC1B,CAAC;AAED,QAAM,UAAU,IAAI,4CAAmB,EAAE,MAAM;AAE/C,MAAI;AACF,UAAM,SAAS,IAAI,aAAa,EAAE,MAAM,CAAC;AACzC,UAAM,SAAS,MAAM,OAAO,OAAO;AAAA,MACjC,UAAU;AAAA,MACV,QAAQ,EAAE,UAAU,UAAU,OAAO,OAAO;AAAA,IAC9C,CAAC;AACD,YAAQ,KAAK;AACb,YAAQ,IAAI,KAAK,MAAM,OAAO,kBAAkB;AAEhD,QAAI,OAAO,QAAQ,WAAW,GAAG;AAC/B,cAAQ;AAAA,QACN;AAAA,IAAOD,OAAM,OAAO,QAAG,CAAC;AAAA,MAC1B;AACA;AAAA,IACF;AAEA,YAAQ,IAAI;AAAA,oBAAuB;AACnC,eAAW,CAAC,GAAG,IAAI,KAAK,OAAO,QAAQ,QAAQ,GAAG;AAChD,YAAM,QAAQ,iBAAiB,IAAI;AACnC,cAAQ,IAAI,OAAO,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,EAAE;AAAA,IACpD;AAEA,UAAM,gBAAgB,MAAM,MAAM;AAAA,MAChC,SAAS;AAAA,MACT,SAAS,OAAO,QAAQ,CAAC,GAAG;AAAA,MAC5B,UAAU,CAAC,UAAU,kBAAkB,KAAK,MAAM,QAAQ;AAAA,IAC5D,CAAC;AACD,UAAM,aAAa,kBAAkB,aAAa;AAElD,UAAM,cAAc,KAAK,EAAE,OAAO,WAAW,CAAC;AAC9C,YAAQ,IAAI;AAAA,IAAO,MAAM,OAAO,oBAAoB,QAAQ,cAAc,UAAU,CAAC,EAAE;AACvF,YAAQ;AAAA,MACN;AAAA,IAAO,OAAOA,OAAM,MAAM,QAAQ,CAAC,CAAC,QAAQA,OAAM,KAAK,YAAY,CAAC;AAAA,IACtE;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,KAAK,kCAAc;AAC3B,YAAQ,MAAM,iBAAiB,QAAQ,MAAM,UAAU,KAAK;AAC5D,YAAQ,WAAW;AAAA,EACrB;AACF,CAAC;AAEH,SAAS,iBAAiB,MAAuC;AAC/D,QAAM,aAAa,KAAK,YAAY;AACpC,MAAI,CAAC,WAAY,QAAO;AAExB,aAAW,QAAQ,OAAO,OAAO,UAAU,GAAG;AAC5C,UAAM,IAAI;AACV,QAAI,EAAE,SAAS,WAAW,EAAE,QAAQ,CAAC,GAAG,YAAY;AAClD,aAAO,EAAE,MAAM,CAAC,EAAE;AAAA,IACpB;AAAA,EACF;AACA,SAAO;AACT;;;AGrJA,SAAS,WAAAE,gBAAe;AACxB,SAAS,iBAAAC,gBAAe,WAAAC,UAAS,gBAAAC,eAAc,wBAAwB;;;ACDvE,SAAS,iBAAiB;AAWnB,SAAS,iBAAuB;AACrC,QAAM,WAAW,CAAC,YAAoB,SAA0B,QAAQ,MAAM,SAAS,GAAG,IAAI;AAC9F,YAAU,EAAE,MAAM,UAAU,OAAO,UAAU,MAAM,UAAU,OAAO,MAAM;AAAA,EAAC,EAAE,CAAC;AAChF;AAkCO,SAAS,SAAS,QAA8B;AACrD,SAAO;AAAA,IACL,SAAS,OAAO;AAAA,IAChB,SAAS,OAAO;AAAA,IAChB,SAAS,OAAO;AAAA,IAChB,OAAO,OAAO;AAAA,IACd,OACE,OAAO,UACP,OAAO,UACP,OAAO,UACP,OAAO,SACN,OAAO,oBAAoB;AAAA,IAC9B,kBAAkB,OAAO,oBAAoB;AAAA,IAC7C,QAAQ,OAAO;AAAA,IACf,YAAY,OAAO;AAAA,EACrB;AACF;AAEO,SAAS,SAAS,QAA8B;AACrD,SAAO;AAAA,IACL,SAAS,OAAO;AAAA,IAChB,SAAS,OAAO;AAAA,IAChB,SAAS,OAAO;AAAA,IAChB,UAAU,OAAO;AAAA,IACjB,OAAO,OAAO,UAAU,OAAO,UAAU,OAAO,UAAU,OAAO;AAAA,IACjE,WAAW,OAAO,UAAU;AAAA,IAC5B,QAAQ,OAAO;AAAA,IACf,cAAc,OAAO,aAAa;AAAA,IAClC,QAAQ,OAAO;AAAA,IACf,OAAO,OAAO;AAAA,IACd,OAAO,OAAO;AAAA,IACd,YAAY,OAAO;AAAA,IACnB,YAAY,OAAO,cAAc;AAAA,EACnC;AACF;AAEO,SAAS,UAAU,OAAsB;AAC9C,UAAQ,OAAO,MAAM,GAAG,KAAK,UAAU,KAAK,CAAC;AAAA,CAAI;AACnD;;;ADrEO,IAAM,cAAc,IAAIC,SAAQ,MAAM,EAC1C,YAAY,uEAAqB,EACjC,OAAO,aAAa,oFAAmB,EACvC,OAAO,yBAAyB,sCAAa,EAC7C,OAAO,UAAU,sHAAsC,EACvD,OAAO,OAAO,YAAY;AACzB,QAAM,OAAO,QAAQ,SAAS;AAC9B,MAAI,KAAM,gBAAe;AACzB,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,gBAAgB,IAAIC,eAAc,GAAG;AAC3C,QAAM,SAAS,MAAM,cAAc,KAAK;AACxC,QAAM,UAAUC,SAAQ,KAAK,cAAc,MAAM;AAEjD,MAAI;AACF,UAAM,SAASC,cAAa,WAAW,MAAM;AAC7C,UAAM,UAAU,MAAM,YAAY,KAAK,OAAO,KAAK;AACnD,UAAM,eAAe,IAAI,iBAAiB,QAAQ,SAAS,QAAQ,OAAO;AAE1E,QAAI,CAAC,MAAM;AACT,cAAQ,IAAI;AAAA,EAAK,OAAO,wBAAwB,CAAC,EAAE;AACnD,UAAI,QAAQ,OAAQ,SAAQ,IAAI,QAAQ,kBAAkB,CAAC;AAC3D,cAAQ,IAAI,EAAE;AAAA,IAChB;AAEA,UAAM,SAAS,MAAM,aAAa,KAAK;AAAA,MACrC,QAAQ,QAAQ;AAAA,MAChB,OAAO,QAAQ;AAAA,MACf,YAAY,OACR,SACA,CAAC,SAAS,OAAO,SAAS;AACxB,cAAM,OAAO,cAAc,KAAK,SAAS;AACzC,cAAM,QAAQ,eAAe,KAAK,SAAS;AAC3C,cAAM,OAAO,SAAS,SAAS,KAAK;AACpC,gBAAQ,IAAI,KAAK,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,IAAI,KAAK,EAAE;AAAA,MACvD;AAAA,IACN,CAAC;AAED,QAAI,MAAM;AACR,gBAAU,SAAS,MAAM,CAAC;AAC1B,UAAI,OAAO,OAAO,SAAS,EAAG,SAAQ,WAAW;AACjD;AAAA,IACF;AAEA,UAAM,cAAc,OAAO,OAAO;AAClC,YAAQ,IAAI;AAAA,IAAO,iBAAiB,QAAQ,WAAW,CAAC,EAAE;AAC1D,YAAQ;AAAA,MACN,KAAK,QAAQ,OAAO,SAAS,OAAO,SAAS,OAAO,SAAS,OAAO,KAAK,CAAC,KAAK,gBAAgB,WAAW,CAAC;AAAA,IAC7G;AACA,QAAI,OAAO,kBAAkB;AAC3B,cAAQ,IAAI,KAAK,QAAQ,SAAS,oBAAU,iBAAO,IAAI,OAAO,gBAAgB,EAAE;AAAA,IAClF;AACA,YAAQ,IAAI,KAAK,SAAS,OAAO,QAAQ,CAAC,EAAE;AAE5C,QAAI,cAAc,GAAG;AACnB,cAAQ,IAAI,EAAE;AACd,iBAAW,KAAK,OAAO,QAAQ;AAC7B,mBAAW,EAAE,MAAM,EAAE,KAAK;AAAA,MAC5B;AAEA,cAAQ,WAAW;AAAA,IACrB;AAAA,EACF,UAAE;AACA,YAAQ,MAAM;AAAA,EAChB;AACF,CAAC;;;AEjFH,SAAS,WAAAC,UAAS,cAAc;AAChC,SAAS,iBAAAC,gBAAe,WAAAC,UAAS,gBAAAC,eAAc,oBAAAC,yBAAwB;AACvE,OAAOC,YAAW;AAkBX,IAAM,cAAc,IAAIC,SAAQ,MAAM,EAC1C,YAAY,uEAAqB,EACjC,OAAO,aAAa,oFAAmB,EACvC,OAAO,yBAAyB,sCAAa,EAC7C;AAAA,EACC;AAAA,EACA;AACF,EAEC;AAAA,EACC,IAAI;AAAA,IACF;AAAA,IACA;AAAA,EACF,EAAE,UAAU,QAAQ;AACtB,EACC,OAAO,UAAU,sHAAsC,EACvD,OAAO,OAAO,YAAY;AACzB,QAAM,OAAO,QAAQ,SAAS;AAC9B,MAAI,KAAM,gBAAe;AACzB,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,gBAAgB,IAAIC,eAAc,GAAG;AAC3C,QAAM,SAAS,MAAM,cAAc,KAAK;AACxC,QAAM,UAAUC,SAAQ,KAAK,cAAc,MAAM;AAEjD,MAAI;AACF,UAAM,SAASC,cAAa,WAAW,MAAM;AAC7C,UAAM,UAAU,MAAM,YAAY,KAAK,OAAO,KAAK;AACnD,UAAM,eAAe,IAAIC,kBAAiB,QAAQ,SAAS,QAAQ,OAAO;AAE1E,QAAI,CAAC,MAAM;AACT,cAAQ,IAAI;AAAA,EAAK,OAAO,0BAA0B,CAAC,EAAE;AACrD,UAAI,QAAQ,OAAQ,SAAQ,IAAI,QAAQ,kBAAkB,CAAC;AAC3D,cAAQ,IAAI,EAAE;AAAA,IAChB;AAEA,UAAM,SAAS,MAAM,aAAa,KAAK;AAAA,MACrC,QAAQ,QAAQ;AAAA,MAChB,OAAO,QAAQ;AAAA,MACf,OAAO,QAAQ;AAAA,MACf,UAAU,QAAQ;AAAA,MAClB,YAAY,OACR,SACA,CAAC,SAAS,OAAO,SAAS;AAExB,YAAI,KAAK,UAAW;AACpB,cAAM,OAAO,cAAc,KAAK,SAAS;AACzC,cAAM,QAAQ,eAAe,KAAK,SAAS;AAC3C,cAAM,OAAO,SAAS,SAAS,KAAK;AACpC,gBAAQ,IAAI,KAAK,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,IAAI,KAAK,EAAE;AAAA,MACvD;AAAA,IACN,CAAC;AAED,QAAI,MAAM;AACR,gBAAU,SAAS,MAAM,CAAC;AAC1B,UAAI,OAAO,OAAO,SAAS,EAAG,SAAQ,WAAW;AACjD;AAAA,IACF;AAEA,QAAI,OAAO,aAAa,GAAG;AACzB,cAAQ;AAAA,QACN;AAAA,IAAO,MAAM,OAAO,IAAI,OAAO,UAAU,kBAAa,QAAQ,GAAG,OAAO,MAAM,WAAW,GAAG,CAAC;AAAA,MAC/F;AAAA,IACF;AACA,QAAI,OAAO,YAAY,GAAG;AACxB,cAAQ;AAAA,QACN,KAAK,MAAM,OAAO,IAAI,OAAO,SAAS,iBAAY,QAAQ,GAAG,OAAO,MAAM,WAAW,GAAG,CAAC;AAAA,MAC3F;AAAA,IACF;AACA,QAAI,OAAO,YAAY,GAAG;AACxB,cAAQ,IAAI,KAAK,MAAM,OAAO,IAAI,OAAO,SAAS,iBAAiB;AAAA,IACrE;AAEA,UAAM,cAAc,OAAO,OAAO;AAClC,YAAQ,IAAI;AAAA,IAAO,iBAAiB,QAAQ,WAAW,CAAC,EAAE;AAC1D,YAAQ;AAAA,MACN,KAAK,QAAQ,OAAO,SAAS,OAAO,SAAS,OAAO,OAAO,CAAC,KAAK,gBAAgB,WAAW,CAAC;AAAA,IAC/F;AAEA,QAAI,OAAO,WAAW,GAAG;AACvB,cAAQ,IAAI,KAAK,MAAM,OAAO,IAAIC,OAAM,OAAO,GAAG,OAAO,QAAQ,WAAW,CAAC,EAAE;AAAA,IACjF;AACA,YAAQ,IAAI,KAAK,SAAS,OAAO,QAAQ,CAAC,EAAE;AAC5C,QAAI,OAAO,YAAY;AACrB,iBAAW,QAAQ,gBAAgB,OAAO,UAAU,EAAG,SAAQ,IAAI,KAAK,IAAI,EAAE;AAAA,IAChF;AAEA,QAAI,OAAO,UAAU,SAAS,GAAG;AAC/B,cAAQ;AAAA,QACN;AAAA,IAAO,MAAM,QAAQ,IAAIA,OAAM,QAAQ,GAAG,OAAO,UAAU,MAAM,YAAY,CAAC,eAAUA,OAAM,KAAK,eAAe,CAAC;AAAA,MACrH;AAAA,IACF;AAEA,QAAI,cAAc,GAAG;AACnB,cAAQ,IAAI,EAAE;AACd,iBAAW,KAAK,OAAO,QAAQ;AAC7B,mBAAW,EAAE,MAAM,EAAE,KAAK;AAAA,MAC5B;AAEA,cAAQ,WAAW;AAAA,IACrB;AAAA,EACF,UAAE;AACA,YAAQ,MAAM;AAAA,EAChB;AACF,CAAC;;;AC3HH,SAAS,WAAAC,gBAAe;AACxB,SAAS,iBAAAC,gBAAe,WAAAC,UAAS,gBAAAC,eAAc,oBAAAC,yBAAwB;AACvE,OAAOC,YAAW;AAiBX,IAAM,cAAc,IAAIC,SAAQ,MAAM,EAC1C,YAAY,0DAAuB,EACnC,OAAO,aAAa,oFAAmB,EACvC,OAAO,UAAU,sHAAsC,EACvD,OAAO,OAAO,YAAY;AACzB,QAAM,OAAO,QAAQ,SAAS;AAC9B,MAAI,KAAM,gBAAe;AAEzB,QAAM,MAAM,OAAO,MAAM;AAAA,EAAC,IAAI,CAAC,SAAiB,QAAQ,IAAI,IAAI;AAChE,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,gBAAgB,IAAIC,eAAc,GAAG;AAC3C,QAAM,SAAS,MAAM,cAAc,KAAK;AACxC,QAAM,UAAUC,SAAQ,KAAK,cAAc,MAAM;AAEjD,MAAI;AACF,UAAM,SAASC,cAAa,WAAW,MAAM;AAC7C,UAAM,UAAU,MAAM,YAAY,KAAK,OAAO,KAAK;AACnD,UAAM,eAAe,IAAIC,kBAAiB,QAAQ,SAAS,QAAQ,OAAO;AAE1E,QAAI;AAAA,EAAK,OAAO,sBAAsB,CAAC,EAAE;AACzC,QAAI,KAAK,UAAU,CAAC,EAAE;AACtB,QAAI,QAAQ,OAAQ,KAAI,QAAQ,kBAAkB,CAAC;AAInD,QAAI;AAAA,IAAO,OAAOC,OAAM,KAAK,aAAQ,CAAC,CAAC,IAAI,QAAQ,0BAAqB,CAAC,EAAE;AAC3E,QAAI,YAAY;AAChB,UAAM,WAAW,MAAY;AAC3B,UAAI,UAAW;AACf,kBAAY;AACZ,UAAI;AAAA,IAAO,OAAOA,OAAM,QAAQ,aAAQ,CAAC,CAAC,IAAI,QAAQ,0BAAqB,CAAC,EAAE;AAAA,IAChF;AAEA,UAAM,SAAS,MAAM,aAAa,KAAK;AAAA,MACrC,QAAQ,QAAQ;AAAA,MAChB,YAAY,CAAC,UAAU,QAAQ,SAAS;AACtC,YAAI,KAAK,cAAc,OAAQ,UAAS;AACxC,YAAI,KAAK,UAAW;AACpB,cAAM,OAAO,cAAc,KAAK,SAAS;AACzC,cAAM,QAAQ,eAAe,KAAK,SAAS;AAC3C,YAAI,OAAO,IAAI,IAAI,KAAK,IAAI,IAAI,KAAK,EAAE;AAAA,MACzC;AAAA,IACF,CAAC;AACD,aAAS;AACT,UAAM,EAAE,MAAM,YAAY,MAAM,WAAW,IAAI;AAE/C,QAAI,MAAM;AACR,YAAM,OAAO,SAAS,UAAU;AAChC,YAAM,OAAO,SAAS,UAAU;AAChC,gBAAU;AAAA,QACR;AAAA,QACA;AAAA,QACA,OAAO,KAAK,QAAQ,KAAK;AAAA,QACzB,WAAW,KAAK;AAAA,QAChB,YAAY,OAAO;AAAA,MACrB,CAAC;AACD,UAAI,WAAW,OAAO,SAAS,WAAW,OAAO,SAAS,EAAG,SAAQ,WAAW;AAChF;AAAA,IACF;AAKA,UAAM,aAAa,WAAW,OAAO;AACrC,UAAM,aAAa,WAAW,OAAO;AACrC,YAAQ,IAAI;AAAA,IAAO,iBAAiB,QAAQ,aAAa,UAAU,CAAC,EAAE;AACtE,YAAQ;AAAA,MACN,KAAKA,OAAM,KAAK,OAAO,CAAC,IAAI,QAAQ,WAAW,SAAS,WAAW,SAAS,WAAW,OAAO,CAAC;AAAA,OAE5F,WAAW,WAAW,IAAI,KAAKA,OAAM,OAAO,GAAG,WAAW,QAAQ,WAAW,CAAC,KAAK,OACnF,aAAa,IAAI,KAAKA,OAAM,IAAI,GAAG,UAAU,SAAS,CAAC,KAAK;AAAA,IACjE;AACA,QAAI,WAAW,aAAa,GAAG;AAC7B,cAAQ,IAAI,OAAO,MAAM,OAAO,IAAI,WAAW,UAAU,oBAAoB;AAAA,IAC/E;AACA,QAAI,WAAW,YAAY,GAAG;AAC5B,cAAQ,IAAI,OAAO,MAAM,OAAO,IAAI,WAAW,SAAS,iBAAiB;AAAA,IAC3E;AACA,YAAQ;AAAA,MACN,KAAKA,OAAM,QAAQ,OAAO,CAAC,IAAI,QAAQ,WAAW,SAAS,WAAW,SAAS,WAAW,SAAS,WAAW,KAAK,CAAC,MACjH,aAAa,IAAI,KAAKA,OAAM,IAAI,GAAG,UAAU,SAAS,CAAC,KAAK;AAAA,IACjE;AACA,YAAQ,IAAI,KAAK,SAAS,OAAO,QAAQ,CAAC,EAAE;AAC5C,QAAI,WAAW,YAAY;AACzB,iBAAW,QAAQ,gBAAgB,WAAW,UAAU,EAAG,SAAQ,IAAI,KAAK,IAAI,EAAE;AAAA,IACpF;AAEA,QAAI,OAAO,UAAU,SAAS,GAAG;AAC/B,cAAQ;AAAA,QACN;AAAA,IAAO,MAAM,QAAQ,IAAIA,OAAM,QAAQ,GAAG,OAAO,UAAU,MAAM,YAAY,CAAC,eAAUA,OAAM,KAAK,eAAe,CAAC;AAAA,MACrH;AAAA,IACF;AAEA,QAAI,aAAa,aAAa,GAAG;AAC/B,cAAQ,IAAI,EAAE;AACd,iBAAW,KAAK,WAAW,OAAQ,YAAW,EAAE,MAAM,EAAE,OAAO,QAAG;AAClE,iBAAW,KAAK,WAAW,OAAQ,YAAW,EAAE,MAAM,EAAE,OAAO,QAAG;AAClE,cAAQ,WAAW;AAAA,IACrB;AAAA,EACF,UAAE;AACA,YAAQ,MAAM;AAAA,EAChB;AACF,CAAC;;;ACzHH,SAAS,WAAAC,gBAAe;AACxB;AAAA,EACE,iBAAAC;AAAA,EACA,WAAAC;AAAA,EACA,gBAAAC;AAAA,EACA,oBAAAC;AAAA,EACA;AAAA,OACK;AACP,OAAOC,YAAW;AAYX,IAAM,gBAAgB,IAAIC,SAAQ,QAAQ,EAC9C,YAAY,+FAA8B,EAC1C,OAAO,UAAU,+GAAoC,EACrD,OAAO,OAAO,SAA6B;AAC1C,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,gBAAgB,IAAIC,eAAc,GAAG;AAC3C,QAAM,SAAS,MAAM,cAAc,KAAK;AACxC,QAAM,UAAUC,SAAQ,KAAK,cAAc,MAAM;AAEjD,MAAI;AACF,UAAM,SAASC,cAAa,WAAW,MAAM;AAC7C,UAAM,UAAU,MAAM,YAAY,KAAK,OAAO,KAAK;AACnD,UAAM,eAAe,IAAIC,kBAAiB,QAAQ,SAAS,QAAQ,OAAO;AAE1E,UAAM,SAAS,KAAK,OAAO,MAAM,aAAa,OAAO,IAAI,MAAM,aAAa,YAAY;AAExF,YAAQ,IAAI;AAAA,EAAK,OAAO,eAAe,CAAC,EAAE;AAC1C,YAAQ,IAAI,KAAK,UAAU,EAAE,CAAC,EAAE;AAEhC,UAAM,SAAS,OAAO,OAAO;AAC7B,UAAM,YAAY,OAAO,KAAK,cAAc,SAAS,kBAAkB,OAAO,KAAK;AACnF,YAAQ,IAAI,KAAK,QAAQ,YAAY,CAAC,KAAK,OAAO,MAAM,GAAG,CAAC,CAAC,KAAK;AAClE,YAAQ,IAAI,KAAK,QAAQ,YAAY,CAAC,KAAK,SAAS,EAAE;AACtD,QAAI,OAAO,YAAY;AACrB,cAAQ,IAAI,KAAK,QAAQ,YAAY,CAAC,KAAK,cAAc,OAAO,UAAU,CAAC,EAAE;AAAA,IAC/E,OAAO;AACL,cAAQ,IAAI,KAAK,QAAQ,YAAY,CAAC,KAAKC,OAAM,OAAO,OAAO,CAAC,EAAE;AAAA,IACpE;AAEA,YAAQ,IAAI,KAAK,QAAQ,YAAY,CAAC,KAAK,iBAAiB,OAAO,cAAc,CAAC,EAAE;AAEpF,eAAW,UAAU,MAAM,kBAAkB,GAAG,GAAG;AACjD,cAAQ;AAAA,QACN,KAAK,QAAQ,aAAa,CAAC,IAAI,MAAM,KAAK,QAAQ,uBAAkB,MAAM,mBAAmB,CAAC;AAAA,MAChG;AAAA,IACF;AAGA,UAAM,aAAa,QAAQ,OAAO;AAClC,UAAM,QAAQ,WAAW;AACzB,UAAM,gBAAgB,OAAO,gBAAgB;AAC7C,UAAM,iBAAiB,OAAO,aAAa,OAAO,CAAC,MAAM,EAAE,SAAS,SAAS;AAC7E,UAAM,kBAAkB,OAAO,aAAa,OAAO,CAAC,MAAM,EAAE,SAAS,UAAU;AAC/E,UAAM,iBAAiB,OAAO,aAAa,OAAO,CAAC,MAAM,EAAE,SAAS,SAAS;AAC7E,UAAM,eAAe,OAAO,aAAa,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO;AAOzE,UAAM,aAAa,oBAAI,IAAI;AAAA,MACzB,GAAG,gBAAgB,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA,MACpC,GAAG,eAAe,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA,MACnC,GAAG,aAAa,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,aAAa,EAAE,IAAI,CAAC;AAAA,MAC9D,GAAG,OAAO,gBAAgB,IAAI,CAAC,MAAM,EAAE,YAAY;AAAA,IACrD,CAAC;AACD,UAAM,SAAS,WAAW;AAAA,MACxB,CAAC,MAAM,EAAE,WAAW,YAAY,CAAC,WAAW,IAAI,EAAE,YAAY;AAAA,IAChE,EAAE;AACF,UAAM,gBAAgB,OAAO,cAAc,OAAO,CAAC,MAAM,EAAE,SAAS,SAAS;AAC7E,UAAM,iBAAiB,OAAO,cAAc,OAAO,CAAC,MAAM,EAAE,SAAS,UAAU;AAC/E,UAAM,gBAAgB,OAAO,cAAc,OAAO,CAAC,MAAM,EAAE,SAAS,SAAS;AAC7E,UAAM,cAAc,OAAO,cAAc,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO;AACzE,UAAM,EAAE,YAAY,IAAI;AAExB,YAAQ,IAAI;AAAA,IAAO,OAAO,kBAAkB,KAAK,EAAE,CAAC,EAAE;AACtD,YAAQ,IAAI,KAAK,MAAM,MAAM,IAAIA,OAAM,MAAM,QAAQ,CAAC,QAAQ,MAAM,EAAE;AACtE,QAAI,gBAAgB,SAAS,GAAG;AAC9B,cAAQ,IAAI,KAAK,MAAM,QAAQ,IAAIA,OAAM,OAAO,UAAU,CAAC,MAAM,gBAAgB,MAAM,EAAE;AAAA,IAC3F;AACA,QAAI,aAAa,SAAS,GAAG;AAC3B,cAAQ,IAAI,KAAK,MAAM,IAAI,IAAIA,OAAM,KAAK,OAAO,CAAC,SAAS,aAAa,MAAM,EAAE;AAAA,IAClF;AACA,QAAI,eAAe,SAAS,GAAG;AAC7B,cAAQ,IAAI,KAAKA,OAAM,IAAI,GAAG,CAAC,IAAIA,OAAM,IAAI,SAAS,CAAC,OAAO,eAAe,MAAM,EAAE;AAAA,IACvF;AACA,QAAI,gBAAgB,GAAG;AACrB,cAAQ,IAAI,KAAK,MAAM,WAAW,IAAIA,OAAM,QAAQ,UAAU,CAAC,MAAM,aAAa,EAAE;AAAA,IACtF;AAGA,QAAI,eAAe,SAAS,GAAG;AAC7B,cAAQ;AAAA,QACN,KAAK,MAAM,OAAO,IAAIA,OAAM,KAAK,KAAK,CAAC,WAAW,eAAe,MAAM,IAAI,QAAQ,aAAa,CAAC;AAAA,MACnG;AAAA,IACF;AAGA,QAAI,gBAAgB,SAAS,GAAG;AAC9B,cAAQ,IAAI;AAAA,IAAO,OAAO,iBAAiB,CAAC,EAAE;AAC9C,iBAAW,KAAK,gBAAgB,MAAM,GAAG,EAAE,GAAG;AAC5C,gBAAQ;AAAA,UACN,OAAOA,OAAM,OAAO,GAAG,CAAC,IAAI,EAAE,KAAK,OAAO,EAAE,CAAC,IAAI,QAAQ,iBAAiB,CAAC;AAAA,QAC7E;AAAA,MACF;AACA,UAAI,gBAAgB,SAAS,IAAI;AAC/B,gBAAQ,IAAI,QAAQ,eAAe,gBAAgB,SAAS,EAAE,OAAO,CAAC;AAAA,MACxE;AAAA,IACF;AAGA,QAAI,YAAY,SAAS,aAAa,SAAS,GAAG;AAChD,cAAQ,IAAI;AAAA,IAAO,OAAO,QAAQ,CAAC,EAAE;AACrC,iBAAW,QAAQ,aAAa;AAC9B,gBAAQ,IAAI,OAAO,MAAM,IAAI,IAAI,KAAK,IAAI,YAAO,KAAK,EAAE,KAAK,QAAQ,UAAU,CAAC,EAAE;AAAA,MACpF;AACA,iBAAW,KAAK,aAAa,MAAM,GAAG,EAAE,GAAG;AACzC,gBAAQ,IAAI,OAAO,MAAM,IAAI,IAAI,EAAE,aAAa,GAAG,WAAM,EAAE,IAAI,EAAE;AAAA,MACnE;AACA,UAAI,aAAa,SAAS,IAAI;AAC5B,gBAAQ,IAAI,QAAQ,eAAe,aAAa,SAAS,EAAE,OAAO,CAAC;AAAA,MACrE;AAAA,IACF;AAGA,QAAI,eAAe,SAAS,GAAG;AAC7B,cAAQ,IAAI;AAAA,IAAO,OAAO,gBAAgB,CAAC,EAAE;AAC7C,iBAAW,KAAK,eAAe,MAAM,GAAG,EAAE,GAAG;AAC3C,gBAAQ,IAAI,OAAOA,OAAM,IAAI,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE;AAAA,MAC/C;AACA,UAAI,eAAe,SAAS,IAAI;AAC9B,gBAAQ,IAAI,QAAQ,eAAe,eAAe,SAAS,EAAE,OAAO,CAAC;AAAA,MACvE;AAAA,IACF;AAGA,QAAI,eAAe,SAAS,GAAG;AAC7B,cAAQ,IAAI;AAAA,IAAO,OAAO,YAAY,CAAC,EAAE;AACzC,iBAAW,KAAK,eAAe,MAAM,GAAG,EAAE,GAAG;AAC3C,gBAAQ,IAAI,OAAOA,OAAM,KAAK,GAAG,CAAC,IAAI,EAAE,KAAK,OAAO,EAAE,CAAC,IAAI,QAAQ,aAAa,CAAC,EAAE;AAAA,MACrF;AACA,UAAI,eAAe,SAAS,IAAI;AAC9B,gBAAQ,IAAI,QAAQ,eAAe,eAAe,SAAS,EAAE,OAAO,CAAC;AAAA,MACvE;AAAA,IACF;AAGA,QAAI,OAAO,cAAc,SAAS,GAAG;AACnC,cAAQ,IAAI;AAAA,IAAO,OAAO,4BAA4B,OAAO,cAAc,MAAM,EAAE,CAAC,EAAE;AACtF,UAAI,cAAc,SAAS,GAAG;AAC5B,gBAAQ,IAAI,OAAOA,OAAM,KAAK,GAAG,CAAC,IAAIA,OAAM,KAAK,KAAK,CAAC,WAAW,cAAc,MAAM,EAAE;AAAA,MAC1F;AACA,UAAI,eAAe,SAAS,GAAG;AAC7B,gBAAQ;AAAA,UACN,OAAOA,OAAM,OAAO,GAAG,CAAC,IAAIA,OAAM,OAAO,UAAU,CAAC,MAAM,eAAe,MAAM;AAAA,QACjF;AAAA,MACF;AACA,UAAI,YAAY,SAAS,GAAG;AAC1B,gBAAQ,IAAI,OAAO,MAAM,IAAI,IAAIA,OAAM,KAAK,OAAO,CAAC,SAAS,YAAY,MAAM,EAAE;AAAA,MACnF;AACA,UAAI,cAAc,SAAS,GAAG;AAC5B,gBAAQ,IAAI,OAAOA,OAAM,IAAI,GAAG,CAAC,IAAIA,OAAM,IAAI,SAAS,CAAC,OAAO,cAAc,MAAM,EAAE;AAAA,MACxF;AACA,iBAAW,KAAK,OAAO,cAAc,MAAM,GAAG,EAAE,GAAG;AACjD,cAAM,OACJ,EAAE,SAAS,YACPA,OAAM,KAAK,GAAG,IACd,EAAE,SAAS,aACTA,OAAM,OAAO,GAAG,IAChB,EAAE,SAAS,UACT,MAAM,OACNA,OAAM,IAAI,GAAG;AAEvB,cAAM,OAAO,EAAE,SAAS,EAAE,QAAQ,GAAG,EAAE,KAAK,IAAI,QAAQ,YAAY,CAAC,KAAK;AAC1E,gBAAQ;AAAA,UACN,OAAO,IAAI,IAAI,QAAQ,GAAG,EAAE,OAAO,MAAM,GAAG,CAAC,CAAC,KAAK,IAAI,QAAQ,IAAI,EAAE,IAAI,GAAG,CAAC;AAAA,QAC/E;AAAA,MACF;AACA,UAAI,OAAO,cAAc,SAAS,IAAI;AACpC,gBAAQ,IAAI,QAAQ,eAAe,OAAO,cAAc,SAAS,EAAE,OAAO,CAAC;AAAA,MAC7E;AAAA,IACF;AAGA,QAAI,gBAAgB,GAAG;AACrB,cAAQ,IAAI;AAAA,IAAO,OAAOA,OAAM,QAAQ,YAAY,CAAC,CAAC,EAAE;AAExD,YAAMC,iBAAgB,IAAI;AAAA,QACxB,OAAO,UAAU,OAAO,gBAAgB,EAAE,IAAI,CAAC,MAAM,EAAE,WAAW,YAAY;AAAA,MAChF;AACA,iBAAW,KAAK,OAAO,iBAAiB;AACtC,cAAM,SAASA,eAAc,IAAI,EAAE,YAAY,IAC3C,gDACA;AACJ,gBAAQ,IAAI,OAAO,MAAM,QAAQ,IAAI,EAAE,aAAa,OAAO,EAAE,CAAC,IAAI,QAAQ,MAAM,CAAC,EAAE;AAAA,MACrF;AAAA,IACF;AAGA,UAAM,aACJ,OAAO,aAAa,SAAS,KAC7B,YAAY,SAAS,KACrB,OAAO,cAAc,SAAS,KAC9B,gBAAgB;AAClB,QAAI,YAAY;AACd,cAAQ,IAAI,EAAE;AACd,UAAI,gBAAgB,GAAG;AACrB,gBAAQ;AAAA,UACN,KAAK,QAAQ,KAAK,CAAC,IAAID,OAAM,KAAK,eAAe,CAAC,IAAI,QAAQ,sBAAsB,CAAC;AAAA,QACvF;AAAA,MACF;AACA,cAAQ;AAAA,QACN,KAAK,QAAQ,KAAK,CAAC,IAAIA,OAAM,KAAK,YAAY,CAAC,IAAI,QAAQ,sBAAsB,CAAC;AAAA,MACpF;AAEA,UAAI,gBAAgB,SAAS,eAAe,SAAS,GAAG;AACtD,gBAAQ;AAAA,UACN,KAAK,QAAQ,KAAK,CAAC,IAAIA,OAAM,KAAK,sBAAsB,CAAC,IAAI,QAAQ,wBAAwB,CAAC;AAAA,QAChG;AAAA,MACF;AAAA,IACF,OAAO;AACL,cAAQ,IAAI;AAAA,IAAOA,OAAM,MAAM,uBAAuB,CAAC,SAAI;AAAA,IAC7D;AACA,QAAI,CAAC,KAAK,MAAM;AACd,cAAQ;AAAA,QACN;AAAA,IAAO,QAAQ,KAAK,CAAC,IAAIA,OAAM,KAAK,qBAAqB,CAAC,IAAI,QAAQ,gCAAgC,CAAC;AAAA,MACzG;AAAA,IACF,WAAW,OAAO,YAAY,mBAAmB;AAE/C,cAAQ,IAAI;AAAA,IAAO,mBAAmB,OAAO,UAAU,CAAC,EAAE;AAAA,IAC5D;AAAA,EACF,UAAE;AACA,YAAQ,MAAM;AAAA,EAChB;AACF,CAAC;;;ACrPH,SAAS,WAAAE,gBAAe;AACxB;AAAA,EACE,iBAAAC;AAAA,EACA,WAAAC;AAAA,EACA,gBAAAC;AAAA,EACA,oBAAAC;AAAA,EACA;AAAA,EACA;AAAA,EACA,oBAAAC;AAAA,OACK;;;ACTP,SAAS,iBAAiB;AAE1B,IAAM,OAAO;AACb,IAAM,MAAM;AACZ,IAAM,QAAQ;AACd,IAAM,OAAO;AACb,IAAM,QAAQ;AAGd,IAAM,aAA+C;AAAA,EACnD,CAAC,UAAU,KAAK,GAAG;AAAA,EACnB,CAAC,UAAU,OAAO,GAAG;AAAA,EACrB,KAAK;AACP;AAQO,SAAS,iBAAiB,OAA0B,OAAsB;AAC/E,QAAM,QAAQ,CAAC,MAAM,UAAU;AAC7B,UAAM,OAAO,QAAQ,IAAI,OAAO,WAAW,KAAK,OAAO,CAAC,CAAC;AACzD,YAAQ,IAAI,SAAS,OAAO,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,KAAK,IAAI;AAAA,EAC7D,CAAC;AACH;;;ADTO,IAAM,cAAc,IAAIC,SAAQ,MAAM,EAC1C;AAAA,EACC;AACF,EACC,SAAS,UAAU,2GAA2B,EAC9C,OAAO,WAAW,6BAAS,IAAI,EAC/B,OAAO,YAAY,sFAA+B,EAClD,OAAO,OAAO,YAAgC,YAAY;AACzD,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,gBAAgB,IAAIC,eAAc,GAAG;AAC3C,QAAM,SAAS,MAAM,cAAc,KAAK;AACxC,QAAM,UAAUC,SAAQ,KAAK,cAAc,MAAM;AAEjD,MAAI;AACF,UAAM,SAASC,cAAa,WAAW,MAAM;AAC7C,UAAM,UAAU,MAAM,YAAY,KAAK,OAAO,KAAK;AACnD,UAAM,eAAe,IAAIC,kBAAiB,QAAQ,SAAS,QAAQ,OAAO;AAE1E,UAAM,SAAS,MAAM,aAAa,YAAY;AAE9C,UAAM,UAAU,aACZ,OAAO,aAAa,OAAO,CAAC,MAAMC,kBAAiB,EAAE,MAAM,UAAU,CAAC,IACtE,OAAO;AAEX,QAAI,QAAQ,WAAW,GAAG;AACxB,cAAQ,IAAI,uCAAS;AACrB;AAAA,IACF;AAEA,QAAI,SAAS;AACb,eAAW,UAAU,SAAS;AAG5B,UAAI,QAAQ,UAAU,OAAO,SAAS,WAAW;AAE/C,YAAI,aAAa,OAAO;AACxB,YAAI,SAAS,MAAM,aAAa,qBAAqB,UAAU;AAC/D,YAAI,WAAW,QAAQ,OAAO,WAAW;AACvC,uBAAa,OAAO;AACpB,mBAAS,MAAM,aAAa,qBAAqB,UAAU;AAAA,QAC7D;AACA,YAAI,WAAW,MAAM;AACnB,kBAAQ,IAAI,mCAAe,OAAO,IAAI,EAAE;AACxC,kBAAQ,IAAI,SAAS,OAAO,IAAI,EAAE;AAClC,kBAAQ,IAAI,0EAAwB;AACpC,kBAAQ,IAAI,EAAE;AACd;AAAA,QACF;AACA,cAAM,iBAAiB,MAAM,QAAQ,SAAS,OAAO,IAAI;AACzD,cAAM,QAAQ,SAAS,QAAQ,cAAc;AAC7C,cAAM,QAAQ,kBAAkB,OAAO,UAAU,UAAU,IAAI,KAAK,OAAO,IAAI,EAAE;AAEjF,YAAI,MAAM,WAAW,EAAG,OAAM,KAAK,6DAAqB;AACxD,yBAAiB,OAAO,QAAQ,KAAK;AACrC,gBAAQ,IAAI,EAAE;AACd;AAAA,MACF;AAGA,UAAI;AACJ,UAAI;AACF,eAAO,MAAM,aAAa,gBAAgB,MAAM;AAAA,MAClD,SAAS,OAAO;AACd;AACA,gBAAQ,IAAI,SAAS,OAAO,IAAI,EAAE;AAClC,gBAAQ,IAAI,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,GAAG;AACzE,gBAAQ,IAAI,EAAE;AACd;AAAA,MACF;AACA,sBAAgB,MAAM,QAAQ,KAAK;AACnC,cAAQ,IAAI,EAAE;AAAA,IAChB;AAEA,QAAI,SAAS,EAAG,SAAQ,WAAW;AAAA,EACrC,UAAE;AACA,YAAQ,MAAM;AAAA,EAChB;AACF,CAAC;AAGH,SAAS,gBAAgB,MAAkB,OAAsB;AAC/D,QAAM,UAAU,KAAK,WAAW,OAAO,cAAc,KAAK,KAAK,aAAa,KAAK,IAAI;AACrF,QAAM,UAAU,KAAK,UAAU,OAAO,cAAc,KAAK,KAAK,IAAI;AAClE,MAAI,KAAK,WAAW;AAClB,YAAQ,IAAI,eAAe,KAAK,SAAS,EAAE;AAC3C,YAAQ,IAAI,aAAa,KAAK,IAAI,EAAE;AACpC,QAAI,KAAK,WAAW,KAAK,OAAO;AAC9B,cAAQ,IAAI,oHAA0B;AACtC;AAAA,IACF;AAAA,EACF;AACA;AAAA,IACE,kBAAkB,SAAS,KAAK,UAAU,IAAI,KAAK,SAAS,EAAE,GAAG,SAAS,OAAO;AAAA,IACjF;AAAA,EACF;AACF;;;AEhHA,SAAS,WAAAC,gBAAe;AACxB,SAAS,iBAAAC,gBAAe,WAAAC,UAAS,gBAAAC,eAAc,oBAAAC,yBAAwB;AAOhE,IAAM,iBAAiB,IAAIC,SAAQ,SAAS,EAChD,YAAY,uJAAyC,EACrD,SAAS,cAAc,uHAAkC,EACzD,OAAO,OAAO,UAAoB;AACjC,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,gBAAgB,IAAIC,eAAc,GAAG;AAC3C,QAAM,SAAS,MAAM,cAAc,KAAK;AACxC,QAAM,UAAUC,SAAQ,KAAK,cAAc,MAAM;AAEjD,MAAI;AACF,UAAM,SAASC,cAAa,WAAW,MAAM;AAC7C,UAAM,UAAU,MAAM,YAAY,KAAK,OAAO,KAAK;AACnD,UAAM,eAAe,IAAIC,kBAAiB,QAAQ,SAAS,QAAQ,OAAO;AAE1E,QAAI,SAAS;AACb,eAAW,QAAQ,OAAO;AACxB,UAAI;AACF,cAAM,aAAa,mBAAmB,IAAI;AAC1C,gBAAQ,IAAI,KAAK,MAAM,OAAO,IAAI,IAAI,EAAE;AAAA,MAC1C,SAAS,OAAO;AACd;AACA,mBAAW,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,MACzE;AAAA,IACF;AACA,QAAI,SAAS,EAAG,SAAQ,WAAW;AAAA,EACrC,UAAE;AACA,YAAQ,MAAM;AAAA,EAChB;AACF,CAAC;;;ACpCH,SAAS,WAAAC,gBAAe;AACxB,SAAS,iBAAAC,gBAAe,WAAAC,UAAS,gBAAAC,eAAc,oBAAAC,yBAAwB;AACvE,OAAOC,YAAW;AAIX,IAAM,eAAe,IAAIC,SAAQ,OAAO,EAC5C,YAAY,8FAA6B,EACzC,OAAO,YAAY;AAClB,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,gBAAgB,IAAIC,eAAc,GAAG;AAC3C,QAAM,SAAS,MAAM,cAAc,KAAK;AACxC,QAAM,UAAUC,SAAQ,KAAK,cAAc,MAAM;AAEjD,MAAI;AACF,UAAM,SAASC,cAAa,WAAW,MAAM;AAC7C,UAAM,UAAU,MAAM,YAAY,KAAK,OAAO,KAAK;AACnD,UAAM,eAAe,IAAIC,kBAAiB,QAAQ,SAAS,QAAQ,OAAO;AAE1E,YAAQ,IAAI,QAAQ,4BAA4B,CAAC;AAEjD,UAAM,SAAS,MAAM,aAAa,MAAM;AAExC,YAAQ,IAAI;AAAA,EAAK,OAAO,eAAe,CAAC,EAAE;AAC1C,YAAQ,IAAI,KAAK,UAAU,EAAE,CAAC,EAAE;AAEhC,QAAI,OAAO,WAAW,GAAG;AACvB,cAAQ,IAAI,KAAKC,OAAM,KAAK,GAAG,CAAC,oBAAoB,OAAO,QAAQ,EAAE;AAAA,IACvE;AACA,QAAI,OAAO,gBAAgB,GAAG;AAC5B,cAAQ,IAAI,KAAKA,OAAM,OAAO,GAAG,CAAC,oBAAoB,OAAO,aAAa,EAAE;AAAA,IAC9E;AACA,QAAI,OAAO,eAAe,GAAG;AAC3B,cAAQ,IAAI,KAAKA,OAAM,IAAI,GAAG,CAAC,oBAAoB,OAAO,YAAY,EAAE;AAAA,IAC1E;AAEA,UAAM,QAAQ,OAAO,WAAW,OAAO,gBAAgB,OAAO;AAC9D,QAAI,UAAU,GAAG;AACf,cAAQ,IAAI,KAAKA,OAAM,MAAM,sBAAsB,CAAC,SAAI;AAAA,IAC1D,OAAO;AACL,cAAQ;AAAA,QACN;AAAA,IAAO,QAAQ,KAAK,CAAC,IAAIA,OAAM,KAAK,YAAY,CAAC,IAAI,QAAQ,yBAAyB,CAAC;AAAA,MACzF;AAAA,IACF;AAEA,YAAQ,IAAI,QAAQ;AAAA,kBAAqB,OAAO,WAAW,KAAM,QAAQ,CAAC,CAAC,GAAG,CAAC;AAAA,EACjF,UAAE;AACA,YAAQ,MAAM;AAAA,EAChB;AACF,CAAC;;;ACjDH,SAAS,WAAAC,gBAAe;AACxB,SAAS,QAAQ,WAAAC,gBAAe;AAChC;AAAA,EACE,iBAAAC;AAAA,EACA,WAAAC;AAAA,EACA,gBAAAC;AAAA,EACA,oBAAAC;AAAA,EACA;AAAA,EACA,oBAAAC;AAAA,OACK;;;ACFP,SAAS,+BAA+B;AAIjC,SAAS,gBAAgB,OAAwB;AACtD,QAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,MAAI,EAAE,iBAAiB,yBAA0B,QAAO;AACxD,SAAO,GAAG,OAAO,WAAM,MAAM,SAAS,gEAAc,CAAC;AACvD;AAMO,SAAS,qBAAqB,OAAgB,MAA6B;AAChF,MAAI,EAAE,iBAAiB,yBAA0B,OAAM;AACvD,UAAQ,MAAM,iBAAO,gBAAgB,KAAK,CAAC,EAAE;AAC7C,OAAK,WAAW;AAClB;;;ADTA,IAAM,aAAa,CAAC,eAAe,gBAAgB,WAAW;AAG9D,IAAM,eAAiD;AAAA,EACrD,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,WAAW;AACb;AAGA,IAAM,+BAA0E;AAAA,EAC9E,OAAO;AAAA,EACP,QAAQ;AACV;AAGA,IAAM,yCAAoF;AAAA,EACxF,OAAO;AACT;AAEA,SAAS,WAAW,UAAoB,QAA0B,SAA0B;AAC1F,QAAM,UAAUC,kBAAiB,QAAQ;AACzC,UACG,WAAW,CAAC,UAAU,uCAAuC,MAAM,IAAI,YACvE,UAAU,6BAA6B,MAAM,IAAI,WAClD,aAAa,MAAM,KACnB;AAEJ;AAQA,SAAS,gBAAyB;AAChC,SAAO,QAAQ,MAAM,UAAU,QAAQ,QAAQ,OAAO,UAAU;AAClE;AAEO,IAAM,iBAAiB,IAAIC,SAAQ,SAAS,EAChD,YAAY,mDAAW,EACvB,OAAO,6BAA6B,2CAAa,WAAW,KAAK,KAAK,CAAC,GAAG,EAC1E,OAAO,aAAa,+FAAyB,EAC7C,OAAO,OAAO,YAAkD;AAC/D,MAAI,QAAQ,YAAY,CAAC,WAAW,SAAS,QAAQ,QAAoB,GAAG;AAC1E,YAAQ,MAAM,4CAAc,QAAQ,QAAQ,uCAAc,WAAW,KAAK,IAAI,CAAC,EAAE;AACjF,YAAQ,WAAW;AACnB;AAAA,EACF;AAEA,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,gBAAgB,IAAIC,gBAAc,GAAG;AAC3C,MAAI;AAEJ,MAAI;AACF,UAAM,SAAS,MAAM,cAAc,KAAK;AACxC,cAAUC,SAAQ,KAAK,cAAc,MAAM;AAC3C,UAAM,SAASC,cAAa,WAAW,MAAM;AAC7C,UAAM,UAAU,MAAM,YAAY,KAAK,OAAO,KAAK;AACnD,UAAM,eAAe,IAAIC,kBAAiB,QAAQ,SAAS,QAAQ,OAAO;AAI1E,UAAM,YAAY,MAAM,aAAa,cAAc;AAEnD,QAAI,UAAU,WAAW,GAAG;AAC1B,cAAQ,IAAI,8CAAW;AACvB;AAAA,IACF;AAIA,UAAM,UAAU,IAAI,IAAI,aAAa,oBAAoB,SAAS,CAAC;AACnE,eAAW,QAAQ,SAAS;AAC1B,cAAQ,IAAI,2BAAsB,IAAI,iFAAqB;AAAA,IAC7D;AAEA,UAAM,UAAU,UAAU,OAAO,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,WAAW,YAAY,CAAC;AAC/E,QAAI,QAAQ,WAAW,GAAG;AACxB,cAAQ,IAAI;AAAA,EAAK,QAAQ,IAAI,0JAAkC;AAC/D;AAAA,IACF;AAEA,YAAQ,IAAI;AAAA,eAAQ,QAAQ,MAAM;AAAA,CAAQ;AAC1C,UAAM,UAAU,OAAO,KAAK,cAAc;AAC1C,QAAI,CAAC,SAAS;AACZ,cAAQ;AAAA,QACN;AAAA,MACF;AAAA,IACF;AAEA,QAAI,QAAQ,UAAU;AACpB,YAAM,WAAW,QAAQ;AAEzB,YAAM,YAAY,QAAQ,OAAOL,iBAAgB,EAAE;AACnD,UAAI,YAAY,GAAG;AACjB,gBAAQ;AAAA,UACN,yDAAsB,SAAS,iBAC7B,aAAa,iBACT,yDACA,UACE,wFACA,mEACR;AAAA,QACF;AAAA,MACF;AAEA,UAAI,CAAC,QAAQ,KAAK;AAChB,YAAI,CAAC,cAAc,GAAG;AACpB,kBAAQ;AAAA,YACN,mEAAiB,QAAQ,8BAAU,QAAQ,MAAM;AAAA,UACnD;AACA,kBAAQ,WAAW;AACnB;AAAA,QACF;AACA,cAAM,gBAAgB,MAAMM,SAAQ;AAAA,UAClC,SAAS,GAAG,QAAQ,MAAM,oCAAW,QAAQ;AAAA,QAC/C,CAAC;AACD,YAAI,CAAC,cAAe;AAAA,MACtB;AAEA,YAAM,UAAU,MAAM,aAAa,oBAAoB,SAAS,QAAQ;AACxE,UAAIC,UAAS;AACb,iBAAW,CAAC,OAAO,MAAM,KAAK,QAAQ,QAAQ,GAAG;AAC/C,YAAI,CAAC,OAAO,QAAS,CAAAA;AACrB,cAAM,OAAO,OAAO,UAAU,0BAAqB;AACnD,cAAM,SAAS,OAAO,QAAQ,WAAM,OAAO,KAAK,KAAK;AACrD,gBAAQ;AAAA,UACN,KAAK,IAAI,IAAI,OAAO,IAAI,WAAM,WAAW,QAAQ,KAAK,GAAI,OAAO,QAAQ,OAAO,CAAC,GAAG,MAAM;AAAA,QAC5F;AAAA,MACF;AACA,aAAO,QAAQ,SAASA,SAAQA,OAAM;AACtC;AAAA,IACF;AAEA,QAAI,CAAC,cAAc,GAAG;AACpB,cAAQ;AAAA,QACN,2JAA6C,WAAW,KAAK,GAAG,CAAC;AAAA,MACnE;AACA,cAAQ,WAAW;AACnB;AAAA,IACF;AAEA,QAAI,WAAW;AACf,QAAI,SAAS;AACb,eAAW,YAAY,SAAS;AAC9B,UAAI,MAAM,mBAAmB,UAAU,cAAc,OAAO,EAAG;AAAA,UAC1D;AAAA,IACP;AACA,WAAO,UAAU,MAAM;AAAA,EACzB,SAAS,OAAO;AACd,YAAQ,MAAM,iBAAO,gBAAgB,KAAK,CAAC;AAC3C,YAAQ,WAAW;AAAA,EACrB,UAAE;AAEA,aAAS,MAAM;AAAA,EACjB;AACF,CAAC;AAGH,SAAS,OAAO,UAAkB,QAAsB;AACtD,MAAI,WAAW,GAAG;AAChB,YAAQ,IAAI;AAAA,gDAAgB,QAAQ,QAAG;AACvC;AAAA,EACF;AACA,UAAQ,IAAI;AAAA,uBAAgB,QAAQ,4BAAU,MAAM,kCAAc;AAClE,UAAQ,WAAW;AACrB;AAGA,eAAe,mBACb,UACA,cACA,SACkB;AAClB,QAAM,OAAO,SAAS,WAAW;AAEjC,UAAQ,IAAI;AAAA,EAAK,SAAI,OAAO,EAAE,CAAC,EAAE;AACjC,UAAQ,IAAI,wBAAc,IAAI,SAAS;AACvC,UAAQ,IAAI,GAAG,SAAI,OAAO,EAAE,CAAC,EAAE;AAE/B,MAAIP,kBAAiB,QAAQ,GAAG;AAE9B,YAAQ;AAAA,MACN;AAAA,IACF;AAAA,EACF,OAAO;AACL,qBAAiB,aAAa,qBAAqB,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;AAAA,EAChF;AAEA,QAAM,SAAS,MAAM,OAAyB;AAAA,IAC5C,SAAS;AAAA,IACT,SAAS,kBAAkB,QAAQ,EAAE,IAAI,CAAC,WAAW;AAAA,MACnD,MAAM,WAAW,UAAU,OAAO,OAAO;AAAA,MACzC;AAAA,IACF,EAAE;AAAA,EACJ,CAAC;AAED,QAAM,SAAS,MAAM,aAAa,gBAAgB,UAAU,MAAM;AAElE,MAAI,OAAO,SAAS;AAClB,YAAQ,IAAI,kBAAa,IAAI,WAAM,WAAW,UAAU,QAAQ,OAAO,CAAC,SAAS;AACjF,WAAO;AAAA,EACT;AACA,MAAI,OAAO,mBAAmB;AAC5B,YAAQ,IAAI,kBAAa,IAAI,6IAAyC;AACtE,WAAO;AAAA,EACT;AACA,UAAQ,IAAI,kBAAa,IAAI,0CAAiB;AAC9C,SAAO;AACT;;;AEpOA,SAAS,WAAAQ,iBAAe;AACxB;AAAA,EACE,iBAAAC;AAAA,EACA,WAAAC;AAAA,EACA,gBAAAC;AAAA,EACA,oBAAAC;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAIP,SAAS,aAAa,QAA4B;AAChD,QAAM,OAAO,UAAU,OAAO,KAAK,OAAO,KAAK,OAAO,KAAK,OAAO,KAAK,OAAO,KAAK,OAAO;AAC1F,QAAM,QAAQ,OAAO,KAAK,QAAQ,IAAI,UAAK,OAAO,KAAK,KAAK,KAAK;AACjE,QAAM,OAAO,UAAU,OAAO,KAAK,OAAO,KAAK,OAAO,KAAK,OAAO,KAAK,OAAO,KAAK,OAAO,GAAG,KAAK;AAClG,QAAM,QAAQ,CAAC,MAAM,IAAI;AAEzB,MAAI,OAAO,UAAU,SAAS,GAAG;AAC/B,UAAM,KAAK,gBAAM,OAAO,UAAU,MAAM,QAAG;AAAA,EAC7C;AAEA,SAAO,MAAM,KAAK,KAAK;AACzB;AAEA,SAAS,YAAoB;AAC3B,UAAO,oBAAI,KAAK,GAAE,mBAAmB,SAAS,EAAE,QAAQ,MAAM,CAAC;AACjE;AAGA,SAAS,UAAU,OAA+B;AAChD,SAAO,UAAU,SAAS,0CAAY;AACxC;AAEO,IAAM,eAAe,IAAIC,UAAQ,OAAO,EAC5C,YAAY,0EAAmB,EAC/B,OAAO,uBAAuB,0DAAkB,OAAO,yBAAyB,CAAC,EACjF,OAAO,4BAA4B,oFAAwB,GAAG,EAC9D,OAAO,OAAO,YAAoD;AAEjE,QAAM,aAAa,OAAO,QAAQ,QAAQ;AAC1C,QAAM,cAAc,OAAO,QAAQ,QAAQ;AAC3C,MAAI,CAAC,OAAO,UAAU,UAAU,KAAK,aAAa,GAAG;AACnD,YAAQ,MAAM,2FAAoC,QAAQ,QAAQ,EAAE;AACpE,YAAQ,WAAW;AACnB;AAAA,EACF;AACA,MAAI,CAAC,OAAO,UAAU,WAAW,KAAK,cAAc,GAAG;AACrD,YAAQ,MAAM,+FAAmC,QAAQ,QAAQ,EAAE;AACnE,YAAQ,WAAW;AACnB;AAAA,EACF;AAEA,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,gBAAgB,IAAIC,gBAAc,GAAG;AAC3C,QAAM,SAAS,MAAM,cAAc,KAAK;AACxC,QAAM,UAAUC,UAAQ,KAAK,cAAc,MAAM;AAEjD,QAAM,SAASC,eAAa,WAAW,MAAM;AAC7C,QAAM,UAAU,MAAM,YAAY,KAAK,OAAO,KAAK;AACnD,QAAM,eAAe,IAAIC,kBAAiB,QAAQ,SAAS,QAAQ,OAAO;AAE1E,QAAM,UAAU,IAAI,iBAAiB,KAAK,cAAc;AAAA,IACtD;AAAA,IACA,cAAc,CAAC,OAAO,SAAS;AAC7B,cAAQ,IAAI,IAAI,UAAU,CAAC,KAAK,KAAK,KAAK,IAAI,EAAE;AAAA,IAClD;AAAA,IACA,aAAa,CAAC,UAAU;AACtB,cAAQ,IAAI,IAAI,UAAU,CAAC,KAAK,UAAU,KAAK,CAAC,kBAAQ;AAAA,IAC1D;AAAA,IACA,gBAAgB,CAAC,QAAQ,UAAU;AACjC,cAAQ,IAAI,IAAI,UAAU,CAAC,KAAK,UAAU,KAAK,CAAC,wBAAS,aAAa,MAAM,CAAC,EAAE;AAAA,IACjF;AAAA,IACA,iBAAiB,CAAC,QAAQ,UAAU;AAClC,cAAQ;AAAA,QACN,IAAI,UAAU,CAAC,KAAK,UAAU,KAAK,CAAC,8DAAiB,aAAa,MAAM,CAAC;AAAA,MAC3E;AAAA,IACF;AAAA,IACA,aAAa,CAAC,OAAO,UAAU;AAC7B,cAAQ,MAAM,IAAI,UAAU,CAAC,KAAK,UAAU,KAAK,CAAC,kBAAQ,MAAM,OAAO,EAAE;AAAA,IAC3E;AAAA,EACF,CAAC;AAED,MAAI,YAAmD;AACvD,MAAI,eAAe;AAEnB,QAAM,WAAW,YAA2B;AAE1C,QAAI,cAAc;AAChB,cAAQ,IAAI,IAAI,UAAU,CAAC,6BAAS;AACpC,aAAO,QAAQ,KAAK,CAAC;AAAA,IACvB;AACA,mBAAe;AACf,YAAQ,IAAI;AAAA,GAAM,UAAU,CAAC,iIAAkC;AAC/D,QAAI,UAAW,eAAc,SAAS;AACtC,UAAM,QAAQ,KAAK;AACnB,YAAQ,MAAM;AACd,YAAQ,IAAI,IAAI,UAAU,CAAC,6BAAS;AACpC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,GAAG,UAAU,MAAM,KAAK,SAAS,CAAC;AAC1C,UAAQ,GAAG,WAAW,MAAM,KAAK,SAAS,CAAC;AAE3C,UAAQ,MAAM;AACd,UAAQ,IAAI,IAAI,UAAU,CAAC,uDAAyB,UAAU,KAAK;AAEnE,MAAI,cAAc,GAAG;AACnB,YAAQ,IAAI,IAAI,UAAU,CAAC,6CAAe,WAAW,QAAG;AAExD,gBAAY,YAAY,MAAM,QAAQ,gBAAgB,GAAG,cAAc,GAAI;AAAA,EAC7E;AAEA,QAAM,IAAI,QAAQ,MAAM;AAAA,EAAC,CAAC;AAC5B,CAAC;;;AClHH,SAAS,WAAAC,iBAAe;AACxB,SAAS,iBAAAC,iBAAe,WAAAC,WAAS,gBAAAC,gBAAc,oBAAAC,0BAAwB;AAMvE,OAAOC,YAAW;AAKlB,IAAM,eAAe;AAErB,SAAS,cAAc,OAAe,KAAwB,OAAoC;AAChG,UAAQ,IAAI,SAAS,MAAM,KAAK,CAAC,IAAI,IAAI,MAAM,EAAE;AACjD,aAAW,MAAM,IAAI,MAAM,GAAG,YAAY,GAAG;AAC3C,YAAQ,IAAI,WAAW,QAAQ,EAAE,CAAC,EAAE;AAAA,EACtC;AACA,MAAI,IAAI,SAAS,cAAc;AAC7B,YAAQ,IAAI,QAAQ,mBAAmB,IAAI,SAAS,YAAY,OAAO,CAAC;AAAA,EAC1E;AACF;AAEA,SAAS,qBAAqBC,SAAkC;AAC9D,UAAQ,IAAI;AAAA,EAAK,OAAO,mBAAmB,CAAC,EAAE;AAC9C,UAAQ,IAAI,KAAK,UAAU,EAAE,CAAC,EAAE;AAChC,UAAQ,IAAI,KAAK,QAAQ,YAAY,CAAC,KAAKA,QAAO,UAAU,MAAM,EAAE;AACpE,UAAQ,IAAI,KAAK,QAAQ,cAAc,CAAC,IAAIA,QAAO,WAAW,EAAE;AAChE,UAAQ,IAAI,KAAK,QAAQ,aAAa,CAAC,KAAKA,QAAO,UAAU,EAAE;AAE/D,QAAM,SAASA,QAAO,UAAU,OAAO,CAAC,MAAM,EAAE,WAAW,SAAS,KAAK,EAAE,SAAS,SAAS,CAAC;AAE9F,MAAI,OAAO,SAAS,GAAG;AACrB,YAAQ,IAAI;AAAA,IAAO,OAAOC,OAAM,IAAI,yBAAyB,OAAO,MAAM,EAAE,CAAC,CAAC,EAAE;AAChF,eAAW,MAAM,QAAQ;AACvB,cAAQ;AAAA,QACN,OAAO,MAAM,IAAI,IAAI,GAAG,UAAU,KAAK,QAAQ,UAAU,GAAG,UAAU,YAAY,GAAG,SAAS,EAAE,CAAC;AAAA,MACnG;AACA,UAAI,GAAG,WAAW,SAAS,EAAG,eAAc,WAAW,GAAG,YAAYA,OAAM,GAAG;AAC/E,UAAI,GAAG,SAAS,SAAS,EAAG,eAAc,SAAS,GAAG,UAAUA,OAAM,MAAM;AAAA,IAC9E;AAAA,EACF;AAEA,MAAID,QAAO,SAAS,SAAS,GAAG;AAC9B,YAAQ,IAAI;AAAA,IAAO,OAAOC,OAAM,OAAO,mBAAmBD,QAAO,SAAS,MAAM,EAAE,CAAC,CAAC,EAAE;AACtF,eAAW,WAAWA,QAAO,UAAU;AACrC,cAAQ,IAAI,OAAO,MAAM,IAAI,IAAI,QAAQ,UAAU,KAAK,QAAQ,QAAQ,KAAK,CAAC,EAAE;AAAA,IAClF;AAAA,EACF;AAEA,MAAIA,QAAO,UAAU;AACnB,YAAQ,IAAI;AAAA,IAAOC,OAAM,MAAM,oCAAoC,CAAC,IAAI,MAAM,OAAO,EAAE;AAAA,EACzF;AACF;AASA,SAAS,iBAAiBD,SAAsC;AAC9D,UAAQ,IAAI;AAAA,EAAK,OAAO,qBAAqB,CAAC,EAAE;AAChD,UAAQ,IAAI,KAAK,UAAU,EAAE,CAAC,EAAE;AAChC,UAAQ,IAAI,KAAK,QAAQ,eAAe,CAAC,IAAIA,QAAO,WAAW,EAAE;AACjE,UAAQ,IAAI,KAAK,QAAQ,cAAc,CAAC,KAAKA,QAAO,UAAU,EAAE;AAEhE,MAAIA,QAAO,OAAO;AAChB,YAAQ,IAAI;AAAA,IAAO,MAAM,IAAI,IAAIC,OAAM,OAAO,yBAAyB,CAAC,EAAE;AAC1E,YAAQ,IAAI,OAAO,QAAQD,QAAO,KAAK,CAAC,EAAE;AAC1C;AAAA,EACF;AAEA,MAAIA,QAAO,WAAW,SAAS,GAAG;AAChC,YAAQ;AAAA,MACN;AAAA,IAAO,OAAOC,OAAM,IAAI,6BAA6BD,QAAO,WAAW,MAAM,EAAE,CAAC,CAAC;AAAA,IACnF;AACA,kBAAc,WAAWA,QAAO,YAAYC,OAAM,GAAG;AAAA,EACvD;AAEA,MAAID,QAAO,aAAa,SAAS,GAAG;AAClC,YAAQ;AAAA,MACN;AAAA,IAAO,QAAQ,qBAAqBA,QAAO,aAAa,MAAM,kBAAkB,CAAC;AAAA,IACnF;AACA,kBAAc,cAAcA,QAAO,cAAc,OAAO;AAAA,EAC1D;AAEA,MAAIA,QAAO,UAAU;AACnB,YAAQ,IAAI;AAAA,IAAOC,OAAM,MAAM,mCAAmC,CAAC,IAAI,MAAM,OAAO,EAAE;AAAA,EACxF;AACF;AAEA,SAAS,YAAYD,SAAuC;AAC1D,uBAAqBA,QAAO,SAAS;AAErC,MAAIA,QAAO,OAAO;AAChB,qBAAiBA,QAAO,KAAK;AAAA,EAC/B,OAAO;AAEL,YAAQ,IAAI;AAAA,IAAO,QAAQ,4CAA4C,CAAC,EAAE;AAAA,EAC5E;AAEA,MAAI,CAACA,QAAO,UAAU;AACpB,YAAQ;AAAA,MACN;AAAA,IAAO,QAAQ,KAAK,CAAC,IAAIC,OAAM,KAAK,YAAY,CAAC,IAAI,QAAQ,0BAA0B,CAAC;AAAA,IAC1F;AAAA,EACF;AACF;AAEO,IAAM,gBAAgB,IAAIC,UAAQ,QAAQ,EAC9C,YAAY,0LAAgD,EAC5D,OAAO,UAAU,mDAAgB,EACjC,OAAO,OAAO,SAA6B;AAC1C,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,gBAAgB,IAAIC,gBAAc,GAAG;AAC3C,QAAM,SAAS,MAAM,cAAc,KAAK;AACxC,QAAM,UAAUC,UAAQ,KAAK,cAAc,MAAM;AAEjD,MAAI;AACF,UAAM,SAASC,eAAa,WAAW,MAAM;AAC7C,UAAM,UAAU,MAAM,YAAY,KAAK,OAAO,KAAK;AACnD,UAAM,eAAe,IAAIC,mBAAiB,QAAQ,SAAS,QAAQ,OAAO;AAE1E,UAAMN,UAAS,MAAM,aAAa,mBAAmB;AAErD,QAAI,KAAK,MAAM;AACb,cAAQ,IAAI,KAAK,UAAUA,SAAQ,MAAM,CAAC,CAAC;AAAA,IAC7C,OAAO;AACL,kBAAYA,OAAM;AAAA,IACpB;AAGA,QAAI,CAACA,QAAO,SAAU,SAAQ,WAAW;AAAA,EAC3C,UAAE;AACA,YAAQ,MAAM;AAAA,EAChB;AACF,CAAC;;;AC1IH,SAAS,WAAAO,iBAAe;AACxB,SAAS,iBAAAC,iBAAe,WAAAC,WAAS,gBAAAC,gBAAc,oBAAAC,0BAAwB;AACvE,OAAOC,YAAW;AAMX,IAAM,mBAAmB,IAAIC,UAAQ,YAAY,EACrD,YAAY,6KAA0D,EACtE,SAAS,YAAY,2JAAwC,EAC7D,OAAO,OAAO,WAAmB;AAChC,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,gBAAgB,IAAIC,gBAAc,GAAG;AAC3C,QAAM,SAAS,MAAM,cAAc,KAAK;AACxC,QAAM,UAAUC,UAAQ,KAAK,cAAc,MAAM;AAEjD,MAAI;AACF,UAAM,SAASC,eAAa,WAAW,MAAM;AAC7C,UAAM,UAAU,MAAM,YAAY,KAAK,OAAO,KAAK;AACnD,UAAM,eAAe,IAAIC,mBAAiB,QAAQ,SAAS,QAAQ,OAAO;AAC1E,UAAMC,UAAS,MAAM,aAAa,sBAAsB,QAAQ,kBAAkB,GAAG,CAAC;AAEtF,YAAQ,IAAI,KAAK,MAAM,OAAO,IAAIA,QAAO,WAAW,kBAAQA,QAAO,IAAI,QAAG;AAC1E,eAAW,YAAYA,QAAO,YAAY;AACxC,cAAQ,IAAI,OAAO,SAAS,IAAI,KAAK,SAAS,IAAI,IAAIC,OAAM,IAAI,IAAI,SAAS,MAAM,GAAG,CAAC,EAAE;AAAA,IAC3F;AACA,QAAID,QAAO,MAAM,SAAS,EAAG,SAAQ,IAAI,qBAAWA,QAAO,MAAM,KAAK,IAAI,CAAC,EAAE;AAC7E,eAAW,QAAQA,QAAO,SAAS;AACjC,cAAQ,IAAIC,OAAM,IAAI,8CAAgB,KAAK,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;AAAA,IACpE;AACA,YAAQ,IAAIA,OAAM,IAAI,2HAAiC,CAAC;AAAA,EAC1D,SAAS,OAAO;AACd,eAAW,QAAQ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AACzE,YAAQ,WAAW;AAAA,EACrB,UAAE;AACA,YAAQ,MAAM;AAAA,EAChB;AACF,CAAC;;;ACxBI,IAAM,uBAAuB;AAc7B,SAAS,gBAAgB,UAAsD;AACpF,MAAI,OAAO,aAAa,SAAU,QAAO;AACzC,MAAI,OAAO,aAAa,UAAU;AAChC,UAAM,SAAS,OAAO,QAAQ;AAC9B,WAAO,OAAO,SAAS,MAAM,IAAI,SAAS;AAAA,EAC5C;AACA,SAAO;AACT;AAGO,SAAS,mBACd,MACA,WAAoD,YAC9C;AACN,WAAS,MAAM,KAAK,KAAK,gBAAgB,KAAK,QAAQ,CAAC,GAAG,oBAAoB;AAChF;;;AlB1BA,IAAM,cAAc,cAAc,YAAY,GAAG;AACjD,IAAM,EAAE,QAAQ,IAAI,YAAY,iBAAiB;AAEjD,IAAM,UAAU,IAAIC,UAAQ;AAE5B,QACG,KAAK,cAAc,EACnB,YAAY,gFAA6C,EACzD,QAAQ,OAAO,EACf,OAAO,aAAa,wCAAU,EAC9B,OAAO,WAAW,2BAAO;AAE5B,QAAQ,WAAW,WAAW;AAC9B,QAAQ,WAAW,WAAW;AAC9B,QAAQ,WAAW,WAAW;AAC9B,QAAQ,WAAW,WAAW;AAC9B,QAAQ,WAAW,aAAa;AAChC,QAAQ,WAAW,WAAW;AAC9B,QAAQ,WAAW,cAAc;AACjC,QAAQ,WAAW,YAAY;AAC/B,QAAQ,WAAW,cAAc;AACjC,QAAQ,WAAW,YAAY;AAC/B,QAAQ,WAAW,aAAa;AAChC,QAAQ,WAAW,gBAAgB;AAInC,QAAQ,WAAW,EAAE;AAAA,EACnB,MAAM;AACJ,uBAAmB,OAAO;AAAA,EAC5B;AAAA,EACA,CAAC,UAAmB;AAClB,yBAAqB,OAAO,OAAO;AACnC,uBAAmB,OAAO;AAAA,EAC5B;AACF;","names":["Command","ConfigManager","chalk","ConfigManager","chalk","spinner","Command","ConfigManager","StateDB","NotionClient","Command","ConfigManager","StateDB","NotionClient","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","chalk","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","chalk","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","chalk","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","chalk","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","chalk","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","chalk","remoteDeleted","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","matchesPathScope","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","matchesPathScope","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","chalk","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","chalk","Command","confirm","ConfigManager","StateDB","NotionClient","SyncOrchestrator","isRemoteDeletion","isRemoteDeletion","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","confirm","failed","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","chalk","report","chalk","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","chalk","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","report","chalk","Command"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "im-nobsidian",
3
- "version": "0.4.1",
3
+ "version": "0.9.1",
4
4
  "description": "CLI tool for Obsidian โ†” Notion bidirectional sync",
5
5
  "type": "module",
6
6
  "bin": {
@@ -15,7 +15,7 @@
15
15
  "chalk": "^5.6.2",
16
16
  "commander": "^15.0.0",
17
17
  "ora": "^9.4.1",
18
- "@im-nobsidian/core": "0.4.1"
18
+ "@im-nobsidian/core": "0.9.1"
19
19
  },
20
20
  "devDependencies": {
21
21
  "tsup": "^8.0.0",